support for including files

This commit is contained in:
Kamil Zbrog 2017-03-28 22:56:03 +02:00 committed by Kamil Zbrog
parent 03f471ba7e
commit b273e25a17

56
foco65
View File

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python2
# foco65 # foco65
# Copyright (C) 2014 Piotr Wiszowaty # Copyright (C) 2014 Piotr Wiszowaty
@ -23,42 +23,45 @@ import sys
##### #####
class StackUnderflow(Exception): class StackUnderflow(Exception):
def __init__(self, text, line, column): def __init__(self, text, filename, line, column):
self.filename = filename
self.text = text self.text = text
self.line = line self.line = line
self.column = column self.column = column
def __str__(self): def __str__(self):
return "stack underflow (%d,%d): %s" % (self.line, self.column, self.text) return "stack underflow (%s,%d,%d): %s" % (self.filename, self.line, self.column, self.text)
class StackNotEmpty(Exception): class StackNotEmpty(Exception):
def __init__(self, line, column): def __init__(self, filename, line, column):
self.filename = filename
self.line = line self.line = line
self.column = column self.column = column
def __str__(self): def __str__(self):
return "stack not empty (%d,%d)" % (self.line, self.column) return "stack not empty (%s,%d,%d)" % (self.filename, self.line, self.column)
class ParseError(Exception): class ParseError(Exception):
def __init__(self, message, line, column): def __init__(self, message, filename, line, column):
self.message = message self.message = message
self.line = line self.line = line
self.column = column self.column = column
self.filename = filename
def __str__(self): def __str__(self):
return "%s at line %d column %d" % (self.message, self.line, self.column) return "%s at line %d column %d in file %s" % (self.message, self.line, self.column, self.filename)
class UnknownWord(ParseError): class UnknownWord(ParseError):
def __init__(self, token): def __init__(self, filename, token):
ParseError.__init__(self, "unknown word '%s'" % token.text, token.line, token.column) ParseError.__init__(self, "unknown word '%s'" % token.text, filename, token.line, token.column)
class UnexpectedEndOfStream(ParseError): class UnexpectedEndOfStream(ParseError):
def __init__(self, line, column): def __init__(self, filename, line, column):
ParseError.__init__(self, "unexpected end of input", line, column) ParseError.__init__(self, "unexpected end of input", filename, line, column)
##### #####
@ -116,7 +119,7 @@ class Input:
def next_char(self): def next_char(self):
if self.end(): if self.end():
raise UnexpectedEndOfStream(self.line, self.column) raise UnexpectedEndOfStream(self.current_file_name, self.line, self.column)
c = self.text[self.offset] c = self.text[self.offset]
self.offset += 1 self.offset += 1
if c in self.EOL: if c in self.EOL:
@ -285,13 +288,14 @@ class Forth:
self.int_prog = re.compile("-?[0-9]+") self.int_prog = re.compile("-?[0-9]+")
self.hex_prog = re.compile("\$[0-9A-Fa-f]+") self.hex_prog = re.compile("\$[0-9A-Fa-f]+")
self.state = None self.state = None
self.inputs = []
def push(self, item): def push(self, item):
self.stack.append(item) self.stack.append(item)
def pop(self, token): def pop(self, token):
if not self.stack: if not self.stack:
raise StackUnderflow(token.text, token.line, token.column) raise StackUnderflow(token.text, self.current_file_name, token.line, token.column)
else: else:
return self.stack.pop() return self.stack.pop()
@ -303,7 +307,7 @@ class Forth:
def pop_do_loop(self, token): def pop_do_loop(self, token):
if not self.do_loop_stack: if not self.do_loop_stack:
raise StackUnderflow(token.text, token.line, token.column) raise StackUnderflow(token.text, self.current_file_name, token.line, token.column)
else: else:
return self.do_loop_stack.pop() return self.do_loop_stack.pop()
@ -339,8 +343,9 @@ class Forth:
def set_state(self, state): def set_state(self, state):
self.state = state self.state = state
def parse_input(self, input): def parse_input(self, input, current_file_name):
self.input = input self.input = input
self.current_file_name = current_file_name
self.set_state("interpret") self.set_state("interpret")
while not self.input.end(): while not self.input.end():
if self.state == "interpret": if self.state == "interpret":
@ -354,6 +359,13 @@ class Forth:
token = self.next() token = self.next()
self.word = Word(token.text, self.text_section, label=token.canon()) self.word = Word(token.text, self.text_section, label=token.canon())
self.set_state("compile") self.set_state("compile")
elif token == "[include]":
self.inputs.append((self.input, self.current_file_name))
include_file_name = self.next().replace('"', '')
with open(include_file_name, "rt") as f:
self.parse_input(Input(f.read()), include_file_name)
self.input, self.current_file_name = self.inputs.pop()
elif token == "[code]": elif token == "[code]":
self.items.append(self.parse_code()) self.items.append(self.parse_code())
elif token == "[text-section]": elif token == "[text-section]":
@ -414,11 +426,11 @@ class Forth:
else: else:
self.push(word.name) self.push(word.name)
else: else:
raise UnknownWord(token) raise UnknownWord(self.current_file_name, token)
def compile(self, word): def compile(self, word):
if self.input.end(): if self.input.end():
raise UnexpectedEndOfStream(self.input.line, self.input.column) raise UnexpectedEndOfStream(self.current_file_name, self.input.line, self.input.column)
token = self.next() token = self.next()
if token == ";": if token == ";":
word.add("exit") word.add("exit")
@ -519,7 +531,7 @@ class Forth:
word.add("lit") word.add("lit")
word.add(token.text) word.add(token.text)
else: else:
raise UnknownWord(token) raise UnknownWord(self.current_file_name, token)
def parse_code(self): def parse_code(self):
self.input.mark_start() self.input.mark_start()
@ -613,7 +625,7 @@ class Forth:
def generate_output(self): def generate_output(self):
self.filter_used_words("main") self.filter_used_words("main")
if self.stack: if self.stack:
raise StackNotEmpty(self.input.line, self.input.column) raise StackNotEmpty(self.current_file_name, self.input.line, self.input.column)
section_outputs = [] section_outputs = []
for section in self.sections: for section in self.sections:
section_outputs.append("; section %s\n" % section) section_outputs.append("; section %s\n" % section)
@ -1952,9 +1964,9 @@ with open(args.file, "rt") as f:
f = Forth(args.sections.split(",")) f = Forth(args.sections.split(","))
try: try:
f.parse_input(Input(boot_text % boot_params)) f.parse_input(Input(boot_text % boot_params), "boot_text % boot_params")
f.parse_input(Input(basewords_text)) f.parse_input(Input(basewords_text), "basewords_text")
f.parse_input(Input(text)) f.parse_input(Input(text), args.file)
print f.generate_output() print f.generate_output()
except (ParseError, StackUnderflow, StackNotEmpty) as e: except (ParseError, StackUnderflow, StackNotEmpty) as e:
sys.stderr.write("error: %s\n" % str(e)) sys.stderr.write("error: %s\n" % str(e))