Add primitive logic operations: "and", "or", "not"
This commit is contained in:
parent
66dbd53dda
commit
b23eb26ffe
6 changed files with 164 additions and 0 deletions
|
@ -62,6 +62,7 @@ set(CPP_TESTS
|
|||
|
||||
set(LISP_TESTS
|
||||
numeric
|
||||
logic
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -512,6 +512,8 @@ Result<Value> Int64::sub_inv(const Float& rhs) const {
|
|||
|
||||
Result<Value> Int64::div(const Object& rhs) const { return rhs.div_inv(*this); }
|
||||
Result<Value> Int64::div_inv(const Int64& rhs) const {
|
||||
if (value() == 0) return ERROR(DivisionByZero);
|
||||
|
||||
if (rhs.value() % value() == 0) {
|
||||
return Value(TRY(Int64::create(rhs.value() / value())));
|
||||
}
|
||||
|
@ -520,6 +522,7 @@ Result<Value> Int64::div_inv(const Int64& rhs) const {
|
|||
}
|
||||
|
||||
Result<Value> Int64::div_inv(const Float& rhs) const {
|
||||
if (value() == 0) return ERROR(DivisionByZero);
|
||||
return Value(TRY(Float::create(rhs.value() / float(value()))));
|
||||
}
|
||||
|
||||
|
@ -550,10 +553,12 @@ Result<Value> Float::sub_inv(const Int64& rhs) const {
|
|||
|
||||
Result<Value> Float::div(const Object& rhs) const { return rhs.div_inv(*this); }
|
||||
Result<Value> Float::div_inv(const Float& rhs) const {
|
||||
if (value() == 0) return ERROR(DivisionByZero);
|
||||
return Value(TRY(Float::create(rhs.value() / value())));
|
||||
}
|
||||
|
||||
Result<Value> Float::div_inv(const Int64& rhs) const {
|
||||
if (value() == 0) return ERROR(DivisionByZero);
|
||||
return Value(TRY(Float::create(float(rhs.value()) / value())));
|
||||
}
|
||||
Result<short> Int64::cmp(const Float& rhs) const {
|
||||
|
|
131
src/compiler.cpp
131
src/compiler.cpp
|
@ -433,6 +433,131 @@ Result<Expression> Compiler::compile_if(Context& context, Symbol& op,
|
|||
return std::move(ex);
|
||||
}
|
||||
|
||||
Result<Expression> Compiler::compile_and(Context& context, Symbol& op,
|
||||
Pair& expr) {
|
||||
Value param = TRY(expr.rest());
|
||||
Expression ex = TRY(Expression::create());
|
||||
if (param.is<Nil>() || !param.is<Pair>()) {
|
||||
return ERROR(CompilationError);
|
||||
}
|
||||
|
||||
uint64_t prev_reg = 0;
|
||||
uint64_t result = context.alloc_reg();
|
||||
int64_t true_c = TRY(context.add_const(TRY(Bool::create(true))));
|
||||
int64_t false_c = TRY(context.add_const(TRY(Bool::create(false))));
|
||||
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)result}, {1, false_c}));
|
||||
|
||||
bool is_first = true;
|
||||
|
||||
while (!param.is<Nil>()) {
|
||||
if (!param.is<Pair>()) {
|
||||
return ERROR(CompilationError);
|
||||
}
|
||||
|
||||
Pair& param_pair = *param.to<Pair>();
|
||||
auto rest = TRY(param_pair.rest());
|
||||
Value param_val = TRY(param_pair.first());
|
||||
|
||||
auto param_ex = TRY(compile_expr(context, param_val));
|
||||
|
||||
if (!is_first) {
|
||||
TRY(ex.add_opcode(Oc::Equal, {0, (int64_t)prev_reg}, {1, (int64_t)true_c},
|
||||
{0, (int64_t)0}));
|
||||
TRY(ex.add_opcode(Oc::Jump, {0, (int64_t)param_ex.code.size() + 2}));
|
||||
}
|
||||
prev_reg = param_ex.reg;
|
||||
TRY(ex.add_code(param_ex.code));
|
||||
|
||||
param = std::move(rest);
|
||||
is_first = false;
|
||||
}
|
||||
TRY(ex.add_opcode(Oc::Equal, {0, (int64_t)prev_reg}, {1, (int64_t)true_c},
|
||||
{0, (int64_t)0}));
|
||||
TRY(ex.add_opcode(Oc::Jump, {0, (int64_t)2}));
|
||||
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)result}, {1, true_c}));
|
||||
|
||||
context.maxreg = result + 1;
|
||||
ex.reg = result;
|
||||
return std::move(ex);
|
||||
}
|
||||
|
||||
Result<Expression> Compiler::compile_or(Context& context, Symbol& op,
|
||||
Pair& expr) {
|
||||
Value param = TRY(expr.rest());
|
||||
Expression ex = TRY(Expression::create());
|
||||
if (param.is<Nil>() || !param.is<Pair>()) {
|
||||
return ERROR(CompilationError);
|
||||
}
|
||||
|
||||
uint64_t prev_reg = 0;
|
||||
uint64_t result = context.alloc_reg();
|
||||
int64_t true_c = TRY(context.add_const(TRY(Bool::create(true))));
|
||||
int64_t false_c = TRY(context.add_const(TRY(Bool::create(false))));
|
||||
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)result}, {1, true_c}));
|
||||
|
||||
bool is_first = true;
|
||||
|
||||
while (!param.is<Nil>()) {
|
||||
if (!param.is<Pair>()) {
|
||||
return ERROR(CompilationError);
|
||||
}
|
||||
|
||||
Pair& param_pair = *param.to<Pair>();
|
||||
auto rest = TRY(param_pair.rest());
|
||||
Value param_val = TRY(param_pair.first());
|
||||
|
||||
auto param_ex = TRY(compile_expr(context, param_val));
|
||||
|
||||
if (!is_first) {
|
||||
TRY(ex.add_opcode(Oc::Equal, {0, (int64_t)prev_reg}, {1, (int64_t)true_c},
|
||||
{0, (int64_t)1}));
|
||||
TRY(ex.add_opcode(Oc::Jump, {0, (int64_t)param_ex.code.size() + 2}));
|
||||
}
|
||||
prev_reg = param_ex.reg;
|
||||
TRY(ex.add_code(param_ex.code));
|
||||
|
||||
param = std::move(rest);
|
||||
is_first = false;
|
||||
}
|
||||
TRY(ex.add_opcode(Oc::Equal, {0, (int64_t)prev_reg}, {1, (int64_t)true_c},
|
||||
{0, (int64_t)1}));
|
||||
TRY(ex.add_opcode(Oc::Jump, {0, (int64_t)2}));
|
||||
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)result}, {1, false_c}));
|
||||
|
||||
context.maxreg = result + 1;
|
||||
ex.reg = result;
|
||||
return std::move(ex);
|
||||
}
|
||||
|
||||
Result<Expression> Compiler::compile_not(Context& context, Symbol& op,
|
||||
Pair& expr) {
|
||||
Value first = TRY(expr.rest());
|
||||
Expression ex = TRY(Expression::create());
|
||||
if (first.is<Nil>() || !first.is<Pair>()) {
|
||||
return ERROR(CompilationError);
|
||||
}
|
||||
|
||||
Pair& first_pair = *first.to<Pair>();
|
||||
|
||||
auto first_expr = TRY(first_pair.first());
|
||||
|
||||
uint64_t result = context.alloc_reg();
|
||||
int64_t true_c = TRY(context.add_const(TRY(Bool::create(true))));
|
||||
int64_t false_c = TRY(context.add_const(TRY(Bool::create(false))));
|
||||
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)result}, {1, true_c}));
|
||||
|
||||
auto comp = TRY(compile_expr(context, first_expr));
|
||||
TRY(ex.add_code(comp.code));
|
||||
|
||||
TRY(ex.add_opcode(Oc::Equal, {0, (int64_t)comp.reg}, {1, (int64_t)true_c},
|
||||
{0, (int64_t)1}));
|
||||
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)result}, {1, false_c}));
|
||||
|
||||
context.maxreg = result + 1;
|
||||
ex.reg = result;
|
||||
return std::move(ex);
|
||||
}
|
||||
|
||||
Result<Expression> Compiler::compile_fn(Context& context, Symbol& op,
|
||||
Pair& expr) {
|
||||
Context ctx = TRY(Context::create(context));
|
||||
|
@ -618,6 +743,12 @@ Result<Expression> Compiler::compile_list(Context& context, Pair& expr) {
|
|||
return compile_comparison(context, sym, expr);
|
||||
} else if (TRY(sym.cmp("if")) == 0) {
|
||||
return compile_if(context, sym, expr);
|
||||
} else if (TRY(sym.cmp("and")) == 0) {
|
||||
return compile_and(context, sym, expr);
|
||||
} else if (TRY(sym.cmp("or")) == 0) {
|
||||
return compile_or(context, sym, expr);
|
||||
} else if (TRY(sym.cmp("not")) == 0) {
|
||||
return compile_not(context, sym, expr);
|
||||
} else if (TRY(sym.cmp("fn")) == 0) {
|
||||
return compile_fn(context, sym, expr);
|
||||
} else {
|
||||
|
|
|
@ -38,6 +38,9 @@ class Compiler {
|
|||
Result<Expression> compile_symbol(Context& context, Symbol& value);
|
||||
Result<Expression> compile_bool(Context& context, Bool& value);
|
||||
Result<Expression> compile_if(Context& context, Symbol& op, Pair& expr);
|
||||
Result<Expression> compile_and(Context& context, Symbol& op, Pair& expr);
|
||||
Result<Expression> compile_or(Context& context, Symbol& op, Pair& expr);
|
||||
Result<Expression> compile_not(Context& context, Symbol& op, Pair& expr);
|
||||
Result<Expression> compile_fn(Context& context, Symbol& op, Pair& expr);
|
||||
Result<Expression> compile_body(Context& context, Pair& expr);
|
||||
Result<Expression> compile_function_call(Context& context, Pair& expr);
|
||||
|
|
|
@ -18,6 +18,7 @@ enum class ErrorCode {
|
|||
IOError,
|
||||
Interrupt,
|
||||
AssertionFailed,
|
||||
DivisionByZero,
|
||||
};
|
||||
|
||||
void seterr(const char* err);
|
||||
|
|
23
test/logic.vli
Normal file
23
test/logic.vli
Normal file
|
@ -0,0 +1,23 @@
|
|||
;; -*- mode: lisp; -*-
|
||||
|
||||
(assert (= (not true) false))
|
||||
(assert (= (not false) true))
|
||||
|
||||
(assert (and true))
|
||||
(assert (= (and false) false))
|
||||
(assert (and true true))
|
||||
(assert (= (and true false) false))
|
||||
(assert (= (and false true) false))
|
||||
|
||||
(assert (and true))
|
||||
(assert (= (or false) false))
|
||||
(assert (or true true))
|
||||
(assert (or true false))
|
||||
(assert (or false true))
|
||||
(assert (= (or false false) false))
|
||||
|
||||
;; "and" is short-circuiting and should stop evaluation on first "false"
|
||||
(assert (= (and false (/ 1 0)) false))
|
||||
|
||||
;; "and" is short-circuiting and should stop evaluation on first "true"
|
||||
(assert (or true (/ 1 0)))
|
Loading…
Reference in a new issue