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