Enhance custom operators.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
//! Configuration settings for [`Engine`].
|
||||
|
||||
use crate::stdlib::{format, num::NonZeroU8, string::String};
|
||||
use crate::token::{is_valid_identifier, Token};
|
||||
use crate::token::Token;
|
||||
use crate::Engine;
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
@@ -252,17 +252,29 @@ impl Engine {
|
||||
if precedence.is_none() {
|
||||
return Err("precedence cannot be zero".into());
|
||||
}
|
||||
if !is_valid_identifier(keyword.chars()) {
|
||||
return Err(format!("not a valid identifier: '{}'", keyword).into());
|
||||
}
|
||||
|
||||
match Token::lookup_from_syntax(keyword) {
|
||||
// Standard identifiers, reserved keywords and custom keywords are OK
|
||||
None | Some(Token::Reserved(_)) | Some(Token::Custom(_)) => (),
|
||||
// Disabled keywords are also OK
|
||||
Some(token) if !self.disabled_symbols.contains(token.syntax().as_ref()) => (),
|
||||
// Active standard keywords cannot be made custom
|
||||
Some(_) => return Err(format!("'{}' is a reserved keyword", keyword).into()),
|
||||
// Disabled keywords are OK
|
||||
Some(token) if token.is_keyword() => {
|
||||
if !self.disabled_symbols.contains(token.syntax().as_ref()) {
|
||||
return Err(format!("'{}' is a reserved keyword", keyword).into());
|
||||
}
|
||||
}
|
||||
// Active standard operators cannot be made custom
|
||||
Some(token) if token.is_operator() => {
|
||||
if !self.disabled_symbols.contains(token.syntax().as_ref()) {
|
||||
return Err(format!("'{}' is a reserved operator", keyword).into());
|
||||
}
|
||||
}
|
||||
// Active standard symbols cannot be made custom
|
||||
Some(token) if !self.disabled_symbols.contains(token.syntax().as_ref()) => {
|
||||
return Err(format!("'{}' is a reserved symbol", keyword).into())
|
||||
}
|
||||
// Disabled symbols are OK
|
||||
Some(_) => (),
|
||||
}
|
||||
|
||||
// Add to custom keywords
|
||||
|
@@ -1665,15 +1665,24 @@ fn parse_binary_op(
|
||||
|
||||
loop {
|
||||
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));
|
||||
let precedence = match current_op {
|
||||
Token::Custom(c) => {
|
||||
if state
|
||||
.engine
|
||||
.custom_keywords
|
||||
.get(c)
|
||||
.map(Option::is_some)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
state.engine.custom_keywords.get(c).unwrap().unwrap().get()
|
||||
} else {
|
||||
return Err(PERR::Reserved(c.clone()).into_err(*current_pos));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
current_op.precedence()
|
||||
Token::Reserved(c) if !is_valid_identifier(c.chars()) => {
|
||||
return Err(PERR::UnknownOperator(c.into()).into_err(*current_pos))
|
||||
}
|
||||
_ => current_op.precedence(),
|
||||
};
|
||||
let bind_right = current_op.is_bind_right();
|
||||
|
||||
@@ -1698,15 +1707,24 @@ fn parse_binary_op(
|
||||
let rhs = parse_unary(input, state, lib, settings)?;
|
||||
|
||||
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));
|
||||
let next_precedence = match next_op {
|
||||
Token::Custom(c) => {
|
||||
if state
|
||||
.engine
|
||||
.custom_keywords
|
||||
.get(c)
|
||||
.map(Option::is_some)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
state.engine.custom_keywords.get(c).unwrap().unwrap().get()
|
||||
} else {
|
||||
return Err(PERR::Reserved(c.clone()).into_err(*next_pos));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
next_op.precedence()
|
||||
Token::Reserved(c) if !is_valid_identifier(c.chars()) => {
|
||||
return Err(PERR::UnknownOperator(c.into()).into_err(*next_pos))
|
||||
}
|
||||
_ => next_op.precedence(),
|
||||
};
|
||||
|
||||
// Bind to right if the next operator has higher precedence
|
||||
@@ -1809,11 +1827,24 @@ fn parse_binary_op(
|
||||
make_dot_expr(state, current_lhs, rhs, pos)?
|
||||
}
|
||||
|
||||
Token::Custom(s) if state.engine.custom_keywords.contains_key(&s) => {
|
||||
// Accept non-native functions for custom operators
|
||||
Token::Custom(s)
|
||||
if state
|
||||
.engine
|
||||
.custom_keywords
|
||||
.get(&s)
|
||||
.map(Option::is_some)
|
||||
.unwrap_or(false) =>
|
||||
{
|
||||
let hash_script = if is_valid_identifier(s.chars()) {
|
||||
// Accept non-native functions for custom operators
|
||||
calc_script_fn_hash(empty(), &s, 2)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Expr::FnCall(
|
||||
Box::new(FnCallExpr {
|
||||
hash_script: calc_script_fn_hash(empty(), &s, 2),
|
||||
hash_script,
|
||||
args,
|
||||
..op_base
|
||||
}),
|
||||
|
@@ -121,20 +121,26 @@ impl Engine {
|
||||
continue;
|
||||
}
|
||||
|
||||
let token = Token::lookup_from_syntax(s);
|
||||
|
||||
let seg = match s {
|
||||
// Markers not in first position
|
||||
MARKER_IDENT | MARKER_EXPR | MARKER_BLOCK if !segments.is_empty() => s.into(),
|
||||
// Standard or reserved keyword/symbol not in first position
|
||||
s if !segments.is_empty() && Token::lookup_from_syntax(s).is_some() => {
|
||||
// Make it a custom keyword/symbol
|
||||
if !self.custom_keywords.contains_key(s) {
|
||||
s if !segments.is_empty() && token.is_some() => {
|
||||
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||
if (self.disabled_symbols.contains(s)
|
||||
|| matches!(token, Some(Token::Reserved(_))))
|
||||
&& !self.custom_keywords.contains_key(s)
|
||||
{
|
||||
self.custom_keywords.insert(s.into(), None);
|
||||
}
|
||||
s.into()
|
||||
}
|
||||
// Standard keyword in first position
|
||||
s if segments.is_empty()
|
||||
&& Token::lookup_from_syntax(s)
|
||||
&& token
|
||||
.as_ref()
|
||||
.map(|v| v.is_keyword() || v.is_reserved())
|
||||
.unwrap_or(false) =>
|
||||
{
|
||||
@@ -151,7 +157,11 @@ impl Engine {
|
||||
}
|
||||
// Identifier in first position
|
||||
s if segments.is_empty() && is_valid_identifier(s.chars()) => {
|
||||
if !self.custom_keywords.contains_key(s) {
|
||||
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||
if (self.disabled_symbols.contains(s)
|
||||
|| matches!(token, Some(Token::Reserved(_))))
|
||||
&& !self.custom_keywords.contains_key(s)
|
||||
{
|
||||
self.custom_keywords.insert(s.into(), None);
|
||||
}
|
||||
s.into()
|
||||
|
28
src/token.rs
28
src/token.rs
@@ -532,10 +532,10 @@ impl Token {
|
||||
#[cfg(feature = "no_module")]
|
||||
"import" | "export" | "as" => Reserved(syntax.into()),
|
||||
|
||||
"===" | "!==" | "->" | "<-" | ":=" | "::<" | "(*" | "*)" | "#" | "public" | "new"
|
||||
| "use" | "module" | "package" | "var" | "static" | "begin" | "end" | "shared"
|
||||
| "with" | "each" | "then" | "goto" | "unless" | "exit" | "match" | "case"
|
||||
| "default" | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync"
|
||||
"===" | "!==" | "->" | "<-" | ":=" | "**" | "::<" | "(*" | "*)" | "#" | "public"
|
||||
| "new" | "use" | "module" | "package" | "var" | "static" | "begin" | "end"
|
||||
| "shared" | "with" | "each" | "then" | "goto" | "unless" | "exit" | "match"
|
||||
| "case" | "default" | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync"
|
||||
| "async" | "await" | "yield" => Reserved(syntax.into()),
|
||||
|
||||
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
||||
@@ -1742,27 +1742,21 @@ impl<'a> Iterator for TokenIterator<'a, '_> {
|
||||
Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.contains_key(&s) => {
|
||||
Some((Token::Custom(s), pos))
|
||||
}
|
||||
// Custom standard keyword - must be disabled
|
||||
Some((token, pos)) if token.is_keyword() && self.engine.custom_keywords.contains_key(token.syntax().as_ref()) => {
|
||||
// Custom standard keyword/symbol - must be disabled
|
||||
Some((token, pos)) if self.engine.custom_keywords.contains_key(token.syntax().as_ref()) => {
|
||||
if self.engine.disabled_symbols.contains(token.syntax().as_ref()) {
|
||||
// Disabled standard keyword
|
||||
// Disabled standard keyword/symbol
|
||||
Some((Token::Custom(token.syntax().into()), pos))
|
||||
} else {
|
||||
// Active standard keyword - should never be a custom keyword!
|
||||
unreachable!()
|
||||
unreachable!("{:?}", token)
|
||||
}
|
||||
}
|
||||
// Disabled operator
|
||||
Some((token, pos)) if token.is_operator() && self.engine.disabled_symbols.contains(token.syntax().as_ref()) => {
|
||||
Some((
|
||||
Token::LexError(LexError::UnexpectedInput(token.syntax().into())),
|
||||
pos,
|
||||
))
|
||||
}
|
||||
// Disabled standard keyword
|
||||
Some((token, pos)) if token.is_keyword() && self.engine.disabled_symbols.contains(token.syntax().as_ref()) => {
|
||||
// Disabled symbol
|
||||
Some((token, pos)) if self.engine.disabled_symbols.contains(token.syntax().as_ref()) => {
|
||||
Some((Token::Reserved(token.syntax().into()), pos))
|
||||
}
|
||||
// Normal symbol
|
||||
r => r,
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user