def parse(self) -> list: """Scanner로부터 받아온 Token들을 parse하는 메서드 최초 기호와 마지막 기호가 담긴 stack과 Token들이 담긴 queue를 이용하여 PARSING_TABLE에 따라 parse한다. LL parser의 원리를 사용한다. Returns: SyntaxTree, SymbolTable로 구성된 list를 반환한다. Raises: NoSemiColonError: 세미 콜론이 있어야할 위치에 없을 때 발생한다. NotDefinedCompileError: 에러를 파악할 수 없을 때 발생한다. RedundantVariableDeclarationError: 심볼 테이블에 이미 같은 식별자, 블록 넘버의 심볼이 있을 때 발생한다. RuntimeError: 심볼 테이블에 int, char 이외의 변수가 입력되는 경우 발생한다. """ # Symbol Table symbol_table = SymbolTable(self._source_code) # 변수의 타입 기록 vtype = None # 현재 word가 변수 선언할 때의 식별자인지 기록 is_decl_var = False # Syntax Tree syntax_tree = SyntaxTree() # syntax tree에서 현재 접근 중인 노드의 스택 노드 current_stack_node: TreeStackNode = None # syntax tree에서 현재 접근 중인 노드의 부모 스택 parent_stack = Stack() # error handling을 위해 token이 담긴 queue에서 꺼낸 token들을 저장하는 stack used_token_stack = Stack() # symbol을 관리할 stack stack = Stack() stack.push(Parser.END_SYMBOL) stack.push(Parser.START_SYMBOL) root_node = TreeStackNode( TokenNode(Token(Parser.TOTAL_SYMBOL, None, None, 0))) root_node.token_node.add_child( Token(Parser.START_SYMBOL, None, None, 0)) root_node.token_node.add_child(Token(Parser.END_SYMBOL, None, None, 0)) syntax_tree.root = root_node.token_node parent_stack.push(root_node) current_stack_node = TreeStackNode( root_node.token_node.get_child(root_node.next_child())) self._token_queue.enqueue(Token(Parser.END_SYMBOL, "", 0, 0)) log(f"queue : {self._token_queue}") log(f"stack : {stack}") log(f"work : start\n") try: while not stack.is_empty(): for tree_stack_node in parent_stack: log(f"stack node : {tree_stack_node.token_node.token}") for token_node in syntax_tree: log(f"node : {token_node.token} '{token_node.token.token_string}'" ) log(f"current node : {current_stack_node.token_node.token}") if self._token_queue.get().token == stack.get(): # 기호가 같을 때 pop token = self._token_queue.dequeue() used_token_stack.push(token) stack.pop() # 토큰이 데이터 타입인 경우, 기록한다 if token.token == 'int' or token.token == 'char': vtype = token.token # 토큰이 세미 콜론인 경우, 심볼 테이블에 추가하는 것을 그만둔다. elif token.token == ';': is_decl_var = False # 토큰이 word인 경우, elif is_decl_var and token.token == 'word': symbol_table.add_symbol(token, vtype) # syntax tree의 현재 접근 중인 노드를 queue에서 빠진 token로 교체한다. current_stack_node.token_node.token = token # 부모로 올라가 다음 child로 이동한다. if not token.token == Parser.END_SYMBOL: while True: current_stack_node = parent_stack.pop() next_child = current_stack_node.token_node.get_child( current_stack_node.next_child()) if next_child is not None: parent_stack.push(current_stack_node) current_stack_node = TreeStackNode(next_child) break log(f"queue : {self._token_queue}") log(f"stack : {stack}") log(f"work : pop\n") else: # 기호가 다를 땐 확장 symbol = stack.pop() next_symbols = Parser.PARSING_TABLE[symbol][ self._token_queue.get().token] # 기호가 decl인 경우, 변수 선언으로 간주하고 앞으로 나오는 word들을 심볼 테이블에 추가한다. if symbol == 'decl': is_decl_var = True if next_symbols[0] != 'e': # e는 엡실론으로 추가하지 않는다. # 역순으로 stack에 추가한다. for i in range(-1, -(len(next_symbols) + 1), -1): stack.push(next_symbols[i]) # 원래 순서로 syntax tree의 현재 접근 중인 노드의 child로 추가한다. for i in range(0, len(next_symbols)): current_stack_node.token_node.add_child( Token(next_symbols[i], None, None, 0)) # 부모 스택에 현재 스택 노드를 추가한 후 첫번째 child로 이동 parent_stack.push(current_stack_node) current_stack_node = TreeStackNode( current_stack_node.token_node.get_child( current_stack_node.next_child())) else: # 부모로 올라가 다음 child로 이동한다. while True: current_stack_node = parent_stack.pop() next_child = current_stack_node.token_node.get_child( current_stack_node.next_child()) if next_child is not None: parent_stack.push(current_stack_node) current_stack_node = TreeStackNode(next_child) break log(f"queue : {self._token_queue}") log(f"stack : {stack}") log(f"work : {symbol} -> {next_symbols}\n") except KeyError: for token in self._token_queue: pass # stack에 남은 다음 token이 세미 콜론이면 세미 콜론이 있어야할 곳에 없는 것으로 판정한다. if stack.get() == ';': raise NoSemiColonError(self._source_code, used_token_stack.get()) raise NotDefinedCompileError() return [syntax_tree, symbol_table]
def Syntax_tree_Optimization(syntax_tree): Code = [] TreeStack = Stack() block_num = 0 for token in syntax_tree: if token.token.token == "total": TreeStack.push(token.token.token) if token.token.token == "block" and TreeStack.get() == "total": block_num += 1 TreeStack.push(token.token.token) Code.append(token) if token.token.token == "}": Code.append(token) block_num -= 1 while TreeStack.get() != "block": if TreeStack.get() == "total": break TreeStack.pop() TreeStack.pop() if not TreeStack.is_empty() and TreeStack.get() == "WHILE": TreeStack.pop() if not TreeStack.is_empty() and TreeStack.get() == "ELSE": TreeStack.pop() TreeStack.pop() TreeStack.pop() if TreeStack.is_empty(): continue elif TreeStack.get() == "stat": if token.token.token == "WHILE" or token.token.token == "IF" or token.token.token == "RETURN": Code.append(token) TreeStack.push(token.token.token) if token.token.token == "word": Code.append(token) TreeStack.push(token.token.token) elif TreeStack.get() == "WHILE": if token.token.token == "cond": Code.append(token) TreeStack.push(token.token.token) elif TreeStack.get() == "cond": if token.token.token != "block" and token.token.token != "THEN": if token.token.token_string is not None: Code.append(token) if token.token.token == "block": TreeStack.pop() block_num += 1 Code.append(token) TreeStack.push(token.token.token) if token.token.token == "THEN": TreeStack.pop() Code.append(token) TreeStack.push(token.token.token) elif TreeStack.get() == "block": if token.token.token == "stat": TreeStack.push(token.token.token) elif TreeStack.get() == "IF": if token.token.token == "cond": Code.append(token) TreeStack.push(token.token.token) elif TreeStack.get() == "IF": if token.token.token == "cond": Code.append(token) TreeStack.push(token.token.token) elif TreeStack.get() == "THEN": if token.token.token == "block": block_num += 1 Code.append(token) TreeStack.push(token.token.token) if token.token.token == "ELSE": Code.append(token) TreeStack.push(token.token.token) elif TreeStack.get() == "ELSE": if token.token.token == "block": block_num += 1 Code.append(token) TreeStack.push(token.token.token) elif TreeStack.get() == 'word': if token.token.token == ";": Code.append(token) TreeStack.pop() continue if token.token.token_string is not None: Code.append(token) elif TreeStack.get() == "RETURN": if token.token.token == ";": Code.append(token) TreeStack.pop() if TreeStack.get() == "stat": TreeStack.pop() continue if token.token.token_string is not None: Code.append(token) Generate_code(Code)