diff --git a/CMakeLists.txt b/CMakeLists.txt index 3878cd059..34794dc9a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,6 +147,7 @@ add_library( src/cpp/jank/util/cli.cpp src/cpp/jank/util/mapped_file.cpp src/cpp/jank/util/scope_exit.cpp + src/cpp/jank/util/escape.cpp src/cpp/jank/profile/time.cpp src/cpp/jank/read/lex.cpp src/cpp/jank/read/parse.cpp diff --git a/include/cpp/jank/option.hpp b/include/cpp/jank/option.hpp index 298462c0f..e5afbecec 100644 --- a/include/cpp/jank/option.hpp +++ b/include/cpp/jank/option.hpp @@ -199,7 +199,7 @@ namespace jank return *reinterpret_cast(data) != *reinterpret_cast(rhs.data); } - return true; + return false; } constexpr native_bool operator==(option const &rhs) const diff --git a/include/cpp/jank/read/lex.hpp b/include/cpp/jank/read/lex.hpp index 2c098c80c..f3bfacf28 100644 --- a/include/cpp/jank/read/lex.hpp +++ b/include/cpp/jank/read/lex.hpp @@ -45,6 +45,7 @@ namespace jank::read::lex struct token { + token() = default; token(token_kind const k); token(size_t const p, token_kind const k); token(size_t const p, token_kind const k, native_integer const); @@ -56,6 +57,7 @@ namespace jank::read::lex token(size_t const p, size_t const s, token_kind const k, native_integer const); token(size_t const p, size_t const s, token_kind const k, native_real const); token(size_t const p, size_t const s, token_kind const k, native_persistent_string_view const); + token(size_t const p, size_t const s, token_kind const k, char const * const); token(size_t const p, size_t const s, token_kind const k, native_bool const); native_bool operator==(token const &rhs) const; @@ -72,7 +74,7 @@ namespace jank::read::lex static constexpr size_t ignore_pos{ std::numeric_limits::max() }; size_t pos{ ignore_pos }; size_t size{ 1 }; - token_kind kind; + token_kind kind{ token_kind::eof }; boost::variant data; }; diff --git a/include/cpp/jank/read/parse.hpp b/include/cpp/jank/read/parse.hpp index 44a48dd99..fc69e35de 100644 --- a/include/cpp/jank/read/parse.hpp +++ b/include/cpp/jank/read/parse.hpp @@ -14,8 +14,16 @@ namespace jank::read::parse { struct processor { - /* TODO: none instead of nullptr. */ - using object_result = result; + struct object_source_info + { + native_bool operator==(object_source_info const &rhs) const; + native_bool operator!=(object_source_info const &rhs) const; + + runtime::object_ptr ptr{}; + lex::token start, end; + }; + + using object_result = result, error>; struct iterator { @@ -73,6 +81,7 @@ namespace jank::read::parse * This is needed because parse iteration works one form at a time and splicing potentially * turns one form into many. */ std::list pending_forms; + lex::token latest_token; /* Whether or not the next form is considered quoted. */ native_bool quoted{}; }; diff --git a/include/cpp/jank/codegen/escape.hpp b/include/cpp/jank/util/escape.hpp similarity index 59% rename from include/cpp/jank/codegen/escape.hpp rename to include/cpp/jank/util/escape.hpp index 32c03c18a..42525fe82 100644 --- a/include/cpp/jank/codegen/escape.hpp +++ b/include/cpp/jank/util/escape.hpp @@ -1,10 +1,14 @@ #pragma once -/* This provides a fmt extension for escaping strings and wrapping them in - * quotes. It's largely adapted from here: - * https://github.com/fmtlib/fmt/issues/825#issuecomment-1227501168 */ -namespace jank::codegen +namespace jank::util { + /* This provides a fmt extension for escaping strings and wrapping them in + * quotes. It's largely adapted from here: + * https://github.com/fmtlib/fmt/issues/825#issuecomment-1227501168 + * + * Usage just looks like: + * fmt::format("{}", util::escaped_quoted_view(s)) + */ template struct escape_view { @@ -50,16 +54,22 @@ namespace jank::codegen }; constexpr escape_view - escaped(native_persistent_string_view const &sv, char const q = '"', char const e = '\\') + escaped_quoted_view(native_persistent_string_view const &sv, + char const q = '"', + char const e = '\\') { return escape_view{ sv, q, e }; } + + /* These provide normal escaping/unescaping, with no quoting. */ + string_result unescape(native_transient_string const &input); + native_transient_string escape(native_transient_string const &input); } template -struct fmt::formatter> +struct fmt::formatter> { - using V = jank::codegen::escape_view; + using V = jank::util::escape_view; template constexpr auto parse(C &ctx) @@ -68,7 +78,7 @@ struct fmt::formatter> } template - auto format(jank::codegen::escape_view const &s, C &ctx) + auto format(jank::util::escape_view const &s, C &ctx) { return s.copy(ctx.out()); } diff --git a/src/cpp/jank/analyze/processor.cpp b/src/cpp/jank/analyze/processor.cpp index 96dc19824..db7ac38cf 100644 --- a/src/cpp/jank/analyze/processor.cpp +++ b/src/cpp/jank/analyze/processor.cpp @@ -65,7 +65,7 @@ namespace jank::analyze { return err(parse_current->expect_err_move()); } - fn.push_back(parse_current->expect_ok()); + fn.push_back(parse_current->expect_ok().unwrap().ptr); } auto fn_list(make_box(std::in_place, fn.rbegin(), fn.rend())); return analyze(fn_list, expression_type::expression); @@ -957,54 +957,68 @@ namespace jank::analyze /* native/raw expressions are broken up into chunks of either literal C++ code or * interpolated jank code, the latter needing to also be analyzed. */ decltype(expr::native_raw::chunks) chunks; - /* TODO: Just use } for end and rely on token parsing info for when that is. - * This requires storing line/col start/end meta in each object. */ - constexpr native_persistent_string_view interp_start{ "#{" }, interp_end{ "}#" }; - for(size_t it{}; it != native_persistent_string::npos;) + constexpr native_persistent_string_view interp_start{ "~{" }; + for(size_t it{}; it < code_str->data.size();) { - auto const next_start(code_str->data.find(interp_start.data(), it)); - if(next_start == native_persistent_string::npos) + auto const next_interp(code_str->data.find(interp_start.data(), it)); + if(next_interp == native_persistent_string::npos) { /* This is the final chunk. */ chunks.emplace_back(native_persistent_string_view{ code_str->data.data() + it }); break; } - auto const next_end(code_str->data.find(interp_end.data(), next_start)); - if(next_end == native_persistent_string::npos) - { - return err( - error{ fmt::format("no matching {} found for native/raw interpolation", interp_end) }); - } + /* Once we've found the start of an interpolation, we begin lexing/parsing at that + * spot, so we can get a jank value. */ read::lex::processor l_prc{ - {code_str->data.data() + next_start + interp_start.size(), - next_end - next_start - interp_end.size()} + {code_str->data.data() + next_interp + interp_start.size(), + code_str->data.data() + code_str->data.size()} }; read::parse::processor p_prc{ rt_ctx, l_prc.begin(), l_prc.end() }; - auto parsed_it(p_prc.begin()); - if(parsed_it->is_err()) + auto parsed_obj(p_prc.next()); + if(parsed_obj.is_err()) + { + return parsed_obj.expect_err_move(); + } + else if(parsed_obj.expect_ok().is_none()) { - return parsed_it->expect_err_move(); + return err(error{ next_interp + interp_start.size(), "invalid native/raw interpolation" }); } - auto result( - analyze(parsed_it->expect_ok(), current_frame, expression_type::expression, fn_ctx, true)); + + /* We get back an AST expression and keep track of it as a chunk for later codegen. */ + auto result(analyze(parsed_obj.expect_ok().unwrap().ptr, + current_frame, + expression_type::expression, + fn_ctx, + true)); if(result.is_err()) { return result.expect_err_move(); } - if(next_start - it > 0) + /* C++ code before the next interpolation. */ + if(next_interp - it > 0) { chunks.emplace_back( - native_persistent_string_view{ code_str->data.data() + it, next_start - it }); + native_persistent_string_view{ code_str->data.data() + it, next_interp - it }); } chunks.emplace_back(result.expect_ok()); - it = next_end + interp_end.size(); - if(++parsed_it != p_prc.end()) + /* The next token needs to be a }, to match our original ~{. If it's not, either multiple + * forms were included in the interpolation or there is no closing }. We don't know for + * sure. */ + auto const next_token(*p_prc.token_current); + if(next_token.is_err()) + { + return next_token.expect_err(); + } + else if(next_token.expect_ok().kind != read::lex::token_kind::close_curly_bracket) { - return err(error{ "invalid native/raw: only one expression per interpolation" }); + return err(error{ + "invalid native/raw interpolation: ~{ must be followed by a single form and then a }" }); } + it = next_interp + interp_start.size() + next_token.expect_ok().pos + + next_token.expect_ok().size; } return make_box(expr::native_raw{ diff --git a/src/cpp/jank/codegen/processor.cpp b/src/cpp/jank/codegen/processor.cpp index 98d36036b..cfaecec50 100644 --- a/src/cpp/jank/codegen/processor.cpp +++ b/src/cpp/jank/codegen/processor.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include /* The strategy for codegen to C++ is quite simple. Codegen always happens on a @@ -197,7 +197,7 @@ namespace jank::codegen { fmt::format_to(inserter, "jank::make_box({})", - escaped(typed_o->data)); + util::escaped_quoted_view(typed_o->data)); } else if constexpr(std::same_as) { diff --git a/src/cpp/jank/read/lex.cpp b/src/cpp/jank/read/lex.cpp index 719241e1e..1623255ca 100644 --- a/src/cpp/jank/read/lex.cpp +++ b/src/cpp/jank/read/lex.cpp @@ -138,6 +138,14 @@ namespace jank::read { } + token::token(size_t const p, size_t const s, token_kind const k, char const * const d) + : pos{ p } + , size{ s } + , kind{ k } + , data{ native_persistent_string_view{ d } } + { + } + token::token(size_t const p, size_t const s, token_kind const k, native_bool const d) : pos{ p } , size{ s } diff --git a/src/cpp/jank/read/parse.cpp b/src/cpp/jank/read/parse.cpp index 2a9b7d523..77489dd82 100644 --- a/src/cpp/jank/read/parse.cpp +++ b/src/cpp/jank/read/parse.cpp @@ -12,58 +12,20 @@ #include #include #include +#include namespace jank::read::parse { - namespace detail + native_bool + processor::object_source_info::operator==(processor::object_source_info const &rhs) const { - string_result unescape(native_transient_string const &input) - { - native_transient_string ss; - ss.reserve(input.size()); - native_bool escape{}; - - for(auto const c : input) - { - if(!escape) - { - if(c == '\\') - { - escape = true; - } - else - { - ss += c; - } - } - else - { - switch(c) - { - case 'n': - ss += '\n'; - break; - case 't': - ss += '\t'; - break; - case 'r': - ss += '\r'; - break; - case '\\': - ss += '\\'; - break; - case '"': - ss += '"'; - break; - default: - return err(fmt::format("invalid escape sequence: \\{}", c)); - } - escape = false; - } - } + return !(*this != rhs); + } - return ok(ss); - } + native_bool + processor::object_source_info::operator!=(processor::object_source_info const &rhs) const + { + return ptr != rhs.ptr || start != rhs.start || end != rhs.end; } processor::iterator::value_type processor::iterator::operator*() const @@ -114,10 +76,9 @@ namespace jank::read::parse processor::object_result processor::next() { - /* TODO: Replace nullptr with none. */ if(token_current == token_end) { - return ok(nullptr); + return ok(none); } while(true) @@ -126,7 +87,10 @@ namespace jank::read::parse { auto const front(pending_forms.front()); pending_forms.pop_front(); - return ok(front); + /* NOTE: Source info for spliced forms will be incorrect. Right now, + * that doesn't matter. If it begins to matter, we need to store the + * source info along with the object in `pending_forms`. */ + return object_source_info{ front, latest_token, latest_token }; } auto token_result(*token_current); @@ -134,8 +98,8 @@ namespace jank::read::parse { return token_result.err().unwrap(); } - auto token(token_result.expect_ok()); - switch(token.kind) + latest_token = token_result.expect_ok(); + switch(latest_token.kind) { /* We ignore comments, but everything else returns out of the loop. */ case lex::token_kind::comment: @@ -151,14 +115,14 @@ namespace jank::read::parse case lex::token_kind::close_square_bracket: case lex::token_kind::close_paren: case lex::token_kind::close_curly_bracket: - if(expected_closer != token.kind) + if(expected_closer != latest_token.kind) { - return err( - error{ token.pos, native_persistent_string{ "unexpected closing character" } }); + return err(error{ latest_token.pos, + native_persistent_string{ "unexpected closing character" } }); } ++token_current; expected_closer = none; - return ok(nullptr); + return ok(none); case lex::token_kind::single_quote: return parse_quote(); case lex::token_kind::meta_hint: @@ -167,7 +131,7 @@ namespace jank::read::parse { auto res(parse_reader_macro()); /* Reader macros can skip some items entirely. */ - if(res.is_ok() && res.expect_ok() == nullptr) + if(res.is_ok() && res.expect_ok().is_none()) { continue; } @@ -176,7 +140,7 @@ namespace jank::read::parse case lex::token_kind::reader_macro_comment: { auto res(parse_reader_macro_comment()); - if(res.is_ok() && res.expect_ok() == nullptr) + if(res.is_ok() && res.expect_ok().is_none()) { continue; } @@ -185,7 +149,7 @@ namespace jank::read::parse case lex::token_kind::reader_macro_conditional: { auto res(parse_reader_macro_conditional(false)); - if(res.is_ok() && res.expect_ok() == nullptr) + if(res.is_ok() && res.expect_ok().is_none()) { continue; } @@ -194,7 +158,7 @@ namespace jank::read::parse case lex::token_kind::reader_macro_conditional_splice: { auto res(parse_reader_macro_conditional(true)); - if(res.is_ok() && res.expect_ok() == nullptr) + if(res.is_ok() && res.expect_ok().is_none()) { continue; } @@ -217,12 +181,12 @@ namespace jank::read::parse case lex::token_kind::escaped_string: return parse_escaped_string(); case lex::token_kind::eof: - return ok(nullptr); + return ok(none); default: { native_persistent_string msg{ fmt::format("unexpected token kind: {}", - magic_enum::enum_name(token.kind)) }; - return err(error{ token.pos, std::move(msg) }); + magic_enum::enum_name(latest_token.kind)) }; + return err(error{ latest_token.pos, std::move(msg) }); } } } @@ -230,7 +194,7 @@ namespace jank::read::parse processor::object_result processor::parse_list() { - auto const start_token(token_current.latest.unwrap().expect_ok()); + auto const start_token((*token_current).expect_ok()); ++token_current; auto const prev_expected_closer(expected_closer); expected_closer = some(lex::token_kind::close_paren); @@ -248,7 +212,7 @@ namespace jank::read::parse { return err(it.latest.unwrap().expect_err()); } - ret.push_back(it.latest.unwrap().expect_ok()); + ret.push_back(it.latest.unwrap().expect_ok().unwrap().ptr); } if(expected_closer.is_some()) { @@ -256,12 +220,16 @@ namespace jank::read::parse } expected_closer = prev_expected_closer; - return make_box(std::in_place, ret.rbegin(), ret.rend()); + return object_source_info{ + make_box(std::in_place, ret.rbegin(), ret.rend()), + start_token, + latest_token + }; } processor::object_result processor::parse_vector() { - auto const start_token(token_current.latest.unwrap().expect_ok()); + auto const start_token((*token_current).expect_ok()); ++token_current; auto const prev_expected_closer(expected_closer); expected_closer = some(lex::token_kind::close_square_bracket); @@ -279,7 +247,7 @@ namespace jank::read::parse { return err(it.latest.unwrap().expect_err()); } - ret.push_back(it.latest.unwrap().expect_ok()); + ret.push_back(it.latest.unwrap().expect_ok().unwrap().ptr); } if(expected_closer.is_some()) { @@ -287,13 +255,15 @@ namespace jank::read::parse } expected_closer = prev_expected_closer; - return make_box(ret.persistent()); + return object_source_info{ make_box(ret.persistent()), + start_token, + latest_token }; } /* TODO: Uniqueness check. */ processor::object_result processor::parse_map() { - auto const start_token(token_current.latest.unwrap().expect_ok()); + auto const start_token((*token_current).expect_ok()); ++token_current; auto const prev_expected_closer(expected_closer); expected_closer = some(lex::token_kind::close_curly_bracket); @@ -324,7 +294,7 @@ namespace jank::read::parse } auto const value(it.latest.unwrap().expect_ok()); - ret.insert_or_assign(key, value); + ret.insert_or_assign(key.unwrap().ptr, value.unwrap().ptr); } if(expected_closer.is_some()) { @@ -332,12 +302,14 @@ namespace jank::read::parse } expected_closer = prev_expected_closer; - return make_box(ret); + return object_source_info{ make_box(ret), + start_token, + latest_token }; } processor::object_result processor::parse_quote() { - auto const start_token(token_current.latest.unwrap().expect_ok()); + auto const start_token((*token_current).expect_ok()); ++token_current; auto const old_quoted(quoted); quoted = true; @@ -347,15 +319,17 @@ namespace jank::read::parse { return val_result; } - else if(val_result.expect_ok() == nullptr) + else if(val_result.expect_ok().is_none()) { return err(error{ start_token.pos, native_persistent_string{ "invalid value after quote" } }); } - return runtime::erase( - make_box(std::in_place, - make_box("quote"), - val_result.expect_ok_move())); + return object_source_info{ runtime::erase(make_box( + std::in_place, + make_box("quote"), + val_result.expect_ok().unwrap().ptr)), + start_token, + latest_token }; } processor::object_result processor::parse_meta_hint() @@ -367,7 +341,7 @@ namespace jank::read::parse { return meta_val_result; } - else if(meta_val_result.expect_ok() == nullptr) + else if(meta_val_result.expect_ok().is_none()) { return err( error{ start_token.pos, native_persistent_string{ "invalid meta value after meta hint" } }); @@ -378,15 +352,17 @@ namespace jank::read::parse using T = typename decltype(typed_val)::value_type; if constexpr(std::same_as) { - return runtime::obj::persistent_array_map::create_unique( - typed_val, - runtime::obj::boolean::true_const()); + return object_source_info{ runtime::obj::persistent_array_map::create_unique( + typed_val, + runtime::obj::boolean::true_const()), + start_token, + latest_token }; } /* TODO: Concept for map-like. */ if constexpr(std::same_as || std::same_as) { - return typed_val; + return object_source_info{ typed_val, start_token, latest_token }; } else { @@ -395,7 +371,7 @@ namespace jank::read::parse native_persistent_string{ "value after meta hint ^ must be a keyword or map" } }); } }, - meta_val_result.expect_ok())); + meta_val_result.expect_ok().unwrap().ptr)); if(meta_result.is_err()) { return meta_result; @@ -406,7 +382,7 @@ namespace jank::read::parse { return target_val_result; } - else if(target_val_result.expect_ok() == nullptr) + else if(target_val_result.expect_ok().is_none()) { return err(error{ start_token.pos, native_persistent_string{ "invalid target value after meta hint" } }); @@ -419,12 +395,17 @@ namespace jank::read::parse { if(typed_val->meta.is_none()) { - return typed_val->with_meta(meta_result.expect_ok()); + return object_source_info{ typed_val->with_meta(meta_result.expect_ok().unwrap().ptr), + start_token, + latest_token }; } else { - return typed_val->with_meta( - runtime::merge(typed_val->meta.unwrap(), meta_result.expect_ok())); + return object_source_info{ typed_val->with_meta( + runtime::merge(typed_val->meta.unwrap(), + meta_result.expect_ok().unwrap().ptr)), + start_token, + latest_token }; } } else @@ -434,7 +415,7 @@ namespace jank::read::parse native_persistent_string{ "target value for meta hint must accept metadata" } }); } }, - target_val_result.expect_ok()); + target_val_result.expect_ok().unwrap().ptr); } processor::object_result processor::parse_reader_macro() @@ -482,7 +463,7 @@ namespace jank::read::parse { return err(it.latest.unwrap().expect_err()); } - ret.insert(it.latest.unwrap().expect_ok()); + ret.insert(it.latest.unwrap().expect_ok().unwrap().ptr); } if(expected_closer.is_some()) { @@ -490,7 +471,9 @@ namespace jank::read::parse } expected_closer = prev_expected_closer; - return make_box(std::move(ret)); + return object_source_info{ make_box(std::move(ret)), + start_token, + latest_token }; } processor::object_result processor::parse_reader_macro_comment() @@ -503,13 +486,13 @@ namespace jank::read::parse { return ignored_result; } - else if(ignored_result.expect_ok() == nullptr) + else if(ignored_result.expect_ok().is_none()) { return err( error{ start_token.pos, native_persistent_string{ "value after #_ must be present" } }); } - return ok(nullptr); + return ok(none); } processor::object_result processor::parse_reader_macro_conditional(native_bool const splice) @@ -522,18 +505,20 @@ namespace jank::read::parse { return list_result; } - else if(list_result.expect_ok() == nullptr) + else if(list_result.expect_ok().is_none()) { return err( error{ start_token.pos, native_persistent_string{ "value after #? must be present" } }); } - else if(list_result.expect_ok()->type != runtime::object_type::persistent_list) + else if(list_result.expect_ok().unwrap().ptr->type != runtime::object_type::persistent_list) { return err( error{ start_token.pos, native_persistent_string{ "value after #? must be a list" } }); } - auto const list(runtime::expect_object(list_result.expect_ok())); + auto const list( + runtime::expect_object(list_result.expect_ok().unwrap().ptr)); + auto const list_end(list_result.expect_ok().unwrap().end); if(list.data->count() % 2 == 1) { @@ -566,7 +551,7 @@ namespace jank::read::parse auto const seq(typed_s->fresh_seq()); if(seq == nullptr) { - return ok(nullptr); + return ok(none); } auto const first(seq->first()); @@ -576,7 +561,7 @@ namespace jank::read::parse pending_forms.insert(front, it->first()); } - return ok(first); + return object_source_info{ first, start_token, list_end }; }, [=]() -> processor::object_result { return err(error{ start_token.pos, @@ -586,20 +571,20 @@ namespace jank::read::parse } else { - return ok(it->next_in_place()->first()); + return object_source_info{ it->next_in_place()->first(), start_token, list_end }; } } it = it->next_in_place()->next_in_place(); } - return ok(nullptr); + return ok(none); } processor::object_result processor::parse_nil() { ++token_current; - return ok(runtime::obj::nil::nil_const()); + return object_source_info{ runtime::obj::nil::nil_const(), latest_token, latest_token }; } processor::object_result processor::parse_boolean() @@ -607,7 +592,7 @@ namespace jank::read::parse auto const token((*token_current).expect_ok()); ++token_current; auto const b(boost::get(token.data)); - return ok(make_box(b)); + return object_source_info{ make_box(b), token, token }; } processor::object_result processor::parse_symbol() @@ -649,7 +634,7 @@ namespace jank::read::parse { name = sv; } - return ok(make_box(ns, name)); + return object_source_info{ make_box(ns, name), token, token }; } processor::object_result processor::parse_keyword() @@ -685,21 +670,26 @@ namespace jank::read::parse { return err(intern_res.expect_err()); } - return ok(intern_res.expect_ok()); + return object_source_info{ intern_res.expect_ok(), token, token }; } processor::object_result processor::parse_integer() { auto const token(token_current->expect_ok()); ++token_current; - return ok(make_box(boost::get(token.data))); + return object_source_info{ make_box( + boost::get(token.data)), + token, + token }; } processor::object_result processor::parse_real() { auto const token(token_current->expect_ok()); ++token_current; - return ok(make_box(boost::get(token.data))); + return object_source_info{ make_box(boost::get(token.data)), + token, + token }; } processor::object_result processor::parse_string() @@ -707,8 +697,10 @@ namespace jank::read::parse auto const token(token_current->expect_ok()); ++token_current; auto const sv(boost::get(token.data)); - return ok( - make_box(native_persistent_string{ sv.data(), sv.size() })); + return object_source_info{ make_box( + native_persistent_string{ sv.data(), sv.size() }), + token, + token }; } processor::object_result processor::parse_escaped_string() @@ -716,12 +708,14 @@ namespace jank::read::parse auto const token(token_current->expect_ok()); ++token_current; auto const sv(boost::get(token.data)); - auto res(detail::unescape({ sv.data(), sv.size() })); + auto res(util::unescape({ sv.data(), sv.size() })); if(res.is_err()) { return err(error{ token.pos, res.expect_err_move() }); } - return ok(make_box(res.expect_ok_move())); + return object_source_info{ make_box(res.expect_ok_move()), + token, + token }; } processor::iterator processor::begin() @@ -731,6 +725,6 @@ namespace jank::read::parse processor::iterator processor::end() { - return { some(ok(nullptr)), *this }; + return { some(ok(none)), *this }; } } diff --git a/src/cpp/jank/runtime/context.cpp b/src/cpp/jank/runtime/context.cpp index 4ecd91ed9..993354ca3 100644 --- a/src/cpp/jank/runtime/context.cpp +++ b/src/cpp/jank/runtime/context.cpp @@ -197,7 +197,8 @@ namespace jank::runtime native_vector exprs{}; for(auto const &form : p_prc) { - auto const expr(an_prc.analyze(form.expect_ok(), analyze::expression_type::statement)); + auto const expr( + an_prc.analyze(form.expect_ok().unwrap().ptr, analyze::expression_type::statement)); ret = evaluate::eval(*this, jit_prc, expr.expect_ok()); exprs.emplace_back(expr.expect_ok()); } @@ -230,7 +231,8 @@ namespace jank::runtime native_vector ret{}; for(auto const &form : p_prc) { - auto const expr(an_prc.analyze(form.expect_ok(), analyze::expression_type::statement)); + auto const expr( + an_prc.analyze(form.expect_ok().unwrap().ptr, analyze::expression_type::statement)); if(eval) { evaluate::eval(*this, jit_prc, expr.expect_ok()); diff --git a/src/cpp/jank/util/escape.cpp b/src/cpp/jank/util/escape.cpp new file mode 100644 index 000000000..825baedc6 --- /dev/null +++ b/src/cpp/jank/util/escape.cpp @@ -0,0 +1,92 @@ +#include + +namespace jank::util +{ + string_result unescape(native_transient_string const &input) + { + native_transient_string ss; + ss.reserve(input.size()); + native_bool escape{}; + + for(auto const c : input) + { + if(!escape) + { + if(c == '\\') + { + escape = true; + } + else + { + ss += c; + } + } + else + { + switch(c) + { + case 'n': + ss += '\n'; + break; + case 't': + ss += '\t'; + break; + case 'r': + ss += '\r'; + break; + case '\\': + ss += '\\'; + break; + case '"': + ss += '"'; + break; + default: + return err(fmt::format("invalid escape sequence: \\{}", c)); + } + escape = false; + } + } + + return ok(std::move(ss)); + } + + native_transient_string escape(native_transient_string const &input) + { + native_transient_string ss; + /* We can expect on relocation, since escaping anything will result in a larger string. + * I'm not going to guess at the stats, to predict a better allocation, until this shows + * up in the profiler, though. */ + ss.reserve(input.size()); + + for(auto const c : input) + { + switch(c) + { + case '\n': + ss += '\\'; + ss += 'n'; + break; + case '\t': + ss += '\\'; + ss += 't'; + break; + case '\r': + ss += '\\'; + ss += 'r'; + break; + case '\\': + ss += '\\'; + ss += '\\'; + break; + case '"': + ss += '\\'; + ss += '"'; + break; + default: + ss += c; + } + } + + return ss; + } +} diff --git a/src/jank/clojure/core.jank b/src/jank/clojure/core.jank index d0ac579f5..3dd918560 100644 --- a/src/jank/clojure/core.jank +++ b/src/jank/clojure/core.jank @@ -5,19 +5,19 @@ ; Exceptions (def ex-info - (fn* [msg map] + (fn* ex-info [msg map] {:error msg :data map})) ; Relations. ;; Miscellaneous. (def nil? - (fn* [o] - (native/raw "__value = (o == obj::nil::nil_const()) ? #{ true }# : #{ false }#"))) + (fn* nil? [o] + (native/raw "__value = (o == obj::nil::nil_const()) ? ~{ true } : ~{ false }"))) ;; Collections. (def empty? - (fn* [o] + (fn* empty? [o] (if (nil? o) true (native/raw "__value = visit_object @@ -29,16 +29,16 @@ if constexpr(behavior::seqable) { return make_box(typed_o->seq() == nullptr); } else - { throw #{ (ex-info :not-seqable {:o o}) }#; } - }, - #{ o }# + { throw ~{ (ex-info :not-seqable {:o o}) }; } + }, + ~{ o } );")))) (def count - (fn* [o] + (fn* count [o] (if (nil? o) 0 - (native/raw "__value = make_box(runtime::detail::sequence_length(#{ o }#));")))) + (native/raw "__value = make_box(runtime::detail::sequence_length(~{ o }));")))) ; Lists. ; TODO: Can these be removed from the C++ source? @@ -61,9 +61,9 @@ ; return ret; ; } ; else -; { throw #{ (ex-info :not-seqable {:o o}) }#; } +; { throw ~{ (ex-info :not-seqable {:o o}) }; } ; }, -; #{ o }# +; ~{ o } ; );"))) ;(def fresh-seq ; (fn* fresh-seq [o] @@ -84,20 +84,20 @@ ; return ret; ; } ; else -; { throw #{ (ex-info :not-seqable {:o o}) }#; } +; { throw ~{ (ex-info :not-seqable {:o o}) }; } ; }, -; #{ o }# +; ~{ o } ; );"))) (def first (fn* first [o] - (native/raw "__value = jank::runtime::first(#{ o }#);"))) + (native/raw "__value = jank::runtime::first(~{ o });"))) ; Returns a fresh seq. (def next (fn* next [o] - (native/raw "__value = jank::runtime::next(#{ o }#);"))) + (native/raw "__value = jank::runtime::next(~{ o });"))) (def next-in-place (fn* next-in-place [o] - (native/raw "__value = jank::runtime::next_in_place(#{ o }#);"))) + (native/raw "__value = jank::runtime::next_in_place(~{ o });"))) (def nnext (fn* nnext [o] (next-in-place (next o)))) @@ -118,16 +118,16 @@ { auto const seq(typed_o->seq()); if(!seq) - { return #{ '() }#; } + { return ~{ '() }; } auto const ret(seq->next()); if(ret == nullptr) - { return #{ '() }#; } + { return ~{ '() }; } return ret; } else - { throw #{ (ex-info :not-seqable {:o o}) }#; } + { throw ~{ (ex-info :not-seqable {:o o}) }; } }, - #{ o }# + ~{ o } );")))) (def cons (fn* cons [head tail] @@ -138,17 +138,17 @@ using T = typename decltype(typed_tail)::value_type; if constexpr(behavior::seqable) - { return make_box(#{ head }#, typed_tail->seq()); } + { return make_box(~{ head }, typed_tail->seq()); } else - { throw #{ (ex-info :invalid-cons-tail {:head head :tail tail}) }#; } + { throw ~{ (ex-info :invalid-cons-tail {:head head :tail tail}) }; } }, - #{ tail }# + ~{ tail } );"))) (def list? (fn* list? [o] ; TODO: Visit and use a behavior for this check instead. It should apply to conses and others. - (native/raw "__value = make_box(#{ o }#->type == object_type::persistent_list);"))) + (native/raw "__value = make_box(~{ o }->type == object_type::persistent_list);"))) (def list (fn* list [& items] @@ -161,15 +161,15 @@ if constexpr(behavior::seqable) { return jank::runtime::obj::persistent_list::create(typed_items); } else - { throw #{ (ex-info :invalid-seq {:items items}) }#; } + { throw ~{ (ex-info :invalid-seq {:items items}) }; } }, - #{ items }# + ~{ items } );"))) ;; Vectors. (def vector? (fn* vector? [o] - (native/raw "__value = make_box(#{ o }#->type == object_type::persistent_vector);"))) + (native/raw "__value = make_box(~{ o }->type == object_type::persistent_vector);"))) (def vec (fn* vec [coll] @@ -182,9 +182,9 @@ if constexpr(behavior::seqable) { return jank::runtime::obj::persistent_vector::create(typed_coll); } else - { throw #{ (ex-info :invalid-seq {:coll coll}) }#; } + { throw ~{ (ex-info :invalid-seq {:coll coll}) }; } }, - #{ coll }# + ~{ coll } );"))) (def conj) @@ -195,7 +195,7 @@ ([coll] coll) ([coll x] - (native/raw "__value = jank::runtime::conj(#{ coll }#, #{ x }#);")) + (native/raw "__value = jank::runtime::conj(~{ coll }, ~{ x });")) ([coll x & args] (let* [res (conj coll x)] (if (empty? args) @@ -209,7 +209,7 @@ ;; Metadata (def meta (fn* meta [o] - (native/raw "__value = runtime::meta(#{ o }#);"))) + (native/raw "__value = runtime::meta(~{ o });"))) (def with-meta (fn* with-meta [o m] (native/raw "__value = visit_object @@ -219,11 +219,11 @@ using T = typename decltype(typed_o)::value_type; if constexpr(behavior::metadatable) - { return typed_o->with_meta(#{ m }#); } + { return typed_o->with_meta(~{ m }); } else - { throw #{ (ex-info :not-metadatable {:o o}) }#; } + { throw ~{ (ex-info :not-metadatable {:o o}) }; } }, - #{ o }# + ~{ o } );"))) (def reset-meta! (fn* reset-meta! [o m] @@ -235,14 +235,14 @@ if constexpr(behavior::metadatable) { - auto const meta(behavior::detail::validate_meta(#{ m }#)); + auto const meta(behavior::detail::validate_meta(~{ m })); typed_o->meta = meta; - return #{ m }#; + return ~{ m }; } else - { throw #{ (ex-info :not-metadatable {:o o}) }#; } + { throw ~{ (ex-info :not-metadatable {:o o}) }; } }, - #{ o }# + ~{ o } );"))) ;; Macros. @@ -261,7 +261,7 @@ ([] (native/raw "__value = make_box(runtime::context::unique_symbol());")) ([prefix] - (native/raw "__value = make_box(runtime::context::unique_symbol(runtime::detail::to_string(#{ prefix }#)));"))) + (native/raw "__value = make_box(runtime::context::unique_symbol(runtime::detail::to_string(~{ prefix })));"))) ; TODO: This doesn't support macros with overloads. (defn defmacro [&form &env fn-name fn-args & body] @@ -291,7 +291,7 @@ ; TODO: Higher arities. Needs clojure.core/spread, which needs clojure.core/cons. (defn apply* [f args] - (native/raw "__value = runtime::apply_to(#{ f }#, #{ args }#);")) + (native/raw "__value = runtime::apply_to(~{ f }, ~{ args });")) (defn or* ([] @@ -362,23 +362,23 @@ [& body]) (defn macroexpand-1 [form] - (native/raw "__value = __rt_ctx.macroexpand1(#{ form }#);")) + (native/raw "__value = __rt_ctx.macroexpand1(~{ form });")) (defn macroexpand [form] - (native/raw "__value = __rt_ctx.macroexpand(#{ form }#);")) + (native/raw "__value = __rt_ctx.macroexpand(~{ form });")) ; Relations. ;; Miscellaneous. (defn true? [o] - (native/raw "__value = runtime::detail::equal(#{ o }#, #{ true }#) ? #{ true }# : #{ false }#")) + (native/raw "__value = runtime::detail::equal(~{ o }, ~{ true }) ? ~{ true } : ~{ false }")) (defn false? [o] - (native/raw "__value = runtime::detail::equal(#{ o }#, #{ false }#) ? #{ true }# : #{ false }#")) + (native/raw "__value = runtime::detail::equal(~{ o }, ~{ false }) ? ~{ true } : ~{ false }")) (defn not [o] (native/raw "if(o == obj::nil::nil_const()) - { __value = #{ true }#; } + { __value = ~{ true }; } else - { __value = runtime::detail::equal(#{ o }#, #{ false }#) ? #{ true }# : #{ false }#; }")) + { __value = runtime::detail::equal(~{ o }, ~{ false }) ? ~{ true } : ~{ false }; }")) (defn some? [o] - (native/raw "__value = (o == obj::nil::nil_const()) ? #{ false }# : #{ true }#")) + (native/raw "__value = (o == obj::nil::nil_const()) ? ~{ false } : ~{ true }")) ; Functions. @@ -405,15 +405,15 @@ ; Applies fn f to the argument list formed by prepending intervening arguments to args. (defn apply ([f args] - (native/raw "__value = runtime::apply_to(#{ f }#, #{ args }#);")) + (native/raw "__value = runtime::apply_to(~{ f }, ~{ args });")) ([f x args] - (native/raw "__value = runtime::apply_to(#{ f }#, #{ (list* x args) }#);")) + (native/raw "__value = runtime::apply_to(~{ f }, ~{ (list* x args) });")) ([f x y args] - (native/raw "__value = runtime::apply_to(#{ f }#, #{ (list* x y args) }#);")) + (native/raw "__value = runtime::apply_to(~{ f }, ~{ (list* x y args) });")) ([f x y z args] - (native/raw "__value = runtime::apply_to(#{ f }#, #{ (list* x y z args) }#);")) + (native/raw "__value = runtime::apply_to(~{ f }, ~{ (list* x y z args) });")) ([f a b c d & args] - (native/raw "__value = runtime::apply_to(#{ f }#, #{ (cons a (cons b (cons c (cons d (spread args))))) }#);"))) + (native/raw "__value = runtime::apply_to(~{ f }, ~{ (cons a (cons b (cons c (cons d (spread args))))) });"))) ; Takes a fn f and returns a fn that takes the same arguments as f, ; has the same effects, if any, and returns the opposite truth value. @@ -440,25 +440,25 @@ if constexpr(behavior::seqable) { - object_ptr res{ #{ val }# }; + object_ptr res{ ~{ val } }; for(auto it(typed_coll->fresh_seq()); it != nullptr; it = it->next_in_place()) - { res = dynamic_call(#{ f }#, res, it->first()); } + { res = dynamic_call(~{ f }, res, it->first()); } return res; } else - { throw #{ (ex-info :invalid-seq {:coll coll}) }#; } + { throw ~{ (ex-info :invalid-seq {:coll coll}) }; } }, - #{ coll }# + ~{ coll } );"))) ; Strings. (defn string? [o] - (native/raw "__value = make_box(#{ o }#->type == object_type::persistent_string);")) + (native/raw "__value = make_box(~{ o }->type == object_type::persistent_string);")) (defn str ([] "") ([o] - (native/raw "__value = make_box(runtime::detail::to_string(#{ o }#));")) + (native/raw "__value = make_box(runtime::detail::to_string(~{ o }));")) ([o & args] (native/raw "__value = visit_object ( @@ -470,7 +470,7 @@ { fmt::memory_buffer buff; buff.reserve(16); - runtime::detail::to_string(#{ o }#, buff); + runtime::detail::to_string(~{ o }, buff); if(0 < runtime::detail::sequence_length(typed_args)) { auto const fresh(typed_args->fresh_seq()); @@ -481,9 +481,9 @@ return make_box(native_persistent_string{ buff.data(), buff.size() }); } else - { throw #{ (ex-info :invalid-seq {:args args}) }#; } + { throw ~{ (ex-info :invalid-seq {:args args}) }; } }, - #{ args }# + ~{ args } );"))) ; TODO: Proper version. @@ -498,11 +498,11 @@ using T = typename decltype(typed_s)::value_type; if constexpr(std::is_same_v) - { return typed_s->substring(to_int(#{ start }#)).expect_ok(); } + { return typed_s->substring(to_int(~{ start })).expect_ok(); } else - { throw #{ (ex-info :not-a-string {:s s}) }#; } + { throw ~{ (ex-info :not-a-string {:s s}) }; } }, - #{ s }# + ~{ s } );")) ([s start end] (native/raw "__value = visit_object @@ -512,11 +512,11 @@ using T = typename decltype(typed_s)::value_type; if constexpr(std::is_same_v) - { return typed_s->substring(to_int(#{ start }#), to_int(#{ end }#)).expect_ok(); } + { return typed_s->substring(to_int(~{ start }), to_int(~{ end })).expect_ok(); } else - { throw #{ (ex-info :not-a-string {:s s}) }#; } + { throw ~{ (ex-info :not-a-string {:s s}) }; } }, - #{ s }# + ~{ s } );"))) (defn- first-index-of [s c] @@ -529,11 +529,11 @@ using T = typename decltype(typed_s)::value_type; if constexpr(std::is_same_v) - { return typed_s->first_index_of(#{ c }#); } + { return typed_s->first_index_of(~{ c }); } else - { throw #{ (ex-info :not-a-string {:s s}) }#; } + { throw ~{ (ex-info :not-a-string {:s s}) }; } }, - #{ s }# + ~{ s } ) );")) (defn- last-index-of [s c] @@ -546,11 +546,11 @@ using T = typename decltype(typed_s)::value_type; if constexpr(std::is_same_v) - { return typed_s->last_index_of(#{ c }#); } + { return typed_s->last_index_of(~{ c }); } else - { throw #{ (ex-info :not-a-string {:s s}) }#; } + { throw ~{ (ex-info :not-a-string {:s s}) }; } }, - #{ s }# + ~{ s } ) );")) @@ -569,7 +569,7 @@ else { return false; } }, - #{ o }# + ~{ o } ) );")) @@ -658,7 +658,7 @@ ([] {}) ([& kvs] - (native/raw "__value = obj::persistent_hash_map::create_from_seq(#{ kvs }#);"))) + (native/raw "__value = obj::persistent_hash_map::create_from_seq(~{ kvs });"))) (defn keys [m] ; TODO: Use a proper key seq instead. @@ -676,18 +676,18 @@ (defn get ([m k] - (native/raw "__value = jank::runtime::get(#{ m }#, #{ k }#);")) + (native/raw "__value = jank::runtime::get(~{ m }, ~{ k });")) ([m k fallback] - (native/raw "__value = jank::runtime::get(#{ m }#, #{ k }#, #{ fallback }#);"))) + (native/raw "__value = jank::runtime::get(~{ m }, ~{ k }, ~{ fallback });"))) (defn get-in ([m ks] - (native/raw "__value = jank::runtime::get_in(#{ m }#, #{ ks }#);")) + (native/raw "__value = jank::runtime::get_in(~{ m }, ~{ ks });")) ([m ks fallback] - (native/raw "__value = jank::runtime::get_in(#{ m }#, #{ ks }#, #{ fallback }#);"))) + (native/raw "__value = jank::runtime::get_in(~{ m }, ~{ ks }, ~{ fallback });"))) (defn assoc ([map key val] - (native/raw "__value = jank::runtime::assoc(#{ map }#, #{ key }#, #{ val }#);")) + (native/raw "__value = jank::runtime::assoc(~{ map }, ~{ key }, ~{ val });")) ([map key val & kvs] (let [res (assoc map key val)] (if (empty? kvs) @@ -702,16 +702,16 @@ ; range of indexes. 'contains?' operates constant or logarithmic time; ; it will not perform a linear search for a value. See also 'some'. (defn contains? [coll k] - (native/raw "__value = make_box(jank::runtime::contains(#{ coll }#, #{ k }#));")) + (native/raw "__value = make_box(jank::runtime::contains(~{ coll }, ~{ k }));")) ; Returns the map entry for key, or nil if key not present. (defn find [coll k] - (native/raw "__value = jank::runtime::find(#{ coll }#, #{ k }#);")) + (native/raw "__value = jank::runtime::find(~{ coll }, ~{ k });")) ; Returns a map containing only those entries in map whose key is in keys (defn select-keys [m ks] (reduce* (fn [acc k] - (let [e (native/raw "__value = jank::runtime::find(#{ m }#, #{ k }#);")] + (let [e (native/raw "__value = jank::runtime::find(~{ m }, ~{ k });")] (if e (conj acc e) acc))) @@ -730,7 +730,7 @@ ;; Sets. (defn set? [o] - (native/raw "__value = make_box(#{ o }#->type == object_type::persistent_set);")) + (native/raw "__value = make_box(~{ o }->type == object_type::persistent_set);")) ; Returns a set of the distinct elements of coll. (defn set [coll] @@ -744,7 +744,7 @@ ;; Other. (defn hash [o] - (native/raw "__value = make_box(hash::visit(#{ o }#));")) + (native/raw "__value = make_box(hash::visit(~{ o }));")) (defn name [o] (if (string? o) @@ -760,9 +760,9 @@ if constexpr(behavior::nameable) { return typed_o->get_name(); } else - { throw #{ (ex-info :not-nameable {:o o}) }#; } + { throw ~{ (ex-info :not-nameable {:o o}) }; } }, - #{ o }# + ~{ o } ) );"))) @@ -778,9 +778,9 @@ if constexpr(behavior::nameable) { return typed_o->get_namespace(); } else - { throw #{ (ex-info :not-nameable {:o o}) }#; } + { throw ~{ (ex-info :not-nameable {:o o}) }; } }, - #{ o }# + ~{ o } ) );")) @@ -792,9 +792,9 @@ ([x] x) ([l r] - (native/raw "__value = add(#{ l }#, #{ r }#);")) + (native/raw "__value = add(~{ l }, ~{ r });")) ([l r & args] - (let [res (native/raw "__value = add(#{ l }#, #{ r }#);")] + (let [res (native/raw "__value = add(~{ l }, ~{ r });")] (if (empty? args) res (recur res (first args) (next args)))))) @@ -805,9 +805,9 @@ ([x] (- 0 x)) ([l r] - (native/raw "__value = sub(#{ l }#, #{ r }#);")) + (native/raw "__value = sub(~{ l }, ~{ r });")) ([l r & args] - (let [res (native/raw "__value = sub(#{ l }#, #{ r }#);")] + (let [res (native/raw "__value = sub(~{ l }, ~{ r });")] (if (empty? args) res (recur res (first args) (next args)))))) @@ -820,9 +820,9 @@ ([x] x) ([l r] - (native/raw "__value = mul(#{ l }#, #{ r }#);")) + (native/raw "__value = mul(~{ l }, ~{ r });")) ([l r & args] - (let [res (native/raw "__value = mul(#{ l }#, #{ r }#);")] + (let [res (native/raw "__value = mul(~{ l }, ~{ r });")] (if (empty? args) res (recur res (first args) (next args)))))) @@ -838,9 +838,9 @@ ([x] (/ 1 x)) ([l r] - (native/raw "__value = div(#{ l }#, #{ r }#);")) + (native/raw "__value = div(~{ l }, ~{ r });")) ([l r & args] - (let [res (native/raw "__value = div(#{ l }#, #{ r }#);")] + (let [res (native/raw "__value = div(~{ l }, ~{ r });")] (if (empty? args) res (recur res (first args) (next args))))))) @@ -851,12 +851,12 @@ ([x] true) ([l r] - (native/raw "__value = make_box(runtime::detail::equal(#{ l }#, #{ r }#));")) + (native/raw "__value = make_box(runtime::detail::equal(~{ l }, ~{ r }));")) ([l r & args] - (if (native/raw "__value = make_box(runtime::detail::equal(#{ l }#, #{ r }#));") + (if (native/raw "__value = make_box(runtime::detail::equal(~{ l }, ~{ r }));") (if (next args) (recur r (first args) (next args)) - (native/raw "__value = make_box(runtime::detail::equal(#{ r }#, #{ (first args) }#));")) + (native/raw "__value = make_box(runtime::detail::equal(~{ r }, ~{ (first args) }));")) false))) (defn not= @@ -871,12 +871,12 @@ ([x] true) ([l r] - (native/raw "__value = make_box(lt(#{ l }#, #{ r }#));")) + (native/raw "__value = make_box(lt(~{ l }, ~{ r }));")) ([l r & args] - (if (native/raw "__value = make_box(lt(#{ l }#, #{ r }#));") + (if (native/raw "__value = make_box(lt(~{ l }, ~{ r }));") (if (next args) (recur r (first args) (next args)) - (native/raw "__value = make_box(lt(#{ r }#, #{ (first args) }#));")) + (native/raw "__value = make_box(lt(~{ r }, ~{ (first args) }));")) false))) (reset-meta! (var <) {:arities {2 {:supports-unboxed-input? true :unboxed-output? true}}}) @@ -885,12 +885,12 @@ ([x] true) ([l r] - (native/raw "__value = make_box(lte(#{ l }#, #{ r }#));")) + (native/raw "__value = make_box(lte(~{ l }, ~{ r }));")) ([l r & args] - (if (native/raw "__value = make_box(lte(#{ l }#, #{ r }#));") + (if (native/raw "__value = make_box(lte(~{ l }, ~{ r }));") (if (next args) (recur r (first args) (next args)) - (native/raw "__value = make_box(lte(#{ r }#, #{ (first args) }#));")) + (native/raw "__value = make_box(lte(~{ r }, ~{ (first args) }));")) false))) (reset-meta! (var <=) {:arities {2 {:supports-unboxed-input? true :unboxed-output? true}}}) @@ -899,12 +899,12 @@ ([x] true) ([l r] - (native/raw "__value = make_box(lt(#{ r }#, #{ l }#));")) + (native/raw "__value = make_box(lt(~{ r }, ~{ l }));")) ([l r & args] - (if (native/raw "__value = make_box(lt(#{ r }#, #{ l }#));") + (if (native/raw "__value = make_box(lt(~{ r }, ~{ l }));") (if (next args) (recur r (first args) (next args)) - (native/raw "__value = make_box(lt(#{ (first args) }#, #{ r }#));")) + (native/raw "__value = make_box(lt(~{ (first args) }, ~{ r }));")) false))) (reset-meta! (var >) {:arities {2 {:supports-unboxed-input? true :unboxed-output? true}}}) @@ -913,12 +913,12 @@ ([x] true) ([l r] - (native/raw "__value = make_box(lte(#{ r }#, #{ l }#));")) + (native/raw "__value = make_box(lte(~{ r }, ~{ l }));")) ([l r & args] - (if (native/raw "__value = make_box(lte(#{ r }#, #{ l }#));") + (if (native/raw "__value = make_box(lte(~{ r }, ~{ l }));") (if (next args) (recur r (first args) (next args)) - (native/raw "__value = make_box(lte(#{ (first args) }#, #{ r }#));")) + (native/raw "__value = make_box(lte(~{ (first args) }, ~{ r }));")) false))) (reset-meta! (var >=) {:arities {2 {:supports-unboxed-input? true :unboxed-output? true}}}) @@ -927,9 +927,9 @@ ([x] x) ([l r] - (native/raw "__value = min(#{ l }#, #{ r }#);")) + (native/raw "__value = min(~{ l }, ~{ r });")) ([l r & args] - (let [res (native/raw "__value = min(#{ l }#, #{ r }#);")] + (let [res (native/raw "__value = min(~{ l }, ~{ r });")] (if (empty? args) res (recur res (first args) (next args)))))) @@ -940,9 +940,9 @@ ([x] x) ([l r] - (native/raw "__value = max(#{ l }#, #{ r }#);")) + (native/raw "__value = max(~{ l }, ~{ r });")) ([l r & args] - (let [res (native/raw "__value = max(#{ l }#, #{ r }#);")] + (let [res (native/raw "__value = max(~{ l }, ~{ r });")] (if (empty? args) res (recur res (first args) (next args)))))) @@ -950,19 +950,19 @@ :unboxed-output? true}}}) (defn inc [n] - (native/raw "__value = inc(#{ n }#);")) + (native/raw "__value = inc(~{ n });")) (defn dec [n] - (native/raw "__value = dec(#{ n }#);")) + (native/raw "__value = dec(~{ n });")) (defn pos? [n] - (native/raw "__value = make_box(is_pos(#{ n }#));")) + (native/raw "__value = make_box(is_pos(~{ n }));")) (defn neg? [n] - (native/raw "__value = make_box(is_neg(#{ n }#));")) + (native/raw "__value = make_box(is_neg(~{ n }));")) (defn zero? [n] - (native/raw "__value = make_box(is_zero(#{ n }#));")) + (native/raw "__value = make_box(is_zero(~{ n }));")) (defn rem [num div] - (native/raw "__value = rem(#{ num }#, #{ div }#);")) + (native/raw "__value = rem(~{ num }, ~{ div });")) (defn mod [num div] (let [m (rem num div)] ; TODO: Could use an or here, if we had it. @@ -974,11 +974,11 @@ ;; Numbers. (defn integer? [o] - (native/raw "__value = make_box(#{ o }#->type == object_type::integer)")) + (native/raw "__value = make_box(~{ o }->type == object_type::integer)")) (defn float? [o] - (native/raw "__value = make_box(#{ o }#->type == object_type::real)")) + (native/raw "__value = make_box(~{ o }->type == object_type::real)")) (defn boolean? [o] - (native/raw "__value = make_box(#{ o }#->type == object_type::boolean)")) + (native/raw "__value = make_box(~{ o }->type == object_type::boolean)")) (defn number? [o] (native/raw "__value = make_box ( @@ -993,12 +993,12 @@ else { return false; } }, - #{ o }# + ~{ o } ) );")) (defn int [o] - (native/raw "__value = make_box(to_int(#{ o }#));")) + (native/raw "__value = make_box(to_int(~{ o }));")) (defn float [o] (native/raw "__value = make_box ( @@ -1011,9 +1011,9 @@ if constexpr(behavior::numberable) { return typed_o->to_real(); } else - { throw #{ (ex-info :not-a-number {:o o}) }#; } + { throw ~{ (ex-info :not-a-number {:o o}) }; } }, - #{ o }# + ~{ o } ) );")) @@ -1029,9 +1029,9 @@ if constexpr(std::is_same_v) { return typed_o->data % 2 == 0; } else - { throw #{ (ex-info :not-an-integer {:o o}) }#; } + { throw ~{ (ex-info :not-an-integer {:o o}) }; } }, - #{ o }# + ~{ o } ) );")) (defn odd? [o] @@ -1046,9 +1046,9 @@ if constexpr(std::is_same_v) { return typed_o->data % 2 == 1; } else - { throw #{ (ex-info :not-an-integer {:o o}) }#; } + { throw ~{ (ex-info :not-an-integer {:o o}) }; } }, - #{ o }# + ~{ o } ) );")) @@ -1093,7 +1093,7 @@ ;; Vars. (defn var? [o] - (native/raw "__value = make_box(#{ o }#->type == object_type::var)")) + (native/raw "__value = make_box(~{ o }->type == object_type::var)")) ; TODO: Make this private. (defmacro assert-macro-args @@ -1128,7 +1128,7 @@ ; (finally ; (pop-thread-bindings))) (defn push-thread-bindings [bindings] - (native/raw "__rt_ctx.push_thread_bindings(#{ bindings }#).expect_ok();")) + (native/raw "__rt_ctx.push_thread_bindings(~{ bindings }).expect_ok();")) ; Pop one set of bindings pushed with push-binding before. It is an error to ; pop bindings without pushing before. @@ -1174,10 +1174,10 @@ (defn with-redefs-fn [binding-map fun] (let [root-bind (fn [m] (doseq [kv m] - (native/raw "expect_object(#{ (first kv) }#)->bind_root(#{ (second kv) }#);"))) + (native/raw "expect_object(~{ (first kv) })->bind_root(~{ (second kv) });"))) old-vals (zipmap (keys binding-map) (map (fn [v] - (native/raw "__value = expect_object(#{ v }#)->get_root();")) + (native/raw "__value = expect_object(~{ v })->get_root();")) (keys binding-map)))] ; TODO: try/finally (do ;try @@ -1205,7 +1205,7 @@ ;; Keywords. (defn keyword? [o] - (native/raw "__value = make_box(#{ o }#->type == object_type::keyword)")) + (native/raw "__value = make_box(~{ o }->type == object_type::keyword)")) (defn simple-keyword? [o] (native/raw "__value = make_box ( @@ -1220,7 +1220,7 @@ else { return false; } }, - #{ o }# + ~{ o } ) );")) (defn qualified-keyword? [o] @@ -1237,7 +1237,7 @@ else { return false; } }, - #{ o }# + ~{ o } ) );")) (defn simple-symbol? [o] @@ -1254,7 +1254,7 @@ else { return false; } }, - #{ o }# + ~{ o } ) );")) (defn qualified-symbol? [o] @@ -1271,11 +1271,11 @@ else { return false; } }, - #{ o }# + ~{ o } ) );")) (defn symbol? [o] - (native/raw "__value = make_box(#{ o }#->type == object_type::symbol)")) + (native/raw "__value = make_box(~{ o }->type == object_type::symbol)")) ; Returns a Symbol with the given namespace and name. Arity-1 works ; on strings, keywords, and vars. @@ -1284,12 +1284,12 @@ (cond (symbol? o) o ; TODO: Intern. - (string? o) (native/raw "__value = make_box(runtime::detail::to_string(#{ o }#));") - (var? o) (native/raw "__value = expect_object(#{ o }#)->name;") - (keyword? o) (native/raw "__value = make_box(expect_object(#{ o }#)->sym) ;") - :else (native/raw "throw #{ (ex-info :cannot-convert-to-symbol {:o o}) }#;"))) + (string? o) (native/raw "__value = make_box(runtime::detail::to_string(~{ o }));") + (var? o) (native/raw "__value = expect_object(~{ o })->name;") + (keyword? o) (native/raw "__value = make_box(expect_object(~{ o })->sym) ;") + :else (native/raw "throw ~{ (ex-info :cannot-convert-to-symbol {:o o}) };"))) ([ns o] - (native/raw "__value = make_box(runtime::detail::to_string(#{ ns }#), runtime::detail::to_string(#{ o }#));"))) + (native/raw "__value = make_box(runtime::detail::to_string(~{ ns }), runtime::detail::to_string(~{ o }));"))) ;; Sequences. (defn iterate [f x] @@ -1300,22 +1300,22 @@ using T = typename decltype(typed_f)::value_type; if constexpr(std::is_base_of_v) - { return make_box(typed_f, #{ x }#); } + { return make_box(typed_f, ~{ x }); } else - { throw #{ (ex-info :not-callable {:f f}) }#; } + { throw ~{ (ex-info :not-callable {:f f}) }; } }, - #{ f }# + ~{ f } );")) (defn range ([] (iterate inc 0)) ([end] - (native/raw "__value = make_box(#{ end }#);")) + (native/raw "__value = make_box(~{ end });")) ([start end] - (native/raw "__value = make_box(#{ start }#, #{ end }#);")) + (native/raw "__value = make_box(~{ start }, ~{ end });")) ([start end step] - (native/raw "__value = make_box(#{ start }#, #{ end }#, #{ step }#);"))) + (native/raw "__value = make_box(~{ start }, ~{ end }, ~{ step });"))) (defn take ; TODO: Transducer. @@ -1377,10 +1377,10 @@ ; TODO: Move println back into here once I sort out two things: ; 1. Escaped strings, so I can do the space in between each ; 2. The linker errors showing up when using fmt::format_to here - (native/raw "__value = jank::runtime::context::println(#{ args }#);")) + (native/raw "__value = jank::runtime::context::println(~{ args });")) (defn print [o] - (native/raw "__value = jank::runtime::context::print(#{ o }#);")) + (native/raw "__value = jank::runtime::context::print(~{ o });")) ;; Functions. (defn ifn? [o] @@ -1397,14 +1397,14 @@ else { return false; } }, - #{ o }# + ~{ o } ) );")) (defn fn? [o] (native/raw "auto const fn ( - #{ o }#->type == object_type::native_function_wrapper - || #{ o }#->type == object_type::jit_function + ~{ o }->type == object_type::native_function_wrapper + || ~{ o }->type == object_type::jit_function ); __value = make_box(fn);")) @@ -1415,7 +1415,7 @@ ;; jank.compiler things. ; TODO: Options, following what criterium offers. (defn benchmark [label fun] - (native/raw "auto const label_str(expect_object(#{ (str label) }#)); + (native/raw "auto const label_str(expect_object(~{ (str label) })); visit_object ( [=](auto const typed_fun) @@ -1449,27 +1449,27 @@ ); } else - { throw #{ (ex-info :not-callable {:fun fun}) }#; } + { throw ~{ (ex-info :not-callable {:fun fun}) }; } }, - #{ fun }# + ~{ fun } );")) ;; Extra things that usually come from Java. (defn tan [o] - (native/raw "__value = make_box(std::tan(runtime::detail::to_real(#{ o }#)));")) + (native/raw "__value = make_box(std::tan(runtime::detail::to_real(~{ o })));")) (defn sqrt [o] - (native/raw "__value = make_box(std::sqrt(runtime::detail::to_real(#{ o }#)));")) + (native/raw "__value = make_box(std::sqrt(runtime::detail::to_real(~{ o })));")) (reset-meta! (var sqrt) {:arities {1 {:supports-unboxed-input? true :unboxed-output? true}}}) (defn abs [o] - (native/raw "__value = abs(#{ o }#);")) + (native/raw "__value = abs(~{ o });")) (reset-meta! (var abs) {:arities {1 {:supports-unboxed-input? true :unboxed-output? true}}}) (defn pow [x y] - (native/raw "__value = make_box(std::pow(runtime::detail::to_real(#{ x }#), runtime::detail::to_real(#{ y }#)));")) + (native/raw "__value = make_box(std::pow(runtime::detail::to_real(~{ x }), runtime::detail::to_real(~{ y })));")) (reset-meta! (var pow) {:arities {2 {:supports-unboxed-input? true :unboxed-output? true}}}) @@ -1481,44 +1481,44 @@ ; Vars. (defn var? [o] - (native/raw "__value = make_box(#{ o }#->type == object_type::var);")) + (native/raw "__value = make_box(~{ o }->type == object_type::var);")) (defn var-get [v] (assert (var? v)) - (native/raw "__value = expect_object(#{ v }#)->deref();")) + (native/raw "__value = expect_object(~{ v })->deref();")) ; Namespaces (again). (defn create-ns [sym] (assert (symbol? sym)) - (native/raw "__value = __rt_ctx.intern_ns(expect_object(#{ sym }#));")) + (native/raw "__value = __rt_ctx.intern_ns(expect_object(~{ sym }));")) (defn find-ns [sym] (assert (symbol? sym)) - (native/raw "__value = __rt_ctx.find_ns(expect_object(#{ sym }#)).unwrap_or(nullptr); + (native/raw "__value = __rt_ctx.find_ns(expect_object(~{ sym })).unwrap_or(nullptr); if(!__value) { __value = obj::nil::nil_const(); }")) (defn remove-ns [sym] (assert (symbol? sym)) - (native/raw "__value = __rt_ctx.remove_ns(expect_object(#{ sym }#)).unwrap_or(nullptr); + (native/raw "__value = __rt_ctx.remove_ns(expect_object(~{ sym })).unwrap_or(nullptr); if(!__value) { __value = obj::nil::nil_const(); }")) (defn the-ns [ns-or-sym] - (if (native/raw "__value = make_box(#{ ns-or-sym }#->type == object_type::ns);") + (if (native/raw "__value = make_box(~{ ns-or-sym }->type == object_type::ns);") ns-or-sym (let [found (find-ns ns-or-sym)] (if (nil? found) - (native/raw "throw #{ (ex-info :not-an-ns-or-sym {:value ns-or-sym}) }#;") + (native/raw "throw ~{ (ex-info :not-an-ns-or-sym {:value ns-or-sym}) };") found)))) (defn ns-name [ns-sym] (let [ns (the-ns ns-sym)] - (native/raw "__value = expect_object(#{ ns }#)->name;"))) + (native/raw "__value = expect_object(~{ ns })->name;"))) (defn ns-map [ns-sym] (let [ns (the-ns ns-sym)] - (native/raw "__value = expect_object(#{ ns }#)->get_mappings();"))) + (native/raw "__value = expect_object(~{ ns })->get_mappings();"))) (defn ns-publics [ns-sym] (let [ns (the-ns ns-sym)] @@ -1529,7 +1529,7 @@ v (second kv)] ; TODO: Check for visibility. (if (var? v) - (if (native/raw "__value = make_box(#{ ns }# == expect_object(#{ v }#)->n);") + (if (native/raw "__value = make_box(~{ ns } == expect_object(~{ v })->n);") (assoc acc k v) acc) acc))) @@ -1552,21 +1552,21 @@ (defn- throw-if [pred msg] (when pred - (native/raw "throw #{ (ex-info :assertion-failure {:msg msg }) }#;"))) + (native/raw "throw ~{ (ex-info :assertion-failure {:msg msg }) };"))) (defn alias [alias ns-sym] (let [ns-obj (the-ns ns-sym)] (assert (symbol? alias)) - (native/raw "expect_object(#{ *ns* }#)->add_alias + (native/raw "expect_object(~{ *ns* })->add_alias ( - expect_object(#{ alias }#), - expect_object(#{ ns-obj }#) + expect_object(~{ alias }), + expect_object(~{ ns-obj }) ).expect_ok();"))) (defn refer [ns-sym & filters] (let [ns (find-ns ns-sym) _ (when (nil? ns) - (native/raw "throw #{ (ex-info :unknown-namespace {:value ns-sym}) }#;")) + (native/raw "throw ~{ (ex-info :unknown-namespace {:value ns-sym}) };")) filters (apply hash-map filters) rename (or (:rename filters) {}) exclude? (set (:exclude filters)) @@ -1580,11 +1580,11 @@ (let [v (get sym->var sym) sym (rename sym sym)] (when (nil? v) - (native/raw "throw #{ (ex-info :var-does-not-exist {:value sym}) }#;")) - (native/raw "expect_object(#{ *ns* }#)->refer + (native/raw "throw ~{ (ex-info :var-does-not-exist {:value sym}) };")) + (native/raw "expect_object(~{ *ns* })->refer ( - expect_object(#{ sym }#), - expect_object(#{ v }#) + expect_object(~{ sym }), + expect_object(~{ v }) ).expect_ok();")))) nil to-refer))) @@ -1594,7 +1594,7 @@ nil (let [path (first paths)] ; TODO: Check for cyclic deps, once we have dynamic vars. - (native/raw "__rt_ctx.load_module(runtime::detail::to_string(#{ path }#)).expect_ok();") + (native/raw "__rt_ctx.load_module(runtime::detail::to_string(~{ path })).expect_ok();") (recur (rest paths))))) ; Loads a lib given its name. If `need-ns?`, ensures that the associated @@ -1637,7 +1637,7 @@ as-alias (:as-alias opts) ; TODO: Dynamic var ;loaded (contains? @*loaded-libs* lib) - loaded? (native/raw "__value = make_box(__rt_ctx.module_loader.is_loaded(runtime::detail::to_string(#{ lib }#)));") + loaded? (native/raw "__value = make_box(__rt_ctx.module_loader.is_loaded(runtime::detail::to_string(~{ lib })));") need-ns? (or as use) load (cond reload-all load-all reload load-one @@ -1688,7 +1688,7 @@ (apply load-lib (prependss arg opts))))) (defn compile [path] - (native/raw "__rt_ctx.compile_module(runtime::detail::to_string(#{ path }#)).expect_ok();")) + (native/raw "__rt_ctx.compile_module(runtime::detail::to_string(~{ path })).expect_ok();")) (defn require [& args] (apply load-libs :require args)) @@ -1721,7 +1721,7 @@ (list 'if (list '= (list 'quote name) (list 'quote 'clojure.core)) 'nil (list 'let (vector 'name (list 'quote name)) - (list 'native/raw "__rt_ctx.module_loader.set_loaded(runtime::detail::to_string(#{ name }#));") + (list 'native/raw "__rt_ctx.module_loader.set_loaded(runtime::detail::to_string(~{ name }));") ; TODO: Dynamic vars ;(dosync (commute @#'*loaded-libs* conj '~name)) 'nil)) diff --git a/test/cpp/jank/read/parse.cpp b/test/cpp/jank/read/parse.cpp index 41c04beac..8a92b32d3 100644 --- a/test/cpp/jank/read/parse.cpp +++ b/test/cpp/jank/read/parse.cpp @@ -14,6 +14,7 @@ #include #include #include +#include /* This must go last; doctest and glog both define CHECK and family. */ #include @@ -29,7 +30,7 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() == nullptr); + CHECK(r.expect_ok().is_none()); } TEST_CASE("Nil") @@ -39,7 +40,9 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(runtime::detail::equal(r.expect_ok(), make_box(nullptr))); + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, runtime::obj::nil::nil_const())); + CHECK(r.expect_ok().unwrap().start == lex::token{ 0, 3, lex::token_kind::nil }); + CHECK(r.expect_ok().unwrap().end == r.expect_ok().unwrap().start); } TEST_CASE("Boolean") @@ -49,9 +52,14 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const t(p.next()); CHECK(t.is_ok()); - CHECK(runtime::detail::equal(t.expect_ok(), make_box(true))); + CHECK(runtime::detail::equal(t.expect_ok().unwrap().ptr, make_box(true))); + CHECK(t.expect_ok().unwrap().start == lex::token{ 0, 4, lex::token_kind::boolean, true }); + CHECK(t.expect_ok().unwrap().end == t.expect_ok().unwrap().start); + auto const f(p.next()); - CHECK(runtime::detail::equal(f.expect_ok(), make_box(false))); + CHECK(runtime::detail::equal(f.expect_ok().unwrap().ptr, make_box(false))); + CHECK(f.expect_ok().unwrap().start == lex::token{ 5, 5, lex::token_kind::boolean, false }); + CHECK(f.expect_ok().unwrap().end == f.expect_ok().unwrap().start); } TEST_CASE("Integer") @@ -61,7 +69,9 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(runtime::detail::equal(r.expect_ok(), make_box(1234))); + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, make_box(1234))); + CHECK(r.expect_ok().unwrap().start == lex::token{ 0, 4, lex::token_kind::integer, 1234ll }); + CHECK(r.expect_ok().unwrap().end == r.expect_ok().unwrap().start); } TEST_CASE("Comments") @@ -71,11 +81,11 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const i(p.next()); CHECK(i.is_ok()); - CHECK(runtime::detail::equal(i.expect_ok(), make_box(1234))); + CHECK(runtime::detail::equal(i.expect_ok().unwrap().ptr, make_box(1234))); auto const eof(p.next()); CHECK(eof.is_ok()); - CHECK(eof.expect_ok() == nullptr); + CHECK(eof.expect_ok().is_none()); } TEST_CASE("Real") @@ -85,7 +95,9 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(runtime::detail::equal(r.expect_ok(), make_box(12.34l))); + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, make_box(12.34l))); + CHECK(r.expect_ok().unwrap().start == lex::token{ 0, 5, lex::token_kind::real, 12.34l }); + CHECK(r.expect_ok().unwrap().end == r.expect_ok().unwrap().start); } TEST_CASE("String") @@ -95,11 +107,21 @@ namespace jank::read::parse lex::processor lp{ R"("foo" "bar")" }; runtime::context rt_ctx; processor p{ rt_ctx, lp.begin(), lp.end() }; + + size_t offset{}; for(auto const &s : { "foo", "bar" }) { auto const r(p.next()); CHECK(r.is_ok()); - CHECK(runtime::detail::equal(r.expect_ok(), make_box(s))); + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, make_box(s))); + + /* We add 2 for the surrounding quotes. */ + auto const len(strlen(s) + 2); + CHECK(r.expect_ok().unwrap().start + == lex::token{ offset, len, lex::token_kind::string, s }); + CHECK(r.expect_ok().unwrap().end == r.expect_ok().unwrap().start); + /* Each string is 1 space apart. */ + offset += len + 1; } } @@ -108,11 +130,22 @@ namespace jank::read::parse lex::processor lp{ R"("foo\n" "\t\"bar\"")" }; runtime::context rt_ctx; processor p{ rt_ctx, lp.begin(), lp.end() }; + + size_t offset{}; for(auto const &s : { "foo\n", "\t\"bar\"" }) { auto const r(p.next()); CHECK(r.is_ok()); - CHECK(runtime::detail::equal(r.expect_ok(), make_box(s))); + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, make_box(s))); + + /* We add 2 for the surrounding quotes. */ + auto const escaped(util::escape(s)); + auto const len(escaped.size() + 2); + CHECK(r.expect_ok().unwrap().start + == lex::token{ offset, len, lex::token_kind::escaped_string, escaped }); + CHECK(r.expect_ok().unwrap().end == r.expect_ok().unwrap().start); + /* Each string is 1 space apart. */ + offset += len + 1; } } } @@ -124,11 +157,21 @@ namespace jank::read::parse lex::processor lp{ "foo bar spam" }; runtime::context rt_ctx; processor p{ rt_ctx, lp.begin(), lp.end() }; + + size_t offset{}; for(auto const &s : { "foo", "bar", "spam" }) { auto const r(p.next()); CHECK(r.is_ok()); - CHECK(runtime::detail::equal(r.expect_ok(), make_box("", s))); + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, + make_box("", s))); + + auto const len(strlen(s)); + CHECK(r.expect_ok().unwrap().start + == lex::token{ offset, len, lex::token_kind::symbol, s }); + CHECK(r.expect_ok().unwrap().end == r.expect_ok().unwrap().start); + /* Each symbol is 1 space apart. */ + offset += len + 1; } } @@ -139,7 +182,10 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(runtime::detail::equal(r.expect_ok(), make_box("", "/"))); + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, + make_box("", "/"))); + CHECK(r.expect_ok().unwrap().start == lex::token{ 0, 1, lex::token_kind::symbol, "/" }); + CHECK(r.expect_ok().unwrap().end == r.expect_ok().unwrap().start); } SUBCASE("Qualified") @@ -150,14 +196,27 @@ namespace jank::read::parse rt_ctx.intern_ns(make_box("foo.bar")); rt_ctx.intern_ns(make_box("spam.bar")); processor p{ rt_ctx, lp.begin(), lp.end() }; + + size_t offset{}; for(auto const &s : { std::make_pair("foo", "foo"), std::make_pair("foo.bar", "bar"), std::make_pair("spam.bar", "spam") }) { auto const r(p.next()); CHECK(r.is_ok()); - CHECK(runtime::detail::equal(r.expect_ok(), + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, make_box(s.first, s.second))); + + /* We add one for the slash. */ + auto const len(strlen(s.first) + strlen(s.second) + 1); + CHECK(r.expect_ok().unwrap().start + == lex::token{ offset, + len, + lex::token_kind::symbol, + fmt::format("{}/{}", s.first, s.second) }); + CHECK(r.expect_ok().unwrap().end == r.expect_ok().unwrap().start); + /* Each symbol is 1 space apart. */ + offset += len + 1; } } @@ -172,7 +231,7 @@ namespace jank::read::parse { auto const r(p.next()); CHECK(r.is_ok()); - CHECK(runtime::detail::equal(r.expect_ok(), + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, make_box(s.first, s.second))); } } @@ -191,17 +250,34 @@ namespace jank::read::parse lex::processor lp{ "'foo 'bar/spam 'foo.bar/bar" }; runtime::context rt_ctx; processor p{ rt_ctx, lp.begin(), lp.end() }; + + size_t offset{}; for(auto const &s : { std::make_pair("", "foo"), std::make_pair("bar", "spam"), std::make_pair("foo.bar", "bar") }) { auto const r(p.next()); CHECK(r.is_ok()); - CHECK(runtime::detail::equal(r.expect_ok(), + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, make_box( std::in_place, make_box("quote"), make_box(s.first, s.second)))); + + auto const ns_len(strlen(s.first)); + /* We add one for the slash. */ + auto const len(strlen(s.first) + strlen(s.second) + (ns_len == 0 ? 0 : 1)); + CHECK(r.expect_ok().unwrap().start + == lex::token{ offset, 1, lex::token_kind::single_quote }); + offset += 1; + CHECK(r.expect_ok().unwrap().end + == lex::token{ offset, + len, + lex::token_kind::symbol, + (ns_len == 0 ? fmt::format("{}", s.second) + : fmt::format("{}/{}", s.first, s.second)) }); + /* Each symbol is 1 space apart. */ + offset += len + 1; } } } @@ -213,13 +289,23 @@ namespace jank::read::parse lex::processor lp{ ":foo :bar :spam" }; runtime::context rt_ctx; processor p{ rt_ctx, lp.begin(), lp.end() }; + + size_t offset{}; for(auto const &s : { "foo", "bar", "spam" }) { auto const r(p.next()); CHECK(r.is_ok()); CHECK(runtime::detail::equal( - r.expect_ok(), + r.expect_ok().unwrap().ptr, rt_ctx.intern_keyword(runtime::obj::symbol{ "", s }, true).expect_ok())); + + /* We add one for the colon. */ + auto const len(strlen(s) + 1); + CHECK(r.expect_ok().unwrap().start + == lex::token{ offset, len, lex::token_kind::keyword, s }); + CHECK(r.expect_ok().unwrap().end == r.expect_ok().unwrap().start); + /* Each symbol is 1 space apart. */ + offset += len + 1; } } @@ -228,6 +314,8 @@ namespace jank::read::parse lex::processor lp{ ":foo/foo :foo.bar/bar :spam.bar/spam" }; runtime::context rt_ctx; processor p{ rt_ctx, lp.begin(), lp.end() }; + + size_t offset{}; for(auto const &s : { std::make_pair("foo", "foo"), std::make_pair("foo.bar", "bar"), std::make_pair("spam.bar", "spam") }) @@ -235,8 +323,19 @@ namespace jank::read::parse auto const r(p.next()); CHECK(r.is_ok()); CHECK(runtime::detail::equal( - r.expect_ok(), + r.expect_ok().unwrap().ptr, rt_ctx.intern_keyword(runtime::obj::symbol{ s.first, s.second }, true).expect_ok())); + + /* We add one for the colon and one for the slash. */ + auto const len(strlen(s.first) + strlen(s.second) + 2); + CHECK(r.expect_ok().unwrap().start + == lex::token{ offset, + len, + lex::token_kind::keyword, + fmt::format("{}/{}", s.first, s.second) }); + CHECK(r.expect_ok().unwrap().end == r.expect_ok().unwrap().start); + /* Each symbol is 1 space apart. */ + offset += len + 1; } } @@ -245,14 +344,24 @@ namespace jank::read::parse lex::processor lp{ "::foo ::spam" }; runtime::context rt_ctx; processor p{ rt_ctx, lp.begin(), lp.end() }; + + size_t offset{}; for(auto const &s : { "foo", "spam" }) { auto const r(p.next()); CHECK(r.is_ok()); CHECK(runtime::detail::equal( - r.expect_ok(), + r.expect_ok().unwrap().ptr, rt_ctx.intern_keyword(runtime::obj::symbol{ "", native_persistent_string{ s } }, false) .expect_ok())); + + /* We add one for each colon. */ + auto const len(strlen(s) + 2); + CHECK(r.expect_ok().unwrap().start + == lex::token{ offset, len, lex::token_kind::keyword, fmt::format(":{}", s) }); + CHECK(r.expect_ok().unwrap().end == r.expect_ok().unwrap().start); + /* Each keyword is 1 space apart. */ + offset += len + 1; } } @@ -276,8 +385,11 @@ namespace jank::read::parse auto const r(p.next()); CHECK(r.is_ok()); CHECK(runtime::detail::equal( - r.expect_ok(), + r.expect_ok().unwrap().ptr, rt_ctx.intern_keyword(runtime::obj::symbol{ "foo.bar.spam", "foo" }, true).expect_ok())); + CHECK(r.expect_ok().unwrap().start + == lex::token{ 0, 9, lex::token_kind::keyword, ":foo/foo" }); + CHECK(r.expect_ok().unwrap().end == r.expect_ok().unwrap().start); } } @@ -285,34 +397,56 @@ namespace jank::read::parse { SUBCASE("Empty") { - lex::processor lp{ "() ( ) ( )" }; + lex::processor lp{ "() ( ) ( )" }; runtime::context rt_ctx; processor p{ rt_ctx, lp.begin(), lp.end() }; + + size_t offset{}; for(size_t i{}; i < 3; ++i) { auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); - CHECK(runtime::detail::equal(r.expect_ok(), make_box())); + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, + runtime::obj::persistent_list::empty())); + + /* We add one for each paren. */ + auto const len(2 + i); + CHECK(r.expect_ok().unwrap().start + == lex::token{ offset, 1, lex::token_kind::open_paren }); + CHECK(r.expect_ok().unwrap().end + == lex::token{ offset + i + 1, 1, lex::token_kind::close_paren }); + /* Each list is 1 space apart. */ + offset += len + 1; } } SUBCASE("Non-empty") { - lex::processor lp{ "(1 2 3 4) ( 2, 4 6, 8 )" }; + lex::processor lp{ "(1, 2, 3, 4) ( 2, 4, 6, 8 )" }; runtime::context rt_ctx; processor p{ rt_ctx, lp.begin(), lp.end() }; + + size_t offset{}; for(size_t i{ 1 }; i < 3; ++i) { auto const r(p.next()); CHECK(r.is_ok()); CHECK(runtime::detail::equal( - r.expect_ok(), + r.expect_ok().unwrap().ptr, make_box(std::in_place, make_box(1 * i), make_box(2 * i), make_box(3 * i), make_box(4 * i)))); + + /* Parens, nums, spaces, commas, and optionally more spaces. */ + auto const len(2 + 4 + 3 + 3 + (i == 2 ? 2 : 0)); + CHECK(r.expect_ok().unwrap().start + == lex::token{ offset, 1, lex::token_kind::open_paren }); + CHECK(r.expect_ok().unwrap().end + == lex::token{ offset + len - 1, 1, lex::token_kind::close_paren }); + /* Each list is 1 space apart. */ + offset += len + 1; } } @@ -324,14 +458,15 @@ namespace jank::read::parse auto const r1(p.next()); CHECK(r1.is_ok()); CHECK(runtime::detail::equal( - r1.expect_ok(), + r1.expect_ok().unwrap().ptr, make_box(std::in_place, make_box("def"), make_box("foo-bar"), make_box(1)))); auto const r2(p.next()); CHECK(r2.is_ok()); - CHECK(runtime::detail::equal(r2.expect_ok(), make_box("foo-bar"))); + CHECK(runtime::detail::equal(r2.expect_ok().unwrap().ptr, + make_box("foo-bar"))); } SUBCASE("Extra close") @@ -341,7 +476,7 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r1(p.next()); CHECK(r1.is_ok()); - CHECK(runtime::detail::equal(r1.expect_ok(), make_box(1))); + CHECK(runtime::detail::equal(r1.expect_ok().unwrap().ptr, make_box(1))); auto const r2(p.next()); CHECK(r2.is_err()); } @@ -360,35 +495,57 @@ namespace jank::read::parse { SUBCASE("Empty") { - lex::processor lp{ "[] [ ] [ ]" }; + lex::processor lp{ "[] [ ] [ ]" }; runtime::context rt_ctx; processor p{ rt_ctx, lp.begin(), lp.end() }; + + size_t offset{}; for(size_t i{}; i < 3; ++i) { auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); - CHECK(runtime::detail::equal(r.expect_ok(), make_box())); + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, + make_box())); + + /* We add one for each bracket. */ + auto const len(2 + i); + CHECK(r.expect_ok().unwrap().start + == lex::token{ offset, 1, lex::token_kind::open_square_bracket }); + CHECK(r.expect_ok().unwrap().end + == lex::token{ offset + i + 1, 1, lex::token_kind::close_square_bracket }); + /* Each vector is 1 space apart. */ + offset += len + 1; } } SUBCASE("Non-empty") { - lex::processor lp{ "[1 2 3 4] [ 2, 4 6, 8 ]" }; + lex::processor lp{ "[1, 2, 3, 4] [ 2, 4, 6, 8 ]" }; runtime::context rt_ctx; processor p{ rt_ctx, lp.begin(), lp.end() }; + + size_t offset{}; for(size_t i{ 1 }; i < 3; ++i) { auto const r(p.next()); CHECK(r.is_ok()); CHECK(runtime::detail::equal( - r.expect_ok(), + r.expect_ok().unwrap().ptr, make_box(runtime::detail::native_persistent_vector{ make_box(1 * i), make_box(2 * i), make_box(3 * i), make_box(4 * i), }))); + + /* Brackets, nums, spaces, commas, and optionally more spaces. */ + auto const len(2 + 4 + 3 + 3 + (i == 2 ? 2 : 0)); + CHECK(r.expect_ok().unwrap().start + == lex::token{ offset, 1, lex::token_kind::open_square_bracket }); + CHECK(r.expect_ok().unwrap().end + == lex::token{ offset + len - 1, 1, lex::token_kind::close_square_bracket }); + /* Each vector is 1 space apart. */ + offset += len + 1; } } @@ -399,7 +556,7 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r1(p.next()); CHECK(r1.is_ok()); - CHECK(runtime::detail::equal(r1.expect_ok(), make_box(1))); + CHECK(runtime::detail::equal(r1.expect_ok().unwrap().ptr, make_box(1))); auto const r2(p.next()); CHECK(r2.is_err()); } @@ -414,6 +571,123 @@ namespace jank::read::parse } } + TEST_CASE("Map") + { + SUBCASE("Empty") + { + lex::processor lp{ "{} { } {,,}" }; + runtime::context rt_ctx; + processor p{ rt_ctx, lp.begin(), lp.end() }; + + size_t offset{}; + for(size_t i{}; i < 3; ++i) + { + auto const r(p.next()); + CHECK(r.is_ok()); + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, + make_box())); + + /* We add one for each bracket. */ + auto const len(2 + i); + CHECK(r.expect_ok().unwrap().start + == lex::token{ offset, 1, lex::token_kind::open_curly_bracket }); + CHECK(r.expect_ok().unwrap().end + == lex::token{ offset + i + 1, 1, lex::token_kind::close_curly_bracket }); + /* Each map is 1 space apart. */ + offset += len + 1; + } + } + + SUBCASE("Non-empty") + { + lex::processor lp{ "{1 2, 3 4} { 2 4, 6 8 }" }; + runtime::context rt_ctx; + processor p{ rt_ctx, lp.begin(), lp.end() }; + + size_t offset{}; + for(size_t i{ 1 }; i < 3; ++i) + { + auto const r(p.next()); + CHECK(r.is_ok()); + CHECK(runtime::detail::equal( + r.expect_ok().unwrap().ptr, + make_box( + runtime::detail::in_place_unique{}, + make_array_box(make_box(1 * i), + make_box(2 * i), + make_box(3 * i), + make_box(4 * i)), + 4))); + + /* Brackets, nums, spaces, commas, and optionally more spaces. */ + auto const len(2 + 4 + 3 + 1 + (i == 2 ? 2 : 0)); + CHECK(r.expect_ok().unwrap().start + == lex::token{ offset, 1, lex::token_kind::open_curly_bracket }); + CHECK(r.expect_ok().unwrap().end + == lex::token{ offset + len - 1, 1, lex::token_kind::close_curly_bracket }); + /* Each map is 1 space apart. */ + offset += len + 1; + } + } + + SUBCASE("Heterogeneous") + { + lex::processor lp{ R"({:foo true 1 :one "meow" "meow"})" }; + runtime::context rt_ctx; + processor p{ rt_ctx, lp.begin(), lp.end() }; + auto const r(p.next()); + CHECK(r.is_ok()); + CHECK(runtime::detail::equal( + r.expect_ok().unwrap().ptr, + make_box( + runtime::detail::in_place_unique{}, + make_array_box( + rt_ctx.intern_keyword(runtime::obj::symbol{ "foo" }, true).expect_ok(), + make_box(true), + make_box(1), + rt_ctx.intern_keyword(runtime::obj::symbol{ "one" }, true).expect_ok(), + make_box("meow"), + make_box("meow")), + 6))); + CHECK(r.expect_ok().unwrap().start + == lex::token{ 0, 1, lex::token_kind::open_curly_bracket }); + CHECK(r.expect_ok().unwrap().end + == lex::token{ 31, 1, lex::token_kind::close_curly_bracket }); + } + + SUBCASE("Odd elements") + { + lex::processor lp{ "{1 2 3}" }; + runtime::context rt_ctx; + processor p{ rt_ctx, lp.begin(), lp.end() }; + auto const r1(p.next()); + CHECK(r1.is_err()); + } + + SUBCASE("Extra close") + { + lex::processor lp{ ":foo}" }; + runtime::context rt_ctx; + processor p{ rt_ctx, lp.begin(), lp.end() }; + auto const r1(p.next()); + CHECK(r1.is_ok()); + CHECK(runtime::detail::equal( + r1.expect_ok().unwrap().ptr, + rt_ctx.intern_keyword(runtime::obj::symbol{ "foo" }, true).expect_ok())); + auto const r2(p.next()); + CHECK(r2.is_err()); + } + + SUBCASE("Unterminated") + { + lex::processor lp{ "{1" }; + runtime::context rt_ctx; + processor p{ rt_ctx, lp.begin(), lp.end() }; + auto const r1(p.next()); + CHECK(r1.is_err()); + } + } + TEST_CASE("Meta hint") { SUBCASE("No following meta value") @@ -441,10 +715,10 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); - CHECK(runtime::detail::equal(r.expect_ok(), runtime::obj::persistent_array_map::empty())); + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, + runtime::obj::persistent_array_map::empty())); CHECK( - runtime::detail::equal(runtime::meta(r.expect_ok()), + runtime::detail::equal(runtime::meta(r.expect_ok().unwrap().ptr), runtime::obj::persistent_array_map::create_unique( rt_ctx.intern_keyword(runtime::obj::symbol{ "foo" }).expect_ok(), runtime::obj::boolean::true_const()))); @@ -466,10 +740,10 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); - CHECK(runtime::detail::equal(r.expect_ok(), runtime::obj::persistent_vector::empty())); + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, + runtime::obj::persistent_vector::empty())); CHECK(runtime::detail::equal( - runtime::meta(r.expect_ok()), + runtime::meta(r.expect_ok().unwrap().ptr), runtime::obj::persistent_array_map::create_unique( rt_ctx.intern_keyword(runtime::obj::symbol{ "foo" }).expect_ok(), rt_ctx.intern_keyword(runtime::obj::symbol{ "bar" }).expect_ok()))); @@ -491,10 +765,10 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); - CHECK(runtime::detail::equal(r.expect_ok(), runtime::obj::persistent_list::empty())); + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, + runtime::obj::persistent_list::empty())); CHECK(runtime::detail::equal( - runtime::meta(r.expect_ok()), + runtime::meta(r.expect_ok().unwrap().ptr), runtime::obj::persistent_array_map::create_unique( rt_ctx.intern_keyword(runtime::obj::symbol{ "foo" }).expect_ok(), rt_ctx.intern_keyword(runtime::obj::symbol{ "bar" }).expect_ok(), @@ -509,10 +783,10 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); - CHECK(runtime::detail::equal(r.expect_ok(), runtime::obj::persistent_vector::empty())); + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, + runtime::obj::persistent_vector::empty())); CHECK(runtime::detail::equal( - runtime::meta(r.expect_ok()), + runtime::meta(r.expect_ok().unwrap().ptr), runtime::obj::persistent_array_map::create_unique( rt_ctx.intern_keyword(runtime::obj::symbol{ "foo" }).expect_ok(), make_box(std::in_place, @@ -527,17 +801,17 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); CHECK(runtime::detail::equal( - r.expect_ok(), + r.expect_ok().unwrap().ptr, make_box(std::in_place, make_box("str"), runtime::obj::persistent_set::empty()))); CHECK(runtime::detail::equal( - runtime::meta(runtime::expect_object(r.expect_ok()) - ->data.rest() - .first() - .unwrap()), + runtime::meta( + runtime::expect_object(r.expect_ok().unwrap().ptr) + ->data.rest() + .first() + .unwrap()), runtime::obj::persistent_array_map::create_unique( rt_ctx.intern_keyword(runtime::obj::symbol{ "foo" }).expect_ok(), runtime::obj::boolean::true_const()))); @@ -573,8 +847,8 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); - CHECK(runtime::detail::equal(r.expect_ok(), runtime::obj::persistent_set::empty())); + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, + runtime::obj::persistent_set::empty())); } SUBCASE("Non-empty") @@ -584,9 +858,8 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); CHECK(runtime::detail::equal( - r.expect_ok(), + r.expect_ok().unwrap().ptr, make_box(std::in_place, make_box(1)))); } @@ -597,9 +870,8 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); CHECK(runtime::detail::equal( - r.expect_ok(), + r.expect_ok().unwrap().ptr, make_box( std::in_place, make_box(1), @@ -625,9 +897,8 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); CHECK(runtime::detail::equal( - r.expect_ok(), + r.expect_ok().unwrap().ptr, make_box(std::in_place, make_box(2)))); } @@ -638,8 +909,7 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); - CHECK(runtime::detail::equal(r.expect_ok(), make_box(3))); + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, make_box(3))); } SUBCASE("Number") @@ -649,8 +919,7 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); - CHECK(runtime::detail::equal(r.expect_ok(), make_box("ok"))); + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, make_box("ok"))); } SUBCASE("Invalid form") @@ -690,9 +959,8 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); CHECK(runtime::detail::equal( - r.expect_ok(), + r.expect_ok().unwrap().ptr, make_box(std::in_place, make_box(9)))); } @@ -703,9 +971,8 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); CHECK(runtime::detail::equal( - r.expect_ok(), + r.expect_ok().unwrap().ptr, make_box(std::in_place, make_box(8), make_box(9)))); } @@ -716,9 +983,8 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); CHECK(runtime::detail::equal( - r.expect_ok(), + r.expect_ok().unwrap().ptr, make_box(std::in_place, make_box(7), make_box(9)))); } @@ -729,9 +995,8 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); CHECK(runtime::detail::equal( - r.expect_ok(), + r.expect_ok().unwrap().ptr, make_box(std::in_place, make_box(-1), make_box(9)))); } @@ -742,9 +1007,8 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); CHECK(runtime::detail::equal( - r.expect_ok(), + r.expect_ok().unwrap().ptr, make_box(std::in_place, make_box(5), make_box(9)))); } @@ -755,9 +1019,8 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); CHECK(runtime::detail::equal( - r.expect_ok(), + r.expect_ok().unwrap().ptr, make_box(std::in_place, make_box(1)))); } @@ -799,9 +1062,8 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); CHECK(runtime::detail::equal( - r.expect_ok(), + r.expect_ok().unwrap().ptr, make_box(std::in_place, make_box(1), make_box(2)))); } @@ -812,8 +1074,7 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); - CHECK(runtime::detail::equal(r.expect_ok(), + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, make_box(std::in_place, make_box(1), make_box(2), @@ -830,8 +1091,7 @@ namespace jank::read::parse processor p{ rt_ctx, lp.begin(), lp.end() }; auto const r(p.next()); CHECK(r.is_ok()); - CHECK(r.expect_ok() != nullptr); - CHECK(runtime::detail::equal(r.expect_ok(), + CHECK(runtime::detail::equal(r.expect_ok().unwrap().ptr, make_box(std::in_place, make_box(0), make_box(1), diff --git a/test/jank/form/native_raw/interpolate/fail-invalid-code.jank b/test/jank/form/native_raw/interpolate/fail-invalid-code.jank index 24757426a..712062556 100644 --- a/test/jank/form/native_raw/interpolate/fail-invalid-code.jank +++ b/test/jank/form/native_raw/interpolate/fail-invalid-code.jank @@ -1 +1 @@ -(native/raw "__value = #{ meow }#;") +(native/raw "__value = ~{ meow };") diff --git a/test/jank/form/native_raw/interpolate/fail-multiple-expresssions.jank b/test/jank/form/native_raw/interpolate/fail-multiple-expresssions.jank index 493b55853..fcbca4fc7 100644 --- a/test/jank/form/native_raw/interpolate/fail-multiple-expresssions.jank +++ b/test/jank/form/native_raw/interpolate/fail-multiple-expresssions.jank @@ -1 +1 @@ -(native/raw "__value = #{ 1 2 }#;") +(native/raw "__value = ~{ 1 2 };") diff --git a/test/jank/form/native_raw/interpolate/pass-symbol.jank b/test/jank/form/native_raw/interpolate/pass-symbol.jank index 2ab2b386e..18f60bcf6 100644 --- a/test/jank/form/native_raw/interpolate/pass-symbol.jank +++ b/test/jank/form/native_raw/interpolate/pass-symbol.jank @@ -1,4 +1,4 @@ (def a 1) -(assert (= a (native/raw "__value = #{ a }#;"))) +(assert (= a (native/raw "__value = ~{ a };"))) :success diff --git a/test/jank/form/native_raw/pass-practical.jank b/test/jank/form/native_raw/pass-practical.jank index 524f06176..934f61d48 100644 --- a/test/jank/form/native_raw/pass-practical.jank +++ b/test/jank/form/native_raw/pass-practical.jank @@ -4,7 +4,7 @@ ; Interpolation will often by used for parameters. (def keyword? (fn* [o] - (native/raw "__value = make_box(#{ o }#->type == object_type::keyword)"))) + (native/raw "__value = make_box(~{ o }->type == object_type::keyword)"))) (assert (= (keyword? :meow) true)) (assert (= (keyword? 1) false)) @@ -14,7 +14,7 @@ (fn* [o] ; Interpolation can contain arbitrary jank code. ; It can also capture vars and locals, which lifts and captures them accordingly. - (native/raw "__value = #{ (keyword? o) }#"))) + (native/raw "__value = ~{ (keyword? o) };"))) (assert (= (also-keyword? :meow) true)) (assert (= (also-keyword? 1) false))