use anyhow::{bail, Result}; use num_parse; use crate::lexer; pub mod ast; fn process_raw_stack_result(s: Option<&str>) -> Vec { match s { Some(x) if !x.trim().is_empty() => { x.split_whitespace().map(|x| x.trim().to_string()).collect() } _ => vec![], } } pub fn parse_stack_result(s: String) -> ast::StackResult { let mut splitter = s.splitn(2, "--"); ast::StackResult { before: process_raw_stack_result(splitter.next()), after: process_raw_stack_result(splitter.next()), } } pub fn parse(tokens: Vec) -> Result { let mut iter = tokens.into_iter().peekable(); let mut body: ast::Body = vec![]; while let Some(token) = iter.next() { match token { lexer::Token::Newline(_) | lexer::Token::Whitespace(_) => {} lexer::Token::ParenComment(x) | lexer::Token::BackslashComment(x) | lexer::Token::DoubleDashComment(x) => { body.push(ast::Node::Comment(x.trim().to_string())); } lexer::Token::StringLiteral { mode, string } => { body.push(ast::Node::String { mode, string }); } lexer::Token::Number(x) => match num_parse::parse_int::(x.as_str()) { Some(n) => { body.push(ast::Node::Number(n)); } None => bail!("Invalid number: {}", x), }, lexer::Token::Word(x) => match x.as_str() { ":" => { let mut depth: usize = 1; let mut content = iter .by_ref() .take_while(|t| match t { lexer::Token::Word(x) => match x.as_str() { ":" => { depth += 1; true } ";" => { depth -= 1; depth != 0 } _ => true, }, _ => true, }) .collect::>() .into_iter(); if depth != 0 { bail!("Unbalanced word definitions"); } let name = match content.find(|t| { !matches!(t, lexer::Token::Newline(_) | lexer::Token::Whitespace(_)) }) { Some(t) => match t { lexer::Token::Word(x) => x.clone(), _ => bail!("Word definition name must be a word itself: {:?}", t), }, None => bail!("Word definition can not be empty"), }; let stack = match content.find(|t| { !matches!(t, lexer::Token::Newline(_) | lexer::Token::Whitespace(_)) }) { Some(t) => match t { lexer::Token::ParenComment(x) | lexer::Token::BackslashComment(x) | lexer::Token::DoubleDashComment(x) => Some(parse_stack_result(x)), _ => None, }, None => None, }; body.push(ast::Node::WordDefinition { name, stack, body: parse(content.collect())?.body, }); } _ => { body.push(ast::Node::Word(x)); } }, } } Ok(ast::AST { body }) }