diff --git a/src/core/enums/errno.enum.ts b/src/core/enums/errno.enum.ts index a7f6365..328e0b8 100644 --- a/src/core/enums/errno.enum.ts +++ b/src/core/enums/errno.enum.ts @@ -6,6 +6,8 @@ export enum Errno { InvalidFileError, UnknownFileError, MissingColonError, + InvalidArrayError, + InvalidStringError, MissingEqualsError, ExpectedCommaError, UnclosedStringError, diff --git a/src/core/errors/index.ts b/src/core/errors/index.ts index ab9e417..8ba91e4 100644 --- a/src/core/errors/index.ts +++ b/src/core/errors/index.ts @@ -4,7 +4,9 @@ export * from './missing-dot.error'; export * from './unknown-file.error'; export * from './expected-key.error'; export * from './invalid-file.error'; +export * from './invalid-array.error'; export * from './missing-colon.error'; +export * from './invalid-string.error'; export * from './expected-comma.error'; export * from './missing-equals.error'; export * from './unclosed-string.error'; diff --git a/src/core/errors/invalid-array.error.ts b/src/core/errors/invalid-array.error.ts new file mode 100644 index 0000000..b9e5241 --- /dev/null +++ b/src/core/errors/invalid-array.error.ts @@ -0,0 +1,13 @@ +import { Errno } from '../enums/errno.enum'; +import { LangKamaError } from './langkama.error'; + + + +export class InvalidArrayError extends LangKamaError { + constructor() { + super('identifier is not a valid array'); + + this.name = 'InvalidArrayError'; + this.errno = Errno.InvalidArrayError; + } +} \ No newline at end of file diff --git a/src/core/errors/invalid-string.error.ts b/src/core/errors/invalid-string.error.ts new file mode 100644 index 0000000..2231df6 --- /dev/null +++ b/src/core/errors/invalid-string.error.ts @@ -0,0 +1,13 @@ +import { Errno } from '../enums/errno.enum'; +import { LangKamaError } from './langkama.error'; + + + +export class InvalidStringError extends LangKamaError { + constructor() { + super('name is not a string'); + + this.name = 'InvalidStringError'; + this.errno = Errno.InvalidStringError; + } +} \ No newline at end of file diff --git a/src/runtime/environment.ts b/src/runtime/environment.ts index d9aeee2..cf4ea50 100644 --- a/src/runtime/environment.ts +++ b/src/runtime/environment.ts @@ -1,4 +1,4 @@ -import { ConstantReassignmentError, TOnStdOutCallbackFn, Type, VariableDefinedError, VariableNotDefinedError } from '..'; +import { ConstantReassignmentError, InvalidArrayError, InvalidStringError, TOnStdOutCallbackFn, Type, VariableDefinedError, VariableNotDefinedError } from '..'; import { ErrorManager } from '../core/managers/error.manager'; import { RuntimeHelper } from '../core/helpers/runtime.helper'; @@ -74,7 +74,7 @@ export class Environment { const [arr] = args; if (arr.type !== Type.Array) { - throw 'needs to be an array'; + this.errorManager?.raise(new InvalidArrayError()); } return RuntimeHelper.createNumber((arr as IArrayVal).value.length); @@ -84,13 +84,13 @@ export class Environment { const [arrName, value] = args; if (arrName.type !== Type.String) { - throw 'needs to ne a string'; + this.errorManager?.raise(new InvalidStringError()); } const arrayVal = this.getValue((arrName as IStringVal).value).value as IArrayVal; if (arrayVal.type !== Type.Array) { - throw 'needs to be an array'; + this.errorManager?.raise(new InvalidArrayError()); } const newArray = [...arrayVal.value]; @@ -104,13 +104,13 @@ export class Environment { const [arrName] = args; if (arrName.type !== Type.String) { - throw 'needs to ne a string'; + this.errorManager?.raise(new InvalidStringError()); } const arrayVal = this.getValue((arrName as IStringVal).value).value as IArrayVal; if (arrayVal.type !== Type.Array) { - throw 'needs to be an array'; + this.errorManager?.raise(new InvalidArrayError()); } const newArray = [...arrayVal.value]; diff --git a/src/runtime/interpreter.ts b/src/runtime/interpreter.ts index eb06926..b20c262 100644 --- a/src/runtime/interpreter.ts +++ b/src/runtime/interpreter.ts @@ -288,8 +288,9 @@ export class Evaluator { const index = this.evaluate(array.index, env) as INumberVal; const identifier = this.evaluate(array.identifier, env) as IArrayVal; const arr = (identifier as IArrayVal).value; + const value = arr[index.value]; - return RuntimeHelper.createValue(arr[index.value]); + return value ? RuntimeHelper.createValue(value) : RuntimeHelper.createNull(); } /** diff --git a/test/arrays.test.js b/test/arrays.test.js new file mode 100644 index 0000000..27eafe4 --- /dev/null +++ b/test/arrays.test.js @@ -0,0 +1,225 @@ +const Errno = require('../dist/@nakamaorg/langkama.umd.cjs').Errno; +const LangKama = require('../dist/@nakamaorg/langkama.umd.cjs').LangKama; +const LangKamaEvent = require('../dist/@nakamaorg/langkama.umd.cjs').LangKamaEvent; + + + +describe('Arrays', () => { + let compiler; + + beforeEach(() => { + compiler = new LangKama(); + }); + + test('Array declaration with value 1', done => { + const code = ` + a sa7 hear me out arr[] is []; + reda arr; + `; + + compiler + .on(LangKamaEvent.Success, result => { + expect(result.value).toEqual([]); + done(); + }) + .interpret(code); + }); + + test('Array declaration with value 2', done => { + const code = ` + a sa7 hear me out arr[] is [1, 2, 3]; + reda arr; + `; + + compiler + .on(LangKamaEvent.Success, result => { + expect(result.value).toEqual([1, 2, 3]); + done(); + }) + .interpret(code); + }); + + test('Array declaration without value', done => { + const code = ` + hear me out arr[]; + reda arr; + `; + + compiler + .on(LangKamaEvent.Success, result => { + expect(result.value).toEqual([]); + done(); + }) + .interpret(code); + }); + + test('Array indexing inbound', done => { + const code = ` + a sa7 hear me out arr[] is [1, 2, 3]; + reda arr[1]; + `; + + compiler + .on(LangKamaEvent.Success, result => { + expect(result.value).toBe(2); + done(); + }) + .interpret(code); + }); + + test('Array indexing out of bound', done => { + const code = ` + a sa7 hear me out arr[] is [1, 2, 3]; + reda arr[3]; + `; + + compiler + .on(LangKamaEvent.Success, result => { + expect(result.value).toBe(null); + done(); + }) + .interpret(code); + }); + + test('Array index assignment', done => { + const code = ` + hear me out arr[] is [1, 2, 3]; + arr[0] is 100; + + reda arr; + `; + + compiler + .on(LangKamaEvent.Success, result => { + expect(result.value).toEqual([100, 2, 3]); + done(); + }) + .interpret(code); + }); + + test('Array length', done => { + const code = ` + hear me out arr[] is [1, 2, 3]; + reda length(arr); + `; + + compiler + .on(LangKamaEvent.Success, result => { + expect(result.value).toBe(3); + done(); + }) + .interpret(code); + }); + + test('Array push 1', done => { + const code = ` + hear me out arr[]; + + push("arr", 0); + push("arr", 1); + push("arr", 2); + + reda arr; + `; + + compiler + .on(LangKamaEvent.Success, result => { + expect(result.value).toEqual([0, 1, 2]); + done(); + }) + .interpret(code); + }); + + test('Array push 2', done => { + const code = ` + hear me out arr[]; + hear me out i is 0; + + cook until (i = 100) { + big if true ((i % 2) = 0) { + push("arr", i); + } + + i is i + 1; + } + + reda push("arr", 100); + `; + + compiler + .on(LangKamaEvent.Success, result => { + expect(result.value).toBe(51); + done(); + }) + .interpret(code); + }); + + test('Array pop 1', done => { + const code = ` + hear me out arr[] is [1, 2, 3]; + + pop("arr"); + pop("arr"); + + reda arr; + `; + + compiler + .on(LangKamaEvent.Success, result => { + expect(result.value).toEqual([1]); + done(); + }) + .interpret(code); + }); + + test('Array pop 2', done => { + const code = ` + hear me out arr[]; + hear me out i is 0; + + cook until (i = 100) { + push("arr", i); + i is i + 1; + } + + i is 0; + cook until (i = 100) { + big if true ((i % 2) = 0) { + pop("arr"); + } + + i is i + 1; + } + + reda pop("arr"); + `; + + compiler + .on(LangKamaEvent.Success, result => { + expect(result.value).toBe(49); + done(); + }) + .interpret(code); + }); +}); + +describe('Arrays errors', () => { + let compiler; + + beforeEach(() => { + compiler = new LangKama(); + }); + + test('Missing close bracket', done => { + const code = ` + hear me out arr[ is []; + `; + + compiler + .on(LangKamaEvent.Error, result => { + expect(result.errno).toBe(Errno.ExpectedCloseBrackError); + done(); + }) + .interpret(code); + }); +}); \ No newline at end of file