Compare commits

...

No commits in common. "main" and "actors" have entirely different histories.
main ... actors

20 changed files with 183 additions and 278 deletions

1
.envrc
View File

@ -1 +0,0 @@
use flake

1
.gitattributes vendored
View File

@ -1 +0,0 @@
*.wasm filter=lfs diff=lfs merge=lfs -text

2
.gitignore vendored
View File

@ -1,5 +1,3 @@
/target
/Cargo.lock
/node_modules
flake-inputs
.direnv

View File

@ -1,93 +1,3 @@
![Ludus logo](logo.png)
## Ludus: A friendly, dynamic, functional language
Ludus is a scripting programming language that is designed to be friendly, dynamic, and functional.
# rudus
This repo currently contains a work-in-progress implementation of an interpreter for the Ludus programming language, using [Janet](https://janet-lang.org) as a host language.
Ludus is part of the [_Thinking with Computers_ project](https://alea.ludus.dev/twc/), run by Scott Richmond at the University of Toronto, with collaborator Matt Nish-Lapidus; Bree Lohman and Mynt Marsellus are the RAs for the project. Ludus is our research language, which aspires to be a free translation of Logo for the 2020s.
Here are our design goals:
#### Friendly
Ludus, like Logo, is meant to be a teaching language, often for students who don't think of themselves as "computer people." Our intended audience are humanities and art people at the university level (undergrads, grads, faculty). Everything is kept as simple as possible, but no simpler. Everything is consistent as possible. We aspire to the best error messages we can muster, which is important for a language to be teachable. That means being as strict as we can muster, _in order to be friendlier_.
Our current development target is Ludus on the web: https://web.ludus.dev. That wires what we do on the langauge interpreter (here in this repo) to a web frontend.
Naturally, it starts with Logo's famed turtle graphics.
#### Dynamic
Statically typed programming languages generally give more helpful error messages than dynamic ones, but learning a type system (even one with robust type inference) requires learning two parallel systems: the type system and the expression system (well, and the pattern system). Type systems only really make sense once you've learned why they're necessary. And their benefits seem (to us, anyway) to be largely necessary when writing long-lived, maintainable, multi-author code. Ludus code is likely to be one-off, expressive, and single-authored.
To stay friendly, Ludus is dynamic. But: despite the dynamism, we aim to be as strict as possible. Certainly, we want to avoid the type conversion shenanigans of a language like JavaScript.
#### Functional
Ludus is emphatically functional: it uses functions for just about everything. This is both because your humble PI had his world reordered when he learned his first functional language (Elixir), and because the research into Logo and the programming cultures of MIT in the 1970s revolve around extremely functional Lisp code (i.e., Scheme). Logo is a weird little language, but it is a descendant of Lisp. So is Ludus.
Also, we believe that Ludus's immutable bindings and persistent or immutable data structures and careful approach to manipulating state lead to a lot of good pedagogical results. Learning a programming language involves learning how to model what's going on inside the computer; Ludus, we think, makes that both simpler and easier.
If you're looking for cognate languages, Ludus takes a _lot_ of design inspiration from Clojure and Elixir (which itself took a lot from Clojure). (The current--quick, dirty, and slow--version of Ludus is written in [Janet](https://janet-lang.org).) Clojure and Elixir are great! If you're asking why you should use Ludus instead of them, you're already at the point where you should be using them. Ludus is, maybe, for the people whom you'd like to work with in 5 years at your Pheonix shop (but even then, probably not).
### Status
Pre-alpha, still under active development. Lots of things change all the time.
The current version of Ludus is a pure function that runs in JavaScript as a WASM blob. We have plans for more and better things.
### Use
Current emphasis is on the web version: https://web.ludus.dev.
### Main features
* Expression-oriented: everything returns a value
* Pattern matching in all the places
* No operators: everything is called as a function
* Easy-peasy partial application with placeholders
* Function pipelines
* Persistent or immutable data structures
* Careful, explicit state management using `box`es
* Clean, concise, expressive syntax
* Value-based equality; only functions are reference types
#### Under construction
* Actor-model style concurrency.
* Faster, bytecode-based VM written in a systems language, for better performance.
* Performant persistent, immutable data structures, à la Clojure.
### `Hello, world!`
Ludus is a scripting language. At current it does not have a good REPL. Our aim is to get interactive coding absolutely correct, and our efforts in [ludus-web](https://github.com/thinking-with-computers/ludus-web) are currently under way to surface the right interactivity models for Ludus.
Either:
```
"Hello, world!"
```
`=> "Hello, world!"`
Ludus scripts (and blocks) simply return their last expression; this script returns the bare string and exits.
Or:
```
print! ("Hello, world!")
```
```
=> Hello, world!
=> :ok
```
Here, we use the `print!` function, which sends a string to a console (`stdout` on Unix, or a little console box on the web). Because `print!` returns the keyword `:ok` when it completes, that is the result of the last expression in the script--and so Ludus also prints this.
### Some code
Fibonacci numbers:
```
& fibonacci!, with multi-clause fns/pattern matching
fn fib {
"Returns the nth fibonacci number."
(1) -> 1
(2) -> 1
(n) -> add (
fib (sub (n, 1))
fib (sub (n, 2)))
}
fib (10) &=> 55
```
### More on Ludus
See the [language reference](language.md) and [the documentation for the prelude](prelude.md).
A Rust implementation of Ludus.

View File

@ -1137,7 +1137,7 @@ fn heed {
fn send_sync {
"Sends the message to the specified process, and waits for a response in the form of a `(:reply, response)` tuple."
(pid, msg) -> {
send! (pid, msg)
send (pid, msg)
receive {
(:reply, res) -> res
}
@ -1157,7 +1157,7 @@ fn request_fetch! {
request_fetch! (pid)
}
else {
send! (pid, (:reply, unbox (fetch_inbox)))
send (pid, (:reply, unbox (fetch_inbox)))
store! (fetch_inbox, ())
}
}
@ -1167,7 +1167,7 @@ fn fetch {
"Requests the contents of the URL passed in. Returns a result tuple of (:ok, <contents>) or (:err, <status code>)."
(url) -> {
let pid = self ()
spawn (fn () -> request_fetch! (pid, url))
spawn! (fn () -> request_fetch! (pid, url))
receive {
(:reply, (_, response)) -> response
}
@ -1182,7 +1182,7 @@ fn input_reader! {
input_reader! (pid)
}
else {
send! (pid, (:reply, unbox (input)))
send (pid, (:reply, unbox (input)))
store! (input, nil)
}
}
@ -1192,7 +1192,7 @@ fn read_input {
"Waits until there is input in the input buffer, and returns it once there is."
() -> {
let pid = self ()
spawn (fn () -> input_reader! (pid))
spawn! (fn () -> input_reader! (pid))
receive {
(:reply, response) -> response
}
@ -1250,6 +1250,8 @@ fn add_command! (turtle_id, command) -> {
update! (command_id, inc)
update! (turtle_commands, append (_, (turtle_id, idx, command)))
let prev = do turtle_states > unbox > turtle_id
print!("previous state: {turtle_id}", prev)
print!("applying command", command)
let curr = apply_command (prev, command)
update! (turtle_states, assoc (_, turtle_id, curr))
:ok
@ -1299,11 +1301,7 @@ let pd! = pendown!
fn pencolor! {
"Changes the turtle's pen color. Takes a single grayscale value, an rgb tuple, or an rgba tuple. Alias: `pencolour!`, `pc!`"
(color as :keyword) -> {
if color (colors)
then add_command! (:turtle_0, (:pencolor, color))
else panic! ("There is no color named {color} in the colors dict.")
}
(color as :keyword) -> add_command! (:turtle_0, (:pencolor, color))
(gray as :number) -> add_command! (:turtle_0, (:pencolor, (gray, gray, gray, 255)))
((r as :number, g as :number, b as :number)) -> add_command! (:turtle_0, (:pencolor, (r, g, b, 255)))
((r as :number, g as :number, b as :number, a as :number)) -> add_command! (:turtle_0, (:pencolor, (r, g, b, a)))
@ -1372,29 +1370,16 @@ fn loadstate! {
fn turtle_listener () -> {
receive {
(:forward!, steps as :number) -> add_command! (self (), (:forward, steps))
(:fd!, steps as :number) -> add_command! (self (), (:forward, steps))
(:back!, steps as :number) -> add_command! (self (), (:back, steps))
(:bk!, steps as :number) -> add_command! (self (), (:back, steps))
(:left!, turns as :number) -> add_command! (self (), (:left, turns))
(:lt!, turns as :number) -> add_command! (self (), (:left, turns))
(:right!, turns as :number) -> add_command! (self (), (:right, turns))
(:rt!, turns as :number) -> add_command! (self (), (:right, turns))
(:penup!) -> add_command! (self (), (:penup))
(:pu!) -> add_command! (self (), (:penup))
(:pendown!) -> add_command! (self (), (:pendown))
(:pd!) -> add_command! (self (), (:pendown))
(:pencolor!, color as :keyword) -> if color (colors)
then add_command! (self (), (:pencolor, color))
else panic! ("There is no color {color} in the colors dict.")
(:pencolor!, color as :keyword) -> add_command! (self (), (:pencolor, color))
(:pencolor!, gray as :number) -> add_command! (self (), (:pencolor, (gray, gray, gray, 255)))
(:pencolor!, (r as :number, g as :number, b as :number)) -> add_command! (self (), (:pencolor, (r, g, b, 255)))
(:pencolor!, (r as :number, g as :number, b as :number, a as :number)) -> add_command! (self (), (:pencolor, (r, g, b, a)))
(:pc!, color as :keyword) -> add_command! (self (), (:pencolor, color))
(:pc!, gray as :number) -> add_command! (self (), (:pencolor, (gray, gray, gray, 255)))
(:pc!, (r as :number, g as :number, b as :number)) -> add_command! (self (), (:pencolor, (r, g, b, 255)))
(:pc!, (r as :number, g as :number, b as :number, a as :number)) -> add_command! (self (), (:pencolor, (r, g, b, a)))
(:penwidth!, width as :number) -> add_command! (self (), (:penwidth, width))
(:pw!, width as :number) -> add_command! (self (), (:penwidth, width))
(:home!) -> add_command! (self (), (:home))
(:goto!, x as :number, y as :number) -> add_command! (self (), (:goto, (x, y)))
(:goto!, (x as :number, y as :number)) -> add_command! (self (), (:goto, (x, y)))
@ -1405,10 +1390,10 @@ fn turtle_listener () -> {
add_command! (self (), (:loadstate, position, heading, visible?, pendown?, penwidth, pencolor))
}
(:pencolor, pid) -> send! (pid, (:reply, do turtle_states > unbox > self () > :pencolor))
(:position, pid) -> send! (pid, (:reply, do turtle_states > unbox > self () > :position))
(:penwidth, pid) -> send! (pid, (:reply, do turtle_states > unbox > self () > :penwidth))
(:heading, pid) -> send! (pid, (:reply, do turtle_states > unbox > self () > :heading))
(:pencolor, pid) -> send (pid, (:reply, do turtle_states > unbox > self () > :pencolor))
(:position, pid) -> send (pid, (:reply, do turtle_states > unbox > self () > :position))
(:penwidth, pid) -> send (pid, (:reply, do turtle_states > unbox > self () > :penwidth))
(:heading, pid) -> send (pid, (:reply, do turtle_states > unbox > self () > :heading))
does_not_understand -> {
let pid = self ()
panic! "{pid} does not understand message: {does_not_understand}"
@ -1420,7 +1405,7 @@ fn turtle_listener () -> {
fn spawn_turtle {
"Spawns a new turtle in a new process. Methods on the turtle process mirror those of turtle graphics functions in prelude. Returns the pid of the new turtle."
() -> {
let pid = spawn (fn () -> turtle_listener ())
let pid = spawn! (fn () -> turtle_listener ())
update! (turtle_states, assoc (_, pid, turtle_init))
pid
}

View File

@ -5,7 +5,7 @@ e.g., running `doc! (add)` will send the documentation for `add` to the console.
For more information on the syntax & semantics of the Ludus language, see [language.md](./language.md).
The prelude itself is just a Ludus file, which you can see at [prelude.ld](../assets/prelude.ld).
The prelude itself is just a Ludus file, which you can see at [prelude.ld](./prelude.ld).
## A few notes
**Naming conventions.** Functions whose name ends with a question mark, e.g., `eq?`, return booleans.

View File

@ -1,64 +0,0 @@
{
"nodes": {
"fenix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1751957021,
"narHash": "sha256-2h9T/5Tyd2ounm3DAn+8LftRXctotQ/XD2aoZ9XBtsI=",
"owner": "nix-community",
"repo": "fenix",
"rev": "fcb0981c8fe996743fc57087e781017eab6e46f9",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1751949589,
"narHash": "sha256-mgFxAPLWw0Kq+C8P3dRrZrOYEQXOtKuYVlo9xvPntt8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9b008d60392981ad674e04016d25619281550a9d",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"root": {
"inputs": {
"fenix": "fenix",
"nixpkgs": "nixpkgs"
}
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1751913732,
"narHash": "sha256-h6rTTB4MBJSIr4xsXxCi8fk8LdiFbwOw/g3QqBHLpW4=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "778e08df16294e4a2c11a40136f69f908e9be879",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@ -1,27 +0,0 @@
{
description = "Ludus dev flake (rust)";
inputs.fenix = {
url = "github:nix-community/fenix";
inputs.nixpkgs.follows = "nixpkgs";
};
outputs = {nixpkgs, fenix, ... }@inputs:
let
system = "x86_64-linux"; # your version
pkgs = nixpkgs.legacyPackages.x86_64-linux;
fx = fenix.packages.x86_64-linux;
in
{
devShells.${system}.default = pkgs.mkShell rec {
packages = [
pkgs.wasm-pack
(fx.combine [
fx.latest.cargo
fx.latest.rustc
fx.targets.wasm32-unknown-unknown.latest.rust-std
])
];
};
};
}

View File

@ -81,7 +81,7 @@ e.g., running `doc! (add)` will send the documentation for `add` to the console.
For more information on the syntax & semantics of the Ludus language, see [language.md](./language.md).
The prelude itself is just a Ludus file, which you can see at [prelude.ld](../assets/prelude.ld).
The prelude itself is just a Ludus file, which you can see at [prelude.ld](./prelude.ld).
## A few notes
**Naming conventions.** Functions whose name ends with a question mark, e.g., `eq?`, return booleans.

View File

@ -27,10 +27,10 @@ git_status := `git status -s`
# publish this branch into release
release:
echo {{ if git_status == "" {"git status ok"} else {error("please commit changes first")} }}
git checkout release
git merge {{from_branch}}
just build
-git commit -am "release build"
git checkout release
git merge {{from_branch}}
git push
git checkout {{from_branch}}

BIN
logo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

View File

@ -194,7 +194,7 @@ export function key_up (key) {
export {p5} from "./p5.js"
export function svg (commands) {
console.log(`generating svg for ${code}`)
console.log("generating svg for ${code}")
return svg_2(commands, code)
}

View File

@ -68,13 +68,11 @@ export function p5 (commands) {
const new_state = command_to_state(prev_state, this_command)
all_states[turtle_id].push(new_state)
}
console.log(all_states)
const [r, g, b, _] = resolve_color(background_color)
if ((r + g + b)/3 > 128) set_turtle_color([0, 0, 0, 150])
const p5_calls = [...p5_call_root()]
for (const states of Object.values(all_states)) {
p5_calls.push(["strokeWeight", 1])
p5_calls.push(["stroke", 255])
// console.log(states)
for (let i = 1; i < states.length; ++i) {
const prev = states[i - 1]
const curr = states[i]

4
pkg/rudus.d.ts vendored
View File

@ -14,8 +14,8 @@ export interface InitOutput {
readonly __wbindgen_malloc: (a: number, b: number) => number;
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
readonly __wbindgen_export_6: WebAssembly.Table;
readonly closure343_externref_shim: (a: number, b: number, c: any) => void;
readonly closure365_externref_shim: (a: number, b: number, c: any, d: any) => void;
readonly closure362_externref_shim: (a: number, b: number, c: any) => void;
readonly closure385_externref_shim: (a: number, b: number, c: any, d: any) => void;
readonly __wbindgen_start: () => void;
}

View File

@ -17,6 +17,22 @@ function handleError(f, args) {
}
}
function logError(f, args) {
try {
return f.apply(this, args);
} catch (e) {
let error = (function () {
try {
return e instanceof Error ? `${e.message}\n\nStack:\n${e.stack}` : e.toString();
} catch(_) {
return "<failed to stringify thrown value>";
}
}());
console.error("wasm-bindgen: imported JS function that was not marked as `catch` threw an error:", error);
throw e;
}
}
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
@ -54,6 +70,8 @@ const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
function passStringToWasm0(arg, malloc, realloc) {
if (typeof(arg) !== 'string') throw new Error(`expected a string argument, found ${typeof(arg)}`);
if (realloc === undefined) {
const buf = cachedTextEncoder.encode(arg);
const ptr = malloc(buf.length, 1) >>> 0;
@ -82,7 +100,7 @@ function passStringToWasm0(arg, malloc, realloc) {
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
const ret = encodeString(arg, view);
if (ret.read !== arg.length) throw new Error('failed to pass whole string');
offset += ret.written;
ptr = realloc(ptr, len, offset, 1) >>> 0;
}
@ -104,6 +122,12 @@ function isLikeNone(x) {
return x === undefined || x === null;
}
function _assertBoolean(n) {
if (typeof(n) !== 'boolean') {
throw new Error(`expected a boolean argument, found ${typeof(n)}`);
}
}
const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined')
? { register: () => {}, unregister: () => {} }
: new FinalizationRegistry(state => {
@ -134,6 +158,71 @@ function makeMutClosure(arg0, arg1, dtor, f) {
CLOSURE_DTORS.register(real, state, state);
return real;
}
function debugString(val) {
// primitive types
const type = typeof val;
if (type == 'number' || type == 'boolean' || val == null) {
return `${val}`;
}
if (type == 'string') {
return `"${val}"`;
}
if (type == 'symbol') {
const description = val.description;
if (description == null) {
return 'Symbol';
} else {
return `Symbol(${description})`;
}
}
if (type == 'function') {
const name = val.name;
if (typeof name == 'string' && name.length > 0) {
return `Function(${name})`;
} else {
return 'Function';
}
}
// objects
if (Array.isArray(val)) {
const length = val.length;
let debug = '[';
if (length > 0) {
debug += debugString(val[0]);
}
for(let i = 1; i < length; i++) {
debug += ', ' + debugString(val[i]);
}
debug += ']';
return debug;
}
// Test for built-in
const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val));
let className;
if (builtInMatches && builtInMatches.length > 1) {
className = builtInMatches[1];
} else {
// Failed to match the standard '[object ClassName]'
return toString.call(val);
}
if (className == 'Object') {
// we're a user defined class or Object
// JSON.stringify avoids problems with cycles, and is generally much
// easier than looping through ownProperties of `val`.
try {
return 'Object(' + JSON.stringify(val) + ')';
} catch (_) {
return 'Object';
}
}
// errors
if (val instanceof Error) {
return `${val.name}: ${val.message}\n${val.stack}`;
}
// TODO we could test for more things here, like `Set`s and `Map`s.
return className;
}
/**
* @param {string} src
* @returns {Promise<void>}
@ -145,12 +234,19 @@ export function ludus(src) {
return ret;
}
function __wbg_adapter_18(arg0, arg1, arg2) {
wasm.closure343_externref_shim(arg0, arg1, arg2);
function _assertNum(n) {
if (typeof(n) !== 'number') throw new Error(`expected a number argument, found ${typeof(n)}`);
}
function __wbg_adapter_20(arg0, arg1, arg2) {
_assertNum(arg0);
_assertNum(arg1);
wasm.closure362_externref_shim(arg0, arg1, arg2);
}
function __wbg_adapter_44(arg0, arg1, arg2, arg3) {
wasm.closure365_externref_shim(arg0, arg1, arg2, arg3);
function __wbg_adapter_46(arg0, arg1, arg2, arg3) {
_assertNum(arg0);
_assertNum(arg1);
wasm.closure385_externref_shim(arg0, arg1, arg2, arg3);
}
async function __wbg_load(module, imports) {
@ -195,7 +291,7 @@ function __wbg_get_imports() {
const ret = arg0.call(arg1, arg2);
return ret;
}, arguments) };
imports.wbg.__wbg_error_7534b8e9a36f1ab4 = function(arg0, arg1) {
imports.wbg.__wbg_error_7534b8e9a36f1ab4 = function() { return logError(function (arg0, arg1) {
let deferred0_0;
let deferred0_1;
try {
@ -205,7 +301,7 @@ function __wbg_get_imports() {
} finally {
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
}
};
}, arguments) };
imports.wbg.__wbg_io_5a3c8ea72d8c6ea3 = function() { return handleError(function (arg0, arg1) {
let deferred0_0;
let deferred0_1;
@ -218,17 +314,17 @@ function __wbg_get_imports() {
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
}
}, arguments) };
imports.wbg.__wbg_log_11652c6a56eeddfb = function(arg0, arg1) {
imports.wbg.__wbg_log_11652c6a56eeddfb = function() { return logError(function (arg0, arg1) {
console.log(getStringFromWasm0(arg0, arg1));
};
imports.wbg.__wbg_new_23a2665fac83c611 = function(arg0, arg1) {
}, arguments) };
imports.wbg.__wbg_new_23a2665fac83c611 = function() { return logError(function (arg0, arg1) {
try {
var state0 = {a: arg0, b: arg1};
var cb0 = (arg0, arg1) => {
const a = state0.a;
state0.a = 0;
try {
return __wbg_adapter_44(a, state0.b, arg0, arg1);
return __wbg_adapter_46(a, state0.b, arg0, arg1);
} finally {
state0.a = a;
}
@ -238,65 +334,65 @@ function __wbg_get_imports() {
} finally {
state0.a = state0.b = 0;
}
};
imports.wbg.__wbg_new_8a6f238a6ece86ea = function() {
}, arguments) };
imports.wbg.__wbg_new_8a6f238a6ece86ea = function() { return logError(function () {
const ret = new Error();
return ret;
};
imports.wbg.__wbg_newnoargs_105ed471475aaf50 = function(arg0, arg1) {
}, arguments) };
imports.wbg.__wbg_newnoargs_105ed471475aaf50 = function() { return logError(function (arg0, arg1) {
const ret = new Function(getStringFromWasm0(arg0, arg1));
return ret;
};
imports.wbg.__wbg_now_8dddb61fa4928554 = function() {
}, arguments) };
imports.wbg.__wbg_now_8dddb61fa4928554 = function() { return logError(function () {
const ret = Date.now();
return ret;
};
imports.wbg.__wbg_queueMicrotask_97d92b4fcc8a61c5 = function(arg0) {
}, arguments) };
imports.wbg.__wbg_queueMicrotask_97d92b4fcc8a61c5 = function() { return logError(function (arg0) {
queueMicrotask(arg0);
};
imports.wbg.__wbg_queueMicrotask_d3219def82552485 = function(arg0) {
}, arguments) };
imports.wbg.__wbg_queueMicrotask_d3219def82552485 = function() { return logError(function (arg0) {
const ret = arg0.queueMicrotask;
return ret;
};
imports.wbg.__wbg_random_57c118f142535bb6 = function() {
}, arguments) };
imports.wbg.__wbg_random_57c118f142535bb6 = function() { return logError(function () {
const ret = Math.random();
return ret;
};
imports.wbg.__wbg_resolve_4851785c9c5f573d = function(arg0) {
}, arguments) };
imports.wbg.__wbg_resolve_4851785c9c5f573d = function() { return logError(function (arg0) {
const ret = Promise.resolve(arg0);
return ret;
};
imports.wbg.__wbg_stack_0ed75d68575b0f3c = function(arg0, arg1) {
}, arguments) };
imports.wbg.__wbg_stack_0ed75d68575b0f3c = function() { return logError(function (arg0, arg1) {
const ret = arg1.stack;
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
};
imports.wbg.__wbg_static_accessor_GLOBAL_88a902d13a557d07 = function() {
}, arguments) };
imports.wbg.__wbg_static_accessor_GLOBAL_88a902d13a557d07 = function() { return logError(function () {
const ret = typeof global === 'undefined' ? null : global;
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
};
imports.wbg.__wbg_static_accessor_GLOBAL_THIS_56578be7e9f832b0 = function() {
}, arguments) };
imports.wbg.__wbg_static_accessor_GLOBAL_THIS_56578be7e9f832b0 = function() { return logError(function () {
const ret = typeof globalThis === 'undefined' ? null : globalThis;
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
};
imports.wbg.__wbg_static_accessor_SELF_37c5d418e4bf5819 = function() {
}, arguments) };
imports.wbg.__wbg_static_accessor_SELF_37c5d418e4bf5819 = function() { return logError(function () {
const ret = typeof self === 'undefined' ? null : self;
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
};
imports.wbg.__wbg_static_accessor_WINDOW_5de37043a91a9c40 = function() {
}, arguments) };
imports.wbg.__wbg_static_accessor_WINDOW_5de37043a91a9c40 = function() { return logError(function () {
const ret = typeof window === 'undefined' ? null : window;
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
};
imports.wbg.__wbg_then_44b73946d2fb3e7d = function(arg0, arg1) {
}, arguments) };
imports.wbg.__wbg_then_44b73946d2fb3e7d = function() { return logError(function (arg0, arg1) {
const ret = arg0.then(arg1);
return ret;
};
imports.wbg.__wbg_then_48b406749878a531 = function(arg0, arg1, arg2) {
}, arguments) };
imports.wbg.__wbg_then_48b406749878a531 = function() { return logError(function (arg0, arg1, arg2) {
const ret = arg0.then(arg1, arg2);
return ret;
};
}, arguments) };
imports.wbg.__wbindgen_cb_drop = function(arg0) {
const obj = arg0.original;
if (obj.cnt-- == 1) {
@ -304,11 +400,19 @@ function __wbg_get_imports() {
return true;
}
const ret = false;
_assertBoolean(ret);
return ret;
};
imports.wbg.__wbindgen_closure_wrapper1044 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 344, __wbg_adapter_18);
imports.wbg.__wbindgen_closure_wrapper8195 = function() { return logError(function (arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 363, __wbg_adapter_20);
return ret;
}, arguments) };
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
const ret = debugString(arg1);
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
};
imports.wbg.__wbindgen_init_externref_table = function() {
const table = wasm.__wbindgen_export_2;
@ -322,10 +426,12 @@ function __wbg_get_imports() {
};
imports.wbg.__wbindgen_is_function = function(arg0) {
const ret = typeof(arg0) === 'function';
_assertBoolean(ret);
return ret;
};
imports.wbg.__wbindgen_is_undefined = function(arg0) {
const ret = arg0 === undefined;
_assertBoolean(ret);
return ret;
};
imports.wbg.__wbindgen_string_get = function(arg0, arg1) {

Binary file not shown.

View File

@ -9,6 +9,6 @@ export const __wbindgen_free: (a: number, b: number, c: number) => void;
export const __wbindgen_malloc: (a: number, b: number) => number;
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
export const __wbindgen_export_6: WebAssembly.Table;
export const closure343_externref_shim: (a: number, b: number, c: any) => void;
export const closure365_externref_shim: (a: number, b: number, c: any, d: any) => void;
export const closure362_externref_shim: (a: number, b: number, c: any) => void;
export const closure385_externref_shim: (a: number, b: number, c: any, d: any) => void;
export const __wbindgen_start: () => void;

View File

@ -79,7 +79,7 @@ export function svg (commands, code) {
const new_state = command_to_state(prev_state, this_command)
all_states[turtle_id].push(new_state)
}
let maxX = 0, maxY = 0, minX = 0, minY = 0
let maxX = -Infinity, maxY = -Infinity, minX = Infinity, minY = Infinity
for (const states of Object.values(all_states)) {
for (const {position: [x, y]} of states) {
maxX = Math.max(maxX, x)
@ -90,8 +90,8 @@ export function svg (commands, code) {
}
const [r, g, b] = resolve_color(background_color)
if ((r+g+b)/3 > 128) set_turtle_color([0, 0, 0, 150])
const view_width = Math.max((maxX - minX) * 1.2, 200)
const view_height = Math.max((maxY - minY) * 1.2, 200)
const view_width = (maxX - minX) * 1.2
const view_height = (maxY - minY) * 1.2
const margin = Math.max(view_width, view_height) * 0.1
const x_origin = minX - margin
const y_origin = -maxY - margin

View File

@ -1158,7 +1158,7 @@ impl Compiler {
}
self.pop_n(self.stack_depth - stack_depth);
self.emit_op(Op::Load);
self.stack_depth += 1;
// self.stack_depth += 1;
self.msg("********receive completed".to_string());
}
MatchClause(..) => unreachable!(),

View File

@ -503,10 +503,11 @@ impl World {
fn report_process_end(&mut self) {
let result = self.active_result().clone().unwrap();
if let Err(panic) = result {
let msg = format!("Process :{} panicked: {}", self.active_id().unwrap(), crate::errors::panic(panic));
self.send_ludus_msg(msg)
}
let msg = match result {
Ok(value) => format!("Process {} returned with {}", self.active_id().unwrap(), value.show()),
Err(panic) => format!("Process {} panicked with {}", self.active_id().unwrap(), crate::errors::panic(panic))
};
self.send_ludus_msg(msg);
}
pub async fn run(&mut self) {