Showing preview only (1,790K chars total). Download the full file or copy to clipboard to get everything.
Repository: MasterQ32/LoLa
Branch: master
Commit: 1bcce682478f
Files: 131
Total size: 11.9 MB
Directory structure:
gitextract_k560vkz5/
├── .envrc
├── .gitattributes
├── .github/
│ └── workflows/
│ ├── build.yml
│ └── website.yml.disabled
├── .gitignore
├── LICENSE
├── README.md
├── benchmarks/
│ ├── code/
│ │ ├── array-concat.lola
│ │ ├── array-creation.lola
│ │ ├── array-deep-copy.lola
│ │ ├── array-modification.lola
│ │ ├── basic-execution.lola
│ │ ├── cow-calls.lola
│ │ ├── function-call.lola
│ │ └── string-concat.lola
│ ├── data/
│ │ ├── .keep
│ │ ├── array-concat-ReleaseFast.csv
│ │ ├── array-concat-ReleaseSafe.csv
│ │ ├── array-concat-ReleaseSmall.csv
│ │ ├── array-creation-ReleaseFast.csv
│ │ ├── array-creation-ReleaseSafe.csv
│ │ ├── array-creation-ReleaseSmall.csv
│ │ ├── array-deep-copy-ReleaseFast.csv
│ │ ├── array-deep-copy-ReleaseSafe.csv
│ │ ├── array-deep-copy-ReleaseSmall.csv
│ │ ├── array-modification-ReleaseFast.csv
│ │ ├── array-modification-ReleaseSafe.csv
│ │ ├── array-modification-ReleaseSmall.csv
│ │ ├── basic-execution-ReleaseFast.csv
│ │ ├── basic-execution-ReleaseSafe.csv
│ │ ├── basic-execution-ReleaseSmall.csv
│ │ ├── cow-calls-ReleaseFast.csv
│ │ ├── cow-calls-ReleaseSafe.csv
│ │ ├── cow-calls-ReleaseSmall.csv
│ │ ├── function-call-ReleaseFast.csv
│ │ ├── function-call-ReleaseSafe.csv
│ │ ├── function-call-ReleaseSmall.csv
│ │ ├── string-concat-ReleaseFast.csv
│ │ ├── string-concat-ReleaseSafe.csv
│ │ └── string-concat-ReleaseSmall.csv
│ └── visualization/
│ ├── .keep
│ └── index.htm
├── build.zig
├── build.zig.zon
├── design/
│ ├── Lola.psb
│ ├── README.md
│ ├── benchmark.xcf
│ ├── face-only.xcf
│ └── logo.xcf
├── develop.lola
├── documentation/
│ ├── LoLa.md
│ ├── README.md
│ ├── checklist.txt
│ ├── ir.md
│ ├── modules.md
│ ├── runtime-library.md
│ ├── standard-library.md
│ └── zig-api.md
├── examples/
│ ├── host/
│ │ ├── minimal-host/
│ │ │ └── main.zig
│ │ ├── multi-environment/
│ │ │ ├── client-a.lola
│ │ │ ├── client-b.lola
│ │ │ ├── main.zig
│ │ │ └── server.lola
│ │ └── serialization/
│ │ └── main.zig
│ └── lola/
│ ├── README.md
│ ├── bubble-sort.lola
│ ├── fib-iterative.lola
│ ├── forth.lola
│ ├── game-code.lola
│ ├── global-setter.lola
│ ├── hello-world.lola
│ ├── reverse-array.lola
│ ├── stupid-bench.lola
│ └── sum-of-array.lola
├── src/
│ ├── README.md
│ ├── benchmark/
│ │ ├── perf.zig
│ │ └── render.zig
│ ├── frontend/
│ │ └── main.zig
│ ├── library/
│ │ ├── common/
│ │ │ ├── CompileUnit.zig
│ │ │ ├── Decoder.zig
│ │ │ ├── disassembler.zig
│ │ │ ├── interface.zig
│ │ │ ├── ir.zig
│ │ │ └── utility.zig
│ │ ├── compiler/
│ │ │ ├── analysis.zig
│ │ │ ├── ast.zig
│ │ │ ├── code-writer.zig
│ │ │ ├── codegen.zig
│ │ │ ├── diagnostics.zig
│ │ │ ├── location.zig
│ │ │ ├── parser.zig
│ │ │ ├── scope.zig
│ │ │ ├── string-escaping.zig
│ │ │ ├── tokenizer.zig
│ │ │ └── typeset.zig
│ │ ├── libraries/
│ │ │ ├── libs.zig
│ │ │ ├── runtime.zig
│ │ │ └── stdlib.zig
│ │ ├── main.zig
│ │ ├── runtime/
│ │ │ ├── Environment.zig
│ │ │ ├── environmentmap.zig
│ │ │ ├── objects.zig
│ │ │ ├── value.zig
│ │ │ └── vm.zig
│ │ ├── test/
│ │ │ ├── behaviour-with-stdlib.lola
│ │ │ ├── behaviour.lola
│ │ │ ├── compiler.lola
│ │ │ ├── empty.lola
│ │ │ ├── global-return.lola
│ │ │ ├── runtime.lola
│ │ │ └── stdlib.lola
│ │ └── test.zig
│ ├── tools/
│ │ └── render-md-page.zig
│ └── wasm-compiler/
│ └── main.zig
└── website/
├── .gitignore
├── documentation.css
├── fonts/
│ ├── PT_Sans/
│ │ └── OFL.txt
│ └── Source_Code_Pro/
│ └── OFL.txt
├── img/
│ └── .gitignore
├── index.htm
├── libs/
│ ├── ace.js
│ ├── mode-javascript.js
│ ├── theme-vibrant_ink.js
│ ├── worker-javascript.js
│ ├── xterm-addon-fit.js
│ ├── xterm.css
│ └── xterm.js
├── playground.css
├── playground.htm
├── playground.js
└── style.css
================================================
FILE CONTENTS
================================================
================================================
FILE: .envrc
================================================
use flake
================================================
FILE: .gitattributes
================================================
*.zig text=auto eol=lf
================================================
FILE: .github/workflows/build.yml
================================================
name: Build
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Setup Zig
uses: mlugg/setup-zig@v2
with:
version: 0.15.2
- name: Build toolchain
run: |
zig build
- name: Build test suite
run: |
zig build test
- name: Build examples
run: |
zig build examples
================================================
FILE: .github/workflows/website.yml.disabled
================================================
name: Render Website
on:
push:
branch: none # disabled for now
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: 'recursive'
- name: Setup Zig
uses: goto-bus-stop/setup-zig@v2
with:
version: master
- name: Render website
run: |
zig build -Denable-website "-Dversion=$(git describe --tags || git rev-parse --short HEAD)" website
- name: Deploy to Server
uses: easingthemes/ssh-deploy@v2.1.1
env:
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_PRIVKEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "website/"
REMOTE_HOST: ${{ secrets.WEBSITE_DEPLOY_HOST }}
REMOTE_USER: ${{ secrets.WEBSITE_DEPLOY_USER }}
TARGET: ${{ secrets.WEBSITE_DEPLOY_FOLDER }}
================================================
FILE: .gitignore
================================================
# Custom Items
*.lm
.zig-cache
scratchpad
docs/
debug/
release/
zig-out/
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2019-2020 Felix Queißner
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# LoLa Programming Language

LoLa is a small programming language meant to be embedded into games to be programmed by the players. The compiler and runtime are implemented in Zig and C++.
## Short Example
```js
var list = [ "Hello", "World" ];
for(text in list) {
Print(text);
}
```
You can find more examples in the [examples](examples/lola) folder.
## Why LoLa when there is *X*?
LoLa isn't meant to be your next best day-to-day scripting language. Its design is focused on embedding the language in environments where the users want/need/should write some small scripts like games or scriptable applications. In most script languages, you as a script host don't have control over the execution time of the scripts you're executing. LoLa protects you against programming errors like endless loops and such:
### Controlled Execution Environment
Every script invocation gets a limit of instructions it might execute. When either this limit is reached or the script yields for other reasons (asynchronous functions), the execution is returned to the host.
This means, you can execute the following script "in parallel" to your application main loop without blocking your application *and* without requiring complex multithreading setups:
```js
var timer = 0;
while(true) {
Print("Script running for ", timer, " seconds.");
timer += 1;
Sleep(1.0);
}
```
### Native Asynchronous Design
LoLa features both synchronous and asynchronous host functions. Synchronous host function calls are short-lived and will be executed in-place. Asynchronous functions, in contrast, will be executed multiple times until they yield a value. When they don't yield a value, control will be returned to the script host.
This script will not exhaust the instruction limit, but will only increment the counter, then return control back to the host:
```js
var counter = 0;
while(true) {
counter += 1;
Yield();
}
```
This behaviour can be utilized to wait for certain events in the host environment, for example to react to key presses, a script could look like this:
```js
while(true) {
var input = WaitForKey();
if(input == " ") {
Print("Space was pressed!");
}
}
```
*Note that the current implementation is not thread-safe, but requires to use the limited execution for running scripts in parallel.*
### Native "RPC" Design
LoLa also allows executing multiple scripts on the same *environment*, meaning that you can easily create cross-script communications:
```js
// script a:
var buffer;
function Set(val) { buffer = val; }
function Get() { return val; }
// script b:
// GetBuffer() returns a object referencing a environment for "script a"
var buffer = GetBuffer();
buffer.Set("Hello, World!");
// script c:
// GetBuffer() returns a object referencing a environment for "script a"
var buffer = GetBuffer();
Print("Buffer contains: ", buffer.Get());
```
With a fitting network stack and library, this can even be utilized cross-computer.
This example implements a small chat client and server that could work with LoLa RPC capabilities:
```js
// Chat client implementation:
var server = Connect("lola-rpc://random-projects.net/chat");
if(server == void) {
Print("Could not connect to chat server!");
Exit(1);
}
while(true) {
var list = server.GetMessages(GetUser());
for(msg in list) {
Print("< ", msg);
}
Print("> ");
var msg = ReadLine();
if(msg == void)
break;
if(msg == "")
continue;
server.Send(GetUser(), msg);
}
```
```js
// Chat server implementation
var messages = CreateDictionary();
function Send(user, msg)
{
for(other in messages.GetKeys())
{
if(other != user) {
var log = messages.Get(other);
if(log != void) {
log = log ++ [ user + ": " + msg ];
} else {
log = [];
}
messages.Set(other, log);
}
}
}
function GetMessages(user)
{
var log = messages.Get(user);
if(log != void) {
messages.Set(user, []);
return log;
} else {
return [];
}
}
```
### Serializable State
As LoLa has no reference semantics except for objects, it is easy to understand and learn. It is also simple in its implementation and does not require a complex garbage collector or advanced programming knowledge. Each LoLa value can be serialized/deserialized into a sequence of bytes (only exception are object handles, those require some special attention), so saving the current state of a environment/vm to disk and loading it at a later point is a first-class supported use case.
This is especially useful for games where it is favourable to save your script state into a save game as well without having any drawbacks.
### Simple Error Handling
LoLa provides little to no in-language error handling, as it's not designed to be robust against user programming errors. Each error is passed to the host as a panic, so it can show the user that there was an error (like `OutOfMemory` or `TypeMismatch`).
In-language error handling is based on the dynamic typing: Functions that allow in-language error handling just return `void` instead of a actual return value or `true`/`false` for *success* or *failure*.
This allows simple error checking like this:
```js
var string = ReadFile("demo.data");
if(string != void) {
Print("File contained ", string);
}
```
This design decision was made with the idea in mind that most LoLa programmers won't write the next best security critical software, but just do a quick hack in game to reach their next item unlock.
### Smart compiler
As LoLa isn't the most complex language, the compiler can support the programmer. Even though the language has fully dynamic typing, the compiler can do some type checking at compile time already:
```js
// warning: Possible type mismatch detected: Expected number|string|array, found boolean
if(a < true) { }
```
Right now, this is only used for validating expressions, but it is planned to extend this behaviour to annotate variables as well, so even more type errors can be found during compile time.
Note that this is a fairly new feature, it does not catch all your type mismatches, but can prevent the obvious ones.
## Starting Points
To get familiar with LoLa, you can check out these starting points:
- [Documentation](documentation/README.md)
- [LoLa Examples](examples/lola/README.md)
- [Script Host Examples](examples/host)
When you want to contribute to the compiler, check out the following documents:
- [Source Code](src/)
- [Bison Grammar](src/library/compiler/grammar.yy)
- [Flex Tokenizer](src/library/compiler/yy.l)
- [Issue List](https://github.com/MasterQ32/LoLa/issues)
## Visual Studio Code Extension
If you want syntax highlighting in VSCode, you can install the [`lola-vscode`](https://github.com/MasterQ32/lola-vscode) extension.
Right now, it's not published in the gallery, so to install the extension, you have to sideload it. [See the VSCode documentation for this](https://vscode-docs.readthedocs.io/en/stable/extensions/install-extension/).
## Building
### Continous Integration
[](https://github.com/MasterQ32/LoLa/actions/workflows/build.yml) [](https://github.com/MasterQ32/LoLa/actions/workflows/website.yml)
### Requirements
- The [Zig Compiler](https://ziglang.org/) (Version 0.15.2 or compatible)
### Building
```sh
zig build
./zig-out/bin/lola
```
### Examples
To compile the host examples, you can use `zig build examples` to build all provided examples. These will be available in `./zig-out/bin` then.
### Running the test suite
When you change things in the compiler or VM implementation, run the test suite:
```sh
zig build test
```
This will execute all zig tests, and also runs a set of predefined tests within the [`src/test/`](src/test/) folder. These tests will verify that the compiler and language runtime behave correctly.
### Building the website
The website generator is gated behind `-Denable-website` which removes a lot of dependencies for people not wanting to render a new version of the website.
If you still want to update/change the website or documentation, use the following command:
```sh
zig build -Denable-website "-Dversion=$(git describe --tags || git rev-parse --short HEAD)" website
```
It will depend on [koino](https://github.com/kivikakk/koino), which is included as a git submodule. Adding new pages to the documentation is done by modifying the `menu_items` array in `src/tools/render-md-page.zig`.
================================================
FILE: benchmarks/code/array-concat.lola
================================================
var i = 0;
while(i < 100000) {
var foo = [ 1,2,3 ] + [ 4,5,6 ] + [ 7,8,9 ];
i += 1;
}
================================================
FILE: benchmarks/code/array-creation.lola
================================================
var i = 0;
while(i < 100000) {
var array = [ 1, 2, 3 ];
var tmp = array[0];
array[0] = array[1];
array[1] = array[2];
array[2] = tmp;
i += 1;
}
================================================
FILE: benchmarks/code/array-deep-copy.lola
================================================
var deep_array = [
"Hello",
"This",
[
"deep",
"deeper",
"deepest",
],
[
[
[
"nesting"
],
"is"
],
"fun"
],
[[[ 1,2,3,4 ]]],
];
var i = 0;
while(i < 100000) {
var copy = deep_array;
i += 1;
}
================================================
FILE: benchmarks/code/array-modification.lola
================================================
var array = [ 1, 2, 3 ];
var i = 0;
while(i < 100000) {
var tmp = array[0];
array[0] = array[1];
array[1] = array[2];
array[2] = tmp;
i += 1;
}
================================================
FILE: benchmarks/code/basic-execution.lola
================================================
var i = 0;
while(i < 100000) {
i += 1;
}
================================================
FILE: benchmarks/code/cow-calls.lola
================================================
// same as function-call.lola, but tests reduced copies on function calls
function Foo(a, b, c) {
Bar(a, b, c);
Bar(a, b, c);
}
function Bar(a, b, c) {
Bam(a, b, c);
Bam(a, b, c);
}
function Bam(a, b, c) {
Baz(a, b, c);
Baz(a, b, c);
}
function Baz(a, b, c) {
// NOP
}
var array = [ 1, 2, 3 ];
var i = 0;
while(i < 100000) {
Foo("Hello", array, "World!");
i += 1;
}
================================================
FILE: benchmarks/code/function-call.lola
================================================
function Foo(a) {
Bar(2 * a);
Bar(2 * a + 1);
}
function Bar(a) {
Bam(2 * a);
Bam(2 * a + 1);
}
function Bam(a) {
Baz();
Baz();
}
function Baz() {
// NOP
}
var i = 0;
while(i < 100000) {
Foo(i);
i += 1;
}
================================================
FILE: benchmarks/code/string-concat.lola
================================================
var i = 0;
while(i < 100000) {
var foo = "a" + "hello" + "bar";
i += 1;
}
================================================
FILE: benchmarks/data/.keep
================================================
================================================
FILE: benchmarks/data/array-concat-ReleaseFast.csv
================================================
time;compile;setup;run
2022-03-14 17:50:57;92471;34921;909061563
2022-03-14 17:51:15;71657;23258;854263712
2022-03-15 09:19:21;106996;26679;924645701
================================================
FILE: benchmarks/data/array-concat-ReleaseSafe.csv
================================================
time;compile;setup;run
2022-03-14 17:50:51;96382;36737;1036393973
2022-03-14 17:51:09;225659;77385;1070012044
2022-03-15 09:19:14;143104;44279;1214704571
================================================
FILE: benchmarks/data/array-concat-ReleaseSmall.csv
================================================
time;compile;setup;run
2022-03-14 17:51:02;144503;43023;1007786085
2022-03-14 17:51:19;110001;24933;1000317886
2022-03-15 09:19:25;442373;86323;1012227965
2022-08-01 07:36:18;141640;40508;1342166897
2022-08-01 07:37:00;98757;30730;1306766120
2022-08-01 07:37:11;93029;27658;1341309781
2022-08-01 07:37:35;92541;27727;1402401383
2022-08-01 07:37:47;240047;77245;1332377442
================================================
FILE: benchmarks/data/array-creation-ReleaseFast.csv
================================================
time;compile;setup;run
2022-03-14 17:50:57;92331;17321;315187057
2022-03-14 17:51:15;80737;15226;304063959
2022-03-15 09:19:21;137726;23746;317808793
================================================
FILE: benchmarks/data/array-creation-ReleaseSafe.csv
================================================
time;compile;setup;run
2022-03-14 17:50:51;103645;18718;451733786
2022-03-14 17:51:09;91982;18159;431864521
2022-03-15 09:19:14;146945;26749;522902601
================================================
FILE: benchmarks/data/array-creation-ReleaseSmall.csv
================================================
time;compile;setup;run
2022-03-14 17:51:02;99315;15994;469843174
2022-03-14 17:51:19;98687;16343;451050382
2022-03-15 09:19:25;114469;17251;462058962
2022-08-01 07:36:18;182357;26540;535090998
2022-08-01 07:37:00;117195;17600;515562770
2022-08-01 07:37:11;107138;17460;521132326
2022-08-01 07:37:35;111747;19975;556306589
2022-08-01 07:37:47;107906;15994;515780206
================================================
FILE: benchmarks/data/array-deep-copy-ReleaseFast.csv
================================================
time;compile;setup;run
2022-03-14 17:50:57;104064;20883;186939577
2022-03-14 17:51:15;84579;19136;169029798
2022-03-15 09:19:21;127251;26819;197526446
================================================
FILE: benchmarks/data/array-deep-copy-ReleaseSafe.csv
================================================
time;compile;setup;run
2022-03-14 17:50:51;153583;24305;252028288
2022-03-14 17:51:09;100851;22699;248200112
2022-03-15 09:19:14;170970;38902;272295801
================================================
FILE: benchmarks/data/array-deep-copy-ReleaseSmall.csv
================================================
time;compile;setup;run
2022-03-14 17:51:02;158262;22978;253425128
2022-03-14 17:51:19;96871;18648;222294338
2022-03-15 09:19:25;116146;21860;233939081
2022-08-01 07:36:18;270638;39670;306097835
2022-08-01 07:37:00;126833;23747;268443990
2022-08-01 07:37:11;114890;23816;265302988
2022-08-01 07:37:35;130465;25352;277489447
2022-08-01 07:37:47;112306;23467;267456085
================================================
FILE: benchmarks/data/array-modification-ReleaseFast.csv
================================================
time;compile;setup;run
2022-03-14 17:50:57;93518;19975;457690885
2022-03-14 17:51:15;124109;20394;465255466
2022-03-15 09:19:21;111536;21931;468068575
================================================
FILE: benchmarks/data/array-modification-ReleaseSafe.csv
================================================
time;compile;setup;run
2022-03-14 17:50:51;115099;22839;619569147
2022-03-14 17:51:09;115519;22838;599702187
2022-03-15 09:19:14;113911;25282;644005590
================================================
FILE: benchmarks/data/array-modification-ReleaseSmall.csv
================================================
time;compile;setup;run
2022-03-14 17:51:02;100642;18927;584032658
2022-03-14 17:51:19;99943;19067;570154854
2022-03-15 09:19:25;124946;24235;609659804
2022-08-01 07:36:18;182358;29124;723839731
2022-08-01 07:37:00;135354;23746;711914022
2022-08-01 07:37:11;128161;27238;767648862
2022-08-01 07:37:35;139614;24654;713072720
2022-08-01 07:37:47;129277;23328;717505318
================================================
FILE: benchmarks/data/basic-execution-ReleaseFast.csv
================================================
time;compile;setup;run
2022-03-14 17:50:57;77385;24236;13819695
2022-03-14 17:51:15;69423;18508;12575531
2022-03-15 09:19:21;91561;39530;14511481
================================================
FILE: benchmarks/data/basic-execution-ReleaseSafe.csv
================================================
time;compile;setup;run
2022-03-14 17:50:51;80318;21930;33102079
2022-03-14 17:51:09;78712;22350;35174987
2022-03-15 09:19:14;121872;34921;40839820
================================================
FILE: benchmarks/data/basic-execution-ReleaseSmall.csv
================================================
time;compile;setup;run
2022-03-14 17:51:02;82554;23397;42498963
2022-03-14 17:51:19;64883;18439;39820105
2022-03-15 09:19:25;106508;21650;41080944
2022-08-01 07:36:18;87302;24585;42057042
2022-08-01 07:37:00;90585;22908;38071370
2022-08-01 07:37:11;93449;24235;36609717
2022-08-01 07:37:35;85067;25772;37940208
2022-08-01 07:37:47;76337;23817;36616981
================================================
FILE: benchmarks/data/cow-calls-ReleaseFast.csv
================================================
time;compile;setup;run
2022-03-14 17:50:57;129697;18717;1587186196
2022-03-14 17:51:15;86255;15155;1544207489
2022-03-15 09:19:21;137657;25562;1582958407
================================================
FILE: benchmarks/data/cow-calls-ReleaseSafe.csv
================================================
time;compile;setup;run
2022-03-14 17:50:51;148274;20534;2034523291
2022-03-14 17:51:09;168947;31499;2019303824
2022-03-15 09:19:14;147365;21301;2190481150
================================================
FILE: benchmarks/data/cow-calls-ReleaseSmall.csv
================================================
time;compile;setup;run
2022-03-14 17:51:02;135423;17181;2045099458
2022-03-14 17:51:19;135703;17112;1971642214
2022-03-15 09:19:25;148343;19834;2010286841
2022-08-01 07:36:18;179634;22698;2560973536
2022-08-01 07:37:00;172440;19905;2363456217
2022-08-01 07:37:11;168040;24794;2601422852
2022-08-01 07:37:35;158402;22908;2547212294
2022-08-01 07:37:47;169785;20534;2363859356
================================================
FILE: benchmarks/data/function-call-ReleaseFast.csv
================================================
time;compile;setup;run
2022-03-14 17:50:57;107137;24934;643612795
2022-03-14 17:51:15;83391;20534;601739616
2022-03-15 09:19:21;101199;19975;674414714
================================================
FILE: benchmarks/data/function-call-ReleaseSafe.csv
================================================
time;compile;setup;run
2022-03-14 17:50:51;136960;30940;961271070
2022-03-14 17:51:09;102458;22420;873761139
2022-03-15 09:19:14;181028;28146;977370731
================================================
FILE: benchmarks/data/function-call-ReleaseSmall.csv
================================================
time;compile;setup;run
2022-03-14 17:51:02;109023;21511;963146676
2022-03-14 17:51:19;101550;20045;858196163
2022-03-15 09:19:25;113631;21511;905170047
2022-08-01 07:36:18;147297;24864;1092839652
2022-08-01 07:37:00;134167;23257;1045643460
2022-08-01 07:37:11;122084;24445;1064094541
2022-08-01 07:37:35;136053;24724;1125510903
2022-08-01 07:37:47;125716;22978;1052544445
================================================
FILE: benchmarks/data/string-concat-ReleaseFast.csv
================================================
time;compile;setup;run
2022-03-14 17:50:57;68096;18997;418526316
2022-03-14 17:51:15;74032;18648;401887800
2022-03-15 09:19:21;141358;38622;410641031
================================================
FILE: benchmarks/data/string-concat-ReleaseSafe.csv
================================================
time;compile;setup;run
2022-03-14 17:50:51;128928;34712;508247513
2022-03-14 17:51:09;75708;22210;494127218
2022-03-15 09:19:14;95891;31219;528157949
================================================
FILE: benchmarks/data/string-concat-ReleaseSmall.csv
================================================
time;compile;setup;run
2022-03-14 17:51:02;90096;20534;504607490
2022-03-14 17:51:19;73404;19067;480256677
2022-03-15 09:19:25;91492;33803;519769968
2022-08-01 07:36:18;94915;23327;658798382
2022-08-01 07:37:00;97570;22978;654418051
2022-08-01 07:37:11;89188;22839;676506152
2022-08-01 07:37:35;96033;24864;648460964
2022-08-01 07:37:47;94916;24654;650100440
================================================
FILE: benchmarks/visualization/.keep
================================================
================================================
FILE: benchmarks/visualization/index.htm
================================================
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
* {
box-sizing: border-box;
}
body {
margin-left: 1em;
margin-right: 1em;
}
h1,
h2,
h3 {
text-align: center;
}
table {
width: 100%;
border-collapse: collapse;
}
td {
width: 30%;
}
</style>
</head>
<body>
<h1>LoLa Benchmark Suite</h1>
<h2>Execution</h2>
<table>
<tr>
<th>Safe</th>
<th>Small</th>
<th>Fast</th>
</tr>
<tr>
<td><img src="run-ReleaseSafe.svg" /></td>
<td><img src="run-ReleaseSmall.svg" /></td>
<td><img src="run-ReleaseFast.svg" /></td>
</tr>
</table>
<h2>Compilation</h2>
<table>
<tr>
<th>Safe</th>
<th>Small</th>
<th>Fast</th>
</tr>
<tr>
<td><img src="compile-ReleaseSafe.svg" /></td>
<td><img src="compile-ReleaseSmall.svg" /></td>
<td><img src="compile-ReleaseFast.svg" /></td>
</tr>
</table>
<h2>Environment Setup</h2>
<table>
<tr>
<th>Safe</th>
<th>Small</th>
<th>Fast</th>
</tr>
<tr>
<td><img src="setup-ReleaseSafe.svg" /></td>
<td><img src="setup-ReleaseSmall.svg" /></td>
<td><img src="setup-ReleaseFast.svg" /></td>
</tr>
</table>
</body>
</html>
================================================
FILE: build.zig
================================================
const std = @import("std");
const builtin = @import("builtin");
const Build = std.Build;
fn sdkPath(comptime suffix: []const u8) []const u8 {
if (suffix[0] != '/') @compileError("sdkPath requires an absolute path!");
return comptime blk: {
const root_dir = std.fs.path.dirname(@src().file) orelse ".";
break :blk root_dir ++ suffix;
};
}
const Example = struct {
name: []const u8,
path: []const u8,
};
const examples = [_]Example{
.{
.name = "minimal-host",
.path = "examples/host/minimal-host/main.zig",
},
.{
.name = "multi-environment",
.path = "examples/host/multi-environment/main.zig",
},
.{
.name = "serialization",
.path = "examples/host/serialization/main.zig",
},
};
pub fn build(b: *Build) !void {
const version_tag = b.option([]const u8, "version", "Sets the version displayed in the docs and for `lola version`");
const optimize = b.standardOptimizeOption(.{});
const target = b.standardTargetOptions(.{});
const mod_args = b.dependency("args", .{}).module("args");
// const mod_interface = b.dependency("interface", .{}).module("interface.zig");
const mod_any_pointer = b.dependency("any_pointer", .{}).module("any-pointer");
const mod_lola = b.addModule("lola", .{
.root_source_file = b.path("src/library/main.zig"),
.imports = &.{
// .{ .name = "interface", .module = mod_interface },
.{ .name = "any-pointer", .module = mod_any_pointer },
},
});
const build_options = b.addOptions();
build_options.addOption([]const u8, "version", version_tag orelse "development");
const exe_mod = b.addModule("exe_mod", .{
.root_source_file = b.path("src/frontend/main.zig"),
.optimize = optimize,
.target = target,
// .sanitize_thread = true,
// .stack_check = true,
});
exe_mod.addImport("lola", mod_lola);
exe_mod.addImport("args", mod_args);
exe_mod.addImport("build_options", build_options.createModule());
const exe = b.addExecutable(.{
.name = "lola",
.root_module = exe_mod,
});
b.installArtifact(exe);
const benchmark_renderer_mod = b.createModule(.{
.root_source_file = b.path("src/benchmark/render.zig"),
.optimize = optimize,
.target = b.graph.host,
});
const benchmark_renderer = b.addExecutable(.{
.name = "benchmark-render",
.root_module = benchmark_renderer_mod,
});
b.installArtifact(benchmark_renderer);
{
const render_benchmark_step = b.step("render-benchmarks", "Runs the benchmark suite.");
const only_render_benchmark = b.addRunArtifact(benchmark_renderer);
only_render_benchmark.addDirectoryArg(b.path("benchmarks/data"));
only_render_benchmark.addDirectoryArg(b.path("benchmarks/visualization"));
render_benchmark_step.dependOn(&only_render_benchmark.step);
}
const benchmark_modes = [_]std.builtin.OptimizeMode{
.ReleaseSafe, .ReleaseFast, .ReleaseSmall,
};
const benchmark_step = b.step("benchmark", "Runs the benchmark suite.");
const render_benchmark = b.addRunArtifact(benchmark_renderer);
render_benchmark.addDirectoryArg(b.path("benchmarks/data"));
render_benchmark.addDirectoryArg(b.path("benchmarks/visualization"));
benchmark_step.dependOn(&render_benchmark.step);
for (benchmark_modes) |benchmark_mode| {
const benchmark_mod = b.createModule(.{
.root_source_file = b.path("src/benchmark/perf.zig"),
.optimize = benchmark_mode,
.target = b.graph.host,
});
benchmark_mod.addImport("lola", mod_lola);
const benchmark = b.addExecutable(.{
.name = b.fmt("benchmark-{s}", .{@tagName(benchmark_mode)}),
.root_module = benchmark_mod,
});
const run_benchmark = b.addRunArtifact(benchmark);
run_benchmark.addDirectoryArg(b.path("benchmarks/code"));
run_benchmark.addDirectoryArg(b.path("benchmarks/data"));
render_benchmark.step.dependOn(&run_benchmark.step);
}
const wasm_runtime_mod = b.createModule(.{
.root_source_file = b.path("src/wasm-compiler/main.zig"),
.target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }),
.optimize = .ReleaseSafe,
});
const wasm_runtime = b.addExecutable(.{
.name = "lola",
.root_module = wasm_runtime_mod,
});
wasm_runtime.entry = .disabled;
wasm_runtime.root_module.addImport("lola", mod_lola);
b.installArtifact(wasm_runtime);
const examples_step = b.step("examples", "Compiles all examples");
inline for (examples) |example| {
const example_exe_mod = b.createModule(.{
.root_source_file = b.path(example.path),
.optimize = optimize,
.target = target,
});
example_exe_mod.addImport("lola", mod_lola);
const example_exe = b.addExecutable(.{
.name = "example-" ++ example.name,
.root_module = example_exe_mod,
});
examples_step.dependOn(&b.addInstallArtifact(example_exe, .{}).step);
}
const main_tests_mod = b.createModule(.{
.root_source_file = b.path("src/library/test.zig"),
.optimize = optimize,
.target = b.graph.host,
});
main_tests_mod.addImport("any-pointer", mod_any_pointer);
const main_tests = b.addTest(.{
.root_module = main_tests_mod,
});
// main_tests.root_module.addImport("interface", mod_interface);
// main_tests.main_pkg_path = b.path(".");
const test_step = b.step("test", "Run test suite");
test_step.dependOn(&b.addRunArtifact(main_tests).step);
// Run compiler test suites
{
const prefix = "src/library/test/";
const behaviour_tests = b.addRunArtifact(exe);
behaviour_tests.addArg("run");
behaviour_tests.addArg("--no-stdlib"); // we don't want the behaviour tests to be run with any stdlib functions
behaviour_tests.addFileArg(b.path(prefix ++ "behaviour.lola"));
behaviour_tests.expectStdOutEqual("Behaviour test suite passed.\n");
test_step.dependOn(&behaviour_tests.step);
const stdib_test = b.addRunArtifact(exe);
stdib_test.addArg("run");
stdib_test.addFileArg(b.path(prefix ++ "stdlib.lola"));
stdib_test.expectStdOutEqual("Standard library test suite passed.\n");
test_step.dependOn(&stdib_test.step);
b.cache_root.handle.makeDir("tmp") catch |err| switch (err) {
error.PathAlreadyExists => {}, // nice
else => |e| return e,
};
const runlib_test = b.addRunArtifact(exe);
// execute in the .zig-cache directory so we have a "safe" playfield
// for file I/O
runlib_test.setCwd(.{ .cwd_relative = try b.cache_root.join(b.allocator, &.{"tmp"}) });
// `Exit(123)` is the last call in the runtime suite
runlib_test.expectExitCode(123);
runlib_test.expectStdOutEqual(
\\
\\1
\\1.2
\\[ ]
\\[ 1, 2 ]
\\truefalse
\\hello
\\Runtime library test suite passed.
\\
);
runlib_test.addArg("run");
runlib_test.addFileArg(b.path(prefix ++ "runtime.lola"));
test_step.dependOn(&runlib_test.step);
const emptyfile_test = b.addRunArtifact(exe);
emptyfile_test.addArg("run");
emptyfile_test.addFileArg(b.path(prefix ++ "empty.lola"));
emptyfile_test.expectStdOutEqual("");
test_step.dependOn(&emptyfile_test.step);
const globreturn_test = b.addRunArtifact(exe);
globreturn_test.addArg("run");
globreturn_test.addFileArg(b.path(prefix ++ "global-return.lola"));
globreturn_test.expectStdOutEqual("");
test_step.dependOn(&globreturn_test.step);
const extended_behaviour_test = b.addRunArtifact(exe);
extended_behaviour_test.addArg("run");
extended_behaviour_test.addFileArg(b.path(prefix ++ "behaviour-with-stdlib.lola"));
extended_behaviour_test.expectStdOutEqual("Extended behaviour test suite passed.\n");
test_step.dependOn(&extended_behaviour_test.step);
const compiler_test = b.addRunArtifact(exe);
compiler_test.addArg("compile");
compiler_test.addArg("--verify"); // verify should not emit a compiled module
compiler_test.addFileArg(b.path(prefix ++ "compiler.lola"));
compiler_test.expectStdOutEqual("");
test_step.dependOn(&compiler_test.step);
}
const run_cmd = b.addRunArtifact(exe);
if (b.args) |args| {
run_cmd.addArgs(args);
}
run_cmd.step.dependOn(b.getInstallStep());
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
// TODO: Re-enable website rendering
// /////////////////////////////////////////////////////////////////////////
// // Documentation and Website generation:
// // this is disabed by-default so we don't depend on any vcpkgs
// if (b.option(bool, "enable-website", "Enables website generation.") orelse false) {
// // Generates documentation and future files.
// const gen_website_step = b.step("website", "Generates the website and all required resources.");
// const md_renderer = b.addExecutable(.{
// .name = "markdown-md-page",
// .root_source_file = b.path("src/tools/render-md-page.zig"),
// });
// md_renderer.addModule("koini", pkgs.koino);
// try linkPcre(md_renderer);
// const render = b.addRunArtifact(md_renderer);
// render.addArg(version_tag orelse "development");
// gen_website_step.dependOn(&render.step);
// const copy_wasm_runtime = b.addSystemCommand(&[_][]const u8{
// "cp",
// });
// copy_wasm_runtime.addArtifactArg(wasm_runtime);
// copy_wasm_runtime.addArg("website/lola.wasm");
// gen_website_step.dependOn(©_wasm_runtime.step);
// var gen_docs_runner = b.addTest(pkgs.lola.source.path);
// // gen_docs_runner.emit_bin = .no_emit;
// gen_docs_runner.emit_asm = .no_emit;
// gen_docs_runner.emit_bin = .no_emit;
// gen_docs_runner.emit_docs = .{ .emit_to = "website/docs" };
// gen_docs_runner.emit_h = false;
// gen_docs_runner.emit_llvm_ir = .no_emit;
// for (pkgs.lola.dependencies.?) |dep| {
// gen_docs_runner.addPackage(dep);
// }
// gen_docs_runner.setBuildMode(optimize);
// gen_docs_runner.setMainPkgPath(".");
// gen_website_step.dependOn(&gen_docs_runner.step);
// // Only generates documentation
// const gen_docs_step = b.step("docs", "Generate the code documentation");
// gen_docs_step.dependOn(&gen_docs_runner.step);
// }
}
================================================
FILE: build.zig.zon
================================================
.{
.name = .lola,
.version = "0.1.0",
.fingerprint = 0xe0c26edfbdb0efcc,
.dependencies = .{
.args = .{
.url = "git+https://github.com/ikskuh/zig-args?ref=HEAD#e060ac80c244e9675471b6d213b22ddc83cc8f98",
.hash = "args-0.0.0-CiLiqo_RAADz2TiHUzG5-0Mk7IZHR-h1SZgUrb_k4c7d",
},
.any_pointer = .{
.url = "git+https://github.com/ikskuh/any-pointer.git#0f9725a324d48239d1d7e07b7f270b0a87be576a",
.hash = "N-V-__8AABtBAABwSNNZpjUoWFLYSZZXjKgv9hKsDuZq64Zk",
},
},
.paths = .{"."},
}
================================================
FILE: design/Lola.psb
================================================
[File too large to display: 10.2 MB]
================================================
FILE: design/README.md
================================================
# LoLa Design
The logo is not licenced under MIT licence!
Do not use this in anywhere without previous agreement with Felix Queißner (felix@ib-queissner.de)!
Logo design by [https://www.artbyluphalia.com/](www.artbyluphalia.com). Give her a visit!
================================================
FILE: develop.lola
================================================
var x = "a";
x[0] = true;
================================================
FILE: documentation/LoLa.md
================================================
# LoLa – Quick Reference
## Example
```js
var stack = CreateStack();
stack.Push(10);
stack.Push(20);
stack.Push(30);
function Operation(op)
{
if(op == "print") {
Print(stack.Pop());
}
if(op == "add") {
var lhs = stack.Pop();
var rhs = stack.Pop();
stack.Push(lhs + rhs);
}
if(op == "mul") {
var lhs = stack.Pop();
var rhs = stack.Pop();
stack.Push(lhs * rhs);
}
}
Operation("mul");
Operation("add");
Operation("print");
Print("Stack Length: ", stack.GetSize());
```
## Overview
- Allows script-style top-level code
- Dynamic typing
- Static scope
### Data Types
- Void (single value type, marks something "not existent")
- Boolean (Logic value, is either `true` or `false`)
- Number (IEEE754 binary64)
- String (ASCII-like or UTF-8 encoded string)
- Array (A ordered list of values, zero-indexed)
- Object (A thing that has methods which can be called)
- Struct (A collection of named fields)
### Syntax
#### Expressions
- literals
- number (`1.0`, `4.33`, `2`, …)
- string (`""`, `"hello"`, `"line\nfeed"`, …)
- boolean (`true`, `false`, …)
- struct (`[.x=1, .y=2]`, `[.name="LoLa"]`, …)
- variable access (`x`, `var`, `var_2`, …)
- array (`[]`, `[expr]`, `[expr, expr]`, …)
- array index (`expr[expr])`
- struct field access (`expr.field`)
- unary operation (`-expr`, `not expr`, …)
- binary operaton (`expr + expr`, `expr and expr`, …)
- function call (`f()`, `f(expr)`, `f(expr,expr)`, …)
- method call (`o.m()`, `o.m(expr)`, …)
- parenthesis (`(expr)`)
#### Operators
Operator precedence in the list low to high. A higher precedence means
that these operators *bind* more to the variables and will be applied
first.
**Binary:**
- `and`, `or`
- `==`, `!=`, `>=`, `<=`, `>`, `<`
- `+`, `-`
- `*`, `/`, `%`
**Unary:**
- `not`, `-`
#### Statements
- terminated by semicolon (`;`)
- allowed on top level
**Elements:**
- scope (`{ … }`)
- var declaration (`var x;`, `var x = expr;` …)
- function call (`f();`, …)
- assignment (`lval = rval;`, `lval[expr] = rval;`, `lval.field = rval;` …)
- for loop (`for(x in expr) { … }`)
- while loop (`while(expr) { … }`)
- condition (`if(expr) { … }`, `if(expr) { … } else { … }`)
- return (`return;`, `return expr;`)
#### Function Declarations
```js
function name()
{
…
}
function name(arg)
{
…
}
function name(arg1, arg2)
{
…
}
```
================================================
FILE: documentation/README.md
================================================
# The LoLa Programming Language
## Introduction
LoLa is a small programming language developed to be embedded in games. It's not meant as a scripting language to create games with but as language to be **programmed in the game by the player**.
The design goals of the language are:
- Easy to learn
- Small set of language features
- No complex features, there are only value types
- Exhaustible execution – Limit how long a certain script run at most in a single script call
## Hello World
```js
Print("Hello, World!");
```
As you can see, the *Hello World*-Program is quite short and expressive. The language itself uses a C-like syntax with semicolons at the end of a statement.
More [Examples](#Examples) can be found at the end of the document.
## Comments
LoLa provides only single-line comments:
```js
// This is a comment
Print("Hello"); // this is a statement, followed by a comment
```
Everything that is in a comment is ignored by the compiler. A comment is introduced by a double-slash (`//`) and is ended by a line feed character or the end of the file.
## Types
The language provides a small set of types data can have:
| Type | Description |
| --------- | ------------------------------------------------------------ |
| `void` | The `void` type can only have a single value (which is also `void`) and indicates the absence of a value. Functions that do not return something will return this. |
| `boolean` | A truth value, which is either `true` or `false`. This type is the result of comparisons and can be passed to conditionals. It is also the input to the [boolean algebra](https://en.wikipedia.org/wiki/Boolean_algebra) operators. |
| `number` | A [IEEE-754 binary64](https://en.wikipedia.org/wiki/Double-precision_floating-point_format) encoded real number. This is the basic type for all algebraic operations. |
| `string` | A [string](https://en.wikipedia.org/wiki/String_(computer_science)) in LoLa is a sequence of bytes, usually encodes text as [ASCII](https://en.wikipedia.org/wiki/ASCII) or [UTF-8](https://en.wikipedia.org/wiki/UTF-8). |
| `object` | An object is a thing that has an interface with callable methods. |
| `array` | An array is a sequence of arbitrary values. |
| `struct` | A struct is a collection of named fields, each holding an arbitrary value. |
## Literals
Literals provide a way to create a primitive value in the language. All of the types except `object` have a literal syntax:
| Type | Examples |
| --------- | ---------------------------------------------------------------------- |
| `void` | `void` (no other values are allowed) |
| `boolean` | `true`, `false` (no other values are allowed) |
| `number` | `0`, `1`, `0.0`, `10.0`, `0.25`, `13.37`, … |
| `string` | `"Hello, World!"`, `""`, `"One\nTwo\nThree"` |
| `array` | `[]`, `[1,2,3,4,5]`, `[true, false, void]` |
| `struct` | `[.x=0, .y=0]`, `[.name="LoLa", .version=1]`, `[.flag=true, .val=42]` |
LoLa also supports character literals. Character literals have the type `number` and will be equivalent to their unicode codepoint and might be written literally (`ö`) or with the same rules as string escapes (`\xF3`):
```js
0x20 == ' '
0x07 == '\a'
0x1F4A9 == '💩'
0xF3 == '\xF3'
0x1F4A9 == '\xf0\x9f\x92\xa9'
```
As LoLa doesn't enforce utf-8 encoding, all single-byte literals will be copied verbatim, allowing support for any 8-bit encoding. All literals that take up more than a single byte will be assumed utf-8 encoded.
### String Escape Sequence
As strings are contained in double quotes and don't allow to contain a line feed, one needs the possibility to escape those characters. For this, LoLa provides two ways to include escaped and non-printable characters in a string:
- Use a hexadecimal escape (`\x63`)
- Use one of the predefined escape shorthands (`\r`, `\n`)
The hexadecimal escape allows the programmer to embed any byte value into the string. It is introduced by the escape character `\`, followed by a small `x`, then a two-digit hexadecimal number. The number is then converted into a byte value and inserted into the string.
The predefined escape codes provide often-required whitespace and control characters without the need to remember their exact value:
| Shorthand | ASCII Value | Name |
| --------- | ----------- | --------------- |
| `\a` | 7 | Alert / Bell |
| `\b` | 8 | Backspace |
| `\t` | 9 | Horizontal Tab |
| `\n` | 10 | Line Feed |
| `\r` | 13 | Carriage Return |
| `\e` | 27 | Escape |
| `\"` | 34 | Double Quotes |
| `\'` | 39 | Single Quote |
## Variables
Variables provide a way to store something beyond the context of a single computation.
```js
var x; // Uninitialized, global variable
var y = 10; // Initialized global variable
{
var z; // Unitialized local variable
var w = 0; // Initialized local variable
}
```
There are three kind of variables in LoLa:
- Global Variables
- Local Variables
Global variables are accessible from any scope and are stored in the execution environment. **If a global variable has no initializer, it's value is preserved over multiple calls of the script.**
Local variables could also be called temporary variables as they are only alive for a short time. A local variables is any variable declared in brackets, so explicit declared locals, loop variables and function parameters.
All variables are dynamically typed and may change the type of the stored value on assignment.
### Shadowing
LoLa allows shadowing of variable names. This means, that you can have a variable with the same name as a previously declared variable. The previously declared variable will be hidden (shadowed) by the newly declared variable for the scope of the shadowing variable.
## Operators
LoLa provides several operators that execute arithmetic, logic or comparison operations.
### Table of Operators
| Operator | Applies to | Description | Example |
| -------- | --------------------------- | ----------------------------------------- | ------------------------------------------------------------ |
| `a + b` <br /> `a += b` | `string`, `number`, `array` | Adds numbers, concats strings and arrays. | `3 + 2 == 5`, `"a" + "b" == "ab"`, `[ 1, 2 ] + [ 3 ] == [ 1, 2, 3 ]` |
| `a - b` <br />`a -= b` | `number` |Subtraction|`5 - 2 == 3`|
| `-a` | `number` | Negation |`-(4) == -4`|
| `a * b` <br />`a *= b` | `number` |Multiplication|`5 * 2 == 10`|
| `a / b `<br />`a /= b` | `number` |Division|`10 / 5 == 2`|
| `a % b` <br />`a %= b` | `number` |Remainder Division|`10 % 4 == 2`|
| `a and b` | `boolean` |Boolean AND|`true and false == false`|
| `a or b` | `boolean` |Boolean OR|`true or false == true`|
| `not a` | `boolean` |Boolean NOT|`not false == true`|
| `a == b` | *all* |Equality test|`(3 == 3) == true`|
| `a != b` | *all* |Inequality test|`(3 != 2) == true`|
| `a >= b` | `number` |Greater-or-equal test|`(3 >= 2) == true`|
| `a <= b` | `number` |Less-or-equal test|`(3 <= 2) == false`|
| `a > b` | `number` |Greater-than test|`(3 > 2) == true`|
| `a < b` | `number` |Less-than test|`(3 < 2) == false`|
| `a[i]` | `array`, `string` | Array index, string index | `([1,2,3])[1] == 2` |
| `a.field` | `struct` | Field access | `([.x=1, .y=2]).x == 1` |
### Operator Precedence
Operator precedence in the list low to high. A higher precedence means that these operators *bind* more to the variables and will be applied first.
#### Binary
- `and`, `or`
- `==`, `!=`, `>=`, `<=`, `>`, `<`
- `+`, `-`
- `*`, `/`, `%`
#### Unary
- `not`, `-`
- `a[i]`
- `a.field`
## Control Flow Structures
LoLa provides a small set of control flow structures that are simple to use and are widespread in a lot of programming languages.
### Blocks
```js
{ // Blocks are always introduced by a curly bracket
var x; // local to this block
// here is the block content
} // and are closed by a curly bracket
// x is not valid here anymore!
```
Blocks are a convenient way of introducing structure into the code. Each block has its own set of local variables, but can access the local variables of its parent as well. Each control structure in LoLa is followed by a block, but blocks can also be freestanding as in the example above.
### Assignments
Assignments in LoLa are statements that return no value. This is different from other programming languages like C that allow nesting assignments into expressions (~~`a + (b = c)`~~).
```js
a = b; // simple assignment, copy the value from b into a.
```
An assignment will always copy the value that is assigned. It will not create equality of the two names:
```js
a = 1;
b = a;
a = 2;
Print(a, b); // Will print "2, 1" as b has not been changed
```
You can always assign an item of array:
```js
a[i] = c; // indexed assignment: copy the value of c into the i'th index of the array a.
```
This allows mutating the contents of the array. The same rules as for a normal variable assignment apply here.
You can also assign a field of a struct:
```js
a.field = c; // field assignment: copy the value of c into the field named 'field' of the struct a.
```
Assigning a field that does not exist in the struct will cause a panic.
### `if`-Conditional
The conditional `if` statement provides a simple way to do things depending on a condition:
```js
if(a > 5) {
// This code is executed only when a > 5.
}
```
The code in the curly brackets is only executed when the condition in the round brackets is `true`. The condition must always be a `boolean` value.
If the code should do an *either-or* semantic, you can add an else block:
```js
if(a > 5) {
// This code is executed only when a > 5.
}
else {
// This code is executed when a <= 5.
}
```
The `else` part is optional.
`if` also provides a short-hand version if only a single statement is conditional:
```js
if(condition)
Statement(); // Function call, control flow or assignment
if(condition)
Statement();
else
Statement();
```
### `while`-Loop
If a piece of code should repeat itself, a loop structure is helpful:
```js
while(a > 5) {
// this code repeats as long as a > 5.
}
```
The `while` loop will check the condition in the round brackets. If the condition is `true`, the code in the curly brackets will be executed. After that, the condition will be checked again and the process starts again.
### `for`-Loop
Iterating over an array is such a common task that LoLa provides a built-in loop for that:
```js
for(x in data) {
// For each loop iteration, x will contain a value from data
}
```
The syntax for the loop is `for(var in data) { … }` where `var` is a new local variable, and `data` is an array value.
The loop will execute one time for each item in `data`, filling `var` with the current item. The items are processed in order.
### Function Calls
```js
Print("Hello, World!"); // Calls the function Print with one arg.
x = GetSomething(); // stores the return value of GetSomething()
```
Function calls will execute a sub-program that may return a value to their caller. A function call may take zero or more arguments, but will always return a value. If the return value is not stored, it will be discarded.
### Method Calls
Methods calls are similar to function calls, but require an `object` value to be executed:
```js
var obj = …; // We require a variable of type object
obj.Print("Hello, World!"); // Call the method Print on obj.
```
The `Print` in this case is not a usual function but a method. Methods are defined on objects and pass the object to the method as well.
This allows the script runtime to provide the user with more complex data structures or interfaces that are implemented via objects instead of free functions.
Objects can also represent resources like [sockets](https://en.wikipedia.org/wiki/Network_socket) or [key-value-stores](https://en.wikipedia.org/wiki/Key-value_database) that are available to the user.
### `return`
`return` will stop the execution of the current sub-program and will return control to the caller.
```js
return; // Stop execution now, return void
return true; // Stop execution now and return true
```
`return` may take an optional value that will be returned as a result of the sub-program.
Returning a value from global execution environment is not allowed, returning a value to the script host should be done with a function provided by the host.
### `break`
`break` will cancel the current loop execution. This means that it will continue execution after the loop-block:
```js
var i = 0;
var j = 0;
while(true)
{
i += 1;
Print("i = ", i);
if(i > 5)
break; // this will jump to after the while-loop
j += 1;
}
// <= break will continue execution here
Print(i, j); // Will print 6, 5
```
The same goes for `for` loops:
```js
for(x in [ 1, 2, 3, 4 ])
{
if(x > 2)
break; // this will jump to after the for-loop
}
// <= break will continue execution here
```
### `continue`
`continue` is the counterpart to `break`: It will continue with the next loop iteration instead of completing the current one. This can be used to skip a whole bunch of code:
```js
var a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
var skipped = 0;
for(x in a)
{
// continue will continue from here
if(x < 3) {
skipped += 1;
continue; // jumps to the start of the loop with the next var
}
Print(x);
}
Print("Skipped ", skipped, " elements!");
```
The same goes for `while` loops as well:
```js
var i = 0;
while(i < 10)
{
// continue will continue from here
i += 1;
if(i < 4)
continue; // jumps to the start of the loop
Print(i);
}
```
## Functions
Functions allows the user to declare custom sub-programs in the script:
```js
function AddFive(a) {
return a + 5;
}
function Compare(a, b) {
if(a > b) {
return "larger";
}
else if(a < b) {
return "smaller";
} else {
return "equal";
}
}
```
Functions have their own scope, and may `return` a value to their caller.
## Top Level Code
Similar to other scripting languages, LoLa allows not only top-level declarations, but also top-level code. This means there is no `main` function that is called when starting execution, but the top-level code will be run instead.
```js
// This is not a snippet, but a valid file!
SayHelloTo("me");
function SayHelloTo(name)
{
Print("Hello, " + name + "!");
}
```
As you can see, the order of declaration is not relevant in LoLa. Functions may be called from top-level before or after declaration.
## Error Handling
The LoLa language has not a very sophisticated error handling concept. If something goes wrong, the runtime will just panic and return execution to the host system with the position where the panic happened as well as some basic info about what failed.
Possible panics are:
- `OutOfMemory` (the host ran out of memory while executing the code)
- `TypeMismatch` (an operation or function expected one type, but got another)
- `IndexOutOfBounds` (an array was indexed outside the boundaries of the array)
- `InvalidArgs` (a wrong number of arguments was passed to a function)
- `OutOfRange` (a numeric value wasn't in the allowed range)
A panic may be caused by the LoLa virtual machine or any library functions.
## Objects
Objects in LoLa are opaque handles except for their methods. Users cannot declare or create objects without the help of the script host. Object handles are usually valid as long as the LoLa script has access to that handle, but the script host might destroy objects actively as well.
Object handles can be considered a reference type as you don't get a copy of the object when you pass a handle around. This type would be the only exception to LoLas design of *value types only*.
## Structs
Structs are value types that hold a fixed set of named fields, each of which can hold an arbitrary value. They are created with a struct literal:
```js
var point = [.x = 1, .y = 2];
```
Fields are accessed and assigned using the `.field` notation:
```js
Print(point.x); // prints 1
point.y = 10;
Print(point.y); // prints 10
```
Accessing or assigning a field that does not exist in the struct will cause a panic.
Because structs are value types, assigning a struct variable copies the entire struct:
```js
var a = [.x = 1];
var b = a;
b.x = 99;
Print(a.x); // still 1 — a was not affected
```
## LoLa File Types
The following list contains LoLa-related file types
- LoLa Code (`*.lola`)
These files contain actual LoLa source code and can be compiled into modules.
- [LoLa Modules](Modules.md) (`*.lola.lm`)
These files contain compiled LoLa byte code and can be executed by a [LoLa VM](IR.md).
## Frequently Asked Questions
This section tries to answer questions that are commonly asked.
### Why does the language not allow user-defined objects?
The design decision of LoLa was to be a simple as possible without losing too much features. User-defined objects would make the language way more complex and harder to reason about code. As the focus group of the language is mostly programmer novices, this complexity was left out and passed to the script host.
It is still possible to create user-defined objects and classes though: The script host might implement a function `MakeObject(str)` which compiles a LoLa source and returns the compiled script as a object, evaluating the top-level code as some kind of constructor and exporting all functions as methods to that object.
## List of Keywords
- `and`
- `break`
- `const`
- `continue`
- `else`
- `for`
- `function`
- `if`
- `in`
- `not`
- `or`
- `return`
- `var`
- `while`
## Wording
The following chapter explains some of the words used in this document with concrete focus on the meaning inside LoLa.
### Statement
A statement is something that can be written as a line of code or execution unit.
The following constructs count as statements:
- Conditionals (`if`)
- Loops (`while`, `for`)
- Everything with a semicolon at the end (`a = …;`, `a[i] = …;`, `Print("Hi!");`)
### Expression
An expression is something that yields a value that can be used in another expression or can be assigned to a value.
Examples for expressions are:
- `1`
- `"Hello"`
- `expr + expr`
- `SumOf(10, 20)`
- …
LoLa does not allow lone statements except for function and method calls. These are special in a way that they may discard their value. The resulting value of all other expressions may not be discarded.
### Block
Any piece of code between `{` and `}`. The block con
## Examples
The following section will contain small examples on how to use the language.
### Sum the values of an array
```js
var a = [ 1, 2, 3 ];
var sum = 0;
for(v in a) {
sum += a;
}
Print("Sum = ", sum);
```
### Bubble Sort
```js
function BubbleSort(arr)
{
var len = Length(arr);
var n = len;
while(n > 1) {
var i = 0;
while(i < n - 1) {
if (arr[i] > arr[i+1]) {
var tmp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = tmp;
}
i += 1;
}
n -= 1;
}
return arr;
}
Print(BubbleSort([ 7, 8, 9, 3, 2, 1 ]));
```
### Reversing an array
```js
// Reverse an array
function RevertArray(arr)
{
var i = 0;
var l = Length(arr);
while(i < l/2) {
var tmp = arr[i];
arr[i] = arr[l - i - 1];
arr[l - i - 1] = tmp;
i += 1;
}
return arr;
}
```
### Using an object
```js
var stack = CreateStack();
stack.Push(10);
stack.Push(20);
stack.Push(30);
function Operation(op)
{
if(op == "print") {
Print(stack.Pop());
}
if(op == "add") {
var lhs = stack.Pop();
var rhs = stack.Pop();
stack.Push(lhs + rhs);
}
if(op == "mul") {
var lhs = stack.Pop();
var rhs = stack.Pop();
stack.Push(lhs * rhs);
}
}
Operation("mul");
Operation("add");
Operation("print");
Print("Stack Length: ", stack.GetSize());
```
================================================
FILE: documentation/checklist.txt
================================================
Programming Language Checklist
by Colin McMillen, Jason Reed, and Elly Fong-Jones, 2011-10-10.
Filled out for LoLa
You appear to be advocating a new:
[ ] functional [X] imperative [X] object-oriented [X] procedural [ ] stack-based
[ ] "multi-paradigm" [ ] lazy [X] eager [ ] statically-typed [X] dynamically-typed
[ ] pure [X] impure [ ] non-hygienic [ ] visual [X] beginner-friendly
[ ] non-programmer-friendly [ ] completely incomprehensible
programming language. Your language will not work. Here is why it will not work.
You appear to believe that:
[ ] Syntax is what makes programming difficult
[ ] Garbage collection is free [X] Computers have infinite memory
[X] Nobody really needs:
[X] concurrency [X] a REPL [X] debugger support [X] IDE support [X] I/O
[ ] to interact with code not written in your language
[X] The entire world speaks 7-bit ASCII
[X] Scaling up to large software projects will be easy
[X] Convincing programmers to adopt a new language will be easy
[ ] Convincing programmers to adopt a language-specific IDE will be easy
[ ] Programmers love writing lots of boilerplate
[ ] Specifying behaviors as "undefined" means that programmers won't rely on them
[ ] "Spooky action at a distance" makes programming more fun
Unfortunately, your language (has/lacks):
[X] comprehensible syntax [X] semicolons [ ] significant whitespace [ ] macros
[ ] implicit type conversion [ ] explicit casting [ ] type inference
[ ] goto [ ] exceptions [ ] closures [ ] tail recursion [ ] coroutines
[ ] reflection [ ] subtyping [ ] multiple inheritance [ ] operator overloading
[ ] algebraic datatypes [ ] recursive types [ ] polymorphic types
[ ] covariant array typing [ ] monads [ ] dependent types
[X] infix operators [ ] nested comments [ ] multi-line strings [ ] regexes
[X] call-by-value [ ] call-by-name [ ] call-by-reference [ ] call-cc
The following philosophical objections apply:
[ ] Programmers should not need to understand category theory to write "Hello, World!"
[ ] Programmers should not develop RSI from writing "Hello, World!"
[ ] The most significant program written in your language is its own compiler
[X] The most significant program written in your language isn't even its own compiler
[ ] No language spec
[ ] "The implementation is the spec"
[ ] The implementation is closed-source [ ] covered by patents [ ] not owned by you
[ ] Your type system is unsound [ ] Your language cannot be unambiguously parsed
[ ] a proof of same is attached
[ ] invoking this proof crashes the compiler
[ ] The name of your language makes it impossible to find on Google
[X] Interpreted languages will never be as fast as C
[X] Compiled languages will never be "extensible"
[ ] Writing a compiler that understands English is AI-complete
[ ] Your language relies on an optimization which has never been shown possible
[ ] There are less than 100 programmers on Earth smart enough to use your language
[X] __anything that'd be fast in C__ takes exponential time
[ ] ________________________________ is known to be undecidable
Your implementation has the following flaws:
[X] CPUs do not work that way
[X] RAM does not work that way
[ ] VMs do not work that way
[ ] Compilers do not work that way
[ ] Compilers cannot work that way
[ ] Shift-reduce conflicts in parsing seem to be resolved using rand()
[ ] You require the compiler to be present at runtime
[X] You require the language runtime to be present at compile-time
[X] Your compiler errors are completely inscrutable
[ ] Dangerous behavior is only a warning
[ ] The compiler crashes if you look at it funny
[ ] The VM crashes if you look at it funny
[X] You don't seem to understand basic optimization techniques
[X] You don't seem to understand basic systems programming
[X] You don't seem to understand pointers
[ ] You don't seem to understand functions
Additionally, your marketing has the following problems:
[ ] Unsupported claims of increased productivity
[ ] Unsupported claims of greater "ease of use"
[ ] Obviously rigged benchmarks
[ ] Graphics, simulation, or crypto benchmarks where your code just calls
handwritten assembly through your FFI
[ ] String-processing benchmarks where you just call PCRE
[ ] Matrix-math benchmarks where you just call BLAS
[X] Noone really believes that your language is faster than:
[ ] assembly [ ] C [ ] FORTRAN [ ] Java [X] Ruby [ ] Prolog
[ ] Rejection of orthodox programming-language theory without justification
[X] Rejection of orthodox systems programming without justification
[ ] Rejection of orthodox algorithmic theory without justification
[ ] Rejection of basic computer science without justification
Taking the wider ecosystem into account, I would like to note that:
[X] Your complex sample code would be one line in: any other language
[ ] We already have an unsafe imperative language
[ ] We already have a safe imperative OO language
[ ] We already have a safe statically-typed eager functional language
[ ] You have reinvented Lisp but worse
[ ] You have reinvented Javascript but worse
[ ] You have reinvented Java but worse
[ ] You have reinvented C++ but worse
[ ] You have reinvented PHP but worse
[ ] You have reinvented PHP better, but that's still no justification
[ ] You have reinvented Brainfuck but non-ironically
In conclusion, this is what I think of you:
[ ] You have some interesting ideas, but this won't fly.
[ ] This is a bad language, and you should feel bad for inventing it.
[X] Programming in this language is an adequate punishment for inventing it.
================================================
FILE: documentation/ir.md
================================================
# LoLa Intermedia Language
This document describes all available instructions of the Lola intermediate language as well as it's encoding in a binary stream.
## Instructions
The following list contains each instruction and describes it's effects on the virtual machine state.
- `nop` No operation
- `store_global_name` stores global variable by name `[ var:str ]`
- pops a value and stores it in the environment-global `str`
- `load_global_name` loads global variable by name `[ var:str ]`
- pushes a value stored in the environment-global `str`
- `push_str` pushes string literal `[ val:str ]`
- pushes the string `str`
- `push_num` pushes number literal `[ val:f64 ]`
- pushes the number `val`
- `array_pack` packs *num* elements into an array `[ num:u16 ]`
- pops `num` elements front-to-back and packs them into an array front to back
- stack top will be the first element
- `call_fn` calls a function `[ fun:str ] [argc:u8 ]`
- pops `argc` elements front-to-back into the argument list, then calls function `fun`
- stack top will be the first argument
- `call_obj` calls an object method `[ fun:str ] [argc:u8 ]`
- pops the object to call,
- then pops `argc` elements front-to-back into the argument list,
- then calls function `fun`
- stack top will be the first argument
- `pop` destroys stack top
- pops a value and discards it
- `add` adds rhs and lhs together
- first pops the right hand side,
- then the left hand side,
- then adds right to left, pushes the result
- `sub` subtracts rhs and lhs together
- first pops the right hand side,
- then the left hand side,
- then subtracts right from left, pushes the result
- `mul` multiplies rhs and lhs together
- first pops the left hand side,
- then the right hand side,
- then multiplies left and right, pushes the result
- `div` divides rhs and lhs together
- first pops the left hand side,
- then the right hand side,
- then divides left by right, pushing the divisor
- `mod` modulo division of rhs and lhs
- first pops the left hand side,
- then the right hand side,
- then divides left by right, pushing the module
- `(-5 % 2) == 1`
- `bool_and` conjunct rhs and lhs
- first pops the left hand side,
- then the right hand side,
- then pushes `true` when both left and right hand side are `true`
- `bool_or` disjuncts rhs and lhs
- first pops the left hand side,
- then the right hand side,
- then pushes `true` when either of left or right hand side is `true`
- `bool_not` logically inverts stack top
- pops a value from the stack
- pushs `true` if the value was `false`, otherwise `true`
- `negate` arithmetically inverts stack top
- pops a value from the stack
- then pushes the negative value
- `eq`
- pops two values from the stack and compares if they are equal
- pushes a boolean containing the result of the comparison
- `neq`
- pops two values from the stack and compares if they are not equal
- pushes a boolean containing the result of the comparison
- `less_eq`
- first pops the right hand side,
- then the left hand side,
- then pushes `true` when left hand side is less or equal to the right hand side.
- `greater_eq`
- first pops the right hand side,
- then the left hand side,
- then pushes `true` when left hand side is greater or equal to the right hand side.
- `less`
- first pops the right hand side,
- then the left hand side,
- then pushes `true` when left hand side is less to the right hand side.
- `greater`
- first pops the right hand side,
- then the left hand side,
- then pushes `true` when left hand side is greater to the right hand side.
- `jmp` jumps unconditionally `[target:u32 ]`
- Sets the instruction pointer to `target`
- `jnf` jump when not false `[target:u32 ]`
- Pops a value from the stack
- If that value is `true`
- Sets the instruction pointer to `target`
- `iter_make`
- Pops an *array* from the stack
- Creates an *iterator* over that *array*.
- Pushes the created *iterator*.
- `iter_next`
- Peeks an *iterator* from the stack
- If that *iterator* still has values to yield:
- Push the *value* from the *iterator*
- Push `true`
- Advance the iterator by 1
- else:
- Push `false`
- `array_store`
- Then pops the *array* from the stack
- Then pops the *index* from the stack
- Pops the *value* from the stack
- Stores *value* at *index* in *array*
- Pushes *array* to the stack.
- `array_load`
- Pops *array* from the stack
- Pops *index* from the stack
- Loads a *value* from the *array* at *index*
- Pushes *value* to the stack
- `ret` returns from the current function with Void
- returns from the function call with a `void` value
- `store_local` stores a local variable `[index : u16 ]`
- Pops a *value* from the stack
- Stores that *value* in the function-local variable at *index*.
- `load_local` loads a local variable `[index : u16 ]`
- Loads a *value* from the function-local *index*.
- Pushes that *value* to the stack.
- `retval` returns from the current function with a value
- pops a value from the stack
- returns from the function call with the popped value
- `jif` jump when false `[ target:u32 ]`
- Pops a value from the stack
- If that value is `false`
- Sets the instruction pointer to `target`
- `store_global_idx` stores global variable by index `[ idx:u16 ]`
- Pops a value from the stack
- Stores this value in the object-global storage
- `load_global_idx` loads global variable by index `[ idx:u16 ]`
- Loads a value from the object-global storage
- Pushes that value to the stack
- `push_true`
- pushes literal boolean `true`
- `push_false`
- pushes literal boolean `false`
- `push_void`
- pushes void value
- `struct_pack` packs *num* named fields into a struct `[ num:u16 ]`
- for each of `num` fields, pops the *value* then pops the *name* (a string)
- packs them into a struct where each name maps to its corresponding value
- pushes the resulting struct
- `struct_store` stores a value into a struct field
- pops the *struct* from the stack
- pops the *field name* (a string) to assign
- pops the *value* to store
- stores *value* at the named field in *struct*
- pushes the modified *struct* to the stack
- panics with `InvalidField` if the field does not exist in the struct
- `struct_load` loads a value from a struct field
- pops the *struct* from the stack
- pops the *field name* (a string) to read
- loads the *value* of the named field from *struct*
- pushes *value* to the stack
- panics with `InvalidField` if the field does not exist in the struct
## Encoding
### Instructions
The instructions are encoded in an intermediate language. Each instruction is encoded by a single byte, followed by arguments different for each instruction.
Argument types are noted in `name:type` notation where type is one of the following: `str`, `f64`, `u16`, `u8`, `u32`. The encoding of these types is described below the table.
| Instruction | Value | Arguments | Description |
| ----------------- | ----- | ------------------ | ---------------------------------------------- |
| nop | 0 | | No operation |
| scope_push | 1 | | *reserved* |
| scope_pop | 2 | | *reserved* |
| declare | 3 | `var:str` | *reserved* |
| store_global_name | 4 | `var:str` | stores global variable by name |
| load_global_name | 5 | `var:str` | loads global variable by name |
| push_str | 6 | `val:str` | pushes string literal |
| push_num | 7 | `val:f64` | pushes number literal |
| array_pack | 8 | `num:u16` | packs *num* elements into an array |
| call_fn | 9 | `fun:str, argc:u8` | calls a function |
| call_obj | 10 | `fun:str, argc:u8` | calls an object method |
| pop | 11 | | destroys stack top |
| add | 12 | | adds rhs and lhs together |
| sub | 13 | | subtracts rhs and lhs together |
| mul | 14 | | multiplies rhs and lhs together |
| div | 15 | | divides rhs and lhs together |
| mod | 16 | | reminder division of rhs and lhs |
| bool_and | 17 | | conjunct rhs and lhs |
| bool_or | 18 | | disjuncts rhs and lhs |
| bool_not | 19 | | logically inverts stack top |
| negate | 20 | | arithmetically inverts stack top |
| eq | 21 | | |
| neq | 22 | | |
| less_eq | 23 | | |
| greater_eq | 24 | | |
| less | 25 | | |
| greater | 26 | | |
| jmp | 27 | `target:u32` | jumps unconditionally |
| jnf | 28 | `target:u32` | jump when not false |
| iter_make | 29 | | |
| iter_next | 30 | | |
| array_store | 31 | | |
| array_load | 32 | | |
| ret | 33 | | returns from the current function with Void |
| store_local | 34 | `index:u16` | |
| load_local | 35 | `index:u16` | |
| retval | 37 | | returns from the current function with a value |
| jif | 38 | `target:u32` | jump when false |
| store_global_idx | 39 | `idx:u16` | stores global variable by index |
| load_global_idx | 40 | `idx:u16` | loads global variable by index |
| push_true | 41 | | pushes a boolean `true` |
| push_false | 42 | | pushes a boolean `false` |
| push_void | 43 | | pushes a `void` value. |
| struct_pack | 44 | `num:u16` | packs *num* named fields into a struct |
| struct_store | 45 | | stores a value into a struct field |
| struct_load | 46 | | loads a value from a struct field |
### Types
#### `u8`, `u16`, `u32`
Each of these corresponds to a single, little endian encoded unsigned integer with either 8, 16 or 32 bits width.
#### `f64`
A 64 bit floating point number, encoded with **IEEE 754** *binary64* format, also known as `double`.
#### `str`
A literal string value with a maximum of 65535 bytes length. It's text is encoded in an application-defined encoding where all values below 128 must follow the ASCII encoding scheme. Values equal or above 128 are interpreted by an application-defined logic.
A string is started by a 16 bit unsigned integer defining the length of the string, followed by *length* bytes of content.
**Rationale:** The encoding is not fixed to UTF-8 as the language is meant to be embedded into games where a unicode encoding would be a burden to the player. Thus, a string is defined to be "at least" ASCII-compatible and allows UTF-8 encoding, but does not enforce this.
================================================
FILE: documentation/modules.md
================================================
# LoLa Module Format
Native LoLa has a binary module format that contains compiled intermediate code. This format both contains meta-data like function names, but also the compiled code.
## Data Structure
The description uses a file notation similar to Zig syntax. Each segment of the file is described as a structure with fields. The fields are packed and don't have any padding bits. Each field is noted by name, colon, type, and an optional fixed value.
`u8`, `u16`, … denote a unsigned integer type with *n* bits, `[x]T` is an Array of `x` times `T` where `T` is a type and `x` is either a constant or variable size of a field declared earlier.
Some fields are commented with C++ style comments, introduced by a `//`.
```rust
// Structure of the whole file
File {
header: FileHeader, // contains the module header
globalCount: u16, // number of global script variables
temporaryCount: u16, // number of temporary variables (global)
functionCount: u16, // number of declared functions
codeSize: u32, // size of the intermediate code in bytes
numSymbols: u32, // number of debug symbols
functions: [functionCount]Function, // contains the function meta data
code: [codeSize]u8, // intermediate code
debugSymbols: [numSymbols]DebugSymbol, // debug symbols
}
FileHeader {
identifier: [8]u8 = "LoLa\xB9\x40\x80\x5A"
version: u32 = 1, // will increment in future versions
comment: [256]u8, // zero terminated
}
Function {
name: [128]u8, // zero-terminated function name
entryPoint: u32, // start of the function in the intermediate code
localCount: u16, // number of local variable slots.
}
DebugSymbol {
offset: u32, // offset in code
sourceLine: u32, // line of the original source
sourceColumn: u16, //
}
```
================================================
FILE: documentation/runtime-library.md
================================================
# LoLa Runtime Library
This file documents the LoLa Runtime Library, a set of basic I/O routines to enable standalone LoLa programs.
The API in this document is meant for the standalone LoLa interpreter and functions listed here are not necessarily available in embedded programs!
## Generic
### `Exit(code: number): noreturn`
This function will stop execution of the program and will return `code` to the OS.
## File I/O
### `ReadFile(path: string): string|void`
Reads in the contents of a file located at `path` as a `string` or returns `void` when the file does not exist or the given path is not a file.
### `WriteFile(path: string, contents: string): void`
Writes `contents` to the file located at `path`. If the file does not exist, it will be created.
### `FileExists(path: string): boolean`
Returns `true` if a file exists at the given `path`, `else` otherwise.
## Console I/O
### `Print(…): void`
Will print every argument to the standard output. All arguments of type `string` will be printed verbatim, non-`string` arguments are converted into a human-readable form and will then be printed.
After all arguments are printed, a line break will be outputted.
### `ReadLine(): string|void`
Reads a line of text from the standard input and returns it as a `string`. If the standard input is in the *end of file* state, `void` will be returned.
## Standard Objects
### `CreateList([init: array]): object`
Returns a new object that implements a dynamic list.
If `init` is given, the list will be initialized with the contents of `init`.
This list has the following API:
#### `list.Add(item): void`
Appends a new item to the back of the list.
#### `list.Remove(item): boolean`
Removes all occurrances of `item` in the list.
#### `list.RemoveAt(index): void`
Removes the item at `index`. Indices start at `0`. When the index is out of range, nothing will happen.
#### `list.GetCount(): number`
Returns the current number of elements in the list.
#### `list.GetItem(index): any`
Returns the item at `index` or panics with `OutOfRange`;
#### `list.SetItem(index, value): void`
Replaces the item at `index` with `value`.
#### `list.ToArray(): array`
Returns the current list as an array.
#### `list.IndexOf(item): number`
Returns first the index of `item` in the list or `void` if the item was not found.
#### `list.Resize(size): void`
Resizes the list to `size` items. New items will be set to `void`.
#### `list.Clear(): void`
Removes all items from the list.
### `CreateDictionary(): object`
Returns a new object that implements a key-value store.
#### `dict.Get(key): any`
Returns the value associated with `key` or returns `void` if `key` does not have a associated value.
#### `dict.Set(key, value): void`
Sets the associated value for `key` to `value`. If `value` is `void`, the key will be removed.
#### `dict.Remove(key): boolean`
Removes any value associated with `key`. Returns `true` when a key was removed else `false`.
#### `dict.Contains(key): boolean`
Returns `true` if the dictionary contains a value associated with `key`.
#### `dict.GetKeys(): array`
Returns an array with all keys stored in the dictionary.
#### `dict.GetValues(): array`
Returns an array with all values stored in the dictionary.
#### `dict.Clear(): void`
Removes all values from the dictionary.
#### `dict.GetCount(): number`
Returns the number of keys currently stored in the list.
================================================
FILE: documentation/standard-library.md
================================================
# LoLa Standard Library
This file documents the LoLa Standard Library, a set of basic routines to enable LoLa programs.
## String API
### `Length(string): number`
Returns the length of the string.
### `SubString(string, start, [length]): string`
Returns a portion of `string`. The portion starts at `start` and is `length` bytes long. If `length` is not given, only the start of the string is cut.
### `Trim(string): string`
Removes leading and trailing white space from the string. White space is on of the following ascii characters:
- `0x09` (horizontal tab)
- `0x0A` (line feed)
- `0x0B` (vertical tab)
- `0x0C` (form feed)
- `0x0D` (carriage return)
- `0x20` (space)
### `TrimLeft(string): string`
Removes leading white space from the string.
### `TrimRight(string): string`
Removes trailing white space from the string.
### `IndexOf(string, text): number|void`
Searches for the first occurrence `text` in `string`, returns the offset to the start in bytes. If `text` is not found, `void` is returned.
### `LastIndexOf(string, text): number|void`
Searches for the last occurrence of `text` in `string`, returns the offset to the start in bytes. If `text` is not found, `void` is returned.
### `Byte(string): number`
Returns the first byte of the string as a number value. If the string is empty, `void` is returned, if the string contains more than one byte, still only the first byte is considered.
### `Chr(byte): string`
Returns a string of the length 1 containing `byte` as a byte value.
### `NumToString(num, [base]=10): string`
Converts the number `num` into a string represenation to base `base`. If `base` is given, it will format the integer value of the number to `base`, otherwise, a decimal floating point output will be given.
### `StringToNum(str, [base]=10): number|void`
Converts the string `str` to a number. If `base` is not given, the number is assumed to be base 10 and a floating point value. Otherwise, `base` is used as the numeric base for conversion, and only integer values are accepted.
If the conversion fails, `void` is returned.
If `base` is 16, `0x` is accepted as a prefix, and `h` as a postfix.
### `Split(str, sep, [removeEmpty]): array`
Splits the string `str` into chunks separated by `sep`. When `removeEmpty` is given and `true`, all empty entries will be removed.
### `Join(array, [sep]): string`
Joins all items in `array`, optionally separated by `sep`. Each item in `array` must be a `string`.
## Array API
### `Array(count, [init]): array`
Returns an array with `count` items initialized with `init` if given. Otherwise, the array will be filled with `void`.
### `Range(count): array`
Returns an array with `count` increasing numbers starting at 0.
### `Range(start, count)`
Returns an array with `count` increasing numbers starting at `start`.
### `Length(array): number`
Returns the number of items in `array`.
### `Slice(array, start, length): array`
Returns a portion of the `array`, starting at `index` (inclusive) and taking up to `length` items from the array. If less items are possible, an empty array is returned.
### `IndexOf(array, item): number|void`
Returns the index of a given `item` in `array`. If the item is not found, `void` is returned.
### `LastIndexOf(array, item): number|void`
Returns the last index of a given `item` in `array`. If the item is not found, `void` is returned.
## Math
### `Pi: number`
Global constant containing the number _pi_.
### `DeltaEqual(a, b, delta): boolean`
Compares `a` and `b` with a certain `delta`. Returns `true` when `abs(a-b) < delta`.
### ``Floor(x): number`
Rounds `x` towards negative infinity.
### ``Ceiling(x): number`
Rounds `x` towards positive infinity.
### ``Round(x): number`
Rounds `x` to the closest integer.
### `Sin(a): number`, `Cos(a): number`, `Tan(a): number`
Trigonometric functions, all use radians.
### `Atan(y, [x]): number`
Calculates the arcus tangens of `y`, and, if `x` is given, divides `y` by `x` before.
Use the two-parameter version for higher precision.
### `Sqrt(x): number`
Calculates the square root of `x`.
### `Pow(v, e): number`
Returns `v` to the power of `e`.
### `Log(v, [base]): number`
Returns the logarithm of `v` to base `base`. If `base` is not given, base 10 is used.
### `Exp(v): number`
Returns _e_ to the power of `v`. _e_ is the euler number.
### `Random([min],[max]): number`
Returns a random number between `min` and `max`. If no argument is given, a random number between `0.0` and `1.0` is returned. If only `min` is given, a number between `0.0` and `min` (inclusive) is returned.
### `RandomInt([min],[max]): number`
Returns a random integer between `min` and `max`. If no argument is given, a random positive number is returned. If only `min` is given, a number between `0` and `min` (exclusive) is returned.
## Auxiliary
### `Sleep(secs): void`
Sleeps for `secs` seconds.
### `Timestamp(): number`
Returns the current wall clock time as a unix timestamp.
### `TypeOf(arg): string`
Returns the type of the argument as a string. Returns one of the following:
```lola
"void", "boolean", "string", "number", "object", "array"
```
### `ToString(val): string`
Converts the input `val` into a string representation.
### `HasFunction(name): boolean`
Returns `true` if the current environment has a function called `name`, `false` otherwise.
### `HasFunction(object, name): boolean`
Returns `true` if the `object` has a function called `name`, `false` otherwise.
### `Serialize(value): string`
Serializes any `value` into a binary representation. This representation can later be loaded by via `Deserialize` and return the exact same value again. Note that objects are stored as opaque handles and are not transferrable between different systems.
### `Deserialize(string): any`
Deserializes a previously serialized value. If the deserialization fails, a panic will occurr.
### `Yield(): void`
This function will yield control back to the host, pausing the current execution. This can be inserted in loops to reduce CPU usage.
================================================
FILE: documentation/zig-api.md
================================================
# Zig API
The Zig API is the main API for the LoLa implementation. It exposes all concepts in a convenient matter
## Basic Architecture
```
ObjectPool CompileUnit
\ /
\ /
\ /
\ /
Environment
|
|
VirtualMachine
```
### Compile Unit
A structure describing a compiled LoLa source file, containing the bytecode and defined function entry points.
### Object Pool
A structure to manage LoLa objects. It's used for garbage collection, handle creation and lifetime management.
### Environment
A script execution environment. This can be seen as an instantiation of a compile unit. Each environment has its own set of global variables and functions.
### Virtual Machine
A virtual machine executes the code instantiated by one or more environments.
================================================
FILE: examples/host/minimal-host/main.zig
================================================
const std = @import("std");
const lola = @import("lola");
////
// Minimal API example:
// This example shows how to get started with the LoLa library.
// Compiles and runs the Hello-World program.
//
const example_source =
\\Print("Hello, World!");
\\
;
// This is required for the runtime library to be able to provide
// object implementations.
pub const ObjectPool = lola.runtime.objects.ObjectPool([_]type{
lola.libs.runtime.LoLaDictionary,
lola.libs.runtime.LoLaList,
});
pub fn main() anyerror!u8 {
var gpa_state = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa_state.deinit();
const allocator = gpa_state.allocator();
// Step 1: Compile the source code into a compile unit
// This stores the error messages and warnings, we
// just keep it and print all messages on exit (if any).
var diagnostics = lola.compiler.Diagnostics.init(allocator);
defer {
for (diagnostics.messages.items) |msg| {
std.debug.print("{f}\n", .{msg});
}
diagnostics.deinit();
}
// This compiles a piece of source code into a compile unit.
// A compile unit is a piece of LoLa IR code with metadata for
// all existing functions, debug symbols and so on. It can be loaded into
// a environment and be executed.
var compile_unit = (try lola.compiler.compile(allocator, &diagnostics, "example_source", example_source)) orelse {
std.debug.print("failed to compile example_source!\n", .{});
return 1;
};
defer compile_unit.deinit();
// A object pool is required for garabge collecting object handles
// stored in several LoLa environments and virtual machines.
var pool = ObjectPool.init(allocator);
defer pool.deinit();
// A environment stores global variables and provides functions
// to the virtual machines. It is also a possible LoLa object that
// can be passed into virtual machines.
var env = try lola.runtime.Environment.init(allocator, &compile_unit, pool.interface());
defer env.deinit();
// Install both standard and runtime library into
// our environment. You can see how to implement custom
// functions if you check out the implementation of both
// libraries!
try env.installModule(lola.libs.std, lola.runtime.Context.null_pointer);
try env.installModule(lola.libs.runtime, lola.runtime.Context.null_pointer);
// Create a virtual machine that is used to execute LoLa bytecode.
// Using `.init` will always run the top-level code.
var vm = try lola.runtime.VM.init(allocator, &env);
defer vm.deinit();
// The main interpreter loop:
while (true) {
// Run the virtual machine for up to 150 instructions
const result = vm.execute(150) catch |err| {
// When the virtua machine panics, we receive a Zig error
std.debug.print("LoLa panic: {s}\n", .{@errorName(err)});
return 1;
};
// Prepare a garbage collection cycle:
pool.clearUsageCounters();
// Mark all objects currently referenced in the environment
try pool.walkEnvironment(env);
// Mark all objects currently referenced in the virtual machine
try pool.walkVM(vm);
// Delete all objects that are not referenced by our system anymore
pool.collectGarbage();
switch (result) {
// This means that our script execution has ended and
// the top-level code has come to an end
.completed => break,
// This means the VM has exhausted its provided instruction quota
// and returned control to the host.
.exhausted => {
std.debug.print("Execution exhausted after 150 instructions!\n", .{});
},
// This means the virtual machine was suspended via a async function call.
.paused => std.Thread.sleep(100),
}
}
return 0;
}
================================================
FILE: examples/host/multi-environment/client-a.lola
================================================
var server = GetServer();
server.Store("msg", "Hello, World!");
================================================
FILE: examples/host/multi-environment/client-b.lola
================================================
var server = GetServer();
Print(server.Load("msg"));
================================================
FILE: examples/host/multi-environment/main.zig
================================================
const std = @import("std");
const lola = @import("lola");
////
// Multi-environment communication example:
// In this example, we have three scripts:
// server.lola: Exporting a simple key-value store
// client-a.lola: Writes "Hello, World!" into the store in server
// client-b.lola: Reads the message from the server and prints it
//
// Real world application:
// This shows the inter-environment communication capabilities of LoLa,
// which is useful for games with interactive computer systems that need
// to interact with each other in a user-scriptable way.
//
// Each computer is its own environment, providing a simple script.
// Computers/Environments can communicate via a object interface, exposing
// other computers as a LoLa object and allowing those environments to
// communicate with each other.
//
pub const ObjectPool = lola.runtime.objects.ObjectPool([_]type{
lola.libs.runtime.LoLaDictionary,
lola.libs.runtime.LoLaList,
// Environment is a non-serializable object. If you need to serialize a whole VM state with cross-references,
// provide your own wrapper implementation
lola.runtime.Environment,
});
pub fn main() anyerror!u8 {
var gpa_state = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa_state.deinit();
const allocator = gpa_state.allocator();
var diagnostics = lola.compiler.Diagnostics.init(allocator);
defer {
for (diagnostics.messages.items) |msg| {
std.debug.print("{f}\n", .{msg});
}
diagnostics.deinit();
}
var server_unit = (try lola.compiler.compile(allocator, &diagnostics, "server.lola", @embedFile("server.lola"))) orelse return 1;
defer server_unit.deinit();
var client_a_unit = (try lola.compiler.compile(allocator, &diagnostics, "client-a.lola", @embedFile("client-a.lola"))) orelse return 1;
defer client_a_unit.deinit();
var client_b_unit = (try lola.compiler.compile(allocator, &diagnostics, "client-b.lola", @embedFile("client-b.lola"))) orelse return 1;
defer client_b_unit.deinit();
var pool = ObjectPool.init(allocator);
defer pool.deinit();
var server_env = try lola.runtime.Environment.init(allocator, &server_unit, pool.interface());
defer server_env.deinit();
var client_a_env = try lola.runtime.Environment.init(allocator, &client_a_unit, pool.interface());
defer client_a_env.deinit();
var client_b_env = try lola.runtime.Environment.init(allocator, &client_b_unit, pool.interface());
defer client_b_env.deinit();
for ([_]*lola.runtime.Environment{ &server_env, &client_a_env, &client_b_env }) |env| {
try env.installModule(lola.libs.std, lola.runtime.Context.null_pointer);
try env.installModule(lola.libs.runtime, lola.runtime.Context.null_pointer);
}
var server_obj_handle = try pool.createObject(&server_env);
// Important: The environment is stored in the ObjectPool,
// but will be destroyed earlier by us, so we have to remove it
// from the pool before we destroy `server_env`!
defer pool.destroyObject(server_obj_handle);
const getServerFunction = lola.runtime.Function{
.syncUser = .{
.context = lola.runtime.Context.make(*lola.runtime.ObjectHandle, &server_obj_handle),
.call = struct {
fn call(
environment: *lola.runtime.Environment,
context: lola.runtime.Context,
args: []const lola.runtime.Value,
) anyerror!lola.runtime.Value {
_ = environment;
_ = args;
return lola.runtime.Value.initObject(context.cast(*lola.runtime.ObjectHandle).*);
}
}.call,
.destructor = null,
},
};
try client_a_env.installFunction("GetServer", getServerFunction);
try client_b_env.installFunction("GetServer", getServerFunction);
// First, initialize the server and let it initialize `storage`.
{
var vm = try lola.runtime.VM.init(allocator, &server_env);
defer vm.deinit();
const result = try vm.execute(null);
if (result != .completed)
return error.CouldNotCompleteCode;
}
// Then, let Client A execute
{
var vm = try lola.runtime.VM.init(allocator, &client_a_env);
defer vm.deinit();
const result = try vm.execute(null);
if (result != .completed)
return error.CouldNotCompleteCode;
}
// Then, let Client B execute
{
var vm = try lola.runtime.VM.init(allocator, &client_b_env);
defer vm.deinit();
const result = try vm.execute(null);
if (result != .completed)
return error.CouldNotCompleteCode;
}
return 0;
}
================================================
FILE: examples/host/multi-environment/server.lola
================================================
var storage = CreateDictionary();
function Store(key, value) {
storage.Set(key, value);
}
function Load(key) {
return storage.Get(key);
}
================================================
FILE: examples/host/serialization/main.zig
================================================
const std = @import("std");
const lola = @import("lola");
////
// Serialization API example:
// This example shows how to save a whole-program state into a buffer
// and restore that later to continue execution.
//
// NOTE: This example is work-in-progress!
const example_source =
\\for(i in Range(1, 100)) {
\\ Print("Round ", i);
\\}
;
pub const ObjectPool = lola.runtime.objects.ObjectPool([_]type{
lola.libs.runtime.LoLaDictionary,
lola.libs.runtime.LoLaList,
});
pub fn main() anyerror!void {
// this will store our intermediate data
var serialization_buffer: [4096]u8 = undefined;
{
var gpa_state = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa_state.deinit();
try run_serialization(
gpa_state.allocator(),
&serialization_buffer,
);
}
{
var stdout = std.fs.File.stdout().writer(&.{});
try stdout.interface.writeAll("\n");
try stdout.interface.writeAll("-----------------------------------\n");
try stdout.interface.writeAll("\n");
try stdout.interface.flush();
}
{
var gpa_state = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa_state.deinit();
try run_deserialization(
gpa_state.allocator(),
&serialization_buffer,
);
}
}
fn run_serialization(allocator: std.mem.Allocator, serialization_buffer: []u8) !void {
var diagnostics = lola.compiler.Diagnostics.init(allocator);
defer {
for (diagnostics.messages.items) |msg| {
std.debug.print("{f}\n", .{msg});
}
diagnostics.deinit();
}
var compile_unit = (try lola.compiler.compile(allocator, &diagnostics, "example_source", example_source)) orelse return error.FailedToCompile;
defer compile_unit.deinit();
var pool = ObjectPool.init(allocator);
defer pool.deinit();
var env = try lola.runtime.Environment.init(allocator, &compile_unit, pool.interface());
defer env.deinit();
try env.installModule(lola.libs.std, lola.runtime.Context.null_pointer);
try env.installModule(lola.libs.runtime, lola.runtime.Context.null_pointer);
var vm = try lola.runtime.vm.VM.init(allocator, &env);
defer vm.deinit();
const result = try vm.execute(405);
std.debug.assert(result == .exhausted); // we didn't finish running our nice example
var stdout = std.fs.File.stdout().writer(&.{});
try stdout.interface.writeAll("Suspend at\n");
try vm.printStackTrace(&stdout.interface);
try stdout.interface.flush();
{
var stream: std.Io.Writer = .fixed(serialization_buffer);
try compile_unit.saveToStream(&stream);
// This saves all objects associated with their handles.
// This will only work when all object classes are serializable though.
try pool.serialize(&stream);
// this saves all global variables saved in the environment
try env.serialize(&stream);
var registry = lola.runtime.EnvironmentMap.init(allocator);
defer registry.deinit();
try registry.add(1234, &env);
// This saves the current virtual machine state
try vm.serialize(®istry, &stream);
try stream.flush();
try stdout.interface.print("saved state to {} bytes!\n", .{
stream.buffered().len,
});
try stdout.interface.flush();
}
}
fn run_deserialization(allocator: std.mem.Allocator, serialization_buffer: []u8) !void {
var reader: std.Io.Reader = .fixed(serialization_buffer);
// Trivial deserialization:
// Just load the compile unit from disk again
var compile_unit = try lola.CompileUnit.loadFromStream(allocator, &reader);
defer compile_unit.deinit();
// This is the reason we need to specialize lola.runtime.ObjectPool() on
// a type list:
// We need a way to do generic deserialization (arbitrary types) and it requires
// a way to get a runtime type-handle and turn it back into a deserialization function.
// this is done by storing the type indices per created object which can then be turned back
// into a real lola.runtime.Object.
var object_pool = try ObjectPool.deserialize(allocator, &reader);
defer object_pool.deinit();
// Environments cannot be deserialized directly from a stream:
// Each environment contains function pointers and references its compile unit.
// Both of these things cannot be done by a pure stream serialization.
// Thus, we need to restore the constant part of the environment by hand and
// install all functions as well:
var env = try lola.runtime.Environment.init(allocator, &compile_unit, object_pool.interface());
defer env.deinit();
// Installs the functions back into the environment.
try env.installModule(lola.libs.std, lola.runtime.Context.null_pointer);
try env.installModule(lola.libs.runtime, lola.runtime.Context.null_pointer);
// This will restore the whole environment state back to how it was at serialization
// time. All globals will be restored here.
try env.deserialize(&reader);
// This is needed for deserialization:
// We need means to have a unique Environment <-> ID mapping
// which is persistent over the serialization process.
var registry = lola.runtime.EnvironmentMap.init(allocator);
defer registry.deinit();
// Here we need to add all environments that were previously serialized with
// the same IDs as before.
try registry.add(1234, &env);
// Restore the virtual machine with all function calls.
var vm = try lola.runtime.VM.deserialize(allocator, ®istry, &reader);
defer vm.deinit();
var stdout = std.fs.File.stdout().writer(&.{});
try stdout.interface.writeAll("Suspend at\n");
try stdout.interface.print("restored state with {} bytes!\n", .{
reader.seek,
});
try stdout.interface.writeAll("Resume at\n");
try vm.printStackTrace(&stdout.interface);
try stdout.interface.flush();
// let the program finish
_ = try vm.execute(null);
}
================================================
FILE: examples/lola/README.md
================================================
# LoLa Examples
- [Hello World](hello-world.lola)
- [Iterative Fibonacci Numbers](fib-iterative.lola)
- [Summing up an array](sum-of-array.lola)
- [Reverse Array](reverse-array.lola)
- [Bubble Sort](bubble-sort.lola)
- [Global Variable Usage](global-setter.lola)
================================================
FILE: examples/lola/bubble-sort.lola
================================================
function BubbleSort(arr)
{
var len = Length(arr);
var n = len;
while(n > 1) {
var i = 0;
while(i < n - 1) {
if (arr[i] > arr[i+1]) {
var tmp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = tmp;
}
i += 1;
}
n -= 1;
}
return arr;
}
// Sorting works on numbers
Print(BubbleSort([ 7, 8, 9, 3, 2, 1 ]));
// as well as strings
Print(BubbleSort([
"scorn",
"by nature",
"Agave cantala",
"solvophobic",
"outpost",
"ovotestis",
"weather",
"ablation",
"boresighting",
"postfix"
]));
================================================
FILE: examples/lola/fib-iterative.lola
================================================
function Fibonacci(num)
{
var a = 0;
var b = 1;
var temp;
while (num >= 0)
{
temp = a;
a = a + b;
b = temp;
num = num - 1;
}
return b;
}
var i = 1;
while(i < 10)
{
Print("fib(", i, ") = ", Fibonacci(i));
i += 1;
}
================================================
FILE: examples/lola/forth.lola
================================================
// A small example implementing a language similar to Forth
// https://en.wikipedia.org/wiki/Forth_(programming_language)
// must be declared before it's used anywhere in the script
const binary_operators = "+-*/%";
RunForth("1 2 + 3 * 4 - =");
Print(RunForth("1 2 +"));
// Applies a singe-character binary operator to the given stack
function ApplyBinOp(stack, op)
{
const l = Length(stack);
const lhs = stack[l - 2];
const rhs = stack[l - 1];
var result;
if(op == "+") result = lhs + rhs;
else if(op == "-") result = lhs - rhs;
else if(op == "*") result = lhs * rhs;
else if(op == "/") result = lhs / rhs;
else if(op == "%") result = lhs % rhs;
var new_stack = Slice(stack, 0, l - 1);
new_stack[l - 2] = result;
return new_stack;
}
function RunForth(script, trace)
{
const items = Split(script, " ", true);
var stack = [ ];
for(command in items)
{
const l = Length(stack);
if(IndexOf(binary_operators, command) != void) {
stack = ApplyBinOp(stack, command);
}
else if(command == "=") {
const top = stack[l - 1];
stack = Slice(stack, 0, l - 1);
Print(top);
}
else {
stack = stack + [ StringToNum(command) ];
}
if(trace == true) // don't do it on false or void
Print(command, " -> ", stack);
}
const len = Length(stack);
if(len > 0)
return stack[len - 1];
}
================================================
FILE: examples/lola/game-code.lola
================================================
// This example was inspired by the Stone Script:
// https://stonestoryrpg.com/stonescript/#example
const location = GetLocation();
if(location == "rocky") {
Equip("shovel");
}
else if(location == "cave") {
SetLoadout(1);
if(GetFoe() == "bolesh") {
Equip("grap");
Equip("hammer *7 D");
}
}
else if(location == "halls") {
EquipLeft("poison wand");
EquipRight("vigor wand");
if(GetStars(location) > 5) {
Equip("vigor staff +13");
}
}
if(GetHP() < 10) {
Activate("potion");
}
================================================
FILE: examples/lola/global-setter.lola
================================================
var name;
function SetName(n)
{
name = n;
}
SetName("xq");
Print(name);
================================================
FILE: examples/lola/hello-world.lola
================================================
Print("Hello, World!");
================================================
FILE: examples/lola/reverse-array.lola
================================================
// Reverse an array
function ReverseArray(arr)
{
var i = 0;
var l = Length(arr);
while(i < l/2) {
var tmp = arr[i];
arr[i] = arr[(l - i) - 1];
arr[(l - i) - 1] = tmp;
i += 1;
}
return arr;
}
Print(ReverseArray([ 4, 3, 2, 1 ]));
================================================
FILE: examples/lola/stupid-bench.lola
================================================
var i = 0;
while(i < 100000000) {
i = i + 1;
}
Print(i);
================================================
FILE: examples/lola/sum-of-array.lola
================================================
var arr = [ 1, 2, 3 ];
var sum = 0;
for(v in arr) {
sum += v;
}
Print("Sum = ", sum);
================================================
FILE: src/README.md
================================================
# Source Structure
The project is structured into two major parts:
- [`frontend`](frontend/) is the compiler frontend which implements the command line executable
- [`library`](library/) is the implementation of both the runtime as well as the compiler. It is structured into several modules:
- [`library/compiler`](library/compiler/) is the compiler which translates LoLa source code into LoLa byte code
- [`library/runtime`](library/runtime) is the virtual machine implementation that allows running LoLa byte code
- [`library/stdlib`](library/stdlib) is the implementation of the LoLa standard library and builds on the runtime
- [`tools`](tools/) contains small tools that are used in this repo, but have no relevance to LoLa itself. One example is the markdown renderer for the website.
- [`test`](test/) contains source files that are used to test the compiler and runtime implementation. See `build.zig` and the source files in `library` for the use of those. Each file in this folder has a header that explains its usage.
================================================
FILE: src/benchmark/perf.zig
================================================
//! This tool measures LoLa performance by running a set of different benchmark files
const std = @import("std");
const builtin = @import("builtin");
const lola = @import("lola");
// This is required for the runtime library to be able to provide
// object implementations.
pub const ObjectPool = lola.runtime.ObjectPool([_]type{
lola.libs.runtime.LoLaDictionary,
lola.libs.runtime.LoLaList,
});
pub fn main() !u8 {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const argv = try std.process.argsAlloc(gpa.allocator());
defer std.process.argsFree(gpa.allocator(), argv);
if (argv.len != 3) {
return 1;
}
const date_time = std.time.epoch.EpochSeconds{
.secs = @as(u64, @intCast(std.time.timestamp())),
};
const time = date_time.getDaySeconds();
const date = date_time.getEpochDay();
const year_day = date.calculateYearDay();
const month_day = year_day.calculateMonthDay();
var files = std.ArrayList(Benchmark).init(gpa.allocator());
defer files.deinit();
var string_arena = std.heap.ArenaAllocator.init(gpa.allocator());
defer string_arena.deinit();
const date_string = try std.fmt.allocPrint(string_arena.allocator(), "{d:0>4}-{d:0>2}-{d:0>2} {d:0>2}:{d:0>2}:{d:0>2}", .{
year_day.year,
month_day.month.numeric(),
month_day.day_index,
time.getHoursIntoDay(),
time.getMinutesIntoHour(),
time.getSecondsIntoMinute(),
});
{
var dir = try std.fs.cwd().openDir(argv[1], .{ .iterate = true });
defer dir.close();
var iterator = dir.iterate();
while (try iterator.next()) |entry| {
const name = try string_arena.allocator().dupe(u8, entry.name);
const source = try dir.readFileAlloc(string_arena.allocator(), entry.name, size(1.5, .MeBi)); // 1 MB source
const target_file = try std.fmt.allocPrint(string_arena.allocator(), "{s}-{s}.csv", .{
name[0 .. name.len - std.fs.path.extension(name).len],
@tagName(builtin.mode),
});
try files.append(.{
.file_name = name,
.source_code = source,
.target_file = target_file,
});
}
}
var output_dir = try std.fs.cwd().openDir(argv[2], .{});
defer output_dir.close();
for (files.items) |benchmark| {
const result = benchmark.run(gpa.allocator()) catch |err| {
std.log.warn("failed to run benchmark {s}: {s}", .{
benchmark.file_name,
@errorName(err),
});
continue;
};
var file: std.fs.File = if (output_dir.openFile(benchmark.target_file, .{ .mode = .write_only })) |file|
file
else |_| blk: {
var file = try output_dir.createFile(benchmark.target_file, .{});
try file.writeAll("time;compile;setup;run\n");
break :blk file;
};
defer file.close();
try file.seekFromEnd(0);
try file.writer().print("{s};{d};{d};{d}\n", .{ date_string, result.compile_time, result.setup_time, result.run_time });
}
return 0;
}
pub const Unit = enum(u64) {
base = 1,
kilo = 1000,
KiBi = 1024,
mega = 1000 * 1000,
MeBi = 1024 * 1024,
giga = 1000 * 1000 * 1000,
GiBi = 1024 * 1024 * 1024,
tera = 1000 * 1000 * 1000 * 1000,
TeBi = 1024 * 1024 * 1024 * 1024,
};
pub fn size(comptime val: comptime_float, comptime unit: Unit) usize {
return @as(usize, @intFromFloat(std.math.floor(@as(f64, @as(comptime_int, @intFromEnum(unit)) * val))));
}
pub const BenchmarkResult = struct {
build_mode: std.builtin.Mode = builtin.mode,
compile_time: u128,
setup_time: u128,
run_time: u128,
};
const Benchmark = struct {
file_name: []const u8,
source_code: []const u8,
target_file: []const u8,
pub fn run(self: Benchmark, allocator: std.mem.Allocator) !BenchmarkResult {
std.log.info("Running benchmark {s}...", .{self.file_name});
var result = BenchmarkResult{
.compile_time = undefined,
.setup_time = undefined,
.run_time = undefined,
};
const compile_start = std.time.nanoTimestamp();
var diagnostics = lola.compiler.Diagnostics.init(allocator);
defer {
for (diagnostics.messages.items) |msg| {
std.debug.print("{}\n", .{msg});
}
diagnostics.deinit();
}
// This compiles a piece of source code into a compile unit.
// A compile unit is a piece of LoLa IR code with metadata for
// all existing functions, debug symbols and so on. It can be loaded into
// a environment and be executed.
var compile_unit = (try lola.compiler.compile(allocator, &diagnostics, self.file_name, self.source_code)) orelse return error.SyntaxError;
defer compile_unit.deinit();
const setup_start = std.time.nanoTimestamp();
result.compile_time = @as(u128, @intCast(setup_start - compile_start));
var pool = ObjectPool.init(allocator);
defer pool.deinit();
var env = try lola.runtime.Environment.init(allocator, &compile_unit, pool.interface());
defer env.deinit();
try env.installModule(lola.libs.std, lola.runtime.Context.null_pointer);
try env.installModule(lola.libs.runtime, lola.runtime.Context.null_pointer);
var vm = try lola.runtime.VM.init(allocator, &env);
defer vm.deinit();
const runtime_start = std.time.nanoTimestamp();
result.setup_time = @as(u128, @intCast(runtime_start - setup_start));
while (true) {
const res = try vm.execute(1_000_000);
pool.clearUsageCounters();
try pool.walkEnvironment(env);
try pool.walkVM(vm);
pool.collectGarbage();
if (res == .completed)
break;
}
result.run_time = @as(u128, @intCast(std.time.nanoTimestamp() - runtime_start));
return result;
}
};
================================================
FILE: src/benchmark/render.zig
================================================
const std = @import("std");
pub fn main() !u8 {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const alloc = gpa.allocator();
const argv = try std.process.argsAlloc(alloc);
defer std.process.argsFree(alloc, argv);
if (argv.len != 3) {
return 1;
}
var src_dir = try std.fs.cwd().openDir(argv[1], .{ .iterate = true });
defer src_dir.close();
var dst_dir = try std.fs.cwd().openDir(argv[2], .{});
defer dst_dir.close();
var data = std.ArrayList(Series).empty;
{
var iter = src_dir.iterate();
while (try iter.next()) |entry| {
if (!std.mem.eql(u8, std.fs.path.extension(entry.name), ".csv"))
continue;
const name_no_ext = entry.name[0 .. entry.name.len - 4];
const idx = std.mem.lastIndexOfScalar(u8, name_no_ext, '-') orelse continue;
var series = Series{
.benchmark = try alloc.dupe(u8, name_no_ext[0..idx]),
.mode = std.meta.stringToEnum(std.builtin.OptimizeMode, name_no_ext[idx + 1 ..]) orelse @panic("unexpected name"),
.data = undefined,
};
var file = try src_dir.openFile(entry.name, .{ .mode = .read_only });
defer file.close();
series.data = try loadSeries(alloc, file);
std.sort.block(DataPoint, series.data, {}, orderDataPoint);
try data.append(alloc, series);
}
}
try renderSeriesSet(dst_dir, "compile-ReleaseSafe.svg", data.items, "compile_time", filterReleaseSafe);
try renderSeriesSet(dst_dir, "setup-ReleaseSafe.svg", data.items, "setup_time", filterReleaseSafe);
try renderSeriesSet(dst_dir, "run-ReleaseSafe.svg", data.items, "run_time", filterReleaseSafe);
try renderSeriesSet(dst_dir, "compile-ReleaseSmall.svg", data.items, "compile_time", filterReleaseSmall);
try renderSeriesSet(dst_dir, "setup-ReleaseSmall.svg", data.items, "setup_time", filterReleaseSmall);
try renderSeriesSet(dst_dir, "run-ReleaseSmall.svg", data.items, "run_time", filterReleaseSmall);
try renderSeriesSet(dst_dir, "compile-ReleaseFast.svg", data.items, "compile_time", filterReleaseFast);
try renderSeriesSet(dst_dir, "setup-ReleaseFast.svg", data.items, "setup_time", filterReleaseFast);
try renderSeriesSet(dst_dir, "run-ReleaseFast.svg", data.items, "run_time", filterReleaseFast);
return 0;
}
pub fn renderSeriesSet(dst_dir: std.fs.Dir, file_name: []const u8, all_series: []Series, comptime field: []const u8, comptime filter: fn (series: Series) bool) !void {
var file = try dst_dir.createFile(file_name, .{});
defer file.close();
var writer_buffer: [4096]u8 = undefined;
var file_writer = file.writer(&writer_buffer);
const writer = &file_writer.interface;
var start_time: u128 = std.math.maxInt(u128);
var end_time: u128 = 0;
var high: f32 = 0;
const scale_base = 5;
for (all_series) |series| {
if (filter(series)) {
start_time = @min(start_time, series.data[0].date.getLinearSortVal());
end_time = @max(end_time, series.data[series.data.len - 1].date.getLinearSortVal());
for (series.data) |dp| {
high = @max(high, @as(f32, @floatFromInt(@field(dp, field))));
}
}
}
high = std.math.pow(f32, scale_base, @ceil(std.math.log(f32, scale_base, 1.3 * high)));
const time_range = end_time - start_time;
const size_x: f32 = 350;
const size_y: f32 = 200;
const legend_size: f32 = 50;
const viewport_size: f32 = size_x - legend_size;
try writer.print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", .{});
try writer.print("<svg version=\"1.1\" viewBox=\"0 0 {d} {d}\" xmlns=\"http://www.w3.org/2000/svg\">\n", .{
size_x,
size_y,
});
const color_palette = [_][]const u8{
"#442434",
"#30346d",
"#4e4a4e",
"#854c30",
"#346524",
"#d04648",
"#757161",
"#597dce",
"#d27d2c",
"#8595a1",
"#6daa2c",
"#d2aa99",
"#6dc2ca",
"#dad45e",
};
var index: u32 = 0;
for (all_series) |series| {
if (filter(series)) {
const color = color_palette[index % color_palette.len];
try writer.print(
\\ <text x="{d:.3}" y="{d:.3}" fill="{s}" font-family="sans-serif" font-size="5" xml:space="preserve">{s}</text>
\\
, .{
viewport_size + 5,
10 + 7 * index,
color,
series.benchmark,
});
try writer.print(" <path d=\"M", .{});
for (series.data) |dp| {
const dx = viewport_size * @as(f32, @floatFromInt(dp.date.getLinearSortVal() - start_time)) / @as(f32, @floatFromInt(time_range));
const dy = size_y * (1.0 - @as(f32, @floatFromInt(@field(dp, field))) / high);
try writer.print(" {d:.4} {d:.4}", .{ dx, dy });
}
try writer.print("\" fill=\"none\" stroke=\"{s}\" stroke-width=\"1.00\" />\n", .{
color,
});
index += 1;
}
}
try writer.print("</svg>\n", .{});
try writer.flush();
}
fn filterReleaseSafe(series: Series) bool {
return (series.mode == .ReleaseSafe);
}
fn filterReleaseSmall(series: Series) bool {
return (series.mode == .ReleaseSmall);
}
fn filterReleaseFast(series: Series) bool {
return (series.mode == .ReleaseFast);
}
fn orderDataPoint(_: void, lhs: DataPoint, rhs: DataPoint) bool {
return lhs.date.getLinearSortVal() < rhs.date.getLinearSortVal();
}
pub const Date = struct {
year: u32,
day: u8,
month: u8,
hour: u8,
minute: u8,
second: u8,
pub fn getLinearSortVal(date: Date) u64 {
return 1 * @as(u64, date.second) +
1_00 * @as(u64, date.minute) +
1_00_00 * @as(u64, date.hour) +
1_00_00_00 * @as(u64, date.month) +
1_00_00_00_00 * @as(u64, date.day) +
1_00_00_00_000 * @as(u64, date.year);
}
};
pub const DataPoint = struct {
date: Date,
compile_time: u64,
setup_time: u64,
run_time: u64,
};
pub const Series = struct {
benchmark: []const u8,
mode: std.builtin.OptimizeMode,
data: []DataPoint,
};
pub fn loadSeries(allocator: std.mem.Allocator, file: std.fs.File) ![]DataPoint {
var line_buffer: [4096]u8 = undefined;
var file_reader = file.reader(&line_buffer);
const reader = &file_reader.interface;
const first_line = reader.takeDelimiterExclusive('\n') catch |e| if (e == error.EndOfFile) return error.UnexpectedData else return e;
if (!std.mem.eql(u8, first_line, "time;compile;setup;run"))
return error.UnexpectedData;
var data_set = std.ArrayList(DataPoint).empty;
defer data_set.deinit(allocator);
while (true) {
const line: []u8 = reader.takeDelimiterExclusive('\n') catch |e| {
if (e == error.EndOfStream) break;
return e;
};
if (line.len == 0)
continue;
var iter = std.mem.splitScalar(u8, line, ';');
const time_str = iter.next() orelse return error.UnexpectedData;
const compile_str = try std.fmt.parseInt(u64, iter.next() orelse return error.UnexpectedData, 10);
const setup_str = try std.fmt.parseInt(u64, iter.next() orelse return error.UnexpectedData, 10);
const run_str = try std.fmt.parseInt(u64, iter.next() orelse return error.UnexpectedData, 10);
if (time_str.len != 19) return error.UnexpectedData;
try data_set.append(allocator, DataPoint{
.date = Date{
// 2022-03-14 14:25:56
.year = try std.fmt.parseInt(u32, time_str[0..4], 10),
.day = try std.fmt.parseInt(u8, time_str[5..7], 10),
.month = try std.fmt.parseInt(u8, time_str[8..10], 10),
.hour = try std.fmt.parseInt(u8, time_str[11..13], 10),
.minute = try std.fmt.parseInt(u8, time_str[14..16], 10),
.second = try std.fmt.parseInt(u8, time_str[17..19], 10),
},
.compile_time = compile_str,
.setup_time = setup_str,
.run_time = run_str,
});
}
return data_set.toOwnedSlice(allocator);
}
================================================
FILE: src/frontend/main.zig
================================================
const std = @import("std");
const builtin = @import("builtin");
const lola = @import("lola");
const args_parser = @import("args");
const build_options = @import("build_options");
var gpa_state = std.heap.GeneralPurposeAllocator(.{}).init;
const gpa = gpa_state.allocator();
// This is our global object pool that is back-referenced
// by the runtime library.
pub const ObjectPool = lola.runtime.objects.ObjectPool([_]type{
lola.libs.runtime.LoLaList,
lola.libs.runtime.LoLaDictionary,
});
pub fn main() !u8 {
defer _ = gpa_state.deinit();
var cli = args_parser.parseWithVerbForCurrentProcess(struct {}, CliVerb, gpa, .print) catch return 1;
defer cli.deinit();
const verb = cli.verb orelse {
try print_usage();
return 1;
};
switch (verb) {
.compile => |options| return try compile(options, cli.positionals),
.dump => |options| return try disassemble(options, cli.positionals),
.run => |options| return try run(options, cli.positionals),
.help => {
try print_usage();
return 0;
},
.version => {
var stdout = std.fs.File.stdout().writer(&.{});
try stdout.interface.writeAll(build_options.version ++ "\n");
return 0;
},
}
return 0;
}
pub fn print_usage() !void {
const usage_msg =
\\Usage: lola [command] [options]
\\
\\Commands:
\\ compile [source] Compiles the given source file into a module.
\\ dump [module] Disassembles the given module.
\\ run [file] Runs the given file. Both modules and source files are allowed.
\\ version Prints version number and exits.
\\
\\General Options:
\\ -o [output file] Defines the output file for the action.
\\
\\Compile Options:
\\ --verify, -v Does not emit the output file, but only runs in-memory checks.
\\ This can be used to do syntax checks of the code.
\\
\\Disassemble Options:
\\ --with-offset, -O Adds offsets to the disassembly.
\\ --with-hexdump, -b Adds the hex dump in the disassembly.
\\ --metadata Dumps information about the module itself.
\\
\\Run Options:
\\ --limit [n] Limits execution to [n] instructions, then halts.
\\ --mode [autodetect|source|module] Determines if run should interpret the file as a source file,
\\ a precompiled module or if it should autodetect the file type.
\\ --no-stdlib Removes the standard library from the environment.
\\ --no-runtime Removes the system runtime from the environment.
\\ --benchmark Runs the script 100 times, measuring the duration of each run and
\\ will print a benchmark result in the end.
\\
;
// \\ -S Intermixes the disassembly with the original source code if possible.
var stdout = std.fs.File.stderr().writer(&.{});
try stdout.interface.writeAll(usage_msg);
}
const CliVerb = union(enum) {
compile: CompileCLI,
dump: DisassemblerCLI,
run: RunCLI,
help: struct {},
version: struct {},
};
const DisassemblerCLI = struct {
output: ?[]const u8 = null,
metadata: bool = false,
@"with-offset": bool = false,
@"with-hexdump": bool = false,
// @"intermix-source": bool = false,
pub const shorthands = .{
// .S = "intermix-source",
.b = "with-hexdump",
.O = "with-offset",
.o = "output",
.m = "metadata",
};
};
fn disassemble(options: DisassemblerCLI, files: []const []const u8) !u8 {
var stream_writer = std.fs.File.stdout().writer(&.{});
if (files.len == 0) {
try print_usage();
return 1;
}
var logfile: ?std.fs.File = null;
defer if (logfile) |f|
f.close();
if (options.output) |outfile| {
logfile = try std.fs.cwd().createFile(outfile, .{
.read = false,
.truncate = true,
.exclusive = false,
});
stream_writer = logfile.?.writer(&.{});
}
const stream = &stream_writer.interface;
for (files) |arg| {
if (files.len != 1) {
try stream.print("Disassembly for {s}:\n", .{arg});
}
var arena = std.heap.ArenaAllocator.init(gpa);
defer arena.deinit();
const allocator = arena.allocator();
var cu = blk: {
var file = try std.fs.cwd().openFile(arg, .{ .mode = .read_only });
defer file.close();
var buffer: [4096]u8 = undefined;
var reader = file.reader(&buffer);
break :blk try lola.CompileUnit.loadFromStream(allocator, &reader.interface);
};
defer cu.deinit();
if (options.metadata) {
try stream.writeAll("metadata:\n");
try stream.print("\tcomment: {s}\n", .{cu.comment});
try stream.print("\tcode size: {d} bytes\n", .{cu.code.len});
try stream.print("\tnum globals: {d}\n", .{cu.globalCount});
try stream.print("\tnum temporaries: {d}\n", .{cu.temporaryCount});
try stream.print("\tnum functions: {d}\n", .{cu.functions.len});
for (cu.functions) |fun| {
try stream.print("\t\tep={X:0>4} lc={: >3} {s}\n", .{
fun.entryPoint,
fun.localCount,
fun.name,
});
}
try stream.print("\tnum debug syms: {d}\n", .{cu.debugSymbols.len});
try stream.writeAll("disassembly:\n");
}
try lola.dis.disassemble(stream, cu, lola.dis.DisassemblerOptions{
.addressPrefix = options.@"with-offset",
.hexwidth = if (options.@"with-hexdump") 8 else null,
.labelOutput = true,
.instructionOutput = true,
});
}
return 0;
}
const CompileCLI = struct {
output: ?[]const u8 = null,
verify: bool = false,
pub const shorthands = .{
.o = "output",
.v = "verify",
};
};
const ModuleBuffer = extern struct {
data: [*]u8,
length: usize,
};
fn compile(options: CompileCLI, files: []const []const u8) !u8 {
if (files.len != 1) {
try print_usage();
return 1;
}
const allocator = gpa;
const inname = files[0];
const outname = if (options.output) |name|
name
else blk: {
const name = try std.mem.concat(allocator, u8, &.{ inname, ".lm" });
break :blk name;
};
defer if (options.output == null)
allocator.free(outname);
const cu = compileFileToUnit(allocator, inname) catch |err| switch (err) {
error.CompileError => return 1,
else => |e| return e,
};
defer cu.deinit();
if (!options.verify) {
var file = try std.fs.cwd().createFile(outname, .{ .truncate = true, .read = false, .exclusive = false });
defer file.close();
var writer = file.writer(&.{});
try cu.saveToStream(&writer.interface);
}
return 0;
}
const RunCLI = struct {
limit: ?u32 = null,
mode: enum { autodetect, source, module } = .autodetect,
@"no-stdlib": bool = false,
@"no-runtime": bool = false,
benchmark: bool = false,
};
fn autoLoadModule(allocator: std.mem.Allocator, options: RunCLI, file: []const u8) !lola.CompileUnit {
return switch (options.mode) {
.autodetect => loadModuleFromFile(allocator, file) catch |err| if (err == error.InvalidFormat)
try compileFileToUnit(allocator, file)
else
return err,
.module => try loadModuleFromFile(allocator, file),
.source => try compileFileToUnit(allocator, file),
};
}
fn run(options: RunCLI, files: []const []const u8) !u8 {
if (files.len != 1) {
try print_usage();
return 1;
}
const allocator = gpa;
var cu = autoLoadModule(allocator, options, files[0]) catch |err| {
var stderr_writer = std.fs.File.stderr().writer(&.{});
const stderr = &stderr_writer.interface;
if (err == error.FileNotFound) {
try stderr.print("Could not find '{s}'. Are you sure you passed the right file?\n", .{files[0]});
return 1;
}
try stderr.writeAll(switch (options.mode) {
.autodetect => "Failed to run file: File seems not to be a compiled module or source file!\n",
.module => "Failed to run file: File seems not to be a compiled module.\n",
.source => return 1, // We already have the diagnostic output of the compiler anyways
});
if (err != error.InvalidFormat and err != error.CompileError) {
try stderr.print("The following error happened: {s}\n", .{
@errorName(err),
});
}
return 1;
};
defer cu.deinit();
var pool = ObjectPool.init(allocator);
defer pool.deinit();
var env = try lola.runtime.Environment.init(allocator, &cu, pool.interface());
defer env.deinit();
if (!options.@"no-stdlib") {
try env.installModule(lola.libs.std, lola.runtime.Context.null_pointer);
}
if (!options.@"no-runtime") {
try env.installModule(lola.libs.runtime, lola.runtime.Context.null_pointer);
// Move these two to a test runner
try env.installFunction("Expect", lola.runtime.Function.initSimpleUser(struct {
fn call(environment: *const lola.runtime.Environment, context: lola.runtime.Context, args: []const lola.runtime.value.Value) anyerror!lola.runtime.value.Value {
_ = environment;
_ = context;
if (args.len != 1)
return error.InvalidArgs;
const assertion = try args[0].toBoolean();
if (!assertion)
return error.AssertionFailed;
return .void;
}
}.call));
try env.installFunction("ExpectEqual", lola.runtime.Function.initSimpleUser(struct {
fn call(environment: *const lola.runtime.Environment, context: lola.runtime.Context, args: []const lola.runtime.value.Value) anyerror!lola.runtime.value.Value {
_ = environment;
_ = context;
if (args.len != 2)
return error.InvalidArgs;
if (!args[0].eql(args[1])) {
std.log.err("Expected {f}, got {f}\n", .{ args[1], args[0] });
return error.AssertionFailed;
}
return .void;
}
}.call));
}
var stderr_writer = std.fs.File.stderr().writer(&.{});
const stderr = &stderr_writer.interface;
if (options.benchmark == false) {
var vm = try lola.runtime.vm.VM.init(allocator, &env);
defer vm.deinit();
while (true) {
const result = vm.execute(options.limit) catch |err| {
if (builtin.mode == .Debug) {
if (@errorReturnTrace()) |err_trace| {
std.debug.dumpStackTrace(err_trace.*);
} else {
try stderr.print("Panic during execution: {s}\n", .{@errorName(err)});
}
} else {
try stderr.print("Panic during execution: {s}\n", .{@errorName(err)});
}
try stderr.print("Call stack:\n", .{});
try vm.printStackTrace(stderr);
return 1;
};
pool.clearUsageCounters();
try pool.walkEnvironment(env);
try pool.walkVM(vm);
pool.collectGarbage();
switch (result) {
.completed => return 0,
.exhausted => {
try stderr.print("Execution exhausted after {?d} instructions!\n", .{
options.limit,
});
return 1;
},
.paused => {
// continue execution here
std.Thread.sleep(100); // sleep at least 100 ns and return control to scheduler
},
}
}
} else {
var cycle: usize = 0;
var stats = lola.runtime.vm.VM.Statistics{};
var total_time: u64 = 0;
var total_timer = try std.time.Timer.start();
// Run at least one second
while ((cycle < 100) or (total_timer.read() < std.time.ns_per_s)) : (cycle += 1) {
var vm = try lola.runtime.vm.VM.init(allocator, &env);
defer vm.deinit();
var timer = try std.time.Timer.start();
emulation: while (true) {
const result = vm.execute(options.limit) catch |err| {
try stderr.print("Panic during execution: {s}\n", .{@errorName(err)});
try stderr.print("Call stack:\n", .{});
try vm.printStackTrace(stderr);
return 1;
};
pool.clearUsageCounters();
try pool.walkEnvironment(env);
try pool.walkVM(vm);
pool.collectGarbage();
switch (result) {
.completed => break :emulation,
.exhausted => {
try stderr.print("Execution exhausted after {?d} instructions!\n", .{
options.limit,
});
return 1;
},
.paused => {},
}
}
total_time += timer.lap();
stats.instructions += vm.stats.instructions;
stats.stalls += vm.stats.stalls;
}
try stderr.print(
\\Benchmark result:
\\ Number of runs: {d}
\\ Mean time: {d} µs
\\ Mean #instructions: {d}
\\ Mean #stalls: {d}
\\ Mean instruction/s: {d}
\\
, .{
cycle,
(@as(f64, @floatFromInt(total_time)) / @as(f64, @floatFromInt(cycle))) / std.time.ns_per_us,
@as(f64, @floatFromInt(stats.instructions)) / @as(f64, @floatFromInt(cycle)),
@as(f64, @floatFromInt(stats.stalls)) / @as(f64, @floatFromInt(cycle)),
std.time.ns_per_s * @as(f64, @floatFromInt(stats.instructions)) / @as(f64, @floatFromInt(total_time)),
});
}
return 0;
}
fn compileFileToUnit(allocator: std.mem.Allocator, fileName: []const u8) !lola.CompileUnit {
const source = blk: {
var file = try std.fs.cwd().openFile(fileName, .{ .mode = .read_only });
defer file.close();
var reader_buffer: [4096]u8 = undefined;
var file_reader = file.reader(&reader_buffer);
const reader = &file_reader.interface;
var array = std.ArrayList(u8).empty;
try reader.appendRemainingUnlimited(allocator, &array);
break :blk try array.toOwnedSlice(allocator);
};
defer gpa.free(source);
var diag = lola.compiler.Diagnostics.init(allocator);
defer {
for (diag.messages.items) |msg| {
std.debug.print("{f}\n", .{msg});
}
diag.deinit();
}
const seq = try lola.compiler.tokenizer.tokenize(allocator, &diag, fileName, source);
defer allocator.free(seq);
var pgm = try lola.compiler.parser.parse(allocator, &diag, seq);
defer pgm.deinit();
const successful = try lola.compiler.validate(allocator, &diag, pgm);
if (!successful)
return error.CompileError;
const compile_unit = try lola.compiler.generateIR(allocator, pgm, fileName);
errdefer compile_unit;
return compile_unit;
}
fn loadModuleFromFile(allocator: std.mem.Allocator, fileName: []const u8) !lola.CompileUnit {
var file = try std.fs.cwd().openFile(fileName, .{ .mode = .read_only });
defer file.close();
var reader_buffer: [4096]u8 = undefined;
var reader = file.reader(&reader_buffer);
return try lola.CompileUnit.loadFromStream(allocator, &reader.interface);
}
================================================
FILE: src/library/common/CompileUnit.zig
================================================
const std = @import("std");
const utility = @import("utility.zig");
// Import modules to reduce file size
// usingnamespace @import("value.zig");
/// A compiled piece of code, provides the building blocks for
/// an environment. Note that a compile unit must be instantiated
/// into an environment to be executed.
pub const CompileUnit = @This();
/// Description of a script function.
pub const Function = struct {
name: []const u8,
entryPoint: u32,
localCount: u16,
};
/// A mapping of which code portion belongs to which
/// line in the source code.
/// Lines are valid from offset until the next available symbol.
pub const DebugSymbol = struct {
/// Offset of the symbol from the start of the compiled code.
offset: u32,
/// The line number, starting at 1.
sourceLine: u32,
/// The offset into the line, starting at 1.
sourceColumn: u16,
};
arena: std.heap.ArenaAllocator,
/// Freeform file structure comment. This usually contains the file name of the compile file.
comment: []const u8,
/// Number of global (persistent) variables stored in the environment
globalCount: u16,
/// Number of temporary (local) variables in the global scope.
temporaryCount: u16,
/// The compiled binary code.
code: []u8,
/// Array of function definitions
functions: []const Function,
/// Sorted array of debug symbols
debugSymbols: []const DebugSymbol,
/// Loads a compile unit from a data stream.
pub fn loadFromStream(allocator: std.mem.Allocator, stream: *std.Io.Reader) !CompileUnit {
// var inStream = file.getInStream();
// var stream = &inStream.stream;
var header: [8]u8 = undefined;
stream.readSliceAll(&header) catch |err| switch (err) {
error.EndOfStream => return error.InvalidFormat, // file is too short!
else => return err,
};
if (!std.mem.eql(u8, &header, "LoLa\xB9\x40\x80\x5A"))
return error.InvalidFormat;
const version = try stream.takeInt(u32, .little);
if (version != 1)
return error.UnsupportedVersion;
var comment: [256]u8 = undefined;
try stream.readSliceAll(&comment);
var unit = CompileUnit{
.arena = std.heap.ArenaAllocator.init(allocator),
.globalCount = undefined,
.temporaryCount = undefined,
.code = undefined,
.functions = undefined,
.debugSymbols = undefined,
.comment = undefined,
};
errdefer unit.arena.deinit();
unit.comment = try unit.arena.allocator().dupe(u8, utility.clampFixedString(&comment));
unit.globalCount = try stream.takeInt(u16, .little);
unit.temporaryCount = try stream.takeInt(u16, .little);
const functionCount = try stream.takeInt(u16, .little);
const codeSize = try stream.takeInt(u32, .little);
const numSymbols = try stream.takeInt(u32, .little);
if (functionCount > codeSize or numSymbols > codeSize) {
// It is not reasonable to have multiple functions per
// byte of code.
// The same is valid for debug symbols.
return error.CorruptedData;
}
const functions = try unit.arena.allocator().alloc(Function, functionCount);
const code = try unit.arena.allocator().alloc(u8, codeSize);
const debugSymbols = try unit.arena.allocator().alloc(DebugSymbol, numSymbols);
for (functions) |*fun| {
var name: [128]u8 = undefined;
try stream.readSliceAll(&name);
const entryPoint = try stream.takeInt(u32, .little);
const localCount = try stream.takeInt(u16, .little);
fun.* = Function{
.name = try unit.arena.allocator().dupe(u8, utility.clampFixedString(&name)),
.entryPoint = entryPoint,
.localCount = localCount,
};
}
unit.functions = functions;
try stream.readSliceAll(code);
unit.code = code;
for (debugSymbols) |*sym| {
const offset = try stream.takeInt(u32, .little);
const sourceLine = try stream.takeInt(u32, .little);
const sourceColumn = try stream.takeInt(u16, .little);
sym.* = DebugSymbol{
.offset = offset,
.sourceLine = sourceLine,
.sourceColumn = sourceColumn,
};
}
std.sort.block(DebugSymbol, debugSymbols, {}, struct {
fn lessThan(context: void, lhs: DebugSymbol, rhs: DebugSymbol) bool {
_ = context;
return lhs.offset < rhs.offset;
}
}.lessThan);
unit.debugSymbols = debugSymbols;
return unit;
}
/// Saves a compile unit to a data stream.
pub fn saveToStream(self: CompileUnit, stream: *std.Io.Writer) !void {
try stream.writeAll("LoLa\xB9\x40\x80\x5A");
try stream.writeInt(u32, 1, .little);
try stream.writeAll(self.comment);
_ = try stream.splatByte(0, 256 - self.comment.len);
try stream.writeInt(u16, self.globalCount, .little);
try stream.writeInt(u16, self.temporaryCount, .little);
try stream.writeInt(u16, @as(u16, @intCast(self.functions.len)), .little);
try stream.writeInt(u32, @as(u32, @intCast(self.code.len)), .little);
try stream.writeInt(u32, @as(u32, @intCast(self.debugSymbols.len)), .little);
for (self.functions) |fun| {
try stream.writeAll(fun.name);
_ = try stream.splatByte(0, 128 - fun.name.len);
try stream.writeInt(u32, fun.entryPoint, .little);
try stream.writeInt(u16, fun.localCount, .little);
}
try stream.writeAll(self.code);
for (self.debugSymbols) |sym| {
try stream.writeInt(u32, sym.offset, .little);
try stream.writeInt(u32, sym.sourceLine, .little);
try stream.writeInt(u16, sym.sourceColumn, .little);
}
}
/// Searches for a debug symbol that preceeds the given address.
/// Function assumes that CompileUnit.debugSymbols is sorted
/// front-to-back by offset.
pub fn lookUp(self: CompileUnit, offset: u32) ?DebugSymbol {
if (offset >= self.code.len)
return null;
var result: ?DebugSymbol = null;
for (self.debugSymbols) |sym| {
if (sym.offset > offset)
break;
result = sym;
}
return result;
}
pub fn deinit(self: CompileUnit) void {
self.arena.deinit();
}
const serializedCompileUnit = "" // SoT
++ "LoLa\xB9\x40\x80\x5A" // Header
++ "\x01\x00\x00\x00" // Version
++ "Made with NativeLola.zig!" ++ ("\x00" ** (256 - 25)) // Comment
++ "\x03\x00" // globalCount
++ "\x55\x11" // temporaryCount
++ "\x02\x00" // functionCount
++ "\x05\x00\x00\x00" // codeSize
++ "\x03\x00\x00\x00" // numSymbols
++ "Function1" ++ ("\x00" ** (128 - 9)) // Name
++ "\x00\x00\x00\x00" // entryPoint
++ "\x01\x00" // localCount
++ "Function2" ++ ("\x00" ** (128 - 9)) // Name
++ "\x10\x10\x00\x00" // entryPoint
++ "\x02\x00" // localCount
++ "Hello" // code
++ "\x01\x00\x00\x00" ++ "\x01\x00\x00\x00" ++ "\x01\x00" // dbgSym1
++ "\x02\x00\x00\x00" ++ "\x02\x00\x00\x00" ++ "\x04\x00" // dbgSym2
++ "\x04\x00\x00\x00" ++ "\x03\x00\x00\x00" ++ "\x08\x00" // dbgSym3
;
test "CompileUnit I/O" {
var sliceInStream = std.Io.Reader.fixed(serializedCompileUnit);
const cu = try CompileUnit.loadFromStream(std.testing.allocator, &sliceInStream);
defer cu.deinit();
std.debug.assert(std.mem.eql(u8, cu.comment, "Made with NativeLola.zig!"));
std.debug.assert(cu.globalCount == 3);
std.debug.assert(cu.temporaryCount == 0x1155);
std.debug.assert(std.mem.eql(u8, cu.code, "Hello"));
std.debug.assert(cu.functions.len == 2);
std.debug.assert(cu.debugSymbols.len == 3);
std.debug.assert(std.mem.eql(u8, cu.functions[0].name, "Function1"));
std.debug.assert(cu.functions[0].entryPoint == 0x00000000);
std.debug.assert(cu.functions[0].localCount == 1);
std.debug.assert(std.mem.eql(u8, cu.functions[1].name, "Function2"));
std.debug.assert(cu.functions[1].entryPoint == 0x00001010);
std.debug.assert(cu.functions[1].localCount == 2);
std.debug.assert(cu.debugSymbols[0].offset == 1);
std.debug.assert(cu.debugSymbols[0].sourceLine == 1);
std.debug.assert(cu.debugSymbols[0].sourceColumn == 1);
std.debug.assert(cu.debugSymbols[1].offset == 2);
std.debug.assert(cu.debugSymbols[1].sourceLine == 2);
std.debug.assert(cu.debugSymbols[1].sourceColumn == 4);
std.debug.assert(cu.debugSymbols[2].offset == 4);
std.debug.assert(cu.debugSymbols[2].sourceLine == 3);
std.debug.assert(cu.debugSymbols[2].sourceColumn == 8);
var storage: [serializedCompileUnit.len]u8 = undefined;
var sliceOutStream = std.Io.Writer.fixed(&storage);
try cu.saveToStream(&sliceOutStream);
std.debug.assert(sliceOutStream.buffered().len == serializedCompileUnit.len);
std.debug.assert(std.mem.eql(u8, sliceOutStream.buffered(), serializedCompileUnit));
}
test "CompileUnit.lookUp" {
var sliceInStream = std.Io.Reader.fixed(serializedCompileUnit);
const cu = try CompileUnit.loadFromStream(std.testing.allocator, &sliceInStream);
defer cu.deinit();
std.debug.assert(cu.lookUp(0) == null); // no debug symbol before 1
std.debug.assert(cu.lookUp(1).?.sourceLine == 1);
std.debug.assert(cu.lookUp(2).?.sourceLine == 2);
std.debug.assert(cu.lookUp(3).?.sourceLine == 2);
std.debug.assert(cu.lookUp(4).?.sourceLine == 3);
std.debug.assert(cu.lookUp(5) == null); // no debug symbol after end-of-code
}
================================================
FILE: src/library/common/Decoder.zig
================================================
const std = @import("std");
const utility = @import("utility.zig");
// Import modules to reduce file size
const ir = @import("ir.zig");
const CompileUnit = @import("CompileUnit.zig");
/// A struct that allows decoding data from LoLa IR code.
pub const Decoder = @This();
data: []const u8,
// we are restricted to 4GB code size in the binary format, the decoder itself can use the same restriction
offset: u32,
pub fn init(source: []const u8) Decoder {
return Decoder{
.data = source,
.offset = 0,
};
}
pub fn isEof(self: Decoder) bool {
return self.offset >= self.data.len;
}
pub fn readRaw(self: *Decoder, dest: []u8) !void {
if (self.offset == self.data.len)
return error.EndOfStream;
if (self.offset + dest.len > self.data.len)
return error.NotEnoughData;
@memcpy(dest, self.data[self.offset .. self.offset + dest.len]);
self.offset += @as(u32, @intCast(dest.len));
}
pub fn readBytes(self: *Decoder, comptime count: comptime_int) ![count]u8 {
var data: [count]u8 = undefined;
try self.readRaw(&data);
return data;
}
/// Reads a value of the given type from the data stream.
/// Allowed types are `u8`, `u16`, `u32`, `f64`, `Instruction`.
pub fn read(self: *Decoder, comptime T: type) !T {
if (T == ir.Instruction) {
return readInstruction(self);
}
const data = try self.readBytes(@sizeOf(T));
switch (T) {
u8, u16, u32 => return std.mem.readInt(T, &data, .little),
f64 => return @as(f64, @bitCast(data)),
ir.InstructionName => return try std.meta.intToEnum(ir.InstructionName, data[0]),
else => @compileError("Unsupported type " ++ @typeName(T) ++ " for Decoder.read!"),
}
}
/// Reads a variable-length string from the data stream.
/// Note that the returned handle is only valid as long as Decoder.data is valid.
pub fn readVarString(self: *Decoder) ![]const u8 {
const len = try self.read(u16);
if (self.offset + len > self.data.len)
return error.NotEnoughData; // this is when a string tells you it's longer than the actual data storage.
const string = self.data[self.offset .. self.offset + len];
self.offset += len;
return string;
}
/// Reads a fixed-length string from the data. The string may either be 0-terminated
/// or use the available length completly.
/// Note that the returned handle is only valid as long as Decoder.data is valid.
pub fn readFixedString(self: *Decoder, comptime len: comptime_int) ![]const u8 {
if (self.offset == self.data.len)
return error.EndOfStream;
if (self.offset + len > self.data.len)
return error.NotEnoughData;
const fullMem = self.data[self.offset .. self.offset + len];
self.offset += len;
return utility.clampFixedString(fullMem);
}
/// Reads a a full instruction from the source.
/// This will provide full decoding and error checking.
fn readInstruction(self: *Decoder) !ir.Instruction {
if (self.isEof())
return error.EndOfStream;
const instr = try self.read(ir.InstructionName);
inline for (std.meta.fields(ir.Instruction)) |fld| {
if (instr == @field(ir.InstructionName, fld.name)) {
if (fld.type == ir.Instruction.Deprecated) {
return error.DeprecatedInstruction;
} else if (fld.type == ir.Instruction.NoArg) {
return @unionInit(ir.Instruction, fld.name, .{});
} else if (fld.type == ir.Instruction.CallArg) {
const fun = self.readVarString() catch |err| return mapEndOfStreamToNotEnoughData(err);
const argc = self.read(u8) catch |err| return mapEndOfStreamToNotEnoughData(err);
return @unionInit(ir.Instruction, fld.name, ir.Instruction.CallArg{
.function = fun,
.argc = argc,
});
} else {
const ValType = std.meta.fieldInfo(fld.type, .value).type;
if (ValType == []const u8) {
return @unionInit(ir.Instruction, fld.name, fld.type{
.value = self.readVarString() catch |err| return mapEndOfStreamToNotEnoughData(err),
});
} else {
return @unionInit(ir.Instruction, fld.name, fld.type{
.value = self.read(ValType) catch |err| return mapEndOfStreamToNotEnoughData(err),
});
}
}
}
}
return error.InvalidInstruction;
}
fn mapEndOfStreamToNotEnoughData(err: anytype) @TypeOf(err) {
return switch (err) {
error.EndOfStream => error.NotEnoughData,
else => err,
};
}
// zig fmt: off
const decoderTestBlob = [_]u8{
1,2,3, // "[3]u8"
8, // u8,
16, 0, // u16
32, 0, 0, 0, // u32
12, // Instruction "add"
5, 0, 'H', 'e', 'l', 'l', 'o', // String(*) "Hello"
0x1F, 0x85, 0xEB, 0x51, 0xB8, 0x1E, 0x09, 0x40, // f64 = 3.14000000000000012434 == 0x40091EB851EB851F
'B', 'y', 'e', 0, 0, 0, 0, 0, // String(8) "Bye"
};
// zig fmt: on
test "Decoder" {
var decoder = Decoder.init(&decoderTestBlob);
std.debug.assert(std.mem.eql(u8, &(try decoder.readBytes(3)), &[3]u8{ 1, 2, 3 }));
std.debug.assert((try decoder.read(u8)) == 8);
std.debug.assert((try decoder.read(u16)) == 16);
std.debug.assert((try decoder.read(u32)) == 32);
std.debug.assert((try decoder.read(ir.InstructionName)) == .add);
std.debug.assert(std.mem.eql(u8, try decoder.readVarString(), "Hello"));
std.debug.assert((try decoder.read(f64)) == 3.14000000000000012434);
std.debug.assert(std.mem.eql(u8, try decoder.readFixedString(8), "Bye"));
if (decoder.readBytes(1)) |_| {
std.debug.assert(false);
} else |err| {
std.debug.assert(err == error.EndOfStream);
}
}
test "Decoder.NotEnoughData" {
const blob = [_]u8{1};
var decoder = Decoder.init(&blob);
if (decoder.read(u16)) |_| {
std.debug.assert(false);
} else |err| {
std.debug.assert(err == error.NotEnoughData);
}
}
test "Decoder.NotEnoughData (string)" {
const blob = [_]u8{ 1, 0 };
var decoder = Decoder.init(&blob);
if (decoder.readVarString()) |_| {
std.debug.assert(false);
} else |err| {
std.debug.assert(err == error.NotEnoughData);
}
}
test "Decoder.read(Instruction)" {
const Pattern = struct {
const ResultType = @typeInfo(@TypeOf(Decoder.readInstruction)).@"fn".return_type orelse unreachable;
text: []const u8,
instr: ResultType,
fn isMatch(self: @This(), testee: ResultType) bool {
if (self.instr) |a_p| {
if (testee) |b_p| return eql(a_p, b_p) else |_| return false;
} else |a_e| {
if (testee) |_| return false else |b_e| return a_e == b_e;
}
}
fn eql(a: ir.Instruction, b: ir.Instruction) bool {
@setEvalBranchQuota(5000);
const activeField = @as(ir.InstructionName, a);
if (activeField != @as(ir.InstructionName, b))
return false;
inline for (std.meta.fields(ir.InstructionName)) |fld| {
if (activeField == @field(ir.InstructionName, fld.name)) {
const FieldType = @TypeOf(@field(a, fld.name));
const lhs = @field(a, fld.name);
const rhs = @field(b, fld.name);
if ((FieldType == ir.Instruction.Deprecated) or (FieldType == ir.Instruction.NoArg)) {
return true;
} else if (FieldType == ir.Instruction.CallArg) {
return lhs.argc == rhs.argc and std.mem.eql(u8, lhs.function, rhs.function);
} else {
const ValType = std.meta.fieldInfo(FieldType, .value).type;
if (ValType == []const u8) {
return std.mem.eql(u8, lhs.value, rhs.value);
} else {
return lhs.value == rhs.value;
}
}
}
}
unreachable;
}
};
const Instruction = ir.Instruction;
const patterns = [_]Pattern{
.{ .text = "\x00", .instr = Instruction{ .nop = .{} } },
.{ .text = "\x01", .instr = error.DeprecatedInstruction },
.{ .text = "\x02", .instr = error.DeprecatedInstruction },
.{ .text = "\x03", .instr = error.DeprecatedInstruction },
.{ .text = "\x06\x03\x00ABC", .instr = Instruction{ .push_str = .{ .value = "ABC" } } },
.{ .text = "\x07\x00\x00\x00\x00\x00\x00\x00\x00", .instr = Instruction{ .push_num = .{ .value = 0 } } },
.{ .text = "\x08\x00\x10", .instr = Instruction{ .array_pack = .{ .value = 0x1000 } } },
.{ .text = "\x09\x01\x00x\x01", .instr = Instruction{ .call_fn = .{ .function = "x", .argc = 1 } } },
.{ .text = "\x0A\x02\x00yz\x03", .instr = Instruction{ .call_obj = .{ .function = "yz", .argc = 3 } } },
.{ .text = "\x0B", .instr = Instruction{ .pop = .{} } },
.{ .text = "\x0C", .instr = Instruction{ .add = .{} } },
.{ .text = "\x0D", .instr = Instruction{ .sub = .{} } },
.{ .text = "\x0E", .instr = Instruction{ .mul = .{} } },
.{ .text = "\x0F", .instr = Instruction{ .div = .{} } },
.{ .text = "\x10", .instr = Instruction{ .mod = .{} } },
.{ .text = "\x11", .instr = Instruction{ .bool_and = .{} } },
.{ .text = "\x12", .instr = Instruction{ .bool_or = .{} } },
.{ .text = "\x13", .instr = Instruction{ .bool_not = .{} } },
.{ .text = "\x14", .instr = Instruction{ .negate = .{} } },
.{ .text = "\x15", .instr = Instruction{ .eq = .{} } },
.{ .text = "\x16", .instr = Instruction{ .neq = .{} } },
.{ .text = "\x17", .instr = Instruction{ .less_eq = .{} } },
.{ .text = "\x18", .instr = Instruction{ .greater_eq = .{} } },
.{ .text = "\x19", .instr = Instruction{ .less = .{} } },
.{ .text = "\x1A", .instr = Instruction{ .greater = .{} } },
.{ .text = "\x1B\x00\x11\x22\x33", .instr = Instruction{ .jmp = .{ .value = 0x33221100 } } },
.{ .text = "\x1C\x44\x33\x22\x11", .instr = Instruction{ .jnf = .{ .value = 0x11223344 } } },
.{ .text = "\x1D", .instr = Instruction{ .iter_make = .{} } },
.{ .text = "\x1E", .instr = Instruction{ .iter_next = .{} } },
.{ .text = "\x1F", .instr = Instruction{ .array_store = .{} } },
.{ .text = "\x20", .instr = Instruction{ .array_load = .{} } },
.{ .text = "\x21", .instr = Instruction{ .ret = .{} } },
.{ .text = "\x22\xDE\xBA", .instr = Instruction{ .store_local = .{ .value = 0xBADE } } },
.{ .text = "\x23\xFE\xAF", .instr = Instruction{ .load_local = .{ .value = 0xAFFE } } },
.{ .text = "\x25", .instr = Instruction{ .retval = .{} } },
.{ .text = "\x26\x00\x12\x34\x56", .instr = Instruction{ .jif = .{ .value = 0x56341200 } } },
.{ .text = "\x27\x34\x12", .instr = Instruction{ .store_global_idx = .{ .value = 0x1234 } } },
.{ .text = "\x28\x21\x43", .instr = Instruction{ .load_global_idx = .{ .value = 0x4321 } } },
.{ .text = "\x29", .instr = Instruction{ .push_true = .{} } },
.{ .text = "\x2A", .instr = Instruction{ .push_false = .{} } },
.{ .text = "\x2B", .instr = Instruction{ .push_void = .{} } },
.{ .text = "", .instr = error.EndOfStream },
.{ .text = "\x26", .instr = error.NotEnoughData },
.{ .text = "\x09\xFF", .instr = error.NotEnoughData },
.{ .text = "\x26\x00\x00", .instr = error.NotEnoughData },
.{ .text = "\x09\xFF\xFF", .instr = error.NotEnoughData },
};
for (patterns) |pattern| {
var decoder = Decoder.init(pattern.text);
const instruction = decoder.read(Instruction);
if (!pattern.isMatch(instruction)) {
std.log.err("expected {!}, got {!}\n", .{ pattern.instr, instruction });
return error.UnexpectedToken;
}
}
}
================================================
FILE: src/library/common/disassembler.zig
================================================
const std = @import("std");
const CompileUnit = @import("CompileUnit.zig");
const Decoder = @import("Decoder.zig");
const ir = @import("ir.zig");
pub const DisassemblerOptions = struct {
/// Prefix each line of the disassembly with the hexadecimal address.
addressPrefix: bool = false,
/// If set, a hexdump with both hex- and ascii display will be emitted.
/// Each line of text will contain `hexwidth` number of bytes.
hexwidth: ?usize = null,
/// If set to `true`, the output will contain a line with the
/// name of function that starts at this offset. This option
/// is set by default.
labelOutput: bool = true,
/// If set to `true`, the disassembled instruction will be emitted.
/// This is set by default.
instructionOutput: bool = true,
};
/// Disassembles a given compile unit into a text stream.
/// The output of the disassembler is adjustable to different formats.
/// If all output is disabled in the config, this function can also be used
/// to verify that a compile unit can be parsed completly without any problems.
pub fn disassemble(stream: *std.Io.Writer, cu: CompileUnit, options: DisassemblerOptions) !void {
var decoder = Decoder.init(cu.code);
const anyOutput = options.addressPrefix or options.labelOutput or options.instructionOutput or (options.hexwidth != null);
if (options.addressPrefix)
try stream.print("{X:0>6}\t", .{decoder.offset});
if (options.labelOutput)
try stream.writeAll("<main>:\n");
while (!decoder.isEof()) {
if (options.labelOutput) {
for (cu.functions) |fun| {
if (fun.entryPoint == decoder.offset) {
if (options.addressPrefix)
try stream.print("{X:0>6}\t", .{decoder.offset});
try stream.print("{s}:\n", .{fun.name});
}
}
}
if (options.addressPrefix)
try stream.print("{X:0>6}\t", .{decoder.offset});
const start = decoder.offset;
const instr = try decoder.read(ir.Instruction);
const end = decoder.offset;
if (options.hexwidth) |hw| {
try writeHexDump(stream, decoder.data, start, end, hw);
}
if (options.instructionOutput) {
try stream.writeAll("\t");
try stream.writeAll(@tagName(@as(ir.InstructionName, instr)));
inline for (std.meta.fields(ir.Instruction)) |fld| {
const instr_name = @field(ir.InstructionName, fld.name);
if (instr == instr_name) {
if (fld.type == ir.Instruction.Deprecated) {
// no-op
} else if (fld.type == ir.Instruction.NoArg) {
// no-op
} else if (fld.type == ir.Instruction.CallArg) {
const args = @field(instr, fld.name);
try stream.print(" {s} {d}", .{ args.function, args.argc });
} else {
if (@TypeOf(@field(instr, fld.name).value) == f64) {
try stream.print(" {d}", .{@field(instr, fld.name).value});
} else if (instr_name == .jif or instr_name == .jmp or instr_name == .jnf) {
try stream.print(" 0x{X}", .{@field(instr, fld.name).value});
} else {
try stream.print(" {any}", .{@field(instr, fld.name).value});
}
}
}
}
}
if (anyOutput)
try stream.writeAll("\n");
if (options.hexwidth) |hw| {
var cursor = start + hw;
var paddedEnd = start + 2 * hw;
while (paddedEnd < end + hw) : (paddedEnd += hw) {
if (options.addressPrefix)
try stream.print("{X:0>6}\t", .{cursor});
try writeHexDump(stream, decoder.data, cursor, end, hw);
cursor += hw;
try stream.writeAll("\n");
}
}
}
}
fn writeHexDump(stream: anytype, data: []const u8, begin: usize, end: usize, width: usize) !void {
var offset_hex = begin;
while (offset_hex < begin + width) : (offset_hex += 1) {
if (offset_hex < end) {
try stream.print("{X:0>2} ", .{data[offset_hex]});
} else {
try stream.writeAll(" ");
}
}
try stream.writeAll("|");
var offset_bin = begin;
while (offset_bin < begin + width) : (offset_bin += 1) {
if (offset_bin < end) {
if (std.ascii.isPrint(data[offset_bin])) {
try stream.print("{c}", .{data[offset_bin]});
} else {
try stream.writeAll(".");
}
} else {
try stream.writeAll(" ");
}
}
try stream.writeAll("|");
}
test "disassemble" {
// dummy test
_ = disassemble;
}
================================================
FILE: src/library/common/interface.zig
================================================
const std = @import("std");
const Struct = std.builtin.Type.Struct;
const StructField = std.builtin.Type.StructField;
pub const Self = struct {};
fn MapSelfType(comptime T: type, comptime NewSelf: type) type {
return if (T == Self)
@compileError("Cannot use Self without pointer")
else if (T == *Self)
return *NewSelf
else if (T == *const Self)
return *const NewSelf
else
T;
}
fn GeneralizedFunc(comptime F: type) type {
const f_in = @typeInfo(F).@"fn";
var f_out = f_in;
f_out.params = &.{};
for (f_in.params) |pi| {
var po = pi;
po.type = MapSelfType(
pi.type orelse @compileError("No support for generic parameters!"),
anyopaque,
);
f_out.params = f_out.params ++ [_]std.builtin.Type.Fn.Param{po};
}
return @Type(.{ .@"fn" = f_out });
}
pub fn Interfaces(comptime spec: anytype) type {
const Parameter = struct {
source_type: type,
generic_type: type,
is_mapped: bool,
};
const Function = struct {
name: [:0]const u8,
spec_type: type,
generic_type: type,
spec_return: type,
generic_return: type,
return_is_mapped: bool,
params: []const Parameter,
};
const spec_fields = comptime std.meta.fields(@TypeOf(spec));
const functions = comptime blk: {
var funcs: [spec_fields.len]Function = undefined;
for (&funcs, spec_fields) |*fun, *sf| {
const info = @typeInfo(@field(spec, sf.name)).@"fn";
fun.* = Function{
.name = sf.name,
.spec_type = @field(spec, sf.name),
.generic_type = GeneralizedFunc(@field(spec, sf.name)),
.spec_return = info.return_type.?,
.generic_return = MapSelfType(info.return_type.?, anyopaque),
.params = &.{},
.return_is_mapped = (info.return_type.? != MapSelfType(info.return_type.?, anyopaque)),
};
for (@typeInfo(fun.spec_type).@"fn".params, @typeInfo(fun.generic_type).@"fn".params) |sfn, gfn| {
const param: Parameter = .{
.source_type = sfn.type.?,
.generic_type = gfn.type.?,
.is_mapped = (sfn.type.? != gfn.type.?),
};
fun.params = fun.params ++ [1]Parameter{param};
}
}
break :blk funcs;
};
return struct {
const Intf = @This();
pub const VTable: type = blk: {
var vti = Struct{
.backing_integer = null,
.decls = &.{},
.fields = &.{},
.is_tuple = false,
.layout = .auto,
};
for (functions) |func| {
vti.fields = vti.fields ++ &[_]StructField{
.{
.name = func.name,
.type = *const func.generic_type,
.default_value_ptr = null,
.is_comptime = false,
.alignment = @alignOf(*const func.generic_type),
},
};
}
break :blk @Type(.{
.@"struct" = vti,
});
};
pub fn createVTable(comptime T: type) *const VTable {
const Buffer = struct {
const vtable: VTable = blk: {
var vt: VTable = undefined;
for (functions) |func| {
const src_func = @field(T, func.name);
const F = struct {
fn MappedArg(comptime i: comptime_int) type {
// @compileLog(func.name, i, func.params[i].generic_type, func.params[i].is_mapped);
return if (func.params[i].is_mapped)
MapSelfType(func.params[i].source_type, anyopaque)
else
func.params[i].generic_type;
}
fn mapArg(comptime i: comptime_int, value: MappedArg(i)) MapSelfType(func.params[i].source_type, T) {
return if (func.params[i].is_mapped)
@ptrCast(@alignCast(value))
else
return value;
}
fn mapResult(value: MapSelfType(func.spec_return, anyopaque)) MapSelfType(func.spec_return, T) {
return if (func.return_is_mapped)
@ptrCast(@alignCast(value))
else
return value;
}
const invoke = CallTranslator(
src_func,
MappedArg,
mapArg,
MapSelfType(func.spec_return, T),
mapResult,
).invoke;
};
@field(vt, func.name) = F.invoke;
}
break :blk vt;
};
};
return &Buffer.vtable;
}
};
}
fn CallTranslator(
comptime target_func: anytype,
comptime MappedArg: anytype,
comptime mapArg: anytype,
comptime MappedResult: type,
comptime mapResult: anytype,
) type {
const fi = @typeInfo(@TypeOf(target_func)).@"fn";
return switch (fi.params.len) {
0 => struct {
pub fn invoke() MappedResult {
return mapResult(target_func());
}
},
1 => struct {
pub fn invoke(a0: MappedArg(0)) MappedResult {
return mapResult(target_func(mapArg(0, a0)));
}
},
2 => struct {
pub fn invoke(a0: MappedArg(0), a1: MappedArg(1)) MappedResult {
return mapResult(target_func(mapArg(0, a0), mapArg(1, a1)));
}
},
3 => struct {
pub fn invoke(a0: MappedArg(0), a1: MappedArg(1), a2: MappedArg(2)) MappedResult {
return mapResult(target_func(mapArg(0, a0), mapArg(1, a1), mapArg(2, a2)));
}
},
4 => struct {
pub fn invoke(a0: MappedArg(0), a1: MappedArg(1), a2: MappedArg(2), a3: MappedArg(3)) MappedResult {
return mapResult(target_func(mapArg(0, a0), mapArg(1, a1), mapArg(2, a2), mapArg(3, a3)));
}
},
5 => struct {
pub fn invoke(a0: MappedArg(0), a1: MappedArg(1), a2: MappedArg(2), a3: MappedArg(3), a4: MappedArg(4)) MappedResult {
return mapResult(target_func(mapArg(0, a0), mapArg(1, a1), mapArg(2, a2), mapArg(3, a3), mapArg(4, a4)));
}
},
6 => struct {
pub fn invoke(a0: MappedArg(0), a1: MappedArg(1), a2: MappedArg(2), a3: MappedArg(3), a4: MappedArg(4), a5: MappedArg(5)) MappedResult {
return mapResult(target_func(mapArg(0, a0), mapArg(1, a1), mapArg(2, a2), mapArg(3, a3), mapArg(4, a4), mapArg(5, a5)));
}
},
7 => struct {
pub fn invoke(a0: MappedArg(0), a1: MappedArg(1), a2: MappedArg(2), a3: MappedArg(3), a4: MappedArg(4), a5: MappedArg(5), a6: MappedArg(6)) MappedResult {
return mapResult(target_func(mapArg(0, a0), mapArg(1, a1), mapArg(2, a2), mapArg(3, a3), mapArg(4, a4), mapArg(5, a5), mapArg(6, a6)));
}
},
8 => struct {
pub fn invoke(a0: MappedArg(0), a1: MappedArg(1), a2: MappedArg(2), a3: MappedArg(3), a4: MappedArg(4), a5: MappedArg(5), a6: MappedArg(6), a7: MappedArg(7)) MappedResult {
return mapResult(target_func(mapArg(0, a0), mapArg(1, a1), mapArg(2, a2), mapArg(3, a3), mapArg(4, a4), mapArg(5, a5), mapArg(6, a6), mapArg(7, a7)));
}
},
9 => struct {
pub fn invoke(a0: MappedArg(0), a1: MappedArg(1), a2: MappedArg(2), a3: MappedArg(3), a4: MappedArg(4), a5: MappedArg(5), a6: MappedArg(6), a7: MappedArg(7), a8: MappedArg(8)) MappedResult {
return mapResult(target_func(mapArg(0, a0), mapArg(1, a1), mapArg(2, a2), mapArg(3, a3), mapArg(4, a4), mapArg(5, a5), mapArg(6, a6), mapArg(7, a7), mapArg(8, a8)));
}
},
10 => struct {
pub fn invoke(a0: MappedArg(0), a1: MappedArg(1), a2: MappedArg(2), a3: MappedArg(3), a4: MappedArg(4), a5: MappedArg(5), a6: MappedArg(6), a7: MappedArg(7), a8: MappedArg(8), a9: MappedArg(9)) MappedResult {
return mapResult(target_func(mapArg(0, a0), mapArg(1, a1), mapArg(2, a2), mapArg(3, a3), mapArg(4, a4), mapArg(5, a5), mapArg(6, a6), mapArg(7, a7), mapArg(8, a8), mapArg(9, a9)));
}
},
else => @compileError("Functions with more than {} args aren't supported yet!"),
};
}
================================================
FILE: src/library/common/ir.zig
================================================
/// This enumeration contains all possible instructions and assigns each a value.
pub const InstructionName = enum(u8) {
nop = 0,
scope_push = 1, // deprecated
scope_pop = 2, // deprecated
declare = 3, // deprecated
store_global_name = 4, // deprecated
load_global_name = 5, // deprecated
push_str = 6,
push_num = 7,
array_pack = 8,
call_fn = 9,
call_obj = 10,
pop = 11,
add = 12,
sub = 13,
mul = 14,
div = 15,
mod = 16,
bool_and = 17,
bool_or = 18,
bool_not = 19,
negate = 20,
eq = 21,
neq = 22,
less_eq = 23,
greater_eq = 24,
less = 25,
greater = 26,
jmp = 27,
jnf = 28,
iter_make = 29,
iter_next = 30,
array_store = 31,
array_load = 32,
ret = 33,
store_local = 34,
load_local = 35,
// HERE BE HOLE
retval = 37,
jif = 38,
store_global_idx = 39,
load_global_idx = 40,
push_true = 41,
push_false = 42,
push_void = 43,
struct_pack = 44,
struct_store = 45,
struct_load = 46,
};
/// This union contains each possible instruction with its (optional) arguments already encoded.
/// Each instruction type is either `NoArg`, `SingleArg`, `CallArg` or `Deprecated`, defining how
/// each instruction is encoded.
/// This information can be used to encode/decode the instructions based on their meta-information.
pub const Instruction = union(InstructionName) {
pub const Deprecated = struct {};
pub const NoArg = struct {};
fn SingleArg(comptime T: type) type {
return struct { value: T };
}
pub const CallArg = struct {
function: []const u8,
argc: u8,
};
nop: NoArg,
scope_push: Deprecated,
scope_pop: Deprecated,
declare: Deprecated,
store_global_name: Deprecated,
load_global_name: Deprecated,
push_str: SingleArg([]const u8),
push_num: SingleArg(f64),
array_pack: SingleArg(u16),
call_fn: CallArg,
call_obj: CallArg,
pop: NoArg,
add: NoArg,
sub: NoArg,
mul: NoArg,
div: NoArg,
mod: NoArg,
bool_and: NoArg,
bool_or: NoArg,
bool_not: NoArg,
negate: NoArg,
eq: NoArg,
neq: NoArg,
less_eq: NoArg,
greater_eq: NoArg,
less: NoArg,
greater: NoArg,
jmp: SingleArg(u32),
jnf: SingleArg(u32),
iter_make: NoArg,
iter_next: NoArg,
array_store: NoArg,
array_load: NoArg,
ret: NoArg,
store_local: SingleArg(u16),
load_local: SingleArg(u16),
retval: NoArg,
jif: SingleArg(u32),
store_global_idx: SingleArg(u16),
load_global_idx: SingleArg(u16),
push_true: NoArg,
push_false: NoArg,
push_void: NoArg,
struct_pack: SingleArg(u16),
struct_store: NoArg,
struct_load: NoArg,
};
================================================
FILE: src/library/common/utility.zig
================================================
const std = @import("std");
pub fn clampFixedString(str: []const u8) []const u8 {
if (std.mem.indexOfScalar(u8, str, 0)) |off| {
return str[0..off];
} else {
return str;
}
}
================================================
FILE: src/library/compiler/analysis.zig
================================================
const std = @import("std");
const ast = @import("ast.zig");
const Location = @import("location.zig").Location;
const Scope = @import("scope.zig").Scope;
const Diagnostics = @import("diagnostics.zig").Diagnostics;
const Type = @import("typeset.zig").Type;
const TypeSet = @import("typeset.zig").TypeSet;
const AnalysisState = struct {
/// Depth of nested loops (while, for)
loop_nesting: usize,
/// Depth of nested conditionally executed scopes (if, while, for)
conditional_scope_depth: usize,
/// Only `true` when not analyzing a function
is_root_script: bool,
};
const ValidationError = error{OutOfMemory};
const array_or_string = TypeSet.init(.{ .array, .string });
fn expressionTypeToString(src: ast.Expression.Type) []const u8 {
return switch (src) {
.array_indexer => "array indexer",
.variable_expr => "variable",
.array_literal => "array literal",
.function_call => "function call",
.method_call => "method call",
.number_literal => "number literal",
.string_literal => "string literal",
.unary_operator => "unary operator application",
.binary_operator => "binary operator application",
.field_access => "field access",
.struct_literal => "struct literal",
};
}
fn emitTooManyVariables(diagnostics: *Diagnostics, location: Location) !void {
try diagnostics.emit(.@"error", location, "Too many variables declared! The maximum allowed number of variables is 35535.", .{});
}
fn performTypeCheck(diagnostics: *Diagnostics, location: Location, expected: TypeSet, actual: TypeSet) !void {
if (expected.intersection(actual).isEmpty()) {
try diagnostics.emit(.warning, location, "Possible type mismatch detected: Expected {f}, found {f}", .{
expected,
actual,
});
}
}
/// Validates a expression and returns a set of possible result types.
fn validateExpression(state: *AnalysisState, diagnostics: *Diagnostics, scope: *Scope, expression: ast.Expression) ValidationError!TypeSet {
// we're happy for now with expressions...
switch (expression.type) {
.array_indexer => |indexer| {
const array_type = try validateExpression(state, diagnostics, scope, indexer.value.*);
const index_type = try validateExpression(state, diagnostics, scope, indexer.index.*);
try performTypeCheck(diagnostics, indexer.value.location, array_or_string, array_type);
try performTypeCheck(diagnostics, indexer.index.location, TypeSet.from(.number), index_type);
if (array_type.contains(.array)) {
// when we're possibly indexing an array,
// we return a value of type `any`
return TypeSet.any;
} else if (array_type.contains(.string)) {
// when we are not an array, but a string,
// we can only return a number.
return TypeSet.from(.number);
} else {
return TypeSet.empty;
}
},
.field_access => |_| {
return TypeSet.any;
},
.struct_literal => |lit| {
for (lit) |entry| {
_ = try validateExpression(state, diagnostics, scope, entry.@"1".*);
}
return TypeSet.from(.@"struct");
},
.variable_expr => |variable_name| {
// Check reserved names
if (std.mem.eql(u8, variable_name, "true")) {
return TypeSet.from(.boolean);
} else if (std.mem.eql(u8, variable_name, "false")) {
return TypeSet.from(.boolean);
} else if (std.mem.eql(u8, variable_name, "void")) {
return TypeSet.from(.void);
}
const variable = scope.get(variable_name) orelse {
try diagnostics.emit(.@"error", expression.location, "Use of undeclared variable {s}", .{
variable_name,
});
return TypeSet.any;
};
return variable.possible_types;
},
.array_literal => |array| {
for (array) |item| {
_ = try validateExpression(state, diagnostics, scope, item);
}
return TypeSet.from(.array);
},
.function_call => |call| {
if (call.function.type != .variable_expr) {
try diagnostics.emit(.@"error", expression.location, "Function name expected", .{});
}
if (call.arguments.len >= 256) {
try diagnostics.emit(.@"error", expression.location, "Function argument list exceeds 255 arguments!", .{});
}
for (call.arguments) |item| {
_ = try validateExpression(state, diagnostics, scope, item);
}
return TypeSet.any;
},
.method_call => |call| {
_ = try validateExpression(state, diagnostics, scope, call.object.*);
for (call.arguments) |item| {
_ = try validateExpression(state, diagnostics, scope, item);
}
return TypeSet.any;
},
.number_literal => {
// these are always ok
return TypeSet.from(.number);
},
.string_literal => {
return TypeSet.from(.string);
},
.unary_operator => |expr| {
const result = try validateExpression(state, diagnostics, scope, expr.value.*);
const expected = switch (expr.operator) {
.negate => Type.number,
.boolean_not => Type.boolean,
};
try performTypeCheck(diagnostics, expression.location, TypeSet.from(expected), result);
return result;
},
.binary_operator => |expr| {
const lhs = try validateExpression(state, diagnostics, scope, expr.lhs.*);
const rhs = try validateExpression(state, diagnostics, scope, expr.rhs.*);
const accepted_set = switch (expr.operator) {
.add => TypeSet.init(.{ .string, .number, .array }),
.subtract, .multiply, .divide, .modulus => TypeSet.from(.number),
.boolean_or, .boolean_and => TypeSet.from(.boolean),
.equal, .different => TypeSet.any,
.less_than, .greater_than, .greater_or_equal_than, .less_or_equal_than => TypeSet.init(.{ .string, .number, .array }),
};
try performTypeCheck(diagnostics, expr.lhs.location, accepted_set, lhs);
try performTypeCheck(diagnostics, expr.rhs.location, accepted_set, rhs);
if (!TypeSet.areCompatible(lhs, rhs)) {
try diagnostics.emit(.warning, expression.location, "Possible type mismatch detected. {f} and {f} are not compatible.\n", .{
lhs,
rhs,
});
return TypeSet.empty;
}
return switch (expr.operator) {
.add => TypeSet.intersection(lhs, rhs),
.subtract, .multiply, .divide, .modulus => TypeSet.from(.number),
.boolean_or, .boolean_and => TypeSet.from(.boolean),
.less_than, .greater_than, .greater_or_equal_than, .less_or_equal_than, .equal, .different => TypeSet.from(.boolean),
};
},
}
return .void;
}
fn validateStore(state: *AnalysisState, diagnostics: *Diagnostics, scope: *Scope, expression: ast.Expression, type_hint: TypeSet) ValidationError!void {
if (!expression.isAssignable()) {
try diagnostics.emit(.@"error", expression.location, "Expected array indexer or a variable, got {s}", .{
expressionTypeToString(expression.type),
});
return;
}
switch (expression.type) {
.array_indexer => |indexer| {
const array_val = try validateExpression(state, diagnostics, scope, indexer.value.*);
const index_val = try validateExpression(state, diagnostics, scope, indexer.index.*);
try performTypeCheck(diagnostics, indexer.value.location, array_or_string, array_val);
try performTypeCheck(diagnostics, indexer.index.location, TypeSet.from(.number), index_val);
if (array_val.contains(.string) and !array_val.contains(.array)) {
// when we are sure we write into a string, but definitly not an array
// check if we're writing a number.
try performTypeCheck(diagnostics, expression.location, TypeSet.from(.number), type_hint);
}
// now propagate the store validation back to the lvalue.
// Note that we can assume that the lvalue _is_ a array, as it would be a type mismatch otherwise.
try validateStore(state, diagnostics, scope, indexer.value.*, array_or_string.intersection(array_val));
},
.variable_expr => |variable_name| {
if (std.mem.eql(u8, variable_name, "true") or std.mem.eql(u8, variable_name, "false") or std.mem.eql(u8, variable_name, "void")) {
try diagnostics.emit(.@"error", expression.location, "Expected array indexer or a variable, got {s}", .{
variable_name,
});
} else if (scope.get(variable_name)) |variable| {
if (variable.is_const) {
try diagnostics.emit(.@"error", expression.location, "Assignment to constant {s} not allowed.", .{
variable_name,
});
}
if (state.conditional_scope_depth > 0) {
variable.possible_types = variable.possible_types.@"union"(type_hint);
} else {
variable.possible_types = type_hint;
}
}
},
.field_access => |field_access| {
const struct_val = try validateExpression(state, diagnostics, scope, field_access.@"struct".*);
try performTypeCheck(diagnostics, field_access.@"struct".location, .from(.@"struct"), struct_val);
try validateStore(state, diagnostics, scope, field_access.@"struct".*, struct_val);
},
else => unreachable,
}
}
fn validateStatement(state: *AnalysisState, diagnostics: *Diagnostics, scope: *Scope, stmt: ast.Statement) ValidationError!void {
switch (stmt.type) {
.empty => {
// trivial: do nothing!
},
.assignment => |ass| {
const value_type = try validateExpression(state, diagnostics, scope, ass.value);
if (ass.target.isAssignable()) {
try validateStore(state, diagnostics, scope, ass.target, value_type);
} else {
try diagnostics.emit(.@"error", ass.target.location, "Expected either a array indexer or a variable, got {s}", .{
@tagName(@as(ast.Expression.Type, ass.target.type)),
});
}
},
.discard_value => |expr| {
_ = try validateExpression(state, diagnostics, scope, expr);
},
.return_void => {
// this is always ok
},
.return_expr => |expr| {
// this is ok when the expr is ok
_ = try validateExpression(state, diagnostics, scope, expr);
// and when we are not on the root script.
if (state.is_root_script) {
try diagnostics.emit(.@"error", stmt.location, "Returning a value from global scope is not allowed.", .{});
}
},
.while_loop => |loop| {
state.loop_nesting += 1;
defer state.loop_nesting -= 1;
state.conditional_scope_depth += 1;
defer state.conditional_scope_depth -= 1;
const condition_type = try validateExpression(state, diagnostics, scope, loop.condition);
try validateStatement(state, diagnostics, scope, loop.body.*);
try performTypeCheck(diagnostics, stmt.location, TypeSet.from(.boolean), condition_type);
},
.for_loop => |loop| {
state.loop_nesting += 1;
defer state.loop_nesting -= 1;
state.conditional_scope_depth += 1;
defer state.conditional_scope_depth -= 1;
try scope.enter();
scope.declare(loop.variable, true) catch |err| switch (err) {
error.AlreadyDeclared => unreachable, // not possible for locals
error.TooManyVariables => try emitTooManyVariables(diagnostics, stmt.location),
else => |e| return e,
};
const array_type = try validateExpression(state, diagnostics, scope, loop.source);
try validateStatement(state, diagnostics, scope, loop.body.*);
try performTypeCheck(diagnostics, stmt.location, TypeSet.from(.array), array_type);
try scope.leave();
},
.if_statement => |conditional| {
state.conditional_scope_depth += 1;
defer state.conditional_scope_depth -= 1;
const conditional_type = try validateExpression(state, diagnostics, scope, conditional.condition);
try validateStatement(state, diagnostics, scope, conditional.true_body.*);
if (conditional.false_body) |body| {
try validateStatement(state, diagnostics, scope, body.*);
}
try performTypeCheck(diagnostics, stmt.location, TypeSet.from(.boolean), conditional_type);
},
.declaration => |decl| {
// evaluate expression before so we can safely reference up-variables:
// var a = a * 2;
const initial_value = if (decl.initial_value) |init_val|
try validateExpression(state, diagnostics, scope, init_val)
else
null;
scope.declare(decl.variable, decl.is_const) catch |err| switch (err) {
error.AlreadyDeclared => try diagnostics.emit(.@"error", stmt.location, "Global variable {s} is already declared!", .{decl.variable}),
error.TooManyVariables => try emitTooManyVariables(diagnostics, stmt.location),
else => |e| return e,
};
if (initial_value) |init_val|
scope.get(decl.variable).?.possible_types = init_val;
if (decl.is_const and decl.initial_value == null) {
try diagnostics.emit(.@"error", stmt.location, "Constant {s} must be initialized!", .{
decl.variable,
});
}
},
.block => |blk| {
try scope.enter();
for (blk) |sub_stmt| {
try validateStatement(state, diagnostics, scope, sub_stmt);
}
try scope.leave();
},
.@"break" => {
if (state.loop_nesting == 0) {
try diagnostics.emit(.@"error", stmt.location, "break outside of loop!", .{});
}
},
.@"continu
gitextract_k560vkz5/
├── .envrc
├── .gitattributes
├── .github/
│ └── workflows/
│ ├── build.yml
│ └── website.yml.disabled
├── .gitignore
├── LICENSE
├── README.md
├── benchmarks/
│ ├── code/
│ │ ├── array-concat.lola
│ │ ├── array-creation.lola
│ │ ├── array-deep-copy.lola
│ │ ├── array-modification.lola
│ │ ├── basic-execution.lola
│ │ ├── cow-calls.lola
│ │ ├── function-call.lola
│ │ └── string-concat.lola
│ ├── data/
│ │ ├── .keep
│ │ ├── array-concat-ReleaseFast.csv
│ │ ├── array-concat-ReleaseSafe.csv
│ │ ├── array-concat-ReleaseSmall.csv
│ │ ├── array-creation-ReleaseFast.csv
│ │ ├── array-creation-ReleaseSafe.csv
│ │ ├── array-creation-ReleaseSmall.csv
│ │ ├── array-deep-copy-ReleaseFast.csv
│ │ ├── array-deep-copy-ReleaseSafe.csv
│ │ ├── array-deep-copy-ReleaseSmall.csv
│ │ ├── array-modification-ReleaseFast.csv
│ │ ├── array-modification-ReleaseSafe.csv
│ │ ├── array-modification-ReleaseSmall.csv
│ │ ├── basic-execution-ReleaseFast.csv
│ │ ├── basic-execution-ReleaseSafe.csv
│ │ ├── basic-execution-ReleaseSmall.csv
│ │ ├── cow-calls-ReleaseFast.csv
│ │ ├── cow-calls-ReleaseSafe.csv
│ │ ├── cow-calls-ReleaseSmall.csv
│ │ ├── function-call-ReleaseFast.csv
│ │ ├── function-call-ReleaseSafe.csv
│ │ ├── function-call-ReleaseSmall.csv
│ │ ├── string-concat-ReleaseFast.csv
│ │ ├── string-concat-ReleaseSafe.csv
│ │ └── string-concat-ReleaseSmall.csv
│ └── visualization/
│ ├── .keep
│ └── index.htm
├── build.zig
├── build.zig.zon
├── design/
│ ├── Lola.psb
│ ├── README.md
│ ├── benchmark.xcf
│ ├── face-only.xcf
│ └── logo.xcf
├── develop.lola
├── documentation/
│ ├── LoLa.md
│ ├── README.md
│ ├── checklist.txt
│ ├── ir.md
│ ├── modules.md
│ ├── runtime-library.md
│ ├── standard-library.md
│ └── zig-api.md
├── examples/
│ ├── host/
│ │ ├── minimal-host/
│ │ │ └── main.zig
│ │ ├── multi-environment/
│ │ │ ├── client-a.lola
│ │ │ ├── client-b.lola
│ │ │ ├── main.zig
│ │ │ └── server.lola
│ │ └── serialization/
│ │ └── main.zig
│ └── lola/
│ ├── README.md
│ ├── bubble-sort.lola
│ ├── fib-iterative.lola
│ ├── forth.lola
│ ├── game-code.lola
│ ├── global-setter.lola
│ ├── hello-world.lola
│ ├── reverse-array.lola
│ ├── stupid-bench.lola
│ └── sum-of-array.lola
├── src/
│ ├── README.md
│ ├── benchmark/
│ │ ├── perf.zig
│ │ └── render.zig
│ ├── frontend/
│ │ └── main.zig
│ ├── library/
│ │ ├── common/
│ │ │ ├── CompileUnit.zig
│ │ │ ├── Decoder.zig
│ │ │ ├── disassembler.zig
│ │ │ ├── interface.zig
│ │ │ ├── ir.zig
│ │ │ └── utility.zig
│ │ ├── compiler/
│ │ │ ├── analysis.zig
│ │ │ ├── ast.zig
│ │ │ ├── code-writer.zig
│ │ │ ├── codegen.zig
│ │ │ ├── diagnostics.zig
│ │ │ ├── location.zig
│ │ │ ├── parser.zig
│ │ │ ├── scope.zig
│ │ │ ├── string-escaping.zig
│ │ │ ├── tokenizer.zig
│ │ │ └── typeset.zig
│ │ ├── libraries/
│ │ │ ├── libs.zig
│ │ │ ├── runtime.zig
│ │ │ └── stdlib.zig
│ │ ├── main.zig
│ │ ├── runtime/
│ │ │ ├── Environment.zig
│ │ │ ├── environmentmap.zig
│ │ │ ├── objects.zig
│ │ │ ├── value.zig
│ │ │ └── vm.zig
│ │ ├── test/
│ │ │ ├── behaviour-with-stdlib.lola
│ │ │ ├── behaviour.lola
│ │ │ ├── compiler.lola
│ │ │ ├── empty.lola
│ │ │ ├── global-return.lola
│ │ │ ├── runtime.lola
│ │ │ └── stdlib.lola
│ │ └── test.zig
│ ├── tools/
│ │ └── render-md-page.zig
│ └── wasm-compiler/
│ └── main.zig
└── website/
├── .gitignore
├── documentation.css
├── fonts/
│ ├── PT_Sans/
│ │ └── OFL.txt
│ └── Source_Code_Pro/
│ └── OFL.txt
├── img/
│ └── .gitignore
├── index.htm
├── libs/
│ ├── ace.js
│ ├── mode-javascript.js
│ ├── theme-vibrant_ink.js
│ ├── worker-javascript.js
│ ├── xterm-addon-fit.js
│ ├── xterm.css
│ └── xterm.js
├── playground.css
├── playground.htm
├── playground.js
└── style.css
SYMBOL INDEX (628 symbols across 6 files)
FILE: website/libs/ace.js
function o (line 1) | function o(n){var i=e;n&&(e[n]||(e[n]={}),i=e[n]);if(!i.define||!i.defin...
function a (line 1) | function a(){u=!1;try{document.createComment("").addEventListener("test"...
function f (line 1) | function f(){return u==undefined&&a(),u}
function l (line 1) | function l(e,t,n){this.elem=e,this.type=t,this.callback=n}
function d (line 1) | function d(e,t,n){var u=p(t);if(!i.isMac&&s){t.getModifierState&&(t.getM...
function v (line 1) | function v(){s=Object.create(null)}
function i (line 1) | function i(e){t&&t(e),n&&n(e),h(r,"mousemove",t),h(r,"mouseup",i),h(r,"d...
function p (line 1) | function p(e){t.getButton(e)!==0?u=0:e.detail>1?(u++,u>4&&(u=1)):u=1;if(...
function X (line 1) | function X(){x=!0,n.blur(),n.focus(),x=!1}
function $ (line 1) | function $(e){e.keyCode==27&&n.value.length<n.selectionStart&&(b||(T=n.v...
function K (line 1) | function K(){clearTimeout(J),J=setTimeout(function(){E&&(n.style.cssText...
function G (line 1) | function G(e,t,n){var r=null,i=!1;n.addEventListener("keydown",function(...
function o (line 1) | function o(e){e.$clickSelection=null;var t=e.editor;t.setDefaultHandler(...
function u (line 1) | function u(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}
function a (line 1) | function a(e,t){if(e.start.row==e.end.row)var n=2*t.column-e.start.colum...
function s (line 1) | function s(e){this.isOpen=!1,this.$element=null,this.$parentNode=e}
function u (line 1) | function u(e){function l(){var r=u.getDocumentPosition().row,s=n.$annota...
function a (line 1) | function a(e){o.call(this,e)}
function f (line 1) | function f(e){function T(e,n){var r=Date.now(),i=!n||e.row!=n.row,s=!n||...
function l (line 1) | function l(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}
function b (line 1) | function b(){var e=window.navigator&&window.navigator.clipboard,r=!1,i=f...
function w (line 1) | function w(){y||b();var e=t.selection.cursor,n=t.renderer.textToScreenCo...
function E (line 1) | function E(e){y&&(y.style.display="none"),t.off("input",E)}
function S (line 1) | function S(){l=null,clearTimeout(l);var e=t.selection.getRange(),r=e.con...
function x (line 1) | function x(){l=null,clearTimeout(l),t.selection.moveToPosition(p);var e=...
function T (line 1) | function T(){h+=60,c=setInterval(function(){h--<=0&&(clearInterval(c),c=...
function o (line 1) | function o(e){typeof console!="undefined"&&console.warn&&console.warn.ap...
function u (line 1) | function u(e,t){var n=new Error(e);n.data=t,typeof console=="object"&&co...
function l (line 1) | function l(r){if(!u||!u.document)return;a.packaged=r||e.packaged||n.pack...
function c (line 1) | function c(e){return e.replace(/-(.)/g,function(e,t){return t.toUpperCas...
function i (line 1) | function i(e){e.on("click",function(t){var n=t.getDocumentPosition(),i=e...
function F (line 1) | function F(e,t,n,r){var i=s?d:p,c=null,h=null,v=null,m=0,g=null,y=null,b...
function I (line 1) | function I(e,t,n){if(o<e)return;if(e==1&&s==m&&!f){n.reverse();return}va...
function q (line 1) | function q(e,t,n,r){var i=t[r],o,c,h,p;switch(i){case g:case y:u=!1;case...
function R (line 1) | function R(e){var t=e.charCodeAt(0),n=t>>8;return n==0?t>191?g:B[t]:n==5...
function U (line 1) | function U(e){return e>="\u064b"&&e<="\u0655"}
function i (line 1) | function i(s){var o=r[s];o.processed=!0;for(var u=0;u<o.length;u++){var ...
function w (line 1) | function w(e){for(var t=n;t<=r;t++)e(i.getLine(t),t)}
function r (line 1) | function r(e,t){throw console.log("Invalid Delta:",e),"Invalid Delta: "+t}
function i (line 1) | function i(e,t){return t.row>=0&&t.row<e.length&&t.column>=0&&t.column<=...
function s (line 1) | function s(e,t){t.action!="insert"&&t.action!="remove"&&r(t,"delta.actio...
function e (line 1) | function e(e,t,n){var r=n?e.column<=t.column:e.column<t.column;return e....
function t (line 1) | function t(t,n,r){var i=t.action=="insert",s=(i?1:-1)*(t.end.row-t.start...
function i (line 1) | function i(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.fol...
function o (line 1) | function o(e,t){e.row-=t.row,e.row==0&&(e.column-=t.column)}
function u (line 1) | function u(e,t){o(e.start,t),o(e.end,t)}
function a (line 1) | function a(e,t){e.row==0&&(e.column+=t.column),e.row+=t.row}
function f (line 1) | function f(e,t){a(e.start,t),a(e.end,t)}
function u (line 1) | function u(){this.getFoldAt=function(e,t,n){var r=this.getFoldLine(e);if...
function s (line 1) | function s(){this.findMatchingBracket=function(e,t){if(e.column==0)retur...
function m (line 1) | function m(e){return e<4352?!1:e>=4352&&e<=4447||e>=4515&&e<=4519||e>=46...
function n (line 1) | function n(e){return t?e.action!=="insert":e.action==="insert"}
function g (line 1) | function g(){var t=0;if(m===0)return t;if(p)for(var n=0;n<e.length;n++){...
function y (line 1) | function y(t){var n=t-f;for(var r=f;r<t;r++){var i=e[r];if(i===12||i===2...
function u (line 1) | function u(e,t){function n(e){return/\w/.test(e)||t.regExp?"\\b":""}retu...
function o (line 1) | function o(e,t){this.platform=t||(i.isMac?"mac":"win"),this.commands={},...
function u (line 1) | function u(e,t){o.call(this,e,t),this.$singleCommand=!1}
function e (line 1) | function e(e){return typeof e=="object"&&e.bindKey&&e.bindKey.position||...
function o (line 1) | function o(e,t){return{win:e,mac:t}}
function i (line 1) | function i(e,t){for(var n=t;n--;){var r=e[n];if(r&&!r[0].ignore){while(n...
function a (line 1) | function a(e){var t=e.action=="insert",n=e.start,r=e.end,i=(r.row-n.row)...
function f (line 1) | function f(e){return{row:e.row,column:e.column}}
function l (line 1) | function l(e){return{start:f(e.start),end:f(e.end),action:e.action,lines...
function c (line 1) | function c(e){e=e||this;if(Array.isArray(e))return e.map(c).join("\n");v...
function h (line 1) | function h(e){return e.start.row+":"+e.start.column+"=>"+e.end.row+":"+e...
function p (line 1) | function p(e,t){var n=e.action=="insert",r=t.action=="insert";if(n&&r)if...
function d (line 1) | function d(e,t){for(var n=e.length;n--;)for(var r=0;r<t.length;r++)if(!p...
function v (line 1) | function v(e,t){var n=e.action=="insert",r=t.action=="insert";if(n&&r)o(...
function m (line 1) | function m(e,t,n){g(e.start,t.start,t.end,n),g(e.end,t.start,t.end,n)}
function g (line 1) | function g(e,t,n,r){e.row==(r==1?t:n).row&&(e.column+=r*(n.column-t.colu...
function y (line 1) | function y(e,t){var n=e.lines,r=e.end;e.end=f(t);var i=e.end.row-e.start...
function b (line 1) | function b(e,t){t=l(t);for(var n=e.length;n--;){var r=e[n];for(var i=0;i...
function w (line 1) | function w(e,t){for(var n=0;n<t.length;n++){var r=t[n];for(var i=0;i<r.l...
function f (line 1) | function f(e){var t=document.createTextNode("");e.appendChild(t);var n=r...
function e (line 1) | function e(e,t,n,r){return(e?1:0)|(t?2:0)|(n?4:0)|(r?8:0)}
function i (line 1) | function i(e,t,n){var i=0,s=0;while(s+e[i].value.length<t){s+=e[i].value...
function r (line 1) | function r(e,t,n){var r=e[1]*t[0]-e[0]*t[1];return[(-t[1]*n[0]+t[0]*n[1]...
function i (line 1) | function i(e,t){return[e[0]-t[0],e[1]-t[1]]}
function s (line 1) | function s(e,t){return[e[0]+t[0],e[1]+t[1]]}
function o (line 1) | function o(e,t){return[e*t[0],e*t[1]]}
function u (line 1) | function u(e){var t=e.getBoundingClientRect();return[t.left,t.top]}
function o (line 1) | function o(r){if(n.$themeId!=e)return t&&t();if(!r||!r.cssClass)throw ne...
function u (line 1) | function u(e){var t="importScripts('"+i.qualifyURL(e)+"');";try{return n...
function a (line 1) | function a(e){if(typeof Worker=="undefined")return{postMessage:function(...
function s (line 1) | function s(e,t){return e.row==t.row&&e.column==t.column}
function o (line 1) | function o(e){var t=e.domEvent,n=t.altKey,o=t.shiftKey,u=t.ctrlKey,a=e.g...
function h (line 1) | function h(e,t,n){return c.$options.wrap=!0,c.$options.needle=t,c.$optio...
function v (line 1) | function v(e,t){return e.row==t.row&&e.column==t.column}
function m (line 1) | function m(e){if(e.$multiselectOnSessionChange)return;e.$onAddRange=e.$o...
function g (line 1) | function g(e){function r(t){n&&(e.renderer.setMouseCursor(""),n=!1)}if(!...
function u (line 1) | function u(e){return a.stringRepeat(" ",e)}
function f (line 1) | function f(e){return e[2]?u(i)+e[2]+u(s-e[2].length+o)+e[4].replace(/^([...
function l (line 1) | function l(e){return e[2]?u(i+s-e[2].length)+e[2]+u(o)+e[4].replace(/^([...
function c (line 1) | function c(e){return e[2]?u(i)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "...
function i (line 1) | function i(e){this.session=e,this.session.widgetManager=this,this.sessio...
function o (line 1) | function o(e,t,n){var r=0,i=e.length-1;while(r<=i){var s=r+i>>1,o=n(t,e[...
function u (line 1) | function u(e,t,n){var r=e.getAnnotations().sort(s.comparePoints);if(!r.l...
FILE: website/libs/mode-javascript.js
function a (line 1) | function a(){var e=o.replace("\\d","\\d\\-"),t={onMatch:function(e,t,n){...
function f (line 1) | function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),...
FILE: website/libs/worker-javascript.js
function t (line 1) | function t(e,t){var n=e,r="";while(n){var i=t[n];if(typeof i=="string")r...
function r (line 1) | function r(e,t){throw console.log("Invalid Delta:",e),"Invalid Delta: "+t}
function i (line 1) | function i(e,t){return t.row>=0&&t.row<e.length&&t.column>=0&&t.column<=...
function s (line 1) | function s(e,t){t.action!="insert"&&t.action!="remove"&&r(t,"delta.actio...
function e (line 1) | function e(e,t,n){var r=n?e.column<=t.column:e.column<t.column;return e....
function t (line 1) | function t(t,n,r){var i=t.action=="insert",s=(i?1:-1)*(t.end.row-t.start...
function t (line 1) | function t(n,r,i){function o(u,a){if(!r[u]){if(!n[u]){var f=typeof e=="f...
function d (line 1) | function d(){}
function v (line 1) | function v(){u.log.apply(u,arguments)}
function m (line 1) | function m(){u.log.apply(u,arguments)}
function g (line 1) | function g(){u.warn.apply(u,arguments)}
function y (line 1) | function y(e){a[e]=s()}
function b (line 1) | function b(e){var t=a[e];if(!t)throw new Error("No such label: "+e);var ...
function w (line 1) | function w(){var e=new Error;e.name="Trace",e.message=r.format.apply(nul...
function E (line 1) | function E(e){u.log(r.inspect(e)+"\n")}
function S (line 1) | function S(e){if(!e){var t=o.call(arguments,1);i.ok(!1,r.format.apply(nu...
function r (line 1) | function r(){return(new Date).getTime()}
function W (line 1) | function W(e,t){return e.set(t[0],t[1]),e}
function X (line 1) | function X(e,t){return e.add(t),e}
function V (line 1) | function V(e,t){var n=-1,r=e?e.length:0;while(++n<r)if(t(e[n],n,e)===!1)...
function $ (line 1) | function $(e,t){var n=-1,r=t.length,i=e.length;while(++n<r)e[i+n]=t[n];r...
function J (line 1) | function J(e,t,n,r){var i=-1,s=e?e.length:0;r&&s&&(n=e[++i]);while(++i<s...
function K (line 1) | function K(e,t){var n=-1,r=Array(e);while(++n<e)r[n]=t(n);return r}
function Q (line 1) | function Q(e,t){return e==null?undefined:e[t]}
function G (line 1) | function G(e){var t=!1;if(e!=null&&typeof e.toString!="function")try{t=!...
function Y (line 1) | function Y(e){var t=-1,n=Array(e.size);return e.forEach(function(e,r){n[...
function Z (line 1) | function Z(e,t){return function(n){return e(t(n))}}
function et (line 1) | function et(e){var t=-1,n=Array(e.size);return e.forEach(function(e){n[+...
function Dt (line 1) | function Dt(e){var t=-1,n=e?e.length:0;this.clear();while(++t<n){var r=e...
function Pt (line 1) | function Pt(){this.__data__=Nt?Nt(null):{}}
function Ht (line 1) | function Ht(e){return this.has(e)&&delete this.__data__[e]}
function Bt (line 1) | function Bt(e){var t=this.__data__;if(Nt){var n=t[e];return n===i?undefi...
function jt (line 1) | function jt(e){var t=this.__data__;return Nt?t[e]!==undefined:ut.call(t,e)}
function Ft (line 1) | function Ft(e,t){var n=this.__data__;return n[e]=Nt&&t===undefined?i:t,t...
function It (line 1) | function It(e){var t=-1,n=e?e.length:0;this.clear();while(++t<n){var r=e...
function qt (line 1) | function qt(){this.__data__=[]}
function Rt (line 1) | function Rt(e){var t=this.__data__,n=on(t,e);if(n<0)return!1;var r=t.len...
function Ut (line 1) | function Ut(e){var t=this.__data__,n=on(t,e);return n<0?undefined:t[n][1]}
function zt (line 1) | function zt(e){return on(this.__data__,e)>-1}
function Wt (line 1) | function Wt(e,t){var n=this.__data__,r=on(n,e);return r<0?n.push([e,t]):...
function Xt (line 1) | function Xt(e){var t=-1,n=e?e.length:0;this.clear();while(++t<n){var r=e...
function Vt (line 1) | function Vt(){this.__data__={hash:new Dt,map:new(Et||It),string:new Dt}}
function $t (line 1) | function $t(e){return Cn(this,e)["delete"](e)}
function Jt (line 1) | function Jt(e){return Cn(this,e).get(e)}
function Kt (line 1) | function Kt(e){return Cn(this,e).has(e)}
function Qt (line 1) | function Qt(e,t){return Cn(this,e).set(e,t),this}
function Gt (line 1) | function Gt(e){this.__data__=new It(e)}
function Yt (line 1) | function Yt(){this.__data__=new It}
function Zt (line 1) | function Zt(e){return this.__data__["delete"](e)}
function en (line 1) | function en(e){return this.__data__.get(e)}
function tn (line 1) | function tn(e){return this.__data__.has(e)}
function nn (line 1) | function nn(e,t){var n=this.__data__;if(n instanceof It){var i=n.__data_...
function rn (line 1) | function rn(e,t){var n=Rn(e)||qn(e)?K(e.length,String):[],r=n.length,i=!...
function sn (line 1) | function sn(e,t,n){var r=e[t];if(!ut.call(e,t)||!In(r,n)||n===undefined&...
function on (line 1) | function on(e,t){var n=e.length;while(n--)if(In(e[n][0],t))return n;retu...
function un (line 1) | function un(e,t){return e&&xn(t,Kn(t),e)}
function an (line 1) | function an(e,t,n,r,i,s,u){var a;r&&(a=s?r(e,i,s,u):r(e));if(a!==undefin...
function fn (line 1) | function fn(e){return $n(e)?dt(e):{}}
function ln (line 1) | function ln(e,t,n){var r=t(e);return Rn(e)?r:$(r,n(e))}
function cn (line 1) | function cn(e){return at.call(e)}
function hn (line 1) | function hn(e){if(!$n(e)||Hn(e))return!1;var t=Xn(e)||G(e)?ft:H;return t...
function pn (line 1) | function pn(e){if(!Bn(e))return bt(e);var t=[];for(var n in Object(e))ut...
function dn (line 1) | function dn(e,t){if(t)return e.slice();var n=new e.constructor(e.length)...
function vn (line 1) | function vn(e){var t=new e.constructor(e.byteLength);return(new ht(t)).s...
function mn (line 1) | function mn(e,t){var n=t?vn(e.buffer):e.buffer;return new e.constructor(...
function gn (line 1) | function gn(e,t,n){var r=t?n(Y(e),!0):Y(e);return J(r,W,new e.constructor)}
function yn (line 1) | function yn(e){var t=new e.constructor(e.source,P.exec(e));return t.last...
function bn (line 1) | function bn(e,t,n){var r=t?n(et(e),!0):et(e);return J(r,X,new e.construc...
function wn (line 1) | function wn(e){return _t?Object(_t.call(e)):{}}
function En (line 1) | function En(e,t){var n=t?vn(e.buffer):e.buffer;return new e.constructor(...
function Sn (line 1) | function Sn(e,t){var n=-1,r=e.length;t||(t=Array(r));while(++n<r)t[n]=e[...
function xn (line 1) | function xn(e,t,n,r){n||(n={});var i=-1,s=t.length;while(++i<s){var o=t[...
function Tn (line 1) | function Tn(e,t){return xn(e,Ln(e),t)}
function Nn (line 1) | function Nn(e){return ln(e,Kn,Ln)}
function Cn (line 1) | function Cn(e,t){var n=e.__data__;return Pn(t)?n[typeof t=="string"?"str...
function kn (line 1) | function kn(e,t){var n=Q(e,t);return hn(n)?n:undefined}
function On (line 1) | function On(e){var t=e.length,n=e.constructor(t);return t&&typeof e[0]==...
function Mn (line 1) | function Mn(e){return typeof e.constructor=="function"&&!Bn(e)?fn(pt(e))...
function _n (line 1) | function _n(e,t,n,r){var i=e.constructor;switch(t){case S:return vn(e);c...
function Dn (line 1) | function Dn(e,t){return t=t==null?s:t,!!t&&(typeof e=="number"||B.test(e...
function Pn (line 1) | function Pn(e){var t=typeof e;return t=="string"||t=="number"||t=="symbo...
function Hn (line 1) | function Hn(e){return!!st&&st in e}
function Bn (line 1) | function Bn(e){var t=e&&e.constructor,n=typeof t=="function"&&t.prototyp...
function jn (line 1) | function jn(e){if(e!=null){try{return ot.call(e)}catch(t){}try{return e+...
function Fn (line 1) | function Fn(e){return an(e,!1,!0)}
function In (line 1) | function In(e,t){return e===t||e!==e&&t!==t}
function qn (line 1) | function qn(e){return zn(e)&&ut.call(e,"callee")&&(!vt.call(e,"callee")|...
function Un (line 1) | function Un(e){return e!=null&&Vn(e.length)&&!Xn(e)}
function zn (line 1) | function zn(e){return Jn(e)&&Un(e)}
function Xn (line 1) | function Xn(e){var t=$n(e)?at.call(e):"";return t==c||t==h}
function Vn (line 1) | function Vn(e){return typeof e=="number"&&e>-1&&e%1==0&&e<=s}
function $n (line 1) | function $n(e){var t=typeof e;return!!e&&(t=="object"||t=="function")}
function Jn (line 1) | function Jn(e){return!!e&&typeof e=="object"}
function Kn (line 1) | function Kn(e){return Un(e)?rn(e):pn(e)}
function Qn (line 1) | function Qn(){return[]}
function Gn (line 1) | function Gn(){return!1}
function y (line 1) | function y(e,t,n){var r=-1,i=e.length;t<0&&(t=-t>i?0:i+t),n=n>i?i:n,n<0&...
function b (line 1) | function b(e,t){return t=t==null?i:t,!!t&&(typeof e=="number"||d.test(e)...
function w (line 1) | function w(e,t,n){if(!C(n))return!1;var r=typeof t;return(r=="number"?x(...
function E (line 1) | function E(e,t,n){var r=e?e.length:0;return r?(n&&typeof n!="number"&&w(...
function S (line 1) | function S(e,t){return e===t||e!==e&&t!==t}
function x (line 1) | function x(e){return e!=null&&N(e.length)&&!T(e)}
function T (line 1) | function T(e){var t=C(e)?g.call(e):"";return t==u||t==a}
function N (line 1) | function N(e){return typeof e=="number"&&e>-1&&e%1==0&&e<=i}
function C (line 1) | function C(e){var t=typeof e;return!!e&&(t=="object"||t=="function")}
function k (line 1) | function k(e){return!!e&&typeof e=="object"}
function L (line 1) | function L(e){return typeof e=="symbol"||k(e)&&g.call(e)==f}
function A (line 1) | function A(e){if(!e)return e===0?e:0;e=M(e);if(e===r||e===-r){var t=e<0?...
function O (line 1) | function O(e){var t=A(e),n=t%1;return t===t?n?t-n:t:0}
function M (line 1) | function M(e){if(typeof e=="number")return e;if(L(e))return o;if(C(e)){v...
function v (line 1) | function v(e){if(e instanceof v)return e;if(!(this instanceof v))return ...
function g (line 1) | function g(e,t,n){if(t===void 0)return e;switch(n==null?3:n){case 1:retu...
function y (line 1) | function y(e,t,n){return e==null?Fn:wn(e)?g(e,t,n):yn(e)&&!gn(e)?zn(e):R...
function b (line 1) | function b(e,t){return y(e,t,Infinity)}
function w (line 1) | function w(e,t,n){return v.iteratee!==b?v.iteratee(e,t):y(e,t,n)}
function E (line 1) | function E(e,t){return t=t==null?e.length-1:+t,function(){var n=Math.max...
function S (line 1) | function S(e){if(!yn(e))return{};if(c)return c(e);d.prototype=e;var t=ne...
function x (line 1) | function x(e){return function(t){return t==null?void 0:t[e]}}
function T (line 1) | function T(e,t){return e!=null&&a.call(e,t)}
function N (line 1) | function N(e,t){var n=t.length;for(var r=0;r<n;r++){if(e==null)return vo...
function L (line 1) | function L(e){var t=k(e);return typeof t=="number"&&t>=0&&t<=C}
function A (line 1) | function A(e,t,n){t=g(t,n);var r,i;if(L(e))for(r=0,i=e.length;r<i;r++)t(...
function O (line 1) | function O(e,t,n){t=w(t,n);var r=!L(e)&&Xt(e),i=(r||e).length,s=Array(i)...
function M (line 1) | function M(e){var t=function(t,n,r,i){var s=!L(t)&&Xt(t),o=(s||t).length...
function P (line 1) | function P(e,t,n){var r=L(e)?bt:tn,i=r(e,t,n);if(i!==void 0&&i!==-1)retu...
function H (line 1) | function H(e,t,n){var r=[];return t=w(t,n),A(e,function(e,n,i){t(e,n,i)&...
function B (line 1) | function B(e,t,n){return H(e,jt(w(t)),n)}
function j (line 1) | function j(e,t,n){t=w(t,n);var r=!L(e)&&Xt(e),i=(r||e).length;for(var s=...
function F (line 1) | function F(e,t,n){t=w(t,n);var r=!L(e)&&Xt(e),i=(r||e).length;for(var s=...
function I (line 1) | function I(e,t,n,r){L(e)||(e=$t(e));if(typeof n!="number"||r)n=0;return ...
function R (line 1) | function R(e,t){return O(e,Rn(t))}
function U (line 1) | function U(e,t){return H(e,zn(t))}
function z (line 1) | function z(e,t){return P(e,zn(t))}
function W (line 1) | function W(e,t,n){var r=-Infinity,i=-Infinity,s,o;if(t==null||typeof t==...
function X (line 1) | function X(e,t,n){var r=Infinity,i=Infinity,s,o;if(t==null||typeof t=="n...
function V (line 1) | function V(e){return $(e,Infinity)}
function $ (line 1) | function $(e,t,n){if(t==null||n)return L(e)||(e=$t(e)),e[Xn(e.length-1)]...
function J (line 1) | function J(e,t,n){var r=0;return t=w(t,n),R(O(e,function(e,n,i){return{v...
function K (line 1) | function K(e,t){return function(n,r,i){var s=t?[[],[]]:{};return r=w(r,i...
function et (line 1) | function et(e){return e?gn(e)?o.call(e):En(e)?e.match(Z):L(e)?O(e,Fn):$t...
function tt (line 1) | function tt(e){return e==null?0:L(e)?e.length:Xt(e).length}
function rt (line 1) | function rt(e,t,n){return e==null||e.length<1?t==null?void 0:[]:t==null|...
function it (line 1) | function it(e,t,n){return o.call(e,0,Math.max(0,e.length-(t==null||n?1:t...
function st (line 1) | function st(e,t,n){return e==null||e.length<1?t==null?void 0:[]:t==null|...
function ot (line 1) | function ot(e,t,n){return o.call(e,t==null||n?1:t)}
function ut (line 1) | function ut(e){return H(e,Boolean)}
function at (line 1) | function at(e,t,n,r){r=r||[];var i=r.length;for(var s=0,o=k(e);s<o;s++){...
function ft (line 1) | function ft(e,t){return at(e,t,!1)}
function ct (line 1) | function ct(e,t,n,r){Pn(t)||(r=n,n=t,t=!1),n!=null&&(n=w(n,r));var i=[],...
function pt (line 1) | function pt(e){var t=[],n=arguments.length;for(var r=0,i=k(e);r<i;r++){v...
function vt (line 1) | function vt(e){var t=e&&W(e,k).length||0,n=Array(t);for(var r=0;r<t;r++)...
function gt (line 1) | function gt(e,t){var n={};for(var r=0,i=k(e);r<i;r++)t?n[e[r]]=t[r]:n[e[...
function yt (line 1) | function yt(e){return function(t,n,r){n=w(n,r);var i=k(t),s=e>0?0:i-1;fo...
function Et (line 1) | function Et(e,t,n,r){n=w(n,r,1);var i=n(t),s=0,o=k(e);while(s<o){var u=M...
function St (line 1) | function St(e,t,n){return function(r,i,s){var u=0,a=k(r);if(typeof s=="n...
function Nt (line 1) | function Nt(e,t,n){t==null&&(t=e||0,e=0),n||(n=t<e?-1:1);var r=Math.max(...
function Ct (line 1) | function Ct(e,t){if(t==null||t<1)return[];var n=[],r=0,i=e.length;while(...
function kt (line 1) | function kt(e,t,n,r,i){if(r instanceof t){var s=S(e.prototype),o=e.apply...
function Mt (line 1) | function Mt(e,t){var n=function(r){var i=n.cache,s=""+(t?t.apply(this,ar...
function Pt (line 1) | function Pt(e,t,n){var r,i,s,o,u=0;n||(n={});var a=function(){u=n.leadin...
function Ht (line 1) | function Ht(e,t,n){var r,i,s=function(t,n){r=null,n&&(i=e.apply(t,n))},o...
function Bt (line 1) | function Bt(e,t){return At(t,e)}
function jt (line 1) | function jt(e){return function(){return!e.apply(this,arguments)}}
function Ft (line 1) | function Ft(){var e=arguments,t=e.length-1;return function(){var n=t,r=e...
function It (line 1) | function It(e,t){return function(){if(--e<1)return t.apply(this,argument...
function qt (line 1) | function qt(e,t){var n;return function(){return--e>0&&(n=t.apply(this,ar...
function Wt (line 1) | function Wt(e,t){var n=zt.length,i=e.constructor,s=wn(i)&&i.prototype||r...
function Xt (line 1) | function Xt(e){if(!yn(e))return[];if(l)return l(e);var t=[];for(var n in...
function Vt (line 1) | function Vt(e){if(!yn(e))return[];var t=[];for(var n in e)t.push(n);retu...
function $t (line 1) | function $t(e){var t=Xt(e),n=t.length,r=Array(n);for(var i=0;i<n;i++)r[i...
function Jt (line 1) | function Jt(e,t,n){t=w(t,n);var r=Xt(e),i=r.length,s={};for(var o=0;o<i;...
function Kt (line 1) | function Kt(e){var t=Xt(e),n=t.length,r=Array(n);for(var i=0;i<n;i++)r[i...
function Qt (line 1) | function Qt(e){var t={},n=Xt(e);for(var r=0,i=n.length;r<i;r++)t[e[n[r]]...
function Gt (line 1) | function Gt(e){var t=[];for(var n in e)wn(e[n])&&t.push(n);return t.sort()}
function Yt (line 1) | function Yt(e,t){return function(n){var r=arguments.length;t&&(n=Object(...
function tn (line 1) | function tn(e,t,n){t=w(t,n);var r=Xt(e),i;for(var s=0,o=r.length;s<o;s++...
function nn (line 1) | function nn(e,t,n){return t in n}
function un (line 1) | function un(e,t){var n=S(e);return t&&en(n,t),n}
function an (line 1) | function an(e){return yn(e)?gn(e)?e.slice():Zt({},e):e}
function fn (line 1) | function fn(e,t){return t(e),e}
function ln (line 1) | function ln(e,t){var n=Xt(t),r=n.length;if(e==null)return!r;var i=Object...
function cn (line 1) | function cn(e,t,n,r){if(e===t)return e!==0||1/e===1/t;if(e==null||t==nul...
function hn (line 1) | function hn(e,t,n,r){e instanceof v&&(e=e._wrapped),t instanceof v&&(t=t...
function pn (line 1) | function pn(e,t){return cn(e,t)}
function dn (line 1) | function dn(e){return e==null?!0:L(e)&&(gn(e)||En(e)||bn(e))?e.length===...
function vn (line 1) | function vn(e){return!!e&&e.nodeType===1}
function mn (line 1) | function mn(e){return function(t){return u.call(t)==="[object "+e+"]"}}
function yn (line 1) | function yn(e){var t=typeof e;return t==="function"||t==="object"&&!!e}
function _n (line 1) | function _n(e){return!Cn(e)&&p(e)&&!h(parseFloat(e))}
function Dn (line 1) | function Dn(e){return Sn(e)&&h(e)}
function Pn (line 1) | function Pn(e){return e===!0||e===!1||u.call(e)==="[object Boolean]"}
function Hn (line 1) | function Hn(e){return e===null}
function Bn (line 1) | function Bn(e){return e===void 0}
function jn (line 1) | function jn(e,t){if(!gn(t))return T(e,t);var n=t.length;for(var r=0;r<n;...
function Fn (line 1) | function Fn(e){return e}
function In (line 1) | function In(e){return function(){return e}}
function qn (line 1) | function qn(){}
function Rn (line 1) | function Rn(e){return gn(e)?function(t){return N(t,e)}:x(e)}
function Un (line 1) | function Un(e){return e==null?function(){}:function(t){return gn(t)?N(e,...
function zn (line 1) | function zn(e){return e=en({},e),function(t){return ln(t,e)}}
function Wn (line 1) | function Wn(e,t,n){var r=Array(Math.max(0,e));t=g(t,n,1);for(var i=0;i<e...
function Xn (line 1) | function Xn(e,t){return t==null&&(t=e,e=0),e+Math.floor(Math.random()*(t...
function Kn (line 1) | function Kn(e){var t=function(t){return e[t]},n="(?:"+Xt(e).join("|")+")...
function Yn (line 1) | function Yn(e,t,n){gn(t)||(t=[t]);var r=t.length;if(!r)return wn(n)?n.ca...
function er (line 1) | function er(e){var t=++Zn+"";return e?e+t:t}
function or (line 1) | function or(e,t,n){!t&&n&&(t=n),t=on({},t,v.templateSettings);var r=RegE...
function ur (line 1) | function ur(e){var t=v(e);return t._chain=!0,t}
function ar (line 1) | function ar(e,t){return e._chain?v(t).chain():t}
function fr (line 1) | function fr(e){return A(Gt(e),function(t){var n=v[t]=e[t];v.prototype[t]...
function N (line 1) | function N(e,t,n){var i,s;return t?(i="",s=c.validNames):(i="unstable ",...
function C (line 1) | function C(e){return Object.prototype.toString.call(e)==="[object String]"}
function k (line 1) | function k(e,t){return e?!e.identifier||e.value!==t?!1:!0:!1}
function L (line 1) | function L(e,t){if(!t.reserved)return!1;var n=t.meta;if(n&&n.isFutureRes...
function A (line 1) | function A(e,t){return e.replace(/\{([^{}]*)\}/g,function(e,n){var r=t[n...
function O (line 1) | function O(e,t){Object.keys(t).forEach(function(n){if(r.has(v.blacklist,...
function M (line 1) | function M(){if(f.option.enforceall){for(var e in c.bool.enforcing)f.opt...
function D (line 1) | function D(){var e=null;M(),e=f.inferEsVersion(),e&&P("E059",f.tokens.ne...
function P (line 1) | function P(e,t,n,r){var i=Math.floor(t.line/f.lines.length*100),s=o.erro...
function H (line 1) | function H(){var e=f.ignoredLines;if(r.isEmpty(e))return;v.errors=r.reje...
function B (line 1) | function B(e,t,n,r,i,s){var u,a,l,c;if(/^W\d{3}$/.test(e)){if(f.ignored[...
function j (line 1) | function j(e,t,n,r,i,s,o){return B(e,{line:t,from:n},r,i,s,o)}
function F (line 1) | function F(e,t,n,r,i,s){B(e,t,n,r,i,s)}
function I (line 1) | function I(e,t,n,r,i,s,o){return F(e,{line:t,from:n},r,i,s,o)}
function q (line 1) | function q(e,t){v.internals.push({id:"(internal)",elem:e,token:t,code:t....
function R (line 1) | function R(e,t){var i=e.body.split(",").map(function(e){return e.trim()}...
function U (line 1) | function U(e){var t=e||0,n=y.length,r;if(t<n)return y[t];while(n<=t){r=b...
function z (line 1) | function z(){var e=0,t;do t=U(e++);while(t.id==="(endline)");return t}
function W (line 1) | function W(e,t){var n=f.tokens.next;e&&n.id!==e&&(t?n.id==="(end)"?F("E0...
function X (line 1) | function X(e){return e.first||e.right||e.left||e.id==="yield"||e.id==="a...
function V (line 1) | function V(e,t,n){return arguments.length<=1&&(t=f.tokens.curr,n=f.token...
function $ (line 1) | function $(e,t){var n,r=!1,i=!1,s=e&p.initial,o;e&=~p.initial,f.nameStac...
function J (line 1) | function J(e,t){return e.line===(t.startLine||t.line)}
function K (line 1) | function K(e,t){!f.option.laxbreak&&!J(e,t)&&B("W014",t,t.value)}
function Q (line 1) | function Q(e){e=e,J(e,f.tokens.next)||B("E022",e,e.value)}
function G (line 1) | function G(e){var t=f.tokens.prev,n=f.tokens.curr;e=e||{},J(t,n)||f.opti...
function Y (line 1) | function Y(e,t){var n=f.syntax[e];if(!n||typeof n!="object")f.syntax[e]=...
function Z (line 1) | function Z(e){var t=Y(e,0);return t.delim=!0,t}
function et (line 1) | function et(e,t){var n=Z(e);return n.identifier=n.reserved=!0,n.fud=t,n}
function tt (line 1) | function tt(e,t){var n=et(e,t);return n.block=!0,n}
function nt (line 1) | function nt(e){var t=e.id.charAt(0);if(t>="a"&&t<="z"||t>="A"&&t<="Z")e....
function rt (line 1) | function rt(e,t){var n=Y(e,150);return nt(n),n.nud=typeof t=="function"?...
function it (line 1) | function it(e,t){var n=Y(e,0);return n.type=e,n.nud=t,n}
function st (line 1) | function st(e,t){var n=it(e,t);return n.identifier=!0,n.reserved=!0,n}
function ot (line 1) | function ot(e,t){var n=it(e,f.syntax["(identifier)"].nud);return t=t||{}...
function ut (line 1) | function ut(e,t,n,r){var i=Y(e,n);return nt(i),i.infix=!0,i.led=function...
function at (line 1) | function at(e){var t=Y(e,42);return t.infix=!0,t.led=function(e,t){retur...
function ft (line 1) | function ft(e,t){var n=Y(e,100);return n.infix=!0,n.led=function(e,n){K(...
function lt (line 1) | function lt(e){return e.arity==="unary"&&e.id!=="++"&&e.id!=="--"}
function ht (line 1) | function ht(e,t,n){var i;return n.option.notypeof?!1:!e||!t?!1:(i=n.inES...
function pt (line 1) | function pt(e,t){var n=!1;return e.type==="this"&&t.funct["(context)"]==...
function dt (line 1) | function dt(e){function n(e){if(typeof e!="object")return;return e.right...
function vt (line 1) | function vt(e,t,n,r){var i=r&&r.allowDestructuring;n=n||t;if(f.option.fr...
function mt (line 1) | function mt(e,t){var n=ut(e,typeof t=="function"?t:function(e,t,n){retur...
function gt (line 1) | function gt(e,t,n){var r=Y(e,n);return nt(r),r.infix=!0,r.led=typeof t==...
function yt (line 1) | function yt(e){return Y(e,20).exps=!0,ut(e,function(e,t,n){return f.opti...
function bt (line 1) | function bt(e){var t=Y(e,150);return t.led=function(e,t){return f.option...
function wt (line 1) | function wt(e,t,n){if(!f.tokens.next.identifier)return;n||W();var r=f.to...
function Et (line 1) | function Et(e){if(!cn(f.tokens.next,"..."))return!1;f.inES6(!0)||B("W119...
function St (line 1) | function St(e,t){var n=wt(e,t,!1);if(n)return n;F("E030",f.tokens.next,f...
function xt (line 1) | function xt(e){var t=0,n;if(f.tokens.next.id!==";"||e.inBracelessBlock)r...
function Tt (line 1) | function Tt(e){if(f.tokens.next.id!==";"){if(f.tokens.next.isUnclosed)re...
function Nt (line 1) | function Nt(e){var t=g,n,r=f.tokens.next,i=!1;e|=p.initial;if(r.id===";"...
function Ct (line 1) | function Ct(e){var t=[],n;while(!f.tokens.next.reach&&f.tokens.next.id!=...
function kt (line 1) | function kt(){var e=f.tokens.next;while(f.tokens.next.id==="(string)"){v...
function Lt (line 1) | function Lt(e,t,n,r,i,s){var o,u=m,a=g,l,c,h;m=t,c=f.tokens.next;var d=f...
function At (line 1) | function At(e){E&&typeof E[e]!="boolean"&&B("W036",f.tokens.curr,e),type...
function _t (line 1) | function _t(e,t){var n=Object.create(null),r,i,s,o,u,a;f.tokens.next.val...
function Dt (line 1) | function Dt(e,t,n,r){r&&(f.inES6()||B("W119",f.tokens.curr,"function*","...
function Pt (line 1) | function Pt(e){var t=f.tokens.next,n=-1,r;do t.value==="("?e+=1:t.value=...
function Ht (line 1) | function Ht(e){var t={};t.exps=!0,f.funct["(comparray)"].stack();var n=!...
function Bt (line 1) | function Bt(){return!!f.funct["(method)"]}
function jt (line 1) | function jt(e,t){var n,r=!0;return typeof t=="object"?n=t:(r=t,n=wt(e,!0...
function Ft (line 1) | function Ft(e,t){function d(e){f.funct["(scope)"].addParam.apply(f.funct...
function It (line 1) | function It(e,t,n){var i={"(name)":e,"(breakage)":0,"(loopage)":0,"(isSt...
function qt (line 1) | function qt(e){return e["(global)"]&&!e["(verb)"]}
function Rt (line 1) | function Rt(e,t){function o(){if(f.tokens.curr.template&&f.tokens.curr.t...
function Ut (line 1) | function Ut(e,t){var n,r,i,s,o,u,a,l,c,h=f.option,v=f.ignored,m=e&p.preA...
function zt (line 1) | function zt(e){return{statementCount:0,nestedBlockDepth:-1,ComplexityCou...
function Wt (line 1) | function Wt(){f.funct["(metrics)"].ComplexityCount+=1}
function Xt (line 1) | function Xt(e){if(!e||e.paren)return;if(e.id===","){Xt(e.right);return}s...
function Vt (line 1) | function Vt(e){if(f.inES5())for(var t in e)e[t]&&e[t].setterToken&&!e[t]...
function $t (line 1) | function $t(e,t,n){if(cn(f.tokens.next,".")){var r=f.tokens.curr.id;W("....
function Jt (line 1) | function Jt(e,t){var n=t&&t.assignment;return e&=~p.noin,f.inES6()||B("W...
function Kt (line 1) | function Kt(e,t){var n,r,i=[],s=t&&t.openingParsed,o=t&&t.assignment,u=o...
function Qt (line 1) | function Qt(e,t){var n=t.first;if(!n)return;r.zip(e,Array.isArray(n)?n:[...
function Gt (line 1) | function Gt(e,t,n){var i=n&p.noin,s=n&p.export,o=e==="let",u=e==="const"...
function Zt (line 1) | function Zt(){return f.tokens.next.id==="("&&f.inMoz()}
function rn (line 1) | function rn(e,t){return e==="call"&&t["(async)"]?!1:e==="property"&&t["(...
function un (line 1) | function un(e,t,n,r,i,s){n.identifier&&(t=n.value);var o=t;r&&i&&(o="sta...
function an (line 1) | function an(e,t,n,r,i,s){var o=e==="get"?"getterToken":"setterToken",u=n...
function fn (line 1) | function fn(e){W("["),f.tokens.curr.delim=!0,f.tokens.curr.lbp=0,f.inES6...
function ln (line 1) | function ln(e,t){return e.type==="(punctuator)"?r.includes(t,e.value):!1}
function cn (line 1) | function cn(e,t){return e.type==="(punctuator)"&&e.value===t}
function hn (line 1) | function hn(e){var t=on();t.notJson?(!f.inES6()&&t.isDestAssign&&B("W104...
function dn (line 1) | function dn(){function e(){var e={},t=f.tokens.next;W("{");if(f.tokens.n...
function vn (line 1) | function vn(e,t,n){var r,i,s,o;for(i=0;i<e.length;i+=1){o=e[i],t.scope=o...
function i (line 1) | function i(){W("(");if(ln(f.tokens.next,["[","{"])){var t=Jt(e);r.each(t...
function i (line 1) | function i(e){var t=n.variables.filter(function(t){if(t.value===e)return...
function s (line 1) | function s(e){var t=n.variables.filter(function(t){if(t.value===e&&!t.un...
function M (line 1) | function M(e,t){if(!e)return;!Array.isArray(e)&&typeof e=="object"&&(e=O...
method isJSON (line 1) | get isJSON(){return f.jsonMode}
function v (line 1) | function v(e){return/^[0-9a-fA-F]+$/.test(e)}
function m (line 1) | function m(e){return e.length===1&&v(e)}
function g (line 1) | function g(){var e=[];return{push:function(t){e.push(t)},check:function(...
function y (line 1) | function y(e){var t=e;typeof t=="string"&&(t=t.replace(/\r\n/g,"\n").rep...
function a (line 1) | function a(e,t,n){var r=["jshint","jshint.unstable","jslint","members","...
function u (line 1) | function u(e){return l.indexOf(e)>-1}
function d (line 1) | function d(e){return u(e)||c.indexOf(e)>-1}
function b (line 1) | function b(e){return e.replace(/\\u([0-9a-fA-F]{4})/g,function(e,t){retu...
function f (line 1) | function f(e){return/^[0-9]$/.test(e)}
function l (line 1) | function l(e){return/^[0-7]$/.test(e)}
function c (line 1) | function c(e){return/^[01]$/.test(e)}
function h (line 1) | function h(e){return e==="$"||e==="_"||e==="\\"||e>="a"&&e<="z"||e>="A"&...
function r (line 1) | function r(){this._stack=[]}
function f (line 1) | function f(e){u={"(bindings)":Object.create(null),"(usages)":Object.crea...
function v (line 1) | function v(e,t){d.emit("warning",{code:e,token:t,data:r.slice(arguments,...
function m (line 1) | function m(e,t){d.emit("warning",{code:e,token:t,data:r.slice(arguments,...
function g (line 1) | function g(e){u["(usages)"][e]||(u["(usages)"][e]={"(modified)":[],"(rea...
function w (line 1) | function w(){if(u["(type)"]==="functionparams"){E();return}var e=u["(bin...
function E (line 1) | function E(){var t=u["(params)"];if(!t)return;var n=t.pop(),r;while(n){v...
function S (line 1) | function S(e){for(var t=a.length-1;t>=0;--t){var n=a[t]["(bindings)"];if...
function x (line 1) | function x(e){for(var t=a.length-1;t>=0;t--){var n=a[t];if(n["(usages)"]...
function T (line 1) | function T(t,n){if(e.option.shadow!=="outer")return;var r=l["(type)"]===...
function N (line 1) | function N(t,n,r){var i;e.option.latedef&&(i=t==="function"||t==="genera...
function i (line 1) | function i(e,t){if(e===t)return 0;var n=e.length,r=t.length;for(var i=0,...
function s (line 1) | function s(e){return n.Buffer&&typeof n.Buffer.isBuffer=="function"?n.Bu...
function l (line 1) | function l(e){return Object.prototype.toString.call(e)}
function c (line 1) | function c(e){return s(e)?!1:typeof n.ArrayBuffer!="function"?!1:typeof ...
function d (line 1) | function d(e){if(!o.isFunction(e))return;if(f)return e.name;var t=e.toSt...
function v (line 1) | function v(e,t){return typeof e=="string"?e.length<t?e:e.slice(0,t):e}
function m (line 1) | function m(e){if(f||!o.isFunction(e))return o.inspect(e);var t=d(e),n=t?...
function g (line 1) | function g(e){return v(m(e.actual),128)+" "+e.operator+" "+v(m(e.expecte...
function y (line 1) | function y(e,t,n,r,i){throw new h.AssertionError({message:n,actual:e,exp...
function b (line 1) | function b(e,t){e||y(e,!0,t,"==",h.ok)}
function w (line 1) | function w(e,t,n,r){if(e===t)return!0;if(s(e)&&s(t))return i(e,t)===0;if...
function E (line 1) | function E(e){return Object.prototype.toString.call(e)=="[object Argumen...
function S (line 1) | function S(e,t,n,r){if(e===null||e===undefined||t===null||t===undefined)...
function x (line 1) | function x(e,t,n){w(e,t,!0)&&y(e,t,n,"notDeepStrictEqual",x)}
function T (line 1) | function T(e,t){if(!e||!t)return!1;if(Object.prototype.toString.call(t)=...
function N (line 1) | function N(e){var t;try{e()}catch(n){t=n}return t}
function C (line 1) | function C(e,t,n,r){var i;if(typeof t!="function")throw new TypeError('"...
function k (line 1) | function k(e,t){e||y(e,!0,t,"==",k)}
function u (line 1) | function u(e,t){var r={seen:[],stylize:f};return arguments.length>=3&&(r...
function a (line 1) | function a(e,t){var n=u.styles[t];return n?"["+u.colors[n][0]+"m"+e+"[...
function f (line 1) | function f(e,t){return e}
function l (line 1) | function l(e){var t={};return e.forEach(function(e,n){t[e]=!0}),t}
function c (line 1) | function c(e,t,r){if(e.customInspect&&t&&A(t.inspect)&&t.inspect!==n.ins...
function h (line 1) | function h(e,t){if(T(t))return e.stylize("undefined","undefined");if(S(t...
function p (line 1) | function p(e){return"["+Error.prototype.toString.call(e)+"]"}
function d (line 1) | function d(e,t,n,r,i){var s=[];for(var o=0,u=t.length;o<u;++o)H(t,String...
function v (line 1) | function v(e,t,n,r,i,s){var o,u,a;a=Object.getOwnPropertyDescriptor(t,i)...
function m (line 1) | function m(e,t,n){var r=0,i=e.reduce(function(e,t){return r++,t.indexOf(...
function g (line 1) | function g(e){return Array.isArray(e)}
function y (line 1) | function y(e){return typeof e=="boolean"}
function b (line 1) | function b(e){return e===null}
function w (line 1) | function w(e){return e==null}
function E (line 1) | function E(e){return typeof e=="number"}
function S (line 1) | function S(e){return typeof e=="string"}
function x (line 1) | function x(e){return typeof e=="symbol"}
function T (line 1) | function T(e){return e===void 0}
function N (line 1) | function N(e){return C(e)&&M(e)==="[object RegExp]"}
function C (line 1) | function C(e){return typeof e=="object"&&e!==null}
function k (line 1) | function k(e){return C(e)&&M(e)==="[object Date]"}
function L (line 1) | function L(e){return C(e)&&(M(e)==="[object Error]"||e instanceof Error)}
function A (line 1) | function A(e){return typeof e=="function"}
function O (line 1) | function O(e){return e===null||typeof e=="boolean"||typeof e=="number"||...
function M (line 1) | function M(e){return Object.prototype.toString.call(e)}
function _ (line 1) | function _(e){return e<10?"0"+e.toString(10):e.toString(10)}
function P (line 1) | function P(){var e=new Date,t=[_(e.getHours()),_(e.getMinutes()),_(e.get...
function H (line 1) | function H(e,t){return Object.prototype.hasOwnProperty.call(e,t)}
function o (line 1) | function o(){if(!s){if(t.throwDeprecation)throw new Error(i);t.traceDepr...
function o (line 1) | function o(){if(!this._events||!Object.prototype.hasOwnProperty.call(thi...
function c (line 1) | function c(e){return e._maxListeners===undefined?o.defaultMaxListeners:e...
function h (line 1) | function h(e,t,n){if(t)e.call(n);else{var r=e.length,i=x(e,r);for(var s=...
function p (line 1) | function p(e,t,n,r){if(t)e.call(n,r);else{var i=e.length,s=x(e,i);for(va...
function d (line 1) | function d(e,t,n,r,i){if(t)e.call(n,r,i);else{var s=e.length,o=x(e,s);fo...
function v (line 1) | function v(e,t,n,r,i,s){if(t)e.call(n,r,i,s);else{var o=e.length,u=x(e,o...
function m (line 1) | function m(e,t,n,r){if(t)e.apply(n,r);else{var i=e.length,s=x(e,i);for(v...
function g (line 1) | function g(e,t,n,i){var s,o,u;if(typeof n!="function")throw new TypeErro...
function y (line 1) | function y(){if(!this.fired){this.target.removeListener(this.type,this.w...
function b (line 1) | function b(e,t,n){var r={fired:!1,wrapFn:undefined,target:e,type:t,liste...
function w (line 1) | function w(e,t,n){var r=e._events;if(!r)return[];var i=r[t];return i?typ...
function E (line 1) | function E(e){var t=this._events;if(t){var n=t[e];if(typeof n=="function...
function S (line 1) | function S(e,t){for(var n=t,r=n+1,i=e.length;r<i;n+=1,r+=1)e[n]=e[r];e.p...
function x (line 1) | function x(e,t){var n=new Array(t);for(var r=0;r<t;++r)n[r]=e[r];return n}
function T (line 1) | function T(e){var t=new Array(e.length);for(var n=0;n<t.length;++n)t[n]=...
function N (line 1) | function N(e){var t=function(){};return t.prototype=e,new t}
function C (line 1) | function C(e){var t=[];for(var n in e)Object.prototype.hasOwnProperty.ca...
function k (line 1) | function k(e){var t=this;return function(){return t.apply(e,arguments)}}
function o (line 1) | function o(e){if(e===null||e===undefined)throw new TypeError("Object.ass...
function u (line 1) | function u(){try{if(!Object.assign)return!1;var e=new String("abc");e[5]...
function o (line 1) | function o(){throw new Error("setTimeout has not been defined")}
function u (line 1) | function u(){throw new Error("clearTimeout has not been defined")}
function a (line 1) | function a(e){if(i===setTimeout)return setTimeout(e,0);if((i===o||!i)&&s...
function f (line 1) | function f(e){if(s===clearTimeout)return clearTimeout(e);if((s===u||!s)&...
function d (line 1) | function d(){if(!c||!h)return;c=!1,h.length?l=h.concat(l):p=-1,l.length&...
function v (line 1) | function v(){if(c)return;var e=a(d);c=!0;var t=l.length;while(t){h=l,l=[...
function m (line 1) | function m(e,t){this.fun=e,this.array=t}
function g (line 1) | function g(){}
function startRegex (line 1) | function startRegex(e){return RegExp("^("+e.join("|")+")")}
FILE: website/libs/xterm-addon-fit.js
function r (line 1) | function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{...
function e (line 1) | function e(){}
FILE: website/libs/xterm.js
function r (line 1) | function r(i){if(t[i])return t[i].exports;var n=t[i]={i:i,l:!1,exports:{...
function e (line 1) | function e(){this._listeners=[],this._disposed=!1}
function e (line 1) | function e(){this._disposables=[],this._isDisposed=!1}
function r (line 1) | function r(){this.constructor=e}
function t (line 1) | function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.cont...
function e (line 1) | function e(){this.fg=0,this.bg=0}
function e (line 1) | function e(){this._interim=0}
function e (line 1) | function e(){this.interim=new Uint8Array(3)}
function a (line 1) | function a(e){var t=e.toString(16);return t.length<2?"0"+t:t}
function c (line 1) | function c(e,t){return e<t?(t+.05)/(e+.05):(e+.05)/(t+.05)}
function t (line 1) | function t(e,t,r){var i=e/255,n=t/255,o=r/255;return.2126*(i<=.03928?i/1...
function t (line 1) | function t(e,t,r){for(var i=e>>24&255,n=e>>16&255,s=e>>8&255,a=t>>24&255...
function r (line 1) | function r(e,t,r){for(var i=e>>24&255,n=e>>16&255,s=e>>8&255,a=t>>24&255...
function s (line 1) | function s(e,t){return e.indexOf(t)>=0}
function e (line 1) | function e(e,t,r,i,n,o,s,a){this._container=e,this._alpha=i,this._colors...
function i (line 1) | function i(e,t,r){t.di$target===t?t.di$dependencies.push({id:e,index:r})...
function i (line 1) | function i(e,t,r,i){if(void 0===r&&(r=0),void 0===i&&(i=e.length),r>=e.l...
function e (line 1) | function e(e,t,r){void 0===r&&(r=!1),this.isWrapped=r,this._combined={},...
function e (line 1) | function e(e,t){if(void 0===e&&(e=32),void 0===t&&(t=32),this.maxLength=...
function e (line 1) | function e(){this._state=0,this._id=-1,this._handlers=Object.create(null...
function e (line 1) | function e(e){this._handler=e,this._data="",this._hitLimit=!1}
function e (line 1) | function e(){this._handlers=Object.create(null),this._active=s,this._ide...
function e (line 1) | function e(e){this._handler=e,this._data="",this._hitLimit=!1}
function e (line 1) | function e(e,r){this.allowTransparency=r;var h=e.createElement("canvas")...
function r (line 1) | function r(){this.constructor=e}
function t (line 1) | function t(t,r,i){var n=e.call(this)||this;return n.content=0,n.combined...
function e (line 1) | function e(e){this._bufferService=e,this._characterJoiners=[],this._next...
function i (line 1) | function i(e,t){var r=t.getBoundingClientRect();return[e.clientX-r.left,...
function e (line 1) | function e(e){this._renderCallback=e}
function r (line 1) | function r(){this.constructor=e}
function t (line 1) | function t(){var t=null!==e&&e.apply(this,arguments)||this;return t._cur...
function e (line 1) | function e(e){this._core=new n.Terminal(e),this._addonManager=new a.Addo...
function e (line 1) | function e(e,t){this._buffer=e,this.type=t}
function e (line 1) | function e(e){var t=this;this._buffers=e,this._onBufferChange=new s.Even...
function e (line 1) | function e(e){this._line=e}
function e (line 1) | function e(e){this._core=e}
function e (line 1) | function e(e){this._core=e}
function r (line 1) | function r(){this.constructor=e}
function t (line 1) | function t(t){void 0===t&&(t={});var r=e.call(this)||this;return r.brows...
function i (line 1) | function i(e){var r,i,n=t._mouseService.getRawByteCoords(e,t.screenEleme...
function e (line 1) | function e(e,t,r,i,n,o){this._textarea=e,this._compositionView=t,this._b...
function r (line 1) | function r(){this.constructor=e}
function t (line 1) | function t(t,r,i,n,o,s,a){var l=e.call(this)||this;return l._scrollLines...
function i (line 1) | function i(e){return e.replace(/\r?\n/g,"\r")}
function n (line 1) | function n(e,t){return t?"[200~"+e+"[201~":e}
function o (line 1) | function o(e,t,r,o){e=n(e=i(e),r),o.triggerDataEvent(e,!0),t.value=""}
function s (line 1) | function s(e,t,r){var i=r.getBoundingClientRect(),n=e.clientX-i.left-10,...
function r (line 1) | function r(){this.constructor=e}
function m (line 1) | function m(e,t){if(e>24)return t.setWinLines||!1;switch(e){case 1:return...
function e (line 1) | function e(e,t,r,i){this._bufferService=e,this._coreService=t,this._logS...
function t (line 1) | function t(t,r,i,n,c,l,_,p,g,y,b){void 0===b&&(b=new a.EscapeSequencePar...
function r (line 1) | function r(){this.constructor=e}
function e (line 1) | function e(e){this.table=new Uint8Array(e)}
function r (line 1) | function r(r){void 0===r&&(r=t.VT500_TRANSITION_TABLE);var i=e.call(this...
function r (line 1) | function r(){this.constructor=e}
function t (line 1) | function t(t,r,i,n,o,s,f,_,d){var p=e.call(this)||this;p._colors=t,p._sc...
function r (line 1) | function r(){this.constructor=e}
function t (line 1) | function t(t,r,i,n,s,a,c,l){var u=e.call(this,t,"text",r,s,i,a,c,l)||thi...
function e (line 1) | function e(){this.cache=[]}
function r (line 1) | function r(){this.constructor=e}
function _ (line 1) | function _(e){return e.code<<21|e.bg<<12|e.fg<<3|(e.bold?0:4)+(e.dim?0:2...
function t (line 1) | function t(t,r){var i=e.call(this)||this;i._config=r,i._drawToCacheCount...
function t (line 1) | function t(t,r){return e.call(this)||this}
function e (line 1) | function e(){this._didWarmUp=!1}
function e (line 1) | function e(){this._color={},this._rgba={}}
function e (line 1) | function e(e){this.capacity=e,this._map={},this._head=null,this._tail=nu...
function r (line 1) | function r(){this.constructor=e}
function t (line 1) | function t(t,r,i,n,o,s){var a=e.call(this,t,"selection",r,!0,i,n,o,s)||t...
function r (line 1) | function r(){this.constructor=e}
function t (line 1) | function t(t,r,i,n,o,a,c,l,h){var u=e.call(this,t,"cursor",r,!0,i,n,a,c)...
function e (line 1) | function e(e,t){this._renderCallback=t,this.isCursorVisible=!0,e&&this._...
function r (line 1) | function r(){this.constructor=e}
function t (line 1) | function t(t,r,i,n,o,s,a,c){var l=e.call(this,t,"link",r,!0,i,n,a,c)||th...
function e (line 1) | function e(e,t,r,n){this._bufferService=e,this._logService=t,this._optio...
function e (line 1) | function e(e,t,r,i,n,o,l,h){var u=this;this._scrollLines=e,this._element...
function e (line 1) | function e(e){this._bufferService=e,this.isSelectAllActive=!1,this.selec...
function n (line 1) | function n(e,t,r,i){var n=e-o(r,e),s=t-o(r,t);return h(Math.abs(n-s)-fun...
function o (line 1) | function o(e,t){for(var r=0,i=e.buffer.lines.get(t),n=i&&i.isWrapped;n&&...
function s (line 1) | function s(e,t,r,i,s,a){var c;return c=n(r,i,s,a).length>0?i-o(s,i):t,e<...
function a (line 1) | function a(e,t){return e>t?"A":"B"}
function c (line 1) | function c(e,t,r,i,n,o){for(var s=e,a=t,c="";s!==r||a!==i;)s+=n?1:-1,n&&...
function l (line 1) | function l(e,t){var r=t?"O":"[";return i.C0.ESC+r+e}
function h (line 1) | function h(e,t){e=Math.floor(e);for(var r="",i=0;i<e;i++)r+=t;return r}
function e (line 1) | function e(e){this._optionsService=e}
function r (line 1) | function r(){this.constructor=e}
function t (line 1) | function t(t,r,i,n,o){var s=e.call(this)||this;return s._element=t,s._sc...
function r (line 1) | function r(){this.constructor=e}
function t (line 1) | function t(t,r){var i=e.call(this)||this;i._terminal=t,i._renderService=...
function r (line 1) | function r(){this.constructor=e}
function t (line 1) | function t(t,r,i,n,o,s,c,l,h){var u=e.call(this)||this;return u._colors=...
function e (line 1) | function e(e,t,r){this._document=e,this._optionsService=t,this._colors=r...
function c (line 1) | function c(e,t,r){for(;e.length<r;)e=t+e;return e}
function r (line 1) | function r(){this.constructor=e}
function t (line 1) | function t(t,r,i,n,o){var s=e.call(this)||this;if(s._renderer=t,s._rowCo...
function e (line 1) | function e(e){var r=this;this._onOptionChange=new i.EventEmitter,this.op...
function e (line 1) | function e(e,t,r){this.document=e,this.parentElement=t,this._optionsServ...
function e (line 1) | function e(e,t,r){this._document=e,this._parentElement=t,this._optionsSe...
function e (line 1) | function e(e){this._optionsService=e,this.cols=Math.max(e.options.cols,t...
function e (line 1) | function e(e,t){this.optionsService=e,this.bufferService=t,this._onBuffe...
function e (line 1) | function e(e,t,r){this._hasScrollback=e,this._optionsService=t,this._buf...
function e (line 1) | function e(e,t,r,i,n,o){void 0===r&&(r=0),void 0===i&&(i=e.lines.length)...
function e (line 1) | function e(e){this._maxLength=e,this.onDeleteEmitter=new i.EventEmitter,...
function i (line 1) | function i(e,t,r){if(t===e.length-1)return e[t].getTrimmedLength();var i...
function r (line 1) | function r(){this.constructor=e}
function t (line 1) | function t(r){var i=e.call(this)||this;return i.line=r,i._id=t._nextId++...
function e (line 1) | function e(e,t){this._renderService=e,this._charSizeService=t}
function e (line 1) | function e(e,t,r,i){this._scrollToBottom=e,this._bufferService=t,this._l...
function e (line 1) | function e(e){var t=this;this._optionsService=e,this._updateLogLevel(),t...
function e (line 1) | function e(e){this._bufferService=e,this.clearRange()}
function e (line 1) | function e(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];t...
function e (line 1) | function e(){this._services=new s,this._services.set(n.IInstantiationSer...
function c (line 1) | function c(e,t){var r=(e.ctrl?16:0)|(e.shift?4:0)|(e.alt?8:0);return 4==...
function e (line 1) | function e(e,t){var r=this;this._bufferService=e,this._coreService=t,thi...
function e (line 1) | function e(e){this._action=e,this._writeBuffer=[],this._callbacks=[],thi...
function e (line 1) | function e(e){this._bufferService=e,this._linkProviders=[],this._linkCac...
function e (line 1) | function e(e){this._textarea=e}
function e (line 1) | function e(){this._providers=Object.create(null),this._active="",this._o...
function e (line 1) | function e(){if(this.version="6",!i){i=new Uint8Array(65536),n.fill(i,1)...
function e (line 1) | function e(){this.charsets=[],this.glevel=0}
function e (line 1) | function e(){this._addons=[]}
FILE: website/playground.js
function loadTemplate (line 23) | function loadTemplate(id) {
function validateCode (line 29) | function validateCode() {
function stopCode (line 54) | function stopCode() {
function runCode (line 59) | function runCode() {
function showHelp (line 99) | function showHelp() {
function translateEmulatorError (line 201) | function translateEmulatorError(ind) {
function stepRuntime (line 222) | function stepRuntime(time) {
Condensed preview — 131 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,818K chars).
[
{
"path": ".envrc",
"chars": 9,
"preview": "use flake"
},
{
"path": ".gitattributes",
"chars": 23,
"preview": "*.zig text=auto eol=lf\n"
},
{
"path": ".github/workflows/build.yml",
"chars": 599,
"preview": "name: Build\n\non:\n push:\n branches: [master]\n pull_request:\n branches: [master]\n\njobs:\n build:\n strategy:\n "
},
{
"path": ".github/workflows/website.yml.disabled",
"chars": 836,
"preview": "name: Render Website\n\non:\n push:\n branch: none # disabled for now\n\njobs:\n build:\n\n runs-on: ubuntu-latest\n\n s"
},
{
"path": ".gitignore",
"chars": 74,
"preview": "# Custom Items\n*.lm\n.zig-cache\nscratchpad\ndocs/\ndebug/\nrelease/\nzig-out/\n\n"
},
{
"path": "LICENSE",
"chars": 1076,
"preview": "MIT License\n\nCopyright (c) 2019-2020 Felix Queißner\n\nPermission is hereby granted, free of charge, to any person obtaini"
},
{
"path": "README.md",
"chars": 8546,
"preview": "# LoLa Programming Language\n\n\n\nLoLa is a small programming language meant to be embedded in"
},
{
"path": "benchmarks/code/array-concat.lola",
"chars": 93,
"preview": "\n\nvar i = 0;\nwhile(i < 100000) {\n\n var foo = [ 1,2,3 ] + [ 4,5,6 ] + [ 7,8,9 ];\n\n i += 1;\n}"
},
{
"path": "benchmarks/code/array-creation.lola",
"chars": 160,
"preview": "\n\nvar i = 0;\nwhile(i < 100000) {\n\n var array = [ 1, 2, 3 ];\n\n var tmp = array[0];\n array[0] = array[1];\n array[1] = "
},
{
"path": "benchmarks/code/array-deep-copy.lola",
"chars": 262,
"preview": "\n\nvar deep_array = [\n \"Hello\",\n \"This\",\n [\n \"deep\",\n \"deeper\",\n \"deepest\",\n ],\n [\n [\n [\n \"n"
},
{
"path": "benchmarks/code/array-modification.lola",
"chars": 157,
"preview": "\nvar array = [ 1, 2, 3 ];\n\nvar i = 0;\nwhile(i < 100000) {\n\n var tmp = array[0];\n array[0] = array[1];\n array[1] = arr"
},
{
"path": "benchmarks/code/basic-execution.lola",
"chars": 42,
"preview": "var i = 0;\nwhile(i < 100000) {\n i += 1;\n}"
},
{
"path": "benchmarks/code/cow-calls.lola",
"chars": 389,
"preview": "// same as function-call.lola, but tests reduced copies on function calls\n\nfunction Foo(a, b, c) {\n Bar(a, b, c);\n Bar"
},
{
"path": "benchmarks/code/function-call.lola",
"chars": 227,
"preview": "\nfunction Foo(a) {\n Bar(2 * a);\n Bar(2 * a + 1);\n}\n\nfunction Bar(a) {\n Bam(2 * a);\n Bam(2 * a + 1);\n}\n\nfunction Bam("
},
{
"path": "benchmarks/code/string-concat.lola",
"chars": 81,
"preview": "\n\nvar i = 0;\nwhile(i < 100000) {\n\n var foo = \"a\" + \"hello\" + \"bar\";\n\n i += 1;\n}"
},
{
"path": "benchmarks/data/.keep",
"chars": 0,
"preview": ""
},
{
"path": "benchmarks/data/array-concat-ReleaseFast.csv",
"chars": 150,
"preview": "time;compile;setup;run\n2022-03-14 17:50:57;92471;34921;909061563\n2022-03-14 17:51:15;71657;23258;854263712\n2022-03-15 09"
},
{
"path": "benchmarks/data/array-concat-ReleaseSafe.csv",
"chars": 154,
"preview": "time;compile;setup;run\n2022-03-14 17:50:51;96382;36737;1036393973\n2022-03-14 17:51:09;225659;77385;1070012044\n2022-03-15"
},
{
"path": "benchmarks/data/array-concat-ReleaseSmall.csv",
"chars": 372,
"preview": "time;compile;setup;run\n2022-03-14 17:51:02;144503;43023;1007786085\n2022-03-14 17:51:19;110001;24933;1000317886\n2022-03-1"
},
{
"path": "benchmarks/data/array-creation-ReleaseFast.csv",
"chars": 150,
"preview": "time;compile;setup;run\n2022-03-14 17:50:57;92331;17321;315187057\n2022-03-14 17:51:15;80737;15226;304063959\n2022-03-15 09"
},
{
"path": "benchmarks/data/array-creation-ReleaseSafe.csv",
"chars": 151,
"preview": "time;compile;setup;run\n2022-03-14 17:50:51;103645;18718;451733786\n2022-03-14 17:51:09;91982;18159;431864521\n2022-03-15 0"
},
{
"path": "benchmarks/data/array-creation-ReleaseSmall.csv",
"chars": 365,
"preview": "time;compile;setup;run\n2022-03-14 17:51:02;99315;15994;469843174\n2022-03-14 17:51:19;98687;16343;451050382\n2022-03-15 09"
},
{
"path": "benchmarks/data/array-deep-copy-ReleaseFast.csv",
"chars": 151,
"preview": "time;compile;setup;run\n2022-03-14 17:50:57;104064;20883;186939577\n2022-03-14 17:51:15;84579;19136;169029798\n2022-03-15 0"
},
{
"path": "benchmarks/data/array-deep-copy-ReleaseSafe.csv",
"chars": 152,
"preview": "time;compile;setup;run\n2022-03-14 17:50:51;153583;24305;252028288\n2022-03-14 17:51:09;100851;22699;248200112\n2022-03-15 "
},
{
"path": "benchmarks/data/array-deep-copy-ReleaseSmall.csv",
"chars": 366,
"preview": "time;compile;setup;run\n2022-03-14 17:51:02;158262;22978;253425128\n2022-03-14 17:51:19;96871;18648;222294338\n2022-03-15 0"
},
{
"path": "benchmarks/data/array-modification-ReleaseFast.csv",
"chars": 151,
"preview": "time;compile;setup;run\n2022-03-14 17:50:57;93518;19975;457690885\n2022-03-14 17:51:15;124109;20394;465255466\n2022-03-15 0"
},
{
"path": "benchmarks/data/array-modification-ReleaseSafe.csv",
"chars": 152,
"preview": "time;compile;setup;run\n2022-03-14 17:50:51;115099;22839;619569147\n2022-03-14 17:51:09;115519;22838;599702187\n2022-03-15 "
},
{
"path": "benchmarks/data/array-modification-ReleaseSmall.csv",
"chars": 366,
"preview": "time;compile;setup;run\n2022-03-14 17:51:02;100642;18927;584032658\n2022-03-14 17:51:19;99943;19067;570154854\n2022-03-15 0"
},
{
"path": "benchmarks/data/basic-execution-ReleaseFast.csv",
"chars": 146,
"preview": "time;compile;setup;run\n2022-03-14 17:50:57;77385;24236;13819695\n2022-03-14 17:51:15;69423;18508;12575531\n2022-03-15 09:1"
},
{
"path": "benchmarks/data/basic-execution-ReleaseSafe.csv",
"chars": 147,
"preview": "time;compile;setup;run\n2022-03-14 17:50:51;80318;21930;33102079\n2022-03-14 17:51:09;78712;22350;35174987\n2022-03-15 09:1"
},
{
"path": "benchmarks/data/basic-execution-ReleaseSmall.csv",
"chars": 352,
"preview": "time;compile;setup;run\n2022-03-14 17:51:02;82554;23397;42498963\n2022-03-14 17:51:19;64883;18439;39820105\n2022-03-15 09:1"
},
{
"path": "benchmarks/data/cow-calls-ReleaseFast.csv",
"chars": 154,
"preview": "time;compile;setup;run\n2022-03-14 17:50:57;129697;18717;1587186196\n2022-03-14 17:51:15;86255;15155;1544207489\n2022-03-15"
},
{
"path": "benchmarks/data/cow-calls-ReleaseSafe.csv",
"chars": 155,
"preview": "time;compile;setup;run\n2022-03-14 17:50:51;148274;20534;2034523291\n2022-03-14 17:51:09;168947;31499;2019303824\n2022-03-1"
},
{
"path": "benchmarks/data/cow-calls-ReleaseSmall.csv",
"chars": 375,
"preview": "time;compile;setup;run\n2022-03-14 17:51:02;135423;17181;2045099458\n2022-03-14 17:51:19;135703;17112;1971642214\n2022-03-1"
},
{
"path": "benchmarks/data/function-call-ReleaseFast.csv",
"chars": 151,
"preview": "time;compile;setup;run\n2022-03-14 17:50:57;107137;24934;643612795\n2022-03-14 17:51:15;83391;20534;601739616\n2022-03-15 0"
},
{
"path": "benchmarks/data/function-call-ReleaseSafe.csv",
"chars": 152,
"preview": "time;compile;setup;run\n2022-03-14 17:50:51;136960;30940;961271070\n2022-03-14 17:51:09;102458;22420;873761139\n2022-03-15 "
},
{
"path": "benchmarks/data/function-call-ReleaseSmall.csv",
"chars": 372,
"preview": "time;compile;setup;run\n2022-03-14 17:51:02;109023;21511;963146676\n2022-03-14 17:51:19;101550;20045;858196163\n2022-03-15 "
},
{
"path": "benchmarks/data/string-concat-ReleaseFast.csv",
"chars": 150,
"preview": "time;compile;setup;run\n2022-03-14 17:50:57;68096;18997;418526316\n2022-03-14 17:51:15;74032;18648;401887800\n2022-03-15 09"
},
{
"path": "benchmarks/data/string-concat-ReleaseSafe.csv",
"chars": 150,
"preview": "time;compile;setup;run\n2022-03-14 17:50:51;128928;34712;508247513\n2022-03-14 17:51:09;75708;22210;494127218\n2022-03-15 0"
},
{
"path": "benchmarks/data/string-concat-ReleaseSmall.csv",
"chars": 359,
"preview": "time;compile;setup;run\n2022-03-14 17:51:02;90096;20534;504607490\n2022-03-14 17:51:19;73404;19067;480256677\n2022-03-15 09"
},
{
"path": "benchmarks/visualization/.keep",
"chars": 0,
"preview": ""
},
{
"path": "benchmarks/visualization/index.htm",
"chars": 1316,
"preview": "<!DOCTYPE html>\n<html>\n\n<head>\n <meta charset=\"utf-8\">\n <style>\n * {\n box-sizing: border-box;\n }\n\n body "
},
{
"path": "build.zig",
"chars": 11053,
"preview": "const std = @import(\"std\");\nconst builtin = @import(\"builtin\");\nconst Build = std.Build;\n\nfn sdkPath(comptime suffix: []"
},
{
"path": "build.zig.zon",
"chars": 577,
"preview": ".{\n .name = .lola,\n .version = \"0.1.0\",\n .fingerprint = 0xe0c26edfbdb0efcc,\n .dependencies = .{\n .arg"
},
{
"path": "design/README.md",
"chars": 250,
"preview": "# LoLa Design\n\nThe logo is not licenced under MIT licence!\nDo not use this in anywhere without previous agreement with F"
},
{
"path": "develop.lola",
"chars": 26,
"preview": "\nvar x = \"a\";\nx[0] = true;"
},
{
"path": "documentation/LoLa.md",
"chars": 2325,
"preview": "# LoLa – Quick Reference\n\n## Example\n\n```js\nvar stack = CreateStack();\n\nstack.Push(10);\nstack.Push(20);\nstack.Push(30);\n"
},
{
"path": "documentation/README.md",
"chars": 20279,
"preview": "# The LoLa Programming Language\n\n## Introduction\n\nLoLa is a small programming language developed to be embedded in games"
},
{
"path": "documentation/checklist.txt",
"chars": 5615,
"preview": "Programming Language Checklist\nby Colin McMillen, Jason Reed, and Elly Fong-Jones, 2011-10-10.\nFilled out for LoLa\n\nYou "
},
{
"path": "documentation/ir.md",
"chars": 12421,
"preview": "# LoLa Intermedia Language\n\nThis document describes all available instructions of the Lola intermediate language as well"
},
{
"path": "documentation/modules.md",
"chars": 1788,
"preview": "# LoLa Module Format\n\nNative LoLa has a binary module format that contains compiled intermediate code. This format both"
},
{
"path": "documentation/runtime-library.md",
"chars": 3416,
"preview": "# LoLa Runtime Library\n\nThis file documents the LoLa Runtime Library, a set of basic I/O routines to enable standalone L"
},
{
"path": "documentation/standard-library.md",
"chars": 6075,
"preview": "# LoLa Standard Library\n\nThis file documents the LoLa Standard Library, a set of basic routines to enable LoLa programs."
},
{
"path": "documentation/zig-api.md",
"chars": 839,
"preview": "# Zig API\n\nThe Zig API is the main API for the LoLa implementation. It exposes all concepts in a convenient matter\n\n## B"
},
{
"path": "examples/host/minimal-host/main.zig",
"chars": 3970,
"preview": "const std = @import(\"std\");\nconst lola = @import(\"lola\");\n\n////\n// Minimal API example:\n// This example shows how to get"
},
{
"path": "examples/host/multi-environment/client-a.lola",
"chars": 64,
"preview": "\nvar server = GetServer();\nserver.Store(\"msg\", \"Hello, World!\");"
},
{
"path": "examples/host/multi-environment/client-b.lola",
"chars": 52,
"preview": "var server = GetServer();\nPrint(server.Load(\"msg\"));"
},
{
"path": "examples/host/multi-environment/main.zig",
"chars": 4795,
"preview": "const std = @import(\"std\");\nconst lola = @import(\"lola\");\n\n////\n// Multi-environment communication example:\n// In this e"
},
{
"path": "examples/host/multi-environment/server.lola",
"chars": 143,
"preview": "var storage = CreateDictionary();\n\nfunction Store(key, value) {\n storage.Set(key, value);\n}\n\nfunction Load(key) {\n ret"
},
{
"path": "examples/host/serialization/main.zig",
"chars": 6127,
"preview": "const std = @import(\"std\");\nconst lola = @import(\"lola\");\n\n////\n// Serialization API example:\n// This example shows how "
},
{
"path": "examples/lola/README.md",
"chars": 263,
"preview": "# LoLa Examples\n\n- [Hello World](hello-world.lola)\n- [Iterative Fibonacci Numbers](fib-iterative.lola)\n- [Summing up an "
},
{
"path": "examples/lola/bubble-sort.lola",
"chars": 547,
"preview": "function BubbleSort(arr)\n{\n var len = Length(arr);\n\n var n = len;\n while(n > 1) {\n\n var i = 0;\n while(i < n - 1"
},
{
"path": "examples/lola/fib-iterative.lola",
"chars": 285,
"preview": "function Fibonacci(num)\n{\n var a = 0;\n var b = 1;\n var temp;\n\n while (num >= 0)\n {\n temp = a;\n "
},
{
"path": "examples/lola/forth.lola",
"chars": 1378,
"preview": "// A small example implementing a language similar to Forth\n// https://en.wikipedia.org/wiki/Forth_(programming_language"
},
{
"path": "examples/lola/game-code.lola",
"chars": 504,
"preview": "// This example was inspired by the Stone Script:\n// https://stonestoryrpg.com/stonescript/#example\n\nconst location = Ge"
},
{
"path": "examples/lola/global-setter.lola",
"chars": 78,
"preview": "var name;\nfunction SetName(n)\n{\n name = n;\n}\n\nSetName(\"xq\");\n\nPrint(name);\n"
},
{
"path": "examples/lola/hello-world.lola",
"chars": 24,
"preview": "Print(\"Hello, World!\");\n"
},
{
"path": "examples/lola/reverse-array.lola",
"chars": 260,
"preview": "// Reverse an array\nfunction ReverseArray(arr)\n{\n var i = 0;\n var l = Length(arr);\n \n while(i < l/2) {\n var tmp"
},
{
"path": "examples/lola/stupid-bench.lola",
"chars": 59,
"preview": "\nvar i = 0;\nwhile(i < 100000000) {\n i = i + 1;\n}\nPrint(i);"
},
{
"path": "examples/lola/sum-of-array.lola",
"chars": 90,
"preview": "var arr = [ 1, 2, 3 ];\nvar sum = 0;\n\nfor(v in arr) {\n sum += v;\n}\n\nPrint(\"Sum = \", sum);\n"
},
{
"path": "src/README.md",
"chars": 1042,
"preview": "# Source Structure\n\nThe project is structured into two major parts:\n- [`frontend`](frontend/) is the compiler frontend w"
},
{
"path": "src/benchmark/perf.zig",
"chars": 6159,
"preview": "//! This tool measures LoLa performance by running a set of different benchmark files\n\nconst std = @import(\"std\");\nconst"
},
{
"path": "src/benchmark/render.zig",
"chars": 8410,
"preview": "const std = @import(\"std\");\n\npub fn main() !u8 {\n var gpa = std.heap.GeneralPurposeAllocator(.{}){};\n const alloc "
},
{
"path": "src/frontend/main.zig",
"chars": 16597,
"preview": "const std = @import(\"std\");\nconst builtin = @import(\"builtin\");\nconst lola = @import(\"lola\");\nconst args_parser = @impor"
},
{
"path": "src/library/common/CompileUnit.zig",
"chars": 9365,
"preview": "const std = @import(\"std\");\n\nconst utility = @import(\"utility.zig\");\n\n// Import modules to reduce file size\n// usingname"
},
{
"path": "src/library/common/Decoder.zig",
"chars": 12166,
"preview": "const std = @import(\"std\");\n\nconst utility = @import(\"utility.zig\");\n\n// Import modules to reduce file size\nconst ir = @"
},
{
"path": "src/library/common/disassembler.zig",
"chars": 4993,
"preview": "const std = @import(\"std\");\n\nconst CompileUnit = @import(\"CompileUnit.zig\");\nconst Decoder = @import(\"Decoder.zig\");\ncon"
},
{
"path": "src/library/common/interface.zig",
"chars": 9164,
"preview": "const std = @import(\"std\");\n\nconst Struct = std.builtin.Type.Struct;\nconst StructField = std.builtin.Type.StructField;\n\n"
},
{
"path": "src/library/common/ir.zig",
"chars": 2778,
"preview": "/// This enumeration contains all possible instructions and assigns each a value.\npub const InstructionName = enum(u8) {"
},
{
"path": "src/library/common/utility.zig",
"chars": 203,
"preview": "const std = @import(\"std\");\n\npub fn clampFixedString(str: []const u8) []const u8 {\n if (std.mem.indexOfScalar(u8, str"
},
{
"path": "src/library/compiler/analysis.zig",
"chars": 19898,
"preview": "const std = @import(\"std\");\n\nconst ast = @import(\"ast.zig\");\n\nconst Location = @import(\"location.zig\").Location;\nconst S"
},
{
"path": "src/library/compiler/ast.zig",
"chars": 6239,
"preview": "const std = @import(\"std\");\n\nconst Location = @import(\"location.zig\").Location;\n\npub const UnaryOperator = enum {\n ne"
},
{
"path": "src/library/compiler/code-writer.zig",
"chars": 13560,
"preview": "const std = @import(\"std\");\n\nconst Instruction = @import(\"../common/ir.zig\").Instruction;\nconst InstructionName = @impor"
},
{
"path": "src/library/compiler/codegen.zig",
"chars": 17303,
"preview": "const std = @import(\"std\");\n\nconst ast = @import(\"ast.zig\");\n\nconst Location = @import(\"location.zig\").Location;\nconst S"
},
{
"path": "src/library/compiler/diagnostics.zig",
"chars": 3588,
"preview": "const std = @import(\"std\");\n\nconst Location = @import(\"location.zig\").Location;\n\npub const Diagnostics = struct {\n co"
},
{
"path": "src/library/compiler/location.zig",
"chars": 1250,
"preview": "const std = @import(\"std\");\n\n/// A location in a chunk of text. Can be used to locate tokens and AST structures.\npub con"
},
{
"path": "src/library/compiler/parser.zig",
"chars": 67195,
"preview": "const std = @import(\"std\");\n\nconst lexer = @import(\"tokenizer.zig\");\nconst ast = @import(\"ast.zig\");\nconst diag = @impor"
},
{
"path": "src/library/compiler/scope.zig",
"chars": 8012,
"preview": "const std = @import(\"std\");\n\nconst Type = @import(\"typeset.zig\").Type;\nconst TypeSet = @import(\"typeset.zig\").TypeSet;\n\n"
},
{
"path": "src/library/compiler/string-escaping.zig",
"chars": 4602,
"preview": "const std = @import(\"std\");\n\npub const EscapedStringIterator = struct {\n slice: []const u8,\n position: usize,\n\n "
},
{
"path": "src/library/compiler/tokenizer.zig",
"chars": 12679,
"preview": "const std = @import(\"std\");\n\nconst Diagnostics = @import(\"diagnostics.zig\").Diagnostics;\n\npub const TokenType = enum {\n "
},
{
"path": "src/library/compiler/typeset.zig",
"chars": 3784,
"preview": "const std = @import(\"std\");\n\npub const Type = enum {\n void,\n number,\n string,\n boolean,\n array,\n objec"
},
{
"path": "src/library/libraries/libs.zig",
"chars": 163,
"preview": "/// Provides the LoLa standard library.\npub const std = @import(\"stdlib.zig\");\n\n/// Provides the LoLa runtime library.\np"
},
{
"path": "src/library/libraries/runtime.zig",
"chars": 24548,
"preview": "// This file implements the LoLa Runtime Library.\n\nconst std = @import(\"std\");\nconst builtin = @import(\"builtin\");\nconst"
},
{
"path": "src/library/libraries/stdlib.zig",
"chars": 25095,
"preview": "// This file implements the LoLa standard library\n\nconst std = @import(\"std\");\nconst builtin = @import(\"builtin\");\nconst"
},
{
"path": "src/library/main.zig",
"chars": 4514,
"preview": "const zig_std = @import(\"std\");\nconst builtin = @import(\"builtin\");\n\npub const ir = @import(\"common/ir.zig\");\npub const "
},
{
"path": "src/library/runtime/Environment.zig",
"chars": 27370,
"preview": "//! An execution environment provides all needed\n//! data to execute a compiled piece of code.\n//! It stores its global "
},
{
"path": "src/library/runtime/environmentmap.zig",
"chars": 2940,
"preview": "const std = @import(\"std\");\nconst lola = @import(\"../main.zig\");\n\n/// This map is required by the VM serialization to id"
},
{
"path": "src/library/runtime/objects.zig",
"chars": 24116,
"preview": "const std = @import(\"std\");\nconst builtin = @import(\"builtin\");\nconst interfaces = @import(\"../common/interface.zig\");\n\n"
},
{
"path": "src/library/runtime/value.zig",
"chars": 28215,
"preview": "const std = @import(\"std\");\n\nconst objects = @import(\"objects.zig\");\nconst Environment = @import(\"Environment.zig\");\n\npu"
},
{
"path": "src/library/runtime/vm.zig",
"chars": 39434,
"preview": "const std = @import(\"std\");\nconst lola = @import(\"../main.zig\");\n\nconst value_unit = @import(\"value.zig\");\nconst Value ="
},
{
"path": "src/library/test/behaviour-with-stdlib.lola",
"chars": 759,
"preview": "///\n// This file tests some more behaviours, but requires the LoLa standard library and is\n// intended to be run via the"
},
{
"path": "src/library/test/behaviour.lola",
"chars": 4577,
"preview": "///\n// This file tests different behaviours of the LoLa language and is intended to be run\n// via the test suite.\n///\n\n/"
},
{
"path": "src/library/test/compiler.lola",
"chars": 2160,
"preview": "///\n// This file tests the compiler implementation and is intended to be compiled\n// via the test suite.\n// It tries to "
},
{
"path": "src/library/test/empty.lola",
"chars": 0,
"preview": ""
},
{
"path": "src/library/test/global-return.lola",
"chars": 196,
"preview": "///\n// This file tests if the implementation can handle returns from the global\n// scope and is intended to be run via t"
},
{
"path": "src/library/test/runtime.lola",
"chars": 5274,
"preview": "///\n// This file tests the implementation of the LoLa runtime library and is\n// intended to be run via the test suite.\n/"
},
{
"path": "src/library/test/stdlib.lola",
"chars": 8903,
"preview": "///\n// This file tests the implementation of the LoLa standard library and is\n// intended to be run via the test suite.\n"
},
{
"path": "src/library/test.zig",
"chars": 189,
"preview": "// this path is mainly to provide a neat test environment\n\nconst lola = @import(\"main.zig\");\n\ncomptime {\n _ = lola;\n}"
},
{
"path": "src/tools/render-md-page.zig",
"chars": 5740,
"preview": "const std = @import(\"std\");\nconst assert = std.debug.assert;\n\nconst koino = @import(\"koino\");\n\nconst MenuItem = struct {"
},
{
"path": "src/wasm-compiler/main.zig",
"chars": 10485,
"preview": "const std = @import(\"std\");\nconst lola = @import(\"lola\");\n\n// This is our global object pool that is back-referenced\n// "
},
{
"path": "website/.gitignore",
"chars": 71,
"preview": "# This is generated automatically via \"zig build website\"\ndocs/*\n*.wasm"
},
{
"path": "website/documentation.css",
"chars": 9206,
"preview": ":root {\n font-size: 1em;\n --ui: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif, \"Apple Co"
},
{
"path": "website/fonts/PT_Sans/OFL.txt",
"chars": 4517,
"preview": "Copyright (c) 2010, ParaType Ltd. (http://www.paratype.com/public),\r\nwith Reserved Font Names \"PT Sans\" and \"ParaType\"."
},
{
"path": "website/fonts/Source_Code_Pro/OFL.txt",
"chars": 4621,
"preview": "Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Re"
},
{
"path": "website/img/.gitignore",
"chars": 70,
"preview": "# this is copyrighted, so no git for you, bad file\nstub-animu-gurl.png"
},
{
"path": "website/index.htm",
"chars": 6528,
"preview": "<!doctype html>\n<html lang=\"en\">\n\n<head>\n <title>LoLa Programming Language</title>\n <link rel=\"stylesheet\" href=\"style"
},
{
"path": "website/libs/ace.js",
"chars": 370746,
"preview": "(function(){function o(n){var i=e;n&&(e[n]||(e[n]={}),i=e[n]);if(!i.define||!i.define.packaged)t.original=i.define,i.def"
},
{
"path": "website/libs/mode-javascript.js",
"chars": 18590,
"preview": "ace.define(\"ace/mode/doc_comment_highlight_rules\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/text_highlight_r"
},
{
"path": "website/libs/theme-vibrant_ink.js",
"chars": 2537,
"preview": "ace.define(\"ace/theme/vibrant_ink\",[\"require\",\"exports\",\"module\",\"ace/lib/dom\"],function(e,t,n){t.isDark=!0,t.cssClass=\""
},
{
"path": "website/libs/worker-javascript.js",
"chars": 506890,
"preview": "\"no use strict\";!function(e){function t(e,t){var n=e,r=\"\";while(n){var i=t[n];if(typeof i==\"string\")return i+r;if(i)retu"
},
{
"path": "website/libs/xterm-addon-fit.js",
"chars": 2397,
"preview": "!function(e,t){\"object\"==typeof exports&&\"object\"==typeof module?module.exports=t():\"function\"==typeof define&&define.am"
},
{
"path": "website/libs/xterm.css",
"chars": 4074,
"preview": "/**\n * Copyright (c) 2014 The xterm.js authors. All rights reserved.\n * Copyright (c) 2012-2013, Christopher Jeffrey (MI"
},
{
"path": "website/libs/xterm.js",
"chars": 276878,
"preview": "!function(e,t){if(\"object\"==typeof exports&&\"object\"==typeof module)module.exports=t();else if(\"function\"==typeof define"
},
{
"path": "website/playground.css",
"chars": 1031,
"preview": "* {\n box-sizing: border-box;\n}\n\n#toolbar {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n height: 3rem;\n paddin"
},
{
"path": "website/playground.htm",
"chars": 1349,
"preview": "<!doctype html>\n<html lang=\"en\">\n\n<head>\n <title>LoLa Playground</title>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewp"
},
{
"path": "website/playground.js",
"chars": 7181,
"preview": "\nconst templates = [\n {\n name: 'Hello, World!',\n text:\n `// Enter LoLa code here and press [Run] above to "
},
{
"path": "website/style.css",
"chars": 3461,
"preview": "@font-face {\n font-family: 'PT Sans';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: local('PT "
}
]
// ... and 4 more files (download for full content)
About this extraction
This page contains the full source code of the MasterQ32/LoLa GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 131 files (11.9 MB), approximately 574.1k tokens, and a symbol index with 628 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.