branching
This commit is contained in:
parent
7cc3ef5fa1
commit
d39cf84965
@ -28,6 +28,117 @@ const addUnaryIntegerOperationsTo = (env: Environment) => {
|
||||
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 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 ? 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',
|
||||
@ -102,5 +207,8 @@ export const putBuiltinsOnEnvironemtn = (env: Environment) => {
|
||||
addBinaryArithmeticOperationsTo,
|
||||
addBinaryIntegerOperationsTo,
|
||||
addUnaryIntegerOperationsTo,
|
||||
addNumberComparisonOperationsTo,
|
||||
addBooleanAlgebraOperationsTo,
|
||||
addEqualityOperationsTo,
|
||||
].reduce((acc, builtinsAdder) => builtinsAdder(acc), env);
|
||||
};
|
||||
|
@ -21,6 +21,7 @@ export type DenotableType =
|
||||
| 'null'
|
||||
| 'int'
|
||||
| 'real'
|
||||
| 'bool'
|
||||
| 'string'
|
||||
| 'bytearray'
|
||||
| 'function'
|
||||
|
@ -29,7 +29,6 @@ const evaluateValue = (
|
||||
return { type: 'int', value: value.int };
|
||||
}
|
||||
if ('name' in value) {
|
||||
logger.debug(`Evaluating variable: ${value.name}`);
|
||||
return env.get(value.name);
|
||||
}
|
||||
|
||||
@ -42,10 +41,6 @@ const evaluatePrimitiveOperation = (
|
||||
logger: TracingLogger,
|
||||
) => {
|
||||
const { opr, operands, resultBindings, continuations } = primitiveOperation;
|
||||
if (operands.length !== 2) {
|
||||
throw new BadArgumentError('Primitive operations must have 2 operands');
|
||||
}
|
||||
|
||||
const operandValues = operands.map(operand =>
|
||||
evaluateValue(operand, env, logger.createChild('evaluteValue')),
|
||||
);
|
||||
@ -56,15 +51,46 @@ const evaluatePrimitiveOperation = (
|
||||
continuationEnvironment.set(name, result);
|
||||
}
|
||||
|
||||
// return the result of the last continuation
|
||||
return continuations.reduce((_, continuation, i) => {
|
||||
const childLogger = logger.createChild(`continuation[${i}]`);
|
||||
if (result.type === 'bool') {
|
||||
if (continuations.length > 2) {
|
||||
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,
|
||||
);
|
||||
}, result);
|
||||
}
|
||||
|
||||
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(
|
||||
continuation,
|
||||
continuationEnvironment,
|
||||
childLogger,
|
||||
);
|
||||
};
|
||||
|
||||
const evaluteContinuationExpression = (
|
||||
|
@ -223,6 +223,7 @@ Value
|
||||
/ LabelStatement
|
||||
/ IntStatement
|
||||
/ RealStatement
|
||||
/ BoolStatement
|
||||
/ StringStatement
|
||||
|
||||
VarStatement = VAR _ ident:Identifier { return ident; }
|
||||
@ -233,6 +234,8 @@ IntStatement = INT _ int:Integer { return int; }
|
||||
|
||||
RealStatement = REAL _ real:Real { return real; }
|
||||
|
||||
BoolStatement = BOOL _ bool:Integer { return bool; }
|
||||
|
||||
StringStatement = STRING _ string:QuotedString { return string; }
|
||||
|
||||
AccessStatement
|
||||
@ -286,6 +289,7 @@ ComparisonOperation
|
||||
/ ">"
|
||||
/ "<"
|
||||
/ "||"
|
||||
/ "&&"
|
||||
|
||||
Integer = digits:("-"? [0-9]+) !"." { return { int: parseInt(digits.join(''), 10) }; }
|
||||
|
||||
@ -317,6 +321,8 @@ INT = "INT"
|
||||
|
||||
REAL = "REAL"
|
||||
|
||||
BOOL = "BOOL"
|
||||
|
||||
STRING = "STRING"
|
||||
|
||||
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 });
|
||||
});
|
||||
|
||||
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 () => {
|
||||
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(
|
||||
join(import.meta.dir + '/primop-scope.cps'),
|
||||
).text();
|
||||
export const Branching = Bun.file(
|
||||
join(import.meta.dir + '/branching.cps'),
|
||||
).text();
|
||||
export const StringEquality = Bun.file(
|
||||
join(import.meta.dir + '/string-equal.cps'),
|
||||
).text();
|
||||
|
@ -1,13 +1,9 @@
|
||||
import { expect, test } from 'bun:test';
|
||||
import { TestPrograms } from './programs';
|
||||
import { peggyParse } from '@/parser';
|
||||
import {
|
||||
evaluate,
|
||||
type DenotableFunctionSignature,
|
||||
denotableTypesEquivalent,
|
||||
matchSignature,
|
||||
} from '@/interpreter';
|
||||
import { testingLogger } from './logger';
|
||||
|
||||
test('simple denotable types are equivalent', () => {
|
||||
expect(denotableTypesEquivalent('int', 'int')).toBe(true);
|
||||
|
Loading…
Reference in New Issue
Block a user