diff --git a/CHANGELOG.md b/CHANGELOG.md index 514c61812b4ed12c22ec47afafb683c0dee69813..87933d5cf9da7b6feffdf8fc5791dcc371e034f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - 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(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.zig b/build.zig index 46b94f428a76ba6f6f501ddb53c6974ed4abb41d..7a1a0d2ac5a7e0664b4a008fe2b16947f3fcc647 100644 --- a/build.zig +++ b/build.zig @@ -58,7 +58,7 @@ pub fn build(b: *std.Build) void { test_all.dependOn(run_step); } - // Language Server + // Language Reference Generator const langref_generator = b.addExecutable(.{ .name = "generate-langref", .root_source_file = b.path("src/langref/generate-langref.zig"), @@ -70,10 +70,10 @@ pub fn build(b: *std.Build) void { 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/langref.md")); - langref_run.addFileArg(b.path("src/langref/langref.template.html")); + 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/langref.html"); + const install_langref = b.addInstallFile(langref_out, "doc/README.html"); b.step("langref", "build language reference").dependOn(&install_langref.step); // const wasm_api = b.addExecutable(.{ diff --git a/doc/architecture.md b/doc/architecture.md new file mode 100644 index 0000000000000000000000000000000000000000..ab7b3b8b903e7351bfd972ba0ec3804ad29c9256 --- /dev/null +++ b/doc/architecture.md @@ -0,0 +1,7 @@ +# compiler architecture + +This document will outline the code structure of the language's SDK as well as the compilation pipeline architecture. The major components of the language are: + +- [**Core**][core] +- [**Standard Library**][std] +- [**Language Server**][lsp] diff --git a/doc/langref.md b/doc/langref.md deleted file mode 100644 index 3fdc304cd29385bd30f3230c1fa1258a20903637..0000000000000000000000000000000000000000 --- a/doc/langref.md +++ /dev/null @@ -1,137 +0,0 @@ -# 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. - -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. -- TODO: some more - -## Standard Library - -Currently, there is no standard library. One will be introduced later. - -## Hello World - -```lang:run -let print = @js("console").log; -print("Hello, world!"); -``` - -The `print` function is implemented by getting an external reference to the `console` JavaScript global, and then retriving the `log` property from it. - -## Primative Data Types - -| name | ts equivilent | description | -| ---------- | --------------------- | ---------------------------------------------- | -| `int` | `number` | a number in signed 32-bit range | -| `float` | `number` | a 64-bit floating point number | -| `bool` | `boolean` | either `true` or `false` | -| `string` | `string` | a sequence of text characters | -| `void` | `undefined` or `null` | detectable lack of a value | -| `noreturn` | `never` | control flow prevents this value from existing | - -> **note**: `bigint` is planned but not added - -## Built-in Constants - -In addition to the above types being built-in constants, the following are available. - -| name | description | -| ------- | ------------------------------------- | -| `true` | boolean true (result of `1 == 1`) | -| `false` | boolean false (result of `1 == 2`) | -| `std` | handle to the standard library module | - -These constants are reserved names, and it is an error to re-declare them. - -```lang:build -let true = 1 == 0; -``` - -## Variables - -There are two types of variables, `let` and `var`. - -`let` bindings cannot be overridden, and should be thought of as aliases of the expression within. Referring to these identifiers is equivilent to inlining it's where it was used, aside from side effect duplication. - -```lang:run -let hello: int = 1; -let world = hello + 2; -let hello = hello * world; - -@js("console").log(hello); -``` - -`var` bindings create mutable memory locations at runtime. This means they can be re-assigned to: - -```lang:run -var message = "first message"; - -if @js("Math").random() > 0.5 { - message = "second message" -} - -@js("console").log(message); -``` - -Using `let` is preferred whenever possible, as it will lead to more readable code, and the compiler is able to optimize it better. The above can be rewritten as: - -```lang:run -let message = if @js("Math").random() > 0.5 { - "second message" -} else { - "first message" -}; - -@js("console").log(message); -``` - -It should be noted that numeric literals are a comptime-only data type, meaning it cannot be assigned to a `var` without an explicit type annotation. Since `int` is a strict subset of valid `float`s, the coersion will implicitly be a float, but a compiler warning will be emitted. - -```lang -var number = 0; // `number` is a `float`, but not explicit -var number: int = 0; // `number` is `int` -var number: float = 0; // `number` is `float` -``` - -## Functions - -Functions are declared with the `fn` keyword. Parameter types must be annotated. The function body is a block, which may use implicit return to return. - -```lang:run -fn add(a: int, b: int) -> int { - a + b -} - -let print = @js("console").log; -//print("Sum of 2 and 7" + add(2, 7)); -print(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 -fn something(a: int) -> bool { - if a == 0 { - return false; - } - - a > 100 -} -``` - -## Number Literals - -TODO - -## String Literals - -TODO diff --git a/doc/language-reference.md b/doc/language-reference.md new file mode 100644 index 0000000000000000000000000000000000000000..714941dd21ff3c80436be19f185c4a8e41baa17e --- /dev/null +++ b/doc/language-reference.md @@ -0,0 +1,154 @@ +# 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. + +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. +- TODO: some more + +## Standard Library + +Currently, there is no standard library. One will be introduced later. + +## Hello World + +```lang:run +let print = @js("console").log; +print("Hello, world!"); +``` + +The `print` function is implemented by getting an external reference to the `console` JavaScript global, and then retriving the `log` property from it. + +## Primative Data Types + +| name | ts equivilent | description | +| ---------- | --------------------- | ---------------------------------------------- | +| `int` | `number` | a number in signed 32-bit range | +| `float` | `number` | a 64-bit floating point number | +| `bool` | `boolean` | either `true` or `false` | +| `string` | `string` | a sequence of text characters | +| `void` | `undefined` or `null` | detectable lack of a value | +| `noreturn` | `never` | control flow prevents this value from existing | + +> **note**: `bigint` is planned but not added + +## Built-in Constants + +In addition to the above types being built-in constants, the following are available. + +| name | description | +| ------- | ------------------------------------- | +| `true` | boolean true (result of `1 == 1`) | +| `false` | boolean false (result of `1 == 2`) | +| `std` | handle to the standard library module | + +These constants are reserved names, and it is an error to re-declare them. + +```lang:build +let true = 1 == 0; +``` + +## Variables + +There are two types of variables, `let` and `var`. + +`let` bindings cannot be overridden, and should be thought of as aliases of the expression within. Referring to these identifiers is equivilent to inlining it's where it was used, aside from side effect duplication. + +```lang:run +let hello: int = 1; +let world = hello + 2; +let hello = hello * world; + +@js("console").log(hello); +``` + +`var` bindings create mutable memory locations at runtime. This means they can be re-assigned to: + +```lang:run +var message = "first message"; + +if @js("Math").random() > 0.5 { + message = "second message" +} + +@js("console").log(message); +``` + +Using `let` is preferred whenever possible, as it will lead to more readable code, and the compiler is able to optimize it better. The above can be rewritten as: + +```lang:run +let message = if @js("Math").random() > 0.5 { + "second message" +} else { + "first message" +}; + +@js("console").log(message); +``` + +It should be noted that numeric literals are a comptime-only data type, meaning it cannot be assigned to a `var` without an explicit type annotation. Since `int` is a strict subset of valid `float`s, the coersion will implicitly be a float, but a compiler warning will be emitted. + +```lang +var number = 0; // `number` is a `float`, but not explicit +var number: int = 0; // `number` is `int` +var number: float = 0; // `number` is `float` +``` + +## Functions + +Functions are declared with the `fn` keyword. Parameter types must be annotated. The function body is a block, which may use implicit return to return. + +```lang:run +fn add(a: int, b: int) -> int { + a + b +} + +let print = @js("console").log; +//print("Sum of 2 and 7" + add(2, 7)); +print(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 +fn something(a: int) -> bool { + if a == 0 { + return false; + } + + a > 100 +} +``` + +## Number Literals + +TODO + +## String Literals + +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. + +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() +} + +print(currentYear()); +``` diff --git a/src/cli.zig b/src/cli.zig new file mode 100644 index 0000000000000000000000000000000000000000..ff8747b1c70dce597c04f50c31f357f33458fab7 --- /dev/null +++ b/src/cli.zig @@ -0,0 +1,6 @@ +//! This is the `lang` command line interface. It combines all components +//! of the software development kit into one. For development, it may +//! be more reasonable to hack on individual components, which each +//! expose their own CLIs + +pub fn main() !void {} diff --git a/src/core/Transform.zig b/src/core/Transform.zig index c113d21c54c2ea2994f576e37e7474e7af563075..e3e0023e263cfa1f8cfc7b38e6bae70132aea048 100644 --- a/src/core/Transform.zig +++ b/src/core/Transform.zig @@ -565,6 +565,7 @@ fn transformParamDecl(t: *Transform, decl: BlockContext, param: NodeIndex) Alloc pub const Builtin = enum { js, + js_new, import, fetch, compile_error, @@ -576,6 +577,7 @@ pub const Builtin = enum { .{ "fetch", .fetch }, .{ "import", .import }, .{ "js", .js }, + .{ "jsNew", .js_new }, .{ "typeOf", .type_of }, // sort-lines: end }); diff --git a/src/langref/generate-langref.zig b/src/langref/generate-langref.zig index fdc109d5bc328ed1ce185fd55f7af46a5229a6ee..9f1fb464a11adee6b6a4a1dba270359c64d402b7 100644 --- a/src/langref/generate-langref.zig +++ b/src/langref/generate-langref.zig @@ -9,7 +9,8 @@ const ArrayList = std.ArrayList; pub fn main() !void { comptime std.debug.assert(@import("builtin").mode == .Debug); - const gpa = std.heap.c_allocator; + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + const gpa = arena.allocator(); var args = try std.process.argsWithAllocator(gpa); _ = args.next().?; diff --git a/src/langref/langref.template.html b/src/langref/langref.template.html deleted file mode 100644 index c68fdf59480dd1ca8e85c386c1cf6ac4fc38bbb8..0000000000000000000000000000000000000000 --- a/src/langref/langref.template.html +++ /dev/null @@ -1,78 +0,0 @@ - - -
- - -