From 170176bc3eb6b67eb76124daf48911b6cebd9bd5 Mon Sep 17 00:00:00 2001 From: Andrew Segavac Date: Sun, 12 Sep 2021 15:00:03 -0600 Subject: [PATCH] finished move to rust --- Dockerfile-python | 3 - README.md | 4 +- boring-test.bl | 5 - boring/__init__.py | 0 boring/interpret.py | 86 ----- boring/main.py | 53 ---- boring/parse.py | 542 -------------------------------- boring/type_alias_resolution.py | 179 ----------- boring/type_checking.py | 407 ------------------------ boring/typedefs.py | 78 ----- docker-compose.yml | 6 - examples/{math => }/main.bl | 0 mypy.ini | 4 - notes.txt | 32 -- setup.py | 12 - src/grammar.lalrpop | 1 - src/main.rs | 6 +- src/type_checking.rs | 11 +- 18 files changed, 5 insertions(+), 1424 deletions(-) delete mode 100644 Dockerfile-python delete mode 100644 boring-test.bl delete mode 100644 boring/__init__.py delete mode 100644 boring/interpret.py delete mode 100644 boring/main.py delete mode 100644 boring/parse.py delete mode 100644 boring/type_alias_resolution.py delete mode 100644 boring/type_checking.py delete mode 100644 boring/typedefs.py rename examples/{math => }/main.bl (100%) delete mode 100644 mypy.ini delete mode 100644 notes.txt delete mode 100644 setup.py diff --git a/Dockerfile-python b/Dockerfile-python deleted file mode 100644 index 2be5cb5..0000000 --- a/Dockerfile-python +++ /dev/null @@ -1,3 +0,0 @@ -FROM python:3.9 - -RUN pip install lark mypy black diff --git a/README.md b/README.md index 5932c86..a3abdbe 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/boring-test.bl b/boring-test.bl deleted file mode 100644 index 2131498..0000000 --- a/boring-test.bl +++ /dev/null @@ -1,5 +0,0 @@ -fn add(a, b) { a + b } -fn subtract(a, b) { a - b } -fn main() { - add(10, subtract(9, 1)) -} diff --git a/boring/__init__.py b/boring/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/boring/interpret.py b/boring/interpret.py deleted file mode 100644 index f552764..0000000 --- a/boring/interpret.py +++ /dev/null @@ -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) diff --git a/boring/main.py b/boring/main.py deleted file mode 100644 index e6c471d..0000000 --- a/boring/main.py +++ /dev/null @@ -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 diff --git a/boring/parse.py b/boring/parse.py deleted file mode 100644 index dfeff23..0000000 --- a/boring/parse.py +++ /dev/null @@ -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) diff --git a/boring/type_alias_resolution.py b/boring/type_alias_resolution.py deleted file mode 100644 index 93ca462..0000000 --- a/boring/type_alias_resolution.py +++ /dev/null @@ -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) diff --git a/boring/type_checking.py b/boring/type_checking.py deleted file mode 100644 index 1b1f148..0000000 --- a/boring/type_checking.py +++ /dev/null @@ -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 diff --git a/boring/typedefs.py b/boring/typedefs.py deleted file mode 100644 index 20ddf54..0000000 --- a/boring/typedefs.py +++ /dev/null @@ -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(), -} diff --git a/docker-compose.yml b/docker-compose.yml index b0b0e7b..74077fd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,9 +4,3 @@ services: build: . volumes: - .:/code/ - # boring-python: - # build: - # context: . - # dockerfile: Dockerfile-python - # volumes: - # - .:/code/ diff --git a/examples/math/main.bl b/examples/main.bl similarity index 100% rename from examples/math/main.bl rename to examples/main.bl diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index b68b08b..0000000 --- a/mypy.ini +++ /dev/null @@ -1,4 +0,0 @@ -[mypy] -python_version = 3.8 -warn_return_any = True -warn_unused_configs = True diff --git a/notes.txt b/notes.txt deleted file mode 100644 index 60731b8..0000000 --- a/notes.txt +++ /dev/null @@ -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; diff --git a/setup.py b/setup.py deleted file mode 100644 index 284449b..0000000 --- a/setup.py +++ /dev/null @@ -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(), - ) diff --git a/src/grammar.lalrpop b/src/grammar.lalrpop index ba5598c..6a29845 100644 --- a/src/grammar.lalrpop +++ b/src/grammar.lalrpop @@ -1,4 +1,3 @@ -use std::str::FromStr; use crate::ast; grammar(id_generator: &ast::IdGenerator); diff --git a/src/main.rs b/src/main.rs index 4c216e4..ec05ba9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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(); diff --git a/src/type_checking.rs b/src/type_checking.rs index 469d231..4c80219 100644 --- a/src/type_checking.rs +++ b/src/type_checking.rs @@ -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() {