pyparsing fails on recursive structure

Viewed 74

I'm trying to write pyparsing rules to parse a document that's similar to JSON.

My document looks like this:

s = {
    id = "DEB36287-8871-4378-8805-B54CC5EED3C0",
    internalName = "1a. Vintage Glow",
    title = "1a. Vintage Glow",
    type = "Develop",
    value = {
        settings = {
            AutoLateralCA = 0,
            Blacks2012 = -6,
            BlueHue = 0,
            BlueSaturation = 0,
            CameraProfile = "Embedded",
            ChromaticAberrationB = 0,
            ChromaticAberrationR = 0,
            Clarity2012 = 0,
            Contrast2012 = 0,
            ConvertToGrayscale = false,
            CropConstrainToWarp = 0,
            Defringe = 0,
            DefringeGreenAmount = 0,
            DefringeGreenHueHi = 60,
            DefringeGreenHueLo = 40,
            DefringePurpleAmount = 0,
            DefringePurpleHueHi = 70,
            DefringePurpleHueLo = 30,
            Dehaze = 0,
            EnableCalibration = true,
            EnableColorAdjustments = true,
            EnableDetail = true,
            EnableEffects = true,
            EnableLensCorrections = true,
            EnableSplitToning = true,
            GrainAmount = 30,
            GrainFrequency = 50,
            GrainSize = 25,
            GreenHue = 0,
            GreenSaturation = 0,
            Highlights2012 = 45,
            HueAdjustmentAqua = 0,
            HueAdjustmentBlue = 0,
            HueAdjustmentGreen = 0,
            HueAdjustmentMagenta = 0,
            HueAdjustmentOrange = 0,
            HueAdjustmentPurple = 0,
            HueAdjustmentRed = 0,
            HueAdjustmentYellow = 0,
            LensManualDistortionAmount = 0,
            LensProfileEnable = 0,
            LensProfileSetup = "LensDefaults",
            LuminanceAdjustmentAqua = 0,
            LuminanceAdjustmentBlue = 0,
            LuminanceAdjustmentGreen = 0,
            LuminanceAdjustmentMagenta = 0,
            LuminanceAdjustmentOrange = 0,
            LuminanceAdjustmentPurple = 0,
            LuminanceAdjustmentRed = -3,
            LuminanceAdjustmentYellow = 0,
            ParametricDarks = 0,
            ParametricHighlightSplit = 75,
            ParametricHighlights = 0,
            ParametricLights = 0,
            ParametricMidtoneSplit = 50,
            ParametricShadowSplit = 25,
            ParametricShadows = 0,
            PostCropVignetteAmount = 0,
            PostCropVignetteFeather = 50,
            PostCropVignetteHighlightContrast = 0,
            PostCropVignetteMidpoint = 50,
            PostCropVignetteRoundness = 0,
            PostCropVignetteStyle = 1,
            ProcessVersion = "6.7",
            RedHue = 0,
            RedSaturation = 0,
            Saturation = 0,
            SaturationAdjustmentAqua = 0,
            SaturationAdjustmentBlue = 0,
            SaturationAdjustmentGreen = 0,
            SaturationAdjustmentMagenta = 0,
            SaturationAdjustmentOrange = 0,
            SaturationAdjustmentPurple = 0,
            SaturationAdjustmentRed = 0,
            SaturationAdjustmentYellow = 0,
            ShadowTint = 0,
            Shadows2012 = 0,
            SharpenDetail = 25,
            SharpenEdgeMasking = 0,
            SharpenRadius = 1,
            Sharpness = 0,
            SplitToningBalance = -32,
            SplitToningHighlightHue = 50,
            SplitToningHighlightSaturation = 6,
            SplitToningShadowHue = 0,
            SplitToningShadowSaturation = 0,
            ToneCurveName2012 = "Custom",
            Vibrance = 0,
            VignetteAmount = 0,
            VignetteMidpoint = 50,
            Whites2012 = -70,
        },
        uuid = "59A86241-054F-4AE0-8C14-188A3038749B",
    },
    version = 0,
}

My rules look like this:

real = Regex(r"[+-]?\d+\.\d*").setParseAction(lambda x: float(x[0]))
integer = Regex(r"[+-]?\d+").setParseAction(lambda x: int(x[0]))
quotedString.setParseAction(removeQuotes)

key = Word(alphanums)
value = Forward() #this tells pyparsing that values can be recursive
eq,leftBrace,rightBrace = map(Suppress, "={}")
entry = Group(key + eq + value + ZeroOrMore(Suppress(','))) #this is the basic name-value pair
struct = Dict(leftBrace + ZeroOrMore(entry) + rightBrace) #we will turn the output into a Dictionary
value << (struct | quotedString | real | integer)

I'm invoking pyparser as follows:

result = Dict(OneOrMore(entry)).parseString(data)

I'm getting the following error on line 6:

pyparsing.ParseException: Expected {Dict:({{Suppress:("{") [Group:({{W:(ABCD...) Suppress:("=") : ...} [Suppress:(",")]...})]...} Suppress:("}")}) | quotedString using single or double quotes | Re:('[+-]?\\d+\\.\\d*') | Re:('[+-]?\\d+')}, found 'v'  (at char 201), (line:6, col:13)

Python version: 3.9.5 pyparsing version: 2.4.7 OS: Mac OS X 11.4 Platform: MacBook Air (M1, 2020)

I'm hoping someone can point me in the right direction for a fix.

EDIT: Final solution on the off-chance it helps someone else:

    real = Regex(r"[+-]?\d+\.\d*").setParseAction(lambda x: float(x[0]))
    integer = Regex(r"[+-]?\d+").setParseAction(lambda x: int(x[0]))
    booleanLiteral = oneOf("true false", asKeyword=True).setParseAction(lambda x: x[0] == "true")

    eq,leftBrace,rightBrace = map(Suppress, "={}")
    value = Forward()
    key = Word(alphanums)
    pair = Group(key + eq + value)
    struct = (Dict(leftBrace + ZeroOrMore(pair) + rightBrace) | (leftBrace + ZeroOrMore(value) + rightBrace))
    value << Group((dblQuotedString | struct | real | integer | booleanLiteral) + ZeroOrMore(Suppress(',')))

    return Dict(pair).parseString(data).asDict()
0 Answers
Related