sardonyx 0.2.0 → 0.3.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/bin/sdx +43 -19
- data/lib/sdx/compiler/parser.rb +93 -23
- data/lib/sdx/vm/scope.rb +1 -2
- data/lib/sdx/vm/vm.rb +51 -37
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 045befa602a581ebc1ac02fe1e83c731e86f8090828173e93c5f9709b9cae0b3
|
|
4
|
+
data.tar.gz: 5f1840a2527d90a2145194c03965a071720bb63c4d959299523b1bf139704e91
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d5749987e6dfb77c48d05096310c2e75aac583a822dc85d25470c08ceb093f600047a5c5c101e0b257bdf94d930ad83a0dab07cbf170ea605f3ea9fb4cb6d055
|
|
7
|
+
data.tar.gz: 17c7214c05edb8272138a5f6bc4ac9a98f36738a615f894f744676b99388c8c20ee0b53f41caa02fd7d53d10fe011c84cccec2c71ad363dd31019c03d5dcd933
|
data/bin/sdx
CHANGED
|
@@ -3,33 +3,57 @@ require "sdx/compiler/parser"
|
|
|
3
3
|
require "sdx/compiler/compiler"
|
|
4
4
|
require "sdx/vm/vm"
|
|
5
5
|
require "stringio"
|
|
6
|
+
require "readline"
|
|
7
|
+
|
|
8
|
+
if (ENV.fetch("SDX_PATH", "").split ":") == []
|
|
9
|
+
puts "\x1b[0;33mLooks like you don't have anything in your SDX_PATH! You should install the stdlib at https://github.com/SardonyxLang/SardonyxStd.\x1b[0;0m"
|
|
10
|
+
end
|
|
6
11
|
|
|
7
12
|
if ARGV.size == 1
|
|
8
13
|
path = [(File.expand_path File.dirname ARGV[0]), *(ENV.fetch("SDX_PATH", "").split ":")]
|
|
9
14
|
code = File.read ARGV[0]
|
|
10
|
-
lexed = Parser::Lexer.lex code
|
|
11
|
-
ast = Parser::Parser.parse lexed, path
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
lexed, lines = Parser::Lexer.lex code
|
|
16
|
+
ast = Parser::Parser.parse lexed, path, lines
|
|
17
|
+
if State::state == :ok
|
|
18
|
+
bc = Compiler::Compiler.compile ast
|
|
19
|
+
vm = VM.new StringIO.new bc
|
|
20
|
+
vm.interpret
|
|
21
|
+
end
|
|
15
22
|
else
|
|
16
23
|
path = [(File.expand_path Dir.pwd), *(ENV.fetch("SDX_PATH", "").split ":")]
|
|
17
24
|
vm = VM.new StringIO.new ""
|
|
18
|
-
puts "Sardonyx v0.
|
|
19
|
-
|
|
25
|
+
puts "Sardonyx v0.3.3"
|
|
26
|
+
puts "Type :help for help, or :exit to exit"
|
|
20
27
|
loop do
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
begin
|
|
29
|
+
code = Readline.readline("> ", true)
|
|
30
|
+
rescue Interrupt
|
|
31
|
+
puts "^C"
|
|
32
|
+
code = ""
|
|
33
|
+
end
|
|
34
|
+
unless code
|
|
35
|
+
puts ":exit"
|
|
36
|
+
exit 0
|
|
37
|
+
end
|
|
38
|
+
case code
|
|
39
|
+
when ":exit"
|
|
40
|
+
exit 0
|
|
41
|
+
when ":help"
|
|
42
|
+
puts "See sardonyxlang.github.io/docs for documentation - for now, try entering \"5 + 5\""
|
|
43
|
+
else
|
|
44
|
+
lexed, lines = Parser::Lexer.lex code
|
|
45
|
+
ast = Parser::Parser.parse lexed, path, lines
|
|
46
|
+
if State::state == :ok
|
|
47
|
+
bc = Compiler::Compiler.compile ast
|
|
48
|
+
vm.bc_io = StringIO.new bc
|
|
49
|
+
vm.byte_pos = 0
|
|
50
|
+
vm.interpret false
|
|
51
|
+
val = vm.stack[-1]
|
|
52
|
+
vm.clear
|
|
53
|
+
if val
|
|
54
|
+
puts vm.stringify val
|
|
55
|
+
end
|
|
56
|
+
end
|
|
33
57
|
end
|
|
34
58
|
end
|
|
35
59
|
end
|
data/lib/sdx/compiler/parser.rb
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
|
+
class State
|
|
2
|
+
class << self
|
|
3
|
+
attr_accessor :state
|
|
4
|
+
end
|
|
5
|
+
@@state = :ok
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def error(msg)
|
|
9
|
+
puts "\x1b[0;31mError in parser: #{msg}\x1b[0;0m"
|
|
10
|
+
end
|
|
11
|
+
|
|
1
12
|
module Parser
|
|
13
|
+
|
|
2
14
|
class Lexer
|
|
3
15
|
TOKENS = {
|
|
16
|
+
/\A#.*/ => :comment,
|
|
4
17
|
/\Aif/ => :if,
|
|
5
18
|
/\Aelse/ => :else,
|
|
6
19
|
/\Awhile/ => :while,
|
|
@@ -29,25 +42,78 @@ module Parser
|
|
|
29
42
|
/\A[A-Za-z_][A-Za-z0-9_]*([:.][A-Za-z_][A-Za-z0-9_]*)*/ => :name
|
|
30
43
|
}
|
|
31
44
|
|
|
45
|
+
class << self
|
|
46
|
+
attr_accessor :lines
|
|
47
|
+
end
|
|
48
|
+
|
|
32
49
|
def self.lex(code)
|
|
50
|
+
@@lines = code.split "\n"
|
|
33
51
|
lexed = []
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
52
|
+
comment = false
|
|
53
|
+
line = col = 0
|
|
54
|
+
State::state = :ok
|
|
55
|
+
while State::state == :ok and code.size > 0
|
|
56
|
+
while true
|
|
57
|
+
if code.size != 0 and code[0] == "\n"
|
|
58
|
+
col = 0
|
|
59
|
+
line += 1
|
|
60
|
+
code = code[1..-1]
|
|
61
|
+
elsif code.size != 0 and code[0].strip.empty?
|
|
62
|
+
if State::state == :ok
|
|
63
|
+
col += 1
|
|
64
|
+
end
|
|
65
|
+
code = code[1..-1]
|
|
66
|
+
else
|
|
42
67
|
break
|
|
43
68
|
end
|
|
44
|
-
|
|
45
|
-
if !
|
|
46
|
-
|
|
47
|
-
|
|
69
|
+
end
|
|
70
|
+
if !comment && (code.start_with? "#>")
|
|
71
|
+
comment = true
|
|
72
|
+
code = code[2..-1]
|
|
73
|
+
elsif comment && (code.start_with? "<#")
|
|
74
|
+
comment = false
|
|
75
|
+
code = code[2..-1]
|
|
76
|
+
elsif comment
|
|
77
|
+
code = code[1..-1]
|
|
78
|
+
else
|
|
79
|
+
found = false
|
|
80
|
+
TOKENS.each { |re, tag|
|
|
81
|
+
if (code =~ re) != nil
|
|
82
|
+
found = true
|
|
83
|
+
m = (re.match code)
|
|
84
|
+
if tag != :comment
|
|
85
|
+
lexed << [ m[0], tag, line, col ]
|
|
86
|
+
end
|
|
87
|
+
code = code[(m.end 0)..-1]
|
|
88
|
+
col += (m.end 0)
|
|
89
|
+
while true
|
|
90
|
+
if code.size != 0 and code[0] == "\n"
|
|
91
|
+
col = 0
|
|
92
|
+
line += 1
|
|
93
|
+
code = code[1..-1]
|
|
94
|
+
elsif code.size != 0 and code[0].strip.empty?
|
|
95
|
+
if State::state == :ok
|
|
96
|
+
col += 1
|
|
97
|
+
end
|
|
98
|
+
code = code[1..-1]
|
|
99
|
+
else
|
|
100
|
+
break
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
break
|
|
104
|
+
end
|
|
105
|
+
}
|
|
106
|
+
if !found
|
|
107
|
+
error %{
|
|
108
|
+
Invalid code at #{line}:#{col}
|
|
109
|
+
#{" " * line.to_s.size} |
|
|
110
|
+
#{line} | #{@@lines[line].rstrip}
|
|
111
|
+
#{" " * line.to_s.size} | #{" " * col}^ here}
|
|
112
|
+
State::state = :error
|
|
113
|
+
end
|
|
48
114
|
end
|
|
49
115
|
end
|
|
50
|
-
lexed
|
|
116
|
+
[ lexed, @@lines ]
|
|
51
117
|
end
|
|
52
118
|
end
|
|
53
119
|
|
|
@@ -623,9 +689,9 @@ module Parser
|
|
|
623
689
|
(self.parse_for tokens)
|
|
624
690
|
end
|
|
625
691
|
|
|
626
|
-
def self.parse(tokens, path)
|
|
692
|
+
def self.parse(tokens, path, lines)
|
|
627
693
|
parsed = []
|
|
628
|
-
while tokens.size > 0
|
|
694
|
+
while State::state == :ok and tokens.size > 0
|
|
629
695
|
e = self.parse_expr tokens
|
|
630
696
|
if e
|
|
631
697
|
if e[0].nodetype == :require
|
|
@@ -638,19 +704,23 @@ module Parser
|
|
|
638
704
|
end
|
|
639
705
|
end
|
|
640
706
|
unless code
|
|
641
|
-
|
|
642
|
-
|
|
707
|
+
error "Cannot find file #{e[0].value}.sdx anywhere in path"
|
|
708
|
+
State::state = :error
|
|
643
709
|
end
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
710
|
+
tokens = tokens[e[1]..-1]
|
|
711
|
+
lexed, _ = Lexer.lex code
|
|
712
|
+
tokens = [*lexed, *tokens]
|
|
647
713
|
else
|
|
648
714
|
parsed << e[0]
|
|
715
|
+
tokens = tokens[e[1]..-1]
|
|
649
716
|
end
|
|
650
|
-
tokens = tokens[e[1]..-1]
|
|
651
717
|
else
|
|
652
|
-
|
|
653
|
-
|
|
718
|
+
error %{
|
|
719
|
+
Unexpected token #{tokens[0][1]} at #{tokens[0][2]}:#{tokens[0][3]}
|
|
720
|
+
#{" " * tokens[0][2].to_s.size} |
|
|
721
|
+
#{tokens[0][2]} | #{lines[tokens[0][2]].rstrip}
|
|
722
|
+
#{" " * tokens[0][2].to_s.size} | #{" " * tokens[0][3]}^ here}
|
|
723
|
+
State::state = :error
|
|
654
724
|
end
|
|
655
725
|
end
|
|
656
726
|
parsed
|
data/lib/sdx/vm/scope.rb
CHANGED
|
@@ -3,7 +3,6 @@ class GLOBAL_SCOPE
|
|
|
3
3
|
|
|
4
4
|
def error(msg)
|
|
5
5
|
puts "\x1b[0;31mError in VM: #{msg}\x1b[0;0m"
|
|
6
|
-
exit 1
|
|
7
6
|
end
|
|
8
7
|
|
|
9
8
|
def initialize(variables={})
|
|
@@ -25,7 +24,7 @@ class GLOBAL_SCOPE
|
|
|
25
24
|
name.each do |part|
|
|
26
25
|
val = scope.variables[part]
|
|
27
26
|
unless val
|
|
28
|
-
|
|
27
|
+
return nil
|
|
29
28
|
end
|
|
30
29
|
case val.value
|
|
31
30
|
when InstantiatedObj
|
data/lib/sdx/vm/vm.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
require '
|
|
2
|
-
require '
|
|
3
|
-
require '
|
|
1
|
+
require 'sdx/vm/variables'
|
|
2
|
+
require 'sdx/vm/datatypes'
|
|
3
|
+
require 'sdx/vm/scope'
|
|
4
4
|
|
|
5
5
|
def codify(val)
|
|
6
6
|
if val.value.fields["__as_code_string"]
|
|
@@ -220,13 +220,15 @@ class VM
|
|
|
220
220
|
|
|
221
221
|
def error(msg)
|
|
222
222
|
puts "\x1b[0;31mError in VM: #{msg}\x1b[0;0m"
|
|
223
|
-
|
|
223
|
+
@stack = []
|
|
224
|
+
State::state = :error
|
|
224
225
|
end
|
|
225
226
|
|
|
226
227
|
def interpret(do_end=true) # builds stack from bytecode
|
|
227
228
|
loop do
|
|
228
229
|
loaded_bytes = load_bytes(1) # loads in first byte for initial instruction
|
|
229
230
|
break if loaded_bytes[0] == :end_prg # end of program reached
|
|
231
|
+
break if State::state != :ok
|
|
230
232
|
|
|
231
233
|
case loaded_bytes[0]
|
|
232
234
|
when :make
|
|
@@ -302,7 +304,7 @@ class VM
|
|
|
302
304
|
res = (call a.value.fields["__add"], b.value)
|
|
303
305
|
push_to_stack (to_var res)
|
|
304
306
|
else
|
|
305
|
-
error "Cannot use + on #{a
|
|
307
|
+
error "Cannot use + on #{codify a}"
|
|
306
308
|
end
|
|
307
309
|
when :sub
|
|
308
310
|
b, a = pop_from_stack, pop_from_stack
|
|
@@ -310,7 +312,7 @@ class VM
|
|
|
310
312
|
res = (call a.value.fields["__sub"], b.value)
|
|
311
313
|
push_to_stack (Variable.new res, (get_type res), @global)
|
|
312
314
|
else
|
|
313
|
-
error "Cannot use - on #{a
|
|
315
|
+
error "Cannot use - on #{codify a}"
|
|
314
316
|
end
|
|
315
317
|
when :mul
|
|
316
318
|
b, a = pop_from_stack, pop_from_stack
|
|
@@ -318,7 +320,7 @@ class VM
|
|
|
318
320
|
res = (call a.value.fields["__mul"], b.value)
|
|
319
321
|
push_to_stack (Variable.new res, (get_type res), @global)
|
|
320
322
|
else
|
|
321
|
-
error "Cannot use * on #{a
|
|
323
|
+
error "Cannot use * on #{codify a}"
|
|
322
324
|
end
|
|
323
325
|
when :div
|
|
324
326
|
b, a = pop_from_stack, pop_from_stack
|
|
@@ -326,7 +328,7 @@ class VM
|
|
|
326
328
|
res = (call a.value.fields["__div"], b.value)
|
|
327
329
|
push_to_stack (Variable.new res, (get_type res), @global)
|
|
328
330
|
else
|
|
329
|
-
error "Cannot use / on #{a
|
|
331
|
+
error "Cannot use / on #{codify a}"
|
|
330
332
|
end
|
|
331
333
|
when :mod
|
|
332
334
|
b, a = pop_from_stack, pop_from_stack
|
|
@@ -334,7 +336,7 @@ class VM
|
|
|
334
336
|
res = (call a.value.fields["__mod"], b.value)
|
|
335
337
|
push_to_stack (Variable.new res, (get_type res), @global)
|
|
336
338
|
else
|
|
337
|
-
error "Cannot use % on #{a
|
|
339
|
+
error "Cannot use % on #{codify a}"
|
|
338
340
|
end
|
|
339
341
|
when :pow
|
|
340
342
|
b, a = pop_from_stack, pop_from_stack
|
|
@@ -342,7 +344,7 @@ class VM
|
|
|
342
344
|
res = (call a.value.fields["__pow"], b.value)
|
|
343
345
|
push_to_stack (Variable.new res, (get_type res), @global)
|
|
344
346
|
else
|
|
345
|
-
error "Cannot use ^ on #{a
|
|
347
|
+
error "Cannot use ^ on #{codify a}"
|
|
346
348
|
end
|
|
347
349
|
when :eq
|
|
348
350
|
b, a = pop_from_stack, pop_from_stack
|
|
@@ -350,7 +352,7 @@ class VM
|
|
|
350
352
|
res = (call a.value.fields["__eq"], b.value)
|
|
351
353
|
push_to_stack (Variable.new res, (get_type res), @global)
|
|
352
354
|
else
|
|
353
|
-
error "Cannot use == on #{a
|
|
355
|
+
error "Cannot use == on #{codify a}"
|
|
354
356
|
end
|
|
355
357
|
when :ne
|
|
356
358
|
b, a = pop_from_stack, pop_from_stack
|
|
@@ -358,7 +360,7 @@ class VM
|
|
|
358
360
|
res = (call a.value.fields["__neq"], b.value)
|
|
359
361
|
push_to_stack (Variable.new res, (get_type res), @global)
|
|
360
362
|
else
|
|
361
|
-
error "Cannot use != on #{a
|
|
363
|
+
error "Cannot use != on #{codify a}"
|
|
362
364
|
end
|
|
363
365
|
when :lt
|
|
364
366
|
b, a = pop_from_stack, pop_from_stack
|
|
@@ -366,7 +368,7 @@ class VM
|
|
|
366
368
|
res = (call a.value.fields["__lt"], b.value)
|
|
367
369
|
push_to_stack (Variable.new res, (get_type res), @global)
|
|
368
370
|
else
|
|
369
|
-
error "Cannot use < on #{a
|
|
371
|
+
error "Cannot use < on #{codify a}"
|
|
370
372
|
end
|
|
371
373
|
when :gt
|
|
372
374
|
b, a = pop_from_stack, pop_from_stack
|
|
@@ -374,7 +376,7 @@ class VM
|
|
|
374
376
|
res = (call a.value.fields["__gt"], b.value)
|
|
375
377
|
push_to_stack (Variable.new res, (get_type res), @global)
|
|
376
378
|
else
|
|
377
|
-
error "Cannot use > on #{a
|
|
379
|
+
error "Cannot use > on #{codify a}"
|
|
378
380
|
end
|
|
379
381
|
when :le
|
|
380
382
|
b, a = pop_from_stack, pop_from_stack
|
|
@@ -382,7 +384,7 @@ class VM
|
|
|
382
384
|
res = (call a.value.fields["__le"], b.value)
|
|
383
385
|
push_to_stack (Variable.new res, (get_type res), @global)
|
|
384
386
|
else
|
|
385
|
-
error "Cannot use <= on #{a
|
|
387
|
+
error "Cannot use <= on #{codify a}"
|
|
386
388
|
end
|
|
387
389
|
when :ge
|
|
388
390
|
b, a = pop_from_stack, pop_from_stack
|
|
@@ -390,7 +392,7 @@ class VM
|
|
|
390
392
|
res = (call a.value.fields["__ge"], b.value)
|
|
391
393
|
push_to_stack (Variable.new res, (get_type res), @global)
|
|
392
394
|
else
|
|
393
|
-
error "Cannot use >= on #{a
|
|
395
|
+
error "Cannot use >= on #{codify a}"
|
|
394
396
|
end
|
|
395
397
|
when :jmpi
|
|
396
398
|
val = pop_from_stack
|
|
@@ -420,12 +422,16 @@ class VM
|
|
|
420
422
|
if val.value.fields["__reset"]
|
|
421
423
|
call val.value.fields["__reset"]
|
|
422
424
|
push_to_stack val
|
|
425
|
+
else
|
|
426
|
+
error "Cannot reset #{codify val}"
|
|
423
427
|
end
|
|
424
428
|
when :iter
|
|
425
429
|
val = pop_from_stack
|
|
426
430
|
if val.value.fields["__iter"]
|
|
427
431
|
res = call val.value.fields["__iter"]
|
|
428
432
|
push_to_stack res
|
|
433
|
+
else
|
|
434
|
+
error "Cannot iterate #{codify val}"
|
|
429
435
|
end
|
|
430
436
|
when :end
|
|
431
437
|
if do_end
|
|
@@ -436,45 +442,53 @@ class VM
|
|
|
436
442
|
if callable val
|
|
437
443
|
args = []
|
|
438
444
|
(arity val).internal.times do
|
|
445
|
+
break if State::state != :ok
|
|
439
446
|
this = pop_from_stack
|
|
440
|
-
|
|
447
|
+
if !this
|
|
441
448
|
error "Not enough arguments: expected #{val.value.fields["__arity"].internal}, got #{args.size}"
|
|
449
|
+
else
|
|
450
|
+
args << this
|
|
442
451
|
end
|
|
443
|
-
args << this
|
|
444
452
|
end
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
453
|
+
if State::state == :ok
|
|
454
|
+
scope = nil
|
|
455
|
+
begin
|
|
456
|
+
scope = val.scope
|
|
457
|
+
rescue
|
|
458
|
+
scope = @global
|
|
459
|
+
end
|
|
460
|
+
ret = call val, *args
|
|
461
|
+
if ret
|
|
462
|
+
push_to_stack ret
|
|
463
|
+
end
|
|
454
464
|
end
|
|
455
465
|
else
|
|
456
|
-
error "Cannot call #{
|
|
466
|
+
error "Cannot call #{codify val}"
|
|
457
467
|
end
|
|
458
468
|
when :new
|
|
459
469
|
val = pop_from_stack
|
|
460
470
|
if val.value.fields["__new"] and val.value.fields["__arity"]
|
|
461
471
|
args = []
|
|
462
472
|
val.value.fields["__arity"].internal.times do
|
|
473
|
+
break if State::state != :ok
|
|
463
474
|
this = pop_from_stack
|
|
464
|
-
|
|
475
|
+
if !this
|
|
465
476
|
error "Not enough arguments: expected #{val.value.fields["__arity"].internal}, got #{args.size}"
|
|
477
|
+
else
|
|
478
|
+
args << this
|
|
466
479
|
end
|
|
467
|
-
args << this
|
|
468
|
-
end
|
|
469
|
-
f = Proc.new do |args, scope|
|
|
470
|
-
call val.value.fields["__new"], args, scope
|
|
471
480
|
end
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
481
|
+
if State::state == :ok
|
|
482
|
+
f = Proc.new do |args, scope|
|
|
483
|
+
call val.value.fields["__new"], args, scope
|
|
484
|
+
end
|
|
485
|
+
ret = Variable.new (InstantiatedObj.new f.call args, @global), :instobj, @global
|
|
486
|
+
if ret
|
|
487
|
+
push_to_stack ret
|
|
488
|
+
end
|
|
475
489
|
end
|
|
476
490
|
else
|
|
477
|
-
error "Cannot instantiate #{
|
|
491
|
+
error "Cannot instantiate #{codify val}"
|
|
478
492
|
end
|
|
479
493
|
end
|
|
480
494
|
end
|