use brink_format::{CountingFlags, DefinitionId, NameId};
use crate::{AssignOp, InfixOp, PostfixOp, PrefixOp, SequenceType};
pub struct Program {
pub root: Container,
pub globals: Vec<GlobalDef>,
pub lists: Vec<ListDef>,
pub list_items: Vec<ListItemDef>,
pub externals: Vec<ExternalDef>,
pub name_table: Vec<String>,
}
pub struct GlobalDef {
pub id: DefinitionId,
pub name: NameId,
pub mutable: bool,
pub default: ConstValue,
}
pub struct ListDef {
pub id: DefinitionId,
pub name: NameId,
pub items: Vec<(NameId, i32)>,
}
pub struct ListItemDef {
pub id: DefinitionId,
pub name: NameId,
pub origin: DefinitionId,
pub ordinal: i32,
}
pub struct ExternalDef {
pub id: DefinitionId,
pub name: NameId,
pub arg_count: u8,
pub fallback: Option<DefinitionId>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ConstValue {
Int(i32),
Float(f32),
Bool(bool),
String(String),
List {
items: Vec<DefinitionId>,
origins: Vec<DefinitionId>,
},
DivertTarget(DefinitionId),
Null,
}
pub struct Container {
pub id: DefinitionId,
pub name: Option<String>,
pub kind: ContainerKind,
pub params: Vec<Param>,
pub body: Vec<Stmt>,
pub children: Vec<Container>,
pub counting_flags: CountingFlags,
pub temp_slot_count: u16,
pub labeled: bool,
pub inline: bool,
pub is_function: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ContainerKind {
Root,
Knot,
Stitch,
Gather,
ChoiceTarget,
Sequence,
SequenceBranch,
ConditionalBranch,
}
pub struct Param {
pub name: NameId,
pub slot: u16,
pub is_ref: bool,
pub is_divert: bool,
}
#[derive(Clone)]
pub enum Stmt {
EmitContent(Content),
EmitLine(ContentEmission),
EvalLine(ContentEmission),
ChoiceOutput {
content: Content,
emission: Option<ContentEmission>,
},
Divert(Divert),
TunnelCall(TunnelCall),
ThreadStart(ThreadStart),
DeclareTemp {
slot: u16,
name: NameId,
value: Option<Expr>,
},
Assign {
target: AssignTarget,
op: AssignOp,
value: Expr,
},
Return {
value: Option<Expr>,
is_tunnel: bool,
args: Vec<CallArg>,
},
ChoiceSet(ChoiceSet),
Conditional(Conditional),
Sequence(Sequence),
EnterContainer(brink_format::DefinitionId),
ExprStmt(Expr),
EndOfLine,
}
#[derive(Clone)]
pub enum AssignTarget {
Global(DefinitionId),
Temp(u16, NameId),
}
#[derive(Clone)]
pub struct Divert {
pub target: DivertTarget,
pub args: Vec<CallArg>,
}
#[derive(Clone)]
pub struct TunnelCall {
pub targets: Vec<TunnelTarget>,
}
#[derive(Clone)]
pub struct TunnelTarget {
pub target: DivertTarget,
pub args: Vec<CallArg>,
}
#[derive(Clone)]
pub struct ThreadStart {
pub target: DivertTarget,
pub args: Vec<CallArg>,
}
#[derive(Clone)]
pub enum DivertTarget {
Address(DefinitionId),
Variable(DefinitionId),
VariableTemp(u16, NameId),
Done,
End,
}
#[derive(Clone)]
pub enum CallArg {
Value(Expr),
RefGlobal(DefinitionId),
RefTemp(u16, NameId),
}
#[derive(Clone)]
pub struct ChoiceSet {
pub choices: Vec<Choice>,
pub gather_target: Option<DefinitionId>,
}
#[derive(Clone)]
pub struct Choice {
pub is_sticky: bool,
pub is_fallback: bool,
pub condition: Option<Expr>,
pub start_content: Option<Content>,
pub choice_only_content: Option<Content>,
pub inner_content: Option<Content>,
pub display_emission: Option<ContentEmission>,
pub output_emission: Option<ContentEmission>,
pub target: DefinitionId,
pub tags: Vec<Vec<ContentPart>>,
}
#[derive(Clone)]
pub enum CondKind {
InitialCondition,
IfElse,
Switch(Expr),
}
#[derive(Clone)]
pub struct Conditional {
pub kind: CondKind,
pub branches: Vec<CondBranch>,
}
#[derive(Clone)]
pub struct CondBranch {
pub condition: Option<Expr>,
pub body: Vec<Stmt>,
}
#[derive(Clone)]
pub struct Sequence {
pub kind: SequenceType,
pub branches: Vec<Vec<Stmt>>,
}
#[derive(Clone)]
pub struct LineMetadata {
pub source_hash: u64,
pub slot_info: Vec<brink_format::SlotInfo>,
pub source_location: Option<brink_format::SourceLocation>,
}
#[derive(Clone)]
pub enum RecognizedLine {
Plain(String),
Template {
parts: Vec<brink_format::LinePart>,
slot_exprs: Vec<Expr>,
},
}
#[derive(Clone)]
pub struct ContentEmission {
pub line: RecognizedLine,
pub metadata: LineMetadata,
pub tags: Vec<Vec<ContentPart>>,
}
#[derive(Clone)]
pub struct Content {
pub parts: Vec<ContentPart>,
pub tags: Vec<Vec<ContentPart>>,
}
#[derive(Clone)]
pub enum ContentPart {
Text(String),
Glue,
Spring,
Interpolation(Expr),
InlineConditional(Conditional),
InlineSequence(Sequence),
EnterSequence(brink_format::DefinitionId),
}
#[derive(Clone)]
pub enum Expr {
Int(i32),
Float(f32),
Bool(bool),
String(StringExpr),
Null,
GetGlobal(DefinitionId),
GetTemp(u16, NameId),
VisitCount(DefinitionId),
DivertTarget(DefinitionId),
ListLiteral {
items: Vec<DefinitionId>,
origins: Vec<DefinitionId>,
},
Prefix(PrefixOp, Box<Expr>),
Infix(Box<Expr>, InfixOp, Box<Expr>),
Postfix(Box<Expr>, PostfixOp),
Call {
target: DefinitionId,
args: Vec<CallArg>,
},
CallExternal {
target: DefinitionId,
args: Vec<CallArg>,
arg_count: u8,
},
CallVariable {
target: DefinitionId,
args: Vec<CallArg>,
},
CallVariableTemp {
slot: u16,
name: NameId,
args: Vec<CallArg>,
},
CallBuiltin {
builtin: BuiltinFn,
args: Vec<Expr>,
},
}
impl Expr {
pub fn is_function_call(&self) -> bool {
matches!(
self,
Self::Call { .. }
| Self::CallVariable { .. }
| Self::CallVariableTemp { .. }
| Self::CallExternal { .. }
)
}
}
#[derive(Clone)]
pub struct StringExpr {
pub parts: Vec<StringPart>,
}
#[derive(Clone)]
pub enum StringPart {
Literal(String),
Interpolation(Box<Expr>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BuiltinFn {
TurnsSince,
ReadCount,
Turns,
ChoiceCount,
Random,
SeedRandom,
CastToInt,
CastToFloat,
Floor,
Ceiling,
Pow,
Min,
Max,
ListCount,
ListMin,
ListMax,
ListAll,
ListInvert,
ListRange,
ListRandom,
ListValue,
ListFromInt,
}