diff --git a/build.py b/build.py index 7df8d87c..ac9337ad 100644 --- a/build.py +++ b/build.py @@ -32,7 +32,7 @@ def release(): build([]) print("Second compilation step") - subprocess.check_call([exe_file("bin/princess2"), "-d", "-Isrc", "--buildfolder=build", "--outfile", exe_file("bin/princess3"), "src/main.pr"]) + subprocess.check_call([exe_file("bin/princess2"), "--no-incremental", "-d", "-Isrc", "--buildfolder=build", "--outfile", exe_file("bin/princess3"), "src/main.pr"]) print("Creating archive") FOLDER.mkdir(exist_ok=True) @@ -74,7 +74,7 @@ def release(): shutil.rmtree(FOLDER) def testrunner(extra): - args = [exe_file("bin/princess"), "--outfile", exe_file("bin/testrunner"), "src/testrunner.pr"] + args = [exe_file("bin/princess"), "--no-incremental", "--outfile", exe_file("bin/testrunner"), "src/testrunner.pr"] if sys.platform == "win32": args += WIN_ARGS subprocess.check_call(args + extra) @@ -108,7 +108,7 @@ def download(): Path(archive).unlink() def build(extra): - args = [exe_file("bin/princess"), "-d", "-Isrc", "--buildfolder=build", "--outfile", exe_file("bin/princess2"), "src/main.pr"] + args = [exe_file("bin/princess"), "--no-incremental", "-d", "-Isrc", "--buildfolder=build", "--outfile", exe_file("bin/princess2"), "src/main.pr"] if sys.platform == "win32": args += WIN_ARGS subprocess.check_call(args + extra) diff --git a/src/compiler.pr b/src/compiler.pr index 99187f7f..02d70b51 100644 --- a/src/compiler.pr +++ b/src/compiler.pr @@ -8498,10 +8498,10 @@ export def compile(state: &State, is_main: bool, no_cleanup: bool = false) { var globals: &Value = null // Create compilation unit and file if toolchain::debug_sym { - var dirname = util::dirname(state.module.filename) + var dirname = dirname(state.module.filename) if length(dirname) == 0 { dirname = "." } let abspath = absolute_path(dirname) - let file = util::basename(state.module.filename) + let file = basename(state.module.filename) let dvalues1 = allocate_ref(DebugParam, 2) dvalues1[0] = { diff --git a/src/debug.pr b/src/debug.pr index 230301d2..889c4f59 100644 --- a/src/debug.pr +++ b/src/debug.pr @@ -8,100 +8,100 @@ import json import linux } -def bin_op_to_json(str: &string, node: &parser::Node) -> &Json { +def bin_op_to_json(str: &string, node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = str - res["left"] = node_to_json(node.value.bin_op.left) - res["right"] = node_to_json(node.value.bin_op.right) + res["left"] = node_to_json(node.value.bin_op.left, types) + res["right"] = node_to_json(node.value.bin_op.right, types) return res } -def un_op_to_json(str: &string, node: &parser::Node) -> &Json { +def un_op_to_json(str: &string, node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = str - res["expr"] = node_to_json(node.value.expr) + res["expr"] = node_to_json(node.value.expr, types) return res } -def node_vec_to_json(vec: &Vector(&parser::Node)) -> &Json { +def node_vec_to_json(vec: &Vector(&parser::Node), types: bool) -> &Json { if not vec { return json::make_null() } else { let res = json::make_array() for var i in 0..vector::length(vec) { - res.push(node_to_json(vec[i])) + res.push(node_to_json(vec[i], types)) } return res } } -def func_call_to_json(node: &parser::Node) -> &Json { +def func_call_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "FuncCall" - res["left"] = node_to_json(node.value.func_call.left) - res["args"] = node_vec_to_json(node.value.func_call.args) - res["kwargs"] = node_vec_to_json(node.value.func_call.kwargs) + res["left"] = node_to_json(node.value.func_call.left, types) + res["args"] = node_vec_to_json(node.value.func_call.args, types) + res["kwargs"] = node_vec_to_json(node.value.func_call.kwargs, types) return res } -def var_decl_to_json(node: &parser::Node) -> &Json { +def var_decl_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "VarDecl" res["share"] = to_string(node.value.var_decl.share) res["kw"] = to_string(node.value.var_decl.kw) - res["left"] = node_vec_to_json(node.value.var_decl.left) - res["right"] = node_vec_to_json(node.value.var_decl.right) + res["left"] = node_vec_to_json(node.value.var_decl.left, types) + res["right"] = node_vec_to_json(node.value.var_decl.right, types) return res } -def id_decl_to_json(node: &parser::Node) -> &Json { +def id_decl_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "IdDecl" - res["value"] = node_to_json(node.value.id_decl.value) - res["tpe"] = node_to_json(node.value.id_decl.tpe) + res["value"] = node_to_json(node.value.id_decl.value, types) + res["tpe"] = node_to_json(node.value.id_decl.tpe, types) return res } -def id_decl_struct_to_json(node: &parser::Node) -> &Json { +def id_decl_struct_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "IdDeclStruct" - res["ident"] = node_to_json(node.value.id_decl_struct.ident) - res["tpe"] = node_to_json(node.value.id_decl_struct.tpe) + res["ident"] = node_to_json(node.value.id_decl_struct.ident, types) + res["tpe"] = node_to_json(node.value.id_decl_struct.tpe, types) return res } -def id_decl_enum_to_json(node: &parser::Node) -> &Json { +def id_decl_enum_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "IdDeclEnum" - res["ident"] = node_to_json(node.value.id_decl_enum.ident) - res["value"] = node_to_json(node.value.id_decl_enum.value) + res["ident"] = node_to_json(node.value.id_decl_enum.ident, types) + res["value"] = node_to_json(node.value.id_decl_enum.value, types) return res } -def enum_to_json(node: &parser::Node) -> &Json { +def enum_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "Enum" - res["tpe"] = node_to_json(node.value.t_enum.tpe) - res["body"] = node_vec_to_json(node.value.t_enum.body) + res["tpe"] = node_to_json(node.value.t_enum.tpe, types) + res["body"] = node_vec_to_json(node.value.t_enum.body, types) return res } -def id_assign_to_json(node: &parser::Node) -> &Json { +def id_assign_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "IdAssign" - res["value"] = node_to_json(node.value.expr) + res["value"] = node_to_json(node.value.expr, types) return res } -def named_arg_to_json(node: &parser::Node) -> &Json { +def named_arg_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "NamedArg" - res["name"] = node_to_json(node.value.named_arg.name) - res["value"] = node_to_json(node.value.named_arg.value) + res["name"] = node_to_json(node.value.named_arg.name, types) + res["value"] = node_to_json(node.value.named_arg.value, types) return res } -def identifier_to_json(node: &parser::Node) -> &Json { +def identifier_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "Identifier" let path = json::make_array() @@ -110,215 +110,215 @@ def identifier_to_json(node: &parser::Node) -> &Json { } res["path"] = path res["prefixed"] = node.value.identifier.prefixed - res["args"] = node_vec_to_json(node.value.identifier.args) + res["args"] = node_vec_to_json(node.value.identifier.args, types) return res } -def function_t_to_json(node: &parser::Node) -> &Json { +def function_t_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "FunctionT" - res["args"] = node_vec_to_json(node.value.t_func.args) - res["ret"] = node_vec_to_json(node.value.t_func.ret) + res["args"] = node_vec_to_json(node.value.t_func.args, types) + res["ret"] = node_vec_to_json(node.value.t_func.ret, types) return res } -def ptrarray_to_json(str: &string, node: &parser::Node) -> &Json { +def ptrarray_to_json(str: &string, node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = str res["kw"] = to_string(node.value.t_parr.kw) - res["tpe"] = node_to_json(node.value.t_parr.tpe) + res["tpe"] = node_to_json(node.value.t_parr.tpe, types) return res } -def array_static_to_json(node: &parser::Node) -> &Json { +def array_static_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "ArrayStaticT" - res["n"] = node_to_json(node.value.t_arrs.n) + res["n"] = node_to_json(node.value.t_arrs.n, types) res["kw"] = to_string(node.value.t_arrs.kw) - res["tpe"] = node_to_json(node.value.t_arrs.tpe) + res["tpe"] = node_to_json(node.value.t_arrs.tpe, types) return res } -def assign_to_json(node: &parser::Node) -> &Json { +def assign_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "Assign" - res["left"] = node_vec_to_json(node.value.assign.left) - res["right"] = node_vec_to_json(node.value.assign.right) + res["left"] = node_vec_to_json(node.value.assign.left, types) + res["right"] = node_vec_to_json(node.value.assign.right, types) return res } -def unsigned_to_json(node: &parser::Node) -> &Json { +def unsigned_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "Unsigned" - res["expr"] = node_to_json(node.value.expr) + res["expr"] = node_to_json(node.value.expr, types) return res } -def switch_to_json(node: &parser::Node) -> &Json { +def switch_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "Switch" - res["expr"] = node_to_json(node.value.switch_.expr) - res["body"] = node_vec_to_json(node.value.switch_.body) + res["expr"] = node_to_json(node.value.switch_.expr, types) + res["body"] = node_vec_to_json(node.value.switch_.body, types) return res } -def case_to_json(node: &parser::Node) -> &Json { +def case_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "Case" - res["expr"] = node_vec_to_json(node.value.case_.expr) - res["body"] = node_vec_to_json(node.value.case_.body) + res["expr"] = node_vec_to_json(node.value.case_.expr, types) + res["body"] = node_vec_to_json(node.value.case_.body, types) return res } -def if_expr_to_json(node: &parser::Node) -> &Json { +def if_expr_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "IfExpr" - res["cond"] = node_to_json(node.value.if_expr.cond) - res["if_true"] = node_to_json(node.value.if_expr.if_true) - res["if_false"] = node_to_json(node.value.if_expr.if_false) + res["cond"] = node_to_json(node.value.if_expr.cond, types) + res["if_true"] = node_to_json(node.value.if_expr.if_true, types) + res["if_false"] = node_to_json(node.value.if_expr.if_false, types) return res } -def if_to_json(node: &parser::Node, static_if: bool) -> &Json { +def if_to_json(node: &parser::Node, static_if: bool, types: bool) -> &Json { let res = json::make_object() if static_if { res["kind"] = "StaticIf" } else { res["kind"] = "If" } - res["cond"] = node_to_json(node.value.if_.cond) - res["body"] = node_vec_to_json(node.value.if_.body) - res["else_if"] = node_vec_to_json(node.value.if_.else_if) - res["else_"] = node_to_json(node.value.if_.else_) + res["cond"] = node_to_json(node.value.if_.cond, types) + res["body"] = node_vec_to_json(node.value.if_.body, types) + res["else_if"] = node_vec_to_json(node.value.if_.else_if, types) + res["else_"] = node_to_json(node.value.if_.else_, types) return res } -def else_if_to_json(node: &parser::Node) -> &Json { +def else_if_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "ElseIf" - res["cond"] = node_to_json(node.value.if_.cond) - res["body"] = node_vec_to_json(node.value.if_.body) + res["cond"] = node_to_json(node.value.if_.cond, types) + res["body"] = node_vec_to_json(node.value.if_.body, types) return res } -def else_to_json(node: &parser::Node) -> &Json { +def else_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "Else" - res["body"] = node_vec_to_json(node.value.body) + res["body"] = node_vec_to_json(node.value.body, types) return res } -def type_decl_to_json(node: &parser::Node) -> &Json { +def type_decl_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "TypeDecl" res["share"] = to_string(node.value.type_decl.share) - res["left"] = node_vec_to_json(node.value.type_decl.left) - res["right"] = node_vec_to_json(node.value.type_decl.right) + res["left"] = node_vec_to_json(node.value.type_decl.left, types) + res["right"] = node_vec_to_json(node.value.type_decl.right, types) return res } -def loop_to_json(node: &parser::Node) -> &Json { +def loop_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "Loop" - res["body"] = node_vec_to_json(node.value.body) + res["body"] = node_vec_to_json(node.value.body, types) return res } -def for_to_json(node: &parser::Node) -> &Json { +def for_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "For" - res["iddecl"] = node_to_json(node.value.for_loop.iddecl) - res["expr"] = node_to_json(node.value.for_loop.expr) - res["body"] = node_vec_to_json(node.value.for_loop.body) + res["iddecl"] = node_to_json(node.value.for_loop.iddecl, types) + res["expr"] = node_to_json(node.value.for_loop.expr, types) + res["body"] = node_vec_to_json(node.value.for_loop.body, types) return res } -def for_id_decl_to_json(node: &parser::Node) -> &Json { +def for_id_decl_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "ForIdDecl" res["kw"] = to_string(node.value.for_id_decl.kw) - res["ident"] = node_to_json(node.value.for_id_decl.ident) + res["ident"] = node_to_json(node.value.for_id_decl.ident, types) return res } -def while_to_json(node: &parser::Node) -> &Json { +def while_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "While" - res["expr"] = node_to_json(node.value.while_loop.expr) - res["body"] = node_vec_to_json(node.value.while_loop.body) + res["expr"] = node_to_json(node.value.while_loop.expr, types) + res["body"] = node_vec_to_json(node.value.while_loop.body, types) return res } -def import_module_to_json(node: &parser::Node) -> &Json { +def import_module_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "ImportModule" - res["name"] = node_to_json(node.value.import_module.name) - res["alias"] = node_to_json(node.value.import_module.alias) + res["name"] = node_to_json(node.value.import_module.name, types) + res["alias"] = node_to_json(node.value.import_module.alias, types) return res } -def def_to_json(node: &parser::Node) -> &Json { +def def_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "Def" res["share"] = to_string(node.value.def_.share) - res["name"] = node_to_json(node.value.def_.name) - res["params"] = node_vec_to_json(node.value.def_.params) - res["returns"] = node_vec_to_json(node.value.def_.returns) - res["body"] = node_vec_to_json(node.value.def_.body) + res["name"] = node_to_json(node.value.def_.name, types) + res["params"] = node_vec_to_json(node.value.def_.params, types) + res["returns"] = node_vec_to_json(node.value.def_.returns, types) + res["body"] = node_vec_to_json(node.value.def_.body, types) return res } -def parameter_to_json(node: &parser::Node) -> &Json { +def parameter_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "Parameter" res["kw"] = to_string(node.value.param.kw) - res["name"] = node_to_json(node.value.param.name) - res["tpe"] = node_to_json(node.value.param.tpe) - res["value"] = node_to_json(node.value.param.value) + res["name"] = node_to_json(node.value.param.name, types) + res["tpe"] = node_to_json(node.value.param.tpe, types) + res["value"] = node_to_json(node.value.param.value, types) return res } -def struct_lit_to_json(node: &parser::Node) -> &Json { +def struct_lit_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "StructLit" - res["args"] = node_vec_to_json(node.value.struct_lit.args) - res["kwargs"] = node_vec_to_json(node.value.struct_lit.kwargs) + res["args"] = node_vec_to_json(node.value.struct_lit.args, types) + res["kwargs"] = node_vec_to_json(node.value.struct_lit.kwargs, types) return res } -def assert_to_json(node: &parser::Node) -> &Json { +def assert_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "Assert" - res["cond"] = node_to_json(node.value.assert_.cond) - res["message"] = node_to_json(node.value.assert_.message) + res["cond"] = node_to_json(node.value.assert_.cond, types) + res["message"] = node_to_json(node.value.assert_.message, types) return res } -def structural_member_to_json(node: &parser::Node) -> &Json { +def structural_member_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "StructuralMember" - res["name"] = node_to_json(node.value.structural_member.name) - res["params"] = node_vec_to_json(node.value.structural_member.params) - res["returns"] = node_vec_to_json(node.value.structural_member.returns) + res["name"] = node_to_json(node.value.structural_member.name, types) + res["params"] = node_vec_to_json(node.value.structural_member.params, types) + res["returns"] = node_vec_to_json(node.value.structural_member.returns, types) return res } -def type_constructor_to_json(node: &parser::Node) -> &Json { +def type_constructor_to_json(node: &parser::Node, types: bool) -> &Json { let res = json::make_object() res["kind"] = "TypeConstructor" - res["name"] = node_to_json(node.value.type_constructor.name) - res["args"] = node_vec_to_json(node.value.type_constructor.args) + res["name"] = node_to_json(node.value.type_constructor.name, types) + res["args"] = node_vec_to_json(node.value.type_constructor.args, types) return res } -export def node_to_json(node: &parser::Node) -> &Json { +export def node_to_json(node: &parser::Node, types: bool = false) -> &Json { if not node { return json::make_null() } var res: &Json switch (@node).kind !int { case parser::NodeKind::PROGRAM: res = json::make_object() res["kind"] = "Program" - res["body"] = node_vec_to_json(node.body) + res["body"] = node_vec_to_json(node.body, types) case parser::NodeKind::INTEGER: res = json::make_object() res["kind"] = "Integer" @@ -340,11 +340,11 @@ export def node_to_json(node: &parser::Node) -> &Json { res["kind"] = "Boolean" res["value"] = node.value.i !bool case parser::NodeKind::IDENTIFIER: - res = identifier_to_json(node) + res = identifier_to_json(node, types) case parser::NodeKind::DEFINED: - res = un_op_to_json("Defined", node) + res = un_op_to_json("Defined", node, types) case parser::NodeKind::ERROR: - res = un_op_to_json("Error", node) + res = un_op_to_json("Error", node, types) case parser::NodeKind::NULL: res = json::make_object() res["kind"] = "Null" @@ -352,135 +352,135 @@ export def node_to_json(node: &parser::Node) -> &Json { res = json::make_object() res["kind"] = "Undef" case parser::NodeKind::RANGE: - res = bin_op_to_json("Range", node) + res = bin_op_to_json("Range", node, types) case parser::NodeKind::RANGE_INC: - res = bin_op_to_json("RangeInc", node) + res = bin_op_to_json("RangeInc", node, types) case parser::NodeKind::ARRAY_LIT: res = json::make_object() res["kind"] = "ArrayLit" - res["body"] = node_vec_to_json(node.value.body) + res["body"] = node_vec_to_json(node.value.body, types) case parser::NodeKind::STRUCT_LIT: - res = struct_lit_to_json(node) + res = struct_lit_to_json(node, types) case parser::NodeKind::MEMBER_ACCESS: - res = bin_op_to_json("MemberAccess", node) + res = bin_op_to_json("MemberAccess", node, types) case parser::NodeKind::CAST: - res = bin_op_to_json("Cast", node) + res = bin_op_to_json("Cast", node, types) case parser::NodeKind::SIZE_OF: - res = un_op_to_json("SizeOf", node) + res = un_op_to_json("SizeOf", node, types) case parser::NodeKind::ALIGN_OF: - res = un_op_to_json("AlignOf", node) + res = un_op_to_json("AlignOf", node, types) case parser::NodeKind::TYPE_OF_T: - res = un_op_to_json("TypeOf", node) + res = un_op_to_json("TypeOf", node, types) case parser::NodeKind::ADD: - res = bin_op_to_json("Add", node) + res = bin_op_to_json("Add", node, types) case parser::NodeKind::SUB: - res = bin_op_to_json("Sub", node) + res = bin_op_to_json("Sub", node, types) case parser::NodeKind::MUL: - res = bin_op_to_json("Mul", node) + res = bin_op_to_json("Mul", node, types) case parser::NodeKind::DIV: - res = bin_op_to_json("Div", node) + res = bin_op_to_json("Div", node, types) case parser::NodeKind::MOD: - res = bin_op_to_json("Mod", node) + res = bin_op_to_json("Mod", node, types) case parser::NodeKind::AND: - res = bin_op_to_json("And", node) + res = bin_op_to_json("And", node, types) case parser::NodeKind::OR: - res = bin_op_to_json("Or", node) + res = bin_op_to_json("Or", node, types) case parser::NodeKind::UADD: - res = un_op_to_json("UAdd", node) + res = un_op_to_json("UAdd", node, types) case parser::NodeKind::USUB: - res = un_op_to_json("USub", node) + res = un_op_to_json("USub", node, types) case parser::NodeKind::PTR: - res = un_op_to_json("Ptr", node) + res = un_op_to_json("Ptr", node, types) case parser::NodeKind::DEREF: - res = un_op_to_json("Deref", node) + res = un_op_to_json("Deref", node, types) case parser::NodeKind::BNOT: - res = un_op_to_json("BNot", node) + res = un_op_to_json("BNot", node, types) case parser::NodeKind::NOT: - res = un_op_to_json("Not", node) + res = un_op_to_json("Not", node, types) case parser::NodeKind::BAND: - res = bin_op_to_json("BAnd", node) + res = bin_op_to_json("BAnd", node, types) case parser::NodeKind::BOR: - res = bin_op_to_json("BOr", node) + res = bin_op_to_json("BOr", node, types) case parser::NodeKind::BXOR: - res = bin_op_to_json("BXor", node) + res = bin_op_to_json("BXor", node, types) case parser::NodeKind::SHL: - res = bin_op_to_json("Shl", node) + res = bin_op_to_json("Shl", node, types) case parser::NodeKind::SHR: - res = bin_op_to_json("Shr", node) + res = bin_op_to_json("Shr", node, types) case parser::NodeKind::PADD: - res = bin_op_to_json("PAdd", node) + res = bin_op_to_json("PAdd", node, types) case parser::NodeKind::PSUB: - res = bin_op_to_json("PSub", node) + res = bin_op_to_json("PSub", node, types) case parser::NodeKind::EQ: - res = bin_op_to_json("Eq", node) + res = bin_op_to_json("Eq", node, types) case parser::NodeKind::NEQ: - res = bin_op_to_json("NEq", node) + res = bin_op_to_json("NEq", node, types) case parser::NodeKind::GT: - res = bin_op_to_json("Gt", node) + res = bin_op_to_json("Gt", node, types) case parser::NodeKind::LT: - res = bin_op_to_json("Lt", node) + res = bin_op_to_json("Lt", node, types) case parser::NodeKind::GEQ: - res = bin_op_to_json("GEq", node) + res = bin_op_to_json("GEq", node, types) case parser::NodeKind::LEQ: - res = bin_op_to_json("LEq", node) + res = bin_op_to_json("LEq", node, types) case parser::NodeKind::PADD_EQ: - res = bin_op_to_json("PAddEq", node) + res = bin_op_to_json("PAddEq", node, types) case parser::NodeKind::PSUB_EQ: - res = bin_op_to_json("PSubEq", node) + res = bin_op_to_json("PSubEq", node, types) case parser::NodeKind::ADD_EQ: - res = bin_op_to_json("AddEq", node) + res = bin_op_to_json("AddEq", node, types) case parser::NodeKind::SUB_EQ: - res = bin_op_to_json("SubEq", node) + res = bin_op_to_json("SubEq", node, types) case parser::NodeKind::MUL_EQ: - res = bin_op_to_json("MulEq", node) + res = bin_op_to_json("MulEq", node, types) case parser::NodeKind::DIV_EQ: - res = bin_op_to_json("DivEq", node) + res = bin_op_to_json("DivEq", node, types) case parser::NodeKind::MOD_EQ: - res = bin_op_to_json("ModEq", node) + res = bin_op_to_json("ModEq", node, types) case parser::NodeKind::AND_EQ: - res = bin_op_to_json("AndEq", node) + res = bin_op_to_json("AndEq", node, types) case parser::NodeKind::OR_EQ: - res = bin_op_to_json("OrEq", node) + res = bin_op_to_json("OrEq", node, types) case parser::NodeKind::XOR_EQ: - res = bin_op_to_json("XorEq", node) + res = bin_op_to_json("XorEq", node, types) case parser::NodeKind::SHL_EQ: - res = bin_op_to_json("ShlEq", node) + res = bin_op_to_json("ShlEq", node, types) case parser::NodeKind::SHR_EQ: - res = bin_op_to_json("ShrEq", node) + res = bin_op_to_json("ShrEq", node, types) case parser::NodeKind::IMPORT: res = json::make_object() res["kind"] = "Import" - res["body"] = node_vec_to_json(node.value.body) + res["body"] = node_vec_to_json(node.value.body, types) case parser::NodeKind::IMPORT_MODULE: - res = import_module_to_json(node) + res = import_module_to_json(node, types) case parser::NodeKind::ASSIGN: - res = assign_to_json(node) + res = assign_to_json(node, types) case parser::NodeKind::DEF: - res = def_to_json(node) + res = def_to_json(node, types) case parser::NodeKind::PARAMETER: - res = parameter_to_json(node) + res = parameter_to_json(node, types) case parser::NodeKind::SWITCH: - res = switch_to_json(node) + res = switch_to_json(node, types) case parser::NodeKind::CASE: - res = case_to_json(node) + res = case_to_json(node, types) case parser::NodeKind::IF_EXPR: - res = if_expr_to_json(node) + res = if_expr_to_json(node, types) case parser::NodeKind::IF: - res = if_to_json(node, false) + res = if_to_json(node, false, types) case parser::NodeKind::STATIC_IF: - res = if_to_json(node, true) + res = if_to_json(node, true, types) case parser::NodeKind::ELSE_IF: - res = else_if_to_json(node) + res = else_if_to_json(node, types) case parser::NodeKind::ELSE: - res = else_to_json(node) + res = else_to_json(node, types) case parser::NodeKind::LOOP: - res = loop_to_json(node) + res = loop_to_json(node, types) case parser::NodeKind::WHILE: - res = while_to_json(node) + res = while_to_json(node, types) case parser::NodeKind::FOR: - res = for_to_json(node) + res = for_to_json(node, types) case parser::NodeKind::FOR_ID_DECL: - res = for_id_decl_to_json(node) + res = for_id_decl_to_json(node, types) case parser::NodeKind::BREAK: res = json::make_object() res["kind"] = "Break" @@ -490,78 +490,85 @@ export def node_to_json(node: &parser::Node) -> &Json { case parser::NodeKind::RETURN: res = json::make_object() res["kind"] = "Return" - res["body"] = node_vec_to_json(node.value.body) + res["body"] = node_vec_to_json(node.value.body, types) case parser::NodeKind::YIELD: res = json::make_object() res["kind"] = "Yield" - res["body"] = node_vec_to_json(node.value.body) + res["body"] = node_vec_to_json(node.value.body, types) case parser::NodeKind::DEFER: res = json::make_object() res["kind"] = "Defer" - res["body"] = node_vec_to_json(node.value.body) + res["body"] = node_vec_to_json(node.value.body, types) case parser::NodeKind::ASSERT: - res = assert_to_json(node) + res = assert_to_json(node, types) case parser::NodeKind::ARRAY_SUBSCRIPT: - res = bin_op_to_json("ArraySubscript", node) + res = bin_op_to_json("ArraySubscript", node, types) case parser::NodeKind::FUNC_CALL: - res = func_call_to_json(node) + res = func_call_to_json(node, types) case parser::NodeKind::TYPE_DECL: - res = type_decl_to_json(node) + res = type_decl_to_json(node, types) case parser::NodeKind::VAR_DECL: - res = var_decl_to_json(node) + res = var_decl_to_json(node, types) case parser::NodeKind::ID_DECL: - res = id_decl_to_json(node) + res = id_decl_to_json(node, types) case parser::NodeKind::ID_ASSIGN: - res = id_assign_to_json(node) + res = id_assign_to_json(node, types) case parser::NodeKind::NAMED_ARG: - res = named_arg_to_json(node) + res = named_arg_to_json(node, types) case parser::NodeKind::ID_DECL_STRUCT: - res = id_decl_struct_to_json(node) + res = id_decl_struct_to_json(node, types) case parser::NodeKind::ID_DECL_ENUM: - res = id_decl_enum_to_json(node) + res = id_decl_enum_to_json(node, types) case parser::NodeKind::ENUM_T: - res = enum_to_json(node) + res = enum_to_json(node, types) case parser::NodeKind::STRUCT_T: res = json::make_object() res["kind"] = "Struct" - res["body"] = node_vec_to_json(node.value.body) + res["body"] = node_vec_to_json(node.value.body, types) case parser::NodeKind::UNION_T: res = json::make_object() res["kind"] = "Union" - res["body"] = node_vec_to_json(node.value.body) + res["body"] = node_vec_to_json(node.value.body, types) case parser::NodeKind::FUNCTION_T, parser::NodeKind::CLOSURE_T: - res = function_t_to_json(node) + res = function_t_to_json(node, types) case parser::NodeKind::UNSIGNED_T: - res = unsigned_to_json(node) + res = unsigned_to_json(node, types) case parser::NodeKind::PTR_T: - res = ptrarray_to_json("PtrT", node) + res = ptrarray_to_json("PtrT", node, types) case parser::NodeKind::REF_T: - res = ptrarray_to_json("RefT", node) + res = ptrarray_to_json("RefT", node, types) case parser::NodeKind::ARRAY_T: - res = ptrarray_to_json("ArrayT", node) + res = ptrarray_to_json("ArrayT", node, types) case parser::NodeKind::WEAK_REF_T: - res = ptrarray_to_json("WeakRefT", node) + res = ptrarray_to_json("WeakRefT", node, types) case parser::NodeKind::WORD_T: res = json::make_object() res["kind"] = "Word" res["size"] = node.value.i !double case parser::NodeKind::ARRAY_STATIC_T: - res = array_static_to_json(node) + res = array_static_to_json(node, types) case parser::NodeKind::TYPE_T: - res = un_op_to_json("TypeT", node) + res = un_op_to_json("TypeT", node, types) case parser::NodeKind::STRUCTURAL_T: res = json::make_object() res["kind"] = "StructuralT" - res["body"] = node_vec_to_json(node.value.body) + res["body"] = node_vec_to_json(node.value.body, types) case parser::NodeKind::STRUCTURAL_T_MEMBER: - res = structural_member_to_json(node) + res = structural_member_to_json(node, types) case parser::NodeKind::TYPE_CONSTRUCTOR: - res = type_constructor_to_json(node) + res = type_constructor_to_json(node, types) case: error(node.kind, "\n") assert } + if types and node.tpe { + let info = json::make_object() + info["name"] = type_to_str(node.tpe) + info["size"] = node.tpe.size + info["align"] = node.tpe.align + res["type_tag"] = info + } return res } diff --git a/src/errors.pr b/src/errors.pr index 853ceaa0..60c76464 100644 --- a/src/errors.pr +++ b/src/errors.pr @@ -88,7 +88,7 @@ export def errorn(node: &parser::Node, msg: &string...) { if suppress_errors { return } if not node { assert(false) } - let filename = (@node).loc.filename + let filename = (@node).loc.display_name let line = (@node).loc.line let column = (@node).loc.column let end_line = node.loc.end_line diff --git a/src/main.pr b/src/main.pr index 96bdc322..c086bc91 100644 --- a/src/main.pr +++ b/src/main.pr @@ -15,6 +15,10 @@ let options = [ .set_help("Print the ast when compiling")), (option("--tokens", false) .set_help("Print the tokens when compiling")), + (option("--typed-ast", false) + .set_help("Print AST with type information")), + (option("--continue-on-output", false) + .set_help("Continue after printing the AST/Tokens")), (option_repeat('I', "--include") .set_help("Include directory") .set_metavar("DIR")), @@ -55,6 +59,8 @@ let options = [ .set_help("Set up language server to use stdio")), (option("--no-incremental", false) .set_help("Starts compiling non incrementally, all caches are invalidated")), + (option("--name", "") + .set_help("Set name for the main file")), (option("compile", "") .set_help("File to compile") .set_metavar("FILE")) @@ -65,6 +71,8 @@ if not res { exit(1) } let print_ast = parser.get_value("--ast").b let print_tokens = parser.get_value("--tokens").b +let print_typed_ast = parser.get_value("--typed-ast").b +let continue_on_output = parser.get_value("--continue-on-output").b let includes = parser.get_value("--include") let defines = parser.get_value("--define") let link_directories = parser.get_value("--link-directory") @@ -80,6 +88,7 @@ let print_version = parser.get_value("--version").b let verbose = parser.get_value("--verbose").b let language_server = parser.get_value("--language-server").b let no_incremental = parser.get_value("--no-incremental").b +let name = parser.get_value("--name").str let filename = parser.get_value("compile").str if print_version { @@ -120,8 +129,14 @@ while define { debug::verbose = verbose +if name.length > 0 { + toolchain::main_file_name = name +} + toolchain::print_ast = print_ast toolchain::print_tokens = print_tokens +toolchain::print_typed_ast = print_typed_ast +toolchain::continue_on_output = continue_on_output toolchain::debug_sym = debug_sym toolchain::time_report = time_report toolchain::dependency_graph = dependency_graph diff --git a/src/parser.pr b/src/parser.pr index f0df83b4..0a5d72ab 100644 --- a/src/parser.pr +++ b/src/parser.pr @@ -368,6 +368,7 @@ export type NodeValue = struct #union { export type SourceLoc = struct { filename: &string + display_name: &string module: &string line: int column: int @@ -1131,6 +1132,7 @@ export def find(node: &Node, line: int, column: int) -> &Node { export type ParseState = struct { filename: &string + display_name: &string module: &string has_error: bool lines: &[&string] @@ -1350,6 +1352,7 @@ def make_node(kind: NodeKind, line: int, column: int, state: &ParseState) -> &No kind = kind, loc = { state.filename, + state.display_name, state.module, line, column, @@ -2385,6 +2388,7 @@ def parse_term(parse_state: &ParseState) -> &Node { node.scope = null node.loc = { parse_state.filename, + parse_state.display_name, parse_state.module, token.line, token.column, @@ -3899,12 +3903,13 @@ def parse_block(parse_state: &ParseState, vec: &Vector(&Node)) { export var time_spent: int64 = 0 -export def parse(list: *lexer::TokenList, lines: &[&string], filename: &string, module: &string) -> &Node { +export def parse(list: *lexer::TokenList, lines: &[&string], filename: &string, module: &string, display_name: &string = null) -> &Node { debug::trace("Parsing ", module) let start = util::millis() var parse_state = { filename = filename, + display_name = display_name, module = module, lines = lines, tokens = list diff --git a/src/scope.pr b/src/scope.pr index 3daaa825..feb0bc83 100644 --- a/src/scope.pr +++ b/src/scope.pr @@ -119,6 +119,7 @@ export def loc(value: &Value) -> parser::SourceLoc { if value.tpe and value.tpe.kind == typechecking::TypeKind::NAMESPACE { let module = value._module.module return { + module.filename, module.filename, module.module, 0, 0, 0, 0 @@ -185,7 +186,9 @@ export def create_dependency_on_type(this: &Value, node: &parser::Node) { // TODO We need to check for type here for some reason, without this enums from other files get caught // Figure out why instead of working around the problem if node.kind == parser::NodeKind::IDENTIFIER and (not node.tpe or node.tpe.kind == typechecking::TypeKind::STUB) { - errors::errorn(node, "Unknown type") + if node.tpe and node.tpe.kind == typechecking::TypeKind::STUB { + errors::errorn(node, "Unknown type") + } create_dependency(this, make_ident(parser::identifier_to_str(node))) } return diff --git a/src/test/main.pr b/src/test/main.pr deleted file mode 100644 index caafaaa7..00000000 --- a/src/test/main.pr +++ /dev/null @@ -1,18 +0,0 @@ -import getopt -import test::testsuite - -let options = [ - option('f', "--filter", "*").set_help("Filter test cases, wildcards * and ? are allowed"), - option("--no-fork", false).set_help("If specified runs the test on the same process instead of creating a fork"), - option("--no-capture", false).set_help("Specifies if stdout and stderr should be captured") -] - -let parser = *getopt::make_parser(options, "Princess Testsuite") -let res = parser.parse(args) -if not res { exit(1) } - -test::testsuite::match_string = parser.get_value("--filter").str -test::testsuite::fork_process = not parser.get_value("--no-fork").b -test::testsuite::capture_stdout = not parser.get_value("--no-capture").b - -test::testsuite::run_test_suite() \ No newline at end of file diff --git a/src/test/test_compiler.pr b/src/test/test_compiler.pr deleted file mode 100644 index 786ec8d9..00000000 --- a/src/test/test_compiler.pr +++ /dev/null @@ -1,837 +0,0 @@ -import vector -import map -import codegen -import lexer -import util -import parser -import scope -import typechecking -import compiler -import toolchain -import builtins -import consteval -import errors - -import test::testsuite - -export var print_ll = false - -def compile(s: &string) -> &toolchain::Module { - toolchain::outfolder = "./build" - errors::error_count = 0 - toolchain::clear_temporaries_in_runtime_module() - - let main = "main" - let tokens = lexer::lex(s) - let lines = util::split_lines(s) - let node = parser::parse(tokens, lines, main, main) - let module = toolchain::make_module( - text = s, - lines = lines, - filename = main, - modulename = main, - node = node, - scpe = scope::enter_function_scope(builtins::builtins, null) - ) - module.scope.module = module - toolchain::modules[main] = module - - consteval::consteval(module) - typechecking::typecheck(module) - - toolchain::load_file_type() - toolchain::reset_types() - - compiler::compile(module) - codegen::gen(module) - - let fh = open("./build/main.ll", "r") - let buf = read_all(fh) - close(fh) - - #if defined WIN32 { - let llc = system("llc.exe build/main.ll") - } else { - let llc = system("llc-13 build/main.ll") - } - - if llc { - error("LLC compilation failed!\n") - } - - if print_ll or llc { - print("\n") - print(s) - print("\n") - print(buf) - } - - if llc { - exit(1) - } - - return module -} - -def test_arithmetic { - var str = " - 10 + 10 + 10 - " - var res = compile(str) - - str = " - 10 * 2 + 5 / 4 - 5 % 3 - " - res = compile(str) - - str = " - 10.5 / 4.0 - " - res = compile(str) - - str = " - 10 << 1 + 10 >> 2 - " - res = compile(str) - - str = " - -10 - -10.5 - " - res = compile(str) -} - -def test_call { - var str = " - def add(a: int, b: int) -> int { - return a + b - } - add(10, 10) - " - var res = compile(str) - - // Overloaded function - str = " - def add(a: int, b: int) -> int { - return a + b - } - def add(a: double, b: double) -> double { - return a + b - } - add(10, 10) - add(10.0, 10.5) - " - res = compile(str) -} - -def test_if { - var str = " - def foo {} - if true { - foo() - } - foo() - " - var res = compile(str) - - str = " - def foo -> int { return 0 } - if true { - let x = foo() - } else if false { - let x = foo() - } else if true { - let x = foo() - } - let x = foo() - " - res = compile(str) - - str = " - def foo - if true { - foo() - } else { - foo() - } - foo() - " - res = compile(str) - - // Nested - str = " - def foo - if true { - foo() - if true { - foo() - } else { - foo() - } - } else { - foo() - if true { - foo() - } else if true { - foo() - } else { - foo() - } - } - foo() - " - res = compile(str) -} - -def test_loop { - var str = " - def foo - loop { - foo() - continue - foo() - break - foo() - } - " - var res = compile(str) - - str = " - loop { - if true { - break - } else { - continue - } - break - } - " - res = compile(str) - - str = " - loop { - continue - loop { - continue - break - } - break - } - " - res = compile(str) -} - -def test_while { - var str = " - var i = 0 - while i < 10 { - i += 1 - } - " - var res = compile(str) -} - -def test_for { - var str = " - for var i in 0..10 { - print(i) - } - " - var res = compile(str) - - str = " - var i = 0 - for i in 0..=10 { - print(i) - } - " - res = compile(str) -} - -def test_vardecl { - var str = " - def foo { - var a: int, b: int - } - " - var res = compile(str) - - // Test scoping - str = " - def foo { - var a: int - if false { - var a: int - } - } - " - res = compile(str) -} - -def test_globals { - var str = " - var global: int - " - var res = compile(str) - - str = " - let global = 20 - " - res = compile(str) - - str = " - let a, b = 10, 20 - " - res = compile(str) - - str = " - def foo -> int, int { - return 10, 20 - } - let a, b = foo() - " - res = compile(str) - - str = " - def foo -> int, int { - return 10, 20 - } - var a: int - let (a), b = foo() - " - res = compile(str) - - str = " - def foo -> int { - return 10 - } - var a: int - var b: int - a = b = foo() - " - res = compile(str) -} - -def test_ptr { - var str = " - let a = 20 - let pa = *a - @pa = 40 - " - var res = compile(str) - - str = " - let a = 20 - let pa = *a - let ppa = *pa - @@ppa = 40 - let b = @@ppa - " - res = compile(str) -} - -def test_ref { - var str = " - var a: &int - var b: & - b = a - " - var res = compile(str) - - str = " - var a: &int = 20 - var b = a - " - res = compile(str) - - str = " - var a: &int = 20 - var b = @a - " - res = compile(str) - - str = " - var a: &int = 20 - var b = a !&float - var c = a !*int - " - res = compile(str) -} - -def test_convert { - var str = " - let a = 10 !float - let b = a !int - " - var res = compile(str) - - str = " - let a = 10!float + 20 - let b = 10!uint + 20 - " - res = compile(str) - - str = " - let a = 200!bool - let b = 1.5!bool - let c: *int = null - let d = c!bool - " - res = compile(str) - - str = " - let a: int64 = 20 - let b: int16 = 20 - - def foo(a: int64) - foo(10) - " - res = compile(str) -} - -def test_member_access { - var str = " - type S = struct { - value: int - } - type T = struct { - a: int - b: S - } - - var t: T - let a = t.a - let b = t.b.value - - t.b.value = 20 - t.a = 40 - " - var res = compile(str) -} - -def test_array_subscript { - var str = " - var a: [int] - var b: *int - var c: [3; int] - - let d = a[2] - let e = b[2] - let f = c[2] - - a[2] = 5 - b[2] = 10 - c[2] = 15 - " - var res = compile(str) -} - -def test_struct_lit { - var str = " - type T = struct { - a: int - b: int - } - let a = {10, 20} !T - " - var res = compile(str) - - str = " - type T = struct { - a: int - b: int - } - let a = 20 - let b = 50 - let c = {a = a, b = b} !T - " - res = compile(str) - - str = " - type A = struct { - a: int - } - type B = struct { - b: A - c: int - } - let a = 20 - let b = 50 - let v = {{a} !A, b} !B - " - res = compile(str) - - str = " - type A = struct { - value: int - } - - def ret_a -> A { - return {10} - } - - let a: A = {10} - var b: A - b = {10} - " - res = compile(str) - - str = " - type A = struct { - a: int - b: int - c: int - } - let a = {a = 10} !A - " - res = compile(str) -} - -def test_size_of { - var str = " - type T = struct { - a: int - b: int - } - - let a = size_of T - let b = size_of int - let c = size_of type * - let d = size_of type_of a - let e = size_of type_of a + b - " - var res = compile(str) -} - -def test_align_of { - var str = " - type T = struct { - a: int - b: int - } - - let a = align_of T - let b = align_of int - let c = align_of type * - let d = align_of type_of a - let e = align_of type_of a + b - " - var res = compile(str) -} - -def test_type_of { - var str = " - let a = 20 - let b: (type_of a) = 30 - - def some_function(type T) {} - - some_function(type_of a) - " - var res = compile(str) -} - -def test_array_lit { - var str = " - let a = [1, 2, 3, 4] - " - var res = compile(str) - - str = " - let a: [int] = [1, 2, 3, 4] - let b: [?; int] = [1, 2, 3, 4] - " - res = compile(str) - - str = " - let a = 10 - let b = [a, 20] - let c: [int] = b - " - res = compile(str) -} - -def test_compare { - var str = " - let a = 10 == 20 - let b = 10 > 20 - let c = 10 < 20 - - let d = 5 - let e = 1 < d < 10 - " - var res = compile(str) -} - -def test_pointer_arithmetic { - var str = " - let a = 20 - let b = *a - let c = b ++ 20 -- 5 - " - var res = compile(str) - - str = " - var a: *int - @(a ++ 10) = 20 - " - res = compile(str) -} - -def test_assign_eq { - var str = " - var a = 10 - a += 20 - a <<= 1 - a >>= 2 - a *= 5 - a /= 10 - " - var res = compile(str) -} - -def test_import { - var str = " - import test::module::a - import test::module::b - - let a, b = return_multiple() - " - var res = compile(str) -} - -def test_array_size_and_value { - var str = " - var a: [4; int] - var b: [int] - - let c = a.size - let d = a.value - let e = b.size - let f = b.value - - b.size = 20 - b.value = null - " - var res = compile(str) -} - -def test_string_literal { - // Conversion between static and dynamic arrays - var str = " - def function -> [4; char] {} - - var a: [char] = \"abc\" - var b: [char] = function() - - def two_returns -> [4; char], [5; char] {} - var c: [char], d: [char] = two_returns() - " - var res = compile(str) -} - -def test_boolean_op { - var str = " - let a = true - let b = false - let c = a and b - let d = a or b - let e = not d - " - var res = compile(str) -} - -def test_builtins { - var str = " - var a = 10 - assert(a == 15) - print(a, 10, 10.5, \"string\") - let b = allocate(int) - let c = allocate(size_of int) !*int - reallocate(c, 10 * (size_of int)) - let d = allocate(int, 10) - free(b) - free(c) - free(d) - " - var res = compile(str) - - str = " - let fp: File = open(\"build/test\", \"wb+\") - - write(fp, \"some string\") - let i = 20 - write(fp, *i) - - rewind(fp) - - var str: [12; char] - read(fp, str) - - var i2: int - read(fp, *i2) - - close(fp) - " - res = compile(str) - - str = " - let fp: File = open(\"build/test_file_io_text\", \"w+\") - - fprint(fp, \"This is a test\\n\", 10) - - seek(fp, 0, SEEK_SET) // Same as rewind - - var buffer: [20; char] - read_line(fp, buffer) - var num: int - cstd::fscanf(fp, \"%d\".value, *num) - - close(fp) - - " - res = compile(str) -} - -def test_switch { - var str = " - let a = 20 - switch a { - case 10: print(1) - case 11..=19: print(2) - case 20, 25: print(3) - case: print(4) - } - " - var res = compile(str) - - str = " - let a = 20 - switch a { - case 0..=100: print(1) - case 101, 102, 103: print(2) - case 104..200, 205: print(3) - case: print(4) - } - " - res = compile(str) -} - -def test_function_pointers { - var str = " - def some_function -> int { return 20 } - let a = *some_function - let b = a() - " - var res = compile(str) -} - -def test_if_expression { - var str = " - let a = 10 if 10 > 20 else 5 - var b: int - var c: int - @(*b if 10 > 20 else *c) = 10 - " - var res = compile(str) -} - -def test_return_multiple { - var str = " - type T = struct { - a: int - b: int - } - - def return_multiple -> T, T { - let a = { 0, 0 } !T - let b = { 1, 1 } !T - return a, b - } - " - var res = compile(str) -} - -def test_assert { - var str = " - assert - assert 1 == 2 - assert 1 == 2, \"What is this\" - " - var res = compile(str) -} - -def test_yield { - var str = " - def some_function -> int { - yield 20 - yield 30 - } - let gen = some_function() - for var i in gen { - print(i) - } - " - var res = compile(str) -} - -def test_closure { - var str = " - def some_function -> int { - var a = 20 - var b = 30 - def inner -> int { - let capture = *b - let res = a + @capture - @capture += 10 - return res - } - inner() - return inner() - } - " - var res = compile(str) -} - -export def run_tests { - toolchain::no_incremental = true // TODO Temporary - toolchain::no_dependency_tracking = true - toolchain::include_path.push("src") - - toolchain::prepare_preload() - toolchain::create_types_main() - - print("Running tests on Compiler...\n") - run_test("test_arithmetic", *test_arithmetic) - run_test("test_call", *test_call) - run_test("test_if", *test_if) - run_test("test_loop", *test_loop) - run_test("test_while", *test_while) - run_test("test_for", *test_for) - run_test("test_vardecl", *test_vardecl) - run_test("test_globals", *test_globals) - run_test("test_ptr", *test_ptr) - run_test("test_ref", *test_ref) - run_test("test_convert", *test_convert) - run_test("test_member_access", *test_member_access) - run_test("test_array_subscript", *test_array_subscript) - run_test("test_struct_lit", *test_struct_lit) - run_test("test_size_of", *test_size_of) - run_test("test_align_of", *test_align_of) - run_test("test_type_of", *test_type_of) - run_test("test_array_lit", *test_array_lit) - run_test("test_compare", *test_compare) - run_test("test_pointer_arithmetic", *test_pointer_arithmetic) - run_test("test_assign_eq", *test_assign_eq) - run_test("test_import", *test_import) - run_test("test_array_size_and_value", *test_array_size_and_value) - run_test("test_string_literal", *test_string_literal) - run_test("test_builtins", *test_builtins) - run_test("test_switch", *test_switch) - run_test("test_function_pointers", *test_function_pointers) - run_test("test_if_expression", *test_if_expression) - run_test("test_return_multiple", *test_return_multiple) - run_test("test_assert", *test_assert) - run_test("test_yield", *test_yield) - run_test("test_closure", *test_closure) -} \ No newline at end of file diff --git a/src/test/test_ctfe.pr b/src/test/test_ctfe.pr deleted file mode 100644 index df47b8e1..00000000 --- a/src/test/test_ctfe.pr +++ /dev/null @@ -1,70 +0,0 @@ -import test::testsuite - -def test_constants { - // We can't use pointers here - const c = 4 - tassert(c == 4) -} - -def sum(a: int, b: int) -> int { - return a + b -} - -def test_func_call { - // TODO Varargs don't work for some reason - const sum = sum(10, 20) - tassert(sum == 30) -} - -def test_static_if { - const s = sum(10, 20) - #if s > 10 { - var a: int = 10 - } else { - var a: int = 20 - } - tassert(a == 10) -} - -def return_random_number -> long { - cstd::srand(100) - return cstd::rand() -} - -def test_c_functions { - const ac = return_random_number() - let al = return_random_number() - - tassert(ac == al) -} - -def append_str(a: string, b: string) -> string { - let res = a + b - return @res -} - -const hello_world = append_str("Hello ", "World!") - -def test_strings { - tassert(hello_world == "Hello World!") -} - -def test_bug_1_ -> bool { - return true == (true == true) -} - -def test_bug_1 { - const ac = test_bug_1_() - let al = test_bug_1_() - tassert(ac == al == true) -} - -export def run_tests { - print("Running tests on compile time functions... \n") - run_test("test_constants", *test_constants) - run_test("test_func_call", *test_func_call) - run_test("test_static_if", *test_static_if) - run_test("test_c_functions", *test_c_functions) - run_test("test_strings", *test_strings) - run_test("test_bug_1", *test_bug_1) -} \ No newline at end of file diff --git a/src/test/test_expr_eval.pr b/src/test/test_expr_eval.pr deleted file mode 100644 index 098ca8c7..00000000 --- a/src/test/test_expr_eval.pr +++ /dev/null @@ -1,53 +0,0 @@ -import test::testsuite - -type Node = interface { - def walk() -> int -} - -type Add = struct { - left: &Node - right: &Node -} - -def walk(add: &Add) -> int { - return add.left.walk() + add.right.walk() -} - -type Sub = struct { - left: &Node - right: &Node -} - -def walk(sub: &Sub) -> int { - return sub.left.walk() - sub.right.walk() -} - -type Value = struct { - value: int -} - -def walk(value: &Value) -> int { - return value.value -} - -def test_expr { - // 10 + (40 - 20) + 40 - let expr = { - { - {10} !&Value, - { - {40} !&Value, - {20} !&Value - } !&Sub - } !&Add, - {40} !&Value - } !&Add - - let result = walk(expr) - tassert(result == 70) -} - -export def run_tests { - print("Running expression test... \n") - run_test("test_expr", *test_expr) -} \ No newline at end of file diff --git a/src/test/test_json.pr b/src/test/test_json.pr deleted file mode 100644 index ca0f9cd8..00000000 --- a/src/test/test_json.pr +++ /dev/null @@ -1,208 +0,0 @@ -import test::testsuite -import json -import optional -import util - -def test_numbers { - let data = """ - { - "key_a": 10, - "key_b": -10, - "key_c": 10.5, - "key_d": 10E2, - "key_e": 10e2, - "key_f": 10e+2, - "key_g": 10e-2, - "key_h": 0 - } - """ - let obj = json::parse(data) - tassert(obj["key_a"].as_double() == 10) - tassert(obj["key_b"].as_double() == -10) - tassert(obj["key_c"].as_double() == 10.5) - tassert(obj["key_d"].as_double() == 10E2) - tassert(obj["key_e"].as_double() == 10e2) - tassert(obj["key_f"].as_double() == 10e+2) - tassert(obj["key_g"].as_double() == 10e-2) - tassert(obj["key_h"].as_double() == 0) -} - -def test_arrays { - let data = """ - { - "key_a": [1, 2, 3], - "key_b": ["a", "b", "c"], - "key_c": [ - {"1": 1}, - {"2": 2}, - {"3": 3} - ] - } - """ - let obj = json::parse(data) - let key_a = obj["key_a"] - tassert(key_a[0].as_double() == 1 and key_a[1].as_double() == 2 and key_a[2].as_double() == 3) - let key_b = obj["key_b"] - tassert(key_b[0].as_string() == "a" and key_b[1].as_string() == "b" and key_b[2].as_string() == "c") - let key_c = obj["key_c"] - tassert(key_c[0]["1"].as_double() == 1 and key_c[1]["2"].as_double() == 2 and key_c[2]["3"].as_double() == 3) -} - -def test_objects { - let data = """ - { - "key_a": { - "1": 1, - "2": 2 - }, - "key_b": { - "1": 1, - "2": 2 - } - } - """ - let obj = json::parse(data) - let key_a = obj["key_a"] - tassert(key_a["1"].as_double() == 1 and key_a["2"].as_double() == 2) - let key_b = obj["key_b"] - tassert(key_b["1"].as_double() == 1 and key_b["2"].as_double() == 2) -} - -def test_values { - let data = """ - { - "key_a": null, - "key_b": true, - "key_c": false - } - """ - let obj = json::parse(data) - tassert(obj["key_a"].is_null()) - tassert(obj["key_b"].as_bool() == true) - tassert(obj["key_c"].as_bool() == false) -} - -def test_strings { - let data = """ - { - "key_a": "some string", - "key_b": "\\" \\\\ \\b \\f \\n \\r \\t", - "key_c": "\\uAABB" - } - """ - let obj = json::parse(data) - tassert(obj["key_a"].as_string() == "some string") - tassert(obj["key_b"].as_string() == "\" \\ \b \f \n \r \t") - tassert(obj["key_c"].as_string() == "\uAABB") -} - -type A = struct { - a: int - b: double - c: B - d: [5; int] -} -type B = struct { - a: int - s: &string -} - -def test_serialization { - let value = { a = 10, b = 20.5, c = { a = 20, s = "some string" } !B, d = [1, 2, 3, 4, 5] } !A - let obj = json::serialize(*value) - print(obj) - - let value2 = json::deserialize(obj, A).get() - tassert(value2.a == value.a) - tassert(value2.b == value.b) - tassert(value2.c.a == value.c.a) - for var i in 0..value2.d.size { - tassert(value2.d[i] == value.d[i]) - } -} - -def test_equality { - let data1 = """ - { - "key_1": 0, - "key_2": 1 - } - """ - let data2 = """ - { - "key_2": 1, - "key_1": 0 - } - """ - let data3 = """ - { - "key_1": 0 - } - """ - - tassert(json::parse(data1) == json::parse(data2)) - tassert(json::parse(data1) != json::parse(data3)) -} - -def test_generation { - let data = """ - { - "key_1": 0, - "key_2": true, - "key_3": "some_string", - "key_4": null, - "key_5": { - "foo": 10, - "bar": 20 - }, - "key_6": [ - 10, 20, 30, 40 - ] - } - """ - let obj = json::parse(data) - let obj2 = json::make_object() - obj2["key_1"] = 0 - obj2["key_2"] = true - obj2["key_3"] = "some_string" - obj2["key_4"] = json::make_null() - let obj3 = json::make_object() - obj3["foo"] = 10 - obj3["bar"] = 20 - obj2["key_5"] = obj3 - let array = json::make_array() - array.push(10) - array.push(20) - array.push(30) - array.push(40) - obj2["key_6"] = array - - tassert(obj == obj2) -} - -export def run_tests { - print("Running json tests... \n") - run_test("test_numbers", *test_numbers) - run_test("test_arrays", *test_arrays) - run_test("test_objects", *test_objects) - run_test("test_values", *test_values) - run_test("test_strings", *test_strings) - run_test("test_serialization", *test_serialization, strip_margin("""\ - |{ - | "a": 10.00000, - | "b": 20.50000, - | "c": { - | "a": 20.00000, - | "s": "some string" - | }, - | "d": [ - | 1.00000, - | 2.00000, - | 3.00000, - | 4.00000, - | 5.00000 - | ] - |}"""), "") - run_test("test_equality", *test_equality) - run_test("test_generation", *test_generation) -} \ No newline at end of file diff --git a/src/test/test_lexer.pr b/src/test/test_lexer.pr deleted file mode 100644 index 7f26d3c9..00000000 --- a/src/test/test_lexer.pr +++ /dev/null @@ -1,291 +0,0 @@ -import lexer -import test::testsuite - -def next_value(list: **lexer::TokenList, tpe: lexer::TokenType) -> lexer::Token { - let value = (@list).value - if value.tpe == lexer::TokenType::ERROR { - error("Error: ", value.value.str, "\n") - exit(2) - } - tassert(value.tpe == tpe) - @list = (@list).next - return value -} - -def next_char(list: **lexer::TokenList) -> char { - return next_value(list, lexer::TokenType::CHAR).value.ch -} - -def next_long(list: **lexer::TokenList) -> uint64 { - return next_value(list, lexer::TokenType::INTEGER).value.i -} - -def next_double(list: **lexer::TokenList) -> double { - return next_value(list, lexer::TokenType::FLOAT).value.f -} - -def next_string(list: **lexer::TokenList) -> &string { - return next_value(list, lexer::TokenType::STRING).value.str -} - -def next_identifier(list: **lexer::TokenList) -> &string { - return next_value(list, lexer::TokenType::IDENTIFIER).value.str -} - -def next_comment(list: **lexer::TokenList) -> &string { - return next_value(list, lexer::TokenType::COMMENT).value.str -} - -def next_pragma(list: **lexer::TokenList) -> &string { - return next_value(list, lexer::TokenType::PRAGMA).value.str -} - -def next_error(list: **lexer::TokenList) -> &string { - let value = (@@list).value - if value.tpe != lexer::TokenType::ERROR { - error("Error: Invalid token type: ", value.tpe, "\n") - exit(2) - } - let s = value.value.str - @list = (@@list).next - return s -} - -def test_float_literal { - var str = "10.5" - var result = lexer::lex(str) - tassert(next_double(*result) == 10.5) - - str = ".5" - result = lexer::lex(str) - tassert(next_double(*result) == .5) - - str = "10." - result = lexer::lex(str) - tassert(next_double(*result) == 10.) - - str = "10E10" - result = lexer::lex(str) - tassert(next_double(*result) == 10E10) - - str = "10.e10" - result = lexer::lex(str) - tassert(next_double(*result) == 10.e10) - - /*str = "10E-4" - result = lexer::lex(str) - tassert(next_double(*result) == 10E-4)*/ // TODO This doesnt work - - str = "10E+10" - result = lexer::lex(str) - tassert(next_double(*result) == 10E+10) -} - -def test_int_literal { - var str = "156" - var result = lexer::lex(str) - tassert(next_long(*result) == 156) - - str = "0b100100" - result = lexer::lex(str) - tassert(next_long(*result) == 0b100100) - - str = "0xDEADBABE" - result = lexer::lex(str) - tassert(next_long(*result) == 0xDEADBABE) - - str = "0o172" - result = lexer::lex(str) - tassert(next_long(*result) == 0o172) -} - -def test_char_literal { - var str = "'A'" - var result = lexer::lex(str) - tassert(next_char(*result) == 'A') - - str = "'A' 'B'" - result = lexer::lex(str) - tassert(next_char(*result) == 'A') - next_value(*result, lexer::TokenType::WHITESPACE) - tassert(next_char(*result) == 'B') - - str = "'\\a'" - result = lexer::lex(str) - tassert(next_char(*result) == '\a') - - str = "'\\xFF'" - result = lexer::lex(str) - tassert(next_char(*result) == '\xFF') - -} - -def test_char_literal_error { - var str = "'A" - var result = lexer::lex(str) - tassert(next_error(*result) == "Unexpected end of file while parsing character") - - str = "'\\d'" - result = lexer::lex(str) - tassert(next_error(*result) == "Invalid escape sequence") - - str = "'\\x" - result = lexer::lex(str) - tassert(next_error(*result) == "Invalid escape sequence") - - //TODO Test more corner cases -} - -def test_string_literal { - var str = "\"this is a test\"" - var result = lexer::lex(str) - tassert(next_string(*result) == "this is a test") - - str = "\"test\" \"more\"" - result = lexer::lex(str) - tassert(next_string(*result) == "test") - next_value(*result, lexer::TokenType::WHITESPACE) - tassert(next_string(*result) == "more") - - str = "\"\\a\\b\\f\\n\\r\\t\\v\\'\\\"\\\\\"" - result = lexer::lex(str) - tassert(next_string(*result) == "\a\b\f\n\r\t\v\'\"\\") - - str = "\"\\xFF\"" - result = lexer::lex(str) - tassert(next_string(*result) == "\xFF") - - str = "\"\\u01FF\"" - result = lexer::lex(str) - tassert(next_string(*result) == "\u01FF") - - str = "\"\\u88AA\"" - result = lexer::lex(str) - tassert(next_string(*result) == "\u88AA") - - str = "\"\\U0010FFFF\"" - result = lexer::lex(str) - tassert(next_string(*result) == "\U0010FFFF") - - str = "\"\n\n\"" - result = lexer::lex(str) - tassert(next_string(*result) == "\n\n") -} - -def test_string_literal_error { - var str = "\"this is a test" - var result = lexer::lex(str) - tassert(next_error(*result) == "Unexpected end of file while parsing string literal") - - str = "\"test \\d \"" - result = lexer::lex(str) - tassert(next_error(*result) == "Invalid escape sequence") - - str = "\"\\UGHRR\"" - result = lexer::lex(str) - tassert(next_error(*result) == "Invalid escape sequence") - - str = "\"\\UFF0000FF\"" - result = lexer::lex(str) - tassert(next_error(*result) == "Invalid unicode sequence") -} - -def test_identifier { - var str = "foo_bar" - var result = lexer::lex(str) - tassert(next_identifier(*result) == "foo_bar") - - str = "foo bar" - result = lexer::lex(str) - tassert(next_identifier(*result) == "foo") - next_value(*result, lexer::TokenType::WHITESPACE) - tassert(next_identifier(*result) == "bar") - - str = "def foo" - result = lexer::lex(str) - next_value(*result, lexer::TokenType::K_DEF) - next_value(*result, lexer::TokenType::WHITESPACE) - tassert(next_identifier(*result) == "foo") -} - -def test_pragma { - var str = "#union" - var result = lexer::lex(str) - tassert(next_pragma(*result) == "#union") - - str = "##compiler_dep" - result = lexer::lex(str) - tassert(next_pragma(*result) == "##compiler_dep") -} - -def test_comment { - var str = "//This is a test" - var result = lexer::lex(str) - tassert(next_comment(*result) == "//This is a test") - - str = "//This - //Test - " - result = lexer::lex(str) - tassert(next_comment(*result) == "//This") - next_value(*result, lexer::TokenType::NEW_LINE) - next_value(*result, lexer::TokenType::WHITESPACE) - tassert(next_comment(*result) == "//Test") - - str = "/*This*/" - result = lexer::lex(str) - tassert(next_comment(*result) == "/*This*/") - - str = "/*Nested/*Comment*/*/" - result = lexer::lex(str) - tassert(next_comment(*result) == "/*Nested/*Comment*/*/") -} - -def test_symbols { - var str = "foo+bar" - var result = lexer::lex(str) - tassert(next_identifier(*result) == "foo") - next_value(*result, lexer::TokenType::OP_ADD) - tassert(next_identifier(*result) == "bar") - - str = "1..2" - result = lexer::lex(str) - tassert(next_long(*result) == 1) - next_value(*result, lexer::TokenType::OP_RANGE) - tassert(next_long(*result) == 2) - - // TODO add more tests -} - -def test_complex { - var str = " - type T = struct { - a: int - b: string - } - - def main(a: int, b: unsigned word) -> T { - print(\"A: \", a, \" B: \", b) - var t: T = { - a = 0, b = \"Test string\" - } !T - } - " - var result = lexer::lex(str) - //lexer::print_token_list(result) -} - -export def run_tests { - print("Running tests on Lexer...\n") - run_test("test_string_literal", *test_string_literal) - run_test("test_string_literal_error", *test_string_literal_error) - run_test("test_char_literal", *test_char_literal) - run_test("test_char_literal_error", *test_char_literal_error) - run_test("test_int_literal", *test_int_literal) - run_test("test_float_literal", *test_float_literal) - run_test("test_identifier", *test_identifier) - run_test("test_pragma", *test_pragma) - run_test("test_comment", *test_comment) - run_test("test_symbols", *test_symbols) - run_test("test_complex", *test_complex) -} \ No newline at end of file diff --git a/src/test/test_parser.pr b/src/test/test_parser.pr deleted file mode 100644 index 26ffcafe..00000000 --- a/src/test/test_parser.pr +++ /dev/null @@ -1,761 +0,0 @@ -import parser -import lexer -import vector -import util -import debug - -import test::testsuite - -export var print_ast = false - -def parse(s: &string) -> &parser::Node { - let tokens = lexer::lex(s) - let lines = util::split_lines(s) - let node = parser::parse(tokens, lines, "main", "main") - - if print_ast { - print("\n") - print(s) - print("\n") - debug::print_node(node) - } - - return node -} - -def test_identifiers { - var str = "foo" - var res = parse(str) - - str = "foo::bar::baz" - res = parse(str) - - str = "foo::bar::" - res = parse(str) - - str = "foo::bar::()" - res = parse(str) - - str = "foo::bar::(int)" - res = parse(str) - - str = "foo::bar::(int, () -> ())" - res = parse(str) -} - -def test_operators { - var str = "1 + 2" - var res = parse(str) - tassert((@res).kind == parser::NodeKind::PROGRAM) - var val = @((@res).body[0]) - tassert(val.kind == parser::NodeKind::ADD) - var left = @val.value.bin_op.left - var right = @val.value.bin_op.right - tassert(left.kind == parser::NodeKind::INTEGER) - tassert(right.kind == parser::NodeKind::INTEGER) - tassert(left.value.i == 1) - tassert(right.value.i == 2) - - str = "1 + 2 * 3 - 5" - res = parse(str) - - str = "1 << 5 + 0xFF >> 1" - res = parse(str) - - str = "0 and 2 or 5" - res = parse(str) - - str = "a == 5 and b != 6 or c <= 10 and d >= 5" - res = parse(str) - - str = "10 > a > 5" - res = parse(str) - - str = "not true" - res = parse(str) - - str = "not not true" - res = parse(str) - - str = "*foo and @bar and ~foo" - res = parse(str) - - str = "+foo and -foo" - res = parse(str) - - str = "5 !int" - res = parse(str) - - str = "5 * 5 !int" - res = parse(str) - - str = "foo[1 + 5][1]" - res = parse(str) - - str = "foo.bar.baz" - res = parse(str) - - str = "a..b" - res = parse(str) - - str = "a..=b..c" - res = parse(str) - - str = "size_of foo" - res = parse(str) - - str = "size_of type *Foo" - res = parse(str) - - str = "align_of foo" - res = parse(str) - - str = "align_of type *Foo" - res = parse(str) - - str = "type_of a" - res = parse(str) - - str = "(type type_of a)" - res = parse(str) -} - -def test_vardecl { - var str = "var foo = 5" - var res = parse(str) - - str = "const foo = 5" - res = parse(str) - - str = "var foo, bar = 1, 2" - res = parse(str) - - str = "let foo, (bar) = 1, 2" - res = parse(str) - - str = "export var foo" - res = parse(str) - - str = "export import var bar" - res = parse(str) - - str = "var foo: int, bar: int" - res = parse(str) - - str = "export let foo: int = 5" - res = parse(str) -} - -def test_typedecl { - var str = "type A" - var res = parse(str) - - str = "export type A, B" - res = parse(str) - - str = "type A = Foo" - res = parse(str) - - str = "type A = unsigned word(16)" - res = parse(str) - - str = "type A, B = int, float" - res = parse(str) -} - -def test_simple_types { - var str = "(type int)" - var res = parse(str) - - str = "(type word(16))" - res = parse(str) - - str = "(type unsigned word(32))" - res = parse(str) -} - -def test_function_types { - var str = "(type T -> F)" - var res = parse(str) - - str = "(type ->)" - res = parse(str) - - str = "(type (->))" - res = parse(str) - - str = "(type A -> B -> C)" - res = parse(str) - - str = "(type () -> ())" - res = parse(str) - - str = "(type (A, B) -> (C, D))" - res = parse(str) -} - -def test_pointer_types { - var str = "(type *A)" - var res = parse(str) - - str = "(type **A)" - res = parse(str) - - str = "(type &A)" - res = parse(str) - - str = "(type &&**A)" - res = parse(str) - - str = "(type *)" - res = parse(str) - - str = "(type &*)" - res = parse(str) - - str = "(type *let A)" - res = parse(str) - - str = "(type *var A)" - res = parse(str) - - str = "(type &let A)" - res = parse(str) - - str = "(type &var A)" - res = parse(str) - - str = "(type weak_ref)" - res = parse(str) - - str = "(type weak_ref(T))" - res = parse(str) - - str = "(type weak_ref(var T))" - res = parse(str) - - str = "(type weak_ref(let *T))" - res = parse(str) - - str = "(type weak_ref())" - res = parse(str) - - str = "(type weak_ref(let))" - res = parse(str) - - str = "(type weak_ref(var))" - res = parse(str) -} - -def test_array_types { - var str = "(type [T])" - var res = parse(str) - - str = "(type [5; T])" - res = parse(str) - - str = "(type [?; T])" - res = parse(str) - - str = "(type [let T])" - res = parse(str) - - str = "(type [5; var T])" - res = parse(str) - - str = "(type [5; [T]])" - res = parse(str) - - str = "(type [5; var [let T]])" - res = parse(str) -} - -def test_func_call { - var str = "really::long::path::foo()" - var res = parse(str) - - str = "foo(bar, baz)" - res = parse(str) - - str = "foo(bar, baz = 20)" - res = parse(str) - - str = "foo(0)(1)" - res = parse(str) -} - -def test_assign_op { - var str = "a = b" - var res = parse(str) - - str = "a, b = c, d" - res = parse(str) - - str = "a, b = c, d = e, f" - res = parse(str) - - str = "a += 2" - res = parse(str) - - str = "a *= 2 %= 3" - res = parse(str) - - str = "a ++= 2 = b += 2" - res = parse(str) -} - -def test_statements { - var str = " - var foo = 20 - foo += 10 - print(foo) - " - var res = parse(str) - - str = " - #if foo { - // Do something - } else if bar { - // Do something else - } else { - // Do more - } - " - res = parse(str) - - str = " - if foo == null { - print(\"Hello World\") - } - end - " - res = parse(str) - - str = " - if true { - // Do something - } else { - // Do something else - } - end - " - res = parse(str) - - str = " - if true { - print(0) - } else if false { - print(0) - print(1) - } else if true { - // Do more - } else { - // Or else...! - } - end - " - res = parse(str) - - str = " - if true { - print(0) - } else if false { - print(1) - } - end - " - res = parse(str) - - str = " - if must { - if go { - if deeper { - - } - } - } - end - " - res = parse(str) - - str = " - if false - { - // on new line - } - else - { - // Very ugly - } - end - " - res = parse(str) - - str = " - loop { - nop() - break - continue - } - end - " - res = parse(str) - - str = " - for var i in 0..10 { - nop() - } - end - " - res = parse(str) - - str = " - var i = 0 - for i in 0..10 { - nop() - } - end - " - res = parse(str) - - str = " - while true { - nop() - } - end - " - res = parse(str) - - str = "return" - res = parse(str) - - str = " - return 1, 2, 3 - " - res = parse(str) - - str = " - return 1, - 2 - " - res = parse(str) -} - -def test_yield { - var str = " - yield - " - var res = parse(str) - - str = " - yield 10 - " - res = parse(str) - - str = " - yield 10, - 20 - " - res = parse(str) -} - -def test_struct { - var str = " - type T = struct { - a: int - } - " - var res = parse(str) - - str = " - type T = struct { - a: int - b: struct { - a: int - } - } - " - res = parse(str) - - str = " - type T = struct #union { - a: int - b: long - } - " - res = parse(str) - - str = " - type T = struct { - a: int - struct #union { - a: int - b: long - } - } - " - res = parse(str) -} - -def test_import { - var str = "import module" - var res = parse(str) - - str = "import module as mod" - res = parse(str) - - str = "import a as foo, bar" - res = parse(str) - - str = " - import - Module as mod, - foo, - bar as baz - " - res = parse(str) -} - -def test_enum { - var str = " - type T = enum { - FOO - BAR - BAZ - } - " - var res = parse(str) - - str = " - type T = enum { - FOO = 10 - BAR - } - " - res = parse(str) - - str = " - type T = enum: long { - FOO; BAR - } - " - res = parse(str) -} - -def test_switch_stmt { - var str = " - switch value {} - " - var res = parse(str) - - str = " - switch value { - case 1: one - case 2: two - case: default - } - " - res = parse(str) - - str = " - switch value { - case 1: - print(foo) - print(bar) - case 2: - print(bar) - print(foo) - } - " - res = parse(str) - - str = " - switch value { - case 1..10: - print(foo) - case 11..20: - print(bar) - } - " - res = parse(str) - - str = " - switch value { - case 10, 20: - one - case 20..30, 30..40: - two - } - " - res = parse(str) -} - -def test_def { - var str = " - def test - " - var res = parse(str) - - str = " - def test(a: int) - " - res = parse(str) - - str = " - def test(type T, var a: int, let b: int) - " - res = parse(str) - - str = " - def test -> int, float - " - res = parse(str) - - str = " - def test() { - return - } - " - res = parse(str) - - str = " - def test(var a: int) -> int { - return a - } - " - res = parse(str) - - str = " - def test(type T = *int, a: int = 0) - " - res = parse(str) - - str = " - def test(a: type T) -> T - " - res = parse(str) - - str = " - def test(a: type [T]) -> T - " - res = parse(str) - - str = " - def test(a: type [type T]) -> T - " - res = parse(str) -} - -def test_array_literal { - var str = " - [1, 2, 3] - " - var res = parse(str) - - str = " - [ - 1, - 2, - 3 - ] - " - res = parse(str) -} - -def test_struct_literal { - var str = " - {foo, bar} !Struct - " - var res = parse(str) - - str = " - {foo, bar = 20, baz = 40} !Struct - " - res = parse(str) - - str = " - { - foo, - bar = 20, - baz = 40 - } !Struct - " - res = parse(str) -} - -def test_if_expression { - var str = " - let a = 10 if 10 > 20 else 20 - " - var res = parse(str) - - str = " - let a = 10 if 10 > 20 else 20 if 20 > 30 else 40 - " - res = parse(str) - - str = " - foo(10 if true else 20) - " - res = parse(str) -} - -def test_closures { - var str = " - def fun { - def fun { - def fun { - def fun { - - } - } - } - } - " - var res = parse(str) -} - -def test_bug_1 { - var str = " - type T = struct { - a: int - // New line here - } - type E = struct { } - " - var res = parse(str) -} - -def test_bug_2 { - var str = " - var a = 20; a += 10 - " - var res = parse(str) -} - -export def run_tests { - print("Running tests on Parser...\n") - run_test("test_identifiers", *test_identifiers) - run_test("test_operators", *test_operators) - run_test("test_simple_types", *test_simple_types) - run_test("test_function_types", *test_function_types) - run_test("test_pointer_types", *test_pointer_types) - run_test("test_array_types", *test_array_types) - run_test("test_vardecl", *test_vardecl) - run_test("test_typedecl", *test_typedecl) - run_test("test_func_call", *test_func_call) - run_test("test_assign_op", *test_assign_op) - run_test("test_statements", *test_statements) - run_test("test_yield", *test_yield) - run_test("test_struct", *test_struct) - run_test("test_import", *test_import) - run_test("test_enum", *test_enum) - run_test("test_switch_stmt", *test_switch_stmt) - run_test("test_def", *test_def) - run_test("test_array_literal", *test_array_literal) - run_test("test_struct_literal", *test_struct_literal) - run_test("test_if_expression", *test_if_expression) - run_test("test_closures", *test_closures) - run_test("test_bug_1", *test_bug_1) - run_test("test_bug_2", *test_bug_2) -} \ No newline at end of file diff --git a/src/test/test_runtime.pr b/src/test/test_runtime.pr deleted file mode 100644 index 85b43b9e..00000000 --- a/src/test/test_runtime.pr +++ /dev/null @@ -1,572 +0,0 @@ -import util -import test::testsuite - -import runtime - -def test_loop { - var cnt = 0 - while cnt < 10 { - print(cnt) - cnt += 1 - } - print("\n") - - for var i in 0..10 { - print(i) - } - print("\n") - - let foo: *int = null - while foo != null { - @foo = 10 - } - - for var i in 0..10 { - if i == 5 { continue } - print(i) - } - print("\n") -} - -type Point = struct { - x: int - y: int -} - -def test_print { - let a = 20 - let b = 0xDEADBABE !* - print("Hello World ", 1, " ", 'x', " ", 10.5, " ", b, "\n") - error("Hello World ", 1, " ", 'x', " ", 10.5, " ", b, "\n") - - let point: Point = {10, 20} - print(point, "\n") - - let array = [1, 2, 3, 4] - print(array, "\n") -} - -def pass_dynamic_array(a: string) { - print(a) -} - -def function(a: int, b: double) -> double { return a * b } -def function(b: double, a: int) -> double { return a * b } -def function -> int { return 10 } - -def test_function_calls { - pass_dynamic_array("Some string\n") - pass_dynamic_array(a = "Named parameter\n") - - function(2, 1.5) - function(1.5, 2) - function(2, b = 1.5) - function(1.5, a = 2) - // function(a = 1, b = 1.5) # Ambiguous reference - - tassert(function == 10) -} - -def test_length { - let a = "Some string" - tassert(length(a) == 11) -} - -def test_allocate { - let a = allocate(size_of int) !*int - @a = 10 - tassert(@a == 10) - free(a) - - let b = allocate(int) - @b = 20 - tassert(@b == 20) - free(b) - - let c = allocate(int, 10) - c[0] = 10 - c[9] = 20 - tassert(c[0] == 10) - tassert(c[9] == 20) - free(c) -} - -def test_file_binary { - let fp = open("tmp/test_file_io_binary", "wb+") - - let str = "Some text" - write(fp, str) - let i = 10 - write(fp, *i) - - rewind(fp) - - var str2: [10; char] - read(fp, str2) - print(str2, "\n") - - var i2: int - read(fp, *i2) - print(i2, "\n") - - close(fp) -} - -def test_file_text { - let fp = open("tmp/test_file_io_text", "w+") - - fprint(fp, "This is a test\n", 10) - - seek(fp, 0, SEEK_SET) // Same as rewind - - var buffer: [20; char] - read_line(fp, buffer) - print(buffer) - var num: int - cstd::fscanf(fp, "%d".value, *num) - print(num, "\n") - - close(fp) -} - -def test_operators { - - let g = true and false - let h = true and true - let i = false and true - tassert(not g) - tassert(h) - tassert(not i) -} - -type Enum = enum { - A = 10; - B; C; D -} - -type Enum2 = enum { - A = 10 - B = A - C -} - -def pass_enum(a: Enum) {} - -def test_enum { - tassert(Enum::A == 10) - tassert(Enum::B == 11) - pass_enum(Enum::A) - let a = Enum::A !int - let b = 10 !Enum - pass_enum(b) - - print(to_string(Enum::A), "\n") - print(Enum::B, "\n") -} - -def some_function { - print("Hello\n") -} - -def test_function_pointers { - let function = *some_function - function() -} - -type Struct = struct { - a: string - b: int - c: char -} - -def test_structs { - let s = { - c = 10 - } !Struct -} - -type Union = struct #union { - a: string - b: long - c: int -} - -def test_unions { - let u = { - "some string" - } !Union - print(u, "\n") - - let u2 = { - b = 20 - } !Union - u2.b = 120 - print(u2.c, "\n") - - let u3 = {} !Union -} - -def test_strings { - let stra = "Some value" - tassert(stra == "Some value") - tassert(stra != "Other value") -} - -def test_if_stmts { - if false { - print("false\n") - } else if starts_with("foo", "f") { - print("true\n") - } else { - print("false\n") - } -} - -def fact(n: int) -> int { - if n <= 1 { return 1 } - else { - return n * fact(n - 1) - } -} - -def test_recursion { - tassert(fact(10) == 3628800) -} - -def test_scoping { - var a = 10 - if true { - a = 10 - var a = 20 - } -} - -def pass_array(a: [int]) { - for var i in 0..a.size { - print(a[i], " ") - } - print("\n") -} - -def test_arrays { - let a = [10, 20, 30] - let b = [] ![int] - pass_array(a) - pass_array(b) -} - -type Struct2 = struct { - a: int - t: *Struct2 -} - -def test_deref { - let s = allocate(Struct2) - s.a = 10 - s.t = allocate(Struct2) - s.t.a = 20 - print(s.a, " ", s.t.a, "\n") -} - -def function(s: Struct2) -> int { return s.a } -def function(s: Struct2, a: int) -> int { return s.a + a } -def inc(a: int) -> int { return a + 1} - -def test_ucs { - let s: Struct2 = { 10, null } - - tassert(s.function() == 10) - tassert(s.function.inc == 11) - tassert(s.function(10).inc() == 21) -} - -type Struct3 = struct { - a: int - b: struct { - c: int - d: int - } - struct #union { - e: int64 - f: double - } -} - -def test_anonymous { - var s: Struct3 - s = {10, {20, 30}} - s.a = 10 - s.b = {20, 30} - s.e = 0x4034800000000000 - - print(s.f, "\n") -} - -def sum(args: int...) -> int { - var sum = 0 - for var i in 0..args.size { - sum += args[i] - } - return sum -} - -def my_print(args: string...) { - for var i in 0..args.size { - print(args[i], " ") - } - print("\n") -} - -def test_varargs { - tassert(sum(1, 2, 3) == 6) - tassert(sum([1, 2, 3]) == 6) - my_print("foo", "bar", "baz") -} - -def test_if_expression { - // TODO I've seen these fail but I can't quite pin down what's wrong with them - var a = 10 if 10 > 20 else 20 - tassert(a == 20) - var b: int - @(*a if 10 > 20 else *b) = 30 - tassert(b == 30) - - let c = { c = 10 } !Struct - let d = { c = 0 } !Struct if 10 > 20 else c - tassert(d.c == 10) -} - -import module::a as A -import module::b - -def test_imports { - tassert(A::multiply_by_2(10) == 20) - let a, b = return_multiple() - tassert(a == 10) - tassert(b == 20) - print(A::Enum::SOME_ENUM_VALUE, "\n") - - let c = {10, 10} !A::Point - let d = module::b::some_value -} - -def takes_default_param(a: int = 0, b: string = "some value") { - print(a, " ", b, "\n") -} - -def test_default_parameters { - takes_default_param() - takes_default_param(10) - takes_default_param(20, "Hello world!") -} - -def test_function_overloads { - let f1 = *function::() - let f2 = *function::(int, double) - let f3 = *function::(double, int) - - tassert(f1() == 10) - tassert(f2(10, 10.5) == 105) - tassert(f3(10.5, 10) == 105) -} - -def test_underscore { - let _, _ = 10, 20 - tassert(_ == 20) - _ = "Hello World!" - tassert(_ == "Hello World!") -} - -def function(type T) -> size_t { - return T.size -} - -def add(a: type T, b: T) -> T { - return a + b -} - -def test_def_polymorph { - tassert(function(int) == 4) - tassert(function(Struct) == 24) - - tassert(add(10, 10) == 20) - tassert(add(10.5, 20.5) == 31.0) -} - -def test_type_of { - let a = 20 - let b: (type_of a) = 30 - type T = type_of a - - tassert(T == int) - tassert((type_of b) == int) -} - -type Struct4 = struct { a: int; b: float } -type Interface = interface { - def function(a: int) -> int -} - -def function(s: Struct4, a: int) -> int { - return s.a + a -} - -def test_reflection { - type T = int - tassert(T.kind == runtime::TypeKind::WORD) - tassert(T.name == "int32") - tassert(T.unsig == false) - - tassert(Struct4.kind == runtime::TypeKind::STRUCT) - tassert(Struct4.fields.size == 2) - tassert(Struct4.fields[0].tpe == int) - tassert(Struct4.fields[0].name == "a") - tassert(Struct4.fields[1].tpe == float) - tassert(Struct4.fields[1].name == "b") - - tassert(Struct4.type_members.size == 1) - let member = Struct4.type_members[0] - tassert(member.name == "test_runtime::function::(test_runtime::Struct4, int32)") - tassert(member.parameter_t.size == 2) - tassert(member.parameter_t[0] == Struct4) - tassert(member.parameter_t[1] == int) - tassert(member.return_t.size == 1) - tassert(member.return_t[0] == int) - - tassert(Interface.kind == runtime::TypeKind::STRUCTURAL) - tassert(Interface.structural_members.size == 1) - let smember = Interface.structural_members[0] - tassert(smember.name == "function") - tassert(smember.parameter_t.size == 1) - tassert(smember.parameter_t[0] == int) - tassert(smember.return_t.size == 1) - tassert(smember.return_t[0] == int) - - tassert(runtime::implements(Struct4, Interface)) -} - -def range_for(a: int, b: int) -> int { - for var i in a..b { - yield i - } -} - -def range_while(a: int, b: int) -> int { - var i = a - while i < b { - yield i - i = i + 1 - } -} - -def test_yield { - for var i in range_for(0, 10) { - print(i, " ") - } - print("\n") - - for var i in range_while(0, 10) { - print(i, " ") - } - print("\n") -} - -type Struct5 = struct { - a: int; b: int; c: int -} - -def iterate(s: *Struct5) -> int { - yield s.a - yield s.b - yield s.c -} - -def test_generators { - let s = { 10, 20, 30 } !Struct5 - - for var i in *s { - print(i, " ") - } - print("\n") -} - -def test_closures { - def inner(a: int) -> int { - def inner -> int { - return a - } - return inner() - } - tassert(inner(10) == 10) - - var a = 20 - var b = 30 - def capture { - tassert(a == 20) - let local = *b - @local = 40 - tassert(@local == 40) - } - capture() - tassert(b == 40) -} - -export def run_tests { - print("Running tests on runtime... \n") - run_test("test_imports", *test_imports, "0\n", "") - run_test("test_loop", *test_loop, strip_margin("\ - |0123456789 - |0123456789 - |012346789\n"), "") - run_test("test_print", *test_print, strip_margin("\ - |Hello World 1 x 10.500000 0xdeadbabe - |{x = 10, y = 20} !test_runtime::Point - |[1, 2, 3, 4]\n"), - strip_margin("\ - |Hello World 1 x 10.500000 0xdeadbabe\n")) - run_test("test_function_calls", *test_function_calls, strip_margin("\ - |Some string - |Named parameter\n"), "") - run_test("test_length", *test_length) - run_test("test_allocate", *test_allocate) - run_test("test_file_binary", *test_file_binary, strip_margin("\ - |Some text - |10\n"), "") - run_test("test_file_text", *test_file_text, strip_margin("\ - |This is a test - |10\n"), "") - run_test("test_operators", *test_operators) - run_test("test_enum", *test_enum, strip_margin("\ - |A - |11\n"), "") - run_test("test_function_pointers", *test_function_pointers, "Hello\n", "") - run_test("test_structs", *test_structs) - run_test("test_unions", *test_unions, strip_margin("\ - |{a = some string, b = 12, c = 12} !test_runtime::Union - |120\n"), "") - run_test("test_strings", *test_strings) - run_test("test_if_stmts", *test_if_stmts, "true\n", "") - run_test("test_recursion", *test_recursion) - run_test("test_scoping", *test_scoping) - run_test("test_arrays", *test_arrays, "10 20 30 \n\n", "") - run_test("test_deref", *test_deref, "10 20\n", "") - run_test("test_ucs", *test_ucs) - run_test("test_anonymous", *test_anonymous, "20.500000\n", "") - run_test("test_varargs", *test_varargs, "foo bar baz \n", "") - run_test("test_if_expression", *test_if_expression) - run_test("test_default_parameters", *test_default_parameters, strip_margin("\ - |0 some value - |10 some value - |20 Hello world!\n"), "") - run_test("test_function_overloads", *test_function_overloads) - run_test("test_underscore", *test_underscore) - run_test("test_def_polymorph", *test_def_polymorph) - run_test("test_type_of", *test_type_of) - run_test("test_reflection", *test_reflection) - run_test("test_yield", *test_yield, strip_margin("\ - |0 1 2 3 4 5 6 7 8 9 - |0 1 2 3 4 5 6 7 8 9 \n"), "") - run_test("test_generators", *test_generators, strip_margin("\ - |10 20 30 \n"), "") - run_test("test_closures", *test_closures) -} diff --git a/src/test/test_typechecking.pr b/src/test/test_typechecking.pr deleted file mode 100644 index b5be90ba..00000000 --- a/src/test/test_typechecking.pr +++ /dev/null @@ -1,711 +0,0 @@ -import vector -import map -import util -import lexer -import parser -import typechecking -import compiler -import scope -import builtins -import debug -import codegen -import toolchain -import consteval -import errors - -import test::testsuite - -type Result = struct { - module: &toolchain::Module - node: &parser::Node -} - -def typecheck(s: &string) -> Result { - toolchain::outfolder = "./build" - errors::error_count = 0 - - let main = "main" - let tokens = lexer::lex(s) - let lines = util::split_lines(s) - let node = parser::parse(tokens, lines, main, main) - let module = toolchain::make_module( - text = s, - lines = lines, - filename = main, - modulename = main, - node = node, - scpe = scope::enter_function_scope(builtins::builtins, null) - ) - toolchain::modules[main] = module - module.scope.module = module - consteval::consteval(module) - typechecking::typecheck(module) - - toolchain::load_file_type() - // TODO This is a bit besides the point of testing typechecking seperately - compiler::compile(module) - - return { module, node } !Result -} - -def test_vardecl { - var str = " - var foo: byte = 5 - " - var res = typecheck(str) - let v1 = (@scope::get(res.module.scope, parser::make_identifier("foo"))).tpe - tassert(v1 == builtins::byte_) - - str = " - var bar: long - let foo, (bar) = 0, 1 - " - res = typecheck(str) - let v2 = (@scope::get(res.module.scope, parser::make_identifier("bar"))).tpe - let v3 = (@scope::get(res.module.scope, parser::make_identifier("foo"))).tpe - tassert(v2 == builtins::long_) - tassert(v3 == builtins::int_) -} - -def test_vardecl_fail { - var str = " - const a, b = 20 - var c, d = 10, 20, 30 - var c = 10 - var e, (c) = 10 - var f: int = \"string\" - " - var res = typecheck(str) -} - -def test_var_let { - var str = " - let a: *let int = null - a = null - @a = 20 - " - var res = typecheck(str) - - str = " - var a: [let int] - a[0] = 20 - " - res = typecheck(str) - - str = " - var a: [4; let int] - a[0] = 20 - " - res = typecheck(str) -} - -def test_literals { - var str = " - let v1 = \"string\" - let v2 = 'c' - let v3 = 0.0 - let v4 = 0xFF - let v5 = false - " - - var res = typecheck(str) - let v1 = (@scope::get(res.module.scope, parser::make_identifier("v1"))).tpe - let v2 = (@scope::get(res.module.scope, parser::make_identifier("v2"))).tpe - let v3 = (@scope::get(res.module.scope, parser::make_identifier("v3"))).tpe - let v4 = (@scope::get(res.module.scope, parser::make_identifier("v4"))).tpe - let v5 = (@scope::get(res.module.scope, parser::make_identifier("v5"))).tpe - - tassert((@v1).kind == typechecking::TypeKind::ARRAY) - tassert((@v1).tpe == builtins::char_) - tassert(v2 == builtins::char_) - tassert(v3 == builtins::double_) - tassert(v4 == builtins::int_) - tassert(v5 == builtins::bool_) -} - -def test_assign { - var str = " - var foo = 5 - var bar = 10 - - foo, bar = 10, 20 - - def function -> int, int { - return 0, 0 - } - foo, bar = function() - " - typecheck(str) -} - -def test_assign_fail { - var str = " - var v - let foo = 20 - foo = 40 - var bar: int - bar = \"string\" - " - var res = typecheck(str) -} - -def test_operators { - var str = " - let foo = 0 - let bar = -foo - let baz = +50 - " - - // TODO This doesnt really test anything, we need to inspect the nodes - var res = typecheck(str) - let v1 = (@scope::get(res.module.scope, parser::make_identifier("foo"))).tpe - let v2 = (@scope::get(res.module.scope, parser::make_identifier("baz"))).tpe - tassert(v1 == builtins::int_) - tassert(v2 == builtins::int_) - - str = " - let v3 = 10 + 20 - let v4 = v3 + 0 !short - let v5 = 0 !long - 0 !int - let v6 = 0 !long & 0 !int - " - res = typecheck(str) - let v3 = (@scope::get(res.module.scope, parser::make_identifier("v3"))).tpe - let v4 = (@scope::get(res.module.scope, parser::make_identifier("v4"))).tpe - let v5 = (@scope::get(res.module.scope, parser::make_identifier("v5"))).tpe - let v6 = (@scope::get(res.module.scope, parser::make_identifier("v6"))).tpe - tassert(v3 == builtins::int_) - tassert(v4 == builtins::int_) - tassert(v5 == builtins::long_) - tassert(v6 == builtins::long_) -} - -def test_operators_fail { - var str = " - var a = 0 !float - var b = 1 - var c = a & b - var d = \"string\" - var e = not d - " - var res = typecheck(str) -} - -def test_def { - var str = " - def foo {} - " - var res = typecheck(str) - - str = " - def foo - def foo {} - " - res = typecheck(str) - - str = " - def foo {} - def foo(a: float) {} - " - res = typecheck(str) -} - -def test_def_fail { - var str = " - def foo {} - def foo {} - " - var res = typecheck(str) - - str = " - def foo { - export var foo: int - export def nested_function {} - } - " - res = typecheck(str) -} - -def test_def_polymorph { - var str = " - def function(type T) -> T { - var t: T - return t - } - function(int) - " - var res = typecheck(str) - - str = " - def function(a: type T) -> T { - return a - } - function(10) - " - res = typecheck(str) -} - -def test_typedecl { - var str = " - type A = int - type B = A - - var a: B = 0 - " - var res = typecheck(str) - - str = " - type A - type A = int - //type module::A - //type module::A = int // No longer allowed - " - res = typecheck(str) -} - -def test_typedecl_fail { - var str = " - type A = int - type A = float - var B = 0 - type B = int - " - var res = typecheck(str) -} - -def test_struct { - var str = " - type A = struct { - a: int32 - b: int64 - c: int16 - d: int8 - } - type B = struct { - a: int8 - b: int16 - } - type C = struct { - a: int16 - b: int8 - c: int32 - } - " - var res = typecheck(str) - var s1 = scope::get_type(res.module.scope, parser::make_identifier("A")) - tassert((@s1).size == 24) - tassert((@s1).align == 8) - var s2 = scope::get_type(res.module.scope, parser::make_identifier("B")) - tassert((@s2).size == 4) - tassert((@s2).align == 2) - var s3 = scope::get_type(res.module.scope, parser::make_identifier("C")) - tassert((@s3).size == 8) - tassert((@s3).align == 4) - - str = " - type A = struct #union { - a: int8 - b: int32 - c: int64 - } - " - res = typecheck(str) - var s4 = scope::get_type(res.module.scope, parser::make_identifier("A")) - tassert((@s4).size == 8) - tassert((@s4).align == 8) -} - -def test_import { - var str = " - import test::module::a - test::module::a::multiply_by_2(4) - multiply_by_2(10) - multiply_by_2(10.5) - " - var res = typecheck(str) - - str = " - import test::module::a - def multiply_by_2(a: int) -> int { - return a * 2 - } - multiply_by_2(10) - test::module::a::multiply_by_2(10) - multiply_by_2(10.5) // module::a - " - res = typecheck(str) -} - -def test_import_fail { - // TODO Fix this - // We print too many errors - /*var str = " - import test::a - import test::b - multiply_by_2(10) - multiply_by_2(10.5) - " - var res = typecheck(str)*/ -} - -def test_return { - var str = " - def foo -> int { - return 0 - } - " - var res = typecheck(str) - - str = " - def foo -> int, bool { - return 0, true - } - def bar -> int, bool { - return foo() - } - " - res = typecheck(str) -} - -def test_return_fail { - var str = " - def foo -> int, bool { - return false, 'c' - } - " - var res = typecheck(str) -} - -def test_pointers { - var str = " - let a = 10 - let b = *a - let c = @b - " - var res = typecheck(str) - let a = (@scope::get(res.module.scope, parser::make_identifier("a"))).tpe - let b = (@scope::get(res.module.scope, parser::make_identifier("b"))).tpe - let c = (@scope::get(res.module.scope, parser::make_identifier("c"))).tpe - tassert(a == builtins::int_) - tassert(typechecking::equals(b, typechecking::pointer(builtins::int_))) - tassert(c == builtins::int_) -} - -def test_pointers_fail { - var str = " - let a = 10 - let b = @a - " - var res = typecheck(str) -} - -def test_member_access { - var str = " - type T = struct { - a: int - b: double - } - var foo: T - let a = foo.a - let b = foo.b - " - var res = typecheck(str) - let a = (@scope::get(res.module.scope, parser::make_identifier("a"))).tpe - let b = (@scope::get(res.module.scope, parser::make_identifier("b"))).tpe - tassert(a == builtins::int_) - tassert(b == builtins::double_) - - // TODO Test error messages -} - -def test_array_subscript { - var str = " - var a: [4; int] - let b = a[2] - - var c = 10 - var d = *c - let e = d[2] - " - var res = typecheck(str) - let b = (@scope::get(res.module.scope, parser::make_identifier("b"))).tpe - let e = (@scope::get(res.module.scope, parser::make_identifier("e"))).tpe - tassert(b == builtins::int_) - tassert(e == builtins::int_) -} - -def test_array_lit { - var str = " - let a = [1, 2, 3, 4] - let b: [int] = a - " - var res = typecheck(str) - let a = (@scope::get(res.module.scope, parser::make_identifier("a"))).tpe - let b = (@scope::get(res.module.scope, parser::make_identifier("b"))).tpe - tassert((@a).kind == typechecking::TypeKind::STATIC_ARRAY) - tassert((@a).tpe == builtins::int_) - tassert((@a).length == 4) - tassert((@b).kind == typechecking::TypeKind::ARRAY) - tassert((@b).tpe == builtins::int_) -} - -def test_array_inference { - var str = " - let a: [?; int] = [1, 2, 3, 4] - let b: [int] = [1, 2, 3, 4] - " - var res = typecheck(str) -} - -def test_array_inference_fail { - var str = " - let a: [?; float] = [1, 2, 3, 4] - " - var res = typecheck(str) -} - -def test_array_size_and_value { - var str = " - let a = [1, 2, 3, 4] - let b: [int] = a - let c = a.size - let d = a.value - let e = b.size - let f = b.value - " - var res = typecheck(str) - let c = (@scope::get(res.module.scope, parser::make_identifier("c"))).tpe - let d = (@scope::get(res.module.scope, parser::make_identifier("d"))).tpe - let e = (@scope::get(res.module.scope, parser::make_identifier("e"))).tpe - let f = (@scope::get(res.module.scope, parser::make_identifier("f"))).tpe - tassert(c == builtins::size_t_ and e == builtins::size_t_) - tassert(typechecking::is_pointer(d) and typechecking::is_pointer(f)) - tassert((@d).tpe == builtins::int_ and (@f).tpe == builtins::int_) -} - -def test_call { - var str = " - def demo_function_1 - def demo_function_2(a: int) - demo_function_1() // empty function - demo_function_2(42) // function with unnamed parameter - demo_function_2(a=42) // function with named parameter - " - var res = typecheck(str) -} - -def test_call_fail { - var str = " - def demo_function_2(a: int) - demo_function_2(a=42, a=11) // function with named parameter twice - " - var res = typecheck(str) - - str = " - def no_return - var a: int - a = no_return() - " - res = typecheck(str) -} - -def test_yield { - var str = " - def some_function -> int { - yield 20 - yield 30 - return 40 - } - for var i in some_function() { - print(i) - } - " - var res = typecheck(str) -} - -def test_closure { - var str = " - def some_function { - def inner_function { - - } - } - " - var res = typecheck(str) -} - -def test_closure_fail { - var str = " - def some_function { - export def inner_function {} - def inner_function(a: int = 20) {} - def inner_function(a: int...) {} - } - " - var res = typecheck(str) -} - -export def run_tests { - toolchain::no_incremental = true // TODO Temporary - toolchain::include_path.push("src") - - toolchain::prepare_preload() - toolchain::create_types_main() - - print("Running tests on Typechecking...\n") - run_test("test_vardecl", *test_vardecl) - run_test("test_vardecl_fail", *test_vardecl_fail, "", strip_margin(" - |main@2:9 - | const a, b = 20 - | ^~~~~~~~~~~~~~~ - |Unbalanced assignment - | - |main@4:13 - | var c = 10 - | ^~ - |Redeclaration of `c` - | - |main@5:16 - | var e, (c) = 10 - | ^~~~ - |Must assign a value - | - |main@6:14 - | var f: int = \"string\" - | ^~~~~~ - |Incompatible types [char] and int\n")) - run_test("test_var_let", *test_var_let, "", strip_margin(" - |main@3:9 - | a = null - | ^~ - |Assignment to non var - | - |main@4:9 - | @a = 20 - | ^~~ - |Assignment to non var - | - |main@3:9 - | a[0] = 20 - | ^~~ - |Assignment to non var - | - |main@3:9 - | a[0] = 20 - | ^~~ - |Assignment to non var\n")) - run_test("test_literals", *test_literals) - run_test("test_assign", *test_assign) - run_test("test_assign_fail", *test_assign_fail, "", strip_margin(" - |main@4:9 - | foo = 40 - | ^~~~ - |Assignment to non var - | - |main@6:9 - | bar = \"string\" - | ^~~~ - |Incompatible types, can't assign [char] to int - | - |main@2:14 - | var v - | ^ - |Need to specify a type\n")) - run_test("test_operators", *test_operators) - run_test("test_operators_fail", *test_operators_fail, "", strip_margin(" - |main@4:17 - | var c = a & b - | ^~~~~ - |Invalid operands of type float and int to bitwise operator - | - |main@6:17 - | var e = not d - | ^~~~~ - |Incompatible type [char], must be boolean type\n")) - run_test("test_def", *test_def) - /*run_test("test_def_fail", *test_def_fail, "", strip_margin(" - |main@3:13 - | def foo {} - | ^~~~ - |Function `foo` was already declared previously (same arguments) - | - |main@3:20 - | export var foo: int - | ^~~~~~~~~~~~ - |Can't share non top level variable - | - |main@4:20 - | export def nested_function {} - | ^~~~~~~~~~~~~~~~~~~~~~ - |Can't share non top level function\n"))*/ // TODO - run_test("test_def_polymorph", *test_def_polymorph) - run_test("test_typedecl", *test_typedecl) - run_test("test_typedecl_fail", *test_typedecl_fail, "", strip_margin(" - |main@3:9 - | type A = float - | ^~~~~~~~~~~~~~ - |Redeclaration of `A` - | - |main@5:9 - | type B = int - | ^~~~~~~~~~~~ - |Redeclaration of `B`\n")) - run_test("test_struct", *test_struct) - run_test("test_import", *test_import) - run_test("test_import_fail", *test_import_fail) - run_test("test_return", *test_return) - run_test("test_return_fail", *test_return_fail, "", strip_margin(" - |main@3:13 - | return false, 'c' - | ^~~~~~~~~~~~~~~~~ - |Wrong type of return argument, got bool, expected int - | - |main@3:13 - | return false, 'c' - | ^~~~~~~~~~~~~~~~~ - |Wrong type of return argument, got char, expected bool\n")) - run_test("test_pointers", *test_pointers) - run_test("test_pointers_fail", *test_pointers_fail, "", strip_margin(" - |main@3:17 - | let b = @a - | ^~ - |Needs to be a pointer or reference type, got int\n")) - run_test("test_member_access", *test_member_access) - run_test("test_array_subscript", *test_array_subscript) - run_test("test_array_lit", *test_array_lit) - run_test("test_array_inference", *test_array_inference) - run_test("test_array_inference_fail", *test_array_inference_fail, "", strip_margin(" - |main@2:14 - | let a: [?; float] = [1, 2, 3, 4] - | ^~~~~~~~~~~~~ - |Incompatible types [4; int] and [?; float]\n")) - run_test("test_array_size_and_value", *test_array_size_and_value) - run_test("test_call", *test_call) - run_test("test_call_fail", *test_call_fail, "", strip_margin(" - |main@3:31 - | demo_function_2(a=42, a=11) // function with named parameter twice - | ^~~~ - |Cannot have the same parameter name multiple times in a function call. Parameter name was `a`. - | - |main@4:9 - | a = no_return() - | ^~ - |Incompatible types, can't assign void to int\n")) - run_test("test_yield", *test_yield) - run_test("test_closure", *test_closure) - run_test("test_closure_fail", *test_closure_fail, "", strip_margin(" - |main@3:24 - | export def inner_function {} - | ^~~~~~~~~~~~~~~ - |Invalid modifier to closure - | - |main@4:32 - | def inner_function(a: int = 20) {} - | ^~~~~~~~~~~ - |Can't have default parameters for closure - | - |main@5:32 - | def inner_function(a: int...) {} - | ^~~~~~~~~ - |Can't have varargs for closure\n")) -} \ No newline at end of file diff --git a/src/test/testsuite.pr b/src/test/testsuite.pr deleted file mode 100644 index 8a800080..00000000 --- a/src/test/testsuite.pr +++ /dev/null @@ -1,374 +0,0 @@ -import util -import parser -import vector -import map -import debug -import io - -#if not defined WIN32 { - import linux -} else { - import windows -} - -import test_json -import test_getopt -import test_lexer -import test_parser -import test_typechecking -import test_compiler -import test_runtime -import test_ctfe -import test_vector as test_vec -import test_expr_eval - -export var match_string: &string = "*" -export var fork_process: bool = true -export var capture_stdout: bool = true - -def print_divider(c: char) { - let termsize = util::get_terminal_size() - for var i in 0..termsize.col { - print(c) - } - print("\n") -} - -let stdout_file = "tmp/stdout.txt" -let stderr_file = "tmp/stderr.txt" - -var test_count = 0 -var failed_test_count = 0 -// Shared memory -var asserts: *int - -export def tassert(cond: bool) { - asserts[0] += 1 - var stream = debug::stdout_orig - if not capture_stdout { - stream = std::stdout() - } - if not cond { - cstd::fprintf(stream, "x".value) - asserts[1] += 1 - } else { - cstd::fprintf(stream, ".".value) - } - cstd::fflush(stream) -} - -export def run_test(desc: &string, function: def () -> (), expected_out: &string, expected_err: &string) { - if not util::match(match_string, desc) { - return - } - - test_count += 1 - - var num_printed = print(">", desc, " ") - cstd::fflush(std::stdout()) - - if capture_stdout { - io::redirect_stdout_to_file(stdout_file) - io::redirect_stderr_to_file(stderr_file) - } - - #if defined WIN32 { - asserts = allocate((size_of int) * 2) !*int - } else { - asserts = linux::mmap(null, (size_of int) * 2, 3 /*PROT_READ | PROT_WRITE*/, 33 /*MAP_SHARED | MAP_ANONYMOUS*/, -1, 0) !*int - } - asserts[0] = asserts[1] = 0 - - var segfault = false - - #if defined WIN32 { - function() - } else { - if fork_process { - let pid = linux::fork() - if pid == 0 { - function() - exit(0) - } - - var status: int - linux::waitpid(pid, *status, 0) - if status & 0x7f == 11 /*SIGSEGV*/{ - segfault = true - } - } else { - function() - } - } - - - var out_ok = true - var err_ok = true - var err: &string - var out: &string - - if capture_stdout { - io::restore_stdout() - io::restore_stderr() - - let stdout_fh = open(stdout_file, "rb") - out = read_all(stdout_fh) - close(stdout_fh) - - let stderr_fh = open(stderr_file, "rb") - err = read_all(stderr_fh) - close(stderr_fh) - - out_ok = out == expected_out - err_ok = err == expected_err - tassert(out_ok) - tassert(err_ok) - } - - let assert_count = asserts[0] - let failed_assert_count = asserts[1] - - #if defined WIN32 { - free(asserts) - } else { - linux::munmap(asserts, (size_of int) * 2) - } - - num_printed += assert_count - let termsize = util::get_terminal_size() - let spacing = termsize.col - num_printed - 13 - - if spacing > 0 { - for var i in 0..spacing { - print(" ") - } - } else { - print("\n") - } - - if segfault { - print("[ \x1B[31mSEGFAULT\x1B[0m ]\n") - } else { - print("[") - cstd::printf("%2d/%-2d".value, assert_count - failed_assert_count, assert_count) - if failed_assert_count == 0 { - print(" \x1B[32mOK\x1B[0m ") - } else { - print(" \x1B[31mERROR\x1B[0m") - } - print("]\n") - } - - if capture_stdout { - if not out_ok { - print_divider('-') - print("Expected standard out:\n") - print(expected_out, "\n") - print("Actual standard out:\n") - print(out, "\n") - print_divider('-') - } - - if not err_ok { - print_divider('-') - print("Expected standard error:\n") - print(expected_err, "\n") - print("Actual standard error:\n") - print(err, "\n") - print_divider('-') - } - } - - if failed_assert_count > 0 or segfault { - failed_test_count += 1 - } -} - -export def run_test(desc: string, function: def () -> ()) { - run_test(desc, function, "", "") -} - -def test_vector { - var vec = vector::make(int) - vec.push(0) - vec.push(1) - vec.push(2) - - tassert(vec[0] == 0) - tassert(vec[1] == 1) - tassert(vec[2] == 2) - - vec[0] = 3 - - tassert(vec[0] == 3) - - vec = vector::make(int) - var vec2 = vector::make(int) - - for var i in 0..5 { - vec.push(i) - vec2.push(i) - } - - vector::insert(vec, 2, 10) - vector::insert(vec, 2, vec2) - - // TODO actually verify this -} - -def test_split_lines { - var str = "this\nis\na\ntest" - var res = util::split_lines(str) - - tassert(res.size == 4) - tassert(res[0] == "this") - tassert(res[1] == "is") - tassert(res[2] == "a") - tassert(res[3] == "test") - - str = "this is a test" - res = util::split_lines(str) - - tassert(res.size == 1) - tassert(res[0] == "this is a test") - - str = "this\r\nis\r\na\r\ntest" - res = util::split_lines(str) - - tassert(res.size == 4) - tassert(res[0] == "this") - tassert(res[1] == "is") - tassert(res[2] == "a") - tassert(res[3] == "test") -} - -def test_find_substr { - var str = "foo%%bar%%baz" - tassert(util::find_substr(str, "banana", 0) == -1) - tassert(util::find_substr(str, "%%", 0) == 3) - tassert(util::find_substr(str, "%%", 4) == 8) -} - -def test_replace_all { - var str = "foo%%bar%%baz" - tassert(util::replace_all(str, "%%", ", ") == "foo, bar, baz") - - str = "%%" - tassert(util::replace_all(str, "%%", "") == "") -} - -def test_util { - print("Running tests on Util...\n") - run_test("test_split_lines", *test_split_lines) - //run_test("test_int_to_str", *test_int_to_str) - run_test("test_find_substr", *test_find_substr) - run_test("test_replace_all", *test_replace_all) -} - -// TODO these tests don't nearly cover all cases -def test_map_simple { - let m = map::make(int) - m["foo"] = 20 - m["bar"] = 50 - - tassert(map::size(m) == 2) - tassert(m["foo"] == 20) - tassert(m["bar"] == 50) - - map::remove(m, "foo") - tassert(map::size(m) == 1) - tassert(not map::get(m, "foo").exists) -} - -def test_map_collision { - let m = map::make(int) - m["JUvEoj"] = 20 - m["JVVdoj"] = 50 - - tassert(map::size(m) == 2) - tassert(m["JUvEoj"] == 20) - tassert(m["JVVdoj"] == 50) - - map::remove(m, "JVVdoj") - tassert(map::size(m) == 1) - tassert(not map::get(m, "JVVdoj").exists) -} - -def test_map_resize { - let m = map::make(int, 2) - m["1"] = 1 - m["2"] = 2 - m["3"] = 3 - m["4"] = 4 - - tassert(map::size(m) == 4) - - tassert(m["1"] == 1) - tassert(m["2"] == 2) - tassert(m["3"] == 3) - tassert(m["4"] == 4) -} - -def is_in(array: &[&string], key: &string) -> bool { - for var i in 0..array.size { - let value = array[i] - if value == key { - return true - } - } - return false -} - -def test_map_keys { - let m = map::make(int) - m["JUvEoj"] = 20 - m["JVVdoj"] = 50 - m["foo"] = 0 - m["bar"] = 1 - - let keys = map::keys(m) - tassert(keys.size == 4) - tassert(is_in(keys, "JUvEoj")) - tassert(is_in(keys, "JVVdoj")) - tassert(is_in(keys, "foo")) - tassert(is_in(keys, "bar")) -} - -def test_map { - print("Running tests on Map...\n") - run_test("test_map_simple", *test_map_simple) - run_test("test_map_collision", *test_map_collision) - run_test("test_map_resize", *test_map_resize) - run_test("test_map_keys", *test_map_keys) -} - -export def run_test_suite { - #if defined WIN32 { - windows::CreateDirectoryA("tmp".value, null) - } else { - linux::mkdir("tmp".value, 0o777) - } - - //run_test("test_buffer", *test_buffer) TODO Write a test for std - run_test("test_vector", *test_vector) - - test_util() - test_map() - - test_json::run_tests() - test_getopt::run_tests() - test_lexer::run_tests() - test_parser::run_tests() - test_typechecking::run_tests() - test_compiler::run_tests() - test_runtime::run_tests() - test_ctfe::run_tests() - test_vec::run_tests() - test_expr_eval::run_tests() - - print_divider('=') - print("Finished. Succeeded: ", test_count - failed_test_count, "/", test_count, "\n") - if failed_test_count > 0 { - exit(1) - } -} \ No newline at end of file diff --git a/src/testrunner.pr b/src/testrunner.pr index ca7e19b6..b02b4f93 100644 --- a/src/testrunner.pr +++ b/src/testrunner.pr @@ -172,7 +172,7 @@ for var i in 0..files.length { let lib = shared::load(dll_file, init = false) // We only want the symbol information for var symbol in @lib.symbols { - if symbol.name.starts_with("__test::") { + if symbol.name.starts_with("__test::") and std::match(filter_str, symbol.name) { tests_to_run.push(symbol.name) } } diff --git a/src/toolchain.pr b/src/toolchain.pr index ad2618b6..2e202e8f 100644 --- a/src/toolchain.pr +++ b/src/toolchain.pr @@ -91,8 +91,11 @@ export const version = create_version_string() // Debug stuff export var print_ast = false export var print_tokens = false +export var print_typed_ast = false +export var continue_on_output = false // This flag controls emitting debug information to llvm export var debug_sym = false +export var main_file_name: &string = null var main_module_file: &string @@ -278,7 +281,7 @@ export def find_module_file(path: &string, calling_module: &Module) -> &string { } if not path.starts_with("/") and calling_module.filename { - let base = util::dirname(calling_module.filename) + let base = dirname(calling_module.filename) let module_path = absolute_path(base + "/" + path + ".pr") if util::exists(module_path) { return module_path @@ -352,7 +355,7 @@ export def create_module_if_absent(filename: &string, modulename: &string) -> &M return module } -export def consteval_file(filename: &string, modulename: &string) -> &Module { +export def consteval_file(filename: &string, modulename: &string, display_name: &string = null) -> &Module { var module: &Module if modules.contains(filename) { module = modules[filename] @@ -365,15 +368,30 @@ export def consteval_file(filename: &string, modulename: &string) -> &Module { module = create_module_if_absent(filename, modulename) module.stage = Stage::PREPROCESS + if display_name { + module.filename = display_name + } debug::trace("Lexing ", modulename) let tokens = lexer::lex(module.text) - let node = parser::parse(tokens, module.lines, filename, modulename) + let node = parser::parse(tokens, module.lines, filename, modulename, display_name = display_name) delete(tokens) module.node = node module.compiler_state = compiler::make_state(module) + let filename_type = typechecking::make_static_array(builtins::char_, filename.size) + let exe_global = compiler::make_global_value( + filename_type, "__file__", + { kind = compiler::ValueKind::STRING, tpe = filename_type, s = filename } !&compiler::Value, + module.compiler_state, private = false + ) + let exe_value = scope::create_variable( + module.scope, make_identifier("__file__"), + parser::ShareMarker::EXPORT, parser::VarDecl::LET, filename_type, null + ) + exe_value.assembly_name = exe_global.name + consteval::consteval(module) return module @@ -558,7 +576,7 @@ export def compile_main_file(filename: &string) { error("File ", filename, " is not a Princess source file\n") exit(1) } - include_path.push(absolute_path(util::dirname(filename))) + include_path.push(absolute_path(dirname(filename))) if print_ast or print_tokens { let buf = file_loader(filename) @@ -571,13 +589,14 @@ export def compile_main_file(filename: &string) { let tokens = lexer::lex(buf) if print_tokens { print(json::to_string(lexer::token_list_to_json(tokens)), "\n") - exit(0) + if not continue_on_output { exit(0) } } let node = parser::parse(tokens, lines, filename, "main") delete(tokens) + if error_count > 0 { exit(1) } print(json::to_string(debug::node_to_json(node)), "\n") - exit(0) + if not continue_on_output { exit(0) } } @@ -587,7 +606,7 @@ export def compile_main_file(filename: &string) { create_types_main() main_module_file = filename - var module = consteval_file(filename, "main") + var module = consteval_file(filename, "main", display_name = main_file_name) if not module { exit(1) } module.stage = Stage::RESOLVE_DEPENDENCIES @@ -596,6 +615,12 @@ export def compile_main_file(filename: &string) { module.stage = Stage::TYPECHECKING typechecking::typecheck(module) + if print_typed_ast { + if error_count > 0 { exit(1) } + print(json::to_string(debug::node_to_json(module.node, types = true)), "\n") + if not continue_on_output { exit(0) } + } + load_file_type() module.stage = Stage::COMPILING diff --git a/src/typechecking.pr b/src/typechecking.pr index b0b5f0a8..7f41e8fd 100644 --- a/src/typechecking.pr +++ b/src/typechecking.pr @@ -3461,6 +3461,8 @@ export def walk_Def(node: &parser::Node, state: &State) { imported = imported } !&compiler::Function node.value.def_.function = function + } else { + function.tpe = tpe } var phase = scope::Phase::DECLARED @@ -3527,6 +3529,8 @@ export def walk_Def(node: &parser::Node, state: &State) { } if function.has_yield { + // FIXME generators should be a separate type + // because this overwrites return_t, which might contain useful information let generator_ctor = { kind = parser::NodeKind::TYPE_CONSTRUCTOR } !&parser::Node diff --git a/src/util.pr b/src/util.pr index 12d6564f..905b1aa8 100644 --- a/src/util.pr +++ b/src/util.pr @@ -114,32 +114,6 @@ export def split(str: &string, by: &string) -> &[&string] { return res.to_array() } -export def dirname(file: &string) -> &string { - var last_slash = -1 - for var i in 0..file.size { - let c = file[i] - if c == '/' or c == '\\' { - last_slash = i - } - } - let ret = allocate_ref(char, last_slash + 2) - memcopy(file.value, ret.value, last_slash + 1) - return ret -} - -export def basename(file: &string) -> &string { - var last_slash = 0 - for var i in 0..file.size { - let c = file[i] - if c == '/' or c == '\\' { - last_slash = i - } - } - let ret = allocate_ref(char, file.size - last_slash) - memcopy(file.value ++ last_slash, ret.value, file.size - last_slash) - return ret -} - export def exe_folder -> &string { return dirname(executable_file()) } @@ -231,31 +205,6 @@ export def get_terminal_size -> TermSize { } -// https://stackoverflow.com/a/23457543/3258949 -// TODO This algorithm is very inefficient - -def match(pattern: &string, candidate: &string, p: int, c: int) -> bool { - if pattern[p] == '\0' { - return candidate[c] == '\0' - } else if pattern[p] == '*' { - while candidate[c] != '\0' { - if match(pattern, candidate, p + 1, c) { - return true - } - c += 1 - } - return match(pattern, candidate, p + 1, c) - } else if pattern[p] != '?' and pattern[p] != candidate[c] { - return false - } else { - return match(pattern, candidate, p + 1, c + 1) - } -} - -export def match(pattern: &string, candidate: &string) -> bool { - return match(pattern, candidate, 0, 0) -} - export def bytes_in_glyph(c: char) -> int { if c & 0b10000000 == 0 { return 1 diff --git a/src/getopt.pr b/std/getopt.pr similarity index 99% rename from src/getopt.pr rename to std/getopt.pr index 8bca7ae0..cdcaf9c9 100644 --- a/src/getopt.pr +++ b/std/getopt.pr @@ -1,8 +1,9 @@ // TODO This needs to be tested, and reworked +import cstd +import std import map import vector -import util export type ValueKind = enum { STRING diff --git a/std/std.pr b/std/std.pr index 1aa9621e..0ac36864 100644 --- a/std/std.pr +++ b/std/std.pr @@ -222,6 +222,31 @@ export def strip_margin(s: &string) -> &string { return res } +// https://stackoverflow.com/a/23457543/3258949 +// TODO This algorithm is very inefficient + +def match(pattern: &string, candidate: &string, p: int, c: int) -> bool { + if pattern[p] == '\0' { + return candidate[c] == '\0' + } else if pattern[p] == '*' { + while candidate[c] != '\0' { + if match(pattern, candidate, p + 1, c) { + return true + } + c += 1 + } + return match(pattern, candidate, p + 1, c) + } else if pattern[p] != '?' and pattern[p] != candidate[c] { + return false + } else { + return match(pattern, candidate, p + 1, c + 1) + } +} + +export def match(pattern: &string, candidate: &string) -> bool { + return match(pattern, candidate, 0, 0) +} + export type ToString = interface { def to_string -> &string } @@ -772,6 +797,32 @@ export def system(command: &string) -> int { return cstd::system(command.value) } +export def dirname(file: &string) -> &string { + var last_slash = -1 + for var i in 0..file.size { + let c = file[i] + if c == '/' or c == '\\' { + last_slash = i + } + } + let ret = allocate_ref(char, last_slash + 2) + memcopy(file.value, ret.value, last_slash + 1) + return ret +} + +export def basename(file: &string) -> &string { + var last_slash = 0 + for var i in 0..file.size { + let c = file[i] + if c == '/' or c == '\\' { + last_slash = i + } + } + let ret = allocate_ref(char, file.size - last_slash) + memcopy(file.value ++ last_slash, ret.value, file.size - last_slash) + return ret +} + export def executable_file -> &string { let resolved = zero_allocate(char, PATH_MAX) defer delete(resolved) diff --git a/test/common.pr b/test/common.pr index f32328a4..b7b52165 100644 --- a/test/common.pr +++ b/test/common.pr @@ -7,7 +7,10 @@ import process cstd::_setmode(2, 0x8000) } -export def run_compiler(file: &string, str: &string, args: &string) -> &string { +export let tmpfolder = tmpfolder("princess") + +export def run_compiler_on_source(str: &string, args: [&string]) -> &string { + let file = tmpfolder + "/main.pr" let fh = open(file, "w") fprint(fh, str) fh.close() @@ -15,16 +18,23 @@ export def run_compiler(file: &string, str: &string, args: &string) -> &string { return run_compiler(file, args) } -export def run_compiler(file: &string, args: &string) -> &string { +export def run_compiler(file: &string, args: [&string]) -> &string { let r, w = io::pipe() var compiler: &string = runtime::executable let env = cstd::getenv("PRINCESS_COMPILER".value) if env { compiler = make_string(env) } + let res = allocate_ref(type &string, args.size + 2) + res[0] = file + res[1] = "--no-incremental" // TODO Enable incremental compilation + for var i in 0..args.size { + res[i + 2] = args[i] + } + let proc = *process::spawn( - compiler, - [file, args] ![&string], + compiler, + res, stdout = w ) proc.wait() @@ -41,4 +51,47 @@ export def run_compiler(file: &string, args: &string) -> &string { let ast = r.read_all_pipe() close(r) return ast +} + +export def test_file(name: &string) -> &string { + return dirname(__file__) + "/runtime/" + name +} + +export def executable(name: &string) -> &string { + #if defined WIN32 { + return name + ".exe" + } + return name +} + +export def compile_source(src: &string) -> int { + return run_source(src, run = false) +} + +export def run_source(src: &string, run: bool = true) -> int { + let file = tmpfolder + "/main" + let fh = open(file + ".pr", "w") + fprint(fh, src) + fh.close() + return run_file(file, run) +} + +export def compile_file(name: &string) -> int { + return run_file(name, run = false) +} + +export def run_file(name: &string, run: bool = true) -> int { + let src = name + ".pr" + let exe = executable(name) + + let res = run_compiler(src, ["-o"! &string, exe]) + if not res { return 1 } + + if run { + let proc = *process::spawn(exe, [] ![&string]) + proc.wait() + proc.dispose() + return proc.exit_code + } + return 0 } \ No newline at end of file diff --git a/src/test/module/a.pr b/test/runtime/module/a.pr similarity index 100% rename from src/test/module/a.pr rename to test/runtime/module/a.pr diff --git a/src/test/module/b.pr b/test/runtime/module/b.pr similarity index 100% rename from src/test/module/b.pr rename to test/runtime/module/b.pr diff --git a/test/runtime/test_imports b/test/runtime/test_imports new file mode 100644 index 00000000..fcb65634 Binary files /dev/null and b/test/runtime/test_imports differ diff --git a/test/runtime/test_imports.pr b/test/runtime/test_imports.pr new file mode 100644 index 00000000..739adec6 --- /dev/null +++ b/test/runtime/test_imports.pr @@ -0,0 +1,14 @@ +import module::a as A +import module::b + +def test_imports { + assert A::multiply_by_2(10) == 20 + let a, b = return_multiple() + assert a == 10 + assert b == 20 + print(A::Enum::SOME_ENUM_VALUE, "\n") + + let c = {10, 10} !A::Point + let d = module::b::some_value +} +test_imports \ No newline at end of file diff --git a/test/runtime/test_reflection b/test/runtime/test_reflection new file mode 100644 index 00000000..ab76c653 Binary files /dev/null and b/test/runtime/test_reflection differ diff --git a/test/runtime/test_reflection.pr b/test/runtime/test_reflection.pr new file mode 100644 index 00000000..c92f9242 --- /dev/null +++ b/test/runtime/test_reflection.pr @@ -0,0 +1,43 @@ +type Struct4 = struct { a: int; b: float } +type Interface = interface { + def function(a: int) -> int +} + +def function(s: Struct4, a: int) -> int { + return s.a + a +} + +def test_reflection { + type T = int + assert T.kind == runtime::TypeKind::WORD + assert T.name == "int32" + assert T.unsig == false + + assert Struct4.kind == runtime::TypeKind::STRUCT + assert Struct4.fields.size == 2 + assert Struct4.fields[0].tpe == int + assert Struct4.fields[0].name == "a" + assert Struct4.fields[1].tpe == float + assert Struct4.fields[1].name == "b" + + assert Struct4.type_members.size == 1 + let member = Struct4.type_members[0] + assert member.name == "main::function::(main::Struct4, int32)" + assert member.parameter_t.size == 2 + assert member.parameter_t[0] == Struct4 + assert member.parameter_t[1] == int + assert member.return_t.size == 1 + assert member.return_t[0] == int + + assert Interface.kind == runtime::TypeKind::STRUCTURAL + assert Interface.structural_members.size == 1 + let smember = Interface.structural_members[0] + assert smember.name == "function" + assert smember.parameter_t.size == 1 + assert smember.parameter_t[0] == int + assert smember.return_t.size == 1 + assert smember.return_t[0] == int + + assert runtime::implements(Struct4, Interface) +} +test_reflection \ No newline at end of file diff --git a/test/runtime/test_vector b/test/runtime/test_vector new file mode 100644 index 00000000..e7cb4a52 Binary files /dev/null and b/test/runtime/test_vector differ diff --git a/src/test/test_vector.pr b/test/runtime/test_vector.pr similarity index 78% rename from src/test/test_vector.pr rename to test/runtime/test_vector.pr index 2eb99ebb..f8acda67 100644 --- a/src/test/test_vector.pr +++ b/test/runtime/test_vector.pr @@ -1,6 +1,3 @@ -import util -import test::testsuite - let INITIAL_SIZE = 8 type Vector = struct { @@ -99,19 +96,5 @@ def test_vector { print("\n") } -export def run_tests { - print("Running vector test... \n") - run_test("test_vector", *test_vector, strip_margin("\ - |Destructing Vector - |0 20 30 40 - |Destructing S: 40 - |Destructing S: 30 - |Destructing S: 20 - |Destructing S: 0 - |Destructing Vector - |Destructing Vector - |Destructing S: 20 - |Destructing S: 30 - |Destructing S: 40 - |Destructing S: 0\n"), "") -} +// run this +test_vector() \ No newline at end of file diff --git a/test/test_compiler.pr b/test/test_compiler.pr new file mode 100644 index 00000000..98bbdf40 --- /dev/null +++ b/test/test_compiler.pr @@ -0,0 +1,970 @@ +import common +import json + +def compile(str: &string) -> &Json { + let include_dir = dirname(__file__) + let types = run_compiler_on_source(str, [ + "--continue-on-output" !&string, + "--typed-ast" !&string, + "--name" !&string, + "main" !&string, + "--include" !&string, + include_dir + ]) + + if not types { return null } + return json::parse(types) +} + +def #test test_arithmetic { + var str = """ + 10 + 10 + 10 + 10 * 2 + 5 / 4 - 5 % 3 + 10.5 / 4.0 + 10 << 1 + 10 >> 2 + -10 + -10.5 + """ + assert compile(str) != null +} + +def #test test_compare { + var str = """ + let a = 10 == 20 + let b = 10 > 20 + let c = 10 < 20 + + let d = 5 + let e = 1 < d < 10 + """ + assert compile(str) != null +} + +def #test test_pointer_arithmetic { + var str = """ + let a = 20 + let b = *a + let c = b ++ 20 -- 5 + + var d: *int + @(d ++ 10) = 20 + """ + assert compile(str) != null +} + +def #test test_assign_eq { + var str = """ + var a = 10 + a += 20 + a <<= 1 + a >>= 2 + a *= 5 + a /= 10 + """ + assert compile(str) != null +} + +def #test test_string_literal { + // Conversion between static and dynamic arrays + var str = """ + def function -> [4; char] {} + + var a: [char] = "abc" + var b: [char] = function() + + def two_returns -> [4; char], [5; char] {} + var c: [char], d: [char] = two_returns() + """ + assert compile(str) != null +} + +def #test test_boolean_op { + var str = """ + let a = true + let b = false + let c = a and b + let d = a or b + let e = not d + """ + assert compile(str) != null +} + +def #test test_assert { + var str = """ + assert + assert 1 == 2 + assert 1 == 2, \"What is this\" + """ + assert compile(str) != null +} + +def #test test_function_pointers { + var str = """ + def some_function -> int { return 20 } + let a = *some_function + let b = a() + """ + assert compile(str) != null +} + +def #test test_if_expression { + var str = """ + let a = 10 if 10 > 20 else 5 + var b: int + var c: int + @(*b if 10 > 20 else *c) = 10 + """ + assert compile(str) != null +} + +def #test test_if { + var str = """ + def foo -> int { return 0 } + + if true { + foo() + } + foo() + + if true { + let x = foo() + } else if false { + let x = foo() + } else if true { + let x = foo() + } + let x = foo() + + if true { + foo() + } else { + foo() + } + foo() + + if true { + foo() + if true { + foo() + } else { + foo() + } + } else { + foo() + if true { + foo() + } else if true { + foo() + } else { + foo() + } + } + foo() + """ + assert compile(str) != null +} + +def #test test_loop { + var str = """ + def foo + loop { + foo() + continue + foo() + break + foo() + } + + loop { + if true { + break + } else { + continue + } + break + } + + loop { + continue + loop { + continue + break + } + break + } + """ + assert compile(str) != null +} + +def #test test_while { + var str = """ + var i = 0 + while i < 10 { + i += 1 + } + """ + assert compile(str) != null +} + +def #test test_for { + var str = """ + for var i in 0..10 { + print(i) + } + + var j = 0 + for j in 0..=10 { + print(j) + } + """ + assert compile(str) != null +} + +def #test test_vardecl { + var str = """ + var foo: byte = 5 + var bar: long + let baz, (bar) = 0, 1 + """ + var res = compile(str) + assert res["body"][0]["left"][0]["value"]["type_tag"]["name"].as_string() == "byte" + assert res["body"][1]["left"][0]["value"]["type_tag"]["name"].as_string() == "long" + assert res["body"][2]["left"][0]["value"]["type_tag"]["name"].as_string() == "int" + assert res["body"][2]["left"][1]["value"]["type_tag"]["name"].as_string() == "long" +} + +def #test test_vardecl_fail { + var str = """ + const a, b = 20 + var c, d = 10, 20, 30 + var c = 10 + var e, (c) = 10 + var f: int = "string" + """ + assert compile(str) == null + assert env.err() == strip_margin(""" + |main@2:9 + | const a, b = 20 + | ^~~~~~~~~~~~~~~ + |Unbalanced assignment + | + |main@4:13 + | var c = 10 + | ^~ + |Redeclaration of `c` + | + |main@5:16 + | var e, (c) = 10 + | ^~~~ + |Must assign a value + | + |main@6:14 + | var f: int = "string" + | ^~~~~~ + |Incompatible types [char] and int\n""") +} + +def #test test_var_let { + var str = """ + let a: *let int = null + a = null + @a = 20 + + var b: [let int] + b[0] = 20 + + var c: [4; let int] + c[0] = 20 + """ + assert compile(str) == null + assert env.err() == strip_margin(""" + |main@3:9 + | a = null + | ^~ + |Assignment to non var + | + |main@4:9 + | @a = 20 + | ^~~ + |Assignment to non var + | + |main@7:9 + | b[0] = 20 + | ^~~ + |Assignment to non var + | + |main@10:9 + | c[0] = 20 + | ^~~ + |Assignment to non var\n""") +} + +def #test test_literals { + var str = """ + let v1 = "string" + let v2 = 'c' + let v3 = 0.0 + let v4 = 0xFF + let v5 = false + """ + var res = compile(str) + assert res["body"][0]["left"][0]["value"]["type_tag"]["name"].as_string() == "[char]" + assert res["body"][1]["left"][0]["value"]["type_tag"]["name"].as_string() == "char" + assert res["body"][2]["left"][0]["value"]["type_tag"]["name"].as_string() == "double" + assert res["body"][3]["left"][0]["value"]["type_tag"]["name"].as_string() == "int" + assert res["body"][4]["left"][0]["value"]["type_tag"]["name"].as_string() == "bool" +} + +def #test test_switch { + var str = """ + let a = 20 + switch a { + case 10: print(1) + case 11..=19: print(2) + case 20, 25: print(3) + case: print(4) + } + + switch a { + case 0..=100: print(1) + case 101, 102, 103: print(2) + case 104..200, 205: print(3) + case: print(4) + } + """ + assert compile(str) != null +} + +def #test test_assign { + var str = """ + var foo = 5 + var bar = 10 + + foo, bar = 10, 20 + + def function -> int, int { + return 0, 0 + } + foo, bar = function() + """ + assert compile(str) != null +} + +def #test test_assign_fail { + var str = """ + var v + let foo = 20 + foo = 40 + var bar: int + bar = "string" + """ + assert compile(str) == null + assert env.err() == strip_margin(" + |main@4:9 + | foo = 40 + | ^~~~ + |Assignment to non var + | + |main@6:9 + | bar = \"string\" + | ^~~~ + |Incompatible types, can't assign [char] to int + | + |main@2:14 + | var v + | ^ + |Need to specify a type\n") +} + +def #test test_operators { + var str = """ + let foo = 0 + let bar = -foo + let baz = +50 + let v3 = 10 + 20 + let v4 = v3 + 0 !short + let v5 = 0 !long - 0 !int + let v6 = 0 !long & 0 !int + """ + let res = compile(str) + + assert res["body"][0]["left"][0]["value"]["type_tag"]["name"].as_string() == "int" + assert res["body"][1]["left"][0]["value"]["type_tag"]["name"].as_string() == "int" + assert res["body"][2]["left"][0]["value"]["type_tag"]["name"].as_string() == "int" + assert res["body"][3]["left"][0]["value"]["type_tag"]["name"].as_string() == "int" + assert res["body"][4]["left"][0]["value"]["type_tag"]["name"].as_string() == "int" + assert res["body"][5]["left"][0]["value"]["type_tag"]["name"].as_string() == "long" + assert res["body"][6]["left"][0]["value"]["type_tag"]["name"].as_string() == "long" +} + +def #test test_operators_fail { + var str = """ + var a = 0 !float + var b = 1 + var c = a & b + var d = \"string\" + var e = not d + """ + assert compile(str) == null + assert env.err() == strip_margin(" + |main@4:17 + | var c = a & b + | ^~~~~ + |Invalid operands of type float and int to bitwise operator + | + |main@6:17 + | var e = not d + | ^~~~~ + |Incompatible type [char], must be boolean type\n") +} + +def #test test_def { + var str = """ + def foo + def foo {} + def foo(a: int) {} + def foo(a: long...) {} + def bar(a: type T, type B) {} + """ + assert compile(str) != null +} + +def #test test_def_fail { + var str = """ + def foo {} + def foo {} + + def bar { + export var foo: int + export def nested_function {} + } + """ + assert compile(str) == null + assert env.err() == strip_margin(""" + |main@3:13 + | def foo {} + | ^~~~ + |Function `foo` was already declared previously (same arguments) + | + |main@6:20 + | export var foo: int + | ^~~~~~~~~~~~ + |Can't share non top level variable + | + |main@7:24 + | export def nested_function {} + | ^~~~~~~~~~~~~~~~ + |Invalid modifier to closure\n""") +} + +def #test test_def_polymorph { + var str = """ + def function(type T) -> T { + var t: T + return t + } + function(int) + + def function2(a: type T) -> T { + return a + } + function2(10) + """ + assert compile(str) != null +} + +def #test test_typedecl { + var str = """ + type A = int + type B = A + + var a: B = 0 + + type C + type C = int + """ + assert compile(str) != null +} + +def #test test_typedecl_fail { + var str = """ + type A = int + type A = float + var B = 0 + type B = int + type module::A + type module::A = int + """ + assert compile(str) == null + assert env.err() == strip_margin(""" + |main@3:9 + | type A = float + | ^~~~~~~~~~~~~~ + |Redeclaration of `A` + | + |main@5:9 + | type B = int + | ^~~~~~~~~~~~ + |Redeclaration of `B` + | + |main@6:14 + | type module::A + | ^~~~~~~~~ + |Can't declare type in sub scope + | + |main@7:14 + | type module::A = int + | ^~~~~~~~~~ + |Can't declare type in sub scope\n""") +} + +def #test test_struct { + var str = """ + type A = struct { + a: int32 + b: int64 + c: int16 + d: int8 + } + type B = struct { + a: int8 + b: int16 + } + type C = struct { + a: int16 + b: int8 + c: int32 + } + type D = struct #union { + a: int8 + b: int32 + c: int64 + } + """ + let res = compile(str) + let A = res["body"][0]["right"][0]["type_tag"] + assert A["size"].as_double() == 24 + assert A["align"].as_double() == 8 + + let B = res["body"][1]["right"][0]["type_tag"] + assert B["size"].as_double() == 4 + assert B["align"].as_double() == 2 + + let C = res["body"][2]["right"][0]["type_tag"] + assert C["size"].as_double() == 8 + assert C["align"].as_double() == 4 + + let D = res["body"][3]["right"][0]["type_tag"] + assert D["size"].as_double() == 8 + assert D["align"].as_double() == 8 +} + +def #test test_struct_lit { + let src = """ + type T = struct { + a: int + b: int + } + let a = {10, 20} !T + + let b = 20 + let c = 50 + let d = {a = b, b = c} !T + + type A = struct { + a: int + } + type B = struct { + b: A + c: int + } + + let e = 20 + let f = 50 + let g = {{e} !A, f} !B + + def ret_a -> A { + return {10} + } + + let h: A = {10} + var i: A + i = {10} + + type C = struct { + a: int + b: int + c: int + } + let j = {a = 10} !C + """ + assert compile(src) != null +} + +def #test test_size_of { + var str = """ + type T = struct { + a: int + b: int + } + + let a = size_of T + let b = size_of int + let c = size_of type * + let d = size_of type_of a + let e = size_of type_of a + b + """ + assert compile(str) != null +} + +def #test test_align_of { + var str = """ + type T = struct { + a: int + b: int + } + + let a = align_of T + let b = align_of int + let c = align_of type * + let d = align_of type_of a + let e = align_of type_of a + b + """ + assert compile(str) != null +} + +def #test test_type_of { + var str = " + let a = 20 + let b: (type_of a) = 30 + + def some_function(type T) {} + + some_function(type_of a) + " + assert compile(str) != null +} + +def #test test_import { + let str = """ + import runtime::module::a + + runtime::module::a::multiply_by_2(4) + + def multiply_by_2(a: int) -> int { + return a * 2 + } + + multiply_by_2(10) + multiply_by_2(10.5) + """ + assert compile(str) != null +} + +def #test test_import_fail { + let str = """ + import test::a as A + multiply_by_2(10.5) + """ + assert compile(str) == null + assert env.err() == strip_margin(" + |main@2:16 + | import test::a as A + | ^~~~~~~~ + |Module `test::a` could not be found + | + |main@3:9 + | multiply_by_2(10.5) + | ^~~~~~~~~~~~~ + |Function `multiply_by_2` not found. Arguments were of type double\n") +} + +def #test test_return { + let str = """ + def foo -> int { + return 0 + } + def bar -> int, bool { + return 0, true + } + def baz -> int, bool { + return bar() + } + """ + assert compile(str) != null +} + +def #test test_return_fail { + let str = """ + def foo -> int, bool { + return false, 'c' + } + """ + assert compile(str) == null + assert env.err() == strip_margin(" + |main@3:13 + | return false, 'c' + | ^~~~~~~~~~~~~~~~~ + |Wrong type of return argument, got bool, expected int + | + |main@3:13 + | return false, 'c' + | ^~~~~~~~~~~~~~~~~ + |Wrong type of return argument, got char, expected bool\n") +} + +def #test test_pointers { + let str = """ + let a = 10 + let b = *a + let c = @b + """ + let res = compile(str) + + assert res["body"][0]["left"][0]["value"]["type_tag"]["name"].as_string() == "int" + assert res["body"][1]["left"][0]["value"]["type_tag"]["name"].as_string() == "*int" + assert res["body"][2]["left"][0]["value"]["type_tag"]["name"].as_string() == "int" +} + +def #test test_pointers_fail { + let str = """ + let a = 10 + let b = @a + """ + + assert compile(str) == null + assert env.err() == strip_margin(" + |main@3:17 + | let b = @a + | ^~ + |Needs to be a pointer or reference type, got int\n") +} + +def #test test_ref { + var str = """ + var a: &int + var b: & + b = a + + var c: &int = 20 + var d = c + + var e: &int = 20 + var f = @e + + var g: &int = 20 + var h = g !&float + var i = g !*int + """ + + let res = compile(str) + + assert res["body"][0]["left"][0]["value"]["type_tag"]["name"].as_string() == "&int" + assert res["body"][1]["left"][0]["value"]["type_tag"]["name"].as_string() == "&" + assert res["body"][3]["left"][0]["value"]["type_tag"]["name"].as_string() == "&int" + assert res["body"][4]["left"][0]["value"]["type_tag"]["name"].as_string() == "&int" + assert res["body"][5]["left"][0]["value"]["type_tag"]["name"].as_string() == "&int" + assert res["body"][6]["left"][0]["value"]["type_tag"]["name"].as_string() == "int" + assert res["body"][7]["left"][0]["value"]["type_tag"]["name"].as_string() == "&int" + assert res["body"][8]["left"][0]["value"]["type_tag"]["name"].as_string() == "&float" + assert res["body"][9]["left"][0]["value"]["type_tag"]["name"].as_string() == "*int" +} + +def #test test_convert { + var str = """ + let a = 10 !float + let b = a !int + + let c = 10 !float + 20 + let d = 10 !uint + 20 + + let e = 200 !bool + let f = 1.5 !bool + let g: *int = null + let h = g !bool + + let i: int64 = 20 + let j: int16 = 20 + + def foo(a: int64) {} + foo(10) + """ + let res = compile(str) + + assert res["body"][0]["left"][0]["value"]["type_tag"]["name"].as_string() == "float" + assert res["body"][1]["left"][0]["value"]["type_tag"]["name"].as_string() == "int" + assert res["body"][2]["left"][0]["value"]["type_tag"]["name"].as_string() == "float" + assert res["body"][3]["left"][0]["value"]["type_tag"]["name"].as_string() == "uint" + assert res["body"][4]["left"][0]["value"]["type_tag"]["name"].as_string() == "bool" + assert res["body"][5]["left"][0]["value"]["type_tag"]["name"].as_string() == "bool" + assert res["body"][6]["left"][0]["value"]["type_tag"]["name"].as_string() == "*int" + assert res["body"][7]["left"][0]["value"]["type_tag"]["name"].as_string() == "bool" +} + +def #test test_member_access { + let str = """ + type T = struct { + a: int + b: double + } + var foo: T + let a = foo.a + let b = foo.b + """ + let res = compile(str) + + assert res["body"][2]["left"][0]["value"]["type_tag"]["name"].as_string() == "int" + assert res["body"][3]["left"][0]["value"]["type_tag"]["name"].as_string() == "double" +} + +def #test test_array_subscript { + let str = """ + var a: [4; int] + let b = a[2] + + var c = 10 + var d = *c + let e = d[2] + """ + let res = compile(str) + + assert res["body"][0]["left"][0]["value"]["type_tag"]["name"].as_string() == "[4; int]" + assert res["body"][1]["left"][0]["value"]["type_tag"]["name"].as_string() == "int" + assert res["body"][2]["left"][0]["value"]["type_tag"]["name"].as_string() == "int" + assert res["body"][3]["left"][0]["value"]["type_tag"]["name"].as_string() == "*int" + assert res["body"][4]["left"][0]["value"]["type_tag"]["name"].as_string() == "int" +} + +def #test test_array_lit { + let str = """ + let a = [1, 2, 3, 4] + let b: [int] = a + """ + let res = compile(str) + + assert res["body"][0]["left"][0]["value"]["type_tag"]["name"].as_string() == "[4; int]" + assert res["body"][1]["left"][0]["value"]["type_tag"]["name"].as_string() == "[int]" +} + +def #test test_array_inference { + let str = """ + let a: [?; int] = [1, 2, 3, 4] + let b: [int] = [1, 2, 3, 4] + """ + let res = compile(str) + + assert res["body"][0]["left"][0]["value"]["type_tag"]["name"].as_string() == "[4; int]" + assert res["body"][1]["left"][0]["value"]["type_tag"]["name"].as_string() == "[int]" +} + +def #test test_array_inference_fail { + let str = """ + let a: [?; float] = [1, 2, 3, 4] + """ + assert compile(str) == null + assert env.err() == strip_margin(" + |main@2:14 + | let a: [?; float] = [1, 2, 3, 4] + | ^~~~~~~~~~~~~ + |Incompatible types [4; int] and [?; float]\n") +} + +def #test test_array_size_and_value { + let str = """ + let a = [1, 2, 3, 4] + let b: [int] = a + let c = a.size + let d = a.value + let e = b.size + let f = b.value + """ + assert compile(str) != null +} + +def #test test_call { + let str = """ + def demo_function_1 + def demo_function_2(a: int) + demo_function_1() // empty function + demo_function_2(42) // function with unnamed parameter + demo_function_2(a=42) // function with named parameter + """ + assert compile(str) != null +} + +def #test test_call_fail { + let str = """ + def demo_function_2(a: int) + demo_function_2(a=42, a=11) // function with named parameter twice + + def no_return + var a: int + a = no_return() + """ + assert compile(str) == null + assert env.err() == strip_margin(" + |main@3:31 + | demo_function_2(a=42, a=11) // function with named parameter twice + | ^~~~ + |Cannot have the same parameter name multiple times in a function call. Parameter name was `a`. + | + |main@7:9 + | a = no_return() + | ^~ + |Incompatible types, can't assign void to int\n") +} + +def #test test_yield { + let str = """ + def some_function -> int { + yield 20 + yield 30 + return 40 + } + for var i in some_function() { + print(i) + } + """ + assert compile(str) != null +} + +def #test test_closure { + let str = """ + def some_function { + var a: int = 4 + def inner_function { + print(a) + let a_ref = *a + @a_ref = 20 + } + inner_function() + } + """ + assert compile(str) != null +} + +def #test test_closure_fail { + let str = """ + def some_function { + export def inner_function {} + def inner_function(a: int = 20) {} + def inner_function(a: int...) {} + } + """ + assert compile(str) == null + assert env.err() == strip_margin(" + |main@3:24 + | export def inner_function {} + | ^~~~~~~~~~~~~~~ + |Invalid modifier to closure + | + |main@4:32 + | def inner_function(a: int = 20) {} + | ^~~~~~~~~~~ + |Can't have default parameters for closure + | + |main@5:32 + | def inner_function(a: int...) {} + | ^~~~~~~~~ + |Can't have varargs for closure\n") +} \ No newline at end of file diff --git a/test/test_ctfe.pr b/test/test_ctfe.pr new file mode 100644 index 00000000..bf467657 --- /dev/null +++ b/test/test_ctfe.pr @@ -0,0 +1,102 @@ +import common + +def #test test_constants { + let src = """ + const c = 4 + def test_constants { + // We can't use pointers here + const c = 4 + assert c == 4 + } + test_constants + assert c == 4 + """ + assert run_source(src) == 0 +} + +def #test test_func_call { + let src = """ + def sum(a: int, b: int) -> int { + return a + b + } + + def test_func_call { + // TODO Varargs don't work for some reason + const sum = sum(10, 20) + assert sum == 30 + } + test_func_call + """ + assert run_source(src) == 0 +} + +def #test test_static_if { + let src = """ + def sum(a: int, b: int) -> int { + return a + b + } + + def test_static_if { + const s = sum(10, 20) + #if s > 10 { + var a: int = 10 + } else { + var a: int = 20 + } + assert a == 10 + } + test_static_if + """ + assert run_source(src) == 0 +} + +def #test test_c_functions { + let src = """ + def return_random_number -> long { + cstd::srand(100) + return cstd::rand() + } + + def test_c_functions { + const ac = return_random_number() + let al = return_random_number() + + assert ac == al + } + test_c_functions + """ + assert run_source(src) == 0 +} + +def #test test_strings { + let src = """ + def append_str(a: string, b: string) -> string { + let res = a + b + return @res + } + + const hello_world = append_str("Hello ", "World!") + + def test_strings { + assert hello_world == "Hello World!" + } + test_strings + """ + assert run_source(src) == 0 +} + +def #test test_bug_1 { + let src = """ + def test_bug_1_ -> bool { + return true == (true == true) + } + + def test_bug_1 { + const ac = test_bug_1_() + let al = test_bug_1_() + assert ac == al == true + } + test_bug_1 + """ + assert run_source(src) == 0 +} \ No newline at end of file diff --git a/src/test/test_getopt.pr b/test/test_getopt.pr similarity index 77% rename from src/test/test_getopt.pr rename to test/test_getopt.pr index cc2270ef..9e3aeace 100644 --- a/src/test/test_getopt.pr +++ b/test/test_getopt.pr @@ -1,16 +1,13 @@ import getopt -import util -import test::testsuite - -def test_positional { +def #test test_positional { let args = ["test", "some", "arg", "more"] let options = [ option("arg1"), option("arg2", 2) ] let parser = *getopt::make_parser(options, "-") - tassert(parser.parse(args)) + assert parser.parse(args) print("arg1: ", parser.get_value("arg1").str, "\n") @@ -20,9 +17,13 @@ def test_positional { print(arg2[i], " ") } print("\n") + + assert env.out() == strip_margin("\ + |arg1: some + |arg2: arg more \n") } -def test_longopts { +def #test test_longopts { let args = [ "test", "--arg1", @@ -38,7 +39,7 @@ def test_longopts { option("--arg5", "default") ] let parser = *getopt::make_parser(options, "-") - tassert(parser.parse(args)) + assert parser.parse(args) print("arg1: ", parser.get_value("--arg1").b, "\n") print("arg2: ", parser.get_value("--arg2").str, "\n") @@ -46,9 +47,16 @@ def test_longopts { let arg4 = parser.get_value("--arg4").arr print("arg4: ", arg4[0], " ", arg4[1], "\n") print("arg5: ", parser.get_value("--arg5").str, "\n") + + assert env.out() == strip_margin("\ + |arg1: true + |arg2: test + |arg3: test + |arg4: foo bar + |arg5: default\n") } -def test_shortopts { +def #test test_shortopts { let args = [ "test", "-ab", @@ -64,7 +72,7 @@ def test_shortopts { option('e', "--arg5", 2) ] let parser = *getopt::make_parser(options, "-") - tassert(parser.parse(args)) + assert parser.parse(args) print("a: ", parser.get_value("--arg1").b, "\n") print("b: ", parser.get_value("--arg2").b, "\n") @@ -72,23 +80,11 @@ def test_shortopts { print("d: ", parser.get_value("--arg4").str, "\n") let arg5 = parser.get_value("--arg5").arr print("e: ", arg5[0], " ", arg5[1], "\n") -} -export def run_tests { - print("Running tests on getopt... \n") - run_test("test_positional", *test_positional, strip_margin("\ - |arg1: some - |arg2: arg more \n"), "") - run_test("test_longopts", *test_longopts, strip_margin("\ - |arg1: true - |arg2: test - |arg3: test - |arg4: foo bar - |arg5: default\n"), "") - run_test("test_shortopts", *test_shortopts, strip_margin("\ + assert env.out() == strip_margin("\ |a: true |b: false |c: Foo |d: test - |e: foo bar\n"), "") -} + |e: foo bar\n") +} \ No newline at end of file diff --git a/test/test_json.pr b/test/test_json.pr index 5eb9be6d..af4b321c 100644 --- a/test/test_json.pr +++ b/test/test_json.pr @@ -1,5 +1,6 @@ import json import optional +import common def #test test_numbers { let data = """ diff --git a/test/test_lexer.pr b/test/test_lexer.pr index e76392fd..a5cb3c2f 100644 --- a/test/test_lexer.pr +++ b/test/test_lexer.pr @@ -2,8 +2,7 @@ import common import json def lex(str: &string) -> &Json { - let file = tmpfolder("princess") + "/main.pr" - return json::parse(common::run_compiler(file, str, "--tokens")) + return json::parse(common::run_compiler_on_source(str, ["--tokens" !&string])) } def #test test_empty_file { diff --git a/test/test_map.pr b/test/test_map.pr new file mode 100644 index 00000000..d75810f7 --- /dev/null +++ b/test/test_map.pr @@ -0,0 +1,71 @@ +import common +import map + +// TODO these tests don't nearly cover all cases +def #test test_map_simple { + let m = map::make(int) + m["foo"] = 20 + m["bar"] = 50 + + assert map::size(m) == 2 + assert m["foo"] == 20 + assert m["bar"] == 50 + + map::remove(m, "foo") + assert map::size(m) == 1 + assert not map::get(m, "foo").exists +} + +def #test test_map_collision { + let m = map::make(int) + m["JUvEoj"] = 20 + m["JVVdoj"] = 50 + + assert map::size(m) == 2 + assert m["JUvEoj"] == 20 + assert m["JVVdoj"] == 50 + + map::remove(m, "JVVdoj") + assert map::size(m) == 1 + assert not map::get(m, "JVVdoj").exists +} + +def #test test_map_resize { + let m = map::make(int, 2) + m["1"] = 1 + m["2"] = 2 + m["3"] = 3 + m["4"] = 4 + + assert map::size(m) == 4 + + assert m["1"] == 1 + assert m["2"] == 2 + assert m["3"] == 3 + assert m["4"] == 4 +} + +def is_in(array: &[&string], key: &string) -> bool { + for var i in 0..array.size { + let value = array[i] + if value == key { + return true + } + } + return false +} + +def #test test_map_keys { + let m = map::make(int) + m["JUvEoj"] = 20 + m["JVVdoj"] = 50 + m["foo"] = 0 + m["bar"] = 1 + + let keys = map::keys(m) + assert keys.size == 4 + assert is_in(keys, "JUvEoj") + assert is_in(keys, "JVVdoj") + assert is_in(keys, "foo") + assert is_in(keys, "bar") +} \ No newline at end of file diff --git a/test/test_parser.pr b/test/test_parser.pr index 645144f6..e5fb9558 100644 --- a/test/test_parser.pr +++ b/test/test_parser.pr @@ -2,8 +2,7 @@ import json import common def parse(str: &string) -> &Json { - let file = tmpfolder("princess") + "/main.pr" - return json::parse(common::run_compiler(file, str, "--ast")) + return json::parse(common::run_compiler_on_source(str, ["--ast" !&string])) } def program(jsn: &Json) -> &Json { diff --git a/test/test_runtime.pr b/test/test_runtime.pr new file mode 100644 index 00000000..598a08a0 --- /dev/null +++ b/test/test_runtime.pr @@ -0,0 +1,729 @@ +import common + +def #test test_vector { + assert run_file(test_file("test_vector")) == 0 + assert env.out() == strip_margin("\ + |Destructing Vector + |0 20 30 40 + |Destructing S: 40 + |Destructing S: 30 + |Destructing S: 20 + |Destructing S: 0 + |Destructing Vector + |Destructing Vector + |Destructing S: 20 + |Destructing S: 30 + |Destructing S: 40 + |Destructing S: 0\n" + ) +} + +def #test test_loop { + let src = """ + def test_loop { + var cnt = 0 + while cnt < 10 { + print(cnt) + cnt += 1 + } + print("\n") + + for var i in 0..10 { + print(i) + } + print("\n") + + let foo: *int = null + while foo != null { + @foo = 10 + } + + for var i in 0..10 { + if i == 5 { continue } + print(i) + } + print("\n") + } + test_loop + """ + assert run_source(src) == 0 + assert env.out() == strip_margin("\ + |0123456789 + |0123456789 + |012346789\n" + ) +} + +def #test test_import { + assert run_file(test_file("test_imports")) == 0 + assert env.out() == "0\n" +} + +def #test test_print { + let src = """ + type Point = struct { + x: int + y: int + } + + def test_print { + let a = 20 + let b = 0xDEADBABE !* + print("Hello World ", 1, " ", 'x', " ", 10.5, " ", b, "\n") + error("Hello World ", 1, " ", 'x', " ", 10.5, " ", b, "\n") + + let point: Point = {10, 20} + print(point, "\n") + + let array = [1, 2, 3, 4] + print(array, "\n") + } + test_print + """ + assert run_source(src) == 0 + assert env.out() == strip_margin("\ + |Hello World 1 x 10.500000 0xdeadbabe + |{x = 10, y = 20} !main::Point + |[1, 2, 3, 4]\n" + ) + assert env.err() == strip_margin("\ + |Hello World 1 x 10.500000 0xdeadbabe\n" + ) +} + +def #test test_function_calls { + let src = """ + def pass_dynamic_array(a: string) { + print(a) + } + + def function(a: int, b: double) -> double { return a * b } + def function(b: double, a: int) -> double { return a * b } + def function -> int { return 10 } + + def test_function_calls { + pass_dynamic_array("Some string\n") + pass_dynamic_array(a = "Named parameter\n") + + function(2, 1.5) + function(1.5, 2) + function(2, b = 1.5) + function(1.5, a = 2) + // function(a = 1, b = 1.5) # Ambiguous reference + + assert function == 10 + } + test_function_calls + """ + assert run_source(src) == 0 + assert env.out() == strip_margin("\ + |Some string + |Named parameter\n" + ) +} + +def #test test_length { + let src = """ + def test_length { + let a = "Some string" + assert length(a) == 11 + assert a.length == 11 + } + test_length + """ + assert run_source(src) == 0 +} + +def #test test_allocate { + let src = """ + def test_allocate { + let a = allocate(size_of int) !*int + @a = 10 + assert @a == 10 + free(a) + + let b = allocate(int) + @b = 20 + assert @b == 20 + free(b) + + let c = allocate(int, 10) + c[0] = 10 + c[9] = 20 + assert c[0] == 10 + assert c[9] == 20 + free(c) + } + test_allocate + """ + assert run_source(src) == 0 +} + +def #test test_file_binary { + let src = """ + def test_file_binary { + let fp = open("tmp/test_file_io_binary", "wb+") + + let str = "Some text" + write(fp, str) + let i = 10 + write(fp, *i) + + rewind(fp) + + var str2: [10; char] + read(fp, str2) + print(str2, "\n") + + var i2: int + read(fp, *i2) + print(i2, "\n") + + close(fp) + } + test_file_binary + """ + assert run_source(src) == 0 +} + +def #test test_file_text { + let src = """ + def test_file_text { + let fp = open("tmp/test_file_io_text", "w+") + + fprint(fp, "This is a test\n", 10) + + seek(fp, 0, SEEK_SET) // Same as rewind + + var buffer: [20; char] + read_line(fp, buffer) + print(buffer) + var num: int + cstd::fscanf(fp, "%d".value, *num) + print(num, "\n") + + close(fp) + } + test_file_text + """ + assert run_source(src) == 0 +} + +def #test test_operators { + // TODO We need to test a lot more + let src = """ + def test_operators { + let g = true and false + let h = true and true + let i = false and true + assert not g + assert h + assert not i + } + test_operators + """ + assert run_source(src) == 0 +} + +def #test test_enum { + let src = """ + type Enum = enum { + A = 10; + B; C; D + } + + type Enum2 = enum { + A = 10 + B = A + C + } + + def pass_enum(a: Enum) {} + + def test_enum { + assert Enum::A == 10 + assert Enum::B == 11 + pass_enum(Enum::A) + let a = Enum::A !int + let b = 10 !Enum + pass_enum(b) + + print(to_string(Enum::A), "\n") + print(Enum::B, "\n") + } + test_enum + """ + assert run_source(src) == 0 + assert env.out() == strip_margin("\ + |A + |11\n" + ) +} + +def #test test_function_pointers { + let src = """ + def some_function { + print("Hello\n") + } + + def test_function_pointers { + let function = *some_function + function() + } + test_function_pointers + """ + assert run_source(src) == 0 + assert env.out() == "Hello\n" +} + +def #test test_structs { + let src = """ + type Struct = struct { + a: &string + b: int + c: char + } + + def test_structs { + let s = { + c = 10 + } !Struct + assert s.a == null + assert s.c == 10 + assert s.b == 0 + } + test_structs + """ + assert run_source(src) == 0 +} + +def #test test_unions { + let src = """ + type Union = struct #union { + a: string + b: long + c: int + } + + def test_unions { + let u = { + "some string" + } !Union + print(u, "\n") + + let u2 = { + b = 20 + } !Union + u2.b = 120 + print(u2.c, "\n") + + let u3 = {} !Union + } + test_unions + """ + assert run_source(src) == 0 + assert env.out() == strip_margin("\ + |{a = some string, b = 12, c = 12} !main::Union + |120\n" + ) +} + +def #test test_strings { + let src = """ + def test_strings { + let stra = "Some value" + assert stra == "Some value" + assert stra != "Other value" + } + test_strings + """ + assert run_source(src) == 0 +} + +def #test test_if_stmts { + let src = """ + def test_if_stmts { + if false { + print("false\n") + } else if starts_with("foo", "f") { + print("true\n") + } else { + print("false\n") + } + } + test_if_stmts + """ + assert run_source(src) == 0 + assert env.out() == "true\n" +} + +def #test test_recursion { + let src = """ + def fact(n: int) -> int { + if n <= 1 { return 1 } + else { + return n * fact(n - 1) + } + } + + def test_recursion { + assert fact(10) == 3628800 + } + test_recursion + """ + assert run_source(src) == 0 +} + +def #test test_scoping { + let src = """ + def test_scoping { + var a = 10 + if true { + assert a == 10 + a = 15 + var a = 20 + assert a == 20 + } + assert a == 15 + } + """ + assert run_source(src) == 0 +} + +def #test test_arrays { + let src = """ + def pass_array(a: [int]) { + for var i in 0..a.size { + print(a[i], " ") + } + print("\n") + } + + def test_arrays { + let a = [10, 20, 30] + let b = [] ![int] + pass_array(a) + pass_array(b) + } + test_arrays + """ + assert run_source(src) == 0 + assert env.out() == "10 20 30 \n\n" +} + +def #test test_deref { + let src = """ + type Struct2 = struct { + a: int + t: *Struct2 + } + + def test_deref { + let s = allocate(Struct2) + s.a = 10 + s.t = allocate(Struct2) + s.t.a = 20 + print(s.a, " ", s.t.a, "\n") + } + test_deref + """ + assert run_source(src) == 0 + assert env.out() == "10 20\n" +} + +def #test test_ucs { + let src = """ + type Struct2 = struct { + a: int + t: *Struct2 + } + + def function(s: Struct2) -> int { return s.a } + def function(s: Struct2, a: int) -> int { return s.a + a } + def inc(a: int) -> int { return a + 1} + + def test_ucs { + let s: Struct2 = { 10, null } + + assert s.function() == 10 + assert s.function.inc == 11 + assert s.function(10).inc() == 21 + } + test_ucs + """ + assert run_source(src) == 0 +} + +def #test test_anonymous { + let src = """ + type Struct3 = struct { + a: int + b: struct { + c: int + d: int + } + struct #union { + e: int64 + f: double + } + } + + def test_anonymous { + var s: Struct3 + s = {10, {20, 30}} + s.a = 10 + s.b = {20, 30} + s.e = 0x4034800000000000 + + print(s.f, "\n") + } + test_anonymous + """ + assert run_source(src) == 0 + assert env.out() == "20.500000\n" +} + +def #test test_varargs { + let src = """ + def sum(args: int...) -> int { + var sum = 0 + for var i in 0..args.size { + sum += args[i] + } + return sum + } + + def my_print(args: string...) { + for var i in 0..args.size { + print(args[i], " ") + } + print("\n") + } + + def test_varargs { + assert sum(1, 2, 3) == 6 + assert sum([1, 2, 3]) == 6 + my_print("foo", "bar", "baz") + } + test_varargs + """ + assert run_source(src) == 0 + assert env.out() == "foo bar baz \n" +} + +def #test test_if_expression { + let src = """ + type Struct = struct { + a: &string + b: int + c: char + } + + def test_if_expression { + // TODO I've seen these fail but I can't quite pin down what's wrong with them + var a = 10 if 10 > 20 else 20 + assert a == 20 + var b: int + @(*a if 10 > 20 else *b) = 30 + assert b == 30 + + let c = { c = 10 } !Struct + let d = { c = 0 } !Struct if 10 > 20 else c + assert d.c == 10 + } + test_if_expression + """ + assert run_source(src) == 0 +} + +def #test test_default_parameters { + let src = """ + def takes_default_param(a: int = 0, b: string = "some value") { + print(a, " ", b, "\n") + } + + def test_default_parameters { + takes_default_param() + takes_default_param(10) + takes_default_param(20, "Hello world!") + } + test_default_parameters + """ + assert run_source(src) == 0 + assert env.out() == strip_margin("\ + |0 some value + |10 some value + |20 Hello world!\n" + ) +} + +def #test test_function_overloads { + let src = """ + def function(a: int, b: double) -> double { return a * b } + def function(b: double, a: int) -> double { return a * b } + def function -> int { return 10 } + + def test_function_overloads { + let f1 = *function::() + let f2 = *function::(int, double) + let f3 = *function::(double, int) + + assert f1() == 10 + assert f2(10, 10.5) == 105 + assert f3(10.5, 10) == 105 + } + test_function_overloads + """ + assert run_source(src) == 0 +} + +def #test test_underscore { + let src = """ + def test_underscore { + let _, _ = 10, 20 + assert _ == 20 + _ = "Hello World!" + assert _ == "Hello World!" + } + test_underscore + """ + assert run_source(src) == 0 +} + +def #test test_def_polymorph { + let src = """ + type Struct = struct { + a: &string + b: int + c: char + } + + def function(type T) -> size_t { + return T.size + } + + def add(a: type T, b: T) -> T { + return a + b + } + + def test_def_polymorph { + assert function(int) == 4 + assert function(Struct) == 32 + + assert add(10, 10) == 20 + assert add(10.5, 20.5) == 31.0 + } + test_def_polymorph + """ + assert run_source(src) == 0 +} + +def #test test_type_of { + let src = """ + def test_type_of { + let a = 20 + let b: (type_of a) = 30 + type T = type_of a + + assert T == int + assert (type_of b) == int + } + test_type_of + """ + assert run_source(src) == 0 +} + +def #test test_reflection { + assert run_file(test_file("test_reflection")) == 0 +} + +def #test test_yield { + let src = """ + def range_for(a: int, b: int) -> int { + for var i in a..b { + yield i + } + } + + def range_while(a: int, b: int) -> int { + var i = a + while i < b { + yield i + i = i + 1 + } + } + + def test_yield { + for var i in range_for(0, 10) { + print(i, " ") + } + print("\n") + + for var i in range_while(0, 10) { + print(i, " ") + } + print("\n") + } + test_yield + """ + assert run_source(src) == 0 + assert env.out() == strip_margin("\ + |0 1 2 3 4 5 6 7 8 9 + |0 1 2 3 4 5 6 7 8 9 \n" + ) +} + +def #test test_generators { + let src = """ + type Struct5 = struct { + a: int; b: int; c: int + } + + def iterate(s: *Struct5) -> int { + yield s.a + yield s.b + yield s.c + } + + def test_generators { + let s = { 10, 20, 30 } !Struct5 + + for var i in *s { + print(i, " ") + } + print("\n") + } + test_generators + """ + assert run_source(src) == 0 + assert env.out() == strip_margin("\ + |10 20 30 \n" + ) +} + +def #test test_closures { + let src = """ + def test_closures { + def inner(a: int) -> int { + def inner -> int { + return a + } + return inner() + } + assert inner(10) == 10 + + var a = 20 + var b = 30 + def capture { + assert a == 20 + let local = *b + @local = 40 + assert @local == 40 + } + capture() + assert b == 40 + } + test_closures + """ + assert run_source(src) == 0 +} \ No newline at end of file diff --git a/travis.py b/travis.py index 8dafc69f..b1518266 100644 --- a/travis.py +++ b/travis.py @@ -3,7 +3,13 @@ import subprocess import build import os +import sys +def compile(extra): + args = [build.exe_file("bin/princess2"), "--no-incremental", "-d", "-Isrc", "--buildfolder=build", "--outfile", build.exe_file("bin/princess3"), "src/main.pr"] + if sys.platform == "win32": + args += build.WIN_ARGS + subprocess.check_call(args + extra) def main(): Path("build").mkdir(exist_ok = True) @@ -15,8 +21,13 @@ def main(): build.testrunner([]) print("Running test suite") - os.environ["PRINCESS_COMPILER"] = build.exe_file("bin/princess2") - subprocess.check_call(["bin/testrunner", "./test"]) + # TODO also test on windows + if sys.platform != "win32": + os.environ["PRINCESS_COMPILER"] = build.exe_file("bin/princess2") + subprocess.check_call(["bin/testrunner", "./test"]) + + print("Bootstrapping...") + compile([]) if __name__ == "__main__": main() \ No newline at end of file diff --git a/version b/version index fd77cfda..80e8e28f 100644 --- a/version +++ b/version @@ -1 +1 @@ -VERSION=0.3.3 \ No newline at end of file +VERSION=0.3.4 \ No newline at end of file