use harn_lexer::{Span, StringSegment};
#[derive(Debug, Clone, PartialEq)]
pub struct Spanned<T> {
pub node: T,
pub span: Span,
}
impl<T> Spanned<T> {
pub fn new(node: T, span: Span) -> Self {
Self { node, span }
}
pub fn dummy(node: T) -> Self {
Self {
node,
span: Span::dummy(),
}
}
}
pub type SNode = Spanned<Node>;
pub fn spanned(node: Node, span: Span) -> SNode {
SNode::new(node, span)
}
pub fn peel_attributes(node: &SNode) -> (&[Attribute], &SNode) {
match &node.node {
Node::AttributedDecl { attributes, inner } => (attributes.as_slice(), inner.as_ref()),
_ => (&[], node),
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct AttributeArg {
pub name: Option<String>,
pub value: SNode,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Attribute {
pub name: String,
pub args: Vec<AttributeArg>,
pub span: Span,
}
impl Attribute {
pub fn named_arg(&self, key: &str) -> Option<&SNode> {
self.args
.iter()
.find(|a| a.name.as_deref() == Some(key))
.map(|a| &a.value)
}
pub fn positional(&self, idx: usize) -> Option<&SNode> {
self.args
.iter()
.filter(|a| a.name.is_none())
.nth(idx)
.map(|a| &a.value)
}
pub fn string_arg(&self, key: &str) -> Option<String> {
match self.named_arg(key).map(|n| &n.node) {
Some(Node::StringLiteral(s)) => Some(s.clone()),
Some(Node::RawStringLiteral(s)) => Some(s.clone()),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Node {
AttributedDecl {
attributes: Vec<Attribute>,
inner: Box<SNode>,
},
Pipeline {
name: String,
params: Vec<String>,
return_type: Option<TypeExpr>,
body: Vec<SNode>,
extends: Option<String>,
is_pub: bool,
},
LetBinding {
pattern: BindingPattern,
type_ann: Option<TypeExpr>,
value: Box<SNode>,
},
VarBinding {
pattern: BindingPattern,
type_ann: Option<TypeExpr>,
value: Box<SNode>,
},
OverrideDecl {
name: String,
params: Vec<String>,
body: Vec<SNode>,
},
ImportDecl {
path: String,
},
SelectiveImport {
names: Vec<String>,
path: String,
},
EnumDecl {
name: String,
type_params: Vec<TypeParam>,
variants: Vec<EnumVariant>,
is_pub: bool,
},
StructDecl {
name: String,
type_params: Vec<TypeParam>,
fields: Vec<StructField>,
is_pub: bool,
},
InterfaceDecl {
name: String,
type_params: Vec<TypeParam>,
associated_types: Vec<(String, Option<TypeExpr>)>,
methods: Vec<InterfaceMethod>,
},
ImplBlock {
type_name: String,
methods: Vec<SNode>,
},
IfElse {
condition: Box<SNode>,
then_body: Vec<SNode>,
else_body: Option<Vec<SNode>>,
},
ForIn {
pattern: BindingPattern,
iterable: Box<SNode>,
body: Vec<SNode>,
},
MatchExpr {
value: Box<SNode>,
arms: Vec<MatchArm>,
},
WhileLoop {
condition: Box<SNode>,
body: Vec<SNode>,
},
Retry {
count: Box<SNode>,
body: Vec<SNode>,
},
ReturnStmt {
value: Option<Box<SNode>>,
},
TryCatch {
body: Vec<SNode>,
error_var: Option<String>,
error_type: Option<TypeExpr>,
catch_body: Vec<SNode>,
finally_body: Option<Vec<SNode>>,
},
TryExpr {
body: Vec<SNode>,
},
FnDecl {
name: String,
type_params: Vec<TypeParam>,
params: Vec<TypedParam>,
return_type: Option<TypeExpr>,
where_clauses: Vec<WhereClause>,
body: Vec<SNode>,
is_pub: bool,
},
ToolDecl {
name: String,
description: Option<String>,
params: Vec<TypedParam>,
return_type: Option<TypeExpr>,
body: Vec<SNode>,
is_pub: bool,
},
TypeDecl {
name: String,
type_params: Vec<TypeParam>,
type_expr: TypeExpr,
},
SpawnExpr {
body: Vec<SNode>,
},
DurationLiteral(u64),
RangeExpr {
start: Box<SNode>,
end: Box<SNode>,
inclusive: bool,
},
GuardStmt {
condition: Box<SNode>,
else_body: Vec<SNode>,
},
RequireStmt {
condition: Box<SNode>,
message: Option<Box<SNode>>,
},
DeferStmt {
body: Vec<SNode>,
},
DeadlineBlock {
duration: Box<SNode>,
body: Vec<SNode>,
},
YieldExpr {
value: Option<Box<SNode>>,
},
MutexBlock {
body: Vec<SNode>,
},
BreakStmt,
ContinueStmt,
Parallel {
mode: ParallelMode,
expr: Box<SNode>,
variable: Option<String>,
body: Vec<SNode>,
options: Vec<(String, SNode)>,
},
SelectExpr {
cases: Vec<SelectCase>,
timeout: Option<(Box<SNode>, Vec<SNode>)>,
default_body: Option<Vec<SNode>>,
},
FunctionCall {
name: String,
args: Vec<SNode>,
},
MethodCall {
object: Box<SNode>,
method: String,
args: Vec<SNode>,
},
OptionalMethodCall {
object: Box<SNode>,
method: String,
args: Vec<SNode>,
},
PropertyAccess {
object: Box<SNode>,
property: String,
},
OptionalPropertyAccess {
object: Box<SNode>,
property: String,
},
SubscriptAccess {
object: Box<SNode>,
index: Box<SNode>,
},
SliceAccess {
object: Box<SNode>,
start: Option<Box<SNode>>,
end: Option<Box<SNode>>,
},
BinaryOp {
op: String,
left: Box<SNode>,
right: Box<SNode>,
},
UnaryOp {
op: String,
operand: Box<SNode>,
},
Ternary {
condition: Box<SNode>,
true_expr: Box<SNode>,
false_expr: Box<SNode>,
},
Assignment {
target: Box<SNode>,
value: Box<SNode>,
op: Option<String>,
},
ThrowStmt {
value: Box<SNode>,
},
EnumConstruct {
enum_name: String,
variant: String,
args: Vec<SNode>,
},
StructConstruct {
struct_name: String,
fields: Vec<DictEntry>,
},
InterpolatedString(Vec<StringSegment>),
StringLiteral(String),
RawStringLiteral(String),
IntLiteral(i64),
FloatLiteral(f64),
BoolLiteral(bool),
NilLiteral,
Identifier(String),
ListLiteral(Vec<SNode>),
DictLiteral(Vec<DictEntry>),
Spread(Box<SNode>),
TryOperator {
operand: Box<SNode>,
},
TryStar {
operand: Box<SNode>,
},
Block(Vec<SNode>),
Closure {
params: Vec<TypedParam>,
body: Vec<SNode>,
fn_syntax: bool,
},
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ParallelMode {
Count,
Each,
Settle,
}
#[derive(Debug, Clone, PartialEq)]
pub struct MatchArm {
pub pattern: SNode,
pub guard: Option<Box<SNode>>,
pub body: Vec<SNode>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct SelectCase {
pub variable: String,
pub channel: Box<SNode>,
pub body: Vec<SNode>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct DictEntry {
pub key: SNode,
pub value: SNode,
}
#[derive(Debug, Clone, PartialEq)]
pub struct EnumVariant {
pub name: String,
pub fields: Vec<TypedParam>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct StructField {
pub name: String,
pub type_expr: Option<TypeExpr>,
pub optional: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct InterfaceMethod {
pub name: String,
pub type_params: Vec<TypeParam>,
pub params: Vec<TypedParam>,
pub return_type: Option<TypeExpr>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum TypeExpr {
Named(String),
Union(Vec<TypeExpr>),
Shape(Vec<ShapeField>),
List(Box<TypeExpr>),
DictType(Box<TypeExpr>, Box<TypeExpr>),
Iter(Box<TypeExpr>),
Applied { name: String, args: Vec<TypeExpr> },
FnType {
params: Vec<TypeExpr>,
return_type: Box<TypeExpr>,
},
Never,
LitString(String),
LitInt(i64),
}
#[derive(Debug, Clone, PartialEq)]
pub struct ShapeField {
pub name: String,
pub type_expr: TypeExpr,
pub optional: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub enum BindingPattern {
Identifier(String),
Dict(Vec<DictPatternField>),
List(Vec<ListPatternElement>),
Pair(String, String),
}
#[derive(Debug, Clone, PartialEq)]
pub struct DictPatternField {
pub key: String,
pub alias: Option<String>,
pub is_rest: bool,
pub default_value: Option<Box<SNode>>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ListPatternElement {
pub name: String,
pub is_rest: bool,
pub default_value: Option<Box<SNode>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Variance {
Invariant,
Covariant,
Contravariant,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TypeParam {
pub name: String,
pub variance: Variance,
}
impl TypeParam {
pub fn invariant(name: impl Into<String>) -> Self {
Self {
name: name.into(),
variance: Variance::Invariant,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct WhereClause {
pub type_name: String,
pub bound: String,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TypedParam {
pub name: String,
pub type_expr: Option<TypeExpr>,
pub default_value: Option<Box<SNode>>,
pub rest: bool,
}
impl TypedParam {
pub fn untyped(name: impl Into<String>) -> Self {
Self {
name: name.into(),
type_expr: None,
default_value: None,
rest: false,
}
}
pub fn typed(name: impl Into<String>, type_expr: TypeExpr) -> Self {
Self {
name: name.into(),
type_expr: Some(type_expr),
default_value: None,
rest: false,
}
}
pub fn names(params: &[TypedParam]) -> Vec<String> {
params.iter().map(|p| p.name.clone()).collect()
}
pub fn default_start(params: &[TypedParam]) -> Option<usize> {
params.iter().position(|p| p.default_value.is_some())
}
}