1use crate::ast::logic::LogicExpr;
28use crate::ast::stmt::{ClosureBody, Expr, ReadSource, SelectBranch, Stmt, StringPart, TypeExpr};
29use crate::error::{ParseError, ParseErrorKind};
30use crate::token::Span;
31
32pub const DEFAULT_MAX_AST_DEPTH: usize = 128;
46
47pub const MIN_AST_DEPTH: usize = 16;
50
51pub fn max_ast_depth() -> usize {
59 static LIMIT: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
60 *LIMIT.get_or_init(|| {
61 limit_from(std::env::var("LOGOS_MAX_AST_DEPTH").ok().as_deref())
62 })
63}
64
65pub fn limit_from(env_value: Option<&str>) -> usize {
69 match env_value.and_then(|v| v.trim().parse::<usize>().ok()) {
70 Some(n) => n.max(MIN_AST_DEPTH),
71 None => DEFAULT_MAX_AST_DEPTH,
72 }
73}
74
75pub fn max_parse_recursion() -> usize {
86 (max_ast_depth() / 4).max(MIN_AST_DEPTH)
87}
88
89enum Node<'w, 'a> {
91 S(&'w Stmt<'a>),
92 E(&'w Expr<'a>),
93 T(&'w TypeExpr<'a>),
94 L(&'w LogicExpr<'a>),
95}
96
97pub fn program_depth(stmts: &[Stmt<'_>]) -> usize {
99 let mut work: Vec<(Node<'_, '_>, usize)> = stmts.iter().map(|s| (Node::S(s), 1)).collect();
100 let mut max_depth = 0usize;
101
102 while let Some((node, depth)) = work.pop() {
103 max_depth = max_depth.max(depth);
104 let d = depth + 1;
105 match node {
106 Node::S(stmt) => push_stmt_children(stmt, d, &mut work),
107 Node::E(expr) => push_expr_children(expr, d, &mut work),
108 Node::T(ty) => push_type_children(ty, d, &mut work),
109 Node::L(logic) => push_logic_children(logic, d, &mut work),
110 }
111 }
112 max_depth
113}
114
115pub fn validate_program_depth(stmts: &[Stmt<'_>], span: Span) -> Result<(), ParseError> {
118 let limit = max_ast_depth();
119 let depth = program_depth(stmts);
120 if depth > limit {
121 return Err(ParseError {
122 kind: ParseErrorKind::AstTooDeep { depth, max_depth: limit },
123 span,
124 });
125 }
126 Ok(())
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn override_resolution() {
135 assert_eq!(limit_from(None), DEFAULT_MAX_AST_DEPTH);
136 assert_eq!(limit_from(Some("1000")), 1000);
137 assert_eq!(limit_from(Some(" 2048 ")), 2048);
138 assert_eq!(limit_from(Some("3")), MIN_AST_DEPTH, "floor applies");
139 assert_eq!(limit_from(Some("potato")), DEFAULT_MAX_AST_DEPTH);
140 assert_eq!(limit_from(Some("")), DEFAULT_MAX_AST_DEPTH);
141 }
142}
143
144fn push_stmt_children<'w, 'a>(stmt: &'w Stmt<'a>, d: usize, work: &mut Vec<(Node<'w, 'a>, usize)>) {
145 macro_rules! e {
146 ($x:expr) => {
147 work.push((Node::E($x), d))
148 };
149 }
150 macro_rules! block {
151 ($b:expr) => {
152 for s in $b.iter() {
153 work.push((Node::S(s), d));
154 }
155 };
156 }
157 match stmt {
158 Stmt::Let { ty, value, var: _, mutable: _ } => {
159 if let Some(t) = ty {
160 work.push((Node::T(t), d));
161 }
162 e!(value);
163 }
164 Stmt::Set { value, target: _ } => e!(value),
165 Stmt::Call { args, function: _ } => for a in args { e!(a); },
166 Stmt::If { cond, then_block, else_block } => {
167 e!(cond);
168 block!(then_block);
169 if let Some(b) = else_block {
170 block!(b);
171 }
172 }
173 Stmt::While { cond, body, decreasing } => {
174 e!(cond);
175 if let Some(x) = decreasing {
176 e!(x);
177 }
178 block!(body);
179 }
180 Stmt::Repeat { iterable, body, pattern: _ } => {
181 e!(iterable);
182 block!(body);
183 }
184 Stmt::Return { value } => {
185 if let Some(x) = value {
186 e!(x);
187 }
188 }
189 Stmt::Break => {}
190 Stmt::Assert { proposition } => work.push((Node::L(proposition), d)),
191 Stmt::Trust { proposition, justification: _ } => work.push((Node::L(proposition), d)),
192 Stmt::RuntimeAssert { condition, hard: _ } => e!(condition),
193 Stmt::Give { object, recipient } => {
194 e!(object);
195 e!(recipient);
196 }
197 Stmt::Show { object, recipient } => {
198 e!(object);
199 e!(recipient);
200 }
201 Stmt::SetField { object, value, field: _ } => {
202 e!(object);
203 e!(value);
204 }
205 Stmt::StructDef { .. } => {}
206 Stmt::FunctionDef { params, body, return_type, .. } => {
207 for (_, t) in params {
208 work.push((Node::T(t), d));
209 }
210 if let Some(t) = return_type {
211 work.push((Node::T(t), d));
212 }
213 block!(body);
214 }
215 Stmt::Inspect { target, arms, has_otherwise: _ } => {
216 e!(target);
217 for arm in arms {
218 block!(arm.body);
219 }
220 }
221 Stmt::Push { value, collection } => {
222 e!(value);
223 e!(collection);
224 }
225 Stmt::Pop { collection, into: _ } => e!(collection),
226 Stmt::Add { value, collection } => {
227 e!(value);
228 e!(collection);
229 }
230 Stmt::Remove { value, collection } => {
231 e!(value);
232 e!(collection);
233 }
234 Stmt::SetIndex { collection, index, value } => {
235 e!(collection);
236 e!(index);
237 e!(value);
238 }
239 Stmt::Splice { body } => block!(body),
240 Stmt::Zone { body, name: _, capacity: _, source_file: _ } => block!(body),
241 Stmt::Concurrent { tasks } => block!(tasks),
242 Stmt::Parallel { tasks } => block!(tasks),
243 Stmt::ReadFrom { source, var: _ } => match source {
244 ReadSource::Console => {}
245 ReadSource::File(path) => e!(path),
246 },
247 Stmt::WriteFile { content, path } => {
248 e!(content);
249 e!(path);
250 }
251 Stmt::Spawn { .. } => {}
252 Stmt::SendMessage { message, destination, .. } => {
253 e!(message);
254 e!(destination);
255 }
256 Stmt::AwaitMessage { source, .. } => e!(source),
257 Stmt::StreamMessage { values, destination } => {
258 e!(values);
259 e!(destination);
260 }
261 Stmt::MergeCrdt { source, target } => {
262 e!(source);
263 e!(target);
264 }
265 Stmt::IncreaseCrdt { object, amount, field: _ } => {
266 e!(object);
267 e!(amount);
268 }
269 Stmt::DecreaseCrdt { object, amount, field: _ } => {
270 e!(object);
271 e!(amount);
272 }
273 Stmt::AppendToSequence { sequence, value } => {
274 e!(sequence);
275 e!(value);
276 }
277 Stmt::ResolveConflict { object, value, field: _ } => {
278 e!(object);
279 e!(value);
280 }
281 Stmt::Check { .. } => {}
282 Stmt::Listen { address, secure } => {
283 e!(address);
284 if let Some(pad) = secure {
285 e!(pad.pad);
286 }
287 }
288 Stmt::ConnectTo { address, secure } => {
289 e!(address);
290 if let Some(pad) = secure {
291 e!(pad.pad);
292 }
293 }
294 Stmt::LetPeerAgent { address, var: _ } => e!(address),
295 Stmt::Sleep { milliseconds } => e!(milliseconds),
296 Stmt::Sync { topic, var: _ } => e!(topic),
297 Stmt::Mount { path, var: _ } => e!(path),
298 Stmt::LaunchTask { args, function: _ } => for a in args { e!(a); },
299 Stmt::LaunchTaskWithHandle { args, handle: _, function: _ } => {
300 for a in args { e!(a); }
301 }
302 Stmt::CreatePipe { .. } => {}
303 Stmt::SendPipe { value, pipe } => {
304 e!(value);
305 e!(pipe);
306 }
307 Stmt::ReceivePipe { pipe, var: _ } => e!(pipe),
308 Stmt::TrySendPipe { value, pipe, result: _ } => {
309 e!(value);
310 e!(pipe);
311 }
312 Stmt::TryReceivePipe { pipe, var: _ } => e!(pipe),
313 Stmt::StopTask { handle } => e!(handle),
314 Stmt::Select { branches } => {
315 for branch in branches {
316 match branch {
317 SelectBranch::Receive { pipe, body, var: _ } => {
318 e!(pipe);
319 block!(body);
320 }
321 SelectBranch::Timeout { milliseconds, body } => {
322 e!(milliseconds);
323 block!(body);
324 }
325 }
326 }
327 }
328 Stmt::Theorem(_) | Stmt::Definition(_) | Stmt::Axiom(_) | Stmt::Theory(_) => {}
331 Stmt::Escape { .. } => {}
332 Stmt::Require { .. } => {}
333 }
334}
335
336fn push_expr_children<'w, 'a>(expr: &'w Expr<'a>, d: usize, work: &mut Vec<(Node<'w, 'a>, usize)>) {
337 macro_rules! e {
338 ($x:expr) => {
339 work.push((Node::E($x), d))
340 };
341 }
342 match expr {
343 Expr::Literal(_) | Expr::Identifier(_) | Expr::OptionNone | Expr::Escape { .. } => {}
344 Expr::BinaryOp { left, right, op: _ } => {
345 e!(left);
346 e!(right);
347 }
348 Expr::Not { operand } => e!(operand),
349 Expr::Call { args, function: _ } => for a in args { e!(a); },
350 Expr::Index { collection, index } => {
351 e!(collection);
352 e!(index);
353 }
354 Expr::Slice { collection, start, end } => {
355 e!(collection);
356 e!(start);
357 e!(end);
358 }
359 Expr::Copy { expr } => e!(expr),
360 Expr::Give { value } => e!(value),
361 Expr::Length { collection } => e!(collection),
362 Expr::Contains { collection, value } => {
363 e!(collection);
364 e!(value);
365 }
366 Expr::Union { left, right } => {
367 e!(left);
368 e!(right);
369 }
370 Expr::Intersection { left, right } => {
371 e!(left);
372 e!(right);
373 }
374 Expr::ManifestOf { zone } => e!(zone),
375 Expr::ChunkAt { index, zone } => {
376 e!(index);
377 e!(zone);
378 }
379 Expr::List(items) => for x in items { e!(x); },
380 Expr::Tuple(items) => for x in items { e!(x); },
381 Expr::Range { start, end } => {
382 e!(start);
383 e!(end);
384 }
385 Expr::FieldAccess { object, field: _ } => e!(object),
386 Expr::New { type_args, init_fields, type_name: _ } => {
387 for t in type_args {
388 work.push((Node::T(t), d));
389 }
390 for (_, x) in init_fields {
391 e!(x);
392 }
393 }
394 Expr::NewVariant { fields, enum_name: _, variant: _ } => {
395 for (_, x) in fields {
396 e!(x);
397 }
398 }
399 Expr::OptionSome { value } => e!(value),
400 Expr::WithCapacity { value, capacity } => {
401 e!(value);
402 e!(capacity);
403 }
404 Expr::Closure { params, body, return_type } => {
405 for (_, t) in params {
406 work.push((Node::T(t), d));
407 }
408 if let Some(t) = return_type {
409 work.push((Node::T(t), d));
410 }
411 match body {
412 ClosureBody::Expression(x) => e!(x),
413 ClosureBody::Block(b) => {
414 for s in *b {
415 work.push((Node::S(s), d));
416 }
417 }
418 }
419 }
420 Expr::CallExpr { callee, args } => {
421 e!(callee);
422 for a in args { e!(a); };
423 }
424 Expr::InterpolatedString(parts) => {
425 for part in parts {
426 match part {
427 StringPart::Literal(_) => {}
428 StringPart::Expr { value, format_spec: _, debug: _ } => e!(value),
429 }
430 }
431 }
432 }
433}
434
435fn push_type_children<'w, 'a>(ty: &'w TypeExpr<'a>, d: usize, work: &mut Vec<(Node<'w, 'a>, usize)>) {
436 match ty {
437 TypeExpr::Primitive(_) | TypeExpr::Named(_) => {}
438 TypeExpr::Generic { params, base: _ } => {
439 for t in *params {
440 work.push((Node::T(t), d));
441 }
442 }
443 TypeExpr::Function { inputs, output } => {
444 for t in *inputs {
445 work.push((Node::T(t), d));
446 }
447 work.push((Node::T(output), d));
448 }
449 TypeExpr::Refinement { base, predicate, var: _ } => {
450 work.push((Node::T(base), d));
451 work.push((Node::L(predicate), d));
452 }
453 TypeExpr::Persistent { inner } | TypeExpr::Mutable { inner } => {
454 work.push((Node::T(inner), d));
455 }
456 }
457}
458
459fn push_logic_children<'w, 'a>(
460 logic: &'w LogicExpr<'a>,
461 d: usize,
462 work: &mut Vec<(Node<'w, 'a>, usize)>,
463) {
464 macro_rules! l {
465 ($x:expr) => {
466 work.push((Node::L($x), d))
467 };
468 }
469 match logic {
470 LogicExpr::Predicate { .. }
473 | LogicExpr::Identity { .. }
474 | LogicExpr::Metaphor { .. }
475 | LogicExpr::Atom(_)
476 | LogicExpr::Categorical(_)
477 | LogicExpr::Relation(_)
478 | LogicExpr::NeoEvent(_)
479 | LogicExpr::Comparative { .. }
480 | LogicExpr::Superlative { .. } => {}
481 LogicExpr::Quantifier { body, .. } => l!(body),
482 LogicExpr::Modal { operand, .. } => l!(operand),
483 LogicExpr::Temporal { body, .. } => l!(body),
484 LogicExpr::TemporalBinary { left, right, .. } => {
485 l!(left);
486 l!(right);
487 }
488 LogicExpr::Aspectual { body, .. } => l!(body),
489 LogicExpr::Voice { body, .. } => l!(body),
490 LogicExpr::BinaryOp { left, right, .. } => {
491 l!(left);
492 l!(right);
493 }
494 LogicExpr::UnaryOp { operand, .. } => l!(operand),
495 LogicExpr::Question { body, .. } => l!(body),
496 LogicExpr::YesNoQuestion { body } => l!(body),
497 LogicExpr::Lambda { body, .. } => l!(body),
498 LogicExpr::App { function, argument } => {
499 l!(function);
500 l!(argument);
501 }
502 LogicExpr::Intensional { content, .. } => l!(content),
503 LogicExpr::Event { predicate, .. } => l!(predicate),
504 LogicExpr::Imperative { action } => l!(action),
505 LogicExpr::Exclamative { body, .. } => l!(body),
506 LogicExpr::Optative { wish } => l!(wish),
507 LogicExpr::Implicature { assertion, implicature } => {
508 l!(assertion);
509 l!(implicature);
510 }
511 LogicExpr::SpeechAct { content, .. } => l!(content),
512 LogicExpr::Counterfactual { antecedent, consequent } => {
513 l!(antecedent);
514 l!(consequent);
515 }
516 LogicExpr::Causal { effect, cause } => {
517 l!(effect);
518 l!(cause);
519 }
520 LogicExpr::Concessive { main, concession } => {
521 l!(main);
522 l!(concession);
523 }
524 LogicExpr::Scopal { body, .. } => l!(body),
525 LogicExpr::Control { infinitive, .. } => l!(infinitive),
526 LogicExpr::Presupposition { assertion, presupposition } => {
527 l!(assertion);
528 l!(presupposition);
529 }
530 LogicExpr::Focus { scope, .. } => l!(scope),
531 LogicExpr::TemporalAnchor { body, .. } => l!(body),
532 LogicExpr::Distributive { predicate } => l!(predicate),
533 LogicExpr::GroupQuantifier { restriction, body, .. } => {
534 l!(restriction);
535 l!(body);
536 }
537 }
538}