use crate::token::Span;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum OnLimitBehavior {
#[default]
Error,
Truncate,
}
#[derive(Debug, Clone)]
pub struct LimitConfig {
pub max_step: usize,
pub max_depth: usize,
pub max_memory: usize,
pub on_limit: OnLimitBehavior,
}
impl Default for LimitConfig {
fn default() -> Self {
Self {
max_step: 1_000_000,
max_depth: 100,
max_memory: 1_000_000,
on_limit: OnLimitBehavior::Truncate,
}
}
}
#[derive(Debug, Clone)]
pub struct Directive {
pub name: String,
pub value: DirectiveValue,
pub span: Span,
}
#[derive(Debug, Clone)]
pub enum DirectiveValue {
Number(i64),
String(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Primitive {
Straight,
Right,
Left,
}
impl Primitive {
pub fn as_char(&self) -> char {
match self {
Primitive::Straight => 's',
Primitive::Right => 'r',
Primitive::Left => 'l',
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NumOp {
Add,
Sub,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NumAtom {
Number(i32),
Param(char),
}
#[derive(Debug, Clone)]
pub enum Arg {
Command(Expr),
Number(i32, Span),
NumExpr {
first: NumAtom,
rest: Vec<(NumOp, NumAtom)>,
span: Span,
},
}
impl Arg {
pub fn span(&self) -> Span {
match self {
Arg::Command(expr) => expr.span(),
Arg::Number(_, span) => *span,
Arg::NumExpr { span, .. } => *span,
}
}
}
#[derive(Debug, Clone)]
pub enum Expr {
Primitive(Primitive, Span),
Param(char, Span),
FuncCall {
name: char,
args: Vec<Arg>,
span: Span,
},
Sequence(Vec<Expr>),
}
impl Expr {
pub fn span(&self) -> Span {
match self {
Expr::Primitive(_, span) => *span,
Expr::Param(_, span) => *span,
Expr::FuncCall { span, .. } => *span,
Expr::Sequence(exprs) => {
if exprs.is_empty() {
Span::default()
} else {
let first = exprs.first().unwrap().span();
let last = exprs.last().unwrap().span();
Span::new(first.start, last.end, first.line, first.column)
}
}
}
}
pub fn is_empty(&self) -> bool {
matches!(self, Expr::Sequence(exprs) if exprs.is_empty())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ParamType {
CmdSeq,
Int,
}
#[derive(Debug, Clone)]
pub struct FuncDef {
pub name: char,
pub params: Vec<char>,
pub param_types: std::collections::HashMap<char, ParamType>,
pub body: Expr,
pub span: Span,
}
#[derive(Debug, Clone)]
pub enum Definition {
Function(FuncDef),
}
impl Definition {
pub fn name(&self) -> char {
match self {
Definition::Function(f) => f.name,
}
}
pub fn span(&self) -> Span {
match self {
Definition::Function(f) => f.span,
}
}
}
#[derive(Debug, Clone)]
pub struct Agent {
pub id: u32,
pub definitions: Vec<Definition>,
pub expression: Expr,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct Program {
pub directives: Vec<Directive>,
pub limits: LimitConfig,
pub agents: Vec<Agent>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_primitive_char() {
assert_eq!(Primitive::Straight.as_char(), 's');
assert_eq!(Primitive::Right.as_char(), 'r');
assert_eq!(Primitive::Left.as_char(), 'l');
}
#[test]
fn test_expr_span() {
let span = Span::new(0, 1, 1, 1);
let expr = Expr::Primitive(Primitive::Straight, span);
assert_eq!(expr.span().start, 0);
assert_eq!(expr.span().end, 1);
}
#[test]
fn test_sequence_span() {
let span1 = Span::new(0, 1, 1, 1);
let span2 = Span::new(1, 2, 1, 2);
let expr = Expr::Sequence(vec![
Expr::Primitive(Primitive::Straight, span1),
Expr::Primitive(Primitive::Right, span2),
]);
let combined = expr.span();
assert_eq!(combined.start, 0);
assert_eq!(combined.end, 2);
}
}