diff --git a/packages/zenscript/src/builtins/bool.dzs b/packages/zenscript/src/builtins/bool.dzs index 1df8d3f5..79d0865c 100644 --- a/packages/zenscript/src/builtins/bool.dzs +++ b/packages/zenscript/src/builtins/bool.dzs @@ -1 +1,12 @@ -zenClass bool +zenClass bool { + operator !() as bool; + + operator &(value as bool) as bool; + operator |(value as bool) as bool; + operator ^(value as bool) as bool; + + operator ==(value as bool) as bool; + operator !=(value as bool) as bool; + + operator as() as string; +} diff --git a/packages/zenscript/src/builtins/byte.dzs b/packages/zenscript/src/builtins/byte.dzs index b88cbffc..1a98933e 100644 --- a/packages/zenscript/src/builtins/byte.dzs +++ b/packages/zenscript/src/builtins/byte.dzs @@ -1 +1,23 @@ -zenClass byte +zenClass byte { + operator -() as byte; + operator !() as byte; + + operator &(value as byte) as byte; + operator |(value as byte) as byte; + operator ^(value as byte) as byte; + + operator +(value as byte) as byte; + operator -(value as byte) as byte; + operator *(value as byte) as byte; + operator /(value as byte) as byte; + operator %(value as byte) as byte; + + operator <(value as byte) as bool; + operator >(value as byte) as bool; + operator <=(value as byte) as bool; + operator >=(value as byte) as bool; + operator ==(value as byte) as bool; + operator !=(value as byte) as bool; + + operator as() as short, int, long, float, double, string; +} diff --git a/packages/zenscript/src/builtins/double.dzs b/packages/zenscript/src/builtins/double.dzs index 39ec4f23..cd26b15e 100644 --- a/packages/zenscript/src/builtins/double.dzs +++ b/packages/zenscript/src/builtins/double.dzs @@ -1 +1,18 @@ -zenClass double +zenClass double { + operator -() as double; + + operator +(value as double) as double; + operator -(value as double) as double; + operator *(value as double) as double; + operator /(value as double) as double; + operator %(value as double) as double; + + operator <(value as double) as bool; + operator >(value as double) as bool; + operator <=(value as double) as bool; + operator >=(value as double) as bool; + operator ==(value as double) as bool; + operator !=(value as double) as bool; + + operator as() as byte, short, int, long, float, string; +} diff --git a/packages/zenscript/src/builtins/float.dzs b/packages/zenscript/src/builtins/float.dzs index 873011b0..5c7d817b 100644 --- a/packages/zenscript/src/builtins/float.dzs +++ b/packages/zenscript/src/builtins/float.dzs @@ -1 +1,18 @@ -zenClass float +zenClass float { + operator -() as float; + + operator +(value as float) as float; + operator -(value as float) as float; + operator *(value as float) as float; + operator /(value as float) as float; + operator %(value as float) as float; + + operator <(value as float) as bool; + operator >(value as float) as bool; + operator <=(value as float) as bool; + operator >=(value as float) as bool; + operator ==(value as float) as bool; + operator !=(value as float) as bool; + + operator as() as byte, short, int, long, double, string; +} diff --git a/packages/zenscript/src/builtins/int.dzs b/packages/zenscript/src/builtins/int.dzs index 700903b9..28e1a270 100644 --- a/packages/zenscript/src/builtins/int.dzs +++ b/packages/zenscript/src/builtins/int.dzs @@ -1 +1,27 @@ -zenClass int +import stanhebben.zenscript.value.IntRange; + +zenClass int { + operator -() as int; + operator !() as int; + + operator &(value as int) as int; + operator |(value as int) as int; + operator ^(value as int) as int; + + operator +(value as int) as int; + operator -(value as int) as int; + operator *(value as int) as int; + operator /(value as int) as int; + operator %(value as int) as int; + + operator <(value as int) as bool; + operator >(value as int) as bool; + operator <=(value as int) as bool; + operator >=(value as int) as bool; + operator ==(value as int) as bool; + operator !=(value as int) as bool; + + operator ..(to as int) as IntRange; + + operator as() as byte, short, long, float, double, string; +} diff --git a/packages/zenscript/src/builtins/long.dzs b/packages/zenscript/src/builtins/long.dzs index 00c175ff..df8c964c 100644 --- a/packages/zenscript/src/builtins/long.dzs +++ b/packages/zenscript/src/builtins/long.dzs @@ -1 +1,23 @@ -zenClass long +zenClass long { + operator -() as long; + operator !() as long; + + operator &(value as long) as long; + operator |(value as long) as long; + operator ^(value as long) as long; + + operator +(value as long) as long; + operator -(value as long) as long; + operator *(value as long) as long; + operator /(value as long) as long; + operator %(value as long) as long; + + operator <(value as long) as bool; + operator >(value as long) as bool; + operator <=(value as long) as bool; + operator >=(value as long) as bool; + operator ==(value as long) as bool; + operator !=(value as long) as bool; + + operator as() as byte, short, int, float, double, string; +} diff --git a/packages/zenscript/src/builtins/short.dzs b/packages/zenscript/src/builtins/short.dzs index de4b3b14..8940aee2 100644 --- a/packages/zenscript/src/builtins/short.dzs +++ b/packages/zenscript/src/builtins/short.dzs @@ -1 +1,23 @@ -zenClass short +zenClass short { + operator -() as short; + operator !() as short; + + operator &(value as short) as short; + operator |(value as short) as short; + operator ^(value as short) as short; + + operator +(value as short) as short; + operator -(value as short) as short; + operator *(value as short) as short; + operator /(value as short) as short; + operator %(value as short) as short; + + operator <(value as short) as bool; + operator >(value as short) as bool; + operator <=(value as short) as bool; + operator >=(value as short) as bool; + operator ==(value as short) as bool; + operator !=(value as short) as bool; + + operator as() as byte, int, long, float, double, string; +} diff --git a/packages/zenscript/src/builtins/string.dzs b/packages/zenscript/src/builtins/string.dzs index 99675566..1b77356f 100644 --- a/packages/zenscript/src/builtins/string.dzs +++ b/packages/zenscript/src/builtins/string.dzs @@ -84,4 +84,16 @@ zenClass string { function lastIndexOf(needle as string, fromIndex as int) as int; function split(separator as string, maximum as int = 0) as [string]; + + operator +(value as string) as string; + + operator has(substr as string) as bool; + + operator [](index as int) as string; + + operator ==(value as string) as bool; + + operator !=(value as string) as bool; + + operator as() as bool, byte, int, short, long, float, double; } diff --git a/packages/zenscript/src/reference/member-provider.ts b/packages/zenscript/src/reference/member-provider.ts index cde086f4..5da1e2fa 100644 --- a/packages/zenscript/src/reference/member-provider.ts +++ b/packages/zenscript/src/reference/member-provider.ts @@ -1,10 +1,10 @@ import type { AstNode, Stream } from 'langium' -import type { ZenScriptAstType } from '../generated/ast' +import type { OperatorFunctionDeclaration, ZenScriptAstType } from '../generated/ast' import type { ZenScriptServices } from '../module' import type { TypeComputer } from '../typing/type-computer' import type { ZenScriptSyntheticAstType } from './synthetic' import { EMPTY_STREAM, stream } from 'langium' -import { isClassDeclaration, isVariableDeclaration } from '../generated/ast' +import { isClassDeclaration, isOperatorFunctionDeclaration, isVariableDeclaration } from '../generated/ast' import { ClassType, isAnyType, isClassType, isFunctionType, type Type, type ZenScriptType } from '../typing/type-description' import { isStatic, streamClassChain, streamDeclaredMembers } from '../utils/ast' import { defineRules } from '../utils/rule' @@ -12,6 +12,7 @@ import { isSyntheticAstNode } from './synthetic' export interface MemberProvider { streamMembers: (source: AstNode | Type | undefined) => Stream + streamOperators: (source: AstNode | Type | undefined) => Stream } type SourceMap = ZenScriptAstType & ZenScriptType & ZenScriptSyntheticAstType @@ -24,10 +25,14 @@ export class ZenScriptMemberProvider implements MemberProvider { this.typeComputer = services.typing.TypeComputer } - streamMembers(source: AstNode | Type | undefined): Stream { + public streamMembers(source: AstNode | Type | undefined): Stream { return this.rules(source?.$type)?.call(this, source) ?? EMPTY_STREAM } + public streamOperators(source: AstNode | Type | undefined): Stream { + return this.streamMembers(source).filter(isOperatorFunctionDeclaration) + } + private readonly rules = defineRules({ SyntheticHierarchyNode: (source) => { const declarations = stream(source.children.values()) diff --git a/packages/zenscript/src/typing/type-computer.ts b/packages/zenscript/src/typing/type-computer.ts index 8f2a4da4..0e4d7f95 100644 --- a/packages/zenscript/src/typing/type-computer.ts +++ b/packages/zenscript/src/typing/type-computer.ts @@ -147,13 +147,12 @@ export class ZenScriptTypeComputer implements TypeComputer { return } - const operatorDecl = this.memberProvider().streamMembers(rangeType) - .filter(it => isOperatorFunctionDeclaration(it)) + const operator = this.memberProvider().streamOperators(rangeType) .filter(it => it.op === 'for') .filter(it => it.parameters.length === length) .head() - let paramType = this.inferType(operatorDecl?.parameters.at(index)) + let paramType = this.inferType(operator?.parameters.at(index)) if (isClassType(rangeType)) { paramType = paramType?.substituteTypeParameters(rangeType.substitutions) } @@ -201,56 +200,72 @@ export class ZenScriptTypeComputer implements TypeComputer { // endregion // region Expression - Assignment: (source) => { - return this.inferType(source.right) + Assignment: () => { + return this.classTypeOf('void') }, - ConditionalExpression: (_) => { - // TODO: operator overloading - return this.classTypeOf('bool') + ConditionalExpression: (source) => { + return this.inferType(source.second) ?? this.inferType(source.third) }, PrefixExpression: (source) => { switch (source.op) { case '-': - return this.classTypeOf('int') - case '!': - return this.classTypeOf('bool') + case '!': { + const operator = this.memberProvider().streamOperators(source.expr) + .filter(it => it.op === source.op) + .filter(it => it.parameters.length === 0) + .head() + return this.inferType(operator?.returnTypeRef) + } } }, InfixExpression: (source) => { - // TODO: operator overloading switch (source.op) { - case '+': + case '&': // Bitwise + case '|': + case '^': + case '+': // Arithmetic case '-': case '*': case '/': case '%': - return this.classTypeOf('int') - case '<': + case '<': // Comparison case '>': case '<=': case '>=': - return this.classTypeOf('bool') case '==': - case '!=': - return this.classTypeOf('bool') - case '&&': + case '!=': { + const operator = this.memberProvider().streamOperators(source.left) + .filter(it => it.op === source.op) + .filter(it => it.parameters.length === 1) + .head() + return this.inferType(operator?.returnTypeRef) + } + case 'has': // Containment + case 'in': { + const operator = this.memberProvider().streamOperators(source.left) + .filter(it => it.op === 'has') + .filter(it => it.parameters.length === 1) + .head() + return this.inferType(operator?.returnTypeRef) + } + case '..': // Range + case 'to': { + const operator = this.memberProvider().streamOperators(source.left) + .filter(it => it.op === '..') + .filter(it => it.parameters.length === 1) + .head() + return this.inferType(operator?.returnTypeRef) + } + + case '&&': // Logical case '||': return this.classTypeOf('bool') - case 'has': - case 'in': - return this.classTypeOf('bool') - case '&': - case '|': - case '^': - return this.classTypeOf('int') - case '~': + + case '~': // String Concat return this.classTypeOf('string') - case 'to': - case '..': - return this.classTypeOf('stanhebben.zenscript.value.IntRange') } }, @@ -258,7 +273,7 @@ export class ZenScriptTypeComputer implements TypeComputer { return this.inferType(source.typeRef) }, - InstanceofExpression: (_) => { + InstanceofExpression: () => { return this.classTypeOf('bool') }, @@ -304,12 +319,17 @@ export class ZenScriptTypeComputer implements TypeComputer { }, MemberAccess: (source) => { + const receiverType = this.inferType(source.receiver) + const targetContainer = source.target.ref?.$container if (isOperatorFunctionDeclaration(targetContainer) && targetContainer.op === '.') { - return this.inferType(targetContainer.returnTypeRef) + let dynamicTargetType = this.inferType(targetContainer.returnTypeRef) + if (dynamicTargetType && isClassType(receiverType)) { + dynamicTargetType = dynamicTargetType.substituteTypeParameters(receiverType.substitutions) + } + return dynamicTargetType } - const receiverType = this.inferType(source.receiver) let targetType = this.inferType(source.target.ref) if (targetType && isClassType(receiverType)) { targetType = targetType.substituteTypeParameters(receiverType.substitutions) @@ -323,11 +343,11 @@ export class ZenScriptTypeComputer implements TypeComputer { return receiverType } - const operatorDecl = this.memberProvider().streamMembers(source.receiver) - .filter(it => isOperatorFunctionDeclaration(it)) + const operator = this.memberProvider().streamOperators(source.receiver) .filter(it => it.op === '[]') + .filter(it => it.parameters.length === 1) .head() - let returnType = this.inferType(operatorDecl?.returnTypeRef) + let returnType = this.inferType(operator?.returnTypeRef) if (isClassType(receiverType)) { returnType = returnType?.substituteTypeParameters(receiverType.substitutions) } @@ -344,12 +364,12 @@ export class ZenScriptTypeComputer implements TypeComputer { } }, - NullLiteral: (_) => { - // TODO: does it make sense? + NullLiteral: () => { + // does it make sense? return this.classTypeOf('any') }, - BooleanLiteral: (_) => { + BooleanLiteral: () => { return this.classTypeOf('bool') }, @@ -379,15 +399,15 @@ export class ZenScriptTypeComputer implements TypeComputer { } }, - StringLiteral: (_) => { + StringLiteral: () => { return this.classTypeOf('string') }, - UnquotedString: (_) => { + UnquotedString: () => { return this.classTypeOf('string') }, - StringTemplate: (_) => { + StringTemplate: () => { return this.classTypeOf('string') }, diff --git a/packages/zenscript/test/typing/operator/intellizen.json b/packages/zenscript/test/typing/operator/intellizen.json new file mode 100644 index 00000000..1e293b03 --- /dev/null +++ b/packages/zenscript/test/typing/operator/intellizen.json @@ -0,0 +1,5 @@ +{ + "srcRoots": [ + "./scripts" + ] +} diff --git a/packages/zenscript/test/typing/operator/operator.test.ts b/packages/zenscript/test/typing/operator/operator.test.ts new file mode 100644 index 00000000..e04d60a1 --- /dev/null +++ b/packages/zenscript/test/typing/operator/operator.test.ts @@ -0,0 +1,759 @@ +import type { ExpressionStatement } from '../../../src/generated/ast' +import path from 'node:path' +import { describe, expect, it, suite } from 'vitest' +import { assertNoErrors, createTestServices, getDocument } from '../../utils' + +const services = await createTestServices(__dirname) +const inferType = services.typing.TypeComputer.inferType.bind(services.typing.TypeComputer) + +describe('check operation of bool', async () => { + const document_bool_zs = await getDocument(services, path.resolve(__dirname, 'scripts', 'bool.zs')) + const script_bool_zs = document_bool_zs.parseResult.value + let line = 0 + const next = () => (script_bool_zs.statements[++line] as ExpressionStatement).expr + + it('syntax', () => { + assertNoErrors(document_bool_zs) + }) + + suite('logical', () => { + it('!', () => { + const type_not = inferType(next()) + expect(type_not?.toString()).toBe('bool') + }) + }) + + suite('bitwise', () => { + it('&', () => { + const type_and = inferType(next()) + expect(type_and?.toString()).toBe('bool') + }) + + it('|', () => { + const type_or = inferType(next()) + expect(type_or?.toString()).toBe('bool') + }) + + it('^', () => { + const type_xor = inferType(next()) + expect(type_xor?.toString()).toBe('bool') + }) + }) + + suite('comparison', () => { + it('==', () => { + const type_eq = inferType(next()) + expect(type_eq?.toString()).toBe('bool') + }) + + it('!=', () => { + const type_ne = inferType(next()) + expect(type_ne?.toString()).toBe('bool') + }) + }) +}) + +describe('check operation of string', async () => { + const document_string_zs = await getDocument(services, path.resolve(__dirname, 'scripts', 'string.zs')) + const script_string_zs = document_string_zs.parseResult.value + let line = 0 + const next = () => (script_string_zs.statements[++line] as ExpressionStatement).expr + + it('syntax', () => { + assertNoErrors(document_string_zs) + }) + + suite('binary', () => { + it('+', () => { + const type_has = inferType(next()) + expect(type_has?.toString()).toBe('string') + }) + + it('has', () => { + const type_has = inferType(next()) + expect(type_has?.toString()).toBe('bool') + }) + + it('[]', () => { + const type_index_get = inferType(next()) + expect(type_index_get?.toString()).toBe('string') + }) + + it('==', () => { + const type_eq = inferType(next()) + expect(type_eq?.toString()).toBe('bool') + }) + + it('!=', () => { + const type_ne = inferType(next()) + expect(type_ne?.toString()).toBe('bool') + }) + }) +}) + +describe(`check operation of byte`, async () => { + const document_byte_zs = await getDocument(services, path.resolve(__dirname, 'scripts', 'byte.zs')) + const script_byte_zs = document_byte_zs.parseResult.value + let line = 0 + const next = () => (script_byte_zs.statements[++line] as ExpressionStatement).expr + + it('syntax', () => { + assertNoErrors(document_byte_zs) + }) + + suite('unary', () => { + it('-', () => { + const type_neg = inferType(next()) + expect(type_neg?.toString()).toBe('byte') + }) + + it('!', () => { + const type_not = inferType(next()) + expect(type_not?.toString()).toBe('byte') + }) + }) + + suite('binary', () => { + suite('bitwise', () => { + it('&', () => { + const type_and = inferType(next()) + expect(type_and?.toString()).toBe('byte') + }) + + it('|', () => { + const type_or = inferType(next()) + expect(type_or?.toString()).toBe('byte') + }) + + it('^', () => { + const type_xor = inferType(next()) + expect(type_xor?.toString()).toBe('byte') + }) + }) + + suite('arithmetic', () => { + it('+', () => { + const type_add = inferType(next()) + expect(type_add?.toString()).toBe('byte') + }) + + it('-', () => { + const type_sub = inferType(next()) + expect(type_sub?.toString()).toBe('byte') + }) + + it('*', () => { + const type_mul = inferType(next()) + expect(type_mul?.toString()).toBe('byte') + }) + + it('/', () => { + const type_div = inferType(next()) + expect(type_div?.toString()).toBe('byte') + }) + + it('%', () => { + const type_mod = inferType(next()) + expect(type_mod?.toString()).toBe('byte') + }) + }) + + suite('comparison', () => { + it('<', () => { + const type_lt = inferType(next()) + expect(type_lt?.toString()).toBe('bool') + }) + + it('>', () => { + const type_gt = inferType(next()) + expect(type_gt?.toString()).toBe('bool') + }) + + it('<=', () => { + const type_le = inferType(next()) + expect(type_le?.toString()).toBe('bool') + }) + + it('>=', () => { + const type_ge = inferType(next()) + expect(type_ge?.toString()).toBe('bool') + }) + + it('==', () => { + const type_eq = inferType(next()) + expect(type_eq?.toString()).toBe('bool') + }) + + it('!=', () => { + const type_ne = inferType(next()) + expect(type_ne?.toString()).toBe('bool') + }) + }) + }) +}) + +describe(`check operation of short`, async () => { + const document_short_zs = await getDocument(services, path.resolve(__dirname, 'scripts', 'short.zs')) + const script_short_zs = document_short_zs.parseResult.value + let line = 0 + const next = () => (script_short_zs.statements[++line] as ExpressionStatement).expr + + it('syntax', () => { + assertNoErrors(document_short_zs) + }) + + suite('unary', () => { + it('-', () => { + const type_neg = inferType(next()) + expect(type_neg?.toString()).toBe('short') + }) + + it('!', () => { + const type_not = inferType(next()) + expect(type_not?.toString()).toBe('short') + }) + }) + + suite('binary', () => { + suite('bitwise', () => { + it('&', () => { + const type_and = inferType(next()) + expect(type_and?.toString()).toBe('short') + }) + + it('|', () => { + const type_or = inferType(next()) + expect(type_or?.toString()).toBe('short') + }) + + it('^', () => { + const type_xor = inferType(next()) + expect(type_xor?.toString()).toBe('short') + }) + }) + + suite('arithmetic', () => { + it('+', () => { + const type_add = inferType(next()) + expect(type_add?.toString()).toBe('short') + }) + + it('-', () => { + const type_sub = inferType(next()) + expect(type_sub?.toString()).toBe('short') + }) + + it('*', () => { + const type_mul = inferType(next()) + expect(type_mul?.toString()).toBe('short') + }) + + it('/', () => { + const type_div = inferType(next()) + expect(type_div?.toString()).toBe('short') + }) + + it('%', () => { + const type_mod = inferType(next()) + expect(type_mod?.toString()).toBe('short') + }) + }) + + suite('comparison', () => { + it('<', () => { + const type_lt = inferType(next()) + expect(type_lt?.toString()).toBe('bool') + }) + + it('>', () => { + const type_gt = inferType(next()) + expect(type_gt?.toString()).toBe('bool') + }) + + it('<=', () => { + const type_le = inferType(next()) + expect(type_le?.toString()).toBe('bool') + }) + + it('>=', () => { + const type_ge = inferType(next()) + expect(type_ge?.toString()).toBe('bool') + }) + + it('==', () => { + const type_eq = inferType(next()) + expect(type_eq?.toString()).toBe('bool') + }) + + it('!=', () => { + const type_ne = inferType(next()) + expect(type_ne?.toString()).toBe('bool') + }) + }) + }) +}) + +describe(`check operation of int`, async () => { + const document_int_zs = await getDocument(services, path.resolve(__dirname, 'scripts', 'int.zs')) + const script_int_zs = document_int_zs.parseResult.value + let line = 0 + const next = () => (script_int_zs.statements[++line] as ExpressionStatement).expr + + it('syntax', () => { + assertNoErrors(document_int_zs) + }) + + suite('unary', () => { + it('-', () => { + const type_neg = inferType(next()) + expect(type_neg?.toString()).toBe('int') + }) + + it('!', () => { + const type_not = inferType(next()) + expect(type_not?.toString()).toBe('int') + }) + }) + + suite('binary', () => { + suite('bitwise', () => { + it('&', () => { + const type_and = inferType(next()) + expect(type_and?.toString()).toBe('int') + }) + + it('|', () => { + const type_or = inferType(next()) + expect(type_or?.toString()).toBe('int') + }) + + it('^', () => { + const type_xor = inferType(next()) + expect(type_xor?.toString()).toBe('int') + }) + }) + + suite('arithmetic', () => { + it('+', () => { + const type_add = inferType(next()) + expect(type_add?.toString()).toBe('int') + }) + + it('-', () => { + const type_sub = inferType(next()) + expect(type_sub?.toString()).toBe('int') + }) + + it('*', () => { + const type_mul = inferType(next()) + expect(type_mul?.toString()).toBe('int') + }) + + it('/', () => { + const type_div = inferType(next()) + expect(type_div?.toString()).toBe('int') + }) + + it('%', () => { + const type_mod = inferType(next()) + expect(type_mod?.toString()).toBe('int') + }) + }) + + suite('comparison', () => { + it('<', () => { + const type_lt = inferType(next()) + expect(type_lt?.toString()).toBe('bool') + }) + + it('>', () => { + const type_gt = inferType(next()) + expect(type_gt?.toString()).toBe('bool') + }) + + it('<=', () => { + const type_le = inferType(next()) + expect(type_le?.toString()).toBe('bool') + }) + + it('>=', () => { + const type_ge = inferType(next()) + expect(type_ge?.toString()).toBe('bool') + }) + + it('==', () => { + const type_eq = inferType(next()) + expect(type_eq?.toString()).toBe('bool') + }) + + it('!=', () => { + const type_ne = inferType(next()) + expect(type_ne?.toString()).toBe('bool') + }) + }) + }) +}) + +describe(`check operation of long`, async () => { + const document_long_zs = await getDocument(services, path.resolve(__dirname, 'scripts', 'long.zs')) + const script_long_zs = document_long_zs.parseResult.value + let line = 0 + const next = () => (script_long_zs.statements[++line] as ExpressionStatement).expr + + it('syntax', () => { + assertNoErrors(document_long_zs) + }) + + suite('unary', () => { + it('-', () => { + const type_neg = inferType(next()) + expect(type_neg?.toString()).toBe('long') + }) + + it('!', () => { + const type_not = inferType(next()) + expect(type_not?.toString()).toBe('long') + }) + }) + + suite('binary', () => { + suite('bitwise', () => { + it('&', () => { + const type_and = inferType(next()) + expect(type_and?.toString()).toBe('long') + }) + + it('|', () => { + const type_or = inferType(next()) + expect(type_or?.toString()).toBe('long') + }) + + it('^', () => { + const type_xor = inferType(next()) + expect(type_xor?.toString()).toBe('long') + }) + }) + + suite('arithmetic', () => { + it('+', () => { + const type_add = inferType(next()) + expect(type_add?.toString()).toBe('long') + }) + + it('-', () => { + const type_sub = inferType(next()) + expect(type_sub?.toString()).toBe('long') + }) + + it('*', () => { + const type_mul = inferType(next()) + expect(type_mul?.toString()).toBe('long') + }) + + it('/', () => { + const type_div = inferType(next()) + expect(type_div?.toString()).toBe('long') + }) + + it('%', () => { + const type_mod = inferType(next()) + expect(type_mod?.toString()).toBe('long') + }) + }) + + suite('comparison', () => { + it('<', () => { + const type_lt = inferType(next()) + expect(type_lt?.toString()).toBe('bool') + }) + + it('>', () => { + const type_gt = inferType(next()) + expect(type_gt?.toString()).toBe('bool') + }) + + it('<=', () => { + const type_le = inferType(next()) + expect(type_le?.toString()).toBe('bool') + }) + + it('>=', () => { + const type_ge = inferType(next()) + expect(type_ge?.toString()).toBe('bool') + }) + + it('==', () => { + const type_eq = inferType(next()) + expect(type_eq?.toString()).toBe('bool') + }) + + it('!=', () => { + const type_ne = inferType(next()) + expect(type_ne?.toString()).toBe('bool') + }) + }) + }) +}) + +describe(`check operation of float`, async () => { + const document_float_zs = await getDocument(services, path.resolve(__dirname, 'scripts', 'float.zs')) + const script_float_zs = document_float_zs.parseResult.value + let line = 0 + const next = () => (script_float_zs.statements[++line] as ExpressionStatement).expr + + it('syntax', () => { + assertNoErrors(document_float_zs) + }) + + suite('unary', () => { + it('-', () => { + const type_neg = inferType(next()) + expect(type_neg?.toString()).toBe('float') + }) + }) + + suite('binary', () => { + suite('arithmetic', () => { + it('+', () => { + const type_add = inferType(next()) + expect(type_add?.toString()).toBe('float') + }) + + it('-', () => { + const type_sub = inferType(next()) + expect(type_sub?.toString()).toBe('float') + }) + + it('*', () => { + const type_mul = inferType(next()) + expect(type_mul?.toString()).toBe('float') + }) + + it('/', () => { + const type_div = inferType(next()) + expect(type_div?.toString()).toBe('float') + }) + + it('%', () => { + const type_mod = inferType(next()) + expect(type_mod?.toString()).toBe('float') + }) + }) + + suite('comparison', () => { + it('<', () => { + const type_lt = inferType(next()) + expect(type_lt?.toString()).toBe('bool') + }) + + it('>', () => { + const type_gt = inferType(next()) + expect(type_gt?.toString()).toBe('bool') + }) + + it('<=', () => { + const type_le = inferType(next()) + expect(type_le?.toString()).toBe('bool') + }) + + it('>=', () => { + const type_ge = inferType(next()) + expect(type_ge?.toString()).toBe('bool') + }) + + it('==', () => { + const type_eq = inferType(next()) + expect(type_eq?.toString()).toBe('bool') + }) + + it('!=', () => { + const type_ne = inferType(next()) + expect(type_ne?.toString()).toBe('bool') + }) + }) + }) +}) + +describe(`check operation of double`, async () => { + const document_double_zs = await getDocument(services, path.resolve(__dirname, 'scripts', 'double.zs')) + const script_double_zs = document_double_zs.parseResult.value + let line = 0 + const next = () => (script_double_zs.statements[++line] as ExpressionStatement).expr + + it('syntax', () => { + assertNoErrors(document_double_zs) + }) + + suite('unary', () => { + it('-', () => { + const type_neg = inferType(next()) + expect(type_neg?.toString()).toBe('double') + }) + }) + + suite('binary', () => { + suite('arithmetic', () => { + it('+', () => { + const type_add = inferType(next()) + expect(type_add?.toString()).toBe('double') + }) + + it('-', () => { + const type_sub = inferType(next()) + expect(type_sub?.toString()).toBe('double') + }) + + it('*', () => { + const type_mul = inferType(next()) + expect(type_mul?.toString()).toBe('double') + }) + + it('/', () => { + const type_div = inferType(next()) + expect(type_div?.toString()).toBe('double') + }) + + it('%', () => { + const type_mod = inferType(next()) + expect(type_mod?.toString()).toBe('double') + }) + }) + + suite('comparison', () => { + it('<', () => { + const type_lt = inferType(next()) + expect(type_lt?.toString()).toBe('bool') + }) + + it('>', () => { + const type_gt = inferType(next()) + expect(type_gt?.toString()).toBe('bool') + }) + + it('<=', () => { + const type_le = inferType(next()) + expect(type_le?.toString()).toBe('bool') + }) + + it('>=', () => { + const type_ge = inferType(next()) + expect(type_ge?.toString()).toBe('bool') + }) + + it('==', () => { + const type_eq = inferType(next()) + expect(type_eq?.toString()).toBe('bool') + }) + + it('!=', () => { + const type_ne = inferType(next()) + expect(type_ne?.toString()).toBe('bool') + }) + }) + }) +}) + +describe('check operation of array', async () => { + const document_array_zs = await getDocument(services, path.resolve(__dirname, 'scripts', 'array.zs')) + const script_array_zs = document_array_zs.parseResult.value + let line = 0 + const next = () => (script_array_zs.statements[++line] as ExpressionStatement).expr + + it('syntax', () => { + assertNoErrors(document_array_zs) + }) + + suite('binary', () => { + it('has', () => { + const type_has = inferType(next()) + expect(type_has?.toString()).toBe('bool') + }) + + it('[]', () => { + const type_index_get = inferType(next()) + expect(type_index_get?.toString()).toBe('long') + }) + + it('+=', () => { + const type_add = inferType(next()) + expect(type_add?.toString()).toBe('void') + }) + }) + + suite('ternary', () => { + it('[]=', () => { + const type_index_set = inferType(next()) + expect(type_index_set?.toString()).toBe('void') + }) + }) +}) + +describe('check operation of list', async () => { + const document_list_zs = await getDocument(services, path.resolve(__dirname, 'scripts', 'list.zs')) + const script_list_zs = document_list_zs.parseResult.value + let line = 0 + const next = () => (script_list_zs.statements[++line] as ExpressionStatement).expr + + it('syntax', () => { + assertNoErrors(document_list_zs) + }) + + suite('binary', () => { + it('has', () => { + const type_has = inferType(next()) + expect(type_has?.toString()).toBe('bool') + }) + + it('[]', () => { + const type_index_get = inferType(next()) + expect(type_index_get?.toString()).toBe('long') + }) + + it('+=', () => { + const type_add = inferType(next()) + expect(type_add?.toString()).toBe('void') + }) + }) + + suite('ternary', () => { + it('[]=', () => { + const type_index_set = inferType(next()) + expect(type_index_set?.toString()).toBe('void') + }) + }) +}) + +describe('check operation of map', async () => { + const document_map_zs = await getDocument(services, path.resolve(__dirname, 'scripts', 'map.zs')) + const script_map_zs = document_map_zs.parseResult.value + let line = 0 + const next = () => (script_map_zs.statements[++line] as ExpressionStatement).expr + + it('syntax', () => { + assertNoErrors(document_map_zs) + }) + + suite('binary', () => { + it('has', () => { + const type_has = inferType(next()) + expect(type_has?.toString()).toBe('bool') + }) + + it('[]', () => { + const type_index_get = inferType(next()) + expect(type_index_get?.toString()).toBe('long') + }) + + it('.', () => { + const type_add = inferType(next()) + expect(type_add?.toString()).toBe('long') + }) + }) + + suite('ternary', () => { + it('[]=', () => { + const type_index_set = inferType(next()) + expect(type_index_set?.toString()).toBe('void') + }) + }) +}) diff --git a/packages/zenscript/test/typing/operator/scripts/array.zs b/packages/zenscript/test/typing/operator/scripts/array.zs new file mode 100644 index 00000000..48cc5ed2 --- /dev/null +++ b/packages/zenscript/test/typing/operator/scripts/array.zs @@ -0,0 +1,7 @@ +val arr = [] as long[]; + +arr has 1; +arr[0]; +arr += 1; + +arr[0] = 1; diff --git a/packages/zenscript/test/typing/operator/scripts/bool.zs b/packages/zenscript/test/typing/operator/scripts/bool.zs new file mode 100644 index 00000000..88684410 --- /dev/null +++ b/packages/zenscript/test/typing/operator/scripts/bool.zs @@ -0,0 +1,10 @@ +val bol = true as bool; + +!bol; + +bol & bol; +bol | bol; +bol ^ bol; + +bol == bol; +bol != bol; diff --git a/packages/zenscript/test/typing/operator/scripts/byte.zs b/packages/zenscript/test/typing/operator/scripts/byte.zs new file mode 100644 index 00000000..e176bd8f --- /dev/null +++ b/packages/zenscript/test/typing/operator/scripts/byte.zs @@ -0,0 +1,21 @@ +val one = 1 as byte; + +-one; +!one; + +one & one; +one | one; +one ^ one; + +one + one; +one - one; +one * one; +one / one; +one % one; + +one < one; +one > one; +one <= one; +one >= one; +one == one; +one != one; diff --git a/packages/zenscript/test/typing/operator/scripts/double.zs b/packages/zenscript/test/typing/operator/scripts/double.zs new file mode 100644 index 00000000..3fa3cccf --- /dev/null +++ b/packages/zenscript/test/typing/operator/scripts/double.zs @@ -0,0 +1,16 @@ +val one = 1.0D as double; + +-one; + +one + one; +one - one; +one * one; +one / one; +one % one; + +one < one; +one > one; +one <= one; +one >= one; +one == one; +one != one; diff --git a/packages/zenscript/test/typing/operator/scripts/float.zs b/packages/zenscript/test/typing/operator/scripts/float.zs new file mode 100644 index 00000000..5d4473ad --- /dev/null +++ b/packages/zenscript/test/typing/operator/scripts/float.zs @@ -0,0 +1,16 @@ +val one = 1.0f as float; + +-one; + +one + one; +one - one; +one * one; +one / one; +one % one; + +one < one; +one > one; +one <= one; +one >= one; +one == one; +one != one; diff --git a/packages/zenscript/test/typing/operator/scripts/int.zs b/packages/zenscript/test/typing/operator/scripts/int.zs new file mode 100644 index 00000000..e359c1e1 --- /dev/null +++ b/packages/zenscript/test/typing/operator/scripts/int.zs @@ -0,0 +1,21 @@ +val one = 1 as int; + +-one; +!one; + +one & one; +one | one; +one ^ one; + +one + one; +one - one; +one * one; +one / one; +one % one; + +one < one; +one > one; +one <= one; +one >= one; +one == one; +one != one; diff --git a/packages/zenscript/test/typing/operator/scripts/list.zs b/packages/zenscript/test/typing/operator/scripts/list.zs new file mode 100644 index 00000000..2bddd9ba --- /dev/null +++ b/packages/zenscript/test/typing/operator/scripts/list.zs @@ -0,0 +1,7 @@ +val list = [] as [long]; + +list has 1; +list[0]; +list += 1; + +list[0] = 1; diff --git a/packages/zenscript/test/typing/operator/scripts/long.zs b/packages/zenscript/test/typing/operator/scripts/long.zs new file mode 100644 index 00000000..8f18e11c --- /dev/null +++ b/packages/zenscript/test/typing/operator/scripts/long.zs @@ -0,0 +1,21 @@ +val one = 1L as long; + +-one; +!one; + +one & one; +one | one; +one ^ one; + +one + one; +one - one; +one * one; +one / one; +one % one; + +one < one; +one > one; +one <= one; +one >= one; +one == one; +one != one; diff --git a/packages/zenscript/test/typing/operator/scripts/map.zs b/packages/zenscript/test/typing/operator/scripts/map.zs new file mode 100644 index 00000000..9c23053f --- /dev/null +++ b/packages/zenscript/test/typing/operator/scripts/map.zs @@ -0,0 +1,7 @@ +val map = {} as long[string]; + +map has "k"; +map["k"]; +map.k; + +map["k"] = 1L; diff --git a/packages/zenscript/test/typing/operator/scripts/short.zs b/packages/zenscript/test/typing/operator/scripts/short.zs new file mode 100644 index 00000000..05a6ae08 --- /dev/null +++ b/packages/zenscript/test/typing/operator/scripts/short.zs @@ -0,0 +1,21 @@ +val one = 1 as short; + +-one; +!one; + +one & one; +one | one; +one ^ one; + +one + one; +one - one; +one * one; +one / one; +one % one; + +one < one; +one > one; +one <= one; +one >= one; +one == one; +one != one; diff --git a/packages/zenscript/test/typing/operator/scripts/string.zs b/packages/zenscript/test/typing/operator/scripts/string.zs new file mode 100644 index 00000000..3ca22a60 --- /dev/null +++ b/packages/zenscript/test/typing/operator/scripts/string.zs @@ -0,0 +1,8 @@ +val str = "str" as string; + +str + str; +str has str; +str[0]; + +str == str; +str != str;