Skip to content
Browse files

Adds binding to literals and bind conditions

  • Loading branch information...
1 parent 3c8bd50 commit 37fe477a20c12ff3cc2db6c7b548207a9cb18acb @kschiess committed Jul 16, 2013
Showing with 64 additions and 16 deletions.
  1. +4 −5 lib/parslet/accelerator.rb
  2. +25 −8 lib/parslet/accelerator/engine.rb
  3. +35 −3 qed/optimizers.md
View
9 lib/parslet/accelerator.rb
@@ -19,19 +19,18 @@ def >> other_expr
end
module_function
- def str variable
- Expression.new(:str, variable)
+ def str variable, *constraints
+ Expression.new(:str, variable, *constraints)
end
- def re variable
- Expression.new(:re, variable)
+ def re variable, *constraints
+ Expression.new(:re, variable, *constraints)
end
def match atom, expr
engine = Engine.new
return engine.bindings if engine.match(atom, expr)
- return false
end
end
View
33 lib/parslet/accelerator/engine.rb
@@ -36,13 +36,15 @@ def visit_lookahead(positive, atom)
false
end
def visit_re(regexp)
- match(:re) do |variable|
- @engine.try_bind(variable, regexp)
+ match(:re) do |*bind_conditions|
+ bind_conditions.all? { |bind_cond|
+ @engine.try_bind(bind_cond, regexp) }
end
end
def visit_str(str)
- match(:str) do |variable|
- @engine.try_bind(variable, str)
+ match(:str) do |*bind_conditions|
+ bind_conditions.all? { |bind_cond|
+ @engine.try_bind(bind_cond, str) }
end
end
@@ -67,12 +69,27 @@ def match(atom, expr)
end
def try_bind(variable, value)
- if @bindings.has_key? variable
- return value == @bindings[variable]
+ if bound? variable
+ return value == lookup(variable)
else
- @bindings[variable] = value
- return true
+ case variable
+ when Symbol
+ bind(variable, value)
+ else
+ # This does not look like a variable - let's try matching it against
+ # the value:
+ variable === value
+ end
end
end
+ def bound? var
+ @bindings.has_key? var
+ end
+ def lookup var
+ @bindings[var]
+ end
+ def bind var, val
+ @bindings[var] = val
+ end
end
end
View
38 qed/optimizers.md
@@ -1,10 +1,10 @@
# Parslet Optimizers
-* Detect patterns in parsers: see section on 'Parser Pattern Detection'
+* Detect patterns in parsers: see section on 'Parser Pattern Matching'
* Replace with optimized parsers: see section on 'Binding and Actions'
-## Parser Pattern Detection
+## Parser Pattern Matching
We'll demonstrate how pattern detection is constructed by showing what the smallest parts do first. Let's require needed libraries.
@@ -47,4 +47,36 @@ Let's start assembling these simple parsers into more complex patterns and match
binding.values_at(:x, :y).assert == %w(a b)
-Matching of 'foo' against 'foo'.
+
+## Binding to Values
+
+As a side note, our parser should also respect literal value matches in the pattern and only bind to subsequent locations when the values match up.
+
+ binding = Accelerator.match(
+ str('a') >> str('b'),
+ Accelerator.str(:x) >> Accelerator.str(:x))
+
+ binding.assert == nil
+
+Another property should be that literal strings passed to the pattern should be matched using ===.
+
+ binding = Accelerator.match(
+ str('abc') >> str('bcd'),
+ Accelerator.str(/b/) >> Accelerator.str('bcd'))
+
+ binding.assert == {}
+
+The binding is empty here, since no variables were given. But lets also implement constrained variable bindings, that seems useful. The way this works is that you specify a variable you want to bind to first, and then a list of constraints that are matched by `#===`.
+
+ A = Accelerator
+ A.match(str('abc'), A.str(:x, /c/))[:x].assert == 'abc'
+ A.match(str('abc'), A.str(:x, /d/)).assert == nil
+
+ A.match(str('abc'), A.str(:x, /a/, /c/))[:x].assert == 'abc'
+ A.match(str('abc'), A.str(:x, /a/, /d/)).assert == nil
+
+Here's a quick demonstration that demonstrates that this feature equally applies to both `Accelerator.re` and `Parslet.match`.
+
+ A.match(match['abc'], A.re(:x, /d/)).assert == nil
+ A.match(match['abc'], A.re(:x, /c/))[:x].assert == '[abc]'
+

0 comments on commit 37fe477

Please sign in to comment.
Something went wrong with that request. Please try again.