From 0e7af66c16fb6a72f6df2863fc964f3943691841 Mon Sep 17 00:00:00 2001 From: Devine Lu Linvega Date: Sun, 21 Jul 2019 07:37:11 +0900 Subject: [PATCH 1/5] Fixed issue with prelude path in builds --- desktop/sources/index.html | 15 ++++----- desktop/sources/scripts/commander.js | 3 +- desktop/sources/scripts/lisp.js | 46 ++++++++++++++++------------ desktop/sources/scripts/ronin.js | 1 + desktop/sources/scripts/source.js | 6 ---- 5 files changed, 37 insertions(+), 34 deletions(-) diff --git a/desktop/sources/index.html b/desktop/sources/index.html index 955ce4b..ce810ca 100644 --- a/desktop/sources/index.html +++ b/desktop/sources/index.html @@ -18,10 +18,10 @@ diff --git a/desktop/sources/scripts/commander.js b/desktop/sources/scripts/commander.js index f42d1d1..6cdfd21 100644 --- a/desktop/sources/scripts/commander.js +++ b/desktop/sources/scripts/commander.js @@ -33,8 +33,7 @@ function Commander (ronin) { this.run = (txt = this._input.value) => { if (txt.indexOf('$') > -1) { ronin.log('Present: $'); return } - const inter = new Lisp(txt, ronin.library) - inter.toPixels() + const inter = new Lisp(txt, ronin.library, ronin.includes).run() ronin.always === true && requestAnimationFrame(() => this.run(txt)) } diff --git a/desktop/sources/scripts/lisp.js b/desktop/sources/scripts/lisp.js index d2a9541..d383fb8 100644 --- a/desktop/sources/scripts/lisp.js +++ b/desktop/sources/scripts/lisp.js @@ -1,6 +1,6 @@ 'use strict' -function Lisp (input, lib) { +function Lisp (input, lib, includes) { const path = require('path') const fs = require('fs') @@ -20,7 +20,7 @@ function Lisp (input, lib) { const special = { include: (input, context) => { - if (!input[1].value || !fs.existsSync(input[1].value)) { console.warn('Source', input[1].value); return [] } + if (!input[1].value || !fs.existsSync(input[1].value)) { console.warn('Lisp', 'No file: ' + input[1].value); return [] } const file = fs.readFileSync(input[1].value, { encoding: 'utf-8' }) return interpret(this.parse(`(${file})`), context) }, @@ -69,7 +69,7 @@ function Lisp (input, lib) { __fn: function (input, context) { return async function () { const lambdaArguments = arguments - const keys = [...new Set(input.slice(2).flat(100).filter(i => + const keys = [...new Set(input.slice(2).flat(100).filter(i => i.type === TYPES.identifier && i.value[0] === '%' ).map(x => x.value).sort())] @@ -82,8 +82,8 @@ function Lisp (input, lib) { }, __obj: async function (input, context) { const obj = {} - for (let i = 1 ; i { + const p = path.join(__dirname, `lisp/${item}.lisp`) + if (!fs.existsSync(p)) { console.warn('Lisp', `Missing include: ${p}`); return acc } + return `${acc}(include "${p}") ` + }, '') } this.parse = function (input) { return parenthesize(tokenize(input)) } - this.toPixels = async function () { + this.run = async function () { return interpret(this.parse(`( - (include "./sources/lisp/prelude.lisp") - ${input})`)) + ${this.inc()} + ${input})`)) } } diff --git a/desktop/sources/scripts/ronin.js b/desktop/sources/scripts/ronin.js index 1e229c7..a782a8b 100644 --- a/desktop/sources/scripts/ronin.js +++ b/desktop/sources/scripts/ronin.js @@ -23,6 +23,7 @@ function Ronin () { // Parameters this.always = false + this.includes = ['prelude'] this.install = function (host = document.body) { this._wrapper = document.createElement('div') diff --git a/desktop/sources/scripts/source.js b/desktop/sources/scripts/source.js index d0abb44..8ceec2e 100644 --- a/desktop/sources/scripts/source.js +++ b/desktop/sources/scripts/source.js @@ -126,12 +126,6 @@ function Source (ronin) { return `${str}` } - this.locate = function (name) { - if (!this.path) { return } - const loc = path.join(this.folder(), name) - return fs.existsSync(loc) ? loc : null - } - // Etc this.name = function () { From aaa77de890b8b15846d48c97f0126479a17122fc Mon Sep 17 00:00:00 2001 From: Devine Lu Linvega Date: Sun, 21 Jul 2019 08:25:23 +0900 Subject: [PATCH 2/5] Moved the interpreter into ronin --- desktop/sources/scripts/commander.js | 2 +- desktop/sources/scripts/lisp.js | 7 ++++--- desktop/sources/scripts/ronin.js | 4 +++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/desktop/sources/scripts/commander.js b/desktop/sources/scripts/commander.js index 6cdfd21..fa74696 100644 --- a/desktop/sources/scripts/commander.js +++ b/desktop/sources/scripts/commander.js @@ -33,7 +33,7 @@ function Commander (ronin) { this.run = (txt = this._input.value) => { if (txt.indexOf('$') > -1) { ronin.log('Present: $'); return } - const inter = new Lisp(txt, ronin.library, ronin.includes).run() + ronin.interpreter.run(txt) ronin.always === true && requestAnimationFrame(() => this.run(txt)) } diff --git a/desktop/sources/scripts/lisp.js b/desktop/sources/scripts/lisp.js index d383fb8..82fe8de 100644 --- a/desktop/sources/scripts/lisp.js +++ b/desktop/sources/scripts/lisp.js @@ -1,14 +1,15 @@ 'use strict' -function Lisp (input, lib, includes) { +function Lisp (lib = {}, includes = []) { + console.log(includes) const path = require('path') const fs = require('fs') const TYPES = { identifier: 0, number: 1, string: 2, bool: 3 } + const Context = function (scope, parent) { this.scope = scope this.parent = parent - this.get = function (identifier) { if (identifier in this.scope) { return this.scope[identifier] @@ -178,7 +179,7 @@ function Lisp (input, lib, includes) { return parenthesize(tokenize(input)) } - this.run = async function () { + this.run = async function (input) { return interpret(this.parse(`( ${this.inc()} ${input})`)) diff --git a/desktop/sources/scripts/ronin.js b/desktop/sources/scripts/ronin.js index a782a8b..fade53f 100644 --- a/desktop/sources/scripts/ronin.js +++ b/desktop/sources/scripts/ronin.js @@ -10,6 +10,8 @@ function Ronin () { b_low: '#aaa', b_inv: '#ffb545' } + + this.includes = ['prelude'] this.el = document.createElement('div') this.el.id = 'ronin' @@ -19,11 +21,11 @@ function Ronin () { this.commander = new Commander(this) this.surface = new Surface(this) this.library = new Library(this) + this.interpreter = new Lisp(this.library, this.includes) // Parameters this.always = false - this.includes = ['prelude'] this.install = function (host = document.body) { this._wrapper = document.createElement('div') From 3ea7fff85f5c0932153f67704010e04e20cde06e Mon Sep 17 00:00:00 2001 From: Devine Lu Linvega Date: Sun, 21 Jul 2019 09:20:04 +0900 Subject: [PATCH 3/5] Added (times i fn) to prelude --- desktop/sources/lisp/prelude.lisp | 27 +++-- desktop/sources/scripts/library.js | 154 ++++++++++++++--------------- desktop/sources/scripts/ronin.js | 2 +- examples/basics.lisp | 9 +- 4 files changed, 104 insertions(+), 88 deletions(-) diff --git a/desktop/sources/lisp/prelude.lisp b/desktop/sources/lisp/prelude.lisp index 82c0999..ef8b670 100644 --- a/desktop/sources/lisp/prelude.lisp +++ b/desktop/sources/lisp/prelude.lisp @@ -1,11 +1,20 @@ +; (echo "Loading prelude.lisp") - +; translate (defn translate - (r p) - (clone - r - (rect - (of p :x) - (of p :y) - (of r :w) - (of r :h)))) + (r p) + (clone r + (rect + (of p :x) + (of p :y) + (of r :w) + (of r :h)))) +; times +(defn times + (v f) + ( + (f v) + (if + (gt v 1) + (times + (sub v 1) f)))) \ No newline at end of file diff --git a/desktop/sources/scripts/library.js b/desktop/sources/scripts/library.js index 0a8aa78..35b48b2 100644 --- a/desktop/sources/scripts/library.js +++ b/desktop/sources/scripts/library.js @@ -64,6 +64,83 @@ function Library (ronin) { return rect } + // Frame + + this.frame = () => { // Returns a rect of the frame. + return ronin.surface.getFrame() + } + + this.center = () => { // Returns a position of the center of the frame. + const rect = this.frame() + return this.pos(rect.w / 2, rect.h / 2) + } + + this.resize = async (w, h, fit = true) => { // Resizes the canvas to target w and h, returns the rect. + const rect = { x: 0, y: 0, w, h } + const a = document.createElement('img') + const b = document.createElement('img') + a.src = ronin.surface.el.toDataURL() + await ronin.surface.resizeImage(a, b) + ronin.surface.resize(rect, fit) + return ronin.surface.draw(b, rect) + } + + this.rescale = async (w, h) => { // Rescales the canvas to target ratio of w and h, returns the rect. + const rect = { x: 0, y: 0, w: this.frame().w * w, h: this.frame().h * h } + const a = document.createElement('img') + const b = document.createElement('img') + a.src = ronin.surface.el.toDataURL() + await ronin.surface.resizeImage(a, b) + ronin.surface.resize(rect, true) + return ronin.surface.draw(b, rect) + } + + this.crop = async (rect) => { // Crop canvas to rect. + return ronin.surface.crop(rect) + } + + this.clone = (a, b) => { + ronin.surface.clone(a, b) + return [a, b] + } + + this.theme = (variable, el = document.documentElement) => { + // ex. (theme "f_main") -> :root { --f_main: "#fff" } + return getComputedStyle(el).getPropertyValue(`--${variable}`) + } + + // Gradients + + this.gradient = ([x1, y1, x2, y2], colors = ['white', 'black']) => { + return ronin.surface.linearGradient(x1, y1, x2, y2, colors) + } + + // Pixels + + this.pixels = (rect, fn, q) => { + const img = ronin.surface.context.getImageData(0, 0, rect.w, rect.h) + for (let i = 0, loop = img.data.length; i < loop; i += 4) { + const pixel = { r: img.data[i], g: img.data[i + 1], b: img.data[i + 2], a: img.data[i + 3] } + const processed = fn(pixel, q) + img.data[i] = processed[0] + img.data[i + 1] = processed[1] + img.data[i + 2] = processed[2] + img.data[i + 3] = processed[3] + } + ronin.surface.context.putImageData(img, 0, 0) + return rect + } + + this.saturation = (pixel, q = 1) => { + const color = 0.2126 * pixel.r + 0.7152 * pixel.g + 0.0722 * pixel.b + return [(color * (1 - q)) + (pixel.r * q), (color * (1 - q)) + (pixel.g * q), (color * (1 - q)) + (pixel.b * q), pixel.a] + } + + this.contrast = (pixel, q = 1) => { + const intercept = 128 * (1 - q) + return [pixel.r * q + intercept, pixel.g * q + intercept, pixel.b * q + intercept, pixel.a] + } + // Strings this.concat = function (...items) { // Concat multiple strings. @@ -241,83 +318,6 @@ function Library (ronin) { return Object.values(item) } - // Frame - - this.frame = () => { // Returns a rect of the frame. - return ronin.surface.getFrame() - } - - this.center = () => { // Returns a position of the center of the frame. - const rect = this.frame() - return this.pos(rect.w / 2, rect.h / 2) - } - - this.resize = async (w, h, fit = true) => { // Resizes the canvas to target w and h, returns the rect. - const rect = { x: 0, y: 0, w, h } - const a = document.createElement('img') - const b = document.createElement('img') - a.src = ronin.surface.el.toDataURL() - await ronin.surface.resizeImage(a, b) - ronin.surface.resize(rect, fit) - return ronin.surface.draw(b, rect) - } - - this.rescale = async (w, h) => { // Rescales the canvas to target ratio of w and h, returns the rect. - const rect = { x: 0, y: 0, w: this.frame().w * w, h: this.frame().h * h } - const a = document.createElement('img') - const b = document.createElement('img') - a.src = ronin.surface.el.toDataURL() - await ronin.surface.resizeImage(a, b) - ronin.surface.resize(rect, true) - return ronin.surface.draw(b, rect) - } - - this.crop = async (rect) => { // Crop canvas to rect. - return ronin.surface.crop(rect) - } - - this.clone = (a, b) => { - ronin.surface.clone(a, b) - return [a, b] - } - - this.theme = (variable, el = document.documentElement) => { - // ex. (theme "f_main") -> :root { --f_main: "#fff" } - return getComputedStyle(el).getPropertyValue(`--${variable}`) - } - - // Gradients - - this.gradient = ([x1, y1, x2, y2], colors = ['white', 'black']) => { - return ronin.surface.linearGradient(x1, y1, x2, y2, colors) - } - - // Pixels - - this.pixels = (rect, fn, q) => { - const img = ronin.surface.context.getImageData(0, 0, rect.w, rect.h) - for (let i = 0, loop = img.data.length; i < loop; i += 4) { - const pixel = { r: img.data[i], g: img.data[i + 1], b: img.data[i + 2], a: img.data[i + 3] } - const processed = fn(pixel, q) - img.data[i] = processed[0] - img.data[i + 1] = processed[1] - img.data[i + 2] = processed[2] - img.data[i + 3] = processed[3] - } - ronin.surface.context.putImageData(img, 0, 0) - return rect - } - - this.saturation = (pixel, q = 1) => { - const color = 0.2126 * pixel.r + 0.7152 * pixel.g + 0.0722 * pixel.b - return [(color * (1 - q)) + (pixel.r * q), (color * (1 - q)) + (pixel.g * q), (color * (1 - q)) + (pixel.b * q), pixel.a] - } - - this.contrast = (pixel, q = 1) => { - const intercept = 128 * (1 - q) - return [pixel.r * q + intercept, pixel.g * q + intercept, pixel.b * q + intercept, pixel.a] - } - // File System this.dir = (path = this.dirpath()) => { // Returns the content of a directory. diff --git a/desktop/sources/scripts/ronin.js b/desktop/sources/scripts/ronin.js index fade53f..927d388 100644 --- a/desktop/sources/scripts/ronin.js +++ b/desktop/sources/scripts/ronin.js @@ -10,7 +10,7 @@ function Ronin () { b_low: '#aaa', b_inv: '#ffb545' } - + this.includes = ['prelude'] this.el = document.createElement('div') diff --git a/examples/basics.lisp b/examples/basics.lisp index 997be5d..3c6f391 100644 --- a/examples/basics.lisp +++ b/examples/basics.lisp @@ -6,4 +6,11 @@ ; define a function (defn add-two (a) (add 2 a)) -(echo (add-two 4)) \ No newline at end of file +(echo (add-two 4)) + +; run +(times 5 + (lambda + (a) + (echo + (concat "time:" a)))) \ No newline at end of file From 938a9d22ec596c1a83dc980af2f5940b4517ff90 Mon Sep 17 00:00:00 2001 From: Devine Lu Linvega Date: Sun, 21 Jul 2019 09:52:37 +0900 Subject: [PATCH 4/5] =?UTF-8?q?Added=20support=20for=20=CE=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 26 +++++++++++++++----------- desktop/sources/scripts/lisp.js | 2 +- examples/basics.lisp | 7 ++----- examples/benchmark.lisp | 8 ++++---- examples/dejong.lisp | 2 +- examples/theme.lisp | 4 ++-- 6 files changed, 25 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index d49c62f..b1251db 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,8 @@ npm start ## Library +Additional functions can be found in the [includes](https://github.com/hundredrabbits/Ronin/tree/master/desktop/sources/lisp), you can also look at the [examples](https://github.com/hundredrabbits/Ronin/tree/master/examples) to see them in action. + - `(import path rect)` Imports a graphic file with format. - `(export path ~format ~quality)` Exports a graphic file with format. - `(open path)` Imports a graphic file and resizes the frame. @@ -38,6 +40,17 @@ npm start - `(stroke ~shape)` Strokes a shape. - `(fill ~rect)` Fills a shape. - `(clear ~rect)` Clears a rect. +- `(frame)` Returns a rect of the frame. +- `(center)` Returns a position of the center of the frame. +- `(resize w h ~fit)` Resizes the canvas to target w and h, returns the rect. +- `(rescale w h)` Rescales the canvas to target ratio of w and h, returns the rect. +- `(crop rect)` Crop canvas to rect. +- `(clone a b)` +- `(theme variable ~el)` +- `(gradient [x1 y1 x2 y2] ~colors 'black'])` +- `(pixels rect fn q)` +- `(saturation pixel ~q)` +- `(contrast pixel ~q)` - `(concat ...items)` Concat multiple strings. - `(add ...args)` Adds values. - `(sub ...args)` Subtracts values. @@ -71,17 +84,8 @@ npm start - `(get item key)` Gets an object's parameter with name. - `(set item key val)` Sets an object's parameter with name as value. - `(of h ...keys)` Gets object parameters with names. -- `(frame)` Returns a rect of the frame. -- `(center)` Returns a position of the center of the frame. -- `(resize w h ~fit)` Resizes the canvas to target w and h, returns the rect. -- `(rescale w h)` Rescales the canvas to target ratio of w and h, returns the rect. -- `(crop rect)` Crop canvas to rect. -- `(clone a b)` -- `(theme variable ~el)` -- `(gradient [x1 y1 x2 y2] ~colors 'black'])` -- `(pixels rect fn q)` -- `(saturation pixel ~q)` -- `(contrast pixel ~q)` +- `(keys item)` Returns a list of the object's keys +- `(values item)` Returns a list of the object's values - `(dir ~path)` Returns the content of a directory. - `(file ~path)` Returns the content of a file. - `(dirpath ~path)` Returns the path of a directory. diff --git a/desktop/sources/scripts/lisp.js b/desktop/sources/scripts/lisp.js index 82fe8de..e13eb79 100644 --- a/desktop/sources/scripts/lisp.js +++ b/desktop/sources/scripts/lisp.js @@ -153,7 +153,7 @@ function Lisp (lib = {}, includes = []) { } const tokenize = function (input) { - const i = input.replace(/^\;.*\n?/gm, '').split('"') + const i = input.replace(/^\;.*\n?/gm, '').replace('λ','lambda ').split('"') return i.map(function (x, i) { return i % 2 === 0 ? x.replace(/\(/g, ' ( ') diff --git a/examples/basics.lisp b/examples/basics.lisp index 3c6f391..cbffb3e 100644 --- a/examples/basics.lisp +++ b/examples/basics.lisp @@ -8,9 +8,6 @@ (defn add-two (a) (add 2 a)) (echo (add-two 4)) -; run +; use a lambda (times 5 - (lambda - (a) - (echo - (concat "time:" a)))) \ No newline at end of file + (λ (a) (echo (concat "time:" a)))) \ No newline at end of file diff --git a/examples/benchmark.lisp b/examples/benchmark.lisp index e0b698e..03e1e51 100644 --- a/examples/benchmark.lisp +++ b/examples/benchmark.lisp @@ -35,14 +35,14 @@ (test "range simple" (range 0 4) (0 1 2 3 4)) (test "range with step" (range 0 4 2) (0 2 4)) (test "range with negative step" (range 0 -4 -1) (0 -1 -2 -3 -4)) - (test "map" (map (lambda (a) (add 1 a)) (1 2 3)) (2 3 4)) - (test "filter" (filter (lambda (a) (eq 0 (mod a 2))) (2 3 4 5 6)) (2 4 6)) - (test "reduce" (reduce (lambda (acc val) (add acc val)) (1 2 3) 4) 10) + (test "map" (map (λ (a) (add 1 a)) (1 2 3)) (2 3 4)) + (test "filter" (filter (λ (a) (eq 0 (mod a 2))) (2 3 4 5 6)) (2 4 6)) + (test "reduce" (reduce (λ (acc val) (add acc val)) (1 2 3) 4) 10) ; Scope (def aaa 123) - (def addOne (lambda (a) (add a 1))) + (def addOne (λ (a) (add a 1))) (test "def - value" aaa 123) (test "def - func" (addOne 4) 5) (defn addTwo (a) (add 2 a)) diff --git a/examples/dejong.lisp b/examples/dejong.lisp index 654271c..d373008 100644 --- a/examples/dejong.lisp +++ b/examples/dejong.lisp @@ -16,7 +16,7 @@ (defn dejong (r a b c d) (reduce - (lambda (acc val) + (λ (acc val) (first ( (_dejong (first acc) (last acc) a b c d) ))) diff --git a/examples/theme.lisp b/examples/theme.lisp index cba7b21..7a5db32 100644 --- a/examples/theme.lisp +++ b/examples/theme.lisp @@ -2,7 +2,7 @@ (clear) (def col - (lambda + (λ (i) (of ( @@ -16,7 +16,7 @@ (theme "b_inv")) (mod i 8)))) (def rec - (lambda + (λ (v i) (if (gt v 0) From 3093a74d57470cdada357b3f1dd2b0db1ba398c0 Mon Sep 17 00:00:00 2001 From: Devine Lu Linvega Date: Sun, 21 Jul 2019 10:01:03 +0900 Subject: [PATCH 5/5] Test for number of parens --- desktop/sources/scripts/commander.js | 4 ++++ desktop/sources/scripts/lisp.js | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/desktop/sources/scripts/commander.js b/desktop/sources/scripts/commander.js index fa74696..852b2b9 100644 --- a/desktop/sources/scripts/commander.js +++ b/desktop/sources/scripts/commander.js @@ -46,6 +46,10 @@ function Commander (ronin) { this.reindent = function () { let val = this._input.value.replace(/\n/g, '').replace(/ +(?= )/g, '').replace(/\( \(/g, '((').replace(/\) \)/g, '))').trim() let depth = 0 + if (val.split('(').length !== val.split(')').length) { + ronin.log('Uneven number of parens.') + return + } for (let i = 0; i < val.length; i++) { const c = val.charAt(i) if (c === '(') { depth++ } else if (c === ')') { depth-- } diff --git a/desktop/sources/scripts/lisp.js b/desktop/sources/scripts/lisp.js index e13eb79..1eebdd8 100644 --- a/desktop/sources/scripts/lisp.js +++ b/desktop/sources/scripts/lisp.js @@ -153,7 +153,7 @@ function Lisp (lib = {}, includes = []) { } const tokenize = function (input) { - const i = input.replace(/^\;.*\n?/gm, '').replace('λ','lambda ').split('"') + const i = input.replace(/^\;.*\n?/gm, '').replace('λ', 'lambda ').split('"') return i.map(function (x, i) { return i % 2 === 0 ? x.replace(/\(/g, ' ( ')