mirror of
https://github.com/Noratrieb/println.git
synced 2026-03-14 14:36:11 +01:00
stuff
This commit is contained in:
parent
464bdc2478
commit
bb234cc0dd
3 changed files with 342 additions and 7 deletions
|
|
@ -2,8 +2,12 @@
|
|||
"name": "println",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"watch": "pnpm '/watch:*/'",
|
||||
"watch:build": "tsc --watch",
|
||||
"watch:run": "node --watch .",
|
||||
"dev": "node .",
|
||||
"build": "tsc"
|
||||
},
|
||||
"keywords": [],
|
||||
|
|
|
|||
334
src/index.ts
334
src/index.ts
|
|
@ -0,0 +1,334 @@
|
|||
// simple
|
||||
const TypeBoolean = Symbol("TypeBoolean");
|
||||
const TypeFontID = Symbol("TypeFontID");
|
||||
const TypeInteger = Symbol("TypeInteger");
|
||||
const TypeMark = Symbol("TypeMark");
|
||||
const TypeName = Symbol("TypeName");
|
||||
const TypeNull = Symbol("TypeNull");
|
||||
const TypeOperator = Symbol("TypeOperator");
|
||||
const TypeReal = Symbol("TypeReal");
|
||||
// composite
|
||||
const TypeArray = Symbol("TypeArray");
|
||||
const TypeDictionary = Symbol("TypeDictionary");
|
||||
const TypeFile = Symbol("TypeFile");
|
||||
const TypeGState = Symbol("TypeGState");
|
||||
const TypePackedArray = Symbol("TypePackedArray");
|
||||
const TypeSave = Symbol("TypeSave");
|
||||
const TypeString = Symbol("TypeString");
|
||||
|
||||
type Name = number;
|
||||
|
||||
type PsObjectBase =
|
||||
| { type: typeof TypeBoolean; value: boolean }
|
||||
| { type: typeof TypeFontID; value: number }
|
||||
| { type: typeof TypeInteger; value: number }
|
||||
| { type: typeof TypeMark }
|
||||
| { type: typeof TypeName; name: Name }
|
||||
| { type: typeof TypeNull }
|
||||
| { type: typeof TypeOperator; implementation: (state: PSExecState) => void }
|
||||
| { type: typeof TypeReal; value: number }
|
||||
| { type: typeof TypeArray }
|
||||
| {
|
||||
type: typeof TypeDictionary;
|
||||
// TODO: store in memory
|
||||
values: Map<Name, PsObject>;
|
||||
}
|
||||
| { type: typeof TypeFile }
|
||||
| { type: typeof TypeGState }
|
||||
| { type: typeof TypePackedArray }
|
||||
| { type: typeof TypeSave }
|
||||
| { type: typeof TypeString };
|
||||
|
||||
const AttrLiteral = Symbol("AttrLiteral");
|
||||
const AttrExecutable = Symbol("AttrExecute");
|
||||
|
||||
const AccessUnlimited = Symbol("AccessUnlimited");
|
||||
const AccessReadOnly = Symbol("AccessReadOnly");
|
||||
const AccessExecuteOnly = Symbol("AccessExecuteOnly");
|
||||
const AccessNone = Symbol("AccessNone");
|
||||
|
||||
type PsObject = PsObjectBase & {
|
||||
litexec: typeof AttrLiteral | typeof AttrExecutable;
|
||||
access:
|
||||
| typeof AccessUnlimited
|
||||
| typeof AccessReadOnly
|
||||
| typeof AccessExecuteOnly
|
||||
| typeof AccessNone;
|
||||
};
|
||||
|
||||
type PsObjectBoolean = Extract<PsObject, { type: typeof TypeBoolean }>;
|
||||
type PsObjectFontID = Extract<PsObject, { type: typeof TypeFontID }>;
|
||||
type PsObjectInteger = Extract<PsObject, { type: typeof TypeInteger }>;
|
||||
type PsObjectMark = Extract<PsObject, { type: typeof TypeMark }>;
|
||||
type PsObjectName = Extract<PsObject, { type: typeof TypeName }>;
|
||||
type PsObjectNull = Extract<PsObject, { type: typeof TypeNull }>;
|
||||
type PsObjectOperator = Extract<PsObject, { type: typeof TypeOperator }>;
|
||||
type PsObjectReal = Extract<PsObject, { type: typeof TypeReal }>;
|
||||
type PsObjectArray = Extract<PsObject, { type: typeof TypeArray }>;
|
||||
type PsObjectDictionary = Extract<PsObject, { type: typeof TypeDictionary }>;
|
||||
type PsObjectFile = Extract<PsObject, { type: typeof TypeFile }>;
|
||||
type PsObjectGState = Extract<PsObject, { type: typeof TypeGState }>;
|
||||
type PsObjectPackedArray = Extract<PsObject, { type: typeof TypePackedArray }>;
|
||||
type PsObjectSave = Extract<PsObject, { type: typeof TypeSave }>;
|
||||
type PsObjectString = Extract<PsObject, { type: typeof TypeString }>;
|
||||
|
||||
// stuff
|
||||
|
||||
let nextNameIdx = 0;
|
||||
const nameIndicies: Map<string, Name> = new Map();
|
||||
const nameStrings: Map<Name, string> = new Map();
|
||||
const newName = (value: string): Name => {
|
||||
let nameIdx = nameIndicies.get(value);
|
||||
if (nameIdx !== undefined) {
|
||||
return nameIdx;
|
||||
}
|
||||
nameIdx = nextNameIdx++;
|
||||
nameIndicies.set(value, nameIdx);
|
||||
nameStrings.set(nameIdx, value);
|
||||
return nameIdx;
|
||||
};
|
||||
const nameString = (name: Name): string => {
|
||||
const string = nameStrings.get(name);
|
||||
if (string === undefined) {
|
||||
throw new Error(`invalid name: ${name} has no associated string`);
|
||||
}
|
||||
return string;
|
||||
};
|
||||
|
||||
// logic
|
||||
|
||||
const file = function* (input: string) {
|
||||
for (const char of input) {
|
||||
yield char;
|
||||
}
|
||||
};
|
||||
|
||||
const scan = function* (
|
||||
file: Generator<string>
|
||||
): Generator<PsObject, void, void> {
|
||||
const splitter = function* (file: Generator<string>): Generator<string> {
|
||||
const isWhitespace = (value: string) =>
|
||||
value === " " || value === "\t" || value === "\r" || value === "\n";
|
||||
|
||||
let accumulator = "";
|
||||
let next;
|
||||
while (((next = file.next()), !next.done)) {
|
||||
if (next.value === "(") {
|
||||
throw new Error("todo (");
|
||||
}
|
||||
|
||||
if (isWhitespace(next.value)) {
|
||||
if (accumulator.length > 0) {
|
||||
yield accumulator;
|
||||
}
|
||||
accumulator = "";
|
||||
continue;
|
||||
} else if (next.value === "%") {
|
||||
while (((next = file.next()), !next.done && next.value !== "\n")) {}
|
||||
continue;
|
||||
} else if (
|
||||
["<", ">", "[", "]", "{", "}", "/", "%"].includes(next.value)
|
||||
) {
|
||||
if (accumulator.length > 0) {
|
||||
yield accumulator;
|
||||
}
|
||||
accumulator = "";
|
||||
}
|
||||
|
||||
accumulator += next.value;
|
||||
}
|
||||
|
||||
if (accumulator.length > 0) {
|
||||
yield accumulator;
|
||||
}
|
||||
};
|
||||
|
||||
const tokens = splitter(file);
|
||||
|
||||
let next;
|
||||
while (((next = tokens.next()), !next.done)) {
|
||||
console.log("token", next.value);
|
||||
switch (true) {
|
||||
case /^\d+$/.test(next.value): {
|
||||
const numericValue = parseInt(next.value, 10);
|
||||
yield {
|
||||
type: TypeInteger,
|
||||
value: numericValue,
|
||||
litexec: AttrLiteral,
|
||||
access: AccessUnlimited,
|
||||
};
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const isLiteral = next.value.startsWith("/");
|
||||
const nameValue = newName(isLiteral ? next.value.slice(1) : next.value);
|
||||
yield {
|
||||
type: TypeName,
|
||||
name: nameValue,
|
||||
litexec: isLiteral ? AttrLiteral : AttrExecutable,
|
||||
access: AccessUnlimited,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
type Stack<T> = T[];
|
||||
|
||||
type PSExecState = {
|
||||
operandStack: Stack<PsObject>;
|
||||
dictionaryStack: Stack<PsObjectDictionary>;
|
||||
executionStack: Stack<PsObject>;
|
||||
};
|
||||
|
||||
const initExecState = (): PSExecState => {
|
||||
const systemdict: PsObjectDictionary = {
|
||||
type: TypeDictionary,
|
||||
values: new Map(),
|
||||
litexec: AttrLiteral,
|
||||
access: AccessReadOnly,
|
||||
};
|
||||
|
||||
const defineOperator = (
|
||||
name: string,
|
||||
implementation: (state: PSExecState) => void
|
||||
) => {
|
||||
systemdict.values.set(newName(name), {
|
||||
type: TypeOperator,
|
||||
litexec: AttrExecutable,
|
||||
access: AccessReadOnly,
|
||||
implementation,
|
||||
});
|
||||
};
|
||||
|
||||
// helpers
|
||||
const popOperand = (state: PSExecState): PsObject => {
|
||||
const value = state.operandStack.pop();
|
||||
if (!value) {
|
||||
throw new Error("error: stackunderflow");
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
defineOperator("add", (state) => {
|
||||
const b = popOperand(state);
|
||||
const a = popOperand(state);
|
||||
if (a.type === TypeInteger && b.type === TypeInteger) {
|
||||
state.operandStack.push({
|
||||
type: TypeInteger,
|
||||
litexec: AttrLiteral,
|
||||
access: AccessUnlimited,
|
||||
value: a.value + b.value,
|
||||
});
|
||||
}
|
||||
});
|
||||
defineOperator("def", (state) => {
|
||||
const value = popOperand(state);
|
||||
const key = popOperand(state);
|
||||
const topDict = state.dictionaryStack.at(-1)!;
|
||||
let name: Name;
|
||||
if (key.type === TypeName) {
|
||||
name = key.name;
|
||||
} else {
|
||||
throw new Error("todo: other def entries");
|
||||
}
|
||||
topDict.values.set(name, value);
|
||||
});
|
||||
|
||||
const globaldict: PsObjectDictionary = {
|
||||
type: TypeDictionary,
|
||||
values: new Map(),
|
||||
litexec: AttrLiteral,
|
||||
access: AccessUnlimited,
|
||||
};
|
||||
const userdict: PsObjectDictionary = {
|
||||
type: TypeDictionary,
|
||||
values: new Map(),
|
||||
litexec: AttrLiteral,
|
||||
access: AccessUnlimited,
|
||||
};
|
||||
|
||||
return {
|
||||
operandStack: [],
|
||||
dictionaryStack: [systemdict, globaldict, userdict],
|
||||
executionStack: [],
|
||||
};
|
||||
};
|
||||
|
||||
const lookupDictionaryStack = (
|
||||
stack: Stack<PsObjectDictionary>,
|
||||
name: Name
|
||||
): PsObject | undefined => {
|
||||
for (let i = stack.length - 1; i >= 0; i--) {
|
||||
const dict = stack[i];
|
||||
const value = dict?.values.get(name);
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const executeObject = (state: PSExecState, object: PsObject) => {
|
||||
console.log(object);
|
||||
|
||||
switch (object.type) {
|
||||
case TypeInteger: {
|
||||
state.operandStack.push(object);
|
||||
break;
|
||||
}
|
||||
case TypeName: {
|
||||
if (object.litexec === AttrLiteral) {
|
||||
state.operandStack.push(object);
|
||||
} else {
|
||||
const value = lookupDictionaryStack(state.dictionaryStack, object.name);
|
||||
if (!value) {
|
||||
throw new Error(`error: 'undefined' '${nameString(object.name)}'`);
|
||||
}
|
||||
executeObject(state, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TypeOperator: {
|
||||
if (object.litexec === AttrLiteral) {
|
||||
state.operandStack.push(object);
|
||||
} else {
|
||||
object.implementation(state);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TypeNull: {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new Error(
|
||||
`cannot execute ${object.type.description}: ${JSON.stringify(object)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const execute = (state: PSExecState, input: Generator<PsObject>) => {
|
||||
for (const object of input) {
|
||||
executeObject(state, object);
|
||||
}
|
||||
};
|
||||
|
||||
// yeet
|
||||
|
||||
const main = () => {
|
||||
const program = `
|
||||
% meow
|
||||
/fourtytwo 42 def
|
||||
1 fourtytwo add
|
||||
`;
|
||||
|
||||
const f = file(program);
|
||||
const scanner = scan(f);
|
||||
|
||||
const state = initExecState();
|
||||
execute(state, scanner);
|
||||
console.log(state.operandStack);
|
||||
};
|
||||
|
||||
main();
|
||||
|
|
@ -25,12 +25,9 @@
|
|||
"exactOptionalPropertyTypes": true,
|
||||
|
||||
// Style Options
|
||||
// "noImplicitReturns": true,
|
||||
// "noImplicitOverride": true,
|
||||
// "noUnusedLocals": true,
|
||||
// "noUnusedParameters": true,
|
||||
// "noFallthroughCasesInSwitch": true,
|
||||
// "noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
// Recommended Options
|
||||
"strict": true,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue