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.
363 lines
10 KiB
Rust
363 lines
10 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 {
|
|
Break,
|
|
Ident(String),
|
|
DepList(Box<Node>),
|
|
SourceList(Box<Node>),
|
|
Bool(bool),
|
|
Int(i128),
|
|
String(String),
|
|
Array(Vec<Node>),
|
|
Block(Vec<Node>),
|
|
Fn {
|
|
name: Option<Box<Node>>,
|
|
params: Vec<Node>,
|
|
body: Box<Node>,
|
|
},
|
|
ArrayExpr {
|
|
// array access
|
|
array: Box<Node>,
|
|
index: Box<Node>,
|
|
},
|
|
CallExpr {
|
|
// function call
|
|
func: Box<Node>,
|
|
params: Vec<Node>,
|
|
},
|
|
IfStmt {
|
|
condition: Box<Node>,
|
|
then_block: Box<Node>,
|
|
else_block: Option<Box<Node>>,
|
|
},
|
|
ReturnStmt(Box<Node>),
|
|
ForStmt {
|
|
setup: Option<Box<Node>>,
|
|
condition: Option<Box<Node>>,
|
|
exec: Option<Box<Node>>,
|
|
body: Box<Node>,
|
|
},
|
|
WhileStmt {
|
|
condition: Box<Node>,
|
|
block: Box<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: Box<Node>,
|
|
},
|
|
File {
|
|
name: String,
|
|
content: Vec<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 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::Block(statements) => {
|
|
for node in statements {
|
|
node.visit(cb, depth);
|
|
}
|
|
}
|
|
Node::Fn { name, params, body } => {
|
|
if let Some(n) = name {
|
|
n.visit(cb, depth);
|
|
}
|
|
for p in params {
|
|
p.visit(cb, depth);
|
|
}
|
|
body.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::IfStmt {
|
|
condition,
|
|
then_block,
|
|
else_block,
|
|
} => {
|
|
condition.visit(cb, depth);
|
|
then_block.visit(cb, depth);
|
|
if let Some(eb) = else_block {
|
|
eb.visit(cb, depth);
|
|
}
|
|
}
|
|
Node::ForStmt { setup, condition, exec, body } => {
|
|
if let Some(s) = setup {
|
|
s.visit(cb, depth);
|
|
}
|
|
if let Some(c) = condition {
|
|
c.visit(cb, depth);
|
|
}
|
|
if let Some(e) = exec {
|
|
e.visit(cb, depth);
|
|
}
|
|
body.visit(cb, depth);
|
|
}
|
|
Node::WhileStmt { condition, block } => {
|
|
condition.visit(cb, depth);
|
|
block.visit(cb, depth);
|
|
}
|
|
Node::ReturnStmt(node) => node.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);
|
|
content.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::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 { name, params, body } => "fn",
|
|
Node::ReturnStmt(_) => "return",
|
|
Node::DepList(_) => "depend",
|
|
Node::SourceList(_) => "source",
|
|
Node::Array(_) => "<array>",
|
|
Node::ArrayExpr { array, index } => "<array-access>",
|
|
Node::CallExpr { func, params } => "<call>",
|
|
Node::IfStmt {
|
|
condition,
|
|
then_block,
|
|
else_block,
|
|
} => "<if>",
|
|
Node::ForStmt { setup, condition, exec, body } => "<for>",
|
|
Node::WhileStmt { condition, block } => "<while>",
|
|
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::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 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",
|
|
}
|
|
)
|
|
}
|
|
}
|