- Notifications
You must be signed in to change notification settings - Fork0
A modern language that compiles to JavaScript
License
shreeve/rip-lang
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
A modern language that compiles to JavaScript
Rip is a modern language inspired by CoffeeScript. It compiles toES2022 (classes,?.,??, modules), adds about adozen new operators, includesbuilt-in reactivity, and sports a self-hosting compiler withzero dependencies — all in about 11,000 lines of code.
No imports. No hooks. No dependency arrays. Just write code.
data= fetchUsers!# Dammit operator (call + await)user=User.newname:"Alice"# Ruby-style constructorsquares= (x* xfor xin [1..10])# List comprehensionstr=~/Hello, (\w+)/# Regex matchlog"Found:#{_[1]}"# Captures in _[1], _[2], etc.get'/users/:id'-># RESTful API endpoint, comma-lessname=read'name','string!'# Required stringage=read'age' , [0,105]# Simple numeric validation
What makes Rip different:
- Modern output — ES2022 with native classes,
?.,??, modules - New operators —
!,!?,//,%%,=~,|>,.new(), and more - Reactive operators —
:=,~=,~>as language syntax - Optional types —
::annotations,::=aliases,.d.tsemission - Zero dependencies — everything included, even the parser generator
- Self-hosting —
bun run parserrebuilds the compiler from source
bun add -g rip-lang# Install globallyrip# Interactive REPLrip file.rip# Run a filerip -c file.rip# Compile to JavaScript
defgreet(name)# Named function"Hello,#{name}!"add= (a,b)-> a+ b# Arrow functionhandler= (e)=>@process e# Fat arrow (preserves this)classDogextendsAnimalspeak:->log"#{@name} barks"dog=Dog.new("Buddy")# Ruby-style constructor
"Hello,#{name}!"# CoffeeScript-style"Hello, ${name}!"# JavaScript-style"#{a} +#{b} =#{a+ b}"# Expressions work in both
Both#{} and${} compile to JavaScript template literals. Use whichever you prefer.
{name,age}= person[first,...rest]= itemssquares= (x* xfor xin [1..10])# Array comprehensionconsole.log xfor xin items# Loop (no array)defloadUser(id)response=awaitfetch"/api/#{id}"awaitresponse.json()user?.profile?.name# Optional chainingdata= fetchData!# Await shorthand
for itemin [1,2,3]# Array iteration (for-in)console.log itemfor key, valueof object# Object iteration (for-of)console.log"#{key}:#{value}"forxas iterable# ES6 for-of on any iterableconsole.log xforxas! asyncIterable# Async iteration shorthandconsole.log x# Equivalent to: for await x as asyncIterableloop# Infinite loop (while true)process!loop5# Repeat N timesconsole.log"hi"
Arrow functions with no params that referenceit auto-inject it as the parameter:
users.filter->it.active# → users.filter(function(it) { ... })names=users.map->it.name# no need to name a throwaway variableorders.filter->it.total>100# works with any expression
State, computed values, and effects as language operators:
| Operator | Mnemonic | Example | What it does |
|---|---|---|---|
= | "gets value" | x = 5 | Regular assignment |
:= | "gets state" | count := 0 | Reactive state container |
~= | "always equals" | twice ~= count * 2 | Auto-updates on changes |
~> | "always calls" | ~> log count | Runs on dependency changes |
=! | "equals, dammit!" | MAX =! 100 | Readonly constant |
Type annotations are erased at compile time — zero runtime cost:
defgreet(name::string)::string# Typed function"Hello,#{name}!"User::= type# Structural typeid: numbername: stringenum HttpCode# Runtime enumok=200notFound=404
Compiles to.js (types erased) +.d.ts (types preserved) — full IDE support via TypeScript Language Server. Seedocs/RIP-TYPES.md.
| Operator | Example | What it does |
|---|---|---|
! (dammit) | fetchData! | Calls AND awaits |
! (void) | def process! | Suppresses implicit return |
!? (otherwise) | val !? 5 | Default only ifundefined |
? (existence) | x? | True ifx != null |
?: (ternary) | x > 0 ? 'yes' : 'no' | JS-style ternary expression |
if...else (postfix) | "yes" if cond else "no" | Python-style ternary expression |
?.?.[]?.() | a?.ba?.[0]a?.() | Optional chaining (ES6) |
?[]?() | a?[0]a?(x) | Optional chaining shorthand |
?? | a ?? b | Nullish coalescing |
... (spread) | [...items, last] | Prefix spread (ES6) |
// | 7 // 2 | Floor division |
%% | -1 %% 3 | True modulo |
=~ | str =~ /Hello, (\w+)/ | Match (captures in_) |
[//, n] | str[/Hello, (\w+)/, 1] | Extract capture n |
.new() | Dog.new() | Ruby-style constructor |
:: (prototype) | String::trim | String.prototype.trim |
[-n] (negative index) | arr[-1] | Last element via.at() |
* (string repeat) | "-" * 40 | String repeat via.repeat() |
<<= (chained) | 1 < x < 10 | Chained comparisons |
|> (pipe) | x |> fn orx |> fn(y) | Pipe operator (first-arg insertion) |
not in | x not in arr | Negated membership test |
not of | k not of obj | Negated key existence |
.= (method assign) | x .= trim() | x = x.trim() — compound method assignment |
* (merge assign) | *obj = {a: 1} | Object.assign(obj, {a: 1}) |
or return | x = get() or return err | Guard clause (Ruby-style) |
?? throw | x = get() ?? throw err | Nullish guard |
Heredoc — The closing''' or""" position defines the left margin. All content is dedented relative to the column where the closing delimiter sits:
html=''' <div> <p>Hello</p> </div>'''# Closing ''' at column 4 (same as content) — no leading whitespace# Result: "<div>\n <p>Hello</p>\n</div>"html=''' <div> <p>Hello</p> </div>'''# Closing ''' at column 2 — 2 spaces of leading whitespace preserved# Result: " <div>\n <p>Hello</p>\n </div>"
Raw heredoc — Append\ to the opening delimiter ('''\ or"""\) to prevent escape processing. Backslash sequences like\n,\t,\u stay literal:
script='''\ echo "hello\nworld" sed 's/\t/ /g' file.txt\'''#\n and\t stay as literal characters, not newline/tab
Heregex — Extended regex with comments and whitespace:
pattern=///^(\d{3})# area code -(\d{4})# number///
| Concept | React | Vue | Solid | Rip |
|---|---|---|---|---|
| State | useState() | ref() | createSignal() | x := 0 |
| Computed | useMemo() | computed() | createMemo() | x ~= y * 2 |
| Effect | useEffect() | watch() | createEffect() | ~> body |
Rip's reactivity is framework-agnostic — use it with React, Vue, Svelte, or vanilla JS.
Loadrip-ui.min.js (~53KB Brotli) — the Rip compiler and pre-compiled UI framework in one file. Components are.rip source files, compiled on demand, rendered with fine-grained reactivity. No build step. No bundler.
<scripttype="module"src="rip-ui.min.js"></script><scripttype="text/rip"data-name="index">exportHome=component @count :=0renderdiv.counterh1"Count: #{@count}"button @click:(-> @count++), "+"button @click:(-> @count--), "-"</script>
That's it. The runtime auto-detects inlinedata-name components, compiles them, and launches the app with hash routing — no bootstrap script needed. Two keywords (component andrender) are all the language adds. Everything else (:= state,~= computed, methods, lifecycle) is standard Rip.
See@rip-lang/ui for the full framework: file-based router, reactive stash, component store, and renderer.Try the demo — a complete app in one HTML file.
| Feature | CoffeeScript | Rip |
|---|---|---|
| Output | ES5 (var, prototypes) | ES2022 (classes,?.,??) |
| Reactivity | None | Built-in |
| Dependencies | Multiple | Zero |
| Self-hosting | No | Yes |
| Lexer | 3,558 LOC | 2,024 LOC |
| Compiler | 10,346 LOC | 3,293 LOC |
| Total | 17,760 LOC | ~11,300 LOC |
Smaller codebase, modern output, built-in reactivity.
Run Rip directly in the browser — inline scripts and the console REPL both supportawait via the! operator:
<scripttype="module"src="rip-ui.min.js"></script><scripttype="text/rip">res=fetch!'https://api.example.com/data'data=res.json!console.logdata</script>
Therip() function is available in the browser console:
rip("42 * 10 + 8")// → 428rip("(x * x for x in [1..5])")// → [1, 4, 9, 16, 25]awaitrip("res = fetch! 'https://api.example.com/todos/1'; res.json!")// → {id: 1, ...}
Try it live:shreeve.github.io/rip-lang
Source -> Lexer -> emitTypes -> Parser -> S-Expressions -> Codegen -> JavaScript (1,761) (types.js) (359) ["=", "x", 42] (3,293) + source mapSimple arrays (with.loc) instead of AST node classes. The compiler is self-hosting —bun run parser rebuilds from source.
| Component | File | Lines |
|---|---|---|
| Lexer + Rewriter | src/lexer.js | 1,761 |
| Compiler + Codegen | src/compiler.js | 3,303 |
| Type System | src/types.js | 1,099 |
| Component System | src/components.js | 1,877 |
| Source Maps | src/sourcemaps.js | 189 |
| Parser (generated) | src/parser.js | 357 |
| Grammar | src/grammar/grammar.rip | 944 |
| Parser Generator | src/grammar/solar.rip | 929 |
| REPL | src/repl.js | 601 |
| Browser Entry | src/browser.js | 167 |
| Tags | src/tags.js | 62 |
| Total | ~11,289 |
Rip includes optional packages for full-stack development:
| Package | Version | Purpose |
|---|---|---|
| rip-lang | 3.10.10 | Core language compiler |
| @rip-lang/api | 1.1.10 | HTTP framework (Sinatra-style routing, 37 validators) |
| @rip-lang/server | 1.1.19 | Multi-worker app server (hot reload, HTTPS, mDNS) |
| @rip-lang/db | 1.2.0 | DuckDB server with official UI + ActiveRecord-style client |
| @rip-lang/ui | 0.3.19 | Zero-build reactive web framework (auto-launch, stash, router) |
| @rip-lang/swarm | 1.1.4 | Parallel job runner with worker pool |
| @rip-lang/csv | 1.1.4 | CSV parser + writer |
| @rip-lang/schema | 0.2.1 | Unified schema → TypeScript types, SQL DDL, validation, ORM |
| VS Code Extension | 0.5.0 | Syntax highlighting, type intelligence, source maps |
bun add -g @rip-lang/db# Installs everything (rip-lang + api + db)Rip rescues what would be invalid syntax and gives it elegant meaning. When a literal value is followed directly by an arrow function, Rip inserts the comma for you:
# Clean route handlers (no comma needed!)get'/users'->User.all!get'/users/:id'->User.findparams.idpost'/users'->User.create body# Works with all literal typeshandle404-> {error:'Not found' }match/^\/api/-> {version:'v1' }checktrue->enable()
This works because'/users' -> was previously a syntax error — there's no valid interpretation. Rip detects this pattern and transforms it into'/users', ->, giving dead syntax a beautiful new life.
Supported literals: strings, numbers, regex, booleans, null, undefined, arrays, objects
rip# REPLrip file.rip# Runrip -c file.rip# Compilerip -t file.rip# Tokensrip -s file.rip# S-expressionsbun runtest# 1243 testsbun run parser# Rebuild parserbun run browser# Build browser bundle
| Guide | Description |
|---|---|
| docs/RIP-LANG.md | Full language reference (syntax, operators, reactivity, types, future ideas) |
| docs/RIP-INTERNALS.md | Compiler architecture (lexer, parser, codegen, S-expressions) |
| docs/RIP-TYPES.md | Type system specification |
| AGENT.md | AI agents — get up to speed for working on the compiler |
{"dependencies": {} }Everything included: compiler, parser generator, REPL, browser bundle, test framework.
Simplicity scales.
Simple IR (S-expressions), clear pipeline (lex -> parse -> generate), minimal code, comprehensive tests.
Inspired by: CoffeeScript, Lisp, Ruby |Powered by:Bun
MIT License
Start simple. Build incrementally. Ship elegantly.
About
A modern language that compiles to JavaScript
Resources
License
Contributing
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Contributors2
Uh oh!
There was an error while loading.Please reload this page.
