diff --git a/src/check/canonicalize.zig b/src/check/canonicalize.zig index 05d5f20ed1..a3e464e16f 100644 --- a/src/check/canonicalize.zig +++ b/src/check/canonicalize.zig @@ -1,6 +1,7 @@ const std = @import("std"); const base = @import("../base.zig"); const parse = @import("parse.zig"); +const tokenize = @import("parse/tokenize.zig"); const collections = @import("../collections.zig"); const types = @import("../types/types.zig"); @@ -9,6 +10,7 @@ const Scope = @import("./canonicalize/Scope.zig"); const Node = @import("./canonicalize/Node.zig"); const AST = parse.AST; +const Token = tokenize.Token; can_ir: *CIR, parse_ir: *AST, @@ -260,12 +262,8 @@ pub fn canonicalize_file( for (self.parse_ir.store.statementSlice(file.statements)) |stmt_id| { const stmt = self.parse_ir.store.getStatement(stmt_id); switch (stmt) { - .import => |_| { - const feature = self.can_ir.env.strings.insert(self.can_ir.env.gpa, "top-level import"); - self.can_ir.pushDiagnostic(CIR.Diagnostic{ .not_implemented = .{ - .feature = feature, - .region = Region.zero(), - } }); + .import => |import_stmt| { + _ = self.canonicalizeImportStatement(import_stmt); }, .decl => |decl| { const def_idx = self.canonicalize_decl(decl); @@ -490,6 +488,205 @@ fn bringIngestedFileIntoScope( } } +/// Canonicalize an import statement, handling both top-level file imports and statement imports +fn canonicalizeImportStatement( + self: *Self, + import_stmt: @TypeOf(@as(AST.Statement, undefined).import), +) ?CIR.Statement.Idx { + // 1. Reconstruct the full module name (e.g., "json.Json") + const module_name = blk: { + if (self.parse_ir.tokens.resolveIdentifier(import_stmt.module_name_tok) == null) { + const region = self.parse_ir.tokenizedRegionToRegion(import_stmt.region); + const feature = self.can_ir.env.strings.insert(self.can_ir.env.gpa, "resolve import module name token"); + self.can_ir.pushDiagnostic(CIR.Diagnostic{ .not_implemented = .{ + .feature = feature, + .region = region, + } }); + return null; + } + + if (import_stmt.qualifier_tok) |qualifier_tok| { + if (self.parse_ir.tokens.resolveIdentifier(qualifier_tok) == null) { + const region = self.parse_ir.tokenizedRegionToRegion(import_stmt.region); + const feature = self.can_ir.env.strings.insert(self.can_ir.env.gpa, "resolve import qualifier token"); + self.can_ir.pushDiagnostic(CIR.Diagnostic{ .not_implemented = .{ + .feature = feature, + .region = region, + } }); + return null; + } + + // Slice from original source to get "qualifier.ModuleName" + const qualifier_region = self.parse_ir.tokens.resolve(qualifier_tok); + const module_region = self.parse_ir.tokens.resolve(import_stmt.module_name_tok); + const full_name = self.parse_ir.source[qualifier_region.start.offset..module_region.end.offset]; + break :blk self.can_ir.env.idents.insert(self.can_ir.env.gpa, base.Ident.for_text(full_name), Region.zero()); + } else { + // No qualifier, just use the module name directly + break :blk self.parse_ir.tokens.resolveIdentifier(import_stmt.module_name_tok).?; + } + }; + + // 2. Determine the alias (either explicit or default to last part) + const alias = self.resolveModuleAlias(import_stmt.alias_tok, module_name) orelse return null; + + // 3. Add to scope: alias -> module_name mapping + self.scopeIntroduceModuleAlias(alias, module_name); + + // 4. Convert exposed items and introduce them into scope + const cir_exposes = self.convertASTExposesToCIR(import_stmt.exposes); + self.introduceExposedItemsIntoScope(cir_exposes, module_name); + + // 5. Create CIR import statement + const cir_import = CIR.Statement{ + .import = .{ + .module_name_tok = module_name, + .qualifier_tok = if (import_stmt.qualifier_tok) |q_tok| self.parse_ir.tokens.resolveIdentifier(q_tok) else null, + .alias_tok = if (import_stmt.alias_tok) |a_tok| self.parse_ir.tokens.resolveIdentifier(a_tok) else null, + .exposes = cir_exposes, + .region = self.parse_ir.tokenizedRegionToRegion(import_stmt.region), + }, + }; + + const import_idx = self.can_ir.store.addStatement(cir_import); + self.can_ir.store.addScratchStatement(import_idx); + return import_idx; +} + +/// Resolve the module alias name from either explicit alias or module name +fn resolveModuleAlias( + self: *Self, + alias_tok: ?Token.Idx, + module_name: Ident.Idx, +) ?Ident.Idx { + if (alias_tok) |alias_token| { + return self.parse_ir.tokens.resolveIdentifier(alias_token); + } else { + // Extract last part from module name - e.g., "Json" from "json.Json" + return self.extractModuleName(module_name); + } +} + +/// Create a qualified name by combining module and field names (e.g., "json.Json.utf8") +fn createQualifiedName( + self: *Self, + module_name: Ident.Idx, + field_name: Ident.Idx, +) Ident.Idx { + const module_text = self.can_ir.env.idents.getText(module_name); + const field_text = self.can_ir.env.idents.getText(field_name); + + // Allocate space for "module.field" - this case still needs allocation since we're combining + // module name from import with field name from usage site + const qualified_text = std.fmt.allocPrint(self.can_ir.env.gpa, "{s}.{s}", .{ module_text, field_text }) catch |err| exitOnOom(err); + defer self.can_ir.env.gpa.free(qualified_text); + + return self.can_ir.env.idents.insert(self.can_ir.env.gpa, base.Ident.for_text(qualified_text), Region.zero()); +} + +/// Create an external declaration for a qualified name +fn createExternalDeclaration( + self: *Self, + qualified_name: Ident.Idx, + module_name: Ident.Idx, + local_name: Ident.Idx, + kind: CIR.ExternalDecl.kind, + region: Region, +) CIR.ExternalDecl.Idx { + const external_decl = CIR.ExternalDecl{ + .qualified_name = qualified_name, + .module_name = module_name, + .local_name = local_name, + .type_var = self.can_ir.pushFreshTypeVar(@enumFromInt(0), region), + .kind = kind, + .region = region, + }; + + return self.can_ir.pushExternalDecl(external_decl); +} + +/// Convert AST exposed items to CIR exposed items +fn convertASTExposesToCIR( + self: *Self, + ast_exposes: AST.ExposedItem.Span, +) CIR.ExposedItem.Span { + const scratch_start = self.can_ir.store.scratchExposedItemTop(); + + const ast_exposed_slice = self.parse_ir.store.exposedItemSlice(ast_exposes); + for (ast_exposed_slice) |ast_exposed_idx| { + const ast_exposed = self.parse_ir.store.getExposedItem(ast_exposed_idx); + + // Convert AST exposed item to CIR exposed item + const cir_exposed = convert_item: { + // Extract identifier token and alias token + const ident_token, const alias_token, const is_wildcard = switch (ast_exposed) { + .lower_ident => |ident| .{ ident.ident, ident.as, false }, + .upper_ident => |ident| .{ ident.ident, ident.as, false }, + .upper_ident_star => |star_ident| .{ star_ident.ident, null, true }, + }; + + // Resolve the main identifier name + const name = resolve_ident: { + if (self.parse_ir.tokens.resolveIdentifier(ident_token)) |resolved| { + break :resolve_ident resolved; + } else { + break :resolve_ident self.can_ir.env.idents.insert(self.can_ir.env.gpa, base.Ident.for_text("unknown"), base.Region.zero()); + } + }; + + // Resolve the alias if present + const alias = resolve_alias: { + if (alias_token) |as_token| { + if (self.parse_ir.tokens.resolveIdentifier(as_token)) |resolved| { + break :resolve_alias resolved; + } else { + break :resolve_alias self.can_ir.env.idents.insert(self.can_ir.env.gpa, base.Ident.for_text("unknown"), base.Region.zero()); + } + } else { + break :resolve_alias null; + } + }; + + break :convert_item CIR.ExposedItem{ + .name = name, + .alias = alias, + .is_wildcard = is_wildcard, + }; + }; + + const cir_exposed_idx = self.can_ir.store.addExposedItem(cir_exposed); + self.can_ir.store.addScratchExposedItem(cir_exposed_idx); + } + + return self.can_ir.store.exposedItemSpanFrom(scratch_start); +} + +/// Introduce converted exposed items into scope for identifier resolution +fn introduceExposedItemsIntoScope( + self: *Self, + exposed_items_span: CIR.ExposedItem.Span, + module_name: Ident.Idx, +) void { + const exposed_items_slice = self.can_ir.store.sliceExposedItems(exposed_items_span); + + for (exposed_items_slice) |exposed_item_idx| { + const exposed_item = self.can_ir.store.getExposedItem(exposed_item_idx); + + // Use the alias if provided, otherwise use the original name for the local lookup + const item_name = exposed_item.alias orelse exposed_item.name; + + // Create the exposed item info with module name and original name + const item_info = Scope.ExposedItemInfo{ + .module_name = module_name, + .original_name = exposed_item.name, // Always use the original name for module lookup + }; + + // Introduce the exposed item into scope + // This allows `decode` to resolve to `json.Json.decode` + self.scopeIntroduceExposedItem(item_name, item_info); + } +} + fn canonicalize_decl( self: *Self, decl: AST.Statement.Decl, @@ -601,6 +798,43 @@ pub fn canonicalize_expr( .ident => |e| { const region = self.parse_ir.tokenizedRegionToRegion(e.region); if (self.parse_ir.tokens.resolveIdentifier(e.token)) |ident| { + // Check if this is a module-qualified identifier + if (e.qualifier) |qualifier_tok| { + if (self.parse_ir.tokens.resolveIdentifier(qualifier_tok)) |module_alias| { + // Check if this is a module alias + if (self.scopeLookupModule(module_alias)) |module_name| { + // This is a module-qualified lookup + // Create qualified name for external declaration + const module_text = self.can_ir.env.idents.getText(module_name); + const field_text = self.can_ir.env.idents.getText(ident); + + // Allocate space for qualified name + const qualified_text = std.fmt.allocPrint(self.can_ir.env.gpa, "{s}.{s}", .{ module_text, field_text }) catch |err| collections.utils.exitOnOom(err); + defer self.can_ir.env.gpa.free(qualified_text); + + const qualified_name = self.can_ir.env.idents.insert(self.can_ir.env.gpa, base.Ident.for_text(qualified_text), Region.zero()); + + // Create external declaration + const external_decl = CIR.ExternalDecl{ + .qualified_name = qualified_name, + .module_name = module_name, + .local_name = ident, + .type_var = self.can_ir.pushFreshTypeVar(@enumFromInt(0), region), + .kind = .value, + .region = region, + }; + + const external_idx = self.can_ir.pushExternalDecl(external_decl); + + // Create lookup expression for external declaration + const expr_idx = self.can_ir.store.addExpr(CIR.Expr{ .lookup = .{ .external = external_idx } }); + _ = self.can_ir.setTypeVarAtExpr(expr_idx, Content{ .flex_var = null }); + return expr_idx; + } + } + } + + // Not a module-qualified lookup, or qualifier not found, proceed with normal lookup switch (self.scopeLookup(&self.can_ir.env.idents, .ident, ident)) { .found => |pattern_idx| { // Mark this pattern as used for unused variable checking @@ -611,15 +845,38 @@ pub fn canonicalize_expr( // We found the ident in scope, lookup to reference the pattern const expr_idx = - self.can_ir.store.addExpr(CIR.Expr{ .lookup = .{ + self.can_ir.store.addExpr(CIR.Expr{ .lookup = .{ .local = .{ .pattern_idx = pattern_idx, .region = region, - } }); + } } }); _ = self.can_ir.setTypeVarAtExpr(expr_idx, Content{ .flex_var = null }); return expr_idx; }, .not_found => { - // We did not find the ident in scope + // Check if this identifier is an exposed item from an import + if (self.scopeLookupExposedItem(ident)) |exposed_info| { + // Create qualified name using the original name, not the alias + const qualified_name = self.createQualifiedName(exposed_info.module_name, exposed_info.original_name); + + // Create external declaration for the exposed item + const external_decl = CIR.ExternalDecl{ + .qualified_name = qualified_name, + .module_name = exposed_info.module_name, + .local_name = ident, + .type_var = self.can_ir.pushFreshTypeVar(@enumFromInt(0), region), + .kind = .value, + .region = region, + }; + + const external_idx = self.can_ir.pushExternalDecl(external_decl); + + // Create lookup expression for external declaration + const expr_idx = self.can_ir.store.addExpr(CIR.Expr{ .lookup = .{ .external = external_idx } }); + _ = self.can_ir.setTypeVarAtExpr(expr_idx, Content{ .flex_var = null }); + return expr_idx; + } + + // We did not find the ident in scope or as an exposed item return self.can_ir.pushMalformed(CIR.Expr.Idx, CIR.Diagnostic{ .ident_not_in_scope = .{ .ident = ident, .region = region, @@ -963,84 +1220,13 @@ pub fn canonicalize_expr( return expr_idx; }, .field_access => |field_access| { - // Canonicalize the receiver (left side of the dot) - const receiver_idx = blk: { - if (self.canonicalize_expr(field_access.left)) |idx| { - break :blk idx; - } else { - // Failed to canonicalize receiver, return malformed - const region = self.parse_ir.tokenizedRegionToRegion(field_access.region); - return self.can_ir.pushMalformed(CIR.Expr.Idx, CIR.Diagnostic{ .expr_not_canonicalized = .{ - .region = region, - } }); - } - }; - - // Parse the right side - this could be just a field name or a method call - const right_expr = self.parse_ir.store.getExpr(field_access.right); - - var field_name: Ident.Idx = undefined; - var args: ?CIR.Expr.Span = null; - - switch (right_expr) { - .apply => |apply| { - // This is a method call like .map(fn) - const method_expr = self.parse_ir.store.getExpr(apply.@"fn"); - switch (method_expr) { - .ident => |ident| { - // Get the method name - if (self.parse_ir.tokens.resolveIdentifier(ident.token)) |ident_idx| { - field_name = ident_idx; - } else { - // Fallback for malformed identifiers - field_name = self.can_ir.env.idents.insert(self.can_ir.env.gpa, base.Ident.for_text("unknown"), Region.zero()); - } - }, - else => { - // Fallback to a generic name - field_name = self.can_ir.env.idents.insert(self.can_ir.env.gpa, base.Ident.for_text("unknown"), Region.zero()); - }, - } - - // Canonicalize the arguments using scratch system - const scratch_top = self.can_ir.store.scratchExprTop(); - for (self.parse_ir.store.exprSlice(apply.args)) |arg_idx| { - if (self.canonicalize_expr(arg_idx)) |canonicalized| { - self.can_ir.store.addScratchExpr(canonicalized); - } else { - self.can_ir.store.clearScratchExprsFrom(scratch_top); - return null; - } - } - args = self.can_ir.store.exprSpanFrom(scratch_top); - }, - .ident => |ident| { - // Get the field name - if (self.parse_ir.tokens.resolveIdentifier(ident.token)) |ident_idx| { - field_name = ident_idx; - } else { - // Fallback for malformed identifiers - field_name = self.can_ir.env.idents.insert(self.can_ir.env.gpa, base.Ident.for_text("unknown"), Region.zero()); - } - }, - else => { - // Fallback - field_name = self.can_ir.env.idents.insert(self.can_ir.env.gpa, base.Ident.for_text("unknown"), Region.zero()); - }, + // Try module-qualified lookup first (e.g., Json.utf8) + if (self.tryModuleQualifiedLookup(field_access)) |expr_idx| { + return expr_idx; } - const dot_access_expr = CIR.Expr{ - .dot_access = .{ - .receiver = receiver_idx, - .field_name = field_name, - .args = args, - .region = self.parse_ir.tokenizedRegionToRegion(field_access.region), - }, - }; - - const expr_idx = self.can_ir.store.addExpr(dot_access_expr); - _ = self.can_ir.setTypeVarAtExpr(expr_idx, Content{ .flex_var = null }); - return expr_idx; + // Regular field access canonicalization + return self.canonicalizeRegularFieldAccess(field_access); }, .local_dispatch => |_| { const feature = self.can_ir.env.strings.insert(self.can_ir.env.gpa, "canonicalize local_dispatch expression"); @@ -2134,6 +2320,20 @@ fn canonicalize_statement(self: *Self, stmt_idx: AST.Statement.Idx) ?CIR.Expr.Id _ = self.can_ir.setTypeVarAtExpr(unit_expr, Content{ .flex_var = null }); return unit_expr; }, + .import => |import_stmt| { + _ = self.canonicalizeImportStatement(import_stmt); + + // Import statements don't produce runtime values, so return a unit expression + const region = self.parse_ir.tokenizedRegionToRegion(import_stmt.region); + const empty_span = CIR.Expr.Span{ .span = base.DataSpan{ .start = 0, .len = 0 } }; + const unit_expr = self.can_ir.store.addExpr(CIR.Expr{ .tuple = .{ + .tuple_var = self.can_ir.pushFreshTypeVar(@enumFromInt(0), region), + .elems = empty_span, + .region = region, + } }); + _ = self.can_ir.setTypeVarAtExpr(unit_expr, Content{ .flex_var = null }); + return unit_expr; + }, else => { // Other statement types not yet implemented const feature = self.can_ir.env.strings.insert(self.can_ir.env.gpa, "statement type in block"); @@ -2625,6 +2825,176 @@ fn scopeLookupTypeDecl(self: *const Self, name_ident: Ident.Idx) ?CIR.Statement. return null; } +/// Look up a module alias in the scope hierarchy +fn scopeLookupModule(self: *const Self, alias_name: Ident.Idx) ?Ident.Idx { + // Search from innermost to outermost scope + var i = self.scopes.items.len; + while (i > 0) { + i -= 1; + const scope = &self.scopes.items[i]; + + switch (scope.lookupModuleAlias(&self.can_ir.env.idents, alias_name)) { + .found => |module_name| return module_name, + .not_found => continue, + } + } + + return null; +} + +/// Introduce a module alias into scope +fn scopeIntroduceModuleAlias(self: *Self, alias_name: Ident.Idx, module_name: Ident.Idx) void { + const gpa = self.can_ir.env.gpa; + const current_scope = &self.scopes.items[self.scopes.items.len - 1]; + + // Simplified introduction without parent lookup for now + const result = current_scope.introduceModuleAlias(gpa, &self.can_ir.env.idents, alias_name, module_name, null); + + switch (result) { + .success => {}, + .shadowing_warning => |shadowed_module| { + // Create diagnostic for module alias shadowing + self.can_ir.pushDiagnostic(CIR.Diagnostic{ + .shadowing_warning = .{ + .ident = alias_name, + .region = Region.zero(), // TODO: get proper region + .original_region = Region.zero(), // TODO: get proper region + }, + }); + _ = shadowed_module; // Suppress unused variable warning + }, + .already_in_scope => |existing_module| { + // Module alias already exists in current scope + // For now, just issue a diagnostic + self.can_ir.pushDiagnostic(CIR.Diagnostic{ + .shadowing_warning = .{ + .ident = alias_name, + .region = Region.zero(), // TODO: get proper region + .original_region = Region.zero(), // TODO: get proper region + }, + }); + _ = existing_module; // Suppress unused variable warning + }, + } +} + +/// Helper function to look up module aliases in parent scopes only +fn scopeLookupModuleInParentScopes(self: *const Self, alias_name: Ident.Idx) ?Ident.Idx { + // Search from second-innermost to outermost scope (excluding current scope) + if (self.scopes.items.len <= 1) return null; + + var i = self.scopes.items.len - 1; + while (i > 0) { + i -= 1; + const scope = &self.scopes.items[i]; + + switch (scope.lookupModuleAlias(&self.can_ir.env.idents, alias_name)) { + .found => |module_name| return module_name, + .not_found => continue, + } + } + + return null; +} + +/// Look up an exposed item across all scopes +fn scopeLookupExposedItem(self: *const Self, item_name: Ident.Idx) ?Scope.ExposedItemInfo { + // Search from innermost to outermost scope + var i = self.scopes.items.len; + while (i > 0) { + i -= 1; + const scope = &self.scopes.items[i]; + + switch (scope.lookupExposedItem(&self.can_ir.env.idents, item_name)) { + .found => |item_info| return item_info, + .not_found => continue, + } + } + + return null; +} + +/// Introduce an exposed item into the current scope +fn scopeIntroduceExposedItem(self: *Self, item_name: Ident.Idx, item_info: Scope.ExposedItemInfo) void { + const gpa = self.can_ir.env.gpa; + const current_scope = &self.scopes.items[self.scopes.items.len - 1]; + + // Simplified introduction without parent lookup for now + const result = current_scope.introduceExposedItem(gpa, &self.can_ir.env.idents, item_name, item_info, null); + + switch (result) { + .success => {}, + .shadowing_warning => |shadowed_info| { + // Create diagnostic for exposed item shadowing + const item_text = self.can_ir.env.idents.getText(item_name); + const shadowed_module_text = self.can_ir.env.idents.getText(shadowed_info.module_name); + const current_module_text = self.can_ir.env.idents.getText(item_info.module_name); + + // For now, just add a simple diagnostic message + const message = std.fmt.allocPrint(gpa, "Exposed item '{s}' from module '{s}' shadows item from module '{s}'", .{ item_text, current_module_text, shadowed_module_text }) catch |err| collections.utils.exitOnOom(err); + const message_str = self.can_ir.env.strings.insert(gpa, message); + gpa.free(message); + + self.can_ir.pushDiagnostic(CIR.Diagnostic{ + .not_implemented = .{ + .feature = message_str, + .region = Region.zero(), // TODO: Get proper region from import statement + }, + }); + }, + .already_in_scope => |existing_info| { + // Create diagnostic for duplicate exposed item + const item_text = self.can_ir.env.idents.getText(item_name); + const existing_module_text = self.can_ir.env.idents.getText(existing_info.module_name); + const new_module_text = self.can_ir.env.idents.getText(item_info.module_name); + + const message = std.fmt.allocPrint(gpa, "Exposed item '{s}' already imported from module '{s}', cannot import again from module '{s}'", .{ item_text, existing_module_text, new_module_text }) catch |err| collections.utils.exitOnOom(err); + const message_str = self.can_ir.env.strings.insert(gpa, message); + gpa.free(message); + + self.can_ir.pushDiagnostic(CIR.Diagnostic{ + .not_implemented = .{ + .feature = message_str, + .region = Region.zero(), // TODO: Get proper region from import statement + }, + }); + }, + } +} + +/// Look up an exposed item in parent scopes (for shadowing detection) +fn scopeLookupExposedItemInParentScopes(self: *const Self, item_name: Ident.Idx) ?Scope.ExposedItemInfo { + // Search from second-innermost to outermost scope (excluding current scope) + if (self.scopes.items.len <= 1) return null; + + var i = self.scopes.items.len - 1; + while (i > 0) { + i -= 1; + const scope = &self.scopes.items[i]; + + switch (scope.lookupExposedItem(&self.can_ir.env.idents, item_name)) { + .found => |item_info| return item_info, + .not_found => continue, + } + } + + return null; +} + +/// Extract the module name from a full qualified name (e.g., "Json" from "json.Json") +fn extractModuleName(self: *Self, module_name_ident: Ident.Idx) Ident.Idx { + const module_text = self.can_ir.env.idents.getText(module_name_ident); + + // Find the last dot and extract the part after it + if (std.mem.lastIndexOf(u8, module_text, ".")) |last_dot_idx| { + const extracted_name = module_text[last_dot_idx + 1 ..]; + return self.can_ir.env.idents.insert(self.can_ir.env.gpa, base.Ident.for_text(extracted_name), Region.zero()); + } else { + // No dot found, return the original name + return module_name_ident; + } +} + /// Convert a parsed TypeAnno into a canonical TypeVar with appropriate Content fn canonicalizeTypeAnnoToTypeVar(self: *Self, type_anno_idx: CIR.TypeAnno.Idx, parent_node_idx: Node.Idx, region: Region) TypeVar { const type_anno = self.can_ir.store.getTypeAnno(type_anno_idx); @@ -2821,6 +3191,168 @@ fn createAnnotationFromTypeAnno(self: *Self, type_anno_idx: CIR.TypeAnno.Idx, _: return annotation_idx; } +/// Try to handle field access as a module-qualified lookup. +/// +/// Examples: +/// - `Json.utf8` where `Json` is a module alias and `utf8` is an exposed function +/// - `Http.get` where `Http` is imported and `get` is available in that module +/// +/// Returns `null` if this is not a module-qualified lookup (e.g., regular field access like `user.name`) +fn tryModuleQualifiedLookup(self: *Self, field_access: AST.BinOp) ?CIR.Expr.Idx { + const left_expr = self.parse_ir.store.getExpr(field_access.left); + if (left_expr != .ident) return null; + + const left_ident = left_expr.ident; + const module_alias = self.parse_ir.tokens.resolveIdentifier(left_ident.token) orelse return null; + + // Check if this is a module alias + const module_name = self.scopeLookupModule(module_alias) orelse return null; + + // This is a module-qualified lookup + const right_expr = self.parse_ir.store.getExpr(field_access.right); + if (right_expr != .ident) return null; + + const right_ident = right_expr.ident; + const field_name = self.parse_ir.tokens.resolveIdentifier(right_ident.token) orelse return null; + + // Create qualified name by slicing from original source text + // The field_access region covers the entire "Module.field" span + const region = self.parse_ir.tokenizedRegionToRegion(field_access.region); + const source_text = self.parse_ir.source[region.start.offset..region.end.offset]; + + const qualified_name = self.can_ir.env.idents.insert(self.can_ir.env.gpa, base.Ident.for_text(source_text), region); + + // Create external declaration + const external_decl = CIR.ExternalDecl{ + .qualified_name = qualified_name, + .module_name = module_name, + .local_name = field_name, + .type_var = self.can_ir.pushFreshTypeVar(@enumFromInt(0), region), + .kind = .value, + .region = region, + }; + + const external_idx = self.can_ir.pushExternalDecl(external_decl); + + // Create lookup expression for external declaration + const expr_idx = self.can_ir.store.addExpr(CIR.Expr{ .lookup = .{ .external = external_idx } }); + _ = self.can_ir.setTypeVarAtExpr(expr_idx, Content{ .flex_var = null }); + return expr_idx; +} + +/// Canonicalize regular field access (not module-qualified). +/// +/// Examples: +/// - `user.name` - accessing a field on a record +/// - `list.map(transform)` - calling a method with arguments +/// - `result.isOk` - accessing a field that might be a function +fn canonicalizeRegularFieldAccess(self: *Self, field_access: AST.BinOp) ?CIR.Expr.Idx { + // Canonicalize the receiver (left side of the dot) + const receiver_idx = self.canonicalizeFieldAccessReceiver(field_access) orelse return null; + + // Parse the right side - this could be just a field name or a method call + const field_name, const args = self.parseFieldAccessRight(field_access); + + const dot_access_expr = CIR.Expr{ + .dot_access = .{ + .receiver = receiver_idx, + .field_name = field_name, + .args = args, + .region = self.parse_ir.tokenizedRegionToRegion(field_access.region), + }, + }; + + const expr_idx = self.can_ir.store.addExpr(dot_access_expr); + _ = self.can_ir.setTypeVarAtExpr(expr_idx, Content{ .flex_var = null }); + return expr_idx; +} + +/// Canonicalize the receiver (left side) of field access. +/// +/// Examples: +/// - In `user.name`, canonicalizes `user` +/// - In `getUser().email`, canonicalizes `getUser()` +/// - In `[1,2,3].map(fn)`, canonicalizes `[1,2,3]` +fn canonicalizeFieldAccessReceiver(self: *Self, field_access: AST.BinOp) ?CIR.Expr.Idx { + if (self.canonicalize_expr(field_access.left)) |idx| { + return idx; + } else { + // Failed to canonicalize receiver, return malformed + const region = self.parse_ir.tokenizedRegionToRegion(field_access.region); + return self.can_ir.pushMalformed(CIR.Expr.Idx, CIR.Diagnostic{ .expr_not_canonicalized = .{ + .region = region, + } }); + } +} + +/// Parse the right side of field access, handling both plain fields and method calls. +/// +/// Examples: +/// - `user.name` - returns `("name", null)` for plain field access +/// - `list.map(fn)` - returns `("map", args)` where args contains the canonicalized function +/// - `obj.method(a, b)` - returns `("method", args)` where args contains canonicalized a and b +fn parseFieldAccessRight(self: *Self, field_access: AST.BinOp) struct { Ident.Idx, ?CIR.Expr.Span } { + const right_expr = self.parse_ir.store.getExpr(field_access.right); + + return switch (right_expr) { + .apply => |apply| self.parseMethodCall(apply), + .ident => |ident| .{ self.resolveIdentOrFallback(ident.token), null }, + else => .{ self.createUnknownIdent(), null }, + }; +} + +/// Parse a method call on the right side of field access. +/// +/// Examples: +/// - `.map(transform)` - extracts "map" as method name and canonicalizes `transform` argument +/// - `.filter(predicate)` - extracts "filter" and canonicalizes `predicate` +/// - `.fold(0, combine)` - extracts "fold" and canonicalizes both `0` and `combine` arguments +fn parseMethodCall(self: *Self, apply: @TypeOf(@as(AST.Expr, undefined).apply)) struct { Ident.Idx, ?CIR.Expr.Span } { + const method_expr = self.parse_ir.store.getExpr(apply.@"fn"); + const field_name = switch (method_expr) { + .ident => |ident| self.resolveIdentOrFallback(ident.token), + else => self.createUnknownIdent(), + }; + + // Canonicalize the arguments using scratch system + const scratch_top = self.can_ir.store.scratchExprTop(); + for (self.parse_ir.store.exprSlice(apply.args)) |arg_idx| { + if (self.canonicalize_expr(arg_idx)) |canonicalized| { + self.can_ir.store.addScratchExpr(canonicalized); + } else { + self.can_ir.store.clearScratchExprsFrom(scratch_top); + return .{ field_name, null }; + } + } + const args = self.can_ir.store.exprSpanFrom(scratch_top); + + return .{ field_name, args }; +} + +/// Resolve an identifier token or return a fallback "unknown" identifier. +/// +/// This helps maintain the "inform don't block" philosophy - even if we can't +/// resolve an identifier (due to malformed input), we continue compilation. +/// +/// Examples: +/// - Valid token for "name" -> returns the interned identifier for "name" +/// - Malformed/missing token -> returns identifier for "unknown" +fn resolveIdentOrFallback(self: *Self, token: Token.Idx) Ident.Idx { + if (self.parse_ir.tokens.resolveIdentifier(token)) |ident_idx| { + return ident_idx; + } else { + return self.createUnknownIdent(); + } +} + +/// Create an "unknown" identifier for fallback cases. +/// +/// Used when we encounter malformed or unexpected syntax but want to continue +/// compilation instead of stopping. This supports the compiler's "inform don't block" approach. +fn createUnknownIdent(self: *Self) Ident.Idx { + return self.can_ir.env.idents.insert(self.can_ir.env.gpa, base.Ident.for_text("unknown"), Region.zero()); +} + /// Context helper for Scope tests const ScopeTestContext = struct { self: Self, diff --git a/src/check/canonicalize/CIR.zig b/src/check/canonicalize/CIR.zig index fe967c3114..3c50dd99c0 100644 --- a/src/check/canonicalize/CIR.zig +++ b/src/check/canonicalize/CIR.zig @@ -45,6 +45,8 @@ temp_source_for_sexpr: ?[]const u8 = null, all_defs: Def.Span, /// All the top-level statements in the module, populated by calling `canonicalize_file` all_statements: Statement.Span, +/// All external declarations referenced in this module +external_decls: std.ArrayList(ExternalDecl), /// Initialize the IR for a module's canonicalization info. /// @@ -66,12 +68,14 @@ pub fn init(env: *ModuleEnv) CIR { .store = NodeStore.initCapacity(env.gpa, NODE_STORE_CAPACITY), .all_defs = .{ .span = .{ .start = 0, .len = 0 } }, .all_statements = .{ .span = .{ .start = 0, .len = 0 } }, + .external_decls = std.ArrayList(ExternalDecl).init(env.gpa), }; } /// Deinit the IR's memory. pub fn deinit(self: *CIR) void { self.store.deinit(); + self.external_decls.deinit(); } /// Records a diagnostic error during canonicalization without blocking compilation. @@ -346,6 +350,32 @@ pub fn setTypeVarAt(self: *CIR, at_idx: Node.Idx, content: types.Content) types. return var_; } +/// Adds an external declaration to the CIR and returns its index +pub fn pushExternalDecl(self: *CIR, decl: ExternalDecl) ExternalDecl.Idx { + const idx = @as(u32, @intCast(self.external_decls.items.len)); + self.external_decls.append(decl) catch |err| exitOnOom(err); + return @enumFromInt(idx); +} + +/// Retrieves an external declaration by its index +pub fn getExternalDecl(self: *const CIR, idx: ExternalDecl.Idx) *const ExternalDecl { + return &self.external_decls.items[@intFromEnum(idx)]; +} + +/// Adds multiple external declarations and returns a span +pub fn pushExternalDecls(self: *CIR, decls: []const ExternalDecl) ExternalDecl.Span { + const start = @as(u32, @intCast(self.external_decls.items.len)); + for (decls) |decl| { + self.external_decls.append(decl) catch |err| exitOnOom(err); + } + return .{ .span = .{ .start = start, .len = @as(u32, @intCast(decls.len)) } }; +} + +/// Gets a slice of external declarations from a span +pub fn sliceExternalDecls(self: *const CIR, span: ExternalDecl.Span) []const ExternalDecl { + return self.external_decls.items[span.span.start .. span.span.start + span.span.len]; +} + // Helper to add type index info to a s-expr node fn appendTypeVar(node: *sexpr.Expr, gpa: std.mem.Allocator, name: []const u8, type_idx: TypeVar) void { var type_node = sexpr.Expr.init(gpa, name); @@ -591,9 +621,10 @@ pub const Statement = union(enum) { var exposes_node = sexpr.Expr.init(gpa, "exposes"); const exposes_slice = ir.store.sliceExposedItems(s.exposes); - for (exposes_slice) |_| { - // TODO: Implement ExposedItem.toSExpr when ExposedItem structure is complete - exposes_node.appendString(gpa, "exposed_item"); + for (exposes_slice) |exposed_idx| { + const exposed_item = ir.store.getExposedItem(exposed_idx); + const exposed_sexpr = exposed_item.toSExpr(gpa, ir.env); + exposes_node.appendNode(gpa, &exposed_sexpr); } node.appendNode(gpa, &exposes_node); @@ -1024,6 +1055,27 @@ pub const ExposedItem = struct { pub const Idx = enum(u32) { _ }; pub const Span = struct { span: DataSpan }; + + pub fn toSExpr(self: ExposedItem, gpa: std.mem.Allocator, env: *const ModuleEnv) sexpr.Expr { + var node = sexpr.Expr.init(gpa, "exposed_item"); + + // Add the original name + const name_text = env.idents.getText(self.name); + node.appendString(gpa, name_text); + + // Add the alias if present + if (self.alias) |alias_idx| { + const alias_text = env.idents.getText(alias_idx); + node.appendString(gpa, alias_text); + } + + // Add wildcard indicator if needed + if (self.is_wildcard) { + node.appendString(gpa, "wildcard"); + } + + return node; + } }; /// An expression that has been canonicalized. @@ -1071,7 +1123,10 @@ pub const Expr = union(enum) { bound: types.Num.Int.Precision, region: Region, }, - lookup: Lookup, + lookup: union(enum) { + local: Lookup, + external: ExternalDecl.Idx, + }, // TODO introduce a new node for re-assign here, used by Var instead of lookup list: struct { elem_var: TypeVar, @@ -1212,7 +1267,14 @@ pub const Expr = union(enum) { .str_segment => |e| return e.region, .str => |e| return e.region, .single_quote => |e| return e.region, - .lookup => |e| return e.region, + .lookup => |e| switch (e) { + .local => |local| return local.region, + .external => |_| { + // External lookups don't have a direct region access from Expr context + // The region should be handled where the CIR context is available + return null; + }, + }, .list => |e| return e.region, .tuple => |e| return e.region, .when => |e| return e.region, @@ -1419,13 +1481,26 @@ pub const Expr = union(enum) { return tuple_node; }, .lookup => |l| { - var lookup_node = sexpr.Expr.init(gpa, "e_lookup"); - lookup_node.appendRegionInfo(gpa, ir.calcRegionInfo(l.region)); + switch (l) { + .local => |local| { + var lookup_node = sexpr.Expr.init(gpa, "e_lookup_local"); + lookup_node.appendRegionInfo(gpa, ir.calcRegionInfo(local.region)); - var pattern_idx = formatPatternIdxNode(gpa, l.pattern_idx); - lookup_node.appendNode(gpa, &pattern_idx); + var pattern_idx = formatPatternIdxNode(gpa, local.pattern_idx); + lookup_node.appendNode(gpa, &pattern_idx); - return lookup_node; + return lookup_node; + }, + .external => |external_idx| { + var lookup_node = sexpr.Expr.init(gpa, "e_lookup_external"); + + const external_decl = ir.getExternalDecl(external_idx); + var external_sexpr = external_decl.toSExpr(ir, env); + lookup_node.appendNode(gpa, &external_sexpr); + + return lookup_node; + }, + } }, .when => |e| { var when_branch_node = sexpr.Expr.init(gpa, "e_when"); @@ -1858,6 +1933,73 @@ pub const Annotation = struct { } }; +/// External declaration node for cross-module references +/// +/// This node represents a reference to a declaration from another module +/// that hasn't been resolved yet. It serves as a placeholder during +/// canonicalization that will be populated with type information +/// later during dependency resolution. +pub const ExternalDecl = struct { + /// Fully qualified name (e.g., "json.Json.utf8") + qualified_name: Ident.Idx, + + /// Module this decl comes from (e.g., "json.Json") + module_name: Ident.Idx, + + /// Local name within that module (e.g., "utf8") + local_name: Ident.Idx, + + /// Type information (populated later after dependency resolution) + type_var: TypeVar, + + /// Whether this is a value or type declaration + kind: enum { value, type }, + + /// Region where this was referenced + region: Region, + + pub const Idx = enum(u32) { _ }; + pub const Span = struct { span: DataSpan }; + + pub fn toSExpr(self: *const @This(), ir: *const CIR, env: *ModuleEnv) sexpr.Expr { + const gpa = ir.env.gpa; + var node = sexpr.Expr.init(gpa, "external_decl"); + node.appendRegionInfo(gpa, ir.calcRegionInfo(self.region)); + + // Add qualified name + var qualified_name_node = sexpr.Expr.init(gpa, "qualified_name"); + const qualified_name_str = env.idents.getText(self.qualified_name); + qualified_name_node.appendString(gpa, qualified_name_str); + node.appendNode(gpa, &qualified_name_node); + + // Add module name + var module_name_node = sexpr.Expr.init(gpa, "module_name"); + const module_name_str = env.idents.getText(self.module_name); + module_name_node.appendString(gpa, module_name_str); + node.appendNode(gpa, &module_name_node); + + // Add local name + var local_name_node = sexpr.Expr.init(gpa, "local_name"); + const local_name_str = env.idents.getText(self.local_name); + local_name_node.appendString(gpa, local_name_str); + node.appendNode(gpa, &local_name_node); + + // Add kind + var kind_node = sexpr.Expr.init(gpa, "kind"); + const kind_str = switch (self.kind) { + .value => "value", + .type => "type", + }; + kind_node.appendString(gpa, kind_str); + node.appendNode(gpa, &kind_node); + + // Add type variable info + appendTypeVar(&node, gpa, "type_var", self.type_var); + + return node; + } +}; + /// Tracks type variables introduced during annotation canonicalization pub const IntroducedVariables = struct { /// Named type variables (e.g., 'a' in 'a -> a') diff --git a/src/check/canonicalize/Node.zig b/src/check/canonicalize/Node.zig index 9eb45544cf..e9c7a99cd8 100644 --- a/src/check/canonicalize/Node.zig +++ b/src/check/canonicalize/Node.zig @@ -44,6 +44,7 @@ pub const Tag = enum { expr_record, expr_field_access, expr_static_dispatch, + expr_external_lookup, expr_dot_access, expr_apply, expr_string, @@ -94,6 +95,8 @@ pub const Tag = enum { pattern_underscore, // Definitions def, + // Exposed Items + exposed_item, // todo -- put me somewhere and rename maybe if_branch, diff --git a/src/check/canonicalize/NodeStore.zig b/src/check/canonicalize/NodeStore.zig index 7323ffff0b..3b75e7c30f 100644 --- a/src/check/canonicalize/NodeStore.zig +++ b/src/check/canonicalize/NodeStore.zig @@ -168,10 +168,14 @@ pub fn getExpr(store: *const NodeStore, expr: CIR.Expr.Idx) CIR.Expr { switch (node.tag) { .expr_var => { - return CIR.Expr{ .lookup = .{ + return CIR.Expr{ .lookup = .{ .local = .{ .pattern_idx = @enumFromInt(node.data_1), .region = node.region, - } }; + } } }; + }, + .expr_external_lookup => { + // Handle external lookups + return CIR.Expr{ .lookup = .{ .external = @enumFromInt(node.data_1) } }; }, .expr_int => { // Retrieve the literal index from data_1 @@ -580,9 +584,19 @@ pub fn getAnnotation(store: *NodeStore, annotation: CIR.Annotation.Idx) CIR.Anno /// Retrieves an exposed item from the store. pub fn getExposedItem(store: *NodeStore, exposedItem: CIR.ExposedItem.Idx) CIR.ExposedItem { - _ = store; - _ = exposedItem; - @panic("TODO: implement getExposedItem"); + const node_idx: Node.Idx = @enumFromInt(@intFromEnum(exposedItem)); + const node = store.nodes.get(node_idx); + + switch (node.tag) { + .exposed_item => { + return CIR.ExposedItem{ + .name = @bitCast(node.data_1), + .alias = if (node.data_2 == 0) null else @bitCast(node.data_2), + .is_wildcard = node.data_3 != 0, + }; + }, + else => std.debug.panic("Expected exposed_item node, got {s}\n", .{@tagName(node.tag)}), + } } /// Adds a statement node to the store. @@ -689,9 +703,20 @@ pub fn addExpr(store: *NodeStore, expr: CIR.Expr) CIR.Expr.Idx { switch (expr) { .lookup => |e| { - node.region = e.region; - node.tag = .expr_var; - node.data_1 = @intFromEnum(e.pattern_idx); + switch (e) { + .local => |local| { + node.region = local.region; + node.tag = .expr_var; + node.data_1 = @intFromEnum(local.pattern_idx); + }, + .external => |external_idx| { + // For external lookups, store the external decl index + // Use external lookup tag to distinguish from local lookups + node.region = base.Region.zero(); + node.tag = .expr_external_lookup; + node.data_1 = @intFromEnum(external_idx); + }, + } }, .int => |e| { node.region = e.region; @@ -1089,16 +1114,16 @@ pub fn addAnnotation(store: *NodeStore, annotation: CIR.Annotation) CIR.Annotati /// Adds an exposed item to the store. pub fn addExposedItem(store: *NodeStore, exposedItem: CIR.ExposedItem) CIR.ExposedItem.Idx { - const node = Node{}; - - switch (exposedItem) { - else => { - std.debug.panic("Exposed Item of type {s} not yet implemented in Can\n", .{@tagName(exposedItem)}); - }, - } + const node = Node{ + .data_1 = @bitCast(exposedItem.name), + .data_2 = if (exposedItem.alias) |alias| @bitCast(alias) else 0, + .data_3 = if (exposedItem.is_wildcard) 1 else 0, + .region = base.Region.zero(), + .tag = .exposed_item, + }; const nid = store.nodes.append(store.gpa, node); - return @enumFromInt(nid); + return @enumFromInt(@intFromEnum(nid)); } /// Adds a definition to the store. @@ -1294,6 +1319,34 @@ pub fn annoRecordFieldSpanFrom(store: *NodeStore, start: u32) CIR.AnnoRecordFiel return .{ .span = .{ .start = ed_start, .len = @as(u32, @intCast(end)) - start } }; } +/// Returns the current top of the scratch exposed items buffer. +pub fn scratchExposedItemTop(store: *NodeStore) u32 { + return store.scratch_exposed_items.top(); +} + +/// Adds an exposed item to the scratch buffer. +pub fn addScratchExposedItem(store: *NodeStore, idx: CIR.ExposedItem.Idx) void { + store.scratch_exposed_items.append(store.gpa, idx); +} + +/// Creates a span from the scratch exposed items starting at the given index. +pub fn exposedItemSpanFrom(store: *NodeStore, start: u32) CIR.ExposedItem.Span { + const end = store.scratch_exposed_items.top(); + defer store.scratch_exposed_items.clearFrom(start); + var i = @as(usize, @intCast(start)); + const ed_start = @as(u32, @intCast(store.extra_data.items.len)); + while (i < end) { + store.extra_data.append(store.gpa, @intFromEnum(store.scratch_exposed_items.items.items[i])) catch |err| exitOnOom(err); + i += 1; + } + return .{ .span = .{ .start = ed_start, .len = @as(u32, @intCast(end)) - start } }; +} + +/// Clears scratch exposed items from the given index. +pub fn clearScratchExposedItemsFrom(store: *NodeStore, start: u32) void { + store.scratch_exposed_items.clearFrom(start); +} + /// Returns the start position for a new Span of annoRecordFieldIdxs in scratch pub fn scratchAnnoRecordFieldTop(store: *NodeStore) u32 { return store.scratch_anno_record_fields.top(); diff --git a/src/check/canonicalize/Scope.zig b/src/check/canonicalize/Scope.zig index 3e47d6dad7..4b3f4a60d6 100644 --- a/src/check/canonicalize/Scope.zig +++ b/src/check/canonicalize/Scope.zig @@ -16,6 +16,10 @@ aliases: std.AutoHashMapUnmanaged(Ident.Idx, CIR.Pattern.Idx), type_decls: std.AutoHashMapUnmanaged(Ident.Idx, CIR.Statement.Idx), /// Maps type variables to their type annotation indices type_vars: std.AutoHashMapUnmanaged(Ident.Idx, CIR.TypeAnno.Idx), +/// Maps module alias names to their full module names +module_aliases: std.AutoHashMapUnmanaged(Ident.Idx, Ident.Idx), +/// Maps exposed item names to their source modules and original names (for import resolution) +exposed_items: std.AutoHashMapUnmanaged(Ident.Idx, ExposedItemInfo), is_function_boundary: bool, /// Initialize the scope @@ -25,6 +29,8 @@ pub fn init(is_function_boundary: bool) Scope { .aliases = std.AutoHashMapUnmanaged(Ident.Idx, CIR.Pattern.Idx){}, .type_decls = std.AutoHashMapUnmanaged(Ident.Idx, CIR.Statement.Idx){}, .type_vars = std.AutoHashMapUnmanaged(Ident.Idx, CIR.TypeAnno.Idx){}, + .module_aliases = std.AutoHashMapUnmanaged(Ident.Idx, Ident.Idx){}, + .exposed_items = std.AutoHashMapUnmanaged(Ident.Idx, ExposedItemInfo){}, .is_function_boundary = is_function_boundary, }; } @@ -35,6 +41,8 @@ pub fn deinit(self: *Scope, gpa: std.mem.Allocator) void { self.aliases.deinit(gpa); self.type_decls.deinit(gpa); self.type_vars.deinit(gpa); + self.module_aliases.deinit(gpa); + self.exposed_items.deinit(gpa); } /// Scope management types and structures @@ -64,6 +72,24 @@ pub const TypeVarLookupResult = union(enum) { not_found: void, }; +/// Result of looking up a module alias +pub const ModuleAliasLookupResult = union(enum) { + found: Ident.Idx, + not_found: void, +}; + +/// Information about an exposed item +pub const ExposedItemInfo = struct { + module_name: Ident.Idx, + original_name: Ident.Idx, +}; + +/// Result of looking up an exposed item +pub const ExposedItemLookupResult = union(enum) { + found: ExposedItemInfo, + not_found: void, +}; + /// Result of introducing an identifier pub const IntroduceResult = union(enum) { success: void, @@ -93,20 +119,38 @@ pub const TypeVarIntroduceResult = union(enum) { already_in_scope: CIR.TypeAnno.Idx, // The type variable already exists in this scope }; +/// Result of introducing a module alias +pub const ModuleAliasIntroduceResult = union(enum) { + success: void, + shadowing_warning: Ident.Idx, // The module alias that was shadowed + already_in_scope: Ident.Idx, // The module alias already exists in this scope +}; + +/// Result of introducing an exposed item +pub const ExposedItemIntroduceResult = union(enum) { + success: void, + shadowing_warning: ExposedItemInfo, // The exposed item that was shadowed + already_in_scope: ExposedItemInfo, // The exposed item already exists in this scope +}; + /// Item kinds in a scope -pub const ItemKind = enum { ident, alias, type_decl, type_var }; +pub const ItemKind = enum { ident, alias, type_decl, type_var, module_alias, exposed_item }; /// Get the appropriate map for the given item kind pub fn items(scope: *Scope, comptime item_kind: ItemKind) switch (item_kind) { .ident, .alias => *std.AutoHashMapUnmanaged(Ident.Idx, CIR.Pattern.Idx), .type_decl => *std.AutoHashMapUnmanaged(Ident.Idx, CIR.Statement.Idx), .type_var => *std.AutoHashMapUnmanaged(Ident.Idx, CIR.TypeAnno.Idx), + .module_alias => *std.AutoHashMapUnmanaged(Ident.Idx, Ident.Idx), + .exposed_item => *std.AutoHashMapUnmanaged(Ident.Idx, ExposedItemInfo), } { return switch (item_kind) { .ident => &scope.idents, .alias => &scope.aliases, .type_decl => &scope.type_decls, .type_var => &scope.type_vars, + .module_alias => &scope.module_aliases, + .exposed_item => &scope.exposed_items, }; } @@ -115,12 +159,16 @@ pub fn itemsConst(scope: *const Scope, comptime item_kind: ItemKind) switch (ite .ident, .alias => *const std.AutoHashMapUnmanaged(Ident.Idx, CIR.Pattern.Idx), .type_decl => *const std.AutoHashMapUnmanaged(Ident.Idx, CIR.Statement.Idx), .type_var => *const std.AutoHashMapUnmanaged(Ident.Idx, CIR.TypeAnno.Idx), + .module_alias => *const std.AutoHashMapUnmanaged(Ident.Idx, Ident.Idx), + .exposed_item => *const std.AutoHashMapUnmanaged(Ident.Idx, ExposedItemInfo), } { return switch (item_kind) { .ident => &scope.idents, .alias => &scope.aliases, .type_decl => &scope.type_decls, .type_var => &scope.type_vars, + .module_alias => &scope.module_aliases, + .exposed_item => &scope.exposed_items, }; } @@ -129,6 +177,8 @@ pub fn put(scope: *Scope, gpa: std.mem.Allocator, comptime item_kind: ItemKind, .ident, .alias => CIR.Pattern.Idx, .type_decl => CIR.Statement.Idx, .type_var => CIR.TypeAnno.Idx, + .module_alias => Ident.Idx, + .exposed_item => ExposedItemInfo, }) void { scope.items(item_kind).put(gpa, name, value) catch |err| collections.utils.exitOnOom(err); } @@ -225,3 +275,93 @@ pub fn lookupTypeVar(scope: *const Scope, ident_store: *const base.Ident.Store, } return TypeVarLookupResult{ .not_found = {} }; } + +/// Look up a module alias in this scope +pub fn lookupModuleAlias(scope: *const Scope, ident_store: *const base.Ident.Store, name: Ident.Idx) ModuleAliasLookupResult { + // Search by comparing text content, not identifier index + var iter = scope.module_aliases.iterator(); + while (iter.next()) |entry| { + if (ident_store.identsHaveSameText(name, entry.key_ptr.*)) { + return ModuleAliasLookupResult{ .found = entry.value_ptr.* }; + } + } + return ModuleAliasLookupResult{ .not_found = {} }; +} + +/// Introduce a module alias into this scope +pub fn introduceModuleAlias( + scope: *Scope, + gpa: std.mem.Allocator, + ident_store: *const base.Ident.Store, + alias_name: Ident.Idx, + module_name: Ident.Idx, + parent_lookup_fn: ?fn (Ident.Idx) ?Ident.Idx, +) ModuleAliasIntroduceResult { + // Check if already exists in current scope by comparing text content + var iter = scope.module_aliases.iterator(); + while (iter.next()) |entry| { + if (ident_store.identsHaveSameText(alias_name, entry.key_ptr.*)) { + // Module alias already exists in this scope + return ModuleAliasIntroduceResult{ .already_in_scope = entry.value_ptr.* }; + } + } + + // Check for shadowing in parent scopes + var shadowed_module: ?Ident.Idx = null; + if (parent_lookup_fn) |lookup_fn| { + shadowed_module = lookup_fn(alias_name); + } + + scope.put(gpa, .module_alias, alias_name, module_name); + + if (shadowed_module) |module| { + return ModuleAliasIntroduceResult{ .shadowing_warning = module }; + } + + return ModuleAliasIntroduceResult{ .success = {} }; +} + +/// Look up an exposed item in this scope +pub fn lookupExposedItem(scope: *const Scope, ident_store: *const base.Ident.Store, name: Ident.Idx) ExposedItemLookupResult { + // Search by comparing text content, not identifier index + var iter = scope.exposed_items.iterator(); + while (iter.next()) |entry| { + if (ident_store.identsHaveSameText(name, entry.key_ptr.*)) { + return ExposedItemLookupResult{ .found = entry.value_ptr.* }; + } + } + return ExposedItemLookupResult{ .not_found = {} }; +} + +/// Introduce an exposed item into this scope +pub fn introduceExposedItem( + scope: *Scope, + gpa: std.mem.Allocator, + ident_store: *const base.Ident.Store, + item_name: Ident.Idx, + item_info: ExposedItemInfo, + parent_lookup_fn: ?fn (Ident.Idx) ?ExposedItemInfo, +) ExposedItemIntroduceResult { + // Check if already exists in current scope by comparing text content + var iter = scope.exposed_items.iterator(); + while (iter.next()) |entry| { + if (ident_store.identsHaveSameText(item_name, entry.key_ptr.*)) { + // Exposed item already exists in this scope + return ExposedItemIntroduceResult{ .already_in_scope = entry.value_ptr.* }; + } + } + + // Check for shadowing in parent scopes + var shadowed_info: ?ExposedItemInfo = null; + if (parent_lookup_fn) |lookup_fn| { + shadowed_info = lookup_fn(item_name); + } + + scope.put(gpa, .exposed_item, item_name, item_info); + + if (shadowed_info) |info| { + return ExposedItemIntroduceResult{ .shadowing_warning = info }; + } + + return ExposedItemIntroduceResult{ .success = {} }; +} diff --git a/src/check/parse/Parser.zig b/src/check/parse/Parser.zig index b31685c28c..830aa39c85 100644 --- a/src/check/parse/Parser.zig +++ b/src/check/parse/Parser.zig @@ -875,6 +875,7 @@ fn parseStmtByType(self: *Parser, statementType: StatementType) ?AST.Statement.I var exposes = AST.ExposedItem.Span{ .span = base.DataSpan.empty() }; const module_name_tok = self.pos; var end = self.pos; + // Handle 'as' clause if present if (self.peekNext() == .KwAs) { self.advance(); // Advance past UpperIdent self.advance(); // Advance past KwAs @@ -885,8 +886,12 @@ fn parseStmtByType(self: *Parser, statementType: StatementType) ?AST.Statement.I self.advance(); return malformed; }; - } else if (self.peekNext() == .KwExposing) { - self.advance(); // Advance past ident + } else { + self.advance(); // Advance past identifier + } + + // Handle 'exposing' clause if present (can occur with or without 'as') + if (self.peek() == .KwExposing) { self.advance(); // Advance past KwExposing self.expect(.OpenSquare) catch { return self.pushMalformed(AST.Statement.Idx, .import_exposing_no_open, start); @@ -901,8 +906,6 @@ fn parseStmtByType(self: *Parser, statementType: StatementType) ?AST.Statement.I return self.pushMalformed(AST.Statement.Idx, .import_exposing_no_close, start); }; exposes = self.store.exposedItemSpanFrom(scratch_top); - } else { - self.advance(); // Advance past identifier } const statement_idx = self.store.addStatement(.{ .import = .{ .module_name_tok = module_name_tok, diff --git a/src/snapshots/can_basic_scoping.md b/src/snapshots/can_basic_scoping.md index 12737532a4..0e4f14a075 100644 --- a/src/snapshots/can_basic_scoping.md +++ b/src/snapshots/can_basic_scoping.md @@ -170,18 +170,18 @@ outerFunc = |_| { (ident "z")) (e_binop (12:13-13:10) "add" - (e_lookup (12:13-12:14) (pid 84)) - (e_lookup (12:17-12:18) (pid 77)))) + (e_lookup_local (12:13-12:14) (pid 84)) + (e_lookup_local (12:17-12:18) (pid 77)))) (e_binop (13:9-14:6) "add" - (e_lookup (13:9-13:10) (pid 91)) + (e_lookup_local (13:9-13:10) (pid 91)) (e_int (13:13-13:14) (int_var 98) (precision_var 97) (literal "1") (value "TODO") (bound "u8"))))) - (e_lookup (15:5-15:16) (pid 90))))))) + (e_lookup_local (15:5-15:16) (pid 90))))))) ~~~ # TYPES ~~~clojure diff --git a/src/snapshots/can_dot_access_with_vars.md b/src/snapshots/can_dot_access_with_vars.md index c9ab18924b..06e06f1fc0 100644 --- a/src/snapshots/can_dot_access_with_vars.md +++ b/src/snapshots/can_dot_access_with_vars.md @@ -94,7 +94,7 @@ CloseCurly(5:1-5:2),EndOfFile(5:2-5:2), (ident "x"))) (e_binop (3:14-4:9) "add" - (e_lookup (3:14-3:15) (pid 86)) + (e_lookup_local (3:14-3:15) (pid 86)) (e_int (3:18-3:19) (int_var 89) (precision_var 88) @@ -102,9 +102,9 @@ CloseCurly(5:1-5:2),EndOfFile(5:2-5:2), (value "TODO") (bound "u8"))))) (e_dot_access (4:5-5:2) - (e_lookup (4:5-4:9) (pid 72)) + (e_lookup_local (4:5-4:9) (pid 72)) "map" - (e_lookup (4:14-4:16) (pid 85)))) + (e_lookup_local (4:14-4:16) (pid 85)))) ~~~ # TYPES ~~~clojure diff --git a/src/snapshots/can_import_comprehensive.md b/src/snapshots/can_import_comprehensive.md new file mode 100644 index 0000000000..e21311bc26 --- /dev/null +++ b/src/snapshots/can_import_comprehensive.md @@ -0,0 +1,309 @@ +# META +~~~ini +description=Comprehensive import test with various module access patterns +type=file +~~~ +# SOURCE +~~~roc +module [] + +import json.Json +import http.Client as Http exposing [get, post] +import utils.String as Str + +main = { + client = Http.get + parser = Json.utf8 + helper = Str.trim + + # Test direct module access + result1 = Json.parse + + # Test aliased module access + result2 = Http.post + + # Test exposed items (should work without module prefix) + result3 = get + result4 = post + + # Test multiple qualified access + combined = Str.concat + + ( + client, + parser, + helper, + result1, + result2, + result3, + result4, + combined, + ) +} +~~~ +# PROBLEMS +NIL +# TOKENS +~~~zig +KwModule(1:1-1:7),OpenSquare(1:8-1:9),CloseSquare(1:9-1:10),Newline(1:1-1:1), +Newline(1:1-1:1), +KwImport(3:1-3:7),LowerIdent(3:8-3:12),NoSpaceDotUpperIdent(3:12-3:17),Newline(1:1-1:1), +KwImport(4:1-4:7),LowerIdent(4:8-4:12),NoSpaceDotUpperIdent(4:12-4:19),KwAs(4:20-4:22),UpperIdent(4:23-4:27),KwExposing(4:28-4:36),OpenSquare(4:37-4:38),LowerIdent(4:38-4:41),Comma(4:41-4:42),LowerIdent(4:43-4:47),CloseSquare(4:47-4:48),Newline(1:1-1:1), +KwImport(5:1-5:7),LowerIdent(5:8-5:13),NoSpaceDotUpperIdent(5:13-5:20),KwAs(5:21-5:23),UpperIdent(5:24-5:27),Newline(1:1-1:1), +Newline(1:1-1:1), +LowerIdent(7:1-7:5),OpAssign(7:6-7:7),OpenCurly(7:8-7:9),Newline(1:1-1:1), +LowerIdent(8:5-8:11),OpAssign(8:12-8:13),UpperIdent(8:14-8:18),NoSpaceDotLowerIdent(8:18-8:22),Newline(1:1-1:1), +LowerIdent(9:5-9:11),OpAssign(9:12-9:13),UpperIdent(9:14-9:18),NoSpaceDotLowerIdent(9:18-9:23),Newline(1:1-1:1), +LowerIdent(10:5-10:11),OpAssign(10:12-10:13),UpperIdent(10:14-10:17),NoSpaceDotLowerIdent(10:17-10:22),Newline(1:1-1:1), +Newline(1:1-1:1), +Newline(12:6-12:32), +LowerIdent(13:5-13:12),OpAssign(13:13-13:14),UpperIdent(13:15-13:19),NoSpaceDotLowerIdent(13:19-13:25),Newline(1:1-1:1), +Newline(1:1-1:1), +Newline(15:6-15:33), +LowerIdent(16:5-16:12),OpAssign(16:13-16:14),UpperIdent(16:15-16:19),NoSpaceDotLowerIdent(16:19-16:24),Newline(1:1-1:1), +Newline(1:1-1:1), +Newline(18:6-18:61), +LowerIdent(19:5-19:12),OpAssign(19:13-19:14),LowerIdent(19:15-19:18),Newline(1:1-1:1), +LowerIdent(20:5-20:12),OpAssign(20:13-20:14),LowerIdent(20:15-20:19),Newline(1:1-1:1), +Newline(1:1-1:1), +Newline(22:6-22:37), +LowerIdent(23:5-23:13),OpAssign(23:14-23:15),UpperIdent(23:16-23:19),NoSpaceDotLowerIdent(23:19-23:26),Newline(1:1-1:1), +Newline(1:1-1:1), +OpenRound(25:5-25:6),Newline(1:1-1:1), +LowerIdent(26:9-26:15),Comma(26:15-26:16),Newline(1:1-1:1), +LowerIdent(27:9-27:15),Comma(27:15-27:16),Newline(1:1-1:1), +LowerIdent(28:9-28:15),Comma(28:15-28:16),Newline(1:1-1:1), +LowerIdent(29:9-29:16),Comma(29:16-29:17),Newline(1:1-1:1), +LowerIdent(30:9-30:16),Comma(30:16-30:17),Newline(1:1-1:1), +LowerIdent(31:9-31:16),Comma(31:16-31:17),Newline(1:1-1:1), +LowerIdent(32:9-32:16),Comma(32:16-32:17),Newline(1:1-1:1), +LowerIdent(33:9-33:17),Comma(33:17-33:18),Newline(1:1-1:1), +CloseRound(34:5-34:6),Newline(1:1-1:1), +CloseCurly(35:1-35:2),EndOfFile(35:2-35:2), +~~~ +# PARSE +~~~clojure +(file (1:1-35:2) + (module (1:1-1:10) (exposes (1:8-1:10))) + (statements + (import (3:1-3:17) ".Json" (qualifier "json")) + (import (4:1-4:48) + ".Client" + (qualifier "http") + (alias "Http") + (exposing + (exposed_item (lower_ident "get")) + (exposed_item (lower_ident "post")))) + (import (5:1-5:27) + ".String" + (qualifier "utils") + (alias "Str")) + (decl (7:1-35:2) + (ident (7:1-7:5) "main") + (block (7:8-35:2) + (statements + (decl (8:5-8:22) + (ident (8:5-8:11) "client") + (ident (8:14-8:22) "Http" ".get")) + (decl (9:5-9:23) + (ident (9:5-9:11) "parser") + (ident (9:14-9:23) "Json" ".utf8")) + (decl (10:5-10:22) + (ident (10:5-10:11) "helper") + (ident (10:14-10:22) "Str" ".trim")) + (decl (13:5-13:25) + (ident (13:5-13:12) "result1") + (ident (13:15-13:25) "Json" ".parse")) + (decl (16:5-16:24) + (ident (16:5-16:12) "result2") + (ident (16:15-16:24) "Http" ".post")) + (decl (19:5-19:18) + (ident (19:5-19:12) "result3") + (ident (19:15-19:18) "" "get")) + (decl (20:5-20:19) + (ident (20:5-20:12) "result4") + (ident (20:15-20:19) "" "post")) + (decl (23:5-23:26) + (ident (23:5-23:13) "combined") + (ident (23:16-23:26) "Str" ".concat")) + (tuple (25:5-34:6) + (ident (26:9-26:15) "" "client") + (ident (27:9-27:15) "" "parser") + (ident (28:9-28:15) "" "helper") + (ident (29:9-29:16) "" "result1") + (ident (30:9-30:16) "" "result2") + (ident (31:9-31:16) "" "result3") + (ident (32:9-32:16) "" "result4") + (ident (33:9-33:17) "" "combined"))))))) +~~~ +# FORMATTED +~~~roc +module [] + +import json.Json +import http.Client as Http exposing [get, post] +import utils.String as Str + +main = { + client = Http.get + parser = Json.utf8 + helper = Str.trim + + # Test direct module access + result1 = Json.parse + + # Test aliased module access + result2 = Http.post + + # Test exposed items (should work without module prefix) + result3 = get + result4 = post + + # Test multiple qualified access + combined = Str.concat + + ( + client, + parser, + helper, + result1, + result2, + result3, + result4, + combined, + ) +} +~~~ +# CANONICALIZE +~~~clojure +(can_ir + (d_let + (def_pattern + (p_assign (7:1-7:5) + (pid 77) + (ident "main"))) + (def_expr + (e_block (7:8-35:2) + (s_let (8:5-8:22) + (p_assign (8:5-8:11) + (pid 78) + (ident "client")) + (e_lookup_external + (external_decl (8:14-8:22) + (qualified_name "http.Client.get") + (module_name "http.Client") + (local_name "get") + (kind "value") + (type_var 79)))) + (s_let (9:5-9:23) + (p_assign (9:5-9:11) + (pid 82) + (ident "parser")) + (e_lookup_external + (external_decl (9:14-9:23) + (qualified_name "json.Json.utf8") + (module_name "json.Json") + (local_name "utf8") + (kind "value") + (type_var 83)))) + (s_let (10:5-10:22) + (p_assign (10:5-10:11) + (pid 86) + (ident "helper")) + (e_lookup_external + (external_decl (10:14-10:22) + (qualified_name "utils.String.trim") + (module_name "utils.String") + (local_name "trim") + (kind "value") + (type_var 87)))) + (s_let (13:5-13:25) + (p_assign (13:5-13:12) + (pid 90) + (ident "result1")) + (e_lookup_external + (external_decl (13:15-13:25) + (qualified_name "json.Json.parse") + (module_name "json.Json") + (local_name "parse") + (kind "value") + (type_var 91)))) + (s_let (16:5-16:24) + (p_assign (16:5-16:12) + (pid 94) + (ident "result2")) + (e_lookup_external + (external_decl (16:15-16:24) + (qualified_name "http.Client.post") + (module_name "http.Client") + (local_name "post") + (kind "value") + (type_var 95)))) + (s_let (19:5-19:18) + (p_assign (19:5-19:12) + (pid 98) + (ident "result3")) + (e_lookup_external + (external_decl (19:15-19:18) + (qualified_name "http.Client.get") + (module_name "http.Client") + (local_name "get") + (kind "value") + (type_var 99)))) + (s_let (20:5-20:19) + (p_assign (20:5-20:12) + (pid 102) + (ident "result4")) + (e_lookup_external + (external_decl (20:15-20:19) + (qualified_name "http.Client.post") + (module_name "http.Client") + (local_name "post") + (kind "value") + (type_var 103)))) + (s_let (23:5-23:26) + (p_assign (23:5-23:13) + (pid 106) + (ident "combined")) + (e_lookup_external + (external_decl (23:16-23:26) + (qualified_name "utils.String.concat") + (module_name "utils.String") + (local_name "concat") + (kind "value") + (type_var 107)))) + (e_tuple (25:5-34:6) + (tuple_var "#118") + (elems + (e_lookup_local (26:9-26:15) (pid 78)) + (e_lookup_local (27:9-27:15) (pid 82)) + (e_lookup_local (28:9-28:15) (pid 86)) + (e_lookup_local (29:9-29:16) (pid 90)) + (e_lookup_local (30:9-30:16) (pid 94)) + (e_lookup_local (31:9-31:16) (pid 98)) + (e_lookup_local (32:9-32:16) (pid 102)) + (e_lookup_local (33:9-33:17) (pid 106))))))) + (s_import (3:1-3:17) + "json.Json" + "" + "" + (exposes)) + (s_import (4:1-4:48) + "http.Client" + "" + "" + (exposes (exposed_item "get") (exposed_item "post"))) + (s_import (5:1-5:27) + "utils.String" + "" + "" + (exposes))) +~~~ +# TYPES +~~~clojure +(inferred_types + (defs + (def "main" 121 (type "*"))) + (expressions + (expr (7:8-35:2) 120 (type "*")))) +~~~ \ No newline at end of file diff --git a/src/snapshots/can_import_json.md b/src/snapshots/can_import_json.md new file mode 100644 index 0000000000..7e191e719e --- /dev/null +++ b/src/snapshots/can_import_json.md @@ -0,0 +1,67 @@ +# META +~~~ini +description=Import with module-qualified usage +type=file +~~~ +# SOURCE +~~~roc +module [] + +import json.Json + +main = Json.utf8 +~~~ +# PROBLEMS +NIL +# TOKENS +~~~zig +KwModule(1:1-1:7),OpenSquare(1:8-1:9),CloseSquare(1:9-1:10),Newline(1:1-1:1), +Newline(1:1-1:1), +KwImport(3:1-3:7),LowerIdent(3:8-3:12),NoSpaceDotUpperIdent(3:12-3:17),Newline(1:1-1:1), +Newline(1:1-1:1), +LowerIdent(5:1-5:5),OpAssign(5:6-5:7),UpperIdent(5:8-5:12),NoSpaceDotLowerIdent(5:12-5:17),EndOfFile(5:17-5:17), +~~~ +# PARSE +~~~clojure +(file (1:1-5:17) + (module (1:1-1:10) (exposes (1:8-1:10))) + (statements + (import (3:1-3:17) ".Json" (qualifier "json")) + (decl (5:1-5:17) + (ident (5:1-5:5) "main") + (ident (5:8-5:17) "Json" ".utf8")))) +~~~ +# FORMATTED +~~~roc +NO CHANGE +~~~ +# CANONICALIZE +~~~clojure +(can_ir + (d_let + (def_pattern + (p_assign (5:1-5:5) + (pid 73) + (ident "main"))) + (def_expr + (e_lookup_external + (external_decl (5:8-5:17) + (qualified_name "json.Json.utf8") + (module_name "json.Json") + (local_name "utf8") + (kind "value") + (type_var 74))))) + (s_import (3:1-3:17) + "json.Json" + "" + "" + (exposes))) +~~~ +# TYPES +~~~clojure +(inferred_types + (defs + (def "main" 76 (type "*"))) + (expressions + (expr (5:8-5:17) 75 (type "*")))) +~~~ \ No newline at end of file diff --git a/src/snapshots/can_import_with_alias.md b/src/snapshots/can_import_with_alias.md new file mode 100644 index 0000000000..5caebf5786 --- /dev/null +++ b/src/snapshots/can_import_with_alias.md @@ -0,0 +1,70 @@ +# META +~~~ini +description=Import with explicit alias +type=file +~~~ +# SOURCE +~~~roc +module [] + +import json.Json as MyJson + +main = MyJson.decode +~~~ +# PROBLEMS +NIL +# TOKENS +~~~zig +KwModule(1:1-1:7),OpenSquare(1:8-1:9),CloseSquare(1:9-1:10),Newline(1:1-1:1), +Newline(1:1-1:1), +KwImport(3:1-3:7),LowerIdent(3:8-3:12),NoSpaceDotUpperIdent(3:12-3:17),KwAs(3:18-3:20),UpperIdent(3:21-3:27),Newline(1:1-1:1), +Newline(1:1-1:1), +LowerIdent(5:1-5:5),OpAssign(5:6-5:7),UpperIdent(5:8-5:14),NoSpaceDotLowerIdent(5:14-5:21),EndOfFile(5:21-5:21), +~~~ +# PARSE +~~~clojure +(file (1:1-5:21) + (module (1:1-1:10) (exposes (1:8-1:10))) + (statements + (import (3:1-3:27) + ".Json" + (qualifier "json") + (alias "MyJson")) + (decl (5:1-5:21) + (ident (5:1-5:5) "main") + (ident (5:8-5:21) "MyJson" ".decode")))) +~~~ +# FORMATTED +~~~roc +NO CHANGE +~~~ +# CANONICALIZE +~~~clojure +(can_ir + (d_let + (def_pattern + (p_assign (5:1-5:5) + (pid 73) + (ident "main"))) + (def_expr + (e_lookup_external + (external_decl (5:8-5:21) + (qualified_name "json.Json.decode") + (module_name "json.Json") + (local_name "decode") + (kind "value") + (type_var 74))))) + (s_import (3:1-3:27) + "json.Json" + "" + "" + (exposes))) +~~~ +# TYPES +~~~clojure +(inferred_types + (defs + (def "main" 76 (type "*"))) + (expressions + (expr (5:8-5:21) 75 (type "*")))) +~~~ \ No newline at end of file diff --git a/src/snapshots/can_two_decls.md b/src/snapshots/can_two_decls.md index 3987948fb8..677aa1a3ed 100644 --- a/src/snapshots/can_two_decls.md +++ b/src/snapshots/can_two_decls.md @@ -69,7 +69,7 @@ NO CHANGE (def_expr (e_binop (4:5-4:10) "add" - (e_lookup (4:5-4:6) (pid 72)) + (e_lookup_local (4:5-4:6) (pid 72)) (e_int (4:9-4:10) (int_var 80) (precision_var 79) diff --git a/src/snapshots/can_var_scoping_regular_var.md b/src/snapshots/can_var_scoping_regular_var.md index d43eda0ada..94ff8f4d01 100644 --- a/src/snapshots/can_var_scoping_regular_var.md +++ b/src/snapshots/can_var_scoping_regular_var.md @@ -177,7 +177,7 @@ NO CHANGE (pid 77) (e_binop (9:11-10:8) "add" - (e_lookup (9:11-9:17) (pid 77)) + (e_lookup_local (9:11-9:17) (pid 77)) (e_int (9:20-9:21) (int_var 86) (precision_var 85) @@ -188,7 +188,7 @@ NO CHANGE (pid 82) (e_binop (10:11-13:12) "add" - (e_lookup (10:11-10:17) (pid 82)) + (e_lookup_local (10:11-10:17) (pid 82)) (e_int (10:20-10:22) (int_var 92) (precision_var 91) @@ -208,18 +208,18 @@ NO CHANGE (s_reassign (15:3-15:9) (pid 82) (e_runtime_error (15:3-15:9) "var_across_function_boundary")) - (e_lookup (16:3-16:9) (pid 77))))) + (e_lookup_local (16:3-16:9) (pid 77))))) (s_let (19:2-19:25) (p_assign (19:2-19:8) (pid 108) (ident "result")) (e_call (19:11-19:25) - (e_lookup (19:11-19:21) (pid 96)) + (e_lookup_local (19:11-19:21) (pid 96)) (e_runtime_error (1:1-1:1) "not_implemented"))) (e_binop (20:2-21:2) "add" - (e_lookup (20:2-20:8) (pid 82)) - (e_lookup (20:11-20:17) (pid 108)))))))) + (e_lookup_local (20:2-20:8) (pid 82)) + (e_lookup_local (20:11-20:17) (pid 108)))))))) ~~~ # TYPES ~~~clojure diff --git a/src/snapshots/can_var_scoping_var_idents.md b/src/snapshots/can_var_scoping_var_idents.md index b86fd6028d..36d6a830c2 100644 --- a/src/snapshots/can_var_scoping_var_idents.md +++ b/src/snapshots/can_var_scoping_var_idents.md @@ -85,7 +85,7 @@ NO CHANGE (p_assign (5:2-5:5) (pid 74) (ident "sum")) - (e_lookup (5:8-5:13) (pid 73))) + (e_lookup_local (5:8-5:13) (pid 73))) (s_var (6:2-8:6) (pid 82) (p_assign (6:2-8:6) @@ -93,7 +93,7 @@ NO CHANGE (ident "sum_")) (e_binop (6:13-8:6) "mul" - (e_lookup (6:13-6:18) (pid 73)) + (e_lookup_local (6:13-6:18) (pid 73)) (e_int (6:21-6:22) (int_var 79) (precision_var 78) @@ -104,12 +104,12 @@ NO CHANGE (pid 82) (e_binop (8:9-9:5) "add" - (e_lookup (8:9-8:13) (pid 82)) - (e_lookup (8:16-8:19) (pid 74)))) + (e_lookup_local (8:9-8:13) (pid 82)) + (e_lookup_local (8:16-8:19) (pid 74)))) (e_binop (9:2-10:2) "add" - (e_lookup (9:2-9:5) (pid 74)) - (e_lookup (9:8-9:12) (pid 82)))))))) + (e_lookup_local (9:2-9:5) (pid 74)) + (e_lookup_local (9:8-9:12) (pid 82)))))))) ~~~ # TYPES ~~~clojure diff --git a/src/snapshots/can_var_scoping_var_redeclaration.md b/src/snapshots/can_var_scoping_var_redeclaration.md index ce5e69d9f7..9ba7f96e35 100644 --- a/src/snapshots/can_var_scoping_var_redeclaration.md +++ b/src/snapshots/can_var_scoping_var_redeclaration.md @@ -138,7 +138,7 @@ NO CHANGE (literal "15") (value "TODO") (bound "u8"))) - (e_lookup (8:2-8:4) (pid 77)))))) + (e_lookup_local (8:2-8:4) (pid 77)))))) (d_let (def_pattern (p_assign (11:1-11:7) @@ -146,7 +146,7 @@ NO CHANGE (ident "result"))) (def_expr (e_call (11:10-11:27) - (e_lookup (11:10-11:23) (pid 72)) + (e_lookup_local (11:10-11:23) (pid 72)) (e_runtime_error (1:1-1:1) "not_implemented"))))) ~~~ # TYPES diff --git a/src/snapshots/crash_and_ellipsis_test.md b/src/snapshots/crash_and_ellipsis_test.md index e5e0672cf0..a414fb2194 100644 --- a/src/snapshots/crash_and_ellipsis_test.md +++ b/src/snapshots/crash_and_ellipsis_test.md @@ -267,7 +267,7 @@ result3 = testCrashSimple(42) (ident "result2"))) (def_expr (e_call (17:15-17:28) - (e_lookup (17:15-17:24) (pid 89)) + (e_lookup_local (17:15-17:24) (pid 89)) (e_int (17:25-17:27) (int_var 127) (precision_var 126) @@ -281,7 +281,7 @@ result3 = testCrashSimple(42) (ident "result3"))) (def_expr (e_call (18:15-18:34) - (e_lookup (18:15-18:30) (pid 104)) + (e_lookup_local (18:15-18:30) (pid 104)) (e_int (18:31-18:33) (int_var 134) (precision_var 133) diff --git a/src/snapshots/exposed_items_test.md b/src/snapshots/exposed_items_test.md new file mode 100644 index 0000000000..e4e080b895 --- /dev/null +++ b/src/snapshots/exposed_items_test.md @@ -0,0 +1,72 @@ +# META +~~~ini +description=Import with exposing syntax test +type=file +~~~ +# SOURCE +~~~roc +module [main] + +import pf.Stdout exposing [line!, write!] + +main = 42 +~~~ +# PROBLEMS +NIL +# TOKENS +~~~zig +KwModule(1:1-1:7),OpenSquare(1:8-1:9),LowerIdent(1:9-1:13),CloseSquare(1:13-1:14),Newline(1:1-1:1), +Newline(1:1-1:1), +KwImport(3:1-3:7),LowerIdent(3:8-3:10),NoSpaceDotUpperIdent(3:10-3:17),KwExposing(3:18-3:26),OpenSquare(3:27-3:28),LowerIdent(3:28-3:33),Comma(3:33-3:34),LowerIdent(3:35-3:41),CloseSquare(3:41-3:42),Newline(1:1-1:1), +Newline(1:1-1:1), +LowerIdent(5:1-5:5),OpAssign(5:6-5:7),Int(5:8-5:10),EndOfFile(5:10-5:10), +~~~ +# PARSE +~~~clojure +(file (1:1-5:10) + (module (1:1-1:14) + (exposes (1:8-1:14) (exposed_item (lower_ident "main")))) + (statements + (import (3:1-3:42) + ".Stdout" + (qualifier "pf") + (exposing + (exposed_item (lower_ident "line!")) + (exposed_item (lower_ident "write!")))) + (decl (5:1-5:10) + (ident (5:1-5:5) "main") + (int (5:8-5:10) "42")))) +~~~ +# FORMATTED +~~~roc +NO CHANGE +~~~ +# CANONICALIZE +~~~clojure +(can_ir + (d_let + (def_pattern + (p_assign (5:1-5:5) + (pid 75) + (ident "main"))) + (def_expr + (e_int (5:8-5:10) + (int_var 77) + (precision_var 76) + (literal "42") + (value "TODO") + (bound "u8")))) + (s_import (3:1-3:42) + "pf.Stdout" + "" + "" + (exposes (exposed_item "line!") (exposed_item "write!")))) +~~~ +# TYPES +~~~clojure +(inferred_types + (defs + (def "main" 79 (type "Num(Int(*))"))) + (expressions + (expr (5:8-5:10) 78 (type "Num(Int(*))")))) +~~~ \ No newline at end of file diff --git a/src/snapshots/expr/block_defs_simple.md b/src/snapshots/expr/block_defs_simple.md index d5aa38f970..fe8b16c8cf 100644 --- a/src/snapshots/expr/block_defs_simple.md +++ b/src/snapshots/expr/block_defs_simple.md @@ -66,7 +66,7 @@ CloseCurly(5:1-5:2),EndOfFile(5:2-5:2), (ident "y")) (e_binop (3:9-4:6) "add" - (e_lookup (3:9-3:10) (pid 72)) + (e_lookup_local (3:9-3:10) (pid 72)) (e_int (3:13-3:14) (int_var 80) (precision_var 79) @@ -75,7 +75,7 @@ CloseCurly(5:1-5:2),EndOfFile(5:2-5:2), (bound "u8")))) (e_binop (4:5-5:2) "mul" - (e_lookup (4:5-4:6) (pid 77)) + (e_lookup_local (4:5-4:6) (pid 77)) (e_int (4:9-4:10) (int_var 86) (precision_var 85) diff --git a/src/snapshots/expr/lambda_simple.md b/src/snapshots/expr/lambda_simple.md index 8384e7cae2..c9cfe6f95a 100644 --- a/src/snapshots/expr/lambda_simple.md +++ b/src/snapshots/expr/lambda_simple.md @@ -35,7 +35,7 @@ NO CHANGE (ident "x"))) (e_binop (1:5-1:10) "add" - (e_lookup (1:5-1:6) (pid 72)) + (e_lookup_local (1:5-1:6) (pid 72)) (e_int (1:9-1:10) (int_var 75) (precision_var 74) diff --git a/src/snapshots/expr/tuple_comprehensive.md b/src/snapshots/expr/tuple_comprehensive.md index ff1a23f0a3..7d5891d48d 100644 --- a/src/snapshots/expr/tuple_comprehensive.md +++ b/src/snapshots/expr/tuple_comprehensive.md @@ -363,7 +363,7 @@ CloseCurly(19:1-19:2),EndOfFile(19:2-19:2), (tuple_var "#164") (elems (e_call (14:11-14:21) - (e_lookup (14:11-14:18) (pid 72)) + (e_lookup_local (14:11-14:18) (pid 72)) (e_int (14:19-14:20) (int_var 148) (precision_var 147) @@ -399,9 +399,9 @@ CloseCurly(19:1-19:2),EndOfFile(19:2-19:2), (e_tuple (15:14-15:23) (tuple_var "#171") (elems - (e_lookup (15:15-15:16) (pid 78)) - (e_lookup (15:18-15:19) (pid 83)) - (e_lookup (15:21-15:22) (pid 88))))) + (e_lookup_local (15:15-15:16) (pid 78)) + (e_lookup_local (15:18-15:19) (pid 83)) + (e_lookup_local (15:21-15:22) (pid 88))))) (s_let (16:2-16:31) (p_assign (16:2-16:13) (pid 174) @@ -416,7 +416,7 @@ CloseCurly(19:1-19:2),EndOfFile(19:2-19:2), (ident "n"))) (e_binop (16:21-16:27) "add" - (e_lookup (16:21-16:22) (pid 175)) + (e_lookup_local (16:21-16:22) (pid 175)) (e_int (16:25-16:26) (int_var 178) (precision_var 177) @@ -429,7 +429,7 @@ CloseCurly(19:1-19:2),EndOfFile(19:2-19:2), (literal "42") (value "TODO") (bound "u8"))))) - (e_lookup (18:2-18:7) (pid 93))) + (e_lookup_local (18:2-18:7) (pid 93))) ~~~ # TYPES ~~~clojure diff --git a/src/snapshots/external_decl_lookup.md b/src/snapshots/external_decl_lookup.md new file mode 100644 index 0000000000..598fa2ef7f --- /dev/null +++ b/src/snapshots/external_decl_lookup.md @@ -0,0 +1,129 @@ +# META +~~~ini +description=External declaration lookup from json module +type=file +~~~ +# SOURCE +~~~roc +app [main!] { pf: platform "../basic-cli/platform.roc" } + +import pf.Stdout +import json.Json + +main! = |_| { + # This should create an external declaration for json.Json.utf8 + result = Json.utf8("Hello from external module!") + Stdout.line!(result) +} +~~~ +# PROBLEMS +NIL +# TOKENS +~~~zig +KwApp(1:1-1:4),OpenSquare(1:5-1:6),LowerIdent(1:6-1:11),CloseSquare(1:11-1:12),OpenCurly(1:13-1:14),LowerIdent(1:15-1:17),OpColon(1:17-1:18),KwPlatform(1:19-1:27),StringStart(1:28-1:29),StringPart(1:29-1:54),StringEnd(1:54-1:55),CloseCurly(1:56-1:57),Newline(1:1-1:1), +Newline(1:1-1:1), +KwImport(3:1-3:7),LowerIdent(3:8-3:10),NoSpaceDotUpperIdent(3:10-3:17),Newline(1:1-1:1), +KwImport(4:1-4:7),LowerIdent(4:8-4:12),NoSpaceDotUpperIdent(4:12-4:17),Newline(1:1-1:1), +Newline(1:1-1:1), +LowerIdent(6:1-6:6),OpAssign(6:7-6:8),OpBar(6:9-6:10),Underscore(6:10-6:11),OpBar(6:11-6:12),OpenCurly(6:13-6:14),Newline(1:1-1:1), +Newline(7:6-7:68), +LowerIdent(8:5-8:11),OpAssign(8:12-8:13),UpperIdent(8:14-8:18),NoSpaceDotLowerIdent(8:18-8:23),NoSpaceOpenRound(8:23-8:24),StringStart(8:24-8:25),StringPart(8:25-8:52),StringEnd(8:52-8:53),CloseRound(8:53-8:54),Newline(1:1-1:1), +UpperIdent(9:5-9:11),NoSpaceDotLowerIdent(9:11-9:17),NoSpaceOpenRound(9:17-9:18),LowerIdent(9:18-9:24),CloseRound(9:24-9:25),Newline(1:1-1:1), +CloseCurly(10:1-10:2),EndOfFile(10:2-10:2), +~~~ +# PARSE +~~~clojure +(file (1:1-10:2) + (app (1:1-1:57) + (provides (1:6-1:12) (exposed_item (lower_ident "main!"))) + (record_field (1:15-1:57) + "pf" + (string (1:28-1:55) (string_part (1:29-1:54) "../basic-cli/platform.roc"))) + (packages (1:13-1:57) + (record_field (1:15-1:57) + "pf" + (string (1:28-1:55) (string_part (1:29-1:54) "../basic-cli/platform.roc"))))) + (statements + (import (3:1-3:17) ".Stdout" (qualifier "pf")) + (import (4:1-4:17) ".Json" (qualifier "json")) + (decl (6:1-10:2) + (ident (6:1-6:6) "main!") + (lambda (6:9-10:2) + (args (underscore)) + (block (6:13-10:2) + (statements + (decl (8:5-8:54) + (ident (8:5-8:11) "result") + (apply (8:14-8:54) + (ident (8:14-8:23) "Json" ".utf8") + (string (8:24-8:53) (string_part (8:25-8:52) "Hello from external module!")))) + (apply (9:5-9:25) + (ident (9:5-9:17) "Stdout" ".line!") + (ident (9:18-9:24) "" "result")))))))) +~~~ +# FORMATTED +~~~roc +app [main!] { pf: platform "../basic-cli/platform.roc" } + +import pf.Stdout +import json.Json + +main! = |_| { + # This should create an external declaration for json.Json.utf8 + result = Json.utf8("Hello from external module!") + Stdout.line!(result) +} +~~~ +# CANONICALIZE +~~~clojure +(can_ir + (d_let + (def_pattern + (p_assign (6:1-6:6) + (pid 74) + (ident "main!"))) + (def_expr + (e_lambda (6:9-10:2) + (args (p_underscore (6:10-6:11) (pid 75))) + (e_block (6:13-10:2) + (s_let (8:5-8:54) + (p_assign (8:5-8:11) + (pid 76) + (ident "result")) + (e_call (8:14-8:54) + (e_lookup_external + (external_decl (8:14-8:23) + (qualified_name "json.Json.utf8") + (module_name "json.Json") + (local_name "utf8") + (kind "value") + (type_var 77))) + (e_string (8:24-8:53) (e_literal (8:25-8:52) "Hello from external module!")))) + (e_call (9:5-9:25) + (e_lookup_external + (external_decl (9:5-9:17) + (qualified_name "pf.Stdout.line!") + (module_name "pf.Stdout") + (local_name "line!") + (kind "value") + (type_var 83))) + (e_lookup_local (9:18-9:24) (pid 76))))))) + (s_import (3:1-3:17) + "pf.Stdout" + "" + "" + (exposes)) + (s_import (4:1-4:17) + "json.Json" + "" + "" + (exposes))) +~~~ +# TYPES +~~~clojure +(inferred_types + (defs + (def "main!" 89 (type "*"))) + (expressions + (expr (6:9-10:2) 88 (type "*")))) +~~~ \ No newline at end of file diff --git a/src/snapshots/external_lookup_expr.md b/src/snapshots/external_lookup_expr.md new file mode 100644 index 0000000000..7589a4dc61 --- /dev/null +++ b/src/snapshots/external_lookup_expr.md @@ -0,0 +1,34 @@ +# META +~~~ini +description=External declaration lookup expression +type=expr +~~~ +# SOURCE +~~~roc +Json.utf8 +~~~ +# PROBLEMS +**UNDEFINED VARIABLE** +Nothing is named `utf8` in this scope. +Is there an `import` or `exposing` missing up-top? + +# TOKENS +~~~zig +UpperIdent(1:1-1:5),NoSpaceDotLowerIdent(1:5-1:10),EndOfFile(1:10-1:10), +~~~ +# PARSE +~~~clojure +(ident (1:1-1:10) "Json" ".utf8") +~~~ +# FORMATTED +~~~roc +NO CHANGE +~~~ +# CANONICALIZE +~~~clojure +(e_runtime_error (1:1-1:10) "ident_not_in_scope") +~~~ +# TYPES +~~~clojure +(expr 73 (type "Error")) +~~~ \ No newline at end of file diff --git a/src/snapshots/hello_world.md b/src/snapshots/hello_world.md index 38363ef6a0..4dd3b2ed6f 100644 --- a/src/snapshots/hello_world.md +++ b/src/snapshots/hello_world.md @@ -12,13 +12,7 @@ import pf.Stdout main! = |_| Stdout.line!("Hello, world!") ~~~ # PROBLEMS -**NOT IMPLEMENTED** -This feature is not yet implemented: top-level import - -**UNDEFINED VARIABLE** -Nothing is named `line!` in this scope. -Is there an `import` or `exposing` missing up-top? - +NIL # TOKENS ~~~zig KwApp(1:1-1:4),OpenSquare(1:5-1:6),LowerIdent(1:6-1:11),CloseSquare(1:11-1:12),OpenCurly(1:13-1:14),LowerIdent(1:15-1:17),OpColon(1:17-1:18),KwPlatform(1:19-1:27),StringStart(1:28-1:29),StringPart(1:29-1:54),StringEnd(1:54-1:55),CloseCurly(1:56-1:57),Newline(1:1-1:1), @@ -65,8 +59,19 @@ NO CHANGE (e_lambda (5:9-5:42) (args (p_underscore (5:10-5:11) (pid 74))) (e_call (5:13-5:42) - (e_runtime_error (5:13-5:25) "ident_not_in_scope") - (e_string (5:26-5:41) (e_literal (5:27-5:40) "Hello, world!"))))))) + (e_lookup_external + (external_decl (5:13-5:25) + (qualified_name "pf.Stdout.line!") + (module_name "pf.Stdout") + (local_name "line!") + (kind "value") + (type_var 75))) + (e_string (5:26-5:41) (e_literal (5:27-5:40) "Hello, world!")))))) + (s_import (3:1-3:17) + "pf.Stdout" + "" + "" + (exposes))) ~~~ # TYPES ~~~clojure diff --git a/src/snapshots/hello_world_with_block.md b/src/snapshots/hello_world_with_block.md index a23bbf7e87..087bb837d1 100644 --- a/src/snapshots/hello_world_with_block.md +++ b/src/snapshots/hello_world_with_block.md @@ -19,13 +19,6 @@ main! = |_| { } ~~~ # PROBLEMS -**NOT IMPLEMENTED** -This feature is not yet implemented: top-level import - -**UNDEFINED VARIABLE** -Nothing is named `line!` in this scope. -Is there an `import` or `exposing` missing up-top? - **UNUSED VARIABLE** Variable ``world`` is not used anywhere in your code. @@ -101,8 +94,19 @@ NO CHANGE (ident "world")) (e_string (9:10-9:17) (e_literal (9:11-9:16) "World"))) (e_call (11:2-11:31) - (e_runtime_error (11:2-11:14) "ident_not_in_scope") - (e_string (11:15-11:30) (e_literal (11:16-11:29) "Hello, world!")))))))) + (e_lookup_external + (external_decl (11:2-11:14) + (qualified_name "pf.Stdout.line!") + (module_name "pf.Stdout") + (local_name "line!") + (kind "value") + (type_var 79))) + (e_string (11:15-11:30) (e_literal (11:16-11:29) "Hello, world!"))))))) + (s_import (6:1-6:17) + "pf.Stdout" + "" + "" + (exposes))) ~~~ # TYPES ~~~clojure diff --git a/src/snapshots/import_exposing_alias.md b/src/snapshots/import_exposing_alias.md new file mode 100644 index 0000000000..0ec4c910cd --- /dev/null +++ b/src/snapshots/import_exposing_alias.md @@ -0,0 +1,132 @@ +# META +~~~ini +description=Import with exposing clause using aliases +type=file +~~~ +# SOURCE +~~~roc +module [main] + +import json.Json exposing [decode as fromJson, encode as toJson] + +main = { + data = { name: "Bob", age: 25 } + encoded = toJson(data) + decoded = fromJson(encoded) + decoded +} +~~~ +# PROBLEMS +**NOT IMPLEMENTED** +This feature is not yet implemented: canonicalize record expression + +# TOKENS +~~~zig +KwModule(1:1-1:7),OpenSquare(1:8-1:9),LowerIdent(1:9-1:13),CloseSquare(1:13-1:14),Newline(1:1-1:1), +Newline(1:1-1:1), +KwImport(3:1-3:7),LowerIdent(3:8-3:12),NoSpaceDotUpperIdent(3:12-3:17),KwExposing(3:18-3:26),OpenSquare(3:27-3:28),LowerIdent(3:28-3:34),KwAs(3:35-3:37),LowerIdent(3:38-3:46),Comma(3:46-3:47),LowerIdent(3:48-3:54),KwAs(3:55-3:57),LowerIdent(3:58-3:64),CloseSquare(3:64-3:65),Newline(1:1-1:1), +Newline(1:1-1:1), +LowerIdent(5:1-5:5),OpAssign(5:6-5:7),OpenCurly(5:8-5:9),Newline(1:1-1:1), +LowerIdent(6:2-6:6),OpAssign(6:7-6:8),OpenCurly(6:9-6:10),LowerIdent(6:11-6:15),OpColon(6:15-6:16),StringStart(6:17-6:18),StringPart(6:18-6:21),StringEnd(6:21-6:22),Comma(6:22-6:23),LowerIdent(6:24-6:27),OpColon(6:27-6:28),Int(6:29-6:31),CloseCurly(6:32-6:33),Newline(1:1-1:1), +LowerIdent(7:2-7:9),OpAssign(7:10-7:11),LowerIdent(7:12-7:18),NoSpaceOpenRound(7:18-7:19),LowerIdent(7:19-7:23),CloseRound(7:23-7:24),Newline(1:1-1:1), +LowerIdent(8:2-8:9),OpAssign(8:10-8:11),LowerIdent(8:12-8:20),NoSpaceOpenRound(8:20-8:21),LowerIdent(8:21-8:28),CloseRound(8:28-8:29),Newline(1:1-1:1), +LowerIdent(9:2-9:9),Newline(1:1-1:1), +CloseCurly(10:1-10:2),EndOfFile(10:2-10:2), +~~~ +# PARSE +~~~clojure +(file (1:1-10:2) + (module (1:1-1:14) + (exposes (1:8-1:14) (exposed_item (lower_ident "main")))) + (statements + (import (3:1-3:65) + ".Json" + (qualifier "json") + (exposing + (exposed_item (lower_ident "decode" "fromJson")) + (exposed_item (lower_ident "encode" "toJson")))) + (decl (5:1-10:2) + (ident (5:1-5:5) "main") + (block (5:8-10:2) + (statements + (decl (6:2-6:33) + (ident (6:2-6:6) "data") + (record (6:9-6:33) + (field + "name" + (string (6:17-6:22) (string_part (6:18-6:21) "Bob"))) + (field "age" (int (6:29-6:31) "25")))) + (decl (7:2-7:24) + (ident (7:2-7:9) "encoded") + (apply (7:12-7:24) + (ident (7:12-7:18) "" "toJson") + (ident (7:19-7:23) "" "data"))) + (decl (8:2-8:29) + (ident (8:2-8:9) "decoded") + (apply (8:12-8:29) + (ident (8:12-8:20) "" "fromJson") + (ident (8:21-8:28) "" "encoded"))) + (ident (9:2-9:9) "" "decoded")))))) +~~~ +# FORMATTED +~~~roc +NO CHANGE +~~~ +# CANONICALIZE +~~~clojure +(can_ir + (d_let + (def_pattern + (p_assign (5:1-5:5) + (pid 75) + (ident "main"))) + (def_expr + (e_block (5:8-10:2) + (s_let (6:2-6:33) + (p_assign (6:2-6:6) + (pid 76) + (ident "data")) + (e_runtime_error (1:1-1:1) "not_implemented")) + (s_let (7:2-7:24) + (p_assign (7:2-7:9) + (pid 80) + (ident "encoded")) + (e_call (7:12-7:24) + (e_lookup_external + (external_decl (7:12-7:18) + (qualified_name "json.Json.encode") + (module_name "json.Json") + (local_name "toJson") + (kind "value") + (type_var 81))) + (e_lookup_local (7:19-7:23) (pid 76)))) + (s_let (8:2-8:29) + (p_assign (8:2-8:9) + (pid 86) + (ident "decoded")) + (e_call (8:12-8:29) + (e_lookup_external + (external_decl (8:12-8:20) + (qualified_name "json.Json.decode") + (module_name "json.Json") + (local_name "fromJson") + (kind "value") + (type_var 87))) + (e_lookup_local (8:21-8:28) (pid 80)))) + (e_lookup_local (9:2-9:9) (pid 86))))) + (s_import (3:1-3:65) + "json.Json" + "" + "" + (exposes + (exposed_item "decode" "fromJson") + (exposed_item "encode" "toJson")))) +~~~ +# TYPES +~~~clojure +(inferred_types + (defs + (def "main" 94 (type "*"))) + (expressions + (expr (5:8-10:2) 93 (type "*")))) +~~~ \ No newline at end of file diff --git a/src/snapshots/import_exposing_basic.md b/src/snapshots/import_exposing_basic.md new file mode 100644 index 0000000000..3ec18f842e --- /dev/null +++ b/src/snapshots/import_exposing_basic.md @@ -0,0 +1,139 @@ +# META +~~~ini +description=Import with exposing clause and usage of exposed items +type=file +~~~ +# SOURCE +~~~roc +module [main] + +import json.Json exposing [decode, encode] + +main = { + data = { name: "Alice", age: 30 } + encoded = encode(data) + decoded = decode(encoded) + decoded +} +~~~ +# PROBLEMS +**NOT IMPLEMENTED** +This feature is not yet implemented: canonicalize record expression + +# TOKENS +~~~zig +KwModule(1:1-1:7),OpenSquare(1:8-1:9),LowerIdent(1:9-1:13),CloseSquare(1:13-1:14),Newline(1:1-1:1), +Newline(1:1-1:1), +KwImport(3:1-3:7),LowerIdent(3:8-3:12),NoSpaceDotUpperIdent(3:12-3:17),KwExposing(3:18-3:26),OpenSquare(3:27-3:28),LowerIdent(3:28-3:34),Comma(3:34-3:35),LowerIdent(3:36-3:42),CloseSquare(3:42-3:43),Newline(1:1-1:1), +Newline(1:1-1:1), +LowerIdent(5:1-5:5),OpAssign(5:6-5:7),OpenCurly(5:8-5:9),Newline(1:1-1:1), +LowerIdent(6:5-6:9),OpAssign(6:10-6:11),OpenCurly(6:12-6:13),LowerIdent(6:14-6:18),OpColon(6:18-6:19),StringStart(6:20-6:21),StringPart(6:21-6:26),StringEnd(6:26-6:27),Comma(6:27-6:28),LowerIdent(6:29-6:32),OpColon(6:32-6:33),Int(6:34-6:36),CloseCurly(6:37-6:38),Newline(1:1-1:1), +LowerIdent(7:5-7:12),OpAssign(7:13-7:14),LowerIdent(7:15-7:21),NoSpaceOpenRound(7:21-7:22),LowerIdent(7:22-7:26),CloseRound(7:26-7:27),Newline(1:1-1:1), +LowerIdent(8:5-8:12),OpAssign(8:13-8:14),LowerIdent(8:15-8:21),NoSpaceOpenRound(8:21-8:22),LowerIdent(8:22-8:29),CloseRound(8:29-8:30),Newline(1:1-1:1), +LowerIdent(9:5-9:12),Newline(1:1-1:1), +CloseCurly(10:1-10:2),EndOfFile(10:2-10:2), +~~~ +# PARSE +~~~clojure +(file (1:1-10:2) + (module (1:1-1:14) + (exposes (1:8-1:14) (exposed_item (lower_ident "main")))) + (statements + (import (3:1-3:43) + ".Json" + (qualifier "json") + (exposing + (exposed_item (lower_ident "decode")) + (exposed_item (lower_ident "encode")))) + (decl (5:1-10:2) + (ident (5:1-5:5) "main") + (block (5:8-10:2) + (statements + (decl (6:5-6:38) + (ident (6:5-6:9) "data") + (record (6:12-6:38) + (field + "name" + (string (6:20-6:27) (string_part (6:21-6:26) "Alice"))) + (field "age" (int (6:34-6:36) "30")))) + (decl (7:5-7:27) + (ident (7:5-7:12) "encoded") + (apply (7:15-7:27) + (ident (7:15-7:21) "" "encode") + (ident (7:22-7:26) "" "data"))) + (decl (8:5-8:30) + (ident (8:5-8:12) "decoded") + (apply (8:15-8:30) + (ident (8:15-8:21) "" "decode") + (ident (8:22-8:29) "" "encoded"))) + (ident (9:5-9:12) "" "decoded")))))) +~~~ +# FORMATTED +~~~roc +module [main] + +import json.Json exposing [decode, encode] + +main = { + data = { name: "Alice", age: 30 } + encoded = encode(data) + decoded = decode(encoded) + decoded +} +~~~ +# CANONICALIZE +~~~clojure +(can_ir + (d_let + (def_pattern + (p_assign (5:1-5:5) + (pid 75) + (ident "main"))) + (def_expr + (e_block (5:8-10:2) + (s_let (6:5-6:38) + (p_assign (6:5-6:9) + (pid 76) + (ident "data")) + (e_runtime_error (1:1-1:1) "not_implemented")) + (s_let (7:5-7:27) + (p_assign (7:5-7:12) + (pid 80) + (ident "encoded")) + (e_call (7:15-7:27) + (e_lookup_external + (external_decl (7:15-7:21) + (qualified_name "json.Json.encode") + (module_name "json.Json") + (local_name "encode") + (kind "value") + (type_var 81))) + (e_lookup_local (7:22-7:26) (pid 76)))) + (s_let (8:5-8:30) + (p_assign (8:5-8:12) + (pid 86) + (ident "decoded")) + (e_call (8:15-8:30) + (e_lookup_external + (external_decl (8:15-8:21) + (qualified_name "json.Json.decode") + (module_name "json.Json") + (local_name "decode") + (kind "value") + (type_var 87))) + (e_lookup_local (8:22-8:29) (pid 80)))) + (e_lookup_local (9:5-9:12) (pid 86))))) + (s_import (3:1-3:43) + "json.Json" + "" + "" + (exposes (exposed_item "decode") (exposed_item "encode")))) +~~~ +# TYPES +~~~clojure +(inferred_types + (defs + (def "main" 94 (type "*"))) + (expressions + (expr (5:8-10:2) 93 (type "*")))) +~~~ \ No newline at end of file diff --git a/src/snapshots/lambda_parameter_unused.md b/src/snapshots/lambda_parameter_unused.md index 4d3bc2df0f..94e6ddcfa7 100644 --- a/src/snapshots/lambda_parameter_unused.md +++ b/src/snapshots/lambda_parameter_unused.md @@ -246,7 +246,7 @@ main! = |_| { (ident "_factor"))) (e_binop (9:22-12:8) "mul" - (e_lookup (9:22-9:29) (pid 92)) + (e_lookup_local (9:22-9:29) (pid 92)) (e_int (9:32-9:33) (int_var 96) (precision_var 95) @@ -297,7 +297,7 @@ main! = |_| { (ident "value"))) (e_binop (17:18-19:6) "mul" - (e_lookup (17:18-17:23) (pid 125)) + (e_lookup_local (17:18-17:23) (pid 125)) (e_int (17:26-17:27) (int_var 128) (precision_var 127) @@ -325,7 +325,7 @@ main! = |_| { (pid 140) (ident "result1")) (e_call (20:15-20:21) - (e_lookup (20:15-20:18) (pid 75)) + (e_lookup_local (20:15-20:18) (pid 75)) (e_int (20:19-20:20) (int_var 143) (precision_var 142) @@ -337,7 +337,7 @@ main! = |_| { (pid 147) (ident "result2")) (e_call (21:15-21:26) - (e_lookup (21:15-21:23) (pid 91)) + (e_lookup_local (21:15-21:23) (pid 91)) (e_int (21:24-21:25) (int_var 150) (precision_var 149) @@ -349,7 +349,7 @@ main! = |_| { (pid 154) (ident "result3")) (e_call (22:15-22:25) - (e_lookup (22:15-22:22) (pid 109)) + (e_lookup_local (22:15-22:22) (pid 109)) (e_int (22:23-22:24) (int_var 157) (precision_var 156) @@ -361,7 +361,7 @@ main! = |_| { (pid 161) (ident "result4")) (e_call (23:15-23:24) - (e_lookup (23:15-23:21) (pid 124)) + (e_lookup_local (23:15-23:21) (pid 124)) (e_int (23:22-23:23) (int_var 164) (precision_var 163) @@ -370,14 +370,14 @@ main! = |_| { (bound "u8")))) (e_binop (24:5-25:2) "add" - (e_lookup (24:5-24:12) (pid 140)) + (e_lookup_local (24:5-24:12) (pid 140)) (e_binop (24:15-25:2) "add" - (e_lookup (24:15-24:22) (pid 147)) + (e_lookup_local (24:15-24:22) (pid 147)) (e_binop (24:25-25:2) "add" - (e_lookup (24:25-24:32) (pid 154)) - (e_lookup (24:35-24:42) (pid 161)))))))))) + (e_lookup_local (24:25-24:32) (pid 154)) + (e_lookup_local (24:35-24:42) (pid 161)))))))))) ~~~ # TYPES ~~~clojure diff --git a/src/snapshots/primitive/expr_string.md b/src/snapshots/primitive/expr_string.md index 4b93591d7a..b8c8310a63 100644 --- a/src/snapshots/primitive/expr_string.md +++ b/src/snapshots/primitive/expr_string.md @@ -55,7 +55,7 @@ NO CHANGE (def_expr (e_string (3:7-3:22) (e_literal (3:8-3:14) "hello ") - (e_lookup (3:16-3:20) (pid 72)) + (e_lookup_local (3:16-3:20) (pid 72)) (e_literal (3:21-3:21) ""))))) ~~~ # TYPES diff --git a/src/snapshots/primitive/stmt_import.md b/src/snapshots/primitive/stmt_import.md index 8ba74028d0..ead3741b9b 100644 --- a/src/snapshots/primitive/stmt_import.md +++ b/src/snapshots/primitive/stmt_import.md @@ -10,9 +10,6 @@ module [] import json.Json [foo, BAR] ~~~ # PROBLEMS -**NOT IMPLEMENTED** -This feature is not yet implemented: top-level import - **INVALID STATEMENT** The statement **expr** is not allowed at the top level. Only definitions, type annotations, and imports are allowed at the top level. @@ -41,7 +38,12 @@ import json.Json[foo, BAR] ~~~ # CANONICALIZE ~~~clojure -(can_ir "empty") +(can_ir + (s_import (3:1-3:17) + "json.Json" + "" + "" + (exposes))) ~~~ # TYPES ~~~clojure diff --git a/src/snapshots/simple_external_lookup.md b/src/snapshots/simple_external_lookup.md new file mode 100644 index 0000000000..ba320a4ed7 --- /dev/null +++ b/src/snapshots/simple_external_lookup.md @@ -0,0 +1,34 @@ +# META +~~~ini +description=Simple external declaration lookup +type=expr +~~~ +# SOURCE +~~~roc +List.map +~~~ +# PROBLEMS +**UNDEFINED VARIABLE** +Nothing is named `map` in this scope. +Is there an `import` or `exposing` missing up-top? + +# TOKENS +~~~zig +UpperIdent(1:1-1:5),NoSpaceDotLowerIdent(1:5-1:9),EndOfFile(1:9-1:9), +~~~ +# PARSE +~~~clojure +(ident (1:1-1:9) "List" ".map") +~~~ +# FORMATTED +~~~roc +NO CHANGE +~~~ +# CANONICALIZE +~~~clojure +(e_runtime_error (1:1-1:9) "ident_not_in_scope") +~~~ +# TYPES +~~~clojure +(expr 73 (type "Error")) +~~~ \ No newline at end of file diff --git a/src/snapshots/simple_module_no_blanks.md b/src/snapshots/simple_module_no_blanks.md index be08101ec6..f1efe96e92 100644 --- a/src/snapshots/simple_module_no_blanks.md +++ b/src/snapshots/simple_module_no_blanks.md @@ -11,13 +11,7 @@ hello! = Stdout.line!("Hello") world = "World" ~~~ # PROBLEMS -**NOT IMPLEMENTED** -This feature is not yet implemented: top-level import - -**UNDEFINED VARIABLE** -Nothing is named `line!` in this scope. -Is there an `import` or `exposing` missing up-top? - +NIL # TOKENS ~~~zig KwModule(1:1-1:7),OpenSquare(1:8-1:9),LowerIdent(1:9-1:15),Comma(1:15-1:16),LowerIdent(1:17-1:22),CloseSquare(1:22-1:23),Newline(1:1-1:1), @@ -57,7 +51,13 @@ NO CHANGE (ident "hello!"))) (def_expr (e_call (3:10-3:31) - (e_runtime_error (3:10-3:22) "ident_not_in_scope") + (e_lookup_external + (external_decl (3:10-3:22) + (qualified_name "pf.Stdout.line!") + (module_name "pf.Stdout") + (local_name "line!") + (kind "value") + (type_var 74))) (e_string (3:23-3:30) (e_literal (3:24-3:29) "Hello"))))) (d_let (def_pattern @@ -65,7 +65,12 @@ NO CHANGE (pid 80) (ident "world"))) (def_expr - (e_string (4:9-4:16) (e_literal (4:10-4:15) "World"))))) + (e_string (4:9-4:16) (e_literal (4:10-4:15) "World")))) + (s_import (2:1-2:17) + "pf.Stdout" + "" + "" + (exposes))) ~~~ # TYPES ~~~clojure diff --git a/src/snapshots/syntax_grab_bag.md b/src/snapshots/syntax_grab_bag.md index dbdbecd112..52b01e95df 100644 --- a/src/snapshots/syntax_grab_bag.md +++ b/src/snapshots/syntax_grab_bag.md @@ -305,19 +305,12 @@ This type is referenced here: **NOT IMPLEMENTED** -This feature is not yet implemented: top-level import +This feature is not yet implemented: Exposed item 'line!' already imported from module 'pf.Stdout', cannot import again from module 'pf # Comment after qualifier + .StdoutMultiline' **NOT IMPLEMENTED** -This feature is not yet implemented: top-level import - -**NOT IMPLEMENTED** -This feature is not yet implemented: top-level import - -**NOT IMPLEMENTED** -This feature is not yet implemented: top-level import - -**NOT IMPLEMENTED** -This feature is not yet implemented: top-level import +This feature is not yet implemented: Exposed item 'write!' already imported from module 'pf.Stdout', cannot import again from module 'pf # Comment after qualifier + .StdoutMultiline' **NOT IMPLEMENTED** This feature is not yet implemented: canonicalize if_then_else expression @@ -476,10 +469,6 @@ This feature is not yet implemented: canonicalize suffix_single_question express **NOT IMPLEMENTED** This feature is not yet implemented: canonicalize suffix_single_question expression -**UNDEFINED VARIABLE** -Nothing is named `line!` in this scope. -Is there an `import` or `exposing` missing up-top? - **UNDEFINED VARIABLE** Nothing is named `toStr` in this scope. Is there an `import` or `exposing` missing up-top? @@ -1351,40 +1340,40 @@ NO CHANGE (d_let (def_pattern (p_assign (65:1-65:16) - (pid 172) + (pid 181) (ident "add_one_oneline"))) (def_expr (e_lambda (65:19-67:8) (args (p_assign (65:20-65:23) - (pid 173) + (pid 182) (ident "num"))) (e_runtime_error (1:1-1:1) "not_implemented")))) (d_let (def_pattern (p_assign (68:1-68:8) - (pid 182) + (pid 191) (ident "add_one"))) (def_expr (e_lambda (68:11-78:2) (args (p_assign (68:12-68:15) - (pid 183) + (pid 192) (ident "num"))) (e_block (68:17-78:2) (s_let (69:2-69:11) (p_assign (69:2-69:7) - (pid 184) + (pid 193) (ident "other")) (e_int (69:10-69:11) - (int_var 186) - (precision_var 185) + (int_var 195) + (precision_var 194) (literal "1") (value "TODO") (bound "u8"))) (e_runtime_error (1:1-1:1) "not_implemented")))) (annotation (68:1-68:8) - (signature 198) + (signature 207) (declared_type (fn (67:11-67:21) (ty (67:11-67:14) "U64") @@ -1393,46 +1382,46 @@ NO CHANGE (d_let (def_pattern (p_assign (80:1-80:11) - (pid 201) + (pid 210) (ident "match_time"))) (def_expr (e_lambda (80:14-140:7) (args (p_assign (81:2-81:3) - (pid 202) + (pid 211) (ident "a")) (p_assign (82:2-82:3) - (pid 203) + (pid 212) (ident "b"))) (e_runtime_error (1:1-1:1) "not_implemented")))) (d_let (def_pattern (p_assign (144:1-144:6) - (pid 218) + (pid 227) (ident "main!"))) (def_expr (e_lambda (144:9-196:2) - (args (p_underscore (144:10-144:11) (pid 219))) + (args (p_underscore (144:10-144:11) (pid 228))) (e_block (144:13-196:2) (s_let (145:2-145:17) (p_assign (145:2-145:7) - (pid 220) + (pid 229) (ident "world")) (e_string (145:10-145:17) (e_literal (145:11-145:16) "World"))) (s_var (146:2-147:8) - (pid 227) + (pid 236) (p_assign (146:2-147:8) - (pid 227) + (pid 236) (ident "number")) (e_int (146:15-146:18) - (int_var 225) - (precision_var 224) + (int_var 234) + (precision_var 233) (literal "123") (value "TODO") (bound "u8"))) (s_let (148:2-148:12) (p_assign (148:2-148:5) - (pid 231) + (pid 240) (ident "tag")) (e_tag (148:8-148:12) (ext_var 0) @@ -1441,7 +1430,7 @@ NO CHANGE (s_expr (154:2-155:12) (e_runtime_error (154:2-154:5) "not_implemented")) (s_expr (155:2-158:11) (e_call (155:2-157:3) - (e_lookup (155:2-155:12) (pid 201)) + (e_lookup_local (155:2-155:12) (pid 210)) (e_runtime_error (156:3-156:6) "not_implemented"))) (s_expr (158:2-162:7) (e_call (158:2-161:3) @@ -1449,106 +1438,106 @@ NO CHANGE (e_runtime_error (1:1-1:1) "not_implemented"))) (s_let (164:2-164:31) (p_assign (164:2-164:18) - (pid 253) + (pid 262) (ident "tag_with_payload")) (e_call (164:21-164:31) (e_tag (164:21-164:23) (ext_var 0) (name "Ok") (args "TODO")) - (e_lookup (164:24-164:30) (pid 227)))) + (e_lookup_local (164:24-164:30) (pid 236)))) (s_let (165:2-165:34) (p_assign (165:2-165:14) - (pid 259) + (pid 268) (ident "interpolated")) (e_string (165:17-165:34) (e_literal (165:18-165:25) "Hello, ") - (e_lookup (165:27-165:32) (pid 220)) + (e_lookup_local (165:27-165:32) (pid 229)) (e_literal (165:33-165:33) ""))) (s_let (166:2-173:3) (p_assign (166:2-166:6) - (pid 265) + (pid 274) (ident "list")) (e_list (166:9-173:3) - (elem_var 276) + (elem_var 285) (elems (e_call (167:3-170:4) - (e_lookup (167:3-167:10) (pid 182)) + (e_lookup_local (167:3-167:10) (pid 191)) (e_runtime_error (1:1-1:1) "not_implemented")) (e_int (171:3-171:6) - (int_var 271) - (precision_var 270) + (int_var 280) + (precision_var 279) (literal "456") (value "TODO") (bound "u8")) (e_int (172:3-172:6) - (int_var 274) - (precision_var 273) + (int_var 283) + (precision_var 282) (literal "789") (value "TODO") (bound "u8"))))) (s_let (178:2-178:71) (p_assign (178:2-178:8) - (pid 281) + (pid 290) (ident "record")) (e_runtime_error (1:1-1:1) "not_implemented")) (s_let (179:2-179:68) (p_assign (179:2-179:7) - (pid 285) + (pid 294) (ident "tuple")) (e_tuple (179:10-179:68) - (tuple_var "#312") + (tuple_var "#321") (elems (e_int (179:11-179:14) - (int_var 287) - (precision_var 286) + (int_var 296) + (precision_var 295) (literal "123") (value "TODO") (bound "u8")) (e_string (179:16-179:23) (e_literal (179:17-179:22) "World")) - (e_lookup (179:25-179:28) (pid 231)) + (e_lookup_local (179:25-179:28) (pid 240)) (e_call (179:30-179:39) (e_tag (179:30-179:32) (ext_var 0) (name "Ok") (args "TODO")) - (e_lookup (179:33-179:38) (pid 220))) + (e_lookup_local (179:33-179:38) (pid 229))) (e_tuple (179:41-179:56) - (tuple_var "#299") + (tuple_var "#308") (elems (e_runtime_error (179:42-179:48) "ident_not_in_scope") - (e_lookup (179:50-179:55) (pid 285)))) + (e_lookup_local (179:50-179:55) (pid 294)))) (e_list (179:58-179:67) - (elem_var 310) + (elem_var 319) (elems (e_int (179:59-179:60) - (int_var 302) - (precision_var 301) + (int_var 311) + (precision_var 310) (literal "1") (value "TODO") (bound "u8")) (e_int (179:62-179:63) - (int_var 305) - (precision_var 304) + (int_var 314) + (precision_var 313) (literal "2") (value "TODO") (bound "u8")) (e_int (179:65-179:66) - (int_var 308) - (precision_var 307) + (int_var 317) + (precision_var 316) (literal "3") (value "TODO") (bound "u8"))))))) (s_let (180:2-187:3) (p_assign (180:2-180:17) - (pid 315) + (pid 324) (ident "multiline_tuple")) (e_tuple (180:20-187:3) - (tuple_var "#343") + (tuple_var "#352") (elems (e_int (181:3-181:6) - (int_var 317) - (precision_var 316) + (int_var 326) + (precision_var 325) (literal "123") (value "TODO") (bound "u8")) @@ -1559,41 +1548,41 @@ NO CHANGE (ext_var 0) (name "Ok") (args "TODO")) - (e_lookup (184:6-184:11) (pid 220))) + (e_lookup_local (184:6-184:11) (pid 229))) (e_tuple (185:3-185:18) - (tuple_var "#330") + (tuple_var "#339") (elems (e_runtime_error (185:4-185:10) "ident_not_in_scope") - (e_lookup (185:12-185:17) (pid 285)))) + (e_lookup_local (185:12-185:17) (pid 294)))) (e_list (186:3-186:12) - (elem_var 341) + (elem_var 350) (elems (e_int (186:4-186:5) - (int_var 333) - (precision_var 332) + (int_var 342) + (precision_var 341) (literal "1") (value "TODO") (bound "u8")) (e_int (186:7-186:8) - (int_var 336) - (precision_var 335) + (int_var 345) + (precision_var 344) (literal "2") (value "TODO") (bound "u8")) (e_int (186:10-186:11) - (int_var 339) - (precision_var 338) + (int_var 348) + (precision_var 347) (literal "3") (value "TODO") (bound "u8"))))))) (s_let (188:2-189:23) (p_assign (188:2-188:15) - (pid 346) + (pid 355) (ident "bin_op_result")) (e_runtime_error (188:18-189:23) "not_implemented")) (s_let (189:2-190:8) (p_assign (189:2-189:23) - (pid 410) + (pid 419) (ident "static_dispatch_style")) (e_dot_access (189:26-190:8) (e_dot_access (189:26-189:110) @@ -1604,15 +1593,21 @@ NO CHANGE "unknown")) (s_expr (190:2-191:8) (e_runtime_error (1:1-1:1) "not_implemented")) (e_call (191:2-195:3) - (e_runtime_error (191:2-191:14) "ident_not_in_scope") + (e_lookup_external + (external_decl (191:2-191:14) + (qualified_name "pf.Stdout.line!") + (module_name "pf.Stdout") + (local_name "line!") + (kind "value") + (type_var 429))) (e_string (192:3-194:18) (e_literal (192:4-192:14) "How about ") (e_call (193:4-193:21) (e_runtime_error (193:4-193:13) "ident_not_in_scope") - (e_lookup (193:14-193:20) (pid 227))) + (e_lookup_local (193:14-193:20) (pid 236))) (e_literal (194:4-194:17) " as a string?")))))) (annotation (144:1-144:6) - (signature 442) + (signature 451) (declared_type (fn (143:9-143:38) (apply (143:9-143:21) @@ -1626,11 +1621,11 @@ NO CHANGE (d_let (def_pattern (p_assign (199:1-199:6) - (pid 446) + (pid 455) (ident "empty"))) (def_expr (e_runtime_error (1:1-1:1) "not_implemented")) (annotation (199:1-199:6) - (signature 450) + (signature 459) (declared_type (record (198:9-198:11))))) (s_type_decl (22:1-23:6) (type_header (22:1-22:10) @@ -1743,21 +1738,50 @@ NO CHANGE (apply (63:30-63:38) "Maybe" (ty_var (63:36-63:37) "a")) - "false"))) + "false")) + (s_import (4:1-4:42) + "pf.Stdout" + "" + "" + (exposes (exposed_item "line!") (exposed_item "write!"))) + (s_import (6:1-12:4) + "pf # Comment after qualifier + .StdoutMultiline" + "" + "" + (exposes (exposed_item "line!") (exposed_item "write!"))) + (s_import (14:1-14:82) + "pkg.Something" + "" + "" + (exposes + (exposed_item "func" "function") + (exposed_item "Type" "ValueCategory") + (exposed_item "Custom" "wildcard"))) + (s_import (16:1-16:27) + "BadName" + "" + "" + (exposes)) + (s_import (17:1-20:20) + "BadNameMultiline" + "" + "" + (exposes))) ~~~ # TYPES ~~~clojure (inferred_types (defs - (def "add_one_oneline" 178 (type "*")) - (def "add_one" 200 (type "*")) - (def "match_time" 209 (type "*")) - (def "main!" 444 (type "*")) - (def "empty" 452 (type "Error"))) + (def "add_one_oneline" 187 (type "*")) + (def "add_one" 209 (type "*")) + (def "match_time" 218 (type "*")) + (def "main!" 453 (type "*")) + (def "empty" 461 (type "Error"))) (expressions - (expr (65:19-67:8) 176 (type "*")) - (expr (68:11-78:2) 193 (type "*")) - (expr (80:14-140:7) 206 (type "*")) - (expr (144:9-196:2) 438 (type "*")) - (expr (199:9-199:11) 448 (type "Error")))) + (expr (65:19-67:8) 185 (type "*")) + (expr (68:11-78:2) 202 (type "*")) + (expr (80:14-140:7) 215 (type "*")) + (expr (144:9-196:2) 447 (type "*")) + (expr (199:9-199:11) 457 (type "Error")))) ~~~ \ No newline at end of file diff --git a/src/snapshots/type_alias_parameterized.md b/src/snapshots/type_alias_parameterized.md index e126dfccad..81c71ca481 100644 --- a/src/snapshots/type_alias_parameterized.md +++ b/src/snapshots/type_alias_parameterized.md @@ -107,8 +107,8 @@ NO CHANGE (e_tuple (6:21-6:27) (tuple_var "#97") (elems - (e_lookup (6:22-6:23) (pid 92)) - (e_lookup (6:25-6:26) (pid 91)))))) + (e_lookup_local (6:22-6:23) (pid 92)) + (e_lookup_local (6:25-6:26) (pid 91)))))) (annotation (6:1-6:9) (signature 103) (declared_type @@ -131,7 +131,7 @@ NO CHANGE (e_lambda (8:9-8:27) (args (p_underscore (8:10-8:11) (pid 107))) (e_call (8:13-8:27) - (e_lookup (8:13-8:21) (pid 90)) + (e_lookup_local (8:13-8:21) (pid 90)) (e_int (8:22-8:23) (int_var 110) (precision_var 109) diff --git a/src/snapshots/type_alias_simple.md b/src/snapshots/type_alias_simple.md index 7a06659869..62a297b288 100644 --- a/src/snapshots/type_alias_simple.md +++ b/src/snapshots/type_alias_simple.md @@ -116,7 +116,7 @@ NO CHANGE (e_lambda (8:9-8:25) (args (p_underscore (8:10-8:11) (pid 91))) (e_call (8:13-8:25) - (e_lookup (8:13-8:20) (pid 78)) + (e_lookup_local (8:13-8:20) (pid 78)) (e_int (8:21-8:24) (int_var 94) (precision_var 93) diff --git a/src/snapshots/type_anno_connection.md b/src/snapshots/type_anno_connection.md index 1d5d03dc43..9ab4c8e7e8 100644 --- a/src/snapshots/type_anno_connection.md +++ b/src/snapshots/type_anno_connection.md @@ -73,7 +73,7 @@ NO CHANGE (ident "x"))) (e_binop (4:15-6:10) "add" - (e_lookup (4:15-4:16) (pid 76)) + (e_lookup_local (4:15-4:16) (pid 76)) (e_int (4:19-4:20) (int_var 79) (precision_var 78) @@ -94,7 +94,7 @@ NO CHANGE (ident "my_number"))) (def_expr (e_call (7:13-7:24) - (e_lookup (7:13-7:20) (pid 75)) + (e_lookup_local (7:13-7:20) (pid 75)) (e_int (7:21-7:23) (int_var 93) (precision_var 92) diff --git a/src/snapshots/type_annotation_basic.md b/src/snapshots/type_annotation_basic.md index cda206491a..2da5540794 100644 --- a/src/snapshots/type_annotation_basic.md +++ b/src/snapshots/type_annotation_basic.md @@ -201,7 +201,7 @@ main! = |_| { (p_assign (5:13-5:14) (pid 78) (ident "x"))) - (e_lookup (5:16-5:17) (pid 78)))) + (e_lookup_local (5:16-5:17) (pid 78)))) (annotation (5:1-5:9) (signature 85) (declared_type @@ -226,8 +226,8 @@ main! = |_| { (e_tuple (9:27-9:42) (tuple_var "#103") (elems - (e_lookup (9:28-9:33) (pid 99)) - (e_lookup (9:35-9:41) (pid 100)))))) + (e_lookup_local (9:28-9:33) (pid 99)) + (e_lookup_local (9:35-9:41) (pid 100)))))) (annotation (9:1-9:8) (signature 111) (declared_type @@ -251,7 +251,7 @@ main! = |_| { (ident "n"))) (e_binop (13:14-15:6) "add" - (e_lookup (13:14-13:15) (pid 118)) + (e_lookup_local (13:14-13:15) (pid 118)) (e_int (13:18-13:19) (int_var 121) (precision_var 120) @@ -279,7 +279,7 @@ main! = |_| { (pid 133) (ident "num")) (e_call (17:11-17:23) - (e_lookup (17:11-17:19) (pid 77)) + (e_lookup_local (17:11-17:19) (pid 77)) (e_int (17:20-17:22) (int_var 136) (precision_var 135) @@ -291,29 +291,29 @@ main! = |_| { (pid 140) (ident "text")) (e_call (18:12-18:29) - (e_lookup (18:12-18:20) (pid 77)) + (e_lookup_local (18:12-18:20) (pid 77)) (e_string (18:21-18:28) (e_literal (18:22-18:27) "hello")))) (s_let (21:5-21:30) (p_assign (21:5-21:9) (pid 146) (ident "pair")) (e_call (21:12-21:30) - (e_lookup (21:12-21:19) (pid 98)) - (e_lookup (21:20-21:23) (pid 133)) - (e_lookup (21:25-21:29) (pid 140)))) + (e_lookup_local (21:12-21:19) (pid 98)) + (e_lookup_local (21:20-21:23) (pid 133)) + (e_lookup_local (21:25-21:29) (pid 140)))) (s_let (24:5-24:23) (p_assign (24:5-24:11) (pid 152) (ident "result")) (e_call (24:14-24:23) - (e_lookup (24:14-24:20) (pid 117)) + (e_lookup_local (24:14-24:20) (pid 117)) (e_int (24:21-24:22) (int_var 155) (precision_var 154) (literal "5") (value "TODO") (bound "u8")))) - (e_lookup (26:5-26:11) (pid 152))))))) + (e_lookup_local (26:5-26:11) (pid 152))))))) ~~~ # TYPES ~~~clojure diff --git a/src/snapshots/type_app_complex_nested.md b/src/snapshots/type_app_complex_nested.md index ef8e6eff5f..0403be3b75 100644 --- a/src/snapshots/type_app_complex_nested.md +++ b/src/snapshots/type_app_complex_nested.md @@ -338,7 +338,7 @@ main! = |_| processComplex(Ok([Some(42), None])) (e_lambda (17:9-17:49) (args (p_underscore (17:10-17:11) (pid 140))) (e_call (17:13-17:49) - (e_lookup (17:13-17:27) (pid 98)) + (e_lookup_local (17:13-17:27) (pid 98)) (e_call (17:28-17:48) (e_tag (17:28-17:30) (ext_var 0) diff --git a/src/snapshots/type_app_multiple_args.md b/src/snapshots/type_app_multiple_args.md index 95dd15ef05..70e4fc7208 100644 --- a/src/snapshots/type_app_multiple_args.md +++ b/src/snapshots/type_app_multiple_args.md @@ -110,7 +110,7 @@ NO CHANGE (e_lambda (6:9-6:55) (args (p_underscore (6:10-6:11) (pid 90))) (e_call (6:13-6:55) - (e_lookup (6:13-6:24) (pid 78)) + (e_lookup_local (6:13-6:24) (pid 78)) (e_dot_access (6:25-6:55) (e_call (6:25-6:37) (e_runtime_error (6:25-6:35) "ident_not_in_scope")) "insert" diff --git a/src/snapshots/type_app_nested.md b/src/snapshots/type_app_nested.md index 26923b2a9e..089316eebd 100644 --- a/src/snapshots/type_app_nested.md +++ b/src/snapshots/type_app_nested.md @@ -123,7 +123,7 @@ main! = |_| processNested([]) (e_lambda (6:9-6:30) (args (p_underscore (6:10-6:11) (pid 96))) (e_call (6:13-6:30) - (e_lookup (6:13-6:26) (pid 80)) + (e_lookup_local (6:13-6:26) (pid 80)) (e_list (6:27-6:29) (elem_var 98) (elems))))))) ~~~ # TYPES diff --git a/src/snapshots/type_app_single_arg.md b/src/snapshots/type_app_single_arg.md index 8ff85e697b..2ede9d04a3 100644 --- a/src/snapshots/type_app_single_arg.md +++ b/src/snapshots/type_app_single_arg.md @@ -87,7 +87,7 @@ main! = |_| processList(["one", "two"]) (pid 77) (ident "list"))) (e_dot_access (4:22-6:6) - (e_lookup (4:22-4:26) (pid 77)) + (e_lookup_local (4:22-4:26) (pid 77)) "len"))) (annotation (4:1-4:12) (signature 84) @@ -107,7 +107,7 @@ main! = |_| processList(["one", "two"]) (e_lambda (6:9-6:39) (args (p_underscore (6:10-6:11) (pid 88))) (e_call (6:13-6:39) - (e_lookup (6:13-6:24) (pid 76)) + (e_lookup_local (6:13-6:24) (pid 76)) (e_list (6:25-6:38) (elem_var 94) (elems diff --git a/src/snapshots/type_app_with_vars.md b/src/snapshots/type_app_with_vars.md index 6c21675e63..2e86847be1 100644 --- a/src/snapshots/type_app_with_vars.md +++ b/src/snapshots/type_app_with_vars.md @@ -101,9 +101,9 @@ main! = |_| mapList([1, 2, 3, 4, 5]) (pid 87) (ident "fn"))) (e_dot_access (4:22-6:6) - (e_lookup (4:22-4:26) (pid 86)) + (e_lookup_local (4:22-4:26) (pid 86)) "map" - (e_lookup (4:31-4:33) (pid 87))))) + (e_lookup_local (4:31-4:33) (pid 87))))) (annotation (4:1-4:8) (signature 100) (declared_type @@ -129,7 +129,7 @@ main! = |_| mapList([1, 2, 3, 4, 5]) (e_lambda (6:9-6:33) (args (p_underscore (6:10-6:11) (pid 104))) (e_call (6:13-6:33) - (e_lookup (6:13-6:20) (pid 85)) + (e_lookup_local (6:13-6:20) (pid 85)) (e_list (6:21-6:32) (elem_var 121) (elems diff --git a/src/snapshots/type_application_basic.md b/src/snapshots/type_application_basic.md index ad37d82147..bb74bcab64 100644 --- a/src/snapshots/type_application_basic.md +++ b/src/snapshots/type_application_basic.md @@ -88,7 +88,7 @@ main! = |_| processList(["one", "two", "three"]) (pid 77) (ident "list"))) (e_dot_access (4:22-6:6) - (e_lookup (4:22-4:26) (pid 77)) + (e_lookup_local (4:22-4:26) (pid 77)) "len"))) (annotation (4:1-4:12) (signature 84) @@ -108,7 +108,7 @@ main! = |_| processList(["one", "two", "three"]) (e_lambda (6:9-6:47) (args (p_underscore (6:10-6:11) (pid 88))) (e_call (6:13-6:47) - (e_lookup (6:13-6:24) (pid 76)) + (e_lookup_local (6:13-6:24) (pid 76)) (e_list (6:25-6:46) (elem_var 96) (elems diff --git a/src/snapshots/type_function_basic.md b/src/snapshots/type_function_basic.md index e1074adf07..3eeb0e2058 100644 --- a/src/snapshots/type_function_basic.md +++ b/src/snapshots/type_function_basic.md @@ -108,8 +108,8 @@ main! = |_| {} (pid 82) (ident "x"))) (e_call (4:17-4:22) - (e_lookup (4:17-4:19) (pid 81)) - (e_lookup (4:20-4:21) (pid 82))))) + (e_lookup_local (4:17-4:19) (pid 81)) + (e_lookup_local (4:20-4:21) (pid 82))))) (annotation (4:1-4:6) (signature 92) (declared_type diff --git a/src/snapshots/type_function_effectful.md b/src/snapshots/type_function_effectful.md index 6aab72897d..c10dbda05b 100644 --- a/src/snapshots/type_function_effectful.md +++ b/src/snapshots/type_function_effectful.md @@ -127,8 +127,8 @@ main! = |_| {} (pid 84) (ident "x"))) (e_call (4:23-4:29) - (e_lookup (4:23-4:26) (pid 83)) - (e_lookup (4:27-4:28) (pid 84))))) + (e_lookup_local (4:23-4:26) (pid 83)) + (e_lookup_local (4:27-4:28) (pid 84))))) (annotation (4:1-4:11) (signature 94) (declared_type diff --git a/src/snapshots/type_function_multi_arg.md b/src/snapshots/type_function_multi_arg.md index ed265a4ab1..0b1fa6f7ec 100644 --- a/src/snapshots/type_function_multi_arg.md +++ b/src/snapshots/type_function_multi_arg.md @@ -122,9 +122,9 @@ main! = |_| {} (pid 85) (ident "y"))) (e_call (4:22-4:30) - (e_lookup (4:22-4:24) (pid 83)) - (e_lookup (4:25-4:26) (pid 84)) - (e_lookup (4:28-4:29) (pid 85))))))) + (e_lookup_local (4:22-4:24) (pid 83)) + (e_lookup_local (4:25-4:26) (pid 84)) + (e_lookup_local (4:28-4:29) (pid 85))))))) (annotation (4:1-4:6) (signature 100) (declared_type diff --git a/src/snapshots/type_function_simple.md b/src/snapshots/type_function_simple.md index ab960adf9b..0d8e150be7 100644 --- a/src/snapshots/type_function_simple.md +++ b/src/snapshots/type_function_simple.md @@ -108,8 +108,8 @@ main! = |_| {} (pid 82) (ident "x"))) (e_call (4:17-4:22) - (e_lookup (4:17-4:19) (pid 81)) - (e_lookup (4:20-4:21) (pid 82))))) + (e_lookup_local (4:17-4:19) (pid 81)) + (e_lookup_local (4:20-4:21) (pid 82))))) (annotation (4:1-4:6) (signature 92) (declared_type diff --git a/src/snapshots/type_higher_order_multiple_vars.md b/src/snapshots/type_higher_order_multiple_vars.md index e76d19f6b9..6516202b62 100644 --- a/src/snapshots/type_higher_order_multiple_vars.md +++ b/src/snapshots/type_higher_order_multiple_vars.md @@ -149,10 +149,10 @@ main! = |_| {} (pid 85) (ident "x"))) (e_call (4:22-4:29) - (e_lookup (4:22-4:23) (pid 83)) + (e_lookup_local (4:22-4:23) (pid 83)) (e_call (4:24-4:28) - (e_lookup (4:24-4:25) (pid 84)) - (e_lookup (4:26-4:27) (pid 85))))))) + (e_lookup_local (4:24-4:25) (pid 84)) + (e_lookup_local (4:26-4:27) (pid 85))))))) (annotation (4:1-4:8) (signature 98) (declared_type diff --git a/src/snapshots/type_multiple_aliases.md b/src/snapshots/type_multiple_aliases.md index 3b973c3df6..68430e7e7f 100644 --- a/src/snapshots/type_multiple_aliases.md +++ b/src/snapshots/type_multiple_aliases.md @@ -219,7 +219,7 @@ main! = |_| { (pid 117) (ident "user"))) (e_dot_access (12:22-14:6) - (e_lookup (12:22-12:26) (pid 117)) + (e_lookup_local (12:22-12:26) (pid 117)) "name"))) (annotation (12:1-12:12) (signature 124) @@ -242,7 +242,7 @@ main! = |_| { (pid 129) (ident "user")) (e_call (15:12-15:40) - (e_lookup (15:12-15:22) (pid 95)) + (e_lookup_local (15:12-15:22) (pid 95)) (e_int (15:23-15:26) (int_var 132) (precision_var 131) @@ -257,8 +257,8 @@ main! = |_| { (value "TODO") (bound "u8")))) (e_call (16:5-16:22) - (e_lookup (16:5-16:16) (pid 116)) - (e_lookup (16:17-16:21) (pid 129))))))) + (e_lookup_local (16:5-16:16) (pid 116)) + (e_lookup_local (16:17-16:21) (pid 129))))))) (s_type_decl (3:1-4:9) (type_header (3:1-3:7) "UserId") (ty (3:10-3:13) "U64")) diff --git a/src/snapshots/type_record_basic.md b/src/snapshots/type_record_basic.md index 1954d91a06..8760409c36 100644 --- a/src/snapshots/type_record_basic.md +++ b/src/snapshots/type_record_basic.md @@ -104,7 +104,7 @@ main! = |_| getName({ name: "luke", age: 21 }) (e_lambda (6:9-6:44) (args (p_underscore (6:10-6:11) (pid 91))) (e_call (6:13-6:44) - (e_lookup (6:13-6:20) (pid 79)) + (e_lookup_local (6:13-6:20) (pid 79)) (e_runtime_error (1:1-1:1) "not_implemented")))))) ~~~ # TYPES diff --git a/src/snapshots/type_record_simple.md b/src/snapshots/type_record_simple.md index 9ad90baa75..7289dbb6c5 100644 --- a/src/snapshots/type_record_simple.md +++ b/src/snapshots/type_record_simple.md @@ -84,7 +84,7 @@ main! = |_| {} (pid 80) (ident "person"))) (e_dot_access (4:20-6:6) - (e_lookup (4:20-4:26) (pid 80)) + (e_lookup_local (4:20-4:26) (pid 80)) "name"))) (annotation (4:1-4:8) (signature 87) diff --git a/src/snapshots/type_record_with_vars.md b/src/snapshots/type_record_with_vars.md index 4da8980a9a..eef635ef0d 100644 --- a/src/snapshots/type_record_with_vars.md +++ b/src/snapshots/type_record_with_vars.md @@ -100,7 +100,7 @@ main! = |_| {} (pid 82) (ident "record"))) (e_dot_access (4:21-6:6) - (e_lookup (4:21-4:27) (pid 82)) + (e_lookup_local (4:21-4:27) (pid 82)) "field"))) (annotation (4:1-4:9) (signature 90) diff --git a/src/snapshots/type_same_var_multiple_uses.md b/src/snapshots/type_same_var_multiple_uses.md index a616c5aa50..e466afb9ae 100644 --- a/src/snapshots/type_same_var_multiple_uses.md +++ b/src/snapshots/type_same_var_multiple_uses.md @@ -79,8 +79,8 @@ NO CHANGE (e_tuple (4:12-4:18) (tuple_var "#84") (elems - (e_lookup (4:13-4:14) (pid 81)) - (e_lookup (4:16-4:17) (pid 81)))))) + (e_lookup_local (4:13-4:14) (pid 81)) + (e_lookup_local (4:16-4:17) (pid 81)))))) (annotation (4:1-4:5) (signature 91) (declared_type diff --git a/src/snapshots/type_simple_type_var.md b/src/snapshots/type_simple_type_var.md index b3320c58e6..b9b5d77e3e 100644 --- a/src/snapshots/type_simple_type_var.md +++ b/src/snapshots/type_simple_type_var.md @@ -72,7 +72,7 @@ NO CHANGE (p_assign (4:13-4:14) (pid 78) (ident "x"))) - (e_lookup (4:16-4:17) (pid 78)))) + (e_lookup_local (4:16-4:17) (pid 78)))) (annotation (4:1-4:9) (signature 85) (declared_type diff --git a/src/snapshots/type_two_variables.md b/src/snapshots/type_two_variables.md index 790101c75e..2bee04741d 100644 --- a/src/snapshots/type_two_variables.md +++ b/src/snapshots/type_two_variables.md @@ -91,8 +91,8 @@ NO CHANGE (e_tuple (4:17-4:23) (tuple_var "#90") (elems - (e_lookup (4:18-4:19) (pid 85)) - (e_lookup (4:21-4:22) (pid 84)))))) + (e_lookup_local (4:18-4:19) (pid 85)) + (e_lookup_local (4:21-4:22) (pid 84)))))) (annotation (4:1-4:5) (signature 96) (declared_type diff --git a/src/snapshots/type_var_basic.md b/src/snapshots/type_var_basic.md index 71fd39b09f..21a1f21fde 100644 --- a/src/snapshots/type_var_basic.md +++ b/src/snapshots/type_var_basic.md @@ -74,7 +74,7 @@ NO CHANGE (p_assign (5:13-5:14) (pid 78) (ident "a"))) - (e_lookup (5:16-5:17) (pid 78)))) + (e_lookup_local (5:16-5:17) (pid 78)))) (annotation (5:1-5:9) (signature 85) (declared_type diff --git a/src/snapshots/type_var_multiple.md b/src/snapshots/type_var_multiple.md index 24f7b0f23b..4cfe31b0bb 100644 --- a/src/snapshots/type_var_multiple.md +++ b/src/snapshots/type_var_multiple.md @@ -139,7 +139,7 @@ main! = |_| {} (e_runtime_error (6:6-6:11) "ident_not_in_scope") (e_runtime_error (6:13-6:19) "ident_not_in_scope")))) (s_expr (6:23-7:6) - (e_lookup (6:23-6:27) (pid 84))) + (e_lookup_local (6:23-6:27) (pid 84))) (e_tuple (7:5-7:20) (tuple_var "#98") (elems diff --git a/src/snapshots/type_var_namespace.md b/src/snapshots/type_var_namespace.md index d851b4a4df..ca31fe6f18 100644 --- a/src/snapshots/type_var_namespace.md +++ b/src/snapshots/type_var_namespace.md @@ -176,12 +176,12 @@ main! = |_| {} (ident "result")) (e_call (11:14-11:30) (e_runtime_error (11:14-11:24) "ident_not_in_scope") - (e_lookup (11:25-11:29) (pid 79)))) + (e_lookup_local (11:25-11:29) (pid 79)))) (s_expr (11:34-13:11) (e_call (11:34-11:58) (e_runtime_error (11:34-11:52) "ident_not_in_scope") - (e_lookup (11:53-11:57) (pid 80)))) - (e_lookup (13:5-13:11) (pid 89))))) + (e_lookup_local (11:53-11:57) (pid 80)))) + (e_lookup_local (13:5-13:11) (pid 89))))) (annotation (5:1-5:8) (signature 107) (declared_type diff --git a/src/snapshots/type_var_nested.md b/src/snapshots/type_var_nested.md index 3d1a59e2d8..367be4cdf2 100644 --- a/src/snapshots/type_var_nested.md +++ b/src/snapshots/type_var_nested.md @@ -198,7 +198,7 @@ main! = |_| {} (e_block (5:33-9:2) (s_expr (6:5-6:16) (e_runtime_error (6:5-6:9) "ident_not_in_scope")) (s_expr (6:10-6:19) - (e_lookup (6:10-6:16) (pid 90))) + (e_lookup_local (6:10-6:16) (pid 90))) (s_expr (6:17-7:11) (e_runtime_error (6:17-6:19) "ident_not_in_scope")) (s_expr (7:9-8:12) (e_runtime_error (1:1-1:1) "not_implemented")) (e_runtime_error (1:1-1:1) "not_implemented")))) @@ -234,7 +234,7 @@ main! = |_| {} (e_block (13:23-15:2) (e_call (14:5-14:23) (e_runtime_error (14:5-14:17) "ident_not_in_scope") - (e_lookup (14:18-14:22) (pid 128)))))) + (e_lookup_local (14:18-14:22) (pid 128)))))) (annotation (13:1-13:13) (signature 138) (declared_type diff --git a/src/snapshots/unused_vars_block.md b/src/snapshots/unused_vars_block.md index 424ad6cb75..3fa221f865 100644 --- a/src/snapshots/unused_vars_block.md +++ b/src/snapshots/unused_vars_block.md @@ -184,14 +184,14 @@ main! = |_| { (ident "result")) (e_binop (17:14-18:11) "add" - (e_lookup (17:14-17:22) (pid 79)) + (e_lookup_local (17:14-17:22) (pid 79)) (e_int (17:25-17:27) (int_var 96) (precision_var 95) (literal "10") (value "TODO") (bound "u8")))) - (e_lookup (18:5-18:11) (pid 93))))))) + (e_lookup_local (18:5-18:11) (pid 93))))))) ~~~ # TYPES ~~~clojure diff --git a/src/snapshots/unused_vars_simple.md b/src/snapshots/unused_vars_simple.md index 098ad916f4..ff8c67815e 100644 --- a/src/snapshots/unused_vars_simple.md +++ b/src/snapshots/unused_vars_simple.md @@ -202,7 +202,7 @@ main! = |_| { (p_assign (7:20-7:26) (pid 81) (ident "_value"))) - (e_lookup (7:28-7:34) (pid 81))))) + (e_lookup_local (7:28-7:34) (pid 81))))) (d_let (def_pattern (p_assign (10:1-10:18) @@ -233,7 +233,7 @@ main! = |_| { (ident "number"))) (e_binop (13:25-15:6) "add" - (e_lookup (13:25-13:31) (pid 94)) + (e_lookup_local (13:25-13:31) (pid 94)) (e_int (13:34-13:35) (int_var 97) (precision_var 96) @@ -254,7 +254,7 @@ main! = |_| { (pid 104) (ident "a")) (e_call (16:9-16:26) - (e_lookup (16:9-16:23) (pid 72)) + (e_lookup_local (16:9-16:23) (pid 72)) (e_int (16:24-16:25) (int_var 107) (precision_var 106) @@ -266,7 +266,7 @@ main! = |_| { (pid 111) (ident "b")) (e_call (17:9-17:28) - (e_lookup (17:9-17:24) (pid 80)) + (e_lookup_local (17:9-17:24) (pid 80)) (e_int (17:25-17:27) (int_var 114) (precision_var 113) @@ -278,7 +278,7 @@ main! = |_| { (pid 118) (ident "c")) (e_call (18:9-18:30) - (e_lookup (18:9-18:26) (pid 86)) + (e_lookup_local (18:9-18:26) (pid 86)) (e_int (18:27-18:29) (int_var 121) (precision_var 120) @@ -290,7 +290,7 @@ main! = |_| { (pid 125) (ident "d")) (e_call (19:9-19:25) - (e_lookup (19:9-19:21) (pid 93)) + (e_lookup_local (19:9-19:21) (pid 93)) (e_int (19:22-19:24) (int_var 128) (precision_var 127) @@ -299,14 +299,14 @@ main! = |_| { (bound "u8")))) (e_binop (20:5-21:2) "add" - (e_lookup (20:5-20:6) (pid 104)) + (e_lookup_local (20:5-20:6) (pid 104)) (e_binop (20:9-21:2) "add" - (e_lookup (20:9-20:10) (pid 111)) + (e_lookup_local (20:9-20:10) (pid 111)) (e_binop (20:13-21:2) "add" - (e_lookup (20:13-20:14) (pid 118)) - (e_lookup (20:17-20:18) (pid 125)))))))))) + (e_lookup_local (20:13-20:14) (pid 118)) + (e_lookup_local (20:17-20:18) (pid 125)))))))))) ~~~ # TYPES ~~~clojure diff --git a/src/snapshots/where_clauses_10.md b/src/snapshots/where_clauses_10.md index a4b6517229..97bae347ec 100644 --- a/src/snapshots/where_clauses_10.md +++ b/src/snapshots/where_clauses_10.md @@ -16,9 +16,7 @@ decodeThings # After member name a.Decode, ~~~ # PROBLEMS -**NOT IMPLEMENTED** -This feature is not yet implemented: top-level import - +NIL # TOKENS ~~~zig KwModule(1:1-1:7),OpenSquare(1:8-1:9),LowerIdent(1:9-1:15),CloseSquare(1:15-1:16),Newline(1:1-1:1), @@ -58,7 +56,12 @@ NO CHANGE ~~~ # CANONICALIZE ~~~clojure -(can_ir "empty") +(can_ir + (s_import (3:1-3:32) + "Decode" + "" + "" + (exposes (exposed_item "Decode")))) ~~~ # TYPES ~~~clojure diff --git a/src/snapshots/where_clauses_4.md b/src/snapshots/where_clauses_4.md index 9b504dccbf..dd6b52b9d4 100644 --- a/src/snapshots/where_clauses_4.md +++ b/src/snapshots/where_clauses_4.md @@ -13,9 +13,7 @@ decodeThings : List(List(U8)) -> List(a) where a.Decode ~~~ # PROBLEMS -**NOT IMPLEMENTED** -This feature is not yet implemented: top-level import - +NIL # TOKENS ~~~zig KwModule(1:1-1:7),OpenSquare(1:8-1:9),LowerIdent(1:9-1:15),CloseSquare(1:15-1:16),Newline(1:1-1:1), @@ -52,7 +50,12 @@ NO CHANGE ~~~ # CANONICALIZE ~~~clojure -(can_ir "empty") +(can_ir + (s_import (3:1-3:32) + "Decode" + "" + "" + (exposes (exposed_item "Decode")))) ~~~ # TYPES ~~~clojure