[NEW] ignore most whitespace. Fixed division (use cdq before idiv).

This commit is contained in:
sjs 2009-05-14 01:12:48 -07:00
parent c008ed007a
commit bd0408fa2f
2 changed files with 86 additions and 50 deletions

View file

@ -17,18 +17,19 @@ class Compiler
@data = '' # data section @data = '' # data section
@bss = '' # bss section @bss = '' # bss section
@code = '' # code section @code = '' # code section
@vars = {}
# seed the lexer # seed the lexer
get_char get_char
skip_whitespace
end end
def parse def parse
statement statement until eof?
expression; newline
[@data, @bss, @code] [@data, @bss, @code]
end end
# Read the next character from the input stream # Read the next character from the input stream.
def get_char def get_char
@look = if @input.eof? @look = if @input.eof?
nil nil
@ -51,14 +52,7 @@ class Compiler
end end
end end
# Match a specific input character
def match(char)
if @look == char
get_char
else
expected("'#{char}'")
end
end
# Recognize an alphabetical character. # Recognize an alphabetical character.
def alpha?(char) def alpha?(char)
@ -75,28 +69,48 @@ class Compiler
alpha?(char) || digit?(char) alpha?(char) || digit?(char)
end end
# Get an identifier. def whitespace?(char)
def get_name char == ' ' || char == '\t'
expected('identifier') unless alpha?(@look) end
# Match a specific input character.
def match(char)
expected("'#{char}'") unless @look == char
get_char
skip_whitespace
end
# Parse zero or more consecutive characters for which the test is
# true.
def many(test)
token = '' token = ''
while alnum?(@look) while test.call(@look)
token << @look token << @look
get_char get_char
end end
skip_whitespace
token token
end end
# Get an identifier.
def get_name
expected('identifier') unless alpha?(@look)
many(method(:alnum?))
end
# Get a number. # Get a number.
def get_num def get_num
expected('integer') unless digit?(@look) expected('integer') unless digit?(@look)
value = '' many(method(:digit?))
while digit?(@look)
value << @look
get_char
end
value
end end
# Skip all leading whitespace.
def skip_whitespace
get_char while whitespace?(@look)
end
# Define a constant in the .data section. # Define a constant in the .data section.
def equ(name, value) def equ(name, value)
@data << "#{name}\tequ #{value}" @data << "#{name}\tequ #{value}"
@ -104,7 +118,12 @@ class Compiler
# Define a variable with the given name and size (in dwords). # Define a variable with the given name and size (in dwords).
def var(name, dwords=1) def var(name, dwords=1)
@bss << "#{name}: resd #{dwords}\n" unless @vars[name]
@bss << "#{name}: resd #{dwords}\n"
@vars[name] = name
# else
# raise ParseError, "identifier #{name} redefined"
end
end end
# Emit a line of code wrapped between a tab and a newline. # Emit a line of code wrapped between a tab and a newline.
@ -112,6 +131,8 @@ class Compiler
@code << "\t#{s}\n" @code << "\t#{s}\n"
end end
# Parse and translate an identifier or function call. # Parse and translate an identifier or function call.
def identifier def identifier
name = get_name name = get_name
@ -120,10 +141,10 @@ class Compiler
# function call # function call
match('(') match('(')
match(')') match(')')
call(name) x86_call(name)
else else
# variable access # variable access
mov("eax", "dword [#{name}]") x86_mov(:eax, "dword [#{name}]")
end end
end end
@ -137,7 +158,7 @@ class Compiler
when alpha?(@look) when alpha?(@look)
identifier identifier
when digit?(@look) when digit?(@look)
mov("eax", get_num) x86_mov(:eax, get_num)
else else
expected("a number, identifier, or an expression wrapped in parens") expected("a number, identifier, or an expression wrapped in parens")
end end
@ -152,7 +173,7 @@ class Compiler
# multiply & divide. Because they leave their results in eax # multiply & divide. Because they leave their results in eax
# associativity works. Each interim result is pushed on the # associativity works. Each interim result is pushed on the
# stack here. # stack here.
push("eax") x86_push(:eax)
if @look == '*' if @look == '*'
multiply multiply
@ -160,7 +181,7 @@ class Compiler
divide divide
end end
add("esp", 4) # Remove the 1st factor from the stack. x86_add(:esp, 4) # Remove the 1st factor from the stack.
end end
end end
@ -170,7 +191,7 @@ class Compiler
if addop? if addop?
# Clear eax simulating a zero before unary plus and minus # Clear eax simulating a zero before unary plus and minus
# operations. # operations.
xor("eax", "eax") x86_xor(:eax, :eax)
else else
term # Result is in eax. term # Result is in eax.
end end
@ -180,7 +201,7 @@ class Compiler
# subtract. Because they leave their results in eax # subtract. Because they leave their results in eax
# associativity works. Each interim result is pushed on the # associativity works. Each interim result is pushed on the
# stack here. # stack here.
push("eax") x86_push(:eax)
if @look == '+' if @look == '+'
add add
@ -188,7 +209,7 @@ class Compiler
subtract subtract
end end
add("esp", 4) # Remove 1st term (a) from the stack. x86_add(:esp, 4) # Remove 1st term (a) from the stack.
end end
end end
@ -198,7 +219,7 @@ class Compiler
match('=') match('=')
expression expression
var(name) var(name)
mov("dword [#{name}]", "eax") x86_mov("dword [#{name}]", :eax)
end end
# Parse one or more newlines. # Parse one or more newlines.
@ -222,7 +243,7 @@ class Compiler
def add def add
match('+') match('+')
term # Result is in eax. term # Result is in eax.
add('eax', '[esp]') # Add a to b. x86_add(:eax, '[esp]') # Add a to b.
end end
# Parse a subtraction operator and the 2nd term (b). The result is # Parse a subtraction operator and the 2nd term (b). The result is
@ -230,8 +251,8 @@ class Compiler
def subtract def subtract
match('-') match('-')
term # Result is in eax. term # Result is in eax.
sub('eax', '[esp]') # Subtract a from b (this is backwards). x86_sub(:eax, '[esp]') # Subtract a from b (this is backwards).
neg('eax') # Fix things up. -(b-a) == a-b x86_neg(:eax) # Fix things up. -(b-a) == a-b
end end
# Parse an addition operator and the 2nd term (b). The result is # Parse an addition operator and the 2nd term (b). The result is
@ -239,7 +260,7 @@ class Compiler
def multiply def multiply
match('*') match('*')
factor # Result is in eax. factor # Result is in eax.
imul('dword [esp]') # Multiply a by b. x86_imul('dword [esp]') # Multiply a by b.
end end
# Parse a division operator and the divisor (b). The result is # Parse a division operator and the divisor (b). The result is
@ -247,9 +268,14 @@ class Compiler
def divide def divide
match('/') match('/')
factor # Result is in eax. factor # Result is in eax.
xchg('eax', '[esp]') # Swap the divisor and dividend into x86_xchg(:eax, '[esp]') # Swap the divisor and dividend into
# the correct places. # the correct places.
idiv('dword [esp]') # Divide a (eax) by b ([esp]).
# idiv uses edx:eax as the dividend so we need to ensure that edx
# is correctly sign-extended w.r.t. eax.
emit('cdq') # Sign-extend eax into edx (Convert Double to
# Quad).
x86_idiv('dword [esp]') # Divide a (eax) by b ([esp]).
end end
@ -273,39 +299,43 @@ private
# Some asm methods for convenience and arity checks. # Some asm methods for convenience and arity checks.
def mov(dest, src) def x86_mov(dest, src)
emit("mov #{dest}, #{src}") emit("mov #{dest}, #{src}")
end end
def add(dest, src) def x86_add(dest, src)
emit("add #{dest}, #{src}") emit("add #{dest}, #{src}")
end end
def sub(dest, src) def x86_sub(dest, src)
emit("sub #{dest}, #{src}") emit("sub #{dest}, #{src}")
end end
def imul(op) def x86_imul(op)
emit("imul #{op}") emit("imul #{op}")
end end
def idiv(op) def x86_idiv(op)
emit("idiv #{op}") emit("idiv #{op}")
end end
def push(reg) def x86_push(reg)
emit("push #{reg}") emit("push #{reg}")
end end
def call(label) def x86_call(label)
emit("call #{label}") emit("call #{label}")
end end
def neg(reg) def x86_neg(reg)
emit("neg #{reg}") emit("neg #{reg}")
end end
def xchg(op1, op2) def x86_xchg(op1, op2)
emit("xchg #{op1}, #{op2}") emit("xchg #{op1}, #{op2}")
end end
def x86_xor(op1, op2)
emit("xor #{op1}, #{op2}")
end
end end

View file

@ -1,5 +1,11 @@
a=9 a=9
5*(3-5)*2+2-9/3-8/2-4*(5+5+5) aa=10
a-1 somethinglong=65536
x()+1 b=5*(3-5)
-1 c=a-1
d=-1
e=b*2+2
f=e-27/9
g=f-8/2
h=g-4*(5+5+5)
i=h+85