pratt_parser 0.0.3 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1e4efc171572cb5d16bf779face5ad265fcb0972
4
- data.tar.gz: 1d735d195b81a1c7244232a3f104764d5f29262c
3
+ metadata.gz: 3e0997841c5a281d0c96da0544ba921381e27bf0
4
+ data.tar.gz: bd6594ea8c4174a973c62270e1c06d17edb45db8
5
5
  SHA512:
6
- metadata.gz: acd4bbdfc592f343074a53a29eee714a90f780c5655a4dc5ea7ade6ec68690850553b9759feaea59a96b3bac5071f1f70eda1fd930b2946e51b4c8ef057966e4
7
- data.tar.gz: bbf0d65f906a6579c33f15850e28ab34afece42722adc30cd27368377d71bee894ea0881c4d0f5c93157f423084e1f473be40777ff8ef3cf65f930e8695d942e
6
+ metadata.gz: 2c9fea5f8b437b4b6a1487d3b5123eab152f7a1aa068ba5ded4340f52a69adb9353419cabc07f275bb4012916248a60aa28b3fe196a546e49a09682cec52c6d5
7
+ data.tar.gz: fb381c4cf7553639cfa6d4581d0101c62a90cc0603a29629bb5b16550567f624d75185ec1943de14233caa5ff9f7b88426aaf8958d9121fcb3cdacd558f24fb6
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ ## 0.1.0 ##
2
+
3
+ * Add postfix factorial operator to examples.
4
+ * PrattParser#expect and #if? can now take a block so token
5
+ expectations can be based on something more general than class.
6
+
1
7
  ## 0.0.3 ##
2
8
 
3
9
  * examples/tree.rb supports prefix + and -.
data/README.md CHANGED
@@ -4,7 +4,7 @@ Pratt Parser
4
4
  [![Gem Version](https://badge.fury.io/rb/pratt_parser.svg)](http://badge.fury.io/rb/pratt_parser)
5
5
  [![Build Status](https://travis-ci.org/tommay/pratt_parser.svg)](https://travis-ci.org/tommay/pratt_parser)
6
6
 
7
- A Pratt Parser. Just a simple parsing framework.
7
+ A Pratt Parser. Just a simple parsing engine.
8
8
 
9
9
  Define tokens that describe your language and combine terms. Write a
10
10
  Lexer (an Enumerator) that produces a stream of tokens. Instantiate a
data/Rakefile CHANGED
@@ -14,6 +14,10 @@ task :install => :gem do
14
14
  sh "gem install pratt_parser"
15
15
  end
16
16
 
17
+ task :push => [:clean, :gem] do
18
+ sh "gem push pratt_parser-*.gem"
19
+ end
20
+
17
21
  task :clean do
18
- sh "rm *.gem"
22
+ sh "rm -f *.gem"
19
23
  end
@@ -47,6 +47,17 @@ class PrattEvaluator
47
47
  end
48
48
  end
49
49
 
50
+ class PostfixToken < Token
51
+ def initialize(lbp, &block)
52
+ super(lbp)
53
+ @block = block
54
+ end
55
+
56
+ def led(parser, left)
57
+ @block.call(left)
58
+ end
59
+ end
60
+
50
61
  class DigitToken < Token
51
62
  def initialize(lbp, value)
52
63
  super(lbp)
@@ -83,6 +94,14 @@ class PrattEvaluator
83
94
  token(char, InfixToken.new(lbp, associates, &block))
84
95
  end
85
96
 
97
+ def self.postfix(char, lbp, &block)
98
+ token(char, PostfixToken.new(lbp, &block))
99
+ end
100
+
101
+ def self.factorial(n)
102
+ n <= 1 ? 1 : n * factorial(n - 1)
103
+ end
104
+
86
105
  token("(", LeftParenToken.new(1))
87
106
  token(")", RightParenToken.new(1))
88
107
 
@@ -92,6 +111,7 @@ class PrattEvaluator
92
111
  infix("*", 30, &:*)
93
112
  infix("/", 30, &:/)
94
113
  infix("^", 40, :right, &:**)
114
+ postfix("!", 50) {|left| factorial(left)}
95
115
 
96
116
  (0..9).each do |d|
97
117
  token(d.to_s, DigitToken.new(100, d.to_f))
data/examples/tree.rb CHANGED
@@ -115,12 +115,23 @@ class TreeBuilder
115
115
 
116
116
  class BifixToken < InfixToken
117
117
  def nud(parser)
118
- # Bind super-right to the right.
118
+ # Bind super-tight to the right.
119
119
  right = parser.expression(1000000)
120
120
  UnaryNode.new(@operator, right)
121
121
  end
122
122
  end
123
123
 
124
+ class PostfixToken < Token
125
+ def initialize(function, lbp)
126
+ super(lbp)
127
+ @function = function
128
+ end
129
+
130
+ def led(parser, left)
131
+ UnaryNode.new(@function, left)
132
+ end
133
+ end
134
+
124
135
  class DigitToken < Token
125
136
  def initialize(lbp, value)
126
137
  super(lbp)
@@ -161,6 +172,10 @@ class TreeBuilder
161
172
  token(char, BifixToken.new(char, lbp, :left))
162
173
  end
163
174
 
175
+ def self.postfix(char, lbp, function, &block)
176
+ token(char, PostfixToken.new(function, lbp))
177
+ end
178
+
164
179
  token("(", LeftParenToken.new(1))
165
180
  token(")", RightParenToken.new(1))
166
181
 
@@ -170,6 +185,7 @@ class TreeBuilder
170
185
  infix("*", 30)
171
186
  infix("/", 30)
172
187
  infix("^", 40, :right)
188
+ postfix("!", 50, "factorial")
173
189
 
174
190
  (0..9).each do |d|
175
191
  token(d.to_s, DigitToken.new(100, d))
data/lib/pratt_parser.rb CHANGED
@@ -63,15 +63,21 @@ class PrattParser
63
63
  left
64
64
  end
65
65
 
66
- def expect(expected_token_class)
67
- if @token.class != expected_token_class
68
- raise "Expected #{expected_token_class}, got #{@token.class}"
66
+ def expect(expected_token_class = nil, &block)
67
+ block ||= lambda do |token|
68
+ if token.class != expected_token_class
69
+ raise "Expected #{expected_token_class}, got #{token.class}"
70
+ end
69
71
  end
72
+ block.call(@token)
70
73
  @token = @lexer.next
71
74
  end
72
75
 
73
- def if?(token_class)
74
- if @token.class == token_class
76
+ def if?(token_class, &block)
77
+ block ||= lambda do |token|
78
+ token.class == token_class
79
+ end
80
+ if block.call(@token)
75
81
  @token = @lexer.next
76
82
  end
77
83
  end
data/pratt_parser.gemspec CHANGED
@@ -8,9 +8,8 @@ Gem::Specification.new do |gem|
8
8
  gem.test_files = `git ls-files -- spec/*`.split("\n")
9
9
  gem.name = "pratt_parser"
10
10
  gem.require_paths = ["lib"]
11
- gem.version = "0.0.3"
11
+ gem.version = "0.1.0"
12
+ gem.license = "MIT"
12
13
  # Needs Enumerator which was added in 1.9.
13
14
  gem.required_ruby_version = ">= 1.9"
14
- gem.license = "MIT"
15
- # gem.required_ruby_version = '>= 2.1.0'
16
15
  end
@@ -22,6 +22,27 @@ describe PrattParser do
22
22
  token.verify
23
23
  end
24
24
 
25
+ it "passes the lookahead token to an expect block" do
26
+ expected_token = Object.new
27
+
28
+ token_a = Object.new.tap do |o|
29
+ o.singleton_class.class_eval do
30
+ define_method(:nud) do |parser|
31
+ parser.expect do |token|
32
+ token.must_be_same_as expected_token
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ lexer = Enumerator.new do |y|
39
+ y << token_a
40
+ y << expected_token
41
+ end
42
+
43
+ PrattParser.new(lexer).eval
44
+ end
45
+
25
46
  it "moves on to the next token on a valid expect" do
26
47
  class TokenA
27
48
  def nud(parser)
@@ -50,6 +71,29 @@ describe PrattParser do
50
71
  assert fetched_expected_token
51
72
  end
52
73
 
74
+ it "moves on to the next token on a valid expect with a block" do
75
+ class TokenA
76
+ def nud(parser)
77
+ parser.expect {}
78
+ end
79
+ end
80
+ class TokenC
81
+ def lbp; 0; end
82
+ end
83
+
84
+ fetched_expected_token = false
85
+ lexer = Enumerator.new do |y|
86
+ y << TokenA.new
87
+ y << Object.new
88
+ fetched_expected_token = true
89
+ y << TokenC.new
90
+ end
91
+
92
+ PrattParser.new(lexer).eval
93
+
94
+ assert fetched_expected_token
95
+ end
96
+
53
97
  it "fails on invalid expect" do
54
98
  class TokenA
55
99
  def nud(parser)
@@ -74,4 +118,23 @@ describe PrattParser do
74
118
  end
75
119
  end
76
120
 
121
+ it "fails on invalid expect with a block" do
122
+ class TokenA
123
+ def nud(parser)
124
+ parser.expect do
125
+ raise "That's not what I was expecting"
126
+ end
127
+ end
128
+ end
129
+
130
+ lexer = Enumerator.new do |y|
131
+ y << TokenA.new
132
+ y << Object.new
133
+ end
134
+
135
+ assert_raises RuntimeError do
136
+ PrattParser.new(lexer).eval
137
+ end
138
+ end
139
+
77
140
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pratt_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom May