Merge pull request #7859 from roc-lang/external-decls

Single module `import` `exposes` `as`
This commit is contained in:
Luke Boswell 2025-06-24 10:28:33 +10:00 committed by GitHub
commit 078ac7c512
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
62 changed files with 2268 additions and 363 deletions

View file

@ -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,

View file

@ -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')

View file

@ -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,

View file

@ -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();

View file

@ -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 = {} };
}

View file

@ -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,

View file

@ -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

View file

@ -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

309
src/snapshots/can_import_comprehensive.md generated Normal file
View file

@ -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 "*"))))
~~~

67
src/snapshots/can_import_json.md generated Normal file
View file

@ -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 "*"))))
~~~

70
src/snapshots/can_import_with_alias.md generated Normal file
View file

@ -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 "*"))))
~~~

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

72
src/snapshots/exposed_items_test.md generated Normal file
View file

@ -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(*))"))))
~~~

View file

@ -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)

View file

@ -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)

View file

@ -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

129
src/snapshots/external_decl_lookup.md generated Normal file
View file

@ -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 "*"))))
~~~

34
src/snapshots/external_lookup_expr.md generated Normal file
View file

@ -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"))
~~~

View file

@ -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

View file

@ -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

132
src/snapshots/import_exposing_alias.md generated Normal file
View file

@ -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 "*"))))
~~~

139
src/snapshots/import_exposing_basic.md generated Normal file
View file

@ -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 "*"))))
~~~

View file

@ -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

View file

@ -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

View file

@ -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

34
src/snapshots/simple_external_lookup.md generated Normal file
View file

@ -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"))
~~~

View file

@ -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

View file

@ -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"))))
~~~

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"))

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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