harsh
harsh

Reputation: 969

proper use of nestedExpr in pyparsing

I have a nested representation of key values pairs something like:
Case 1:

str = " value 23"

Case 2:

str = " value { \
          {'name' {value 'joint'}}, 
          {'id' {value 51}} \
         }"

Or case 3:

str = " value { \
           { 'drive' { value 'joint1'}}, \
           { 'steer' { value 45.35}}
         }"

As can be seen, the nesting can go to unlimited levels. e.g. in above example, 'joint2' can have curly braces as well. value is the only keyword here. Lastly, the values can be integers, floats or single quoted strings.

I have a very simple code here:

field_name = Keyword("value").supress()
real = Combine(Word(nums) + '.' + Word(nums))
values = real | Word(
    nums) | sglQuotedString.setParseAction(removeQuotes)

nested = nestedExpr('{', '}')
grammar = field_name + \
    (values | (nestedExpr('{', '}') + Optional(',').suppress()))("values")

for case 2, I get an output

{'values': [[["'name'", ['value', "'joint'"]], ',', ["'id'", ['value', '51']]]]}

Firstly the ',' and value are being parsed as well. Secondly I am not sure if I am using nestedExpr correctly. Should I be using Dict inside nestedExpr?

Edit 1: I have already posted the solution for above cases, but I realized case 2 and 3 are identical. What I actually wanted to show was

Case 4:

str = "value { \
         {'name' {value 'joint1' { \
                           value 12} \
                                 } \
                 } \
         }, \
         {'id' {value 51}} \
       }"

For such cases do I need to know how deep is the nesting?

Upvotes: 1

Views: 1141

Answers (1)

harsh
harsh

Reputation: 969

I got a solution:

from pyparsing import *

_value = Keyword("value").suppress()
real = Combine(Word(nums) + '.' + Word(nums))
values = real | Word(
    nums) | sglQuotedString.setParseAction(removeQuotes)


LCB, RCB = map(Suppress, "{}")
param_val = _value + values
param_vals = Dict(OneOrMore(Group(sglQuotedString.setParseAction(
    removeQuotes) + nestedExpr('{', '}', content=param_val))))
param_values = _value + \
    nestedExpr('{', '}', content=delimitedList(
        LCB + param_vals + RCB, delim=','))

str = "value {{'name' {value 'arm_right_1_joint'}}, {'id' {value 51}}}"
res = param_values.parseString(str)

print(res.dump())

I get the result:

[[['name', ['arm_right_1_joint']], ['id', ['51']]]]
[0]:
  [['name', ['arm_right_1_joint']], ['id', ['51']]]
  - id: ['51']
  - name: ['arm_right_1_joint']

Please feel free to suggest other solutions.

Edit: I found an even better solution. Now it can parse any level of recursiveness.

real = Combine(Word(nums) + '.' + Word(nums))

listStr = Forward()
mapStr = Forward()
param_value = Forward()

string_value = Dict(Group(sglQuotedString.setParseAction(
    removeQuotes) + ZeroOrMore(LCB + param_value + RCB)))
string_value.setParseAction(parseActionStr)
values = Combine(Optional("-") + real) | Combine(Optional("-") + Word(
    nums)) | string_value | Keyword("false") | Keyword("true") | listStr | mapStr
listStr << delimitedList(Group(LCB + delimitedList(values) + RCB))

mapStr << (LSB + delimitedList(Group(LCB + delimitedList((Group(sglQuotedString.setParseAction(removeQuotes) +
                                                                Suppress(":") + values))) + RCB)) + RSB)
mapStr.setParseAction(parseActionDict)

param_value << _value + (values | listStr)

And the results for:

str_param4 = "value { \
                  {'type' {value 'laser'}}, \
                  {'params' {value { \
                      {'upper_replacement_value' {value 10.0}}, \
                      {'use_message_range_limits' {value false}}, \
                      {'upper_threshold' {value 10.0}}}, \
                  {'name' {value 'range'}}}}}"

are:

[['laser']]
[['range']]
[[[['type', 'laser']], [['params', [[['upper_replacement_value', '10.0']], [['use_message_range_limits', 'false']], [['upper_threshold', '10.0']]], [['name', 'range']]]]]]
[0]:
  [[['type', 'laser']], [['params', [[['upper_replacement_value', '10.0']], [['use_message_range_limits', 'false']], [['upper_threshold', '10.0']]], [['name', 'range']]]]]
  [0]:
    [['type', 'laser']]
    - type: 'laser'
  [1]:
    [['params', [[['upper_replacement_value', '10.0']], [['use_message_range_limits', 'false']], [['upper_threshold', '10.0']]], [['name', 'range']]]]
    - params: [[[['upper_replacement_value', '10.0']], [['use_message_range_limits', 'false']], [['upper_threshold', '10.0']]], [['name', 'range']]]
      [0]:
        [[['upper_replacement_value', '10.0']], [['use_message_range_limits', 'false']], [['upper_threshold', '10.0']]]
        [0]:
          [['upper_replacement_value', '10.0']]
          - upper_replacement_value: '10.0'
        [1]:
          [['use_message_range_limits', 'false']]
          - use_message_range_limits: 'false'
        [2]:
          [['upper_threshold', '10.0']]
          - upper_threshold: '10.0'
      [1]:
        [['name', 'range']]
        - name: 'range'

Upvotes: 2

Related Questions