npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

yaplang

v0.1.0

Published

YapLang — a beginner-friendly toy programming language whose keywords are Gen-Z slang. Real lexer → parser → tree-walking interpreter.

Readme

YapLang 🔥

A tiny, beginner-friendly programming language whose keywords are Gen-Z slang. Real lexer → parser → tree-walking interpreter. No regex hacks, no magic.

bestie fib(n) {
  fr (n < 2) { bet n }
  bet fib(n - 1) + fib(n - 2)
}

grind (vibe i = 0; i < 11; i = i + 1) {
  spill("fib(" + i + ") = " + fib(i))
}

YapLang (.yap) is dynamically typed and interpreted. It's small enough to read in an afternoon, so it's a nice way to see how a language actually works under the hood.


Why this exists

Most "learn how interpreters work" projects are either toy regex tricks (not real) or huge and intimidating. YapLang is a genuine pipeline — lexer, recursive descent parser, tree-walking evaluator with closures — kept deliberately tiny and friendly. The slang keywords are just a fun coat of paint over a normal little language.

Want to re-skin it? Every keyword spelling lives in one file (src/keywords.ts). Change the table (and LANGUAGE_NAME) and you've made your own language — nothing in the lexer/parser/interpreter hard-codes a word.


Install & run

Requires Node.js 18+.

# install the CLI globally from npm
npm i -g yaplang

Verify it's available anywhere (from any directory, in any shell):

yap --version    # -> 0.1.0
yap --help       # usage

Don't have npm publish access / want the latest from git? Install straight from the repo, or from a local clone:

npm i -g github:xOAviOx/yap-lang   # from GitHub

# …or from a clone (also the dev setup)
git clone https://github.com/xOAviOx/yap-lang.git
cd yap-lang
npm install
npm run build
npm install -g .                   # symlinks `yap`; after edits just re-run `npm run build`

Running your own .yap files

Create a file anywhere on disk — it doesn't need to live inside this repo:

# hello.yap (put it wherever you like)
spill("yo, I'm running YapLang 🔥")

Run it with the global command, from that file's folder or via a full path:

yap hello.yap                 # relative to your current directory
yap C:\Users\you\hello.yap    # or an absolute path (Windows)
yap /home/you/hello.yap       # or an absolute path (macOS/Linux)

The repo ships a mycode.yap scratchpad and an examples/ folder to copy from:

yap examples/fizzbuzz.yap
yap examples/fibonacci.yap
yap mycode.yap

No-install option: to run without a global install, use the dev runner from inside the repo: npm run dev -- path/to/file.yap.

From a clone (development)

npm install
npm run build        # bundles dist/ (CLI + lib) and the playground bundle
npm test             # vitest: lexer, parser, interpreter, examples
npm run dev -- examples/hello.yap   # run without building (via tsx)

The playground (zero install, runs in your browser)

▶ Try it live: https://xoaviox.github.io/yap-lang/ — no install, runs entirely in your browser.

Or serve it locally:

npm run playground   # builds, then serves playground/ at a local URL

Then open the printed URL. Paste code on the left, hit Run it 🔥, see output on the right. Everything runs client-side — there's no backend. (Press Ctrl/Cmd + Enter to run.)


Keywords

| Concept | YapLang keyword | | ------------------ | --------------- | | declare variable | vibe | | print to console | spill (built-in fn) | | boolean true | nocap | | boolean false | cap | | null / empty | ghosted | | if | fr | | else if | orfr | | else | nah | | while loop | onloop | | for loop | grind | | define function | bestie | | return | bet | | logical AND | vibin | | logical OR | lowkey | | logical NOT | sus | | break | dip | | continue | skrt |

Comments are // to end of line. Statements end with a newline or a ; (both fine). Blocks use { }.


Language tour

Variables + output

vibe lang = "YapLang"
vibe year = 2026
spill("yo, welcome to " + lang + " 🔥")
spill("it's giving " + year + " energy fr fr")   // number coerced to text

Strings use double quotes and support \n, \t, \", \\. + concatenates; a number next to a string is coerced to text.

If / else if / else

vibe score = 85
fr (score >= 90) {
  spill("you ate that")
} orfr (score >= 60) {
  spill("mid but passing")     // <- this one
} nah {
  spill("it's giving fail")
}

While loop (onloop) with break / continue

vibe i = 0
onloop (i < 10) {
  i = i + 1
  fr (i == 3) { skrt }   // continue
  fr (i == 7) { dip }    // break
  spill(i)               // 1 2 4 5 6
}

For loop (grind) — C-style

grind (vibe j = 0; j < 5; j = j + 1) {
  spill("rep " + j)
}

Functions (bestie), return (bet), recursion, closures

bestie add(a, b) {
  bet a + b
}

bestie fib(n) {
  fr (n < 2) { bet n }
  bet fib(n - 1) + fib(n - 2)
}

spill(add(2, 3))   // 5
spill(fib(10))     // 55

Functions are first-class values and close over their defining scope:

bestie makeCounter() {
  vibe n = 0
  bestie tick() { n = n + 1  bet n }
  bet tick
}
vibe c = makeCounter()
spill(c())  // 1
spill(c())  // 2

Arrays

vibe squad = ["zoomer", "boomer", "doomer"]
spill(squad[0])         // zoomer
squad[1] = "gamer"      // assignable by index
spill(howmany(squad))   // 3

Logical operators (short-circuit)

vibe a = nocap
vibe b = cap
fr (a vibin sus b) { spill("logic checks out") }

Truthiness (kept simple on purpose)

cap (false) and ghosted (null) are falsy. Everything else is truthy — including 0 and "". No surprises for beginners.


Built-in functions

| Function | What it does | | ----------------- | ----------------------------------------------------------------------- | | spill(...args) | print args joined by a space, then a newline. Returns ghosted. | | vibecheck(x) | the type as a string: "number", "string", "boolean", "ghosted", "array", "function". | | howmany(x) | length of a string or array. | | glowup(s) | uppercase a string. | | chill(s) | lowercase a string. | | slide(arr, x) | push x onto arr; returns the new length. | | yoink(arr) | pop and return the last element (ghosted if empty). | | numify(s) | parse a string to a number (errors if it isn't one). |


Errors that actually help

YapLang never dumps a raw stack trace at users. Every error is line-numbered and written in the language's own dialect:

💀 sus syntax at line 4: expected ')' after condition
💀 fr real? at line 9: 'squadd' is not defined
💀 that ain't it at line 2: can't divide by zero

The CLI exits non-zero on error; the playground prints the message in red.


How it works

YapLang is a classic three-stage interpreter pipeline. Source text flows through:

  1. Lexer (src/lexer.ts) — scans the raw string into a flat list of tokens (numbers, strings, identifiers, keywords, operators), tagging each with a line and column. Newlines are significant (they end statements) but are suppressed inside ( ) and [ ] so expressions can wrap.
  2. Parser (src/parser.ts) — a hand-written recursive descent parser turns tokens into an AST (src/ast.ts), following a strict precedence ladder: assignment → or → and → equality → comparison → term → factor → unary → call/index → primary.
  3. Interpreter (src/interpreter.ts) — a tree-walking evaluator walks the AST and executes it. Variable scopes live in src/environment.ts; functions capture their environment to form closures; return/break/ continue are implemented as thrown control-flow signals caught by the relevant node.

src/runner.ts glues the three together and exports run(source, output) — the same function the CLI (src/cli.ts) and the browser playground both call. The interpreter prints through an injectable output callback, which is why the playground can capture spill output instead of it going to a terminal.

source ──▶ Lexer ──▶ tokens ──▶ Parser ──▶ AST ──▶ Interpreter ──▶ output

Project layout

src/
  token.ts        Token type + TokenType enum
  keywords.ts     THE central slang keyword map (single source of truth)
  lexer.ts        source string -> tokens
  ast.ts          AST node definitions
  parser.ts       tokens -> AST (recursive descent)
  values.ts       runtime value model + helpers (stringify, truthiness, ...)
  environment.ts  variable scopes with a parent chain
  builtins.ts     the standard library (spill, howmany, ...)
  interpreter.ts  tree-walking evaluator
  errors.ts       YapError classes with line info
  runner.ts       run(source, output) — public entry point
  cli.ts          the `yap` command
playground/       single-page in-browser editor + runner
examples/         hello, fizzbuzz, fibonacci, guess_logic
tests/            vitest suites for lexer, parser, interpreter

Contributing: add a built-in or keyword

A new built-in function — edit src/builtins.ts. Add a BuiltinFunction inside createBuiltins():

new BuiltinFunction("clamp", 3, (_ctx, args) => {
  // args are already evaluated YapValues; throw BuiltinError on bad input.
  const [x, lo, hi] = args as [number, number, number];
  return Math.max(lo as number, Math.min(hi as number, x as number));
}),

It's instantly available in every program (and the playground) as clamp(...).

A new / renamed keyword — edit src/keywords.ts only. Add or change an entry in KEYWORDS mapping a spelling to a TokenType. Because the rest of the compiler speaks in concept-level TokenTypes, you don't touch the lexer, parser, or interpreter. To introduce a brand-new concept (say, a switch), you'd add a TokenType, a keyword entry, an AST node, a parser rule, and an interpreter case — in that order. Run npm test and add a case to the relevant suite.


License

MIT