sardonyx 0.2.1 → 0.3.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b18e4b27873132c4a3c503bdd971322516681b0766d2de32c4937bfb502a0e6
4
- data.tar.gz: 32ab2476bb7f1f98fee2860e721f52e3ef38e8e739652b28a74c68a6889fca47
3
+ metadata.gz: 5d369226413f552c8f1cb2fc20675a4364c2d4ec03f7b22897c341567a6da09e
4
+ data.tar.gz: a39fb4fb82a76ea92c4799e3a5b51f52c62d7dfd853402a0e83877b790bda36c
5
5
  SHA512:
6
- metadata.gz: eaa1ca5fa9355b8a351556aaae7f8b997f5cd0cc22ce66bce333b21f819a9c950c1ac98ebed53d4f19bf522844ed61221b57b04dedf710974fb743b32d28fe0b
7
- data.tar.gz: 9c950d38de0816ab641f36cdfbf05d958460b97b97b23ba7331c38f836cafef13102bfdf0ca16f5bcfcc89dda4d00923a83bdaffc1cd0e183c2c6da4319aabe5
6
+ metadata.gz: 9714fee82b1115876a3533a4e9cf9cb82875f6c0b4e2749ceeb1fc63218b262364e52cfd0e4649c8b1f825206a1772572033fe2d6f569f88fa1def58477f3e30
7
+ data.tar.gz: 858e1cffdf236a2ef9fb14e7af794d68b6ca83c9724ef05b02c65d7ce2ba73954e808ace033d8423fe52f9dde6ebbc54c56adc6036bc177b6a9ce4888711ae69
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
- bc = Compiler::Compiler.compile ast
13
- vm = VM.new StringIO.new bc
14
- vm.interpret
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.2"
19
- def exit(_) end
25
+ puts "Sardonyx v0.3.4"
26
+ puts "Type :help for help, or :exit to exit"
20
27
  loop do
21
- print "> "
22
- code = gets
23
- lexed = Parser::Lexer.lex code
24
- ast = Parser::Parser.parse lexed, path
25
- bc = Compiler::Compiler.compile ast
26
- vm.bc_io = StringIO.new bc
27
- vm.byte_pos = 0
28
- vm.interpret false
29
- val = vm.stack[-1]
30
- vm.clear
31
- if val
32
- puts vm.stringify val
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 stringify val
55
+ end
56
+ end
33
57
  end
34
58
  end
35
59
  end
@@ -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
- found = false
35
- while code.size > 0
36
- TOKENS.each { |re, tag|
37
- if (code =~ re) != nil
38
- found = true
39
- m = (re.match code)
40
- lexed << [ m[0], tag ]
41
- code = code[(m.end 0)..code.size].lstrip
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 !found
46
- puts "Syntax error: ", code
47
- Kernel.exit 1
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
- puts "Cannot find file #{e[0].value}.sdx anywhere in path"
642
- Kernel.exit 1
707
+ error "Cannot find file #{e[0].value}.sdx anywhere in path"
708
+ State::state = :error
643
709
  end
644
- lexed = Lexer.lex code
645
- ast = self.parse lexed, path
646
- parsed.concat ast
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
- puts "Syntax error at token ", tokens[0][1]
653
- Kernel.exit 1
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
@@ -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
- error "No such variable #{part}"
27
+ return nil
29
28
  end
30
29
  case val.value
31
30
  when InstantiatedObj
@@ -14,6 +14,18 @@ def codify(val)
14
14
  end
15
15
  end
16
16
 
17
+ def stringify(val)
18
+ if val.value.fields["__as_string"]
19
+ if val.value.fields["__as_string"].respond_to? :fields
20
+ (val.value.fields["__as_string"].fields["__call"].call [], val.scope).internal
21
+ else
22
+ (val.value.fields["__as_string"]).call.internal
23
+ end
24
+ else
25
+ val.value.to_s
26
+ end
27
+ end
28
+
17
29
  class VM
18
30
  attr_accessor :bc_io
19
31
 
@@ -28,14 +40,6 @@ class VM
28
40
  return true
29
41
  end
30
42
  end
31
-
32
- def stringify(val)
33
- if val.value.fields["__as_string"]
34
- (call val.value.fields["__as_string"], [], val.scope).internal
35
- else
36
- val.value.to_s
37
- end
38
- end
39
43
 
40
44
  def call(val, *args)
41
45
  if val.respond_to? :value and val.value.respond_to? :fields
@@ -220,13 +224,15 @@ class VM
220
224
 
221
225
  def error(msg)
222
226
  puts "\x1b[0;31mError in VM: #{msg}\x1b[0;0m"
223
- exit 1
227
+ @stack = []
228
+ State::state = :error
224
229
  end
225
230
 
226
231
  def interpret(do_end=true) # builds stack from bytecode
227
232
  loop do
228
233
  loaded_bytes = load_bytes(1) # loads in first byte for initial instruction
229
234
  break if loaded_bytes[0] == :end_prg # end of program reached
235
+ break if State::state != :ok
230
236
 
231
237
  case loaded_bytes[0]
232
238
  when :make
@@ -302,7 +308,7 @@ class VM
302
308
  res = (call a.value.fields["__add"], b.value)
303
309
  push_to_stack (to_var res)
304
310
  else
305
- error "Cannot use + on #{a.type}"
311
+ error "Cannot use + on #{codify a}"
306
312
  end
307
313
  when :sub
308
314
  b, a = pop_from_stack, pop_from_stack
@@ -310,7 +316,7 @@ class VM
310
316
  res = (call a.value.fields["__sub"], b.value)
311
317
  push_to_stack (Variable.new res, (get_type res), @global)
312
318
  else
313
- error "Cannot use - on #{a.type}"
319
+ error "Cannot use - on #{codify a}"
314
320
  end
315
321
  when :mul
316
322
  b, a = pop_from_stack, pop_from_stack
@@ -318,7 +324,7 @@ class VM
318
324
  res = (call a.value.fields["__mul"], b.value)
319
325
  push_to_stack (Variable.new res, (get_type res), @global)
320
326
  else
321
- error "Cannot use * on #{a.type}"
327
+ error "Cannot use * on #{codify a}"
322
328
  end
323
329
  when :div
324
330
  b, a = pop_from_stack, pop_from_stack
@@ -326,7 +332,7 @@ class VM
326
332
  res = (call a.value.fields["__div"], b.value)
327
333
  push_to_stack (Variable.new res, (get_type res), @global)
328
334
  else
329
- error "Cannot use / on #{a.type}"
335
+ error "Cannot use / on #{codify a}"
330
336
  end
331
337
  when :mod
332
338
  b, a = pop_from_stack, pop_from_stack
@@ -334,7 +340,7 @@ class VM
334
340
  res = (call a.value.fields["__mod"], b.value)
335
341
  push_to_stack (Variable.new res, (get_type res), @global)
336
342
  else
337
- error "Cannot use % on #{a.type}"
343
+ error "Cannot use % on #{codify a}"
338
344
  end
339
345
  when :pow
340
346
  b, a = pop_from_stack, pop_from_stack
@@ -342,7 +348,7 @@ class VM
342
348
  res = (call a.value.fields["__pow"], b.value)
343
349
  push_to_stack (Variable.new res, (get_type res), @global)
344
350
  else
345
- error "Cannot use ^ on #{a.type}"
351
+ error "Cannot use ^ on #{codify a}"
346
352
  end
347
353
  when :eq
348
354
  b, a = pop_from_stack, pop_from_stack
@@ -350,7 +356,7 @@ class VM
350
356
  res = (call a.value.fields["__eq"], b.value)
351
357
  push_to_stack (Variable.new res, (get_type res), @global)
352
358
  else
353
- error "Cannot use == on #{a.type}"
359
+ error "Cannot use == on #{codify a}"
354
360
  end
355
361
  when :ne
356
362
  b, a = pop_from_stack, pop_from_stack
@@ -358,7 +364,7 @@ class VM
358
364
  res = (call a.value.fields["__neq"], b.value)
359
365
  push_to_stack (Variable.new res, (get_type res), @global)
360
366
  else
361
- error "Cannot use != on #{a.type}"
367
+ error "Cannot use != on #{codify a}"
362
368
  end
363
369
  when :lt
364
370
  b, a = pop_from_stack, pop_from_stack
@@ -366,7 +372,7 @@ class VM
366
372
  res = (call a.value.fields["__lt"], b.value)
367
373
  push_to_stack (Variable.new res, (get_type res), @global)
368
374
  else
369
- error "Cannot use < on #{a.type}"
375
+ error "Cannot use < on #{codify a}"
370
376
  end
371
377
  when :gt
372
378
  b, a = pop_from_stack, pop_from_stack
@@ -374,7 +380,7 @@ class VM
374
380
  res = (call a.value.fields["__gt"], b.value)
375
381
  push_to_stack (Variable.new res, (get_type res), @global)
376
382
  else
377
- error "Cannot use > on #{a.type}"
383
+ error "Cannot use > on #{codify a}"
378
384
  end
379
385
  when :le
380
386
  b, a = pop_from_stack, pop_from_stack
@@ -382,7 +388,7 @@ class VM
382
388
  res = (call a.value.fields["__le"], b.value)
383
389
  push_to_stack (Variable.new res, (get_type res), @global)
384
390
  else
385
- error "Cannot use <= on #{a.type}"
391
+ error "Cannot use <= on #{codify a}"
386
392
  end
387
393
  when :ge
388
394
  b, a = pop_from_stack, pop_from_stack
@@ -390,7 +396,7 @@ class VM
390
396
  res = (call a.value.fields["__ge"], b.value)
391
397
  push_to_stack (Variable.new res, (get_type res), @global)
392
398
  else
393
- error "Cannot use >= on #{a.type}"
399
+ error "Cannot use >= on #{codify a}"
394
400
  end
395
401
  when :jmpi
396
402
  val = pop_from_stack
@@ -420,12 +426,16 @@ class VM
420
426
  if val.value.fields["__reset"]
421
427
  call val.value.fields["__reset"]
422
428
  push_to_stack val
429
+ else
430
+ error "Cannot reset #{codify val}"
423
431
  end
424
432
  when :iter
425
433
  val = pop_from_stack
426
434
  if val.value.fields["__iter"]
427
435
  res = call val.value.fields["__iter"]
428
436
  push_to_stack res
437
+ else
438
+ error "Cannot iterate #{codify val}"
429
439
  end
430
440
  when :end
431
441
  if do_end
@@ -436,45 +446,53 @@ class VM
436
446
  if callable val
437
447
  args = []
438
448
  (arity val).internal.times do
449
+ break if State::state != :ok
439
450
  this = pop_from_stack
440
- unless this
451
+ if !this
441
452
  error "Not enough arguments: expected #{val.value.fields["__arity"].internal}, got #{args.size}"
453
+ else
454
+ args << this
442
455
  end
443
- args << this
444
456
  end
445
- scope = nil
446
- begin
447
- scope = val.scope
448
- rescue
449
- scope = @global
450
- end
451
- ret = call val, *args
452
- if ret
453
- push_to_stack ret
457
+ if State::state == :ok
458
+ scope = nil
459
+ begin
460
+ scope = val.scope
461
+ rescue
462
+ scope = @global
463
+ end
464
+ ret = call val, *args
465
+ if ret
466
+ push_to_stack ret
467
+ end
454
468
  end
455
469
  else
456
- error "Cannot call #{stringify val}"
470
+ error "Cannot call #{codify val}"
457
471
  end
458
472
  when :new
459
473
  val = pop_from_stack
460
474
  if val.value.fields["__new"] and val.value.fields["__arity"]
461
475
  args = []
462
476
  val.value.fields["__arity"].internal.times do
477
+ break if State::state != :ok
463
478
  this = pop_from_stack
464
- unless this
479
+ if !this
465
480
  error "Not enough arguments: expected #{val.value.fields["__arity"].internal}, got #{args.size}"
481
+ else
482
+ args << this
466
483
  end
467
- args << this
468
- end
469
- f = Proc.new do |args, scope|
470
- call val.value.fields["__new"], args, scope
471
484
  end
472
- ret = Variable.new (InstantiatedObj.new f.call args, @global), :instobj, @global
473
- if ret
474
- push_to_stack ret
485
+ if State::state == :ok
486
+ f = Proc.new do |args, scope|
487
+ call val.value.fields["__new"], args, scope
488
+ end
489
+ ret = Variable.new (InstantiatedObj.new f.call args, @global), :instobj, @global
490
+ if ret
491
+ push_to_stack ret
492
+ end
475
493
  end
476
494
  else
477
- error "Cannot instantiate #{stringify val}"
495
+ error "Cannot instantiate #{codify val}"
478
496
  end
479
497
  end
480
498
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sardonyx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - sugarfi