Fixes in lambda/function definitions (body args). Cleanup of examples and decumentation. Added convenience methods for JS interop, lists and objects.

This commit is contained in:
Kuba Antosik 2021-04-04 18:35:26 +10:00
parent 8d23e5264e
commit 0cebbe52cb
24 changed files with 655 additions and 120 deletions

View File

@ -59,11 +59,16 @@ To save an image in memory, open an image file with Ronin, or drag an image file
Library
Files
- (open name ~scale) Imports a graphic file with format.
- (import name ~shape) Imports a graphic file with format.
- (export ~format ~quality) Exports a graphic file with format.
- (files) Returns the list of loaded files.
- (print string) Exports string to file.
Shapes
- (pos ~x ~y) Returns a position shape.
- (line ax ay bx by) Returns a line shape.
- (size w h) Returns a size shape.
@ -74,6 +79,9 @@ Library
- (poly ...pos) Returns a poly shape.
- (text x y p t ~a ~f) Returns a text shape.
- (svg x y d) Returns a svg shape.
Canvas operations
- (color r g b ~a) Returns a color object.
- (hsl h s l ~a) returns a HSL color object
- (resize ~w) Resizes the canvas to target w and h, returns the rect.
@ -92,6 +100,9 @@ Library
- (clear ~rect) Clears a rect.
- (gradient line ~colors 'black']) Defines a gradient color.
- (guide shape color) Draws a shape on the guide layer.
Pixel operations
- (pixels fn ~q ~rect)
- (saturation pixel q) Change the saturation of pixels.
- (contrast pixel q) Change the contrast of pixels.
@ -100,8 +111,14 @@ Library
- (multiply pixel q) Change the color balance of pixels.
- (normalize pixel q) Normalize the color of pixels with another color.
- (lum color) Return the luminance of a color.
Strings
- (concat ...items) Concat multiple strings.
- (split string char) Split string at character.
Math
- (add ...args) Adds values.
- (sub ...args) Subtracts values.
- (mul ...args) Multiplies values.
@ -125,38 +142,71 @@ Library
- (PI)
- (TWO_PI)
- (random ...args)
Logic
- (gt a b) Returns true if a is greater than b, else false.
- (lt a b) Returns true if a is less than b, else false.
- (eq a b) Returns true if a is equal to b, else false.
- (and ...args) Returns true if all conditions are true.
- (or a b ...rest) Returns true if at least one condition is true.
- (not a) Negation. Returns true if a is false. Returns false if a is true.
- (while fn action) While loop. Execute action for as long as fn is true.
Language
- (let name value)
- (def name value)
- (defn fname ~(...fnparams) ...instructions)
- (λ (...args) ...instructions)
- (if (condition) ...instruction-when-true ~...instruction-when-false)
Lists
- (each arr fn) Run a function for each element in a list.
- (map arr fn) Run a function on each element in a list.
- (filter arr fn) Remove from list, when function returns false.
- (reduce arr fn acc)
- (len item) Returns the length of a list.
- (cons arr ...items) Returns a new array with the items appended.
- (push arr ...items) Appends the items into the existing list.
- (pop arr) Pop the last item from the list and return the item.
- (first arr) Returns the first item of a list.
- (last arr) Returns the last
- (rest [_ ...arr])
- (range start end ~step)
- (rest [_ ...arr]) Returns all arguments except the first
- (range start end ~step) Returns a list of numbers counting from start to end. Step defaults to 1.
Objects (maps)
- (get item key) Gets an object's parameter with name.
- (set item ...args) Sets an object's parameter with name as value.
- (of h ...keys) Gets object parameters with names.
- (object ...entries) Creates an object with provided entries.
- (keys item) Returns a list of the object's keys
- (values item) Returns a list of the object's values
- (convolve kernel ~rect)
Convolution filters
- (convolve kernel ~rect) Apply convolution filter with given kernel on an area.
- (blur) Returns the blur kernel.
- (sharpen) Returns the sharpen kernel.
- (edge) Returns the edge kernel.
Points
- (offset a b) Offsets pos a with pos b, returns a.
- (distance a b) Get distance between positions.
Utilities
- (echo ...args) Print arguments to interface.
- (debug arg) Print arguments to console.
- (time ~rate) Returns timestamp in milliseconds.
- (js) Javascript interop.
- (js) Javascript interop. Returns window object.
- (on event f) Triggers on event.
- (test name a b)
- (test name a b) Unit test. Checks if a is equal to b, logs results to console.
- (benchmark fn) Logs time taken to execute a function.
- (get-theme) Returns an object with current theme colors.
- (get-frame) Returns a shape object describing the current canvas.
Extras

View File

@ -35,9 +35,9 @@
;
(defn redraw ()
(
(clear)
(rec 300)))
(rec 300))
;
(on "animate" redraw)

View File

@ -3,6 +3,9 @@
;
(clear)
(def frame
(get-frame))
;
(def gradient-line
(line frame:c 0 frame:c frame:h))

View File

@ -2,6 +2,9 @@
(clear)
(def frame
(get-frame))
; times
@ -26,13 +29,13 @@
; position on a circle from angle
(defn circle-pos
(cx cy r a) {:x
(cx cy r a) (object "x"
(add cx
(mul r
(cos a))) :y
(cos a))) "y"
(add cy
(mul r
(sin a)))})
(sin a)))))
; draw
@ -42,10 +45,10 @@
(
(stroke
(line cx cy
(:x
(circle-pos cx cy r a))
(:y
(circle-pos cx cy r a))) "white" 2)))
(get
(circle-pos cx cy r a) "x")
(get
(circle-pos cx cy r a) "y")) "white" 2)))
;
(defn draw-star

View File

@ -2,7 +2,8 @@
(def theme
(get-theme))
(def frame
(get-frame))
; ex: theme:f_high

View File

@ -1,5 +1,11 @@
(clear)
(def theme
(get-theme))
(def frame
(get-frame))
(defn branch
(v)
(if

View File

@ -2,6 +2,9 @@
(clear)
(def theme
(get-theme))
(transform:move 150 150)
(fill

View File

@ -0,0 +1,168 @@
;A simple MIDI visualiser
;showing activity per note
;and channel.
(clear)
(def theme
(get-theme))
(resize 1200 600)
(def maxcirclesize 50)
(def mincirclesize 20)
(def circlexdist 40)
(def circleydist 30)
(def notes
(object 0 "A" 1 "A#" 2 "B" 3 "C" 4 "C#" 5 "D" 6 "D#" 7 "E" 8 "F" 9 "F#" 10 "G" 11 "G#"))
(def sharpnotes
(object 1 "A#" 4 "C#" 6 "D#" 9 "F#" 11 "G#"))
(def frame
(get-frame))
(def jswindow
(js))
(defn isblackkey
(num)
(def distfromA0
(sub num 21))
(def semitonenum
(mod distfromA0 12))
(not
(eq
(get sharpnotes semitonenum) undefined)))
(defn circlebynotenum
(num)
(circle
(mul
(sub num 47) circlexdist)
(if
(isblackkey num) circleydist
(mul 2.5 circleydist)) mincirclesize))
(def reactivecircles
(reduce
(range 48 76)
(λ
(acc num index)
(set acc num
(circlebynotenum num)))
(object)))
(def channelcircles
(map
(range 0 15)
(λ
(num)
(circle
(add
(mul num circlexdist 1.25) circlexdist) 200 mincirclesize))))
(defn js-exec
(obj fname listargs)
(def boundfunction
(js-bind
(get obj fname) obj))
(def result
(apply boundfunction
(if
(eq listargs undefined) () listargs))) result)
(defn midimsghandler
(midiMessage)
(def eventType
(get
(:data midiMessage) "0"))
;zero based
(def channelNum
(logand eventType 15))
;ignore clock in debug to keep things cleaner
(if
(not
(eq eventType 248))
(debug "incoming MIDI:" "CH" channelNum
(:data midiMessage)))
(def noteNum
(get
(:data midiMessage) "1"))
(def noteVelocity
(get
(:data midiMessage) "2"))
(set
(get channelcircles channelNum) "r"
(add mincirclesize
(mul
(sub maxcirclesize mincirclesize)
(div noteVelocity 100))))
(set
(or
(get reactivecircles noteNum)()) "r"
(add mincirclesize
(mul
(sub maxcirclesize mincirclesize)
(div noteVelocity 100)))))
(defn midiokhandler
(midiAccess)
(def midiInputs
(js-exec
(:inputs midiAccess) "values"))
(eachof midiInputs
(λ
(input id)
(debug "Setting listener on"
(:name input)
(:manufacturer input))
(set input "onmidimessage" midimsghandler))))
(defn midierrhandler
(err)
(debug "midierrhandler" err))
(js-exec
(js-exec
(:navigator jswindow) "requestMIDIAccess") "then"
(list midiokhandler midierrhandler))
(defn drawcircle
(arglist)
(def notenum
(first arglist))
(def i
(last arglist))
(if
(gt
(:r i) mincirclesize)
(set i "r"
(sub
(:r i) 0.6)))
(fill i
(if
(isblackkey notenum)
(theme:f_med)
(theme:f_low))))
(defn redraw ()
(clear)
(each
(entries reactivecircles) drawcircle)
(each channelcircles
(λ
(s i)
(fill s theme:b_high)
(fill
(text
(sub s:cx mincirclesize)
(add s:cy mincirclesize 18) 18
(concat "CH" i)) theme:b_inv 2))))
;
(on "animate" redraw)

View File

@ -0,0 +1,3 @@
# Not implemented (yet)
This set of examples shows the limitations of Ronin's LISP.

View File

@ -0,0 +1,44 @@
;To inspect the results of these tests,
;open your browser's debugging tools
;
(usually F12)
and navigate to the
;JavaScript console.
(clear)
(def mydog
(object "color" "gold" "coat" "long" :speed "quick" :health "good"))
(debug mydog)
(test "My dog's color is"
(get mydog "color") "gold")
(test "My dog's coat is"
(get mydog "coat") "long")
(test "My dog's speed is"
(get mydog "speed") "quick")
;the last one passes only because :health
;in the set instruction above
;resolves to undefined - and
;technically you can have one object value
;for key=undefined
(test "My dog's health is"
(get mydog :health) "good")
;You can, however, use obj:param syntax to
;get properties of objects.
;A shorthand for
(get obj "param")
.
;
(test "Get color" mydog:color "gold")
;Also,
(:coat mydog)
;is another shorthand.
(test "Get coat shorthand function"
(:coat mydog) "long")

View File

@ -2,17 +2,31 @@
(clear)
(defn glitch
(rec)
(if (gt rec 1)
(
(translate
(rect (random 400) (random 400) (random 10) (random 10))
(pos (random 400) (random 400)))
(glitch (sub rec 1)))))
(import
"../static/crystal.jpg"
(import $path
(rect 0 0 400 400))
(defn translate
(source-rect dest-pos)
(
(paste
(copy source-rect)
(rect dest-pos:x dest-pos:y source-rect:w source-rect:h))))
(defn glitch
(rec)
(if
(gt rec 1)
(
(translate
(rect
(random 1 400)
(random 1 400)
(random 1 10)
(random 1 10))
(pos
(random 1 400)
(random 1 400)))
(glitch
(sub rec 1)))))
(glitch 500)

View File

@ -1,6 +1,12 @@
; Normalize photo colors
(open $path 0.5)
;(pick (rect 0 0 100 100)) will return the average color
;of 100x100px rectangle.
;without extra arguments,
;pick grabs the whole canvas by default,
;and calculates the average color.
(def average-color
(pick))
(pixels normalize average-color)

View File

@ -1,4 +1,4 @@
; saturate image
(open "../static/crystal.jpg")
(pixels
(frame) saturation 12)
(open $path)
(pixels saturation 12
(get-frame))

View File

@ -2,7 +2,7 @@
; drag an image on the window
(open $path)
;
(pixels
(rect 100 100 400 400) saturation 0)
(pixels
(rect 300 300 400 400) contrast 0.5)
(pixels saturation 0
(rect 100 100 400 400))
(pixels contrast 0.5
(rect 300 300 400 400))

View File

@ -6,7 +6,7 @@
(if
(gt rec 0)
(
(import "../static/crystal.jpg"
(import $path
(rect
(random 200)
(random 200)

View File

@ -18,63 +18,10 @@
(pick
(guide
(rect $xy unit unit))))
(def color-2
(pick
(guide
(rect $xy unit unit))))
(def color-3
(pick
(guide
(rect $xy unit unit))))
(def color-4
(pick
(guide
(rect $xy unit unit))))
(def color-5
(pick
(guide
(rect $xy unit unit))))
(def color-6
(pick
(guide
(rect $xy unit unit))))
(def color-7
(pick
(guide
(rect $xy unit unit))))
(def color-8
(pick
(guide
(rect $xy unit unit))))
(echo color-1)
; display
(fill
(circle
(mul 20 2) pos-row-1 18) color-1)
(fill
(circle
(mul 20 4) pos-row-1 18) color-2)
(fill
(circle
(mul 20 6) pos-row-1 18) color-3)
(fill
(circle
(mul 20 8) pos-row-1 18) color-4)
(fill
(circle
(mul 20 3) pos-row-2 18) color-5)
(fill
(circle
(mul 20 5) pos-row-2 18) color-6)
(fill
(circle
(mul 20 7) pos-row-2 18) color-7)
(fill
(circle
(mul 20 9) pos-row-2 18) color-8)
;
(def res
(add color-1:hex ":" color-2:hex ":" color-3:hex ":" color-4:hex ":" color-5:hex ":" color-6:hex ":" color-7:hex ":" color-8:hex))
(echo
(add res ":" res))

View File

@ -1,4 +1,4 @@
(resize 600 200)
(resize 600 800)
(clear)
@ -14,6 +14,9 @@
(guide
(line 0 100 600 100)) colors))
;collect colors to prepared list,
;in particular points from the gradient
;marked by the guides
(each picked-colors
(λ
(color id)
@ -25,5 +28,33 @@
(mul id
(div 600 9)) 100)))))))
;show picked colors as swatches
(each picked-colors
(λ
(color id)
(
;swatch circle
(fill
(circle
20 (add (mul id
(div 600 9)) 300) 18) color)
"black")))
;show picked colors as text
(each picked-colors
(λ
(color id)
(
(fill
(text
12 (add (mul id
(div 600 9)) 300 5) 24
(concat id ": "
(get
(get picked-colors
(concat "" id)) "hex"))) "black"))))
;get the first color in different formats
(echo
(get picked-colors:1 "hex"))
(get picked-colors:0 "hex")
(get picked-colors:0 "rgba"))

View File

@ -7,5 +7,8 @@
(add
(mul index 40) 50) 40 name) "red"))
;this will display the list of loaded files.
;drag a few pictures into Ronin, then eval
;this script again.
(each
(files) print-file)

View File

@ -3,6 +3,8 @@
;
(clear)
(def frame (get-frame))
;
(def gradient-line
(line frame:c 0 frame:c frame:h))

134
examples/tests/lists.lisp Normal file
View File

@ -0,0 +1,134 @@
(test "Native last call - list of numbers"
(last
(1 2 3)) 3)
(test "Native last call - list of numbers, 1 element"
(last
(1)) 1)
(test "Native last call - list of numbers, no elements"
(last ()) undefined)
(test "Native last call - list of strings, 1 element"
(last
("abc")) "abc")
(test "Native last call - list of strings, many elements"
(last
("ala" "bla" "cla")) "cla")
;functions defined as defn/lambda args bodyinstrs...
(defn lastfn
(listarg) (debug "some other instruction")
(last listarg))
(test "Proxied last call - list of numbers"
(lastfn
(1 2 3)) 3)
(test "Proxied last call - list of numbers, 1 element"
(lastfn
(1)) 1)
(test "Proxied last call - list of numbers, no elements"
(lastfn ()) undefined)
(test "Proxied last call - list of strings, 1 element"
(lastfn
("abc")) "abc")
(test "Proxied last call - list of strings, many elements"
(lastfn
("ala" "bla" "cla")) "cla")
(test "Lambda last call - list of numbers"
((λ
(listarg) (debug "some other instruction")
(last listarg))
(1 2 3)) 3)
(test "Lambda last call - list of numbers, 1 element"
((λ
(listarg) (debug "some other instruction")
(last listarg))
(1)) 1)
(test "Lambda last call - list of numbers, no elements"
((λ
(listarg) (debug "some other instruction")
(last listarg)) ()) undefined)
(test "Lambda last call - list of strings, 1 element"
((λ
(listarg) (debug "some other instruction")
(last listarg))
("abc")) "abc")
(test "Lambda last call - list of strings, many elements"
((λ
(listarg) (debug "some other instruction")
(last listarg))
("ala" "bla" "cla")) "cla")
;functions defined as defn/lambda args bodyinstr
(defn lastfn2
(listarg)
(last listarg))
(test "Proxied last call - list of numbers"
(lastfn2
(1 2 3)) 3)
(test "Proxied last call - list of numbers, 1 element"
(lastfn2
(1)) 1)
(test "Proxied last call - list of numbers, no elements"
(lastfn2 ()) undefined)
(test "Proxied last call - list of strings, 1 element"
(lastfn2
("abc")) "abc")
(test "Proxied last call - list of strings, many elements"
(lastfn2
("ala" "bla" "cla")) "cla")
(test "Lambda last call - list of numbers"
((λ
(listarg)
(last listarg))
(1 2 3)) 3)
(test "Lambda last call - list of numbers, 1 element"
((λ
(listarg)
(last listarg))
(1)) 1)
(test "Lambda last call - list of numbers, no elements"
((λ
(listarg)
(last listarg)) ()) undefined)
(test "Lambda last call - list of strings, 1 element"
((λ
(listarg)
(last listarg))
("abc")) "abc")
(test "Lambda last call - list of strings, many elements"
((λ
(listarg)
(last listarg))
("ala" "bla" "cla")) "cla")

View File

@ -148,14 +148,16 @@ function Lain (lib = {}) {
const identifier = input[1].value
if (context.scope[identifier]) { console.warn('Lain', `Redefining function: ${identifier}`) }
const fnParams = input[2].type === TYPES.string && input[3] ? input[3] : input[2]
const fnBody = input[2].type === TYPES.string && input[4] ? input[4] : input[3]
const fnBodyFirstIndex = input[2].type === TYPES.string && input[4] ? 4 : 3
const fnBody = input.slice(fnBodyFirstIndex)
context.scope[identifier] = function () {
const lambdaArguments = arguments
const lambdaScope = fnParams.reduce(function (acc, x, i) {
acc[x.value] = lambdaArguments[i]
return acc
}, {})
return interpret(fnBody, new Context(lambdaScope, context))
let result = interpret(fnBody, new Context(lambdaScope, context))
return getReturnValue(result)
}
},
λ: function (input, context) {
@ -165,13 +167,20 @@ function Lain (lib = {}) {
acc[x.value] = lambdaArguments[i]
return acc
}, {})
return interpret(input[2], new Context(lambdaScope, context))
let result = interpret(input.slice(2), new Context(lambdaScope, context))
return getReturnValue(result)
}
},
if: function (input, context) {
return interpret(input[1], context) ? interpret(input[2], context) : input[3] ? interpret(input[3], context) : []
}
}
const getReturnValue = function (interpretResult) {
if(!interpretResult || !(interpretResult instanceof Array) || !interpretResult.length){
return interpretResult
}
return interpretResult[interpretResult.length - 1]
}
const interpretList = function (input, context) {
if (input.length > 0 && input[0].value in special) {
return special[input[0].value](input, context)
@ -1185,6 +1194,21 @@ function Library (client) {
}
return Math.random()
}
this.logand = (a, b) => {
return a & b
}
this.logior = (a, b) => {
return a | b
}
this.logxor = (a, b) => {
return a ^ b
}
this.lognot = (a) => {
return ~ a
}
this.ash = (a, b) => {
return a << b
}
this.gt = (a, b) => { // Returns true if a is greater than b, else false.
return a > b
}
@ -1211,20 +1235,29 @@ function Library (client) {
}
return args[args.length - 1]
}
this.not = (a) => {
this.not = (a) => { //Negation. Returns true if a is false. Returns false if a is true.
return !a
}
this.while = (fn, action) => {
this.while = (fn, action) => { //While loop. Execute action for as long as fn is true.
while (fn()) {
action()
}
}
this.apply = (fn, argslist) => {
let result = fn(...argslist);
return result;
}
this.each = (arr, fn) => { // Run a function for each element in a list.
for (let i = 0; i < arr.length; i++) {
const arg = arr[i]
fn(arg, i)
}
}
this.eachof = (arr, fn) => {
for(let elem of arr){
fn(elem);
}
}
this.map = (arr, fn) => { // Returns a new list with fn applied to each value.
return arr.map(fn);
}
@ -1242,7 +1275,10 @@ function Library (client) {
this.len = (item) => { // Returns the length of a list.
return item.length
}
this.cons = (arr, ...items) => { // Retruns a new array with the items appended.
this.list = (...items) => { // Returns a new array with the items
return items
}
this.cons = (arr, ...items) => { // Returns a new array with the items appended to arr.
return arr.concat(items)
}
this.push = (arr, ...items) => { // Appends the items into the existing list.
@ -1260,10 +1296,10 @@ function Library (client) {
this.last = (arr) => { // Returns the last
return arr[arr.length - 1]
}
this.rest = ([_, ...arr]) => {
this.rest = ([_, ...arr]) => { // Returns all arguments except the first
return arr
}
this.range = (start, end, step = 1) => {
this.range = (start, end, step = 1) => { //Returns a list of numbers counting from start to end. Step defaults to 1.
const arr = []
if (step > 0) {
for (let i = start; i <= end; i += step) {
@ -1277,7 +1313,7 @@ function Library (client) {
return arr
}
this.get = (item, key) => { // Gets an object's parameter with name.
return item && key ? item[key] : null
return item && (key !== null && key !== undefined) ? item[key] : null
}
this.set = (item, ...args) => { // Sets an object's parameter with name as value.
for (let i = 0; i < args.length; i += 2) {
@ -1305,6 +1341,9 @@ function Library (client) {
this.values = (item) => { // Returns a list of the object's values
return Object.values(item)
}
this.entries = (item) => { // Returns a list of the object's properties, each in array of [key, value]
return Object.entries(item);
}
this.convolve = (kernel, rect = this['get-frame']()) => {
const sigma = kernel.flat().reduce((a, x) => (a + x))
const kw = kernel[0].length; const kh = kernel.length
@ -1357,9 +1396,9 @@ function Library (client) {
client.log(args)
return args
}
this.debug = (arg) => { // Print arguments to console.
console.log(arg)
return arg
this.debug = (...args) => { // Print arguments to console.
console.log(...args)
return args
}
this.time = (rate = 1) => { // Returns timestamp in milliseconds.
return (Date.now() * rate)
@ -1367,10 +1406,13 @@ function Library (client) {
this.js = () => { // Javascript interop.
return window
}
this['js-bind'] = (fn, thisArg, ...args) => {
return fn.bind(thisArg, ...args)
}
this.on = (event, f) => { // Triggers on event.
client.bind(event, f)
}
this.test = (name, a, b) => {
this.test = (name, a, b) => { //nit test. Checks if a is equal to b, logs results to console.
if (`${a}` !== `${b}`) {
console.warn('failed ' + name, a, b)
} else {
@ -1387,7 +1429,7 @@ function Library (client) {
this['get-theme'] = () => { // Get theme values.
return client.theme.active
}
this['get-frame'] = () => { // Get theme values.
this['get-frame'] = () => { // Get frame shape.
return client.surface.getFrame()
}
}

View File

@ -40,15 +40,21 @@ function Lain (lib = {}) {
defn: function (input, context) {
const identifier = input[1].value
if (context.scope[identifier]) { console.warn('Lain', `Redefining function: ${identifier}`) }
const fnParams = input[2].type === TYPES.string && input[3] ? input[3] : input[2]
const fnBody = input[2].type === TYPES.string && input[4] ? input[4] : input[3]
const fnBodyFirstIndex = input[2].type === TYPES.string && input[4] ? 4 : 3
const fnBody = input.slice(fnBodyFirstIndex)
context.scope[identifier] = function () {
const lambdaArguments = arguments
const lambdaScope = fnParams.reduce(function (acc, x, i) {
acc[x.value] = lambdaArguments[i]
return acc
}, {})
return interpret(fnBody, new Context(lambdaScope, context))
let result = interpret(fnBody, new Context(lambdaScope, context))
//lisp returns the return value of the last executed function, not a list of all results of all functions.
return getReturnValue(result)
}
},
λ: function (input, context) {
@ -58,7 +64,10 @@ function Lain (lib = {}) {
acc[x.value] = lambdaArguments[i]
return acc
}, {})
return interpret(input[2], new Context(lambdaScope, context))
let result = interpret(input.slice(2), new Context(lambdaScope, context))
//lisp returns the return value of the last executed function, not a list of all results of all functions.
return getReturnValue(result)
}
},
if: function (input, context) {
@ -66,6 +75,15 @@ function Lain (lib = {}) {
}
}
const getReturnValue = function (interpretResult) {
//lisp returns the return value of the last executed function,
//not a list of all results of all functions.
if(!interpretResult || !(interpretResult instanceof Array) || !interpretResult.length){
return interpretResult
}
return interpretResult[interpretResult.length - 1]
}
const interpretList = function (input, context) {
if (input.length > 0 && input[0].value in special) {
return special[input[0].value](input, context)

View File

@ -382,6 +382,28 @@ function Library (client) {
return Math.random()
}
// Binary
this.logand = (a, b) => {
return a & b
}
this.logior = (a, b) => {
return a | b
}
this.logxor = (a, b) => {
return a ^ b
}
this.lognot = (a) => {
return ~ a
}
this.ash = (a, b) => {
return a << b
}
// Logic
this.gt = (a, b) => { // Returns true if a is greater than b, else false.
@ -415,18 +437,22 @@ function Library (client) {
return args[args.length - 1]
}
this.not = (a) => {
this.not = (a) => { //Negation. Returns true if a is false. Returns false if a is true.
return !a
}
// Arrays
this.while = (fn, action) => {
this.while = (fn, action) => { //While loop. Execute action for as long as fn is true.
while (fn()) {
action()
}
}
// Arrays
this.apply = (fn, argslist) => {
let result = fn(...argslist);
return result;
}
this.each = (arr, fn) => { // Run a function for each element in a list.
for (let i = 0; i < arr.length; i++) {
const arg = arr[i]
@ -434,6 +460,12 @@ function Library (client) {
}
}
this.eachof = (arr, fn) => {
for(let elem of arr){
fn(elem);
}
}
this.map = (arr, fn) => { // Returns a new list with fn applied to each value.
return arr.map(fn);
}
@ -455,7 +487,11 @@ function Library (client) {
return item.length
}
this.cons = (arr, ...items) => { // Retruns a new array with the items appended.
this.list = (...items) => { // Returns a new array with the items
return items
}
this.cons = (arr, ...items) => { // Returns a new array with the items appended to arr.
return arr.concat(items)
}
@ -478,11 +514,11 @@ function Library (client) {
return arr[arr.length - 1]
}
this.rest = ([_, ...arr]) => {
this.rest = ([_, ...arr]) => { // Returns all arguments except the first
return arr
}
this.range = (start, end, step = 1) => {
this.range = (start, end, step = 1) => { //Returns a list of numbers counting from start to end. Step defaults to 1.
const arr = []
if (step > 0) {
for (let i = start; i <= end; i += step) {
@ -499,7 +535,7 @@ function Library (client) {
// Objects
this.get = (item, key) => { // Gets an object's parameter with name.
return item && key ? item[key] : null
return item && (key !== null && key !== undefined) ? item[key] : null
}
this.set = (item, ...args) => { // Sets an object's parameter with name as value.
@ -533,6 +569,10 @@ function Library (client) {
return Object.values(item)
}
this.entries = (item) => { // Returns a list of the object's properties, each in array of [key, value]
return Object.entries(item);
}
// Convolve
this.convolve = (kernel, rect = this['get-frame']()) => {
@ -575,6 +615,8 @@ function Library (client) {
[-1, -1, -1]]
}
// Points
this.offset = (a, b) => { // Offsets pos a with pos b, returns a.
a.x += b.x
a.y += b.y
@ -585,6 +627,8 @@ function Library (client) {
return Math.sqrt(((a.x - b.x) * (a.x - b.x)) + ((a.y - b.y) * (a.y - b.y)))
}
// Utilities
this.print = (value) => {
client.source.write('ronin-print', 'txt', value, 'text/plain')
return value
@ -595,9 +639,9 @@ function Library (client) {
return args
}
this.debug = (arg) => { // Print arguments to console.
console.log(arg)
return arg
this.debug = (...args) => { // Print arguments to console.
console.log(...args)
return args
}
this.time = (rate = 1) => { // Returns timestamp in milliseconds.
@ -608,11 +652,24 @@ function Library (client) {
return window
}
// Returns a new function that
// Useful when executing JS functions that need a strict `this` context.
//
// An example:
// `(get (get (js) "navigator") "requestMIDIAccess")` in Ronin
// should be equivalent to `window.navigator.requestMIDIAccess` in JS.
// Executing such retrieved JS method will crash though - as the method
// needs an internal `this` context - in exemplary case, window.navigator.
//
this['js-bind'] = (fn, thisArg, ...args) => {
return fn.bind(thisArg, ...args)
}
this.on = (event, f) => { // Triggers on event.
client.bind(event, f)
}
this.test = (name, a, b) => {
this.test = (name, a, b) => { //nit test. Checks if a is equal to b, logs results to console.
if (`${a}` !== `${b}`) {
console.warn('failed ' + name, a, b)
} else {
@ -634,7 +691,7 @@ function Library (client) {
return client.theme.active
}
this['get-frame'] = () => { // Get theme values.
this['get-frame'] = () => { // Get frame shape.
return client.surface.getFrame()
}
}