Skip to content
Sign upLog in
This post is read-only. Explore Repls and connect with other creators on Community.View Community
The info in this post might be out of date, check out our docs instead. View docs



Strings is a programming language with only one data type: the string.

Overview and motivation

Inspiration comes from:

  • Z
  • TCL
  • JavaScript's horrible type-checking (string comparison for type-checking? seriously?)

I only found out about this jam three days before its end date, but it just so happened that I'd already been working on the spec and design for Strings. All code was written after August 28th, but I'd been working on the design doc for about a week before that.

This project is a combination of a few ideas I had:

  • It occurred to me that any data (including code) can be represented as a string, so I wanted to push that concept to its most absurd extreme and explore what a language with the string as its only data type would look and feel like.
  • I wanted to see how much of a usable language I could get out of as small an implementation as possible. The concept of bootstrapping "something" from "nothing" is one of my favorite aspects of programming.
  • Storytime: I was once given an assignment to implement an interpreter for a simple language. Not wanting to bother with an AST, I instead created a "tape recording" model where the interpreter would execute the code linearly, and control flow structures would insert instructions into the "playback" as needed. For example, a while loop would repeatedly insert its body into the "playback" stream each time its condition evaluated to true. While I'm no longer that lazy in implementing more proper, full-size languages, I was rather proud of that solution and wanted to see if I could expand on it.

Execution model


Strings programs operate on three I/O channels: input, output, and self. The buffer in the self channel is treated as the program code, and is automatically read and executed line-by-line. The input and output channels each have macros for reading and writing. The buffer assigned to each of the three channels can be changed during runtime.

Strings takes a folder as its target and looks for a []( file as its entry point. When executing in main, input is the CLI argument (blank for now), output is printed to the standard output at the end of execution, and self is the []( file.

When called in a macro, input is the parameter passed into macro, output is used as the result returned to the callsite, and self is the .st file with the macro name.

There are some special buffers:

  • in, out, and self will always refer to the original buffers associated with the input, output, and self channels.
  • null is always empty, always at eof, and consumes and discards any input it is given.

In addition to these defaults, you may create your own buffers. Simply use your own buffer name anywhere a buffer is expected, and it will be created for you if it doesn't already exist.

stdin and stdout buffers are planned so stay away from those names for now.

Built-in macros

The base language for Strings is quite slim. Here is the entire "standard library":

  • +* Arithmetic
  • < Arithmetic comparison
  • ? Conditional: If the first word in the parameter is "true" (case-sensitive), the rest of the param is evaluated as code. Anything else is considered false.
  • -> <- <> set buffer for output, input, and self respectively.
  • Buffer control:
    • >> append param to output buffer
    • << read input buffer until first instance of the parameter as delimiter. Everything up to and including the delimiter is consumed and discarded.
    • ?< peek input buffer to delimiter. Nothing is consumed.
    • <<< read all remaining contents of input buffer. Parameter is ignored but still required for parsing.
    • ?<< peek all remaining contents of input buffer. Nothing is consumed.

Everything else is up to you! Even concatenation (see examples/2-quote) and boolean negation (see examples/3-conditional) are user-implemented. Note that all built-in macros use symbols to distinguish them from user-created macros, which must all be valid file names.

With enough time and effort, it is possible (if not unwieldy) to implement all sorts of higher-level features such as compound data structures and OOP using only user-created macros.

Escape sequences

These allow you to use whitespace characters without disrupting the control flow by actually inserting a line-break or indent.

  • \s: space
  • \n: newline
  • \t: tab

Trying it out

Please be aware that the interpreter is very sensitive to whitespace. I highly recommend setting your editor to show whitespace. Make sure that your editor is set to use tabs, not spaces. The interpreter will not work if your .st files are tabbed with spaces instead of tab characters.

Create a folder with at least a []( file and point the interpreter to that folder, and it should do the rest!

Try out these examples:

  • kotlin -classpath main.jar MainKt examples/0-helloworld
  • kotlin -classpath main.jar MainKt examples/1-composition
  • kotlin -classpath main.jar MainKt examples/2-quote
  • kotlin -classpath main.jar MainKt examples/3-conditional
  • kotlin -classpath main.jar MainKt examples/4-boolean-logic
  • kotlin -classpath main.jar MainKt examples/5-loops
2 years ago




Hello there, this jam required you to work as a team of at least 2 people and submit a team repl. In case you did do the above, please mention your teammates in the post and provide a link to the team repl in the description as well.

I will be moving this to the SHare board for now.

2 years ago
Load more