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",
|
"name": "println",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "dist/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"watch": "pnpm '/watch:*/'",
|
||||||
|
"watch:build": "tsc --watch",
|
||||||
|
"watch:run": "node --watch .",
|
||||||
|
"dev": "node .",
|
||||||
"build": "tsc"
|
"build": "tsc"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"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,
|
"exactOptionalPropertyTypes": true,
|
||||||
|
|
||||||
// Style Options
|
// Style Options
|
||||||
// "noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
// "noImplicitOverride": true,
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
// "noUnusedLocals": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
// "noUnusedParameters": true,
|
|
||||||
// "noFallthroughCasesInSwitch": true,
|
|
||||||
// "noPropertyAccessFromIndexSignature": true,
|
|
||||||
|
|
||||||
// Recommended Options
|
// Recommended Options
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue