UPDATE: Dump of initial files
This commit is contained in:
319
base/src/expressions/lexer/ranges.rs
Normal file
319
base/src/expressions/lexer/ranges.rs
Normal file
@@ -0,0 +1,319 @@
|
||||
use crate::constants::{LAST_COLUMN, LAST_ROW};
|
||||
use crate::expressions::{token::TokenType, utils::column_to_number};
|
||||
|
||||
use super::Lexer;
|
||||
use super::{ParsedRange, ParsedReference, Result};
|
||||
|
||||
impl Lexer {
|
||||
// Consumes a reference in A1 style like:
|
||||
// AS23, $AS23, AS$23, $AS$23, R12
|
||||
// Or returns an error
|
||||
fn consume_reference_a1(&mut self) -> Result<ParsedReference> {
|
||||
let mut absolute_column = false;
|
||||
let mut absolute_row = false;
|
||||
let mut position = self.position;
|
||||
let len = self.len;
|
||||
if position < len && self.chars[position] == '$' {
|
||||
absolute_column = true;
|
||||
position += 1;
|
||||
}
|
||||
let mut column = "".to_string();
|
||||
while position < len {
|
||||
let x = self.chars[position].to_ascii_uppercase();
|
||||
match x {
|
||||
'A'..='Z' => column.push(x),
|
||||
_ => break,
|
||||
}
|
||||
position += 1;
|
||||
}
|
||||
if column.is_empty() {
|
||||
return Err(self.set_error("Failed to parse reference", position));
|
||||
}
|
||||
if position < len && self.chars[position] == '$' {
|
||||
absolute_row = true;
|
||||
position += 1;
|
||||
}
|
||||
let mut row = "".to_string();
|
||||
while position < len {
|
||||
let x = self.chars[position];
|
||||
match x {
|
||||
'0'..='9' => row.push(x),
|
||||
_ => break,
|
||||
}
|
||||
position += 1;
|
||||
}
|
||||
// Note that row numbers could start with 0
|
||||
self.position = position;
|
||||
let column = column_to_number(&column).map_err(|error| self.set_error(&error, position))?;
|
||||
|
||||
match row.parse::<i32>() {
|
||||
Ok(row) => {
|
||||
if row > LAST_ROW {
|
||||
return Err(self.set_error("Row too large in reference", position));
|
||||
}
|
||||
Ok(ParsedReference {
|
||||
column,
|
||||
row,
|
||||
absolute_column,
|
||||
absolute_row,
|
||||
})
|
||||
}
|
||||
Err(..) => Err(self.set_error("Failed to parse integer", position)),
|
||||
}
|
||||
}
|
||||
|
||||
// Parsing a range is a parser on it's own right. Here is the grammar:
|
||||
//
|
||||
// range -> cell | cell ':' cell | row ':' row | column ':' column
|
||||
// cell -> column row
|
||||
// column -> '$' column_name | column_name
|
||||
// row -> '$' row_name | row_name
|
||||
// column_name -> 'A'..'XFD'
|
||||
// row_name -> 1..1_048_576
|
||||
pub(super) fn consume_range_a1(&mut self) -> Result<ParsedRange> {
|
||||
// first let's try to parse a cell
|
||||
let mut position = self.position;
|
||||
match self.consume_reference_a1() {
|
||||
Ok(cell) => {
|
||||
if self.peek_char() == Some(':') {
|
||||
// It's a range
|
||||
self.position += 1;
|
||||
if let Ok(cell2) = self.consume_reference_a1() {
|
||||
Ok(ParsedRange {
|
||||
left: cell,
|
||||
right: Some(cell2),
|
||||
})
|
||||
} else {
|
||||
Err(self.set_error("Expecting reference in range", self.position))
|
||||
}
|
||||
} else {
|
||||
// just a reference
|
||||
Ok(ParsedRange {
|
||||
left: cell,
|
||||
right: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
self.position = position;
|
||||
// It's either a row range or a column range (or not a range at all)
|
||||
let len = self.len;
|
||||
let mut absolute_left = false;
|
||||
if position < len && self.chars[position] == '$' {
|
||||
absolute_left = true;
|
||||
position += 1;
|
||||
}
|
||||
let mut column_left = "".to_string();
|
||||
let mut row_left = "".to_string();
|
||||
while position < len {
|
||||
let x = self.chars[position].to_ascii_uppercase();
|
||||
match x {
|
||||
'A'..='Z' => column_left.push(x),
|
||||
'0'..='9' => row_left.push(x),
|
||||
_ => break,
|
||||
}
|
||||
position += 1;
|
||||
}
|
||||
if position >= len || self.chars[position] != ':' {
|
||||
return Err(self.set_error("Expecting reference in range", self.position));
|
||||
}
|
||||
position += 1;
|
||||
let mut absolute_right = false;
|
||||
if position < len && self.chars[position] == '$' {
|
||||
absolute_right = true;
|
||||
position += 1;
|
||||
}
|
||||
let mut column_right = "".to_string();
|
||||
let mut row_right = "".to_string();
|
||||
while position < len {
|
||||
let x = self.chars[position].to_ascii_uppercase();
|
||||
match x {
|
||||
'A'..='Z' => column_right.push(x),
|
||||
'0'..='9' => row_right.push(x),
|
||||
_ => break,
|
||||
}
|
||||
position += 1;
|
||||
}
|
||||
self.position = position;
|
||||
// At this point either the columns are the empty string or the rows are the empty string
|
||||
if !row_left.is_empty() {
|
||||
// It is a row range 23:56
|
||||
if row_right.is_empty() || !column_left.is_empty() || !column_right.is_empty() {
|
||||
return Err(self.set_error("Error parsing Range", position));
|
||||
}
|
||||
// Note that row numbers can start with 0
|
||||
let row_left = match row_left.parse::<i32>() {
|
||||
Ok(n) => n,
|
||||
Err(_) => {
|
||||
return Err(self
|
||||
.set_error(&format!("Failed parsing row {}", row_left), position))
|
||||
}
|
||||
};
|
||||
let row_right = match row_right.parse::<i32>() {
|
||||
Ok(n) => n,
|
||||
Err(_) => {
|
||||
return Err(self
|
||||
.set_error(&format!("Failed parsing row {}", row_right), position))
|
||||
}
|
||||
};
|
||||
if row_left > LAST_ROW {
|
||||
return Err(self.set_error("Row too large in reference", position));
|
||||
}
|
||||
if row_right > LAST_ROW {
|
||||
return Err(self.set_error("Row too large in reference", position));
|
||||
}
|
||||
return Ok(ParsedRange {
|
||||
left: ParsedReference {
|
||||
row: row_left,
|
||||
absolute_row: absolute_left,
|
||||
column: 1,
|
||||
absolute_column: true,
|
||||
},
|
||||
right: Some(ParsedReference {
|
||||
row: row_right,
|
||||
absolute_row: absolute_right,
|
||||
column: LAST_COLUMN,
|
||||
absolute_column: true,
|
||||
}),
|
||||
});
|
||||
}
|
||||
// It is a column range
|
||||
if column_right.is_empty() || !row_right.is_empty() {
|
||||
return Err(self.set_error("Error parsing Range", position));
|
||||
}
|
||||
let column_left = column_to_number(&column_left)
|
||||
.map_err(|error| self.set_error(&error, position))?;
|
||||
let column_right = column_to_number(&column_right)
|
||||
.map_err(|error| self.set_error(&error, position))?;
|
||||
Ok(ParsedRange {
|
||||
left: ParsedReference {
|
||||
row: 1,
|
||||
absolute_row: true,
|
||||
column: column_left,
|
||||
absolute_column: absolute_left,
|
||||
},
|
||||
right: Some(ParsedReference {
|
||||
row: LAST_ROW,
|
||||
absolute_row: true,
|
||||
column: column_right,
|
||||
absolute_column: absolute_right,
|
||||
}),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn consume_range_r1c1(&mut self) -> Result<ParsedRange> {
|
||||
// first let's try to parse a cell
|
||||
match self.consume_reference_r1c1() {
|
||||
Ok(cell) => {
|
||||
if self.peek_char() == Some(':') {
|
||||
// It's a range
|
||||
self.position += 1;
|
||||
if let Ok(cell2) = self.consume_reference_r1c1() {
|
||||
Ok(ParsedRange {
|
||||
left: cell,
|
||||
right: Some(cell2),
|
||||
})
|
||||
} else {
|
||||
Err(self.set_error("Expecting reference in range", self.position))
|
||||
}
|
||||
} else {
|
||||
// just a reference
|
||||
Ok(ParsedRange {
|
||||
left: cell,
|
||||
right: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
Err(s) => Err(s),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn consume_reference_r1c1(&mut self) -> Result<ParsedReference> {
|
||||
// R12C3, R[2]C[-2], R3C[6], R[-3]C4, RC1, R[-2]C
|
||||
let absolute_column;
|
||||
let absolute_row;
|
||||
let position = self.position;
|
||||
let row;
|
||||
let column;
|
||||
self.expect_char('R')?;
|
||||
match self.peek_char() {
|
||||
Some('[') => {
|
||||
absolute_row = false;
|
||||
self.expect_char('[')?;
|
||||
let c = match self.read_next_char() {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
return Err(self.set_error("Expected column number", position));
|
||||
}
|
||||
};
|
||||
match self.consume_integer(c) {
|
||||
Ok(v) => row = v,
|
||||
Err(_) => {
|
||||
return Err(self.set_error("Expected row number", position));
|
||||
}
|
||||
}
|
||||
self.expect(TokenType::RightBracket)?;
|
||||
}
|
||||
Some(c) => {
|
||||
absolute_row = true;
|
||||
self.expect_char(c)?;
|
||||
match self.consume_integer(c) {
|
||||
Ok(v) => row = v,
|
||||
Err(_) => {
|
||||
return Err(self.set_error("Expected row number", position));
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Err(self.set_error("Expected row number or '['", position));
|
||||
}
|
||||
}
|
||||
self.expect_char('C')?;
|
||||
match self.peek_char() {
|
||||
Some('[') => {
|
||||
self.expect_char('[')?;
|
||||
absolute_column = false;
|
||||
let c = match self.read_next_char() {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
return Err(self.set_error("Expected column number", position));
|
||||
}
|
||||
};
|
||||
match self.consume_integer(c) {
|
||||
Ok(v) => column = v,
|
||||
Err(_) => {
|
||||
return Err(self.set_error("Expected column number", position));
|
||||
}
|
||||
}
|
||||
self.expect(TokenType::RightBracket)?;
|
||||
}
|
||||
Some(c) => {
|
||||
absolute_column = true;
|
||||
self.expect_char(c)?;
|
||||
match self.consume_integer(c) {
|
||||
Ok(v) => column = v,
|
||||
Err(_) => {
|
||||
return Err(self.set_error("Expected column number", position));
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Err(self.set_error("Expected column number or '['", position));
|
||||
}
|
||||
}
|
||||
if let Some(c) = self.peek_char() {
|
||||
if c.is_alphanumeric() {
|
||||
return Err(self.set_error("Expected end of reference", position));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ParsedReference {
|
||||
column,
|
||||
row,
|
||||
absolute_column,
|
||||
absolute_row,
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user