use crate::builtins::Builtin;
use probl_syntax::Span;
use probl_syntax::ast::{BinOp, UnOp};
pub type SlotId = u32;
pub type FnId = u32;
pub type StmtId = u32;
pub const MAIN: FnId = 0;
pub const TEMP: &str = "(temporary)";
#[derive(Debug)]
pub struct Program {
pub functions: Vec<Function>,
pub reports: Vec<ReportSite>,
pub records: Vec<RecordType>,
pub enums: Vec<EnumType>,
pub inputs: Vec<Input>,
pub settings: Settings,
pub stmt_count: u32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Input {
pub name: String,
pub path: String,
pub format: DataFormat,
pub ty: TypeSpec,
pub span: Span,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DataFormat {
Csv,
Json,
Lines,
}
impl Program {
pub fn main(&self) -> &Function {
&self.functions[MAIN as usize]
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Settings {
pub mode: Mode,
pub mode_span: Option<Span>,
pub epsilon: f64,
pub max_iterations: u64,
pub max_worlds: usize,
pub on_error: Option<FailureMode>,
}
impl Default for Settings {
fn default() -> Settings {
Settings {
mode: Mode::Auto,
mode_span: None,
epsilon: 1e-12,
max_iterations: 10_000_000,
max_worlds: 10_000_000,
on_error: None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FailureMode {
Total,
Partial,
}
impl FailureMode {
pub fn name(self) -> &'static str {
match self {
FailureMode::Total => "total",
FailureMode::Partial => "partial",
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Mode {
Auto,
Enumerate,
Beam { worlds: u64 },
Sample { runs: u64, seed: u64 },
Particles { runs: u64, seed: u64 },
}
impl Mode {
pub fn name(&self) -> &'static str {
match self {
Mode::Auto => "auto",
Mode::Enumerate => "enumerate",
Mode::Beam { .. } => "beam",
Mode::Sample { .. } => "sample",
Mode::Particles { .. } => "particles",
}
}
}
#[derive(Clone, Debug)]
pub struct ReportSite {
pub label: String,
pub key_label: Option<String>,
pub kind: ReportKind,
pub span: Span,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ReportKind {
Once,
PerKey,
PerVisit,
}
#[derive(Clone, Debug)]
pub struct RecordType {
pub name: String,
pub fields: Vec<RecordField>,
pub span: Span,
}
#[derive(Clone, Debug)]
pub struct RecordField {
pub name: String,
pub ty: TypeSpec,
pub span: Span,
}
#[derive(Clone, Debug)]
pub struct EnumType {
pub name: String,
pub variants: Vec<String>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FnKind {
Main,
Named,
Lambda,
Simulate,
}
#[derive(Clone, Debug)]
pub struct Function {
pub name: String,
pub kind: FnKind,
pub span: Span,
pub n_params: u32,
pub captures: Vec<Capture>,
pub slots: Vec<SlotInfo>,
pub body: Block,
pub effects: Effects,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Effects {
pub observes: bool,
pub prints: bool,
}
impl Function {
pub fn n_slots(&self) -> usize {
self.slots.len()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Capture {
pub slot: SlotId,
pub source: CaptureSource,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CaptureSource {
Global(SlotId),
Parent(SlotId),
}
#[derive(Clone, Debug)]
pub struct SlotInfo {
pub name: String,
pub span: Span,
}
#[derive(Clone, Debug, Default)]
pub struct Block {
pub stmts: Vec<Stmt>,
}
#[derive(Clone, Debug)]
pub struct Stmt {
pub id: StmtId,
pub span: Span,
pub kind: StmtKind,
}
#[derive(Clone, Debug)]
pub enum StmtKind {
Set {
place: Place,
value: Expr,
},
Draw {
place: Place,
dist: Expr,
},
Take {
place: Place,
bag: Place,
},
Call {
dest: Place,
callee: Callee,
args: Vec<Expr>,
},
If {
cond: Expr,
then: Block,
otherwise: Block,
},
Chance {
arms: Vec<(Expr, Block)>,
otherwise: Option<Block>,
exhaustive: bool,
},
Loop {
body: Block,
bounded: bool,
},
Break,
Continue,
Return(Expr),
Observe {
value: Expr,
from: Option<Expr>,
},
Report {
site: u32,
value: Expr,
key: Option<Expr>,
},
Fail {
message: String,
},
Check {
slot: SlotId,
ty: TypeSpec,
},
Try {
body: Block,
catches: Vec<Catch>,
},
}
#[derive(Clone, Debug)]
pub struct Catch {
pub fault: Option<Fault>,
pub body: Block,
}
impl Catch {
pub fn catches(&self, fault: Option<Fault>) -> bool {
fault.is_some() && (self.fault.is_none() || self.fault == fault)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Fault {
DivisionByZero,
DomainError,
IndexOutOfBounds,
MissingKey,
EmptyCollection,
ConversionError,
NumericOverflow,
}
impl Fault {
pub const ALL: [Fault; 7] = [
Fault::DivisionByZero,
Fault::DomainError,
Fault::IndexOutOfBounds,
Fault::MissingKey,
Fault::EmptyCollection,
Fault::ConversionError,
Fault::NumericOverflow,
];
pub fn name(self) -> &'static str {
match self {
Fault::DivisionByZero => "DivisionByZero",
Fault::DomainError => "DomainError",
Fault::IndexOutOfBounds => "IndexOutOfBounds",
Fault::MissingKey => "MissingKey",
Fault::EmptyCollection => "EmptyCollection",
Fault::ConversionError => "ConversionError",
Fault::NumericOverflow => "NumericOverflow",
}
}
pub fn from_name(name: &str) -> Option<Fault> {
Fault::ALL.into_iter().find(|f| f.name() == name)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum TypeSpec {
Int,
Float,
Complex,
Prob,
Bool,
Str,
Date,
Unit,
Function,
List(Box<TypeSpec>),
Map(Box<TypeSpec>, Box<TypeSpec>),
Bag(Box<TypeSpec>),
Dist(Box<TypeSpec>),
Record(u32),
Enum(u32),
AnonRecord(Vec<(String, TypeSpec)>),
}
impl TypeSpec {
pub fn describe(&self, program: &Program) -> String {
self.describe_in(&program.records, &program.enums)
}
pub fn describe_in(&self, records: &[RecordType], enums: &[EnumType]) -> String {
let describe = |t: &TypeSpec| t.describe_in(records, enums);
match self {
TypeSpec::Int => "int".into(),
TypeSpec::Float => "float".into(),
TypeSpec::Complex => "complex".into(),
TypeSpec::Prob => "prob".into(),
TypeSpec::Bool => "bool".into(),
TypeSpec::Str => "str".into(),
TypeSpec::Date => "date".into(),
TypeSpec::Unit => "()".into(),
TypeSpec::Function => "fn".into(),
TypeSpec::List(t) => format!("list[{}]", describe(t)),
TypeSpec::Map(k, v) => format!("map[{}, {}]", describe(k), describe(v)),
TypeSpec::Bag(t) => format!("bag[{}]", describe(t)),
TypeSpec::Dist(t) => format!("dist[{}]", describe(t)),
TypeSpec::Record(r) => records[*r as usize].name.clone(),
TypeSpec::Enum(e) => enums[*e as usize].name.clone(),
TypeSpec::AnonRecord(fields) => {
let fields: Vec<String> = fields.iter().map(|(n, t)| format!("{n}: {}", describe(t))).collect();
format!("{{ {} }}", fields.join(", "))
}
}
}
}
#[derive(Clone, Debug)]
pub struct Place {
pub slot: SlotId,
pub path: Vec<PathElem>,
}
impl Place {
pub fn slot(slot: SlotId) -> Place {
Place { slot, path: Vec::new() }
}
}
#[derive(Clone, Debug)]
pub enum PathElem {
Field(String),
Index(Expr),
}
#[derive(Clone, Debug)]
pub enum Callee {
Fn { func: FnId, capture_args: Vec<SlotId> },
Value(Expr, Vec<String>),
}
#[derive(Clone, Debug)]
pub struct Expr {
pub kind: ExprKind,
pub span: Span,
}
#[derive(Clone, Debug)]
pub enum ExprKind {
Lit(Lit),
Slot(SlotId),
Unary(UnOp, Box<Expr>),
Binary(BinOp, Box<Expr>, Box<Expr>),
List(Vec<Expr>),
Map(Vec<(Expr, Expr)>),
Record {
ty: Option<u32>,
fields: Vec<(String, Expr)>,
},
Field(Box<Expr>, String),
Index(Box<Expr>, Box<Expr>),
With(Box<Expr>, Vec<(String, Expr)>),
Builtin {
func: Builtin,
args: Vec<Expr>,
named: Vec<(String, Expr)>,
},
Closure {
func: FnId,
capture_args: Vec<SlotId>,
},
Simulate {
func: FnId,
capture_args: Vec<SlotId>,
},
Interp(Vec<InterpPart>),
Input(u32),
}
#[derive(Clone, Debug)]
pub enum InterpPart {
Lit(String),
Expr(Expr),
}
#[derive(Clone, Debug, PartialEq)]
pub enum Lit {
Builtin(Builtin),
Unit,
Bool(bool),
Int(probl_number::Integer),
Float(f64),
FloatConstant(f64),
Prob(f64),
Str(String),
Dice {
count: u32,
sides: u32,
},
Enum {
ty: u32,
variant: u32,
},
}