Make web playground reference spec
switch to flakes; fix flatten action
Remove unneeded add in docs
A funky programming language made in 24(ish) hours
; Simple "Hello, world!" program ~
(`hello, world' println)
;
It's pretty simple to define a variable
You need: the data, and a string as a name.
~
((1 2 3 4 add) `myvarname' defvar)
(myvarname println)
(myvarname println)
(myvarname println)
(myvarname println)
; All variables are immutable! You can replace the variable, but you can't edit it. ~
((1 2 3 4/3 add) `myvarname' defvar)
(myvarname println)
((1 2 3 4 5 add) `myvarname' defvar)
(myvarname println)
;
actions, or functions, are just strings that are executed. arguments passed are available
through the `args' variable
~
([(args add)] `myaction' defact) ; define an action called `myaction' ~
(1 2 3 4 myaction print) ; call myaction with arguments 1 2 3 4 ~
; A simple program that prints the numbers from 1 to 100 ~
; Creates a variable to store the counter, then recursively increments it ~
; This does mean that loops have a finite size before stack overflow occurs ~
(1 `counter' defvar)
(
[(counter 100
[
(counter println)
(1 counter add `counter' defvar)
(body)
]
`' lte)]
`body' defact
)
(body)
; Prints the numbers from 1 to 1000000 without recursion ~
(1 `count' defvar)
(
[
(count println)
(count 1 add `count' defvar)
]
1000000 repeat
)
It's very simple. Install Rust and compile it just like with every other Rust project:
$ cargo build --release
$ target/release/cane FILE
Go into the cane-wasm
directory and run:
wasm-pack build --target web
Open the index.html
file in your favourite browser.
Well, there really isn't one. I'll just tell you what you can and can't expect, as well as the currently-implemented tokens.
Cane is a reverse-polish-notation language. This means that the data comes before the "trigger" for the action that is executed. Some actions consume the data, some change it, some add to it, and some do nothing to it.
Name | Args | Notes |
---|---|---|
print |
any | Prints the data as a string. Does not consume data |
println |
any | Prints the data as a string with a newline appended. Does not consume data |
add |
any | Adds the data together. Not recursive. Consumes data. Based on type of first item. Adds second item to first, then third item to that, then so on. |
drop |
any | Deletes all previous data |
duplicate |
(Data..., Number) |
Duplicates the data N times. N must be a non-negative integer. Does not consume data (it makes more of it) |
flatten |
Data | If lists are nested, move them all to the top level. No lists will remain. |
Data in Cane can be of the following:
Lists contain data, including other lists.
Lists can be joined into strings with a separator:
Name | Args | Notes |
---|---|---|
join |
(Data..., String) |
Joins the data into a string with delimiter specified by the last parameter. |
To take the contents of an inner list and move it to the parent list, use an unwrap: ?
.
Example:
(1 2 3 (4 5)?)
That will bring 4 5
into the outer list:
(1 2 3 4 5)
Strings begin with a `
and end with a '
. See also special action notation.
Strings can be escaped. Currently the following escapes are implemented:
\n
: newline\r
: carriage return\'
: single quote\\
: single backslashStrings can be split into a list inplace: the string will be "exploded". More strings will be returned.
Name | Args | Notes |
---|---|---|
split |
(String, String) |
Splits the first string by the delimiter in the second string. |
All numbers are infinite precision rational numbers. They are represented as base-10 irrational fractions.
Examples:
(1 2 8/2 -5439053/123129)
Name | Args | Notes |
---|---|---|
mult |
(Number...) |
Multiplies the numbers in order: (1 2 3 mult) is 1 * 2 * 3 = 6 |
sub |
(Number...) |
Subtracts the numbers in order: (1 2 3 sub) is 1 - 2 - 3 = -4 |
div |
(Number...) |
Divides the numbers in order: (1 2 3 div) is (1/2)/3 = 1/6 |
neg |
Number |
Negates the previous number: (1 neg) is (-1) |
floor |
Number |
Rounds down the previous number: (99/98 floor) is (1) |
ceil |
Number |
Rounds up the previous number: (99/98 ceil) is (2) |
abs |
Number |
Finds the distance from zero of previous number: (-99/98 abs) is (99/98) |
Note that add
is not number-specific so it is documented in basic actions.
These can be converted between each other with casts. Each consumes the data preceding and replaces it with the new data.
Name | From | To | Notes |
---|---|---|---|
tostring |
any | String | Concats list to string |
tonumber |
any | Number | Returns list length. Tries to convert string to number and panics on fail. |
tolist |
any | List | Explodes string into list of characters. Wraps number in list. |
Variables are saved data with a String key. Actions are saved strings with a String key. Actions are executed by making a new interpreter and running the code in the string.
Name | Args | Notes |
---|---|---|
defvar |
(Data, String) |
The String will be the name of the variable |
defact |
(String, String) |
The String will be the name of the action |
Because actions take strings, nesting them can make code very messy. Surround the string with [
and ]
. No escaping will be done.
Before:
(1 `counter' defvar)
(
`(counter 100 `(counter `\n\\\' print) (1 counter add `counter\\\' defvar) (body)\' `\' lte)'
`body' defact
)
(body)
After:
(1 `counter' defvar)
(
[(counter 100 [(counter `\n' print) (1 counter add `counter' defvar) (body)] 0 lte)]
`body' defact
)
(body)
Control flow works similarly to actions. It will evaluate the data, and then execute a string based on the result.
(Data..., String, String)
.Name | Args | Notes |
---|---|---|
eq |
(Data..., String, String) |
All data must be of the same type and value |
lt |
(Data..., String, String) |
Checks if the data is in ascending order (no duplicates) |
lte |
(Data..., String, String) |
Checks if the data is in ascending order (duplicates allowed) |
gt |
(Data..., String, String) |
Checks if the data is in descending order (no duplicates) |
gte |
(Data..., String, String) |
Checks if the data is in descending order (duplicates allowed) |
When returning a string, it would have to be nested like so:
(1 2 `(`ascending\')' ``descending\'' lt print)
or by using special action notation:
(1 2 [(`ascending')] [(`descending')] lt print)
instead, the string can be passed directly:
(1 2 `ascending' `descending' lt print)
This has the interesting property making any program with invalid tokens valid because it will be saved into an internal string buffer, then it will exit.
Loops are similar to control flow. It will execute the string N
(or infinity) times.
Name | Args | Notes |
---|---|---|
repeat |
String, Number |
Executes the string N times. N must be a non-negative integer. |
forever |
String |
Executes the string forever. |
Example:
; Prints "hi" 100 times ~
(
[
(`hi' println)
]
100 repeat
)