Carbon Lang Script — Compiler & Runtime Requirements
This document is intended for the developer building the Carbon Lib mod and the CJ compiler. It describes everything the compiler and runtime must do.
Overview
The Carbon Lang Script compiler is built into Carbon Lib (a Fabric mod).
It runs entirely at mod-load time — modders never run a compiler themselves.
The compiler takes .cj source files and produces JVM bytecode via ASM.
1. Mod Zip Loader
On startup (and on hot-reload), Carbon Lib must:
- Scan the mods directory for
.zipfiles. - For each zip, read and validate
carbon.mod.jsonat the root. - Unzip the mod into a temp directory.
- Resolve and fetch any missing dependencies from Modrinth.
- Run the Trust Audit (check mod against whitelist for XP eligibility).
- Load resources from
resources/into the game's resource pipeline. - Compile and load all
.cjfiles fromsrc/. - Register all items, blocks, mobs, commands, effects defined in definitions/.
Load Order
src/definitions/— items, blocks, mobs, potions (in any order within folder)src/commands/— command registrationsrc/recipes/— recipe registrationsrc/scripts/— action and behaviour scripts (loaded on demand, not all upfront)
2. Lexer
The lexer tokenizes raw .cj text into a stream of tokens.
Token Types
HEADER— the wordcarbonjavaon line 1KEYWORD—add,end,on,run,if,anif,else,not,or,loop,stop,input,modifier,action,fire,effect,allotrope,copy,trigger,execute,recipeIDENTIFIER— any name likeruby_sword,weak_skeletonTYPE_NAME— known block types:item,mob,block,command,behaviour,action,effect,potionPROPERTY— known property names:name,health,damage,type, etc.VALUE— content inside( )parenthesesTARGET—#p,#s,#a,#w,#mod,#e,#band dot-accessSELECTOR—nearest_entity,target_entityNUMBER— integer or float literalSTRING— quoted string literalBOOL—trueorfalseOPERATOR—+,-,*,/,%,=,+=,-=,*=,/=,<,>,<=,>=COMMENT— everything after@on a line (discarded)NEWLINE— significant for block separationEOF
3. Parser
The parser builds an Abstract Syntax Tree (AST) from the token stream.
Top-Level Structure
Program
├── Header ("carbonjava")
└── Statement[]
├── AddBlock
├── Assignment
├── IfStatement
├── LoopStatement
├── RunStatement
├── OnBlock
├── AllotropeBlock
└── InputDeclaration
Key Parsing Rules
- First line must be
carbonjavaor the file is rejected. add [type]opens a block. All indented or following lines untilendbelong to that block.on (event)blocks end at the nexton, the nextadd, orend.- Empty lines separate
onblocks within anaddblock. if/anif/elsedo not useend— they end at the next statement.loopusesendto close.allotropeblocks contain raw Java between#atand#end— this section must be passed through to ASM without further parsing.- Recipe files do not start with
carbonjavaand use their own simple parser.
4. Type Inference
The compiler infers variable types from their assigned values:
| Value pattern | Inferred type |
|---|---|
Integer literal (10) |
int |
Float literal (1.5) |
float |
Quoted string ("text") |
String |
true / false |
boolean |
nearest_entity, target_entity, #p, #s |
EntitySelector |
Types are tracked in a symbol table per file scope. Persistent data (#p., #w., etc.)
is stored in a Carbon-managed persistent store and typed on first assignment.
5. ASM Code Generation
The compiler uses ASM to generate JVM bytecode from the AST.
Each add block compiles to:
- A registered Fabric object (Item, Block, EntityType, etc.)
- Event listener registrations for each
onblock - Behaviour/AI goal registrations for mob
add behaviourblocks
Each script file compiles to:
- A Java class with a main
execute()method - Input parameters become method arguments
- Variables become local variables or persistent store reads/writes
runcalls become method invocations on compiled script classesloopcompiles to a while loop or a scheduled tick handlerif/anif/elsecompile to standard Java conditionals
Allotrope blocks:
- Raw Java between
#atand#endis injected via ASM at the specified injection point in the target class.
6. Error Reporting
Errors must be clear, friendly, and actionable. Format:
[Carbon] Error in src/definitions/items.cj line 6:
"durabilty" is not recognised. Did you mean "durability"?
Error Types
- Unknown keyword — suggest closest match (Levenshtein distance ≤ 2)
- Missing
end— tell the user whichaddblock was not closed - Wrong value type — e.g.
damage ("hello")— damage expects a number - Missing required property — e.g.
add itemwith noname - File not found —
run (missing.cj)— file does not exist in src/ - Wrong classloader target — Allotrope mixin in wrong parent/child array
On any error: - Print the error with file, line number, and suggestion. - Do not load the mod. - Continue loading other mods unaffected.
7. Sandboxing
Scripts must not be able to:
- Call arbitrary Java classes outside the Carbon API
- Access System.exit, ProcessBuilder, file I/O outside the mod zip
- Access Parent classloader secrets (auth tokens, IPC keys)
- Load or instantiate arbitrary classes via reflection
The ASM code generator must whitelist which Java packages compiled scripts can reference. Any attempt to reference a non-whitelisted class is a compile error.
8. Hot Reload
When a mod zip is modified: 1. Unregister all items/blocks/mobs/events from the previous version. 2. Discard the old compiled classes. 3. Re-run the full load pipeline on the new zip. 4. Re-register everything. 5. Report success or error to the Carbon Loader terminal and HUD.
9. Resource Pipeline
Files in resources/ are handled as follows:
| Type | Handling |
|---|---|
.png |
Registered as Minecraft texture (item, block, entity) |
.glb |
Loaded as 3D model, registered for the item/mob/block that references it |
.obj |
Same as .glb |
.mp3 / .ogg |
Registered as a Minecraft sound event |
Resources are passed to the client. Carbon Lib handles the Fabric resource pack injection automatically — the modder does not need to write any resource pack JSON.