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 CHANGED
@@ -2,3 +2,10 @@
2
2
 
3
3
  * initial release
4
4
 
5
+ == 0.0.2 / 2009-02-20
6
+
7
+ * コンパイラではないので、CompilerクラスをParserクラスに名称変更
8
+ * Brainf*ckのパーサを追加
9
+ * てってってーのパーサを追加 [http://kugyu.info/?page=%A5%D7%A5%ED%A5%B0%A5%E9%A5%E0%B8%C0%B8%EC%A1%D6%A4%C6%A4%C3%A4%C6%A4%C3%A4%C6%A1%BC%A1%D7]
10
+ * 中間コードを似非アセンブラからParseTreeの抽象構文木相当のものに変更
11
+ * Esoteric LanguageからRubyスクリプトを作るツールesocを追加
data/README CHANGED
@@ -5,14 +5,10 @@ by faultier <roteshund+github@gmail.com>
5
5
 
6
6
  == Description
7
7
 
8
- Esoteric language compilers and virtual machine
8
+ Esoteric language parsers/compilers and virtual machine
9
9
 
10
10
  == Installation
11
11
 
12
- === Archive Installation
13
-
14
- rake install
15
-
16
12
  === Gem Installation
17
13
 
18
14
  gem install faultier-esoteric
@@ -25,10 +21,17 @@ Hosted by GitHub[http://github.com/faultier/esoteric/tree/master]
25
21
 
26
22
  == Features/Problems
27
23
 
24
+ * Whitespace parser
25
+ * DT parser
26
+ * Brainf*ck parser
27
+ * てってってー parser [http://kugyu.info/?page=%A5%D7%A5%ED%A5%B0%A5%E9%A5%E0%B8%C0%B8%EC%A1%D6%A4%C6%A4%C3%A4%C6%A4%C3%A4%C6%A1%BC%A1%D7]
28
+ * Esoteric Language compiler (単に実行可能なRubyスクリプトを生成するだけのツール)
29
+ * Ruby AST を実行するVM (できてない)
28
30
  * しょうもない言語ばかりなのが一番の問題
29
31
 
30
- == Synopsis
32
+ == Usaeg
31
33
 
34
+ そのうち気が向いたら書く
32
35
 
33
36
  == Copyright
34
37
 
data/Rakefile CHANGED
@@ -14,11 +14,11 @@ include FileUtils
14
14
  NAME = "esoteric"
15
15
  AUTHOR = "faultier"
16
16
  EMAIL = "roteshund+github@gmail.com"
17
- DESCRIPTION = ""
17
+ DESCRIPTION = "Esoteric langage compilers and virtual machines"
18
18
  HOMEPATH = "http://blog.livedoor.jp/faultier/"
19
- BIN_FILES = %w(esm whitespace dt)
19
+ BIN_FILES = %w(bf dt esoc tetete ws)
20
20
 
21
- VERS = Esoteric::VERSION::STRING
21
+ VERS = Esoteric::VERSION
22
22
  REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
23
23
  CLEAN.include ['**/.*.sw?', '*.gem', '.config']
24
24
  RDOC_OPTS = [
@@ -50,6 +50,8 @@ spec = Gem::Specification.new do |s|
50
50
  s.require_path = "lib"
51
51
  #s.autorequire = ""
52
52
 
53
+ s.add_dependency('ruby2ruby', '>= 1.2.2')
54
+
53
55
  s.required_ruby_version = '>= 1.8.6'
54
56
 
55
57
  s.files = %w(README ChangeLog Rakefile) +
data/bin/bf ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: filetype=ruby fileencoding=utf-8 :
3
+
4
+ require 'esoteric/brainfuck'
5
+ require 'esoteric/runner'
6
+ require 'esoteric/easyvm'
7
+ $esoteric_bin_version = "esoteric #{Esoteric::VERSION}, Brainf*ck #{Esoteric::Brainfuck::VERSION}"
8
+ source, options = Esoteric::Runner.parse_option
9
+ Esoteric::Runner.run(source, Esoteric::Brainfuck::Parser, Esoteric::EasyVM, options)
data/bin/dt CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
  # vim: filetype=ruby fileencoding=utf-8 :
3
3
 
4
- require 'esoteric'
5
- require 'esoteric/compiler/dt'
6
- ESOTERIC_BIN_VERSION = Esoteric::Compiler::DT::VERSION unless defined?(ESOTERIC_BIN_VERSION)
7
-
4
+ require 'esoteric/dt'
8
5
  require 'esoteric/runner'
9
- Esoteric::Runner.run($source, Esoteric::Compiler::DT, Esoteric::VM, $options)
6
+ require 'esoteric/easyvm'
7
+ $esoteric_bin_version = "esoteric #{Esoteric::VERSION}, DT #{Esoteric::DT::VERSION}"
8
+ source, options = Esoteric::Runner.parse_option
9
+ Esoteric::Runner.run(source, Esoteric::DT::Parser, Esoteric::EasyVM, options)
data/bin/esoc ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: filetype=ruby fileencoding=utf-8
3
+
4
+ require 'esoteric/brainfuck'
5
+ require 'esoteric/dt'
6
+ require 'esoteric/tetete'
7
+ require 'esoteric/whitespace'
8
+ require 'optparse'
9
+ require 'ruby2ruby'
10
+
11
+ options = {
12
+ :outfile => 'eso.out',
13
+ }
14
+ OptionParser.new { |opt|
15
+ opt.on('-o FILENAME') { |v| options[:outfile] = v }
16
+ opt.on('-l LANGNAME', '--language=LANGNAME') { |v| options[:language] = v }
17
+ opt.on('-v', '--version') {
18
+ puts <<-"EOS"
19
+ esoteric #{Esoteric::VERSION}
20
+ ruby2ruby #{Ruby2Ruby::VERSION}
21
+ EOS
22
+ exit 0
23
+ }
24
+
25
+ opt.parse!(ARGV)
26
+ }
27
+
28
+ options[:language] ||= case ARGV.first
29
+ when /\.bf\z/ then 'brainfuck'
30
+ when /\.dt\z/ then 'dt'
31
+ when /\.ttt\z/ then 'てってってー'
32
+ when /\.ws\z/ then 'whitespace'
33
+ end
34
+
35
+ parser = case options[:language]
36
+ when /\A(brainf[u*]ck|bf)\z/ then Esoteric::Brainfuck::Parser
37
+ when 'dt' then Esoteric::DT::Parser
38
+ when 'てってってー','tetete' then Esoteric::Tetete::Parser
39
+ when 'whitespace','ws' then Esoteric::Whitespace::Parser
40
+ end
41
+
42
+ ast = parser.parse(ARGF.read)
43
+ processor = Ruby2Ruby.new
44
+
45
+ open(options[:outfile], 'w+') { |f|
46
+ f.print %(#!/usr/bin/env ruby\n# coding: utf-8\n#{processor.process(Sexp.from_array(ast))})
47
+ f.chmod 0755
48
+ }
data/bin/tetete ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: fileencoding=utf-8 filetype=ruby :
3
+
4
+ require 'esoteric/tetete'
5
+ require 'esoteric/easyvm'
6
+ require 'esoteric/runner'
7
+ $esoteric_bin_version = "esoteric #{Esoteric::VERSION}, てってってー #{Esoteric::Tetete::VERSION}"
8
+ source, options = Esoteric::Runner.parse_option
9
+ Esoteric::Runner.run(source, Esoteric::Tetete::Parser, Esoteric::EasyVM, options)
data/bin/ws ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: filetype=ruby fileencoding=utf-8 :
3
+
4
+ require 'esoteric/whitespace'
5
+ require 'esoteric/runner'
6
+ require 'esoteric/easyvm'
7
+ $esoteric_bin_version = "esoteric #{Esoteric::VERSION}, Whitespace #{Esoteric::Whitespace::VERSION}"
8
+ source, options = Esoteric::Runner.parse_option
9
+ Esoteric::Runner.run(source, Esoteric::Whitespace::Parser, Esoteric::EasyVM, options)
data/lib/esoteric.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- require 'esoteric/version'
4
- require 'esoteric/compiler'
3
+ require 'esoteric/parser'
5
4
  require 'esoteric/vm'
6
5
 
7
6
  module Esoteric
7
+ VERSION = '0.0.2'
8
8
  end
@@ -0,0 +1,10 @@
1
+ # coding: utf-8
2
+
3
+ require 'esoteric'
4
+ require 'esoteric/brainfuck/parser'
5
+
6
+ module Esoteric
7
+ module Brainfuck
8
+ VERSION = '0.0.1'
9
+ end
10
+ end
@@ -0,0 +1,71 @@
1
+ # coding: utf-8
2
+
3
+ module Esoteric
4
+ module Brainfuck
5
+ class Parser < Esoteric::Parser
6
+ def initialize(src, logger = nil)
7
+ super
8
+ @p = 0
9
+ @ast = [
10
+ exp_gasgn(:pc, exp_literal(0)),
11
+ exp_gasgn(:tape, [:array]),
12
+ [:defn, :get_value,
13
+ [:scope,
14
+ [:block,
15
+ [:args],
16
+ [:op_asgn1,
17
+ [:gvar, :$tape],
18
+ [:arglist, [:gvar, :$pc]],
19
+ :"||",
20
+ [:lit, 0]]]]],
21
+ [:defn, :set_value,
22
+ [:scope,
23
+ [:block,
24
+ [:args, :val],
25
+ [:call,
26
+ [:gvar, :$tape],
27
+ :[]=,
28
+ [:arglist,
29
+ [:gvar, :$pc],
30
+ [:lvar, :val]]]]]]
31
+ ]
32
+ end
33
+
34
+ def normalize(src)
35
+ src.gsub(/[^><+-.,\[\]]/, '').split(//)
36
+ end
37
+
38
+ def next_token
39
+ @src[@p]
40
+ end
41
+
42
+ def process
43
+ exp = case next_token
44
+ when '>' then exp_opgasgn :pc, :+, exp_literal(1)
45
+ when '<' then exp_opgasgn :pc, :+, exp_literal(-1)
46
+ when '+' then
47
+ exp_fcall :set_value, exp_mcall( exp_fcall(:get_value), :+, exp_literal(1))
48
+ when '-' then
49
+ exp_fcall :set_value, exp_mcall( exp_fcall(:get_value), :+, exp_literal(-1))
50
+ when '.' then
51
+ exp_fcall :print, exp_mcall( exp_fcall(:get_value), :chr )
52
+ when ',' then
53
+ exp_fcall :set_value, exp_mcall( exp_fcall(:getc), :ord )
54
+ when '[' then
55
+ @p += 1
56
+ [:until,
57
+ [:call,
58
+ [:call, nil,:get_value, [:arglist]],
59
+ :==,
60
+ [:arglist, [:lit, 0]]],
61
+ process_until(lambda { |t| t == ']' }),
62
+ nil
63
+ ]
64
+ when ']' then raise LoopInterrapt
65
+ end
66
+ @p += 1
67
+ exp
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,10 @@
1
+ # coding: utf-8
2
+
3
+ require 'esoteric'
4
+ require 'esoteric/dt/parser'
5
+
6
+ module Esoteric
7
+ module DT
8
+ VERSION = '0.0.2'
9
+ end
10
+ end
@@ -0,0 +1,147 @@
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 DT
12
+ class Parser < Esoteric::Parser
13
+ NVAL = /((?:ど|童貞ちゃうわっ!)+)…/
14
+ LVAL = NVAL
15
+ PUSH = /どど#{NVAL}/
16
+ DUP = /ど…ど/
17
+ COPY = /ど童貞ちゃうわっ!ど#{NVAL}/
18
+ SWAP = /ど…童貞ちゃうわっ!/
19
+ DISCARD = /ど……/
20
+ SLIDE = /ど童貞ちゃうわっ!…#{NVAL}/
21
+ ADD = /童貞ちゃうわっ!どどど/
22
+ SUB = /童貞ちゃうわっ!どど童貞ちゃうわっ!/
23
+ MUL = /童貞ちゃうわっ!どど…/
24
+ DIV = /童貞ちゃうわっ!ど童貞ちゃうわっ!ど/
25
+ MOD = /童貞ちゃうわっ!ど童貞ちゃうわっ!童貞ちゃうわっ!/
26
+ HWRITE = /童貞ちゃうわっ!童貞ちゃうわっ!ど/
27
+ HREAD = /童貞ちゃうわっ!童貞ちゃうわっ!童貞ちゃうわっ!/
28
+ LABEL = /…どど#{LVAL}/
29
+ CALL = /…ど童貞ちゃうわっ!#{LVAL}/
30
+ JUMP = /…ど…#{LVAL}/
31
+ JUMPZ = /…童貞ちゃうわっ!ど#{LVAL}/
32
+ JUMPN = /…童貞ちゃうわっ!童貞ちゃうわっ!#{LVAL}/
33
+ RETURN = /…童貞ちゃうわっ!…/
34
+ EXIT = /………/
35
+ COUT = /童貞ちゃうわっ!…どど/
36
+ NOUT = /童貞ちゃうわっ!…ど童貞ちゃうわっ!/
37
+ CIN = /童貞ちゃうわっ!…童貞ちゃうわっ!ど/
38
+ NIN = /童貞ちゃうわっ!…童貞ちゃうわっ!童貞ちゃうわっ!/
39
+
40
+ def initialize(src, logger=nil)
41
+ super
42
+ @s = StringScanner.new(@src)
43
+ @ast = [
44
+ [:gasgn, :$stack, [:array]],
45
+ [:gasgn, :$heap, [:hash]],
46
+ ]
47
+ end
48
+
49
+ def parse
50
+ begin
51
+ loop do
52
+ exp = process
53
+ next unless !!exp
54
+ if exp.first == :defn
55
+ @ast.unshift exp
56
+ else
57
+ @ast.push exp
58
+ end
59
+ end
60
+ rescue ProcessInterrapt
61
+ # do nothing
62
+ end
63
+ @ast.unshift :block
64
+ # require 'pp'; pp @ast
65
+ @ast
66
+ end
67
+
68
+
69
+ private
70
+
71
+ def normalize(src)
72
+ normalized = ''
73
+ normalized << $1 while src.sub!(/(ど|童貞ちゃうわっ!|…)/, '*')
74
+ normalized
75
+ end
76
+
77
+ def process
78
+ case
79
+ when @s.eos? then raise ProcessInterrapt
80
+ when @s.scan(PUSH) then exp_push exp_literal(numeric(@s[1]))
81
+ when @s.scan(DUP) then exp_push exp_gvarcall(:stack, :last)
82
+ when @s.scan(COPY) then exp_push exp_gvarcall(:stack, :[], exp_literal(-(numeric(@s[1]))-1))
83
+ when @s.scan(SWAP) then exp_push exp_pop, exp_pop
84
+ when @s.scan(DISCARD) then exp_pop
85
+ when @s.scan(SLIDE) then
86
+ exp_block exp_lasgn(:top, exp_gvarcall(:stack, :pop)), exp_mcall(exp_literal(numeric(@s[1])), :times, exp_pop), exp_push(exp_lvar(:top))
87
+ when @s.scan(ADD) 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(SUB) then
90
+ exp_block exp_lasgn(:y, exp_pop), exp_lasgn(:x, exp_pop), exp_mcallpush(exp_lvar(:x), :-, exp_lvar(:y))
91
+ when @s.scan(MUL) then
92
+ exp_block exp_lasgn(:y, exp_pop), exp_lasgn(:x, exp_pop), exp_mcallpush(exp_lvar(:x), :*, exp_lvar(:y))
93
+ when @s.scan(DIV) then
94
+ exp_block exp_lasgn(:y, exp_pop), exp_lasgn(:x, exp_pop), exp_mcallpush(exp_lvar(:x), :/, exp_lvar(:y))
95
+ when @s.scan(MOD) then
96
+ exp_block exp_lasgn(:y, exp_pop), exp_lasgn(:x, exp_pop), exp_mcallpush(exp_lvar(:x), :%, exp_lvar(:y))
97
+ when @s.scan(HWRITE) then
98
+ exp_block exp_lasgn(:val, exp_pop), exp_lasgn(:addr, exp_pop), exp_gvarcall(:heap, :[]=, exp_lvar(:addr), exp_lvar(:val))
99
+ when @s.scan(HREAD) then
100
+ exp_block exp_lasgn(:addr, exp_pop), exp_push(exp_gvarcall(:heap, :[], exp_lvar(:addr)))
101
+ when @s.scan(LABEL) then
102
+ defn(string(@s[1]).intern) { process_until( lambda {|*args| @s.eos? || !!@s.match?(RETURN)} ) }
103
+ when @s.scan(CALL) then exp_fcall(string(@s[1]).intern)
104
+ when @s.scan(JUMP)
105
+ exp_if exp_literal(true), exp_fcall(string(@s[1]).intern), process_until( lambda {|*args| @s.eos? || !!@s.match?(RETURN)} )
106
+ when @s.scan(JUMPZ)
107
+ exp_if exp_mcall(pop, :==, exp_literal(0)), exp_fcall(string(@s[1]).intern), process_until( lambda {|*args| @s.eos? || !!@s.match?(RETURN)} )
108
+ when @s.scan(JUMPN)
109
+ exp_if exp_mcall(pop, :<, exp_literal(0)), exp_fcall(string(@s[1]).intern), process_until( lambda {|*args| @s.eos? || !!@s.match?(RETURN)} )
110
+ when @s.scan(RETURN) then nil
111
+ when @s.scan(EXIT) then exp_fcall :exit, exp_literal(0)
112
+ when @s.scan(COUT) then exp_gvarcall :stdout, :print, exp_mcall(exp_pop, :chr)
113
+ when @s.scan(NOUT) then exp_gvarcall :stdout, :print, exp_mcall(exp_pop, :to_i)
114
+ when @s.scan(CIN) then exp_mcallpush exp_gvarcall(:stdin, :getc), :ord
115
+ when @s.scan(NIN) then exp_mcallpush exp_gvarcall(:stdin, :getc), :to_i
116
+ else raise SyntaxError
117
+ end
118
+ end
119
+
120
+ def exp_pop
121
+ exp_gvarcall :stack, :pop
122
+ end
123
+
124
+ def exp_push(value)
125
+ exp_gvarcall :stack, :push, value
126
+ end
127
+
128
+ def exp_mcallpush(receiver, method, *args)
129
+ exp_push exp_mcall(receiver, method, *args)
130
+ end
131
+
132
+ def numeric(value)
133
+ raise ArgumentError if "#{value}…" !~ /\A#{NVAL}\z/
134
+ n = value.sub(/\Aど/, '+').
135
+ sub(/\A童貞ちゃうわっ!/, '-').
136
+ gsub(/ど/, '0').
137
+ gsub(/童貞ちゃうわっ!/, '1')
138
+ n.to_i(2)
139
+ end
140
+
141
+ def string(value)
142
+ raise ArgumentError if "#{value}…" !~ /\A#{LVAL}\z/
143
+ value.sub(/ど/, 'd').sub(/童貞ちゃうわっ!/, 'D')
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,25 @@
1
+ # config: utf-8
2
+
3
+ require 'logger'
4
+ require 'ruby2ruby'
5
+
6
+ module Esoteric
7
+ class EasyVM
8
+ def self.run(ast, logger = nil)
9
+ new(ast, logger).run
10
+ end
11
+
12
+ def initialize(ast, logger = nil)
13
+ @ast = Sexp.from_array(ast)
14
+ @processor = Ruby2Ruby.new
15
+ end
16
+
17
+ def run
18
+ # これじゃああんまりにもあんまりなので
19
+ # 抽象構文木を実行できるVMをあとで作る
20
+ # puts code = @processor.process(@ast)
21
+ # eval code
22
+ eval @processor.process(@ast)
23
+ end
24
+ end
25
+ end