Reputation: 969
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
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