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

CommandDescription
npm run buildCompile TypeScript to dist/
npx vitest runRun all tests once
npx vitestRun tests in watch mode
npx vitest run tests/lexerRun 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 any type: Use proper typing or unknown with 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.ts barrel 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

DirectoryCovers
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 features
  • fix/description - Bug fixes
  • refactor/description - Code refactoring
  • docs/description - Documentation changes
  • test/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 any types introduced
  • Example programs still run correctly

Architecture for Contributors

Adding a New Language Feature

  1. Lexer: Add new token types in src/types/tokens.ts, handle in lexer
  2. Parser: Add new AST node in src/types/ast.ts, add parsing logic
  3. Interpreter: Add evaluation logic for the new node type
  4. Compiler: Add bytecode emission for the new node
  5. VM: Add new opcode handling if needed
  6. Tests: Add tests at each level (lexer, parser, interpreter, compiler, VM)
  7. Docs: Update language reference and examples

Adding a New Stdlib Function

  1. Identify the appropriate module in src/stdlib/
  2. Add the function to the module's array (follow existing patterns)
  3. Write tests in tests/stdlib/
  4. Document in docs/stdlib.html

Adding a New CLI Command

  1. Add a handler function in src/cli/index.ts
  2. Add the case to the switch statement in main()
  3. Update the help text
  4. 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

FilePurpose
tsconfig.jsonTypeScript compiler settings (strict mode, ES2022 target)
package.jsonDependencies, scripts, project metadata
vitest.config.tsTest 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.