Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
374a221e94
|
|||
|
a42770f4cc
|
|||
|
d7e71d58e1
|
|||
|
69bd99a830
|
|||
|
ecb9a63efb
|
|||
|
a018213ec5
|
|||
|
bd95d6b779
|
|||
|
73268f59b5
|
|||
|
8f5a3e00b8
|
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"MD033": false
|
|
||||||
}
|
|
||||||
11
README.md
11
README.md
@@ -1,20 +1,15 @@
|
|||||||
# neon
|
# neon
|
||||||
|
|
||||||
<small>(stands for Not Everything Oughtta've a Name)</small>
|
[](LICENSE) [](https://git.svecs.cz/svecs/neon/actions/?workflow=test.yml)
|
||||||
|
|
||||||
[](LICENSE)
|
|
||||||
[](https://git.svecs.cz/svecs/neon/actions/?workflow=test.yml)
|
|
||||||
|
|
||||||
> a simple programming language (hopefully one day)
|
> a simple programming language (hopefully one day)
|
||||||
|
|
||||||
## features
|
## features
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> this project is still in very early development, so features are limited and
|
> this project is still in very early development, so features are limited and subject to change
|
||||||
> subject to change
|
|
||||||
>
|
>
|
||||||
> for now the language uses an s-expression syntax (even though it's not a lisp)
|
> for now the language uses an s-expression syntax (even though it's not a lisp) to simplify parsing. this syntax WILL change in the future!
|
||||||
> to simplify parsing. this syntax WILL change in the future!
|
|
||||||
|
|
||||||
- currently none lol
|
- currently none lol
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ pub fn build(b: *std.Build) void {
|
|||||||
.root_source_file = b.path("src/main.zig"),
|
.root_source_file = b.path("src/main.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
.imports = &.{.{ .name = "libneon", .module = lib.root_module }},
|
.imports = &.{
|
||||||
|
.{ .name = "libneon", .module = lib.root_module },
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -40,7 +42,9 @@ pub fn build(b: *std.Build) void {
|
|||||||
.root_source_file = b.path("tests/all.zig"),
|
.root_source_file = b.path("tests/all.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
.imports = &.{.{ .name = "libneon", .module = lib.root_module }},
|
.imports = &.{
|
||||||
|
.{ .name = "libneon", .module = lib.root_module },
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1 @@
|
|||||||
(func print_fibs [limit] {
|
(print_int (+ 1 (* 2 34)))
|
||||||
(let a 1)
|
|
||||||
(let b 1)
|
|
||||||
|
|
||||||
(while (< a limit) {
|
|
||||||
(printf "%d\n" a)
|
|
||||||
(set b (+ b a))
|
|
||||||
(set a (- b a))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
(func main [] {
|
|
||||||
(print_fibs 100)
|
|
||||||
})
|
|
||||||
|
|||||||
75
lib/interpreter.zig
Normal file
75
lib/interpreter.zig
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const parser = @import("parser.zig");
|
||||||
|
|
||||||
|
pub const Interpreter = struct {
|
||||||
|
stdout: *std.Io.Writer,
|
||||||
|
|
||||||
|
pub fn interpret(self: @This(), program: parser.Program) !void {
|
||||||
|
for (program.stmts) |stmt| {
|
||||||
|
try self.run_stmt(stmt);
|
||||||
|
try self.stdout.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_stmt(self: @This(), stmt: parser.Stmt) !void {
|
||||||
|
switch (stmt) {
|
||||||
|
.expr => |expr| {
|
||||||
|
const val = try self.evaluate(expr);
|
||||||
|
if (val != .nil) {
|
||||||
|
try self.stdout.print("{any}\n", .{val});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
std.debug.print("unimplemented run for '{t}' stmt\n", .{stmt});
|
||||||
|
return error.Todo;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evaluate(self: @This(), expr: parser.Expr) !Value {
|
||||||
|
switch (expr) {
|
||||||
|
.nil => return .nil,
|
||||||
|
.variable => |val| {
|
||||||
|
std.debug.print("unimplemented variable '{s}' access\n", .{val});
|
||||||
|
return error.Todo;
|
||||||
|
},
|
||||||
|
.integer => |val| return .{ .integer = @intCast(val) },
|
||||||
|
.string => |val| return .{ .string = val },
|
||||||
|
.func_call => |call| {
|
||||||
|
if (std.mem.eql(u8, call.name, "+")) {
|
||||||
|
var result: i64 = 0;
|
||||||
|
for (call.args) |arg| {
|
||||||
|
const val = try self.evaluate(arg);
|
||||||
|
if (val != .integer) return error.InvalidArg;
|
||||||
|
result += val.integer;
|
||||||
|
}
|
||||||
|
return .{ .integer = result };
|
||||||
|
} else if (std.mem.eql(u8, call.name, "*")) {
|
||||||
|
var result: i64 = 1;
|
||||||
|
for (call.args) |arg| {
|
||||||
|
const val = try self.evaluate(arg);
|
||||||
|
if (val != .integer) return error.InvalidArg;
|
||||||
|
result *= val.integer;
|
||||||
|
}
|
||||||
|
return .{ .integer = result };
|
||||||
|
} else if (std.mem.eql(u8, call.name, "print_int")) {
|
||||||
|
if (call.args.len != 1) return error.InvalidArg;
|
||||||
|
const arg = try self.evaluate(call.args[0]);
|
||||||
|
if (arg != .integer) return error.InvalidArg;
|
||||||
|
try self.stdout.print("{d}\n", .{arg.integer});
|
||||||
|
return .nil;
|
||||||
|
} else {
|
||||||
|
std.debug.print("unimplemented call to '{s}'\n", .{call.name});
|
||||||
|
return error.Todo;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Value = union(enum) {
|
||||||
|
nil,
|
||||||
|
integer: i64,
|
||||||
|
string: []const u8,
|
||||||
|
};
|
||||||
@@ -11,7 +11,7 @@ pub const Token = union(enum) {
|
|||||||
identifier: []const u8,
|
identifier: []const u8,
|
||||||
string: []const u8,
|
string: []const u8,
|
||||||
|
|
||||||
pub fn format(self: @This(), w: *std.Io.Writer) !void {
|
pub fn format(self: @This(), w: *std.Io.Writer) error{WriteFailed}!void {
|
||||||
try switch (self) {
|
try switch (self) {
|
||||||
.lparen => w.print("lparen", .{}),
|
.lparen => w.print("lparen", .{}),
|
||||||
.rparen => w.print("rparen", .{}),
|
.rparen => w.print("rparen", .{}),
|
||||||
@@ -26,7 +26,7 @@ pub const Token = union(enum) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn lex(gpa: std.mem.Allocator, src: []const u8) ![]const Token {
|
pub fn lex(gpa: std.mem.Allocator, src: []const u8) error{ OutOfMemory, Overflow, InvalidCharacter }![]const Token {
|
||||||
var result = std.ArrayList(Token).empty;
|
var result = std.ArrayList(Token).empty;
|
||||||
var idx: usize = 0;
|
var idx: usize = 0;
|
||||||
while (idx < src.len) {
|
while (idx < src.len) {
|
||||||
@@ -51,11 +51,30 @@ pub fn lex(gpa: std.mem.Allocator, src: []const u8) ![]const Token {
|
|||||||
idx -= 1;
|
idx -= 1;
|
||||||
break :sw .{ .integer = int };
|
break :sw .{ .integer = int };
|
||||||
},
|
},
|
||||||
'A'...'Z', 'a'...'z', '!', '$'...'\'', '*'...'/', ':'...'@', '^', '_', '~' => {
|
'A'...'Z',
|
||||||
|
'a'...'z',
|
||||||
|
'!',
|
||||||
|
'$'...'\'',
|
||||||
|
'*'...'/',
|
||||||
|
':'...'@',
|
||||||
|
'^',
|
||||||
|
'_',
|
||||||
|
'~',
|
||||||
|
=> {
|
||||||
const start = idx;
|
const start = idx;
|
||||||
while (idx < src.len) {
|
while (idx < src.len) {
|
||||||
switch (src[idx]) {
|
switch (src[idx]) {
|
||||||
'A'...'Z', 'a'...'z', '0'...'9', '!', '$'...'\'', '*'...'/', ':'...'@', '^', '_', '~' => {
|
'A'...'Z',
|
||||||
|
'a'...'z',
|
||||||
|
'0'...'9',
|
||||||
|
'!',
|
||||||
|
'$'...'\'',
|
||||||
|
'*'...'/',
|
||||||
|
':'...'@',
|
||||||
|
'^',
|
||||||
|
'_',
|
||||||
|
'~',
|
||||||
|
=> {
|
||||||
idx += 1;
|
idx += 1;
|
||||||
},
|
},
|
||||||
else => break,
|
else => break,
|
||||||
|
|||||||
170
lib/parser.zig
170
lib/parser.zig
@@ -1,49 +1,16 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const lexer = @import("lexer.zig");
|
const lexer = @import("lexer.zig");
|
||||||
|
|
||||||
pub const SExpr = union(enum) {
|
const SExpr = union(enum) {
|
||||||
integer: u64, // 42
|
integer: u64, // 42
|
||||||
identifier: []const u8, // module.name
|
identifier: []const u8, // module.name
|
||||||
string: []const u8, // "foo bar"
|
string: []const u8, // "foo bar"
|
||||||
call: []const SExpr, // (+ 1 2 3)
|
call: []const SExpr, // (+ 1 2 3)
|
||||||
block: []const SExpr, // { (print "hi") }
|
block: []const SExpr, // { (print "hi") }
|
||||||
list: []const SExpr, // [1 2 3]
|
list: []const SExpr, // [1 2 3]
|
||||||
|
|
||||||
fn format_(self: @This(), w: *std.Io.Writer, depth: usize) !void {
|
|
||||||
for (0..depth) |_| {
|
|
||||||
try w.print(" ", .{});
|
|
||||||
}
|
|
||||||
switch (self) {
|
|
||||||
.integer => |int| try w.print("int: {d}\n", .{int}),
|
|
||||||
.identifier => |id| try w.print("id: {s}\n", .{id}),
|
|
||||||
.string => |str| try w.print("str: {s}\n", .{str}),
|
|
||||||
.call => |call| {
|
|
||||||
try w.print("call: {s}\n", .{if (call.len == 0) "()" else ""});
|
|
||||||
for (call) |expr| {
|
|
||||||
try expr.format_(w, depth + 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.block => |block| {
|
|
||||||
try w.print("block: {s}\n", .{if (block.len == 0) "{}" else ""});
|
|
||||||
for (block) |expr| {
|
|
||||||
try expr.format_(w, depth + 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.list => |list| {
|
|
||||||
try w.print("list: {s}\n", .{if (list.len == 0) "[]" else ""});
|
|
||||||
for (list) |expr| {
|
|
||||||
try expr.format_(w, depth + 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn format(self: @This(), w: *std.Io.Writer) !void {
|
|
||||||
try self.format_(w, 0);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn parse_expr(gpa: std.mem.Allocator, tokens: []const lexer.Token) !struct { SExpr, usize } {
|
fn parse_sexpr(gpa: std.mem.Allocator, tokens: []const lexer.Token) !struct { SExpr, usize } {
|
||||||
if (tokens.len == 0) {
|
if (tokens.len == 0) {
|
||||||
return error.UnexpectedEOF;
|
return error.UnexpectedEOF;
|
||||||
}
|
}
|
||||||
@@ -56,8 +23,8 @@ fn parse_expr(gpa: std.mem.Allocator, tokens: []const lexer.Token) !struct { SEx
|
|||||||
var call = std.ArrayList(SExpr).empty;
|
var call = std.ArrayList(SExpr).empty;
|
||||||
var tokens_rest = tokens[1..];
|
var tokens_rest = tokens[1..];
|
||||||
while (tokens_rest.len > 0 and tokens_rest[0] != .rparen) {
|
while (tokens_rest.len > 0 and tokens_rest[0] != .rparen) {
|
||||||
const expr, const consumed = try parse_expr(gpa, tokens_rest);
|
const sexpr, const consumed = try parse_sexpr(gpa, tokens_rest);
|
||||||
try call.append(gpa, expr);
|
try call.append(gpa, sexpr);
|
||||||
tokens_rest = tokens_rest[consumed..];
|
tokens_rest = tokens_rest[consumed..];
|
||||||
}
|
}
|
||||||
if (tokens_rest.len == 0) {
|
if (tokens_rest.len == 0) {
|
||||||
@@ -70,8 +37,8 @@ fn parse_expr(gpa: std.mem.Allocator, tokens: []const lexer.Token) !struct { SEx
|
|||||||
var block = std.ArrayList(SExpr).empty;
|
var block = std.ArrayList(SExpr).empty;
|
||||||
var tokens_rest = tokens[1..];
|
var tokens_rest = tokens[1..];
|
||||||
while (tokens_rest.len > 0 and tokens_rest[0] != .rbrace) {
|
while (tokens_rest.len > 0 and tokens_rest[0] != .rbrace) {
|
||||||
const expr, const consumed = try parse_expr(gpa, tokens_rest);
|
const sexpr, const consumed = try parse_sexpr(gpa, tokens_rest);
|
||||||
try block.append(gpa, expr);
|
try block.append(gpa, sexpr);
|
||||||
tokens_rest = tokens_rest[consumed..];
|
tokens_rest = tokens_rest[consumed..];
|
||||||
}
|
}
|
||||||
if (tokens_rest.len == 0) {
|
if (tokens_rest.len == 0) {
|
||||||
@@ -84,8 +51,8 @@ fn parse_expr(gpa: std.mem.Allocator, tokens: []const lexer.Token) !struct { SEx
|
|||||||
var list = std.ArrayList(SExpr).empty;
|
var list = std.ArrayList(SExpr).empty;
|
||||||
var tokens_rest = tokens[1..];
|
var tokens_rest = tokens[1..];
|
||||||
while (tokens_rest.len > 0 and tokens_rest[0] != .rbracket) {
|
while (tokens_rest.len > 0 and tokens_rest[0] != .rbracket) {
|
||||||
const expr, const consumed = try parse_expr(gpa, tokens_rest);
|
const sexpr, const consumed = try parse_sexpr(gpa, tokens_rest);
|
||||||
try list.append(gpa, expr);
|
try list.append(gpa, sexpr);
|
||||||
tokens_rest = tokens_rest[consumed..];
|
tokens_rest = tokens_rest[consumed..];
|
||||||
}
|
}
|
||||||
if (tokens_rest.len == 0) {
|
if (tokens_rest.len == 0) {
|
||||||
@@ -100,15 +67,130 @@ fn parse_expr(gpa: std.mem.Allocator, tokens: []const lexer.Token) !struct { SEx
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_sexprs(gpa: std.mem.Allocator, tokens: []const lexer.Token) ![]const SExpr {
|
fn parse_sexprs(gpa: std.mem.Allocator, tokens: []const lexer.Token) ![]const SExpr {
|
||||||
var result = std.ArrayList(SExpr).empty;
|
var result = std.ArrayList(SExpr).empty;
|
||||||
var idx: usize = 0;
|
var idx: usize = 0;
|
||||||
|
|
||||||
while (idx < tokens.len) {
|
while (idx < tokens.len) {
|
||||||
const expr, const consumed = try parse_expr(gpa, tokens[idx..]);
|
const sexpr, const consumed = try parse_sexpr(gpa, tokens[idx..]);
|
||||||
try result.append(gpa, expr);
|
try result.append(gpa, sexpr);
|
||||||
idx += consumed;
|
idx += consumed;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.toOwnedSlice(gpa);
|
return result.toOwnedSlice(gpa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const Program = struct {
|
||||||
|
stmts: []const Stmt,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Stmt = union(enum) {
|
||||||
|
var_let: struct {
|
||||||
|
name: []const u8,
|
||||||
|
value: Expr,
|
||||||
|
},
|
||||||
|
var_set: struct {
|
||||||
|
name: []const u8,
|
||||||
|
value: Expr,
|
||||||
|
},
|
||||||
|
while_loop: struct {
|
||||||
|
cond: Expr,
|
||||||
|
body: []const Stmt,
|
||||||
|
},
|
||||||
|
expr: Expr,
|
||||||
|
|
||||||
|
fn from(gpa: std.mem.Allocator, sexpr: SExpr) !Stmt {
|
||||||
|
switch (sexpr) {
|
||||||
|
.call => |ss| {
|
||||||
|
if (ss.len == 0 or ss[0] != .identifier) {
|
||||||
|
return error.InvalidCall;
|
||||||
|
}
|
||||||
|
const name = ss[0].identifier;
|
||||||
|
if (std.mem.eql(u8, name, "let")) {
|
||||||
|
if (ss.len != 3 or ss[1] != .identifier) {
|
||||||
|
return error.InvalidLet;
|
||||||
|
}
|
||||||
|
const var_name = ss[1].identifier;
|
||||||
|
return .{ .var_let = .{
|
||||||
|
.name = var_name,
|
||||||
|
.value = try Expr.from(gpa, ss[2]),
|
||||||
|
} };
|
||||||
|
} else if (std.mem.eql(u8, name, "set")) {
|
||||||
|
if (ss.len != 3 or ss[1] != .identifier) {
|
||||||
|
return error.InvalidSet;
|
||||||
|
}
|
||||||
|
const var_name = ss[1].identifier;
|
||||||
|
return .{ .var_set = .{
|
||||||
|
.name = var_name,
|
||||||
|
.value = try Expr.from(gpa, ss[2]),
|
||||||
|
} };
|
||||||
|
} else if (std.mem.eql(u8, name, "while")) {
|
||||||
|
if (ss.len != 3 or ss[2] != .block) {
|
||||||
|
return error.InvalidWhile;
|
||||||
|
}
|
||||||
|
const cond = try Expr.from(gpa, ss[1]);
|
||||||
|
var body = std.ArrayList(Stmt).empty;
|
||||||
|
for (ss[2].block) |inner| {
|
||||||
|
try body.append(gpa, try Stmt.from(gpa, inner));
|
||||||
|
}
|
||||||
|
return .{ .while_loop = .{
|
||||||
|
.cond = cond,
|
||||||
|
.body = try body.toOwnedSlice(gpa),
|
||||||
|
} };
|
||||||
|
} else {
|
||||||
|
return .{ .expr = try Expr.from(gpa, sexpr) };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => return .{ .expr = try Expr.from(gpa, sexpr) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Expr = union(enum) {
|
||||||
|
nil,
|
||||||
|
variable: []const u8,
|
||||||
|
integer: u64,
|
||||||
|
string: []const u8,
|
||||||
|
func_call: struct {
|
||||||
|
name: []const u8,
|
||||||
|
args: []const Expr,
|
||||||
|
},
|
||||||
|
|
||||||
|
fn from(gpa: std.mem.Allocator, sexpr: SExpr) !Expr {
|
||||||
|
switch (sexpr) {
|
||||||
|
.integer => |int| return .{ .integer = int },
|
||||||
|
.identifier => |id| {
|
||||||
|
if (std.mem.eql(u8, id, "nil")) {
|
||||||
|
return .nil;
|
||||||
|
}
|
||||||
|
return .{ .variable = id };
|
||||||
|
},
|
||||||
|
.string => |str| return .{ .string = str },
|
||||||
|
.call => |ss| {
|
||||||
|
if (ss.len < 1 or ss[0] != .identifier) return error.InvalidCall;
|
||||||
|
var args = std.ArrayList(Expr).empty;
|
||||||
|
for (ss[1..]) |inner| {
|
||||||
|
try args.append(gpa, try Expr.from(gpa, inner));
|
||||||
|
}
|
||||||
|
return .{ .func_call = .{
|
||||||
|
.name = ss[0].identifier,
|
||||||
|
.args = try args.toOwnedSlice(gpa),
|
||||||
|
} };
|
||||||
|
},
|
||||||
|
.block => return error.BlockAsExpr,
|
||||||
|
.list => return error.ListsNotImplemented,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn parse(gpa: std.mem.Allocator, tokens: []const lexer.Token) !Program {
|
||||||
|
const sexprs = try parse_sexprs(gpa, tokens);
|
||||||
|
var stmts = std.ArrayList(Stmt).empty;
|
||||||
|
for (sexprs) |sexpr| {
|
||||||
|
const stmt = try Stmt.from(gpa, sexpr);
|
||||||
|
try stmts.append(gpa, stmt);
|
||||||
|
}
|
||||||
|
return Program{
|
||||||
|
.stmts = try stmts.toOwnedSlice(gpa),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
pub const lexer = @import("lexer.zig");
|
pub const lexer = @import("lexer.zig");
|
||||||
pub const parser = @import("parser.zig");
|
pub const parser = @import("parser.zig");
|
||||||
|
pub const interpreter = @import("interpreter.zig");
|
||||||
|
|||||||
24
src/main.zig
24
src/main.zig
@@ -3,7 +3,12 @@ const std = @import("std");
|
|||||||
const lib = @import("libneon");
|
const lib = @import("libneon");
|
||||||
|
|
||||||
fn usage(w: *std.Io.Writer, program: []const u8) error{WriteFailed}!void {
|
fn usage(w: *std.Io.Writer, program: []const u8) error{WriteFailed}!void {
|
||||||
try w.print("Usage: {s} <input>\n", .{program});
|
try w.print(
|
||||||
|
\\Usage: {s} [flags] <input>
|
||||||
|
\\Flags:
|
||||||
|
\\ -h, --help Show this help message and quit
|
||||||
|
\\
|
||||||
|
, .{program});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn entry(init: struct {
|
fn entry(init: struct {
|
||||||
@@ -16,7 +21,7 @@ fn entry(init: struct {
|
|||||||
var input_path: ?[]const u8 = null;
|
var input_path: ?[]const u8 = null;
|
||||||
|
|
||||||
for (init.args[1..]) |arg| {
|
for (init.args[1..]) |arg| {
|
||||||
if (std.mem.eql(u8, arg, "--help")) {
|
if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) {
|
||||||
try usage(init.stdout, program_name);
|
try usage(init.stdout, program_name);
|
||||||
try init.stdout.flush();
|
try init.stdout.flush();
|
||||||
return;
|
return;
|
||||||
@@ -37,16 +42,19 @@ fn entry(init: struct {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
const src = try std.fs.cwd().readFileAlloc(init.arena.allocator(), input_path.?, input_stat.size);
|
const src = try std.fs.cwd().readFileAlloc(
|
||||||
|
init.arena.allocator(),
|
||||||
|
input_path.?,
|
||||||
|
input_stat.size,
|
||||||
|
);
|
||||||
|
|
||||||
const tokens = try lib.lexer.lex(init.arena.allocator(), src);
|
const tokens = try lib.lexer.lex(init.arena.allocator(), src);
|
||||||
|
|
||||||
const exprs = try lib.parser.parse_sexprs(init.arena.allocator(), tokens);
|
const program = try lib.parser.parse(init.arena.allocator(), tokens);
|
||||||
|
|
||||||
for (exprs) |expr| {
|
const interpreter = lib.interpreter.Interpreter{ .stdout = init.stdout };
|
||||||
try init.stdout.print("{f}", .{expr});
|
|
||||||
try init.stdout.flush();
|
try interpreter.interpret(program);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
|
|||||||
@@ -3,50 +3,49 @@ const t = std.testing;
|
|||||||
|
|
||||||
const lib = @import("libneon");
|
const lib = @import("libneon");
|
||||||
|
|
||||||
test "large tree" {
|
test "program" {
|
||||||
var arena = std.heap.ArenaAllocator.init(t.allocator);
|
var arena = std.heap.ArenaAllocator.init(t.allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
const gpa = arena.allocator();
|
const gpa = arena.allocator();
|
||||||
|
|
||||||
try t.expectEqualDeep(
|
try t.expectEqualDeep(
|
||||||
&[_]lib.parser.SExpr{
|
lib.parser.Program{
|
||||||
.{ .call = &.{
|
.stmts = &.{
|
||||||
.{ .identifier = "foo" },
|
.{ .expr = .{ .func_call = .{ .name = "foo", .args = &.{
|
||||||
.{ .identifier = "bar" },
|
.{ .variable = "bar" },
|
||||||
.{ .string = "baz" },
|
.{ .string = "baz" },
|
||||||
.{ .list = &.{
|
.{ .func_call = .{ .name = "+", .args = &.{
|
||||||
.{ .integer = 1 },
|
.{ .integer = 1 },
|
||||||
.{ .integer = 2 },
|
.{ .integer = 2 },
|
||||||
.{ .integer = 3 },
|
} } },
|
||||||
} },
|
} } } },
|
||||||
.{ .block = &.{} },
|
},
|
||||||
} },
|
|
||||||
},
|
},
|
||||||
try lib.parser.parse_sexprs(gpa, &.{
|
try lib.parser.parse(gpa, &.{
|
||||||
.lparen,
|
.lparen,
|
||||||
.{ .identifier = "foo" },
|
.{ .identifier = "foo" },
|
||||||
.{ .identifier = "bar" },
|
.{ .identifier = "bar" },
|
||||||
.{ .string = "baz" },
|
.{ .string = "baz" },
|
||||||
.lbracket,
|
.lparen,
|
||||||
|
.{ .identifier = "+" },
|
||||||
.{ .integer = 1 },
|
.{ .integer = 1 },
|
||||||
.{ .integer = 2 },
|
.{ .integer = 2 },
|
||||||
.{ .integer = 3 },
|
.rparen,
|
||||||
.rbracket,
|
|
||||||
.lbrace,
|
|
||||||
.rbrace,
|
|
||||||
.rparen,
|
.rparen,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "empty" {
|
test "empty program" {
|
||||||
var arena = std.heap.ArenaAllocator.init(t.allocator);
|
var arena = std.heap.ArenaAllocator.init(t.allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
const gpa = arena.allocator();
|
const gpa = arena.allocator();
|
||||||
|
|
||||||
try t.expectEqualDeep(
|
try t.expectEqualDeep(
|
||||||
&.{},
|
lib.parser.Program{
|
||||||
lib.parser.parse_sexprs(gpa, &.{}),
|
.stmts = &.{},
|
||||||
|
},
|
||||||
|
lib.parser.parse(gpa, &.{}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +56,7 @@ test "unmatched delimiters" {
|
|||||||
|
|
||||||
try t.expectError(
|
try t.expectError(
|
||||||
error.UnmatchedParen,
|
error.UnmatchedParen,
|
||||||
lib.parser.parse_sexprs(gpa, &.{
|
lib.parser.parse(gpa, &.{
|
||||||
.lbracket,
|
.lbracket,
|
||||||
.{ .integer = 34 },
|
.{ .integer = 34 },
|
||||||
.lparen,
|
.lparen,
|
||||||
@@ -67,7 +66,7 @@ test "unmatched delimiters" {
|
|||||||
|
|
||||||
try t.expectError(
|
try t.expectError(
|
||||||
error.UnmatchedBracket,
|
error.UnmatchedBracket,
|
||||||
lib.parser.parse_sexprs(gpa, &.{
|
lib.parser.parse(gpa, &.{
|
||||||
.lparen,
|
.lparen,
|
||||||
.{ .integer = 34 },
|
.{ .integer = 34 },
|
||||||
.lbracket,
|
.lbracket,
|
||||||
|
|||||||
Reference in New Issue
Block a user