Skip to content

Commit

Permalink
feat: Add Quoted::tokens (#5942)
Browse files Browse the repository at this point in the history
# Description

## Problem\*

While `fmtstr.quoted_contents` is great for 99% of use cases, sometimes
the raw str (including double quotes) is needed. Consider this case (fn
signature generation in `aztec.nr`)

```noir 
pub(crate) comptime fn compute_fn_selector(f: FunctionDefinition) -> Field {
    let fn_name = f.name();
    let args_signatures = f.parameters().map(
        | (_, typ): (Quoted, Type) | {
        signature_of_type(typ)
    }
    ).join(quote {,});
    let signature = f"{fn_name}({args_signatures})".quoted_contents();
    // from_signature takes a str, quoted_contents is no good here!
    let computation_quote = quote {
        protocol_types::abis::function_selector::FunctionSelector::from_signature($signature).to_field()
    };
    unquote!(computation_quote)
}
```

We have a bunch of `Quoted` values obtained during comptime that we have
to string (ha) together. The result is then needed to compute a hash.

## Summary\*
~~This just adds a builtin that "resolves" the `fmtstr` and then turns
it into a `Token::Str`~~

It's possible to create a string from quoted values by doing:

```noir
   let signature = f"{name}({args})";
   let signature_as_str = unquote!(quote { $signature });
```

But this includes whitespaces around the tokens. To get rid of them,
ended up going with the approach suggested by @jfecher, which certainly
complicates user code, but avoids the "eating whitespaces" problem:

> Going forward I think we'll need to instead control the formatting of
Quoted values more. The compiler itself can never guess what exact
formatting users will want of them since spaces are already not present.
So I'm leaning towards a Quoted::tokens(self) -> [Quoted] method to
return a slice of each individual token in the Quoted value. Then users
could iterate over it and apply (or not apply) spacing in between each
token as they see fit.

Also, the returned values are `str<_>`, since the compiler has no idea
of the returned length after formatting. This means values can only be
used by unquoting them and not directly.

## Additional Context

My first intuition was to do

```noir
let signature = f"\"{quotedvalues}\""
```

But it doesn't work =/

## Documentation\*

Check one:
- [ ] No documentation needed.
- [x] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.

---------

Co-authored-by: jfecher <[email protected]>
  • Loading branch information
Thunkar and jfecher authored Sep 5, 2024
1 parent b84009c commit a297ec6
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 4 deletions.
12 changes: 12 additions & 0 deletions compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ impl<'local, 'context> Interpreter<'local, 'context> {
"quoted_as_trait_constraint" => quoted_as_trait_constraint(self, arguments, location),
"quoted_as_type" => quoted_as_type(self, arguments, location),
"quoted_eq" => quoted_eq(arguments, location),
"quoted_tokens" => quoted_tokens(arguments, location),
"slice_insert" => slice_insert(interner, arguments, location),
"slice_pop_back" => slice_pop_back(interner, arguments, location, call_stack),
"slice_pop_front" => slice_pop_front(interner, arguments, location, call_stack),
Expand Down Expand Up @@ -535,6 +536,17 @@ fn quoted_as_type(
Ok(Value::Type(typ))
}

// fn tokens(quoted: Quoted) -> [Quoted]
fn quoted_tokens(arguments: Vec<(Value, Location)>, location: Location) -> IResult<Value> {
let argument = check_one_argument(arguments, location)?;
let value = get_quoted(argument)?;

Ok(Value::Slice(
value.iter().map(|token| Value::Quoted(Rc::new(vec![token.clone()]))).collect(),
Type::Slice(Box::new(Type::Quoted(QuotedType::Quoted))),
))
}

fn to_le_radix(
arguments: Vec<(Value, Location)>,
return_type: Type,
Expand Down
6 changes: 6 additions & 0 deletions docs/docs/noir/standard_library/meta/quoted.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ stream doesn't parse to a type or if the type isn't a valid type in scope.

#include_code implements_example test_programs/compile_success_empty/comptime_type/src/main.nr rust

### tokens

#include_code tokens noir_stdlib/src/meta/quoted.nr rust

Returns a slice of the individual tokens that form this token stream.

## Trait Implementations

```rust
Expand Down
13 changes: 9 additions & 4 deletions noir_stdlib/src/meta/quoted.nr
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,29 @@ use crate::option::Option;

impl Quoted {
#[builtin(quoted_as_expr)]
// docs:start:as_expr
// docs:start:as_expr
fn as_expr(self) -> Option<Expr> {}
// docs:end:as_expr

#[builtin(quoted_as_module)]
// docs:start:as_module
// docs:start:as_module
fn as_module(self) -> Option<Module> {}
// docs:end:as_module

#[builtin(quoted_as_trait_constraint)]
// docs:start:as_trait_constraint
// docs:start:as_trait_constraint
fn as_trait_constraint(self) -> TraitConstraint {}
// docs:end:as_trait_constraint

#[builtin(quoted_as_type)]
// docs:start:as_type
// docs:start:as_type
fn as_type(self) -> Type {}
// docs:end:as_type

#[builtin(quoted_tokens)]
// docs:start:tokens
fn tokens(self) -> [Quoted] {}
// docs:end:tokens
}

impl Eq for Quoted {
Expand Down

0 comments on commit a297ec6

Please sign in to comment.