Skip to content

Commit

Permalink
feature: scaffold parser into rebo runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
graeme-lockley committed May 18, 2024
1 parent deffb58 commit b732493
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 29 deletions.
44 changes: 44 additions & 0 deletions docs/parser.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Parser

The rebo parser parsers a string and produces an Abstract Syntax Tree (AST). The AST that is produced is used by all tools in the rebo ecosystem.

```rebo-repl
> rebo.lang.parse("1 + 2 * 3", { position: true })
{ kind: "exprs"
, value:
[ { kind: "binaryOp"
, op: "+"
, lhs: { kind: "literalInt", value: 1, position: [ 0, 1 ] }
, rhs:
{ kind: "binaryOp"
, op: "*"
, lhs: { kind: "literalInt", value: 2, position: [ 4, 5 ] }
, rhs: { kind: "literalInt", value: 3, position: [ 8, 9 ] }
, position: [ 4, 9 ]
}
, position: [ 0, 9 ]
}
]
, position: [ 0, 9 ]
}
```

The littering of `position` fields in the AST is optional and, going through the different scenarios, it is best to not represent them as it creates loads of clutter.

```rebo-repl
> rebo.lang.parse("1 + 2 * 3")
{ kind: "exprs"
, value:
[ { kind: "binaryOp"
, op: "+"
, lhs: { kind: "literalInt", value: 1 }
, rhs:
{ kind: "binaryOp"
, op: "*"
, lhs: { kind: "literalInt", value: 2 }
, rhs: { kind: "literalInt", value: 3 }
}
}
]
}
```
14 changes: 0 additions & 14 deletions src/builtins/eval.zig
Original file line number Diff line number Diff line change
@@ -1,20 +1,6 @@
const std = @import("std");
const Helper = @import("./helper.zig");

fn booleanOption(stringPool: *Helper.StringPool, options: *Helper.Value, name: []const u8, default: bool) !bool {
if (options.v != Helper.ValueValue.RecordKind) {
return default;
}

const option = try options.v.RecordKind.getU8(stringPool, name);

if (option == null or option.?.v != Helper.ValueKind.BoolKind) {
return default;
}

return option.?.v.BoolKind;
}

pub fn eval(machine: *Helper.Runtime, numberOfArgs: usize) !void {
const code = try Helper.getArgument(machine, numberOfArgs, 0, &[_]Helper.ValueKind{Helper.ValueValue.StringKind});
const scope = try Helper.getArgument(machine, numberOfArgs, 1, &[_]Helper.ValueKind{Helper.ValueValue.ScopeKind});
Expand Down
14 changes: 14 additions & 0 deletions src/builtins/helper.zig
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,17 @@ pub fn getArgument(runtime: *Runtime, numberOfArgs: usize, position: usize, expe

return runtime.unitValue.?;
}

pub fn booleanOption(stringPool: *StringPool, options: *Value, name: []const u8, default: bool) !bool {
if (options.v != ValueValue.RecordKind) {
return default;
}

const option = try options.v.RecordKind.getU8(stringPool, name);

if (option == null or option.?.v != ValueKind.BoolKind) {
return default;
}

return option.?.v.BoolKind;
}
20 changes: 5 additions & 15 deletions src/builtins/open.zig
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
const std = @import("std");
const Helper = @import("./helper.zig");

fn booleanOption(stringPool: *Helper.StringPool, options: *Helper.Value, name: []const u8, default: bool) !bool {
const option = try options.v.RecordKind.getU8(stringPool, name);

if (option == null or option.?.v != Helper.ValueKind.BoolKind) {
return default;
}

return option.?.v.BoolKind;
}

pub fn open(machine: *Helper.Runtime, numberOfArgs: usize) !void {
const path = (try Helper.getArgument(machine, numberOfArgs, 0, &[_]Helper.ValueKind{Helper.ValueValue.StringKind})).v.StringKind.slice();
const options = try Helper.getArgument(machine, numberOfArgs, 1, &[_]Helper.ValueKind{ Helper.ValueValue.RecordKind, Helper.ValueValue.UnitKind });
Expand All @@ -20,11 +10,11 @@ pub fn open(machine: *Helper.Runtime, numberOfArgs: usize) !void {
return;
}

const readF = try booleanOption(machine.stringPool, options, "read", false);
const writeF = try booleanOption(machine.stringPool, options, "write", false);
const appendF = try booleanOption(machine.stringPool, options, "append", false);
const truncateF = try booleanOption(machine.stringPool, options, "truncate", false);
const createF = try booleanOption(machine.stringPool, options, "create", false);
const readF = try Helper.booleanOption(machine.stringPool, options, "read", false);
const writeF = try Helper.booleanOption(machine.stringPool, options, "write", false);
const appendF = try Helper.booleanOption(machine.stringPool, options, "append", false);
const truncateF = try Helper.booleanOption(machine.stringPool, options, "truncate", false);
const createF = try Helper.booleanOption(machine.stringPool, options, "create", false);

if (createF) {
const file = std.fs.cwd().createFile(path, .{ .read = readF, .truncate = truncateF, .exclusive = false }) catch |err| return Helper.raiseOsError(machine, "open", err);
Expand Down
82 changes: 82 additions & 0 deletions src/builtins/parser.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
const std = @import("std");

const AST = @import("./../ast.zig");
const Helper = @import("./helper.zig");
const Parser = @import("./../ast-interpreter.zig");

pub fn parse(machine: *Helper.Runtime, numberOfArgs: usize) !void {
const v = try Helper.getArgument(machine, numberOfArgs, 0, &[_]Helper.ValueKind{Helper.ValueValue.StringKind});
const options = try Helper.getArgument(machine, numberOfArgs, 1, &[_]Helper.ValueKind{ Helper.ValueValue.RecordKind, Helper.ValueValue.UnitKind });

const ast = try Parser.parse(machine, "input", v.v.StringKind.slice());
defer ast.destroy(machine.allocator);

const position = try Helper.booleanOption(machine.stringPool, options, "position", false);

try emit(machine, ast, position);
}
const pos = Helper.Errors.Position{ .start = 0, .end = 0 };

fn emit(machine: *Helper.Runtime, ast: *AST.Expression, position: bool) !void {
switch (ast.kind) {
.binaryOp => {
try machine.pushEmptyRecordValue();

try machine.pushStringValue("kind");
try machine.pushStringValue("binaryOp");
try machine.setRecordItemBang(pos);

try machine.pushStringValue("op");
try machine.pushStringValue(ast.kind.binaryOp.op.toString());
try machine.setRecordItemBang(pos);

try machine.pushStringValue("lhs");
try emit(machine, ast.kind.binaryOp.left, position);
try machine.setRecordItemBang(pos);

try machine.pushStringValue("rhs");
try emit(machine, ast.kind.binaryOp.right, position);
try machine.setRecordItemBang(pos);
},
.exprs => {
try machine.pushEmptyRecordValue();

try machine.pushStringValue("kind");
try machine.pushStringValue("exprs");
try machine.setRecordItemBang(pos);

try machine.pushStringValue("value");
try machine.pushEmptySequenceValue();
for (ast.kind.exprs) |expr| {
try emit(machine, expr, position);
try machine.appendSequenceItemBang(pos);
}
try machine.setRecordItemBang(pos);
},
.literalInt => {
try machine.pushEmptyRecordValue();

try machine.pushStringValue("kind");
try machine.pushStringValue("literalInt");
try machine.setRecordItemBang(pos);

try machine.pushStringValue("value");
try machine.pushIntValue(ast.kind.literalInt);
try machine.setRecordItemBang(pos);
},
else => {
std.io.getStdErr().writer().print("unreachable: {}\n", .{ast.kind}) catch {};
unreachable;
},
}

if (position) {
try machine.pushStringValue("position");
try machine.pushEmptySequenceValue();
try machine.pushIntValue(@intCast(ast.position.start));
try machine.appendSequenceItemBang(pos);
try machine.pushIntValue(@intCast(ast.position.end));
try machine.appendSequenceItemBang(pos);
try machine.setRecordItemBang(pos);
}
}
1 change: 1 addition & 0 deletions src/runtime.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1608,6 +1608,7 @@ fn setupRebo(state: *Runtime) !void {
try reboLang.v.RecordKind.setU8(state.stringPool, "float", try state.newBuiltinValue(@import("builtins/float.zig").float));
try reboLang.v.RecordKind.setU8(state.stringPool, "keys", try state.newBuiltinValue(@import("builtins/keys.zig").keys));
try reboLang.v.RecordKind.setU8(state.stringPool, "len", try state.newBuiltinValue(@import("builtins/len.zig").len));
try reboLang.v.RecordKind.setU8(state.stringPool, "parse", try state.newBuiltinValue(@import("builtins/parser.zig").parse));
try reboLang.v.RecordKind.setU8(state.stringPool, "scope", try state.newBuiltinValue(@import("builtins/scope.zig").scope));
try reboLang.v.RecordKind.setU8(state.stringPool, "scope.bind!", try state.newBuiltinValue(@import("builtins/scope.zig").bind));
try reboLang.v.RecordKind.setU8(state.stringPool, "scope.delete!", try state.newBuiltinValue(@import("builtins/scope.zig").delete));
Expand Down

0 comments on commit b732493

Please sign in to comment.