expression parsing and basic tree-walk interpreter
This commit is contained in:
parent
f954a8276d
commit
5ed453a0fc
3 changed files with 585 additions and 76 deletions
181
vm/interp.jai
Normal file
181
vm/interp.jai
Normal file
|
|
@ -0,0 +1,181 @@
|
||||||
|
Interp :: struct {
|
||||||
|
allocator: Allocator;
|
||||||
|
|
||||||
|
symbols: kv.Kv(string, *Interp_Value);
|
||||||
|
toplevel: []*Node;
|
||||||
|
// stack: [..]*Interp_Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Interp_Value :: struct {
|
||||||
|
kind: Kind;
|
||||||
|
union {
|
||||||
|
b: bool;
|
||||||
|
i: s64;
|
||||||
|
u: u64;
|
||||||
|
f: float64;
|
||||||
|
s: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
Kind :: enum {
|
||||||
|
none;
|
||||||
|
nil;
|
||||||
|
bool;
|
||||||
|
int;
|
||||||
|
float;
|
||||||
|
string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init :: (i: *Interp, allocator: Allocator) {
|
||||||
|
// i.stack.allocator = allocator;
|
||||||
|
|
||||||
|
value_nil = make_interp_value(i, .nil);
|
||||||
|
|
||||||
|
value_true = make_interp_value(i, .bool);
|
||||||
|
value_true.b = true;
|
||||||
|
|
||||||
|
value_false = make_interp_value(i, .bool);
|
||||||
|
value_false.b = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
interp_program :: (i: *Interp) {
|
||||||
|
for i.toplevel if it.kind == {
|
||||||
|
case .variable;
|
||||||
|
var := it.(*Node_Var);
|
||||||
|
sym := var.symbol.(*Node_Symbol);
|
||||||
|
basic.assert(!kv.exists(*i.symbols, sym.str), "redeclaring symbol '%'", sym.str); // @errors
|
||||||
|
|
||||||
|
value := value_nil;
|
||||||
|
if var.value_expr != null {
|
||||||
|
value = interp_expr(i, var.value_expr);
|
||||||
|
basic.assert(value != null); // @errors
|
||||||
|
}
|
||||||
|
|
||||||
|
kv.set(*i.symbols, sym.str, value);
|
||||||
|
|
||||||
|
case .print;
|
||||||
|
print := it.(*Node_Print);
|
||||||
|
expr := interp_expr(i, print.expr);
|
||||||
|
basic.assert(expr != null); // @errors
|
||||||
|
|
||||||
|
if expr.kind == {
|
||||||
|
case .none; basic.print("()");
|
||||||
|
case .nil; basic.print("nil");
|
||||||
|
case .bool; basic.print("%", expr.b);
|
||||||
|
case .int; basic.print("%", expr.i);
|
||||||
|
case .float; basic.print("%", expr.f);
|
||||||
|
case .string; basic.print("%", expr.s);
|
||||||
|
}
|
||||||
|
|
||||||
|
basic.print("\n");
|
||||||
|
|
||||||
|
case;
|
||||||
|
basic.assert(false, "unhandled node kind: %", it.kind); // @errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interp_expr :: (i: *Interp, expr: *Node) -> *Interp_Value {
|
||||||
|
if expr.kind == {
|
||||||
|
case .unary;
|
||||||
|
do_unop :: (code: Code) #expand {
|
||||||
|
if rhs.kind == {
|
||||||
|
case .int;
|
||||||
|
right := rhs.i;
|
||||||
|
res.i = #insert,scope() code;
|
||||||
|
case .float;
|
||||||
|
right := rhs.f;
|
||||||
|
res.f = #insert,scope() code;
|
||||||
|
case;
|
||||||
|
basic.assert(false, "cannot use unary operator '%' on values of type '%'", un.op, rhs.kind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
un := expr.(*Node_Unary);
|
||||||
|
rhs := interp_expr(i, un.right);
|
||||||
|
res := make_interp_value(i, rhs.kind);
|
||||||
|
if un.op.kind == {
|
||||||
|
case .plus; do_unop(#code ifx right < 0 then -right else right);
|
||||||
|
case .minus; do_unop(#code -right);
|
||||||
|
case; basic.assert(false, "unhandled unary operator '%'", un.op.str); // @errors
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
|
||||||
|
case .binary;
|
||||||
|
bin := expr.(*Node_Binary);
|
||||||
|
lhs := interp_expr(i, bin.left);
|
||||||
|
rhs := interp_expr(i, bin.right);
|
||||||
|
basic.assert(lhs.kind == rhs.kind, "type mismatch % vs. %", lhs.kind, rhs.kind); // @errors
|
||||||
|
|
||||||
|
do_binop :: (code: Code) #expand {
|
||||||
|
if lhs.kind == {
|
||||||
|
case .int;
|
||||||
|
left := lhs.i;
|
||||||
|
right := rhs.i;
|
||||||
|
res.i = #insert,scope() code;
|
||||||
|
case .float;
|
||||||
|
left := lhs.f;
|
||||||
|
right := rhs.f;
|
||||||
|
res.f = #insert,scope() code;
|
||||||
|
case;
|
||||||
|
basic.assert(false, "cannot use binary operator '%' on values of type '%'", bin.op, lhs.kind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res := make_interp_value(i, lhs.kind);
|
||||||
|
if bin.op.kind == {
|
||||||
|
case .plus; do_binop(#code left + right);
|
||||||
|
case .minus; do_binop(#code left - right);
|
||||||
|
case .star; do_binop(#code left * right);
|
||||||
|
case .f_slash;
|
||||||
|
basic.assert(rhs.i != 0, "divide by zero"); // @errors
|
||||||
|
do_binop(#code left / right);
|
||||||
|
case .percent;
|
||||||
|
basic.assert(lhs.kind == .int, "cannot use binary operator '%%' on values of type '%'", lhs.kind);
|
||||||
|
res.i = lhs.i % rhs.i;
|
||||||
|
|
||||||
|
case; basic.assert(false, "unhandled binary operator '%'", bin.op.str);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
|
||||||
|
case .symbol;
|
||||||
|
sym := expr.(*Node_Symbol);
|
||||||
|
|
||||||
|
value, ok := kv.get(*i.symbols, sym.str);
|
||||||
|
basic.assert(ok, "use of undeclared symbol '%'", sym.str); // @errors
|
||||||
|
return value;
|
||||||
|
|
||||||
|
case .literal;
|
||||||
|
lit := expr.(*Node_Literal);
|
||||||
|
if lit.value_kind == {
|
||||||
|
case .int;
|
||||||
|
value := make_interp_value(i, .int);
|
||||||
|
value.i = lit.i;
|
||||||
|
return value;
|
||||||
|
|
||||||
|
case .float;
|
||||||
|
value := make_interp_value(i, .float);
|
||||||
|
value.f = lit.f;
|
||||||
|
return value;
|
||||||
|
|
||||||
|
case; basic.assert(false, "unhandled literal kind: %", lit.value_kind); // @errors
|
||||||
|
}
|
||||||
|
|
||||||
|
case; basic.assert(false, "unhandled node kind: %", expr.kind); // @errors
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#scope_file;
|
||||||
|
|
||||||
|
value_nil: *Interp_Value;
|
||||||
|
value_true: *Interp_Value;
|
||||||
|
value_false: *Interp_Value;
|
||||||
|
|
||||||
|
make_interp_value :: (i: *Interp, kind: Interp_Value.Kind) -> *Interp_Value {
|
||||||
|
value := mem.request_memory(Interp_Value,, allocator = i.allocator);
|
||||||
|
value.kind = kind;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,38 @@
|
||||||
#module_parameters(RUN_TESTS := false);
|
// #module_parameters(RUN_TESTS := false);
|
||||||
|
|
||||||
|
#load "parser.jai";
|
||||||
|
#load "interp.jai";
|
||||||
|
|
||||||
|
#scope_module;
|
||||||
|
|
||||||
|
mem :: #import "jc/memory";
|
||||||
|
array :: #import "jc/array";
|
||||||
|
kv :: #import "jc/kv";
|
||||||
|
|
||||||
|
basic :: #import "Basic"; // @future
|
||||||
|
strings :: #import "String"; // @future
|
||||||
|
|
||||||
#scope_file;
|
#scope_file;
|
||||||
|
|
||||||
#if RUN_TESTS {
|
#run {
|
||||||
test :: #import "jc/test";
|
parser: Parser;
|
||||||
|
init(*parser, context.allocator);
|
||||||
|
|
||||||
|
ok := parse_string(*parser, #string END
|
||||||
|
var x = 10.0
|
||||||
|
var y = 20.0
|
||||||
|
|
||||||
|
print x
|
||||||
|
print y
|
||||||
|
END);
|
||||||
|
|
||||||
|
interp: Interp;
|
||||||
|
interp.toplevel = parser.toplevel;
|
||||||
|
init(*interp, context.allocator);
|
||||||
|
|
||||||
|
interp_program(*interp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #if RUN_TESTS {
|
||||||
|
// test :: #import "jc/test";
|
||||||
|
// }
|
||||||
|
|
|
||||||
373
vm/parser.jai
373
vm/parser.jai
|
|
@ -15,11 +15,29 @@ Token :: struct {
|
||||||
kw_type;
|
kw_type;
|
||||||
kw_do;
|
kw_do;
|
||||||
kw_end;
|
kw_end;
|
||||||
|
kw_if;
|
||||||
|
kw_else;
|
||||||
|
kw_switch;
|
||||||
|
kw_case;
|
||||||
|
kw_for;
|
||||||
|
kw_in;
|
||||||
|
kw_loop;
|
||||||
|
kw_return;
|
||||||
|
kw_break;
|
||||||
|
kw_continue;
|
||||||
|
kw_goto;
|
||||||
|
kw_true;
|
||||||
|
kw_false;
|
||||||
|
|
||||||
|
kw_print;
|
||||||
|
|
||||||
equal :: #char "=";
|
equal :: #char "=";
|
||||||
plus :: #char "+";
|
plus :: #char "+";
|
||||||
minus :: #char "-";
|
minus :: #char "-";
|
||||||
star :: #char "*";
|
star :: #char "*";
|
||||||
|
percent :: #char "%";
|
||||||
|
bang :: #char "!";
|
||||||
|
and :: #char "&";
|
||||||
f_slash :: #char "/";
|
f_slash :: #char "/";
|
||||||
b_slash :: #char "\\";
|
b_slash :: #char "\\";
|
||||||
|
|
||||||
|
|
@ -38,28 +56,65 @@ Token :: struct {
|
||||||
|
|
||||||
Node :: struct {
|
Node :: struct {
|
||||||
kind: Kind;
|
kind: Kind;
|
||||||
|
type: *Type_Info;
|
||||||
|
|
||||||
Kind :: enum {
|
Kind :: enum {
|
||||||
invalid;
|
invalid;
|
||||||
|
|
||||||
|
stmt_start;
|
||||||
|
print;
|
||||||
|
stmt_end;
|
||||||
|
|
||||||
decl_start;
|
decl_start;
|
||||||
var;
|
variable;
|
||||||
decl_end;
|
decl_end;
|
||||||
|
|
||||||
expr_start;
|
expr_start;
|
||||||
|
type;
|
||||||
|
unary;
|
||||||
|
binary;
|
||||||
symbol;
|
symbol;
|
||||||
literal;
|
literal;
|
||||||
expr_end;
|
expr_end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node_Print :: struct {
|
||||||
|
#as using n: Node;
|
||||||
|
n.kind = .print;
|
||||||
|
|
||||||
|
expr: *Node;
|
||||||
|
}
|
||||||
|
|
||||||
Node_Var :: struct {
|
Node_Var :: struct {
|
||||||
#as using n: Node;
|
#as using n: Node;
|
||||||
n.kind = .var;
|
n.kind = .variable;
|
||||||
|
|
||||||
symbol: *Node;
|
symbol: *Node; // always *Node_Symbol
|
||||||
type_expr: *Node;
|
type_expr: *Node; // always *Node_Type
|
||||||
value_expr: *Node;
|
value_expr: *Node;
|
||||||
|
var_flags: Var_Flag;
|
||||||
|
|
||||||
|
Var_Flag :: enum_flags {
|
||||||
|
immutable; // def
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Node_Unary :: struct {
|
||||||
|
#as using n: Node;
|
||||||
|
n.kind = .unary;
|
||||||
|
|
||||||
|
op: Token;
|
||||||
|
right: *Node;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node_Binary :: struct {
|
||||||
|
#as using n: Node;
|
||||||
|
n.kind = .binary;
|
||||||
|
|
||||||
|
op: Token;
|
||||||
|
left: *Node;
|
||||||
|
right: *Node;
|
||||||
}
|
}
|
||||||
|
|
||||||
Node_Symbol :: struct {
|
Node_Symbol :: struct {
|
||||||
|
|
@ -73,7 +128,49 @@ Node_Literal :: struct {
|
||||||
#as using n: Node;
|
#as using n: Node;
|
||||||
n.kind = .literal;
|
n.kind = .literal;
|
||||||
|
|
||||||
|
value_kind: Value_Kind;
|
||||||
|
value_flags: Value_Flag;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
|
i: s64;
|
||||||
|
u: u64;
|
||||||
|
f: float64;
|
||||||
|
b: bool;
|
||||||
|
s: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value_Kind :: enum {
|
||||||
|
int;
|
||||||
|
float;
|
||||||
|
bool;
|
||||||
|
string;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value_Flag :: enum_flags {
|
||||||
|
can_be_unsigned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Node_Type :: struct {
|
||||||
|
#as using n: Node;
|
||||||
|
n.kind = .type;
|
||||||
|
|
||||||
|
resolved_type: *Type_Info;
|
||||||
|
type_kind: Type_Kind;
|
||||||
|
|
||||||
|
union {
|
||||||
|
alias_target: *Node;
|
||||||
|
pointer_target: *Node_Type;
|
||||||
|
struct {
|
||||||
|
array_element: *Node_Type;
|
||||||
|
array_count: *Node; // can be null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Type_Kind :: enum {
|
||||||
|
alias;
|
||||||
|
pointer;
|
||||||
|
array;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,10 +199,14 @@ parse_string :: (p: *Parser, source: string) -> bool {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
node := parse_toplevel_declaration(p);
|
node := parse_toplevel(p);
|
||||||
if node == null break;
|
if node == null break;
|
||||||
|
|
||||||
array.append(*p.toplevel, node);
|
array.append(*p.toplevel, node);
|
||||||
|
|
||||||
|
if node.kind == {
|
||||||
|
case .variable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -114,32 +215,38 @@ parse_string :: (p: *Parser, source: string) -> bool {
|
||||||
|
|
||||||
#scope_file;
|
#scope_file;
|
||||||
|
|
||||||
parse_toplevel_declaration :: (p: *Parser) -> *Node {
|
parse_toplevel :: (p: *Parser) -> *Node {
|
||||||
t, ok := expect_token(p, .kw_var);
|
t, ok := expect_token(p, .kw_var, .kw_def, .kw_print);
|
||||||
if ok == false return null;
|
basic.assert(ok, "var, def, print"); // @errors
|
||||||
|
|
||||||
|
if t.kind == {
|
||||||
|
// var sym type_expr
|
||||||
|
// var sym type_expr = expr
|
||||||
|
// var sym = expr
|
||||||
|
case .kw_var; #through;
|
||||||
|
case .kw_def;
|
||||||
s:, ok = expect_token(p, .symbol);
|
s:, ok = expect_token(p, .symbol);
|
||||||
if ok == false return null;
|
basic.assert(ok, "symbol"); // @errors
|
||||||
|
|
||||||
type_expr: *Node;
|
type_expr: *Node;
|
||||||
value_expr: *Node;
|
value_expr: *Node;
|
||||||
|
|
||||||
// var sym int
|
is_const := t.kind == .kw_def;
|
||||||
// var sym int = value
|
|
||||||
// var sym = value
|
|
||||||
|
|
||||||
t = peek_token(p);
|
t = peek_token(p);
|
||||||
if t.kind == .equal {
|
if t.kind == .equal {
|
||||||
consume_token(p);
|
consume_token(p);
|
||||||
value_expr = parse_expression(p);
|
value_expr = parse_expression(p);
|
||||||
|
basic.assert(value_expr != null, "value expr"); // @errors
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
type_expr = parse_type_expression(p);
|
type_expr = parse_type_expression(p);
|
||||||
if type_expr == null return null;
|
basic.assert(type_expr != null, "type expr"); // @errors
|
||||||
|
|
||||||
if peek_token(p).kind == .equal {
|
if peek_token(p).kind == .equal {
|
||||||
consume_token(p);
|
consume_token(p);
|
||||||
value_expr = parse_expression(p);
|
value_expr = parse_expression(p);
|
||||||
|
basic.assert(value_expr != null, "value expr"); // @errors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,24 +258,211 @@ parse_toplevel_declaration :: (p: *Parser) -> *Node {
|
||||||
node.type_expr = type_expr;
|
node.type_expr = type_expr;
|
||||||
node.value_expr = value_expr;
|
node.value_expr = value_expr;
|
||||||
|
|
||||||
return node;
|
if is_const {
|
||||||
|
node.var_flags |= .immutable;
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_type_expression :: (p: *Parser) -> *Node {
|
return node;
|
||||||
t, ok := expect_token(p, .symbol, .star);
|
|
||||||
if ok == false return null;
|
|
||||||
|
|
||||||
if t.kind == {
|
// print(expr)
|
||||||
case .symbol;
|
// print expr
|
||||||
node := make_node(p, Node_Symbol);
|
case .kw_print;
|
||||||
node.str = t.str;
|
expr := parse_expression(p);
|
||||||
|
basic.assert(expr != null, "expected expression"); // @errors
|
||||||
|
|
||||||
|
node := make_node(p, Node_Print);
|
||||||
|
node.expr = expr;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_expression :: (p: *Parser) -> *Node {
|
parse_type_expression :: (p: *Parser) -> *Node_Type {
|
||||||
|
t, ok := expect_token(p, .symbol, .star, .l_square);
|
||||||
|
basic.assert(ok, "type expression"); // @errors
|
||||||
|
|
||||||
|
if t.kind == {
|
||||||
|
case .star;
|
||||||
|
target := parse_type_expression(p);
|
||||||
|
basic.assert(target != null, "pointer target"); // @errors
|
||||||
|
|
||||||
|
node := make_node(p, Node_Type);
|
||||||
|
node.type_kind = .pointer;
|
||||||
|
node.pointer_target = target;
|
||||||
|
return node;
|
||||||
|
|
||||||
|
case .l_square;
|
||||||
|
node := make_node(p, Node_Type);
|
||||||
|
node.type_kind = .array;
|
||||||
|
|
||||||
|
// slice
|
||||||
|
if peek_token(p).kind == .r_square {
|
||||||
|
consume_token(p);
|
||||||
|
|
||||||
|
element := parse_type_expression(p);
|
||||||
|
basic.assert(element != null, "array element"); // @errors
|
||||||
|
|
||||||
|
node.array_element = element;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
count := parse_expression(p);
|
||||||
|
basic.assert(count != null, "array count"); // @errors
|
||||||
|
|
||||||
|
_, ok := expect_token(p, .r_square);
|
||||||
|
basic.assert(ok, "end of array type");
|
||||||
|
|
||||||
|
element := parse_type_expression(p);
|
||||||
|
basic.assert(element != null, "array element"); // @errors
|
||||||
|
|
||||||
|
node.array_count = count;
|
||||||
|
node.array_element = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
|
||||||
|
case .symbol;
|
||||||
|
symbol := make_node(p, Node_Symbol);
|
||||||
|
symbol.str = t.str;
|
||||||
|
|
||||||
|
node := make_node(p, Node_Type);
|
||||||
|
node.type_kind = .alias;
|
||||||
|
node.alias_target = symbol;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_expression :: (p: *Parser, min_precedence := 1) -> *Node {
|
||||||
|
get_precedence :: inline (t: Token) -> int {
|
||||||
|
if t.kind == {
|
||||||
|
case .star; #through;
|
||||||
|
case .f_slash; #through;
|
||||||
|
case .percent; #through;
|
||||||
|
case .and;
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case .plus; #through;
|
||||||
|
case .minus;
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
// case .equal_equal; #through;
|
||||||
|
// case .bang_equal; #through;
|
||||||
|
// case .less; #through;
|
||||||
|
// case .less_equal; #through;
|
||||||
|
// case .more; #through;
|
||||||
|
// case .more_equal;
|
||||||
|
// return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
node := parse_expression_unary(p);
|
||||||
|
basic.assert(node != null, "expected expression"); // @errors
|
||||||
|
|
||||||
|
while !at_end(p) {
|
||||||
|
op := peek_token(p);
|
||||||
|
prec := get_precedence(op);
|
||||||
|
if prec <= min_precedence break;
|
||||||
|
|
||||||
|
op = consume_token(p);
|
||||||
|
|
||||||
|
lhs := node;
|
||||||
|
rhs := parse_expression(p, prec);
|
||||||
|
basic.assert(rhs != null, "expected rhs"); // @errors
|
||||||
|
|
||||||
|
new := make_node(p, Node_Binary);
|
||||||
|
new.op = op;
|
||||||
|
new.left = lhs;
|
||||||
|
new.right = rhs;
|
||||||
|
|
||||||
|
node = new;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_expression_unary :: (p: *Parser) -> *Node {
|
||||||
|
op := peek_token(p);
|
||||||
|
if op.kind == {
|
||||||
|
case .plus; #through;
|
||||||
|
case .minus;
|
||||||
|
op = consume_token(p);
|
||||||
|
|
||||||
|
node := parse_expression_unary(p);
|
||||||
|
basic.assert(node != null, "expected expr"); // @errors
|
||||||
|
|
||||||
|
unary := make_node(p, Node_Unary);
|
||||||
|
unary.op = op;
|
||||||
|
unary.right = node;
|
||||||
|
return unary;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parse_expression_postfix(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_expression_postfix :: (p: *Parser) -> *Node {
|
||||||
|
// @TODO
|
||||||
|
base := parse_expression_base(p);
|
||||||
|
basic.assert(base != null, "expected expression"); // @errors
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_expression_base :: (p: *Parser) -> *Node {
|
||||||
|
t, ok := expect_token(p, .kw_true, .kw_false, .number, .symbol, .l_paren);
|
||||||
|
basic.assert(ok, "expected expression, found '%'", t.str); // @errors
|
||||||
|
|
||||||
|
if t.kind == {
|
||||||
|
case .kw_true; #through;
|
||||||
|
case .kw_false;
|
||||||
|
node := make_node(p, Node_Literal);
|
||||||
|
node.b = t.kind == .kw_true;
|
||||||
|
node.value_kind = .bool;
|
||||||
|
return node;
|
||||||
|
|
||||||
|
case .symbol;
|
||||||
|
node := make_node(p, Node_Symbol);
|
||||||
|
node.str = t.str;
|
||||||
|
return node;
|
||||||
|
|
||||||
|
case .number;
|
||||||
|
node := make_node(p, Node_Literal);
|
||||||
|
copy := t.str;
|
||||||
|
|
||||||
|
if strings.contains(t.str, ".") {
|
||||||
|
node.value_kind = .float;
|
||||||
|
|
||||||
|
value, ok := strings.parse_float(*copy);
|
||||||
|
basic.assert(ok, "malformed float '%'", t.str); // @errors
|
||||||
|
|
||||||
|
node.f = value;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
node.value_kind = .int;
|
||||||
|
if t.str[0] == "-" {
|
||||||
|
node.value_flags |= .can_be_unsigned;
|
||||||
|
}
|
||||||
|
|
||||||
|
value, ok := strings.parse_int(*copy);
|
||||||
|
basic.assert(ok, "malformed integer '%'", t.str); // @errors
|
||||||
|
|
||||||
|
node.i = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
|
||||||
|
case .l_paren;
|
||||||
|
node := parse_expression(p);
|
||||||
|
basic.assert(node != null, "expected expression"); // @errors
|
||||||
|
|
||||||
|
_, ok := expect_token(p, .r_paren);
|
||||||
|
basic.assert(ok, "expected ')'"); // @errors
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -229,9 +523,24 @@ consume_token :: (p: *Parser) -> Token {
|
||||||
if t.str == {
|
if t.str == {
|
||||||
case "var"; t.kind = .kw_var;
|
case "var"; t.kind = .kw_var;
|
||||||
case "def"; t.kind = .kw_def;
|
case "def"; t.kind = .kw_def;
|
||||||
|
case "type"; t.kind = .kw_type;
|
||||||
case "do"; t.kind = .kw_do;
|
case "do"; t.kind = .kw_do;
|
||||||
case "end"; t.kind = .kw_end;
|
case "end"; t.kind = .kw_end;
|
||||||
case "type"; t.kind = .kw_type;
|
case "if"; t.kind = .kw_if;
|
||||||
|
case "else"; t.kind = .kw_else;
|
||||||
|
case "switch"; t.kind = .kw_switch;
|
||||||
|
case "case"; t.kind = .kw_case;
|
||||||
|
case "for"; t.kind = .kw_for;
|
||||||
|
case "in"; t.kind = .kw_in;
|
||||||
|
case "loop"; t.kind = .kw_loop;
|
||||||
|
case "return"; t.kind = .kw_return;
|
||||||
|
case "break"; t.kind = .kw_break;
|
||||||
|
case "continue"; t.kind = .kw_continue;
|
||||||
|
case "goto"; t.kind = .kw_goto;
|
||||||
|
case "true"; t.kind = .kw_true;
|
||||||
|
case "false"; t.kind = .kw_false;
|
||||||
|
|
||||||
|
case "print"; t.kind = .kw_print;
|
||||||
case; t.kind = .symbol;
|
case; t.kind = .symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -256,6 +565,9 @@ consume_token :: (p: *Parser) -> Token {
|
||||||
case "*"; #through;
|
case "*"; #through;
|
||||||
case "/"; #through;
|
case "/"; #through;
|
||||||
case "="; #through;
|
case "="; #through;
|
||||||
|
case "%"; #through;
|
||||||
|
case "!"; #through;
|
||||||
|
case "&"; #through;
|
||||||
case "("; #through;
|
case "("; #through;
|
||||||
case ")"; #through;
|
case ")"; #through;
|
||||||
case "["; #through;
|
case "["; #through;
|
||||||
|
|
@ -280,18 +592,3 @@ expect_token :: (p: *Parser, kinds: ..Token.Kind) -> Token, bool {
|
||||||
return t, false;
|
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
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue