diff --git a/ls/src/features/completion.rs b/ls/src/features/completion.rs index 4f5a0b4cb..92463e140 100644 --- a/ls/src/features/completion.rs +++ b/ls/src/features/completion.rs @@ -15,8 +15,8 @@ use yara_x_parser::cst::{Immutable, Node, SyntaxKind, Token, CST}; use crate::documents::storage::DocumentStorage; use crate::utils::cst_traversal::{ - idents_declared_by_expr, non_error_parent, prev_non_trivia_token, - rule_containing_token, token_at_position, + find_declaration, idents_declared_by_expr, non_error_parent, + prev_non_trivia_token, rule_containing_token, token_at_position, }; const PATTERN_MODS: &[(SyntaxKind, &[&str])] = &[ @@ -98,7 +98,7 @@ pub fn completion( if !is_trigger_character && non_error_parent(&token)?.kind() == SyntaxKind::SOURCE_FILE { - return Some(source_file_suggestions()); + return Some(top_level_suggestions()); } let prev_token = prev_non_trivia_token(&token)?; @@ -142,7 +142,7 @@ fn condition_suggestions( let mut result = Vec::new(); #[cfg(feature = "full-compiler")] - if let Some(suggestions) = module_suggestions(&token) { + if let Some(suggestions) = field_suggestions(&token) { return Some(suggestions); } @@ -271,7 +271,7 @@ fn import_suggestions() -> Vec { } /// Collects completion suggestions outside any block. -fn source_file_suggestions() -> Vec { +fn top_level_suggestions() -> Vec { // Propose import or rule definition with snippet SRC_SUGGESTIONS .map(|(label, insert_text)| CompletionItem { @@ -292,6 +292,7 @@ fn source_file_suggestions() -> Vec { .collect() } +/// Collects completion suggestions for pattern modifiers. fn pattern_modifier_suggestions(node: Node) -> Vec { for (kind, valid_modifiers) in PATTERN_MODS { if node.children_with_tokens().any(|child| child.kind() == *kind) { @@ -326,43 +327,137 @@ fn rule_suggestions() -> Vec { .collect() } -#[cfg(feature = "full-compiler")] -fn module_suggestions( - token: &Token, -) -> Option> { - let mut curr; +#[derive(Debug)] +enum Segment { + Field(String), + Index, +} +/// Collects completion suggestions for structure fields. +#[cfg(feature = "full-compiler")] +fn field_suggestions(token: &Token) -> Option> { // Check if we are at a position that triggers completion. - match token.kind() { + let token = match token.kind() { SyntaxKind::DOT => { // structure. - curr = prev_non_trivia_token(token); + prev_non_trivia_token(token) } SyntaxKind::IDENT => { // structure.field // We need to check if previous is DOT - let prev = prev_non_trivia_token(token)?; - if prev.kind() == SyntaxKind::DOT { - // It is a field - curr = prev_non_trivia_token(&prev); - } else { - return None; - } + prev_non_trivia_token(token) + .filter(|t| t.kind() == SyntaxKind::DOT) + .and_then(|t| prev_non_trivia_token(&t)) } + _ => None, + }?; + + let current_struct = match get_struct(&token)? { + Type::Struct(s) => s, _ => return None, - } + }; - #[derive(Debug)] - enum Segment { - Field(String), - Index, - } + // Now `current_struct` is the structure before the cursor. + // We want to suggest fields for this structure. + let suggestions = current_struct + .fields() + .flat_map(|f| { + let name = f.name(); + let ty = f.ty(); + + if let Type::Func(ref func_def) = ty { + func_def + .signatures + .iter() + .map(|sig| { + let arg_types = sig + .args + .iter() + .map(ty_to_string) + .collect::>(); + + let args_template = arg_types + .iter() + .enumerate() + .map(|(n, arg_type)| { + format!("${{{}:{arg_type}}}", n + 1) + }) + .join(","); + CompletionItem { + label: format!( + "{}({})", + name, + arg_types.join(", ") + ), + kind: Some(CompletionItemKind::METHOD), + insert_text: Some(format!( + "{name}({args_template})", + )), + insert_text_format: Some( + InsertTextFormat::SNIPPET, + ), + label_details: Some(CompletionItemLabelDetails { + description: Some(ty_to_string(&ty)), + ..Default::default() + }), + ..Default::default() + } + }) + .collect() + } else { + let insert_text = match &ty { + Type::Array(_) => format!("{name}[${{1}}]${{2}}"), + _ => name.to_string(), + }; + + vec![CompletionItem { + label: name.to_string(), + kind: Some(CompletionItemKind::FIELD), + insert_text: Some(insert_text), + insert_text_format: Some(InsertTextFormat::SNIPPET), + label_details: Some(CompletionItemLabelDetails { + description: Some(ty_to_string(&ty)), + ..Default::default() + }), + ..Default::default() + }] + } + }) + .collect(); + + Some(suggestions) +} + +#[cfg(feature = "full-compiler")] +/// Given a token, returns the type of the structure that the token is part of. +/// +/// This function traverses the CST backwards from the given token to determine +/// the full path to a field within a structure (e.g., `module.field.subfield`). +/// It then uses this path to look up the corresponding `Type` definition. +/// +/// If the token is part of a `for` or `with` statement, it will try to resolve +/// the type from the declared variables in those statements. +/// +/// Returns an `Option` representing the type of the structure or field +/// identified by the token. Returns `None` if the type cannot be determined. +fn get_struct(token: &Token) -> Option { let mut path = Vec::new(); + let mut curr = Some(token.clone()); while let Some(token) = curr { match token.kind() { SyntaxKind::IDENT => { + // If the identifier is a variable declared in a `for` or `with` + // statement, we need to find the type of that variable. + if let Some((_, declaration)) = find_declaration(&token) { + return get_type_from_declaration( + &declaration, + &token, + path.into_iter().rev(), + ); + } + path.push(Segment::Field(token.text().to_string())); // Look for previous DOT if let Some(prev) = prev_non_trivia_token(&token) { @@ -428,82 +523,113 @@ fn module_suggestions( } } } + Some(current_kind) +} - let current_struct = match current_kind { - Type::Struct(s) => s, - _ => return None, - }; - - // Now `current_struct` is the structure before the cursor. - // We want to suggest fields for this structure. - let suggestions = current_struct - .fields() - .flat_map(|f| { - let name = f.name(); - let ty = f.ty(); - - if let Type::Func(ref func_def) = ty { - func_def - .signatures - .iter() - .map(|sig| { - let arg_types = sig - .args - .iter() - .map(ty_to_string) - .collect::>(); +#[cfg(feature = "full-compiler")] +/// Resolves the `Type` of an identifier declared within `for` or `with` statements. +/// +/// This function is called when `get_struct` identifies an identifier that is +/// not a module name but rather a variable declared in a `for` or `with` expression. +/// It then attempts to deduce the type of this variable based on its declaration. +/// +/// # Arguments +/// +/// * `declaration` - The `Node` representing the `for` or `with` declaration. +/// * `ident` - The `Token` of the identifier whose type needs to be resolved. +/// * `path` - An iterator over `Segment`s representing the access path (fields, +/// array indices) applied to the declared variable. +/// +/// # Returns +/// +/// An `Option` representing the resolved type of the identifier. Returns `None` +/// if the type cannot be determined or if the access path is invalid for the type. +fn get_type_from_declaration( + declaration: &Node, + ident: &Token, + path: impl Iterator, +) -> Option { + match declaration.kind() { + SyntaxKind::WITH_EXPR => { + let with_decls = declaration + .children() + .find(|n| n.kind() == SyntaxKind::WITH_DECLS)?; - let args_template = arg_types - .iter() - .enumerate() - .map(|(n, arg_type)| { - format!("${{{}:{arg_type}}}", n + 1) - }) - .join(","); + for with_decl in with_decls.children() { + let declared_ident = with_decl.first_token()?; + if declared_ident.text() != ident.text() { + continue; + } - CompletionItem { - label: format!( - "{}({})", - name, - arg_types.join(", ") - ), - kind: Some(CompletionItemKind::METHOD), - insert_text: Some(format!( - "{name}({args_template})", - )), - insert_text_format: Some( - InsertTextFormat::SNIPPET, - ), - label_details: Some(CompletionItemLabelDetails { - description: Some(ty_to_string(&ty)), - ..Default::default() - }), - ..Default::default() + let mut current_type = get_struct(&with_decl.last_token()?)?; + + for segment in path { + match segment { + Segment::Field(name) => { + if let Type::Struct(struct_def) = current_type { + current_type = struct_def + .fields() + .find(|field| field.name() == name)? + .ty() + } else { + return None; + } } - }) - .collect() - } else { - let insert_text = match &ty { - Type::Array(_) => format!("{name}[${{1}}]${{2}}"), - _ => name.to_string(), - }; - - vec![CompletionItem { - label: name.to_string(), - kind: Some(CompletionItemKind::FIELD), - insert_text: Some(insert_text), - insert_text_format: Some(InsertTextFormat::SNIPPET), - label_details: Some(CompletionItemLabelDetails { - description: Some(ty_to_string(&ty)), - ..Default::default() - }), - ..Default::default() - }] + Segment::Index => { + if let Type::Array(inner) = current_type { + current_type = *inner + } else { + return None; + } + } + } + } + return Some(current_type); } - }) - .collect(); - - Some(suggestions) + return None; + } + SyntaxKind::FOR_EXPR => { + let colon = declaration + .children_with_tokens() + .find(|child| child.kind() == SyntaxKind::COLON)? + .into_token()?; + + let iterable_last_token = prev_non_trivia_token(&colon)?; + + let iterable_type = get_struct(&iterable_last_token)?; + + let mut current_type = match iterable_type { + Type::Array(inner) => *inner, + Type::Map(_, value) => *value, + _ => return None, + }; + + for segment in path { + match segment { + Segment::Field(name) => { + if let Type::Struct(struct_def) = current_type { + current_type = struct_def + .fields() + .find(|field| field.name() == name)? + .ty() + } else { + return None; + } + } + Segment::Index => { + if let Type::Array(inner) = current_type { + current_type = *inner + } else { + return None; + } + } + } + } + return Some(current_type); + } + _ => {} + } + None } /// Given a token that must be a closing (right) bracket, find the diff --git a/ls/src/tests/mod.rs b/ls/src/tests/mod.rs index 8d83e0cd5..071244be6 100644 --- a/ls/src/tests/mod.rs +++ b/ls/src/tests/mod.rs @@ -376,8 +376,17 @@ async fn completion() { #[cfg(all(feature = "full-compiler", not(feature = "magic-module")))] test_lsp_request::<_, Completion>("completion13.yar").await; + #[cfg(feature = "full-compiler")] test_lsp_request::<_, Completion>("completion14.yar").await; + + #[cfg(feature = "full-compiler")] test_lsp_request::<_, Completion>("completion15.yar").await; + + #[cfg(feature = "full-compiler")] + test_lsp_request::<_, Completion>("completion16.yar").await; + + #[cfg(feature = "full-compiler")] + test_lsp_request::<_, Completion>("completion17.yar").await; } #[tokio::test] diff --git a/ls/src/tests/testdata/completion16.request.json b/ls/src/tests/testdata/completion16.request.json new file mode 100644 index 000000000..6c8ea212a --- /dev/null +++ b/ls/src/tests/testdata/completion16.request.json @@ -0,0 +1,13 @@ +{ + "textDocument": { + "uri": "${test_dir}/completion16.yar" + }, + "position": { + "line": 5, + "character": 16 + }, + "context": { + "triggerKind": 2, + "triggerCharacter": "." + } +} diff --git a/ls/src/tests/testdata/completion16.response.json b/ls/src/tests/testdata/completion16.response.json new file mode 100644 index 000000000..7c282ca67 --- /dev/null +++ b/ls/src/tests/testdata/completion16.response.json @@ -0,0 +1,20 @@ +[ + { + "insertText": "virtual_address", + "insertTextFormat": 2, + "kind": 5, + "label": "virtual_address", + "labelDetails": { + "description": "integer" + } + }, + { + "insertText": "size", + "insertTextFormat": 2, + "kind": 5, + "label": "size", + "labelDetails": { + "description": "integer" + } + } +] \ No newline at end of file diff --git a/ls/src/tests/testdata/completion16.yar b/ls/src/tests/testdata/completion16.yar new file mode 100644 index 000000000..af939ba1a --- /dev/null +++ b/ls/src/tests/testdata/completion16.yar @@ -0,0 +1,8 @@ +import "pe" + +rule test_with { + condition: + with foo = pe.data_directories[0]: ( + foo. + ) +} diff --git a/ls/src/tests/testdata/completion17.request.json b/ls/src/tests/testdata/completion17.request.json new file mode 100644 index 000000000..dc1dd20a5 --- /dev/null +++ b/ls/src/tests/testdata/completion17.request.json @@ -0,0 +1,13 @@ +{ + "textDocument": { + "uri": "${test_dir}/completion17.yar" + }, + "position": { + "line": 5, + "character": 16 + }, + "context": { + "triggerKind": 2, + "triggerCharacter": "." + } +} diff --git a/ls/src/tests/testdata/completion17.response.json b/ls/src/tests/testdata/completion17.response.json new file mode 100644 index 000000000..7c282ca67 --- /dev/null +++ b/ls/src/tests/testdata/completion17.response.json @@ -0,0 +1,20 @@ +[ + { + "insertText": "virtual_address", + "insertTextFormat": 2, + "kind": 5, + "label": "virtual_address", + "labelDetails": { + "description": "integer" + } + }, + { + "insertText": "size", + "insertTextFormat": 2, + "kind": 5, + "label": "size", + "labelDetails": { + "description": "integer" + } + } +] \ No newline at end of file diff --git a/ls/src/tests/testdata/completion17.yar b/ls/src/tests/testdata/completion17.yar new file mode 100644 index 000000000..7688494f8 --- /dev/null +++ b/ls/src/tests/testdata/completion17.yar @@ -0,0 +1,8 @@ +import "pe" + +rule test_for { + condition: + for any dir in pe.data_directories: ( + dir. + ) +} diff --git a/parser/src/parser/mod.rs b/parser/src/parser/mod.rs index bf0e9ce7b..2eb554d26 100644 --- a/parser/src/parser/mod.rs +++ b/parser/src/parser/mod.rs @@ -133,9 +133,10 @@ pub(crate) struct ParserImpl<'src> { #[cfg(feature = "logging")] depth: usize, - /// Hash map where keys are spans within the source code, and values - /// are tuples containing the ID of the token actually found and a list of - /// tokens that were expected to match at that span. + /// Hash map where keys are tuples (Span, TokenId) identifying a token in + /// the source code (the Span and the TokenId correspond to the same token), + /// and values are lists of tokens that were expected to match at that + /// span. /// /// This hash map plays a crucial role in error reporting during parsing. /// Consider the following grammar rule: @@ -146,7 +147,7 @@ pub(crate) struct ParserImpl<'src> { /// can be represented (conceptually, not actual code) as: /// /// ```text - /// self.start(A) + /// self.begin(A) /// .opt(|p| p.expect(a)) /// .expect(b) /// .end() @@ -169,7 +170,7 @@ pub(crate) struct ParserImpl<'src> { /// the position of `c`. When `expect(b)` fails later, the parser looks up /// any other token (besides `b`) that were expected to match at the /// position and produces a comprehensive error message. - expected_token_errors: FxHashMap)>, + expected_token_errors: FxHashMap<(Span, TokenId), IndexSet<&'static str>>, /// Similar to `expected_token_errors` but tracks the positions where /// unexpected tokens were found. This type of error is produced when @@ -434,6 +435,9 @@ impl<'src> ParserImpl<'src> { self.depth -= 1; } if matches!(self.state, State::Failure | State::OutOfFuel) { + if self.opt_depth == 0 { + self.handle_errors(); + } self.output.end_with_error(); } else { self.output.end(); @@ -441,9 +445,20 @@ impl<'src> ParserImpl<'src> { self } - /// Recovers from errors by discarding any token until finding - /// one that is in `recovery_set`. + /// Recovers from errors and discards tokens until finding one that is in + /// `recovery_set`. + /// + /// If the parser is in [`State::Failure`], it will go back to [`State::OK`]. + /// Any token that is not in `recovery_set` will be consumed in put into an + /// `ERROR` node, regardless of the initial parser state. fn recover(&mut self, recovery_set: &'static TokenSet) -> &mut Self { + // If parser is out-of-fuel there's nothing to recover from. + if matches!(self.state, State::OutOfFuel) { + return self; + } + + let state_was_ok = matches!(self.state, State::OK); + self.set_state(State::OK); let token = match self.peek_non_trivia() { @@ -451,46 +466,46 @@ impl<'src> ParserImpl<'src> { None => return self, }; - if recovery_set.contains(token).is_some() { - return self; - } - let token_span = token.span(); let token_id = token.id(); - - self.trivia(); - self.begin(ERROR); - self.bump(); + let token_in_recovery_set = recovery_set.contains(token).is_some(); // If no previous error exists, create an error that tells that we are - // expecting any of the tokens in the recovery set. If there were - // previous errors, flush them and don't produce new ones. + // expecting any of the tokens in the recovery set. if self.pending_errors.is_empty() { - let (actual_token_id, expected) = - self.expected_token_errors.entry(token_span).or_default(); - - *actual_token_id = token_id; - - expected.extend( - recovery_set.token_ids().map(|token| token.description()), - ); + let expected = self + .expected_token_errors + .entry((token_span, token_id)) + .or_default(); + + if !token_in_recovery_set { + expected.extend( + recovery_set.token_ids().map(|token| token.description()), + ); + } + } + if !state_was_ok || !token_in_recovery_set { self.handle_errors(); - } else { - self.flush_errors(); } // Consume any token that is not in the recovery set. - while let Some(token) = self.peek_non_trivia() { - if recovery_set.contains(token).is_some() { - break; - } else { - self.trivia(); - self.bump(); + if !token_in_recovery_set { + self.trivia(); + self.begin(ERROR); + self.bump(); + + while let Some(token) = self.peek_non_trivia() { + if recovery_set.contains(token).is_some() { + break; + } else { + self.trivia(); + self.bump(); + } } - } - self.end(); + self.end(); + } self } @@ -538,20 +553,18 @@ impl<'src> ParserImpl<'src> { return self; } - let (token_id, token_match, token_span) = match self.peek_non_trivia() + let (token_span, token_id, token_match) = match self.peek_non_trivia() { None => { // Special case when the end of the source is reached. The span // used for error reporting is a zero-length span pointing to // the end of the code. let last = self.tokens.source().len() as u32; - (None, None, Span(last..last)) + (Span(last..last), TokenId::UNKNOWN, None) + } + Some(token) => { + (token.span(), token.id(), expected_tokens.contains(token)) } - Some(token) => ( - Some(token.id()), - expected_tokens.contains(token), - token.span(), - ), }; match (self.not_depth, token_match) { @@ -560,15 +573,14 @@ impl<'src> ParserImpl<'src> { // actually means that the token was *not* expected. (not_depth, Some(_)) if not_depth > 0 => { self.unexpected_token_errors.insert(token_span); - self.handle_errors() } // We are not inside a "not", and the expected token was // not found. (0, None) => { - let (actual_token_id, expected) = - self.expected_token_errors.entry(token_span).or_default(); - - *actual_token_id = token_id.unwrap_or(TokenId::UNKNOWN); + let expected = self + .expected_token_errors + .entry((token_span, token_id)) + .or_default(); if let Some(description) = description { expected.insert(description); @@ -579,8 +591,6 @@ impl<'src> ParserImpl<'src> { .map(|token| token.description()), ); } - - self.handle_errors(); } _ => {} } @@ -592,13 +602,6 @@ impl<'src> ParserImpl<'src> { // Consume the expected token. let token = self.tokens.next_token().unwrap(); self.output.push_token(*t, token.span()); - // After matching a token that is not inside an "optional" branch - // in the grammar, it's guaranteed that the parser won't go back - // to a position at the left of the matched token. This is a good - // opportunity for flushing errors. - if self.opt_depth == 0 { - self.flush_errors() - } } else { self.set_state(State::Failure); } @@ -733,13 +736,11 @@ impl<'src> ParserImpl<'src> { let token_span = token.span(); let token_id = token.id(); - let (actual_token, expected) = self + let expected = self .expected_token_errors - .entry(token_span) + .entry((token_span, token_id)) .or_default(); - *actual_token = token_id; - expected.extend( expected_tokens .token_ids() @@ -865,24 +866,19 @@ impl<'src> ParserImpl<'src> { } fn flush_errors(&mut self) { - self.expected_token_errors.clear(); - self.unexpected_token_errors.clear(); for (span, error) in self.pending_errors.drain(0..) { self.output.push_error(error, span); } } fn handle_errors(&mut self) { - if self.opt_depth > 0 { - return; - } // From all errors in expected_token_errors, use the one at the largest // offset. If several errors start at the same offset, the last one is // used. let expected_token = self .expected_token_errors .drain() - .max_by_key(|(span, _)| span.start()); + .max_by_key(|((span, _), _)| span.start()); // From all errors in unexpected_token_errors, use the one at the // largest offset. If several errors start at the same offset, the last @@ -893,9 +889,11 @@ impl<'src> ParserImpl<'src> { .max_by_key(|span| span.start()); let (span, expected) = match (expected_token, unexpected_token) { - (Some((e, _)), Some(u)) if u.start() > e.start() => (u, None), + (Some(((e, _), _)), Some(u)) if u.start() > e.start() => (u, None), (None, Some(u)) => (u, None), - (Some((e, expected)), _) => (e, Some(expected)), + (Some(((e, token_id), expected)), _) => { + (e, Some((token_id, expected))) + } (None, None) => return, }; @@ -1104,14 +1102,14 @@ impl ParserImpl<'_> { /// ``` fn rule_decl(&mut self) -> &mut Self { self.begin(RULE_DECL) - .opt(|p| p.rule_mods()) + .opt(Self::rule_mods) .expect(t!(RULE_KW)) .expect(t!(IDENT)) - .if_next(t!(COLON), |p| p.rule_tags()) + .if_next(t!(COLON), Self::rule_tags) .expect(t!(L_BRACE)) - .if_next(t!(META_KW), |p| p.meta_blk()) - .if_next(t!(STRINGS_KW), |p| p.patterns_blk()) - .condition_blk() + .if_next(t!(META_KW), Self::meta_blk) + .if_next(t!(STRINGS_KW), Self::patterns_blk) + .then(Self::condition_blk) .expect(t!(R_BRACE)) .end() .recover(t!(GLOBAL_KW @@ -1157,7 +1155,7 @@ impl ParserImpl<'_> { self.begin(META_BLK) .expect(t!(META_KW)) .expect(t!(COLON)) - .one_or_more(|p| p.meta_def()) + .one_or_more(Self::meta_def) .end() .recover(t!(STRINGS_KW | CONDITION_KW)) } @@ -1195,7 +1193,7 @@ impl ParserImpl<'_> { self.begin(PATTERNS_BLK) .expect(t!(STRINGS_KW)) .expect(t!(COLON)) - .one_or_more(|p| p.pattern_def()) + .one_or_more(Self::pattern_def) .end() .recover(t!(CONDITION_KW)) } @@ -1216,9 +1214,9 @@ impl ParserImpl<'_> { .begin_alt() .alt(|p| p.expect(t!(STRING_LIT))) .alt(|p| p.expect(t!(REGEXP))) - .alt(|p| p.hex_pattern()) + .alt(Self::hex_pattern) .end_alt() - .opt(|p| p.pattern_mods()) + .opt(Self::pattern_mods) .end() } @@ -1228,7 +1226,7 @@ impl ParserImpl<'_> { /// PATTERN_MODS := PATTERN_MOD+ /// `` fn pattern_mods(&mut self) -> &mut Self { - self.begin(PATTERN_MODS).one_or_more(|p| p.pattern_mod()).end() + self.begin(PATTERN_MODS).one_or_more(Self::pattern_mod).end() } /// Parses a pattern modifier. @@ -1289,7 +1287,7 @@ impl ParserImpl<'_> { self.begin(HEX_PATTERN) .expect(t!(L_BRACE)) .enter_hex_pattern_mode() - .hex_sub_pattern() + .then(Self::hex_sub_pattern) .expect(t!(R_BRACE)) .end() } @@ -1304,13 +1302,13 @@ impl ParserImpl<'_> { self.begin(HEX_SUB_PATTERN) .begin_alt() .alt(|p| p.expect(t!(HEX_BYTE))) - .alt(|p| p.hex_alternative()) + .alt(Self::hex_alternative) .end_alt() .zero_or_more(|p| { - p.zero_or_more(|p| p.hex_jump()) + p.zero_or_more(Self::hex_jump) .begin_alt() .alt(|p| p.expect(t!(HEX_BYTE))) - .alt(|p| p.hex_alternative()) + .alt(Self::hex_alternative) .end_alt() }) .end() @@ -1324,8 +1322,8 @@ impl ParserImpl<'_> { fn hex_alternative(&mut self) -> &mut Self { self.begin(HEX_ALTERNATIVE) .expect(t!(L_PAREN)) - .hex_sub_pattern() - .zero_or_more(|p| p.expect(t!(PIPE)).hex_sub_pattern()) + .then(Self::hex_sub_pattern) + .zero_or_more(|p| p.expect(t!(PIPE)).then(Self::hex_sub_pattern)) .expect(t!(R_PAREN)) .end() } @@ -1372,9 +1370,10 @@ impl ParserImpl<'_> { /// `` fn boolean_expr(&mut self) -> &mut Self { self.begin(BOOLEAN_EXPR) - .boolean_term() + .then(Self::boolean_term) .zero_or_more(|p| { - p.expect_d(t!(AND_KW | OR_KW), Some("operator")).boolean_term() + p.expect_d(t!(AND_KW | OR_KW), Some("operator")) + .then(Self::boolean_term) }) .end() } @@ -1383,10 +1382,15 @@ impl ParserImpl<'_> { /// /// ```text /// BOOLEAN_TERM := ( + /// PATTERN_IDENT ( 'at' EXPR | 'in' RANGE )? /// `true` | /// `false` | /// `not` BOOLEAN_TERM | /// `defined` BOOLEAN_TERM | + /// FOR_EXPR + /// OF_EXPR + /// WITH_EXPR + /// EXPR ( comparison_op EXPR )* /// `(` BOOLEAN_EXPR `)` /// ) /// `` @@ -1396,21 +1400,17 @@ impl ParserImpl<'_> { self.begin(BOOLEAN_TERM) .begin_alt() .alt(|p| { - p.expect_d(t!(PATTERN_IDENT), DESC).if_next( - t!(AT_KW | IN_KW), - |p| { - p.begin_alt() - .alt(|p| p.expect(t!(AT_KW)).expr()) - .alt(|p| p.expect(t!(IN_KW)).range()) - .end_alt() - }, - ) + p.expect_d(t!(PATTERN_IDENT), DESC) + .if_next(t!(AT_KW | IN_KW), Self::at_or_in) }) .alt(|p| p.expect_d(t!(TRUE_KW | FALSE_KW), DESC)) - .alt(|p| p.expect_d(t!(NOT_KW | DEFINED_KW), DESC).boolean_term()) - .alt(|p| p.for_expr()) - .alt(|p| p.of_expr()) - .alt(|p| p.with_expr()) + .alt(|p| { + p.expect_d(t!(NOT_KW | DEFINED_KW), DESC) + .then(Self::boolean_term) + }) + .alt(Self::for_expr) + .alt(Self::of_expr) + .alt(Self::with_expr) .alt(|p| { p.expr().zero_or_more(|p| { p.expect_d( @@ -1430,18 +1430,25 @@ impl ParserImpl<'_> { | MATCHES_KW), DESC, ) - .expr() + .then(Self::expr) }) }) .alt(|p| { p.expect_d(t!(L_PAREN), DESC) - .boolean_expr() + .then(Self::boolean_expr) .expect(t!(R_PAREN)) }) .end_alt() .end() } + fn at_or_in(&mut self) -> &mut Self { + self.begin_alt() + .alt(|p| p.expect(t!(AT_KW)).then(Self::expr)) + .alt(|p| p.expect(t!(IN_KW)).then(Self::range)) + .end_alt() + } + /// Parses an expression. /// /// ```text @@ -1452,7 +1459,7 @@ impl ParserImpl<'_> { fn expr(&mut self) -> &mut Self { self.cached(EXPR, |p| { p.begin(EXPR) - .term() + .then(Self::term) .zero_or_more(|p| { p.expect_d( t!(ADD @@ -1467,7 +1474,7 @@ impl ParserImpl<'_> { | BITWISE_XOR), Some("operator"), ) - .term() + .then(Self::term) }) .end() }) @@ -1483,8 +1490,9 @@ impl ParserImpl<'_> { .expect(t!(IDENT)) .expect(t!(L_PAREN)) .opt(|p| { - p.boolean_expr() - .zero_or_more(|p| p.expect(t!(COMMA)).boolean_expr()) + p.boolean_expr().zero_or_more(|p| { + p.expect(t!(COMMA)).then(Self::boolean_expr) + }) }) .expect(t!(R_PAREN)) .end() @@ -1498,10 +1506,10 @@ impl ParserImpl<'_> { fn range(&mut self) -> &mut Self { self.begin(RANGE) .expect(t!(L_PAREN)) - .expr() + .then(Self::expr) .expect(t!(DOT)) .expect(t!(DOT)) - .expr() + .then(Self::expr) .expect(t!(R_PAREN)) .end() } @@ -1544,7 +1552,7 @@ impl ParserImpl<'_> { }) .alt(|p| { p.expect_d(t!(PATTERN_COUNT), DESC) - .cond(t!(IN_KW), |p| p.range()) + .cond(t!(IN_KW), Self::range) }) .alt(|p| { p.expect_d(t!(PATTERN_OFFSET | PATTERN_LENGTH), DESC) @@ -1552,14 +1560,16 @@ impl ParserImpl<'_> { p.expr().expect(t!(R_BRACKET)) }) }) - .alt(|p| p.expect_d(t!(MINUS), DESC).term()) - .alt(|p| p.expect_d(t!(BITWISE_NOT), DESC).term()) + .alt(|p| p.expect_d(t!(MINUS), DESC).then(Self::term)) + .alt(|p| p.expect_d(t!(BITWISE_NOT), DESC).then(Self::term)) .alt(|p| { - p.expect_d(t!(L_PAREN), DESC).expr().expect(t!(R_PAREN)) + p.expect_d(t!(L_PAREN), DESC) + .then(Self::expr) + .expect(t!(R_PAREN)) }) .alt(|p| { p.primary_expr().zero_or_more(|p| { - p.expect_d(t!(DOT), DESC).primary_expr() + p.expect_d(t!(DOT), DESC).then(Self::primary_expr) }) }) .end_alt() @@ -1578,11 +1588,11 @@ impl ParserImpl<'_> { fn primary_expr(&mut self) -> &mut Self { self.begin(PRIMARY_EXPR) .begin_alt() - .alt(|p| p.func_call()) + .alt(Self::func_call) .alt(|p| { p.expect(t!(IDENT)) .expect(t!(L_BRACKET)) - .expr() + .then(Self::expr) .expect(t![R_BRACKET]) }) .alt(|p| p.expect(t!(IDENT))) @@ -1602,13 +1612,13 @@ impl ParserImpl<'_> { fn for_expr(&mut self) -> &mut Self { self.begin(FOR_EXPR) .expect_d(t!(FOR_KW), Some("expression")) - .quantifier() + .then(Self::quantifier) .begin_alt() .alt(|p| { p.expect(t!(OF_KW)) .begin_alt() .alt(|p| p.expect(t!(THEM_KW))) - .alt(|p| p.pattern_ident_tuple()) + .alt(Self::pattern_ident_tuple) .end_alt() }) .alt(|p| { @@ -1620,7 +1630,7 @@ impl ParserImpl<'_> { .end_alt() .expect(t!(COLON)) .expect(t!(L_PAREN)) - .boolean_expr() + .then(|p| p.boolean_expr().recover(t!(R_PAREN))) .expect(t!(R_PAREN)) .end() } @@ -1635,20 +1645,15 @@ impl ParserImpl<'_> { /// `` fn of_expr(&mut self) -> &mut Self { self.begin(OF_EXPR) - .quantifier() + .then(Self::quantifier) .expect(t!(OF_KW)) .begin_alt() .alt(|p| { p.begin_alt() .alt(|p| p.expect(t!(THEM_KW))) - .alt(|p| p.pattern_ident_tuple()) + .alt(Self::pattern_ident_tuple) .end_alt() - .if_next(t!(AT_KW | IN_KW), |p| { - p.begin_alt() - .alt(|p| p.expect(t!(AT_KW)).expr()) - .alt(|p| p.expect(t!(IN_KW)).range()) - .end_alt() - }) + .if_next(t!(AT_KW | IN_KW), Self::at_or_in) }) .alt(|p| { p.boolean_expr_tuple().not(|p| p.expect(t!(AT_KW | IN_KW))) @@ -1665,10 +1670,10 @@ impl ParserImpl<'_> { fn with_expr(&mut self) -> &mut Self { self.begin(WITH_EXPR) .expect_d(t!(WITH_KW), Some("expression")) - .with_declarations() + .then(Self::with_declarations) .expect(t!(COLON)) .expect(t!(L_PAREN)) - .boolean_expr() + .then(|p| p.boolean_expr().recover(t!(R_PAREN))) .expect(t!(R_PAREN)) .end() } @@ -1680,9 +1685,10 @@ impl ParserImpl<'_> { /// fn with_declarations(&mut self) -> &mut Self { self.begin(WITH_DECLS) - .with_declaration() + .then(Self::with_declaration) .zero_or_more(|p| p.expect(t!(COMMA)).with_declaration()) .end() + .recover(t!(COLON | L_PAREN)) } /// Parses a `with` declaration. @@ -1691,7 +1697,11 @@ impl ParserImpl<'_> { /// WITH_DECL := IDENT `=` EXPR /// ``` fn with_declaration(&mut self) -> &mut Self { - self.begin(WITH_DECL).expect(t!(IDENT)).expect(t!(EQUAL)).expr().end() + self.begin(WITH_DECL) + .expect(t!(IDENT)) + .expect(t!(EQUAL)) + .then(Self::expr) + .end() } /// Parses quantifier. @@ -1733,9 +1743,9 @@ impl ParserImpl<'_> { fn iterable(&mut self) -> &mut Self { self.begin(ITERABLE) .begin_alt() - .alt(|p| p.range()) - .alt(|p| p.expr_tuple()) - .alt(|p| p.expr()) + .alt(Self::range) + .alt(Self::expr_tuple) + .alt(Self::expr) .end_alt() .end() } @@ -1748,8 +1758,8 @@ impl ParserImpl<'_> { fn boolean_expr_tuple(&mut self) -> &mut Self { self.begin(BOOLEAN_EXPR_TUPLE) .expect(t!(L_PAREN)) - .boolean_expr() - .zero_or_more(|p| p.expect(t!(COMMA)).boolean_expr()) + .then(Self::boolean_expr) + .zero_or_more(|p| p.expect(t!(COMMA)).then(Self::boolean_expr)) .expect(t!(R_PAREN)) .end() } @@ -1762,8 +1772,8 @@ impl ParserImpl<'_> { fn expr_tuple(&mut self) -> &mut Self { self.begin(EXPR_TUPLE) .expect(t!(L_PAREN)) - .expr() - .zero_or_more(|p| p.expect(t!(COMMA)).expr()) + .then(Self::expr) + .zero_or_more(|p| p.expect(t!(COMMA)).then(Self::expr)) .expect(t!(R_PAREN)) .end() } @@ -1826,19 +1836,19 @@ struct Alt<'a, 'src> { } impl<'a, 'src> Alt<'a, 'src> { - fn alt(mut self, f: F) -> Self + fn alt

(mut self, parser: P) -> Self where - F: Fn(&'a mut ParserImpl<'src>) -> &'a mut ParserImpl<'src>, + P: Fn(&'a mut ParserImpl<'src>) -> &'a mut ParserImpl<'src>, { if matches!(self.parser.state, State::Failure | State::OutOfFuel) { return self; } - // Don't try to match the current alternative if the parser a previous - // one already matched. + // Don't try to match the current alternative if a previous one already + // matched. if !self.matched { self.parser.trivia(); self.parser.opt_depth += 1; - self.parser = f(self.parser); + self.parser = parser(self.parser); self.parser.opt_depth -= 1; match self.parser.state { // The current alternative matched. @@ -1865,7 +1875,6 @@ impl<'a, 'src> Alt<'a, 'src> { self.parser.set_state(State::OK); } else { self.parser.set_state(State::Failure); - self.parser.handle_errors(); }; self.parser } diff --git a/parser/src/parser/tests/testdata/basic-error-1.cststream b/parser/src/parser/tests/testdata/basic-error-1.cststream index 21807e370..be40bf165 100644 --- a/parser/src/parser/tests/testdata/basic-error-1.cststream +++ b/parser/src/parser/tests/testdata/basic-error-1.cststream @@ -7,7 +7,6 @@ Token { kind: WHITESPACE, span: Span(9..10) } Token { kind: L_BRACE, span: Span(10..11) } Begin { kind: ERROR, span: Span(11..56) } Token { kind: L_BRACE, span: Span(11..12) } -Error { message: "expecting `meta`, `strings` or `condition`, found `{`", span: Span(11..12) } Token { kind: NEWLINE, span: Span(12..13) } Token { kind: WHITESPACE, span: Span(13..15) } Token { kind: META_KW, span: Span(15..19) } @@ -30,4 +29,5 @@ End { kind: ERROR, span: Span(11..56) } Token { kind: NEWLINE, span: Span(56..57) } Token { kind: R_BRACE, span: Span(57..58) } End { kind: RULE_DECL, span: Span(0..58) } +Error { message: "expecting `meta`, `strings` or `condition`, found `{`", span: Span(11..12) } End { kind: SOURCE_FILE, span: Span(0..58) } diff --git a/parser/src/parser/tests/testdata/basic-error-2.cststream b/parser/src/parser/tests/testdata/basic-error-2.cststream index 8c4ef8e88..113a8233e 100644 --- a/parser/src/parser/tests/testdata/basic-error-2.cststream +++ b/parser/src/parser/tests/testdata/basic-error-2.cststream @@ -21,7 +21,6 @@ End { kind: ERROR, span: Span(14..32) } Token { kind: WHITESPACE, span: Span(32..33) } Begin { kind: ERROR, span: Span(33..37) } Token { kind: UNKNOWN, span: Span(33..37) } -Error { message: "unclosed literal string", span: Span(33..37) } End { kind: ERROR, span: Span(33..37) } Token { kind: NEWLINE, span: Span(37..38) } Token { kind: WHITESPACE, span: Span(38..40) } @@ -39,4 +38,5 @@ End { kind: CONDITION_BLK, span: Span(40..60) } Token { kind: NEWLINE, span: Span(60..61) } Token { kind: R_BRACE, span: Span(61..62) } End { kind: RULE_DECL, span: Span(0..62) } +Error { message: "unclosed literal string", span: Span(33..37) } End { kind: SOURCE_FILE, span: Span(0..62) } diff --git a/parser/src/parser/tests/testdata/basic-error-4.cst b/parser/src/parser/tests/testdata/basic-error-4.cst index 0dc1382e0..a97bf119d 100644 --- a/parser/src/parser/tests/testdata/basic-error-4.cst +++ b/parser/src/parser/tests/testdata/basic-error-4.cst @@ -1,15 +1,14 @@ SOURCE_FILE@0..26 - RULE_DECL@0..26 + ERROR@0..26 RULE_KW@0..4 "rule" - ERROR@4..24 - L_BRACE@4..5 "{" - NEWLINE@5..6 "\n" - WHITESPACE@6..7 "\t" - CONDITION_KW@7..16 "condition" - COLON@16..17 ":" - NEWLINE@17..18 "\n" - WHITESPACE@18..20 "\t\t" - TRUE_KW@20..24 "true" + L_BRACE@4..5 "{" + NEWLINE@5..6 "\n" + WHITESPACE@6..7 "\t" + CONDITION_KW@7..16 "condition" + COLON@16..17 ":" + NEWLINE@17..18 "\n" + WHITESPACE@18..20 "\t\t" + TRUE_KW@20..24 "true" NEWLINE@24..25 "\n" R_BRACE@25..26 "}" diff --git a/parser/src/parser/tests/testdata/basic-error-4.cststream b/parser/src/parser/tests/testdata/basic-error-4.cststream index e23d01076..e5bc94814 100644 --- a/parser/src/parser/tests/testdata/basic-error-4.cststream +++ b/parser/src/parser/tests/testdata/basic-error-4.cststream @@ -1,9 +1,7 @@ Begin { kind: SOURCE_FILE, span: Span(0..26) } -Begin { kind: RULE_DECL, span: Span(0..26) } +Begin { kind: ERROR, span: Span(0..26) } Token { kind: RULE_KW, span: Span(0..4) } -Begin { kind: ERROR, span: Span(4..24) } Token { kind: L_BRACE, span: Span(4..5) } -Error { message: "expecting identifier, found `{`", span: Span(4..5) } Token { kind: NEWLINE, span: Span(5..6) } Token { kind: WHITESPACE, span: Span(6..7) } Token { kind: CONDITION_KW, span: Span(7..16) } @@ -11,8 +9,8 @@ Token { kind: COLON, span: Span(16..17) } Token { kind: NEWLINE, span: Span(17..18) } Token { kind: WHITESPACE, span: Span(18..20) } Token { kind: TRUE_KW, span: Span(20..24) } -End { kind: ERROR, span: Span(4..24) } Token { kind: NEWLINE, span: Span(24..25) } Token { kind: R_BRACE, span: Span(25..26) } -End { kind: RULE_DECL, span: Span(0..26) } +End { kind: ERROR, span: Span(0..26) } +Error { message: "expecting identifier, found `{`", span: Span(4..5) } End { kind: SOURCE_FILE, span: Span(0..26) } diff --git a/parser/src/parser/tests/testdata/basic-error-5.cststream b/parser/src/parser/tests/testdata/basic-error-5.cststream index a028d0119..8f37bfa2e 100644 --- a/parser/src/parser/tests/testdata/basic-error-5.cststream +++ b/parser/src/parser/tests/testdata/basic-error-5.cststream @@ -6,6 +6,6 @@ Token { kind: IDENT, span: Span(5..9) } Token { kind: WHITESPACE, span: Span(9..10) } Token { kind: L_BRACE, span: Span(10..11) } Token { kind: R_BRACE, span: Span(11..12) } -Error { message: "expecting `meta`, `strings` or `condition`, found `}`", span: Span(11..12) } End { kind: RULE_DECL, span: Span(0..12) } +Error { message: "expecting `meta`, `strings` or `condition`, found `}`", span: Span(11..12) } End { kind: SOURCE_FILE, span: Span(0..12) } diff --git a/parser/src/parser/tests/testdata/bitwise-ops-error-1.cststream b/parser/src/parser/tests/testdata/bitwise-ops-error-1.cststream index 03a07b525..5779a7c4f 100644 --- a/parser/src/parser/tests/testdata/bitwise-ops-error-1.cststream +++ b/parser/src/parser/tests/testdata/bitwise-ops-error-1.cststream @@ -32,6 +32,6 @@ End { kind: ERROR, span: Span(30..33) } End { kind: CONDITION_BLK, span: Span(13..33) } Token { kind: NEWLINE, span: Span(33..34) } Token { kind: R_BRACE, span: Span(34..35) } -Error { message: "expecting `(`, `[`, expression, `%`, operator, `of` or `}`, found `~`", span: Span(30..31) } End { kind: RULE_DECL, span: Span(0..35) } +Error { message: "expecting `(`, `[`, expression, `%`, operator, `of` or `}`, found `~`", span: Span(30..31) } End { kind: SOURCE_FILE, span: Span(0..35) } diff --git a/parser/src/parser/tests/testdata/expr-error-1.cststream b/parser/src/parser/tests/testdata/expr-error-1.cststream index 5ad8a043c..97380baab 100644 --- a/parser/src/parser/tests/testdata/expr-error-1.cststream +++ b/parser/src/parser/tests/testdata/expr-error-1.cststream @@ -34,8 +34,8 @@ End { kind: ERROR, span: Span(34..36) } End { kind: CONDITION_BLK, span: Span(15..36) } Token { kind: NEWLINE, span: Span(36..37) } Token { kind: R_BRACE, span: Span(37..38) } -Error { message: "expecting expression or identifier, found `}`", span: Span(37..38) } End { kind: RULE_DECL, span: Span(0..38) } +Error { message: "expecting expression or identifier, found `}`", span: Span(37..38) } Token { kind: NEWLINE, span: Span(38..39) } Token { kind: NEWLINE, span: Span(39..40) } Begin { kind: RULE_DECL, span: Span(40..81) } @@ -74,8 +74,8 @@ End { kind: ERROR, span: Span(70..79) } End { kind: CONDITION_BLK, span: Span(55..79) } Token { kind: NEWLINE, span: Span(79..80) } Token { kind: R_BRACE, span: Span(80..81) } -Error { message: "expecting expression or identifier, found `+`", span: Span(71..72) } End { kind: RULE_DECL, span: Span(40..81) } +Error { message: "expecting expression or identifier, found `+`", span: Span(71..72) } Token { kind: NEWLINE, span: Span(81..82) } Token { kind: NEWLINE, span: Span(82..83) } Begin { kind: RULE_DECL, span: Span(83..121) } @@ -111,8 +111,8 @@ End { kind: ERROR, span: Span(113..119) } End { kind: CONDITION_BLK, span: Span(98..119) } Token { kind: NEWLINE, span: Span(119..120) } Token { kind: R_BRACE, span: Span(120..121) } -Error { message: "expecting expression or identifier, found `==`", span: Span(115..117) } End { kind: RULE_DECL, span: Span(83..121) } +Error { message: "expecting expression or identifier, found `==`", span: Span(115..117) } Token { kind: NEWLINE, span: Span(121..122) } Token { kind: NEWLINE, span: Span(122..123) } Begin { kind: RULE_DECL, span: Span(123..167) } @@ -154,8 +154,8 @@ End { kind: ERROR, span: Span(162..165) } End { kind: CONDITION_BLK, span: Span(138..165) } Token { kind: NEWLINE, span: Span(165..166) } Token { kind: R_BRACE, span: Span(166..167) } -Error { message: "expecting expression, `%`, operator, `of` or `}`, found `(`", span: Span(162..163) } End { kind: RULE_DECL, span: Span(123..167) } +Error { message: "expecting expression, `%`, operator, `of` or `}`, found `(`", span: Span(162..163) } Token { kind: NEWLINE, span: Span(167..168) } Token { kind: NEWLINE, span: Span(168..169) } Begin { kind: RULE_DECL, span: Span(169..250) } @@ -213,7 +213,7 @@ End { kind: ERROR, span: Span(238..248) } End { kind: CONDITION_BLK, span: Span(214..248) } Token { kind: NEWLINE, span: Span(248..249) } Token { kind: R_BRACE, span: Span(249..250) } -Error { message: "expecting operator or `}`, found `in`", span: Span(238..240) } End { kind: RULE_DECL, span: Span(169..250) } +Error { message: "expecting operator or `}`, found `in`", span: Span(238..240) } Token { kind: NEWLINE, span: Span(250..251) } End { kind: SOURCE_FILE, span: Span(0..251) } diff --git a/parser/src/parser/tests/testdata/for-error-2.ast b/parser/src/parser/tests/testdata/for-error-2.ast new file mode 100644 index 000000000..37fdbbc92 --- /dev/null +++ b/parser/src/parser/tests/testdata/for-error-2.ast @@ -0,0 +1,2 @@ +ERRORS: +- SyntaxError { message: "expecting identifier, found `)`", span: Span(95..96) } diff --git a/parser/src/parser/tests/testdata/for-error-2.cst b/parser/src/parser/tests/testdata/for-error-2.cst new file mode 100644 index 000000000..07f021bd9 --- /dev/null +++ b/parser/src/parser/tests/testdata/for-error-2.cst @@ -0,0 +1,66 @@ +SOURCE_FILE@0..98 + RULE_DECL@0..98 + RULE_KW@0..4 "rule" + WHITESPACE@4..5 " " + IDENT@5..9 "test" + WHITESPACE@9..10 " " + L_BRACE@10..11 "{" + NEWLINE@11..12 "\n" + WHITESPACE@12..14 " " + PATTERNS_BLK@14..52 + STRINGS_KW@14..21 "strings" + COLON@21..22 ":" + NEWLINE@22..23 "\n" + WHITESPACE@23..27 " " + PATTERN_DEF@27..37 + PATTERN_IDENT@27..29 "$a" + WHITESPACE@29..30 " " + EQUAL@30..31 "=" + WHITESPACE@31..32 " " + STRING_LIT@32..37 "\"foo\"" + NEWLINE@37..38 "\n" + WHITESPACE@38..42 " " + PATTERN_DEF@42..52 + PATTERN_IDENT@42..44 "$b" + WHITESPACE@44..45 " " + EQUAL@45..46 "=" + WHITESPACE@46..47 " " + STRING_LIT@47..52 "\"bar\"" + NEWLINE@52..53 "\n" + WHITESPACE@53..55 " " + CONDITION_BLK@55..96 + CONDITION_KW@55..64 "condition" + COLON@64..65 ":" + NEWLINE@65..66 "\n" + WHITESPACE@66..70 " " + BOOLEAN_EXPR@70..96 + BOOLEAN_TERM@70..96 + FOR_EXPR@70..96 + FOR_KW@70..73 "for" + WHITESPACE@73..74 " " + QUANTIFIER@74..77 + ALL_KW@74..77 "all" + WHITESPACE@77..78 " " + OF_KW@78..80 "of" + WHITESPACE@80..81 " " + THEM_KW@81..85 "them" + WHITESPACE@85..86 " " + COLON@86..87 ":" + WHITESPACE@87..88 " " + L_PAREN@88..89 "(" + WHITESPACE@89..90 " " + BOOLEAN_EXPR@90..93 + BOOLEAN_TERM@90..93 + EXPR@90..93 + TERM@90..93 + PRIMARY_EXPR@90..93 + IDENT@90..93 "foo" + ERROR@93..94 + DOT@93..94 "." + WHITESPACE@94..95 " " + R_PAREN@95..96 ")" + NEWLINE@96..97 "\n" + R_BRACE@97..98 "}" + +ERRORS: +- [95..96]: expecting identifier, found `)` diff --git a/parser/src/parser/tests/testdata/for-error-2.cststream b/parser/src/parser/tests/testdata/for-error-2.cststream new file mode 100644 index 000000000..9f2d5ffc4 --- /dev/null +++ b/parser/src/parser/tests/testdata/for-error-2.cststream @@ -0,0 +1,80 @@ +Begin { kind: SOURCE_FILE, span: Span(0..98) } +Begin { kind: RULE_DECL, span: Span(0..98) } +Token { kind: RULE_KW, span: Span(0..4) } +Token { kind: WHITESPACE, span: Span(4..5) } +Token { kind: IDENT, span: Span(5..9) } +Token { kind: WHITESPACE, span: Span(9..10) } +Token { kind: L_BRACE, span: Span(10..11) } +Token { kind: NEWLINE, span: Span(11..12) } +Token { kind: WHITESPACE, span: Span(12..14) } +Begin { kind: PATTERNS_BLK, span: Span(14..52) } +Token { kind: STRINGS_KW, span: Span(14..21) } +Token { kind: COLON, span: Span(21..22) } +Token { kind: NEWLINE, span: Span(22..23) } +Token { kind: WHITESPACE, span: Span(23..27) } +Begin { kind: PATTERN_DEF, span: Span(27..37) } +Token { kind: PATTERN_IDENT, span: Span(27..29) } +Token { kind: WHITESPACE, span: Span(29..30) } +Token { kind: EQUAL, span: Span(30..31) } +Token { kind: WHITESPACE, span: Span(31..32) } +Token { kind: STRING_LIT, span: Span(32..37) } +End { kind: PATTERN_DEF, span: Span(27..37) } +Token { kind: NEWLINE, span: Span(37..38) } +Token { kind: WHITESPACE, span: Span(38..42) } +Begin { kind: PATTERN_DEF, span: Span(42..52) } +Token { kind: PATTERN_IDENT, span: Span(42..44) } +Token { kind: WHITESPACE, span: Span(44..45) } +Token { kind: EQUAL, span: Span(45..46) } +Token { kind: WHITESPACE, span: Span(46..47) } +Token { kind: STRING_LIT, span: Span(47..52) } +End { kind: PATTERN_DEF, span: Span(42..52) } +End { kind: PATTERNS_BLK, span: Span(14..52) } +Token { kind: NEWLINE, span: Span(52..53) } +Token { kind: WHITESPACE, span: Span(53..55) } +Begin { kind: CONDITION_BLK, span: Span(55..96) } +Token { kind: CONDITION_KW, span: Span(55..64) } +Token { kind: COLON, span: Span(64..65) } +Token { kind: NEWLINE, span: Span(65..66) } +Token { kind: WHITESPACE, span: Span(66..70) } +Begin { kind: BOOLEAN_EXPR, span: Span(70..96) } +Begin { kind: BOOLEAN_TERM, span: Span(70..96) } +Begin { kind: FOR_EXPR, span: Span(70..96) } +Token { kind: FOR_KW, span: Span(70..73) } +Token { kind: WHITESPACE, span: Span(73..74) } +Begin { kind: QUANTIFIER, span: Span(74..77) } +Token { kind: ALL_KW, span: Span(74..77) } +End { kind: QUANTIFIER, span: Span(74..77) } +Token { kind: WHITESPACE, span: Span(77..78) } +Token { kind: OF_KW, span: Span(78..80) } +Token { kind: WHITESPACE, span: Span(80..81) } +Token { kind: THEM_KW, span: Span(81..85) } +Token { kind: WHITESPACE, span: Span(85..86) } +Token { kind: COLON, span: Span(86..87) } +Token { kind: WHITESPACE, span: Span(87..88) } +Token { kind: L_PAREN, span: Span(88..89) } +Token { kind: WHITESPACE, span: Span(89..90) } +Begin { kind: BOOLEAN_EXPR, span: Span(90..93) } +Begin { kind: BOOLEAN_TERM, span: Span(90..93) } +Begin { kind: EXPR, span: Span(90..93) } +Begin { kind: TERM, span: Span(90..93) } +Begin { kind: PRIMARY_EXPR, span: Span(90..93) } +Token { kind: IDENT, span: Span(90..93) } +End { kind: PRIMARY_EXPR, span: Span(90..93) } +End { kind: TERM, span: Span(90..93) } +End { kind: EXPR, span: Span(90..93) } +End { kind: BOOLEAN_TERM, span: Span(90..93) } +End { kind: BOOLEAN_EXPR, span: Span(90..93) } +Begin { kind: ERROR, span: Span(93..94) } +Token { kind: DOT, span: Span(93..94) } +End { kind: ERROR, span: Span(93..94) } +Token { kind: WHITESPACE, span: Span(94..95) } +Token { kind: R_PAREN, span: Span(95..96) } +End { kind: FOR_EXPR, span: Span(70..96) } +End { kind: BOOLEAN_TERM, span: Span(70..96) } +End { kind: BOOLEAN_EXPR, span: Span(70..96) } +End { kind: CONDITION_BLK, span: Span(55..96) } +Token { kind: NEWLINE, span: Span(96..97) } +Token { kind: R_BRACE, span: Span(97..98) } +End { kind: RULE_DECL, span: Span(0..98) } +Error { message: "expecting identifier, found `)`", span: Span(95..96) } +End { kind: SOURCE_FILE, span: Span(0..98) } diff --git a/parser/src/parser/tests/testdata/for-error-2.in b/parser/src/parser/tests/testdata/for-error-2.in new file mode 100644 index 000000000..3ec04ab56 --- /dev/null +++ b/parser/src/parser/tests/testdata/for-error-2.in @@ -0,0 +1,7 @@ +rule test { + strings: + $a = "foo" + $b = "bar" + condition: + for all of them : ( foo. ) +} \ No newline at end of file diff --git a/parser/src/parser/tests/testdata/hex-patterns-error-1.cststream b/parser/src/parser/tests/testdata/hex-patterns-error-1.cststream index cf8121ae2..e87e15832 100644 --- a/parser/src/parser/tests/testdata/hex-patterns-error-1.cststream +++ b/parser/src/parser/tests/testdata/hex-patterns-error-1.cststream @@ -21,7 +21,6 @@ End { kind: ERROR, span: Span(13..29) } Token { kind: WHITESPACE, span: Span(29..30) } Begin { kind: ERROR, span: Span(30..43) } Token { kind: L_BRACE, span: Span(30..31) } -Error { message: "expecting BYTE or `(`, found `)`", span: Span(40..41) } Token { kind: WHITESPACE, span: Span(31..32) } Token { kind: HEX_BYTE, span: Span(32..34) } Token { kind: WHITESPACE, span: Span(34..35) } @@ -49,6 +48,7 @@ End { kind: CONDITION_BLK, span: Span(46..64) } Token { kind: NEWLINE, span: Span(64..65) } Token { kind: R_BRACE, span: Span(65..66) } End { kind: RULE_DECL, span: Span(0..66) } +Error { message: "expecting BYTE or `(`, found `)`", span: Span(40..41) } Token { kind: NEWLINE, span: Span(66..67) } Token { kind: NEWLINE, span: Span(67..68) } Begin { kind: RULE_DECL, span: Span(68..124) } @@ -73,7 +73,6 @@ End { kind: ERROR, span: Span(81..97) } Token { kind: WHITESPACE, span: Span(97..98) } Begin { kind: ERROR, span: Span(98..101) } Token { kind: L_BRACE, span: Span(98..99) } -Error { message: "expecting BYTE or `(`, found `}`", span: Span(100..101) } Token { kind: WHITESPACE, span: Span(99..100) } Token { kind: R_BRACE, span: Span(100..101) } End { kind: ERROR, span: Span(98..101) } @@ -93,4 +92,5 @@ End { kind: CONDITION_BLK, span: Span(104..122) } Token { kind: NEWLINE, span: Span(122..123) } Token { kind: R_BRACE, span: Span(123..124) } End { kind: RULE_DECL, span: Span(68..124) } +Error { message: "expecting BYTE or `(`, found `}`", span: Span(100..101) } End { kind: SOURCE_FILE, span: Span(0..124) } diff --git a/parser/src/parser/tests/testdata/hex-patterns-error-2.cststream b/parser/src/parser/tests/testdata/hex-patterns-error-2.cststream index d6c03f89c..10d64e636 100644 --- a/parser/src/parser/tests/testdata/hex-patterns-error-2.cststream +++ b/parser/src/parser/tests/testdata/hex-patterns-error-2.cststream @@ -21,7 +21,6 @@ End { kind: ERROR, span: Span(13..29) } Token { kind: WHITESPACE, span: Span(29..30) } Begin { kind: ERROR, span: Span(30..35) } Token { kind: L_BRACE, span: Span(30..31) } -Error { message: "expecting `[`, BYTE, `(` or `}`, found `0`", span: Span(33..34) } Token { kind: HEX_BYTE, span: Span(31..33) } Token { kind: INTEGER_LIT, span: Span(33..34) } Token { kind: R_BRACE, span: Span(34..35) } @@ -42,6 +41,7 @@ End { kind: CONDITION_BLK, span: Span(38..56) } Token { kind: NEWLINE, span: Span(56..57) } Token { kind: R_BRACE, span: Span(57..58) } End { kind: RULE_DECL, span: Span(0..58) } +Error { message: "expecting `[`, BYTE, `(` or `}`, found `0`", span: Span(33..34) } Token { kind: NEWLINE, span: Span(58..59) } Token { kind: NEWLINE, span: Span(59..60) } Begin { kind: RULE_DECL, span: Span(60..118) } @@ -66,7 +66,6 @@ End { kind: ERROR, span: Span(73..89) } Token { kind: WHITESPACE, span: Span(89..90) } Begin { kind: ERROR, span: Span(90..95) } Token { kind: L_BRACE, span: Span(90..91) } -Error { message: "expecting `[`, BYTE, `(` or `}`, found `a`", span: Span(93..94) } Token { kind: HEX_BYTE, span: Span(91..93) } Token { kind: IDENT, span: Span(93..94) } Token { kind: R_BRACE, span: Span(94..95) } @@ -87,4 +86,5 @@ End { kind: CONDITION_BLK, span: Span(98..116) } Token { kind: NEWLINE, span: Span(116..117) } Token { kind: R_BRACE, span: Span(117..118) } End { kind: RULE_DECL, span: Span(60..118) } +Error { message: "expecting `[`, BYTE, `(` or `}`, found `a`", span: Span(93..94) } End { kind: SOURCE_FILE, span: Span(0..118) } diff --git a/parser/src/parser/tests/testdata/meta-error-1.cststream b/parser/src/parser/tests/testdata/meta-error-1.cststream index cf555855a..336c39003 100644 --- a/parser/src/parser/tests/testdata/meta-error-1.cststream +++ b/parser/src/parser/tests/testdata/meta-error-1.cststream @@ -9,7 +9,6 @@ Token { kind: NEWLINE, span: Span(11..12) } Token { kind: WHITESPACE, span: Span(12..14) } Begin { kind: ERROR, span: Span(14..38) } Token { kind: IDENT, span: Span(14..17) } -Error { message: "expecting `meta`, `strings` or `condition`, found `foo`", span: Span(14..17) } Token { kind: NEWLINE, span: Span(17..18) } Token { kind: WHITESPACE, span: Span(18..20) } Token { kind: CONDITION_KW, span: Span(20..29) } @@ -21,4 +20,5 @@ End { kind: ERROR, span: Span(14..38) } Token { kind: NEWLINE, span: Span(38..39) } Token { kind: R_BRACE, span: Span(39..40) } End { kind: RULE_DECL, span: Span(0..40) } +Error { message: "expecting `meta`, `strings` or `condition`, found `foo`", span: Span(14..17) } End { kind: SOURCE_FILE, span: Span(0..40) } diff --git a/parser/src/parser/tests/testdata/meta-error-2.cststream b/parser/src/parser/tests/testdata/meta-error-2.cststream index aec06f2aa..046b09f0a 100644 --- a/parser/src/parser/tests/testdata/meta-error-2.cststream +++ b/parser/src/parser/tests/testdata/meta-error-2.cststream @@ -20,7 +20,6 @@ Token { kind: NEWLINE, span: Span(27..28) } Token { kind: WHITESPACE, span: Span(28..30) } Begin { kind: CONDITION_BLK, span: Span(30..48) } Token { kind: CONDITION_KW, span: Span(30..39) } -Error { message: "expecting `=`, found `condition`", span: Span(30..39) } Token { kind: COLON, span: Span(39..40) } Token { kind: NEWLINE, span: Span(40..41) } Token { kind: WHITESPACE, span: Span(41..44) } @@ -33,4 +32,5 @@ End { kind: CONDITION_BLK, span: Span(30..48) } Token { kind: NEWLINE, span: Span(48..49) } Token { kind: R_BRACE, span: Span(49..50) } End { kind: RULE_DECL, span: Span(0..50) } +Error { message: "expecting `=`, found `condition`", span: Span(30..39) } End { kind: SOURCE_FILE, span: Span(0..50) } diff --git a/parser/src/parser/tests/testdata/meta-error-3.cststream b/parser/src/parser/tests/testdata/meta-error-3.cststream index 2e2bf6973..3934ca946 100644 --- a/parser/src/parser/tests/testdata/meta-error-3.cststream +++ b/parser/src/parser/tests/testdata/meta-error-3.cststream @@ -22,7 +22,6 @@ Token { kind: NEWLINE, span: Span(29..30) } Token { kind: WHITESPACE, span: Span(30..32) } Begin { kind: CONDITION_BLK, span: Span(32..50) } Token { kind: CONDITION_KW, span: Span(32..41) } -Error { message: "expecting `-`, INTEGER, FLOAT, STRING, `true` or `false`, found `condition`", span: Span(32..41) } Token { kind: COLON, span: Span(41..42) } Token { kind: NEWLINE, span: Span(42..43) } Token { kind: WHITESPACE, span: Span(43..46) } @@ -35,4 +34,5 @@ End { kind: CONDITION_BLK, span: Span(32..50) } Token { kind: NEWLINE, span: Span(50..51) } Token { kind: R_BRACE, span: Span(51..52) } End { kind: RULE_DECL, span: Span(0..52) } +Error { message: "expecting `-`, INTEGER, FLOAT, STRING, `true` or `false`, found `condition`", span: Span(32..41) } End { kind: SOURCE_FILE, span: Span(0..52) } diff --git a/parser/src/parser/tests/testdata/meta-error-4.cststream b/parser/src/parser/tests/testdata/meta-error-4.cststream index 9f6cef142..fcca64bda 100644 --- a/parser/src/parser/tests/testdata/meta-error-4.cststream +++ b/parser/src/parser/tests/testdata/meta-error-4.cststream @@ -21,7 +21,6 @@ End { kind: ERROR, span: Span(14..29) } Token { kind: WHITESPACE, span: Span(29..30) } Begin { kind: ERROR, span: Span(30..50) } Token { kind: META_KW, span: Span(30..34) } -Error { message: "expecting `-`, INTEGER, FLOAT, STRING, `true` or `false`, found `meta`", span: Span(30..34) } Token { kind: NEWLINE, span: Span(34..35) } Token { kind: WHITESPACE, span: Span(35..39) } Token { kind: IDENT, span: Span(39..42) } @@ -46,4 +45,5 @@ End { kind: CONDITION_BLK, span: Span(53..71) } Token { kind: NEWLINE, span: Span(71..72) } Token { kind: R_BRACE, span: Span(72..73) } End { kind: RULE_DECL, span: Span(0..73) } +Error { message: "expecting `-`, INTEGER, FLOAT, STRING, `true` or `false`, found `meta`", span: Span(30..34) } End { kind: SOURCE_FILE, span: Span(0..73) } diff --git a/parser/src/parser/tests/testdata/meta-error-5.cststream b/parser/src/parser/tests/testdata/meta-error-5.cststream index cb2059106..a91b583d5 100644 --- a/parser/src/parser/tests/testdata/meta-error-5.cststream +++ b/parser/src/parser/tests/testdata/meta-error-5.cststream @@ -21,7 +21,6 @@ End { kind: ERROR, span: Span(14..29) } Token { kind: WHITESPACE, span: Span(29..30) } Begin { kind: ERROR, span: Span(30..49) } Token { kind: IDENT, span: Span(30..33) } -Error { message: "expecting `-`, INTEGER, FLOAT, STRING, `true` or `false`, found `bar`", span: Span(30..33) } Token { kind: NEWLINE, span: Span(33..34) } Token { kind: WHITESPACE, span: Span(34..38) } Token { kind: IDENT, span: Span(38..41) } @@ -61,4 +60,5 @@ End { kind: CONDITION_BLK, span: Span(77..95) } Token { kind: NEWLINE, span: Span(95..96) } Token { kind: R_BRACE, span: Span(96..97) } End { kind: RULE_DECL, span: Span(0..97) } +Error { message: "expecting `-`, INTEGER, FLOAT, STRING, `true` or `false`, found `bar`", span: Span(30..33) } End { kind: SOURCE_FILE, span: Span(0..97) } diff --git a/parser/src/parser/tests/testdata/meta-error-6.cststream b/parser/src/parser/tests/testdata/meta-error-6.cststream index 7eedce871..d69160870 100644 --- a/parser/src/parser/tests/testdata/meta-error-6.cststream +++ b/parser/src/parser/tests/testdata/meta-error-6.cststream @@ -22,13 +22,11 @@ Token { kind: NEWLINE, span: Span(29..30) } Token { kind: WHITESPACE, span: Span(30..32) } Begin { kind: ERROR, span: Span(32..39) } Token { kind: STRINGS_KW, span: Span(32..39) } -Error { message: "expecting `-`, INTEGER, FLOAT, STRING, `true` or `false`, found `strings`", span: Span(32..39) } End { kind: ERROR, span: Span(32..39) } Token { kind: NEWLINE, span: Span(39..40) } Token { kind: WHITESPACE, span: Span(40..44) } Begin { kind: ERROR, span: Span(44..81) } Token { kind: IDENT, span: Span(44..47) } -Error { message: "expecting `:`, found `bar`", span: Span(44..47) } Token { kind: WHITESPACE, span: Span(47..48) } Token { kind: EQUAL, span: Span(48..49) } Token { kind: WHITESPACE, span: Span(49..50) } @@ -61,4 +59,6 @@ End { kind: CONDITION_BLK, span: Span(84..103) } Token { kind: NEWLINE, span: Span(103..104) } Token { kind: R_BRACE, span: Span(104..105) } End { kind: RULE_DECL, span: Span(0..105) } +Error { message: "expecting `-`, INTEGER, FLOAT, STRING, `true` or `false`, found `strings`", span: Span(32..39) } +Error { message: "expecting `:`, found `bar`", span: Span(44..47) } End { kind: SOURCE_FILE, span: Span(0..105) } diff --git a/parser/src/parser/tests/testdata/of-error-1.cststream b/parser/src/parser/tests/testdata/of-error-1.cststream index 3063feded..08baec0bc 100644 --- a/parser/src/parser/tests/testdata/of-error-1.cststream +++ b/parser/src/parser/tests/testdata/of-error-1.cststream @@ -14,7 +14,6 @@ Token { kind: NEWLINE, span: Span(23..24) } Token { kind: WHITESPACE, span: Span(24..26) } Begin { kind: ERROR, span: Span(26..45) } Token { kind: ANY_KW, span: Span(26..29) } -Error { message: "unexpected `at`", span: Span(41..43) } Token { kind: WHITESPACE, span: Span(29..30) } Token { kind: OF_KW, span: Span(30..32) } Token { kind: WHITESPACE, span: Span(32..33) } @@ -34,4 +33,5 @@ End { kind: CONDITION_BLK, span: Span(13..45) } Token { kind: NEWLINE, span: Span(45..46) } Token { kind: R_BRACE, span: Span(46..47) } End { kind: RULE_DECL, span: Span(0..47) } +Error { message: "unexpected `at`", span: Span(41..43) } End { kind: SOURCE_FILE, span: Span(0..47) } diff --git a/parser/src/parser/tests/testdata/pattern-mods-error-1.cststream b/parser/src/parser/tests/testdata/pattern-mods-error-1.cststream index 321bbd9a1..a4f673ac2 100644 --- a/parser/src/parser/tests/testdata/pattern-mods-error-1.cststream +++ b/parser/src/parser/tests/testdata/pattern-mods-error-1.cststream @@ -28,7 +28,6 @@ Token { kind: NEWLINE, span: Span(39..40) } Token { kind: WHITESPACE, span: Span(40..42) } Begin { kind: CONDITION_BLK, span: Span(42..60) } Token { kind: CONDITION_KW, span: Span(42..51) } -Error { message: "expecting pattern modifier, pattern identifier or `condition`, found `foo`", span: Span(36..39) } Token { kind: COLON, span: Span(51..52) } Token { kind: NEWLINE, span: Span(52..53) } Token { kind: WHITESPACE, span: Span(53..56) } @@ -41,6 +40,7 @@ End { kind: CONDITION_BLK, span: Span(42..60) } Token { kind: NEWLINE, span: Span(60..61) } Token { kind: R_BRACE, span: Span(61..62) } End { kind: RULE_DECL, span: Span(0..62) } +Error { message: "expecting pattern modifier, pattern identifier or `condition`, found `foo`", span: Span(36..39) } Token { kind: NEWLINE, span: Span(62..63) } Token { kind: NEWLINE, span: Span(63..64) } Begin { kind: RULE_DECL, span: Span(64..131) } @@ -74,7 +74,6 @@ Token { kind: NEWLINE, span: Span(108..109) } Token { kind: WHITESPACE, span: Span(109..111) } Begin { kind: CONDITION_BLK, span: Span(111..129) } Token { kind: CONDITION_KW, span: Span(111..120) } -Error { message: "expecting STRING, found `)`", span: Span(107..108) } Token { kind: COLON, span: Span(120..121) } Token { kind: NEWLINE, span: Span(121..122) } Token { kind: WHITESPACE, span: Span(122..125) } @@ -87,4 +86,5 @@ End { kind: CONDITION_BLK, span: Span(111..129) } Token { kind: NEWLINE, span: Span(129..130) } Token { kind: R_BRACE, span: Span(130..131) } End { kind: RULE_DECL, span: Span(64..131) } +Error { message: "expecting STRING, found `)`", span: Span(107..108) } End { kind: SOURCE_FILE, span: Span(0..131) } diff --git a/parser/src/parser/tests/testdata/patterns-error-1.cststream b/parser/src/parser/tests/testdata/patterns-error-1.cststream index 77032d900..186546671 100644 --- a/parser/src/parser/tests/testdata/patterns-error-1.cststream +++ b/parser/src/parser/tests/testdata/patterns-error-1.cststream @@ -21,7 +21,6 @@ End { kind: ERROR, span: Span(13..29) } Token { kind: WHITESPACE, span: Span(29..30) } Begin { kind: ERROR, span: Span(30..34) } Token { kind: L_BRACE, span: Span(30..31) } -Error { message: "expecting `[`, BYTE, `(` or `}`, found `condition`", span: Span(37..46) } Token { kind: WHITESPACE, span: Span(31..32) } Token { kind: HEX_BYTE, span: Span(32..34) } End { kind: ERROR, span: Span(30..34) } @@ -41,4 +40,5 @@ End { kind: CONDITION_BLK, span: Span(37..55) } Token { kind: NEWLINE, span: Span(55..56) } Token { kind: R_BRACE, span: Span(56..57) } End { kind: RULE_DECL, span: Span(0..57) } +Error { message: "expecting `[`, BYTE, `(` or `}`, found `condition`", span: Span(37..46) } End { kind: SOURCE_FILE, span: Span(0..57) } diff --git a/parser/src/parser/tests/testdata/patterns-error-2.cststream b/parser/src/parser/tests/testdata/patterns-error-2.cststream index 84126ebb7..6b98336a3 100644 --- a/parser/src/parser/tests/testdata/patterns-error-2.cststream +++ b/parser/src/parser/tests/testdata/patterns-error-2.cststream @@ -35,7 +35,6 @@ Token { kind: NEWLINE, span: Span(47..48) } Token { kind: WHITESPACE, span: Span(48..50) } Begin { kind: CONDITION_BLK, span: Span(50..68) } Token { kind: CONDITION_KW, span: Span(50..59) } -Error { message: "expecting `[`, BYTE, `(` or `}`, found `condition`", span: Span(50..59) } Token { kind: COLON, span: Span(59..60) } Token { kind: NEWLINE, span: Span(60..61) } Token { kind: WHITESPACE, span: Span(61..64) } @@ -48,4 +47,5 @@ End { kind: CONDITION_BLK, span: Span(50..68) } Token { kind: NEWLINE, span: Span(68..69) } Token { kind: R_BRACE, span: Span(69..70) } End { kind: RULE_DECL, span: Span(0..70) } +Error { message: "expecting `[`, BYTE, `(` or `}`, found `condition`", span: Span(50..59) } End { kind: SOURCE_FILE, span: Span(0..70) } diff --git a/parser/src/parser/tests/testdata/patterns-error-3.cststream b/parser/src/parser/tests/testdata/patterns-error-3.cststream index 553c8ff53..13ec5dd8a 100644 --- a/parser/src/parser/tests/testdata/patterns-error-3.cststream +++ b/parser/src/parser/tests/testdata/patterns-error-3.cststream @@ -40,7 +40,6 @@ Token { kind: NEWLINE, span: Span(65..66) } Token { kind: WHITESPACE, span: Span(66..68) } Begin { kind: CONDITION_BLK, span: Span(68..87) } Token { kind: CONDITION_KW, span: Span(68..77) } -Error { message: "expecting STRING, regexp or `{`, found `bar`", span: Span(47..50) } Token { kind: COLON, span: Span(77..78) } Token { kind: NEWLINE, span: Span(78..79) } Token { kind: WHITESPACE, span: Span(79..83) } @@ -53,4 +52,5 @@ End { kind: CONDITION_BLK, span: Span(68..87) } Token { kind: NEWLINE, span: Span(87..88) } Token { kind: R_BRACE, span: Span(88..89) } End { kind: RULE_DECL, span: Span(0..89) } +Error { message: "expecting STRING, regexp or `{`, found `bar`", span: Span(47..50) } End { kind: SOURCE_FILE, span: Span(0..89) } diff --git a/parser/src/parser/tests/testdata/rule-tags-error-1.cststream b/parser/src/parser/tests/testdata/rule-tags-error-1.cststream index 1ffad9461..d666ef729 100644 --- a/parser/src/parser/tests/testdata/rule-tags-error-1.cststream +++ b/parser/src/parser/tests/testdata/rule-tags-error-1.cststream @@ -9,7 +9,6 @@ Token { kind: COLON, span: Span(10..11) } Token { kind: WHITESPACE, span: Span(11..12) } End { kind: ERROR, span: Span(10..12) } Token { kind: L_BRACE, span: Span(12..13) } -Error { message: "expecting identifier, found `{`", span: Span(12..13) } Token { kind: NEWLINE, span: Span(13..14) } Token { kind: WHITESPACE, span: Span(14..16) } Begin { kind: CONDITION_BLK, span: Span(16..34) } @@ -26,4 +25,5 @@ End { kind: CONDITION_BLK, span: Span(16..34) } Token { kind: NEWLINE, span: Span(34..35) } Token { kind: R_BRACE, span: Span(35..36) } End { kind: RULE_DECL, span: Span(0..36) } +Error { message: "expecting identifier, found `{`", span: Span(12..13) } End { kind: SOURCE_FILE, span: Span(0..36) } diff --git a/parser/src/parser/tests/testdata/rule-tags-error-2.cst b/parser/src/parser/tests/testdata/rule-tags-error-2.cst index a062df8fb..848db8717 100644 --- a/parser/src/parser/tests/testdata/rule-tags-error-2.cst +++ b/parser/src/parser/tests/testdata/rule-tags-error-2.cst @@ -1,20 +1,20 @@ SOURCE_FILE@0..36 - RULE_DECL@0..36 + ERROR@0..9 RULE_KW@0..4 "rule" WHITESPACE@4..5 " " IDENT@5..9 "test" - WHITESPACE@9..10 " " - ERROR@10..34 - EQUAL@10..11 "=" - WHITESPACE@11..12 " " - L_BRACE@12..13 "{" - NEWLINE@13..14 "\n" - WHITESPACE@14..16 " " - CONDITION_KW@16..25 "condition" - COLON@25..26 ":" - NEWLINE@26..27 "\n" - WHITESPACE@27..30 "\t " - TRUE_KW@30..34 "true" + WHITESPACE@9..10 " " + ERROR@10..36 + EQUAL@10..11 "=" + WHITESPACE@11..12 " " + L_BRACE@12..13 "{" + NEWLINE@13..14 "\n" + WHITESPACE@14..16 " " + CONDITION_KW@16..25 "condition" + COLON@25..26 ":" + NEWLINE@26..27 "\n" + WHITESPACE@27..30 "\t " + TRUE_KW@30..34 "true" NEWLINE@34..35 "\n" R_BRACE@35..36 "}" diff --git a/parser/src/parser/tests/testdata/rule-tags-error-2.cststream b/parser/src/parser/tests/testdata/rule-tags-error-2.cststream index 491373848..0c9dd3adc 100644 --- a/parser/src/parser/tests/testdata/rule-tags-error-2.cststream +++ b/parser/src/parser/tests/testdata/rule-tags-error-2.cststream @@ -1,12 +1,12 @@ Begin { kind: SOURCE_FILE, span: Span(0..36) } -Begin { kind: RULE_DECL, span: Span(0..36) } +Begin { kind: ERROR, span: Span(0..9) } Token { kind: RULE_KW, span: Span(0..4) } Token { kind: WHITESPACE, span: Span(4..5) } Token { kind: IDENT, span: Span(5..9) } +End { kind: ERROR, span: Span(0..9) } Token { kind: WHITESPACE, span: Span(9..10) } -Begin { kind: ERROR, span: Span(10..34) } +Begin { kind: ERROR, span: Span(10..36) } Token { kind: EQUAL, span: Span(10..11) } -Error { message: "expecting `:` or `{`, found `=`", span: Span(10..11) } Token { kind: WHITESPACE, span: Span(11..12) } Token { kind: L_BRACE, span: Span(12..13) } Token { kind: NEWLINE, span: Span(13..14) } @@ -16,8 +16,8 @@ Token { kind: COLON, span: Span(25..26) } Token { kind: NEWLINE, span: Span(26..27) } Token { kind: WHITESPACE, span: Span(27..30) } Token { kind: TRUE_KW, span: Span(30..34) } -End { kind: ERROR, span: Span(10..34) } Token { kind: NEWLINE, span: Span(34..35) } Token { kind: R_BRACE, span: Span(35..36) } -End { kind: RULE_DECL, span: Span(0..36) } +End { kind: ERROR, span: Span(10..36) } +Error { message: "expecting `:` or `{`, found `=`", span: Span(10..11) } End { kind: SOURCE_FILE, span: Span(0..36) } diff --git a/parser/src/parser/tests/testdata/rule-tags-error-3.cststream b/parser/src/parser/tests/testdata/rule-tags-error-3.cststream index 1c91bf716..80da33007 100644 --- a/parser/src/parser/tests/testdata/rule-tags-error-3.cststream +++ b/parser/src/parser/tests/testdata/rule-tags-error-3.cststream @@ -8,7 +8,6 @@ Begin { kind: ERROR, span: Span(10..13) } Token { kind: COLON, span: Span(10..11) } Token { kind: WHITESPACE, span: Span(11..12) } Token { kind: EQUAL, span: Span(12..13) } -Error { message: "expecting identifier, found `=`", span: Span(12..13) } End { kind: ERROR, span: Span(10..13) } Token { kind: WHITESPACE, span: Span(13..14) } Token { kind: L_BRACE, span: Span(14..15) } @@ -28,4 +27,5 @@ End { kind: CONDITION_BLK, span: Span(18..36) } Token { kind: NEWLINE, span: Span(36..37) } Token { kind: R_BRACE, span: Span(37..38) } End { kind: RULE_DECL, span: Span(0..38) } +Error { message: "expecting identifier, found `=`", span: Span(12..13) } End { kind: SOURCE_FILE, span: Span(0..38) } diff --git a/parser/src/parser/tests/testdata/with-error-1.cststream b/parser/src/parser/tests/testdata/with-error-1.cststream index 94446d6b9..babce5923 100644 --- a/parser/src/parser/tests/testdata/with-error-1.cststream +++ b/parser/src/parser/tests/testdata/with-error-1.cststream @@ -14,7 +14,6 @@ Token { kind: NEWLINE, span: Span(23..24) } Token { kind: WHITESPACE, span: Span(24..26) } Begin { kind: ERROR, span: Span(26..90) } Token { kind: WITH_KW, span: Span(26..30) } -Error { message: "expecting expression, operator, `,` or `:`, found `(`", span: Span(50..51) } Token { kind: WHITESPACE, span: Span(30..31) } Token { kind: IDENT, span: Span(31..36) } Token { kind: WHITESPACE, span: Span(36..37) } @@ -44,4 +43,5 @@ End { kind: CONDITION_BLK, span: Span(13..90) } Token { kind: NEWLINE, span: Span(90..91) } Token { kind: R_BRACE, span: Span(91..92) } End { kind: RULE_DECL, span: Span(0..92) } +Error { message: "expecting expression, operator, `,` or `:`, found `(`", span: Span(50..51) } End { kind: SOURCE_FILE, span: Span(0..92) } diff --git a/parser/src/parser/tests/testdata/with-error-2.cst b/parser/src/parser/tests/testdata/with-error-2.cst index 9a74a6318..9c280c149 100644 --- a/parser/src/parser/tests/testdata/with-error-2.cst +++ b/parser/src/parser/tests/testdata/with-error-2.cst @@ -12,26 +12,36 @@ SOURCE_FILE@0..65 COLON@22..23 ":" NEWLINE@23..24 "\n" WHITESPACE@24..26 "\t\t" - ERROR@26..63 - WITH_KW@26..30 "with" - WHITESPACE@30..31 " " - IDENT@31..36 "first" - WHITESPACE@36..37 " " - EQUAL@37..38 "=" - WHITESPACE@38..39 " " - IDENT@39..42 "foo" - DOT@42..43 "." - IDENT@43..46 "bar" - L_BRACKET@46..47 "[" - INTEGER_LIT@47..48 "0" - R_BRACKET@48..49 "]" - WHITESPACE@49..50 " " - COLON@50..51 ":" - WHITESPACE@51..52 " " - L_PAREN@52..53 "(" - NEWLINE@53..54 "\n" - WHITESPACE@54..62 " " - R_PAREN@62..63 ")" + BOOLEAN_EXPR@26..63 + BOOLEAN_TERM@26..63 + WITH_EXPR@26..63 + WITH_KW@26..30 "with" + WHITESPACE@30..31 " " + WITH_DECLS@31..49 + WITH_DECL@31..49 + IDENT@31..36 "first" + WHITESPACE@36..37 " " + EQUAL@37..38 "=" + WHITESPACE@38..39 " " + EXPR@39..49 + TERM@39..49 + PRIMARY_EXPR@39..42 + IDENT@39..42 "foo" + DOT@42..43 "." + PRIMARY_EXPR@43..49 + IDENT@43..46 "bar" + L_BRACKET@46..47 "[" + EXPR@47..48 + TERM@47..48 + INTEGER_LIT@47..48 "0" + R_BRACKET@48..49 "]" + WHITESPACE@49..50 " " + COLON@50..51 ":" + WHITESPACE@51..52 " " + L_PAREN@52..53 "(" + NEWLINE@53..54 "\n" + WHITESPACE@54..62 " " + R_PAREN@62..63 ")" NEWLINE@63..64 "\n" R_BRACE@64..65 "}" diff --git a/parser/src/parser/tests/testdata/with-error-2.cststream b/parser/src/parser/tests/testdata/with-error-2.cststream index 3bba6ee62..2bae4436f 100644 --- a/parser/src/parser/tests/testdata/with-error-2.cststream +++ b/parser/src/parser/tests/testdata/with-error-2.cststream @@ -12,20 +12,37 @@ Token { kind: CONDITION_KW, span: Span(13..22) } Token { kind: COLON, span: Span(22..23) } Token { kind: NEWLINE, span: Span(23..24) } Token { kind: WHITESPACE, span: Span(24..26) } -Begin { kind: ERROR, span: Span(26..63) } +Begin { kind: BOOLEAN_EXPR, span: Span(26..63) } +Begin { kind: BOOLEAN_TERM, span: Span(26..63) } +Begin { kind: WITH_EXPR, span: Span(26..63) } Token { kind: WITH_KW, span: Span(26..30) } -Error { message: "expecting expression or identifier, found `)`", span: Span(62..63) } Token { kind: WHITESPACE, span: Span(30..31) } +Begin { kind: WITH_DECLS, span: Span(31..49) } +Begin { kind: WITH_DECL, span: Span(31..49) } Token { kind: IDENT, span: Span(31..36) } Token { kind: WHITESPACE, span: Span(36..37) } Token { kind: EQUAL, span: Span(37..38) } Token { kind: WHITESPACE, span: Span(38..39) } +Begin { kind: EXPR, span: Span(39..49) } +Begin { kind: TERM, span: Span(39..49) } +Begin { kind: PRIMARY_EXPR, span: Span(39..42) } Token { kind: IDENT, span: Span(39..42) } +End { kind: PRIMARY_EXPR, span: Span(39..42) } Token { kind: DOT, span: Span(42..43) } +Begin { kind: PRIMARY_EXPR, span: Span(43..49) } Token { kind: IDENT, span: Span(43..46) } Token { kind: L_BRACKET, span: Span(46..47) } +Begin { kind: EXPR, span: Span(47..48) } +Begin { kind: TERM, span: Span(47..48) } Token { kind: INTEGER_LIT, span: Span(47..48) } +End { kind: TERM, span: Span(47..48) } +End { kind: EXPR, span: Span(47..48) } Token { kind: R_BRACKET, span: Span(48..49) } +End { kind: PRIMARY_EXPR, span: Span(43..49) } +End { kind: TERM, span: Span(39..49) } +End { kind: EXPR, span: Span(39..49) } +End { kind: WITH_DECL, span: Span(31..49) } +End { kind: WITH_DECLS, span: Span(31..49) } Token { kind: WHITESPACE, span: Span(49..50) } Token { kind: COLON, span: Span(50..51) } Token { kind: WHITESPACE, span: Span(51..52) } @@ -33,9 +50,12 @@ Token { kind: L_PAREN, span: Span(52..53) } Token { kind: NEWLINE, span: Span(53..54) } Token { kind: WHITESPACE, span: Span(54..62) } Token { kind: R_PAREN, span: Span(62..63) } -End { kind: ERROR, span: Span(26..63) } +End { kind: WITH_EXPR, span: Span(26..63) } +End { kind: BOOLEAN_TERM, span: Span(26..63) } +End { kind: BOOLEAN_EXPR, span: Span(26..63) } End { kind: CONDITION_BLK, span: Span(13..63) } Token { kind: NEWLINE, span: Span(63..64) } Token { kind: R_BRACE, span: Span(64..65) } End { kind: RULE_DECL, span: Span(0..65) } +Error { message: "expecting expression or identifier, found `)`", span: Span(62..63) } End { kind: SOURCE_FILE, span: Span(0..65) } diff --git a/parser/src/parser/tests/testdata/with-error-3.cst b/parser/src/parser/tests/testdata/with-error-3.cst index 27156379c..0cfb1ec64 100644 --- a/parser/src/parser/tests/testdata/with-error-3.cst +++ b/parser/src/parser/tests/testdata/with-error-3.cst @@ -12,18 +12,22 @@ SOURCE_FILE@0..63 COLON@22..23 ":" NEWLINE@23..24 "\n" WHITESPACE@24..26 "\t\t" - ERROR@26..61 - WITH_KW@26..30 "with" - WHITESPACE@30..31 " " - COLON@31..32 ":" - WHITESPACE@32..33 " " - L_PAREN@33..34 "(" - NEWLINE@34..35 "\n" - WHITESPACE@35..47 " " - TRUE_KW@47..51 "true" - NEWLINE@51..52 "\n" - WHITESPACE@52..60 " " - R_PAREN@60..61 ")" + BOOLEAN_EXPR@26..61 + BOOLEAN_TERM@26..61 + WITH_EXPR@26..61 + WITH_KW@26..30 "with" + WHITESPACE@30..31 " " + COLON@31..32 ":" + WHITESPACE@32..33 " " + L_PAREN@33..34 "(" + NEWLINE@34..35 "\n" + WHITESPACE@35..47 " " + BOOLEAN_EXPR@47..51 + BOOLEAN_TERM@47..51 + TRUE_KW@47..51 "true" + NEWLINE@51..52 "\n" + WHITESPACE@52..60 " " + R_PAREN@60..61 ")" NEWLINE@61..62 "\n" R_BRACE@62..63 "}" diff --git a/parser/src/parser/tests/testdata/with-error-3.cststream b/parser/src/parser/tests/testdata/with-error-3.cststream index f87d19302..e54319284 100644 --- a/parser/src/parser/tests/testdata/with-error-3.cststream +++ b/parser/src/parser/tests/testdata/with-error-3.cststream @@ -12,22 +12,30 @@ Token { kind: CONDITION_KW, span: Span(13..22) } Token { kind: COLON, span: Span(22..23) } Token { kind: NEWLINE, span: Span(23..24) } Token { kind: WHITESPACE, span: Span(24..26) } -Begin { kind: ERROR, span: Span(26..61) } +Begin { kind: BOOLEAN_EXPR, span: Span(26..61) } +Begin { kind: BOOLEAN_TERM, span: Span(26..61) } +Begin { kind: WITH_EXPR, span: Span(26..61) } Token { kind: WITH_KW, span: Span(26..30) } -Error { message: "expecting identifier, found `:`", span: Span(31..32) } Token { kind: WHITESPACE, span: Span(30..31) } Token { kind: COLON, span: Span(31..32) } Token { kind: WHITESPACE, span: Span(32..33) } Token { kind: L_PAREN, span: Span(33..34) } Token { kind: NEWLINE, span: Span(34..35) } Token { kind: WHITESPACE, span: Span(35..47) } +Begin { kind: BOOLEAN_EXPR, span: Span(47..51) } +Begin { kind: BOOLEAN_TERM, span: Span(47..51) } Token { kind: TRUE_KW, span: Span(47..51) } +End { kind: BOOLEAN_TERM, span: Span(47..51) } +End { kind: BOOLEAN_EXPR, span: Span(47..51) } Token { kind: NEWLINE, span: Span(51..52) } Token { kind: WHITESPACE, span: Span(52..60) } Token { kind: R_PAREN, span: Span(60..61) } -End { kind: ERROR, span: Span(26..61) } +End { kind: WITH_EXPR, span: Span(26..61) } +End { kind: BOOLEAN_TERM, span: Span(26..61) } +End { kind: BOOLEAN_EXPR, span: Span(26..61) } End { kind: CONDITION_BLK, span: Span(13..61) } Token { kind: NEWLINE, span: Span(61..62) } Token { kind: R_BRACE, span: Span(62..63) } End { kind: RULE_DECL, span: Span(0..63) } +Error { message: "expecting identifier, found `:`", span: Span(31..32) } End { kind: SOURCE_FILE, span: Span(0..63) } diff --git a/parser/src/parser/tests/testdata/with-error-4.ast b/parser/src/parser/tests/testdata/with-error-4.ast new file mode 100644 index 000000000..6b4a9a9ed --- /dev/null +++ b/parser/src/parser/tests/testdata/with-error-4.ast @@ -0,0 +1,2 @@ +ERRORS: +- SyntaxError { message: "expecting identifier, found `)`", span: Span(77..78) } diff --git a/parser/src/parser/tests/testdata/with-error-4.cst b/parser/src/parser/tests/testdata/with-error-4.cst new file mode 100644 index 000000000..7b549ca92 --- /dev/null +++ b/parser/src/parser/tests/testdata/with-error-4.cst @@ -0,0 +1,50 @@ +SOURCE_FILE@0..80 + RULE_DECL@0..80 + RULE_KW@0..4 "rule" + WHITESPACE@4..5 " " + IDENT@5..9 "test" + WHITESPACE@9..10 " " + L_BRACE@10..11 "{" + NEWLINE@11..12 "\n" + WHITESPACE@12..16 " " + CONDITION_BLK@16..78 + CONDITION_KW@16..25 "condition" + COLON@25..26 ":" + NEWLINE@26..27 "\n" + WHITESPACE@27..35 " " + BOOLEAN_EXPR@35..78 + BOOLEAN_TERM@35..78 + WITH_EXPR@35..78 + WITH_KW@35..39 "with" + WHITESPACE@39..40 " " + WITH_DECLS@40..47 + WITH_DECL@40..47 + IDENT@40..43 "foo" + WHITESPACE@43..44 " " + EQUAL@44..45 "=" + WHITESPACE@45..46 " " + EXPR@46..47 + TERM@46..47 + INTEGER_LIT@46..47 "1" + WHITESPACE@47..48 " " + COLON@48..49 ":" + WHITESPACE@49..50 " " + L_PAREN@50..51 "(" + NEWLINE@51..52 "\n" + WHITESPACE@52..64 " " + BOOLEAN_EXPR@64..67 + BOOLEAN_TERM@64..67 + EXPR@64..67 + TERM@64..67 + PRIMARY_EXPR@64..67 + IDENT@64..67 "foo" + ERROR@67..68 + DOT@67..68 "." + NEWLINE@68..69 "\n" + WHITESPACE@69..77 " " + R_PAREN@77..78 ")" + NEWLINE@78..79 "\n" + R_BRACE@79..80 "}" + +ERRORS: +- [77..78]: expecting identifier, found `)` diff --git a/parser/src/parser/tests/testdata/with-error-4.cststream b/parser/src/parser/tests/testdata/with-error-4.cststream new file mode 100644 index 000000000..c71cae369 --- /dev/null +++ b/parser/src/parser/tests/testdata/with-error-4.cststream @@ -0,0 +1,64 @@ +Begin { kind: SOURCE_FILE, span: Span(0..80) } +Begin { kind: RULE_DECL, span: Span(0..80) } +Token { kind: RULE_KW, span: Span(0..4) } +Token { kind: WHITESPACE, span: Span(4..5) } +Token { kind: IDENT, span: Span(5..9) } +Token { kind: WHITESPACE, span: Span(9..10) } +Token { kind: L_BRACE, span: Span(10..11) } +Token { kind: NEWLINE, span: Span(11..12) } +Token { kind: WHITESPACE, span: Span(12..16) } +Begin { kind: CONDITION_BLK, span: Span(16..78) } +Token { kind: CONDITION_KW, span: Span(16..25) } +Token { kind: COLON, span: Span(25..26) } +Token { kind: NEWLINE, span: Span(26..27) } +Token { kind: WHITESPACE, span: Span(27..35) } +Begin { kind: BOOLEAN_EXPR, span: Span(35..78) } +Begin { kind: BOOLEAN_TERM, span: Span(35..78) } +Begin { kind: WITH_EXPR, span: Span(35..78) } +Token { kind: WITH_KW, span: Span(35..39) } +Token { kind: WHITESPACE, span: Span(39..40) } +Begin { kind: WITH_DECLS, span: Span(40..47) } +Begin { kind: WITH_DECL, span: Span(40..47) } +Token { kind: IDENT, span: Span(40..43) } +Token { kind: WHITESPACE, span: Span(43..44) } +Token { kind: EQUAL, span: Span(44..45) } +Token { kind: WHITESPACE, span: Span(45..46) } +Begin { kind: EXPR, span: Span(46..47) } +Begin { kind: TERM, span: Span(46..47) } +Token { kind: INTEGER_LIT, span: Span(46..47) } +End { kind: TERM, span: Span(46..47) } +End { kind: EXPR, span: Span(46..47) } +End { kind: WITH_DECL, span: Span(40..47) } +End { kind: WITH_DECLS, span: Span(40..47) } +Token { kind: WHITESPACE, span: Span(47..48) } +Token { kind: COLON, span: Span(48..49) } +Token { kind: WHITESPACE, span: Span(49..50) } +Token { kind: L_PAREN, span: Span(50..51) } +Token { kind: NEWLINE, span: Span(51..52) } +Token { kind: WHITESPACE, span: Span(52..64) } +Begin { kind: BOOLEAN_EXPR, span: Span(64..67) } +Begin { kind: BOOLEAN_TERM, span: Span(64..67) } +Begin { kind: EXPR, span: Span(64..67) } +Begin { kind: TERM, span: Span(64..67) } +Begin { kind: PRIMARY_EXPR, span: Span(64..67) } +Token { kind: IDENT, span: Span(64..67) } +End { kind: PRIMARY_EXPR, span: Span(64..67) } +End { kind: TERM, span: Span(64..67) } +End { kind: EXPR, span: Span(64..67) } +End { kind: BOOLEAN_TERM, span: Span(64..67) } +End { kind: BOOLEAN_EXPR, span: Span(64..67) } +Begin { kind: ERROR, span: Span(67..68) } +Token { kind: DOT, span: Span(67..68) } +End { kind: ERROR, span: Span(67..68) } +Token { kind: NEWLINE, span: Span(68..69) } +Token { kind: WHITESPACE, span: Span(69..77) } +Token { kind: R_PAREN, span: Span(77..78) } +End { kind: WITH_EXPR, span: Span(35..78) } +End { kind: BOOLEAN_TERM, span: Span(35..78) } +End { kind: BOOLEAN_EXPR, span: Span(35..78) } +End { kind: CONDITION_BLK, span: Span(16..78) } +Token { kind: NEWLINE, span: Span(78..79) } +Token { kind: R_BRACE, span: Span(79..80) } +End { kind: RULE_DECL, span: Span(0..80) } +Error { message: "expecting identifier, found `)`", span: Span(77..78) } +End { kind: SOURCE_FILE, span: Span(0..80) } diff --git a/parser/src/parser/tests/testdata/with-error-4.in b/parser/src/parser/tests/testdata/with-error-4.in new file mode 100644 index 000000000..89080ca5b --- /dev/null +++ b/parser/src/parser/tests/testdata/with-error-4.in @@ -0,0 +1,6 @@ +rule test { + condition: + with foo = 1 : ( + foo. + ) +} \ No newline at end of file diff --git a/parser/src/parser/tests/testdata/with-error-5.ast b/parser/src/parser/tests/testdata/with-error-5.ast new file mode 100644 index 000000000..293ce4c1c --- /dev/null +++ b/parser/src/parser/tests/testdata/with-error-5.ast @@ -0,0 +1,2 @@ +ERRORS: +- SyntaxError { message: "expecting `=`, found `(`", span: Span(44..45) } diff --git a/parser/src/parser/tests/testdata/with-error-5.cst b/parser/src/parser/tests/testdata/with-error-5.cst new file mode 100644 index 000000000..b1152bf7c --- /dev/null +++ b/parser/src/parser/tests/testdata/with-error-5.cst @@ -0,0 +1,31 @@ +SOURCE_FILE@0..73 + RULE_DECL@0..73 + RULE_KW@0..4 "rule" + WHITESPACE@4..5 " " + IDENT@5..9 "test" + WHITESPACE@9..10 " " + L_BRACE@10..11 "{" + NEWLINE@11..12 "\n" + WHITESPACE@12..16 " " + CONDITION_BLK@16..71 + CONDITION_KW@16..25 "condition" + COLON@25..26 ":" + NEWLINE@26..27 "\n" + WHITESPACE@27..35 " " + ERROR@35..71 + WITH_KW@35..39 "with" + WHITESPACE@39..40 " " + IDENT@40..43 "foo" + WHITESPACE@43..44 " " + L_PAREN@44..45 "(" + NEWLINE@45..46 "\n" + WHITESPACE@46..58 " " + IDENT@58..61 "foo" + NEWLINE@61..62 "\n" + WHITESPACE@62..70 " " + R_PAREN@70..71 ")" + NEWLINE@71..72 "\n" + R_BRACE@72..73 "}" + +ERRORS: +- [44..45]: expecting `=`, found `(` diff --git a/parser/src/parser/tests/testdata/with-error-5.cststream b/parser/src/parser/tests/testdata/with-error-5.cststream new file mode 100644 index 000000000..8fd4ab4fc --- /dev/null +++ b/parser/src/parser/tests/testdata/with-error-5.cststream @@ -0,0 +1,33 @@ +Begin { kind: SOURCE_FILE, span: Span(0..73) } +Begin { kind: RULE_DECL, span: Span(0..73) } +Token { kind: RULE_KW, span: Span(0..4) } +Token { kind: WHITESPACE, span: Span(4..5) } +Token { kind: IDENT, span: Span(5..9) } +Token { kind: WHITESPACE, span: Span(9..10) } +Token { kind: L_BRACE, span: Span(10..11) } +Token { kind: NEWLINE, span: Span(11..12) } +Token { kind: WHITESPACE, span: Span(12..16) } +Begin { kind: CONDITION_BLK, span: Span(16..71) } +Token { kind: CONDITION_KW, span: Span(16..25) } +Token { kind: COLON, span: Span(25..26) } +Token { kind: NEWLINE, span: Span(26..27) } +Token { kind: WHITESPACE, span: Span(27..35) } +Begin { kind: ERROR, span: Span(35..71) } +Token { kind: WITH_KW, span: Span(35..39) } +Token { kind: WHITESPACE, span: Span(39..40) } +Token { kind: IDENT, span: Span(40..43) } +Token { kind: WHITESPACE, span: Span(43..44) } +Token { kind: L_PAREN, span: Span(44..45) } +Token { kind: NEWLINE, span: Span(45..46) } +Token { kind: WHITESPACE, span: Span(46..58) } +Token { kind: IDENT, span: Span(58..61) } +Token { kind: NEWLINE, span: Span(61..62) } +Token { kind: WHITESPACE, span: Span(62..70) } +Token { kind: R_PAREN, span: Span(70..71) } +End { kind: ERROR, span: Span(35..71) } +End { kind: CONDITION_BLK, span: Span(16..71) } +Token { kind: NEWLINE, span: Span(71..72) } +Token { kind: R_BRACE, span: Span(72..73) } +End { kind: RULE_DECL, span: Span(0..73) } +Error { message: "expecting `=`, found `(`", span: Span(44..45) } +End { kind: SOURCE_FILE, span: Span(0..73) } diff --git a/parser/src/parser/tests/testdata/with-error-5.in b/parser/src/parser/tests/testdata/with-error-5.in new file mode 100644 index 000000000..bcb6a1b77 --- /dev/null +++ b/parser/src/parser/tests/testdata/with-error-5.in @@ -0,0 +1,6 @@ +rule test { + condition: + with foo ( + foo + ) +} \ No newline at end of file diff --git a/parser/src/tokenizer/tokens.rs b/parser/src/tokenizer/tokens.rs index 77eb16750..cdb49eec5 100644 --- a/parser/src/tokenizer/tokens.rs +++ b/parser/src/tokenizer/tokens.rs @@ -4,6 +4,7 @@ use crate::Span; #[allow(clippy::upper_case_acronyms)] #[derive(Copy, Clone, PartialEq, Eq, Default)] #[repr(u8)] +#[derive(Hash)] pub(crate) enum TokenId { // Keywords. ALL_KW,