Contributing
Thank you for your interest in contributing to TinyLang! This guide covers everything you need to know to get started.
Development Setup
Prerequisites
- Node.js 18+ (22 recommended)
- npm 9+
- Git
Initial Setup
# Clone the repository
git clone https://github.com/tinylang/tinylang.git
cd tinylang
# Install dependencies
npm install
# Build the project
npm run build
# Run all tests
npx vitest run
# Verify everything works
node dist/cli/index.js run examples/01-hello.tiny
Development Commands
| Command | Description |
|---|---|
npm run build | Compile TypeScript to dist/ |
npx vitest run | Run all tests once |
npx vitest | Run tests in watch mode |
npx vitest run tests/lexer | Run tests for a specific module |
npm run build && node dist/cli/index.js run <file> | Build and run a program |
Coding Standards
TypeScript Guidelines
- Strict mode: All TypeScript strict checks are enabled
- No
anytype: Use proper typing orunknownwith type guards - JSDoc comments: All public methods and exported functions must have JSDoc
- Descriptive naming: Use clear, descriptive variable and function names
- Error messages: Include educational hints that help users understand and fix issues
File Organization
- Each module lives in its own directory under
src/ - Export public API through an
index.tsbarrel file - Tests mirror the source structure under
tests/ - AST node types are defined in
src/types/ast.ts - Runtime values in
src/types/values.ts - Token types in
src/types/tokens.ts
Code Style
/**
* Calculate the factorial of a number.
* Returns 1 for n <= 1.
*/
export function factorial(n: number): number {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
Error Messages
TinyLang prioritizes helpful error messages. When throwing errors:
// Good - educational, actionable
throw new RuntimeError(
`Cannot call '${name}' - it is a ${type}, not a function. ` +
`Did you mean to access a property instead?`
);
// Bad - unhelpful
throw new Error("not callable");
Testing
Test Framework
We use Vitest for testing. Tests are in the tests/ directory, mirroring the src/ structure.
Writing Tests
import { describe, it, expect } from 'vitest';
import { Lexer } from '../src/lexer';
describe('Lexer', () => {
it('tokenizes numbers', () => {
const lexer = new Lexer('42 3.14');
const tokens = lexer.tokenize();
expect(tokens[0].type).toBe('NUMBER');
expect(tokens[0].value).toBe('42');
});
});
Test Categories
| Directory | Covers |
|---|---|
tests/lexer/ | Token generation, edge cases, error recovery |
tests/parser/ | AST structure, operator precedence, error messages |
tests/interpreter/ | Expression evaluation, control flow, scoping |
tests/compiler/ | Bytecode generation, optimization passes |
tests/vm/ | VM execution, stack operations, closures |
tests/stdlib/ | Standard library function behavior |
tests/formatter/ | Code formatting output |
tests/linter/ | Lint rule detection and auto-fix |
Running Tests
# All tests
npx vitest run
# Specific module
npx vitest run tests/compiler
# Watch mode (re-runs on changes)
npx vitest
# With coverage
npx vitest run --coverage
Pull Request Process
Branch Naming
feat/description- New featuresfix/description- Bug fixesrefactor/description- Code refactoringdocs/description- Documentation changestest/description- Test additions/fixes
Commit Messages
Use conventional commit format:
feat: add pattern matching with match/when syntax
fix: resolve closure variable capture in nested loops
refactor: extract token types into separate module
docs: add stdlib function examples
test: add edge cases for array methods
PR Checklist
- All existing tests pass (
npx vitest run) - New code has tests covering happy path and edge cases
- TypeScript compiles without errors (
npm run build) - Public APIs have JSDoc comments
- Error messages are educational and include hints
- No
anytypes introduced - Example programs still run correctly
Architecture for Contributors
Adding a New Language Feature
- Lexer: Add new token types in
src/types/tokens.ts, handle in lexer - Parser: Add new AST node in
src/types/ast.ts, add parsing logic - Interpreter: Add evaluation logic for the new node type
- Compiler: Add bytecode emission for the new node
- VM: Add new opcode handling if needed
- Tests: Add tests at each level (lexer, parser, interpreter, compiler, VM)
- Docs: Update language reference and examples
Adding a New Stdlib Function
- Identify the appropriate module in
src/stdlib/ - Add the function to the module's array (follow existing patterns)
- Write tests in
tests/stdlib/ - Document in
docs/stdlib.html
Adding a New CLI Command
- Add a handler function in
src/cli/index.ts - Add the case to the switch statement in
main() - Update the help text
- Document in
docs/cli-reference.html
Development Environment
Recommended Tools
- Editor: VS Code with TypeScript support
- Node.js: v22 (for latest features)
- Terminal: Any with color support for TinyLang CLI output
Project Configuration
| File | Purpose |
|---|---|
tsconfig.json | TypeScript compiler settings (strict mode, ES2022 target) |
package.json | Dependencies, scripts, project metadata |
vitest.config.ts | Test runner configuration |
Getting Help
- Read the Architecture Guide to understand the codebase
- Look at existing implementations for patterns to follow
- Check existing tests to understand expected behavior
- Open an issue to discuss major changes before implementing
First Contribution?
Start with something small: fix a typo in docs, add a missing test case, or improve an error message. These are great ways to learn the codebase.