extern crate alloc;
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use crate::token::Span;
#[derive(Debug, Clone, PartialEq)]
pub struct Program {
pub uses: Vec<UseDecl>,
pub types: Vec<TypeDef>,
pub data_decls: Vec<DataDecl>,
pub functions: Vec<FunctionDef>,
pub traits: Vec<TraitDef>,
pub impls: Vec<ImplBlock>,
pub span: Span,
pub fn_expr_types:
alloc::collections::BTreeMap<String, alloc::collections::BTreeMap<Span, TypeExpr>>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TraitDef {
pub name: String,
pub type_params: Vec<TypeParam>,
pub methods: Vec<TraitMethodSig>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TraitMethodSig {
pub name: String,
pub params: Vec<Param>,
pub return_type: TypeExpr,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ImplBlock {
pub trait_name: String,
pub type_params: Vec<TypeParam>,
pub const_params: Vec<ConstParam>,
pub for_type: TypeExpr,
pub methods: Vec<FunctionDef>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct DataDecl {
pub name: String,
pub fields: Vec<DataFieldDecl>,
pub visibility: DataVisibility,
pub span: Span,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DataVisibility {
Shared,
Private,
Const,
}
#[derive(Debug, Clone, PartialEq)]
pub struct DataFieldDecl {
pub name: String,
pub type_expr: TypeExpr,
pub initializer: Option<ConstInitializer>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ConstInitializer {
Scalar(Literal),
Tuple(Vec<ConstInitializer>),
Array(Vec<ConstInitializer>),
Struct {
name: String,
fields: Vec<(String, ConstInitializer)>,
},
Enum {
enum_name: String,
variant: String,
args: Vec<ConstInitializer>,
},
}
#[derive(Debug, Clone, PartialEq)]
pub struct UseDecl {
pub path: Vec<String>,
pub import: ImportItem,
pub signature: Option<NativeSignature>,
pub is_external: bool,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ImportItem {
Name(String),
Wildcard,
}
#[derive(Debug, Clone, PartialEq)]
pub struct NativeSignature {
pub params: Vec<TypeExpr>,
pub return_type: TypeExpr,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub enum TypeDef {
Struct(StructDef),
Enum(EnumDef),
Newtype(NewtypeDef),
}
#[derive(Debug, Clone, PartialEq)]
pub struct NewtypeDef {
pub name: String,
pub underlying: TypeExpr,
pub refinement: Option<String>,
pub saturate_max: Option<i64>,
pub saturate_min: Option<i64>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct StructDef {
pub name: String,
pub type_params: Vec<TypeParam>,
pub const_params: Vec<ConstParam>,
pub fields: Vec<FieldDecl>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FieldDecl {
pub name: String,
pub type_expr: TypeExpr,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct EnumDef {
pub name: String,
pub type_params: Vec<TypeParam>,
pub const_params: Vec<ConstParam>,
pub variants: Vec<VariantDecl>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct VariantDecl {
pub name: String,
pub fields: Vec<TypeExpr>,
pub explicit_discriminant: Option<i64>,
pub discriminant_value: i64,
pub span: Span,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FunctionCategory {
Fn,
Yield,
Loop,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FunctionDef {
pub category: FunctionCategory,
pub name: String,
pub type_params: Vec<TypeParam>,
pub const_params: Vec<ConstParam>,
pub params: Vec<Param>,
pub return_type: TypeExpr,
pub guard: Option<Box<Expr>>,
pub body: Block,
pub ephemeral: bool,
pub signed: bool,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TypeParam {
pub name: String,
pub bounds: Vec<String>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ConstParam {
pub name: String,
pub span: Span,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConstBinOp {
Add,
Sub,
Mul,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ConstExpr {
Lit(i64, Span),
Param(String, Span),
Bin(ConstBinOp, Box<ConstExpr>, Box<ConstExpr>, Span),
}
impl ConstExpr {
pub fn span(&self) -> Span {
match self {
ConstExpr::Lit(_, s) | ConstExpr::Param(_, s) | ConstExpr::Bin(_, _, _, s) => *s,
}
}
pub fn as_lit(&self) -> Option<i64> {
match self {
ConstExpr::Lit(n, _) => Some(*n),
_ => None,
}
}
}
impl core::fmt::Display for ConstExpr {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ConstExpr::Lit(n, _) => write!(f, "{}", n),
ConstExpr::Param(name, _) => write!(f, "{}", name),
ConstExpr::Bin(op, l, r, _) => {
let sym = match op {
ConstBinOp::Add => "+",
ConstBinOp::Sub => "-",
ConstBinOp::Mul => "*",
};
write!(f, "{} {} {}", l, sym, r)
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Param {
pub pattern: Pattern,
pub type_expr: Option<TypeExpr>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Block {
pub stmts: Vec<Stmt>,
pub tail_expr: Option<Box<Expr>>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Stmt {
Let(LetStmt),
For(ForStmt),
Break(Span),
DataFieldAssign {
data_name: String,
field: String,
value: Expr,
span: Span,
},
DataFieldIndexAssign {
data_name: String,
field: String,
indices: Vec<Expr>,
value: Expr,
span: Span,
},
Expr(Expr),
Assert {
cond: Expr,
message: Option<String>,
span: Span,
},
}
#[derive(Debug, Clone, PartialEq)]
pub struct LetStmt {
pub pattern: Pattern,
pub type_expr: Option<TypeExpr>,
pub value: Expr,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ForStmt {
pub var: String,
pub iterable: Iterable,
pub body: Block,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Iterable {
Expr(Box<Expr>),
Range(Box<Expr>, Box<Expr>),
}
#[derive(Debug, Clone, PartialEq)]
#[allow(missing_docs)] pub enum Expr {
Literal { value: Literal, span: Span },
Ident { name: String, span: Span },
BinOp {
op: BinOp,
left: Box<Expr>,
right: Box<Expr>,
span: Span,
},
UnaryOp {
op: UnaryOp,
operand: Box<Expr>,
span: Span,
},
Call {
name: String,
args: Vec<Expr>,
const_args: Vec<ConstExpr>,
span: Span,
},
Pipeline {
left: Box<Expr>,
func: String,
args: Vec<Expr>,
span: Span,
},
Yield { value: Box<Expr>, span: Span },
If {
condition: Box<Expr>,
then_block: Block,
else_block: Option<Block>,
span: Span,
},
Match {
scrutinee: Box<Expr>,
arms: Vec<MatchArm>,
span: Span,
},
Loop { body: Block, span: Span },
FieldAccess {
object: Box<Expr>,
field: String,
span: Span,
},
MethodCall {
receiver: Box<Expr>,
method: String,
args: Vec<Expr>,
span: Span,
},
TupleIndex {
object: Box<Expr>,
index: u64,
span: Span,
},
ArrayIndex {
object: Box<Expr>,
index: Box<Expr>,
span: Span,
},
StructInit {
name: String,
fields: Vec<FieldInit>,
const_args: Vec<ConstExpr>,
span: Span,
},
EnumVariant {
enum_name: String,
variant: String,
args: Vec<Expr>,
const_args: Vec<ConstExpr>,
span: Span,
},
ArrayLiteral { elements: Vec<Expr>, span: Span },
TupleLiteral { elements: Vec<Expr>, span: Span },
Cast {
expr: Box<Expr>,
target: TypeExpr,
span: Span,
},
Placeholder { span: Span },
Closure {
params: Vec<Param>,
return_type: Option<TypeExpr>,
body: Block,
span: Span,
},
ClosureRef {
name: String,
captures: Vec<String>,
recursive: bool,
span: Span,
},
Checked {
op_expr: Box<Expr>,
arms: Vec<CheckedArm>,
span: Span,
},
SaturateMax { span: Span },
SaturateMin { span: Span },
Classify {
value: Box<Expr>,
labels: Vec<String>,
span: Span,
},
Declassify {
value: Box<Expr>,
labels: Vec<String>,
span: Span,
},
}
#[derive(Debug, Clone, PartialEq)]
pub struct CheckedArm {
pub kind: CheckedArmKind,
pub guard: Option<Expr>,
pub body: Expr,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub enum CheckedArmKind {
Ok(Pattern),
Overflow(Pattern, Option<Pattern>),
Underflow(Pattern, Option<Pattern>),
ZeroDivisor(Pattern),
Nan(Pattern),
InvalidIndex(Pattern),
InvalidNewtype(Pattern),
PayloadDiscriminant(Pattern),
InvalidDiscriminant(Pattern),
Error(Pattern),
}
impl Expr {
pub fn span(&self) -> Span {
match self {
Expr::Literal { span, .. }
| Expr::Ident { span, .. }
| Expr::BinOp { span, .. }
| Expr::UnaryOp { span, .. }
| Expr::Call { span, .. }
| Expr::Pipeline { span, .. }
| Expr::Yield { span, .. }
| Expr::If { span, .. }
| Expr::Match { span, .. }
| Expr::Loop { span, .. }
| Expr::FieldAccess { span, .. }
| Expr::MethodCall { span, .. }
| Expr::TupleIndex { span, .. }
| Expr::ArrayIndex { span, .. }
| Expr::StructInit { span, .. }
| Expr::EnumVariant { span, .. }
| Expr::ArrayLiteral { span, .. }
| Expr::TupleLiteral { span, .. }
| Expr::Cast { span, .. }
| Expr::Placeholder { span }
| Expr::Closure { span, .. }
| Expr::ClosureRef { span, .. }
| Expr::Checked { span, .. }
| Expr::SaturateMax { span }
| Expr::SaturateMin { span }
| Expr::Classify { span, .. }
| Expr::Declassify { span, .. } => *span,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Literal {
Int(i64),
Float(f64),
Byte(u8),
Fixed {
raw: i64,
frac_bits: u8,
},
String(String),
Bool(bool),
Unit,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(missing_docs)] pub enum BinOp {
Add,
Sub,
Mul,
Div,
Mod,
Shl,
AShl,
ShrA,
ShrL,
Band,
Bor,
Bxor,
Eq,
NotEq,
Lt,
Gt,
LtEq,
GtEq,
And,
Or,
Xor,
Andalso,
Orelse,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnaryOp {
Neg,
Not,
Bnot,
}
#[derive(Debug, Clone, PartialEq)]
pub struct MatchArm {
pub pattern: Pattern,
pub guard: Option<Expr>,
pub expr: Expr,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FieldInit {
pub name: String,
pub value: Expr,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub enum TypeExpr {
Prim(PrimType, Span),
Named(String, Vec<TypeExpr>, Vec<ConstExpr>, Span),
Tuple(Vec<TypeExpr>, Span),
Array(Box<TypeExpr>, ConstExpr, Span),
Multiword(ConstExpr, ConstExpr, Span),
Option(Box<TypeExpr>, Span),
Unit(Span),
Labelled(Box<TypeExpr>, Vec<String>, Span),
NegativeLabelled(Box<TypeExpr>, Vec<String>, Span),
}
impl TypeExpr {
pub fn multiword_lit(n: u16, f: u16, span: Span) -> TypeExpr {
TypeExpr::Multiword(
ConstExpr::Lit(n as i64, span),
ConstExpr::Lit(f as i64, span),
span,
)
}
pub fn array_lit(elem: Box<TypeExpr>, n: i64, span: Span) -> TypeExpr {
TypeExpr::Array(elem, ConstExpr::Lit(n, span), span)
}
pub fn as_multiword_lit(&self) -> Option<(u16, u16)> {
match self {
TypeExpr::Multiword(n, f, _) => Some((
u16::try_from(n.as_lit()?).ok()?,
u16::try_from(f.as_lit()?).ok()?,
)),
_ => None,
}
}
pub fn as_array_lit(&self) -> Option<(&TypeExpr, i64)> {
match self {
TypeExpr::Array(elem, n, _) => Some((elem, n.as_lit()?)),
_ => None,
}
}
pub fn span(&self) -> Span {
match self {
TypeExpr::Prim(_, span)
| TypeExpr::Named(_, _, _, span)
| TypeExpr::Tuple(_, span)
| TypeExpr::Array(_, _, span)
| TypeExpr::Multiword(_, _, span)
| TypeExpr::Option(_, span)
| TypeExpr::Unit(span)
| TypeExpr::Labelled(_, _, span) => *span,
TypeExpr::NegativeLabelled(_, _, span) => *span,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PrimType {
Byte,
Word,
Fixed(Option<u8>),
Float,
Bool,
Text,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Pattern {
Literal(Literal, Span),
Enum(String, String, Vec<Pattern>, Span),
Struct(String, Vec<FieldPattern>, Span),
Tuple(Vec<Pattern>, Span),
Wildcard(Span),
Variable(String, Span),
}
impl Pattern {
pub fn span(&self) -> Span {
match self {
Pattern::Literal(_, span)
| Pattern::Enum(_, _, _, span)
| Pattern::Struct(_, _, span)
| Pattern::Tuple(_, span)
| Pattern::Wildcard(span)
| Pattern::Variable(_, span) => *span,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct FieldPattern {
pub name: String,
pub pattern: Option<Pattern>,
pub span: Span,
}
pub fn merge_spans(start: Span, end: Span) -> Span {
Span {
start: start.start,
end: end.end,
line: start.line,
column: start.column,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn span_at(start: usize, end: usize) -> Span {
Span {
start,
end,
line: 1,
column: start as u32 + 1,
}
}
#[test]
fn as_multiword_lit_refuses_to_truncate() {
let in_range = TypeExpr::Multiword(
ConstExpr::Lit(2, span_at(0, 1)),
ConstExpr::Lit(0, span_at(2, 3)),
span_at(0, 3),
);
assert_eq!(in_range.as_multiword_lit(), Some((2, 0)));
let over = TypeExpr::Multiword(
ConstExpr::Lit(65537, span_at(0, 1)),
ConstExpr::Lit(0, span_at(2, 3)),
span_at(0, 3),
);
assert_eq!(over.as_multiword_lit(), None);
let negative = TypeExpr::Multiword(
ConstExpr::Lit(-1, span_at(0, 1)),
ConstExpr::Lit(0, span_at(2, 3)),
span_at(0, 3),
);
assert_eq!(negative.as_multiword_lit(), None);
}
#[test]
fn const_expr_reports_its_span() {
let lit = ConstExpr::Lit(3, span_at(0, 1));
let param = ConstExpr::Param(String::from("n"), span_at(2, 3));
let bin = ConstExpr::Bin(
ConstBinOp::Add,
Box::new(param.clone()),
Box::new(lit.clone()),
span_at(0, 3),
);
assert_eq!(lit.span(), span_at(0, 1));
assert_eq!(param.span(), span_at(2, 3));
assert_eq!(bin.span(), span_at(0, 3));
match bin {
ConstExpr::Bin(ConstBinOp::Add, l, r, _) => {
assert!(matches!(*l, ConstExpr::Param(_, _)));
assert!(matches!(*r, ConstExpr::Lit(3, _)));
}
_ => panic!("expected a binary const expression"),
}
}
}