use crate::*; #[derive(Debug)] pub struct Parser { row: usize, column: usize, lines: Vec>, } #[derive(Debug, Clone)] pub(crate) struct Instruction { pub name: String, pub arguments: Vec, } #[derive(Debug, Clone)] pub(crate) enum Value { Instruction(Instruction), StringLiteral(String), IntegerLiteral(u32), Identifier(String), } impl Parser { pub fn parse(stream: String) -> Result { let mut parser = Parser { row: 0, column: 0, lines: stream .split('\n') .map(|a| a.chars().collect::>()) .collect::>(), }; parser.instruction() } fn increment(&mut self) -> Option<()> { self.column += 1; if self.column == self.lines[self.row].len() { self.row += 1; if self.row == self.lines.len() { self.row -= 1; self.column -= 1; return None; } self.column = 0; } return Some(()); } fn jump_whitespace(&mut self) -> Option<()> { self.increment()?; while self.lines[self.row][self.column].is_whitespace() { self.increment()?; } Some(()) } fn instruction(&mut self) -> Result { if self.lines[self.row][self.column] != '(' { return Err(Error::SyntaxError(SyntaxError { row: self.row, column: self.column, message: "SyntaxError: Expected '('".to_string(), })); } self.jump_whitespace(); let name = self.identifier()?; let mut arguments = Vec::new(); while self.lines[self.row][self.column] != ')' { match self.lines[self.row][self.column] { '\"' => arguments.push(Value::StringLiteral(self.string_literal()?)), '(' => arguments.push(Value::Instruction(self.instruction()?)), character => { if character.is_numeric() { arguments.push(Value::IntegerLiteral(self.integer_literal()?)); } else { arguments.push(Value::Identifier(self.identifier()?)); } } } } self.jump_whitespace(); return Ok(Instruction { name, arguments }); } fn string_literal(&mut self) -> Result { self.increment(); let mut accumulator = String::new(); while self.lines[self.row][self.column] != '"' { accumulator.push(self.lines[self.row][self.column]); self.increment(); } self.jump_whitespace(); Ok(accumulator) } fn integer_literal(&mut self) -> Result { let mut accumulator = String::new(); while self.lines[self.row][self.column].is_numeric() { accumulator.push(self.lines[self.row][self.column]); self.increment(); } self.jump_whitespace(); match accumulator.parse() { Ok(v) => Ok(v), Err(_e) => Err(Error::SyntaxError(SyntaxError { row: self.row, column: self.column, message: "Couldn't parse integer literal".to_string(), })), } } fn identifier(&mut self) -> Result { let mut accumulator = String::new(); let mut current = self.lines[self.row][self.column]; while !"()\"".contains(current) && !current.is_whitespace() { accumulator.push(self.lines[self.row][self.column]); self.increment(); current = self.lines[self.row][self.column]; } self.jump_whitespace(); Ok(accumulator) } }