Add primitive logic operations: "and", "or", "not"

This commit is contained in:
Konstantin Nazarov 2024-08-27 22:31:13 +01:00
parent 66dbd53dda
commit b23eb26ffe
Signed by: knazarov
GPG key ID: 4CFE0A42FA409C22
6 changed files with 164 additions and 0 deletions

View file

@ -62,6 +62,7 @@ set(CPP_TESTS
set(LISP_TESTS set(LISP_TESTS
numeric numeric
logic
) )

View file

@ -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 {

View file

@ -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 {

View file

@ -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);

View file

@ -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
View 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)))