builtin_match_signatures #1
@ -1,106 +0,0 @@
|
|||||||
import { BadArgumentError } from '@/utils';
|
|
||||||
import type { DenotableValueType } from '.';
|
|
||||||
|
|
||||||
export type Signature = Array<Array<DenotableValueType | Signature>>;
|
|
||||||
|
|
||||||
export const primitiveOperationSignatures: Record<string, Signature> = {
|
|
||||||
'+': [
|
|
||||||
['int', 'int', 'int'],
|
|
||||||
['real', 'real', 'real'],
|
|
||||||
['int', 'real', 'real'],
|
|
||||||
['real', 'int', 'real'],
|
|
||||||
],
|
|
||||||
'-': [
|
|
||||||
['int', 'int', 'int'],
|
|
||||||
['real', 'real', 'real'],
|
|
||||||
['int', 'real', 'real'],
|
|
||||||
['real', 'int', 'real'],
|
|
||||||
],
|
|
||||||
'*': [
|
|
||||||
['int', 'int', 'int'],
|
|
||||||
['real', 'real', 'real'],
|
|
||||||
['int', 'real', 'real'],
|
|
||||||
['real', 'int', 'real'],
|
|
||||||
],
|
|
||||||
'/': [
|
|
||||||
['int', 'int', 'int'],
|
|
||||||
['real', 'real', 'real'],
|
|
||||||
['int', 'real', 'real'],
|
|
||||||
['real', 'int', 'real'],
|
|
||||||
],
|
|
||||||
'%': [['int', 'int', 'int']],
|
|
||||||
'**': [
|
|
||||||
['int', 'int', 'int'],
|
|
||||||
['real', 'real', 'real'],
|
|
||||||
['int', 'real', 'real'],
|
|
||||||
['real', 'int', 'real'],
|
|
||||||
],
|
|
||||||
'>>': [['int', 'int', 'int']],
|
|
||||||
'<<': [['int', 'int', 'int']],
|
|
||||||
'&': [['int', 'int', 'int']],
|
|
||||||
'|': [['int', 'int', 'int']],
|
|
||||||
'^': [['int', 'int', 'int']],
|
|
||||||
'==': [
|
|
||||||
['int', 'int', 'int'],
|
|
||||||
['real', 'real', 'int'],
|
|
||||||
['string', 'string', 'int'],
|
|
||||||
],
|
|
||||||
'!=': [
|
|
||||||
['int', 'int', 'int'],
|
|
||||||
['real', 'real', 'int'],
|
|
||||||
['string', 'string', 'int'],
|
|
||||||
],
|
|
||||||
'<': [
|
|
||||||
['int', 'int', 'int'],
|
|
||||||
['real', 'real', 'int'],
|
|
||||||
],
|
|
||||||
'>': [
|
|
||||||
['int', 'int', 'int'],
|
|
||||||
['real', 'real', 'int'],
|
|
||||||
],
|
|
||||||
'<=': [
|
|
||||||
['int', 'int', 'int'],
|
|
||||||
['real', 'real', 'int'],
|
|
||||||
],
|
|
||||||
'>=': [
|
|
||||||
['int', 'int', 'int'],
|
|
||||||
['real', 'real', 'int'],
|
|
||||||
],
|
|
||||||
'&&': [['int', 'int', 'int']],
|
|
||||||
'||': [['int', 'int', 'int']],
|
|
||||||
'!': [['int', 'int']],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getResultingType = (
|
|
||||||
opr: string,
|
|
||||||
types: DenotableValueType[],
|
|
||||||
): DenotableValueType | Signature => {
|
|
||||||
const signature = primitiveOperationSignatures[opr];
|
|
||||||
if (!signature) {
|
|
||||||
throw new BadArgumentError(`Invalid operation: ${opr}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const resultingType = signature.find(sig => {
|
|
||||||
if (sig.length !== types.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sig.every((type, i) => {
|
|
||||||
if (Array.isArray(type)) {
|
|
||||||
return getResultingType(
|
|
||||||
opr,
|
|
||||||
types.map((t, j) => (i === j ? t : 'int')),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return type === 'int' || type === types[i];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!resultingType) {
|
|
||||||
throw new TypeError(
|
|
||||||
`Invalid types for operation ${opr}: ${types.join(', ')}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultingType as DenotableValueType | Signature;
|
|
||||||
};
|
|
94
src/interpreter/builtins.ts
Normal file
94
src/interpreter/builtins.ts
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import { type DenotableFunctionSignature, Environment } from '.';
|
||||||
|
|
||||||
|
const addUnaryIntegerOperationsTo = (env: Environment) => {
|
||||||
|
const unaryIntegerOperationSignatures: DenotableFunctionSignature[] = [
|
||||||
|
{
|
||||||
|
arguments: ['int'],
|
||||||
|
return: 'int',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { name, fn } of [
|
||||||
|
{ name: '~', fn: (a: number) => ~a },
|
||||||
|
{ name: '!', fn: (a: number) => (!a ? 1 : 0) },
|
||||||
|
]) {
|
||||||
|
env.set(name, {
|
||||||
|
type: 'function',
|
||||||
|
value: { signatures: unaryIntegerOperationSignatures, body: fn },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return env;
|
||||||
|
};
|
||||||
|
|
||||||
|
const addBinaryIntegerOperationsTo = (env: Environment) => {
|
||||||
|
const binaryIntegerOperationSignatures: DenotableFunctionSignature[] = [
|
||||||
|
{
|
||||||
|
arguments: ['int', 'int'],
|
||||||
|
return: 'int',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { name, fn } of [
|
||||||
|
{ name: '%', fn: (a: number, b: number) => a % b },
|
||||||
|
{ name: '>>', fn: (a: number, b: number) => a >> b },
|
||||||
|
{ name: '<<', fn: (a: number, b: number) => a << b },
|
||||||
|
{ name: '|', fn: (a: number, b: number) => a | b },
|
||||||
|
{ name: '^', fn: (a: number, b: number) => a ^ b },
|
||||||
|
{ name: '&&', fn: (a: number, b: number) => (a && b ? 1 : 0) },
|
||||||
|
{ name: '<=', fn: (a: number, b: number) => (a <= b ? 1 : 0) },
|
||||||
|
{ name: '<', fn: (a: number, b: number) => (a < b ? 1 : 0) },
|
||||||
|
{ name: '>', fn: (a: number, b: number) => (a > b ? 1 : 0) },
|
||||||
|
{ name: '>=', fn: (a: number, b: number) => (a >= b ? 1 : 0) },
|
||||||
|
{ name: '||', fn: (a: number, b: number) => (a || b ? 1 : 0) },
|
||||||
|
{ name: '==', fn: (a: number, b: number) => (a == b ? 1 : 0) },
|
||||||
|
]) {
|
||||||
|
env.set(name, {
|
||||||
|
type: 'function',
|
||||||
|
value: { signatures: binaryIntegerOperationSignatures, body: fn },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return env;
|
||||||
|
};
|
||||||
|
|
||||||
|
const addBinaryArithmeticOperationsTo = (env: Environment) => {
|
||||||
|
const binaryArithmeticSignatures: DenotableFunctionSignature[] = [
|
||||||
|
{
|
||||||
|
arguments: ['int', 'int'],
|
||||||
|
return: 'int',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arguments: [
|
||||||
|
['int', 'real'],
|
||||||
|
['int', 'real'],
|
||||||
|
],
|
||||||
|
return: 'real',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { name, fn } of [
|
||||||
|
{ name: '+', fn: (a: number, b: number) => a + b },
|
||||||
|
{ name: '-', fn: (a: number, b: number) => a - b },
|
||||||
|
{ name: '*', fn: (a: number, b: number) => a * b },
|
||||||
|
{ name: '/', fn: (a: number, b: number) => a / b },
|
||||||
|
{ name: '**', fn: (a: number, b: number) => a ** b },
|
||||||
|
]) {
|
||||||
|
env.set(name, {
|
||||||
|
type: 'function',
|
||||||
|
value: { signatures: binaryArithmeticSignatures, body: fn },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return env;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const environmentWithBuiltins = () => {
|
||||||
|
const environment = new Environment();
|
||||||
|
|
||||||
|
return [
|
||||||
|
addBinaryArithmeticOperationsTo,
|
||||||
|
addBinaryIntegerOperationsTo,
|
||||||
|
addUnaryIntegerOperationsTo,
|
||||||
|
].reduce((acc, builtinsAdder) => builtinsAdder(acc), environment);
|
||||||
|
};
|
71
src/interpreter/denotable.ts
Normal file
71
src/interpreter/denotable.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import type { Identifier } from '@/parser';
|
||||||
|
|
||||||
|
export type UnionDenotableType =
|
||||||
|
| Array<DenotableType | DenotableFunctionSignature>
|
||||||
|
| DenotableType
|
||||||
|
| DenotableFunctionSignature;
|
||||||
|
|
||||||
|
export type DenotableFunctionSignature = {
|
||||||
|
arguments: Array<UnionDenotableType>;
|
||||||
|
return: UnionDenotableType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DenotableFunction = {
|
||||||
|
signatures: Array<DenotableFunctionSignature>;
|
||||||
|
body: Function;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DenotableType =
|
||||||
|
| 'null'
|
||||||
|
| 'int'
|
||||||
|
| 'real'
|
||||||
|
| 'string'
|
||||||
|
| 'bytearray'
|
||||||
|
| 'function'
|
||||||
|
| 'reference';
|
||||||
|
|
||||||
|
export type DenotableValue =
|
||||||
|
| null
|
||||||
|
| number
|
||||||
|
| string
|
||||||
|
| Uint8Array
|
||||||
|
| DenotableFunction
|
||||||
|
| Identifier;
|
||||||
|
|
||||||
|
export type Denotable = {
|
||||||
|
type: DenotableType;
|
||||||
|
value: DenotableValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const functionSignaturesEqual = (
|
||||||
|
a: DenotableFunctionSignature,
|
||||||
|
b: DenotableFunctionSignature,
|
||||||
|
): boolean => {
|
||||||
|
if (a.arguments.length !== b.arguments.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof a.return !== typeof b.return) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof a.return === 'object' &&
|
||||||
|
typeof b.return === 'object' &&
|
||||||
|
'return' in a.return &&
|
||||||
|
'return' in b.return
|
||||||
|
) {
|
||||||
|
return functionSignaturesEqual(a.return, b.return);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const matchSignature = (
|
||||||
|
args: Array<DenotableFunction | DenotableType>,
|
||||||
|
signatures: Array<DenotableFunctionSignature>,
|
||||||
|
): DenotableFunctionSignature | undefined => {
|
||||||
|
return signatures.find(signature => {
|
||||||
|
if (args.length !== signature.arguments.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@ -1,8 +1,14 @@
|
|||||||
import { UnknownSymbolError } from '@/utils';
|
import { UnknownSymbolError, InvalidType } from '@/utils';
|
||||||
import type { DenotableValue } from '.';
|
import type {
|
||||||
|
Denotable,
|
||||||
|
DenotableFunction,
|
||||||
|
DenotableFunctionSignature,
|
||||||
|
DenotableType,
|
||||||
|
DenotableValue,
|
||||||
|
} from '.';
|
||||||
|
|
||||||
export class Environment {
|
export class Environment {
|
||||||
private scope: Map<string, DenotableValue>;
|
private scope: Map<string, Denotable>;
|
||||||
private parent: Environment | null;
|
private parent: Environment | null;
|
||||||
|
|
||||||
constructor(parent: Environment | null = null) {
|
constructor(parent: Environment | null = null) {
|
||||||
@ -10,13 +16,13 @@ export class Environment {
|
|||||||
this.scope = new Map();
|
this.scope = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
public set(name: string, value: DenotableValue) {
|
public set(name: string, value: Denotable) {
|
||||||
this.scope.set(name, value);
|
this.scope.set(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get(name: string): DenotableValue {
|
public get(name: string): Denotable {
|
||||||
if (this.scope.has(name)) {
|
if (this.scope.has(name)) {
|
||||||
return this.scope.get(name);
|
return this.scope.get(name)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.parent) {
|
if (this.parent) {
|
||||||
@ -41,4 +47,13 @@ export class Environment {
|
|||||||
public createChild(): Environment {
|
public createChild(): Environment {
|
||||||
return new Environment(this);
|
return new Environment(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public apply(name: string, args: Denotable[]): Denotable {
|
||||||
|
const fn = this.get(name);
|
||||||
|
if (typeof fn.value !== 'object' || !fn.value || !('body' in fn.value)) {
|
||||||
|
throw new InvalidType(name + ' is not a valid function');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { type: 'real', value: 0 };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,3 @@
|
|||||||
export type DenotableValueType =
|
export * from './denotable';
|
||||||
| 'null'
|
|
||||||
| 'int'
|
|
||||||
| 'real'
|
|
||||||
| 'string'
|
|
||||||
| 'function'
|
|
||||||
| 'reference';
|
|
||||||
export type DenotableValue = { type: DenotableValueType; value: any };
|
|
||||||
|
|
||||||
export * from './environment';
|
export * from './environment';
|
||||||
export * from './interpreter';
|
export * from './interpreter';
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
type Program,
|
type Program,
|
||||||
type Value,
|
type Value,
|
||||||
} from '@/parser';
|
} from '@/parser';
|
||||||
import { Environment, type DenotableValue } from '.';
|
import { Environment, type Denotable } from '.';
|
||||||
import {
|
import {
|
||||||
BadArgumentError,
|
BadArgumentError,
|
||||||
InvalidStateError,
|
InvalidStateError,
|
||||||
@ -16,7 +16,7 @@ const evaluateValue = (
|
|||||||
value: Value,
|
value: Value,
|
||||||
env: Environment,
|
env: Environment,
|
||||||
logger: TracingLogger,
|
logger: TracingLogger,
|
||||||
): DenotableValue => {
|
): Denotable => {
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
return { type: 'string', value };
|
return { type: 'string', value };
|
||||||
}
|
}
|
||||||
@ -49,50 +49,8 @@ const evaluatePrimitiveOperation = (
|
|||||||
evaluateValue(operand, env, logger.createChild('evaluteValue')),
|
evaluateValue(operand, env, logger.createChild('evaluteValue')),
|
||||||
);
|
);
|
||||||
|
|
||||||
const rightReducingOperations = {
|
let result: Denotable = { type: 'null', value: null };
|
||||||
'+': (acc: number, operand: { value: number }) => acc + operand.value,
|
|
||||||
'-': (acc: number, operand: { value: number }) => acc - operand.value,
|
|
||||||
'*': (acc: number, operand: { value: number }) => acc * operand.value,
|
|
||||||
};
|
|
||||||
const leftReducingOperations = {
|
|
||||||
'/': (acc: number, operand: { value: number }) => acc / operand.value,
|
|
||||||
'%': (acc: number, operand: { value: number }) => acc % operand.value,
|
|
||||||
'**': (acc: number, operand: { value: number }) => acc ** operand.value,
|
|
||||||
'>>': (acc: number, operand: { value: number }) => acc >> operand.value,
|
|
||||||
'<<': (acc: number, operand: { value: number }) => acc << operand.value,
|
|
||||||
'&': (acc: number, operand: { value: number }) => acc & operand.value,
|
|
||||||
'|': (acc: number, operand: { value: number }) => acc | operand.value,
|
|
||||||
'^': (acc: number, operand: { value: number }) => acc ^ operand.value,
|
|
||||||
};
|
|
||||||
const someNumberIsReal = operandValues.some(({ type }) => type === 'real');
|
|
||||||
|
|
||||||
let result: DenotableValue = { type: 'null', value: null };
|
|
||||||
const continuationEnvironment = env.createChild();
|
const continuationEnvironment = env.createChild();
|
||||||
if (opr in rightReducingOperations) {
|
|
||||||
logger.debug('Evaluating right reducing operation: ' + opr);
|
|
||||||
const sum = operandValues.reduce(
|
|
||||||
rightReducingOperations[opr as keyof typeof rightReducingOperations],
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
result = { type: someNumberIsReal ? 'real' : 'int', value: sum };
|
|
||||||
|
|
||||||
for (const binding of resultBindings) {
|
|
||||||
continuationEnvironment.set(binding.name, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (opr in leftReducingOperations) {
|
|
||||||
logger.debug('Evaluating left reducing operation: ' + opr);
|
|
||||||
const [first, ...rest] = operandValues;
|
|
||||||
const sum = rest.reduce(
|
|
||||||
leftReducingOperations[opr as keyof typeof leftReducingOperations],
|
|
||||||
first.value,
|
|
||||||
);
|
|
||||||
result = { type: someNumberIsReal ? 'real' : 'int', value: sum };
|
|
||||||
|
|
||||||
for (const binding of resultBindings) {
|
|
||||||
continuationEnvironment.set(binding.name, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the result of the last continuation
|
// return the result of the last continuation
|
||||||
return continuations.reduce((_, continuation, i) => {
|
return continuations.reduce((_, continuation, i) => {
|
||||||
@ -109,7 +67,7 @@ const evaluteContinuationExpression = (
|
|||||||
expr: ContinuationExpression,
|
expr: ContinuationExpression,
|
||||||
env: Environment,
|
env: Environment,
|
||||||
logger: TracingLogger,
|
logger: TracingLogger,
|
||||||
): DenotableValue => {
|
): Denotable => {
|
||||||
if ('primitiveOperation' in expr) {
|
if ('primitiveOperation' in expr) {
|
||||||
logger.debug('Evaluating primitive operation');
|
logger.debug('Evaluating primitive operation');
|
||||||
return evaluatePrimitiveOperation(
|
return evaluatePrimitiveOperation(
|
||||||
@ -146,7 +104,7 @@ const evaluteContinuationExpression = (
|
|||||||
export const evaluate = async (
|
export const evaluate = async (
|
||||||
ast: Program,
|
ast: Program,
|
||||||
logger: TracingLogger,
|
logger: TracingLogger,
|
||||||
): Promise<DenotableValue> => {
|
): Promise<Denotable> => {
|
||||||
const environment = new Environment();
|
const environment = new Environment();
|
||||||
|
|
||||||
return ast.reduce((_, continuation, i) => {
|
return ast.reduce((_, continuation, i) => {
|
||||||
|
@ -6,4 +6,4 @@ export class InvalidStateError extends Error {}
|
|||||||
|
|
||||||
export class BadArgumentError extends Error {}
|
export class BadArgumentError extends Error {}
|
||||||
|
|
||||||
export class TypeError extends Error {}
|
export class InvalidType extends Error {}
|
||||||
|
@ -10,3 +10,19 @@ test('Add (1 real) and (3 int) => (4 real)', async () => {
|
|||||||
const result = await evaluate(ast, testingLogger);
|
const result = await evaluate(ast, testingLogger);
|
||||||
expect(result).toEqual({ type: 'real', value: 4 });
|
expect(result).toEqual({ type: 'real', value: 4 });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
test('String equality', async () => {
|
||||||
|
const ast = peggyParse(await TestPrograms.StringEquality);
|
||||||
|
|
||||||
|
const result = await evaluate(ast, testingLogger);
|
||||||
|
expect(result).toEqual({ type: 'int', value: 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('String inequality', async () => {
|
||||||
|
const ast = peggyParse(await TestPrograms.StringInEquality);
|
||||||
|
|
||||||
|
const result = await evaluate(ast, testingLogger);
|
||||||
|
expect(result).toEqual({ type: 'int', value: 0 });
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
@ -4,4 +4,10 @@ export namespace TestPrograms {
|
|||||||
export const AddOneThree = Bun.file(
|
export const AddOneThree = Bun.file(
|
||||||
join(import.meta.dir + '/add-1-3.cps'),
|
join(import.meta.dir + '/add-1-3.cps'),
|
||||||
).text();
|
).text();
|
||||||
|
export const StringEquality = Bun.file(
|
||||||
|
join(import.meta.dir + '/string-equal.cps'),
|
||||||
|
).text();
|
||||||
|
export const StringInEquality = Bun.file(
|
||||||
|
join(import.meta.dir + '/string-unequal.cps'),
|
||||||
|
).text();
|
||||||
}
|
}
|
||||||
|
1
test/programs/string-equal.cps
Normal file
1
test/programs/string-equal.cps
Normal file
@ -0,0 +1 @@
|
|||||||
|
PRIMOP(==, ["asdf", "asdf"], [result], [])
|
1
test/programs/string-unequal.cps
Normal file
1
test/programs/string-unequal.cps
Normal file
@ -0,0 +1 @@
|
|||||||
|
PRIMOP(==, ["asdfasdf", "asdf"], [result], [])
|
41
test/signature_match.spec.ts
Normal file
41
test/signature_match.spec.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { expect, test } from 'bun:test';
|
||||||
|
import { TestPrograms } from './programs';
|
||||||
|
import { peggyParse } from '@/parser';
|
||||||
|
import {
|
||||||
|
evaluate,
|
||||||
|
type DenotableFunctionSignature,
|
||||||
|
matchSignature,
|
||||||
|
} from '@/interpreter';
|
||||||
|
import { testingLogger } from './logger';
|
||||||
|
|
||||||
|
test('matches simple signatures', async () => {
|
||||||
|
const simpleSignature: DenotableFunctionSignature[] = [
|
||||||
|
{
|
||||||
|
arguments: ['int'],
|
||||||
|
return: 'int',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(matchSignature(['int'], simpleSignature)).toEqual(simpleSignature[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('finds first match', async () => {
|
||||||
|
const simpleSignature: DenotableFunctionSignature[] = [
|
||||||
|
{
|
||||||
|
arguments: ['int', 'int'],
|
||||||
|
return: 'int',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arguments: [['int', 'real'], 'int'],
|
||||||
|
return: 'real',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(matchSignature(['int', 'int'], simpleSignature)).toEqual(
|
||||||
|
simpleSignature[0],
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(matchSignature(['real', 'int'], simpleSignature)).toEqual(
|
||||||
|
simpleSignature[1],
|
||||||
|
);
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user