lib/dolos.rb in dolos-0.2.1 vs lib/dolos.rb in dolos-0.3.0

- old
+ new

@@ -13,43 +13,57 @@ attr_accessor :parser_proc def initialize(&block) @parser_proc = block end + # Run the parser with the given input + # Returns a Result<Success|Failure> + # string("hello").run("hello") => Success.new("hello", 5) def run(input) run_with_state(ParserState.new(input)) end + def run_with_state(state) result = @parser_proc.call(state) state.last_success_position = state.input.offset if result.success? result end + # Capture the result of the parser + # p = string("hello").capture! + # p.run("hello").captures => ["hello"] + # Captures is a flat array of all captured values def capture!(wrap_in = nil) Parser.new do |state| result = run_with_state(state) result.success? ? result.capture!(wrap_in) : result end end - # Will call `map` on captures + # Map the captures of the parser + # p = string("hello").map_captures { |captures| captures.map(&:upcase) } + # p.run("hello") => Success.new("hello", 5, ["HELLO"]) + # This only maps over captures, not the value def map_captures(&block) Parser.new do |state| result = run_with_state(state) result.success? ? Success.new(result.value, result.length, block.call(result.captures)) : result end end - # Will call block on tuple of value + # Map the result of the parser + # p = string("hello").map { |s| s.upcase } + # p.run("hello") => Success.new("HELLO", 5) def map(&block) Parser.new do |state| result = run_with_state(state) result.success? ? Success.new(block.call(result.value), result.length, result.captures) : result end end + # Combine the result of the parser with another parser def combine(&block) Parser.new do |state| result = run_with_state(state) if result.success? @@ -60,16 +74,14 @@ result end end end - def flatten - map_captures do |captures| - captures.flatten - end - end - + # Combine the result of the parser with another parser + # Has an alias of `&` + # p = string("hello") & string("world") + # p.run("helloworld") => Success.new(["hello", "world"], 10) def product(other_parser) combine do |value1, capture1| other_parser.map do |value2| [value1, value2] end.map_captures do |capture2| @@ -77,20 +89,27 @@ end end end alias_method :&, :product + + # Combine the result of the parser with another parser + # Discards the result of the second parser + # p = string("hello") << string("world") def product_l(other_parser) combine do |value1, capture1| other_parser.map do |_| value1 end.map_captures do |capture2| [capture1, capture2].flatten end end end + # Combine the result of the parser with another parser + # Discards the result of the first parser + # p = string("hello") >> string("world") def product_r(other_parser) combine do |_, capture1| other_parser.map do |value2| value2 end.map_captures do |capture2| @@ -100,10 +119,14 @@ end alias_method :<<, :product_l alias_method :>>, :product_r + # Combine the result of the parser with another parser + # If the first parser fails, it will try the second parser + # p = string("hello") | string("world") | string("!") + # p.run("hello") => Success.new("hello", 5) def choice(other_parser) Parser.new do |state| result = run_with_state(state) if result.success? result @@ -112,10 +135,13 @@ end end end alias_method :|, :choice + + # Repeat the parser n times + # Separator is optional, its another parser that will be run between each repetition # rep0 # 0 or more # rep # 1 or more # rep(n = 2) # exactly 2 # repeat(n_min: 2, n_max: 4) # 2 to 4 # repeat(n_min: 2) # 2 or more @@ -154,23 +180,31 @@ else Success.new(values, 0, captures) end end end + + # Repeat the parser zero or more times + # c(" ").rep0.run(" ") => Success.new([" ", " ", " "], 3) def zero_or_more repeat(n_min: 0, n_max: Float::INFINITY) end alias_method :rep0, :zero_or_more + # Repeat the parser one or more times + # Same as rep0, but must match at least once + # c(" ").rep.run("A") => Failure.new("...") def one_or_more(exactly = nil) if exactly.nil? repeat(n_min: 1, n_max: Float::INFINITY) else repeat(n_min: exactly, n_max: exactly) end end alias_method :rep, :one_or_more + # Make parser optional + # c(" ").opt.run("A") => Success.new([], 0) def optional Parser.new do |state| result = run_with_state(state.dup) if result.success? result