diff --git a/src/codegen.pr b/src/codegen.pr index 62500bcf..aadd1f18 100644 --- a/src/codegen.pr +++ b/src/codegen.pr @@ -125,6 +125,8 @@ def type_to_str(tpe: &typechecking::Type) -> Str { ret = "%\"" + tpe.type_name + '"' case typechecking::TypeKind::INTERFACE_IMPL ret = type_to_str(tpe.tpe) + case typechecking::TypeKind::TO_INFER + ret = "" case error(debug::type_to_str(tpe), " ", typechecking::is_polymorph(tpe), "\n") error(tpe.kind, "\n") diff --git a/src/compiler.pr b/src/compiler.pr index f1535adb..cbc1fd31 100644 --- a/src/compiler.pr +++ b/src/compiler.pr @@ -4743,6 +4743,8 @@ export def walk_expression(node: &parser::Node, state: &State) -> Value { case parser::NodeKind::IF_EXPR expr = walk_IfExpr(node, state) case parser::NodeKind::RANGE, parser::NodeKind::RANGE_INC + case parser::NodeKind::LAMBDA + expr = walk_Lambda(node, state) case; error(node.kind, "\n") assert(false) @@ -5778,6 +5780,31 @@ export def walk_VarDecl(node: &parser::Node, state: &State, set_constant: bool = } } +def walk_Lambda(node: &parser::Node, state: &State) -> Value { + import_cstd_function("malloc", state) + let function = node.value.lambda.function + if not function { return NO_VALUE } + + let loc = make_location(node, state) + let context_tpe = function.state + + let ret = state.alloca(node.value.lambda.closure_type, loc) + + typechecking::create_type_entry(typechecking::reference(context_tpe)) + let context_ptr = create_closure_context(function, ret, loc, state, insert_temporary = true) + + predeclare_function(function) + create_function(node, function.tpe, node.value.lambda.body, node.inner_scope, null, state, is_closure = true, params = node.value.lambda.parameters) + state.module.imported.add(node.tpe.type_name) + + let context = create_closure_context_captures(function, loc, state) + state.store(context_ptr, context, loc) + + push_local_var(context_tpe.type_name, reference(context_tpe), state.current_function) + + return state.load(node.value.lambda.closure_type, ret, loc) +} + def walk_Def(node: &parser::Node, state: &State) { import_cstd_function("malloc", state) let function = node.value.def_.function @@ -5809,6 +5836,21 @@ def walk_Def(node: &parser::Node, state: &State) { push_declare(node, ret, value.name, state) + let context_ptr = create_closure_context(function, ret, loc, state) + + predeclare_function(function) + create_function(node, node.tpe, node.value.def_.body, node.inner_scope, null, state, is_closure = true, params = node.value.def_.params) + state.module.imported.add(node.tpe.type_name) + + let context = create_closure_context_captures(function, loc, state) + state.store(context_ptr, context, loc) + + push_local_var(context_tpe.type_name, reference(context_tpe), state.current_function) +} + +def create_closure_context(function: &Function, ret: Value, loc: &Value, state: &State, insert_temporary: bool = false) -> Value { + let context_tpe = function.state + let context_ptr_i8 = state.call("malloc", pointer(builtins::int8_), [[ kind = ValueKind::INT, tpe = builtins::size_t_, i = context_tpe.size ] !Value], loc) let ref_count_i8 = state.call("malloc", pointer(builtins::int8_), [[ kind = ValueKind::INT, tpe = builtins::size_t_, i = builtins::int64_.size ] !Value], loc) let ref_count = state.bitcast(pointer(builtins::int64_), ref_count_i8, loc) @@ -5821,16 +5863,23 @@ def walk_Def(node: &parser::Node, state: &State) { closure = state.insert_value(typechecking::reference(null), closure, context_ptr_i8, [1], loc) closure = state.insert_value(typechecking::reference(null), closure, context_tpe_value, [2], loc) - let context_fun_ptr = state.gep(pointer(pointer(node.tpe)), value.tpe, ret, [make_int_value(0), make_int_value(0)], loc) - state.store(context_fun_ptr, [ kind = ValueKind::GLOBAL, tpe = pointer(node.tpe), name = node.tpe.type_name ] !Value, loc) - let context_ref_ptr = state.gep(pointer(typechecking::reference(null)), value.tpe, ret, [make_int_value(0), make_int_value(1)], loc) + let context_fun_ptr = state.gep(pointer(pointer(function.tpe)), ret.tpe.tpe, ret, [make_int_value(0), make_int_value(0)], loc) + state.store(context_fun_ptr, [ kind = ValueKind::GLOBAL, tpe = pointer(function.tpe), name = function.tpe.type_name ] !Value, loc) + let context_ref_ptr = state.gep(pointer(typechecking::reference(null)), ret.tpe.tpe, ret, [make_int_value(0), make_int_value(1)], loc) state.store(context_ref_ptr, closure, loc) - predeclare_function(function) - create_function(node, node.tpe, node.value.def_.body, node.inner_scope, null, state, is_closure = true) - state.module.imported.add(node.tpe.type_name) + if insert_temporary { + let ctx = state.alloca(typechecking::reference(null), loc) + state.store(ctx, closure, loc) + create_temporary(ctx, closure, loc, state) + } let context_ptr = state.bitcast(pointer(context_tpe), context_ptr_i8, loc) + return context_ptr +} + +def create_closure_context_captures(function: &Function, loc: &Value, state: &State) -> Value { + let context_tpe = function.state var context = [ kind = ValueKind::UNDEF, tpe = context_tpe ] !Value for var i in 0..function.captures.length { @@ -5854,9 +5903,7 @@ def walk_Def(node: &parser::Node, state: &State) { } context = state.insert_value(context_tpe, context, value, [i], loc) } - state.store(context_ptr, context, loc) - - push_local_var(context_tpe.type_name, reference(context_tpe), state.current_function) + return context } def walk_Defer(node: &parser::Node, state: &State) { @@ -7180,7 +7227,8 @@ export def create_function( block: &Block, state: &State, no_cleanup: bool = false, - is_closure: bool = false + is_closure: bool = false, + params: &Vector(&Node) = null ) { if not tpe { return } let function = state.module.result.functions.get_or_default(tpe.type_name, null) @@ -7203,9 +7251,9 @@ export def create_function( state.inline_start_block = block function.locals = map::make(Str, type &typechecking::Type) - if node.value.def_.params { - for var i in 0..vector::length(node.value.def_.params) { - let param = node.value.def_.params(i).value.param.name + if params { + for var i in 0..vector::length(params) { + let param = params(i).value.param.name if not param or not param.svalue { continue } state.add_local(function, param.svalue, param.svalue.tpe) } @@ -7264,10 +7312,10 @@ export def create_function( add_type_meta(tpe.parameter_t(i).tpe, state) } - if node and node.value.def_.params { + if params { errors::current_signature = node.signature_hash - for var i in 0..vector::length(node.value.def_.params) { - let param = node.value.def_.params(i).value.param.name + for var i in 0..vector::length(params) { + let param = params(i).value.param.name if not param { continue } scope::create_dependency(state.current_value(), param.svalue) } @@ -9090,6 +9138,8 @@ def do_create_type(tpe: &typechecking::Type, svalue: &scope::Value, module: &too value.values(8) = [ kind = ValueKind::STRUCT, tpe = array(pointer(builtins::Type_)), values = array_values ] !Value push_variants(tpe, global, module, state, cache) + case typechecking::TypeKind::TO_INFER + // This should only happen in an error case case error(tpe.kind, "\n") assert(false) diff --git a/src/consteval.pr b/src/consteval.pr index 73f18d80..099978c7 100644 --- a/src/consteval.pr +++ b/src/consteval.pr @@ -271,6 +271,28 @@ def unwrap_type_def(tpe: &typechecking::Type) -> &typechecking::Type { return res } +def walk_Lambda(node: &parser::Node, state: &typechecking::State) { + let body = node.value.lambda.body + node.inner_scope = scope::enter_function_scope(state.module.scope) + + // Add return for single value + if body.length == 1 { + let expr = body(0) + if not expr { return } + + switch expr.kind { + case parser::NodeKind::INTEGER..=parser::NodeKind::IDENTIFIER, parser::NodeKind::DEFINED, + parser::NodeKind::RANGE..=parser::NodeKind::SHR_EQ, + parser::NodeKind::ASSIGN, parser::NodeKind::FUNC_CALL, parser::NodeKind::LAMBDA + + let n = [ kind = parser::NodeKind::RETURN] !&Node + n.value.body = vector::make(type &Node) + n.value.body.push(body(0)) + body(0) = n + } + } +} + export def walk_Def(node: &parser::Node, state: &typechecking::State) { let share = node.value.def_.share let name = node.value.def_.name @@ -793,7 +815,7 @@ export def compile_function(value: &scope::Value, context: &scope::Scope, argume // This is a big ugly but what can we do let debug = toolchain::debug_sym toolchain::debug_sym = false - compiler::create_function(node, node.tpe, node.value.def_.body, node.inner_scope, null, compiler_state) + compiler::create_function(node, node.tpe, node.value.def_.body, node.inner_scope, null, compiler_state, params = node.value.def_.params) toolchain::debug_sym = debug if function.defer_functions { @@ -1096,6 +1118,8 @@ def do_walk(node: &parser::Node, state: &typechecking::State) { walk_Assert(node, state) case parser::NodeKind::FROM walk_From(node, state) + case parser::NodeKind::LAMBDA + walk_Lambda(node, state) } } @@ -1154,6 +1178,7 @@ export def consteval(state: &typechecking::State) { compiler::predeclare_functions(state.module) let function = [ + unmangled = "__main", is_global = true, locals = map::make(type &typechecking::Type) ] !&compiler::Function diff --git a/src/debug.pr b/src/debug.pr index b25c5d95..dd4d91e8 100644 --- a/src/debug.pr +++ b/src/debug.pr @@ -609,6 +609,11 @@ export def node_to_json(node: &parser::Node, types: bool = false) -> &Json { case parser::NodeKind::STAR res = json::make_object() res("kind") = "Star" + case parser::NodeKind::LAMBDA + res = json::make_object() + res("kind") = "Lambda" + res("parameters") = node_vec_to_json(node.value.lambda.parameters, types) + res("body") = node_vec_to_json(node.value.lambda.body, types) case error(node.kind, "\n") assert @@ -876,6 +881,8 @@ export def type_to_str(tpe: &typechecking::Type, full_name: bool = false) -> Str return variant_t_to_string(tpe, full_name) case typechecking::TypeKind::INTERFACE_IMPL return type_to_str(tpe.tpe, full_name) + "/" + type_to_str(tpe.intf, full_name) + "#" + tpe.module.module + case typechecking::TypeKind::TO_INFER + return "" case error(tpe.kind, "\n") assert diff --git a/src/lexer.pr b/src/lexer.pr index d9c76d5b..111b73d1 100644 --- a/src/lexer.pr +++ b/src/lexer.pr @@ -1,5 +1,6 @@ import util import json +import vector // Reserve 0 for empty token export type TokenType = enum { @@ -111,6 +112,12 @@ export type TokenType = enum { EOF } +export type Brace = enum { + PAREN + SQUARE + BRACE +} + export type TokenValue = struct #union { str: StringSlice ch: char @@ -779,10 +786,6 @@ def parse_symbol(s: Str, i: *int, line: *int, column: *int) -> Token { switch first { case '>'; tt = TokenType::OP_GT case '<'; tt = TokenType::OP_LT - case '{'; tt = TokenType::O_BRACE - case '}'; tt = TokenType::C_BRACE - case '['; tt = TokenType::O_SQUARE - case ']'; tt = TokenType::C_SQUARE case '+'; tt = TokenType::OP_ADD case '-'; tt = TokenType::OP_SUB case '*'; tt = TokenType::OP_MUL @@ -819,12 +822,12 @@ def is_whitespace(c: char) -> bool { return c == ' ' or c == '\t' or c == '\r' } -def parse_whitespace(depth: int, s: Str, i: *int, line: *int, column: *int) -> Token { +def parse_whitespace(brace_stack: &Vector(Brace), s: Str, i: *int, line: *int, column: *int) -> Token { let start_line = @line let start_column = @column var c = peek_char(s, i, 0) - while is_whitespace(c) or (c == '\n' and depth > 0) { + while is_whitespace(c) or (c == '\n' and brace_stack.length > 0 and brace_stack.peek() == Brace::PAREN) { var is_newline = c == '\n' c = next_char(s, i, line, column) if is_newline { @@ -839,7 +842,7 @@ export def lex(s: Str, line: int = 0, column: int = 0, end_line: int = MAX_INT32 var token_list = zero_allocate(TokenList) var head = token_list - var depth = 0 + var brace_stack = vector::make(Brace) var i = 0 var start_column = 0 @@ -863,24 +866,44 @@ export def lex(s: Str, line: int = 0, column: int = 0, end_line: int = MAX_INT32 let c = peek_char(s, *i, 0) var token: Token - if is_whitespace(c) or c == '\n' and depth > 0 { + if is_whitespace(c) or c == '\n' and brace_stack.length > 0 and brace_stack.peek() == Brace::PAREN { // TODO Make this work inside {} - token = parse_whitespace(depth, s, *i, *line, *column) + token = parse_whitespace(brace_stack, s, *i, *line, *column) } else if c == '\n' { token = simple_token(TokenType::NEW_LINE, line, column, line, column + 1) column = 0 line += 1 i += 1 } else if c == '(' { - depth += 1 + brace_stack.push(Brace::PAREN) token = simple_token(TokenType::O_PAREN, line, column, line, column + 1) i += 1 column += 1 + } else if c == '[' { + brace_stack.push(Brace::SQUARE) + token = simple_token(TokenType::O_SQUARE, line, column, line, column + 1) + i += 1 + column += 1 + } else if c == '{' { + brace_stack.push(Brace::BRACE) + token = simple_token(TokenType::O_BRACE, line, column, line, column + 1) + i += 1 + column += 1 } else if c == ')' { - depth -= 1 + if brace_stack.length > 0 { brace_stack.pop() } token = simple_token(TokenType::C_PAREN, line, column, line, column + 1) i += 1 column += 1 + } else if c == ']' { + if brace_stack.length > 0 { brace_stack.pop() } + token = simple_token(TokenType::C_SQUARE, line, column, line, column + 1) + i += 1 + column += 1 + } else if c == '}' { + if brace_stack.length > 0 { brace_stack.pop() } + token = simple_token(TokenType::C_BRACE, line, column, line, column + 1) + i += 1 + column += 1 } else if c == '"' { var triple_quoted = false if peek_char(s, *i, 1) == '"' and peek_char(s, *i, 2) == '"' { diff --git a/src/parser.pr b/src/parser.pr index b9336a47..e11bce4c 100644 --- a/src/parser.pr +++ b/src/parser.pr @@ -121,6 +121,7 @@ export type NodeKind = enum { TYPE_CONSTRUCTOR VARIANT_T TUPLE_T // Uses variant_t + LAMBDA } export type ShareMarker = enum { @@ -167,6 +168,13 @@ export type NodeDef = struct { doc: Str } +export type NodeLambda = struct { + parameters: &Vector(&Node) + body: &Vector(&Node) + closure_type: &typechecking::Type + function: &compiler::Function +} + export type NodeParam = struct { varargs: bool kw: VarDecl @@ -395,6 +403,7 @@ export type NodeValue = struct #union { program: NodeProgram type_constructor: NodeTypeConstructor from_: NodeFrom + lambda: NodeLambda body: &Vector(&Node) expr: &Node @@ -586,6 +595,8 @@ export def destruct(node: *Node) { case NodeKind::INTEGER, NodeKind::CHAR, NodeKind::FLOAT, NodeKind::BOOLEAN, NodeKind::NULL, NodeKind::UNDEF, NodeKind::BREAK, NodeKind::CONTINUE, NodeKind::WORD_T, NodeKind::STAR + case NodeKind::LAMBDA + __destruct__(*node.value.lambda) case error(node.kind, "\n") assert @@ -702,6 +713,8 @@ export def construct(copy: *Node, node: *Node) { NodeKind::BOOLEAN, NodeKind::NULL, NodeKind::UNDEF, NodeKind::BREAK, NodeKind::CONTINUE, NodeKind::WORD_T, NodeKind::STAR copy.value = node.value + case NodeKind::LAMBDA + copy.value.lambda = node.value.lambda case error(node.kind, "\n") assert @@ -849,6 +862,9 @@ export def offset(node: &Node, changes: &[server::TextDocumentChangeEvent]) { case NodeKind::INTEGER, NodeKind::CHAR, NodeKind::FLOAT, NodeKind::BOOLEAN, NodeKind::NULL, NodeKind::UNDEF, NodeKind::BREAK, NodeKind::CONTINUE, NodeKind::WORD_T, NodeKind::STAR + case NodeKind::LAMBDA + offset(node.value.lambda.parameters, changes) + offset(node.value.lambda.body, changes) case error(node.kind, "\n") assert @@ -980,6 +996,9 @@ export def clear(node: &Node) { case NodeKind::INTEGER, NodeKind::CHAR, NodeKind::FLOAT, NodeKind::BOOLEAN, NodeKind::NULL, NodeKind::UNDEF, NodeKind::BREAK, NodeKind::CONTINUE, NodeKind::WORD_T, NodeKind::STAR + case NodeKind::LAMBDA + clear(node.value.lambda.parameters) + clear(node.value.lambda.body) case error(node.kind, "\n") assert @@ -1194,6 +1213,11 @@ export def find(node: &Node, line: int, column: int) -> &Node { NodeKind::BOOLEAN, NodeKind::NULL, NodeKind::UNDEF, NodeKind::BREAK, NodeKind::CONTINUE, NodeKind::WORD_T, NodeKind::STAR return node + case NodeKind::LAMBDA + var n2 = find(node.value.lambda.parameters, line, column) + if n2 { return n2 } + n2 = find(node.value.lambda.body, line, column) + if n2 { return n2 } case error(node.kind, "\n") assert @@ -1390,6 +1414,9 @@ export def deep_copy_node(node: &Node, clear_svalue: bool = true) -> &Node { case NodeKind::INTEGER, NodeKind::CHAR, NodeKind::FLOAT, NodeKind::BOOLEAN, NodeKind::NULL, NodeKind::UNDEF, NodeKind::BREAK, NodeKind::CONTINUE, NodeKind::WORD_T, NodeKind::STAR + case NodeKind::LAMBDA + copy.value.lambda.parameters = deep_copy_vector_of_nodes(node.value.lambda.parameters, clear_svalue) + copy.value.lambda.body = deep_copy_vector_of_nodes(node.value.lambda.body, clear_svalue) case error(node.kind, "\n") assert @@ -1618,6 +1645,8 @@ def parse_identifier(parse_state: &ParseState) -> &Node { break } + if path.length == 0 { return null } + var args: &Vector(&Node) = null token = peek(parse_state) if token.tpe == lexer::TokenType::DOUBLE_COLON { @@ -2512,7 +2541,10 @@ def parse_term(parse_state: &ParseState) -> &Node { var node = [] !&Node var last_string: String - if token.tpe == lexer::TokenType::O_PAREN { + if token.tpe == lexer::TokenType::O_BRACE { + back(parse_state) + return expect_lambda(parse_state) + } else if token.tpe == lexer::TokenType::O_PAREN { node = parse_expression(parse_state) let end_token = expect(parse_state, lexer::TokenType::C_PAREN, "Expecting ')'") if node { @@ -3111,7 +3143,7 @@ def parse_assign(parse_state: &ParseState) -> &Node { def parse_expression(parse_state: &ParseState) -> &Node { let node = parse_assign(parse_state) - let token = peek(parse_state) + var token = peek(parse_state) if token.tpe == lexer::TokenType::K_IF { return expect_if_expr(parse_state, node) } @@ -3129,7 +3161,7 @@ def expect_expression(parse_state: &ParseState) -> &Node { def parse_expression_no_assign(parse_state: &ParseState) -> &Node { let node = parse_type_of(parse_state) - let token = peek(parse_state) + var token = peek(parse_state) if token.tpe == lexer::TokenType::K_IF { return expect_if_expr(parse_state, node) } @@ -4105,6 +4137,81 @@ def expect_yield_stmt(parse_state: &ParseState) -> &Node { return expect_yield_or_return(true, parse_state) } +def expect_lambda(parse_state: &ParseState) -> &Node { + let start = peek(parse_state) + expect(parse_state, lexer::TokenType::O_BRACE, "Expected {") + var token = peek(parse_state) + + var tokens = parse_state.tokens + var parsed_parameters = true + var params = vector::make(type &Node) + + // Try to parse parameters + loop { + let name_tok = peek(parse_state) + let name = parse_identifier(parse_state) + if not name { parsed_parameters = false; break } + var tpe: &Node + + let colon = peek(parse_state) + if colon.tpe == lexer::TokenType::COLON { + pop(parse_state) + + tpe = parse_variant_type(parse_state, false) + if not tpe { + errors::errort(peek(parse_state), parse_state, "Expected type") + break + } + } + + let param = make_node(NodeKind::PARAMETER, name_tok.line, name_tok.column, parse_state) + param.value.param = [ + name = name, + tpe = tpe + ] !NodeParam + + param._hash = combine_hashes(param.kind !uint64, hash(name), hash(tpe)) + params.push(param) + + token = peek(parse_state) + if token.tpe == lexer::TokenType::COMMA or + token.tpe == lexer::TokenType::EOF { + + pop(parse_state) + continue + } + break + } + + if parsed_parameters { + token = peek(parse_state) + if token.tpe != lexer::TokenType::ARROW { + parsed_parameters = false + params = vector::make(type &Node) + } else { + pop(parse_state) + } + } + + if not parsed_parameters { + parse_state.tokens = tokens + } + + let body = vector::make(type &Node) + parse_block(parse_state, body) + + expect(parse_state, lexer::TokenType::C_BRACE, "Expected }") + + let lam = make_node(NodeKind::LAMBDA, start.line, start.column, parse_state) + lam.value.lambda = [ + parameters = params, + body = body + ] !NodeLambda + lam._hash = combine_hashes(lam.kind !uint64, hash(params), hash(body)) + + return lam +} + def expect_from(parse_state: &ParseState) -> &Node { var token = expect(parse_state, lexer::TokenType::K_FROM, "Expected from") let line = token.line diff --git a/src/server/cache.pr b/src/server/cache.pr index 5cf6ccee..505bf7ed 100644 --- a/src/server/cache.pr +++ b/src/server/cache.pr @@ -210,7 +210,7 @@ def recompile_node(module: &toolchain::Module, node: &parser::Node) { typechecking::walk_Def(node, module.state) compiler::verify_function(node) compiler::predeclare_function(node.tpe, module) - compiler::create_function(node, node.tpe, node.value.def_.body, node.inner_scope, null, module.compiler_state) + compiler::create_function(node, node.tpe, node.value.def_.body, node.inner_scope, null, module.compiler_state, params = node.value.def_.params) } } else if node.kind == parser::NodeKind::VAR_DECL and node.value.var_decl.kw != parser::VarDecl::CONST { typechecking::walk_top_VarDecl(node, module.state) diff --git a/src/server/server.pr b/src/server/server.pr index a53feca0..d2200778 100644 --- a/src/server/server.pr +++ b/src/server/server.pr @@ -433,7 +433,7 @@ def push_members(node: &parser::Node, completions: &Vector(CompletionItem)) { description = "", detail = ": " + debug::type_to_str(field.tpe) ] !CompletionItemLabelDetails, - kind = CompletionItemKind::Field !int, + kind = CompletionItemKind::Field !int ] !CompletionItem) } } diff --git a/src/typechecking.pr b/src/typechecking.pr index 6c1d1d25..e473fe55 100644 --- a/src/typechecking.pr +++ b/src/typechecking.pr @@ -67,6 +67,7 @@ export type TypeKind = enum { // This is a type that gets assigned whenever you pass an interface implementation to // a generic function INTERFACE_IMPL + TO_INFER // Placeholder type for inference } export type TypeMember = struct { @@ -167,7 +168,7 @@ export type Type = struct { // This gets set if it might also be a type argument, in that case we also consider the overload with a type // As an example, consider [int], this might be an array of types or an array type may_be_type: &Type - // True if this is an anonymous struct. name should be "" + // True if this is an anonymous struct or a lambda is_anon: bool } @@ -513,7 +514,6 @@ export def tpe(np: NamedParameter) -> &Type { export type State = struct { module: weak_ref(toolchain::Module) counter: int - lambda_counter: int scope: weak_ref(scope::Scope) // Vector of Type function_stack: &Vector(&compiler::Function) @@ -523,8 +523,11 @@ export type State = struct { current_variable: &scope::Value // TODO This should be a parameter but its currently too hard to fix block_implicit_conv: bool + current_lambda: &Type } +var lambda_counter = 0 // TODO Why is this global state? + def current_value(state: &State) -> &scope::Value { if state.current_variable { return state.current_variable @@ -1651,6 +1654,35 @@ export def convert_type_score(a: &Type, b: &Type, module: &toolchain::Module, is return 0 if equals(a.tpe, b.tpe) else -1 } } + if a.kind == TypeKind::CLOSURE and b.kind == TypeKind::CLOSURE and b.is_anon { + if vector::length(a.parameter_t) != vector::length(b.parameter_t) { + return -1 + } + for var i in 0..vector::length(a.parameter_t) { + let param_a = a.parameter_t(i) + let param_b = b.parameter_t(i) + if not equals(param_a.tpe, param_b.tpe) and not (param_b.tpe and param_b.tpe.kind == TypeKind::TO_INFER) { + return -1 + } + } + // If we have a return type we're going to check them + var has_right_return_types = false + if b.return_t.length > 0 { + if vector::length((@a).return_t) != vector::length((@b).return_t) { + return -1 + } + for var i in 0..vector::length((@a).return_t) { + if not equals(a.return_t(i), b.return_t(i)) { + return -1 + } + } + has_right_return_types = true + } else if a.return_t.length == 0 { + has_right_return_types = true + } + + return 2 if has_right_return_types else 3 + } if a.kind == TypeKind::CLOSURE and b.kind == TypeKind::POINTER and b._tpe and b._tpe.kind == TypeKind::FUNCTION { b = b._tpe if vector::length(a.parameter_t) != vector::length(b.parameter_t) { @@ -2193,6 +2225,21 @@ export def make_union_type(fields: &[StructMember], current_type: &Type = null) return tpe } +export def make_tuple_type(types: &Vector(&Type)) -> &Type { + let fields = vector::make(StructMember) + let tpe = make_type_raw(TypeKind::TUPLE) + + for var t in types { + fields.push([ tpe = t ] !StructMember) + } + + let struct_type = make_struct_type(fields.to_array()) // TODO Maybe a have a seperate function to calculate align and size + tpe.return_t = types + tpe.align = struct_type.align + tpe.size = struct_type.size + return tpe +} + export def make_struct_type(fields: &[StructMember], current_type: &Type = null) -> &Type { let struct_tpe = make_type_raw(TypeKind::STRUCT) if not current_type else current_type let field_types = vector::make(TypeMember) @@ -2680,7 +2727,7 @@ export def do_type_lookup(node: &parser::Node, state: &State, current_type: &Typ for var i in 0..vector::length(node.value.t_func.ret) { var return_type: &Type = null if current_type { - return_type = return_type.return_t(i) + return_type = current_type.return_t(i) } let arg = node.value.t_func.ret(i) @@ -3277,6 +3324,64 @@ def infer_struct_type(node: &parser::Node, tpe: &Type) { } } +def infer_lambda_parameter_types(node: &parser::Node, tpe: &Type, state: &State) -> bool { + let ftpe = node.value.lambda.closure_type + + if tpe.parameter_t.length != ftpe.parameter_t.length { + errors::errorn(node, "Can't assign lambda, parameter length mismatch, got ", + to_string(ftpe.parameter_t.length), " parameters, expected ", to_string(tpe.parameter_t.length)) + return false + } + + // Replace to_infer types + for var i in 0..ftpe.parameter_t.length { + let ftpe_n = ftpe.parameter_t(i).tpe + let expected = tpe.parameter_t(i).tpe + + if ftpe_n and ftpe_n.kind == TypeKind::TO_INFER { + @ftpe_n = @expected + } + } + + ftpe.return_t.add_all(tpe.return_t) + + return true +} + +def check_lambda_return_type(node: &parser::Node, tpe: &Type, state: &State) { + let ftpe = node.value.lambda.closure_type + + if tpe.return_t.length > 0 { + if ftpe.return_t.length < 1 { + errors::errorn(node, "Can't assign lambda, expected a return type of ", + debug::type_to_str(tpe.return_t(0)), " and lambda doesn't return a value") + } else if not is_assignable(tpe.return_t(0), ftpe.return_t(0), state.module) { + errors::errorn(node, "Can't assign lambda, wrong return type. Expected ", + debug::type_to_str(tpe.return_t(0)), " got ", debug::type_to_str(ftpe.return_t(0))) + } + } else { + if ftpe.return_t.length > 0 and tpe.return_t.length > 0 { + errors::errorn(node, "Can't assign lambda, expected no return type but lambda returns ", + debug::type_to_str(tpe.return_t(0))) + } + } +} + +def walk_lambda(value: &parser::Node, ltpe: &Type, state: &State) -> bool { + if value and value.kind == parser::NodeKind::LAMBDA and ltpe { + // Infer type for lambda + walk_Lambda(value, state) + if value.value.lambda.function.is_typechecked { return true } + + if infer_lambda_parameter_types(value, ltpe, state) { + walk_Lambda_body(value, state) + check_lambda_return_type(value, ltpe, state) + } + return true + } + return false +} + def collapse_types( node: &parser::Node, ltypes: &Vector(&Type), right: &Vector(&parser::Node), state: &State, @@ -3293,18 +3398,25 @@ def collapse_types( ltpe = ltypes(k) infer_struct_type(value, ltpe) } - + if not value.is_assign_right { - if left and i < left.length and node.parent.kind == parser::NodeKind::PROGRAM { + let cond = left and i < left.length and node.parent.kind == parser::NodeKind::PROGRAM + var current_value: &scope::Value + if cond { let l = left(i) let current_value = state.current_value state.current_variable = l.value.id_decl.value.svalue + } + + if not walk_lambda(value, ltpe, state) { walk(node, value, state) + } + + if cond { state.current_variable = current_value - } else { - walk(node, value, state) } } + let rtpe = (@value).tpe if rtpe and (@rtpe).kind == TypeKind::TUPLE and (not left or left.length > 1) { for var j in 0..vector::length((@rtpe).return_t) { @@ -4075,6 +4187,173 @@ def check_is_valid_function(ident: &parser::Node, par: &Vector(NamedParameter), } } +def make_closure_type(name: &parser::Node, parameter_t: &Vector(NamedParameter), return_t: &Vector(&Type)) -> &Type { + let types = allocate_ref(StructMember, 2) + types(0) = [ tpe = pointer(make_function_type_n(name, parameter_t, return_t)), name = "function" ] !StructMember + types(1) = [ tpe = reference(null), name = "data"] !StructMember + let closure_type = make_struct_type(types) + closure_type.kind = TypeKind::CLOSURE + closure_type.parameter_t = vector::make(NamedParameter) + closure_type.return_t = vector::make(type &typechecking::Type) + return closure_type +} + +export def walk_Lambda(node: &parser::Node, state: &State) { + let params = node.value.lambda.parameters + let outer_scope = node.scope + let inner_scope = node.inner_scope + + var tpe: &Type + var closure_type: &Type + + let parameter_t = vector::make(NamedParameter) + var return_t = vector::make(type &Type) + + let ident = parser::make_identifier(state.function_stack.peek().unmangled + "#lambda." + lambda_counter) + closure_type = make_closure_type(ident, parameter_t, return_t) + closure_type.is_anon = true + lambda_counter += 1 + + let c_parameter_t = closure_type.parameter_t + return_t = closure_type.return_t + + // Need to store a strong reference + node.value.lambda.closure_type = closure_type + + // State argument + parameter_t.push([ name = "__state", _tpe = reference(null) ] !NamedParameter) + + var needs_inference = false + for var i in 0..params.length { + let param = params(i) + + let name = param.value.param.name + var tpe: &Type + if param.value.param.tpe { + tpe = typechecking::type_lookup(param.value.param.tpe, state, null, true) + } else { + // Place type placeholder for inference + tpe = make_type_raw(TypeKind::TO_INFER) + needs_inference = true + } + + let np = [ + name = last_ident_to_str(name), + node = name, + _tpe = tpe + ] !NamedParameter + parameter_t.push(np) + c_parameter_t.push(np) + } + + // Create function type + tpe = make_function_type() + tpe.parameter_t = parameter_t + tpe.return_t = return_t + tpe.node = node + tpe.name = parser::identifier_to_str(ident, false) + tpe.type_name = tpe.name + tpe.line = node.loc.line + tpe.module = state.module + + let function = [ + tpe = tpe, + module = state.module, + locals = map::make(type &Type), + unmangled = tpe.name, + is_closure = true, + scope = state.scope, + inner_scope = inner_scope, + captures = vector::make(type weak_ref(scope::Value)) + ] !&compiler::Function + node.value.lambda.function = function + + node.tpe = closure_type + + if not needs_inference { + walk_Lambda_body(node, state) + function.is_typechecked = true + } +} + +// We can only go into the body once the types are figured out +def walk_Lambda_body(node: &parser::Node, state: &State) { + let body = node.value.lambda.body + let inner_scope = node.inner_scope + let outer_scope = node.scope + let function = node.value.lambda.function + + let tpe = function.tpe + if function.is_typechecked { return } + + // State parameter + let value = scope::create_variable(inner_scope, parser::make_identifier("__state"), parser::ShareMarker::NONE, parser::VarDecl::VAR, reference(null), null) + + // Create parameters + for var i in 0..tpe.parameter_t.length - 1 { + let np = tpe.parameter_t(i + 1) + if not np.node { continue } + var tpe2 = np.tpe + if tpe2 { + if tpe2.kind == TypeKind::INTERFACE_IMPL { + tpe2 = tpe2.tpe + } else if tpe2.kind == TypeKind::TO_INFER { + errors::errorn(np.node, "Couldn't infer type for lambda parameter") + node.value.lambda.function = null // So that this doesn't make it to compile + return + } + } + + let value = scope::create_variable(inner_scope, np.node, parser::ShareMarker::NONE, parser::VarDecl::VAR, tpe2, null) + np.node.svalue = value + } + + state.scope = inner_scope + push_function(state, function) + let prev_lambda = state.current_lambda + state.current_lambda = tpe + + var global_scope = state.scope.parent + while not scope::is_global(global_scope) { + global_scope = global_scope.parent + } + + let body_copy = vector::copy(body) + for var i in 0..vector::length(body_copy) { + // Eval constant expressions + consteval::walk(node, body_copy(i), state) + } + for var i in 0..vector::length(body) { + let inner = body(i) + // Create scopes for inner functions + if inner.kind == parser::NodeKind::DEF { + inner.scope = state.scope + inner.inner_scope = scope::enter_function_scope(global_scope) + } + walk(node, inner, state) + } + + pop_function(state) + state.scope = outer_scope + + let members = allocate_ref(StructMember, function.captures.length) + for var i in 0..function.captures.length { + members(i) = [ name = to_string(i), tpe = function.captures(i).tpe ] !StructMember + } + + function.state = make_struct_type(members) + function.state.name = function.unmangled + ".context" + function.state.type_name = append_module(function.state.name, state.module.module) + + // We need to do this here again + if vector::length(tpe.parameter_t) > 0 { + let first_param = tpe.parameter_t(0) + create_type_entry(first_param.tpe, false, tpe, state.module, overwrite = true) + } + + state.current_lambda = prev_lambda +} + export def walk_Def(node: &parser::Node, state: &State, polymorph: bool = false) { node.value.def_.is_compiled = true @@ -4099,6 +4378,10 @@ export def walk_Def(node: &parser::Node, state: &State, polymorph: bool = false) function.is_typechecked = true } + // So that returns don't confuse lambda functions with written out function definitions + let prev_lambda = state.current_lambda + state.current_lambda = null + let is_closure = state.function_stack.length > 1 // Main function is 0 var tpe: &Type @@ -4108,19 +4391,13 @@ export def walk_Def(node: &parser::Node, state: &State, polymorph: bool = false) let current_signature = errors::current_signature if is_closure { - let c_parameter_t = vector::make(NamedParameter) - let c_return_t = vector::make(type &Type) - let parameter_t = vector::make(NamedParameter) var return_t = vector::make(type &Type) - let types = allocate_ref(StructMember, 2) - types(0) = [ tpe = pointer(make_function_type_n(name, parameter_t, return_t)), name = "function" ] !StructMember - types(1) = [ tpe = reference(null), name = "data"] !StructMember - closure_type = make_struct_type(types) - closure_type.kind = TypeKind::CLOSURE - closure_type.parameter_t = c_parameter_t - closure_type.return_t = c_return_t + closure_type = make_closure_type(name, parameter_t, return_t) + let c_parameter_t = closure_type.parameter_t + let c_return_t = closure_type.return_t + // Need to store a strong reference node.value.def_.closure_type = closure_type @@ -4181,8 +4458,8 @@ export def walk_Def(node: &parser::Node, state: &State, polymorph: bool = false) } tpe = make_function_type_n(make_identifier(arr), parameter_t, return_t, state.module) - tpe.type_name += "." + state.lambda_counter - state.lambda_counter += 1 + tpe.type_name += "." + lambda_counter + lambda_counter += 1 delete(arr) tpe.node = node @@ -4299,8 +4576,6 @@ export def walk_Def(node: &parser::Node, state: &State, polymorph: bool = false) if body { state.scope = inner_scope push_function(state, function) - let lambda_counter = state.lambda_counter - state.lambda_counter = 0 if function.has_yield { let generator_ctor = [ @@ -4340,7 +4615,6 @@ export def walk_Def(node: &parser::Node, state: &State, polymorph: bool = false) } pop_function(state) - state.lambda_counter = lambda_counter state.scope = outer_scope } @@ -4363,6 +4637,7 @@ export def walk_Def(node: &parser::Node, state: &State, polymorph: bool = false) errors::current_function = current_function errors::current_signature = current_signature + state.current_lambda = prev_lambda node.tpe = tpe } @@ -4471,8 +4746,31 @@ def check_return_arguments(is_yield: bool, node: &parser::Node, ltypes: &Vector( } def walk_Return(node: &parser::Node, state: &State) { - let current_fun = current_function(state) let body = node.value.body + + if state.current_lambda { + let return_t = state.current_lambda.return_t + + let rtypes = collapse_types(node, return_t, body, state) + if state.current_lambda.return_t.length != 0 { + // Check if the types match up + if return_t.length != rtypes.length { + errors::errorn(node, "Return types for lambda disagree with previous return statement") + return + } + for var i in 0..return_t.length { + if not equals(return_t(i), rtypes(i)) { + errors::errorn(node, "Return types for lambda disagree with previous return statement") + return + } + } + } else { + state.current_lambda.return_t.add_all(rtypes) + } + return + } + + let current_fun = current_function(state) if current_fun.is_global { // TODO This is actually allowed, I just need to infer the types // and assign to some sort of internal variable @@ -4711,7 +5009,13 @@ export def walk_Call(node: &parser::Node, dry_run: bool, state: &State) -> bool for var i in 0..vector::length(node.value.func_call.args) { let n = node.value.func_call.args(i) if not n { continue } - walk(node, n, state) + + if n.kind == parser::NodeKind::LAMBDA { + walk_Lambda(n, state) + } else { + walk(node, n, state) + } + let np = [ _tpe = (@n).tpe, varargs = false, @@ -4729,7 +5033,14 @@ export def walk_Call(node: &parser::Node, dry_run: bool, state: &State) -> bool for var i in 0..vector::length(node.value.func_call.kwargs) { let n = node.value.func_call.kwargs(i) if not n { continue } - walk(node, (@n).value.named_arg.value, state) + let v = (@n).value.named_arg.value + + if v.kind == parser::NodeKind::LAMBDA { + walk_Lambda(v, state) + } else { + walk(node, v, state) + } + let name = last_ident_to_str((@n).value.named_arg.name) if map::contains(parameter_map, name) { @@ -4783,6 +5094,24 @@ export def walk_Call(node: &parser::Node, dry_run: bool, state: &State) -> bool } } + if function { + for var i in 0..arguments.length { + let param = arguments(i) + // TODO Check for varargs? + let ltpe = function.tpe.parameter_t(i).tpe if i < length(function.tpe.parameter_t) else function.tpe.parameter_t.peek().tpe + + if param.node and param.node.kind == parser::NodeKind::LAMBDA { + let value = param.node + if not value.value.lambda.function.is_typechecked { + if infer_lambda_parameter_types(value, ltpe, state) { + walk_Lambda_body(value, state) + check_lambda_return_type(value, ltpe, state) + } + } + } + } + } + if not function and not exists { for var i in 0..vector::length(arguments) { // Suppress error message if we got invalid parameters @@ -5762,6 +6091,9 @@ export def walk(parent: &parser::Node, node: &parser::Node, state: &State) { // Do nothing case parser::NodeKind::YIELD_FROM walk_YieldFrom(node, state) + case parser::NodeKind::LAMBDA + walk_Lambda(node, state) + walk_Lambda_body(node, state) case error(node.kind, "\n") assert diff --git a/std/vector.pr b/std/vector.pr index eb947cf6..35fb8d02 100644 --- a/std/vector.pr +++ b/std/vector.pr @@ -180,8 +180,9 @@ export def insert(vec: &Vector(type T), index: size_t, vec2: &Vector(T)) { export def add_all(vec: &Vector(type T), vec2: &Vector(T)) { let offset = vec.length extend(vec, vec2.length) + vec.length += vec2.length for var i in 0..vec2.length { - vec(offset + i) + vec(offset + i) = vec2(i) } } diff --git a/test/test_compiler.pr b/test/test_compiler.pr index 92fc8db5..5e6b4594 100644 --- a/test/test_compiler.pr +++ b/test/test_compiler.pr @@ -1045,4 +1045,14 @@ def #test test_variants { x = [b = 20] !T """ assert compile(str) != null +} + +def #test test_lambda { + let str = """ + def make_adder(base: int) -> int -> int -> int { + return { x -> { y -> x + y + base } } + } + make_adder(10)(20)(30) + """ + assert compile(str) != null } \ No newline at end of file diff --git a/test/test_lexer.pr b/test/test_lexer.pr index 5381cb98..319cacf4 100644 --- a/test/test_lexer.pr +++ b/test/test_lexer.pr @@ -212,7 +212,7 @@ def #test test_newline_in_parens { { "kind": "IDENTIFIER", "line": 1, "column": 0, "value": "ident" }, { "kind": "NEW_LINE", "line": 1, "column": 5 }, { "kind": "C_BRACE", "line": 2, "column": 0 }, - { "kind": "EOF", "line": 2, "column": 0 } + { "kind": "EOF", "line": 2, "column": 1 } ]""") } @@ -232,20 +232,69 @@ def #test test_triple_quoted { ]""") } +def #test test_lambda { + assert lex(""" + function({ x: int -> + print(x) + return x * 2 + }) + """) == json::parse("""[ + { "kind": "NEW_LINE", "line": 0, "column": 0 }, + { "kind": "WHITESPACE", "line": 1, "column": 0 }, + { "kind": "IDENTIFIER", "line": 1, "column": 8, "value": "function" }, + { "kind": "O_PAREN", "line": 1, "column": 16 }, + { "kind": "O_BRACE", "line": 1, "column": 17 }, + { "kind": "WHITESPACE", "line": 1, "column": 18 }, + { "kind": "IDENTIFIER", "line": 1, "column": 19, "value": "x" }, + { "kind": "COLON", "line": 1, "column": 20 }, + { "kind": "WHITESPACE", "line": 1, "column": 21 }, + { "kind": "IDENTIFIER", "line": 1, "column": 22, "value": "int" }, + { "kind": "WHITESPACE", "line": 1, "column": 25 }, + { "kind": "ARROW", "line": 1, "column": 26 }, + { "kind": "WHITESPACE", "line": 1, "column": 28 }, + { "kind": "NEW_LINE", "line": 1, "column": 29 }, + { "kind": "WHITESPACE", "line": 2, "column": 0 }, + { "kind": "IDENTIFIER", "line": 2, "column": 12, "value": "print" }, + { "kind": "O_PAREN", "line": 2, "column": 17 }, + { "kind": "IDENTIFIER", "line": 2, "column": 18, "value": "x" }, + { "kind": "C_PAREN", "line": 2, "column": 19 }, + { "kind": "WHITESPACE", "line": 2, "column": 20 }, + { "kind": "NEW_LINE", "line": 2, "column": 21 }, + { "kind": "WHITESPACE", "line": 3, "column": 0 }, + { "kind": "K_RETURN", "line": 3, "column": 12 }, + { "kind": "WHITESPACE", "line": 3, "column": 18 }, + { "kind": "IDENTIFIER", "line": 3, "column": 19, "value": "x" }, + { "kind": "WHITESPACE", "line": 3, "column": 20 }, + { "kind": "OP_MUL", "line": 3, "column": 21 }, + { "kind": "WHITESPACE", "line": 3, "column": 22 }, + { "kind": "INTEGER", "line": 3, "column": 23, "value": 2 }, + { "kind": "NEW_LINE", "line": 3, "column": 24 }, + { "kind": "WHITESPACE", "line": 4, "column": 0 }, + { "kind": "C_BRACE", "line": 4, "column": 8 }, + { "kind": "C_PAREN", "line": 4, "column": 9 }, + { "kind": "NEW_LINE", "line": 4, "column": 10 }, + { "kind": "WHITESPACE", "line": 5, "column": 0 }, + { "kind": "EOF", "line": 5, "column": 3} + ]""") +} + def #test test_complex { var str = """ type T = struct { a: int - b: string } + b: string + } def main(a: int, b: unsigned word) -> T { print("A: ", a, " B: ", b) var t: T = [ - a = 0, b = "Test string" ] !T } - """ + a = 0, b = "Test string" + ] !T + } + """ assert lex(str) == json::parse("""[\ - { "kind": "NEW_LINE", "line": 0, "column": 0 }, + { "kind": "NEW_LINE", "line": 0, "column": 0 }, { "kind": "WHITESPACE", "line": 1, "column": 0 }, { "kind": "K_TYPE", "line": 1, "column": 8 }, { "kind": "WHITESPACE", "line": 1, "column": 12 }, @@ -268,84 +317,87 @@ def #test test_complex { { "kind": "COLON", "line": 3, "column": 13 }, { "kind": "WHITESPACE", "line": 3, "column": 14 }, { "kind": "IDENTIFIER", "line": 3, "column": 15, "value": "string" }, - { "kind": "WHITESPACE", "line": 3, "column": 21 }, - { "kind": "C_BRACE", "line": 3, "column": 22 }, - { "kind": "NEW_LINE", "line": 3, "column": 23 }, - { "kind": "NEW_LINE", "line": 4, "column": 0 }, - { "kind": "WHITESPACE", "line": 5, "column": 0 }, - { "kind": "K_DEF", "line": 5, "column": 8 }, - { "kind": "WHITESPACE", "line": 5, "column": 11 }, - { "kind": "IDENTIFIER", "line": 5, "column": 12, "value": "main" }, - { "kind": "O_PAREN", "line": 5, "column": 16 }, - { "kind": "IDENTIFIER", "line": 5, "column": 17, "value": "a" }, - { "kind": "COLON", "line": 5, "column": 18 }, - { "kind": "WHITESPACE", "line": 5, "column": 19 }, - { "kind": "IDENTIFIER", "line": 5, "column": 20, "value": "int" }, - { "kind": "COMMA", "line": 5, "column": 23 }, - { "kind": "WHITESPACE", "line": 5, "column": 24 }, - { "kind": "IDENTIFIER", "line": 5, "column": 25, "value": "b" }, - { "kind": "COLON", "line": 5, "column": 26 }, - { "kind": "WHITESPACE", "line": 5, "column": 27 }, - { "kind": "K_UNSIGNED", "line": 5, "column": 28 }, - { "kind": "WHITESPACE", "line": 5, "column": 36 }, - { "kind": "K_WORD", "line": 5, "column": 37 }, - { "kind": "C_PAREN", "line": 5, "column": 41 }, - { "kind": "WHITESPACE", "line": 5, "column": 42 }, - { "kind": "ARROW", "line": 5, "column": 43 }, - { "kind": "WHITESPACE", "line": 5, "column": 45 }, - { "kind": "IDENTIFIER", "line": 5, "column": 46, "value": "T" }, - { "kind": "WHITESPACE", "line": 5, "column": 47 }, - { "kind": "O_BRACE", "line": 5, "column": 48 }, - { "kind": "NEW_LINE", "line": 5, "column": 49 }, + { "kind": "NEW_LINE", "line": 3, "column": 21 }, + { "kind": "WHITESPACE", "line": 4, "column": 0 }, + { "kind": "C_BRACE", "line": 4, "column": 8 }, + { "kind": "NEW_LINE", "line": 4, "column": 9 }, + { "kind": "NEW_LINE", "line": 5, "column": 0 }, { "kind": "WHITESPACE", "line": 6, "column": 0 }, - { "kind": "IDENTIFIER", "line": 6, "column": 12, "value": "print" }, - { "kind": "O_PAREN", "line": 6, "column": 17 }, - { "kind": "STRING", "line": 6, "column": 18, "value": "A: " }, + { "kind": "K_DEF", "line": 6, "column": 8 }, + { "kind": "WHITESPACE", "line": 6, "column": 11 }, + { "kind": "IDENTIFIER", "line": 6, "column": 12, "value": "main" }, + { "kind": "O_PAREN", "line": 6, "column": 16 }, + { "kind": "IDENTIFIER", "line": 6, "column": 17, "value": "a" }, + { "kind": "COLON", "line": 6, "column": 18 }, + { "kind": "WHITESPACE", "line": 6, "column": 19 }, + { "kind": "IDENTIFIER", "line": 6, "column": 20, "value": "int" }, { "kind": "COMMA", "line": 6, "column": 23 }, { "kind": "WHITESPACE", "line": 6, "column": 24 }, - { "kind": "IDENTIFIER", "line": 6, "column": 25, "value": "a" }, - { "kind": "COMMA", "line": 6, "column": 26 }, + { "kind": "IDENTIFIER", "line": 6, "column": 25, "value": "b" }, + { "kind": "COLON", "line": 6, "column": 26 }, { "kind": "WHITESPACE", "line": 6, "column": 27 }, - { "kind": "STRING", "line": 6, "column": 28, "value": " B: " }, - { "kind": "COMMA", "line": 6, "column": 34 }, - { "kind": "WHITESPACE", "line": 6, "column": 35 }, - { "kind": "IDENTIFIER", "line": 6, "column": 36, "value": "b" }, - { "kind": "C_PAREN", "line": 6, "column": 37 }, - { "kind": "NEW_LINE", "line": 6, "column": 38 }, + { "kind": "K_UNSIGNED", "line": 6, "column": 28 }, + { "kind": "WHITESPACE", "line": 6, "column": 36 }, + { "kind": "K_WORD", "line": 6, "column": 37 }, + { "kind": "C_PAREN", "line": 6, "column": 41 }, + { "kind": "WHITESPACE", "line": 6, "column": 42 }, + { "kind": "ARROW", "line": 6, "column": 43 }, + { "kind": "WHITESPACE", "line": 6, "column": 45 }, + { "kind": "IDENTIFIER", "line": 6, "column": 46, "value": "T" }, + { "kind": "WHITESPACE", "line": 6, "column": 47 }, + { "kind": "O_BRACE", "line": 6, "column": 48 }, + { "kind": "NEW_LINE", "line": 6, "column": 49 }, { "kind": "WHITESPACE", "line": 7, "column": 0 }, - { "kind": "K_VAR", "line": 7, "column": 12 }, - { "kind": "WHITESPACE", "line": 7, "column": 15 }, - { "kind": "IDENTIFIER", "line": 7, "column": 16, "value": "t" }, - { "kind": "COLON", "line": 7, "column": 17 }, - { "kind": "WHITESPACE", "line": 7, "column": 18 }, - { "kind": "IDENTIFIER", "line": 7, "column": 19, "value": "T" }, - { "kind": "WHITESPACE", "line": 7, "column": 20 }, - { "kind": "OP_ASSIGN", "line": 7, "column": 21 }, - { "kind": "WHITESPACE", "line": 7, "column": 22 }, - { "kind": "O_SQUARE", "line": 7, "column": 23 }, - { "kind": "NEW_LINE", "line": 7, "column": 24 }, + { "kind": "IDENTIFIER", "line": 7, "column": 12, "value": "print" }, + { "kind": "O_PAREN", "line": 7, "column": 17 }, + { "kind": "STRING", "line": 7, "column": 18, "value": "A: " }, + { "kind": "COMMA", "line": 7, "column": 23 }, + { "kind": "WHITESPACE", "line": 7, "column": 24 }, + { "kind": "IDENTIFIER", "line": 7, "column": 25, "value": "a" }, + { "kind": "COMMA", "line": 7, "column": 26 }, + { "kind": "WHITESPACE", "line": 7, "column": 27 }, + { "kind": "STRING", "line": 7, "column": 28, "value": " B: " }, + { "kind": "COMMA", "line": 7, "column": 34 }, + { "kind": "WHITESPACE", "line": 7, "column": 35 }, + { "kind": "IDENTIFIER", "line": 7, "column": 36, "value": "b" }, + { "kind": "C_PAREN", "line": 7, "column": 37 }, + { "kind": "NEW_LINE", "line": 7, "column": 38 }, { "kind": "WHITESPACE", "line": 8, "column": 0 }, - { "kind": "IDENTIFIER", "line": 8, "column": 16, "value": "a" }, - { "kind": "WHITESPACE", "line": 8, "column": 17 }, - { "kind": "OP_ASSIGN", "line": 8, "column": 18 }, - { "kind": "WHITESPACE", "line": 8, "column": 19 }, - { "kind": "INTEGER", "line": 8, "column": 20, "value": 0 }, - { "kind": "COMMA", "line": 8, "column": 21 }, + { "kind": "K_VAR", "line": 8, "column": 12 }, + { "kind": "WHITESPACE", "line": 8, "column": 15 }, + { "kind": "IDENTIFIER", "line": 8, "column": 16, "value": "t" }, + { "kind": "COLON", "line": 8, "column": 17 }, + { "kind": "WHITESPACE", "line": 8, "column": 18 }, + { "kind": "IDENTIFIER", "line": 8, "column": 19, "value": "T" }, + { "kind": "WHITESPACE", "line": 8, "column": 20 }, + { "kind": "OP_ASSIGN", "line": 8, "column": 21 }, { "kind": "WHITESPACE", "line": 8, "column": 22 }, - { "kind": "IDENTIFIER", "line": 8, "column": 23, "value": "b" }, - { "kind": "WHITESPACE", "line": 8, "column": 24 }, - { "kind": "OP_ASSIGN", "line": 8, "column": 25 }, - { "kind": "WHITESPACE", "line": 8, "column": 26 }, - { "kind": "STRING", "line": 8, "column": 27, "value": "Test string" }, - { "kind": "WHITESPACE", "line": 8, "column": 40 }, - { "kind": "C_SQUARE", "line": 8, "column": 41 }, - { "kind": "WHITESPACE", "line": 8, "column": 42 }, - { "kind": "OP_CAST", "line": 8, "column": 43 }, - { "kind": "IDENTIFIER", "line": 8, "column": 44, "value": "T" }, - { "kind": "WHITESPACE", "line": 8, "column": 45 }, - { "kind": "C_BRACE", "line": 8, "column": 46 }, - { "kind": "NEW_LINE", "line": 8, "column": 47 }, + { "kind": "O_SQUARE", "line": 8, "column": 23 }, + { "kind": "NEW_LINE", "line": 8, "column": 24 }, { "kind": "WHITESPACE", "line": 9, "column": 0 }, - { "kind": "EOF", "line": 9, "column": 3 } + { "kind": "IDENTIFIER", "line": 9, "column": 16, "value": "a" }, + { "kind": "WHITESPACE", "line": 9, "column": 17 }, + { "kind": "OP_ASSIGN", "line": 9, "column": 18 }, + { "kind": "WHITESPACE", "line": 9, "column": 19 }, + { "kind": "INTEGER", "line": 9, "column": 20, "value": 0 }, + { "kind": "COMMA", "line": 9, "column": 21 }, + { "kind": "WHITESPACE", "line": 9, "column": 22 }, + { "kind": "IDENTIFIER", "line": 9, "column": 23, "value": "b" }, + { "kind": "WHITESPACE", "line": 9, "column": 24 }, + { "kind": "OP_ASSIGN", "line": 9, "column": 25 }, + { "kind": "WHITESPACE", "line": 9, "column": 26 }, + { "kind": "STRING", "line": 9, "column": 27, "value": "Test string" }, + { "kind": "NEW_LINE", "line": 9, "column": 40 }, + { "kind": "WHITESPACE", "line": 10, "column": 0 }, + { "kind": "C_SQUARE", "line": 10, "column": 12 }, + { "kind": "WHITESPACE", "line": 10, "column": 13 }, + { "kind": "OP_CAST", "line": 10, "column": 14 }, + { "kind": "IDENTIFIER", "line": 10, "column": 15, "value": "T" }, + { "kind": "NEW_LINE", "line": 10, "column": 16 }, + { "kind": "WHITESPACE", "line": 11, "column": 0 }, + { "kind": "C_BRACE", "line": 11, "column": 8 }, + { "kind": "NEW_LINE", "line": 11, "column": 9 }, + { "kind": "WHITESPACE", "line": 12, "column": 0 }, + { "kind": "EOF", "line": 12, "column": 4 } ]""") } \ No newline at end of file diff --git a/test/test_parser.pr b/test/test_parser.pr index 38cfb77e..8d24e093 100644 --- a/test/test_parser.pr +++ b/test/test_parser.pr @@ -4950,4 +4950,103 @@ def #test test_bug_2 { } ] }""") +} + +def #test test_lambda { + assert parse(""" + function({ x: int -> + print(x) + return x * 2 + }) + """) == json::parse("""{ + "kind": "Program", + "body": [ + { + "kind": "FuncCall", + "left": { + "kind": "Identifier", + "path": [ + "function" + ], + "prefixed": false, + "args": null + }, + "args": [ + { + "kind": "Lambda", + "parameters": [ + { + "kind": "Parameter", + "kw": "VAR", + "name": { + "kind": "Identifier", + "path": [ + "x" + ], + "prefixed": false, + "args": null + }, + "tpe": { + "kind": "Identifier", + "path": [ + "int" + ], + "prefixed": false, + "args": null + }, + "value": null + } + ], + "body": [ + { + "kind": "FuncCall", + "left": { + "kind": "Identifier", + "path": [ + "print" + ], + "prefixed": false, + "args": null + }, + "args": [ + { + "kind": "Identifier", + "path": [ + "x" + ], + "prefixed": false, + "args": null + } + ], + "kwargs": [ + ] + }, + { + "kind": "Return", + "body": [ + { + "kind": "Mul", + "left": { + "kind": "Identifier", + "path": [ + "x" + ], + "prefixed": false, + "args": null + }, + "right": { + "kind": "Integer", + "value": 2.000000 + } + } + ] + } + ] + } + ], + "kwargs": [ + ] + } + ] + }""") } \ No newline at end of file diff --git a/test/test_runtime.pr b/test/test_runtime.pr index ee89950e..29206e7f 100644 --- a/test/test_runtime.pr +++ b/test/test_runtime.pr @@ -858,5 +858,16 @@ def #test test_interface { takes_interface(s) """ + assert run_source(str) == 0 +} + +def #test test_lambda { + var str = """ + def make_adder(base: int) -> int -> int -> int { + return { x -> { y -> x + y + base } } + } + assert make_adder(10)(20)(30) == 60 + """ + assert run_source(str) == 0 } \ No newline at end of file