emfrp 0.1.2 → 0.1.3
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 +4 -4
- data/README.md +45 -12
- data/bin/emfrp +4 -1
- data/examples/LCDClock/LCDClock.mfrp +93 -93
- data/examples/LCDClock/LCDClock_LPC1768.bin +0 -0
- data/examples/LCDClock/README.md +24 -24
- data/examples/LCDPositioner/LCDPositioner.mfrp +30 -30
- data/examples/LCDPositioner/LCDPositionerMain.c +15 -15
- data/examples/MostDistantPoint/MostDistantPoint.mfrp +25 -25
- data/examples/MostDistantPoint/MostDistantPointMain.c +14 -14
- data/lib/emfrp/compile/c/alloc.rb +200 -200
- data/lib/emfrp/compile/c/codegen.rb +18 -18
- data/lib/emfrp/compile/c/codegen_context.rb +218 -218
- data/lib/emfrp/compile/c/monofy.rb +185 -185
- data/lib/emfrp/compile/c/syntax_codegen.rb +364 -364
- data/lib/emfrp/compile/c/syntax_exp_codegen.rb +119 -119
- data/lib/emfrp/compile/graphviz/graphviz.rb +53 -53
- data/lib/emfrp/compile_error.rb +95 -95
- data/lib/emfrp/interpreter/command_manager.rb +367 -367
- data/lib/emfrp/interpreter/evaluater.rb +146 -146
- data/lib/emfrp/interpreter/file_loader.rb +52 -52
- data/lib/emfrp/interpreter/interpreter.rb +200 -195
- data/lib/emfrp/parser/expression.rb +386 -386
- data/lib/emfrp/parser/misc.rb +184 -184
- data/lib/emfrp/parser/newnode_convert.rb +72 -72
- data/lib/emfrp/parser/operator.rb +25 -25
- data/lib/emfrp/parser/parser.rb +150 -150
- data/lib/emfrp/parser/parsing_error.rb +49 -49
- data/lib/emfrp/parser/toplevel.rb +555 -555
- data/lib/emfrp/pre_convert/pre_convert.rb +32 -32
- data/lib/emfrp/syntax.rb +171 -171
- data/lib/emfrp/typing/typing_error.rb +47 -47
- data/lib/emfrp/typing/union_type.rb +197 -197
- data/lib/emfrp/version.rb +1 -1
- data/mfrp_include/Std.mfrp +122 -122
- data/tests/Rakefile +8 -8
- data/tests/Rakefile.common +27 -27
- data/tests/command/Rakefile +2 -2
- data/tests/command/ReplaceNode.mfrp +39 -39
- data/tests/compiler/ComplexDataType/ComplexDataType.mfrp +14 -14
- data/tests/compiler/ComplexDataType/ComplexDataTypeMain.c +15 -15
- data/tests/compiler/ComplexDataType/Rakefile +2 -2
- data/tests/compiler/ComplexDataType/expected_out.txt +0 -0
- data/tests/compiler/ComplexDataType/in.txt +5 -5
- data/tests/compiler/LCDClock/LCDClock.mfrp +90 -90
- data/tests/compiler/LCDClock/LCDClockMain.c +0 -0
- data/tests/compiler/LCDClock/Rakefile +2 -2
- data/tests/compiler/LCDClock/expected_out.txt +0 -0
- data/tests/compiler/LCDClock/in.txt +0 -0
- data/tests/compiler/LCDPositioner/LCDPositioner.mfrp +30 -30
- data/tests/compiler/LCDPositioner/LCDPositionerMain.c +15 -15
- data/tests/compiler/LCDPositioner/Rakefile +2 -2
- data/tests/compiler/LCDPositioner/graph.dot +0 -0
- data/tests/compiler/LCDPositioner/graph.png +0 -0
- data/tests/compiler/Rakefile +8 -8
- data/tests/compiler/Rakefile.common +23 -23
- data/tests/compiler/UseData/Rakefile +2 -2
- data/tests/compiler/UseData/UseData.mfrp +8 -8
- data/tests/compiler/UseSubModule/Rakefile +2 -2
- data/tests/compiler/UseSubModule/SubModule.mfrp +8 -8
- data/tests/compiler/UseSubModule/SubModule2.mfrp +5 -5
- data/tests/compiler/UseSubModule/UseSubModule.mfrp +11 -11
- data/tests/core/FromAnnotation.mfrp +18 -18
- data/tests/core/Last.mfrp +10 -10
- data/tests/core/Rakefile +2 -2
- data/tests/core/TypingTest.mfrp +11 -11
- data/tests/core/WithoutInputs.mfrp +19 -19
- data/tests/load_time_error/Rakefile +32 -32
- data/tests/load_time_error/TypeMismatch.mfrp +4 -4
- metadata +3 -3
@@ -1,119 +1,119 @@
|
|
1
|
-
require 'emfrp/syntax'
|
2
|
-
|
3
|
-
module Emfrp
|
4
|
-
class FuncCall
|
5
|
-
def codegen(ct, stmts)
|
6
|
-
name = ct.func_name(self[:name][:desc], self[:typing], self[:args].map{|x| x[:typing]})
|
7
|
-
"#{name}(#{self[:args].map{|x| x.codegen(ct, stmts)}.join(", ")})"
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
class ValueConst
|
12
|
-
def codegen(ct, stmts)
|
13
|
-
if ct.tdef(self[:typing]).enum?(ct)
|
14
|
-
ct.tdef(self[:typing])[:tvalues].index{|t| t[:name] == self[:name]}.to_s
|
15
|
-
else
|
16
|
-
name = ct.constructor_name(self[:name][:desc], self[:typing])
|
17
|
-
"#{name}(#{self[:args].map{|x| x.codegen(ct, stmts)}.join(", ")})"
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
class ParenthExp
|
23
|
-
def codegen(ct, stmts)
|
24
|
-
self[:exp].codegen(ct, stmts)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
class LiteralIntegral
|
29
|
-
def codegen(ct, stmts)
|
30
|
-
self[:entity][:desc]
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
class LiteralFloating
|
35
|
-
def codegen(ct, stmts)
|
36
|
-
self[:entity][:desc]
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
class LitaralChar
|
41
|
-
def codegen(ct, stmts)
|
42
|
-
self[:entity][:desc]
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
class VarRef
|
47
|
-
def codegen(ct, stmts)
|
48
|
-
self[:binder].get.var_name(ct, self[:name][:desc])
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
class MatchExp
|
53
|
-
def codegen(ct, stmts)
|
54
|
-
vname = "_tmp%03d" % ct.uniq_id_gen
|
55
|
-
stmts << "#{ct.tref(self)} #{vname};"
|
56
|
-
left = self[:exp]
|
57
|
-
if left.is_a?(VarRef)
|
58
|
-
left_vname = left[:name][:desc]
|
59
|
-
else
|
60
|
-
left_vname = "_tmp%03d" % ct.uniq_id_gen
|
61
|
-
stmts.unshift "#{ct.tref(left)} #{left_vname};"
|
62
|
-
stmts.push "#{left_vname} = #{left.codegen(ct, stmts)};"
|
63
|
-
end
|
64
|
-
self[:cases].each_with_index do |c, i|
|
65
|
-
then_stmts = []
|
66
|
-
cond_exps = pattern_to_cond_exps(ct, left_vname, then_stmts, c, c[:pattern])
|
67
|
-
cond_exp = cond_exps.size == 0 ? "1" : cond_exps.join(" && ")
|
68
|
-
if c[:exp].is_a?(SkipExp)
|
69
|
-
then_stmts << "return 0;"
|
70
|
-
else
|
71
|
-
then_stmts << "#{vname} = #{c[:exp].codegen(ct, then_stmts)};"
|
72
|
-
end
|
73
|
-
if i == 0
|
74
|
-
stmts << ct.make_block("if (#{cond_exp}) {", then_stmts, "}")
|
75
|
-
else
|
76
|
-
stmts << ct.make_block("else if (#{cond_exp}) {", then_stmts, "}")
|
77
|
-
end
|
78
|
-
end
|
79
|
-
return vname
|
80
|
-
end
|
81
|
-
|
82
|
-
def pattern_to_cond_exps(ct, receiver, stmts, case_def, pattern)
|
83
|
-
if pattern[:ref]
|
84
|
-
vname = case_def.var_name(ct, pattern[:ref][:desc])
|
85
|
-
stmts << "#{ct.tref(pattern)} #{vname} = #{receiver};"
|
86
|
-
end
|
87
|
-
case pattern
|
88
|
-
when ValuePattern
|
89
|
-
conds = []
|
90
|
-
type_def = ct.tdef(pattern)
|
91
|
-
accessor = type_def[:static] ? "." : "->"
|
92
|
-
if type_def[:tvalues].size > 1
|
93
|
-
tvalue_id = type_def[:tvalues].index{|x| x[:name] == pattern[:name]}
|
94
|
-
if type_def.enum?(ct)
|
95
|
-
conds << "#{receiver} == #{tvalue_id}"
|
96
|
-
else
|
97
|
-
conds << "#{receiver}" + accessor + "tvalue_type == " + tvalue_id.to_s
|
98
|
-
end
|
99
|
-
end
|
100
|
-
new_receiver = "#{receiver}" + accessor + "value." + pattern[:name][:desc]
|
101
|
-
pattern[:args].each_with_index do |x, i|
|
102
|
-
conds += pattern_to_cond_exps(ct, new_receiver + ".member#{i}", stmts, case_def, x)
|
103
|
-
end
|
104
|
-
return conds
|
105
|
-
when IntegralPattern
|
106
|
-
return ["#{receiver} == #{pattern[:val][:entity][:desc]}"]
|
107
|
-
else
|
108
|
-
return []
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
class Case
|
114
|
-
def var_name(ct, name)
|
115
|
-
"pvar#{ct.serial(nil, self)}_" + ct.escape_name(name)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
end
|
1
|
+
require 'emfrp/syntax'
|
2
|
+
|
3
|
+
module Emfrp
|
4
|
+
class FuncCall
|
5
|
+
def codegen(ct, stmts)
|
6
|
+
name = ct.func_name(self[:name][:desc], self[:typing], self[:args].map{|x| x[:typing]})
|
7
|
+
"#{name}(#{self[:args].map{|x| x.codegen(ct, stmts)}.join(", ")})"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class ValueConst
|
12
|
+
def codegen(ct, stmts)
|
13
|
+
if ct.tdef(self[:typing]).enum?(ct)
|
14
|
+
ct.tdef(self[:typing])[:tvalues].index{|t| t[:name] == self[:name]}.to_s
|
15
|
+
else
|
16
|
+
name = ct.constructor_name(self[:name][:desc], self[:typing])
|
17
|
+
"#{name}(#{self[:args].map{|x| x.codegen(ct, stmts)}.join(", ")})"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class ParenthExp
|
23
|
+
def codegen(ct, stmts)
|
24
|
+
self[:exp].codegen(ct, stmts)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class LiteralIntegral
|
29
|
+
def codegen(ct, stmts)
|
30
|
+
self[:entity][:desc]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class LiteralFloating
|
35
|
+
def codegen(ct, stmts)
|
36
|
+
self[:entity][:desc]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class LitaralChar
|
41
|
+
def codegen(ct, stmts)
|
42
|
+
self[:entity][:desc]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class VarRef
|
47
|
+
def codegen(ct, stmts)
|
48
|
+
self[:binder].get.var_name(ct, self[:name][:desc])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class MatchExp
|
53
|
+
def codegen(ct, stmts)
|
54
|
+
vname = "_tmp%03d" % ct.uniq_id_gen
|
55
|
+
stmts << "#{ct.tref(self)} #{vname};"
|
56
|
+
left = self[:exp]
|
57
|
+
if left.is_a?(VarRef)
|
58
|
+
left_vname = ct.escape_name(left[:name][:desc])
|
59
|
+
else
|
60
|
+
left_vname = "_tmp%03d" % ct.uniq_id_gen
|
61
|
+
stmts.unshift "#{ct.tref(left)} #{left_vname};"
|
62
|
+
stmts.push "#{left_vname} = #{left.codegen(ct, stmts)};"
|
63
|
+
end
|
64
|
+
self[:cases].each_with_index do |c, i|
|
65
|
+
then_stmts = []
|
66
|
+
cond_exps = pattern_to_cond_exps(ct, left_vname, then_stmts, c, c[:pattern])
|
67
|
+
cond_exp = cond_exps.size == 0 ? "1" : cond_exps.join(" && ")
|
68
|
+
if c[:exp].is_a?(SkipExp)
|
69
|
+
then_stmts << "return 0;"
|
70
|
+
else
|
71
|
+
then_stmts << "#{vname} = #{c[:exp].codegen(ct, then_stmts)};"
|
72
|
+
end
|
73
|
+
if i == 0
|
74
|
+
stmts << ct.make_block("if (#{cond_exp}) {", then_stmts, "}")
|
75
|
+
else
|
76
|
+
stmts << ct.make_block("else if (#{cond_exp}) {", then_stmts, "}")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
return vname
|
80
|
+
end
|
81
|
+
|
82
|
+
def pattern_to_cond_exps(ct, receiver, stmts, case_def, pattern)
|
83
|
+
if pattern[:ref]
|
84
|
+
vname = case_def.var_name(ct, pattern[:ref][:desc])
|
85
|
+
stmts << "#{ct.tref(pattern)} #{vname} = #{receiver};"
|
86
|
+
end
|
87
|
+
case pattern
|
88
|
+
when ValuePattern
|
89
|
+
conds = []
|
90
|
+
type_def = ct.tdef(pattern)
|
91
|
+
accessor = type_def[:static] ? "." : "->"
|
92
|
+
if type_def[:tvalues].size > 1
|
93
|
+
tvalue_id = type_def[:tvalues].index{|x| x[:name] == pattern[:name]}
|
94
|
+
if type_def.enum?(ct)
|
95
|
+
conds << "#{receiver} == #{tvalue_id}"
|
96
|
+
else
|
97
|
+
conds << "#{receiver}" + accessor + "tvalue_type == " + tvalue_id.to_s
|
98
|
+
end
|
99
|
+
end
|
100
|
+
new_receiver = "#{receiver}" + accessor + "value." + pattern[:name][:desc]
|
101
|
+
pattern[:args].each_with_index do |x, i|
|
102
|
+
conds += pattern_to_cond_exps(ct, new_receiver + ".member#{i}", stmts, case_def, x)
|
103
|
+
end
|
104
|
+
return conds
|
105
|
+
when IntegralPattern
|
106
|
+
return ["#{receiver} == #{pattern[:val][:entity][:desc]}"]
|
107
|
+
else
|
108
|
+
return []
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class Case
|
114
|
+
def var_name(ct, name)
|
115
|
+
"pvar#{ct.serial(nil, self)}_" + ct.escape_name(name)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
@@ -1,53 +1,53 @@
|
|
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 << "#{escape_name(ch_name)} -> #{escape_name(name)} [style = dashed];"
|
37
|
-
else
|
38
|
-
edge_stmts << "#{escape_name(ch_name)} -> #{escape_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 << "#{escape_name(name)} [#{node_attrs.join(", ")}];"
|
47
|
-
end
|
48
|
-
|
49
|
-
def escape_name(name)
|
50
|
-
CodegenContext.new(nil).escape_name(name)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
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 << "#{escape_name(ch_name)} -> #{escape_name(name)} [style = dashed];"
|
37
|
+
else
|
38
|
+
edge_stmts << "#{escape_name(ch_name)} -> #{escape_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 << "#{escape_name(name)} [#{node_attrs.join(", ")}];"
|
47
|
+
end
|
48
|
+
|
49
|
+
def escape_name(name)
|
50
|
+
CodegenContext.new(nil).escape_name(name)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/emfrp/compile_error.rb
CHANGED
@@ -1,95 +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
|
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
|