• Stars
    star
    557
  • Rank 79,968 (Top 2 %)
  • Language
    Go
  • License
    MIT License
  • Created over 5 years ago
  • Updated over 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Ink is a minimal programming language inspired by modern JavaScript and Go, with functional style.
Ink programming language logo

Ink programming language

GoDoc Build Status

Ink is a minimal programming language inspired by modern JavaScript and Go, with functional style. Ink can be embedded in Go applications with a simple interpreter API. Ink is used to write my current personal productivity suite, Polyx, as well as my day-to-day scripts and other small programs. Ink's documentation is on the Ink website.


Ink has a few goals. In order, they are

  • Ink should have a simple, minimal syntax and feature set
  • Ink should be quickly readable and clear in expression
  • Ink should have a well designed, fully featured, and modular standard library
  • Ink should have an ergonomic interpreter and runtime API

Design is always a game of tradeoffs. Ink's goals for minimalism and readability / expressiveness means the language deliberately does not aim to be best in other ways:

  • Ink doesn't need to be highly efficient or fast, especially compared to compiled languages
    • However, within the constraints of the interpreter design, I try not to leave performance on the table, both in execution speed and in memory footprint. Efficiently composed Ink programs are between 2-4x slower than equivalent Python programs, in my experience. Small programs can run on as little as 3MB of memory, while the interpreter can stably scale up to gigabytes of memory for data-heavy tasks.
  • Ink doesn't need to be particularly concise, though we try to avoid verbosity when we can
  • Ink doesn't value platform portability as much as some other languages in this realm, like Lua or JavaScript -- not running on every piece of hardware available is okay, as long as it runs on most of the popular platforms

The rest of this README is a light introduction to the Ink language and documentation about the project and its interpreter, written in Go. For more information and formal specification about the Ink language itself, please see SPEC.md.

Introduction

Here's an implementation of FizzBuzz in Ink.

` ink fizzbuzz implementation `

std := load('std')

log := std.log
range := std.range
each := std.each

fizzbuzz := n => each(
	range(1, n + 1, 1)
	n => [n % 3, n % 5] :: {
		[0, 0] -> log('FizzBuzz')
		[0, _] -> log('Fizz')
		[_, 0] -> log('Buzz')
		_ -> log(n)
	}
)

fizzbuzz(100)

Here's a simple Hello World HTTP server program.

std := load('std')

log := std.log

listen('0.0.0.0:8080', evt => (
	evt.type :: {
		'error' -> log('Error: ' + evt.message)
		'req' -> (evt.end)({
			status: 200
			headers: {'Content-Type': 'text/plain'}
			body: 'Hello, World!'
		})
	}
))

If you're looking for more realistic and complex examples, check out...

You'll notice a few characteristics about Ink:

  • Functions are defined using arrows (=>) a la JavaScript arrow functions
  • Ink does not have a looping primitive (no for or while), and instead defaults to tail-optimized recursion. Loops may be possible to have in syntax with macros in the near future.
  • Rather than using if/else, Ink uses pattern matching using the match (::) operator. Match expressions in Ink allows for very expressive definition of complex flow control.
  • Ink does not have explicit return statements. Instead, everything is an expression that evaluates to a value, and function bodies are a list of expressions whose last-evaluated expression value becomes the "return value" of the function.
  • As a general principle, Ink tries not to use English keywords in favor of a small set of short symbols.

You can find more sample code in the samples/ directory and run them with ink samples/<file>.ink.

Getting started

You can run Ink in three main ways:

  1. The Ink binary ink defaults to executing whatever comes through standard input, if there is any, or else starts a repl. So you can pipe any Ink script (say, main.ink) to the binary to execute it.
$ cat main.ink | ink
	# or
$ ink < main.ink
  1. Use ink main.ink to execute an Ink script file.
  2. Invoke ink without flags (or with the optional -repl flag) to start an interactive repl session, and start typing Ink code. You can run files in this context by loading Ink files into the context using the load builtin function, like load('main'). (Note that we remove the .ink file extension when we call load.)

Additionally, you can also invoke an Ink script with a shebang. Mark the first line of your Ink program file with this directive, which tells the operating system to run the program file with ink, which will then accept this file and run it for you when you execute the file.

#!/usr/bin/env ink

`` ... the rest of your program

You can find an example of this in samples/fileserver.ink, which you can start by simply running ./samples/fileserver.ink (without having to specifically call ink samples/fileserver.ink).

To summarize, ink's input priority is, from highest to lowest, -repl -> -eval -> files -> stdin. Note that command line flags to ink should precede any program files given as arguments. If you need to pass a file name that begins with a dash, use --.

Why?

I started the Ink project to become more familiar with how interpreters work, and to try my hand at designing a language that fit my preferences for the balance between elegance, simplicity, practicality, and expressiveness. The first part -- to learn about programming languages and interpreters -- is straightforward, so I want to expand on the second part.

My language of choice at work is currently JavaScript. JavaScript is expressive, very fast (for a dynamic language), and has an approach to concurrency that I really like, using a combination of closures with event loops and message passing to communicate between separate threads of execution. But JavaScript has grown increasingly large in its size and complexity, and also carries a lot of old cruft for sake of backwards compatibility. I've also been increasingly interested in composing programs from functional components, and there are features in the functional PL world that haven't yet made their way into JavaScript like expressive pattern matching and guaranteed tail recursion optimizations (the former has been in TC39 limbo for several years, and the latter is only supported by recent versions of WebKit/JavaScriptCore).

So Ink as a language is my attempt to build a language in the functional paradigm that doesn't sacrifice the concurrency benefits or expressiveness of JavaScript, while being minimal and self-consistent in syntax and semantics. I sometimes think about Ink as what JavaScript would be if it were rewritten by a Lisp programmer. Given this motivation, Ink tries to be a small language with little noise in the syntax, few special tokens, and a few essential builtins, that becomes expressive and powerful by being extremely composable and extensible. While modern dynamic languages routinely have over 100 syntactic forms, Ink has just 10 syntactic forms, from which everything else is derived. Ink deliberately avoids adding features into the language for sake of building a feature-rich language; whenever something can be achieved idiomatically within the constraints and patterns of the existing language or core libraries, that's preferred over adding new features into the language itself. This is how Ink remains tiny and self-consistent.

I'm also very interested in Elixir's approach towards language development, where there is a finite set of features planned to be added to the language itself, and the language is designed to become "complete" at some point in its lifetime, after which further growth happens through extending the language with macros and the ecosystem. Since simplicity and minimalism is a core goal of Ink, this perspective really appeals to me, and you can expect Ink to become "complete" at some finite point in the future. In fact, the feature set documented in this repository today is probably 85-90% of the total language features Ink will get eventually.

Isolation and permissions model

Ink has a very small surface area to interface with the rest of the interpreter and runtime, which is through the list of builtin functions defined in runtime.go. In an effort to make it safe and easy to run potentially untrusted scripts, the Ink interpreter provides a few flags that determine whether the running Ink program may interface with the operating system in certain ways. Rather than simply fail or error on any restricted interface calls, the runtime will silently ignore the requested action and potentially return empty but valid data.

  • -no-read: When enabled, the builtin read() function will simply return an empty read, as if the file being read was of size 0. -no-read also blocks directory traversals.
  • -no-write: When enabled, the builtins write(), delete(), and make() will pretend to have written the requested data or finished the requested filesystem operations safely, but cause no change.
  • -no-net: When enabled, the builtin listen() function will pretend to have bound to a local network socket, but will not actually bind. The builtin req() will also pretend to have sent a valid request, but will do nothing.

To run an Ink program completely untrusted, run ink -isolate (with the "isolate" flag), which will revoke all revokable permissions from the running script.

Build scripts and Make

Ink uses GNU Make to manage build and development processes:

  • make test runs the full test suite, including filesystem and syntax/parser tests
  • make run runs the extra set of tests, which are at the moment just the full suite of samples in the repository
  • make build-(platform) builds the Ink interpreter for a given operating system target. For example, make build-linux will build Ink for Linux to ink-linux.
  • make build by itself builds all release targets. We currently build for 4 OS targets: Windows, macOS, Linux, and OpenBSD
  • make install installs Ink to your system
  • make precommit will perform any pre-commit checks for commiting changes to the development tree. Currently it lints and formats the Go code.
  • make clean cleans any files that may have been generated by running make scripts or sample Ink programs

Go API

As the baseline interpreter is currently written in Go, if you want to embed Ink within your own application, you can use the Go APIs from this package to do so.

The APIs are still in flux, but you can check out main.go and eval.go for the Go channels-based concurrent lexer/parser/evaler APIs. As the APIs are finalized, I'll put more information here directly.

For now, here's a minimal example of creating an execution context for Ink and running some Ink code from standard input, and from a file as an io.Reader. (In fact, this is very nearly the implementation of executing from stdin in the interpreter.)

package main

import (
	"os"

	"github.com/thesephist/ink/pkg/ink"
)

func main() {
	// Create an "Engine", which is a global execution context for the lifetime of an Ink program.
	eng := ink.Engine{}
	// Create a "Context", which is a temporary execution context for a given source of input.
	ctx := eng.CreateContext()

	// Execute code from an io.Reader
	ctx.Exec(os.Stdin)
	// Wait until all concurrent callbacks finish from the program before exiting
	eng.Listeners.Wait()
}

To run from a file, use os.File as an io.Reader.

package main

import (
	"log"
	"os"

	"github.com/thesephist/ink/pkg/ink"
)

func main() {
	eng := ink.Engine{}
	ctx := eng.CreateContext()

	file, err := os.Open("main.ink")
	defer file.Close()
	if err != nil {
		log.Fatal("Could not open main.ink for execution")
	}

	ctx.Exec(file)
	eng.Listeners.Wait()
}

IDE support

Ink currently has a vim syntax definition file, under utils/ink.vim. I'm also hoping to support Monaco / VSCode's language definition format soon with LSP support, but those are on the backburner as I use vim full-time and don't have a personal need for more advanced LSP support.

More Repositories

1

monocle

Universal personal search engine, powered by a full text search algorithm written in pure Ink, indexing Linus's blogs and private note archives, contacts, tweets, and over a decade of journals.
JavaScript
1,472
star
2

blocks.css

Add some dimension to your page with blocks 🚀
HTML
466
star
3

tabloid

A minimal programming language inspired by clickbait headlines
JavaScript
457
star
4

torus

Torus is an event-driven model-view UI framework for the web, focused on being tiny, efficient, and free of dependencies.
JavaScript
321
star
5

revery

A personal semantic search engine capable of surfacing relevant bookmarks, journal entries, notes, blogs, contacts, and more, built on an efficient document embedding algorithm and Monocle's personal search index.
JavaScript
273
star
6

unim.press

A Reddit front-page reader in the style of The New York Times.
JavaScript
244
star
7

oak

An expressive, simple, dynamic programming language.
HTML
231
star
8

polyx

Productivity suite written from scratch in Ink on the backend and Torus on the web
JavaScript
212
star
9

libsearch

Simple, index-free full-text search for JavaScript
JavaScript
159
star
10

merlot

Web based Markdown writing app built with isomorphic Ink and Torus
JavaScript
150
star
11

h12y

The email service for when just "hey.com" isn't enough.
HTML
147
star
12

modelexicon

This AI Does Not Exist: generate realistic descriptions of made-up machine learning models.
CSS
145
star
13

codeframe

The fastest, easiest way to build and deploy quick static webpages
JavaScript
127
star
14

draw

Real-time collaborative whiteboard on the web
JavaScript
126
star
15

inc

A note-taking tool based on the principles of incremental note-taking, designed for quickly capturing fleeting ideas and growing a knowledge base over time.
Makefile
126
star
16

histools

A collection of tools for generating data visualizations from browser history data
JavaScript
125
star
17

mira

A place for notes, but for the people I keep in touch with
JavaScript
116
star
18

tinyhumans

A little interactive sandbox for tiny people, tiny thoughts, and their tiny stories
HTML
116
star
19

lucerne

A Twitter reader designed for learning from the Twittersphere, built with Ink and Torus
JavaScript
115
star
20

calamity

Self-hosted GPT playground
CSS
110
star
21

stream

A Twitter-like micro-blog for personal project updates and snippets of thought, written in Oak
CSS
83
star
22

burds

Just some burds, jumpin' around in their own little world.
JavaScript
81
star
23

thingboard

A board of things, anywhere you want on the screen
JavaScript
67
star
24

ycvibecheck

Semantic search across every YC company ever. Vibe check your idea?
CSS
57
star
25

lovecroft

Minimal mailing list manager for static sites, with a simple JSON API
Go
57
star
26

frieden

My personal, read-only public availability calendar
JavaScript
55
star
27

paper.css

Lightweight, modern CSS to add some flair to your web-things 📜
HTML
41
star
28

klisp

A Lisp written in about 200 lines of Ink, featuring an interactive literate programming notebook
JavaScript
38
star
29

superstat

Git status + diff across every repo in a directory
Makefile
35
star
30

typogram

Small, minimalistic graphics for powerful ideas in a few words
JavaScript
34
star
31

plume

Small in-memory real time chat server with Go and WebSockets
Go
33
star
32

september

Ink to JavaScript compiler and toolchain, written in Ink itself
JavaScript
29
star
33

yolo

On the yolo page, anything goes... I'll merge any pull request you make to this website.
HTML
28
star
34

x-oak-notebook

Experimental tool for writing dynamic Markdown docs that embed interactive explorable visualizations
HTML
28
star
35

maverick

Web IDE and REPL for the Ink programming language, written in pure Ink on a self-hosted compiler toolchain
JavaScript
27
star
36

pico

Lightweight notepad for ephemeral memos, todos, meeting notes, and more
JavaScript
26
star
37

zone

A URL shortener / note sharing service.
JavaScript
25
star
38

kin

A refined tool for exploring open-source projects on GitHub with a file tree, rich Markdown and image previews, multi-pane multi-tab layouts and first-class support for Ink syntax highlighting.
JavaScript
24
star
39

august

Assembler from scratch written in Ink, supporting ELF on x86_64 and more.
Assembly
23
star
40

codesynth

Generate music from your source code 🎹
JavaScript
23
star
41

albatross

A simple to-do list app
CSS
23
star
42

spectre

Sparse autoencoders for Contra text embedding models
Jupyter Notebook
23
star
43

cornelia

Guess that Taylor Swift line <3
JavaScript
22
star
44

hfm

Hugging Face Download (Cache) Manager
Makefile
21
star
45

sistine

A simple, flexible, productive static site generator written entirely in Ink
HTML
21
star
46

shelf.page

An online, public “blog-shelf” for collecting and sharing interesting reads with your audience. What's on your digital shelf?
JavaScript
21
star
47

clozoom

Close your Zoom meeting tabs automatically
JavaScript
20
star
48

carlisle

A minimal template for a Hugo site
CSS
20
star
49

zerotocode

The best place on the web to learn to make stuff with code
HTML
19
star
50

litterate

Generate beautiful literate programming-style description of your code from comment annotations
JavaScript
19
star
51

xin

Xin (신/心) is a flexible functional programming language with a tiny core, inspired by Lisp and CSP
Go
19
star
52

animated-value

Imperative animation API for declarative UI renderers, like React, Preact, and Torus.
JavaScript
18
star
53

schrift

A more experimental runtime for Ink, focused on perf and instrumentation
Rust
17
star
54

rush

Rush lets you work on many files at once
Makefile
17
star
55

eliza

A modern port of the ELIZA conversational program to pure Ink to run as a command line and in the browser.
CSS
15
star
56

entr

A searchable repository of my personal notes from readings
JavaScript
15
star
57

sounds

A collection of sounds from places I've been
JavaScript
15
star
58

micropress

An Ink library for automatic text summarization
14
star
59

dotink

dotink (.ink) is the Ink programming language's blog, and my general technical blog
HTML
14
star
60

matisse

Gallery of generative art written with Ink
HTML
12
star
61

socialite

Fast social sharing metadata tag generator
JavaScript
12
star
62

traceur

Experimental pathtracing 3D renderer written in Ink
Makefile
12
star
63

tsqdm

TQDM for TypeScript / Deno
TypeScript
12
star
64

xi

A dynamic, stack-based concatenative toy programming language.
Logos
11
star
65

ink-vscode

Support for the Ink programming language in Visual Studio Code
10
star
66

x-oak-klisp

A Klisp (scheme-like flavor of Lisp) implementation in Oak
Vim Script
9
star
67

wintermute

Generating fake blog posts from my blog with a Markov chain
Go
8
star
68

looking-glass

A simple web screenshot API using Puppeteer
JavaScript
8
star
69

lambda

The untyped lambda calculus, implemented in Ink
8
star
70

etch

Dead simple project scaffolding for my commonly used layouts
CSS
7
star
71

oak-syntax-visualizer

Oak syntax visualizer, made for GopherCon 2021
CSS
7
star
72

vanta

Port of thesephist/klisp to pure Go
Go
6
star
73

dotfiles

Config, scripts, rc files 💻
JavaScript
5
star
74

ky

A modal text editor
Go
5
star
75

rational-arithmetic

A no-dependency, lightweight JS library for arithmetic with rational numbers
JavaScript
5
star
76

inkfmt

Code formatter for the Ink programming language
Shell
4
star
77

markovify

Using Markov chains to naively generate sequences of words from training samples.
JavaScript
4
star
78

nought

Personal people-manager, what some people might call a personal CRM
HTML
3
star
79

dessi

A quick, simple server-side-includes expander
JavaScript
3
star
80

papyrus

Small, static-site for hosting read-optimized content, like stories or e-books
HTML
3
star
81

codeliner

Generate codelines: like silhouette outlines, but for your source code
3
star
82

inker

Web API to run Ink code on any device 💻
JavaScript
3
star
83

state-of-startups-bearx

BearX's State of Startups Report
HTML
3
star
84

web-audio-workshop-2020

Web Audio API Workshop for Hack the Fog and hackswiftly 2020
JavaScript
3
star
85

send-tweet

Small Ink program to send tweets using the Twitter JSON API
2
star
86

korona

Take any JavaScript data and get back a reasonably unique hex or rgb color with an optional alpha channel 🖌
JavaScript
2
star
87

strat

Minimal framework for futures, options, and cryptocurrency investments built on the Robinhood (private) API
JavaScript
2
star
88

brandish

Visual branding for humans
JavaScript
2
star
89

hurricane

Zero-configuration, read-only JSON API proxy in front of an Airtable base
Go
2
star
90

traceur-web

Web and JavaScript port of thesephist/traceur
JavaScript
2
star
91

ittr

Small library of iterator-related utility functions for JavaScript
JavaScript
2
star
92

ansi.ink

Ink library for printing with ANSI escape sequences
1
star
93

pandora

A small, HTML5 quiz SPA
JavaScript
1
star
94

generator-vanilla-extension

Yeoman generator for simple chrome extensions
JavaScript
1
star
95

thesephist.github.io

Placeholder for personal website
HTML
1
star
96

pyro

Check if any of the routes in a list of critical routes of an app are failing
Go
1
star
97

sigil

My full-time to-do list and task manager 🔥
JavaScript
1
star
98

notepad

Short bash script to pull up $EDITOR
Shell
1
star
99

talaria

Inward-out gesture recognition in a wearable
Python
1
star
100

blocky-logos

Blocky logos, an exploratory creative project about logo and branding 🌁
HTML
1
star