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
|
set(LISP_TESTS
|
||||||
numeric
|
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(const Object& rhs) const { return rhs.div_inv(*this); }
|
||||||
Result<Value> Int64::div_inv(const Int64& rhs) const {
|
Result<Value> Int64::div_inv(const Int64& rhs) const {
|
||||||
|
if (value() == 0) return ERROR(DivisionByZero);
|
||||||
|
|
||||||
if (rhs.value() % value() == 0) {
|
if (rhs.value() % value() == 0) {
|
||||||
return Value(TRY(Int64::create(rhs.value() / value())));
|
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 {
|
Result<Value> Int64::div_inv(const Float& rhs) const {
|
||||||
|
if (value() == 0) return ERROR(DivisionByZero);
|
||||||
return Value(TRY(Float::create(rhs.value() / float(value()))));
|
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(const Object& rhs) const { return rhs.div_inv(*this); }
|
||||||
Result<Value> Float::div_inv(const Float& rhs) const {
|
Result<Value> Float::div_inv(const Float& rhs) const {
|
||||||
|
if (value() == 0) return ERROR(DivisionByZero);
|
||||||
return Value(TRY(Float::create(rhs.value() / value())));
|
return Value(TRY(Float::create(rhs.value() / value())));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<Value> Float::div_inv(const Int64& rhs) const {
|
Result<Value> Float::div_inv(const Int64& rhs) const {
|
||||||
|
if (value() == 0) return ERROR(DivisionByZero);
|
||||||
return Value(TRY(Float::create(float(rhs.value()) / value())));
|
return Value(TRY(Float::create(float(rhs.value()) / value())));
|
||||||
}
|
}
|
||||||
Result<short> Int64::cmp(const Float& rhs) const {
|
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);
|
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,
|
Result<Expression> Compiler::compile_fn(Context& context, Symbol& op,
|
||||||
Pair& expr) {
|
Pair& expr) {
|
||||||
Context ctx = TRY(Context::create(context));
|
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);
|
return compile_comparison(context, sym, expr);
|
||||||
} else if (TRY(sym.cmp("if")) == 0) {
|
} else if (TRY(sym.cmp("if")) == 0) {
|
||||||
return compile_if(context, sym, expr);
|
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) {
|
} else if (TRY(sym.cmp("fn")) == 0) {
|
||||||
return compile_fn(context, sym, expr);
|
return compile_fn(context, sym, expr);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -38,6 +38,9 @@ class Compiler {
|
||||||
Result<Expression> compile_symbol(Context& context, Symbol& value);
|
Result<Expression> compile_symbol(Context& context, Symbol& value);
|
||||||
Result<Expression> compile_bool(Context& context, Bool& value);
|
Result<Expression> compile_bool(Context& context, Bool& value);
|
||||||
Result<Expression> compile_if(Context& context, Symbol& op, Pair& expr);
|
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_fn(Context& context, Symbol& op, Pair& expr);
|
||||||
Result<Expression> compile_body(Context& context, Pair& expr);
|
Result<Expression> compile_body(Context& context, Pair& expr);
|
||||||
Result<Expression> compile_function_call(Context& context, Pair& expr);
|
Result<Expression> compile_function_call(Context& context, Pair& expr);
|
||||||
|
|
|
@ -18,6 +18,7 @@ enum class ErrorCode {
|
||||||
IOError,
|
IOError,
|
||||||
Interrupt,
|
Interrupt,
|
||||||
AssertionFailed,
|
AssertionFailed,
|
||||||
|
DivisionByZero,
|
||||||
};
|
};
|
||||||
|
|
||||||
void seterr(const char* err);
|
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