Add raw API for custom syntax.

This commit is contained in:
Stephen Chung
2020-10-25 21:57:18 +08:00
parent f670d55871
commit b607a3a9ba
11 changed files with 298 additions and 176 deletions

View File

@@ -2549,9 +2549,17 @@ fn parse_binary_op(
let mut root = lhs;
loop {
let (current_op, _) = input.peek().unwrap();
let custom = state.engine.custom_keywords.as_ref();
let precedence = current_op.precedence(custom);
let (current_op, current_pos) = input.peek().unwrap();
let precedence = if let Token::Custom(c) = current_op {
// Custom operators
if let Some(Some(p)) = state.engine.custom_keywords.get(c) {
*p
} else {
return Err(PERR::Reserved(c.clone()).into_err(*current_pos));
}
} else {
current_op.precedence()
};
let bind_right = current_op.is_bind_right();
// Bind left to the parent lhs expression if precedence is higher
@@ -2574,7 +2582,17 @@ fn parse_binary_op(
let rhs = parse_unary(input, state, lib, settings)?;
let next_precedence = input.peek().unwrap().0.precedence(custom);
let (next_op, next_pos) = input.peek().unwrap();
let next_precedence = if let Token::Custom(c) = next_op {
// Custom operators
if let Some(Some(p)) = state.engine.custom_keywords.get(c) {
*p
} else {
return Err(PERR::Reserved(c.clone()).into_err(*next_pos));
}
} else {
next_op.precedence()
};
// Bind to right if the next operator has higher precedence
// If same precedence, then check if the operator binds right
@@ -2646,14 +2664,7 @@ fn parse_binary_op(
make_dot_expr(current_lhs, rhs, pos)?
}
Token::Custom(s)
if state
.engine
.custom_keywords
.as_ref()
.map(|c| c.contains_key(&s))
.unwrap_or(false) =>
{
Token::Custom(s) if state.engine.custom_keywords.contains_key(&s) => {
// Accept non-native functions for custom operators
let op = (op.0, false, op.2, op.3);
Expr::FnCall(Box::new((op, None, hash, args, None)))
@@ -2665,12 +2676,12 @@ fn parse_binary_op(
}
/// Parse a custom syntax.
fn parse_custom(
fn parse_custom_syntax(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
mut settings: ParseSettings,
key: &str,
key: String,
syntax: &CustomSyntax,
pos: Position,
) -> Result<Expr, ParseError> {
@@ -2691,13 +2702,26 @@ fn parse_custom(
_ => (),
}
for segment in syntax.segments.iter() {
let mut segments: StaticVec<_> = Default::default();
segments.push(key);
loop {
settings.pos = input.peek().unwrap().1;
let token = if let Some(seg) = (syntax.parse)(&segments.iter().collect::<StaticVec<_>>())
.map_err(|err| err.0.into_err(settings.pos))?
{
seg
} else {
break;
};
let settings = settings.level_up();
match segment.as_str() {
match token.as_str() {
MARKER_IDENT => match input.next().unwrap() {
(Token::Identifier(s), pos) => {
segments.push(s.to_string());
exprs.push(Expr::Variable(Box::new(((s, pos), None, 0, None))));
}
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
@@ -2705,20 +2729,25 @@ fn parse_custom(
}
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
},
MARKER_EXPR => exprs.push(parse_expr(input, state, lib, settings)?),
MARKER_EXPR => {
exprs.push(parse_expr(input, state, lib, settings)?);
segments.push(MARKER_EXPR.to_string());
}
MARKER_BLOCK => {
let stmt = parse_block(input, state, lib, settings)?;
let pos = stmt.position();
exprs.push(Expr::Stmt(Box::new((stmt, pos))))
exprs.push(Expr::Stmt(Box::new((stmt, pos))));
segments.push(MARKER_BLOCK.to_string());
}
s => match input.peek().unwrap() {
(Token::LexError(err), pos) => return Err(err.into_err(*pos)),
(t, _) if t.syntax().as_ref() == s => {
input.next().unwrap();
segments.push(input.next().unwrap().0.syntax().into_owned());
}
(_, pos) => {
return Err(PERR::MissingToken(
s.to_string(),
format!("for '{}' expression", key),
format!("for '{}' expression", segments[0]),
)
.into_err(*pos))
}
@@ -2745,16 +2774,22 @@ fn parse_expr(
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
// Check if it is a custom syntax.
if let Some(ref custom) = state.engine.custom_syntax {
if !state.engine.custom_syntax.is_empty() {
let (token, pos) = input.peek().unwrap();
let token_pos = *pos;
match token {
Token::Custom(key) if custom.contains_key(key) => {
let custom = custom.get_key_value(key).unwrap();
let (key, syntax) = custom;
input.next().unwrap();
return parse_custom(input, state, lib, settings, key, syntax, token_pos);
Token::Custom(key) | Token::Reserved(key) | Token::Identifier(key) => {
match state.engine.custom_syntax.get_key_value(key) {
Some((key, syntax)) => {
let key = key.to_string();
input.next().unwrap();
return parse_custom_syntax(
input, state, lib, settings, key, syntax, token_pos,
);
}
_ => (),
}
}
_ => (),
}