lib/dolos.rb in dolos-0.1.3 vs lib/dolos.rb in dolos-0.2.0

- old
+ new

@@ -27,44 +27,46 @@ state.last_success_position = state.input.offset end result end - def capture! + def capture!(wrap_in = nil) Parser.new do |state| result = run_with_state(state) if result.success? - result.capture! + result.capture!(wrap_in) else result end end end - def map(&block) + # Will call block on captures + def map_captures(&block) Parser.new do |state| result = run_with_state(state) if result.success? Success.new(result.value, result.length, block.call(result.captures)) else result end end end - def map_value(&block) + # Will call block on tuple of value + def map(&block) Parser.new do |state| result = run_with_state(state) if result.success? Success.new(block.call(result.value), result.length, result.captures) else result end end end - def flat_map(&block) + def combine(&block) Parser.new do |state| result = run_with_state(state) if result.success? new_parser = block.call(result.value, result.captures) new_state = state.dup @@ -75,27 +77,49 @@ end end end def flatten - map do |captures| + map_captures do |captures| captures.flatten end end def product(other_parser) - flat_map do |value1, capture1| - other_parser.map_value do |value2| + combine do |value1, capture1| + other_parser.map do |value2| [value1, value2] - end.map do |capture2| + end.map_captures do |capture2| [capture1, capture2].flatten end end end + alias_method :&, :product - alias_method :>>, :product + 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 + def product_r(other_parser) + combine do |_, capture1| + other_parser.map do |value2| + value2 + end.map_captures do |capture2| + [capture1, capture2].flatten + end + end + end + + alias_method :<<, :product_l + alias_method :>>, :product_r + def choice(other_parser) Parser.new do |state| result = run_with_state(state) if result.success? result @@ -109,26 +133,35 @@ # 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 - def repeat(n_min:, n_max: Float::INFINITY) + def repeat(n_min:, n_max: Float::INFINITY, separator: nil) Parser.new do |state| values = [] captures = [] count = 0 state.input.mark_offset - while count < n_max + loop do result = run_with_state(state.dup) - break if result.failure? + if result.failure? || count >= n_max + break + end values << result.value captures.concat(result.captures) state.input.advance(result.length) count += 1 + + if separator && count < n_max + sep_result = separator.run_with_state(state.dup) + break if sep_result.failure? + + state.input.advance(sep_result.length) + end end if count < n_min error_pos = state.input.offset Failure.new( @@ -165,8 +198,25 @@ Success.new([], 0) end end end alias_method :opt, :optional + + # Unstable API + # Used to declare lazy parser to avoid infinite loops in recursive parsers + def lazy + parser_memo = nil + + Parser.new do |state| + parser_memo ||= self + parser_memo.run_with_state(state) + end + end + + private + + def combine_and_discard_empty(*arrays) + arrays.compact.reject { |arr| arr.is_a?(Array) && arr.empty? } + end end end