This commit is contained in:
pjht 2018-03-03 08:52:57 -06:00
parent fc3eaf7abe
commit ae23e9fbe6
2 changed files with 151 additions and 33 deletions

View File

@ -1,9 +1,3 @@
# TODO: Make compilation two-phase:
=begin
Phase one will accept an array of tokens and update the tables accordingly.
Phase two wil take the data from the tables and do the actual compilation.
=end
$types=[:int,:void] $types=[:int,:void]
$functable={} $functable={}
$cfunc=nil $cfunc=nil
@ -17,15 +11,58 @@ class String
end end
end end
def is_var(var)
return false if var.class!=Symbol
vartable=$functable[$cfunc][:vars]
argtable=$functable[$cfunc][:args]
if vartable.include? var or argtable.include? var
return true
else
return false
end
end
def get_index(var) def get_index(var)
vartable=$functable[$cfunc][:vars] vartable=$functable[$cfunc][:vars]
argtable=$functable[$cfunc][:args]
if vartable.include? var if vartable.include? var
return vartable.find_index(var) return vartable.find_index(var),"local"
else else
if argtable.include? var
return argtable.find_index(var),"argument"
end
raise CompilationError,"No such variable #{var}" raise CompilationError,"No such variable #{var}"
end end
end end
def push(obj)
if obj.class == Symbol
index,segment=get_index(obj)
$outfile.puts "push #{segment} #{index}"
elsif obj.class == String
if obj.is_integer?
$outfile.puts "push constant #{obj.to_i}"
elsif is_var(obj.to_sym)
index,segment=get_index(obj.to_sym)
$outfile.puts "push #{segment} #{index}"
else
raise CompilationError, "Cannot push #{obj}, is not a variable or a number."
end
elsif obj.is_a? Numeric
$outfile.puts "push constant #{obj}"
else
raise CompilationError, "Cannot push #{obj}, is not a variable or a number."
end
end
def pop(var)
if var.class != Symbol and !is_var(var)
raise CompilationError, "Cannot push #{var}, is not a variable."
end
index,segment=get_index(var)
$outfile.puts "pop #{segment} #{index}"
end
def tokenize_line(line) def tokenize_line(line)
split_line=line.split(" ") split_line=line.split(" ")
cmd=split_line.shift.to_sym cmd=split_line.shift.to_sym
@ -33,61 +70,109 @@ def tokenize_line(line)
if $types.include? cmd if $types.include? cmd
if /(\w+)\((.*)\) \{/.match(split_line.join(" ")) if /(\w+)\((.*)\) \{/.match(split_line.join(" "))
tokens.push(:func) tokens.push(:func)
tokens.push(cmd)
tokens.push($1.to_sym) tokens.push($1.to_sym)
tokens.push($2) args=[]
$2.split(",").each do |arg|
temp=arg.split(" ")
args.push(temp[1].to_sym)
end
tokens.push(args)
else else
tokens.push(:newvar) tokens.push(:newvar)
tokens.push(cmd) tokens.push(cmd)
tokens.push(split_line[0].to_sym) tokens.push(split_line[0].to_sym)
end end
else else
if /(\w+)\((.*)\)/.match(cmd)
tokens.push(:call)
tokens.push($1.to_sym)
args=[]
$2.split(",").each do |arg|
args.push(arg.to_sym)
end
tokens.push(args)
end
if split_line[0] == "=" if split_line[0] == "="
tokens.push(:assignment) tokens.push(:assignment)
tokens.push(cmd.to_sym) tokens.push(cmd)
tokens.push(split_line[1]) tokens.push(split_line[1])
end end
if cmd == "}" if cmd == "}"
tokens.push(:endfunc) tokens.push(:endfunc)
end end
if cmd == :return
tokens.push(:return)
tokens.push(split_line[0])
end
end end
return tokens return tokens
end end
def phase_one(tokens) def phase_one(tokens)
if $cfunc==nil and tokens[0] != :func if $cfunc==nil and tokens[0] != :func
raise CompilationError, "Code must be inside a fuction" raise CompilationError, "Code must be inside a fuction, for line #{tokens}"
end end
case tokens[0] case tokens[0]
when :func when :func
$functable[tokens[1]]={:vars=>[],:code=>[]} $functable[tokens[2]]={:vars=>[],:code=>[], :type=>tokens[1], :args=>tokens[3]}
$cfunc=tokens[1] $cfunc=tokens[2]
when :endfunc when :endfunc
$cfunc=nil $cfunc=nil
when :newvar when :newvar
$functable[$cfunc][:vars].push(tokens[2]) $functable[$cfunc][:vars].push(tokens[2])
else else
$functable[$cfunc][:code].push(tokens) $functable[$cfunc][:code].push(tokens) if tokens != []
end end
end end
def phase_two def phase_two
$functable.each do |func,info| $functable.each do |func,info|
$cfunc=func
vars=info[:vars] vars=info[:vars]
$outfile.puts "function Main.#{func.to_s} #{vars.length}" $outfile.puts "function Main.#{func.to_s} #{vars.length}"
info[:code].each do |line| info[:code].each do |line|
#puts "Parsing line #{line}" action=line.shift
action=line.shift case action
case action when :assignment
when :assignment if line[1].is_integer? or is_var(line[1])
index=get_index(line[0]) push(line[1])
if line[1].is_integer? else
$outfile.puts "push constant #{line[1].to_i}" parsed_line=tokenize_line(line[1])
$outfile.puts "pop local #{index}" parsed_line=[""] if parsed_line==nil
if parsed_line[0]==:call
parsed_line[2].each do |arg|
push(arg)
end
nargs=$functable[parsed_line[1]][:args].length
$outfile.puts "call Main.#{parsed_line[1]} #{nargs}"
else else
index1=get_index(line[1]) if line[1].include? "+"
$outfile.puts "push local #{index1}" operand=line[1].split("+")
op="add"
end
if line[1].include? "-"
operand=line[1].split("-")
op="sub"
end
push(operand[0])
push(operand[1])
$outfile.puts(op)
end end
end end
pop(line[0])
when :call
line[1].each do |arg|
push(arg)
end
nargs=$functable[line[0]][:args].length
$outfile.puts "call Main.#{line[0]} #{nargs}"
when :return
push(line[0])
end
end
if info[:type]==:void
push(0)
end end
$outfile.puts "return" $outfile.puts "return"
$outfile.puts(" ") $outfile.puts(" ")
@ -102,17 +187,36 @@ def write_init
sysfile.puts("goto halt") sysfile.puts("goto halt")
sysfile.puts(" ") sysfile.puts(" ")
end end
if !File.exists? "vmprog" if !File.exists? "vmprog"
Dir.mkdir("vmprog") Dir.mkdir("vmprog")
write_init()
end end
Dir.chdir("vmprog") Dir.chdir("vmprog")
$outfile=File.new("Main.vm","w") $outfile=File.new("Main.vm","w")
write_init()
phase_one(tokenize_line("int main() {")) prog=<<-END
phase_one(tokenize_line("int x")) void main() {
phase_one(tokenize_line("x = 10")) int x
phase_one(tokenize_line("int y")) x = 10
phase_one(tokenize_line("y = 10")) int y
phase_one(tokenize_line("}")) y = x+17
int z
z = testfunc(y)
}
int testfunc(int c) {
int x
x = c-7
return x
}
END
prog.each_line do |line|
phase_one(tokenize_line(line))
end
puts $functable puts $functable
phase_two
phase_two()

View File

@ -1,7 +1,21 @@
function Main.main 2 function Main.main 3
push constant 10 push constant 10
pop local 0 pop local 0
push constant 10 push local 0
push constant 17
add
pop local 1 pop local 1
push local 1
call Main.testfunc 1
pop local 2
push constant 0
return
function Main.testfunc 1
push argument 0
push constant 7
sub
pop local 0
push local 0
return return