match() shortened to just m(), now accepts strings as well as regexps.
I found myself writing a parser with lots of matches in sequence, and wanted something a bit smarter. This adds two bits of smarts: a shorter name and string handling makes something like @seq(m('{'), @string, m('}'))[1] much easier to read and debug.
This commit is contained in:
parent
3cffc3d44e
commit
3d7d9cfe69
|
@ -13,10 +13,10 @@ class Calc extends ReParse
|
|||
expr: => @chainl @term, @addop
|
||||
term: => @chainl1 @factor, @mulop
|
||||
factor: => @choice @group, @number
|
||||
group: => @between /^\(/, /^\)/, @expr
|
||||
number: => parseFloat @match(/^(\-?\d+(\.\d+)?)/)
|
||||
mulop: => @OPS[@match(/^[\*\/]/)]
|
||||
addop: => @OPS[@match(/^[\+\-]/)]
|
||||
group: => @between '(', ')', @expr
|
||||
number: => parseFloat @m(/^(\-?\d+(\.\d+)?)/)
|
||||
mulop: => @OPS[@m(/^[\*\/]/)]
|
||||
addop: => @OPS[@m(/^[\+\-]/)]
|
||||
|
||||
parse: =>
|
||||
super
|
||||
|
|
|
@ -5,12 +5,12 @@ class EmailAddress extends ReParse
|
|||
|
||||
addressList: => @sepEndBy @address, /^\s*,\s*/
|
||||
address: => @choice @namedAddress, @bareAddress
|
||||
namedAddress: => @seq(@phrase, /^\s*</m, @bareAddress, /^>/)[2]
|
||||
bareAddress: => @seq(@word, /^@/, @word).join ""
|
||||
namedAddress: => @seq(@phrase, /^\s*</m, @bareAddress, '>')[2]
|
||||
bareAddress: => @seq(@word, '@', @word).join ""
|
||||
phrase: => @many @word
|
||||
word: => @skip(/^\s+/).choice @quoted, @dottedAtom
|
||||
quoted: => @match /^"(?:\\.|[^"\r\n])+"/m
|
||||
dottedAtom: => @match /^[!#\$%&'\*\+\-\/\w=\?\^`\{\|\}~]+(?:\.[!#\$%&'\*\+\-\/\w=\?\^`\{\|\}~]+)*/m
|
||||
quoted: => @m /^"(?:\\.|[^"\r\n])+"/m
|
||||
dottedAtom: => @m /^[!#\$%&'\*\+\-\/\w=\?\^`\{\|\}~]+(?:\.[!#\$%&'\*\+\-\/\w=\?\^`\{\|\}~]+)*/m
|
||||
|
||||
parse: =>
|
||||
super
|
||||
|
|
|
@ -10,16 +10,16 @@ class ReJSON extends ReParse
|
|||
STRING = {"\"": 34, "\\": 92, "/": 47, 'b': 8, 'f': 12, 'n': 10, 'r': 13, 't': 9}
|
||||
|
||||
value: => @choice @literal, @string, @number, @array, @object
|
||||
object: => @between(/^\{/, /^\}/, @members).reduce ((obj, pair) => obj[pair[0]] = pair[2]; obj), {}
|
||||
members: => @sepBy @pair, /^,/
|
||||
pair: => @seq @string, /^:/, @value
|
||||
array: => @between /^\[/, /^\]/, @elements
|
||||
object: => @between('{', '}', @members).reduce ((obj, pair) => obj[pair[0]] = pair[2]; obj), {}
|
||||
members: => @sepBy @pair, ','
|
||||
pair: => @seq @string, ':', @value
|
||||
array: => @between '[', ']', @elements
|
||||
elements: => @sepBy @value, /^,/
|
||||
literal: => LITERAL[@match(/^(true|false|null)/)]
|
||||
number: => parseFloat @match(/^\-?\d+(?:\.\d+)?(?:[eE][\+\-]?\d+)?/)
|
||||
literal: => LITERAL[@m(/^(true|false|null)/)]
|
||||
number: => parseFloat @m(/^\-?\d+(?:\.\d+)?(?:[eE][\+\-]?\d+)?/)
|
||||
|
||||
string: =>
|
||||
chars = @match(/^"((?:\\["\\/bfnrt]|\\u[0-9a-fA-F]{4}|[^"\\])*)"/)
|
||||
chars = @m(/^"((?:\\["\\/bfnrt]|\\u[0-9a-fA-F]{4}|[^"\\])*)"/)
|
||||
chars.replace /\\(["\\/bfnrt])|\\u([0-9a-fA-F]{4})/g, (_, $1, $2) =>
|
||||
String.fromCharCode (if $1 then STRING[$1] else parseInt($2, 16)) # "
|
||||
|
||||
|
@ -48,4 +48,3 @@ jsonparse = new ReJSON()
|
|||
time "JSON", 1000, => JSON.parse input
|
||||
time "PEG.js", 1000, => peg.parse input
|
||||
time "ReParse", 1000, => jsonparse.parse(input)
|
||||
|
||||
|
|
|
@ -44,7 +44,10 @@ exports.ReParse = class ReParse
|
|||
# Execute a production, which could be a function or a RegExp.
|
||||
|
||||
produce: (method) =>
|
||||
val = if (method instanceof RegExp) then @match(method) else method.call(@)
|
||||
val = if ((method instanceof RegExp) or (typeof method == 'string'))
|
||||
@m(method)
|
||||
else
|
||||
method.call(@)
|
||||
@skipWS() if @ignorews
|
||||
val
|
||||
|
||||
|
@ -75,7 +78,13 @@ exports.ReParse = class ReParse
|
|||
#
|
||||
# Note that the `return fail()` call eventually leads to a throw.
|
||||
|
||||
match: (pattern, putback = false) =>
|
||||
m: (pattern, putback = false) =>
|
||||
if typeof pattern == 'string'
|
||||
if @input.substr(0, pattern.length) == pattern
|
||||
@input = @input.substr(pattern.length)
|
||||
return pattern
|
||||
return @fail()
|
||||
|
||||
probe = @input.match pattern
|
||||
return @fail() unless probe
|
||||
@input = @input.substr (if probe[1]? and putback then probe[1].length else probe[0].length)
|
||||
|
@ -181,7 +190,7 @@ exports.ReParse = class ReParse
|
|||
# will not skip carriage returns or linefeeds.
|
||||
|
||||
skipWS: =>
|
||||
@match(/^\s*/)
|
||||
@m(/^\s*/)
|
||||
@
|
||||
|
||||
# Returns an array of `min` values produced by `method`.
|
||||
|
@ -292,4 +301,3 @@ exports.ReParse = class ReParse
|
|||
# if there are zero productions.
|
||||
|
||||
chainl1: (method, op) => @chainl method, op, null, 1
|
||||
|
||||
|
|
Loading…
Reference in New Issue