diff options
| author | DanielGavin <danielgavin5@hotmail.com> | 2024-02-03 13:08:38 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-02-03 13:08:38 +0100 |
| commit | 1e87c5bc9732fcda326674283be75272a95f8dd3 (patch) | |
| tree | 7214ad5d341c9581257d2ae1abb70c8f932be27e | |
| parent | d7531bd1ae6caf7fcd7395195865c553aa2c4ce4 (diff) | |
| parent | b7145b01decc292702f6e39e4a5e69a3d3401615 (diff) | |
Merge pull request #289 from DanielGavin/poly
New poly resolve system
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | editors/vscode/package.json | 2 | ||||
| -rw-r--r-- | src/common/ast.odin | 35 | ||||
| -rw-r--r-- | src/server/analysis.odin | 629 | ||||
| -rw-r--r-- | src/server/check.odin | 2 | ||||
| -rw-r--r-- | src/server/clone.odin | 292 | ||||
| -rw-r--r-- | src/server/collector.odin | 8 | ||||
| -rw-r--r-- | src/server/generics.odin | 736 | ||||
| -rw-r--r-- | src/server/methods.odin | 11 | ||||
| -rw-r--r-- | src/server/requests.odin | 17 | ||||
| -rw-r--r-- | src/server/symbol.odin | 70 | ||||
| -rw-r--r-- | tests/completions_test.odin | 255 |
12 files changed, 1301 insertions, 757 deletions
@@ -6,7 +6,6 @@ *.ll *.sublime-project *.sublime-workspace -*.vscode *.lib *.exp *.sqlite diff --git a/editors/vscode/package.json b/editors/vscode/package.json index b8ffe8a..b50d575 100644 --- a/editors/vscode/package.json +++ b/editors/vscode/package.json @@ -7,7 +7,7 @@ "type": "git", "url": "git://github.com/DanielGavin/ols.git" }, - "version": "0.1.16", + "version": "0.1.23", "engines": { "vscode": "^1.66.0" }, diff --git a/src/common/ast.odin b/src/common/ast.odin index 9adc57c..a2e5d42 100644 --- a/src/common/ast.odin +++ b/src/common/ast.odin @@ -163,7 +163,7 @@ get_attribute_objc_is_class_method :: proc( return false } -unwrap_pointer :: proc(expr: ^ast.Expr) -> (ast.Ident, int, bool) { +unwrap_pointer_ident :: proc(expr: ^ast.Expr) -> (ast.Ident, int, bool) { n := 0 expr := expr for expr != nil { @@ -186,6 +186,25 @@ unwrap_pointer :: proc(expr: ^ast.Expr) -> (ast.Ident, int, bool) { return {}, n, false } +unwrap_pointer_expr :: proc(expr: ^ast.Expr) -> (^ast.Expr, int, bool) { + n := 0 + expr := expr + for expr != nil { + if pointer, ok := expr.derived.(^ast.Pointer_Type); ok { + expr = pointer.elem + n += 1 + } else { + break + } + } + + if expr == nil { + return {}, n, false + } + + return expr, n, true +} + is_expr_basic_lit :: proc(expr: ^ast.Expr) -> bool { _, ok := expr.derived.(^ast.Basic_Lit) return ok @@ -198,10 +217,10 @@ collect_value_decl :: proc( skip_private: bool, ) { if value_decl, ok := stmt.derived.(^ast.Value_Decl); ok { - is_deprecated := false + is_deprecated := false is_private_file := false - is_private_pkg := false - is_builtin := false + is_private_pkg := false + is_builtin := false for attribute in value_decl.attributes { for elem in attribute.elems { @@ -238,7 +257,7 @@ collect_value_decl :: proc( if is_private_file && skip_private { return } - + // If a private status is not explicitly set with an attribute above the declaration // check the file comment. if !is_private_file && !is_private_pkg && file.docs != nil { @@ -247,7 +266,7 @@ collect_value_decl :: proc( if strings.has_prefix(txt, "//+private") { txt = strings.trim_prefix(txt, "//+private") is_private_pkg = true - + if strings.has_prefix(txt, " ") { txt = strings.trim_space(txt) if txt == "file" { @@ -264,7 +283,7 @@ collect_value_decl :: proc( if value_decl.type != nil { append( exprs, - GlobalExpr{ + GlobalExpr { name = str, name_expr = name, expr = value_decl.type, @@ -280,7 +299,7 @@ collect_value_decl :: proc( if len(value_decl.values) > i { append( exprs, - GlobalExpr{ + GlobalExpr { name = str, name_expr = name, expr = value_decl.values[i], diff --git a/src/server/analysis.odin b/src/server/analysis.odin index 07c079c..c855b22 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -87,7 +87,7 @@ AstContext :: struct { document_package: string, use_locals: bool, local_id: int, - call: ^ast.Call_Expr, //used to determene the types for generics and the correct function for overloaded functions + call: ^ast.Call_Expr, //used to determine the types for generics and the correct function for overloaded functions value_decl: ^ast.Value_Decl, field_name: ast.Ident, uri: string, @@ -135,217 +135,6 @@ reset_ast_context :: proc(ast_context: ^AstContext) { tokenizer_error_handler :: proc(pos: tokenizer.Pos, msg: string, args: ..any) { } -/* - Walk through the type expression while both the call expression and specialization type are the same -*/ - -resolve_poly_spec :: proc { - resolve_poly_spec_node, - resolve_poly_spec_array, - resolve_poly_spec_dynamic_array, -} - -resolve_poly_spec_array :: proc( - ast_context: ^AstContext, - call_array: $A/[]^$T, - spec_array: $D/[]^$K, - poly_map: ^map[string]^ast.Expr, -) { - if len(call_array) != len(spec_array) { - return - } - - for elem, i in call_array { - resolve_poly_spec(ast_context, elem, spec_array[i], poly_map) - } -} - -resolve_poly_spec_dynamic_array :: proc( - ast_context: ^AstContext, - call_array: $A/[dynamic]^$T, - spec_array: $D/[dynamic]^$K, - poly_map: ^map[string]^ast.Expr, -) { - if len(call_array) != len(spec_array) { - return - } - - for elem, i in call_array { - resolve_poly_spec(ast_context, elem, spec_array[i], poly_map) - } -} - -get_poly_node_to_expr :: proc(node: ^ast.Node) -> ^ast.Expr { - using ast - - #partial switch v in node.derived { - case ^Ident: - return cast(^Expr)node - case ^Comp_Lit: - return v.type - case: - log.warnf("Unhandled poly to node kind %v", v) - } - - return nil -} - -resolve_poly_spec_node :: proc( - ast_context: ^AstContext, - call_node: ^ast.Node, - spec_node: ^ast.Node, - poly_map: ^map[string]^ast.Expr, -) { - using ast - - if call_node == nil || spec_node == nil { - return - } - - #partial switch m in spec_node.derived { - case ^Bad_Expr: - case ^Ident: - case ^Implicit: - case ^Undef: - case ^Basic_Lit: - case ^Poly_Type: - if expr := get_poly_node_to_expr(call_node); expr != nil { - poly_map[m.type.name] = expr - } - case ^Ellipsis: - if n, ok := call_node.derived.(^Ellipsis); ok { - resolve_poly_spec(ast_context, n.expr, m.expr, poly_map) - } - case ^Tag_Expr: - if n, ok := call_node.derived.(^Tag_Expr); ok { - resolve_poly_spec(ast_context, n.expr, m.expr, poly_map) - } - case ^Unary_Expr: - if n, ok := call_node.derived.(^Unary_Expr); ok { - resolve_poly_spec(ast_context, n.expr, m.expr, poly_map) - } - case ^Binary_Expr: - if n, ok := call_node.derived.(^Binary_Expr); ok { - resolve_poly_spec(ast_context, n.left, m.left, poly_map) - resolve_poly_spec(ast_context, n.right, m.right, poly_map) - } - case ^Paren_Expr: - if n, ok := call_node.derived.(^Paren_Expr); ok { - resolve_poly_spec(ast_context, n.expr, m.expr, poly_map) - } - case ^Selector_Expr: - if n, ok := call_node.derived.(^Selector_Expr); ok { - resolve_poly_spec(ast_context, n.expr, m.expr, poly_map) - resolve_poly_spec(ast_context, n.field, m.field, poly_map) - } - case ^Slice_Expr: - if n, ok := call_node.derived.(^Slice_Expr); ok { - resolve_poly_spec(ast_context, n.expr, m.expr, poly_map) - resolve_poly_spec(ast_context, n.low, m.low, poly_map) - resolve_poly_spec(ast_context, n.high, m.high, poly_map) - } - case ^Distinct_Type: - if n, ok := call_node.derived.(^Distinct_Type); ok { - resolve_poly_spec(ast_context, n.type, m.type, poly_map) - } - case ^Proc_Type: - if n, ok := call_node.derived.(^Proc_Type); ok { - resolve_poly_spec(ast_context, n.params, m.params, poly_map) - resolve_poly_spec(ast_context, n.results, m.results, poly_map) - } - case ^Pointer_Type: - if n, ok := call_node.derived.(^Pointer_Type); ok { - resolve_poly_spec(ast_context, n.elem, m.elem, poly_map) - } - case ^Array_Type: - if n, ok := call_node.derived.(^Array_Type); ok { - resolve_poly_spec(ast_context, n.len, m.len, poly_map) - resolve_poly_spec(ast_context, n.elem, m.elem, poly_map) - } - case ^Dynamic_Array_Type: - if n, ok := call_node.derived.(^Dynamic_Array_Type); ok { - resolve_poly_spec(ast_context, n.elem, m.elem, poly_map) - } - case ^Struct_Type: - if n, ok := call_node.derived.(^Struct_Type); ok { - resolve_poly_spec( - ast_context, - n.poly_params, - m.poly_params, - poly_map, - ) - resolve_poly_spec(ast_context, n.align, m.align, poly_map) - resolve_poly_spec(ast_context, n.fields, m.fields, poly_map) - } - case ^Field: - if n, ok := call_node.derived.(^Field); ok { - resolve_poly_spec(ast_context, n.names, m.names, poly_map) - resolve_poly_spec(ast_context, n.type, m.type, poly_map) - resolve_poly_spec( - ast_context, - n.default_value, - m.default_value, - poly_map, - ) - } - case ^Field_List: - if n, ok := call_node.derived.(^Field_List); ok { - resolve_poly_spec(ast_context, n.list, m.list, poly_map) - } - case ^Field_Value: - if n, ok := call_node.derived.(^Field_Value); ok { - resolve_poly_spec(ast_context, n.field, m.field, poly_map) - resolve_poly_spec(ast_context, n.value, m.value, poly_map) - } - case ^Union_Type: - if n, ok := call_node.derived.(^Union_Type); ok { - resolve_poly_spec( - ast_context, - n.poly_params, - m.poly_params, - poly_map, - ) - resolve_poly_spec(ast_context, n.align, m.align, poly_map) - resolve_poly_spec(ast_context, n.variants, m.variants, poly_map) - } - case ^Enum_Type: - if n, ok := call_node.derived.(^Enum_Type); ok { - resolve_poly_spec(ast_context, n.base_type, m.base_type, poly_map) - resolve_poly_spec(ast_context, n.fields, m.fields, poly_map) - } - case ^Bit_Set_Type: - if n, ok := call_node.derived.(^Bit_Set_Type); ok { - resolve_poly_spec(ast_context, n.elem, m.elem, poly_map) - resolve_poly_spec( - ast_context, - n.underlying, - m.underlying, - poly_map, - ) - } - case ^Map_Type: - if n, ok := call_node.derived.(^Map_Type); ok { - resolve_poly_spec(ast_context, n.key, m.key, poly_map) - resolve_poly_spec(ast_context, n.value, m.value, poly_map) - } - case ^Call_Expr: - if n, ok := call_node.derived.(^Call_Expr); ok { - resolve_poly_spec(ast_context, n.expr, m.expr, poly_map) - resolve_poly_spec(ast_context, n.args, m.args, poly_map) - } - case ^Typeid_Type: - if n, ok := call_node.derived.(^Typeid_Type); ok { - resolve_poly_spec( - ast_context, - n.specialization, - m.specialization, - poly_map, - ) - } - case: - log.error("Unhandled poly node kind: %T", m) - } -} resolve_type_comp_literal :: proc( ast_context: ^AstContext, @@ -463,185 +252,6 @@ resolve_type_comp_literal :: proc( return current_symbol, current_comp_lit, true } -resolve_generic_function :: proc { - resolve_generic_function_ast, - resolve_generic_function_symbol, -} - -resolve_generic_function_symbol :: proc( - ast_context: ^AstContext, - params: []^ast.Field, - results: []^ast.Field, -) -> ( - Symbol, - bool, -) { - if params == nil { - return {}, false - } - - if results == nil { - return {}, false - } - - if ast_context.call == nil { - return {}, false - } - - call_expr := ast_context.call - poly_map := make(map[string]^ast.Expr, 0, context.temp_allocator) - i := 0 - count_required_params := 0 - - for param in params { - if param.default_value == nil { - count_required_params += 1 - } - - for name in param.names { - if len(call_expr.args) <= i { - break - } - - if poly, ok := name.derived.(^ast.Poly_Type); ok { - poly_map[poly.type.name] = call_expr.args[i] - } - - if param.type == nil { - continue - } - - if type_id, ok := param.type.derived.(^ast.Typeid_Type); ok { - if type_id.specialization != nil && - !common.node_equal( - call_expr.args[i], - type_id.specialization, - ) { - return {}, false - } - } - - resolve_poly_spec_node( - ast_context, - call_expr.args[i], - param.type, - &poly_map, - ) - - i += 1 - } - } - - if count_required_params > len(call_expr.args) || - count_required_params == 0 || - len(call_expr.args) == 0 { - return {}, false - } - - function_name := "" - function_range: common.Range - - if ident, ok := call_expr.expr.derived.(^ast.Ident); ok { - function_name = ident.name - function_range = common.get_token_range(ident, ast_context.file.src) - } else if selector, ok := call_expr.expr.derived.(^ast.Selector_Expr); ok { - function_name = selector.field.name - function_range = common.get_token_range(selector, ast_context.file.src) - } else { - return {}, false - } - - symbol := Symbol { - range = function_range, - type = .Function, - name = function_name, - } - - return_types := make([dynamic]^ast.Field, ast_context.allocator) - argument_types := make([dynamic]^ast.Field, ast_context.allocator) - - for result in results { - if result.type == nil { - continue - } - - ident, wrapped, ok := common.unwrap_pointer(result.type) - - if ok { - if m, ok := poly_map[ident.name]; ok { - field := cast(^ast.Field)clone_node( - result, - ast_context.allocator, - nil, - ) - field.type = wrap_pointer(m, wrapped) - append(&return_types, field) - } else { - append(&return_types, result) - } - } else { - append(&return_types, result) - } - } - - for param in params { - if len(param.names) == 0 { - continue - } - - //check the name for poly - if poly_type, ok := param.names[0].derived.(^ast.Poly_Type); - ok && param.type != nil { - if m, ok := poly_map[poly_type.type.name]; ok { - field := cast(^ast.Field)clone_node( - param, - ast_context.allocator, - nil, - ) - field.type = m - append(&argument_types, field) - } - } else { - append(&argument_types, param) - } - } - - symbol.value = SymbolProcedureValue { - return_types = return_types[:], - arg_types = argument_types[:], - } - - return symbol, true -} - -resolve_generic_function_ast :: proc( - ast_context: ^AstContext, - proc_lit: ast.Proc_Lit, -) -> ( - Symbol, - bool, -) { - - using ast - - if proc_lit.type.params == nil { - return Symbol{}, false - } - - if proc_lit.type.results == nil { - return Symbol{}, false - } - - if ast_context.call == nil { - return Symbol{}, false - } - - return resolve_generic_function_symbol( - ast_context, - proc_lit.type.params.list, - proc_lit.type.results.list, - ) -} is_symbol_same_typed :: proc( ast_context: ^AstContext, @@ -1024,6 +634,8 @@ resolve_basic_lit :: proc( value: SymbolUntypedValue + value.tok = basic_lit.tok + if len(basic_lit.tok.text) == 0 { return {}, false } @@ -1327,8 +939,13 @@ internal_resolve_type_expression :: proc( return symbol, ok case ^Call_Expr: + old_call := ast_context.call ast_context.call = cast(^Call_Expr)node + defer { + ast_context.call = old_call + } + if ident, ok := v.expr.derived.(^ast.Ident); ok && len(v.args) >= 1 { switch ident.name { case "type_of": @@ -1636,8 +1253,8 @@ internal_resolve_type_identifier :: proc( for imp in ast_context.imports { if strings.compare(imp.base, node.name) == 0 { symbol := Symbol { - type = .Package, - pkg = imp.name, + type = .Package, + pkg = imp.name, value = SymbolPackageValue{}, } @@ -1688,7 +1305,7 @@ internal_resolve_type_identifier :: proc( make_symbol_bitset_from_ast(ast_context, v^, node), true return_symbol.name = node.name case ^Proc_Lit: - if !v.type.generic { + if !is_procedure_generic(v.type) { return_symbol, ok = make_symbol_procedure_from_ast( ast_context, @@ -1776,6 +1393,13 @@ internal_resolve_type_identifier :: proc( v^, ) case ^ast.Call_Expr: + old_call := ast_context.call + ast_context.call = cast(^Call_Expr)global.expr + + defer { + ast_context.call = old_call + } + call_symbol := internal_resolve_type_expression( ast_context, v.expr, @@ -1813,7 +1437,7 @@ internal_resolve_type_identifier :: proc( make_symbol_enum_from_ast(ast_context, v^, node), true return_symbol.name = node.name case ^Proc_Lit: - if !v.type.generic { + if !is_procedure_generic(v.type) { return_symbol, ok = make_symbol_procedure_from_ast( ast_context, @@ -1893,8 +1517,8 @@ internal_resolve_type_identifier :: proc( //right now we replace the package ident with the absolute directory name, so it should have '/' which is not a valid ident character if strings.contains(node.name, "/") { symbol := Symbol { - type = .Package, - pkg = node.name, + type = .Package, + pkg = node.name, value = SymbolPackageValue{}, } @@ -1903,16 +1527,33 @@ internal_resolve_type_identifier :: proc( return symbol, true } + is_runtime := strings.contains( + ast_context.current_package, + "base/runtime", + ) + + if is_runtime { + if symbol, ok := lookup(node.name, "$builtin"); ok { + return resolve_symbol_return(ast_context, symbol) + } + } + //last option is to check the index if symbol, ok := lookup(node.name, ast_context.current_package); ok { return resolve_symbol_return(ast_context, symbol) } + if !is_runtime { + if symbol, ok := lookup(node.name, "$builtin"); ok { + return resolve_symbol_return(ast_context, symbol) + } + } + for imp in ast_context.imports { if strings.compare(imp.base, node.name) == 0 { symbol := Symbol { - type = .Package, - pkg = imp.name, + type = .Package, + pkg = imp.name, value = SymbolPackageValue{}, } @@ -1922,9 +1563,6 @@ internal_resolve_type_identifier :: proc( } } - if symbol, ok := lookup(node.name, "$builtin"); ok { - return resolve_symbol_return(ast_context, symbol) - } for built in indexer.builtin_packages { if symbol, ok := lookup(node.name, built); ok { return resolve_symbol_return(ast_context, symbol) @@ -2710,6 +2348,17 @@ make_int_ast :: proc( return ident } +make_ident_ast :: proc( + ast_context: ^AstContext, + pos: tokenizer.Pos, + end: tokenizer.Pos, + name: string, +) -> ^ast.Ident { + ident := new_type(ast.Ident, pos, end, ast_context.allocator) + ident.name = name + return ident +} + make_int_basic_value :: proc( ast_context: ^AstContext, n: int, @@ -3123,6 +2772,7 @@ make_symbol_struct_from_ast :: proc( types = types[:], ranges = ranges[:], usings = usings, + poly = v.poly_params, } if _, ok := common.get_attribute_objc_class_name(attributes); ok { @@ -3148,166 +2798,6 @@ make_symbol_struct_from_ast :: proc( return symbol } -resolve_poly_union :: proc( - ast_context: ^AstContext, - poly_params: ^ast.Field_List, - symbol: ^Symbol, -) { - if ast_context.call == nil { - return - } - - symbol_value := &symbol.value.(SymbolUnionValue) - - if symbol_value == nil { - return - } - - i := 0 - - poly_map := make(map[string]^ast.Expr, 0, context.temp_allocator) - - for param in poly_params.list { - for name in param.names { - if len(ast_context.call.args) <= i { - break - } - - if param.type == nil { - continue - } - - if poly, ok := param.type.derived.(^ast.Typeid_Type); ok { - if ident, ok := name.derived.(^ast.Ident); ok { - poly_map[ident.name] = ast_context.call.args[i] - } else if poly, ok := name.derived.(^ast.Poly_Type); ok { - if poly.type != nil { - poly_map[poly.type.name] = ast_context.call.args[i] - } - } - } - - i += 1 - } - } - - for type, i in symbol_value.types { - if ident, ok := type.derived.(^ast.Ident); ok { - if expr, ok := poly_map[ident.name]; ok { - symbol_value.types[i] = expr - } - } else if call_expr, ok := type.derived.(^ast.Call_Expr); ok { - if call_expr.args == nil { - continue - } - - for arg, i in call_expr.args { - if ident, ok := arg.derived.(^ast.Ident); ok { - if expr, ok := poly_map[ident.name]; ok { - symbol_value.types[i] = expr - } - } - } - } - } -} - -resolve_poly_struct :: proc( - ast_context: ^AstContext, - poly_params: ^ast.Field_List, - symbol: ^Symbol, -) { - if ast_context.call == nil { - return - } - - symbol_value := &symbol.value.(SymbolStructValue) - - if symbol_value == nil { - return - } - - i := 0 - - poly_map := make(map[string]^ast.Expr, 0, context.temp_allocator) - - for param in poly_params.list { - for name in param.names { - if len(ast_context.call.args) <= i { - break - } - - if param.type == nil { - continue - } - - if poly, ok := param.type.derived.(^ast.Typeid_Type); ok { - if ident, ok := name.derived.(^ast.Ident); ok { - poly_map[ident.name] = ast_context.call.args[i] - } else if poly, ok := name.derived.(^ast.Poly_Type); ok { - if poly.type != nil { - poly_map[poly.type.name] = ast_context.call.args[i] - } - } - } - - i += 1 - } - } - - Visit_Data :: struct { - poly_map: map[string]^ast.Expr, - symbol_value: ^SymbolStructValue, - parent: ^ast.Node, - i: int, - } - - visit :: proc(visitor: ^ast.Visitor, node: ^ast.Node) -> ^ast.Visitor { - if node == nil || visitor == nil { - return nil - } - - data := cast(^Visit_Data)visitor.data - - if ident, ok := node.derived.(^ast.Ident); ok { - if expr, ok := data.poly_map[ident.name]; ok { - if data.parent != nil { - #partial switch &v in data.parent.derived { - case ^ast.Array_Type: - v.elem = expr - case ^ast.Dynamic_Array_Type: - v.elem = expr - } - } else { - data.symbol_value.types[data.i] = expr - } - } - } - - #partial switch v in node.derived { - case ^ast.Array_Type, ^ast.Dynamic_Array_Type, ^ast.Selector_Expr: - data.parent = node - } - - return visitor - } - - for type, i in symbol_value.types { - data := Visit_Data { - poly_map = poly_map, - symbol_value = symbol_value, - i = i, - } - - visitor := ast.Visitor { - data = &data, - visit = visit, - } - - ast.walk(&visitor, type) - } -} - get_globals :: proc(file: ast.File, ast_context: ^AstContext) { exprs := common.collect_globals(file) @@ -3331,8 +2821,13 @@ get_generic_assignment :: proc( case ^Or_Return_Expr: get_generic_assignment(file, v.expr, ast_context, results, calls) case ^Call_Expr: + old_call := ast_context.call ast_context.call = cast(^ast.Call_Expr)value + defer { + ast_context.call = old_call + } + if symbol, ok := resolve_type_expression(ast_context, v.expr); ok { if procedure, ok := symbol.value.(SymbolProcedureValue); ok { for ret in procedure.return_types { @@ -4093,6 +3588,10 @@ resolve_entire_file :: proc( symbols := make(map[uintptr]SymbolAndNode, 10000, allocator) for decl in document.ast.decls { + if _, is_value := decl.derived.(^ast.Value_Decl); !is_value { + continue + } + resolve_entire_decl( &ast_context, document, @@ -5035,10 +4534,10 @@ fallback_position_context_completion :: proc( } p := parser.Parser { - err = common.parser_warning_handler, //empty - warn = common.parser_warning_handler, //empty + err = common.parser_warning_handler, //empty + warn = common.parser_warning_handler, //empty flags = {.Optional_Semicolons}, - file = &position_context.file, + file = &position_context.file, } tokenizer.init( diff --git a/src/server/check.odin b/src/server/check.odin index 502beb5..bd94f34 100644 --- a/src/server/check.odin +++ b/src/server/check.odin @@ -19,7 +19,7 @@ import "core:thread" import "shared:common" check :: proc(uri: common.Uri, writer: ^Writer, config: ^common.Config) { - data := make([]byte, mem.Kilobyte * 100, context.temp_allocator) + data := make([]byte, mem.Kilobyte * 200, context.temp_allocator) buffer: []byte code: u32 diff --git a/src/server/clone.odin b/src/server/clone.odin index e8cd090..09827d1 100644 --- a/src/server/clone.odin +++ b/src/server/clone.odin @@ -1,13 +1,13 @@ package server -import "core:mem" import "core:fmt" -import "core:odin/tokenizer" -import "core:odin/ast" -import "core:strings" -import "core:log" import "core:intrinsics" +import "core:log" +import "core:mem" +import "core:odin/ast" +import "core:odin/tokenizer" import "core:reflect" +import "core:strings" _ :: intrinsics new_type :: proc( @@ -143,148 +143,150 @@ clone_node :: proc( } if res.derived != nil do #partial switch r in res.derived { - case ^Ident: - n := node.derived.(^Ident) + case ^Ident: + n := node.derived.(^Ident) - if unique_strings == nil { - r.name = strings.clone(n.name, allocator) - } else { - r.name = get_index_unique_string(unique_strings, allocator, n.name) - } - case ^Implicit: - n := node.derived.(^Implicit) - if unique_strings == nil { - r.tok.text = strings.clone(n.tok.text, allocator) - } else { - r.tok.text = get_index_unique_string(unique_strings, allocator, n.tok.text) - } - case ^Undef: - case ^Basic_Lit: - n := node.derived.(^Basic_Lit) - if unique_strings == nil { - r.tok.text = strings.clone(n.tok.text, allocator) - } else { - r.tok.text = get_index_unique_string(unique_strings, allocator, n.tok.text) - } - case ^Basic_Directive: - n := node.derived.(^Basic_Directive) - if unique_strings == nil { - r.name = strings.clone(n.name, allocator) - } else { - r.name = get_index_unique_string(unique_strings, allocator, n.name) - } - case ^Ellipsis: - r.expr = clone_type(r.expr, allocator, unique_strings) - case ^Tag_Expr: - r.expr = clone_type(r.expr, allocator, unique_strings) - case ^Unary_Expr: - r.expr = clone_type(r.expr, allocator, unique_strings) - case ^Binary_Expr: - n := node.derived.(^Binary_Expr) - r.left = clone_type(r.left, allocator, unique_strings) - r.right = clone_type(r.right, allocator, unique_strings) - //Todo: Replace this with some constant table for opeator text - if unique_strings == nil { - r.op.text = strings.clone(n.op.text, allocator) - } else { - r.op.text = get_index_unique_string(unique_strings, allocator, n.op.text) - } - case ^Paren_Expr: - r.expr = clone_type(r.expr, allocator, unique_strings) - case ^Selector_Expr: - r.expr = clone_type(r.expr, allocator, unique_strings) - r.field = auto_cast clone_type(r.field, allocator, unique_strings) - case ^Implicit_Selector_Expr: - r.field = auto_cast clone_type(r.field, allocator, unique_strings) - case ^Slice_Expr: - r.expr = clone_type(r.expr, allocator, unique_strings) - r.low = clone_type(r.low, allocator, unique_strings) - r.high = clone_type(r.high, allocator, unique_strings) - case ^Attribute: - r.elems = clone_type(r.elems, allocator, unique_strings) - case ^Distinct_Type: - r.type = clone_type(r.type, allocator, unique_strings) - case ^Proc_Type: - r.params = auto_cast clone_type(r.params, allocator, unique_strings) - r.results = auto_cast clone_type(r.results, allocator, unique_strings) - case ^Pointer_Type: - r.elem = clone_type(r.elem, allocator, unique_strings) - case ^Array_Type: - r.len = clone_type(r.len, allocator, unique_strings) - r.elem = clone_type(r.elem, allocator, unique_strings) - r.tag = clone_type(r.tag, allocator, unique_strings) - case ^Dynamic_Array_Type: - r.elem = clone_type(r.elem, allocator, unique_strings) - r.tag = clone_type(r.tag, allocator, unique_strings) - case ^Struct_Type: - r.poly_params = auto_cast clone_type(r.poly_params, allocator, unique_strings) - r.align = clone_type(r.align, allocator, unique_strings) - r.fields = auto_cast clone_type(r.fields, allocator, unique_strings) - r.where_clauses = clone_type(r.where_clauses, allocator, unique_strings) - case ^Field: - r.names = clone_type(r.names, allocator, unique_strings) - r.type = clone_type(r.type, allocator, unique_strings) - r.default_value = clone_type(r.default_value, allocator, unique_strings) - case ^Field_List: - r.list = clone_type(r.list, allocator, unique_strings) - case ^Field_Value: - r.field = clone_type(r.field, allocator, unique_strings) - r.value = clone_type(r.value, allocator, unique_strings) - case ^Union_Type: - r.poly_params = auto_cast clone_type(r.poly_params, allocator, unique_strings) - r.align = clone_type(r.align, allocator, unique_strings) - r.variants = clone_type(r.variants, allocator, unique_strings) - r.where_clauses = clone_type(r.where_clauses, allocator, unique_strings) - case ^Enum_Type: - r.base_type = clone_type(r.base_type, allocator, unique_strings) - r.fields = clone_type(r.fields, allocator, unique_strings) - case ^Bit_Set_Type: - r.elem = clone_type(r.elem, allocator, unique_strings) - r.underlying = clone_type(r.underlying, allocator, unique_strings) - case ^Map_Type: - r.key = clone_type(r.key, allocator, unique_strings) - r.value = clone_type(r.value, allocator, unique_strings) - case ^Call_Expr: - r.expr = clone_type(r.expr, allocator, unique_strings) - r.args = clone_type(r.args, allocator, unique_strings) - case ^Typeid_Type: - r.specialization = clone_type(r.specialization, allocator, unique_strings) - case ^Ternary_When_Expr: - r.x = clone_type(r.x, allocator, unique_strings) - r.cond = clone_type(r.cond, allocator, unique_strings) - r.y = clone_type(r.y, allocator, unique_strings) - case ^Poly_Type: - r.type = auto_cast clone_type(r.type, allocator, unique_strings) - r.specialization = clone_type(r.specialization, allocator, unique_strings) - case ^Proc_Group: - r.args = clone_type(r.args, allocator, unique_strings) - case ^Comp_Lit: - r.type = clone_type(r.type, allocator, unique_strings) - r.elems = clone_type(r.elems, allocator, unique_strings) - case ^Proc_Lit: - r.type = cast(^Proc_Type)clone_type(cast(^Node)r.type, allocator, unique_strings) - r.body = nil - r.where_clauses = nil - case ^Helper_Type: - r.type = clone_type(r.type, allocator, unique_strings) - case ^Type_Cast: - r.type = clone_type(r.type, allocator, unique_strings) - r.expr = clone_type(r.expr, allocator, unique_strings) - case ^Deref_Expr: - r.expr = clone_type(r.expr, allocator, unique_strings) - case ^Index_Expr: - r.expr = clone_type(r.expr, allocator, unique_strings) - r.index = clone_type(r.index, allocator, unique_strings) - case ^Multi_Pointer_Type: - r.elem = clone_type(r.elem, allocator, unique_strings) - case ^Matrix_Type: - r.elem = clone_type(r.elem, allocator, unique_strings) - case ^Type_Assertion: - r.expr = clone_type(r.expr, allocator, unique_strings) - r.type = clone_type(r.type, allocator, unique_strings) - case: - //fmt.logf("Unhandled node kind: %T", r) + if unique_strings == nil { + r.name = strings.clone(n.name, allocator) + } else { + r.name = get_index_unique_string(unique_strings, allocator, n.name) + } + case ^Implicit: + n := node.derived.(^Implicit) + if unique_strings == nil { + r.tok.text = strings.clone(n.tok.text, allocator) + } else { + r.tok.text = get_index_unique_string(unique_strings, allocator, n.tok.text) + } + case ^Undef: + case ^Basic_Lit: + n := node.derived.(^Basic_Lit) + if unique_strings == nil { + r.tok.text = strings.clone(n.tok.text, allocator) + } else { + r.tok.text = get_index_unique_string(unique_strings, allocator, n.tok.text) } + case ^Basic_Directive: + n := node.derived.(^Basic_Directive) + if unique_strings == nil { + r.name = strings.clone(n.name, allocator) + } else { + r.name = get_index_unique_string(unique_strings, allocator, n.name) + } + case ^Ellipsis: + r.expr = clone_type(r.expr, allocator, unique_strings) + case ^Tag_Expr: + r.expr = clone_type(r.expr, allocator, unique_strings) + case ^Unary_Expr: + r.expr = clone_type(r.expr, allocator, unique_strings) + case ^Binary_Expr: + n := node.derived.(^Binary_Expr) + r.left = clone_type(r.left, allocator, unique_strings) + r.right = clone_type(r.right, allocator, unique_strings) + //Todo: Replace this with some constant table for opeator text + if unique_strings == nil { + r.op.text = strings.clone(n.op.text, allocator) + } else { + r.op.text = get_index_unique_string(unique_strings, allocator, n.op.text) + } + case ^Paren_Expr: + r.expr = clone_type(r.expr, allocator, unique_strings) + case ^Selector_Expr: + r.expr = clone_type(r.expr, allocator, unique_strings) + r.field = auto_cast clone_type(r.field, allocator, unique_strings) + case ^Implicit_Selector_Expr: + r.field = auto_cast clone_type(r.field, allocator, unique_strings) + case ^Slice_Expr: + r.expr = clone_type(r.expr, allocator, unique_strings) + r.low = clone_type(r.low, allocator, unique_strings) + r.high = clone_type(r.high, allocator, unique_strings) + case ^Attribute: + r.elems = clone_type(r.elems, allocator, unique_strings) + case ^Distinct_Type: + r.type = clone_type(r.type, allocator, unique_strings) + case ^Proc_Type: + r.params = auto_cast clone_type(r.params, allocator, unique_strings) + r.results = auto_cast clone_type(r.results, allocator, unique_strings) + case ^Pointer_Type: + r.elem = clone_type(r.elem, allocator, unique_strings) + case ^Array_Type: + r.len = clone_type(r.len, allocator, unique_strings) + r.elem = clone_type(r.elem, allocator, unique_strings) + r.tag = clone_type(r.tag, allocator, unique_strings) + case ^Dynamic_Array_Type: + r.elem = clone_type(r.elem, allocator, unique_strings) + r.tag = clone_type(r.tag, allocator, unique_strings) + case ^Struct_Type: + r.poly_params = auto_cast clone_type(r.poly_params, allocator, unique_strings) + r.align = clone_type(r.align, allocator, unique_strings) + r.fields = auto_cast clone_type(r.fields, allocator, unique_strings) + r.where_clauses = clone_type(r.where_clauses, allocator, unique_strings) + case ^Field: + r.names = clone_type(r.names, allocator, unique_strings) + r.type = clone_type(r.type, allocator, unique_strings) + r.default_value = clone_type(r.default_value, allocator, unique_strings) + case ^Field_List: + r.list = clone_type(r.list, allocator, unique_strings) + case ^Field_Value: + r.field = clone_type(r.field, allocator, unique_strings) + r.value = clone_type(r.value, allocator, unique_strings) + case ^Union_Type: + r.poly_params = auto_cast clone_type(r.poly_params, allocator, unique_strings) + r.align = clone_type(r.align, allocator, unique_strings) + r.variants = clone_type(r.variants, allocator, unique_strings) + r.where_clauses = clone_type(r.where_clauses, allocator, unique_strings) + case ^Enum_Type: + r.base_type = clone_type(r.base_type, allocator, unique_strings) + r.fields = clone_type(r.fields, allocator, unique_strings) + case ^Bit_Set_Type: + r.elem = clone_type(r.elem, allocator, unique_strings) + r.underlying = clone_type(r.underlying, allocator, unique_strings) + case ^Map_Type: + r.key = clone_type(r.key, allocator, unique_strings) + r.value = clone_type(r.value, allocator, unique_strings) + case ^Call_Expr: + r.expr = clone_type(r.expr, allocator, unique_strings) + r.args = clone_type(r.args, allocator, unique_strings) + case ^Typeid_Type: + r.specialization = clone_type(r.specialization, allocator, unique_strings) + case ^Ternary_When_Expr: + r.x = clone_type(r.x, allocator, unique_strings) + r.cond = clone_type(r.cond, allocator, unique_strings) + r.y = clone_type(r.y, allocator, unique_strings) + case ^Poly_Type: + r.type = auto_cast clone_type(r.type, allocator, unique_strings) + r.specialization = clone_type(r.specialization, allocator, unique_strings) + case ^Proc_Group: + r.args = clone_type(r.args, allocator, unique_strings) + case ^Comp_Lit: + r.type = clone_type(r.type, allocator, unique_strings) + r.elems = clone_type(r.elems, allocator, unique_strings) + case ^Proc_Lit: + r.type = cast(^Proc_Type)clone_type(cast(^Node)r.type, allocator, unique_strings) + r.body = nil + r.where_clauses = nil + case ^Helper_Type: + r.type = clone_type(r.type, allocator, unique_strings) + case ^Type_Cast: + r.type = clone_type(r.type, allocator, unique_strings) + r.expr = clone_type(r.expr, allocator, unique_strings) + case ^Deref_Expr: + r.expr = clone_type(r.expr, allocator, unique_strings) + case ^Index_Expr: + r.expr = clone_type(r.expr, allocator, unique_strings) + r.index = clone_type(r.index, allocator, unique_strings) + case ^Multi_Pointer_Type: + r.elem = clone_type(r.elem, allocator, unique_strings) + case ^Matrix_Type: + r.elem = clone_type(r.elem, allocator, unique_strings) + r.column_count = clone_type(r.column_count, allocator, unique_strings) + r.row_count = clone_type(r.row_count, allocator, unique_strings) + case ^Type_Assertion: + r.expr = clone_type(r.expr, allocator, unique_strings) + r.type = clone_type(r.type, allocator, unique_strings) + case: + //fmt.logf("Unhandled node kind: %T", r) + } return res } diff --git a/src/server/collector.odin b/src/server/collector.odin index 917dea6..5d91039 100644 --- a/src/server/collector.odin +++ b/src/server/collector.odin @@ -75,7 +75,7 @@ make_symbol_collection :: proc( config: ^common.Config, ) -> SymbolCollection { return( - SymbolCollection{ + SymbolCollection { allocator = allocator, config = config, packages = make(map[string]SymbolPackage, 16, allocator), @@ -141,7 +141,7 @@ collect_procedure_fields :: proc( value := SymbolProcedureValue { return_types = returns[:], arg_types = args[:], - generic = proc_type.generic, + generic = is_procedure_generic(proc_type), } return value @@ -435,7 +435,7 @@ collect_method :: proc(collection: ^SymbolCollection, symbol: Symbol) { return } - expr, _, ok := common.unwrap_pointer(value.arg_types[0].type) + expr, _, ok := common.unwrap_pointer_ident(value.arg_types[0].type) if !ok { return @@ -514,7 +514,7 @@ collect_objc :: proc( append( &objc_struct.functions, - ObjcFunction{ + ObjcFunction { logical_name = get_index_unique_string_collection( collection, objc_name, diff --git a/src/server/generics.odin b/src/server/generics.odin new file mode 100644 index 0000000..50a477a --- /dev/null +++ b/src/server/generics.odin @@ -0,0 +1,736 @@ +package server + +import "core:fmt" +import "core:log" +import "core:mem" +import "core:odin/ast" +import "core:odin/parser" +import "core:odin/tokenizer" +import "core:path/filepath" +import path "core:path/slashpath" +import "core:reflect" +import "core:slice" +import "core:sort" +import "core:strconv" +import "core:strings" +import "core:unicode/utf8" + +import "shared:common" + +resolve_poly :: proc( + ast_context: ^AstContext, + call_node: ^ast.Expr, + call_symbol: Symbol, + poly_node: ^ast.Expr, + poly_map: ^map[string]^ast.Expr, +) -> bool { + if poly_node == nil || call_node == nil { + return false + } + + specialization: ^ast.Expr + type: ^ast.Expr + + poly_node := poly_node + poly_node, _, _ = common.unwrap_pointer_expr(poly_node) + + #partial switch v in poly_node.derived { + case ^ast.Typeid_Type: + specialization = v.specialization + case ^ast.Poly_Type: + specialization = v.specialization + type = v.type + } + + if specialization == nil { + if type != nil { + if ident, ok := unwrap_ident(type); ok { + poly_map[ident.name] = make_ident_ast( + ast_context, + call_node.pos, + call_node.end, + call_symbol.name, + ) + } + } + return true + } else if type != nil { + if ident, ok := unwrap_ident(type); ok { + poly_map[ident.name] = specialization + } + } + + #partial switch p in specialization.derived { + case ^ast.Matrix_Type: + if call_matrix, ok := call_node.derived.(^ast.Matrix_Type); ok { + found := false + if poly_type, ok := p.row_count.derived.(^ast.Poly_Type); ok { + if ident, ok := unwrap_ident(poly_type.type); ok { + poly_map[ident.name] = call_matrix.row_count + } + + if poly_type.specialization != nil { + return resolve_poly( + ast_context, + call_matrix.row_count, + call_symbol, + p.row_count, + poly_map, + ) + } + found |= true + } + + if poly_type, ok := p.column_count.derived.(^ast.Poly_Type); ok { + if ident, ok := unwrap_ident(poly_type.type); ok { + poly_map[ident.name] = call_matrix.column_count + } + + if poly_type.specialization != nil { + return resolve_poly( + ast_context, + call_matrix.column_count, + call_symbol, + p.column_count, + poly_map, + ) + } + found |= true + } + + if poly_type, ok := p.elem.derived.(^ast.Poly_Type); ok { + if ident, ok := unwrap_ident(poly_type.type); ok { + poly_map[ident.name] = call_matrix.elem + } + + if poly_type.specialization != nil { + return resolve_poly( + ast_context, + call_matrix.elem, + call_symbol, + p.elem, + poly_map, + ) + } + found |= true + } + return found + } + case ^ast.Call_Expr: + if call_struct, ok := call_node.derived.(^ast.Struct_Type); ok { + arg_index := 0 + struct_value := call_symbol.value.(SymbolStructValue) + + for arg in p.args { + if poly_type, ok := arg.derived.(^ast.Poly_Type); ok { + if poly_type.type == nil || + struct_value.poly == nil || + len(struct_value.args) <= arg_index { + return false + } + + poly_map[poly_type.type.name] = + struct_value.args[arg_index] + + arg_index += 1 + } + } + } + case ^ast.Struct_Type: + case ^ast.Dynamic_Array_Type: + if call_array, ok := call_node.derived.(^ast.Dynamic_Array_Type); ok { + if poly_type, ok := p.elem.derived.(^ast.Poly_Type); ok { + if ident, ok := unwrap_ident(poly_type.type); ok { + poly_map[ident.name] = call_array.elem + } + + if poly_type.specialization != nil { + return resolve_poly( + ast_context, + call_array.elem, + call_symbol, + p.elem, + poly_map, + ) + } + return true + } + } + case ^ast.Array_Type: + if call_array, ok := call_node.derived.(^ast.Array_Type); ok { + found := false + if poly_type, ok := p.elem.derived.(^ast.Poly_Type); ok { + if ident, ok := unwrap_ident(poly_type.type); ok { + poly_map[ident.name] = call_array.elem + } + + if poly_type.specialization != nil { + return resolve_poly( + ast_context, + call_array.elem, + call_symbol, + p.elem, + poly_map, + ) + } + found |= true + } + if p.len != nil { + if poly_type, ok := p.len.derived.(^ast.Poly_Type); ok { + if ident, ok := unwrap_ident(poly_type.type); ok { + poly_map[ident.name] = call_array.len + } + + if poly_type.specialization != nil { + return resolve_poly( + ast_context, + call_array.len, + call_symbol, + p.len, + poly_map, + ) + } + found |= true + } + } + + return found + } + case ^ast.Map_Type: + if call_map, ok := call_node.derived.(^ast.Map_Type); ok { + found := false + if poly_type, ok := p.key.derived.(^ast.Poly_Type); ok { + if ident, ok := unwrap_ident(poly_type.type); ok { + poly_map[ident.name] = call_map.key + } + + if poly_type.specialization != nil { + return resolve_poly( + ast_context, + call_map.key, + call_symbol, + p.key, + poly_map, + ) + } + found |= true + } + + if poly_type, ok := p.value.derived.(^ast.Poly_Type); ok { + if ident, ok := unwrap_ident(poly_type.type); ok { + poly_map[ident.name] = call_map.value + } + + if poly_type.specialization != nil { + return resolve_poly( + ast_context, + call_map.value, + call_symbol, + p.value, + poly_map, + ) + } + found |= true + } + return found + } + case ^ast.Multi_Pointer_Type: + if call_pointer, ok := call_node.derived.(^ast.Multi_Pointer_Type); + ok { + if poly_type, ok := p.elem.derived.(^ast.Poly_Type); ok { + if ident, ok := unwrap_ident(poly_type.type); ok { + poly_map[ident.name] = call_pointer.elem + } + + if poly_type.specialization != nil { + return resolve_poly( + ast_context, + call_pointer.elem, + call_symbol, + p.elem, + poly_map, + ) + } + return true + } + } + case ^ast.Ident: + if n, ok := call_node.derived.(^ast.Ident); ok { + return true + } + case: + log.panicf("Unhandled specialization %v", specialization.derived) + } + + return false +} + +find_and_replace_poly_type :: proc( + expr: ^ast.Expr, + poly_map: ^map[string]^ast.Expr, +) { + is_in_poly_map :: proc( + node: ^ast.Node, + poly_map: ^map[string]^ast.Expr, + ) -> ( + ^ast.Expr, + bool, + ) { + if node == nil { + return {}, false + } + + if ident, ok := node.derived.(^ast.Ident); ok { + if v, ok := poly_map[ident.name]; ok { + return v, ok + } + } + if poly, ok := node.derived.(^ast.Poly_Type); ok && poly.type != nil { + if v, ok := poly_map[poly.type.name]; ok { + return v, ok + } + } + + return nil, false + } + + visit_function :: proc( + visitor: ^ast.Visitor, + node: ^ast.Node, + ) -> ^ast.Visitor { + if node == nil { + return nil + } + + poly_map := cast(^map[string]^ast.Expr)visitor.data + + #partial switch v in node.derived { + case ^ast.Matrix_Type: + if expr, ok := is_in_poly_map(v.elem, poly_map); ok { + v.elem = expr + } + if expr, ok := is_in_poly_map(v.column_count, poly_map); ok { + v.column_count = expr + } + if expr, ok := is_in_poly_map(v.row_count, poly_map); ok { + v.row_count = expr + } + case ^ast.Dynamic_Array_Type: + if expr, ok := is_in_poly_map(v.elem, poly_map); ok { + v.elem = expr + } + case ^ast.Array_Type: + if expr, ok := is_in_poly_map(v.elem, poly_map); ok { + v.elem = expr + } + if expr, ok := is_in_poly_map(v.len, poly_map); ok { + v.len = expr + } + case ^ast.Multi_Pointer_Type: + if expr, ok := is_in_poly_map(v.elem, poly_map); ok { + v.elem = expr + } + case ^ast.Pointer_Type: + if expr, ok := is_in_poly_map(v.elem, poly_map); ok { + v.elem = expr + } + } + + return visitor + } + + visitor := ast.Visitor { + data = poly_map, + visit = visit_function, + } + + ast.walk(&visitor, expr) +} + +resolve_generic_function :: proc { + resolve_generic_function_ast, + resolve_generic_function_symbol, +} + +resolve_generic_function_ast :: proc( + ast_context: ^AstContext, + proc_lit: ast.Proc_Lit, +) -> ( + Symbol, + bool, +) { + + using ast + + if proc_lit.type.params == nil { + return Symbol{}, false + } + + if proc_lit.type.results == nil { + return Symbol{}, false + } + + if ast_context.call == nil { + return Symbol{}, false + } + + return resolve_generic_function_symbol( + ast_context, + proc_lit.type.params.list, + proc_lit.type.results.list, + ) +} + + +resolve_generic_function_symbol :: proc( + ast_context: ^AstContext, + params: []^ast.Field, + results: []^ast.Field, +) -> ( + Symbol, + bool, +) { + if params == nil { + return {}, false + } + + if results == nil { + return {}, false + } + + if ast_context.call == nil { + return {}, false + } + + call_expr := ast_context.call + + poly_map := make(map[string]^ast.Expr, 0, context.temp_allocator) + + i := 0 + count_required_params := 0 + + for param in params { + if param.default_value == nil { + count_required_params += 1 + } + + for name in param.names { + if len(call_expr.args) <= i { + break + } + + if param.type == nil { + continue + } + + reset_ast_context(ast_context) + + if symbol, ok := resolve_type_expression( + ast_context, + call_expr.args[i], + ); ok { + symbol_expr := symbol_to_expr( + symbol, + call_expr.args[i].pos.file, + context.temp_allocator, + ) + + if symbol_expr == nil { + return {}, false + } + + symbol_expr = clone_expr(symbol_expr, ast_context.allocator, nil) + param_type := clone_expr(param.type, ast_context.allocator, nil) + + if resolve_poly( + ast_context, + symbol_expr, + symbol, + param_type, + &poly_map, + ) { + if poly, ok := name.derived.(^ast.Poly_Type); ok { + poly_map[poly.type.name] = clone_expr( + call_expr.args[i], + ast_context.allocator, + nil, + ) + } + } + } + + i += 1 + } + } + + for k, v in poly_map { + find_and_replace_poly_type(v, &poly_map) + //fmt.println(k, v.derived, "\n") + } + + if count_required_params > len(call_expr.args) || + count_required_params == 0 || + len(call_expr.args) == 0 { + return {}, false + } + + function_name := "" + function_range: common.Range + + if ident, ok := call_expr.expr.derived.(^ast.Ident); ok { + function_name = ident.name + function_range = common.get_token_range(ident, ast_context.file.src) + } else if selector, ok := call_expr.expr.derived.(^ast.Selector_Expr); ok { + function_name = selector.field.name + function_range = common.get_token_range(selector, ast_context.file.src) + } else { + return {}, false + } + + symbol := Symbol { + range = function_range, + type = .Function, + name = function_name, + pkg = ast_context.current_package, + } + + return_types := make([dynamic]^ast.Field, ast_context.allocator) + argument_types := make([dynamic]^ast.Field, ast_context.allocator) + + for result in results { + if result.type == nil { + continue + } + + field := cast(^ast.Field)clone_node(result, ast_context.allocator, nil) + + if ident, ok := unwrap_ident(field.type); ok { + if expr, ok := poly_map[ident.name]; ok { + field.type = expr + } + } + + find_and_replace_poly_type(field.type, &poly_map) + + append(&return_types, field) + } + + for param in params { + if len(param.names) == 0 { + continue + } + + //check the name for poly + if poly_type, ok := param.names[0].derived.(^ast.Poly_Type); + ok && param.type != nil { + if m, ok := poly_map[poly_type.type.name]; ok { + field := cast(^ast.Field)clone_node( + param, + ast_context.allocator, + nil, + ) + field.type = m + append(&argument_types, field) + } + } else { + append(&argument_types, param) + } + } + + symbol.value = SymbolProcedureValue { + return_types = return_types[:], + arg_types = argument_types[:], + } + + return symbol, true +} + +is_procedure_generic :: proc(proc_type: ^ast.Proc_Type) -> bool { + if proc_type.generic { + return true + } + + for param in proc_type.params.list { + if param.type == nil { + continue + } + + if expr, _, ok := common.unwrap_pointer_expr(param.type); ok { + if _, ok := expr.derived.(^ast.Poly_Type); ok { + return true + } + } + + } + + return false +} + + +resolve_poly_struct :: proc( + ast_context: ^AstContext, + poly_params: ^ast.Field_List, + symbol: ^Symbol, +) { + if ast_context.call == nil { + return + } + + symbol_value := &symbol.value.(SymbolStructValue) + + if symbol_value == nil { + return + } + + i := 0 + + poly_map := make(map[string]^ast.Expr, 0, context.temp_allocator) + args := make([dynamic]^ast.Expr, 0, context.temp_allocator) + + for param in poly_params.list { + for name in param.names { + if len(ast_context.call.args) <= i { + break + } + + if param.type == nil { + continue + } + + if poly, ok := param.type.derived.(^ast.Typeid_Type); ok { + if ident, ok := name.derived.(^ast.Ident); ok { + poly_map[ident.name] = ast_context.call.args[i] + } else if poly, ok := name.derived.(^ast.Poly_Type); ok { + if poly.type != nil { + poly_map[poly.type.name] = ast_context.call.args[i] + } + } + } + + append(&args, ast_context.call.args[i]) + + i += 1 + } + } + + Visit_Data :: struct { + poly_map: map[string]^ast.Expr, + symbol_value: ^SymbolStructValue, + parent: ^ast.Node, + i: int, + poly_index: int, + } + + visit :: proc(visitor: ^ast.Visitor, node: ^ast.Node) -> ^ast.Visitor { + if node == nil || visitor == nil { + return nil + } + + data := cast(^Visit_Data)visitor.data + + if ident, ok := node.derived.(^ast.Ident); ok { + if expr, ok := data.poly_map[ident.name]; ok { + if data.parent != nil { + #partial switch &v in data.parent.derived { + case ^ast.Array_Type: + v.elem = expr + case ^ast.Dynamic_Array_Type: + v.elem = expr + } + } else { + data.symbol_value.types[data.i] = expr + data.poly_index += 1 + } + } + } + + #partial switch v in node.derived { + case ^ast.Array_Type, ^ast.Dynamic_Array_Type, ^ast.Selector_Expr: + data.parent = node + } + + return visitor + } + + for type, i in symbol_value.types { + data := Visit_Data { + poly_map = poly_map, + symbol_value = symbol_value, + i = i, + } + + visitor := ast.Visitor { + data = &data, + visit = visit, + } + + ast.walk(&visitor, type) + } + + symbol_value.args = args[:] +} + + +resolve_poly_union :: proc( + ast_context: ^AstContext, + poly_params: ^ast.Field_List, + symbol: ^Symbol, +) { + if ast_context.call == nil { + return + } + + symbol_value := &symbol.value.(SymbolUnionValue) + + if symbol_value == nil { + return + } + + i := 0 + + poly_map := make(map[string]^ast.Expr, 0, context.temp_allocator) + + for param in poly_params.list { + for name in param.names { + if len(ast_context.call.args) <= i { + break + } + + if param.type == nil { + continue + } + + if poly, ok := param.type.derived.(^ast.Typeid_Type); ok { + if ident, ok := name.derived.(^ast.Ident); ok { + poly_map[ident.name] = ast_context.call.args[i] + } else if poly, ok := name.derived.(^ast.Poly_Type); ok { + if poly.type != nil { + poly_map[poly.type.name] = ast_context.call.args[i] + } + } + } + + i += 1 + } + } + + for type, i in symbol_value.types { + if ident, ok := type.derived.(^ast.Ident); ok { + if expr, ok := poly_map[ident.name]; ok { + symbol_value.types[i] = expr + } + } else if call_expr, ok := type.derived.(^ast.Call_Expr); ok { + if call_expr.args == nil { + continue + } + + for arg, i in call_expr.args { + if ident, ok := arg.derived.(^ast.Ident); ok { + if expr, ok := poly_map[ident.name]; ok { + symbol_value.types[i] = expr + } + } + } + } + } +} diff --git a/src/server/methods.odin b/src/server/methods.odin index 4d1d3f2..62d2cb4 100644 --- a/src/server/methods.odin +++ b/src/server/methods.odin @@ -88,6 +88,11 @@ append_method_completion :: proc( continue } + if len(value.arg_types) == 0 || + value.arg_types[0].type == nil { + continue + } + first_arg: Symbol first_arg, ok = resolve_type_expression( ast_context, @@ -109,7 +114,11 @@ append_method_completion :: proc( if symbol.pkg != ast_context.document_package { new_text = fmt.tprintf( "%v.%v($0)", - path.base(symbol.pkg, false, ast_context.allocator), + path.base( + get_symbol_pkg_name(ast_context, symbol), + false, + ast_context.allocator, + ), symbol.name, ) } else { diff --git a/src/server/requests.odin b/src/server/requests.odin index cdd2702..4c653ad 100644 --- a/src/server/requests.odin +++ b/src/server/requests.odin @@ -10,7 +10,6 @@ import "core:odin/parser" import "core:os" import "core:path/filepath" import path "core:path/slashpath" -import "core:runtime" import "core:slice" import "core:strconv" import "core:strings" @@ -20,6 +19,7 @@ import "core:time" import "shared:common" +import "base:runtime" Header :: struct { content_length: int, @@ -551,6 +551,17 @@ read_ols_initialize_options :: proc( allocator = context.allocator, ) } + + if "base" not_in config.collections && odin_core_env != "" { + forward_path, _ := filepath.to_slash( + odin_core_env, + context.temp_allocator, + ) + config.collections[strings.clone("base")] = path.join( + elems = {forward_path, "base"}, + allocator = context.allocator, + ) + } } request_initialize :: proc( @@ -1077,8 +1088,8 @@ notification_did_save :: proc( fullpath := uri.path p := parser.Parser { - err = log_error_handler, - warn = log_warning_handler, + err = log_error_handler, + warn = log_warning_handler, flags = {.Optional_Semicolons}, } diff --git a/src/server/symbol.odin b/src/server/symbol.odin index d6a6aa5..57d3946 100644 --- a/src/server/symbol.odin +++ b/src/server/symbol.odin @@ -1,8 +1,11 @@ package server +import "core:fmt" import "core:hash" +import "core:log" import "core:mem" import "core:odin/ast" +import "core:odin/tokenizer" import "core:path/filepath" import path "core:path/slashpath" import "core:slice" @@ -22,6 +25,7 @@ SymbolStructValue :: struct { types: []^ast.Expr, usings: map[int]bool, poly: ^ast.Field_List, + args: []^ast.Expr, //The arguments in the call expression for poly } SymbolPackageValue :: struct {} @@ -83,6 +87,7 @@ SymbolUntypedValue :: struct { String, Bool, }, + tok: tokenizer.Token, } SymbolMapValue :: struct { @@ -227,7 +232,9 @@ free_symbol :: proc(symbol: Symbol, allocator: mem.Allocator) { case SymbolMapValue: common.free_ast(v.key, allocator) common.free_ast(v.value, allocator) - case SymbolUntypedValue, SymbolPackageValue: + case SymbolUntypedValue: + delete(v.tok.text) + case SymbolPackageValue: } } @@ -284,3 +291,64 @@ symbol_kind_to_type :: proc(type: SymbolType) -> SymbolKind { return .Null } } + +symbol_to_expr :: proc( + symbol: Symbol, + file: string, + allocator := context.temp_allocator, +) -> ^ast.Expr { + + pos := tokenizer.Pos { + file = file, + } + + end := tokenizer.Pos { + file = file, + } + + #partial switch v in symbol.value { + case SymbolDynamicArrayValue: + type := new_type(ast.Dynamic_Array_Type, pos, end, allocator) + type.elem = v.expr + return type + case SymbolFixedArrayValue: + type := new_type(ast.Array_Type, pos, end, allocator) + type.elem = v.expr + type.len = v.len + return type + case SymbolMapValue: + type := new_type(ast.Map_Type, pos, end, allocator) + type.key = v.key + type.value = v.value + return type + case SymbolBasicValue: + return v.ident + case SymbolSliceValue: + type := new_type(ast.Array_Type, pos, end, allocator) + type.elem = v.expr + return type + case SymbolStructValue: + type := new_type(ast.Struct_Type, pos, end, allocator) + return type + case SymbolUntypedValue: + type := new_type(ast.Basic_Lit, pos, end, allocator) + return type + case SymbolMatrixValue: + type := new_type(ast.Matrix_Type, pos, end, allocator) + type.row_count = v.x + type.column_count = v.y + type.elem = v.expr + return type + case SymbolProcedureValue: + type := new_type(ast.Proc_Type, pos, end, allocator) + type.results = new_type(ast.Field_List, pos, end, allocator) + type.results.list = v.return_types + type.params = new_type(ast.Field_List, pos, end, allocator) + type.params.list = v.arg_types + return type + case: + return nil + } + + return nil +} diff --git a/tests/completions_test.odin b/tests/completions_test.odin index c3496e4..e5a9460 100644 --- a/tests/completions_test.odin +++ b/tests/completions_test.odin @@ -789,33 +789,6 @@ ast_struct_for_in_switch_stmt_completion :: proc(t: ^testing.T) { test.expect_completion_details(t, &source, ".", {"Window.height: int"}) } -/* -@(test) -ast_overload_with_autocast_completion :: proc(t: ^testing.T) { - source := test.Source { - main = `package test - - my_group :: proc{ - with_autocast, - with_bool, - }; - with_autocast :: proc(#any_int a: int) -> bool { - } - with_bool :: proc(a: bool) -> int { - } - - main :: proc() { - my_uint: uint = 0; - my_value := my_group(my_uint); - my_val{*} - } - `, - packages = {}, - } - - test.expect_completion_details(t, &source, "", {"test.my_value: bool"}) -} -*/ @(test) ast_overload_with_any_int_completion :: proc(t: ^testing.T) { @@ -2407,3 +2380,231 @@ ast_assign_to_global_function :: proc(t: ^testing.T) { test.expect_completion_details(t, &source, "", {"test.global_foo: string"}) } + +@(test) +ast_poly_dynamic_type :: proc(t: ^testing.T) { + packages := make([dynamic]test.Package) + + source := test.Source { + main = `package test + import "my_package" + + _raw_data_dynamic :: proc(data: $T/[dynamic]$E) -> [^]E { + return {} + } + + main :: proc() { + my_dynamic: [dynamic]int + ret_dynamic := _raw_data_dynamic(my_dynamic) + ret_dy{*} + } + + `, + packages = packages[:], + } + + test.expect_completion_details( + t, + &source, + "", + {"test.ret_dynamic: [^]int"}, + ) +} + +@(test) +ast_poly_array_type :: proc(t: ^testing.T) { + packages := make([dynamic]test.Package) + + source := test.Source { + main = `package test + import "my_package" + + _raw_data_array :: proc(data: $T/[]$E) -> [^]E { + return {} + } + + main :: proc() { + my_array: []int + ret_array := _raw_data_array(my_array) + ret_arr{*} + } + + `, + packages = packages[:], + } + + test.expect_completion_details(t, &source, "", {"test.ret_array: [^]int"}) +} + +@(test) +ast_poly_struct_with_poly :: proc(t: ^testing.T) { + packages := make([dynamic]test.Package) + + source := test.Source { + main = `package test + Small_Array :: struct($N: int, $T: typeid) where N >= 0 { + data: [N]T, + len: int, + } + + Animal :: struct { + happy: bool, + sad: bool, + fluffy: bool, + } + + get_ptr :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int) -> ^T { + return &a.data[index] + } + + main :: proc() { + animals := Small_Array(5, Animal){} + first := get_ptr(&animals, 0) + fir{*} + } + `, + packages = packages[:], + } + + + test.expect_completion_details(t, &source, "", {"test.first: ^Animal"}) +} + +@(test) +ast_poly_proc_array_constant :: proc(t: ^testing.T) { + packages := make([dynamic]test.Package) + + source := test.Source { + main = `package test + make_f32_array :: proc($N: int, $val: f32) -> (res: [N]f32) { + for _, i in res { + res[i] = val*val + } + return + } + + main :: proc() { + array := make_f32_array(3, 2) + arr{*} + } + `, + packages = packages[:], + } + + + test.expect_completion_details(t, &source, "", {"test.array: [3]f32"}) +} + +@(test) +ast_poly_proc_matrix_type :: proc(t: ^testing.T) { + packages := make([dynamic]test.Package) + + source := test.Source { + main = `package test + + matrix_to_ptr :: proc "contextless" (m: ^$A/matrix[$I, $J]$E) -> ^E { + return &m[0, 0] + } + + + main :: proc() { + my_matrix: matrix[2, 2]f32 + ptr := matrix_to_ptr(&my_matrix) + pt{*} + } + + `, + packages = packages[:], + } + + test.expect_completion_details(t, &source, "", {"test.ptr: ^f32"}) +} + +@(test) +ast_poly_proc_matrix_constant_array :: proc(t: ^testing.T) { + packages := make([dynamic]test.Package) + + source := test.Source { + main = `package test + + matrix_to_ptr :: proc "contextless" (m: ^$A/matrix[$I, $J]$E) -> [J]E { + return {} + } + + main :: proc() { + my_matrix: matrix[4, 3]f32 + + ptr := matrix_to_ptr(&my_matrix) + pt{*} + } + `, + packages = packages[:], + } + + test.expect_completion_details(t, &source, "", {"test.ptr: [3]f32"}) +} + +@(test) +ast_poly_proc_matrix_constant_array_2 :: proc(t: ^testing.T) { + packages := make([dynamic]test.Package) + + source := test.Source { + main = `package test + array_cast :: proc "contextless" ( + v: $A/[$N]$T, + $Elem_Type: typeid, + ) -> ( + w: [N]Elem_Type, + ) { + for i in 0 ..< N { + w[i] = Elem_Type(v[i]) + } + return + } + main :: proc() { + my_vector: [10]int + myss := array_cast(my_vector, f32) + mys{*} + } + `, + packages = packages[:], + } + + test.expect_completion_details(t, &source, "", {"test.myss: [10]f32"}) +} + +@(test) +ast_poly_proc_matrix_whole :: proc(t: ^testing.T) { + packages := make([dynamic]test.Package) + + source := test.Source { + main = `package test + + @(require_results) + matrix_mul :: proc "contextless" ( + a, b: $M/matrix[$N, N]$E, + ) -> ( + c: M, + ) where !IS_ARRAY(E), + IS_NUMERIC(E) #no_bounds_check { + return a * b + } + + matrix4_from_trs_f16 :: proc "contextless" () -> matrix[4, 4]f32 { + translation: matrix[4, 4]f32 + rotation: matrix[4, 4]f32 + dsszz := matrix_mul(scale, translation) + dssz{*} + } + `, + packages = {}, + } + + test.expect_completion_details( + t, + &source, + "", + {"test.dsszz: matrix[4,4]f32"}, + ) + +} |