{"id":20858699,"url":"https://github.com/thesephist/xi","last_synced_at":"2026-03-14T09:02:27.323Z","repository":{"id":54476851,"uuid":"402998718","full_name":"thesephist/xi","owner":"thesephist","description":"A dynamic, stack-based concatenative toy programming language.","archived":false,"fork":false,"pushed_at":"2022-03-12T04:14:15.000Z","size":32,"stargazers_count":14,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-01-19T07:24:16.428Z","etag":null,"topics":["concatenative-language","factor","oaklang","stack-language"],"latest_commit_sha":null,"homepage":"https://oaklang.org/posts/xi/","language":"Logos","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thesephist.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-09-04T07:58:14.000Z","updated_at":"2025-01-14T11:06:51.000Z","dependencies_parsed_at":"2022-08-13T17:00:54.354Z","dependency_job_id":null,"html_url":"https://github.com/thesephist/xi","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thesephist%2Fxi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thesephist%2Fxi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thesephist%2Fxi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thesephist%2Fxi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thesephist","download_url":"https://codeload.github.com/thesephist/xi/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243230101,"owners_count":20257644,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["concatenative-language","factor","oaklang","stack-language"],"created_at":"2024-11-18T04:47:01.784Z","updated_at":"2025-12-24T09:53:13.152Z","avatar_url":"https://github.com/thesephist.png","language":"Logos","readme":"# Xi 🗼\n\n**Xi** (pronounced _Zai_) is a dynamic, stack-based concatenative language, written in [Oak](https://oaklang.org/) and using Oak types and semantics. I wrote Xi over the 2021 Labor Day weekend as a learning exercise to understand how stack languages work and why they're interesting. Xi is modeled mainly after [Factor](https://factorcode.org/), but this implementation is neither complete nor robust -- there's basically no error handling, for example, and Xi is not meant to be a faithful _re-implementation_ of Factor. It should run correct programs correctly, but will often fail on bad input.\n\n```js\n// Factorials to 10!\nfactorial : nat prod\n10 ( ++ factorial print ) each-integer\n\n// Fibonacci sequence to fib(25)\n(fib) : dup 2 \u003c ( drop swap drop ) ( ( swap over + ) dip -- (fib) ) if\nfib : 1 1 rot (fib)\n25 ( fib print ) each-integer\n```\n\nI've written more in-depth about my experiments with concatenative languages on [the Oak blog](https://oaklang.org/posts/xi/).\n\n## Overview\n\nXi is dynamically typed, and operates on the same basic values as Oak except that all numbers are `float`s. Like other concatenative stack programming languages, each statement (line) in a Xi program is a sequence of _words_, where each word manipulates a single global _data stack_ in some way, usually by moving and changing a few values at the top of the stack. Literal values like numbers and strings simply move those values onto the stack.\n\nFor example, the word `+` pops the top two values off the stack, adds them together, and pushes the sum back on the stack.\n\n```js\n1 2 10\n// stack: \u003c 1 2 10 \u003e\n+\n// stack: \u003c 1 12 \u003e\n+\n// stack: \u003c 13 \u003e\n```\n\nWe can also write these words all next to each other, and have the same program.\n\n```js\n1 2 10 + +\n// stack: \u003c 13 \u003e\n```\n\nThis is where the name _concatenative_ language comes from -- putting words next to each other composes those functions together in a predictable way.\n\nSometimes, we need to shuffle some items in the stack around to work on the right values without doing any other computation. These are called _stack shuffling_ words. Xi provides 4 basic ones, from which more complex words can be defined:\n\n```js\n2 dup\n// stack: \u003c 2 2 \u003e — duplicates the top value\n\n1 2 3 ( + ) dip\n// stack: \u003c 3 3 \u003e — runs a quotation (words inside `( ... )`) underneath the\n// topmost value on the stack\n\n1 2 drop\n// stack: \u003c 1 \u003e — simply drops the topmost value on the stack\n\n10 20 swap\n// stack: \u003c 20 10 \u003e — swaps the top 2 values' places on the stack\n```\n\nAs an example of basic composition, we can define `rot`, which rotates the top 3 items' places in the stack, like this.\n\n```js\n// define the word \"rot\"\nrot : ( swap ) dip swap\n\n1 2 3 rot\n// stack: \u003c 2 3 1 \u003e\n```\n\nXi's syntax is, like most concatenative languages, minimal. There are three kinds of primitive values: single-quoted strings, number (floating point) literals, and booleans `true` and `false`. Xi has lists and objects, delimited using `[ ... ]` and `{ ... }` respectively, though there isn't much of a vocabulary to work with objects. Finally, Xi has _quotations_, which are sequences of words that can be evaluated later, analogous to closures in other high-level languages. These types of values also represent the entirely of Xi's type system. There is no more sophisticated class system as in Factor.\n\nXi is a learning project, and thus not a great introduction to concatenative programming if you're new to it yourself. If you want to learn more about concatenative programming, you might want to check out these resources I found helpful as I learned about this space myself.\n\n- [A panoramic tour of Factor](https://andreaferretti.github.io/factor-tutorial/), which is the most beginner-friendly treatment of Factor and concatenative programming I could find\n- [A survey of stack shufflers](http://useless-factor.blogspot.com/2007/09/survey-of-stack-shufflers.html), which helped me get a better sense of how to use stack shuffling words, and how to \"think in Factor\", i.e. think about programming by composing words together\n- [Google TechTalk on Factor by its creator Slava Pestov](https://www.youtube.com/watch?v=f_0QlhYlS8g), which gives a great high-level overview of what makes concatenative programming and Factor attractive\n- [Bare metal x86 Forth](https://ph1lter.bitbucket.io/blog/2021-01-15-baremetal-x86-forth.html), an advanced and insightful deep dive into bootstrapping a concatenative programming language from assembly\n\n### Xi repl\n\nXi has a basic repl, which I used to test and debug words before adding them to my programs. Simply running `./xi.oak` without any arguments will start the repl. Through the repl, you can run any Xi code. However, there are a few specific \"debugging words\" that are useful for inspecting program state when in the repl.\n\n- `.` will pop the top value off of the stack and print it out.\n- `.s` (\"s\" for \"stack\") will print out a representation of the entire data stack at that point in the program\n- `.e` (\"e\" for \"environment\") will print out a dictionary of every word currently defined in scope and their definitions\n\n## Examples\n\nThough Xi is a pedagogical toy language and not exactly practical due to its brittleness and minimalism, there are a few sample programs I wrote to demonstrate how Xi programs work, and test my implementation. Besides these two, there is a small unit testing helper and test suite in `./test/unittests.xi`.\n\n### Fizzbuzz\n\nHere is some sample Xi code, the [FizzBuzz](https://en.wikipedia.org/wiki/Fizz_buzz) program. Though each statement must be in a single line in Xi, I've broken them up here into multiple lines for readability.\n\n```js\n// FizzBuzz in Xi\n\n// n -\u003e _\nfizzbuzz : dup 15 divisible? (\n    'FizzBuzz' print drop\n) (\n    dup 3 divisible?\n    (\n        'Fizz' print drop\n    ) (\n        dup 5 divisible?\n        ( 'Buzz' print drop ) ( print ) if\n    )\n    if\n) if\n\n// main\n100 ( ++ fizzbuzz ) each-integer\n```\n\nHere, the word `fizzbuzz` consumes a number at the top of the data stack and prints either 'Fizz', 'Buzz', 'FizzBuzz', or the number to output. The main program `100 ( ++ fizzbuzz ) each-integer` performs the quotation (`++ fizzbuzz`) for each integer counting up from 0 to 100, exclusive. Running this program with\n\n```sh\n./xi.oak ./samples/fizzbuzz.xi\n```\n\nshould produce the correct output.\n\n### Factorial\n\nThe sample [`./samples/factorial.xi`](samples/factorial.xi) computes factorials of every number from 1 to 10, inclusive, and prints it. This program is a great demonstration of how elegant and concise well-designed concatenative programs can be, if the right primitives are composed well. This program is just two short lines:\n\n```js\nfactorial : nat prod\n10 ( ++ factorial print ) each-integer\n```\n\nFirst, we define the word `factorial` that takes a number, generates a list of numbers counting up from 1 to that number (`nat`), and takes their total product (`prod`). Then we loop through every number from 1 to 10, and compute the factorial and print it. This generates the correct output\n\n```\n1\n2\n6\n24\n120\n720\n5040\n40320\n362880\n3.6288e+06\n```\n\n## Development\n\nXi is a project written in the [Oak programming language](https://oaklang.org/). All of the core language and \"kernel\" is defined in a single Oak program, `./xi.oak`.\n\nA small unit test suite is defined in `./test/unittests.xi`. To run it, simply run\n\n```sh\n./xi.oak ./test/unittests.xi\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthesephist%2Fxi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthesephist%2Fxi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthesephist%2Fxi/lists"}