personify 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/.gitignore +1 -0
- data/LICENSE +20 -0
- data/README.md +172 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/doc/syntax_ideas.md +141 -0
- data/lib/personify/context.rb +55 -0
- data/lib/personify/parser/personify.rb +1071 -0
- data/lib/personify/parser/personify.treetop +107 -0
- data/lib/personify/parser/personify_node_classes.rb +121 -0
- data/lib/personify/template.rb +17 -0
- data/lib/personify.rb +8 -0
- data/script/generate_parser.rb +6 -0
- data/test/context_test.rb +122 -0
- data/test/fixtures/multiple_tags.txt +8 -0
- data/test/parse_runner.rb +60 -0
- data/test/parser_test.rb +291 -0
- data/test/test_helper.rb +16 -0
- data/vendor/treetop/.gitignore +5 -0
- data/vendor/treetop/History.txt +9 -0
- data/vendor/treetop/README +164 -0
- data/vendor/treetop/Rakefile +20 -0
- data/vendor/treetop/Treetop.tmbundle/Snippets/grammar ___ end.tmSnippet +20 -0
- data/vendor/treetop/Treetop.tmbundle/Snippets/rule ___ end.tmSnippet +18 -0
- data/vendor/treetop/Treetop.tmbundle/Syntaxes/Treetop Grammar.tmLanguage +251 -0
- data/vendor/treetop/Treetop.tmbundle/info.plist +10 -0
- data/vendor/treetop/bin/tt +28 -0
- data/vendor/treetop/doc/contributing_and_planned_features.markdown +103 -0
- data/vendor/treetop/doc/grammar_composition.markdown +65 -0
- data/vendor/treetop/doc/index.markdown +90 -0
- data/vendor/treetop/doc/pitfalls_and_advanced_techniques.markdown +51 -0
- data/vendor/treetop/doc/semantic_interpretation.markdown +189 -0
- data/vendor/treetop/doc/site.rb +110 -0
- data/vendor/treetop/doc/sitegen.rb +60 -0
- data/vendor/treetop/doc/syntactic_recognition.markdown +100 -0
- data/vendor/treetop/doc/using_in_ruby.markdown +21 -0
- data/vendor/treetop/examples/lambda_calculus/arithmetic.rb +551 -0
- data/vendor/treetop/examples/lambda_calculus/arithmetic.treetop +97 -0
- data/vendor/treetop/examples/lambda_calculus/arithmetic_node_classes.rb +7 -0
- data/vendor/treetop/examples/lambda_calculus/arithmetic_test.rb +54 -0
- data/vendor/treetop/examples/lambda_calculus/lambda_calculus +0 -0
- data/vendor/treetop/examples/lambda_calculus/lambda_calculus.rb +718 -0
- data/vendor/treetop/examples/lambda_calculus/lambda_calculus.treetop +132 -0
- data/vendor/treetop/examples/lambda_calculus/lambda_calculus_node_classes.rb +5 -0
- data/vendor/treetop/examples/lambda_calculus/lambda_calculus_test.rb +89 -0
- data/vendor/treetop/examples/lambda_calculus/test_helper.rb +18 -0
- data/vendor/treetop/lib/treetop/bootstrap_gen_1_metagrammar.rb +45 -0
- data/vendor/treetop/lib/treetop/compiler/grammar_compiler.rb +40 -0
- data/vendor/treetop/lib/treetop/compiler/lexical_address_space.rb +17 -0
- data/vendor/treetop/lib/treetop/compiler/metagrammar.rb +2955 -0
- data/vendor/treetop/lib/treetop/compiler/metagrammar.treetop +404 -0
- data/vendor/treetop/lib/treetop/compiler/node_classes/anything_symbol.rb +20 -0
- data/vendor/treetop/lib/treetop/compiler/node_classes/atomic_expression.rb +14 -0
- data/vendor/treetop/lib/treetop/compiler/node_classes/character_class.rb +22 -0
- data/vendor/treetop/lib/treetop/compiler/node_classes/choice.rb +31 -0
- data/vendor/treetop/lib/treetop/compiler/node_classes/declaration_sequence.rb +24 -0
- data/vendor/treetop/lib/treetop/compiler/node_classes/grammar.rb +28 -0
- data/vendor/treetop/lib/treetop/compiler/node_classes/inline_module.rb +27 -0
- data/vendor/treetop/lib/treetop/compiler/node_classes/nonterminal.rb +13 -0
- data/vendor/treetop/lib/treetop/compiler/node_classes/optional.rb +19 -0
- data/vendor/treetop/lib/treetop/compiler/node_classes/parenthesized_expression.rb +9 -0
- data/vendor/treetop/lib/treetop/compiler/node_classes/parsing_expression.rb +138 -0
- data/vendor/treetop/lib/treetop/compiler/node_classes/parsing_rule.rb +55 -0
- data/vendor/treetop/lib/treetop/compiler/node_classes/predicate.rb +45 -0
- data/vendor/treetop/lib/treetop/compiler/node_classes/repetition.rb +55 -0
- data/vendor/treetop/lib/treetop/compiler/node_classes/sequence.rb +68 -0
- data/vendor/treetop/lib/treetop/compiler/node_classes/terminal.rb +20 -0
- data/vendor/treetop/lib/treetop/compiler/node_classes/transient_prefix.rb +9 -0
- data/vendor/treetop/lib/treetop/compiler/node_classes/treetop_file.rb +9 -0
- data/vendor/treetop/lib/treetop/compiler/node_classes.rb +19 -0
- data/vendor/treetop/lib/treetop/compiler/ruby_builder.rb +113 -0
- data/vendor/treetop/lib/treetop/compiler.rb +6 -0
- data/vendor/treetop/lib/treetop/ruby_extensions/string.rb +42 -0
- data/vendor/treetop/lib/treetop/ruby_extensions.rb +2 -0
- data/vendor/treetop/lib/treetop/runtime/compiled_parser.rb +95 -0
- data/vendor/treetop/lib/treetop/runtime/interval_skip_list/head_node.rb +15 -0
- data/vendor/treetop/lib/treetop/runtime/interval_skip_list/interval_skip_list.rb +200 -0
- data/vendor/treetop/lib/treetop/runtime/interval_skip_list/node.rb +164 -0
- data/vendor/treetop/lib/treetop/runtime/interval_skip_list.rb +4 -0
- data/vendor/treetop/lib/treetop/runtime/syntax_node.rb +72 -0
- data/vendor/treetop/lib/treetop/runtime/terminal_parse_failure.rb +16 -0
- data/vendor/treetop/lib/treetop/runtime/terminal_syntax_node.rb +17 -0
- data/vendor/treetop/lib/treetop/runtime.rb +5 -0
- data/vendor/treetop/lib/treetop/version.rb +9 -0
- data/vendor/treetop/lib/treetop.rb +11 -0
- data/vendor/treetop/script/generate_metagrammar.rb +14 -0
- data/vendor/treetop/script/svnadd +11 -0
- data/vendor/treetop/script/svnrm +11 -0
- data/vendor/treetop/spec/compiler/and_predicate_spec.rb +36 -0
- data/vendor/treetop/spec/compiler/anything_symbol_spec.rb +52 -0
- data/vendor/treetop/spec/compiler/character_class_spec.rb +188 -0
- data/vendor/treetop/spec/compiler/choice_spec.rb +80 -0
- data/vendor/treetop/spec/compiler/circular_compilation_spec.rb +28 -0
- data/vendor/treetop/spec/compiler/failure_propagation_functional_spec.rb +21 -0
- data/vendor/treetop/spec/compiler/grammar_compiler_spec.rb +84 -0
- data/vendor/treetop/spec/compiler/grammar_spec.rb +41 -0
- data/vendor/treetop/spec/compiler/nonterminal_symbol_spec.rb +40 -0
- data/vendor/treetop/spec/compiler/not_predicate_spec.rb +38 -0
- data/vendor/treetop/spec/compiler/one_or_more_spec.rb +35 -0
- data/vendor/treetop/spec/compiler/optional_spec.rb +37 -0
- data/vendor/treetop/spec/compiler/parenthesized_expression_spec.rb +19 -0
- data/vendor/treetop/spec/compiler/parsing_rule_spec.rb +32 -0
- data/vendor/treetop/spec/compiler/sequence_spec.rb +115 -0
- data/vendor/treetop/spec/compiler/terminal_spec.rb +81 -0
- data/vendor/treetop/spec/compiler/terminal_symbol_spec.rb +37 -0
- data/vendor/treetop/spec/compiler/test_grammar.treetop +7 -0
- data/vendor/treetop/spec/compiler/test_grammar.tt +7 -0
- data/vendor/treetop/spec/compiler/test_grammar_do.treetop +7 -0
- data/vendor/treetop/spec/compiler/zero_or_more_spec.rb +56 -0
- data/vendor/treetop/spec/composition/a.treetop +11 -0
- data/vendor/treetop/spec/composition/b.treetop +11 -0
- data/vendor/treetop/spec/composition/c.treetop +10 -0
- data/vendor/treetop/spec/composition/d.treetop +10 -0
- data/vendor/treetop/spec/composition/grammar_composition_spec.rb +26 -0
- data/vendor/treetop/spec/ruby_extensions/string_spec.rb +32 -0
- data/vendor/treetop/spec/runtime/compiled_parser_spec.rb +101 -0
- data/vendor/treetop/spec/runtime/interval_skip_list/delete_spec.rb +147 -0
- data/vendor/treetop/spec/runtime/interval_skip_list/expire_range_spec.rb +349 -0
- data/vendor/treetop/spec/runtime/interval_skip_list/insert_and_delete_node.rb +385 -0
- data/vendor/treetop/spec/runtime/interval_skip_list/insert_spec.rb +660 -0
- data/vendor/treetop/spec/runtime/interval_skip_list/interval_skip_list_spec.graffle +6175 -0
- data/vendor/treetop/spec/runtime/interval_skip_list/interval_skip_list_spec.rb +58 -0
- data/vendor/treetop/spec/runtime/interval_skip_list/palindromic_fixture.rb +23 -0
- data/vendor/treetop/spec/runtime/interval_skip_list/palindromic_fixture_spec.rb +164 -0
- data/vendor/treetop/spec/runtime/interval_skip_list/spec_helper.rb +84 -0
- data/vendor/treetop/spec/runtime/syntax_node_spec.rb +53 -0
- data/vendor/treetop/spec/spec_helper.rb +106 -0
- data/vendor/treetop/spec/spec_suite.rb +4 -0
- data/vendor/treetop/treetop.gemspec +18 -0
- metadata +196 -0
|
@@ -0,0 +1,132 @@
|
|
|
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 (
|
|
109
|
+
super {
|
|
110
|
+
def bind(value, env)
|
|
111
|
+
env.merge(name => value)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def to_s(env={})
|
|
115
|
+
env.has_key?(name) ? env[name].to_s : name
|
|
116
|
+
end
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
rule keyword
|
|
122
|
+
('if' / 'else') !non_space_char
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
rule non_space_char
|
|
126
|
+
![ \n] .
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
rule space
|
|
130
|
+
[ \n]*
|
|
131
|
+
end
|
|
132
|
+
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
|
+
Treetop.load File.expand_path("#{dir}/arithmetic")
|
|
6
|
+
Treetop.load 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
|
+
unless result
|
|
13
|
+
puts @parser.terminal_failures.join("\n")
|
|
14
|
+
end
|
|
15
|
+
assert !result.nil?
|
|
16
|
+
result
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# This file's job is to load a Treetop::Compiler::Metagrammar and Treetop::Compiler::MetagrammarParser
|
|
2
|
+
# into the environment by compiling the current metagrammar.treetop using a trusted version of Treetop.
|
|
3
|
+
|
|
4
|
+
require 'rubygems'
|
|
5
|
+
dir = File.dirname(__FILE__)
|
|
6
|
+
|
|
7
|
+
TREETOP_VERSION_REQUIRED_TO_BOOTSTRAP = '>= 1.1.5'
|
|
8
|
+
|
|
9
|
+
# Loading trusted version of Treetop to compile the compiler
|
|
10
|
+
gem_spec = Gem.source_index.find_name('treetop', TREETOP_VERSION_REQUIRED_TO_BOOTSTRAP).last
|
|
11
|
+
raise "Install a Treetop Gem version #{TREETOP_VERSION_REQUIRED_TO_BOOTSTRAP} to bootstrap." unless gem_spec
|
|
12
|
+
trusted_treetop_path = gem_spec.full_gem_path
|
|
13
|
+
require File.join(trusted_treetop_path, 'lib', 'treetop')
|
|
14
|
+
|
|
15
|
+
# Relocating trusted version of Treetop to Trusted::Treetop
|
|
16
|
+
Trusted = Module.new
|
|
17
|
+
Trusted::Treetop = Treetop
|
|
18
|
+
Object.send(:remove_const, :Treetop)
|
|
19
|
+
Object.send(:remove_const, :TREETOP_ROOT)
|
|
20
|
+
|
|
21
|
+
# Requiring version of Treetop that is under test
|
|
22
|
+
$exclude_metagrammar = true
|
|
23
|
+
require File.expand_path(File.join(dir, '..', 'treetop'))
|
|
24
|
+
|
|
25
|
+
# Compile and evaluate freshly generated metagrammar source
|
|
26
|
+
METAGRAMMAR_PATH = File.join(TREETOP_ROOT, 'compiler', 'metagrammar.treetop')
|
|
27
|
+
compiled_metagrammar_source = Trusted::Treetop::Compiler::GrammarCompiler.new.ruby_source(METAGRAMMAR_PATH)
|
|
28
|
+
Object.class_eval(compiled_metagrammar_source)
|
|
29
|
+
|
|
30
|
+
# The compiler under test was compiled with the trusted grammar and therefore depends on its runtime
|
|
31
|
+
# But the runtime in the global namespace is the new runtime. We therefore inject the trusted runtime
|
|
32
|
+
# into the compiler so its parser functions correctly. It will still not work for custom classes that
|
|
33
|
+
# explicitly subclass the wrong runtime. For now I am working around this by keeping 1 generation of
|
|
34
|
+
# backward compatibility in these cases.
|
|
35
|
+
# Treetop::Compiler::Metagrammar.module_eval do
|
|
36
|
+
# include Trusted::Treetop::Runtime
|
|
37
|
+
# end
|
|
38
|
+
#
|
|
39
|
+
# Treetop::Compiler.send(:remove_const, :MetagrammarParser)
|
|
40
|
+
# class Treetop::Compiler::MetagrammarParser < Trusted::Treetop::Runtime::CompiledParser
|
|
41
|
+
# include Treetop::Compiler::Metagrammar
|
|
42
|
+
# include Trusted::Treetop::Runtime
|
|
43
|
+
# end
|
|
44
|
+
|
|
45
|
+
$bootstrapped_gen_1_metagrammar = true
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Treetop
|
|
2
|
+
module Compiler
|
|
3
|
+
class GrammarCompiler
|
|
4
|
+
def compile(source_path, target_path = source_path.gsub(/\.(treetop|tt)\Z/, '.rb'))
|
|
5
|
+
File.open(target_path, 'w') do |target_file|
|
|
6
|
+
target_file.write(ruby_source(source_path))
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# compile a treetop file into ruby
|
|
11
|
+
def ruby_source(source_path)
|
|
12
|
+
ruby_source_from_string(File.read(source_path))
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# compile a string containing treetop source into ruby
|
|
16
|
+
def ruby_source_from_string(s)
|
|
17
|
+
parser = MetagrammarParser.new
|
|
18
|
+
result = parser.parse(s)
|
|
19
|
+
unless result
|
|
20
|
+
raise RuntimeError.new(parser.failure_reason)
|
|
21
|
+
end
|
|
22
|
+
result.compile
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# compile a treetop source file and load it
|
|
28
|
+
def self.load(path)
|
|
29
|
+
adjusted_path = path =~ /\.(treetop|tt)\Z/ ? path : path + '.treetop'
|
|
30
|
+
File.open(adjusted_path) do |source_file|
|
|
31
|
+
load_from_string(source_file.read)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# compile a treetop source string and load it
|
|
36
|
+
def self.load_from_string(s)
|
|
37
|
+
compiler = Treetop::Compiler::GrammarCompiler.new
|
|
38
|
+
Object.class_eval(compiler.ruby_source_from_string(s))
|
|
39
|
+
end
|
|
40
|
+
end
|