Skip to content

Commit

Permalink
nixd/Sema: lowering some operators (binary + unary)
Browse files Browse the repository at this point in the history
Co-authored-by: Yingchi Long <[email protected]>
  • Loading branch information
euphgh and inclyc committed Sep 23, 2023
1 parent 2c449bd commit 24d3db8
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 0 deletions.
2 changes: 2 additions & 0 deletions nixd/include/nixd/Sema/EvalContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
namespace nixd {

struct EvalContext {
using ES = std::vector<std::pair<nix::PosIdx, nix::Expr *>>;
GCPool<nix::Expr> Pool;
GCPool<nix::Formals> FormalsPool;
GCPool<ES> ESPool;
};

} // namespace nixd
59 changes: 59 additions & 0 deletions nixd/include/nixd/Sema/Lowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,65 @@ struct Lowering {
nix::ExprLambda *lowerFunction(const syntax::Function *Fn);
nix::Formal lowerFormal(const syntax::Formal &Formal);
nix::AttrPath lowerAttrPath(const syntax::AttrPath &Path);
nix::Expr *lowerOp(const syntax::Node *Op);

private:
constexpr static std::string_view LessThan = "__lessThan";
constexpr static std::string_view Sub = "__sub";
constexpr static std::string_view Mul = "__mul";
constexpr static std::string_view Div = "__div";

nix::ExprVar *mkVar(std::string_view Sym) {
return mkVar(STable.create(Sym));
}

nix::ExprVar *mkVar(nix::Symbol Sym) {
auto *Var = new nix::ExprVar(Sym);
Ctx.Pool.record(Var);
return Var;
};

nix::ExprOpNot *mkNot(nix::Expr *Body) {
auto *OpNot = new nix::ExprOpNot(Body);
Ctx.Pool.record(OpNot);
return OpNot;
}

nix::ExprCall *mkCall(std::string_view FnName, nix::PosIdx P,
std::vector<nix::Expr *> Args) {
nix::ExprVar *Fn = mkVar(FnName);
auto *Nix = new nix::ExprCall(P, Fn, std::move(Args));
return Ctx.Pool.record(Nix);
}

template <class SyntaxTy>
nix::Expr *lowerCallOp(std::string_view FnName, const syntax::Node *Op,
bool SwapOperands = false, bool Not = false) {
auto *SOP = dynamic_cast<const SyntaxTy *>(Op);
assert(SOP && "types are not matching between op pointer & syntax type!");
nix::Expr *LHS = lower(SOP->LHS);
nix::Expr *RHS = lower(SOP->RHS);
if (SwapOperands)
std::swap(LHS, RHS);
nix::PosIdx P = SOP->OpRange.Begin;
nix::ExprCall *Call = mkCall(FnName, P, {LHS, RHS});
if (Not)
return mkNot(Call);
return Call;
}

/// The bin-op expression is actually legal in the nix evaluator
/// OpImpl -> nix::ExprOpImpl
template <class NixTy, class SyntaxTy>
NixTy *lowerLegalOp(const syntax::Node *Op) {
auto *SOP = dynamic_cast<const SyntaxTy *>(Op);
assert(SOP && "types are not matching between op pointer & syntax type!");
nix::Expr *LHS = lower(SOP->LHS);
nix::Expr *RHS = lower(SOP->RHS);
nix::PosIdx P = SOP->OpRange.Begin;
auto *Nix = new NixTy(P, LHS, RHS);
return Ctx.Pool.record(Nix);
}
};

} // namespace nixd
106 changes: 106 additions & 0 deletions nixd/lib/Sema/Lowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <nix/nixexpr.hh>

#include <llvm/Support/ErrorHandling.h>
#include <llvm/Support/FormatVariadic.h>

#include <memory>
Expand Down Expand Up @@ -381,6 +382,101 @@ void ExprAttrsBuilder::addInherited(const syntax::InheritedAttribute &IA) {
}
}

nix::Expr *Lowering::lowerOp(const syntax::Node *Op) {
if (!Op)
return nullptr;

switch (Op->getKind()) {
case Node::NK_OpNot: {
const auto *OpNot = dynamic_cast<const syntax::OpNot *>(Op);
return mkNot(lower(OpNot->Body));
}

// arithmetic
case Node::NK_OpAdd: {
// A + B
// ->
// ExprConcatStrings:
// - pos: "+"
// - forceString: false
// - es: {{A.pos, A}, {B.pos, B}}
const auto *OpAdd = dynamic_cast<const syntax::OpAdd *>(Op);
nix::Expr *A = lower(OpAdd->LHS);
nix::Expr *B = lower(OpAdd->RHS);
nix::PosIdx APos = OpAdd->LHS->Range.Begin;
nix::PosIdx BPos = OpAdd->RHS->Range.Begin;
nix::PosIdx Pos = OpAdd->OpRange.Begin;
auto *ES = Ctx.ESPool.record(new EvalContext::ES{{APos, A}, {BPos, B}});
auto ECS = new nix::ExprConcatStrings(Pos, /*forceString=*/false, ES);
return Ctx.Pool.record(ECS);
}
case Node::NK_OpSub:
return lowerCallOp<syntax::OpSub>(Sub, Op);
case Node::NK_OpMul:
return lowerCallOp<syntax::OpMul>(Mul, Op);
case Node::NK_OpDiv:
return lowerCallOp<syntax::OpDiv>(Div, Op);

// comparison
case Node::NK_OpNegate: {
// -X
// ->
// (__sub 0 X)
const auto *OpNegate = dynamic_cast<const syntax::OpNegate *>(Op);
nix::Expr *I0 = Ctx.Pool.record(new nix::ExprInt(0));
nix::Expr *X = lower(OpNegate->Body);
nix::PosIdx P = OpNegate->OpRange.Begin;
return mkCall(Sub, P, {I0, X});
}
case Node::NK_OpLe: {
// A < B
// ->
// (__lessThan A B)
return lowerCallOp<syntax::OpLe>(LessThan, Op);
}
case Node::NK_OpLeq: {
// A <= B
// ->
// (! (__lessThan B A))
return lowerCallOp<syntax::OpLeq>(LessThan, Op, /*SwapOperands=*/true,
/*Not=*/true);
}
case Node::NK_OpGe: {
// A > B
// ->
// (__lessThan B A)
return lowerCallOp<syntax::OpGe>(LessThan, Op, /*SwapOperands=*/true);
}
case Node::NK_OpGeq: {
// A >= B
// ->
// ! (__lessThan A B)
return lowerCallOp<syntax::OpGeq>(LessThan, Op, /*SwapOperands=*/false,
/*Not=*/true);
}

// legal nodes, the list here has the same order with official parser.
// src/libexpr/nixexpr.hh, `MakeBinOp`
case Node::NK_OpEq:
return lowerLegalOp<nix::ExprOpEq, syntax::OpEq>(Op);
case Node::NK_OpNEq:
return lowerLegalOp<nix::ExprOpNEq, syntax::OpNEq>(Op);
case Node::NK_OpAnd:
return lowerLegalOp<nix::ExprOpAnd, syntax::OpAnd>(Op);
case Node::NK_OpOr:
return lowerLegalOp<nix::ExprOpOr, syntax::OpOr>(Op);
case Node::NK_OpImpl:
return lowerLegalOp<nix::ExprOpImpl, syntax::OpImpl>(Op);
case Node::NK_OpUpdate:
return lowerLegalOp<nix::ExprOpUpdate, syntax::OpUpdate>(Op);
case Node::NK_OpConcatLists:
return lowerLegalOp<nix::ExprOpConcatLists, syntax::OpConcatLists>(Op);
default:
llvm_unreachable("do not know how to lower this op!");
}
return nullptr;
}

nix::Expr *Lowering::lower(const syntax::Node *Root) {
if (!Root)
return nullptr;
Expand Down Expand Up @@ -445,6 +541,16 @@ nix::Expr *Lowering::lower(const syntax::Node *Root) {
Ctx.Pool.record(new nix::ExprIf(If->Range.Begin, Cond, Then, Else));
return NixIf;
}

// operators
case Node::NK_OpNot:
case Node::NK_OpNegate:
#define BIN_OP(NAME, _) case Node::NK_##NAME:
#include "nixd/Syntax/BinaryOps.inc"
#undef BIN_OP
{
return lowerOp(Root);
}
}

return nullptr;
Expand Down
58 changes: 58 additions & 0 deletions nixd/tools/nixd-lint/test/lowering-ops.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# RUN: nixd-lint -dump-nix-ast %s | FileCheck %s
# RUN: nixd-lint -dump-nix-ast -show-position %s | FileCheck --check-prefix=POSITION %s

{
# POSITION: ExprCall: (__sub 0 1) 6:14
Position = -1;

# CHECK: ExprOpNot: (! 1)
Not = !1;

# CHECK: ExprCall: (__sub 0 1)
Negate = -1;

# CHECK: ExprOpEq: (1 == 1)
Eq = 1 == 1;

# CHECK: ExprOpNEq: (1 != 1)
NEq = 1 != 1;

# CHECK: ExprCall: (__lessThan 2 1)
Ge = 1 > 2;

# CHECK: ExprCall: (__lessThan 1 2)
Le = 1 < 2;

# CHECK: (! (__lessThan 2 1))
Leq = 1 <= 2;

# CHECK: ExprOpNot: (! (__lessThan 1 2))
Geq = 1 >= 2;

# CHECK: ExprOpAnd: (1 && 2)
And = 1 && 2;

# CHECK: ExprOpOr: (1 || 2)
Or = 1 || 2;

# CHECK: ExprOpImpl: (1 -> 2)
Impl = 1 -> 2;

# CHECK: ExprOpUpdate: (1 // 2)
Update = 1 // 2;

# CHECK: ExprConcatStrings: (1 + 2)
Add = 1 + 2;

# CHECK: ExprCall: (__sub 1 2)
Sub = 1 - 2;

# CHECK: ExprCall: (__mul 1 2)
Mul = 1 * 2;

# CHECK: ExprCall: (__div 1 2)
Div = 1 / 2;

# CHECK: ExprOpConcatLists: (1 ++ 2)
Concat = 1 ++ 2;
}

0 comments on commit 24d3db8

Please sign in to comment.