From 3b07edefc40452c1784499771053c39d7323481e Mon Sep 17 00:00:00 2001 From: neauoire Date: Thu, 26 Mar 2020 20:04:24 +0900 Subject: [PATCH] Misc improvements --- README.txt | 1 + debug.html | 1 - examples/projects/spire.lisp | 20 +++ index.html | 239 ++++---------------------------- resources/MANUAL.txt | 22 --- resources/PRELUDE.md | 46 ------ resources/PREVIEW.jpg | Bin 856120 -> 0 bytes resources/PREVIEW2.jpg | Bin 905484 -> 0 bytes resources/PREVIEW3.jpg | Bin 1162838 -> 0 bytes resources/WORKSHOP.md | 261 ----------------------------------- scripts/client.js | 11 -- scripts/commander.js | 79 ++++++----- scripts/lain(old).js | 180 ------------------------ scripts/lib/acels.js | 5 - scripts/library.js | 1 - 15 files changed, 92 insertions(+), 774 deletions(-) create mode 100644 examples/projects/spire.lisp delete mode 100644 resources/MANUAL.txt delete mode 100644 resources/PRELUDE.md delete mode 100644 resources/PREVIEW.jpg delete mode 100644 resources/PREVIEW2.jpg delete mode 100644 resources/PREVIEW3.jpg delete mode 100644 resources/WORKSHOP.md delete mode 100644 scripts/lain(old).js diff --git a/README.txt b/README.txt index 5a3c5bc..6618797 100644 --- a/README.txt +++ b/README.txt @@ -62,6 +62,7 @@ Library - (open name ~scale) Imports a graphic file with format. - (import name ~shape) Imports a graphic file with format. - (export ~name ~format ~quality) Exports a graphic file with format. +- (print string) Exports string to file. - (pos ~x ~y) Returns a position shape. - (line ax ay bx by) Returns a line shape. - (size w h) Returns a size shape. diff --git a/debug.html b/debug.html index 1abce14..1dd1ba3 100644 --- a/debug.html +++ b/debug.html @@ -16,7 +16,6 @@ - diff --git a/examples/projects/spire.lisp b/examples/projects/spire.lisp new file mode 100644 index 0000000..f68569b --- /dev/null +++ b/examples/projects/spire.lisp @@ -0,0 +1,20 @@ +; this demo shows how to use the mouse events to draw make a simple drawing tool. + +; +(clear) + +; +(def gradient-line + (line frame:c 0 frame:c frame:h)) + +; +(defn draw-circle + (e) + ( + (stroke + (circle e:x e:y e:d) + (gradient gradient-line + ("black" "#72dec2"))))) + +; +(on "mouse-move" draw-circle) \ No newline at end of file diff --git a/index.html b/index.html index 742b643..351471e 100644 --- a/index.html +++ b/index.html @@ -14,7 +14,6 @@ function Acels (client) { this.el.id = 'acels' this.order = [] this.all = {} - this.roles = {} this.pipe = null this.install = (host = document.body) => { window.addEventListener('keydown', this.onKeyDown, false) @@ -44,9 +43,6 @@ function Acels (client) { if (this.order.indexOf(cat) < 0) { this.order.push(cat) } this.all[accelerator] = { cat, name, downfn, upfn, accelerator } } - this.add = (cat, role) => { - this.all[':' + role] = { cat, name: role, role } - } this.get = (accelerator) => { return this.all[accelerator] } @@ -503,12 +499,6 @@ function Client () { this.acels.set('File', 'Save', 'CmdOrCtrl+S', () => { this.source.write('ronin', 'lisp', this.commander._input.value, 'text/plain') }) this.acels.set('File', 'Export Image', 'CmdOrCtrl+E', () => { this.source.write('ronin', 'png', this.surface.el.toDataURL('image/png', 1.0), 'image/png') }) this.acels.set('File', 'Open', 'CmdOrCtrl+U', () => { this.source.open('lisp', this.whenOpen) }) - this.acels.add('Edit', 'undo') - this.acels.add('Edit', 'redo') - this.acels.add('Edit', 'cut') - this.acels.add('Edit', 'copy') - this.acels.add('Edit', 'paste') - this.acels.add('Edit', 'selectAll') this.acels.set('View', 'Toggle Guides', 'CmdOrCtrl+Shift+H', () => { this.surface.toggleGuides() }) this.acels.set('View', 'Toggle Commander', 'CmdOrCtrl+K', () => { this.commander.toggle() }) this.acels.set('View', 'Expand Commander', 'CmdOrCtrl+Shift+K', () => { this.commander.toggle(true) }) @@ -802,19 +792,19 @@ function Commander (client) { if (word === 'x' || word === 'y' || word === 'xy' || word === 'wh' || word === 'a' || word === 'r') { return `${shape}` } return '' } - this.show = function (expand = false) { - if (this.isVisible === true) { return } + this.show = (expand = false) => { + if (this.isVisible === true && expand !== true) { return } client.el.className = expand ? 'expand' : '' this.isVisible = true this._input.focus() } - this.hide = function () { + this.hide = () => { if (this.isVisible !== true) { return } client.el.className = 'hidden' this.isVisible = false this._input.blur() } - this.toggle = function (expand = false) { + this.toggle = (expand = false) => { if (this.isVisible !== true) { this.show(expand) } else { @@ -853,212 +843,41 @@ function Commander (client) { return `(${(name + ' ' + fnParams).trim()})` } this.lint = function () { - let val = this._input.value.replace(/\n/g, '').replace(/ \)/g, ')').replace(/ +(?= )/g, '').replace(/\( \(/g, '((').replace(/\) \)/g, '))').trim() - let depth = 0 - if (val.split('(').length !== val.split(')').length) { - client.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-- } - if (c === ';') { - const indent = '\n' + (' '.repeat(depth)) - val = insert(val, indent, i) - i += indent.length - } - if (c === '(') { - const indent = '\n' + (' '.repeat(depth - 1)) - val = insert(val, indent, i) - i += indent.length - } - if (c === ')' && depth === 0) { - val = insert(val, '\n', i + 1) - } - } - val = val.split('\n').map((line) => { return line.substr(0, 2) === '; ' ? `\n${line}\n` : line }).join('\n') - this._input.value = val.trim() - function insert (str, add, i) { - return [str.slice(0, i), `${add}`, str.slice(i)].join('') + const value = this._input.value + if (value.split('(').length !== value.split(')').length) { + return client.log('Uneven number of parens.') } + this._input.value = lintLISP(value) } this.splash = `; Ronin v2.50 (def logo-path "M60,60 L195,60 A45,45 0 0,1 240,105 A45,45 0 0,1 195,150 L60,150 M195,150 A45,45 0 0,1 240,195 L240,240 ") -; -(clear) +(clear) (resize 600 600) (stroke (svg 140 140 logo-path) "black" 7)` } -'use strict' -function Lisp (lib = {}) { - const TYPES = { identifier: 0, number: 1, string: 2, bool: 3, symbol: 4 } - const Context = function (scope, parent) { - this.scope = scope - this.parent = parent - this.get = function (identifier) { - if (identifier in this.scope) { - return this.scope[identifier] - } else if (this.parent !== undefined) { - return this.parent.get(identifier) - } +function lintLISP (str) { + let val = str.replace(/\n/g, '').replace(/ \)/g, ')').replace(/ +(?= )/g, '').replace(/\( \(/g, '((').replace(/\) \)/g, '))').trim() + let depth = 0 + for (let i = 0; i < val.length; i++) { + const c = val.charAt(i) + depth += c === '(' ? 1 : c === ')' ? -1 : 0 + if (c === ';') { + const indent = '\n' + (' '.repeat(depth)) + val = [val.slice(0, i), `${indent}`, val.slice(i)].join('') + i += indent.length + } + if (c === '(' && val.charAt(i + 1) !== ')') { + const indent = '\n' + (' '.repeat(depth - 1)) + val = [val.slice(0, i), `${indent}`, val.slice(i)].join('') + i += indent.length + } + if (c === ')' && depth === 0) { + val = [val.slice(0, i), ')\n', val.slice(i + 1)].join('') } } - const special = { - let: function (input, context) { - const letContext = input[1].reduce(function (acc, x) { - acc.scope[x[0].value] = interpret(x[1], context) - return acc - }, new Context({}, context)) - return interpret(input[2], letContext) - }, - def: function (input, context) { - const identifier = input[1].value - const value = input[2].type === TYPES.string && input[3] ? input[3] : input[2] - context.scope[identifier] = interpret(value, context) - return value - }, - defn: function (input, context) { - const fnName = input[1].value - 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] - context.scope[fnName] = async 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)) - } - }, - lambda: function (input, context) { - return async function () { - const lambdaArguments = arguments - const lambdaScope = input[1].reduce(function (acc, x, i) { - acc[x.value] = lambdaArguments[i] - return acc - }, {}) - return interpret(input[2], new Context(lambdaScope, context)) - } - }, - if: async function (input, context) { - if (await interpret(input[1], context)) { - return interpret(input[2], context) - } - return input[3] ? interpret(input[3], context) : [] - }, - __fn: function (input, context) { - return async function () { - const lambdaArguments = arguments - const keys = [...new Set(input.slice(2).flat(100).filter(i => - i.type === TYPES.identifier && - i.value[0] === '%' - ).map(x => x.value).sort())] - const lambdaScope = keys.reduce(function (acc, x, i) { - acc[x] = lambdaArguments[i] - return acc - }, {}) - return interpret(input.slice(1), new Context(lambdaScope, context)) - } - }, - __obj: async function (input, context) { - const obj = {} - for (let i = 1; i < input.length; i += 2) { - obj[await interpret(input[i], context)] = await interpret(input[i + 1], context) - } - return obj - } - } - const interpretList = async function (input, context) { - if (input.length > 0 && input[0].value in special) { - return special[input[0].value](input, context) - } - const list = [] - for (let i = 0; i < input.length; i++) { - if (input[i].type === TYPES.symbol) { - if (input[i].host) { - const host = await context.get(input[i].host) - if (host) { - list.push(host[input[i].value]) - } - } else { - list.push(obj => obj[input[i].value]) - } - } else { - list.push(await interpret(input[i], context)) - } - } - return list[0] instanceof Function ? list[0].apply(undefined, list.slice(1)) : list - } - const interpret = async function (input, context) { - if (!input) { console.warn('Lisp', 'error', context.scope); return null } - if (context === undefined) { - return interpret(input, new Context(lib)) - } else if (input instanceof Array) { - return interpretList(input, context) - } else if (input.type === TYPES.identifier) { - return context.get(input.value) - } else if (input.type === TYPES.number || input.type === TYPES.symbol || input.type === TYPES.string || input.type === TYPES.bool) { - return input.value - } - } - const categorize = function (input) { - if (!isNaN(parseFloat(input))) { - return { type: TYPES.number, value: parseFloat(input) } - } else if (input[0] === '"' && input.slice(-1) === '"') { - return { type: TYPES.string, value: input.slice(1, -1) } - } else if (input[0] === ':') { - return { type: TYPES.symbol, value: input.slice(1) } - } else if (input.indexOf(':') > 0) { - return { type: TYPES.symbol, host: input.split(':')[0], value: input.split(':')[1] } - } else if (input === 'true' || input === 'false') { - return { type: TYPES.bool, value: input === 'true' } - } else { - return { type: TYPES.identifier, value: input } - } - } - const parenthesize = function (input, list) { - if (list === undefined) { return parenthesize(input, []) } - const token = input.shift() - if (token === undefined) { - return list.pop() - } else if (token === '\'(') { - input.unshift('__fn') - list.push(parenthesize(input, [])) - return parenthesize(input, list) - } else if (token === '{') { - input.unshift('__obj') - list.push(parenthesize(input, [])) - return parenthesize(input, list) - } else if (token === '(') { - list.push(parenthesize(input, [])) - return parenthesize(input, list) - } else if (token === ')' || token === '}') { - return list - } else { - return parenthesize(input, list.concat(categorize(token))) - } - } - const tokenize = function (input) { - const i = input.replace(/^;.*\n?/gm, '').replace(/λ /g, 'lambda ').split('"') - return i.map(function (x, i) { - return i % 2 === 0 - ? x.replace(/\(/g, ' ( ') - .replace(/\)/g, ' ) ') - .replace(/' \( /g, ' \'( ') // '() - .replace(/\{/g, ' { ') // {} - .replace(/\}/g, ' } ') // {} - : x.replace(/ /g, '!whitespace!') - }) - .join('"').trim().split(/\s+/) - .map(function (x) { return x.replace(/!whitespace!/g, ' ') }) - } - this.parse = function (input) { - return parenthesize(tokenize(input)) - } - this.run = async function (input) { - return interpret(this.parse(`((def theme (get-theme))(def frame (get-frame))(${input}))`)) - } + val = val.split('\n').map((line) => { return line.substr(0, 2) === '; ' ? `\n${line}\n` : line }).join('\n') + return val.trim() } 'use strict' function Library (client) { diff --git a/resources/MANUAL.txt b/resources/MANUAL.txt deleted file mode 100644 index f763f1a..0000000 --- a/resources/MANUAL.txt +++ /dev/null @@ -1,22 +0,0 @@ -File - -New...................... CmdOrCtrl+N -Save..................... CmdOrCtrl+S -Export Image............. CmdOrCtrl+E -Open..................... CmdOrCtrl+O - -Edit - - -View - -Toggle Guides............ CmdOrCtrl+Shift+H -Toggle Commander......... CmdOrCtrl+K -Expand Commander......... CmdOrCtrl+Shift+K - -Project - -Run...................... CmdOrCtrl+R -Reload Run............... CmdOrCtrl+Shift+R -Re-Indent................ CmdOrCtrl+Shift+I -Clean.................... Escape \ No newline at end of file diff --git a/resources/PRELUDE.md b/resources/PRELUDE.md deleted file mode 100644 index 55fc238..0000000 --- a/resources/PRELUDE.md +++ /dev/null @@ -1,46 +0,0 @@ -# Prelude - -## Colors - -### Compare two Colors - -Use: `(if (color-eq (255 200 0) (200 255 0)) "yes" "no")`. - -``` -(defn color-eq - (a b) - (and - (eq a:0 b:0) - (eq a:1 b:1) - (eq a:2 b:2))) -``` - -## Pixels - -### Erase all pixels of a specific color - -Use: `(pixels erase-color (255 0 0))`. - -``` -(defn erase-color - (a b) - (a:0 a:1 a:2 - (if - (and - (eq a:0 b:0) - (eq a:1 b:1) - (eq a:2 b:2)) 0 255))) -``` - -### Posterize - -Use: `(pixels posterize 40)`. - -``` -(defn posterize - (a q) - ( - (step a:0 q) - (step a:1 q) - (step a:2 q) a:3)) -``` \ No newline at end of file diff --git a/resources/PREVIEW.jpg b/resources/PREVIEW.jpg deleted file mode 100644 index 38781008ffc57d84884ac233e0201db185f98061..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 856120 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