├── .gitignore
├── .vscode
└── settings.json
├── README.md
└── src
├── bytecode_parser.py
├── constant.py
├── environment.py
├── error.py
├── frame.py
├── instruction.py
├── interpreter.py
└── target.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.hn
3 | target-c
4 | logfile
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "python.pythonPath": "/usr/bin/python",
3 | "python.linting.enabled": false
4 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # 하늘
6 |
7 | 누리의 RPython 백엔드입니다.
8 |
--------------------------------------------------------------------------------
/src/bytecode_parser.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import sys
3 | import os
4 | sys.path.append('/Users/suhdonghwi/Documents/utils/pypy')
5 | sys.path.append('C:\Users\hwido\Documents\Nuri\pypy')
6 |
7 | import math
8 |
9 | from instruction import *
10 | from constant import *
11 | from interpreter import *
12 |
13 | from rpython.rlib.rarithmetic import intmask
14 | from rpython.rlib.rstruct.runpack import runpack
15 |
16 | from target import *
17 |
18 | TYPE_NAMES = ['NONE', 'INTEGER', 'REAL', 'CHAR', 'BOOLEAN', 'FUNC']
19 | for (i, typename) in enumerate(TYPE_NAMES):
20 | globals()['TYPE_' + typename] = i
21 |
22 |
23 | class LineInfo:
24 | def __init__(self, line = 0):
25 | self.line = line
26 | self.file_path = None
27 |
28 | class BytecodeParser:
29 | def __init__(self, code):
30 | self.code = str(code)
31 | self.pos = 0
32 |
33 | def consume_raw(self, offset=1):
34 | consumed = self.code[self.pos:self.pos + offset]
35 | self.pos += offset
36 | return consumed
37 |
38 | def consume_raw_reverse(self, offset=1):
39 | consumed = ''
40 | for i in range(self.pos + offset - 1, intmask(self.pos) - 1, -1):
41 | consumed += self.code[i]
42 |
43 | self.pos += offset
44 | return consumed
45 |
46 | def consume_ubyte(self):
47 | return runpack(">B", self.consume_raw(1))
48 |
49 | def consume_byte(self):
50 | return runpack(">b", self.consume_raw(1))
51 |
52 | def consume_ushort(self):
53 | return runpack(">H", self.consume_raw(2))
54 |
55 | def consume_int(self):
56 | return runpack(">i", self.consume_raw(4))
57 |
58 | def consume_uint(self):
59 | return runpack(">I", self.consume_raw(4))
60 |
61 | def consume_longlong(self):
62 | return runpack(">q", self.consume_raw(8))
63 |
64 | def consume_ulonglong(self):
65 | return runpack(">Q", self.consume_raw(8))
66 |
67 | def consume_double(self):
68 | return runpack(">d", self.consume_raw(8))
69 |
70 | def parse_integer(self):
71 | data = self.consume_longlong()
72 | return ConstInteger(data)
73 |
74 | def parse_double(self):
75 | data = self.consume_double()
76 | return ConstReal(data)
77 |
78 | def parse_char(self):
79 | head = self.consume_ubyte()
80 | result = u''
81 |
82 | if head < 0x80:
83 | result = unichr(head)
84 | elif head < 0xe0:
85 | result = (chr(head) + self.consume_raw(1)).decode('utf-8')
86 | elif head < 0xf0:
87 | result = (chr(head) + self.consume_raw(2)).decode('utf-8')
88 | else:
89 | result = (chr(head) + self.consume_raw(3)).decode('utf-8')
90 |
91 | return ConstChar(result)
92 |
93 | def parse_string(self):
94 | count = self.consume_ulonglong()
95 |
96 | result = u''
97 | for i in range(count):
98 | result += self.parse_char().charval
99 |
100 | return result
101 |
102 | def parse_string_ubyte(self):
103 | count = self.consume_ubyte()
104 |
105 | result = u''
106 | for i in range(count):
107 | result += self.parse_char().charval
108 |
109 | return result
110 |
111 | def parse_boolean(self):
112 | value = self.consume_ubyte()
113 | return ConstBoolean(value == 1)
114 |
115 | def parse_instruction(self):
116 | opcode = self.consume_ubyte()
117 |
118 | inst = Instruction(opcode)
119 | if opcode in (INST_PUSH, INST_LOAD_LOCAL, INST_STORE_LOCAL, INST_LOAD_DEREF, INST_STORE_GLOBAL, INST_LOAD_GLOBAL, INST_POP_JMP_IF_FALSE, INST_JMP):
120 | inst.operand_int = self.consume_uint()
121 | elif opcode == INST_FREE_VAR:
122 | inst.operand_free_var_list = self.parse_free_var_list()
123 | elif opcode == INST_CALL:
124 | inst.operand_josa_list = self.parse_josa_list()
125 | elif opcode in (INST_ADD_STRUCT, INST_MAKE_STRUCT):
126 | inst.operand_str = self.parse_string_ubyte()
127 | inst.operand_josa_list = self.parse_josa_list()
128 | elif opcode == INST_GET_FIELD:
129 | inst.operand_str = self.parse_string_ubyte()
130 |
131 |
132 | return inst
133 |
134 | def parse_constant_list(self):
135 | count = self.consume_ulonglong()
136 |
137 | result = []
138 | for i in range(count):
139 | const_type = self.consume_ubyte()
140 |
141 | if const_type == TYPE_NONE:
142 | result.append(ConstNone())
143 | elif const_type == TYPE_INTEGER:
144 | result.append(self.parse_integer())
145 | elif const_type == TYPE_REAL:
146 | result.append(self.parse_double())
147 | elif const_type == TYPE_CHAR:
148 | result.append(self.parse_char())
149 | elif const_type == TYPE_BOOLEAN:
150 | result.append(self.parse_boolean())
151 | elif const_type == TYPE_FUNC:
152 | result.append(self.parse_funcobject())
153 |
154 | return result
155 |
156 | def parse_instruction_list(self):
157 | count = self.consume_ulonglong()
158 |
159 | result = []
160 | for i in range(0, count):
161 | result.append(self.parse_instruction())
162 |
163 | return result
164 |
165 | def parse_string_list(self):
166 | count = self.consume_ulonglong()
167 |
168 | result = []
169 | for i in range(0, count):
170 | result.append(self.parse_string())
171 |
172 | return result
173 |
174 | def parse_josa_list(self):
175 | count = self.consume_ubyte()
176 |
177 | result = []
178 | for i in range(0, count):
179 | result.append(self.parse_string_ubyte())
180 |
181 | return result
182 |
183 | def parse_free_var_list(self):
184 | count = self.consume_ubyte()
185 |
186 | result = []
187 | for i in range(0, count):
188 | is_free_var = self.consume_ubyte() == 1
189 | index = self.consume_ubyte()
190 |
191 | result.append((is_free_var, index))
192 |
193 | return result
194 |
195 | def parse_line_no_table(self):
196 | count = self.consume_ulonglong()
197 |
198 | result = []
199 | for i in range(0, count):
200 | inst_offset = self.consume_uint()
201 | is_line = self.consume_ubyte() == 1
202 | if is_line:
203 | line = self.consume_ushort()
204 | result.append((inst_offset, LineInfo(line)))
205 | else:
206 | path = self.parse_string()
207 | info = LineInfo()
208 | info.file_path = path
209 | result.append((inst_offset, info))
210 |
211 | return result
212 |
213 | def parse_funcobject(self):
214 | josa_list = self.parse_josa_list()
215 | var_names = self.parse_string_list()
216 | stack_size = self.consume_ulonglong()
217 | local_number = self.consume_uint()
218 | const_table = self.parse_constant_list()
219 | name = self.parse_string()
220 | file_path = self.parse_string()
221 | line_no = self.consume_ushort()
222 | line_no_table = self.parse_line_no_table()
223 | insts = self.parse_instruction_list()
224 |
225 | josa_map = []
226 | for josa in josa_list:
227 | josa_map.append((josa, None))
228 |
229 | return ConstFunc(josa_map, CodeObject(var_names, const_table, name, file_path, insts, local_number, stack_size, line_no, line_no_table))
230 |
231 |
232 | if __name__ == "__main__":
233 | entry_point(sys.argv)
234 |
--------------------------------------------------------------------------------
/src/constant.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from rpython.rlib import jit
3 |
4 | from error import *
5 |
6 |
7 | class Constant:
8 | _attrs_ = []
9 |
10 | def add(self, other):
11 | raise BinaryTypeError(self, other, u"더하기")
12 |
13 | def subtract(self, other):
14 | raise BinaryTypeError(self, other, u"빼기")
15 |
16 | def multiply(self, other):
17 | raise BinaryTypeError(self, other, u"곱하기")
18 |
19 | def divide(self, other):
20 | raise BinaryTypeError(self, other, u"나누기")
21 |
22 | def mod(self, other):
23 | raise BinaryTypeError(self, other, u"나머지")
24 |
25 | def equal(self, other):
26 | raise BinaryTypeError(self, other, u"비교")
27 |
28 | def less_than(self, other):
29 | raise BinaryTypeError(self, other, u"대소 비교")
30 |
31 | def greater_than(self, other):
32 | raise BinaryTypeError(self, other, u"대소 비교")
33 |
34 | def negate(self):
35 | raise UnaryTypeError(self, u"부호 반전")
36 |
37 | def logic_not(self):
38 | raise UnaryTypeError(self, u"논리 부정")
39 |
40 | def logic_and(self, other):
41 | raise BinaryTypeError(self, other, u"그리고")
42 |
43 | def logic_or(self, other):
44 | raise BinaryTypeError(self, other, u"또는")
45 |
46 | def show(self):
47 | raise NotImplementedError()
48 |
49 | def type_name(self):
50 | raise NotImplementedError()
51 |
52 |
53 | class ConstNone(Constant):
54 | def equal(self, other):
55 | if isinstance(other, ConstNone):
56 | return ConstBoolean(True)
57 | else:
58 | return ConstBoolean(False)
59 |
60 | def show(self):
61 | return u"(없음)"
62 |
63 | def type_name(self):
64 | return u"(없음)"
65 |
66 |
67 | class ConstInteger(Constant):
68 | _attrs_ = _immutable_fields_ = ['intval']
69 |
70 | def __init__(self, value):
71 | self.intval = value
72 |
73 | def add(self, other):
74 | if isinstance(other, ConstInteger):
75 | return ConstInteger(self.intval + other.intval)
76 | elif isinstance(other, ConstReal):
77 | return ConstReal(self.intval + other.doubleval)
78 | else:
79 | raise BinaryTypeError(self, other, u"더하기")
80 |
81 | def subtract(self, other):
82 | if isinstance(other, ConstInteger):
83 | return ConstInteger(self.intval - other.intval)
84 | elif isinstance(other, ConstReal):
85 | return ConstReal(self.intval - other.doubleval)
86 | else:
87 | raise BinaryTypeError(self, other, u"빼기")
88 |
89 | def multiply(self, other):
90 | if isinstance(other, ConstInteger):
91 | return ConstInteger(self.intval * other.intval)
92 | elif isinstance(other, ConstReal):
93 | return ConstReal(self.intval * other.doubleval)
94 | else:
95 | raise BinaryTypeError(self, other, u"곱하기")
96 |
97 | def divide(self, other):
98 | if isinstance(other, ConstInteger):
99 | if other.intval == 0:
100 | raise DivideByZero()
101 | return ConstInteger(self.intval / other.intval)
102 | elif isinstance(other, ConstReal):
103 | if other.doubleval == 0:
104 | raise DivideByZero()
105 | return ConstReal(self.intval / other.doubleval)
106 | else:
107 | raise BinaryTypeError(self, other, u"나누기")
108 |
109 | def mod(self, other):
110 | if isinstance(other, ConstInteger):
111 | if other.intval == 0:
112 | raise DivideByZero()
113 | return ConstInteger(self.intval % other.intval)
114 | else:
115 | raise BinaryTypeError(self, other, u"나머지")
116 |
117 | def equal(self, other):
118 | if isinstance(other, ConstInteger):
119 | return ConstBoolean(self.intval == other.intval)
120 | elif isinstance(other, ConstReal):
121 | return ConstBoolean(self.intval == other.doubleval)
122 | else:
123 | return ConstBoolean(False)
124 |
125 | def less_than(self, other):
126 | if isinstance(other, ConstInteger):
127 | return ConstBoolean(self.intval < other.intval)
128 | elif isinstance(other, ConstReal):
129 | return ConstBoolean(self.intval < other.doubleval)
130 | else:
131 | raise BinaryTypeError(self, other, u"대소 비교")
132 |
133 | def greater_than(self, other):
134 | if isinstance(other, ConstInteger):
135 | return ConstBoolean(self.intval > other.intval)
136 | elif isinstance(other, ConstReal):
137 | return ConstBoolean(self.intval > other.doubleval)
138 | else:
139 | raise BinaryTypeError(self, other, u"대소 비교")
140 |
141 | def negate(self):
142 | return ConstInteger(-self.intval)
143 |
144 | def show(self):
145 | return str(self.intval).decode('utf-8')
146 |
147 | def type_name(self):
148 | return u"정수"
149 |
150 |
151 | class ConstReal(Constant):
152 | _attrs_ = _immutable_fields_ = ['doubleval']
153 |
154 | def __init__(self, value):
155 | self.doubleval = value
156 |
157 | def add(self, other):
158 | if isinstance(other, ConstInteger):
159 | return ConstReal(self.doubleval + other.intval)
160 | elif isinstance(other, ConstReal):
161 | return ConstReal(self.doubleval + other.doubleval)
162 | else:
163 | raise BinaryTypeError(self, other, u"더하기")
164 |
165 | def subtract(self, other):
166 | if isinstance(other, ConstInteger):
167 | return ConstReal(self.doubleval - other.intval)
168 | elif isinstance(other, ConstReal):
169 | return ConstReal(self.doubleval - other.doubleval)
170 | else:
171 | raise BinaryTypeError(self, other, u"빼기")
172 |
173 | def multiply(self, other):
174 | if isinstance(other, ConstInteger):
175 | return ConstReal(self.doubleval * other.intval)
176 | elif isinstance(other, ConstReal):
177 | return ConstReal(self.doubleval * other.doubleval)
178 | else:
179 | raise BinaryTypeError(self, other, u"곱하기")
180 |
181 | def divide(self, other):
182 | if isinstance(other, ConstInteger):
183 | if other.intval == 0:
184 | raise DivideByZero()
185 | return ConstReal(self.doubleval / other.intval)
186 | elif isinstance(other, ConstReal):
187 | if other.doubleval == 0:
188 | raise DivideByZero()
189 | return ConstReal(self.doubleval / other.doubleval)
190 | else:
191 | raise BinaryTypeError(self, other, u"나누기")
192 |
193 | def equal(self, other):
194 | if isinstance(other, ConstReal):
195 | return ConstBoolean(self.doubleval == other.doubleval)
196 | elif isinstance(other, ConstInteger):
197 | return ConstBoolean(self.doubleval == other.intval)
198 | else:
199 | return ConstBoolean(False)
200 |
201 | def less_than(self, other):
202 | if isinstance(other, ConstInteger):
203 | return ConstBoolean(self.doubleval < other.intval)
204 | elif isinstance(other, ConstReal):
205 | return ConstBoolean(self.doubleval < other.doubleval)
206 | else:
207 | raise BinaryTypeError(self, other, u"대소 비교")
208 |
209 | def greater_than(self, other):
210 | if isinstance(other, ConstInteger):
211 | return ConstBoolean(self.doubleval > other.intval)
212 | elif isinstance(other, ConstReal):
213 | return ConstBoolean(self.doubleval > other.doubleval)
214 | else:
215 | raise BinaryTypeError(self, other, u"대소 비교")
216 |
217 | def negate(self):
218 | return ConstReal(-self.doubleval)
219 |
220 | def show(self):
221 | return str(self.doubleval).decode('utf-8')
222 |
223 | def type_name(self):
224 | return u"실수"
225 |
226 |
227 | class ConstBoolean(Constant):
228 | _attrs_ = _immutable_fields_ = ['boolval']
229 |
230 | def __init__(self, value):
231 | self.boolval = value
232 |
233 | def equal(self, other):
234 | if isinstance(other, ConstBoolean):
235 | return ConstBoolean(self.boolval == other.boolval)
236 | else:
237 | return ConstBoolean(False)
238 |
239 | def logic_not(self):
240 | return ConstBoolean(not self.boolval)
241 |
242 | def logic_and(self, other):
243 | if isinstance(other, ConstBoolean):
244 | return ConstBoolean(self.boolval and other.boolval)
245 | else:
246 | raise BinaryTypeError(self, other, u"그리고")
247 |
248 | def logic_or(self, other):
249 | if isinstance(other, ConstBoolean):
250 | return ConstBoolean(self.boolval or other.boolval)
251 | else:
252 | raise BinaryTypeError(self, other, u"또는")
253 |
254 | def show(self):
255 | return u"참" if self.boolval else u"거짓"
256 |
257 | def type_name(self):
258 | return u"부울"
259 |
260 |
261 | class ConstChar(Constant):
262 | _attrs_ = _immutable_fields_ = ['charval']
263 |
264 | def __init__(self, value):
265 | self.charval = value
266 |
267 | def equal(self, other):
268 | if isinstance(other, ConstChar):
269 | return ConstBoolean(self.charval == other.charval)
270 | else:
271 | return ConstBoolean(False)
272 |
273 | def show(self):
274 | return u"'" + self.charval + u"'"
275 |
276 | def type_name(self):
277 | return u"문자"
278 |
279 |
280 | class ConstFunc(Constant):
281 | _attrs_ = _immutable_fields_ = ['funcval', 'builtinval', 'josa_map']
282 |
283 | def __init__(self, josa_map, value, builtin_func=None):
284 | self.josa_map = josa_map
285 | self.funcval = value
286 | self.builtinval = builtin_func
287 |
288 | def show(self):
289 | return u"(함수)"
290 |
291 | def type_name(self):
292 | return u"함수"
293 |
294 | def copy(self):
295 | func = ConstFunc(self.josa_map, self.funcval.copy(), self.builtinval)
296 | return func
297 |
298 | class ConstStruct(Constant):
299 | _attrs_ = _immutable_fields_ = ['struct_data']
300 |
301 | def __init__(self, struct_data):
302 | self.struct_data = struct_data
303 |
304 | def equal(self, other):
305 | if isinstance(other, ConstStruct):
306 | if len(self.struct_data) == len(other.struct_data):
307 | for k in self.struct_data.keys():
308 | try:
309 | if not self.struct_data[k].equal(other.struct_data[k]).boolval:
310 | return ConstBoolean(False)
311 | except:
312 | return ConstBoolean(False)
313 |
314 | return ConstBoolean(True)
315 | else:
316 | return ConstBoolean(False)
317 |
318 | def show(self):
319 | result = u"{"
320 | items = self.struct_data.items()
321 | for (k, v) in items[:-1]:
322 | result += k + u": " + v.show() + u", "
323 | (k, v) = items[-1]
324 | result += k + u": " + v.show() + u"}"
325 |
326 | return result
327 |
328 | def type_name(self):
329 | return u"구조체"
330 |
331 | def copy(self):
332 | return ConstStruct(self.struct_data)
333 |
334 | def get_field(self, field):
335 | try:
336 | return self.struct_data[field]
337 | except KeyError:
338 | raise UnknownField(field)
339 |
340 | class CodeObject:
341 | _attrs_ = _immutable_fields_ = [
342 | 'var_names', 'const_table', 'name', 'file_path', 'code', 'local_number', 'stack_size', 'line_no', 'line_no_table', 'free_vars'
343 | ]
344 |
345 | def __init__(self, var_names, const_table, name, file_path, code, local_number, stack_size, line_no, line_no_table, free_vars=[]):
346 | self.var_names = var_names
347 | self.const_table = const_table
348 | self.name = name
349 | self.file_path = file_path
350 | self.code = code
351 | self.local_number = local_number
352 | self.stack_size = stack_size
353 | self.line_no = line_no
354 | self.line_no_table = line_no_table
355 | self.free_vars = free_vars
356 |
357 | @jit.elidable
358 | def get_constant(self, index):
359 | return self.const_table[index]
360 |
361 | def calculate_pos(self, pc):
362 | line = self.line_no
363 | path = self.file_path
364 |
365 | for (inst_offset, line_info) in self.line_no_table:
366 | if pc >= inst_offset:
367 | if line_info.file_path is None:
368 | line = line_info.line
369 | else:
370 | path = line_info.file_path
371 |
372 | else:
373 | break
374 |
375 | assert path is not None
376 | return (line, path)
377 |
378 | def copy(self):
379 | return CodeObject(self.var_names,
380 | self.const_table,
381 | self.name,
382 | self.file_path,
383 | self.code,
384 | self.local_number,
385 | self.stack_size,
386 | self.line_no,
387 | self.line_no_table,
388 | list(self.free_vars))
389 |
390 |
391 | class BuiltinObject:
392 | _attrs_ = _immutable_fields_ = ['func']
393 |
394 | def __init__(self, func):
395 | self.func = func
396 |
397 | def list_to_struct(lst):
398 | if len(lst) == 0:
399 | return ConstNone()
400 | else:
401 | return ConstStruct({u'첫번째': lst[0], u'나머지': list_to_struct(lst[1:])})
402 |
403 | def collect(lst):
404 | if isinstance(lst, ConstNone):
405 | return []
406 | elif isinstance(lst, ConstStruct):
407 | fst = lst.get_field(u'첫번째')
408 | return [fst] + collect(lst.get_field(u'나머지'))
409 | else:
410 | raise InvalidType(u'구조체', lst.type_name())
411 |
412 | def collect_string(lst):
413 | if isinstance(lst, ConstNone):
414 | return u''
415 | elif isinstance(lst, ConstStruct):
416 | fst = lst.get_field(u'첫번째')
417 | if isinstance(fst, ConstChar):
418 | return fst.charval + collect_string(lst.get_field(u'나머지'))
419 | else:
420 | raise InvalidType(u'문자', fst.type_name())
421 | else:
422 | raise InvalidType(u'구조체', lst.type_name())
423 |
--------------------------------------------------------------------------------
/src/environment.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from rpython.rlib import rfile
3 |
4 | from constant import *
5 | from bytecode_parser import BytecodeParser
6 | from error import InvalidType
7 | from rpython.rlib import rrandom, rtimer
8 | from rpython.rlib.rarithmetic import intmask, r_uint
9 | import os
10 | import time
11 |
12 | LINE_BUFFER_LENGTH = 1024
13 |
14 |
15 | def print_char_builtin_func(args):
16 | ch = args[0]
17 | assert ch is not None
18 | if isinstance(ch, ConstChar):
19 | os.write(1, ch.charval.encode('utf-8'))
20 | return ConstNone()
21 | else:
22 | raise InvalidType(u"문자", ch.type_name())
23 |
24 | def stringize_builtin_func(args):
25 | l = []
26 | for ch in args[0].show():
27 | l.append(ConstChar(ch))
28 |
29 | return list_to_struct(l)
30 |
31 | def input_builtin_func(args):
32 | stdin, stdout, stderr = rfile.create_stdio()
33 | line = stdin.readline(LINE_BUFFER_LENGTH)
34 | parser = BytecodeParser(line)
35 |
36 | result = []
37 | while parser.code[parser.pos] != '\n':
38 | result.append(parser.parse_char())
39 |
40 | return list_to_struct(result)
41 |
42 | def to_integer_builtin_func(args):
43 | a = args[0]
44 | if isinstance(a, ConstStruct):
45 | s = collect_string(a)
46 | try:
47 | return ConstInteger(int(s.encode('utf-8')))
48 | except:
49 | return ConstNone()
50 | elif isinstance(a, ConstChar):
51 | try:
52 | return ConstInteger(int(a.charval.encode('utf-8')))
53 | except:
54 | return ConstNone()
55 | elif isinstance(a, ConstReal):
56 | return ConstInteger(int(a.doubleval))
57 | elif isinstance(a, ConstInteger):
58 | return a
59 | else:
60 | raise InvalidType(u"정수화할 수 있는", a.type_name())
61 |
62 | def to_real_builtin_func(args):
63 | a = args[0]
64 | if isinstance(a, ConstStruct):
65 | s = collect_string(a)
66 | try:
67 | return ConstReal(float(s.encode('utf-8')))
68 | except:
69 | return ConstNone()
70 | elif isinstance(a, ConstChar):
71 | try:
72 | return ConstReal(float(a.charval.encode('utf-8')))
73 | except:
74 | return ConstNone()
75 | elif isinstance(a, ConstReal):
76 | return a
77 | elif isinstance(a, ConstInteger):
78 | return ConstReal(float(a.intval))
79 | else:
80 | raise InvalidType(u"실수화할 수 있는", a.type_name())
81 |
82 | def random_builtin_func(args):
83 | rng = rrandom.Random(seed=r_uint(rtimer.read_timestamp()))
84 | return ConstInteger(intmask(rng.genrand32()))
85 |
86 | def get_unicode_func(args):
87 | a = args[0]
88 | if isinstance(a, ConstChar):
89 | return ConstInteger(ord(a.charval[0]))
90 | else:
91 | raise InvalidType(u"문자", a.type_name())
92 |
93 | def nth_unicode_func(args):
94 | a = args[0]
95 | if isinstance(a, ConstInteger):
96 | return ConstChar(unichr(a.intval))
97 | else:
98 | raise InvalidType(u"정수", a.type_name())
99 |
100 |
101 | print_char_builtin = ConstFunc([(u"을", None)], None, print_char_builtin_func)
102 | stringize_builtin = ConstFunc([(u"을", None)], None, stringize_builtin_func)
103 | input_builtin = ConstFunc([], None, input_builtin_func)
104 | to_integer_builtin = ConstFunc([(u"을", None)], None, to_integer_builtin_func)
105 | to_real_builtin = ConstFunc([(u"을", None)], None, to_real_builtin_func)
106 | random_builtin = ConstFunc([], None, random_builtin_func)
107 | get_unicode_builtin = ConstFunc([(u"의", None)], None, get_unicode_func)
108 | nth_unicode_builtin = ConstFunc([(u"번째", None)], None, nth_unicode_func)
109 |
110 | default_globals = {
111 | u"문자로 보여주다": print_char_builtin,
112 | u"문자열로 바꾸다": stringize_builtin,
113 | u"정수로 바꾸다": to_integer_builtin,
114 | u"실수로 바꾸다": to_real_builtin,
115 | u"난수를 가져오다": random_builtin,
116 | u"입력받다": input_builtin,
117 | u"유니코드 값": get_unicode_builtin,
118 | u"유니코드 문자": nth_unicode_builtin,
119 | }
120 |
121 |
--------------------------------------------------------------------------------
/src/error.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 |
4 | class HaneulError(Exception):
5 | def __init__(self, message):
6 | self.message = message
7 | self.error_line = 0
8 |
9 |
10 | class InvalidType(HaneulError):
11 | def __init__(self, expected, given):
12 | HaneulError.__init__(self, u"%s 타입의 값을 받아야하는데 %s 타입의 값이 주어졌습니다." % (expected, given))
13 |
14 |
15 | class UnboundVariable(HaneulError):
16 | def __init__(self, name):
17 | HaneulError.__init__(self, u"변수 '%s'를 찾을 수 없습니다." % name)
18 |
19 |
20 | class UnboundJosa(HaneulError):
21 | def __init__(self, name):
22 | HaneulError.__init__(self, u"조사 '%s'를 찾을 수 없습니다." % name)
23 |
24 | class DuplicateJosa(HaneulError):
25 | def __init__(self, name):
26 | HaneulError.__init__(self, u"조사 '%s'는 이미 적용되었습니다." % name)
27 |
28 |
29 | class UndefinedFunction(HaneulError):
30 | def __init__(self):
31 | HaneulError.__init__(self, u"선언은 되었으나 정의되지 않은 함수를 호출할 수 없습니다.")
32 |
33 | class UndefinedStruct(HaneulError):
34 | def __init__(self, name):
35 | HaneulError.__init__(self, u"구조체 '%s'를 찾을 수 없습니다." % name)
36 |
37 | class UnknownField(HaneulError):
38 | def __init__(self, field):
39 | HaneulError.__init__(self, u"'%s'라는 필드를 찾을 수 없습니다." % field)
40 |
41 | class FieldNumberMismatch(HaneulError):
42 | def __init__(self, expected, given):
43 | HaneulError.__init__(self, u"구조체에 %d개의 필드가 있는데 %d가 주어졌습니다." % (expected, given))
44 |
45 | class DivideByZero(HaneulError):
46 | def __init__(self):
47 | HaneulError.__init__(self, u"0으로 나눌 수 없습니다.")
48 |
49 | class BinaryTypeError(HaneulError):
50 | def __init__(self, lhs, rhs, operation):
51 | HaneulError.__init__(self, u"%s 타입의 값과 %s 타입의 값은 %s 연산을 지원하지 않습니다." % (lhs.type_name(), rhs.type_name(), operation))
52 |
53 | class UnaryTypeError(HaneulError):
54 | def __init__(self, value, operation):
55 | HaneulError.__init__(self, u"%s 타입의 값은 %s 연산을 지원하지 않습니다." % (value.type_name(), operation))
56 |
--------------------------------------------------------------------------------
/src/frame.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from rpython.rlib import jit
4 | from rpython.rlib.rarithmetic import intmask
5 |
6 | from constant import ConstFunc
7 |
8 |
9 | class Frame:
10 | _immutable_fields_ = ['locals', 'stack']
11 | # _virtualizable_ = ['local_list[*]', 'stack[*]', 'stack_top']
12 |
13 | def __init__(self, local_number, local_list, max_stack_size):
14 | # self = jit.hint(self, access_directly=True, fresh_virtualizable=True)
15 |
16 | self.local_list = local_list + [None] * intmask(local_number - len(local_list))
17 |
18 | self.stack = [None] * max_stack_size
19 | self.stack_top = 0
20 |
21 | def push(self, value):
22 | index = self.stack_top
23 | assert index >= 0
24 | self.stack[index] = value
25 | self.stack_top += 1
26 |
27 | def pop(self):
28 | index = self.stack_top - 1
29 | assert index >= 0
30 | value = self.stack[index]
31 | self.stack[index] = None
32 | self.stack_top = index
33 | return value
34 |
35 | def load(self, index):
36 | assert index >= 0
37 |
38 | return self.local_list[index]
39 |
40 | def load_reserve(self, index):
41 | """
42 | Free variable 등록이 아직 정의되지 않은 상수에 대해 적용되었을 때,
43 | 해당 위치에 비어있는 ConstFunc를 넣어주고 반환하는 함수입니다.
44 | """
45 | assert(index >= 0)
46 |
47 | value = self.local_list[index]
48 | if value is None:
49 | self.local_list[index] = ConstFunc(None, None, None)
50 | return self.local_list[index]
51 | else:
52 | return value
53 |
54 | def store(self, value, index):
55 | """
56 | 로컬 인덱스를 받고 해당 위치에 값을 넣어주는 함수입니다.
57 | 해당 위치에 (`load_reserve`를 통해 저장된) ConstNone이 들어있으면
58 | ConstNone 객체를 value로 바꿉니다.
59 | """
60 | assert(index >= 0)
61 |
62 | dest = self.local_list[index]
63 | if isinstance(dest, ConstFunc) and isinstance(value, ConstFunc):
64 | dest.funcval = value.funcval
65 | dest.builtinval = value.builtinval
66 | dest.josa_map = value.josa_map
67 | else:
68 | self.local_list[index] = value
69 |
--------------------------------------------------------------------------------
/src/instruction.py:
--------------------------------------------------------------------------------
1 | INSTRUCTION_NAMES = ['PUSH', 'POP', 'LOAD_LOCAL', 'STORE_LOCAL', 'LOAD_DEREF', 'STORE_GLOBAL', 'LOAD_GLOBAL',
2 | 'CALL', 'ADD_STRUCT', 'MAKE_STRUCT', 'GET_FIELD', 'JMP', 'POP_JMP_IF_FALSE', 'FREE_VAR',
3 | 'ADD', 'SUBTRACT', 'MULTIPLY', 'DIVIDE', 'MOD',
4 | 'EQUAL', 'LESS_THAN', 'GREATER_THAN', 'NEGATE', "LOGIC_NOT", "LOGIC_AND", "LOGIC_OR"]
5 | for (i, inst) in enumerate(INSTRUCTION_NAMES):
6 | globals()['INST_' + inst] = i
7 |
8 |
9 | class Instruction:
10 | _immutable_fields_ = ['opcode', 'operand_int', 'operand_josa_list[*]', 'operand_free_var_list[*]', 'operand_str']
11 |
12 | def __init__(self, opcode):
13 | self.opcode = opcode
14 | self.operand_int = 0
15 | self.operand_josa_list = None
16 | self.operand_free_var_list = None
17 | self.operand_str = None
18 |
--------------------------------------------------------------------------------
/src/interpreter.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from rpython.rlib import jit
3 |
4 | from instruction import *
5 | from constant import *
6 | from error import *
7 | from frame import Frame
8 |
9 | jitdriver = jit.JitDriver(greens=['pc', 'code_object'],
10 | reds=['frame', 'self']
11 | # , virtualizables=['frame']
12 | )
13 |
14 |
15 | @jit.unroll_safe
16 | def resolve_josa(josa, josa_map):
17 | if josa == u"_":
18 | for (j, (k, v)) in enumerate(josa_map):
19 | if v is None:
20 | return (k, j)
21 | raise HaneulError(u"이 함수에는 더 이상 값을 적용할 수 없습니다.")
22 | else:
23 | for (j, (k, v)) in enumerate(josa_map):
24 | if josa == k:
25 | if v is not None:
26 | raise DuplicateJosa(josa)
27 |
28 | return (josa, j)
29 |
30 | raise UnboundJosa(josa)
31 |
32 |
33 | def resize_list(l, size, value=None):
34 | l.extend([value] * (size - len(l)))
35 |
36 | class Env:
37 | def __init__(self, var_map, struct_map):
38 | self.var_map = var_map
39 | self.struct_map = struct_map
40 |
41 | def store(self, name, value):
42 | self.var_map[name] = value
43 |
44 | def add_struct(self, name, fields):
45 | self.struct_map[name] = fields
46 |
47 | @jit.elidable
48 | def lookup(self, name):
49 | try:
50 | return self.var_map[name]
51 | except KeyError:
52 | raise UnboundVariable(name)
53 |
54 | @jit.elidable
55 | def lookup_struct(self, name):
56 | try:
57 | return self.struct_map[name]
58 | except KeyError:
59 | raise UndefinedStruct(name)
60 |
61 |
62 | class Interpreter:
63 | def __init__(self, env):
64 | self.env = env
65 | self.call_stack = []
66 |
67 | def run(self, code_object, args):
68 | pc = 0
69 | frame = Frame(code_object.local_number, args, code_object.stack_size)
70 |
71 | try:
72 | while True: # pc < len(code_object.code):
73 | jitdriver.jit_merge_point(
74 | pc=pc, code_object=code_object,
75 | frame=frame, self=self)
76 |
77 | if pc >= len(code_object.code):
78 | if frame.stack_top == 0:
79 | return None
80 | else:
81 | result = frame.pop()
82 |
83 | (pc, frame, code_object) = self.call_stack.pop()
84 |
85 | pc += 1
86 | frame.push(result)
87 | continue
88 |
89 | inst = jit.promote(code_object.code[pc])
90 | op = jit.promote(inst.opcode)
91 |
92 | if op == INST_PUSH:
93 | frame.push(code_object.get_constant(inst.operand_int))
94 |
95 | elif op == INST_POP:
96 | frame.pop()
97 |
98 | elif op == INST_LOAD_LOCAL:
99 | frame.push(frame.load(inst.operand_int))
100 |
101 | elif op == INST_STORE_LOCAL:
102 | # frame.append(frame.pop())
103 | frame.store(frame.pop(), inst.operand_int)
104 |
105 | elif op == INST_LOAD_DEREF:
106 | frame.push(code_object.free_vars[inst.operand_int])
107 |
108 | elif op == INST_LOAD_GLOBAL:
109 | frame.push(self.env.lookup(code_object.var_names[inst.operand_int]))
110 |
111 | elif op == INST_STORE_GLOBAL:
112 | self.env.store(code_object.var_names[inst.operand_int], frame.pop())
113 |
114 | elif op == INST_CALL:
115 | given_arity = len(inst.operand_josa_list)
116 |
117 | value = frame.pop()
118 | if isinstance(value, ConstFunc):
119 | args = []
120 | rest_arity = 0
121 |
122 | if value.josa_map is None: # **자유 변수가** 선언만 된 경우
123 | raise UndefinedFunction()
124 |
125 | josa_map = list(value.josa_map)
126 | for josa in inst.operand_josa_list:
127 | (found_josa, index) = resolve_josa(josa, josa_map)
128 | josa_map[index] = (found_josa, frame.pop())
129 |
130 | for (_, v) in josa_map:
131 | args.append(v)
132 | if v is None:
133 | rest_arity += 1
134 |
135 | if rest_arity > 0:
136 | func = ConstFunc([], value.funcval, value.builtinval)
137 | func.josa_map = josa_map
138 | frame.push(func)
139 | else: # rest_arity == 0
140 | if value.builtinval is not None:
141 | func_result = value.builtinval(args)
142 | frame.push(func_result)
143 | else:
144 | self.call_stack.append((pc, frame, code_object))
145 | pc = 0
146 | frame = Frame(value.funcval.local_number, args, value.funcval.stack_size)
147 | code_object = value.funcval
148 | continue
149 |
150 | elif value is None: # **로컬 함수가** 선언만 된 경우
151 | raise UndefinedFunction()
152 | else:
153 | raise InvalidType(u"함수", value.type_name())
154 |
155 | elif op == INST_ADD_STRUCT:
156 | self.env.add_struct(inst.operand_str, inst.operand_josa_list)
157 |
158 | elif op == INST_MAKE_STRUCT:
159 | fields = self.env.lookup_struct(inst.operand_str)
160 | struct_data = {}
161 |
162 | expected_field_num = len(fields)
163 | given_field_num = len(inst.operand_josa_list)
164 |
165 | if expected_field_num != given_field_num:
166 | raise FieldNumberMismatch(expected_field_num, given_field_num)
167 |
168 | for field in inst.operand_josa_list:
169 | if field not in fields:
170 | raise UnknownField(field)
171 |
172 | value = frame.pop()
173 | struct_data[field] = value
174 |
175 | frame.push(ConstStruct(struct_data))
176 |
177 | elif op == INST_GET_FIELD:
178 | value = frame.pop()
179 | field = inst.operand_str
180 |
181 | if isinstance(value, ConstStruct):
182 | frame.push(value.get_field(field))
183 | else:
184 | raise InvalidType(u"구조체", value.type_name())
185 |
186 | elif op == INST_JMP:
187 | pc = inst.operand_int
188 | continue
189 |
190 | elif op == INST_POP_JMP_IF_FALSE:
191 | value = frame.pop()
192 | if isinstance(value, ConstBoolean):
193 | if value.boolval == False:
194 | pc = inst.operand_int
195 | continue
196 | else:
197 | raise InvalidType(u"참 또는 거짓", value.type_name())
198 |
199 | elif op == INST_FREE_VAR:
200 | func = frame.pop().copy()
201 |
202 | for (is_free_var, index) in inst.operand_free_var_list:
203 | if is_free_var:
204 | value = code_object.free_vars[index]
205 | else:
206 | value = frame.load_reserve(index)
207 |
208 | assert isinstance(func, ConstFunc)
209 | func.funcval.free_vars.append(value)
210 |
211 | frame.push(func)
212 |
213 | elif op == INST_NEGATE:
214 | value = frame.pop()
215 | frame.push(value.negate())
216 | elif op == INST_LOGIC_NOT:
217 | value = frame.pop()
218 | frame.push(value.logic_not())
219 | else:
220 | rhs, lhs = frame.pop(), frame.pop()
221 | if op == INST_ADD:
222 | frame.push(lhs.add(rhs))
223 | elif op == INST_SUBTRACT:
224 | frame.push(lhs.subtract(rhs))
225 | elif op == INST_MULTIPLY:
226 | frame.push(lhs.multiply(rhs))
227 | elif op == INST_DIVIDE:
228 | frame.push(lhs.divide(rhs))
229 | elif op == INST_MOD:
230 | frame.push(lhs.mod(rhs))
231 | elif op == INST_EQUAL:
232 | frame.push(lhs.equal(rhs))
233 | elif op == INST_LESS_THAN:
234 | frame.push(lhs.less_than(rhs))
235 | elif op == INST_GREATER_THAN:
236 | frame.push(lhs.greater_than(rhs))
237 | elif op == INST_LOGIC_AND:
238 | frame.push(lhs.logic_and(rhs))
239 | elif op == INST_LOGIC_OR:
240 | frame.push(lhs.logic_or(rhs))
241 |
242 | pc += 1
243 |
244 | except HaneulError as e:
245 | self.call_stack.append((pc, frame, code_object))
246 | raise e
247 |
--------------------------------------------------------------------------------
/src/target.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import os
3 |
4 | from interpreter import Interpreter, CodeObject, Env
5 | from bytecode_parser import BytecodeParser
6 | from error import HaneulError
7 | from constant import ConstInteger
8 | from environment import default_globals
9 |
10 | def entry_point(argv):
11 |
12 | try:
13 | filename = argv[1]
14 | except IndexError:
15 | print "파일이 필요합니다."
16 | return 1
17 |
18 | content = ""
19 | if os.name == 'nt':
20 | fp = open(filename, 'rb')
21 | content = fp.read()
22 | fp.close()
23 | else:
24 | fp = os.open(filename, os.O_RDONLY, 0777)
25 | while True:
26 | read = os.read(fp, 4096)
27 | if len(read) == 0:
28 | break
29 | content += read
30 | os.close(fp)
31 |
32 |
33 | parser = BytecodeParser(content)
34 | func_object = parser.parse_funcobject()
35 |
36 | code_object = func_object.funcval
37 | interpreter = Interpreter(Env(default_globals, {}))
38 | try:
39 | interpreter.run(code_object, [])
40 | except HaneulError as e:
41 | stack_trace = []
42 |
43 | for (pc, frame, code_object) in interpreter.call_stack:
44 | (error_line, error_path) = code_object.calculate_pos(pc)
45 | """
46 | if e.error_line == 0:
47 | e.error_line = error_line
48 | """
49 |
50 | if len(code_object.file_path) != 0:
51 | stack_trace.append((code_object.name, error_path, error_line))
52 |
53 | for (name, path, line) in stack_trace:
54 | if name == u"":
55 | print (u"파일 '%s', %d번째 줄:" % (path, line)).encode('utf-8')
56 | else:
57 | print (u"파일 '%s', %d번째 줄, %s:" % (path, line, name)).encode('utf-8')
58 |
59 | (_, path, line) = stack_trace[-1]
60 | error_file = open(path.encode('utf-8'), 'r')
61 | last_line = None
62 | for i in range(0, line):
63 | last_line = error_file.readline()
64 |
65 | assert last_line is not None
66 | if last_line[-1] == '\n':
67 | last_line = last_line[:-1]
68 |
69 | line_width = len(str(line))
70 | print (" " * line_width) + " |"
71 | print "%d | %s" % (line, last_line)
72 | print (" " * line_width) + " |"
73 | print e.message.encode('utf-8')
74 |
75 | return 0
76 |
77 |
78 | def target(*args):
79 | return entry_point, None
80 |
81 |
82 | def jitpolicy(driver):
83 | from rpython.jit.codewriter.policy import JitPolicy
84 | return JitPolicy()
85 |
--------------------------------------------------------------------------------