branching
This commit is contained in:
parent
7cc3ef5fa1
commit
d39cf84965
@ -28,6 +28,117 @@ const addUnaryIntegerOperationsTo = (env: Environment) => {
|
|||||||
return env;
|
return env;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addNumberComparisonOperationsTo = (env: Environment) => {
|
||||||
|
const comparisonOperationsSignatures: DenotableFunctionSignature[] = [
|
||||||
|
{
|
||||||
|
arguments: [
|
||||||
|
['real', 'int'],
|
||||||
|
['real', 'int'],
|
||||||
|
],
|
||||||
|
return: 'bool',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { name, fn } of [
|
||||||
|
{ 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: comparisonOperationsSignatures,
|
||||||
|
body: ({ value: a }: Denotable, { value: b }: Denotable) =>
|
||||||
|
fn(a as number, b as number),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return env;
|
||||||
|
};
|
||||||
|
|
||||||
|
const addEqualityOperationsTo = (env: Environment) => {
|
||||||
|
const equalityOperationSignatures: DenotableFunctionSignature[] = [
|
||||||
|
{
|
||||||
|
arguments: ['int', 'int'],
|
||||||
|
return: 'bool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arguments: ['real', 'real'],
|
||||||
|
return: 'bool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arguments: ['bool', 'bool'],
|
||||||
|
return: 'bool',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arguments: ['string', 'string'],
|
||||||
|
return: 'bool',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { name, fn } of [
|
||||||
|
{
|
||||||
|
name: '==',
|
||||||
|
fn: (a: number | string, b: number | string) => (a === b ? 1 : 0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '!=',
|
||||||
|
fn: (a: number | string, b: number | string) => (a !== b ? 1 : 0),
|
||||||
|
},
|
||||||
|
]) {
|
||||||
|
env.set(name, {
|
||||||
|
type: 'function',
|
||||||
|
value: {
|
||||||
|
signatures: equalityOperationSignatures,
|
||||||
|
body: ({ value: a }: Denotable, { value: b }: Denotable) =>
|
||||||
|
fn(a as number | string, b as number | string),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return env;
|
||||||
|
};
|
||||||
|
|
||||||
|
const addBooleanAlgebraOperationsTo = (env: Environment) => {
|
||||||
|
const binaryBooleanOps: DenotableFunctionSignature[] = [
|
||||||
|
{
|
||||||
|
arguments: ['bool', 'bool'],
|
||||||
|
return: 'bool',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { name, fn } of [
|
||||||
|
{ name: '||', fn: (a: number, b: number) => (a === 1 || b === 1 ? 1 : 0) },
|
||||||
|
{ name: '&&', fn: (a: number, b: number) => (a === 1 && b === 1 ? 1 : 0) },
|
||||||
|
]) {
|
||||||
|
env.set(name, {
|
||||||
|
type: 'function',
|
||||||
|
value: {
|
||||||
|
signatures: binaryBooleanOps,
|
||||||
|
body: ({ value: a }: Denotable, { value: b }: Denotable) =>
|
||||||
|
fn(a as number, b as number),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
env.set('!', {
|
||||||
|
type: 'function',
|
||||||
|
value: {
|
||||||
|
signatures: [
|
||||||
|
{
|
||||||
|
arguments: ['bool'],
|
||||||
|
return: 'bool',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: ({ value }: Denotable) => (value === 1 ? 0 : 1),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return env;
|
||||||
|
};
|
||||||
|
|
||||||
const addBinaryIntegerOperationsTo = (env: Environment) => {
|
const addBinaryIntegerOperationsTo = (env: Environment) => {
|
||||||
const binaryIntegerOperationSignatures: DenotableFunctionSignature[] = [
|
const binaryIntegerOperationSignatures: DenotableFunctionSignature[] = [
|
||||||
{
|
{
|
||||||
@ -42,12 +153,6 @@ const addBinaryIntegerOperationsTo = (env: Environment) => {
|
|||||||
{ 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 },
|
||||||
{ 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, {
|
env.set(name, {
|
||||||
type: 'function',
|
type: 'function',
|
||||||
@ -102,5 +207,8 @@ export const putBuiltinsOnEnvironemtn = (env: Environment) => {
|
|||||||
addBinaryArithmeticOperationsTo,
|
addBinaryArithmeticOperationsTo,
|
||||||
addBinaryIntegerOperationsTo,
|
addBinaryIntegerOperationsTo,
|
||||||
addUnaryIntegerOperationsTo,
|
addUnaryIntegerOperationsTo,
|
||||||
|
addNumberComparisonOperationsTo,
|
||||||
|
addBooleanAlgebraOperationsTo,
|
||||||
|
addEqualityOperationsTo,
|
||||||
].reduce((acc, builtinsAdder) => builtinsAdder(acc), env);
|
].reduce((acc, builtinsAdder) => builtinsAdder(acc), env);
|
||||||
};
|
};
|
||||||
|
@ -21,6 +21,7 @@ export type DenotableType =
|
|||||||
| 'null'
|
| 'null'
|
||||||
| 'int'
|
| 'int'
|
||||||
| 'real'
|
| 'real'
|
||||||
|
| 'bool'
|
||||||
| 'string'
|
| 'string'
|
||||||
| 'bytearray'
|
| 'bytearray'
|
||||||
| 'function'
|
| 'function'
|
||||||
|
@ -29,7 +29,6 @@ const evaluateValue = (
|
|||||||
return { type: 'int', value: value.int };
|
return { type: 'int', value: value.int };
|
||||||
}
|
}
|
||||||
if ('name' in value) {
|
if ('name' in value) {
|
||||||
logger.debug(`Evaluating variable: ${value.name}`);
|
|
||||||
return env.get(value.name);
|
return env.get(value.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,10 +41,6 @@ const evaluatePrimitiveOperation = (
|
|||||||
logger: TracingLogger,
|
logger: TracingLogger,
|
||||||
) => {
|
) => {
|
||||||
const { opr, operands, resultBindings, continuations } = primitiveOperation;
|
const { opr, operands, resultBindings, continuations } = primitiveOperation;
|
||||||
if (operands.length !== 2) {
|
|
||||||
throw new BadArgumentError('Primitive operations must have 2 operands');
|
|
||||||
}
|
|
||||||
|
|
||||||
const operandValues = operands.map(operand =>
|
const operandValues = operands.map(operand =>
|
||||||
evaluateValue(operand, env, logger.createChild('evaluteValue')),
|
evaluateValue(operand, env, logger.createChild('evaluteValue')),
|
||||||
);
|
);
|
||||||
@ -56,15 +51,46 @@ const evaluatePrimitiveOperation = (
|
|||||||
continuationEnvironment.set(name, result);
|
continuationEnvironment.set(name, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// return the result of the last continuation
|
if (result.type === 'bool') {
|
||||||
return continuations.reduce((_, continuation, i) => {
|
if (continuations.length > 2) {
|
||||||
const childLogger = logger.createChild(`continuation[${i}]`);
|
throw new BadArgumentError(
|
||||||
|
`Expected <= 2 continuations for boolean result, got ${continuations.length}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (continuations.length !== 2) {
|
||||||
|
logger.warn(
|
||||||
|
`Expected 2 continuations for boolean result, got ContinuationLength=(${continuations.length})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [trueContinuation, falseContinuation] = continuations;
|
||||||
|
const childLogger = logger.createChild('continuation[true]');
|
||||||
|
const continuation = result.value ? trueContinuation : falseContinuation;
|
||||||
|
return evaluteContinuationExpression(
|
||||||
|
continuation,
|
||||||
|
continuationEnvironment,
|
||||||
|
childLogger,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (continuations.length > 1) {
|
||||||
|
throw new BadArgumentError(
|
||||||
|
`Expected <= 1 continuations for non-boolean result, got ${continuations.length}`,
|
||||||
|
);
|
||||||
|
} else if (continuations.length === 0) {
|
||||||
|
logger.warn(
|
||||||
|
`!! Expected 1 continuation in continuation list... implicitly returning result but PLEASE NOTE this is technically undefined behavior !!`,
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [continuation] = continuations;
|
||||||
|
const childLogger = logger.createChild('continuation');
|
||||||
return evaluteContinuationExpression(
|
return evaluteContinuationExpression(
|
||||||
continuation,
|
continuation,
|
||||||
continuationEnvironment,
|
continuationEnvironment,
|
||||||
childLogger,
|
childLogger,
|
||||||
);
|
);
|
||||||
}, result);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const evaluteContinuationExpression = (
|
const evaluteContinuationExpression = (
|
||||||
|
@ -223,6 +223,7 @@ Value
|
|||||||
/ LabelStatement
|
/ LabelStatement
|
||||||
/ IntStatement
|
/ IntStatement
|
||||||
/ RealStatement
|
/ RealStatement
|
||||||
|
/ BoolStatement
|
||||||
/ StringStatement
|
/ StringStatement
|
||||||
|
|
||||||
VarStatement = VAR _ ident:Identifier { return ident; }
|
VarStatement = VAR _ ident:Identifier { return ident; }
|
||||||
@ -233,6 +234,8 @@ IntStatement = INT _ int:Integer { return int; }
|
|||||||
|
|
||||||
RealStatement = REAL _ real:Real { return real; }
|
RealStatement = REAL _ real:Real { return real; }
|
||||||
|
|
||||||
|
BoolStatement = BOOL _ bool:Integer { return bool; }
|
||||||
|
|
||||||
StringStatement = STRING _ string:QuotedString { return string; }
|
StringStatement = STRING _ string:QuotedString { return string; }
|
||||||
|
|
||||||
AccessStatement
|
AccessStatement
|
||||||
@ -286,6 +289,7 @@ ComparisonOperation
|
|||||||
/ ">"
|
/ ">"
|
||||||
/ "<"
|
/ "<"
|
||||||
/ "||"
|
/ "||"
|
||||||
|
/ "&&"
|
||||||
|
|
||||||
Integer = digits:("-"? [0-9]+) !"." { return { int: parseInt(digits.join(''), 10) }; }
|
Integer = digits:("-"? [0-9]+) !"." { return { int: parseInt(digits.join(''), 10) }; }
|
||||||
|
|
||||||
@ -317,6 +321,8 @@ INT = "INT"
|
|||||||
|
|
||||||
REAL = "REAL"
|
REAL = "REAL"
|
||||||
|
|
||||||
|
BOOL = "BOOL"
|
||||||
|
|
||||||
STRING = "STRING"
|
STRING = "STRING"
|
||||||
|
|
||||||
APP = "APP"
|
APP = "APP"
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -18,6 +18,13 @@ test('Add (1 real) and (3 int) -> result => (real 1 - result) = -3 done with cor
|
|||||||
expect(result).toEqual({ type: 'real', value: -3 });
|
expect(result).toEqual({ type: 'real', value: -3 });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Branching', async () => {
|
||||||
|
const ast = peggyParse(await TestPrograms.Branching);
|
||||||
|
|
||||||
|
const result = await evaluate(ast, testingLogger);
|
||||||
|
expect(result).toEqual({ type: 'real', value: 2 });
|
||||||
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
test('String equality', async () => {
|
test('String equality', async () => {
|
||||||
const ast = peggyParse(await TestPrograms.StringEquality);
|
const ast = peggyParse(await TestPrograms.StringEquality);
|
||||||
|
10
test/programs/branching.cps
Normal file
10
test/programs/branching.cps
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
PRIMOP(>=, [REAL 0, REAL 1], [resultFalse], [
|
||||||
|
PRIMOP(+, [REAL 2, REAL 4], [result], []),
|
||||||
|
PRIMOP(<=, [INT 1, REAL 1], [resultTrue], [
|
||||||
|
PRIMOP(&&, [VAR resultTrue, VAR resultFalse], [fin], [
|
||||||
|
PRIMOP(-, [REAL 1, REAL 1], [result], []),
|
||||||
|
PRIMOP(+, [REAL 1, REAL 1], [twoWhenFinIsFalse], [])
|
||||||
|
]),
|
||||||
|
PRIMOP(-, [REAL 1, REAL 1], [result], [])
|
||||||
|
])
|
||||||
|
])
|
@ -7,6 +7,9 @@ export namespace TestPrograms {
|
|||||||
export const PrimopScope = Bun.file(
|
export const PrimopScope = Bun.file(
|
||||||
join(import.meta.dir + '/primop-scope.cps'),
|
join(import.meta.dir + '/primop-scope.cps'),
|
||||||
).text();
|
).text();
|
||||||
|
export const Branching = Bun.file(
|
||||||
|
join(import.meta.dir + '/branching.cps'),
|
||||||
|
).text();
|
||||||
export const StringEquality = Bun.file(
|
export const StringEquality = Bun.file(
|
||||||
join(import.meta.dir + '/string-equal.cps'),
|
join(import.meta.dir + '/string-equal.cps'),
|
||||||
).text();
|
).text();
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
import { expect, test } from 'bun:test';
|
import { expect, test } from 'bun:test';
|
||||||
import { TestPrograms } from './programs';
|
|
||||||
import { peggyParse } from '@/parser';
|
|
||||||
import {
|
import {
|
||||||
evaluate,
|
|
||||||
type DenotableFunctionSignature,
|
type DenotableFunctionSignature,
|
||||||
denotableTypesEquivalent,
|
denotableTypesEquivalent,
|
||||||
matchSignature,
|
matchSignature,
|
||||||
} from '@/interpreter';
|
} from '@/interpreter';
|
||||||
import { testingLogger } from './logger';
|
|
||||||
|
|
||||||
test('simple denotable types are equivalent', () => {
|
test('simple denotable types are equivalent', () => {
|
||||||
expect(denotableTypesEquivalent('int', 'int')).toBe(true);
|
expect(denotableTypesEquivalent('int', 'int')).toBe(true);
|
||||||
|
Loading…
Reference in New Issue
Block a user