Skip to content

Commit

Permalink
Merge pull request #660 from Kipper-Lang/526-feature-add-full-object-…
Browse files Browse the repository at this point in the history
…support-and-translation-for-jsts-targets

Added full object support and translation for JavaScript & TypeScript targets
  • Loading branch information
ByteWolf-dev authored Jul 16, 2024
2 parents ec3e373 + 7d3d2e9 commit e3ad3e2
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 25 deletions.
8 changes: 5 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ To use development versions of Kipper download the

### Added

- Added semantic checking and code generation for object literals and object properties.
([#526](https://github.com/Kipper-Lang/Kipper/issues/526))
- 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
implementation of all custom types in the future. ([#524](https://github.com/Kipper-Lang/Kipper/issues/524))
- Implemented the generic `Array<T>` type and single-type array initializers.
([#499](https://github.com/Kipper-Lang/Kipper/issues/499))
([#499](https://github.com/Kipper-Lang/Kipper/issues/499))
- New module:
- `semantics/runtime-built-ins`, which contains runtime built-in functions, variables and types.
- `semantics/runtime-internals`, which contains the runtime internal functions.
Expand All @@ -43,9 +45,9 @@ To use development versions of Kipper download the
- `GenericArgumentTypeError`, which is thrown when a generic argument is used with an invalid type. This is an error
indicating an invalid logic that should be fixed.
- `InvalidAmountOfGenericArgumentsError`, which is thrown when an invalid amount of generic arguments is used. This is
an error indicating an invalid logic that should be fixed.
an error indicating an invalid logic that should be fixed.
- `CanNotUseNonGenericAsGenericTypeError`, which is thrown when a non-generic type is used as a generic type. This is
an error indicating an invalid logic that should be fixed.
an error indicating an invalid logic that should be fixed.
- New interfaces:
- `InterfaceDeclarationSemantics`, which represents the semantics of an interface declaration.
- `InterfaceDeclarationTypeSemantics`, which represents the type semantics of an interface declaration.
Expand Down
1 change: 1 addition & 0 deletions kipper/core/src/compiler/ast/ast-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ import type {
VariableDeclarationContext,
VoidOrNullOrUndefinedPrimaryExpressionContext,
WhileLoopIterationStatementContext,
InterfaceMemberDeclarationContext,
} from "../lexer-parser";
import type { KipperProgramContext } from "../program-ctx";
import type { CompilableASTNode } from "./compilable-ast-node";
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,9 +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 { 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 @@ -74,9 +82,16 @@ 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> {
throw this.programCtx
.semanticCheck(this)
.notImplementedError(new KipperNotImplementedError("Object Literals have not been implemented yet."));
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: keyValuePairs,
};
}

/**
Expand All @@ -88,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,7 +1,12 @@
import type { PrimaryExpressionSemantics } from "../../primary-expression-semantics";
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 {}
export interface ObjectPropertySemantics extends PrimaryExpressionSemantics {
identifier: string;
expressoDepresso: Expression;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import type { ObjectPropertyContext } from "../../../../../../lexer-parser";
import { KindParseRuleMapping, ParseRuleKindMapping } from "../../../../../../lexer-parser";
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 @@ -74,7 +77,25 @@ 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> {
return; // For now, we don't have any semantic analysis for object properties.
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
}

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;
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: value,
};
}

/**
Expand All @@ -86,7 +107,14 @@ export class ObjectProperty extends PrimaryExpression<ObjectPropertySemantics, O
* @since 0.11.0
*/
public async primarySemanticTypeChecking(): Promise<void> {
return; // For now, we don't have any type checking for object properties.
const semanticData = this.getSemanticData();

const expression = semanticData.expressoDepresso;
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 @@ -114,14 +114,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
19 changes: 17 additions & 2 deletions kipper/target-js/src/code-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -447,15 +447,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;
const translatedKeyValuePairs = await Promise.all(
keyValuePairs.map(async (pair) => {
return [...(await pair.translateCtxAndChildren()), ",", "\n"];

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

View check run for this annotation

Codecov / codecov/patch

kipper/target-js/src/code-generator.ts#L450-L454

Added lines #L450 - L454 were not covered by tests
}),
);

return ["{", "\n", ...indentLines(translatedKeyValuePairs).flat(), "}"];

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L458 was not covered by tests
};

/**
* Translates a {@link ObjectProperty} into the JavaScript language.
* @since 0.11.0
*/
objectProperty = async (node: ObjectProperty): Promise<TranslatedExpression> => {
return [];
const expression = node.getSemanticData().expressoDepresso;
const identifier = node.getSemanticData().identifier;

// 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
19 changes: 17 additions & 2 deletions 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,
InterfaceMethodDeclaration,
TranslatedExpression,
ParameterDeclaration,
TranslatedCodeLine,
VariableDeclaration,
} from "@kipper/core";
import { createTSFunctionSignature, getTSFunctionSignature } from "./tools";
import { JavaScriptTargetCodeGenerator } from "@kipper/target-js";
import { indentLines, 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,17 @@ export class TypeScriptTargetCodeGenerator extends JavaScriptTargetCodeGenerator
// Return the parameter declaration
return [[identifier, ":", " ", valueType]];
};

override objectPrimaryExpression = async (node: ObjectPrimaryExpression): Promise<TranslatedExpression> => {
const semanticData = node.getSemanticData();
const keyValuePairs = semanticData.keyValuePairs;
const translatedKeyValuePairs = await Promise.all(
keyValuePairs.map(async (pair) => {
return [...(await pair.translateCtxAndChildren()), ",", "\n"];
}),
);

// Return the object primary expression
return ["{", "\n", ...indentLines(translatedKeyValuePairs).flat(), "}"];
};
}
12 changes: 12 additions & 0 deletions test/module/core/core-functionality.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1518,4 +1518,16 @@ 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 e3ad3e2

Please sign in to comment.