diff --git a/src/build/roc/Builtin.roc b/src/build/roc/Builtin.roc index c550332013..c36b5a4601 100644 --- a/src/build/roc/Builtin.roc +++ b/src/build/roc/Builtin.roc @@ -1,12 +1,12 @@ -Builtin := [].{ - Str := [ProvidedByCompiler].{ +Builtin :: [].{ + Str :: [ProvidedByCompiler].{ is_empty : Str -> Bool contains : Str, Str -> Bool contains = |_str, _other| True } - List := [ProvidedByCompiler].{ + List :: [ProvidedByCompiler].{ len : List(_item) -> U64 is_empty : List(_item) -> Bool concat : List(item), List(item) -> List(item) @@ -90,17 +90,25 @@ Builtin := [].{ #} } - Dict := [EmptyDict].{} + Dict :: [EmptyDict].{} - Set(item) := [].{ + Set(item) :: [].{ is_empty : Set(item) -> Bool is_eq : Set(item), Set(item) -> Bool is_eq = |_a, _b| Bool.False } - Num := {}.{ - U8 := [].{ + Num :: {}.{ + NumLiteral :: [Self(Bool)].{ + is_negative : NumLiteral -> Bool + is_negative = |self| match self { + # TODO make this a nominal record once we have those + Self(is_negative) => is_negative + } + } + + U8 :: [].{ is_zero : U8 -> Bool is_eq : U8, U8 -> Bool is_gt : U8, U8 -> Bool @@ -115,9 +123,10 @@ Builtin := [].{ rem_by : U8, U8 -> U8 from_int_digits : List(U8) -> Try(U8, [OutOfRange]) + from_num_literal : NumLiteral -> Try(U8, [InvalidNumLiteral(Str)]) } - I8 := [].{ + I8 :: [].{ is_zero : I8 -> Bool is_negative : I8 -> Bool is_positive : I8 -> Bool @@ -135,9 +144,10 @@ Builtin := [].{ rem_by : I8, I8 -> I8 from_int_digits : List(U8) -> Try(I8, [OutOfRange]) + from_num_literal : NumLiteral -> Try(I8, [InvalidNumLiteral(Str)]) } - U16 := [].{ + U16 :: [].{ is_zero : U16 -> Bool is_eq : U16, U16 -> Bool is_gt : U16, U16 -> Bool @@ -152,9 +162,10 @@ Builtin := [].{ rem_by : U16, U16 -> U16 from_int_digits : List(U8) -> Try(U16, [OutOfRange]) + from_num_literal : NumLiteral -> Try(U16, [InvalidNumLiteral(Str)]) } - I16 := [].{ + I16 :: [].{ is_zero : I16 -> Bool is_negative : I16 -> Bool is_positive : I16 -> Bool @@ -172,9 +183,10 @@ Builtin := [].{ rem_by : I16, I16 -> I16 from_int_digits : List(U8) -> Try(I16, [OutOfRange]) + from_num_literal : NumLiteral -> Try(I16, [InvalidNumLiteral(Str)]) } - U32 := [].{ + U32 :: [].{ is_zero : U32 -> Bool is_eq : U32, U32 -> Bool is_gt : U32, U32 -> Bool @@ -189,9 +201,10 @@ Builtin := [].{ rem_by : U32, U32 -> U32 from_int_digits : List(U8) -> Try(U32, [OutOfRange]) + from_num_literal : NumLiteral -> Try(U32, [InvalidNumLiteral(Str)]) } - I32 := [].{ + I32 :: [].{ is_zero : I32 -> Bool is_negative : I32 -> Bool is_positive : I32 -> Bool @@ -209,9 +222,10 @@ Builtin := [].{ rem_by : I32, I32 -> I32 from_int_digits : List(U8) -> Try(I32, [OutOfRange]) + from_num_literal : NumLiteral -> Try(I32, [InvalidNumLiteral(Str)]) } - U64 := [].{ + U64 :: [].{ is_zero : U64 -> Bool is_eq : U64, U64 -> Bool is_gt : U64, U64 -> Bool @@ -226,9 +240,10 @@ Builtin := [].{ rem_by : U64, U64 -> U64 from_int_digits : List(U8) -> Try(U64, [OutOfRange]) + from_num_literal : NumLiteral -> Try(U64, [InvalidNumLiteral(Str)]) } - I64 := [].{ + I64 :: [].{ is_zero : I64 -> Bool is_negative : I64 -> Bool is_positive : I64 -> Bool @@ -246,9 +261,10 @@ Builtin := [].{ rem_by : I64, I64 -> I64 from_int_digits : List(U8) -> Try(I64, [OutOfRange]) + from_num_literal : NumLiteral -> Try(I64, [InvalidNumLiteral(Str)]) } - U128 := [].{ + U128 :: [].{ is_zero : U128 -> Bool is_eq : U128, U128 -> Bool is_gt : U128, U128 -> Bool @@ -263,9 +279,10 @@ Builtin := [].{ rem_by : U128, U128 -> U128 from_int_digits : List(U8) -> Try(U128, [OutOfRange]) + from_num_literal : NumLiteral -> Try(U128, [InvalidNumLiteral(Str)]) } - I128 := [].{ + I128 :: [].{ is_zero : I128 -> Bool is_negative : I128 -> Bool is_positive : I128 -> Bool @@ -283,9 +300,10 @@ Builtin := [].{ rem_by : I128, I128 -> I128 from_int_digits : List(U8) -> Try(I128, [OutOfRange]) + from_num_literal : NumLiteral -> Try(I128, [InvalidNumLiteral(Str)]) } - Dec := [].{ + Dec :: [].{ is_zero : Dec -> Bool is_negative : Dec -> Bool is_positive : Dec -> Bool @@ -305,9 +323,10 @@ Builtin := [].{ from_int_digits : List(U8) -> Try(Dec, [OutOfRange]) from_dec_digits : (List(U8), List(U8)) -> Try(Dec, [OutOfRange]) + from_num_literal : NumLiteral -> Try(Dec, [InvalidNumLiteral(Str)]) } - F32 := [].{ + F32 :: [].{ is_zero : F32 -> Bool is_negative : F32 -> Bool is_positive : F32 -> Bool @@ -325,9 +344,10 @@ Builtin := [].{ from_int_digits : List(U8) -> Try(F32, [OutOfRange]) from_dec_digits : (List(U8), List(U8)) -> Try(F32, [OutOfRange]) + from_num_literal : NumLiteral -> Try(F32, [InvalidNumLiteral(Str)]) } - F64 := [].{ + F64 :: [].{ is_zero : F64 -> Bool is_negative : F64 -> Bool is_positive : F64 -> Bool @@ -345,6 +365,7 @@ Builtin := [].{ from_int_digits : List(U8) -> Try(F64, [OutOfRange]) from_dec_digits : (List(U8), List(U8)) -> Try(F64, [OutOfRange]) + from_num_literal : NumLiteral -> Try(F64, [InvalidNumLiteral(Str)]) } } } diff --git a/src/canonicalize/Can.zig b/src/canonicalize/Can.zig index 2edfc42966..c1a2c71365 100644 --- a/src/canonicalize/Can.zig +++ b/src/canonicalize/Can.zig @@ -562,7 +562,11 @@ fn processAssociatedBlock( try current_scope.introduceTypeAlias(self.env.gpa, user_qualified_ident_idx, qualified_type_decl_idx); } - // Introduce associated items of nested types + // Introduce associated items of nested types into this scope + // Note: Nested types with associated blocks were already fully processed + // during processAssociatedItemsFirstPass (Phase 2b), so these items are + // NOT placeholders - they're fully processed definitions. + // We're just aliasing them into this scope for convenience. if (nested_type_decl.associated) |nested_assoc| { for (self.parse_ir.store.statementSlice(nested_assoc.statements)) |nested_assoc_stmt_idx| { const nested_assoc_stmt = self.parse_ir.store.getStatement(nested_assoc_stmt_idx); @@ -572,7 +576,7 @@ fn processAssociatedBlock( if (nested_pattern == .ident) { const nested_pattern_ident_tok = nested_pattern.ident.ident_tok; if (self.parse_ir.tokens.resolveIdentifier(nested_pattern_ident_tok)) |nested_decl_ident| { - // Build fully qualified name (e.g., "Test.MyBool.my_not") + // Build fully qualified name (e.g., "Builtin.Num.NumLiteral.is_negative") const qualified_text = self.env.getIdent(qualified_ident_idx); const nested_decl_text = self.env.getIdent(nested_decl_ident); const full_qualified_ident_idx = try self.env.insertQualifiedIdent(qualified_text, nested_decl_text); @@ -582,21 +586,15 @@ fn processAssociatedBlock( .found => |pattern_idx| { const scope = &self.scopes.items[self.scopes.items.len - 1]; - // Check if this is a placeholder - const is_placeholder = self.isPlaceholder(full_qualified_ident_idx); + // Just add aliases to scope - don't track as placeholders + // because these were already fully processed - // Add unqualified name (e.g., "my_not") + // Add unqualified name (e.g., "is_negative") try scope.idents.put(self.env.gpa, nested_decl_ident, pattern_idx); - if (is_placeholder) { - try self.placeholder_idents.put(self.env.gpa, nested_decl_ident, {}); - } - // Add type-qualified name (e.g., "MyBool.my_not") + // Add type-qualified name (e.g., "NumLiteral.is_negative") const type_qualified_ident_idx = try self.env.insertQualifiedIdent(nested_type_text, nested_decl_text); try scope.idents.put(self.env.gpa, type_qualified_ident_idx, pattern_idx); - if (is_placeholder) { - try self.placeholder_idents.put(self.env.gpa, type_qualified_ident_idx, {}); - } }, .not_found => {}, } @@ -862,18 +860,25 @@ fn processAssociatedItemsSecondPass( const pattern_idx = def_cir.pattern; const current_scope = &self.scopes.items[self.scopes.items.len - 1]; - // Update unqualified name (e.g., "my_not") - try self.updatePlaceholder(current_scope, decl_ident, pattern_idx); + // Check if this is still a placeholder before updating. + // For nested types with associated blocks (e.g., NumLiteral inside Num), + // the item may have already been fully processed during the recursive + // processAssociatedBlock call in Phase 2b of processAssociatedItemsFirstPass. + // In that case, the placeholder was already consumed and we should skip it. + if (self.isPlaceholder(decl_ident)) { + // Update unqualified name (e.g., "my_not") + try self.updatePlaceholder(current_scope, decl_ident, pattern_idx); - // Update type-qualified name (e.g., "MyBool.my_not") - const type_qualified_idx = try self.env.insertQualifiedIdent(self.env.getIdent(parent_type_name), decl_text); - if (type_qualified_idx.idx != decl_ident.idx) { - try self.updatePlaceholder(current_scope, type_qualified_idx, pattern_idx); - } + // Update type-qualified name (e.g., "MyBool.my_not") + const type_qualified_idx = try self.env.insertQualifiedIdent(self.env.getIdent(parent_type_name), decl_text); + if (type_qualified_idx.idx != decl_ident.idx) { + try self.updatePlaceholder(current_scope, type_qualified_idx, pattern_idx); + } - // Update fully qualified name (e.g., "Test.MyBool.my_not") - if (qualified_idx.idx != type_qualified_idx.idx and qualified_idx.idx != decl_ident.idx) { - try self.updatePlaceholder(current_scope, qualified_idx, pattern_idx); + // Update fully qualified name (e.g., "Test.MyBool.my_not") + if (qualified_idx.idx != type_qualified_idx.idx and qualified_idx.idx != decl_ident.idx) { + try self.updatePlaceholder(current_scope, qualified_idx, pattern_idx); + } } break :blk true; // Found and processed matching decl diff --git a/src/layout/layout.zig b/src/layout/layout.zig index 0f24f8f586..28a9805172 100644 --- a/src/layout/layout.zig +++ b/src/layout/layout.zig @@ -323,6 +323,11 @@ pub const Layout = packed struct { return Layout{ .data = .{ .scalar = .{ .data = .{ .frac = precision }, .tag = .frac } }, .tag = .scalar }; } + /// Default number layout (Dec) for unresolved polymorphic number types + pub fn default_num() Layout { + return Layout.frac(.dec); + } + /// bool layout pub fn boolType() Layout { return Layout{ .data = .{ .scalar = .{ .data = .{ .bool = {} }, .tag = .bool } }, .tag = .scalar }; diff --git a/src/layout/store.zig b/src/layout/store.zig index be4c337631..e712292642 100644 --- a/src/layout/store.zig +++ b/src/layout/store.zig @@ -912,23 +912,23 @@ pub const Store = struct { .frac_precision => |precision| break :flat_type Layout.frac(precision), // For polymorphic types, use default precision .num_unbound => |_| { - // TODO: Should we consider requirements here? - break :flat_type Layout.int(types.Num.Int.Precision.default); + // Default unbound number types to Dec + break :flat_type Layout.default_num(); }, .int_unbound => { - // TODO: Should we consider requirements here? - break :flat_type Layout.int(types.Num.Int.Precision.default); + // Default unbound int types to Dec + break :flat_type Layout.default_num(); }, .frac_unbound => { - // TODO: Should we consider requirements here? - break :flat_type Layout.frac(types.Num.Frac.Precision.default); + // Default unbound frac types to Dec + break :flat_type Layout.default_num(); }, .num_poly => |var_| { const next_type = self.types_store.resolveVar(var_).desc.content; if (next_type == .structure and next_type.structure == .num) { num = next_type.structure.num; } else if (next_type == .flex) { - break :flat_type Layout.int(types.Num.Int.Precision.default); + break :flat_type Layout.default_num(); } else { return LayoutError.InvalidRecordExtension; } @@ -938,7 +938,7 @@ pub const Store = struct { if (next_type == .structure and next_type.structure == .num) { num = next_type.structure.num; } else if (next_type == .flex) { - break :flat_type Layout.int(types.Num.Int.Precision.default); + break :flat_type Layout.default_num(); } else { return LayoutError.InvalidRecordExtension; } @@ -948,7 +948,7 @@ pub const Store = struct { if (next_type == .structure and next_type.structure == .num) { num = next_type.structure.num; } else if (next_type == .flex) { - break :flat_type Layout.frac(types.Num.Frac.Precision.default); + break :flat_type Layout.default_num(); } else { return LayoutError.InvalidRecordExtension; } @@ -1315,8 +1315,8 @@ pub const Store = struct { // Flex vars appear in REPL/eval contexts where type constraints haven't been fully solved. // This is a known issue that needs proper constraint solving before layout computation. - // For now, default to I64 for numeric flex vars. - break :blk Layout.int(.i64); + // For now, default to Dec for unresolved polymorphic types. + break :blk Layout.default_num(); }, .rigid => blk: { // First, check if this rigid var is mapped in the TypeScope diff --git a/src/layout/store_test.zig b/src/layout/store_test.zig index b6904bcdf9..a056ca364a 100644 --- a/src/layout/store_test.zig +++ b/src/layout/store_test.zig @@ -83,23 +83,23 @@ test "addTypeVar - default layouts for polymorphic types" { lt.type_scope = TypeScope.init(lt.gpa); defer lt.deinit(); - // Flex number var (Num a) defaults to i128 + // Flex number var (Num a) defaults to Dec const num_var = try lt.type_store.fresh(); const flex_num_var = try lt.type_store.freshFromContent(.{ .structure = .{ .num = .{ .num_poly = num_var } } }); const num_layout_idx = try lt.layout_store.addTypeVar(flex_num_var, <.type_scope); const num_layout = lt.layout_store.getLayout(num_layout_idx); try testing.expect(num_layout.tag == .scalar); - try testing.expect(num_layout.data.scalar.data.int == .i128); + try testing.expect(num_layout.data.scalar.data.frac == .dec); - // Flex int var (Int a) defaults to i128 + // Flex int var (Int a) defaults to Dec const int_var = try lt.type_store.fresh(); const flex_int_var = try lt.type_store.freshFromContent(.{ .structure = .{ .num = .{ .int_poly = int_var } } }); const int_layout_idx = try lt.layout_store.addTypeVar(flex_int_var, <.type_scope); const int_layout = lt.layout_store.getLayout(int_layout_idx); try testing.expect(int_layout.tag == .scalar); - try testing.expect(int_layout.data.scalar.data.int == .i128); + try testing.expect(int_layout.data.scalar.data.frac == .dec); - // Flex frac var (Frac a) defaults to dec + // Flex frac var (Frac a) defaults to Dec const frac_var = try lt.type_store.fresh(); const flex_frac_var = try lt.type_store.freshFromContent(.{ .structure = .{ .num = .{ .frac_poly = frac_var } } }); const frac_layout_idx = try lt.layout_store.addTypeVar(flex_frac_var, <.type_scope); diff --git a/src/parse/Parser.zig b/src/parse/Parser.zig index df5e166bff..5e3bb90006 100644 --- a/src/parse/Parser.zig +++ b/src/parse/Parser.zig @@ -1207,11 +1207,11 @@ fn parseStmtByType(self: *Parser, statementType: StatementType) Error!AST.Statem const start = self.pos; if (statementType == .top_level or statementType == .in_associated_block) { const header = try self.parseTypeHeader(); - if (self.peek() != .OpColon and self.peek() != .OpColonEqual) { + if (self.peek() != .OpColon and self.peek() != .OpColonEqual and self.peek() != .OpDoubleColon) { // Point to the unexpected token (e.g., "U8" in "List U8") return try self.pushMalformed(AST.Statement.Idx, .expected_colon_after_type_annotation, self.pos); } - const kind: AST.TypeDeclKind = if (self.peek() == .OpColonEqual) .nominal else .alias; + const kind: AST.TypeDeclKind = if (self.peek() == .OpColonEqual or self.peek() == .OpDoubleColon) .nominal else .alias; self.advance(); const anno = try self.parseTypeAnno(.not_looking_for_args); const where_clause = try self.parseWhereConstraint(); diff --git a/src/parse/tokenize.zig b/src/parse/tokenize.zig index 3c61158991..f40114e9ba 100644 --- a/src/parse/tokenize.zig +++ b/src/parse/tokenize.zig @@ -117,6 +117,7 @@ pub const Token = struct { OpLessThan, OpEquals, OpColonEqual, + OpDoubleColon, NoSpaceOpQuestion, Comma, @@ -257,6 +258,7 @@ pub const Token = struct { .OpLessThan, .OpEquals, .OpColonEqual, + .OpDoubleColon, .NoSpaceOpQuestion, .Comma, .Dot, @@ -1389,6 +1391,9 @@ pub const Tokenizer = struct { if (self.cursor.peekAt(1) == '=') { self.cursor.pos += 2; try self.pushTokenNormalHere(gpa, .OpColonEqual, start); + } else if (self.cursor.peekAt(1) == ':') { + self.cursor.pos += 2; + try self.pushTokenNormalHere(gpa, .OpDoubleColon, start); } else { self.cursor.pos += 1; try self.pushTokenNormalHere(gpa, .OpColon, start); @@ -2140,6 +2145,11 @@ fn rebuildBufferForTesting(buf: []const u8, tokens: *TokenizedBuffer, alloc: std try buf2.append(':'); try buf2.append('='); }, + .OpDoubleColon => { + std.debug.assert(length == 2); + try buf2.append(':'); + try buf2.append(':'); + }, .Comma => { std.debug.assert(length == 1); diff --git a/test/snapshots/fuzz_crash/fuzz_crash_002.md b/test/snapshots/fuzz_crash/fuzz_crash_002.md index 0125186fcb..80cba11734 100644 --- a/test/snapshots/fuzz_crash/fuzz_crash_002.md +++ b/test/snapshots/fuzz_crash/fuzz_crash_002.md @@ -9,20 +9,13 @@ modu:;::::::::::::::le[% ~~~ # EXPECTED UNEXPECTED TOKEN IN TYPE ANNOTATION - fuzz_crash_002.md:1:6:1:7 -PARSE ERROR - fuzz_crash_002.md:1:7:1:8 -PARSE ERROR - fuzz_crash_002.md:1:8:1:9 -PARSE ERROR - fuzz_crash_002.md:1:9:1:10 -PARSE ERROR - fuzz_crash_002.md:1:10:1:11 -PARSE ERROR - fuzz_crash_002.md:1:11:1:12 -PARSE ERROR - fuzz_crash_002.md:1:12:1:13 -PARSE ERROR - fuzz_crash_002.md:1:13:1:14 -PARSE ERROR - fuzz_crash_002.md:1:14:1:15 -PARSE ERROR - fuzz_crash_002.md:1:15:1:16 -PARSE ERROR - fuzz_crash_002.md:1:16:1:17 -PARSE ERROR - fuzz_crash_002.md:1:17:1:18 -PARSE ERROR - fuzz_crash_002.md:1:18:1:19 -PARSE ERROR - fuzz_crash_002.md:1:19:1:20 -PARSE ERROR - fuzz_crash_002.md:1:20:1:21 +PARSE ERROR - fuzz_crash_002.md:1:7:1:9 +PARSE ERROR - fuzz_crash_002.md:1:9:1:11 +PARSE ERROR - fuzz_crash_002.md:1:11:1:13 +PARSE ERROR - fuzz_crash_002.md:1:13:1:15 +PARSE ERROR - fuzz_crash_002.md:1:15:1:17 +PARSE ERROR - fuzz_crash_002.md:1:17:1:19 +PARSE ERROR - fuzz_crash_002.md:1:19:1:21 PARSE ERROR - fuzz_crash_002.md:1:21:1:23 PARSE ERROR - fuzz_crash_002.md:1:23:1:24 PARSE ERROR - fuzz_crash_002.md:1:24:1:25 @@ -44,154 +37,77 @@ modu:;::::::::::::::le[% A parsing error occurred: `statement_unexpected_token` This is an unexpected parsing error. Please check your syntax. -**fuzz_crash_002.md:1:7:1:8:** +**fuzz_crash_002.md:1:7:1:9:** ```roc modu:;::::::::::::::le[% ``` - ^ + ^^ **PARSE ERROR** A parsing error occurred: `statement_unexpected_token` This is an unexpected parsing error. Please check your syntax. -**fuzz_crash_002.md:1:8:1:9:** +**fuzz_crash_002.md:1:9:1:11:** ```roc modu:;::::::::::::::le[% ``` - ^ + ^^ **PARSE ERROR** A parsing error occurred: `statement_unexpected_token` This is an unexpected parsing error. Please check your syntax. -**fuzz_crash_002.md:1:9:1:10:** +**fuzz_crash_002.md:1:11:1:13:** ```roc modu:;::::::::::::::le[% ``` - ^ + ^^ **PARSE ERROR** A parsing error occurred: `statement_unexpected_token` This is an unexpected parsing error. Please check your syntax. -**fuzz_crash_002.md:1:10:1:11:** +**fuzz_crash_002.md:1:13:1:15:** ```roc modu:;::::::::::::::le[% ``` - ^ + ^^ **PARSE ERROR** A parsing error occurred: `statement_unexpected_token` This is an unexpected parsing error. Please check your syntax. -**fuzz_crash_002.md:1:11:1:12:** +**fuzz_crash_002.md:1:15:1:17:** ```roc modu:;::::::::::::::le[% ``` - ^ + ^^ **PARSE ERROR** A parsing error occurred: `statement_unexpected_token` This is an unexpected parsing error. Please check your syntax. -**fuzz_crash_002.md:1:12:1:13:** +**fuzz_crash_002.md:1:17:1:19:** ```roc modu:;::::::::::::::le[% ``` - ^ + ^^ **PARSE ERROR** A parsing error occurred: `statement_unexpected_token` This is an unexpected parsing error. Please check your syntax. -**fuzz_crash_002.md:1:13:1:14:** +**fuzz_crash_002.md:1:19:1:21:** ```roc modu:;::::::::::::::le[% ``` - ^ - - -**PARSE ERROR** -A parsing error occurred: `statement_unexpected_token` -This is an unexpected parsing error. Please check your syntax. - -**fuzz_crash_002.md:1:14:1:15:** -```roc -modu:;::::::::::::::le[% -``` - ^ - - -**PARSE ERROR** -A parsing error occurred: `statement_unexpected_token` -This is an unexpected parsing error. Please check your syntax. - -**fuzz_crash_002.md:1:15:1:16:** -```roc -modu:;::::::::::::::le[% -``` - ^ - - -**PARSE ERROR** -A parsing error occurred: `statement_unexpected_token` -This is an unexpected parsing error. Please check your syntax. - -**fuzz_crash_002.md:1:16:1:17:** -```roc -modu:;::::::::::::::le[% -``` - ^ - - -**PARSE ERROR** -A parsing error occurred: `statement_unexpected_token` -This is an unexpected parsing error. Please check your syntax. - -**fuzz_crash_002.md:1:17:1:18:** -```roc -modu:;::::::::::::::le[% -``` - ^ - - -**PARSE ERROR** -A parsing error occurred: `statement_unexpected_token` -This is an unexpected parsing error. Please check your syntax. - -**fuzz_crash_002.md:1:18:1:19:** -```roc -modu:;::::::::::::::le[% -``` - ^ - - -**PARSE ERROR** -A parsing error occurred: `statement_unexpected_token` -This is an unexpected parsing error. Please check your syntax. - -**fuzz_crash_002.md:1:19:1:20:** -```roc -modu:;::::::::::::::le[% -``` - ^ - - -**PARSE ERROR** -A parsing error occurred: `statement_unexpected_token` -This is an unexpected parsing error. Please check your syntax. - -**fuzz_crash_002.md:1:20:1:21:** -```roc -modu:;::::::::::::::le[% -``` - ^ + ^^ **PARSE ERROR** @@ -253,7 +169,7 @@ modu:;::::::::::::::le[% # TOKENS ~~~zig -LowerIdent,OpColon,MalformedUnknownToken,OpColon,OpColon,OpColon,OpColon,OpColon,OpColon,OpColon,OpColon,OpColon,OpColon,OpColon,OpColon,OpColon,OpColon,LowerIdent,OpenSquare,OpPercent, +LowerIdent,OpColon,MalformedUnknownToken,OpDoubleColon,OpDoubleColon,OpDoubleColon,OpDoubleColon,OpDoubleColon,OpDoubleColon,OpDoubleColon,LowerIdent,OpenSquare,OpPercent, EndOfFile, ~~~ # PARSE @@ -272,13 +188,6 @@ EndOfFile, (s-malformed (tag "statement_unexpected_token")) (s-malformed (tag "statement_unexpected_token")) (s-malformed (tag "statement_unexpected_token")) - (s-malformed (tag "statement_unexpected_token")) - (s-malformed (tag "statement_unexpected_token")) - (s-malformed (tag "statement_unexpected_token")) - (s-malformed (tag "statement_unexpected_token")) - (s-malformed (tag "statement_unexpected_token")) - (s-malformed (tag "statement_unexpected_token")) - (s-malformed (tag "statement_unexpected_token")) (s-malformed (tag "statement_unexpected_token")))) ~~~ # FORMATTED