haml--haml/lib/hamlit/hash_parser.rb

113 lines
2.4 KiB
Ruby
Raw Normal View History

2015-10-31 16:18:18 +00:00
require 'ripper'
module Hamlit
class HashParser
2015-11-03 05:32:14 +00:00
class ParseSkip < StandardError
end
2015-10-31 16:18:18 +00:00
def self.parse(text)
self.new.parse(text)
end
def parse(text)
2015-11-01 01:55:15 +00:00
exp = '{' << text.strip << '}'.freeze
return if syntax_error?(exp)
2015-10-31 16:18:18 +00:00
hash = {}
2015-11-01 01:55:15 +00:00
tokens = Ripper.lex(exp)[1..-2] || []
2015-10-31 16:18:18 +00:00
each_attr(tokens) do |attr_tokens|
key = parse_key!(attr_tokens)
hash[key] = attr_tokens.map(&:last).join.strip
end
hash
2015-11-03 05:32:14 +00:00
rescue ParseSkip
nil
2015-10-31 16:18:18 +00:00
end
private
def parse_key!(tokens)
_, type, str = tokens.shift
case type
when :on_sp
parse_key!(tokens)
when :on_label
2015-11-03 05:32:14 +00:00
str.tr(':'.freeze, ''.freeze)
2015-11-01 11:08:04 +00:00
when :on_symbeg
_, _, key = tokens.shift
2015-11-03 05:32:14 +00:00
assert_type!(tokens.shift, :on_tstring_end) if str != ':'.freeze
skip_until_hash_rocket!(tokens)
key
when :on_tstring_beg
_, _, key = tokens.shift
next_token = tokens.shift
unless next_token[1] == :on_label_end
assert_type!(next_token, :on_tstring_end)
skip_until_hash_rocket!(tokens)
2015-11-01 11:08:04 +00:00
end
key
2015-10-31 16:18:18 +00:00
else
2015-11-03 05:55:39 +00:00
raise ParseSkip
2015-10-31 16:18:18 +00:00
end
end
2015-11-03 05:32:14 +00:00
def assert_type!(token, type)
raise ParseSkip if token[1] != type
end
def skip_until_hash_rocket!(tokens)
until tokens.empty?
_, type, str = tokens.shift
break if type == :on_op && str == '=>'.freeze
end
end
2015-10-31 16:18:18 +00:00
def each_attr(tokens)
attr_tokens = []
2015-11-03 05:55:39 +00:00
array_open = 0
2015-11-03 06:20:10 +00:00
brace_open = 0
2015-11-03 05:55:39 +00:00
2015-10-31 16:18:18 +00:00
tokens.each do |token|
(row, col), type, str = token
case type
when :on_comma
2015-11-03 06:20:10 +00:00
if array_open == 0 && brace_open == 0
2015-11-03 05:55:39 +00:00
yield(attr_tokens)
attr_tokens = []
next
end
when :on_lbracket
array_open += 1
when :on_rbracket
array_open -= 1
2015-11-03 06:20:10 +00:00
when :on_lbrace
brace_open += 1
when :on_rbrace
brace_open -= 1
2015-10-31 16:18:18 +00:00
end
2015-11-03 05:55:39 +00:00
attr_tokens << token
2015-10-31 16:18:18 +00:00
end
yield(attr_tokens) unless attr_tokens.empty?
end
2015-11-01 01:55:15 +00:00
def syntax_error?(code)
ParseErrorChecker.new(code).parse
false
rescue ParseErrorChecker::ParseError
true
end
end
class ParseErrorChecker < Ripper
class ParseError < StandardError
end
private
def on_parse_error(*)
raise ParseError
end
2015-10-31 16:18:18 +00:00
end
end