git @ Cat's Eye Technologies SixtyPical / d9e625d
Clean up driver code, add filename to error messages. Chris Pressey 4 years ago
4 changed file(s) with 82 addition(s) and 80 deletion(s). Raw diff Collapse all Expand all
00 SixtyPical
11 ==========
22
3 _Version 0.14. Work-in-progress, everything is subject to change._
3 _Version 0.15. Work-in-progress, everything is subject to change._
44
55 **SixtyPical** is a 6502-like programming language with advanced
66 static analysis.
2121 from sixtypical.analyzer import Analyzer
2222 from sixtypical.emitter import Emitter, Byte, Word
2323 from sixtypical.compiler import Compiler
24
25
26 def process_input_files(filenames, options):
27 programs = []
28
29 for filename in options.filenames:
30 text = open(filename).read()
31 parser = Parser(text, filename)
32 program = parser.program()
33 programs.append(program)
34
35 if options.parse_only:
36 return
37
38 #program = merge_programs(programs)
39 program = programs[0]
40
41 analyzer = Analyzer(debug=options.debug)
42 analyzer.analyze_program(program)
43
44 if options.analyze_only:
45 return
46
47 fh = sys.stdout
48
49 if options.origin.startswith('0x'):
50 start_addr = int(options.origin, 16)
51 else:
52 start_addr = int(options.origin, 10)
53
54 output_format = options.output_format
55
56 prelude = []
57 if options.prelude == 'c64':
58 output_format = 'prg'
59 start_addr = 0x0801
60 prelude = [0x10, 0x08, 0xc9, 0x07, 0x9e, 0x32,
61 0x30, 0x36, 0x31, 0x00, 0x00, 0x00]
62 elif options.prelude == 'vic20':
63 output_format = 'prg'
64 start_addr = 0x1001
65 prelude = [0x0b, 0x10, 0xc9, 0x07, 0x9e, 0x34,
66 0x31, 0x30, 0x39, 0x00, 0x00, 0x00]
67 elif options.prelude:
68 raise NotImplementedError("Unknown prelude: {}".format(options.prelude))
69
70 # If we are outputting a .PRG, we output the load address first.
71 # We don't use the Emitter for this b/c not part of addr space.
72 if output_format == 'prg':
73 fh.write(Word(start_addr).serialize(0))
74
75 emitter = Emitter(start_addr)
76 for byte in prelude:
77 emitter.emit(Byte(byte))
78 compiler = Compiler(emitter)
79 compiler.compile_program(program)
80 if options.debug:
81 pprint(emitter.accum)
82 else:
83 emitter.serialize(fh)
2484
2585
2686 if __name__ == '__main__':
71131 options, unknown = argparser.parse_known_args(sys.argv[1:])
72132 remainder = ' '.join(unknown)
73133
74 for filename in options.filenames:
75 text = open(filename).read()
76
77 try:
78 parser = Parser(text)
79 program = parser.program()
80 except Exception as e:
81 if options.traceback:
82 raise
83 else:
84 traceback.print_exception(e.__class__, e, None)
85 sys.exit(1)
86
87 if options.parse_only:
88 sys.exit(0)
89
90 try:
91 analyzer = Analyzer(debug=options.debug)
92 analyzer.analyze_program(program)
93 except Exception as e:
94 if options.traceback:
95 raise
96 else:
97 traceback.print_exception(e.__class__, e, None)
98 sys.exit(1)
99
100 if options.analyze_only:
101 sys.exit(0)
102
103 fh = sys.stdout
104
105 if options.origin.startswith('0x'):
106 start_addr = int(options.origin, 16)
134 try:
135 process_input_files(options.filenames, options)
136 except Exception as e:
137 if options.traceback:
138 raise
107139 else:
108 start_addr = int(options.origin, 10)
109
110 output_format = options.output_format
111
112 prelude = []
113 if options.prelude == 'c64':
114 output_format = 'prg'
115 start_addr = 0x0801
116 prelude = [0x10, 0x08, 0xc9, 0x07, 0x9e, 0x32,
117 0x30, 0x36, 0x31, 0x00, 0x00, 0x00]
118 elif options.prelude == 'vic20':
119 output_format = 'prg'
120 start_addr = 0x1001
121 prelude = [0x0b, 0x10, 0xc9, 0x07, 0x9e, 0x34,
122 0x31, 0x30, 0x39, 0x00, 0x00, 0x00]
123 elif options.prelude:
124 raise NotImplementedError("Unknown prelude: {}".format(options.prelude))
125
126 # If we are outputting a .PRG, we output the load address first.
127 # We don't use the Emitter for this b/c not part of addr space.
128 if output_format == 'prg':
129 fh.write(Word(start_addr).serialize(0))
130
131 emitter = Emitter(start_addr)
132 for byte in prelude:
133 emitter.emit(Byte(byte))
134 compiler = Compiler(emitter)
135 compiler.compile_program(program)
136 if options.debug:
137 pprint(emitter.accum)
138 else:
139 emitter.serialize(fh)
140 traceback.print_exception(e.__class__, e, None)
141 sys.exit(1)
55 RoutineType, VectorType, TableType, BufferType, PointerType,
66 LocationRef, ConstantRef, IndirectRef, IndexedRef, AddressRef,
77 )
8 from sixtypical.scanner import Scanner, SixtyPicalSyntaxError
8 from sixtypical.scanner import Scanner
99
1010
1111 class SymEntry(object):
1818
1919
2020 class Parser(object):
21 def __init__(self, text):
22 self.scanner = Scanner(text)
21 def __init__(self, text, filename):
22 self.scanner = Scanner(text, filename)
2323 self.symbols = {} # token -> SymEntry
2424 self.current_statics = {} # token -> SymEntry
2525 self.typedefs = {} # token -> Type AST
3131 self.backpatch_instrs = []
3232
3333 def syntax_error(self, msg):
34 raise SixtyPicalSyntaxError(self.scanner.line_number, msg)
34 self.scanner.syntax_error(msg)
3535
3636 def soft_lookup(self, name):
3737 if name in self.current_statics:
33
44
55 class SixtyPicalSyntaxError(ValueError):
6 def __init__(self, line_number, message):
7 super(SixtyPicalSyntaxError, self).__init__(line_number, message)
6 def __init__(self, filename, line_number, message):
7 super(SixtyPicalSyntaxError, self).__init__(filename, line_number, message)
88
99 def __str__(self):
10 return "Line {}: {}".format(self.args[0], self.args[1])
10 return "{}, line {}: {}".format(self.args[0], self.args[1], self.args[2])
1111
1212
1313 class Scanner(object):
14 def __init__(self, text):
14 def __init__(self, text, filename):
1515 self.text = text
16 self.filename = filename
1617 self.token = None
1718 self.type = None
1819 self.line_number = 1
6162 if self.token == token:
6263 self.scan()
6364 else:
64 raise SixtyPicalSyntaxError(self.line_number, "Expected '{}', but found '{}'".format(
65 token, self.token
66 ))
65 self.syntax_error("Expected '{}', but found '{}'".format(token, self.token))
6766
6867 def on(self, *tokens):
6968 return self.token in tokens
7372
7473 def check_type(self, type):
7574 if not self.type == type:
76 raise SixtyPicalSyntaxError(self.line_number, "Expected {}, but found '{}'".format(
77 self.type, self.token
78 ))
75 self.syntax_error("Expected {}, but found '{}'".format(self.type, self.token))
7976
8077 def consume(self, token):
8178 if self.token == token:
8380 return True
8481 else:
8582 return False
83
84 def syntax_error(self, msg):
85 raise SixtyPicalSyntaxError(self.filename, self.line_number, msg)