I have a problem with transforming parsed JSON-like string, which contains nested arrays, to structured object. I am using parslet to do this.
I created parser and transformer, presented below. But I can't handle nested arrays situation.
require 'parslet'
class JSONLikeDataParser < Parslet::Parser
rule(:l_map_paren) {str('{')}
rule(:r_map_paren) {str('}')}
rule(:l_list_paren) {str('[')}
rule(:r_list_paren) {str(']')}
rule(:map_entry_delimiter) {str(':')}
rule(:val_delimiter) {str(',')}
rule(:quote) {str('"')}
rule(:simple_val) {match('[^",:\.\{\}\[\]]').repeat(1)}
rule(:quoted_val) {quote >> (quote.absnt? >> any).repeat(0) >> quote}
rule(:map) {l_map_paren >> map_entries.maybe.as(:map) >> r_map_paren}
rule(:map_entries) {map_entry >> (val_delimiter >> map_entry).repeat}
rule(:map_entry) {map_key >> map_entry_delimiter >> object}
rule(:map_key) {(match('[A-Za-z_]').repeat(1) >> match('[A-Za-z0-9_]').repeat).as(:key)}
rule(:list) {l_list_paren >> list_values.maybe.as(:list) >> r_list_paren}
rule(:list_values) {object >> (val_delimiter >> object).repeat}
rule(:object) {map | (simple_val | quoted_val).as(:value) | list }
root(:object)
end
#TODO doesn't handle properly nested array: [[[1,2],[3]]]
class JSONLikeDataTransform < Parslet::Transform
rule(map: subtree(:s)) do
ret = {}
if (s.is_a?(Hash))
ret[s[:key]] = s[:value]
else
s.each do |h|
ret.merge!(h)
end
end
OpenStruct.new(ret)
end
rule(key: simple(:k), value: simple(:v)) {{k.str => v.str}}
rule(key: simple(:k), list: simple(:v)) {{k.str => [v]}}
rule(key: simple(:k), list: sequence(:v)) {{k.str => v}}
rule(map: simple(:s)) {s ? OpenStruct.new(s) : OpenStruct.new}
rule(list: subtree(:s)) {[s]}
rule(list: sequence(:s)) {s}
rule(list: simple(:s)) {s ? [s] : []}
rule(value: subtree(:s)) {s}
rule(value: sequence(:s)) {s}
rule(value: simple(:s)) {s.str}
end
puts JSONLikeDataTransform.new.apply(JSONLikeDataParser.new.parse("[[[1],[2,3]]]")).inspect
Problematic string is "[[[1],[2,3]]]". I expect to receive properly nested structure. But what I get is "[[[[1],[2,3]]]]" one bracket too much.
Thanks everyone for sharing especially, @NigelThorne whose answer in other thread lead me to actual solution of this problem. I Introduced some additional classes like Map/Value/Arr so I am able to recognize whether particular array is created by Transform framework, or it is a result of list matching.
Below is a working code and some tests for future reference.
Tests