summaryrefslogtreecommitdiff
path: root/src/js-lang/core/parser.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/js-lang/core/parser.ts')
-rw-r--r--src/js-lang/core/parser.ts132
1 files changed, 132 insertions, 0 deletions
diff --git a/src/js-lang/core/parser.ts b/src/js-lang/core/parser.ts
new file mode 100644
index 0000000..f193d6a
--- /dev/null
+++ b/src/js-lang/core/parser.ts
@@ -0,0 +1,132 @@
+import { Token, Parse, TokenType, ASTNode, NodeType } from "../../ts-lang";
+import { lex } from "./lexer";
+
+const resolveNodeFromToken = (token: Token): ASTNode => {
+ // FIXME not correct
+ if (!isNaN(Number(token.name))) {
+ return {
+ type: NodeType.INT,
+ name: "",
+ value: Number(token.name),
+ children: [],
+ };
+ }
+ if (token.name[0] === '"' && token.name[token.name.length - 1] === '"') {
+ return {
+ type: NodeType.INT,
+ name: "",
+ value: token.name.slice(1, token.name.length - 1),
+ children: [],
+ };
+ }
+ return {
+ type: NodeType.EXT,
+ name: token.name,
+ value: null,
+ children: [],
+ };
+};
+
+const _parse = ({
+ remainingTokens,
+ lastToken,
+ stack,
+}: {
+ remainingTokens: Token[];
+ lastToken: Token | null;
+ stack: ASTNode[];
+}): ASTNode | null => {
+ const head = remainingTokens.shift();
+ if (!head) {
+ if (lastToken) {
+ (stack[stack.length - 1].children as ASTNode[]).push(
+ resolveNodeFromToken(lastToken)
+ );
+
+ return _parse({
+ lastToken: null,
+ remainingTokens: [],
+ stack,
+ });
+ }
+
+ return stack[0] ?? null;
+ }
+
+ if (lastToken) {
+ if (head.type === TokenType.NAME) {
+ (stack[stack.length - 1].children as ASTNode[]).push(
+ resolveNodeFromToken(lastToken)
+ );
+ return _parse({
+ lastToken: head,
+ remainingTokens,
+ stack,
+ });
+ }
+
+ if (head.type === TokenType.CLOSE_PAREN) {
+ const top = stack.pop()!;
+ (top.children as ASTNode[]).push(resolveNodeFromToken(lastToken));
+ (stack[stack.length - 1].children as ASTNode[]).push(top);
+
+ return _parse({
+ lastToken: null,
+ remainingTokens,
+ stack,
+ });
+ }
+
+ if (head.type === TokenType.OPEN_PAREN) {
+ return _parse({
+ lastToken: null,
+ remainingTokens,
+ stack: [...stack, resolveNodeFromToken(lastToken)],
+ });
+ }
+
+ throw new Error(
+ `${JSON.stringify({
+ lastToken,
+ remainingTokens,
+ stack,
+ })} Was not expecting ${head.type}`
+ );
+ }
+
+ if (head.type === TokenType.NAME) {
+ return _parse({
+ lastToken: head,
+ remainingTokens,
+ stack,
+ });
+ }
+
+ if (head.type === TokenType.CLOSE_PAREN) {
+ const top = stack.pop()!;
+ (stack[stack.length - 1].children as ASTNode[]).push(top);
+
+ return _parse({
+ lastToken: null,
+ remainingTokens,
+ stack,
+ });
+ }
+
+ throw new Error(
+ `${JSON.stringify({
+ lastToken,
+ remainingTokens,
+ stack,
+ })} Expected nextToken to be a name or close paren at ${head.type}`
+ );
+};
+
+export const parse = <const Raw extends readonly Token[]>(
+ raw: Raw
+): Parse<Raw> =>
+ _parse({
+ remainingTokens: raw as unknown as Token[],
+ lastToken: null,
+ stack: [{ type: NodeType.EXT, name: "arr", value: null, children: [] }],
+ }) as Parse<Raw>;