/home/noah/src/ruchy/src/testing/generators.rs
Line | Count | Source |
1 | | //! Property-based test generators for AST nodes |
2 | | |
3 | | use crate::frontend::ast::{BinaryOp, Expr, ExprKind, Literal, Pattern, Span, UnaryOp}; |
4 | | use proptest::prelude::*; |
5 | | use proptest::strategy::{BoxedStrategy, Strategy}; |
6 | | |
7 | | /// Maximum depth for recursive AST generation to avoid stack overflow |
8 | | const MAX_DEPTH: u32 = 5; |
9 | | |
10 | | /// Generate arbitrary literals |
11 | 9.33k | pub fn arb_literal() -> BoxedStrategy<Literal> { |
12 | 9.33k | prop_oneof![ |
13 | 9.33k | (0i64..i64::MAX).prop_map(Literal::Integer), |
14 | 9.33k | any::<f64>().prop_map(Literal::Float), |
15 | 9.33k | any::<bool>().prop_map(Literal::Bool), |
16 | 9.33k | ".*".prop_map(|s: String| Literal::String(s.chars()15 .take15 (20).collect15 ())), |
17 | 9.33k | Just(Literal::Unit), |
18 | | ] |
19 | 9.33k | .boxed() |
20 | 9.33k | } |
21 | | |
22 | | /// Generate arbitrary identifiers |
23 | 9.33k | pub fn arb_identifier() -> BoxedStrategy<String> { |
24 | 9.33k | "[a-z][a-z0-9_]{0,10}".prop_map(|s| s).boxed() |
25 | 9.33k | } |
26 | | |
27 | | /// Generate arbitrary binary operators |
28 | 1.55k | pub fn arb_binary_op() -> BoxedStrategy<BinaryOp> { |
29 | 1.55k | prop_oneof![ |
30 | 1.55k | Just(BinaryOp::Add), |
31 | 1.55k | Just(BinaryOp::Subtract), |
32 | 1.55k | Just(BinaryOp::Multiply), |
33 | 1.55k | Just(BinaryOp::Divide), |
34 | 1.55k | Just(BinaryOp::Modulo), |
35 | 1.55k | Just(BinaryOp::Power), |
36 | 1.55k | Just(BinaryOp::Equal), |
37 | 1.55k | Just(BinaryOp::NotEqual), |
38 | 1.55k | Just(BinaryOp::Less), |
39 | 1.55k | Just(BinaryOp::LessEqual), |
40 | 1.55k | Just(BinaryOp::Greater), |
41 | 1.55k | Just(BinaryOp::GreaterEqual), |
42 | 1.55k | Just(BinaryOp::And), |
43 | 1.55k | Just(BinaryOp::Or), |
44 | 1.55k | Just(BinaryOp::BitwiseAnd), |
45 | 1.55k | Just(BinaryOp::BitwiseOr), |
46 | 1.55k | Just(BinaryOp::BitwiseXor), |
47 | 1.55k | Just(BinaryOp::LeftShift), |
48 | | ] |
49 | 1.55k | .boxed() |
50 | 1.55k | } |
51 | | |
52 | | /// Generate arbitrary unary operators |
53 | 1.55k | pub fn arb_unary_op() -> BoxedStrategy<UnaryOp> { |
54 | 1.55k | prop_oneof![ |
55 | 1.55k | Just(UnaryOp::Negate), |
56 | 1.55k | Just(UnaryOp::Not), |
57 | 1.55k | Just(UnaryOp::BitwiseNot), |
58 | 1.55k | Just(UnaryOp::Reference), |
59 | | ] |
60 | 1.55k | .boxed() |
61 | 1.55k | } |
62 | | |
63 | | /// Generate arbitrary expressions with depth limiting |
64 | 9.33k | pub fn arb_expr_with_depth(depth: u32) -> BoxedStrategy<Expr> { |
65 | 9.33k | if depth >= MAX_DEPTH { |
66 | | // Base case: only generate non-recursive expressions |
67 | 7.77k | prop_oneof![ |
68 | 7.77k | arb_literal().prop_map(|lit| Expr::new23 (ExprKind::Literal(lit)23 , Span::new23 (0, 0))), |
69 | 7.77k | arb_identifier().prop_map(|id| Expr::new24 (ExprKind::Identifier(id)24 , Span::new24 (0, 0))), |
70 | | ] |
71 | 7.77k | .boxed() |
72 | | } else { |
73 | | // Recursive case |
74 | 1.55k | prop_oneof![ |
75 | | // Literals |
76 | 1.55k | arb_literal().prop_map(|lit| Expr::new36 (ExprKind::Literal(lit)36 , Span::new36 (0, 0))), |
77 | | // Identifiers |
78 | 1.55k | arb_identifier().prop_map(|id| Expr::new45 (ExprKind::Identifier(id)45 , Span::new45 (0, 0))), |
79 | | // Binary operations |
80 | 1.55k | ( |
81 | 1.55k | arb_expr_with_depth(depth + 1), |
82 | 1.55k | arb_binary_op(), |
83 | 1.55k | arb_expr_with_depth(depth + 1), |
84 | 1.55k | ) |
85 | 1.55k | .prop_map(|(left, op, right)| {41 |
86 | 41 | Expr::new( |
87 | 41 | ExprKind::Binary { |
88 | 41 | left: Box::new(left), |
89 | 41 | op, |
90 | 41 | right: Box::new(right), |
91 | 41 | }, |
92 | 41 | Span::new(0, 0), |
93 | | ) |
94 | 41 | }), |
95 | | // Unary operations |
96 | 1.55k | (arb_unary_op(), arb_expr_with_depth(depth + 1)).prop_map(|(op, operand)| {42 |
97 | 42 | Expr::new( |
98 | 42 | ExprKind::Unary { |
99 | 42 | op, |
100 | 42 | operand: Box::new(operand), |
101 | 42 | }, |
102 | 42 | Span::new(0, 0), |
103 | | ) |
104 | 42 | }), |
105 | | // If expressions |
106 | 1.55k | ( |
107 | 1.55k | arb_expr_with_depth(depth + 1), |
108 | 1.55k | arb_expr_with_depth(depth + 1), |
109 | 1.55k | prop::option::of(arb_expr_with_depth(depth + 1)), |
110 | 1.55k | ) |
111 | 1.55k | .prop_map(|(condition, then_branch, else_branch)| {39 |
112 | 39 | Expr::new( |
113 | 39 | ExprKind::If { |
114 | 39 | condition: Box::new(condition), |
115 | 39 | then_branch: Box::new(then_branch), |
116 | 39 | else_branch: else_branch.map(Box::new), |
117 | 39 | }, |
118 | 39 | Span::new(0, 0), |
119 | | ) |
120 | 39 | }), |
121 | | ] |
122 | 1.55k | .boxed() |
123 | | } |
124 | 9.33k | } |
125 | | |
126 | | /// Generate arbitrary expressions |
127 | 1 | pub fn arb_expr() -> BoxedStrategy<Expr> { |
128 | 1 | arb_expr_with_depth(0) |
129 | 1 | } |
130 | | |
131 | | /// Generate arbitrary patterns |
132 | 0 | pub fn arb_pattern() -> BoxedStrategy<Pattern> { |
133 | 0 | prop_oneof![ |
134 | 0 | any::<i64>().prop_map(|i| Pattern::Literal(Literal::Integer(i))), |
135 | 0 | any::<bool>().prop_map(|b| Pattern::Literal(Literal::Bool(b))), |
136 | 0 | arb_identifier().prop_map(Pattern::Identifier), |
137 | 0 | Just(Pattern::Wildcard), |
138 | | ] |
139 | 0 | .boxed() |
140 | 0 | } |
141 | | |
142 | | /// Generate well-typed expressions (simplified for testing) |
143 | 2 | pub fn arb_well_typed_expr() -> BoxedStrategy<Expr> { |
144 | | // For now, just use simple expressions that are likely to be well-typed |
145 | 2 | prop_oneof![ |
146 | 19 | arb_literal2 ().prop_map2 (|lit| Expr::new(ExprKind::Literal(lit), Span::new(0, 0))), |
147 | 23 | arb_identifier2 ().prop_map2 (|id| Expr::new(ExprKind::Identifier(id), Span::new(0, 0))), |
148 | | // Simple arithmetic that's always valid |
149 | 24 | (any::<i64>(), any::<i64>())2 .prop_map2 (|(a, b)| { |
150 | 24 | Expr::new( |
151 | 24 | ExprKind::Binary { |
152 | 24 | left: Box::new(Expr::new( |
153 | 24 | ExprKind::Literal(Literal::Integer(a)), |
154 | 24 | Span::new(0, 0), |
155 | 24 | )), |
156 | 24 | op: BinaryOp::Add, |
157 | 24 | right: Box::new(Expr::new( |
158 | 24 | ExprKind::Literal(Literal::Integer(b)), |
159 | 24 | Span::new(0, 0), |
160 | 24 | )), |
161 | 24 | }, |
162 | 24 | Span::new(0, 0), |
163 | | ) |
164 | 24 | }), |
165 | | ] |
166 | 2 | .boxed() |
167 | 2 | } |