faultier-esoteric 0.0.0
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 +4 -0
- data/README +37 -0
- data/Rakefile +134 -0
- data/bin/dt +9 -0
- data/bin/esm +8 -0
- data/bin/whitespace +9 -0
- data/lib/esoteric/compiler/dt.rb +101 -0
- data/lib/esoteric/compiler/whitespace.rb +94 -0
- data/lib/esoteric/compiler.rb +60 -0
- data/lib/esoteric/runner.rb +38 -0
- data/lib/esoteric/version.rb +16 -0
- data/lib/esoteric/vm.rb +97 -0
- data/lib/esoteric.rb +8 -0
- data/spec/esoteric_spec.rb +1 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +7 -0
- metadata +84 -0
data/ChangeLog
ADDED
data/README
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
= esoteric
|
3
|
+
|
4
|
+
by faultier <roteshund+github@gmail.com>
|
5
|
+
|
6
|
+
== Description
|
7
|
+
|
8
|
+
Esoteric language compilers and virtual machine
|
9
|
+
|
10
|
+
== Installation
|
11
|
+
|
12
|
+
=== Archive Installation
|
13
|
+
|
14
|
+
rake install
|
15
|
+
|
16
|
+
=== Gem Installation
|
17
|
+
|
18
|
+
gem install faultier-esoteric
|
19
|
+
|
20
|
+
=== Git Repository
|
21
|
+
|
22
|
+
Hosted by GitHub[http://github.com/faultier/esoteric/tree/master]
|
23
|
+
|
24
|
+
git clone git://github.com/faultier/esoteric.git
|
25
|
+
|
26
|
+
== Features/Problems
|
27
|
+
|
28
|
+
* しょうもない言語ばかりなのが一番の問題
|
29
|
+
|
30
|
+
== Synopsis
|
31
|
+
|
32
|
+
|
33
|
+
== Copyright
|
34
|
+
|
35
|
+
Author:: faultier <roteshund+github@gmail.com>
|
36
|
+
Copyright:: Copyright (c) 2009 faultier
|
37
|
+
License:: Ruby's
|
data/Rakefile
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
$:.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake'
|
5
|
+
require 'rake/clean'
|
6
|
+
require 'rake/packagetask'
|
7
|
+
require 'rake/gempackagetask'
|
8
|
+
require 'rake/rdoctask'
|
9
|
+
require 'rake/contrib/sshpublisher'
|
10
|
+
require 'fileutils'
|
11
|
+
require 'esoteric'
|
12
|
+
include FileUtils
|
13
|
+
|
14
|
+
NAME = "esoteric"
|
15
|
+
AUTHOR = "faultier"
|
16
|
+
EMAIL = "roteshund+github@gmail.com"
|
17
|
+
DESCRIPTION = ""
|
18
|
+
HOMEPATH = "http://blog.livedoor.jp/faultier/"
|
19
|
+
BIN_FILES = %w(esm whitespace dt)
|
20
|
+
|
21
|
+
VERS = Esoteric::VERSION::STRING
|
22
|
+
REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
|
23
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
24
|
+
RDOC_OPTS = [
|
25
|
+
'--title', "#{NAME} documentation",
|
26
|
+
"--charset", "utf-8",
|
27
|
+
"--opname", "index.html",
|
28
|
+
"--line-numbers",
|
29
|
+
"--main", "README",
|
30
|
+
"--inline-source",
|
31
|
+
]
|
32
|
+
|
33
|
+
task :default => [:spec]
|
34
|
+
task :package => [:clean]
|
35
|
+
|
36
|
+
spec = Gem::Specification.new do |s|
|
37
|
+
s.name = NAME
|
38
|
+
s.version = VERS
|
39
|
+
s.platform = Gem::Platform::RUBY
|
40
|
+
s.has_rdoc = true
|
41
|
+
s.extra_rdoc_files = ["README", "ChangeLog"]
|
42
|
+
s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)/']
|
43
|
+
s.summary = DESCRIPTION
|
44
|
+
s.description = DESCRIPTION
|
45
|
+
s.author = AUTHOR
|
46
|
+
s.email = EMAIL
|
47
|
+
s.homepage = HOMEPATH
|
48
|
+
s.executables = BIN_FILES
|
49
|
+
s.bindir = "bin"
|
50
|
+
s.require_path = "lib"
|
51
|
+
#s.autorequire = ""
|
52
|
+
|
53
|
+
s.required_ruby_version = '>= 1.8.6'
|
54
|
+
|
55
|
+
s.files = %w(README ChangeLog Rakefile) +
|
56
|
+
Dir.glob("{bin,doc,spec,lib,templates,generator,extras,website,script}/**/*") +
|
57
|
+
Dir.glob("ext/**/*.{h,c,rb}") +
|
58
|
+
Dir.glob("examples/**/*.rb") +
|
59
|
+
Dir.glob("tools/*.rb") +
|
60
|
+
|
61
|
+
s.extensions = FileList["ext/**/extconf.rb"].to_a
|
62
|
+
end
|
63
|
+
|
64
|
+
Rake::GemPackageTask.new(spec) do |p|
|
65
|
+
p.need_tar = true
|
66
|
+
p.gem_spec = spec
|
67
|
+
end
|
68
|
+
|
69
|
+
task :install do
|
70
|
+
name = "#{NAME}-#{VERS}.gem"
|
71
|
+
sh %{rake package}
|
72
|
+
sh %{sudo gem install pkg/#{name}}
|
73
|
+
end
|
74
|
+
|
75
|
+
task :uninstall => [:clean] do
|
76
|
+
sh %{sudo gem uninstall #{NAME}}
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
Rake::RDocTask.new do |rdoc|
|
81
|
+
rdoc.rdoc_dir = 'html'
|
82
|
+
rdoc.options += RDOC_OPTS
|
83
|
+
rdoc.template = "resh"
|
84
|
+
#rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
85
|
+
if ENV['DOC_FILES']
|
86
|
+
rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
|
87
|
+
else
|
88
|
+
rdoc.rdoc_files.include('README', 'ChangeLog')
|
89
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
90
|
+
rdoc.rdoc_files.include('ext/**/*.c')
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
desc 'Show information about the gem.'
|
95
|
+
task :debug_gem do
|
96
|
+
puts spec.to_ruby
|
97
|
+
end
|
98
|
+
|
99
|
+
desc 'Update gem spec'
|
100
|
+
task :gemspec do
|
101
|
+
open("#{NAME}.gemspec", 'w').write spec.to_ruby
|
102
|
+
end
|
103
|
+
|
104
|
+
require 'spec/rake/spectask'
|
105
|
+
|
106
|
+
SPEC_DIR = 'spec'
|
107
|
+
SPEC_OPTS = [
|
108
|
+
'--options', "#{SPEC_DIR}/spec.opts",
|
109
|
+
]
|
110
|
+
SPEC_FILES = FileList["#{SPEC_DIR}/**/*_spec.rb"]
|
111
|
+
RCOV_OPTS = [
|
112
|
+
'--exclude', 'spec',
|
113
|
+
]
|
114
|
+
SPEC_CONTEXT = lambda {|t|
|
115
|
+
t.spec_files = SPEC_FILES
|
116
|
+
t.spec_opts = SPEC_OPTS
|
117
|
+
t.warning = false
|
118
|
+
t.libs = %w(lib)
|
119
|
+
}
|
120
|
+
|
121
|
+
desc "Run specs"
|
122
|
+
Spec::Rake::SpecTask.new { |t| SPEC_CONTEXT.call(t) }
|
123
|
+
|
124
|
+
Spec::Rake::SpecTask.new :rcov do |t|
|
125
|
+
SPEC_CONTEXT.call(t)
|
126
|
+
t.rcov = true
|
127
|
+
t.rcov_opts = RCOV_OPTS
|
128
|
+
end
|
129
|
+
|
130
|
+
desc "Run specs and rcov"
|
131
|
+
task 'spec:rcov' => :rcov
|
132
|
+
|
133
|
+
namespace :spec do
|
134
|
+
end
|
data/bin/dt
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim: filetype=ruby fileencoding=utf-8 :
|
3
|
+
|
4
|
+
require 'esoteric'
|
5
|
+
require 'esoteric/compiler/dt'
|
6
|
+
ESOTERIC_BIN_VERSION = Esoteric::Compiler::DT::VERSION unless defined?(ESOTERIC_BIN_VERSION)
|
7
|
+
|
8
|
+
require 'esoteric/runner'
|
9
|
+
Esoteric::Runner.run($source, Esoteric::Compiler::DT, Esoteric::VM, $options)
|
data/bin/esm
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim: filetype=ruby fileencoding=utf-8 :
|
3
|
+
|
4
|
+
require 'esoteric'
|
5
|
+
ESOTERIC_BIN_VERSION = "essembler #{Esoteric::VERSION::STRING}" unless defined?(ESOTERIC_BIN_VERSION)
|
6
|
+
|
7
|
+
require 'esoteric/runner'
|
8
|
+
Esoteric::Runner.run($source, nil, Esoteric::VM, $opitons)
|
data/bin/whitespace
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim: filetype=ruby fileencoding=utf-8 :
|
3
|
+
|
4
|
+
require 'esoteric'
|
5
|
+
require 'esoteric/compiler/whitespace'
|
6
|
+
ESOTERIC_BIN_VERSION = Esoteric::Compiler::Whitespace::VERSION unless defined?(ESOTERIC_BIN_VERSION)
|
7
|
+
|
8
|
+
require 'esoteric/runner'
|
9
|
+
Esoteric::Runner.run($source, Esoteric::Compiler::Whitespace, Esoteric::VM, $options)
|
@@ -0,0 +1,101 @@
|
|
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 Compiler
|
12
|
+
class DT < Base
|
13
|
+
VERSION = "#{Esoteric::VERSION::SUMMARY}, dt 0.0.1"
|
14
|
+
|
15
|
+
NVAL = /((?:ど|童貞ちゃうわっ!)+)…/
|
16
|
+
LVAL = NVAL
|
17
|
+
PUSH = /どど#{NVAL}/
|
18
|
+
DUP = /ど…ど/
|
19
|
+
COPY = /ど童貞ちゃうわっ!ど#{NVAL}/
|
20
|
+
SWAP = /ど…童貞ちゃうわっ!/
|
21
|
+
DISCARD = /ど……/
|
22
|
+
SLIDE = /ど童貞ちゃうわっ!…#{NVAL}/
|
23
|
+
ADD = /童貞ちゃうわっ!どどど/
|
24
|
+
SUB = /童貞ちゃうわっ!どど童貞ちゃうわっ!/
|
25
|
+
MUL = /童貞ちゃうわっ!どど…/
|
26
|
+
DIV = /童貞ちゃうわっ!ど童貞ちゃうわっ!ど/
|
27
|
+
MOD = /童貞ちゃうわっ!ど童貞ちゃうわっ!童貞ちゃうわっ!/
|
28
|
+
HWRITE = /童貞ちゃうわっ!童貞ちゃうわっ!ど/
|
29
|
+
HREAD = /童貞ちゃうわっ!童貞ちゃうわっ!童貞ちゃうわっ!/
|
30
|
+
LABEL = /…どど#{LVAL}/
|
31
|
+
CALL = /…ど童貞ちゃうわっ!#{LVAL}/
|
32
|
+
JUMP = /…ど…#{LVAL}/
|
33
|
+
JUMPZ = /…童貞ちゃうわっ!ど#{LVAL}/
|
34
|
+
JUMPN = /…童貞ちゃうわっ!童貞ちゃうわっ!#{LVAL}/
|
35
|
+
RETURN = /…童貞ちゃうわっ!…/
|
36
|
+
EXIT = /………/
|
37
|
+
COUT = /童貞ちゃうわっ!…どど/
|
38
|
+
NOUT = /童貞ちゃうわっ!…ど童貞ちゃうわっ!/
|
39
|
+
CIN = /童貞ちゃうわっ!…童貞ちゃうわっ!ど/
|
40
|
+
NIN = /童貞ちゃうわっ!…童貞ちゃうわっ!童貞ちゃうわっ!/
|
41
|
+
|
42
|
+
def initialize(src, logger=nil)
|
43
|
+
super
|
44
|
+
@s = StringScanner.new(@src)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def normalize(src)
|
50
|
+
normalized = ''
|
51
|
+
normalized << $1 while src.sub!(/(ど|童貞ちゃうわっ!|…)/, '*')
|
52
|
+
normalized
|
53
|
+
end
|
54
|
+
|
55
|
+
def step
|
56
|
+
case
|
57
|
+
when @s.eos? then nil
|
58
|
+
when @s.scan(PUSH) then command :push, @s[1]
|
59
|
+
when @s.scan(DUP) then command :dup
|
60
|
+
when @s.scan(COPY) then command :copy, @s[1]
|
61
|
+
when @s.scan(SWAP) then command :swap
|
62
|
+
when @s.scan(DISCARD) then command :discard
|
63
|
+
when @s.scan(SLIDE) then command :slide, @s[1]
|
64
|
+
when @s.scan(ADD) then command :add
|
65
|
+
when @s.scan(SUB) then command :sub
|
66
|
+
when @s.scan(MUL) then command :mul
|
67
|
+
when @s.scan(DIV) then command :div
|
68
|
+
when @s.scan(MOD) then command :mod
|
69
|
+
when @s.scan(HWRITE) then command :hwrite
|
70
|
+
when @s.scan(HREAD) then command :hread
|
71
|
+
when @s.scan(LABEL) then command :label, @s[1]
|
72
|
+
when @s.scan(CALL) then command :call, @s[1]
|
73
|
+
when @s.scan(JUMP) then command :jump, @s[1]
|
74
|
+
when @s.scan(JUMPZ) then command :jumpz, @s[1]
|
75
|
+
when @s.scan(JUMPN) then command :jumpn, @s[1]
|
76
|
+
when @s.scan(RETURN) then command :return
|
77
|
+
when @s.scan(EXIT) then command :exit
|
78
|
+
when @s.scan(COUT) then command :cout
|
79
|
+
when @s.scan(NOUT) then command :nout
|
80
|
+
when @s.scan(CIN) then command :cin
|
81
|
+
when @s.scan(NIN) then command :nin
|
82
|
+
else raise ::Esoteric::SyntaxError
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def numeric(value)
|
87
|
+
raise ArgumentError if "#{value}…" !~ /\A#{NVAL}\z/
|
88
|
+
n = value.sub(/\Aど/, '+').
|
89
|
+
sub(/\A童貞ちゃうわっ!/, '-').
|
90
|
+
gsub(/ど/, '0').
|
91
|
+
gsub(/童貞ちゃうわっ!/, '1')
|
92
|
+
n.to_i(2)
|
93
|
+
end
|
94
|
+
|
95
|
+
def string(value)
|
96
|
+
raise ArgumentError if "#{value}…" !~ /\A#{LVAL}\z/
|
97
|
+
value.sub(/ど/, '0').sub(/童貞ちゃうわっ!/, '1')
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'strscan'
|
4
|
+
|
5
|
+
module Esoteric
|
6
|
+
module Compiler
|
7
|
+
class Whitespace < Base
|
8
|
+
VERSION = "#{Esoteric::VERSION::SUMMARY}, whitespace 0.0.1"
|
9
|
+
|
10
|
+
NVAL = /([ \t]+)\n/
|
11
|
+
LVAL = NVAL
|
12
|
+
PUSH = / #{NVAL}/
|
13
|
+
DUP = / \n /
|
14
|
+
COPY = / \t #{NVAL}/
|
15
|
+
SWAP = / \n\t/
|
16
|
+
DISCARD = / \n\n/
|
17
|
+
SLIDE = / \t\n#{NVAL}/
|
18
|
+
ADD = /\t /
|
19
|
+
SUB = /\t \t/
|
20
|
+
MUL = /\t \n/
|
21
|
+
DIV = /\t \t /
|
22
|
+
MOD = /\t \t\t/
|
23
|
+
HWRITE = /\t\t /
|
24
|
+
HREAD = /\t\t\t/
|
25
|
+
LABEL = /\n #{LVAL}/
|
26
|
+
CALL = /\n \t#{LVAL}/
|
27
|
+
JUMP = /\n \n#{LVAL}/
|
28
|
+
JUMPZ = /\n\t #{LVAL}/
|
29
|
+
JUMPN = /\n\t\t#{LVAL}/
|
30
|
+
RETURN = /\n\t\n/
|
31
|
+
EXIT = /\n\n\n/
|
32
|
+
COUT = /\t\n /
|
33
|
+
NOUT = /\t\n \t/
|
34
|
+
CIN = /\t\n\t /
|
35
|
+
NIN = /\t\n\t\t/
|
36
|
+
|
37
|
+
def initialize(src,logger=nil)
|
38
|
+
super
|
39
|
+
@s = StringScanner.new(@src)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def normalize(src)
|
45
|
+
src.gsub(/[^ \t\n]/, '')
|
46
|
+
end
|
47
|
+
|
48
|
+
def step
|
49
|
+
case
|
50
|
+
when @s.eos? then nil
|
51
|
+
when @s.scan(PUSH) then command :push, @s[1]
|
52
|
+
when @s.scan(DUP) then command :dup
|
53
|
+
when @s.scan(COPY) then command :copy, @s[1]
|
54
|
+
when @s.scan(SWAP) then command :swap
|
55
|
+
when @s.scan(DISCARD) then command :discard
|
56
|
+
when @s.scan(SLIDE) then command :slide, @s[1]
|
57
|
+
when @s.scan(ADD) then command :add
|
58
|
+
when @s.scan(SUB) then command :sub
|
59
|
+
when @s.scan(MUL) then command :mul
|
60
|
+
when @s.scan(DIV) then command :div
|
61
|
+
when @s.scan(MOD) then command :mod
|
62
|
+
when @s.scan(HWRITE) then command :hwrite
|
63
|
+
when @s.scan(HREAD) then command :hread
|
64
|
+
when @s.scan(LABEL) then command :label, @s[1]
|
65
|
+
when @s.scan(CALL) then command :call, @s[1]
|
66
|
+
when @s.scan(JUMP) then command :jump, @s[1]
|
67
|
+
when @s.scan(JUMPZ) then command :jumpz, @s[1]
|
68
|
+
when @s.scan(JUMPN) then command :jumpn, @s[1]
|
69
|
+
when @s.scan(RETURN) then command :return
|
70
|
+
when @s.scan(EXIT) then command :exit
|
71
|
+
when @s.scan(COUT) then command :cout
|
72
|
+
when @s.scan(NOUT) then command :nout
|
73
|
+
when @s.scan(CIN) then command :cin
|
74
|
+
when @s.scan(NIN) then command :nin
|
75
|
+
else raise SyntaxError
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def numeric(value)
|
80
|
+
raise ArgumentError if "#{value}\n" !~ /\A#{NVAL}\z/
|
81
|
+
n = value.sub(/\A /, '+').
|
82
|
+
sub(/\A\t/, '-').
|
83
|
+
gsub(/ /, '0').
|
84
|
+
gsub(/\t/, '1')
|
85
|
+
n.to_i(2)
|
86
|
+
end
|
87
|
+
|
88
|
+
def string(value)
|
89
|
+
raise ArgumentError if "#{value}\n" !~ /\A#{LVAL}\z/
|
90
|
+
value.gsub(/ /, '0').gsub(/\t/, '1')
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module Esoteric
|
6
|
+
module Compiler
|
7
|
+
class Base
|
8
|
+
def self.compile(src, logger=nil)
|
9
|
+
new(src, logger).compile
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(src, logger=nil)
|
13
|
+
@src = normalize(src)
|
14
|
+
unless @logger = logger
|
15
|
+
@logger = Logger.new(STDOUT)
|
16
|
+
@logger.level = Logger::ERROR
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def compile
|
21
|
+
insns = []
|
22
|
+
while st = step
|
23
|
+
insns.push st
|
24
|
+
end
|
25
|
+
insns.map {|c,a| a.nil? ? c.to_s : "#{c}\t#{a}"}.join("\n")
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def normalize(src)
|
31
|
+
src
|
32
|
+
end
|
33
|
+
|
34
|
+
def step
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def command(name, arg=nil)
|
39
|
+
case name
|
40
|
+
when :push,:copy,:slide then [name, numeric(arg)]
|
41
|
+
when :label,:call,:jump,:jumpz,:jumpn then [name, string(arg)]
|
42
|
+
when :dup,:swap,:discard then [name]
|
43
|
+
when :add,:sub,:mul,:div,:mod then [name]
|
44
|
+
when :hwrite,:hread then [name]
|
45
|
+
when :return,:exit then [name]
|
46
|
+
when :cout,:nout,:cin,:nin then [name]
|
47
|
+
else raise SyntaxError
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def numeric(value)
|
52
|
+
value.to_i
|
53
|
+
end
|
54
|
+
|
55
|
+
def string(value)
|
56
|
+
value.to_s
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module Esoteric
|
6
|
+
class Runner
|
7
|
+
def self.run(source, compiler, vm, options={}, logger=nil)
|
8
|
+
logger ||= Logger.new(STDOUT)
|
9
|
+
logger.level = options[:loglevel] if !!options[:loglevel]
|
10
|
+
if options[:interactive]
|
11
|
+
raise NotImplementedError
|
12
|
+
else
|
13
|
+
esm = !!compiler ? compiler.compile(source) : source
|
14
|
+
if options[:checkonly]
|
15
|
+
puts 'Syntax OK'
|
16
|
+
else
|
17
|
+
vm.run esm, logger
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
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,16 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Esoteric
|
4
|
+
module VERSION
|
5
|
+
unless defined? MAJOR
|
6
|
+
MAJOR = 0
|
7
|
+
MINOR = 0
|
8
|
+
TINY = 1
|
9
|
+
MINESCULE = nil
|
10
|
+
|
11
|
+
STRING = [MAJOR, MINOR, TINY, MINESCULE].compact.join('.')
|
12
|
+
|
13
|
+
SUMMARY = "esoteric #{STRING}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/esoteric/vm.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module Esoteric
|
6
|
+
class ProgramInterrapt < StandardError; end
|
7
|
+
|
8
|
+
class VM
|
9
|
+
def self.run(esm, logger=nil)
|
10
|
+
new(esm,logger).run
|
11
|
+
end
|
12
|
+
|
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 = []
|
21
|
+
unless @logger = logger
|
22
|
+
@logger = Logger.new(STDOUT)
|
23
|
+
@logger.level = Logger::ERROR
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def run
|
28
|
+
step while @pc < @insns.size
|
29
|
+
raise RuntimeError, ':exit missing'
|
30
|
+
rescue ProgramInterrapt => e
|
31
|
+
# successfully exit
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
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
|
+
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]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/lib/esoteric.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__)) + '/spec_helper'
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: faultier-esoteric
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- faultier
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-17 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: ""
|
17
|
+
email: roteshund+github@gmail.com
|
18
|
+
executables:
|
19
|
+
- esm
|
20
|
+
- whitespace
|
21
|
+
- dt
|
22
|
+
extensions: []
|
23
|
+
|
24
|
+
extra_rdoc_files:
|
25
|
+
- README
|
26
|
+
- ChangeLog
|
27
|
+
files:
|
28
|
+
- README
|
29
|
+
- ChangeLog
|
30
|
+
- Rakefile
|
31
|
+
- bin/whitespace
|
32
|
+
- bin/dt
|
33
|
+
- bin/esm
|
34
|
+
- spec/spec_helper.rb
|
35
|
+
- spec/esoteric_spec.rb
|
36
|
+
- spec/spec.opts
|
37
|
+
- lib/esoteric.rb
|
38
|
+
- lib/esoteric
|
39
|
+
- lib/esoteric/version.rb
|
40
|
+
- lib/esoteric/vm.rb
|
41
|
+
- lib/esoteric/compiler
|
42
|
+
- lib/esoteric/compiler/dt.rb
|
43
|
+
- lib/esoteric/compiler/whitespace.rb
|
44
|
+
- lib/esoteric/compiler.rb
|
45
|
+
- lib/esoteric/runner.rb
|
46
|
+
has_rdoc: true
|
47
|
+
homepage: http://blog.livedoor.jp/faultier/
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options:
|
50
|
+
- --title
|
51
|
+
- esoteric documentation
|
52
|
+
- --charset
|
53
|
+
- utf-8
|
54
|
+
- --opname
|
55
|
+
- index.html
|
56
|
+
- --line-numbers
|
57
|
+
- --main
|
58
|
+
- README
|
59
|
+
- --inline-source
|
60
|
+
- --exclude
|
61
|
+
- ^(examples|extras)/
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.8.6
|
69
|
+
version:
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: "0"
|
75
|
+
version:
|
76
|
+
requirements: []
|
77
|
+
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 1.2.0
|
80
|
+
signing_key:
|
81
|
+
specification_version: 2
|
82
|
+
summary: ""
|
83
|
+
test_files: []
|
84
|
+
|