use crate::token::{Identifier, Token};
#[derive(Debug, Default, Clone)]
pub struct Ast {
pub nodes: Vec<ASTNode>,
}
#[derive(Debug, PartialEq, Clone)]
pub enum ASTNodeIdentifier {
Unknown,
Text,
Variable,
Loop,
LoopEnd,
NewLine,
}
#[derive(Debug, Clone)]
pub struct ASTNode {
pub identifier: ASTNodeIdentifier,
pub value: String,
pub tokens: Vec<Token>,
pub children: Option<Ast>,
}
fn assign_identity(ast_node: &mut ASTNode) -> Result<(), String> {
let opening_chars = &ast_node.value[0..2];
if opening_chars == "${" && ast_node.value.ends_with('}') {
ast_node.identifier = ASTNodeIdentifier::Variable;
} else if opening_chars == "{#" && ast_node.value.ends_with("#}") {
let control_flow = ast_node.value.split(' ').next().unwrap();
match control_flow {
"{#endfor#}" => ast_node.identifier = ASTNodeIdentifier::LoopEnd,
"{#for" => {
ast_node.identifier = ASTNodeIdentifier::Loop;
ast_node.children = Some(Ast { nodes: vec![] });
}
_ => {
let construct_token = ast_node.tokens.get(2).unwrap();
return Err(format!(
"\nUnknown construct: {} at line {}:{}\n{}{}\n",
ast_node.value,
construct_token.line_start,
construct_token.pos_start,
" ".to_string().repeat(21),
"^".to_string().repeat(control_flow.len() - 2),
));
}
}
};
Ok(())
}
pub fn construct_ast(parsed_tokens: Vec<Vec<Token>>) -> Result<Ast, String> {
let mut open_brace_count = 0;
let mut constructed_ast = Ast { nodes: vec![] };
for token_group in parsed_tokens {
let mut ast_node = ASTNode {
identifier: ASTNodeIdentifier::Unknown,
value: String::new(),
tokens: vec![],
children: Option::None,
};
let mut bad_token = Token::new(Identifier::Text, 1, 1, 1, 1);
for token in token_group {
match token.identifier {
Identifier::Text => ast_node.identifier = ASTNodeIdentifier::Text,
Identifier::NewLine => ast_node.identifier = ASTNodeIdentifier::NewLine,
_ => {
if token.value == "{" {
open_brace_count += 1;
}
if open_brace_count > 1 && bad_token.value.is_empty() {
bad_token = token.clone();
}
}
}
ast_node.value.push_str(token.value.as_str());
ast_node.tokens.push(token);
}
if ast_node.value.len() > 2 {
match assign_identity(&mut ast_node) {
Ok(()) => {}
Err(e) => return Err(e),
}
}
if open_brace_count > 1 {
return Err(format!(
"Error: Extra opening {{ found at line: {} position: {} in {}",
bad_token.line_start,
bad_token.pos_start - 1,
ast_node.value
));
}
let last_node = constructed_ast.nodes.last();
if let Some(last_node) = last_node {
if (ast_node.identifier == ASTNodeIdentifier::LoopEnd
|| ast_node.identifier == ASTNodeIdentifier::Loop)
&& last_node.value.replace(' ', "").is_empty()
{
constructed_ast.nodes.pop();
}
}
match ast_node.identifier {
ASTNodeIdentifier::NewLine => {
if constructed_ast.nodes.last().unwrap().identifier != ASTNodeIdentifier::LoopEnd {
constructed_ast.nodes.push(ast_node);
}
}
_ => constructed_ast.nodes.push(ast_node),
}
open_brace_count = 0;
}
let mut new_ast = Ast { nodes: vec![] };
let mut loop_node_vec: Vec<ASTNode> = vec![];
for node in constructed_ast.nodes {
match node.identifier {
ASTNodeIdentifier::Loop => {
loop_node_vec.push(node);
}
ASTNodeIdentifier::LoopEnd => {
if loop_node_vec.len() > 1 {
let length = loop_node_vec.len() - 1;
let latest_node = loop_node_vec[length].clone();
let length_of_next = length - 1;
let latest_node_next =
&mut loop_node_vec[length_of_next].children.as_mut().unwrap();
latest_node_next.nodes.push(latest_node);
loop_node_vec.pop();
} else {
new_ast.nodes.push(loop_node_vec.last().unwrap().clone());
loop_node_vec.pop();
}
if !loop_node_vec.is_empty() {
let length = loop_node_vec.len() - 1;
let latest_node = &mut loop_node_vec[length].children.as_mut().unwrap();
latest_node.nodes.push(node);
} else {
new_ast.nodes.push(node);
}
}
ASTNodeIdentifier::NewLine => {
if !loop_node_vec.is_empty() {
let length = loop_node_vec.len() - 1;
let latest_node = &mut loop_node_vec[length].children.as_mut().unwrap();
if new_ast.nodes.last().unwrap().identifier != ASTNodeIdentifier::NewLine
|| !latest_node.nodes.is_empty()
{
latest_node.nodes.push(node);
}
} else {
new_ast.nodes.push(node);
}
}
_ => {
if !loop_node_vec.is_empty() {
let length = loop_node_vec.len() - 1;
let latest_node = &mut loop_node_vec[length].children.as_mut().unwrap();
latest_node.nodes.push(node);
} else {
new_ast.nodes.push(node);
}
}
}
}
if !loop_node_vec.is_empty() {
let last_node = loop_node_vec.last().unwrap();
return Err(format!(
"\n'{}' has no closing statement\nat line: {}:{}\n",
last_node.value,
last_node.tokens.first().unwrap().line_start,
last_node.tokens.first().unwrap().pos_start
));
}
Ok(new_ast)
}