add statements and bug fix for arrays
This commit is contained in:
@@ -375,6 +375,7 @@ where
|
|||||||
Value::Array(_, x) => Ok(x.last().ok_or(RuntimeError::EmptyArray)?.clone()),
|
Value::Array(_, x) => Ok(x.last().ok_or(RuntimeError::EmptyArray)?.clone()),
|
||||||
t => Err(RuntimeError::NoOverloadForTypes("fini".into(), vec![t]))
|
t => Err(RuntimeError::NoOverloadForTypes("fini".into(), vec![t]))
|
||||||
},
|
},
|
||||||
|
ParseTree::Nop => Ok(Value::Nil),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,11 +41,11 @@ impl Function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> Option<&str> {
|
pub(crate) fn name(&self) -> Option<&str> {
|
||||||
self.name.as_ref().map(|x| x.as_str())
|
self.name.as_ref().map(|x| x.as_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_type(&self) -> FunctionType {
|
pub(crate) fn get_type(&self) -> FunctionType {
|
||||||
self.t.clone()
|
self.t.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
314
src/parser.rs
314
src/parser.rs
@@ -89,6 +89,7 @@ pub(crate) enum ParseTree {
|
|||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
Print(Box<ParseTree>),
|
Print(Box<ParseTree>),
|
||||||
|
Nop,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses input tokens and produces ParseTrees for an Executor
|
/// Parses input tokens and produces ParseTrees for an Executor
|
||||||
@@ -133,148 +134,178 @@ impl<'a, I: Iterator<Item = Result<Token, TokenizeError>>> Parser<'a, I> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse(&mut self) -> Result<ParseTree, ParseError> {
|
fn parse(&mut self) -> Result<ParseTree, ParseError> {
|
||||||
match self.tokens.next() {
|
match self.tokens.next().ok_or(ParseError::NoInput)?.map_err(|e| ParseError::TokenizeError(e))? {
|
||||||
Some(Ok(token)) => {
|
Token::Constant(c) => Ok(ParseTree::Constant(c)),
|
||||||
match token {
|
Token::Identifier(ident) => {
|
||||||
Token::Constant(c) => Ok(ParseTree::Constant(c)),
|
match self.get_object_type(&ident)? {
|
||||||
Token::Identifier(ident) => {
|
Type::Function(f) => {
|
||||||
match self.get_object_type(&ident)? {
|
let args = f.1.clone().iter()
|
||||||
Type::Function(f) => {
|
.map(|_| self.parse()).collect::<Result<Vec<_>, ParseError>>()?;
|
||||||
let args = f.1.clone().iter()
|
|
||||||
.map(|_| self.parse()).collect::<Result<Vec<_>, ParseError>>()?;
|
Ok(ParseTree::FunctionCall(ident, args))
|
||||||
|
|
||||||
Ok(ParseTree::FunctionCall(ident, args))
|
|
||||||
}
|
|
||||||
_ => Ok(ParseTree::Variable(ident)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Token::Operator(op) => {
|
_ => Ok(ParseTree::Variable(ident)),
|
||||||
match op {
|
|
||||||
Op::Add => Ok(ParseTree::Add(Box::new(self.parse()?), Box::new(self.parse()?))),
|
|
||||||
Op::Sub => Ok(ParseTree::Sub(Box::new(self.parse()?), Box::new(self.parse()?))),
|
|
||||||
Op::Mul => Ok(ParseTree::Mul(Box::new(self.parse()?), Box::new(self.parse()?))),
|
|
||||||
Op::Div => Ok(ParseTree::Div(Box::new(self.parse()?), Box::new(self.parse()?))),
|
|
||||||
Op::Exp => Ok(ParseTree::Exp(Box::new(self.parse()?), Box::new(self.parse()?))),
|
|
||||||
Op::Mod => Ok(ParseTree::Mod(Box::new(self.parse()?), Box::new(self.parse()?))),
|
|
||||||
Op::Equ | Op::LazyEqu => {
|
|
||||||
let token = self.tokens.next()
|
|
||||||
.ok_or(ParseError::UnexpectedEndInput)?
|
|
||||||
.map_err(|e| ParseError::TokenizeError(e))?;
|
|
||||||
|
|
||||||
let body = Box::new(self.parse()?);
|
|
||||||
|
|
||||||
if let Token::Identifier(ident) = token {
|
|
||||||
match op {
|
|
||||||
Op::Equ => Ok(ParseTree::Equ(ident.clone(),
|
|
||||||
body.clone(),
|
|
||||||
Box::new(Parser::new(self.tokens.by_ref())
|
|
||||||
.locals(self.locals.clone())
|
|
||||||
.globals(self.globals.clone())
|
|
||||||
.add_local(ident, Type::Any)
|
|
||||||
.parse()?))
|
|
||||||
),
|
|
||||||
Op::LazyEqu => Ok(ParseTree::LazyEqu(ident.clone(),
|
|
||||||
body.clone(),
|
|
||||||
Box::new(Parser::new(self.tokens.by_ref())
|
|
||||||
.locals(self.locals.clone())
|
|
||||||
.globals(self.globals.clone())
|
|
||||||
.add_local(ident, Type::Any)
|
|
||||||
.parse()?))
|
|
||||||
),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(ParseError::InvalidIdentifier(token))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Op::FunctionDefine(arg_count) => {
|
|
||||||
let f = self.parse_function(arg_count)?;
|
|
||||||
|
|
||||||
Ok(ParseTree::FunctionDefinition(f.clone(),
|
|
||||||
Box::new(
|
|
||||||
Parser::new(self.tokens)
|
|
||||||
.globals(self.globals.clone())
|
|
||||||
.locals(self.locals.clone())
|
|
||||||
.add_local(f.name().unwrap().to_string(), Type::Function(f.get_type()))
|
|
||||||
.parse()?
|
|
||||||
)))
|
|
||||||
},
|
|
||||||
Op::Compose => Ok(ParseTree::Compose(Box::new(self.parse()?), Box::new(self.parse()?))),
|
|
||||||
Op::Id => Ok(ParseTree::Id(Box::new(self.parse()?))),
|
|
||||||
Op::IfElse => Ok(ParseTree::IfElse(Box::new(self.parse()?), Box::new(self.parse()?), Box::new(self.parse()?))),
|
|
||||||
Op::If => Ok(ParseTree::If(Box::new(self.parse()?), Box::new(self.parse()?))),
|
|
||||||
Op::EqualTo => Ok(ParseTree::EqualTo(Box::new(self.parse()?), Box::new(self.parse()?))),
|
|
||||||
Op::GreaterThan => Ok(ParseTree::GreaterThan(Box::new(self.parse()?), Box::new(self.parse()?))),
|
|
||||||
Op::LessThan => Ok(ParseTree::LessThan(Box::new(self.parse()?), Box::new(self.parse()?))),
|
|
||||||
Op::GreaterThanOrEqualTo => Ok(ParseTree::GreaterThanOrEqualTo(Box::new(self.parse()?), Box::new(self.parse()?))),
|
|
||||||
Op::LessThanOrEqualTo => Ok(ParseTree::LessThanOrEqualTo(Box::new(self.parse()?), Box::new(self.parse()?))),
|
|
||||||
Op::Not => Ok(ParseTree::Not(Box::new(self.parse()?))),
|
|
||||||
Op::IntCast => Ok(ParseTree::IntCast(Box::new(self.parse()?))),
|
|
||||||
Op::FloatCast => Ok(ParseTree::FloatCast(Box::new(self.parse()?))),
|
|
||||||
Op::BoolCast => Ok(ParseTree::BoolCast(Box::new(self.parse()?))),
|
|
||||||
Op::StringCast => Ok(ParseTree::StringCast(Box::new(self.parse()?))),
|
|
||||||
Op::Print => Ok(ParseTree::Print(Box::new(self.parse()?))),
|
|
||||||
Op::OpenArray => {
|
|
||||||
let mut depth = 1;
|
|
||||||
|
|
||||||
// take tokens until we reach the end of this array
|
|
||||||
// if we don't collect them here it causes rust to overflow computing the types
|
|
||||||
let array_tokens = self.tokens.by_ref().take_while(|t| match t {
|
|
||||||
Ok(Token::Operator(Op::OpenArray)) => {
|
|
||||||
depth += 1;
|
|
||||||
true
|
|
||||||
},
|
|
||||||
Ok(Token::Operator(Op::CloseArray)) => {
|
|
||||||
depth -= 1;
|
|
||||||
depth > 0
|
|
||||||
}
|
|
||||||
_ => true,
|
|
||||||
}).collect::<Result<Vec<_>, TokenizeError>>().map_err(|e| ParseError::TokenizeError(e))?;
|
|
||||||
|
|
||||||
let mut array_tokens = array_tokens
|
|
||||||
.into_iter()
|
|
||||||
.map(|t| Ok(t))
|
|
||||||
.collect::<Vec<Result<Token, TokenizeError>>>()
|
|
||||||
.into_iter()
|
|
||||||
.peekable();
|
|
||||||
|
|
||||||
let trees: Vec<ParseTree> = Parser::new(&mut array_tokens)
|
|
||||||
.globals(self.globals.to_owned())
|
|
||||||
.locals(self.locals.to_owned())
|
|
||||||
.collect::<Result<_, ParseError>>()?;
|
|
||||||
|
|
||||||
let tree = trees.into_iter().fold(
|
|
||||||
ParseTree::Constant(Value::Array(Type::Any, vec![])),
|
|
||||||
|acc, x| ParseTree::Add(Box::new(acc), Box::new(x.clone())),
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(tree)
|
|
||||||
}
|
|
||||||
Op::Empty => Ok(ParseTree::Constant(Value::Array(Type::Any, vec![]))),
|
|
||||||
Op::CloseArray => Err(ParseError::UnmatchedArrayClose),
|
|
||||||
Op::NotEqualTo => Ok(ParseTree::NotEqualTo(Box::new(self.parse()?), Box::new(self.parse()?))),
|
|
||||||
Op::And => Ok(ParseTree::And(Box::new(self.parse()?), Box::new(self.parse()?))),
|
|
||||||
Op::Or => Ok(ParseTree::Or(Box::new(self.parse()?), Box::new(self.parse()?))),
|
|
||||||
Op::LambdaDefine(arg_count) => {
|
|
||||||
let f = self.parse_lambda(arg_count)?;
|
|
||||||
Ok(ParseTree::LambdaDefinition(f))
|
|
||||||
}
|
|
||||||
Op::NonCall => {
|
|
||||||
let name = Self::get_identifier(self.tokens.next())?;
|
|
||||||
Ok(ParseTree::NonCall(name))
|
|
||||||
},
|
|
||||||
Op::Head => Ok(ParseTree::Head(Box::new(self.parse()?))),
|
|
||||||
Op::Tail => Ok(ParseTree::Tail(Box::new(self.parse()?))),
|
|
||||||
Op::Init => Ok(ParseTree::Init(Box::new(self.parse()?))),
|
|
||||||
Op::Fini => Ok(ParseTree::Fini(Box::new(self.parse()?))),
|
|
||||||
op => Err(ParseError::UnwantedToken(Token::Operator(op))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t => Err(ParseError::UnwantedToken(t)),
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Some(Err(e)) => Err(ParseError::TokenizeError(e)),
|
Token::Operator(op) => {
|
||||||
None => Err(ParseError::NoInput),
|
match op {
|
||||||
|
Op::Add => Ok(ParseTree::Add(Box::new(self.parse()?), Box::new(self.parse()?))),
|
||||||
|
Op::Sub => Ok(ParseTree::Sub(Box::new(self.parse()?), Box::new(self.parse()?))),
|
||||||
|
Op::Mul => Ok(ParseTree::Mul(Box::new(self.parse()?), Box::new(self.parse()?))),
|
||||||
|
Op::Div => Ok(ParseTree::Div(Box::new(self.parse()?), Box::new(self.parse()?))),
|
||||||
|
Op::Exp => Ok(ParseTree::Exp(Box::new(self.parse()?), Box::new(self.parse()?))),
|
||||||
|
Op::Mod => Ok(ParseTree::Mod(Box::new(self.parse()?), Box::new(self.parse()?))),
|
||||||
|
Op::Equ | Op::LazyEqu => {
|
||||||
|
let token = self.tokens.next()
|
||||||
|
.ok_or(ParseError::UnexpectedEndInput)?
|
||||||
|
.map_err(|e| ParseError::TokenizeError(e))?;
|
||||||
|
|
||||||
|
let body = Box::new(self.parse()?);
|
||||||
|
|
||||||
|
if let Token::Identifier(ident) = token {
|
||||||
|
match op {
|
||||||
|
Op::Equ => Ok(ParseTree::Equ(ident.clone(),
|
||||||
|
body.clone(),
|
||||||
|
Box::new(Parser::new(self.tokens.by_ref())
|
||||||
|
.locals(self.locals.clone())
|
||||||
|
.globals(self.globals.clone())
|
||||||
|
.add_local(ident, Type::Any)
|
||||||
|
.parse()?))
|
||||||
|
),
|
||||||
|
Op::LazyEqu => Ok(ParseTree::LazyEqu(ident.clone(),
|
||||||
|
body.clone(),
|
||||||
|
Box::new(Parser::new(self.tokens.by_ref())
|
||||||
|
.locals(self.locals.clone())
|
||||||
|
.globals(self.globals.clone())
|
||||||
|
.add_local(ident, Type::Any)
|
||||||
|
.parse()?))
|
||||||
|
),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(ParseError::InvalidIdentifier(token))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Op::FunctionDefine(arg_count) => {
|
||||||
|
let f = self.parse_function(arg_count)?;
|
||||||
|
|
||||||
|
Ok(ParseTree::FunctionDefinition(f.clone(),
|
||||||
|
Box::new(
|
||||||
|
Parser::new(self.tokens)
|
||||||
|
.globals(self.globals.clone())
|
||||||
|
.locals(self.locals.clone())
|
||||||
|
.add_local(f.name().unwrap().to_string(), Type::Function(f.get_type()))
|
||||||
|
.parse()?
|
||||||
|
)))
|
||||||
|
},
|
||||||
|
Op::Compose => Ok(ParseTree::Compose(Box::new(self.parse()?), Box::new(self.parse()?))),
|
||||||
|
Op::Id => Ok(ParseTree::Id(Box::new(self.parse()?))),
|
||||||
|
Op::IfElse => Ok(ParseTree::IfElse(Box::new(self.parse()?), Box::new(self.parse()?), Box::new(self.parse()?))),
|
||||||
|
Op::If => Ok(ParseTree::If(Box::new(self.parse()?), Box::new(self.parse()?))),
|
||||||
|
Op::EqualTo => Ok(ParseTree::EqualTo(Box::new(self.parse()?), Box::new(self.parse()?))),
|
||||||
|
Op::GreaterThan => Ok(ParseTree::GreaterThan(Box::new(self.parse()?), Box::new(self.parse()?))),
|
||||||
|
Op::LessThan => Ok(ParseTree::LessThan(Box::new(self.parse()?), Box::new(self.parse()?))),
|
||||||
|
Op::GreaterThanOrEqualTo => Ok(ParseTree::GreaterThanOrEqualTo(Box::new(self.parse()?), Box::new(self.parse()?))),
|
||||||
|
Op::LessThanOrEqualTo => Ok(ParseTree::LessThanOrEqualTo(Box::new(self.parse()?), Box::new(self.parse()?))),
|
||||||
|
Op::Not => Ok(ParseTree::Not(Box::new(self.parse()?))),
|
||||||
|
Op::IntCast => Ok(ParseTree::IntCast(Box::new(self.parse()?))),
|
||||||
|
Op::FloatCast => Ok(ParseTree::FloatCast(Box::new(self.parse()?))),
|
||||||
|
Op::BoolCast => Ok(ParseTree::BoolCast(Box::new(self.parse()?))),
|
||||||
|
Op::StringCast => Ok(ParseTree::StringCast(Box::new(self.parse()?))),
|
||||||
|
Op::Print => Ok(ParseTree::Print(Box::new(self.parse()?))),
|
||||||
|
Op::OpenArray => {
|
||||||
|
let mut depth = 1;
|
||||||
|
|
||||||
|
// take tokens until we reach the end of this array
|
||||||
|
// if we don't collect them here it causes rust to overflow computing the types
|
||||||
|
let array_tokens = self.tokens.by_ref().take_while(|t| match t {
|
||||||
|
Ok(Token::Operator(Op::OpenArray)) => {
|
||||||
|
depth += 1;
|
||||||
|
true
|
||||||
|
},
|
||||||
|
Ok(Token::Operator(Op::CloseArray)) => {
|
||||||
|
depth -= 1;
|
||||||
|
depth > 0
|
||||||
|
}
|
||||||
|
_ => true,
|
||||||
|
}).collect::<Result<Vec<_>, TokenizeError>>().map_err(|e| ParseError::TokenizeError(e))?;
|
||||||
|
|
||||||
|
let mut array_tokens = array_tokens
|
||||||
|
.into_iter()
|
||||||
|
.map(|t| Ok(t))
|
||||||
|
.collect::<Vec<Result<Token, TokenizeError>>>()
|
||||||
|
.into_iter()
|
||||||
|
.peekable();
|
||||||
|
|
||||||
|
let trees: Vec<ParseTree> = Parser::new(&mut array_tokens)
|
||||||
|
.globals(self.globals.to_owned())
|
||||||
|
.locals(self.locals.to_owned())
|
||||||
|
.collect::<Result<_, ParseError>>()?;
|
||||||
|
|
||||||
|
let tree = trees.into_iter().fold(
|
||||||
|
ParseTree::Constant(Value::Array(Type::Any, vec![])),
|
||||||
|
|acc, x| ParseTree::Add(Box::new(acc), Box::new(x.clone())),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(tree)
|
||||||
|
}
|
||||||
|
Op::OpenStatement => {
|
||||||
|
let mut depth = 1;
|
||||||
|
|
||||||
|
// take tokens until we reach the end of this array
|
||||||
|
// if we don't collect them here it causes rust to overflow computing the types
|
||||||
|
let tokens = self.tokens.by_ref().take_while(|t| match t {
|
||||||
|
Ok(Token::Operator(Op::OpenStatement)) => {
|
||||||
|
depth += 1;
|
||||||
|
true
|
||||||
|
},
|
||||||
|
Ok(Token::Operator(Op::CloseStatement)) => {
|
||||||
|
depth -= 1;
|
||||||
|
depth > 0
|
||||||
|
}
|
||||||
|
_ => true,
|
||||||
|
}).collect::<Result<Vec<_>, TokenizeError>>().map_err(|e| ParseError::TokenizeError(e))?;
|
||||||
|
|
||||||
|
let mut tokens = tokens
|
||||||
|
.into_iter()
|
||||||
|
.map(|t| Ok(t))
|
||||||
|
.collect::<Vec<Result<Token, TokenizeError>>>()
|
||||||
|
.into_iter()
|
||||||
|
.peekable();
|
||||||
|
|
||||||
|
let trees: Vec<ParseTree> = Parser::new(&mut tokens)
|
||||||
|
.globals(self.globals.to_owned())
|
||||||
|
.locals(self.locals.to_owned())
|
||||||
|
.collect::<Result<_, ParseError>>()?;
|
||||||
|
|
||||||
|
let tree = trees.into_iter().fold(
|
||||||
|
ParseTree::Nop,
|
||||||
|
|acc, x| ParseTree::Compose(Box::new(acc), Box::new(x.clone())),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(tree)
|
||||||
|
}
|
||||||
|
Op::Empty => Ok(ParseTree::Constant(Value::Array(Type::Any, vec![]))),
|
||||||
|
Op::CloseArray => Err(ParseError::UnmatchedArrayClose),
|
||||||
|
Op::NotEqualTo => Ok(ParseTree::NotEqualTo(Box::new(self.parse()?), Box::new(self.parse()?))),
|
||||||
|
Op::And => Ok(ParseTree::And(Box::new(self.parse()?), Box::new(self.parse()?))),
|
||||||
|
Op::Or => Ok(ParseTree::Or(Box::new(self.parse()?), Box::new(self.parse()?))),
|
||||||
|
Op::LambdaDefine(arg_count) => {
|
||||||
|
let f = self.parse_lambda(arg_count)?;
|
||||||
|
Ok(ParseTree::LambdaDefinition(f))
|
||||||
|
}
|
||||||
|
Op::NonCall => {
|
||||||
|
let name = Self::get_identifier(self.tokens.next())?;
|
||||||
|
Ok(ParseTree::NonCall(name))
|
||||||
|
},
|
||||||
|
Op::Head => Ok(ParseTree::Head(Box::new(self.parse()?))),
|
||||||
|
Op::Tail => Ok(ParseTree::Tail(Box::new(self.parse()?))),
|
||||||
|
Op::Init => Ok(ParseTree::Init(Box::new(self.parse()?))),
|
||||||
|
Op::Fini => Ok(ParseTree::Fini(Box::new(self.parse()?))),
|
||||||
|
op => Err(ParseError::UnwantedToken(Token::Operator(op))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t => Err(ParseError::UnwantedToken(t)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,10 +347,9 @@ impl<'a, I: Iterator<Item = Result<Token, TokenizeError>>> Parser<'a, I> {
|
|||||||
.map(|_| Self::parse_function_declaration_parameter(tokens))
|
.map(|_| Self::parse_function_declaration_parameter(tokens))
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
|
|
||||||
let (types, names): (Vec<_>, Vec<_>) = args.into_iter().unzip();
|
let (types, names): (Vec<_>, Vec<_>) = args.into_iter().unzip();
|
||||||
let mut ret = Type::Any;
|
let mut ret = Type::Any;
|
||||||
|
|
||||||
if tokens.next_if(|x| matches!(x, Ok(Token::Operator(Op::Arrow)))).is_some() {
|
if tokens.next_if(|x| matches!(x, Ok(Token::Operator(Op::Arrow)))).is_some() {
|
||||||
ret = Self::parse_type(tokens)?;
|
ret = Self::parse_type(tokens)?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,8 @@ pub enum Op {
|
|||||||
Print,
|
Print,
|
||||||
OpenArray,
|
OpenArray,
|
||||||
CloseArray,
|
CloseArray,
|
||||||
|
OpenStatement,
|
||||||
|
CloseStatement,
|
||||||
Empty,
|
Empty,
|
||||||
And,
|
And,
|
||||||
Or,
|
Or,
|
||||||
@@ -84,7 +86,7 @@ pub enum Op {
|
|||||||
Fini,
|
Fini,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
Identifier(String),
|
Identifier(String),
|
||||||
Operator(Op),
|
Operator(Op),
|
||||||
@@ -196,6 +198,8 @@ impl<R: BufRead> Tokenizer<R> {
|
|||||||
("!=", Op::NotEqualTo),
|
("!=", Op::NotEqualTo),
|
||||||
("[", Op::OpenArray),
|
("[", Op::OpenArray),
|
||||||
("]", Op::CloseArray),
|
("]", Op::CloseArray),
|
||||||
|
("(", Op::OpenStatement),
|
||||||
|
(")", Op::CloseStatement),
|
||||||
("!", Op::Not),
|
("!", Op::Not),
|
||||||
("&&", Op::And),
|
("&&", Op::And),
|
||||||
("||", Op::Or),
|
("||", Op::Or),
|
||||||
|
|||||||
Reference in New Issue
Block a user