hivemind 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 +22 -0
- data/Gemfile +7 -0
- data/LICENSE +20 -0
- data/README.md +42 -0
- data/Rakefile +18 -0
- data/bin/hivemind +52 -0
- data/hivemind.gemspec +57 -0
- data/lib/hivemind.rb +6 -0
- data/lib/hivemind/code_viewer.rb +41 -0
- data/lib/hivemind/combinators.rb +202 -0
- data/lib/hivemind/environment.rb +32 -0
- data/lib/hivemind/errors.rb +7 -0
- data/lib/hivemind/renderer.rb +71 -0
- data/lib/hivemind/runtime.rb +90 -0
- data/lib/hivemind/syntax.rb +404 -0
- data/lib/hivemind/universal_ast.rb +169 -0
- data/lib/hivemind/vm.rb +186 -0
- data/spec/hivemind/parser_spec.rb +0 -0
- data/spec/hivemind/universal_ast_spec.rb +22 -0
- data/spec/hivemind/vm_spec.rb +10 -0
- data/spec/spec_helper.rb +7 -0
- data/syntaxes/lolcode.syntax +34 -0
- data/syntaxes/paren.syntax +34 -0
- data/syntaxes/pythonic.syntax +36 -0
- metadata +112 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative 'errors'
|
2
|
+
|
3
|
+
module Hivemind
|
4
|
+
class Environment
|
5
|
+
attr_reader :parent, :values, :top
|
6
|
+
attr_accessor :current_self
|
7
|
+
|
8
|
+
def initialize(parent, **values)
|
9
|
+
@parent, @values, @top = parent, values, (parent.nil? ? values : parent.top)
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](key)
|
13
|
+
value = fetch key
|
14
|
+
return value if value
|
15
|
+
raise HivemindMissingNameError.new("#{key} is missing")
|
16
|
+
end
|
17
|
+
|
18
|
+
def fetch(key)
|
19
|
+
current = self
|
20
|
+
until current.nil? || current.values.key?(key)
|
21
|
+
current = current.parent
|
22
|
+
end
|
23
|
+
return current.values[key] unless current.nil?
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def []=(key, value)
|
28
|
+
@values[key] = value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require_relative 'syntax'
|
2
|
+
|
3
|
+
module Hivemind
|
4
|
+
|
5
|
+
BASE_RULES = {
|
6
|
+
image: -> element, depth = 0 do
|
7
|
+
element.statements.map { |s| render_element(s) }.join("\n")
|
8
|
+
end,
|
9
|
+
|
10
|
+
int: -> element, depth = 0 do
|
11
|
+
element.value.to_s
|
12
|
+
end,
|
13
|
+
|
14
|
+
float: -> element, depth = 0 do
|
15
|
+
element.value.to_s
|
16
|
+
end,
|
17
|
+
|
18
|
+
string: -> element, depth = 0 do
|
19
|
+
'"' + element.value.to_s + '"'
|
20
|
+
end,
|
21
|
+
|
22
|
+
name: -> element, depth = 0 do
|
23
|
+
element.value.to_s
|
24
|
+
end,
|
25
|
+
|
26
|
+
operation: -> element, depth = 0 do
|
27
|
+
element.value.to_s
|
28
|
+
end
|
29
|
+
}
|
30
|
+
|
31
|
+
class Renderer
|
32
|
+
def initialize(tree, syntax)
|
33
|
+
@tree, @syntax = tree, syntax
|
34
|
+
@rules = BASE_RULES.merge(Syntax.load_rules(syntax))
|
35
|
+
end
|
36
|
+
|
37
|
+
def render(depth = 0)
|
38
|
+
render_element(@tree, depth).gsub(/\n\n+/, "\n\n").gsub(/\)\s+\)/, '))').gsub(/\}\s+\}/, '}}')
|
39
|
+
end
|
40
|
+
|
41
|
+
def offset(depth = 0)
|
42
|
+
' ' * depth
|
43
|
+
end
|
44
|
+
|
45
|
+
def render_element(element, depth = 0)
|
46
|
+
rule = @rules[element.class.name.split('::').last.downcase.gsub('attributeassign', 'attribute_assign').gsub('statement', '_statement').to_sym]
|
47
|
+
depth += 1 if element.class.name.end_with?('MethodStatement')
|
48
|
+
# p "for #{element.class.name.split('::').last.downcase.gsub('statement', '_statement').to_sym} #{depth}"
|
49
|
+
offset(depth) + if rule.is_a?(String)
|
50
|
+
render_template rule, element, depth
|
51
|
+
elsif rule.is_a?(Proc)
|
52
|
+
instance_exec element, depth, &rule
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def render_template(plan, element, depth = 0)
|
57
|
+
plan = plan.gsub(/\<\<([a-zA-Z_]+)\:'([^\']*)'\>\>/) do
|
58
|
+
element.send(Regexp.last_match[1]).map(&method(:render_element)).join(Regexp.last_match[2])
|
59
|
+
end
|
60
|
+
# p plan
|
61
|
+
plan = plan.gsub(/\<\<([a-zA-Z_]+)\>\>/) do
|
62
|
+
element.send(Regexp.last_match[1]).map { |e| render_element(e, depth) }.join("\n")
|
63
|
+
end
|
64
|
+
p plan
|
65
|
+
plan = plan.gsub(/\<([a-zA-Z_]+)\>/) do
|
66
|
+
render_element(element.send(Regexp.last_match[1]))
|
67
|
+
end
|
68
|
+
plan
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require_relative 'environment'
|
2
|
+
|
3
|
+
module Hivemind
|
4
|
+
module Runtime
|
5
|
+
class HivemindObject
|
6
|
+
attr_reader :data, :klass
|
7
|
+
|
8
|
+
def initialize(data, klass)
|
9
|
+
@data, @klass = data, klass
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class HivemindClass
|
14
|
+
attr_reader :label, :methods, :parent
|
15
|
+
|
16
|
+
def initialize(label, parent = nil, methods = {})
|
17
|
+
@label, @parent, @methods = label, parent, methods
|
18
|
+
end
|
19
|
+
|
20
|
+
def define_hivemind_method(label, &handler)
|
21
|
+
@methods[label] = handler
|
22
|
+
end
|
23
|
+
|
24
|
+
def dispatch_method(label)
|
25
|
+
current = self
|
26
|
+
until current.nil? || current.methods.key?(label)
|
27
|
+
current = current.parent
|
28
|
+
end
|
29
|
+
!current ? nil : current.methods[label]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class HivemindModule
|
34
|
+
attr_reader :label, :elements
|
35
|
+
|
36
|
+
def initialize(label, elements = [])
|
37
|
+
@elements = elements
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.hivemind_string(value)
|
42
|
+
HivemindObject.new({_value: value}, HivemindEnv[:String])
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.hivemind_numbr(value)
|
46
|
+
HivemindObject.new({_value: value}, HivemindEnv[value.is_a?(Fixnum) ? :Int : :Float])
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.hivemind_object(data)
|
50
|
+
HivemindObject.new(data, HivemindEnv[:Object])
|
51
|
+
end
|
52
|
+
|
53
|
+
HivemindEnv = Environment.new(nil,
|
54
|
+
Object: HivemindClass.new('Object')
|
55
|
+
)
|
56
|
+
|
57
|
+
HivemindEnv[:Class] = HivemindClass.new('Class', HivemindEnv[:Object])
|
58
|
+
HivemindEnv[:String] = HivemindClass.new('String', HivemindEnv[:Object])
|
59
|
+
HivemindEnv[:Int] = HivemindClass.new('Int', HivemindEnv[:Object])
|
60
|
+
HivemindEnv[:Float] = HivemindClass.new('Float', HivemindEnv[:Object])
|
61
|
+
HivemindEnv[:Boolean] = HivemindClass.new('Boolean', HivemindEnv[:Object])
|
62
|
+
HivemindEnv[:@true] = HivemindObject.new({}, HivemindEnv[:Boolean])
|
63
|
+
HivemindEnv[:NilClass] = HivemindClass.new('NilClass', HivemindEnv[:Object])
|
64
|
+
HivemindEnv[:@nil] = HivemindObject.new({}, HivemindEnv[:NilClass])
|
65
|
+
|
66
|
+
HivemindEnv[:Object].define_hivemind_method(:display) do |hivemind_self, *args, env|
|
67
|
+
puts hivemind_self.call(hivemind_self.klass.dispatch_method(:to_string), args, env).data[:_value]
|
68
|
+
end
|
69
|
+
|
70
|
+
HivemindEnv[:Object].define_hivemind_method(:to_string) do |hivemind_self, *args, env|
|
71
|
+
# p hivemind_self
|
72
|
+
if [HivemindEnv[:Int], HivemindEnv[:Float], HivemindEnv[:String], HivemindEnv[:Boolean]].include? hivemind_self.klass
|
73
|
+
hivemind_string(hivemind_self.data[:_value])
|
74
|
+
elsif hivemind_self.klass == HivemindEnv[:NilClass]
|
75
|
+
hivemind_string('null')
|
76
|
+
else
|
77
|
+
y = ''
|
78
|
+
i = 0
|
79
|
+
y2 = []
|
80
|
+
hivemind_self.data.each do |key, value|
|
81
|
+
y2 << key.to_s + ':' + value.call(value.klass.dispatch_method(:to_string), [], env).data[:_value].to_s
|
82
|
+
end
|
83
|
+
y = y2.join(', ')
|
84
|
+
hivemind_string("{#{y}}")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
|
@@ -0,0 +1,404 @@
|
|
1
|
+
require_relative 'combinators'
|
2
|
+
|
3
|
+
module Hivemind
|
4
|
+
|
5
|
+
# BaseGrammar = Phoenix::Grammar.new
|
6
|
+
# BaseGrammar.rules = {
|
7
|
+
# segment: many(ref(:keyword_structure), as: :elements),
|
8
|
+
# keyword_structure: ref(:module) | ref(:class) | ref(:method),
|
9
|
+
# method: lit('module') & some(ws) & ref(:name, as: :module_name) &
|
10
|
+
# many(ref(:expr), as: :body),
|
11
|
+
# expr: ref(:if) | ref(:sequence) | ref(:attribute) | ref(:attribute_assign) |
|
12
|
+
# ref(:assign) | ref(:literal),
|
13
|
+
# sequence: ref(:list) | ref(:dictionary),
|
14
|
+
# list: lit('[') & join(ref(:expr), ', ', as: :elements) & lit(']'),
|
15
|
+
# dictionary: lit('{') & join(ref(:pair), ', ', as: :elements) & lit('}'),
|
16
|
+
# pair: ref(:expr, as: :key) & some(ws) & '=>' & some(ws) & ref(:expr, as: :value),
|
17
|
+
# attribute: ref(:expr, as: :object, except: :attribute) & '.' & ref(:name, as: :label),
|
18
|
+
# attribute_assign: ref(:expr, as: :object, except: :attribute) & '.' & ref(:name, as: :label) & some(ws) & lit('=') & some(ws) & ref(:expr, as: :right),
|
19
|
+
# assign: ref(:name, as: :left) & some(ws) & lit('=') & some(ws) & ref(:expr, as: :right),
|
20
|
+
# literal: ref(:string) | ref(:boolean) | ref(:nil_literal) | ref(:number) | ref(:name)
|
21
|
+
# name: regex(/[a-zA-Z_]+/, as: :value),
|
22
|
+
# string: lit('"') & regex(/[^"]*/, as: :value) + lit('"'),
|
23
|
+
# number: regex(/[0-9]+(\.[0-9]+)?/, as: :value),
|
24
|
+
# boolean: ref(:true_literal) | ref(:false_literal),
|
25
|
+
# true_literal: 'true',
|
26
|
+
# false_literal: 'false',
|
27
|
+
# nil_literal: 'nil',
|
28
|
+
# :if => lit('if') & some(ws) & ref(:expr, as: :test) & lit(':') & nl & indent & join(ref(:expr), "\n", as: :true_branch) &
|
29
|
+
# nl & dedent & lit('else') & lit(':') & nl & indent & join(ref(:expr), "\n", as: :else_branch) & nl & dedent,
|
30
|
+
# :class => lit('class') & some(ws) & ref(:name, as: :class_name) &
|
31
|
+
# many(ref(:ex)
|
32
|
+
|
33
|
+
# }
|
34
|
+
|
35
|
+
|
36
|
+
TYPES = {
|
37
|
+
assign: {
|
38
|
+
left: :name,
|
39
|
+
right: :expr
|
40
|
+
},
|
41
|
+
|
42
|
+
attribute: {
|
43
|
+
object: :expr_no_attr,
|
44
|
+
label: :name_or_attr
|
45
|
+
},
|
46
|
+
|
47
|
+
image: {
|
48
|
+
statements: :class_statement
|
49
|
+
},
|
50
|
+
|
51
|
+
binary: {
|
52
|
+
left: :expr_no_binary,
|
53
|
+
operation: :operation,
|
54
|
+
right: :expr
|
55
|
+
},
|
56
|
+
|
57
|
+
attribute_assign: {
|
58
|
+
object: :expr_no_attr,
|
59
|
+
label: :name_or_attr,
|
60
|
+
right: :expr
|
61
|
+
},
|
62
|
+
|
63
|
+
call: {
|
64
|
+
function: :expr_no_call,
|
65
|
+
args: :expr
|
66
|
+
},
|
67
|
+
|
68
|
+
list: {
|
69
|
+
elements: :expr
|
70
|
+
},
|
71
|
+
|
72
|
+
dictionary: {
|
73
|
+
pair: :pair
|
74
|
+
},
|
75
|
+
|
76
|
+
pair: {
|
77
|
+
key: :string,
|
78
|
+
value: :expr
|
79
|
+
},
|
80
|
+
|
81
|
+
method_statement: {
|
82
|
+
method_name: :name,
|
83
|
+
args: :name,
|
84
|
+
body: :expr
|
85
|
+
},
|
86
|
+
|
87
|
+
class_statement: {
|
88
|
+
class_name: :name,
|
89
|
+
methods: :method_statement
|
90
|
+
},
|
91
|
+
|
92
|
+
module_statement: {
|
93
|
+
module_name: :name,
|
94
|
+
elements: :statement
|
95
|
+
},
|
96
|
+
|
97
|
+
if_statement: {
|
98
|
+
test: :expr,
|
99
|
+
true_branch: :expr,
|
100
|
+
else_branch: :expr
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
class Syntax
|
105
|
+
def self.generate_syntax(bidirectional_grammar)
|
106
|
+
new(bidirectional_grammar).generate
|
107
|
+
end
|
108
|
+
|
109
|
+
def initialize(grammar)
|
110
|
+
@grammar_source = grammar
|
111
|
+
end
|
112
|
+
|
113
|
+
def generate
|
114
|
+
# parse grammar
|
115
|
+
# combine into base grammar
|
116
|
+
rules = self.class.load_rules(@grammar_source)
|
117
|
+
refs = {}
|
118
|
+
|
119
|
+
rules.each do |name, rule|
|
120
|
+
refs[:"_#{name}"] = parse_rule rule, TYPES[name]
|
121
|
+
end
|
122
|
+
|
123
|
+
[REFS[:image], REFS.merge(refs)]
|
124
|
+
end
|
125
|
+
|
126
|
+
def parse_rule(rule, types)
|
127
|
+
parsers = []
|
128
|
+
tokens = []
|
129
|
+
token = ''
|
130
|
+
i = 0
|
131
|
+
while i < rule.length
|
132
|
+
z = rule[i]
|
133
|
+
if '<>'.include?(z)
|
134
|
+
tokens << token unless token.empty?
|
135
|
+
token = ''
|
136
|
+
if z == '>'
|
137
|
+
if rule[i + 1] == '>'
|
138
|
+
tokens << '>>'
|
139
|
+
i += 1
|
140
|
+
else
|
141
|
+
tokens << '>'
|
142
|
+
end
|
143
|
+
elsif z == '<'
|
144
|
+
if rule[i + 1] == '<'
|
145
|
+
tokens << '<<'
|
146
|
+
i += 1
|
147
|
+
else
|
148
|
+
tokens << '<'
|
149
|
+
end
|
150
|
+
end
|
151
|
+
elsif z == "'"
|
152
|
+
tokens << token unless token.empty?
|
153
|
+
token = ''
|
154
|
+
j = i
|
155
|
+
i += 1
|
156
|
+
while rule[i] != "'"
|
157
|
+
i += 1
|
158
|
+
end
|
159
|
+
tokens << rule[j..i]
|
160
|
+
elsif z == ' '
|
161
|
+
tokens << token unless token.empty?
|
162
|
+
token = ''
|
163
|
+
j = i
|
164
|
+
while rule[i] == ' '
|
165
|
+
i += 1
|
166
|
+
end
|
167
|
+
tokens << rule[j..i - 1]
|
168
|
+
i -= 1
|
169
|
+
elsif z == "\n"
|
170
|
+
tokens << token unless token.empty?
|
171
|
+
tokens << "\n"
|
172
|
+
elsif z.match /[a-zA-Z_0-9]/
|
173
|
+
token += z
|
174
|
+
else
|
175
|
+
tokens << token unless token.empty?
|
176
|
+
token = ''
|
177
|
+
tokens << z
|
178
|
+
end
|
179
|
+
i += 1
|
180
|
+
end
|
181
|
+
tokens << token unless token.empty?
|
182
|
+
|
183
|
+
r = 0
|
184
|
+
in_var = false
|
185
|
+
|
186
|
+
tokens.each_with_index do |token, i|
|
187
|
+
if token == '>>'
|
188
|
+
if tokens[i - 2] == ':'
|
189
|
+
parsers << Join.new(
|
190
|
+
Ref.new(types[tokens[i - 3].to_sym]),
|
191
|
+
tokens[i - 1][1..-2], as: tokens[i - 3])
|
192
|
+
else
|
193
|
+
parsers << Join.new(Ref.new(types[tokens[i - 1].to_sym]), "\n#{' ' * r}", as: tokens[i - 1])
|
194
|
+
#Many.new(Ref.new(types[tokens[i - 1].to_sym]), as: tokens[i - 1])
|
195
|
+
end
|
196
|
+
in_var = false
|
197
|
+
elsif token == '>'
|
198
|
+
parsers << Ref.new(types[tokens[i - 1].to_sym])
|
199
|
+
in_var = false
|
200
|
+
elsif token == "\n"
|
201
|
+
parsers << Ref.new(:nl)
|
202
|
+
if tokens[i + 1] == "\n"
|
203
|
+
e = 2
|
204
|
+
elsif tokens[i + 1]
|
205
|
+
match = tokens[i + 1].match /([ ]+)/
|
206
|
+
if match.nil? || match.captures.empty?
|
207
|
+
indent = 0
|
208
|
+
else
|
209
|
+
indent = match.captures.first.size / 4
|
210
|
+
end
|
211
|
+
if indent > r
|
212
|
+
parsers << Ref.new(:indent)
|
213
|
+
elsif indent < r
|
214
|
+
parsers << Ref.new(:dedent)
|
215
|
+
end
|
216
|
+
r = indent
|
217
|
+
end
|
218
|
+
elsif token.match(/\A +\z/)
|
219
|
+
parsers << Ref.new(:ws)
|
220
|
+
elsif (token == '<<' || token == '<') && tokens[i + 1] >= 'a' && tokens[i + 1] <= 'z'
|
221
|
+
in_var = true
|
222
|
+
elsif !in_var
|
223
|
+
parsers << Lit.new(token)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
# parsers.map { |pa| puts pa.inspect }
|
227
|
+
parsers.reduce(:&)
|
228
|
+
end
|
229
|
+
|
230
|
+
def self.load_rules(grammar)
|
231
|
+
lines = grammar.split("\n")
|
232
|
+
rules = {}
|
233
|
+
current_rule = nil
|
234
|
+
rule_body = []
|
235
|
+
lines.each do |line|
|
236
|
+
if line.start_with? '#'
|
237
|
+
if not current_rule.nil?
|
238
|
+
rule_body << "\n" if rule_body.length > 1
|
239
|
+
rules[current_rule.to_sym] = rule_body.join("\n")
|
240
|
+
rule_body = []
|
241
|
+
end
|
242
|
+
current_rule = line[1..-1].strip
|
243
|
+
elsif line.strip != ''
|
244
|
+
rule_body << line
|
245
|
+
end
|
246
|
+
end
|
247
|
+
rule_body << "\n" if rule_body.length > 1
|
248
|
+
rules[current_rule.to_sym] = rule_body.join("\n")
|
249
|
+
rules
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
Name = UniversalAST::Name
|
254
|
+
Number = UniversalAST::Number
|
255
|
+
Assign = UniversalAST::Assign
|
256
|
+
Element = UniversalAST::Element
|
257
|
+
Call = UniversalAST::Call
|
258
|
+
List = UniversalAST::List
|
259
|
+
Dictionary = UniversalAST::Dictionary
|
260
|
+
Pair = UniversalAST::Pair
|
261
|
+
Attribute = UniversalAST::Attribute
|
262
|
+
AttributeAssign = UniversalAST::AttributeAssign
|
263
|
+
IfStatement = UniversalAST::IfStatement
|
264
|
+
MethodStatement = UniversalAST::MethodStatement
|
265
|
+
ClassStatement = UniversalAST::ClassStatement
|
266
|
+
Image = UniversalAST::Image
|
267
|
+
Operation = UniversalAST::Operation
|
268
|
+
Float = UniversalAST::Float
|
269
|
+
Int = UniversalAST::Int
|
270
|
+
|
271
|
+
REFS = {
|
272
|
+
name: Apply.new(Mat.new(/[a-zA-Z][a-zA-Z_]*/)) do |result|
|
273
|
+
Name.new(result.to_sym)
|
274
|
+
end,
|
275
|
+
|
276
|
+
image: Apply.new(Join.new(Ref.new(:class_statement), "", as: :statements)) do |children|
|
277
|
+
# d = children.select { |child| child.is_a?(MethodStatement) }
|
278
|
+
e = children.select { |child| child.is_a?(ClassStatement) }
|
279
|
+
# obj = e.find { |element| element.is_a?(ClassStatement) && element.class_name == :Object }
|
280
|
+
# p e[0].class_name
|
281
|
+
# if obj.nil? && !d.empty?
|
282
|
+
# obj = ClassStatement.new(Name.new(:Object), d)
|
283
|
+
# e << obj
|
284
|
+
# elsif obj
|
285
|
+
# obj.methods += d
|
286
|
+
# end
|
287
|
+
Image.new(e)
|
288
|
+
end,
|
289
|
+
|
290
|
+
statement: Ref.new(:module_statement) | Ref.new(:class_statement) | Ref.new(:method_statement),
|
291
|
+
|
292
|
+
number: Ref.new(:float) | Ref.new(:int),
|
293
|
+
|
294
|
+
float: Apply.new(Mat.new(/[0-9]+\.[0-9]+/)) do |result|
|
295
|
+
Float.new(result.to_f)
|
296
|
+
end,
|
297
|
+
|
298
|
+
int: Apply.new(Mat.new(/[0-9]+/)) do |result|
|
299
|
+
Int.new(result.to_i)
|
300
|
+
end,
|
301
|
+
|
302
|
+
string: Apply.new(Mat.new(/\"[^\"]*\"/)) do |result|
|
303
|
+
String.new(result[1..-2])
|
304
|
+
end,
|
305
|
+
|
306
|
+
ws: Mat.new(/ +/),
|
307
|
+
|
308
|
+
nl: Mat.new(/\n*/),
|
309
|
+
|
310
|
+
indent: Lit.new(''),
|
311
|
+
|
312
|
+
dedent: Lit.new(''),
|
313
|
+
|
314
|
+
expr: Ref.new(:attribute_assign) | Ref.new(:assign) | Ref.new(:binary) | Ref.new(:call) | Ref.new(:attribute) | Ref.new(:number) | Ref.new(:name) | Ref.new(:string),
|
315
|
+
|
316
|
+
expr_no_attr: Ref.new(:number) | Ref.new(:nil) | Ref.new(:name) | Ref.new(:string),
|
317
|
+
|
318
|
+
expr_no_call: Ref.new(:binary) | Ref.new(:attribute) | Ref.new(:number) | Ref.new(:name) | Ref.new(:string),
|
319
|
+
|
320
|
+
nil: Lit.new('nil'),
|
321
|
+
|
322
|
+
name_or_attr: Ref.new(:name) | Ref.new(:attribute),
|
323
|
+
|
324
|
+
assign: Apply.new(Ref.new(:_assign)) do |results|
|
325
|
+
Assign.new(*results.select { |r| r.is_a?(Element) })
|
326
|
+
end,
|
327
|
+
|
328
|
+
attribute_assign: Apply.new(Ref.new(:_attribute_assign)) do |results|
|
329
|
+
AttributeAssign.new(*results.select { |r| r.is_a?(Element) })
|
330
|
+
end,
|
331
|
+
|
332
|
+
call: Apply.new(Ref.new(:_call)) do |results|
|
333
|
+
function, args = results.select { |r| r.is_a?(Element) || r.is_a?(Array) }
|
334
|
+
Call.new(function, args)
|
335
|
+
end,
|
336
|
+
|
337
|
+
list: Apply.new(Ref.new(:_list)) do |results|
|
338
|
+
List.new(results[1])
|
339
|
+
end,
|
340
|
+
|
341
|
+
dictionary: Apply.new(Ref.new(:_dictionary)) do |results|
|
342
|
+
Dictionary.new(results[1])
|
343
|
+
end,
|
344
|
+
|
345
|
+
pair: Apply.new(Ref.new(:_pair)) do |results|
|
346
|
+
key, value = results.select { |r| r.is_a?(Element) }
|
347
|
+
Pair.new(key, value)
|
348
|
+
end,
|
349
|
+
|
350
|
+
binary: Apply.new(Ref.new(:_binary)) do |results|
|
351
|
+
if results[0].is_a?(String)
|
352
|
+
results = results[1..-1]
|
353
|
+
end
|
354
|
+
# detect operation intelligently
|
355
|
+
tokens = results[0], results[2], results[4]
|
356
|
+
if tokens[0].is_a?(UniversalAST::Operation)
|
357
|
+
operation, left, right = tokens
|
358
|
+
elsif tokens[1].is_a?(UniversalAST::Operation)
|
359
|
+
left, operation, right = tokens
|
360
|
+
else
|
361
|
+
left, right, operation = tokens
|
362
|
+
end
|
363
|
+
# p results
|
364
|
+
UniversalAST::Binary.new(left, operation, right)
|
365
|
+
end,
|
366
|
+
|
367
|
+
expr_no_binary: Ref.new(:attribute) | Ref.new(:number) | Ref.new(:name) | Ref.new(:string),
|
368
|
+
|
369
|
+
operation: Apply.new(Lit.new('+') | Lit.new('-') | Lit.new('**') | Lit.new('/') | Lit.new('*') | Lit.new('||')) do |result|
|
370
|
+
Operation.new(result)
|
371
|
+
end,
|
372
|
+
|
373
|
+
attribute: Apply.new(Ref.new(:_attribute)) do |results|
|
374
|
+
object, label = results.select { |r| r.is_a?(Element) }
|
375
|
+
Attribute.new(object, label)
|
376
|
+
end,
|
377
|
+
|
378
|
+
if_statement: Apply.new(Ref.new(:_if_statement)) do |results|
|
379
|
+
test, true_branch, else_branch = results.select { |r| r.is_a?(Element) || r.is_a?(Array) }
|
380
|
+
IfStatement.new(test, true_branch, else_branch)
|
381
|
+
end,
|
382
|
+
|
383
|
+
method_statement: Apply.new(Ref.new(:_method_statement)) do |results|
|
384
|
+
method_name, args, body = results.select { |r| r.is_a?(Element) || r.is_a?(Array) }
|
385
|
+
MethodStatement.new(method_name, args, body)
|
386
|
+
end,
|
387
|
+
|
388
|
+
class_statement: Apply.new(Ref.new(:_class_statement)) do |results|
|
389
|
+
class_name, methods = results.select { |r| r.is_a?(Element) || r.is_a?(Array) }
|
390
|
+
ClassStatement.new(class_name, methods)
|
391
|
+
end,
|
392
|
+
|
393
|
+
module_statement: Apply.new(Ref.new(:_module_statement)) do |results|
|
394
|
+
module_name, classes = results.select { |r| r.is_a?(Element) || r.is_a?(Array) }
|
395
|
+
ModuleStatement.new(module_name, classes)
|
396
|
+
end
|
397
|
+
}
|
398
|
+
|
399
|
+
end
|
400
|
+
|
401
|
+
|
402
|
+
|
403
|
+
|
404
|
+
|