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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b28cfaca2d8790be84bad3e1f6bffd5ead67aeaa6be749643a68828530b68bbe
4
- data.tar.gz: 92a7ea6bccf98cf8099fbe8665e20c1a6548c6c7fe4a7c92289ce8dda7ca6393
3
+ metadata.gz: 045befa602a581ebc1ac02fe1e83c731e86f8090828173e93c5f9709b9cae0b3
4
+ data.tar.gz: 5f1840a2527d90a2145194c03965a071720bb63c4d959299523b1bf139704e91
5
5
  SHA512:
6
- metadata.gz: 89cde9393ea7e0f22af24ebe35c18a0b414c9dd115df9d378bcc98b4686f246cd550210ef3ed7f417f00e71b1bc866ccdc3611230c2d63379f8bdae93e651ba4
7
- data.tar.gz: 19dc7e4ccf4700d2158d29812fedfc673ad68fb893c99d9ff98f5bc3d8044b3f0cf10efa9738fbe41931f683e2a276d5f5cf8823509428f9b75540b68f371dcc
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
- 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.3"
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 vm.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
@@ -1,6 +1,6 @@
1
- require './lib/sdx/vm/variables'
2
- require './lib/sdx/vm/datatypes'
3
- require './lib/sdx/vm/scope'
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
- exit 1
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.type}"
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.type}"
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.type}"
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.type}"
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.type}"
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.type}"
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.type}"
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.type}"
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.type}"
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.type}"
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.type}"
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.type}"
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
- unless this
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
- 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
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 #{stringify val}"
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
- unless this
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
- ret = Variable.new (InstantiatedObj.new f.call args, @global), :instobj, @global
473
- if ret
474
- push_to_stack ret
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 #{stringify val}"
491
+ error "Cannot instantiate #{codify val}"
478
492
  end
479
493
  end
480
494
  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.0
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - sugarfi