initial
This commit is contained in:
commit
78a29ce471
7 changed files with 371 additions and 0 deletions
175
.gitignore
vendored
Normal file
175
.gitignore
vendored
Normal file
|
@ -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
|
15
README.md
Normal file
15
README.md
Normal file
|
@ -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.
|
BIN
bun.lockb
Normal file
BIN
bun.lockb
Normal file
Binary file not shown.
6
examples/basic.ciad
Normal file
6
examples/basic.ciad
Normal file
|
@ -0,0 +1,6 @@
|
|||
fn main:
|
||||
let x = 1
|
||||
if x > 1:
|
||||
print("hello, world")
|
||||
|
||||
fn other:
|
137
main.ts
Normal file
137
main.ts
Normal file
|
@ -0,0 +1,137 @@
|
|||
// Parsing combinators
|
||||
const ok = <T>(r: T, e: string): Result<T> => ({ status: "ok", value: r, remain: e });
|
||||
const err = <T, E>(m: string, e?: E): Result<T> => ({ status: "err", msg: m, data: e });
|
||||
const wrapErr = <T>(e: string, r: Result<T>): Result<T> => r.status === "ok" ? r : { ...r, msg: `${e}: ${r.msg}`}
|
||||
|
||||
type Result<T> = {status:"ok",value:T, remain:string} | {status:"err", msg: string, data?: unknown}
|
||||
type Parser<T = unknown> = (i: string) => Result<T>;
|
||||
|
||||
const re = (r: RegExp): Parser<string> => (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 = <T, U>(p: Parser<T>, f: (_: T) => U): Parser<U> => (i: string) => {
|
||||
const res = p(i);
|
||||
if (res.status === "ok") return {...res, value: f(res.value)}
|
||||
else return res;
|
||||
};
|
||||
const opt = <T>(p: Parser<T>): Parser<T|null> => (i: string) => p(i).status === "ok" ? p(i) : ok(null,i);
|
||||
const seq = (...parsers) => (i: string) => parsers.reduce((acc: Result<unknown[]>, 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<unknown>, 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(/</), _, exprL), ([left,,,,right]) => ({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 = /^(?<space>\s*)(?<rest>.*)$/.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();
|
11
package.json
Normal file
11
package.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "ciad",
|
||||
"module": "main.ts",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
}
|
27
tsconfig.json
Normal file
27
tsconfig.json
Normal file
|
@ -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
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue