Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

moved lr support into Parslet::Atoms::Rule; added Parslet::Atoms::Rul…

…e::Position to process evaluation at given position in source
  • Loading branch information...
commit f3c57f6656234bf465a8b3b634b235f5154a80d1 1 parent 22f7eb5
@xli authored
View
14 lib/parslet/atoms/base.rb
@@ -233,13 +233,6 @@ def error_tree
Parslet::ErrorTree.new(self)
end
- # Produces an instance of Fail and returns it.
- #
- def error(source, str, pos=nil)
- @last_cause = format_cause(source, str, pos)
- Fail.new(@last_cause)
- end
-
private
# Produces an instance of Success and returns it.
@@ -248,6 +241,13 @@ def success(result)
Success.new(result)
end
+ # Produces an instance of Fail and returns it.
+ #
+ def error(source, str, pos=nil)
+ @last_cause = format_cause(source, str, pos)
+ Fail.new(@last_cause)
+ end
+
# Signals to the outside that the parse has failed. Use this in conjunction
# with #format_cause for nice error messages.
#
View
64 lib/parslet/atoms/context.rb
@@ -4,76 +4,14 @@ module Parslet::Atoms
# style.
#
class Context
- class LR < Struct.new(:detected)
- def error?; false end
- end
-
def initialize
@cache = Hash.new { |h, k| h[k] = {} }
end
- # Caches a parse answer for obj at source.pos. Applying the same parslet
- # at one position of input always yields the same result, unless the input
- # has changed.
- #
- # We need the entire source here so we can ask for how many characters
- # were consumed by a successful parse. Imitation of such a parse must
- # advance the input pos by the same amount of bytes.
- #
- def cache(obj, source, &block)
- beg = source.pos
-
- # Not in cache yet? Return early.
- unless entry = lookup(obj, beg)
- lr = LR.new
- memo = [lr, beg]
- set obj, beg, memo
- result = yield
- memo[0] = result
- memo[1] = source.pos - beg
- if lr.detected && !result.error?
- return grow_lr(obj, source, beg, memo, nil, &block)
- else
- return result
- end
- end
-
- # the condition in unless has returned true, so entry is not nil.
- result, advance = entry
-
- # The data we're skipping here has been read before. (since it is in
- # the cache) PLUS the actual contents are not interesting anymore since
- # we know obj matches at beg. So skip reading.
- source.pos = beg + advance
-
- if result.is_a?(LR)
- result.detected = true
- # FIXME this is a quick hack, need to find out a
- return obj.error(source, 'left recursion detected')
- end
-
- return result
- end
-
- private
- def grow_lr(obj, source, beg, memo, h, &block)
- loop do
- source.pos = beg
- ans = block.call
- if ans.error? || source.pos <= (memo[1] + beg)
- break
- end
- memo[0] = ans
- memo[1] = source.pos - beg
- end
-
- source.pos = memo[1] + beg
- memo[0]
- end
-
def lookup(obj, pos)
@cache[pos][obj]
end
+
def set(obj, pos, val)
@cache[pos][obj] = val
end
View
69 lib/parslet/atoms/rule.rb
@@ -1,8 +1,71 @@
class Parslet::Atoms::Rule < Parslet::Atoms::Entity
+ class MemoEntry < Struct.new(:ans, :pos)
+ def error?
+ self.ans.error?
+ end
+ end
+
+ class LR < Struct.new(:pos, :detected)
+ class Error < RuntimeError; end
+ def ans
+ self.detected = true
+ raise Error
+ end
+ end
+
+ # Update/fetch parsed entry at a given position in source
+ # Eval rule body from a given position in source and cache the result
+ class Position < Struct.new(:pos, :source, :context, :rule)
+ def entry=(entry)
+ context.set rule, pos, entry
+ end
+
+ def entry
+ context.lookup(rule, pos)
+ end
+
+ # Eval rule body with LR supported by
+ # placing a LR flag before eval rule body
+ # and growing LR seed after detected LR
+ def eval_rule_body_with_lr_support
+ self.entry = lr = LR.new(pos)
+ self.entry = eval_rule_body
+ if lr.detected && !self.entry.error?
+ grow_lr
+ end
+ self.entry
+ end
+
+ def eval_rule_body
+ rewind
+ ans = rule.eval_rule_body(source, context)
+ MemoEntry.new(ans, source.pos)
+ end
+
+ private
+ def rewind
+ source.pos = self.pos
+ end
+
+ # Tries to grow the parse of rule at given position
+ def grow_lr
+ loop do
+ entry = eval_rule_body
+ break if entry.error? || entry.pos <= self.entry.pos
+ self.entry = entry
+ end
+ end
+ end
+
+ alias_method :eval_rule_body, :try
+
def try(source, context)
- context.cache(self, source) {
- super(source, context)
- }
+ position = Position.new(source.pos, source, context, self)
+ entry = position.entry || position.eval_rule_body_with_lr_support
+ source.pos = entry.pos
+ entry.ans
+ rescue LR::Error
+ error(source, 'left recursion detected')
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.