diff --git a/calculate_pi.script b/calculate_pi.script index d489ae8..d0f4c04 100644 --- a/calculate_pi.script +++ b/calculate_pi.script @@ -3,7 +3,7 @@ k := 0 iterations := 100 -while k - iterations { +while k < iterations { k_f := float(k) sum = sum + 1.0 / pow(16.0, k_f) * (4.0 / (8.0 * k_f + 1.0) - diff --git a/ir.c b/ir.c index 0f58aa7..4147220 100644 --- a/ir.c +++ b/ir.c @@ -10,6 +10,10 @@ const char *irinstr_str[IRInstrEnumSize] = { [IRSub] = "sub", [IRMul] = "mul", [IRDiv] = "div", + [IREq] = "eq", + [IRLt] = "lt", + [IRLe] = "le", + [IRNot] = "not", [IRJmp] = "jmp", [IRJnz] = "jnz", [IRCallInternal] = "calli", @@ -77,6 +81,9 @@ static void print_val(const Value *v) { case TypeInt: printf("%zd", v->Int); break; + case TypeBool: + printf("%s", v->Bool ? "true" : "false"); + break; default: printf("(unknown type)"); break; @@ -105,6 +112,9 @@ void print_ir(IRToks *v, const BuiltinFunc *builtin_funcs) { case IRSub: case IRDiv: case IRMul: + case IREq: + case IRLt: + case IRLe: printf(" %%%zx ", v->toks[i].Binary.addr); print_irparam(&v->toks[i].Binary.lhs); printf(" "); diff --git a/ir.h b/ir.h index fae539e..1ab87d2 100644 --- a/ir.h +++ b/ir.h @@ -18,6 +18,10 @@ enum IRInstr { IRSub, IRMul, IRDiv, + IREq, + IRLt, + IRLe, + IRNot, IRJmp, IRJnz, IRCallInternal, diff --git a/lex.c b/lex.c index deee6c0..68aa3a5 100644 --- a/lex.c +++ b/lex.c @@ -58,6 +58,10 @@ TokList lex(const char *s) { emit(&toks, &pos, (Tok){ .kind = TokElse }); else if (streq_0_n("while", start, i)) emit(&toks, &pos, (Tok){ .kind = TokWhile }); + else if (streq_0_n("true", start, i)) + emit(&toks, &pos, (Tok){ .kind = TokVal, .Val = { .type = { .kind = TypeBool, }, .Bool = true, }, }); + else if (streq_0_n("false", start, i)) + emit(&toks, &pos, (Tok){ .kind = TokVal, .Val = { .type = { .kind = TypeBool, }, .Bool = false, }, }); else { emit(&toks, &pos, (Tok){ .kind = TokIdent, @@ -162,15 +166,39 @@ TokList lex(const char *s) { break; case ':': consume(&pos, *(s++)); - if (s[0] == '=') { + if (s[0] == '=') emit(&toks, &pos, (Tok){ .kind = TokDeclare }); - } else { + else { set_err("Expected ':='"); return toks; } break; case '=': - emit(&toks, &pos, (Tok){ .kind = TokAssign }); + consume(&pos, *(s++)); + if (s[0] == '=') + emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpEq }); + else { + emit(&toks, &pos, (Tok){ .kind = TokAssign }); + continue; + } + break; + case '<': + consume(&pos, *(s++)); + if (s[0] == '=') + emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpLe }); + else { + emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpLt }); + continue; + } + break; + case '>': + consume(&pos, *(s++)); + if (s[0] == '=') + emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpGe }); + else { + emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = OpGt }); + continue; + } break; case '{': case '}': @@ -180,6 +208,7 @@ TokList lex(const char *s) { case '+': case '-': case '*': + case '!': emit(&toks, &pos, (Tok){ .kind = TokOp, .Op = s[0], diff --git a/main.c b/main.c index f202cca..9aa265a 100644 --- a/main.c +++ b/main.c @@ -35,9 +35,10 @@ static void die(const char *fmt, ...) { static Value fn_print(Value *args) { switch (args[0].type.kind) { - case TypeVoid: printf("(void)\n"); break; - case TypeFloat: printf("%f\n", args[0].Float); break; - case TypeInt: printf("%zd\n", args[0].Int); break; + case TypeVoid: printf("(void)\n"); break; + case TypeFloat: printf("%f\n", args[0].Float); break; + case TypeInt: printf("%zd\n", args[0].Int); break; + case TypeBool: printf("%s\n", args[0].Bool ? "true" : "false"); break; default: ASSERT_UNREACHED(); } diff --git a/parse.c b/parse.c index 0bb68d8..7f6b31d 100644 --- a/parse.c +++ b/parse.c @@ -46,12 +46,16 @@ static void set_irtok_dest_addr(IRTok *t, size_t addr) { switch (t->instr) { case IRSet: case IRNeg: + case IRNot: t->Unary.addr = addr; break; case IRAdd: case IRSub: case IRMul: case IRDiv: + case IREq: + case IRLt: + case IRLe: t->Binary.addr = addr; break; case IRCallInternal: @@ -172,11 +176,19 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, Scope sc = make_scope(parent_sc, false); for (;;) { - /* Prepare to collapse negative factor. */ - bool negate = false; - if (t->tok.kind == TokOp && t->tok.Op == OpSub) { - t = t->next; - negate = true; + /* Prepare to collapse unary operation. */ + bool perform_unary = false; + IRInstr unary_op; + if (t->tok.kind == TokOp) { + if (t->tok.Op == OpSub) { + t = t->next; + perform_unary = true; + unary_op = IRNeg; + } else if (t->tok.Op == OpNot) { + t = t->next; + perform_unary = true; + unary_op = IRNot; + } } /* Delete newline if we're definitely expecting an operand. */ @@ -312,28 +324,28 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, } } - /* Collapse negative factor. */ - if (negate) { - Tok *v = &t->tok; /* what we want to negate */ + /* Collapse unary operation. */ + if (perform_unary) { + Tok *v = &t->tok; /* what we want to perform the operation on */ t = t->prev; /* go back to the '-' sign */ toklist_del(toks, t->next, t->next); /* again, just removing the reference */ bool is_last_operation = t == start && t->next->tok.kind == TokOp && op_prec[t->next->tok.Op] == PREC_DELIM; if (v->kind == TokVal) { - /* immediately negate value */ + /* immediately perform operation */ t->tok.kind = TokVal; - t->tok.Val = eval_unary(IRNeg, &v->Val); + TRY_RET(t->tok.Val = eval_unary(unary_op, &v->Val), (ExprRet){0}); } else { size_t res_addr = is_last_operation ? 0 : sc.mem_addr++; - /* Instruction to negate. */ + /* unary IR instruction */ IRParam v_irparam; TRY_RET(v_irparam = tok_to_irparam(&sc, v), (ExprRet){0}); IRTok ir = { .ln = t->tok.ln, .col = t->tok.col, - .instr = IRNeg, + .instr = unary_op, .Unary = { .addr = res_addr, .val = v_irparam, @@ -421,12 +433,19 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, * because we're still using their values later on) */ toklist_del(toks, t->next, t->next->next); + bool swap_operands = false; + IRInstr instr; switch (l_op->Op) { case OpAdd: instr = IRAdd; break; case OpSub: instr = IRSub; break; case OpMul: instr = IRMul; break; case OpDiv: instr = IRDiv; break; + case OpEq: instr = IREq; break; + case OpLt: instr = IRLt; break; + case OpLe: instr = IRLe; break; + case OpGt: instr = IRLt; swap_operands = true; break; + case OpGe: instr = IRLe; swap_operands = true; break; default: mark_err(l_op); set_err("Unknown operation: '%s'", op_str[l_op->Op]); @@ -435,8 +454,10 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, if (lhs->kind == TokVal && rhs->kind == TokVal) { /* evaluate the constant expression immediately */ + Value *lhs_val = swap_operands ? &rhs->Val : &lhs->Val; + Value *rhs_val = swap_operands ? &lhs->Val : &rhs->Val; lhs->kind = TokVal; - TRY_RET(lhs->Val = eval_binary(instr, &lhs->Val, &rhs->Val), (ExprRet){0}); + TRY_RET(lhs->Val = eval_binary(instr, lhs_val, rhs_val), (ExprRet){0}); } else { bool is_last_operation = t == start && r_op_prec == PREC_DELIM; @@ -452,8 +473,8 @@ static ExprRet expr(IRToks *out_ir, TokList *toks, Map *funcs, Scope *parent_sc, .instr = instr, .Binary = { .addr = res_addr, - .lhs = lhs_irparam, - .rhs = rhs_irparam, + .lhs = swap_operands ? rhs_irparam : lhs_irparam, + .rhs = swap_operands ? lhs_irparam : rhs_irparam, }, }; diff --git a/runtime.c b/runtime.c index 2f50f61..0c18a45 100644 --- a/runtime.c +++ b/runtime.c @@ -39,6 +39,32 @@ Value eval_binary(IRInstr instr, const Value *lhs, const Value *rhs) { return (Value){0}; } } + case IREq: + case IRLt: + case IRLe: + bool res; + if (lhs->type.kind == TypeInt && rhs->type.kind == TypeInt) { + switch (instr) { + case IREq: res = lhs->Int == rhs->Int; break; + case IRLt: res = lhs->Int < rhs->Int; break; + case IRLe: res = lhs->Int <= rhs->Int; break; + default: ASSERT_UNREACHED(); + }; + } else if (lhs->type.kind == TypeFloat && rhs->type.kind == TypeFloat) { + switch (instr) { + case IREq: res = lhs->Float == rhs->Float; break; + case IRLt: res = lhs->Float < rhs->Float; break; + case IRLe: res = lhs->Float <= rhs->Float; break; + default: ASSERT_UNREACHED(); + }; + } else { + set_err("Unsupported types for operation '%s'", irinstr_str[instr]); + return (Value){0}; + } + return (Value){ + .type.kind = TypeBool, + .Bool = res, + }; default: ASSERT_UNREACHED(); } @@ -55,7 +81,14 @@ Value eval_unary(IRInstr instr, const Value *v) { else if (v->type.kind == TypeFloat) return (Value){ .type.kind = TypeFloat, .Float = -v->Float }; else { - set_err("Unsupported types for operation '%s'", irinstr_str[instr]); + set_err("Unsupported type for operation '%s'", irinstr_str[instr]); + return (Value){0}; + } + case IRNot: + if (v->type.kind == TypeBool) { + return (Value){ .type.kind = TypeBool, .Bool = !v->Bool }; + } else { + set_err("Unsupported type for operation '%s'", irinstr_str[instr]); return (Value){0}; } default: @@ -67,6 +100,7 @@ bool is_nonzero(const Value *v) { switch (v->type.kind) { case TypeInt: return v->Int != 0; case TypeFloat: return v->Float != 0.0; + case TypeBool: return v->Bool; default: ASSERT_UNREACHED(); } } @@ -75,8 +109,9 @@ Value zero_val(Type ty) { Value ret; ret.type = ty; switch (ty.kind) { - case TypeInt: ret.Int = 0; break; - case TypeFloat: ret.Float = 0.0; break; + case TypeInt: ret.Int = 0; break; + case TypeFloat: ret.Float = 0.0; break; + case TypeBool: ret.Bool = false; break; default: ASSERT_UNREACHED(); } return ret; diff --git a/tok.c b/tok.c index 6756fe4..4d324e2 100644 --- a/tok.c +++ b/tok.c @@ -11,10 +11,15 @@ int8_t op_prec[OperatorEnumSize] = { [OpLCurl] = PREC_DELIM, [OpRParen] = PREC_DELIM, [OpComma] = PREC_DELIM, - [OpAdd] = 0, - [OpSub] = 0, - [OpMul] = 1, - [OpDiv] = 1, + [OpEq] = 0, + [OpLt] = 0, + [OpGt] = 0, + [OpLe] = 0, + [OpGe] = 0, + [OpAdd] = 1, + [OpSub] = 1, + [OpMul] = 2, + [OpDiv] = 2, }; const char *op_str[OperatorEnumSize] = { @@ -27,8 +32,14 @@ const char *op_str[OperatorEnumSize] = { [OpSub] = "-", [OpMul] = "*", [OpDiv] = "/", + [OpNot] = "!", [OpNewLn] = "\\n", [OpEOF] = "EOF", + [OpEq] = "==", + [OpLt] = "<", + [OpGt] = ">", + [OpLe] = "<=", + [OpGe] = ">=", }; const char *tok_str[TokKindEnumSize] = { @@ -101,6 +112,9 @@ void print_toks(TokList *l) { case TypeInt: printf(": " C_ICYAN "%zd" C_RESET, i->tok.Val.Int); break; + case TypeBool: + printf(": " C_ICYAN "%s" C_RESET, i->tok.Val.Bool ? "true" : "false"); + break; default: printf(" " C_ICYAN "(unknown type)" C_RESET); break; diff --git a/tok.h b/tok.h index 54ec65d..3a8e1f3 100644 --- a/tok.h +++ b/tok.h @@ -11,6 +11,7 @@ typedef struct Type { TypeVoid = 0, TypeFloat, TypeInt, + TypeBool, } kind; /*union { @@ -23,6 +24,7 @@ typedef struct Value { union { double Float; ssize_t Int; + bool Bool; }; } Value; @@ -36,7 +38,13 @@ enum Operator { OpSub = '-', OpMul = '*', OpDiv = '/', + OpNot = '!', OpBeginNonchars = 256, + OpEq, + OpLt, + OpGt, + OpLe, + OpGe, OpNewLn, OpEOF, OperatorEnumSize, diff --git a/vm.c b/vm.c index 39eb2d6..d998585 100644 --- a/vm.c +++ b/vm.c @@ -55,6 +55,7 @@ void run(const IRToks *ir, const BuiltinFunc *builtin_funcs) { switch (instr->instr) { case IRSet: case IRNeg: + case IRNot: stack_fit(&s, instr->Unary.addr); TRY_ELSE(s.mem[instr->Unary.addr] = eval_unary(instr->instr, irparam_to_val(&s, &instr->Unary.val)), {free(fn_args); stack_term(&s);}); @@ -63,6 +64,9 @@ void run(const IRToks *ir, const BuiltinFunc *builtin_funcs) { case IRSub: case IRDiv: case IRMul: + case IREq: + case IRLt: + case IRLe: stack_fit(&s, instr->Binary.addr); TRY_ELSE(s.mem[instr->Binary.addr] = eval_binary(instr->instr, irparam_to_val(&s, &instr->Binary.lhs),