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

@@ -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()
}
}