jc/vm/parser.jai
2025-06-01 22:19:31 -06:00

297 lines
5.8 KiB
Text

Token :: struct {
kind: Kind;
str: string;
Kind :: enum {
invalid;
end_of_file;
symbol;
number;
string;
kw_var;
kw_def;
kw_type;
kw_do;
kw_end;
equal :: #char "=";
plus :: #char "+";
minus :: #char "-";
star :: #char "*";
f_slash :: #char "/";
b_slash :: #char "\\";
l_paren :: #char "(";
r_paren :: #char ")";
l_square :: #char "[";
r_square :: #char "]";
l_brace :: #char "{";
r_brace :: #char "}";
comma :: #char ",";
dot :: #char ".";
colon :: #char ":";
semicolon :: #char ";";
}
}
Node :: struct {
kind: Kind;
Kind :: enum {
invalid;
decl_start;
var;
decl_end;
expr_start;
symbol;
literal;
expr_end;
}
}
Node_Var :: struct {
#as using n: Node;
n.kind = .var;
symbol: *Node;
type_expr: *Node;
value_expr: *Node;
}
Node_Symbol :: struct {
#as using n: Node;
n.kind = .symbol;
str: string;
}
Node_Literal :: struct {
#as using n: Node;
n.kind = .literal;
union {
}
}
Parser :: struct {
allocator: Allocator;
toplevel: [..]*Node;
previous: Token;
filename: string;
source: string;
offset: int;
}
init :: (p: *Parser, allocator: Allocator) {
p.allocator = allocator;
p.toplevel.allocator = allocator;
}
parse_string :: (p: *Parser, source: string) -> bool {
p.source = source;
p.offset = 0;
while !at_end(p) {
t := peek_token(p);
if t.kind == .invalid || t.kind == .end_of_file {
break;
}
node := parse_toplevel_declaration(p);
if node == null break;
array.append(*p.toplevel, node);
}
return false;
}
#scope_file;
parse_toplevel_declaration :: (p: *Parser) -> *Node {
t, ok := expect_token(p, .kw_var);
if ok == false return null;
s:, ok = expect_token(p, .symbol);
if ok == false return null;
type_expr: *Node;
value_expr: *Node;
// var sym int
// var sym int = value
// var sym = value
t = peek_token(p);
if t.kind == .equal {
consume_token(p);
value_expr = parse_expression(p);
}
else {
type_expr = parse_type_expression(p);
if type_expr == null return null;
if peek_token(p).kind == .equal {
consume_token(p);
value_expr = parse_expression(p);
}
}
symbol := make_node(p, Node_Symbol);
symbol.str = s.str;
node := make_node(p, Node_Var);
node.symbol = symbol;
node.type_expr = type_expr;
node.value_expr = value_expr;
return node;
}
parse_type_expression :: (p: *Parser) -> *Node {
t, ok := expect_token(p, .symbol, .star);
if ok == false return null;
if t.kind == {
case .symbol;
node := make_node(p, Node_Symbol);
node.str = t.str;
return node;
}
return null;
}
parse_expression :: (p: *Parser) -> *Node {
return null;
}
make_node :: (p: *Parser, $T: Type) -> *T {
return mem.request_memory(T,, allocator = p.allocator);
}
peek_token :: (p: *Parser) -> Token {
copy := p.*;
return consume_token(*copy);
}
at_end :: (p: *Parser) -> bool {
return p.offset >= p.source.count;
}
starts_symbol :: (c: u8) -> bool {
return (c >= "a" && c <= "z") ||
(c >= "A" && c <= "Z") ||
(c == "_");
}
continues_symbol :: (c: u8) -> bool {
return starts_symbol(c) || (c >= "0" && c <= "9");
}
starts_number :: (c: u8) -> bool {
return (c >= "0" && c <= "9");
}
continues_number :: (c: u8) -> bool {
return starts_number(c) || c == ".";
}
consume_token :: (p: *Parser) -> Token {
if at_end(p) return .{ kind = .end_of_file };
c := p.source[p.offset];
while !at_end(p) {
c = p.source[p.offset];
if c == {
case " "; #through;
case "\n"; #through;
case "\t";
p.offset += 1;
case;
break;
}
}
if starts_symbol(c) {
t := Token.{ str = .{ data = p.source.data + p.offset } };
while !at_end(p) {
c = p.source[p.offset];
if !continues_symbol(c) break;
p.offset += 1;
}
t.str.count = (p.source.data + p.offset) - t.str.data;
if t.str == {
case "var"; t.kind = .kw_var;
case "def"; t.kind = .kw_def;
case "do"; t.kind = .kw_do;
case "end"; t.kind = .kw_end;
case "type"; t.kind = .kw_type;
case; t.kind = .symbol;
}
return t;
}
if starts_number(c) {
t := Token.{ kind = .number, str = .{ data = p.source.data + p.offset } };
while !at_end(p) {
c = p.source[p.offset];
if !continues_number(c) break;
p.offset += 1;
}
t.str.count = (p.source.data + p.offset) - t.str.data;
return t;
}
if c == {
case "+"; #through;
case "-"; #through;
case "*"; #through;
case "/"; #through;
case "="; #through;
case "("; #through;
case ")"; #through;
case "["; #through;
case "]"; #through;
case "{"; #through;
case "}";
s := string.{ data = p.source.data + p.offset, count = 1 };
p.offset += 1;
return .{ kind = xx c, str = s };
}
s := string.{ data = p.source.data + p.offset, count = 1 };
return .{ kind = .invalid, str = s };
}
expect_token :: (p: *Parser, kinds: ..Token.Kind) -> Token, bool {
t := consume_token(p);
for kinds if it == t.kind {
return t, true;
}
return t, false;
}
#run {
parser: Parser;
init(*parser, context.allocator);
ok := parse_string(*parser, #string END
var x = 10
var y = 20
END);
}
mem :: #import "jc/memory";
array :: #import "jc/array";
basic :: #import "Basic"; // @future
strings :: #import "String"; // @future