Skip to content
Browse files

resolve lazy parsers at most once

  • Loading branch information...
1 parent 8c52739 commit ec36ed0658b6acbe100e0fe71d86da6269b54490 Robey Pointer committed Jun 12, 2012
Showing with 38 additions and 10 deletions.
  1. +3 −3 README.md
  2. +14 −7 src/packrattle/parser.coffee
  3. +21 −0 test/test_parser.coffee
View
6 README.md
@@ -15,7 +15,7 @@ an example, from the unit tests:
parser.string("(").skip(ws).drop(),
(-> expr),
parser.string(")").skip(ws).drop()
- ].onMatch (e) -> e[0]
+ ).onMatch (e) -> e[0]
atom = number.or(parens)
term = atom.chain(parser.string("*").or("/").or("%").skip(ws), binary)
expr = term.chain(parser.string("+").or("-").skip(ws), binary)
@@ -116,8 +116,8 @@ are parsed. it takes a hash of key/value parameters:
- `first` - the parser to match the first occurance (defaults to `tail` if
not supplied)
- `tail` - the parser to match all successive occurances
-- `sep` - the parser to match the things separating the items (comma, for
- example), or null for no separator
+- `sep` - optional parser to match the things separating the items (comma,
+ for example); if missing or null, no separators are parsed
- `accumulator` - a function to transform the first match result into a
running accumulator of the parser (defaults to an array containing only
the first match result)
View
21 src/packrattle/parser.coffee
@@ -145,9 +145,10 @@ regex = (r) ->
seq = (parsers...) ->
parsers = (implicit(p) for p in parsers)
new Parser parsers[0].message, (state) ->
+ parsers = (resolve(p) for p in parsers)
results = []
for p in parsers
- rv = resolve(p).matcher(state)
+ rv = p.matcher(state)
if not rv.ok then return rv
if rv.match? then results.push(rv.match)
state = rv.state
@@ -157,7 +158,8 @@ seq = (parsers...) ->
optional = (p) ->
p = implicit(p)
new Parser p.message, (state) ->
- rv = resolve(p).matcher(state)
+ p = resolve(p)
+ rv = p.matcher(state)
if rv.ok then return rv
new Match(state, "")
@@ -170,18 +172,20 @@ repeat = (p, atLeast = 1, sep = null) ->
sep = implicit(sep)
message += " separated by #{sep.message}"
new Parser message, (state) ->
+ p = resolve(p)
+ if sep? then sep = resolve(sep)
count = 0
results = []
loop
- rv = resolve(p).matcher(state)
+ rv = p.matcher(state)
if not rv.ok
if count < atLeast then return @fail(state)
return new Match(state, results)
count++
results.push(rv.match)
state = rv.state
if sep?
- rv = resolve(sep).matcher(state)
+ rv = sep.matcher(state)
if not rv.ok
if count < atLeast then return @fail(state)
return new Match(state, results)
@@ -208,19 +212,22 @@ foldLeft = (args) ->
sep = implicit(sep)
message += " separated by (#{sep.message})"
new Parser message, (state) ->
- rv = resolve(first).matcher(state)
+ first = resolve(first)
+ if sep? then sep = resolve(sep)
+ tail = resolve(tail)
+ rv = first.matcher(state)
if not rv.ok then return @fail(state)
results = accumulator(rv.match)
state = rv.state
loop
initial_state = state
sep_match = ""
if sep?
- rv = resolve(sep).matcher(state)
+ rv = sep.matcher(state)
if not rv.ok then return new Match(state, results)
sep_match = rv.match
state = rv.state
- rv = resolve(tail).matcher(state)
+ rv = tail.matcher(state)
if not rv.ok then return new Match(initial_state, results)
results = fold(results, sep_match, rv.match)
state = rv.state
View
21 test/test_parser.coffee
@@ -218,6 +218,27 @@ describe "Parser", ->
rv.state.pos.should.equal(8)
rv.match.should.eql([ "hi", "hi", "hi" ])
+ it "resolves a lazy parser", ->
+ p = parser.seq ":", -> /\w+/
+ rv = p.exec(":hello")
+ rv.state.pos.should.equal(6)
+ rv.match[0].should.eql(":")
+ rv.match[1][0].should.eql("hello")
+
+ it "resolves a lazy parser only once", ->
+ count = 0
+ p = parser.seq ":", ->
+ count++
+ parser.regex(/\w+/).onMatch (m) -> m[0].toUpperCase()
+ rv = p.exec(":hello")
+ rv.state.pos.should.equal(6)
+ rv.match.should.eql([ ":", "HELLO" ])
+ count.should.equal(1)
+ rv = p.exec(":goodbye")
+ rv.state.pos.should.equal(8)
+ rv.match.should.eql([ ":", "GOODBYE" ])
+ count.should.equal(1)
+
describe "Parser#foldLeft", ->
it "matches one", ->
p = parser.foldLeft(tail: parser.regex(/\d+/).onMatch((x) -> x[0]), sep: /\s*,\s*/)

0 comments on commit ec36ed0

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