treetop 1.0.2 → 1.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.
- data/Rakefile +2 -2
- data/examples/TALK +33 -0
- data/examples/lambda_calculus/arithmetic.treetop +99 -0
- data/examples/lambda_calculus/arithmetic_node_classes.rb +7 -0
- data/examples/lambda_calculus/arithmetic_test.rb +54 -0
- data/examples/lambda_calculus/lambda_calculus.treetop +139 -0
- data/examples/lambda_calculus/lambda_calculus_node_classes.rb +5 -0
- data/examples/lambda_calculus/lambda_calculus_test.rb +89 -0
- data/examples/lambda_calculus/test_helper.rb +18 -0
- data/lib/treetop/compiler/metagrammar.rb +17 -12
- data/lib/treetop/compiler/metagrammar.treetop +11 -11
- data/lib/treetop/compiler/node_classes/anything_symbol.rb +1 -1
- data/lib/treetop/compiler/node_classes/character_class.rb +1 -1
- data/lib/treetop/compiler/node_classes/choice.rb +2 -0
- data/lib/treetop/compiler/node_classes/nonterminal.rb +2 -0
- data/lib/treetop/compiler/node_classes/parenthesized_expression.rb +1 -1
- data/lib/treetop/compiler/node_classes/parsing_expression.rb +19 -4
- data/lib/treetop/compiler/node_classes/repetition.rb +2 -2
- data/lib/treetop/compiler/node_classes/sequence.rb +7 -3
- data/lib/treetop/compiler/node_classes/terminal.rb +1 -1
- data/lib/treetop/compiler/ruby_builder.rb +4 -0
- data/test/compiler/choice_test.rb +26 -1
- data/test/compiler/nonterminal_symbol_test.rb +28 -5
- metadata +11 -2
data/Rakefile
CHANGED
@@ -15,13 +15,13 @@ end
|
|
15
15
|
|
16
16
|
gemspec = Gem::Specification.new do |s|
|
17
17
|
s.name = "treetop"
|
18
|
-
s.version = "1.0
|
18
|
+
s.version = "1.1.0"
|
19
19
|
s.author = "Nathan Sobo"
|
20
20
|
s.email = "nathansobo@gmail.com"
|
21
21
|
s.homepage = "http://functionalform.blogspot.com"
|
22
22
|
s.platform = Gem::Platform::RUBY
|
23
23
|
s.summary = "A Ruby-based text parsing and interpretation DSL"
|
24
|
-
s.files = FileList["README", "Rakefile", "{test,lib,bin}/**/*"].to_a
|
24
|
+
s.files = FileList["README", "Rakefile", "{test,lib,bin,examples}/**/*"].to_a
|
25
25
|
s.bindir = "bin"
|
26
26
|
s.executables = ["tt"]
|
27
27
|
s.require_path = "lib"
|
data/examples/TALK
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
Before talk?
|
2
|
+
------------
|
3
|
+
- non node-instantiating expressions extend their results with inline modules
|
4
|
+
- allow load_grammar and require statements at the top of treetop files
|
5
|
+
- deal with facets versioning issue
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
Function application... the left-recursion problem
|
10
|
+
--------------------------------------------------
|
11
|
+
f g x
|
12
|
+
|
13
|
+
rule expression
|
14
|
+
application / function / variable
|
15
|
+
end
|
16
|
+
|
17
|
+
rule application
|
18
|
+
expression space expression
|
19
|
+
end
|
20
|
+
|
21
|
+
Function application... how can we avoid left recursion?
|
22
|
+
--------------------------------------------------------
|
23
|
+
rule expression
|
24
|
+
application / function / variable
|
25
|
+
end
|
26
|
+
|
27
|
+
rule application
|
28
|
+
operator space expression
|
29
|
+
end
|
30
|
+
|
31
|
+
rule operator
|
32
|
+
function / variable
|
33
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
grammar Arithmetic
|
2
|
+
rule expression
|
3
|
+
comparative / additive
|
4
|
+
end
|
5
|
+
|
6
|
+
rule comparative
|
7
|
+
exp_1:additive space '==' space exp_2:additive {
|
8
|
+
def eval(env={})
|
9
|
+
exp_1.eval(env) == exp_2.eval(env)
|
10
|
+
end
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
rule additive
|
15
|
+
operand_1:multitive
|
16
|
+
space operator:additive_op space
|
17
|
+
operand_2:additive <BinaryOperation>
|
18
|
+
/
|
19
|
+
multitive
|
20
|
+
end
|
21
|
+
|
22
|
+
rule additive_op
|
23
|
+
'+' {
|
24
|
+
def apply(a, b)
|
25
|
+
a + b
|
26
|
+
end
|
27
|
+
}
|
28
|
+
/
|
29
|
+
'-' {
|
30
|
+
def apply(a, b)
|
31
|
+
a - b
|
32
|
+
end
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
rule multitive
|
37
|
+
operand_1:primary
|
38
|
+
space operator:multitive_op space
|
39
|
+
operand_2:multitive <BinaryOperation>
|
40
|
+
/
|
41
|
+
primary
|
42
|
+
end
|
43
|
+
|
44
|
+
rule multitive_op
|
45
|
+
'*' {
|
46
|
+
def apply(a, b)
|
47
|
+
a * b
|
48
|
+
end
|
49
|
+
}
|
50
|
+
/
|
51
|
+
'/' {
|
52
|
+
def apply(a, b)
|
53
|
+
a / b
|
54
|
+
end
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
rule primary
|
59
|
+
variable
|
60
|
+
/
|
61
|
+
number
|
62
|
+
/
|
63
|
+
'(' space expression space ')' {
|
64
|
+
def eval(env={})
|
65
|
+
expression.eval(env)
|
66
|
+
end
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
rule variable
|
71
|
+
[a-z] {
|
72
|
+
def eval(env={})
|
73
|
+
env[name]
|
74
|
+
end
|
75
|
+
|
76
|
+
def name
|
77
|
+
text_value
|
78
|
+
end
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
rule number
|
83
|
+
[1-9] [0-9]* {
|
84
|
+
def eval(env={})
|
85
|
+
text_value.to_i
|
86
|
+
end
|
87
|
+
}
|
88
|
+
/
|
89
|
+
'0' {
|
90
|
+
def eval(env={})
|
91
|
+
text_value.to_i
|
92
|
+
end
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
rule space
|
97
|
+
' '*
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
require File.expand_path("#{dir}/test_helper")
|
3
|
+
|
4
|
+
require File.expand_path("#{dir}/arithmetic_node_classes")
|
5
|
+
load_grammar File.expand_path("#{dir}/arithmetic")
|
6
|
+
|
7
|
+
class ArithmeticParserTest < Test::Unit::TestCase
|
8
|
+
include ParserTestHelper
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@parser = ArithmeticParser.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_number
|
15
|
+
assert_equal 0, parse('0').eval
|
16
|
+
assert_equal 1, parse('1').eval
|
17
|
+
assert_equal 123, parse('123').eval
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_variable
|
21
|
+
assert_equal 0, parse('x').eval('x' => 0)
|
22
|
+
assert_equal 3, parse('x').eval('x' => 3)
|
23
|
+
assert_equal 10, parse('y').eval('y' => 10)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_addition
|
27
|
+
assert_equal 10, parse('x + 5').eval('x' => 5)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_subtraction
|
31
|
+
assert_equal 0, parse('x - 5').eval('x' => 5)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_multiplication
|
35
|
+
assert_equal 6, parse('x * 2').eval('x' => 3)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_division
|
39
|
+
assert_equal 3, parse('x / 2').eval('x' => 6)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_order_of_operations
|
43
|
+
assert_equal 11, parse('1 + 2 * 3 + 4').eval
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_parentheses
|
47
|
+
assert_equal 25, parse('(5 + x) * (10 - y)').eval('x' => 0, 'y' => 5)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_equality
|
51
|
+
assert parse('4 == 4').eval
|
52
|
+
assert !parse('4 == 3').eval
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
grammar LambdaCalculus
|
2
|
+
include Arithmetic
|
3
|
+
|
4
|
+
rule program
|
5
|
+
expression more_expressions:(';' space expression)* {
|
6
|
+
def eval(env={})
|
7
|
+
env = env.clone
|
8
|
+
last_eval = nil
|
9
|
+
expressions.each do |exp|
|
10
|
+
last_eval = exp.eval(env)
|
11
|
+
end
|
12
|
+
last_eval
|
13
|
+
end
|
14
|
+
|
15
|
+
def expressions
|
16
|
+
[expression] + more_expressions.elements.map {|elt| elt.expression}
|
17
|
+
end
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
rule expression
|
22
|
+
definition / conditional / application / function / super
|
23
|
+
end
|
24
|
+
|
25
|
+
rule definition
|
26
|
+
'def' space variable space expression {
|
27
|
+
def eval(env)
|
28
|
+
env[variable.name] = expression.eval(env)
|
29
|
+
end
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
rule conditional
|
34
|
+
'if' space '(' space condition:expression space ')' space
|
35
|
+
true_case:expression space 'else' space false_case:expression {
|
36
|
+
def eval(env)
|
37
|
+
if condition.eval(env)
|
38
|
+
true_case.eval(env)
|
39
|
+
else
|
40
|
+
false_case.eval(env)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
rule primary
|
47
|
+
application / super
|
48
|
+
end
|
49
|
+
|
50
|
+
rule application
|
51
|
+
operator space expression <Application> {
|
52
|
+
def eval(env={})
|
53
|
+
left_associative_apply(operator.eval(env), env)
|
54
|
+
end
|
55
|
+
|
56
|
+
def left_associative_apply(operator, env)
|
57
|
+
if expression.instance_of?(Application)
|
58
|
+
expression.left_associative_apply(operator.apply(expression.operator.eval(env)), env)
|
59
|
+
else
|
60
|
+
operator.apply(expression.eval(env))
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_s(env={})
|
65
|
+
operator.to_s(env) + ' ' + expression.to_s(env)
|
66
|
+
end
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
rule operator
|
71
|
+
function / variable
|
72
|
+
end
|
73
|
+
|
74
|
+
rule non_application
|
75
|
+
function / variable
|
76
|
+
end
|
77
|
+
|
78
|
+
rule function
|
79
|
+
'\\' param:variable '(' body:expression ')' {
|
80
|
+
class Closure
|
81
|
+
attr_reader :env, :function
|
82
|
+
|
83
|
+
def initialize(function, env)
|
84
|
+
@function = function
|
85
|
+
@env = env
|
86
|
+
end
|
87
|
+
|
88
|
+
def apply(arg)
|
89
|
+
function.body.eval(function.param.bind(arg, env))
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_s(other_env={})
|
93
|
+
"\\#{function.param.to_s}(#{function.body.to_s(other_env.merge(env))})"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def eval(env={})
|
98
|
+
Closure.new(self, env)
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_s(env={})
|
102
|
+
eval(env).to_s
|
103
|
+
end
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
rule variable
|
108
|
+
!keyword [a-z]+ {
|
109
|
+
def eval(env={})
|
110
|
+
env.has_key?(name) ? env[name] : self
|
111
|
+
end
|
112
|
+
|
113
|
+
def bind(value, env)
|
114
|
+
env.merge(name => value)
|
115
|
+
end
|
116
|
+
|
117
|
+
def name
|
118
|
+
text_value
|
119
|
+
end
|
120
|
+
|
121
|
+
def to_s(env={})
|
122
|
+
env.has_key?(name) ? env[name].to_s : name
|
123
|
+
end
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
rule keyword
|
128
|
+
('if' / 'else') !non_space_char
|
129
|
+
end
|
130
|
+
|
131
|
+
rule non_space_char
|
132
|
+
![ \n] .
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
rule space
|
137
|
+
[ \n]*
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
require File.expand_path("#{dir}/test_helper")
|
3
|
+
require File.expand_path("#{dir}/arithmetic_node_classes")
|
4
|
+
require File.expand_path("#{dir}/lambda_calculus_node_classes")
|
5
|
+
load_grammar File.expand_path("#{dir}/arithmetic")
|
6
|
+
load_grammar File.expand_path("#{dir}/lambda_calculus")
|
7
|
+
|
8
|
+
class Treetop::Runtime::SyntaxNode
|
9
|
+
def method_missing(method, *args)
|
10
|
+
raise "Node representing #{text_value} does not respond to #{method}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class LambdaCalculusParserTest < Test::Unit::TestCase
|
15
|
+
include ParserTestHelper
|
16
|
+
|
17
|
+
def setup
|
18
|
+
@parser = LambdaCalculusParser.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_free_variable
|
22
|
+
assert_equal 'x', parse('x').eval.to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_variable_binding
|
26
|
+
variable = parse('x').eval
|
27
|
+
env = variable.bind(1, {})
|
28
|
+
assert_equal 1, env['x']
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_bound_variable_evaluation
|
32
|
+
assert_equal 1, parse('x').eval({'x' => 1})
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_identity_function
|
36
|
+
assert_equal '\x(x)', parse('\x(x)').eval.to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_function_returning_constant_function
|
40
|
+
assert_equal '\x(\y(x))', parse('\x(\y(x))').eval.to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_identity_function_application
|
44
|
+
assert_equal 1, parse('\x(x) 1').eval
|
45
|
+
assert_equal '\y(y)', parse('\x(x) \y(y)').eval.to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_constant_function_construction
|
49
|
+
assert_equal '\y(1)', parse('\x(\y(x)) 1').eval.to_s
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_multiple_argument_application_is_left_associative
|
53
|
+
assert_equal '\b(b)', parse('\x(\y(x y)) \a(a) \b(b)').eval.to_s
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_parentheses_override_application_order
|
57
|
+
assert_equal '\y(\b(b) y)', parse('\x(\y(x y)) (\a(a) \b(b))').eval.to_s
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_arithmetic_in_function_body
|
61
|
+
assert_equal 10, parse('\x(x + 5) 5').eval
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_addition_of_function_results
|
65
|
+
assert_equal 20, parse('\x(x + 5) 5 + \x(15 - x) 5').eval
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_conditional
|
69
|
+
result = parse('if (x) 1 else 2')
|
70
|
+
assert_equal 1, result.eval({'x' => true})
|
71
|
+
assert_equal 2, result.eval({'x' => false})
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_keyword
|
75
|
+
assert @parser.parse('if').failure?
|
76
|
+
assert @parser.parse('else').failure?
|
77
|
+
assert parse('elsee').success?
|
78
|
+
assert parse('iff').success?
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_program
|
82
|
+
result = parse('def fact \x(if (x == 0)
|
83
|
+
1
|
84
|
+
else
|
85
|
+
x * fact (x - 1));
|
86
|
+
fact(5)').eval
|
87
|
+
assert_equal 5 * 4 * 3 * 2, result
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'treetop'
|
4
|
+
|
5
|
+
module ParserTestHelper
|
6
|
+
def assert_evals_to_self(input)
|
7
|
+
assert_evals_to(input, input)
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse(input)
|
11
|
+
result = @parser.parse(input)
|
12
|
+
if result.failure?
|
13
|
+
puts result.nested_failures.join("\n")
|
14
|
+
end
|
15
|
+
assert result.success?
|
16
|
+
result
|
17
|
+
end
|
18
|
+
end
|
@@ -4,7 +4,12 @@ module Treetop
|
|
4
4
|
include ::Treetop::Runtime
|
5
5
|
|
6
6
|
def root
|
7
|
-
_nt_treetop_file
|
7
|
+
result = _nt_treetop_file
|
8
|
+
if index == input.size
|
9
|
+
return result
|
10
|
+
else
|
11
|
+
return ParseFailure.new(input, index, result.nested_failures)
|
12
|
+
end
|
8
13
|
end
|
9
14
|
|
10
15
|
module TreetopFile0
|
@@ -928,8 +933,8 @@ module Treetop
|
|
928
933
|
atomic
|
929
934
|
end
|
930
935
|
|
931
|
-
def
|
932
|
-
node_class_declarations.
|
936
|
+
def node_class_name
|
937
|
+
node_class_declarations.node_class_name
|
933
938
|
end
|
934
939
|
|
935
940
|
def inline_modules
|
@@ -956,8 +961,8 @@ module Treetop
|
|
956
961
|
atomic.compile(address, builder, self)
|
957
962
|
end
|
958
963
|
|
959
|
-
def
|
960
|
-
node_class_declarations.
|
964
|
+
def node_class_name
|
965
|
+
node_class_declarations.node_class_name
|
961
966
|
end
|
962
967
|
|
963
968
|
def inline_modules
|
@@ -1245,8 +1250,8 @@ module Treetop
|
|
1245
1250
|
suffix.compile(lexical_address, builder, self)
|
1246
1251
|
end
|
1247
1252
|
|
1248
|
-
def
|
1249
|
-
|
1253
|
+
def node_class_name
|
1254
|
+
nil
|
1250
1255
|
end
|
1251
1256
|
|
1252
1257
|
def inline_modules
|
@@ -1381,8 +1386,8 @@ module Treetop
|
|
1381
1386
|
end
|
1382
1387
|
|
1383
1388
|
module NodeClassDeclarations1
|
1384
|
-
def
|
1385
|
-
node_class_expression.
|
1389
|
+
def node_class_name
|
1390
|
+
node_class_expression.node_class_name
|
1386
1391
|
end
|
1387
1392
|
|
1388
1393
|
def inline_modules
|
@@ -2001,14 +2006,14 @@ module Treetop
|
|
2001
2006
|
end
|
2002
2007
|
|
2003
2008
|
module NodeClassExpression2
|
2004
|
-
def
|
2009
|
+
def node_class_name
|
2005
2010
|
elements[2].text_value
|
2006
2011
|
end
|
2007
2012
|
end
|
2008
2013
|
|
2009
2014
|
module NodeClassExpression3
|
2010
|
-
def
|
2011
|
-
|
2015
|
+
def node_class_name
|
2016
|
+
nil
|
2012
2017
|
end
|
2013
2018
|
end
|
2014
2019
|
|
@@ -132,8 +132,8 @@ module Treetop
|
|
132
132
|
atomic
|
133
133
|
end
|
134
134
|
|
135
|
-
def
|
136
|
-
node_class_declarations.
|
135
|
+
def node_class_name
|
136
|
+
node_class_declarations.node_class_name
|
137
137
|
end
|
138
138
|
|
139
139
|
def inline_modules
|
@@ -150,8 +150,8 @@ module Treetop
|
|
150
150
|
atomic.compile(address, builder, self)
|
151
151
|
end
|
152
152
|
|
153
|
-
def
|
154
|
-
node_class_declarations.
|
153
|
+
def node_class_name
|
154
|
+
node_class_declarations.node_class_name
|
155
155
|
end
|
156
156
|
|
157
157
|
def inline_modules
|
@@ -224,8 +224,8 @@ module Treetop
|
|
224
224
|
suffix.compile(lexical_address, builder, self)
|
225
225
|
end
|
226
226
|
|
227
|
-
def
|
228
|
-
|
227
|
+
def node_class_name
|
228
|
+
nil
|
229
229
|
end
|
230
230
|
|
231
231
|
def inline_modules
|
@@ -250,8 +250,8 @@ module Treetop
|
|
250
250
|
|
251
251
|
rule node_class_declarations
|
252
252
|
node_class_expression trailing_inline_module {
|
253
|
-
def
|
254
|
-
node_class_expression.
|
253
|
+
def node_class_name
|
254
|
+
node_class_expression.node_class_name
|
255
255
|
end
|
256
256
|
|
257
257
|
def inline_modules
|
@@ -318,14 +318,14 @@ module Treetop
|
|
318
318
|
|
319
319
|
rule node_class_expression
|
320
320
|
space '<' (!'>' .)+ '>' {
|
321
|
-
def
|
321
|
+
def node_class_name
|
322
322
|
elements[2].text_value
|
323
323
|
end
|
324
324
|
}
|
325
325
|
/
|
326
326
|
'' {
|
327
|
-
def
|
328
|
-
|
327
|
+
def node_class_name
|
328
|
+
nil
|
329
329
|
end
|
330
330
|
}
|
331
331
|
end
|
@@ -3,7 +3,7 @@ module Treetop
|
|
3
3
|
class AnythingSymbol < AtomicExpression
|
4
4
|
def compile(address, builder, parent_expression = nil)
|
5
5
|
super
|
6
|
-
assign_result "parse_anything(#{
|
6
|
+
assign_result "parse_anything(#{node_class_name}#{optional_arg(inline_module_name)})"
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -3,7 +3,7 @@ module Treetop
|
|
3
3
|
class CharacterClass < AtomicExpression
|
4
4
|
def compile(address, builder, parent_expression = nil)
|
5
5
|
super
|
6
|
-
assign_result "parse_char_class(/#{text_value}/, '#{elements[1].text_value.gsub(/'$/, "\\\\'")}', #{
|
6
|
+
assign_result "parse_char_class(/#{text_value}/, '#{elements[1].text_value.gsub(/'$/, "\\\\'")}', #{node_class_name}#{optional_arg(inline_module_name)})"
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -15,6 +15,8 @@ module Treetop
|
|
15
15
|
accumulate_nested_result
|
16
16
|
builder.if__ subexpression_success? do
|
17
17
|
assign_result subexpression_result_var
|
18
|
+
extend_result_with_declared_module
|
19
|
+
extend_result_with_inline_module
|
18
20
|
builder << "#{subexpression_result_var}.update_nested_results(#{nested_results_var})"
|
19
21
|
end
|
20
22
|
builder.else_ do
|
@@ -9,13 +9,16 @@ module Treetop
|
|
9
9
|
@parent_expression = parent_expression
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
13
|
-
parent_expression && parent_expression.
|
12
|
+
def node_class_name
|
13
|
+
parent_expression && parent_expression.node_class_name || 'SyntaxNode'
|
14
|
+
end
|
15
|
+
|
16
|
+
def declared_module_name
|
17
|
+
parent_expression && parent_expression.node_class_name
|
14
18
|
end
|
15
19
|
|
16
20
|
def inline_module_name
|
17
|
-
|
18
|
-
parent_expression.inline_module_name
|
21
|
+
parent_expression && parent_expression.inline_module_name
|
19
22
|
end
|
20
23
|
|
21
24
|
def optional_arg(arg)
|
@@ -70,6 +73,18 @@ module Treetop
|
|
70
73
|
def assign_result(value_ruby)
|
71
74
|
builder.assign result_var, value_ruby
|
72
75
|
end
|
76
|
+
|
77
|
+
def extend_result(module_name)
|
78
|
+
builder.extend result_var, module_name
|
79
|
+
end
|
80
|
+
|
81
|
+
def extend_result_with_declared_module
|
82
|
+
extend_result declared_module_name if declared_module_name
|
83
|
+
end
|
84
|
+
|
85
|
+
def extend_result_with_inline_module
|
86
|
+
extend_result inline_module_name if inline_module_name
|
87
|
+
end
|
73
88
|
|
74
89
|
def reset_index
|
75
90
|
builder.assign 'self.index', start_index_var
|
@@ -25,8 +25,8 @@ module Treetop
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def assign_and_extend_result
|
28
|
-
assign_result "#{
|
29
|
-
|
28
|
+
assign_result "#{node_class_name}.new(input, #{start_index_var}...index, #{accumulator_var}, #{nested_results_var})"
|
29
|
+
extend_result_with_inline_module
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
@@ -7,9 +7,9 @@ module Treetop
|
|
7
7
|
use_vars :result, :start_index, :accumulator, :nested_results
|
8
8
|
compile_sequence_elements(sequence_elements)
|
9
9
|
builder.if__ "#{accumulator_var}.last.success?" do
|
10
|
-
assign_result "(#{
|
11
|
-
|
12
|
-
|
10
|
+
assign_result "(#{node_class_name}).new(input, #{start_index_var}...index, #{accumulator_var})"
|
11
|
+
extend_result sequence_element_accessor_module_name if sequence_element_accessor_module_name
|
12
|
+
extend_result_with_inline_module
|
13
13
|
end
|
14
14
|
builder.else_ do
|
15
15
|
reset_index
|
@@ -18,6 +18,10 @@ module Treetop
|
|
18
18
|
end_comment(self)
|
19
19
|
end
|
20
20
|
|
21
|
+
def node_class_name
|
22
|
+
node_class_declarations.node_class_name || 'SyntaxNode'
|
23
|
+
end
|
24
|
+
|
21
25
|
def compile_sequence_elements(elements)
|
22
26
|
obtain_new_subexpression_address
|
23
27
|
elements.first.compile(subexpression_address, builder)
|
@@ -3,7 +3,7 @@ module Treetop
|
|
3
3
|
class Terminal < AtomicExpression
|
4
4
|
def compile(address, builder, parent_expression = nil)
|
5
5
|
super
|
6
|
-
assign_result "parse_terminal(#{text_value}, #{
|
6
|
+
assign_result "parse_terminal(#{text_value}, #{node_class_name}#{optional_arg(inline_module_name)})"
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -46,4 +46,29 @@ describe "A choice between sequences", :extend => CompilerTestCase do
|
|
46
46
|
parse('foobarbaz').should be_success
|
47
47
|
parse('bingbangboom').should be_success
|
48
48
|
end
|
49
|
-
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "A choice between terminals followed by a block", :extend => CompilerTestCase do
|
52
|
+
testing_expression "('a'/ 'b' / 'c') { def a_method; end }"
|
53
|
+
|
54
|
+
it "extends a match of any of its subexpressions with a module created from the block" do
|
55
|
+
['a', 'b', 'c'].each do |letter|
|
56
|
+
parse(letter).should respond_to(:a_method)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class ChoiceWithDeclaredModuleTest < CompilerTestCase
|
62
|
+
module TestModule
|
63
|
+
def a_method
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
testing_expression "('a'/ 'b' / 'c') <TestModule>"
|
68
|
+
|
69
|
+
it "extends a match of any of its subexpressions with a module created from the block" do
|
70
|
+
['a', 'b', 'c'].each do |letter|
|
71
|
+
parse(letter).should respond_to(:a_method)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), '..', 'test_helper')
|
2
2
|
|
3
|
-
describe "A nonterminal symbol", :extend => CompilerTestCase do
|
4
|
-
testing_expression 'foo'
|
3
|
+
describe "A nonterminal symbol followed by a block", :extend => CompilerTestCase do
|
4
|
+
testing_expression 'foo { def a_method; end }'
|
5
5
|
|
6
6
|
parser_class_under_test.class_eval do
|
7
7
|
def _nt_foo
|
@@ -9,7 +9,30 @@ describe "A nonterminal symbol", :extend => CompilerTestCase do
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
it "compiles to a
|
13
|
-
parse('')
|
12
|
+
it "compiles to a method call, extending its results with the anonymous module for the block" do
|
13
|
+
result = parse('')
|
14
|
+
result.should == '_nt_foo called'
|
15
|
+
result.should respond_to(:a_method)
|
14
16
|
end
|
15
|
-
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class NonterminalWithModuleDeclationTest < CompilerTestCase
|
20
|
+
module TestModule
|
21
|
+
def a_method
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
testing_expression 'foo <TestModule>'
|
26
|
+
|
27
|
+
parser_class_under_test.class_eval do
|
28
|
+
def _nt_foo
|
29
|
+
'_nt_foo called'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "compiles to a method call, extending its results with the anonymous module for the block" do
|
34
|
+
result = parse('')
|
35
|
+
result.should == '_nt_foo called'
|
36
|
+
result.should respond_to(:a_method)
|
37
|
+
end
|
38
|
+
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
|
|
3
3
|
specification_version: 1
|
4
4
|
name: treetop
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 1.0
|
7
|
-
date: 2007-10-
|
6
|
+
version: 1.1.0
|
7
|
+
date: 2007-10-25 00:00:00 -07:00
|
8
8
|
summary: A Ruby-based text parsing and interpretation DSL
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -144,6 +144,15 @@ files:
|
|
144
144
|
- lib/treetop/runtime.rb
|
145
145
|
- lib/treetop.rb
|
146
146
|
- bin/tt
|
147
|
+
- examples/lambda_calculus
|
148
|
+
- examples/lambda_calculus/arithmetic.treetop
|
149
|
+
- examples/lambda_calculus/arithmetic_node_classes.rb
|
150
|
+
- examples/lambda_calculus/arithmetic_test.rb
|
151
|
+
- examples/lambda_calculus/lambda_calculus.treetop
|
152
|
+
- examples/lambda_calculus/lambda_calculus_node_classes.rb
|
153
|
+
- examples/lambda_calculus/lambda_calculus_test.rb
|
154
|
+
- examples/lambda_calculus/test_helper.rb
|
155
|
+
- examples/TALK
|
147
156
|
test_files: []
|
148
157
|
|
149
158
|
rdoc_options: []
|