1058 lines
37 KiB
Rust
1058 lines
37 KiB
Rust
/*!
|
|
# GRAMMAR
|
|
|
|
<pre class="rust">
|
|
opComp => '=' | '<' | '>' | '<=' } '>=' | '<>'
|
|
opFactor => '*' | '/'
|
|
unaryOp => '-' | '+'
|
|
|
|
expr => concat (opComp concat)*
|
|
concat => term ('&' term)*
|
|
term => factor (opFactor factor)*
|
|
factor => prod (opProd prod)*
|
|
prod => power ('^' power)*
|
|
power => (unaryOp)* range '%'*
|
|
range => implicit (':' primary)?
|
|
implicit=> '@' primary | primary
|
|
primary => '(' expr ')'
|
|
=> number
|
|
=> function '(' f_args ')'
|
|
=> name
|
|
=> string
|
|
=> '{' a_args '}'
|
|
=> bool
|
|
=> bool()
|
|
=> error
|
|
|
|
f_args => e (',' e)*
|
|
</pre>
|
|
*/
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use crate::functions::Function;
|
|
use crate::language::get_language;
|
|
use crate::locale::get_locale;
|
|
use crate::types::Table;
|
|
|
|
use super::lexer;
|
|
use super::token;
|
|
use super::token::OpUnary;
|
|
use super::token::TableReference;
|
|
use super::token::TokenType;
|
|
use super::types::*;
|
|
use super::utils::number_to_column;
|
|
|
|
use token::OpCompare;
|
|
|
|
pub mod move_formula;
|
|
pub mod static_analysis;
|
|
pub mod stringify;
|
|
|
|
#[cfg(test)]
|
|
mod tests;
|
|
|
|
pub(crate) fn parse_range(formula: &str) -> Result<(i32, i32, i32, i32), String> {
|
|
let mut lexer = lexer::Lexer::new(
|
|
formula,
|
|
lexer::LexerMode::A1,
|
|
#[allow(clippy::expect_used)]
|
|
get_locale("en").expect(""),
|
|
#[allow(clippy::expect_used)]
|
|
get_language("en").expect(""),
|
|
);
|
|
if let TokenType::Range {
|
|
left,
|
|
right,
|
|
sheet: _,
|
|
} = lexer.next_token()
|
|
{
|
|
Ok((left.column, left.row, right.column, right.row))
|
|
} else {
|
|
Err("Not a range".to_string())
|
|
}
|
|
}
|
|
|
|
fn get_table_column_by_name(table_column_name: &str, table: &Table) -> Option<i32> {
|
|
for (index, table_column) in table.columns.iter().enumerate() {
|
|
if table_column.name == table_column_name {
|
|
return Some(index as i32);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
// DefinedNameS is a tuple with the name of the defined name, the index of the sheet and the formula
|
|
pub type DefinedNameS = (String, Option<u32>, String);
|
|
|
|
pub(crate) struct Reference<'a> {
|
|
sheet_name: &'a Option<String>,
|
|
sheet_index: u32,
|
|
absolute_row: bool,
|
|
absolute_column: bool,
|
|
row: i32,
|
|
column: i32,
|
|
}
|
|
|
|
#[derive(PartialEq, Clone, Debug)]
|
|
pub enum ArrayNode {
|
|
Boolean(bool),
|
|
Number(f64),
|
|
String(String),
|
|
Error(token::Error),
|
|
}
|
|
|
|
#[derive(PartialEq, Clone, Debug)]
|
|
pub enum Node {
|
|
BooleanKind(bool),
|
|
NumberKind(f64),
|
|
StringKind(String),
|
|
ReferenceKind {
|
|
sheet_name: Option<String>,
|
|
sheet_index: u32,
|
|
absolute_row: bool,
|
|
absolute_column: bool,
|
|
row: i32,
|
|
column: i32,
|
|
},
|
|
RangeKind {
|
|
sheet_name: Option<String>,
|
|
sheet_index: u32,
|
|
absolute_row1: bool,
|
|
absolute_column1: bool,
|
|
row1: i32,
|
|
column1: i32,
|
|
absolute_row2: bool,
|
|
absolute_column2: bool,
|
|
row2: i32,
|
|
column2: i32,
|
|
},
|
|
WrongReferenceKind {
|
|
sheet_name: Option<String>,
|
|
absolute_row: bool,
|
|
absolute_column: bool,
|
|
row: i32,
|
|
column: i32,
|
|
},
|
|
WrongRangeKind {
|
|
sheet_name: Option<String>,
|
|
absolute_row1: bool,
|
|
absolute_column1: bool,
|
|
row1: i32,
|
|
column1: i32,
|
|
absolute_row2: bool,
|
|
absolute_column2: bool,
|
|
row2: i32,
|
|
column2: i32,
|
|
},
|
|
OpRangeKind {
|
|
left: Box<Node>,
|
|
right: Box<Node>,
|
|
},
|
|
OpConcatenateKind {
|
|
left: Box<Node>,
|
|
right: Box<Node>,
|
|
},
|
|
OpSumKind {
|
|
kind: token::OpSum,
|
|
left: Box<Node>,
|
|
right: Box<Node>,
|
|
},
|
|
OpProductKind {
|
|
kind: token::OpProduct,
|
|
left: Box<Node>,
|
|
right: Box<Node>,
|
|
},
|
|
OpPowerKind {
|
|
left: Box<Node>,
|
|
right: Box<Node>,
|
|
},
|
|
FunctionKind {
|
|
kind: Function,
|
|
args: Vec<Node>,
|
|
},
|
|
InvalidFunctionKind {
|
|
name: String,
|
|
args: Vec<Node>,
|
|
},
|
|
ArrayKind(Vec<Vec<ArrayNode>>),
|
|
DefinedNameKind(DefinedNameS),
|
|
TableNameKind(String),
|
|
WrongVariableKind(String),
|
|
ImplicitIntersection {
|
|
automatic: bool,
|
|
child: Box<Node>,
|
|
},
|
|
CompareKind {
|
|
kind: OpCompare,
|
|
left: Box<Node>,
|
|
right: Box<Node>,
|
|
},
|
|
UnaryKind {
|
|
kind: OpUnary,
|
|
right: Box<Node>,
|
|
},
|
|
ErrorKind(token::Error),
|
|
ParseErrorKind {
|
|
formula: String,
|
|
message: String,
|
|
position: usize,
|
|
},
|
|
EmptyArgKind,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct Parser {
|
|
lexer: lexer::Lexer,
|
|
worksheets: Vec<String>,
|
|
defined_names: Vec<DefinedNameS>,
|
|
context: CellReferenceRC,
|
|
tables: HashMap<String, Table>,
|
|
}
|
|
|
|
impl Parser {
|
|
pub fn new(
|
|
worksheets: Vec<String>,
|
|
defined_names: Vec<DefinedNameS>,
|
|
tables: HashMap<String, Table>,
|
|
) -> Parser {
|
|
let lexer = lexer::Lexer::new(
|
|
"",
|
|
lexer::LexerMode::A1,
|
|
#[allow(clippy::expect_used)]
|
|
get_locale("en").expect(""),
|
|
#[allow(clippy::expect_used)]
|
|
get_language("en").expect(""),
|
|
);
|
|
let context = CellReferenceRC {
|
|
sheet: worksheets.first().map_or("", |v| v).to_string(),
|
|
column: 1,
|
|
row: 1,
|
|
};
|
|
Parser {
|
|
lexer,
|
|
worksheets,
|
|
defined_names,
|
|
context,
|
|
tables,
|
|
}
|
|
}
|
|
pub fn set_lexer_mode(&mut self, mode: lexer::LexerMode) {
|
|
self.lexer.set_lexer_mode(mode)
|
|
}
|
|
|
|
pub fn set_worksheets_and_names(
|
|
&mut self,
|
|
worksheets: Vec<String>,
|
|
defined_names: Vec<DefinedNameS>,
|
|
) {
|
|
self.worksheets = worksheets;
|
|
self.defined_names = defined_names;
|
|
}
|
|
|
|
pub fn parse(&mut self, formula: &str, context: &CellReferenceRC) -> Node {
|
|
self.lexer.set_formula(formula);
|
|
self.context = context.clone();
|
|
self.parse_expr()
|
|
}
|
|
|
|
fn get_sheet_index_by_name(&self, name: &str) -> Option<u32> {
|
|
let worksheets = &self.worksheets;
|
|
for (i, sheet) in worksheets.iter().enumerate() {
|
|
if sheet == name {
|
|
return Some(i as u32);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
// Returns:
|
|
// * None: If there is no defined name by that name
|
|
// * Some((Some(index), formula)): If there is a defined name local to that sheet
|
|
// * Some(None): If there is a global defined name
|
|
fn get_defined_name(&self, name: &str, sheet: u32) -> Option<(Option<u32>, String)> {
|
|
for (df_name, df_scope, df_formula) in &self.defined_names {
|
|
if name.to_lowercase() == df_name.to_lowercase() && df_scope == &Some(sheet) {
|
|
return Some((*df_scope, df_formula.to_owned()));
|
|
}
|
|
}
|
|
for (df_name, df_scope, df_formula) in &self.defined_names {
|
|
if name.to_lowercase() == df_name.to_lowercase() && df_scope.is_none() {
|
|
return Some((None, df_formula.to_owned()));
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
fn parse_expr(&mut self) -> Node {
|
|
let mut t = self.parse_concat();
|
|
if let Node::ParseErrorKind { .. } = t {
|
|
return t;
|
|
}
|
|
let mut next_token = self.lexer.peek_token();
|
|
while let TokenType::Compare(op) = next_token {
|
|
self.lexer.advance_token();
|
|
let p = self.parse_concat();
|
|
if let Node::ParseErrorKind { .. } = p {
|
|
return p;
|
|
}
|
|
t = Node::CompareKind {
|
|
kind: op,
|
|
left: Box::new(t),
|
|
right: Box::new(p),
|
|
};
|
|
next_token = self.lexer.peek_token();
|
|
}
|
|
t
|
|
}
|
|
|
|
fn parse_concat(&mut self) -> Node {
|
|
let mut t = self.parse_term();
|
|
if let Node::ParseErrorKind { .. } = t {
|
|
return t;
|
|
}
|
|
let mut next_token = self.lexer.peek_token();
|
|
while next_token == TokenType::And {
|
|
self.lexer.advance_token();
|
|
let p = self.parse_term();
|
|
if let Node::ParseErrorKind { .. } = p {
|
|
return p;
|
|
}
|
|
t = Node::OpConcatenateKind {
|
|
left: Box::new(t),
|
|
right: Box::new(p),
|
|
};
|
|
next_token = self.lexer.peek_token();
|
|
}
|
|
t
|
|
}
|
|
|
|
fn parse_term(&mut self) -> Node {
|
|
let mut t = self.parse_factor();
|
|
if let Node::ParseErrorKind { .. } = t {
|
|
return t;
|
|
}
|
|
let mut next_token = self.lexer.peek_token();
|
|
while let TokenType::Addition(op) = next_token {
|
|
self.lexer.advance_token();
|
|
let p = self.parse_factor();
|
|
if let Node::ParseErrorKind { .. } = p {
|
|
return p;
|
|
}
|
|
t = Node::OpSumKind {
|
|
kind: op,
|
|
left: Box::new(t),
|
|
right: Box::new(p),
|
|
};
|
|
|
|
next_token = self.lexer.peek_token();
|
|
}
|
|
t
|
|
}
|
|
|
|
fn parse_factor(&mut self) -> Node {
|
|
let mut t = self.parse_prod();
|
|
if let Node::ParseErrorKind { .. } = t {
|
|
return t;
|
|
}
|
|
let mut next_token = self.lexer.peek_token();
|
|
while let TokenType::Product(op) = next_token {
|
|
self.lexer.advance_token();
|
|
let p = self.parse_prod();
|
|
if let Node::ParseErrorKind { .. } = p {
|
|
return p;
|
|
}
|
|
t = Node::OpProductKind {
|
|
kind: op,
|
|
left: Box::new(t),
|
|
right: Box::new(p),
|
|
};
|
|
next_token = self.lexer.peek_token();
|
|
}
|
|
t
|
|
}
|
|
|
|
fn parse_prod(&mut self) -> Node {
|
|
let mut t = self.parse_power();
|
|
if let Node::ParseErrorKind { .. } = t {
|
|
return t;
|
|
}
|
|
let mut next_token = self.lexer.peek_token();
|
|
while next_token == TokenType::Power {
|
|
self.lexer.advance_token();
|
|
let p = self.parse_power();
|
|
if let Node::ParseErrorKind { .. } = p {
|
|
return p;
|
|
}
|
|
t = Node::OpPowerKind {
|
|
left: Box::new(t),
|
|
right: Box::new(p),
|
|
};
|
|
next_token = self.lexer.peek_token();
|
|
}
|
|
t
|
|
}
|
|
|
|
fn parse_power(&mut self) -> Node {
|
|
let mut next_token = self.lexer.peek_token();
|
|
let mut sign = 1;
|
|
while let TokenType::Addition(op) = next_token {
|
|
self.lexer.advance_token();
|
|
if op == token::OpSum::Minus {
|
|
sign = -sign;
|
|
}
|
|
next_token = self.lexer.peek_token();
|
|
}
|
|
|
|
let mut t = self.parse_range();
|
|
if let Node::ParseErrorKind { .. } = t {
|
|
return t;
|
|
}
|
|
if sign == -1 {
|
|
t = Node::UnaryKind {
|
|
kind: token::OpUnary::Minus,
|
|
right: Box::new(t),
|
|
}
|
|
}
|
|
next_token = self.lexer.peek_token();
|
|
while next_token == TokenType::Percent {
|
|
self.lexer.advance_token();
|
|
t = Node::UnaryKind {
|
|
kind: token::OpUnary::Percentage,
|
|
right: Box::new(t),
|
|
};
|
|
next_token = self.lexer.peek_token();
|
|
}
|
|
t
|
|
}
|
|
|
|
fn parse_range(&mut self) -> Node {
|
|
let t = self.parse_implicit();
|
|
if let Node::ParseErrorKind { .. } = t {
|
|
return t;
|
|
}
|
|
let next_token = self.lexer.peek_token();
|
|
if next_token == TokenType::Colon {
|
|
self.lexer.advance_token();
|
|
let p = self.parse_primary();
|
|
if let Node::ParseErrorKind { .. } = p {
|
|
return p;
|
|
}
|
|
return Node::OpRangeKind {
|
|
left: Box::new(t),
|
|
right: Box::new(p),
|
|
};
|
|
}
|
|
t
|
|
}
|
|
|
|
fn parse_implicit(&mut self) -> Node {
|
|
let next_token = self.lexer.peek_token();
|
|
if next_token == TokenType::At {
|
|
self.lexer.advance_token();
|
|
let t = self.parse_primary();
|
|
if let Node::ParseErrorKind { .. } = t {
|
|
return t;
|
|
}
|
|
return Node::ImplicitIntersection {
|
|
automatic: false,
|
|
child: Box::new(t),
|
|
};
|
|
}
|
|
self.parse_primary()
|
|
}
|
|
|
|
fn parse_array_row(&mut self) -> Result<Vec<ArrayNode>, Node> {
|
|
let mut row = Vec::new();
|
|
// and array can only have numbers, string or booleans
|
|
// otherwise it is a syntax error
|
|
let first_element = match self.parse_expr() {
|
|
Node::BooleanKind(s) => ArrayNode::Boolean(s),
|
|
Node::NumberKind(s) => ArrayNode::Number(s),
|
|
Node::StringKind(s) => ArrayNode::String(s),
|
|
Node::ErrorKind(kind) => ArrayNode::Error(kind),
|
|
error @ Node::ParseErrorKind { .. } => return Err(error),
|
|
_ => {
|
|
return Err(Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
message: "Invalid value in array".to_string(),
|
|
position: self.lexer.get_position() as usize,
|
|
});
|
|
}
|
|
};
|
|
row.push(first_element);
|
|
let mut next_token = self.lexer.peek_token();
|
|
// FIXME: this is not respecting the locale
|
|
while next_token == TokenType::Comma {
|
|
self.lexer.advance_token();
|
|
let value = match self.parse_expr() {
|
|
Node::BooleanKind(s) => ArrayNode::Boolean(s),
|
|
Node::NumberKind(s) => ArrayNode::Number(s),
|
|
Node::StringKind(s) => ArrayNode::String(s),
|
|
Node::ErrorKind(kind) => ArrayNode::Error(kind),
|
|
error @ Node::ParseErrorKind { .. } => return Err(error),
|
|
_ => {
|
|
return Err(Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
message: "Invalid value in array".to_string(),
|
|
position: self.lexer.get_position() as usize,
|
|
});
|
|
}
|
|
};
|
|
row.push(value);
|
|
next_token = self.lexer.peek_token();
|
|
}
|
|
Ok(row)
|
|
}
|
|
|
|
fn parse_primary(&mut self) -> Node {
|
|
let next_token = self.lexer.next_token();
|
|
match next_token {
|
|
TokenType::LeftParenthesis => {
|
|
let t = self.parse_expr();
|
|
if let Node::ParseErrorKind { .. } = t {
|
|
return t;
|
|
}
|
|
|
|
if let Err(err) = self.lexer.expect(TokenType::RightParenthesis) {
|
|
return Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: err.position,
|
|
message: err.message,
|
|
};
|
|
}
|
|
t
|
|
}
|
|
TokenType::Number(s) => Node::NumberKind(s),
|
|
TokenType::String(s) => Node::StringKind(s),
|
|
TokenType::LeftBrace => {
|
|
// It's an array. It's a collection of rows all of the same dimension
|
|
|
|
let first_row = match self.parse_array_row() {
|
|
Ok(s) => s,
|
|
Err(error) => return error,
|
|
};
|
|
let length = first_row.len();
|
|
|
|
let mut matrix = Vec::new();
|
|
matrix.push(first_row);
|
|
// FIXME: this is not respecting the locale
|
|
let mut next_token = self.lexer.peek_token();
|
|
while next_token == TokenType::Semicolon {
|
|
self.lexer.advance_token();
|
|
let row = match self.parse_array_row() {
|
|
Ok(s) => s,
|
|
Err(error) => return error,
|
|
};
|
|
next_token = self.lexer.peek_token();
|
|
if row.len() != length {
|
|
return Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: self.lexer.get_position() as usize,
|
|
message: "All rows in an array should be the same length".to_string(),
|
|
};
|
|
}
|
|
matrix.push(row);
|
|
}
|
|
|
|
if let Err(err) = self.lexer.expect(TokenType::RightBrace) {
|
|
return Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: err.position,
|
|
message: err.message,
|
|
};
|
|
}
|
|
Node::ArrayKind(matrix)
|
|
}
|
|
TokenType::Reference {
|
|
sheet,
|
|
row,
|
|
column,
|
|
absolute_column,
|
|
absolute_row,
|
|
} => {
|
|
let context = &self.context;
|
|
let sheet_index = match &sheet {
|
|
Some(name) => self.get_sheet_index_by_name(name),
|
|
None => self.get_sheet_index_by_name(&context.sheet),
|
|
};
|
|
let a1_mode = self.lexer.is_a1_mode();
|
|
let row = if absolute_row || !a1_mode {
|
|
row
|
|
} else {
|
|
row - context.row
|
|
};
|
|
let column = if absolute_column || !a1_mode {
|
|
column
|
|
} else {
|
|
column - context.column
|
|
};
|
|
match sheet_index {
|
|
Some(index) => Node::ReferenceKind {
|
|
sheet_name: sheet,
|
|
sheet_index: index,
|
|
row,
|
|
column,
|
|
absolute_row,
|
|
absolute_column,
|
|
},
|
|
None => Node::WrongReferenceKind {
|
|
sheet_name: sheet,
|
|
row,
|
|
column,
|
|
absolute_row,
|
|
absolute_column,
|
|
},
|
|
}
|
|
}
|
|
TokenType::Range { sheet, left, right } => {
|
|
let context = &self.context;
|
|
let sheet_index = match &sheet {
|
|
Some(name) => self.get_sheet_index_by_name(name),
|
|
None => self.get_sheet_index_by_name(&context.sheet),
|
|
};
|
|
let mut row1 = left.row;
|
|
let mut column1 = left.column;
|
|
let mut row2 = right.row;
|
|
let mut column2 = right.column;
|
|
|
|
let mut absolute_column1 = left.absolute_column;
|
|
let mut absolute_column2 = right.absolute_column;
|
|
let mut absolute_row1 = left.absolute_row;
|
|
let mut absolute_row2 = right.absolute_row;
|
|
|
|
if row1 > row2 {
|
|
(row2, row1) = (row1, row2);
|
|
(absolute_row2, absolute_row1) = (absolute_row1, absolute_row2);
|
|
}
|
|
if column1 > column2 {
|
|
(column2, column1) = (column1, column2);
|
|
(absolute_column2, absolute_column1) = (absolute_column1, absolute_column2);
|
|
}
|
|
|
|
if self.lexer.is_a1_mode() {
|
|
if !absolute_row1 {
|
|
row1 -= context.row
|
|
};
|
|
if !absolute_column1 {
|
|
column1 -= context.column
|
|
};
|
|
if !absolute_row2 {
|
|
row2 -= context.row
|
|
};
|
|
if !absolute_column2 {
|
|
column2 -= context.column
|
|
};
|
|
}
|
|
|
|
match sheet_index {
|
|
Some(index) => Node::RangeKind {
|
|
sheet_name: sheet,
|
|
sheet_index: index,
|
|
row1,
|
|
column1,
|
|
row2,
|
|
column2,
|
|
absolute_column1,
|
|
absolute_column2,
|
|
absolute_row1,
|
|
absolute_row2,
|
|
},
|
|
None => Node::WrongRangeKind {
|
|
sheet_name: sheet,
|
|
row1,
|
|
column1,
|
|
row2,
|
|
column2,
|
|
absolute_column1,
|
|
absolute_column2,
|
|
absolute_row1,
|
|
absolute_row2,
|
|
},
|
|
}
|
|
}
|
|
TokenType::Ident(name) => {
|
|
let next_token = self.lexer.peek_token();
|
|
if next_token == TokenType::LeftParenthesis {
|
|
// It's a function call "SUM(.."
|
|
self.lexer.advance_token();
|
|
let args = match self.parse_function_args() {
|
|
Ok(s) => s,
|
|
Err(e) => return e,
|
|
};
|
|
if let Err(err) = self.lexer.expect(TokenType::RightParenthesis) {
|
|
return Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: err.position,
|
|
message: err.message,
|
|
};
|
|
}
|
|
if let Some(function_kind) = Function::get_function(&name) {
|
|
return Node::FunctionKind {
|
|
kind: function_kind,
|
|
args,
|
|
};
|
|
}
|
|
if &name == "_xlfn.SINGLE" {
|
|
if args.len() != 1 {
|
|
return Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: self.lexer.get_position() as usize,
|
|
message: "Implicit Intersection requires just one argument"
|
|
.to_string(),
|
|
};
|
|
}
|
|
return Node::ImplicitIntersection {
|
|
automatic: false,
|
|
child: Box::new(args[0].clone()),
|
|
};
|
|
}
|
|
return Node::InvalidFunctionKind { name, args };
|
|
}
|
|
let context = &self.context;
|
|
|
|
let context_sheet_index = match self.get_sheet_index_by_name(&context.sheet) {
|
|
Some(i) => i,
|
|
None => {
|
|
return Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: 0,
|
|
message: format!("sheet not found: {}", context.sheet),
|
|
};
|
|
}
|
|
};
|
|
|
|
// Could be a defined name or a table
|
|
if let Some((scope, formula)) = self.get_defined_name(&name, context_sheet_index) {
|
|
return Node::DefinedNameKind((name, scope, formula));
|
|
}
|
|
let name_lower = name.to_lowercase();
|
|
for table_name in self.tables.keys() {
|
|
if table_name.to_lowercase() == name_lower {
|
|
return Node::TableNameKind(name);
|
|
}
|
|
}
|
|
Node::WrongVariableKind(name)
|
|
}
|
|
TokenType::Error(kind) => Node::ErrorKind(kind),
|
|
TokenType::Illegal(error) => Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: error.position,
|
|
message: error.message,
|
|
},
|
|
TokenType::EOF => Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: 0,
|
|
message: "Unexpected end of input.".to_string(),
|
|
},
|
|
TokenType::Boolean(value) => {
|
|
// Could be a function call "TRUE()"
|
|
let next_token = self.lexer.peek_token();
|
|
if next_token == TokenType::LeftParenthesis {
|
|
self.lexer.advance_token();
|
|
// We parse all the arguments, although technically this is moot
|
|
// But is has the upside of transforming `=TRUE( 4 )` into `=TRUE(4)`
|
|
let args = match self.parse_function_args() {
|
|
Ok(s) => s,
|
|
Err(e) => return e,
|
|
};
|
|
if let Err(err) = self.lexer.expect(TokenType::RightParenthesis) {
|
|
return Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: err.position,
|
|
message: err.message,
|
|
};
|
|
}
|
|
if value {
|
|
return Node::FunctionKind {
|
|
kind: Function::True,
|
|
args,
|
|
};
|
|
} else {
|
|
return Node::FunctionKind {
|
|
kind: Function::False,
|
|
args,
|
|
};
|
|
}
|
|
}
|
|
Node::BooleanKind(value)
|
|
}
|
|
TokenType::Compare(_) => {
|
|
// A primary Node cannot start with an operator
|
|
Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: 0,
|
|
message: "Unexpected token: 'COMPARE'".to_string(),
|
|
}
|
|
}
|
|
TokenType::Addition(_) => {
|
|
// A primary Node cannot start with an operator
|
|
Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: 0,
|
|
message: "Unexpected token: 'SUM'".to_string(),
|
|
}
|
|
}
|
|
TokenType::Product(_) => {
|
|
// A primary Node cannot start with an operator
|
|
Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: 0,
|
|
message: "Unexpected token: 'PRODUCT'".to_string(),
|
|
}
|
|
}
|
|
TokenType::Power => {
|
|
// A primary Node cannot start with an operator
|
|
Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: 0,
|
|
message: "Unexpected token: 'POWER'".to_string(),
|
|
}
|
|
}
|
|
TokenType::At => {
|
|
// A primary Node cannot start with an operator
|
|
Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: 0,
|
|
message: "Unexpected token: '@'".to_string(),
|
|
}
|
|
}
|
|
TokenType::RightParenthesis
|
|
| TokenType::RightBracket
|
|
| TokenType::Colon
|
|
| TokenType::Semicolon
|
|
| TokenType::RightBrace
|
|
| TokenType::Comma
|
|
| TokenType::Bang
|
|
| TokenType::And
|
|
| TokenType::Percent => Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: 0,
|
|
message: format!("Unexpected token: '{next_token:?}'"),
|
|
},
|
|
TokenType::LeftBracket => Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: 0,
|
|
message: "Unexpected token: '['".to_string(),
|
|
},
|
|
TokenType::StructuredReference {
|
|
table_name,
|
|
specifier,
|
|
table_reference,
|
|
} => {
|
|
// We will try to convert to a normal reference
|
|
// table_name[column_name] => cell1:cell2
|
|
// table_name[[#This Row], [column_name]:[column_name]] => cell1:cell2
|
|
let context = &self.context;
|
|
let context_sheet_index = match self.get_sheet_index_by_name(&context.sheet) {
|
|
Some(i) => i,
|
|
None => {
|
|
return Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: 0,
|
|
message: format!("sheet not found: {}", context.sheet),
|
|
};
|
|
}
|
|
};
|
|
// table-name => table
|
|
let table = match self.tables.get(&table_name) {
|
|
Some(t) => t,
|
|
None => {
|
|
let message = format!(
|
|
"Table not found: '{table_name}' at '{}!{}{}'",
|
|
context.sheet,
|
|
number_to_column(context.column)
|
|
.unwrap_or(format!("{}", context.column)),
|
|
context.row
|
|
);
|
|
return Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: 0,
|
|
message,
|
|
};
|
|
}
|
|
};
|
|
let table_sheet_index = match self.get_sheet_index_by_name(&table.sheet_name) {
|
|
Some(i) => i,
|
|
None => {
|
|
return Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: 0,
|
|
message: format!("table sheet not found: {}", table.sheet_name),
|
|
};
|
|
}
|
|
};
|
|
|
|
let sheet_name = if table_sheet_index == context_sheet_index {
|
|
None
|
|
} else {
|
|
Some(table.sheet_name.clone())
|
|
};
|
|
|
|
// context must be with tables.reference
|
|
#[allow(clippy::expect_used)]
|
|
let (column_start, mut row_start, column_end, mut row_end) =
|
|
parse_range(&table.reference).expect("Failed parsing range");
|
|
|
|
let totals_row_count = table.totals_row_count as i32;
|
|
let header_row_count = table.header_row_count as i32;
|
|
row_end -= totals_row_count;
|
|
|
|
match specifier {
|
|
Some(token::TableSpecifier::ThisRow) => {
|
|
row_start = context.row;
|
|
row_end = context.row;
|
|
}
|
|
Some(token::TableSpecifier::Totals) => {
|
|
if totals_row_count != 0 {
|
|
row_start = row_end + 1;
|
|
row_end = row_start;
|
|
} else {
|
|
// Table1[#Totals] is #REF! if Table1 does not have totals
|
|
return Node::ErrorKind(token::Error::REF);
|
|
}
|
|
}
|
|
Some(token::TableSpecifier::Headers) => {
|
|
row_end = row_start;
|
|
}
|
|
Some(token::TableSpecifier::Data) => {
|
|
row_start += header_row_count;
|
|
}
|
|
Some(token::TableSpecifier::All) => {
|
|
if totals_row_count != 0 {
|
|
row_end += 1;
|
|
}
|
|
}
|
|
None => {
|
|
// skip the headers
|
|
row_start += header_row_count;
|
|
}
|
|
}
|
|
match table_reference {
|
|
None => Node::RangeKind {
|
|
sheet_name,
|
|
sheet_index: table_sheet_index,
|
|
absolute_row1: true,
|
|
absolute_column1: true,
|
|
row1: row_start,
|
|
column1: column_start,
|
|
absolute_row2: true,
|
|
absolute_column2: true,
|
|
row2: row_end,
|
|
column2: column_end,
|
|
},
|
|
Some(TableReference::ColumnReference(s)) => {
|
|
let column_index = match get_table_column_by_name(&s, table) {
|
|
Some(s) => s + column_start,
|
|
None => {
|
|
return Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: self.lexer.get_position() as usize,
|
|
message: format!("Expecting column: {s} in table {table_name}"),
|
|
};
|
|
}
|
|
};
|
|
if row_start == row_end {
|
|
return Node::ReferenceKind {
|
|
sheet_name,
|
|
sheet_index: table_sheet_index,
|
|
absolute_row: true,
|
|
absolute_column: true,
|
|
row: row_start,
|
|
column: column_index,
|
|
};
|
|
}
|
|
Node::RangeKind {
|
|
sheet_name,
|
|
sheet_index: table_sheet_index,
|
|
absolute_row1: true,
|
|
absolute_column1: true,
|
|
row1: row_start,
|
|
column1: column_index,
|
|
absolute_row2: true,
|
|
absolute_column2: true,
|
|
row2: row_end,
|
|
column2: column_index,
|
|
}
|
|
}
|
|
Some(TableReference::RangeReference((left, right))) => {
|
|
let left_column_index = match get_table_column_by_name(&left, table) {
|
|
Some(f) => f + column_start,
|
|
None => {
|
|
return Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: self.lexer.get_position() as usize,
|
|
message: format!(
|
|
"Expecting column: {left} in table {table_name}"
|
|
),
|
|
};
|
|
}
|
|
};
|
|
|
|
let right_column_index = match get_table_column_by_name(&right, table) {
|
|
Some(f) => f + column_start,
|
|
None => {
|
|
return Node::ParseErrorKind {
|
|
formula: self.lexer.get_formula(),
|
|
position: self.lexer.get_position() as usize,
|
|
message: format!(
|
|
"Expecting column: {right} in table {table_name}"
|
|
),
|
|
};
|
|
}
|
|
};
|
|
Node::RangeKind {
|
|
sheet_name,
|
|
sheet_index: table_sheet_index,
|
|
absolute_row1: true,
|
|
absolute_column1: true,
|
|
row1: row_start,
|
|
column1: left_column_index,
|
|
absolute_row2: true,
|
|
absolute_column2: true,
|
|
row2: row_end,
|
|
column2: right_column_index,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn parse_function_args(&mut self) -> Result<Vec<Node>, Node> {
|
|
let mut args: Vec<Node> = Vec::new();
|
|
let mut next_token = self.lexer.peek_token();
|
|
if next_token == TokenType::RightParenthesis {
|
|
return Ok(args);
|
|
}
|
|
if self.lexer.peek_token() == TokenType::Comma {
|
|
args.push(Node::EmptyArgKind);
|
|
} else {
|
|
let t = self.parse_expr();
|
|
if let Node::ParseErrorKind { .. } = t {
|
|
return Err(t);
|
|
}
|
|
args.push(t);
|
|
}
|
|
next_token = self.lexer.peek_token();
|
|
while next_token == TokenType::Comma {
|
|
self.lexer.advance_token();
|
|
if self.lexer.peek_token() == TokenType::Comma {
|
|
args.push(Node::EmptyArgKind);
|
|
next_token = TokenType::Comma;
|
|
} else if self.lexer.peek_token() == TokenType::RightParenthesis {
|
|
args.push(Node::EmptyArgKind);
|
|
return Ok(args);
|
|
} else {
|
|
let p = self.parse_expr();
|
|
if let Node::ParseErrorKind { .. } = p {
|
|
return Err(p);
|
|
}
|
|
next_token = self.lexer.peek_token();
|
|
args.push(p);
|
|
}
|
|
}
|
|
Ok(args)
|
|
}
|
|
}
|