Skip to content

Instantly share code, notes, and snippets.

@marcj
Created September 3, 2021 00:33
Show Gist options
  • Save marcj/a568cd83ea678bd4cd28034a1b2d8e14 to your computer and use it in GitHub Desktop.
Save marcj/a568cd83ea678bd4cd28034a1b2d8e14 to your computer and use it in GitHub Desktop.
stack based scripting language for runtime TypeScript reflection and validation
enum OP {
string = 0,
number = 1,
boolean = 2,
object = 3,
function = 4,
array = 5,
date = 6,
class = 7,
interface = 8,
void = 9,
//modifier
optional = 10,
name = 11,
private = 12,
protected = 13,
}
function extractByteCode(object: object): (OP | string)[] {
const str = object.toString() as any as (OP | string)[];
//poor man's pattern matching of a hex number.
let i = str.indexOf('\'0x') + 3;
const opCodes: (number | string)[] = [];
while (true) {
if (str[i] === undefined) break;
if (str[i] === '\'') break;
if (str[i] === ' ') {
i++;
continue;
}
const s = '0x' + str[i++] + str[i++];
const type = Number(s);
opCodes.push(type);
if (type === OP.name) {
//read string until null byte
let name = '';
//only ascii for the moment
while (true) {
if (str[i] === '0' && str[i + 1] === '0') break;
name += str[i++];
}
i += 2;
opCodes.push(name);
}
}
return opCodes;
}
function getSignature(object: object) {
return getSignatureFromByteCodes(extractByteCode(object));
}
function getTypescript(object: object) {
const signature = getSignature(object);
if (signature.rootType === OP.class) {
const lines: string[] = [`class ${(object as any).name} {`];
for (const p of signature.properties) {
lines.push(
` ` +
(p.private ? 'private ' : 'public ') +
p.name + ': ' + p.type + ';'
);
}
return lines.join('\n') + '\n}';
}
if (signature.rootType === OP.function) {
const lines: string[] = [];
for (const p of signature.properties) {
lines.push(p.type);
}
const last = lines.pop();
return `function ${(object as any).name}(` + lines.join(',') + '): ' + last;
}
}
function getSignatureFromByteCodes(ops: (OP | string)[]): any {
const rootType = ops[0];
const struct: any = { rootType: rootType, properties: [] };
let i = 1;
const stack: any[] = [];
do {
const t: any = {};
const type = ops[i];
if (type === undefined) break;
t.type = OP[type as any];
struct.properties.push(t);
//read modifier
outer:
while (true) {
const modifier = ops[i + 1];
switch (modifier) {
case OP.optional: {
i++;
t.optional = true;
break;
}
case OP.private: {
i++;
t.private = true;
break;
}
case OP.name: {
i++;
t.name = ops[++i];
break;
}
default: {
break outer;
}
}
}
} while (i++ < ops.length);
return struct;
}
test('asd', () => {
function trim(text: string): string {
'0x040000';
return text.trim();
}
class User {
public id: number = 0;
constructor(private name: string) {
'0x07 000c0bname00 010bid00';
}
}
console.log(trim.toString());
console.log(extractByteCode(User));
console.log(getSignature(User));
console.log(getSignature(trim));
console.log(getTypescript(trim));
console.log(getTypescript(User));
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment