From 01cac9f672e24df6f73de1f741ab42ec2142ee71 Mon Sep 17 00:00:00 2001 From: bendtherules Date: Thu, 13 Aug 2020 18:06:56 +0530 Subject: [PATCH] Feature - Support plus binary expression for number and string --- src/acorn-helpers/isType.ts | 5 +++++ src/acorn-helpers/types.ts | 12 +++++++++++- src/evaluate.ts | 18 ++++++++++++++++++ src/internal-types/CompletionRecord.ts | 10 ++++++++++ src/language-types/NumberValue.ts | 5 +++++ src/language-types/StringValue.ts | 5 +++++ test/evaluate1.test.ts | 24 ++++++++++++++++++++++++ 7 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/acorn-helpers/isType.ts b/src/acorn-helpers/isType.ts index b5f1cfe..ff23ac5 100644 --- a/src/acorn-helpers/isType.ts +++ b/src/acorn-helpers/isType.ts @@ -6,6 +6,7 @@ import { NumericLiteral, StringLiteral, Program, + BinaryExpression, } from './types'; export function isProgram(node: Node): node is Program { @@ -27,3 +28,7 @@ export function isNumericLiteral(node: Node): node is NumericLiteral { export function isStringLiteral(node: Node): node is StringLiteral { return isLiteral(node) && typeof node.value === 'string'; } + +export function isBinaryExpression(node: Node): node is BinaryExpression { + return node.type === TypeStrings.BinaryExpression; +} diff --git a/src/acorn-helpers/types.ts b/src/acorn-helpers/types.ts index 6d76a38..4fd63a4 100644 --- a/src/acorn-helpers/types.ts +++ b/src/acorn-helpers/types.ts @@ -6,6 +6,7 @@ export enum TypeStrings { Program = 'Program', ExpressionStatement = 'ExpressionStatement', Literal = 'Literal', + BinaryExpression = 'BinaryExpression', } export interface Program extends Node { @@ -18,7 +19,8 @@ export interface ExpressionStatement extends Node { expression: Expression; } -export type Expression = Literal | Node; +// TODO: Add more possible type of expressions here and remove generic node +export type Expression = Literal | BinaryExpression | Node; export interface Literal extends Node { type: TypeStrings.Literal; @@ -34,3 +36,11 @@ export interface StringLiteral extends Node { type: TypeStrings.Literal; value: string; } + +export interface BinaryExpression extends Node { + type: TypeStrings.BinaryExpression; + left: Literal; + right: Literal; + // TODO: Add actual possible operators here + operator: string; +} diff --git a/src/evaluate.ts b/src/evaluate.ts index de44307..40eeb50 100644 --- a/src/evaluate.ts +++ b/src/evaluate.ts @@ -4,11 +4,13 @@ import { isNumericLiteral, isStringLiteral, isProgram, + isBinaryExpression, } from './acorn-helpers/isType'; import { NormalCompletion, CompletionRecord, + v, } from './internal-types/CompletionRecord'; import { NumberValue } from './language-types/NumberValue'; @@ -30,6 +32,22 @@ export function Evaluate(node: Node): CompletionRecord { } else if (isStringLiteral(node)) { // Handle StringLiteral return NormalCompletion(new StringValue(node.value)); + } else if (isBinaryExpression(node)) { + if (node.operator === '+') { + // TODO - Forward completion record for abrupt completions + const leftVal = v(Evaluate(node.left)); + const rightVal = v(Evaluate(node.right)); + if (leftVal instanceof NumberValue && rightVal instanceof NumberValue) { + return NormalCompletion( + new NumberValue(leftVal.getValue() + rightVal.getValue()) + ); + } + if (leftVal instanceof StringValue && rightVal instanceof StringValue) { + return NormalCompletion( + new StringValue(leftVal.getValue() + rightVal.getValue()) + ); + } + } } else { // If not handled, throw error throwUnknownNode(node); diff --git a/src/internal-types/CompletionRecord.ts b/src/internal-types/CompletionRecord.ts index f1c950b..2e91420 100644 --- a/src/internal-types/CompletionRecord.ts +++ b/src/internal-types/CompletionRecord.ts @@ -29,3 +29,13 @@ export class CompletionRecord implements TCompletionRecord { export function NormalCompletion(value?: TValue) { return new CompletionRecord('normal', value); } + +export function v(cr: CompletionRecord): LanguageType | undefined { + if (cr[TypeString] === 'normal') { + return cr[ValueString]; + } + // TODO: Else, from call site, return same value + return undefined; +} + +export class AbruptCompletion extends TypeError {} diff --git a/src/language-types/NumberValue.ts b/src/language-types/NumberValue.ts index 66775c5..2313750 100644 --- a/src/language-types/NumberValue.ts +++ b/src/language-types/NumberValue.ts @@ -3,6 +3,7 @@ import { NumberString } from './typeNames'; export interface TNumberValue { type: typeof NumberString; value: number; + getValue(): number; } export class NumberValue implements TNumberValue { @@ -13,4 +14,8 @@ export class NumberValue implements TNumberValue { this.type = NumberString; this.value = value; } + + getValue(): number { + return this.value; + } } diff --git a/src/language-types/StringValue.ts b/src/language-types/StringValue.ts index e76e036..02fd944 100644 --- a/src/language-types/StringValue.ts +++ b/src/language-types/StringValue.ts @@ -3,6 +3,7 @@ import { StringString } from './typeNames'; export interface TStringValue { type: typeof StringString; value: string; + getValue(): string; } export class StringValue implements TStringValue { @@ -13,4 +14,8 @@ export class StringValue implements TStringValue { this.type = StringString; this.value = value; } + + getValue(): string { + return this.value; + } } diff --git a/test/evaluate1.test.ts b/test/evaluate1.test.ts index 96c6321..ff30f6c 100644 --- a/test/evaluate1.test.ts +++ b/test/evaluate1.test.ts @@ -22,4 +22,28 @@ describe('evaluate literals', () => { expect(returnVal).toBeInstanceOf(CompletionRecord); expect(returnVal[ValueString]?.value).toBe('abc'); }); + + it('for 10+20', () => { + const node = ParseScript(`10 + 20`); + const returnVal = Evaluate(node); + + expect(returnVal).toBeInstanceOf(CompletionRecord); + expect(returnVal[ValueString]?.value).toBe(30); + }); + + it('for 10+20+30', () => { + const node = ParseScript(`10 + 20 + 30`); + const returnVal = Evaluate(node); + + expect(returnVal).toBeInstanceOf(CompletionRecord); + expect(returnVal[ValueString]?.value).toBe(60); + }); + + it('for "ab"+"cd"', () => { + const node = ParseScript(`"ab"+"cd"`); + const returnVal = Evaluate(node); + + expect(returnVal).toBeInstanceOf(CompletionRecord); + expect(returnVal[ValueString]?.value).toBe('abcd'); + }); });