Compare commits

..

7 Commits

Author SHA1 Message Date
Scott Richmond
db5622bccd new random seed for every run 2024-08-01 18:00:11 -04:00
Scott Richmond
7f64164078 build 2024-08-01 17:45:09 -04:00
Scott Richmond
d9e0fd23ec build 2024-08-01 17:43:29 -04:00
Scott Richmond
ffed651b6e build 2024-08-01 17:40:54 -04:00
Scott Richmond
ea80f81c33 build 2024-08-01 17:31:36 -04:00
Scott Richmond
d477782ff6 build 2024-08-01 17:05:41 -04:00
Scott Richmond
4baabc0a20 build 2024-08-01 16:43:01 -04:00
9 changed files with 4071 additions and 59 deletions

Binary file not shown.

View File

@ -121,26 +121,15 @@ function command_to_state (prev_state, curr_command) {
return {...prev_state, penwidth: width} return {...prev_state, penwidth: width}
} }
case "pencolor": { case "pencolor": {
console.log(curr_command)
if (curr_command.length = 2) {
const [_, color] = curr_command const [_, color] = curr_command
return {...prev_state, pencolor: color} return {...prev_state, pencolor: color}
} else {
const [_, r, g, b, a] = curr_command
return {...prev_state, pencolor: [r, g, b, a]}
}
} }
case "setheading": { case "setheading": {
const [_, heading] = curr_command const [_, heading] = curr_command
return {...prev_state, heading: heading} return {...prev_state, heading: heading}
} }
case "loadstate": { case "loadstate": {
const [_, x, y, heading, visible, pendown, penwidth] = curr_command const [_, x, y, heading, visible, pendown, penwidth, pencolor] = curr_command
// there are 7 fixed-arity arguments
// color will either be the last one or four
let pencolor = curr_command.slice(7)
// if there's one, it's a string, unpack it
if (pencolor.length = 1) pencolor = pencolor[0]
return {position: [x, y], heading, visible, pendown, penwidth, pencolor} return {position: [x, y], heading, visible, pendown, penwidth, pencolor}
} }
case "show": { case "show": {
@ -150,9 +139,7 @@ function command_to_state (prev_state, curr_command) {
return {...prev_state, visible: false} return {...prev_state, visible: false}
} }
case "background": { case "background": {
let color = curr_command.slice(1) background_color = curr_command[1]
if (color.lengh = 1) color = color[0]
background_color = color
return prev_state return prev_state
} }
} }
@ -211,7 +198,7 @@ function turn_to_rad (heading) {
} }
function turn_to_deg (heading) { function turn_to_deg (heading) {
return (heading * 360) + 180 return (heading * 360)
} }
function svg_render_line (prev, curr) { function svg_render_line (prev, curr) {
@ -283,16 +270,16 @@ export function svg (commands) {
const view_width = maxX - minX const view_width = maxX - minX
const view_height = maxY - minY const view_height = maxY - minY
const margin = Math.max(view_width, view_height) * 0.1 const margin = Math.max(view_width, view_height) * 0.1
const x1 = minX - margin const x1 = Math.min(-300, (minX - margin))
const y1 = minY - margin const y1 = Math.min(-300, (minY - margin))
const x2 = maxX + margin const x2 = Math.max(600, (maxX + margin))
const y2 = maxY + margin const y2 = Math.max(600, (maxY + margin))
const path = svg_render_path(states) const path = svg_render_path(states)
const turtle = svg_render_turtle(states[states.length - 1]) const turtle = svg_render_turtle(states[states.length - 1])
return `<?xml version="1.0" xmlns="http://www.w3.org/2000/svg" standalone="no"?> return `<?xml version="1.0" standalone="no"?>
<svg version="1.1" style="background-color:rgb(${r} ${g} ${b}); background-opacity: ${a/255}" viewBox="${x1} ${y1} ${x2} ${y2}"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" style="background-color:rgb(${r} ${g} ${b}); background-opacity: ${a/255}" viewBox="${x1} ${y1} ${x2} ${y2}">
<g> <g transform="scale(-1, 1) rotate(180)">
${path} ${path}
${turtle} ${turtle}
</g> </g>

View File

@ -6489,7 +6489,7 @@ var __emscripten_stack_alloc = (a0) => (__emscripten_stack_alloc = wasmExports['
var _emscripten_stack_get_current = () => (_emscripten_stack_get_current = wasmExports['emscripten_stack_get_current'])(); var _emscripten_stack_get_current = () => (_emscripten_stack_get_current = wasmExports['emscripten_stack_get_current'])();
var ___cxa_is_pointer_type = createExportWrapper('__cxa_is_pointer_type', 1); var ___cxa_is_pointer_type = createExportWrapper('__cxa_is_pointer_type', 1);
var dynCall_jiji = Module['dynCall_jiji'] = createExportWrapper('dynCall_jiji', 5); var dynCall_jiji = Module['dynCall_jiji'] = createExportWrapper('dynCall_jiji', 5);
var ___emscripten_embedded_file_data = Module['___emscripten_embedded_file_data'] = 1820072; var ___emscripten_embedded_file_data = Module['___emscripten_embedded_file_data'] = 1816104;
function invoke_i(index) { function invoke_i(index) {
var sp = stackSave(); var sp = stackSave();
try { try {

Binary file not shown.

View File

@ -1,11 +1,15 @@
import {run, svg} from "./ludus.mjs" import {run, svg} from "./ludus.mjs"
const code = ` const code = `
forward! (100) print! (random ())
right! (0.25) print! (random ())
forward! (100) print! (random ())
print! (random ())
print! (random ())
print! (random ())
` `
const result = run(code) const result = run(code)
console.log(svg(result.io.turtle.data)) console.log(result.io.stdout.data)

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 937 B

After

Width:  |  Height:  |  Size: 338 KiB

View File

@ -23,9 +23,9 @@ publish:
# build the ludus jimage # build the ludus jimage
build: build:
rm build/out.mjs rm -f build/out.mjs
rm build/out.wasm rm -f build/out.wasm
rm build/ludus.jimage rm -f build/ludus.jimage
janet -c src/ludus.janet build/ludus.jimage janet -c src/ludus.janet build/ludus.jimage
cd build && just build cd build && just build
git commit -am "build" git commit -am "build"

View File

@ -1176,9 +1176,9 @@ 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! ((:background, color))
(gray as :number) -> add_command! ((:background, gray, gray, gray, 255)) (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)) -> 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)) ((r as :number, g as :number, b as :number, a as :number)) -> add_command! ((:background, (r, g, b, a)))
} }
let bg! = background! let bg! = background!
@ -1215,17 +1215,10 @@ fn hideturtle! {
} }
fn loadstate! { fn loadstate! {
"Sets the turtle state to a previously saved state. Returns the state." "Sets the turtle state to a previously saved state."
(state) -> { (state) -> {
let #{:position (x, y), heading, pendown?, pencolor, penwidth, visible?} = state let #{position, heading, pendown?, pencolor, penwidth, visible?} = state
let command = if tuple? (pencolor) add_command! ((:loadstate, position, heading, visible?, pendown?, penwidth, pencolor))
then {
let (r, g, b, a) = pencolor
(:loadstate, x, y, heading, visible?, pendown?, penwidth, r, g, b, a)
}
else (:loadstate, x, y, heading, visible?, pendown?, penwidth, pencolor)
add_command! (command)
state
} }
} }
@ -1258,8 +1251,7 @@ fn apply_command {
(:penwidth, pixels) -> assoc (state, :penwidth, pixels) (:penwidth, pixels) -> assoc (state, :penwidth, pixels)
(:pencolor, color) -> assoc (state, :pencolor, color) (:pencolor, color) -> assoc (state, :pencolor, color)
(:setheading, heading) -> assoc (state, :heading, heading) (:setheading, heading) -> assoc (state, :heading, heading)
(:loadstate, x, y, heading, visible?, pendown?, penwidth, pencolor) -> #{:position (x, y), heading, visible?, pendown?, penwidth, pencolor} (:loadstate, position, heading, visible?, pendown?, penwidth, pencolor) -> #{position, heading, visible?, pendown?, penwidth, pencolor}
(:loadstate, x, y, heading, visible?, pendown?, penwidth, r, g, b, a) -> #{:position (x, y), heading, visible?, pendown?, penwidth, :pencolor (r, g, b, a)}
(:show) -> assoc (state, :visible?, true) (:show) -> assoc (state, :visible?, true)
(:hide) -> assoc (state, :visible?, false) (:hide) -> assoc (state, :visible?, false)
(:background, _) -> state (:background, _) -> state

View File

@ -12,44 +12,72 @@
(import /src/json :as j) (import /src/json :as j)
(defn ludus [source] (defn ludus [source]
# if we can't load prelude, bail
(when (= :error prelude/pkg) (error "could not load prelude")) (when (= :error prelude/pkg) (error "could not load prelude"))
# get us a clean working slate
(def ctx @{:^parent prelude/ctx}) (def ctx @{:^parent prelude/ctx})
(def errors @[]) (def errors @[])
(var result @"") (var result @"")
(def console @"") (def console @"")
# capture all `print`s
(setdyn :out console) (setdyn :out console)
# an output table
# this will change: the shape of our output
# at the moment, there's only one stack of turtle graphics
# we will be getting more
(def out @{:errors errors :result result (def out @{:errors errors :result result
:io @{ :io @{
:stdout @{:proto [:text-stream "0.1.0"] :data console} :stdout @{:proto [:text-stream "0.1.0"] :data console}
:turtle @{:proto [:turtle-graphics "0.1.0"] :data @[]}}}) :turtle @{:proto [:turtle-graphics "0.1.0"] :data @[]}}})
### start the program
# first, scanning
(def scanned (s/scan source)) (def scanned (s/scan source))
(when (any? (scanned :errors)) (when (any? (scanned :errors))
(each err (scanned :errors) (each err (scanned :errors)
(e/scan-error err)) (e/scan-error err))
(break (-> out j/encode string))) (break (-> out j/encode string)))
# then, parsing
(def parsed (p/parse scanned)) (def parsed (p/parse scanned))
(when (any? (parsed :errors)) (when (any? (parsed :errors))
(each err (parsed :errors) (each err (parsed :errors)
(e/parse-error err)) (e/parse-error err))
(break (-> out j/encode string))) (break (-> out j/encode string)))
# then, validation
(def validated (v/valid parsed ctx)) (def validated (v/valid parsed ctx))
(when (any? (validated :errors)) (when (any? (validated :errors))
(each err (validated :errors) (each err (validated :errors)
(e/validation-error err)) (e/validation-error err))
(break (-> out j/encode string))) (break (-> out j/encode string)))
(try # and, finally, try interpreting the program
(set result (i/interpret (parsed :ast) ctx)) (try (do
# we need to do this every run or we get the very same sequence of "random" numbers every time we run a program
(math/seedrandom (os/cryptorand 8))
(set result (i/interpret (parsed :ast) ctx)))
([err] ([err]
(e/runtime-error err) (e/runtime-error err)
(break (-> out j/encode string)))) (break (-> out j/encode string))))
# stop capturing output
(setdyn :out stdout) (setdyn :out stdout)
# update our output table with our output
(set (out :result) (b/show result)) (set (out :result) (b/show result))
(set (((out :io) :turtle) :data) (get-in prelude/pkg [:turtle_commands :^value])) (set (((out :io) :turtle) :data) (get-in prelude/pkg [:turtle_commands :^value]))
# run the "postlude": any Ludus code that needs to run after each program
# right now this is just resetting the boxes that hold turtle commands and state
(try (try
(i/interpret prelude/post/ast ctx) (i/interpret prelude/post/ast ctx)
([err] (e/runtime-error err))) ([err] (e/runtime-error err)))
# json-encode our output table, and convert it from a buffer to a string (which we require for playing nice with WASM/C)
(-> out j/encode string)) (-> out j/encode string))
#### REPL
(comment (comment
# (do # (do
# (def start (os/clock)) # (def start (os/clock))