├── .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 | --------------------------------------------------------------------------------