t64/c_compiler/frontend/frontend.rb
2020-06-21 14:14:57 -05:00

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