190 lines
3.9 KiB
Ruby
190 lines
3.9 KiB
Ruby
|
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
|