diff --git a/CITATION.cff b/CITATION.cff index 714c8d605..b0a08ad6a 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -21,8 +21,8 @@ authors: identifiers: - type: url value: >- - https://github.com/Kipper-Lang/Kipper/releases/tag/v0.13.0-alpha.1 - description: The GitHub release URL of tag 0.13.0-alpha.1 + https://github.com/Kipper-Lang/Kipper/releases/tag/v0.13.0-alpha.2 + description: The GitHub release URL of tag 0.13.0-alpha.2 repository-code: 'https://github.com/Kipper-Lang/Kipper/' url: 'https://kipper-lang.org' abstract: >- @@ -39,6 +39,6 @@ keywords: - oop-programming - type-safety license: GPL-3.0-or-later -license-url: 'https://github.com/Kipper-Lang/Kipper/blob/v0.13.0-alpha.1/LICENSE' -version: 0.13.0-alpha.1 +license-url: 'https://github.com/Kipper-Lang/Kipper/blob/v0.13.0-alpha.2/LICENSE' +version: 0.13.0-alpha.2 date-released: '2025-02-26' diff --git a/kipper/cli/README.md b/kipper/cli/README.md index 483beb9a9..1dcd5aa9a 100644 --- a/kipper/cli/README.md +++ b/kipper/cli/README.md @@ -44,7 +44,7 @@ $ npm install -g @kipper/cli $ kipper COMMAND running command... $ kipper (--version) -@kipper/cli/0.13.0-alpha.1 linux-x64 node-v23.4.0 +@kipper/cli/0.13.0-alpha.2 linux-x64 node-v23.4.0 $ kipper --help [COMMAND] USAGE $ kipper COMMAND @@ -111,7 +111,7 @@ EXAMPLES kipper compile -t ts ./path/to/file.kip -o build/ -e utf16le --warnings --log-timestamp ``` -_See code: [src/commands/compile.ts](https://github.com/Kipper-Lang/Kipper/blob/v0.13.0-alpha.1/kipper/cli/src/commands/compile.ts)_ +_See code: [src/commands/compile.ts](https://github.com/Kipper-Lang/Kipper/blob/v0.13.0-alpha.2/kipper/cli/src/commands/compile.ts)_ ## `kipper help [COMMAND]` @@ -128,7 +128,7 @@ OPTIONS --all see all commands in CLI ``` -_See code: [src/commands/help.ts](https://github.com/Kipper-Lang/Kipper/blob/v0.13.0-alpha.1/kipper/cli/src/commands/help.ts)_ +_See code: [src/commands/help.ts](https://github.com/Kipper-Lang/Kipper/blob/v0.13.0-alpha.2/kipper/cli/src/commands/help.ts)_ ## `kipper new [LOCATION]` @@ -145,7 +145,7 @@ OPTIONS -d, --default Use the default settings for the new project. Skips the setup wizard. ``` -_See code: [src/commands/new.ts](https://github.com/Kipper-Lang/Kipper/blob/v0.13.0-alpha.1/kipper/cli/src/commands/new.ts)_ +_See code: [src/commands/new.ts](https://github.com/Kipper-Lang/Kipper/blob/v0.13.0-alpha.2/kipper/cli/src/commands/new.ts)_ ## `kipper run [FILE]` @@ -190,7 +190,7 @@ EXAMPLES kipper run -t ts -o build/ -e utf8 -s "print('Hello, World!');" --warnings --log-timestamp ``` -_See code: [src/commands/run.ts](https://github.com/Kipper-Lang/Kipper/blob/v0.13.0-alpha.1/kipper/cli/src/commands/run.ts)_ +_See code: [src/commands/run.ts](https://github.com/Kipper-Lang/Kipper/blob/v0.13.0-alpha.2/kipper/cli/src/commands/run.ts)_ ## `kipper version` @@ -201,7 +201,7 @@ USAGE $ kipper version ``` -_See code: [src/commands/version.ts](https://github.com/Kipper-Lang/Kipper/blob/v0.13.0-alpha.1/kipper/cli/src/commands/version.ts)_ +_See code: [src/commands/version.ts](https://github.com/Kipper-Lang/Kipper/blob/v0.13.0-alpha.2/kipper/cli/src/commands/version.ts)_ ## Contributing to Kipper diff --git a/kipper/cli/package.json b/kipper/cli/package.json index a86435aa4..60aabe17d 100644 --- a/kipper/cli/package.json +++ b/kipper/cli/package.json @@ -1,7 +1,7 @@ { "name": "@kipper/cli", "description": "The Kipper Command Line Interface (CLI).", - "version": "0.13.0-alpha.1", + "version": "0.13.0-alpha.2", "author": "Luna-Klatzer @Luna-Klatzer", "bin": { "kipper": "./bin/run", diff --git a/kipper/cli/src/index.ts b/kipper/cli/src/index.ts index 7284a71d2..1568be946 100644 --- a/kipper/cli/src/index.ts +++ b/kipper/cli/src/index.ts @@ -13,7 +13,7 @@ export * from "./output/compile"; // eslint-disable-next-line no-unused-vars export const name = "@kipper/cli"; // eslint-disable-next-line no-unused-vars -export const version = "0.13.0-alpha.1"; +export const version = "0.13.0-alpha.2"; // eslint-disable-next-line no-unused-vars export const author = "Luna Klatzer"; // eslint-disable-next-line no-unused-vars diff --git a/kipper/config/package.json b/kipper/config/package.json index 7343daafa..99de4df00 100644 --- a/kipper/config/package.json +++ b/kipper/config/package.json @@ -1,7 +1,7 @@ { "name": "@kipper/config", "description": "The config file support package adding support for kip-config.json/kipper-config.json 🦊", - "version": "0.13.0-alpha.1", + "version": "0.13.0-alpha.2", "author": "Luna-Klatzer @Luna-Klatzer", "dependencies": { "is-plain-object": "5.0.0", diff --git a/kipper/config/src/index.ts b/kipper/config/src/index.ts index 72790432a..b3097fb1f 100644 --- a/kipper/config/src/index.ts +++ b/kipper/config/src/index.ts @@ -12,7 +12,7 @@ export * from "./evaluated-kipper-config-file"; // eslint-disable-next-line no-unused-vars export const name = "@kipper/config"; // eslint-disable-next-line no-unused-vars -export const version = "0.13.0-alpha.1"; +export const version = "0.13.0-alpha.2"; // eslint-disable-next-line no-unused-vars export const author = "Luna Klatzer"; // eslint-disable-next-line no-unused-vars diff --git a/kipper/core/package.json b/kipper/core/package.json index 6f24c6459..d6b0ac8c5 100644 --- a/kipper/core/package.json +++ b/kipper/core/package.json @@ -1,7 +1,7 @@ { "name": "@kipper/core", "description": "The core implementation of the Kipper compiler 🦊", - "version": "0.13.0-alpha.1", + "version": "0.13.0-alpha.2", "author": "Luna-Klatzer @Luna-Klatzer", "dependencies": { "antlr4ts": "^0.5.0-alpha.4", diff --git a/kipper/core/src/index.ts b/kipper/core/src/index.ts index 33581f9fe..29a90652c 100644 --- a/kipper/core/src/index.ts +++ b/kipper/core/src/index.ts @@ -17,7 +17,7 @@ export * as utils from "./tools"; // eslint-disable-next-line no-unused-vars export const name = "@kipper/core"; // eslint-disable-next-line no-unused-vars -export const version = "0.13.0-alpha.1"; +export const version = "0.13.0-alpha.2"; // eslint-disable-next-line no-unused-vars export const author = "Luna Klatzer"; // eslint-disable-next-line no-unused-vars diff --git a/kipper/index.ts b/kipper/index.ts index 99e6dc281..ca882d679 100644 --- a/kipper/index.ts +++ b/kipper/index.ts @@ -13,7 +13,7 @@ export * from "@kipper/target-ts"; // eslint-disable-next-line no-unused-vars export const name = "kipper"; // eslint-disable-next-line no-unused-vars -export const version = "0.13.0-alpha.1"; +export const version = "0.13.0-alpha.2"; // eslint-disable-next-line no-unused-vars export const author = "Luna Klatzer"; // eslint-disable-next-line no-unused-vars diff --git a/kipper/target-js/package.json b/kipper/target-js/package.json index f138437a8..8e90b2acc 100644 --- a/kipper/target-js/package.json +++ b/kipper/target-js/package.json @@ -1,7 +1,7 @@ { "name": "@kipper/target-js", "description": "The JavaScript target for the Kipper compiler 🦊", - "version": "0.13.0-alpha.1", + "version": "0.13.0-alpha.2", "author": "Luna-Klatzer @Luna-Klatzer", "dependencies": { "@kipper/core": "workspace:~", diff --git a/kipper/target-js/src/built-in-generator.ts b/kipper/target-js/src/built-in-generator.ts index 85160c02c..9e76a900f 100644 --- a/kipper/target-js/src/built-in-generator.ts +++ b/kipper/target-js/src/built-in-generator.ts @@ -134,7 +134,9 @@ export class JavaScriptTargetBuiltInGenerator extends KipperTargetBuiltInGenerat return genJSFunction( signature, - `{ if (${typeIdentifier}.acceptsVal(${valIdentifier})) { return ${valIdentifier} }` + `return null; }`, + `{ if ('acceptsVal' in ${typeIdentifier} && ${typeIdentifier}.acceptsVal(${valIdentifier})) { return ${valIdentifier} }` + + `try { if (${valIdentifier} instanceof ${typeIdentifier}) { return ${valIdentifier} } } catch (_) {}` + + `return null; }`, ); } @@ -148,7 +150,8 @@ export class JavaScriptTargetBuiltInGenerator extends KipperTargetBuiltInGenerat return genJSFunction( signature, `{ const valType = ${valType};` + - `if (${typeIdentifier}.accepts(valType)) { return ${valIdentifier} }` + + `if ('acceptsVal' in ${typeIdentifier} && ${typeIdentifier}.acceptsVal(${valIdentifier})) { return ${valIdentifier} }` + + `try { if (${valIdentifier} instanceof ${typeIdentifier}) { return ${valIdentifier} } } catch (_) {}` + `throw new ${typeErr}(\`Invalid force cast from '\${${valType}.name}' to '\${${typeIdentifier}.name}'.\`); }`, ); } diff --git a/kipper/target-js/src/index.ts b/kipper/target-js/src/index.ts index 248865b6d..2514569a4 100644 --- a/kipper/target-js/src/index.ts +++ b/kipper/target-js/src/index.ts @@ -14,7 +14,7 @@ export * from "./runtime/"; // eslint-disable-next-line no-unused-vars export const name = "@kipper/target-js"; // eslint-disable-next-line no-unused-vars -export const version = "0.13.0-alpha.1"; +export const version = "0.13.0-alpha.2"; // eslint-disable-next-line no-unused-vars export const author = "Luna Klatzer"; // eslint-disable-next-line no-unused-vars diff --git a/kipper/target-js/src/runtime/create-kipper.ts b/kipper/target-js/src/runtime/create-kipper.ts index f1e310bcf..4d6e43484 100644 --- a/kipper/target-js/src/runtime/create-kipper.ts +++ b/kipper/target-js/src/runtime/create-kipper.ts @@ -39,9 +39,9 @@ export const createKipper = (args: CreateKipperOptions = {}): string => if (type.baseType === null) { return false; } return this.isInHierarchyOfType(type.baseType); } - accepts(obj) { - if (this === obj) return true; - return obj instanceof KipperType && (this.customComparer ? this.customComparer(this, obj) : true); + accepts(objT) { + if (this === objT) return true; + return objT instanceof KipperType && (this.customComparer ? this.customComparer(this, objT) : true); } acceptsVal(obj) { return this.accepts(__tmpKip.typeOf(obj)); @@ -51,12 +51,12 @@ export const createKipper = (args: CreateKipperOptions = {}): string => constructor(name, fields, methods, genericArgs, baseType = null, customComparer = null) { super(name, fields, methods, baseType, customComparer); this.genericArgs = genericArgs; } - accepts(obj) { - if (this === obj) return true; - if (!(obj instanceof KipperGenericType) || this.genericArgs.length !== obj.genericArgs.length) return false; - const foreignGenericArgs = Object.entries(obj.genericArgs); - return this.name === obj.name && - (this.customComparer ? this.customComparer(this, obj) : true) && + accepts(objT) { + if (this === objT) return true; + if (!(objT instanceof KipperGenericType) || this.genericArgs.length !== objT.genericArgs.length) return false; + const foreignGenericArgs = Object.entries(objT.genericArgs); + return this.name === objT.name && + (this.customComparer ? this.customComparer(this, objT) : true) && Object.entries(this.genericArgs).every((arg, i) => { if (Array.isArray(arg)) { if (!Array.isArray(foreignGenericArgs[i]) || arg.length !== foreignGenericArgs[i].length) { return false; } @@ -67,6 +67,32 @@ export const createKipper = (args: CreateKipperOptions = {}): string => } changeGenericTypeArguments(genericArgs) { return new KipperGenericType(this.name, this.fields, this.methods, genericArgs, this.baseType) } }; + class KipperInterfaceType extends KipperType { + constructor(name, fields, methods, baseType = null, customComparer = null) { + super(name, fields, methods, baseType, customComparer); + } + accepts(objT) { + if (this === objT) return true; + if (!(objT instanceof KipperInterfaceType)) return false; + return this.isSubsetOfOrEqual(objT) && + (this.customComparer ? this.customComparer(this, objT) : true); + } + acceptsVal(obj) { + const objT = __tmpKip.typeOf(obj); + if (objT !== __tmpKip.builtIn.obj && !(obj instanceof Object)) return false; + return __tmpKip.matches(obj, this); + } + isSubsetOfOrEqual(objT) { + return this.fields.every((field) => { + const fieldInObjT = objT.fields.find((f) => f.name === field.name); + return fieldInObjT && field.type.accepts(fieldInObjT.type); + }) && this.methods.every((method) => { + const methodInObjT = objT.methods.find((m) => m.name === method.name); + return methodInObjT && method.returnType.accepts(methodInObjT.returnType) && method.parameters.length === methodInObjT.parameters.length && + method.parameters.every((param, i) => param.type.accepts(methodInObjT.parameters[i].type)); + }); + } + } const __type_any = new KipperType('any', undefined, undefined); const __type_null = new KipperType('null', undefined, undefined, undefined, (a, b) => a.name === b.name); const __type_undefined = new KipperType('undefined', undefined, undefined, undefined, (a, b) => a.name === b.name); @@ -77,15 +103,15 @@ export const createKipper = (args: CreateKipperOptions = {}): string => const __type_Array = new KipperGenericType('Array', undefined, undefined, {T: __type_any}, undefined, (a, b) => a.name === b.name); const __type_Func = new KipperGenericType('Func', undefined, undefined, {T: [], R: __type_any}, undefined, (a, b) => a.name === b.name); __tmpKip.builtIn = { - any: __type_any, - null: __type_null, - undefined: __type_undefined, - str: __type_str, - num: __type_num, - bool: __type_bool, - obj: __type_obj, - Array: __type_Array, - Func: __type_Func, + any: __type_any, + null: __type_null, + undefined: __type_undefined, + str: __type_str, + num: __type_num, + bool: __type_bool, + obj: __type_obj, + Array: __type_Array, + Func: __type_Func, }; __tmpKip.KipperError = KipperError; __tmpKip.TypeError = (class KipperTypeError extends KipperError { constructor(msg) { super(msg); this.name = 'KipTypeError'; } }); @@ -96,29 +122,32 @@ export const createKipper = (args: CreateKipperOptions = {}): string => __tmpKip.Method = class KipperMethod { constructor(name, returnType, parameters) { this.name = name; this.returnType = returnType; this.parameters = parameters; } }; __tmpKip.Type = KipperType; __tmpKip.assignTypeMeta = (value, typeMeta) => Object.assign(value, { __kipType: typeMeta }); + __tmpKip.getTypeMeta = (value) => value.__kipType; __tmpKip.newArrayT = (type) => __tmpKip.builtIn.Array.changeGenericTypeArguments({ T: type }); + __tmpKip.newFuncT = (params, returnType) => __tmpKip.builtIn.Func.changeGenericTypeArguments({ T: params, R: returnType }); + __tmpKip.newIntfT = (name, fields, methods, baseType = __tmpKip.builtIn.obj, customComparer = null) => new KipperInterfaceType(name, fields, methods, baseType, customComparer); __tmpKip.typeOf = (value) => { const prim = typeof value; switch (prim) { - case 'undefined': return __kipper.builtIn.undefined; - case 'string': return __kipper.builtIn.str; - case 'number': return __kipper.builtIn.num; - case 'boolean': return __kipper.builtIn.bool; + case 'undefined': return __tmpKip.builtIn.undefined; + case 'string': return __tmpKip.builtIn.str; + case 'number': return __tmpKip.builtIn.num; + case 'boolean': return __tmpKip.builtIn.bool; case 'function': { - return '__kipType' in value ? value.__kipType : __kipper.builtIn.Func; + return '__kipType' in value ? value.__kipType : __tmpKip.builtIn.Func; } case 'symbol': case 'bigint': case 'object': { - if (value === null) return __kipper.builtIn.null; + if (value === null) return __tmpKip.builtIn.null; if (Array.isArray(value)) { - return '__kipType' in value ? value.__kipType : __kipper.builtIn.Array; + return '__kipType' in value ? value.__kipType : __tmpKip.builtIn.Array; } const prot = Object.getPrototypeOf(value); if (prot && prot.constructor !== Object) { return prot.constructor; } - return __kipper.builtIn.obj; + return __tmpKip.builtIn.obj; } } }; @@ -134,7 +163,7 @@ export const createKipper = (args: CreateKipperOptions = {}): string => return false; } const fieldValue = value[fieldName]; - const isSameType = __kipper.typeOf(fieldValue) === field.type; + const isSameType = __tmpKip.typeOf(fieldValue) === field.type; if (primTypes.includes(field.type.name) && !isSameType) { return false; } @@ -142,7 +171,7 @@ export const createKipper = (args: CreateKipperOptions = {}): string => throw new KipperNotImplementedError("Matches does not yet support the 'Array' and 'Func' types"); } if (!primTypes.includes(fieldType.name)) { - if (!__kipper.matches(fieldValue, fieldType)) { + if (!__tmpKip.matches(fieldValue, fieldType)) { return false; } } diff --git a/kipper/target-js/src/runtime/runtime-types.ts b/kipper/target-js/src/runtime/runtime-types.ts index 24f055785..913d570e5 100644 --- a/kipper/target-js/src/runtime/runtime-types.ts +++ b/kipper/target-js/src/runtime/runtime-types.ts @@ -62,7 +62,7 @@ export class RuntimeTypesGenerator { "const ", identifier, " = ", - `new ${TargetJS.getBuiltInIdentifier("Type")}`, + TargetJS.getBuiltInIdentifier("newIntfT"), "(", `"${interfaceName}"`, ",", diff --git a/kipper/target-ts/package.json b/kipper/target-ts/package.json index a007f1996..cb0efe710 100644 --- a/kipper/target-ts/package.json +++ b/kipper/target-ts/package.json @@ -1,7 +1,7 @@ { "name": "@kipper/target-ts", "description": "The TypeScript target for the Kipper compiler 🦊", - "version": "0.13.0-alpha.1", + "version": "0.13.0-alpha.2", "author": "Luna-Klatzer @Luna-Klatzer", "dependencies": { "@kipper/target-js": "workspace:~", diff --git a/kipper/target-ts/src/index.ts b/kipper/target-ts/src/index.ts index 066492715..47523cf75 100644 --- a/kipper/target-ts/src/index.ts +++ b/kipper/target-ts/src/index.ts @@ -13,7 +13,7 @@ export * from "./tools"; // eslint-disable-next-line no-unused-vars export const name = "@kipper/target-ts"; // eslint-disable-next-line no-unused-vars -export const version = "0.13.0-alpha.1"; +export const version = "0.13.0-alpha.2"; // eslint-disable-next-line no-unused-vars export const author = "Luna Klatzer"; // eslint-disable-next-line no-unused-vars diff --git a/kipper/web/package.json b/kipper/web/package.json index 1744953d7..b9fe3e852 100644 --- a/kipper/web/package.json +++ b/kipper/web/package.json @@ -1,7 +1,7 @@ { "name": "@kipper/web", "description": "The standalone web-module for the Kipper compiler 🦊", - "version": "0.13.0-alpha.1", + "version": "0.13.0-alpha.2", "author": "Luna-Klatzer @Luna-Klatzer", "devDependencies": { "@kipper/target-js": "workspace:~", diff --git a/package.json b/package.json index a26d47bc0..7e3d61d9d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "kipper", "description": "The Kipper programming language and compiler 🦊", - "version": "0.13.0-alpha.1", + "version": "0.13.0-alpha.2", "author": "Luna-Klatzer @Luna-Klatzer", "dependencies": { "@kipper/cli": "workspace:~", diff --git a/test/module/core/core-functionality/arrays.test.ts b/test/module/core/core-functionality/arrays.test.ts index d8b2fb3ef..6b53c4fd1 100644 --- a/test/module/core/core-functionality/arrays.test.ts +++ b/test/module/core/core-functionality/arrays.test.ts @@ -6,7 +6,7 @@ import { compiler, defaultTarget } from "."; import { assertCodeIncludesSnippet, testPrintOutput } from ".."; describe("Arrays", () => { - it("Simple array declaration", async () => { + it("simple array declaration", async () => { const fileContent = `var x: Array = [1, 2, 3];`; const instance: KipperCompileResult = await compiler.compile(fileContent, { target: defaultTarget }); @@ -20,7 +20,7 @@ describe("Arrays", () => { ); }); - it("Assign array to array", async () => { + it("assign array to array", async () => { const code = "var x: Array = [1, 2, 3]; var y: Array = x;"; const instance: KipperCompileResult = await compiler.compile(code, { target: defaultTarget }); @@ -34,7 +34,7 @@ describe("Arrays", () => { ); }); - it("Accessing array element", async () => { + it("accessing array element", async () => { const code = "var x: Array = [1, 2, 3]; print(x[1] as str);"; const instance: KipperCompileResult = await compiler.compile(code, { target: defaultTarget }); @@ -45,7 +45,7 @@ describe("Arrays", () => { testPrintOutput((message: any) => assert.equal(message, "2", "Expected different output"), tsCode); }); - it("Assigning one array element to another", async () => { + it("assigning one array element to another", async () => { const code = "var x: Array = [1, 2, 3]; var y: num = x[1]; print(y as str);"; const instance: KipperCompileResult = await compiler.compile(code, { target: defaultTarget }); @@ -56,8 +56,8 @@ describe("Arrays", () => { testPrintOutput((message: any) => assert.equal(message, "2", "Expected different output"), tsCode); }); - describe("Empty array", async () => { - it("Empty array assignable to any array variable definition", async () => { + describe("empty array", async () => { + it("empty array assignable to any array variable definition", async () => { const code = "var x: Array = [];"; const instance: KipperCompileResult = await compiler.compile(code, { target: defaultTarget }); @@ -71,7 +71,7 @@ describe("Arrays", () => { ); }); - it("Nested empty array assignable to any array variable definition", async () => { + it("nested empty array assignable to any array variable definition", async () => { const code = "var x: Array = ((([])));"; const instance: KipperCompileResult = await compiler.compile(code, { target: defaultTarget }); @@ -85,7 +85,7 @@ describe("Arrays", () => { ); }); - it("Empty array assignable to any array variable", async () => { + it("empty array assignable to any array variable", async () => { const code = "var x: Array = [1, 2, 3]; x = [];"; const instance: KipperCompileResult = await compiler.compile(code, { target: defaultTarget }); @@ -103,7 +103,7 @@ describe("Arrays", () => { ); }); - it("Empty array passable to any array function argument", async () => { + it("empty array passable to any array function argument", async () => { const code = "def test(x: Array) -> void { print(x); } test([]);"; const instance: KipperCompileResult = await compiler.compile(code, { target: defaultTarget }); @@ -117,7 +117,7 @@ describe("Arrays", () => { ); }); - it("Empty array passable to any array constructor argument", async () => { + it("empty array passable to any array constructor argument", async () => { const code = "class Test { constructor(x: Array) { print(x); } } new Test([]);"; const instance: KipperCompileResult = await compiler.compile(code, { target: defaultTarget }); @@ -131,7 +131,7 @@ describe("Arrays", () => { ); }); - it("Empty array returnable from any array function", async () => { + it("empty array returnable from any array function", async () => { const code = "def test() -> Array { return []; } print(test());"; const instance: KipperCompileResult = await compiler.compile(code, { target: defaultTarget }); diff --git a/test/module/core/core-functionality/cast-or-convert.test.ts b/test/module/core/core-functionality/cast-or-convert.test.ts index 0f2d03f16..ca71da7ce 100644 --- a/test/module/core/core-functionality/cast-or-convert.test.ts +++ b/test/module/core/core-functionality/cast-or-convert.test.ts @@ -141,94 +141,299 @@ describe("Cast-or-Convert", () => { }); describe("force as", () => { - it("should convert a correct value from EXP to T", async () => { - const fileContent = ` - var x: any = 123; - var y: num = x force as num; - print(y); - `; - const instance: KipperCompileResult = await compiler.compile(fileContent, { target: defaultTarget }); + describe("should convert a correct value from EXP to T", () => { + it("using primitives", async () => { + const fileContent = ` + var x: any = 123; + var y: num = x force as num; + print(y); + `; + const instance: KipperCompileResult = await compiler.compile(fileContent, { target: defaultTarget }); - assert.isDefined(instance.programCtx); - assertErrorsAreEmpty(instance.programCtx!); + assert.isDefined(instance.programCtx); + assertErrorsAreEmpty(instance.programCtx!); - const code = instance.write(); - assertCodeIncludesSnippet(code, "let x: any = 123;"); - assertCodeIncludesSnippet(code, "let y: number = (__kipper.forceCastAs(x,__kipper.builtIn.num) as number);"); + const code = instance.write(); + assertCodeIncludesSnippet(code, "let x: any = 123;"); + assertCodeIncludesSnippet(code, "let y: number = (__kipper.forceCastAs(x,__kipper.builtIn.num) as number);"); - // Run the code to make sure it works - const jsCode = ts.transpile(code, { target: ScriptTarget.ES2015 }); - testPrintOutput((message: any) => assert.equal(message, 123, "Expected different output"), jsCode); + // Run the code to make sure it works + const jsCode = ts.transpile(code, { target: ScriptTarget.ES2015 }); + testPrintOutput((message: any) => assert.equal(message, 123, "Expected different output"), jsCode); + }); + + it("using interfaces", async () => { + const fileContent = ` + interface Test { x: num; } + var x: any = { x: 1 }; + var y: Test = x force as Test; + print(y.x); + `; + const instance: KipperCompileResult = await compiler.compile(fileContent, { target: defaultTarget }); + + assert.isDefined(instance.programCtx); + assertErrorsAreEmpty(instance.programCtx!); + + const code = instance.write(); + assertCodeIncludesSnippet(code, "let x: any = {\n x: 1,\n};"); + assertCodeIncludesSnippet(code, "let y: Test = (__kipper.forceCastAs(x,__intf_Test) as Test);"); + + // Run the code to make sure it works + const jsCode = ts.transpile(code, { target: ScriptTarget.ES2015 }); + testPrintOutput((message: any) => assert.equal(message, 1, "Expected different output"), jsCode); + }); + + it("using class instances", async () => { + const fileContent = ` + class Test { x: num; constructor() { this.x = 1; } } + var x: any = new Test(); + var y: Test = x force as Test; + print(y.x); + `; + const instance: KipperCompileResult = await compiler.compile(fileContent, { target: defaultTarget }); + + assert.isDefined(instance.programCtx); + assertErrorsAreEmpty(instance.programCtx!); + + const code = instance.write(); + assertCodeIncludesSnippet(code, "class Test {\n x: number;\n constructor()\n {\n this.x = 1;\n }\n}"); + assertCodeIncludesSnippet(code, "let x: any = new Test();"); + assertCodeIncludesSnippet(code, "let y: Test = (__kipper.forceCastAs(x,Test) as Test);"); + + // Run the code to make sure it works + const jsCode = ts.transpile(code, { target: ScriptTarget.ES2015 }); + testPrintOutput((message: any) => assert.equal(message, 1, "Expected different output"), jsCode); + }); }); - it("should raise a type error when converting an incorrect value from EXP to T", async () => { - const fileContent = ` - var x: any = 123; - var y: str = x force as str; - print(y); - `; - const instance: KipperCompileResult = await compiler.compile(fileContent, { target: defaultTarget }); + describe("should raise a type error when converting an incorrect value from EXP to T", () => { + it("using primitives", async () => { + const fileContent = ` + var x: any = 123; + var y: str = x force as str; + print(y); + `; + const instance: KipperCompileResult = await compiler.compile(fileContent, { target: defaultTarget }); - assert.isDefined(instance.programCtx); - assertErrorsAreEmpty(instance.programCtx!); + assert.isDefined(instance.programCtx); + assertErrorsAreEmpty(instance.programCtx!); - const code = instance.write(); - assertCodeIncludesSnippet(code, "let x: any = 123;"); - assertCodeIncludesSnippet(code, "let y: string = (__kipper.forceCastAs(x,__kipper.builtIn.str) as string);"); + const code = instance.write(); + assertCodeIncludesSnippet(code, "let x: any = 123;"); + assertCodeIncludesSnippet(code, "let y: string = (__kipper.forceCastAs(x,__kipper.builtIn.str) as string);"); - // Run the code to make sure it works - const jsCode = ts.transpile(code, { target: ScriptTarget.ES2015 }); - testErrorThrows("KipTypeError", "Invalid force cast from 'num' to 'str'.", jsCode); + // Run the code to make sure it works + const jsCode = ts.transpile(code, { target: ScriptTarget.ES2015 }); + testErrorThrows("KipTypeError", "Invalid force cast from 'num' to 'str'.", jsCode); + }); + + it("using interfaces", async () => { + const fileContent = ` + interface Test { x: num; } + var x: any = { y: 2 }; + var y: Test = x force as Test; + print(y.x); + `; + const instance: KipperCompileResult = await compiler.compile(fileContent, { target: defaultTarget }); + + assert.isDefined(instance.programCtx); + assertErrorsAreEmpty(instance.programCtx!); + + const code = instance.write(); + assertCodeIncludesSnippet(code, "let x: any = {\n y: 2,\n};"); + assertCodeIncludesSnippet(code, "let y: Test = (__kipper.forceCastAs(x,__intf_Test) as Test);"); + + // Run the code to make sure it works + const jsCode = ts.transpile(code, { target: ScriptTarget.ES2015 }); + testErrorThrows("KipTypeError", "Invalid force cast from 'obj' to 'Test'.", jsCode); + }); + + it("using class instances", async () => { + const fileContent = ` + class Test { x: num; constructor() { this.x = 1; } } + var x: any = { x: 1, y: 2 }; + var y: Test = x force as Test; + print(y.x); + `; + const instance: KipperCompileResult = await compiler.compile(fileContent, { target: defaultTarget }); + + assert.isDefined(instance.programCtx); + assertErrorsAreEmpty(instance.programCtx!); + + const code = instance.write(); + assertCodeIncludesSnippet(code, "let x: any = {\n x: 1,\n y: 2,\n};"); + assertCodeIncludesSnippet(code, "let y: Test = (__kipper.forceCastAs(x,Test) as Test);"); + + // Run the code to make sure it works + const jsCode = ts.transpile(code, { target: ScriptTarget.ES2015 }); + testErrorThrows("KipTypeError", "Invalid force cast from 'obj' to 'Test'.", jsCode); + }); }); }); describe("try as", () => { - it("should convert a correct value from EXP to T", async () => { - const fileContent = ` - var x: any = 123; - var y: any = x try as num; - if (y == null) { - print("Failed to cast"); - } else { - print(y); - } - `; - const instance: KipperCompileResult = await compiler.compile(fileContent, { target: defaultTarget }); + describe("should convert a correct value from EXP to T", () => { + it("using primitives", async () => { + const fileContent = ` + var x: any = 123; + var y: num? = x try as num; + if (y == null) { + print("Failed to cast"); + } else { + print(y); + } + `; + const instance: KipperCompileResult = await compiler.compile(fileContent, { target: defaultTarget }); - assert.isDefined(instance.programCtx); - assertErrorsAreEmpty(instance.programCtx!); + assert.isDefined(instance.programCtx); + assertErrorsAreEmpty(instance.programCtx!); - const code = instance.write(); - assertCodeIncludesSnippet(code, "let x: any = 123;"); - assertCodeIncludesSnippet(code, "let y: any = (__kipper.tryCastAs(x,__kipper.builtIn.num) as number | null);"); + const code = instance.write(); + assertCodeIncludesSnippet(code, "let x: any = 123;"); + assertCodeIncludesSnippet( + code, + "let y: number | null = (__kipper.tryCastAs(x,__kipper.builtIn.num) as number | null);", + ); - // Run the code to make sure it works - const jsCode = ts.transpile(code, { target: ScriptTarget.ES2015 }); - testPrintOutput((message: any) => assert.equal(message, 123, "Expected different output"), jsCode); + // Run the code to make sure it works + const jsCode = ts.transpile(code, { target: ScriptTarget.ES2015 }); + testPrintOutput((message: any) => assert.equal(message, 123, "Expected different output"), jsCode); + }); + + it("using interfaces", async () => { + const fileContent = ` + interface Test { x: num; } + var x: any = { x: 1 }; + var y: Test? = x try as Test; + if (y == null) { + print("Failed to cast"); + } else { + print("Succeeded to cast"); + } + `; + const instance: KipperCompileResult = await compiler.compile(fileContent, { target: defaultTarget }); + + assert.isDefined(instance.programCtx); + assertErrorsAreEmpty(instance.programCtx!); + + const code = instance.write(); + assertCodeIncludesSnippet(code, "let x: any = {\n x: 1,\n};"); + assertCodeIncludesSnippet(code, "let y: Test | null = (__kipper.tryCastAs(x,__intf_Test) as Test | null);"); + + // Run the code to make sure it works + const jsCode = ts.transpile(code, { target: ScriptTarget.ES2015 }); + testPrintOutput( + (message: any) => assert.equal(message, "Succeeded to cast", "Expected different output"), + jsCode, + ); + }); + + it("using class instances", async () => { + const fileContent = ` + class Test { x: num; constructor() { this.x = 1; } } + var x: any = new Test(); + var y: Test? = x try as Test; + if (y == null) { + print("Failed to cast"); + } else { + print("Succeeded to cast"); + } + `; + const instance: KipperCompileResult = await compiler.compile(fileContent, { target: defaultTarget }); + + assert.isDefined(instance.programCtx); + assertErrorsAreEmpty(instance.programCtx!); + + const code = instance.write(); + assertCodeIncludesSnippet(code, "let x: any = new Test();"); + assertCodeIncludesSnippet(code, "let y: Test | null = (__kipper.tryCastAs(x,Test) as Test | null);"); + + // Run the code to make sure it works + const jsCode = ts.transpile(code, { target: ScriptTarget.ES2015 }); + testPrintOutput( + (message: any) => assert.equal(message, "Succeeded to cast", "Expected different output"), + jsCode, + ); + }); }); - it("should return null when converting an incorrect value from EXP to T", async () => { - const fileContent = ` - var x: any = 123; - var y: any = x try as str; - if (y == null) { - print("Failed to cast"); - } else { - print(y); - } - `; - const instance: KipperCompileResult = await compiler.compile(fileContent, { target: defaultTarget }); + describe("should return null when converting an incorrect value from EXP to T", () => { + it("using primitives", async () => { + const fileContent = ` + var x: any = 123; + var y: str? = x try as str; + if (y == null) { + print("Failed to cast"); + } else { + print(y); + } + `; + const instance: KipperCompileResult = await compiler.compile(fileContent, { target: defaultTarget }); - assert.isDefined(instance.programCtx); - assertErrorsAreEmpty(instance.programCtx!); + assert.isDefined(instance.programCtx); + assertErrorsAreEmpty(instance.programCtx!); - const code = instance.write(); - assertCodeIncludesSnippet(code, "let x: any = 123;"); - assertCodeIncludesSnippet(code, "let y: any = (__kipper.tryCastAs(x,__kipper.builtIn.str) as string | null);"); + const code = instance.write(); + assertCodeIncludesSnippet(code, "let x: any = 123;"); + assertCodeIncludesSnippet( + code, + "let y: string | null = (__kipper.tryCastAs(x,__kipper.builtIn.str) as string | null);", + ); - // Run the code to make sure it works - const jsCode = ts.transpile(code, { target: ScriptTarget.ES2015 }); - testPrintOutput((message: any) => assert.equal(message, "Failed to cast", "Expected different output"), jsCode); + // Run the code to make sure it works + const jsCode = ts.transpile(code, { target: ScriptTarget.ES2015 }); + testPrintOutput((message: any) => assert.equal(message, "Failed to cast", "Expected different output"), jsCode); + }); + + it("using interfaces", async () => { + const fileContent = ` + interface Test { x: num; } + var x: any = { y: 2 }; + var y: Test? = x try as Test; + if (y == null) { + print("Failed to cast"); + } else { + print("Succeeded to cast"); + } + `; + const instance: KipperCompileResult = await compiler.compile(fileContent, { target: defaultTarget }); + + assert.isDefined(instance.programCtx); + assertErrorsAreEmpty(instance.programCtx!); + + const code = instance.write(); + assertCodeIncludesSnippet(code, "let x: any = {\n y: 2,\n};"); + assertCodeIncludesSnippet(code, "let y: Test | null = (__kipper.tryCastAs(x,__intf_Test) as Test | null);"); + + // Run the code to make sure it works + const jsCode = ts.transpile(code, { target: ScriptTarget.ES2015 }); + testPrintOutput((message: any) => assert.equal(message, "Failed to cast", "Expected different output"), jsCode); + }); + + it("using class instances", async () => { + const fileContent = ` + class Test { x: num; constructor() { this.x = 1; } } + var x: any = { x: 1, y: 2 }; + var y: Test? = x try as Test; + if (y == null) { + print("Failed to cast"); + } else { + print("Succeeded to cast"); + } + `; + const instance: KipperCompileResult = await compiler.compile(fileContent, { target: defaultTarget }); + + assert.isDefined(instance.programCtx); + assertErrorsAreEmpty(instance.programCtx!); + + const code = instance.write(); + assertCodeIncludesSnippet(code, "let x: any = {\n x: 1,\n y: 2,\n};"); + assertCodeIncludesSnippet(code, "let y: Test | null = (__kipper.tryCastAs(x,Test) as Test | null);"); + + // Run the code to make sure it works + const jsCode = ts.transpile(code, { target: ScriptTarget.ES2015 }); + testPrintOutput((message: any) => assert.equal(message, "Failed to cast", "Expected different output"), jsCode); + }); }); }); }); diff --git a/test/module/core/core-functionality/interfaces.test.ts b/test/module/core/core-functionality/interfaces.test.ts index 6211f6e1e..b417c88a4 100644 --- a/test/module/core/core-functionality/interfaces.test.ts +++ b/test/module/core/core-functionality/interfaces.test.ts @@ -1,7 +1,7 @@ import type { KipperCompileResult } from "@kipper/core"; import { assert } from "chai"; import { compiler, defaultTarget } from "."; -import { assertErrorsAreEmpty } from "../index"; +import { assertCodeIncludesSnippet, assertErrorsAreEmpty } from "../index"; describe("Interfaces", async () => { it("Can initialize empty interface", async () => { @@ -12,7 +12,7 @@ describe("Interfaces", async () => { assertErrorsAreEmpty(instance.programCtx!); let written = instance.write(); - assert.include(written, "interface Test {\n}", "Invalid TypeScript code (Expected different output)"); + assertCodeIncludesSnippet(written, "interface Test {\n}"); }); it("should be able to to create object with interface blueprint", async () => { @@ -23,17 +23,16 @@ describe("Interfaces", async () => { assertErrorsAreEmpty(instance.programCtx!); let written = instance.write(); - assert.include( + assertCodeIncludesSnippet( written, `interface Test {\n` + ` a: string;\n` + `}\n` + - `const __intf_Test = new __kipper.Type("Test",[new __kipper.Property("a", __kipper.builtIn.str),],[],__kipper.builtIn.obj)\n` + + `const __intf_Test = __kipper.newIntfT("Test",[new __kipper.Property("a", __kipper.builtIn.str),],[],__kipper.builtIn.obj)\n` + "let x: Test = {\n" + ' a: "3",\n' + "};\n" + "__kipper.print(x.a);", - "Invalid TypeScript code (Expected different output)", ); }); @@ -45,10 +44,9 @@ describe("Interfaces", async () => { assertErrorsAreEmpty(instance.programCtx!); let written = instance.write(); - assert.include( + assertCodeIncludesSnippet( written, "interface Test {\n x: number;\n y: string;\n greet(name: string): string;\n}", - "Invalid TypeScript code (Expected different output)", ); }); @@ -60,11 +58,10 @@ describe("Interfaces", async () => { assertErrorsAreEmpty(instance.programCtx!); let written = instance.write(); - assert.include( + assertCodeIncludesSnippet( written, "interface Test {\n x: number;\n isTrue(f: boolean): string;\n y: string;\n greet(name: string):" + " string;\n}", - "Invalid TypeScript code (Expected different output)", ); }); }); diff --git a/test/module/core/index.ts b/test/module/core/index.ts index d642526b9..7a5012057 100644 --- a/test/module/core/index.ts +++ b/test/module/core/index.ts @@ -35,9 +35,15 @@ export function testErrorThrows(expectedError: string, expectedErrorMsg: string eval(jsProgram); assert.fail(`Expected error '${expectedError}' but no error was thrown.`); } catch (e) { - assert.equal((e).name, expectedError, `Expected error '${expectedError}' but got '${(e).name}'.`); - if (expectedErrorMsg) { - assert.include((e).message, expectedErrorMsg, `Expected error message to include '${expectedErrorMsg}'`); + try { + assert.equal((e).name, expectedError, `Expected error '${expectedError}' but got '${(e).name}'.`); + if (expectedErrorMsg) { + assert.include((e).message, expectedErrorMsg, `Expected error message to include '${expectedErrorMsg}'`); + } + } catch (assertErr) { + console.error(`Encountered error: ${e}`); + console.error(`Actual code:\n${jsProgram}`); + throw assertErr; } } }