use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Comment {
pub kind: CommentKind,
pub span: Span,
}
impl Comment {
#[must_use]
pub fn new(kind: CommentKind, span: Span) -> Self {
Self { kind, span }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum CommentKind {
Line(String),
Doc(String),
Block(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
pub struct Span {
pub start: usize,
pub end: usize,
}
impl Span {
#[must_use]
pub fn new(start: usize, end: usize) -> Self {
Self { start, end }
}
#[must_use]
pub fn merge(self, other: Self) -> Self {
Self {
start: self.start.min(other.start),
end: self.end.max(other.end),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CatchClause {
pub pattern: Pattern,
pub body: Box<Expr>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ComprehensionClause {
pub variable: String,
pub iterable: Box<Expr>,
pub condition: Option<Box<Expr>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Expr {
pub kind: ExprKind,
pub span: Span,
pub attributes: Vec<Attribute>,
pub leading_comments: Vec<Comment>,
pub trailing_comment: Option<Comment>,
}
impl Expr {
#[must_use]
pub fn new(kind: ExprKind, span: Span) -> Self {
Self {
kind,
span,
attributes: Vec::new(),
leading_comments: Vec::new(),
trailing_comment: None,
}
}
#[must_use]
pub fn with_comments(
kind: ExprKind,
span: Span,
leading_comments: Vec<Comment>,
trailing_comment: Option<Comment>,
) -> Self {
Self {
kind,
span,
attributes: Vec::new(),
leading_comments,
trailing_comment,
}
}
#[must_use]
pub fn with_attributes(kind: ExprKind, span: Span, attributes: Vec<Attribute>) -> Self {
Self {
kind,
span,
attributes,
leading_comments: Vec::new(),
trailing_comment: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ExprKind {
Literal(Literal),
Identifier(String),
QualifiedName {
module: String,
name: String,
},
StringInterpolation {
parts: Vec<StringPart>,
},
Binary {
left: Box<Expr>,
op: BinaryOp,
right: Box<Expr>,
},
Unary {
op: UnaryOp,
operand: Box<Expr>,
},
Throw {
expr: Box<Expr>,
},
TryCatch {
try_block: Box<Expr>,
catch_clauses: Vec<CatchClause>,
finally_block: Option<Box<Expr>>,
},
Ok {
value: Box<Expr>,
},
Err {
error: Box<Expr>,
},
Some {
value: Box<Expr>,
},
None,
TypeCast {
expr: Box<Expr>,
target_type: String,
},
Ternary {
condition: Box<Expr>,
true_expr: Box<Expr>,
false_expr: Box<Expr>,
},
Try {
expr: Box<Expr>,
},
Await {
expr: Box<Expr>,
},
Spawn {
actor: Box<Expr>,
},
AsyncBlock {
body: Box<Expr>,
},
Lazy {
expr: Box<Expr>,
},
If {
condition: Box<Expr>,
then_branch: Box<Expr>,
else_branch: Option<Box<Expr>>,
},
IfLet {
pattern: Pattern,
expr: Box<Expr>,
then_branch: Box<Expr>,
else_branch: Option<Box<Expr>>,
},
Let {
name: String,
type_annotation: Option<Type>,
value: Box<Expr>,
body: Box<Expr>,
is_mutable: bool,
else_block: Option<Box<Expr>>, },
LetPattern {
pattern: Pattern,
type_annotation: Option<Type>,
value: Box<Expr>,
body: Box<Expr>,
is_mutable: bool,
else_block: Option<Box<Expr>>, },
Function {
name: String,
type_params: Vec<String>,
params: Vec<Param>,
return_type: Option<Type>,
body: Box<Expr>,
is_async: bool,
is_pub: bool,
},
Lambda {
params: Vec<Param>,
body: Box<Expr>,
},
AsyncLambda {
params: Vec<String>,
body: Box<Expr>,
},
Struct {
name: String,
type_params: Vec<String>,
fields: Vec<StructField>,
methods: Vec<ClassMethod>, derives: Vec<String>, is_pub: bool,
},
TupleStruct {
name: String,
type_params: Vec<String>,
fields: Vec<Type>, derives: Vec<String>,
is_pub: bool,
},
Class {
name: String,
type_params: Vec<String>,
superclass: Option<String>, traits: Vec<String>, fields: Vec<StructField>,
constructors: Vec<Constructor>, methods: Vec<ClassMethod>,
constants: Vec<ClassConstant>, properties: Vec<ClassProperty>, derives: Vec<String>, decorators: Vec<Decorator>, is_pub: bool,
is_sealed: bool, is_abstract: bool, },
Enum {
name: String,
type_params: Vec<String>,
variants: Vec<EnumVariant>,
is_pub: bool,
},
StructLiteral {
name: String,
fields: Vec<(String, Expr)>,
base: Option<Box<Expr>>, },
ObjectLiteral {
fields: Vec<ObjectField>,
},
FieldAccess {
object: Box<Expr>,
field: String,
},
OptionalFieldAccess {
object: Box<Expr>,
field: String,
},
IndexAccess {
object: Box<Expr>,
index: Box<Expr>,
},
Slice {
object: Box<Expr>,
start: Option<Box<Expr>>,
end: Option<Box<Expr>>,
},
Trait {
name: String,
type_params: Vec<String>,
associated_types: Vec<String>, methods: Vec<TraitMethod>,
is_pub: bool,
},
Impl {
type_params: Vec<String>,
trait_name: Option<String>,
for_type: String,
methods: Vec<ImplMethod>,
is_pub: bool,
},
Actor {
name: String,
state: Vec<StructField>,
handlers: Vec<ActorHandler>,
},
Effect {
name: String,
operations: Vec<EffectOperation>,
},
Handle {
expr: Box<Expr>,
handlers: Vec<EffectHandler>,
},
Send {
actor: Box<Expr>,
message: Box<Expr>,
},
Command {
program: String,
args: Vec<String>,
env: Vec<(String, String)>,
working_dir: Option<String>,
},
Ask {
actor: Box<Expr>,
message: Box<Expr>,
timeout: Option<Box<Expr>>,
},
ActorSend {
actor: Box<Expr>,
message: Box<Expr>,
},
ActorQuery {
actor: Box<Expr>,
message: Box<Expr>,
},
Call {
func: Box<Expr>,
args: Vec<Expr>,
},
Macro {
name: String,
args: Vec<Expr>,
},
MethodCall {
receiver: Box<Expr>,
method: String,
args: Vec<Expr>,
},
OptionalMethodCall {
receiver: Box<Expr>,
method: String,
args: Vec<Expr>,
},
Block(Vec<Expr>),
Pipeline {
expr: Box<Expr>,
stages: Vec<PipelineStage>,
},
Match {
expr: Box<Expr>,
arms: Vec<MatchArm>,
},
List(Vec<Expr>),
Set(Vec<Expr>),
ArrayInit {
value: Box<Expr>,
size: Box<Expr>,
},
Tuple(Vec<Expr>),
Spread {
expr: Box<Expr>,
},
ListComprehension {
element: Box<Expr>,
clauses: Vec<ComprehensionClause>,
},
SetComprehension {
element: Box<Expr>,
clauses: Vec<ComprehensionClause>,
},
DictComprehension {
key: Box<Expr>,
value: Box<Expr>,
clauses: Vec<ComprehensionClause>,
},
DataFrame {
columns: Vec<DataFrameColumn>,
},
DataFrameOperation {
source: Box<Expr>,
operation: DataFrameOp,
},
For {
label: Option<String>,
var: String, pattern: Option<Pattern>, iter: Box<Expr>,
body: Box<Expr>,
},
While {
label: Option<String>,
condition: Box<Expr>,
body: Box<Expr>,
},
WhileLet {
label: Option<String>,
pattern: Pattern,
expr: Box<Expr>,
body: Box<Expr>,
},
Loop {
label: Option<String>,
body: Box<Expr>,
},
Range {
start: Box<Expr>,
end: Box<Expr>,
inclusive: bool,
},
Module {
name: String,
body: Box<Expr>,
},
ModuleDeclaration {
name: String,
},
Break {
label: Option<String>,
value: Option<Box<Expr>>,
},
Continue {
label: Option<String>,
},
Return {
value: Option<Box<Expr>>,
},
Assign {
target: Box<Expr>,
value: Box<Expr>,
},
CompoundAssign {
target: Box<Expr>,
op: BinaryOp,
value: Box<Expr>,
},
PreIncrement {
target: Box<Expr>,
},
PostIncrement {
target: Box<Expr>,
},
PreDecrement {
target: Box<Expr>,
},
PostDecrement {
target: Box<Expr>,
},
Extension {
target_type: String,
methods: Vec<ImplMethod>,
},
Import {
module: String,
items: Option<Vec<String>>,
},
ImportAll {
module: String,
alias: String,
},
ImportDefault {
module: String,
name: String,
},
Export {
expr: Box<Expr>,
is_default: bool,
},
ExportList {
names: Vec<String>,
},
ReExport {
items: Vec<String>,
module: String,
},
ExportDefault {
expr: Box<Expr>,
},
TypeAlias {
name: String,
target_type: Type,
},
MacroInvocation {
name: String,
args: Vec<Expr>,
},
VecRepeat {
value: Box<Expr>,
count: Box<Expr>,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Literal {
Integer(i64, Option<String>),
Float(f64),
String(String),
Bool(bool),
Char(char),
Byte(u8),
Unit,
Null,
Atom(String),
}
impl Literal {
pub fn from_value(value: &crate::runtime::interpreter::Value) -> Self {
use crate::runtime::interpreter::Value;
match value {
Value::Integer(i) => Literal::Integer(*i, None),
Value::Float(f) => Literal::Float(*f),
Value::String(s) => Literal::String(s.to_string()),
Value::Bool(b) => Literal::Bool(*b),
Value::Nil => Literal::Unit,
Value::Atom(s) => Literal::Atom(s.clone()),
_ => Literal::Unit, }
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum StringPart {
Text(String),
Expr(Box<Expr>),
ExprWithFormat {
expr: Box<Expr>,
format_spec: String,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum BinaryOp {
Add,
Subtract,
Multiply,
Divide,
Modulo,
Power,
Equal,
NotEqual,
Less,
LessEqual,
Greater,
GreaterEqual,
Gt, In, And,
Or,
NullCoalesce,
BitwiseAnd,
BitwiseOr,
BitwiseXor,
LeftShift,
RightShift,
Send, }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum UnaryOp {
Not,
Negate,
BitwiseNot,
Reference,
MutableReference, Deref,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Param {
pub pattern: Pattern,
pub ty: Type,
pub span: Span,
pub is_mutable: bool,
pub default_value: Option<Box<Expr>>,
}
impl Param {
#[must_use]
pub fn name(&self) -> String {
self.pattern.primary_name()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Visibility {
Private,
Public,
PubCrate,
PubSuper,
Protected, }
impl Visibility {
pub fn is_public(&self) -> bool {
matches!(
self,
Visibility::Public | Visibility::PubCrate | Visibility::PubSuper
)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct StructField {
pub name: String,
pub ty: Type,
pub visibility: Visibility,
pub is_mut: bool, pub default_value: Option<Expr>, pub decorators: Vec<Decorator>, }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EnumVariant {
pub name: String,
pub kind: EnumVariantKind,
pub discriminant: Option<i64>, }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum EnumVariantKind {
Unit, Tuple(Vec<Type>), Struct(Vec<StructField>), }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ObjectField {
KeyValue { key: String, value: Expr },
Spread { expr: Expr },
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TraitMethod {
pub name: String,
pub params: Vec<Param>,
pub return_type: Option<Type>,
pub body: Option<Box<Expr>>, pub is_pub: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ImplMethod {
pub name: String,
pub params: Vec<Param>,
pub return_type: Option<Type>,
pub body: Box<Expr>,
pub is_pub: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ActorHandler {
pub message_type: String,
pub params: Vec<Param>,
pub body: Box<Expr>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EffectOperation {
pub name: String,
pub params: Vec<Param>,
pub return_type: Option<Type>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EffectHandler {
pub operation: String,
pub params: Vec<Pattern>,
pub body: Box<Expr>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ClassMethod {
pub name: String,
pub params: Vec<Param>,
pub return_type: Option<Type>,
pub body: Box<Expr>,
pub is_pub: bool,
pub is_static: bool, pub is_override: bool, pub is_final: bool, pub is_abstract: bool, pub is_async: bool, pub self_type: SelfType, }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ClassConstant {
pub name: String,
pub ty: Type,
pub value: Expr,
pub is_pub: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ClassProperty {
pub name: String,
pub ty: Type,
pub getter: Option<Box<Expr>>,
pub setter: Option<PropertySetter>,
pub is_pub: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PropertySetter {
pub param_name: String,
pub body: Box<Expr>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum SelfType {
None, Owned, Borrowed, MutBorrowed, }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Constructor {
pub name: Option<String>, pub params: Vec<Param>,
pub return_type: Option<Type>, pub body: Box<Expr>,
pub is_pub: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Type {
pub kind: TypeKind,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum TypeKind {
Named(String),
Generic {
base: String,
params: Vec<Type>,
},
Optional(Box<Type>),
List(Box<Type>),
Array {
elem_type: Box<Type>,
size: usize,
},
Tuple(Vec<Type>),
Function {
params: Vec<Type>,
ret: Box<Type>,
},
DataFrame {
columns: Vec<(String, Type)>,
},
Series {
dtype: Box<Type>,
},
Reference {
is_mut: bool,
lifetime: Option<String>,
inner: Box<Type>,
},
Refined {
base: Box<Type>,
constraint: Box<Expr>,
},
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PipelineStage {
pub op: Box<Expr>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MatchArm {
pub pattern: Pattern,
pub guard: Option<Box<Expr>>,
pub body: Box<Expr>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Pattern {
Wildcard,
Literal(Literal),
Identifier(String),
QualifiedName(Vec<String>), Tuple(Vec<Pattern>),
List(Vec<Pattern>),
Struct {
name: String,
fields: Vec<StructPatternField>,
has_rest: bool,
},
TupleVariant {
path: Vec<String>, patterns: Vec<Pattern>, }, Range {
start: Box<Pattern>,
end: Box<Pattern>,
inclusive: bool,
},
Or(Vec<Pattern>),
Rest, RestNamed(String), AtBinding {
name: String,
pattern: Box<Pattern>,
}, WithDefault {
pattern: Box<Pattern>,
default: Box<Expr>,
}, Mut(Box<Pattern>), Ok(Box<Pattern>),
Err(Box<Pattern>),
Some(Box<Pattern>),
None,
}
impl Pattern {
#[must_use]
pub fn primary_name(&self) -> String {
match self {
Pattern::Identifier(name) => name.clone(),
Pattern::QualifiedName(path) => path.join("::"),
Pattern::Tuple(patterns) => {
patterns
.first()
.map_or_else(|| "_tuple".to_string(), Pattern::primary_name)
}
Pattern::List(patterns) => {
patterns
.first()
.map_or_else(|| "_list".to_string(), Pattern::primary_name)
}
Pattern::Struct { name, fields, .. } => {
if name.is_empty() {
fields
.first()
.map_or_else(|| "_struct".to_string(), |f| f.name.clone())
} else {
name.clone()
}
}
Pattern::TupleVariant { path, patterns } => {
patterns
.first()
.map_or_else(|| path.join("::"), Pattern::primary_name)
}
Pattern::Ok(inner) | Pattern::Err(inner) | Pattern::Some(inner) => inner.primary_name(),
Pattern::None => "_none".to_string(),
Pattern::Or(patterns) => {
patterns
.first()
.map_or_else(|| "_or".to_string(), Pattern::primary_name)
}
Pattern::Wildcard => "_".to_string(),
Pattern::Rest => "_rest".to_string(),
Pattern::RestNamed(name) => name.clone(),
Pattern::AtBinding { name, .. } => name.clone(),
Pattern::WithDefault { pattern, .. } => pattern.primary_name(),
Pattern::Mut(inner) => inner.primary_name(),
Pattern::Literal(lit) => format!("_literal_{lit:?}"),
Pattern::Range { .. } => "_range".to_string(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct StructPatternField {
pub name: String,
pub pattern: Option<Pattern>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ErrorTypeDef {
pub name: String,
pub fields: Vec<StructField>,
pub extends: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Attribute {
pub name: String,
pub args: Vec<String>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Decorator {
pub name: String,
pub args: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DataFrameColumn {
pub name: String,
pub values: Vec<Expr>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum DataFrameOp {
Filter(Box<Expr>),
Select(Vec<String>),
GroupBy(Vec<String>),
Sort(Vec<String>),
Join {
other: Box<Expr>,
on: Vec<String>,
how: JoinType,
},
Aggregate(Vec<AggregateOp>),
Limit(usize),
Head(usize),
Tail(usize),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum JoinType {
Inner,
Left,
Right,
Outer,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ImportItem {
Named(String),
Aliased { name: String, alias: String },
Wildcard,
}
impl ImportItem {
pub fn is_url_import(path: &str) -> bool {
path.starts_with("https://") || path.starts_with("http://")
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum AggregateOp {
Sum(String),
Mean(String),
Min(String),
Max(String),
Count(String),
Std(String),
Var(String),
}
impl fmt::Display for BinaryOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Add => write!(f, "+"),
Self::Subtract => write!(f, "-"),
Self::Multiply => write!(f, "*"),
Self::Divide => write!(f, "/"),
Self::Modulo => write!(f, "%"),
Self::Power => write!(f, "**"),
Self::Equal => write!(f, "=="),
Self::NotEqual => write!(f, "!="),
Self::Less => write!(f, "<"),
Self::LessEqual => write!(f, "<="),
Self::Greater => write!(f, ">"),
Self::GreaterEqual => write!(f, ">="),
Self::And => write!(f, "&&"),
Self::Or => write!(f, "||"),
Self::NullCoalesce => write!(f, "??"),
Self::BitwiseAnd => write!(f, "&"),
Self::BitwiseOr => write!(f, "|"),
Self::BitwiseXor => write!(f, "^"),
Self::LeftShift => write!(f, "<<"),
Self::RightShift => write!(f, ">>"),
Self::Gt => write!(f, ">"),
Self::Send => write!(f, "!"),
Self::In => write!(f, "in"),
}
}
}
impl fmt::Display for UnaryOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Not => write!(f, "!"),
Self::Negate => write!(f, "-"),
Self::BitwiseNot => write!(f, "~"),
Self::Reference => write!(f, "&"),
Self::MutableReference => write!(f, "&mut "), Self::Deref => write!(f, "*"),
}
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::panic, clippy::expect_used)]
#[path = "ast_tests.rs"]
mod tests;
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::panic, clippy::expect_used)]
#[path = "ast_tests_part2.rs"]
mod tests_part2;