# EX Script

A modern, safer, faster-to-develop alternative to TypeScript — compiled to
readable JavaScript.

EX is designed to eliminate the failure classes that consume production time:
unhandled errors, null dereferences, lost context, and silent type drift. It
keeps TypeScript's best ideas (structural types, generics, inference) and
replaces the rest with a small, regular language you can learn in an afternoon.

```xan
import std.io

schema User {
    name: String
    age: Int
}

export fun main(args: List<String>) -> Int {
    let name = args.first() or "world"
    io.println("hello, {name}")
    io.println("countdown: {(1..=5).toList().reverse()}")
    return 0
}
```

```console
$ ex run src/main.xan
hello, world
countdown: [5, 4, 3, 2, 1]
```

## Why not TypeScript?

- **Errors are values.** Functions declare fallibility with `-> T!`; you must
  handle the failure at every boundary (`?`, `try/catch`, `or raise`, `match`).
  No unhandled-promise debugging, no `throw` down the stack.
- **Optionals are explicit.** `T?` values can be `undefined`; the compiler
  forces you to handle it (`or`, `if let`, `?.`) — no accidental `null`.
- **No `null`, no `any`-leaks.** `Any` exists but must be narrowed before it
  flows into typed positions; `schema` validates untrusted data at the
  boundary.
- **Immutability by default.** `let` is immutable; `let mut` is explicit.
- **One statement per line.** Newlines are syntax — the language is scannable
  and the formatter is deterministic.
- **Designed to be a language, not a superset.** Regular syntax, exhaustive
  matches, `test` blocks built in, and a compiler that explains its
  diagnostics: what, why, and how to fix.

## Status

Working prototype (v0.1.0). The language core is implemented and verified
end-to-end: lexer, parser, type checker, code generation, module system,
standard library, and CLI. See [docs/13-roadmap.md](docs/13-roadmap.md) for
exactly what is DONE vs designed-but-unbuilt — nothing is marked done until it
runs.

## Install & build

Requires Node.js >= 20.

```console
$ npm install
$ npm run build          # tsc -> dist/
$ npm link               # optional: expose the `ex` command
```

## Usage

```console
$ ex run [file] [args...]   # compile and run (file, directory, or project.xan entry)
$ ex test [file]            # run test blocks
$ ex build [file] -o out.mjs
$ ex check [file]           # type-check and report diagnostics
$ ex help
```

- Without a file, `ex run` uses `entry` from `project.xan`, else `src/main.xan`.
- `ex run examples/hello` runs the example project (entry `main.xan`).
- Test blocks auto-import `std.test`:

```xan
import std.io

test "greet works" {
    expect(greet("bob")).eq("dear bob")
}
```

## Examples

- [`examples/hello/`](examples/hello/) — multi-module project: local imports,
  `project.xan` manifest, stdlib, tests, CLI arguments.

## Standard library (native)

`std.io`, `std.string`, `std.collections`, `std.math`, `std.test` — plus the
built-in method tables on `String`, `List`, `Map`, `Set`, `Bytes`, ranges
(`.toList()`, `.map()`, ...), and `schema` (`parse`, `from`).

## Repository layout

```
src/compiler/ast.ts        AST definitions + spans
src/compiler/lexer.ts      tokenizer (newline-aware)
src/compiler/parser.ts     recursive-descent parser
src/compiler/checker.ts    type checker + diagnostics
src/compiler/builtins.ts   stdlib natives + method tables
src/compiler/codegen.ts    JS code generation + runtime emission
src/compiler/runtime.ts    `$ex` runtime header (results, optionals, schemas, tests)
src/compiler/modules.ts    module graph loader
src/cli/index.ts           `ex` CLI
docs/                      the design docs — the spec
```

## Documentation

The design docs are the specification; the implementation is held to them.

| Doc | Topic |
| --- | --- |
| [01-philosophy.md](docs/01-philosophy.md) | Why EX exists |
| [02-problems-with-typescript.md](docs/02-problems-with-typescript.md) | The failure classes EX removes |
| [03-syntax.md](docs/03-syntax.md) | Syntax reference |
| [04-type-system.md](docs/04-type-system.md) | Types, inference, generics, narrowing |
| [05-error-handling.md](docs/05-error-handling.md) | Results, `raise`, `try/catch` |
| [06-runtime-model.md](docs/06-runtime-model.md) | How EX compiles to JS |
| [07-compiler-architecture.md](docs/07-compiler-architecture.md) | Compiler internals |
| [08-toolchain.md](docs/08-toolchain.md) | CLI, formatter, linter design |
| [09-package-ecosystem.md](docs/09-package-ecosystem.md) | Packages, manifest, registry |
| [10-migration.md](docs/10-migration.md) | TS → EX migration |
| [11-examples.md](docs/11-examples.md) | Example programs |
| [12-comparison.md](docs/12-comparison.md) | EX vs TS vs Rust vs Go |
| [13-roadmap.md](docs/13-roadmap.md) | What is done, what is next |

## Development

```console
$ npm run build          # type-check + emit dist
$ npx tsc --noEmit       # type-check only
$ npm test               # unit tests
$ npm run smoke          # end-to-end: ex run examples/hello
$ npx tsx src/tests/smoke1.ts   # checker smoke (expects no diagnostics)
$ npx tsx src/tests/smoke2.ts   # end-to-end bundle output test
```

## License

MIT — see [LICENSE](LICENSE).