aiparser/references provided by Dave/test.ts
2025-02-04 16:55:57 -05:00

123 lines
4.3 KiB
TypeScript

class Token {
constructor(public type: string, public value: string) {}
}
class Lexer {
private position = 0;
private tokens: Token[] = [];
constructor(private input: string) {}
tokenize(): Token[] {
const regex = /([a-zA-Z_][a-zA-Z0-9_]*)|([0-9]+)|([+\-*%!@^\/\\])|([()\[\]{};,])/g;
let match;
while ((match = regex.exec(this.input)) !== null) {
if (match[1]) this.tokens.push(new Token("IDENTIFIER", match[1]));
else if (match[2]) this.tokens.push(new Token("NUMBER", match[2]));
else if (match[3]) this.tokens.push(new Token("OPERATOR", match[3]));
else if (match[4]) this.tokens.push(new Token("SYMBOL", match[4]));
}
return this.tokens;
}
}
class Parser {
private position = 0;
constructor(private tokens: Token[]) {}
parseExpressions(): any[] {
let expressions = [];
while (this.position < this.tokens.length) {
expressions.push(this.parseExpression());
}
return expressions;
}
parseExpression(): any {
let token = this.tokens[this.position];
if (!token) return null;
if (token.type === "IDENTIFIER") {
this.position++;
if (this.match("SYMBOL", "[")) {
return this.parseIndexingOrFunctionCall(token);
}
return token;
} else if (token.type === "NUMBER") {
this.position++;
return token;
} else if (token.type === "OPERATOR") {
return this.parseOperatorOrUnary();
}
throw new Error(`Unexpected token: ${token.value}`);
}
parseIndexingOrFunctionCall(identifier: Token): any {
let args = [];
this.position++; // Consume "["
while (!this.match("SYMBOL", "]") && !this.match("SYMBOL", "}")) {
let expr = this.parseExpression();
if (!expr) throw new Error("Expected expression inside indexing or function call");
args.push(expr);
if (this.match("SYMBOL", ";")) this.position++;
}
let closingSymbol = this.tokens[this.position].value;
this.position++; // Consume closing bracket or brace
return { type: closingSymbol === "]" ? "FunctionCall" : "Indexing", name: identifier.value, args };
}
parseOperatorOrUnary(): any {
let operator = this.tokens[this.position];
this.position++;
if (this.isUnaryOperator(operator.value)) {
let operand = this.parseExpression();
return { type: "UnaryExpression", operator: operator.value, operand };
}
let left = this.parseExpression();
let right = this.parseExpression();
// Reverse arguments for non-commutative operators
if (this.isNonCommutative(operator.value)) {
return { type: "BinaryExpression", operator: operator.value, left: right, right: left };
}
return { type: "BinaryExpression", operator: operator.value, left, right };
}
isUnaryOperator(op: string): boolean {
return ["!", "#", "_", "$"].includes(op);
}
isNonCommutative(op: string): boolean {
return ["-", "%", "!", "^"].includes(op);
}
generateNGNK(ast: any): string {
if (!ast) return "";
switch (ast.type) {
case "FunctionCall":
return `${ast.name}[${ast.args.map(this.generateNGNK).join(";")}]`;
case "Indexing":
return `${ast.name}[${ast.args.map(this.generateNGNK).join(";")}]`;
case "BinaryExpression":
return `${this.generateNGNK(ast.left)} ${ast.operator} ${this.generateNGNK(ast.right)}`;
case "UnaryExpression":
return `${this.generateNGNK(ast.operand)} ${ast.operator}`;
case "NUMBER":
case "IDENTIFIER":
return ast.value;
default:
return "";
}
}
}
// Example usage for Deno
if (import.meta.main) {
const input = "sum[1; 2; 3] + array{5} $[ x > y ; \"greater\" ; \"smaller\" ] while[ x < 10 ; x + 1 ] 'fold /+ [1;2;3] \\load \"file.pong\" x ! 15 # 20 _ \"abc\" $ 10 - 3 8 % 2 5 ! 2 3 ^ 4";
const lexer = new Lexer(input);
const tokens = lexer.tokenize();
const parser = new Parser(tokens);
const ast = parser.parseExpressions();
console.log(ast.map(parser.generateNGNK).join("; "));
}