Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Errors #380

Merged
merged 6 commits into from
Mar 22, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/IR/assignments.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PolygolfError } from "../common/errors";
import { UserError } from "../common/errors";
import {
type BaseNode,
id,
@@ -138,9 +138,9 @@ export function varDeclarationWithAssignment<T extends SomeAssignment>(
(!isAssignment(assignment) &&
assignment.variables.some((y) => !isIdent()(y)))
) {
throw new PolygolfError(
throw new UserError(
"VarDeclarationWithAssignment needs assignments to variables.",
assignment.source,
assignment,
);
}
return {
3 changes: 2 additions & 1 deletion src/IR/collections.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { InvariantError } from "../common/errors";
import {
type BaseNode,
type Node,
@@ -62,7 +63,7 @@ export function isEqualToLiteral(x: Node, literal: Literal): boolean {
isEqualToLiteral(x.value, literal.value)
);
}
throw new Error("Unknown literal kind.");
throw new InvariantError(`Unknown literal kind. ${literal.kind}`);
}

export function array(value: readonly Node[]): Array {
29 changes: 17 additions & 12 deletions src/IR/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { InvariantError, UserError } from "../common/errors";
import { getType } from "../common/getType";
import { type Spine } from "../common/Spine";
import {
@@ -358,7 +359,7 @@ export function intersection(a: Type, b: Type): Type {
} else if (a.kind === b.kind) {
return a;
}
throw new Error("Empty intersection.");
throw new UserError("Empty intersection.", undefined);
}

export function union(a: Type, b: Type): Type {
@@ -397,12 +398,16 @@ export function union(a: Type, b: Type): Type {
} else if (a.kind === b.kind) {
return a;
}
throw new Error(`Cannot model union of ${toString(a)} and ${toString(b)}.`);
throw new UserError(
`Cannot model union of ${toString(a)} and ${toString(b)}.`,
undefined,
);
} catch (e) {
throw new Error(
throw new UserError(
`Cannot model union of ${toString(a)} and ${toString(b)}.\n${
e instanceof Error ? e.message : ""
}`,
undefined,
);
}
}
@@ -461,7 +466,7 @@ export function neg(a: IntegerBound): IntegerBound {
export function add(a: IntegerBound, b: IntegerBound): IntegerBound {
if (leq(b, a)) [a, b] = [b, a];
if (a === "-oo" && b === "oo")
throw new Error("Indeterminate result of -oo + oo.");
throw new InvariantError("Indeterminate result of -oo + oo.");
if (a === "-oo") return a;
if (b === "oo") return b;
return (a as bigint) + (b as bigint);
@@ -472,7 +477,7 @@ export function sub(a: IntegerBound, b: IntegerBound): IntegerBound {
export function mul(a: IntegerBound, b: IntegerBound): IntegerBound {
if (leq(b, a)) [a, b] = [b, a];
if ((a === "-oo" && b === 0n) || (b === "oo" && a === 0n))
throw new Error("Indeterminate result of 0 * oo.");
throw new InvariantError("Indeterminate result of 0 * oo.");
if (a === "-oo") return lt(b, 0n) ? "oo" : "-oo";
if (b === "oo") return lt(a, 0n) ? "-oo" : "oo";
return (a as bigint) * (b as bigint);
@@ -482,9 +487,9 @@ export function floorDiv(a: IntegerBound, b: IntegerBound): IntegerBound {
return mul(res, b) !== a && lt(a, 0n) !== lt(b, 0n) ? sub(res, 1n) : res;
}
export function truncDiv(a: IntegerBound, b: IntegerBound): IntegerBound {
if (b === 0n) throw new Error("Indeterminate result of x / 0.");
if (b === 0n) throw new InvariantError("Indeterminate result of x / 0.");
if (!isFiniteBound(a) && !isFiniteBound(b))
throw new Error("Indeterminate result of +-oo / +-oo.");
throw new InvariantError("Indeterminate result of +-oo / +-oo.");
if (!isFiniteBound(a)) {
if (lt(a, 0n) === lt(b, 0n)) return "oo";
else return "-oo";
@@ -531,7 +536,7 @@ export function defaultValue(a: Type): Node {
if (lt(0n, a.low)) return int(a.low as bigint);
return int(0);
}
throw new Error(`Unsupported default value for type ${toString(a)}`);
throw new InvariantError(`Unsupported default value for type ${toString(a)}`);
}

export function instantiateGenerics(
@@ -542,7 +547,7 @@ export function instantiateGenerics(
case "Array": {
const lengthType = instantiate(type.length);
if (lengthType.kind !== "TypeArg" && !isArrayIndexType(lengthType))
throw new Error(
throw new InvariantError(
"Array type's second argument must be a constant integer type.",
);
return arrayType(instantiate(type.member), lengthType);
@@ -555,7 +560,7 @@ export function instantiateGenerics(
case "KeyValue": {
const keyType = instantiate(type.key);
if (keyType.kind !== "integer" && keyType.kind !== "text")
throw new Error(
throw new InvariantError(
"KeyValue type's first argument must be an integer or text type.",
);
return keyValueType(keyType, instantiate(type.value));
@@ -567,7 +572,7 @@ export function instantiateGenerics(
keyType.kind !== "text" &&
keyType.kind !== "TypeArg"
)
throw new Error(
throw new InvariantError(
"Table type's first argument must be an integer or text type.",
);
return tableType(keyType, instantiate(type.value));
@@ -626,5 +631,5 @@ export function getLiteralOfType(type: Type, nonEmpty = false): Node {
: [],
);
}
throw new Error(`There's no literal of type '${type.kind}'.`);
throw new InvariantError(`There's no literal of type '${type.kind}'.`);
}
154 changes: 82 additions & 72 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -4,9 +4,12 @@ import yargs from "yargs";
import fs from "fs";
import path from "path";
import compile, { debugEmit } from "./common/compile";
import { PolygolfError } from "./common/errors";
import {
InvariantError,
NotImplementedError,
UserError,
} from "./common/errors";
import languages, { findLang } from "./languages/languages";
import { EmitError } from "./common/emit";

const languageChoices = [
...new Set(languages.flatMap((x) => [x.name.toLowerCase(), x.extension])),
@@ -49,85 +52,92 @@ const options = yargs()
.parseSync(process.argv.slice(2));

if (options.all === true && options.output !== undefined) {
throw new Error(
console.log(
"All variants options is only allowed when the output file is not specified.",
);
}
} else {
const langs =
options.lang === undefined ? languages : [findLang(options.lang)!];
let input = options.input;
if (!fs.existsSync(input)) input += ".polygolf";
const code = fs.readFileSync(input, { encoding: "utf-8" });
const printingMultipleLangs =
langs.length > 1 && options.output === undefined;
for (const result of compile(
code,
{
objective: options.chars === true ? "chars" : "bytes",
getAllVariants: options.all === true,
},
...langs,
)) {
if (printingMultipleLangs) console.log(result.language);
if (typeof result.result === "string") {
if (options.output !== undefined) {
fs.mkdirSync(path.dirname(options.output), { recursive: true });
fs.writeFileSync(
options.output +
(langs.length > 1 || !options.output.includes(".")
? "." + findLang(result.language)!.extension
: ""),
result.result,
);
} else {
console.log(result.result);
}
if (result.warnings.length > 0) {
console.log("Warnings:");
console.log(result.warnings.map((x) => x.message).join("\n"));
}
if (options.debug === true) {
console.log("History:");
console.log(
result.history.map(([c, name]) => `${c} ${name}`).join("\n"),
);

const langs =
options.lang === undefined ? languages : [findLang(options.lang)!];
let input = options.input;
if (!fs.existsSync(input)) input += ".polygolf";
const code = fs.readFileSync(input, { encoding: "utf-8" });
const printingMultipleLangs = langs.length > 1 && options.output === undefined;
for (const result of compile(
code,
{
objective: options.chars === true ? "chars" : "bytes",
getAllVariants: options.all === true,
},
...langs,
)) {
if (printingMultipleLangs) console.log(result.language);
if (typeof result.result === "string") {
if (options.output !== undefined) {
fs.mkdirSync(path.dirname(options.output), { recursive: true });
fs.writeFileSync(
options.output +
(langs.length > 1 || !options.output.includes(".")
? "." + findLang(result.language)!.extension
: ""),
result.result,
);
if (result.errors.length > 0) {
console.log("Errors:");
console.log(
result.errors
.map(
(e) =>
e.message +
(e instanceof NotImplementedError
? "\n" + debugEmit(e.expr)
: ""),
)
.join("\n"),
);
}
}
} else {
console.log(result.result);
if (!printingMultipleLangs && langs.length > 1)
console.log(result.language);
handleError(result.result);
}
if (result.warnings.length > 0) {
console.log("Warnings:");
console.log(result.warnings.map((x) => x.message).join("\n"));
}
if (options.debug === true) {
console.log("History:");
console.log(result.history.map(([c, name]) => `${c} ${name}`).join("\n"));
console.log("");
}

if (result.errors.length > 0) {
console.log("Errors:");
function handleError(e: unknown) {
if (e instanceof UserError) {
console.log(e.message);
if (e.source != null) {
const startLine = e.source.line === 0 ? 0 : e.source.line - 2;
console.log(
result.errors
.map(
(e) =>
e.message +
(e instanceof EmitError ? "\n" + debugEmit(e.expr) : ""),
)
.join("\n"),
code
.split(/\r?\n/)
.slice(startLine, e.source.line)
.map((x, i) => `${startLine + i + 1}`.padStart(3, " ") + " " + x)
.join("\n") +
"\n" +
" ".repeat(e.source.column + 3) +
"^",
);
}
} else if (e instanceof InvariantError) {
console.log(e);
} else {
throw e;
}
} else {
if (!printingMultipleLangs && langs.length > 1)
console.log(result.language);
handleError(result.result);
}
console.log("");
}

function handleError(e: unknown) {
if (e instanceof PolygolfError) {
console.log(e.message);
if (e.source != null) {
const startLine = e.source.line === 0 ? 0 : e.source.line - 2;
console.log(
code
.split(/\r?\n/)
.slice(startLine, e.source.line)
.map((x, i) => `${startLine + i + 1}`.padStart(3, " ") + " " + x)
.join("\n") +
"\n" +
" ".repeat(e.source.column + 3) +
"^",
);
}
} else {
throw e;
}
}
17 changes: 11 additions & 6 deletions src/common/compile.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { isOp, isText, text } from "@/IR";
import { type Language, type Plugin, search, required } from "./Language";
import { EmitError } from "./emit";
import compile from "./compile";
import { compilationOptionsFromKeywords } from "@/markdown-tests";
import { PolygolfError } from "./errors";
import { NotImplementedError, UserError } from "./errors";

const textLang: Language = {
name: "text",
@@ -14,18 +13,24 @@ const textLang: Language = {
.map((x) => {
if (isOp()(x) && x.args.length > 0 && isText()(x.args[0]!)) {
if (x.args[0].value.endsWith("X")) {
context.addWarning(new PolygolfError("global warning"), true);
context.addWarning(
new PolygolfError("local warning that should not be visible"),
new UserError("global warning", undefined),
true,
);
context.addWarning(
new UserError(
"local warning that should not be visible",
undefined,
),
false,
);
}
context.addWarning(
new PolygolfError("local warning that should be visible"),
new UserError("local warning that should be visible", undefined),
false,
);
return [x.args[0].value];
} else throw new EmitError(x);
} else throw new NotImplementedError(x);
})
.join("");
},
Loading
Loading