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

Binary file not shown.

View File

@ -1117,6 +1117,14 @@ impl Creature {
self.call_stack.push(frame); self.call_stack.push(frame);
self.ip = 0; 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())) return self.panic_with(format!("{} is not a function", called.show()))
} }
@ -1229,6 +1237,15 @@ impl Creature {
self.call_stack.push(frame); self.call_stack.push(frame);
self.ip = 0; 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())) return self.panic_with(format!("{} is not a function", called.show()))
} }