use super::span::{Span, TokenRange};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StringKind {
Single,
Double,
HereSingle,
HereDouble,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct CSharpParam {
pub type_name: String,
pub name: String,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct CSharpImport {
pub dll: String,
pub function: String,
pub returns: String,
pub params: Vec<CSharpParam>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct CSharpMemberDef {
pub code: String,
pub imports: Vec<CSharpImport>,
pub apis: Vec<String>,
pub parameter: String,
}
#[derive(Debug, Clone)]
pub struct Node {
pub kind: NodeKind,
pub span: Span,
pub range: TokenRange,
}
#[derive(Debug, Clone)]
pub enum NodeKind {
Script(Vec<Node>),
ParamBlock(Vec<Node>),
Pipeline(Vec<Node>),
PipelineChain {
left: Box<Node>,
op: String,
right: Box<Node>,
},
Command {
name: Box<Node>,
invocation: bool,
elements: Vec<Node>,
redirections: Vec<Node>,
csharp: Option<Box<Node>>,
},
CSharpMemberDef(CSharpMemberDef),
CommandParameter {
name: String,
argument: Option<Box<Node>>,
},
Redirection {
op: String,
target: Option<Box<Node>>,
},
Assignment {
target: Box<Node>,
op: String,
value: Box<Node>,
},
If {
conditions: Vec<Node>,
blocks: Vec<Node>,
else_block: Option<Box<Node>>,
},
While {
condition: Box<Node>,
body: Box<Node>,
},
DoWhile {
body: Box<Node>,
condition: Box<Node>,
until: bool,
},
For {
init: Option<Box<Node>>,
condition: Option<Box<Node>>,
update: Option<Box<Node>>,
body: Box<Node>,
},
ForEach {
variable: Box<Node>,
iterable: Box<Node>,
body: Box<Node>,
},
Switch {
input: Box<Node>,
cases: Vec<Node>,
},
Function {
name: String,
filter: bool,
body: Box<Node>,
},
Try {
body: Box<Node>,
catches: Vec<Node>,
finally_block: Option<Box<Node>>,
},
Catch {
body: Box<Node>,
},
Using {
kind: String,
name: String,
},
ClassDefinition {
name: String,
bases: Vec<String>,
members: Vec<Node>,
},
ClassMember {
member_kind: String,
name: String,
parameters: Vec<Node>,
default: Option<Box<Node>>,
body: Option<Box<Node>>,
},
EnumDefinition {
name: String,
base: String,
members: Vec<Node>,
},
Flow {
keyword: String,
value: Option<Box<Node>>,
},
Ternary {
condition: Box<Node>,
if_true: Box<Node>,
if_false: Box<Node>,
},
Binary {
op: String,
left: Box<Node>,
right: Box<Node>,
},
Unary {
op: String,
operand: Box<Node>,
},
PostfixUnary {
op: String,
operand: Box<Node>,
},
Cast {
type_name: String,
operand: Box<Node>,
},
MemberAccess {
target: Box<Node>,
member: String,
is_static: bool,
},
InvokeMember {
target: Box<Node>,
member: String,
is_static: bool,
args: Vec<Node>,
},
Index {
target: Box<Node>,
index: Box<Node>,
},
Paren(Box<Node>),
SubExpression(Box<Node>),
ScriptBlockExpression(Box<Node>),
Array(Vec<Node>),
ArrayLiteral(Vec<Node>),
Hashtable(Vec<(Node, Node)>),
Variable(String),
Number(String),
StringLiteral {
kind: StringKind,
value: String,
parts: Vec<Node>,
},
TypeExpression(String),
BareWord(String),
Error(String),
}
impl Node {
pub fn label(&self) -> &'static str {
use NodeKind::*;
match &self.kind {
Script(_) => "ScriptBlock",
ParamBlock(_) => "ParamBlock",
Pipeline(_) => "Pipeline",
PipelineChain { .. } => "PipelineChain",
Command { .. } => "Command",
CSharpMemberDef(_) => "CSharpMemberDef",
CommandParameter { .. } => "CommandParameter",
Redirection { .. } => "Redirection",
Assignment { .. } => "AssignmentStatement",
If { .. } => "IfStatement",
While { .. } => "WhileStatement",
DoWhile { .. } => "WhileStatement",
For { .. } => "ForStatement",
ForEach { .. } => "ForEachStatement",
Switch { .. } => "SwitchStatement",
Function { .. } => "FunctionDefinition",
Try { .. } => "TryStatement",
Catch { .. } => "CatchClause",
Using { .. } => "UsingStatement",
ClassDefinition { .. } => "ClassDefinition",
ClassMember { .. } => "ClassMember",
EnumDefinition { .. } => "EnumDefinition",
Flow { keyword, .. } => match keyword.as_str() {
"return" => "ReturnStatement",
"throw" => "ThrowStatement",
_ => "FlowStatement",
},
Ternary { .. } => "TernaryExpression",
Binary { .. } => "BinaryExpression",
Unary { .. } => "UnaryExpression",
PostfixUnary { .. } => "UnaryExpression",
Cast { .. } => "CastExpression",
MemberAccess { .. } => "MemberAccess",
InvokeMember { .. } => "InvokeMember",
Index { .. } => "IndexExpression",
Paren(_) => "ParenExpression",
SubExpression(_) => "SubExpression",
ScriptBlockExpression(_) => "ScriptBlockExpression",
Array(_) => "ArrayExpression",
ArrayLiteral(_) => "ArrayLiteral",
Hashtable(_) => "HashtableExpression",
Variable(_) => "Variable",
Number(_) => "NumberLiteral",
StringLiteral { .. } => "StringLiteral",
TypeExpression(_) => "TypeExpression",
BareWord(_) => "BareWord",
Error(_) => "ErrorNode",
}
}
pub fn for_each_child<'a>(&'a self, f: &mut impl FnMut(&'a Node)) {
use NodeKind::*;
match &self.kind {
Script(v) | ParamBlock(v) | Pipeline(v) | Array(v) | ArrayLiteral(v) => {
for n in v {
f(n);
}
}
ClassDefinition { members, .. } | EnumDefinition { members, .. } => {
for n in members {
f(n);
}
}
PipelineChain { left, right, .. } => {
f(left);
f(right);
}
Command {
name,
elements,
redirections,
csharp,
..
} => {
f(name);
for n in elements {
f(n);
}
if let Some(cs) = csharp {
f(cs);
}
for n in redirections {
f(n);
}
}
CSharpMemberDef(_) => {}
CommandParameter { argument, .. } => {
if let Some(a) = argument {
f(a);
}
}
Redirection { target, .. } => {
if let Some(t) = target {
f(t);
}
}
Assignment { target, value, .. } => {
f(target);
f(value);
}
If {
conditions,
blocks,
else_block,
} => {
for n in conditions {
f(n);
}
for n in blocks {
f(n);
}
if let Some(e) = else_block {
f(e);
}
}
While { condition, body } => {
f(condition);
f(body);
}
DoWhile {
body, condition, ..
} => {
f(body);
f(condition);
}
For {
init,
condition,
update,
body,
} => {
if let Some(n) = init {
f(n);
}
if let Some(n) = condition {
f(n);
}
if let Some(n) = update {
f(n);
}
f(body);
}
ForEach {
variable,
iterable,
body,
} => {
f(variable);
f(iterable);
f(body);
}
Switch { input, cases } => {
f(input);
for n in cases {
f(n);
}
}
Function { body, .. } => f(body),
Try {
body,
catches,
finally_block,
} => {
f(body);
for n in catches {
f(n);
}
if let Some(fin) = finally_block {
f(fin);
}
}
Catch { body } => f(body),
ClassMember {
parameters,
default,
body,
..
} => {
for n in parameters {
f(n);
}
if let Some(d) = default {
f(d);
}
if let Some(b) = body {
f(b);
}
}
Flow { value, .. } => {
if let Some(v) = value {
f(v);
}
}
Ternary {
condition,
if_true,
if_false,
} => {
f(condition);
f(if_true);
f(if_false);
}
Binary { left, right, .. } => {
f(left);
f(right);
}
Unary { operand, .. } | PostfixUnary { operand, .. } | Cast { operand, .. } => {
f(operand)
}
MemberAccess { target, .. } => f(target),
InvokeMember { target, args, .. } => {
f(target);
for n in args {
f(n);
}
}
Index { target, index } => {
f(target);
f(index);
}
Paren(n) | SubExpression(n) | ScriptBlockExpression(n) => f(n),
Hashtable(pairs) => {
for (k, v) in pairs {
f(k);
f(v);
}
}
StringLiteral { parts, .. } => {
for n in parts {
f(n);
}
}
Variable(_) | Number(_) | TypeExpression(_) | BareWord(_) | Using { .. } | Error(_) => {
}
}
}
pub fn children(&self) -> Vec<&Node> {
let mut out = Vec::new();
self.for_each_child(&mut |c| out.push(c));
out
}
pub fn walk(&self, visitor: &mut impl FnMut(&Node)) {
visitor(self);
self.for_each_child(&mut |c| c.walk(visitor));
}
pub fn walk_with_ancestors(&self, visitor: &mut impl FnMut(&Node, &[&Node])) {
fn go<'a>(
node: &'a Node,
stack: &mut Vec<&'a Node>,
visitor: &mut impl FnMut(&Node, &[&Node]),
) {
visitor(node, stack);
stack.push(node);
node.for_each_child(&mut |c| go(c, stack, visitor));
stack.pop();
}
go(self, &mut Vec::new(), visitor);
}
}