use crate::token::Span;
#[derive(Debug, Clone)]
pub struct Module {
pub name: Ident,
pub decls: Vec<Decl>,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct Ident {
pub name: String,
pub span: Span,
}
impl Ident {
pub fn new(name: impl Into<String>, span: Span) -> Self {
Self {
name: name.into(),
span,
}
}
}
#[derive(Debug, Clone)]
pub enum Decl {
Use(UseDecl),
Const(ConstDecl),
Var(VarDecl),
Type(TypeDecl),
Func(FuncDecl),
Init(InitDecl),
Action(ActionDecl),
Invariant(InvariantDecl),
Property(PropertyDecl),
Fairness(FairnessDecl),
}
impl Decl {
pub fn span(&self) -> Span {
match self {
Decl::Use(d) => d.span,
Decl::Const(d) => d.span,
Decl::Var(d) => d.span,
Decl::Type(d) => d.span,
Decl::Func(d) => d.span,
Decl::Init(d) => d.span,
Decl::Action(d) => d.span,
Decl::Invariant(d) => d.span,
Decl::Property(d) => d.span,
Decl::Fairness(d) => d.span,
}
}
}
#[derive(Debug, Clone)]
pub struct UseDecl {
pub module: Ident,
pub span: Span,
}
#[derive(Debug, Clone)]
pub enum ConstValue {
Type(TypeExpr),
Scalar(i64),
}
#[derive(Debug, Clone)]
pub struct ConstDecl {
pub name: Ident,
pub value: ConstValue,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct VarDecl {
pub name: Ident,
pub ty: TypeExpr,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct TypeDecl {
pub name: Ident,
pub ty: TypeExpr,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct FuncDecl {
pub name: Ident,
pub params: Vec<FuncParam>,
pub body: Expr,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct FuncParam {
pub name: Ident,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct InitDecl {
pub body: Expr,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct ActionDecl {
pub name: Ident,
pub params: Vec<Param>,
pub body: ActionBody,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct ActionBody {
pub requires: Vec<Expr>,
pub effect: Option<Expr>,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct Param {
pub name: Ident,
pub ty: TypeExpr,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct InvariantDecl {
pub name: Ident,
pub body: Expr,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct PropertyDecl {
pub name: Ident,
pub body: Expr,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct FairnessDecl {
pub constraints: Vec<FairnessConstraint>,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct FairnessConstraint {
pub kind: FairnessKind,
pub action: Ident,
pub span: Span,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FairnessKind {
Weak,
Strong,
}
#[derive(Debug, Clone)]
pub enum TypeExpr {
Named(Ident),
Set(Box<TypeExpr>, Span),
Seq(Box<TypeExpr>, Span),
Dict(Box<TypeExpr>, Box<TypeExpr>, Span),
Option(Box<TypeExpr>, Span),
Range(Box<Expr>, Box<Expr>, Span),
Tuple(Vec<TypeExpr>, Span),
}
impl TypeExpr {
pub fn span(&self) -> Span {
match self {
TypeExpr::Named(id) => id.span,
TypeExpr::Set(_, span) => *span,
TypeExpr::Seq(_, span) => *span,
TypeExpr::Dict(_, _, span) => *span,
TypeExpr::Option(_, span) => *span,
TypeExpr::Range(_, _, span) => *span,
TypeExpr::Tuple(_, span) => *span,
}
}
}
#[derive(Debug, Clone)]
pub struct Expr {
pub kind: ExprKind,
pub span: Span,
}
impl Expr {
pub fn new(kind: ExprKind, span: Span) -> Self {
Self { kind, span }
}
}
#[derive(Debug, Clone)]
pub enum ExprKind {
Bool(bool),
Int(i64),
String(String),
Ident(String),
Primed(String),
Binary {
op: BinOp,
left: Box<Expr>,
right: Box<Expr>,
},
Unary { op: UnaryOp, operand: Box<Expr> },
Index { base: Box<Expr>, index: Box<Expr> },
Slice {
base: Box<Expr>,
lo: Box<Expr>,
hi: Box<Expr>,
},
Field { base: Box<Expr>, field: Ident },
Call { func: Box<Expr>, args: Vec<Expr> },
SetLit(Vec<Expr>),
SeqLit(Vec<Expr>),
TupleLit(Vec<Expr>),
DictLit(Vec<(Expr, Expr)>),
FnLit {
var: Ident,
domain: Box<Expr>,
body: Box<Expr>,
},
SetComprehension {
element: Box<Expr>,
var: Ident,
domain: Box<Expr>,
filter: Option<Box<Expr>>,
},
RecordUpdate {
base: Box<Expr>,
updates: Vec<RecordFieldUpdate>,
},
Quantifier {
kind: QuantifierKind,
bindings: Vec<Binding>,
body: Box<Expr>,
},
Choose {
var: Ident,
domain: Box<Expr>,
predicate: Box<Expr>,
},
Fix { var: Ident, predicate: Box<Expr> },
Let {
var: Ident,
value: Box<Expr>,
body: Box<Expr>,
},
If {
cond: Box<Expr>,
then_branch: Box<Expr>,
else_branch: Box<Expr>,
},
Require(Box<Expr>),
Changes(Ident),
Enabled(Ident),
SeqHead(Box<Expr>),
SeqTail(Box<Expr>),
Len(Box<Expr>),
Keys(Box<Expr>),
Values(Box<Expr>),
BigUnion(Box<Expr>),
Powerset(Box<Expr>),
Always(Box<Expr>),
Eventually(Box<Expr>),
LeadsTo { left: Box<Expr>, right: Box<Expr> },
Range { lo: Box<Expr>, hi: Box<Expr> },
Paren(Box<Expr>),
}
#[derive(Debug, Clone)]
pub enum RecordFieldUpdate {
Field { name: Ident, value: Expr },
Dynamic { key: Expr, value: Expr },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BinOp {
And,
Or,
Implies,
Iff,
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
Add,
Sub,
Mul,
Div,
Mod,
In,
NotIn,
Union,
Intersect,
Diff,
SubsetOf,
Concat,
}
impl BinOp {
pub fn precedence(self) -> u8 {
match self {
BinOp::Iff => 1,
BinOp::Implies => 2,
BinOp::Or => 3,
BinOp::And => 4,
BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge => 5,
BinOp::In | BinOp::NotIn | BinOp::SubsetOf => 5,
BinOp::Union | BinOp::Diff | BinOp::Concat => 6,
BinOp::Add | BinOp::Sub => 6,
BinOp::Intersect => 7,
BinOp::Mul | BinOp::Div | BinOp::Mod => 7,
}
}
pub fn is_right_assoc(self) -> bool {
matches!(self, BinOp::Implies | BinOp::Iff)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnaryOp {
Not,
Neg,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QuantifierKind {
Forall,
Exists,
}
#[derive(Debug, Clone)]
pub struct Binding {
pub var: Ident,
pub domain: Expr,
pub span: Span,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_binop_precedence() {
assert!(BinOp::Mul.precedence() > BinOp::Add.precedence());
assert!(BinOp::Add.precedence() > BinOp::Eq.precedence());
assert!(BinOp::Eq.precedence() > BinOp::And.precedence());
assert!(BinOp::And.precedence() > BinOp::Or.precedence());
assert!(BinOp::Or.precedence() > BinOp::Implies.precedence());
}
}