Skip to content

Commit

Permalink
Switch to ~{} interpolation syntax
Browse files Browse the repository at this point in the history
This is consistent with ClojureScript.
  • Loading branch information
jeaye committed Mar 10, 2024
1 parent 9a134d4 commit 4bfd17f
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 219 deletions.
57 changes: 34 additions & 23 deletions src/cpp/jank/analyze/processor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -957,36 +957,36 @@ 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<expression>::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().unwrap().ptr,

/* 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,
Expand All @@ -996,18 +996,29 @@ namespace jank::analyze
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<expression>(expr::native_raw<expression>{
Expand Down
Loading

0 comments on commit 4bfd17f

Please sign in to comment.