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

@nicktomlin/tak

v0.1.0

Published

tak — a stack-based language for the browser and command line

Downloads

28

Readme

tak

tak is a small stack-based language that runs in the browser. Inspired by Factor's quotation model, PostScript's dictionary flavor, and a desire for maximum debuggability. The name means "thank you" in Danish — a tribute to the language hackers who came before.


A Taste of tak

In tak, values are pushed onto a stack and words consume and produce them. A program is a sequence of values and words — there are no variables, only the stack.

// Push two numbers and add them
3 4 +           // stack: [ 7 ]
dup *           // stack: [ 49 ]  (square it)

// Functions are defined with a stack effect annotation
fn factorial ( n -- n ) {
  dup 1 > [ dup 1 - factorial * ] [ drop 1 ] if
}

5 factorial .   // prints 120

// Quotations are first-class — deferred sequences of words
[ 1 2 3 4 5 ] [ dup * ] map .   // prints [ 1 4 9 16 25 ]

// Fetch data, parse it, work with the result
"https://api.example.com/data" fetch await response/json
"results" get 0 nth "name" get .

Embed it in any page — no install required:

<!-- via CDN -->
<script type="module" src="https://esm.sh/@nicktomlin/tak"></script>
<script type="text/tak">
  fn greet ( str -- ) { "Hello, " swap concat . }
  "World" greet
</script>

Quick Start

Load tak from a CDN — no build step, no install:

<!-- ESM via esm.sh -->
<script type="module" src="https://esm.sh/@nicktomlin/tak"></script>

<!-- or classic script via unpkg -->
<script src="https://unpkg.com/@nicktomlin/tak/dist/tak.js"></script>

Then write tak inline:

<script type="text/tak">
  fn greet ( str -- ) { "Hello, " swap concat . }
  "World" greet
</script>

Enable the live debug panel (floating stack overlay):

<script type="module" src="https://esm.sh/@nicktomlin/tak" data-debug="true"></script>

Syntax

Literals

42          // integer
3.14        // float
"hello"     // string (supports \n \t \r \" \\)
true false  // booleans
null

Function definitions (stack effects required)

fn square ( n -- n ) { dup * }
fn greet  ( str -- ) { "Hello, " swap concat . }

Quotations / arrays

[ 1 2 3 ]         // array of numbers (pushed as a value)
[ 1 + ]           // callable quotation
[ "hello" . ]     // quotation pushed as a value, called with 'call'

Dict literals

{ name: "Alice", age: 30 }

Control flow (combinators)

5 0 > [ "positive" . ] [ "non-positive" . ] if
10 [ dup 0 > ] [ 1 - ] while
5 [ "tick" . ] times
true [ "yes" . ] when

JavaScript escape hatch

@js(document.querySelector('#app'))
@js(Math.random())

Comments

// line comment

Standard Library

Stack

| Word | Effect | Description | |------|--------|-------------| | dup | ( a -- a a ) | Duplicate top | | drop | ( a -- ) | Discard top | | swap | ( a b -- b a ) | Swap top two | | over | ( a b -- a b a ) | Copy second | | rot | ( a b c -- b c a ) | Rotate top three | | nip | ( a b -- b ) | Drop second | | tuck | ( a b -- b a b ) | Copy top under second |

Arithmetic

+ - * / % ** neg

Comparison → boolean

= != < > <= >=

Logic

and or not

String

| Word | Effect | |------|--------| | concat | ( str str -- str ) | | length | ( str\|arr -- n ) | | substr | ( str start end -- str ) | | split | ( str sep -- arr ) |

Array / Quotation

| Word | Effect | |------|--------| | push | ( arr val -- arr ) | | pop | ( arr -- arr val ) | | nth | ( arr n -- val ) | | map | ( arr quot -- arr ) | | filter | ( arr quot -- arr ) | | reduce | ( arr init quot -- val ) | | each | ( arr quot -- ) | | call | ( quot -- ) |

Dict

| Word | Effect | |------|--------| | get | ( dict key -- val ) | | set | ( dict key val -- dict ) | | has | ( dict key -- bool ) | | keys | ( dict -- arr ) |

Control

| Word | Effect | |------|--------| | if | ( bool true-q false-q -- ) | | when | ( bool quot -- ) | | unless | ( bool quot -- ) | | while | ( cond-q body-q -- ) | | times | ( n quot -- ) |

I/O

  • . / log — print top of stack to console
  • str( val -- str ) — convert any value to its string representation

Type

  • type — push type name as string: "number", "string", "bool", "array", "dict", "quot", "null", "element"
  • number? string? bool? array? dict? null? quot?

Async / Fetch

| Word | Effect | Description | |------|--------|-------------| | fetch | ( url -- Promise ) | HTTP GET | | fetch-post | ( url body -- Promise ) | HTTP POST; body can be string or dict | | await | ( Promise -- val ) | Resolve a promise | | response/json | ( Response -- val ) | Read response body as JSON → tak value | | response/text | ( Response -- str ) | Read response body as plain text | | json/parse | ( str -- val ) | Parse a JSON string | | json/str | ( val -- str ) | Serialize a tak value to JSON |

"https://example.com/api" fetch await response/json   // → tak dict/array
"https://example.com/api" fetch await response/text   // → string
"https://example.com/api" { x: 1 } fetch-post await response/json
'{"x":1}' json/parse
{ x: 1 } json/str

JS Interop

| Word | Effect | Description | |------|--------|-------------| | import | ( url -- Promise ) | Dynamic JS module import (async; use await) | | js/get | ( js key -- val ) | Read a property from a JS object | | js/call | ( js arg1..argN n -- result ) | Call a JS function with N args | | js/unwrap | ( js -- val ) | Convert a JS value to a tak value |

"https://cdn.example.com/lib.js" import await   // ( -- js-module )
"Chart" js/get                                   // ( js-module -- Chart )
0 js/call                                        // call with 0 args

Static Imports (use)

use is a hoisted import — all URLs are fetched concurrently before the program runs, with results available synchronously at the call site.

"./lib.js" use                           ( -- module )
"./lib.js" [ Chart ] use                 // defines word `Chart`
"./lib.js" [ renderFn as render ] use    // defines word `render`
"./lib.js" [ Chart renderFn as render ] use

The URL must be a string literal (known at parse time). Without a binding list, the whole module is pushed onto the stack. With bindings, each named export becomes a word that pushes its value.

Debug

  • trace — log top value without consuming it
  • debug — fire debug event + log full stack

DOM

| Word | Effect | |------|--------| | dom/query | ( selector -- el\|null ) | | dom/query-all | ( selector -- arr ) | | dom/create | ( tag -- el ) | | dom/append | ( parent child -- parent ) | | dom/text | ( el str -- el ) | | dom/html | ( el str -- el ) | | dom/on | ( el event quot -- el ) | | dom/attr-get | ( el attr -- val ) | | dom/attr-set | ( el attr val -- el ) | | dom/class-add | ( el class -- el ) | | dom/class-rm | ( el class -- el ) | | dom/remove | ( el -- ) |

dom/on pushes the element onto the stack before calling the quotation.


Debug Panel API

const interp = new Tak.TakInterpreter({ debug: true });

interp.on('beforeWord', ({ word, stack }) => { });
interp.on('afterWord',  ({ word, stack }) => { });
interp.on('push',       ({ value, stack }) => { });
interp.on('pop',        ({ value, stack }) => { });
interp.on('error',      ({ error, word, stack }) => { });
interp.on('scriptEnd',  ({ stack }) => { });

interp.stack   // read-only snapshot
interp.dict    // Map of all defined words

Ctrl+D toggles the debug overlay when data-debug="true" is set.


Programmatic Use

import { TakInterpreter, registerStdlib, registerDomStdlib } from './dist/tak.js';
import { tokenize } from './dist/tak.js';
import { parse } from './dist/tak.js';

const interp = new TakInterpreter({ debug: true });
registerStdlib(interp);
registerDomStdlib(interp);

const tokens = tokenize('5 3 + .');
const program = parse(tokens);
await interp.run(program);

Project Structure

src/
  lexer.ts          — tokenizer
  parser.ts         — recursive-descent parser
  ast.ts            — AST node types
  interpreter.ts    — tree-walking interpreter + event system
  stdlib.ts         — built-in words
  dom-stdlib.ts     — DOM words
  debug-panel.ts    — floating stack overlay
  browser-runtime.ts — script tag scanner + bootstrap
examples/
  hello.html        — stack ops, fn defs, combinators
  counter.html      — DOM event handling
  fetch.html        — async fetch with debug panel

Build

npm install
npm run build        # production bundle → dist/tak.js
npm run build:dev    # with source maps
npm run watch        # watch mode
npm run typecheck    # tsc type check only

License

MIT