323 lines
6.7 KiB
Rust
323 lines
6.7 KiB
Rust
//! Abstract syntax tree definitions for the doot language.
|
|
|
|
use crate::lexer::Span;
|
|
use std::collections::HashMap;
|
|
|
|
/// Identifier type alias.
|
|
pub type Ident = String;
|
|
|
|
/// A parsed doot program.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct Program {
|
|
pub statements: Vec<Spanned<Statement>>,
|
|
}
|
|
|
|
/// Wraps a node with source location information.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct Spanned<T> {
|
|
pub node: T,
|
|
pub span: Span,
|
|
}
|
|
|
|
impl<T> Spanned<T> {
|
|
/// Creates a new spanned node.
|
|
pub fn new(node: T, span: Span) -> Self {
|
|
Self { node, span }
|
|
}
|
|
}
|
|
|
|
/// Top-level statement types.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum Statement {
|
|
VarDecl(VarDecl),
|
|
FnDecl(FnDecl),
|
|
StructDecl(StructDecl),
|
|
EnumDecl(EnumDecl),
|
|
TypeAlias(TypeAlias),
|
|
Import(Import),
|
|
Dotfile(Dotfile),
|
|
Package(Package),
|
|
Secret(Secret),
|
|
Hook(Hook),
|
|
MacroDecl(MacroDecl),
|
|
MacroCall(MacroCall),
|
|
ForLoop(ForLoop),
|
|
If(IfStatement),
|
|
Match(MatchStatement),
|
|
Expr(Expr),
|
|
Return(Option<Expr>),
|
|
}
|
|
|
|
/// Variable declaration.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct VarDecl {
|
|
pub name: Ident,
|
|
pub ty: Option<TypeAnnotation>,
|
|
pub value: Expr,
|
|
}
|
|
|
|
/// Function declaration.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct FnDecl {
|
|
pub name: Ident,
|
|
pub is_async: bool,
|
|
pub params: Vec<FnParam>,
|
|
pub return_type: Option<TypeAnnotation>,
|
|
pub body: Vec<Spanned<Statement>>,
|
|
}
|
|
|
|
/// Function parameter.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct FnParam {
|
|
pub name: Ident,
|
|
pub ty: TypeAnnotation,
|
|
pub default: Option<Expr>,
|
|
}
|
|
|
|
/// Struct type declaration.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct StructDecl {
|
|
pub name: Ident,
|
|
pub fields: Vec<StructField>,
|
|
pub methods: Vec<FnDecl>,
|
|
}
|
|
|
|
/// Struct field definition.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct StructField {
|
|
pub name: Ident,
|
|
pub ty: TypeAnnotation,
|
|
pub default: Option<Expr>,
|
|
}
|
|
|
|
/// Enum type declaration.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct EnumDecl {
|
|
pub name: Ident,
|
|
pub variants: Vec<EnumVariant>,
|
|
}
|
|
|
|
/// Enum variant definition.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct EnumVariant {
|
|
pub name: Ident,
|
|
pub fields: Option<Vec<TypeAnnotation>>,
|
|
}
|
|
|
|
/// Type alias declaration.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct TypeAlias {
|
|
pub name: Ident,
|
|
pub ty: TypeAnnotation,
|
|
}
|
|
|
|
/// Module import statement.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct Import {
|
|
pub path: String,
|
|
pub alias: Option<Ident>,
|
|
}
|
|
|
|
/// Deploy mode for dotfiles.
|
|
#[derive(Clone, Copy, Debug, PartialEq, Default)]
|
|
pub enum DeployMode {
|
|
#[default]
|
|
Copy,
|
|
Link,
|
|
}
|
|
|
|
/// Permission rule - either a single mode or pattern-based.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum PermissionRule {
|
|
Single(u32),
|
|
Pattern { pattern: String, mode: u32 },
|
|
}
|
|
|
|
/// Dotfile deployment declaration.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct Dotfile {
|
|
pub source: Expr,
|
|
pub target: Expr,
|
|
pub when: Option<Expr>,
|
|
pub template: Option<bool>,
|
|
pub permissions: Vec<PermissionRule>,
|
|
pub owner: Option<String>,
|
|
pub deploy: DeployMode,
|
|
pub link_patterns: Vec<String>,
|
|
pub copy_patterns: Vec<String>,
|
|
}
|
|
|
|
/// Package installation declaration.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct Package {
|
|
pub default: Option<Expr>,
|
|
pub brew: Option<PackageSpec>,
|
|
pub apt: Option<PackageSpec>,
|
|
pub pacman: Option<PackageSpec>,
|
|
pub yay: Option<PackageSpec>,
|
|
pub when: Option<Expr>,
|
|
}
|
|
|
|
/// Package manager-specific specification.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct PackageSpec {
|
|
pub name: Expr,
|
|
pub cask: Option<bool>,
|
|
pub tap: Option<String>,
|
|
}
|
|
|
|
/// Encrypted secret file declaration.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct Secret {
|
|
pub source: Expr,
|
|
pub target: Expr,
|
|
pub mode: Option<u32>,
|
|
}
|
|
|
|
/// Lifecycle hook declaration.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct Hook {
|
|
pub stage: HookStage,
|
|
pub run: Expr,
|
|
pub when: Option<Expr>,
|
|
}
|
|
|
|
/// Hook execution stage.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum HookStage {
|
|
BeforeDeploy,
|
|
AfterDeploy,
|
|
BeforePackage,
|
|
AfterPackage,
|
|
}
|
|
|
|
/// Macro definition.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct MacroDecl {
|
|
pub name: Ident,
|
|
pub params: Vec<Ident>,
|
|
pub body: Vec<Spanned<Statement>>,
|
|
}
|
|
|
|
/// Macro invocation.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct MacroCall {
|
|
pub name: Ident,
|
|
pub args: Vec<Expr>,
|
|
}
|
|
|
|
/// For loop statement.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct ForLoop {
|
|
pub var: Ident,
|
|
pub iter: Expr,
|
|
pub body: Vec<Spanned<Statement>>,
|
|
}
|
|
|
|
/// Conditional statement.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct IfStatement {
|
|
pub condition: Expr,
|
|
pub then_body: Vec<Spanned<Statement>>,
|
|
pub else_body: Option<Vec<Spanned<Statement>>>,
|
|
}
|
|
|
|
/// Pattern matching statement.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct MatchStatement {
|
|
pub expr: Expr,
|
|
pub arms: Vec<MatchArm>,
|
|
}
|
|
|
|
/// Single arm in a match statement.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct MatchArm {
|
|
pub pattern: Pattern,
|
|
pub body: Expr,
|
|
}
|
|
|
|
/// Match pattern types.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum Pattern {
|
|
Literal(Literal),
|
|
Ident(Ident),
|
|
EnumVariant { ty: Ident, variant: Ident },
|
|
Wildcard,
|
|
}
|
|
|
|
/// Expression types.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum Expr {
|
|
Literal(Literal),
|
|
Ident(Ident),
|
|
Path(Box<Expr>, Box<Expr>),
|
|
Binary(Box<Expr>, BinOp, Box<Expr>),
|
|
Unary(UnaryOp, Box<Expr>),
|
|
Call(Box<Expr>, Vec<Expr>),
|
|
MethodCall(Box<Expr>, Ident, Vec<Expr>),
|
|
Index(Box<Expr>, Box<Expr>),
|
|
Field(Box<Expr>, Ident),
|
|
EnumVariant(Ident, Ident),
|
|
StructInit(Ident, HashMap<Ident, Expr>),
|
|
List(Vec<Expr>),
|
|
If(Box<Expr>, Box<Expr>, Option<Box<Expr>>),
|
|
Lambda(Vec<FnParam>, Box<Expr>),
|
|
Await(Box<Expr>),
|
|
Interpolated(Vec<InterpolatedPart>),
|
|
HomePath(Box<Expr>),
|
|
}
|
|
|
|
/// Part of an interpolated string.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum InterpolatedPart {
|
|
Literal(String),
|
|
Expr(Expr),
|
|
}
|
|
|
|
/// Literal value types.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum Literal {
|
|
Int(i64),
|
|
Float(f64),
|
|
Str(String),
|
|
Bool(bool),
|
|
None,
|
|
}
|
|
|
|
/// Binary operators.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum BinOp {
|
|
Add,
|
|
Sub,
|
|
Mul,
|
|
Div,
|
|
Mod,
|
|
Eq,
|
|
NotEq,
|
|
Lt,
|
|
Gt,
|
|
LtEq,
|
|
GtEq,
|
|
And,
|
|
Or,
|
|
PathJoin,
|
|
NullCoalesce,
|
|
}
|
|
|
|
/// Unary operators.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum UnaryOp {
|
|
Neg,
|
|
Not,
|
|
}
|
|
|
|
/// Type annotation in source code.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum TypeAnnotation {
|
|
Simple(Ident),
|
|
List(Box<TypeAnnotation>),
|
|
Optional(Box<TypeAnnotation>),
|
|
Function(Vec<TypeAnnotation>, Box<TypeAnnotation>),
|
|
Union(Vec<TypeAnnotation>),
|
|
Literal(Literal),
|
|
}
|