From 78a29ce471be0bfceece3987bf56e5e88f854d90 Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Sat, 14 Sep 2024 00:51:42 -0500 Subject: [PATCH] initial --- .gitignore | 175 ++++++++++++++++++++++++++++++++++++++++++++ README.md | 15 ++++ bun.lockb | Bin 0 -> 3124 bytes examples/basic.ciad | 6 ++ main.ts | 137 ++++++++++++++++++++++++++++++++++ package.json | 11 +++ tsconfig.json | 27 +++++++ 7 files changed, 371 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 bun.lockb create mode 100644 examples/basic.ciad create mode 100644 main.ts create mode 100644 package.json create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9b1ee42 --- /dev/null +++ b/.gitignore @@ -0,0 +1,175 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..9062a29 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# ciad + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run main.ts +``` + +This project was created using `bun init` in bun v1.1.20. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/bun.lockb b/bun.lockb new file mode 100644 index 0000000000000000000000000000000000000000..cafc2098f4f837ebd22fd753f1bd9becd692711e GIT binary patch literal 3124 zcmY#Z)GsYA(of3F(@)JSQ%EY!;{sycoc!eMw9K4T-L(9o+{6;yG6OCq1_p-nUDk}! z1+FWjcD;8{dEc_g^I^iTbtk<=(!I>>T8riwFX09%0s?jj#lV3^H$eH>Fa-?BnTaVN zK`sbx@C4GF3=9p^fHcsshTA}z2T1oq#eoz^Z9~ngM^`VXGaov|rn%SKpz%xfoAk3C zLa&u3>PPjeZMC)J%(prADsa~B#iwUf&d<7(IbXFwN4qsOi4kM~1ppKZARq$7ra*Uq z%mZPdn~4H2{Xsz0)=2uLiPFu$0Mow^sNM>wALI^T_%QI1sQ)=oy(3URE0C4~Vh~LX zfZ4AB^t1_3KPZMldO>a=2Eg=Z0QK8I?PmjOgozVt21q^72nJ@TUXU9=e2`fn0HQ&B zctQiRL3&8n_mBS{$RKJ0pm~f85Oa}@X7MZvtTKH$gWY_!Oz+>89e*ZH6E?d4@WR3D z>8G~}JX*?ne8qK{9Y-w$-Ix}&p7lGkRNCR$fe;-jMrL_2*WU%N+>y*h4o{Xfr3X41 z?$tgP`uj7^04D`?{td-JZ;jKm(1S$9g7 z95TC5TyM9!BttRK;3(t7!^L*}Gcrw>?WVDo7e;B%LNb>HW+;$mQ48p?e!j58iECA0 z;p9c~VGSZ-8OCpqTA#6h__eHLwfTyTiq8V3`khtL-`g@HvXV7MzoTZ;B*pI!_j`+` zy{cM*WG-^LX1RCy;-tSW+Z=l1L?m-z=?vs-7SnvjQ|(S37dE86Q0%$;;>XmV z7J*-^EUY#zPZn5a(e|wJrqyg!qjbK$I)DC}sXDvvT+8q7{8w$4cI#d2gP)S8f#wbr zfQ37ZCdVBE=zMaVJ5YKyK+B>dP@2o8xF}gKGq1QLF(*e4R^#f06s4xxDHs_j6lYeY z=BMc>m?$LXWTxlkr-AB75cu~W0zhm~I{W}NkONxXa@mv`8`%N%;V=OdU!d{?qo{!EN@7J8<7h6W6< zb_J}R0F*H@&@(jBvw*cUU@ZrrjH#ZHnVtz2Jz79tfI|V~TAXvZyp`d zV5UB>)t6CHQc!HAuV0j!o>^Q{RH>I&kegMkmtT~wk5Htq3sR~J3TaEdlJqJN2W$c! z-EcKf&0zIlBaF;JW5dr7>A?^Xdp 1: + print("hello, world") + +fn other: \ No newline at end of file diff --git a/main.ts b/main.ts new file mode 100644 index 0000000..b883a31 --- /dev/null +++ b/main.ts @@ -0,0 +1,137 @@ +// Parsing combinators +const ok = (r: T, e: string): Result => ({ status: "ok", value: r, remain: e }); +const err = (m: string, e?: E): Result => ({ status: "err", msg: m, data: e }); +const wrapErr = (e: string, r: Result): Result => r.status === "ok" ? r : { ...r, msg: `${e}: ${r.msg}`} + +type Result = {status:"ok",value:T, remain:string} | {status:"err", msg: string, data?: unknown} +type Parser = (i: string) => Result; + +const re = (r: RegExp): Parser => (i: string) => { + const r2 = r.source.startsWith("^") ? r : new RegExp(`^${r.source}`) + const m = r2.exec(i); + if (!m) return err("failed to match " + r.source) + return ok(m[0], i.slice(m[0].length)) +}; +const map = (p: Parser, f: (_: T) => U): Parser => (i: string) => { + const res = p(i); + if (res.status === "ok") return {...res, value: f(res.value)} + else return res; +}; +const opt = (p: Parser): Parser => (i: string) => p(i).status === "ok" ? p(i) : ok(null,i); +const seq = (...parsers) => (i: string) => parsers.reduce((acc: Result, next: Parser, idx) => { + if (acc.status === "err") return acc; + const res = next(acc.remain); + if (res.status === "err") return wrapErr(`failed seq #${idx}`, res); + return ok([...acc.value, res.value], res.remain) +}, ok([], i)); +const alt = (...parsers) => (i: string) => { + const res = parsers.reduce((acc: Result, next: Parser) => { + if (acc.status === "ok") return acc; + return next(i) + }, err("nothing matched")); + if (res.status === "err") return err("failed alt") + return res; +}; + +const _: Parser = re(/\s*/) // whitespace + +// Grammar +const ident: Parser = re(/(\_[A-Za-z0-9_]+)|([A-Za-z][A-Za-z0-9_]*)/); +const ty: Parser = alt( + re(/uint/), re(/int/), re(/ptr/), + re(/str/), ident, +) + +const exprL: Parser = alt( + seq(re(/\(/), _, (i) => expr(i), _, re(/\)/)), + map(re(/(\+|\-)?[0-9]+/), (n) => ({expr:"intLit", value:parseInt(n)})), + map(ident, (name) => ({expr:"ident", name})), +) +const expr2: Parser = alt( + map(seq(exprL, _, re(/ ({expr:"<", left, right})), + map(seq(exprL, _, re(/>/), _, exprL), ([left,,,,right]) => ({expr:">", left, right})), + exprL) +const expr1: Parser = alt( + map(seq(expr2, _, re(/==/), _, expr2), ([left,,,,right]) => ({expr: "==", left, right})), + expr2) +const expr: Parser = expr1 + +const top: Parser = alt( + map(seq(re(/extern/), _, ident), (([, name]) => ({type: "extern", name}))), + seq(re(/struct/), _, ident, _, re(/:/)), + map(seq(re(/fn/), _, ident, _, re(/:/)), (([,, name]) => ({type: "func", name}))), +) +const stmt: Parser = alt( + map(seq(re(/let/), _, ident, _, re(/=/), _, (i) => expr(i)), + ([,, name,,,, value]) => ({stmt: "let",name, value})), + map(seq(re(/if/), _, (i) => expr(i), _, re(/:/)), ([,, cond]) => ({stmt: "if", cond})), + map((i) => expr(i), (expr) => ({stmt:"expr", expr})), +) + + +// Parsing driver +interface Program { + structs: object[] + functions: object[] +} + +function parseProgram(input: string): Program { + let currentFunc: string | null = null; + let indentStack = [0]; + let expectIndent = false; + + for (const line of input.split(/\r?\n/)) { + const leadingWhitespace = /^(?\s*)(?.*)$/.exec(line) + const numSpaces = leadingWhitespace?.groups?.space?.length ?? 0; + const rest = leadingWhitespace?.groups?.rest ?? ""; + + console.log("stack", indentStack) + + const lastIndent = indentStack[indentStack.length - 1]; + if (numSpaces > lastIndent) { + if (expectIndent) { + indentStack.push(numSpaces) + expectIndent = false; + } else { + // Possible error? + } + } else if (numSpaces === lastIndent) { + if (expectIndent) { + console.log("empty block") + } + } else { + indentStack.pop(); + console.log("dedented") + } + + if (numSpaces === 0) { + // Parse top level + const result = opt(top)(rest) + console.log("top", JSON.stringify(result)) + if (result.status === "ok") { + if (result.value === null) continue; + switch(result.value.type) { + case "func": + currentFunc = result.value.name; + expectIndent = true; + break; + } + } + } else if (currentFunc) { + const result = stmt(rest); + console.log("stmt", JSON.stringify(rest), JSON.stringify(result)) + } + } +} + +// Codegen + +// Main +async function main() { + const filename = Bun.argv[2]; + const contents = await Bun.file(filename).text() + + const programAst = parseProgram(contents); +} + +main(); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..570ddcd --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "name": "ciad", + "module": "main.ts", + "type": "module", + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5.0.0" + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..238655f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +}