use new tok
All checks were successful
Check / Explore-Gitea-Actions (push) Successful in 9s

This commit is contained in:
sfja 2026-03-16 21:41:57 +01:00
parent c7741b8d31
commit d908ff30e0
5 changed files with 55 additions and 53 deletions

View File

@ -1,29 +1,31 @@
import { Loc } from "./diagnostics.ts";
export function create<Tag extends NodeKind["tag"]>(
line: number,
loc: Loc,
tag: Tag,
kind: Omit<NodeKind & { tag: Tag }, "tag">,
): Node {
return Node.create(line, tag, kind);
return Node.create(loc, tag, kind);
}
export class Node {
private static idCounter = 0;
static create<Tag extends NodeKind["tag"]>(
line: number,
loc: Loc,
tag: Tag,
kind: Omit<NodeKind & { tag: Tag }, "tag">,
): Node {
return new Node(
Node.idCounter++,
line,
loc,
{ tag, ...kind } as NodeKind & { tag: Tag },
);
}
private constructor(
public id: number,
public line: number,
public loc: Loc,
public kind: NodeKind,
) {}

View File

@ -15,11 +15,13 @@ export type FileInfo = {
export function printDiagnostics(
filename: string,
line: number,
loc: Loc,
severity: "error" | "info",
message: string,
text?: string,
) {
const line = loc.line;
const severityColor = ({
"error": "red",
"info": "blue",

View File

@ -1,5 +1,5 @@
import * as ast from "../ast.ts";
import { printDiagnostics } from "../diagnostics.ts";
import { Loc, printDiagnostics } from "../diagnostics.ts";
import { Ty } from "../ty.ts";
import { builtins } from "./builtins.ts";
import { ResolveMap } from "./resolve.ts";
@ -46,14 +46,14 @@ export class Checker {
this.assertCompatible(
exprTy,
explicitTy,
sym.stmt.kind.expr.line,
sym.stmt.kind.expr.loc,
);
}
return exprTy;
}
if (sym.tag === "FnParam") {
if (!node.kind.ty) {
this.error(node.line, `parameter must have a type`);
this.error(node.loc, `parameter must have a type`);
this.fail();
}
return this.check(node.kind.ty);
@ -91,13 +91,13 @@ export class Checker {
for (const value of node.kind.values) {
const valueTy = this.check(value);
if (ty) {
this.assertCompatible(ty, valueTy, value.line);
this.assertCompatible(ty, valueTy, value.loc);
} else {
ty = valueTy;
}
}
if (!ty) {
this.error(node.line, `could not infer type of empty array`);
this.error(node.loc, `could not infer type of empty array`);
this.fail();
}
const length = node.kind.values.length;
@ -120,7 +120,7 @@ export class Checker {
return Ty.create("Slice", { ty: exprTy.kind.ty });
}
this.error(
node.line,
node.loc,
`cannot use index operator on '${exprTy.pretty()}' with '${argTy.pretty()}'`,
);
this.fail();
@ -150,7 +150,7 @@ export class Checker {
}
}
this.error(
node.line,
node.loc,
`operator '${node.kind.tok}' cannot be applied to type '${exprTy.pretty()}'`,
);
this.fail();
@ -167,7 +167,7 @@ export class Checker {
);
if (!binaryOp) {
this.error(
node.line,
node.loc,
`operator '${node.kind.tok}' cannot be applied to types '${left.pretty()}' and '${right.pretty()}'`,
);
this.fail();
@ -180,7 +180,7 @@ export class Checker {
const operandTy = operandExpr && this.check(operandExpr);
if (operandTy && !operandTy.compatibleWith(Ty.Int)) {
this.error(
operandExpr.line,
operandExpr.loc,
`range operand must be '${Ty.Int.pretty()}', not '${operandTy.pretty()}'`,
);
this.fail();
@ -198,7 +198,7 @@ export class Checker {
case "bool":
return Ty.Bool;
default:
this.error(node.line, `unknown type '${node.kind.ident}'`);
this.error(node.loc, `unknown type '${node.kind.ident}'`);
}
}
@ -216,14 +216,14 @@ export class Checker {
const lengthTy = this.check(node.kind.length);
if (!lengthTy.compatibleWith(Ty.Int)) {
this.error(
node.kind.length.line,
node.kind.length.loc,
`for array length, expected 'int', got '${lengthTy.pretty()}'`,
);
this.fail();
}
if (!node.kind.length.is("IntExpr")) {
this.error(
node.kind.length.line,
node.kind.length.loc,
`array length must be an 'int' expression`,
);
this.fail();
@ -254,11 +254,11 @@ export class Checker {
: Ty.Void;
if (!ty.compatibleWith(retTy)) {
this.error(
node.line,
node.loc,
`type '${ty.pretty()}' not compatible with return type '${retTy.pretty()}'`,
);
this.info(
stmt.kind.retTy?.line ?? stmt.line,
stmt.kind.retTy?.loc ?? stmt.loc,
`return type '${retTy}' defined here`,
);
this.fail();
@ -282,7 +282,7 @@ export class Checker {
if (!callableTy) {
this.error(
node.line,
node.loc,
`type '${calleeTy.pretty()}' not callable`,
);
this.fail();
@ -293,12 +293,12 @@ export class Checker {
const params = callableTy.kind.params;
if (args.length !== params.length) {
this.error(
node.line,
node.loc,
`incorrect amount of arguments. got ${args.length} expected ${params.length}`,
);
if (calleeTy.is("FnStmt")) {
this.info(
calleeTy.kind.stmt.line,
calleeTy.kind.stmt.loc,
"function defined here",
);
}
@ -307,14 +307,14 @@ export class Checker {
for (const i of args.keys()) {
if (!args[i].compatibleWith(params[i])) {
this.error(
node.kind.args[i].line,
node.kind.args[i].loc,
`type '${args[i].pretty()}' not compatible with type '${
params[i].pretty()
}', for argument ${i}`,
);
if (calleeTy.is("FnStmt")) {
this.info(
calleeTy.kind.stmt.kind.params[i].line,
calleeTy.kind.stmt.kind.params[i].loc,
`parameter '${
calleeTy.kind.stmt.kind.params[i]
.as("Param").kind.ident
@ -327,30 +327,30 @@ export class Checker {
return callableTy.kind.retTy;
}
private assertCompatible(left: Ty, right: Ty, line: number): void {
private assertCompatible(left: Ty, right: Ty, loc: Loc): void {
if (!left.compatibleWith(right)) {
this.error(
line,
loc,
`type '${left.pretty()}' not compatible with type '${right.pretty()}'`,
);
this.fail();
}
}
private error(line: number, message: string) {
private error(loc: Loc, message: string) {
printDiagnostics(
this.filename,
line,
loc,
"error",
message,
this.text,
);
}
private info(line: number, message: string) {
private info(loc: Loc, message: string) {
printDiagnostics(
this.filename,
line,
loc,
"info",
message,
this.text,

View File

@ -11,7 +11,7 @@ export function parse(
export class Parser {
private toks: Tok[];
private idx = 0;
private currentLine = 1;
private currentLoc: Loc = { idx: 0, line: 1, col: 1 };
private prevTok: Tok | null = null;
constructor(
@ -148,7 +148,7 @@ export class Parser {
}
}
parseRangeTail(loc: number, begin: ast.Node | null, tok: string): ast.Node {
parseRangeTail(loc: Loc, begin: ast.Node | null, tok: string): ast.Node {
const limit: ast.RangeLimit = tok === ".." ? "Exclusive" : "Inclusive";
let end: ast.Node | null = null;
if (![";", ",", ")", "]"].some((tok) => this.test(tok))) {
@ -319,7 +319,7 @@ export class Parser {
}
}
private mustEat(type: string, loc: number = this.loc()): Tok {
private mustEat(type: string, loc = this.loc()): Tok {
const tok = this.current;
if (tok.type !== type) {
this.error(
@ -333,7 +333,7 @@ export class Parser {
return tok;
}
private error(message: string, loc: number): never {
private error(message: string, loc: Loc): never {
printDiagnostics(this.filename, loc, "error", message, this.text);
throw new Error();
Deno.exit(1);
@ -353,7 +353,7 @@ export class Parser {
}
this.idx += 1;
if (!this.done) {
this.currentLine = this.current.line;
this.currentLoc = this.current.loc;
}
}
@ -361,8 +361,8 @@ export class Parser {
return !this.done && this.current.type == type;
}
private loc(): number {
return this.currentLine;
private loc(): Loc {
return this.currentLoc;
}
private get current(): Tok {
@ -374,8 +374,7 @@ export class Parser {
}
}
export type Tok = { type: string; value: string; line: number };
export type Tok2 = { type: string; value: string; loc: Loc };
export type Tok = { type: string; value: string; loc: Loc };
const keywordPattern =
/^(?:(?:fn)|(?:return)|(?:let)|(?:if)|(?:else)|(?:while)|(?:break)|(?:or)|(?:and)|(?:not)|(?:mut))/;
@ -384,13 +383,13 @@ const operatorPattern2 =
/((?:\->)|(?:==)|(?:!=)|(?:<=)|(?:>=)|(?:<<)|(?:>>)|(?:\.\*)|(?:\.\.)|(?:\.\.=)|[\n\(\)\{\}\[\]\,\.\;\:\!\=\<\>\&\^\|\+\-\*\/\%])/g;
export function tokenize(text: string): Tok[] {
return new Lexer<Tok2>()
return new Lexer()
.add(/[ \t\r\n]+/, (_) => null)
.add(/\/\/[^\n]*/, (_) => null)
.add(operatorPattern2, (loc, value) => ({ type: value, value, loc }))
.add(/[a-zA-Z_][a-zA-Z0-9_]*/, (loc, value) => {
const type = keywordPattern.test(value) ? value : "ident";
return ({ type, value, loc });
return { type, value, loc };
})
.add(/0|(?:[1-9][0-9]*)/, (loc, value) => {
return { type: "int", value, loc };
@ -398,21 +397,20 @@ export function tokenize(text: string): Tok[] {
.add(/./, (loc, value) => {
return null;
})
.lex(text)
.map<Tok>(({ type, value, loc: { line } }) => ({ type, value, line }));
.lex(text);
}
type LexRule<TokT> = {
type LexRule = {
pattern: RegExp;
action: LexAction<TokT>;
action: LexAction;
};
type LexAction<TokT> = (loc: Loc, match: string) => TokT | null;
type LexAction = (loc: Loc, match: string) => Tok | null;
class Lexer<TokT> {
private rules: LexRule<TokT>[] = [];
class Lexer {
private rules: LexRule[] = [];
add(pattern: RegExp, action: LexAction<TokT>): this {
add(pattern: RegExp, action: LexAction): this {
this.rules.push({
pattern: new RegExp(`^(?:${pattern.source})`),
action,
@ -420,8 +418,8 @@ class Lexer<TokT> {
return this;
}
lex(text: string): TokT[] {
const toks: TokT[] = [];
lex(text: string): Tok[] {
const toks: Tok[] = [];
let idx = 0;
let line = 1;
let col = 1;

View File

@ -83,7 +83,7 @@ export function resolve(
if (sym === null) {
printDiagnostics(
filename,
node.line,
node.loc,
"error",
`undefined symbol '${k.ident}'`,
text,