finished move to rust

This commit is contained in:
Andrew Segavac
2021-09-12 15:00:03 -06:00
parent 8c131c035b
commit 170176bc3e
18 changed files with 5 additions and 1424 deletions

View File

@@ -1,3 +0,0 @@
FROM python:3.9
RUN pip install lark mypy black

View File

@@ -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 language:
The language (goals):
* is compiled with a run-time (llvm for convenience + c/rust compatibility)
* 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 parametric polymorphism (generics) with higher kinded types
* uses struct+traits, rather than classes or stuct+interfaces

View File

@@ -1,5 +0,0 @@
fn add(a, b) { a + b }
fn subtract(a, b) { a - b }
fn main() {
add(10, subtract(9, 1))
}

View File

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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(),
}

View File

@@ -4,9 +4,3 @@ services:
build: .
volumes:
- .:/code/
# boring-python:
# build:
# context: .
# dockerfile: Dockerfile-python
# volumes:
# - .:/code/

View File

@@ -1,4 +0,0 @@
[mypy]
python_version = 3.8
warn_return_any = True
warn_unused_configs = True

View File

@@ -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;

View File

@@ -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(),
)

View File

@@ -1,4 +1,3 @@
use std::str::FromStr;
use crate::ast;
grammar(id_generator: &ast::IdGenerator);

View File

@@ -9,9 +9,7 @@ extern crate lalrpop_util;
lalrpop_mod!(pub grammar); // synthesized by LALRPOP
use std::fs;
use std::io::Write;
// mod compiler;
// use inkwell::context::Context;
extern crate clap;
use clap::{App, Arg};
@@ -49,7 +47,7 @@ fn main() {
.last()
.unwrap()
.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 unknown_id_gen = ast::IdGenerator::new();

View File

@@ -148,15 +148,6 @@ impl Context {
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 {
let mut ctx = self.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) => {
let mut errs = vec![];
for arg in function.arguments.iter() {