Initial Commit
This commit is contained in:
commit
846b782493
16
backend.rb
Normal file
16
backend.rb
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
class Backend
|
||||||
|
attr_writer :dbg
|
||||||
|
def initialize(code)
|
||||||
|
@code=code
|
||||||
|
@dbg=false
|
||||||
|
@pc=0
|
||||||
|
end
|
||||||
|
def run
|
||||||
|
while true
|
||||||
|
op=@code[@pc]
|
||||||
|
break if op==nil
|
||||||
|
inc=execute(op)
|
||||||
|
@pc+=1 if inc
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
16
compiler.rb
Normal file
16
compiler.rb
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
require_relative "parser.rb"
|
||||||
|
require_relative "interpreter.rb"
|
||||||
|
require_relative "copyprop.rb"
|
||||||
|
require_relative "deadopt.rb"
|
||||||
|
if ARGV[0]!=nil
|
||||||
|
parser=Parser.new(File.read(ARGV[0]))
|
||||||
|
else
|
||||||
|
parser=Parser.new(File.read("prg.lang"))
|
||||||
|
end
|
||||||
|
parser.parse
|
||||||
|
opt=CopyProp.new(parser.output)
|
||||||
|
opt=DeadOpt.new(opt.optimize)
|
||||||
|
out=opt.optimize
|
||||||
|
p out
|
||||||
|
interp=Interpreter.new(out)
|
||||||
|
interp.run
|
4
console
Executable file
4
console
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
require "pry"
|
||||||
|
require_relative "parser.rb"
|
||||||
|
Pry.start()
|
32
copyprop.rb
Normal file
32
copyprop.rb
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
class CopyProp
|
||||||
|
def initialize(code)
|
||||||
|
@code=code
|
||||||
|
end
|
||||||
|
def optimize()
|
||||||
|
table={}
|
||||||
|
i=0
|
||||||
|
@code.clone.each do |quad|
|
||||||
|
if quad[3]==nil and quad[1]=="="
|
||||||
|
table[quad[0]]=quad[2]
|
||||||
|
else
|
||||||
|
if table.has_key? quad[2]
|
||||||
|
while true
|
||||||
|
replace=table[quad[2]]
|
||||||
|
break if replace==nil
|
||||||
|
quad[2]=replace
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if table.has_key? quad[3]
|
||||||
|
while true
|
||||||
|
replace=table[quad[3]]
|
||||||
|
break if replace==nil
|
||||||
|
quad[3]=replace
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@code[i]=quad
|
||||||
|
i+=1
|
||||||
|
end
|
||||||
|
return @code
|
||||||
|
end
|
||||||
|
end
|
49
deadopt.rb
Normal file
49
deadopt.rb
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
require "set"
|
||||||
|
class DeadOpt
|
||||||
|
def initialize(code)
|
||||||
|
@code=code
|
||||||
|
@always=["call","func"]
|
||||||
|
@ignore=["call","func"]
|
||||||
|
end
|
||||||
|
def optimize()
|
||||||
|
optimized=false
|
||||||
|
while true
|
||||||
|
table=Set.new
|
||||||
|
@code.each do |quad|
|
||||||
|
next if @ignore.include? quad[1]
|
||||||
|
if quad[2]
|
||||||
|
table.add quad[2] if quad[2].is_a? String
|
||||||
|
end
|
||||||
|
if quad[3]
|
||||||
|
table.add quad[3] if quad[2].is_a? String
|
||||||
|
end
|
||||||
|
end
|
||||||
|
dupcode=@code.clone
|
||||||
|
@code=[]
|
||||||
|
dupcode.each do |quad|
|
||||||
|
if @always.include? quad[1]
|
||||||
|
if quad[0]
|
||||||
|
if !(table.include? quad[0])
|
||||||
|
quad[0]=nil
|
||||||
|
optimized=true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@code.push quad
|
||||||
|
else
|
||||||
|
if quad[0]
|
||||||
|
if table.include? quad[0]
|
||||||
|
@code.push quad
|
||||||
|
else
|
||||||
|
optimized=true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
@code.push quad
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
break if optimized==false
|
||||||
|
optimized=false
|
||||||
|
end
|
||||||
|
return @code
|
||||||
|
end
|
||||||
|
end
|
35
grammar.txt
Normal file
35
grammar.txt
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
tokens:
|
||||||
|
\s+ # Do nothing
|
||||||
|
( OPEN_PAREN
|
||||||
|
) CLOSE_PAREN
|
||||||
|
{ OPEN_CURL
|
||||||
|
} CLOSE_CURL
|
||||||
|
+ ADD
|
||||||
|
- SUB
|
||||||
|
* MUL
|
||||||
|
/ DIV
|
||||||
|
= EQUALS
|
||||||
|
, COMMA
|
||||||
|
; SEMICOLON
|
||||||
|
"[^"]*" STRING
|
||||||
|
def DEF
|
||||||
|
return RETURN
|
||||||
|
\d+ NUMBER
|
||||||
|
\w+ IDENT
|
||||||
|
grammar:
|
||||||
|
program: block
|
||||||
|
block: { statement }
|
||||||
|
statement: IDENT EQUALS expression SEMICOLON # ident=expression; : x=10;
|
||||||
|
| DEF IDENT OPEN_CURL block CLOSE_CURL # def ident { block } : def hi{return 10}
|
||||||
|
| RETURN expression SEMICOLON # return expression; : return 10;
|
||||||
|
| expression SEMICOLON # expression; : 1+2;
|
||||||
|
expression: term { addop term } # 1+2-3+400-67...
|
||||||
|
term: factor { mulop factor } # 7*3*67/4*8*3...
|
||||||
|
factor: IDENT OPEN_PAREN CLOSE_PAREN # ident() : hi()
|
||||||
|
| IDENT # ident : hi
|
||||||
|
| OPEN_PAREN expression CLOSE_PAREN # (expression): (1+2)
|
||||||
|
| SUB NUMBER # -number : -10
|
||||||
|
| NUMBER # number : 10
|
||||||
|
| STRING # string : "hi"
|
||||||
|
addop: ADD | SUB
|
||||||
|
mulop: MUL | DIV
|
83
interpreter.rb
Normal file
83
interpreter.rb
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
require_relative "backend.rb"
|
||||||
|
class Interpreter < Backend
|
||||||
|
def initialize(code)
|
||||||
|
@vartable={}
|
||||||
|
@paramtable=[]
|
||||||
|
@functable={}
|
||||||
|
@retstack=[]
|
||||||
|
@envstack=[]
|
||||||
|
@noinc=["call","return"]
|
||||||
|
@infunc=false
|
||||||
|
super(code)
|
||||||
|
end
|
||||||
|
def execute(op)
|
||||||
|
if @infunc
|
||||||
|
if op[1]=="funcend"
|
||||||
|
@infunc=false
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
puts "Running #{op}"
|
||||||
|
case op[1]
|
||||||
|
when "="
|
||||||
|
@vartable[op[0]]=toval(op[2])
|
||||||
|
when "+","-","*","/"
|
||||||
|
op[2]=toval(op[2])
|
||||||
|
op[3]=toval(op[3])
|
||||||
|
@vartable[op[0]]=op[2].send(op[1],op[3])
|
||||||
|
when "param"
|
||||||
|
@paramtable.push toval(op[2])
|
||||||
|
when "call"
|
||||||
|
result=nil
|
||||||
|
case op[2]
|
||||||
|
when "puts"
|
||||||
|
puts @paramtable.shift
|
||||||
|
result=1
|
||||||
|
@pc+=1
|
||||||
|
when "gets"
|
||||||
|
print "Enter number:"
|
||||||
|
result=gets.chomp!.to_i
|
||||||
|
@pc+=1
|
||||||
|
else
|
||||||
|
if @functable.has_key? op[2]
|
||||||
|
@retstack.push([@pc+1,op[0]])
|
||||||
|
@pc=@functable[op[2]]
|
||||||
|
@envstack.push @vartable
|
||||||
|
@vartable={}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if result and op[0]
|
||||||
|
@vartable[op[0]]=result
|
||||||
|
end
|
||||||
|
when "func"
|
||||||
|
@functable[op[2]]=@pc+1
|
||||||
|
@infunc=true
|
||||||
|
when "return"
|
||||||
|
info=@retstack.pop()
|
||||||
|
@pc=info[0]
|
||||||
|
if op[2]
|
||||||
|
rval=toval(op[2])
|
||||||
|
else
|
||||||
|
rval=nil
|
||||||
|
end
|
||||||
|
@vartable=@envstack.pop()
|
||||||
|
@vartable[info[1]]=rval
|
||||||
|
end
|
||||||
|
puts "Vars:#{@vartable}"
|
||||||
|
puts "Params:#{@paramtable}"
|
||||||
|
puts "Funcs:#{@functable}"
|
||||||
|
return !(@noinc.include? op[1])
|
||||||
|
end
|
||||||
|
private
|
||||||
|
def toval(obj)
|
||||||
|
if obj.is_a? Integer
|
||||||
|
return obj
|
||||||
|
elsif /^\d+/.match obj
|
||||||
|
return obj.to_i
|
||||||
|
elsif /^"([^"]+)"/.match obj
|
||||||
|
return $1
|
||||||
|
else
|
||||||
|
return @vartable[obj]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
69
lexer.rb
Normal file
69
lexer.rb
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
require "strscan"
|
||||||
|
require_relative "token.rb"
|
||||||
|
class Lexer
|
||||||
|
def initialize(string)
|
||||||
|
@scan=StringScanner.new(string)
|
||||||
|
@line=0
|
||||||
|
end
|
||||||
|
def next_token()
|
||||||
|
return nil if @scan.eos?
|
||||||
|
lexeme=@scan.scan(/\n/)
|
||||||
|
if lexeme
|
||||||
|
@line+=1
|
||||||
|
return next_token
|
||||||
|
end
|
||||||
|
lexeme=@scan.scan(/\s+/)
|
||||||
|
return next_token if lexeme
|
||||||
|
lexeme=@scan.scan(/\(/)
|
||||||
|
return Token.new(:OPEN_PAREN) if lexeme
|
||||||
|
lexeme=@scan.scan(/\)/)
|
||||||
|
return Token.new(:CLOSE_PAREN) if lexeme
|
||||||
|
lexeme=@scan.scan(/\{/)
|
||||||
|
return Token.new(:OPEN_CURL) if lexeme
|
||||||
|
lexeme=@scan.scan(/\}/)
|
||||||
|
return Token.new(:CLOSE_CURL) if lexeme
|
||||||
|
lexeme=@scan.scan(/\+/)
|
||||||
|
return Token.new(:ADD) if lexeme
|
||||||
|
lexeme=@scan.scan(/\-/)
|
||||||
|
return Token.new(:SUB) if lexeme
|
||||||
|
lexeme=@scan.scan(/\*/)
|
||||||
|
return Token.new(:MUL) if lexeme
|
||||||
|
lexeme=@scan.scan(/\//)
|
||||||
|
return Token.new(:DIV) if lexeme
|
||||||
|
lexeme=@scan.scan(/=/)
|
||||||
|
return Token.new(:EQUALS) if lexeme
|
||||||
|
lexeme=@scan.scan(/,/)
|
||||||
|
return Token.new(:COMMA) if lexeme
|
||||||
|
lexeme=@scan.scan(/;/)
|
||||||
|
return Token.new(:SEMICOLON) if lexeme
|
||||||
|
lexeme=@scan.scan(/"/)
|
||||||
|
return string() if lexeme
|
||||||
|
lexeme=@scan.scan(/def/)
|
||||||
|
return Token.new(:DEF) if lexeme
|
||||||
|
lexeme=@scan.scan(/return/)
|
||||||
|
return Token.new(:RETURN) if lexeme
|
||||||
|
lexeme=@scan.scan(/\d+/)
|
||||||
|
return Number.new(lexeme) if lexeme
|
||||||
|
lexeme=@scan.scan(/\w+/)
|
||||||
|
return Ident.new(lexeme) if lexeme
|
||||||
|
puts "Error: Cannot match #{@scan.rest}"
|
||||||
|
exit
|
||||||
|
end
|
||||||
|
def tokenize()
|
||||||
|
prg=[]
|
||||||
|
while true
|
||||||
|
token=next_token
|
||||||
|
return prg if token==nil
|
||||||
|
prg.push token
|
||||||
|
end
|
||||||
|
end
|
||||||
|
private
|
||||||
|
def string()
|
||||||
|
lexeme=@scan.scan(/\[^"]*/)
|
||||||
|
ok=@scan.scan(/"/)
|
||||||
|
if ok==nil
|
||||||
|
puts "Error: Unterminated string \"#{lexeme} on line #{@line}"
|
||||||
|
end
|
||||||
|
return StrTok.new('"'+lexeme+'"')
|
||||||
|
end
|
||||||
|
end
|
189
parser.rb
Normal file
189
parser.rb
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
require_relative "lexer.rb"
|
||||||
|
require_relative "token.rb"
|
||||||
|
require "pry"
|
||||||
|
class Parser
|
||||||
|
@@tag_to_op={:ADD=>"+",:SUB=>"-",:MUL=>"*",:DIV=>"/"}
|
||||||
|
attr_reader :output
|
||||||
|
def initialize(string)
|
||||||
|
@debug=false
|
||||||
|
@tokens=Lexer.new(string).tokenize
|
||||||
|
p @tokens if @debug
|
||||||
|
@token=@tokens.shift
|
||||||
|
@tmpid=1
|
||||||
|
@output=[]
|
||||||
|
end
|
||||||
|
def parse()
|
||||||
|
block()
|
||||||
|
expected("EOF") if @token != nil
|
||||||
|
end
|
||||||
|
private
|
||||||
|
def expected(str)
|
||||||
|
puts "Error: #{str} Expected"
|
||||||
|
begin
|
||||||
|
raise StandardError
|
||||||
|
rescue StandardError => e
|
||||||
|
e.backtrace.shift
|
||||||
|
puts e.backtrace
|
||||||
|
end
|
||||||
|
exit
|
||||||
|
end
|
||||||
|
def get_numb()
|
||||||
|
expected("Number") if @token.class!=Number
|
||||||
|
tok=@token
|
||||||
|
@token=@tokens.shift
|
||||||
|
p tok if @debug
|
||||||
|
p [@token,@tokens].flatten if @debug
|
||||||
|
return tok.val
|
||||||
|
end
|
||||||
|
def get_name()
|
||||||
|
expected("Name") if @token.class!=Ident
|
||||||
|
tok=@token
|
||||||
|
@token=@tokens.shift
|
||||||
|
p tok if @debug
|
||||||
|
p [@token,@tokens].flatten if @debug
|
||||||
|
return tok.val
|
||||||
|
end
|
||||||
|
def get_str()
|
||||||
|
expected("String") if @token.class!=StrTok
|
||||||
|
tok=@token
|
||||||
|
@token=@tokens.shift
|
||||||
|
p tok if @debug
|
||||||
|
p [@token,@tokens].flatten if @debug
|
||||||
|
return tok.strval
|
||||||
|
end
|
||||||
|
def match(tag)
|
||||||
|
expected(tag) if @token==nil
|
||||||
|
expected(tag) if @token.tag!=tag
|
||||||
|
@token=@tokens.shift
|
||||||
|
p tok if @debug
|
||||||
|
p [@token,@tokens].flatten if @debug
|
||||||
|
end
|
||||||
|
def make_tmp()
|
||||||
|
tmp="t#{@tmpid}"
|
||||||
|
@tmpid+=1
|
||||||
|
return tmp
|
||||||
|
end
|
||||||
|
def factor()
|
||||||
|
case @token.tag
|
||||||
|
when :IDENT
|
||||||
|
name=get_name()
|
||||||
|
tmp=make_tmp()
|
||||||
|
if @token.tag==:OPEN_PAREN
|
||||||
|
params=[]
|
||||||
|
match(:OPEN_PAREN)
|
||||||
|
while true
|
||||||
|
break if @token.tag==:CLOSE_PAREN
|
||||||
|
params.push expression()
|
||||||
|
break if @token.tag!=:COMMA
|
||||||
|
match(:COMMA)
|
||||||
|
end
|
||||||
|
match(:CLOSE_PAREN)
|
||||||
|
params.each do |tmpvar|
|
||||||
|
@output.push [nil,"param",tmpvar,nil]
|
||||||
|
end
|
||||||
|
@output.push [tmp,"call",name,nil]
|
||||||
|
else
|
||||||
|
@output.push [tmp,"=",name,nil]
|
||||||
|
end
|
||||||
|
when :OPEN_PAREN
|
||||||
|
match(:OPEN_PAREN)
|
||||||
|
tmp=expression()
|
||||||
|
match(:CLOSE_PAREN)
|
||||||
|
when :SUB
|
||||||
|
match(:SUB)
|
||||||
|
tmp=make_tmp()
|
||||||
|
num=get_numb()
|
||||||
|
@output.push [tmp,"=","-#{num}",nil]
|
||||||
|
else
|
||||||
|
tmp=make_tmp()
|
||||||
|
if @token.class==StrTok
|
||||||
|
val=get_str()
|
||||||
|
else
|
||||||
|
val=get_numb()
|
||||||
|
end
|
||||||
|
@output.push [tmp,"=",val,nil]
|
||||||
|
end
|
||||||
|
return tmp
|
||||||
|
end
|
||||||
|
def term()
|
||||||
|
ops=[:MUL,:DIV]
|
||||||
|
tmp=factor()
|
||||||
|
oldtmp=tmp
|
||||||
|
return tmp if @token==nil
|
||||||
|
while ops.include? @token.tag
|
||||||
|
tmp=make_tmp()
|
||||||
|
op=@token.tag
|
||||||
|
match(op)
|
||||||
|
op=@@tag_to_op[op]
|
||||||
|
fact=factor()
|
||||||
|
@output.push [tmp,op,oldtmp,fact]
|
||||||
|
oldtmp=tmp
|
||||||
|
break if @token==nil
|
||||||
|
end
|
||||||
|
return tmp
|
||||||
|
end
|
||||||
|
def expression()
|
||||||
|
ops=[:ADD,:SUB]
|
||||||
|
tmp=term()
|
||||||
|
oldtmp=tmp
|
||||||
|
return tmp if @token==nil
|
||||||
|
while ops.include? @token.tag
|
||||||
|
tmp=make_tmp()
|
||||||
|
op=@token.tag
|
||||||
|
match(op)
|
||||||
|
op=@@tag_to_op[op]
|
||||||
|
trm=term()
|
||||||
|
@output.push [tmp,op,oldtmp,trm]
|
||||||
|
oldtmp=tmp
|
||||||
|
break if @token==nil
|
||||||
|
end
|
||||||
|
return tmp
|
||||||
|
end
|
||||||
|
def assingment()
|
||||||
|
name=get_name()
|
||||||
|
match(:EQUALS)
|
||||||
|
tmpvar=expression()
|
||||||
|
@output.push [name,"=",tmpvar,nil]
|
||||||
|
end
|
||||||
|
def func()
|
||||||
|
match(:DEF)
|
||||||
|
name=@token.val
|
||||||
|
match(:IDENT)
|
||||||
|
match(:OPEN_CURL)
|
||||||
|
@output.push [nil,"func",name,nil]
|
||||||
|
block(:CLOSE_CURL)
|
||||||
|
@output.push [nil,"return",nil,nil]
|
||||||
|
@output.push [nil,"funcend",nil,nil]
|
||||||
|
end
|
||||||
|
def ret()
|
||||||
|
match(:RETURN)
|
||||||
|
tmpvar=expression()
|
||||||
|
@output.push [nil,"return",tmpvar,nil]
|
||||||
|
end
|
||||||
|
def statement()
|
||||||
|
if @token.tag==:IDENT and @tokens[0].tag==:EQUALS
|
||||||
|
assingment()
|
||||||
|
match(:SEMICOLON)
|
||||||
|
elsif @token.tag==:DEF
|
||||||
|
func()
|
||||||
|
elsif @token.tag==:RETURN
|
||||||
|
ret()
|
||||||
|
match(:SEMICOLON)
|
||||||
|
else
|
||||||
|
expression()
|
||||||
|
match(:SEMICOLON)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
def block(end_tok=nil)
|
||||||
|
if end_tok
|
||||||
|
while @token.tag!=end_tok
|
||||||
|
statement()
|
||||||
|
end
|
||||||
|
match(end_tok)
|
||||||
|
else
|
||||||
|
while @token!=nil
|
||||||
|
statement()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
9
prg.lang
Normal file
9
prg.lang
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
def hi {
|
||||||
|
x=10;
|
||||||
|
puts(x);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
x=15;
|
||||||
|
y=hi();
|
||||||
|
puts(x);
|
||||||
|
puts(y);
|
30
token.rb
Normal file
30
token.rb
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
class Token
|
||||||
|
attr_reader :tag
|
||||||
|
def initialize(tag)
|
||||||
|
@tag=tag
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Number < Token
|
||||||
|
attr_reader :val
|
||||||
|
def initialize(lexeme)
|
||||||
|
super(:NUMB)
|
||||||
|
@val=lexeme.to_i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Ident < Token
|
||||||
|
attr_reader :val
|
||||||
|
def initialize(lexeme)
|
||||||
|
super(:IDENT)
|
||||||
|
@val=lexeme
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class StrTok < Token
|
||||||
|
attr_reader :strval
|
||||||
|
def initialize(lexeme)
|
||||||
|
super(:STRING)
|
||||||
|
@strval=lexeme
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user