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("; ")); }