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.

288 lines
8.2 KiB
Rust

use std::fmt;
use std::fmt::Formatter;
use crate::error::Error;
use crate::lex::token;
use crate::lex::token::Token;
#[derive(Debug)]
pub enum Node {
Ident(String),
DepList(Box<Node>),
SourceList(Box<Node>),
Int(i128),
String(String),
Array(Vec<Node>),
ArrayExpr {
// array access
array: Box<Node>,
index: Box<Node>,
},
CallExpr {
// function call
func: Box<Node>,
params: Vec<Node>,
},
UnaryExpr {
op: Operator,
node: Box<Node>,
},
BinaryExpr {
op: Operator,
lhs: Box<Node>,
rhs: Box<Node>,
},
TypeExpr(Box<Node>),
SetExpr {
name: Box<Node>,
val: Box<Node>,
},
Target {
name: Box<Node>,
content: Vec<Node>,
},
File {
name: String,
content: Vec<Node>,
},
}
#[derive(Debug, PartialEq)]
pub enum Operator {
Eq,
EqEq,
Bang,
BangEq,
Gt,
GtGt,
GtEq,
GtGtEq,
Lt,
LtLt,
LtEq,
LtLtEq,
Plus,
PlusEq,
Minus,
MinusEq,
Asterisk,
AsteriskAsterisk,
AsteriskEq,
AsteriskAsteriskEq,
Slash,
SlashEq,
Percent,
PercentEq,
Pipe,
PipePipe,
PipeEq,
Amp,
AmpAmp,
AmpEq,
Caret,
CaretEq,
}
impl Node {
pub fn walk(&self, cb: fn(node: &Node, depth: u32)) {
self.visit(cb, 0);
}
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::ArrayExpr { array, index } => {
array.visit(cb, depth);
index.visit(cb, depth);
}
Node::CallExpr { func, params } => {
func.visit(cb, depth);
for p in params {
p.visit(cb, depth);
}
}
Node::UnaryExpr { op, node } => node.visit(cb, depth),
Node::BinaryExpr { op, lhs, rhs } => {
lhs.visit(cb, depth);
rhs.visit(cb, depth);
}
Node::TypeExpr(node) => node.visit(cb, depth),
Node::SetExpr { name, val } => {
name.visit(cb, depth);
val.visit(cb, depth);
}
Node::Target { name, content } => {
name.visit(cb, depth);
for n in content {
n.visit(cb, depth);
}
}
Node::File { name, content } => {
for n in content {
n.visit(cb, depth);
}
}
_ => return,
}
}
}
impl fmt::Display for Node {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let tmp: String;
write!(
f,
"{}",
match self {
Node::Ident(name) => name.as_str(),
Node::Int(i) => {
tmp = format!("{}", i);
tmp.as_str()
}
Node::String(s) => s.as_str(),
Node::DepList(_) => "depend",
Node::SourceList(_) => "source",
Node::Array(_) => "<array>",
Node::ArrayExpr { array, index } => "<array-access>",
Node::CallExpr { func, params } => "<call>",
Node::UnaryExpr { op, node } => op.raw(),
Node::BinaryExpr { op, lhs, rhs } => op.raw(),
Node::TypeExpr(_) => "type",
Node::SetExpr { name, val } => "set",
Node::Target { name, content } => "target",
Node::File { name, content } => "file",
}
)
}
}
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::AsteriskAsterisk => Ok(Operator::AsteriskAsterisk),
token::Kind::AsteriskEq => Ok(Operator::AsteriskEq),
token::Kind::AsteriskAsteriskEq => Ok(Operator::AsteriskAsteriskEq),
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::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::AsteriskAsterisk => "**",
Operator::AsteriskEq => "*=",
Operator::AsteriskAsteriskEq => "**=",
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 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
None,
Int,
String,
}
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::None => "()",
Type::Int => "int",
Type::String => "string",
}
)
}
}