ParserCombinator
Yet another class-base parser combinator (monadic parser) library.
Installation
Add this line to your application's Gemfile:
gem 'parser_combinator'
And then execute:
$ bundle
Or install it yourself as:
$ gem install parser_combinator
Basics
Item class
Abstraction of Char (in Ruby, it is String of 1-length). Parsing charactors through this abstraction, you can handle meta-info of source-string such as file-name, line-number, column-number.
Items class
Abstraction of String. This is Array of Item.
Usage
Basic Example
require 'parser_combinator/string_parser'
class MyParser < ParserCombinator::StringParser
parser :noun do
str("I") | str("you") | str("it")
end
parser :verb do
str("love") | str("hate") | str("live") | str("die")
end
parser :token do |p|
many(str("\s")) > p < many(str("\s"))
end
parser :sentence_way1 do
token(noun) >> proc{|n1|
token(verb) >> proc{|v|
token(noun) >> proc{|n2|
ok("You said, '#{n1} #{v} #{n2}'")
}}}
end
parser :sentence_way2 do
seq(token(noun), token(verb), token(noun)).map do |x|
"You said, '#{x[0]} #{x[1]} #{x[2]}'"
end
end
parser :sentence_way3 do
seq(token(noun).name(:a), token(verb).name(:b), token(noun).name(:c)).map do |x|
"You said, '#{x[:a]} #{x[:b]} #{x[:c]}'"
end
end
end
result = MyParser.sentence_way1.parse_from_string("I love you")
puts result.parsed # => You said, 'I love you.'
result = MyParser.sentence_way2.parse_from_string("I love you")
puts result.parsed # => You said, 'I love you.'
result = MyParser.sentence_way3.parse_from_string("I love you")
puts result.parsed # => You said, 'I love you.'
Error Handling
^
is error handling version of |
.
require 'parser_combinator/string_parser'
class MyParser < ParserCombinator::StringParser
parser :love_sentence do
str("I") > str("\s") > str("love") > str("you").onfail("Who do you love?")
end
parser :hate_sentence do
str("I") > str("\s") > str("hate") > str("you").onfail("Who do you hate?")
end
parser :sentence do
love_sentence ^ hate_sentence
end
end
result = MyParser.sentence.parse_from_string("I love")
puts result.status. # => Who do you love?
result = MyParser.sentence.parse_from_string("I hate")
puts result.status. # => Who do you hate?
result = MyParser.sentence.parse_from_string("I am laughing")
puts result.status == nil # => true
Recursive parsing and Left recursion
require 'parser_combinator/string_parser'
class MyParser < ParserCombinator::StringParser
parser :expression do
add_sub
end
parser :add_sub do
add_op = str("+").map{ proc{|l, r| l + r}}
sub_op = str("-").map{ proc{|l, r| l - r}}
binopl(mul_div, add_op | sub_op)
end
parser :mul_div do
mul_op = str("*").map{ proc{|l, r| l * r}}
div_op = str("/").map{ proc{|l, r| l / r}}
binopl(integer | parenth, mul_op | div_op)
end
parser :integer do
many1(digit).map{|x| x.map{|i| i.item}.join.to_i}
end
parser :parenth do
str("(") > expression < str(")")
end
end
result = MyParser.expression.parse_from_string("(1+2)*3+10/2")
puts result.parsed # => 14
result = MyParser.expression.parse_from_string("3-2-1")
puts result.parsed # => 0
Contributing
- Fork it ( https://github.com/sawaken/parser_combinator/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request