From 8ee16fb09dd67d9dbf00f39085338eaf61d644c3 Mon Sep 17 00:00:00 2001 From: Fabian Stamm Date: Sat, 16 Jul 2022 21:03:27 +0000 Subject: [PATCH] Start implementing ZIG --- .editorconfig | 2 + .yarnrc.yml | 1 + examples/Zig/.gitignore | 2 + examples/Zig/build.zig | 34 ++++++++++++ examples/Zig/src/main.zig | 20 +++++++ src/index.ts | 25 +++++---- src/process.ts | 2 + src/targets/zig.ts | 107 ++++++++++++++++++++++++++++++++++++++ src/utils.ts | 20 +++++++ 9 files changed, 204 insertions(+), 9 deletions(-) create mode 100644 examples/Zig/.gitignore create mode 100644 examples/Zig/build.zig create mode 100644 examples/Zig/src/main.zig create mode 100644 src/targets/zig.ts create mode 100644 src/utils.ts diff --git a/.editorconfig b/.editorconfig index 0ed4985..fa73e82 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,3 +9,5 @@ insert_final_newline = true indent_size = 2 [*.md] indent_size = 2 +[*.zig] +indent_size = 4 diff --git a/.yarnrc.yml b/.yarnrc.yml index c3c72dc..ca86dd2 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,4 +1,5 @@ nodeLinker: node-modules +npmRegistryServer: https://npm.hibas123.de plugins: - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs diff --git a/examples/Zig/.gitignore b/examples/Zig/.gitignore new file mode 100644 index 0000000..1c020f6 --- /dev/null +++ b/examples/Zig/.gitignore @@ -0,0 +1,2 @@ +zig-cache/ +generated/ diff --git a/examples/Zig/build.zig b/examples/Zig/build.zig new file mode 100644 index 0000000..87db239 --- /dev/null +++ b/examples/Zig/build.zig @@ -0,0 +1,34 @@ +const std = @import("std"); + +pub fn build(b: *std.build.Builder) void { + // Standard target options allows the person running `zig build` to choose + // what target to build for. Here we do not override the defaults, which + // means any target is allowed, and the default is native. Other options + // for restricting supported target set are available. + const target = b.standardTargetOptions(.{}); + + // Standard release options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. + const mode = b.standardReleaseOptions(); + + const exe = b.addExecutable("ZigTests", "src/main.zig"); + exe.setTarget(target); + exe.setBuildMode(mode); + exe.install(); + + const run_cmd = exe.run(); + run_cmd.step.dependOn(b.getInstallStep()); + if (b.args) |args| { + run_cmd.addArgs(args); + } + + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + + const exe_tests = b.addTest("src/main.zig"); + exe_tests.setTarget(target); + exe_tests.setBuildMode(mode); + + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&exe_tests.step); +} diff --git a/examples/Zig/src/main.zig b/examples/Zig/src/main.zig new file mode 100644 index 0000000..ab0f581 --- /dev/null +++ b/examples/Zig/src/main.zig @@ -0,0 +1,20 @@ +const std = @import("std"); +const t = @import("./generated/mod.zig"); + +var mygpa = std.heap.GeneralPurposeAllocator(.{}){}; +const gpa = mygpa.allocator(); + +const payload = + \\{ + \\ "val_number": 0.12, + \\ "val_boolean": true, + \\ "val_string": "Hallo Welt" + \\} +; + +pub fn main() !void { + var stream = std.json.TokenStream.init(payload); + const res = std.json.parse(t.TestAtom, &stream, .{ .allocator = gpa }) catch unreachable; + + std.log.info("{} {s}", .{ res, res.val_string }); +} diff --git a/src/index.ts b/src/index.ts index 41b0f09..4854cf9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -44,22 +44,29 @@ yargs(hideBin(process.argv)) }); }, (argv) => { - if (argv.verbose) {dbg.enable("app");} + if (argv.verbose) { + dbg.enable("app"); + } log("Received compile command with args", argv); startCompile({ input: argv.input, targets: argv.output as any, - emitDefinitions: argv.definition - }) + emitDefinitions: argv.definition, + }); + } + ) + .command( + "targets", + "List all targets", + (yargs) => yargs, + () => { + console.log("Targets:"); + Targets.forEach((__dirname, target) => { + console.log(" " + target); + }); } ) - .command("targets", "List all targets", (yargs)=>yargs, ()=>{ - console.log("Targets:") - Targets.forEach((__dirname, target) => { - console.log(" " + target); - }) - }) .option("verbose", { alias: "v", type: "boolean", diff --git a/src/process.ts b/src/process.ts index 42ade9f..a6db5b0 100644 --- a/src/process.ts +++ b/src/process.ts @@ -12,6 +12,7 @@ import { } from "./targets/typescript"; import { CSharpTarget } from "./targets/csharp"; import { RustTarget } from "./targets/rust"; +import { ZIGTarget } from "./targets/zig"; class CatchedError extends Error {} @@ -23,6 +24,7 @@ Targets.set("ts-esm", ESMTypescriptTarget); Targets.set("ts-node", NodeJSTypescriptTarget); Targets.set("c#", CSharpTarget as typeof CompileTarget); Targets.set("rust", RustTarget as typeof CompileTarget); +Targets.set("zig", ZIGTarget as typeof CompileTarget); function indexToLineAndCol(src: string, index: number) { let line = 1; diff --git a/src/targets/zig.ts b/src/targets/zig.ts new file mode 100644 index 0000000..aaf9d49 --- /dev/null +++ b/src/targets/zig.ts @@ -0,0 +1,107 @@ +import { TypeDefinition, ServiceDefinition, EnumDefinition, Step } from "../ir"; + +import { CompileTarget } from "../compile"; +import { LineAppender, lineAppender } from "../utils"; +import chalk from "chalk"; + +const conversion = { + boolean: "bool", + int: "i64", + float: "f64", + string: "[]u8", + void: "void", + bytes: "[]u8", +}; + +function toZigType(type: string): string { + return (conversion as any)[type] || type; +} + +export class ZIGTarget extends CompileTarget<{ csharp_namespace: string }> { + name: string = "zig"; + + start(): void { + if (this.options.allow_bytes == true) { + throw new Error("Zig has no support for 'bytes' yet!"); + } + } + + getImport(name: string) { + return `const ${name} = @import("./${name}.zig").${name};`; + } + + generateImports(a: lineAppender, def: TypeDefinition | ServiceDefinition) { + a(0, `const std = @import("std");`); + def.depends.forEach((dep) => { + a(0, this.getImport(dep)); + }); + } + + generateType(definition: TypeDefinition): void { + const { a, getResult } = LineAppender(); + + this.generateImports(a, definition); + + a(0, ``); + + a(0, `pub const ${definition.name} = struct {`); + for (const field of definition.fields) { + if (field.array) { + a(1, `${field.name}: std.ArrayList(${toZigType(field.type)}),`); + } else if (field.map) { + a( + 1, + `${field.name}: std.AutoHashMap(${toZigType( + field.map + )}, ${toZigType(field.type)}),` + ); + } else { + a(1, `${field.name}: ${toZigType(field.type)},`); + } + } + + a(0, `};`); + + this.writeFile(`${definition.name}.zig`, getResult()); + } + + generateEnum(definition: EnumDefinition): void { + const { a, getResult } = LineAppender(); + + a(0, `pub const ${definition.name} = enum(i32) {`); + for (const entry of definition.values) { + a(1, `${entry.name} = ${entry.value},`); + } + a(0, `};`); + + this.writeFile(`${definition.name}.zig`, getResult()); + } + + generateService(definition: ServiceDefinition): void { + throw new Error("Method not implemented."); + } + + finalize(steps: Step[]): void { + const { a, getResult } = LineAppender(); + + steps.forEach(([type, def]) => { + switch (type) { + case "type": + a(0, `pub ${this.getImport(def.name)}`); + break; + case "enum": + a(0, `pub ${this.getImport(def.name)}`); + break; + default: + console.warn( + chalk.yellow("WARNING:"), + "unimplemented step found:", + type + ); + // case "service": + } + }); + + this.writeFile(`mod.zig`, getResult()); + } +} diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..5bd85df --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,20 @@ +export type lineAppender = (ind: number, line: string | string[]) => void; + +export function LineAppender(indentSize = 3): { + a: lineAppender; + getResult: () => string; +} { + const lines: string[] = []; + + return { + a: (indentation: number, line: string | string[]) => { + if (!Array.isArray(line)) { + line = [line]; + } + line.forEach((l) => + lines.push(" ".repeat(indentation * indentSize) + l.trim()) + ); + }, + getResult: () => lines.join("\n"), + }; +}