From 24d3db8687397f9a6d135386b73a3f2c1f3c9f8e Mon Sep 17 00:00:00 2001 From: Guanghui Hu <120L052208@stu.hit.edu.cn> Date: Thu, 21 Sep 2023 22:07:45 +0800 Subject: [PATCH] nixd/Sema: lowering some operators (binary + unary) Co-authored-by: Yingchi Long --- nixd/include/nixd/Sema/EvalContext.h | 2 + nixd/include/nixd/Sema/Lowering.h | 59 ++++++++++++ nixd/lib/Sema/Lowering.cpp | 106 +++++++++++++++++++++ nixd/tools/nixd-lint/test/lowering-ops.nix | 58 +++++++++++ 4 files changed, 225 insertions(+) create mode 100644 nixd/tools/nixd-lint/test/lowering-ops.nix diff --git a/nixd/include/nixd/Sema/EvalContext.h b/nixd/include/nixd/Sema/EvalContext.h index 146a26320..12260a6e5 100644 --- a/nixd/include/nixd/Sema/EvalContext.h +++ b/nixd/include/nixd/Sema/EvalContext.h @@ -7,8 +7,10 @@ namespace nixd { struct EvalContext { + using ES = std::vector>; GCPool Pool; GCPool FormalsPool; + GCPool ESPool; }; } // namespace nixd diff --git a/nixd/include/nixd/Sema/Lowering.h b/nixd/include/nixd/Sema/Lowering.h index f6d20ff7a..d792be59f 100644 --- a/nixd/include/nixd/Sema/Lowering.h +++ b/nixd/include/nixd/Sema/Lowering.h @@ -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 Args) { + nix::ExprVar *Fn = mkVar(FnName); + auto *Nix = new nix::ExprCall(P, Fn, std::move(Args)); + return Ctx.Pool.record(Nix); + } + + template + nix::Expr *lowerCallOp(std::string_view FnName, const syntax::Node *Op, + bool SwapOperands = false, bool Not = false) { + auto *SOP = dynamic_cast(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 + NixTy *lowerLegalOp(const syntax::Node *Op) { + auto *SOP = dynamic_cast(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 diff --git a/nixd/lib/Sema/Lowering.cpp b/nixd/lib/Sema/Lowering.cpp index 9bf6724a5..97fa2ab86 100644 --- a/nixd/lib/Sema/Lowering.cpp +++ b/nixd/lib/Sema/Lowering.cpp @@ -6,6 +6,7 @@ #include +#include #include #include @@ -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(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(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(Sub, Op); + case Node::NK_OpMul: + return lowerCallOp(Mul, Op); + case Node::NK_OpDiv: + return lowerCallOp(Div, Op); + + // comparison + case Node::NK_OpNegate: { + // -X + // -> + // (__sub 0 X) + const auto *OpNegate = dynamic_cast(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(LessThan, Op); + } + case Node::NK_OpLeq: { + // A <= B + // -> + // (! (__lessThan B A)) + return lowerCallOp(LessThan, Op, /*SwapOperands=*/true, + /*Not=*/true); + } + case Node::NK_OpGe: { + // A > B + // -> + // (__lessThan B A) + return lowerCallOp(LessThan, Op, /*SwapOperands=*/true); + } + case Node::NK_OpGeq: { + // A >= B + // -> + // ! (__lessThan A B) + return lowerCallOp(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(Op); + case Node::NK_OpNEq: + return lowerLegalOp(Op); + case Node::NK_OpAnd: + return lowerLegalOp(Op); + case Node::NK_OpOr: + return lowerLegalOp(Op); + case Node::NK_OpImpl: + return lowerLegalOp(Op); + case Node::NK_OpUpdate: + return lowerLegalOp(Op); + case Node::NK_OpConcatLists: + return lowerLegalOp(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; @@ -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; diff --git a/nixd/tools/nixd-lint/test/lowering-ops.nix b/nixd/tools/nixd-lint/test/lowering-ops.nix new file mode 100644 index 000000000..34f1f2935 --- /dev/null +++ b/nixd/tools/nixd-lint/test/lowering-ops.nix @@ -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; +}