Compare commits
2 Commits
73268f59b5
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
41d36ce8e1
|
|||
|
083f5c4016
|
3
.markdownlint.json
Normal file
3
.markdownlint.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"MD033": false
|
||||||
|
}
|
||||||
11
README.md
11
README.md
@@ -1,15 +1,20 @@
|
|||||||
# neon
|
# neon
|
||||||
|
|
||||||
[](LICENSE) [](https://git.svecs.cz/svecs/neon/actions/?workflow=test.yml)
|
<small>(stands for Not Everything Oughtta've a Name)</small>
|
||||||
|
|
||||||
|
[](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 subject to change
|
> this project is still in very early development, so features are limited and
|
||||||
|
> subject to change
|
||||||
>
|
>
|
||||||
> 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!
|
> 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!
|
||||||
|
|
||||||
- currently none lol
|
- currently none lol
|
||||||
|
|
||||||
|
|||||||
133
lib/parser.zig
133
lib/parser.zig
@@ -41,92 +41,9 @@ pub const SExpr = union(enum) {
|
|||||||
pub fn format(self: @This(), w: *std.Io.Writer) !void {
|
pub fn format(self: @This(), w: *std.Io.Writer) !void {
|
||||||
try self.format_(w, 0);
|
try self.format_(w, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_stmt(self: @This(), gpa: std.mem.Allocator) !Stmt {
|
|
||||||
switch (self) {
|
|
||||||
.call => |ss| {
|
|
||||||
if (ss.len == 0) {
|
|
||||||
return error.EmptyCall;
|
|
||||||
}
|
|
||||||
const name = ss[0].identifier;
|
|
||||||
if (std.mem.eql(u8, name, "func")) {
|
|
||||||
if (ss.len != 4 or ss[1] != .identifier or ss[2] != .list or ss[3] != .block) return error.InvalidFunc;
|
|
||||||
const func_name = ss[1].identifier;
|
|
||||||
var args = std.ArrayList([]const u8).empty;
|
|
||||||
for (ss[2].list) |arg| {
|
|
||||||
if (arg != .identifier) return error.InvalidFuncArg;
|
|
||||||
try args.append(gpa, arg.identifier);
|
|
||||||
}
|
|
||||||
var body = std.ArrayList(Stmt).empty;
|
|
||||||
for (ss[3].block) |sexpr| {
|
|
||||||
try body.append(gpa, try sexpr.parse_stmt(gpa));
|
|
||||||
}
|
|
||||||
return .{ .func_decl = .{
|
|
||||||
.name = func_name,
|
|
||||||
.args = try args.toOwnedSlice(gpa),
|
|
||||||
.body = try body.toOwnedSlice(gpa),
|
|
||||||
} };
|
|
||||||
} else 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 ss[2].parse_expr(gpa),
|
|
||||||
} };
|
|
||||||
} else if (std.mem.eql(u8, name, "set")) {
|
|
||||||
if (ss.len != 3 or ss[1] != .identifier) return error.InvalidLet;
|
|
||||||
const var_name = ss[1].identifier;
|
|
||||||
return .{ .var_set = .{
|
|
||||||
.name = var_name,
|
|
||||||
.value = try ss[2].parse_expr(gpa),
|
|
||||||
} };
|
|
||||||
} else if (std.mem.eql(u8, name, "while")) {
|
|
||||||
if (ss.len != 3 or ss[2] != .block) return error.InvalidWhile;
|
|
||||||
const cond = try ss[1].parse_expr(gpa);
|
|
||||||
var body = std.ArrayList(Stmt).empty;
|
|
||||||
for (ss[2].block) |sexpr| {
|
|
||||||
try body.append(gpa, try sexpr.parse_stmt(gpa));
|
|
||||||
}
|
|
||||||
return .{ .while_loop = .{
|
|
||||||
.cond = cond,
|
|
||||||
.body = try body.toOwnedSlice(gpa),
|
|
||||||
} };
|
|
||||||
} else {
|
|
||||||
return .{ .expr = try self.parse_expr(gpa) };
|
|
||||||
}
|
|
||||||
},
|
|
||||||
else => return .{ .expr = try self.parse_expr(gpa) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_expr(self: @This(), gpa: std.mem.Allocator) !Expr {
|
|
||||||
switch (self) {
|
|
||||||
.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..]) |sexpr| {
|
|
||||||
try args.append(gpa, try sexpr.parse_expr(gpa));
|
|
||||||
}
|
|
||||||
return .{ .func_call = .{
|
|
||||||
.name = ss[0].identifier,
|
|
||||||
.args = try args.toOwnedSlice(gpa),
|
|
||||||
} };
|
|
||||||
},
|
|
||||||
.block => return error.BlockAsExpr,
|
|
||||||
.list => return error.ListsNotImplemented,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn parse_sexpr(gpa: std.mem.Allocator, tokens: []const lexer.Token) !struct { SExpr, usize } {
|
fn parse_expr(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;
|
||||||
}
|
}
|
||||||
@@ -139,8 +56,8 @@ fn parse_sexpr(gpa: std.mem.Allocator, tokens: []const lexer.Token) !struct { SE
|
|||||||
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 sexpr, const consumed = try parse_sexpr(gpa, tokens_rest);
|
const expr, const consumed = try parse_expr(gpa, tokens_rest);
|
||||||
try call.append(gpa, sexpr);
|
try call.append(gpa, expr);
|
||||||
tokens_rest = tokens_rest[consumed..];
|
tokens_rest = tokens_rest[consumed..];
|
||||||
}
|
}
|
||||||
if (tokens_rest.len == 0) {
|
if (tokens_rest.len == 0) {
|
||||||
@@ -153,8 +70,8 @@ fn parse_sexpr(gpa: std.mem.Allocator, tokens: []const lexer.Token) !struct { SE
|
|||||||
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 sexpr, const consumed = try parse_sexpr(gpa, tokens_rest);
|
const expr, const consumed = try parse_expr(gpa, tokens_rest);
|
||||||
try block.append(gpa, sexpr);
|
try block.append(gpa, expr);
|
||||||
tokens_rest = tokens_rest[consumed..];
|
tokens_rest = tokens_rest[consumed..];
|
||||||
}
|
}
|
||||||
if (tokens_rest.len == 0) {
|
if (tokens_rest.len == 0) {
|
||||||
@@ -167,8 +84,8 @@ fn parse_sexpr(gpa: std.mem.Allocator, tokens: []const lexer.Token) !struct { SE
|
|||||||
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 sexpr, const consumed = try parse_sexpr(gpa, tokens_rest);
|
const expr, const consumed = try parse_expr(gpa, tokens_rest);
|
||||||
try list.append(gpa, sexpr);
|
try list.append(gpa, expr);
|
||||||
tokens_rest = tokens_rest[consumed..];
|
tokens_rest = tokens_rest[consumed..];
|
||||||
}
|
}
|
||||||
if (tokens_rest.len == 0) {
|
if (tokens_rest.len == 0) {
|
||||||
@@ -188,42 +105,10 @@ pub fn parse_sexprs(gpa: std.mem.Allocator, tokens: []const lexer.Token) ![]cons
|
|||||||
var idx: usize = 0;
|
var idx: usize = 0;
|
||||||
|
|
||||||
while (idx < tokens.len) {
|
while (idx < tokens.len) {
|
||||||
const sexpr, const consumed = try parse_sexpr(gpa, tokens[idx..]);
|
const expr, const consumed = try parse_expr(gpa, tokens[idx..]);
|
||||||
try result.append(gpa, sexpr);
|
try result.append(gpa, expr);
|
||||||
idx += consumed;
|
idx += consumed;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.toOwnedSlice(gpa);
|
return result.toOwnedSlice(gpa);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Stmt = union(enum) {
|
|
||||||
func_decl: struct {
|
|
||||||
name: []const u8,
|
|
||||||
args: []const []const u8,
|
|
||||||
body: []const Stmt,
|
|
||||||
},
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Expr = union(enum) {
|
|
||||||
nil,
|
|
||||||
variable: []const u8,
|
|
||||||
integer: u64,
|
|
||||||
string: []const u8,
|
|
||||||
func_call: struct {
|
|
||||||
name: []const u8,
|
|
||||||
args: []const Expr,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -41,11 +41,10 @@ fn entry(init: struct {
|
|||||||
|
|
||||||
const tokens = try lib.lexer.lex(init.arena.allocator(), src);
|
const tokens = try lib.lexer.lex(init.arena.allocator(), src);
|
||||||
|
|
||||||
const sexprs = try lib.parser.parse_sexprs(init.arena.allocator(), tokens);
|
const exprs = try lib.parser.parse_sexprs(init.arena.allocator(), tokens);
|
||||||
|
|
||||||
for (sexprs) |sexpr| {
|
for (exprs) |expr| {
|
||||||
const stmt = try sexpr.parse_stmt(init.arena.allocator());
|
try init.stdout.print("{f}", .{expr});
|
||||||
try init.stdout.print("{any}\n", .{stmt});
|
|
||||||
try init.stdout.flush();
|
try init.stdout.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user