Added nestable code blocks

This commit is contained in:
Kyler 2024-07-06 16:27:01 -06:00
parent 6c00f2b5da
commit d802ca3e07
3 changed files with 490 additions and 16 deletions

View File

@ -192,6 +192,8 @@ fn test_func2() -> Point {
test += 15;
}
let length: int = 10;
for (i: int = 0; i < length; i++)
$(inData + i) ^= $(pass + ((i + offset) % pLength));
@ -204,6 +206,8 @@ fn test_func2() -> Point {
$(inData + i) ^= $(pass + ((i + offset) % pLength));
}
let i: int;
for (i = 0; i < length; i++) {
$(inData + i) ^= $(pass + ((i + offset) % pLength));
} else {

View File

@ -12,7 +12,7 @@ type SymbolDefinitionTypes = (
InternalDefinition |
syntactical_analyzer.FunctionParameter |
syntactical_analyzer.LetStatement |
syntactical_analyzer.ForPreDef |
ForPreDef |
syntactical_analyzer.StructBlock |
FunctionBlock |
syntactical_analyzer.EnumBlock |
@ -36,7 +36,10 @@ type Identifier = (
)
type Statement = (
type NestableCodeBlock = ForBlock | WhileBlock | DoBlock | IfBlock
type IntermediateStatement = (
syntactical_analyzer.Expression |
syntactical_analyzer.LetStatement |
syntactical_analyzer.LoopStatements |
@ -46,6 +49,16 @@ type Statement = (
)
type Statement = (
syntactical_analyzer.Expression |
syntactical_analyzer.LetStatement |
syntactical_analyzer.LoopStatements |
NestableCodeBlock |
InternalDefinition |
Identifier
)
type BlockHolder = (
ElseBlock |
ForPreDef |
@ -167,7 +180,7 @@ class InvalidOperand(SyntaxError):
syntactical_analyzer.UnaryExpression |
syntactical_analyzer.Operator
),
operand: Statement,
operand: IntermediateStatement | Statement,
):
if isinstance(operator, (
syntactical_analyzer.TernaryExpression,
@ -212,6 +225,10 @@ class CompoundIdentifier:
@property
def member(self) -> Identifier: return self._member
@property
def content(self) -> str:
return self.owner.content + '.' + self.member.content
@property
def file_info(self) -> FileInfo: return self._file_info
@ -240,6 +257,9 @@ class AddressOfIdentifier:
@property
def operand(self) -> Identifier: return self._operand
@property
def content(self) -> str: return self._operand.content
@property
def file_info(self) -> FileInfo: return self._file_info
@ -267,6 +287,9 @@ class DereferenceIdentifier:
@property
def operand(self) -> Identifier: return self._operand
@property
def content(self) -> str: return self._operand.content
@property
def file_info(self) -> FileInfo: return self._file_info
@ -423,13 +446,32 @@ class SymbolTable:
else: return f"{pre} o-{title}-o\n"
class ForSymbolTable(SymbolTable):
_parent_table: SymbolTable
_symbols: list[Symbol]
def __init__(self, parent_table: SymbolTable):
self._parent_table = parent_table
self._symbols = []
def set(self, value: Symbol): self._parent_table.set(value)
def add(self, value: Symbol): self._parent_table.add(value)
def for_add(self, value: Symbol):
for symbol in self._symbols:
if symbol.name == value.name:
raise KeyError
else:
self._symbols.append(value)
class CodeBlock:
_parent: BlockHolder
_code: list[Statement]
def __init__(self, parent: BlockHolder, code: list[Statement]):
self._parent = parent
def __init__(self, code: list[Statement]):
self._code = code[:]
def tree_str(
@ -449,7 +491,6 @@ class CodeBlock:
@staticmethod
def _sa(
parent: BlockHolder,
code: list[syntactical_analyzer.Statement],
symbol_table: SymbolTable,
members: list[syntactical_analyzer.LetStatement],
@ -568,11 +609,377 @@ class CodeBlock:
code_out.append(statement)
elif isinstance(statement, AddRefTypes):
add_ref_if(statement) # type: ignore
code_out.append(statement)
code_out.append(statement) # type: ignore
elif isinstance(statement, syntactical_analyzer.IfBlock):
code_out.append(
IfBlock._sa(statement, symbol_table, members))
elif isinstance(statement, syntactical_analyzer.DoBlock):
code_out.append(
DoBlock._sa(statement, symbol_table, members))
elif isinstance(statement, syntactical_analyzer.WhileBlock):
code_out.append(
WhileBlock._sa(statement, symbol_table, members))
elif isinstance(statement, syntactical_analyzer.ForBlock):
code_out.append(
ForBlock._sa(statement, symbol_table, members))
else:
code_out.append(statement)
return CodeBlock(parent, code_out)
return CodeBlock(code_out)
class ElseBlock:
_code: CodeBlock
_file_info: FileInfo
def __init__(
self,
code: CodeBlock,
file_info: FileInfo,
):
self._code = code
self._file_info = file_info
@property
def file_info(self) -> FileInfo: return self._file_info
def tree_str(self, pre: str = "", pre_cont: str = "") -> str:
s: str = f"{pre} Else Block\n"
s += self._code.tree_str(pre_cont + " ├─", pre_cont + "")
return s
@staticmethod
def _sa(
else_block: syntactical_analyzer.ElseBlock,
symbol_table: SymbolTable,
members: list[syntactical_analyzer.LetStatement],
) -> "ElseBlock":
code = CodeBlock._sa(else_block.code, symbol_table, members)
return ElseBlock(code, else_block.file_info)
class ForPreDef:
_identifier: Identifier
_type: syntactical_analyzer.DataType
_pointer: bool
_assignment: CodeBlock | None
_file_info: FileInfo
def __init__(
self,
identifier: Identifier,
type: syntactical_analyzer.DataType,
pointer: bool,
assignment: CodeBlock | None,
file_info: FileInfo,
):
self._identifier = identifier
self._type = type
self._pointer = pointer
self._assignment = assignment
self._file_info = file_info
@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: "
else: s += f"{pre_cont}└─ Type: "
if self._pointer: s+= "@"
s += f"{self._type}\n"
if self._assignment:
s += f"{pre_cont}└─ Value\n"
s += self._assignment.tree_str(pre_cont + " ├─", pre_cont + "")
return s
class ForBlock:
_pre_statement: CodeBlock | ForPreDef
_condition: CodeBlock
_code: CodeBlock
_post_statement: CodeBlock
_else: ElseBlock | None
_file_info: FileInfo
def __init__(
self,
pre_statement: CodeBlock | ForPreDef,
condition: CodeBlock,
code: CodeBlock,
post_statement: CodeBlock,
else_block: ElseBlock | None,
file_info: FileInfo,
):
self._pre_statement = pre_statement
self._condition = condition
self._code = code
self._post_statement = post_statement
self._else = else_block
self._file_info = file_info
@property
def file_info(self) -> FileInfo: return self._file_info
def tree_str(self, pre: str = "", pre_cont: str = "") -> str:
s: str = f"{pre} For Loop\n"
if self._code or self._else is not None:
cond_pre = f"{pre_cont}├─"
cond_pre_cont = f"{pre_cont}"
else:
cond_pre = f"{pre_cont}└─"
cond_pre_cont = f"{pre_cont} "
s += f"{cond_pre} Pre-Statement\n"
s += self._pre_statement.tree_str(
cond_pre_cont + "└─", cond_pre_cont + " ")
s += f"{cond_pre} Condition\n"
s += self._condition.tree_str(
cond_pre_cont + "└─", cond_pre_cont + " ")
s += f"{cond_pre} Post-Statement\n"
s += self._post_statement.tree_str(
cond_pre_cont + "└─", cond_pre_cont + " ")
s += self._code.tree_str(
pre_cont + " ├─", pre_cont + "", self._else is not None)
if self._else is not None:
s += self._else.tree_str(pre_cont + "└─", pre_cont + " ")
return s
@staticmethod
def _sa(
for_block: syntactical_analyzer.ForBlock,
parent_table: SymbolTable,
members: list[syntactical_analyzer.LetStatement],
) -> "ForBlock":
symbol_table = ForSymbolTable(parent_table)
if isinstance(for_block.pre_statement, syntactical_analyzer.ForPreDef):
assignment = CodeBlock._sa(for_block.code, symbol_table, members)
pre_statement = ForPreDef(
for_block.pre_statement.identifier,
for_block.pre_statement.data_type,
for_block.pre_statement.pointer,
assignment,
for_block.pre_statement.file_info,
)
try: symbol_table.for_add(Symbol(
pre_statement.identifier.content,
SymbolType.variable,
pre_statement,
))
except KeyError: raise VariableAlreadyDeclared(
pre_statement,
symbol_table.get(pre_statement.identifier.content).definition,
)
else: pre_statement = CodeBlock._sa(
[for_block.pre_statement], symbol_table, members)
condition = CodeBlock._sa([for_block.condition], symbol_table, members)
code = CodeBlock._sa(for_block.code, symbol_table, members)
post_statement = CodeBlock._sa(
[for_block.post_statement], symbol_table, members)
if for_block.else_block is None: else_block = None
else: else_block = ElseBlock._sa(
for_block.else_block, symbol_table, members)
return ForBlock(
pre_statement,
condition,
code,
post_statement,
else_block,
for_block.file_info,
)
class WhileBlock:
_condition: CodeBlock
_code: CodeBlock
_else: ElseBlock | None
_file_info: FileInfo
def __init__(
self,
condition: CodeBlock,
code: CodeBlock,
else_block: ElseBlock | None,
file_info: FileInfo,
):
self._condition = condition
self._code = code
self._else = else_block
self._file_info = file_info
@property
def file_info(self) -> FileInfo: return self._file_info
def tree_str(self, pre: str = "", pre_cont: str = "") -> str:
s: str = f"{pre} While Loop\n"
if self._code or self._else is not None:
s += f"{pre_cont}├─ Condition\n"
cond_pre = f"{pre_cont}"
else:
s += f"{pre_cont}└─ Condition\n"
cond_pre = f"{pre_cont} "
s += self._condition.tree_str(cond_pre + "└─", cond_pre + " ")
s += self._code.tree_str(
pre_cont + " ├─", pre_cont + "", self._else is not None)
if self._else is not None:
s += self._else.tree_str(pre_cont + "└─", pre_cont + " ")
return s
@staticmethod
def _sa(
while_block: syntactical_analyzer.WhileBlock,
symbol_table: SymbolTable,
members: list[syntactical_analyzer.LetStatement],
) -> "WhileBlock":
condition = CodeBlock._sa(
[while_block.condition], symbol_table, members)
code = CodeBlock._sa(while_block.code, symbol_table, members)
if while_block.else_block is None: else_block = None
else: else_block = ElseBlock._sa(
while_block.else_block, symbol_table, members)
return WhileBlock(
condition,
code,
else_block,
while_block.file_info,
)
class DoBlock:
_first_code: CodeBlock
_condition: CodeBlock
_second_code: CodeBlock | None
_else: ElseBlock | None
_file_info: FileInfo
def __init__(
self,
first_code: CodeBlock,
condition: CodeBlock,
second_code: CodeBlock | None,
else_block: ElseBlock | None,
file_info: FileInfo,
):
self._first_code = first_code
self._condition = condition
if second_code:
self._second_code = second_code
else:
self._second_code = None
self._else = else_block
self._file_info = file_info
@property
def file_info(self) -> FileInfo: return self._file_info
def tree_str(self, pre: str = "", pre_cont: str = "") -> str:
s: str = f"{pre} Do Loop\n"
s += self._first_code.tree_str(
pre_cont + " ├─", pre_cont + "", True)
if self._second_code or self._else is not None:
s += f"{pre_cont}├─ Condition\n"
cond_pre = f"{pre_cont}"
else:
s += f"{pre_cont}└─ Condition\n"
cond_pre = f"{pre_cont} "
s += self._condition.tree_str(cond_pre + "└─", cond_pre + " ")
if self._second_code is not None:
s += self._second_code.tree_str(
pre_cont + " ├─", pre_cont + "", self._else is not None)
if self._else is not None:
s += self._else.tree_str(pre_cont + "└─", pre_cont + " ")
return s
@staticmethod
def _sa(
do_block: syntactical_analyzer.DoBlock,
symbol_table: SymbolTable,
members: list[syntactical_analyzer.LetStatement],
) -> "DoBlock":
condition = CodeBlock._sa([do_block.condition], symbol_table, members)
first_code = CodeBlock._sa(do_block.first_code, symbol_table, members)
if do_block.second_code is None: second_code = None
else: second_code = CodeBlock._sa(
do_block.second_code, symbol_table, members)
if do_block.else_block is None: else_block = None
else: else_block = ElseBlock._sa(
do_block.else_block, symbol_table, members)
return DoBlock(
condition,
first_code,
second_code,
else_block,
do_block.file_info,
)
class IfBlock:
_condition: CodeBlock
_code: CodeBlock
_else: ElseBlock | None
_file_info: FileInfo
def __init__(
self,
condition: CodeBlock,
code: CodeBlock,
else_block: ElseBlock | None,
file_info: FileInfo,
):
self._condition = condition
self._code = code
self._else = else_block
self._file_info = file_info
@property
def file_info(self) -> FileInfo: return self._file_info
def tree_str(self, pre: str = "", pre_cont: str = "") -> str:
s: str = f"{pre} If Statement\n"
if self._code or self._else is not None:
s += f"{pre_cont}├─ Condition\n"
cond_pre = f"{pre_cont}"
else:
s += f"{pre_cont}└─ Condition\n"
cond_pre = f"{pre_cont} "
s += self._condition.tree_str(cond_pre + "└─", cond_pre + " ")
s += self._code.tree_str(
pre_cont + " ├─", pre_cont + "", self._else is not None)
if self._else is not None:
s += self._else.tree_str(pre_cont + "└─", pre_cont + " ")
return s
@staticmethod
def _sa(
if_block: syntactical_analyzer.IfBlock,
symbol_table: SymbolTable,
members: list[syntactical_analyzer.LetStatement],
) -> "IfBlock":
condition = CodeBlock._sa([if_block.condition], symbol_table, members)
code = CodeBlock._sa(if_block.code, symbol_table, members)
if if_block.else_block is None: else_block = None
else: else_block = ElseBlock._sa(
if_block.else_block, symbol_table, members)
return IfBlock(
condition,
code,
else_block,
if_block.file_info,
)
class FunctionReturnDefinition:
@ -721,7 +1128,7 @@ class FunctionBlock:
symbol_table.get(param.identifier.content).definition,
)
code = CodeBlock._sa(func, func.code, symbol_table, members)
code = CodeBlock._sa(func.code, symbol_table, members)
return FunctionBlock(
func.identifier,
@ -834,8 +1241,8 @@ def _compound_identifier(
statement.operator.content ==
syntactical_analyzer.BinaryOperatorEnum.MemberOf
): return CompoundIdentifier(
_assert_identifier(statement.operand1, statement.operator),
_assert_identifier(statement.operand2, statement.operator),
_assert_identifier(statement.operand1, statement.operator, True),
_assert_identifier(statement.operand2, statement.operator, True),
statement.file_info,
)
else: raise InvalidOperand(operator, statement)
@ -848,14 +1255,14 @@ def _augment_identifier(
statement.operator.content ==
syntactical_analyzer.PrefixUnaryOperatorEnum.AddressOf
): return AddressOfIdentifier(
_assert_identifier(statement.operand, statement.operator),
_assert_identifier(statement.operand, statement.operator, True),
statement.file_info,
)
elif (
statement.operator.content ==
syntactical_analyzer.PrefixUnaryOperatorEnum.Dereference
): return DereferenceIdentifier(
_assert_identifier(statement.operand, statement.operator),
_assert_identifier(statement.operand, statement.operator, True),
statement.file_info,
)
else: raise InvalidOperand(operator, statement)
@ -863,10 +1270,16 @@ def _augment_identifier(
def _assert_identifier(
statement: syntactical_analyzer.Statement,
operator: syntactical_analyzer.Operator,
harsh: bool = False
) -> Identifier:
if isinstance(statement, syntactical_analyzer.Identifier):
return statement
elif isinstance(statement, syntactical_analyzer.UnaryExpression):
if (
isinstance(statement.operand, syntactical_analyzer.BinaryExpression)
and not harsh
):
return statement # type: ignore
return _augment_identifier(statement, operator)
elif isinstance(statement, syntactical_analyzer.BinaryExpression):
return _compound_identifier(statement, operator)
@ -874,7 +1287,7 @@ def _assert_identifier(
def _create_internal_definition(
statement: syntactical_analyzer.Expression,
) -> list[Statement]:
) -> list[IntermediateStatement]:
flattened = _flatten_statement(statement)
internal_definition = InternalDefinition(
flattened[-1]) # type: ignore
@ -883,7 +1296,7 @@ def _create_internal_definition(
def _flatten_statement(
statement: syntactical_analyzer.Statement,
) -> list[Statement]:
) -> list[IntermediateStatement]:
if isinstance(statement, syntactical_analyzer.UnaryExpression):
if statement.operator.content in IncrementOperators:

View File

@ -829,6 +829,9 @@ class ElseBlock:
@property
def file_info(self) -> FileInfo: return self._file_info
@property
def code(self) -> list[Statement]: return self._code[:]
def tree_str(self, pre: str = "", pre_cont: str = "") -> str:
s: str = f"{pre} Else Block\n"
if self._code:
@ -882,6 +885,12 @@ class ForPreDef:
@property
def identifier(self) -> Identifier: return self._identifier
@property
def data_type(self) -> DataType: return self._type
@property
def pointer(self) -> bool: return self._pointer
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: "
@ -920,6 +929,22 @@ class ForBlock:
@property
def file_info(self) -> FileInfo: return self._file_info
@property
def pre_statement(self) -> Expression | ForPreDef:
return self._pre_statement
@property
def condition(self) -> Expression: return self._condition
@property
def code(self) -> list[Statement]: return self._code[:]
@property
def post_statement(self) -> Expression: return self._post_statement
@property
def else_block(self) -> ElseBlock | None: return self._else
def tree_str(self, pre: str = "", pre_cont: str = "") -> str:
s: str = f"{pre} For Loop\n"
if self._code or self._else is not None:
@ -1032,6 +1057,15 @@ class WhileBlock:
@property
def file_info(self) -> FileInfo: return self._file_info
@property
def condition(self) -> Expression: return self._condition
@property
def code(self) -> list[Statement]: return self._code[:]
@property
def else_block(self) -> ElseBlock | None: return self._else
def tree_str(self, pre: str = "", pre_cont: str = "") -> str:
s: str = f"{pre} While Loop\n"
if self._code or self._else is not None:
@ -1103,6 +1137,20 @@ class DoBlock:
@property
def file_info(self) -> FileInfo: return self._file_info
@property
def condition(self) -> Expression: return self._condition
@property
def first_code(self) -> list[Statement]: return self._first_code[:]
@property
def second_code(self) -> list[Statement] | None:
if self._second_code is None: return None
else: return self._second_code[:]
@property
def else_block(self) -> ElseBlock | None: return self._else
def tree_str(self, pre: str = "", pre_cont: str = "") -> str:
s: str = f"{pre} Do Loop\n"
if self._first_code:
@ -1190,6 +1238,15 @@ class IfBlock:
@property
def file_info(self) -> FileInfo: return self._file_info
@property
def condition(self) -> Expression: return self._condition
@property
def code(self) -> list[Statement]: return self._code[:]
@property
def else_block(self) -> ElseBlock | None: return self._else
def tree_str(self, pre: str = "", pre_cont: str = "") -> str:
s: str = f"{pre} If Statement\n"
if self._code or self._else is not None: