diff --git a/CHANGELOG.md b/CHANGELOG.md index dbdad7884..eed2249db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ To use development versions of Kipper download the [`next` tag release](https://www.npmjs.com/package/kipper?activeTab=versions), which will include the specified changes. ### Added - +- Added semantic checking and code generation for PrimaryObjectExpression and ObjectProperty - Implemented internal representation for custom types such as objects, interfaces and classes. This change means that the entire core type system has been reworked and adjusted to also support custom types as well as complex types (objects, arrays etc.). This does not inheritely add functionality but serves as the stepping stone for the diff --git a/kipper/core/src/compiler/ast/ast-generator.ts b/kipper/core/src/compiler/ast/ast-generator.ts index ffdca57e9..49e6d85f6 100644 --- a/kipper/core/src/compiler/ast/ast-generator.ts +++ b/kipper/core/src/compiler/ast/ast-generator.ts @@ -201,7 +201,7 @@ export class KipperFileASTGenerator implements KipperParserListener, ParseTreeLi if (this.currentNode instanceof RootASTNode) { throw new KipperInternalError( "An expression may not have the root file token as a parent. It must be child to a statement or a" + - " definition.", + " definition.", ); } this._currentPrimaryNode = this.expressionFactory.create(ctx, this.currentNode); @@ -211,7 +211,7 @@ export class KipperFileASTGenerator implements KipperParserListener, ParseTreeLi this.programCtx.logger.debug( `Created AST node of type '${this.currentNode.constructor.name}' for context '${ctx.astSyntaxKind}'` + - `(Loc: ${ctx.start.line}:${ctx.start.charPositionInLine})`, + `(Loc: ${ctx.start.line}:${ctx.start.charPositionInLine})`, ); } diff --git a/kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-primary-expression-semantics.ts b/kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-primary-expression-semantics.ts index 263f06d4a..22b16a518 100644 --- a/kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-primary-expression-semantics.ts +++ b/kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-primary-expression-semantics.ts @@ -1,5 +1,8 @@ import type { PrimaryExpressionSemantics } from "../primary-expression-semantics"; import type { ObjectProperty } from "./object-property"; +import { StringPrimaryExpression } from "../string-primary-expression"; +import type { KipperStorageType } from "../../../../../const"; +import type { IdentifierTypeSpecifierExpression } from "../../type-specifier-expression"; /** * Semantics for AST Node {@link VariableDeclaration}. diff --git a/kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-primary-expression.ts b/kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-primary-expression.ts index 6fcf7e2ab..f69736672 100644 --- a/kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-primary-expression.ts +++ b/kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-primary-expression.ts @@ -6,10 +6,17 @@ import type { ObjectPrimaryExpressionSemantics } from "./object-primary-expressi import type { ObjectPrimaryExpressionTypeSemantics } from "./object-primary-expression-type-semantics"; import type { CompilableASTNode } from "../../../../compilable-ast-node"; import type { ObjectPrimaryExpressionContext } from "../../../../../lexer-parser"; +import { DeclaratorContext } from "../../../../../lexer-parser"; +import { InitDeclaratorContext, StorageTypeSpecifierContext } from "../../../../../lexer-parser"; import { KindParseRuleMapping, ParseRuleKindMapping } from "../../../../../lexer-parser"; -import { KipperNotImplementedError } from "../../../../../../errors"; import { PrimaryExpression } from "../primary-expression"; -import { ObjectProperty } from "./object-property"; +import { CustomType } from "../../../../../semantics"; +import type { ObjectProperty } from "./object-property"; +import { UnableToDetermineSemanticDataError } from "../../../../../../errors"; +import type { KipperStorageType } from "../../../../../const"; +import type { IdentifierTypeSpecifierExpression } from "../../type-specifier-expression"; +import type { ParseTree } from "antlr4ts/tree"; +import type { Expression } from "../../expression"; /** * Object literal constant, which represents an object that was defined in the source code. @@ -75,10 +82,15 @@ export class ObjectPrimaryExpression extends PrimaryExpression< * the children has already failed and as such no parent node should run type checking. */ public async primarySemanticAnalysis(): Promise { - const objectProperties = this.children; + const children: Array = this.getAntlrRuleChildren(); + if (!children) { + throw new UnableToDetermineSemanticDataError(); + } + + const keyValuePairs = this.children; this.semanticData = { - keyValuePairs: objectProperties + keyValuePairs: keyValuePairs, }; } @@ -91,9 +103,9 @@ export class ObjectPrimaryExpression extends PrimaryExpression< * @since 0.11.0 */ public async primarySemanticTypeChecking(): Promise { - throw this.programCtx - .semanticCheck(this) - .notImplementedError(new KipperNotImplementedError("Object Literals have not been implemented yet.")); + this.typeSemantics = { + evaluatedType: CustomType.fromObjectLiteral(this), + }; } /** diff --git a/kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-property/object-property-semantics.ts b/kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-property/object-property-semantics.ts index 030d958bf..6aed0fa69 100644 --- a/kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-property/object-property-semantics.ts +++ b/kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-property/object-property-semantics.ts @@ -1,5 +1,5 @@ import type { PrimaryExpressionSemantics } from "../../primary-expression-semantics"; -import { Expression } from "../../../expression"; +import type { Expression } from "../../../expression"; import { StringPrimaryExpression } from "../../string-primary-expression"; /** @@ -7,6 +7,6 @@ import { StringPrimaryExpression } from "../../string-primary-expression"; * @since 0.11.0 */ export interface ObjectPropertySemantics extends PrimaryExpressionSemantics { - identifier: string - expressoDepresso: Expression + identifier: string; + expressoDepresso: Expression; } diff --git a/kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-property/object-property.ts b/kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-property/object-property.ts index e32b01c08..84f59e0cf 100644 --- a/kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-property/object-property.ts +++ b/kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-property/object-property.ts @@ -5,6 +5,8 @@ import { KindParseRuleMapping, ParseRuleKindMapping } from "../../../../../../le import { PrimaryExpression } from "../../primary-expression"; import type { CompilableASTNode } from "../../../../../compilable-ast-node"; import type { StringPrimaryExpression } from "../../string-primary-expression"; +import type { Expression } from "../../../expression"; +import { UnableToDetermineSemanticDataError } from "../../../../../../../errors"; /** * Object property, which represents a property inside an {@link ObjectPrimaryExpression object}. This is a key-value @@ -75,20 +77,24 @@ export class ObjectProperty extends PrimaryExpression { - let id; - let expression; + const antlrChildren = this.antlrRuleCtx.children; + if (!antlrChildren?.length) { + throw new UnableToDetermineSemanticDataError(); + } - if(this.children.length < 2) { - id = this.antlrRuleCtx.children![0].text; - expression = this.children[0]; + let id: string | StringPrimaryExpression; + let value: Expression; + if (this.children.length < 2) { + id = antlrChildren[0].text; + value = this.children[0]; } else { - id = ( this.children[0]).getSemanticData().value; - expression = this.children[1]; + id = (this.children[0]).getSemanticData().value; + value = this.children[1]; } this.semanticData = { identifier: id, - expressoDepresso: expression + expressoDepresso: value, }; } @@ -104,7 +110,11 @@ export class ObjectProperty extends PrimaryExpression => { - return []; + const semanticData = node.getSemanticData(); + const keyValuePairs = semanticData.keyValuePairs; + let keyValuePairsCodeString = await this.getStringifiedKeyValuePairs(keyValuePairs); + + return ["{", ...keyValuePairsCodeString, "}", ";"]; }; + protected async getStringifiedKeyValuePairs(keyValuePairs: Array): Promise { + let keyValuePairsCode: Array = []; + + for (let pair of keyValuePairs) { + let pairCode = await pair.translateCtxAndChildren(); + keyValuePairsCode.push(pairCode[0] + ",\n"); + } + + let code = keyValuePairsCode.join(""); + + if (code.endsWith(",\n")) { + code = code.slice(0, -2); + } + + return code; + } + /** * Translates a {@link ObjectProperty} into the JavaScript language. * @since 0.11.0 @@ -451,7 +472,11 @@ export class JavaScriptTargetCodeGenerator extends KipperTargetCodeGenerator { const expression = node.getSemanticData().expressoDepresso; const identifier = node.getSemanticData().identifier; - return [identifier, ":", ...(await expression.translateCtxAndChildren())]; + // Await the translation and join the array into a string + const translatedExpression = (await expression.translateCtxAndChildren()).join(""); + + // Return the concatenated result + return [` ${identifier}: ${translatedExpression}`]; }; /** diff --git a/kipper/target-ts/src/code-generator.ts b/kipper/target-ts/src/code-generator.ts index d2066b7a0..512f0642a 100644 --- a/kipper/target-ts/src/code-generator.ts +++ b/kipper/target-ts/src/code-generator.ts @@ -6,14 +6,16 @@ import type { FunctionDeclaration, InterfaceDeclaration, InterfacePropertyDeclaration, + ObjectPrimaryExpression, ParameterDeclaration, TranslatedCodeLine, VariableDeclaration, + InterfaceMethodDeclaration, + TranslatedExpression, } from "@kipper/core"; import { createTSFunctionSignature, getTSFunctionSignature } from "./tools"; import { JavaScriptTargetCodeGenerator } from "@kipper/target-js"; import { TargetTS } from "./target"; -import type { InterfaceMethodDeclaration } from "@kipper/core/lib/compiler/ast/nodes/declarations/type-declaration/interface-declaration/interface-member-declaration/interface-method-declaration"; /** * The TypeScript target-specific code generator for translating Kipper code into TypeScript. @@ -127,4 +129,13 @@ export class TypeScriptTargetCodeGenerator extends JavaScriptTargetCodeGenerator // Return the parameter declaration return [[identifier, ":", " ", valueType]]; }; + + objectPrimaryExpression = async (node: ObjectPrimaryExpression): Promise => { + const semanticData = node.getSemanticData(); + const keyValuePairs = semanticData.keyValuePairs; + let keyValuePairsCodeString = await this.getStringifiedKeyValuePairs(keyValuePairs); + + // Return the object primary expression + return ["{\n", ...keyValuePairsCodeString, "\n}"]; + }; } diff --git a/test/module/core/core-functionality.test.ts b/test/module/core/core-functionality.test.ts index e342dad3c..8c62c5c61 100644 --- a/test/module/core/core-functionality.test.ts +++ b/test/module/core/core-functionality.test.ts @@ -1412,4 +1412,17 @@ describe("Core functionality", () => { ); }); }); + + describe("Object literals", () => { + it("should be able to create an Object literal", async () => { + const fileContent = "{ x: 1, y: '2' };"; + const instance: KipperCompileResult = await compiler.compile(fileContent, { target: defaultTarget }); + + assert.isDefined(instance.programCtx); + assert.equal(instance.programCtx!!.errors.length, 0, "Expected no compilation errors"); + let written = instance.write(); + assert.include(written, "{\n x: 1,\n y: '2'\n};", "Invalid TypeScript code (Expected different output)"); + }); + + }); });