80 lines
2.7 KiB
Rust
80 lines
2.7 KiB
Rust
use rand::Rng;
|
|
use crate::compiler::ast::{UntypedAst, OpCode};
|
|
|
|
pub fn random_sublist_ast(rng: &mut impl Rng, max_depth: usize) -> UntypedAst {
|
|
// 1) Choose how many children the top-level `Sublist` will have.
|
|
let len = rng.gen_range(1..=3);
|
|
|
|
// 2) Build a vector of sub-ASTs by calling `random_ast` for each child
|
|
let mut children = Vec::with_capacity(len);
|
|
for _ in 0..len {
|
|
// We start at `depth=1` because the top-level sublist itself is `depth=0`.
|
|
children.push(random_ast(rng, 1, max_depth));
|
|
}
|
|
|
|
// 3) Return a `Sublist` as the root node
|
|
UntypedAst::Sublist(children)
|
|
}
|
|
|
|
/// Generate a random `UntypedAst` by recursively building sub-trees,
|
|
/// int literals, or instructions.
|
|
///
|
|
/// - `depth` tracks how deep we are in the tree.
|
|
/// - `max_depth` is the maximum allowed depth to prevent infinite recursion.
|
|
pub fn random_ast(rng: &mut impl Rng, depth: usize, max_depth: usize) -> UntypedAst {
|
|
if depth >= max_depth {
|
|
// Return something "terminal,"
|
|
// e.g. an IntLiteral or a single Instruction
|
|
random_terminal(rng)
|
|
} else {
|
|
// Weighted choice:
|
|
// 0 => IntLiteral,
|
|
// 1 => single Instruction,
|
|
// 2 => Sublist with children
|
|
let choice = rng.gen_range(0..3);
|
|
match choice {
|
|
0 => UntypedAst::IntLiteral(rng.gen_range(-10..10)),
|
|
1 => UntypedAst::Instruction(random_opcode(rng)),
|
|
2 => {
|
|
// Make a sublist with 1..=3 children
|
|
let len = rng.gen_range(1..=3);
|
|
let mut children = Vec::with_capacity(len);
|
|
for _ in 0..len {
|
|
children.push(random_ast(rng, depth + 1, max_depth));
|
|
}
|
|
UntypedAst::Sublist(children)
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Generate a random "terminal" node.
|
|
/// Typically either an int literal or a single instruction.
|
|
fn random_terminal(rng: &mut impl Rng) -> UntypedAst {
|
|
// Weighted choice:
|
|
// 0 => IntLiteral
|
|
// 1 => single Instruction
|
|
let choice = rng.gen_range(0..2);
|
|
match choice {
|
|
0 => UntypedAst::IntLiteral(rng.gen_range(-10..10)),
|
|
1 => UntypedAst::Instruction(random_opcode(rng)),
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
/// Generate a random `OpCode` from your known set of instructions.
|
|
pub fn random_opcode(rng: &mut impl Rng) -> OpCode {
|
|
let choice = rng.gen_range(0..6);
|
|
// because we have 6 variants: Noop, Plus, Minus, Mult, Dup, Pop
|
|
match choice {
|
|
0 => OpCode::Noop,
|
|
1 => OpCode::Plus,
|
|
2 => OpCode::Minus,
|
|
3 => OpCode::Mult,
|
|
4 => OpCode::Dup,
|
|
5 => OpCode::Pop,
|
|
_ => unreachable!(),
|
|
}
|
|
}
|