Contributing

Thank you for your interest in contributing to the Meow programming language! This guide covers everything you need to know to get started.

Development Environment Setup

Prerequisites

  • Go 1.26+ — required (see go.mod)
  • stringer — for code generation of token type names
# Install stringer
go install golang.org/x/tools/cmd/stringer@latest

Clone and Build

git clone https://github.com/135yshr/meow.git
cd meow
go build ./cmd/meow

Run Tests

go test ./...

Build and Run

# Build the compiler
go build ./cmd/meow

# Run a .nyan program
go run ./cmd/meow run examples/hello.nyan

# Show transpiled Go code
go run ./cmd/meow transpile examples/hello.nyan

# Run tests
go test ./...

# Run tests verbose
go test ./... -v

# Static analysis
go vet ./...

# Update golden files (required when changing compiler output)
go test ./compiler/ -update

# Regenerate stringer output (after changing token types)
go generate ./...

Project Structure

flowchart LR
    subgraph meow[" meow/ "]
        direction TB
        subgraph cmd["cmd/meow/ — CLI entry point"]
            cmd_main["main.go"]
        end
        subgraph compiler["compiler/ — Pipeline orchestration + E2E tests"]
            comp_go["compiler.go"]
            comp_test["compiler_test.go"]
        end
        subgraph pkg["pkg/"]
            token["token/ — Token types, keywords, positions<br/>token.go, tokentype_string.go"]
            lexer["lexer/ — iter.Seq-based tokenizer<br/>lexer.go, lexer_test.go"]
            ast["ast/ — AST node definitions<br/>ast.go, types.go, walk.go"]
            parser["parser/ — Pratt parser (iter.Pull)<br/>parser.go, parser_test.go"]
            checker["checker/ — Type checker<br/>checker.go"]
            types["types/ — Type system definitions<br/>types.go"]
            codegen["codegen/ — AST → Go source generation<br/>codegen.go, codegen_test.go"]
            formatter["formatter/ — Code formatter<br/>formatter.go"]
            linter["linter/ — Code linter<br/>linter.go"]
            mutation["mutation/ — Mutation testing<br/>mutation.go"]
        end
        subgraph runtime["runtime/"]
            meowrt["meowrt/ — Core runtime: Value, operators, builtins<br/>value.go, operators.go, builtins.go, list.go"]
            file["file/ — File I/O (snoop, stalk)<br/>file.go"]
            http["http/ — HTTP client (pounce, toss, knead, swat, prowl)<br/>http.go"]
            testing["testing/ — Test framework (judge, expect, refuse, run)<br/>testing.go"]
            coverage["coverage/ — Statement coverage tracking<br/>coverage.go"]
        end
        examples["examples/ — Sample .nyan programs"]
        testdata["testdata/ — Golden file tests (.nyan + .golden)"]
        docs["docs/ — Documentation"]
    end

Adding a New Keyword

  1. Add the token in pkg/token/token.go:

    • Add a constant in the const block (between keywordsStart and keywordsEnd)
    • Add an entry in the keywords map
  2. Regenerate stringer output:

    go generate ./...
    
  3. Update the lexer if needed (usually no changes — keywords are handled by LookupIdent)

  4. Update the parser in pkg/parser/parser.go:

    • Add a case in parseStmt() or parsePrefix() as appropriate
    • Implement the parsing function
  5. Add AST node in pkg/ast/ast.go if needed

  6. Update codegen in pkg/codegen/codegen.go:

    • Handle the new AST node in genStmt() or genExpr()
  7. Add tests:

    • Parser tests
    • Golden file tests in testdata/
    • Example in examples/
  8. Update documentation:

    • docs/reference.md — add to keywords table
    • docs/spec.md — add grammar and semantics
    • Other docs as appropriate

Adding a New Built-in Function

  1. Implement the function in runtime/meowrt/builtins.go (or list.go for list operations):

    • Signature: func Name(args ...Value) Value
    • Use "Hiss! ... , nya~" for error messages
  2. Register in codegen — add a case in pkg/codegen/codegen.go:

    • In genCall(), map the Meow name to the Go function name
    • In genTypedCall() if needed for typed mode
  3. Add tests:

    • Unit tests for the runtime function
    • Integration tests (golden files)
  4. Update documentation:

    • docs/stdlib.md — add function documentation
    • docs/reference.md — add to built-in functions table

Adding a New Standard Library Package

  1. Create the package at runtime/<name>/:

    • All public functions have signature func Name(args ...meowrt.Value) meowrt.Value
    • Use "Hiss! ... , nya~" for errors
  2. Register in codegen — add entry to the stdPackages map in pkg/codegen/codegen.go:

    var stdPackages = map[string]string{
        "file":    "github.com/135yshr/meow/runtime/file",
        "http":    "github.com/135yshr/meow/runtime/http",
        "testing": "github.com/135yshr/meow/runtime/testing",
        "mypackage": "github.com/135yshr/meow/runtime/mypackage",  // new
    }
    
  3. The existing genMemberCall handles routing automatically — no other codegen changes needed

  4. Users will write:

    nab "mypackage"
    mypackage.my_func("arg")
    
  5. Add tests:

    • Unit tests in runtime/<name>/<name>_test.go
    • Integration test with nab
  6. Update documentation:

    • docs/stdlib.md — add package section
    • docs/reference.md — mention the package

Adding a Lint Rule

  1. Implement the rule in pkg/linter/linter.go

  2. Add tests for the new rule

  3. Update documentation — mention the rule in docs/effective-meow.md

Testing Conventions

Golden File Tests

Located in testdata/ with .nyan input and .golden expected output:

flowchart LR
    subgraph testdata["testdata/"]
        nyan["hello.nyan — Input program"]
        golden["hello.golden — Expected output"]
    end

Run golden tests:

go test ./compiler/

Update golden files when output changes:

go test ./compiler/ -update

Unit Tests

Standard Go _test.go files in each package. Follow Go testing conventions:

func TestSomething(t *testing.T) {
    // ...
}

HTTP Tests

Use httptest.NewServer for local testing (see runtime/http/http_test.go).

Panic Tests

Use defer func() { recover() }() to test Hiss! error paths:

func TestHissOnInvalidInput(t *testing.T) {
    defer func() {
        if r := recover(); r == nil {
            t.Error("expected panic")
        }
    }()
    SomeFunction(invalidInput)
}

Commit Style

We use gitmoji prefixes in commit messages:

PrefixUsage
✨ feat:New feature
🐛 fix:Bug fix
♻️ refactor:Code refactoring
📝 docs:Documentation
✅ test:Adding/updating tests
🎨 style:Code style/formatting
⬆️ chore:Dependencies/tooling
🚀 ci:CI/CD changes
🔧 config:Configuration changes

Commit messages are in English.

Example:

✨ feat: Add string interpolation support

PR Process

  1. Fork the repository
  2. Create a feature branch: git checkout -b feat/my-feature
  3. Make your changes with tests
  4. Ensure all tests pass: go test ./...
  5. Ensure code passes vet: go vet ./...
  6. Commit with gitmoji prefix
  7. Push and open a Pull Request

PR Checklist

  • Tests pass (go test ./...)
  • No vet warnings (go vet ./...)
  • Golden files updated if output changed (go test ./compiler/ -update)
  • Stringer regenerated if tokens changed (go generate ./...)
  • Documentation updated for new features
  • Commit messages use gitmoji prefix

Dependencies

Meow has zero runtime dependencies — standard library only. Development tools like stringer are allowed as build-time dependencies. Please do not introduce third-party runtime packages.