mirror of
https://github.com/samsonjs/compiler.git
synced 2026-04-27 14:57:45 +00:00
[NEW] ignore most whitespace. Fixed division (use cdq before idiv).
This commit is contained in:
parent
c008ed007a
commit
bd0408fa2f
2 changed files with 86 additions and 50 deletions
122
compiler.rb
122
compiler.rb
|
|
@ -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
|
||||||
|
|
|
||||||
14
test.code
14
test.code
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue