in rhai/src/parser.rs [2542:2741]
fn parse_custom_syntax(
&self,
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FnLib,
settings: ParseSettings,
key: impl Into<ImmutableString>,
syntax: &crate::api::custom_syntax::CustomSyntax,
pos: Position,
) -> ParseResult<Expr> {
#[allow(clippy::wildcard_imports)]
use crate::api::custom_syntax::markers::*;
const KEYWORD_SEMICOLON: &str = Token::SemiColon.literal_syntax();
const KEYWORD_CLOSE_BRACE: &str = Token::RightBrace.literal_syntax();
let mut settings = settings;
let mut inputs = StaticVec::new_const();
let mut segments = StaticVec::new_const();
let mut tokens = StaticVec::new_const();
// Adjust the variables stack
if syntax.scope_may_be_changed {
// Add a barrier variable to the stack so earlier variables will not be matched.
// Variable searches stop at the first barrier.
let marker = state.get_interned_string(SCOPE_SEARCH_BARRIER_MARKER);
state
.stack
.get_or_insert_with(Default::default)
.push(marker, ());
}
let mut user_state = Dynamic::UNIT;
let parse_func = &*syntax.parse;
let mut required_token: ImmutableString = key.into();
tokens.push(required_token.clone());
segments.push(required_token.clone());
loop {
let (fwd_token, fwd_pos) = input.peek().expect(NEVER_ENDS);
settings.pos = *fwd_pos;
let settings = settings.level_up()?;
required_token = match parse_func(&segments, &fwd_token.to_string(), &mut user_state) {
Ok(Some(seg))
if seg.starts_with(CUSTOM_SYNTAX_MARKER_SYNTAX_VARIANT)
&& seg.len() > CUSTOM_SYNTAX_MARKER_SYNTAX_VARIANT.len() =>
{
inputs.push(Expr::StringConstant(state.get_interned_string(seg), pos));
break;
}
Ok(Some(seg)) => seg,
Ok(None) => break,
Err(err) => return Err(err.0.into_err(settings.pos)),
};
match required_token.as_str() {
CUSTOM_SYNTAX_MARKER_IDENT => {
let (name, pos) = parse_var_name(input)?;
let name = state.get_interned_string(name);
let ns = Namespace::NONE;
segments.push(name.clone());
tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_IDENT));
inputs.push(Expr::Variable((None, ns, 0, name).into(), None, pos));
}
CUSTOM_SYNTAX_MARKER_SYMBOL => {
let (symbol, pos) = match input.next().expect(NEVER_ENDS) {
// Standard symbol
(token, pos) if token.is_standard_symbol() => {
Ok((token.literal_syntax().into(), pos))
}
// Reserved symbol
(Token::Reserved(s), pos) if !is_valid_identifier(s.as_str()) => {
Ok((*s, pos))
}
// Bad symbol
(Token::LexError(err), pos) => Err(err.into_err(pos)),
// Not a symbol
(.., pos) => Err(PERR::MissingSymbol(String::new()).into_err(pos)),
}?;
let symbol = state.get_interned_string(symbol);
segments.push(symbol.clone());
tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_SYMBOL));
inputs.push(Expr::StringConstant(symbol, pos));
}
CUSTOM_SYNTAX_MARKER_EXPR => {
inputs.push(self.parse_expr(input, state, lib, settings)?);
let keyword = state.get_interned_string(CUSTOM_SYNTAX_MARKER_EXPR);
segments.push(keyword.clone());
tokens.push(keyword);
}
CUSTOM_SYNTAX_MARKER_BLOCK => {
match self.parse_block(input, state, lib, settings)? {
block @ Stmt::Block(..) => {
inputs.push(Expr::Stmt(Box::new(block.into())));
let keyword = state.get_interned_string(CUSTOM_SYNTAX_MARKER_BLOCK);
segments.push(keyword.clone());
tokens.push(keyword);
}
stmt => unreachable!("Stmt::Block expected but gets {:?}", stmt),
}
}
CUSTOM_SYNTAX_MARKER_BOOL => match input.next().expect(NEVER_ENDS) {
(b @ (Token::True | Token::False), pos) => {
inputs.push(Expr::BoolConstant(b == Token::True, pos));
segments.push(state.get_interned_string(b.literal_syntax()));
tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_BOOL));
}
(.., pos) => {
return Err(
PERR::MissingSymbol("Expecting 'true' or 'false'".into()).into_err(pos)
)
}
},
CUSTOM_SYNTAX_MARKER_INT => match input.next().expect(NEVER_ENDS) {
(Token::IntegerConstant(i), pos) => {
inputs.push(Expr::IntegerConstant(i, pos));
segments.push(i.to_string().into());
tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_INT));
}
(.., pos) => {
return Err(
PERR::MissingSymbol("Expecting an integer number".into()).into_err(pos)
)
}
},
#[cfg(not(feature = "no_float"))]
CUSTOM_SYNTAX_MARKER_FLOAT => match input.next().expect(NEVER_ENDS) {
(Token::FloatConstant(f), pos) => {
inputs.push(Expr::FloatConstant(f, pos));
segments.push(f.to_string().into());
tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_FLOAT));
}
(.., pos) => {
return Err(
PERR::MissingSymbol("Expecting a floating-point number".into())
.into_err(pos),
)
}
},
CUSTOM_SYNTAX_MARKER_STRING => match input.next().expect(NEVER_ENDS) {
(Token::StringConstant(s), pos) => {
let s = state.get_interned_string(*s);
inputs.push(Expr::StringConstant(s.clone(), pos));
segments.push(s);
tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_STRING));
}
(.., pos) => {
return Err(PERR::MissingSymbol("Expecting a string".into()).into_err(pos))
}
},
s => match input.next().expect(NEVER_ENDS) {
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
(Token::Identifier(t) | Token::Reserved(t) | Token::Custom(t), ..)
if *t == s =>
{
segments.push(required_token.clone());
tokens.push(required_token.clone());
}
(t, ..) if t.is_literal() && t.literal_syntax() == s => {
segments.push(required_token.clone());
tokens.push(required_token.clone());
}
(.., pos) => {
return Err(PERR::MissingToken(
s.into(),
format!("for '{}' expression", segments[0]),
)
.into_err(pos))
}
},
}
}
inputs.shrink_to_fit();
tokens.shrink_to_fit();
let self_terminated = matches!(
required_token.as_str(),
// It is self-terminating if the last symbol is a block
CUSTOM_SYNTAX_MARKER_BLOCK |
// If the last symbol is `;` or `}`, it is self-terminating
KEYWORD_SEMICOLON | KEYWORD_CLOSE_BRACE
);
Ok(Expr::Custom(
crate::ast::CustomExpr {
inputs,
tokens,
state: user_state,
scope_may_be_changed: syntax.scope_may_be_changed,
self_terminated,
}
.into(),
pos,
))
}