oops: implement called keywords!

This commit is contained in:
Scott Richmond 2025-07-05 15:49:36 -04:00
parent ff6aaf5cdf
commit 84101711f2
4 changed files with 50 additions and 32 deletions

View File

@ -1052,125 +1052,126 @@ let turtle_init = #{
& turtle_commands is a list of commands, expressed as tuples
box turtle_commands = []
box turtle_state = turtle_init
box turtle_states = #{:turtle_0 turtle_init}
box command_id = 0
fn apply_command
fn add_command! (command) -> {
fn add_command! (turtle_id, command) -> {
let idx = unbox (command_id)
update! (command_id, inc)
update! (turtle_commands, append (_, (:turtle_0, idx, command)))
let prev = unbox (turtle_state)
update! (turtle_commands, append (_, (turtle_id, idx, command)))
let prev = do turtle_states > unbox > turtle_id
let curr = apply_command (prev, command)
store! (turtle_state, curr)
update! (turtle_states, assoc (_, turtle_id, curr))
:ok
}
fn forward! {
"Moves the turtle forward by a number of steps. Alias: fd!"
(steps as :number) -> add_command! ((:forward, steps))
(steps as :number) -> add_command! (:turtle_0, (:forward, steps))
}
let fd! = forward!
fn back! {
"Moves the turtle backward by a number of steps. Alias: bk!"
(steps as :number) -> add_command! ((:back, steps))
(steps as :number) -> add_command! (:turtle_0, (:back, steps))
}
let bk! = back!
fn left! {
"Rotates the turtle left, measured in turns. Alias: lt!"
(turns as :number) -> add_command! ((:left, turns))
(turns as :number) -> add_command! (:turtle_0, (:left, turns))
}
let lt! = left!
fn right! {
"Rotates the turtle right, measured in turns. Alias: rt!"
(turns as :number) -> add_command! ((:right, turns))
(turns as :number) -> add_command! (:turtle_0, (:right, turns))
}
let rt! = right!
fn penup! {
"Lifts the turtle's pen, stopping it from drawing. Alias: pu!"
() -> add_command! ((:penup))
() -> add_command! (:turtle_0, (:penup))
}
let pu! = penup!
fn pendown! {
"Lowers the turtle's pen, causing it to draw. Alias: pd!"
() -> add_command! ((:pendown))
() -> add_command! (:turtle_0, (:pendown))
}
let pd! = pendown!
fn pencolor! {
"Changes the turtle's pen color. Takes a single grayscale value, an rgb tuple, or an rgba tuple. Alias: pc!"
(color as :keyword) -> add_command! ((:pencolor, color))
(gray as :number) -> add_command! ((:pencolor, (gray, gray, gray, 255)))
((r as :number, g as :number, b as :number)) -> add_command! ((:pencolor, (r, g, b, 255)))
((r as :number, g as :number, b as :number, a as :number)) -> add_command! ((:pencolor, (r, g, b, a)))
(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)))
}
let pc! = pencolor!
fn penwidth! {
"Sets the width of the turtle's pen, measured in pixels. Alias: pw!"
(width as :number) -> add_command! ((:penwidth, width))
(width as :number) -> add_command! (:turtle_0, (:penwidth, width))
}
let pw! = penwidth!
fn background! {
"Sets the background color behind the turtle and path. Alias: bg!"
(color as :keyword) -> add_command! ((:background, color))
(gray as :number) -> add_command! ((:background, (gray, gray, gray, 255)))
((r as :number, g as :number, b as :number)) -> add_command! ((:background, (r, g, b, 255)))
((r as :number, g as :number, b as :number, a as :number)) -> add_command! ((:background, (r, g, b, a)))
(color as :keyword) -> add_command! (:turtle_0, (:background, color))
(gray as :number) -> add_command! (:turtle_0, (:background, (gray, gray, gray, 255)))
((r as :number, g as :number, b as :number)) -> add_command! (:turtle_0, (:background, (r, g, b, 255)))
((r as :number, g as :number, b as :number, a as :number)) -> add_command! (:turtle_0, (:background, (r, g, b, a)))
}
let bg! = background!
fn home! {
"Sends the turtle home: to the centre of the screen, pointing up. If the pen is down, the turtle will draw a path to home."
() -> add_command! ((:home))
() -> add_command! (:turtle_0, (:home))
}
fn clear! {
"Clears the canvas and sends the turtle home."
() -> add_command! ((:clear))
() -> add_command! (:turtle_0, (:clear))
}
fn goto! {
"Sends the turtle to (x, y) coordinates. If the pen is down, the turtle will draw a path to its new location."
(x as :number, y as :number) -> add_command! ((:goto, (x, y)))
(x as :number, y as :number) -> add_command! (:turtle_0, (:goto, (x, y)))
((x, y)) -> goto! (x, y)
}
fn setheading! {
"Sets the turtle's heading. The angle is specified in turns, with 0 pointing up. Increasing values rotate the turtle counter-clockwise."
(heading as :number) -> add_command! ((:setheading, heading))
(heading as :number) -> add_command! (:turtle_0, (:setheading, heading))
}
fn showturtle! {
"If the turtle is hidden, shows the turtle. If the turtle is already visible, does nothing."
() -> add_command! ((:show))
() -> add_command! (:turtle_0, (:show))
}
fn hideturtle! {
"If the turtle is visible, hides it. If the turtle is already hidden, does nothing."
() -> add_command! ((:hide))
() -> add_command! (:turtle_0, (:hide))
}
fn loadstate! {
"Sets the turtle state to a previously saved state."
(state) -> {
let #{position, heading, pendown?, pencolor, penwidth, visible?} = state
add_command! ((:loadstate, position, heading, visible?, pendown?, penwidth, pencolor))
add_command! (:turtle_0, (:loadstate, position, heading, visible?, pendown?, penwidth, pencolor))
}
}
@ -1214,27 +1215,27 @@ fn apply_command {
& position () -> (x, y)
fn position {
"Returns the turtle's current position."
() -> do turtle_state > unbox > :position
() -> do turtle_states > unbox > :turtle_0 > :position
}
fn heading {
"Returns the turtle's current heading."
() -> do turtle_state > unbox > :heading
() -> do turtle_states > unbox > :turtle_0 > :heading
}
fn pendown? {
"Returns the turtle's pen state: true if the pen is down."
() -> do turtle_state > unbox > :pendown?
() -> do turtle_states > unbox > :turtle_0 > :pendown?
}
fn pencolor {
"Returns the turtle's pen color as an (r, g, b, a) tuple or keyword."
() -> do turtle_state > unbox > :pencolor
() -> do turtle_states > unbox > :turtle_0 > :pencolor
}
fn penwidth {
"Returns the turtle's pen width in pixels."
() -> do turtle_state > unbox > :penwidth
() -> do turtle_states > unbox > :turtle_0 > :penwidth
}
&&& fake some lispisms with tuples

View File

@ -1248,6 +1248,6 @@ We can revisit the idea of a special spawn form later.
And now, we build capacity for building projects.
* [ ] allow for multiple turtles
- [ ] rework p5 function in `ludus.js` to properly parse commands targeted at different turtles
- [x] rework p5 function in `ludus.js` to properly parse commands targeted at different turtles
- [ ] write ludus code to spawn & manipulate turtle actors
* [ ] do some sample multiturtle sketches

Binary file not shown.

View File

@ -1117,6 +1117,14 @@ impl Creature {
self.call_stack.push(frame);
self.ip = 0;
}
Value::Keyword(key) => {
let dict = self.pop();
match dict {
Value::Dict(d) => self
.push(d.get(&Key::Keyword(key)).unwrap_or(&Value::Nil).clone()),
_ => self.push(Value::Nil),
}
}
_ => {
return self.panic_with(format!("{} is not a function", called.show()))
}
@ -1229,6 +1237,15 @@ impl Creature {
self.call_stack.push(frame);
self.ip = 0;
}
Value::Keyword(key) => {
let dict = self.pop();
match dict {
Value::Dict(d) => self
.push(d.get(&Key::Keyword(key)).unwrap_or(&Value::Nil).clone()),
_ => self.push(Value::Nil),
}
}
_ => {
return self.panic_with(format!("{} is not a function", called.show()))
}