Skip to content

Commit

Permalink
nixd/AST: use std::optional instead of exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
inclyc committed Aug 11, 2023
1 parent b8d6205 commit 78d9de4
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 115 deletions.
11 changes: 7 additions & 4 deletions nixd/include/nixd/AST/EvalAST.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,20 @@ class EvalAST : public ParseAST {
/// Inject myself into nix cache.
void injectAST(nix::EvalState &State, lspserver::PathRef Path) const;

std::optional<nix::Value> searchUpValue(const nix::Expr *Expr) const;

/// Try to search (traverse) up the expr and find the first `Env` associated
/// ancestor, return its env
nix::Env *searchUpEnv(const nix::Expr *Expr) const;

/// Similar to `searchUpEnv`, but search for Values
nix::Value searchUpValue(const nix::Expr *Expr) const;
std::optional<nix::Value>
getValueStatic(const nix::Expr *Expr) const noexcept;

/// Get the evaluation result (fixed point) of the expression.
nix::Value getValue(const nix::Expr *Expr) const;
std::optional<nix::Value> getValue(const nix::Expr *Expr) const noexcept;

nix::Value getValueEval(const nix::Expr *Expr, nix::EvalState &State) const;
std::optional<nix::Value> getValueEval(const nix::Expr *Expr,
nix::EvalState &State) const noexcept;

/// Get the corresponding 'Env' while evaluating the expression.
/// nix 'Env's contains dynamic variable name bindings at evaluation, might
Expand Down
14 changes: 10 additions & 4 deletions nixd/include/nixd/AST/ParseAST.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ class ParseAST {
return nPair(getDisplOf(E, Displ));
}

std::optional<lspserver::Range> lRange(const void *Ptr) const {
std::optional<lspserver::Range> lRange(const void *Ptr) const noexcept {
try {
return toLSPRange(nRange(Ptr));
} catch (...) {
Expand All @@ -147,20 +147,26 @@ class ParseAST {
/// Lookup an AST node that ends before or on the cursor.
/// { } |
/// ^
[[nodiscard]] const nix::Expr *lookupEnd(lspserver::Position Desired) const;
/// @returns nullptr, if not found
[[nodiscard]] const nix::Expr *
lookupEnd(lspserver::Position Desired) const noexcept;

/// Lookup AST nodes that contains the cursor
/// { | }
/// ^~~~~~~~^
///
/// @returns empty vector, if there is no such expression
[[nodiscard]] std::vector<const nix::Expr *>
lookupContain(lspserver::Position Desired) const;
lookupContain(lspserver::Position Desired) const noexcept;

[[nodiscard]] const nix::Expr *
lookupContainMin(lspserver::Position Desired) const;
lookupContainMin(lspserver::Position Desired) const noexcept;

/// Lookup an AST node that starts after or on the cursor
/// | { }
/// ^
///
/// @returns nullptr, if not found
[[nodiscard]] const nix::Expr *lookupStart(lspserver::Position Desired) const;

void collectSymbols(const nix::Expr *E, std::vector<nix::Symbol> &R) const {
Expand Down
71 changes: 46 additions & 25 deletions nixd/lib/AST/EvalAST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,13 @@ void EvalAST::rewriteAST() {
}
}

nix::Value EvalAST::getValue(const nix::Expr *Expr) const {
if (const auto *EV = dynamic_cast<const nix::ExprVar *>(Expr)) {
if (!EV->fromWith) {
const auto *EnvExpr = searchEnvExpr(EV, ParentMap);
if (const auto *EL = dynamic_cast<const nix::ExprLambda *>(EnvExpr)) {
const auto *F = EnvMap.at(EL->body);
return *F->values[EV->displ];
}
}
}
return ValueMap.at(Expr);
std::optional<nix::Value>
EvalAST::getValue(const nix::Expr *Expr) const noexcept {
if (std::optional<nix::Value> V = getValueStatic(Expr))
return V;
if (ValueMap.contains(Expr))
return ValueMap.at(Expr);
return std::nullopt;
}

void EvalAST::injectAST(nix::EvalState &State, lspserver::PathRef Path) const {
Expand All @@ -45,7 +41,7 @@ void EvalAST::injectAST(nix::EvalState &State, lspserver::PathRef Path) const {
DummyValue);
}

nix::Value EvalAST::searchUpValue(const nix::Expr *Expr) const {
std::optional<nix::Value> EvalAST::searchUpValue(const nix::Expr *Expr) const {
for (;;) {
if (ValueMap.contains(Expr))
return ValueMap.at(Expr);
Expand All @@ -54,7 +50,7 @@ nix::Value EvalAST::searchUpValue(const nix::Expr *Expr) const {

Expr = parent(Expr);
}
throw std::out_of_range("No such value associated to ancestors");
return std::nullopt;
}

nix::Env *EvalAST::searchUpEnv(const nix::Expr *Expr) const {
Expand All @@ -66,20 +62,45 @@ nix::Env *EvalAST::searchUpEnv(const nix::Expr *Expr) const {

Expr = parent(Expr);
}
throw std::out_of_range("No such env associated to ancestors");
return nullptr;
}

nix::Value EvalAST::getValueEval(const nix::Expr *Expr,
nix::EvalState &State) const {
try {
return getValue(Expr);
} catch (std::out_of_range &) {
// It is not evaluated.
// Let's find evaluated parent, and try to eval it then.
auto V = searchUpValue(Expr);
forceValueDepth(State, V, 2);
return getValue(Expr);
}
std::optional<nix::Value>
EvalAST::getValueEval(const nix::Expr *Expr,
nix::EvalState &State) const noexcept {
std::optional<nix::Value> V0 = getValue(Expr);
if (V0)
return V0;

// It is not evaluated.
// Let's find evaluated parent, and try to eval it then.
auto V1 = searchUpValue(Expr);
if (!V1)
return std::nullopt;

forceValueDepth(State, *V1, 2);
return getValue(Expr);
}

std::optional<nix::Value>
EvalAST::getValueStatic(const nix::Expr *Expr) const noexcept {
const auto *EVar = dynamic_cast<const nix::ExprVar *>(Expr);
if (!EVar)
return std::nullopt;

if (EVar->fromWith)
return std::nullopt;

const auto *EnvExpr = searchEnvExpr(EVar, ParentMap);

// TODO: split this method, and make rec { }, let-binding available
const auto *ELambda = dynamic_cast<const nix::ExprLambda *>(EnvExpr);
if (!ELambda)
return std::nullopt;

if (!EnvMap.contains(ELambda->body))
return std::nullopt;
const auto *F = EnvMap.at(ELambda->body);
return *F->values[EVar->displ];
}
} // namespace nixd
10 changes: 5 additions & 5 deletions nixd/lib/AST/ParseAST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ ParseAST::lookupDef(lspserver::Position Desired) const {
}

[[nodiscard]] const nix::Expr *
ParseAST::lookupEnd(lspserver::Position Desired) const {
ParseAST::lookupEnd(lspserver::Position Desired) const noexcept {
struct VTy : RecursiveASTVisitor<VTy> {
const ParseAST &This;

const nix::Expr *R;
const nix::Expr *R = nullptr;
lspserver::Position RStart = {INT_MAX, INT_MAX};
lspserver::Position REnd = {INT_MIN, INT_MIN};

Expand All @@ -52,7 +52,7 @@ ParseAST::lookupEnd(lspserver::Position Desired) const {
}

std::vector<const nix::Expr *>
ParseAST::lookupContain(lspserver::Position Desired) const {
ParseAST::lookupContain(lspserver::Position Desired) const noexcept {
struct VTy : RecursiveASTVisitor<VTy> {
const ParseAST &This;

Expand All @@ -73,7 +73,7 @@ ParseAST::lookupContain(lspserver::Position Desired) const {
}

[[nodiscard]] const nix::Expr *
ParseAST::lookupContainMin(lspserver::Position Desired) const {
ParseAST::lookupContainMin(lspserver::Position Desired) const noexcept {
struct VTy : RecursiveASTVisitor<VTy> {
const ParseAST &This;

Expand Down Expand Up @@ -103,7 +103,7 @@ ParseAST::lookupStart(lspserver::Position Desired) const {
struct VTy : RecursiveASTVisitor<VTy> {
const ParseAST &This;

const nix::Expr *R;
const nix::Expr *R = nullptr;
lspserver::Position RStart = {INT_MAX, INT_MAX};
lspserver::Position REnd = {INT_MIN, INT_MIN};

Expand Down
89 changes: 47 additions & 42 deletions nixd/lib/Sema/CompletionBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,27 @@ using lspserver::CompletionItemKind;
void CompletionBuilder::addAttrFields(const EvalAST &AST,
const lspserver::Position &Pos,
nix::EvalState &State) {
try {
if (const auto *Node = AST.lookupEnd(Pos)) {
auto Value = AST.getValueEval(Node, State);
if (Value.type() == nix::ValueType::nAttrs) {
// Traverse attribute bindings
for (auto Binding : *Value.attrs) {
CompletionItem R;
R.label = State.symbols[Binding.name];
R.kind = CompletionItemKind::Field;
Result.items.emplace_back(std::move(R));
if (Result.items.size() > Limit) {
Result.isIncomplete = true;
break;
}
}
}
const nix::Expr *Node = AST.lookupEnd(Pos);

if (!Node)
return;

auto OpV = AST.getValueEval(Node, State);

if (!OpV || OpV->type() != nix::ValueType::nAttrs)
return;

for (nix::Attr Attr : *OpV->attrs) {

CompletionItem R;
R.label = State.symbols[Attr.name];
R.kind = CompletionItemKind::Field;
Result.items.emplace_back(std::move(R));

if (Result.items.size() > Limit) {
Result.isIncomplete = true;
break;
}
} catch (std::out_of_range &) {
}
}

Expand All @@ -56,24 +59,29 @@ void CompletionBuilder::addLambdaFormals(const EvalAST &AST,
if (!dynamic_cast<const nix::ExprAttrs *>(Node))
return;

try {
const auto *Parent = AST.parent(Node);
if (const auto *SomeExprCall =
dynamic_cast<const nix::ExprCall *>(Parent)) {
auto Value = AST.getValueEval(SomeExprCall->fun, State);
if (!Value.isLambda())
return;
auto *Fun = Value.lambda.fun;
if (!Fun->hasFormals())
return;
for (auto Formal : Fun->formals->formals) {
CompletionItem R;
R.label = State.symbols[Formal.name];
R.kind = CompletionItemKind::Constructor;
Result.items.emplace_back(std::move(R));
}
}
} catch (std::out_of_range &) {
// Matching this structure:
//
// *lambda* { | }
// ^

const auto *ParentCall =
dynamic_cast<const nix::ExprCall *>(AST.parent(Node));
if (!ParentCall)
return;

std::optional<nix::Value> LV = AST.getValueEval(ParentCall->fun, State);
if (!LV || !LV->isLambda())
return;

nix::ExprLambda *EL = LV->lambda.fun;
if (!EL->hasFormals())
return;

for (nix::Formal Formal : EL->formals->formals) {
CompletionItem R;
R.label = State.symbols[Formal.name];
R.kind = CompletionItemKind::Constructor;
Result.items.emplace_back(std::move(R));
}
}

Expand Down Expand Up @@ -105,13 +113,10 @@ void CompletionBuilder::addEnv(nix::EvalState &State, nix::Env &NixEnv) {

void CompletionBuilder::addEnv(const EvalAST &AST, nix::EvalState &State,
const nix::Expr *Node) {
try {
// Eval this node, for reaching deeper envs (e.g. with expressions).
AST.getValueEval(Node, State);
if (auto *ExprEnv = AST.searchUpEnv(Node))
addEnv(State, *ExprEnv);
} catch (std::out_of_range &) {
}
// Eval this node, for reaching deeper envs (e.g. with expressions).
AST.getValueEval(Node, State);
if (auto *ExprEnv = AST.searchUpEnv(Node))
addEnv(State, *ExprEnv);
}

void CompletionBuilder::addStaticEnv(const nix::SymbolTable &STable,
Expand Down
Loading

0 comments on commit 78d9de4

Please sign in to comment.