Ruby: Parsing a string representation of nested arrays into an Array?

后端 未结 4 476
梦毁少年i
梦毁少年i 2020-12-03 14:01

Let\'s say I had the string

\"[1,2,[3,4,[5,6]],7]\"

How would I parse that into the array

[1,2,[3,4,[5,6]],7]
4条回答
  •  天命终不由人
    2020-12-03 14:36

    "Obviously" the best solution is to write your own parser. [ If you like writing parsers, have never done it before and want to learn something new, or want control over the exact grammar ]

    require 'parslet'
    
    class Parser < Parslet::Parser
      rule(:space)       { str(' ') }
      rule(:space?)      { space.repeat(0) }
      rule(:openbrace_)  { str('[').as(:op) >> space? }
      rule(:closebrace_) { str(']').as(:cl) >> space? }
      rule(:comma_)      { str(',') >> space?  }
      rule(:integer)     { match['0-9'].repeat(1).as(:int) }
      rule(:value)       { (array | integer) >> space? }
      rule(:list)        { value >> ( comma_ >> value ).repeat(0) }
      rule(:array)       { (openbrace_ >> list.maybe.as(:list) >> closebrace_ )}
      rule(:nest)        { space? >> array.maybe }
      root(:nest)
    end
    
    class Arr
      def initialize(args)
        @val = args
      end
      def val
        @val.map{|v| v.is_a?(Arr) ? v.val : v}
      end
    end
    
    
    class MyTransform < Parslet::Transform
      rule(:int => simple(:x))      { Integer(x) }
      rule(:op => '[', :cl => ']')  { Arr.new([]) }
      rule(:op => '[', :list => simple(:x), :cl => ']')   {  Arr.new([x]) }
      rule(:op => '[', :list => sequence(:x), :cl => ']')   { Arr.new(x) }
    end
    
    def parse(s)
      MyTransform.new.apply(Parser.new.parse(s)).val
    end
    
    parse " [   1  ,   2  ,  [  3  ,  4  ,  [  5   ,  6  , [ ]]   ]  ,  7  ]  "
    

    Parslet transforms will match a single value as "simple" but if that value returns an array, you soon get arrays of arrays, then you have to start using subtree. returning objects however are fine as they represent a single value when transforming the layer above... so sequence will match fine.

    Couple the trouble with returning bare arrays, with the problem that Array([x]) and Array(x) give you the same thing... and you get very confusing results.

    To avoid this I made a helper class called Arr which represents an array of items. I could then dictate what I pass into it. Then I can get the parser to keep all the brackets even if you have the example that @MateuszFryc called out :) (thanks @MateuszFryc)

提交回复
热议问题