thiselang/src/ast.rs
2023-03-19 04:40:25 +01:00

392 lines
11 KiB
Rust

use crate::tokens::Position;
pub trait AstNode {
fn fancy_string(&self, depth: usize) -> String;
}
#[derive(Debug)]
pub struct Node<T: AstNode> {
pub value: T,
pub pos: Position,
}
impl<T: AstNode> Node<T> {
pub fn fancy_string(&self, depth: usize) -> String {
self.value.fancy_string(depth)
}
}
#[derive(Debug)]
pub enum Expr {
Error(String),
Unit,
Id(String),
Int(i64),
Float(f64),
String(String),
Bool(bool),
Array(Vec<Node<Expr>>),
Object(Vec<Node<ObjectEntry>>),
Tuple(Vec<Node<Expr>>),
If {
condition: Box<Node<Expr>>,
truthy: Box<Node<Expr>>,
falsy: Option<Box<Node<Expr>>>,
},
FunctionValue {
parameters: Vec<Node<Parameter>>,
body: Box<Node<Expr>>,
},
Member {
subject: Box<Node<Expr>>,
value: String,
},
Index {
subject: Box<Node<Expr>>,
value: Box<Node<Expr>>,
},
Call {
subject: Box<Node<Expr>>,
arguments: Vec<Node<Expr>>,
},
Unary {
unary_type: UnaryType,
subject: Box<Node<Expr>>,
},
Binary {
binary_type: BinaryType,
left: Box<Node<Expr>>,
right: Box<Node<Expr>>,
},
RangeExclusive {
begin: Box<Node<Expr>>,
end: Box<Node<Expr>>,
},
RangeInclusive {
begin: Box<Node<Expr>>,
end: Box<Node<Expr>>,
},
Assign {
assign_type: AssignType,
subject: Box<Node<Expr>>,
value: Box<Node<Expr>>,
},
Let {
subject: Node<Parameter>,
value: Option<Box<Node<Expr>>>,
},
Continue,
Break,
While {
condition: Box<Node<Expr>>,
body: Box<Node<Expr>>,
},
For {
subject: Node<Parameter>,
value: Box<Node<Expr>>,
body: Box<Node<Expr>>,
},
Return(Option<Box<Node<Expr>>>),
Function {
name: String,
parameters: Vec<Node<Parameter>>,
body: Box<Node<Expr>>,
},
Block {
statements: Vec<Node<Expr>>,
value: Option<Box<Node<Expr>>>,
},
Spread(Box<Node<Expr>>),
}
#[derive(Debug)]
pub enum ObjectEntry {
Error(String),
Pair {
key: Box<Node<Expr>>,
value: Box<Node<Expr>>,
},
Spread(Box<Node<Expr>>),
}
#[derive(Debug)]
pub enum UnaryType {
Not,
Negate,
Reference,
ReferenceMut,
Dereference,
}
#[derive(Debug)]
pub enum BinaryType {
Exponentiate,
Multiply,
Divide,
Modulo,
Add,
Subtract,
LT,
LTE,
GT,
GTE,
In,
NotIn,
Equal,
Inequal,
And,
Or,
}
#[derive(Debug)]
pub enum AssignType {
Assign,
Add,
Subtract,
Multiply,
Divide,
Modulo,
}
#[derive(Debug)]
pub enum Parameter {
Error(String),
Id { name: String, mutable: bool },
Spread(Box<Node<Parameter>>),
}
fn indent(depth: usize) -> String {
"".repeat(depth)
}
pub fn escape_string(text: &str) -> String {
let mut result = String::new();
for c in text.chars() {
match c {
'\\' => result.push_str("\\\\"),
'\0' => result.push_str("\\0"),
'\t' => result.push_str("\\t"),
'\r' => result.push_str("\\r"),
'\n' => result.push_str("\\n"),
'\'' => result.push_str("\\\'"),
'\"' => result.push_str("\\\""),
c => result.push(c),
};
}
return result;
}
impl AstNode for Expr {
fn fancy_string(&self, depth: usize) -> String {
match self {
Expr::Error(message) => format!("Error({message})"),
Expr::Unit => format!("Unit"),
Expr::Id(v) => format!("Id(`{v}`)"),
Expr::Int(v) => format!("Int({v})"),
Expr::Float(v) => format!("Float({v})"),
Expr::String(v) => format!("String(\"{}\")", escape_string(v)),
Expr::Bool(v) => format!("Bool({v})"),
Expr::Array(values) => format!(
"Array\n{}",
values
.iter()
.map(|v| format!(
"{}{}\n",
indent(depth + 1),
(*v).value.fancy_string(depth + 1)
))
.reduce(|mut acc, v| {
acc.push_str(&v);
acc
})
.unwrap_or("".to_string()),
),
Expr::Object(_) => format!("Object <...>"),
Expr::Tuple(_) => format!("Tuple <...>"),
Expr::If {
condition,
truthy,
falsy,
} => format!(
"If\n{}condition: {}\n{}truthy: {}\n{}falsy: {}",
indent(depth + 1),
condition.fancy_string(depth + 1),
indent(depth + 1),
truthy.fancy_string(depth + 1),
indent(depth + 1),
match falsy {
Some(expr) => expr.fancy_string(depth + 1),
None => "None".to_string(),
},
),
Expr::FunctionValue {
parameters: _,
body: _,
} => format!("FunctionValue <...>"),
Expr::Member {
subject: _,
value: _,
} => format!("Member <...>"),
Expr::Index {
subject: _,
value: _,
} => format!("Index <...>"),
Expr::Call { subject, arguments } => format!(
"Call\n{}subject: {}\n{}arguments:\n{}",
indent(depth + 1),
subject.fancy_string(depth + 1),
indent(depth + 1),
arguments
.iter()
.map(|v| format!(
"{}{}\n",
indent(depth + 2),
(*v).value.fancy_string(depth + 2)
))
.reduce(|mut acc, v| {
acc.push_str(&v);
acc
})
.map(|s| s.trim_end().to_string())
.unwrap_or("".to_string()),
),
Expr::Unary {
unary_type,
subject,
} => format!(
"Unary\n{}unary_type: {:?}\n{}subject: {}",
indent(depth + 1),
unary_type,
indent(depth + 1),
subject.fancy_string(depth + 1),
),
Expr::Binary {
binary_type,
left,
right,
} => format!(
"Binary\n{}binary_type: {:?}\n{}left: {}\n{}right: {}",
indent(depth + 1),
binary_type,
indent(depth + 1),
left.fancy_string(depth + 1),
indent(depth + 1),
right.fancy_string(depth + 1),
),
Expr::RangeExclusive { begin: _, end: _ } => format!("RangeExclusive <...>"),
Expr::RangeInclusive { begin: _, end: _ } => format!("RangeInclusive <...>"),
Expr::Assign {
assign_type: _,
subject: _,
value: _,
} => format!("Assign <...>"),
Expr::Let { subject, value } => format!(
"Let {{\n{}subject: {}\n{}value: {}\n{}}}",
indent(depth + 1),
subject.fancy_string(depth + 1),
indent(depth + 1),
match value {
Some(expr) => expr.fancy_string(depth + 1),
None => "None".to_string(),
},
indent(depth),
),
Expr::Continue => format!("Continue"),
Expr::Break => format!("Break"),
Expr::While {
condition: _,
body: _,
} => format!("While <...>"),
Expr::For {
subject: _,
value: _,
body: _,
} => format!("For <...>"),
Expr::Return(value) => format!(
"Return{}",
match value {
Some(v) => format!(
"\n{}value: {}",
indent(depth + 1),
v.fancy_string(depth + 1)
),
None => "".to_string(),
}
),
Expr::Function {
name,
parameters,
body,
} => format!(
"Function\n{}name: {}\n{}parameters:\n{}{}body: {}",
indent(depth + 1),
name,
indent(depth + 1),
parameters
.iter()
.map(|v| format!(
"{}{}\n",
indent(depth + 2),
(*v).value.fancy_string(depth + 2)
))
.reduce(|mut acc, v| {
acc.push_str(&v);
acc
})
.unwrap_or("".to_string()),
indent(depth + 1),
body.fancy_string(depth + 1)
),
Expr::Block { statements, value } => format!(
"Block\n{}statements:\n{}{}value: \n{}{}",
indent(depth + 1),
statements
.iter()
.map(|v| format!(
"{}{}\n",
indent(depth + 2),
(*v).value.fancy_string(depth + 2)
))
.reduce(|mut acc, v| {
acc.push_str(&v);
acc
})
.unwrap_or("".to_string()),
indent(depth + 1),
indent(depth + 2),
match value {
Some(expr) => expr.fancy_string(depth + 2),
None => "None".to_string(),
},
),
Expr::Spread(_) => format!("Spread <...>"),
}
}
}
impl AstNode for ObjectEntry {
fn fancy_string(&self, _depth: usize) -> String {
match self {
ObjectEntry::Error(message) => format!("Error({message})"),
ObjectEntry::Pair { key: _, value: _ } => format!("Pair <...>"),
ObjectEntry::Spread(_) => format!("Spread <...>"),
}
}
}
impl AstNode for Parameter {
fn fancy_string(&self, _depth: usize) -> String {
match self {
Parameter::Error(message) => format!("Error({message})"),
Parameter::Id { name, mutable } => {
format!("Id(`{name}`{})", if *mutable { ", mutable" } else { "" })
}
Parameter::Spread(_) => format!("Spread <...>"),
}
}
}