diff --git a/.gitignore b/.gitignore index 6c08354510e655ecc6f55e468a65aba4c006d11e..ae163bea1d23a4d4b8708c5171bdf61396efa7e2 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,5 @@ # excludesfile = ~/.gitignore # +.zig-cache zig-out -zig-cache diff --git a/build.zig b/build.zig index 7a1a0d2ac5a7e0664b4a008fe2b16947f3fcc647..369cb7d7e9f0aeca14d21f02fe1f3ae197e591c5 100644 --- a/build.zig +++ b/build.zig @@ -127,7 +127,7 @@ pub fn build(b: *std.Build) void { package.dependOn(&b.addInstallFile( artifact.getEmittedBin(), b.pathJoin(&.{ - "release", + "package", name, std.mem.concat(b.allocator, u8, &.{ bin_name, diff --git a/build.zig.zon b/build.zig.zon index 3c75338bb1c8bba9e1d92569fc1c914bd19e7d8e..927b751a340b3ada67cce95226a172d08402fb6f 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -2,12 +2,12 @@ .name = "", .version = "0.0.0", - .minimum_zig_version = "0.12.0", + .minimum_zig_version = "0.13.0-dev.348+c564a16a0", .dependencies = .{ .zg = .{ - .url = "https://codeberg.org/dude_the_builder/zg/archive/16735685fcc3410de361ba3411788ad1fb4fe188.tar.gz", - .hash = "1220fe9ac5cdb41833d327a78745614e67d472469f8666567bd8cf9f5847a52b1c51", + .url = "https://codeberg.org/dude_the_builder/zg/archive/c425c9c8511bf92e14b8b612d1d16e774b186f2e.tar.gz", + .hash = "1220c79009f1e443b015db4de6e5319432538ead2fea10d271f3f35d3ae83c996789", }, .tracer = .{ .url = "https://github.com/paperdave/zig-tracer/archive/d4c2eff040e9e69b9f08d2175b315f3055ad7ba3.tar.gz", diff --git a/doc/language-reference.md b/doc/language-reference.md index 714941dd21ff3c80436be19f185c4a8e41baa17e..abc1ebb3cb5c98d9b25f568f362dcf66c590d51f 100644 --- a/doc/language-reference.md +++ b/doc/language-reference.md @@ -1,10 +1,6 @@ # New Language Project -This is a high-level, general purpose programming language. It is a mix of Rust, Zig, and JavaScript's great points. - -- **Rust** - Expressive control flow that is easy to write. -- **Zig** - Execute any code at compile time, everything is an expression. -- **JavaScript** - Garbage collected values with a versitile type system. +This is a high-level, general purpose programming language. Key goals of this project are: @@ -12,6 +8,12 @@ Key goals of this project are: - **Fast Iteration**: The compiler is designed for quick incremental compilation and hot module reloading. - TODO: some more +It primarily focuses on merging some great points of Rust, Zig, and JavaScript's language designs: + +- **Rust** - Expressive control flow that is easy to write. +- **Zig** - Execute any code at compile time, everything is an expression. +- **JavaScript** - Garbage collected values with a versitile type system. + ## Standard Library Currently, there is no standard library. One will be introduced later. @@ -74,7 +76,7 @@ let hello = hello * world; var message = "first message"; if @js("Math").random() > 0.5 { - message = "second message" + message = "second message"; } @js("console").log(message); @@ -110,15 +112,14 @@ fn add(a: int, b: int) -> int { } let print = @js("console").log; -//print("Sum of 2 and 7" + add(2, 7)); -print(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. The `return` expression can be used within functions to return early. -```lang +```lang:run fn something(a: int) -> bool { if a == 0 { return false; @@ -126,6 +127,9 @@ fn something(a: int) -> bool { a > 100 } + +let print = @js("console").log; +print(something(0xFF), something(0)); ``` ## Number Literals @@ -143,11 +147,10 @@ A global variable handle can be retrived with `@js`. We've seen this in the hell Many JS-exclusive actions are exposed via built-in functions, such as using `@jsNew` for calling constructors. ```lang:run -let date_constructor = @js("Date"); let print = @js("console").log; fn currentYear() -> int { - @jsNew(date_constructor).getFullYear() + @jsNew(@js("Date")).getFullYear() } print(currentYear()); diff --git a/src/core/Air.Dump.zig b/src/core/Air.Dump.zig index 51d7af7eaf89f4021e3893f0202526b803c38115..cc8751aa6a80d2a362508a53d13c1ab497ef6a89 100644 --- a/src/core/Air.Dump.zig +++ b/src/core/Air.Dump.zig @@ -126,8 +126,8 @@ pub const State = struct { try tty.setColor(w, .reset); try w.writeAll(")"); }, - .call => { - const call: Air.Call = air.instData(.call, inst); + inline .call, .new => |t| { + const call: Air.Call = air.instData(t, inst); const call_data = call.call_data.get(air); try w.writeAll("("); try s.dumpRef(w, call.base); diff --git a/src/core/Air.zig b/src/core/Air.zig index 4252ffa290e9295ce7318681d943d70aad6298ed..89aaef766e0b02ff44d61e332553be2ae3ec9ea6 100644 --- a/src/core/Air.zig +++ b/src/core/Air.zig @@ -16,6 +16,7 @@ pub const Inst = struct { block, js_global_ref, call, + new, @"break", @"if", arg, @@ -45,7 +46,7 @@ pub const Inst = struct { .js_global_ref => InternPool.String.WithLen, .@"break" => Break, .@"if" => If, - .call => Call, + .call, .new => Call, .arg => Arg, .field => Field, .memory => Memory, @@ -258,7 +259,7 @@ pub fn typeOf(air: *const Air, ref: Inst.Ref, ip: *InternPool) InternPool.Type { pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *InternPool) InternPool.Type { return switch (air.instTag(index)) { - .js_global_ref => .@"opaque", + .js_global_ref, .new => .@"opaque", .block => { const items = air.instData(.block, index).slice(air); diff --git a/src/core/Ast.zig b/src/core/Ast.zig index d56f3857479ddd8ec42405ee7d48dad4d06e877f..01f6404f4a441ef307fea2911bc9230550b94560 100644 --- a/src/core/Ast.zig +++ b/src/core/Ast.zig @@ -38,6 +38,7 @@ utf8_source: [:0]const u8, tokens: Token.List.Slice, nodes: Node.List.Slice, +/// TODO: Move this to using array.ExtraStore extra: []const u32, pub const Node = struct { diff --git a/src/core/JsLink.zig b/src/core/JsLink.zig index ad1247e5b95eafdfed79ad61479622a352881bfb..33db47c68d495888b4f0d5e90871f5e64f94430c 100644 --- a/src/core/JsLink.zig +++ b/src/core/JsLink.zig @@ -152,9 +152,22 @@ fn printDecl(link: *JsLink, decl_index: Decl.Index, writer: anytype) !void { try link.printLocal(@intCast(id), writer); if (id != tree.param_count - 1) try writer.writeAll(", "); } - try writer.writeAll(") => {\n"); - try link.printDeclContents(tree, writer); - try writer.writeAll("};\n"); + try writer.writeAll(") => "); + + const stmts = tree.mainStatements(); + if (stmts.len == 1 and tree.nodeTag(stmts[0]) == .stmt_return) { + if (tree.nodeData(.stmt_return, stmts[0]).operand.unwrap()) |operand| { + try link.printExpr(tree, operand, writer); + } else { + try writer.writeAll("{}"); + } + } else { + try writer.writeAll("{\n"); + try link.printDeclContents(tree, writer); + try writer.writeAll("}"); + } + + try writer.writeAll(";\n"); } fn printDeclContents(link: *JsLink, tree: *const JsTree, writer: anytype) !void { @@ -167,7 +180,12 @@ fn printDeclContents(link: *JsLink, tree: *const JsTree, writer: anytype) !void } } -fn printBlockContents(link: *JsLink, tree: *const JsTree, list: JsTree.IndexList, writer: anytype) !void { +fn printBlockContents( + link: *JsLink, + tree: *const JsTree, + list: JsTree.IndexList, + writer: anytype, +) !void { var t = tracer.trace(@src()); defer t.end(); @@ -377,6 +395,28 @@ fn printExprLevel( try writer.writeAll(")"); }, + .new => { + const call: JsTree.Call = tree.nodeData(.new, node); + const args = call.args.get(tree); + + if (Level.new.needsParens(level)) + try writer.writeAll("("); + try writer.writeAll("new "); + try link.printExpr(tree, call.base, writer); + + if (args.len > 0) { + try writer.writeAll("("); + + for (args.slice(tree), 0..) |arg, i| { + if (i != 0) try writer.writeAll(", "); + try link.printExpr(tree, arg, writer); + } + + try writer.writeAll(")"); + } + if (Level.new.needsParens(level)) + try writer.writeAll(")"); + }, .field => { const field: JsTree.Field = tree.nodeData(.field, node); diff --git a/src/core/JsNodeGen.zig b/src/core/JsNodeGen.zig index f3b801e05e8e7b37b38e3903f81bcc39a5e1381e..cbf728a18f387014a2805fbe424ad363458b7146 100644 --- a/src/core/JsNodeGen.zig +++ b/src/core/JsNodeGen.zig @@ -260,8 +260,8 @@ fn codeGenInst(c: *JsNodeGen, inst: Air.Inst.Index) Error!void { try c.markExpression(inst, node); }, - .call => { - const data: Air.Call = c.air.instData(.call, inst); + inline .call, .new => |tag| { + const data: Air.Call = c.air.instData(tag, inst); const call_data = data.call_data.get(c.air); try c.extra.list.ensureUnusedCapacity(c.gpa, call_data.args.len); @@ -274,7 +274,7 @@ fn codeGenInst(c: *JsNodeGen, inst: Air.Inst.Index) Error!void { dest.* = @intFromEnum(try c.fetchExpression(arg)); } - const node = try c.add(.call, .{ + const node = try c.add(@field(JsTree.Tag, @tagName(tag)), .{ .base = try c.fetchExpression(data.base), .args = try c.extra.append(c.gpa, JsTree.IndexList, .{ .start = @intCast(start), @@ -603,6 +603,7 @@ fn canLowerToExpression(c: *const JsNodeGen, inst: Air.Inst.Ref) bool { .index => |index| switch (c.air.instTag(index)) { .js_global_ref, .call, + .new, .arg, .field, .add, diff --git a/src/core/JsTree.zig b/src/core/JsTree.zig index 291b621802fe1957910128273fd77b223064ee14..a22f4ab834d5cd0b11e267395ba2b01a15fbeab4 100644 --- a/src/core/JsTree.zig +++ b/src/core/JsTree.zig @@ -39,6 +39,7 @@ pub const Tag = enum { gt, // exprs call, + new, field, ternary, // prefix ops @@ -84,7 +85,7 @@ pub const Tag = enum { .pre_inc, .pre_dec, => Un, - .call => Call, + .call, .new => Call, .field => Field, .stmt_if => StmtIf, .stmt_break => StmtBreak, diff --git a/src/core/Report.zig b/src/core/Report.zig index 22bdaf0bc240c111724a0935f67237ade21007ca..808b104619c990956d700d493c006358073c6b30 100644 --- a/src/core/Report.zig +++ b/src/core/Report.zig @@ -817,11 +817,10 @@ pub const LazyNode = struct { switch (teg.instTag(lazy.inst)) { .as => switch (node_tag) { - .var_decl => { - switch (lazy.operand) { - .rhs => return data.var_decl.init, - else => {}, - } + .var_decl => switch (lazy.operand) { + .lhs => return data.var_decl.type.unwrap().?, + .rhs => return data.var_decl.init, + else => {}, }, else => {}, }, @@ -865,6 +864,9 @@ pub const LazyNode = struct { else => fn_decl.params.slice(ctx.ast)[lazy.operand.getArgId()], }; }, + .js => { + return base_node; + }, else => {}, } diff --git a/src/core/Sema.zig b/src/core/Sema.zig index 894d536dd11852edb184c236bc954aed3d90c58c..2ad726b477901e72b2039ac9cc71577a6e9960d6 100644 --- a/src/core/Sema.zig +++ b/src/core/Sema.zig @@ -425,6 +425,31 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { }), }); }, + .js_new => { + const call: Teg.Inst.JsNew = s.code.instPayload(.js_new, in); + const base = try s.resolveRefRuntime(call.base); + + const scratch = try s.arena.alloc(Air.Inst.Ref, call.args.len); + defer s.arena.free(scratch); + for (call.args.slice(&s.code), 0..) |arg, i| { + const ref = try s.ensureValueRuntime( + in.op(Report.Operand.argument(@intCast(i))), + try s.resolveRefRuntime(arg), + ); + scratch[i] = ref; + } + + const start = s.air_extra.list.items.len; + try s.air_extra.list.appendSlice(s.gpa, @ptrCast(scratch)); + + return s.addInstRef(.new, .{ + .base = base, + .call_data = try s.air_extra.append(s.gpa, Air.Call.Data, .{ .args = .{ + .start = @intCast(start), + .len = @intCast(scratch.len), + } }), + }); + }, .ensure_void => { const operand = try s.resolveRefRuntimeExtra(s.code.instData(.ensure_void, in).operand, false); const t = s.typeOfRef(operand); @@ -447,9 +472,8 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref { .@"if" => { const data: Teg.Inst.If = s.code.instData(.@"if", in).extra_index.get(&s.code); - const cond = try s.resolveRefRuntime(data.cond); - - // try s.validateType(s.typeOfRef(cond), .bool_type); + const cond_raw = try s.resolveRefRuntime(data.cond); + const cond = try s.coerceRef(in.op(.operand), cond_raw, .bool); // If known at comptime, only analyze the taken path if (cond.kind == .value) { @@ -636,6 +660,9 @@ fn coerceRef(s: *Sema, src: InstOp, value: Air.Inst.Ref, to: Type) !Air.Inst.Ref if (from == to) { return value; } + if (from == .@"opaque") { + return value; + } const index = switch (value.decode()) { .value => |v| return (try s.coerceValue(src, v, to)).toAir(), .index => |index| index, diff --git a/src/core/Teg.Dump.zig b/src/core/Teg.Dump.zig index d7700f140a74099b8b1c7106790b706b271057f6..66d5768ff7bef497ef43b67e40b750f84daaef37 100644 --- a/src/core/Teg.Dump.zig +++ b/src/core/Teg.Dump.zig @@ -186,6 +186,14 @@ const State = struct { try s.dumpRefMarkerList(writer, call.args.slice(teg), '[', ']'); try writer.writeAll(")"); }, + .js_new => { + const call: Teg.Inst.JsNew = teg.instData(.js_new, inst).extra_index.get(teg); + try writer.writeAll("("); + try s.dumpRefMarker(writer, call.base); + try writer.writeAll(","); + try s.dumpRefMarkerList(writer, call.args.slice(teg), '[', ']'); + try writer.writeAll(")"); + }, .load_decl => { const call: Teg.Inst.StrNode = teg.instData(.load_decl, inst); diff --git a/src/core/Teg.zig b/src/core/Teg.zig index 9ca445aced09f084856915b78f2c5263a0050d6b..931a6c2c56844746b3010f60260e42fdc9449ec6 100644 --- a/src/core/Teg.zig +++ b/src/core/Teg.zig @@ -72,6 +72,8 @@ pub const Inst = struct { /// Implements the `@js` builtin js, + /// Implements the `@jsNew` builtin + js_new, /// Implements the `@import` builtin import, /// Implements the `@fetch` builtin @@ -95,6 +97,7 @@ pub const Inst = struct { .load_decl => StrNode, .param => DataNode, .store => PlNode(Store), + .js_new => PlNode(JsNew), // Single instruction operand .memory, @@ -307,6 +310,11 @@ pub const Inst = struct { no: Inst.Ref, }; + pub const JsNew = struct { + base: Inst.Ref, + args: RefList, + }; + /// This will contain trailing data depending on the .kind pub const Decl = struct { name: NullTerminatedString, diff --git a/src/core/Tokenizer.zig b/src/core/Tokenizer.zig index a647ff4cde792112953ab60bd3a6a333638ac52d..72aa83aa5bf0262e2c7127f6e371ed8315301290 100644 --- a/src/core/Tokenizer.zig +++ b/src/core/Tokenizer.zig @@ -274,7 +274,7 @@ pub const Token = struct { utf8_source[token.offset..][0..token.len(utf8_source)]; } - const keywords = std.ComptimeStringMap(Tag, keywords: { + const keywords = std.StaticStringMap(Tag).initComptime(keywords: { const fields = std.enums.values(Tag); const KV = struct { []const u8, Tag }; var kws: [fields.len]KV = undefined; @@ -355,18 +355,33 @@ pub fn next(t: *Tokenizer) !?Token { (t.byteAt(3) orelse 0 != '/')) { const start = t.pos; - t.pos = @intCast(mem.indexOfScalarPos(u8, t.code, t.pos + 3, '\n') orelse t.code.len); + t.pos = @intCast(mem.indexOfScalarPos( + u8, + t.code, + t.pos + 3, + '\n', + ) orelse t.code.len); return .{ .tag = .doc_comment, .offset = start }; } if (t.byteAt(2) orelse 0 == '!') { const start = t.pos; - t.pos = @intCast(mem.indexOfScalarPos(u8, t.code, t.pos + 2, '\n') orelse t.code.len); + t.pos = @intCast(mem.indexOfScalarPos( + u8, + t.code, + t.pos + 2, + '\n', + ) orelse t.code.len); return .{ .tag = .module_doc_comment, .offset = start }; } // skip the comment - t.pos = @intCast(mem.indexOfScalarPos(u8, t.code, t.pos + 2, '\n') orelse t.code.len); + t.pos = @intCast(mem.indexOfScalarPos( + u8, + t.code, + t.pos + 2, + '\n', + ) orelse t.code.len); t.skipWhitespace(); continue; }, @@ -376,7 +391,12 @@ pub fn next(t: *Tokenizer) !?Token { '\\' => switch (t.nextByte()) { '\\' => { const start = t.pos; - t.pos = @intCast(mem.indexOfScalarPos(u8, t.code, t.pos + 2, '\n') orelse t.code.len); + t.pos = @intCast(mem.indexOfScalarPos( + u8, + t.code, + t.pos + 2, + '\n', + ) orelse t.code.len); return .{ .tag = .multiline_string, .offset = start }; }, else => return t.maybeUTF8Error(error.InvalidSyntax), @@ -607,8 +627,11 @@ fn tokenizeNumber(t: *Tokenizer) !?Token { else => unreachable, } + const start = t.pos; + var tag: Token.Tag = .number_int; + // Parse Hex, Binary, Octal numbers - if (t.byte() == '0') { + if (t.byte() == '0') alt_base: { switch (t.nextByte()) { 'x', 'X' => { t.pos += 2; @@ -622,13 +645,22 @@ fn tokenizeNumber(t: *Tokenizer) !?Token { t.pos += 2; try t.consumeIntegerRadix(.octal); }, - else => {}, + '0' => @panic("TODO: no double zero"), + else => break :alt_base, } + + // after numbers must be a valid word break the above returns are right + // before parsing `.` so those can be unchecked. + if (try t.isAtIdentifierStart()) { + return error.InvalidSyntax; + } + + return .{ + .tag = tag, + .offset = start, + }; } - const start = t.pos; - var tag: Token.Tag = .number_int; - if (t.byte() == '.') { t.pos += 1; tag = .number_float; diff --git a/src/core/Transform.zig b/src/core/Transform.zig index e3e0023e263cfa1f8cfc7b38e6bae70132aea048..b13a0335440160b5631ef6dc881433fa05503538 100644 --- a/src/core/Transform.zig +++ b/src/core/Transform.zig @@ -216,7 +216,7 @@ const BlockContext = struct { const Liveness = enum { alive, dead }; -const BuiltInIds = std.ComptimeStringMap(Inst.Ref, .{ +const BuiltInIds = std.StaticStringMap(Inst.Ref).initComptime(.{ .{ "void", .void }, .{ "true", .bool_true }, .{ "false", .bool_false }, @@ -571,7 +571,7 @@ pub const Builtin = enum { compile_error, type_of, - const map = std.ComptimeStringMap(Builtin, .{ + const map = std.StaticStringMap(Builtin).initComptime(.{ // sort-lines: start .{ "compileError", .compile_error }, .{ "fetch", .fetch }, @@ -602,6 +602,30 @@ pub const Builtin = enum { }); } + fn builtin_jsNew(t: *Transform, decl: BlockContext, node: NodeIndex, args: []const NodeIndex) !?Inst.Ref { + const trace = tracer.trace(@src()); + defer trace.end(); + + if (args.len == 0) { + try t.report.err(.builtin_mismatch_args, .{ .node = node, .args = 1 }); + return .void; + } + + const base = try t.transformExpression(decl, args[0]) orelse return null; + const args_list = try t.transformCallArgs(decl, args[1..]) orelse return null; + + return try t.addInstRef(.{ + .tag = .js_new, + .data = EncodedData.encode(.js_new, .{ + .src_node = decl.nodeOffset(args[0]), + .extra_index = try t.extra.append(t.gpa, Inst.JsNew, .{ + .base = base, + .args = args_list, + }), + }), + }); + } + fn builtin_compileError(t: *Transform, decl: BlockContext, node: NodeIndex, args: []const NodeIndex) !?Inst.Ref { const trace = tracer.trace(@src()); defer trace.end(); @@ -616,7 +640,7 @@ pub const Builtin = enum { return try t.addInstRef(.{ .tag = .compile_error, - .data = EncodedData.encode(.js, .{ + .data = EncodedData.encode(.compile_error, .{ .src_node = decl.nodeOffset(node), .operand = source, }), @@ -637,7 +661,7 @@ pub const Builtin = enum { return try t.addInstRef(.{ .tag = .type_of, - .data = EncodedData.encode(.js, .{ + .data = EncodedData.encode(.type_of, .{ .src_node = decl.nodeOffset(node), .operand = source, }), @@ -932,6 +956,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc return switch (builtin) { .js => Builtin.builtin_js(t, decl, node, args), + .js_new => Builtin.builtin_jsNew(t, decl, node, args), // .import => Builtin.import(t, decl, node, args), // .fetch => Builtin.fetch(t, decl, node, args), .compile_error => Builtin.builtin_compileError(t, decl, node, args), @@ -945,28 +970,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc const args = t.ast.extraData(call.extra, Ast.Call).args.slice(&t.ast); const base = try t.transformExpression(decl, call.base) orelse return null; - - const args_list: RefList = brk: { - const args_top = t.call_args.items.len; - defer t.call_args.shrinkRetainingCapacity(args_top); - - for (args) |arg| { - try t.call_args.append( - t.gpa, - try t.transformExpression(decl, arg) orelse - return null, - ); - } - - const items = t.call_args.items[args_top..]; - const extra_top = t.extra.list.items.len; - try t.extra.list.appendSlice(t.gpa, @ptrCast(items)); - - break :brk .{ - .start = @intCast(extra_top), - .len = @intCast(items.len), - }; - }; + const args_list = try t.transformCallArgs(decl, args) orelse return null; const call_inst = try t.addInstRef(.{ .tag = .call, @@ -1138,6 +1142,28 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc } } +fn transformCallArgs(t: *Transform, decl: BlockContext, args: []const NodeIndex) !?RefList { + const args_top = t.call_args.items.len; + defer t.call_args.shrinkRetainingCapacity(args_top); + + for (args) |arg| { + try t.call_args.append( + t.gpa, + try t.transformExpression(decl, arg) orelse + return null, + ); + } + + const items = t.call_args.items[args_top..]; + const extra_top = t.extra.list.items.len; + try t.extra.list.appendSlice(t.gpa, @ptrCast(items)); + + return .{ + .start = @intCast(extra_top), + .len = @intCast(items.len), + }; +} + fn transformAssignmentTarget(t: *Transform, decl: BlockContext, node: NodeIndex) Allocator.Error!?Inst.Index { const node_tag = t.ast.nodes.items(.tag)[node]; diff --git a/src/langref/generate-langref.zig b/src/langref/generate-langref.zig index 9f1fb464a11adee6b6a4a1dba270359c64d402b7..cab82334924c09e8528aaa57e0a860a7134822d1 100644 --- a/src/langref/generate-langref.zig +++ b/src/langref/generate-langref.zig @@ -182,6 +182,8 @@ fn processMarkdown(gpa: std.mem.Allocator, src: []const u8, compiler_path: []con try writeTokenizedCode(gpa, code.items, writer); try writer.writeAll(""); + try code.append('\n'); + switch (tag) { .code_run, .code_build, @@ -208,7 +210,8 @@ fn processMarkdown(gpa: std.mem.Allocator, src: []const u8, compiler_path: []con } if (std.mem.trim(u8, out.items, " \n\r").len > 0) { try writer.writeAll("
");
-                            try writer.writeAll(out.items);
+                            try writer.writeAll("// Compiled JavaScript Code");
+                            try writer.writeAll(out.items[std.mem.indexOfScalar(u8, out.items, '\n').?..]);
                             try writer.writeAll("
"); } }, @@ -302,6 +305,8 @@ fn processInlineMarkdown(gpa: std.mem.Allocator, src: []const u8, writer: anytyp } } +// TODO: Change this to use Tree-sitter, which would also make it very simple +// to highlight JavaScript code in addition to the language. fn writeTokenizedCode(gpa: std.mem.Allocator, code: []const u8, writer: anytype) !void { const code_z = try gpa.dupeZ(u8, code); defer gpa.free(code_z); diff --git a/src/langref/template.html b/src/langref/template.html index c68fdf59480dd1ca8e85c386c1cf6ac4fc38bbb8..765f49eff96554c42aa93dceec6f5dad82b9af1b 100644 --- a/src/langref/template.html +++ b/src/langref/template.html @@ -8,7 +8,7 @@ * { margin: 0; padding: 0; box-sizing: border-box; } body { background-color: #fefefe; - font-family: sans-serif; + font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; max-width: 45rem; padding: 1rem; margin: auto; @@ -29,44 +29,66 @@ padding-top: 8px; padding-bottom: 8px; } - pre { - padding: 0.5rem; - } pre > code { border: none; padding: 0; background-color: transparent; } code, pre { - border: 1px solid #999; - background-color: #eeeeee; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + font-size: 0.9rem; + border: 1px solid #9ca0b0; + background-color: #eff1f5; border-radius: 4px; padding: 2px 4px; } + pre { + padding: 0.75rem 0.75rem; + } .term { - border: 1px solid #333; - background-color: #222; - color:white; + border: 1px solid #11111b; + background-color: #24273a; + color: #cdd6f4; } + .ansi-1 { font-weight: bold; } .ansi-31 { - color:rgb(255, 68, 68); + color: #ed8796; + } + .ansi-32 { + color: #a6e3a1; + } + .ansi-33 { + color: #eed49f; + } + .ansi-34 { + color: #8aadf4; + } + .ansi-35 { + color: #f5c2e7; + } + .ansi-36 { + color: #94e2d5; } .ansi-2 { - color:#aaa; + color: #8087a2; } .kw { - color: mediumpurple; + color: #8839ef; font-weight: bold; } .tok-string { - color: green; + color: #40a02b; + } + .tok-builtin_id { + color: #d20f39; + font-weight: bold; } .tok-number_int, .tok-number_float { - color: orangered; + color: #fe640b; }