This commit is contained in:
Michael Zhang 2024-09-14 01:39:00 -05:00
parent e65f457530
commit 8942ee5638
7 changed files with 178 additions and 112 deletions

35
biome.json Normal file
View file

@ -0,0 +1,35 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
"vcs": {
"enabled": false,
"clientKind": "git",
"useIgnoreFile": false
},
"files": {
"ignoreUnknown": false,
"ignore": []
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 240
},
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"javascript": {
"formatter": {
"quoteStyle": "double",
"semicolons": "always",
"trailingCommas": "all",
"bracketSameLine": true
}
}
}

BIN
bun.lockb

Binary file not shown.

View file

@ -3,9 +3,13 @@
"module": "main.ts",
"type": "module",
"devDependencies": {
"@biomejs/biome": "^1.9.0",
"@types/bun": "latest"
},
"peerDependencies": {
"typescript": "^5.0.0"
}
},
"trustedDependencies": [
"@biomejs/biome"
]
}

1
src/codegen_x86.ts Normal file
View file

@ -0,0 +1 @@
export function codegen_x86() {}

View file

@ -1,12 +1,16 @@
import { parseArgs } from "node:util";
import { parseProgram } from "./parser";
// Main
async function main() {
const filename = Bun.argv[2];
const contents = await Bun.file(filename).text()
const programAst = parseProgram(contents);
const args = parseArgs({
args: Bun.argv,
strict: true,
allowPositionals: true,
});
const filename = args.positionals[2];
const contents = await Bun.file(filename).text();
const programAst = parseProgram(contents);
}
main();

View file

@ -1,136 +1,157 @@
// 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}`}
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 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 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) => {
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)}
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 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)
if (acc.status === "ok") return acc;
return next(i);
}, err("nothing matched"));
if (res.status === "err") return err("failed alt")
if (res.status === "err") return err("failed alt");
return res;
};
};
// whitespace
const __: Parser = re(/\s+/)
const _: Parser = re(/\s*/)
const __: Parser = re(/\s+/);
const _: Parser = re(/\s*/);
// Grammar
const ident: Parser = re(/(\_[A-Za-z0-9_]+)|([A-Za-z][A-Za-z0-9_]*)/);
const kwd = (s: string) => (i: string) => {
const res = ident(i)
if (res.status === "err") return wrapErr(`expected ${s}`, res)
if (s !== res.value) return err(`expected ${s}`)
return res
}
const res = ident(i);
if (res.status === "err") return wrapErr(`expected ${s}`, res);
if (s !== res.value) return err(`expected ${s}`);
return res;
};
const ty: Parser = alt(
kwd("uint"), kwd("int"), kwd("ptr"),
kwd("str"), ident,
)
const ty: Parser = alt(kwd("uint"), kwd("int"), kwd("ptr"), kwd("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})),
)
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)
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)
export const expr: Parser = expr1
map(seq(expr2, _, re(/==/), _, expr2), ([left, , , , right]) => ({ expr: "==", left, right })),
expr2,
);
export const expr: Parser = expr1;
export const top: Parser = alt(
map(seq(kwd("extern"), _, ident), (([, name]) => ({type: "extern", name}))),
seq(kwd("struct"), _, ident, _, re(/:/)),
map(seq(kwd("fn"), _, ident, _, re(/:/)), (([,, name]) => ({type: "func", name}))),
)
map(seq(kwd("extern"), _, ident), ([, name]) => ({ type: "extern", name })),
seq(kwd("struct"), _, ident, _, re(/:/)),
map(seq(kwd("fn"), _, ident, _, re(/:/)), ([, , name]) => ({ type: "func", name })),
);
export const stmt: Parser = alt(
map(seq(kwd("let"), _, ident, _, re(/=/), _, (i) => expr(i)),
([,, name,,,, value]) => ({stmt: "let",name, value})),
map(seq(kwd("if"), _, (i) => expr(i), _, re(/:/)), ([,, cond]) => ({stmt: "if", cond})),
map((i) => expr(i), (expr) => ({stmt:"expr", expr})),
)
map(
seq(kwd("let"), _, ident, _, re(/=/), _, (i) => expr(i)),
([, , name, , , , value]) => ({ stmt: "let", name, value }),
),
map(
seq(kwd("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[]
structs: object[];
functions: object[];
}
export function parseProgram(input: string): Program {
let currentFunc: string | null = null;
let indentStack = [0];
let expectIndent = false;
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 ?? "";
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)
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))
}
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

View file

@ -1,8 +1,9 @@
import {test,expect} from "bun:test"
import { stmt } from "../src/parser"
import { test, expect } from "bun:test";
import { stmt } from "../src/parser";
test("ifx", () => expect(stmt("ifx")).toMatchObject({status:"ok", value: {expr: {}}, remain: ""}))
test("ifx:", () => expect(stmt("ifx:")).toMatchObject({status:"ok", value: {expr: {}}, remain: ":"}))
test("if x:", () => expect(stmt("if x:")).toMatchObject({status:"ok", value: {stmt: "if"}, remain: ""}))
test("if(x):", () => expect(stmt("if(x):")).toMatchObject({status:"ok", value: {stmt: "if"}, remain: ""}))
test("let let = let", () => expect(stmt("let let = let")).toMatchObject({status:"ok", value: {stmt: "let"}, remain: ""}))
test("ifx", () => expect(stmt("ifx")).toMatchObject({ status: "ok", value: { expr: {} }, remain: "" }));
test("ifx:", () => expect(stmt("ifx:")).toMatchObject({ status: "ok", value: { expr: {} }, remain: ":" }));
test("if x:", () => expect(stmt("if x:")).toMatchObject({ status: "ok", value: { stmt: "if" }, remain: "" }));
test("if(x):", () => expect(stmt("if(x):")).toMatchObject({ status: "ok", value: { stmt: "if" }, remain: "" }));
// test("let let = let", () => expect(stmt("let let = let")).toMatchObject({status:"ok", value: {stmt: "let"}, remain: ""}))