init
This commit is contained in:
commit
181f237f4d
6
deno.jsonc
Normal file
6
deno.jsonc
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"fmt": {
|
||||
"indentWidth": 4
|
||||
}
|
||||
}
|
||||
|
||||
15
deno.lock
generated
Normal file
15
deno.lock
generated
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"version": "5",
|
||||
"specifiers": {
|
||||
"jsr:@std/cli@*": "1.0.24",
|
||||
"jsr:@std/yaml@*": "1.0.10"
|
||||
},
|
||||
"jsr": {
|
||||
"@std/cli@1.0.24": {
|
||||
"integrity": "b655a5beb26aa94f98add6bc8889f5fb9bc3ee2cc3fc954e151201f4c4200a5e"
|
||||
},
|
||||
"@std/yaml@1.0.10": {
|
||||
"integrity": "245706ea3511cc50c8c6d00339c23ea2ffa27bd2c7ea5445338f8feff31fa58e"
|
||||
}
|
||||
}
|
||||
}
|
||||
20
example.lang4
Normal file
20
example.lang4
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
fn main() -> int {
|
||||
let v: int = 123;
|
||||
|
||||
let ch = 'c';
|
||||
let s = "hello\ world";
|
||||
|
||||
print_int(v);
|
||||
print_str(v);
|
||||
}
|
||||
|
||||
|
||||
fn print_int(v: int) {
|
||||
}
|
||||
|
||||
fn print_str(v: str) {
|
||||
}
|
||||
|
||||
// vim: syntax=rust
|
||||
|
||||
271
src/ast.ts
Normal file
271
src/ast.ts
Normal file
@ -0,0 +1,271 @@
|
||||
export class File {
|
||||
constructor(
|
||||
public readonly id: number,
|
||||
public readonly stmts: Stmt[],
|
||||
) {}
|
||||
|
||||
visit(v: Visitor) {
|
||||
v.visitFile?.(this);
|
||||
this.stmts.forEach((stmt) => stmt.visit(v));
|
||||
}
|
||||
}
|
||||
|
||||
export class Block {
|
||||
constructor(
|
||||
public readonly id: number,
|
||||
public readonly line: number,
|
||||
public readonly stmts: Stmt[],
|
||||
public readonly expr?: Expr,
|
||||
) {}
|
||||
|
||||
visit(v: Visitor) {
|
||||
if (v.visitBlock?.(this) === VisitorBreak) {
|
||||
return;
|
||||
}
|
||||
this.stmts.forEach((stmt) => stmt.visit(v));
|
||||
this.expr?.visit(v);
|
||||
}
|
||||
}
|
||||
|
||||
export class Stmt {
|
||||
constructor(
|
||||
public readonly id: number,
|
||||
public readonly line: number,
|
||||
public readonly kind: StmtKind,
|
||||
) {}
|
||||
|
||||
visit(v: Visitor) {
|
||||
if (v.visitStmt?.(this) === VisitorBreak) {
|
||||
return;
|
||||
}
|
||||
const k = this.kind;
|
||||
switch (k.tag) {
|
||||
case "error":
|
||||
break;
|
||||
case "fn":
|
||||
k.params.forEach((param) => param.visit(v));
|
||||
k.retTy?.visit(v);
|
||||
k.body.visit(v);
|
||||
break;
|
||||
case "let":
|
||||
k.param.visit(v);
|
||||
k.init.visit(v);
|
||||
break;
|
||||
case "assign":
|
||||
k.place.visit(v);
|
||||
k.expr.visit(v);
|
||||
break;
|
||||
case "expr":
|
||||
k.expr.visit(v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type StmtKind =
|
||||
| { tag: "error" }
|
||||
| { tag: "fn"; ident: string; params: Param[]; retTy?: Ty; body: Block }
|
||||
| { tag: "let"; param: Param; init: Expr }
|
||||
| { tag: "assign"; place: Expr; expr: Expr }
|
||||
| { tag: "expr"; expr: Expr };
|
||||
|
||||
export class Expr {
|
||||
constructor(
|
||||
public readonly id: number,
|
||||
public readonly line: number,
|
||||
public readonly kind: ExprKind,
|
||||
) {}
|
||||
|
||||
visit(v: Visitor) {
|
||||
if (v.visitExpr?.(this) === VisitorBreak) {
|
||||
return;
|
||||
}
|
||||
const k = this.kind;
|
||||
switch (k.tag) {
|
||||
case "error":
|
||||
break;
|
||||
case "ident":
|
||||
break;
|
||||
case "int":
|
||||
break;
|
||||
case "char":
|
||||
break;
|
||||
case "str":
|
||||
break;
|
||||
case "call":
|
||||
k.expr.visit(v);
|
||||
k.args.forEach((expr) => expr.visit(v));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type ExprKind =
|
||||
| { tag: "error" }
|
||||
| { tag: "ident"; ident: string }
|
||||
| { tag: "int"; value: string }
|
||||
| { tag: "char"; value: string }
|
||||
| { tag: "str"; value: string }
|
||||
| { tag: "call"; expr: Expr; args: Expr[] };
|
||||
|
||||
export class Param {
|
||||
constructor(
|
||||
public readonly id: number,
|
||||
public readonly line: number,
|
||||
public readonly pat: Pat,
|
||||
public readonly ty?: Ty,
|
||||
) {}
|
||||
|
||||
visit(v: Visitor) {
|
||||
if (v.visitParam?.(this) === VisitorBreak) {
|
||||
return;
|
||||
}
|
||||
this.pat.visit(v);
|
||||
this.ty?.visit(v);
|
||||
}
|
||||
}
|
||||
|
||||
export class Pat {
|
||||
constructor(
|
||||
public readonly id: number,
|
||||
public readonly line: number,
|
||||
public readonly kind: PatKind,
|
||||
) {}
|
||||
|
||||
visit(v: Visitor) {
|
||||
if (v.visitPat?.(this) === VisitorBreak) {
|
||||
return;
|
||||
}
|
||||
const k = this.kind;
|
||||
switch (k.tag) {
|
||||
case "error":
|
||||
break;
|
||||
case "ident":
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type PatKind =
|
||||
| { tag: "error" }
|
||||
| { tag: "ident"; ident: string };
|
||||
|
||||
export class Ty {
|
||||
constructor(
|
||||
public readonly id: number,
|
||||
public readonly line: number,
|
||||
public readonly kind: TyKind,
|
||||
) {}
|
||||
|
||||
visit(v: Visitor) {
|
||||
if (v.visitTy?.(this) === VisitorBreak) {
|
||||
return;
|
||||
}
|
||||
const k = this.kind;
|
||||
switch (k.tag) {
|
||||
case "error":
|
||||
break;
|
||||
case "ident":
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type TyKind =
|
||||
| { tag: "error" }
|
||||
| { tag: "ident"; ident: string };
|
||||
|
||||
export const VisitorBreak = Symbol();
|
||||
export type VisitorBreak = typeof VisitorBreak;
|
||||
|
||||
export interface Visitor {
|
||||
visitFile?(file: File): void | VisitorBreak;
|
||||
visitBlock?(block: Block): void | VisitorBreak;
|
||||
visitStmt?(stmt: Stmt): void | VisitorBreak;
|
||||
visitExpr?(expr: Expr): void | VisitorBreak;
|
||||
visitParam?(param: Param): void | VisitorBreak;
|
||||
visitPat?(pat: Pat): void | VisitorBreak;
|
||||
visitTy?(ty: Ty): void | VisitorBreak;
|
||||
}
|
||||
|
||||
export class AstBuilder {
|
||||
private id = 0;
|
||||
|
||||
file(
|
||||
stmts: Stmt[],
|
||||
): File {
|
||||
return new File(
|
||||
this.id++,
|
||||
stmts,
|
||||
);
|
||||
}
|
||||
|
||||
block(
|
||||
line: number,
|
||||
stmts: Stmt[],
|
||||
expr?: Expr,
|
||||
): Block {
|
||||
return new Block(
|
||||
this.id++,
|
||||
line,
|
||||
stmts,
|
||||
expr,
|
||||
);
|
||||
}
|
||||
|
||||
stmt<Tag extends StmtKind["tag"]>(
|
||||
line: number,
|
||||
tag: Tag,
|
||||
kind: Omit<StmtKind & { tag: Tag }, "tag">,
|
||||
): Stmt {
|
||||
return new Stmt(
|
||||
this.id++,
|
||||
line,
|
||||
{ tag, ...kind } as StmtKind,
|
||||
);
|
||||
}
|
||||
|
||||
expr<Tag extends ExprKind["tag"]>(
|
||||
line: number,
|
||||
tag: Tag,
|
||||
kind: Omit<StmtKind & { tag: Tag }, "tag">,
|
||||
): Expr {
|
||||
return new Expr(
|
||||
this.id++,
|
||||
line,
|
||||
{ tag, ...kind } as ExprKind,
|
||||
);
|
||||
}
|
||||
|
||||
param(
|
||||
line: number,
|
||||
pat: Pat,
|
||||
ty?: Ty,
|
||||
): Param {
|
||||
return new Param(this.id++, line, pat, ty);
|
||||
}
|
||||
|
||||
pat<Tag extends PatKind["tag"]>(
|
||||
line: number,
|
||||
tag: Tag,
|
||||
kind: Omit<PatKind & { tag: Tag }, "tag">,
|
||||
): Pat {
|
||||
return new Pat(
|
||||
this.id++,
|
||||
line,
|
||||
{ tag, ...kind } as PatKind,
|
||||
);
|
||||
}
|
||||
|
||||
ty<Tag extends TyKind["tag"]>(
|
||||
line: number,
|
||||
tag: Tag,
|
||||
kind: Omit<TyKind & { tag: Tag }, "tag">,
|
||||
): Ty {
|
||||
return new Ty(
|
||||
this.id++,
|
||||
line,
|
||||
{ tag, ...kind } as TyKind,
|
||||
);
|
||||
}
|
||||
}
|
||||
22
src/main.ts
Normal file
22
src/main.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import * as yaml from "jsr:@std/yaml";
|
||||
import { tokenize } from "./tok.ts";
|
||||
import { Parser } from "./parse.ts";
|
||||
import { Resolver } from "./resolve.ts";
|
||||
|
||||
async function main() {
|
||||
const text = await Deno.readTextFile(Deno.args[0]);
|
||||
const toks = tokenize(text);
|
||||
// console.log({ toks });
|
||||
|
||||
const parser = new Parser(toks);
|
||||
const file = parser.parseFile();
|
||||
|
||||
// console.log(yaml.stringify({ file }, { skipInvalid: true, indent: 2 }));
|
||||
// console.log(JSON.stringify({ file }, null, 2));
|
||||
|
||||
file.visit(new Resolver());
|
||||
|
||||
// console.log(cx);
|
||||
}
|
||||
|
||||
await main();
|
||||
254
src/parse.ts
Normal file
254
src/parse.ts
Normal file
@ -0,0 +1,254 @@
|
||||
import { AstBuilder, Block, Expr, File, Param, Pat, Stmt, Ty } from "./ast.ts";
|
||||
import { Tok } from "./tok.ts";
|
||||
|
||||
const t = new AstBuilder();
|
||||
|
||||
export class Parser {
|
||||
private i = 0;
|
||||
private eaten?: Tok;
|
||||
|
||||
constructor(
|
||||
private toks: Tok[],
|
||||
) {}
|
||||
|
||||
parseFile(): File {
|
||||
const stmts: Stmt[] = [];
|
||||
while (!this.done) {
|
||||
stmts.push(this.parseItem());
|
||||
}
|
||||
return t.file(stmts);
|
||||
}
|
||||
|
||||
parseItem(): Stmt {
|
||||
const line = this.line();
|
||||
if (this.test("fn")) {
|
||||
return this.parseFn();
|
||||
} else if (this.test("let")) {
|
||||
return this.parseFn();
|
||||
} else {
|
||||
this.expect("item");
|
||||
this.step();
|
||||
return t.stmt(line, "error", {});
|
||||
}
|
||||
}
|
||||
|
||||
parseBlock(): Block {
|
||||
const line = this.line();
|
||||
this.step();
|
||||
|
||||
const stmts: Stmt[] = [];
|
||||
let expr: Expr | undefined = undefined;
|
||||
|
||||
while (!this.done && !this.test("}")) {
|
||||
const line = this.line();
|
||||
if (this.test("fn")) {
|
||||
stmts.push(this.parseFn());
|
||||
} else if (this.test("let")) {
|
||||
stmts.push(this.parseLet());
|
||||
this.expect(";");
|
||||
} else {
|
||||
const lhs = this.parseExpr();
|
||||
if (this.eat("=")) {
|
||||
const rhs = this.parseExpr();
|
||||
this.expect(";");
|
||||
stmts.push(
|
||||
t.stmt(line, "assign", { place: lhs, expr: rhs }),
|
||||
);
|
||||
} else if (this.eat(";")) {
|
||||
stmts.push(t.stmt(line, "expr", { expr: lhs }));
|
||||
} else if (this.test("}")) {
|
||||
expr = lhs;
|
||||
break;
|
||||
} else {
|
||||
this.expect(";");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.expect("}");
|
||||
return t.block(line, stmts, expr);
|
||||
}
|
||||
|
||||
parseFn(): Stmt {
|
||||
const line = this.line();
|
||||
this.step();
|
||||
if (!this.expect("ident")) {
|
||||
return t.stmt(line, "error", {});
|
||||
}
|
||||
const ident = this.eaten!.value!;
|
||||
const params: Param[] = [];
|
||||
if (!this.expect("(")) {
|
||||
return t.stmt(line, "error", {});
|
||||
}
|
||||
if (!this.done && !this.test(")")) {
|
||||
params.push(this.parseParam());
|
||||
while (this.eat(",")) {
|
||||
if (this.done || this.test(")")) {
|
||||
break;
|
||||
}
|
||||
params.push(this.parseParam());
|
||||
}
|
||||
}
|
||||
if (!this.expect(")")) {
|
||||
return t.stmt(line, "error", {});
|
||||
}
|
||||
let retTy: Ty | undefined = undefined;
|
||||
if (this.eat("->")) {
|
||||
retTy = this.parseTy();
|
||||
}
|
||||
if (!this.test("{")) {
|
||||
this.expect("{");
|
||||
return t.stmt(line, "error", {});
|
||||
}
|
||||
const body = this.parseBlock();
|
||||
return t.stmt(line, "fn", { ident, params, retTy, body });
|
||||
}
|
||||
|
||||
parseLet(): Stmt {
|
||||
const line = this.line();
|
||||
this.step();
|
||||
const param = this.parseParam();
|
||||
if (!this.expect("=")) {
|
||||
return t.stmt(line, "error", {});
|
||||
}
|
||||
const init = this.parseExpr();
|
||||
return t.stmt(line, "let", { param, init });
|
||||
}
|
||||
|
||||
parseExpr(): Expr {
|
||||
return this.parsePostfix();
|
||||
}
|
||||
|
||||
parsePostfix(): Expr {
|
||||
let expr = this.parseOp();
|
||||
while (true) {
|
||||
const line = this.line();
|
||||
if (this.eat("(")) {
|
||||
const args: Expr[] = [];
|
||||
if (!this.done && !this.test(")")) {
|
||||
args.push(this.parseExpr());
|
||||
while (this.eat(",")) {
|
||||
if (this.done || this.test(")")) {
|
||||
break;
|
||||
}
|
||||
args.push(this.parseExpr());
|
||||
}
|
||||
}
|
||||
if (!this.expect(")")) {
|
||||
return t.expr(line, "error", {});
|
||||
}
|
||||
expr = t.expr(line, "call", { expr, args });
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
parseOp(): Expr {
|
||||
const line = this.line();
|
||||
if (this.eat("ident")) {
|
||||
const ident = this.eaten!.value!;
|
||||
return t.expr(line, "ident", { ident });
|
||||
} else if (this.eat("int")) {
|
||||
const value = this.eaten!.value!;
|
||||
return t.expr(line, "int", { value });
|
||||
} else if (this.eat("char")) {
|
||||
const value = this.eaten!.value!;
|
||||
return t.expr(line, "int", { value });
|
||||
} else if (this.eat("str")) {
|
||||
const value = this.eaten!.value!;
|
||||
return t.expr(line, "int", { value });
|
||||
} else {
|
||||
this.expect("expr");
|
||||
this.step();
|
||||
return t.expr(line, "error", {});
|
||||
}
|
||||
}
|
||||
|
||||
parseParam(): Param {
|
||||
const line = this.line();
|
||||
const pat = this.parsePat();
|
||||
let ty: Ty | undefined = undefined;
|
||||
if (this.eat(":")) {
|
||||
ty = this.parseTy();
|
||||
}
|
||||
return t.param(line, pat, ty);
|
||||
}
|
||||
|
||||
parsePat(): Pat {
|
||||
const line = this.line();
|
||||
if (this.eat("ident")) {
|
||||
const ident = this.eaten!.value!;
|
||||
return t.pat(line, "ident", { ident });
|
||||
} else {
|
||||
this.expect("pat");
|
||||
this.step();
|
||||
return t.pat(line, "error", {});
|
||||
}
|
||||
}
|
||||
|
||||
parseTy(): Ty {
|
||||
const line = this.line();
|
||||
if (this.eat("ident")) {
|
||||
const ident = this.eaten!.value!;
|
||||
return t.ty(line, "ident", { ident });
|
||||
} else {
|
||||
this.expect("ty");
|
||||
this.step();
|
||||
return t.ty(line, "error", {});
|
||||
}
|
||||
}
|
||||
|
||||
private expect(type: string): boolean {
|
||||
const line = this.line();
|
||||
if (!this.eat(type)) {
|
||||
if (this.done) {
|
||||
this.error(line, `expected '${type}', got 'eof'`);
|
||||
} else {
|
||||
this.error(line, `expected '${type}', got '${this.tok.type}'`);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private eat(type: string): boolean {
|
||||
if (this.test(type)) {
|
||||
this.eaten = this.tok;
|
||||
this.step();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private test(type: string): boolean {
|
||||
return !this.done && this.tok.type === type;
|
||||
}
|
||||
|
||||
private step() {
|
||||
this.i += 1;
|
||||
}
|
||||
|
||||
private line(): number {
|
||||
return this.tok.line;
|
||||
}
|
||||
|
||||
private get tok(): Tok {
|
||||
return this.toks[this.i];
|
||||
}
|
||||
|
||||
private get done(): boolean {
|
||||
return this.i >= this.toks.length;
|
||||
}
|
||||
|
||||
private error(line: number, message: string) {
|
||||
console.error(
|
||||
`%cerror%c: ${message}\n %c--> line ${line}%c`,
|
||||
"font-weight: bold; color: red",
|
||||
"font-weight: bold; color: while",
|
||||
"color: cyan",
|
||||
"",
|
||||
);
|
||||
}
|
||||
}
|
||||
81
src/resolve.ts
Normal file
81
src/resolve.ts
Normal file
@ -0,0 +1,81 @@
|
||||
import { Pat, Stmt, Visitor, VisitorBreak } from "./ast.ts";
|
||||
|
||||
export class Def {
|
||||
constructor(
|
||||
kind: DefKind,
|
||||
) {}
|
||||
}
|
||||
|
||||
export type DefKind =
|
||||
| { tag: "fn"; stmt: Stmt }
|
||||
| { tag: "param"; stmt: Stmt; pat: Pat }
|
||||
| { tag: "let"; stmt: Stmt; pat: Pat };
|
||||
|
||||
export class Rib {
|
||||
private syms = new Map<string, Def>();
|
||||
|
||||
constructor(
|
||||
private kind: RibKind,
|
||||
) {}
|
||||
|
||||
define(ident: string, def: Def) {
|
||||
this.syms.set(ident, def);
|
||||
}
|
||||
}
|
||||
|
||||
export type RibKind =
|
||||
| { tag: "root" }
|
||||
| { tag: "fn"; parent: Rib }
|
||||
| { tag: "block"; parent: Rib }
|
||||
| { tag: "let"; parent: Rib };
|
||||
|
||||
export class Resolver implements Visitor {
|
||||
private rib = new Rib({ tag: "root" });
|
||||
private ribStateStack: Rib[] = [];
|
||||
|
||||
pushRib<Tag extends RibKind["tag"]>(
|
||||
tag: Tag,
|
||||
kind: Omit<RibKind & { tag: Tag }, "tag" | "parent">,
|
||||
) {
|
||||
this.rib = new Rib({ tag, parent: this.rib, ...kind } as RibKind);
|
||||
}
|
||||
|
||||
saveRibState() {
|
||||
this.ribStateStack.push(this.rib);
|
||||
}
|
||||
|
||||
restoreRibState() {
|
||||
this.rib = this.ribStateStack.pop()!;
|
||||
}
|
||||
|
||||
define(ident: string, def: Def) {
|
||||
this.rib.define(ident, def);
|
||||
}
|
||||
|
||||
visitStmt(stmt: Stmt): void | VisitorBreak {
|
||||
const k = stmt.kind;
|
||||
if (k.tag === "fn") {
|
||||
this.define(k.ident, new Def({ tag: "fn", stmt }));
|
||||
this.saveRibState();
|
||||
this.pushRib("fn", {});
|
||||
k.params.forEach((param) => param.visit(this));
|
||||
k.retTy?.visit(this);
|
||||
k.body.visit(this);
|
||||
this.restoreRibState();
|
||||
return VisitorBreak;
|
||||
} else if (k.tag === "let") {
|
||||
this.pushRib("let", {});
|
||||
k.param.pat.visit({
|
||||
visitPat: (pat: Pat) => {
|
||||
const k = pat.kind;
|
||||
if (k.tag === "ident") {
|
||||
this.define(
|
||||
k.ident,
|
||||
new Def({ tag: "let", stmt, pat }),
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
77
src/tok.ts
Normal file
77
src/tok.ts
Normal file
@ -0,0 +1,77 @@
|
||||
export type Tok = {
|
||||
type: string;
|
||||
line: number;
|
||||
value?: string;
|
||||
};
|
||||
|
||||
const keywords = new Set([
|
||||
"fn",
|
||||
"let",
|
||||
"true",
|
||||
"false",
|
||||
]);
|
||||
|
||||
type OpTree = Map<string, OpTree | null>;
|
||||
const opTreeRoot: OpTree = new Map(
|
||||
Object.entries({
|
||||
"-": new Map(Object.entries({
|
||||
">": null,
|
||||
})),
|
||||
}),
|
||||
);
|
||||
|
||||
export function tokenize(text: string): Tok[] {
|
||||
return text
|
||||
.replace(/\/\/[^\n]*/g, "")
|
||||
.replace(/\/\*.*?\*\//gs, "")
|
||||
.replace(/([^a-zA-Z0-9_'"\\ \t\r])/g, " $1 ")
|
||||
.split(/(?<!\\)[ \t\r]/)
|
||||
.filter((tok) => tok !== "")
|
||||
.map((tok) => tok.replace(/\\ /g, " "))
|
||||
.reduce<[string[], OpTree]>(([toks, opTree], tok) => {
|
||||
if (toks.length === 0) {
|
||||
toks.push(tok);
|
||||
return [toks, opTree];
|
||||
}
|
||||
const last = toks.at(-1)!;
|
||||
if (!opTree.has(last)) {
|
||||
toks.push(tok);
|
||||
return [toks, opTreeRoot];
|
||||
}
|
||||
if (opTree.get(last) === null) {
|
||||
toks.push(tok);
|
||||
return [toks, opTreeRoot];
|
||||
} else if (opTree.get(last)!.has(tok)) {
|
||||
toks[toks.length - 1] += tok;
|
||||
return [toks, opTree.get(last)!];
|
||||
} else {
|
||||
toks.push(tok);
|
||||
return [toks, opTreeRoot];
|
||||
}
|
||||
}, [[], opTreeRoot])[0]
|
||||
.slice(0, -1)
|
||||
.reduce<[Tok[], number]>(([toks, line], type) => {
|
||||
if (type === "\n") {
|
||||
return [toks, line + 1];
|
||||
} else {
|
||||
toks.push({ type, line });
|
||||
return [toks, line];
|
||||
}
|
||||
}, [[], 1])[0]
|
||||
.map((tok) => {
|
||||
if (
|
||||
/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tok.type) &&
|
||||
!keywords.has(tok.type)
|
||||
) {
|
||||
return { type: "ident", line: tok.line, value: tok.type };
|
||||
} else if (/^[0-9_]+$/.test(tok.type)) {
|
||||
return { type: "int", line: tok.line, value: tok.type };
|
||||
} else if (/^'.*?'$/.test(tok.type)) {
|
||||
return { type: "char", line: tok.line, value: tok.type };
|
||||
} else if (/^".*?"$/.test(tok.type)) {
|
||||
return { type: "str", line: tok.line, value: tok.type };
|
||||
} else {
|
||||
return tok;
|
||||
}
|
||||
});
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user