sardonyx 0.1.5
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/bin/sdx +32 -0
- data/lib/sdx/compiler/compiler.rb +183 -0
- data/lib/sdx/compiler/parser.rb +522 -0
- data/lib/sdx/vm/datatypes.rb +410 -0
- data/lib/sdx/vm/scope.rb +60 -0
- data/lib/sdx/vm/variables.rb +19 -0
- data/lib/sdx/vm/vm.rb +385 -0
- metadata +51 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class Variable
|
|
2
|
+
@mut = false
|
|
3
|
+
attr_reader :type, :value, :scope
|
|
4
|
+
|
|
5
|
+
def initialize(val, type, scope)
|
|
6
|
+
@value = val
|
|
7
|
+
@type = type
|
|
8
|
+
@scope = scope
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def set(val)
|
|
12
|
+
if @mut == true
|
|
13
|
+
@value = val
|
|
14
|
+
else
|
|
15
|
+
puts "cant change value of immutable" # add error handling
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
data/lib/sdx/vm/vm.rb
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
require 'sdx/vm/variables'
|
|
2
|
+
require 'sdx/vm/datatypes'
|
|
3
|
+
require 'sdx/vm/scope'
|
|
4
|
+
|
|
5
|
+
def stringify(val)
|
|
6
|
+
if val.value.fields["__as_string"]
|
|
7
|
+
(val.value.fields["__as_string"].call).internal
|
|
8
|
+
else
|
|
9
|
+
val.value.to_s
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def codify(val)
|
|
14
|
+
if val.value.fields["__as_code_string"]
|
|
15
|
+
(val.value.fields["__as_code_string"].call).internal
|
|
16
|
+
else
|
|
17
|
+
val.value.pretty_inspect
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class VM
|
|
22
|
+
attr_accessor :bc_io
|
|
23
|
+
|
|
24
|
+
def truthy(val)
|
|
25
|
+
if val.value.fields["__as_bool"]
|
|
26
|
+
(val.value.fields["__as_bool"].call).internal
|
|
27
|
+
else
|
|
28
|
+
true
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def call(val, *args)
|
|
33
|
+
if val.respond_to? :value and val.value.respond_to? :fields
|
|
34
|
+
case val.value
|
|
35
|
+
when Function
|
|
36
|
+
return val.value.fields["__call"].call args.map { |x| to_var x }, val.scope
|
|
37
|
+
else
|
|
38
|
+
return val.value.fields["__call"].call *args
|
|
39
|
+
end
|
|
40
|
+
elsif val.respond_to? :fields
|
|
41
|
+
return val.fields["__call"].call *args
|
|
42
|
+
else
|
|
43
|
+
return (to_var val).value.call *args
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def callable(val)
|
|
48
|
+
if val.respond_to? :value and val.value.respond_to? :fields
|
|
49
|
+
if val.value.fields["__call"] and val.value.fields["__arity"]
|
|
50
|
+
return true
|
|
51
|
+
end
|
|
52
|
+
return false
|
|
53
|
+
elsif val.respond_to? :fields
|
|
54
|
+
if val.fields["__call"] and val.fields["__arity"]
|
|
55
|
+
return true
|
|
56
|
+
end
|
|
57
|
+
return false
|
|
58
|
+
else
|
|
59
|
+
return true
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def arity(val)
|
|
64
|
+
if val.respond_to? :value and val.value.respond_to? :fields
|
|
65
|
+
if val.value.fields["__call"] and val.value.fields["__arity"]
|
|
66
|
+
return val.value.fields["__arity"]
|
|
67
|
+
end
|
|
68
|
+
else
|
|
69
|
+
return (to_var val).value.arity
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def to_var(val)
|
|
74
|
+
case val
|
|
75
|
+
when Variable
|
|
76
|
+
return val
|
|
77
|
+
else
|
|
78
|
+
return Variable.new val, (get_type val), @global
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def initialize(bc_io)
|
|
83
|
+
@bc_io = bc_io
|
|
84
|
+
@global = GLOBAL_SCOPE.new
|
|
85
|
+
@global.add_fn "__rb_call", (Variable.new (NativeFn.new 2, (Proc.new do |name, args|
|
|
86
|
+
args = (codify args)[1..-2]
|
|
87
|
+
eval "#{name.value.internal}(#{args})"
|
|
88
|
+
end)), :fn, @global)
|
|
89
|
+
@stack = []
|
|
90
|
+
@byte_pos = 0
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def global
|
|
95
|
+
@global
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def byte_pos=(n)
|
|
99
|
+
@byte_pos = n
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def load_bytes(x_bytes, to_symbol=true) # loads in next x bytes from file, returns in array
|
|
103
|
+
insts = { # just a simple hash to make interpreter code more readable
|
|
104
|
+
0x01 => :make,
|
|
105
|
+
0x02 => :call,
|
|
106
|
+
0x03 => :return,
|
|
107
|
+
0x04 => :set,
|
|
108
|
+
0x05 => :mut,
|
|
109
|
+
0x06 => :exec,
|
|
110
|
+
0x10 => :var,
|
|
111
|
+
0x16 => :end_prg,
|
|
112
|
+
0x17 => :fn,
|
|
113
|
+
0x18 => :strend,
|
|
114
|
+
0x21 => :const,
|
|
115
|
+
0x23 => :add,
|
|
116
|
+
0x24 => :sub,
|
|
117
|
+
0x25 => :mul,
|
|
118
|
+
0x26 => :div,
|
|
119
|
+
0x12 => :bool,
|
|
120
|
+
0x13 => :int,
|
|
121
|
+
0x14 => :str,
|
|
122
|
+
0x15 => :num,
|
|
123
|
+
0x29 => :jmpi,
|
|
124
|
+
0x2a => :jmp,
|
|
125
|
+
0x2b => :jmpn,
|
|
126
|
+
0x20 => :get,
|
|
127
|
+
0x2c => :list,
|
|
128
|
+
0x2f => :nil,
|
|
129
|
+
0x2d => :reset,
|
|
130
|
+
0x2e => :iter,
|
|
131
|
+
0x30 => :object,
|
|
132
|
+
0x31 => :new
|
|
133
|
+
}
|
|
134
|
+
bytes = []
|
|
135
|
+
begin
|
|
136
|
+
x_bytes.times do
|
|
137
|
+
@bc_io.seek(@byte_pos)
|
|
138
|
+
byte_integer = @bc_io.sysread(1).ord
|
|
139
|
+
bytes.push(to_symbol ? insts[byte_integer] : byte_integer)
|
|
140
|
+
@byte_pos += 1
|
|
141
|
+
end
|
|
142
|
+
# add rescue here eventually (mainly to handle end of file error)
|
|
143
|
+
end
|
|
144
|
+
bytes
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def get_string # when called, gets all bytes until STREND and returns them as string
|
|
148
|
+
string = ""
|
|
149
|
+
while (byte = load_bytes(1, false)[0]) != 24 # cant use STREND here, need byte as is
|
|
150
|
+
string += byte.chr
|
|
151
|
+
end
|
|
152
|
+
string
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def get_args
|
|
156
|
+
args = []
|
|
157
|
+
this = ""
|
|
158
|
+
while (byte = load_bytes(1, false)[0]) != 0x08
|
|
159
|
+
if byte == 0x07
|
|
160
|
+
args << this
|
|
161
|
+
this = ""
|
|
162
|
+
else
|
|
163
|
+
this += byte.chr
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
if this != ""
|
|
167
|
+
args << this
|
|
168
|
+
end
|
|
169
|
+
args
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def push_to_stack(to_push)
|
|
173
|
+
@stack.push to_var to_push
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def pop_from_stack
|
|
177
|
+
@stack.pop
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def stack
|
|
181
|
+
@stack
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def error(msg)
|
|
185
|
+
puts "\x1b[0;31mError in VM: #{msg}\x1b[0;0m"
|
|
186
|
+
exit 1
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def interpret # builds stack from bytecode
|
|
190
|
+
loop do
|
|
191
|
+
loaded_bytes = load_bytes(1) # loads in first byte for initial instruction
|
|
192
|
+
break if loaded_bytes[0] == :end_prg # end of program reached
|
|
193
|
+
|
|
194
|
+
case loaded_bytes[0]
|
|
195
|
+
when :make
|
|
196
|
+
loaded_bytes.concat load_bytes(1)
|
|
197
|
+
case loaded_bytes[1]
|
|
198
|
+
when :var
|
|
199
|
+
var_name = get_string
|
|
200
|
+
val = pop_from_stack
|
|
201
|
+
@global.add_var var_name, val
|
|
202
|
+
push_to_stack val # assignments evaluate to their value
|
|
203
|
+
when :fn
|
|
204
|
+
# make fn <name>
|
|
205
|
+
fn_name = get_string
|
|
206
|
+
args = get_args
|
|
207
|
+
size = get_string.to_i
|
|
208
|
+
body =
|
|
209
|
+
((load_bytes size, false).map { |e| e.chr }).join ""
|
|
210
|
+
fn = Function.new args, body
|
|
211
|
+
@global.add_fn fn_name, (Variable.new fn, :fn, @global)
|
|
212
|
+
when :object
|
|
213
|
+
# make fn <name>
|
|
214
|
+
obj_name = get_string
|
|
215
|
+
args = get_args
|
|
216
|
+
size = get_string.to_i
|
|
217
|
+
body =
|
|
218
|
+
((load_bytes size, false).map { |e| e.chr }).join ""
|
|
219
|
+
obj = Obj.new args, body
|
|
220
|
+
@global.add_obj obj_name, (Variable.new obj, :obj, @global)
|
|
221
|
+
end
|
|
222
|
+
when :set
|
|
223
|
+
var_name = get_string
|
|
224
|
+
val = pop_from_stack
|
|
225
|
+
@global.add_var var_name, val
|
|
226
|
+
push_to_stack val # assignments evaluate to their value
|
|
227
|
+
when :const
|
|
228
|
+
loaded_bytes.concat load_bytes(1)
|
|
229
|
+
case loaded_bytes[1]
|
|
230
|
+
when :int
|
|
231
|
+
val = get_string
|
|
232
|
+
push_to_stack Variable.new (Int.new val.to_i), :int, @global
|
|
233
|
+
when :num
|
|
234
|
+
val = get_string
|
|
235
|
+
push_to_stack Variable.new (Num.new val.to_f), :num, @global
|
|
236
|
+
when :str
|
|
237
|
+
val = get_string
|
|
238
|
+
push_to_stack Variable.new (Str.new val), :str, @global
|
|
239
|
+
when :list
|
|
240
|
+
count = get_string.to_i
|
|
241
|
+
vals = []
|
|
242
|
+
count.times do
|
|
243
|
+
vals << pop_from_stack
|
|
244
|
+
end
|
|
245
|
+
vals.reverse!
|
|
246
|
+
push_to_stack Variable.new (List.new vals), :list, @global
|
|
247
|
+
when :nil
|
|
248
|
+
push_to_stack Variable.new (Nil.new), :nil, @global
|
|
249
|
+
end
|
|
250
|
+
when :add
|
|
251
|
+
b, a = pop_from_stack, pop_from_stack
|
|
252
|
+
if a.value.fields["__add"]
|
|
253
|
+
res = (call a.value.fields["__add"], b.value)
|
|
254
|
+
push_to_stack (to_var res)
|
|
255
|
+
else
|
|
256
|
+
error "Cannot add to #{a.type}"
|
|
257
|
+
end
|
|
258
|
+
when :sub
|
|
259
|
+
b, a = pop_from_stack, pop_from_stack
|
|
260
|
+
if a.value.fields["__sub"]
|
|
261
|
+
res = (call a.value.fields["__sub"], b.value)
|
|
262
|
+
push_to_stack (Variable.new res, (get_type res), @global)
|
|
263
|
+
else
|
|
264
|
+
error "Cannot subtract from #{a.type}"
|
|
265
|
+
end
|
|
266
|
+
when :mul
|
|
267
|
+
b, a = pop_from_stack, pop_from_stack
|
|
268
|
+
if a.value.fields["__mul"]
|
|
269
|
+
res = (call a.value.fields["__mul"], b.value)
|
|
270
|
+
push_to_stack (Variable.new res, (get_type res), @global)
|
|
271
|
+
else
|
|
272
|
+
error "Cannot multiply #{a.type}"
|
|
273
|
+
end
|
|
274
|
+
when :div
|
|
275
|
+
b, a = pop_from_stack, pop_from_stack
|
|
276
|
+
if a.value.fields["__div"]
|
|
277
|
+
res = (call a.value.fields["__div"], b.value)
|
|
278
|
+
push_to_stack (Variable.new res, (get_type res), @global)
|
|
279
|
+
else
|
|
280
|
+
error "Cannot divide #{a.type}"
|
|
281
|
+
end
|
|
282
|
+
when :mod
|
|
283
|
+
b, a = pop_from_stack, pop_from_stack
|
|
284
|
+
if a.value.fields["__mod"]
|
|
285
|
+
res = (call a.value.fields["__mod"], b.value)
|
|
286
|
+
push_to_stack (Variable.new res, (get_type res), @global)
|
|
287
|
+
else
|
|
288
|
+
error "Cannot modulo #{a.type}"
|
|
289
|
+
end
|
|
290
|
+
when :pow
|
|
291
|
+
b, a = pop_from_stack, pop_from_stack
|
|
292
|
+
if a.value.fields["__pow"]
|
|
293
|
+
res = (call a.value.fields["__pow"], b.value)
|
|
294
|
+
push_to_stack (Variable.new res, (get_type res), @global)
|
|
295
|
+
else
|
|
296
|
+
error "Cannot exponentiate #{a.type}"
|
|
297
|
+
end
|
|
298
|
+
when :jmpi
|
|
299
|
+
val = pop_from_stack
|
|
300
|
+
amt = get_string
|
|
301
|
+
if truthy val
|
|
302
|
+
@byte_pos += amt.to_i
|
|
303
|
+
end
|
|
304
|
+
when :jmpn
|
|
305
|
+
val = pop_from_stack
|
|
306
|
+
amt = get_string
|
|
307
|
+
unless truthy val
|
|
308
|
+
@byte_pos += amt.to_i
|
|
309
|
+
end
|
|
310
|
+
when :jmp
|
|
311
|
+
amt = get_string
|
|
312
|
+
@byte_pos += amt.to_i
|
|
313
|
+
when :get
|
|
314
|
+
name = get_string
|
|
315
|
+
var = @global.get_var name
|
|
316
|
+
if var
|
|
317
|
+
push_to_stack var
|
|
318
|
+
else
|
|
319
|
+
error "No such variable #{name}"
|
|
320
|
+
end
|
|
321
|
+
when :reset
|
|
322
|
+
val = pop_from_stack
|
|
323
|
+
if val.value.fields["__reset"]
|
|
324
|
+
call val.value.fields["__reset"]
|
|
325
|
+
push_to_stack val
|
|
326
|
+
end
|
|
327
|
+
when :iter
|
|
328
|
+
val = pop_from_stack
|
|
329
|
+
if val.value.fields["__iter"]
|
|
330
|
+
res = call val.value.fields["__iter"]
|
|
331
|
+
push_to_stack res
|
|
332
|
+
end
|
|
333
|
+
when :call
|
|
334
|
+
val = pop_from_stack
|
|
335
|
+
if callable val
|
|
336
|
+
args = []
|
|
337
|
+
(arity val).internal.times do
|
|
338
|
+
this = pop_from_stack
|
|
339
|
+
unless this
|
|
340
|
+
error "Not enough arguments: expected #{val.value.fields["__arity"].internal}, got #{args.size}"
|
|
341
|
+
end
|
|
342
|
+
args << this
|
|
343
|
+
end
|
|
344
|
+
f = Proc.new do |args, scope|
|
|
345
|
+
call val, args, scope
|
|
346
|
+
end
|
|
347
|
+
scope = nil
|
|
348
|
+
begin
|
|
349
|
+
scope = val.scope
|
|
350
|
+
rescue
|
|
351
|
+
scope = @global
|
|
352
|
+
end
|
|
353
|
+
ret = f.call args, scope
|
|
354
|
+
if ret
|
|
355
|
+
push_to_stack ret
|
|
356
|
+
end
|
|
357
|
+
else
|
|
358
|
+
error "Cannot call #{stringify val}"
|
|
359
|
+
end
|
|
360
|
+
when :new
|
|
361
|
+
val = pop_from_stack
|
|
362
|
+
if val.value.fields["__new"] and val.value.fields["__arity"]
|
|
363
|
+
args = []
|
|
364
|
+
val.value.fields["__arity"].internal.times do
|
|
365
|
+
this = pop_from_stack
|
|
366
|
+
unless this
|
|
367
|
+
error "Not enough arguments: expected #{val.value.fields["__arity"].internal}, got #{args.size}"
|
|
368
|
+
end
|
|
369
|
+
args << this
|
|
370
|
+
end
|
|
371
|
+
f = Proc.new do |args, scope|
|
|
372
|
+
call val.value.fields["__new"], args, scope
|
|
373
|
+
end
|
|
374
|
+
ret = Variable.new (InstantiatedObj.new f.call args, @global), :instobj, @global
|
|
375
|
+
if ret
|
|
376
|
+
push_to_stack ret
|
|
377
|
+
end
|
|
378
|
+
else
|
|
379
|
+
error "Cannot instantiate #{stringify val}"
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
|
metadata
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: sardonyx
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.5
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- sugarfi
|
|
8
|
+
- Zavexeon
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2020-08-25 00:00:00.000000000 Z
|
|
13
|
+
dependencies: []
|
|
14
|
+
description: The Sardonyx programming language
|
|
15
|
+
email: sugarfi@sugarfi.dev
|
|
16
|
+
executables:
|
|
17
|
+
- sdx
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- bin/sdx
|
|
22
|
+
- lib/sdx/compiler/compiler.rb
|
|
23
|
+
- lib/sdx/compiler/parser.rb
|
|
24
|
+
- lib/sdx/vm/datatypes.rb
|
|
25
|
+
- lib/sdx/vm/scope.rb
|
|
26
|
+
- lib/sdx/vm/variables.rb
|
|
27
|
+
- lib/sdx/vm/vm.rb
|
|
28
|
+
homepage: https://sardonyxlang.github.io
|
|
29
|
+
licenses:
|
|
30
|
+
- MIT
|
|
31
|
+
metadata: {}
|
|
32
|
+
post_install_message:
|
|
33
|
+
rdoc_options: []
|
|
34
|
+
require_paths:
|
|
35
|
+
- lib
|
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - ">="
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
requirements: []
|
|
47
|
+
rubygems_version: 3.1.2
|
|
48
|
+
signing_key:
|
|
49
|
+
specification_version: 4
|
|
50
|
+
summary: Sardonyx Language
|
|
51
|
+
test_files: []
|