diff --git a/base/src/actions.rs b/base/src/actions.rs index 5d416ec..cbcb816 100644 --- a/base/src/actions.rs +++ b/base/src/actions.rs @@ -1,5 +1,6 @@ use crate::constants::{LAST_COLUMN, LAST_ROW}; -use crate::expressions::parser::stringify::DisplaceData; +use crate::expressions::parser::stringify::{to_string, to_string_displaced, DisplaceData}; +use crate::expressions::types::CellReferenceRC; use crate::model::Model; // NOTE: There is a difference with Excel behaviour when deleting cells/rows/columns @@ -8,16 +9,45 @@ use crate::model::Model; // I feel this is unimportant for now. impl Model { + fn shift_cell_formula( + &mut self, + sheet: u32, + row: i32, + column: i32, + displace_data: &DisplaceData, + ) -> Result<(), String> { + if let Some(f) = self + .workbook + .worksheet(sheet)? + .cell(row, column) + .and_then(|c| c.get_formula()) + { + let node = &self.parsed_formulas[sheet as usize][f as usize].clone(); + let cell_reference = CellReferenceRC { + sheet: self.workbook.worksheets[sheet as usize].get_name(), + row, + column, + }; + // FIXME: This is not a very performant way if the formula has changed :S. + let formula = to_string(node, &cell_reference); + let formula_displaced = to_string_displaced(node, &cell_reference, displace_data); + if formula != formula_displaced { + self.update_cell_with_formula(sheet, row, column, format!("={formula_displaced}"))?; + } + } + Ok(()) + } /// This function iterates over all cells in the model and shifts their formulas according to the displacement data. /// /// # Arguments /// /// * `displace_data` - A reference to `DisplaceData` describing the displacement's direction and magnitude. - fn displace_cells(&mut self, displace_data: &DisplaceData) { + fn displace_cells(&mut self, displace_data: &DisplaceData) -> Result<(), String> { let cells = self.get_all_cells(); for cell in cells { - self.shift_cell_formula(cell.index, cell.row, cell.column, displace_data); + self.shift_cell_formula(cell.index, cell.row, cell.column, displace_data)?; } + Ok(()) } /// Retrieves the column indices for a specific row in a given sheet, sorted in ascending or descending order. @@ -134,7 +164,7 @@ impl Model { column, delta: column_count, }), - ); + )?; // In the list of columns: // * Keep all the columns to the left @@ -214,7 +244,7 @@ impl Model { column, delta: -column_count, }), - ); + )?; let worksheet = &mut self.workbook.worksheet_mut(sheet)?; // deletes all the column styles @@ -338,7 +368,7 @@ impl Model { row, delta: row_count, }), - ); + )?; Ok(()) } @@ -399,7 +429,7 @@ impl Model { row, delta: -row_count, }), - ); + )?; Ok(()) } @@ -420,14 +450,14 @@ impl Model { sheet: u32, column: i32, delta: i32, - ) -> Result<(), &'static str> { + ) -> Result<(), String> { // Check boundaries let target_column = column + delta; if !(1..=LAST_COLUMN).contains(&target_column) { - return Err("Target column out of boundaries"); + return Err("Target column out of boundaries".to_string()); } if !(1..=LAST_COLUMN).contains(&column) { - return Err("Initial column out of boundaries"); + return Err("Initial column out of boundaries".to_string()); } // TODO: Add the actual displacement of data and styles @@ -439,7 +469,7 @@ impl Model { column, delta, }), - ); + )?; Ok(()) } diff --git a/base/src/cast.rs b/base/src/cast.rs index 9d0e2e4..afc6338 100644 --- a/base/src/cast.rs +++ b/base/src/cast.rs @@ -1,7 +1,6 @@ use crate::{ calc_result::{CalcResult, Range}, expressions::{parser::Node, token::Error, types::CellReferenceIndex}, - implicit_intersection::implicit_intersection, model::Model, }; @@ -39,19 +38,11 @@ impl Model { } CalcResult::EmptyCell | CalcResult::EmptyArg => Ok(0.0), error @ CalcResult::Error { .. } => Err(error), - CalcResult::Range { left, right } => { - match implicit_intersection(&cell, &Range { left, right }) { - Some(cell_reference) => { - let result = self.evaluate_cell(cell_reference); - self.cast_to_number(result, cell_reference) - } - None => Err(CalcResult::Error { - error: Error::VALUE, - origin: cell, - message: "Invalid reference (number)".to_string(), - }), - } - } + CalcResult::Range { .. } => Err(CalcResult::Error { + error: Error::NIMPL, + origin: cell, + message: "Arrays not supported yet".to_string(), + }), } } @@ -99,19 +90,11 @@ impl Model { } CalcResult::EmptyCell | CalcResult::EmptyArg => Ok("".to_string()), error @ CalcResult::Error { .. } => Err(error), - CalcResult::Range { left, right } => { - match implicit_intersection(&cell, &Range { left, right }) { - Some(cell_reference) => { - let result = self.evaluate_cell(cell_reference); - self.cast_to_string(result, cell_reference) - } - None => Err(CalcResult::Error { - error: Error::VALUE, - origin: cell, - message: "Invalid reference (string)".to_string(), - }), - } - } + CalcResult::Range { .. } => Err(CalcResult::Error { + error: Error::NIMPL, + origin: cell, + message: "Arrays not supported yet".to_string(), + }), } } @@ -151,19 +134,11 @@ impl Model { CalcResult::Boolean(b) => Ok(b), CalcResult::EmptyCell | CalcResult::EmptyArg => Ok(false), error @ CalcResult::Error { .. } => Err(error), - CalcResult::Range { left, right } => { - match implicit_intersection(&cell, &Range { left, right }) { - Some(cell_reference) => { - let result = self.evaluate_cell(cell_reference); - self.cast_to_bool(result, cell_reference) - } - None => Err(CalcResult::Error { - error: Error::VALUE, - origin: cell, - message: "Invalid reference (bool)".to_string(), - }), - } - } + CalcResult::Range { .. } => Err(CalcResult::Error { + error: Error::NIMPL, + origin: cell, + message: "Arrays not supported yet".to_string(), + }), } } diff --git a/base/src/diffs.rs b/base/src/diffs.rs deleted file mode 100644 index e02c115..0000000 --- a/base/src/diffs.rs +++ /dev/null @@ -1,138 +0,0 @@ -use crate::{ - expressions::{ - parser::{ - move_formula::ref_is_in_area, - stringify::{to_string, to_string_displaced, DisplaceData}, - walk::forward_references, - }, - types::{Area, CellReferenceIndex, CellReferenceRC}, - }, - model::Model, -}; -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -#[serde(untagged, deny_unknown_fields)] -pub enum CellValue { - Value(String), - None, -} - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -pub struct SetCellValue { - cell: CellReferenceIndex, - new_value: CellValue, - old_value: CellValue, -} - -impl Model { - #[allow(clippy::expect_used)] - pub(crate) fn shift_cell_formula( - &mut self, - sheet: u32, - row: i32, - column: i32, - displace_data: &DisplaceData, - ) { - if let Some(f) = self - .workbook - .worksheet(sheet) - .expect("Worksheet must exist") - .cell(row, column) - .expect("Cell must exist") - .get_formula() - { - let node = &self.parsed_formulas[sheet as usize][f as usize].clone(); - let cell_reference = CellReferenceRC { - sheet: self.workbook.worksheets[sheet as usize].get_name(), - row, - column, - }; - // FIXME: This is not a very performant way if the formula has changed :S. - let formula = to_string(node, &cell_reference); - let formula_displaced = to_string_displaced(node, &cell_reference, displace_data); - if formula != formula_displaced { - self.update_cell_with_formula(sheet, row, column, format!("={formula_displaced}")) - .expect("Failed to shift cell formula"); - } - } - } - - #[allow(clippy::expect_used)] - pub fn forward_references( - &mut self, - source_area: &Area, - target: &CellReferenceIndex, - ) -> Result, String> { - let mut diff_list: Vec = Vec::new(); - let target_area = &Area { - sheet: target.sheet, - row: target.row, - column: target.column, - width: source_area.width, - height: source_area.height, - }; - // Walk over every formula - let cells = self.get_all_cells(); - for cell in cells { - if let Some(f) = self - .workbook - .worksheet(cell.index) - .expect("Worksheet must exist") - .cell(cell.row, cell.column) - .expect("Cell must exist") - .get_formula() - { - let sheet = cell.index; - let row = cell.row; - let column = cell.column; - - // If cell is in the source or target area, skip - if ref_is_in_area(sheet, row, column, source_area) - || ref_is_in_area(sheet, row, column, target_area) - { - continue; - } - - // Get the formula - // Get a copy of the AST - let node = &mut self.parsed_formulas[sheet as usize][f as usize].clone(); - let cell_reference = CellReferenceRC { - sheet: self.workbook.worksheets[sheet as usize].get_name(), - column: cell.column, - row: cell.row, - }; - let context = CellReferenceIndex { sheet, column, row }; - let formula = to_string(node, &cell_reference); - let target_sheet_name = &self.workbook.worksheets[target.sheet as usize].name; - forward_references( - node, - &context, - source_area, - target.sheet, - target_sheet_name, - target.row, - target.column, - ); - - // If the string representation of the formula has changed update the cell - let updated_formula = to_string(node, &cell_reference); - if formula != updated_formula { - self.update_cell_with_formula( - sheet, - row, - column, - format!("={updated_formula}"), - )?; - // Update the diff list - diff_list.push(SetCellValue { - cell: CellReferenceIndex { sheet, column, row }, - new_value: CellValue::Value(format!("={}", updated_formula)), - old_value: CellValue::Value(format!("={}", formula)), - }); - } - } - } - Ok(diff_list) - } -} diff --git a/base/src/expressions/lexer/mod.rs b/base/src/expressions/lexer/mod.rs index 0e4dded..d629d0d 100644 --- a/base/src/expressions/lexer/mod.rs +++ b/base/src/expressions/lexer/mod.rs @@ -187,6 +187,7 @@ impl Lexer { ']' => TokenType::RightBracket, ':' => TokenType::Colon, ';' => TokenType::Semicolon, + '@' => TokenType::At, ',' => { if self.locale.numbers.symbols.decimal == "," { match self.consume_number(',') { diff --git a/base/src/expressions/lexer/test/mod.rs b/base/src/expressions/lexer/test/mod.rs index f795ba5..2da65f5 100644 --- a/base/src/expressions/lexer/test/mod.rs +++ b/base/src/expressions/lexer/test/mod.rs @@ -1,4 +1,5 @@ mod test_common; +mod test_implicit_intersection; mod test_language; mod test_locale; mod test_ranges; diff --git a/base/src/expressions/lexer/test/test_implicit_intersection.rs b/base/src/expressions/lexer/test/test_implicit_intersection.rs new file mode 100644 index 0000000..d055850 --- /dev/null +++ b/base/src/expressions/lexer/test/test_implicit_intersection.rs @@ -0,0 +1,25 @@ +#![allow(clippy::unwrap_used)] + +use crate::expressions::{ + lexer::{Lexer, LexerMode}, + token::TokenType::*, +}; +use crate::language::get_language; +use crate::locale::get_locale; + +fn new_lexer(formula: &str) -> Lexer { + let locale = get_locale("en").unwrap(); + let language = get_language("en").unwrap(); + Lexer::new(formula, LexerMode::A1, locale, language) +} + +#[test] +fn sum_implicit_intersection() { + let mut lx = new_lexer("sum(@A1:A3)"); + assert_eq!(lx.next_token(), Ident("sum".to_string())); + assert_eq!(lx.next_token(), LeftParenthesis); + assert_eq!(lx.next_token(), At); + assert!(matches!(lx.next_token(), Range { .. })); + assert_eq!(lx.next_token(), RightParenthesis); + assert_eq!(lx.next_token(), EOF); +} diff --git a/base/src/expressions/parser/mod.rs b/base/src/expressions/parser/mod.rs index 62a4d47..f284fb9 100644 --- a/base/src/expressions/parser/mod.rs +++ b/base/src/expressions/parser/mod.rs @@ -1,5 +1,5 @@ /*! -# GRAMAR +# GRAMMAR
 opComp   => '=' | '<' | '>' | '<=' } '>=' | '<>'
@@ -12,7 +12,8 @@ term    => factor (opFactor factor)*
 factor  => prod (opProd prod)*
 prod    => power ('^' power)*
 power   => (unaryOp)* range '%'*
-range   => primary (':' primary)?
+range   => implicit (':' primary)?
+implicit=> '@' primary | primary
 primary => '(' expr ')'
         => number
         => function '(' f_args ')'
@@ -45,8 +46,8 @@ use super::utils::number_to_column;
 use token::OpCompare;
 
 pub mod move_formula;
+pub mod static_analysis;
 pub mod stringify;
-pub mod walk;
 
 #[cfg(test)]
 mod tests;
@@ -81,6 +82,9 @@ fn get_table_column_by_name(table_column_name: &str, table: &Table) -> Option, String);
+
 pub(crate) struct Reference<'a> {
     sheet_name: &'a Option,
     sheet_index: u32,
@@ -164,9 +168,13 @@ pub enum Node {
         args: Vec,
     },
     ArrayKind(Vec),
-    DefinedNameKind((String, Option)),
+    DefinedNameKind(DefinedNameS),
     TableNameKind(String),
     WrongVariableKind(String),
+    ImplicitIntersection {
+        automatic: bool,
+        child: Box,
+    },
     CompareKind {
         kind: OpCompare,
         left: Box,
@@ -189,7 +197,7 @@ pub enum Node {
 pub struct Parser {
     lexer: lexer::Lexer,
     worksheets: Vec,
-    defined_names: Vec<(String, Option)>,
+    defined_names: Vec,
     context: CellReferenceRC,
     tables: HashMap,
 }
@@ -197,7 +205,7 @@ pub struct Parser {
 impl Parser {
     pub fn new(
         worksheets: Vec,
-        defined_names: Vec<(String, Option)>,
+        defined_names: Vec,
         tables: HashMap,
     ) -> Parser {
         let lexer = lexer::Lexer::new(
@@ -228,7 +236,7 @@ impl Parser {
     pub fn set_worksheets_and_names(
         &mut self,
         worksheets: Vec,
-        defined_names: Vec<(String, Option)>,
+        defined_names: Vec,
     ) {
         self.worksheets = worksheets;
         self.defined_names = defined_names;
@@ -252,17 +260,17 @@ impl Parser {
 
     // Returns:
     //  * None: If there is no defined name by that name
-    //  * Some(Some(index)): If there is a defined name local to that sheet
+    //  * 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> {
-        for (df_name, df_scope) in &self.defined_names {
+    fn get_defined_name(&self, name: &str, sheet: u32) -> Option<(Option, 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);
+                return Some((*df_scope, df_formula.to_owned()));
             }
         }
-        for (df_name, df_scope) in &self.defined_names {
+        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);
+                return Some((None, df_formula.to_owned()));
             }
         }
         None
@@ -411,7 +419,7 @@ impl Parser {
     }
 
     fn parse_range(&mut self) -> Node {
-        let t = self.parse_primary();
+        let t = self.parse_implicit();
         if let Node::ParseErrorKind { .. } = t {
             return t;
         }
@@ -430,6 +438,22 @@ impl Parser {
         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_primary(&mut self) -> Node {
         let next_token = self.lexer.next_token();
         match next_token {
@@ -604,6 +628,20 @@ impl Parser {
                             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;
@@ -620,8 +658,8 @@ impl Parser {
                 };
 
                 // Could be a defined name or a table
-                if let Some(scope) = self.get_defined_name(&name, context_sheet_index) {
-                    return Node::DefinedNameKind((name, scope));
+                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() {
@@ -706,6 +744,14 @@ impl Parser {
                     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
diff --git a/base/src/expressions/parser/move_formula.rs b/base/src/expressions/parser/move_formula.rs
index 5453cb0..6b2bb1f 100644
--- a/base/src/expressions/parser/move_formula.rs
+++ b/base/src/expressions/parser/move_formula.rs
@@ -375,7 +375,7 @@ fn to_string_moved(node: &Node, move_context: &MoveContext) -> String {
             }
             format!("{{{}}}", arguments)
         }
-        DefinedNameKind((name, _)) => name.to_string(),
+        DefinedNameKind((name, ..)) => name.to_string(),
         TableNameKind(name) => name.to_string(),
         WrongVariableKind(name) => name.to_string(),
         CompareKind { kind, left, right } => format!(
@@ -395,5 +395,11 @@ fn to_string_moved(node: &Node, move_context: &MoveContext) -> String {
             position: _,
         } => formula.to_string(),
         EmptyArgKind => "".to_string(),
+        ImplicitIntersection {
+            automatic: _,
+            child,
+        } => {
+            format!("@{}", to_string_moved(child, move_context))
+        }
     }
 }
diff --git a/base/src/expressions/parser/static_analysis.rs b/base/src/expressions/parser/static_analysis.rs
new file mode 100644
index 0000000..280ac24
--- /dev/null
+++ b/base/src/expressions/parser/static_analysis.rs
@@ -0,0 +1,984 @@
+use crate::functions::Function;
+
+use super::Node;
+
+use once_cell::sync::Lazy;
+use regex::Regex;
+
+#[allow(clippy::expect_used)]
+static RE: Lazy =
+    Lazy::new(|| Regex::new(r":[A-Z]*[0-9]*$").expect("Regex is known to be valid"));
+
+fn is_range_reference(s: &str) -> bool {
+    RE.is_match(s)
+}
+
+/*
+
+# NOTES on the Implicit Intersection operator: @
+
+ Sometimes we obtain a range where we expected a single argument. This can happen:
+
+ * As an argument of a function, eg: `SIN(A1:A5)`
+ * As the result of a computation of a formula `=A1:A5`
+
+ In previous versions of the Friendly Giant the spreadsheet engine would perform an operation called _implicit intersection_
+ that tries to find a single cell within the range. It works by picking a cell in the range that is the same row or the same column
+ as the cell. If there is just one we return that otherwise we return the `#REF!` error.
+
+ Examples:
+
+ * Siting on `C3` the formula `=D1:D5` will return `D3`
+ * Sitting on `C3` the formula `=D:D` will return `D3`
+ * Sitting on `C3` the formula `=A1:A7` will return `A3`
+ * Sitting on `C3` the formula `=A5:A8` will return `#REF!`
+ * Sitting on `C3` the formula `D1:G7` will return `#REF!`
+
+ Today's version of the engine will result in a dynamic array spilling the result through several cells.
+ To force the old behaviour we can use the _implicit intersection operator_: @
+
+ * `=@A1:A7` or `=SIN(@A1:A7)
+
+ When parsing formulas that come form old workbooks this is done automatically.
+ We call this version of the II operator the _automatic_ II operator.
+
+ We can also insert the II operator in places where before was impossible:
+
+ * `=SUM(@A1:A7)`
+
+ This formulas will not be compatible with old versions of the engine. The FG will stringify this as `=SUM(_xlfn.SIMPLE(A1:A7))`.
+ */
+
+/// Transverses the formula tree adding the implicit intersection operator in all arguments of functions that
+/// expect a scalar but get a range.
+///  * A:A => @A:A
+///  * SIN(A1:D1) => SIN(@A1:D1)
+///
+/// Assumes formula return a scalar
+pub fn add_implicit_intersection(node: &mut Node, add: bool) {
+    match node {
+        Node::BooleanKind(_)
+        | Node::NumberKind(_)
+        | Node::StringKind(_)
+        | Node::ErrorKind(_)
+        | Node::EmptyArgKind
+        | Node::ParseErrorKind { .. }
+        | Node::WrongReferenceKind { .. }
+        | Node::WrongRangeKind { .. }
+        | Node::InvalidFunctionKind { .. }
+        | Node::ArrayKind(_)
+        | Node::ReferenceKind { .. } => {}
+        Node::ImplicitIntersection { child, .. } => {
+            // We need to check wether the II can be automatic or not
+            let mut new_node = child.as_ref().clone();
+            add_implicit_intersection(&mut new_node, add);
+            if matches!(&new_node, Node::ImplicitIntersection { .. }) {
+                *node = new_node
+            }
+        }
+        Node::RangeKind {
+            row1,
+            column1,
+            row2,
+            column2,
+            sheet_name,
+            sheet_index,
+            absolute_row1,
+            absolute_column1,
+            absolute_row2,
+            absolute_column2,
+        } => {
+            if add {
+                *node = Node::ImplicitIntersection {
+                    automatic: true,
+                    child: Box::new(Node::RangeKind {
+                        sheet_name: sheet_name.clone(),
+                        sheet_index: *sheet_index,
+                        absolute_row1: *absolute_row1,
+                        absolute_column1: *absolute_column1,
+                        row1: *row1,
+                        column1: *column1,
+                        absolute_row2: *absolute_row2,
+                        absolute_column2: *absolute_column2,
+                        row2: *row2,
+                        column2: *column2,
+                    }),
+                };
+            }
+        }
+        Node::OpRangeKind { left, right } => {
+            if add {
+                *node = Node::ImplicitIntersection {
+                    automatic: true,
+                    child: Box::new(Node::OpRangeKind {
+                        left: left.clone(),
+                        right: right.clone(),
+                    }),
+                }
+            }
+        }
+
+        // operations
+        Node::UnaryKind { right, .. } => add_implicit_intersection(right, add),
+        Node::OpConcatenateKind { left, right }
+        | Node::OpSumKind { left, right, .. }
+        | Node::OpProductKind { left, right, .. }
+        | Node::OpPowerKind { left, right, .. }
+        | Node::CompareKind { left, right, .. } => {
+            add_implicit_intersection(left, add);
+            add_implicit_intersection(right, add);
+        }
+
+        Node::DefinedNameKind(v) => {
+            if add {
+                // Not all defined names deserve the II operator
+                // For instance =Sheet1!A1 doesn't need to be intersected
+                if is_range_reference(&v.2) {
+                    *node = Node::ImplicitIntersection {
+                        automatic: true,
+                        child: Box::new(Node::DefinedNameKind(v.to_owned())),
+                    }
+                }
+            }
+        }
+        Node::WrongVariableKind(v) => {
+            if add {
+                *node = Node::ImplicitIntersection {
+                    automatic: true,
+                    child: Box::new(Node::WrongVariableKind(v.to_owned())),
+                }
+            }
+        }
+        Node::TableNameKind(_) => {
+            // noop for now
+        }
+        Node::FunctionKind { kind, args } => {
+            let arg_count = args.len();
+            let signature = get_function_args_signature(kind, arg_count);
+            for index in 0..arg_count {
+                if matches!(signature[index], Signature::Scalar)
+                    && matches!(
+                        run_static_analysis_on_node(&args[index]),
+                        StaticResult::Range(_, _) | StaticResult::Unknown
+                    )
+                {
+                    add_implicit_intersection(&mut args[index], true);
+                } else {
+                    add_implicit_intersection(&mut args[index], false);
+                }
+            }
+            if add
+                && matches!(
+                    run_static_analysis_on_node(node),
+                    StaticResult::Range(_, _) | StaticResult::Unknown
+                )
+            {
+                *node = Node::ImplicitIntersection {
+                    automatic: true,
+                    child: Box::new(node.clone()),
+                }
+            }
+        }
+    };
+}
+
+pub(crate) enum StaticResult {
+    Scalar,
+    Array(i32, i32),
+    Range(i32, i32),
+    Unknown,
+    // TODO: What if one of the dimensions is known?
+    // what if the dimensions are unknown but bounded?
+}
+
+fn static_analysis_op_nodes(left: &Node, right: &Node) -> StaticResult {
+    let lhs = run_static_analysis_on_node(left);
+    let rhs = run_static_analysis_on_node(right);
+    match (lhs, rhs) {
+        (StaticResult::Scalar, StaticResult::Scalar) => StaticResult::Scalar,
+        (StaticResult::Scalar, StaticResult::Array(a, b) | StaticResult::Range(a, b)) => {
+            StaticResult::Array(a, b)
+        }
+
+        (StaticResult::Array(a, b) | StaticResult::Range(a, b), StaticResult::Scalar) => {
+            StaticResult::Array(a, b)
+        }
+        (
+            StaticResult::Array(a1, b1) | StaticResult::Range(a1, b1),
+            StaticResult::Array(a2, b2) | StaticResult::Range(a2, b2),
+        ) => StaticResult::Array(a1.max(a2), b1.max(b2)),
+
+        (_, StaticResult::Unknown) => StaticResult::Unknown,
+        (StaticResult::Unknown, _) => StaticResult::Unknown,
+    }
+}
+
+// Returns:
+//  * Scalar if we can proof the result of the evaluation is a scalar
+//  * Array(a, b) if we know it will be an a x b array.
+//  * Range(a, b) if we know it will be a a x b range.
+//  * Unknown if we cannot guaranty either
+fn run_static_analysis_on_node(node: &Node) -> StaticResult {
+    match node {
+        Node::BooleanKind(_)
+        | Node::NumberKind(_)
+        | Node::StringKind(_)
+        | Node::ErrorKind(_)
+        | Node::EmptyArgKind => StaticResult::Scalar,
+        Node::UnaryKind { right, .. } => run_static_analysis_on_node(right),
+        Node::ParseErrorKind { .. } => {
+            // StaticResult::Unknown is also valid
+            StaticResult::Scalar
+        }
+        Node::WrongReferenceKind { .. } => {
+            // StaticResult::Unknown is also valid
+            StaticResult::Scalar
+        }
+        Node::WrongRangeKind { .. } => {
+            // StaticResult::Unknown or Array is also valid
+            StaticResult::Scalar
+        }
+        Node::InvalidFunctionKind { .. } => {
+            // StaticResult::Unknown is also valid
+            StaticResult::Scalar
+        }
+        Node::ArrayKind(array) => {
+            let n = array.len() as i32;
+            // FIXME: This is a placeholder until we implement arrays
+            StaticResult::Array(n, 1)
+        }
+        Node::RangeKind {
+            row1,
+            column1,
+            row2,
+            column2,
+            ..
+        } => StaticResult::Range(row2 - row1, column2 - column1),
+        Node::OpRangeKind { .. } => {
+            // TODO: We could do a bit better here
+            StaticResult::Unknown
+        }
+        Node::ReferenceKind { .. } => StaticResult::Scalar,
+
+        // binary operations
+        Node::OpConcatenateKind { left, right } => static_analysis_op_nodes(left, right),
+        Node::OpSumKind { left, right, .. } => static_analysis_op_nodes(left, right),
+        Node::OpProductKind { left, right, .. } => static_analysis_op_nodes(left, right),
+        Node::OpPowerKind { left, right, .. } => static_analysis_op_nodes(left, right),
+        Node::CompareKind { left, right, .. } => static_analysis_op_nodes(left, right),
+
+        // defined names
+        Node::DefinedNameKind(_) => StaticResult::Unknown,
+        Node::WrongVariableKind(_) => StaticResult::Unknown,
+        Node::TableNameKind(_) => StaticResult::Unknown,
+        Node::FunctionKind { kind, args } => static_analysis_on_function(kind, args),
+        Node::ImplicitIntersection { .. } => StaticResult::Scalar,
+    }
+}
+
+// If all the arguments are scalars the function will return a scalar
+// If any of the arguments is a range or an array it will return an array
+fn scalar_arguments(args: &[Node]) -> StaticResult {
+    let mut n = 0;
+    let mut m = 0;
+    for arg in args {
+        match run_static_analysis_on_node(arg) {
+            StaticResult::Scalar => {
+                // noop
+            }
+            StaticResult::Array(a, b) | StaticResult::Range(a, b) => {
+                n = n.max(a);
+                m = m.max(b);
+            }
+            StaticResult::Unknown => return StaticResult::Unknown,
+        }
+    }
+    if n == 0 && m == 0 {
+        return StaticResult::Scalar;
+    }
+    StaticResult::Array(n, m)
+}
+
+// We only care if the function can return a range or not
+fn not_implemented(_args: &[Node]) -> StaticResult {
+    StaticResult::Scalar
+}
+
+fn static_analysis_offset(args: &[Node]) -> StaticResult {
+    // If first argument is a single cell reference and there are no4th and 5th argument,
+    // or they are 1, then it is a scalar
+    let arg_count = args.len();
+    if arg_count < 3 {
+        // Actually an error
+        return StaticResult::Scalar;
+    }
+    if !matches!(args[0], Node::ReferenceKind { .. }) {
+        return StaticResult::Unknown;
+    }
+    if arg_count == 3 {
+        return StaticResult::Scalar;
+    }
+    match args[3] {
+        Node::NumberKind(f) => {
+            if f != 1.0 {
+                return StaticResult::Unknown;
+            }
+        }
+        _ => return StaticResult::Unknown,
+    };
+    if arg_count == 4 {
+        return StaticResult::Scalar;
+    }
+    match args[4] {
+        Node::NumberKind(f) => {
+            if f != 1.0 {
+                return StaticResult::Unknown;
+            }
+        }
+        _ => return StaticResult::Unknown,
+    };
+    StaticResult::Unknown
+}
+
+// fn static_analysis_choose(_args: &[Node]) -> StaticResult {
+//     // We will always insert the @ in CHOOSE, but technically it is only needed if one of the elements is a range
+//     StaticResult::Unknown
+// }
+
+fn static_analysis_indirect(_args: &[Node]) -> StaticResult {
+    // We will always insert the @, but we don't need to do that in every scenario`
+    StaticResult::Unknown
+}
+
+fn static_analysis_index(_args: &[Node]) -> StaticResult {
+    // INDEX has two forms, but they are indistinguishable at parse time.
+    StaticResult::Unknown
+}
+
+#[derive(Clone)]
+enum Signature {
+    Scalar,
+    Vector,
+    Error,
+}
+
+fn args_signature_no_args(arg_count: usize) -> Vec {
+    if arg_count == 0 {
+        vec![]
+    } else {
+        vec![Signature::Error; arg_count]
+    }
+}
+
+fn args_signature_scalars(
+    arg_count: usize,
+    required_count: usize,
+    optional_count: usize,
+) -> Vec {
+    if arg_count >= required_count && arg_count <= required_count + optional_count {
+        vec![Signature::Scalar; arg_count]
+    } else {
+        vec![Signature::Error; arg_count]
+    }
+}
+
+fn args_signature_one_vector(arg_count: usize) -> Vec {
+    if arg_count == 1 {
+        vec![Signature::Vector]
+    } else {
+        vec![Signature::Error; arg_count]
+    }
+}
+
+fn args_signature_sumif(arg_count: usize) -> Vec {
+    if arg_count == 2 {
+        vec![Signature::Vector, Signature::Scalar]
+    } else if arg_count == 3 {
+        vec![Signature::Vector, Signature::Scalar, Signature::Vector]
+    } else {
+        vec![Signature::Error; arg_count]
+    }
+}
+
+// 1 or none scalars
+fn args_signature_sheet(arg_count: usize) -> Vec {
+    if arg_count == 0 {
+        vec![]
+    } else if arg_count == 1 {
+        vec![Signature::Scalar]
+    } else {
+        vec![Signature::Error; arg_count]
+    }
+}
+
+fn args_signature_hlookup(arg_count: usize) -> Vec {
+    if arg_count == 3 {
+        vec![Signature::Vector, Signature::Vector, Signature::Scalar]
+    } else if arg_count == 4 {
+        vec![
+            Signature::Vector,
+            Signature::Vector,
+            Signature::Scalar,
+            Signature::Vector,
+        ]
+    } else {
+        vec![Signature::Error; arg_count]
+    }
+}
+
+fn args_signature_index(arg_count: usize) -> Vec {
+    if arg_count == 2 {
+        vec![Signature::Vector, Signature::Scalar]
+    } else if arg_count == 3 {
+        vec![Signature::Vector, Signature::Scalar, Signature::Scalar]
+    } else if arg_count == 4 {
+        vec![
+            Signature::Vector,
+            Signature::Scalar,
+            Signature::Scalar,
+            Signature::Scalar,
+        ]
+    } else {
+        vec![Signature::Error; arg_count]
+    }
+}
+
+fn args_signature_lookup(arg_count: usize) -> Vec {
+    if arg_count == 2 {
+        vec![Signature::Vector, Signature::Vector]
+    } else if arg_count == 3 {
+        vec![Signature::Vector, Signature::Vector, Signature::Vector]
+    } else {
+        vec![Signature::Error; arg_count]
+    }
+}
+
+fn args_signature_match(arg_count: usize) -> Vec {
+    if arg_count == 2 {
+        vec![Signature::Vector, Signature::Vector]
+    } else if arg_count == 3 {
+        vec![Signature::Vector, Signature::Vector, Signature::Scalar]
+    } else {
+        vec![Signature::Error; arg_count]
+    }
+}
+
+fn args_signature_offset(arg_count: usize) -> Vec {
+    if arg_count == 3 {
+        vec![Signature::Vector, Signature::Scalar, Signature::Scalar]
+    } else if arg_count == 4 {
+        vec![
+            Signature::Vector,
+            Signature::Scalar,
+            Signature::Scalar,
+            Signature::Scalar,
+        ]
+    } else if arg_count == 5 {
+        vec![
+            Signature::Vector,
+            Signature::Scalar,
+            Signature::Scalar,
+            Signature::Scalar,
+            Signature::Scalar,
+        ]
+    } else {
+        vec![Signature::Error; arg_count]
+    }
+}
+
+fn args_signature_row(arg_count: usize) -> Vec {
+    if arg_count == 0 {
+        vec![]
+    } else if arg_count == 1 {
+        vec![Signature::Vector]
+    } else {
+        vec![Signature::Error; arg_count]
+    }
+}
+
+fn args_signature_xlookup(arg_count: usize) -> Vec {
+    if !(3..=6).contains(&arg_count) {
+        return vec![Signature::Error; arg_count];
+    }
+    let mut result = vec![Signature::Scalar; arg_count];
+    result[0] = Signature::Vector;
+    result[1] = Signature::Vector;
+    result[2] = Signature::Vector;
+    result
+}
+
+fn args_signature_textafter(arg_count: usize) -> Vec {
+    if !(2..=6).contains(&arg_count) {
+        vec![Signature::Scalar; arg_count]
+    } else {
+        vec![Signature::Error; arg_count]
+    }
+}
+
+fn args_signature_textjoin(arg_count: usize) -> Vec {
+    if arg_count >= 3 {
+        let mut result = vec![Signature::Vector; arg_count];
+        result[0] = Signature::Scalar;
+        result[1] = Signature::Scalar;
+        result
+    } else {
+        vec![Signature::Error; arg_count]
+    }
+}
+
+fn args_signature_npv(arg_count: usize) -> Vec {
+    if arg_count < 2 {
+        return vec![Signature::Error; arg_count];
+    }
+    let mut result = vec![Signature::Vector; arg_count];
+    result[0] = Signature::Scalar;
+    result
+}
+
+fn args_signature_irr(arg_count: usize) -> Vec {
+    if arg_count > 2 {
+        vec![Signature::Error; arg_count]
+    } else if arg_count == 1 {
+        vec![Signature::Vector]
+    } else {
+        vec![Signature::Vector, Signature::Scalar]
+    }
+}
+
+fn args_signature_xirr(arg_count: usize) -> Vec {
+    if arg_count == 2 {
+        vec![Signature::Vector; arg_count]
+    } else if arg_count == 3 {
+        vec![Signature::Vector, Signature::Vector, Signature::Scalar]
+    } else {
+        vec![Signature::Error; arg_count]
+    }
+}
+
+fn args_signature_mirr(arg_count: usize) -> Vec {
+    if arg_count != 3 {
+        vec![Signature::Error; arg_count]
+    } else {
+        vec![Signature::Vector, Signature::Scalar, Signature::Scalar]
+    }
+}
+
+fn args_signature_xnpv(arg_count: usize) -> Vec {
+    if arg_count != 3 {
+        vec![Signature::Error; arg_count]
+    } else {
+        vec![Signature::Scalar, Signature::Vector, Signature::Vector]
+    }
+}
+
+// FIXME: This is terrible duplications of efforts. We use the signature in at least three different places:
+// 1. When computing the function
+// 2. Checking the arguments to see if we need to insert the implicit intersection operator
+// 3. Understanding the return value
+//
+// The signature of the functions should be defined only once
+
+// Given a function and a number of arguments this returns the arguments at each position
+// are expected to be scalars or vectors (array/ranges).
+// Sets signature::Error to all arguments if the number of arguments is incorrect.
+fn get_function_args_signature(kind: &Function, arg_count: usize) -> Vec {
+    match kind {
+        Function::And => vec![Signature::Vector; arg_count],
+        Function::False => args_signature_no_args(arg_count),
+        Function::If => args_signature_scalars(arg_count, 2, 1),
+        Function::Iferror => args_signature_scalars(arg_count, 2, 0),
+        Function::Ifna => args_signature_scalars(arg_count, 2, 0),
+        Function::Ifs => vec![Signature::Scalar; arg_count],
+        Function::Not => args_signature_scalars(arg_count, 1, 0),
+        Function::Or => vec![Signature::Vector; arg_count],
+        Function::Switch => vec![Signature::Scalar; arg_count],
+        Function::True => args_signature_no_args(arg_count),
+        Function::Xor => vec![Signature::Vector; arg_count],
+        Function::Abs => args_signature_scalars(arg_count, 1, 0),
+        Function::Acos => args_signature_scalars(arg_count, 1, 0),
+        Function::Acosh => args_signature_scalars(arg_count, 1, 0),
+        Function::Asin => args_signature_scalars(arg_count, 1, 0),
+        Function::Asinh => args_signature_scalars(arg_count, 1, 0),
+        Function::Atan => args_signature_scalars(arg_count, 1, 0),
+        Function::Atan2 => args_signature_scalars(arg_count, 2, 0),
+        Function::Atanh => args_signature_scalars(arg_count, 1, 0),
+        Function::Choose => vec![Signature::Scalar; arg_count],
+        Function::Column => args_signature_row(arg_count),
+        Function::Columns => args_signature_one_vector(arg_count),
+        Function::Cos => args_signature_scalars(arg_count, 1, 0),
+        Function::Cosh => args_signature_scalars(arg_count, 1, 0),
+        Function::Max => vec![Signature::Vector; arg_count],
+        Function::Min => vec![Signature::Vector; arg_count],
+        Function::Pi => args_signature_no_args(arg_count),
+        Function::Power => args_signature_scalars(arg_count, 2, 0),
+        Function::Product => vec![Signature::Vector; arg_count],
+        Function::Round => args_signature_scalars(arg_count, 2, 0),
+        Function::Rounddown => args_signature_scalars(arg_count, 2, 0),
+        Function::Roundup => args_signature_scalars(arg_count, 2, 0),
+        Function::Sin => args_signature_scalars(arg_count, 1, 0),
+        Function::Sinh => args_signature_scalars(arg_count, 1, 0),
+        Function::Sqrt => args_signature_scalars(arg_count, 1, 0),
+        Function::Sqrtpi => args_signature_scalars(arg_count, 1, 0),
+        Function::Sum => vec![Signature::Vector; arg_count],
+        Function::Sumif => args_signature_sumif(arg_count),
+        Function::Sumifs => vec![Signature::Vector; arg_count],
+        Function::Tan => args_signature_scalars(arg_count, 1, 0),
+        Function::Tanh => args_signature_scalars(arg_count, 1, 0),
+        Function::ErrorType => args_signature_scalars(arg_count, 1, 0),
+        Function::Isblank => args_signature_scalars(arg_count, 1, 0),
+        Function::Iserr => args_signature_scalars(arg_count, 1, 0),
+        Function::Iserror => args_signature_scalars(arg_count, 1, 0),
+        Function::Iseven => args_signature_scalars(arg_count, 1, 0),
+        Function::Isformula => args_signature_scalars(arg_count, 1, 0),
+        Function::Islogical => args_signature_scalars(arg_count, 1, 0),
+        Function::Isna => args_signature_scalars(arg_count, 1, 0),
+        Function::Isnontext => args_signature_scalars(arg_count, 1, 0),
+        Function::Isnumber => args_signature_scalars(arg_count, 1, 0),
+        Function::Isodd => args_signature_scalars(arg_count, 1, 0),
+        Function::Isref => args_signature_one_vector(arg_count),
+        Function::Istext => args_signature_scalars(arg_count, 1, 0),
+        Function::Na => args_signature_no_args(arg_count),
+        Function::Sheet => args_signature_sheet(arg_count),
+        Function::Type => args_signature_one_vector(arg_count),
+        Function::Hlookup => args_signature_hlookup(arg_count),
+        Function::Index => args_signature_index(arg_count),
+        Function::Indirect => args_signature_scalars(arg_count, 1, 0),
+        Function::Lookup => args_signature_lookup(arg_count),
+        Function::Match => args_signature_match(arg_count),
+        Function::Offset => args_signature_offset(arg_count),
+        Function::Row => args_signature_row(arg_count),
+        Function::Rows => args_signature_one_vector(arg_count),
+        Function::Vlookup => args_signature_hlookup(arg_count),
+        Function::Xlookup => args_signature_xlookup(arg_count),
+        Function::Concat => vec![Signature::Vector; arg_count],
+        Function::Concatenate => vec![Signature::Scalar; arg_count],
+        Function::Exact => args_signature_scalars(arg_count, 2, 0),
+        Function::Find => args_signature_scalars(arg_count, 2, 1),
+        Function::Left => args_signature_scalars(arg_count, 1, 1),
+        Function::Len => args_signature_scalars(arg_count, 1, 0),
+        Function::Lower => args_signature_scalars(arg_count, 1, 0),
+        Function::Mid => args_signature_scalars(arg_count, 3, 0),
+        Function::Rept => args_signature_scalars(arg_count, 2, 0),
+        Function::Right => args_signature_scalars(arg_count, 2, 1),
+        Function::Search => args_signature_scalars(arg_count, 2, 1),
+        Function::Substitute => args_signature_scalars(arg_count, 3, 1),
+        Function::T => args_signature_scalars(arg_count, 1, 0),
+        Function::Text => args_signature_scalars(arg_count, 2, 0),
+        Function::Textafter => args_signature_textafter(arg_count),
+        Function::Textbefore => args_signature_textafter(arg_count),
+        Function::Textjoin => args_signature_textjoin(arg_count),
+        Function::Trim => args_signature_scalars(arg_count, 1, 0),
+        Function::Upper => args_signature_scalars(arg_count, 1, 0),
+        Function::Value => args_signature_scalars(arg_count, 1, 0),
+        Function::Valuetotext => args_signature_scalars(arg_count, 1, 1),
+        Function::Average => vec![Signature::Vector; arg_count],
+        Function::Averagea => vec![Signature::Vector; arg_count],
+        Function::Averageif => args_signature_sumif(arg_count),
+        Function::Averageifs => vec![Signature::Vector; arg_count],
+        Function::Count => vec![Signature::Vector; arg_count],
+        Function::Counta => vec![Signature::Vector; arg_count],
+        Function::Countblank => vec![Signature::Vector; arg_count],
+        Function::Countif => args_signature_sumif(arg_count),
+        Function::Countifs => vec![Signature::Vector; arg_count],
+        Function::Maxifs => vec![Signature::Vector; arg_count],
+        Function::Minifs => vec![Signature::Vector; arg_count],
+        Function::Date => args_signature_scalars(arg_count, 3, 0),
+        Function::Day => args_signature_scalars(arg_count, 1, 0),
+        Function::Edate => args_signature_scalars(arg_count, 2, 0),
+        Function::Eomonth => args_signature_scalars(arg_count, 2, 0),
+        Function::Month => args_signature_scalars(arg_count, 1, 0),
+        Function::Now => args_signature_no_args(arg_count),
+        Function::Today => args_signature_no_args(arg_count),
+        Function::Year => args_signature_scalars(arg_count, 1, 0),
+        Function::Cumipmt => args_signature_scalars(arg_count, 6, 0),
+        Function::Cumprinc => args_signature_scalars(arg_count, 6, 0),
+        Function::Db => args_signature_scalars(arg_count, 4, 1),
+        Function::Ddb => args_signature_scalars(arg_count, 4, 1),
+        Function::Dollarde => args_signature_scalars(arg_count, 2, 0),
+        Function::Dollarfr => args_signature_scalars(arg_count, 2, 0),
+        Function::Effect => args_signature_scalars(arg_count, 2, 0),
+        Function::Fv => args_signature_scalars(arg_count, 3, 2),
+        Function::Ipmt => args_signature_scalars(arg_count, 4, 2),
+        Function::Irr => args_signature_irr(arg_count),
+        Function::Ispmt => args_signature_scalars(arg_count, 4, 0),
+        Function::Mirr => args_signature_mirr(arg_count),
+        Function::Nominal => args_signature_scalars(arg_count, 2, 0),
+        Function::Nper => args_signature_scalars(arg_count, 3, 2),
+        Function::Npv => args_signature_npv(arg_count),
+        Function::Pduration => args_signature_scalars(arg_count, 3, 0),
+        Function::Pmt => args_signature_scalars(arg_count, 3, 2),
+        Function::Ppmt => args_signature_scalars(arg_count, 4, 2),
+        Function::Pv => args_signature_scalars(arg_count, 3, 2),
+        Function::Rate => args_signature_scalars(arg_count, 3, 3),
+        Function::Rri => args_signature_scalars(arg_count, 3, 0),
+        Function::Sln => args_signature_scalars(arg_count, 3, 0),
+        Function::Syd => args_signature_scalars(arg_count, 4, 0),
+        Function::Tbilleq => args_signature_scalars(arg_count, 3, 0),
+        Function::Tbillprice => args_signature_scalars(arg_count, 3, 0),
+        Function::Tbillyield => args_signature_scalars(arg_count, 3, 0),
+        Function::Xirr => args_signature_xirr(arg_count),
+        Function::Xnpv => args_signature_xnpv(arg_count),
+        Function::Besseli => args_signature_scalars(arg_count, 2, 0),
+        Function::Besselj => args_signature_scalars(arg_count, 2, 0),
+        Function::Besselk => args_signature_scalars(arg_count, 2, 0),
+        Function::Bessely => args_signature_scalars(arg_count, 2, 0),
+        Function::Erf => args_signature_scalars(arg_count, 1, 1),
+        Function::Erfc => args_signature_scalars(arg_count, 1, 0),
+        Function::ErfcPrecise => args_signature_scalars(arg_count, 1, 0),
+        Function::ErfPrecise => args_signature_scalars(arg_count, 1, 0),
+        Function::Bin2dec => args_signature_scalars(arg_count, 1, 0),
+        Function::Bin2hex => args_signature_scalars(arg_count, 1, 0),
+        Function::Bin2oct => args_signature_scalars(arg_count, 1, 0),
+        Function::Dec2Bin => args_signature_scalars(arg_count, 1, 0),
+        Function::Dec2hex => args_signature_scalars(arg_count, 1, 0),
+        Function::Dec2oct => args_signature_scalars(arg_count, 1, 0),
+        Function::Hex2bin => args_signature_scalars(arg_count, 1, 0),
+        Function::Hex2dec => args_signature_scalars(arg_count, 1, 0),
+        Function::Hex2oct => args_signature_scalars(arg_count, 1, 0),
+        Function::Oct2bin => args_signature_scalars(arg_count, 1, 0),
+        Function::Oct2dec => args_signature_scalars(arg_count, 1, 0),
+        Function::Oct2hex => args_signature_scalars(arg_count, 1, 0),
+        Function::Bitand => args_signature_scalars(arg_count, 2, 0),
+        Function::Bitlshift => args_signature_scalars(arg_count, 2, 0),
+        Function::Bitor => args_signature_scalars(arg_count, 2, 0),
+        Function::Bitrshift => args_signature_scalars(arg_count, 2, 0),
+        Function::Bitxor => args_signature_scalars(arg_count, 2, 0),
+        Function::Complex => args_signature_scalars(arg_count, 2, 1),
+        Function::Imabs => args_signature_scalars(arg_count, 1, 0),
+        Function::Imaginary => args_signature_scalars(arg_count, 1, 0),
+        Function::Imargument => args_signature_scalars(arg_count, 1, 0),
+        Function::Imconjugate => args_signature_scalars(arg_count, 1, 0),
+        Function::Imcos => args_signature_scalars(arg_count, 1, 0),
+        Function::Imcosh => args_signature_scalars(arg_count, 1, 0),
+        Function::Imcot => args_signature_scalars(arg_count, 1, 0),
+        Function::Imcsc => args_signature_scalars(arg_count, 1, 0),
+        Function::Imcsch => args_signature_scalars(arg_count, 1, 0),
+        Function::Imdiv => args_signature_scalars(arg_count, 2, 0),
+        Function::Imexp => args_signature_scalars(arg_count, 1, 0),
+        Function::Imln => args_signature_scalars(arg_count, 1, 0),
+        Function::Imlog10 => args_signature_scalars(arg_count, 1, 0),
+        Function::Imlog2 => args_signature_scalars(arg_count, 1, 0),
+        Function::Impower => args_signature_scalars(arg_count, 2, 0),
+        Function::Improduct => args_signature_scalars(arg_count, 2, 0),
+        Function::Imreal => args_signature_scalars(arg_count, 1, 0),
+        Function::Imsec => args_signature_scalars(arg_count, 1, 0),
+        Function::Imsech => args_signature_scalars(arg_count, 1, 0),
+        Function::Imsin => args_signature_scalars(arg_count, 1, 0),
+        Function::Imsinh => args_signature_scalars(arg_count, 1, 0),
+        Function::Imsqrt => args_signature_scalars(arg_count, 1, 0),
+        Function::Imsub => args_signature_scalars(arg_count, 2, 0),
+        Function::Imsum => args_signature_scalars(arg_count, 2, 0),
+        Function::Imtan => args_signature_scalars(arg_count, 1, 0),
+        Function::Convert => args_signature_scalars(arg_count, 3, 0),
+        Function::Delta => args_signature_scalars(arg_count, 1, 1),
+        Function::Gestep => args_signature_scalars(arg_count, 1, 1),
+        Function::Subtotal => args_signature_npv(arg_count),
+        Function::Rand => args_signature_no_args(arg_count),
+        Function::Randbetween => args_signature_scalars(arg_count, 2, 0),
+        Function::Formulatext => args_signature_scalars(arg_count, 1, 0),
+        Function::Unicode => args_signature_scalars(arg_count, 1, 0),
+        Function::Geomean => vec![Signature::Vector; arg_count],
+    }
+}
+
+// Returns the type of the result (Scalar, Array or Range) depending on the arguments
+fn static_analysis_on_function(kind: &Function, args: &[Node]) -> StaticResult {
+    match kind {
+        Function::And => StaticResult::Scalar,
+        Function::False => StaticResult::Scalar,
+        Function::If => scalar_arguments(args),
+        Function::Iferror => scalar_arguments(args),
+        Function::Ifna => scalar_arguments(args),
+        Function::Ifs => not_implemented(args),
+        Function::Not => StaticResult::Scalar,
+        Function::Or => StaticResult::Scalar,
+        Function::Switch => not_implemented(args),
+        Function::True => StaticResult::Scalar,
+        Function::Xor => StaticResult::Scalar,
+        Function::Abs => scalar_arguments(args),
+        Function::Acos => scalar_arguments(args),
+        Function::Acosh => scalar_arguments(args),
+        Function::Asin => scalar_arguments(args),
+        Function::Asinh => scalar_arguments(args),
+        Function::Atan => scalar_arguments(args),
+        Function::Atan2 => scalar_arguments(args),
+        Function::Atanh => scalar_arguments(args),
+        Function::Choose => scalar_arguments(args), // static_analysis_choose(args, cell),
+        Function::Column => not_implemented(args),
+        Function::Columns => not_implemented(args),
+        Function::Cos => scalar_arguments(args),
+        Function::Cosh => scalar_arguments(args),
+        Function::Max => StaticResult::Scalar,
+        Function::Min => StaticResult::Scalar,
+        Function::Pi => StaticResult::Scalar,
+        Function::Power => scalar_arguments(args),
+        Function::Product => not_implemented(args),
+        Function::Round => scalar_arguments(args),
+        Function::Rounddown => scalar_arguments(args),
+        Function::Roundup => scalar_arguments(args),
+        Function::Sin => scalar_arguments(args),
+        Function::Sinh => scalar_arguments(args),
+        Function::Sqrt => scalar_arguments(args),
+        Function::Sqrtpi => StaticResult::Scalar,
+        Function::Sum => StaticResult::Scalar,
+        Function::Sumif => not_implemented(args),
+        Function::Sumifs => not_implemented(args),
+        Function::Tan => scalar_arguments(args),
+        Function::Tanh => scalar_arguments(args),
+        Function::ErrorType => not_implemented(args),
+        Function::Isblank => not_implemented(args),
+        Function::Iserr => not_implemented(args),
+        Function::Iserror => not_implemented(args),
+        Function::Iseven => not_implemented(args),
+        Function::Isformula => not_implemented(args),
+        Function::Islogical => not_implemented(args),
+        Function::Isna => not_implemented(args),
+        Function::Isnontext => not_implemented(args),
+        Function::Isnumber => not_implemented(args),
+        Function::Isodd => not_implemented(args),
+        Function::Isref => not_implemented(args),
+        Function::Istext => not_implemented(args),
+        Function::Na => StaticResult::Scalar,
+        Function::Sheet => StaticResult::Scalar,
+        Function::Type => not_implemented(args),
+        Function::Hlookup => not_implemented(args),
+        Function::Index => static_analysis_index(args),
+        Function::Indirect => static_analysis_indirect(args),
+        Function::Lookup => not_implemented(args),
+        Function::Match => not_implemented(args),
+        Function::Offset => static_analysis_offset(args),
+        // FIXME: Row could return an array
+        Function::Row => StaticResult::Scalar,
+        Function::Rows => not_implemented(args),
+        Function::Vlookup => not_implemented(args),
+        Function::Xlookup => not_implemented(args),
+        Function::Concat => not_implemented(args),
+        Function::Concatenate => not_implemented(args),
+        Function::Exact => not_implemented(args),
+        Function::Find => not_implemented(args),
+        Function::Left => not_implemented(args),
+        Function::Len => not_implemented(args),
+        Function::Lower => not_implemented(args),
+        Function::Mid => not_implemented(args),
+        Function::Rept => not_implemented(args),
+        Function::Right => not_implemented(args),
+        Function::Search => not_implemented(args),
+        Function::Substitute => not_implemented(args),
+        Function::T => not_implemented(args),
+        Function::Text => not_implemented(args),
+        Function::Textafter => not_implemented(args),
+        Function::Textbefore => not_implemented(args),
+        Function::Textjoin => not_implemented(args),
+        Function::Trim => not_implemented(args),
+        Function::Unicode => not_implemented(args),
+        Function::Upper => not_implemented(args),
+        Function::Value => not_implemented(args),
+        Function::Valuetotext => not_implemented(args),
+        Function::Average => not_implemented(args),
+        Function::Averagea => not_implemented(args),
+        Function::Averageif => not_implemented(args),
+        Function::Averageifs => not_implemented(args),
+        Function::Count => not_implemented(args),
+        Function::Counta => not_implemented(args),
+        Function::Countblank => not_implemented(args),
+        Function::Countif => not_implemented(args),
+        Function::Countifs => not_implemented(args),
+        Function::Maxifs => not_implemented(args),
+        Function::Minifs => not_implemented(args),
+        Function::Date => not_implemented(args),
+        Function::Day => not_implemented(args),
+        Function::Edate => not_implemented(args),
+        Function::Month => not_implemented(args),
+        Function::Now => not_implemented(args),
+        Function::Today => not_implemented(args),
+        Function::Year => not_implemented(args),
+        Function::Cumipmt => not_implemented(args),
+        Function::Cumprinc => not_implemented(args),
+        Function::Db => not_implemented(args),
+        Function::Ddb => not_implemented(args),
+        Function::Dollarde => not_implemented(args),
+        Function::Dollarfr => not_implemented(args),
+        Function::Effect => not_implemented(args),
+        Function::Fv => not_implemented(args),
+        Function::Ipmt => not_implemented(args),
+        Function::Irr => not_implemented(args),
+        Function::Ispmt => not_implemented(args),
+        Function::Mirr => not_implemented(args),
+        Function::Nominal => not_implemented(args),
+        Function::Nper => not_implemented(args),
+        Function::Npv => not_implemented(args),
+        Function::Pduration => not_implemented(args),
+        Function::Pmt => not_implemented(args),
+        Function::Ppmt => not_implemented(args),
+        Function::Pv => not_implemented(args),
+        Function::Rate => not_implemented(args),
+        Function::Rri => not_implemented(args),
+        Function::Sln => not_implemented(args),
+        Function::Syd => not_implemented(args),
+        Function::Tbilleq => not_implemented(args),
+        Function::Tbillprice => not_implemented(args),
+        Function::Tbillyield => not_implemented(args),
+        Function::Xirr => not_implemented(args),
+        Function::Xnpv => not_implemented(args),
+        Function::Besseli => scalar_arguments(args),
+        Function::Besselj => scalar_arguments(args),
+        Function::Besselk => scalar_arguments(args),
+        Function::Bessely => scalar_arguments(args),
+        Function::Erf => scalar_arguments(args),
+        Function::Erfc => scalar_arguments(args),
+        Function::ErfcPrecise => scalar_arguments(args),
+        Function::ErfPrecise => scalar_arguments(args),
+        Function::Bin2dec => scalar_arguments(args),
+        Function::Bin2hex => scalar_arguments(args),
+        Function::Bin2oct => scalar_arguments(args),
+        Function::Dec2Bin => scalar_arguments(args),
+        Function::Dec2hex => scalar_arguments(args),
+        Function::Dec2oct => scalar_arguments(args),
+        Function::Hex2bin => scalar_arguments(args),
+        Function::Hex2dec => scalar_arguments(args),
+        Function::Hex2oct => scalar_arguments(args),
+        Function::Oct2bin => scalar_arguments(args),
+        Function::Oct2dec => scalar_arguments(args),
+        Function::Oct2hex => scalar_arguments(args),
+        Function::Bitand => scalar_arguments(args),
+        Function::Bitlshift => scalar_arguments(args),
+        Function::Bitor => scalar_arguments(args),
+        Function::Bitrshift => scalar_arguments(args),
+        Function::Bitxor => scalar_arguments(args),
+        Function::Complex => scalar_arguments(args),
+        Function::Imabs => scalar_arguments(args),
+        Function::Imaginary => scalar_arguments(args),
+        Function::Imargument => scalar_arguments(args),
+        Function::Imconjugate => scalar_arguments(args),
+        Function::Imcos => scalar_arguments(args),
+        Function::Imcosh => scalar_arguments(args),
+        Function::Imcot => scalar_arguments(args),
+        Function::Imcsc => scalar_arguments(args),
+        Function::Imcsch => scalar_arguments(args),
+        Function::Imdiv => scalar_arguments(args),
+        Function::Imexp => scalar_arguments(args),
+        Function::Imln => scalar_arguments(args),
+        Function::Imlog10 => scalar_arguments(args),
+        Function::Imlog2 => scalar_arguments(args),
+        Function::Impower => scalar_arguments(args),
+        Function::Improduct => scalar_arguments(args),
+        Function::Imreal => scalar_arguments(args),
+        Function::Imsec => scalar_arguments(args),
+        Function::Imsech => scalar_arguments(args),
+        Function::Imsin => scalar_arguments(args),
+        Function::Imsinh => scalar_arguments(args),
+        Function::Imsqrt => scalar_arguments(args),
+        Function::Imsub => scalar_arguments(args),
+        Function::Imsum => scalar_arguments(args),
+        Function::Imtan => scalar_arguments(args),
+        Function::Convert => not_implemented(args),
+        Function::Delta => not_implemented(args),
+        Function::Gestep => not_implemented(args),
+        Function::Subtotal => not_implemented(args),
+        Function::Rand => not_implemented(args),
+        Function::Randbetween => scalar_arguments(args),
+        Function::Eomonth => scalar_arguments(args),
+        Function::Formulatext => not_implemented(args),
+        Function::Geomean => not_implemented(args),
+    }
+}
diff --git a/base/src/expressions/parser/stringify.rs b/base/src/expressions/parser/stringify.rs
index 00f2d5f..0b87865 100644
--- a/base/src/expressions/parser/stringify.rs
+++ b/base/src/expressions/parser/stringify.rs
@@ -1,5 +1,6 @@
 use super::{super::utils::quote_name, Node, Reference};
 use crate::constants::{LAST_COLUMN, LAST_ROW};
+use crate::expressions::parser::static_analysis::add_implicit_intersection;
 use crate::expressions::token::OpUnary;
 use crate::{expressions::types::CellReferenceRC, number_format::to_excel_precision_str};
 
@@ -34,10 +35,21 @@ pub enum DisplaceData {
     None,
 }
 
+/// This is the internal mode in IronCalc
 pub fn to_rc_format(node: &Node) -> String {
     stringify(node, None, &DisplaceData::None, false)
 }
 
+/// This is the mode used to display the formula in the UI
+pub fn to_string(node: &Node, context: &CellReferenceRC) -> String {
+    stringify(node, Some(context), &DisplaceData::None, false)
+}
+
+/// This is the mode used to export the formula to Excel
+pub fn to_excel_string(node: &Node, context: &CellReferenceRC) -> String {
+    stringify(node, Some(context), &DisplaceData::None, true)
+}
+
 pub fn to_string_displaced(
     node: &Node,
     context: &CellReferenceRC,
@@ -46,18 +58,10 @@ pub fn to_string_displaced(
     stringify(node, Some(context), displace_data, false)
 }
 
-pub fn to_string(node: &Node, context: &CellReferenceRC) -> String {
-    stringify(node, Some(context), &DisplaceData::None, false)
-}
-
-pub fn to_excel_string(node: &Node, context: &CellReferenceRC) -> String {
-    stringify(node, Some(context), &DisplaceData::None, true)
-}
-
 /// Converts a local reference to a string applying some displacement if needed.
 /// It uses A1 style if context is not None. If context is None it uses R1C1 style
 /// If full_row is true then the row details will be omitted in the A1 case
-/// If full_colum is true then column details will be omitted.
+/// If full_column is true then column details will be omitted.
 pub(crate) fn stringify_reference(
     context: Option<&CellReferenceRC>,
     displace_data: &DisplaceData,
@@ -235,7 +239,7 @@ fn format_function(
     args: &Vec,
     context: Option<&CellReferenceRC>,
     displace_data: &DisplaceData,
-    use_original_name: bool,
+    export_to_excel: bool,
 ) -> String {
     let mut first = true;
     let mut arguments = "".to_string();
@@ -244,11 +248,11 @@ fn format_function(
             arguments = format!(
                 "{},{}",
                 arguments,
-                stringify(el, context, displace_data, use_original_name)
+                stringify(el, context, displace_data, export_to_excel)
             );
         } else {
             first = false;
-            arguments = stringify(el, context, displace_data, use_original_name);
+            arguments = stringify(el, context, displace_data, export_to_excel);
         }
     }
     format!("{}({})", name, arguments)
@@ -258,7 +262,7 @@ fn stringify(
     node: &Node,
     context: Option<&CellReferenceRC>,
     displace_data: &DisplaceData,
-    use_original_name: bool,
+    export_to_excel: bool,
 ) -> String {
     use self::Node::*;
     match node {
@@ -407,52 +411,52 @@ fn stringify(
         }
         OpRangeKind { left, right } => format!(
             "{}:{}",
-            stringify(left, context, displace_data, use_original_name),
-            stringify(right, context, displace_data, use_original_name)
+            stringify(left, context, displace_data, export_to_excel),
+            stringify(right, context, displace_data, export_to_excel)
         ),
         OpConcatenateKind { left, right } => format!(
             "{}&{}",
-            stringify(left, context, displace_data, use_original_name),
-            stringify(right, context, displace_data, use_original_name)
+            stringify(left, context, displace_data, export_to_excel),
+            stringify(right, context, displace_data, export_to_excel)
         ),
         CompareKind { kind, left, right } => format!(
             "{}{}{}",
-            stringify(left, context, displace_data, use_original_name),
+            stringify(left, context, displace_data, export_to_excel),
             kind,
-            stringify(right, context, displace_data, use_original_name)
+            stringify(right, context, displace_data, export_to_excel)
         ),
         OpSumKind { kind, left, right } => format!(
             "{}{}{}",
-            stringify(left, context, displace_data, use_original_name),
+            stringify(left, context, displace_data, export_to_excel),
             kind,
-            stringify(right, context, displace_data, use_original_name)
+            stringify(right, context, displace_data, export_to_excel)
         ),
         OpProductKind { kind, left, right } => {
             let x = match **left {
                 OpSumKind { .. } => format!(
                     "({})",
-                    stringify(left, context, displace_data, use_original_name)
+                    stringify(left, context, displace_data, export_to_excel)
                 ),
                 CompareKind { .. } => format!(
                     "({})",
-                    stringify(left, context, displace_data, use_original_name)
+                    stringify(left, context, displace_data, export_to_excel)
                 ),
-                _ => stringify(left, context, displace_data, use_original_name),
+                _ => stringify(left, context, displace_data, export_to_excel),
             };
             let y = match **right {
                 OpSumKind { .. } => format!(
                     "({})",
-                    stringify(right, context, displace_data, use_original_name)
+                    stringify(right, context, displace_data, export_to_excel)
                 ),
                 CompareKind { .. } => format!(
                     "({})",
-                    stringify(right, context, displace_data, use_original_name)
+                    stringify(right, context, displace_data, export_to_excel)
                 ),
                 OpProductKind { .. } => format!(
                     "({})",
-                    stringify(right, context, displace_data, use_original_name)
+                    stringify(right, context, displace_data, export_to_excel)
                 ),
-                _ => stringify(right, context, displace_data, use_original_name),
+                _ => stringify(right, context, displace_data, export_to_excel),
             };
             format!("{}{}{}", x, kind, y)
         }
@@ -467,9 +471,7 @@ fn stringify(
                 | DefinedNameKind(_)
                 | TableNameKind(_)
                 | WrongVariableKind(_)
-                | WrongRangeKind { .. } => {
-                    stringify(left, context, displace_data, use_original_name)
-                }
+                | WrongRangeKind { .. } => stringify(left, context, displace_data, export_to_excel),
                 OpRangeKind { .. }
                 | OpConcatenateKind { .. }
                 | OpProductKind { .. }
@@ -482,9 +484,10 @@ fn stringify(
                 | ParseErrorKind { .. }
                 | OpSumKind { .. }
                 | CompareKind { .. }
+                | ImplicitIntersection { .. }
                 | EmptyArgKind => format!(
                     "({})",
-                    stringify(left, context, displace_data, use_original_name)
+                    stringify(left, context, displace_data, export_to_excel)
                 ),
             };
             let y = match **right {
@@ -498,7 +501,7 @@ fn stringify(
                 | TableNameKind(_)
                 | WrongVariableKind(_)
                 | WrongRangeKind { .. } => {
-                    stringify(right, context, displace_data, use_original_name)
+                    stringify(right, context, displace_data, export_to_excel)
                 }
                 OpRangeKind { .. }
                 | OpConcatenateKind { .. }
@@ -512,23 +515,24 @@ fn stringify(
                 | ParseErrorKind { .. }
                 | OpSumKind { .. }
                 | CompareKind { .. }
+                | ImplicitIntersection { .. }
                 | EmptyArgKind => format!(
                     "({})",
-                    stringify(right, context, displace_data, use_original_name)
+                    stringify(right, context, displace_data, export_to_excel)
                 ),
             };
             format!("{}^{}", x, y)
         }
         InvalidFunctionKind { name, args } => {
-            format_function(name, args, context, displace_data, use_original_name)
+            format_function(name, args, context, displace_data, export_to_excel)
         }
         FunctionKind { kind, args } => {
-            let name = if use_original_name {
+            let name = if export_to_excel {
                 kind.to_xlsx_string()
             } else {
                 kind.to_string()
             };
-            format_function(&name, args, context, displace_data, use_original_name)
+            format_function(&name, args, context, displace_data, export_to_excel)
         }
         ArrayKind(args) => {
             let mut first = true;
@@ -538,29 +542,29 @@ fn stringify(
                     arguments = format!(
                         "{},{}",
                         arguments,
-                        stringify(el, context, displace_data, use_original_name)
+                        stringify(el, context, displace_data, export_to_excel)
                     );
                 } else {
                     first = false;
-                    arguments = stringify(el, context, displace_data, use_original_name);
+                    arguments = stringify(el, context, displace_data, export_to_excel);
                 }
             }
             format!("{{{}}}", arguments)
         }
         TableNameKind(value) => value.to_string(),
-        DefinedNameKind((name, _)) => name.to_string(),
+        DefinedNameKind((name, ..)) => name.to_string(),
         WrongVariableKind(name) => name.to_string(),
         UnaryKind { kind, right } => match kind {
             OpUnary::Minus => {
                 format!(
                     "-{}",
-                    stringify(right, context, displace_data, use_original_name)
+                    stringify(right, context, displace_data, export_to_excel)
                 )
             }
             OpUnary::Percentage => {
                 format!(
                     "{}%",
-                    stringify(right, context, displace_data, use_original_name)
+                    stringify(right, context, displace_data, export_to_excel)
                 )
             }
         },
@@ -571,6 +575,29 @@ fn stringify(
             message: _,
         } => formula.to_string(),
         EmptyArgKind => "".to_string(),
+        ImplicitIntersection {
+            automatic: _,
+            child,
+        } => {
+            if export_to_excel {
+                // We need to check wether the II can be automatic or not
+                let mut new_node = child.as_ref().clone();
+
+                add_implicit_intersection(&mut new_node, true);
+                if matches!(&new_node, Node::ImplicitIntersection { .. }) {
+                    return stringify(child, context, displace_data, export_to_excel);
+                }
+
+                return format!(
+                    "_xlfn.SINGLE({})",
+                    stringify(child, context, displace_data, export_to_excel)
+                );
+            }
+            format!(
+                "@{}",
+                stringify(child, context, displace_data, export_to_excel)
+            )
+        }
     }
 }
 
@@ -658,6 +685,12 @@ pub(crate) fn rename_sheet_in_node(node: &mut Node, sheet_index: u32, new_name:
         Node::UnaryKind { kind: _, right } => {
             rename_sheet_in_node(right, sheet_index, new_name);
         }
+        Node::ImplicitIntersection {
+            automatic: _,
+            child,
+        } => {
+            rename_sheet_in_node(child, sheet_index, new_name);
+        }
 
         // Do nothing
         Node::BooleanKind(_) => {}
@@ -681,7 +714,7 @@ pub(crate) fn rename_defined_name_in_node(
 ) {
     match node {
         // Rename
-        Node::DefinedNameKind((n, s)) => {
+        Node::DefinedNameKind((n, s, _)) => {
             if name.to_lowercase() == n.to_lowercase() && *s == scope {
                 *n = new_name.to_string();
             }
@@ -736,6 +769,12 @@ pub(crate) fn rename_defined_name_in_node(
         Node::UnaryKind { kind: _, right } => {
             rename_defined_name_in_node(right, name, scope, new_name);
         }
+        Node::ImplicitIntersection {
+            automatic: _,
+            child,
+        } => {
+            rename_defined_name_in_node(child, name, scope, new_name);
+        }
 
         // Do nothing
         Node::BooleanKind(_) => {}
diff --git a/base/src/expressions/parser/tests/mod.rs b/base/src/expressions/parser/tests/mod.rs
index 7661338..009514a 100644
--- a/base/src/expressions/parser/tests/mod.rs
+++ b/base/src/expressions/parser/tests/mod.rs
@@ -1,4 +1,6 @@
+mod test_add_implicit_intersection;
 mod test_general;
+mod test_implicit_intersection;
 mod test_issue_155;
 mod test_move_formula;
 mod test_ranges;
diff --git a/base/src/expressions/parser/tests/test_add_implicit_intersection.rs b/base/src/expressions/parser/tests/test_add_implicit_intersection.rs
new file mode 100644
index 0000000..53abbe5
--- /dev/null
+++ b/base/src/expressions/parser/tests/test_add_implicit_intersection.rs
@@ -0,0 +1,80 @@
+use std::collections::HashMap;
+
+use crate::expressions::{
+    parser::{
+        stringify::{to_excel_string, to_string},
+        Parser,
+    },
+    types::CellReferenceRC,
+};
+
+use crate::expressions::parser::static_analysis::add_implicit_intersection;
+
+#[test]
+fn simple_test() {
+    let worksheets = vec!["Sheet1".to_string()];
+    let mut parser = Parser::new(worksheets, vec![], HashMap::new());
+
+    // Reference cell is Sheet1!A1
+    let cell_reference = CellReferenceRC {
+        sheet: "Sheet1".to_string(),
+        row: 1,
+        column: 1,
+    };
+    let cases = vec![
+        ("A1:A10*SUM(A1:A10)", "@A1:A10*SUM(A1:A10)"),
+        ("A1:A10", "@A1:A10"),
+        // Math and trigonometry functions
+        ("SUM(A1:A10)", "SUM(A1:A10)"),
+        ("SIN(A1:A10)", "SIN(@A1:A10)"),
+        ("COS(A1:A10)", "COS(@A1:A10)"),
+        ("TAN(A1:A10)", "TAN(@A1:A10)"),
+        ("ASIN(A1:A10)", "ASIN(@A1:A10)"),
+        ("ACOS(A1:A10)", "ACOS(@A1:A10)"),
+        ("ATAN(A1:A10)", "ATAN(@A1:A10)"),
+        ("SINH(A1:A10)", "SINH(@A1:A10)"),
+        ("COSH(A1:A10)", "COSH(@A1:A10)"),
+        ("TANH(A1:A10)", "TANH(@A1:A10)"),
+        ("ASINH(A1:A10)", "ASINH(@A1:A10)"),
+        ("ACOSH(A1:A10)", "ACOSH(@A1:A10)"),
+        ("ATANH(A1:A10)", "ATANH(@A1:A10)"),
+        ("ATAN2(A1:A10,B1:B10)", "ATAN2(@A1:A10,@B1:B10)"),
+        ("ATAN2(A1:A10,A1)", "ATAN2(@A1:A10,A1)"),
+        ("SQRT(A1:A10)", "SQRT(@A1:A10)"),
+        ("SQRTPI(A1:A10)", "SQRTPI(@A1:A10)"),
+        ("POWER(A1:A10,A1)", "POWER(@A1:A10,A1)"),
+        ("POWER(A1:A10,B1:B10)", "POWER(@A1:A10,@B1:B10)"),
+        ("MAX(A1:A10)", "MAX(A1:A10)"),
+        ("MIN(A1:A10)", "MIN(A1:A10)"),
+        ("ABS(A1:A10)", "ABS(@A1:A10)"),
+        ("FALSE()", "FALSE()"),
+        ("TRUE()", "TRUE()"),
+        // Defined names
+        ("BADNMAE", "@BADNMAE"),
+        // Logical
+        ("AND(A1:A10)", "AND(A1:A10)"),
+        ("OR(A1:A10)", "OR(A1:A10)"),
+        ("NOT(A1:A10)", "NOT(@A1:A10)"),
+        ("IF(A1:A10,B1:B10,C1:C10)", "IF(@A1:A10,@B1:B10,@C1:C10)"),
+        // Information
+        // ("ISBLANK(A1:A10)", "ISBLANK(A1:A10)"),
+        // ("ISERR(A1:A10)", "ISERR(A1:A10)"),
+        // ("ISERROR(A1:A10)", "ISERROR(A1:A10)"),
+        // ("ISEVEN(A1:A10)", "ISEVEN(A1:A10)"),
+        // ("ISLOGICAL(A1:A10)", "ISLOGICAL(A1:A10)"),
+        // ("ISNA(A1:A10)", "ISNA(A1:A10)"),
+        // ("ISNONTEXT(A1:A10)", "ISNONTEXT(A1:A10)"),
+        // ("ISNUMBER(A1:A10)", "ISNUMBER(A1:A10)"),
+        // ("ISODD(A1:A10)", "ISODD(A1:A10)"),
+        // ("ISREF(A1:A10)", "ISREF(A1:A10)"),
+        // ("ISTEXT(A1:A10)", "ISTEXT(A1:A10)"),
+    ];
+    for (formula, expected) in cases {
+        let mut t = parser.parse(formula, &cell_reference);
+        add_implicit_intersection(&mut t, true);
+        let r = to_string(&t, &cell_reference);
+        assert_eq!(r, expected);
+        let excel_formula = to_excel_string(&t, &cell_reference);
+        assert_eq!(excel_formula, formula);
+    }
+}
diff --git a/base/src/expressions/parser/tests/test_implicit_intersection.rs b/base/src/expressions/parser/tests/test_implicit_intersection.rs
new file mode 100644
index 0000000..ab555bb
--- /dev/null
+++ b/base/src/expressions/parser/tests/test_implicit_intersection.rs
@@ -0,0 +1,75 @@
+#![allow(clippy::panic)]
+
+use crate::expressions::parser::{Node, Parser};
+use crate::expressions::types::CellReferenceRC;
+use std::collections::HashMap;
+
+#[test]
+fn simple() {
+    let worksheets = vec!["Sheet1".to_string()];
+    let mut parser = Parser::new(worksheets, vec![], HashMap::new());
+
+    // Reference cell is Sheet1!B3
+    let cell_reference = CellReferenceRC {
+        sheet: "Sheet1".to_string(),
+        row: 3,
+        column: 2,
+    };
+    let t = parser.parse("@A1:A10", &cell_reference);
+    let child = Node::RangeKind {
+        sheet_name: None,
+        sheet_index: 0,
+        absolute_row1: false,
+        absolute_column1: false,
+        row1: -2,
+        column1: -1,
+        absolute_row2: false,
+        absolute_column2: false,
+        row2: 7,
+        column2: -1,
+    };
+    assert_eq!(
+        t,
+        Node::ImplicitIntersection {
+            automatic: false,
+            child: Box::new(child)
+        }
+    )
+}
+
+#[test]
+fn simple_add() {
+    let worksheets = vec!["Sheet1".to_string()];
+    let mut parser = Parser::new(worksheets, vec![], HashMap::new());
+
+    // Reference cell is Sheet1!B3
+    let cell_reference = CellReferenceRC {
+        sheet: "Sheet1".to_string(),
+        row: 3,
+        column: 2,
+    };
+    let t = parser.parse("@A1:A10+12", &cell_reference);
+    let child = Node::RangeKind {
+        sheet_name: None,
+        sheet_index: 0,
+        absolute_row1: false,
+        absolute_column1: false,
+        row1: -2,
+        column1: -1,
+        absolute_row2: false,
+        absolute_column2: false,
+        row2: 7,
+        column2: -1,
+    };
+    assert_eq!(
+        t,
+        Node::OpSumKind {
+            kind: crate::expressions::token::OpSum::Add,
+            left: Box::new(Node::ImplicitIntersection {
+                automatic: false,
+                child: Box::new(child)
+            }),
+            right: Box::new(Node::NumberKind(12.0))
+        }
+    )
+}
diff --git a/base/src/expressions/parser/tests/test_move_formula.rs b/base/src/expressions/parser/tests/test_move_formula.rs
index 37fde78..4e36c72 100644
--- a/base/src/expressions/parser/tests/test_move_formula.rs
+++ b/base/src/expressions/parser/tests/test_move_formula.rs
@@ -387,7 +387,7 @@ fn test_move_formula_misc() {
         width: 4,
         height: 5,
     };
-    let node = parser.parse("X9^C2-F4*H2", context);
+    let node = parser.parse("X9^C2-F4*H2+SUM(F2:H4)+SUM(C2:F6)", context);
     let t = move_formula(
         &node,
         &MoveContext {
@@ -400,7 +400,7 @@ fn test_move_formula_misc() {
             column_delta: 10,
         },
     );
-    assert_eq!(t, "X9^M12-P14*H2");
+    assert_eq!(t, "X9^M12-P14*H2+SUM(F2:H4)+SUM(M12:P16)");
 
     let node = parser.parse("F5*(-D5)*SUM(A1, X9, $D$5)", context);
     let t = move_formula(
@@ -475,3 +475,77 @@ fn test_move_formula_another_sheet() {
         "Sheet1!AB31*SUM(Sheet1!JJ3:JJ4)+SUM(Sheet2!C2:F6)*SUM(M12:P16)"
     );
 }
+
+#[test]
+fn move_formula_implicit_intersetion() {
+    // context is E4
+    let row = 4;
+    let column = 5;
+    let context = &CellReferenceRC {
+        sheet: "Sheet1".to_string(),
+        row,
+        column,
+    };
+    let worksheets = vec!["Sheet1".to_string()];
+    let mut parser = Parser::new(worksheets, vec![], HashMap::new());
+
+    // Area is C2:F6
+    let area = &Area {
+        sheet: 0,
+        row: 2,
+        column: 3,
+        width: 4,
+        height: 5,
+    };
+    let node = parser.parse("SUM(@F2:H4)+SUM(@C2:F6)", context);
+    let t = move_formula(
+        &node,
+        &MoveContext {
+            source_sheet_name: "Sheet1",
+            row,
+            column,
+            area,
+            target_sheet_name: "Sheet1",
+            row_delta: 10,
+            column_delta: 10,
+        },
+    );
+    assert_eq!(t, "SUM(@F2:H4)+SUM(@M12:P16)");
+}
+
+#[test]
+fn move_formula_implicit_intersetion_with_ranges() {
+    // context is E4
+    let row = 4;
+    let column = 5;
+    let context = &CellReferenceRC {
+        sheet: "Sheet1".to_string(),
+        row,
+        column,
+    };
+    let worksheets = vec!["Sheet1".to_string()];
+    let mut parser = Parser::new(worksheets, vec![], HashMap::new());
+
+    // Area is C2:F6
+    let area = &Area {
+        sheet: 0,
+        row: 2,
+        column: 3,
+        width: 4,
+        height: 5,
+    };
+    let node = parser.parse("SUM(@F2:H4)+SUM(@C2:F6)+SUM(@A1, @X9, @$D$5)", context);
+    let t = move_formula(
+        &node,
+        &MoveContext {
+            source_sheet_name: "Sheet1",
+            row,
+            column,
+            area,
+            target_sheet_name: "Sheet1",
+            row_delta: 10,
+            column_delta: 10,
+        },
+    );
+    assert_eq!(t, "SUM(@F2:H4)+SUM(@M12:P16)+SUM(@A1,@X9,@$N$15)");
+}
diff --git a/base/src/expressions/parser/tests/test_tables.rs b/base/src/expressions/parser/tests/test_tables.rs
index 3addf98..eced95d 100644
--- a/base/src/expressions/parser/tests/test_tables.rs
+++ b/base/src/expressions/parser/tests/test_tables.rs
@@ -3,11 +3,10 @@
 use std::collections::HashMap;
 
 use crate::expressions::parser::stringify::to_string;
-use crate::expressions::utils::{number_to_column, parse_reference_a1};
-use crate::types::{Table, TableColumn, TableStyleInfo};
-
 use crate::expressions::parser::Parser;
 use crate::expressions::types::CellReferenceRC;
+use crate::expressions::utils::{number_to_column, parse_reference_a1};
+use crate::types::{Table, TableColumn, TableStyleInfo};
 
 fn create_test_table(
     table_name: &str,
diff --git a/base/src/expressions/parser/walk.rs b/base/src/expressions/parser/walk.rs
deleted file mode 100644
index 746e379..0000000
--- a/base/src/expressions/parser/walk.rs
+++ /dev/null
@@ -1,278 +0,0 @@
-use super::{move_formula::ref_is_in_area, Node};
-
-use crate::expressions::types::{Area, CellReferenceIndex};
-
-pub(crate) fn forward_references(
-    node: &mut Node,
-    context: &CellReferenceIndex,
-    source_area: &Area,
-    target_sheet: u32,
-    target_sheet_name: &str,
-    target_row: i32,
-    target_column: i32,
-) {
-    match node {
-        Node::ReferenceKind {
-            sheet_name,
-            sheet_index: reference_sheet,
-            absolute_row,
-            absolute_column,
-            row: reference_row,
-            column: reference_column,
-        } => {
-            let reference_row_absolute = if *absolute_row {
-                *reference_row
-            } else {
-                *reference_row + context.row
-            };
-            let reference_column_absolute = if *absolute_column {
-                *reference_column
-            } else {
-                *reference_column + context.column
-            };
-            if ref_is_in_area(
-                *reference_sheet,
-                reference_row_absolute,
-                reference_column_absolute,
-                source_area,
-            ) {
-                if *reference_sheet != target_sheet {
-                    *sheet_name = Some(target_sheet_name.to_string());
-                    *reference_sheet = target_sheet;
-                }
-                *reference_row = target_row + *reference_row - source_area.row;
-                *reference_column = target_column + *reference_column - source_area.column;
-            }
-        }
-        Node::RangeKind {
-            sheet_name,
-            sheet_index,
-            absolute_row1,
-            absolute_column1,
-            row1,
-            column1,
-            absolute_row2,
-            absolute_column2,
-            row2,
-            column2,
-        } => {
-            let reference_row1 = if *absolute_row1 {
-                *row1
-            } else {
-                *row1 + context.row
-            };
-            let reference_column1 = if *absolute_column1 {
-                *column1
-            } else {
-                *column1 + context.column
-            };
-
-            let reference_row2 = if *absolute_row2 {
-                *row2
-            } else {
-                *row2 + context.row
-            };
-            let reference_column2 = if *absolute_column2 {
-                *column2
-            } else {
-                *column2 + context.column
-            };
-            if ref_is_in_area(*sheet_index, reference_row1, reference_column1, source_area)
-                && ref_is_in_area(*sheet_index, reference_row2, reference_column2, source_area)
-            {
-                if *sheet_index != target_sheet {
-                    *sheet_index = target_sheet;
-                    *sheet_name = Some(target_sheet_name.to_string());
-                }
-                *row1 = target_row + *row1 - source_area.row;
-                *column1 = target_column + *column1 - source_area.column;
-                *row2 = target_row + *row2 - source_area.row;
-                *column2 = target_column + *column2 - source_area.column;
-            }
-        }
-        // Recurse
-        Node::OpRangeKind { left, right } => {
-            forward_references(
-                left,
-                context,
-                source_area,
-                target_sheet,
-                target_sheet_name,
-                target_row,
-                target_column,
-            );
-            forward_references(
-                right,
-                context,
-                source_area,
-                target_sheet,
-                target_sheet_name,
-                target_row,
-                target_column,
-            );
-        }
-        Node::OpConcatenateKind { left, right } => {
-            forward_references(
-                left,
-                context,
-                source_area,
-                target_sheet,
-                target_sheet_name,
-                target_row,
-                target_column,
-            );
-            forward_references(
-                right,
-                context,
-                source_area,
-                target_sheet,
-                target_sheet_name,
-                target_row,
-                target_column,
-            );
-        }
-        Node::OpSumKind {
-            kind: _,
-            left,
-            right,
-        } => {
-            forward_references(
-                left,
-                context,
-                source_area,
-                target_sheet,
-                target_sheet_name,
-                target_row,
-                target_column,
-            );
-            forward_references(
-                right,
-                context,
-                source_area,
-                target_sheet,
-                target_sheet_name,
-                target_row,
-                target_column,
-            );
-        }
-        Node::OpProductKind {
-            kind: _,
-            left,
-            right,
-        } => {
-            forward_references(
-                left,
-                context,
-                source_area,
-                target_sheet,
-                target_sheet_name,
-                target_row,
-                target_column,
-            );
-            forward_references(
-                right,
-                context,
-                source_area,
-                target_sheet,
-                target_sheet_name,
-                target_row,
-                target_column,
-            );
-        }
-        Node::OpPowerKind { left, right } => {
-            forward_references(
-                left,
-                context,
-                source_area,
-                target_sheet,
-                target_sheet_name,
-                target_row,
-                target_column,
-            );
-            forward_references(
-                right,
-                context,
-                source_area,
-                target_sheet,
-                target_sheet_name,
-                target_row,
-                target_column,
-            );
-        }
-        Node::FunctionKind { kind: _, args } => {
-            for arg in args {
-                forward_references(
-                    arg,
-                    context,
-                    source_area,
-                    target_sheet,
-                    target_sheet_name,
-                    target_row,
-                    target_column,
-                );
-            }
-        }
-        Node::InvalidFunctionKind { name: _, args } => {
-            for arg in args {
-                forward_references(
-                    arg,
-                    context,
-                    source_area,
-                    target_sheet,
-                    target_sheet_name,
-                    target_row,
-                    target_column,
-                );
-            }
-        }
-        Node::CompareKind {
-            kind: _,
-            left,
-            right,
-        } => {
-            forward_references(
-                left,
-                context,
-                source_area,
-                target_sheet,
-                target_sheet_name,
-                target_row,
-                target_column,
-            );
-            forward_references(
-                right,
-                context,
-                source_area,
-                target_sheet,
-                target_sheet_name,
-                target_row,
-                target_column,
-            );
-        }
-        Node::UnaryKind { kind: _, right } => {
-            forward_references(
-                right,
-                context,
-                source_area,
-                target_sheet,
-                target_sheet_name,
-                target_row,
-                target_column,
-            );
-        }
-        // TODO: Not implemented
-        Node::ArrayKind(_) => {}
-        // Do nothing. Note: we could do a blanket _ => {}
-        Node::DefinedNameKind(_) => {}
-        Node::TableNameKind(_) => {}
-        Node::WrongVariableKind(_) => {}
-        Node::ErrorKind(_) => {}
-        Node::ParseErrorKind { .. } => {}
-        Node::EmptyArgKind => {}
-        Node::BooleanKind(_) => {}
-        Node::NumberKind(_) => {}
-        Node::StringKind(_) => {}
-        Node::WrongReferenceKind { .. } => {}
-        Node::WrongRangeKind { .. } => {}
-    }
-}
diff --git a/base/src/expressions/token.rs b/base/src/expressions/token.rs
index 877ddc6..cd98144 100644
--- a/base/src/expressions/token.rs
+++ b/base/src/expressions/token.rs
@@ -240,6 +240,7 @@ pub enum TokenType {
     Bang,               // !
     Percent,            // %
     And,                // &
+    At,                 // @
     Reference {
         sheet: Option,
         row: i32,
diff --git a/base/src/functions/information.rs b/base/src/functions/information.rs
index 44d847c..5901539 100644
--- a/base/src/functions/information.rs
+++ b/base/src/functions/information.rs
@@ -249,7 +249,7 @@ impl Model {
         // The arg could be a defined name or a table
         // let  = &args[0];
         match &args[0] {
-            Node::DefinedNameKind((name, scope)) => {
+            Node::DefinedNameKind((name, scope, _)) => {
                 // Let's see if it is a defined name
                 if let Some(defined_name) = self
                     .parsed_defined_names
diff --git a/base/src/functions/lookup_and_reference.rs b/base/src/functions/lookup_and_reference.rs
index 04a9e95..f8f9735 100644
--- a/base/src/functions/lookup_and_reference.rs
+++ b/base/src/functions/lookup_and_reference.rs
@@ -855,7 +855,7 @@ impl Model {
             if left.row != right.row || left.column != right.column {
                 // FIXME: Implicit intersection or dynamic arrays
                 return CalcResult::Error {
-                    error: Error::ERROR,
+                    error: Error::NIMPL,
                     origin: cell,
                     message: "argument must be a reference to a single cell".to_string(),
                 };
diff --git a/base/src/lib.rs b/base/src/lib.rs
index 840a969..ec25fc9 100644
--- a/base/src/lib.rs
+++ b/base/src/lib.rs
@@ -41,7 +41,6 @@ pub mod worksheet;
 mod actions;
 mod cast;
 mod constants;
-mod diffs;
 mod functions;
 mod implicit_intersection;
 mod model;
diff --git a/base/src/model.rs b/base/src/model.rs
index f3ea71c..8364730 100644
--- a/base/src/model.rs
+++ b/base/src/model.rs
@@ -207,6 +207,17 @@ impl Model {
                     },
                 }
             }
+            Node::ImplicitIntersection {
+                automatic: _,
+                child,
+            } => match self.evaluate_node_with_reference(child, cell) {
+                CalcResult::Range { left, right } => CalcResult::Range { left, right },
+                _ => CalcResult::new_error(
+                    Error::ERROR,
+                    cell,
+                    format!("Error with Implicit Intersection in cell {:?}", cell),
+                ),
+            },
             _ => self.evaluate_node_in_context(node, cell),
         }
     }
@@ -416,7 +427,7 @@ impl Model {
                 // TODO: NOT IMPLEMENTED
                 CalcResult::new_error(Error::NIMPL, cell, "Arrays not implemented".to_string())
             }
-            DefinedNameKind((name, scope)) => {
+            DefinedNameKind((name, scope, _)) => {
                 if let Ok(Some(parsed_defined_name)) = self.get_parsed_defined_name(name, *scope) {
                     match parsed_defined_name {
                         ParsedDefinedName::CellReference(reference) => {
@@ -528,6 +539,22 @@ impl Model {
                 format!("Error parsing {}: {}", formula, message),
             ),
             EmptyArgKind => CalcResult::EmptyArg,
+            ImplicitIntersection {
+                automatic: _,
+                child,
+            } => match self.evaluate_node_with_reference(child, cell) {
+                CalcResult::Range { left, right } => {
+                    match implicit_intersection(&cell, &Range { left, right }) {
+                        Some(cell_reference) => self.evaluate_cell(cell_reference),
+                        None => CalcResult::new_error(
+                            Error::VALUE,
+                            cell,
+                            format!("Error with Implicit Intersection in cell {:?}", cell),
+                        ),
+                    }
+                }
+                _ => self.evaluate_node_in_context(child, cell),
+            },
         }
     }
 
@@ -617,12 +644,15 @@ impl Model {
                     };
                 }
                 CalcResult::Range { left, right } => {
-                    let range = Range {
-                        left: *left,
-                        right: *right,
-                    };
-                    if let Some(intersection_cell) = implicit_intersection(&cell_reference, &range)
+                    if left.sheet == right.sheet
+                        && left.row == right.row
+                        && left.column == right.column
                     {
+                        let intersection_cell = CellReferenceIndex {
+                            sheet: left.sheet,
+                            column: left.column,
+                            row: left.row,
+                        };
                         let v = self.evaluate_cell(intersection_cell);
                         self.set_cell_value(cell_reference, &v);
                     } else {
@@ -639,10 +669,32 @@ impl Model {
                             f,
                             s,
                             o,
-                            m: "Invalid reference".to_string(),
-                            ei: Error::VALUE,
+                            m: "Implicit Intersection not implemented".to_string(),
+                            ei: Error::NIMPL,
                         };
                     }
+                    // if let Some(intersection_cell) = implicit_intersection(&cell_reference, &range)
+                    // {
+                    //     let v = self.evaluate_cell(intersection_cell);
+                    //     self.set_cell_value(cell_reference, &v);
+                    // } else {
+                    //     let o = match self.cell_reference_to_string(&cell_reference) {
+                    //         Ok(s) => s,
+                    //         Err(_) => "".to_string(),
+                    //     };
+                    //     *self.workbook.worksheets[sheet as usize]
+                    //         .sheet_data
+                    //         .get_mut(&row)
+                    //         .expect("expected a row")
+                    //         .get_mut(&column)
+                    //         .expect("expected a column") = Cell::CellFormulaError {
+                    //         f,
+                    //         s,
+                    //         o,
+                    //         m: "Invalid reference".to_string(),
+                    //         ei: Error::VALUE,
+                    //     };
+                    // }
                 }
                 CalcResult::EmptyCell | CalcResult::EmptyArg => {
                     *self.workbook.worksheets[sheet as usize]
@@ -865,11 +917,7 @@ impl Model {
 
         let worksheet_names = worksheets.iter().map(|s| s.get_name()).collect();
 
-        let defined_names = workbook
-            .get_defined_names_with_scope()
-            .iter()
-            .map(|s| (s.0.to_owned(), s.1))
-            .collect();
+        let defined_names = workbook.get_defined_names_with_scope();
         // add all tables
         // let mut tables = Vec::new();
         // for worksheet in worksheets {
diff --git a/base/src/new_empty.rs b/base/src/new_empty.rs
index a313fe0..2e76696 100644
--- a/base/src/new_empty.rs
+++ b/base/src/new_empty.rs
@@ -144,12 +144,7 @@ impl Model {
 
     /// Reparses all formulas and defined names
     pub(crate) fn reset_parsed_structures(&mut self) {
-        let defined_names = self
-            .workbook
-            .get_defined_names_with_scope()
-            .iter()
-            .map(|s| (s.0.to_owned(), s.1))
-            .collect();
+        let defined_names = self.workbook.get_defined_names_with_scope();
         self.parser
             .set_worksheets_and_names(self.workbook.get_worksheet_names(), defined_names);
         self.parsed_formulas = vec![];
diff --git a/base/src/test/mod.rs b/base/src/test/mod.rs
index d5e8186..bfe23d2 100644
--- a/base/src/test/mod.rs
+++ b/base/src/test/mod.rs
@@ -28,7 +28,6 @@ mod test_fn_sumifs;
 mod test_fn_textbefore;
 mod test_fn_textjoin;
 mod test_fn_unicode;
-mod test_forward_references;
 mod test_frozen_rows_columns;
 mod test_general;
 mod test_math;
@@ -59,6 +58,7 @@ mod test_fn_type;
 mod test_frozen_rows_and_columns;
 mod test_geomean;
 mod test_get_cell_content;
+mod test_implicit_intersection;
 mod test_issue_155;
 mod test_percentage;
 mod test_set_functions_error_handling;
diff --git a/base/src/test/test_fn_concatenate.rs b/base/src/test/test_fn_concatenate.rs
index 727fe95..dcb2c0a 100644
--- a/base/src/test/test_fn_concatenate.rs
+++ b/base/src/test/test_fn_concatenate.rs
@@ -22,13 +22,14 @@ fn fn_concatenate() {
     model._set("B1", r#"=CONCATENATE(A1, A2, A3, "!")"#);
     // This will break once we implement the implicit intersection operator
     // It should be:
-    // model._set("B2", r#"=CONCATENATE(@A1:A3, "!")"#);
+    model._set("C2", r#"=CONCATENATE(@A1:A3, "!")"#);
     model._set("B2", r#"=CONCATENATE(A1:A3, "!")"#);
     model._set("B3", r#"=CONCAT(A1:A3, "!")"#);
 
     model.evaluate();
 
     assert_eq!(model._get_text("B1"), *"Hello my World!");
-    assert_eq!(model._get_text("B2"), *" my !");
+    assert_eq!(model._get_text("B2"), *"#N/IMPL!");
     assert_eq!(model._get_text("B3"), *"Hello my World!");
+    assert_eq!(model._get_text("C2"), *" my !");
 }
diff --git a/base/src/test/test_fn_formulatext.rs b/base/src/test/test_fn_formulatext.rs
index bfe338d..b15180e 100644
--- a/base/src/test/test_fn_formulatext.rs
+++ b/base/src/test/test_fn_formulatext.rs
@@ -30,8 +30,18 @@ fn implicit_intersection() {
     model._set("A2", "=FORMULATEXT(D1:E1)");
     model.evaluate();
 
-    assert_eq!(model._get_text("A1"), *"#ERROR!");
-    assert_eq!(model._get_text("A2"), *"#ERROR!");
+    assert_eq!(model._get_text("A1"), *"#N/IMPL!");
+    assert_eq!(model._get_text("A2"), *"#N/IMPL!");
+}
+
+#[test]
+fn implicit_intersection_operator() {
+    let mut model = new_empty_model();
+    model._set("A1", "=1 +  2");
+    model._set("B1", "=FORMULATEXT(@A:A)");
+    model.evaluate();
+
+    assert_eq!(model._get_text("B1"), *"#N/IMPL!");
 }
 
 #[test]
diff --git a/base/src/test/test_forward_references.rs b/base/src/test/test_forward_references.rs
deleted file mode 100644
index 0b8aa23..0000000
--- a/base/src/test/test_forward_references.rs
+++ /dev/null
@@ -1,121 +0,0 @@
-#![allow(clippy::unwrap_used)]
-
-use crate::expressions::types::{Area, CellReferenceIndex};
-use crate::test::util::new_empty_model;
-
-#[test]
-fn test_forward_references() {
-    let mut model = new_empty_model();
-
-    // test single ref changed nd not changed
-    model._set("H8", "=F6*G9");
-    // tests areas
-    model._set("H9", "=SUM(D4:F6)");
-    // absolute coordinates
-    model._set("H10", "=$F$6");
-    // area larger than the source area
-    model._set("H11", "=SUM(D3:F6)");
-    // Test arguments and concat
-    model._set("H12", "=SUM(F6, D4:F6) & D4");
-    // Test range operator. This is syntax error for now.
-    // model._set("H13", "=SUM(D4:INDEX(D4:F5,4,2))");
-    // Test operations
-    model._set("H14", "=-D4+D5*F6/F5");
-
-    model.evaluate();
-
-    // Source Area is D4:F6
-    let source_area = &Area {
-        sheet: 0,
-        row: 4,
-        column: 4,
-        width: 3,
-        height: 3,
-    };
-
-    // We paste in B10
-    let target_row = 10;
-    let target_column = 2;
-    let result = model.forward_references(
-        source_area,
-        &CellReferenceIndex {
-            sheet: 0,
-            row: target_row,
-            column: target_column,
-        },
-    );
-    assert!(result.is_ok());
-    model.evaluate();
-
-    assert_eq!(model._get_formula("H8"), "=D12*G9");
-    assert_eq!(model._get_formula("H9"), "=SUM(B10:D12)");
-    assert_eq!(model._get_formula("H10"), "=$D$12");
-
-    assert_eq!(model._get_formula("H11"), "=SUM(D3:F6)");
-    assert_eq!(model._get_formula("H12"), "=SUM(D12,B10:D12)&B10");
-    // assert_eq!(model._get_formula("H13"), "=SUM(B10:INDEX(B10:D11,4,2))");
-    assert_eq!(model._get_formula("H14"), "=-B10+B11*D12/D11");
-}
-
-#[test]
-fn test_different_sheet() {
-    let mut model = new_empty_model();
-
-    // test single ref changed not changed
-    model._set("H8", "=F6*G9");
-    // tests areas
-    model._set("H9", "=SUM(D4:F6)");
-    // absolute coordinates
-    model._set("H10", "=$F$6");
-    // area larger than the source area
-    model._set("H11", "=SUM(D3:F6)");
-    // Test arguments and concat
-    model._set("H12", "=SUM(F6, D4:F6) & D4");
-    // Test range operator. This is syntax error for now.
-    // model._set("H13", "=SUM(D4:INDEX(D4:F5,4,2))");
-    // Test operations
-    model._set("H14", "=-D4+D5*F6/F5");
-
-    // Adds a new sheet
-    assert!(model.add_sheet("Sheet2").is_ok());
-
-    model.evaluate();
-
-    // Source Area is D4:F6
-    let source_area = &Area {
-        sheet: 0,
-        row: 4,
-        column: 4,
-        width: 3,
-        height: 3,
-    };
-
-    // We paste in Sheet2!B10
-    let target_row = 10;
-    let target_column = 2;
-    let result = model.forward_references(
-        source_area,
-        &CellReferenceIndex {
-            sheet: 1,
-            row: target_row,
-            column: target_column,
-        },
-    );
-    assert!(result.is_ok());
-    model.evaluate();
-
-    assert_eq!(model._get_formula("H8"), "=Sheet2!D12*G9");
-    assert_eq!(model._get_formula("H9"), "=SUM(Sheet2!B10:D12)");
-    assert_eq!(model._get_formula("H10"), "=Sheet2!$D$12");
-
-    assert_eq!(model._get_formula("H11"), "=SUM(D3:F6)");
-    assert_eq!(
-        model._get_formula("H12"),
-        "=SUM(Sheet2!D12,Sheet2!B10:D12)&Sheet2!B10"
-    );
-    // assert_eq!(model._get_formula("H13"), "=SUM(B10:INDEX(B10:D11,4,2))");
-    assert_eq!(
-        model._get_formula("H14"),
-        "=-Sheet2!B10+Sheet2!B11*Sheet2!D12/Sheet2!D11"
-    );
-}
diff --git a/base/src/test/test_implicit_intersection.rs b/base/src/test/test_implicit_intersection.rs
new file mode 100644
index 0000000..43a039b
--- /dev/null
+++ b/base/src/test/test_implicit_intersection.rs
@@ -0,0 +1,50 @@
+#![allow(clippy::unwrap_used)]
+
+use crate::test::util::new_empty_model;
+
+#[test]
+fn simple_colum() {
+    let mut model = new_empty_model();
+    // We populate cells A1 to A3
+    model._set("A1", "1");
+    model._set("A2", "2");
+    model._set("A3", "3");
+
+    model._set("C2", "=@A1:A3");
+
+    model.evaluate();
+
+    assert_eq!(model._get_text("C2"), "2".to_string());
+}
+
+#[test]
+fn return_of_array_is_n_impl() {
+    let mut model = new_empty_model();
+    // We populate cells A1 to A3
+    model._set("A1", "1");
+    model._set("A2", "2");
+    model._set("A3", "3");
+
+    model._set("C2", "=A1:A3");
+    model._set("D2", "=SUM(SIN(A:A)");
+
+    model.evaluate();
+
+    assert_eq!(model._get_text("C2"), "#N/IMPL!".to_string());
+    assert_eq!(model._get_text("D2"), "#N/IMPL!".to_string());
+}
+
+#[test]
+fn concat() {
+    let mut model = new_empty_model();
+    model._set("A1", "=CONCAT(@B1:B3)");
+    model._set("A2", "=CONCAT(B1:B3)");
+    model._set("B1", "Hello");
+    model._set("B2", " ");
+    model._set("B3", "world!");
+
+    model.evaluate();
+
+    assert_eq!(model._get_text("A1"), *"Hello");
+    assert_eq!(model._get_text("A2"), *"Hello world!");
+}
diff --git a/base/src/units.rs b/base/src/units.rs
index 1b71865..533f0e7 100644
--- a/base/src/units.rs
+++ b/base/src/units.rs
@@ -299,6 +299,7 @@ impl Model {
             Node::WrongVariableKind(_) => None,
             Node::CompareKind { .. } => None,
             Node::OpPowerKind { .. } => None,
+            Node::ImplicitIntersection { .. } => None,
         }
     }
 
diff --git a/base/src/workbook.rs b/base/src/workbook.rs
index 841537d..53ad0ff 100644
--- a/base/src/workbook.rs
+++ b/base/src/workbook.rs
@@ -1,6 +1,6 @@
 use std::vec::Vec;
 
-use crate::types::*;
+use crate::{expressions::parser::DefinedNameS, types::*};
 
 impl Workbook {
     pub fn get_worksheet_names(&self) -> Vec {
@@ -29,7 +29,7 @@ impl Workbook {
     }
 
     /// Returns the a list of defined names in the workbook with their scope
-    pub fn get_defined_names_with_scope(&self) -> Vec<(String, Option, String)> {
+    pub fn get_defined_names_with_scope(&self) -> Vec {
         let sheet_id_index: Vec = self.worksheets.iter().map(|s| s.sheet_id).collect();
 
         let defined_names = self
diff --git a/xlsx/src/import/worksheets.rs b/xlsx/src/import/worksheets.rs
index 1c450b5..a446660 100644
--- a/xlsx/src/import/worksheets.rs
+++ b/xlsx/src/import/worksheets.rs
@@ -1,10 +1,11 @@
 #![allow(clippy::unwrap_used)]
 
+use ironcalc_base::expressions::parser::static_analysis::add_implicit_intersection;
 use std::{collections::HashMap, io::Read, num::ParseIntError};
 
 use ironcalc_base::{
     expressions::{
-        parser::{stringify::to_rc_format, Parser},
+        parser::{stringify::to_rc_format, DefinedNameS, Parser},
         token::{get_error_by_english_name, Error},
         types::CellReferenceRC,
         utils::{column_to_number, parse_reference_a1},
@@ -42,7 +43,7 @@ pub(crate) struct Relationship {
 }
 
 impl WorkbookXML {
-    fn get_defined_names_with_scope(&self) -> Vec<(String, Option)> {
+    fn get_defined_names_with_scope(&self) -> Vec {
         let sheet_id_index: Vec = self.worksheets.iter().map(|s| s.sheet_id).collect();
 
         let defined_names = self
@@ -58,7 +59,7 @@ impl WorkbookXML {
                     // convert Option to Option
                     .map(|pos| pos as u32);
 
-                (dn.name.clone(), index)
+                (dn.name.clone(), index, dn.formula.clone())
             })
             .collect::>();
         defined_names
@@ -304,12 +305,14 @@ fn from_a1_to_rc(
     worksheets: &[String],
     context: String,
     tables: HashMap,
-    defined_names: Vec<(String, Option)>,
+    defined_names: Vec,
 ) -> Result {
     let mut parser = Parser::new(worksheets.to_owned(), defined_names, tables);
     let cell_reference =
         parse_reference(&context).map_err(|error| XlsxError::Xml(error.to_string()))?;
-    let t = parser.parse(&formula, &cell_reference);
+    let mut t = parser.parse(&formula, &cell_reference);
+    add_implicit_intersection(&mut t, true);
+
     Ok(to_rc_format(&t))
 }
 
@@ -706,7 +709,7 @@ pub(super) fn load_sheet(
     worksheets: &[String],
     tables: &HashMap,
     shared_strings: &mut Vec,
-    defined_names: Vec<(String, Option)>,
+    defined_names: Vec,
 ) -> Result<(Worksheet, bool), XlsxError> {
     let sheet_name = &settings.name;
     let sheet_id = settings.id;
diff --git a/xlsx/tests/test.rs b/xlsx/tests/test.rs
index 5b34de6..0845da5 100644
--- a/xlsx/tests/test.rs
+++ b/xlsx/tests/test.rs
@@ -48,8 +48,8 @@ fn test_example() {
     assert_eq!(ws[0].views[&0].range, [13, 5, 20, 14]);
 
     let model2 = load_from_icalc("tests/example.ic").unwrap();
-    let s = bitcode::encode(&model2.workbook);
-    assert_eq!(workbook, model2.workbook, "{:?}", s);
+    let _ = bitcode::encode(&model2.workbook);
+    assert_eq!(workbook, model2.workbook);
 }
 
 #[test]
@@ -346,21 +346,31 @@ fn test_xlsx() {
     let path = format!("{}", Uuid::new_v4());
     let dir = temp_folder.join(path);
     fs::create_dir(&dir).unwrap();
+    let mut is_error = false;
     for file_path in entries {
         let file_name_str = file_path.file_name().unwrap().to_str().unwrap();
         let file_path_str = file_path.to_str().unwrap();
         println!("Testing file: {}", file_path_str);
         if file_name_str.ends_with(".xlsx") && !file_name_str.starts_with('~') {
             if let Err(message) = test_file(file_path_str) {
+                println!("Error with file: '{file_path_str}'");
                 println!("{}", message);
-                panic!("Model was evaluated inconsistently with XLSX data.")
+                is_error = true;
+            }
+            let t = test_load_and_saving(file_path_str, &dir);
+            if t.is_err() {
+                println!("Error while load and saving file: {file_path_str}");
+                is_error = true;
             }
-            assert!(test_load_and_saving(file_path_str, &dir).is_ok());
         } else {
             println!("skipping");
         }
     }
     fs::remove_dir_all(&dir).unwrap();
+    assert!(
+        !is_error,
+        "Models were evaluated inconsistently with XLSX data."
+    );
 }
 
 #[test]
@@ -375,20 +385,26 @@ fn no_export() {
     let path = format!("{}", Uuid::new_v4());
     let dir = temp_folder.join(path);
     fs::create_dir(&dir).unwrap();
+    let mut is_error = false;
     for file_path in entries {
         let file_name_str = file_path.file_name().unwrap().to_str().unwrap();
         let file_path_str = file_path.to_str().unwrap();
         println!("Testing file: {}", file_path_str);
         if file_name_str.ends_with(".xlsx") && !file_name_str.starts_with('~') {
             if let Err(message) = test_file(file_path_str) {
+                println!("Error with file: '{file_path_str}'");
                 println!("{}", message);
-                panic!("Model was evaluated inconsistently with XLSX data.")
+                is_error = true;
             }
         } else {
             println!("skipping");
         }
     }
     fs::remove_dir_all(&dir).unwrap();
+    assert!(
+        !is_error,
+        "Models were evaluated inconsistently with XLSX data."
+    );
 }
 
 // This test verifies whether exporting the merged cells functionality is happening properly or not.
@@ -476,6 +492,7 @@ fn test_documentation_xlsx() {
     let path = format!("{}", Uuid::new_v4());
     let dir = temp_folder.join(path);
     fs::create_dir(&dir).unwrap();
+    let mut is_error = false;
     for file_path in entries {
         let file_name_str = file_path.file_name().unwrap().to_str().unwrap();
         let file_path_str = file_path.to_str().unwrap();
@@ -487,7 +504,7 @@ fn test_documentation_xlsx() {
         if file_name_str.ends_with(".xlsx") && !file_name_str.starts_with('~') {
             if let Err(message) = test_file(file_path_str) {
                 println!("{}", message);
-                panic!("Model was evaluated inconsistently with XLSX data.")
+                is_error = true;
             }
             assert!(test_load_and_saving(file_path_str, &dir).is_ok());
         } else {
@@ -495,6 +512,10 @@ fn test_documentation_xlsx() {
         }
     }
     fs::remove_dir_all(&dir).unwrap();
+    assert!(
+        !is_error,
+        "Models were evaluated inconsistently with XLSX data."
+    )
 }
 
 #[test]