#!/usr/pkg/bin/python """bootstrap compiler for postFORTH version 1 purpose of this is to keep things extremely simple when bootstrapping; I was doing this with the RCS file but each time I change machines I lose that trail""" Copyright = """ pfcompile -- bootstrap compiler for postFORTH version 1 Copyright (C) 2005 John Comeau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. """ errormessage = "Not all needed libraries found, upgrade or check path" try: True # not defined in older Python releases except: True, False = 1, 0 try: import sys, os, types, re, pwd sys.path.append(os.sep.join([pwd.getpwuid(os.geteuid())[5], 'lib', 'python'])) from com.jcomeau import gpl, jclicense except: try: sys.stderr.write("%s\n" % errormessage) except: print errormessage raise # get name this program was called as self = sys.argv[0].split(os.sep)[-1] command = self.split('.')[0] # chop any suffix (extension) # now get name we gave it when we wrote it originalself = Copyright.split()[0] # globals and routines that should be in every program # (yes, you could import them, but there are problems in that approach too) def DebugPrint(*whatever): return False # defined instead by pytest module, use that for debugging def noTuples(*args): "reduces args to lists or scalars, helps with command-line testing" while type(args[0]) == types.TupleType: args = args[0] return args def scalars(*args): "reduces args to scalars, helps with command-line testing" args = noTuples(args) while type(args[0]) == types.ListType: args = args[0] return args # other globals, specific to this program from com.jcomeau.arcane import countdict import struct baseAddress = 0x401000 # for Windows NT only postforth = { '/*': 'multilineComment', '2#': 'pushnumber', '10#': 'pushnumber', '16#': 'pushnumber', ',': 'comma', "'": 'tick', 'LIT,': 'litcomma', 'CREATE': 'create', ':': 'colon', ';': 'semicolon', } def comma(*args): "comma is the FORTH single-word compiler" args = scalars(args) compiler = args[0] compiler.compile(compiler.pop()) def litcomma(*args): "compile a FORTH LIT followed by the compiled TOS (top-of-stack)" args = scalars(args) compiler = args[0] compiler.compile('LIT') comma(compiler) def multilineComment(*args): "skip past next */" DebugPrint('processing multiline comment') args = scalars(args) compiler = args[0] skipped = '' while True: token = compiler.getnext() if token == None or token.endswith('*/'): break skipped += token + ' ' DebugPrint('skipped', skipped) def pushnumber(*args): args = scalars(args) compiler = args[0] DebugPrint('pushing', '%s%s' % (compiler.getlast(), compiler.getcurrent())) compiler.push(baseconvert(compiler.getlast(), compiler.getnext())) def baseconvert(*args): args = scalars(args) base, integer = args base = int(base.rstrip('#')) return int(integer, base) def tick(*args): args = scalars(args) compiler = args[0] compiler.push(0) DebugPrint('tick: faked pushing address of', compiler.getnext()) def create(*args): args = scalars(args) compiler = args[0] word = compiler.getnext() compiler.addr[word] = compiler.here() compiler.compile(struct.pack('B %ds B' % len(word), len(word), word, len(word))) def colon(*args): create(args) args = scalars(args) compiler = args[0] compiler.compile(0) # fake compiling DOCOL def semicolon(*args): args = scalars(args) compiler = args[0] compiler.compile(0) # fake compiling ;S class compiler: pointer = 0 list = [] stack = [] compiled = [] addr = dict() def __init__(self, *args): args = scalars(args) sourcefile = args[0] self.list = open(sourcefile).read().split() DebugPrint('list', self.list) def dump(self): DebugPrint('pointer', self.pointer, 'list', self.list, 'stack', self.stack, 'compiled', self.compiled, 'addr', self.addr) def getnext(self): "return next token or None if end of list, and advance pointer" if self.pointer == len(self.list): DebugPrint('getnext: end of tokens') return None else: next = self.list[self.pointer] self.pointer += 1 return next def getlast(self): "last token processed" return self.list[self.pointer - 1] def getcurrent(self): "next available token, will throw exception if called at end of list" return self.list[self.pointer] def push(self, *args): args = scalars(args) self.stack.extend(list(args)) def pop(self): DebugPrint('popping', self.stack[-1]) return self.stack.pop() def compile(self, *args): args = noTuples(args) for arg in args: DebugPrint('compiling', arg) self.compiled.append(arg) def here(self): return baseAddress + len(self.compiled) def pfcompile(*args): """compile postFORTH tokens from program(s) passed in on command line using "inside knowledge" of postFORTH, find tokens and output as a linked binary """ DebugPrint("'pytest %s.%s' should show this; args: " % ( self, originalself), args) args = scalars(args) tokenmap = dict(postforth) tokens = countdict() for sourcefile in args: pfcompiler = compiler(sourcefile) while True: token = pfcompiler.getnext() if token == None: break tokens[token] += 1 if token in tokenmap.keys(): DebugPrint('processing token:', token) eval(tokenmap[token])(pfcompiler) else: DebugPrint('unrecognized token', token) pfcompiler.dump(); DebugPrint('tokens', tokens) if __name__ == '__main__': # if this program was imported by another, the above test will fail, # and this following code won't be used... pfcompile(sys.argv[1:]) else: # if you want something to be done on import, do it here; otherwise pass pass