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.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/Gemfile +6 -0
  4. data/LICENSE +28 -0
  5. data/README.md +27 -0
  6. data/Rakefile +2 -0
  7. data/bin/emfrp +21 -0
  8. data/bin/emfrpi +24 -0
  9. data/emfrp.gemspec +26 -0
  10. data/examples/LCDPositioner/LCDPositioner.c +278 -0
  11. data/examples/LCDPositioner/LCDPositioner.h +6 -0
  12. data/examples/LCDPositioner/LCDPositioner.mfrp +30 -0
  13. data/examples/LCDPositioner/LCDPositionerMain.c +15 -0
  14. data/examples/LCDPositioner/LCDPositionerMain.c.gen +11 -0
  15. data/examples/LCDPositioner/graph.dot +31 -0
  16. data/examples/LCDPositioner/graph.png +0 -0
  17. data/examples/MostDistantPoint/MostDistantPoint.c +199 -0
  18. data/examples/MostDistantPoint/MostDistantPoint.h +6 -0
  19. data/examples/MostDistantPoint/MostDistantPoint.mfrp +25 -0
  20. data/examples/MostDistantPoint/MostDistantPointMain.c +14 -0
  21. data/examples/MostDistantPoint/graph.dot +12 -0
  22. data/examples/MostDistantPoint/graph.png +0 -0
  23. data/lib/emfrp/compile/c/alloc.rb +200 -0
  24. data/lib/emfrp/compile/c/codegen.rb +18 -0
  25. data/lib/emfrp/compile/c/codegen_context.rb +215 -0
  26. data/lib/emfrp/compile/c/monofy.rb +185 -0
  27. data/lib/emfrp/compile/c/syntax_codegen.rb +364 -0
  28. data/lib/emfrp/compile/c/syntax_exp_codegen.rb +119 -0
  29. data/lib/emfrp/compile/graphviz/graphviz.rb +49 -0
  30. data/lib/emfrp/compile_error.rb +95 -0
  31. data/lib/emfrp/interpreter/command_manager.rb +367 -0
  32. data/lib/emfrp/interpreter/evaluater.rb +146 -0
  33. data/lib/emfrp/interpreter/file_loader.rb +52 -0
  34. data/lib/emfrp/interpreter/interpreter.rb +195 -0
  35. data/lib/emfrp/parser/expression.rb +386 -0
  36. data/lib/emfrp/parser/misc.rb +184 -0
  37. data/lib/emfrp/parser/operator.rb +25 -0
  38. data/lib/emfrp/parser/parser.rb +145 -0
  39. data/lib/emfrp/parser/parsing_error.rb +49 -0
  40. data/lib/emfrp/parser/toplevel.rb +523 -0
  41. data/lib/emfrp/pre_convert/alpha_convert.rb +119 -0
  42. data/lib/emfrp/pre_convert/make_name_dict.rb +96 -0
  43. data/lib/emfrp/pre_convert/node_check.rb +60 -0
  44. data/lib/emfrp/pre_convert/pre_convert.rb +32 -0
  45. data/lib/emfrp/syntax.rb +169 -0
  46. data/lib/emfrp/typing/typing.rb +256 -0
  47. data/lib/emfrp/typing/typing_error.rb +47 -0
  48. data/lib/emfrp/typing/union_type.rb +197 -0
  49. data/lib/emfrp/version.rb +3 -0
  50. data/lib/emfrp.rb +14 -0
  51. data/mfrp_include/Std.mfrp +122 -0
  52. data/tests/Rakefile +8 -0
  53. data/tests/Rakefile.common +27 -0
  54. data/tests/command/Rakefile +2 -0
  55. data/tests/command/ReplaceNode.mfrp +39 -0
  56. data/tests/compiler/ComplexDataType/ComplexDataType.mfrp +14 -0
  57. data/tests/compiler/ComplexDataType/ComplexDataTypeMain.c +15 -0
  58. data/tests/compiler/ComplexDataType/Rakefile +2 -0
  59. data/tests/compiler/ComplexDataType/actual_out.txt +5 -0
  60. data/tests/compiler/ComplexDataType/expected_out.txt +5 -0
  61. data/tests/compiler/ComplexDataType/graph.png +0 -0
  62. data/tests/compiler/ComplexDataType/in.txt +5 -0
  63. data/tests/compiler/LCDPositioner/LCDPositioner.mfrp +30 -0
  64. data/tests/compiler/LCDPositioner/LCDPositionerMain.c +15 -0
  65. data/tests/compiler/LCDPositioner/Rakefile +2 -0
  66. data/tests/compiler/LCDPositioner/actual_out.txt +9 -0
  67. data/tests/compiler/LCDPositioner/expected_out.txt +9 -0
  68. data/tests/compiler/LCDPositioner/graph.dot +31 -0
  69. data/tests/compiler/LCDPositioner/graph.png +0 -0
  70. data/tests/compiler/LCDPositioner/in.txt +11 -0
  71. data/tests/compiler/Rakefile +8 -0
  72. data/tests/compiler/Rakefile.common +23 -0
  73. data/tests/core/FromAnnotation.mfrp +18 -0
  74. data/tests/core/Last.mfrp +10 -0
  75. data/tests/core/Rakefile +2 -0
  76. data/tests/core/TypingTest.mfrp +11 -0
  77. data/tests/core/WithoutInputs.mfrp +19 -0
  78. data/tests/load_time_error/Rakefile +32 -0
  79. data/tests/load_time_error/TypeMismatch.mfrp +4 -0
  80. 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