M README.md => README.md +2 -0
@@ 24,6 24,8 @@ These links point point to urls which encode the full text of the program, so th
TODO: This uses mousemove to tick. Will reintroduce tick as soon as I can make errors stop shooting out like a firehose during editing.
[Basic snake game](https://kyleperik.com/pipes/#ZGVmIGxvZyBtOgogIFsnbG9nJywgbV07CmRlZiBzZXRzdGF0ZSBzdGF0ZTogCiAgWydzZXRfc3RhdGUnLCBzdGF0ZV07CmRlZiBpbnB1dCBbJ2lucHV0JywgbSwgc106IG07CmRlZiBkcmF3IHggOiBbJ2RyYXcnLCB4XTsKZGVmIGNsZWFyCiAgeCA6IHt0eXBlOiAnY2xlYXInfSB8IGRyYXc7CgpkZWYgbW9kICgKICBbbiwgbV0gPyAoWwogICAgKFtuLCAwXSB8IGd0ZSksCiAgICAoW24sIG1dIHwgbHQpLAogIF0gfCBhbmQpOiBuOwogIFtuLCBtXSA/IChbbiwgbV0gfCBndGUpOgogIFsoW24sIG1dIHwgc3ViKSwgbV0gfCBtb2Q7CiAgW24sIG1dID8gKFtuLCAwXSB8IGx0KToKICBbKFtuLCBtXSB8IGFkZCksIG1dIHwgbW9kOwopOwpkZWYgc2NyZWVud3JhcCB7eCwgeX06CnsKICB4OiAoW3gsIDIwMF0gfCBtb2QpLAogIHk6IChbeSwgMjAwXSB8IG1vZCksCn07CgpkZWYgdmVjYWRkIFsKICB7eDogeGEsIHk6IHlhfSwgCiAge3g6IHhiLCB5OiB5Yn0KXTogewogIHg6IChbeGEsIHhiXSB8IGFkZCksCiAgeTogKFt5YSwgeWJdIHwgYWRkKQp9OwpkZWYgdmVjbXVsCiAgW3t4LCB5fSwgc10gOiB7CiAgICB4OiAoW3gsIHNdIHwgbXVsKSwKICAgIHk6IChbeSwgc10gfCBtdWwpLAogIH07CgpkZWYgc2xpY2UgKAogIFtwYXJ0cywgbGVuZ3RoXTogWwogICAgcGFydHMsIGxlbmd0aCwgW10KICBdIHwgc2xpY2U7CgogIFtwYXJ0cywgMCwgcmVzdWx0XTogcmVzdWx0OwoKICBbW10sIGxlbmd0aCwgcmVzdWx0XSAKICA/IChbbGVuZ3RoLCAwXSB8IGd0KTogcmVzdWx0OwoKICBbW2hlYWQsIC4uLnBhcnRzXSwgbGVuZ3RoLCByZXN1bHRdCiAgPyAoW2xlbmd0aCwgMF0gfCBndCk6IFsKICAgcGFydHMsCiAgIChbbGVuZ3RoLCAxXSB8IHN1YiksCiAgIFsuLi5yZXN1bHQsIGhlYWRdCiAgXSB8IHNsaWNlOwopOwoKZGVmIGNpcmNsZQogIHBvcyA6IHsKICAgIHR5cGU6ICdjaXJjbGUnLAogICAgcmFkaXVzOiAxMCwKICAgIHBvcwogIH0gfCBkcmF3OwoKZGVmIGVhY2ggKAogIFtwYXJ0LCAuLi5yZXN0XTogcmVzdCB8IGVhY2g7CiAgW3BhcnQsIC4uLnJlc3RdOiBwYXJ0OwopOwoKWydpbml0JywgbnVsbCwgc106IHsKICBwYXJ0czogW3sgeDogNTAsIHk6IDUwIH1dLAogIGRpcmVjdGlvbjogeyB4OiAxLCB5OiAwIH0sCiAgbGVuZ3RoOiAyMCwKfSB8IHNldHN0YXRlOwoKWydrZXlwcmVzcycsIGtleSwgc3RhdGVdOiBrZXkKfCAoCiAgJ2EnOiB7IHg6IC0xLCB5OiAwIH07CiAgJ2QnOiB7IHg6IDEsIHk6IDAgfTsKICAndyc6IHsgeDogMCwgeTogLTEgfTsKICAncyc6IHsgeDogMCwgeTogMSB9OwopCnwgZGlyZWN0aW9uOiB7IGRpcmVjdGlvbiB9Cnwgc2V0c3RhdGU7CgpbJ21vdXNlbW92ZScsIG51bGwsIHN0YXRlXTogc3RhdGUKfCB7cGFydHM6IFtoZWFkLCAuLi5wYXJ0c10sIGRpcmVjdGlvbiwgbGVuZ3RofTogewogIHBhcnRzOiAoW1sKICAgIChbCiAgICAgIGhlYWQsCiAgICAgIChbZGlyZWN0aW9uLCAzXSB8IHZlY211bCkKICAgIF0gfCB2ZWNhZGQpIHwgc2NyZWVud3JhcCwKICAgIGhlYWQsCiAgICAuLi5wYXJ0cwogIF0sIGxlbmd0aF0gfCBzbGljZSkKfSB8ICgKICBzZXRzdGF0ZTsKICBjbGVhcjsKICB7cGFydHN9OiBwYXJ0cwogIHwgZWFjaCB8IGNpcmNsZTsKKTs=)
+[Fizzbuzz](https://kyleperik.com/pipes/#aW5wdXQgfCByYW5nZSB8IGZpenpidXp6IHwgbG9nOwoKZGVmIGZpenpidXp6Cm46IFsKICBuLAogIChbbiwgM10gfCBkaXZpc2libGUpLAogIChbbiwgNV0gfCBkaXZpc2libGUpLApdCnwgKAogIFtuLCB0cnVlLCB0cnVlXTogJ2ZpenpidXp6JzsKICBbbiwgdHJ1ZSwgZmFsc2VdOiAnZml6eic7CiAgW24sIGZhbHNlLCB0cnVlXTogJ2J1enonOwogIFtuLCBmYWxzZSwgZmFsc2VdOiBuOwopOwoKZGVmIGRpdmlzaWJsZSBtb2QgfCBuOiBbbiwgMF0gfCBlcTsKCgpkZWYgcmFuZ2UgbiA/KFtuLCAwXSB8IGd0KTogW24sIDFdCnwgc3ViIAp8ICgKICBuOiBuOwogIHJhbmdlOwopOwoKZGVmIGlucHV0CiAgWydpbnB1dCcsIHZhbHVlLCBzXTogdmFsdWU7CgpkZWYgbG9nCiAgdmFsdWU6IFsnbG9nJywgdmFsdWVdOwoK)
+
## Overview
Pipes statements define what it should select, and what to generate with it.
M src/device.js => src/device.js +1 -1
@@ 74,7 74,7 @@ function processEvent (payload) {
if (!runningProgram) {
return
}
- send([...payload, state], runningProgram.patterns, runningProgram.definitions).forEach(handle)
+ run([...payload, state], runningProgram.patterns, runningProgram.definitions).forEach(handle)
}
function draw (element) {
M src/parser.js => src/parser.js +26 -16
@@ 163,6 163,10 @@ function parsePatternFragment (tokens) {
if (!isNaN(number)) {
return { type: LIT, value: number }
}
+ const bool = {'true': true, 'false': false}[token]
+ if (typeof bool !== 'undefined') {
+ return { type: LIT, value: bool };
+ }
// This is a single token, which we assume is a reference
return {
type: REF,
@@ 171,33 175,39 @@ function parsePatternFragment (tokens) {
}
}
-function processPipeToken (r, tokens) {
+function getPipeResult(tokens) {
if (tokens.length !== 1) {
console.error(r)
throw Error(`Only one token allowed for pipe: ${JSON.stringify(tokens)}`)
}
- token = tokens[0]
+ token = tokens[0];
if (typeof token === 'object') {
if (token.type === 'parens') {
return {
- ...r,
- mode: 'pending',
- pendingPatterns: r.pendingPatterns.concat([
- {
- type: GROUP,
- patterns: parseGroups(token.tokens).patterns
- }
- ])
+ type: GROUP,
+ patterns: parseGroups(token.tokens).patterns
}
}
}
return {
+ type: REF,
+ value: token
+ }
+}
+
+function processPipeToken (r, allTokens) {
+ const bundle = allTokens[0] === '@';
+ const tokens = bundle ? allTokens.slice(1) : allTokens;
+ const result = bundle ? {
+ type: BUNDLE,
+ value: getPipeResult(tokens)
+ } : getPipeResult(tokens);
+ return {
...r,
mode: 'pending',
- pendingPatterns: r.pendingPatterns.concat([{
- type: REF,
- value: token
- }])
+ pendingPatterns: r.pendingPatterns.concat([
+ result
+ ])
}
}
@@ 206,7 216,7 @@ function processPatternMatch(r, tokens) {
...r,
mode: 'pending',
match: null,
- opeartion: null,
+ condition: null,
pendingPatterns: r.pendingPatterns.concat([{
type: PATTERN,
match: r.match,
@@ 339,7 349,7 @@ function parseGroups (tokenGroups) {
}
function parse (body) {
- const tokenRegex = /({|}|\[|\]|\n|'|,|:|;|\?|\s+|[A-Za-z]+)/g;
+ const tokenRegex = /({|}|\[|\]|\n|'|,|:|;|\?|@|\s+|[A-Za-z]+)/g;
const allTokens = body.split(tokenRegex).filter(token => token)
const tokenGroups = groupTokens(allTokens)
M src/runtime.js => src/runtime.js +19 -3
@@ 6,6 6,7 @@ const REF = 'reference'
const REST = 'rest'
const GROUP = 'group'
const PATTERN = 'pattern'
+const BUNDLE = 'bundle'
function checkMatch (data, match, refs=[]) {
if (match.type === LIT) {
@@ 66,12 67,21 @@ function checkMatch (data, match, refs=[]) {
throw new Error(`Match type not recognized ${match.type}`)
}
+function mod(a, b) {
+ return Math.abs(a % b)
+}
+
function handleOp(result, refs, defs) {
+ // TODO: Better way to handle any arity ops in a standard way
+ if (result.op === 'not') {
+ return buildResult(result.operands, refs, defs).flatMap(a => !a)
+ }
const func = {
add: (a, b) => a + b,
sub: (a, b) => a - b,
mul: (a, b) => a * b,
div: (a, b) => a / b,
+ mod: (a, b) => mod(a, b),
// TODO: check equality by value, not by reference
eq: (a, b) => a === b,
gt: (a, b) => a > b,
@@ 150,6 160,9 @@ function handlePipe (data, pipe, definitions) {
return send(data, definition, definitions)
} else if (pipe.type === GROUP) {
return send(data, pipe.patterns, definitions)
+ } else if (pipe.type === BUNDLE) {
+ const result = handlePipe(data, pipe.value, definitions)
+ return [result]
} else if (pipe.type === PATTERN) {
const match = checkMatch(data, pipe.match)
if (!match.matched) {
@@ 183,14 196,17 @@ function getOpDef (op) {
const standardDefinitions = [
'add', 'sub', 'mul', 'div',
'eq', 'gt', 'lt', 'gte',
- 'and', 'or'
+ 'and', 'or', 'not', 'mod'
].reduce((r, op) => ({...r, [op]: [[getOpDef(op)]]}), {})
// Run a payload through the program
function send (data, patterns, definitions) {
- const allDefinitions = {...definitions, ...standardDefinitions}
return patterns.flatMap(pipes => pipes.reduce((r, pipe) => {
- return r.flatMap(d => handlePipe(d, pipe, allDefinitions))
+ return r.flatMap(d => handlePipe(d, pipe, definitions))
}, [data]))
}
+function run (data, patterns, definitions) {
+ const allDefinitions = {...definitions, ...standardDefinitions}
+ return send(data, patterns, allDefinitions)
+}