Development Guide¶
Guide for contributing to the Onion programming language.
Getting Started¶
Prerequisites¶
- JDK 17 or later
- SBT (Scala Build Tool)
- Git
- Text editor or IDE (IntelliJ IDEA recommended)
Clone the Repository¶
Build the Project¶
This will: - Download dependencies - Generate the parser from JavaCC grammar - Compile all Scala and Java source files
Project Structure¶
onion/
├── build.sbt # SBT build configuration
├── grammar/
│ └── JJOnionParser.jj # JavaCC parser grammar
├── src/
│ ├── main/
│ │ ├── scala/ # Scala source code
│ │ │ └── onion/
│ │ │ ├── compiler/ # Compiler phases
│ │ │ ├── tools/ # CLI tools
│ │ │ └── ...
│ │ └── java/ # Java runtime library
│ │ └── onion/
│ │ ├── Function0.java - Function10.java
│ │ ├── IO.java
│ │ └── ...
│ └── test/
│ ├── scala/ # Test suites
│ └── run/ # Example programs
├── run/ # Example Onion programs
└── docs/ # Documentation
Development Workflow¶
1. Create a Feature Branch¶
2. Make Changes¶
Edit source files using your preferred editor.
3. Compile¶
4. Run Tests¶
5. Test Manually¶
# Run examples
sbt 'runScript run/Hello.on'
# Or use the compiler
sbt compile
sbt 'run-main onion.tools.CompilerFrontend run/Hello.on'
java -cp . Hello
6. Format Code¶
Follow Scala style conventions: - 2 spaces for indentation - Line length limit: 120 characters - Use meaningful variable names
7. Commit Changes¶
8. Push and Create PR¶
Then create a Pull Request on GitHub.
Common Development Tasks¶
Modifying the Parser¶
Edit grammar/JJOnionParser.jj, then:
The parser will be regenerated automatically.
Adding a Language Feature¶
- Update grammar in
JJOnionParser.jj - Update AST in
AST.scala - Update type checking in
Typing.scala - Update the bytecode boundary in
codegen/TypedAstCodeGeneration.scala/backend/asm/AsmBackend.scala - If needed, update the legacy implementation body in
backend/asm/AsmCodeGeneration.scala - Add tests
Adding a Test¶
Create a new test in src/test/scala/onion/compiler/tools/:
package onion.compiler.tools
class MyFeatureSpec extends AbstractShellSpec {
"MyFeature" should "work correctly" in {
val source = """
|// Your test code here
|println("Test")
""".stripMargin
val result = runShell(source)
result should include("Test")
}
}
Run the test:
Debugging the Compiler¶
Add print statements or use a debugger:
Or use IntelliJ IDEA's debugger: 1. Set breakpoints 2. Run tests in debug mode
Code Organization¶
Compiler Phases¶
Parsing (Parsing.scala):
- Entry point for compilation
- Uses JavaCC-generated parser
- Produces untyped AST
Rewriting (Rewriting.scala):
- Normalizes AST
- Desugars complex constructs
Type Checking (Typing.scala):
- Type inference and validation
- Name resolution
- Symbol table management
- Session state is held in typing/session/TypingSession.scala
Code Generation (codegen/TypedAstCodeGeneration.scala, backend/asm/AsmBackend.scala):
- Typed-AST to bytecode boundary
- ASM-based bytecode generation
- JVM instruction emission
Support Modules¶
AST (AST.scala, TypedAST.scala):
- Abstract syntax tree definitions
Symbol Tables (ClassTable.scala, LocalContext.scala):
- Symbol management
- Scope handling
Error Handling (SemanticError.scala, CompilationReporter.scala):
- Error collection
- Error formatting
Testing Strategy¶
Unit Tests¶
Test individual components:
class TypingSpec extends AnyFlatSpec with Matchers {
"Type checker" should "infer Int type" in {
// Test type inference
}
}
Integration Tests¶
Test complete compilation:
class IntegrationSpec extends AbstractShellSpec {
"Compiler" should "compile and run program" in {
val source = """println("Hello")"""
val result = runShell(source)
result should include("Hello")
}
}
Example-Based Tests¶
Verify example programs compile and run:
Documentation¶
Code Documentation¶
Use ScalaDoc for public APIs:
/**
* Compiles Onion source code to JVM bytecode.
*
* @param source Source code string
* @param config Compiler configuration
* @return Compilation outcome
*/
def compile(source: String, config: CompilerConfig): CompilationOutcome = {
// ...
}
User Documentation¶
Update docs in docs/ directory:
- Use Markdown format
- Include code examples
- Keep examples up-to-date
Contributing Guidelines¶
Code Style¶
- Follow Scala conventions
- Use meaningful names
- Keep functions focused
- Add comments for complex logic
Commit Messages¶
Use clear, descriptive commit messages:
Add feature: Lambda expression support
- Implement lambda syntax in parser
- Add lambda type checking
- Generate Function interface calls
- Add lambda tests
Pull Request Process¶
- Fork the repository
- Create a feature branch
- Make changes with tests
- Ensure all tests pass
- Update documentation
- Create pull request
- Respond to review feedback
Issues¶
When creating issues: - Use descriptive titles - Provide example code - Include error messages - Specify Onion version
Performance Considerations¶
- Parser generation is slow; avoid unnecessary recompilation
- Type checking can be expensive for large files
- Use incremental compilation when possible
Debugging Tips¶
Compiler Crashes¶
- Identify the failing phase
- Add debug output
- Check AST structure
- Verify type information
Type Errors¶
- Check symbol table
- Verify type resolution
- Review type conversion rules
Bytecode Issues¶
- Use
javapto inspect generated bytecode - Verify stack frame correctness
- Check local variable indices
Release Process¶
- Update version in
build.sbt - Run all tests:
sbt test - Create distribution:
sbt dist - Tag release:
git tag v0.2.0 - Push tag:
git push origin v0.2.0 - Create GitHub release
See RELEASING.md for the full release process.
Next Steps¶
- Building from Source - Detailed build instructions
- Compiler Architecture - Compiler internals
- Language Specification - Language details