Skip to content

Commit

Permalink
Add ns aliasing
Browse files Browse the repository at this point in the history
This includes the `alias` fn as well as support for resolving aliases
for both keywords and symbols during parsing. Fortunately, this allowed
us to remove some state from keyword objects, too.
  • Loading branch information
jeaye committed Nov 17, 2023
1 parent f326199 commit c6f811b
Show file tree
Hide file tree
Showing 14 changed files with 176 additions and 63 deletions.
2 changes: 2 additions & 0 deletions include/cpp/jank/read/parse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,7 @@ namespace jank::read::parse
runtime::context &rt_ctx;
lex::processor::iterator token_current, token_end;
option<lex::token_kind> expected_closer;
/* Whether or not the next form is considered quoted. */
native_bool quoted{};
};
}
13 changes: 11 additions & 2 deletions include/cpp/jank/runtime/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,26 @@ namespace jank::runtime

ns_ptr intern_ns(obj::symbol_ptr const &);
option<ns_ptr> remove_ns(obj::symbol_ptr const &);
/* Looks up a ns by its symbol. Does not resolve aliases. Does not intern. */
option<ns_ptr> find_ns(obj::symbol_ptr const &);
/* Resolves a symbol which could be an alias to its ns, based on the aliases
* in the current ns. Does not intern. */
option<ns_ptr> resolve_ns(obj::symbol_ptr const &);
ns_ptr current_ns();

/* Adds the current ns to unqualified symbols and resolves the ns of qualified symbols.
* Does not intern. */
obj::symbol_ptr qualify_symbol(obj::symbol_ptr const &);
option<object_ptr> find_local(obj::symbol_ptr const &);

result<var_ptr, native_string> intern_var(obj::symbol_ptr const &);
result<var_ptr, native_string> intern_var(native_string const &ns, native_string const &name);
option<var_ptr> find_var(obj::symbol_ptr const &);
option<var_ptr> find_var(native_string const &ns, native_string const &name);

obj::keyword_ptr intern_keyword(obj::symbol const &sym, bool const resolved);
obj::keyword_ptr intern_keyword(native_string_view const &ns, native_string_view const &name, bool resolved);
result<obj::keyword_ptr, native_string> intern_keyword(obj::symbol const &sym, bool const resolved);
result<obj::keyword_ptr, native_string> intern_keyword
(native_string_view const &ns, native_string_view const &name, bool resolved);

object_ptr macroexpand1(object_ptr o);
object_ptr macroexpand(object_ptr o);
Expand Down
8 changes: 5 additions & 3 deletions include/cpp/jank/runtime/ns.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ namespace jank::runtime
static_object() = delete;
static_object(static_object &&) = default;
static_object(static_object const &) = default;
static_object(obj::symbol_ptr const &name, context const &c)
: name{ name }, rt_ctx{ c }
{ }
static_object(obj::symbol_ptr const &name, context const &c);

result<void, native_string> add_alias(obj::symbol_ptr const &sym, native_box<static_object> const &ns);
option<ns_ptr> find_alias(obj::symbol_ptr const &sym);

/* behavior::objectable */
native_bool equal(object const &) const;
Expand All @@ -38,6 +39,7 @@ namespace jank::runtime
object base{ object_type::ns };
obj::symbol_ptr name{};
folly::Synchronized<native_unordered_map<obj::symbol_ptr, var_ptr>> vars;
folly::Synchronized<native_unordered_map<obj::symbol_ptr, ns_ptr>> aliases;
context const &rt_ctx;
};

Expand Down
8 changes: 2 additions & 6 deletions include/cpp/jank/runtime/obj/keyword.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ namespace jank::runtime
static_object(static_object &&) = default;
static_object(static_object const &) = default;
static_object(object &&base);
static_object(obj::symbol const &s, bool resolved);
static_object(obj::symbol &&s, bool resolved);
static_object(obj::symbol const &s);
static_object(obj::symbol &&s);

/* behavior::objectable */
native_bool equal(object const &) const;
Expand All @@ -40,10 +40,6 @@ namespace jank::runtime
object base{ object_type::keyword };
obj::symbol sym;
option<object_ptr> meta;
/* TODO: Remove this state and always use the RT context to resolve upon creation. */
/* Not resolved means this is a :: keyword. If ns is set, when this is true, it's an ns alias.
* Upon interning, this will be resolved. */
bool resolved{ true };
};

namespace obj
Expand Down
8 changes: 4 additions & 4 deletions src/cpp/jank/analyze/processor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ namespace jank::analyze

/* Macros aren't lifted, since they're not used during runtime. */
auto const unwrapped_var(var.unwrap());
auto const macro_kw(rt_ctx.intern_keyword("", "macro", true));
auto const macro_kw(rt_ctx.intern_keyword("", "macro", true).expect_ok());
if
(
unwrapped_var->meta.is_none() ||
Expand Down Expand Up @@ -958,7 +958,7 @@ namespace jank::analyze
var_deref->var->meta.unwrap(),
make_box<runtime::obj::vector>
(
rt_ctx.intern_keyword("", "arities", true),
rt_ctx.intern_keyword("", "arities", true).expect_ok(),
/* NOTE: We don't support unboxed meta on variadic arities. */
make_box(arg_count)
)
Expand All @@ -968,13 +968,13 @@ namespace jank::analyze
native_bool const supports_unboxed_input
(
runtime::detail::truthy
(get(arity_meta, rt_ctx.intern_keyword("", "supports-unboxed-input?", true)))
(get(arity_meta, rt_ctx.intern_keyword("", "supports-unboxed-input?", true).expect_ok()))
);
native_bool const supports_unboxed_output
(
runtime::detail::truthy
/* TODO: Rename key. */
(get(arity_meta, rt_ctx.intern_keyword("", "unboxed-output?", true)))
(get(arity_meta, rt_ctx.intern_keyword("", "unboxed-output?", true).expect_ok()))
);

if(supports_unboxed_input || supports_unboxed_output)
Expand Down
5 changes: 2 additions & 3 deletions src/cpp/jank/codegen/processor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,9 @@ namespace jank::codegen
fmt::format_to
(
inserter,
R"(__rt_ctx.intern_keyword("{}", "{}", {}))",
R"(__rt_ctx.intern_keyword("{}", "{}", true).expect_ok())",
typed_o->sym.ns,
typed_o->sym.name,
typed_o->resolved
typed_o->sym.name
);
}
else if constexpr(std::same_as<T, runtime::obj::string>)
Expand Down
2 changes: 1 addition & 1 deletion src/cpp/jank/evaluate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ namespace jank::evaluate
if(expr.data->type == runtime::object_type::keyword)
{
auto const d(runtime::expect_object<runtime::obj::keyword>(expr.data));
return rt_ctx.intern_keyword(d->sym, d->resolved);
return rt_ctx.intern_keyword(d->sym, true).expect_ok();
}
return expr.data;
}
Expand Down
30 changes: 25 additions & 5 deletions src/cpp/jank/read/parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,17 +181,20 @@ namespace jank::read::parse
{
auto const start_token(token_current.latest.unwrap().expect_ok());
++token_current;
auto const old_quoted(quoted);
quoted = true;
auto val_result(next());
quoted = old_quoted;
if(val_result.is_err())
{ return val_result; }
else if(val_result.expect_ok() == nullptr)
{ return err(error{ start_token.pos, native_string{ "invalid value after quote" } }); }

return runtime::erase
(
jank::make_box<runtime::obj::list>
make_box<runtime::obj::list>
(
jank::make_box<runtime::obj::symbol>("quote"),
make_box<runtime::obj::symbol>("quote"),
val_result.expect_ok_move()
)
);
Expand Down Expand Up @@ -220,12 +223,23 @@ namespace jank::read::parse
native_string ns, name;
if(slash != native_string::npos)
{
/* If it's only a slash, that a name. Otherwise, it's a ns/name separator. */
/* If it's only a slash, it's a name. Otherwise, it's a ns/name separator. */
if(sv.size() == 1)
{ name = sv; }
else
{
ns = sv.substr(0, slash);
auto const ns_portion(sv.substr(0, slash));
/* Quoted symbols can have any ns and it doesn't need to exist. */
if(quoted)
{ ns = ns_portion; }
/* Normal symbols will have the ns resolved immediately. */
else
{
auto const resolved_ns(rt_ctx.resolve_ns(make_box<runtime::obj::symbol>(ns_portion)));
if(resolved_ns.is_none())
{ return err(error{ token.pos, fmt::format("unknown namespace: {}", ns_portion) }); }
ns = resolved_ns.unwrap()->name->name;
}
name = sv.substr(slash + 1);
}
}
Expand All @@ -239,6 +253,8 @@ namespace jank::read::parse
auto const token((*token_current).expect_ok());
++token_current;
auto const sv(boost::get<native_string_view>(token.data));
/* A :: keyword either resolves to the current ns or an alias, depending on
* whether or not it's qualified. */
bool const resolved{ sv[0] != ':' };

auto const slash(sv.find('/'));
Expand All @@ -253,7 +269,11 @@ namespace jank::read::parse
}
else
{ name = sv.substr(resolved ? 0 : 1); }
return ok(rt_ctx.intern_keyword(runtime::obj::symbol{ ns, name }, resolved));

auto const intern_res(rt_ctx.intern_keyword(runtime::obj::symbol{ ns, name }, resolved));
if(intern_res.is_err())
{ return err(intern_res.expect_err()); }
return ok(intern_res.expect_ok());
}

processor::object_result processor::parse_integer()
Expand Down
41 changes: 31 additions & 10 deletions src/cpp/jank/runtime/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ namespace jank::runtime
t_state.current_ns = ns_res.first->second;
}

intern_ns(make_box<obj::symbol>("native"));

auto const in_ns_sym(make_box<obj::symbol>("clojure.core/in-ns"));
std::function<object_ptr (object_ptr)> in_ns_fn
(
Expand Down Expand Up @@ -151,10 +153,11 @@ namespace jank::runtime
}
}

option<var_ptr> context::find_var(native_string const &ns, native_string const &name)
{ return find_var(make_box<obj::symbol>(ns, name)); }

option<object_ptr> context::find_local(obj::symbol_ptr const &)
{
return none;
}
{ return none; }

/* TODO: Use module loader. */
void context::eval_prelude()
Expand Down Expand Up @@ -351,6 +354,20 @@ namespace jank::runtime
return none;
}

option<ns_ptr> context::resolve_ns(obj::symbol_ptr const &target)
{
auto const ns(current_ns());
auto const alias(ns->find_alias(target));
if(alias.is_some())
{ return alias.unwrap(); }

return find_ns(target);
}

/* TODO: Cache this var. */
ns_ptr context::current_ns()
{ return expect_object<ns>(find_var("clojure.core", "*ns*").unwrap()->get_root()); }

result<var_ptr, native_string> context::intern_var
(native_string const &ns, native_string const &name)
{ return intern_var(make_box<obj::symbol>(ns, name)); }
Expand All @@ -377,24 +394,28 @@ namespace jank::runtime
}

/* TODO: Swap these. The other one makes a symbol anyway. */
obj::keyword_ptr context::intern_keyword(obj::symbol const &sym, bool const resolved)
result<obj::keyword_ptr, native_string> context::intern_keyword(obj::symbol const &sym, bool const resolved)
{ return intern_keyword(sym.ns, sym.name, resolved); }
obj::keyword_ptr context::intern_keyword
(native_string_view const &ns, native_string_view const &name, bool resolved)
result<obj::keyword_ptr, native_string> context::intern_keyword
(native_string_view const &ns, native_string_view const &name, bool const resolved)
{
profile::timer timer{ "rt intern_keyword" };
obj::symbol sym{ ns, name };
if(!resolved)
{
/* The ns will be an ns alias. */
if(!ns.empty())
{ throw std::runtime_error{ "unimplemented: auto-resolved ns aliases" }; }
{
auto const resolved_ns(resolve_ns(make_box<obj::symbol>(ns)));
if(resolved_ns.is_none())
{ return err(fmt::format("Unable to resolve ns for keyword: {}", ns)); }
sym.ns = resolved_ns.unwrap()->name->name;
}
else
{
auto const t_state(get_thread_state());
auto const current_ns(expect_object<jank::runtime::ns>(t_state.current_ns->get_root()));
sym.ns = current_ns->name->name;
resolved = true;
}
}

Expand All @@ -403,7 +424,7 @@ namespace jank::runtime
if(found != locked_keywords->end())
{ return found->second; }

auto const res(locked_keywords->emplace(sym, make_box<obj::keyword>(sym, resolved)));
auto const res(locked_keywords->emplace(sym, make_box<obj::keyword>(sym)));
return res.first->second;
}

Expand Down Expand Up @@ -433,7 +454,7 @@ namespace jank::runtime
{ return typed_o; }

auto const meta(var.unwrap()->meta.unwrap());
auto const found_macro(get(meta, intern_keyword("", "macro", true)));
auto const found_macro(get(meta, intern_keyword("", "macro", true).expect_ok()));
if(!found_macro || !detail::truthy(found_macro))
{ return typed_o; }

Expand Down
24 changes: 24 additions & 0 deletions src/cpp/jank/runtime/ns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,30 @@

namespace jank::runtime
{
ns::static_object(obj::symbol_ptr const &name, context const &c)
: name{ name }, rt_ctx{ c }
{ }

result<void, native_string> ns::add_alias(obj::symbol_ptr const &sym, native_box<static_object> const &ns)
{
auto locked_aliases(aliases.wlock());
auto const found(locked_aliases->find(sym));
if(found != locked_aliases->end() && found->second != ns)
{ return err(fmt::format("Alias already bound to a different ns: {}", sym->to_string())); }

locked_aliases->emplace(sym, ns);
return ok();
}

option<ns_ptr> ns::find_alias(obj::symbol_ptr const &sym)
{
auto locked_aliases(aliases.rlock());
auto const found(locked_aliases->find(sym));
if(found != locked_aliases->end())
{ return found->second; }
return none;
}

native_bool ns::equal(object const &o) const
{
if(o.type != object_type::ns)
Expand Down
12 changes: 6 additions & 6 deletions src/cpp/jank/runtime/obj/keyword.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

namespace jank::runtime
{
obj::keyword::static_object(obj::symbol const &s, bool const resolved)
: sym{ s }, resolved{ resolved }
obj::keyword::static_object(obj::symbol const &s)
: sym{ s }
{ }
obj::keyword::static_object(obj::symbol &&s, bool const resolved)
: sym{ std::move(s) }, resolved{ resolved }
obj::keyword::static_object(obj::symbol &&s)
: sym{ std::move(s) }
{ }

/* Keywords are interned, so we can always count on identity equality. */
Expand All @@ -35,11 +35,11 @@ namespace jank::runtime
object_ptr obj::keyword::with_meta(object_ptr m) const
{
auto const meta(behavior::detail::validate_meta(m));
auto ret(make_box<obj::keyword>(sym, resolved));
auto ret(make_box<obj::keyword>(sym));
ret->meta = meta;
return ret;
}

bool obj::keyword::operator ==(obj::keyword const &rhs) const
{ return resolved == rhs.resolved && sym == rhs.sym; }
{ return sym == rhs.sym; }
}
8 changes: 7 additions & 1 deletion src/jank/clojure/core.jank
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,12 @@
(recur (rest libs)))))

(defn alias [alias ns-sym]
"TODO")
(let [ns-obj (the-ns ns-sym)]
(assert (symbol? alias))
(native/raw "expect_object<ns>(#{ *ns* }#)->add_alias
(
expect_object<obj::symbol>(#{ alias }#),
expect_object<ns>(#{ ns-obj }#)
).expect_ok();")))

(println "Bottom of clojure.core")
2 changes: 1 addition & 1 deletion test/cpp/jank/jit/processor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ namespace jank::jit
TEST_CASE("Files")
{
runtime::context rt_ctx;
auto const cardinal_result(rt_ctx.intern_keyword(runtime::obj::symbol{ "", "success" }, true));
auto const cardinal_result(rt_ctx.intern_keyword(runtime::obj::symbol{ "", "success" }, true).expect_ok());
rt_ctx.load_module("clojure.core");
size_t test_count{};

Expand Down

0 comments on commit c6f811b

Please sign in to comment.