JSPM

rip-lang

3.4.4
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 204
  • Score
    100M100P100Q97086F
  • License MIT

A modern language that compiles to JavaScript

Package Exports

  • rip-lang
  • rip-lang/loader

Readme

Rip Logo

Rip

A modern language that compiles to JavaScript

Version Dependencies Tests License


Rip is a modern language inspired by CoffeeScript. It compiles to ES2022 (classes, ?., ??, modules), adds about a dozen new operators, includes built-in reactivity, and sports a self-hosting compiler with zero dependencies — all in about 10,300 lines of code.

No imports. No hooks. No dependency arrays. Just write code.

data = fetchUsers!                  # Dammit operator (call + await)
user = User.new name: "Alice"       # Ruby-style constructor
squares = (x * x for x in [1..10])  # List comprehension

str =~ /Hello, (\w+)/               # Regex match
log "Found: #{_[1]}"                # Captures in _[1], _[2], etc.

get '/users/:id' ->                 # RESTful API endpoint, comma-less
  name = read 'name', 'string!'     # Required string
  age  = 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.ts emission
  • Zero dependencies — everything included, even the parser generator
  • Self-hostingbun run parser rebuilds the compiler from source

Installation

bun add -g rip-lang            # Install globally
rip                            # Interactive REPL
rip file.rip                   # Run a file
rip -c file.rip                # Compile to JavaScript

Language

Functions & Classes

def greet(name)                # Named function
  "Hello, #{name}!"

add = (a, b) -> a + b          # Arrow function
handler = (e) => @process e    # Fat arrow (preserves this)

class Dog extends Animal
  speak: -> log "#{@name} barks"

dog = Dog.new("Buddy")         # Ruby-style constructor

String Interpolation

"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.

Destructuring & Comprehensions

{name, age} = person
[first, ...rest] = items

squares = (x * x for x in [1..10])   # Array comprehension
console.log x for x in items         # Loop (no array)

Async & Chaining

def loadUser(id)
  response = await fetch "/api/#{id}"
  await response.json()

user?.profile?.name            # Optional chaining
data = fetchData!              # Await shorthand

Iteration

for item in [1, 2, 3]         # Array iteration (for-in)
  console.log item

for key, value of object       # Object iteration (for-of)
  console.log "#{key}: #{value}"

for x as iterable              # ES6 for-of on any iterable
  console.log x

for x as! asyncIterable        # Async iteration shorthand
  console.log x                # Equivalent to: for await x as asyncIterable

Reactivity

State, computed values, and effects as language operators:

Operator Mnemonic Example What it does
= "gets value" x = 5 Regular assignment
:= "has state" count := 0 Reactive state container
~= "always equals" twice ~= count * 2 Auto-updates on changes
~> "reacts to" ~> log count Runs on dependency changes
=! "equals, dammit!" MAX =! 100 Readonly constant

Types (Optional)

Type annotations are erased at compile time — zero runtime cost:

def greet(name:: string):: string        # Typed function
  "Hello, #{name}!"

User ::= type                            # Structural type
  id: number
  name: string

enum HttpCode                            # Runtime enum
  ok = 200
  notFound = 404

Compiles to .js (types erased) + .d.ts (types preserved) — full IDE support via TypeScript Language Server. See docs/RIP-TYPES.md.


Operators

Operator Example What it does
! (dammit) fetchData! Calls AND awaits
! (void) def process! Suppresses implicit return
!? (otherwise) val !? 5 Default only if undefined
? (existence) x? True if x != null
?: (ternary) x > 0 ? 'yes' : 'no' JS-style ternary expression
?. ?.[] ?.() a?.b a?.[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
or return x = get() or return err Guard clause (Ruby-style)
?? throw x = get() ?? throw err Nullish guard

Heredoc & Heregex

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>"

Heregex — Extended regex with comments and whitespace:

pattern = ///
  ^(\d{3})    # area code
  -(\d{4})    # number
///

vs React / Vue / Solid

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.


Rip UI

Ship the 40KB Rip compiler to the browser. Components are .rip source files, compiled on demand, rendered with fine-grained reactivity. No build step. No bundler.

Counter = component
  @count := 0

  render
    div.counter
      h1 "Count: #{@count}"
      button @click: (-> @count++), "+"
      button @click: (-> @count--), "-"

Two keywords — component and render — are all the language adds. Everything else (:= state, ~= computed, methods, lifecycle) is standard Rip.

See @rip-lang/ui for the full framework: Virtual File System, file-based router, reactive stash, and component renderer.


vs CoffeeScript

Feature CoffeeScript Rip
Output ES5 (var, prototypes) ES2022 (classes, ?., ??)
Reactivity None Built-in
Dependencies Multiple Zero
Self-hosting No Yes
Lexer 3,558 LOC 1,867 LOC
Compiler 10,346 LOC 3,292 LOC
Total 17,760 LOC ~10,300 LOC

Smaller codebase, modern output, built-in reactivity.


Browser

Run Rip directly in the browser:

<script src="https://shreeve.github.io/rip-lang/docs/dist/rip.browser.min.js"></script>
<script type="text/rip">
  def greet(name)
    console.log "Hello, #{name}!"
  greet "World"
</script>

Try it live: shreeve.github.io/rip-lang


Architecture

Source  ->  Lexer  ->  emitTypes  ->  Parser  ->  S-Expressions  ->  Codegen  ->  JavaScript
           (1,867)    (types.js)     (357)       ["=", "x", 42]     (3,292)      + source map

Simple 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,867
Compiler + Codegen src/compiler.js 3,292
Type System src/types.js 719
Component System src/components.js 1,240
Source Maps src/sourcemaps.js 122
Parser (generated) src/parser.js 357
Grammar src/grammar/grammar.rip 935
Parser Generator src/grammar/solar.rip 916
REPL src/repl.js 707
Browser Entry src/browser.js 80
Tags src/tags.js 63
Total 10,298

The Rip Stack

Rip includes optional packages for full-stack development:

Package Purpose
@rip-lang/ui Zero-build reactive web framework (VFS, router, components)
@rip-lang/api HTTP framework (Sinatra-style routing, 37 validators)
@rip-lang/server Multi-worker app server (hot reload, HTTPS, mDNS)
@rip-lang/db DuckDB server with official UI (pure Bun FFI)
@rip-lang/swarm Parallel job runner with worker pool
@rip-lang/csv CSV parser + writer
@rip-lang/schema ORM + validation
VS Code Extension Syntax highlighting, type intelligence, source maps
bun add -g @rip-lang/db    # Installs everything (rip-lang + api + db)

Implicit Commas

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.find params.id
post '/users' -> User.create body

# Works with all literal types
handle 404 -> { error: 'Not found' }
match /^\/api/ -> { version: 'v1' }
check true -> 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


Quick Reference

rip                    # REPL
rip file.rip           # Run
rip -c file.rip        # Compile
rip -t file.rip        # Tokens
rip -s file.rip        # S-expressions
bun run test           # 1140 tests
bun run parser         # Rebuild parser
bun run browser        # Build browser bundle

Documentation

Guide Description
docs/RIP-LANG.md Language reference
docs/RIP-REACTIVITY.md Reactivity deep dive
docs/RIP-INTERNALS.md Compiler internals
packages/ Full-stack packages
CONTRIBUTING.md How to contribute

Zero Dependencies

{ "dependencies": {} }

Everything included: compiler, parser generator, REPL, browser bundle, test framework.


Philosophy

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.