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