Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

resolve lazy parsers at most once

  • Loading branch information...
commit ec36ed0658b6acbe100e0fe71d86da6269b54490 1 parent 8c52739
Robey Pointer authored
6 README.md
Source Rendered
@@ -15,7 +15,7 @@ an example, from the unit tests:
15 15 parser.string("(").skip(ws).drop(),
16 16 (-> expr),
17 17 parser.string(")").skip(ws).drop()
18   - ].onMatch (e) -> e[0]
  18 + ).onMatch (e) -> e[0]
19 19 atom = number.or(parens)
20 20 term = atom.chain(parser.string("*").or("/").or("%").skip(ws), binary)
21 21 expr = term.chain(parser.string("+").or("-").skip(ws), binary)
@@ -116,8 +116,8 @@ are parsed. it takes a hash of key/value parameters:
116 116 - `first` - the parser to match the first occurance (defaults to `tail` if
117 117 not supplied)
118 118 - `tail` - the parser to match all successive occurances
119   -- `sep` - the parser to match the things separating the items (comma, for
120   - example), or null for no separator
  119 +- `sep` - optional parser to match the things separating the items (comma,
  120 + for example); if missing or null, no separators are parsed
121 121 - `accumulator` - a function to transform the first match result into a
122 122 running accumulator of the parser (defaults to an array containing only
123 123 the first match result)
21 src/packrattle/parser.coffee
@@ -145,9 +145,10 @@ regex = (r) ->
145 145 seq = (parsers...) ->
146 146 parsers = (implicit(p) for p in parsers)
147 147 new Parser parsers[0].message, (state) ->
  148 + parsers = (resolve(p) for p in parsers)
148 149 results = []
149 150 for p in parsers
150   - rv = resolve(p).matcher(state)
  151 + rv = p.matcher(state)
151 152 if not rv.ok then return rv
152 153 if rv.match? then results.push(rv.match)
153 154 state = rv.state
@@ -157,7 +158,8 @@ seq = (parsers...) ->
157 158 optional = (p) ->
158 159 p = implicit(p)
159 160 new Parser p.message, (state) ->
160   - rv = resolve(p).matcher(state)
  161 + p = resolve(p)
  162 + rv = p.matcher(state)
161 163 if rv.ok then return rv
162 164 new Match(state, "")
163 165
@@ -170,10 +172,12 @@ repeat = (p, atLeast = 1, sep = null) ->
170 172 sep = implicit(sep)
171 173 message += " separated by #{sep.message}"
172 174 new Parser message, (state) ->
  175 + p = resolve(p)
  176 + if sep? then sep = resolve(sep)
173 177 count = 0
174 178 results = []
175 179 loop
176   - rv = resolve(p).matcher(state)
  180 + rv = p.matcher(state)
177 181 if not rv.ok
178 182 if count < atLeast then return @fail(state)
179 183 return new Match(state, results)
@@ -181,7 +185,7 @@ repeat = (p, atLeast = 1, sep = null) ->
181 185 results.push(rv.match)
182 186 state = rv.state
183 187 if sep?
184   - rv = resolve(sep).matcher(state)
  188 + rv = sep.matcher(state)
185 189 if not rv.ok
186 190 if count < atLeast then return @fail(state)
187 191 return new Match(state, results)
@@ -208,7 +212,10 @@ foldLeft = (args) ->
208 212 sep = implicit(sep)
209 213 message += " separated by (#{sep.message})"
210 214 new Parser message, (state) ->
211   - rv = resolve(first).matcher(state)
  215 + first = resolve(first)
  216 + if sep? then sep = resolve(sep)
  217 + tail = resolve(tail)
  218 + rv = first.matcher(state)
212 219 if not rv.ok then return @fail(state)
213 220 results = accumulator(rv.match)
214 221 state = rv.state
@@ -216,11 +223,11 @@ foldLeft = (args) ->
216 223 initial_state = state
217 224 sep_match = ""
218 225 if sep?
219   - rv = resolve(sep).matcher(state)
  226 + rv = sep.matcher(state)
220 227 if not rv.ok then return new Match(state, results)
221 228 sep_match = rv.match
222 229 state = rv.state
223   - rv = resolve(tail).matcher(state)
  230 + rv = tail.matcher(state)
224 231 if not rv.ok then return new Match(initial_state, results)
225 232 results = fold(results, sep_match, rv.match)
226 233 state = rv.state
21 test/test_parser.coffee
@@ -218,6 +218,27 @@ describe "Parser", ->
218 218 rv.state.pos.should.equal(8)
219 219 rv.match.should.eql([ "hi", "hi", "hi" ])
220 220
  221 + it "resolves a lazy parser", ->
  222 + p = parser.seq ":", -> /\w+/
  223 + rv = p.exec(":hello")
  224 + rv.state.pos.should.equal(6)
  225 + rv.match[0].should.eql(":")
  226 + rv.match[1][0].should.eql("hello")
  227 +
  228 + it "resolves a lazy parser only once", ->
  229 + count = 0
  230 + p = parser.seq ":", ->
  231 + count++
  232 + parser.regex(/\w+/).onMatch (m) -> m[0].toUpperCase()
  233 + rv = p.exec(":hello")
  234 + rv.state.pos.should.equal(6)
  235 + rv.match.should.eql([ ":", "HELLO" ])
  236 + count.should.equal(1)
  237 + rv = p.exec(":goodbye")
  238 + rv.state.pos.should.equal(8)
  239 + rv.match.should.eql([ ":", "GOODBYE" ])
  240 + count.should.equal(1)
  241 +
221 242 describe "Parser#foldLeft", ->
222 243 it "matches one", ->
223 244 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.