treetop 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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.2"
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"
@@ -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,7 @@
1
+ module Arithmetic
2
+ class BinaryOperation < Treetop::Runtime::SyntaxNode
3
+ def eval(env={})
4
+ operator.apply(operand_1.eval(env), operand_2.eval(env))
5
+ end
6
+ end
7
+ 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,5 @@
1
+ module LambdaCalculus
2
+ class Application < Treetop::Runtime::SyntaxNode
3
+
4
+ end
5
+ 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 node_class
932
- node_class_declarations.node_class
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 node_class
960
- node_class_declarations.node_class
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 node_class
1249
- 'SyntaxNode'
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 node_class
1385
- node_class_expression.node_class
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 node_class
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 node_class
2011
- 'SyntaxNode'
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 node_class
136
- node_class_declarations.node_class
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 node_class
154
- node_class_declarations.node_class
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 node_class
228
- 'SyntaxNode'
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 node_class
254
- node_class_expression.node_class
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 node_class
321
+ def node_class_name
322
322
  elements[2].text_value
323
323
  end
324
324
  }
325
325
  /
326
326
  '' {
327
- def node_class
328
- 'SyntaxNode'
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(#{node_class}#{optional_arg(inline_module_name)})"
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(/'$/, "\\\\'")}', #{node_class}#{optional_arg(inline_module_name)})"
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
@@ -5,6 +5,8 @@ module Treetop
5
5
  super
6
6
  use_vars :result
7
7
  assign_result text_value == 'super' ? 'super' : "_nt_#{text_value}"
8
+ extend_result_with_declared_module
9
+ extend_result_with_inline_module
8
10
  end
9
11
  end
10
12
  end
@@ -2,7 +2,7 @@ module Treetop
2
2
  module Compiler
3
3
  class ParenthesizedExpression < ParsingExpression
4
4
  def compile(address, builder, parent_expression = nil)
5
- elements[2].compile(address, builder)
5
+ elements[2].compile(address, builder, parent_expression)
6
6
  end
7
7
  end
8
8
  end
@@ -9,13 +9,16 @@ module Treetop
9
9
  @parent_expression = parent_expression
10
10
  end
11
11
 
12
- def node_class
13
- parent_expression && parent_expression.node_class || 'SyntaxNode'
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
- return nil unless parent_expression
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 "#{node_class}.new(input, #{start_index_var}...index, #{accumulator_var}, #{nested_results_var})"
29
- builder << "#{result_var}.extend(#{inline_module_name})" if inline_module_name
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 "(#{node_class_declarations.node_class}).new(input, #{start_index_var}...index, #{accumulator_var})"
11
- builder << "#{result_var}.extend(#{sequence_element_accessor_module_name})" if sequence_element_accessor_module_name
12
- builder << "#{result_var}.extend(#{inline_module_name})" if inline_module_name
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}, #{node_class}#{optional_arg(inline_module_name)})"
6
+ assign_result "parse_terminal(#{text_value}, #{node_class_name}#{optional_arg(inline_module_name)})"
7
7
  end
8
8
  end
9
9
  end
@@ -51,6 +51,10 @@ module Treetop
51
51
  end
52
52
  end
53
53
 
54
+ def extend(var, module_name)
55
+ self << "#{var}.extend(#{module_name})"
56
+ end
57
+
54
58
  def accumulate(left, right)
55
59
  self << "#{left} << #{right}"
56
60
  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 message send" do
13
- parse('').should == '_nt_foo called'
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.2
7
- date: 2007-10-22 00:00:00 -07:00
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: []