faultier-esoteric 0.0.1 → 0.0.2
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.
- data/ChangeLog +7 -0
- data/README +9 -6
- data/Rakefile +5 -3
- data/bin/bf +9 -0
- data/bin/dt +5 -5
- data/bin/esoc +48 -0
- data/bin/tetete +9 -0
- data/bin/ws +9 -0
- data/lib/esoteric.rb +2 -2
- data/lib/esoteric/brainfuck.rb +10 -0
- data/lib/esoteric/brainfuck/parser.rb +71 -0
- data/lib/esoteric/dt.rb +10 -0
- data/lib/esoteric/dt/parser.rb +147 -0
- data/lib/esoteric/easyvm.rb +25 -0
- data/lib/esoteric/parser.rb +149 -0
- data/lib/esoteric/runner.rb +20 -19
- data/lib/esoteric/tetete.rb +10 -0
- data/lib/esoteric/tetete/parser.rb +87 -0
- data/lib/esoteric/vm.rb +6 -74
- data/lib/esoteric/whitespace.rb +10 -0
- data/lib/esoteric/whitespace/parser.rb +139 -0
- metadata +40 -18
- data/bin/esm +0 -8
- data/bin/whitespace +0 -9
- data/lib/esoteric/compiler.rb +0 -60
- data/lib/esoteric/compiler/dt.rb +0 -101
- data/lib/esoteric/compiler/whitespace.rb +0 -94
- data/lib/esoteric/version.rb +0 -16
@@ -0,0 +1,149 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module Esoteric
|
6
|
+
class ProcessInterrapt < StandardError; end
|
7
|
+
class LoopInterrapt < StandardError; end
|
8
|
+
|
9
|
+
class Parser
|
10
|
+
def self.parse(src, logger = nil)
|
11
|
+
new(src, logger).parse
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(src, logger = nil)
|
15
|
+
@src = normalize(src)
|
16
|
+
@ast = []
|
17
|
+
unless @logger = logger
|
18
|
+
@logger = Logger.new(STDOUT)
|
19
|
+
@logger.level = Logger::ERROR
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse
|
24
|
+
while exp = process
|
25
|
+
if exp.first == :defn
|
26
|
+
@ast.unshift exp
|
27
|
+
else
|
28
|
+
@ast.push exp
|
29
|
+
end
|
30
|
+
end
|
31
|
+
@ast.unshift :block
|
32
|
+
@ast
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def normalize(src)
|
38
|
+
src
|
39
|
+
end
|
40
|
+
|
41
|
+
def next_token
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def process
|
46
|
+
next_token
|
47
|
+
end
|
48
|
+
|
49
|
+
def process_until(terminate_expr)
|
50
|
+
block = [:block]
|
51
|
+
begin
|
52
|
+
until terminate_expr.call(next_token)
|
53
|
+
block << process
|
54
|
+
break if block.last.nil?
|
55
|
+
end
|
56
|
+
rescue LoopInterrapt
|
57
|
+
end
|
58
|
+
block
|
59
|
+
end
|
60
|
+
|
61
|
+
def exp_arglist(args=[])
|
62
|
+
_arglist = [:arglist]
|
63
|
+
_arglist += args unless args.empty?
|
64
|
+
_arglist
|
65
|
+
end
|
66
|
+
|
67
|
+
def exp_defn_arglist(args=[])
|
68
|
+
_arglist = [:args]
|
69
|
+
_arglist += args unless args.empty?
|
70
|
+
_arglist
|
71
|
+
end
|
72
|
+
|
73
|
+
def exp_defn(name, *args)
|
74
|
+
[:defn, name, [:scope, [:block, exp_defn_arglist(args), yield]]]
|
75
|
+
end
|
76
|
+
|
77
|
+
def exp_mcall(receiver, name, *args)
|
78
|
+
[:call, receiver, name, exp_arglist(args)]
|
79
|
+
end
|
80
|
+
|
81
|
+
def exp_fcall(name, *args)
|
82
|
+
exp_mcall(nil, name, *args)
|
83
|
+
end
|
84
|
+
|
85
|
+
def exp_variable(scope, name)
|
86
|
+
[scope, name]
|
87
|
+
end
|
88
|
+
|
89
|
+
def exp_gvar(name)
|
90
|
+
exp_variable :gvar, "$#{name}".intern
|
91
|
+
end
|
92
|
+
|
93
|
+
def exp_gvarcall(name, *args)
|
94
|
+
exp_mcall exp_gvar(name), *args
|
95
|
+
end
|
96
|
+
|
97
|
+
def exp_lvar(name)
|
98
|
+
exp_variable :lvar, name
|
99
|
+
end
|
100
|
+
|
101
|
+
def exp_lvarcall(name, *args)
|
102
|
+
exp_mcall exp_lvar(name), *args
|
103
|
+
end
|
104
|
+
|
105
|
+
def exp_assign(scope, name, value)
|
106
|
+
[scope, name, value]
|
107
|
+
end
|
108
|
+
|
109
|
+
def exp_gasgn(name, value)
|
110
|
+
exp_assign :gasgn, "$#{name}".intern, value
|
111
|
+
end
|
112
|
+
|
113
|
+
def exp_opgasgn(name, operator, value)
|
114
|
+
exp_gasgn name, exp_gvarcall(name, operator, value)
|
115
|
+
end
|
116
|
+
|
117
|
+
def exp_lasgn(name, value)
|
118
|
+
exp_assign :lasgn, name, value
|
119
|
+
end
|
120
|
+
|
121
|
+
def exp_oplasgn(name, operator, value)
|
122
|
+
exp_lasgn name, exp_lvarcall(name, operator, value)
|
123
|
+
end
|
124
|
+
|
125
|
+
def exp_literal(value)
|
126
|
+
[:lit, value]
|
127
|
+
end
|
128
|
+
|
129
|
+
def exp_condition(name, cond, tblock, fblock=nil)
|
130
|
+
[name, cond, tblock, fblock]
|
131
|
+
end
|
132
|
+
|
133
|
+
def exp_if(cond, tblock, fblock=nil)
|
134
|
+
exp_condition :if, cond, tblock, fblock
|
135
|
+
end
|
136
|
+
|
137
|
+
def exp_block(*args)
|
138
|
+
[:block, *args]
|
139
|
+
end
|
140
|
+
|
141
|
+
def numeric(value)
|
142
|
+
value.to_i
|
143
|
+
end
|
144
|
+
|
145
|
+
def string(value)
|
146
|
+
value.to_s
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
data/lib/esoteric/runner.rb
CHANGED
@@ -4,35 +4,36 @@ require 'logger'
|
|
4
4
|
|
5
5
|
module Esoteric
|
6
6
|
class Runner
|
7
|
-
def self.
|
7
|
+
def self.parse_option
|
8
|
+
require 'optparse'
|
9
|
+
source = nil
|
10
|
+
options = {}
|
11
|
+
OptionParser.new {|opt|
|
12
|
+
opt.on('-e EXPR') {|v| source = v }
|
13
|
+
opt.on('-i','--interactive') { options[:interactive] = true }
|
14
|
+
opt.on('-c','--check-only') { options[:checkonly] = true }
|
15
|
+
opt.on('-d','--debug') { options[:loglevel] = Logger::DEBUG }
|
16
|
+
opt.on('-w','--warning') { options[:loglevel] ||= Logger::WARN }
|
17
|
+
opt.on('-v','--version') { puts $esoteric_bin_version; exit 0 }
|
18
|
+
opt.parse!(ARGV)
|
19
|
+
}
|
20
|
+
source = ARGF.read unless source
|
21
|
+
return source, options
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.run(source, parser, vm, options={}, logger=nil)
|
8
25
|
logger ||= Logger.new(STDOUT)
|
9
26
|
logger.level = options[:loglevel] if !!options[:loglevel]
|
10
27
|
if options[:interactive]
|
11
28
|
raise NotImplementedError
|
12
29
|
else
|
13
|
-
|
30
|
+
ast = parser.parse(source)
|
14
31
|
if options[:checkonly]
|
15
32
|
puts 'Syntax OK'
|
16
33
|
else
|
17
|
-
vm.run
|
34
|
+
vm.run ast, logger
|
18
35
|
end
|
19
36
|
end
|
20
37
|
end
|
21
38
|
end
|
22
39
|
end
|
23
|
-
|
24
|
-
if defined?(ESOTERIC_BIN_VERSION)
|
25
|
-
require 'optparse'
|
26
|
-
$source = nil
|
27
|
-
$options = {}
|
28
|
-
OptionParser.new {|opt|
|
29
|
-
opt.on('-e EXPR') {|v| $source = v }
|
30
|
-
opt.on('-i','--interactive') { $options[:interactive] = true }
|
31
|
-
opt.on('-c','--check-only') { $options[:checkonly] = true }
|
32
|
-
opt.on('-d','--debug') { $options[:loglevel] = Logger::DEBUG }
|
33
|
-
opt.on('-w','--warning') { $options[:loglevel] ||= Logger::WARN }
|
34
|
-
opt.on('-v','--version') { puts ESOTERIC_BIN_VERSION; exit 0 }
|
35
|
-
opt.parse!(ARGV)
|
36
|
-
}
|
37
|
-
$source = ARGF.read unless $source
|
38
|
-
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
if RUBY_VERSION =~ /^1\.8\./
|
4
|
+
$KCODE = 'u'
|
5
|
+
require 'jcode'
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'strscan'
|
9
|
+
|
10
|
+
module Esoteric
|
11
|
+
module Tetete
|
12
|
+
class Parser < Esoteric::Parser
|
13
|
+
def initialize(src, logger = nil)
|
14
|
+
super
|
15
|
+
@s = StringScanner.new(@src)
|
16
|
+
@ast = [
|
17
|
+
exp_gasgn(:P, exp_literal(0)),
|
18
|
+
exp_gasgn(:B, [:array]),
|
19
|
+
exp_defn(:get_value) {
|
20
|
+
[:op_asgn1, exp_gvar(:B), exp_arglist([exp_gvar(:P)]), :"||", exp_literal(0)]
|
21
|
+
},
|
22
|
+
exp_defn(:set_value, :val) { exp_gvarcall(:B, :[]=, exp_gvar(:P), exp_lvar(:val)) }
|
23
|
+
]
|
24
|
+
end
|
25
|
+
|
26
|
+
def parse
|
27
|
+
begin
|
28
|
+
loop do
|
29
|
+
exp = process
|
30
|
+
next unless !!exp
|
31
|
+
case exp.first
|
32
|
+
when :defn then @ast.unshift exp
|
33
|
+
when :block then @ast += exp[1..-1]
|
34
|
+
else @ast.push exp
|
35
|
+
end
|
36
|
+
end
|
37
|
+
rescue ProcessInterrapt
|
38
|
+
# do nothing
|
39
|
+
end
|
40
|
+
@ast.unshift :block
|
41
|
+
# require 'pp'; pp @ast
|
42
|
+
@ast
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def normalize(src)
|
48
|
+
src.gsub! /\{.*\}/, ''
|
49
|
+
src
|
50
|
+
end
|
51
|
+
|
52
|
+
def process
|
53
|
+
case
|
54
|
+
when @s.eos?
|
55
|
+
raise ProcessInterrapt
|
56
|
+
when @s.scan(/ー(.+?)てー/m)
|
57
|
+
@s[1].split(//).inject([:block]) { |block, c|
|
58
|
+
block << exp_fcall(:set_value, exp_literal(c))
|
59
|
+
block << exp_opgasgn(:P, :+, exp_literal(1))
|
60
|
+
}
|
61
|
+
when @s.scan(/ててー/)
|
62
|
+
exp_fcall :set_value, exp_mcall(exp_fcall(:get_value), :+, exp_literal(1))
|
63
|
+
when @s.scan(/てっー/)
|
64
|
+
exp_fcall :set_value, exp_mcall(exp_fcall(:get_value), :+, exp_literal(-1))
|
65
|
+
when @s.scan(/てってー/)
|
66
|
+
exp_opgasgn :P, :+, exp_literal(1)
|
67
|
+
when @s.scan(/てっててー/)
|
68
|
+
exp_opgasgn :P, :+, exp_literal(-1)
|
69
|
+
when @s.scan(/てってっー/)
|
70
|
+
[:block, exp_fcall(:print, exp_fcall(:get_value)), exp_opgasgn(:P, :+, exp_literal(1))]
|
71
|
+
when @s.scan(/てってってー/)
|
72
|
+
[:block, exp_fcall(:set_value, exp_fcall(:getc)), exp_opgasgn(:P, :+, exp_literal(1))]
|
73
|
+
when @s.scan(/てってっててー/)
|
74
|
+
[:until,
|
75
|
+
exp_mcall(exp_fcall(:get_value), :==, exp_literal(0)),
|
76
|
+
process_until(lambda { |*args| @s.eos? || !!@s.match?(/てってってっー/) }),
|
77
|
+
nil
|
78
|
+
]
|
79
|
+
when @s.scan(/てってってっー/)
|
80
|
+
raise LoopInterrapt
|
81
|
+
when @s.scan(/.[^てっー]*/m)
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/esoteric/vm.rb
CHANGED
@@ -3,21 +3,13 @@
|
|
3
3
|
require 'logger'
|
4
4
|
|
5
5
|
module Esoteric
|
6
|
-
class ProgramInterrapt < StandardError; end
|
7
|
-
|
8
6
|
class VM
|
9
|
-
def self.run(
|
10
|
-
new(
|
7
|
+
def self.run(ast, logger=nil)
|
8
|
+
new(ast, logger).run
|
11
9
|
end
|
12
10
|
|
13
|
-
def initialize(
|
14
|
-
@
|
15
|
-
@stack = []
|
16
|
-
@heap = Hash.new { raise RuntimeError, 'can not read uninitialized heap value' }
|
17
|
-
@pc = 0
|
18
|
-
@labels = {}
|
19
|
-
@skip_to = nil
|
20
|
-
@caller = []
|
11
|
+
def initialize(ast, logger=nil)
|
12
|
+
@ast = ast
|
21
13
|
unless @logger = logger
|
22
14
|
@logger = Logger.new(STDOUT)
|
23
15
|
@logger.level = Logger::ERROR
|
@@ -25,73 +17,13 @@ module Esoteric
|
|
25
17
|
end
|
26
18
|
|
27
19
|
def run
|
28
|
-
|
29
|
-
raise RuntimeError, ':exit missing'
|
30
|
-
rescue ProgramInterrapt => e
|
31
|
-
# successfully exit
|
20
|
+
raise NotImplementedError
|
32
21
|
end
|
33
22
|
|
34
23
|
private
|
35
24
|
|
36
|
-
def parse(asm)
|
37
|
-
asm.split(/\n/).map {|line|
|
38
|
-
next unless line =~ /([a-z]+)(?:[\s]+([\w]+))?/
|
39
|
-
cmd, arg = $1.intern, $2
|
40
|
-
arg ? [cmd, (arg =~ /\A[\d]+\z/ ? arg.to_i : arg)] : [cmd]
|
41
|
-
}.compact
|
42
|
-
end
|
43
|
-
|
44
25
|
def step
|
45
|
-
|
46
|
-
# puts "@pc => #{@pc}, insn => #{insn}, @stack => #{@stack.inspect}, @heap => #{@heap.inspect}, @skip_to => #{@skip_to}, @labels => #{@labels.inspect}, @caller => #{@caller.inspect}"
|
47
|
-
if @skip_to.nil? || insn == :label
|
48
|
-
case insn
|
49
|
-
when :push then push arg
|
50
|
-
when :dup then value = pop; push value; push value
|
51
|
-
when :copy then push @stack[-arg-1]
|
52
|
-
when :swap then push *[pop, pop]
|
53
|
-
when :discard then pop
|
54
|
-
when :slide then top = pop; arg.times { pop }; push top
|
55
|
-
when :add then y, x = pop, pop; push x + y
|
56
|
-
when :sub then y, x = pop, pop; push x - y
|
57
|
-
when :mul then y, x = pop, pop; push x * y
|
58
|
-
when :div then y, x = pop, pop; push (x / y).to_i
|
59
|
-
when :mod then y, x = pop, pop; push x % y
|
60
|
-
when :hwrite then value, address = pop, pop; @heap[address] = value
|
61
|
-
when :hread then address = pop; push @heap[address]
|
62
|
-
when :label then @labels[arg] ||= @pc; @skip_to = nil if @skip_to == arg
|
63
|
-
when :call then @caller.push @pc; jump_to arg
|
64
|
-
when :jump then jump_to arg
|
65
|
-
when :jumpz then jump_to arg if pop == 0
|
66
|
-
when :jumpn then jump_to arg if pop < 0
|
67
|
-
when :return then raise RuntimeError, 'invalid return' if @caller.empty?; @pc = @caller.pop
|
68
|
-
when :exit then raise ProgramInterrapt
|
69
|
-
when :cout then print pop.chr
|
70
|
-
when :nout then print pop
|
71
|
-
when :cin then address = pop; @heap[address] = $stdin.getc.ord
|
72
|
-
when :nin then address = pop; @heap[address] = $stdin.getc.to_i
|
73
|
-
end
|
74
|
-
end
|
75
|
-
@pc += 1
|
76
|
-
end
|
77
|
-
|
78
|
-
def push(value)
|
79
|
-
@stack.push value
|
80
|
-
end
|
81
|
-
|
82
|
-
def pop
|
83
|
-
@stack.pop
|
84
|
-
end
|
85
|
-
|
86
|
-
def jump_to(label)
|
87
|
-
unless @labels[label]
|
88
|
-
@skip_to = label
|
89
|
-
while @skip_to
|
90
|
-
raise RuntimeError, "label '#{label}' is missing" unless @pc < @insns.size
|
91
|
-
step
|
92
|
-
end
|
93
|
-
end
|
94
|
-
@pc = @labels[label]
|
26
|
+
raise NotImplementedError
|
95
27
|
end
|
96
28
|
end
|
97
29
|
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'strscan'
|
4
|
+
|
5
|
+
module Esoteric
|
6
|
+
module Whitespace
|
7
|
+
class Parser < Esoteric::Parser
|
8
|
+
NVAL = /([ \t]+)\n/
|
9
|
+
LVAL = NVAL
|
10
|
+
PUSH = / #{NVAL}/
|
11
|
+
DUP = / \n /
|
12
|
+
COPY = / \t #{NVAL}/
|
13
|
+
SWAP = / \n\t/
|
14
|
+
DISCARD = / \n\n/
|
15
|
+
SLIDE = / \t\n#{NVAL}/
|
16
|
+
ADD = /\t /
|
17
|
+
SUB = /\t \t/
|
18
|
+
MUL = /\t \n/
|
19
|
+
DIV = /\t \t /
|
20
|
+
MOD = /\t \t\t/
|
21
|
+
HWRITE = /\t\t /
|
22
|
+
HREAD = /\t\t\t/
|
23
|
+
LABEL = /\n #{LVAL}/
|
24
|
+
CALL = /\n \t#{LVAL}/
|
25
|
+
JUMP = /\n \n#{LVAL}/
|
26
|
+
JUMPZ = /\n\t #{LVAL}/
|
27
|
+
JUMPN = /\n\t\t#{LVAL}/
|
28
|
+
RETURN = /\n\t\n/
|
29
|
+
EXIT = /\n\n\n/
|
30
|
+
COUT = /\t\n /
|
31
|
+
NOUT = /\t\n \t/
|
32
|
+
CIN = /\t\n\t /
|
33
|
+
NIN = /\t\n\t\t/
|
34
|
+
|
35
|
+
def initialize(src,logger=nil)
|
36
|
+
super
|
37
|
+
@s = StringScanner.new(@src)
|
38
|
+
@ast = [
|
39
|
+
[:gasgn, :$stack, [:array]],
|
40
|
+
[:gasgn, :$heap, [:hash]],
|
41
|
+
]
|
42
|
+
end
|
43
|
+
|
44
|
+
def parse
|
45
|
+
begin
|
46
|
+
loop do
|
47
|
+
exp = process
|
48
|
+
next unless !!exp
|
49
|
+
if exp.first == :defn
|
50
|
+
@ast.unshift exp
|
51
|
+
else
|
52
|
+
@ast.push exp
|
53
|
+
end
|
54
|
+
end
|
55
|
+
rescue ProcessInterrapt
|
56
|
+
# do nothing
|
57
|
+
end
|
58
|
+
@ast.unshift :block
|
59
|
+
# require 'pp'; pp @ast
|
60
|
+
@ast
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def normalize(src)
|
66
|
+
src.gsub(/[^ \t\n]/, '')
|
67
|
+
end
|
68
|
+
|
69
|
+
def process
|
70
|
+
case
|
71
|
+
when @s.eos? then raise ProcessInterrapt
|
72
|
+
when @s.scan(PUSH) then exp_push exp_literal(numeric(@s[1]))
|
73
|
+
when @s.scan(DUP) then exp_push exp_gvarcall(:stack, :last)
|
74
|
+
when @s.scan(COPY) then exp_push exp_gvarcall(:stack, :[], exp_literal(-(numeric(@s[1]))-1))
|
75
|
+
when @s.scan(SWAP) then exp_push exp_pop, exp_pop
|
76
|
+
when @s.scan(DISCARD) then exp_pop
|
77
|
+
when @s.scan(SLIDE) then
|
78
|
+
exp_block exp_lasgn(:top, exp_gvarcall(:stack, :pop)), exp_mcall(exp_literal(numeric(@s[1])), :times, exp_pop), exp_push(exp_lvar(:top))
|
79
|
+
when @s.scan(ADD) then
|
80
|
+
exp_block exp_lasgn(:y, exp_pop), exp_lasgn(:x, exp_pop), exp_mcallpush(exp_lvar(:x), :+, exp_lvar(:y))
|
81
|
+
when @s.scan(SUB) then
|
82
|
+
exp_block exp_lasgn(:y, exp_pop), exp_lasgn(:x, exp_pop), exp_mcallpush(exp_lvar(:x), :-, exp_lvar(:y))
|
83
|
+
when @s.scan(MUL) then
|
84
|
+
exp_block exp_lasgn(:y, exp_pop), exp_lasgn(:x, exp_pop), exp_mcallpush(exp_lvar(:x), :*, exp_lvar(:y))
|
85
|
+
when @s.scan(DIV) then
|
86
|
+
exp_block exp_lasgn(:y, exp_pop), exp_lasgn(:x, exp_pop), exp_mcallpush(exp_lvar(:x), :/, exp_lvar(:y))
|
87
|
+
when @s.scan(MOD) then
|
88
|
+
exp_block exp_lasgn(:y, exp_pop), exp_lasgn(:x, exp_pop), exp_mcallpush(exp_lvar(:x), :%, exp_lvar(:y))
|
89
|
+
when @s.scan(HWRITE) then
|
90
|
+
exp_block exp_lasgn(:val, exp_pop), exp_lasgn(:addr, exp_pop), exp_gvarcall(:heap, :[]=, exp_lvar(:addr), exp_lvar(:val))
|
91
|
+
when @s.scan(HREAD) then
|
92
|
+
exp_block exp_lasgn(:addr, exp_pop), exp_push(exp_gvarcall(:heap, :[], exp_lvar(:addr)))
|
93
|
+
when @s.scan(LABEL) then
|
94
|
+
defn(string(@s[1]).intern) { process_until( lambda {|*args| @s.eos? || !!@s.match?(RETURN)} ) }
|
95
|
+
when @s.scan(CALL) then exp_fcall(string(@s[1]).intern)
|
96
|
+
when @s.scan(JUMP)
|
97
|
+
exp_if exp_literal(true), exp_fcall(string(@s[1]).intern), process_until( lambda {|*args| @s.eos? || !!@s.match?(RETURN)} )
|
98
|
+
when @s.scan(JUMPZ)
|
99
|
+
exp_if exp_mcall(pop, :==, exp_literal(0)), exp_fcall(string(@s[1]).intern), process_until( lambda {|*args| @s.eos? || !!@s.match?(RETURN)} )
|
100
|
+
when @s.scan(JUMPN)
|
101
|
+
exp_if exp_mcall(pop, :<, exp_literal(0)), exp_fcall(string(@s[1]).intern), process_until( lambda {|*args| @s.eos? || !!@s.match?(RETURN)} )
|
102
|
+
when @s.scan(RETURN) then nil
|
103
|
+
when @s.scan(EXIT) then exp_fcall :exit, exp_literal(0)
|
104
|
+
when @s.scan(COUT) then exp_gvarcall :stdout, :print, exp_mcall(exp_pop, :chr)
|
105
|
+
when @s.scan(NOUT) then exp_gvarcall :stdout, :print, exp_mcall(exp_pop, :to_i)
|
106
|
+
when @s.scan(CIN) then exp_mcallpush exp_gvarcall(:stdin, :getc), :ord
|
107
|
+
when @s.scan(NIN) then exp_mcallpush exp_gvarcall(:stdin, :getc), :to_i
|
108
|
+
else raise SyntaxError
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def exp_pop
|
113
|
+
exp_gvarcall :stack, :pop
|
114
|
+
end
|
115
|
+
|
116
|
+
def exp_push(value)
|
117
|
+
exp_gvarcall :stack, :push, value
|
118
|
+
end
|
119
|
+
|
120
|
+
def exp_mcallpush(receiver, method, *args)
|
121
|
+
exp_push exp_mcall(receiver, method, *args)
|
122
|
+
end
|
123
|
+
|
124
|
+
def numeric(value)
|
125
|
+
raise ArgumentError if "#{value}\n" !~ /\A#{NVAL}\z/
|
126
|
+
n = value.sub(/\A /, '+').
|
127
|
+
sub(/\A\t/, '-').
|
128
|
+
gsub(/ /, '0').
|
129
|
+
gsub(/\t/, '1')
|
130
|
+
n.to_i(2)
|
131
|
+
end
|
132
|
+
|
133
|
+
def string(value)
|
134
|
+
raise ArgumentError if "#{value}\n" !~ /\A#{LVAL}\z/
|
135
|
+
value.gsub(/ /, 's').gsub(/\t/, 't')
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|