From 985688aed8715d76c6c3307fb1c61087c36bf3ad Mon Sep 17 00:00:00 2001 From: Simeon Chaos Date: Wed, 28 Aug 2013 16:30:14 +0800 Subject: [PATCH] =?UTF-8?q?0.2.7=EF=BC=9A=20remove=20the=20parameter=20`st?= =?UTF-8?q?art`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### what's new in 0.2.7 * remove the parameter `start` from the matcher functions * `orp` play well in testpeasy --- README.md | 6 +- doc/peasy.html | 512 ++++++----------------- lib/logicpeasy.coffee | 468 +++++++++++++++++++++ lib/logicpeasy.js | 917 ++++++++++++++++++++++++++++++++++++++++++ lib/peasy.coffee | 298 +++++--------- lib/peasy.js | 393 +++++------------- package.json | 2 +- test/testpeasy.coffee | 57 ++- test/testpeasy.js | 118 +++--- whatsnew.md | 4 + 10 files changed, 1819 insertions(+), 956 deletions(-) create mode 100644 lib/logicpeasy.coffee create mode 100644 lib/logicpeasy.js diff --git a/README.md b/README.md index 6c671b6..64e5b2a 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ You just play [one ball like so](https://raw.github.com/chaosim/peasy/master/doc To use Peasy, just require or copy the module to your project, read it, modify it, write the grammar rules,, and remove any unnecessary stuffs in Peasy, and parse with the grammar.
-### what's new in 0.2.6 - * better support to modular grammar - * simpler method to declare left recursive rule and memorized rule +### what's new in 0.2.7 + * remove the parameter `start` from the matcher functions + * `orp` play well in testpeasy ### Documentation The [annotated peasy.coffee](http://chaosim.github.io/peasy/doc/peasy.html) is the best document for Peasy at the moment.
diff --git a/doc/peasy.html b/doc/peasy.html index 280c1c5..b3003e2 100644 --- a/doc/peasy.html +++ b/doc/peasy.html @@ -164,7 +164,9 @@
Nothing but the method in Peasy is indispensable:
isMatcher = (item) ->  typeof(item)=="function"
 
-exports.makeInfo = makeInfo = (data) -> {data:data, cursor:0, parsingLeftRecursives: {}, parseCache: {}}
+exports.makeInfo = makeInfo = (data, options={cursor:0, tabWidth:2}) ->
+  {data:data, cursor:options.cursor or 0, tabWidth: options.tabWidth or 2,
+  parsingLeftRecursives: {}, parseCache: {}}
 
 memoSymbolIndex = 0
@@ -179,7 +181,7 @@
Nothing but the method in Peasy is indispensable:

recursive: this is the key function to left recursive.
Make @symbol a left recursive symbol, which means to wrap @originalRules[symbol] with recursive. -when recursiv(info, rules, symbol)(start) is executed, loop computing rule(start) until no changes happened

+when recursiv(info, rules, symbol)() is executed, loop computing rule() until no changes happened

@@ -189,13 +191,15 @@
Nothing but the method in Peasy is indispensable:
memoSymbolIndex++ parsingLeftRecursives = info.parsingLeftRecursives parseCache = info.parseCache[tag] = {} - (start) -> + -> + start = info.cursor callPath = parsingLeftRecursives[start] ?= [] if tag not in callPath callPath.push(tag) m = parseCache[start] ?= [undefined, start] while 1 - result = rule(start) + info.cursor = start + result = rule() if not result then result = m[0]; info.cursor = m[1]; break if m[1]==info.cursor then m[0] = result; break else m[0] = result; m[1] = info.cursor @@ -225,11 +229,12 @@
Nothing but the method in Peasy is indispensable:
tag = index+':' memoSymbolIndex++ parseCache = info.parseCache[tag] = {} - (start) -> + -> + start = info.cursor m = parseCache[start] if m then info.cursor = m[1]; m[0] else - result = rule(start) + result = rule() parseCache[start] = [result, info.cursor] result @@ -259,7 +264,7 @@

matchers and combinators

All matcher should return truth value on succeed, and return falsic value on fail.
A combinator is a function which receive zero or more matchers as parameter(maybe plus some other parameters which are not matchers), and generate a new matchers.
-there are other matcher generator besides the standard combinators described as above, like recursive, memo, memorize, +there are other matcher generator besides the standard combinators described as above, like recursive, memorize, which we have met above.

@@ -274,23 +279,22 @@

matchers and combinators

combinator andp
-execute item(info.cursor) in sequence, return the result of the last one.
+execute item() in sequence, return the result of the last one.
when andp is used to combined of the matchers, the effect is the same as by using the Short-circuit evaluation, like below:
-item1(start) and item2(info.cursor] ... and itemn(info.cursor).
-In fact, you maybe would rather like to use item1(start) and item2(info.cursor) .. when you write the grammar rule in the +item1() and item2() ... and itemn().
+In fact, you maybe would rather like to use item1() and item2(info.cursor) .. when you write the grammar rule in the manner of Peasy. That would be simpler, faster and more elegant.
And in that manner, you would have more control on your grammar rule, say like below:
-if (x=item1(start) and (x>100) and item2(info.cursor) and not item3(info.cursor) and (y = item(info.cursor)) then doSomething()

+if (x=item1() and (x>100) and item2() and not item3() and (y = item()) then doSomething()

exports.andp = andp = (info) -> (items...) ->
   items = for item in items
     if not isMatcher(item) then literal(info)(item) else item
-  (start) ->
-    info.cursor = start
+  ->
     for item in items
-      if not(result = item(info.cursor)) then return
+      if not (result = item()) then return
     result
@@ -302,24 +306,18 @@

matchers and combinators

-

combinator orp
-execute item(start) one by one, until the first item which is evaluated to truth value and return the value.
-when orp is used to combined of the matchers, the effect is the same as by using the Short-circuit evaluation, like below:
-item1(start) or item2(info.cursor] ... or itemn(info.cursor)
-In fact, you maybe would rather like to use item1(start) or item2(info.cursor) .. when you write the grammar rule in the -manner of Peasy. That would be simpler, faster and more elegant.
-And in that manner, you would have more control on your grammar rule, say like below:
-if ((x=item1(start) and (x>100)) or (item2(info.cursor) and not item3(info.cursor)) or (y = item(info.cursor)) then doSomething()

+

combinator orp

exports.orp = orp = (info) -> (items...) ->
   items = for item in items
     if not isMatcher(item) then literal(info)(item) else item
-  (start) ->
+  ->
+    start = info.cursor
     for item in items
       info.cursor = start
-      if result = item(start) then return result
+      if result = item() then return result
     result
@@ -333,13 +331,13 @@

matchers and combinators

combinator notp
notp is not useful except being used in other combinators, just like this = andp(item1, notp(item2)).
-It's unnessary, low effecient and ugly to write notp(item)(start), just write not item(start).

+It's unnessary, low effecient and ugly to write notp(item)(), just write not item().

exports.notp = notp = (info) -> (item) ->
   if not isMatcher(item) then item = literal(info)(item)
-  (start) -> not item(start)
+ -> not item() @@ -351,15 +349,15 @@

matchers and combinators

combinator may: a.k.a optional
-try to match item(info.cursor), wether item(info.cursor) succeed or not, maybe(item)(start) succeed.

+try to match item(), wether item() succeed or not, maybe(item)() succeed.

exports.may = may = (info) -> (item) ->
   if not isMatcher(item) then item = literal(info)(item)
-  (start) ->
-    info.cursor = start
-    if x = item(info.cursor) then x
+  ->
+    start = info.cursor
+    if x = item() then x
     else info.cursor = start; true
@@ -371,15 +369,15 @@

matchers and combinators

-

combinator any: zero or more times of item(info.cursor)

+

combinator any: zero or more times of item()

exports.any = any = (info) -> (item) ->
   if not isMatcher(item) then item = literal(info)(item)
-  (start) ->
-    result = []; info.cursor = start
-    while ( x = item(info.cursor)) then result.push(x)
+  ->
+    result = []
+    while (x = item()) then result.push(x)
     result
@@ -391,19 +389,16 @@

matchers and combinators

-

combinator some: one or more times of item(info.cursor)

+

combinator some: one or more times of item()

exports.some = some = (info) -> (item) ->
   if not isMatcher(item) then item = literal(info)(item)
-  (start) ->
-    result = []; info.cursor = start
-    if not (x = item(info.cursor)) then return x
-    while 1
-      result.push(x)
-      x = item(info.cursor)
-      if not x then break
+  ->
+    if not (x = item()) then return
+    result = [x]
+    while (x = item()) then result.push(x)
     result
@@ -415,16 +410,16 @@

matchers and combinators

-

combinator times: match @n times item(info.cursor), n>=1

+

combinator times: match @n times item(), n>=1

exports.times = times = (info) -> (item, n) ->
   if not isMatcher(item) then item = literal(info)(item)
-  (start) ->
-    info.cursor = start; i = 0
+  ->
+    i = 0
     while i++<n
-      if x = item(info.cursor) then result.push(x)
+      if x = item() then result.push(x)
       else return
     result
@@ -437,21 +432,17 @@

matchers and combinators

-

combinator seperatedList: some times item(info.cursor), separated by info.separator

+

combinator seperatedList: some times item(), separated by info.separator

-
exports.seperatedList = seperatedList = (info) -> (item, separator=spaces) ->
+            
exports.seperatedList = seperatedList = (info) -> (item, separator=spaces(info)) ->
   if not isMatcher(item) then item = literal(info)(item)
   if not isMatcher(separator) then separator = literal(info)(separator)
-  (start) ->
-    info.cursor = start
-    result = []
-    x = item(info.cursor)
-    if not x then return
-    while 1
-      result.push(x)
-      if not(x = item(info.cursor)) then break
+  ->
+    if not (x = item()) then return
+    result = [x]
+    while separator() and (x=item()) then result.push(x)
     result
@@ -467,18 +458,16 @@

matchers and combinators

-
exports.timesSeperatedList = timesSeperatedList = (info) -> (item, n, separator=spaces) ->
+            
exports.timesSeperatedList = timesSeperatedList = (info) -> (item, n, separator=spaces(info)) ->
   if not isMatcher(item) then item = literal(info)(item)
   if not isMatcher(separator) then separator = literal(info)(separator)
-  (start) ->
-    info.cursor = start
-    result = []
-    x = item(info.cursor)
-    if not x then return
+  ->
+    if not (x = item()) then return
+    result = [x]
     i = 1
     while i++<n
-      result.push(x)
-      if not(x = item(info.cursor)) then break
+      if separator() and (x=item()) then result.push(x)
+      else return
     result
@@ -496,14 +485,15 @@

matchers and combinators

exports.follow = follow = (info) -> (item) ->
   if not isMatcher(item) then item = literal(info)(item)
-  (start) ->
-    info.cursor = start
-    if x = item(info.cursor) then info.cursor = start; x
+  ->
+    start = info.cursor
+    if x = item() then  info.cursor = start; x
+    else info.cursor = start; return
 
 exports.combinators = combinators = (info) ->
   rec: recursive(info), memo: memorize(info),
   andp: andp(info), orp: orp(info), notp: notp(info)
-  may: may(info), any: any(info), some: some(infor), times: times(info)
+  may: may(info), any: any(info), some: some(info), times: times(info)
   seperatedList: seperatedList(info), timesSeperatedList: timesSeperatedList(info)
   follow: follow(info)
@@ -579,7 +569,7 @@

matchers and combinators

other predicates, matchers, and combinators

-

below is some little utilities which may be useful. Some of them is provided with both normal and faster version.
+

below is some little utilities which may be useful.
just remove them if you don't need them, except literal, which is depended by the combinators above.

@@ -613,14 +603,15 @@

other predicates, matchers, and combinators

-

matcher literal, normal version
+

matcher literal
match a text string.
`notice = some combinators like andp, orp, notp, any, some, etc. use literal to wrap a object which is not a matcher.

-
exports.literal = literal = (info) -> (string) -> (start) ->
+            
exports.literal = literal = (info) -> (string) -> ->
   len = string.length
+  start = info.cursor
   if info.data.slice(start, stop = start+len)==string then info.cursor = stop; true
@@ -632,14 +623,11 @@

other predicates, matchers, and combinators

-

matcher literal_, faster version
-match a text string.

+

matcher char: match one character

-
exports.literal_ = literal_ = (info) -> (string) -> (start) ->
-  len = string.length
-  if info.data.slice(info.cursor,  stop = info.cursor+len)==string then info.cursor = stop; true
+
exports.char = char = (info) -> (c) -> -> if info.data[info.cursor]==c then info.cursor++; return c
@@ -650,39 +638,7 @@

other predicates, matchers, and combinators

-

matcher char, normal version
-match one character

- -
- -
exports.char = char = (info) -> (c) -> (start) -> if info.data[start]==c then info.cursor = start+1; return c
- - - - -
  • -
    - -
    - -
    -

    matcher char_, normal version
    -match one character

    - -
    - -
    exports.char_ = char_ = (info) -> (c) -> () -> if info.data[info.cursor]==c then info.cursor++; return c
    - -
  • - - -
  • -
    - -
    - -
    -

    In spaces, spaces, spaces1, spaces1, a tat('\t') is seen as tabWidth spaces,
    +

    In spaces, spaces1, a tab('\t') is computed as info.tabWidth spaces,
    which is used in indent style language, such as coffeescript, python, haskell, etc.
    If you don't need this feature, you can easily rewrite these utilities to remove the code about tab width yourself.

    @@ -691,130 +647,83 @@

    other predicates, matchers, and combinators

  • -
  • -
    - -
    - -
    -

    matcher spaces, normal version
    -zero or more whitespaces, ie. space or tab.

    - -
    - -
    exports.spaces = spaces = (info) -> (start) ->
    -  len = 0
    -  info.cursor = start
    -  while 1
    -    switch info.data[info.cursor++]
    -      when ' ' then len++
    -      when '\t' then len += tabWidth
    -      else break
    -  return len
    - -
  • - - -
  • +
  • - +
    -

    matcher spaces_, faster version
    -zero or more whitespaces, ie. space or tab.

    +

    matcher spaces: zero or more whitespaces, ie. space or tab.

    -
    exports.spaces_ = spaces_ = (info) -> () ->
    +            
    exports.spaces = spaces = (info) -> ->
    +  data = info.data
       len = 0
    +  cursor = info.cursor
    +  tabWidth = info.tabWidth
       while 1
    -    switch info.data[info.cursor++]
    +    switch data[cursor++]
           when ' ' then len++
           when '\t' then len += tabWidth
           else break
    -  len
    + info.cursor = cursor + len+1
  • -
  • +
  • - +
    -

    matcher spaces1, normal version
    +

    matcher spaces1
    one or more whitespaces, ie. space or tab.

    -
    exports.spaces1 = spaces1 = (info) -> (start) ->
    -  len = 0
    -  info.cursor = start
    -  while 1
    -    switch info.data[info.cursor++]
    -      when ' ' then len++
    -      when '\t' then len += tabWidth
    -      else break
    -  if len then return info.cursor = info.cursor; len
    - -
  • - - -
  • -
    - -
    - -
    -

    matcher spaces1_, faster version
    -one or more whitespaces, ie. space or tab.

    - -
    - -
    exports.spaces1_ = spaces1_ = (info) -> () ->
    +            
    exports.spaces1 = spaces1 = (info) -> ->
    +  data = info.data
    +  cursor = info.cursor
       len = 0
    -  info.cursor = start
    +  tabWidth = info.tabWidth
       while 1
    -    switch info.data[info.cursor++]
    +    switch data[cursor++]
           when ' ' then len++
           when '\t' then len += tabWidth
           else break
    -  if len then return info.cursor = info.cursor; len
    + info.cursor = cursor + len
  • -
  • +
  • - +
    -

    matcher wrap, normal version
    +

    matcher wrap
    match left, then match item, match right at last

    exports.wrap = wrap = (info) -> (item, left=spaces(info), right=spaces(info)) ->
       if not isMatcher(item) then item = literal(info)(item)
    -  (start) ->
    -     if left(start) and result = item(info.cursor) and right(info.cursor) then result
    -
    -exports.wrap_ = wrap_ = (info) -> (item, left=spaces(info), right=spaces(info)) ->
    -  if not isMatcher(item) then item = literal(info)(item)
    -  () ->
    -    if left(info.cursor) and result = item(info.cursor) and right(info.cursor) then result
    + -> + if left() and result = item() and right() then result
  • -
  • +
  • - +

    matcher identifierLetter = normal version
    is a letter than can be used in identifer?
    @@ -822,28 +731,7 @@

    other predicates, matchers, and combinators

    -
    exports.identifierLetter = identifierLetter = (info) -> (start) ->
    -  start = info.cursor
    -  c = info.data[info.cursor]
    -  if c is '@' or c is '_' or 'a'<=c<'z' or 'A'<=c<='Z' or '0'<=c<='9'
    -    info.cursor++; true
    - -
  • - - -
  • -
    - -
    - -
    -

    matcher identifierLetter_, version
    -is a letter that can be used in identifer?
    -javascript style, '@' is a identifierLetter_

    - -
    - -
    exports.identifierLetter_ = identifierLetter_ = (info) -> () ->
    +            
    exports.identifierLetter = identifierLetter = (info) -> ->
       c = info.data[info.cursor]
       if c is '@' or c is '_' or 'a'<=c<'z' or 'A'<=c<='Z' or '0'<=c<='9'
         info.cursor++; true
    @@ -851,11 +739,11 @@

    other predicates, matchers, and combinators

  • -
  • +
  • - +

    matcher followIdentifierLetter_, faster version
    lookahead whether the following character is a letter used in identifer. don't change info.cursor.
    @@ -863,186 +751,46 @@

    other predicates, matchers, and combinators

    -
    exports.followIdentifierLetter_ = followIdentifierLetter_ = (info) -> () ->
    +            
    exports.followIdentifierLetter_ = followIdentifierLetter_ = (info) -> ->
       c = info.data[info.cursor]
    -  c is '@' or c is '_' or 'a'<=c<'z' or 'A'<=c<='Z' or '0'<=c<='9'
    - -
  • - - -
  • -
    - -
    - -
    -

    matcher digit_, normal version

    - -
    - -
    exports.digit = digit = (info) -> (start) -> c = info.data[start];  if '0'<=c<='9' then info.cursor = start+1
    - -
  • - - -
  • -
    - -
    - -
    -

    matcher digit_, faster version

    - -
    - -
    exports.digit_ = digit_ = (info) -> () -> c = info.data[info.cursor];  if '0'<=c<='9' then info.cursor++
    - -
  • - - -
  • -
    - -
    - -
    -

    matcher letter, normal version

    - -
    - -
    exports.letter = letter = (info) -> (start) -> c = info.data[start]; if 'a'<=c<='z' or 'A'<=c<='Z' then info.cursor = start+1
    - -
  • - - -
  • -
    - -
    - -
    -

    matcher letter, faster version

    - -
    - -
    exports.letter_ = letter_ = (info) -> () -> c = info.data[info.cursor]; if 'a'<=c<='z' or 'A'<=c<='Z' then info.cursor++
    - -
  • - - -
  • -
    - -
    - -
    -

    matcher lower, normal version

    - -
    - -
    exports.lower = lower = (info) -> (start) -> c = info.data[start]; if 'a'<=c<='z' then info.cursor = start+1
    - -
  • - - -
  • -
    - -
    - -
    -

    matcher lower_, faster version

    - -
    - -
    exports.lower_ = lower_ = (info) -> () -> c = info.data[info.cursor]; if 'a'<=c<='z' then info.cursor++
    - -
  • - - -
  • -
    - -
    - -
    -

    matcher upper, normal version

    - -
    - -
    exports.upper = upper = (info) -> (start) ->  c = info.data[start]; if 'A'<=c<='Z' then info.cursor = start+1
    - -
  • - - -
  • -
    - -
    - -
    -

    matcher upper_, faster version

    + (c is '@' or c is '_' or 'a'<=c<'z' or 'A'<=c<='Z' or '0'<=c<='9') and c -
    - -
    exports.upper_ = upper_ = (info) -> () -> c = info.data[info.cursor]; if 'A'<=c<='Z' then info.cursor++
    - -
  • - - -
  • -
    - -
    - -
    -

    matcher identifier, normal version

    - -
    - -
    exports.identifier = identifier = (info) -> (start) ->
    -  info.cursor = start
    -  c = info.data[info.cursor]
    -  if 'a'<=c<='z' or 'A'<=c<='Z' or 'c'=='@' or 'c'=='_' then info.cursor++
    -  else return
    -  while 1
    -    c = info.data[info.cursor]
    -    if 'a'<=c<='z' or 'A'<=c<='Z' or '0'<=c<='9' or 'c'=='@' or 'c'=='_' then info.cursor++
    -    else break
    -  true
    +exports.digit = digit = (info) -> -> c = info.data[info.cursor]; if '0'<=c<='9' then info.cursor++; c +exports.letter = letter = (info) -> -> c = info.data[info.cursor]; if 'a'<=c<='z' or 'A'<=c<='Z' then info.cursor++; c +exports.lower = lower = (info) -> -> c = info.data[info.cursor]; if 'a'<=c<='z' then info.cursor++; c +exports.upper = upper = (info) -> -> c = info.data[info.cursor]; if 'A'<=c<='Z' then info.cursor++; c
  • -
  • +
  • - +
    -

    matcher identifier_, faster version

    +

    matcher identifier

    -
    exports.identifier_ = identifier_ = (info) -> () ->
    -  c = info.data[info.cursor]
    -  if 'a'<=c<='z' or 'A'<=c<='Z' or 'c'=='@' or 'c'=='_' then info.cursor++
    +            
    exports.identifier = identifier = (info) -> ->
    +  data = info.data
    +  cursor = info.cursor
    +  c = data[cursor]
    +  if 'a'<=c<='z' or 'A'<=c<='Z' or 'c'=='@' or 'c'=='_' then cursor++
       else return
       while 1
    -    c = info.data[info.cursor]
    -    if 'a'<=c<='z' or 'A'<=c<='Z' or '0'<=c<='9' or 'c'=='@' or 'c'=='_' then info.cursor++
    +    c = data[cursor]
    +    if 'a'<=c<='z' or 'A'<=c<='Z' or '0'<=c<='9' or 'c'=='@' or 'c'=='_' then cursor++
         else break
    +  info.cursor = cursor
       true
     
     exports.matchers = matchers = (info) ->
    -  literal: literal(info), literal_: literal_(info), char: char(info), char_: char_(info)
    -  spaces: spaces(info), spaces_: spaces_(info),spaces1: spaces1(info),spaces1_: spaces1_(info)
    -  wrap: wrap(info), wrap_: wrap_(info),
    -  identifierLetter: identifierLetter(info), identifierLetter_: identifierLetter_(info)
    -  followIdentifierLetter_: followIdentifierLetter_(info)
    -  digit: digit(info), digit_: digit_(info), letter: letter(info), letter_: letter_(info),
    -  lower: lower(info), lower_: lower_(info), upper: upper(info), upper_: upper_(info)
    -  identifier: identifier(info), identifier_: identifier_(info)
    +  literal: literal(info), char: char(info), spaces: spaces(info), spaces1: spaces1(info), wrap: wrap(info),
    +  identifierLetter: identifierLetter(info), followIdentifierLetter: followIdentifierLetter(info)
    +  digit: digit(info), letter: letter(info), lower: lower(info), upper: upper(info),
    +  identifier: identifier(info)
     
     exports.digits = digits = (info) ->
       ch = char(info)
    @@ -1063,11 +811,11 @@ 

    other predicates, matchers, and combinators

  • -
  • +
  • - +

    The utilites above is just for providing some examples on how to write matchers for Peasy.
    In fact, It's realy easy peasy to write the matchers for your grammar rule yourself.
    @@ -1078,11 +826,11 @@

    other predicates, matchers, and combinators

  • -
  • +
  • - +

    parse: this is a sample parse function to demonstrate on how to write your own grammar rules yourself.
    @@ -1096,11 +844,11 @@

    other predicates, matchers, and combinators

  • -
  • +
  • - +

    generate the matchers by the combinators in advance for better performance.
    if you don't mind performance, you can write them in the rule directly.

    @@ -1108,28 +856,26 @@

    other predicates, matchers, and combinators

        {a, b, x, y} = letters(info)
    -    rec = recursive(info) # or {rec, memo, andp, orp} = combinators(info)
    + {rec, orp} = combinators(info)
  • -
  • +
  • - +

    the grammar rules object.

        rules =
    -      Root: (start) -> (m = rules.A(start)) and z(info.cursor) and m+'z'
    -      A: rec (start) ->
    -        (m =  rules.B(start)) and x(info.cursor) and m+'x' or m\
    -        or a(start)
    -      B: rec (start) ->(m = rules.A(start))  and y(info.cursor) and m+'y'or rules.C(start)
    -      C: rec (start) -> rules.A(start) or b(start)
    +      Root: -> (m = rules.A()) and z() and m+'z'
    +      A: rec orp((-> (m =  rules.B()) and x() and m+'x' or m), a)
    +      B: rec orp((-> (m = rules.A())  and y() and m+'y'), -> rules.C())
    +      C: rec orp((-> rules.A()), b)
       grammar = makeGrammar(makeInfo(text))
       grammar.Root(0)
    diff --git a/lib/logicpeasy.coffee b/lib/logicpeasy.coffee new file mode 100644 index 0000000..2a09bdb --- /dev/null +++ b/lib/logicpeasy.coffee @@ -0,0 +1,468 @@ +# # Peasy +# ###### Peasy means parsing is easy +# ###### an easy but powerful parser + +# To use Peasy, just require or copy this file to your project, read it, modify it, write the grammar rules,, and +# remove any unnecessary stuffs in it, and parse with the data.@
    +# See [here](#peasysample) for a sample grammar in Peasy.
    + +# With Peasy, you write the parser by hand, just like to write any other kind of program.
    +# You need not play many balls like that any more:

    +# ![ballacrobatics.jpg](https://raw.github.com/chaosim/peasy/master/doc/ballacrobatics.jpg)
    +# You just play one ball like so:

    +# ![dolphinball.jpg](https://raw.github.com/chaosim/peasy/master/doc/dolphinball.jpg)
    , + +# You can embeded other features in the grammar rules seamless, such as lexer, rewriter, semantic action, error +# process or any other tasks; you can dynamicly modify the grammar rules, even the parsed object, if you wish.
    + +# Instead of a tool or a library, Peasy is rather a new method to write parser. As a demonstration, Peasy presents +# itself as a module of single file, which includes some functions to process left recursives symbols, some matchers +# which parse text, and some matchers and cominators.

    + +# With the method provided by Peasy, you can parse any object which is not only text, but also array, embedded list, +# binary stream, or other data structures, like tree, graph, and so on.
    + +# ###### Nothing but the method in Peasy is indispensable:
    + +# * If there is no left recursive symbol, you can remove the code about that(mainly refer to the function `recursive` and 20 lines).
    +# * If you don't need memorize the parsed result, you can remove the stuffs about +# memorization(mainly refer to the function `memorize`).
    +# * The matchers(e.g. spaces, literal, identifier, etc.) and combinators(e.g. andp, orp, etc) in this module exists just +# for demostrating the method initiated by Peasy. You will see they are all very simple, after seeing them, I bet +# that you would rather to remove them and write them by hand yourself when you write the grammar rules.

    + +# The notation *@name* below means a parameter.
    +# all occurences of *@info* refer to the parsed object and the cursor, e.g. it's initial value can be {data: text, cursor:0} + +# A *matcher* is a function which matches the data being parsed and move cursor directly.
    +isMatcher = (item) -> typeof(item)=="function" + +exports.makeInfo = makeInfo = (data) -> {data:data, cursor:0, parsingLeftRecursives: {}, parseCache: {}} + +memoSymbolIndex = 0 + +# *recursive*: this is the key function to left recursive.
    +# Make *@symbol* a left recursive symbol, which means to wrap `@originalRules[symbol]` with recursive. +# when recursiv(info, rules, symbol)(start) is executed, loop computing rule(start) until no changes happened +exports.recursive = recursive = (info) -> (rule) -> + index = memoSymbolIndex + tag = index+':' + memoSymbolIndex++ + parsingLeftRecursives = info.parsingLeftRecursives + parseCache = info.parseCache[tag] = {} + (start) -> + callPath = parsingLeftRecursives[start] ?= [] + if tag not in callPath + callPath.push(tag) + m = parseCache[start] ?= [undefined, start] + while 1 + result = rule(start) + if not result then result = m[0]; info.cursor = m[1]; break + if m[1]==info.cursor then m[0] = result; break + else m[0] = result; m[1] = info.cursor + callPath.pop() + result + else + m = parseCache[start] + info.cursor = m[1] + m[0] + +# *memorize*: memorize result and @cursor for *@symbol* which is not left recursive.
    +# *The symbols which is left recursive should be wrapped by `recursive(symbol)`, not `memorize(symbol)`!!!* +exports.memorize = memorize = (info) -> (rule) -> + index = memoSymbolIndex + tag = index+':' + memoSymbolIndex++ + parseCache = info.parseCache[tag] = {} + (start) -> + m = parseCache[start] + if m then info.cursor = m[1]; m[0] + else + result = rule(start) + parseCache[start] = [result, info.cursor] + result + +# #### matchers and combinators
    + +# A *matcher* is a function which matches the data being parsed and move cursor directly.
    +# All matcher should return truth value on succeed, and return falsic value on fail.
    +# A *combinator* is a function which receive zero or more matchers as parameter(maybe plus some other parameters +# which are not matchers), and generate a new matchers.
    +# there are other matcher generator besides the standard combinators described as above, like `recursive`, `memo`, `memorize`, +# which we have met above. + +# combinator *andp*
    +# execute item(info.cursor) in sequence, return the result of the last one.
    +# when `andp` is used to combined of the matchers, the effect is the same as by using the Short-circuit evaluation, like below:
    +# `item1(start) and item2(info.cursor] ... and itemn(info.cursor).`
    +# In fact, you maybe would rather like to use `item1(start) and item2(info.cursor) ..` when you write the grammar rule in the +# manner of Peasy. That would be simpler, faster and more elegant.
    +# And in that manner, you would have more control on your grammar rule, say like below:
    +# `if (x=item1(start) and (x>100) and item2(info.cursor) and not item3(info.cursor) and (y = item(info.cursor)) then doSomething()` +exports.andp = andp = (info) -> (items...) -> + items = for item in items + if not isMatcher(item) then literal(info)(item) else item + (start) -> + info.cursor = start + for item in items + if not(result = item(info.cursor)) then return + result + +# combinator *orp*
    +# execute `item(start)` one by one, until the first item which is evaluated to truth value and return the value.
    +# when orp is used to combined of the matchers, the effect is the same as by using the Short-circuit evaluation, like below:
    +# item1(start) or item2(info.cursor] ... or itemn(info.cursor)
    +# In fact, you maybe would rather like to use `item1(start) or item2(info.cursor) ..` when you write the grammar rule in the +# manner of Peasy. That would be simpler, faster and more elegant.
    +# And in that manner, you would have more control on your grammar rule, say like below:
    +# `if ((x=item1(start) and (x>100)) or (item2(info.cursor) and not item3(info.cursor)) or (y = item(info.cursor)) then doSomething()` +exports.orp = orp = (info) -> (items...) -> + items = for item in items + if not isMatcher(item) then literal(info)(item) else item + (start) -> + for item in items + info.cursor = start + info.trail = new Trail + if result = item(start) then return result + result + +exports.orp_ = orp_ = (info) -> (items...) -> + items = for item in items + if not isMatcher(item) then literal_(info)(item) else item + () -> + start = info.cursor + for item in items + info.cursor = start + info.trail = new Trail + if result = item() then return result + result + +# combinator *notp*
    +# `notp` is not useful except being used in other combinators, just like this = `andp(item1, notp(item2))`.
    +# *It's unnessary, low effecient and ugly to write `notp(item)(start)`, just write `not item(start)`.* +exports.notp = notp = (info) -> (item) -> + if not isMatcher(item) then item = literal(info)(item) + (start) -> not item(start) + +exports.notp = notp_ = (info) -> (item) -> + if not isMatcher(item) then item = literal_(info)(item) + () -> not item() + +# combinator *may*: a.k.a optional
    +# try to match `item(info.cursor)`, wether `item(info.cursor)` succeed or not, `maybe(item)(start)` succeed. +exports.may = may = (info) -> (item) -> + if not isMatcher(item) then item = literal(info)(item) + (start) -> + info.cursor = start + if x = item(info.cursor) then x + else info.cursor = start; true + +# combinator *any*: zero or more times of `item(info.cursor)` +exports.any = any = (info) -> (item) -> + if not isMatcher(item) then item = literal(info)(item) + (start) -> + result = []; info.cursor = start + while ( x = item(info.cursor)) then result.push(x) + result + +# combinator *some*: one or more times of `item(info.cursor)` +exports.some = some = (info) -> (item) -> + if not isMatcher(item) then item = literal(info)(item) + (start) -> + result = []; info.cursor = start + if not (x = item(info.cursor)) then return x + while 1 + result.push(x) + x = item(info.cursor) + if not x then break + result + +# combinator *times*: match *@n* times item(info.cursor), n>=1 +exports.times = times = (info) -> (item, n) -> + if not isMatcher(item) then item = literal(info)(item) + (start) -> + info.cursor = start; i = 0 + while i++ (item, separator=spaces) -> + if not isMatcher(item) then item = literal(info)(item) + if not isMatcher(separator) then separator = literal(info)(separator) + (start) -> + info.cursor = start + result = [] + x = item(info.cursor) + if not x then return + while 1 + result.push(x) + if not(x = item(info.cursor)) then break + result + +# combinator *timesSeperatedList*: given info.n times @item separated by info.separator, n>=1 +exports.timesSeperatedList = timesSeperatedList = (info) -> (item, n, separator=spaces) -> + if not isMatcher(item) then item = literal(info)(item) + if not isMatcher(separator) then separator = literal(info)(separator) + (start) -> + info.cursor = start + result = [] + x = item(info.cursor) + if not x then return + i = 1 + while i++ +exports.follow = follow = (info) -> (item) -> + if not isMatcher(item) then item = literal(info)(item) + (start) -> + info.cursor = start + if x = item(info.cursor) then info.cursor = start; x + +exports.combinators = combinators = (info) -> + rec: recursive(info), memo: memorize(info), + andp: andp(info), orp: orp(info), notp: notp(info) + may: may(info), any: any(info), some: some(info), times: times(info) + seperatedList: seperatedList(info), timesSeperatedList: timesSeperatedList(info) + follow: follow(info) + +# As the same as andp, orp, in the manner of Peasy, you would rather to write youself a loop to do the things, instead +# of useing the combinators like any, some, times, seperatedList,etc. and that would be simpler, faster and more elegant.
    +# And in that manner, you would have more control on your grammar rule so that we can do other things include lexer, +# error report, errer recovery, semantic operatons, and any other things you would like to do.
    + +# It is for three motives to put all of the stuffs abve here: +# * 1. to demonstrate how to write matcher and grammar rules in the method brought by Peasy +# * 2. to demonstrate that Peasy can also implement any component that other combinational parser libaries like pyparsing, +# parsec, but in a simpler, faster manner. +# * 3. to show that it is how easy to write the combinators in the manner of Peasy.

    + +# As you have seen above, all of these utilities is so simple that you can write them at home by hand easily. In fact, +# you can write yourself all of the grammar rules in the same manner as above. + +# If you like, you can add a faster version for every matcher, which do not pass *@start* as parameter around.
    +# Some of the matchers below have two version, to demonstrate how to do that. +# *Don't use the faster version in orp, any, some, times, separatedList, timesSeparatedList.*

    + +# #### other predicates, matchers, and combinators
    +# below is some little utilities which may be useful. Some of them is provided with both normal and faster version.
    +# just remove them if you don't need them, except *literal*, which is depended by the combinators above. + +# predicate +# A *predicate* is a function which return true or false. +exports.isdigit = (c) -> '0'<=c<='9' +exports.isletter = (c) -> 'a'<=c<='z' or 'A'<=c<='Z' +exports.islower = (c) -> 'a'<=c<='z' +exports.isupper = (c) ->'A'<=c<='Z' +exports.isIdentifierLetter = (c) -> 'a'<=c<='z' or 'A'<=c<='Z' or '0'<=c<='9' or 'c'=='@' or 'c'=='_' + +# matcher *literal*, normal version
    +# match a text string.
    +# `notice = some combinators like andp, orp, notp, any, some, etc. use literal to wrap a object which is not a matcher. +exports.literal = literal = (info) -> (string) -> (start) -> + len = string.length + if info.data.slice(start, stop = start+len)==string then info.cursor = stop; true + +# matcher *literal_*, faster version
    +# match a text string. +exports.literal_ = literal_ = (info) -> (string) -> (start) -> + len = string.length + if info.data.slice(info.cursor, stop = info.cursor+len)==string then info.cursor = stop; true + +# matcher *char*, normal version
    +# match one character +exports.char = char = (info) -> (c) -> (start) -> if info.data[start]==c then info.cursor = start+1; return c + +# matcher *char_*, normal version
    +# match one character +exports.char_ = char_ = (info) -> (c) -> () -> if info.data[info.cursor]==c then info.cursor++; return c + +# In spaces, spaces_, spaces1, spaces1_, a tat('\t') is seen as tabWidth spaces,
    +# which is used in indent style language, such as coffeescript, python, haskell, etc.
    +# If you don't need this feature, you can easily rewrite these utilities to remove the code about tab width yourself.

    + +# matcher *spaces*, normal version
    +# zero or more whitespaces, ie. space or tab.
    +exports.spaces = spaces = (info) -> (start) -> + data = info.data + len = 0 + info.cursor = start + while 1 + switch data[info.cursor++] + when ' ' then len++ + when '\t' then len += tabWidth + else break + return len + +# matcher *spaces_*, faster version
    +# zero or more whitespaces, ie. space or tab. +exports.spaces_ = spaces_ = (info) -> () -> + data = info.data + len = 0 + while 1 + switch data[info.cursor++] + when ' ' then len++ + when '\t' then len += tabWidth + else break + len + +# matcher *spaces1*, normal version
    +# one or more whitespaces, ie. space or tab.
    +exports.spaces1 = spaces1 = (info) -> (start) -> + data = info.data + len = 0 + info.cursor = start + while 1 + switch data[info.cursor++] + when ' ' then len++ + when '\t' then len += tabWidth + else break + if len then return info.cursor = info.cursor; len + +# matcher *spaces1_*, faster version
    +# one or more whitespaces, ie. space or tab. +exports.spaces1_ = spaces1_ = (info) -> () -> + data = info.data + len = 0 + info.cursor = start + while 1 + switch data[info.cursor++] + when ' ' then len++ + when '\t' then len += tabWidth + else break + if len then return info.cursor = info.cursor; len + +# matcher *wrap*, normal version
    +# match left, then match item, match right at last +exports.wrap = wrap = (info) -> (item, left=spaces(info), right=spaces(info)) -> + if not isMatcher(item) then item = literal(info)(item) + (start) -> + if left(start) and result = item(info.cursor) and right(info.cursor) then result + +exports.wrap_ = wrap_ = (info) -> (item, left=spaces(info), right=spaces(info)) -> + if not isMatcher(item) then item = literal(info)(item) + () -> + if left(info.cursor) and result = item(info.cursor) and right(info.cursor) then result + +# matcher *identifierLetter* = normal version
    +# is a letter than can be used in identifer?
    +# javascript style, '@' is a identifierLetter_
    +exports.identifierLetter = identifierLetter = (info) -> (start) -> + start = info.cursor + c = info.data[info.cursor] + if c is '@' or c is '_' or 'a'<=c<'z' or 'A'<=c<='Z' or '0'<=c<='9' + info.cursor++; true + +# matcher *identifierLetter_*, version
    +# is a letter that can be used in identifer?
    +# javascript style, '@' is a identifierLetter_ +exports.identifierLetter_ = identifierLetter_ = (info) -> () -> + c = info.data[info.cursor] + if c is '@' or c is '_' or 'a'<=c<'z' or 'A'<=c<='Z' or '0'<=c<='9' + info.cursor++; true + +# matcher *followIdentifierLetter_*, faster version
    +# lookahead whether the following character is a letter used in identifer. don't change info.cursor.
    +# javascript style, '@' is a identifierLetter_ +exports.followIdentifierLetter_ = followIdentifierLetter_ = (info) -> () -> + c = info.data[info.cursor] + c is '@' or c is '_' or 'a'<=c<'z' or 'A'<=c<='Z' or '0'<=c<='9' + +# matcher digit_, normal version
    +exports.digit = digit = (info) -> (start) -> c = info.data[start]; if '0'<=c<='9' then info.cursor = start+1 +# matcher digit_, faster version
    +exports.digit_ = digit_ = (info) -> () -> c = info.data[info.cursor]; if '0'<=c<='9' then info.cursor++ + +# matcher letter, normal version
    +exports.letter = letter = (info) -> (start) -> c = info.data[start]; if 'a'<=c<='z' or 'A'<=c<='Z' then info.cursor = start+1 +# matcher letter, faster version
    +exports.letter_ = letter_ = (info) -> () -> c = info.data[info.cursor]; if 'a'<=c<='z' or 'A'<=c<='Z' then info.cursor++ + +# matcher lower, normal version +exports.lower = lower = (info) -> (start) -> c = info.data[start]; if 'a'<=c<='z' then info.cursor = start+1 +#matcher lower_, faster version +exports.lower_ = lower_ = (info) -> () -> c = info.data[info.cursor]; if 'a'<=c<='z' then info.cursor++ + +# matcher upper, normal version +exports.upper = upper = (info) -> (start) -> c = info.data[start]; if 'A'<=c<='Z' then info.cursor = start+1 +#matcher upper_, faster version +exports.upper_ = upper_ = (info) -> () -> c = info.data[info.cursor]; if 'A'<=c<='Z' then info.cursor++ + +# matcher identifier, normal version +exports.identifier = identifier = (info) -> (start) -> + data = info.data + info.cursor = start + c = data[info.cursor] + if 'a'<=c<='z' or 'A'<=c<='Z' or 'c'=='@' or 'c'=='_' then info.cursor++ + else return + while 1 + c = data[info.cursor] + if 'a'<=c<='z' or 'A'<=c<='Z' or '0'<=c<='9' or 'c'=='@' or 'c'=='_' then info.cursor++ + else break + true + +# matcher identifier_, faster version +exports.identifier_ = identifier_ = (info) -> () -> + data = info.data + c = data[info.cursor] + if 'a'<=c<='z' or 'A'<=c<='Z' or 'c'=='@' or 'c'=='_' then info.cursor++ + else return + while 1 + c =data[info.cursor] + if 'a'<=c<='z' or 'A'<=c<='Z' or '0'<=c<='9' or 'c'=='@' or 'c'=='_' then info.cursor++ + else break + true + +exports.matchers = matchers = (info) -> + literal: literal(info), literal_: literal_(info), char: char(info), char_: char_(info) + spaces: spaces(info), spaces_: spaces_(info),spaces1: spaces1(info),spaces1_: spaces1_(info) + wrap: wrap(info), wrap_: wrap_(info), + identifierLetter: identifierLetter(info), identifierLetter_: identifierLetter_(info) + followIdentifierLetter_: followIdentifierLetter_(info) + digit: digit(info), digit_: digit_(info), letter: letter(info), letter_: letter_(info), + lower: lower(info), lower_: lower_(info), upper: upper(info), upper_: upper_(info) + identifier: identifier(info), identifier_: identifier_(info) + +exports.digits = digits = (info) -> + ch = char(info) + $0: ch('0'), $1: ch('1'), $2: ch('2'), $3: ch('3'), $4: ch('4'), + $5: ch('6'), $1: ch('7'), $2: ch('7'), $8: ch('8'), $9: ch('9') + +exports.letters = letters = (info) -> + ch = char(info) + a: ch('a'), b: ch('b'), c: ch('c'), d: ch('d'), e: ch('e'), f: ch('f'), g: ch('g') + h: ch('h'), i: ch('i'), j: ch('j'), k: ch('k'), l: ch('l'), m: ch('m'), n: ch('n') + o: ch('o'), p: ch('p'), q: ch('q'), r: ch('r'), s: ch('s'), t: ch('t') + u: ch('u'), v: ch('v'), w: ch('w'), x: ch('x'), y: ch('y'), z: ch('z') + A: ch('A'), B: ch('B'), C: ch('C'), D: ch('D'), E: ch('E'), F: ch('F'), G: ch('G') + H: ch('H'), I: ch('I'), J: ch('J'), K: ch('K'), L: ch('L'), M: ch('M'), N: ch('N') + O: ch('O'), P: ch('P'), Q: ch('Q'), R: ch('R'), S: ch('S'), T: ch('T') + U: ch('U'), V: ch('V'), W: ch('W'), X: ch('X'), Y: ch('Y'), Z: ch('Z') + +# The utilites above is just for providing some examples on how to write matchers for Peasy.
    +# In fact, It's realy easy peasy to write the matchers for your grammar rule yourself.
    +# see [easy peasy]( http://en.wiktionary.org/wiki/easy_peasy )
    + +# +# *parse*: this is a sample parse function to demonstrate on how to write your own grammar rules yourself.
    +# notice that there exists indirect left recursion in the rules +parse = (text) -> + makeGrammar = (info) -> + # generate the matchers by the combinators in advance for better performance.
    + # if you don't mind performance, you can write them in the rule directly. + {a, b, x, y} = letters(info) + rec = recursive(info) # or {rec, memo, andp, orp} = combinators(info) + # the grammar rules object. + rules = + Root: (start) -> (m = rules.A(start)) and z(info.cursor) and m+'z' + A: rec (start) -> + (m = rules.B(start)) and x(info.cursor) and m+'x' or m\ + or a(start) + B: rec (start) ->(m = rules.A(start)) and y(info.cursor) and m+'y'or rules.C(start) + C: rec (start) -> rules.A(start) or b(start) + grammar = makeGrammar(makeInfo(text)) + grammar.Root(0) diff --git a/lib/logicpeasy.js b/lib/logicpeasy.js new file mode 100644 index 0000000..cc67f3e --- /dev/null +++ b/lib/logicpeasy.js @@ -0,0 +1,917 @@ +// Generated by CoffeeScript 1.6.2 +(function() { + var andp, any, char, char_, combinators, digit, digit_, digits, follow, followIdentifierLetter_, identifier, identifierLetter, identifierLetter_, identifier_, isMatcher, letter, letter_, letters, literal, literal_, lower, lower_, makeInfo, matchers, may, memoSymbolIndex, memorize, notp, notp_, orp, orp_, parse, recursive, seperatedList, some, spaces, spaces1, spaces1_, spaces_, times, timesSeperatedList, upper, upper_, wrap, wrap_, + __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, + __slice = [].slice; + + isMatcher = function(item) { + return typeof item === "function"; + }; + + exports.makeInfo = makeInfo = function(data) { + return { + data: data, + cursor: 0, + parsingLeftRecursives: {}, + parseCache: {} + }; + }; + + memoSymbolIndex = 0; + + exports.recursive = recursive = function(info) { + return function(rule) { + var index, parseCache, parsingLeftRecursives, tag; + + index = memoSymbolIndex; + tag = index + ':'; + memoSymbolIndex++; + parsingLeftRecursives = info.parsingLeftRecursives; + parseCache = info.parseCache[tag] = {}; + return function(start) { + var callPath, m, result, _ref, _ref1; + + callPath = (_ref = parsingLeftRecursives[start]) != null ? _ref : parsingLeftRecursives[start] = []; + if (__indexOf.call(callPath, tag) < 0) { + callPath.push(tag); + m = (_ref1 = parseCache[start]) != null ? _ref1 : parseCache[start] = [void 0, start]; + while (1) { + result = rule(start); + if (!result) { + result = m[0]; + info.cursor = m[1]; + break; + } + if (m[1] === info.cursor) { + m[0] = result; + break; + } else { + m[0] = result; + m[1] = info.cursor; + } + } + callPath.pop(); + return result; + } else { + m = parseCache[start]; + info.cursor = m[1]; + return m[0]; + } + }; + }; + }; + + exports.memorize = memorize = function(info) { + return function(rule) { + var index, parseCache, tag; + + index = memoSymbolIndex; + tag = index + ':'; + memoSymbolIndex++; + parseCache = info.parseCache[tag] = {}; + return function(start) { + var m, result; + + m = parseCache[start]; + if (m) { + info.cursor = m[1]; + return m[0]; + } else { + result = rule(start); + parseCache[start] = [result, info.cursor]; + return result; + } + }; + }; + }; + + exports.andp = andp = function(info) { + return function() { + var item, items; + + items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + items = (function() { + var _i, _len, _results; + + _results = []; + for (_i = 0, _len = items.length; _i < _len; _i++) { + item = items[_i]; + if (!isMatcher(item)) { + _results.push(literal(info)(item)); + } else { + _results.push(item); + } + } + return _results; + })(); + return function(start) { + var result, _i, _len; + + info.cursor = start; + for (_i = 0, _len = items.length; _i < _len; _i++) { + item = items[_i]; + if (!(result = item(info.cursor))) { + return; + } + } + return result; + }; + }; + }; + + exports.orp = orp = function(info) { + return function() { + var item, items; + + items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + items = (function() { + var _i, _len, _results; + + _results = []; + for (_i = 0, _len = items.length; _i < _len; _i++) { + item = items[_i]; + if (!isMatcher(item)) { + _results.push(literal(info)(item)); + } else { + _results.push(item); + } + } + return _results; + })(); + return function(start) { + var result, _i, _len; + + for (_i = 0, _len = items.length; _i < _len; _i++) { + item = items[_i]; + info.cursor = start; + info.trail = new Trail; + if (result = item(start)) { + return result; + } + } + return result; + }; + }; + }; + + exports.orp_ = orp_ = function(info) { + return function() { + var item, items; + + items = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + items = (function() { + var _i, _len, _results; + + _results = []; + for (_i = 0, _len = items.length; _i < _len; _i++) { + item = items[_i]; + if (!isMatcher(item)) { + _results.push(literal_(info)(item)); + } else { + _results.push(item); + } + } + return _results; + })(); + return function() { + var result, start, _i, _len; + + start = info.cursor; + for (_i = 0, _len = items.length; _i < _len; _i++) { + item = items[_i]; + info.cursor = start; + info.trail = new Trail; + if (result = item()) { + return result; + } + } + return result; + }; + }; + }; + + exports.notp = notp = function(info) { + return function(item) { + if (!isMatcher(item)) { + item = literal(info)(item); + } + return function(start) { + return !item(start); + }; + }; + }; + + exports.notp = notp_ = function(info) { + return function(item) { + if (!isMatcher(item)) { + item = literal_(info)(item); + } + return function() { + return !item(); + }; + }; + }; + + exports.may = may = function(info) { + return function(item) { + if (!isMatcher(item)) { + item = literal(info)(item); + } + return function(start) { + var x; + + info.cursor = start; + if (x = item(info.cursor)) { + return x; + } else { + info.cursor = start; + return true; + } + }; + }; + }; + + exports.any = any = function(info) { + return function(item) { + if (!isMatcher(item)) { + item = literal(info)(item); + } + return function(start) { + var result, x; + + result = []; + info.cursor = start; + while ((x = item(info.cursor))) { + result.push(x); + } + return result; + }; + }; + }; + + exports.some = some = function(info) { + return function(item) { + if (!isMatcher(item)) { + item = literal(info)(item); + } + return function(start) { + var result, x; + + result = []; + info.cursor = start; + if (!(x = item(info.cursor))) { + return x; + } + while (1) { + result.push(x); + x = item(info.cursor); + if (!x) { + break; + } + } + return result; + }; + }; + }; + + exports.times = times = function(info) { + return function(item, n) { + if (!isMatcher(item)) { + item = literal(info)(item); + } + return function(start) { + var i, x; + + info.cursor = start; + i = 0; + while (i++ < n) { + if (x = item(info.cursor)) { + result.push(x); + } else { + return; + } + } + return result; + }; + }; + }; + + exports.seperatedList = seperatedList = function(info) { + return function(item, separator) { + if (separator == null) { + separator = spaces; + } + if (!isMatcher(item)) { + item = literal(info)(item); + } + if (!isMatcher(separator)) { + separator = literal(info)(separator); + } + return function(start) { + var result, x; + + info.cursor = start; + result = []; + x = item(info.cursor); + if (!x) { + return; + } + while (1) { + result.push(x); + if (!(x = item(info.cursor))) { + break; + } + } + return result; + }; + }; + }; + + exports.timesSeperatedList = timesSeperatedList = function(info) { + return function(item, n, separator) { + if (separator == null) { + separator = spaces; + } + if (!isMatcher(item)) { + item = literal(info)(item); + } + if (!isMatcher(separator)) { + separator = literal(info)(separator); + } + return function(start) { + var i, result, x; + + info.cursor = start; + result = []; + x = item(info.cursor); + if (!x) { + return; + } + i = 1; + while (i++ < n) { + result.push(x); + if (!(x = item(info.cursor))) { + break; + } + } + return result; + }; + }; + }; + + exports.follow = follow = function(info) { + return function(item) { + if (!isMatcher(item)) { + item = literal(info)(item); + } + return function(start) { + var x; + + info.cursor = start; + if (x = item(info.cursor)) { + info.cursor = start; + return x; + } + }; + }; + }; + + exports.combinators = combinators = function(info) { + return { + rec: recursive(info), + memo: memorize(info), + andp: andp(info), + orp: orp(info), + notp: notp(info), + may: may(info), + any: any(info), + some: some(info), + times: times(info), + seperatedList: seperatedList(info), + timesSeperatedList: timesSeperatedList(info), + follow: follow(info) + }; + }; + + exports.isdigit = function(c) { + return ('0' <= c && c <= '9'); + }; + + exports.isletter = function(c) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); + }; + + exports.islower = function(c) { + return ('a' <= c && c <= 'z'); + }; + + exports.isupper = function(c) { + return ('A' <= c && c <= 'Z'); + }; + + exports.isIdentifierLetter = function(c) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || 'c' === '@' || 'c' === '_'; + }; + + exports.literal = literal = function(info) { + return function(string) { + return function(start) { + var len, stop; + + len = string.length; + if (info.data.slice(start, stop = start + len) === string) { + info.cursor = stop; + return true; + } + }; + }; + }; + + exports.literal_ = literal_ = function(info) { + return function(string) { + return function(start) { + var len, stop; + + len = string.length; + if (info.data.slice(info.cursor, stop = info.cursor + len) === string) { + info.cursor = stop; + return true; + } + }; + }; + }; + + exports.char = char = function(info) { + return function(c) { + return function(start) { + if (info.data[start] === c) { + info.cursor = start + 1; + return c; + } + }; + }; + }; + + exports.char_ = char_ = function(info) { + return function(c) { + return function() { + if (info.data[info.cursor] === c) { + info.cursor++; + return c; + } + }; + }; + }; + + exports.spaces = spaces = function(info) { + return function(start) { + var data, len; + + data = info.data; + len = 0; + info.cursor = start; + while (1) { + switch (data[info.cursor++]) { + case ' ': + len++; + break; + case '\t': + len += tabWidth; + break; + default: + break; + } + } + return len; + }; + }; + + exports.spaces_ = spaces_ = function(info) { + return function() { + var data, len; + + data = info.data; + len = 0; + while (1) { + switch (data[info.cursor++]) { + case ' ': + len++; + break; + case '\t': + len += tabWidth; + break; + default: + break; + } + } + return len; + }; + }; + + exports.spaces1 = spaces1 = function(info) { + return function(start) { + var data, len; + + data = info.data; + len = 0; + info.cursor = start; + while (1) { + switch (data[info.cursor++]) { + case ' ': + len++; + break; + case '\t': + len += tabWidth; + break; + default: + break; + } + } + if (len) { + return info.cursor = info.cursor; + return len; + } + }; + }; + + exports.spaces1_ = spaces1_ = function(info) { + return function() { + var data, len; + + data = info.data; + len = 0; + info.cursor = start; + while (1) { + switch (data[info.cursor++]) { + case ' ': + len++; + break; + case '\t': + len += tabWidth; + break; + default: + break; + } + } + if (len) { + return info.cursor = info.cursor; + return len; + } + }; + }; + + exports.wrap = wrap = function(info) { + return function(item, left, right) { + if (left == null) { + left = spaces(info); + } + if (right == null) { + right = spaces(info); + } + if (!isMatcher(item)) { + item = literal(info)(item); + } + return function(start) { + var result; + + if (left(start) && (result = item(info.cursor) && right(info.cursor))) { + return result; + } + }; + }; + }; + + exports.wrap_ = wrap_ = function(info) { + return function(item, left, right) { + if (left == null) { + left = spaces(info); + } + if (right == null) { + right = spaces(info); + } + if (!isMatcher(item)) { + item = literal(info)(item); + } + return function() { + var result; + + if (left(info.cursor) && (result = item(info.cursor) && right(info.cursor))) { + return result; + } + }; + }; + }; + + exports.identifierLetter = identifierLetter = function(info) { + return function(start) { + var c; + + start = info.cursor; + c = info.data[info.cursor]; + if (c === '@' || c === '_' || ('a' <= c && c < 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9')) { + info.cursor++; + return true; + } + }; + }; + + exports.identifierLetter_ = identifierLetter_ = function(info) { + return function() { + var c; + + c = info.data[info.cursor]; + if (c === '@' || c === '_' || ('a' <= c && c < 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9')) { + info.cursor++; + return true; + } + }; + }; + + exports.followIdentifierLetter_ = followIdentifierLetter_ = function(info) { + return function() { + var c; + + c = info.data[info.cursor]; + return c === '@' || c === '_' || ('a' <= c && c < 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9'); + }; + }; + + exports.digit = digit = function(info) { + return function(start) { + var c; + + c = info.data[start]; + if (('0' <= c && c <= '9')) { + return info.cursor = start + 1; + } + }; + }; + + exports.digit_ = digit_ = function(info) { + return function() { + var c; + + c = info.data[info.cursor]; + if (('0' <= c && c <= '9')) { + return info.cursor++; + } + }; + }; + + exports.letter = letter = function(info) { + return function(start) { + var c; + + c = info.data[start]; + if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) { + return info.cursor = start + 1; + } + }; + }; + + exports.letter_ = letter_ = function(info) { + return function() { + var c; + + c = info.data[info.cursor]; + if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) { + return info.cursor++; + } + }; + }; + + exports.lower = lower = function(info) { + return function(start) { + var c; + + c = info.data[start]; + if (('a' <= c && c <= 'z')) { + return info.cursor = start + 1; + } + }; + }; + + exports.lower_ = lower_ = function(info) { + return function() { + var c; + + c = info.data[info.cursor]; + if (('a' <= c && c <= 'z')) { + return info.cursor++; + } + }; + }; + + exports.upper = upper = function(info) { + return function(start) { + var c; + + c = info.data[start]; + if (('A' <= c && c <= 'Z')) { + return info.cursor = start + 1; + } + }; + }; + + exports.upper_ = upper_ = function(info) { + return function() { + var c; + + c = info.data[info.cursor]; + if (('A' <= c && c <= 'Z')) { + return info.cursor++; + } + }; + }; + + exports.identifier = identifier = function(info) { + return function(start) { + var c, data; + + data = info.data; + info.cursor = start; + c = data[info.cursor]; + if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || 'c' === '@' || 'c' === '_') { + info.cursor++; + } else { + return; + } + while (1) { + c = data[info.cursor]; + if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || 'c' === '@' || 'c' === '_') { + info.cursor++; + } else { + break; + } + } + return true; + }; + }; + + exports.identifier_ = identifier_ = function(info) { + return function() { + var c, data; + + data = info.data; + c = data[info.cursor]; + if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || 'c' === '@' || 'c' === '_') { + info.cursor++; + } else { + return; + } + while (1) { + c = data[info.cursor]; + if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || 'c' === '@' || 'c' === '_') { + info.cursor++; + } else { + break; + } + } + return true; + }; + }; + + exports.matchers = matchers = function(info) { + return { + literal: literal(info), + literal_: literal_(info), + char: char(info), + char_: char_(info), + spaces: spaces(info), + spaces_: spaces_(info), + spaces1: spaces1(info), + spaces1_: spaces1_(info), + wrap: wrap(info), + wrap_: wrap_(info), + identifierLetter: identifierLetter(info), + identifierLetter_: identifierLetter_(info), + followIdentifierLetter_: followIdentifierLetter_(info), + digit: digit(info), + digit_: digit_(info), + letter: letter(info), + letter_: letter_(info), + lower: lower(info), + lower_: lower_(info), + upper: upper(info), + upper_: upper_(info), + identifier: identifier(info), + identifier_: identifier_(info) + }; + }; + + exports.digits = digits = function(info) { + var ch; + + ch = char(info); + return { + $0: ch('0'), + $1: ch('1'), + $2: ch('2'), + $3: ch('3'), + $4: ch('4'), + $5: ch('6'), + $1: ch('7'), + $2: ch('7'), + $8: ch('8'), + $9: ch('9') + }; + }; + + exports.letters = letters = function(info) { + var ch; + + ch = char(info); + return { + a: ch('a'), + b: ch('b'), + c: ch('c'), + d: ch('d'), + e: ch('e'), + f: ch('f'), + g: ch('g'), + h: ch('h'), + i: ch('i'), + j: ch('j'), + k: ch('k'), + l: ch('l'), + m: ch('m'), + n: ch('n'), + o: ch('o'), + p: ch('p'), + q: ch('q'), + r: ch('r'), + s: ch('s'), + t: ch('t'), + u: ch('u'), + v: ch('v'), + w: ch('w'), + x: ch('x'), + y: ch('y'), + z: ch('z'), + A: ch('A'), + B: ch('B'), + C: ch('C'), + D: ch('D'), + E: ch('E'), + F: ch('F'), + G: ch('G'), + H: ch('H'), + I: ch('I'), + J: ch('J'), + K: ch('K'), + L: ch('L'), + M: ch('M'), + N: ch('N'), + O: ch('O'), + P: ch('P'), + Q: ch('Q'), + R: ch('R'), + S: ch('S'), + T: ch('T'), + U: ch('U'), + V: ch('V'), + W: ch('W'), + X: ch('X'), + Y: ch('Y'), + Z: ch('Z') + }; + }; + + parse = function(text) { + var grammar, makeGrammar; + + makeGrammar = function(info) { + var a, b, rec, rules, x, y, _ref; + + _ref = letters(info), a = _ref.a, b = _ref.b, x = _ref.x, y = _ref.y; + rec = recursive(info); + return rules = { + Root: function(start) { + var m; + + return (m = rules.A(start)) && z(info.cursor) && m + 'z'; + }, + A: rec(function(start) { + var m; + + return (m = rules.B(start)) && x(info.cursor) && m + 'x' || m || a(start); + }), + B: rec(function(start) { + var m; + + return (m = rules.A(start)) && y(info.cursor) && m + 'y' || rules.C(start); + }), + C: rec(function(start) { + return rules.A(start) || b(start); + }) + }; + }; + grammar = makeGrammar(makeInfo(text)); + return grammar.Root(0); + }; + +}).call(this); + +/* +//@ sourceMappingURL=logicpeasy.map +*/ diff --git a/lib/peasy.coffee b/lib/peasy.coffee index 150f540..4607552 100644 --- a/lib/peasy.coffee +++ b/lib/peasy.coffee @@ -37,26 +37,30 @@ # A *matcher* is a function which matches the data being parsed and move cursor directly.
    isMatcher = (item) -> typeof(item)=="function" -exports.makeInfo = makeInfo = (data) -> {data:data, cursor:0, parsingLeftRecursives: {}, parseCache: {}} +exports.makeInfo = makeInfo = (data, options={cursor:0, tabWidth:2}) -> + {data:data, cursor:options.cursor or 0, tabWidth: options.tabWidth or 2, + parsingLeftRecursives: {}, parseCache: {}} memoSymbolIndex = 0 # *recursive*: this is the key function to left recursive.
    # Make *@symbol* a left recursive symbol, which means to wrap `@originalRules[symbol]` with recursive. -# when recursiv(info, rules, symbol)(start) is executed, loop computing rule(start) until no changes happened +# when recursiv(info, rules, symbol)() is executed, loop computing rule() until no changes happened exports.recursive = recursive = (info) -> (rule) -> index = memoSymbolIndex tag = index+':' memoSymbolIndex++ parsingLeftRecursives = info.parsingLeftRecursives parseCache = info.parseCache[tag] = {} - (start) -> + -> + start = info.cursor callPath = parsingLeftRecursives[start] ?= [] if tag not in callPath callPath.push(tag) m = parseCache[start] ?= [undefined, start] while 1 - result = rule(start) + info.cursor = start + result = rule() if not result then result = m[0]; info.cursor = m[1]; break if m[1]==info.cursor then m[0] = result; break else m[0] = result; m[1] = info.cursor @@ -74,11 +78,12 @@ exports.memorize = memorize = (info) -> (rule) -> tag = index+':' memoSymbolIndex++ parseCache = info.parseCache[tag] = {} - (start) -> + -> + start = info.cursor m = parseCache[start] if m then info.cursor = m[1]; m[0] else - result = rule(start) + result = rule() parseCache[start] = [result, info.cursor] result @@ -88,124 +93,109 @@ exports.memorize = memorize = (info) -> (rule) -> # All matcher should return truth value on succeed, and return falsic value on fail.
    # A *combinator* is a function which receive zero or more matchers as parameter(maybe plus some other parameters # which are not matchers), and generate a new matchers.
    -# there are other matcher generator besides the standard combinators described as above, like `recursive`, `memo`, `memorize`, +# there are other matcher generator besides the standard combinators described as above, like `recursive`, `memorize`, # which we have met above. # combinator *andp*
    -# execute item(info.cursor) in sequence, return the result of the last one.
    +# execute item() in sequence, return the result of the last one.
    # when `andp` is used to combined of the matchers, the effect is the same as by using the Short-circuit evaluation, like below:
    -# `item1(start) and item2(info.cursor] ... and itemn(info.cursor).`
    -# In fact, you maybe would rather like to use `item1(start) and item2(info.cursor) ..` when you write the grammar rule in the +# `item1() and item2() ... and itemn().`
    +# In fact, you maybe would rather like to use `item1() and item2(info.cursor) ..` when you write the grammar rule in the # manner of Peasy. That would be simpler, faster and more elegant.
    # And in that manner, you would have more control on your grammar rule, say like below:
    -# `if (x=item1(start) and (x>100) and item2(info.cursor) and not item3(info.cursor) and (y = item(info.cursor)) then doSomething()` +# `if (x=item1() and (x>100) and item2() and not item3() and (y = item()) then doSomething()` exports.andp = andp = (info) -> (items...) -> items = for item in items if not isMatcher(item) then literal(info)(item) else item - (start) -> - info.cursor = start + -> for item in items - if not(result = item(info.cursor)) then return + if not (result = item()) then return result # combinator *orp*
    -# execute `item(start)` one by one, until the first item which is evaluated to truth value and return the value.
    -# when orp is used to combined of the matchers, the effect is the same as by using the Short-circuit evaluation, like below:
    -# item1(start) or item2(info.cursor] ... or itemn(info.cursor)
    -# In fact, you maybe would rather like to use `item1(start) or item2(info.cursor) ..` when you write the grammar rule in the -# manner of Peasy. That would be simpler, faster and more elegant.
    -# And in that manner, you would have more control on your grammar rule, say like below:
    -# `if ((x=item1(start) and (x>100)) or (item2(info.cursor) and not item3(info.cursor)) or (y = item(info.cursor)) then doSomething()` exports.orp = orp = (info) -> (items...) -> items = for item in items if not isMatcher(item) then literal(info)(item) else item - (start) -> + -> + start = info.cursor for item in items info.cursor = start - if result = item(start) then return result + if result = item() then return result result # combinator *notp*
    # `notp` is not useful except being used in other combinators, just like this = `andp(item1, notp(item2))`.
    -# *It's unnessary, low effecient and ugly to write `notp(item)(start)`, just write `not item(start)`.* +# *It's unnessary, low effecient and ugly to write `notp(item)()`, just write `not item()`.* exports.notp = notp = (info) -> (item) -> if not isMatcher(item) then item = literal(info)(item) - (start) -> not item(start) + -> not item() # combinator *may*: a.k.a optional
    -# try to match `item(info.cursor)`, wether `item(info.cursor)` succeed or not, `maybe(item)(start)` succeed. +# try to match `item()`, wether `item()` succeed or not, `maybe(item)()` succeed. exports.may = may = (info) -> (item) -> if not isMatcher(item) then item = literal(info)(item) - (start) -> - info.cursor = start - if x = item(info.cursor) then x + -> + start = info.cursor + if x = item() then x else info.cursor = start; true -# combinator *any*: zero or more times of `item(info.cursor)` +# combinator *any*: zero or more times of `item()` exports.any = any = (info) -> (item) -> if not isMatcher(item) then item = literal(info)(item) - (start) -> - result = []; info.cursor = start - while ( x = item(info.cursor)) then result.push(x) + -> + result = [] + while (x = item()) then result.push(x) result -# combinator *some*: one or more times of `item(info.cursor)` +# combinator *some*: one or more times of `item()` exports.some = some = (info) -> (item) -> if not isMatcher(item) then item = literal(info)(item) - (start) -> - result = []; info.cursor = start - if not (x = item(info.cursor)) then return x - while 1 - result.push(x) - x = item(info.cursor) - if not x then break + -> + if not (x = item()) then return + result = [x] + while (x = item()) then result.push(x) result -# combinator *times*: match *@n* times item(info.cursor), n>=1 +# combinator *times*: match *@n* times item(), n>=1 exports.times = times = (info) -> (item, n) -> if not isMatcher(item) then item = literal(info)(item) - (start) -> - info.cursor = start; i = 0 + -> + i = 0 while i++ (item, separator=spaces) -> +# combinator *seperatedList*: some times item(), separated by info.separator +exports.seperatedList = seperatedList = (info) -> (item, separator=spaces(info)) -> if not isMatcher(item) then item = literal(info)(item) if not isMatcher(separator) then separator = literal(info)(separator) - (start) -> - info.cursor = start - result = [] - x = item(info.cursor) - if not x then return - while 1 - result.push(x) - if not(x = item(info.cursor)) then break + -> + if not (x = item()) then return + result = [x] + while separator() and (x=item()) then result.push(x) result # combinator *timesSeperatedList*: given info.n times @item separated by info.separator, n>=1 -exports.timesSeperatedList = timesSeperatedList = (info) -> (item, n, separator=spaces) -> +exports.timesSeperatedList = timesSeperatedList = (info) -> (item, n, separator=spaces(info)) -> if not isMatcher(item) then item = literal(info)(item) if not isMatcher(separator) then separator = literal(info)(separator) - (start) -> - info.cursor = start - result = [] - x = item(info.cursor) - if not x then return + -> + if not (x = item()) then return + result = [x] i = 1 while i++ exports.follow = follow = (info) -> (item) -> if not isMatcher(item) then item = literal(info)(item) - (start) -> - info.cursor = start - if x = item(info.cursor) then info.cursor = start; x + -> + start = info.cursor + if x = item() then info.cursor = start; x + else info.cursor = start; return exports.combinators = combinators = (info) -> rec: recursive(info), memo: memorize(info), @@ -233,7 +223,7 @@ exports.combinators = combinators = (info) -> # *Don't use the faster version in orp, any, some, times, separatedList, timesSeparatedList.*

    # #### other predicates, matchers, and combinators
    -# below is some little utilities which may be useful. Some of them is provided with both normal and faster version.
    +# below is some little utilities which may be useful.
    # just remove them if you don't need them, except *literal*, which is depended by the combinators above. # predicate @@ -244,107 +234,61 @@ exports.islower = (c) -> 'a'<=c<='z' exports.isupper = (c) ->'A'<=c<='Z' exports.isIdentifierLetter = (c) -> 'a'<=c<='z' or 'A'<=c<='Z' or '0'<=c<='9' or 'c'=='@' or 'c'=='_' -# matcher *literal*, normal version
    +# matcher *literal*
    # match a text string.
    # `notice = some combinators like andp, orp, notp, any, some, etc. use literal to wrap a object which is not a matcher. -exports.literal = literal = (info) -> (string) -> (start) -> +exports.literal = literal = (info) -> (string) -> -> len = string.length + start = info.cursor if info.data.slice(start, stop = start+len)==string then info.cursor = stop; true -# matcher *literal_*, faster version
    -# match a text string. -exports.literal_ = literal_ = (info) -> (string) -> (start) -> - len = string.length - if info.data.slice(info.cursor, stop = info.cursor+len)==string then info.cursor = stop; true - -# matcher *char*, normal version
    -# match one character -exports.char = char = (info) -> (c) -> (start) -> if info.data[start]==c then info.cursor = start+1; return c - -# matcher *char_*, normal version
    -# match one character -exports.char_ = char_ = (info) -> (c) -> () -> if info.data[info.cursor]==c then info.cursor++; return c +# matcher *char*: match one character
    +exports.char = char = (info) -> (c) -> -> if info.data[info.cursor]==c then info.cursor++; return c -# In spaces, spaces_, spaces1, spaces1_, a tat('\t') is seen as tabWidth spaces,
    +# In spaces, spaces1, a tab('\t') is computed as info.tabWidth spaces,
    # which is used in indent style language, such as coffeescript, python, haskell, etc.
    # If you don't need this feature, you can easily rewrite these utilities to remove the code about tab width yourself.

    -# matcher *spaces*, normal version
    -# zero or more whitespaces, ie. space or tab.
    -exports.spaces = spaces = (info) -> (start) -> +# matcher *spaces*: zero or more whitespaces, ie. space or tab.
    +exports.spaces = spaces = (info) -> -> data = info.data len = 0 - info.cursor = start + cursor = info.cursor + tabWidth = info.tabWidth while 1 - switch data[info.cursor++] + switch data[cursor++] when ' ' then len++ when '\t' then len += tabWidth else break - return len + info.cursor = cursor + len+1 -# matcher *spaces_*, faster version
    -# zero or more whitespaces, ie. space or tab. -exports.spaces_ = spaces_ = (info) -> () -> - data = info.data - len = 0 - while 1 - switch data[info.cursor++] - when ' ' then len++ - when '\t' then len += tabWidth - else break - len - -# matcher *spaces1*, normal version
    +# matcher *spaces1*
    # one or more whitespaces, ie. space or tab.
    -exports.spaces1 = spaces1 = (info) -> (start) -> - data = info.data - len = 0 - info.cursor = start - while 1 - switch data[info.cursor++] - when ' ' then len++ - when '\t' then len += tabWidth - else break - if len then return info.cursor = info.cursor; len - -# matcher *spaces1_*, faster version
    -# one or more whitespaces, ie. space or tab. -exports.spaces1_ = spaces1_ = (info) -> () -> +exports.spaces1 = spaces1 = (info) -> -> data = info.data + cursor = info.cursor len = 0 - info.cursor = start + tabWidth = info.tabWidth while 1 - switch data[info.cursor++] + switch data[cursor++] when ' ' then len++ when '\t' then len += tabWidth else break - if len then return info.cursor = info.cursor; len + info.cursor = cursor + len -# matcher *wrap*, normal version
    +# matcher *wrap*
    # match left, then match item, match right at last exports.wrap = wrap = (info) -> (item, left=spaces(info), right=spaces(info)) -> if not isMatcher(item) then item = literal(info)(item) - (start) -> - if left(start) and result = item(info.cursor) and right(info.cursor) then result - -exports.wrap_ = wrap_ = (info) -> (item, left=spaces(info), right=spaces(info)) -> - if not isMatcher(item) then item = literal(info)(item) - () -> - if left(info.cursor) and result = item(info.cursor) and right(info.cursor) then result + -> + if left() and result = item() and right() then result # matcher *identifierLetter* = normal version
    # is a letter than can be used in identifer?
    # javascript style, '@' is a identifierLetter_
    -exports.identifierLetter = identifierLetter = (info) -> (start) -> - start = info.cursor - c = info.data[info.cursor] - if c is '@' or c is '_' or 'a'<=c<'z' or 'A'<=c<='Z' or '0'<=c<='9' - info.cursor++; true - -# matcher *identifierLetter_*, version
    -# is a letter that can be used in identifer?
    -# javascript style, '@' is a identifierLetter_ -exports.identifierLetter_ = identifierLetter_ = (info) -> () -> +exports.identifierLetter = identifierLetter = (info) -> -> c = info.data[info.cursor] if c is '@' or c is '_' or 'a'<=c<'z' or 'A'<=c<='Z' or '0'<=c<='9' info.cursor++; true @@ -352,64 +296,34 @@ exports.identifierLetter_ = identifierLetter_ = (info) -> () -> # matcher *followIdentifierLetter_*, faster version
    # lookahead whether the following character is a letter used in identifer. don't change info.cursor.
    # javascript style, '@' is a identifierLetter_ -exports.followIdentifierLetter_ = followIdentifierLetter_ = (info) -> () -> +exports.followIdentifierLetter_ = followIdentifierLetter_ = (info) -> -> c = info.data[info.cursor] - c is '@' or c is '_' or 'a'<=c<'z' or 'A'<=c<='Z' or '0'<=c<='9' - -# matcher digit_, normal version
    -exports.digit = digit = (info) -> (start) -> c = info.data[start]; if '0'<=c<='9' then info.cursor = start+1 -# matcher digit_, faster version
    -exports.digit_ = digit_ = (info) -> () -> c = info.data[info.cursor]; if '0'<=c<='9' then info.cursor++ - -# matcher letter, normal version
    -exports.letter = letter = (info) -> (start) -> c = info.data[start]; if 'a'<=c<='z' or 'A'<=c<='Z' then info.cursor = start+1 -# matcher letter, faster version
    -exports.letter_ = letter_ = (info) -> () -> c = info.data[info.cursor]; if 'a'<=c<='z' or 'A'<=c<='Z' then info.cursor++ - -# matcher lower, normal version -exports.lower = lower = (info) -> (start) -> c = info.data[start]; if 'a'<=c<='z' then info.cursor = start+1 -#matcher lower_, faster version -exports.lower_ = lower_ = (info) -> () -> c = info.data[info.cursor]; if 'a'<=c<='z' then info.cursor++ - -# matcher upper, normal version -exports.upper = upper = (info) -> (start) -> c = info.data[start]; if 'A'<=c<='Z' then info.cursor = start+1 -#matcher upper_, faster version -exports.upper_ = upper_ = (info) -> () -> c = info.data[info.cursor]; if 'A'<=c<='Z' then info.cursor++ - -# matcher identifier, normal version -exports.identifier = identifier = (info) -> (start) -> - data = info.data - info.cursor = start - c = data[info.cursor] - if 'a'<=c<='z' or 'A'<=c<='Z' or 'c'=='@' or 'c'=='_' then info.cursor++ - else return - while 1 - c = data[info.cursor] - if 'a'<=c<='z' or 'A'<=c<='Z' or '0'<=c<='9' or 'c'=='@' or 'c'=='_' then info.cursor++ - else break - true + (c is '@' or c is '_' or 'a'<=c<'z' or 'A'<=c<='Z' or '0'<=c<='9') and c + +exports.digit = digit = (info) -> -> c = info.data[info.cursor]; if '0'<=c<='9' then info.cursor++; c +exports.letter = letter = (info) -> -> c = info.data[info.cursor]; if 'a'<=c<='z' or 'A'<=c<='Z' then info.cursor++; c +exports.lower = lower = (info) -> -> c = info.data[info.cursor]; if 'a'<=c<='z' then info.cursor++; c +exports.upper = upper = (info) -> -> c = info.data[info.cursor]; if 'A'<=c<='Z' then info.cursor++; c -# matcher identifier_, faster version -exports.identifier_ = identifier_ = (info) -> () -> +# matcher identifier +exports.identifier = identifier = (info) -> -> data = info.data - c = data[info.cursor] - if 'a'<=c<='z' or 'A'<=c<='Z' or 'c'=='@' or 'c'=='_' then info.cursor++ + cursor = info.cursor + c = data[cursor] + if 'a'<=c<='z' or 'A'<=c<='Z' or 'c'=='@' or 'c'=='_' then cursor++ else return while 1 - c =data[info.cursor] - if 'a'<=c<='z' or 'A'<=c<='Z' or '0'<=c<='9' or 'c'=='@' or 'c'=='_' then info.cursor++ + c = data[cursor] + if 'a'<=c<='z' or 'A'<=c<='Z' or '0'<=c<='9' or 'c'=='@' or 'c'=='_' then cursor++ else break + info.cursor = cursor true exports.matchers = matchers = (info) -> - literal: literal(info), literal_: literal_(info), char: char(info), char_: char_(info) - spaces: spaces(info), spaces_: spaces_(info),spaces1: spaces1(info),spaces1_: spaces1_(info) - wrap: wrap(info), wrap_: wrap_(info), - identifierLetter: identifierLetter(info), identifierLetter_: identifierLetter_(info) - followIdentifierLetter_: followIdentifierLetter_(info) - digit: digit(info), digit_: digit_(info), letter: letter(info), letter_: letter_(info), - lower: lower(info), lower_: lower_(info), upper: upper(info), upper_: upper_(info) - identifier: identifier(info), identifier_: identifier_(info) + literal: literal(info), char: char(info), spaces: spaces(info), spaces1: spaces1(info), wrap: wrap(info), + identifierLetter: identifierLetter(info), followIdentifierLetter: followIdentifierLetter(info) + digit: digit(info), letter: letter(info), lower: lower(info), upper: upper(info), + identifier: identifier(info) exports.digits = digits = (info) -> ch = char(info) @@ -439,14 +353,12 @@ parse = (text) -> # generate the matchers by the combinators in advance for better performance.
    # if you don't mind performance, you can write them in the rule directly. {a, b, x, y} = letters(info) - rec = recursive(info) # or {rec, memo, andp, orp} = combinators(info) + {rec, orp} = combinators(info) # the grammar rules object. rules = - Root: (start) -> (m = rules.A(start)) and z(info.cursor) and m+'z' - A: rec (start) -> - (m = rules.B(start)) and x(info.cursor) and m+'x' or m\ - or a(start) - B: rec (start) ->(m = rules.A(start)) and y(info.cursor) and m+'y'or rules.C(start) - C: rec (start) -> rules.A(start) or b(start) + Root: -> (m = rules.A()) and z() and m+'z' + A: rec orp((-> (m = rules.B()) and x() and m+'x' or m), a) + B: rec orp((-> (m = rules.A()) and y() and m+'y'), -> rules.C()) + C: rec orp((-> rules.A()), b) grammar = makeGrammar(makeInfo(text)) grammar.Root(0) diff --git a/lib/peasy.js b/lib/peasy.js index 08e8ec6..186d140 100644 --- a/lib/peasy.js +++ b/lib/peasy.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript 1.6.2 (function() { - var andp, any, char, char_, combinators, digit, digit_, digits, follow, followIdentifierLetter_, identifier, identifierLetter, identifierLetter_, identifier_, isMatcher, letter, letter_, letters, literal, literal_, lower, lower_, makeInfo, matchers, may, memoSymbolIndex, memorize, notp, orp, parse, recursive, seperatedList, some, spaces, spaces1, spaces1_, spaces_, times, timesSeperatedList, upper, upper_, wrap, wrap_, + var andp, any, char, combinators, digit, digits, follow, followIdentifierLetter_, identifier, identifierLetter, isMatcher, letter, letters, literal, lower, makeInfo, matchers, may, memoSymbolIndex, memorize, notp, orp, parse, recursive, seperatedList, some, spaces, spaces1, times, timesSeperatedList, upper, wrap, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, __slice = [].slice; @@ -8,10 +8,17 @@ return typeof item === "function"; }; - exports.makeInfo = makeInfo = function(data) { + exports.makeInfo = makeInfo = function(data, options) { + if (options == null) { + options = { + cursor: 0, + tabWidth: 2 + }; + } return { data: data, - cursor: 0, + cursor: options.cursor || 0, + tabWidth: options.tabWidth || 2, parsingLeftRecursives: {}, parseCache: {} }; @@ -28,15 +35,17 @@ memoSymbolIndex++; parsingLeftRecursives = info.parsingLeftRecursives; parseCache = info.parseCache[tag] = {}; - return function(start) { - var callPath, m, result, _ref, _ref1; + return function() { + var callPath, m, result, start, _ref, _ref1; + start = info.cursor; callPath = (_ref = parsingLeftRecursives[start]) != null ? _ref : parsingLeftRecursives[start] = []; if (__indexOf.call(callPath, tag) < 0) { callPath.push(tag); m = (_ref1 = parseCache[start]) != null ? _ref1 : parseCache[start] = [void 0, start]; while (1) { - result = rule(start); + info.cursor = start; + result = rule(); if (!result) { result = m[0]; info.cursor = m[1]; @@ -69,15 +78,16 @@ tag = index + ':'; memoSymbolIndex++; parseCache = info.parseCache[tag] = {}; - return function(start) { - var m, result; + return function() { + var m, result, start; + start = info.cursor; m = parseCache[start]; if (m) { info.cursor = m[1]; return m[0]; } else { - result = rule(start); + result = rule(); parseCache[start] = [result, info.cursor]; return result; } @@ -104,13 +114,12 @@ } return _results; })(); - return function(start) { + return function() { var result, _i, _len; - info.cursor = start; for (_i = 0, _len = items.length; _i < _len; _i++) { item = items[_i]; - if (!(result = item(info.cursor))) { + if (!(result = item())) { return; } } @@ -138,13 +147,14 @@ } return _results; })(); - return function(start) { - var result, _i, _len; + return function() { + var result, start, _i, _len; + start = info.cursor; for (_i = 0, _len = items.length; _i < _len; _i++) { item = items[_i]; info.cursor = start; - if (result = item(start)) { + if (result = item()) { return result; } } @@ -158,8 +168,8 @@ if (!isMatcher(item)) { item = literal(info)(item); } - return function(start) { - return !item(start); + return function() { + return !item(); }; }; }; @@ -169,11 +179,11 @@ if (!isMatcher(item)) { item = literal(info)(item); } - return function(start) { - var x; + return function() { + var start, x; - info.cursor = start; - if (x = item(info.cursor)) { + start = info.cursor; + if (x = item()) { return x; } else { info.cursor = start; @@ -188,12 +198,11 @@ if (!isMatcher(item)) { item = literal(info)(item); } - return function(start) { + return function() { var result, x; result = []; - info.cursor = start; - while ((x = item(info.cursor))) { + while ((x = item())) { result.push(x); } return result; @@ -206,20 +215,15 @@ if (!isMatcher(item)) { item = literal(info)(item); } - return function(start) { + return function() { var result, x; - result = []; - info.cursor = start; - if (!(x = item(info.cursor))) { - return x; + if (!(x = item())) { + return; } - while (1) { + result = [x]; + while ((x = item())) { result.push(x); - x = item(info.cursor); - if (!x) { - break; - } } return result; }; @@ -231,13 +235,12 @@ if (!isMatcher(item)) { item = literal(info)(item); } - return function(start) { + return function() { var i, x; - info.cursor = start; i = 0; while (i++ < n) { - if (x = item(info.cursor)) { + if (x = item()) { result.push(x); } else { return; @@ -251,7 +254,7 @@ exports.seperatedList = seperatedList = function(info) { return function(item, separator) { if (separator == null) { - separator = spaces; + separator = spaces(info); } if (!isMatcher(item)) { item = literal(info)(item); @@ -259,20 +262,15 @@ if (!isMatcher(separator)) { separator = literal(info)(separator); } - return function(start) { + return function() { var result, x; - info.cursor = start; - result = []; - x = item(info.cursor); - if (!x) { + if (!(x = item())) { return; } - while (1) { + result = [x]; + while (separator() && (x = item())) { result.push(x); - if (!(x = item(info.cursor))) { - break; - } } return result; }; @@ -282,7 +280,7 @@ exports.timesSeperatedList = timesSeperatedList = function(info) { return function(item, n, separator) { if (separator == null) { - separator = spaces; + separator = spaces(info); } if (!isMatcher(item)) { item = literal(info)(item); @@ -290,20 +288,19 @@ if (!isMatcher(separator)) { separator = literal(info)(separator); } - return function(start) { + return function() { var i, result, x; - info.cursor = start; - result = []; - x = item(info.cursor); - if (!x) { + if (!(x = item())) { return; } + result = [x]; i = 1; while (i++ < n) { - result.push(x); - if (!(x = item(info.cursor))) { - break; + if (separator() && (x = item())) { + result.push(x); + } else { + return; } } return result; @@ -316,13 +313,15 @@ if (!isMatcher(item)) { item = literal(info)(item); } - return function(start) { - var x; + return function() { + var start, x; - info.cursor = start; - if (x = item(info.cursor)) { + start = info.cursor; + if (x = item()) { info.cursor = start; return x; + } else { + info.cursor = start; } }; }; @@ -367,10 +366,11 @@ exports.literal = literal = function(info) { return function(string) { - return function(start) { - var len, stop; + return function() { + var len, start, stop; len = string.length; + start = info.cursor; if (info.data.slice(start, stop = start + len) === string) { info.cursor = stop; return true; @@ -379,32 +379,7 @@ }; }; - exports.literal_ = literal_ = function(info) { - return function(string) { - return function(start) { - var len, stop; - - len = string.length; - if (info.data.slice(info.cursor, stop = info.cursor + len) === string) { - info.cursor = stop; - return true; - } - }; - }; - }; - exports.char = char = function(info) { - return function(c) { - return function(start) { - if (info.data[start] === c) { - info.cursor = start + 1; - return c; - } - }; - }; - }; - - exports.char_ = char_ = function(info) { return function(c) { return function() { if (info.data[info.cursor] === c) { @@ -416,36 +391,15 @@ }; exports.spaces = spaces = function(info) { - return function(start) { - var data, len; - - data = info.data; - len = 0; - info.cursor = start; - while (1) { - switch (data[info.cursor++]) { - case ' ': - len++; - break; - case '\t': - len += tabWidth; - break; - default: - break; - } - } - return len; - }; - }; - - exports.spaces_ = spaces_ = function(info) { return function() { - var data, len; + var cursor, data, len, tabWidth; data = info.data; len = 0; + cursor = info.cursor; + tabWidth = info.tabWidth; while (1) { - switch (data[info.cursor++]) { + switch (data[cursor++]) { case ' ': len++; break; @@ -456,45 +410,21 @@ break; } } - return len; + info.cursor = cursor; + return len + 1; }; }; exports.spaces1 = spaces1 = function(info) { - return function(start) { - var data, len; - - data = info.data; - len = 0; - info.cursor = start; - while (1) { - switch (data[info.cursor++]) { - case ' ': - len++; - break; - case '\t': - len += tabWidth; - break; - default: - break; - } - } - if (len) { - return info.cursor = info.cursor; - return len; - } - }; - }; - - exports.spaces1_ = spaces1_ = function(info) { return function() { - var data, len; + var cursor, data, len, tabWidth; data = info.data; + cursor = info.cursor; len = 0; - info.cursor = start; + tabWidth = info.tabWidth; while (1) { - switch (data[info.cursor++]) { + switch (data[cursor++]) { case ' ': len++; break; @@ -505,35 +435,12 @@ break; } } - if (len) { - return info.cursor = info.cursor; - return len; - } + info.cursor = cursor; + return len; }; }; exports.wrap = wrap = function(info) { - return function(item, left, right) { - if (left == null) { - left = spaces(info); - } - if (right == null) { - right = spaces(info); - } - if (!isMatcher(item)) { - item = literal(info)(item); - } - return function(start) { - var result; - - if (left(start) && (result = item(info.cursor) && right(info.cursor))) { - return result; - } - }; - }; - }; - - exports.wrap_ = wrap_ = function(info) { return function(item, left, right) { if (left == null) { left = spaces(info); @@ -547,7 +454,7 @@ return function() { var result; - if (left(info.cursor) && (result = item(info.cursor) && right(info.cursor))) { + if (left() && (result = item() && right())) { return result; } }; @@ -555,19 +462,6 @@ }; exports.identifierLetter = identifierLetter = function(info) { - return function(start) { - var c; - - start = info.cursor; - c = info.data[info.cursor]; - if (c === '@' || c === '_' || ('a' <= c && c < 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9')) { - info.cursor++; - return true; - } - }; - }; - - exports.identifierLetter_ = identifierLetter_ = function(info) { return function() { var c; @@ -584,141 +478,79 @@ var c; c = info.data[info.cursor]; - return c === '@' || c === '_' || ('a' <= c && c < 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9'); + return (c === '@' || c === '_' || ('a' <= c && c < 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9')) && c; }; }; exports.digit = digit = function(info) { - return function(start) { - var c; - - c = info.data[start]; - if (('0' <= c && c <= '9')) { - return info.cursor = start + 1; - } - }; - }; - - exports.digit_ = digit_ = function(info) { return function() { var c; c = info.data[info.cursor]; if (('0' <= c && c <= '9')) { - return info.cursor++; + info.cursor++; + return c; } }; }; exports.letter = letter = function(info) { - return function(start) { - var c; - - c = info.data[start]; - if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) { - return info.cursor = start + 1; - } - }; - }; - - exports.letter_ = letter_ = function(info) { return function() { var c; c = info.data[info.cursor]; if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) { - return info.cursor++; + info.cursor++; + return c; } }; }; exports.lower = lower = function(info) { - return function(start) { - var c; - - c = info.data[start]; - if (('a' <= c && c <= 'z')) { - return info.cursor = start + 1; - } - }; - }; - - exports.lower_ = lower_ = function(info) { return function() { var c; c = info.data[info.cursor]; if (('a' <= c && c <= 'z')) { - return info.cursor++; + info.cursor++; + return c; } }; }; exports.upper = upper = function(info) { - return function(start) { - var c; - - c = info.data[start]; - if (('A' <= c && c <= 'Z')) { - return info.cursor = start + 1; - } - }; - }; - - exports.upper_ = upper_ = function(info) { return function() { var c; c = info.data[info.cursor]; if (('A' <= c && c <= 'Z')) { - return info.cursor++; - } - }; - }; - - exports.identifier = identifier = function(info) { - return function(start) { - var c, data; - - data = info.data; - info.cursor = start; - c = data[info.cursor]; - if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || 'c' === '@' || 'c' === '_') { info.cursor++; - } else { - return; - } - while (1) { - c = data[info.cursor]; - if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || 'c' === '@' || 'c' === '_') { - info.cursor++; - } else { - break; - } + return c; } - return true; }; }; - exports.identifier_ = identifier_ = function(info) { + exports.identifier = identifier = function(info) { return function() { - var c, data; + var c, cursor, data; data = info.data; - c = data[info.cursor]; + cursor = info.cursor; + c = data[cursor]; if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || 'c' === '@' || 'c' === '_') { - info.cursor++; + cursor++; } else { return; } while (1) { - c = data[info.cursor]; + c = data[cursor]; if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || 'c' === '@' || 'c' === '_') { - info.cursor++; + cursor++; } else { break; } } + info.cursor = cursor; return true; }; }; @@ -726,28 +558,17 @@ exports.matchers = matchers = function(info) { return { literal: literal(info), - literal_: literal_(info), char: char(info), - char_: char_(info), spaces: spaces(info), - spaces_: spaces_(info), spaces1: spaces1(info), - spaces1_: spaces1_(info), wrap: wrap(info), - wrap_: wrap_(info), identifierLetter: identifierLetter(info), - identifierLetter_: identifierLetter_(info), - followIdentifierLetter_: followIdentifierLetter_(info), + followIdentifierLetter: followIdentifierLetter(info), digit: digit(info), - digit_: digit_(info), letter: letter(info), - letter_: letter_(info), lower: lower(info), - lower_: lower_(info), upper: upper(info), - upper_: upper_(info), - identifier: identifier(info), - identifier_: identifier_(info) + identifier: identifier(info) }; }; @@ -833,29 +654,31 @@ var grammar, makeGrammar; makeGrammar = function(info) { - var a, b, rec, rules, x, y, _ref; + var a, b, rec, rules, x, y, _ref, _ref1; _ref = letters(info), a = _ref.a, b = _ref.b, x = _ref.x, y = _ref.y; - rec = recursive(info); + _ref1 = combinators(info), rec = _ref1.rec, orp = _ref1.orp; return rules = { - Root: function(start) { + Root: function() { var m; - return (m = rules.A(start)) && z(info.cursor) && m + 'z'; + return (m = rules.A()) && z() && m + 'z'; }, - A: rec(function(start) { + A: rec(orp((function() { var m; - return (m = rules.B(start)) && x(info.cursor) && m + 'x' || m || a(start); - }), - B: rec(function(start) { + return (m = rules.B()) && x() && m + 'x' || m; + }), a)), + B: rec(orp((function() { var m; - return (m = rules.A(start)) && y(info.cursor) && m + 'y' || rules.C(start); - }), - C: rec(function(start) { - return rules.A(start) || b(start); - }) + return (m = rules.A()) && y() && m + 'y'; + }), function() { + return rules.C(); + })), + C: rec(orp((function() { + return rules.A(); + }), b)) }; }; grammar = makeGrammar(makeInfo(text)); diff --git a/package.json b/package.json index 72df402..7914ac3 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "coffeescript" ], "author": "Caoxingming", - "version": "0.2.6", + "version": "0.2.7", "licenses": [ { "type": "MIT", diff --git a/test/testpeasy.coffee b/test/testpeasy.coffee index 473d3dd..0bbc013 100644 --- a/test/testpeasy.coffee +++ b/test/testpeasy.coffee @@ -1,65 +1,56 @@ -{makeInfo, letters, recursive} = require "../lib/peasy.js" +{makeInfo, letters, combinators} = require "../lib/peasy.js" parse1 = (text) -> makeGrammar = (info) -> - {a, b, x} = letters(info) - rec = recursive(info) - rules = - A: rec (start) -> - (m = rules.A(start)) and x(info.cursor) and m+'x' or m\ - or a(start) + {a, x} = letters(info) + {rec, orp} = combinators(info) + rules = A: rec orp(( -> (m = rules.A()) and x() and m+'x' or m), a) grammar = makeGrammar(makeInfo(text)) grammar.A(0) parse2 = (text) -> makeGrammar = (info) -> {a, b, x} = letters(info) - rec = recursive(info) - rules = - A: rec (start) -> - (m = rules.B(start)) and x(info.cursor) and m+'x' or m\ - or a(start) - B: rec (start) -> rules.A(start) or b(start) + {rec, orp} = combinators(info) + rules = {} + rules.A = rec orp((-> (m = rules.B()) and x() and m+'x' or m), a) + rules.B = rec orp(rules.A, b) + rules grammar = makeGrammar(makeInfo(text)) grammar.A(0) parse3 = (text) -> makeGrammar = (info) -> {a, b, x} = letters(info) - rec = recursive(info) - rules = - A: rec (start) -> - (m = rules.B(start)) and x(info.cursor) and m+'x' or m\ - or a(start) - B: rec (start) -> rules.C(start) - C: rec (start) -> rules.A(start) or b(start) + {rec, orp} = combinators(info) + rules = {} + rules.A = rec orp((-> (m = rules.B()) and x() and m+'x' or m), a) + rules.B = rec -> rules.C() + rules.C = rec orp(rules.A, b) + rules grammar = makeGrammar(makeInfo(text)) grammar.A(0) parse4 = (text) -> makeGrammar = (info) -> {a, b, x, y} = letters(info) - rec = recursive(info) + {rec, orp} = combinators(info) rules = - A: rec (start) -> - (m = rules.B(start)) and x(info.cursor) and m+'x' or m\ - or a(start) - B: rec (start) ->(m = rules.A(start)) and y(info.cursor) and m+'y'or rules.C(start) - C: rec (start) -> rules.A(start) or b(start) + A: rec -> orp((-> (m = rules.B()) and x() and m+'x' or m), a)() + B: rec -> orp(( -> (m = rules.A()) and y() and m+'y'), rules.C)() + C: rec -> orp(rules.A, b)() grammar = makeGrammar(makeInfo(text)) grammar.A(0) parse5 = (text) -> makeGrammar = (info) -> {a, b, x, y, z} = letters(info) - rec = recursive(info) + {rec, orp} = combinators(info) rules = - Root: (start) -> (m = rules.A(start)) and z(info.cursor) and m+'z' - A: rec (start) -> - (m = rules.B(start)) and x(info.cursor) and m+'x' or m\ - or a(start) - B: rec (start) ->(m = rules.A(start)) and y(info.cursor) and m+'y'or rules.C(start) - C: rec (start) -> rules.A(start) or b(start) + Root: -> (m = rules.A()) and z() and m+'z' + A: rec orp(( -> (m = rules.B()) and x() and m+'x' or m), a) + B: rec orp((-> (m = rules.A()) and y() and m+'y'), -> rules.C()) + C: rec orp((-> rules.A()), b) grammar = makeGrammar(makeInfo(text)) grammar.Root(0) diff --git a/test/testpeasy.js b/test/testpeasy.js index c774a30..6c0d864 100644 --- a/test/testpeasy.js +++ b/test/testpeasy.js @@ -1,23 +1,23 @@ // Generated by CoffeeScript 1.6.2 (function() { - var letters, makeInfo, parse1, parse2, parse3, parse4, parse5, recursive, xexports, _ref; + var combinators, letters, makeInfo, parse1, parse2, parse3, parse4, parse5, xexports, _ref; - _ref = require("../lib/peasy.js"), makeInfo = _ref.makeInfo, letters = _ref.letters, recursive = _ref.recursive; + _ref = require("../lib/peasy.js"), makeInfo = _ref.makeInfo, letters = _ref.letters, combinators = _ref.combinators; parse1 = function(text) { var grammar, makeGrammar; makeGrammar = function(info) { - var a, b, rec, rules, x, _ref1; + var a, orp, rec, rules, x, _ref1, _ref2; - _ref1 = letters(info), a = _ref1.a, b = _ref1.b, x = _ref1.x; - rec = recursive(info); + _ref1 = letters(info), a = _ref1.a, x = _ref1.x; + _ref2 = combinators(info), rec = _ref2.rec, orp = _ref2.orp; return rules = { - A: rec(function(start) { + A: rec(orp((function() { var m; - return (m = rules.A(start)) && x(info.cursor) && m + 'x' || m || a(start); - }) + return (m = rules.A()) && x() && m + 'x' || m; + }), a)) }; }; grammar = makeGrammar(makeInfo(text)); @@ -28,20 +28,18 @@ var grammar, makeGrammar; makeGrammar = function(info) { - var a, b, rec, rules, x, _ref1; + var a, b, orp, rec, rules, x, _ref1, _ref2; _ref1 = letters(info), a = _ref1.a, b = _ref1.b, x = _ref1.x; - rec = recursive(info); - return rules = { - A: rec(function(start) { - var m; - - return (m = rules.B(start)) && x(info.cursor) && m + 'x' || m || a(start); - }), - B: rec(function(start) { - return rules.A(start) || b(start); - }) - }; + _ref2 = combinators(info), rec = _ref2.rec, orp = _ref2.orp; + rules = {}; + rules.A = rec(orp((function() { + var m; + + return (m = rules.B()) && x() && m + 'x' || m; + }), a)); + rules.B = rec(orp(rules.A, b)); + return rules; }; grammar = makeGrammar(makeInfo(text)); return grammar.A(0); @@ -51,23 +49,21 @@ var grammar, makeGrammar; makeGrammar = function(info) { - var a, b, rec, rules, x, _ref1; + var a, b, orp, rec, rules, x, _ref1, _ref2; _ref1 = letters(info), a = _ref1.a, b = _ref1.b, x = _ref1.x; - rec = recursive(info); - return rules = { - A: rec(function(start) { - var m; - - return (m = rules.B(start)) && x(info.cursor) && m + 'x' || m || a(start); - }), - B: rec(function(start) { - return rules.C(start); - }), - C: rec(function(start) { - return rules.A(start) || b(start); - }) - }; + _ref2 = combinators(info), rec = _ref2.rec, orp = _ref2.orp; + rules = {}; + rules.A = rec(orp((function() { + var m; + + return (m = rules.B()) && x() && m + 'x' || m; + }), a)); + rules.B = rec(function() { + return rules.C(); + }); + rules.C = rec(orp(rules.A, b)); + return rules; }; grammar = makeGrammar(makeInfo(text)); return grammar.A(0); @@ -77,23 +73,27 @@ var grammar, makeGrammar; makeGrammar = function(info) { - var a, b, rec, rules, x, y, _ref1; + var a, b, orp, rec, rules, x, y, _ref1, _ref2; _ref1 = letters(info), a = _ref1.a, b = _ref1.b, x = _ref1.x, y = _ref1.y; - rec = recursive(info); + _ref2 = combinators(info), rec = _ref2.rec, orp = _ref2.orp; return rules = { - A: rec(function(start) { - var m; + A: rec(function() { + return orp((function() { + var m; - return (m = rules.B(start)) && x(info.cursor) && m + 'x' || m || a(start); + return (m = rules.B()) && x() && m + 'x' || m; + }), a)(); }), - B: rec(function(start) { - var m; + B: rec(function() { + return orp((function() { + var m; - return (m = rules.A(start)) && y(info.cursor) && m + 'y' || rules.C(start); + return (m = rules.A()) && y() && m + 'y'; + }), rules.C)(); }), - C: rec(function(start) { - return rules.A(start) || b(start); + C: rec(function() { + return orp(rules.A, b)(); }) }; }; @@ -105,29 +105,31 @@ var grammar, makeGrammar; makeGrammar = function(info) { - var a, b, rec, rules, x, y, z, _ref1; + var a, b, orp, rec, rules, x, y, z, _ref1, _ref2; _ref1 = letters(info), a = _ref1.a, b = _ref1.b, x = _ref1.x, y = _ref1.y, z = _ref1.z; - rec = recursive(info); + _ref2 = combinators(info), rec = _ref2.rec, orp = _ref2.orp; return rules = { - Root: function(start) { + Root: function() { var m; - return (m = rules.A(start)) && z(info.cursor) && m + 'z'; + return (m = rules.A()) && z() && m + 'z'; }, - A: rec(function(start) { + A: rec(orp((function() { var m; - return (m = rules.B(start)) && x(info.cursor) && m + 'x' || m || a(start); - }), - B: rec(function(start) { + return (m = rules.B()) && x() && m + 'x' || m; + }), a)), + B: rec(orp((function() { var m; - return (m = rules.A(start)) && y(info.cursor) && m + 'y' || rules.C(start); - }), - C: rec(function(start) { - return rules.A(start) || b(start); - }) + return (m = rules.A()) && y() && m + 'y'; + }), function() { + return rules.C(); + })), + C: rec(orp((function() { + return rules.A(); + }), b)) }; }; grammar = makeGrammar(makeInfo(text)); diff --git a/whatsnew.md b/whatsnew.md index 520c02d..4f02b34 100644 --- a/whatsnew.md +++ b/whatsnew.md @@ -1,5 +1,9 @@ ## What's new in peasy +### what's new in 0.2.7 + * remove the parameter `start` from the matcher functions + * `orp` play well in testpeasy + ### what's new in 0.2.6 * better support to modular grammar * simpler method to declare left recursive rule and memorized rule