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.
@@ -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
+
@@ -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: []