[FIXED] relations and the do loop. abstracted simple loops.

This commit is contained in:
sjs 2009-05-24 12:57:56 -07:00
parent 41eb590d44
commit b8581b8b24

View file

@ -6,8 +6,11 @@
# sjs # sjs
# may 2009 # may 2009
require 'rubygems' # XXX Comment if unused, unroller is too fucking slow! rubygems is a bit
require 'unroller' # of a slouch too.
#
# require 'rubygems'
# require 'unroller'
class ParseError < StandardError class ParseError < StandardError
attr_reader :caller, :context attr_reader :caller, :context
@ -42,7 +45,7 @@ class Compiler
get_char get_char
end end
def parse def compile
block block
expected(:'end of file') unless eof? expected(:'end of file') unless eof?
[@data, @bss, @code] [@data, @bss, @code]
@ -276,74 +279,90 @@ class Compiler
end end
end end
def eq_relation # a: [esp]
expression # b: eax
x86_sub(:eax, '[esp]') #
make_boolean # If b - a is zero then a = b, and make_boolean will leave the zero
x86_not(:eax) # to effectively return false. If b - a is non-zero then a != b,
end # and make_boolean will leave -1 (true) for us in eax.
def neq_relation def neq_relation
expression expression
x86_sub(:eax, '[esp]') x86_sub(:eax, '[esp]')
make_boolean make_boolean
end end
# Invert the != test for equal.
def eq_relation
neq_relation
x86_not(:eax)
end
# > and < are both implemented in terms of jl (jump if less than).
# We exploit the fact that cmp is the subtraction of src from dest
# and order the terms appropriately for each function. As for >=
# and <=, they in turn are implemented in terms of > and <. a is
# greater than or equal to b if and only if a is *not* less than b.
# The next 4 relations all compare 2 values a and b, then return
# true (-1) if the difference was below zero and false (0)
# otherwise (using JL, jump if less than).
def cmp_relation(a, b, options={})
# Invert the sense of the test?
invert = options[:invert]
true_label = unique_label(:cmp)
end_label = unique_label(:endcmp)
x86_cmp(a, b)
x86_jl(true_label)
x86_xor(:eax, :eax) # return false
x86_not(:eax) if invert # (or true if inverted)
x86_jmp(end_label)
emit_label(true_label)
x86_xor(:eax, :eax) # return true
x86_not(:eax) unless invert # (or false if inverted)
emit_label(end_label)
end
# a: [esp]
# b: eax
#
# if a > b then b - a < 0
def gt_relation def gt_relation
gt_label = unique_label(:gt)
end_label = unique_label(:endgt)
expression expression
x86_cmp(:eax, '[esp]') # b - a < 0 if a > b cmp_relation(:eax, '[esp]') # b - a
x86_jl(gt_label)
x86_xor(:eax, :eax)
x86_jmp(end_label)
emit_label(gt_label)
x86_xor(:eax, :eax)
x86_not(:eax)
emit_label(end_label)
end end
# a: [esp]
# b: eax
#
# if a < b then a - b < 0
def lt_relation def lt_relation
lt_label = unique_label(:lt)
end_label = unique_label(:endlt)
expression expression
x86_cmp('[esp]', :eax) # a - b < 0 if a < b cmp_relation('[esp]', :eax) # a - b
x86_jl(lt_label)
x86_xor(:eax, :eax)
x86_jmp(end_label)
emit_label(lt_label)
x86_xor(:eax, :eax)
x86_not(:eax)
emit_label(end_label)
end end
# def ge_relation # a: [esp]
# ge_label = unique_label(:ge) # b: eax
# end_label = unique_label(:endge) #
# expression # if a >= b then !(a < b)
# x86_cmp(:eax, '[esp]') # b - a < 0 if a > b def ge_relation
# x86_jl(gt_label) expression
# x86_xor(:eax, :eax) # Compare them as in less than but invert the result.
# x86_jmp(end_label) cmp_relation('[esp]', :eax, :invert => true)
# emit_label(gt_label) end
# x86_xor(:eax, :eax)
# x86_not(:eax)
# emit_label(end_label)
# end
# def lt_relation # a: [esp]
# lt_label = unique_label(:lt) # b: eax
# end_label = unique_label(:endlt) #
# expression # if a <= b then !(a > b)
# x86_cmp('[esp]', :eax) # a - b < 0 if a < b def le_relation
# x86_jl(lt_label) expression
# x86_xor(:eax, :eax) # Compare them as in greater than but invert the result.
# x86_jmp(end_label) cmp_relation(:eax, '[esp]', :invert => true)
# emit_label(lt_label) end
# x86_xor(:eax, :eax)
# x86_not(:eax)
# emit_label(end_label)
# end
###################################### ######################################
@ -415,47 +434,46 @@ class Compiler
emit_label(end_label) emit_label(end_label)
end end
def while_stmt # Used to implement the Two-Label-Loops (while, until, repeat).
while_label = unique_label(:while) #
end_label = unique_label(:endwhile) # name: Name of the loop for readable labels.
emit_label(while_label) # block: Code to execute at the start of each iteration. (e.g. a
condition # condition)
skip_any_whitespace def simple_loop(name)
x86_jz(end_label) start_label = unique_label(:"loop_#{name}")
end_label = unique_label(:"end_#{name}")
emit_label(start_label)
yield(end_label)
@indent += 1 @indent += 1
block(end_label) block(end_label)
@indent -= 1 @indent -= 1
match_word('end') match_word('end')
x86_jmp(while_label) x86_jmp(start_label)
emit_label(end_label) emit_label(end_label)
end end
def while_stmt
simple_loop('while') do |end_label|
condition
skip_any_whitespace
x86_jz(end_label)
end
end
def until_stmt def until_stmt
until_label = unique_label(:until) simple_loop('until') do |end_label|
end_label = unique_label(:enduntil) condition
emit_label(until_label) skip_any_whitespace
condition x86_jnz(end_label)
skip_any_whitespace end
x86_jnz(end_label)
@indent += 1
block(end_label)
@indent -= 1
match_word('end')
x86_jmp(until_label)
emit_label(end_label)
end end
def repeat_stmt def repeat_stmt
skip_any_whitespace # no condition, slurp whitespace simple_loop('repeat') do |end_label|
repeat_label = unique_label(:repeat) skip_any_whitespace
end_label = unique_label(:endrepeat) end
emit_label(repeat_label)
@indent += 1
block(end_label)
@indent -= 1
match_word('end')
x86_jmp(repeat_label)
emit_label(end_label)
end end
# s = 0 # s = 0
@ -463,8 +481,6 @@ class Compiler
# s = s + x # s = s + x
# e # e
def for_stmt def for_stmt
start_label = unique_label(:for)
end_label = unique_label(:endfor)
counter = "[#{get_name}]" counter = "[#{get_name}]"
match('=') match('=')
boolean_expression # initial value boolean_expression # initial value
@ -476,42 +492,52 @@ class Compiler
skip_any_whitespace skip_any_whitespace
x86_push(:eax) # stash final value on stack x86_push(:eax) # stash final value on stack
final = '[esp]' final = '[esp]'
emit_label(start_label)
x86_mov(:ecx, counter) # get the counter simple_loop('for') do |end_label|
x86_add(:ecx, 1) # increment x86_mov(:ecx, counter) # get the counter
x86_mov(counter, :ecx) # store the counter x86_add(:ecx, 1) # increment
x86_cmp(final, :ecx) # check if we're done x86_mov(counter, :ecx) # store the counter
x86_jz(end_label) # if so jump to the end x86_cmp(final, :ecx) # check if we're done
@indent += 1 x86_jz(end_label) # if so jump to the end
block(end_label) # otherwise execute the block end
@indent -= 1
match_word('end')
x86_jmp(start_label) # lather, rinse, repeat
emit_label(end_label)
x86_add(:esp, 4) # clean up the stack x86_add(:esp, 4) # clean up the stack
end end
# d 5 # do 5
# ... # ...
# e # end
def do_stmt def do_stmt
start_label = unique_label(:do)
end_label = unique_label(:enddo)
boolean_expression boolean_expression
skip_any_whitespace skip_any_whitespace
x86_mov(:ecx, :eax) x86_mov(:ecx, :eax)
x86_push(:ecx) x86_push(:ecx)
counter = '[esp]'
start_label = unique_label(:do)
end_label = unique_label(:enddo)
emit_label(start_label) emit_label(start_label)
x86_mov(counter, :ecx)
x86_push(:ecx)
@indent += 1 @indent += 1
block(end_label) block(end_label)
@indent -= 1 @indent -= 1
x86_mov(:ecx, counter)
x86_pop(:ecx)
match_word('end') match_word('end')
x86_loop(start_label) x86_loop(start_label)
# Phony push! break needs to clean up the stack, but since we
# don't know if there is a break at this point we fake a push and
# always clean up the stack after.
x86_sub(:esp, 4) x86_sub(:esp, 4)
emit_label(end_label) emit_label(end_label)
# If there was a break we have to clean up the stack here. If
# there was no break we clean up the phony push above.
x86_add(:esp, 4) x86_add(:esp, 4)
end end
@ -700,7 +726,7 @@ class Compiler
def many(test) def many(test)
test = method(test) if test.is_a?(Symbol) test = method(test) if test.is_a?(Symbol)
token = '' token = ''
while test[@look] while !eof? && test[@look]
token << @look token << @look
get_char get_char
end end