emfrp 0.0.1
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 +7 -0
- data/.gitignore +14 -0
- data/Gemfile +6 -0
- data/LICENSE +28 -0
- data/README.md +27 -0
- data/Rakefile +2 -0
- data/bin/emfrp +21 -0
- data/bin/emfrpi +24 -0
- data/emfrp.gemspec +26 -0
- data/examples/LCDPositioner/LCDPositioner.c +278 -0
- data/examples/LCDPositioner/LCDPositioner.h +6 -0
- data/examples/LCDPositioner/LCDPositioner.mfrp +30 -0
- data/examples/LCDPositioner/LCDPositionerMain.c +15 -0
- data/examples/LCDPositioner/LCDPositionerMain.c.gen +11 -0
- data/examples/LCDPositioner/graph.dot +31 -0
- data/examples/LCDPositioner/graph.png +0 -0
- data/examples/MostDistantPoint/MostDistantPoint.c +199 -0
- data/examples/MostDistantPoint/MostDistantPoint.h +6 -0
- data/examples/MostDistantPoint/MostDistantPoint.mfrp +25 -0
- data/examples/MostDistantPoint/MostDistantPointMain.c +14 -0
- data/examples/MostDistantPoint/graph.dot +12 -0
- data/examples/MostDistantPoint/graph.png +0 -0
- data/lib/emfrp/compile/c/alloc.rb +200 -0
- data/lib/emfrp/compile/c/codegen.rb +18 -0
- data/lib/emfrp/compile/c/codegen_context.rb +215 -0
- data/lib/emfrp/compile/c/monofy.rb +185 -0
- data/lib/emfrp/compile/c/syntax_codegen.rb +364 -0
- data/lib/emfrp/compile/c/syntax_exp_codegen.rb +119 -0
- data/lib/emfrp/compile/graphviz/graphviz.rb +49 -0
- data/lib/emfrp/compile_error.rb +95 -0
- data/lib/emfrp/interpreter/command_manager.rb +367 -0
- data/lib/emfrp/interpreter/evaluater.rb +146 -0
- data/lib/emfrp/interpreter/file_loader.rb +52 -0
- data/lib/emfrp/interpreter/interpreter.rb +195 -0
- data/lib/emfrp/parser/expression.rb +386 -0
- data/lib/emfrp/parser/misc.rb +184 -0
- data/lib/emfrp/parser/operator.rb +25 -0
- data/lib/emfrp/parser/parser.rb +145 -0
- data/lib/emfrp/parser/parsing_error.rb +49 -0
- data/lib/emfrp/parser/toplevel.rb +523 -0
- data/lib/emfrp/pre_convert/alpha_convert.rb +119 -0
- data/lib/emfrp/pre_convert/make_name_dict.rb +96 -0
- data/lib/emfrp/pre_convert/node_check.rb +60 -0
- data/lib/emfrp/pre_convert/pre_convert.rb +32 -0
- data/lib/emfrp/syntax.rb +169 -0
- data/lib/emfrp/typing/typing.rb +256 -0
- data/lib/emfrp/typing/typing_error.rb +47 -0
- data/lib/emfrp/typing/union_type.rb +197 -0
- data/lib/emfrp/version.rb +3 -0
- data/lib/emfrp.rb +14 -0
- data/mfrp_include/Std.mfrp +122 -0
- data/tests/Rakefile +8 -0
- data/tests/Rakefile.common +27 -0
- data/tests/command/Rakefile +2 -0
- data/tests/command/ReplaceNode.mfrp +39 -0
- data/tests/compiler/ComplexDataType/ComplexDataType.mfrp +14 -0
- data/tests/compiler/ComplexDataType/ComplexDataTypeMain.c +15 -0
- data/tests/compiler/ComplexDataType/Rakefile +2 -0
- data/tests/compiler/ComplexDataType/actual_out.txt +5 -0
- data/tests/compiler/ComplexDataType/expected_out.txt +5 -0
- data/tests/compiler/ComplexDataType/graph.png +0 -0
- data/tests/compiler/ComplexDataType/in.txt +5 -0
- data/tests/compiler/LCDPositioner/LCDPositioner.mfrp +30 -0
- data/tests/compiler/LCDPositioner/LCDPositionerMain.c +15 -0
- data/tests/compiler/LCDPositioner/Rakefile +2 -0
- data/tests/compiler/LCDPositioner/actual_out.txt +9 -0
- data/tests/compiler/LCDPositioner/expected_out.txt +9 -0
- data/tests/compiler/LCDPositioner/graph.dot +31 -0
- data/tests/compiler/LCDPositioner/graph.png +0 -0
- data/tests/compiler/LCDPositioner/in.txt +11 -0
- data/tests/compiler/Rakefile +8 -0
- data/tests/compiler/Rakefile.common +23 -0
- data/tests/core/FromAnnotation.mfrp +18 -0
- data/tests/core/Last.mfrp +10 -0
- data/tests/core/Rakefile +2 -0
- data/tests/core/TypingTest.mfrp +11 -0
- data/tests/core/WithoutInputs.mfrp +19 -0
- data/tests/load_time_error/Rakefile +32 -0
- data/tests/load_time_error/TypeMismatch.mfrp +4 -0
- metadata +180 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module Emfrp
|
|
2
|
+
module Graphviz
|
|
3
|
+
extend self
|
|
4
|
+
|
|
5
|
+
def compile(top, output_io)
|
|
6
|
+
node_stmts, edge_stmts = [], []
|
|
7
|
+
visited = {}
|
|
8
|
+
top[:outputs].each do |n|
|
|
9
|
+
node = top[:dict][:node_space][n[:name][:desc]].get
|
|
10
|
+
traverse(top, node, node_stmts, edge_stmts, visited)
|
|
11
|
+
end
|
|
12
|
+
output_io << "digraph #{top[:module_name][:desc]} {\n"
|
|
13
|
+
node_stmts.each do |s|
|
|
14
|
+
output_io << " #{s}\n"
|
|
15
|
+
end
|
|
16
|
+
edge_stmts.each do |s|
|
|
17
|
+
output_io << " #{s}\n"
|
|
18
|
+
end
|
|
19
|
+
output_io << "}\n"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def traverse(top, node, node_stmts, edge_stmts, visited)
|
|
23
|
+
return if visited[node]
|
|
24
|
+
visited[node] = true
|
|
25
|
+
name = node[:name][:desc]
|
|
26
|
+
type = node[:typing].to_uniq_str
|
|
27
|
+
node_attrs = ["label = \"#{name} : #{type}\""]
|
|
28
|
+
case node
|
|
29
|
+
when NodeDef
|
|
30
|
+
if top[:outputs].find{|o| o[:name] == node[:name]}
|
|
31
|
+
node_attrs += ["style = filled", "fillcolor = \"#e4e4e4\""]
|
|
32
|
+
end
|
|
33
|
+
node[:params].each do |n|
|
|
34
|
+
ch_name = n[:name][:desc]
|
|
35
|
+
if n[:last]
|
|
36
|
+
edge_stmts << "#{ch_name} -> #{name} [style = dashed];"
|
|
37
|
+
else
|
|
38
|
+
edge_stmts << "#{ch_name} -> #{name};"
|
|
39
|
+
end
|
|
40
|
+
ch_node = top[:dict][:node_space][ch_name].get
|
|
41
|
+
traverse(top, ch_node, node_stmts, edge_stmts, visited)
|
|
42
|
+
end
|
|
43
|
+
when InputDef
|
|
44
|
+
node_attrs << "shape = \"invhouse\""
|
|
45
|
+
end
|
|
46
|
+
node_stmts << "#{name} [#{node_attrs.join(", ")}];"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
require 'colorize'
|
|
2
|
+
|
|
3
|
+
module Emfrp
|
|
4
|
+
class CompileError < RuntimeError
|
|
5
|
+
attr_reader :message, :factors
|
|
6
|
+
|
|
7
|
+
def initialize(message, code, *factors)
|
|
8
|
+
@message = message
|
|
9
|
+
@code = message
|
|
10
|
+
@factors = factors
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def code
|
|
14
|
+
@code
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def tag_comp(a, b)
|
|
18
|
+
[a[:line_number], a[:column_number]] <=> [b[:line_number], b[:column_number]]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def collect_factor_tags(factor)
|
|
22
|
+
case factor
|
|
23
|
+
when Syntax
|
|
24
|
+
if factor.has_key?(:start_pos)
|
|
25
|
+
collect_factor_tags(factor.values) + [[factor[:start_pos], factor[:end_pos]]]
|
|
26
|
+
else
|
|
27
|
+
collect_factor_tags(factor.values)
|
|
28
|
+
end
|
|
29
|
+
when Array
|
|
30
|
+
factor.flat_map{|x| collect_factor_tags(x)}
|
|
31
|
+
else
|
|
32
|
+
[]
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def find_factor_file_name(factor)
|
|
37
|
+
case factor
|
|
38
|
+
when Syntax
|
|
39
|
+
if factor.has_key?(:start_pos)
|
|
40
|
+
return factor[:start_pos][:document_name]
|
|
41
|
+
else
|
|
42
|
+
return find_factor_file_name(factor.values)
|
|
43
|
+
end
|
|
44
|
+
when Array
|
|
45
|
+
factor.each do |x|
|
|
46
|
+
if res = find_factor_file_name(x)
|
|
47
|
+
return res
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def factor_name(factor)
|
|
55
|
+
klass = factor.class.name.split("::").last
|
|
56
|
+
if factor.is_a?(Syntax)
|
|
57
|
+
name = factor.has_key?(:name) ? "`#{factor[:name][:desc]}`" : ""
|
|
58
|
+
else
|
|
59
|
+
name = factor.inspect
|
|
60
|
+
end
|
|
61
|
+
klass + " " + name
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def print_lexical_factor(factor, output_io, file_loader)
|
|
65
|
+
src_lines = nil
|
|
66
|
+
unless factor_file_name = find_factor_file_name(factor)
|
|
67
|
+
raise "assertion error: cannot detect file_name"
|
|
68
|
+
end
|
|
69
|
+
src_str = file_loader.get_src_from_full_path(factor_file_name)
|
|
70
|
+
src_lines = src_str.each_line.to_a
|
|
71
|
+
tags = collect_factor_tags(factor)
|
|
72
|
+
spos = tags.map{|x| x[0]}.min{|a, b| tag_comp(a, b)}
|
|
73
|
+
epos = tags.map{|x| x[1]}.max{|a, b| tag_comp(a, b)}
|
|
74
|
+
line_nums = (spos[:line_number]..epos[:line_number]).to_a
|
|
75
|
+
if line_nums.size == 1
|
|
76
|
+
output_io << "#{factor_file_name}:#{line_nums[0]}:\n"
|
|
77
|
+
output_io << "> " + src_lines[line_nums[0] - 1].chomp + "\n"
|
|
78
|
+
output_io << " " + " " * (spos[:column_number] - 1)
|
|
79
|
+
output_io << ("^" * (epos[:column_number] - spos[:column_number] + 1)).colorize(:green) + "\n"
|
|
80
|
+
else
|
|
81
|
+
output_io << "#{factor_file_name}:#{line_nums.first}-#{line_nums.last}:\n"
|
|
82
|
+
line_nums.each do |line_num|
|
|
83
|
+
output_io << "> " + src_lines[line_num - 1]
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def print_error(output_io, file_loader)
|
|
89
|
+
output_io << "\e[31m[Error]\e[m: #{@message}"
|
|
90
|
+
@factors.each do |factor|
|
|
91
|
+
print_lexical_factor(factor, output_io, file_loader)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
require 'emfrp/compile/graphviz/graphviz'
|
|
2
|
+
|
|
3
|
+
module Emfrp
|
|
4
|
+
class Interpreter
|
|
5
|
+
class CommandManager
|
|
6
|
+
def initialize(interpreter, &proc)
|
|
7
|
+
@interpreter = interpreter
|
|
8
|
+
@command_names = []
|
|
9
|
+
@command_tbl = {}
|
|
10
|
+
@command_desc_tbl = {}
|
|
11
|
+
@command_usage_tbl = {}
|
|
12
|
+
@command_example_tbl = {}
|
|
13
|
+
@command_desc_buf = []
|
|
14
|
+
@command_usage_buf = []
|
|
15
|
+
@command_example_buf = []
|
|
16
|
+
instance_exec(&proc)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def command(*names, &proc)
|
|
20
|
+
names.each do |name|
|
|
21
|
+
@command_names << ":" + name
|
|
22
|
+
@command_tbl[name] = proc
|
|
23
|
+
@command_desc_tbl[name] = @command_desc_buf
|
|
24
|
+
@command_usage_tbl[name] = @command_usage_buf
|
|
25
|
+
@command_example_tbl[name] = @command_example_buf
|
|
26
|
+
@command_desc_buf, @command_usage_buf, @command_example_buf = [], [], []
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def desc(str)
|
|
31
|
+
@command_desc_buf << str
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def usage(str)
|
|
35
|
+
@command_usage_buf << str
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def example(str)
|
|
39
|
+
@command_example_buf << str
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def print_usage(command_name, output_io)
|
|
43
|
+
if @command_tbl[command_name]
|
|
44
|
+
output_io.puts ":#{command_name}".colorize(:light_blue)
|
|
45
|
+
output_io.puts @command_desc_tbl[command_name].map{|x| " " + x}.join("\n")
|
|
46
|
+
if @command_usage_tbl[command_name].size > 0
|
|
47
|
+
output_io.puts " Usage:".colorize(:green)
|
|
48
|
+
output_io.puts @command_usage_tbl[command_name].map{|x| " " + x}.join("\n")
|
|
49
|
+
end
|
|
50
|
+
if @command_example_tbl[command_name].size > 0
|
|
51
|
+
output_io.puts " Example:".colorize(:green)
|
|
52
|
+
output_io.puts @command_example_tbl[command_name].map{|x| " " + x}.join("\n")
|
|
53
|
+
end
|
|
54
|
+
return nil
|
|
55
|
+
else
|
|
56
|
+
return :command_not_found
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def print_all_usages(output_io)
|
|
61
|
+
@command_tbl.keys.sort.each do |name|
|
|
62
|
+
print_usage(name, output_io)
|
|
63
|
+
end
|
|
64
|
+
return nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def exec(command_name, arg, readline_id)
|
|
68
|
+
if @command_tbl[command_name]
|
|
69
|
+
@interpreter.instance_exec(arg, command_name, readline_id, &@command_tbl[command_name])
|
|
70
|
+
else
|
|
71
|
+
@interpreter.puts "Error: undefined command `#{command_name}'"
|
|
72
|
+
return :exec_error
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def completion_proc
|
|
77
|
+
proc do |s|
|
|
78
|
+
@command_names.select{|name| name.index(s) == 0}
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def self.make(interpreter)
|
|
83
|
+
CommandManager.new(interpreter) do
|
|
84
|
+
|
|
85
|
+
desc "Showing object type by specifying it's name."
|
|
86
|
+
example "data x = 1"
|
|
87
|
+
example ":t x"
|
|
88
|
+
command "t" do |arg|
|
|
89
|
+
name = arg.strip
|
|
90
|
+
if f = @top[:dict][:func_space][name]
|
|
91
|
+
puts "func #{name} : " + f.get[:params].map{|x| x[:typing].inspect}.join(", ") +
|
|
92
|
+
" -> " + f.get[:typing].inspect
|
|
93
|
+
end
|
|
94
|
+
if d = @top[:dict][:data_space][name]
|
|
95
|
+
puts "data #{name} : " + d.get[:typing].inspect
|
|
96
|
+
end
|
|
97
|
+
if t = @top[:dict][:type_space][name]
|
|
98
|
+
case t.get
|
|
99
|
+
when TypeDef
|
|
100
|
+
puts "Type #{name} : " + t.get[:tvalues][0][:typing].inspect
|
|
101
|
+
when PrimTypeDef
|
|
102
|
+
puts "PrimType #{name} : " + name
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
if c = @top[:dict][:const_space][name]
|
|
106
|
+
puts "constructor #{name} : " + c.get[:params].map{|x| x[:typing].inspect}.join(", ") +
|
|
107
|
+
" -> " + c.get[:typing].inspect
|
|
108
|
+
end
|
|
109
|
+
if n = @top[:dict][:node_space][name]
|
|
110
|
+
puts "node #{name} : " + n.get[:typing].inspect
|
|
111
|
+
end
|
|
112
|
+
next nil
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
desc "Showing internal AST by specifying element's name."
|
|
116
|
+
command "ast" do |arg|
|
|
117
|
+
name = arg.strip
|
|
118
|
+
if f = @top[:dict][:func_space][name]
|
|
119
|
+
pp f.get
|
|
120
|
+
elsif d = @top[:dict][:data_space][name]
|
|
121
|
+
pp d.get
|
|
122
|
+
elsif t = @top[:dict][:type_space][name]
|
|
123
|
+
pp t.get
|
|
124
|
+
elsif c = @top[:dict][:const_space][name]
|
|
125
|
+
pp c.get
|
|
126
|
+
elsif n = @top[:dict][:node_space][name]
|
|
127
|
+
pp n.get
|
|
128
|
+
elsif name == "top"
|
|
129
|
+
pp @top
|
|
130
|
+
elsif name == "ifuncs"
|
|
131
|
+
pp @top[:dict][:ifuncs_space].keys
|
|
132
|
+
elsif name == "itypes"
|
|
133
|
+
pp @top[:dict][:itypes_space].keys
|
|
134
|
+
else
|
|
135
|
+
puts "Error: `#{name}' is not found"
|
|
136
|
+
next :target_not_found
|
|
137
|
+
end
|
|
138
|
+
next nil
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
desc "Testing two expression's equality."
|
|
142
|
+
usage ":assert-equals <expected-exp>, <testing-exp>"
|
|
143
|
+
command "assert-equals" do |arg, c, rid|
|
|
144
|
+
if exp = str_to_exp("Pair(#{arg})")
|
|
145
|
+
val1 = Evaluater.eval_exp(@top, exp[:args][0])
|
|
146
|
+
val2 = Evaluater.eval_exp(@top, exp[:args][1])
|
|
147
|
+
if val1 == val2
|
|
148
|
+
nil
|
|
149
|
+
else
|
|
150
|
+
puts "Assertion failed".colorize(:red)
|
|
151
|
+
puts "Description: #{arg}"
|
|
152
|
+
puts "Type: #{exp[:args][0][:typing].inspect.colorize(:green)}"
|
|
153
|
+
puts "Expected: #{Evaluater.value_to_s(val1)}"
|
|
154
|
+
puts "Actual: #{Evaluater.value_to_s(val2)}"
|
|
155
|
+
:assertion_error
|
|
156
|
+
end
|
|
157
|
+
else
|
|
158
|
+
:command_format_error
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
desc "Executing all commands embeded on source-files."
|
|
163
|
+
command "exec-embeded-commands" do
|
|
164
|
+
exec_embeded_commands()
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
desc "Define documentation about function. (in preparation)"
|
|
168
|
+
command "set-func-doc" do |arg|
|
|
169
|
+
nil
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
desc "Testing node as function."
|
|
173
|
+
usage ":assert-node <node-name> <input-exp>* => <expected-output-exp>"
|
|
174
|
+
example ":assert-node mynode 1, 2 => 3"
|
|
175
|
+
command "assert-node" do |arg|
|
|
176
|
+
if arg =~ /^\s*([a-z][a-zA-Z0-9]*)\s+(.*)=>(.*)$/
|
|
177
|
+
n = @top[:dict][:node_space][$1]
|
|
178
|
+
if n && n.get.is_a?(NodeDef)
|
|
179
|
+
node_def = n.get
|
|
180
|
+
types = ["Unit", "Unit"] + node_def[:params].map{|x| x[:typing].to_uniq_str}
|
|
181
|
+
exp_str = ($2.strip == "" ? "(Unit, Unit)" : "(Unit, Unit, #{$2.strip})")
|
|
182
|
+
if a_exp = str_to_exp(exp_str, "(#{types.join(", ")})")
|
|
183
|
+
v1 = Evaluater.eval_node_as_func(@top, node_def, a_exp[:args].drop(2))
|
|
184
|
+
if $3.strip == "skip"
|
|
185
|
+
v2 = :skip
|
|
186
|
+
elsif r_exp = str_to_exp($3.strip, "#{node_def[:typing].to_uniq_str}")
|
|
187
|
+
v2 = Evaluater.eval_exp(@top, r_exp)
|
|
188
|
+
else
|
|
189
|
+
puts "Error: invalid expected-return-expression"
|
|
190
|
+
next :assert_node_error1
|
|
191
|
+
end
|
|
192
|
+
if v1 == v2
|
|
193
|
+
next nil
|
|
194
|
+
else
|
|
195
|
+
puts "Node Assertion failed".colorize(:red)
|
|
196
|
+
puts "Description: #{arg}"
|
|
197
|
+
puts "Expected: #{Evaluater.value_to_s(v2)}"
|
|
198
|
+
puts "Actual: #{Evaluater.value_to_s(v1)}"
|
|
199
|
+
next :assertion_error
|
|
200
|
+
end
|
|
201
|
+
else
|
|
202
|
+
puts "Error: invalid node-argument-expression"
|
|
203
|
+
next :assert_node_error2
|
|
204
|
+
end
|
|
205
|
+
else
|
|
206
|
+
puts "Error: invalid node name #{$1}"
|
|
207
|
+
next :assert_node_error3
|
|
208
|
+
end
|
|
209
|
+
else
|
|
210
|
+
next :command_format_error
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
desc "Testing whole-module by feeding inputs."
|
|
215
|
+
usage ":assert-module <input-exp>* => <expected-ouput-exp>*"
|
|
216
|
+
example ":assert-module 1, 2 => 2, 4"
|
|
217
|
+
command "assert-module" do |arg|
|
|
218
|
+
if arg =~ /^(.*)=>(.*)$/
|
|
219
|
+
input_types = ["Unit", "Unit"] + @top[:inputs].map{|x| x[:typing].to_uniq_str}
|
|
220
|
+
exp_str = ($1.strip == "" ? "(Unit, Unit)" : "(Unit, Unit, #{$1.strip})")
|
|
221
|
+
input_exps = str_to_exp(exp_str, "(#{input_types.join(", ")})")
|
|
222
|
+
output_types = @top[:outputs].map{|x| x[:typing].to_uniq_str}
|
|
223
|
+
output_exps = str_to_exp("(Unit, #{$2})", "(Unit, #{output_types.join(", ")})")
|
|
224
|
+
if input_exps == nil || output_exps == nil
|
|
225
|
+
puts "Error: invalid expression"
|
|
226
|
+
next :assert_module_error1
|
|
227
|
+
end
|
|
228
|
+
# evaluate
|
|
229
|
+
last_state = @current_state ? @current_state.clone : nil
|
|
230
|
+
@current_state = {}
|
|
231
|
+
@node_replacement ||= {}
|
|
232
|
+
output_vals = Evaluater.eval_module(@top, input_exps[:args].drop(2),
|
|
233
|
+
@current_state, last_state, @node_replacement)
|
|
234
|
+
expected_output_vals = output_exps[:args].drop(1).map{|x| Evaluater.eval_exp(@top, x)}
|
|
235
|
+
# assert
|
|
236
|
+
if expected_output_vals != output_vals
|
|
237
|
+
puts "Module Assertion failed".colorize(:red)
|
|
238
|
+
puts "Description: #{arg}"
|
|
239
|
+
puts "Expected: #{expected_output_vals.map{|x| Evaluater.value_to_s(x)}.join(", ")}"
|
|
240
|
+
puts "Actual: #{output_vals.map{|x| Evaluater.value_to_s(x)}.join(", ")}"
|
|
241
|
+
:assertion_error
|
|
242
|
+
else
|
|
243
|
+
nil
|
|
244
|
+
end
|
|
245
|
+
else
|
|
246
|
+
:command_format_error
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
desc "Testing expression's type."
|
|
251
|
+
usage ":assert-type: <exp> => <type>"
|
|
252
|
+
command "assert-type" do |arg|
|
|
253
|
+
if arg =~ /^(.*)=>(.*)$/
|
|
254
|
+
if exp = str_to_exp($1.strip)
|
|
255
|
+
if exp[:typing].to_uniq_str == $2.strip
|
|
256
|
+
next nil
|
|
257
|
+
else
|
|
258
|
+
puts "Type Assertion failed".colorize(:red)
|
|
259
|
+
puts "Description: #{$1.strip}"
|
|
260
|
+
puts "Expected: #{$2.strip}"
|
|
261
|
+
puts "Actual: #{exp[:typing].to_uniq_str}"
|
|
262
|
+
next :assertion_error
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
next :command_format_error
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
desc "Testing that specified command finishes with specified error-code"
|
|
270
|
+
usage ":assert-error <expected-error-code> => <testing-command>"
|
|
271
|
+
example ":assert-error assertion_error => :assert-type 1 => Double"
|
|
272
|
+
command "assert-error" do |arg|
|
|
273
|
+
if arg =~ /^\s*([a-z][a-zA-Z0-9_]*)\s*=>\s*(.*)$/
|
|
274
|
+
expected_error_code = $1
|
|
275
|
+
res = disable_io{ process_repl_line($2) }
|
|
276
|
+
if res.to_s == expected_error_code
|
|
277
|
+
next nil
|
|
278
|
+
else
|
|
279
|
+
puts "Error-Assertion error"
|
|
280
|
+
puts "Expected error-code: #{expected_error_code}"
|
|
281
|
+
puts "Actual error-code: #{res}"
|
|
282
|
+
next :assertion_error
|
|
283
|
+
end
|
|
284
|
+
else
|
|
285
|
+
next :command_format_error
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
desc "Replace one node to another like Stab."
|
|
290
|
+
desc "currently, this is only for testing (command-line assertion)."
|
|
291
|
+
usage ":replace-node <replaced-node-name> => <alternative-node-name>"
|
|
292
|
+
command "replace-node" do |arg|
|
|
293
|
+
if arg =~ /^\s*([a-z][a-zA-Z0-9]*)\s*=>\s*([a-z][a-zA-Z0-9]*)\s*$/
|
|
294
|
+
real_n_ln, dummy_n_ln = @top[:dict][:node_space][$1], @top[:dict][:node_space][$2]
|
|
295
|
+
unless real_n_ln
|
|
296
|
+
puts "Error: Node `#{$1}' is undefined"
|
|
297
|
+
next :replace_node_err1
|
|
298
|
+
end
|
|
299
|
+
unless dummy_n_ln
|
|
300
|
+
puts "Error: Node `#{$2}' is undefined"
|
|
301
|
+
next :replace_node_err2
|
|
302
|
+
end
|
|
303
|
+
unless real_n_ln.get[:typing].to_uniq_str == dummy_n_ln.get[:typing].to_uniq_str
|
|
304
|
+
puts "Error: Types of Real-Node `#{$1}' and Dummy-Node `#{$2}' are different"
|
|
305
|
+
puts "#{$1} : #{real_n_ln.get[:typing].to_uniq_str}"
|
|
306
|
+
puts "#{$2} : #{dummy_n_ln.get[:typing].to_uniq_str}"
|
|
307
|
+
next :replace_node_err3
|
|
308
|
+
end
|
|
309
|
+
collect_deps = proc do |node|
|
|
310
|
+
if node.is_a?(NodeDef)
|
|
311
|
+
[node] + node[:params].reject{|x| x[:last]}.map{|p|
|
|
312
|
+
collect_deps.call(@top[:dict][:node_space][p[:name][:desc]].get)
|
|
313
|
+
}.flatten
|
|
314
|
+
else
|
|
315
|
+
[node]
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
c1 = collect_deps.call(dummy_n_ln.get).find{|x| x[:name] == real_n_ln.get[:name]}
|
|
319
|
+
c2 = collect_deps.call(real_n_ln.get).find{|x| x[:name] == dummy_n_ln.get[:name]}
|
|
320
|
+
unless c1 == nil && c2 == nil
|
|
321
|
+
puts "Error: Real-Node `#{$1}' and Dummy-Node `#{$2}' are on depending relation"
|
|
322
|
+
next :replace_node_err4
|
|
323
|
+
end
|
|
324
|
+
if real_n_ln.get[:init_exp] && !dummy_n_ln.get[:init_exp]
|
|
325
|
+
puts "Error: Dummy-Node `#{$2}' should have init-exp"
|
|
326
|
+
next :replace_node_err5
|
|
327
|
+
end
|
|
328
|
+
@node_replacement ||= {}
|
|
329
|
+
@node_replacement[$1] = dummy_n_ln.get
|
|
330
|
+
next nil
|
|
331
|
+
else
|
|
332
|
+
next :command_format_error
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
desc "Compiling module-file into c-program code."
|
|
337
|
+
desc "Target file-name is currently fixed..."
|
|
338
|
+
desc "(module-name is used and files are dumped on current-dir)"
|
|
339
|
+
usage ":compile"
|
|
340
|
+
command "compile" do
|
|
341
|
+
next compile_default()
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
desc "Compiling module-file into graphviz-source code (.dot file)."
|
|
345
|
+
desc "If file-name is given as a command-argument, the code is output to it."
|
|
346
|
+
desc "Otherwise, the code is output on console."
|
|
347
|
+
example ":compile-dot graph.dot"
|
|
348
|
+
command "compile-dot" do |arg|
|
|
349
|
+
if arg.strip != ""
|
|
350
|
+
File.open(arg, "w") do |f|
|
|
351
|
+
Graphviz.compile(@top, f)
|
|
352
|
+
end
|
|
353
|
+
else
|
|
354
|
+
Graphviz.compile(@top, @output_io)
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
desc "Showing usage of all commands."
|
|
359
|
+
command "commands" do
|
|
360
|
+
@command_manager.print_all_usages(@output_io)
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
end
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
module Emfrp
|
|
2
|
+
class Interpreter
|
|
3
|
+
module Evaluater
|
|
4
|
+
extend self
|
|
5
|
+
|
|
6
|
+
def eval_exp(top, exp, env={})
|
|
7
|
+
case exp
|
|
8
|
+
when FuncCall
|
|
9
|
+
case f = top[:dict][:func_space][exp[:name][:desc]].get
|
|
10
|
+
when PrimFuncDef
|
|
11
|
+
if ruby_exp = f[:foreigns].find{|x| x[:language][:desc] == "ruby"}
|
|
12
|
+
proc_str = "proc{|#{f[:params].map{|x| x[:name][:desc]}.join(",")}| #{ruby_exp[:desc]}}"
|
|
13
|
+
return eval(proc_str).call(*exp[:args].map{|e| eval_exp(top, e, env)})
|
|
14
|
+
else
|
|
15
|
+
raise "Primitive Function `#{f[:name][:desc]}` is not defined for ruby"
|
|
16
|
+
end
|
|
17
|
+
when FuncDef
|
|
18
|
+
f[:params].map{|param| [param[:name], Link.new(f)]}.zip(exp[:args]).each do |key, arg|
|
|
19
|
+
env[key] = eval_exp(top, arg, env)
|
|
20
|
+
end
|
|
21
|
+
return eval_exp(top, f[:exp], env)
|
|
22
|
+
end
|
|
23
|
+
when ValueConst
|
|
24
|
+
return [exp[:name][:desc].to_sym] + exp[:args].map{|e| eval_exp(top, e, env)}
|
|
25
|
+
when LiteralIntegral
|
|
26
|
+
return exp[:entity][:desc].to_i
|
|
27
|
+
when LiteralChar
|
|
28
|
+
return exp[:entity].ord
|
|
29
|
+
when LiteralFloating
|
|
30
|
+
return exp[:entity][:desc].to_f
|
|
31
|
+
when VarRef
|
|
32
|
+
key = [exp[:name], exp[:binder]]
|
|
33
|
+
unless env[key]
|
|
34
|
+
if exp[:binder].get.is_a?(DataDef)
|
|
35
|
+
env[key] = eval_exp(top, exp[:binder].get[:exp], env)
|
|
36
|
+
else
|
|
37
|
+
raise "Assertion error: #{key} is unbound"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
return env[key]
|
|
41
|
+
when ParenthExp
|
|
42
|
+
return eval_exp(top, exp[:exp], env)
|
|
43
|
+
when MatchExp
|
|
44
|
+
left_val = eval_exp(top, exp[:exp], env)
|
|
45
|
+
exp[:cases].each do |c|
|
|
46
|
+
if match_result = pattern_match(c, left_val)
|
|
47
|
+
return eval_exp(top, c[:exp], env.merge(match_result))
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
raise "pattern match fail"
|
|
51
|
+
when SkipExp
|
|
52
|
+
throw :skip, :skip
|
|
53
|
+
else
|
|
54
|
+
raise "Unexpected expression type #{exp.class} (bug)"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def eval_node_as_func(top, node_def, exps)
|
|
59
|
+
env = {}
|
|
60
|
+
if node_def[:params].size != exps.size
|
|
61
|
+
raise "Assertion error: invalid length of args"
|
|
62
|
+
end
|
|
63
|
+
node_def[:params].map{|param| [param[:as], Link.new(node_def)]}.zip(exps).each do |key, arg|
|
|
64
|
+
env[key] = eval_exp(top, arg, env)
|
|
65
|
+
end
|
|
66
|
+
return catch(:skip) do
|
|
67
|
+
return eval_exp(top, node_def[:exp], env)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def eval_module(top, input_exps, current_state, last_state, replacement)
|
|
72
|
+
top[:inputs].zip(input_exps).each do |i, e|
|
|
73
|
+
current_state[i] = eval_exp(top, e)
|
|
74
|
+
end
|
|
75
|
+
unless last_state
|
|
76
|
+
last_state = {}
|
|
77
|
+
(top[:inputs] + top[:nodes]).each do |d|
|
|
78
|
+
last_state[d] = eval_exp(top, d[:init_exp]) if d[:init_exp]
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
(top[:inputs] + top[:nodes]).each do |d|
|
|
82
|
+
eval_node(top, d, current_state, last_state, replacement)
|
|
83
|
+
end
|
|
84
|
+
return top[:outputs].map do |x|
|
|
85
|
+
eval_node(top, top[:dict][:node_space][x[:name][:desc]].get, current_state, last_state, replacement)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def eval_node(top, node_def, current_state, last_state, replacement)
|
|
90
|
+
if replacement[node_def[:name][:desc]]
|
|
91
|
+
rep_node = replacement[node_def[:name][:desc]]
|
|
92
|
+
return eval_node(top, rep_node, current_state, last_state, replacement)
|
|
93
|
+
end
|
|
94
|
+
return current_state[node_def] if current_state[node_def]
|
|
95
|
+
env = {}
|
|
96
|
+
node_def[:params].each do |param|
|
|
97
|
+
key = [param[:as], Link.new(node_def)]
|
|
98
|
+
pn = top[:dict][:node_space][param[:name][:desc]].get
|
|
99
|
+
if param[:last]
|
|
100
|
+
raise "Assertion error" unless last_state[pn]
|
|
101
|
+
if rep_node = replacement[param[:name][:desc]]
|
|
102
|
+
env[key] = last_state[rep_node]
|
|
103
|
+
else
|
|
104
|
+
env[key] = last_state[pn]
|
|
105
|
+
end
|
|
106
|
+
else
|
|
107
|
+
env[key] = eval_node(top, pn, current_state, last_state, replacement)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
res = catch(:skip){ eval_exp(top, node_def[:exp], env) }
|
|
111
|
+
return current_state[node_def] = res == :skip ? last_state[node_def] : res
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def pattern_match(c, v, pattern=c[:pattern], vars={})
|
|
115
|
+
if pattern[:ref]
|
|
116
|
+
key = [pattern[:ref], Link.new(c)]
|
|
117
|
+
vars[key] = v
|
|
118
|
+
end
|
|
119
|
+
case pattern
|
|
120
|
+
when ValuePattern
|
|
121
|
+
if v.is_a?(Array) && pattern[:name][:desc].to_sym == v[0]
|
|
122
|
+
res = v.drop(1).zip(pattern[:args]).all? do |ch_v, ch_p|
|
|
123
|
+
pattern_match(c, ch_v, ch_p, vars)
|
|
124
|
+
end
|
|
125
|
+
return vars if res
|
|
126
|
+
end
|
|
127
|
+
when IntegralPattern
|
|
128
|
+
if v.is_a?(Integer) && pattern[:val][:entity][:desc].to_i == v
|
|
129
|
+
return vars
|
|
130
|
+
end
|
|
131
|
+
when AnyPattern
|
|
132
|
+
return vars
|
|
133
|
+
end
|
|
134
|
+
return nil
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def value_to_s(val)
|
|
138
|
+
if val.is_a?(Array) && val.first.is_a?(Symbol)
|
|
139
|
+
"#{val.first}" + (val.size > 1 ? "(#{val.drop(1).map{|x| value_to_s(x)}.join(", ")})" : "")
|
|
140
|
+
else
|
|
141
|
+
val.to_s
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|