Code spacing.
This commit is contained in:
parent
505d280fe0
commit
9eecd92c7e
1 changed files with 796 additions and 701 deletions
|
|
@ -37,8 +37,11 @@ sort of thing:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def fib(x) if x < 3 then 1 else fib(x-1) +
|
def fib(x)
|
||||||
fib(x-2)
|
if x < 3 then
|
||||||
|
1
|
||||||
|
else
|
||||||
|
fib(x-1) + fib(x-2)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -69,8 +72,12 @@ for the relevant tokens:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
class IfToken(object): pass class
|
class IfToken(object):
|
||||||
ThenToken(object): pass class ElseToken(object): pass
|
pass
|
||||||
|
class ThenToken(object):
|
||||||
|
pass
|
||||||
|
class ElseToken(object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -80,11 +87,19 @@ pretty simple stuff:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
... if identifier == 'def': yield DefToken() elif
|
...
|
||||||
identifier == 'extern': yield ExternToken() elif identifier == 'if':
|
if identifier == 'def':
|
||||||
yield IfToken() elif identifier == 'then': yield ThenToken() elif
|
yield DefToken()
|
||||||
identifier == 'else': yield ElseToken() else: yield
|
elif identifier == 'extern':
|
||||||
IdentifierToken(identifier)
|
yield ExternToken()
|
||||||
|
elif identifier == 'if':
|
||||||
|
yield IfToken()
|
||||||
|
elif identifier == 'then':
|
||||||
|
yield ThenToken()
|
||||||
|
elif identifier == 'else':
|
||||||
|
yield ElseToken()
|
||||||
|
else:
|
||||||
|
yield IdentifierToken(identifier)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -96,14 +111,16 @@ To represent the new expression we add a new AST node for it:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
# Expression class for if/then/else. class
|
# Expression class for if/then/else.
|
||||||
IfExpressionNode(ExpressionNode):
|
class IfExpressionNode(ExpressionNode):
|
||||||
|
|
||||||
def __init__(self, condition, then_branch, else_branch):
|
def __init__(self, condition, then_branch, else_branch):
|
||||||
self.condition = condition self.then_branch = then_branch
|
self.condition = condition
|
||||||
|
self.then_branch = then_branch
|
||||||
self.else_branch = else_branch
|
self.else_branch = else_branch
|
||||||
|
|
||||||
def CodeGen(self): ...
|
def CodeGen(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -119,10 +136,9 @@ First we define a new parsing function:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
# ifexpr ::= 'if' expression 'then' expression
|
# ifexpr ::= 'if' expression 'then' expression 'else' expression
|
||||||
'else' expression def ParseIfExpr(self): self.Next() # eat the if.
|
def ParseIfExpr(self):
|
||||||
|
self.Next() # eat the if.
|
||||||
::
|
|
||||||
|
|
||||||
# condition.
|
# condition.
|
||||||
condition = self.ParseExpression()
|
condition = self.ParseExpression()
|
||||||
|
|
@ -150,13 +166,17 @@ Next we hook it up as a primary expression:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def ParsePrimary(self): if
|
def ParsePrimary(self):
|
||||||
isinstance(self.current, IdentifierToken): return
|
if isinstance(self.current, IdentifierToken):
|
||||||
self.ParseIdentifierExpr() elif isinstance(self.current, NumberToken):
|
return self.ParseIdentifierExpr()
|
||||||
return self.ParseNumberExpr(); elif isinstance(self.current, IfToken):
|
elif isinstance(self.current, NumberToken):
|
||||||
return self.ParseIfExpr() elif self.current == CharacterToken('('):
|
return self.ParseNumberExpr();
|
||||||
return self.ParseParenExpr() else: raise RuntimeError('Unknown token
|
elif isinstance(self.current, IfToken):
|
||||||
when expecting an expression.')
|
return self.ParseIfExpr()
|
||||||
|
elif self.current == CharacterToken('('):
|
||||||
|
return self.ParseParenExpr()
|
||||||
|
else:
|
||||||
|
raise RuntimeError('Unknown token when expecting an expression.')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -175,8 +195,9 @@ example. Consider:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
extern foo(); extern bar(); def baz(x) if x then
|
extern foo();
|
||||||
foo() else bar();
|
extern bar();
|
||||||
|
def baz(x) if x then foo() else bar();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -186,13 +207,26 @@ Kaleidoscope looks something like this:
|
||||||
|
|
||||||
.. code-block:: llvm
|
.. code-block:: llvm
|
||||||
|
|
||||||
declare double @foo() declare double @bar() define
|
declare double @foo()
|
||||||
double @baz(double %x) { entry: %ifcond = fcmp one double %x,
|
|
||||||
0.000000e+00 br i1 %ifcond, label %then, label %else then: ; preds =
|
declare double @bar()
|
||||||
%entry %calltmp1 = call double @bar() else: ; preds = %entry %calltmp1 =
|
|
||||||
call double @bar() br label %ifcont ifcont: ; preds = %else, %then
|
define double @baz(double %x) {
|
||||||
%iftmp = phi double [ %calltmp, %then ], [ %calltmp1, %else ] ret double
|
entry:
|
||||||
%iftmp }
|
%ifcond = fcmp one double %x, 0.000000e+00
|
||||||
|
br i1 %ifcond, label %then, label %else
|
||||||
|
|
||||||
|
then: ; preds = %entry
|
||||||
|
%calltmp1 = call double @bar()
|
||||||
|
|
||||||
|
else: ; preds = %entry
|
||||||
|
%calltmp1 = call double @bar()
|
||||||
|
br label %ifcont
|
||||||
|
|
||||||
|
ifcont: ; preds = %else, %then
|
||||||
|
%iftmp = phi double [ %calltmp, %then ], [ %calltmp1, %else ]
|
||||||
|
ret double %iftmp
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -247,8 +281,8 @@ practice, there are two sorts of values that float around in code
|
||||||
written for your average imperative programming language that might need
|
written for your average imperative programming language that might need
|
||||||
Phi nodes:
|
Phi nodes:
|
||||||
|
|
||||||
1. Code that involves user variables: ``x = 1; x = x + 1;``
|
- 1. Code that involves user variables: ``x = 1; x = x + 1;``
|
||||||
2. Values that are implicit in the structure of your AST, such as the
|
- 2. Values that are implicit in the structure of your AST, such as the
|
||||||
Phi node in this case.
|
Phi node in this case.
|
||||||
|
|
||||||
In `Chapter 7 <PythonLangImpl7.html>`_ of this tutorial ("mutable
|
In `Chapter 7 <PythonLangImpl7.html>`_ of this tutorial ("mutable
|
||||||
|
|
@ -269,10 +303,8 @@ for ``IfExpressionNode``:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def CodeGen(self): condition =
|
def CodeGen(self):
|
||||||
self.condition.CodeGen()
|
condition = self.condition.CodeGen()
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
# Convert condition to a bool by comparing equal to 0.0.
|
# Convert condition to a bool by comparing equal to 0.0.
|
||||||
condition_bool = g_llvm_builder.fcmp(
|
condition_bool = g_llvm_builder.fcmp(
|
||||||
|
|
@ -291,8 +323,6 @@ a truth value as a 1-bit (bool) value.
|
||||||
|
|
||||||
function = g_llvm_builder.basic_block.function
|
function = g_llvm_builder.basic_block.function
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
# Create blocks for the then and else cases. Insert the 'then' block at the
|
# Create blocks for the then and else cases. Insert the 'then' block at the
|
||||||
# end of the function.
|
# end of the function.
|
||||||
then_block = function.append_basic_block('then')
|
then_block = function.append_basic_block('then')
|
||||||
|
|
@ -322,10 +352,9 @@ still inserting into the block that the condition went into.
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
# Emit then value.
|
# Emit then value.
|
||||||
g_llvm_builder.position_at_end(then_block) then_value =
|
g_llvm_builder.position_at_end(then_block)
|
||||||
self.then_branch.CodeGen() g_llvm_builder.branch(merge_block)
|
then_value = self.then_branch.CodeGen()
|
||||||
|
g_llvm_builder.branch(merge_block)
|
||||||
::
|
|
||||||
|
|
||||||
# Codegen of 'Then' can change the current block; update then_block for the
|
# Codegen of 'Then' can change the current block; update then_block for the
|
||||||
# PHI node.
|
# PHI node.
|
||||||
|
|
@ -369,10 +398,9 @@ value for code that will set up the Phi node.
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
# Emit else block.
|
# Emit else block.
|
||||||
g_llvm_builder.position_at_end(else_block) else_value =
|
g_llvm_builder.position_at_end(else_block)
|
||||||
self.else_branch.CodeGen() g_llvm_builder.branch(merge_block)
|
else_value = self.else_branch.CodeGen()
|
||||||
|
g_llvm_builder.branch(merge_block)
|
||||||
::
|
|
||||||
|
|
||||||
# Codegen of 'Else' can change the current block, update else_block for the
|
# Codegen of 'Else' can change the current block, update else_block for the
|
||||||
# PHI node.
|
# PHI node.
|
||||||
|
|
@ -393,13 +421,11 @@ code:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
# Emit merge block.
|
# Emit merge block.
|
||||||
g_llvm_builder.position_at_end(merge_block) phi =
|
g_llvm_builder.position_at_end(merge_block)
|
||||||
g_llvm_builder.phi(Type.double(), 'iftmp')
|
phi = g_llvm_builder.phi(Type.double(), 'iftmp')
|
||||||
phi.add_incoming(then_value, then_block)
|
phi.add_incoming(then_value, then_block)
|
||||||
phi.add_incoming(else_value, else_block)
|
phi.add_incoming(else_value, else_block)
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
return phi
|
return phi
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -433,10 +459,10 @@ something more aggressive, a 'for' expression:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
extern putchard(char) def printstar(n) for i = 1,
|
extern putchard(char)
|
||||||
i < n, 1.0 in putchard(42) # ascii 42 = '\*'
|
def printstar(n)
|
||||||
|
for i = 1, i < n, 1.0 in
|
||||||
::
|
putchard(42) # ascii 42 = '*'
|
||||||
|
|
||||||
# print 100 '*' characters
|
# print 100 '*' characters
|
||||||
printstar(100)
|
printstar(100)
|
||||||
|
|
@ -466,15 +492,19 @@ The lexer extensions are the same sort of thing as for if/then/else:
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
class ThenToken(object): pass class ElseToken(object): pass class
|
class ThenToken(object):
|
||||||
ForToken(object): pass class InToken(object): pass
|
pass
|
||||||
|
class ElseToken(object):
|
||||||
|
pass
|
||||||
|
class ForToken(object):
|
||||||
|
pass
|
||||||
|
class InToken(object):
|
||||||
|
pass
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
def Tokenize(string):
|
def Tokenize(string):
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
elif identifier == 'else':
|
elif identifier == 'else':
|
||||||
|
|
@ -499,14 +529,18 @@ variable name and the constituent expressions in the node.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
# Expression class for for/in. class
|
# Expression class for for/in.
|
||||||
ForExpressionNode(ExpressionNode):
|
class ForExpressionNode(ExpressionNode):
|
||||||
|
|
||||||
def __init__(self, loop_variable, start, end, step, body):
|
def __init__(self, loop_variable, start, end, step, body):
|
||||||
self.loop_variable = loop_variable self.start = start self.end = end
|
self.loop_variable = loop_variable
|
||||||
self.step = step self.body = body
|
self.start = start
|
||||||
|
self.end = end
|
||||||
|
self.step = step
|
||||||
|
self.body = body
|
||||||
|
|
||||||
def CodeGen(self): ...
|
def CodeGen(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -521,11 +555,9 @@ value to null in the AST node:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
# forexpr ::= 'for' identifier '=' expr ',' expr
|
# forexpr ::= 'for' identifier '=' expr ',' expr (',' expr)? 'in' expression
|
||||||
(',' expr)? 'in' expression def ParseForExpr(self): self.Next() # eat
|
def ParseForExpr(self):
|
||||||
the for.
|
self.Next() # eat the for.
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
if not isinstance(self.current, IdentifierToken):
|
if not isinstance(self.current, IdentifierToken):
|
||||||
raise RuntimeError('Expected identifier after for.')
|
raise RuntimeError('Expected identifier after for.')
|
||||||
|
|
@ -574,16 +606,30 @@ this dump is generated with optimizations disabled for clarity):
|
||||||
|
|
||||||
.. code-block:: llvm
|
.. code-block:: llvm
|
||||||
|
|
||||||
declare double @putchard(double) define double
|
declare double @putchard(double)
|
||||||
@printstar(double %n) { entry: ; initial value = 1.0 (inlined into phi)
|
|
||||||
br label %loop loop: ; preds = %loop, %entry %i = phi double [
|
define double @printstar(double %n) {
|
||||||
1.000000e+00, %entry ], [ %nextvar, %loop ] ; body %calltmp = call
|
entry:
|
||||||
double @putchard(double 4.200000e+01) ; increment %nextvar = fadd double
|
; initial value = 1.0 (inlined into phi)
|
||||||
%i, 1.000000e+00 ; termination test %cmptmp = fcmp ult double %i, %n
|
br label %loop
|
||||||
%booltmp = uitofp i1 %cmptmp to double %loopcond = fcmp one double
|
|
||||||
%booltmp, 0.000000e+00 br i1 %loopcond, label %loop, label %afterloop
|
loop: ; preds = %loop, %entry
|
||||||
afterloop: ; preds = %loop ; loop always returns 0.0 ret double
|
%i = phi double [
|
||||||
0.000000e+00 }
|
1.000000e+00, %entry ], [ %nextvar, %loop ]
|
||||||
|
; body
|
||||||
|
%calltmp = call double @putchard(double 4.200000e+01)
|
||||||
|
; increment
|
||||||
|
%nextvar = fadd double %i, 1.000000e+00
|
||||||
|
|
||||||
|
; termination test
|
||||||
|
%cmptmp = fcmp ult double %i, %n
|
||||||
|
%booltmp = uitofp i1 %cmptmp to double
|
||||||
|
%loopcond = fcmp one double %booltmp, 0.000000e+00
|
||||||
|
br i1 %loopcond, label %loop, label %afterloop
|
||||||
|
|
||||||
|
afterloop: ; preds = %loop
|
||||||
|
; loop always returns 0.0
|
||||||
|
ret double 0.000000e+00 }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -600,8 +646,9 @@ expression for the loop value:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def CodeGen(self): # Emit the start code first,
|
def CodeGen(self):
|
||||||
without 'variable' in scope. start_value = self.start.CodeGen()
|
# Emit the start code first, without 'variable' in scope.
|
||||||
|
start_value = self.start.CodeGen()
|
||||||
|
|
||||||
With this out of the way, the next step is to set up the LLVM basic
|
With this out of the way, the next step is to set up the LLVM basic
|
||||||
block for the start of the loop body. In the case above, the whole loop
|
block for the start of the loop body. In the case above, the whole loop
|
||||||
|
|
@ -609,13 +656,13 @@ expression for the loop value:
|
||||||
of multiple blocks (e.g. if it contains an if/then/else or a for/in
|
of multiple blocks (e.g. if it contains an if/then/else or a for/in
|
||||||
expression).
|
expression).
|
||||||
|
|
||||||
# Make the new basic block for the loop header,
|
.. code-block:: python
|
||||||
inserting after current # block. function =
|
|
||||||
g_llvm_builder.basic_block.function pre_header_block =
|
|
||||||
g_llvm_builder.basic_block loop_block =
|
|
||||||
function.append_basic_block('loop')
|
|
||||||
|
|
||||||
::
|
# Make the new basic block for the loop header, inserting after current
|
||||||
|
# block.
|
||||||
|
function = g_llvm_builder.basic_block.function
|
||||||
|
pre_header_block = g_llvm_builder.basic_block
|
||||||
|
loop_block = function.append_basic_block('loop')
|
||||||
|
|
||||||
# Insert an explicit fallthrough from the current block to the loop_block.
|
# Insert an explicit fallthrough from the current block to the loop_block.
|
||||||
g_llvm_builder.branch(loop_block)
|
g_llvm_builder.branch(loop_block)
|
||||||
|
|
@ -636,8 +683,6 @@ the two blocks.
|
||||||
# Start insertion in loop_block.
|
# Start insertion in loop_block.
|
||||||
g_llvm_builder.position_at_end(loop_block);
|
g_llvm_builder.position_at_end(loop_block);
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
# Start the PHI node with an entry for start.
|
# Start the PHI node with an entry for start.
|
||||||
variable_phi = g_llvm_builder.phi(Type.double(), self.loop_variable)
|
variable_phi = g_llvm_builder.phi(Type.double(), self.loop_variable)
|
||||||
variable_phi.add_incoming(start_value, pre_header_block)
|
variable_phi.add_incoming(start_value, pre_header_block)
|
||||||
|
|
@ -656,14 +701,11 @@ backedge, but we can't set it up yet (because it doesn't exist!).
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
# Within the loop, the variable is defined equal
|
# Within the loop, the variable is defined equal to the PHI node. If it
|
||||||
to the PHI node. If it # shadows an existing variable, we have to
|
# shadows an existing variable, we have to restore it, so save it now.
|
||||||
restore it, so save it now. old_value =
|
old_value = g_named_values.get(self.loop_variable, None)
|
||||||
g_named_values.get(self.loop_variable, None)
|
|
||||||
g_named_values[self.loop_variable] = variable_phi
|
g_named_values[self.loop_variable] = variable_phi
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
# Emit the body of the loop. This, like any other expr, can change the
|
# Emit the body of the loop. This, like any other expr, can change the
|
||||||
# current BB. Note that we ignore the value computed by the body.
|
# current BB. Note that we ignore the value computed by the body.
|
||||||
self.body.CodeGen()
|
self.body.CodeGen()
|
||||||
|
|
@ -692,11 +734,12 @@ table.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
# Emit the step value. if self.step: step_value
|
# Emit the step value.
|
||||||
= self.step.CodeGen() else: # If not specified, use 1.0. step_value =
|
if self.step:
|
||||||
Constant.real(Type.double(), 1)
|
step_value = self.step.CodeGen()
|
||||||
|
else:
|
||||||
::
|
# If not specified, use 1.0.
|
||||||
|
step_value = Constant.real(Type.double(), 1)
|
||||||
|
|
||||||
next_value = g_llvm_builder.fadd(variable_phi, step_value, 'next')
|
next_value = g_llvm_builder.fadd(variable_phi, step_value, 'next')
|
||||||
|
|
||||||
|
|
@ -712,10 +755,10 @@ iteration of the loop.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
# Compute the end condition and convert it to a
|
# Compute the end condition and convert it to a bool by comparing to 0.0.
|
||||||
bool by comparing to 0.0. end_condition = self.end.CodeGen()
|
end_condition = self.end.CodeGen()
|
||||||
end_condition_bool = g_llvm_builder.fcmp( FCMP_ONE, end_condition,
|
end_condition_bool = g_llvm_builder.fcmp(
|
||||||
Constant.real(Type.double(), 0), 'loopcond')
|
FCMP_ONE, end_condition, Constant.real(Type.double(), 0), 'loopcond')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -727,10 +770,8 @@ if/then/else statement.
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
# Create the "after loop" block and insert it.
|
# Create the "after loop" block and insert it.
|
||||||
loop_end_block = g_llvm_builder.basic_block after_block =
|
loop_end_block = g_llvm_builder.basic_block
|
||||||
function.append_basic_block('afterloop')
|
after_block = function.append_basic_block('afterloop')
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
# Insert the conditional branch into the end of loop_end_block.
|
# Insert the conditional branch into the end of loop_end_block.
|
||||||
g_llvm_builder.cbranch(end_condition_bool, loop_block, after_block)
|
g_llvm_builder.cbranch(end_condition_bool, loop_block, after_block)
|
||||||
|
|
@ -753,10 +794,8 @@ insertion position to it.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
# Add a new entry to the PHI node for the
|
# Add a new entry to the PHI node for the backedge.
|
||||||
backedge. variable_phi.add_incoming(next_value, loop_end_block)
|
variable_phi.add_incoming(next_value, loop_end_block)
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
# Restore the unshadowed variable.
|
# Restore the unshadowed variable.
|
||||||
if old_value:
|
if old_value:
|
||||||
|
|
@ -798,17 +837,22 @@ the if/then/else and for expressions:
|
||||||
|
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import re from llvm.core import Module, Constant, Type, Function,
|
import re
|
||||||
Builder from llvm.ee import ExecutionEngine, TargetData from llvm.passes
|
from llvm.core import Module, Constant, Type, Function, Builder
|
||||||
import FunctionPassManager
|
from llvm.ee import ExecutionEngine, TargetData
|
||||||
|
from llvm.passes import FunctionPassManager
|
||||||
|
|
||||||
from llvm.core import FCMP_ULT, FCMP_ONE from llvm.passes import
|
from llvm.core import FCMP_ULT, FCMP_ONE
|
||||||
(PASS_INSTRUCTION_COMBINING, PASS_REASSOCIATE, PASS_GVN,
|
from llvm.passes import (PASS_INSTRUCTION_COMBINING,
|
||||||
|
PASS_REASSOCIATE,
|
||||||
|
PASS_GVN,
|
||||||
PASS_CFG_SIMPLIFICATION)
|
PASS_CFG_SIMPLIFICATION)
|
||||||
|
|
||||||
Globals
|
Globals
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
# The LLVM module, which holds all the IR code.
|
# The LLVM module, which holds all the IR code.
|
||||||
g_llvm_module = Module.new('my cool jit')
|
g_llvm_module = Module.new('my cool jit')
|
||||||
|
|
||||||
|
|
@ -828,31 +872,53 @@ the if/then/else and for expressions:
|
||||||
Lexer
|
Lexer
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
# The lexer yields one of these types for each token.
|
# The lexer yields one of these types for each token.
|
||||||
class EOFToken(object): pass class DefToken(object): pass class
|
class EOFToken(object):
|
||||||
ExternToken(object): pass class IfToken(object): pass class
|
pass
|
||||||
ThenToken(object): pass class ElseToken(object): pass class
|
class DefToken(object):
|
||||||
ForToken(object): pass class InToken(object): pass
|
pass
|
||||||
|
class ExternToken(object):
|
||||||
|
pass
|
||||||
|
class IfToken(object):
|
||||||
|
pass
|
||||||
|
class ThenToken(object):
|
||||||
|
pass
|
||||||
|
class ElseToken(object):
|
||||||
|
pass
|
||||||
|
class ForToken(object):
|
||||||
|
pass
|
||||||
|
class InToken(object):
|
||||||
|
pass
|
||||||
|
|
||||||
class IdentifierToken(object): def __init__(self, name): self.name =
|
class IdentifierToken(object):
|
||||||
name
|
def __init__(self, name):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
class NumberToken(object): def __init__(self, value): self.value =
|
class NumberToken(object):
|
||||||
value
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
|
||||||
class CharacterToken(object): def __init__(self, char): self.char =
|
class CharacterToken(object):
|
||||||
char def __eq__(self, other): return isinstance(other, CharacterToken)
|
def __init__(self, char):
|
||||||
and self.char == other.char def __ne__(self, other): return not self
|
self.char = char
|
||||||
== other
|
def __eq__(self, other):
|
||||||
|
return isinstance(other, CharacterToken) and self.char == other.char
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not self == other
|
||||||
|
|
||||||
# Regular expressions that tokens and comments of our language.
|
# Regular expressions that tokens and comments of our language.
|
||||||
REGEX_NUMBER = re.compile('[0-9]+(?:.[0-9]+)?') REGEX_IDENTIFIER =
|
REGEX_NUMBER = re.compile('[0-9]+(?:.[0-9]+)?')
|
||||||
re.compile('[a-zA-Z][a-zA-Z0-9]\ *') REGEX_COMMENT = re.compile('#.*')
|
REGEX_IDENTIFIER = re.compile('[a-zA-Z][a-zA-Z0-9]\ *')
|
||||||
|
REGEX_COMMENT = re.compile('#.*')
|
||||||
|
|
||||||
def Tokenize(string): while string: # Skip whitespace. if
|
def Tokenize(string):
|
||||||
string[0].isspace(): string = string[1:] continue
|
while string:
|
||||||
|
# Skip whitespace.
|
||||||
::
|
if string[0].isspace():
|
||||||
|
string = string[1:]
|
||||||
|
continue
|
||||||
|
|
||||||
# Run regexes.
|
# Run regexes.
|
||||||
comment_match = REGEX_COMMENT.match(string)
|
comment_match = REGEX_COMMENT.match(string)
|
||||||
|
|
@ -897,35 +963,44 @@ the if/then/else and for expressions:
|
||||||
Abstract Syntax Tree (aka Parse Tree)
|
Abstract Syntax Tree (aka Parse Tree)
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
# Base class for all expression nodes.
|
# Base class for all expression nodes.
|
||||||
class ExpressionNode(object): pass
|
class ExpressionNode(object):
|
||||||
|
pass
|
||||||
|
|
||||||
# Expression class for numeric literals like "1.0".
|
# Expression class for numeric literals like "1.0".
|
||||||
class NumberExpressionNode(ExpressionNode):
|
class NumberExpressionNode(ExpressionNode):
|
||||||
|
|
||||||
def __init__(self, value): self.value = value
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
|
||||||
def CodeGen(self): return Constant.real(Type.double(), self.value)
|
def CodeGen(self):
|
||||||
|
return Constant.real(Type.double(), self.value)
|
||||||
|
|
||||||
# Expression class for referencing a variable, like "a".
|
# Expression class for referencing a variable, like "a".
|
||||||
class VariableExpressionNode(ExpressionNode):
|
class VariableExpressionNode(ExpressionNode):
|
||||||
|
|
||||||
def __init__(self, name): self.name = name
|
def __init__(self, name):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
def CodeGen(self): if self.name in g_named_values: return
|
def CodeGen(self):
|
||||||
g_named_values[self.name] else: raise RuntimeError('Unknown variable
|
if self.name in g_named_values:
|
||||||
name: ' + self.name)
|
return g_named_values[self.name]
|
||||||
|
else:
|
||||||
|
raise RuntimeError('Unknown variable name: ' + self.name)
|
||||||
|
|
||||||
# Expression class for a binary operator.
|
# Expression class for a binary operator.
|
||||||
class BinaryOperatorExpressionNode(ExpressionNode):
|
class BinaryOperatorExpressionNode(ExpressionNode):
|
||||||
|
|
||||||
def __init__(self, operator, left, right): self.operator = operator
|
def __init__(self, operator, left, right):
|
||||||
self.left = left self.right = right
|
self.operator = operator
|
||||||
|
self.left = left
|
||||||
|
self.right = right
|
||||||
|
|
||||||
def CodeGen(self): left = self.left.CodeGen() right =
|
def CodeGen(self):
|
||||||
self.right.CodeGen()
|
left = self.left.CodeGen()
|
||||||
|
right = self.right.CodeGen()
|
||||||
::
|
|
||||||
|
|
||||||
if self.operator == '+':
|
if self.operator == '+':
|
||||||
return g_llvm_builder.fadd(left, right, 'addtmp')
|
return g_llvm_builder.fadd(left, right, 'addtmp')
|
||||||
|
|
@ -943,13 +1018,13 @@ the if/then/else and for expressions:
|
||||||
# Expression class for function calls.
|
# Expression class for function calls.
|
||||||
class CallExpressionNode(ExpressionNode):
|
class CallExpressionNode(ExpressionNode):
|
||||||
|
|
||||||
def __init__(self, callee, args): self.callee = callee self.args =
|
def __init__(self, callee, args):
|
||||||
args
|
self.callee = callee
|
||||||
|
self.args = args
|
||||||
|
|
||||||
def CodeGen(self): # Look up the name in the global module table. callee
|
def CodeGen(self):
|
||||||
= g_llvm_module.get_function_named(self.callee)
|
# Look up the name in the global module table.
|
||||||
|
callee = g_llvm_module.get_function_named(self.callee)
|
||||||
::
|
|
||||||
|
|
||||||
# Check for argument mismatch error.
|
# Check for argument mismatch error.
|
||||||
if len(callee.args) != len(self.args):
|
if len(callee.args) != len(self.args):
|
||||||
|
|
@ -963,12 +1038,12 @@ the if/then/else and for expressions:
|
||||||
class IfExpressionNode(ExpressionNode):
|
class IfExpressionNode(ExpressionNode):
|
||||||
|
|
||||||
def __init__(self, condition, then_branch, else_branch):
|
def __init__(self, condition, then_branch, else_branch):
|
||||||
self.condition = condition self.then_branch = then_branch
|
self.condition = condition
|
||||||
|
self.then_branch = then_branch
|
||||||
self.else_branch = else_branch
|
self.else_branch = else_branch
|
||||||
|
|
||||||
def CodeGen(self): condition = self.condition.CodeGen()
|
def CodeGen(self):
|
||||||
|
condition = self.condition.CodeGen()
|
||||||
::
|
|
||||||
|
|
||||||
# Convert condition to a bool by comparing equal to 0.0.
|
# Convert condition to a bool by comparing equal to 0.0.
|
||||||
condition_bool = g_llvm_builder.fcmp(
|
condition_bool = g_llvm_builder.fcmp(
|
||||||
|
|
@ -1014,16 +1089,26 @@ the if/then/else and for expressions:
|
||||||
class ForExpressionNode(ExpressionNode):
|
class ForExpressionNode(ExpressionNode):
|
||||||
|
|
||||||
def __init__(self, loop_variable, start, end, step, body):
|
def __init__(self, loop_variable, start, end, step, body):
|
||||||
self.loop_variable = loop_variable self.start = start self.end = end
|
self.loop_variable = loop_variable
|
||||||
self.step = step self.body = body
|
self.start = start
|
||||||
|
self.end = end
|
||||||
|
self.step = step
|
||||||
|
self.body = body
|
||||||
|
|
||||||
def CodeGen(self): # Output this as: # ... # start = startexpr # goto
|
def CodeGen(self):
|
||||||
loop # loop: # variable = phi [start, loopheader], [nextvariable,
|
# Output this as:
|
||||||
loopend] # ... # bodyexpr # ... # loopend: # step = stepexpr #
|
# ...
|
||||||
nextvariable = variable + step # endcond = endexpr # br endcond, loop,
|
# start = startexpr
|
||||||
endloop # outloop:
|
# goto loop
|
||||||
|
# loop:
|
||||||
::
|
# variable = phi [start, loopheader], [nextvariable, loopend]
|
||||||
|
# ...
|
||||||
|
# bodyexpr
|
||||||
|
# ...
|
||||||
|
# loopend:
|
||||||
|
# step = stepexpr
|
||||||
|
# nextvariable = variable + step # endcond = endexpr # br endcond, loop, endloop
|
||||||
|
# outloop:
|
||||||
|
|
||||||
# Emit the start code first, without 'variable' in scope.
|
# Emit the start code first, without 'variable' in scope.
|
||||||
start_value = self.start.CodeGen()
|
start_value = self.start.CodeGen()
|
||||||
|
|
@ -1094,13 +1179,14 @@ the if/then/else and for expressions:
|
||||||
# takes).
|
# takes).
|
||||||
class PrototypeNode(object):
|
class PrototypeNode(object):
|
||||||
|
|
||||||
def __init__(self, name, args): self.name = name self.args = args
|
def __init__(self, name, args):
|
||||||
|
self.name = name
|
||||||
|
self.args = args
|
||||||
|
|
||||||
def CodeGen(self): # Make the function type, eg. double(double,double).
|
def CodeGen(self):
|
||||||
funct_type = Type.function( Type.double(), [Type.double()] \*
|
# Make the function type, eg. double(double,double).
|
||||||
len(self.args), False)
|
funct_type = Type.function(
|
||||||
|
Type.double(), [Type.double()] * len(self.args), False)
|
||||||
::
|
|
||||||
|
|
||||||
function = Function.new(g_llvm_module, funct_type, self.name)
|
function = Function.new(g_llvm_module, funct_type, self.name)
|
||||||
|
|
||||||
|
|
@ -1130,12 +1216,13 @@ the if/then/else and for expressions:
|
||||||
# This class represents a function definition itself.
|
# This class represents a function definition itself.
|
||||||
class FunctionNode(object):
|
class FunctionNode(object):
|
||||||
|
|
||||||
def __init__(self, prototype, body): self.prototype = prototype
|
def __init__(self, prototype, body):
|
||||||
|
self.prototype = prototype
|
||||||
self.body = body
|
self.body = body
|
||||||
|
|
||||||
def CodeGen(self): # Clear scope. g_named_values.clear()
|
def CodeGen(self):
|
||||||
|
# Clear scope.
|
||||||
::
|
g_named_values.clear()
|
||||||
|
|
||||||
# Create a function object.
|
# Create a function object.
|
||||||
function = self.prototype.CodeGen()
|
function = self.prototype.CodeGen()
|
||||||
|
|
@ -1164,27 +1251,33 @@ the if/then/else and for expressions:
|
||||||
Parser
|
Parser
|
||||||
------
|
------
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
class Parser(object):
|
class Parser(object):
|
||||||
|
|
||||||
def __init__(self, tokens, binop_precedence): self.tokens = tokens
|
def __init__(self, tokens, binop_precedence):
|
||||||
|
self.tokens = tokens
|
||||||
self.binop_precedence = binop_precedence self.Next()
|
self.binop_precedence = binop_precedence self.Next()
|
||||||
|
|
||||||
# Provide a simple token buffer. Parser.current is the current token the
|
# Provide a simple token buffer. Parser.current is the current token the
|
||||||
# parser is looking at. Parser.Next() reads another token from the lexer
|
# parser is looking at. Parser.Next() reads another token from the lexer and
|
||||||
and # updates Parser.current with its results. def Next(self):
|
# updates Parser.current with its results.
|
||||||
|
def Next(self):
|
||||||
self.current = self.tokens.next()
|
self.current = self.tokens.next()
|
||||||
|
|
||||||
# Gets the precedence of the current token, or -1 if the token is not a
|
# Gets the precedence of the current token, or -1 if the token is not a binary
|
||||||
binary # operator. def GetCurrentTokenPrecedence(self): if
|
# operator.
|
||||||
isinstance(self.current, CharacterToken): return
|
def GetCurrentTokenPrecedence(self):
|
||||||
self.binop_precedence.get(self.current.char, -1) else: return -1
|
if isinstance(self.current, CharacterToken):
|
||||||
|
return self.binop_precedence.get(self.current.char, -1)
|
||||||
|
else:
|
||||||
|
return -1
|
||||||
|
|
||||||
# identifierexpr ::= identifier \| identifier '(' expression\* ')' def
|
# identifierexpr ::= identifier | identifier '(' expression* ')'
|
||||||
ParseIdentifierExpr(self): identifier_name = self.current.name
|
def ParseIdentifierExpr(self):
|
||||||
|
identifier_name = self.current.name
|
||||||
self.Next() # eat identifier.
|
self.Next() # eat identifier.
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
if self.current != CharacterToken('('): # Simple variable reference.
|
if self.current != CharacterToken('('): # Simple variable reference.
|
||||||
return VariableExpressionNode(identifier_name)
|
return VariableExpressionNode(identifier_name)
|
||||||
|
|
||||||
|
|
@ -1203,14 +1296,17 @@ the if/then/else and for expressions:
|
||||||
self.Next() # eat ')'.
|
self.Next() # eat ')'.
|
||||||
return CallExpressionNode(identifier_name, args)
|
return CallExpressionNode(identifier_name, args)
|
||||||
|
|
||||||
# numberexpr ::= number def ParseNumberExpr(self): result =
|
# numberexpr ::= number
|
||||||
NumberExpressionNode(self.current.value) self.Next() # consume the
|
def ParseNumberExpr(self):
|
||||||
number. return result
|
result = NumberExpressionNode(self.current.value)
|
||||||
|
self.Next() # consume the number.
|
||||||
|
return result
|
||||||
|
|
||||||
|
# parenexpr ::= '(' expression ')'
|
||||||
|
def ParseParenExpr(self):
|
||||||
|
self.Next() # eat '('.
|
||||||
|
|
||||||
# parenexpr ::= '(' expression ')' def ParseParenExpr(self): self.Next()
|
|
||||||
# eat '('.
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
contents = self.ParseExpression()
|
contents = self.ParseExpression()
|
||||||
|
|
||||||
|
|
@ -1220,10 +1316,9 @@ the if/then/else and for expressions:
|
||||||
|
|
||||||
return contents
|
return contents
|
||||||
|
|
||||||
# ifexpr ::= 'if' expression 'then' expression 'else' expression def
|
# ifexpr ::= 'if' expression 'then' expression 'else' expression
|
||||||
ParseIfExpr(self): self.Next() # eat the if.
|
def ParseIfExpr(self):
|
||||||
|
self.Next() # eat the if.
|
||||||
::
|
|
||||||
|
|
||||||
# condition.
|
# condition.
|
||||||
condition = self.ParseExpression()
|
condition = self.ParseExpression()
|
||||||
|
|
@ -1243,9 +1338,9 @@ the if/then/else and for expressions:
|
||||||
return IfExpressionNode(condition, then_branch, else_branch)
|
return IfExpressionNode(condition, then_branch, else_branch)
|
||||||
|
|
||||||
# forexpr ::= 'for' identifier '=' expr ',' expr (',' expr)? 'in'
|
# forexpr ::= 'for' identifier '=' expr ',' expr (',' expr)? 'in'
|
||||||
expression def ParseForExpr(self): self.Next() # eat the for.
|
expression
|
||||||
|
def ParseForExpr(self):
|
||||||
::
|
self.Next() # eat the for.
|
||||||
|
|
||||||
if not isinstance(self.current, IdentifierToken):
|
if not isinstance(self.current, IdentifierToken):
|
||||||
raise RuntimeError('Expected identifier after for.')
|
raise RuntimeError('Expected identifier after for.')
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue