257 lines
6.7 KiB
Ruby
257 lines
6.7 KiB
Ruby
require_relative "parser.rb"
|
|
require_relative "lexer.rb"
|
|
$vars={}
|
|
$type_to_len={"char"=>8,"short"=>16,"int"=>32,"long"=>64}
|
|
$immvals={}
|
|
$labelno=0
|
|
$errors=false
|
|
|
|
def gen_label()
|
|
label="label#{$labelno}"
|
|
$labelno+=1
|
|
return label
|
|
end
|
|
|
|
def val_for(var)
|
|
# puts "val for #{var}"
|
|
# puts $immvals
|
|
# puts $vars
|
|
if $immvals[var]
|
|
val=$immvals[var]
|
|
$immvals[var]=nil
|
|
else
|
|
if !(var.match /c_temp/) and $vars[var]==nil
|
|
puts "error: use of undeclared variable '#{var}'"
|
|
$errors=true
|
|
end
|
|
val=var
|
|
end
|
|
return val
|
|
end
|
|
|
|
def is_num(val)
|
|
return val.is_a? Numeric || (val.match /^\d+$/)!=nil
|
|
end
|
|
|
|
def binop(dest,var1,op,var2)
|
|
code=[]
|
|
code+=gen_expr(var1,dest,true)
|
|
code+=gen_expr(var2,"exprc_temp",true)
|
|
var1=val_for(dest)
|
|
var2=val_for("exprc_temp")
|
|
# if is_num(var1) and is_num(var2)
|
|
# $immvals[dest]=eval("#{var1}#{op}#{var2}")
|
|
# else
|
|
code.push [dest,var1,op,var2]
|
|
# end
|
|
return code
|
|
end
|
|
|
|
|
|
def gen_expr(expr,dest,in_recurse=false)
|
|
# puts "GEN EXPR #{expr}"
|
|
code=[]
|
|
case expr[:type]
|
|
when :bitor
|
|
code+=binop(dest,expr[:v1],"|",expr[:v2])
|
|
when :gt
|
|
code+=binop(dest,expr[:v1],">",expr[:v2])
|
|
when :add
|
|
code+=binop(dest,expr[:v1],"+",expr[:v2])
|
|
when :sub
|
|
code+=binop(dest,expr[:v1],"-",expr[:v2])
|
|
when :num
|
|
code.push [dest,expr[:val],"num"]
|
|
# $immvals[dest]=expr[:val]
|
|
when :var
|
|
if $vars[expr[:var]]==nil and (expr[:var].match /c_temp/)==nil
|
|
puts "error: use of undeclared variable '#{expr[:var]}' at line #{expr[:line]}"
|
|
$errors=true
|
|
end
|
|
code.push [dest,expr[:var],"var"]
|
|
# $immvals[dest]=expr[:var]
|
|
when :deref
|
|
code+=gen_expr(expr[:addr],"exprc_temp",true)
|
|
# if $immvals["exprc_temp"]
|
|
# if $immvals["exprc_temp"].is_a? String
|
|
# puts $vars
|
|
# puts $vars[$immvals["exprc_temp"]]
|
|
# if $vars[$immvals["exprc_temp"]][:type_type]!="pointer"
|
|
# puts "Warning! Dereferencing #{$vars[$immvals["exprc_temp"]][:type]} #{$immvals["exprc_temp"]}"
|
|
# end
|
|
# end
|
|
# $immvals[dest]="*#{val_for("exprc_temp")}"
|
|
# else
|
|
code.push [dest,"exprc_temp","*"]
|
|
# end
|
|
when :inv
|
|
code+=gen_expr(expr[:val],"exprc_temp",true)
|
|
code.push [dest,"exprc_temp","~"]
|
|
when :array
|
|
code+=gen_expr(expr[:base],"exprc_temp1",true)
|
|
code+=gen_expr(expr[:off],"exprc_temp2",true)
|
|
code.push [dest,"exprc_temp1","[]","exprc_temp2"]
|
|
when :cast
|
|
code=gen_expr(expr[:expr],dest,true)
|
|
len=$type_to_len[expr[:typ][:type]]
|
|
# if $immvals[dest]
|
|
# if $immvals[dest].match /^\d+$/
|
|
# case len
|
|
# when 8
|
|
# code.push "#{dest}=#{$immvals[dest]}&0xFF;\n"
|
|
# when 16
|
|
# code.push "#{dest}=#{$immvals[dest]}&0xFFFF;\n"
|
|
# when 32
|
|
# code.push "#{dest}=#{$immvals[dest]}&0xFFFFFFFF;\n"
|
|
# end
|
|
# else
|
|
# currlen=$type_to_len[$vars[$immvals[dest]][:type]]
|
|
# case len
|
|
# when 8
|
|
# code.push "#{dest}=#{dest}&0xFF;\n" if currlen>8
|
|
# when 16
|
|
# code.push "#{dest}=#{dest}&0xFFFF;\n" if currlen>16
|
|
# when 32
|
|
# code.push "#{dest}=#{dest}&0xFFFFFFFF;\n" if currlen>32
|
|
# end
|
|
# end
|
|
# $immvals[dest]=nil
|
|
# else
|
|
case expr[:typ][:type_type]
|
|
when :scalar
|
|
if $vars[dest]==nil
|
|
currlen=64
|
|
else
|
|
currlen=$type_to_len[$vars[dest][:type]]
|
|
end
|
|
case len
|
|
when 8
|
|
code.push [dest,dest,"&","0xFF"] if currlen>8
|
|
when 16
|
|
code.push [dest,dest,"&","0xFFFF"] if currlen>16
|
|
when 32
|
|
code.push [dest,dest,"&","0xFFFFFFFF"] if currlen>32
|
|
end
|
|
# end
|
|
end
|
|
end
|
|
if !in_recurse
|
|
code.push [nil,nil,"expr_done"]
|
|
$used_exp_temps=false
|
|
end
|
|
# puts "DONE EXPR"
|
|
return code
|
|
end
|
|
|
|
def gen_stmt(stmt)
|
|
# puts "GEN STMT #{stmt}"
|
|
code=[]
|
|
case stmt[:type]
|
|
when :vardec
|
|
$vars[stmt[:var]]=stmt[:typ]
|
|
type_stmt=stmt[:extern] ? "type_extern" : "type"
|
|
case stmt[:typ][:type_type]
|
|
when :scalar
|
|
code.push [nil,stmt[:var],type_stmt,$type_to_len[stmt[:typ][:type]]]
|
|
when :pointer
|
|
code.push [nil,stmt[:var],type_stmt,64]
|
|
end
|
|
if stmt[:init]
|
|
code+=gen_expr(stmt[:init],stmt[:var])
|
|
# if $immvals[stmt[:var]]
|
|
# code.push "#{stmt[:var]}=#{val_for(stmt[:var])};\n"
|
|
# $immvals[stmt[:var]]=nil
|
|
# end
|
|
elsif !stmt[:extern]
|
|
code.push [stmt[:var],stmt[:var],"^",stmt[:var]]
|
|
end
|
|
when :set
|
|
if $vars[stmt[:var]]==nil and !stmt[:var].match /c_temp/
|
|
puts "error: use of undeclared variable '#{stmt[:var]}' at line #{stmt[:line]}"
|
|
$errors=true
|
|
end
|
|
code+=gen_expr(stmt[:expr],stmt[:var])
|
|
# if $immvals[stmt[:var]]
|
|
# code.push "#{stmt[:var]}=#{val_for(stmt[:var])};\n"
|
|
# $immvals[stmt[:var]]=nil
|
|
# end
|
|
when :arrayset
|
|
code+=gen_expr(stmt[:getexpr][:base],"c_temp1")
|
|
code+=gen_expr(stmt[:getexpr][:off],"c_temp2")
|
|
code+=gen_expr(stmt[:expr],"c_temp3")
|
|
code.push ["c_temp1","c_temp2","[]=","c_temp3"]
|
|
when :call
|
|
code+=gen_expr(stmt[:addr],"c_temp1")
|
|
code.push [nil,"c_temp1","*()"]
|
|
when :while
|
|
cond_label=gen_label()
|
|
end_label=gen_label()
|
|
code.push [nil,nil,"start_scope"]
|
|
code.push [nil,cond_label,":"]
|
|
if stmt[:cond]
|
|
code+=gen_expr(stmt[:cond],"c_temp1")
|
|
code.push [nil,c_temp1,"ifnot",end_label]
|
|
end
|
|
for stmt in stmt[:code]
|
|
line=gen_stmt(stmt)
|
|
if line==[]
|
|
puts "Cannot generate code for #{stmt}. Skipping."
|
|
else
|
|
code.push line
|
|
end
|
|
end
|
|
code.push [nil,cond_label,"goto"]
|
|
code.push [nil,nil,"end_scope"]
|
|
when :for
|
|
cond_label=gen_label()
|
|
end_label=gen_label()
|
|
code.push [nil,nil,"start_scope"]
|
|
if stmt[:init]
|
|
code+=gen_stmt(stmt[:init])
|
|
end
|
|
code.push [nil,cond_label,":"]
|
|
if stmt[:cond]
|
|
code+=gen_expr(stmt[:cond],"c_temp1")
|
|
code.push [nil,"c_temp1","ifnot",end_label]
|
|
end
|
|
for stmt in stmt[:code]
|
|
line=gen_stmt(stmt)
|
|
if line==nil
|
|
puts "Cannot generate code for #{stmt}. Skipping."
|
|
else
|
|
code+=line
|
|
end
|
|
end
|
|
if stmt[:post]
|
|
code+=gen_stmt(stmt[:post])
|
|
end
|
|
code.push [nil,cond_label,"goto"]
|
|
code.push [nil,end_label,":"]
|
|
code.push [nil,nil,"end_scope"]
|
|
end
|
|
code.push [nil,nil,"stmt_done"]
|
|
$used_c_temps=false
|
|
# puts "DONE STMT"
|
|
return code
|
|
end
|
|
|
|
def genir(code)
|
|
parser=C.new()
|
|
ast=parser.parse(code)
|
|
irast=[]
|
|
for func in ast
|
|
code=[]
|
|
stmts=func[:code]
|
|
for stmt in stmts
|
|
line=gen_stmt(stmt)
|
|
if line==nil
|
|
puts "Cannot generate code for #{stmt}. Skipping."
|
|
else
|
|
code+=line
|
|
end
|
|
end
|
|
irast.push({:name=>func[:name],:code=>code})
|
|
end
|
|
return irast
|
|
end
|