From 13b6661d7d2225dc03240b4d8a2b2b77cd7f7b39 Mon Sep 17 00:00:00 2001 From: "Marc J. Schmidt" Date: Fri, 29 Jul 2022 05:26:15 +0200 Subject: [PATCH] basic class support + property access --- src/checker/MemoryPool.h | 1 - src/checker/compiler.h | 228 +++++++++++++++++++++++------------ src/checker/debug.h | 3 + src/checker/instructions.h | 11 +- src/checker/types2.h | 8 ++ src/checker/utils.h | 3 + src/checker/vm2.cpp | 238 ++++++++++++++++++++++++++++++++----- src/factory.h | 24 ++-- src/parser2.h | 41 ++++--- src/tests/test_vm2.cpp | 14 +++ 10 files changed, 430 insertions(+), 141 deletions(-) diff --git a/src/checker/MemoryPool.h b/src/checker/MemoryPool.h index da52cdd..8ec2339 100644 --- a/src/checker/MemoryPool.h +++ b/src/checker/MemoryPool.h @@ -9,7 +9,6 @@ template class MemoryPool { public: - /* Member types */ typedef T value_type; typedef T *pointer; typedef T &reference; diff --git a/src/checker/compiler.h b/src/checker/compiler.h index c78a872..76e920c 100644 --- a/src/checker/compiler.h +++ b/src/checker/compiler.h @@ -765,6 +765,85 @@ namespace ts::checker { return std::move(program); } + void pushName(sharedOpt name, Program &program) { + if (!name) { + program.pushOp(OP::Never); + return; + } + + if (name->kind == SyntaxKind::Identifier) { + program.pushStringLiteral(to(name)->escapedText, name); + } else { + //computed type name like `[a]: string` + handle(name, program); + } + } + + void pushFunction(OP op, sharedOpt node, Program &program, sharedOpt withName) { + if (node->typeParameters) { + //when there are type parameters, FunctionDeclaration returns a FunctionRef + //which indicates the VM that the function needs to be instantiated first. + + auto subroutineIndex = program.pushSubroutineNameLess(); + unsigned int size = 0; + for (auto &¶m: node->typeParameters->list) { + handle(param, program); + } + program.pushSlots(); + + //first comes the return type + size++; + if (node->type) { + handle(node->type, program); + } else { + //todo: Infer from body, put into own subroutine so it is cached + program.pushOp(OP::Unknown); + if (node->body) { + } else { + } + } + + for (auto &¶m: node->parameters->list) { + size++; + handle(param, program); + } + + if (withName) { + pushName(withName, program); + } + program.pushOp(op, reinterpret_pointer_cast(node)); + program.pushUint16(size); + program.popSubroutine(); + + program.pushOp(OP::FunctionRef, reinterpret_pointer_cast(node)); + program.pushAddress(subroutineIndex); + } else { + unsigned int size = 0; + //first comes the return type + size++; + if (node->type) { + handle(node->type, program); + } else { + //todo: Infer from body + program.pushOp(OP::Unknown); + if (node->body) { + } else { + } + } + + for (auto &¶m: node->parameters->list) { + size++; + handle(param, program); + } + + if (withName) { + pushName(withName, program); + } + program.pushOp(op, reinterpret_pointer_cast(node)); + program.pushUint16(size); + } + } + void handle(const shared &node, Program &program) { switch (node->kind) { case SyntaxKind::SourceFile: { @@ -980,7 +1059,10 @@ namespace ts::checker { } else { program.pushOp(instructions::TypeArgument, n->name); } - //todo constraints + if (n->constraint) { + handle(n->constraint, program); + program.pushOp(instructions::TypeArgumentConstraint); + } break; } case SyntaxKind::FunctionDeclaration: { @@ -990,67 +1072,10 @@ namespace ts::checker { if (symbol.declarations>1) { //todo: embed error since function is declared twice } else { - if (n->typeParameters) { - program.pushSubroutine(symbol); - //when there are type parameters, FunctionDeclaration returns a FunctionRef - //which indicates the VM that the function needs to be instantiated first. - - auto subroutineIndex = program.pushSubroutineNameLess(); - unsigned int size = 0; - for (auto &¶m: n->typeParameters->list) { - handle(param, program); - } - program.pushSlots(); - - //first comes the return type - size++; - if (n->type) { - handle(n->type, program); - } else { - //todo: Infer from body, put into own subroutine so it is cached - program.pushOp(OP::Unknown); - if (n->body) { - } else { - } - } - - for (auto &¶m: n->parameters->list) { - size++; - handle(param, program); - } - program.pushOp(OP::Function, node); - program.pushUint16(size); - program.popSubroutine(); - - program.pushOp(OP::FunctionRef, node); - program.pushAddress(subroutineIndex); - program.popSubroutine(); - } else { - program.pushSubroutine(symbol); - program.pushSlots(); - - unsigned int size = 0; - //first comes the return type - size++; - if (n->type) { - handle(n->type, program); - } else { - //todo: Infer from body - program.pushOp(OP::Unknown); - if (n->body) { - } else { - } - } - - for (auto &¶m: n->parameters->list) { - size++; - handle(param, program); - } - - program.pushOp(OP::Function, node); - program.pushUint16(size); - program.popSubroutine(); - } + program.pushSubroutine(symbol); + program.pushSlots(); + pushFunction(OP::Function, n, program, n->name); + program.popSubroutine(); } } else { debug("No identifier in name"); @@ -1095,15 +1120,11 @@ namespace ts::checker { } else { program.pushOp(OP::Any, n); } - if (n->name->kind == SyntaxKind::Identifier) { - program.pushStringLiteral(to(n->name)->escapedText, n->name); - } else { - //computed type name like `[a]: string` - handle(n->name, program); - } + pushName(n, program); program.pushOp(OP::PropertySignature, n->name); if (n->questionToken) program.pushOp(OP::Optional); if (hasModifier(n, SyntaxKind::ReadonlyKeyword)) program.pushOp(OP::Readonly); + if (hasModifier(n, SyntaxKind::StaticKeyword)) program.pushOp(OP::Static); break; } case SyntaxKind::PropertySignature: { @@ -1113,12 +1134,7 @@ namespace ts::checker { } else { program.pushOp(OP::Any); } - if (n->name->kind == SyntaxKind::Identifier) { - program.pushStringLiteral(to(n->name)->escapedText, n->name); - } else { - //computed type name like `[a]: string` - handle(n->name, program); - } + pushName(n, program); program.pushOp(OP::PropertySignature, node); if (n->questionToken) program.pushOp(OP::Optional); if (hasModifier(n, SyntaxKind::ReadonlyKeyword)) program.pushOp(OP::Readonly); @@ -1256,7 +1272,7 @@ namespace ts::checker { program.pushOp(OP::Distribute); distributeJumpIp = program.ip(); - program.pushUint16(symbol.index); + program.pushSymbolAddress(symbol); program.pushAddress(0); } @@ -1337,6 +1353,64 @@ namespace ts::checker { //todo: handle `as const`, widen if not const break; } + case SyntaxKind::PropertyAccessExpression: { + //e.g. object.method + auto n = to(node); + handle(n->expression, program); + //to avoid resolving Identifier to a symbol, we handle it manually + pushName(n->name, program); + program.pushOp(OP::PropertyAccess, node); + break; + } + case SyntaxKind::ClassExpression: { + //todo: class expression, e.g. `const a = class {}` + throw std::runtime_error("class expression not supported"); + break; + } + case SyntaxKind::ClassDeclaration: { + auto n = to(node); + + if (!n->name) { + throw std::runtime_error("class without name not supported"); + } + + auto &symbol = program.pushSymbolForRoutine(n->name->escapedText, SymbolType::Class, n); //move this to earlier symbol-scan round + if (symbol.declarations>1) { + //todo: for functions/variable embed an error that symbol was declared twice in the same scope + throw std::runtime_error("Nope"); + } else { + //populate routine + program.pushSubroutine(symbol); + program.blockTailCall(); + + if (n->typeParameters) { + for (auto &&p: n->typeParameters->list) { + handle(p, program); + } + } + program.pushSlots(); + + unsigned int size = 0; + for (auto &&member: n->members->list) { + size++; + handle(member, program); + } + program.pushOp(OP::Class, node); + program.pushUint16(size); + program.popSubroutine(); + } + break; + } + case SyntaxKind::MethodDeclaration: { + auto n = to(node); + + pushFunction(OP::Method, n, program, n->name); + + if (n->questionToken) program.pushOp(OP::Optional); + if (hasModifier(n, SyntaxKind::ReadonlyKeyword)) program.pushOp(OP::Readonly); + if (hasModifier(n, SyntaxKind::StaticKeyword)) program.pushOp(OP::Static); + break; + } case SyntaxKind::ArrayType: { auto n = to(node); handle(n->elementType, program); @@ -1453,6 +1527,10 @@ namespace ts::checker { program.popSubroutine(); } } + if (symbol.routine) { + program.pushOp(OP::SelfCheck); + program.pushAddress(symbol.routine->index); + } } } else { debug("No identifier in name"); diff --git a/src/checker/debug.h b/src/checker/debug.h index b8b26a0..ca588fe 100644 --- a/src/checker/debug.h +++ b/src/checker/debug.h @@ -141,6 +141,7 @@ namespace ts::checker { newLine = true; break; } + case OP::SelfCheck: case OP::Set: case OP::TypeArgumentDefault: { params += fmt::format(" &{}", vm::readUint32(bin, i + 1)); @@ -167,10 +168,12 @@ namespace ts::checker { vm::eatParams(op, &i); break; } + case OP::Method: case OP::Function: case OP::Union: case OP::Tuple: case OP::TemplateLiteral: + case OP::Class: case OP::ObjectLiteral: case OP::Slots: case OP::Loads: { diff --git a/src/checker/instructions.h b/src/checker/instructions.h index 05a0a34..446f9f0 100644 --- a/src/checker/instructions.h +++ b/src/checker/instructions.h @@ -44,9 +44,9 @@ namespace ts::instructions { PropertySignature, Class, - ObjectLiteral, IndexSignature, + PropertyAccess, Array, Tuple, @@ -55,6 +55,7 @@ namespace ts::instructions { Optional, Readonly, + Static, Rest, RestReuse, //in expressions like [...T, x] indicates that T can be stolen instead of copied @@ -75,6 +76,12 @@ namespace ts::instructions { */ Slots, + /** + * Self checks an expression, like variable, class, interface, type, etc. + * TypeScript checks types even if they are never used. This SelfCheck makes sure all expression are visited at least once in main. + */ + SelfCheck, + ///** // * Stack parameter. For each JS variable, JS function, as well as type variables (mapped-type variable for example). // * @@ -98,7 +105,7 @@ namespace ts::instructions { TypeArgument, TypeArgumentDefault, //one parameter with the address of the subroutine of the default value - TypeArgumentConstraint, //expects an entry on the stack + TypeArgumentConstraint, //expects an entry (the constraint) on the stack TemplateLiteral, diff --git a/src/checker/types2.h b/src/checker/types2.h index 6d08040..d11bb32 100644 --- a/src/checker/types2.h +++ b/src/checker/types2.h @@ -21,8 +21,15 @@ namespace ts::vm2 { Number, BigInt, Boolean, + Symbol, Literal, + IndexSignature, + Method, + MethodSignature, + Property, PropertySignature, + Class, + ClassInstance, ObjectLiteral, Union, Array, @@ -48,6 +55,7 @@ namespace ts::vm2 { Stored = 1 << 8, //Used somewhere as cache or as value (subroutine->result for example), and thus can not be stolen/modified RestReuse = 1 << 9, //allow to reuse/steal T in ...T Deleted = 1 << 10, //for debugging purposes + Static = 1 << 11, }; struct Type; diff --git a/src/checker/utils.h b/src/checker/utils.h index 22dc552..399346c 100644 --- a/src/checker/utils.h +++ b/src/checker/utils.h @@ -90,6 +90,7 @@ namespace ts::vm { break; } case OP::Set: + case OP::SelfCheck: case OP::TypeArgumentDefault: { *i += 4; break; @@ -106,10 +107,12 @@ namespace ts::vm { *i += 2; break; } + case OP::Method: case OP::Function: case OP::Union: case OP::Tuple: case OP::TemplateLiteral: + case OP::Class: case OP::ObjectLiteral: case OP::Slots: case OP::CallExpression: { diff --git a/src/checker/vm2.cpp b/src/checker/vm2.cpp index 25c4d72..4dd0ca3 100644 --- a/src/checker/vm2.cpp +++ b/src/checker/vm2.cpp @@ -48,7 +48,7 @@ namespace ts::vm2 { gcQueue[gcQueueIdx++] = type; } - inline Type * widen(Type * type) { + inline Type *widen(Type *type) { switch (type->kind) { case TypeKind::Literal: { if (type->flag & TypeFlag::StringLiteral) return allocate(TypeKind::String); @@ -329,6 +329,48 @@ namespace ts::vm2 { return i; } + Type *resolveObjectIndexType(Type *object, Type *index) { + auto current = (TypeRef *) object->type; + switch (index->kind) { + case TypeKind::Literal: { + while (current) { + auto member = current->type; + switch (member->kind) { + case TypeKind::Method: + case TypeKind::MethodSignature: + case TypeKind::Property: + case TypeKind::PropertySignature: { + if (object->kind == TypeKind::Class && !(member->flag & TypeFlag::Static)) break; + auto first = (TypeRef *) member->type; + //first->type == index compares unique symbol equality + if (first->type == index || first->type->hash == index->hash) return member; + break; + } + } + current = current->next; + } + return allocate(TypeKind::Never); + } + case TypeKind::Number: + case TypeKind::BigInt: + case TypeKind::Symbol: + case TypeKind::String: { + while (current) { + auto member = current->type; + switch (member->kind) { + case TypeKind::IndexSignature: { + //todo + //if (extends(index, member->type)) return member.type; + break; + } + } + current = current->next; + } + return allocate(TypeKind::Never); + } + } + } + /** * Query a container type and return the result. * @@ -342,6 +384,53 @@ namespace ts::vm2 { inline uint64_t lengthHash = hash::const_hash("length"); inline Type *indexAccess(Type *container, Type *index) { + switch (container->kind) { + case TypeKind::Array: { + throw std::runtime_error("Not implemented"); + break; + } + case TypeKind::Tuple: { + if (index->hash == lengthHash) { + auto t = allocate(TypeKind::Literal); + t->setDynamicLiteral(TypeFlag::NumberLiteral, std::to_string(container->size)); + return t; + } + throw std::runtime_error("Not implemented"); + break; + } + case TypeKind::ObjectLiteral: + case TypeKind::ClassInstance: + case TypeKind::Class: { + switch (index->kind) { + case TypeKind::Literal: { + return resolveObjectIndexType(container, index); + } + case TypeKind::Union: { + // const union: TypeUnion = { kind: TypeKind::union, types: [] }; + // for (const t of index.types) { + // const result = resolveObjectIndexType(container, t); + // if (result.kind == TypeKind::never) continue; + // + // if (result.kind == TypeKind::union) { + // for (const resultT of result.types) { + // if (isTypeIncluded(union.types, resultT)) continue; + // union.types.push(resultT); + // } + // } else { + // if (isTypeIncluded(union.types, result)) continue; + // union.types.push(result); + // } + // } + // return unboxUnion(union); + throw std::runtime_error("Not implemented"); + break; + } + default: { + return allocate(TypeKind::Never); + } + } + } + } if (container->kind == TypeKind::Array) { // if ((index.kind == TypeKind::literal && 'number' == typeof index.literal) || index.kind == TypeKind::number) return container.type; // if (index.kind == TypeKind::literal && index.literal == 'length') return { kind: TypeKind::number }; @@ -585,11 +674,36 @@ namespace ts::vm2 { debug("[{}] {} refCount={} {} ref={}", activeSubroutine->ip, title, type->refCount, stringify(type), (void *) type); } + Type *handleFunction(TypeKind kind) { + const auto size = activeSubroutine->parseUint16(); + + auto type = allocate(kind); + //first is the name + type->type = useAsRef(pop()); + + auto types = frame->pop(size); + auto current = (TypeRef *) type->type; + + //second is always the return type + current->next = useAsRef(types[0]); + current = current->next; + + if (types.size()>1) { + for_each(++types.begin(), types.end(), [¤t](auto v) { + current->next = useAsRef(v); + current = current->next; + }); + current->next = nullptr; + } + stack[sp++] = type; + return type; + } + void process() { start: auto &bin = activeSubroutine->module->bin; while (true) { - //debug("[{}:{}] OP {} {}", activeSubroutine->depth, frame->depth, activeSubroutine->ip, (OP) bin[activeSubroutine->ip]); + debug("[{}:{}] OP {} {}", activeSubroutine->depth, frame->depth, activeSubroutine->ip, (OP) bin[activeSubroutine->ip]); switch ((OP) bin[activeSubroutine->ip]) { case OP::Halt: { // activeSubroutine = activeSubroutines.reset(); @@ -628,20 +742,7 @@ namespace ts::vm2 { break; } case OP::Function: { - const auto size = activeSubroutine->parseUint16(); - auto types = frame->pop(size); - auto type = allocate(TypeKind::Function); - //first is always the return type - type->type = useAsRef(types[0]); - if (types.size()>1) { - auto current = (TypeRef *) type->type; - for_each(++types.begin(), types.end(), [¤t](auto v) { - current->next = useAsRef(v); - current = current->next; - }); - current->next = nullptr; - } - stack[sp++] = type; + handleFunction(TypeKind::Function); break; } case OP::FunctionRef: { @@ -666,15 +767,30 @@ namespace ts::vm2 { } break; } + case OP::Static: { + stack[sp - 1]->flag |= TypeFlag::Static; + break; + } + case OP::Optional: { + stack[sp - 1]->flag |= TypeFlag::Optional; + break; + break; + } case OP::CallExpression: { const auto parameterAmount = activeSubroutine->parseUint16(); auto parameters = frame->pop(parameterAmount); auto typeToCall = pop(); switch (typeToCall->kind) { + case TypeKind::Method: case TypeKind::Function: { //it's important to handle parameters/typeArguments first before changing the stack with push() since `parameters` is a std::span. auto current = (TypeRef *) typeToCall->type; + + //first always the name, so we jump over it + current = (TypeRef *) current->next; + auto returnType = current->type; + for (unsigned int i = 0;; i++) { current = (TypeRef *) current->next; if (!current) break; //end @@ -699,6 +815,8 @@ namespace ts::vm2 { //we could convert parameters to a tuple and then run isExtendable() on two tuples, //necessary for REST parameters. + + push(returnType); break; } default: { @@ -791,6 +909,19 @@ namespace ts::vm2 { break; //} } + case OP::SelfCheck: { + const auto address = activeSubroutine->parseUint32(); + //todo: this needs more definition: A type alias like `type a = T`; needs to type check as well without throwing `Generic type 'a' requires 1 type argument(s).` + auto routine = activeSubroutine->module->getSubroutine(address); + if (routine->result) { + break; + } + + if (call(address, 0)) { + goto start; + } + break; + } case OP::Call: { const auto address = activeSubroutine->parseUint32(); const auto arguments = activeSubroutine->parseUint16(); @@ -913,12 +1044,24 @@ namespace ts::vm2 { sp += size; break; } + case OP::TypeArgumentConstraint: { + auto constraint = pop(); + if (frame->size() == activeSubroutine->typeArguments) { + auto argument = stack[frame->initialSp + activeSubroutine->typeArguments]; + if (!extends(argument, constraint)) { + report(fmt::format("Type '{}' does not satisfy the constraint '{}'", stringify(argument), stringify(constraint))); + } + } + gc(constraint); + break; + } case OP::TypeArgument: { if (frame->size()<=activeSubroutine->typeArguments) { //all variables will be dropped at the end of the subroutine push(use(allocate(TypeKind::Unknown))); } else { //for provided argument we do not increase refCount, because it's the caller's job + //check constraints } activeSubroutine->typeArguments++; frame->variables++; @@ -1035,25 +1178,60 @@ namespace ts::vm2 { stack[sp++] = item; break; } - case OP::PropertySignature: { - auto nameLiteral = pop(); - auto type = pop(); - switch (nameLiteral->kind) { - case TypeKind::Literal: { - auto item = allocate(TypeKind::PropertySignature); - item->type = use(type); - item->text = nameLiteral->text; - gc(nameLiteral); - push(item); + case OP::PropertyAccess: { + auto name = pop(); + auto container = pop(); + //e.g. container.name + switch (container->kind) { + case TypeKind::Class: { + //MyClass.name, static access + auto type = indexAccess(container, name); + push(type); break; } default: { - //popped types need either be consumed (owned) or deallocated. - gc(type); - gc(nameLiteral); - report("A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type", type); + throw std::runtime_error(fmt::format("Property access to {} not supported", container->kind)); } } + + gc(name); + gc(container); + break; + } + case OP::Method: { + handleFunction(TypeKind::Method); + break; + } + case OP::PropertySignature: { + auto name = pop(); + auto propertyType = pop(); + //PropertySignature has a linked list of name->type + auto type = allocate(TypeKind::PropertySignature); + type->type = useAsRef(name); + ((TypeRef *) type->type)->next = useAsRef(propertyType); + push(type); + break; + } + case OP::Class: { + const auto size = activeSubroutine->parseUint16(); + auto item = allocate(TypeKind::Class); + if (!size) { + item->type = nullptr; + push(item); + break; + } + + auto types = frame->pop(size); + item->type = useAsRef(types[0]); + if (types.size()>1) { + auto current = (TypeRef *) item->type; + for_each(++types.begin(), types.end(), [¤t](auto v) { + current->next = useAsRef(v); + current = current->next; + }); + current->next = nullptr; + } + push(item); break; } case OP::ObjectLiteral: { diff --git a/src/factory.h b/src/factory.h index e04ed09..d7f34cd 100644 --- a/src/factory.h +++ b/src/factory.h @@ -2072,18 +2072,18 @@ namespace ts { // : node; // } // -// // @api -// function createReturnStatement(expression?: Expression): ReturnStatement { -// auto node = createBaseNode(SyntaxKind::ReturnStatement); -// node->expression = expression; -// // return in an ES2018 async generator must be awaited -// node->transformFlags |= -// propagateChildFlags(node->expression) | -// (int)TransformFlags::ContainsES2018 | -// (int)TransformFlags::ContainsHoistedDeclarationOrCompletion; -// return node; -// } -// + // @api + shared createReturnStatement(sharedOpt expression) { + auto node = createBaseNode(SyntaxKind::ReturnStatement); + node->expression = expression; + // return in an ES2018 async generator must be awaited + node->transformFlags |= + propagateChildFlags(node->expression) | + (int)TransformFlags::ContainsES2018 | + (int)TransformFlags::ContainsHoistedDeclarationOrCompletion; + return node; + } + // // @api // function updateReturnStatement(node: ReturnStatement, sharedOpt expression) { // return node->expression != expression diff --git a/src/parser2.h b/src/parser2.h index f202299..050c64e 100644 --- a/src/parser2.h +++ b/src/parser2.h @@ -6943,15 +6943,15 @@ namespace ts { // : factory.createContinueStatement(label); // return withJSDoc(finishNode(node, pos), hasJSDoc); // } -// -// function parseReturnStatement(): ReturnStatement { -// auto pos = getNodePos(); -// auto hasJSDoc = hasPrecedingJSDocComment(); -// parseExpected(SyntaxKind::ReturnKeyword); -// auto expression = canParseSemicolon() ? undefined : allowInAnd>(parseExpression); -// parseSemicolon(); -// return withJSDoc(finishNode(factory.createReturnStatement(expression), pos), hasJSDoc); -// } + + shared parseReturnStatement() { + auto pos = getNodePos(); + auto hasJSDoc = hasPrecedingJSDocComment(); + parseExpected(SyntaxKind::ReturnKeyword); + shared expression = canParseSemicolon() ? nullptr : allowInAnd>(CALLBACK(parseExpression)); + parseSemicolon(); + return withJSDoc(finishNode(factory.createReturnStatement(expression), pos), hasJSDoc); + } // // function parseWithStatement(): WithStatement { // auto pos = getNodePos(); @@ -7267,8 +7267,8 @@ namespace ts { return parseVariableStatement(pos, hasJSDoc, decorators, modifiers); case SyntaxKind::FunctionKeyword: return parseFunctionDeclaration(pos, hasJSDoc, decorators, modifiers); -// case SyntaxKind::ClassKeyword: -// return parseClassDeclaration(pos, hasJSDoc, decorators, modifiers); + case SyntaxKind::ClassKeyword: + return parseClassDeclaration(pos, hasJSDoc, decorators, modifiers); // case SyntaxKind::InterfaceKeyword: // return parseInterfaceDeclaration(pos, hasJSDoc, decorators, modifiers); case SyntaxKind::TypeKeyword: @@ -7323,8 +7323,8 @@ namespace ts { break; case SyntaxKind::FunctionKeyword: return parseFunctionDeclaration(getNodePos(), hasPrecedingJSDocComment(), /*decorators*/ {}, /*modifiers*/ {}); -// case SyntaxKind::ClassKeyword: -// return parseClassDeclaration(getNodePos(), hasPrecedingJSDocComment(), /*decorators*/ {}, /*modifiers*/ {}); + case SyntaxKind::ClassKeyword: + return parseClassDeclaration(getNodePos(), hasPrecedingJSDocComment(), /*decorators*/ {}, /*modifiers*/ {}); // case SyntaxKind::IfKeyword: // return parseIfStatement(); // case SyntaxKind::DoKeyword: @@ -7337,8 +7337,8 @@ namespace ts { // return parseBreakOrContinueStatement(SyntaxKind::ContinueStatement); // case SyntaxKind::BreakKeyword: // return parseBreakOrContinueStatement(SyntaxKind::BreakStatement); -// case SyntaxKind::ReturnKeyword: -// return parseReturnStatement(); + case SyntaxKind::ReturnKeyword: + return parseReturnStatement(); // case SyntaxKind::WithKeyword: // return parseWithStatement(); // case SyntaxKind::SwitchKeyword: @@ -7424,13 +7424,12 @@ namespace ts { // parseDiagnostics.push(createDetachedDiagnostic(fileName, pos, end, diagnostic)); // } } -// -// // DECLARATIONS -// function parseClassDeclaration(int pos, bool hasJSDoc, sharedOpt decorators, sharedOpt modifiers): ClassDeclaration { -// return parseClassDeclarationOrExpression(pos, hasJSDoc, decorators, modifiers, SyntaxKind::ClassDeclaration) as ClassDeclaration; -// } -// + // DECLARATIONS + shared parseClassDeclaration(int pos, bool hasJSDoc, sharedOpt decorators, sharedOpt modifiers) { + return to(parseClassDeclarationOrExpression(pos, hasJSDoc, decorators, modifiers, SyntaxKind::ClassDeclaration)); + } + // function parseInterfaceDeclaration(int pos, bool hasJSDoc, sharedOpt decorators, sharedOpt modifiers): InterfaceDeclaration { // parseExpected(SyntaxKind::InterfaceKeyword); // auto name = parseIdentifier(); diff --git a/src/tests/test_vm2.cpp b/src/tests/test_vm2.cpp index e4eb087..ba01e16 100644 --- a/src/tests/test_vm2.cpp +++ b/src/tests/test_vm2.cpp @@ -572,6 +572,20 @@ TEST_CASE("controlFlow1") { testBench(code, 2); } +TEST_CASE("class1") { + string code = R"( + class Date { + static now(): number { + return 0; + } + } + const now: number = Date.now(); + const now2: string = Date.now(); +)"; + test(code, 1); + //testBench(code, 0); +} + TEST_CASE("vm2Cartesian") { { vm2::CartesianProduct cartesian;