Compare commits
No commits in common. "db5622bccd9c52870d7885e463cb39636a0076ed" and "1085c7ae4474ee32262182d687bc7ae61d4afb1c" have entirely different histories.
db5622bccd
...
1085c7ae44
Binary file not shown.
|
@ -121,15 +121,26 @@ function command_to_state (prev_state, curr_command) {
|
||||||
return {...prev_state, penwidth: width}
|
return {...prev_state, penwidth: width}
|
||||||
}
|
}
|
||||||
case "pencolor": {
|
case "pencolor": {
|
||||||
const [_, color] = curr_command
|
console.log(curr_command)
|
||||||
return {...prev_state, pencolor: color}
|
if (curr_command.length = 2) {
|
||||||
|
const [_, color] = curr_command
|
||||||
|
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, pencolor] = curr_command
|
const [_, x, y, heading, visible, pendown, penwidth] = 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": {
|
||||||
|
@ -139,7 +150,9 @@ function command_to_state (prev_state, curr_command) {
|
||||||
return {...prev_state, visible: false}
|
return {...prev_state, visible: false}
|
||||||
}
|
}
|
||||||
case "background": {
|
case "background": {
|
||||||
background_color = curr_command[1]
|
let color = curr_command.slice(1)
|
||||||
|
if (color.lengh = 1) color = color[0]
|
||||||
|
background_color = color
|
||||||
return prev_state
|
return prev_state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,7 +211,7 @@ function turn_to_rad (heading) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function turn_to_deg (heading) {
|
function turn_to_deg (heading) {
|
||||||
return (heading * 360)
|
return (heading * 360) + 180
|
||||||
}
|
}
|
||||||
|
|
||||||
function svg_render_line (prev, curr) {
|
function svg_render_line (prev, curr) {
|
||||||
|
@ -270,16 +283,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 = Math.min(-300, (minX - margin))
|
const x1 = minX - margin
|
||||||
const y1 = Math.min(-300, (minY - margin))
|
const y1 = minY - margin
|
||||||
const x2 = Math.max(600, (maxX + margin))
|
const x2 = maxX + margin
|
||||||
const y2 = Math.max(600, (maxY + margin))
|
const y2 = 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" standalone="no"?>
|
return `<?xml version="1.0" xmlns="http://www.w3.org/2000/svg" standalone="no"?>
|
||||||
<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}">
|
<svg version="1.1" style="background-color:rgb(${r} ${g} ${b}); background-opacity: ${a/255}" viewBox="${x1} ${y1} ${x2} ${y2}">
|
||||||
|
|
||||||
<g transform="scale(-1, 1) rotate(180)">
|
<g>
|
||||||
${path}
|
${path}
|
||||||
${turtle}
|
${turtle}
|
||||||
</g>
|
</g>
|
||||||
|
|
|
@ -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'] = 1816104;
|
var ___emscripten_embedded_file_data = Module['___emscripten_embedded_file_data'] = 1820072;
|
||||||
function invoke_i(index) {
|
function invoke_i(index) {
|
||||||
var sp = stackSave();
|
var sp = stackSave();
|
||||||
try {
|
try {
|
||||||
|
|
BIN
build/out.wasm
BIN
build/out.wasm
Binary file not shown.
|
@ -1,15 +1,11 @@
|
||||||
import {run, svg} from "./ludus.mjs"
|
import {run, svg} from "./ludus.mjs"
|
||||||
|
|
||||||
const code = `
|
const code = `
|
||||||
print! (random ())
|
forward! (100)
|
||||||
print! (random ())
|
right! (0.25)
|
||||||
print! (random ())
|
forward! (100)
|
||||||
print! (random ())
|
|
||||||
print! (random ())
|
|
||||||
print! (random ())
|
|
||||||
|
|
||||||
`
|
`
|
||||||
|
|
||||||
const result = run(code)
|
const result = run(code)
|
||||||
|
|
||||||
console.log(result.io.stdout.data)
|
console.log(svg(result.io.turtle.data))
|
||||||
|
|
4019
build/svg_test.svg
4019
build/svg_test.svg
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 338 KiB After Width: | Height: | Size: 937 B |
6
justfile
6
justfile
|
@ -23,9 +23,9 @@ publish:
|
||||||
|
|
||||||
# build the ludus jimage
|
# build the ludus jimage
|
||||||
build:
|
build:
|
||||||
rm -f build/out.mjs
|
rm build/out.mjs
|
||||||
rm -f build/out.wasm
|
rm build/out.wasm
|
||||||
rm -f build/ludus.jimage
|
rm 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"
|
||||||
|
|
22
prelude.ld
22
prelude.ld
|
@ -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,10 +1215,17 @@ fn hideturtle! {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loadstate! {
|
fn loadstate! {
|
||||||
"Sets the turtle state to a previously saved state."
|
"Sets the turtle state to a previously saved state. Returns the state."
|
||||||
(state) -> {
|
(state) -> {
|
||||||
let #{position, heading, pendown?, pencolor, penwidth, visible?} = state
|
let #{:position (x, y), heading, pendown?, pencolor, penwidth, visible?} = state
|
||||||
add_command! ((:loadstate, position, heading, visible?, pendown?, penwidth, pencolor))
|
let command = if tuple? (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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1251,7 +1258,8 @@ 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, position, heading, visible?, pendown?, penwidth, pencolor) -> #{position, heading, visible?, pendown?, penwidth, pencolor}
|
(:loadstate, x, y, heading, visible?, pendown?, penwidth, pencolor) -> #{:position (x, y), 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
|
||||||
|
|
|
@ -12,72 +12,44 @@
|
||||||
(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)))
|
||||||
# and, finally, try interpreting the program
|
(try
|
||||||
(try (do
|
(set result (i/interpret (parsed :ast) ctx))
|
||||||
# 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))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user