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,169 @@
|
|
1
|
+
module Hivemind
|
2
|
+
module UniversalAST
|
3
|
+
class Element
|
4
|
+
def self.fields(*labels)
|
5
|
+
define_method(:initialize) do |*args|
|
6
|
+
args.zip(labels).each do |arg, label|
|
7
|
+
instance_variable_set "@#{label}", arg
|
8
|
+
end
|
9
|
+
end
|
10
|
+
attr_reader *labels
|
11
|
+
end
|
12
|
+
|
13
|
+
def offset(depth)
|
14
|
+
' ' * depth
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class IfStatement < Element
|
19
|
+
# if <test>:
|
20
|
+
# <<true-branch>>
|
21
|
+
# else:
|
22
|
+
# <<else-branch>>
|
23
|
+
|
24
|
+
fields :test, :true_branch, :else_branch
|
25
|
+
|
26
|
+
def render(depth = 0)
|
27
|
+
"#{offset(depth)}If\n#{offset(depth + 1)}#{@test.render(depth + 1)}\n"
|
28
|
+
"#{@true_branch.render(depth + 1)}\n#{@else_branch.render(depth + 1)}\n"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Assign < Element
|
33
|
+
# <left> = <right>
|
34
|
+
|
35
|
+
fields :left, :right
|
36
|
+
|
37
|
+
def render(depth = 0)
|
38
|
+
"#{offset(depth)}Assign left: #{@left.render} right: #{@right.render}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class Attribute < Element
|
43
|
+
# <object>.<label>
|
44
|
+
|
45
|
+
fields :object, :label
|
46
|
+
|
47
|
+
def render(depth = 0)
|
48
|
+
"#{offset(depth)}Attribute : #{@object.render} #{@label.render}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class AttributeAssign < Element
|
53
|
+
# <object>.<label> = <right>
|
54
|
+
|
55
|
+
fields :object, :label, :right
|
56
|
+
|
57
|
+
def render(depth = 0)
|
58
|
+
"#{offset(depth)}AttributeAssign : #{@object.render} #{@label.render} #{@right.render}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Call < Element
|
63
|
+
# <function>(<<args:', '>>)
|
64
|
+
|
65
|
+
fields :function, :args
|
66
|
+
|
67
|
+
def render(depth = 0)
|
68
|
+
"#{offset(depth)}Call\n#{@function.render(depth + 1)}\n#{offset(depth + 1)}#{@args.map(&:render).join(' ')}\n"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class List < Element
|
73
|
+
# [el1, el2..]
|
74
|
+
|
75
|
+
fields :elements
|
76
|
+
|
77
|
+
def render(depth = 0)
|
78
|
+
"#{offset(depth)}List\n#{@elements.map { |e| e.render(depth + 1) }.join("\n")}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class Dictionary < Element
|
83
|
+
# {key1: val1, key2: val2..}
|
84
|
+
|
85
|
+
fields :pairs
|
86
|
+
end
|
87
|
+
|
88
|
+
class Binary < Element
|
89
|
+
# <left> <operation> <right>
|
90
|
+
|
91
|
+
fields :left, :operation, :right
|
92
|
+
|
93
|
+
def render(depth = 0)
|
94
|
+
"#{offset(depth)}Binary #{@left.render} #{@operation.value} #{@right.render}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class MethodStatement < Element
|
99
|
+
# method <method-name>(<<args:', '):
|
100
|
+
# <<body>>
|
101
|
+
|
102
|
+
fields :method_name, :args, :body
|
103
|
+
|
104
|
+
def render(depth = 0)
|
105
|
+
"#{offset(depth)}MethodStatement #{@method_name.value} #{@args.map(&:render).join(' ')}\n" +
|
106
|
+
"#{@body.map { |e| e.render(depth + 1) }.join("\n")}\n"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class ClassStatement < Element
|
111
|
+
# type <class-name>:
|
112
|
+
# <<methods>>
|
113
|
+
|
114
|
+
fields :class_name, :methods
|
115
|
+
|
116
|
+
def render(depth = 0)
|
117
|
+
"#{offset(depth)}ClassStatement #{@class_name.value}\n" +
|
118
|
+
"#{@methods.map { |e| e.render(depth + 1) }.join("\n")}\n"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
class Value < Element
|
123
|
+
fields :value
|
124
|
+
|
125
|
+
def render(depth = 0)
|
126
|
+
"#{offset(depth)}#{@value}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
class Name < Value
|
131
|
+
end
|
132
|
+
|
133
|
+
class String < Value
|
134
|
+
end
|
135
|
+
|
136
|
+
class Number < Value
|
137
|
+
end
|
138
|
+
|
139
|
+
class Int < Number
|
140
|
+
end
|
141
|
+
|
142
|
+
class Float < Number
|
143
|
+
end
|
144
|
+
|
145
|
+
class Operation < Value
|
146
|
+
end
|
147
|
+
|
148
|
+
class ModuleStatement < Element
|
149
|
+
# module <module-name>:
|
150
|
+
# <<children>>
|
151
|
+
|
152
|
+
fields :module_name, :elements
|
153
|
+
end
|
154
|
+
|
155
|
+
class Pair < Element
|
156
|
+
# key => value
|
157
|
+
fields :key, :value
|
158
|
+
end
|
159
|
+
|
160
|
+
class Image < Element
|
161
|
+
fields :statements
|
162
|
+
|
163
|
+
def render(depth = 0)
|
164
|
+
@statements.map(&:render).join "\n"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
data/lib/hivemind/vm.rb
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
require_relative 'runtime'
|
2
|
+
require_relative 'universal_ast'
|
3
|
+
|
4
|
+
module Hivemind
|
5
|
+
class VM
|
6
|
+
def initialize(ast)
|
7
|
+
@ast = ast
|
8
|
+
end
|
9
|
+
|
10
|
+
def run(env)
|
11
|
+
@ast.run env
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Runtime::HivemindObject
|
16
|
+
def call(function, args, env)
|
17
|
+
if function.is_a?(UniversalAST::MethodStatement)
|
18
|
+
args_values = {:self => self}
|
19
|
+
function.args[1..-1].zip(args) do |label, arg|
|
20
|
+
args_values[label.value.to_sym] = arg
|
21
|
+
end
|
22
|
+
body_env = Environment.new(env, **args_values)
|
23
|
+
function.body.map { |expr| expr.run(body_env) }[-1] || env.top[:@nil]
|
24
|
+
else
|
25
|
+
function.call self, *args, env
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Runtime::HivemindClass
|
31
|
+
def call(function, args, env)
|
32
|
+
h = Runtime::HivemindObject.new({}, self)
|
33
|
+
function = dispatch_method(:init)
|
34
|
+
if function.is_a?(UniversalAST::MethodStatement)
|
35
|
+
args_values = {:self => h}
|
36
|
+
function.args[1..-1].zip(args) do |label, arg|
|
37
|
+
args_values[label.value.to_sym] = arg
|
38
|
+
end
|
39
|
+
body_env = Environment.new(env, **args_values)
|
40
|
+
function.body.map { |expr| expr.run(body_env) }[-1] || env.top[:@nil]
|
41
|
+
else
|
42
|
+
function.call h, *args, env
|
43
|
+
end
|
44
|
+
h
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module UniversalAST
|
49
|
+
class Image
|
50
|
+
def run(env)
|
51
|
+
@statements.each do |statement|
|
52
|
+
statement.run(env)
|
53
|
+
end
|
54
|
+
# puts env.top[:Object].methods.keys
|
55
|
+
if env.top[:Object].methods.key? :start
|
56
|
+
weird_object = Runtime::hivemind_object({})
|
57
|
+
weird_object.call(env.top[:Object].methods[:start], [], env)
|
58
|
+
else
|
59
|
+
env.top[:@nil]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class ModuleStatement
|
65
|
+
def run(env)
|
66
|
+
module_statement = Runtime::HivemindModule.new(@module_name)
|
67
|
+
@statements.each do |statement|
|
68
|
+
module_statement.elements[@statement.is_a?(ModuleStatement) ? @statement.module_name : @statement.class_name] =
|
69
|
+
statement.run(env)
|
70
|
+
end
|
71
|
+
env
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class If
|
76
|
+
def run(env)
|
77
|
+
if @test.run(env) == env.top[:@true]
|
78
|
+
@true_branch.run env
|
79
|
+
else
|
80
|
+
@else_branch.run env
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Assign
|
86
|
+
def run(env)
|
87
|
+
env[@left.value.to_sym] = @right.run(env)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Attribute
|
92
|
+
def run(env)
|
93
|
+
obj = @object.run(env)
|
94
|
+
env.current_self = obj
|
95
|
+
|
96
|
+
if obj.respond_to?(:data)
|
97
|
+
if obj.data.key? @label.value
|
98
|
+
obj.data[@label.value]
|
99
|
+
else
|
100
|
+
method = obj.klass.dispatch_method(@label.value)
|
101
|
+
if method
|
102
|
+
method
|
103
|
+
else
|
104
|
+
raise HivemindAccessError.new("No #{@label.value} in obj")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
else
|
108
|
+
obj.methods[@label.value]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class AttributeAssign
|
114
|
+
def run(env)
|
115
|
+
@object.run(env).data[@label.value] = @right.run(env)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class Call
|
120
|
+
def run(env)
|
121
|
+
if !@function.is_a?(Attribute)
|
122
|
+
function = @function.run(env)
|
123
|
+
env.current_self.call(function, @args.map { |arg| arg.run(env) }, env)
|
124
|
+
elsif @function.label.value != :new
|
125
|
+
obj = @function.object.run(env)
|
126
|
+
function = obj.klass.dispatch_method(@function.label.value)
|
127
|
+
obj.call(function, @args.map { |arg| arg.run(env) }, env)
|
128
|
+
else
|
129
|
+
obj = @function.object.run(env)
|
130
|
+
function == obj.dispatch_method(:init)
|
131
|
+
obj.call(function, @args.map { |arg| arg.run(env) }, env)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
class Binary
|
137
|
+
def run(env)
|
138
|
+
Runtime::hivemind_numbr(@left.run(env).data[:_value].send(@operation.value, @right.run(env).data[:_value]))
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
class List
|
143
|
+
def run(env)
|
144
|
+
Runtime::HivemindObject.new({_elements: @elements.map { |elem| elem.run(env) }}, env.top[:List])
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
class Dictionary
|
149
|
+
def run(env)
|
150
|
+
dict = {}
|
151
|
+
@pairs.each do |pair|
|
152
|
+
dict[pair.key.value.to_sym] = pair.value.run(env)
|
153
|
+
end
|
154
|
+
Runtime::HivemindObject.new({_dict: dict}, env.top[:Dict])
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
class Value
|
159
|
+
def run(env)
|
160
|
+
Runtime::HivemindObject.new({_value: @value}, env.top[self.class.name.split('::').last.to_sym])
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class ClassStatement
|
165
|
+
def run(env)
|
166
|
+
definition = env.fetch(@class_name.value) || Runtime::HivemindClass.new(@class_name.value, env.top[:Object], {})
|
167
|
+
@methods.each do |method|
|
168
|
+
definition.methods[method.method_name.value] = method
|
169
|
+
end
|
170
|
+
env[@class_name.value] = definition
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
class MethodStatement
|
175
|
+
def run(env)
|
176
|
+
self
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
class Name
|
181
|
+
def run(env)
|
182
|
+
env[@value]
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
File without changes
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Hivemind
|
4
|
+
module UniversalAST
|
5
|
+
describe Element do
|
6
|
+
it 'fields initializes a class with given labels' do
|
7
|
+
class A < Element
|
8
|
+
fields :a
|
9
|
+
end
|
10
|
+
|
11
|
+
expect(A.new(2).a).to eq(2)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe ModuleStatement do
|
16
|
+
it 'is initialized with an elements attribute' do
|
17
|
+
mod = ModuleStatement.new('ha', [])
|
18
|
+
expect(mod.elements).to eq([])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#if_statement
|
2
|
+
<test> O RLY
|
3
|
+
YA RLY
|
4
|
+
<<true_branch>>
|
5
|
+
NO WAI
|
6
|
+
<<else_branch>>
|
7
|
+
|
8
|
+
|
9
|
+
#assign
|
10
|
+
DO <left> <right>
|
11
|
+
|
12
|
+
#method_statement
|
13
|
+
HOW DUZ I <method_name>(<<args:' '>>)
|
14
|
+
<<body>>
|
15
|
+
IF U SAY SO
|
16
|
+
|
17
|
+
#attribute
|
18
|
+
<object> HEY <label>
|
19
|
+
|
20
|
+
#attribute_assign
|
21
|
+
<object> BE <label> <right>
|
22
|
+
|
23
|
+
#call
|
24
|
+
GIMME <function> (<<args:' '>>)
|
25
|
+
|
26
|
+
#class_statement
|
27
|
+
TOM SAYS <class_name>
|
28
|
+
<<methods>>
|
29
|
+
LOVE
|
30
|
+
|
31
|
+
#module_statement
|
32
|
+
HAI <module_name>
|
33
|
+
<<elements>>
|
34
|
+
KTHXBYE
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#if_statement
|
2
|
+
(if <test>
|
3
|
+
<true_branch>
|
4
|
+
<else_branch>)
|
5
|
+
|
6
|
+
#assign
|
7
|
+
(define <left> <right>)
|
8
|
+
|
9
|
+
#method_statement
|
10
|
+
(method <method_name> (<<args:' '>>)
|
11
|
+
<<body>>)
|
12
|
+
|
13
|
+
#attribute
|
14
|
+
<object>.<label>
|
15
|
+
|
16
|
+
#attribute_assign
|
17
|
+
(update <object>.<label> <right>)
|
18
|
+
|
19
|
+
#binary
|
20
|
+
(<operation> <left> <right>)
|
21
|
+
|
22
|
+
#call
|
23
|
+
(! <function> <<args:' '>>)
|
24
|
+
|
25
|
+
#list
|
26
|
+
_(<<elements:' '>>)
|
27
|
+
|
28
|
+
#class_statement
|
29
|
+
(class <class_name>
|
30
|
+
<<methods>>)
|
31
|
+
|
32
|
+
#module_statement
|
33
|
+
(module <module_name>
|
34
|
+
<<elements>>)
|