You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

488 lines
14 KiB
Rust

use std::fmt;
use crate::error::Error;
use crate::lex::token;
use crate::lex::token::{Position, Token};
pub enum Node {
Break(Position),
Ident(Position, String),
DepList(Position, Box<Node>),
SourceList(Position, Box<Node>),
Bool(Position, bool),
Int(Position, i128),
String(Position, String),
Array(Position, Vec<Node>),
Block(Position, Vec<Node>),
Fn(Position, FnStmt),
ArrayExpr(Position, ArrayExpr),
CallExpr(Position, CallExpr),
IfStmt(Position, IfStmt),
ReturnStmt(Position, Box<Node>),
ForStmt(Position, ForStmt),
WhileStmt(Position, WhileStmt),
UnaryExpr(Position, UnaryExpr),
BinaryExpr(Position, BinaryExpr),
TypeStmt(Position, Box<Node>),
SetStmt(Position, SetStmt),
TargetStmt(Position, TargetStmt),
File(Position, File),
}
pub struct File {
pub name: String,
pub content: Vec<Node>,
}
pub struct FnStmt {
pub name: Option<Box<Node>>,
pub params: Vec<Node>,
pub body: Box<Node>,
}
pub struct IfStmt {
pub condition: Box<Node>,
pub then_body: Box<Node>,
pub else_body: Option<Box<Node>>,
}
pub struct ForStmt {
pub setup: Option<Box<Node>>,
pub condition: Option<Box<Node>>,
pub exec: Option<Box<Node>>,
pub body: Box<Node>,
}
pub struct WhileStmt {
pub condition: Box<Node>,
pub body: Box<Node>,
}
pub struct SetStmt {
pub name: Box<Node>,
pub val: Box<Node>,
}
pub struct TargetStmt {
pub name: Box<Node>,
pub val: Box<Node>,
}
pub struct BinaryExpr {
pub lhs: Box<Node>,
pub op: Operator,
pub rhs: Box<Node>,
}
pub struct UnaryExpr {
pub op: Operator,
pub rhs: Box<Node>,
}
pub struct CallExpr {
pub func: Box<Node>,
pub params: Vec<Node>,
}
pub struct ArrayExpr {
pub array: Box<Node>,
pub index: Box<Node>,
}
#[derive(Debug, PartialEq)]
pub enum Operator {
Dot,
Eq,
EqEq,
Bang,
BangEq,
Gt,
GtGt,
GtEq,
GtGtEq,
Lt,
LtLt,
LtEq,
LtLtEq,
Plus,
PlusEq,
Minus,
MinusEq,
Asterisk,
AsteriskEq,
Slash,
SlashEq,
Percent,
PercentEq,
Pipe,
PipePipe,
PipeEq,
Amp,
AmpAmp,
AmpEq,
Caret,
CaretEq,
}
impl Node {
pub fn assert_ident(&self) -> Result<&String, Error> {
match self {
Node::Ident(_, name) => Ok(name),
_ => Err(Error::syntax_error(
self.pos().clone(),
format!("Expected an identifier, got {}", self),
)),
}
}
pub fn walk(&self, cb: fn(node: &Node, depth: u32)) {
self.visit(cb, 0);
}
pub fn pos(&self) -> &Position {
match self {
Node::Break(pos) => pos,
Node::Ident(pos, _) => pos,
Node::Bool(pos, _) => pos,
Node::Int(pos, _) => pos,
Node::String(pos, _) => pos,
Node::Block(pos, _) => pos,
Node::Fn(pos, _) => pos,
Node::ReturnStmt(pos, _) => pos,
Node::DepList(pos, _) => pos,
Node::SourceList(pos, _) => pos,
Node::Array(pos, _) => pos,
Node::ArrayExpr(pos, _) => pos,
Node::CallExpr(pos, _) => pos,
Node::IfStmt(pos, _) => pos,
Node::ForStmt(pos, _) => pos,
Node::WhileStmt(pos, _) => pos,
Node::UnaryExpr(pos, _) => pos,
Node::BinaryExpr(pos, _) => pos,
Node::TypeStmt(pos, _) => pos,
Node::SetStmt(pos, _) => pos,
Node::TargetStmt(pos, _) => pos,
Node::File(pos, _) => pos,
}
}
fn visit(&self, cb: fn(node: &Node, depth: u32), current_depth: u32) {
cb(self, current_depth);
let depth = current_depth + 1;
match self {
Node::DepList(_, list) => list.visit(cb, depth),
Node::SourceList(_, list) => list.visit(cb, depth),
Node::Array(_, elements) => {
for node in elements {
node.visit(cb, depth);
}
}
Node::Block(_, statements) => {
for node in statements {
node.visit(cb, depth);
}
}
Node::Fn(_, func) => {
if let Some(n) = &func.name {
n.visit(cb, depth);
}
for p in &func.params {
p.visit(cb, depth);
}
func.body.visit(cb, depth);
}
Node::ArrayExpr(_, expr) => {
expr.array.visit(cb, depth);
expr.index.visit(cb, depth);
}
Node::CallExpr(_, expr) => {
expr.func.visit(cb, depth);
for p in &expr.params {
p.visit(cb, depth);
}
}
Node::IfStmt(_, stmt) => {
stmt.condition.visit(cb, depth);
stmt.then_body.visit(cb, depth);
if let Some(eb) = &stmt.else_body {
eb.visit(cb, depth);
}
}
Node::ForStmt(_, stmt) => {
if let Some(s) = &stmt.setup {
s.visit(cb, depth);
}
if let Some(c) = &stmt.condition {
c.visit(cb, depth);
}
if let Some(e) = &stmt.exec {
e.visit(cb, depth);
}
stmt.body.visit(cb, depth);
}
Node::WhileStmt(_, stmt) => {
stmt.condition.visit(cb, depth);
stmt.body.visit(cb, depth);
}
Node::ReturnStmt(_, node) => node.visit(cb, depth),
Node::UnaryExpr(_, expr) => expr.rhs.visit(cb, depth),
Node::BinaryExpr(_, expr) => {
expr.lhs.visit(cb, depth);
expr.rhs.visit(cb, depth);
}
Node::TypeStmt(_, expr) => expr.visit(cb, depth),
Node::SetStmt(_, expr) => {
expr.name.visit(cb, depth);
expr.val.visit(cb, depth);
}
Node::TargetStmt(_, stmt) => {
stmt.name.visit(cb, depth);
stmt.val.visit(cb, depth);
}
Node::File(_, file) => {
for n in &file.content {
n.visit(cb, depth);
}
}
_ => return,
}
}
pub fn make_fn(pos: Position, name: Option<Node>, params: Vec<Node>, body: Node) -> Node {
let name = if let Some(name) = name {
Some(Box::new(name))
} else {
None
};
Node::Fn(pos, FnStmt { name, params, body: Box::new(body) })
}
pub fn make_binary_expr(pos: Position, lhs: Node, op: Operator, rhs: Node) -> Node {
Node::BinaryExpr(pos, BinaryExpr { lhs: Box::new(lhs), op, rhs: Box::new(rhs) })
}
pub fn make_unary_expr(pos: Position, op: Operator, rhs: Node) -> Node {
Node::UnaryExpr(pos, UnaryExpr { op, rhs: Box::new(rhs) })
}
pub fn make_call_expr(pos: Position, func: Node, params: Vec<Node>) -> Node {
Node::CallExpr(pos, CallExpr { func: Box::new(func), params })
}
pub fn make_array_expr(pos: Position, array: Node, index: Node) -> Node {
Node::ArrayExpr(pos, ArrayExpr { array: Box::new(array), index: Box::new(index) })
}
pub fn make_set_stmt(pos: Position, name: Node, val: Node) -> Node {
Node::SetStmt(pos, SetStmt { name: Box::new(name), val: Box::new(val) })
}
pub fn make_target_stmt(pos: Position, name: Node, val: Node) -> Node {
Node::TargetStmt(pos, TargetStmt { name: Box::new(name), val: Box::new(val) })
}
pub fn make_if_stmt(pos: Position, condition: Node, then_body: Node, else_body: Option<Node>) -> Node {
let else_body = if let Some(else_body) = else_body {
Some(Box::new(else_body))
} else {
None
};
Node::IfStmt(pos, IfStmt {
condition: Box::new(condition),
then_body: Box::new(then_body),
else_body,
})
}
pub fn make_while_stmt(pos: Position, condition: Node, body: Node) -> Node {
Node::WhileStmt(pos, WhileStmt {
condition: Box::new(condition),
body: Box::new(body)
})
}
pub fn make_for_stmt(pos: Position, setup: Option<Node>, condition: Option<Node>, exec: Option<Node>, body: Node) -> Node {
let setup = if let Some(setup) = setup {
Some(Box::new(setup))
} else {
None
};
let condition = if let Some(condition) = condition {
Some(Box::new(condition))
} else {
None
};
let exec = if let Some(exec) = exec {
Some(Box::new(exec))
} else {
None
};
Node::ForStmt(pos, ForStmt { setup, condition, exec, body: Box::new(body) })
}
}
impl fmt::Display for Node {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let tmp: String;
write!(
f,
"{}",
match self {
Node::Break(_) => "<break>",
Node::Ident(_, name) => name.as_str(),
Node::Bool(_, b) => {
tmp = format!("{}", b);
tmp.as_str()
}
Node::Int(_, i) => {
tmp = format!("{}", i);
tmp.as_str()
}
Node::String(_, s) => s.as_str(),
Node::Block(_, _) => "<block>",
Node::Fn(_, _) => "fn",
Node::ReturnStmt(_, _) => "return",
Node::DepList(_, _) => "depend",
Node::SourceList(_, _) => "source",
Node::Array(_, _) => "<array>",
Node::ArrayExpr(_, _) => "<array-access>",
Node::CallExpr(_, _) => "<call>",
Node::IfStmt(_, _) => "<if>",
Node::ForStmt(_, _) => "<for>",
Node::WhileStmt(_, _) => "<while>",
Node::UnaryExpr(_, expr) => expr.op.raw(),
Node::BinaryExpr(_, expr) => expr.op.raw(),
Node::TypeStmt(_, _) => "type",
Node::SetStmt(_, _) => "set",
Node::TargetStmt(_, _) => "target",
Node::File(pos, _) => pos.file.as_str(),
}
)
}
}
impl Operator {
pub fn from_token(token: &Token) -> Result<Operator, Error> {
match token.kind {
token::Kind::Eq => Ok(Operator::Eq),
token::Kind::EqEq => Ok(Operator::EqEq),
token::Kind::BangEq => Ok(Operator::BangEq),
token::Kind::Bang => Ok(Operator::Bang),
token::Kind::Gt => Ok(Operator::Gt),
token::Kind::GtGt => Ok(Operator::GtGt),
token::Kind::GtEq => Ok(Operator::GtEq),
token::Kind::GtGtEq => Ok(Operator::GtGtEq),
token::Kind::Lt => Ok(Operator::Lt),
token::Kind::LtLt => Ok(Operator::LtLt),
token::Kind::LtEq => Ok(Operator::LtEq),
token::Kind::LtLtEq => Ok(Operator::LtLtEq),
token::Kind::Plus => Ok(Operator::Plus),
token::Kind::PlusEq => Ok(Operator::PlusEq),
token::Kind::Minus => Ok(Operator::Minus),
token::Kind::MinusEq => Ok(Operator::MinusEq),
token::Kind::Asterisk => Ok(Operator::Asterisk),
token::Kind::AsteriskEq => Ok(Operator::AsteriskEq),
token::Kind::Slash => Ok(Operator::Slash),
token::Kind::SlashEq => Ok(Operator::SlashEq),
token::Kind::Percent => Ok(Operator::Percent),
token::Kind::PercentEq => Ok(Operator::PercentEq),
token::Kind::Pipe => Ok(Operator::Pipe),
token::Kind::PipePipe => Ok(Operator::PipePipe),
token::Kind::PipeEq => Ok(Operator::PipeEq),
token::Kind::Amp => Ok(Operator::Amp),
token::Kind::AmpAmp => Ok(Operator::AmpAmp),
token::Kind::AmpEq => Ok(Operator::AmpEq),
token::Kind::Caret => Ok(Operator::Caret),
token::Kind::CaretEq => Ok(Operator::CaretEq),
_ => Err(Error::syntax_error(
token.pos.clone(),
format!("\"{}\" is not an operator", token.raw),
)),
}
}
pub fn raw(&self) -> &'static str {
match self {
Operator::Dot => ".",
Operator::Eq => "=",
Operator::EqEq => "==",
Operator::BangEq => "!=",
Operator::Gt => ">",
Operator::GtGt => ">>",
Operator::GtEq => ">=",
Operator::GtGtEq => ">>=",
Operator::Lt => "<",
Operator::LtLt => "<<",
Operator::LtEq => "<=",
Operator::LtLtEq => "<<=",
Operator::Plus => "+",
Operator::PlusEq => "+=",
Operator::Minus => "-",
Operator::MinusEq => "-=",
Operator::Asterisk => "*",
Operator::AsteriskEq => "*=",
Operator::Slash => "/",
Operator::SlashEq => "/=",
Operator::Percent => "%",
Operator::PercentEq => "%=",
Operator::Bang => "!",
Operator::Pipe => "|",
Operator::PipePipe => "||",
Operator::PipeEq => "|=",
Operator::Amp => "&",
Operator::AmpAmp => "&&",
Operator::AmpEq => "&=",
Operator::Caret => "^",
Operator::CaretEq => "^=",
}
}
}
impl fmt::Display for Operator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.raw())
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Type {
/// For identifier tokens (evaluates at runtime)
Unknown,
/// For expressions that don't emit a value
Void,
Bool,
Int,
String,
Array,
Fn,
}
impl Type {
pub fn from_token(token: &Token) -> Option<Type> {
match token.kind {
token::Kind::IntLiteral => Some(Type::Int),
token::Kind::StringLiteral => Some(Type::String),
_ => None,
}
}
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
Type::Unknown => "<unknown>",
Type::Void => "()",
Type::Bool => "bool",
Type::Int => "int",
Type::String => "string",
Type::Array => "array",
Type::Fn => "fn",
}
)
}
}