finished move to rust
This commit is contained in:
@@ -1,3 +0,0 @@
|
|||||||
FROM python:3.9
|
|
||||||
|
|
||||||
RUN pip install lark mypy black
|
|
||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
The Boring Programming Language (Boring-Lang) is an attempt to create an easy, productive, general purpose programming language that makes as few interesting choices as possible while still being in line with modern concepts in programming languages.
|
The Boring Programming Language (Boring-Lang) is an attempt to create an easy, productive, general purpose programming language that makes as few interesting choices as possible while still being in line with modern concepts in programming languages.
|
||||||
|
|
||||||
The language:
|
The language (goals):
|
||||||
* is compiled with a run-time (llvm for convenience + c/rust compatibility)
|
* is compiled with a run-time (llvm for convenience + c/rust compatibility)
|
||||||
* has managed memory (via strong/weak pointers and automatic reference counting)
|
* has managed memory (via strong/weak pointers and automatic reference counting)
|
||||||
* uses async-await for all IO, with a built-in multi-core scheduler (tokio)
|
* uses async-await for all IO, with a built-in multi-core scheduler (tokio-based)
|
||||||
* supports algebraic data types (Result type for errors, Maybe/Optional type for nullables)
|
* supports algebraic data types (Result type for errors, Maybe/Optional type for nullables)
|
||||||
* supports parametric polymorphism (generics) with higher kinded types
|
* supports parametric polymorphism (generics) with higher kinded types
|
||||||
* uses struct+traits, rather than classes or stuct+interfaces
|
* uses struct+traits, rather than classes or stuct+interfaces
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
fn add(a, b) { a + b }
|
|
||||||
fn subtract(a, b) { a - b }
|
|
||||||
fn main() {
|
|
||||||
add(10, subtract(9, 1))
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
import sys
|
|
||||||
import copy
|
|
||||||
from boring import parse
|
|
||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Environment:
|
|
||||||
identifiers: dict
|
|
||||||
|
|
||||||
|
|
||||||
class Interpreter:
|
|
||||||
def handle_identifier(self, env, identifier):
|
|
||||||
return env.identifiers[identifier.name]
|
|
||||||
|
|
||||||
def handle_literal_int(self, env, literal_int):
|
|
||||||
return literal_int.value
|
|
||||||
|
|
||||||
def handle_function_call(self, env, function_call):
|
|
||||||
new_env = copy.deepcopy(env)
|
|
||||||
function_definition = new_env.identifiers[function_call.name.name]
|
|
||||||
assert len(function_definition.arguments) == len(function_call.arguments)
|
|
||||||
|
|
||||||
for i, argument in enumerate(function_definition.arguments):
|
|
||||||
new_env.identifiers[argument.name.name] = self.handle_expression(
|
|
||||||
env, function_call.arguments[i]
|
|
||||||
)
|
|
||||||
return self.handle_block(new_env, function_definition.block)
|
|
||||||
|
|
||||||
def handle_operation(self, env, operation):
|
|
||||||
if operation.op == parse.Operator.plus:
|
|
||||||
return self.handle_expression(env, operation.left) + self.handle_expression(
|
|
||||||
env, operation.right
|
|
||||||
)
|
|
||||||
elif operation.op == parse.Operator.minus:
|
|
||||||
return self.handle_expression(env, operation.left) - self.handle_expression(
|
|
||||||
env, operation.right
|
|
||||||
)
|
|
||||||
elif operation.op == parse.Operator.mult:
|
|
||||||
return self.handle_expression(env, operation.left) * self.handle_expression(
|
|
||||||
env, operation.right
|
|
||||||
)
|
|
||||||
elif operation.op == parse.Operator.div:
|
|
||||||
return self.handle_expression(env, operation.left) / self.handle_expression(
|
|
||||||
env, operation.right
|
|
||||||
)
|
|
||||||
|
|
||||||
def handle_expression(self, env, expression):
|
|
||||||
if type(expression.expression) == parse.LiteralInt:
|
|
||||||
return self.handle_literal_int(env, expression.expression)
|
|
||||||
elif type(expression.expression) == parse.FunctionCall:
|
|
||||||
return self.handle_function_call(env, expression.expression)
|
|
||||||
elif type(expression.expression) == parse.Identifier:
|
|
||||||
return self.handle_identifier(env, expression.expression)
|
|
||||||
elif type(expression.expression) == parse.Operation:
|
|
||||||
return self.handle_operation(env, expression.expression)
|
|
||||||
elif type(expression.expression) == parse.Expression:
|
|
||||||
return self.handle_expression(env, expression.expression)
|
|
||||||
else:
|
|
||||||
raise Exception(f"unexpected type: {type(expression.expression)}")
|
|
||||||
|
|
||||||
def handle_block(self, env, block):
|
|
||||||
return self.handle_expression(env, block.expression)
|
|
||||||
|
|
||||||
def run(self, module):
|
|
||||||
env = Environment(identifiers={})
|
|
||||||
for function in module.functions:
|
|
||||||
env.identifiers[function.name.name] = function
|
|
||||||
|
|
||||||
if "main" not in env.identifiers:
|
|
||||||
raise Exception("must have main function")
|
|
||||||
|
|
||||||
return self.handle_function_call(
|
|
||||||
env, parse.FunctionCall(name=parse.Identifier("main"), arguments=[])
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
with open(sys.argv[1]) as f:
|
|
||||||
tree = parse.boring_parser.parse(f.read())
|
|
||||||
# print(tree)
|
|
||||||
ast = parse.TreeToBoring().transform(tree)
|
|
||||||
print(ast)
|
|
||||||
result = Interpreter().run(ast)
|
|
||||||
print(result)
|
|
||||||
exit(result)
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
import sys
|
|
||||||
from typing import List
|
|
||||||
from boring.parse import boring_parser, TreeToBoring, pretty_print
|
|
||||||
from boring.type_checking import TypeChecker, Context
|
|
||||||
from boring.type_alias_resolution import TypeAliasResolver, Context as AliasContex
|
|
||||||
from boring import typedefs, parse
|
|
||||||
|
|
||||||
builtins = {
|
|
||||||
"U8": parse.PrimitiveTypeDeclaration("U8"),
|
|
||||||
"U16": parse.PrimitiveTypeDeclaration("U16"),
|
|
||||||
"U32": parse.PrimitiveTypeDeclaration("U32"),
|
|
||||||
"U64": parse.PrimitiveTypeDeclaration("U64"),
|
|
||||||
"U128": parse.PrimitiveTypeDeclaration("U128"),
|
|
||||||
"I8": parse.PrimitiveTypeDeclaration("I8"),
|
|
||||||
"I16": parse.PrimitiveTypeDeclaration("I16"),
|
|
||||||
"I32": parse.PrimitiveTypeDeclaration("I32"),
|
|
||||||
"I64": parse.PrimitiveTypeDeclaration("I64"),
|
|
||||||
"I128": parse.PrimitiveTypeDeclaration("I128"),
|
|
||||||
"F32": parse.PrimitiveTypeDeclaration("F32"),
|
|
||||||
"F64": parse.PrimitiveTypeDeclaration("F64"),
|
|
||||||
"F128": parse.PrimitiveTypeDeclaration("F128"),
|
|
||||||
"()": parse.PrimitiveTypeDeclaration("()"), # Unit
|
|
||||||
"!": parse.PrimitiveTypeDeclaration("!"), # Never
|
|
||||||
}
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
with open(sys.argv[1]) as f:
|
|
||||||
tree = boring_parser.parse(f.read())
|
|
||||||
# print(tree)
|
|
||||||
result = TreeToBoring().transform(tree)
|
|
||||||
# pretty_print(result)
|
|
||||||
alias_resolver = TypeAliasResolver()
|
|
||||||
alias_resolver.with_module(AliasContex([]), result)
|
|
||||||
# pretty_print(result)
|
|
||||||
type_checker = TypeChecker()
|
|
||||||
while type_checker.with_module(Context(builtins, None, result), result):
|
|
||||||
print("loop")
|
|
||||||
# type_checker.with_module({}, result)
|
|
||||||
pretty_print(result)
|
|
||||||
# tctb = TypeCheckTableBuilder()
|
|
||||||
# table: List[TypeComparison] = []
|
|
||||||
# tctb.with_module({}, table, result)
|
|
||||||
# for e in table:
|
|
||||||
# print(e)
|
|
||||||
# print("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^")
|
|
||||||
# check_types(table)
|
|
||||||
# print("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
|
|
||||||
# for e in table:
|
|
||||||
# print(e)
|
|
||||||
|
|
||||||
# None, Some
|
|
||||||
# None skip, set
|
|
||||||
# Some set, check
|
|
||||||
542
boring/parse.py
542
boring/parse.py
@@ -1,542 +0,0 @@
|
|||||||
import sys
|
|
||||||
import enum
|
|
||||||
from typing import Union, List, Optional, Dict
|
|
||||||
from dataclasses import dataclass, field
|
|
||||||
from lark import Lark, Transformer
|
|
||||||
|
|
||||||
|
|
||||||
def pretty_print(clas, indent=0):
|
|
||||||
print(" " * indent + type(clas).__name__ + ":")
|
|
||||||
if type(clas) == list:
|
|
||||||
for e in clas:
|
|
||||||
pretty_print(e)
|
|
||||||
return
|
|
||||||
indent += 2
|
|
||||||
for k, v in clas.__dict__.items():
|
|
||||||
if "__dict__" in dir(v):
|
|
||||||
print(" " * indent + k + ": ")
|
|
||||||
pretty_print(v, indent + 2)
|
|
||||||
elif type(v) == list:
|
|
||||||
print(" " * indent + k + ": " "[")
|
|
||||||
for e in v:
|
|
||||||
pretty_print(e, indent + 2)
|
|
||||||
print(" " * indent + "]")
|
|
||||||
else:
|
|
||||||
print(" " * indent + k + ": " + str(v))
|
|
||||||
|
|
||||||
|
|
||||||
UNIT_TYPE = "()"
|
|
||||||
NEVER_TYPE = "!"
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class FunctionTypeUsage:
|
|
||||||
arguments: List["TypeUsage"]
|
|
||||||
return_type: "TypeUsage"
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class DataTypeUsage:
|
|
||||||
name: str
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class UnknownTypeUsage:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
TypeUsage = Union[FunctionTypeUsage, DataTypeUsage, UnknownTypeUsage]
|
|
||||||
|
|
||||||
|
|
||||||
class Operator(enum.Enum):
|
|
||||||
mult = "mult"
|
|
||||||
div = "div"
|
|
||||||
plus = "plus"
|
|
||||||
minus = "minus"
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class LiteralInt:
|
|
||||||
value: int
|
|
||||||
type: TypeUsage
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class LiteralFloat:
|
|
||||||
value: float
|
|
||||||
type: TypeUsage
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class LiteralStruct:
|
|
||||||
fields: Dict[str, "Expression"]
|
|
||||||
type: TypeUsage
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class FunctionCall:
|
|
||||||
source: "Expression"
|
|
||||||
arguments: List["Expression"]
|
|
||||||
type: TypeUsage
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class StructGetter:
|
|
||||||
source: "Expression"
|
|
||||||
attribute: str
|
|
||||||
type: TypeUsage
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Operation:
|
|
||||||
left: "Expression"
|
|
||||||
op: Operator
|
|
||||||
right: "Expression"
|
|
||||||
type: TypeUsage
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class VariableUsage:
|
|
||||||
name: str
|
|
||||||
type: TypeUsage
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ReturnStatement:
|
|
||||||
source: "Expression"
|
|
||||||
type: TypeUsage
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Expression:
|
|
||||||
expression: Union[
|
|
||||||
LiteralInt,
|
|
||||||
LiteralFloat,
|
|
||||||
LiteralStruct,
|
|
||||||
FunctionCall,
|
|
||||||
StructGetter,
|
|
||||||
"Block",
|
|
||||||
ReturnStatement,
|
|
||||||
VariableUsage,
|
|
||||||
Operation,
|
|
||||||
]
|
|
||||||
type: TypeUsage
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class LetStatement:
|
|
||||||
variable_name: str
|
|
||||||
type: TypeUsage
|
|
||||||
expression: Expression
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class AssignmentStatement:
|
|
||||||
source: Union[VariableUsage, StructGetter]
|
|
||||||
type: TypeUsage
|
|
||||||
expression: Expression
|
|
||||||
|
|
||||||
|
|
||||||
Statement = Union[LetStatement, AssignmentStatement, Expression]
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Block:
|
|
||||||
statements: List[Statement]
|
|
||||||
type: TypeUsage
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class VariableDeclaration:
|
|
||||||
name: str
|
|
||||||
type: TypeUsage
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Function:
|
|
||||||
declaration: "FunctionDeclaration"
|
|
||||||
block: Block
|
|
||||||
|
|
||||||
@property
|
|
||||||
def type(self):
|
|
||||||
return self.declaration.type
|
|
||||||
|
|
||||||
@type.setter
|
|
||||||
def type(self, value):
|
|
||||||
self.declaration.type = value
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class FunctionDeclaration:
|
|
||||||
name: str
|
|
||||||
arguments: List[VariableDeclaration]
|
|
||||||
return_type: TypeUsage
|
|
||||||
type: TypeUsage
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class PrimitiveTypeDeclaration:
|
|
||||||
name: str
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class StructTypeDeclaration:
|
|
||||||
name: str
|
|
||||||
fields: Dict[str, TypeUsage]
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class AliasTypeDeclaration:
|
|
||||||
new: DataTypeUsage
|
|
||||||
old: TypeUsage
|
|
||||||
|
|
||||||
|
|
||||||
TypeDeclaration = Union[
|
|
||||||
StructTypeDeclaration, PrimitiveTypeDeclaration, AliasTypeDeclaration
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Impl:
|
|
||||||
struct: str
|
|
||||||
functions: List[Function]
|
|
||||||
|
|
||||||
|
|
||||||
TraitItem = Union[FunctionDeclaration, Function]
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class TraitTypeDeclaration:
|
|
||||||
struct: str
|
|
||||||
items: List[TraitItem]
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class TraitImpl:
|
|
||||||
struct: str
|
|
||||||
trait: str
|
|
||||||
functions: List[Function]
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Module:
|
|
||||||
functions: List[Function]
|
|
||||||
types: List[TypeDeclaration]
|
|
||||||
impls: List[Impl]
|
|
||||||
|
|
||||||
|
|
||||||
boring_grammar = r"""
|
|
||||||
plus : "+"
|
|
||||||
minus : "-"
|
|
||||||
mult : "*"
|
|
||||||
div : "/"
|
|
||||||
|
|
||||||
identifier : CNAME
|
|
||||||
literal_float : SIGNED_FLOAT
|
|
||||||
literal_int : SIGNED_INT
|
|
||||||
|
|
||||||
literal_struct_field : identifier ":" expression
|
|
||||||
literal_struct : data_type "{" (literal_struct_field ",")* "}"
|
|
||||||
|
|
||||||
function_call : expression "(" [expression ("," expression)*] ")"
|
|
||||||
|
|
||||||
struct_getter : expression "." identifier
|
|
||||||
|
|
||||||
add_expression : expression plus factor
|
|
||||||
sub_expression : expression minus factor
|
|
||||||
mult_expression : expression mult term
|
|
||||||
div_expression : expression div term
|
|
||||||
|
|
||||||
variable_usage : identifier
|
|
||||||
|
|
||||||
return_statement : "return" expression ";"
|
|
||||||
|
|
||||||
expression : add_expression
|
|
||||||
| sub_expression
|
|
||||||
| factor
|
|
||||||
|
|
||||||
factor : mult_expression
|
|
||||||
| div_expression
|
|
||||||
| term
|
|
||||||
|
|
||||||
term : literal_int
|
|
||||||
| literal_float
|
|
||||||
| literal_struct
|
|
||||||
| variable_usage
|
|
||||||
| function_call
|
|
||||||
| struct_getter
|
|
||||||
| "(" expression ")"
|
|
||||||
| block
|
|
||||||
|
|
||||||
let_statement : "let" identifier "=" expression ";"
|
|
||||||
| "let" identifier ":" type_usage "=" expression ";"
|
|
||||||
|
|
||||||
assignment_statement : variable_usage "=" expression ";"
|
|
||||||
| struct_getter "=" expression ";"
|
|
||||||
|
|
||||||
statement : let_statement
|
|
||||||
| assignment_statement
|
|
||||||
| return_statement
|
|
||||||
| expression
|
|
||||||
|
|
||||||
block : "{" (statement)* "}"
|
|
||||||
|
|
||||||
data_type : identifier
|
|
||||||
|
|
||||||
function_type : "fn" "(" (type_usage)* ")"
|
|
||||||
|
|
||||||
function_type_with_return : "fn" "(" (type_usage)* ")" ":" type_usage
|
|
||||||
|
|
||||||
type_usage : data_type
|
|
||||||
| function_type
|
|
||||||
| function_type_with_return
|
|
||||||
|
|
||||||
variable_declaration : identifier ":" type_usage
|
|
||||||
|
|
||||||
function_declaration_without_return : "fn" identifier "(" [variable_declaration ("," variable_declaration)*] ")"
|
|
||||||
|
|
||||||
function_declaration_with_return : "fn" identifier "(" [variable_declaration ("," variable_declaration)*] ")" ":" type_usage
|
|
||||||
|
|
||||||
function_declaration : function_declaration_with_return
|
|
||||||
| function_declaration_without_return
|
|
||||||
|
|
||||||
function : function_declaration block
|
|
||||||
|
|
||||||
struct_definition_field : identifier ":" type_usage
|
|
||||||
|
|
||||||
struct_type_declaration : "type" identifier "struct" "{" (struct_definition_field ",")* "}"
|
|
||||||
|
|
||||||
type_alias_declaration : "type" identifier "=" type_usage ";"
|
|
||||||
|
|
||||||
|
|
||||||
trait_item : function_declaration ";"
|
|
||||||
| function
|
|
||||||
|
|
||||||
trait_declaration : "type" identifier "trait" "{" trait_item* "}"
|
|
||||||
|
|
||||||
type_declaration : struct_type_declaration
|
|
||||||
| type_alias_declaration
|
|
||||||
| trait_declaration
|
|
||||||
|
|
||||||
impl : "impl" identifier "{" function* "}"
|
|
||||||
| "impl" identifier "for" identifier "{" function* "}"
|
|
||||||
|
|
||||||
module : (function|type_declaration|impl)*
|
|
||||||
|
|
||||||
%import common.CNAME
|
|
||||||
%import common.SIGNED_INT
|
|
||||||
%import common.SIGNED_FLOAT
|
|
||||||
%import common.WS
|
|
||||||
%import common.CPP_COMMENT
|
|
||||||
%ignore WS
|
|
||||||
%ignore CPP_COMMENT
|
|
||||||
"""
|
|
||||||
|
|
||||||
next_sub_id = 0
|
|
||||||
|
|
||||||
|
|
||||||
class TreeToBoring(Transformer):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def plus(self, p) -> Operator:
|
|
||||||
return Operator.plus
|
|
||||||
|
|
||||||
def minus(self, m) -> Operator:
|
|
||||||
return Operator.minus
|
|
||||||
|
|
||||||
def mult(self, m) -> Operator:
|
|
||||||
return Operator.mult
|
|
||||||
|
|
||||||
def div(self, d) -> Operator:
|
|
||||||
return Operator.div
|
|
||||||
|
|
||||||
def literal_int(self, n) -> LiteralInt:
|
|
||||||
(n,) = n
|
|
||||||
return LiteralInt(value=int(n), type=UnknownTypeUsage())
|
|
||||||
|
|
||||||
def literal_float(self, f) -> LiteralFloat:
|
|
||||||
(f,) = f
|
|
||||||
return LiteralFloat(value=float(f), type=UnknownTypeUsage())
|
|
||||||
|
|
||||||
def literal_struct_field(self, lsf):
|
|
||||||
(name, expression) = lsf
|
|
||||||
return name, expression
|
|
||||||
|
|
||||||
def literal_struct(self, literal_struct) -> LiteralStruct:
|
|
||||||
data_type = literal_struct[0]
|
|
||||||
fields = {key: value for (key, value) in literal_struct[1:]}
|
|
||||||
return LiteralStruct(fields=fields, type=data_type)
|
|
||||||
|
|
||||||
def identifier(self, i) -> str:
|
|
||||||
(i,) = i
|
|
||||||
return str(i)
|
|
||||||
|
|
||||||
def variable_usage(self, variable) -> VariableUsage:
|
|
||||||
(variable,) = variable
|
|
||||||
return VariableUsage(name=variable, type=UnknownTypeUsage())
|
|
||||||
|
|
||||||
def return_statement(self, return_expression) -> ReturnStatement:
|
|
||||||
(return_expression,) = return_expression
|
|
||||||
return ReturnStatement(
|
|
||||||
source=return_expression, type=DataTypeUsage(name=NEVER_TYPE)
|
|
||||||
)
|
|
||||||
|
|
||||||
def function_call(self, call) -> FunctionCall:
|
|
||||||
return FunctionCall(source=call[0], arguments=call[1:], type=UnknownTypeUsage())
|
|
||||||
|
|
||||||
def struct_getter(self, getter) -> StructGetter:
|
|
||||||
expression, attribute = getter
|
|
||||||
return StructGetter(expression, attribute, UnknownTypeUsage())
|
|
||||||
|
|
||||||
def add_expression(self, ae) -> Operation:
|
|
||||||
return Operation(left=ae[0], op=ae[1], right=ae[2], type=UnknownTypeUsage())
|
|
||||||
|
|
||||||
def sub_expression(self, se) -> Operation:
|
|
||||||
return Operation(left=se[0], op=se[1], right=se[2], type=UnknownTypeUsage())
|
|
||||||
|
|
||||||
def mult_expression(self, se) -> Operation:
|
|
||||||
return Operation(left=se[0], op=se[1], right=se[2], type=UnknownTypeUsage())
|
|
||||||
|
|
||||||
def div_expression(self, se) -> Operation:
|
|
||||||
return Operation(left=se[0], op=se[1], right=se[2], type=UnknownTypeUsage())
|
|
||||||
|
|
||||||
def expression(self, exp) -> Expression:
|
|
||||||
(exp,) = exp
|
|
||||||
if isinstance(exp, Expression):
|
|
||||||
return exp
|
|
||||||
return Expression(expression=exp, type=UnknownTypeUsage())
|
|
||||||
|
|
||||||
def factor(self, factor) -> Expression:
|
|
||||||
(factor,) = factor
|
|
||||||
if isinstance(factor, Expression):
|
|
||||||
return factor
|
|
||||||
return Expression(expression=factor, type=UnknownTypeUsage())
|
|
||||||
|
|
||||||
def term(self, term) -> Expression:
|
|
||||||
(term,) = term
|
|
||||||
return Expression(expression=term, type=UnknownTypeUsage())
|
|
||||||
|
|
||||||
def let_statement(self, let_statement) -> LetStatement:
|
|
||||||
if len(let_statement) == 3:
|
|
||||||
(variable_name, type_usage, expression) = let_statement
|
|
||||||
return LetStatement(
|
|
||||||
variable_name=variable_name,
|
|
||||||
type=type_usage,
|
|
||||||
expression=expression,
|
|
||||||
)
|
|
||||||
(variable_name, expression) = let_statement
|
|
||||||
return LetStatement(
|
|
||||||
variable_name=variable_name,
|
|
||||||
type=UnknownTypeUsage(),
|
|
||||||
expression=expression,
|
|
||||||
)
|
|
||||||
|
|
||||||
def assignment_statement(self, assignment_statement) -> AssignmentStatement:
|
|
||||||
(source, expression) = assignment_statement
|
|
||||||
return AssignmentStatement(
|
|
||||||
source=source,
|
|
||||||
type=UnknownTypeUsage(),
|
|
||||||
expression=expression,
|
|
||||||
)
|
|
||||||
|
|
||||||
def statement(self, statement):
|
|
||||||
(statement,) = statement
|
|
||||||
return statement
|
|
||||||
|
|
||||||
def block(self, block) -> Block:
|
|
||||||
return Block(statements=block, type=UnknownTypeUsage())
|
|
||||||
|
|
||||||
def data_type(self, name) -> TypeUsage:
|
|
||||||
(name,) = name
|
|
||||||
return DataTypeUsage(name=name)
|
|
||||||
|
|
||||||
def function_type(self, type_usage) -> TypeUsage:
|
|
||||||
return FunctionTypeUsage(
|
|
||||||
arguments=type_usage,
|
|
||||||
return_type=DataTypeUsage(name=UNIT_TYPE),
|
|
||||||
)
|
|
||||||
|
|
||||||
def function_type_with_return(self, type_usage) -> TypeUsage:
|
|
||||||
return FunctionTypeUsage(arguments=type_usage[0:-1], return_type=type_usage[-1])
|
|
||||||
|
|
||||||
def type_usage(self, type_usage):
|
|
||||||
(type_usage,) = type_usage
|
|
||||||
return type_usage
|
|
||||||
|
|
||||||
def variable_declaration(self, identifier) -> VariableDeclaration:
|
|
||||||
(identifier, type_usage) = identifier
|
|
||||||
return VariableDeclaration(name=identifier, type=type_usage)
|
|
||||||
|
|
||||||
def function_declaration_without_return(self, fdwr) -> FunctionDeclaration:
|
|
||||||
return FunctionDeclaration(
|
|
||||||
name=fdwr[0],
|
|
||||||
arguments=fdwr[1:],
|
|
||||||
return_type=DataTypeUsage(name=UNIT_TYPE),
|
|
||||||
type=FunctionTypeUsage(
|
|
||||||
arguments=[arg.type for arg in fdwr[1:]],
|
|
||||||
return_type=DataTypeUsage(name=UNIT_TYPE),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
def function_declaration_with_return(self, fdwr) -> FunctionDeclaration:
|
|
||||||
return FunctionDeclaration(
|
|
||||||
name=fdwr[0],
|
|
||||||
arguments=fdwr[1:-1],
|
|
||||||
return_type=fdwr[-1],
|
|
||||||
type=FunctionTypeUsage(
|
|
||||||
arguments=[arg.type for arg in fdwr[1:-1]], return_type=fdwr[-1]
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
def function_declaration(self, fd) -> FunctionDeclaration:
|
|
||||||
(fd,) = fd
|
|
||||||
assert isinstance(fd, FunctionDeclaration)
|
|
||||||
return fd
|
|
||||||
|
|
||||||
def function(self, function):
|
|
||||||
(declaration, block) = function
|
|
||||||
return Function(declaration, block)
|
|
||||||
|
|
||||||
def struct_definition_field(self, struct_definition_field):
|
|
||||||
(field, type_usage) = struct_definition_field
|
|
||||||
return (field, type_usage)
|
|
||||||
|
|
||||||
def struct_type_declaration(self, struct_type_declaration) -> StructTypeDeclaration:
|
|
||||||
name = struct_type_declaration[0]
|
|
||||||
fields = {key: value for (key, value) in struct_type_declaration[1:]}
|
|
||||||
return StructTypeDeclaration(name=name, fields=fields)
|
|
||||||
|
|
||||||
def type_alias_declaration(self, type_alias_declaration) -> AliasTypeDeclaration:
|
|
||||||
(name, existing) = type_alias_declaration
|
|
||||||
return AliasTypeDeclaration(new=DataTypeUsage(name), old=type_alias_declaration)
|
|
||||||
|
|
||||||
def type_declaration(self, type_declaration):
|
|
||||||
(type_declaration,) = type_declaration
|
|
||||||
return type_declaration
|
|
||||||
|
|
||||||
def impl(self, impl) -> Impl:
|
|
||||||
return Impl(struct=impl[0], functions=impl[1:])
|
|
||||||
|
|
||||||
def module(self, module_items) -> Module:
|
|
||||||
functions = []
|
|
||||||
types = []
|
|
||||||
impls = []
|
|
||||||
for item in module_items:
|
|
||||||
if isinstance(item, Function):
|
|
||||||
functions.append(item)
|
|
||||||
elif isinstance(item, Impl):
|
|
||||||
impls.append(item)
|
|
||||||
else:
|
|
||||||
types.append(item)
|
|
||||||
return Module(functions=functions, types=types, impls=impls)
|
|
||||||
|
|
||||||
|
|
||||||
boring_parser = Lark(boring_grammar, start="module", lexer="standard")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
with open(sys.argv[1]) as f:
|
|
||||||
tree = boring_parser.parse(f.read())
|
|
||||||
# print(tree)
|
|
||||||
result = TreeToBoring().transform(tree)
|
|
||||||
pretty_print(result)
|
|
||||||
@@ -1,179 +0,0 @@
|
|||||||
from dataclasses import dataclass
|
|
||||||
from typing import List, Dict, Optional, Union, Tuple
|
|
||||||
from boring import parse
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Context:
|
|
||||||
type_aliases: List[parse.AliasTypeDeclaration]
|
|
||||||
|
|
||||||
def copy(self):
|
|
||||||
return Context(self.type_aliases.copy())
|
|
||||||
|
|
||||||
|
|
||||||
def resolve_type(ctx: Context, type: parse.DataTypeUsage) -> parse.TypeUsage:
|
|
||||||
changed = True
|
|
||||||
result: parse.TypeUsage = type
|
|
||||||
while changed:
|
|
||||||
changed = False
|
|
||||||
if isinstance(result, parse.DataTypeUsage):
|
|
||||||
for type_alias in ctx.type_aliases:
|
|
||||||
if type_alias.new.name == result.name: # type: ignore
|
|
||||||
result = type_alias.old
|
|
||||||
changed = True
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def process_type(ctx: Context, type: parse.TypeUsage) -> parse.TypeUsage:
|
|
||||||
if isinstance(type, parse.DataTypeUsage):
|
|
||||||
return resolve_type(ctx, type)
|
|
||||||
elif isinstance(type, parse.FunctionTypeUsage):
|
|
||||||
return parse.FunctionTypeUsage(
|
|
||||||
return_type=process_type(ctx, type.return_type),
|
|
||||||
arguments=[process_type(ctx, argument) for argument in type.arguments],
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return type
|
|
||||||
|
|
||||||
|
|
||||||
class TypeAliasResolver:
|
|
||||||
def with_module(self, ctx: Context, module: parse.Module):
|
|
||||||
for type_declaration in module.types:
|
|
||||||
if isinstance(type_declaration, parse.AliasTypeDeclaration):
|
|
||||||
ctx.type_aliases.append(type_declaration)
|
|
||||||
|
|
||||||
for type_declaration in module.types:
|
|
||||||
if isinstance(type_declaration, parse.StructTypeDeclaration):
|
|
||||||
for field in type_declaration.fields:
|
|
||||||
type_declaration.fields[field] = process_type(
|
|
||||||
ctx, type_declaration.fields[field]
|
|
||||||
)
|
|
||||||
|
|
||||||
for impl in module.impls:
|
|
||||||
impl_ctx = ctx.copy()
|
|
||||||
impl_ctx.type_aliases.append(
|
|
||||||
parse.AliasTypeDeclaration(
|
|
||||||
new=parse.DataTypeUsage("Self"),
|
|
||||||
old=parse.DataTypeUsage(impl.struct),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
for function in impl.functions:
|
|
||||||
self.with_function(impl_ctx, function)
|
|
||||||
|
|
||||||
for function in module.functions:
|
|
||||||
self.with_function(ctx, function)
|
|
||||||
return
|
|
||||||
|
|
||||||
def with_function(self, ctx: Context, function: parse.Function):
|
|
||||||
for argument in function.declaration.arguments:
|
|
||||||
argument.type = process_type(ctx, argument.type)
|
|
||||||
function.declaration.return_type = process_type(ctx, function.declaration.return_type)
|
|
||||||
function.declaration.type = process_type(ctx, function.declaration.type)
|
|
||||||
|
|
||||||
self.with_block(ctx, function.block)
|
|
||||||
return
|
|
||||||
|
|
||||||
# Skip variable VariableDeclaration
|
|
||||||
|
|
||||||
def with_block(self, ctx: Context, block: parse.Block):
|
|
||||||
for statement in block.statements:
|
|
||||||
self.with_statement(ctx, statement)
|
|
||||||
block.type = process_type(ctx, block.type)
|
|
||||||
|
|
||||||
def with_statement(self, ctx: Context, statement: parse.Statement):
|
|
||||||
if isinstance(statement, parse.ReturnStatement):
|
|
||||||
return self.with_return_statement(ctx, statement)
|
|
||||||
elif isinstance(statement, parse.LetStatement):
|
|
||||||
return self.with_let_statement(ctx, statement)
|
|
||||||
elif isinstance(statement, parse.AssignmentStatement):
|
|
||||||
return self.with_assignment_statement(ctx, statement)
|
|
||||||
elif isinstance(statement, parse.Expression): # expression
|
|
||||||
return self.with_expression(ctx, statement)
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
def with_let_statement(self, ctx: Context, let_statement: parse.LetStatement):
|
|
||||||
self.with_expression(ctx, let_statement.expression)
|
|
||||||
let_statement.type = process_type(ctx, let_statement.type)
|
|
||||||
|
|
||||||
def with_assignment_statement(
|
|
||||||
self, ctx: Context, assignment_statement: parse.AssignmentStatement
|
|
||||||
):
|
|
||||||
self.with_expression(ctx, assignment_statement.expression)
|
|
||||||
|
|
||||||
if isinstance(assignment_statement.source, parse.VariableUsage):
|
|
||||||
self.with_variable_usage(ctx, assignment_statement.source)
|
|
||||||
elif isinstance(assignment_statement.source, parse.StructGetter):
|
|
||||||
self.with_struct_getter(ctx, assignment_statement.source)
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
assignment_statement.type = process_type(ctx, assignment_statement.type)
|
|
||||||
return
|
|
||||||
|
|
||||||
def with_return_statement(
|
|
||||||
self, ctx: Context, return_statement: parse.ReturnStatement
|
|
||||||
):
|
|
||||||
self.with_expression(ctx, return_statement.source)
|
|
||||||
return_statement.type = process_type(ctx, return_statement.type)
|
|
||||||
return
|
|
||||||
|
|
||||||
def with_expression(self, ctx: Context, expression: parse.Expression):
|
|
||||||
subexpression = expression.expression
|
|
||||||
expression.type = process_type(ctx, expression.type)
|
|
||||||
|
|
||||||
if isinstance(subexpression, parse.LiteralInt):
|
|
||||||
self.with_literal_int(ctx, subexpression)
|
|
||||||
elif isinstance(subexpression, parse.LiteralFloat):
|
|
||||||
self.with_literal_float(ctx, subexpression)
|
|
||||||
elif isinstance(subexpression, parse.LiteralStruct):
|
|
||||||
self.with_literal_struct(ctx, subexpression)
|
|
||||||
elif isinstance(subexpression, parse.FunctionCall):
|
|
||||||
self.with_function_call(ctx, subexpression)
|
|
||||||
elif isinstance(subexpression, parse.StructGetter):
|
|
||||||
self.with_struct_getter(ctx, subexpression)
|
|
||||||
elif isinstance(subexpression, parse.Block):
|
|
||||||
self.with_block(ctx, subexpression)
|
|
||||||
elif isinstance(subexpression, parse.VariableUsage):
|
|
||||||
self.with_variable_usage(ctx, subexpression)
|
|
||||||
elif isinstance(subexpression, parse.Operation):
|
|
||||||
self.with_operation(ctx, subexpression)
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
return
|
|
||||||
|
|
||||||
def with_variable_usage(self, ctx: Context, variable_usage: parse.VariableUsage):
|
|
||||||
variable_usage.type = process_type(ctx, variable_usage.type)
|
|
||||||
|
|
||||||
def with_operation(self, ctx: Context, operation: parse.Operation):
|
|
||||||
self.with_expression(ctx, operation.left)
|
|
||||||
self.with_expression(ctx, operation.right)
|
|
||||||
operation.type = process_type(ctx, operation.type)
|
|
||||||
return
|
|
||||||
|
|
||||||
def with_function_call(self, ctx: Context, function_call: parse.FunctionCall):
|
|
||||||
self.with_expression(ctx, function_call.source)
|
|
||||||
for argument in function_call.arguments:
|
|
||||||
self.with_expression(ctx, argument)
|
|
||||||
function_call.type = process_type(ctx, function_call.type)
|
|
||||||
return
|
|
||||||
|
|
||||||
def with_struct_getter(self, ctx: Context, struct_getter: parse.StructGetter):
|
|
||||||
self.with_expression(ctx, struct_getter.source)
|
|
||||||
struct_getter.type = process_type(ctx, struct_getter.type)
|
|
||||||
return
|
|
||||||
|
|
||||||
def with_literal_float(self, ctx: Context, literal_float: parse.LiteralFloat):
|
|
||||||
literal_float.type = process_type(ctx, literal_float.type)
|
|
||||||
return
|
|
||||||
|
|
||||||
def with_literal_int(self, ctx: Context, literal_int: parse.LiteralInt):
|
|
||||||
literal_int.type = process_type(ctx, literal_int.type)
|
|
||||||
return
|
|
||||||
|
|
||||||
def with_literal_struct(self, ctx: Context, literal_struct: parse.LiteralStruct):
|
|
||||||
for name, expression in literal_struct.fields.items():
|
|
||||||
self.with_expression(ctx, expression)
|
|
||||||
literal_struct.type = process_type(ctx, literal_struct.type)
|
|
||||||
@@ -1,407 +0,0 @@
|
|||||||
from dataclasses import dataclass
|
|
||||||
from typing import List, Dict, Optional, Union, Tuple
|
|
||||||
|
|
||||||
|
|
||||||
from boring import parse, typedefs
|
|
||||||
|
|
||||||
|
|
||||||
Identified = Union[
|
|
||||||
parse.LetStatement, parse.Function, parse.VariableDeclaration, parse.TypeDeclaration
|
|
||||||
]
|
|
||||||
Environment = Dict[str, Identified]
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Context:
|
|
||||||
environment: Environment
|
|
||||||
current_function: Optional[parse.Function]
|
|
||||||
current_module: parse.Module
|
|
||||||
|
|
||||||
def copy(self):
|
|
||||||
return Context(
|
|
||||||
self.environment.copy(), self.current_function, self.current_module
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def unify(ctx: Context, first, second) -> bool:
|
|
||||||
changed: bool
|
|
||||||
result, changed = type_compare(ctx, first.type, second.type)
|
|
||||||
first.type = result
|
|
||||||
second.type = result
|
|
||||||
return changed
|
|
||||||
|
|
||||||
|
|
||||||
def function_to_method(type: parse.FunctionTypeUsage) -> parse.FunctionTypeUsage:
|
|
||||||
return parse.FunctionTypeUsage(type.arguments[1:], type.return_type)
|
|
||||||
|
|
||||||
|
|
||||||
def type_compare(
|
|
||||||
ctx: Context, first: parse.TypeUsage, second: parse.TypeUsage
|
|
||||||
) -> Tuple[parse.TypeUsage, bool]:
|
|
||||||
print(first, second)
|
|
||||||
if isinstance(first, parse.UnknownTypeUsage):
|
|
||||||
if not isinstance(second, parse.UnknownTypeUsage):
|
|
||||||
return second, True
|
|
||||||
else:
|
|
||||||
return parse.UnknownTypeUsage(), False
|
|
||||||
else:
|
|
||||||
if isinstance(second, parse.UnknownTypeUsage):
|
|
||||||
return first, True
|
|
||||||
else:
|
|
||||||
if isinstance(first, parse.DataTypeUsage) and isinstance(
|
|
||||||
second, parse.DataTypeUsage
|
|
||||||
):
|
|
||||||
assert second == first
|
|
||||||
assert first.name in ctx.environment # TODO: validate that it is a type
|
|
||||||
assert isinstance(
|
|
||||||
ctx.environment[first.name], parse.StructTypeDeclaration
|
|
||||||
) or isinstance(
|
|
||||||
ctx.environment[first.name], parse.PrimitiveTypeDeclaration
|
|
||||||
)
|
|
||||||
assert second.name in ctx.environment
|
|
||||||
assert isinstance(
|
|
||||||
ctx.environment[second.name], parse.StructTypeDeclaration
|
|
||||||
) or isinstance(
|
|
||||||
ctx.environment[second.name], parse.PrimitiveTypeDeclaration
|
|
||||||
)
|
|
||||||
return first, False
|
|
||||||
elif isinstance(first, parse.FunctionTypeUsage) and isinstance(
|
|
||||||
second, parse.FunctionTypeUsage
|
|
||||||
):
|
|
||||||
return_type, changed = type_compare(
|
|
||||||
ctx, first.return_type, second.return_type
|
|
||||||
)
|
|
||||||
arguments = []
|
|
||||||
assert len(first.arguments) == len(second.arguments)
|
|
||||||
for first_arg, second_arg in zip(first.arguments, second.arguments):
|
|
||||||
argument_type, argument_changed = type_compare(
|
|
||||||
ctx, first_arg, second_arg
|
|
||||||
)
|
|
||||||
arguments.append(argument_type)
|
|
||||||
if argument_changed:
|
|
||||||
changed = True
|
|
||||||
return parse.FunctionTypeUsage(arguments, return_type), changed
|
|
||||||
else:
|
|
||||||
assert False, f"mismatched types {first}, {second}"
|
|
||||||
|
|
||||||
|
|
||||||
def assert_exists(ctx: Context, type: parse.TypeUsage):
|
|
||||||
if isinstance(type, parse.DataTypeUsage):
|
|
||||||
assert type.name in ctx.environment
|
|
||||||
elif isinstance(type, parse.FunctionTypeUsage):
|
|
||||||
assert_exists(ctx, type.return_type)
|
|
||||||
for argument in type.arguments:
|
|
||||||
assert_exists(ctx, argument)
|
|
||||||
|
|
||||||
|
|
||||||
class TypeChecker:
|
|
||||||
def with_module(self, ctx: Context, module: parse.Module) -> bool:
|
|
||||||
for type_declaration in module.types:
|
|
||||||
if isinstance(type_declaration, parse.StructTypeDeclaration):
|
|
||||||
ctx.environment[type_declaration.name] = type_declaration
|
|
||||||
for type_declaration in module.types:
|
|
||||||
if isinstance(type_declaration, parse.StructTypeDeclaration):
|
|
||||||
for name, field in type_declaration.fields.items():
|
|
||||||
assert_exists(ctx, field)
|
|
||||||
for function in module.functions:
|
|
||||||
ctx.environment[function.declaration.name] = function
|
|
||||||
|
|
||||||
changed = False
|
|
||||||
for impl in module.impls:
|
|
||||||
for function in impl.functions:
|
|
||||||
if self.with_function(ctx, function):
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
for function in module.functions:
|
|
||||||
if self.with_function(ctx, function):
|
|
||||||
changed = True
|
|
||||||
return changed
|
|
||||||
|
|
||||||
def with_function(self, ctx: Context, function: parse.Function) -> bool:
|
|
||||||
function_ctx = ctx.copy()
|
|
||||||
function_ctx.current_function = function
|
|
||||||
for argument in function.declaration.arguments:
|
|
||||||
function_ctx.environment[argument.name] = argument
|
|
||||||
assert isinstance(function.declaration.type, parse.FunctionTypeUsage)
|
|
||||||
|
|
||||||
changed = self.with_block(function_ctx, function.block)
|
|
||||||
|
|
||||||
if not (
|
|
||||||
isinstance(function.block.type, parse.DataTypeUsage)
|
|
||||||
and function.block.type.name == parse.NEVER_TYPE
|
|
||||||
):
|
|
||||||
type, compare_changed = type_compare(
|
|
||||||
function_ctx, function.block.type, function.declaration.type.return_type
|
|
||||||
)
|
|
||||||
function.block.type = type
|
|
||||||
function.declaration.type.return_type = type
|
|
||||||
if compare_changed is True:
|
|
||||||
changed = True
|
|
||||||
return changed
|
|
||||||
|
|
||||||
# Skip variable VariableDeclaration
|
|
||||||
|
|
||||||
def with_block(self, ctx: Context, block: parse.Block) -> bool:
|
|
||||||
block_ctx = ctx.copy()
|
|
||||||
# if parent is void, must be statement
|
|
||||||
# if parent is type, must be expression
|
|
||||||
changed = False
|
|
||||||
for statement in block.statements:
|
|
||||||
if self.with_statement(block_ctx, statement):
|
|
||||||
changed = True
|
|
||||||
final = block.statements[-1]
|
|
||||||
if isinstance(final, parse.LetStatement):
|
|
||||||
if isinstance(block.type, parse.UnknownTypeUsage):
|
|
||||||
changed = True
|
|
||||||
block.type = parse.DataTypeUsage(name=parse.UNIT_TYPE)
|
|
||||||
else:
|
|
||||||
assert block.type == parse.DataTypeUsage(name=parse.UNIT_TYPE)
|
|
||||||
elif isinstance(final, parse.ReturnStatement):
|
|
||||||
if isinstance(block.type, parse.UnknownTypeUsage):
|
|
||||||
changed = True
|
|
||||||
block.type = parse.DataTypeUsage(name=parse.NEVER_TYPE)
|
|
||||||
else:
|
|
||||||
assert block.type == parse.DataTypeUsage(name=parse.NEVER_TYPE)
|
|
||||||
elif isinstance(final, parse.Expression):
|
|
||||||
if unify(block_ctx, final, block):
|
|
||||||
changed = True
|
|
||||||
return changed
|
|
||||||
|
|
||||||
def with_statement(self, ctx: Context, statement: parse.Statement) -> bool:
|
|
||||||
if isinstance(statement, parse.ReturnStatement):
|
|
||||||
return self.with_return_statement(ctx, statement)
|
|
||||||
elif isinstance(statement, parse.LetStatement):
|
|
||||||
return self.with_let_statement(ctx, statement)
|
|
||||||
elif isinstance(statement, parse.AssignmentStatement):
|
|
||||||
return self.with_assignment_statement(ctx, statement)
|
|
||||||
elif isinstance(statement, parse.Expression): # expression
|
|
||||||
return self.with_expression(ctx, statement)
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
def with_let_statement(
|
|
||||||
self, ctx: Context, let_statement: parse.LetStatement
|
|
||||||
) -> bool:
|
|
||||||
changed = False
|
|
||||||
ctx.environment[let_statement.variable_name] = let_statement
|
|
||||||
if self.with_expression(ctx, let_statement.expression):
|
|
||||||
changed = True
|
|
||||||
if unify(ctx, let_statement, let_statement.expression):
|
|
||||||
changed = True
|
|
||||||
return changed
|
|
||||||
|
|
||||||
def with_assignment_statement(
|
|
||||||
self, ctx: Context, assignment_statement: parse.AssignmentStatement
|
|
||||||
) -> bool:
|
|
||||||
changed = False
|
|
||||||
if self.with_expression(ctx, assignment_statement.expression):
|
|
||||||
changed = True
|
|
||||||
if isinstance(assignment_statement.source, parse.VariableUsage):
|
|
||||||
self.with_variable_usage(ctx, assignment_statement.source)
|
|
||||||
elif isinstance(assignment_statement.source, parse.StructGetter):
|
|
||||||
self.with_struct_getter(ctx, assignment_statement.source)
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
if unify(
|
|
||||||
ctx,
|
|
||||||
assignment_statement,
|
|
||||||
assignment_statement.source,
|
|
||||||
):
|
|
||||||
changed = True
|
|
||||||
if unify(ctx, assignment_statement, assignment_statement.expression):
|
|
||||||
changed = True
|
|
||||||
return changed
|
|
||||||
|
|
||||||
def with_return_statement(
|
|
||||||
self, ctx: Context, return_statement: parse.ReturnStatement
|
|
||||||
) -> bool:
|
|
||||||
changed = self.with_expression(ctx, return_statement.source)
|
|
||||||
|
|
||||||
# Doesn't match on an unreachable return
|
|
||||||
if not (
|
|
||||||
isinstance(return_statement.source.type, parse.DataTypeUsage)
|
|
||||||
and return_statement.source.type.name == parse.NEVER_TYPE
|
|
||||||
):
|
|
||||||
assert isinstance(ctx.current_function, parse.Function)
|
|
||||||
assert isinstance(ctx.current_function.declaration.type, parse.FunctionTypeUsage)
|
|
||||||
type, compare_changed = type_compare(
|
|
||||||
ctx, return_statement.source.type, ctx.current_function.declaration.type.return_type
|
|
||||||
)
|
|
||||||
return_statement.source.type = type
|
|
||||||
ctx.current_function.declaration.type.return_type = type
|
|
||||||
if compare_changed is True:
|
|
||||||
changed = True
|
|
||||||
return changed
|
|
||||||
|
|
||||||
def with_expression(self, ctx: Context, expression: parse.Expression) -> bool:
|
|
||||||
subexpression = expression.expression
|
|
||||||
changed = False
|
|
||||||
|
|
||||||
if isinstance(subexpression, parse.LiteralInt):
|
|
||||||
changed = self.with_literal_int(ctx, subexpression)
|
|
||||||
if unify(ctx, subexpression, expression):
|
|
||||||
changed = True
|
|
||||||
return changed
|
|
||||||
if isinstance(subexpression, parse.LiteralFloat):
|
|
||||||
changed = self.with_literal_float(ctx, subexpression)
|
|
||||||
if unify(ctx, subexpression, expression):
|
|
||||||
changed = True
|
|
||||||
return changed
|
|
||||||
if isinstance(subexpression, parse.LiteralStruct):
|
|
||||||
changed = self.with_literal_struct(ctx, subexpression)
|
|
||||||
if unify(ctx, subexpression, expression):
|
|
||||||
changed = True
|
|
||||||
return changed
|
|
||||||
if isinstance(subexpression, parse.FunctionCall):
|
|
||||||
changed = self.with_function_call(ctx, subexpression)
|
|
||||||
if unify(ctx, subexpression, expression):
|
|
||||||
changed = True
|
|
||||||
return changed
|
|
||||||
if isinstance(subexpression, parse.StructGetter):
|
|
||||||
changed = self.with_struct_getter(ctx, subexpression)
|
|
||||||
if unify(ctx, subexpression, expression):
|
|
||||||
changed = True
|
|
||||||
return changed
|
|
||||||
if isinstance(subexpression, parse.Block):
|
|
||||||
changed = self.with_block(ctx, subexpression)
|
|
||||||
if unify(ctx, subexpression, expression):
|
|
||||||
changed = True
|
|
||||||
return changed
|
|
||||||
if isinstance(subexpression, parse.VariableUsage):
|
|
||||||
changed = self.with_variable_usage(ctx, subexpression)
|
|
||||||
if unify(ctx, subexpression, expression):
|
|
||||||
changed = True
|
|
||||||
return changed
|
|
||||||
if isinstance(subexpression, parse.Operation):
|
|
||||||
changed = self.with_operation(ctx, subexpression)
|
|
||||||
if unify(ctx, subexpression, expression):
|
|
||||||
changed = True
|
|
||||||
return changed
|
|
||||||
assert False
|
|
||||||
|
|
||||||
def with_variable_usage(
|
|
||||||
self, ctx: Context, variable_usage: parse.VariableUsage
|
|
||||||
) -> bool:
|
|
||||||
return unify(ctx, variable_usage, ctx.environment[variable_usage.name])
|
|
||||||
|
|
||||||
def with_operation(self, ctx: Context, operation: parse.Operation) -> bool:
|
|
||||||
changed = False
|
|
||||||
if self.with_expression(ctx, operation.left):
|
|
||||||
changed = True
|
|
||||||
if self.with_expression(ctx, operation.right):
|
|
||||||
changed = True
|
|
||||||
if unify(ctx, operation, operation.left):
|
|
||||||
changed = True
|
|
||||||
if unify(ctx, operation, operation.right):
|
|
||||||
changed = True
|
|
||||||
return changed
|
|
||||||
|
|
||||||
def with_function_call(
|
|
||||||
self, ctx: Context, function_call: parse.FunctionCall
|
|
||||||
) -> bool:
|
|
||||||
changed = False
|
|
||||||
if isinstance(function_call.source.type, parse.UnknownTypeUsage):
|
|
||||||
function_call.source.type = parse.FunctionTypeUsage(
|
|
||||||
arguments=[parse.UnknownTypeUsage()] * len(function_call.arguments),
|
|
||||||
return_type=parse.UnknownTypeUsage(),
|
|
||||||
)
|
|
||||||
changed = True
|
|
||||||
if self.with_expression(ctx, function_call.source):
|
|
||||||
changed = True
|
|
||||||
for argument in function_call.arguments:
|
|
||||||
if self.with_expression(ctx, argument):
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
assert isinstance(function_call.source.type, parse.FunctionTypeUsage)
|
|
||||||
return_type, return_changed = type_compare(
|
|
||||||
ctx, function_call.type, function_call.source.type.return_type
|
|
||||||
)
|
|
||||||
function_call.type = return_type
|
|
||||||
function_call.source.type.return_type = return_type
|
|
||||||
if return_changed:
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
for argument, argument_type in zip(
|
|
||||||
function_call.arguments, function_call.source.type.arguments
|
|
||||||
):
|
|
||||||
argument_out_type, argument_changed = type_compare(
|
|
||||||
ctx, argument.type, function_call.source.type.return_type
|
|
||||||
)
|
|
||||||
argument.type = argument_out_type
|
|
||||||
function_call.source.type.return_type = argument_out_type
|
|
||||||
if argument_changed:
|
|
||||||
changed = True
|
|
||||||
return changed
|
|
||||||
|
|
||||||
def with_struct_getter(
|
|
||||||
self, ctx: Context, struct_getter: parse.StructGetter
|
|
||||||
) -> bool:
|
|
||||||
changed = self.with_expression(ctx, struct_getter.source)
|
|
||||||
assert isinstance(struct_getter.source.type, parse.DataTypeUsage)
|
|
||||||
struct_declaration = ctx.environment[struct_getter.source.type.name]
|
|
||||||
assert isinstance(struct_declaration, parse.StructTypeDeclaration)
|
|
||||||
if struct_getter.attribute in struct_declaration.fields:
|
|
||||||
result_type, changed_getter = type_compare(
|
|
||||||
ctx,
|
|
||||||
struct_getter.type,
|
|
||||||
struct_declaration.fields[struct_getter.attribute],
|
|
||||||
)
|
|
||||||
else: # check methods and traits
|
|
||||||
impls = []
|
|
||||||
for module_impl in ctx.current_module.impls:
|
|
||||||
if module_impl.struct == struct_getter.source.type.name:
|
|
||||||
impls.append(module_impl)
|
|
||||||
assert len(impls) != 0
|
|
||||||
found = False
|
|
||||||
for impl in impls:
|
|
||||||
for function in impl.functions:
|
|
||||||
if function.declaration.name == struct_getter.attribute:
|
|
||||||
assert isinstance(function.declaration.type, parse.FunctionTypeUsage)
|
|
||||||
result_type, changed_getter = type_compare(
|
|
||||||
ctx, struct_getter.type, function_to_method(function.declaration.type)
|
|
||||||
)
|
|
||||||
found = True
|
|
||||||
break
|
|
||||||
assert found, "Method not found"
|
|
||||||
|
|
||||||
if changed_getter:
|
|
||||||
changed = True
|
|
||||||
struct_getter.type = result_type
|
|
||||||
return changed
|
|
||||||
|
|
||||||
def with_literal_float(
|
|
||||||
self, ctx: Context, literal_float: parse.LiteralFloat
|
|
||||||
) -> bool:
|
|
||||||
floats = ["F32", "F64", "F128"]
|
|
||||||
if not isinstance(literal_float.type, parse.UnknownTypeUsage):
|
|
||||||
assert isinstance(literal_float.type, parse.DataTypeUsage)
|
|
||||||
assert literal_float.type.name in floats, f"{literal_float.type}"
|
|
||||||
return False
|
|
||||||
|
|
||||||
def with_literal_int(self, ctx: Context, literal_int: parse.LiteralInt) -> bool:
|
|
||||||
ints = ["I8", "I16", "I32", "I64", "I128", "U8", "U16", "U32", "U64", "U128"]
|
|
||||||
if not isinstance(literal_int.type, parse.UnknownTypeUsage):
|
|
||||||
assert isinstance(literal_int.type, parse.DataTypeUsage)
|
|
||||||
assert literal_int.type.name in ints, f"{literal_int.type}"
|
|
||||||
return False
|
|
||||||
|
|
||||||
def with_literal_struct(
|
|
||||||
self, ctx: Context, literal_struct: parse.LiteralStruct
|
|
||||||
) -> bool:
|
|
||||||
assert isinstance(literal_struct.type, parse.DataTypeUsage)
|
|
||||||
assert literal_struct.type.name in ctx.environment, literal_struct.type.name
|
|
||||||
struct_declaration = ctx.environment[literal_struct.type.name]
|
|
||||||
assert isinstance(struct_declaration, parse.StructTypeDeclaration)
|
|
||||||
changed = False
|
|
||||||
for name, field_type in struct_declaration.fields.items():
|
|
||||||
assert name in literal_struct.fields
|
|
||||||
if self.with_expression(ctx, literal_struct.fields[name]):
|
|
||||||
changed = True
|
|
||||||
result_type, field_changed = type_compare(
|
|
||||||
ctx, field_type, literal_struct.fields[name].type
|
|
||||||
)
|
|
||||||
if field_changed:
|
|
||||||
literal_struct.fields[name].type = result_type
|
|
||||||
changed = True
|
|
||||||
return changed
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
from dataclasses import dataclass, field
|
|
||||||
import enum
|
|
||||||
from typing import List, Dict, Optional, Union
|
|
||||||
|
|
||||||
|
|
||||||
class IntBitness(enum.Enum):
|
|
||||||
X8 = "X8"
|
|
||||||
X16 = "X16"
|
|
||||||
X32 = "X32"
|
|
||||||
X64 = "X64"
|
|
||||||
X128 = "X128"
|
|
||||||
|
|
||||||
|
|
||||||
class Signedness(enum.Enum):
|
|
||||||
Signed = "Signed"
|
|
||||||
Unsigned = "Unsigned"
|
|
||||||
|
|
||||||
|
|
||||||
class FloatBitness(enum.Enum):
|
|
||||||
X32 = "X32"
|
|
||||||
X64 = "X64"
|
|
||||||
X128 = "X128"
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class IntTypeDef:
|
|
||||||
signedness: Signedness
|
|
||||||
bitness: IntBitness
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class FloatTypeDef:
|
|
||||||
bitness: FloatBitness
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class FunctionTypeDef:
|
|
||||||
arguments: List["TypeDef"]
|
|
||||||
return_type: "TypeDef"
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class StructTypeDef:
|
|
||||||
fields: Dict[str, "TypeDef"]
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class UnitTypeDef:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class NeverTypeDef:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
TypeDef = Union[
|
|
||||||
IntTypeDef, FloatTypeDef, FunctionTypeDef, StructTypeDef, UnitTypeDef, NeverTypeDef
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
builtins: Dict[str, TypeDef] = {
|
|
||||||
"U8": IntTypeDef(Signedness.Unsigned, IntBitness.X8),
|
|
||||||
"U16": IntTypeDef(Signedness.Unsigned, IntBitness.X16),
|
|
||||||
"U32": IntTypeDef(Signedness.Unsigned, IntBitness.X32),
|
|
||||||
"U64": IntTypeDef(Signedness.Unsigned, IntBitness.X64),
|
|
||||||
"U128": IntTypeDef(Signedness.Unsigned, IntBitness.X128),
|
|
||||||
"I8": IntTypeDef(Signedness.Signed, IntBitness.X8),
|
|
||||||
"I16": IntTypeDef(Signedness.Signed, IntBitness.X16),
|
|
||||||
"I32": IntTypeDef(Signedness.Signed, IntBitness.X32),
|
|
||||||
"I64": IntTypeDef(Signedness.Signed, IntBitness.X64),
|
|
||||||
"I128": IntTypeDef(Signedness.Signed, IntBitness.X128),
|
|
||||||
"F32": FloatTypeDef(FloatBitness.X32),
|
|
||||||
"F64": FloatTypeDef(FloatBitness.X64),
|
|
||||||
"F128": FloatTypeDef(FloatBitness.X128),
|
|
||||||
"()": UnitTypeDef(),
|
|
||||||
"!": NeverTypeDef(),
|
|
||||||
}
|
|
||||||
@@ -4,9 +4,3 @@ services:
|
|||||||
build: .
|
build: .
|
||||||
volumes:
|
volumes:
|
||||||
- .:/code/
|
- .:/code/
|
||||||
# boring-python:
|
|
||||||
# build:
|
|
||||||
# context: .
|
|
||||||
# dockerfile: Dockerfile-python
|
|
||||||
# volumes:
|
|
||||||
# - .:/code/
|
|
||||||
|
|||||||
4
mypy.ini
4
mypy.ini
@@ -1,4 +0,0 @@
|
|||||||
[mypy]
|
|
||||||
python_version = 3.8
|
|
||||||
warn_return_any = True
|
|
||||||
warn_unused_configs = True
|
|
||||||
32
notes.txt
32
notes.txt
@@ -1,32 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# On types
|
|
||||||
Type Usage != Type Definition
|
|
||||||
|
|
||||||
|
|
||||||
type List[T] struct {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn add[T: addable](a: T, b: T): T {
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
|
|
||||||
type usages:
|
|
||||||
List[Int64]
|
|
||||||
fn(int, int): List[Int64]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class TypeUsage:
|
|
||||||
result: Identifier # Result of useage - either is the type, or is the return value if it's a function
|
|
||||||
type_args: List[Type] # Generics
|
|
||||||
arguments: Optional[List[Type]] # Specified if it is a function, this is how you tell if it's a function
|
|
||||||
|
|
||||||
|
|
||||||
if / match return never if all blocks return never;
|
|
||||||
blocks propagate never except at the function level;
|
|
||||||
12
setup.py
12
setup.py
@@ -1,12 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
from distutils.core import setup
|
|
||||||
from setuptools import find_packages
|
|
||||||
|
|
||||||
setup(name='boring-lang',
|
|
||||||
version='0.0.1',
|
|
||||||
description='Boring programming language',
|
|
||||||
author='Andrew Segavac',
|
|
||||||
author_email='andrew@eunomia.io',
|
|
||||||
packages=find_packages(),
|
|
||||||
)
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
use std::str::FromStr;
|
|
||||||
use crate::ast;
|
use crate::ast;
|
||||||
|
|
||||||
grammar(id_generator: &ast::IdGenerator);
|
grammar(id_generator: &ast::IdGenerator);
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ extern crate lalrpop_util;
|
|||||||
lalrpop_mod!(pub grammar); // synthesized by LALRPOP
|
lalrpop_mod!(pub grammar); // synthesized by LALRPOP
|
||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::Write;
|
|
||||||
// mod compiler;
|
|
||||||
// use inkwell::context::Context;
|
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
|
|
||||||
@@ -49,7 +47,7 @@ fn main() {
|
|||||||
.last()
|
.last()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clone();
|
.clone();
|
||||||
let output = matches.value_of("OUTPUT").unwrap_or(default_output);
|
let _output = matches.value_of("OUTPUT").unwrap_or(default_output);
|
||||||
|
|
||||||
let contents = fs::read_to_string(input).expect("input file not found");
|
let contents = fs::read_to_string(input).expect("input file not found");
|
||||||
let unknown_id_gen = ast::IdGenerator::new();
|
let unknown_id_gen = ast::IdGenerator::new();
|
||||||
|
|||||||
@@ -148,15 +148,6 @@ impl Context {
|
|||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_type(&self, name: String, type_decl: &ast::TypeDeclaration) -> Context {
|
|
||||||
let mut ctx = self.clone();
|
|
||||||
ctx.environment.insert(
|
|
||||||
name.to_string(),
|
|
||||||
NamedEntity::TypeDeclaration(type_decl.clone()),
|
|
||||||
);
|
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_current_function_return(&self, function: &ast::TypeUsage) -> Context {
|
fn set_current_function_return(&self, function: &ast::TypeUsage) -> Context {
|
||||||
let mut ctx = self.clone();
|
let mut ctx = self.clone();
|
||||||
ctx.current_function_return = Some(function.clone());
|
ctx.current_function_return = Some(function.clone());
|
||||||
@@ -183,7 +174,7 @@ fn type_exists(ctx: &Context, type_: &ast::TypeUsage) -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::TypeUsage::Unknown(unknown) => {} // do nothing
|
ast::TypeUsage::Unknown(_) => {} // do nothing
|
||||||
ast::TypeUsage::Function(function) => {
|
ast::TypeUsage::Function(function) => {
|
||||||
let mut errs = vec![];
|
let mut errs = vec![];
|
||||||
for arg in function.arguments.iter() {
|
for arg in function.arguments.iter() {
|
||||||
|
|||||||
Reference in New Issue
Block a user