This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Implish is a tiny, ultraportable programming language designed to be embedded across different tech stacks. It combines an imperative core (inspired by Eric Hehner's "A Practical Theory of Programming") with functional, array-centric features borrowed from APL/K/J/Nial.
Key principles:
- Programs are specifications of behavior that result in changes to named variables
- Code-as-data: simple token tree syntax that can be extended via domain-specific languages
- Portable across platforms (currently TypeScript/Node.js implementation)
# Compile TypeScript to JavaScript (outputs to dist/)
npm run build
# Watch mode for continuous compilation
npm run watch
# Run the Node.js REPL
node dist/imp-cmd.mjs
# Run the REPL in quiet mode (no prompt, useful for testing)
node dist/imp-cmd.mjs -q
# or
node dist/imp-cmd.mjs --quiet
# Start the browser development server (with hot reload)
npm run dev
# Preview the built site
npm run previewNo formal test suite yet. Use imp-tests.org for manual testing examples.
The REPL supports a -q or --quiet flag that suppresses the prompt, making it easier to pipe test cases and compare outputs.
imp-core.mts: Type system and data structures
ImpVal: Discriminated union for all implish values (INT, NUM, STR, SYM, LST, etc.)ImpT: Enum of value types (TOP, INT, NUM, STR, SYM, LST, SEP, JSF, JDY, NIL, etc.)SymT: Enum of symbol variants (RAW, SET, GET, LIT, FILE, URL, etc.)ImpP: Parts of speech (V=verb, N=noun, O=operator, S=setter, etc.)JSF: JavaScript function wrapper (with arity)JDY: JavaScript dyad (infix operator)SymTable: Symbol interning tableTreeBuilder: Generic tree construction utility
imp-load.mts: Lexer/parser ("code-as-data" loader)
ImpLoader: Converts strings to token treeslexerTable: Regex-based token matching (numbers, strings, symbols, delimiters)load(): Main entry point to parse implish code- Handles nested structures with
[,(,{, and.:...:.comments - Symbol prefixes:
%file,:get,foo:set,'lit,`quote,.msg,!msg2,@ann,#ish,/refn,?err,,unq
imp-eval.mts: Evaluator/interpreter
ImpEvaluator: Stack-based evaluator using "parts of speech" for parsingimpWords: Global dictionary of built-in functions- Handles assignment (
x: 123), function calls, infix operators, strands (juxtaposed values) - Async evaluation (supports Promise-returning functions)
nextItem(): Advances through token stream, resolving symbolscollectStrand(): Handles numeric/symbol vectors (e.g.,1 2 3→ INTs vector)modifyNoun(): Applies infix operators (dyads) to nounsmodifyVerb(): Handles verb composition and adverbsquasiquote(): Evaluates backtick-quoted expressions with unquote (,x)
imp-show.mts: Serializer
ImpWriter.show(): Converts ImpVal back to implish source codeimpShow(): Public API for showing values
imp-cmd.mts: REPL (Read-Eval-Print Loop)
- Interactive shell with readline support
- Tab completion for file paths (
%path) and word names - History persistence in
~/.imp-history - Handles multi-line input (unclosed delimiters)
lib-file.mts: File path utilities
toNativePath(): Converts implish paths (%/d/path) to OS paths (d:/pathon Windows)toImplishPath(): Reverse conversionparsePartialPath(): For tab completion- Windows drive handling:
%/lists drives,%/c/is C:/, etc.
Arithmetic (element-wise for vectors):
+,-,*,%(integer division)
Functions:
!: Range/iota (e.g.,! 5→0 1 2 3 4)rd: Read file/URL (async)wr: Write file (async)e?: File exists checkrm: Remove fileload: Parse implish code from string or fileeval: Evaluate JavaScript expression (dangerous, use cautiously)echo: Print value to consoleshow: Convert value to string representationxmls: Convert value to XMLlook: Show value of a wordpart: Get part of speech for a valuetype?: Get type of a value
Adverbs (higher-order functions):
each[f; x]: Apply functionfto each element ofx- Example:
each[{* 2 x}; 5 7 2]→10 14 4 - Works on atoms, vectors (INTs, NUMs), and lists
- Preserves the structure of the input (INTs → INTs, lists → lists)
- Example:
each2[f; x; y]: Apply dyadic functionfpairwise to elements ofxandy- Example:
each2[+; 2 7 9; 1 3 4]→[3, 10, 13] - Atom spreading: if
xoryis an atom, it's used for all pairs - Example:
each2[+; 5; 1 3 4]→[6, 8, 9]
- Example:
To add a new built-in function to implish:
- Edit imp-defs.mts where
createImpWords()function is defined (around line 328) - Add your word to the
impWordsobject using this pattern:'wordname': imp.jsf((arg1, arg2, ...) => { // Your implementation here // Return an ImpVal (use ImpC.int(), ImpC.str(), ImpC.sym(), etc.) }, arity),
- Choose the correct arity (number of arguments):
- 0 for nullary functions (no arguments)
- 1 for unary functions (one argument)
- 2 for binary functions (two arguments)
- Return values using constructors from ImpC:
ImpC.int(n)for integersImpC.num(n)for floatsImpC.str(s)for stringsImpC.sym(Symbol('name'), SymT.RAW)for symbolsImpC.ints([...])for integer vectorsImpC.nums([...])for float vectorsImpC.syms([...])for symbol vectorsimp.lst(type, [...])for listsNILfor nil/void
- Rebuild: Run
npm run build - Test: The MCP server will automatically load the fresh code on next eval
Example:
'double': imp.jsf(x => {
if (x[0] === ImpT.INT) {
return ImpC.int((x[2] as number) * 2)
}
throw "double expects an integer"
}, 1),Strands: Juxtaposed values form vectors
x: 1 2 3 .: creates INTs vector :.
y: `foo `bar .: creates SYMs vector :.
Assignment chains: Right-associative
a: b: 123 .: sets both a and b to 123 :.
Projection syntax: Partial/total application
f[1 2 3] .: calls f with args [1, 2, 3] :.
f[a; b] .: calls f with args a and b (semicolon separates) :.
Symbol variants affect evaluation:
foo→ look up word, evaluatefoo:→ assignment target (set-word):foo→ get value of word without further evaluation (get-word)'foo→ literal symbol (lit-word)`foo→ quoted symbol (backtick)%foo/bar→ file pathhttp://...→ URL (auto-detected)
Quasiquotation: Backtick quotes, comma unquotes
x: 42
`[1 + ,x] .: evaluates to [1 + 42] :.
The browser version of implish is served at implish.org.
- Source:
web/directory (entry point:web/index.html) - Build output:
dist-web/directory - Build tool: Vite (configured in
vite.config.js)
# Build the web version (outputs to dist-web/)
npx vite build
# Dev server with hot reload
npm run dev
# Preview the built site locally
npm run previewThe Vite build automatically runs npm run build (TypeScript compilation) first via the ensureCoreBuild plugin, then bundles everything for the browser.
Deployment: dist-web/ is the live site served at implish.org. After running npx vite build, deploy the contents of dist-web/ to the server.
- Uses ES2022 modules (
.mtsextension) - Strict TypeScript with full type checking
- All source files in root directory
- Output goes to
dist/directory (Node.js),dist-web/(browser) - Discriminated unions for type safety (check
x[0]for ImpT) - Async/await throughout evaluation pipeline
- imp-load.mts: Nested `.: :. comments, unterminated strings, floats
- imp-eval.mts: Adverbs, prepositions, conjunctions (modifyVerb)
- docs/parse.md: PEG-style parse library not yet implemented
Current work is tracked in plan.org - always check this file first to understand:
- Current refactoring efforts
- Architecture decisions
- Test cases and expected behaviors
- Implementation strategy
- The evaluator uses a "parts of speech" approach (verbs, nouns, operators, etc.) rather than traditional precedence climbing
- Symbol resolution happens during evaluation, not parsing (late binding)
- The loader is deliberately simple: it only recognizes basic token types, leaving semantic interpretation to evaluators
- File paths use a unified syntax (
%/d/pathfor Windows,%/pathfor Unix) to avoid platform-specific quoting issues - always use the mcp server to execute implish code.
- ALWAYS use the mcp server to run implish!
- the implish mcp server reloads implish on each version. you don't have to worry about old versions being cached.
- the implish mcp server maintains state between calls, but reloads and clears state when you change one of its source files.
- you can use the reset_implish mp tool to reset the interpreter state without making a change