Skip to content

Commit

Permalink
Implement the REPL for the CLI.
Browse files Browse the repository at this point in the history
  • Loading branch information
fubark committed Apr 12, 2024
1 parent 365f9e1 commit 1ad836c
Show file tree
Hide file tree
Showing 19 changed files with 2,101 additions and 156 deletions.
51 changes: 49 additions & 2 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ var optRT: ?config.Runtime = undefined;

var stdx: *std.build.Module = undefined;
var tcc: *std.build.Module = undefined;
var linenoise: *std.build.Module = undefined;
var mimalloc: *std.build.Module = undefined;

pub fn build(b: *std.build.Builder) !void {
Expand All @@ -42,6 +43,7 @@ pub fn build(b: *std.build.Builder) !void {
});
tcc = tcc_lib.createModule(b);
mimalloc = mimalloc_lib.createModule(b);
linenoise = createLinenoiseModule(b);

{
const step = b.step("cli", "Build main cli.");
Expand Down Expand Up @@ -303,6 +305,15 @@ pub fn buildAndLinkDeps(step: *std.build.Step.Compile, opts: Options) !void {
step.stack_protector = false;
}

if (opts.cli and opts.target.getOsTag() != .windows) {
addLinenoiseModule(step, "linenoise", linenoise);
buildAndLinkLinenoise(b, step);
} else {
step.addAnonymousModule("tcc", .{
.source_file = .{ .path = thisDir() ++ "/src/nopkg.zig" },
});
}

if (opts.jit) {
// step.addIncludePath(.{ .path = "/opt/homebrew/Cellar/llvm/17.0.1/include" });
// step.addLibraryPath(.{ .path = "/opt/homebrew/Cellar/llvm/17.0.1/lib" });
Expand Down Expand Up @@ -377,7 +388,7 @@ fn getDefaultOptions(target: std.zig.CrossTarget, optimize: std.builtin.Optimize
fn createBuildOptions(b: *std.build.Builder, opts: Options) !*std.build.Step.Options {
const buildTag = std.process.getEnvVarOwned(b.allocator, "BUILD") catch |err| b: {
if (err == error.EnvironmentVariableNotFound) {
break :b "local";
break :b "0";
} else {
return err;
}
Expand Down Expand Up @@ -571,4 +582,40 @@ fn buildLib(b: *std.Build, opts: Options) !*std.build.Step.Compile {

try buildAndLinkDeps(lib, opts);
return lib;
}
}

pub fn createLinenoiseModule(b: *std.Build) *std.build.Module {
return b.createModule(.{
.source_file = .{ .path = thisDir() ++ "/lib/linenoise/linenoise.zig" },
});
}

pub fn addLinenoiseModule(step: *std.build.CompileStep, name: []const u8, mod: *std.build.Module) void {
step.addModule(name, mod);
step.addIncludePath(.{ .path = thisDir() ++ "/lib/linenoise" });
}

pub fn buildAndLinkLinenoise(b: *std.Build, step: *std.build.CompileStep) void {
const lib = b.addStaticLibrary(.{
.name = "linenoise",
.target = step.target,
.optimize = step.optimize,
});
lib.addIncludePath(.{ .path = thisDir() ++ "/lib/linenoise" });
lib.linkLibC();
// lib.disable_sanitize_c = true;

var c_flags = std.ArrayList([]const u8).init(b.allocator);

var sources = std.ArrayList([]const u8).init(b.allocator);
sources.appendSlice(&.{
"/lib/linenoise/linenoise.c",
}) catch @panic("error");
for (sources.items) |src| {
lib.addCSourceFile(.{
.file = .{ .path = b.fmt("{s}{s}", .{thisDir(), src}) },
.flags = c_flags.items,
});
}
step.linkLibrary(lib);
}
72 changes: 64 additions & 8 deletions docs/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
- [Metaprogramming.](#metaprogramming)
- [Embedding.](#embedding)
- [Memory.](#memory)
- [Backends.](#backends)
- [CLI.](#cli)

<!--TOC-END-->

Expand Down Expand Up @@ -3261,20 +3261,76 @@ print res['numCycFreed'] -- Output: 2
print res['numObjFreed'] -- Output: 2
```
# Backends.
# CLI.
* [JIT.](#jit)
* [AOT.](#aot)
* [REPL.](#repl)
* [JIT compiler.](#jit-compiler)
* [AOT compiler.](#aot-compiler)
[^top](#table-of-contents)
## JIT.
## REPL.
The REPL is started by running the CLI without any arguments:
```bash
cyber
```
The REPL starts new sessions with [`use $global`](#use-global). This allows undeclared variables to be used similar to other dynamic languages:
```bash
> a = 123
> a * 2
`int` 246
```
When the first input ends with `:`, the REPL will automatically indent the next line. To recede the indentation, provide an empty input. Once the indent returns to the beginning, the entire code block is submitted for evaluation:
```bash
> if true:
| print 'hello!'
|
hello!
```
Top level declarations such as imports, types, and functions can be referenced in subsequent evals:
```bash
> use math
> math.random()
`float` 0.3650744641604983
```
```bash
> type Foo:
| a int
|
> f = Foo{a: 123}
> f.a
`int` 123
```
Local variables **can not** be referenced in subsequent evals, since their scope ends with each eval input:
```bash
> let a = 123
> a
panic: Variable is not defined in `$global`.

input:1:1 main:
a
^
```
## JIT compiler.
Cyber's just-in-time compiler is incomplete and unstable. To run your script with JIT enabled:
```bash
cyber -jit &lt;script&gt;
```
The JIT compiler is just as fast as the bytecode generation so when it's enabled, the entire script is compiled from the start.
The goal of the JIT compiler is to be fast at compilation while still being significantly faster than the interpreter. The codegen involves stitching together pregenerated machine code that targets the same runtime stack slots used by the VM. This technique is also known as `copy-and-patch`. As the VM transitions to unboxed data types, the generated code will see more performance gains.
## AOT compiler.
The ahead-of-time compiler generates a static or dynamic binary from Cyber source code.
This is done by first compiling Cyber code into C code, and using a C compiler to generate the final binary.
The user can specify the system's `cc` compiler or the builtin `tinyc` compiler that is bundled with the CLI.
*This is currently in progress.*
&nbsp;
## AOT
Work on the ahead-of-time compiler has not begun.
&nbsp;
Loading

0 comments on commit 1ad836c

Please sign in to comment.