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.
@@ -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
@@ -4,35 +4,36 @@ require 'logger'
4
4
 
5
5
  module Esoteric
6
6
  class Runner
7
- def self.run(source, compiler, vm, options={}, logger=nil)
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
- esm = !!compiler ? compiler.compile(source) : source
30
+ ast = parser.parse(source)
14
31
  if options[:checkonly]
15
32
  puts 'Syntax OK'
16
33
  else
17
- vm.run esm, logger
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,10 @@
1
+ # coding: utf-8
2
+
3
+ require 'esoteric'
4
+ require 'esoteric/tetete/parser'
5
+
6
+ module Esoteric
7
+ module Tetete
8
+ VERSION = '0.0.1'
9
+ end
10
+ 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(esm, logger=nil)
10
- new(esm,logger).run
7
+ def self.run(ast, logger=nil)
8
+ new(ast, logger).run
11
9
  end
12
10
 
13
- def initialize(esm, logger=nil)
14
- @insns = parse(esm)
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
- step while @pc < @insns.size
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
- insn, arg = *@insns[@pc]
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,10 @@
1
+ # coding: utf-8
2
+
3
+ require 'esoteric'
4
+ require 'esoteric/whitespace/parser'
5
+
6
+ module Esoteric
7
+ module Whitespace
8
+ VERSION = '0.0.2'
9
+ end
10
+ 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