cobble-lang 0.6.3

A modern, Python-like language for creating Minecraft Data Packs
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
# Cobble API Documentation

This document describes the internal API for Cobble's compiler and tools.

## Overview

Cobble consists of several modules that work together to compile high-level code into Minecraft data packs:

- **Parser** - Converts source code into an Abstract Syntax Tree (AST)
- **Transpiler** - Transforms AST into Minecraft commands
- **Standard Library** - Provides event handling and utilities
- **CLI** - Command-line interface for building projects

## Module: Parser

The parser module is organized into several files:

### `parser/mod.rs`

Public API for the parser module. Exports the main `parse` function and re-exports components from submodules.

### `parser/tokenizer.rs`

Manual tokenizer that handles indentation with INDENT/DEDENT tokens and distinguishes `/` division from `/` command prefix.

**Key Features:**
- Tracks indentation levels to generate INDENT/DEDENT tokens
- Handles string literals with proper escaping
- Distinguishes between division operator (`a / b`) and command prefix (`/say`)
- Processes inline comments (starting with `#`)

### `parser/combinators.rs`

The main parser built with chumsky combinator library that handles Python-style indented syntax.

#### Function: `parse(source: &str) -> Result<Program, Vec<String>>`

Parses Cobble source code into an AST using chumsky parser combinators.

**Parameters:**
- `source` - The source code string to parse

**Returns:**
- `Ok(Program)` - Successfully parsed program
- `Err(Vec<String>)` - Parse errors with messages

**Example:**
```rust
use cobble::parser::parse;

let source = r#"
def hello():
    /say Hello, world!
"#;

let program = parse(source)
    .map_err(|errors| errors.join(", "))?;
```

**Implementation Details:**
- Uses chumsky 0.11 parser combinator library
- Supports all Cobble syntax including execute blocks, global keyword, and complex expressions
- **Operator precedence**: Four-level precedence for expressions (pow > mul/div/mod > add/sub > comparisons)
- **Multi-operator expressions**: Chains operations using `.foldl().repeated()` pattern
- Error reporting integrated with ariadne for beautiful error messages

**Historical Parser Enhancements**:
- Fixed division operator tokenization to correctly distinguish `a / b` from `/command`
- Implemented proper operator precedence (multiplication and division before addition and subtraction)
- Added support for chained multi-operator expressions like `a + b + c` and `x * y * z`

## Module: AST

### `ast.rs`

Defines the Abstract Syntax Tree structures.

#### Struct: `Program`

Represents a complete Cobble program.

```rust
pub struct Program {
    pub imports: Vec<Import>,
    pub statements: Vec<Statement>,
}
```

#### Enum: `Statement`

Represents different types of statements.

```rust
pub enum Statement {
    Import(Import),
    FunctionDef(FunctionDef),
    Assignment(Assignment),
    ConstAssignment(ConstAssignment),  // v0.3.0
    Expression(Expression),
    If(IfStatement),
    For(ForLoop),
    While(WhileLoop),
    Match(MatchStatement),  // v0.3.0
    Return(Option<Expression>),
    Pass,
    MinecraftCommand(String),
    Global(Vec<String>),
    Execute(ExecuteBlock),
    SelectorDef(SelectorDef),  // v0.4.0
    EntityDef(EntityDef),
    CreateEntity(String),
}
```

**Recent Additions:**
- **v0.4.0**: `SelectorDef` - Entity selector definitions (e.g., `@Player = @a[type=player]`)
- **v0.3.0**: `ConstAssignment` - Compile-time constants (e.g., `const MAX_HEALTH = 100`)
- **v0.3.0**: `Match` - Match/switch statements with literal, range, and wildcard patterns

#### Struct: `FunctionDef`

Represents a function definition.

```rust
pub struct FunctionDef {
    pub name: String,
    pub params: Vec<Parameter>,
    pub body: Vec<Statement>,
    pub decorators: Vec<String>,
}
```

#### Struct: `Parameter`

Represents a function parameter.

```rust
pub struct Parameter {
    pub name: String,
    pub default: Option<Expression>,
}
```

#### Struct: `ConstAssignment` (v0.3.0)

Represents a compile-time constant assignment.

```rust
pub struct ConstAssignment {
    pub target: String,
    pub value: Expression,
}
```

Constants are evaluated at compile time and replaced with their literal values.

#### Struct: `MatchStatement` (v0.3.0)

Represents a match/switch statement.

```rust
pub struct MatchStatement {
    pub value: Expression,  // Expression to match against
    pub cases: Vec<MatchCase>,
}

pub struct MatchCase {
    pub pattern: MatchPattern,
    pub body: Vec<Statement>,
}

pub enum MatchPattern {
    Literal(i32),        // case 5:
    Range(i32, i32),     // case 1 to 10:
    Wildcard,            // case _:
}
```

Match statements compile to efficient 4-way split algorithm for optimal branching.

#### Struct: `SelectorDef` (v0.4.0)

Represents an entity selector definition.

```rust
pub struct SelectorDef {
    pub name: String,      // e.g., "Player"
    pub selector: String,  // e.g., "@a[type=player,gamemode=survival]"
}
```

Selector aliases are replaced at compile time with zero runtime overhead.

#### Enum: `Expression`

Represents different types of expressions.

```rust
pub enum Expression {
    Number(f64),
    String(String),
    Boolean(bool),
    None,
    Array(Vec<Expression>),
    Map(Vec<(String, Expression)>),
    Identifier(String),
    Attribute(Box<Expression>, String),
    Binary(Box<Expression>, BinaryOp, Box<Expression>),
    Unary(UnaryOp, Box<Expression>),
    Call(Box<Expression>, Vec<Expression>),
    Subscript(Box<Expression>, Box<Expression>),
}
```

#### Enum: `BinaryOp`

Binary operators.

```rust
pub enum BinaryOp {
    Add, Sub, Mul, Div, Mod, Pow,
    Eq, NotEq, Lt, LtEq, Gt, GtEq,
    And, Or,
}
```

## Module: Transpiler

The transpiler module is organized into several files:

### `transpiler/mod.rs`

Main transpiler that converts AST into Minecraft data pack format.

#### Struct: `Transpiler`

The main transpiler instance.

```rust
pub struct Transpiler {
    data_pack: DataPack,
    current_function: Option<Vec<String>>,
    current_context: FunctionContext,
    variables: HashMap<String, Expression>,
    module_level_vars: HashMap<String, Expression>,  // Top-level variables to initialize
    constants: HashMap<String, i32>,                  // v0.3.0: Compile-time constants
    selector_aliases: HashMap<String, String>,        // v0.4.0: Entity selector definitions
    imported_files: HashSet<PathBuf>,                 // v0.4.0: Imported file tracking
    current_file_dir: PathBuf,                        // v0.4.0: Current file directory for imports
    temp_counter: u32,
    variable_objectives: HashMap<String, String>,  // Tracks which objective each variable uses
    function_params: HashMap<String, Vec<String>>,  // Tracks parameter names for each function
}
```

### `transpiler/command_processor.rs`

Processes Minecraft command strings with variable substitution.

**Key Features:**
- Substitutes function parameters (macro variables)
- Converts scoreboard variables in JSON text components
- Handles selector alias replacement
- Escapes strings properly for Minecraft commands

### `transpiler/expression_evaluator.rs`

Evaluates complex expressions into scoreboard operations.

**Key Features:**
- Converts arithmetic expressions to scoreboard operations
- Handles power operator with compile-time expansion
- Generates temporary variables for intermediate results
- Optimizes self-assignment operations

### `transpiler/condition_translator.rs`

Translates Python-style conditions to Minecraft execute conditions.

**Key Features:**
- Converts comparison operators to scoreboard matches
- Handles boolean AND/OR/NOT operators
- Supports literal comparisons on both sides
- Generates OR logic using temporary variables

### `transpiler/data_pack.rs`

Manages data pack structure and file generation.

**Key Features:**
- Tracks objectives and functions
- Generates `pack.mcmeta` with the supported pack format
- Creates modern `data/<namespace>/function` and `data/<namespace>/tags/function` files
- Writes namespaced JSON resources such as predicates, recipes, dialogs, and tags
- Handles standard library event listeners

### `transpiler/statement_processors/`

Individual processors for each statement type:
- `assignment.rs` - Variable assignments with type checking
- `if_processor.rs` - If/elif/else statements with function extraction
- `loop_processor.rs` - For and while loops
- `match_processor.rs` - Match/switch statements with 4-way split
- `execute_processor.rs` - Execute block modifiers
- `selector_processor.rs` - Selector definitions

**Historical Transpiler Enhancements:**
- **v0.4.0**:
  - **Entity selector definitions**: Custom selector aliases with compile-time replacement
  - **File import system**: Import functions and definitions from other `.cbl` files
  - **Circular dependency prevention**: Automatic detection via `imported_files` HashSet
  - **Relative import resolution**: Resolve imports relative to current file location
- **v0.3.0**:
  - **Compile-time constants**: `const` keyword for constant declarations
  - **Match/switch statements**: Efficient multi-way branching with 4-way split algorithm
- **v0.1.0**:
  - **Module-level variable initialization**: Top-level assignments are automatically initialized in `_cobble_init`
  - **Complex expression evaluation**: New `evaluate_expression_to_target()` helper handles nested binary expressions
  - **Correct loop variable objectives**: Loop variables (like `i` in for loops) now correctly track their objective
  - **Duplicate objective prevention**: `ensure_init_function()` checks for existing objectives before adding

#### Implementation: `Transpiler`

##### `new(namespace: String, output_dir: PathBuf) -> Self`

Creates a new transpiler instance.

**Parameters:**
- `namespace` - The data pack namespace
- `output_dir` - Output directory path

##### `transpile(&mut self, program: &Program) -> Result<(), String>`

Transpiles a program into a data pack.

**Parameters:**
- `program` - The parsed program AST

**Returns:**
- `Ok(())` - Successfully transpiled
- `Err(String)` - Transpilation error

##### `write_data_pack(&self) -> std::io::Result<()>`

Writes the data pack to disk.

**Example:**
```rust
use cobble::pack_format::PackFormat;
use cobble::transpiler::Transpiler;
use cobble::parser::parse;
use std::path::PathBuf;

let source = "...";
let program = parse(source)
    .map_err(|errors| errors.join(", "))?;

let mut transpiler = Transpiler::new(
    "my_namespace".to_string(),
    PathBuf::from("./output")
);

transpiler.transpile(&program)?;
transpiler.write_data_pack()?;
```

### Struct: `DataPack`

Represents a Minecraft data pack.

```rust
pub struct DataPack {
    pub namespace: String,
    pub description: String,
    pub output_dir: PathBuf,
    pub functions: HashMap<String, Vec<String>>,
    pub tags: HashMap<String, Vec<String>>,
    pub advancements: HashMap<String, String>,
    pub loot_tables: HashMap<String, String>,
    pub recipes: HashMap<String, String>,
    pub predicates: HashMap<String, String>,
    pub item_modifiers: HashMap<String, String>,
    pub json_resources: HashMap<String, String>,
    pub command_metadata: HashMap<String, HashMap<usize, GeneratedCommand>>,
    pub pack_format: PackFormat,  // Cobble v0.6.3 requires 101.1
    pub stdlib: StdLib,
    pub used_objectives: HashSet<String>,
    pub source_display_root: Option<PathBuf>,
}
```

**Note**: `pack_format` uses the `PackFormat` enum. Cobble v0.6.3 targets Minecraft Java Edition 26.1.2 and requires `PackFormat::Decimal(101, 1)`, serialized into `pack.mcmeta` as `min_format` and `max_format` arrays.

#### Methods

##### `new(namespace: String, output_dir: PathBuf) -> Self`

Creates a new data pack.

##### `add_function(&mut self, name: String, commands: Vec<String>)`

Adds a function to the data pack.

##### `write(&self) -> std::io::Result<()>`

Writes the data pack to the file system, including `.cobble/source_map.json`
when generated command metadata exists and `.cobble/build_manifest.json` for
build metadata. Source-map source paths are normalized against
`source_display_root` when available.

##### `set_source_display_root(&mut self, root: PathBuf)`

Sets the display root used to avoid unnecessary absolute source paths in
`.cobble/source_map.json`.

### Struct: `BuildManifest`

Summary metadata written to `.cobble/build_manifest.json`.

```rust
pub struct BuildManifest {
    pub version: u8,
    pub cobble_version: String,
    pub minecraft_version: String,
    pub pack_format: PackFormat,
    pub pack_format_text: String,
    pub namespace: String,
    pub description: String,
    pub input: Option<BuildManifestInput>,
    pub generated_namespaces: Vec<String>,
    pub generated: BuildManifestGenerated,
    pub resources: Vec<BuildManifestResourceEntry>,
    pub validation: Option<BuildManifestValidation>,
}

pub struct BuildManifestInput {
    pub source: String,
    pub entry_points: Vec<String>,
    pub compiled_files: Vec<String>,
}

pub struct BuildManifestResourceEntry {
    pub kind: String,
    pub namespace: String,
    pub path: String,
}

pub struct BuildManifestValidation {
    pub enabled: bool,
    pub commands_json: String,
    pub files_checked: usize,
    pub commands_checked: usize,
    pub macro_commands_checked: usize,
    pub commands_skipped: usize,
    pub errors: usize,
    pub source_map_errors: usize,
}
```

## Module: Standard Library

### `stdlib.rs`

Provides the Cobble standard library implementation.

#### Struct: `StdLib`

```rust
pub struct StdLib {
    event_listeners: HashMap<EventType, Vec<String>>,
}
```

#### Enum: `EventType`

```rust
pub enum EventType {
    Load,
    Tick,
    Custom(String),
}
```

#### Methods

##### `new() -> Self`

Creates a new standard library instance.

##### `add_event_listener(&mut self, event_type: EventType, function_name: String)`

Registers a function as an event listener.

**Parameters:**
- `event_type` - The type of event to listen for
- `function_name` - The name of the function to call

##### `generate_tags(&self, namespace: &str) -> HashMap<String, Vec<String>>`

Generates Minecraft function tags for registered events.

**Returns:**
- HashMap of tag names to function lists

## Module: Configuration

### `config.rs`

Handles project configuration.

#### Struct: `CobbleConfig`

```rust
pub struct CobbleConfig {
    pub project: ProjectConfig,
    pub build: BuildConfig,
}

pub struct ProjectConfig {
    pub name: String,
    pub description: String,
    pub namespace: String,
    pub version: String,
    pub pack_format: String,
}

pub struct BuildConfig {
    pub source: String,
    pub output: String,
    pub entry_points: Vec<String>,
}
```

#### Functions

##### `CobbleConfig::load(path: impl AsRef<Path>) -> Result<CobbleConfig, String>`

Loads and validates configuration from a `cobble.toml` file.

##### `CobbleConfig::load_unvalidated(path: impl AsRef<Path>) -> Result<CobbleConfig, String>`

Loads configuration without enforcing the current release's pack format. The build command uses this only when the user explicitly overrides `--pack-format`.

##### `CobbleConfig::default_with_name(name: String) -> CobbleConfig`

Creates a default configuration.

## Module: Commands

### `commands/build.rs`

Handles the `build` command.

#### Function: `build(options: BuildOptions) -> Result<(), String>`

Executes the build command.

**Parameters:**
- `options` - Input/output paths, namespace, pack format, zip, and validation settings

**Returns:**
- `Ok(())` - Build succeeded
- `Err(String)` - Build error

### `commands/init.rs`

Handles the `init` command.

#### Function: `init(options: InitOptions) -> Result<(), String>`

Initializes a new Cobble project.

### `commands/validate.rs`

Handles command-tree validation for generated data packs.

#### Function: `validate(options: ValidateOptions) -> Result<(), String>`

Validates `.mcfunction` files against `data/commands.json` and checks generated source maps when present.

### `commands/doctor.rs`

Handles project and environment diagnostics.

#### Function: `doctor(options: DoctorOptions) -> Result<(), String>`

Reports Cobble version, Minecraft target, pack format, Java/curl availability,
project config status, and command-tree fingerprint status without contacting
the network.

### `commands/inspect.rs`

Handles generated metadata inspection.

#### Function: `inspect(options: InspectOptions) -> Result<(), String>`

Reads `.cobble/build_manifest.json` and `.cobble/source_map.json` from a
generated data pack directory and prints either a text summary or formatted JSON.

### `commands/check.rs`

Handles the `check` command.

#### Function: `check(input: Option<PathBuf>) -> Result<(), String>`

Checks source files for syntax errors.

### `commands/watch.rs`

Handles the `watch` command.

#### Function: `watch(...) -> Result<(), String>`

Watches files for changes and rebuilds automatically.

## Error Handling

### Module: `error.rs`

#### Enum: `CobbleError`

```rust
pub enum CobbleError {
    ParseError(String),
    IoError(std::io::Error),
    TranspileError(String),
}
```

All compiler errors are wrapped in this enum for consistent handling.

## Usage Examples

### Basic Compilation

```rust
use cobble::parser::parse;
use cobble::transpiler::Transpiler;
use std::path::PathBuf;

fn compile(source: &str, output_dir: &str) -> Result<(), String> {
    // Parse
    let program = parse(source)
        .map_err(|errors| format!("Parse errors: {}", errors.join(", ")))?;

    // Transpile
    let mut transpiler = Transpiler::new(
        "my_pack".to_string(),
        PathBuf::from(output_dir)
    );

    transpiler.transpile(&program)
        .map_err(|e| format!("Transpile error: {}", e))?;

    // Write
    transpiler.write_data_pack()
        .map_err(|e| format!("IO error: {}", e))?;

    Ok(())
}
```

### Custom Data Pack Options

```rust
use cobble::transpiler::Transpiler;

let mut transpiler = Transpiler::new(
    "custom_pack".to_string(),
    PathBuf::from("./output")
);

// Customize pack settings
transpiler.set_description("My Custom Data Pack".to_string());
transpiler.set_pack_format(PackFormat::Decimal(101, 1));  // Minecraft Java Edition 26.1.2

transpiler.transpile(&program)?;
transpiler.write_data_pack()?;
```

### AST Traversal

```rust
use cobble::ast::{Statement, Expression};

fn count_functions(program: &Program) -> usize {
    program.statements.iter().filter(|stmt| {
        matches!(stmt, Statement::FunctionDef(_))
    }).count()
}

fn find_minecraft_commands(statements: &[Statement]) -> Vec<String> {
    statements.iter().filter_map(|stmt| {
        match stmt {
            Statement::MinecraftCommand(cmd) => Some(cmd.clone()),
            _ => None
        }
    }).collect()
}
```

## Internal Architecture

### Compilation Pipeline

```
Source Code (.cbl)
[Parser] - Lexical Analysis & Parsing
Abstract Syntax Tree (AST)
[Transpiler] - Code Generation
Minecraft Commands
[DataPack Writer] - File Generation
Data Pack (.mcfunction files, pack.mcmeta, tags)
```

### Key Design Decisions

1. **Python-style Syntax**: Familiar to many programmers, clean and readable
2. **chumsky Parser Combinators**: Modern, composable parsing with excellent error handling
3. **Operator Precedence**: Four-level precedence system matches standard mathematical conventions (pow > mul/div/mod > add/sub > comparisons)
4. **Complex Expression Handling**: Recursive evaluation for nested binary expressions with temporary variables
5. **Macro-based Parameters**: Uses Minecraft's function macro system for function parameters
6. **Automatic Recursion**: For loops and while loops compile to recursive functions
7. **Separate Functions for Complex Control Flow**: Nested if statements become separate functions to avoid command limit issues
8. **Scoreboard Variables**: All variables are stored as scoreboard objectives with proper tracking
9. **Module-level Initialization**: Top-level variables automatically initialized at pack load
10. **ariadne Error Reporting**: Beautiful, user-friendly error messages with context

## Performance Considerations

### Compilation Speed

- Parsing is O(n) where n is source code length
- Transpilation is O(m) where m is AST node count
- File writing is O(f) where f is function count

### Generated Code Size

- Simple functions: 1-10 commands
- For loops: 2-3 functions (init + loop function)
- While loops: 1-2 functions
- Nested control flow: Additional helper functions as needed

## Contributing

When adding new features to the API:

1. Update the AST structures in `ast.rs`
2. Add parser support in `parser.rs` using chumsky combinators
3. Implement transpilation in `transpiler.rs`
4. Add tests in `tests/`
5. Update this documentation

### Parser Development

When extending the parser:
- Tokenizer is in `parser.rs::tokenize()` - handles lexical analysis and indentation
- Parser combinators are in `parser.rs::token_parser()` - handles syntax analysis
- Use chumsky's `recursive()` for nested structures
- Test with both unit tests in `parser.rs` and integration tests in `tests/`

## Further Reading

- [Language Reference]language.md - Cobble language syntax
- [CLI Documentation]cli.md - Command-line usage
- [Examples]../examples/ - Example code
- [Rust Documentation]https://doc.rust-lang.org/book/ - Rust programming language