add rudimentary variable support; note: the code sucks

This commit is contained in:
hippoz 2022-05-31 17:33:29 +03:00
parent 2e27b99dbf
commit 0240dcc069
Signed by: hippoz
GPG key ID: 7C52899193467641
2 changed files with 98 additions and 44 deletions

View file

@ -64,5 +64,14 @@ export default {
val ??= "null";
return typeof val === "string" ? val : JSON.stringify(val);
},
set: function({ createStream, argv, argc }) {
if (argc < 2) {
return "set: expected at least 2 arguments";
}
const name = argv[0];
argv.splice(0, 1);
createStream(name, argv.join(" "));
}
}

View file

@ -6,7 +6,9 @@ const MAX_STREAMS = 6;
const TokenType = {
Command: "Command",
Argument: "Argument",
Pipe: "Pipe"
Pipe: "Pipe",
VariableArgument: "VariableArgument",
EndOfLine: "EndOfLine"
};
const InstructionType = {
@ -28,32 +30,43 @@ function tokenize(message) {
const tokens = [];
let val = "";
let expecting = Expected.Command;
// length +1 so we get char === undefined at the end of the string
for (let i = 0; i < message.length + 1; i++) {
const char = message[i];
const lines = message.split("\n");
if (char === " " || (char === undefined && val !== "")) { // if the character is a string OR if we are at the end of the string and still have a value
if (expecting === Expected.AfterCommand) {
if (val === "|") {
tokens.push(token(TokenType.Pipe, "|"));
expecting = Expected.Command;
} else {
tokens.push(token(TokenType.Argument, val));
for (let line = 0; line < lines.length; line++) {
expecting = Expected.Command;
// length +1 so we get char === undefined at the end of the string
for (let i = 0; i < lines[line].length + 1; i++) {
const char = lines[line][i];
if (char === " " || (char === undefined && val !== "")) { // if the character is a string OR if we are at the end of the string and still have a value
if (expecting === Expected.AfterCommand) {
if (val === "|") {
tokens.push(token(TokenType.Pipe, "|"));
expecting = Expected.Command;
} else if (val.startsWith("$") && val.length > 1) {
tokens.push(token(TokenType.VariableArgument, val.substring(1, val.length)));
expecting = Expected.AfterCommand;
} else {
tokens.push(token(TokenType.Argument, val));
expecting = Expected.AfterCommand;
}
} else if (expecting === Expected.Command) {
tokens.push(token(TokenType.Command, val));
expecting = Expected.AfterCommand;
} else {
return {
error: "Unexpected 'expecting' value, this is probably a bug in the parser",
tokens: null,
};
}
} else if (expecting === Expected.Command) {
tokens.push(token(TokenType.Command, val));
expecting = Expected.AfterCommand;
val = "";
} else {
return {
error: "Unexpected 'expecting' value, this is probably a bug in the parser",
tokens: null,
};
val += char;
}
val = "";
} else {
val += char;
}
tokens.push(token(TokenType.EndOfLine, ""));
}
if (expecting !== Expected.AfterCommand) {
return {
@ -67,21 +80,31 @@ function tokenize(message) {
};
}
const CommandArgumentType = {
Variable: "Variable",
Immediate: "Immediate"
};
function tokensToInstructions(tokens) {
const streamName = "_commandStream"
const instructions = [
{ type: InstructionType.CreateStream, name: streamName }
{ type: InstructionType.CreateStream, name: "SHELL", value: "tuna" },
];
let commandName = null;
let commandArgs = [];
let currentLine = 0;
let currentStream = `_commandStream$${currentLine}`;
instructions.push({ type: InstructionType.CreateStream, name: currentStream, value: "" });
const endCommand = () => {
instructions.push({
type: InstructionType.Run,
name: commandName,
args: commandArgs,
inStream: streamName,
outStream: streamName
inStream: currentStream,
outStream: currentStream
});
commandName = null;
commandArgs = [];
@ -104,7 +127,7 @@ function tokensToInstructions(tokens) {
instructions: null
}
}
commandArgs.push(tok.value);
commandArgs.push({ value: tok.value, type: CommandArgumentType.Immediate });
} else if (tok.type === TokenType.Pipe) {
if (!commandName) {
return {
@ -113,25 +136,31 @@ function tokensToInstructions(tokens) {
}
}
endCommand();
} else if (tok.type === TokenType.VariableArgument) {
if (!commandName) {
return {
error: "Got TokenType.VariableArgument, however commandName is null",
instructions: null
}
}
commandArgs.push({ value: tok.value, type: CommandArgumentType.Variable });
} else if (tok.type === TokenType.EndOfLine) {
endCommand();
currentLine++;
currentStream = `_commandStream$${currentLine}`;
instructions.push({ type: InstructionType.CreateStream, name: currentStream, value: "" });
} else {
return {
error: "Unknown tok.type",
instructions: null
}
}
if ((i + 1) === tokens.length) {
if (commandName === null) {
return {
error: "End of input, yet commandName is null",
instructions: null
}
}
endCommand();
}
}
instructions.push({ type: InstructionType.DisplayStream, name: streamName });
for (let i = 0; i < currentLine; i++) {
instructions.push({ type: InstructionType.DisplayStream, name: `_commandStream$${i}` });
}
return {
error: null,
@ -140,19 +169,25 @@ function tokensToInstructions(tokens) {
}
function interpretInstructions(instructions, displayFunc) {
console.log(instructions);
if (instructions.length > MAX_INSTRUCTIONS) {
return { error: `Arbitrary limit of ${MAX_INSTRUCTIONS} instructions reached` };
}
const streams = new Map();
const createStream = (name, value) => {
if (streams.size > MAX_STREAMS) {
return { error: `Arbitrary limit of ${MAX_STREAMS} streams reached` };
}
streams.set(name, value);
};
for (let i = 0; i < instructions.length; i++) {
const inst = instructions[i];
switch (inst.type) {
case InstructionType.CreateStream: {
if (streams.size > MAX_STREAMS) {
return { error: `Arbitrary limit of ${MAX_STREAMS} streams reached` };
}
streams.set(inst.name, "");
createStream(inst.name, inst.value);
break;
}
case InstructionType.Run: {
@ -164,11 +199,21 @@ function interpretInstructions(instructions, displayFunc) {
if (inst.inStream) {
streamInput = streams.get(inst.inStream);
}
const args = [];
for (let i = 0; i < inst.args.length; i++) {
const arg = inst.args[i];
if (arg.type === CommandArgumentType.Variable) {
args.push(streams.get(arg.value) ?? "");
} else {
args.push(arg.value);
}
}
try {
let streamOutput = commandFunc({
argv: inst.args,
argc: inst.args.length,
streamInput
argv: args,
argc: args.length,
streamInput,
createStream
});
if (inst.outStream) {
streams.set(inst.outStream, streamOutput);