nix-super/src/libexpr/nixexpr.cc

629 lines
17 KiB
C++
Raw Normal View History

#include "nixexpr.hh"
#include "derivations.hh"
#include "eval.hh"
#include "symbol-table.hh"
#include "util.hh"
#include "print.hh"
#include <cstdlib>
namespace nix {
unsigned long Expr::nrExprs = 0;
ExprBlackHole eBlackHole;
// FIXME: remove, because *symbols* are abstract and do not have a single
// textual representation; see printIdentifier()
std::ostream & operator <<(std::ostream & str, const SymbolStr & symbol)
2014-10-20 09:44:32 +03:00
{
std::string_view s = symbol;
return printIdentifier(str, s);
2014-10-20 09:44:32 +03:00
}
void Expr::show(const SymbolTable & symbols, std::ostream & str) const
2010-04-13 01:03:27 +03:00
{
abort();
}
void ExprInt::show(const SymbolTable & symbols, std::ostream & str) const
{
str << v.integer;
}
void ExprFloat::show(const SymbolTable & symbols, std::ostream & str) const
{
str << v.fpoint;
}
void ExprString::show(const SymbolTable & symbols, std::ostream & str) const
{
2023-04-16 13:56:31 +03:00
printLiteralString(str, s);
}
void ExprPath::show(const SymbolTable & symbols, std::ostream & str) const
{
str << s;
}
void ExprVar::show(const SymbolTable & symbols, std::ostream & str) const
{
str << symbols[name];
}
void ExprSelect::show(const SymbolTable & symbols, std::ostream & str) const
{
str << "(";
e->show(symbols, str);
str << ")." << showAttrPath(symbols, attrPath);
if (def) {
str << " or (";
def->show(symbols, str);
str << ")";
}
}
void ExprOpHasAttr::show(const SymbolTable & symbols, std::ostream & str) const
2010-04-13 00:21:24 +03:00
{
str << "((";
e->show(symbols, str);
str << ") ? " << showAttrPath(symbols, attrPath) << ")";
2010-04-13 00:21:24 +03:00
}
void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) const
{
typedef const decltype(attrs)::value_type * Attr;
std::vector<Attr> sorted;
for (auto & i : attrs) sorted.push_back(&i);
std::sort(sorted.begin(), sorted.end(), [&](Attr a, Attr b) {
std::string_view sa = symbols[a->first], sb = symbols[b->first];
return sa < sb;
});
std::vector<Symbol> inherits;
std::map<ExprInheritFrom *, std::vector<Symbol>> inheritsFrom;
for (auto & i : sorted) {
switch (i->second.kind) {
case AttrDef::Kind::Plain:
break;
case AttrDef::Kind::Inherited:
inherits.push_back(i->first);
break;
case AttrDef::Kind::InheritedFrom: {
auto & select = dynamic_cast<ExprSelect &>(*i->second.e);
auto & from = dynamic_cast<ExprInheritFrom &>(*select.e);
inheritsFrom[&from].push_back(i->first);
break;
}
}
}
if (!inherits.empty()) {
str << "inherit";
for (auto sym : inherits) str << " " << symbols[sym];
str << "; ";
}
for (const auto & [from, syms] : inheritsFrom) {
str << "inherit (";
(*inheritFromExprs)[from->displ]->show(symbols, str);
str << ")";
for (auto sym : syms) str << " " << symbols[sym];
str << "; ";
}
for (auto & i : sorted) {
if (i->second.kind == AttrDef::Kind::Plain) {
str << symbols[i->first] << " = ";
i->second.e->show(symbols, str);
str << "; ";
}
}
for (auto & i : dynamicAttrs) {
str << "\"${";
i.nameExpr->show(symbols, str);
str << "}\" = ";
i.valueExpr->show(symbols, str);
str << "; ";
}
}
void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
{
if (recursive) str << "rec ";
str << "{ ";
showBindings(symbols, str);
str << "}";
}
void ExprList::show(const SymbolTable & symbols, std::ostream & str) const
{
str << "[ ";
for (auto & i : elems) {
str << "(";
i->show(symbols, str);
str << ") ";
}
str << "]";
}
void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const
{
str << "(";
if (hasFormals()) {
str << "{ ";
bool first = true;
// the natural Symbol ordering is by creation time, which can lead to the
// same expression being printed in two different ways depending on its
// context. always use lexicographic ordering to avoid this.
for (auto & i : formals->lexicographicOrder(symbols)) {
if (first) first = false; else str << ", ";
str << symbols[i.name];
if (i.def) {
str << " ? ";
i.def->show(symbols, str);
}
}
2014-10-20 09:44:32 +03:00
if (formals->ellipsis) {
if (!first) str << ", ";
str << "...";
}
str << " }";
if (arg) str << " @ ";
}
if (arg) str << symbols[arg];
str << ": ";
body->show(symbols, str);
str << ")";
}
void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const
{
str << '(';
fun->show(symbols, str);
for (auto e : args) {
str << ' ';
e->show(symbols, str);
}
str << ')';
}
void ExprLet::show(const SymbolTable & symbols, std::ostream & str) const
{
2014-10-20 09:44:32 +03:00
str << "(let ";
attrs->showBindings(symbols, str);
str << "in ";
body->show(symbols, str);
str << ")";
}
void ExprWith::show(const SymbolTable & symbols, std::ostream & str) const
{
str << "(with ";
attrs->show(symbols, str);
str << "; ";
body->show(symbols, str);
str << ")";
}
void ExprIf::show(const SymbolTable & symbols, std::ostream & str) const
{
str << "(if ";
cond->show(symbols, str);
str << " then ";
then->show(symbols, str);
str << " else ";
else_->show(symbols, str);
str << ")";
}
void ExprAssert::show(const SymbolTable & symbols, std::ostream & str) const
2010-04-13 00:21:24 +03:00
{
str << "assert ";
cond->show(symbols, str);
str << "; ";
body->show(symbols, str);
2010-04-13 00:21:24 +03:00
}
void ExprOpNot::show(const SymbolTable & symbols, std::ostream & str) const
2010-04-13 00:21:24 +03:00
{
str << "(! ";
e->show(symbols, str);
str << ")";
2010-04-13 00:21:24 +03:00
}
void ExprConcatStrings::show(const SymbolTable & symbols, std::ostream & str) const
2010-04-13 00:21:24 +03:00
{
bool first = true;
2014-10-20 09:44:32 +03:00
str << "(";
2015-07-17 20:24:28 +03:00
for (auto & i : *es) {
2010-04-13 00:21:24 +03:00
if (first) first = false; else str << " + ";
i.second->show(symbols, str);
2010-04-13 00:21:24 +03:00
}
2014-10-20 09:44:32 +03:00
str << ")";
2010-04-13 00:21:24 +03:00
}
void ExprPos::show(const SymbolTable & symbols, std::ostream & str) const
{
str << "__curPos";
}
2010-04-13 00:21:24 +03:00
std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath)
{
std::ostringstream out;
bool first = true;
2015-03-06 15:24:08 +02:00
for (auto & i : attrPath) {
if (!first) out << '.'; else first = false;
if (i.symbol)
out << symbols[i.symbol];
else {
out << "\"${";
i.expr->show(symbols, out);
out << "}\"";
}
}
return out.str();
}
/* Computing levels/displacements for variables. */
void Expr::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
abort();
}
void ExprInt::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
2022-05-23 06:45:24 +03:00
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
}
void ExprFloat::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
2022-05-23 06:45:24 +03:00
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
}
void ExprString::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
2022-05-23 06:45:24 +03:00
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
}
void ExprPath::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
2022-05-23 06:45:24 +03:00
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
}
void ExprVar::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
2022-05-23 06:45:24 +03:00
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
2021-10-02 22:47:36 +03:00
fromWith = nullptr;
/* Check whether the variable appears in the environment. If so,
set its level and displacement. */
2021-10-22 23:49:58 +03:00
const StaticEnv * curEnv;
2020-02-24 15:33:01 +02:00
Level level;
2021-10-22 23:49:58 +03:00
int withLevel = -1;
for (curEnv = env.get(), level = 0; curEnv; curEnv = curEnv->up, level++) {
if (curEnv->isWith) {
if (withLevel == -1) withLevel = level;
} else {
auto i = curEnv->find(name);
2021-10-22 23:49:58 +03:00
if (i != curEnv->vars.end()) {
this->level = level;
displ = i->second;
return;
}
}
}
2021-10-22 23:49:58 +03:00
/* Otherwise, the variable must be obtained from the nearest
enclosing `with'. If there is no `with', then we can issue an
"undefined variable" error now. */
2022-04-29 20:24:54 +03:00
if (withLevel == -1)
es.error<UndefinedVarError>(
"undefined variable '%1%'",
es.symbols[name]
).atPos(pos).debugThrow();
for (auto * e = env.get(); e && !fromWith; e = e->up)
fromWith = e->isWith;
2021-10-22 23:49:58 +03:00
this->level = withLevel;
}
void ExprInheritFrom::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
}
void ExprSelect::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
2022-05-23 06:45:24 +03:00
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
2021-10-02 22:47:36 +03:00
e->bindVars(es, env);
if (def) def->bindVars(es, env);
2015-07-17 20:24:28 +03:00
for (auto & i : attrPath)
if (!i.symbol)
i.expr->bindVars(es, env);
}
void ExprOpHasAttr::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
2022-05-23 06:45:24 +03:00
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
2021-10-02 22:47:36 +03:00
e->bindVars(es, env);
2015-07-17 20:24:28 +03:00
for (auto & i : attrPath)
if (!i.symbol)
i.expr->bindVars(es, env);
}
std::shared_ptr<const StaticEnv> ExprAttrs::bindInheritSources(
EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
if (!inheritFromExprs)
return nullptr;
// the inherit (from) source values are inserted into an env of its own, which
// does not introduce any variable names.
// analysis must see an empty env, or an env that contains only entries with
// otherwise unused names to not interfere with regular names. the parser
// has already filled all exprs that access this env with appropriate level
// and displacement, and nothing else is allowed to access it. ideally we'd
// not even *have* an expr that grabs anything from this env since it's fully
// invisible, but the evaluator does not allow for this yet.
auto inner = std::make_shared<StaticEnv>(nullptr, env.get(), 0);
for (auto from : *inheritFromExprs)
from->bindVars(es, env);
return inner;
}
void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
2022-05-23 06:45:24 +03:00
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
2021-10-02 22:47:36 +03:00
if (recursive) {
auto newEnv = [&] () -> std::shared_ptr<const StaticEnv> {
auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs.size());
2013-09-02 17:29:15 +03:00
Displacement displ = 0;
for (auto & i : attrs)
newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
return newEnv;
}();
// No need to sort newEnv since attrs is in sorted order.
2013-09-02 17:29:15 +03:00
auto inheritFromEnv = bindInheritSources(es, newEnv);
2015-07-17 20:24:28 +03:00
for (auto & i : attrs)
i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv));
2021-09-16 01:16:53 +03:00
for (auto & i : dynamicAttrs) {
i.nameExpr->bindVars(es, newEnv);
i.valueExpr->bindVars(es, newEnv);
2021-09-16 01:16:53 +03:00
}
}
2021-09-16 01:16:53 +03:00
else {
auto inheritFromEnv = bindInheritSources(es, env);
2015-07-17 20:24:28 +03:00
for (auto & i : attrs)
i.second.e->bindVars(es, i.second.chooseByKind(env, env, inheritFromEnv));
Dynamic attrs This adds new syntax for attribute names: * attrs."${name}" => getAttr name attrs * attrs ? "${name}" => isAttrs attrs && hasAttr attrs name * attrs."${name}" or def => if attrs ? "${name}" then attrs."${name}" else def * { "${name}" = value; } => listToAttrs [{ inherit name value; }] Of course, it's a bit more complicated than that. The attribute chains can be arbitrarily long and contain combinations of static and dynamic parts (e.g. attrs."${foo}".bar."${baz}" or qux), which is relatively straightforward for the getAttrs/hasAttrs cases but is more complex for the listToAttrs case due to rules about duplicate attribute definitions. For attribute sets with dynamic attribute names, duplicate static attributes are detected at parse time while duplicate dynamic attributes are detected when the attribute set is forced. So, for example, { a = null; a.b = null; "${"c"}" = true; } will be a parse-time error, while { a = {}; "${"a"}".b = null; c = true; } will be an eval-time error (technically that case could theoretically be detected at parse time, but the general case would require full evaluation). Moreover, duplicate dynamic attributes are not allowed even in cases where they would be with static attributes ({ a.b.d = true; a.b.c = false; } is legal, but { a."${"b"}".d = true; a."${"b"}".c = false; } is not). This restriction might be relaxed in the future in cases where the static variant would not be an error, but it is not obvious that that is desirable. Finally, recursive attribute sets with dynamic attributes have the static attributes in scope but not the dynamic ones. So rec { a = true; "${"b"}" = a; } is equivalent to { a = true; b = true; } but rec { "${"a"}" = true; b = a; } would be an error or use a from the surrounding scope if it exists. Note that the getAttr, getAttr or default, and hasAttr are all implemented purely in the parser as syntactic sugar, while attribute sets with dynamic attribute names required changes to the AST to be implemented cleanly. This is an alternative solution to and closes #167 Signed-off-by: Shea Levy <shea@shealevy.com>
2013-09-21 06:25:30 +03:00
2021-09-16 01:16:53 +03:00
for (auto & i : dynamicAttrs) {
i.nameExpr->bindVars(es, env);
i.valueExpr->bindVars(es, env);
2021-09-16 01:16:53 +03:00
}
Dynamic attrs This adds new syntax for attribute names: * attrs."${name}" => getAttr name attrs * attrs ? "${name}" => isAttrs attrs && hasAttr attrs name * attrs."${name}" or def => if attrs ? "${name}" then attrs."${name}" else def * { "${name}" = value; } => listToAttrs [{ inherit name value; }] Of course, it's a bit more complicated than that. The attribute chains can be arbitrarily long and contain combinations of static and dynamic parts (e.g. attrs."${foo}".bar."${baz}" or qux), which is relatively straightforward for the getAttrs/hasAttrs cases but is more complex for the listToAttrs case due to rules about duplicate attribute definitions. For attribute sets with dynamic attribute names, duplicate static attributes are detected at parse time while duplicate dynamic attributes are detected when the attribute set is forced. So, for example, { a = null; a.b = null; "${"c"}" = true; } will be a parse-time error, while { a = {}; "${"a"}".b = null; c = true; } will be an eval-time error (technically that case could theoretically be detected at parse time, but the general case would require full evaluation). Moreover, duplicate dynamic attributes are not allowed even in cases where they would be with static attributes ({ a.b.d = true; a.b.c = false; } is legal, but { a."${"b"}".d = true; a."${"b"}".c = false; } is not). This restriction might be relaxed in the future in cases where the static variant would not be an error, but it is not obvious that that is desirable. Finally, recursive attribute sets with dynamic attributes have the static attributes in scope but not the dynamic ones. So rec { a = true; "${"b"}" = a; } is equivalent to { a = true; b = true; } but rec { "${"a"}" = true; b = a; } would be an error or use a from the surrounding scope if it exists. Note that the getAttr, getAttr or default, and hasAttr are all implemented purely in the parser as syntactic sugar, while attribute sets with dynamic attribute names required changes to the AST to be implemented cleanly. This is an alternative solution to and closes #167 Signed-off-by: Shea Levy <shea@shealevy.com>
2013-09-21 06:25:30 +03:00
}
}
void ExprList::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
2022-05-23 06:45:24 +03:00
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
2021-10-02 22:47:36 +03:00
2015-07-17 20:24:28 +03:00
for (auto & i : elems)
i->bindVars(es, env);
}
void ExprLambda::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
2022-05-23 06:45:24 +03:00
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
2021-10-02 22:47:36 +03:00
auto newEnv = std::make_shared<StaticEnv>(
nullptr, env.get(),
(hasFormals() ? formals->formals.size() : 0) +
(!arg ? 0 : 1));
2013-09-02 17:29:15 +03:00
2020-02-24 15:33:01 +02:00
Displacement displ = 0;
2013-09-02 17:29:15 +03:00
if (arg) newEnv->vars.emplace_back(arg, displ++);
if (hasFormals()) {
2015-07-17 20:24:28 +03:00
for (auto & i : formals->formals)
2021-11-30 23:15:02 +02:00
newEnv->vars.emplace_back(i.name, displ++);
2021-11-30 23:15:02 +02:00
newEnv->sort();
2015-07-17 20:24:28 +03:00
for (auto & i : formals->formals)
if (i.def) i.def->bindVars(es, newEnv);
}
body->bindVars(es, newEnv);
}
void ExprCall::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
2022-05-23 06:45:24 +03:00
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
2021-10-02 22:47:36 +03:00
fun->bindVars(es, env);
for (auto e : args)
e->bindVars(es, env);
}
void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
auto newEnv = [&] () -> std::shared_ptr<const StaticEnv> {
auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs->attrs.size());
2013-09-02 17:29:15 +03:00
Displacement displ = 0;
for (auto & i : attrs->attrs)
newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
return newEnv;
}();
// No need to sort newEnv since attrs->attrs is in sorted order.
2013-09-02 17:29:15 +03:00
auto inheritFromEnv = attrs->bindInheritSources(es, newEnv);
2015-07-17 20:24:28 +03:00
for (auto & i : attrs->attrs)
i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv));
2013-09-02 17:29:15 +03:00
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, newEnv));
body->bindVars(es, newEnv);
}
void ExprWith::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
2022-05-23 06:45:24 +03:00
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
2021-10-02 22:47:36 +03:00
parentWith = nullptr;
for (auto * e = env.get(); e && !parentWith; e = e->up)
parentWith = e->isWith;
2010-04-14 18:01:04 +03:00
/* Does this `with' have an enclosing `with'? If so, record its
level so that `lookupVar' can look up variables in the previous
`with' if this one doesn't contain the desired attribute. */
2010-04-14 18:01:04 +03:00
const StaticEnv * curEnv;
2020-02-24 15:33:01 +02:00
Level level;
prevWith = 0;
2021-09-14 19:49:22 +03:00
for (curEnv = env.get(), level = 1; curEnv; curEnv = curEnv->up, level++)
2010-04-14 18:01:04 +03:00
if (curEnv->isWith) {
prevWith = level;
break;
}
2013-09-02 17:29:15 +03:00
attrs->bindVars(es, env);
auto newEnv = std::make_shared<StaticEnv>(this, env.get());
body->bindVars(es, newEnv);
}
void ExprIf::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
2022-05-23 06:45:24 +03:00
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
2021-10-02 22:47:36 +03:00
cond->bindVars(es, env);
then->bindVars(es, env);
else_->bindVars(es, env);
}
void ExprAssert::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
2022-05-23 06:45:24 +03:00
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
2021-10-02 22:47:36 +03:00
cond->bindVars(es, env);
body->bindVars(es, env);
}
void ExprOpNot::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
2022-05-23 06:45:24 +03:00
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
2021-10-02 22:47:36 +03:00
e->bindVars(es, env);
}
void ExprConcatStrings::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
2022-05-23 06:45:24 +03:00
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
2021-10-02 22:47:36 +03:00
for (auto & i : *this->es)
i.second->bindVars(es, env);
}
void ExprPos::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{
2022-05-23 06:45:24 +03:00
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
}
/* Storing function names. */
void Expr::setName(Symbol name)
{
}
void ExprLambda::setName(Symbol name)
{
this->name = name;
body->setName(name);
}
std::string ExprLambda::showNamePos(const EvalState & state) const
{
std::string id(name
? concatStrings("'", state.symbols[name], "'")
: "anonymous function");
return fmt("%1% at %2%", id, state.positions[pos]);
}
use byte indexed locations for PosIdx we now keep not a table of all positions, but a table of all origins and their sizes. position indices are now direct pointers into the virtual concatenation of all parsed contents. this slightly reduces memory usage and time spent in the parser, at the cost of not being able to report positions if the total input size exceeds 4GiB. this limit is not unique to nix though, rustc and clang also limit their input to 4GiB (although at least clang refuses to process inputs that are larger, we will not). this new 4GiB limit probably will not cause any problems for quite a while, all of nixpkgs together is less than 100MiB in size and already needs over 700MiB of memory and multiple seconds just to parse. 4GiB worth of input will easily take multiple minutes and over 30GiB of memory without even evaluating anything. if problems *do* arise we can probably recover the old table-based system by adding some tracking to Pos::Origin (or increasing the size of PosIdx outright), but for time being this looks like more complexity than it's worth. since we now need to read the entire input again to determine the line/column of a position we'll make unsafeGetAttrPos slightly lazy: mostly the set it returns is only used to determine the file of origin of an attribute, not its exact location. the thunks do not add measurable runtime overhead. notably this change is necessary to allow changing the parser since apparently nothing supports nix's very idiosyncratic line ending choice of "anything goes", making it very hard to calculate line/column positions in the parser (while byte offsets are very easy).
2024-01-29 07:19:23 +02:00
/* Position table. */
Pos PosTable::operator[](PosIdx p) const
{
auto origin = resolve(p);
if (!origin)
return {};
const auto offset = origin->offsetOf(p);
Pos result{0, 0, origin->origin};
auto lines = this->lines.lock();
auto linesForInput = (*lines)[origin->offset];
if (linesForInput.empty()) {
auto source = result.getSource().value_or("");
const char * begin = source.data();
for (Pos::LinesIterator it(source), end; it != end; it++)
linesForInput.push_back(it->data() - begin);
if (linesForInput.empty())
linesForInput.push_back(0);
}
// as above: the first line starts at byte 0 and is always present
auto lineStartOffset = std::prev(
std::upper_bound(linesForInput.begin(), linesForInput.end(), offset));
result.line = 1 + (lineStartOffset - linesForInput.begin());
result.column = 1 + (offset - *lineStartOffset);
return result;
}
/* Symbol table. */
size_t SymbolTable::totalSize() const
{
size_t n = 0;
dump([&] (const std::string & s) { n += s.size(); });
return n;
}
}