Skip to content

Commit

Permalink
Fixed parse rule ordering issue.
Browse files Browse the repository at this point in the history
Added parsing of negative integers.
Refactoring of specs and rules.
  • Loading branch information
karlll committed Mar 29, 2013
1 parent d59a1bd commit 49a1fa9
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 108 deletions.
116 changes: 86 additions & 30 deletions lib/str2hash.rb
Expand Up @@ -5,37 +5,84 @@

class HashParse < Parslet::Parser

rule(:lcbracket) { str('{') >> space? }
rule(:rcbracket) { str('}') >> space? }
rule(:lbracket) { str('[') >> space? }
rule(:rbracket) { str(']') >> space? }
rule(:lparen) { str('(') >> space? }
rule(:rparen) { str(')') >> space? }
rule(:comma) { str(',') >> space? }
rule(:larrow) { str('=>') >> space? }

rule(:space) { match('\s').repeat(1) }
rule(:space?) { space.maybe }
rule(:lcbracket) { str('{') >> sp? }
rule(:rcbracket) { str('}') >> sp? }
rule(:lbracket) { str('[') >> sp? }
rule(:rbracket) { str(']') >> sp? }
rule(:lparen) { str('(') >> sp? }
rule(:rparen) { str(')') >> sp? }
rule(:comma) { str(',') >> sp? }
rule(:larrow) { str('=>') >> sp? }

rule(:sp) { match('\s').repeat(1) }
rule(:sp?) { sp.maybe }

# values
rule(:dec_integer) { match('[0-9]').repeat(1).as(:dec_int) >> space? }
rule(:hex_integer) { (str('0x') | str('0X')) >> match['0-9a-fA-F'].repeat(1).as(:hex_int) >> space? }
rule(:bin_integer) { (str('0b') | str('0B')) >> (str('1')|str('0')).repeat(1).as(:bin_int) >> space? }
rule(:string) { ((str("'") >> match["^'"].repeat(1).as(:str) >> str("'")) |
(str('"') >> match['^"'].repeat(1).as(:str) >> str('"'))) >> space? }
rule(:pattern) { str("/") >> match['^/'].repeat(1).as(:pattern) >> str("/") >> space? }
rule(:symbol) { str(":") >> (match['a-zA-Z_'] >> match['a-zA-Z0-9_'].repeat).as(:symbol) >> space? }
rule(:h_symbol) { (match['a-zA-Z_'] >> match['a-zA-Z0-9_'].repeat).as(:symbol) >> str(':') >> space? }
rule(:dec_float) { (match['0-9'].repeat(1) >> str('.') >> match['0-9'].repeat(1)).as(:float) >> space? }
rule(:boolean) { (str('true') | str('false')).as(:boolean) >> space? }

rule(:value) { dec_integer | hex_integer | bin_integer | string | pattern | symbol | dec_float | boolean }

rule(:hex_integer) {
( str('-').maybe >> (str('0x')|str('0X')) >> match['0-9a-fA-F'].repeat(1) ).as(:hex_int) >> sp?
}

rule(:dec_integer) {
( str('-').maybe >> match['0-9'].repeat(1) ).as(:dec_int) >> sp?
}

rule(:bin_integer) {
( str('-').maybe >> (str('0b') | str('0B')) >> (str('1')|str('0')).repeat(1) ).as(:bin_int) >> sp?
}

rule(:dec_float) {
( str('-').maybe >> match['0-9'].repeat(1) >> str('.') >> match['0-9'].repeat(1) ).as(:dec_f) >> sp?
}

rule(:string) {
(
(str("'") >> match["^'"].repeat(1).as(:str) >> str("'")) |
(str('"') >> match['^"'].repeat(1).as(:str) >> str('"'))
) >> sp?
}

rule(:pattern) {
str("/") >> match['^/'].repeat(1).as(:pat) >> str("/") >> sp?
}

rule(:symbol) {
str(":") >> ( match['a-zA-Z_'] >> match['a-zA-Z0-9_'].repeat ).as(:sym) >> sp?
}
rule(:h_symbol) {
( match['a-zA-Z_'] >> match['a-zA-Z0-9_'].repeat ).as(:sym) >> str(':') >> sp?
}

rule(:boolean) {
( str('true') | str('false') ).as(:boolean) >> sp?
}

rule(:value) {

hex_integer |
dec_float |
bin_integer |
dec_integer |
string |
pattern |
symbol |
boolean

}

# hashes

rule(:key_value) { ((value.as(:key) >> larrow) | (h_symbol.as(:key))) >> value.as(:value) }
rule(:key_value_lst) { key_value >> (comma >> key_value).repeat }
rule(:simple_hash) { ((lcbracket >> key_value_lst >> rcbracket) | key_value_lst).as(:hash) }
rule(:key_value) {
( value.as(:key) >> larrow | h_symbol.as(:key) ) >> value.as(:val)
}

rule(:key_value_lst) {
key_value >> (comma >> key_value).repeat
}

rule(:simple_hash) {
( lcbracket >> key_value_lst >> rcbracket | key_value_lst ).as(:hash)
}

root :simple_hash

Expand All @@ -44,12 +91,19 @@ class HashParse < Parslet::Parser
class HashTransform < Parslet::Transform

rule(:dec_int => simple(:i)) { Integer(i) }

rule(:hex_int => simple(:i)) { Integer(i) }

rule(:bin_int => simple(:i)) { Integer(i) }

rule(:str => simple(:s)) { s.to_s }
rule(:pattern => simple(:p)) { /#{p}/ }
rule(:symbol => simple(:sym)) { sym.to_sym }
rule(:dec_float => simple(:f)) { Float(f) }

rule(:pat => simple(:p)) { /#{p}/ }

rule(:sym => simple(:symb)) { symb.to_sym }

rule(:dec_f => simple(:f)) { Float(f) }

rule(:boolean => simple(:b)) {
case b
when "true"
Expand All @@ -58,9 +112,11 @@ class HashTransform < Parslet::Transform
false
end
}

rule(:hash => subtree(:h)) {
(h.is_a?(Array) ? h : [ h ]).inject({}) { |h, e| h[e[:key]] = e[:value] ;h }
(h.is_a?(Array) ? h : [ h ]).inject({}) { |h, e| h[e[:key]] = e[:val] ;h }
}

end


Expand Down
126 changes: 48 additions & 78 deletions spec/str2hash_spec.rb
Expand Up @@ -3,107 +3,77 @@
require 'parslet/convenience'


describe HashParse do

=begin
it 'should parse Ruby values' do
val_strs = ["123","'123'","\"foo\"","/foo/",":foo",":SYMBOL",":f_O_o","123.0","0x12AB","0b0101001001"]
p = HashParse.new
val_strs.each do |item|
pp p.parse(item)
end
PRODS = [
{s: "{ :foo => 123 }", h: {:foo => 123}},
{s: "{ :foo => -123 }", h: {:foo => -123}},
{s: "{ :foo => :bar }", h: {:foo => :bar}},
{s: "{ :foo => 123.456 }", h: {:foo => 123.456}},
{s: "{ :foo => -123.456 }", h: {:foo => -123.456}},
{s: "{ :foo => 0x0123 }", h: {:foo => 291}},
{s: "{ :foo => 0X0123 }", h: {:foo => 291}},
{s: "{ :foo => -0x0123 }", h: {:foo => -291}},
{s: "{ :foo => -0X0123 }", h: {:foo => -291}},
{s: "{ :foo => 0b010010010100010101}", h: {:foo => 75029}},
{s: "{ :foo => 0B010010010100010101}", h: {:foo => 75029}},
{s: "{ :foo => -0b010010010100010101}", h: {:foo => -75029}},
{s: "{ :foo => -0B010010010100010101}", h: {:foo => -75029}},
{s: "{ 123 => 456 }", h: {123 => 456}},
{s: "{ foo: 456 }", h: {:foo => 456}},
{s: "{ foo: :bar }", h: {:foo => :bar}},
{s: "{ foo: '123' }", h: {:foo => '123'}},
{s: "{ foo: \"123\" }", h: {:foo => "123"}},
{s: "{ foo: /foo/ }", h: {:foo => /foo/}},
{s: "{ foo: true }", h: {:foo => true}},
{s: "{ :foo => :bar, :foo2 => :bar2}", h: {:foo => :bar, :foo2 => :bar2}},
{s: ":foo => :bar, :foo2 => :bar2", h: {:foo => :bar, :foo2 => :bar2}}
]

describe HashParse do

end
=end

it 'should parse a basic value hash' do
p = HashParse.new

prods = [
{s: "{ :foo => 123 }", h: {:foo => 123}},
{s: "{ :foo => :bar }", h: {:foo => :bar}},
{s: "{ 123 => 456 }", h: {123 => 456}},
{s: "{ foo: 456 }", h: {:foo => 456}},
{s: "{ foo: :bar }", h: {:foo => :bar}},
{s: "{ foo: '123' }", h: {:foo => '123'}},
{s: "{ foo: \"123\" }", h: {:foo => "123"}},
{s: "{ foo: /foo/ }", h: {:foo => /foo/}},
{s: "{ foo: true }", h: {:foo => true}},
{s: "{ :foo => :bar, :foo2 => :bar2}", h: {:foo => :bar, :foo2 => :bar2}},
{s: ":foo => :bar, :foo2 => :bar2", h: {:foo => :bar, :foo2 => :bar2}}

]

prods.each do |item|
p p.parse_with_debug(item[:s])
# p p.parse(item[:s])
PRODS.each do |item|
p.parse(item[:s])
end

end

describe HashTransform do

it 'should parse and transform a basic value hash' do
p = HashParse.new
t = HashTransform.new
end

prods = [
{s: "{ :foo => 123 }", h: {:foo => 123}},
{s: "{ :foo => :bar }", h: {:foo => :bar}},
{s: "{ 123 => 456 }", h: {123 => 456}},
{s: "{ foo: 456 }", h: {:foo => 456}},
{s: "{ foo: :bar }", h: {:foo => :bar}},
{s: "{ foo: '123' }", h: {:foo => '123'}},
{s: "{ foo: /foo/ }", h: {:foo => /foo/}},
{s: "{ foo: true }", h: {:foo => true}},
{s: "{ :foo => :bar, :foo2 => :bar2}", h: {:foo => :bar, :foo2 => :bar2}},
{s: ":foo => :bar, :foo2 => :bar2", h: {:foo => :bar, :foo2 => :bar2}}
describe HashTransform do

]
it 'should parse and transform a basic value hash' do
p = HashParse.new
t = HashTransform.new

prods.each do |item|
s = item[:s]
h = t.apply(p.parse(s))
h.should eq(item[:h])
puts "#{s} => #{h}"
PRODS.each do |item|

end
s = item[:s]
h = t.apply(p.parse(s))
h.should eq(item[:h])

end
end


describe "String to Hash" do
end
end

it 'should Convert a string to corresponding hash' do

prods = [
{s: "{ :foo => 123 }", h: {:foo => 123}},
{s: "{ :foo => :bar }", h: {:foo => :bar}},
{s: "{ 123 => 456 }", h: {123 => 456}},
{s: "{ foo: 456 }", h: {:foo => 456}},
{s: "{ foo: :bar }", h: {:foo => :bar}},
{s: "{ foo: '123' }", h: {:foo => '123'}},
{s: "{ foo: /foo/ }", h: {:foo => /foo/}},
{s: " { foo: true } ", h: {:foo => true}},
{s: "{ :foo => :bar, :foo2 => :bar2}", h: {:foo => :bar, :foo2 => :bar2}},
{s: ":foo => :bar, :foo2 => :bar2", h: {:foo => :bar, :foo2 => :bar2}}
describe "String to Hash" do

]
it 'should Convert a string to corresponding hash' do

prods.each do |item|
s = item[:s]
h = s.to_h
h.should eq(item[:h])
puts "\"#{s}\" => #{h}"
PRODS.each do |item|
s = item[:s]
h = s.to_h
h.should eq(item[:h])

end
puts "\"#{s}\".to_h => #{h}"

end
end

end
end


0 comments on commit 49a1fa9

Please sign in to comment.