Permalink
Browse files

Add very hacky, but working, if/else/then support.

  • Loading branch information...
1 parent 522d4e5 commit 066684a445fd44a50a155aeedf478ba2e454beed @MostAwesomeDude committed Apr 7, 2012
Showing with 117 additions and 10 deletions.
  1. +59 −1 cauliflower/control.py
  2. +58 −9 test.py
View
@@ -2,7 +2,8 @@
Flow control and ABI helpers.
"""
-from cauliflower.assembler import A, ADD, PC, SET, SUB, Z, Absolute, assemble
+from cauliflower.assembler import (A, ADD, IFE, PC, POP, SET, SUB, Z,
+ Absolute, assemble)
def call(target):
@@ -17,6 +18,8 @@ def call(target):
Safety not guaranteed; you might not ever come back.
"""
+ print "Calling", hex(target)
+
# Make space on the call stack.
ucode = assemble(SUB, Z, 0x1)
# Hax. Calculate where we currently are based on PC, and then expect that
@@ -46,3 +49,58 @@ def ret():
"""
return assemble(SET, PC, [Z])
+
+
+def if_alone(target):
+ """
+ Consider the current value on the stack. If it's true, then execute a
+ given code block. Otherwise, jump to the next code block.
+ """
+
+ print "Making if", hex(target)
+
+ # We don't know the size of the block we wish to jump over quite yet;
+ # let's figure that out first.
+ block = call(target)
+
+ # Our strategy is to put together a small jump over the block if the value
+ # is false. If it's true, then the IFE will jump over the jump. Double
+ # negatives fail to lose again!
+ ucode = assemble(IFE, 0x0, POP)
+ # Now we jump over the block...
+ ucode += assemble(ADD, PC, len(block) // 2)
+ # And insert the call to the block.
+ ucode += block
+ # All done!
+ return ucode
+
+
+def if_else(target, otherwise):
+ """
+ Add a call to a block directly after an if statement. The block will only
+ be executed if the if block was not executed.
+ """
+
+ print "Making if/else", hex(target), hex(otherwise)
+
+ # We don't know the size of the block we wish to jump over quite yet;
+ # let's figure that out first.
+ ifblock = call(target)
+
+ # Let's also make the else block.
+ elseblock = call(otherwise)
+
+ # Same as before, but with a twist: At the end of the ifblock, we're going
+ # to jump over the else block in the same style.
+ ifblock += assemble(ADD, PC, len(elseblock) // 2)
+
+ # Now assemble as before. First, the test.
+ ucode = assemble(IFE, 0x0, POP)
+ # Now we jump over the block...
+ ucode += assemble(ADD, PC, len(ifblock) // 2)
+ # And insert the call to the block.
+ ucode += ifblock
+ # Now the else block.
+ ucode += elseblock
+ # All done!
+ return ucode
View
67 test.py
@@ -20,7 +20,7 @@
from cauliflower.assembler import I, J, POP, SET, Z, assemble
from cauliflower.builtins import builtin
-from cauliflower.control import call, ret
+from cauliflower.control import call, if_alone, if_else, ret
def bootloader(start):
@@ -56,24 +56,74 @@ def compile_word(word, context):
return builtin(word)
+def compile_if(name, count, words, pc, context):
+ """
+ Find an if statement, compile one or two blocks of it, and return the
+ pieces.
+ """
+
+ print "Compiling if", name, count, words, pc, context
+
+ if_clause = []
+ else_clause = []
+ else_pc = None
+
+ it = iter(words)
+ word = next(it)
+ while word not in ("else", "then"):
+ if_clause.append(word)
+ word = next(it)
+ print "If clause:", if_clause
+ if_pc = pc
+ pc = subroutine("%s_if_%d" % (name, count), if_clause, pc, context)
+
+ if word == "else":
+ word = next(it)
+ while word != "then":
+ else_clause.append(word)
+ word = next(it)
+ print "Else clause:", else_clause
+ else_pc = pc
+ pc = subroutine("%s_else_%d" % (name, count), else_clause, pc,
+ context)
+
+ return count, if_pc, else_pc, pc
+
+
def subroutine(name, words, pc, context):
"""
- Compile a list of words into a new word and add it to the context.
+ Compile a list of words into a new word.
All subroutines, including main, are called into.
"""
ucode = []
-
- for word in words:
- ucode.append(compile_word(word, context))
+ it = iter(words)
+ ifs = 0
+
+ for word in it:
+ if word == "if":
+ ifs, ifpc, elsepc, pc = compile_if(name, ifs, it, pc, context)
+ print "Compiled if", ifs, ifpc, elsepc
+ print "PC is currently", pc
+ if elsepc is None:
+ ucode.append(if_alone(ifpc))
+ else:
+ ucode.append(if_else(ifpc, elsepc))
+ else:
+ ucode.append(compile_word(word, context))
ucode.append(ret())
ucode = "".join(ucode)
+ # Add the word to the dictionary.
context[name] = pc, ucode
- return ucode
+ print "Added", name, context[name]
+ # Add the size of the subroutine to PC.
+ pc += len(ucode) // 2
+
+ return pc
def compile_tokens(tokens, pc, context):
@@ -108,9 +158,8 @@ def compile_tokens(tokens, pc, context):
elif token == ";":
if not subtokens:
raise Exception("Empty word definition!")
- sub = subroutine(subtokens[0], subtokens[1:], pc, context)
- # Add the size of the subroutine to PC.
- pc += len(sub) // 2
+ name = subtokens[0]
+ pc = subroutine(name, subtokens[1:], pc, context)
continue
elif subtokens is not None:
subtokens.append(token)

0 comments on commit 066684a

Please sign in to comment.