This commit is contained in:
trimill 2024-12-25 12:53:56 -05:00
parent 2a8a7b347e
commit 859afa2b94
21 changed files with 1139 additions and 3 deletions

View file

@ -1,7 +1,26 @@
# talc
# Talc
## installation
Talc is a dynamically-typed functional programming language designed primarily
for use as a terminal calculator.
```sh
## Installation
Talc is written in Rust and requires at least version 1.81 to compile.
```bash
cargo install --profile release-opt --path talc-bin
```
Read the documentation for more info.
## Documentation
Documentation is available online at [TODO](TODO).
To build the documentation locally, install
[mdBook](https://rust-lang.github.io/mdBook/) and run the following from the
`docs/` directory:
```bash
mdbook serve --open
```

1
docs/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
book

6
docs/book.toml Normal file
View file

@ -0,0 +1,6 @@
[book]
authors = ["trimill"]
language = "en"
multilingual = false
src = "src"
title = "Talc"

24
docs/src/SUMMARY.md Normal file
View file

@ -0,0 +1,24 @@
- [Introduction](./introduction.md)
# Installation
- [Installation](./install/installation.md)
- [Tour of the REPL](./install/repl.md)
# The language
- [Arithmetic](./lang/arithmetic.md)
- [Bitwise operators](./lang/bitwise.md)
- [Operators reference](./lang/operators.md)
- [Variables and scope](./lang/variables.md)
- [Functions](./lang/functions.md)
- [Partial functions and pipes](./lang/partial.md)
- [Strings and symbols](./lang/strings.md)
- [Lists](./lang/lists.md)
- [Tables](./lang/tables.md)
- [Control flow](./lang/controlflow.md)
- [Iterators](./lang/iterators.md)
- [Ranges](./lang/ranges.md)
- [Exceptions](./lang/exceptions.md)

View file

@ -0,0 +1,17 @@
# Installation
To install Talc, first you must install the Rust programming language. Follow
[the instructions here](https://doc.rust-lang.org/book/ch01-01-installation.html)
to do so. Talc requires at least Rust 1.81.
Once Rust is installed, clone
[the Talc repository](https://g.trimill.xyz/trimill/talc) and, from the root of
the repository, run:
```bash
cargo install --profile release-opt --path talc-bin
```
This will build and install the Talc REPL. To run Talc, run `talc` (ensuring
that Cargo's installation path is part of your `$PATH`). For information about
command-line arguments, use `talc --help`.

36
docs/src/install/repl.md Normal file
View file

@ -0,0 +1,36 @@
# Tour of the REPL
To begin using Talc, launch the REPL by running `talc` with no arguments.
This chapter will showcase various features of the REPL environment. If you do
not understand any, try coming back to this chapter after reading the rest of
the book.
## Using the REPL
To use the REPL, simply enter an expression after the prompt (`>> `). If your
terminal supports it, entered text will be syntax-highlighted. To disable this,
use the argument `--color=never`. To force coloring even when support is not
detected, use `--color=always`.
To evaluate the expression, press Enter. If the input is incomplete (eg. some
delimiter is unmatched) the prompt will continue onto the next line.
## Previous results
The special global variables `_`, `__`, and `___` store the result of the
previous, second previous, and third previous results. Expressions that yield
a syntax or compile error or exception are skipped over.
## History
Use the up and down arrows to cycle through previous entries. By specifiying
a history file with the `-H`/`--histfile` argument, history can be preserved
between sessions.
## Advanced features
Use the `-a`/`--show-ast` flag to print the AST generated from the expression.
Use the `-d`/`--disasm` flag to show the disassembled bytecode generated by the
compiler.
The `--no-opt` flag disables the optimization step, which may affect the
bytecode but should not affect behavior.

36
docs/src/introduction.md Normal file
View file

@ -0,0 +1,36 @@
# Introduction
Talc is a dynamically-typed functional programming language designed primarily
for use as a terminal calculator.
Expand the sidebar to view installation instructions and a guide for using
Talc.
```talc
>> -- Basic arithmetic
>> 2*(6+3i) + ln(4) / sin(tau/5)
13.457636152392087+6.0i
>> -- Large integers
>> factors(2^107+1)
[3, 643, 84115747449047881488635567801]
>> -- Binary operations
>> bin(0xffff #& ~(0x1234 #& 0b10001101))
"1111111111111011"
>> -- Functions
>> dist = \x1,y1,x2,y2 -> sqrt((x1-x2)^2 + (y1-y2)^2)
<anon function(4) @57c274b03fc0>
>> dist(2, 3, 4, 4)
2.23606797749979
>> -- Pipeline syntax
>> [2, 4, 6, 8, 10] | map(\x -> x^2) | sum | factors | count
{ 2=2, 5=1, 11=1 }
>> -- Project Euler question 1
>> 0..1000 | filter(\x -> (x%3 == 0 or x%5 == 0)) | sum
233168
```

130
docs/src/lang/arithmetic.md Normal file
View file

@ -0,0 +1,130 @@
# Arithmetic
The most natural use for a calculator is to calculate. The Talc REPL will
evaluate any expression you enter and print the result.
```talc
>> 2 + 3
5
>> 10^2 + 5*6
130
```
All the above operations were performed on **integers**. To operate on
fractional numbers (**floats**), enter numbers with a decimal point:
```talc
>> 2.0 + 3.0
5.0
>> 1.0 / 3.0
0.3333333333333333
>> 1.2e3
1200.0
>> 0.1 + 0.2
0.30000000000000004
>> 0.1 + 0.2 == 0.3
false
```
Note that arithmetic on floats in Talc, as with other languages, may be
imprecise. For exact arithmetic on fractional numbers, use **ratios**:
```talc
>> 1/10 + 2/10
3/10
>> 1/10 + 2/10 == 3/10
true
```
Talc also supports **complex floats**, writing imaginary numbers with an `i`
suffix:
```talc
>> (1 + 2i)*(3 - 1i)
5.0+5.0i
>> 1i^1i
0.20787957635076193+0.0i
```
So far, we have seen five operators: addition (`+`), subtraction (`-`),
multiplication (`*`), division (`/`), and powers (`^`). Talc also supports
(Euclidean) integer division using `//` and the modulo operator `%`. A unary
minus can be used to negate a number or expression.
We have also seen parentheses (`( )`) used for grouping expressions. Operator
precendence follows mathematical convention, for a complete specification see
the [operator reference](./operators.md).
When an operation is performed on numbers of different types, one is
automatically coerced into the type of the other. Coercion goes along the
following heirarchy: `int -> ratio -> float -> complex`. The type of the result
is generally the same as the type of the inputs, with some exceptions:
- Division on integers yields a ratio
- An integer to a negative integer power yields a ratio (a nonnegative power
will still yield an integer)
- A ratio to a ratio power yields a float (a ratio to an integer power
still yields a ratio)
Multiple expressions may be written on the same line of the REPL by separating
them with semicolons. Only the value of the final one will be printed.
```talc
>> x = 5; x += 1; x * 2
12
```
## Comparison and booleans
To compare two values, use the equality operators `==` (equal) or `!=`
(inequal), or the comparison operators `>` (greater), `<` (less), `>=`
(greater or equal), and `<=` (less or equal). These also coerce their arguments
automatically. Complex numbers cannot be compared.
Equality and comparison operators evaluate to a *boolean*: either `true` or
`false`.
```talc
>> 1 + 2 == 3
true
>> 5 < 3.0
false
>> 1/2 >= 0.5
true
>> 1 + 3i < 5
Error: type_error: cannot compare 1.0+3.0i and 5
```
The boolean operators `and`, `or`, and `not` perform the expected operations
on booleans. Like in many other languages, `and` and `or`
[short-circuit](https://en.wikipedia.org/wiki/Short-circuit_evaluation).
```talc
>> 1 < 2 and 3 < 4
true
>> 1 > 2 or not 5 == 6
true
>> x = 5
5
>> 1 == 2 and (x += 3) == 8
false
>> x
5
```
`and`, `or`, and `not` can also be used on non-boolean values. Some Talc values
are considered *falsy* and coerce to `false` under such operations, these are
listed below.
- The boolean `false`
- The value `nil`
- A number equal to zero
- An empty range, string, list, or table
- A cell containing a falsy value
All other values are considered *truthy*, notably `true`, all nonzero numbers
(including `NaN`), nonempty collections, symbols, and functions.
The operators behave as follows
- `x and y` evaluates to `y` if `x` is truthy and to `x` if `x` is truthy
- `x or y` evaluates to `x` if `x` is truthy and to `y` if `x` is falsy
- `not x` evaluates to `true` if x is falsy and `false` if x is truthy

27
docs/src/lang/bitwise.md Normal file
View file

@ -0,0 +1,27 @@
# Bitwise operators
While most operators treat their operands as numbers, *bitwise operators*
consider the underlying binary representation. As such, these can only be
applied to integers.
```talc
>> 5 #& 12 -- bitwise AND
4
>> 5 #| 12 -- bitwise OR
13
>> 5 #^ 12 -- bitwise XOR
9
>> ~5 -- bitwise NOT
-6
>> 5 >> 2 -- shift right
1
>> 5 << 2 -- shift left
20
```
Integers are represented using
[two's complement](https://en.wikipedia.org/wiki/Two%27s_complement), so `~x`
is equivalent to `-x - 1`. Bitwise operations work as expected on large
integers - negative numbers are considered to have an infinite trail of ones
to the left (mirroring the infinite trail of zeroes present on positive
numbers).

View file

@ -0,0 +1,92 @@
# Control flow
Although Talc is a functional language, it also has imperative-style control
flow.
## If statement
The if-statement can be used to conditionally execute some code.
```talc
if 1 == 2 then
println("this will never print!")
end
```
Multiple `elif` clauses can be chained to an `if` to add alternate conditions,
and a final `else` clause can also be used if none of the previous clauses'
conditions are satisfied.
```talc
if 1 == 2 then
println("nope")
elif 3 == 4 then
println("not yet")
elif 5 == 5 then
println("here!")
else
println("too late.")
end
```
Like `do ... end` blocks, if-statements create a new scope for each of their
bodies.
Since every statement in Talc is an expression, it is meaningful to speak of
the value of an if-statement; this is just the value of whichever block is
evaluated.
```
>> -- note: there is a builtin function also named "abs"
>> abs = \x -> if x < 0 then -x else x end
<anon function(1) @5661c4350f40>
>> abs(3)
3
>> abs(-5)
5
```
If an `else` clause is missing and no condition is satisfied, the if-statement
will evaluate to `nil`.
## For loop
The `for` loop can be used to iterate over the values of a collection (or
[iterator](./iterators.md)). The body will be run once per item, with the
loop variable's value being that item.
```talc
# prints each animal on a new line
animals = ["cat", "elephant", "zebra", "goose"]
for a in animals do
println(a)
end
```
## While loop
The `while` loop can be used to repeat a section of code while a condition is
satisfied.
```talc
# prints the numbers 0, 1, 2, 5, 26, and 667, each on their own line
n = 0
while n < 1000 do
println(n)
n = n^2 + 1
end
```
## Break and Continue
The `break` and `continue` expressions can be used to exit a loop early or
skip to the next iteration. They always only affect the innermost loop.
`break` can optionally be provided an argument, e.g. `break 5`. If a loop is
exited using a `break` statement, the resulting value will be the value passed
to `break`, or `nil` if none was passed. If the loop exits normally, its value
is always `nil`.

View file

@ -0,0 +1,33 @@
# Exceptions
When an error occurs during execution of a program, an *exception* is thrown.
Exceptions propagate backwards through function calls until an appropriate
try-catch block is found.
To throw an exception, use the standard library function `throw`. The first
argument is a symbol representing the type of exception, the second is a
message (which must be a string).
```talc
>> throw(:my_exception, "something bad happened!")
Error: my_exception: something bad happened!
```
The exception can then be caught in a try-catch block:
```talc
try
println("1 / 0 = " ++ str(1/0))
catch :value_error in exc do
println("oops, can't divide by zero")
end
```
Each catch block may specify multiple exception types to match (separated by
commas), or an asterisk (`*`) to match all exceptions. The following portion
(to capture the exception into a variable) is optional. Multiple catch blocks
may be present and will be checked in order until a matching one is found.
Pressing `Ctrl`+`C` in the REPL will cause the VM to create an `:interrupted`
exception. This will always be detected quickly for Talc-native code but some
standard library functions might not check for this.

View file

@ -0,0 +1,67 @@
# Functions
Talc is a functional language, so many features are built on top of functions
and function application. As such, there are a few ways to define a function.
The simplest creates an anonymous (sometimes called "lambda") function using
a backslash (`\`). Since functions are first-class, we can assign the
newly-created function to a variable:
```talc
>> square = \x -> x^2
<anon function(1) @58e147c2f250>
>> square(5)
25
```
Functions may take multiple arguments:
```talc
>> area = \width,height -> width*height
<anon function(2) @58e147c30b50>
>> area(3, 6)
18
```
For simple one-argument functions, the parameter list can be omitted by using
an ampersand instead of a backslash. The single argument's name is the special
variable `$`:
```talc
>> square = &$^2
<anon function(1) @58e147c31950>
>> square(5)
25
```
As seen above, calling a function can be done by writing the arguments in
parentheses separated by commas immediately after the function.
An alternate function definition syntax allows you to name the function and is
more suitable for scripting:
```talc
fn square(x) do
x^2
end
println(square(5)) -- prints 25
```
By default, the value returned from a function is the value of the last
expression in the function's body. To return early, use the `return` keyword.
This will be more useful once we have seen other methods of control flow.
```talc
fn counting(amount) do
if amount < 0 then
println("I can't count backward!")
return
end
for i in 1..=amount do
println(i)
end
end
```
For more advanced use of functions, see
[Partial functions and pipes](./partial.md).

142
docs/src/lang/iterators.md Normal file
View file

@ -0,0 +1,142 @@
# Iterators
Iterators are one of the most powerful features of the Talc language and
standard library. An iterator is represented with a zero-argument function.
Every time the function is called it returns the next item in the sequence,
or the special value `:end_iteration` to signal that no more items are
available.
An iterator can be created simply by defining such a function:
```talc
>> n = 0
0
>> fn counting() do
if n == 3 then
return :end_iteration
end
n += 1
n
end
<function counting(0) @5661c434f760>
>> counting()
1
>> counting()
2
>> counting()
3
>> counting()
:end_iteration
>> counting()
:end_iteration
```
Much like lists and ranges, iterators can be used in a `for` loop. In fact,
lists and ranges are converted to iterators automatically by the `for` loop.
This can be done manually using the built-in `iter` function:
```talc
>> lst = [2, 3, 5]
[2, 3, 5]
>> lst_iter = iter(lst)
<native function iter[list](0) @5661c4352ef0>
>> lst_iter()
2
>> lst_iter()
3
>> lst_iter()
5
>> lst_iter()
:end_iteration
```
An iterator can be converted to a list using the `list` function:
```talc
>> lst = [2, 3, 5]
[2, 3, 5]
>> list(iter(lst))
[2, 3, 5]
```
## Iterator functions
The standard library includes many functions to manipulate iterators. Some
essential ones are listed here. Examples will show both conventional call
syntax as well as a version using a pipeline.
All standard library functions will take the iterator as the final argument.
### Map
`map` creates a new iterator from an existing one by passing each element
through a provided function.
```talc
>> list(map(\x -> x^2, [2, 3, 5]))
[4, 9, 25]
>> [2, 3, 5] | map(\x -> x^2) | list
[4, 9, 25]
```
### Filter
`filter` creates a new iterator only including values that, when passed to the
provided function, return a truthy value.
```talc
>> list(filter(\x -> x % 3 != 0, [1, 2, 3, 4, 5, 6]))
[1, 2, 4, 5]
>> [1, 2, 3, 4, 5, 6] | filter(\x -> x % 3 != 0) | list
[1, 2, 4, 5]
```
### Fold
`fold` consumes an iterator, applying a two-argument function repeatedly to its
values.
```talc
>> fold(\x,y -> x*y, [1, 2, 3, 4, 5, 6])
720
>> [1, 2, 3, 4, 5, 6] | fold(\x,y -> x*y)
720
```
`foldi` does the same but allows an initial value to be specified.
```talc
>> foldi(11, \x,y -> x/y, [1, 2, 3, 4, 5, 6])
720
>> [1,2,3,4,5,6] | foldi(11, \x,y -> x/y)
720
```
`sum` and `prod` consume an iterator and return the sum or product of its
elements, respectively.
### Forever and Take
`forever` creates an iterator that returns the given value every time it is
called - an endless iterator. It is useful in combination with `take`, which
creates an iterator that returns only the first *n* elements of its argument.
```talc
>> list(take(10, forever(42)))
[42, 42, 42, 42, 42, 42, 42, 42, 42, 42]
>> forever(42) | take(10) | list
[42, 42, 42, 42, 42, 42, 42, 42, 42, 42]
>> forever(42) | list -- bad idea!
```
### Enumerate
`enumerate` creates an iterator of two-element arrays, the first is the index
(starting from `0`) and the second is the item from the original iterator.
```
>> list(enumerate(["a", "b", "c"]))
[[0, "a"], [1, "b"], [2, "c"]]
>> ["a", "b", "c"] | enumerate | list
[[0, "a"], [1, "b"], [2, "c"]]
```

48
docs/src/lang/lists.md Normal file
View file

@ -0,0 +1,48 @@
# Lists
Lists in Talc are written as comma-separated items between square brackets.
Lists may be *heterogeneous* (contain items of different types). Similarly to
strings, lists may be concatenated using `++`. Lists can be indexed by writing
the index in square brackets after the list. The first element of the list is
at index zero.
```talc
>> numbers = [1, 5, 6, 4, 2, 3]
[1, 5, 6, 4, 2, 3]
>> numbers[3]
4
>> numbers ++ [8, 9]
[1, 5, 6, 4, 2, 3, 8, 9]
```
Lists are *mutable* and their elements can be assigned to or modified.
```talc
>> animals = ["cat", "dog", "elephant"]
["cat", "dog", "elephant"]
>> animals[1] = "moose"
"moose"
>> animals
["cat", "moose", "elephant"]
```
Use the built-in function `len` to get the length of a list.
```talc
>> languages = ["C", "Rust", "Haskell", "Lua"]; len(languages)
4
```
*Ranges* can also be used to get a subsequence of a list. We will discuss
ranges further in their own chapter.
```talc
>> squares = [0, 1, 4, 9, 16, 25, 36, 49]
[0, 1, 4, 9, 16, 25, 36, 49]
>> squares[1..4]
[1, 4, 9]
>> squares[3..*]
[9, 16, 25, 36, 49]
>> squares[*..-3]
[0, 1, 4, 9, 16]
```

View file

@ -0,0 +1,38 @@
# Operators reference
In increasing order of precedence:
| Operator | Kind | Description |
|------------------|--------|------------------------------------------|
| `=` | Right | Assignment |
| `\|` | Left | Pipeline |
| `\ &` | Prefix | Function definition |
| `or` | Left | Boolean disjunction |
| `and` | Left | Boolean conjunction |
| `not` | Prefix | Boolean negation |
| `= != > >= < <=` | None | Equality and inequality |
| `++` | Left | Concatenation |
| `&` | Left | Append |
| `.. ..=` | None | Range (exclusive and inclusive) |
| `*.. *..=` | Prefix | Range to (exclusive and inclusive) |
| `..*` | Suffix | Range from |
| `#\|` | Left | Bitwise OR |
| `#^` | Left | Bitwise XOR |
| `#&` | Left | Bitwise AND |
| `<< >>` | Left | Shift left and right |
| `+ -` | Left | Add and subtract |
| `* / // %` | Left | Multiply, divide, integer divide, modulo |
| `- ~` | Prefix | Negation and bitwise NOT |
| `^` | Right | Power |
| `() [] . ->` | Left | Call, index, access, associated call |
"Prefix" and "Suffix" denote unary operators placed before or after an
expression. "Left", "Right", and "None" denote binary operators that are left-,
right-, or not associative.
All three sets of range operators (`.. ..= *.. *..= ..*`) have the same
precedence level, despite being listed seperately.
Augmented assignment operators (
`++= &= #|= #^= #&= <<= >>= += -= *= /= //= %= ^=`) have the same
precedence and associativity as ordinary assignment.

54
docs/src/lang/partial.md Normal file
View file

@ -0,0 +1,54 @@
# Partial functions
If a function is supplied with more arguments than it expects, it will throw
an exception. If it is supplied with too few arguments, however, it will return
a *partially-applied function* (or partial function for short). This value may
then be called with the remaining arguments and the expected result will be
returned.
```talc
>> add_nums = \x,y,z -> x + y + z
<anon function(3) @58e147c31910>
>> add_nums(5, 6, 8)
19
>> add5 = add_nums(5)
<anon native function(2) @58e147c329e0>
>> add5_and6 = add5(6)
<anon native function(1) @58e147c33360>
>> add5_and6(8)
19
```
Note that each time the function is given an argument the number the result
expects decreases by one.
## Pipes
An alternate way of calling a function with one argument is using the pipe
operator (`|`). The order is reversed to a normal function call - the argument
is written before the pipe.
```talc
>> sin(2)
0.9092974268256817
>> 2 | sin
0.9092974268256817
```
Partial application combines very nicely with pipes: functions may first be
called with all but one of their arguments, and the resulting partially-applied
function may be called using a pipe.
```talc
>> gcd(70, 15)
5
>> 15 | gcd(70)
5
```
Note that the argument to the left of the pipe becomes the *final* argument to
the function. This is the opposite of langauges like Elixir, in which it
becomes the initial argument.
Pipelines will be featured heavily in the chapter on
[ranges and iterators](./iterators.md).

67
docs/src/lang/ranges.md Normal file
View file

@ -0,0 +1,67 @@
# Ranges
In the previous chapter we showed how to build an iterator that produces
numbers sequentially. To simplify this task, we can use *ranges*:
```talc
>> numbers = 2..10
2..10
>> list(numbers)
[2, 3, 4, 5, 6, 7, 8, 9]
>> numbers | filter(\x -> x%2 == 1) | list
[3, 5, 6, 9]
```
There are six kinds of range, depending on whether the range is endless on one
or both sides and if the ending bound is included or not
| Range | Description |
|-------|-------------------------------------------|
| 2..5 | Range from 2 to 5, excluding 5 |
| 2..=5 | Range from 2 to 5, including 5 |
| 2..* | Endless range starting at 2 |
| *..5 | Endless range stopping before 5 |
| *..=5 | Endless range stopping at 5 (including 5) |
| *..* | Endless in both directions |
Only ranges with an initial value can be used as iterators.
## Ranges for indexing
Ranges can also be used to index a list, returning the sublist specified by the
range.
```talc
>> items = [5, 10, 15, 20, 25, 30]
[5, 10, 15, 20, 25, 30]
>> items[2..4]
[15, 20]
>> items[2..=4]
[15, 20, 25]
>> items[*..4]
[5, 10, 15, 20]
>> items[*..=4]
[5, 10, 15, 20, 25]
>> items[2..*]
[15, 20, 25, 30]
>> items[*..*]
[5, 10, 15, 20, 25, 30]
```
The same works for strings:
```talc
>> "Hello, world!"[*..=4]
"Hello"
```
Negative indices will index starting at the back of the list:
```talc
>> items[-3]
20
>> items[*..-1]
[5, 10, 15, 20, 25]
```

74
docs/src/lang/strings.md Normal file
View file

@ -0,0 +1,74 @@
# Strings and symbols
Strings in Talc can contain any sequence of bytes. When representing text, they
conventionally contain UTF-8 encoded characters. String literals are written
between a pair of single- or double-quotes. Double-quoted strings may contain
escape sequences while single-quoted strings may not.
```talc
>> "Hello, world!"
"Hello, world!"
>> "Write \"escapes\" with a backslash: \\"
"Write \"escapes\" with a backslash: \\"
>> 'No escape needed: \'
"No escape needed: \\"
```
Strings can be concatenated using the `++` operator. Use the built-in function
`str` to convert values to strings.
```talc
>> apples = 5
5
>> "I have " ++ str(apples) ++ " apples"
"I have 5 apples"
```
See [the table below](#string-escapes) for a list of all escape sequences.
## Symbols
*Symbols* (called *atoms* in some languages) represent interned strings.
Symbols are used by default for the keys of [tables](./tables.md), but can also
be used for other purposes, such as enumerations. Since symbols are interned,
comparing or hashing them is very fast.
Symbols can be written with a colon followed by any sequence of characters that
would form a valid identifier. A string literal may also be used.
```talc
>> :a_symbol
:a_symbol
>> :"symbol with spaces"
:"symbol with spaces"
```
## String escapes
| Escape code | Result |
|-------------|---------------------------------------------------------|
| `\'` | Single quote (`'`) |
| `\"` | Double quote (`"`) |
| `\\` | Backslash (`/`) |
| `\0` | Null byte (0x00) |
| `\a` | Bell (0x07) |
| `\b` | Backspace (0x08) |
| `\t` | Tab (0x09) |
| `\n` | Newline (0x0A) |
| `\v` | Vertical tab (0x0B) |
| `\f` | Form feed (0x0C) |
| `\r` | Carriage return (0x0D) |
| `\e` | Escape (0x1B) |
| `\xXX` | An arbitrary byte specified by two hex digits |
| `\u{XX..}` | The sequence of UTF-8 bytes for the specified codepoint |
Examples:
```talc
>> "\x41"
"A"
>> "\x0A"
"\n"
>> "\u{1F41D}"
"🐝"
```

55
docs/src/lang/tables.md Normal file
View file

@ -0,0 +1,55 @@
# Tables
While lists associate their items with integer indices, *tables* associate
their values with keys of varying type. Tables are written in curly braces,
with the key and value separated by an equals sign (`=`).
```talc
>> numbers = { 1="one", 2="two", 55="fifty-five" }
{ 2="two", 55="fifty-five", 1="one" }
>> numbers[55]
"fifty-five"
>> numbers[20] = "twenty"
"twenty"
>> numbers
{ 2="two", 1="one", 20="twenty", 55="fifty-five" }
```
The order of elements in a table is unspecified and may even be different
between two otherwise identical tables.
Table keys may be any of the following:
- Integers (useful for representing sparse lists)
- Ratios
- Strings
- Symbols
- Booleans
- Nil
As a special case, if the key is written with an identifier, it will be treated
like a symbol. Accessing values with symbol keys can also be done using the
dot operator (`.`).
```talc
>> oxygen = { number=8, mass=15.999, symbol="O" }
{ mass=15.999, symbol="O", number=8 }
>> oxygen[:mass]
15.999
>> oxygen.number
8
```
To use the value of an expression as a key, surround the expression in
parentheses. This also allows for keys based on the value of a variable.
```talc
>> a = 12
12
>> tbl = { a="no parens", (a)="parens" }
{ 12="parens", a="no parens" }
>> tbl[:a]
"no parens"
>> tbl[a]
"parens"
```

View file

@ -0,0 +1,63 @@
# Variables and scope
Values can be assigned to variables for later use using an equals sign:
```talc
>> total = 13.95 + 6.98
20.93
>> tax = 0.04
0.04
>> total + total * tax
21.7672
```
Variables may contain Unicode; the first character must be an underscore (`_`)
or a character in the `XID_Start` character class and subsequent characters
must be in the `XID_Continue` class. From ASCII, the former includes lower-
and uppercase letters and underscores and the latter additionally includes
digits.
Variables can be reassigned and are dynamically-typed:
```talc
>> x = 5
5
>> x = x * (3.0 + 1.5i)
15.0+7.5i
```
The previous expression can be written more tersely using a compound assignment
operator:
```talc
>> x *= 3.0 + 1.5i
15.0+7.5i
```
In the REPL, variables at the top level are **global** by default. A variable
can be made **local** using the `var` keyword. The values of local variables
will not persist between entries to the REPL. Variables in scripts and in
inner scopes are local by default and can be made global by using the `global`
keyword.
Talc is lexically scoped. To create a scope, use the `do` and `end` keywords.
The REPL will not accept input until all delimiters have been matched, enabling
you to write multiline programs.
```talc
>> x = 5
5
>> do
y = 4
y += 5
x *= y
end
45
>> x
45
>> y
Error: name_error: undefined global y
```
Later we will see other expressions that create scopes, so it is important to
understand how they behave.

107
docs/theme/highlight.js vendored Normal file

File diff suppressed because one or more lines are too long