From 3d7d9cfe69ba2314348a8d1ed82c4ae685ebb963 Mon Sep 17 00:00:00 2001 From: "Elf M. Sternberg" Date: Fri, 10 May 2013 21:04:24 -0700 Subject: [PATCH] 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. --- examples/calc.coffee | 8 ++++---- examples/email-address.coffee | 8 ++++---- examples/json.coffee | 15 +++++++-------- src/reparse.coffee | 16 ++++++++++++---- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/examples/calc.coffee b/examples/calc.coffee index 389b655..f6983be 100644 --- a/examples/calc.coffee +++ b/examples/calc.coffee @@ -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 diff --git a/examples/email-address.coffee b/examples/email-address.coffee index 044e7f0..359ef30 100644 --- a/examples/email-address.coffee +++ b/examples/email-address.coffee @@ -5,12 +5,12 @@ class EmailAddress extends ReParse addressList: => @sepEndBy @address, /^\s*,\s*/ address: => @choice @namedAddress, @bareAddress - namedAddress: => @seq(@phrase, /^\s*/)[2] - bareAddress: => @seq(@word, /^@/, @word).join "" + namedAddress: => @seq(@phrase, /^\s*')[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 diff --git a/examples/json.coffee b/examples/json.coffee index 6b1f3fb..1b50fc7 100644 --- a/examples/json.coffee +++ b/examples/json.coffee @@ -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) - diff --git a/src/reparse.coffee b/src/reparse.coffee index 408e7e4..e81a02c 100644 --- a/src/reparse.coffee +++ b/src/reparse.coffee @@ -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 -