Skip to content

Commit

Permalink
Added semantic analysis and code generation to ObjectPrimaryExpressio…
Browse files Browse the repository at this point in the history
…n and
  • Loading branch information
ByteWolf-dev committed Jul 15, 2024
1 parent 3c365fc commit b910f64
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 39 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions kipper/core/src/compiler/ast/ast-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ export class KipperFileASTGenerator implements KipperParserListener, ParseTreeLi
if (this.currentNode instanceof RootASTNode) {
throw new KipperInternalError(

Check warning on line 202 in kipper/core/src/compiler/ast/ast-generator.ts

View check run for this annotation

Codecov / codecov/patch

kipper/core/src/compiler/ast/ast-generator.ts#L202

Added line #L202 was not covered by tests
"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(<ParserExpressionContext>ctx, this.currentNode);
Expand All @@ -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})`,
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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<void> {
const objectProperties = this.children;
const children: Array<ParseTree> = this.getAntlrRuleChildren();
if (!children) {
throw new UnableToDetermineSemanticDataError();

Check warning on line 87 in kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-primary-expression.ts

View check run for this annotation

Codecov / codecov/patch

kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-primary-expression.ts#L87

Added line #L87 was not covered by tests
}

const keyValuePairs = <ObjectProperty[]>this.children;

this.semanticData = {
keyValuePairs: <ObjectProperty[]> objectProperties
keyValuePairs: keyValuePairs,
};
}

Expand All @@ -91,9 +103,9 @@ export class ObjectPrimaryExpression extends PrimaryExpression<
* @since 0.11.0
*/
public async primarySemanticTypeChecking(): Promise<void> {
throw this.programCtx
.semanticCheck(this)
.notImplementedError(new KipperNotImplementedError("Object Literals have not been implemented yet."));
this.typeSemantics = {
evaluatedType: CustomType.fromObjectLiteral(this),
};
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { PrimaryExpressionSemantics } from "../../primary-expression-semantics";
import { Expression } from "../../../expression";
import type { Expression } from "../../../expression";
import { StringPrimaryExpression } from "../../string-primary-expression";

/**
* Semantics for AST Node {@link ObjectPrimaryExpression}.
* @since 0.11.0
*/
export interface ObjectPropertySemantics extends PrimaryExpressionSemantics {
identifier: string
expressoDepresso: Expression
identifier: string;
expressoDepresso: Expression;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -75,20 +77,24 @@ export class ObjectProperty extends PrimaryExpression<ObjectPropertySemantics, O
* the children has already failed and as such no parent node should run type checking.
*/
public async primarySemanticAnalysis(): Promise<void> {
let id;
let expression;
const antlrChildren = this.antlrRuleCtx.children;
if (!antlrChildren?.length) {
throw new UnableToDetermineSemanticDataError();

Check warning on line 82 in kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-property/object-property.ts

View check run for this annotation

Codecov / codecov/patch

kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-property/object-property.ts#L82

Added line #L82 was not covered by tests
}

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 = (<StringPrimaryExpression> this.children[0]).getSemanticData().value;
expression = this.children[1];
id = (<StringPrimaryExpression>this.children[0]).getSemanticData().value;
value = this.children[1];

Check warning on line 92 in kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-property/object-property.ts

View check run for this annotation

Codecov / codecov/patch

kipper/core/src/compiler/ast/nodes/expressions/primary-expression/object-primary-expression/object-property/object-property.ts#L91-L92

Added lines #L91 - L92 were not covered by tests
}

this.semanticData = {
identifier: id,
expressoDepresso: expression
expressoDepresso: value,
};
}

Expand All @@ -104,7 +110,11 @@ export class ObjectProperty extends PrimaryExpression<ObjectPropertySemantics, O
const semanticData = this.getSemanticData();

const expression = semanticData.expressoDepresso;
expression
expression.ensureTypeSemanticallyValid();

this.typeSemantics = {
evaluatedType: expression.getTypeSemanticData().evaluatedType,
};
}

public checkForWarnings = undefined; // TODO!
Expand Down
13 changes: 5 additions & 8 deletions kipper/core/src/compiler/semantics/types/custom-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,11 @@ export class CustomType extends ProcessedType {
*/
public static fromObjectLiteral(objectPrimaryExpression: ObjectPrimaryExpression): CustomType {
objectPrimaryExpression.ensureSemanticallyValid();
throw new KipperInternalError("Object literal custom types are not implemented.");

// TODO! Implement custom type generation from object literal
// const fields: CustomTypeFields = new Map();
// for (const field of objectLiteral.fields) {
// fields.set(field.identifier, field.type);
// }
// return new CustomType("object", "interface", fields);
const fields: CustomTypeFields = new Map();
for (const field of objectPrimaryExpression.getSemanticData().keyValuePairs) {
fields.set(field.getSemanticData().identifier, field.getTypeSemanticData().evaluatedType);
}
return new CustomType("", "interface", fields);
}

/**
Expand Down
41 changes: 33 additions & 8 deletions kipper/target-js/src/code-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
BitwiseXorExpression,
BoolPrimaryExpression,
CastOrConvertExpression,
ClassDeclaration,
ComparativeExpression,
ComparativeExpressionSemantics,
ConditionalExpression,
Expand All @@ -29,6 +30,9 @@ import type {
IdentifierTypeSpecifierExpression,
IncrementOrDecrementPostfixExpression,
IncrementOrDecrementUnaryExpression,
InterfaceDeclaration,
InterfaceMethodDeclaration,
InterfacePropertyDeclaration,
JumpStatement,
KipperProgramContext,
LambdaExpression,
Expand All @@ -53,20 +57,16 @@ import type {
TranslatedExpression,
TypeofTypeSpecifierExpression,
VoidOrNullOrUndefinedPrimaryExpression,
InterfacePropertyDeclaration,
WhileLoopIterationStatement,
InterfaceDeclaration,
ClassDeclaration,
InterfaceMethodDeclaration,
} from "@kipper/core";
import {
BuiltInTypes,
CompoundStatement,
Expression,
getConversionFunctionIdentifier,
IfStatement,
KipperTargetCodeGenerator,
VariableDeclaration,
Expression,
BuiltInTypes,
} from "@kipper/core";
import { createJSFunctionSignature, getJSFunctionSignature, indentLines, removeBraces } from "./tools";
import { TargetJS, version } from "./index";
Expand Down Expand Up @@ -440,9 +440,30 @@ export class JavaScriptTargetCodeGenerator extends KipperTargetCodeGenerator {
* @since 0.11.0
*/
objectPrimaryExpression = async (node: ObjectPrimaryExpression): Promise<TranslatedExpression> => {
return [];
const semanticData = node.getSemanticData();
const keyValuePairs = semanticData.keyValuePairs;
let keyValuePairsCodeString = await this.getStringifiedKeyValuePairs(keyValuePairs);

Check warning on line 445 in kipper/target-js/src/code-generator.ts

View check run for this annotation

Codecov / codecov/patch

kipper/target-js/src/code-generator.ts#L443-L445

Added lines #L443 - L445 were not covered by tests

return ["{", ...keyValuePairsCodeString, "}", ";"];

Check warning on line 447 in kipper/target-js/src/code-generator.ts

View check run for this annotation

Codecov / codecov/patch

kipper/target-js/src/code-generator.ts#L447

Added line #L447 was not covered by tests
};

protected async getStringifiedKeyValuePairs(keyValuePairs: Array<ObjectProperty>): Promise<string> {
let keyValuePairsCode: Array<string> = [];

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
Expand All @@ -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}`];
};

/**
Expand Down
13 changes: 12 additions & 1 deletion kipper/target-ts/src/code-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -127,4 +129,13 @@ export class TypeScriptTargetCodeGenerator extends JavaScriptTargetCodeGenerator
// Return the parameter declaration
return [[identifier, ":", " ", valueType]];
};

objectPrimaryExpression = async (node: ObjectPrimaryExpression): Promise<TranslatedExpression> => {
const semanticData = node.getSemanticData();
const keyValuePairs = semanticData.keyValuePairs;
let keyValuePairsCodeString = await this.getStringifiedKeyValuePairs(keyValuePairs);

// Return the object primary expression
return ["{\n", ...keyValuePairsCodeString, "\n}"];
};
}
13 changes: 13 additions & 0 deletions test/module/core/core-functionality.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)");
});

});
});

0 comments on commit b910f64

Please sign in to comment.