diff options
| author | Kai Stevenson <kai@kaistevenson.com> | 2025-10-28 22:49:46 -0700 |
|---|---|---|
| committer | Kai Stevenson <kai@kaistevenson.com> | 2025-10-28 22:49:46 -0700 |
| commit | 8896b33a652563d90cb211a85dd2f7213f42e5d1 (patch) | |
| tree | e9adf51a2ccf91e58f5e8edae4d3d6a4bb03e986 | |
parse, lex works
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | package-lock.json | 518 | ||||
| -rw-r--r-- | package.json | 5 | ||||
| -rw-r--r-- | src/index.ts | 1 | ||||
| -rw-r--r-- | src/lib/core/common.ts | 45 | ||||
| -rw-r--r-- | src/lib/core/index.ts | 0 | ||||
| -rw-r--r-- | src/lib/core/lexer.ts | 64 | ||||
| -rw-r--r-- | src/lib/core/parser.ts | 212 | ||||
| -rw-r--r-- | src/lib/core/repl.ts | 3 | ||||
| -rw-r--r-- | src/lib/index.ts | 1 |
10 files changed, 851 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f32e4b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode +node_modules
\ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5847fe9 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,518 @@ +{ + "name": "kai-script", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "tsx": "^4.19.2" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/tsx": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c55e97a --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "tsx": "^4.19.2" + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..8cd5167 --- /dev/null +++ b/src/index.ts @@ -0,0 +1 @@ +export * from "./lib"; diff --git a/src/lib/core/common.ts b/src/lib/core/common.ts new file mode 100644 index 0000000..1283000 --- /dev/null +++ b/src/lib/core/common.ts @@ -0,0 +1,45 @@ +export enum TokenType { + OPEN_PAREN = "(", + CLOSE_PAREN = ")", + SPACE = " ", + SEMICOLON = ";", + COMMA = ",", + UNIQUE_SYMBOL = "UNIQUE_SYMBOL", +} + +export type Token< + Type extends TokenType = TokenType, + Name extends string = string +> = { + type: Type; + name: Name; +}; + +export type LexerCtx = { + next: string; + nameCollection: string; + tokens: readonly Token[]; +}; + +export enum NodeType { + ROOT = "ROOT", + LITERAL = "LITERAL", + CALL = "CALL", + PARSER_ERROR = "PARSER_ERROR", +} + +export type ASTNode< + Type extends NodeType = NodeType, + Name extends string = string, + Children extends ASTNode[] = ASTNode<NodeType, string, any>[] +> = { + type: Type; + name: Name; + children: Children; +}; + +export type ParserCtx = { + remainingTokens: readonly Token[]; + lastName: string | null; + stack: readonly ASTNode[]; +}; diff --git a/src/lib/core/index.ts b/src/lib/core/index.ts new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/lib/core/index.ts diff --git a/src/lib/core/lexer.ts b/src/lib/core/lexer.ts new file mode 100644 index 0000000..3315fb2 --- /dev/null +++ b/src/lib/core/lexer.ts @@ -0,0 +1,64 @@ +import { LexerCtx, Token, TokenType } from "./common"; + +export type BreakingToken = + | TokenType.OPEN_PAREN + | TokenType.CLOSE_PAREN + | TokenType.COMMA + | TokenType.SEMICOLON + | TokenType.SPACE; + +export type IsWhitespace<T> = T extends `${TokenType.SPACE}` + ? true + : T extends `${TokenType.COMMA}` + ? true + : T extends `${TokenType.SEMICOLON}` + ? true + : false; + +export type ProcessNameCollection< + Cur extends LexerCtx, + Tail extends string, + _Token extends Token | null +> = { + next: Tail; + nameCollection: ""; + tokens: _Token extends null + ? [ + ...Cur["tokens"], + ...(Cur["nameCollection"] extends "" + ? [] + : [Token<TokenType.UNIQUE_SYMBOL, Cur["nameCollection"]>]) + ] + : [ + ...Cur["tokens"], + ...(Cur["nameCollection"] extends "" + ? [_Token] + : [Token<TokenType.UNIQUE_SYMBOL, Cur["nameCollection"]>, _Token]) + ]; +}; + +export type IsOpen<T> = T extends `${TokenType.OPEN_PAREN}` ? true : false; +export type IsClose<T> = T extends `${TokenType.CLOSE_PAREN}` ? true : false; + +export type _Lex<Ctx extends LexerCtx> = + Ctx["next"] extends `${infer Head}${infer Tail}` + ? IsWhitespace<Head> extends true + ? _Lex<ProcessNameCollection<Ctx, Tail, null>> + : IsOpen<Head> extends true + ? _Lex<ProcessNameCollection<Ctx, Tail, Token<TokenType.OPEN_PAREN>>> + : IsClose<Head> extends true + ? _Lex<ProcessNameCollection<Ctx, Tail, Token<TokenType.CLOSE_PAREN>>> + : _Lex<{ + next: Tail; + nameCollection: `${Ctx["nameCollection"]}${Head}`; + tokens: Ctx["tokens"]; + }> + : // : Ctx["next"] extends `${infer Head}` + // ? _Lex<{ next: Head; tokens: Ctx["tokens"] }> + Ctx["tokens"]; + +export type Lex<Raw extends string> = _Lex<{ + next: `${Raw};`; + tokens: []; + nameCollection: ""; +}>; diff --git a/src/lib/core/parser.ts b/src/lib/core/parser.ts new file mode 100644 index 0000000..fbbbdc2 --- /dev/null +++ b/src/lib/core/parser.ts @@ -0,0 +1,212 @@ +import { ASTNode, NodeType, ParserCtx, Token, TokenType } from "./common"; +import { IsWhitespace, Lex } from "./lexer"; + +/* +start +if no 'lastName' +then: + expect nextToken to be a name + lastName = nextToken + goto start + +else: + if nextToken is name + then: + // we already have a lastName + mutate last element of stack to push lastName as child + lastName = nextToken + goto start + + else: + //nextToken is openParen or close paren + if nextToken is closeParen + then: + set last element of stack as child of prev element on stack + pop stack + // [stack[last - 1].children.push(stack.pop) + goto start + else if nextToken is openParen: + push lastName onto stack + goto start + + +finally: + // only one element remains on the stack + return stack[0] + + + CALL ( param, CALL2 ( param2 ) ) + + param2 ret call2 param ret call + + | call + |-- param + |-- | call2 + |-- param2 + + */ + +export type Error<T extends string> = ASTNode<NodeType.PARSER_ERROR, T, []>; + +export type PushChild<Node extends ASTNode, Child extends ASTNode> = { + type: Node["type"]; + name: Node["name"]; + children: [...Node["children"], Child]; +}; + +export type PushChildToLastElementOfStack< + Stack extends ParserCtx["stack"], + Child extends ASTNode +> = Stack extends [...infer Head, infer Tail extends ASTNode] + ? [...Head, PushChild<Tail, Child>] + : Stack extends [infer Only extends ASTNode] + ? [PushChild<Only, Child>] + : never; + +export type PushChildToSecondLastElementOfStack< + Stack extends ParserCtx["stack"], + Child extends ASTNode +> = Stack extends [ + ...infer Head, + infer Tail extends ASTNode, + infer Final extends ASTNode +] + ? [...Head, PushChild<Tail, Child>, Final] + : Stack extends [infer Only extends ASTNode, infer Final extends ASTNode] + ? [PushChild<Only, Child>, Final] + : never; + +export type GetLastOnStack<Stack extends ParserCtx["stack"]> = Stack extends [ + ...infer Head, + infer Tail extends ASTNode +] + ? Tail + : Stack extends [infer Only extends ASTNode] + ? Only + : never; + +export type StackWithoutLast<Stack extends ParserCtx["stack"]> = Stack extends [ + ...infer Head extends ASTNode[], + infer Tail +] + ? [...Head] + : Stack extends [infer Only extends ASTNode] + ? [] + : never; + +export type _Parse<Ctx extends ParserCtx> = Ctx["remainingTokens"] extends [ + infer Head extends Token, + ...infer Tail extends readonly Token[] +] + ? Ctx["lastName"] extends string + ? Head["type"] extends TokenType.UNIQUE_SYMBOL + ? // we already have a lastName + // mutate last element of stack to push lastName as child + // lastName = nextToken + // goto start + _Parse<{ + lastName: Head["name"]; + remainingTokens: Tail; + stack: PushChildToLastElementOfStack< + Ctx["stack"], + ASTNode<NodeType.LITERAL, Ctx["lastName"], []> + >; + }> + : //nextToken is openParen or close paren + Head["type"] extends TokenType.CLOSE_PAREN + ? // handle lastName + // set last element of stack as child of prev element on stack + // pop stack + // [stack[last - 1].children.push(stack.pop) + // goto start + _Parse<{ + lastName: null; + remainingTokens: Tail; + // first push the last name onto the children of the top + // then push the top onto the children of the next + // then remove the top + stack: StackWithoutLast< + PushChildToSecondLastElementOfStack< + Ctx["stack"], + PushChild< + GetLastOnStack<Ctx["stack"]>, + ASTNode<NodeType.LITERAL, Ctx["lastName"], []> + > + > + >; + }> + : Head["type"] extends TokenType.OPEN_PAREN + ? // push lastName onto stack + // goto start + _Parse<{ + lastName: null; + remainingTokens: Tail; + stack: [...Ctx["stack"], ASTNode<NodeType.CALL, Ctx["lastName"], []>]; + }> + : Ctx & Error<`Was not expecting ${Head["type"]}`> + : // expect nextToken to be a name or close paren + Head["type"] extends TokenType.UNIQUE_SYMBOL + ? // lastName = nextToken + // goto start + _Parse<{ + lastName: Head["name"]; + remainingTokens: Tail; + stack: Ctx["stack"]; + }> + : Head["type"] extends TokenType.CLOSE_PAREN + ? _Parse<{ + lastName: null; + remainingTokens: Tail; + // push the top onto the children of the next + // then remove the top + stack: StackWithoutLast< + PushChildToSecondLastElementOfStack< + Ctx["stack"], + GetLastOnStack<Ctx["stack"]> + > + >; + }> + : Ctx & + Error<`Expected nextToken to be a name or close paren at ${Head["type"]}`> + : Ctx["stack"]; + +// v1 +// ? Ctx["isCollecting"] extends true +// ? Head["type"] extends TokenType.CLOSE_PAREN +// ? "return" +// : Head["type"] extends TokenType.UNIQUE_SYMBOL +// ? _Parse<{ +// remainingTokens: Tail; +// isCollecting: true; +// name: Ctx["name"]; +// // fuck, how to do this without advancing seek pointer?? +// ret: [...Ctx["ret"], ASTNode; +// }> +// : Error<"Expected another name, or `)`"> +// : Head["type"] extends TokenType.OPEN_PAREN +// ? _Parse<{ +// remainingTokens: Tail; +// isCollecting: true; +// name: Ctx["name"]; +// ret: Ctx["ret"]; +// }> +// : Error<"Expected open paren"> +// : Ctx["ret"]; + +//v2 +// ? Ctx["previousName"] extends null +// ? Head["type"] extends TokenType.UNIQUE_SYMBOL +// ? _Parse<{ remainingTokens: Tail; previousName: Head; node: Ctx["node"] }> +// : ASTNode<NodeType.PARSER_ERROR, "Expected name", []> +// : IsWhitespace<`${Head["type"]}`> extends true +// ? ASTNode<NodeType.PARSER_ERROR, "1", []> +// : ASTNode<NodeType.PARSER_ERROR, "2", []> +// : Ctx["node"]; + +export type Parse<Raw extends readonly Token[]> = _Parse<{ + lastName: null; + remainingTokens: Raw; + stack: [ASTNode<NodeType.ROOT, "ROOT", []>]; +}>; + +const a = "a" as any as Parse<Lex<"test(a)">>; diff --git a/src/lib/core/repl.ts b/src/lib/core/repl.ts new file mode 100644 index 0000000..5235a64 --- /dev/null +++ b/src/lib/core/repl.ts @@ -0,0 +1,3 @@ +import { ASTNode } from "./common"; + +export type Evaluate<Node extends ASTNode> = "Not implemented"; diff --git a/src/lib/index.ts b/src/lib/index.ts new file mode 100644 index 0000000..8d119de --- /dev/null +++ b/src/lib/index.ts @@ -0,0 +1 @@ +export * from "./core"; |
