neg 0.3.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +1 -1
- data/LICENSE.txt +1 -1
- data/README.md +168 -1
- data/TODO.txt +4 -5
- data/lib/neg.rb +1 -0
- data/lib/neg/errors.rb +66 -0
- data/lib/neg/input.rb +1 -1
- data/lib/neg/parser.rb +71 -49
- data/lib/neg/translator.rb +76 -0
- data/lib/neg/version.rb +2 -2
- data/spec/parser_alternative_spec.rb +8 -3
- data/spec/parser_character_spec.rb +2 -19
- data/spec/parser_lookahead_parser_spec.rb +18 -16
- data/spec/parser_non_terminal_spec.rb +9 -12
- data/spec/parser_repetition_spec.rb +5 -14
- data/spec/parser_sequence_spec.rb +2 -15
- data/spec/parser_spec.rb +15 -0
- data/spec/parser_string_spec.rb +3 -3
- data/spec/sample_arith_spec.rb +90 -0
- data/spec/sample_compact_spec.rb +50 -0
- data/spec/sample_json_parser_spec.rb +169 -50
- metadata +12 -2
| @@ -0,0 +1,76 @@ | |
| 1 | 
            +
            #--
         | 
| 2 | 
            +
            # Copyright (c) 2012-2013, John Mettraux, jmettraux@gmail.com
         | 
| 3 | 
            +
            #
         | 
| 4 | 
            +
            # Permission is hereby granted, free of charge, to any person obtaining a copy
         | 
| 5 | 
            +
            # of this software and associated documentation files (the "Software"), to deal
         | 
| 6 | 
            +
            # in the Software without restriction, including without limitation the rights
         | 
| 7 | 
            +
            # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         | 
| 8 | 
            +
            # copies of the Software, and to permit persons to whom the Software is
         | 
| 9 | 
            +
            # furnished to do so, subject to the following conditions:
         | 
| 10 | 
            +
            #
         | 
| 11 | 
            +
            # The above copyright notice and this permission notice shall be included in
         | 
| 12 | 
            +
            # all copies or substantial portions of the Software.
         | 
| 13 | 
            +
            #
         | 
| 14 | 
            +
            # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         | 
| 15 | 
            +
            # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         | 
| 16 | 
            +
            # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         | 
| 17 | 
            +
            # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         | 
| 18 | 
            +
            # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         | 
| 19 | 
            +
            # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
         | 
| 20 | 
            +
            # THE SOFTWARE.
         | 
| 21 | 
            +
            #
         | 
| 22 | 
            +
            # Made in Japan.
         | 
| 23 | 
            +
            #++
         | 
| 24 | 
            +
             | 
| 25 | 
            +
             | 
| 26 | 
            +
            module Neg
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              class Translator
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def self.on(node_name, &block)
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  @rules ||=
         | 
| 33 | 
            +
                    { nil => lambda { |n| n.results.empty? ? throw(nil) : n.results } }
         | 
| 34 | 
            +
                      #
         | 
| 35 | 
            +
                      # the default rule for anonymous nodes...
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  @rules[node_name] = block
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                def self.translate(parse_tree)
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  results =
         | 
| 43 | 
            +
                    parse_tree[4].each_with_object([]) { |node, a|
         | 
| 44 | 
            +
                      catch(nil) { a << translate(node) }
         | 
| 45 | 
            +
                    }
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  apply(parse_tree, results)
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                def self.apply(parse_tree, results)
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  rule = (@rules || {})[parse_tree[0]]
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  rule ? rule.call(Node.new(parse_tree, results)) : results
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                class Node
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  attr_reader :parse_tree, :results
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  def initialize(parse_tree, results)
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                    @parse_tree = parse_tree
         | 
| 64 | 
            +
                    @results = results
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  def position;  @parse_tree[1]; end
         | 
| 68 | 
            +
                  def offset;    @parse_tree[1][0]; end
         | 
| 69 | 
            +
                  def line;      @parse_tree[1][1]; end
         | 
| 70 | 
            +
                  def column;    @parse_tree[1][2]; end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  def result;    @parse_tree[3]; end
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
            end
         | 
| 76 | 
            +
             | 
    
        data/lib/neg/version.rb
    CHANGED
    
    | @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            #--
         | 
| 2 | 
            -
            # Copyright (c) 2012- | 
| 2 | 
            +
            # Copyright (c) 2012-2013, John Mettraux, jmettraux@gmail.com
         | 
| 3 3 | 
             
            #
         | 
| 4 4 | 
             
            # Permission is hereby granted, free of charge, to any person obtaining a copy
         | 
| 5 5 | 
             
            # of this software and associated documentation files (the "Software"), to deal
         | 
| @@ -25,6 +25,6 @@ | |
| 25 25 |  | 
| 26 26 | 
             
            module Neg
         | 
| 27 27 |  | 
| 28 | 
            -
              VERSION = '0. | 
| 28 | 
            +
              VERSION = '1.0.0'
         | 
| 29 29 | 
             
            end
         | 
| 30 30 |  | 
| @@ -11,14 +11,19 @@ describe Neg::Parser::AlternativeParser do | |
| 11 11 | 
             
              it 'parses' do
         | 
| 12 12 |  | 
| 13 13 | 
             
                AltParser.parse('x').should ==
         | 
| 14 | 
            -
                  [ :text, [ 0, 1, 1 ], true,  | 
| 15 | 
            -
                    [ nil, [ 0, 1, 1 ], true, 'x', [] ] ] ]
         | 
| 14 | 
            +
                  [ :text, [ 0, 1, 1 ], true, 'x', [] ]
         | 
| 16 15 | 
             
              end
         | 
| 17 16 |  | 
| 18 17 | 
             
              it 'parses (2nd alternative succeeds)' do
         | 
| 19 18 |  | 
| 20 19 | 
             
                AltParser.parse('y').should ==
         | 
| 21 | 
            -
                  [ :text, [ 0, 1, 1 ], true,  | 
| 20 | 
            +
                  [ :text, [ 0, 1, 1 ], true, 'y', [] ]
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              it 'parses (2nd alternative succeeds) :noreduce => true' do
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                AltParser.parse('y', :noreduce => true).should ==
         | 
| 26 | 
            +
                  [ :text, [ 0, 1, 1 ], true, 'y', [
         | 
| 22 27 | 
             
                    [ nil, [ 0, 1, 1 ], false, 'expected "x", got "y"', [] ],
         | 
| 23 28 | 
             
                    [ nil, [ 0, 1, 1 ], true, 'y', [] ] ] ]
         | 
| 24 29 | 
             
              end
         | 
| @@ -15,12 +15,7 @@ describe Neg::Parser::CharacterParser do | |
| 15 15 | 
             
                it 'parses "xy"' do
         | 
| 16 16 |  | 
| 17 17 | 
             
                  parser.parse('xy').should ==
         | 
| 18 | 
            -
                    [ :text,
         | 
| 19 | 
            -
                      [ 0, 1, 1 ],
         | 
| 20 | 
            -
                      true,
         | 
| 21 | 
            -
                      nil,
         | 
| 22 | 
            -
                      [ [ nil, [ 0, 1, 1 ], true, "x", [] ],
         | 
| 23 | 
            -
                        [ nil, [ 1, 1, 2 ], true, "y", [] ] ] ]
         | 
| 18 | 
            +
                    [ :text, [ 0, 1, 1 ], true, 'xy', [] ]
         | 
| 24 19 | 
             
                end
         | 
| 25 20 |  | 
| 26 21 | 
             
                it 'fails gracefully' do
         | 
| @@ -55,19 +50,7 @@ describe Neg::Parser::CharacterParser do | |
| 55 50 | 
             
                it 'parses "tel:0-99"' do
         | 
| 56 51 |  | 
| 57 52 | 
             
                  parser.parse('tel:0-99').should ==
         | 
| 58 | 
            -
                    [ :text,
         | 
| 59 | 
            -
                      [ 0, 1, 1 ],
         | 
| 60 | 
            -
                      true,
         | 
| 61 | 
            -
                      nil,
         | 
| 62 | 
            -
                      [ [ nil, [ 0, 1, 1 ], true, "tel:", [] ],
         | 
| 63 | 
            -
                        [ nil,
         | 
| 64 | 
            -
                          [ 4, 1, 5 ],
         | 
| 65 | 
            -
                          true,
         | 
| 66 | 
            -
                          nil,
         | 
| 67 | 
            -
                          [ [ nil, [ 4, 1, 5 ], true, "0", [] ],
         | 
| 68 | 
            -
                            [ nil, [ 5, 1, 6 ], true, "-", [] ],
         | 
| 69 | 
            -
                            [ nil, [ 6, 1, 7 ], true, "9", [] ],
         | 
| 70 | 
            -
                            [ nil, [ 7, 1, 8 ], true, "9", [] ] ] ] ] ]
         | 
| 53 | 
            +
                    [ :text, [ 0, 1, 1 ], true, 'tel:0-99', [] ]
         | 
| 71 54 | 
             
                end
         | 
| 72 55 |  | 
| 73 56 | 
             
                it 'fails gracefully' do
         | 
| @@ -17,6 +17,17 @@ describe Neg::Parser::LookaheadParser do | |
| 17 17 | 
             
                it 'parses' do
         | 
| 18 18 |  | 
| 19 19 | 
             
                  parser.parse('xz').should ==
         | 
| 20 | 
            +
                    [ :root,
         | 
| 21 | 
            +
                      [ 0, 1, 1 ],
         | 
| 22 | 
            +
                      true,
         | 
| 23 | 
            +
                      nil,
         | 
| 24 | 
            +
                      [ [ :x, [ 0, 1, 1 ], true, 'x', [] ],
         | 
| 25 | 
            +
                        [ :z, [ 1, 1, 2 ], true, 'z', [] ] ] ]
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                it 'parses (:noreduce => true)' do
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  parser.parse('xz', :noreduce => true).should ==
         | 
| 20 31 | 
             
                    [ :root,
         | 
| 21 32 | 
             
                      [ 0, 1, 1 ],
         | 
| 22 33 | 
             
                      true,
         | 
| @@ -24,11 +35,11 @@ describe Neg::Parser::LookaheadParser do | |
| 24 35 | 
             
                      [ [ :x,
         | 
| 25 36 | 
             
                          [ 0, 1, 1 ],
         | 
| 26 37 | 
             
                          true,
         | 
| 27 | 
            -
                           | 
| 28 | 
            -
                          [ [ nil, [ 0, 1, 1 ], true,  | 
| 29 | 
            -
                            [ nil, [ 1, 1, 2 ], true,  | 
| 30 | 
            -
                              [ nil, [ 1, 1, 2 ], true,  | 
| 31 | 
            -
                        [ :z, [ 1, 1, 2 ], true,  | 
| 38 | 
            +
                          'x',
         | 
| 39 | 
            +
                          [ [ nil, [ 0, 1, 1 ], true, 'x', [] ],
         | 
| 40 | 
            +
                            [ nil, [ 1, 1, 2 ], true, '', [
         | 
| 41 | 
            +
                              [ nil, [ 1, 1, 2 ], true, 'z', [] ] ] ] ] ],
         | 
| 42 | 
            +
                        [ :z, [ 1, 1, 2 ], true, 'z', [] ] ] ]
         | 
| 32 43 | 
             
                end
         | 
| 33 44 |  | 
| 34 45 | 
             
                it 'fails gracefully' do
         | 
| @@ -79,17 +90,8 @@ describe Neg::Parser::LookaheadParser do | |
| 79 90 | 
             
                      [ 0, 1, 1 ],
         | 
| 80 91 | 
             
                      true,
         | 
| 81 92 | 
             
                      nil,
         | 
| 82 | 
            -
                      [ [ :x,
         | 
| 83 | 
            -
             | 
| 84 | 
            -
                          true,
         | 
| 85 | 
            -
                          nil,
         | 
| 86 | 
            -
                          [ [ nil, [ 0, 1, 1 ], true, "x", [] ],
         | 
| 87 | 
            -
                            [ nil,
         | 
| 88 | 
            -
                              [ 1, 1, 2 ],
         | 
| 89 | 
            -
                              true,
         | 
| 90 | 
            -
                              nil,
         | 
| 91 | 
            -
                              [ [ nil, [ 1, 1, 2 ], false, "expected \"y\", got \"z\"", [] ] ] ] ] ],
         | 
| 92 | 
            -
                        [ :z, [ 1, 1, 2 ], true, "z", [] ] ] ]
         | 
| 93 | 
            +
                      [ [ :x, [ 0, 1, 1 ], true, 'x', [] ],
         | 
| 94 | 
            +
                        [ :z, [ 1, 1, 2 ], true, 'z', [] ] ] ]
         | 
| 93 95 | 
             
                end
         | 
| 94 96 |  | 
| 95 97 | 
             
                it 'fails gracefully' do
         | 
| @@ -27,9 +27,7 @@ describe Neg::Parser::NonTerminalParser do | |
| 27 27 | 
             
                  end
         | 
| 28 28 |  | 
| 29 29 | 
             
                  parser.parse('X').should ==
         | 
| 30 | 
            -
                    [ :x, [ 0, 1, 1 ], true,  | 
| 31 | 
            -
                      [ nil, [ 0, 1, 1 ], false, "expected \"x\", got \"X\"", [] ],
         | 
| 32 | 
            -
                      [ nil, [ 0, 1, 1 ], true, "X", [] ] ] ]
         | 
| 30 | 
            +
                    [ :x, [ 0, 1, 1 ], true, 'X', [] ]
         | 
| 33 31 | 
             
                end
         | 
| 34 32 |  | 
| 35 33 | 
             
                it 'is rendered as x when on the right side' do
         | 
| @@ -49,14 +47,15 @@ describe Neg::Parser::NonTerminalParser do | |
| 49 47 | 
             
                  }.strip
         | 
| 50 48 | 
             
                end
         | 
| 51 49 |  | 
| 52 | 
            -
                it ' | 
| 50 | 
            +
                it 'does not bind if the name starts with _' do
         | 
| 53 51 |  | 
| 54 52 | 
             
                  parser = Class.new(Neg::Parser) do
         | 
| 55 | 
            -
                    sentence == ( | 
| 56 | 
            -
                     | 
| 53 | 
            +
                    sentence == (_word + ` `) * 1
         | 
| 54 | 
            +
                    _word  == _("a-z") * 1
         | 
| 57 55 | 
             
                  end
         | 
| 58 56 |  | 
| 59 | 
            -
                   | 
| 57 | 
            +
                  parser.parse("ab cd ").should ==
         | 
| 58 | 
            +
                    [ :sentence, [ 0, 1, 1 ], true, 'ab cd ', [] ]
         | 
| 60 59 | 
             
                end
         | 
| 61 60 | 
             
              end
         | 
| 62 61 |  | 
| @@ -87,11 +86,9 @@ describe Neg::Parser::NonTerminalParser do | |
| 87 86 | 
             
                      [ 0, 1, 1 ],
         | 
| 88 87 | 
             
                      true,
         | 
| 89 88 | 
             
                      nil,
         | 
| 90 | 
            -
                      [ [ 'vehicle', [ 0, 1, 1 ], true,  | 
| 91 | 
            -
                        [ nil, [  | 
| 92 | 
            -
             | 
| 93 | 
            -
                      [ 'city', [ 4, 1, 5 ], true, nil, [
         | 
| 94 | 
            -
                        [ nil, [ 4, 1, 5 ], true, 'cluj', [] ] ] ] ] ]
         | 
| 89 | 
            +
                      [ [ 'vehicle', [ 0, 1, 1 ], true, 'car', [] ],
         | 
| 90 | 
            +
                        [ nil, [ 3, 1, 4 ], true, '_', [] ],
         | 
| 91 | 
            +
                        [ 'city', [ 4, 1, 5 ], true, 'cluj', [] ] ] ]
         | 
| 95 92 | 
             
                end
         | 
| 96 93 | 
             
              end
         | 
| 97 94 | 
             
            end
         | 
| @@ -15,7 +15,7 @@ describe Neg::Parser::RepetitionParser do | |
| 15 15 | 
             
                it 'parses the empty string' do
         | 
| 16 16 |  | 
| 17 17 | 
             
                  parser.parse('').should ==
         | 
| 18 | 
            -
                    [ :text, [ 0, 1, 1 ], true,  | 
| 18 | 
            +
                    [ :text, [ 0, 1, 1 ], true, '', [] ]
         | 
| 19 19 | 
             
                end
         | 
| 20 20 |  | 
| 21 21 | 
             
                it 'fails gracefully' do
         | 
| @@ -48,16 +48,13 @@ describe Neg::Parser::RepetitionParser do | |
| 48 48 | 
             
                it 'parses the empty string' do
         | 
| 49 49 |  | 
| 50 50 | 
             
                  parser.parse('').should ==
         | 
| 51 | 
            -
                    [ :text, [ 0, 1, 1 ], true,  | 
| 51 | 
            +
                    [ :text, [ 0, 1, 1 ], true, '', [] ]
         | 
| 52 52 | 
             
                end
         | 
| 53 53 |  | 
| 54 54 | 
             
                it 'parses' do
         | 
| 55 55 |  | 
| 56 56 | 
             
                  parser.parse('xxx').should ==
         | 
| 57 | 
            -
                    [ :text, [ 0, 1, 1 ], true,  | 
| 58 | 
            -
                      [ nil, [ 0, 1, 1 ], true, 'x', [] ],
         | 
| 59 | 
            -
                      [ nil, [ 1, 1, 2 ], true, 'x', [] ],
         | 
| 60 | 
            -
                      [ nil, [ 2, 1, 3 ], true, 'x', [] ] ] ]
         | 
| 57 | 
            +
                    [ :text, [ 0, 1, 1 ], true, 'xxx', [] ]
         | 
| 61 58 | 
             
                end
         | 
| 62 59 |  | 
| 63 60 | 
             
                it 'fails gracefully' do
         | 
| @@ -81,10 +78,7 @@ describe Neg::Parser::RepetitionParser do | |
| 81 78 | 
             
                it 'parses' do
         | 
| 82 79 |  | 
| 83 80 | 
             
                  parser.parse('xxx').should ==
         | 
| 84 | 
            -
                    [ :text, [ 0, 1, 1 ], true,  | 
| 85 | 
            -
                      [ nil, [ 0, 1, 1 ], true, 'x', [] ],
         | 
| 86 | 
            -
                      [ nil, [ 1, 1, 2 ], true, 'x', [] ],
         | 
| 87 | 
            -
                      [ nil, [ 2, 1, 3 ], true, 'x', [] ] ] ]
         | 
| 81 | 
            +
                    [ :text, [ 0, 1, 1 ], true, 'xxx', [] ]
         | 
| 88 82 | 
             
                end
         | 
| 89 83 |  | 
| 90 84 | 
             
                it 'fails gracefully' do
         | 
| @@ -107,10 +101,7 @@ describe Neg::Parser::RepetitionParser do | |
| 107 101 | 
             
                it 'parses' do
         | 
| 108 102 |  | 
| 109 103 | 
             
                  parser.parse('xxx').should ==
         | 
| 110 | 
            -
                    [ :text, [ 0, 1, 1 ], true,  | 
| 111 | 
            -
                      [ nil, [ 0, 1, 1 ], true, 'x', [] ],
         | 
| 112 | 
            -
                      [ nil, [ 1, 1, 2 ], true, 'x', [] ],
         | 
| 113 | 
            -
                      [ nil, [ 2, 1, 3 ], true, 'x', [] ] ] ]
         | 
| 104 | 
            +
                    [ :text, [ 0, 1, 1 ], true, 'xxx', [] ]
         | 
| 114 105 | 
             
                end
         | 
| 115 106 |  | 
| 116 107 | 
             
                it 'fails gracefully' do
         | 
| @@ -11,9 +11,7 @@ describe Neg::Parser::SequenceParser do | |
| 11 11 | 
             
              it 'parses' do
         | 
| 12 12 |  | 
| 13 13 | 
             
                SeqParser.parse('xy').should ==
         | 
| 14 | 
            -
                  [ :text, [ 0, 1, 1 ], true,  | 
| 15 | 
            -
                    [ nil, [ 0, 1, 1 ], true, 'x', [] ],
         | 
| 16 | 
            -
                    [ nil, [ 1, 1, 2 ], true, 'y', [] ] ] ]
         | 
| 14 | 
            +
                  [ :text, [ 0, 1, 1 ], true, 'xy', [] ]
         | 
| 17 15 | 
             
              end
         | 
| 18 16 |  | 
| 19 17 | 
             
              it 'fails gracefully' do
         | 
| @@ -51,18 +49,7 @@ describe Neg::Parser::SequenceParser do | |
| 51 49 | 
             
                    [ 0, 1, 1 ],
         | 
| 52 50 | 
             
                    true,
         | 
| 53 51 | 
             
                    nil,
         | 
| 54 | 
            -
                    [ [ : | 
| 55 | 
            -
                        [ 0, 1, 1 ],
         | 
| 56 | 
            -
                        false,
         | 
| 57 | 
            -
                        nil,
         | 
| 58 | 
            -
                        [ [ nil, [ 0, 1, 1 ], true, "poo", [] ],
         | 
| 59 | 
            -
                          [ nil, [ 3, 1, 4 ], false, "expected \"dle\", got \"l\"", [] ] ] ],
         | 
| 60 | 
            -
                      [ :pool,
         | 
| 61 | 
            -
                        [ 0, 1, 1 ],
         | 
| 62 | 
            -
                        true,
         | 
| 63 | 
            -
                        nil,
         | 
| 64 | 
            -
                        [ [ nil, [ 0, 1, 1 ], true, "poo", [] ],
         | 
| 65 | 
            -
                          [ nil, [ 3, 1, 4 ], true, "l", [] ] ] ] ] ]
         | 
| 52 | 
            +
                    [ [ :pool, [ 0, 1, 1 ], true, "pool", [] ] ] ]
         | 
| 66 53 | 
             
              end
         | 
| 67 54 | 
             
            end
         | 
| 68 55 |  | 
    
        data/spec/parser_spec.rb
    CHANGED
    
    | @@ -18,6 +18,21 @@ describe 'Neg::Parser' do | |
| 18 18 | 
             
                end
         | 
| 19 19 | 
             
              end
         | 
| 20 20 |  | 
| 21 | 
            +
            #  describe 'the blankslate' do
         | 
| 22 | 
            +
            #
         | 
| 23 | 
            +
            #    it 'lets through node names like "send"' do
         | 
| 24 | 
            +
            #
         | 
| 25 | 
            +
            #      parser =
         | 
| 26 | 
            +
            #        Class.new(Neg::Parser) do
         | 
| 27 | 
            +
            #          send == `x`
         | 
| 28 | 
            +
            #        end
         | 
| 29 | 
            +
            #
         | 
| 30 | 
            +
            #      parser.parse("x").should == [ :send, [ 0, 1, 1 ], true, 'x', [] ]
         | 
| 31 | 
            +
            #    end
         | 
| 32 | 
            +
            #  end
         | 
| 33 | 
            +
              #
         | 
| 34 | 
            +
              # not worth the pain for now
         | 
| 35 | 
            +
             | 
| 21 36 | 
             
              describe '.parse' do
         | 
| 22 37 |  | 
| 23 38 | 
             
                let(:parser) {
         | 
    
        data/spec/parser_string_spec.rb
    CHANGED
    
    | @@ -10,7 +10,7 @@ describe Neg::Parser::StringParser do | |
| 10 10 |  | 
| 11 11 | 
             
                  parser = Neg::Parser::StringParser.new('xxx')
         | 
| 12 12 |  | 
| 13 | 
            -
                  parser.parse('xxx').should ==
         | 
| 13 | 
            +
                  parser.parse('xxx', {}).should ==
         | 
| 14 14 | 
             
                    [ nil, [ 0, 1, 1 ], true, 'xxx', [] ]
         | 
| 15 15 | 
             
                end
         | 
| 16 16 |  | 
| @@ -18,7 +18,7 @@ describe Neg::Parser::StringParser do | |
| 18 18 |  | 
| 19 19 | 
             
                  parser = Neg::Parser::StringParser.new('xxx')
         | 
| 20 20 |  | 
| 21 | 
            -
                  parser.parse('yyy').should ==
         | 
| 21 | 
            +
                  parser.parse('yyy', {}).should ==
         | 
| 22 22 | 
             
                    [ nil, [ 0, 1, 1 ], false, 'expected "xxx", got "yyy"', [] ]
         | 
| 23 23 | 
             
                end
         | 
| 24 24 | 
             
              end
         | 
| @@ -33,7 +33,7 @@ describe Neg::Parser::StringParser do | |
| 33 33 |  | 
| 34 34 | 
             
                it 'parses an exact string' do
         | 
| 35 35 |  | 
| 36 | 
            -
                  parser.parse('x').should ==
         | 
| 36 | 
            +
                  parser.parse('x', {}).should ==
         | 
| 37 37 | 
             
                    [ :root, [ 0, 1, 1 ], true, 'x', [] ]
         | 
| 38 38 | 
             
                end
         | 
| 39 39 | 
             
              end
         | 
| @@ -0,0 +1,90 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            require 'spec_helper'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
             | 
| 5 | 
            +
            describe 'sample math parser' do
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              class ArithParser < Neg::Parser
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                expression  == operation
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                operator    == `+` | `-` | `*` | `/`
         | 
| 12 | 
            +
                operation   == value + (operator + value) * 0
         | 
| 13 | 
            +
                value       == parenthese | number
         | 
| 14 | 
            +
                parenthese  == `(` + expression + `)`
         | 
| 15 | 
            +
                number      == `-` * -1 + _('0-9') * 1
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              class ArithTranslator < Neg::Translator
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                on(:number)    { |n| n.result.to_i }
         | 
| 21 | 
            +
                on(:operator)  { |n| n.result }
         | 
| 22 | 
            +
                on(:value)     { |n| n.results.first }
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                on(:expression) { |n|
         | 
| 25 | 
            +
                  results = n.results.flatten(2)
         | 
| 26 | 
            +
                  results.size == 1 ? results.first : results
         | 
| 27 | 
            +
                }
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              def parse(s, opts={})
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                r = ArithParser.parse(s, opts)
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                if ENV['DEBUG'] == 'true' # /!\ not $DEBUG
         | 
| 35 | 
            +
                  puts "--#{s.inspect}-->"
         | 
| 36 | 
            +
                  pp r
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                r[2]
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              def translate(s)
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                ArithTranslator.translate(ArithParser.parse(s))
         | 
| 45 | 
            +
              end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
              it 'parses numbers' do
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                parse("0").should == true
         | 
| 50 | 
            +
                parse("-0").should == true
         | 
| 51 | 
            +
                parse("12").should == true
         | 
| 52 | 
            +
                parse("-3").should == true
         | 
| 53 | 
            +
                parse("-12").should == true
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
              it 'parses parentheses' do
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                parse("(1)").should == true
         | 
| 59 | 
            +
                parse("((1))").should == true
         | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
              it 'parses operations' do
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                parse("1+1").should == true
         | 
| 65 | 
            +
                parse("1+-1").should == true
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
              it 'parses at large' do
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                parse("1+(1+1)").should == true
         | 
| 71 | 
            +
                parse("12+(34-(56/78))").should == true
         | 
| 72 | 
            +
              end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
              it 'translates numbers' do
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                translate("0").should == 0
         | 
| 77 | 
            +
                translate("101").should == 101
         | 
| 78 | 
            +
              end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
              it 'translates parentheses' do
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                translate("(12)").should == 12
         | 
| 83 | 
            +
              end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
              it 'translates operations' do
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                translate("1+2+3").should == [ 1, '+', 2, '+', 3 ]
         | 
| 88 | 
            +
              end
         | 
| 89 | 
            +
            end
         | 
| 90 | 
            +
             |