mirror of
https://github.com/michaelcmartin/Ophis.git
synced 2024-09-13 19:54:25 +00:00
Massive code modernization spree.
Full PEP8 compliance. Also, booleans have been inserted where they make sense (introduced in 2.3!) and I haven't knowingly added anything that will break 2.3 compatibility. At this point the code really doesn't look like it was written ten years ago. Hooray!
This commit is contained in:
parent
f83379287f
commit
14a37ca879
2
README
2
README
@ -28,4 +28,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
THE SOFTWARE.
|
||||
|
@ -9,4 +9,4 @@ _next: .word 0 ; End of program
|
||||
|
||||
.advance 2064
|
||||
|
||||
.require "kernal.oph"
|
||||
.require "kernal.oph"
|
||||
|
@ -37,4 +37,4 @@ _next: .word 0 ; End of program
|
||||
|
||||
_main:
|
||||
; Program follows...
|
||||
.scend
|
||||
.scend
|
||||
|
@ -249,7 +249,7 @@
|
||||
.checkpc $100
|
||||
</programlisting>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
<section>
|
||||
<title>Macros</title>
|
||||
<para>
|
||||
@ -400,7 +400,7 @@
|
||||
code in and of itself, nor does it overwrite anything that
|
||||
previously existed.</emphasis> If you wish to jump ahead in memory,
|
||||
use <literal>.advance</literal>.</para></listitem>
|
||||
<listitem><para><literal>.require</literal> <emphasis>filename</emphasis>: Includes the entirety
|
||||
<listitem><para><literal>.require</literal> <emphasis>filename</emphasis>: Includes the entirety
|
||||
of the file specified at that point in the program. Unlike <literal>.include</literal>,
|
||||
however, code included with <literal>.require</literal> will only be inserted once.
|
||||
The <literal>.require</literal> directive is useful for ensuring that certain code libraries
|
||||
@ -447,5 +447,5 @@
|
||||
macro definition intends to read. A shorthand for <literal>.invoke</literal>
|
||||
is the name of the macro to invoke, backquoted.</para></listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
</section>
|
||||
</appendix>
|
||||
|
@ -333,7 +333,7 @@ delay: tax
|
||||
iny
|
||||
bne -
|
||||
dex
|
||||
bne -
|
||||
bne -
|
||||
rts
|
||||
</programlisting>
|
||||
</section>
|
||||
|
@ -153,19 +153,19 @@ next: .word 0 ; End of program
|
||||
Labels are defined by putting their name, then a colon, as
|
||||
seen in the definition of <literal>next</literal>.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
<listitem><para>
|
||||
Instead of putting in the hex codes for the string part of
|
||||
the BASIC code, we included the string directly. Each
|
||||
character in the string becomes one byte.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
<listitem><para>
|
||||
Instead of adding the buffer ourselves, we
|
||||
used <literal>.advance</literal>, which outputs zeros until
|
||||
the specified address is reached. Attempting
|
||||
to <literal>.advance</literal> backwards produces an
|
||||
assemble-time error.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
<listitem><para>
|
||||
It has comments that explain what the data are for. The
|
||||
semicolon is the comment marker; everything from a semicolon
|
||||
to the end of the line is ignored.
|
||||
@ -263,9 +263,9 @@ hello: .byte "HELLO, WORLD!", 0
|
||||
<row>
|
||||
<entry align="center">Option</entry>
|
||||
<entry align="center">Effect</entry>
|
||||
</row>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody>
|
||||
<row><entry><option>-6510</option></entry><entry>Allows the 6510 undocumented opcodes as listed in the VICE documentation.</entry></row>
|
||||
<row><entry><option>-65c02</option></entry><entry>Allows opcodes and addressing modes added by the 65C02.</entry></row>
|
||||
<row><entry><option>-v 0</option></entry><entry>Quiet operation. Only reports errors.</entry></row>
|
||||
|
@ -64,4 +64,4 @@
|
||||
|
||||
; ...and character set
|
||||
.alias upper'case 142
|
||||
.alias lower'case 14
|
||||
.alias lower'case 14
|
||||
|
@ -15,4 +15,4 @@ loop: lda hello, x
|
||||
bne loop
|
||||
done: rts
|
||||
|
||||
hello: .byte "HELLO, WORLD!", 0
|
||||
hello: .byte "HELLO, WORLD!", 0
|
||||
|
@ -19,4 +19,4 @@ _next: .word 0 ; End of program
|
||||
bne -
|
||||
* rts
|
||||
|
||||
hello: .byte "HELLO, WORLD!", 0
|
||||
hello: .byte "HELLO, WORLD!", 0
|
||||
|
@ -42,4 +42,4 @@ target6: .byte "NATION", 0
|
||||
target7: .byte "WORLD", 0
|
||||
target8: .byte "SOLAR SYSTEM", 0
|
||||
target9: .byte "GALAXY", 0
|
||||
target10: .byte "UNIVERSE", 0
|
||||
target10: .byte "UNIVERSE", 0
|
||||
|
@ -63,7 +63,7 @@ delay: tax
|
||||
bne -
|
||||
dex
|
||||
bne -
|
||||
|
||||
|
||||
|
||||
|
||||
rts
|
||||
|
||||
|
@ -65,7 +65,7 @@ delay: tax
|
||||
bne -
|
||||
dex
|
||||
bne -
|
||||
|
||||
|
||||
|
||||
|
||||
rts
|
||||
|
||||
|
@ -68,6 +68,6 @@ delay: tax
|
||||
bne -
|
||||
dex
|
||||
bne -
|
||||
|
||||
|
||||
rts
|
||||
|
||||
|
||||
rts
|
||||
|
@ -72,4 +72,4 @@ delay: sta _tmp ; save argument (rdtim destroys it)
|
||||
|
||||
.checkpc $A000
|
||||
.data
|
||||
.checkpc $D000
|
||||
.checkpc $D000
|
||||
|
@ -99,4 +99,4 @@ _done: rts
|
||||
|
||||
.checkpc $A000
|
||||
.data
|
||||
.checkpc $D000
|
||||
.checkpc $D000
|
||||
|
@ -93,4 +93,4 @@ _done: rts
|
||||
.checkpc $D000
|
||||
|
||||
.data zp
|
||||
.checkpc $80
|
||||
.checkpc $80
|
||||
|
@ -6,44 +6,59 @@ import optparse
|
||||
# You may use, modify, and distribute this file under the MIT
|
||||
# license: See README for details.
|
||||
|
||||
enable_collapse = True
|
||||
enable_branch_extend = True
|
||||
enable_undoc_ops = False
|
||||
enable_65c02_exts = False
|
||||
enable_collapse = True
|
||||
enable_branch_extend = True
|
||||
enable_undoc_ops = False
|
||||
enable_65c02_exts = False
|
||||
|
||||
warn_on_branch_extend = True
|
||||
|
||||
print_summary = True
|
||||
print_loaded_files = False
|
||||
print_pass = False
|
||||
print_ir = False
|
||||
print_labels = False
|
||||
print_summary = True
|
||||
print_loaded_files = False
|
||||
print_pass = False
|
||||
print_ir = False
|
||||
print_labels = False
|
||||
|
||||
infile = None
|
||||
outfile = None
|
||||
|
||||
infile = None
|
||||
outfile = None
|
||||
|
||||
def parse_args(raw_args):
|
||||
"Populate the module's globals based on the command-line options given."
|
||||
global enable_collapse, enable_branch_extend, enable_undoc_ops, enable_65c02_exts
|
||||
global enable_collapse, enable_branch_extend
|
||||
global enable_undoc_ops, enable_65c02_exts
|
||||
global warn_on_branch_extend
|
||||
global print_summary, print_loaded_files, print_pass, print_ir, print_labels
|
||||
global print_summary, print_loaded_files
|
||||
global print_pass, print_ir, print_labels
|
||||
global infile, outfile
|
||||
|
||||
parser = optparse.OptionParser(usage= "Usage: %prog [options] srcfile outfile", version="Ophis 6502 cross-assembler, version 2.0")
|
||||
parser = optparse.OptionParser(
|
||||
usage="Usage: %prog [options] srcfile outfile",
|
||||
version="Ophis 6502 cross-assembler, version 2.0")
|
||||
|
||||
ingrp = optparse.OptionGroup(parser, "Input options")
|
||||
ingrp.add_option("-u", "--undoc", action="store_true", default=False, help="Enable 6502 undocumented opcodes")
|
||||
ingrp.add_option("-c", "--65c02", action="store_true", default=False, dest="c02", help="Enable 65c02 extended instruction set")
|
||||
ingrp.add_option("-u", "--undoc", action="store_true", default=False,
|
||||
help="Enable 6502 undocumented opcodes")
|
||||
ingrp.add_option("-c", "--65c02", action="store_true", default=False,
|
||||
dest="c02", help="Enable 65c02 extended instruction set")
|
||||
|
||||
outgrp = optparse.OptionGroup(parser, "Console output options")
|
||||
outgrp.add_option("-v", "--verbose", action="store_const", const=2, help="Verbose mode", default=1)
|
||||
outgrp.add_option("-q", "--quiet", action="store_const", help="Quiet mode", dest="verbose", const=0)
|
||||
outgrp.add_option("-d", "--debug", action="count", dest="verbose", help=optparse.SUPPRESS_HELP)
|
||||
outgrp.add_option("--no-warn", action="store_false", dest="warn", default=True, help="Do not print warnings")
|
||||
outgrp.add_option("-v", "--verbose", action="store_const", const=2,
|
||||
help="Verbose mode", default=1)
|
||||
outgrp.add_option("-q", "--quiet", action="store_const", help="Quiet mode",
|
||||
dest="verbose", const=0)
|
||||
outgrp.add_option("-d", "--debug", action="count", dest="verbose",
|
||||
help=optparse.SUPPRESS_HELP)
|
||||
outgrp.add_option("--no-warn", action="store_false", dest="warn",
|
||||
default=True, help="Do not print warnings")
|
||||
|
||||
bingrp = optparse.OptionGroup(parser, "Binary output options")
|
||||
bingrp.add_option("--no-collapse", action="store_false", dest="enable_collapse", default="True", help="Disable zero-page collapse pass")
|
||||
bingrp.add_option("--no-branch-extend", action="store_false", dest="enable_branch_extend", default="True", help="Disable branch-extension pass")
|
||||
bingrp = optparse.OptionGroup(parser, "Compilation options")
|
||||
bingrp.add_option("--no-collapse", action="store_false",
|
||||
dest="enable_collapse", default="True",
|
||||
help="Disable zero-page collapse pass")
|
||||
bingrp.add_option("--no-branch-extend", action="store_false",
|
||||
dest="enable_branch_extend", default="True",
|
||||
help="Disable branch-extension pass")
|
||||
|
||||
parser.add_option_group(ingrp)
|
||||
parser.add_option_group(outgrp)
|
||||
@ -60,15 +75,15 @@ def parse_args(raw_args):
|
||||
if options.c02 and options.undoc:
|
||||
parser.error("--undoc and --65c02 are mutually exclusive")
|
||||
|
||||
infile = args[0]
|
||||
outfile = args[1]
|
||||
enable_collapse = options.enable_collapse
|
||||
enable_branch_extend = options.enable_branch_extend
|
||||
enable_undoc_ops = options.undoc
|
||||
enable_65c02_exts = options.c02
|
||||
infile = args[0]
|
||||
outfile = args[1]
|
||||
enable_collapse = options.enable_collapse
|
||||
enable_branch_extend = options.enable_branch_extend
|
||||
enable_undoc_ops = options.undoc
|
||||
enable_65c02_exts = options.c02
|
||||
warn_on_branch_extend = options.warn
|
||||
print_summary = options.verbose > 0 # no options set
|
||||
print_loaded_files = options.verbose > 1 # v
|
||||
print_pass = options.verbose > 2 # dd
|
||||
print_ir = options.verbose > 3 # ddd
|
||||
print_labels = options.verbose > 4 # dddd
|
||||
print_summary = options.verbose > 0 # no options set
|
||||
print_loaded_files = options.verbose > 1 # v
|
||||
print_pass = options.verbose > 2 # dd
|
||||
print_ir = options.verbose > 3 # ddd
|
||||
print_labels = options.verbose > 4 # dddd
|
||||
|
@ -10,46 +10,52 @@ import Ophis.IR as IR
|
||||
import Ophis.Frontend as FE
|
||||
import Ophis.Errors as Err
|
||||
|
||||
loadedfiles={}
|
||||
loadedfiles = {}
|
||||
basecharmap = "".join([chr(x) for x in range(256)])
|
||||
currentcharmap = basecharmap
|
||||
|
||||
|
||||
def reset():
|
||||
global loadedfiles, currentcharmap, basecharmap
|
||||
loadedfiles={}
|
||||
loadedfiles = {}
|
||||
currentcharmap = basecharmap
|
||||
|
||||
|
||||
def pragmaInclude(ppt, line, result):
|
||||
"Includes a source file"
|
||||
filename = line.expect("STRING").value
|
||||
line.expect("EOL")
|
||||
if type(filename)==str: result.append(FE.parse_file(ppt, filename))
|
||||
if type(filename) == str:
|
||||
result.append(FE.parse_file(ppt, filename))
|
||||
|
||||
|
||||
def pragmaRequire(ppt, line, result):
|
||||
"Includes a source file at most one time"
|
||||
filename = line.expect("STRING").value
|
||||
line.expect("EOL")
|
||||
if type(filename)==str:
|
||||
if type(filename) == str:
|
||||
global loadedfiles
|
||||
if filename not in loadedfiles:
|
||||
loadedfiles[filename]=1
|
||||
loadedfiles[filename] = True
|
||||
result.append(FE.parse_file(ppt, filename))
|
||||
|
||||
|
||||
def pragmaIncbin(ppt, line, result):
|
||||
"Includes a binary file"
|
||||
filename = line.expect("STRING").value
|
||||
line.expect("EOL")
|
||||
if type(filename)==str:
|
||||
if type(filename) == str:
|
||||
try:
|
||||
f = file(filename, "rb")
|
||||
bytes = f.read()
|
||||
f.close()
|
||||
except IOError:
|
||||
Err.log ("Could not read "+filename)
|
||||
Err.log("Could not read " + filename)
|
||||
return
|
||||
bytes = [IR.ConstantExpr(ord(x)) for x in bytes]
|
||||
result.append(IR.Node(ppt, "Byte", *bytes))
|
||||
|
||||
|
||||
def pragmaCharmap(ppt, line, result):
|
||||
"Modify the character map."
|
||||
global currentcharmap, basecharmap
|
||||
@ -60,30 +66,33 @@ def pragmaCharmap(ppt, line, result):
|
||||
try:
|
||||
base = bytes[0].data
|
||||
newsubstr = "".join([chr(x.data) for x in bytes[1:]])
|
||||
currentcharmap = currentcharmap[:base] + newsubstr + currentcharmap[base+len(newsubstr):]
|
||||
currentcharmap = currentcharmap[:base] + newsubstr + \
|
||||
currentcharmap[base + len(newsubstr):]
|
||||
if len(currentcharmap) != 256 or base < 0 or base > 255:
|
||||
Err.log("Charmap replacement out of range")
|
||||
currentcharmap = currentcharmap[:256]
|
||||
except ValueError:
|
||||
Err.log("Illegal character in .charmap directive")
|
||||
|
||||
|
||||
def pragmaCharmapbin(ppt, line, result):
|
||||
"Load a new character map from a file"
|
||||
global currentcharmap
|
||||
filename = line.expect("STRING").value
|
||||
line.expect("EOL")
|
||||
if type(filename)==str:
|
||||
if type(filename) == str:
|
||||
try:
|
||||
f = file(filename, "rb")
|
||||
bytes = f.read()
|
||||
f.close()
|
||||
except IOError:
|
||||
Err.log ("Could not read "+filename)
|
||||
Err.log("Could not read " + filename)
|
||||
return
|
||||
if len(bytes)==256:
|
||||
if len(bytes) == 256:
|
||||
currentcharmap = bytes
|
||||
else:
|
||||
Err.log("Character map "+filename+" not 256 bytes long")
|
||||
Err.log("Character map " + filename + " not 256 bytes long")
|
||||
|
||||
|
||||
def pragmaOrg(ppt, line, result):
|
||||
"Relocates the PC with no output"
|
||||
@ -91,6 +100,7 @@ def pragmaOrg(ppt, line, result):
|
||||
line.expect("EOL")
|
||||
result.append(IR.Node(ppt, "SetPC", newPC))
|
||||
|
||||
|
||||
def pragmaAdvance(ppt, line, result):
|
||||
"Outputs filler until reaching the target PC"
|
||||
newPC = FE.parse_expr(line)
|
||||
@ -102,25 +112,31 @@ def pragmaAdvance(ppt, line, result):
|
||||
line.expect("EOL")
|
||||
result.append(IR.Node(ppt, "Advance", newPC, fillexpr))
|
||||
|
||||
|
||||
def pragmaCheckpc(ppt, line, result):
|
||||
"Enforces that the PC has not exceeded a certain point"
|
||||
target = FE.parse_expr(line)
|
||||
line.expect("EOL")
|
||||
result.append(IR.Node(ppt, "CheckPC", target))
|
||||
|
||||
|
||||
def pragmaAlias(ppt, line, result):
|
||||
"Assigns an arbitrary label"
|
||||
lbl = line.expect("LABEL").value
|
||||
target = FE.parse_expr(line)
|
||||
result.append(IR.Node(ppt, "Label", lbl, target))
|
||||
|
||||
|
||||
def pragmaSpace(ppt, line, result):
|
||||
"Reserves space in a data segment for a variable"
|
||||
lbl = line.expect("LABEL").value
|
||||
size = line.expect("NUM").value
|
||||
line.expect("EOL")
|
||||
result.append(IR.Node(ppt, "Label", lbl, IR.PCExpr()))
|
||||
result.append(IR.Node(ppt, "SetPC", IR.SequenceExpr([IR.PCExpr(), "+", IR.ConstantExpr(size)])))
|
||||
result.append(IR.Node(ppt, "SetPC",
|
||||
IR.SequenceExpr([IR.PCExpr(), "+",
|
||||
IR.ConstantExpr(size)])))
|
||||
|
||||
|
||||
def pragmaText(ppt, line, result):
|
||||
"Switches to a text segment"
|
||||
@ -132,6 +148,7 @@ def pragmaText(ppt, line, result):
|
||||
segment = "*text-default*"
|
||||
result.append(IR.Node(ppt, "TextSegment", segment))
|
||||
|
||||
|
||||
def pragmaData(ppt, line, result):
|
||||
"Switches to a data segment (no output allowed)"
|
||||
next = line.expect("LABEL", "EOL")
|
||||
@ -142,67 +159,80 @@ def pragmaData(ppt, line, result):
|
||||
segment = "*data-default*"
|
||||
result.append(IR.Node(ppt, "DataSegment", segment))
|
||||
|
||||
|
||||
def readData(line):
|
||||
"Read raw data from a comma-separated list"
|
||||
if line.lookahead(0).type == "STRING":
|
||||
data = [IR.ConstantExpr(ord(x)) for x in line.expect("STRING").value.translate(currentcharmap)]
|
||||
data = [IR.ConstantExpr(ord(x))
|
||||
for x in line.expect("STRING").value.translate(currentcharmap)]
|
||||
else:
|
||||
data = [FE.parse_expr(line)]
|
||||
next = line.expect(',', 'EOL').type
|
||||
while next == ',':
|
||||
if line.lookahead(0).type == "STRING":
|
||||
data.extend([IR.ConstantExpr(ord(x)) for x in line.expect("STRING").value])
|
||||
data.extend([IR.ConstantExpr(ord(x))
|
||||
for x in line.expect("STRING").value])
|
||||
else:
|
||||
data.append(FE.parse_expr(line))
|
||||
next = line.expect(',', 'EOL').type
|
||||
return data
|
||||
|
||||
|
||||
def pragmaByte(ppt, line, result):
|
||||
"Raw data, a byte at a time"
|
||||
bytes = readData(line)
|
||||
result.append(IR.Node(ppt, "Byte", *bytes))
|
||||
|
||||
|
||||
def pragmaWord(ppt, line, result):
|
||||
"Raw data, a word at a time, little-endian"
|
||||
words = readData(line)
|
||||
result.append(IR.Node(ppt, "Word", *words))
|
||||
|
||||
|
||||
def pragmaDword(ppt, line, result):
|
||||
"Raw data, a double-word at a time, little-endian"
|
||||
dwords = readData(line)
|
||||
result.append(IR.Node(ppt, "Dword", *dwords))
|
||||
|
||||
|
||||
def pragmaWordbe(ppt, line, result):
|
||||
"Raw data, a word at a time, big-endian"
|
||||
words = readData(line)
|
||||
result.append(IR.Node(ppt, "WordBE", *words))
|
||||
|
||||
|
||||
def pragmaDwordbe(ppt, line, result):
|
||||
"Raw data, a dword at a time, big-endian"
|
||||
dwords = readData(line)
|
||||
result.append(IR.Node(ppt, "DwordBE", *dwords))
|
||||
|
||||
|
||||
def pragmaScope(ppt, line, result):
|
||||
"Create a new lexical scoping block"
|
||||
line.expect("EOL")
|
||||
result.append(IR.Node(ppt, "ScopeBegin"))
|
||||
|
||||
|
||||
def pragmaScend(ppt, line, result):
|
||||
"End the innermost lexical scoping block"
|
||||
line.expect("EOL")
|
||||
result.append(IR.Node(ppt, "ScopeEnd"))
|
||||
|
||||
|
||||
def pragmaMacro(ppt, line, result):
|
||||
"Begin a macro definition"
|
||||
lbl = line.expect("LABEL").value
|
||||
line.expect("EOL")
|
||||
result.append(IR.Node(ppt, "MacroBegin", lbl))
|
||||
|
||||
|
||||
def pragmaMacend(ppt, line, result):
|
||||
"End a macro definition"
|
||||
line.expect("EOL")
|
||||
result.append(IR.Node(ppt, "MacroEnd"))
|
||||
|
||||
|
||||
def pragmaInvoke(ppt, line, result):
|
||||
macro = line.expect("LABEL").value
|
||||
if line.lookahead(0).type == "EOL":
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
import Ophis.Errors as Err
|
||||
|
||||
|
||||
class Environment(object):
|
||||
"""Environment class.
|
||||
Controls the various scopes and global abstract execution variables."""
|
||||
@ -19,39 +20,52 @@ class Environment(object):
|
||||
self.segmentdict = {}
|
||||
self.segment = "*text-default*"
|
||||
self.scopecount = 0
|
||||
|
||||
def __contains__(self, item):
|
||||
if item[0] == '_':
|
||||
for dict in [self.dicts[i] for i in self.stack]:
|
||||
if item in dict: return 1
|
||||
return 0
|
||||
if item in dict:
|
||||
return True
|
||||
return False
|
||||
return item in self.dicts[0]
|
||||
|
||||
def __getitem__(self, item):
|
||||
if item[0] == '_':
|
||||
for dict in [self.dicts[i] for i in self.stack]:
|
||||
if item in dict: return dict[item]
|
||||
if item in dict:
|
||||
return dict[item]
|
||||
else:
|
||||
if item in self.dicts[0]: return self.dicts[0][item]
|
||||
if item in self.dicts[0]:
|
||||
return self.dicts[0][item]
|
||||
Err.log("Unknown label '%s'" % item)
|
||||
return 0
|
||||
|
||||
def __setitem__(self, item, value):
|
||||
if item[0] == '_':
|
||||
self.dicts[self.stack[0]][item] = value
|
||||
else:
|
||||
self.dicts[0][item] = value
|
||||
|
||||
def __str__(self):
|
||||
return str(self.dicts)
|
||||
|
||||
def getPC(self):
|
||||
return self.pc
|
||||
|
||||
def setPC(self, value):
|
||||
self.pc = value
|
||||
|
||||
def incPC(self, amount):
|
||||
self.pc += amount
|
||||
|
||||
def getsegment(self):
|
||||
return self.segment
|
||||
|
||||
def setsegment(self, segment):
|
||||
self.segmentdict[self.segment] = self.pc
|
||||
self.segment = segment
|
||||
self.pc = self.segmentdict.get(segment, 0)
|
||||
|
||||
def reset(self):
|
||||
"Clears out program counter, segment, and scoping information"
|
||||
self.pc = 0
|
||||
@ -61,14 +75,16 @@ class Environment(object):
|
||||
if len(self.stack) > 1:
|
||||
Err.log("Unmatched .scope")
|
||||
self.stack = [0]
|
||||
|
||||
def newscope(self):
|
||||
"Enters a new scope for temporary labels."
|
||||
self.scopecount += 1
|
||||
self.stack.insert(0, self.scopecount)
|
||||
if len(self.dicts) <= self.scopecount: self.dicts.append({})
|
||||
if len(self.dicts) <= self.scopecount:
|
||||
self.dicts.append({})
|
||||
|
||||
def endscope(self):
|
||||
"Leaves a scope."
|
||||
if len(self.stack) == 1:
|
||||
Err.log("Unmatched .scend")
|
||||
self.stack.pop(0)
|
||||
|
||||
|
@ -12,15 +12,20 @@ import sys
|
||||
count = 0
|
||||
currentpoint = "<Top Level>"
|
||||
|
||||
|
||||
def log(err):
|
||||
"""Reports an error at the current program point, and increases
|
||||
the global error count."""
|
||||
global count
|
||||
count = count+1
|
||||
print>>sys.stderr, currentpoint+": "+err
|
||||
count = count + 1
|
||||
print>>sys.stderr, currentpoint + ": " + err
|
||||
|
||||
|
||||
def report():
|
||||
"Print out the number of errors."
|
||||
if count == 0: print>>sys.stderr, "No errors"
|
||||
elif count == 1: print>>sys.stderr, "1 error"
|
||||
else: print>>sys.stderr, str(count)+" errors"
|
||||
if count == 0:
|
||||
print>>sys.stderr, "No errors"
|
||||
elif count == 1:
|
||||
print>>sys.stderr, "1 error"
|
||||
else:
|
||||
print>>sys.stderr, str(count) + " errors"
|
||||
|
@ -13,35 +13,44 @@ import os
|
||||
# You may use, modify, and distribute this file under the MIT
|
||||
# license: See README for details.
|
||||
|
||||
|
||||
class Lexeme(object):
|
||||
"Class for lexer tokens. Used by lexer and parser."
|
||||
def __init__(self, type="UNKNOWN", value=None):
|
||||
self.type = type.upper()
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
if self.value == None:
|
||||
if self.value is None:
|
||||
return self.type
|
||||
else:
|
||||
return self.type+":"+str(self.value)
|
||||
return self.type + ":" + str(self.value)
|
||||
|
||||
def __repr__(self):
|
||||
return "Lexeme("+`self.type`+", "+`self.value`+")"
|
||||
return "Lexeme(" + repr(self.type) + ", " + repr(self.value) + ")"
|
||||
|
||||
def matches(self, other):
|
||||
"1 if Lexemes a and b have the same type."
|
||||
return self.type == other.type
|
||||
|
||||
bases = {"$":("hexadecimal", 16),
|
||||
"%":("binary", 2),
|
||||
"0":("octal", 8)}
|
||||
|
||||
bases = {"$": ("hexadecimal", 16),
|
||||
"%": ("binary", 2),
|
||||
"0": ("octal", 8)}
|
||||
|
||||
|
||||
punctuation = "#,`<>():.+-*/&|^[]"
|
||||
|
||||
|
||||
def lex(point, line):
|
||||
"""Turns a line of source into a sequence of lexemes."""
|
||||
Err.currentpoint = point
|
||||
result = []
|
||||
|
||||
def is_opcode(op):
|
||||
"Tests whether a string is an opcode or an identifier"
|
||||
return op in Ops.opcodes
|
||||
|
||||
def add_token(token):
|
||||
"Converts a substring into a single lexeme"
|
||||
if token == "":
|
||||
@ -59,7 +68,8 @@ def lex(point, line):
|
||||
result.append(Lexeme("NUM", long(rest, bases[firstchar][1])))
|
||||
return
|
||||
except ValueError:
|
||||
Err.log('Invalid '+bases[firstchar][0]+' constant: '+rest)
|
||||
Err.log('Invalid ' + bases[firstchar][0] + ' constant: ' +
|
||||
rest)
|
||||
result.append(Lexeme("NUM", 0))
|
||||
return
|
||||
elif firstchar.isdigit():
|
||||
@ -73,12 +83,12 @@ def lex(point, line):
|
||||
if len(rest) == 1:
|
||||
result.append(Lexeme("NUM", ord(rest)))
|
||||
else:
|
||||
Err.log("Invalid character constant '"+rest+"'")
|
||||
Err.log("Invalid character constant '" + rest + "'")
|
||||
result.append(Lexeme("NUM", 0))
|
||||
return
|
||||
elif firstchar in punctuation:
|
||||
if rest != "":
|
||||
Err.log("Internal lexer error! '"+token+"' can't happen!")
|
||||
Err.log("Internal lexer error! '" + token + "' can't happen!")
|
||||
result.append(Lexeme(firstchar))
|
||||
return
|
||||
else: # Label, opcode, or index register
|
||||
@ -94,22 +104,24 @@ def lex(point, line):
|
||||
return
|
||||
# should never reach here
|
||||
Err.log("Internal lexer error: add_token fall-through")
|
||||
|
||||
def add_EOL():
|
||||
"Adds an end-of-line lexeme"
|
||||
result.append(Lexeme("EOL"))
|
||||
|
||||
# Actual routine begins here
|
||||
value = ""
|
||||
quotemode = 0
|
||||
backslashmode = 0
|
||||
quotemode = False
|
||||
backslashmode = False
|
||||
for c in line.strip():
|
||||
if backslashmode:
|
||||
backslashmode = 0
|
||||
backslashmode = False
|
||||
value = value + c
|
||||
elif c == "\\":
|
||||
backslashmode = 1
|
||||
backslashmode = True
|
||||
elif quotemode:
|
||||
if c == '"':
|
||||
quotemode = 0
|
||||
quotemode = False
|
||||
else:
|
||||
value = value + c
|
||||
elif c == ';':
|
||||
@ -126,7 +138,7 @@ def lex(point, line):
|
||||
elif c == '"':
|
||||
add_token(value)
|
||||
value = '"'
|
||||
quotemode = 1
|
||||
quotemode = True
|
||||
else:
|
||||
value = value + c
|
||||
if backslashmode:
|
||||
@ -137,35 +149,45 @@ def lex(point, line):
|
||||
add_EOL()
|
||||
return result
|
||||
|
||||
|
||||
class ParseLine(object):
|
||||
"Maintains the parse state of a line of code. Enables arbitrary lookahead."
|
||||
"Maintains the parse state of a line of code. Enables arbitrary lookahead."
|
||||
|
||||
def __init__(self, lexemes):
|
||||
self.lexemes = lexemes
|
||||
self.location = 0
|
||||
|
||||
def lookahead(self, i):
|
||||
"""Returns the token i units ahead in the parse.
|
||||
lookahead(0) returns the next token; trying to read off the end of
|
||||
"""Returns the token i units ahead in the parse.
|
||||
lookahead(0) returns the next token; trying to read off the end of
|
||||
the sequence returns the last token in the sequence (usually EOL)."""
|
||||
target = self.location+i
|
||||
if target >= len(self.lexemes): target = -1
|
||||
target = self.location + i
|
||||
if target >= len(self.lexemes):
|
||||
target = -1
|
||||
return self.lexemes[target]
|
||||
|
||||
def pop(self):
|
||||
"Returns and removes the next element in the line."
|
||||
old = self.location
|
||||
if self.location < len(self.lexemes)-1: self.location += 1
|
||||
if self.location < len(self.lexemes) - 1:
|
||||
self.location += 1
|
||||
return self.lexemes[old]
|
||||
|
||||
def expect(self, *tokens):
|
||||
"""Reads a token from the ParseLine line and returns it if it's of a type
|
||||
in the sequence tokens. Otherwise, it logs an error."""
|
||||
"""Reads a token from the ParseLine line and returns it if it's of a
|
||||
type in the sequence tokens. Otherwise, it logs an error."""
|
||||
token = self.pop()
|
||||
if token.type not in tokens:
|
||||
Err.log('Expected: "'+'", "'.join(tokens)+'"')
|
||||
if token.type not in tokens:
|
||||
Err.log('Expected: "' + '", "'.join(tokens) + '"')
|
||||
return token
|
||||
|
||||
|
||||
pragma_modules = []
|
||||
|
||||
|
||||
def parse_expr(line):
|
||||
"Parses an Ophis arithmetic expression."
|
||||
|
||||
def atom():
|
||||
"Parses lowest-priority expression components."
|
||||
next = line.lookahead(0).type
|
||||
@ -187,14 +209,14 @@ def parse_expr(line):
|
||||
offset += 1
|
||||
line.expect("+")
|
||||
next = line.lookahead(0).type
|
||||
return IR.LabelExpr("*"+str(templabelcount+offset))
|
||||
return IR.LabelExpr("*" + str(templabelcount + offset))
|
||||
elif next == "-":
|
||||
offset = 1
|
||||
while next == "-":
|
||||
offset -= 1
|
||||
line.expect("-")
|
||||
next = line.lookahead(0).type
|
||||
return IR.LabelExpr("*"+str(templabelcount+offset))
|
||||
return IR.LabelExpr("*" + str(templabelcount + offset))
|
||||
elif next == ">":
|
||||
line.expect(">")
|
||||
return IR.HighByteExpr(atom())
|
||||
@ -203,6 +225,7 @@ def parse_expr(line):
|
||||
return IR.LowByteExpr(atom())
|
||||
else:
|
||||
Err.log('Expected: expression')
|
||||
|
||||
def precedence_read(constructor, reader, separators):
|
||||
"""Handles precedence. The reader argument is a function that returns
|
||||
expressions that bind more tightly than these; separators is a list
|
||||
@ -218,51 +241,60 @@ def parse_expr(line):
|
||||
result.append(nextop)
|
||||
result.append(reader())
|
||||
nextop = line.lookahead(0).type
|
||||
if len(result) == 1: return result[0]
|
||||
if len(result) == 1:
|
||||
return result[0]
|
||||
return constructor(result)
|
||||
|
||||
def term():
|
||||
"Parses * and /"
|
||||
return precedence_read(IR.SequenceExpr, atom, ["*", "/"])
|
||||
|
||||
def arith():
|
||||
"Parses + and -"
|
||||
return precedence_read(IR.SequenceExpr, term, ["+", "-"])
|
||||
|
||||
def bits():
|
||||
"Parses &, |, and ^"
|
||||
return precedence_read(IR.SequenceExpr, arith, ["&", "|", "^"])
|
||||
|
||||
return bits()
|
||||
|
||||
|
||||
def parse_line(ppt, lexemelist):
|
||||
"Turn a line of source into an IR Node."
|
||||
Err.currentpoint = ppt
|
||||
result = []
|
||||
line = ParseLine(lexemelist)
|
||||
|
||||
def aux():
|
||||
"Accumulates all IR nodes defined by this line."
|
||||
if line.lookahead(0).type == "EOL":
|
||||
pass
|
||||
elif line.lookahead(1).type == ":":
|
||||
newlabel=line.expect("LABEL").value
|
||||
newlabel = line.expect("LABEL").value
|
||||
line.expect(":")
|
||||
result.append(IR.Node(ppt, "Label", newlabel, IR.PCExpr()))
|
||||
aux()
|
||||
elif line.lookahead(0).type == "*":
|
||||
global templabelcount
|
||||
templabelcount = templabelcount + 1
|
||||
result.append(IR.Node(ppt, "Label", "*"+str(templabelcount), IR.PCExpr()))
|
||||
result.append(IR.Node(ppt, "Label", "*" + str(templabelcount),
|
||||
IR.PCExpr()))
|
||||
line.expect("*")
|
||||
aux()
|
||||
elif line.lookahead(0).type == "." or line.lookahead(0).type == "`":
|
||||
which = line.expect(".", "`").type
|
||||
if (which == "."): pragma = line.expect("LABEL").value
|
||||
else: pragma = "invoke"
|
||||
pragmaFunction = "pragma"+pragma.title()
|
||||
if (which == "."):
|
||||
pragma = line.expect("LABEL").value
|
||||
else:
|
||||
pragma = "invoke"
|
||||
pragmaFunction = "pragma" + pragma.title()
|
||||
for mod in pragma_modules:
|
||||
if hasattr(mod, pragmaFunction):
|
||||
getattr(mod, pragmaFunction)(ppt, line, result)
|
||||
break
|
||||
else:
|
||||
Err.log("Unknown pragma "+pragma)
|
||||
|
||||
Err.log("Unknown pragma " + pragma)
|
||||
else: # Instruction
|
||||
opcode = line.expect("OPCODE").value
|
||||
if line.lookahead(0).type == "#":
|
||||
@ -296,23 +328,30 @@ def parse_line(ppt, lexemelist):
|
||||
tok = line.expect("EOL", ",").type
|
||||
if tok == ",":
|
||||
tok = line.expect("X", "Y").type
|
||||
if tok == "X": mode = "MemoryX"
|
||||
else: mode = "MemoryY"
|
||||
if tok == "X":
|
||||
mode = "MemoryX"
|
||||
else:
|
||||
mode = "MemoryY"
|
||||
line.expect("EOL")
|
||||
else: mode = "Memory"
|
||||
else:
|
||||
mode = "Memory"
|
||||
result.append(IR.Node(ppt, mode, opcode, arg))
|
||||
|
||||
aux()
|
||||
result = [node for node in result if node is not IR.NullNode]
|
||||
if len(result) == 0: return IR.NullNode
|
||||
if len(result) == 1: return result[0]
|
||||
if len(result) == 0:
|
||||
return IR.NullNode
|
||||
if len(result) == 1:
|
||||
return result[0]
|
||||
return IR.SequenceNode(ppt, result)
|
||||
|
||||
|
||||
def parse_file(ppt, filename):
|
||||
"Loads an Ophis source file, and returns an IR list."
|
||||
Err.currentpoint = ppt
|
||||
if Cmd.print_loaded_files:
|
||||
if filename != '-':
|
||||
print>>sys.stderr, "Loading "+filename
|
||||
print>>sys.stderr, "Loading " + filename
|
||||
else:
|
||||
print>>sys.stderr, "Loading from standard input"
|
||||
try:
|
||||
@ -322,18 +361,19 @@ def parse_file(ppt, filename):
|
||||
f.close()
|
||||
else:
|
||||
linelist = sys.stdin.readlines()
|
||||
pptlist = ["%s:%d" % (filename, i+1) for i in range(len(linelist))]
|
||||
pptlist = ["%s:%d" % (filename, i + 1) for i in range(len(linelist))]
|
||||
lexlist = map(lex, pptlist, linelist)
|
||||
IRlist = map(parse_line, pptlist, lexlist)
|
||||
IRlist = [node for node in IRlist if node is not IR.NullNode]
|
||||
return IR.SequenceNode(ppt, IRlist)
|
||||
except IOError:
|
||||
Err.log ("Could not read "+filename)
|
||||
Err.log("Could not read " + filename)
|
||||
return IR.NullNode
|
||||
|
||||
|
||||
def parse(filename):
|
||||
"Top level parsing routine, taking a source file name and returning an IR list."
|
||||
"""Top level parsing routine, taking a source file name
|
||||
and returning an IR list."""
|
||||
global templabelcount
|
||||
templabelcount = 0
|
||||
return parse_file("<Top Level>", filename)
|
||||
|
||||
|
110
src/Ophis/IR.py
110
src/Ophis/IR.py
@ -9,6 +9,7 @@
|
||||
|
||||
import Ophis.Errors as Err
|
||||
|
||||
|
||||
class Node(object):
|
||||
"""The default IR Node
|
||||
Instances of Node always have the three fields ppt(Program Point),
|
||||
@ -17,27 +18,35 @@ class Node(object):
|
||||
self.ppt = ppt
|
||||
self.nodetype = nodetype
|
||||
self.data = list(data)
|
||||
|
||||
def accept(self, asmpass, env=None):
|
||||
"""Implements the Visitor pattern for an assembler pass.
|
||||
Calls the routine 'asmpass.visitTYPE(self, env)' where
|
||||
Calls the routine 'asmpass.visitTYPE(self, env)' where
|
||||
TYPE is the value of self.nodetype."""
|
||||
Err.currentpoint = self.ppt
|
||||
routine = getattr(asmpass, "visit"+self.nodetype, asmpass.visitUnknown)
|
||||
routine = getattr(asmpass, "visit" + self.nodetype,
|
||||
asmpass.visitUnknown)
|
||||
routine(self, env)
|
||||
|
||||
def __str__(self):
|
||||
if self.nodetype != "SEQUENCE":
|
||||
return str(self.ppt)+": "+self.nodetype+" - "+" ".join(map(str, self.data))
|
||||
return str(self.ppt) + ": " + self.nodetype + " - " + \
|
||||
" ".join(map(str, self.data))
|
||||
else:
|
||||
return "\n".join(map(str, self.data))
|
||||
|
||||
def __repr__(self):
|
||||
args = [self.ppt, self.nodetype] + self.data
|
||||
return "Node(" + ", ".join(map(repr, args)) + ")"
|
||||
|
||||
|
||||
NullNode = Node("<none>", "None")
|
||||
|
||||
|
||||
def SequenceNode(ppt, nodelist):
|
||||
return Node(ppt, "SEQUENCE", *nodelist)
|
||||
|
||||
|
||||
class Expr(object):
|
||||
"""Base class for Ophis expressions
|
||||
All expressions have a field called "data" and a boolean field
|
||||
@ -45,78 +54,102 @@ class Expr(object):
|
||||
symbolic values in it."""
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.hardcoded = 0
|
||||
self.hardcoded = False
|
||||
|
||||
def __str__(self):
|
||||
return "<UNKNOWN: "+`self.data`+">"
|
||||
def valid(self, env=None, PCvalid=0):
|
||||
return "<UNKNOWN: " + repr(self.data) + ">"
|
||||
|
||||
def valid(self, env=None, PCvalid=False):
|
||||
"""Returns true if the the expression can be successfully
|
||||
evaluated in the specified environment."""
|
||||
return 0
|
||||
return False
|
||||
|
||||
def value(self, env=None):
|
||||
"Evaluates this expression in the given environment."
|
||||
return None
|
||||
|
||||
|
||||
class ConstantExpr(Expr):
|
||||
"Represents a numeric constant"
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.hardcoded = 1
|
||||
self.hardcoded = True
|
||||
|
||||
def __str__(self):
|
||||
return str(self.data)
|
||||
def valid(self, env=None, PCvalid=0):
|
||||
return 1
|
||||
|
||||
def valid(self, env=None, PCvalid=False):
|
||||
return True
|
||||
|
||||
def value(self, env=None):
|
||||
return self.data
|
||||
|
||||
|
||||
class LabelExpr(Expr):
|
||||
"Represents a symbolic constant"
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.hardcoded = 0
|
||||
self.hardcoded = False
|
||||
|
||||
def __str__(self):
|
||||
return self.data
|
||||
def valid(self, env=None, PCvalid=0):
|
||||
|
||||
def valid(self, env=None, PCvalid=False):
|
||||
return (env is not None) and self.data in env
|
||||
|
||||
def value(self, env=None):
|
||||
return env[self.data]
|
||||
|
||||
|
||||
class PCExpr(Expr):
|
||||
"Represents the current program counter: ^"
|
||||
def __init__(self):
|
||||
self.hardcoded = 0
|
||||
self.hardcoded = False
|
||||
|
||||
def __str__(self):
|
||||
return "^"
|
||||
def valid(self, env=None, PCvalid=0):
|
||||
|
||||
def valid(self, env=None, PCvalid=False):
|
||||
return env is not None and PCvalid
|
||||
|
||||
def value(self, env=None):
|
||||
return env.getPC()
|
||||
|
||||
|
||||
class HighByteExpr(Expr):
|
||||
"Represents the expression >{data}"
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.hardcoded = data.hardcoded
|
||||
|
||||
def __str__(self):
|
||||
return ">"+str(self.data)
|
||||
def valid(self, env=None, PCvalid=0):
|
||||
return ">" + str(self.data)
|
||||
|
||||
def valid(self, env=None, PCvalid=False):
|
||||
return self.data.valid(env, PCvalid)
|
||||
|
||||
def value(self, env=None):
|
||||
val = self.data.value(env)
|
||||
return (val >> 8) & 0xff
|
||||
|
||||
|
||||
|
||||
class LowByteExpr(Expr):
|
||||
"Represents the expression <{data}"
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.hardcoded = data.hardcoded
|
||||
|
||||
def __str__(self):
|
||||
return "<"+str(self.data)
|
||||
def valid(self, env=None, PCvalid=0):
|
||||
return "<" + str(self.data)
|
||||
|
||||
def valid(self, env=None, PCvalid=False):
|
||||
return self.data.valid(env, PCvalid)
|
||||
|
||||
def value(self, env=None):
|
||||
val = self.data.value(env)
|
||||
return val & 0xff
|
||||
|
||||
|
||||
class SequenceExpr(Expr):
|
||||
"""Represents an interleaving of operands (of type Expr) and
|
||||
operators (of type String). Subclasses must provide a routine
|
||||
@ -128,20 +161,23 @@ class SequenceExpr(Expr):
|
||||
[Expr, str, Expr, str, Expr, str, ... Expr, str, Expr]."""
|
||||
self.data = data
|
||||
self.operands = [x for x in data if isinstance(x, Expr)]
|
||||
self.operators = [x for x in data if type(x)==str]
|
||||
self.operators = [x for x in data if type(x) == str]
|
||||
for i in self.operands:
|
||||
if not i.hardcoded:
|
||||
self.hardcoded = 0
|
||||
self.hardcoded = False
|
||||
break
|
||||
else:
|
||||
self.hardcoded = 1
|
||||
self.hardcoded = True
|
||||
|
||||
def __str__(self):
|
||||
return "["+" ".join(map(str, self.data))+"]"
|
||||
def valid(self, env=None, PCvalid=0):
|
||||
return "[" + " ".join(map(str, self.data)) + "]"
|
||||
|
||||
def valid(self, env=None, PCvalid=False):
|
||||
for i in self.operands:
|
||||
if not i.valid(env, PCvalid):
|
||||
return 0
|
||||
return 1
|
||||
return False
|
||||
return True
|
||||
|
||||
def value(self, env=None):
|
||||
subs = map((lambda x: x.value(env)), self.operands)
|
||||
result = subs[0]
|
||||
@ -150,11 +186,19 @@ class SequenceExpr(Expr):
|
||||
result = self.operate(result, op, subs[index])
|
||||
index += 1
|
||||
return result
|
||||
|
||||
def operate(self, start, op, other):
|
||||
if op=="*": return start * other
|
||||
if op=="/": return start // other
|
||||
if op=="+": return start + other
|
||||
if op=="-": return start - other
|
||||
if op=="&": return start & other
|
||||
if op=="|": return start | other
|
||||
if op=="^": return start ^ other
|
||||
if op == "*":
|
||||
return start * other
|
||||
if op == "/":
|
||||
return start // other
|
||||
if op == "+":
|
||||
return start + other
|
||||
if op == "-":
|
||||
return start - other
|
||||
if op == "&":
|
||||
return start & other
|
||||
if op == "|":
|
||||
return start | other
|
||||
if op == "^":
|
||||
return start ^ other
|
||||
|
@ -18,6 +18,7 @@ macros = {}
|
||||
currentname = None
|
||||
currentbody = None
|
||||
|
||||
|
||||
def newMacro(name):
|
||||
"Start creating a new macro with the specified name."
|
||||
global currentname
|
||||
@ -31,10 +32,12 @@ def newMacro(name):
|
||||
currentname = name
|
||||
currentbody = []
|
||||
|
||||
|
||||
def registerNode(node):
|
||||
global currentbody
|
||||
currentbody.append(IR.Node(node.ppt, node.nodetype, *node.data))
|
||||
|
||||
|
||||
def endMacro():
|
||||
global currentname
|
||||
global currentbody
|
||||
@ -46,21 +49,29 @@ def endMacro():
|
||||
currentname = None
|
||||
currentbody = None
|
||||
|
||||
|
||||
def expandMacro(ppt, name, arglist):
|
||||
global macros
|
||||
if name not in macros:
|
||||
Err.log("Undefined macro '%s'" % name)
|
||||
return IR.NullNode
|
||||
argexprs = [IR.Node(ppt, "Label", "_*%d" % i, arg) for (i, arg) in zip(xrange(1, sys.maxint), arglist)]
|
||||
bindexprs = [IR.Node(ppt, "Label", "_%d" % i, IR.LabelExpr("_*%d" % i)) for i in range(1, len(arglist)+1)]
|
||||
body = [IR.Node("%s->%s" % (ppt, node.ppt), node.nodetype, *node.data) for node in macros[name]]
|
||||
invocation = [IR.Node(ppt, "ScopeBegin")] + argexprs + [IR.Node(ppt, "ScopeBegin")] + bindexprs + body + [IR.Node(ppt, "ScopeEnd"), IR.Node(ppt, "ScopeEnd")]
|
||||
argexprs = [IR.Node(ppt, "Label", "_*%d" % i, arg)
|
||||
for (i, arg) in zip(xrange(1, sys.maxint), arglist)]
|
||||
bindexprs = [IR.Node(ppt, "Label", "_%d" % i, IR.LabelExpr("_*%d" % i))
|
||||
for i in range(1, len(arglist) + 1)]
|
||||
body = [IR.Node("%s->%s" % (ppt, node.ppt), node.nodetype, *node.data)
|
||||
for node in macros[name]]
|
||||
invocation = [IR.Node(ppt, "ScopeBegin")] + argexprs + \
|
||||
[IR.Node(ppt, "ScopeBegin")] + bindexprs + body + \
|
||||
[IR.Node(ppt, "ScopeEnd"), IR.Node(ppt, "ScopeEnd")]
|
||||
return IR.SequenceNode(ppt, invocation)
|
||||
|
||||
|
||||
def dump():
|
||||
global macros
|
||||
for mac in macros:
|
||||
body = macros[mac]
|
||||
print>>sys.stderr, "Macro: "+mac
|
||||
for node in body: print>>sys.stderr, node
|
||||
print>>sys.stderr, "Macro: " + mac
|
||||