Worked on semantical analyzer

This commit is contained in:
Kyler 2024-03-16 01:24:01 -06:00
parent 43e971f6c7
commit cb02a237dd
5 changed files with 343 additions and 43 deletions

View File

@ -1,5 +1,5 @@
# YTD 12-bit Computer
*Yeahbut, aka Kyler Olsen*
*Kyler Olsen*
**NOTICE: This project is still a *work in progress*.**

View File

@ -22,6 +22,7 @@ fn test_func(arg1: int, arg2: unsigned = 10) -> fixed {
}
fn test_func2() -> Point {
let test: float;
Point.points;
test++;
test--;

View File

@ -32,6 +32,9 @@ class FileInfo:
f"('{self._filename}',{self._line},{self._col},{self._length})"
)
def __str__(self) -> str:
return f"Ln {self.line}, Col {self.col} in file {self.filename}"
def __add__(self, other: "FileInfo") -> "FileInfo":
filename = self.filename
line = self.line
@ -69,11 +72,18 @@ class CompilerError(Exception):
_compiler_error_type = "Compiler"
def __init__(self, message: str, file_info: FileInfo):
def __init__(
self,
message: str,
file_info: FileInfo,
file_info_context: FileInfo | None = None,
):
new_message = message
new_message += (
f"\nIn file {file_info.filename} at line {file_info.line} "
)
if file_info_context is not None and file_info_context.lines:
file_info_context = None
if file_info.lines:
new_message += f"to line {file_info.line + file_info.lines}"
with open(file_info.filename, 'r') as file:
@ -84,8 +94,25 @@ class CompilerError(Exception):
new_message += f"col {file_info.col}\n\n"
with open(file_info.filename, 'r') as file:
new_message += file.readlines()[file_info.line-1]
new_message += ' ' * (
file_info.col - 1) + '^' * file_info.length
if file_info_context is not None:
context_line = [' '] * max(
file_info.col + file_info.length,
file_info_context.col +file_info_context.length,
)
for i in range(
file_info_context.col - 1,
file_info_context.col + file_info_context.length
):
context_line[i] = '~'
for i in range(
file_info.col - 1,
file_info.col + file_info.length
):
context_line[i] = '^'
new_message += ''.join(context_line)
else:
new_message += ' ' * (
file_info.col - 1) + '^' * file_info.length
super().__init__(new_message)

View File

@ -7,13 +7,9 @@ from .compiler_types import CompilerError, FileInfo
from . import syntactical_analyzer
class SyntaxError(CompilerError):
_compiler_error_type = "Semantic"
type SymbolDefinitionTypes = (
InternalDefinition |
syntactical_analyzer.FunctionParameter |
syntactical_analyzer.LetStatement |
syntactical_analyzer.ForPreDef |
syntactical_analyzer.StructBlock |
@ -30,6 +26,123 @@ type SymbolReferenceTypes = (
)
type Identifier = syntactical_analyzer.Identifier | CompoundIdentifier
type Statement = (
syntactical_analyzer.Expression |
syntactical_analyzer.LetStatement |
syntactical_analyzer.LoopStatements |
syntactical_analyzer.NestableCodeBlock |
Identifier
)
BaseValues: tuple[type, ...] = (
syntactical_analyzer.BuiltInConst,
syntactical_analyzer.NumberLiteral,
syntactical_analyzer.CharLiteral,
syntactical_analyzer.StringLiteral,
syntactical_analyzer.Identifier,
syntactical_analyzer.FunctionCall,
)
NestableCodeBlocks: tuple[type, ...] = (
syntactical_analyzer.ForBlock,
syntactical_analyzer.WhileBlock,
syntactical_analyzer.DoBlock,
syntactical_analyzer.IfBlock,
)
class SyntaxError(CompilerError):
_compiler_error_type = "Semantic"
class VariableAlreadyDeclared(SyntaxError):
def __init__(
self,
new: SymbolDefinitionTypes,
existing: SymbolDefinitionTypes,
):
message = (
f"The variable '{new.identifier.content}' was already "
f"declared at {str(existing.file_info)}" # type: ignore
)
super().__init__(message, new.file_info) # type: ignore
class UndeclaredVariable(SyntaxError):
def __init__(
self,
variable: SymbolDefinitionTypes,
):
message = (
f"The variable '{variable.identifier.content}' is undeclared."
)
super().__init__(message, variable.file_info) # type: ignore
class InvalidOperand(SyntaxError):
def __init__(
self,
operator: (
syntactical_analyzer.TernaryExpression |
syntactical_analyzer.BinaryExpression |
syntactical_analyzer.UnaryExpression
),
operand: Statement,
):
message = (
f"The operand at '{operand}' is invalid for the "
f"operator '{operator.operator.content.value}'."
)
super().__init__(
message,
operand.file_info, # type: ignore
operator.file_info, # type: ignore
)
class CompoundIdentifier:
_owner: Identifier
_member: Identifier
_file_info: FileInfo
def __init__(
self,
owner: Identifier,
member: Identifier,
file_info: FileInfo,
):
self._owner = owner
self._member = member
self._file_info = file_info
@property
def owner(self) -> Identifier: return self._owner
@property
def member(self) -> Identifier: return self._member
@property
def file_info(self) -> FileInfo: return self._file_info
def tree_str(self, pre: str = "", pre_cont: str = "") -> str:
s: str = f"{pre} CompoundIdentifier\n"
s += f"{pre_cont}├─ Owner\n"
s += self._owner.tree_str(pre_cont + " ├─", pre_cont + "")
s += f"{pre_cont}└─ Member\n"
s += self._member.tree_str(pre_cont + " └─", pre_cont + " ")
return s
class InternalDefinition:
_identifier: syntactical_analyzer.Identifier
@ -92,7 +205,11 @@ class Symbol:
def symbol_type(self) -> SymbolType: return self._symbol_type
@property
def references(self): return self._references[:]
def references(self) -> list[SymbolReferenceTypes]:
return self._references[:]
@property
def definition(self) -> SymbolDefinitionTypes: return self._definition
def add_reference(self, ref: SymbolReferenceTypes):
self._references.append(ref)
@ -223,12 +340,19 @@ class FunctionBlock:
def tree_str(self, pre: str = "", pre_cont: str = "") -> str:
s: str = f"{pre} Function: {self._identifier}\n"
if self._params or self._code or self._return_type is not None:
s += self._symbol_table.table_str("GLOBAL", "├─", "")
if (
self._params or
self._code or
self._return_type is not None or
self._members
):
s += self._symbol_table.table_str(
self.identifier.content, "├─", "")
else:
s += self._symbol_table.table_str("GLOBAL", "└─", " ")
s += self._symbol_table.table_str(
self.identifier.content, "└─", " ")
if self._params:
if self._code or self._return_type is not None:
if self._code or self._return_type is not None or self._members:
s += f"{pre_cont}├─ Parameters\n"
params_pre = f"{pre_cont}"
else:
@ -238,12 +362,21 @@ class FunctionBlock:
s += param.tree_str(params_pre + "├─", params_pre + "")
s += self._params[-1].tree_str(params_pre + "└─", params_pre + " ")
if self._return_type is not None:
if self._code:
if self._code or self._members:
s += f"{pre_cont}├─ Return Type: "
else:
s += f"{pre_cont}└─ Return Type: "
if self._return_type_pointer: s+= "@"
s += f"{self._return_type}\n"
if self._members:
if self._code:
s += f"{pre_cont}├─ Members: "
else:
s += f"{pre_cont}└─ Members: "
for code in self._members[:-1]:
s += code.tree_str(pre_cont + " ├─", pre_cont + "")
s += self._members[-1].tree_str(
pre_cont + " └─", pre_cont + " ")
if self._code:
s += f"{pre_cont}└─ Code\n"
for code in self._code[:-1]:
@ -257,7 +390,48 @@ class FunctionBlock:
parent_table: SymbolTable,
) -> "FunctionBlock":
symbol_table = SymbolTable(parent_table)
for param in func.params:
try:
symbol_table.add(Symbol(
param.identifier.content, SymbolType.variable, param))
except KeyError:
raise VariableAlreadyDeclared(
param,
symbol_table.get(param.identifier.content).definition,
)
members: list[syntactical_analyzer.LetStatement] = []
code: list[syntactical_analyzer.Statement] = []
for statement in func.code:
if isinstance(statement, syntactical_analyzer.LetStatement):
try:
symbol_table.add(Symbol(
statement.identifier.content,
SymbolType.variable, statement,
))
except KeyError:
raise VariableAlreadyDeclared(
statement,
symbol_table.get(
statement.identifier.content
).definition,
)
if statement.static:
members.append(statement)
else:
code.append(statement)
else:
code.append(statement)
return FunctionBlock(
func.identifier,
func.params,
func.return_type_pointer,
func.return_type,
members,
code,
func.file_info,
symbol_table,
)
class File:
@ -350,5 +524,55 @@ class File:
return file
def _get_all_operands(
expression: syntactical_analyzer.Expression,
) -> list[syntactical_analyzer.Expression]:
if isinstance(
expression,
BaseValues + (
syntactical_analyzer.LoopStatements,
syntactical_analyzer.NoOperation,
),
):
return [expression]
elif isinstance(expression, syntactical_analyzer.UnaryExpression):
return _get_all_operands(expression.operand)
elif isinstance(expression, syntactical_analyzer.BinaryExpression):
return (
_get_all_operands(expression.operand1) +
_get_all_operands(expression.operand2)
)
elif isinstance(expression, syntactical_analyzer.TernaryExpression):
return (
_get_all_operands(expression.operand1) +
_get_all_operands(expression.operand2) +
_get_all_operands(expression.operand3)
)
def _flatten_statement(
statement: syntactical_analyzer.Statement,
) -> list[syntactical_analyzer.Statement]:
if isinstance(statement, NestableCodeBlocks):
return [statement]
elif isinstance(
statement,
BaseValues + (
syntactical_analyzer.LoopStatements,
syntactical_analyzer.NoOperation,
),
):
return [statement]
elif isinstance(statement, syntactical_analyzer.UnaryExpression):
if isinstance(statement.operand, BaseValues):
return [statement]
elif isinstance(statement, syntactical_analyzer.BinaryExpression):
if (
statement.operator.content ==
syntactical_analyzer.BinaryOperatorEnum.MemberOf
):
pass
elif isinstance(statement, syntactical_analyzer.TernaryExpression):
pass
def semantical_analyzer(syntax_tree: syntactical_analyzer.File) -> File:
return File._sa(syntax_tree)

View File

@ -2,12 +2,38 @@
# Feb 2024
from enum import Enum
from typing import Iterable, Sequence
from typing import Sequence
from .compiler_types import CompilerError, FileInfo
from . import lexer
type NestableCodeBlock = ForBlock | WhileBlock | DoBlock | IfBlock
type Literal = (
BuiltInConst |
NumberLiteral |
CharLiteral |
StringLiteral
)
type Expression = (
Literal |
Identifier |
UnaryExpression |
BinaryExpression |
TernaryExpression |
FunctionCall |
NoOperation
)
type Statement = Expression | LetStatement | LoopStatements | NestableCodeBlock
type DataType = BuiltInDataType | Identifier
type Operator = UnaryOperator | BinaryOperator | TernaryOperator
class SyntaxError(CompilerError):
_compiler_error_type = "Syntax"
@ -129,30 +155,6 @@ class UnexpectedPunctuation(_UnexpectedTokenBase):
class ExpressionError(Exception): pass
type NestableCodeBlock = ForBlock | WhileBlock | DoBlock | IfBlock
type Literal = (
BuiltInConst |
NumberLiteral |
CharLiteral |
StringLiteral
)
type Expression = (
Literal |
Identifier |
UnaryExpression |
BinaryExpression |
TernaryExpression |
FunctionCall |
NoOperation
)
type Statement = Expression | LetStatement | LoopStatements | NestableCodeBlock
type DataType = BuiltInDataType | Identifier
class BuiltInConstEnum(Enum):
ConstTrue = "True"
ConstFalse = "False"
@ -238,6 +240,10 @@ class UnaryOperator:
self._content = content
self._file_info = file_info
@property
def content(self) -> PostfixUnaryOperatorEnum | PrefixUnaryOperatorEnum:
return self._content
@property
def file_info(self) -> FileInfo: return self._file_info
@ -296,6 +302,9 @@ class BinaryOperator:
self._content = content
self._file_info = file_info
@property
def content(self) -> BinaryOperatorEnum: return self._content
@property
def file_info(self) -> FileInfo: return self._file_info
@ -324,6 +333,9 @@ class TernaryOperator:
self._content = content
self._file_info = file_info
@property
def content(self) -> TernaryOperatorEnum: return self._content
@property
def file_info(self) -> FileInfo: return self._file_info
@ -620,6 +632,18 @@ class TernaryExpression:
self._operand3 = operand3
self._file_info = file_info
@property
def operator(self) -> TernaryOperator: return self._operator
@property
def operand1(self) -> Expression: return self._operand1
@property
def operand2(self) -> Expression: return self._operand2
@property
def operand3(self) -> Expression: return self._operand3
@property
def file_info(self) -> FileInfo: return self._file_info
@ -650,6 +674,15 @@ class BinaryExpression:
self._operand2 = operand2
self._file_info = file_info
@property
def operator(self) -> BinaryOperator: return self._operator
@property
def operand1(self) -> Expression: return self._operand1
@property
def operand2(self) -> Expression: return self._operand2
@property
def file_info(self) -> FileInfo: return self._file_info
@ -676,6 +709,12 @@ class UnaryExpression:
self._operand = operand
self._file_info = file_info
@property
def operator(self) -> UnaryOperator: return self._operator
@property
def operand(self) -> Expression: return self._operand
@property
def file_info(self) -> FileInfo: return self._file_info
@ -691,7 +730,7 @@ class LetStatement:
_type: DataType
_pointer: bool
_static: bool
_assignment: Expression | None
_assignment: Literal | None
_file_info: FileInfo
def __init__(
@ -711,10 +750,16 @@ class LetStatement:
self._file_info = file_info
@property
def file_info(self) -> FileInfo: return self._file_info
def identifier(self) -> Identifier: return self._identifier
@property
def identifier(self) -> Identifier: return self._identifier
def assignment(self) -> Literal | None: return self._assignment
@property
def static(self) -> bool: return self._static
@property
def file_info(self) -> FileInfo: return self._file_info
def tree_str(self, pre: str = "", pre_cont: str = "") -> str:
s: str = f"{pre} Let Statement: {self._identifier}\n"
@ -828,6 +873,9 @@ class ForPreDef:
@property
def file_info(self) -> FileInfo: return self._file_info
@property
def identifier(self) -> Identifier: return self._identifier
def tree_str(self, pre: str = "", pre_cont: str = "") -> str:
s: str = f"{pre} For Loop Pre-Definition: {self._identifier}\n"
if self._assignment: s += f"{pre_cont}├─ Type: "