Compare commits
2 commits
0f8e19adf1
...
ec652b29d9
Author | SHA1 | Date | |
---|---|---|---|
ec652b29d9 | |||
589413488c |
5 changed files with 111 additions and 31 deletions
|
@ -167,8 +167,6 @@ pub const Compiler = struct {
|
||||||
if (token_opt) |token| {
|
if (token_opt) |token| {
|
||||||
self.parser.current = token;
|
self.parser.current = token;
|
||||||
break;
|
break;
|
||||||
} else {
|
|
||||||
self.errorCurrent(self.parser.current.lexeme);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,11 +219,12 @@ pub const Compiler = struct {
|
||||||
|
|
||||||
/// Emits bytecode for a number being loaded into the code.
|
/// Emits bytecode for a number being loaded into the code.
|
||||||
fn number(self: *Compiler) !void {
|
fn number(self: *Compiler) !void {
|
||||||
|
std.debug.warn("parsing number: '{}'\n", self.parser.previous.lexeme);
|
||||||
var value: f64 = try std.fmt.parseFloat(
|
var value: f64 = try std.fmt.parseFloat(
|
||||||
f64,
|
f64,
|
||||||
self.parser.previous.lexeme,
|
self.parser.previous.lexeme,
|
||||||
);
|
);
|
||||||
try self.emitConstant(value);
|
try self.emitConstant(values.NumberVal(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emits bytecode for a given unary.
|
/// Emits bytecode for a given unary.
|
||||||
|
|
25
src/main.zig
25
src/main.zig
|
@ -12,12 +12,12 @@ const InterpretResult = vm.InterpretResult;
|
||||||
|
|
||||||
pub var hadError = false;
|
pub var hadError = false;
|
||||||
|
|
||||||
fn run(allocator: *Allocator, data: []u8) !InterpretResult {
|
fn run(allocator: *Allocator, data: []u8) !void {
|
||||||
var stdout_file = try std.io.getStdOut();
|
var stdout_file = try std.io.getStdOut();
|
||||||
const stdout = &stdout_file.outStream().stream;
|
const stdout = &stdout_file.outStream().stream;
|
||||||
|
|
||||||
var vmach = try vm.VM.init(allocator, stdout, data, true);
|
var vmach = try vm.VM.init(allocator, stdout, data, true);
|
||||||
return try vmach.interpret();
|
try vmach.interpret();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn doError(line: usize, message: []const u8) !void {
|
pub fn doError(line: usize, message: []const u8) !void {
|
||||||
|
@ -40,9 +40,14 @@ fn runFile(allocator: *Allocator, path: []const u8) !void {
|
||||||
var slice = try allocator.alloc(u8, total_bytes);
|
var slice = try allocator.alloc(u8, total_bytes);
|
||||||
_ = try lox_file.read(slice);
|
_ = try lox_file.read(slice);
|
||||||
|
|
||||||
var res = try run(allocator, slice);
|
run(allocator, slice) catch |err| {
|
||||||
if (res == vm.InterpretResult.CompileError) std.os.exit(65);
|
switch (err) {
|
||||||
if (res == vm.InterpretResult.RuntimeError) std.os.exit(70);
|
InterpretResult.Ok => {},
|
||||||
|
InterpretResult.CompileError => std.os.exit(65),
|
||||||
|
InterpretResult.RuntimeError => std.os.exit(70),
|
||||||
|
else => return err,
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runPrompt(allocator: *Allocator) !void {
|
fn runPrompt(allocator: *Allocator) !void {
|
||||||
|
@ -59,7 +64,15 @@ fn runPrompt(allocator: *Allocator) !void {
|
||||||
return err;
|
return err;
|
||||||
};
|
};
|
||||||
|
|
||||||
_ = try run(allocator, line);
|
run(allocator, line) catch |err| {
|
||||||
|
switch (err) {
|
||||||
|
InterpretResult.Ok => {},
|
||||||
|
InterpretResult.CompileError, InterpretResult.RuntimeError => blk: {
|
||||||
|
try stdout.print("compile/runtime error.\n");
|
||||||
|
},
|
||||||
|
else => return err,
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -147,8 +147,8 @@ pub const Scanner = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peekNext(self: *Scanner) u8 {
|
fn peekNext(self: *Scanner) u8 {
|
||||||
if (self.isAtEnd()) return 0;
|
if (self.current + 1 >= self.source.len) return 0;
|
||||||
return self.source[self.current - 1];
|
return self.source[self.current + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn skipWhitespace(self: *Scanner) void {
|
fn skipWhitespace(self: *Scanner) void {
|
||||||
|
@ -259,6 +259,7 @@ pub const Scanner = struct {
|
||||||
while (self.peek() != '\n' and !self.isAtEnd()) {
|
while (self.peek() != '\n' and !self.isAtEnd()) {
|
||||||
_ = self.advance();
|
_ = self.advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
break :blk null;
|
break :blk null;
|
||||||
} else {
|
} else {
|
||||||
break :blk self.makeToken(.SLASH);
|
break :blk self.makeToken(.SLASH);
|
||||||
|
|
|
@ -3,10 +3,42 @@ const std = @import("std");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
// NOTE: right now, only numbers.
|
// NOTE: right now, only numbers.
|
||||||
pub const Value = f64;
|
|
||||||
|
pub const ValueType = enum(u8) {
|
||||||
|
Bool,
|
||||||
|
Nil,
|
||||||
|
Number,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ValueValue = union(ValueType) {
|
||||||
|
Bool: bool,
|
||||||
|
Nil: void,
|
||||||
|
Number: f64,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Value = struct {
|
||||||
|
vtype: ValueType,
|
||||||
|
as: ValueValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
// helper functions
|
||||||
|
pub fn BoolVal(val: bool) Value {
|
||||||
|
return Value{ .vtype = .Bool, .as = ValueValue{ .Bool = val } };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn NilVal() Value {
|
||||||
|
return Value{ .vtype = .Nil, .as = ValueValue{ .Nil = {} } };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn NumberVal(val: f64) Value {
|
||||||
|
return Value{ .vtype = .Number, .as = ValueValue{ .Number = val } };
|
||||||
|
}
|
||||||
|
|
||||||
pub fn printValue(stdout: var, value: Value) !void {
|
pub fn printValue(stdout: var, value: Value) !void {
|
||||||
try stdout.print("{}", value);
|
switch (value.as) {
|
||||||
|
.Number => try stdout.print("{}", value.as.Number),
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const ValueList = struct {
|
pub const ValueList = struct {
|
||||||
|
|
71
src/vm.zig
71
src/vm.zig
|
@ -1,6 +1,7 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const chunk = @import("chunk.zig");
|
const chunk = @import("chunk.zig");
|
||||||
const value = @import("value.zig");
|
const value = @import("value.zig");
|
||||||
|
const values = value;
|
||||||
const compiler = @import("compiler.zig");
|
const compiler = @import("compiler.zig");
|
||||||
|
|
||||||
const Chunk = chunk.Chunk;
|
const Chunk = chunk.Chunk;
|
||||||
|
@ -9,7 +10,7 @@ const Compiler = compiler.Compiler;
|
||||||
|
|
||||||
pub const StdOut = *std.io.OutStream(std.fs.File.WriteError);
|
pub const StdOut = *std.io.OutStream(std.fs.File.WriteError);
|
||||||
|
|
||||||
pub const InterpretResult = enum {
|
pub const InterpretResult = error{
|
||||||
Ok,
|
Ok,
|
||||||
CompileError,
|
CompileError,
|
||||||
RuntimeError,
|
RuntimeError,
|
||||||
|
@ -91,35 +92,50 @@ pub const VM = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// gets a f64 out of a value on the top of the stack.
|
/// gets a f64 out of a value on the top of the stack.
|
||||||
fn popNum(self: *VM) f64 {
|
fn popNum(self: *VM) !f64 {
|
||||||
return self.pop();
|
var val: Value = self.pop();
|
||||||
|
|
||||||
|
switch (val.vtype) {
|
||||||
|
.Number => return val.as.Number,
|
||||||
|
|
||||||
|
else => |vtype| blk: {
|
||||||
|
self.runtimeError("Expected number, got {x}", vtype);
|
||||||
|
return InterpretResult.RuntimeError;
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn doAdd(self: *VM) !void {
|
fn doAdd(self: *VM) !void {
|
||||||
var b = self.popNum();
|
var b = try self.popNum();
|
||||||
var a = self.popNum();
|
var a = try self.popNum();
|
||||||
try self.push(a + b);
|
try self.push(values.NumberVal(a + b));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn doSub(self: *VM) !void {
|
fn doSub(self: *VM) !void {
|
||||||
var b = self.popNum();
|
var b = try self.popNum();
|
||||||
var a = self.popNum();
|
var a = try self.popNum();
|
||||||
try self.push(a * b);
|
try self.push(values.NumberVal(a - b));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn doMul(self: *VM) !void {
|
fn doMul(self: *VM) !void {
|
||||||
var b = self.popNum();
|
var b = try self.popNum();
|
||||||
var a = self.popNum();
|
var a = try self.popNum();
|
||||||
try self.push(a * b);
|
try self.push(values.NumberVal(a * b));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn doDiv(self: *VM) !void {
|
fn doDiv(self: *VM) !void {
|
||||||
var b = self.popNum();
|
var b = try self.popNum();
|
||||||
var a = self.popNum();
|
var a = try self.popNum();
|
||||||
try self.push(a / b);
|
try self.push(values.NumberVal(a / b));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(self: *VM) !InterpretResult {
|
fn runtimeError(self: *VM, comptime fmt: []const u8, args: ...) void {
|
||||||
|
std.debug.warn(fmt, args);
|
||||||
|
std.debug.warn("\n[line {}] in script\n", self.chk.lines[self.ip]);
|
||||||
|
self.resetStack();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(self: *VM) !void {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (self.debug_flag) {
|
if (self.debug_flag) {
|
||||||
try self.debugStack();
|
try self.debugStack();
|
||||||
|
@ -150,7 +166,22 @@ pub const VM = struct {
|
||||||
chunk.OpCode.Subtract => try self.doSub(),
|
chunk.OpCode.Subtract => try self.doSub(),
|
||||||
chunk.OpCode.Multiply => try self.doMul(),
|
chunk.OpCode.Multiply => try self.doMul(),
|
||||||
chunk.OpCode.Divide => try self.doDiv(),
|
chunk.OpCode.Divide => try self.doDiv(),
|
||||||
chunk.OpCode.Negate => try self.push(-self.pop()),
|
chunk.OpCode.Negate => blk: {
|
||||||
|
var val = self.peek(0);
|
||||||
|
if (val.vtype != .Bool) {
|
||||||
|
self.runtimeError("Operand must be a number.");
|
||||||
|
return InterpretResult.RuntimeError;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = self.pop();
|
||||||
|
switch (val.as) {
|
||||||
|
.Number => |num| {
|
||||||
|
try self.push(values.NumberVal(-num));
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
else => blk: {
|
else => blk: {
|
||||||
std.debug.warn("Unknown instruction: {x}\n", instruction);
|
std.debug.warn("Unknown instruction: {x}\n", instruction);
|
||||||
return InterpretResult.RuntimeError;
|
return InterpretResult.RuntimeError;
|
||||||
|
@ -159,7 +190,7 @@ pub const VM = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interpret(self: *VM) !InterpretResult {
|
pub fn interpret(self: *VM) !void {
|
||||||
//self.ip = 0;
|
//self.ip = 0;
|
||||||
//self.debug("VM start\n");
|
//self.debug("VM start\n");
|
||||||
//var res = try self.run();
|
//var res = try self.run();
|
||||||
|
@ -196,4 +227,8 @@ pub const VM = struct {
|
||||||
self.stackTop -= 1;
|
self.stackTop -= 1;
|
||||||
return self.stack[self.stackTop];
|
return self.stack[self.stackTop];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn peek(self: *VM, distance: usize) Value {
|
||||||
|
return self.stack[self.stackTop - 1 - distance];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue