class Parser options no_result_var rule json_text : ws object ws { val.flatten.join } | ws array ws { val.flatten.join } ws : { val.flatten.join } | ws SPACE { val.flatten.join } value : FALSE { colorize(val[0], :false, :bool) } | NULL { colorize(val[0], :null) } | TRUE { colorize(val[0], :true, :bool) } | object { val.flatten.join } | array { val.flatten.join } | NUMBER { colorize(val[0], :number) } | STRING { colorize(val[0], :string) } object : '{' ws '}' { val.flatten.join } | '{' ws members ws '}' { val.flatten.join } members : member { val.flatten.join } | members ws ',' ws member { val.flatten.join } member : STRING ws ':' ws value { val[0] = colorize(val[0], :key, :string) val.flatten.join } array : '[' ws ']' { val.flatten.join } | '[' ws values ws ']' { val.flatten.join } values : value { val.flatten.join } | values ws ',' ws value { val.flatten.join } end ---- header module JsonColor ---- footer end # JsonColor ---- inner R_SPACE = /\A[ \t\n\r]+/ R_RESERVED = /\A[\{\}\[\],:]/ R_NUMBER = /\A-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][-+]?\d+)?/ R_STRING = /\A"(?:\\["\\\/bfnrt]|\\u[\dA-Za-z]{4}|[^\\"])*"/ R_FALSE = /\Afalse/ R_NULL = /\Anull/ R_TRUE = /\Atrue/ def self.parse(src, color_map) self.new(src, color_map).parse end def initialize(src, color_map) @ss = StringScanner.new(src) @color_map = color_map end def scan tok = nil until @ss.eos? if (tok = @ss.scan R_SPACE) yield [:SPACE, tok] elsif (tok = @ss.scan R_RESERVED) yield [tok, tok] elsif (tok = @ss.scan R_NUMBER) yield [:NUMBER, tok] elsif (tok = @ss.scan R_STRING) yield [:STRING, tok] elsif (tok = @ss.scan R_FALSE) yield [:FALSE, tok] elsif (tok = @ss.scan R_NULL) yield [:NULL, tok] elsif (tok = @ss.scan R_TRUE) yield [:TRUE, tok] else raise Racc::ParseError, ('parse error on value "%s"' % @ss.rest.inspect) end end yield [false, '$'] end def parse yyparse self, :scan end def colorize(str, *types) types.each do |t| color = @color_map[t] if color return Term::ANSIColor.send(color, str) end end return str end