Skip to content
Snippets Groups Projects
Commit 4d8b589b authored by Lorenz Glißmann's avatar Lorenz Glißmann
Browse files

initial version

parents
No related branches found
No related tags found
No related merge requests found
node_modules
dist
shell
tsconfig.json
.cache
.idea
# simple lisplike shell
## How to use
System Dependencies:
- node js
- yarn/npm
## How it works
- all programs are expressions
- all expressions are withing round brackets () excepting:
- there are implicit round brackets () around the outer expression
- expressions get parsed into an abstrace syntax tree (AST)
- currently 2 types of nodes (expression, assignment) and 1 atom (string)
- the AST gets interpreted by a recursive function
## What works
- expressions/return values/outputs
- `> echo hi` // => hi
- nested expressions
- `> echo (echo hi)` // => hi
- `> x = echo hi` // global symbol binding (aka setting variables); lazy evaluated
- `> x` // calling nested expressions
- piping, after a fashion; currently bash does the piping for us ;)
- extending the language by writing js functions
## What doesn't (yet)
- piping inside the language (currently bash does it)
- local variables/context/`this`
- symbols with arguments (aka functions with arguments)
- `> f x = echo (x)`
- `> + x y = echo (x)+(y) | bc`
- extending the language with macros (functions on the raw ast)
const chalk = require('chalk');
const execa = require("execa");
symbols = {
info: async function (symbol, args) {
return `hallo lieber erdling. ich bin eine seltsame Kreuzung aus bash, lisp und haskell. Noch kann man mich keine Missgeburt taufen, denn es ist ja kaum der Kopf noch nicht draußen...`;
},
run: async function (symbol, args) {
let result = "";
try {
result = await execa.shell(symbol + " " + args.join(" "));
} catch (e) {
return chalk.red(e);
}
return result.stdout;
},
debug: async function (symbol, args) {
console.log(chalk.green(JSON.stringify(args)));
console.log(symbols.f.toString());
}
};
function symbolFactory(symbol, args) {
symbols[symbol] = function () {
};
}
/**
* returns a function that can be called with arguments
* @param symbol
*/
function lookup(symbol) {
if (symbols.hasOwnProperty(symbol)) {
return symbols[symbol];
}
return symbols.run;
}
async function evaluate(command) {
if (typeof command === "string") {
return command;
}
// command is an object
switch (command.type) {
case "command":
// first evaluate all children nodes
for (let i = 0; i < command.data.length; i++) {
command.data[i] = await evaluate((command.data[i]));
}
return await lookup(command.symbol)(command.symbol, command.data);
break;
case "assignment":
symbols[command.symbol] = async (symbol, args) => { // args not used
// for (let i = 0; i < command.data.length; i++) {
// command.data[i] = await evaluate((command.data[i]));
// }
return evaluate({
"type": "command",
"symbol": command.data[0],
"data": command.data.slice(1)
});
};
//throw "Error: assignment not implemented";
break;
}
}
module.exports = evaluate;
/*
* Generated by PEG.js 0.10.0.
*
* http://pegjs.org/
*/
"use strict";
function peg$subclass(child, parent) {
function ctor() { this.constructor = child; }
ctor.prototype = parent.prototype;
child.prototype = new ctor();
}
function peg$SyntaxError(message, expected, found, location) {
this.message = message;
this.expected = expected;
this.found = found;
this.location = location;
this.name = "SyntaxError";
if (typeof Error.captureStackTrace === "function") {
Error.captureStackTrace(this, peg$SyntaxError);
}
}
peg$subclass(peg$SyntaxError, Error);
peg$SyntaxError.buildMessage = function(expected, found) {
var DESCRIBE_EXPECTATION_FNS = {
literal: function(expectation) {
return "\"" + literalEscape(expectation.text) + "\"";
},
"class": function(expectation) {
var escapedParts = "",
i;
for (i = 0; i < expectation.parts.length; i++) {
escapedParts += expectation.parts[i] instanceof Array
? classEscape(expectation.parts[i][0]) + "-" + classEscape(expectation.parts[i][1])
: classEscape(expectation.parts[i]);
}
return "[" + (expectation.inverted ? "^" : "") + escapedParts + "]";
},
any: function(expectation) {
return "any character";
},
end: function(expectation) {
return "end of input";
},
other: function(expectation) {
return expectation.description;
}
};
function hex(ch) {
return ch.charCodeAt(0).toString(16).toUpperCase();
}
function literalEscape(s) {
return s
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\0/g, '\\0')
.replace(/\t/g, '\\t')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/[\x00-\x0F]/g, function(ch) { return '\\x0' + hex(ch); })
.replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return '\\x' + hex(ch); });
}
function classEscape(s) {
return s
.replace(/\\/g, '\\\\')
.replace(/\]/g, '\\]')
.replace(/\^/g, '\\^')
.replace(/-/g, '\\-')
.replace(/\0/g, '\\0')
.replace(/\t/g, '\\t')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/[\x00-\x0F]/g, function(ch) { return '\\x0' + hex(ch); })
.replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return '\\x' + hex(ch); });
}
function describeExpectation(expectation) {
return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation);
}
function describeExpected(expected) {
var descriptions = new Array(expected.length),
i, j;
for (i = 0; i < expected.length; i++) {
descriptions[i] = describeExpectation(expected[i]);
}
descriptions.sort();
if (descriptions.length > 0) {
for (i = 1, j = 1; i < descriptions.length; i++) {
if (descriptions[i - 1] !== descriptions[i]) {
descriptions[j] = descriptions[i];
j++;
}
}
descriptions.length = j;
}
switch (descriptions.length) {
case 1:
return descriptions[0];
case 2:
return descriptions[0] + " or " + descriptions[1];
default:
return descriptions.slice(0, -1).join(", ")
+ ", or "
+ descriptions[descriptions.length - 1];
}
}
function describeFound(found) {
return found ? "\"" + literalEscape(found) + "\"" : "end of input";
}
return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found.";
};
function peg$parse(input, options) {
options = options !== void 0 ? options : {};
var peg$FAILED = {},
peg$startRuleFunctions = { Command: peg$parseCommand },
peg$startRuleFunction = peg$parseCommand,
peg$c0 = "=",
peg$c1 = peg$literalExpectation("=", false),
peg$c2 = function(symbol, args, cmd) {
return {
"type": "assignment",
"symbol": symbol,
"args": args.map(x=>x[0]),
"data": cmd.map(x=>x[0])
}
},
peg$c3 = function(symbol, cmd) {
return {
"type": "command",
"symbol": symbol,
"data": cmd.map(x=>x[0])
}
},
peg$c4 = "",
peg$c5 = "(",
peg$c6 = peg$literalExpectation("(", false),
peg$c7 = ")",
peg$c8 = peg$literalExpectation(")", false),
peg$c9 = function(s) { return s },
peg$c10 = /^[a-zA-Z0-9*+|]/,
peg$c11 = peg$classExpectation([["a", "z"], ["A", "Z"], ["0", "9"], "*", "+", "|"], false, false),
peg$c12 = function(s) { return s.join(""); },
peg$c13 = peg$otherExpectation("whitespace"),
peg$c14 = /^[ \t\r]/,
peg$c15 = peg$classExpectation([" ", "\t", "\r"], false, false),
peg$currPos = 0,
peg$savedPos = 0,
peg$posDetailsCache = [{ line: 1, column: 1 }],
peg$maxFailPos = 0,
peg$maxFailExpected = [],
peg$silentFails = 0,
peg$result;
if ("startRule" in options) {
if (!(options.startRule in peg$startRuleFunctions)) {
throw new Error("Can't start parsing from rule \"" + options.startRule + "\".");
}
peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
}
function text() {
return input.substring(peg$savedPos, peg$currPos);
}
function location() {
return peg$computeLocation(peg$savedPos, peg$currPos);
}
function expected(description, location) {
location = location !== void 0 ? location : peg$computeLocation(peg$savedPos, peg$currPos)
throw peg$buildStructuredError(
[peg$otherExpectation(description)],
input.substring(peg$savedPos, peg$currPos),
location
);
}
function error(message, location) {
location = location !== void 0 ? location : peg$computeLocation(peg$savedPos, peg$currPos)
throw peg$buildSimpleError(message, location);
}
function peg$literalExpectation(text, ignoreCase) {
return { type: "literal", text: text, ignoreCase: ignoreCase };
}
function peg$classExpectation(parts, inverted, ignoreCase) {
return { type: "class", parts: parts, inverted: inverted, ignoreCase: ignoreCase };
}
function peg$anyExpectation() {
return { type: "any" };
}
function peg$endExpectation() {
return { type: "end" };
}
function peg$otherExpectation(description) {
return { type: "other", description: description };
}
function peg$computePosDetails(pos) {
var details = peg$posDetailsCache[pos], p;
if (details) {
return details;
} else {
p = pos - 1;
while (!peg$posDetailsCache[p]) {
p--;
}
details = peg$posDetailsCache[p];
details = {
line: details.line,
column: details.column
};
while (p < pos) {
if (input.charCodeAt(p) === 10) {
details.line++;
details.column = 1;
} else {
details.column++;
}
p++;
}
peg$posDetailsCache[pos] = details;
return details;
}
}
function peg$computeLocation(startPos, endPos) {
var startPosDetails = peg$computePosDetails(startPos),
endPosDetails = peg$computePosDetails(endPos);
return {
start: {
offset: startPos,
line: startPosDetails.line,
column: startPosDetails.column
},
end: {
offset: endPos,
line: endPosDetails.line,
column: endPosDetails.column
}
};
}
function peg$fail(expected) {
if (peg$currPos < peg$maxFailPos) { return; }
if (peg$currPos > peg$maxFailPos) {
peg$maxFailPos = peg$currPos;
peg$maxFailExpected = [];
}
peg$maxFailExpected.push(expected);
}
function peg$buildSimpleError(message, location) {
return new peg$SyntaxError(message, null, null, location);
}
function peg$buildStructuredError(expected, found, location) {
return new peg$SyntaxError(
peg$SyntaxError.buildMessage(expected, found),
expected,
found,
location
);
}
function peg$parseCommand() {
var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
s0 = peg$currPos;
s1 = peg$parseString();
if (s1 !== peg$FAILED) {
s2 = peg$parse_();
if (s2 !== peg$FAILED) {
s3 = [];
s4 = peg$currPos;
s5 = peg$parseString();
if (s5 !== peg$FAILED) {
s6 = peg$parse_();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$parseString();
if (s5 !== peg$FAILED) {
s6 = peg$parse_();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
if (s3 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 61) {
s4 = peg$c0;
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c1); }
}
if (s4 !== peg$FAILED) {
s5 = peg$parse_();
if (s5 !== peg$FAILED) {
s6 = [];
s7 = peg$currPos;
s8 = peg$parseSubCommand();
if (s8 !== peg$FAILED) {
s9 = peg$parse_();
if (s9 !== peg$FAILED) {
s8 = [s8, s9];
s7 = s8;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
if (s7 !== peg$FAILED) {
while (s7 !== peg$FAILED) {
s6.push(s7);
s7 = peg$currPos;
s8 = peg$parseSubCommand();
if (s8 !== peg$FAILED) {
s9 = peg$parse_();
if (s9 !== peg$FAILED) {
s8 = [s8, s9];
s7 = s8;
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
} else {
peg$currPos = s7;
s7 = peg$FAILED;
}
}
} else {
s6 = peg$FAILED;
}
if (s6 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$c2(s1, s3, s6);
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseString();
if (s1 !== peg$FAILED) {
s2 = peg$parse_();
if (s2 !== peg$FAILED) {
s3 = [];
s4 = peg$currPos;
s5 = peg$parseSubCommand();
if (s5 !== peg$FAILED) {
s6 = peg$parse_();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$currPos;
s5 = peg$parseSubCommand();
if (s5 !== peg$FAILED) {
s6 = peg$parse_();
if (s6 !== peg$FAILED) {
s5 = [s5, s6];
s4 = s5;
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
} else {
peg$currPos = s4;
s4 = peg$FAILED;
}
}
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$c3(s1, s3);
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$c4;
}
}
return s0;
}
function peg$parseSubCommand() {
var s0, s1, s2, s3, s4, s5, s6, s7;
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 40) {
s1 = peg$c5;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c6); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parseString();
if (s2 !== peg$FAILED) {
s3 = peg$parse_();
if (s3 !== peg$FAILED) {
s4 = [];
s5 = peg$currPos;
s6 = peg$parseSubCommand();
if (s6 !== peg$FAILED) {
s7 = peg$parse_();
if (s7 !== peg$FAILED) {
s6 = [s6, s7];
s5 = s6;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
while (s5 !== peg$FAILED) {
s4.push(s5);
s5 = peg$currPos;
s6 = peg$parseSubCommand();
if (s6 !== peg$FAILED) {
s7 = peg$parse_();
if (s7 !== peg$FAILED) {
s6 = [s6, s7];
s5 = s6;
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
} else {
peg$currPos = s5;
s5 = peg$FAILED;
}
}
if (s4 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 41) {
s5 = peg$c7;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c8); }
}
if (s5 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$c3(s2, s4);
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseString();
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$c9(s1);
}
s0 = s1;
}
return s0;
}
function peg$parseString() {
var s0, s1, s2;
s0 = peg$currPos;
s1 = [];
if (peg$c10.test(input.charAt(peg$currPos))) {
s2 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c11); }
}
if (s2 !== peg$FAILED) {
while (s2 !== peg$FAILED) {
s1.push(s2);
if (peg$c10.test(input.charAt(peg$currPos))) {
s2 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c11); }
}
}
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$c12(s1);
}
s0 = s1;
return s0;
}
function peg$parse_() {
var s0, s1;
peg$silentFails++;
s0 = [];
if (peg$c14.test(input.charAt(peg$currPos))) {
s1 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c15); }
}
while (s1 !== peg$FAILED) {
s0.push(s1);
if (peg$c14.test(input.charAt(peg$currPos))) {
s1 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c15); }
}
}
peg$silentFails--;
if (s0 === peg$FAILED) {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$c13); }
}
return s0;
}
peg$result = peg$startRuleFunction();
if (peg$result !== peg$FAILED && peg$currPos === input.length) {
return peg$result;
} else {
if (peg$result !== peg$FAILED && peg$currPos < input.length) {
peg$fail(peg$endExpectation());
}
throw peg$buildStructuredError(
peg$maxFailExpected,
peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null,
peg$maxFailPos < input.length
? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1)
: peg$computeLocation(peg$maxFailPos, peg$maxFailPos)
);
}
}
module.exports = {
SyntaxError: peg$SyntaxError,
parse: peg$parse
};
Command
= symbol:String _ args:( String _)* '=' _ cmd:( SubCommand _)+ {
return {
"type": "assignment",
"symbol": symbol,
"args": args.map(x=>x[0]),
"data": cmd.map(x=>x[0])
}
}
/ symbol:String _ cmd:( SubCommand _)* {
return {
"type": "command",
"symbol": symbol,
"data": cmd.map(x=>x[0])
}
}
/ ""
//{ return cmd.map(x => x[0]) }
SubCommand
= '(' symbol:String _ cmd:( SubCommand _)* ')' {
return {
"type": "command",
"symbol": symbol,
"data": cmd.map(x=>x[0])
}
}
/ s:String { return s }
String = s:[a-zA-Z0-9\*\+|]+ { return s.join(""); }
_ "whitespace"
= [ \t\r]*
index.js 0 → 100644
let grammar = require("./grammar");
let evaluate = require("./evaluate");
var readline = require('readline');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: true
});
rl.setPrompt("> ");
rl.prompt();
rl.on('line', function(line){
evaluate(grammar.parse(line)).then((result) => {
console.log(result);
rl.prompt();
});
});
{
"name": "shell",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"chalk": "^2.4.2",
"execa": "^1.0.0",
"immutable": "^4.0.0-rc.12",
"parcel": "^1.12.3",
"pegjs": "^0.10.0"
},
"scripts": {
"start": "npx pegjs grammar.pegjs && node index.js",
"bundle": "npx parcel "
}
}
run.sh 0 → 100755
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment