diff --git a/README.md b/README.md index 7e829df..1bb5a32 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,60 @@ -# docs -read the [spec](spec) +# Chloride / Argon Documentation - - [Variable and function assignment](variable-and-function-assignment) +This is community documentation for **Argon**, the programming language, as implemented by +**Chloride**, its C-based interpreter (a bytecode compiler + virtual machine). + +If you already know a scripting language like Python or JavaScript, most of this will feel +familiar. Argon uses `do` blocks instead of `{ }`, and functions are declared as +`let name(args) = do ... `. + +> **Note:** This documentation is written from reading the interpreter's source code and +> test suite, since the project doesn't yet have complete official docs. If something here +> looks wrong, trust the source code (and please fix this doc!). + +## Start here + +- [Installation](getting-started/README.md) +- [Your first program](getting-started/first-program/README.md) +- [Running the REPL](tooling/repl/README.md) + +## Language guide + +- [Variables and `let`](language/variables/README.md) +- [Operators](language/operators/README.md) +- [Control flow](language/control-flow/README.md) + - [`if` / `else`](language/control-flow/if/README.md) + - [`while` loops](language/control-flow/while/README.md) + - [`for` loops and iterators](language/control-flow/for/README.md) + - [`break` and `continue`](language/control-flow/break-continue/README.md) +- [Functions](language/functions/README.md) +- [Strings](language/strings/README.md) +- [Arrays](language/arrays/README.md) +- [Dictionaries and tuples](language/dictionaries-and-tuples/README.md) +- [Classes and objects](language/classes/README.md) +- [Errors and exceptions](language/error-handling/README.md) +- [Modules and `import`](language/modules/README.md) + +## Standard library + +- [`term`](standard-library/term/README.md) — printing and input +- [`maths`](standard-library/maths/README.md) +- [`random`](standard-library/random/README.md) +- [`path`](standard-library/path/README.md) +- [`json`](standard-library/json/README.md) +- [`file`](standard-library/file/README.md) +- [`date`](standard-library/date/README.md) +- [`time`](standard-library/time/README.md) +- [`network`](standard-library/network/README.md) +- [`threading`](standard-library/threading/README.md) +- [`regex`](standard-library/regex/README.md) + +## Tooling + +- [Command line usage](tooling/README.md) +- [The REPL](tooling/repl/README.md) +- [Building from source](tooling/building-from-source/README.md) + +## License + +Chloride is released under the GNU General Public License v3.0 (or later). See the +project's `LICENSE` file for details. \ No newline at end of file diff --git a/_config.yml b/_config.yml index 0e199a9..d8bccd8 100644 --- a/_config.yml +++ b/_config.yml @@ -1,3 +1,3 @@ -title: Argon v3 -description: This is the documentation for the Argon v3 Programming language. +title: Argon +description: This is the documentation for the Argon Programming language. This Documentation mainly targets the chloride interpreter. theme: jekyll-theme-dinky diff --git a/getting-started/README.md b/getting-started/README.md new file mode 100644 index 0000000..05c13c8 --- /dev/null +++ b/getting-started/README.md @@ -0,0 +1,71 @@ +# Installation + +Chloride mainly targets **Linux**, **macOS**, **Windows**, and **FreeBSD**, in that order of +priority. Official builds are produced for Linux x86_64, Windows x86_64, and macOS arm64. + +If your platform isn't officially supported, feel free to open an issue, or build from +source (see [Building from source](../tooling/building-from-source/README.md)) and send a +pull request. + +## Debian (and Debian-based distributions) + +Add the Open-Argon apt repository: + +```bash +sudo curl https://git.wbell.dev/api/packages/Open-Argon/debian/repository.key -o /etc/apt/keyrings/gitea-Open-Argon.asc +echo "deb [signed-by=/etc/apt/keyrings/gitea-Open-Argon.asc] https://git.wbell.dev/api/packages/Open-Argon/debian trixie main" | sudo tee -a /etc/apt/sources.list.d/gitea.list +sudo apt update +``` + +Then install: + +```bash +sudo apt install argon +``` + +## Fedora (and Fedora-based distributions) + +Import the signing key: + +```bash +curl https://git.wbell.dev/Open-Argon/Chloride/raw/branch/main/gpg/argon-packages-public.gpg | sudo rpm --import - +``` + +Add the repository: + +```bash +# on RedHat based distributions +dnf config-manager --add-repo https://git.wbell.dev/api/packages/Open-Argon/rpm.repo + +# Fedora 41+ (DNF5) +dnf config-manager addrepo --from-repofile=https://git.wbell.dev/api/packages/Open-Argon/rpm.repo +``` + +Then install: + +```bash +sudo dnf install argon +``` + +## Windows + +There's no package manager distribution yet (no winget/chocolatey), but a Windows setup +executable is built for every release. + +## macOS (Homebrew) + +Homebrew is the recommended way to install on macOS, supporting both Apple Silicon and +Intel: + +```bash +brew tap open-argon/argon https://git.wbell.dev/Open-Argon/homebrew-open-argon +brew install argon +``` + +## Building from source + +If none of the above work for you, see [Building from source](../tooling/building-from-source/README.md). + +## Next steps + +Once `argon` is installed, head to [Your first program](first-program/README.md). \ No newline at end of file diff --git a/getting-started/first-program/README.md b/getting-started/first-program/README.md new file mode 100644 index 0000000..8a7c5fa --- /dev/null +++ b/getting-started/first-program/README.md @@ -0,0 +1,56 @@ +# Your first program + +Create a file called `hello.ar`: + +``` +term.log("Hello, world!") +``` + +Run it with the `argon` interpreter: + +```bash +argon hello.ar +``` + +You should see: + +``` +Hello, world! +``` + +## A quick tour + +Argon files use the `.ar` extension. Comments start with `#`: + +``` +# this is a comment +let name = "world" +term.log(`Hello, $(name)!`) +``` + +Notice the backtick string above — this is a **template string**, and anything inside +`$( ... )` is evaluated as Argon code and interpolated into the string. See +[Strings](../../language/strings/README.md) for more. + +Blocks in Argon are introduced with the `do` keyword rather than braces: + +``` +let add(a, b) = do + return a + b + +let x = 5 +if (x > 3) do + term.log("big") +else do + term.log("small") +``` + +Whitespace/indentation is not significant for parsing (the `do ... ` block continues until +the parser determines it's finished based on structure), but you should still indent +consistently for readability — see the examples throughout this documentation. + +## Next steps + +- [Variables and `let`](../../language/variables/README.md) +- [Functions](../../language/functions/README.md) +- [Control flow](../../language/control-flow/README.md) \ No newline at end of file diff --git a/language/arrays/README.md b/language/arrays/README.md new file mode 100644 index 0000000..e69de29 diff --git a/language/control-flow/README.md b/language/control-flow/README.md new file mode 100644 index 0000000..77e3727 --- /dev/null +++ b/language/control-flow/README.md @@ -0,0 +1,20 @@ +# Control flow + +Argon has the control flow constructs you'd expect from a C-family / scripting language: + +- [`if` / `else`](if/README.md) +- [`while` loops](while/README.md) +- [`for` loops and iterators](for/README.md) +- [`break` and `continue`](break-continue/README.md) + +All of these use `do` blocks instead of curly braces. A condition is always wrapped in +parentheses: + +``` +if (x > 0) do + term.log("positive") +``` + +A single-statement body doesn't strictly need `do` in every case (see +[`factorial.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/factorial.ar), where `if (n) return x*factorial(n)` +has no `do`), but using `do` for multi-statement bodies is required. \ No newline at end of file diff --git a/language/control-flow/break-continue/README.md b/language/control-flow/break-continue/README.md new file mode 100644 index 0000000..f2ee7aa --- /dev/null +++ b/language/control-flow/break-continue/README.md @@ -0,0 +1,33 @@ +# `break` and `continue` + +`break` exits the nearest enclosing loop; `continue` skips to the next iteration. + +``` +let f() = do + let x = 10 + let i = 0 + while (i < 100) do + i = i + 1 + if (i > (x * 2)) break + if (i > x) continue + term.log(i) + + term.log(i, x) + +let g() = f() + +g() +``` + +(from +[`loop_continue_and_break_test.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/loop_continue_and_break_test.ar)) + +This example logs `1` through `10` (stopping the `term.log(i)` calls once `i > x`, via +`continue`), then keeps looping silently until `i` exceeds `x * 2`, at which point `break` +exits the loop entirely and the final `term.log(i, x)` runs — printing `21 10`. + +Both keywords work inside `for` loops too, in exactly the same way. + +## Next + +- [Functions](../../functions/README.md) \ No newline at end of file diff --git a/language/control-flow/for/README.md b/language/control-flow/for/README.md new file mode 100644 index 0000000..7b37ed1 --- /dev/null +++ b/language/control-flow/for/README.md @@ -0,0 +1,74 @@ +# `for` loops and iterators + +## Basic syntax + +``` +for (x in 0 until 1e7) do + ... +``` + +(from [`range_test.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/range_test.ar)) + +`for (item in iterable) do ...` iterates over anything **iterable** — arrays, ranges, +strings, dictionaries, or any custom object that implements the iterator protocol +described below. + +``` +for (item in [1, 2, 3]) do + term.log(item) +``` + +## Ranges + +`a until b` produces a range you can loop over. The standard library also exposes a +`range(...)` function (used e.g. in +[`map_filter_sort.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/map_filter_sort.ar) as +`range(-1000000, 1000000)`), which is the built-in `range` type +(`ARGON_RANGE_ITERATOR_TYPE`) constructed directly. + +## The iterator protocol + +Any object can be made iterable by implementing two dunder methods: + +- `__iter__(self)` — returns an **iterator** object. +- `__next__(self)` — called repeatedly on the iterator; each call returns the next value, + or throws `StopIteration()` once exhausted. + +Here's a full custom range implementation from the test suite: + +``` +class custom_range_iterator do + this.__init__(self, stop) = do + self.current = 0 + self.stop = stop + this.__next__(self) = do + if (self.stop <= self.current) do + throw StopIteration() + let result = self.current + self.current = self.current + 1 + return result + +class custom_range do + this.__init__(self, stop) = do + self.stop = stop + this.__iter__(self) = custom_range_iterator(self.stop) + +for (x in custom_range(1e4)) do + for (y in custom_range(1e3)) do + ... +``` + +(from [`custom_range.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/custom_range.ar)) + +`StopIteration` is one of the built-in system exceptions (see +[Errors and exceptions](../../error-handling/README.md)) and is how iterators signal "no +more values" — it's caught internally by the `for` loop machinery, not something you +usually need to catch yourself. + +## Nested loops + +Loops can be nested arbitrarily, as shown in the `custom_range` example above. + +## Next + +- [`break` and `continue`](../break-continue/README.md) \ No newline at end of file diff --git a/language/control-flow/if/README.md b/language/control-flow/if/README.md new file mode 100644 index 0000000..a0529f1 --- /dev/null +++ b/language/control-flow/if/README.md @@ -0,0 +1,44 @@ +# `if` / `else` + +``` +let x = 5 + +if (x > 10) do + term.log("big") +else if (x > 0) do + term.log("small positive") +else do + term.log("zero or negative") +``` + +## Single-statement form + +If the body is a single statement, `do` can be omitted: + +``` +let factorial(x) = do + let n = x - 1 + if (n) return x*factorial(n) + return 1 +``` + +(from [`factorial.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/factorial.ar)) + +## Truthiness + +Any value can be used as a condition. `0`, `null`, `false`, and empty strings/collections +are falsy; everything else is truthy. In the `factorial.ar` example above, `if (n)` is +shorthand for "if `n` is not zero". + +## Ternary expression + +For simple conditional *expressions* (not statements), see the ternary operator described +in [Operators](../../operators/README.md#ternary-conditional-expression): + +``` +let sign = a < b ? -1 : a > b ? 1 : 0 +``` + +## Next + +- [`while` loops](../while/README.md) \ No newline at end of file diff --git a/language/control-flow/while/README.md b/language/control-flow/while/README.md new file mode 100644 index 0000000..d5dcec1 --- /dev/null +++ b/language/control-flow/while/README.md @@ -0,0 +1,44 @@ +# `while` loops + +``` +let i = 0 +while (i < 10) do + term.log(i) + i += 1 +``` + +A `while` loop with a truthy constant condition (e.g. a non-zero length) can be used to +drain a collection: + +``` +import "../stdlib/random" expose random + +let x = [] +let i = 0 + +while (i < 1e6) do + x.append(i) + i = i + 1 + +let y = [] +while (x.length) do # loops while x.length is truthy (non-zero) + let result = x.pop(random.int(0, x.length - 1)) + y.append(result) +``` + +(from [`array_pop.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/array_pop.ar)) + +## `while` as an expression-friendly loop + +`while` conditions are parenthesized expressions, so they can contain assignments, +function calls, comparisons, etc. — anything that evaluates to a value. + +## Infinite loops + +There's no dedicated `loop` keyword; use `while (true) do ...` and `break` to exit (see +[`break` and `continue`](../break-continue/README.md)). The test suite has a file named +[`foreverloop.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/foreverloop.ar) demonstrating this pattern. + +## Next + +- [`for` loops and iterators](../for/README.md) \ No newline at end of file diff --git a/language/functions/README.md b/language/functions/README.md new file mode 100644 index 0000000..bb91c65 --- /dev/null +++ b/language/functions/README.md @@ -0,0 +1,155 @@ +# Functions + +## Declaring a function + +Functions are declared with `let`, just like variables — a function is a value: + +``` +let add(a, b) = do + return a + b + +term.log(add(2, 3)) # 5 +``` + +For a single-expression body, you can skip `do` and `return` entirely: + +``` +let square(x) = x * x +``` + +This is the same pattern used for one-line class methods (see +[Classes and objects](../classes/README.md)). + +## Anonymous (lambda) functions + +Functions don't need a name. An anonymous function is written the same way, just without +an identifier before the parameter list: + +``` +term.log(()=10) # a zero-argument anonymous function returning 10 +term.log((x)=10) +term.log((x,y)=10) +term.log((x,y,z)=10) + +# named versions, for comparison: +term.log(a()=10) +term.log(b(x)=10) +``` + +(from [`anonymous_function.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/anonymous_function.ar)) + +Anonymous functions are commonly passed directly as arguments, e.g. to `array.map`: + +``` +term.log( + array(range(-1000000, 1000000)) + .map((val) = val * val) + .filter((val) = val < 10000) + .sort((a, b) = a < b ? -1 : a > b ? 1 : 0) +) +``` + +(from [`map_filter_sort.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/map_filter_sort.ar) — this also shows +method chaining on arrays; see [Arrays](../arrays/README.md).) + +## Default arguments + +``` +let round(num, precision=1) = do + ... +``` + +(from [`stdlib/maths/init.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/stdlib/maths/init.ar)) + +## Variadic arguments: `*args` and `**kwargs` + +A function can collect any extra positional arguments into an array with `*args`, and any +extra keyword arguments into a dictionary with `**kwargs`: + +``` +let f(*args, **kwargs) = do + term.log(args, kwargs) + +f("Hello world", *[1, 2, 3], **{x: 1, y: 5, z: 7}) +``` + +(from [`argument_unpacking.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/argument_unpacking.ar)) — note that +`*` and `**` are also used at the **call site** to unpack an existing array/dictionary into +positional/keyword arguments, exactly like the definition-site collectors. + +### Mixing required, default, `*args`, and `**kwargs` + +``` +let f(x, a=null, *args, **kwargs) = do + return `x=$(x), a=$(a), args=$(args), kwargs=$(kwargs)` + +term.log(f(1)) +term.log(f(1, 2)) +term.log(f(1, 2, 3)) +term.log(f(a=2, *["this should go to x"])) +term.log(f(1, a=2, **["this should go to kwargs"])) +term.log(f(1, a=2, **{wow: "this should go to kwargs"})) +term.log(f(a=2, **{x: "this should go to x"})) +``` + +(from +[`test_mixed_args_defaults_and_unpacking.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/test_mixed_args_defaults_and_unpacking.ar)) + +This shows that keyword arguments can fill in named parameters like `x` even when passed +via `**{x: ...}`, and that unpacking a plain array with `**` still works, filling in +`kwargs` positionally in the array's order. + +## Recursion + +Functions can call themselves normally: + +``` +let factorial(x) = do + let n = x - 1 + if (n) return x * factorial(n) + return 1 +``` + +(from [`factorial.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/factorial.ar)) + +## Closures + +Functions capture variables from their enclosing scope: + +``` +let f() = do + let x = 10 + let i = 0 + while (i < 100) do + i = i + 1 + ... + +let g() = f() + +g() +``` + +(from +[`loop_continue_and_break_test.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/loop_continue_and_break_test.ar)) + +## Functions as methods on built-in types + +You can attach a method directly to a built-in type, e.g. `string`: + +``` +string.cool(self) = do + term.log(self, self.length) + return 10 + +'hello world'.cool() +string.cool('goodbye world') +``` + +(from [`class_method.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/class_method.ar)) — note the two +equivalent call styles: as an instance method (`'hello world'.cool()`) or as an explicit +call on the type passing `self` (`string.cool('goodbye world')`). + +## Next + +- [Classes and objects](../classes/README.md) +- [Error handling](../error-handling/README.md) \ No newline at end of file diff --git a/language/operators/README.md b/language/operators/README.md new file mode 100644 index 0000000..71c06e0 --- /dev/null +++ b/language/operators/README.md @@ -0,0 +1,119 @@ +# Operators + +## Arithmetic + +| Operator | Meaning | +|----------|-----------------| +| `+` | addition | +| `-` | subtraction / unary negation | +| `*` | multiplication | +| `/` | division | +| `//` | floor division | +| `%` | modulo | +| `**` | exponent (based on the `EXPONENT_FUNCTION` builtin) | + +``` +term.log(1 + 2) # 3 +term.log(7 // 2) # 3 +term.log(7 % 2) # 1 +``` + +Every arithmetic operator is backed by a global function you can also call directly, and +which classes can override by implementing the matching dunder method (e.g. `__add__`): + +``` +term.log(add(1, 2)) # 3 +term.log(subtract(5, 2)) # 3 +term.log(multiply(2, 3)) # 6 +term.log(divide(6, 2)) # 3 +term.log(floor_divide(7, 2))# 3 +term.log(modulo(7, 2)) # 1 +term.log(exponent(2, 3)) # 8 +``` + +Numeric literals also support scientific notation and hex, e.g. `1e5`, `1e7`, `0xD800` +(see [`array_set.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/array_set.ar) for `1e5` and +[`json/init.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/stdlib/json/init.ar) for hex literals). + +## Comparison + +| Operator | Meaning | +|----------|---------| +| `==` | equal | +| `!=` | not equal | +| `<` | less than | +| `<=` | less than or equal | +| `>` | greater than | +| `>=` | greater than or equal | + +These also have global function equivalents: `equal`, `not_equal`, `less_than`, +`less_than_equal`, `greater_than`, `greater_than_equal` — and each is backed by a dunder +method (`__equal__`, `__less_than__`, etc.) that a class can override, e.g. the standard +`number` and `string` types both implement their own comparison dunders. + +## Logical operators + +``` +a && b # logical AND +a || b # logical OR (used in stdlib code, e.g. path.ar's `while ("//" in p)` style checks) +!a # logical NOT +``` + +See [`my_first_calculator.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/my_first_calculator.ar) for repeated +use of `&&` in condition chains. + +## The `in` operator + +Test membership in a string, array, or dictionary: + +``` +term.log("ell" in "hello") # true, substring check +term.log(3 in [1,2,3]) # true +term.log("key" in {key: 1}) # true, checks dictionary keys +``` + +This is backed by the `__contains__` dunder method, which is implemented on strings, +arrays, and dictionaries — and can be implemented on your own classes too. + +## Ternary conditional expression + +``` +let result = a < b ? -1 : a > b ? 1 : 0 +``` + +This is the pattern used in +[`map_filter_sort.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/map_filter_sort.ar) to write a comparator +function in one line. + +## String concatenation + +`+` also works on strings: + +``` +term.log("hello " + "world") +``` + +## Ranges + +`a until b` (as seen in +[`range_test.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/range_test.ar)) produces an iterable range, most +commonly used with `for`: + +``` +for (x in 0 until 1e7) do + ... +``` + +See [`for` loops](../control-flow/for/README.md) for more on ranges and the built-in +`range()` function. + +## Unary operators + +``` +-x # negation +!x # logical not +``` + +## Next + +- [Control flow](../control-flow/README.md) \ No newline at end of file diff --git a/language/strings/README.md b/language/strings/README.md new file mode 100644 index 0000000..ab53a45 --- /dev/null +++ b/language/strings/README.md @@ -0,0 +1,118 @@ +# Strings + +Strings can be written with single or double quotes: + +``` +let a = "hello" +let b = 'hello' +``` + +## Template strings + +Backtick strings support interpolation via `$( ... )`, where anything inside the +parentheses is evaluated as Argon code: + +``` +let wow = "this is cool" + +term.log( + `hello world + +$( + do + term.log("wow") + return wow +)` +) +``` + +(from [`templates.ar`](../../../chloride/tests/templates.ar)) — this demonstrates that the +interpolated expression can be an entire `do` block with side effects, not just a simple +variable. + +A simpler, more common case: + +``` +let name = "world" +term.log(`Hello, $(name)!`) +``` + +Template strings also support unicode escapes like `\u1324`: + +``` +throw MyError(`hel\u1324lo`) +``` + +(from [`error_system.ar`](../../../chloride/tests/error_system.ar)) + +## Escape sequences + +Standard escapes are supported inside strings, including `\"`, `\\`, `\/`, `\b`, `\f`, +`\n`, `\r`, `\t`, and `\uXXXX` (see the JSON standard library's string parser at +[`stdlib/json/init.ar`](../../../chloride/stdlib/json/init.ar) for the full escape table +used when parsing JSON strings — the same escapes are valid in Argon string literals). + +## Indexing and slicing + +Strings support the same indexing/slicing syntax as arrays — see +[Arrays](../arrays/README.md#indexing-and-slicing) for the full slice syntax +(`s[1:]`, `s[:5]`, `s[::-1]`, negative indices, step values, etc). + +``` +let s = "hello" +term.log(s[0]) # "h" +term.log(s[-1]) # "o" +term.log(s[1:3]) # "el" +``` + +## Built-in string methods + +Strings support (at minimum) the following built-in methods and properties, based on the +interpreter's string type implementation: + +| Member | Description | +|---|---| +| `.length` | number of characters | +| `.upper()` | uppercase copy | +| `.lower()` | lowercase copy | +| `.title()` | title-cased copy | +| `.replace(old, new)` | replace all occurrences | +| `.split(sep)` | split into an array of strings | +| `.strip()` | strip surrounding whitespace | +| `.index_of(sub)` | index of a substring | +| `.chr()` / `.ord()` | character/code-point conversion | +| `in` | substring membership test (`"ell" in "hello"`) | +| `[start:stop:step]` | slicing | + +``` +let __starts_with__(s, prefix) = do + if (prefix.length > s.length) do + return false + return s[0:prefix.length] == prefix +``` + +(from [`stdlib/path/init.ar`](../../../chloride/stdlib/path/init.ar), showing `.length` and +slicing used together) + +## Comparing strings + +Strings support the usual comparison operators (`==`, `!=`, `<`, `<=`, `>`, `>=`), which +compare lexicographically. + +## Joining strings + +Arrays of strings can be joined with `.join(sep)` — see +[Arrays](../arrays/README.md#join). + +## Converting to a string + +Every object has a `__string__()` method used for display/printing, and calling +`string(value)` converts any value to its string form: + +``` +term.log(string(123)) # "123" +``` + +## Next + +- [Arrays](../arrays/README.md) \ No newline at end of file diff --git a/language/variables/README.md b/language/variables/README.md new file mode 100644 index 0000000..b5e782c --- /dev/null +++ b/language/variables/README.md @@ -0,0 +1,89 @@ +# Variables + +Declare a variable with `let`: + +``` +let x = 10 +let name = "chloride" +let pi = 3.14159 +``` + +Once declared, you can reassign without `let`: + +``` +let x = 10 +x = 20 +x = x + 1 +``` + +Argon also has compound assignment operators: + +``` +let i = 0 +i += 1 # i = i + 1 +i -= 1 +i *= 2 +i /= 2 +``` + +(See [`iteration.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/iteration.ar) in the test suite for `+=` used +in a loop counter.) + +## Dynamic typing + +Argon is dynamically typed — a variable can hold any type, and the type isn't declared up +front: + +``` +let value = 10 +value = "now I'm a string" +value = [1, 2, 3] +``` + +## `null` + +The absence of a value is `null`: + +``` +let x = null +``` + +## Everything is an object + +Unlike some older/simpler interpreters, in Chloride **every value is an object** — numbers, +strings, booleans, functions, and `null` all have an underlying type and can have methods +called on them: + +``` +term.log((5).__string__()) +term.log("hi".length) +``` + +You can find a value's type with the built-in `type()` function: + +``` +term.log(type(5)) # number +term.log(type("hi")) # string +term.log(type([1,2])) # array +``` + +and check whether something is an instance of a type (or class) with `is_instance`: + +``` +term.log(is_instance(5, number)) # true +term.log(is_instance("hi", string)) # true +``` + +## Scope + +Variables declared with `let` are scoped to the block/function they're declared in. +Functions form closures over the scope they were defined in — see +[Functions](../functions/README.md) for details, and +[`loop_continue_and_break_test.ar`](https://git.wbell.dev/Open-Argon/Chloride/src/commit/8e1ee06b88200ab112cf34e49efe969030ea435c/tests/loop_continue_and_break_test.ar) +for an example of a nested function (`g`) calling an outer function (`f`) that itself +closes over local variables. + +## Next + +- [Operators](../operators/README.md) +- [Control flow](../control-flow/README.md) \ No newline at end of file diff --git a/spec/README.md b/spec/README.md deleted file mode 100644 index 7e1228b..0000000 --- a/spec/README.md +++ /dev/null @@ -1,119 +0,0 @@ -# ARGON 3 BETA 1.0 - -This is the specification on how to create an Argon interpreter. - -This specification should be used as a guildline, and is subject to change for later versions. - -Later updates for Argon 3 should be backwards compatable (where possible) with code designed for older versions -of the interpreter. - -The Argon 3 interpreter is not intentionally designed to understand code written for Argon 1 and/or 2, but may -follow some design patterns found in those interpreters. - -Argon 3 is a programming language primarily designed for maths computation, it is designed to use techniques and -rules set in maths. It's designed to be easy for mathematicians to write and understand algorithms in. -(e.g. `f(x) = x^2` to set a function) - -reused variables, and infomation for use in understanding the pseudo REGEX: - -- NAME = [a-zA-Z][a-za-z0-9]\* -- spaces used in the pseudo REGEX should be taken as 1 or many spaces. -- there can be 0 or more spaces around any of the pseudo REGEX - - -## set variable - -`(let or nothing) {NAME} = {ANY}` - -let variables will set variables. at the end of a opperation (e.g. if, while, or function) drop the -variable stack. - - -having no verb at the start suggests the program would like to edit a variables from a different stack. -if there is no variable found in the other stacks, then it sets one in the current stack as a let. - -example: -```javascript -let x = 10 -y = 20 -let z = x + y -``` - -setting variables returns the value, which can be used. - -example: - -```javascript -if ((x = 10) > 5) do - term.log(x, 'is bigger than 5') -``` - - -## functions - -`(let or nothing) {NAME}({PARAMS}) = {CODE}` - -the starting verb follows the rules set by [set variable](#set-variable). - -function can be called by using its name followed by brackets with the params. - -example: - -```javascript -let f(x) = x^2 + 2*x + 1 -term.log('f(10) =', f(10)) -``` - -output: - -```javascript -f(10) = 121 -``` - -if the function does not return, then the value returned is `null` - -## wrap - -``` -do - {CODE} -``` - -a wrap encloses code indented in after the word `do` its used to create a new scope, so variables set from -inside the wraps scope are deleted once the wrap is finished. - -example: - -```javascript -let name = null - -do - name = input('name: ') - let age = input('age: ') - log('you are', age, 'years old!') - -term.log('hello', name) -term.log('we do not know your age anymore because it got deleted when the wrap finished.') -``` - -A wrap, unless specificifed otherwise, can have a return value. This value can be used as the value of the wrap. - -example: - -```javascript -let password = do - let password = passwordInput("set password: ") - while (password.length < 8) do - term.log("password must be longer then 8 characters!") - password = passwordInput("set password: ") - return password - -term.log("your password is", password) -``` - -If the wrap does not take a return value, then the wrap passes the return value back to a parent wrap. - -## Comments -`#{COMMENT}` - -Comments allow the programmer to write a message into their code, without the message being processed by the computer. \ No newline at end of file diff --git a/variable-and-function-assignment/README.md b/variable-and-function-assignment/README.md deleted file mode 100644 index 210dd23..0000000 --- a/variable-and-function-assignment/README.md +++ /dev/null @@ -1,79 +0,0 @@ -# Variable Assignment - -To define a variable in Argon, you can use either the `let` keyword or simply assign a value to a variable name without using `let`. Here's how you can define a variable using both methods: - -Method 1: Using the `let` keyword - -To define a variable using the `let` keyword, you need to specify the variable name and its initial value. Here's an example: -```javascript -let x = 10 -``` - -In this example, we define a variable named `x` and initialise it with a value of `10`. The `let` keyword tells the Argon interpreter to define the variable on the current stack. If `x` already exists in the current stack, then it will throw a `Name Error` of `Name Error: variable "x" already exists`. - -Method 2: Assigning a value to a variable name - -You can also define a variable by simply assigning a value to a variable name without using `let`. Here's an example: -```javascript -x = 10 -``` - -In this example, we define a variable named `x` and initialise it with a value of `10`. If the variable does not exist on any of the stacks below the current, then it will be created on the current stack. If the variable already exists on any of the stacks below the current, then it will be replaced on the highest stack that contains the variable. - -It's worth noting that if you define a variable without using let, it will be created on the current stack, and if you define a variable using let, it will also be created on the current stack but won't be replaced on a lower stack. This means that variables defined with let are only accessible within the current scope and cannot be accessed from nested scopes. - -Here's an example that demonstrates this: -```javascript -let x = 10 - -do - let x = 20 - term.log(x) # Output: 20 - -term.log(x) # Output: 10 -``` - -In this example, we define a variable `x` with a value of `10` using `let`. We then create a new block using the `do` keyword, which creates a new scope. Within this scope, we define a new variable `x` with a value of `20` using let. When we log the value of `x` using `term.log` within the scope, we get the value `20`. However, when we log the value of `x` outside of the scope, we get the value `10`, which is the value of the `x` variable defined in the outer scope. This demonstrates how variables defined with `let` are only accessible within the current scope and cannot be accessed from nested scopes. - -```javascript -let x = 10 - -do - x = 20 - term.log(x) # Output: 20 - -term.log(x) # Output: 20 -``` - -In this example, we define a variable `x` with a value of `10` using `let`. We then create a new block using the `do` keyword, which creates a new scope. Within this scope, we re-assign the value of `x` to `20` without using `let`. When we log the value of `x` using `term.log` within the scope, we get the value `20`. When we log the value of x outside of the scope, we also get the value `20`, because the re-assignment of `x` in the nested scope affects the value of `x` in the outer scope. This demonstrates how re-assigning a variable without using let will replace the value of the variable in the current scope, and will also affect the value of the variable in any enclosing scopes. - -# Function Assignment - -`let` works exactly the same way as it does in variable assignment. - -```javascript -let square(x) = do - return x * x -``` -In this example, we define a function named `square` with a parameter `x`. The function body uses a `do` block to group multiple statements together, and then returns the result of `x * x`. Note that the `return` keyword is used to indicate that the value of the expression `x * x` is the result of the function. This syntax is useful when you need to group multiple statements together or do some additional processing before returning a result. - -```javascript -let square(x) = x * x -``` - -In this example, we define a function named `square` with a parameter `x`. The function body simply multiplies `x` by itself and returns the result. This syntax is more concise and easier to read when you have a simple expression that can be evaluated in a single line. -you can define an anonymous function using a syntax similar to defining a named function, but without providing a name. Here's an example: -```javascript -let square = (x) = x * x -``` - -In this example, we define an anonymous function that takes one parameter `x` and returns the result of `x * x`. We store this function in a variable named `square`. - -Anonymous functions are useful when you need to define a small, one-off function that you don't need to use again later in your code. They can be passed as arguments to other functions, assigned to variables, and used wherever you would use a named function. - -Both of these ways will work the same way when you call the function with an argument. For example, if you call the function `square` with an argument of `4`, you will get a result of `16`: -```javascript -let result = square(4) -term.log(result) # Output: 16 -``` -In this example, we call the square function with an argument of `5` and store the result in a variable named `result`. We log the value of `result` to the console using `term.log`. \ No newline at end of file