From dce2f82e1b6a711155e4a9a097e83cd0717b33a6 Mon Sep 17 00:00:00 2001 From: chloe caruso Date: Sat, 1 Jun 2024 21:21:51 -0700 Subject: [PATCH] implement binary operand stuff --- CHANGELOG.md | 3 + build | 10 +- build.cmd | 18 +- build.zig | 28 +-- demo.todo | 10 + ...nguage-reference.md => language-manual.md} | 11 +- src/core/InternPool.zig | 8 + src/core/JsLink.zig | 2 +- src/core/Parse.zig | 37 +++- src/core/Report.zig | 31 ++- src/core/Sema.zig | 182 ++++++++++++++---- src/core/Tokenizer.zig | 3 +- src/langref/template.html | 100 ---------- .../generate-man.zig} | 23 +++ src/manual/template.html | 134 +++++++++++++ 15 files changed, 411 insertions(+), 189 deletions(-) create mode 100644 demo.todo rename doc/{language-reference.md => language-manual.md} (91%) delete mode 100644 src/langref/template.html rename src/{langref/generate-langref.zig => manual/generate-man.zig} (91%) create mode 100644 src/manual/template.html diff --git a/CHANGELOG.md b/CHANGELOG.md index 87933d5cf9da7b6feffdf8fc5791dcc371e034f5..cfd8175aba330b45c6fc9a28a89615b87102ad39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,15 @@ - feat(sema): coersion: numeric\_literal -> float - feat(sema): prevent passing non-runtime compatible types like `type` to opaque functions +- feat(sema): properly validate and coerce binary operations +- feat(sema): string concat with `+` - feat(transform): lhs of assignments now validated - feat(transform): mark useless discard - feat(transform): parse string escapes properly - feat(link): print string escapes properly - feat: implement the `comptime` keyword (many comptime ops are not implemented) - feat: add `@jsNew` to create JavaScript `new` expressions +- fix(parse): top-level if statements previously required semicolons - fix(sema): properly coerce assignments - fix(transform): empty block generated in top level code tripped sema assertion - fix(link): emit parenthesis to preserve operator precedence diff --git a/build b/build index 3c2e1a79d588ca1c4ba3e0d7a5a6d723e87f1ab7..c45b3fcbc8c33908e4212dfb68754946035478df 100755 --- a/build +++ b/build @@ -1,6 +1,6 @@ #!/bin/sh # This script will build the compiler on any POSIX system. -# It automatically installs a Zig compiler to ./zig-cache +# It automatically installs a Zig compiler to ./.zig-cache # if needed. The output will be in ./zig-out/bin. # # `curl` and `tar` is required in order to download and @@ -11,7 +11,7 @@ cd -- "$(dirname -- "$0")" ZIG_VERSION=$(grep .minimum_zig_version build.zig.zon \ | cut -d '"' -f 2) -ZIG="zig-cache/zig/$ZIG_VERSION/zig" +ZIG=".zig-cache/zig/$ZIG_VERSION/zig" if [ ! -f "$ZIG" ]; then OS=$(uname -s | tr '[:upper:]' '[:lower:]') if [ "$OS" = "darwin" ]; then @@ -40,12 +40,12 @@ if [ ! -f "$ZIG" ]; then exit 1 fi - mkdir -p zig-cache/zig/$ZIG_VERSION + mkdir -p .zig-cache/zig/$ZIG_VERSION curl -fsSL --retry 3 --retry-delay 3 $URL \ - | tar -xJ --strip-components=1 -C zig-cache/zig/$ZIG_VERSION + | tar -xJ --strip-components=1 -C .zig-cache/zig/$ZIG_VERSION if [ "$($ZIG version)" != "$ZIG_VERSION" ]; then echo "Zig failed to install." - rm -rf zig-cache/zig/$ZIG_VERSION + rm -rf .zig-cache/zig/$ZIG_VERSION exit 1 fi fi diff --git a/build.cmd b/build.cmd index 07879cf6f0c99cdada497c0caa5fc6fb491beafb..f3c5fff0671365fad1e5a846bcf2ddf5bf593f60 100644 --- a/build.cmd +++ b/build.cmd @@ -1,7 +1,7 @@ @ECHO OFF @REM This script will build the compiler on any Windows system -@REM It automatically installs a Zig compiler to ./zig-cache -@REM The output will be in ./zig-out/bin +@REM It automatically installs a Zig compiler to \/.zig-cache +@REM The output will be in .\zig-out\bin SET ROOT=%~dp0 FOR /F "tokens=3 delims=, " %%i IN ( @@ -9,9 +9,9 @@ FOR /F "tokens=3 delims=, " %%i IN ( "%ROOT%build.zig.zon"' ) DO SET ZIG_VERSION=%%i SET ZIG_VERSION=%ZIG_VERSION:~1,-1% -IF NOT EXIST "%ROOT%zig-cache\zig\%ZIG_VERSION%\zig.exe" GOTO :find_powershell +IF NOT EXIST "%ROOT%.zig-cache\zig\%ZIG_VERSION%\zig.exe" GOTO :find_powershell :build -"%ROOT%zig-cache\zig\%ZIG_VERSION%\zig.exe" build %* +"%ROOT%.zig-cache\zig\%ZIG_VERSION%\zig.exe" build %* EXIT /b %ERRORLEVEL% :find_powershell FOR %%i IN (pwsh powershell) DO WHERE %%i /Q && SET PS=%%i && GOTO :download @@ -20,10 +20,10 @@ EXIT /b 1 :download SET ZIG_ARCH=x86_64 SET URI=https://ziglang.org/builds/zig-windows-%ZIG_ARCH%-%ZIG_VERSION%.zip -SET X=$null = New-Item -ItemType Directory -Force -Path zig-cache\zig -SET X=%X%;Invoke-RestMethod -Uri %URI% -OutFile zig-cache/zig-%ZIG_VERSION%.zip -SET X=%X%;Expand-Archive zig-cache/zig-%ZIG_VERSION%.zip -DestinationPath zig-cache/zig -SET X=%X%;Remove-Item zig-cache/zig-%ZIG_VERSION%.zip +SET X=$null = New-Item -ItemType Directory -Force -Path .zig-cache\zig +SET X=%X%;Invoke-RestMethod -Uri %URI% -OutFile .zig-cache/zig-%ZIG_VERSION%.zip +SET X=%X%;Expand-Archive .zig-cache/zig-%ZIG_VERSION%.zip -DestinationPath .zig-cache/zig +SET X=%X%;Remove-Item .zig-cache/zig-%ZIG_VERSION%.zip %PS% -NoProfile -Command "%X%" -MOVE zig-cache\zig\zig-windows-%ZIG_ARCH%-%ZIG_VERSION% zig-cache\zig\%ZIG_VERSION% >NUL +MOVE .zig-cache\zig\zig-windows-%ZIG_ARCH%-%ZIG_VERSION% .zig-cache\zig\%ZIG_VERSION% >NUL GOTO :build diff --git a/build.zig b/build.zig index 369cb7d7e9f0aeca14d21f02fe1f3ae197e591c5..d6729aaeef876a735eb11a9479a9dbe85e04f4ab 100644 --- a/build.zig +++ b/build.zig @@ -59,22 +59,22 @@ pub fn build(b: *std.Build) void { } // Language Reference Generator - const langref_generator = b.addExecutable(.{ - .name = "generate-langref", - .root_source_file = b.path("src/langref/generate-langref.zig"), + const manual_generator = b.addExecutable(.{ + .name = "generate-man", + .root_source_file = b.path("src/manual/generate-man.zig"), .target = native_target, .optimize = .Debug, .link_libc = true, }); - langref_generator.root_module.addImport("build_options", native_debug.opts_mod); - langref_generator.root_module.addImport("core", native_debug.core); - const langref_run = b.addRunArtifact(langref_generator); - langref_run.addFileArg(native_debug.compiler.getEmittedBin()); - langref_run.addFileArg(b.path("doc/language-reference.md")); - langref_run.addFileArg(b.path("src/langref/template.html")); - const langref_out = langref_run.addOutputFileArg("language-reference.html"); - const install_langref = b.addInstallFile(langref_out, "doc/README.html"); - b.step("langref", "build language reference").dependOn(&install_langref.step); + manual_generator.root_module.addImport("build_options", native_debug.opts_mod); + manual_generator.root_module.addImport("core", native_debug.core); + const manual_run = b.addRunArtifact(manual_generator); + manual_run.addFileArg(native_debug.compiler.getEmittedBin()); + manual_run.addFileArg(b.path("doc/language-manual.md")); + manual_run.addFileArg(b.path("src/manual/template.html")); + const manual_out = manual_run.addOutputFileArg("language-reference.html"); + const install_manual = b.addInstallFile(manual_out, "doc/README.html"); + b.step("manual", "build language reference").dependOn(&install_manual.step); // const wasm_api = b.addExecutable(.{ // .name = "compiler", @@ -137,8 +137,8 @@ pub fn build(b: *std.Build) void { ).step); } - package.dependOn(&b.addInstallFile(b.path("CHANGELOG.md"), "release/CHANGELOG.md").step); - package.dependOn(&b.addInstallFile(langref_out, "release/README.html").step); + package.dependOn(&b.addInstallFile(b.path("CHANGELOG.md"), "package/CHANGELOG.md").step); + package.dependOn(&b.addInstallFile(manual_out, "package/README.html").step); } } diff --git a/demo.todo b/demo.todo new file mode 100644 index 0000000000000000000000000000000000000000..0a2e8fb7b365173c38ff8aa3b1263685f9b2dc96 --- /dev/null +++ b/demo.todo @@ -0,0 +1,10 @@ +fn something(a: int) -> bool { + if a == 0 { + return false; + } + + a > 100 +} + +let print = @js("console").log; +print(something(0xFF), something(0)); diff --git a/doc/language-reference.md b/doc/language-manual.md similarity index 91% rename from doc/language-reference.md rename to doc/language-manual.md index abc1ebb3cb5c98d9b25f568f362dcf66c590d51f..3194dda9d0a3482e8a479808be7cfc9d488e50cc 100644 --- a/doc/language-reference.md +++ b/doc/language-manual.md @@ -1,11 +1,11 @@ # New Language Project -This is a high-level, general purpose programming language. +This is a high-level, general purpose programming language. Key goals of this project are: - **Ease of Use**: Use of type inference keeps code concise and easy to read and write. -- **Fast Iteration**: The compiler is designed for quick incremental compilation and hot module reloading. +- **Fast Iteration**: The compiler is designed for quick incremental compilation and hot module reloading. (note: incremental is not yet available) - TODO: some more It primarily focuses on merging some great points of Rust, Zig, and JavaScript's language designs: @@ -36,6 +36,7 @@ The `print` function is implemented by getting an external reference to the `con | `bool` | `boolean` | either `true` or `false` | | `string` | `string` | a sequence of text characters | | `void` | `undefined` or `null` | detectable lack of a value | +| `opaque` | `unknown` or `any` | could be any value | | `noreturn` | `never` | control flow prevents this value from existing | > **note**: `bigint` is planned but not added @@ -112,7 +113,7 @@ fn add(a: int, b: int) -> int { } let print = @js("console").log; -print("Sum of 2 and 7" + add(2, 7)); +print("Sum of 2 and 7:", add(2, 7)); ``` > **Bug**: Return types are currently required. Omitting the `-> type` syntax currently makes the return type `void`, but this will be changed in the future to be an inferred return type. @@ -142,7 +143,9 @@ TODO ## JavaScript Interop -A global variable handle can be retrived with `@js`. We've seen this in the hello world. The plan is to eventually replace this construct with something else, but implementing a function like this is extremely trivial for the compiler. +A global variable handle can be retrived with `@js`. We've seen this in the hello world. The type of this value is a special type called `opaque`, which disables type-checking. + +> **Note**: A future update will improve on the type-safety of `opaque` Many JS-exclusive actions are exposed via built-in functions, such as using `@jsNew` for calling constructors. diff --git a/src/core/InternPool.zig b/src/core/InternPool.zig index 208fb755e435af2ceb8b49e8ed59881cd7358056..92bac50ea2f5b886022525f8ee2c963ecae801c5 100644 --- a/src/core/InternPool.zig +++ b/src/core/InternPool.zig @@ -288,6 +288,14 @@ pub const Value = enum(u32) { pub fn toFloat(val: Value, ip: *const InternPool) ?f64 { return switch (val.valueTag(ip)) { inline .comptime_float, .float => |tag| val.valueData(ip, tag).get(ip).decode(), + .int32 => @floatFromInt(val.valueData(ip, .int32)), + else => null, + }; + } + + pub fn toInt32(val: Value, ip: *const InternPool) ?i32 { + return switch (val.valueTag(ip)) { + .int32 => val.valueData(ip, .int32), else => null, }; } diff --git a/src/core/JsLink.zig b/src/core/JsLink.zig index 33db47c68d495888b4f0d5e90871f5e64f94430c..7c9975be09458410f563d5ba057867727df1e40e 100644 --- a/src/core/JsLink.zig +++ b/src/core/JsLink.zig @@ -193,10 +193,10 @@ fn printBlockContents( for (list.slice(tree)) |stmt| { try link.printExpr(tree, stmt, writer); switch (tree.nodeTag(stmt)) { - else => try writer.writeAll(";"), .stmt_block, .stmt_if, => {}, + else => try writer.writeAll(";"), } try writer.writeAll("\n"); } diff --git a/src/core/Parse.zig b/src/core/Parse.zig index 2fb2433bc2b0138d1da8f3ecc75211b3c0b5064c..b374d560d6570551f13126969a85e38916915e93 100644 --- a/src/core/Parse.zig +++ b/src/core/Parse.zig @@ -245,8 +245,20 @@ fn parseModuleMembers(p: *Parse) Error!SubList { return p.failUnexpectedToken(.statement); } + const first_tag = p.currentTag(); + try p.scratch.append(p.gpa, try p.parseExpression(true)); - _ = try p.expect(.semi); + + switch (first_tag) { + // some control flow and blocks are allowed to not have a + // trailing semicolons. this is a stylistic choice. + .kw_if, + .start_brace, + => {}, + + // TODO: make semicolons optional, but emit a warning + else => _ = try p.expect(.semi), + } }, }; @@ -423,9 +435,7 @@ fn parseBlockContents(p: *Parse) Error!struct { Node.Tag, SubList } { if (p.currentTag() == .end_brace) break .block_implicit_return; }, - // TODO: make kw_break and kw_return REQUIRE a semicolon, as - // they are noreturn statements, doing implicit break on - // them makes no sense + // TODO: make semicolons optional, but emit a warning else => { if (p.eatToken(.semi) == null) { @@ -456,13 +466,13 @@ fn parseVarDecl(p: *Parse) Error!NodeIndex { _ = try p.expect(.id); const data_type: OptionalNodeIndex = if (p.eatToken(.colon)) |_| - optionalNodeIndex(try p.parseExpression(false)) + optionalNodeIndex(try p.tryParseExpression(false)) else .none; _ = try p.expect(.equal); - const data_init = try p.parseExpression(true); + const data_init = try p.tryParseExpression(true); _ = try p.expect(.semi); @@ -711,6 +721,13 @@ fn parseExpression(p: *Parse, allow_assign_operators: bool) Error!NodeIndex { return expr; } +fn tryParseExpression(p: *Parse, allow_assign_operators: bool) Error!NodeIndex { + return if (p.isExpressionStart()) + p.parseExpression(allow_assign_operators) + else + p.failUnexpectedToken(.expression); +} + fn parseExpressionPart(p: *Parse) Error!NodeIndex { const trace = t.trace(@src()); defer trace.end(); @@ -1040,8 +1057,14 @@ fn fail(p: *Parse, ast_error: AstError) Error { } fn failUnexpectedToken(p: *Parse, kind: ExpectedContext) Error { + var got = p.fullToken(p.pos); + if (got.tag == .eof) { + // note: it is impossible to get a failure at an empty file, so subtraction is fine + const prev = p.fullToken(p.pos - 1); + got.offset = prev.offset + prev.len(p.source); + } return p.fail(.{ .unexpected_context = .{ - .got = p.fullToken(p.pos), + .got = got, .expected = kind, } }); } diff --git a/src/core/Report.zig b/src/core/Report.zig index 808b104619c990956d700d493c006358073c6b30..5cfc4ec59718f2026b2d50a6c3253961e378b679 100644 --- a/src/core/Report.zig +++ b/src/core/Report.zig @@ -247,10 +247,24 @@ pub const Err = union(enum) { node: NodeIndex, fn build(data: @This(), ctx: *MsgContext) !void { + try ctx.msgNode(data.node, "invalid assignment target", .{}); + } + }, + bin_op_mismatch: struct { + src: LazyNode, + lhs: Type, + rhs: Type, + + fn build(data: @This(), ctx: *MsgContext) !void { + const node = try data.src.get(ctx); try ctx.msgNode( - data.node, - "invalid assignment target", - .{}, + node, + "\"{s}\" operator is not compatible with types \"{}\" and \"{}\"", + .{ + ctx.ast.tokenSlice(ctx.ast.nodeToken(node)), + data.lhs.displayName(&ctx.comp.intern_pool), + data.rhs.displayName(&ctx.comp.intern_pool), + }, ); } }, @@ -458,7 +472,13 @@ pub fn formatItem( const file = try comp.getFile(item.url.slice(report)); const need_load_ast = !(item_type == .@"error" and item.data.@"error" == .ast); - const ast_data = if (need_load_ast) try file.getAst(comp) else undefined; + const ast_data = if (need_load_ast) + try file.getAst(comp) + else brk: { + var ast: Ast = undefined; + ast.utf8_source = (try file.getSource(comp.gpa)).code; + break :brk ast; + }; var ctx: MsgContext = .{ .arena = alloc, @@ -700,13 +720,12 @@ pub const Highlight = union(enum) { .byte = token.offset, }; }, - .token_stored => |token| loc: { const location = Tokenizer.tokenLocation(ast.utf8_source, 0, token.offset); break :loc .{ .loc = location, - .slice = token.slice(ast.utf8_source), + .slice = if (token.tag != .eof) token.slice(ast.utf8_source) else "\n", .byte = token.offset, }; }, diff --git a/src/core/Sema.zig b/src/core/Sema.zig index 2ad726b477901e72b2039ac9cc71577a6e9960d6..e6703ac99863aeeada652c1f546b5de985068290 100644 --- a/src/core/Sema.zig +++ b/src/core/Sema.zig @@ -315,28 +315,30 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { .eq, => |tag| { const bin: Teg.Inst.Bin = s.code.instPayload(tag, in); + const op = @field(BinaryOp, @tagName(tag)); const lhs = try s.resolveRefRuntime(bin.lhs); const rhs = try s.resolveRefRuntime(bin.rhs); - if (lhs.kind == .value and rhs.kind == .value) { + const left_ty = s.typeOfRef(lhs); + const right_ty = s.typeOfRef(rhs); + + const ty = try s.binTypeResolution(in, op, left_ty, right_ty); + + const left = try s.coerceRef(in.op(.lhs), lhs, ty); + const right = try s.coerceRef(in.op(.rhs), lhs, ty); + + if (left.kind == .value and right.kind == .value) { return (try s.comptimeBin( - @field(BinaryOp, @tagName(tag)), - lhs.decode().value, - rhs.decode().value, - )); + op, + left.decode().value, + right.decode().value, + )).toAir(); } - const left_type = s.typeOfRef(lhs); - _ = left_type; // autofix - const right_type = s.typeOfRef(rhs); - _ = right_type; // autofix - - // TODO: validate type for operation - return s.addInstRef(tag.airTag(), .{ - .lhs = lhs, - .rhs = rhs, + .lhs = left, + .rhs = right, }); }, inline .negate, .bool_not => |tag| { @@ -881,35 +883,133 @@ const BinaryOp = enum { eq, }; -fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Air.Inst.Ref { +fn binTypeResolution(s: *Sema, src: Teg.Inst.Index, op: BinaryOp, lhs: Type, rhs: Type) !Type { + if (lhs == .@"opaque") return rhs; + if (rhs == .@"opaque") return rhs; + + switch (lhs) { + .string => if (op == .add) switch (rhs) { + .string => return .string, + else => {}, + }, + .numeric_literal => switch (rhs) { + .int32, + .float, + .numeric_literal, + => return rhs, + else => {}, + }, + .int32 => switch (rhs) { + .int32 => return .int32, + .numeric_literal => return .int32, + else => {}, + }, + .float => switch (rhs) { + .int32 => return .float, + .numeric_literal => return .float, + else => {}, + }, + else => {}, + } + + return s.fail(.bin_op_mismatch, .{ + .src = s.nodeLoc(src, .main_node), + .lhs = lhs, + .rhs = rhs, + }); +} + +fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Value { + std.debug.print("{} {s} {}\n", .{ + lhs.dump(s.ip), + @tagName(op), + rhs.dump(s.ip), + }); + + const left_ty = lhs.typeOf(s.ip); + assert(rhs.typeOf(s.ip) == left_ty); + switch (op) { - .add => { - const l = lhs.toFloat(s.ip) orelse @panic("TODO: typecheck / string add"); - const r = rhs.toFloat(s.ip) orelse @panic("TODO: typecheck / string add"); - return (try s.ip.get(s.gpa, .{ - .float = l + r, - })).toAir(); + .add => switch (left_ty) { + .numeric_literal, .float => { + const l = lhs.toFloat(s.ip) orelse + unreachable; // must be coerced + const r = rhs.toFloat(s.ip) orelse + unreachable; // must be coerced + + const result = l + r; + + return try s.makeValue(if (left_ty == .numeric_literal) + .{ .comptime_float = result } + else + .{ .float = result }); + }, + .int32 => { + const l = lhs.toInt32(s.ip) orelse + unreachable; // must be coerced + const r = rhs.toInt32(s.ip) orelse + unreachable; // must be coerced + + const result = std.math.add(i32, l, r) catch + @panic("TODO: out of range"); + + return try s.makeValue(.{ .int32 = result }); + }, + .string => { + const l = lhs.asString(s.ip).toSlice(s.ip); + const r = rhs.asString(s.ip).toSlice(s.ip); + var sfa = std.heap.stackFallback(4096, s.arena); + const alloc = sfa.get(); + const joined = try std.mem.concat(alloc, u8, &.{ l, r }); + defer alloc.free(joined); + return try s.makeValue(.{ .string = joined }); + }, + else => unreachable, }, - inline .sub, .mul, .div => |tag| { - const l = lhs.toFloat(s.ip) orelse @panic("TODO: typecheck"); - const r = rhs.toFloat(s.ip) orelse @panic("TODO: typecheck"); - - const result = switch (tag) { - .sub => l - r, - .mul => l * r, - .div => l / r, - else => comptime unreachable, - }; - - return (try s.ip.get(s.gpa, .{ - .float = result, - })).toAir(); + inline .sub, .mul, .div => |tag| switch (left_ty) { + .numeric_literal, .float => { + const l = lhs.toFloat(s.ip) orelse + unreachable; // must be coerced + const r = rhs.toFloat(s.ip) orelse + unreachable; // must be coerced + + const result = switch (tag) { + .sub => l - r, + .mul => l * r, + .div => l / r, + else => comptime unreachable, + }; + + return try s.makeValue(if (left_ty == .numeric_literal) + .{ .comptime_float = result } + else + .{ .float = result }); + }, + .int32 => { + const l = lhs.toInt32(s.ip) orelse + unreachable; // must be coerced + const r = rhs.toInt32(s.ip) orelse + unreachable; // must be coerced + + const result = switch (tag) { + .sub => std.math.sub(i32, l, r), + .mul => std.math.mul(i32, l, r), + .div => return try s.makeValue(.{ .float = @as(f64, @floatFromInt(l)) / @as(f64, @floatFromInt(r)) }), + else => comptime unreachable, + } catch @panic("TODO: out of range"); + + return try s.makeValue(.{ .int32 = result }); + }, + else => unreachable, }, inline .lt, .gt, => |tag| { - const l = lhs.toFloat(s.ip) orelse @panic("TODO: typecheck"); - const r = rhs.toFloat(s.ip) orelse @panic("TODO: typecheck"); + const l = lhs.toFloat(s.ip) orelse + unreachable; // must be coerced + const r = rhs.toFloat(s.ip) orelse + unreachable; // must be coerced + const result = switch (tag) { .lt => l < r, .gt => l > r, @@ -918,17 +1018,15 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Air.Inst.Ref { return @as( InternPool.Value, if (result) .bool_true else .bool_false, - ).toAir(); + ); }, .eq, => { - const l = lhs.toFloat(s.ip) orelse @panic("TODO: typecheck / non-numeric eq"); - const r = rhs.toFloat(s.ip) orelse @panic("TODO: typecheck / non-numeric eq"); - const result = l == r; + const result = lhs == rhs; return @as( InternPool.Value, if (result) .bool_true else .bool_false, - ).toAir(); + ); }, } } diff --git a/src/core/Tokenizer.zig b/src/core/Tokenizer.zig index 72aa83aa5bf0262e2c7127f6e371ed8315301290..d0256f8552eefec40dbb6dd72f64ebeb727772a4 100644 --- a/src/core/Tokenizer.zig +++ b/src/core/Tokenizer.zig @@ -152,7 +152,6 @@ pub const Token = struct { .invalid => unreachable, // do not have a static lexeme - .eof, .id, .builtin_id, .number_int, @@ -163,6 +162,8 @@ pub const Token = struct { .module_doc_comment, => null, + .eof => unreachable, // always must be special cased + .@"and" => "&", .and_and => "&&", .bang => "!", diff --git a/src/langref/template.html b/src/langref/template.html deleted file mode 100644 index 765f49eff96554c42aa93dceec6f5dad82b9af1b..0000000000000000000000000000000000000000 --- a/src/langref/template.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - Language Reference - - - -
- {contents} -
- - diff --git a/src/langref/generate-langref.zig b/src/manual/generate-man.zig similarity index 91% rename from src/langref/generate-langref.zig rename to src/manual/generate-man.zig index cab82334924c09e8528aaa57e0a860a7134822d1..7e6654136a4a947e5a4ba349e5f72cd988c86529 100644 --- a/src/langref/generate-langref.zig +++ b/src/manual/generate-man.zig @@ -232,7 +232,30 @@ fn processMarkdown(gpa: std.mem.Allocator, src: []const u8, compiler_path: []con }, .table, => { + try writer.writeAll(""); + { + try writer.writeAll(""); + var it = std.mem.tokenizeScalar(u8, t.advanceLine()[1..], '|'); + while (it.next()) |tok| { + try writer.writeAll(""); + } + try writer.writeAll(""); + } _ = t.advanceLine(); + t.skipWhitespace(); + while (t.str.len > 0 and t.str[0] == '|') { + try writer.writeAll(""); + var it = std.mem.tokenizeScalar(u8, t.advanceLine()[1..], '|'); + while (it.next()) |tok| { + try writer.writeAll(""); + } + try writer.writeAll(""); + } + try writer.writeAll("
"); + try processInlineMarkdown(gpa, tok, writer); + try writer.writeAll("
"); + try processInlineMarkdown(gpa, tok, writer); + try writer.writeAll("
"); }, .blockquote, => { diff --git a/src/manual/template.html b/src/manual/template.html new file mode 100644 index 0000000000000000000000000000000000000000..a50003ed0cb3b3bd4c3aad9dbf685e7f22ddf201 --- /dev/null +++ b/src/manual/template.html @@ -0,0 +1,134 @@ + + + + + + Language Reference + + + +
{contents}
+ + -- 2.54.0