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 +4 -4
- data/CHANGELOG +6 -0
- data/README.md +1 -1
- data/Rakefile +5 -1
- data/examples/expression_evaluator.rb +20 -0
- data/examples/tree.rb +17 -1
- data/lib/pratt_parser.rb +11 -5
- data/pratt_parser.gemspec +2 -3
- data/spec/pratt_parser_spec.rb +63 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e0997841c5a281d0c96da0544ba921381e27bf0
|
4
|
+
data.tar.gz: bd6594ea8c4174a973c62270e1c06d17edb45db8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c9fea5f8b437b4b6a1487d3b5123eab152f7a1aa068ba5ded4340f52a69adb9353419cabc07f275bb4012916248a60aa28b3fe196a546e49a09682cec52c6d5
|
7
|
+
data.tar.gz: fb381c4cf7553639cfa6d4581d0101c62a90cc0603a29629bb5b16550567f624d75185ec1943de14233caa5ff9f7b88426aaf8958d9121fcb3cdacd558f24fb6
|
data/CHANGELOG
CHANGED
data/README.md
CHANGED
@@ -4,7 +4,7 @@ Pratt Parser
|
|
4
4
|
[](http://badge.fury.io/rb/pratt_parser)
|
5
5
|
[](https://travis-ci.org/tommay/pratt_parser)
|
6
6
|
|
7
|
-
A Pratt Parser. Just a simple parsing
|
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
@@ -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-
|
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
|
-
|
68
|
-
|
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
|
-
|
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
|
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
|
data/spec/pratt_parser_spec.rb
CHANGED
@@ -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
|