Skip to content

Commit

Permalink
add tuple value in semantic tree
Browse files Browse the repository at this point in the history
Signed-off-by: Su Yihan <[email protected]>
  • Loading branch information
yviansu committed Jan 24, 2024
1 parent f6ff63e commit 196b390
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 42 deletions.
103 changes: 63 additions & 40 deletions src/semantics/expression_builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
ObjectType,
ValueTypeWithArguments,
WASMArrayType,
TupleType,
} from './value_types.js';
import { PredefinedTypeId, getNodeLoc, isTypeGeneric } from '../utils.js';
import { Logger } from '../log.js';
Expand All @@ -33,6 +34,7 @@ import {
createObjectType,
SpecializeTypeMapper,
CreateWideTypeFromTypes,
createTupleType,
} from './type_creator.js';

import { GetPredefinedType } from './predefined_types.js';
Expand Down Expand Up @@ -101,6 +103,7 @@ import {
SpreadValue,
TemplateExprValue,
EnumerateKeysGetValue,
NewLiteralTupleValue,
} from './value.js';

import {
Expand Down Expand Up @@ -149,7 +152,7 @@ import {
importSearchTypes,
} from '../scope.js';

import { Type, TSClass, TypeKind, TSArray } from '../type.js';
import { Type, TSClass, TypeKind, TSArray, TSTuple } from '../type.js';

import {
BuildContext,
Expand All @@ -174,6 +177,7 @@ import {
import { processEscape } from '../utils.js';
import { BuiltinNames } from '../../lib/builtin/builtin_name.js';
import { getConfig } from '../../config/config_mgr.js';
import { UnimplementError } from '../error.js';

function isInt(expr: Expression): boolean {
/* TODO: currently we treat all numbers as f64, we can make some analysis and optimize some number to int */
Expand Down Expand Up @@ -937,38 +941,47 @@ function buildArrayLiteralExpression(
if (
expr.exprType instanceof TSArray &&
expr.exprType.elementType.kind == TypeKind.UNKNOWN
)
) {
return new NewArrayLenValue(
GetPredefinedType(PredefinedTypeId.ARRAY_ANY)! as ArrayType,
new LiteralValue(Primitive.Int, 0),
);
}
}

// TODO: arrayLiteral may be tuple type

const init_values: SemanticsValue[] = [];
let array_type = context.findValueType(expr.exprType);

let init_types: Set<ValueType> | undefined = undefined;
if (!array_type || array_type.kind != ValueTypeKind.ARRAY) {
init_types = new Set<ValueType>();
let arrayLiteral_type = context.findValueType(expr.exprType);
/* ArrayLiteral may be array type, and it can be tuple type */
let init_array_types: Set<ValueType> | undefined = undefined;
if (expr.exprType instanceof TSArray) {
if (
!arrayLiteral_type ||
arrayLiteral_type.kind != ValueTypeKind.ARRAY
) {
init_array_types = new Set<ValueType>();
}
}

// element type calculated from exprType
let element_type: ValueType | undefined;
if (array_type instanceof ArrayType) {
element_type = (<ArrayType>array_type).element;
let element_type: ValueType | undefined = undefined;
if (arrayLiteral_type instanceof ArrayType) {
element_type = (<ArrayType>arrayLiteral_type).element;
}

for (const element of expr.arrayValues) {
for (let i = 0; i < expr.arrayValues.length; i++) {
const element = expr.arrayValues[i];
context.pushReference(ValueReferenceKind.RIGHT);
let v = buildExpression(element, context);
if (element_type != undefined) {
/* get element type if exprType is TSTuple */
if (expr.exprType instanceof TSTuple) {
element_type = createType(context, expr.exprType.elements[i]);
}
if (element_type !== undefined) {
v = newCastValue(element_type, v);
}
context.popReference();
init_values.push(v);
// if v is SpreadValue, add it's elem-type to init_types
/* if v is SpreadValue, add it's elem-type to init_types */
let v_type = v.type;
if (v instanceof SpreadValue) {
const target = v.target;
Expand All @@ -978,39 +991,49 @@ function buildArrayLiteralExpression(
v_type = target.type;
}
}
if (init_types) {
init_types.add(v_type);
if (init_array_types) {
init_array_types.add(v_type);
}
}

if (init_types) {
array_type = createArrayType(
if (init_array_types) {
arrayLiteral_type = createArrayType(
context,
CreateWideTypeFromTypes(context, init_types),
CreateWideTypeFromTypes(context, init_array_types),
);
}

const elem_type = (array_type as ArrayType).element;
const initValues =
expr.arrayValues.length == 0
? []
: init_values.map((v) => {
return elem_type.equals(v.type)
? v
: newCastValue(elem_type, v);
});
// process generic array type
if (initValues.length > 0) {
// actual element type
const value_type = initValues[0].type;
if (
elem_type.kind == ValueTypeKind.TYPE_PARAMETER &&
!value_type.equals(elem_type)
)
array_type = createArrayType(context, value_type);
if (arrayLiteral_type instanceof ArrayType) {
const elem_type = (arrayLiteral_type as ArrayType).element;
const initValues =
expr.arrayValues.length == 0
? []
: init_values.map((v) => {
return elem_type.equals(v.type)
? v
: newCastValue(elem_type, v);
});
/* process generic array type */
if (initValues.length > 0) {
// actual element type
const value_type = initValues[0].type;
if (
elem_type.kind == ValueTypeKind.TYPE_PARAMETER &&
!value_type.equals(elem_type)
)
arrayLiteral_type = createArrayType(context, value_type);
}
return new NewLiteralArrayValue(arrayLiteral_type!, initValues);
} else if (arrayLiteral_type instanceof TupleType) {
return new NewLiteralTupleValue(
createType(context, expr.exprType),
init_values,
);
} else {
throw new UnimplementError(
`ArrayLiteralExpression's parse not implemented`,
);
}

return new NewLiteralArrayValue(array_type!, initValues);
}

export function isEqualOperator(kind: ts.SyntaxKind): boolean {
Expand Down
16 changes: 16 additions & 0 deletions src/semantics/semantics_nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
EnumType,
ObjectType,
WASM,
TupleType,
} from './value_types.js';

import { GetPredefinedType } from './predefined_types.js';
Expand Down Expand Up @@ -909,6 +910,21 @@ export class ModuleNode extends SemanticsNode {
return undefined;
}

findTupleElementTypes(elementTypes: ValueType[]): ValueType | undefined {
for (const t of this.types.values()) {
if (
t.kind == ValueTypeKind.TUPLE &&
(t as TupleType).elements.toString() === elementTypes.toString()
) {
Logger.debug(
`==== t: ${t}, elementTypes in tupleType: ${elementTypes}`,
);
return t;
}
}
return undefined;
}

findObjectValueType(tsclass: TSClass): ValueType | undefined {
for (const t of this.types.keys()) {
if (!(t instanceof TSClass)) {
Expand Down
11 changes: 11 additions & 0 deletions src/semantics/type_creator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,17 @@ export function createArrayType(
return specializeBuiltinObjectType('Array', [element_type])! as ArrayType;
}

export function createTupleType(
context: BuildContext,
element_types: ValueType[],
): TupleType {
const tuple_type = context.module.findTupleElementTypes(element_types);
if (tuple_type) {
return tuple_type as TupleType;
}
return new TupleType(context.nextTypeId(), element_types);
}

function createTypeScores(): Map<ValueTypeKind, number> {
const m = new Map<ValueTypeKind, number>();

Expand Down
7 changes: 7 additions & 0 deletions src/semantics/value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export enum SemanticsValueKind {
NEW_CONSTRCTOR_OBJECT,
NEW_LITERAL_OBJECT,
NEW_LITERAL_ARRAY,
NEW_LITERAL_TUPLE,
NEW_ARRAY,
NEW_ARRAY_LEN,
NEW_FROM_CLASS_OBJECT,
Expand Down Expand Up @@ -1175,6 +1176,12 @@ export class NewLiteralArrayValue extends SemanticsValue {
}
}

export class NewLiteralTupleValue extends SemanticsValue {
constructor(type: ValueType, public initValues: SemanticsValue[]) {
super(SemanticsValueKind.NEW_LITERAL_TUPLE, type);
}
}

export class NewConstructorObjectValue extends SemanticsValue {
constructor(
type: ValueType,
Expand Down
14 changes: 12 additions & 2 deletions src/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1526,9 +1526,10 @@ export class TypeResolver {
/* 6. parse tsType to type */
let type = this.tsTypeToType(tsType, (node as any).type);
/* 7. set right value's type based on left value's type */
/* for example, a: string[] = new Array(), the type of new Array() should be string[]
instead of any[]*/
if (type instanceof TSArray) {
/** Example: a: string[] = new Array()
* the type of new Array() should be string[], instead of any[]
*/
const parentNode = node.parent;
if (
ts.isVariableDeclaration(parentNode) ||
Expand All @@ -1549,6 +1550,15 @@ export class TypeResolver {
.elementType;
}
}
} else if (type instanceof TSTuple) {
/** Example: const a: [i32, string] = [1, 'hi'];
* the type of [1, 'hi'] should be [i32, string], instead of [number, string]
*/
const parentNode = node.parent;
if (ts.isArrayLiteralExpression(node)) {
const parentType = <TSTuple>this.generateNodeType(parentNode);
type = parentType;
}
}
this.nodeTypeCache.set(node, type);
return type;
Expand Down

0 comments on commit 196b390

Please sign in to comment.