From c273eacc4b2b6d3732262407869ce8c0b12d6d8b Mon Sep 17 00:00:00 2001 From: Nikolaos Georgiou Date: Sat, 14 Feb 2026 17:01:25 +0100 Subject: [PATCH 01/14] Remove wildcard imports --- rusty_parser/src/expr/binary_expression.rs | 2 +- rusty_parser/src/expr/built_in_function_call.rs | 2 +- rusty_parser/src/expr/file_handle.rs | 2 +- rusty_parser/src/expr/function_call_or_array_element.rs | 2 +- rusty_parser/src/expr/integer_or_long_literal.rs | 2 +- rusty_parser/src/expr/parenthesis.rs | 2 +- rusty_parser/src/expr/parsers.rs | 1 + rusty_parser/src/expr/property.rs | 2 +- rusty_parser/src/expr/single_or_double_literal.rs | 2 +- rusty_parser/src/expr/string_literal.rs | 2 +- rusty_parser/src/expr/unary_expression.rs | 2 +- rusty_parser/src/expr/variable.rs | 4 +++- 12 files changed, 14 insertions(+), 11 deletions(-) diff --git a/rusty_parser/src/expr/binary_expression.rs b/rusty_parser/src/expr/binary_expression.rs index 1cafc9a2..f91a6155 100644 --- a/rusty_parser/src/expr/binary_expression.rs +++ b/rusty_parser/src/expr/binary_expression.rs @@ -7,7 +7,7 @@ use crate::expr::expression_pos_p; use crate::input::StringView; use crate::pc_specific::{OrExpected, WithPos, lead_opt_ws, lead_ws}; use crate::tokens::{TokenType, any_token}; -use crate::*; +use crate::{ExpressionPos, ExpressionPosTrait, ExpressionTrait, Keyword, Operator}; // result ::= pub fn parser() -> impl Parser { diff --git a/rusty_parser/src/expr/built_in_function_call.rs b/rusty_parser/src/expr/built_in_function_call.rs index 716c785d..4f8c9392 100644 --- a/rusty_parser/src/expr/built_in_function_call.rs +++ b/rusty_parser/src/expr/built_in_function_call.rs @@ -3,7 +3,7 @@ use rusty_pc::Parser; use crate::built_ins::built_in_function_call_p; use crate::input::StringView; use crate::pc_specific::WithPos; -use crate::{ParserError, *}; +use crate::{ExpressionPos, ParserError}; pub fn parser() -> impl Parser { built_in_function_call_p().with_pos() diff --git a/rusty_parser/src/expr/file_handle.rs b/rusty_parser/src/expr/file_handle.rs index 290f5a62..c94bdf7f 100644 --- a/rusty_parser/src/expr/file_handle.rs +++ b/rusty_parser/src/expr/file_handle.rs @@ -8,7 +8,7 @@ use crate::expr::ws_expr_pos_p; use crate::input::StringView; use crate::pc_specific::*; use crate::tokens::{TokenType, any_token_of, pound}; -use crate::*; +use crate::{Expression, ExpressionPos, FileHandle}; pub fn file_handle_p() -> impl Parser, Error = ParserError> { diff --git a/rusty_parser/src/expr/function_call_or_array_element.rs b/rusty_parser/src/expr/function_call_or_array_element.rs index e056fca9..5eb8768c 100644 --- a/rusty_parser/src/expr/function_call_or_array_element.rs +++ b/rusty_parser/src/expr/function_call_or_array_element.rs @@ -4,7 +4,7 @@ use crate::core::name_as_tokens_p; use crate::expr::expression_pos_p; use crate::input::StringView; use crate::pc_specific::{WithPos, csv, in_parenthesis}; -use crate::{ParserError, *}; +use crate::{Expression, ExpressionPos, Expressions, NameAsTokens, ParserError}; // function_call ::= "(" * ")" // function-name ::= diff --git a/rusty_parser/src/expr/integer_or_long_literal.rs b/rusty_parser/src/expr/integer_or_long_literal.rs index 457672dd..4f947750 100644 --- a/rusty_parser/src/expr/integer_or_long_literal.rs +++ b/rusty_parser/src/expr/integer_or_long_literal.rs @@ -5,7 +5,7 @@ use crate::error::ParserError; use crate::input::StringView; use crate::pc_specific::WithPos; use crate::tokens::{TokenType, any_token_of}; -use crate::*; +use crate::{Expression, ExpressionPos}; // result ::= | | pub fn parser() -> impl Parser { diff --git a/rusty_parser/src/expr/parenthesis.rs b/rusty_parser/src/expr/parenthesis.rs index 7161d655..f889e98b 100644 --- a/rusty_parser/src/expr/parenthesis.rs +++ b/rusty_parser/src/expr/parenthesis.rs @@ -3,7 +3,7 @@ use rusty_pc::*; use crate::expr::expression_pos_p; use crate::input::StringView; use crate::pc_specific::{OrExpected, WithPos, in_parenthesis}; -use crate::{ParserError, *}; +use crate::{Expression, ExpressionPos, ParserError}; pub fn parser() -> impl Parser { in_parenthesis(expression_pos_p().or_expected("expression inside parenthesis")) diff --git a/rusty_parser/src/expr/parsers.rs b/rusty_parser/src/expr/parsers.rs index 52bfaf9f..08a3aa37 100644 --- a/rusty_parser/src/expr/parsers.rs +++ b/rusty_parser/src/expr/parsers.rs @@ -6,6 +6,7 @@ use crate::input::StringView; use crate::pc_specific::*; use crate::tokens::comma_ws; use crate::{ExpressionPos, ExpressionTrait, Expressions, ParserError}; + /// `( expr [, expr]* )` pub fn in_parenthesis_csv_expressions_non_opt( expectation: &str, diff --git a/rusty_parser/src/expr/property.rs b/rusty_parser/src/expr/property.rs index 0f99f186..6de20254 100644 --- a/rusty_parser/src/expr/property.rs +++ b/rusty_parser/src/expr/property.rs @@ -6,7 +6,7 @@ use crate::error::ParserError; use crate::input::StringView; use crate::pc_specific::OrExpected; use crate::tokens::dot; -use crate::*; +use crate::{BareName, Expression, ExpressionPos, ExpressionType, Name, NameAsTokens}; // property ::= "." // property-name ::= diff --git a/rusty_parser/src/expr/single_or_double_literal.rs b/rusty_parser/src/expr/single_or_double_literal.rs index 056eacda..aee51c18 100644 --- a/rusty_parser/src/expr/single_or_double_literal.rs +++ b/rusty_parser/src/expr/single_or_double_literal.rs @@ -3,7 +3,7 @@ use rusty_pc::*; use crate::input::StringView; use crate::pc_specific::WithPos; use crate::tokens::{digits, dot, pound}; -use crate::{ParserError, *}; +use crate::{Expression, ExpressionPos, ParserError}; // single ::= . // single ::= . (without leading zero) diff --git a/rusty_parser/src/expr/string_literal.rs b/rusty_parser/src/expr/string_literal.rs index 9dd708f9..c5c5769f 100644 --- a/rusty_parser/src/expr/string_literal.rs +++ b/rusty_parser/src/expr/string_literal.rs @@ -4,7 +4,7 @@ use rusty_pc::*; use crate::input::StringView; use crate::pc_specific::*; use crate::tokens::{MatchMode, TokenType, any_symbol_of, any_token_of}; -use crate::{ParserError, *}; +use crate::{Expression, ExpressionPos, ParserError}; pub fn parser() -> impl Parser { surround( diff --git a/rusty_parser/src/expr/unary_expression.rs b/rusty_parser/src/expr/unary_expression.rs index 3e478fdd..1de007a2 100644 --- a/rusty_parser/src/expr/unary_expression.rs +++ b/rusty_parser/src/expr/unary_expression.rs @@ -5,7 +5,7 @@ use crate::expr::{expression_pos_p, guard}; use crate::input::StringView; use crate::pc_specific::{OrExpected, WithPos, keyword}; use crate::tokens::minus_sign; -use crate::{ParserError, *}; +use crate::{ExpressionPos, ExpressionPosTrait, Keyword, ParserError, UnaryOperator}; pub fn parser() -> impl Parser { seq2( diff --git a/rusty_parser/src/expr/variable.rs b/rusty_parser/src/expr/variable.rs index 0d885f62..f7aac525 100644 --- a/rusty_parser/src/expr/variable.rs +++ b/rusty_parser/src/expr/variable.rs @@ -6,7 +6,9 @@ use rusty_pc::*; use crate::core::{name_as_tokens_p, token_to_type_qualifier}; use crate::input::StringView; use crate::pc_specific::WithPos; -use crate::{ParserError, *}; +use crate::{ + BareName, Expression, ExpressionPos, ExpressionType, Name, NameAsTokens, ParserError, VariableInfo +}; // variable ::= // | From f43d48aed045f143651b4bfdc5a6d3ca3bce180d Mon Sep 17 00:00:00 2001 From: Nikolaos Georgiou Date: Sat, 14 Feb 2026 19:05:18 +0100 Subject: [PATCH 02/14] Reduced visibility of specialized expression parsers --- rusty_parser/src/expr/binary_expression.rs | 2 +- rusty_parser/src/expr/built_in_function_call.rs | 2 +- rusty_parser/src/expr/function_call_or_array_element.rs | 2 +- rusty_parser/src/expr/guard.rs | 2 +- rusty_parser/src/expr/integer_or_long_literal.rs | 2 +- rusty_parser/src/expr/parenthesis.rs | 2 +- rusty_parser/src/expr/single_or_double_literal.rs | 2 +- rusty_parser/src/expr/string_literal.rs | 2 +- rusty_parser/src/expr/unary_expression.rs | 2 +- rusty_parser/src/expr/variable.rs | 3 +-- 10 files changed, 10 insertions(+), 11 deletions(-) diff --git a/rusty_parser/src/expr/binary_expression.rs b/rusty_parser/src/expr/binary_expression.rs index f91a6155..88dba810 100644 --- a/rusty_parser/src/expr/binary_expression.rs +++ b/rusty_parser/src/expr/binary_expression.rs @@ -10,7 +10,7 @@ use crate::tokens::{TokenType, any_token}; use crate::{ExpressionPos, ExpressionPosTrait, ExpressionTrait, Keyword, Operator}; // result ::= -pub fn parser() -> impl Parser { +pub(super) fn parser() -> impl Parser { non_bin_expr() .then_with_in_context( second_parser(), diff --git a/rusty_parser/src/expr/built_in_function_call.rs b/rusty_parser/src/expr/built_in_function_call.rs index 4f8c9392..84a48092 100644 --- a/rusty_parser/src/expr/built_in_function_call.rs +++ b/rusty_parser/src/expr/built_in_function_call.rs @@ -5,7 +5,7 @@ use crate::input::StringView; use crate::pc_specific::WithPos; use crate::{ExpressionPos, ParserError}; -pub fn parser() -> impl Parser { +pub(super) fn parser() -> impl Parser { built_in_function_call_p().with_pos() } diff --git a/rusty_parser/src/expr/function_call_or_array_element.rs b/rusty_parser/src/expr/function_call_or_array_element.rs index 5eb8768c..a23a35fe 100644 --- a/rusty_parser/src/expr/function_call_or_array_element.rs +++ b/rusty_parser/src/expr/function_call_or_array_element.rs @@ -16,7 +16,7 @@ use crate::{Expression, ExpressionPos, Expressions, NameAsTokens, ParserError}; // // A function can be qualified. -pub fn parser() -> impl Parser { +pub(super) fn parser() -> impl Parser { name_as_tokens_p() .and( in_parenthesis(csv(expression_pos_p()).or_default()), diff --git a/rusty_parser/src/expr/guard.rs b/rusty_parser/src/expr/guard.rs index d33aec3e..962a9210 100644 --- a/rusty_parser/src/expr/guard.rs +++ b/rusty_parser/src/expr/guard.rs @@ -8,7 +8,7 @@ use crate::tokens::{any_symbol_of, any_token_of}; /// `result ::= " " | "("` /// /// The "(" will be undone. -pub fn parser() -> impl Parser { +pub(super) fn parser() -> impl Parser { whitespace_guard() .or(lparen_guard()) .with_expected_message("Expected: '(' or whitespace") diff --git a/rusty_parser/src/expr/integer_or_long_literal.rs b/rusty_parser/src/expr/integer_or_long_literal.rs index 4f947750..d8d1f4aa 100644 --- a/rusty_parser/src/expr/integer_or_long_literal.rs +++ b/rusty_parser/src/expr/integer_or_long_literal.rs @@ -8,7 +8,7 @@ use crate::tokens::{TokenType, any_token_of}; use crate::{Expression, ExpressionPos}; // result ::= | | -pub fn parser() -> impl Parser { +pub(super) fn parser() -> impl Parser { any_token_of!( TokenType::Digits, TokenType::HexDigits, diff --git a/rusty_parser/src/expr/parenthesis.rs b/rusty_parser/src/expr/parenthesis.rs index f889e98b..d18dbd7c 100644 --- a/rusty_parser/src/expr/parenthesis.rs +++ b/rusty_parser/src/expr/parenthesis.rs @@ -5,7 +5,7 @@ use crate::input::StringView; use crate::pc_specific::{OrExpected, WithPos, in_parenthesis}; use crate::{Expression, ExpressionPos, ParserError}; -pub fn parser() -> impl Parser { +pub(super) fn parser() -> impl Parser { in_parenthesis(expression_pos_p().or_expected("expression inside parenthesis")) .map(|child| Expression::Parenthesis(Box::new(child))) .with_pos() diff --git a/rusty_parser/src/expr/single_or_double_literal.rs b/rusty_parser/src/expr/single_or_double_literal.rs index aee51c18..d519bcf5 100644 --- a/rusty_parser/src/expr/single_or_double_literal.rs +++ b/rusty_parser/src/expr/single_or_double_literal.rs @@ -12,7 +12,7 @@ use crate::{Expression, ExpressionPos, ParserError}; // TODO support more qualifiers besides '#' -pub fn parser() -> impl Parser { +pub(super) fn parser() -> impl Parser { // read integer digits optionally (might start with . e.g. `.123`) digits() .to_option() diff --git a/rusty_parser/src/expr/string_literal.rs b/rusty_parser/src/expr/string_literal.rs index c5c5769f..11ec55a1 100644 --- a/rusty_parser/src/expr/string_literal.rs +++ b/rusty_parser/src/expr/string_literal.rs @@ -6,7 +6,7 @@ use crate::pc_specific::*; use crate::tokens::{MatchMode, TokenType, any_symbol_of, any_token_of}; use crate::{Expression, ExpressionPos, ParserError}; -pub fn parser() -> impl Parser { +pub(super) fn parser() -> impl Parser { surround( string_delimiter(), inside_string(), diff --git a/rusty_parser/src/expr/unary_expression.rs b/rusty_parser/src/expr/unary_expression.rs index 1de007a2..0eef2c75 100644 --- a/rusty_parser/src/expr/unary_expression.rs +++ b/rusty_parser/src/expr/unary_expression.rs @@ -7,7 +7,7 @@ use crate::pc_specific::{OrExpected, WithPos, keyword}; use crate::tokens::minus_sign; use crate::{ExpressionPos, ExpressionPosTrait, Keyword, ParserError, UnaryOperator}; -pub fn parser() -> impl Parser { +pub(super) fn parser() -> impl Parser { seq2( unary_op(), expression_pos_p().or_expected("expression after unary operator"), diff --git a/rusty_parser/src/expr/variable.rs b/rusty_parser/src/expr/variable.rs index f7aac525..14ab606a 100644 --- a/rusty_parser/src/expr/variable.rs +++ b/rusty_parser/src/expr/variable.rs @@ -1,4 +1,3 @@ -// TODO consider nesting variable/function_call modules inside property as they are only used there use std::collections::VecDeque; use rusty_pc::*; @@ -17,7 +16,7 @@ use crate::{ // must not be followed by parenthesis (solved by ordering of parsers) // // if contains dots, it might be converted to a property expression -pub fn parser() -> impl Parser { +pub(super) fn parser() -> impl Parser { name_as_tokens_p().map(map_to_expr).with_pos() } From daab04c127587509b7759ebc1f1e4a1ec2276221 Mon Sep 17 00:00:00 2001 From: Nikolaos Georgiou Date: Sat, 14 Feb 2026 19:22:16 +0100 Subject: [PATCH 03/14] Removed guard parser --- rusty_parser/src/expr/binary_expression.rs | 29 +++++++++++----------- rusty_parser/src/expr/guard.rs | 23 ----------------- rusty_parser/src/expr/mod.rs | 1 - rusty_parser/src/expr/parsers.rs | 10 +------- rusty_parser/src/expr/unary_expression.rs | 27 ++++++++++++-------- 5 files changed, 33 insertions(+), 57 deletions(-) delete mode 100644 rusty_parser/src/expr/guard.rs diff --git a/rusty_parser/src/expr/binary_expression.rs b/rusty_parser/src/expr/binary_expression.rs index 88dba810..d36b0ad1 100644 --- a/rusty_parser/src/expr/binary_expression.rs +++ b/rusty_parser/src/expr/binary_expression.rs @@ -3,7 +3,7 @@ use rusty_pc::and::TupleCombiner; use rusty_pc::*; use crate::error::ParserError; -use crate::expr::expression_pos_p; +use crate::expr::{expression_pos_p, ws_expr_pos_p}; use crate::input::StringView; use crate::pc_specific::{OrExpected, WithPos, lead_opt_ws, lead_ws}; use crate::tokens::{TokenType, any_token}; @@ -31,7 +31,7 @@ fn second_parser() -> impl Parser< Error = ParserError, > { operator() - .then_with_in_context(third_parser(), is_keyword_op, TupleCombiner) + .then_with_in_context(expr_after_binary_operator(), is_keyword_op, TupleCombiner) .to_option() } @@ -39,20 +39,21 @@ fn is_keyword_op(op: &Positioned) -> bool { op.element == Operator::And || op.element == Operator::Or || op.element == Operator::Modulo } -fn third_parser() -> impl Parser { +fn expr_after_binary_operator() +-> impl Parser { + // boxed breaks apart the recursive type evaluation IifParser::new( - super::guard::parser().to_fatal(), - super::guard::parser().to_option().map_to_unit(), + // the previous operator is a keyword op, must have whitespace or parenthesis + ws_expr_pos_p() + .boxed() + .or_expected("expression after operator"), + // the previous operator is a symbol, whitespace is optional + lead_opt_ws( + expression_pos_p() + .boxed() + .or_expected("expression after operator"), + ), ) - .and_keep_right(right_side_expr().no_context()) -} - -/// Parses the right side expression, after having parsed the binary operator -fn right_side_expr() -> impl Parser { - // boxed breaks apart the recursive type evaluation - expression_pos_p() - .or_expected("expression after operator") - .boxed() } fn non_bin_expr() -> impl Parser { diff --git a/rusty_parser/src/expr/guard.rs b/rusty_parser/src/expr/guard.rs deleted file mode 100644 index 962a9210..00000000 --- a/rusty_parser/src/expr/guard.rs +++ /dev/null @@ -1,23 +0,0 @@ -use rusty_pc::*; - -use crate::ParserError; -use crate::input::StringView; -use crate::pc_specific::{WithExpected, whitespace_ignoring}; -use crate::tokens::{any_symbol_of, any_token_of}; - -/// `result ::= " " | "("` -/// -/// The "(" will be undone. -pub(super) fn parser() -> impl Parser { - whitespace_guard() - .or(lparen_guard()) - .with_expected_message("Expected: '(' or whitespace") -} - -fn whitespace_guard() -> impl Parser { - whitespace_ignoring() -} - -fn lparen_guard() -> impl Parser { - any_symbol_of!('(').map_to_unit().peek() -} diff --git a/rusty_parser/src/expr/mod.rs b/rusty_parser/src/expr/mod.rs index 89b90a7f..1351a363 100644 --- a/rusty_parser/src/expr/mod.rs +++ b/rusty_parser/src/expr/mod.rs @@ -2,7 +2,6 @@ mod binary_expression; mod built_in_function_call; pub mod file_handle; mod function_call_or_array_element; -mod guard; mod integer_or_long_literal; mod opt_second_expression; mod parenthesis; diff --git a/rusty_parser/src/expr/parsers.rs b/rusty_parser/src/expr/parsers.rs index 08a3aa37..f1d1ef3c 100644 --- a/rusty_parser/src/expr/parsers.rs +++ b/rusty_parser/src/expr/parsers.rs @@ -49,9 +49,7 @@ pub fn expression_pos_p() -> impl Parser /// ``` pub fn ws_expr_pos_p() -> impl Parser { - // ws* ( expr ) - // ws+ expr - preceded_by_ws(expression_pos_p()) + super::parenthesis::parser().or(lead_ws(expression_pos_p())) } /// Parses an expression that is either followed by whitespace @@ -84,12 +82,6 @@ pub fn ws_expr_pos_ws_p() -> impl Parser, -) -> impl Parser { - super::guard::parser().and_keep_right(parser) -} - fn followed_by_ws( parser: impl Parser, ) -> impl Parser { diff --git a/rusty_parser/src/expr/unary_expression.rs b/rusty_parser/src/expr/unary_expression.rs index 0eef2c75..95f95574 100644 --- a/rusty_parser/src/expr/unary_expression.rs +++ b/rusty_parser/src/expr/unary_expression.rs @@ -1,27 +1,34 @@ use rusty_common::Positioned; use rusty_pc::*; -use crate::expr::{expression_pos_p, guard}; +use crate::expr::{expression_pos_p, ws_expr_pos_p}; use crate::input::StringView; use crate::pc_specific::{OrExpected, WithPos, keyword}; use crate::tokens::minus_sign; use crate::{ExpressionPos, ExpressionPosTrait, Keyword, ParserError, UnaryOperator}; pub(super) fn parser() -> impl Parser { - seq2( - unary_op(), - expression_pos_p().or_expected("expression after unary operator"), - |Positioned { element: op, pos }, expr| expr.apply_unary_priority_order(op, pos), - ) + unary_minus() + .or(unary_not()) + .map(|(Positioned { element: op, pos }, expr)| expr.apply_unary_priority_order(op, pos)) } -fn unary_op() -> impl Parser, Error = ParserError> { +fn unary_minus() +-> impl Parser, ExpressionPos), Error = ParserError> +{ minus_sign() .map(|_| UnaryOperator::Minus) - .or(keyword(Keyword::Not) - .and_keep_right(guard::parser().to_fatal()) - .map(|_| UnaryOperator::Not)) .with_pos() + .and_tuple(expression_pos_p().or_expected("expression after -")) +} + +fn unary_not() +-> impl Parser, ExpressionPos), Error = ParserError> +{ + keyword(Keyword::Not) + .map(|_| UnaryOperator::Not) + .with_pos() + .and_tuple(ws_expr_pos_p().or_expected("expression after NOT")) } #[cfg(test)] From 6298c3d7477b1cff0eab478c5aee4991242e80c9 Mon Sep 17 00:00:00 2001 From: Nikolaos Georgiou Date: Sat, 14 Feb 2026 19:42:45 +0100 Subject: [PATCH 04/14] Refactored binary_expression implementation --- rusty_parser/src/expr/binary_expression.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/rusty_parser/src/expr/binary_expression.rs b/rusty_parser/src/expr/binary_expression.rs index d36b0ad1..6901eb4f 100644 --- a/rusty_parser/src/expr/binary_expression.rs +++ b/rusty_parser/src/expr/binary_expression.rs @@ -44,16 +44,11 @@ fn expr_after_binary_operator() // boxed breaks apart the recursive type evaluation IifParser::new( // the previous operator is a keyword op, must have whitespace or parenthesis - ws_expr_pos_p() - .boxed() - .or_expected("expression after operator"), + ws_expr_pos_p().boxed(), // the previous operator is a symbol, whitespace is optional - lead_opt_ws( - expression_pos_p() - .boxed() - .or_expected("expression after operator"), - ), + lead_opt_ws(expression_pos_p().boxed()), ) + .or_expected("expression after operator") } fn non_bin_expr() -> impl Parser { From e73133786d163d34684835ca03e1c78a54069144 Mon Sep 17 00:00:00 2001 From: Nikolaos Georgiou Date: Sat, 14 Feb 2026 19:44:17 +0100 Subject: [PATCH 05/14] Updated documentation --- rusty_parser/src/expr/parsers.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rusty_parser/src/expr/parsers.rs b/rusty_parser/src/expr/parsers.rs index f1d1ef3c..c334c3a5 100644 --- a/rusty_parser/src/expr/parsers.rs +++ b/rusty_parser/src/expr/parsers.rs @@ -44,9 +44,8 @@ pub fn expression_pos_p() -> impl Parser | -/// | -/// +/// | +/// /// ``` pub fn ws_expr_pos_p() -> impl Parser { super::parenthesis::parser().or(lead_ws(expression_pos_p())) From 33a81cf3add6c6910e3ec6f14891d017179e7ef9 Mon Sep 17 00:00:00 2001 From: Nikolaos Georgiou Date: Sat, 14 Feb 2026 19:50:35 +0100 Subject: [PATCH 06/14] Refactored operator parser of binary_expression --- rusty_parser/src/expr/binary_expression.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rusty_parser/src/expr/binary_expression.rs b/rusty_parser/src/expr/binary_expression.rs index 6901eb4f..e1bd323c 100644 --- a/rusty_parser/src/expr/binary_expression.rs +++ b/rusty_parser/src/expr/binary_expression.rs @@ -73,7 +73,7 @@ fn operator() -> impl Parser, Er // no whitespace needed lead_opt_ws(operator_p()), // whitespace needed - lead_ws(operator_p()).or(lead_opt_ws(symbol_operator_p())), + symbol_operator_p().or(lead_ws(operator_p())), ) } From 7a97458822da10c312e0f05da85b51b047912c0c Mon Sep 17 00:00:00 2001 From: Nikolaos Georgiou Date: Sat, 14 Feb 2026 19:53:41 +0100 Subject: [PATCH 07/14] Deprecate `expr_pos_ws_p` --- rusty_parser/src/expr/parsers.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rusty_parser/src/expr/parsers.rs b/rusty_parser/src/expr/parsers.rs index c334c3a5..4849f5d0 100644 --- a/rusty_parser/src/expr/parsers.rs +++ b/rusty_parser/src/expr/parsers.rs @@ -62,6 +62,7 @@ pub fn ws_expr_pos_p() -> impl Parser | /// /// ``` +#[deprecated] pub fn expr_pos_ws_p() -> impl Parser { followed_by_ws(expression_pos_p()) } From 1ad502f4a5e68c6e1628e8d1666349c4abdf4206 Mon Sep 17 00:00:00 2001 From: Nikolaos Georgiou Date: Sat, 14 Feb 2026 20:46:10 +0100 Subject: [PATCH 08/14] Added expr_ws_keyword_p and demand_expr_ws_keyword_p --- rusty_parser/src/built_ins/field.rs | 9 +-- rusty_parser/src/core/for_loop.rs | 15 ++--- .../src/expr/opt_second_expression.rs | 1 + rusty_parser/src/expr/parsers.rs | 57 +++++++++++++------ 4 files changed, 52 insertions(+), 30 deletions(-) diff --git a/rusty_parser/src/built_ins/field.rs b/rusty_parser/src/built_ins/field.rs index 1b711a28..ef16c3c2 100644 --- a/rusty_parser/src/built_ins/field.rs +++ b/rusty_parser/src/built_ins/field.rs @@ -1,7 +1,7 @@ use rusty_common::*; use rusty_pc::*; -use crate::expr::expr_pos_ws_p; +use crate::expr::expr_ws_keyword_p; use crate::expr::file_handle::file_handle_p; use crate::input::StringView; use crate::pc_specific::*; @@ -23,12 +23,9 @@ pub fn parse() -> impl Parser impl Parser { - seq3( - expr_pos_ws_p(), - keyword_ws_p(Keyword::As), + expr_ws_keyword_p(Keyword::As).and_tuple(demand_lead_ws( name_p().with_pos().or_expected("variable name"), - |width, _, name| (width, name), - ) + )) } fn build_args( diff --git a/rusty_parser/src/core/for_loop.rs b/rusty_parser/src/core/for_loop.rs index 47f956d4..03c82180 100644 --- a/rusty_parser/src/core/for_loop.rs +++ b/rusty_parser/src/core/for_loop.rs @@ -2,7 +2,9 @@ use rusty_pc::*; use crate::core::statements::zero_or_more_statements; use crate::error::ParserError; -use crate::expr::{expr_pos_ws_p, opt_second_expression_after_keyword, property, ws_expr_pos_p}; +use crate::expr::{ + demand_expr_ws_keyword_p, opt_second_expression_after_keyword, property, ws_expr_pos_p +}; use crate::input::StringView; use crate::pc_specific::*; use crate::tokens::equal_sign_ws; @@ -52,14 +54,13 @@ fn parse_for_step_p() -> impl Parser< fn parse_for_p() -> impl Parser { - seq6( - keyword_ws_p(Keyword::For), - property::parser().or_expected("name after FOR"), + seq5( + keyword_ignoring(Keyword::For), + demand_lead_ws(property::parser().or_expected("name after FOR")), equal_sign_ws(), - expr_pos_ws_p().or_expected("lower bound of FOR loop"), - keyword(Keyword::To), + demand_expr_ws_keyword_p("lower bound of FOR loop", Keyword::To), ws_expr_pos_p().or_expected("upper bound of FOR loop"), - |_, name, _, lower_bound, _, upper_bound| (name, lower_bound, upper_bound), + |_, name, _, lower_bound, upper_bound| (name, lower_bound, upper_bound), ) } diff --git a/rusty_parser/src/expr/opt_second_expression.rs b/rusty_parser/src/expr/opt_second_expression.rs index 6e40a23b..eaa146c0 100644 --- a/rusty_parser/src/expr/opt_second_expression.rs +++ b/rusty_parser/src/expr/opt_second_expression.rs @@ -13,6 +13,7 @@ use crate::{ExpressionPos, Keyword}; /// If the keyword is present, the second expression is mandatory. /// /// Example: `FOR I = 1 TO 100 [STEP 5]` +#[deprecated] pub fn opt_second_expression_after_keyword( first_parser: P, keyword: Keyword, diff --git a/rusty_parser/src/expr/parsers.rs b/rusty_parser/src/expr/parsers.rs index 4849f5d0..011dcdb8 100644 --- a/rusty_parser/src/expr/parsers.rs +++ b/rusty_parser/src/expr/parsers.rs @@ -5,7 +5,7 @@ use crate::expr::opt_second_expression::conditionally_opt_whitespace; use crate::input::StringView; use crate::pc_specific::*; use crate::tokens::comma_ws; -use crate::{ExpressionPos, ExpressionTrait, Expressions, ParserError}; +use crate::{ExpressionPos, ExpressionTrait, Expressions, Keyword, ParserError}; /// `( expr [, expr]* )` pub fn in_parenthesis_csv_expressions_non_opt( @@ -51,22 +51,6 @@ pub fn ws_expr_pos_p() -> impl Parser | -/// | -/// -/// ``` -#[deprecated] -pub fn expr_pos_ws_p() -> impl Parser { - followed_by_ws(expression_pos_p()) -} - /// Parses an expression that is either surrounded by whitespace /// or is a parenthesis expression. /// @@ -78,6 +62,7 @@ pub fn expr_pos_ws_p() -> impl Parser | /// /// ``` +#[deprecated] pub fn ws_expr_pos_ws_p() -> impl Parser { followed_by_ws(ws_expr_pos_p()) } @@ -97,3 +82,41 @@ fn eager_expression_pos_p() -> impl Parser impl Parser { + expr_ws_followed_by(expression_pos_p(), keyword_ignoring(keyword)) +} + +/// Parses an expression, failing fatally with the given expectation message if it can't be parsed. +/// Then it demands whitespace, unless the expression is a parenthesis. +/// Finally it demands the given keyword. +pub fn demand_expr_ws_keyword_p( + expectation: &str, + keyword: Keyword, +) -> impl Parser { + expr_ws_followed_by( + expression_pos_p().or_expected(expectation), + keyword_ignoring(keyword), + ) +} + +/// Parses the expression using the given parser, +/// then demands whitespace, unless the expression is a parenthesis. +/// Finally it demands the second parser. +pub fn expr_ws_followed_by( + expr_parser: impl Parser, + other_parser: impl Parser, +) -> impl Parser { + expr_parser.then_with_in_context( + conditionally_opt_whitespace() + .to_fatal() + .and_keep_right(other_parser.to_fatal().no_context()), + |e| e.is_parenthesis(), + KeepLeftCombiner, + ) +} From 40e07152a9a4cb40d84afaf58a1f4145002b3654 Mon Sep 17 00:00:00 2001 From: Nikolaos Georgiou Date: Sat, 14 Feb 2026 21:01:28 +0100 Subject: [PATCH 09/14] Added `demand_ws_expr_ws_keyword_p` and `ws_expr_ws_keyword_p` --- rusty_parser/src/built_ins/name.rs | 9 ++++----- rusty_parser/src/built_ins/view_print.rs | 10 +++------- rusty_parser/src/core/if_block.rs | 22 +++++++++------------- rusty_parser/src/expr/parsers.rs | 16 ++++++++++++++++ rusty_pc/src/and.rs | 10 +++++++++- 5 files changed, 41 insertions(+), 26 deletions(-) diff --git a/rusty_parser/src/built_ins/name.rs b/rusty_parser/src/built_ins/name.rs index 09412858..0d211c1a 100644 --- a/rusty_parser/src/built_ins/name.rs +++ b/rusty_parser/src/built_ins/name.rs @@ -1,16 +1,15 @@ use rusty_pc::*; -use crate::expr::{ws_expr_pos_p, ws_expr_pos_ws_p}; +use crate::expr::{demand_ws_expr_ws_keyword_p, ws_expr_pos_p}; use crate::input::StringView; use crate::pc_specific::*; use crate::{BuiltInSub, ParserError, *}; pub fn parse() -> impl Parser { - seq4( + seq3( keyword(Keyword::Name), - ws_expr_pos_ws_p().or_expected("old file name"), - keyword(Keyword::As), + demand_ws_expr_ws_keyword_p("old file name", Keyword::As), ws_expr_pos_p().or_expected("new file name"), - |_, l, _, r| Statement::built_in_sub_call(BuiltInSub::Name, vec![l, r]), + |_, l, r| Statement::built_in_sub_call(BuiltInSub::Name, vec![l, r]), ) } diff --git a/rusty_parser/src/built_ins/view_print.rs b/rusty_parser/src/built_ins/view_print.rs index e3527f8f..03fecd68 100644 --- a/rusty_parser/src/built_ins/view_print.rs +++ b/rusty_parser/src/built_ins/view_print.rs @@ -1,6 +1,7 @@ +use rusty_pc::and::VecCombiner; use rusty_pc::*; -use crate::expr::{ws_expr_pos_p, ws_expr_pos_ws_p}; +use crate::expr::{ws_expr_pos_p, ws_expr_ws_keyword_p}; use crate::input::StringView; use crate::pc_specific::*; use crate::{BuiltInSub, ParserError, *}; @@ -12,12 +13,7 @@ pub fn parse() -> impl Parser impl Parser { - seq3( - ws_expr_pos_ws_p(), - keyword(Keyword::To), - ws_expr_pos_p().or_expected("expression"), - |l, _, r| vec![l, r], - ) + ws_expr_ws_keyword_p(Keyword::To).and(ws_expr_pos_p().or_expected("expression"), VecCombiner) } #[cfg(test)] diff --git a/rusty_parser/src/core/if_block.rs b/rusty_parser/src/core/if_block.rs index b912e4c3..e6207fe0 100644 --- a/rusty_parser/src/core/if_block.rs +++ b/rusty_parser/src/core/if_block.rs @@ -5,7 +5,7 @@ use crate::core::single_line_statements::{ single_line_non_comment_statements_p, single_line_statements_p }; use crate::core::statements::zero_or_more_statements; -use crate::expr::ws_expr_pos_ws_p; +use crate::expr::demand_ws_expr_ws_keyword_p; use crate::input::StringView; use crate::pc_specific::*; use crate::{ParserError, *}; @@ -35,12 +35,10 @@ pub fn if_block_p() -> impl Parser impl Parser { - seq3( - keyword(Keyword::If), - ws_expr_pos_ws_p().or_expected("expression after IF"), - keyword(Keyword::Then), - |_, m, _| m, - ) + keyword(Keyword::If).and_keep_right(demand_ws_expr_ws_keyword_p( + "expression after IF", + Keyword::Then, + )) } fn single_line_if_else_p() -> impl Parser< @@ -79,12 +77,10 @@ fn multi_line_if_p() -> impl Parser< } fn else_if_expr_then_p() -> impl Parser { - seq3( - keyword(Keyword::ElseIf), - ws_expr_pos_ws_p().or_expected("expression after ELSEIF"), - keyword(Keyword::Then), - |_, m, _| m, - ) + keyword(Keyword::ElseIf).and_keep_right(demand_ws_expr_ws_keyword_p( + "expression after ELSEIF", + Keyword::Then, + )) } fn else_if_block_p() -> impl Parser { diff --git a/rusty_parser/src/expr/parsers.rs b/rusty_parser/src/expr/parsers.rs index 011dcdb8..fb564738 100644 --- a/rusty_parser/src/expr/parsers.rs +++ b/rusty_parser/src/expr/parsers.rs @@ -105,6 +105,22 @@ pub fn demand_expr_ws_keyword_p( ) } +pub fn ws_expr_ws_keyword_p( + keyword: Keyword, +) -> impl Parser { + expr_ws_followed_by(ws_expr_pos_p(), keyword_ignoring(keyword)) +} + +pub fn demand_ws_expr_ws_keyword_p( + expectation: &str, + keyword: Keyword, +) -> impl Parser { + expr_ws_followed_by( + ws_expr_pos_p().or_expected(expectation), + keyword_ignoring(keyword), + ) +} + /// Parses the expression using the given parser, /// then demands whitespace, unless the expression is a parenthesis. /// Finally it demands the second parser. diff --git a/rusty_pc/src/and.rs b/rusty_pc/src/and.rs index b92de24f..cdd418e4 100644 --- a/rusty_pc/src/and.rs +++ b/rusty_pc/src/and.rs @@ -103,9 +103,17 @@ where } } -/// Combines two vectors by concatenating them into one. +/// Combines items into a vector. pub struct VecCombiner; +/// Combines two items into a vector. +impl Combiner> for VecCombiner { + fn combine(&self, left: L, right: L) -> Vec { + vec![left, right] + } +} + +/// Combines two vectors by concatenating them into one. impl Combiner, Vec, Vec> for VecCombiner { fn combine(&self, mut left: Vec, mut right: Vec) -> Vec { left.append(&mut right); From 1aa3e7ded5274a96e88a8143addd016647306c03 Mon Sep 17 00:00:00 2001 From: Nikolaos Georgiou Date: Sat, 14 Feb 2026 21:17:24 +0100 Subject: [PATCH 10/14] Added some TODO comments --- rusty_parser/src/built_ins/open.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rusty_parser/src/built_ins/open.rs b/rusty_parser/src/built_ins/open.rs index fc2b9a08..80096021 100644 --- a/rusty_parser/src/built_ins/open.rs +++ b/rusty_parser/src/built_ins/open.rs @@ -11,6 +11,8 @@ pub fn parse() -> impl Parser impl Parser impl Parser { seq3( lead_ws(keyword_ignoring(Keyword::Len)), From c9adff3acf57a1a0a0023f1f4e46b1be9619639a Mon Sep 17 00:00:00 2001 From: Nikolaos Georgiou Date: Sun, 15 Feb 2026 14:07:20 +0100 Subject: [PATCH 11/14] Moved `conditionally_opt_whitespace` to `whitespace` module. Added `expr_keyword_opt_expr` parser. --- rusty_parser/src/core/dim_name.rs | 19 ++++++------- rusty_parser/src/core/select_case.rs | 11 ++------ .../src/expr/opt_second_expression.rs | 25 ++--------------- rusty_parser/src/expr/parsers.rs | 28 +++++++++++++++++-- rusty_parser/src/pc_specific/whitespace.rs | 23 ++++++++++++++- 5 files changed, 61 insertions(+), 45 deletions(-) diff --git a/rusty_parser/src/core/dim_name.rs b/rusty_parser/src/core/dim_name.rs index bb3eb404..803edc52 100644 --- a/rusty_parser/src/core/dim_name.rs +++ b/rusty_parser/src/core/dim_name.rs @@ -4,7 +4,9 @@ use rusty_pc::*; use crate::core::var_name; use crate::input::StringView; use crate::pc_specific::*; -use crate::{ParserError, *}; +use crate::{ + ArrayDimensions, BareName, BuiltInStyle, DimList, DimType, Name, ParserError, ToBareName, TypeQualifier, TypedName +}; pub type DimVar = TypedName; pub type DimVarPos = Positioned; @@ -122,10 +124,10 @@ where mod array_dimensions { use rusty_pc::*; - use crate::expr::{expression_pos_p, opt_second_expression_after_keyword}; + use crate::expr::expr_keyword_opt_expr; use crate::input::StringView; use crate::pc_specific::*; - use crate::{ParserError, *}; + use crate::{ArrayDimension, ArrayDimensions, Keyword, ParserError}; pub fn array_dimensions_p() -> impl Parser { @@ -137,12 +139,7 @@ mod array_dimensions { // paren_expr ws* TO ws* paren_expr fn array_dimension_p() -> impl Parser { - opt_second_expression_after_keyword( - expression_pos_p(), - Keyword::To, - ExpressionTrait::is_parenthesis, - ) - .map(|(l, opt_r)| match opt_r { + expr_keyword_opt_expr(Keyword::To).map(|(l, opt_r)| match opt_r { Some(r) => ArrayDimension { lbound: Some(l), ubound: r, @@ -158,12 +155,12 @@ mod array_dimensions { mod type_definition { use rusty_pc::*; - use crate::core::VarNameCtx; + use crate::core::{VarNameCtx, user_defined_type}; use crate::expr::expression_pos_p; use crate::input::StringView; use crate::pc_specific::*; use crate::tokens::star_ws; - use crate::{ParserError, *}; + use crate::{BuiltInStyle, DimType, ExpressionPos, Keyword, ParserError, TypeQualifier}; pub fn extended_type() -> impl Parser { diff --git a/rusty_parser/src/core/select_case.rs b/rusty_parser/src/core/select_case.rs index 266fbb72..59fce85a 100644 --- a/rusty_parser/src/core/select_case.rs +++ b/rusty_parser/src/core/select_case.rs @@ -105,11 +105,11 @@ mod case_expression_parser { use rusty_common::Positioned; use rusty_pc::*; - use crate::expr::{expression_pos_p, opt_second_expression_after_keyword}; + use crate::expr::{expr_keyword_opt_expr, expression_pos_p}; use crate::input::StringView; use crate::pc_specific::*; use crate::tokens::{TokenType, any_token}; - use crate::{CaseExpression, ExpressionTrait, Keyword, Operator, ParserError}; + use crate::{CaseExpression, Keyword, Operator, ParserError}; pub fn parser() -> impl Parser { case_is().or(simple_or_range()) @@ -140,12 +140,7 @@ mod case_expression_parser { } fn simple_or_range() -> impl Parser { - opt_second_expression_after_keyword( - expression_pos_p(), - Keyword::To, - ExpressionTrait::is_parenthesis, - ) - .map(|(left, opt_right)| match opt_right { + expr_keyword_opt_expr(Keyword::To).map(|(left, opt_right)| match opt_right { Some(right) => CaseExpression::Range(left, right), _ => CaseExpression::Simple(left), }) diff --git a/rusty_parser/src/expr/opt_second_expression.rs b/rusty_parser/src/expr/opt_second_expression.rs index eaa146c0..d5c5b83f 100644 --- a/rusty_parser/src/expr/opt_second_expression.rs +++ b/rusty_parser/src/expr/opt_second_expression.rs @@ -1,10 +1,10 @@ use rusty_pc::and::TupleCombiner; -use rusty_pc::{IifParser, Parser, ParserErrorTrait}; +use rusty_pc::{Parser, ParserErrorTrait}; use crate::error::ParserError; use crate::expr::parsers::ws_expr_pos_p; use crate::input::StringView; -use crate::pc_specific::{keyword, whitespace_ignoring}; +use crate::pc_specific::{conditionally_opt_whitespace, keyword}; use crate::{ExpressionPos, Keyword}; /// Parses an optional second expression that follows the first expression @@ -55,24 +55,3 @@ fn ws_keyword(k: Keyword) -> impl Parser fn err(keyword: Keyword) -> ParserError { ParserError::expected(&format!("expression after {}", keyword)).to_fatal() } - -/// Creates a parser that parses whitespace, -/// conditionally allowing it to be missing. -/// When [allow_none] is false, whitespace is mandatory. -/// When [allow_none] is true, the whitespace can be missing. -/// This is typically the case when the previously parsed -/// token was a right side parenthesis. -/// -/// Examples -/// -/// * `(1 + 2)AND` no whitespace is required before `AND` -/// * `1 + 2AND` the lack of whitespace before `AND` is an error -pub(super) fn conditionally_opt_whitespace() --> impl Parser { - IifParser::new( - // allow none - whitespace_ignoring().to_option().map_to_unit(), - // whitespace is required - whitespace_ignoring(), - ) -} diff --git a/rusty_parser/src/expr/parsers.rs b/rusty_parser/src/expr/parsers.rs index fb564738..cebffcfa 100644 --- a/rusty_parser/src/expr/parsers.rs +++ b/rusty_parser/src/expr/parsers.rs @@ -1,7 +1,6 @@ -use rusty_pc::and::{KeepLeftCombiner, VecCombiner}; +use rusty_pc::and::{KeepLeftCombiner, TupleCombiner, VecCombiner}; use rusty_pc::*; -use crate::expr::opt_second_expression::conditionally_opt_whitespace; use crate::input::StringView; use crate::pc_specific::*; use crate::tokens::comma_ws; @@ -136,3 +135,28 @@ pub fn expr_ws_followed_by( KeepLeftCombiner, ) } + +/// Parses an expression, followed optionally by a keyword and a second expression. +/// If the keyword is present, the second expression is mandatory. +/// +/// Examples: `FOR I = 1 TO 100 [STEP 5]`, `CASE 1 [TO 2]` +pub fn expr_keyword_opt_expr( + keyword: Keyword, +) -> impl Parser), Error = ParserError> { + expression_pos_p().then_with_in_context( + opt_keyword_expr(keyword), + ExpressionTrait::is_parenthesis, + TupleCombiner, + ) +} + +/// Parses the optional `TO expr` part (e.g. `CASE 1 TO 2`) +fn opt_keyword_expr( + keyword: Keyword, +) -> impl Parser, Error = ParserError> { + let msg = format!("expression after {}", keyword); + conditionally_opt_whitespace() + .and_keep_right(keyword_ignoring(keyword).no_context()) + .and_keep_right(ws_expr_pos_p().or_expected(&msg).no_context()) + .to_option() +} diff --git a/rusty_parser/src/pc_specific/whitespace.rs b/rusty_parser/src/pc_specific/whitespace.rs index e0918fa4..1c86c5d1 100644 --- a/rusty_parser/src/pc_specific/whitespace.rs +++ b/rusty_parser/src/pc_specific/whitespace.rs @@ -1,4 +1,4 @@ -use rusty_pc::{Parser, SurroundMode, surround}; +use rusty_pc::{IifParser, Parser, SurroundMode, surround}; use crate::ParserError; use crate::input::StringView; @@ -70,3 +70,24 @@ where { demand_ws().no_context().and_keep_right(parser) } + +/// Creates a parser that parses whitespace, +/// conditionally allowing it to be missing. +/// When [allow_none] is false, whitespace is mandatory. +/// When [allow_none] is true, the whitespace can be missing. +/// This is typically the case when the previously parsed +/// token was a right side parenthesis. +/// +/// Examples +/// +/// * `(1 + 2)AND` no whitespace is required before `AND` +/// * `1 + 2AND` the lack of whitespace before `AND` is an error +pub fn conditionally_opt_whitespace() +-> impl Parser { + IifParser::new( + // allow none + opt_ws(), + // whitespace is required + whitespace_ignoring(), + ) +} From a15fd11792e5d0f1c99761a17914c8aa7fb17368 Mon Sep 17 00:00:00 2001 From: Nikolaos Georgiou Date: Sun, 15 Feb 2026 14:15:09 +0100 Subject: [PATCH 12/14] Removed opt_second_expression module --- rusty_parser/src/core/for_loop.rs | 27 ++++++--- rusty_parser/src/expr/mod.rs | 2 - .../src/expr/opt_second_expression.rs | 57 ------------------- 3 files changed, 19 insertions(+), 67 deletions(-) delete mode 100644 rusty_parser/src/expr/opt_second_expression.rs diff --git a/rusty_parser/src/core/for_loop.rs b/rusty_parser/src/core/for_loop.rs index 03c82180..aefa2795 100644 --- a/rusty_parser/src/core/for_loop.rs +++ b/rusty_parser/src/core/for_loop.rs @@ -2,13 +2,11 @@ use rusty_pc::*; use crate::core::statements::zero_or_more_statements; use crate::error::ParserError; -use crate::expr::{ - demand_expr_ws_keyword_p, opt_second_expression_after_keyword, property, ws_expr_pos_p -}; +use crate::expr::{demand_expr_ws_keyword_p, property, ws_expr_pos_p}; use crate::input::StringView; use crate::pc_specific::*; use crate::tokens::equal_sign_ws; -use crate::*; +use crate::{ExpressionPos, ExpressionTrait, ForLoop, Keyword, Statement}; // FOR I = 0 TO 5 STEP 1 // statements @@ -44,10 +42,11 @@ fn parse_for_step_p() -> impl Parser< ), Error = ParserError, > { - opt_second_expression_after_keyword(parse_for_p(), Keyword::Step, |(_var, _low, upper)| { - upper.is_parenthesis() - }) - .map(|((n, l, u), opt_step)| (n, l, u, opt_step)) + parse_for_p().then_with_in_context( + opt_step_p(), + |(_, _, upper)| upper.is_parenthesis(), + |(n, l, u), opt_step| (n, l, u, opt_step), + ) } /// Parses the "FOR I = 1 TO 2" part @@ -68,6 +67,18 @@ fn next_counter_p() -> impl Parser impl Parser, Error = ParserError> +{ + conditionally_opt_whitespace() + .and_keep_right(keyword_ignoring(Keyword::Step).no_context()) + .and_keep_right( + ws_expr_pos_p() + .or_expected("expression after STEP") + .no_context(), + ) + .to_option() +} + #[cfg(test)] mod tests { use rusty_common::*; diff --git a/rusty_parser/src/expr/mod.rs b/rusty_parser/src/expr/mod.rs index 1351a363..a1c665aa 100644 --- a/rusty_parser/src/expr/mod.rs +++ b/rusty_parser/src/expr/mod.rs @@ -3,7 +3,6 @@ mod built_in_function_call; pub mod file_handle; mod function_call_or_array_element; mod integer_or_long_literal; -mod opt_second_expression; mod parenthesis; mod parsers; pub mod property; @@ -13,5 +12,4 @@ pub mod types; mod unary_expression; mod variable; -pub use self::opt_second_expression::opt_second_expression_after_keyword; pub use self::parsers::*; diff --git a/rusty_parser/src/expr/opt_second_expression.rs b/rusty_parser/src/expr/opt_second_expression.rs deleted file mode 100644 index d5c5b83f..00000000 --- a/rusty_parser/src/expr/opt_second_expression.rs +++ /dev/null @@ -1,57 +0,0 @@ -use rusty_pc::and::TupleCombiner; -use rusty_pc::{Parser, ParserErrorTrait}; - -use crate::error::ParserError; -use crate::expr::parsers::ws_expr_pos_p; -use crate::input::StringView; -use crate::pc_specific::{conditionally_opt_whitespace, keyword}; -use crate::{ExpressionPos, Keyword}; - -/// Parses an optional second expression that follows the first expression -/// and a keyword. -/// -/// If the keyword is present, the second expression is mandatory. -/// -/// Example: `FOR I = 1 TO 100 [STEP 5]` -#[deprecated] -pub fn opt_second_expression_after_keyword( - first_parser: P, - keyword: Keyword, - is_first_wrapped_in_parenthesis: F, -) -> impl Parser), Error = ParserError> -where - P: Parser, - F: Fn(&P::Output) -> bool + 'static, -{ - first_parser.then_with_in_context( - parse_second(keyword), - move |first| is_first_wrapped_in_parenthesis(first), - TupleCombiner, - ) -} - -// first_parser AND [ cond_ws(is_first_paren) KEYWORD !AND! ws_expr ] -fn parse_second( - k: Keyword, -) -> impl Parser, Error = ParserError> { - // the left side needs the context - ws_keyword(k) - .and_keep_right( - // but the right side does not need it... - ws_expr_pos_p().no_context().or_fail(err(k)), - ) - // finally to_option needs to send the context down to the "and_keep_right" - .to_option() -} - -fn ws_keyword(k: Keyword) -> impl Parser { - // the left side has the context - conditionally_opt_whitespace().and_tuple( - // but the right side does not - keyword(k).no_context(), - ) -} - -fn err(keyword: Keyword) -> ParserError { - ParserError::expected(&format!("expression after {}", keyword)).to_fatal() -} From 80c54802dd092dc89a5e39cc4221f6497a892f0a Mon Sep 17 00:00:00 2001 From: Nikolaos Georgiou Date: Sun, 15 Feb 2026 14:32:35 +0100 Subject: [PATCH 13/14] Added original QBasic doc for OPEN --- rusty_parser/src/built_ins/open.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/rusty_parser/src/built_ins/open.rs b/rusty_parser/src/built_ins/open.rs index 80096021..d7dc52b9 100644 --- a/rusty_parser/src/built_ins/open.rs +++ b/rusty_parser/src/built_ins/open.rs @@ -7,6 +7,29 @@ use crate::input::StringView; use crate::pc_specific::*; use crate::tokens::equal_sign_ws; use crate::{BuiltInSub, ParserError, *}; + +/// Original doc from QBasic regarding OPEN: +/// +/// Opens a file or device. +/// +/// ```txt +/// OPEN file$ [FOR mode] [ACCESS access] [lock] AS [#]filenumber% [LEN=reclen%] +/// ``` +/// +/// file$: The name of the file or device. The file name may include a drive and path. +/// +/// mode: One of the following file modes: APPEND, BINARY, INPUT, OUTPUT, or RANDOM. +/// +/// access: In network environments, specifies whether the file is +/// opened for READ, WRITE, or READ WRITE access. +/// +/// lock: Specifies the file locking in network environments: +/// SHARED, LOCK READ, LOCK WRITE, LOCK READ WRITE. +/// +/// filenumber%: A number in the range 1 through 255 that identifies the file while it is open. +/// +/// reclen%: For random-access files, the record length (default is 128 bytes). For sequential files, +/// the number of characters buffered (default is 512 bytes). pub fn parse() -> impl Parser { seq6( keyword(Keyword::Open), From 929e9557d7f3d27393be23d2f68a080a67c2560d Mon Sep 17 00:00:00 2001 From: Nikolaos Georgiou Date: Sun, 15 Feb 2026 14:37:43 +0100 Subject: [PATCH 14/14] Leaving OPEN use seq6 for now --- rusty_parser/src/built_ins/open.rs | 2 -- rusty_parser/src/expr/parsers.rs | 16 +++++----------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/rusty_parser/src/built_ins/open.rs b/rusty_parser/src/built_ins/open.rs index d7dc52b9..4e669a49 100644 --- a/rusty_parser/src/built_ins/open.rs +++ b/rusty_parser/src/built_ins/open.rs @@ -34,8 +34,6 @@ pub fn parse() -> impl Parser impl Parser { + lazy(super::binary_expression::parser) +} + /// `( expr [, expr]* )` pub fn in_parenthesis_csv_expressions_non_opt( expectation: &str, @@ -35,10 +40,6 @@ pub fn csv_expressions_first_guarded() ) } -pub fn expression_pos_p() -> impl Parser { - lazy(eager_expression_pos_p) -} - /// Parses an expression that is either preceded by whitespace /// or is a parenthesis expression. /// @@ -61,7 +62,6 @@ pub fn ws_expr_pos_p() -> impl Parser | /// /// ``` -#[deprecated] pub fn ws_expr_pos_ws_p() -> impl Parser { followed_by_ws(ws_expr_pos_p()) } @@ -76,12 +76,6 @@ fn followed_by_ws( ) } -/// Parses an expression -fn eager_expression_pos_p() -> impl Parser -{ - super::binary_expression::parser() -} - /// Parses an expression, /// then demands whitespace, unless the expression is a parenthesis. /// Finally it demands the given keyword.