Full Moon’s AST nodes are immutable by default, using a builder pattern for modifications. Each node has with_* methods that return new instances with updated values.
All AST nodes use the builder pattern for modifications:
use full_moon::ast::*;// Create a new return statementlet return_stmt = Return::new() .with_token(TokenReference::basic_symbol("return ")) .with_returns(Punctuated::new());
Immutable AST nodes ensure thread safety and make it easier to reason about transformations. When you “modify” a node, you create a new instance with the changes applied.
use full_moon::ast::*;// Create: function add(a, b) return a + b endlet params = Punctuated::from_iter([ Parameter::Name(TokenReference::basic_symbol("a")), Parameter::Name(TokenReference::basic_symbol("b")),]);let body = FunctionBody::new() .with_parameters(params) .with_block(/* block with return statement */);let func = AnonymousFunction::new() .with_body(body);
use full_moon::parse;use full_moon::ast::*;let ast = parse("local x = 1")?;let block = ast.nodes();// Get existing statementslet stmts: Vec<_> = block.stmts_with_semicolon().cloned().collect();// Create a new block with modified statementslet new_block = Block::new() .with_stmts(stmts);
use full_moon::ast::*;// Original: for i = 1, 10 do endlet numeric_for = NumericFor::new( TokenReference::basic_symbol("i"), Expression::Number(TokenReference::basic_symbol("1")), Expression::Number(TokenReference::basic_symbol("10")),);// Change to: for i = 1, 100 do endlet modified = numeric_for .with_end(Expression::Number(TokenReference::basic_symbol("100")));// Add step: for i = 1, 100, 2 do endlet with_step = modified .with_step(Some(Expression::Number(TokenReference::basic_symbol("2")))) .with_end_step_comma(Some(TokenReference::basic_symbol(", ")));
Many AST nodes contain Punctuated<T> for comma-separated lists:
use full_moon::ast::punctuated::Punctuated;use full_moon::tokenizer::TokenReference;// Create a punctuated list: a, b, clet mut list = Punctuated::new();list.push(Pair::new( TokenReference::basic_symbol("a"), Some(TokenReference::basic_symbol(", ")),));list.push(Pair::new( TokenReference::basic_symbol("b"), Some(TokenReference::basic_symbol(", ")),));list.push(Pair::End( TokenReference::basic_symbol("c"),));// Iterate over valuesfor value in list.iter() { println!("Value: {}", value);}
The last element in a Punctuated list uses Pair::End (no comma), while others use Pair::Punctuated (with comma).
use full_moon::tokenizer::TokenReference;// Clone formatting from an existing tokenlet original = TokenReference::basic_symbol("original");// Create new token with same trivialet leading = original.leading_trivia().cloned().collect();let trailing = original.trailing_trivia().cloned().collect();let new_token = TokenReference::new( leading, Token::new(TokenType::Identifier { identifier: "new".into() }), trailing,);
use full_moon::ast::*;// Convert local assignment to regular assignmentfn local_to_assignment(local: LocalAssignment) -> Option<Assignment> { // Extract variables and expressions let vars = local.names().iter().map(|name| { Var::Name(name.clone()) }).collect(); let exprs = local.expressions().clone(); Some(Assignment::new( Punctuated::from_iter(vars), exprs, ))}
When the luau feature is enabled, you can work with type annotations:
#[cfg(feature = "luau")]use full_moon::ast::*;// Add type annotation to function parameterlet type_specifier = TypeSpecifier::new( TokenReference::basic_symbol(": "), TypeInfo::Basic(TokenReference::basic_symbol("number")),);let body = FunctionBody::new() .with_type_specifiers(vec![Some(type_specifier)]);