Skip to content

Commit

Permalink
fix: Always parse all tokens from quoted token streams (#6064)
Browse files Browse the repository at this point in the history
# Description

## Problem\*

Resolves #5803
Resolves #6059

## Summary\*

If we ever had a token stream with a subset of valid input we'd parse
just that subset instead of erroring that the full input doesn't parse.

## Additional Context

We don't have EOF tokens in token streams so I used the `end()`
primitive instead.

## Documentation\*

Check one:
- [x] No documentation needed.
- [ ] 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.
  • Loading branch information
jfecher authored Sep 17, 2024
1 parent e993da1 commit 23ed74b
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 26 deletions.
37 changes: 11 additions & 26 deletions compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use builtin_helpers::{
mutate_func_meta_type, parse, quote_ident, replace_func_meta_parameters,
replace_func_meta_return_type,
};
use chumsky::{chain::Chain, prelude::choice, Parser};
use chumsky::{chain::Chain, prelude::choice, primitive::just, Parser};
use im::Vector;
use iter_extended::{try_vecmap, vecmap};
use noirc_errors::Location;
Expand All @@ -33,15 +33,14 @@ use crate::{
def_map::ModuleId,
},
hir_def::function::FunctionBody,
lexer::Lexer,
macros_api::{HirExpression, HirLiteral, Ident, ModuleDefId, NodeInterner, Signedness},
node_interner::{DefinitionKind, TraitImplKind},
parser::{self},
parser,
token::{Attribute, SecondaryAttribute, Token},
Kind, QuotedType, ResolvedGeneric, Shared, Type, TypeVariable,
};

use self::builtin_helpers::{eq_item, get_array, get_ctstring, get_str, get_u8, hash_item};
use self::builtin_helpers::{eq_item, get_array, get_ctstring, get_str, get_u8, hash_item, lex};
use super::Interpreter;

pub(crate) mod builtin_helpers;
Expand Down Expand Up @@ -330,18 +329,15 @@ fn struct_def_add_attribute(
let attribute_location = attribute.1;
let attribute = get_str(interner, attribute)?;

let mut tokens = Lexer::lex(&format!("#[{}]", attribute)).0 .0;
if let Some(Token::EOF) = tokens.last().map(|token| token.token()) {
tokens.pop();
}
let mut tokens = lex(&format!("#[{}]", attribute));
if tokens.len() != 1 {
return Err(InterpreterError::InvalidAttribute {
attribute: attribute.to_string(),
location: attribute_location,
});
}

let token = tokens.into_iter().next().unwrap().into_token();
let token = tokens.remove(0);
let Token::Attribute(attribute) = token else {
return Err(InterpreterError::InvalidAttribute {
attribute: attribute.to_string(),
Expand Down Expand Up @@ -374,19 +370,15 @@ fn struct_def_add_generic(
let generic_location = generic.1;
let generic = get_str(interner, generic)?;

let mut tokens = Lexer::lex(&generic).0 .0;
if let Some(Token::EOF) = tokens.last().map(|token| token.token()) {
tokens.pop();
}

let mut tokens = lex(&generic);
if tokens.len() != 1 {
return Err(InterpreterError::GenericNameShouldBeAnIdent {
name: generic,
location: generic_location,
});
}

let Token::Ident(generic_name) = tokens.pop().unwrap().into_token() else {
let Token::Ident(generic_name) = tokens.remove(0) else {
return Err(InterpreterError::GenericNameShouldBeAnIdent {
name: generic,
location: generic_location,
Expand Down Expand Up @@ -690,6 +682,7 @@ fn quoted_as_expr(
let statement_parser = parser::fresh_statement().map(Value::statement);
let lvalue_parser = parser::lvalue(parser::expression()).map(Value::lvalue);
let parser = choice((expr_parser, statement_parser, lvalue_parser));
let parser = parser.then_ignore(just(Token::Semicolon).or_not());

let expr = parse(argument, parser, "an expression").ok();

Expand Down Expand Up @@ -2059,12 +2052,7 @@ fn fmtstr_quoted_contents(
) -> IResult<Value> {
let self_argument = check_one_argument(arguments, location)?;
let (string, _) = get_format_string(interner, self_argument)?;
let (tokens, _) = Lexer::lex(&string);
let mut tokens: Vec<_> = tokens.0.into_iter().map(|token| token.into_token()).collect();
if let Some(Token::EOF) = tokens.last() {
tokens.pop();
}

let tokens = lex(&string);
Ok(Value::Quoted(Rc::new(tokens)))
}

Expand All @@ -2083,18 +2071,15 @@ fn function_def_add_attribute(
let attribute_location = attribute.1;
let attribute = get_str(interpreter.elaborator.interner, attribute)?;

let mut tokens = Lexer::lex(&format!("#[{}]", attribute)).0 .0;
if let Some(Token::EOF) = tokens.last().map(|token| token.token()) {
tokens.pop();
}
let mut tokens = lex(&format!("#[{}]", attribute));
if tokens.len() != 1 {
return Err(InterpreterError::InvalidAttribute {
attribute: attribute.to_string(),
location: attribute_location,
});
}

let token = tokens.into_iter().next().unwrap().into_token();
let token = tokens.remove(0);
let Token::Attribute(attribute) = token else {
return Err(InterpreterError::InvalidAttribute {
attribute: attribute.to_string(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::{hash::Hasher, rc::Rc};
use acvm::FieldElement;
use noirc_errors::Location;

use crate::lexer::Lexer;
use crate::{
ast::{
BlockExpression, ExpressionKind, Ident, IntegerBitSize, LValue, Pattern, Signedness,
Expand Down Expand Up @@ -392,11 +393,21 @@ pub(super) fn check_function_not_yet_resolved(
}
}

pub(super) fn lex(input: &str) -> Vec<Token> {
let (tokens, _) = Lexer::lex(input);
let mut tokens: Vec<_> = tokens.0.into_iter().map(|token| token.into_token()).collect();
if let Some(Token::EOF) = tokens.last() {
tokens.pop();
}
tokens
}

pub(super) fn parse<T>(
(value, location): (Value, Location),
parser: impl NoirParser<T>,
rule: &'static str,
) -> IResult<T> {
let parser = parser.then_ignore(chumsky::primitive::end());
let tokens = get_quoted((value, location))?;
let quoted = add_token_spans(tokens.clone(), location.span);
parse_tokens(tokens, quoted, location, parser, rule)
Expand Down
1 change: 1 addition & 0 deletions compiler/noirc_frontend/src/hir/comptime/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,7 @@ fn parse_tokens<T>(
parser: impl NoirParser<T>,
location: Location,
) -> IResult<T> {
let parser = parser.then_ignore(chumsky::primitive::end());
match parser.parse(add_token_spans(tokens.clone(), location.span)) {
Ok(expr) => Ok(expr),
Err(mut errors) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "comptime_parse_all_tokens"
type = "bin"
authors = [""]
compiler_version = ">=0.34.0"

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#[foo]
fn main() {}

comptime fn foo(_f: FunctionDefinition) -> Quoted {
let t = quote { Field }.as_type();
// We parse 0 or more top level expressions from attribute output
// so for invalid input we previously "successfully" parsed 0 items.
quote { use $t; }
}

0 comments on commit 23ed74b

Please sign in to comment.