started semantical analyzer
This commit is contained in:
parent
6116c7ed44
commit
780ac646c3
|
@ -7,6 +7,7 @@ import argparse
|
||||||
from .compiler_types import CompilerError
|
from .compiler_types import CompilerError
|
||||||
from .lexer import lexer
|
from .lexer import lexer
|
||||||
from .syntactical_analyzer import syntactical_analyzer
|
from .syntactical_analyzer import syntactical_analyzer
|
||||||
|
from .semantical_analyzer import semantical_analyzer
|
||||||
|
|
||||||
|
|
||||||
def _compile(args: argparse.Namespace):
|
def _compile(args: argparse.Namespace):
|
||||||
|
@ -16,10 +17,15 @@ def _compile(args: argparse.Namespace):
|
||||||
for token in tokens:
|
for token in tokens:
|
||||||
args.token_file.write(str(token) + "\n")
|
args.token_file.write(str(token) + "\n")
|
||||||
|
|
||||||
syntax = syntactical_analyzer(tokens)
|
syntax_tree = syntactical_analyzer(tokens)
|
||||||
|
|
||||||
if args.syntax_file:
|
if args.syntax_file:
|
||||||
args.syntax_file.write(syntax.tree_str())
|
args.syntax_file.write(syntax_tree.tree_str())
|
||||||
|
|
||||||
|
annotated_syntax_tree = semantical_analyzer(syntax_tree)
|
||||||
|
|
||||||
|
if args.annotated_file:
|
||||||
|
args.annotated_file.write(annotated_syntax_tree.tree_str())
|
||||||
|
|
||||||
def compile(args: argparse.Namespace):
|
def compile(args: argparse.Namespace):
|
||||||
try: _compile(args)
|
try: _compile(args)
|
||||||
|
@ -39,6 +45,8 @@ def parser(parser: argparse.ArgumentParser):
|
||||||
'-t', '--token_file', type=argparse.FileType('w', encoding='utf-8'))
|
'-t', '--token_file', type=argparse.FileType('w', encoding='utf-8'))
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-x', '--syntax_file', type=argparse.FileType('w', encoding='utf-8'))
|
'-x', '--syntax_file', type=argparse.FileType('w', encoding='utf-8'))
|
||||||
|
parser.add_argument(
|
||||||
|
'-n', '--annotated_file', type=argparse.FileType('w', encoding='utf-8'))
|
||||||
parser.set_defaults(func=compile)
|
parser.set_defaults(func=compile)
|
||||||
|
|
||||||
def main(argv: Sequence[str] | None = None):
|
def main(argv: Sequence[str] | None = None):
|
||||||
|
|
|
@ -1,2 +1,164 @@
|
||||||
# Kyler Olsen
|
# Kyler Olsen
|
||||||
# Mar 2024
|
# Mar 2024
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from .compiler_types import CompilerError, FileInfo
|
||||||
|
from . import syntactical_analyzer
|
||||||
|
|
||||||
|
|
||||||
|
class SymbolType(Enum):
|
||||||
|
struct = "struct"
|
||||||
|
enum = "enum"
|
||||||
|
function = "function"
|
||||||
|
builtin = "builtin"
|
||||||
|
variable = "variable"
|
||||||
|
|
||||||
|
|
||||||
|
class Symbol:
|
||||||
|
|
||||||
|
_name: str
|
||||||
|
_static: bool
|
||||||
|
_symbol_type: SymbolType
|
||||||
|
_references: list[Any]
|
||||||
|
|
||||||
|
def __init__(self, name: str, symbol_type: SymbolType):
|
||||||
|
self._name = name
|
||||||
|
self._symbol_type = symbol_type
|
||||||
|
self._references = []
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str: return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def symbol_type(self) -> SymbolType: return self._symbol_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def references(self): return self._references[:]
|
||||||
|
|
||||||
|
|
||||||
|
class SymbolTable:
|
||||||
|
|
||||||
|
_parent_table: "SymbolTable | None"
|
||||||
|
_symbols: list[Symbol]
|
||||||
|
|
||||||
|
def __init__(self, parent_table: "SymbolTable | None" = None):
|
||||||
|
self._parent_table = parent_table
|
||||||
|
self._symbols = []
|
||||||
|
|
||||||
|
def __getitem__(self, key: str) -> Symbol: return self.get(key)
|
||||||
|
def __setitem__(self, key: str, value: Symbol):
|
||||||
|
if key != value.name:
|
||||||
|
raise KeyError
|
||||||
|
self.set(value)
|
||||||
|
|
||||||
|
def get(self, key: str) -> Symbol:
|
||||||
|
for symbol in self._symbols:
|
||||||
|
if symbol.name == key:
|
||||||
|
return symbol
|
||||||
|
if self._parent_table is None:
|
||||||
|
raise KeyError
|
||||||
|
else:
|
||||||
|
return self._parent_table.get(key)
|
||||||
|
|
||||||
|
def set(self, value: Symbol):
|
||||||
|
for i, symbol in enumerate(self._symbols):
|
||||||
|
if symbol.name == value.name:
|
||||||
|
self._symbols[i] = value
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if self._parent_table is None:
|
||||||
|
raise KeyError
|
||||||
|
else:
|
||||||
|
self._parent_table.set(value)
|
||||||
|
|
||||||
|
def add(self, value: Symbol):
|
||||||
|
for symbol in self._symbols:
|
||||||
|
if symbol.name == value.name:
|
||||||
|
raise KeyError
|
||||||
|
else:
|
||||||
|
self._symbols.append(value)
|
||||||
|
|
||||||
|
def table_str(self, title: str, pre: str = "", pre_cont: str = "") -> str:
|
||||||
|
if len(self._symbols):
|
||||||
|
names: list[str] = []
|
||||||
|
types: list[SymbolType] = []
|
||||||
|
counts: list[int] = []
|
||||||
|
for symbol in self._symbols:
|
||||||
|
names.append(symbol.name)
|
||||||
|
types.append(symbol.symbol_type)
|
||||||
|
counts.append(len(symbol.references))
|
||||||
|
name_width = max(len(i) for i in names)
|
||||||
|
type_width = max(len(i.value) for i in types)
|
||||||
|
count_width = max(len(str(i)) for i in counts)
|
||||||
|
title_width = name_width + 2 + type_width + 3 + count_width
|
||||||
|
|
||||||
|
s = f"{pre} o{title.center(title_width, '-')}o\n"
|
||||||
|
for i in range(len(self._symbols)):
|
||||||
|
s += f"{pre_cont} |{(names[i] + ':').ljust(name_width + 1)} "
|
||||||
|
s += f"{types[i].value.ljust(type_width)} - "
|
||||||
|
s += f"{str(counts[i]).rjust(count_width)}|\n"
|
||||||
|
s += f"{pre_cont} o{'-' * title_width}o\n"
|
||||||
|
|
||||||
|
return s
|
||||||
|
else: return f"{pre} o-{title}-o\n"
|
||||||
|
|
||||||
|
|
||||||
|
class File:
|
||||||
|
|
||||||
|
_children: list[
|
||||||
|
syntactical_analyzer.Directive |
|
||||||
|
syntactical_analyzer.StructBlock |
|
||||||
|
syntactical_analyzer.FunctionBlock |
|
||||||
|
syntactical_analyzer.EnumBlock
|
||||||
|
]
|
||||||
|
_file_info: FileInfo
|
||||||
|
_symbol_table: SymbolTable
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
children: list[
|
||||||
|
syntactical_analyzer.Directive |
|
||||||
|
syntactical_analyzer.StructBlock |
|
||||||
|
syntactical_analyzer.FunctionBlock |
|
||||||
|
syntactical_analyzer.EnumBlock
|
||||||
|
],
|
||||||
|
file_info: FileInfo,
|
||||||
|
):
|
||||||
|
self._children = children[:]
|
||||||
|
self._file_info = file_info
|
||||||
|
self._symbol_table = SymbolTable()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def file_info(self) -> FileInfo: return self._file_info
|
||||||
|
|
||||||
|
def tree_str(self) -> str:
|
||||||
|
s: str = " File\n"
|
||||||
|
if self._children:
|
||||||
|
s += self._symbol_table.table_str("GLOBAL", "├─", "│ ")
|
||||||
|
for child in self._children[:-1]:
|
||||||
|
s += child.tree_str("├─", "│ ")
|
||||||
|
s += self._children[-1].tree_str("└─", " ")
|
||||||
|
else:
|
||||||
|
s += self._symbol_table.table_str("GLOBAL", "└─", " ")
|
||||||
|
return s
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _sa(syntax_tree: syntactical_analyzer.File) -> "File":
|
||||||
|
file = File(syntax_tree.children, syntax_tree._file_info)
|
||||||
|
for child in file._children:
|
||||||
|
symbol: Symbol | None = None
|
||||||
|
if isinstance(child, syntactical_analyzer.StructBlock):
|
||||||
|
symbol = Symbol(child._identifier._content, SymbolType.struct)
|
||||||
|
elif isinstance(child, syntactical_analyzer.FunctionBlock):
|
||||||
|
symbol = Symbol(child._identifier._content, SymbolType.function)
|
||||||
|
elif isinstance(child, syntactical_analyzer.EnumBlock):
|
||||||
|
symbol = Symbol(child._identifier._content, SymbolType.enum)
|
||||||
|
if symbol is not None:
|
||||||
|
file._symbol_table.add(symbol)
|
||||||
|
return file
|
||||||
|
|
||||||
|
|
||||||
|
def semantical_analyzer(syntax_tree: syntactical_analyzer.File) -> File:
|
||||||
|
return File._sa(syntax_tree)
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
# Feb 2024
|
# Feb 2024
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Sequence
|
from typing import Iterable, Sequence
|
||||||
|
|
||||||
from .compiler_types import CompilerError , FileInfo
|
from .compiler_types import CompilerError, FileInfo
|
||||||
from . import lexer
|
from . import lexer
|
||||||
|
|
||||||
|
|
||||||
|
@ -1546,6 +1546,15 @@ class File:
|
||||||
self._children = children[:]
|
self._children = children[:]
|
||||||
self._file_info = file_info
|
self._file_info = file_info
|
||||||
|
|
||||||
|
@property
|
||||||
|
def children(self) -> list[
|
||||||
|
Directive |
|
||||||
|
StructBlock |
|
||||||
|
FunctionBlock |
|
||||||
|
EnumBlock
|
||||||
|
]:
|
||||||
|
return self._children[:]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def file_info(self) -> FileInfo: return self._file_info
|
def file_info(self) -> FileInfo: return self._file_info
|
||||||
|
|
||||||
|
@ -1948,4 +1957,3 @@ def _statement_sa(tokens: list[lexer.Token]) -> Statement:
|
||||||
|
|
||||||
def syntactical_analyzer(tokens: Sequence[lexer.Token]) -> File:
|
def syntactical_analyzer(tokens: Sequence[lexer.Token]) -> File:
|
||||||
return File._sa(list(tokens))
|
return File._sa(list(tokens))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue