Skip to content
This repository has been archived by the owner on Jul 3, 2024. It is now read-only.

Commit

Permalink
Merge pull request #9 from open-formulieren/feature/8-exc
Browse files Browse the repository at this point in the history
[#8] Add proper exception handling
  • Loading branch information
Viicos authored Dec 27, 2023
2 parents 38732b7 + 039007a commit cf35b30
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 24 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ dist/
lib/
storybook-static/
docs/
coverage/
6 changes: 6 additions & 0 deletions src/exceptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export class InferenceError extends Error {
constructor(message: string) {
super(message);
this.name = 'InferenceError';
}
}
23 changes: 13 additions & 10 deletions src/helper.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) 2023 Adam Jones
//
// SPDX-License-Identifier: MIT
import {InferenceError} from './exceptions';
import {
Context,
ExplainPath,
Expand Down Expand Up @@ -56,7 +57,7 @@ function apply(
return {...value, sigma: apply(s, value.sigma)};
}
((_: never): never => {
throw new Error('Unknown argument passed to substitution');
throw new InferenceError('Unknown argument passed to substitution');
})(value);
}

Expand Down Expand Up @@ -96,7 +97,7 @@ export const instantiate = (
}

((_: never): never => {
throw new Error('Unknown type passed to instantiate');
throw new InferenceError('Unknown type passed to instantiate');
})(type);
};

Expand Down Expand Up @@ -133,7 +134,7 @@ const freeVars = (value: PolyType | Context): string[] => {
}

((_: never): never => {
throw new Error('Unknown argument passed to substitution');
throw new InferenceError('Unknown argument passed to substitution');
})(value);
};

Expand All @@ -152,7 +153,7 @@ export const unify = (

if (type1.type === 'ty-var') {
if (contains(type2, type1))
throw new Error(`Infinite type detected: ${type1} occurs in ${type2}`);
throw new InferenceError(`Infinite type detected: ${type1} occurs in ${type2}`);

if (type2.type === 'ty-var') {
// var with other name -> explain
Expand All @@ -164,7 +165,7 @@ export const unify = (
type1.explain = [type2, {type: 'ExplainInstan', path: path1, expr}];
} else {
((_: never): never => {
throw new Error('Unknown argument passed to unify');
throw new InferenceError('Unknown argument passed to unify');
})(type2);
}
return makeSubstitution({
Expand All @@ -178,11 +179,13 @@ export const unify = (

if (type1.C !== type2.C) {
const msg = formatUnificationError(type1, type2, expr, path1, path2);
throw new Error(msg);
throw new InferenceError(msg);
}

if (type1.mus.length !== type2.mus.length) {
throw new Error(`Could not unify types (different argument lengths): ${type1} and ${type2}`);
throw new InferenceError(
`Could not unify types (different argument lengths): ${type1} and ${type2}`
);
}

let s: Substitution = makeSubstitution({});
Expand All @@ -208,7 +211,7 @@ const formatUnificationError = (
}, but it is a ${type2.C}`;
return msg;
}
throw new Error(`Unexpected expression type ${expr}`);
throw new InferenceError(`Unexpected expression type ${expr}`);
};

export const reprExpression = (expr: Expression): string => {
Expand All @@ -220,7 +223,7 @@ export const reprExpression = (expr: Expression): string => {
if (expr.type === 'let')
return `"${expr.x}" = ${reprExpression(expr.e1)} in ${reprExpression(expr.e2)}`;
((_: never): never => {
throw new Error(`Unexpected expression type ${expr}`);
throw new InferenceError(`Unexpected expression type ${expr}`);
})(expr);
};

Expand All @@ -246,6 +249,6 @@ const contains = (value: MonoType, type2: TypeVariable): boolean => {
}

((_: never): never => {
throw new Error('Unknown argument passed to substitution');
throw new InferenceError('Unknown argument passed to substitution');
})(value);
};
22 changes: 13 additions & 9 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import type {Substitution} from './helper';
import type {Context} from './models';
import type {JSONValue} from './parser';
import {defaultContext, parseContext, parseJsonLogicExpression} from './parser';
import {W} from './w';

export {defaultContext};

interface InferenceResult {
resultType: string;
intermediateVariables: Substitution['raw'];
}

/**
* @beta
* @param jsonLogic - JsonLogic expression/rule
Expand All @@ -16,14 +22,12 @@ export const infer = (
jsonLogic: JSONValue,
data: JSONValue,
context: Context = defaultContext
): string => {
): InferenceResult => {
const typeenv = parseContext(data, context);
try {
const [subsitution, t] = W(...parseJsonLogicExpression(jsonLogic, typeenv));
const type: string = 'C' in t ? t.C : JSON.stringify([t.a, Object.keys(subsitution.raw)]);
const ctx: string = JSON.stringify(subsitution.raw);
return `result type: ${type}\n(intermediate) variables:${ctx}`;
} catch (error) {
return error.toString();
}
const [subsitution, t] = W(...parseJsonLogicExpression(jsonLogic, typeenv));
const type: string = 'C' in t ? t.C : JSON.stringify([t.a, Object.keys(subsitution.raw)]);
return {
resultType: type,
intermediateVariables: subsitution.raw,
};
};
5 changes: 3 additions & 2 deletions src/m.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) 2023 Adam Jones
//
// SPDX-License-Identifier: MIT
import {InferenceError} from './exceptions';
import {Substitution, generalise, instantiate, newTypeVar, unify} from './helper';
import {Context, Expression, MonoType, makeContext} from './models';

Expand All @@ -9,7 +10,7 @@ export const M = (typEnv: Context, expr: Expression, type: MonoType): Substituti
console.log(`Variable ${expr.x}: expected to have type ${JSON.stringify(type)}`);

const value = typEnv[expr.x];
if (value === undefined) throw new Error(`Undefined variable: ${expr.x}`);
if (value === undefined) throw new InferenceError(`Undefined variable: ${expr.x}`);
return unify(type, instantiate(value), expr);
}

Expand Down Expand Up @@ -69,5 +70,5 @@ export const M = (typEnv: Context, expr: Expression, type: MonoType): Substituti
return s2(s1);
}

throw new Error('Unknown expression type');
throw new InferenceError('Unknown expression type');
};
3 changes: 2 additions & 1 deletion src/parser.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {InferenceError} from './exceptions';
import {
ApplicationExpression,
Context,
Expand Down Expand Up @@ -221,7 +222,7 @@ const applyRight = (expressions: Expression[]): ApplicationExpression => {
// [...rest, e1, e2] = expressions; isn't legal TS
const length = expressions.length;
if (length < 2) {
throw new Error('applyRight requires at least two expressions');
throw new InferenceError('applyRight requires at least two expressions');
}
const rest = expressions.slice(0, length - 2);
const [e1, e2] = expressions.slice(length - 2);
Expand Down
5 changes: 3 additions & 2 deletions src/w.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
// Copyright (c) 2023 Maykin
//
// SPDX-License-Identifier: MIT
import {InferenceError} from './exceptions';
import {Substitution, generalise, instantiate, makeSubstitution, newTypeVar, unify} from './helper';
import {Context, Expression, MonoType, PolyType, makeContext} from './models';

export const W = (typEnv: Context, expr: Expression): [Substitution, MonoType] => {
if (expr.type === 'var') {
const value = typEnv[expr.x];
if (value === undefined) throw new Error(`Undefined variable: ${expr.x}`);
if (value === undefined) throw new InferenceError(`Undefined variable: ${expr.x}`);
return [makeSubstitution({}), instantiate(value)];
}

Expand Down Expand Up @@ -78,5 +79,5 @@ export const W = (typEnv: Context, expr: Expression): [Substitution, MonoType] =
);
return [s2(s1), t2];
}
throw new Error('Unknown expression type');
throw new InferenceError('Unknown expression type');
};

0 comments on commit cf35b30

Please sign in to comment.