diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4afd645..82656c9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,7 +9,7 @@ project(typescript) add_subdirectory(tests) add_library(typescript utf.h utf.cpp core.h core.cpp utilities.h utilities.cpp node_test.h node_test.cpp - syntax_cursor.h syntax_cursor.cpp parser2.h parser2.cpp types.h types.cpp path.h path.cpp + parser2.h parser2.cpp types.h types.cpp path.h path.cpp factory.h factory.cpp parenthesizer.h parenthesizer.cpp scanner.h scanner.cpp checker/instructions.h checker/compiler.h checker/types.h checker/utils.h checker/checks.h checker/debug.h checker/vm2.cpp) # ${CMAKE_CURRENT_SOURCE_DIR}/../libs/tracy/TracyClient.cpp diff --git a/src/checker/check2.h b/src/checker/check2.h index 84e447e..ade5930 100644 --- a/src/checker/check2.h +++ b/src/checker/check2.h @@ -14,11 +14,110 @@ namespace ts::vm2 { inline unsigned int depth; } + inline Type *findMember(TypeRef *start, uint64_t hash) { + auto current = start; + while (current) { + if (current->type->hash == hash) return current->type; + current = current->next; + } + return nullptr; + } + /** * `left extends right ? true : false` */ bool extends(Type *left, Type *right) { switch (right->kind) { + case TypeKind::TupleMember: { + if (left->kind != TypeKind::TupleMember) return false; + //todo: handle optional + if (!extends((Type *)left->type, (Type *)right->type)) return false; + + return true; + } + case TypeKind::Tuple: { + switch (left->kind) { + case TypeKind::Tuple: { + //todo: comparing tuple is much more complex than that + + auto rightCurrent = (TypeRef *) right->type; + auto leftCurrent = (TypeRef *) left->type; + while (rightCurrent) { + if (!extends(leftCurrent->type, rightCurrent->type)) return false; + + rightCurrent = rightCurrent->next; + leftCurrent = leftCurrent->next; + if (rightCurrent && !leftCurrent) return false; + if (!rightCurrent && leftCurrent) return false; + } + return true; + } + case TypeKind::Array: { + auto elementType = (Type *) left->type; + if (elementType->kind == TypeKind::Any) return true; + + auto current = (TypeRef *) right->type; + while (current) { + if (!extends(elementType, current->type)) return false; + current = current->next; + } + return true; + } + } + + return false; + } + case TypeKind::Array: { + switch (left->kind) { + case TypeKind::Array: { + return extends((Type *) left->type, (Type *) right->type); + } + case TypeKind::Tuple: { + auto elementType = (Type *) right->type; + if (elementType->kind == TypeKind::Any) return true; + + auto current = (TypeRef *) left->type; + while (current) { + //current->type is TupleMember + if (!extends((Type *) current->type->type, elementType)) return false; + current = current->next; + } + return true; + } + } + return false; + } + case TypeKind::PropertySignature: { + switch (left->kind) { + case TypeKind::PropertySignature: { + return true; + } + } + return false; + } + case TypeKind::ObjectLiteral: { + switch (left->kind) { + case TypeKind::ObjectLiteral: { + auto rightCurrent = (TypeRef *) right->type; + auto leftStart = (TypeRef *) left->type; + + while (rightCurrent) { + switch (rightCurrent->type->kind) { +// case TypeKind::PropertySignature: + case TypeKind::PropertySignature: { + auto found = findMember(leftStart, rightCurrent->type->hash); + if (!found) return false; + if (!extends((Type *) found->type, (Type *) rightCurrent->type->type)) return false; + } + } + rightCurrent = rightCurrent->next; + } + + return true; + } + } + return false; + } case TypeKind::Union: { auto current = (TypeRef *) right->type; while (current) { diff --git a/src/checker/checks.h b/src/checker/checks.h index 970097a..8a8f4ca 100644 --- a/src/checker/checks.h +++ b/src/checker/checks.h @@ -129,7 +129,7 @@ namespace ts::vm { right = to(right)->type; } profiler.compare(left, right); - return true; +// return true; switch (right->kind) { case TypeKind::ObjectLiteral: { diff --git a/src/checker/compiler.h b/src/checker/compiler.h index 2b6feee..26ab9b0 100644 --- a/src/checker/compiler.h +++ b/src/checker/compiler.h @@ -598,15 +598,16 @@ namespace ts::checker { case types::SyntaxKind::TypeParameter: { const auto n = to(node); auto &symbol = program.pushSymbol(n->name->escapedText, SymbolType::TypeVariable, n); - program.pushOp(instructions::TypeArgument, n->name); if (n->defaultType) { program.pushSubroutineNameLess(n->defaultType); handle(n->defaultType, program); auto routine = program.popSubroutine(); - program.pushOp(instructions::TypeArgumentDefault); + program.pushOp(instructions::TypeArgumentDefault, n->name); program.pushAddress(routine->index); + } else { + program.pushOp(instructions::TypeArgument, n->name); } - //todo constraints + default + //todo constraints break; } case types::SyntaxKind::FunctionDeclaration: { diff --git a/src/checker/instructions.h b/src/checker/instructions.h index 40fe562..152ed9e 100644 --- a/src/checker/instructions.h +++ b/src/checker/instructions.h @@ -88,7 +88,8 @@ namespace ts::instructions { * ``` */ TypeArgument, - TypeArgumentDefault, //expects an entry on the stack + TypeArgumentDefault, //one parameter with the address of the subroutine of the default value + TypeArgumentConstraint, //expects an entry on the stack diff --git a/src/checker/types2.h b/src/checker/types2.h index e40692a..8f584ca 100644 --- a/src/checker/types2.h +++ b/src/checker/types2.h @@ -15,6 +15,8 @@ namespace ts::vm2 { Unknown, Never, Any, + Null, + Undefined, String, Number, Boolean, @@ -23,20 +25,21 @@ namespace ts::vm2 { ObjectLiteral, Union, Array, + Rest, Tuple, TupleMember, TemplateLiteral, }; enum TypeFlag: unsigned int { - Readonly = 1<<0, - Optional = 1<<1, - StringLiteral = 1<<2, - NumberLiteral = 1<<3, - BooleanLiteral = 1<<4, - True = 1<<5, - False = 1<<6, - UnprovidedArgument = 1<<7, + Readonly = 1 << 0, + Optional = 1 << 1, + StringLiteral = 1 << 2, + NumberLiteral = 1 << 3, + BooleanLiteral = 1 << 4, + True = 1 << 5, + False = 1 << 6, + UnprovidedArgument = 1 << 7, }; struct Type; @@ -74,7 +77,7 @@ namespace ts::vm2 { // if (extended) { // extended->push_back(type); // } else { - if (p>defaultTypesSize) { + if (p > defaultTypesSize) { // createExtended(300); // extended = new std::vector(32); // extended->insert(extended->begin(), list.begin(), list.end()); @@ -106,7 +109,7 @@ namespace ts::vm2 { struct TypeRef { Type *type; - TypeRef *next; + TypeRef *next = nullptr; }; struct Type { @@ -117,12 +120,85 @@ namespace ts::vm2 { unsigned int flag; unsigned int users; uint64_t hash; - void *type; //either Type* or TypeRef* depending on kind + void *type; //either Type* or TypeRef* or string* depending on kind - void setLiteral(TypeFlag flag, std::string_view value) { - text = value; + ~Type() { + if (kind == TypeKind::Literal && type) delete (string *) type; + }; + + void fromLiteral(Type *literal) { + flag = literal->flag; + hash = literal->hash; + if (literal->type) { + //dynamic value, so copy it + setDynamicText(literal->text); + } else { + //static value, safe reuse of string_view's reference + text = literal->text; + } + } + + /** + * Returns true of there is only one child + */ + bool singleChild() { + return type && ((TypeRef *) type)->next == nullptr; + } + /** + * Returns true of there is only one child + */ + Type *child() { + return type ? ((TypeRef *) type)->type : nullptr; + } + + void appendLiteral(Type *literal) { + appendText(literal->text); + } + + void appendText(string_view value) { + if (!type) { + setDynamicText(value); + } else { + ((string *) type)->append(value); + text = *(string *) type; + hash = hash::runtime_hash(text); + } + } + + void setDynamicText(string_view value) { + if (!type) { + type = new string(value); + } else { + ((string *) type)->clear(); + ((string *) type)->append(value); + } + text = *(string *) type; + hash = hash::runtime_hash(text); + } + + void setDynamicLiteral(TypeFlag flag, string_view value) { + setDynamicText(value); + setLiteral(flag, *(string *) type); + } + + Type *setFlag(TypeFlag flag) { this->flag |= flag; - hash = hash::runtime_hash(value); + return this; + } + + Type *setLiteral(TypeFlag flag, string_view value) { + this->flag |= flag; + text = value; + hash = hash::runtime_hash(text); + return this; + } + + void appendChild(TypeRef *ref) { + if (!type) { + type = ref; + } else { + ((TypeRef *) type)->next = ref; + } } void readStorage(const string_view &bin, uint32_t offset) { @@ -161,13 +237,18 @@ namespace ts::vm2 { } case TypeKind::PropertySignature: { r += string(type->text) + ": "; - stringifyType((Type *)type->type, r); + stringifyType((Type *) type->type, r); break; } case TypeKind::ObjectLiteral: { r += "{"; - auto current = (TypeRef *)type->type; + unsigned int i = 0; + auto current = (TypeRef *) type->type; while (current) { + if (i++ > 20) { + r += "..."; + break; + } stringifyType(current->type, r); current = current->next; r + "; "; @@ -181,13 +262,28 @@ namespace ts::vm2 { if (type->flag & TypeFlag::Optional) r += "?"; r += ": "; } - stringifyType((Type *)type->type, r); + if (!type->type) { + r += "unknown"; + } else { + stringifyType((Type *) type->type, r); + } + break; + } + case TypeKind::Array: { + r += "<"; + stringifyType((Type *) type->type, r); + r += ">"; break; } case TypeKind::Tuple: { r += "["; - auto current = (TypeRef *)type->type; + auto current = (TypeRef *) type->type; + unsigned int i = 0; while (current) { + if (i++ > 20) { + r += "..."; + break; + } stringifyType(current->type, r); current = current->next; if (current) r += ", "; @@ -196,17 +292,40 @@ namespace ts::vm2 { break; } case TypeKind::Union: { - auto current = (TypeRef *)type->type; + auto current = (TypeRef *) type->type; + unsigned int i = 0; while (current) { + if (i++ > 20) { + r += "..."; + break; + } stringifyType(current->type, r); current = current->next; if (current) r += " | "; } break; } + case TypeKind::TemplateLiteral: { + r += "`"; + auto current = (TypeRef *) type->type; + while (current) { + if (current->type->kind != TypeKind::Literal) r += "${"; + if (current->type->flag & TypeFlag::StringLiteral) { + r += current->type->text; + } else { + stringifyType(current->type, r); + } + if (current->type->kind != TypeKind::Literal) r += "}"; + current = current->next; + } + r += "`"; + break; + } case TypeKind::Literal: { if (type->flag & TypeFlag::StringLiteral) { - r += string("\"").append(type->text).append("\""); + r += "\""; + r += type->text; + r += "\""; } else if (type->flag & TypeFlag::NumberLiteral) { r += type->text; } else if (type->flag & TypeFlag::True) { @@ -286,7 +405,7 @@ namespace ts::vm2 { } template<> -struct fmt::formatter: formatter { +struct fmt::formatter: formatter { template auto format(ts::vm2::TypeKind p, FormatContext &ctx) { return formatter::format(magic_enum::enum_name(p), ctx); diff --git a/src/checker/utils.h b/src/checker/utils.h index d9d569c..8d64df5 100644 --- a/src/checker/utils.h +++ b/src/checker/utils.h @@ -8,11 +8,11 @@ namespace ts::vm { using std::vector; using std::string_view; - inline uint32_t readUint64(const vector &bin, unsigned int offset) { + inline uint64_t readUint64(const vector &bin, unsigned int offset) { return *(uint64_t *) (bin.data() + offset); } - inline uint32_t readUint64(const string_view &bin, unsigned int offset) { + inline uint64_t readUint64(const string_view &bin, unsigned int offset) { return *(uint64_t *) (bin.data() + offset); } diff --git a/src/checker/vm2.cpp b/src/checker/vm2.cpp index 05a56fc..9c5dd7b 100644 --- a/src/checker/vm2.cpp +++ b/src/checker/vm2.cpp @@ -1,6 +1,7 @@ #include "./vm2.h" #include "../hash.h" #include "./check2.h" +#include "./vm2_utils.h" #include namespace ts::vm2 { @@ -19,23 +20,23 @@ namespace ts::vm2 { } // TypeRef is an owning reference - inline TypeRef *useAsRef(Type *type) { + TypeRef *useAsRef(Type *type) { type->users++; auto t = poolRef.newElement(); t->type = type; return t; } - inline Type *allocate(TypeKind kind) { + Type *allocate(TypeKind kind) { auto type = pool.newElement(); type->kind = kind; type->users = 0; -// debug("allocate {}", stringify(type)); + debug("allocate {}", stringify(type)); return type; } void gc(TypeRef *typeRef) { - if (gcQueueRefIdx>=maxGcSize) { + if (gcQueueRefIdx >= maxGcSize) { //garbage collect now gcRefFlush(); } @@ -43,16 +44,16 @@ namespace ts::vm2 { } inline void gcWithoutChildren(Type *type) { - if (gcQueueIdx>=maxGcSize) { + if (gcQueueIdx >= maxGcSize) { //garbage collect now gcFlush(); } -// debug("gc ({}) {}", type->users, stringify(type)); - if (type->users > 0) return; + debug("gc ({}) {}", type->users, stringify(type)); gcQueue[gcQueueIdx++] = type; } void gc(Type *type) { + if (type->users > 0) return; gcWithoutChildren(type); switch (type->kind) { @@ -85,7 +86,7 @@ namespace ts::vm2 { } void gcRefFlush() { - for (unsigned int i = 0; iusers) continue; pool.deleteElement(type); @@ -124,12 +125,15 @@ namespace ts::vm2 { } void gcStack() { - for (unsigned int i = 0; i &module) { for (auto &&subroutine: module->subroutines) { if (subroutine.result) drop(subroutine.result); @@ -230,6 +234,15 @@ namespace ts::vm2 { return type->flag & TypeFlag::True; } + unsigned int refLength(TypeRef *current) { + unsigned int i = 0; + while (current) { + i++; + current = current->next; + } + return i; + } + /** * Query a container type and return the result. * @@ -240,16 +253,16 @@ namespace ts::vm2 { * e.g. [string, number][0] => string * e.g. [string, number][number] => string | number */ + inline uint64_t lengthHash = hash::const_hash("length"); + inline Type *indexAccess(Type *container, Type *index) { 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 }; } else if (container->kind == TypeKind::Tuple) { - if (index->hash == hash::const_hash("length")) { + if (index->hash == lengthHash) { auto t = allocate(TypeKind::Literal); -// t->setLiteral(TypeFlag::NumberLiteral, std::to_string(container->types.size())); -// auto t = make_shared("", TypeLiteralType::Number); -// t->append(to(container)->types.size()); + t->setDynamicLiteral(TypeFlag::NumberLiteral, std::to_string(refLength((TypeRef *) container->type))); return t; } @@ -340,82 +353,82 @@ namespace ts::vm2 { } void handleTemplateLiteral() { -// auto types = popFrame(); -// -// //short path for `{'asd'}` -// if (types.size() == 1 && types[0]->kind == TypeKind::Literal) { -// //we can not just change the TypeLiteral->type, we have to create a new one -//// to(types[0])->type = TypeLiteralType::String; -// auto t = types[0]; -// auto res = make_shared(t->literal, TypeLiteralType::String); -// if (t->dynamicString) { -// res->dynamicString = new string(*t->dynamicString); -// } -// push(res); -// return; -// } -// -// auto result = make_shared(); -// CartesianProduct cartesian; -// for (auto &&type: types) { -// cartesian.add(type); -// } -// auto product = cartesian.calculate(); -// -// outer: -// for (auto &&combination: product) { -// auto templateType = make_shared(); -// bool hasPlaceholder = false; -//// let lastLiteral: { kind: TypeKind::literal, literal: string, parent?: Type } | undefined = undefined; -// sharedOpt lastLiteral; -// -// //merge a combination of types, e.g. [string, 'abc', '3'] as template literal => `${string}abc3`. -// for (auto &&item: combination) { -// if (item->kind == TypeKind::Never) { -// //template literals that contain a never like `prefix.${never}` are completely ignored -// goto outer; -// } -// -// if (item->kind == TypeKind::Literal) { -// if (lastLiteral) { -// lastLiteral->append(to(item)->text()); -// } else { -// lastLiteral = make_shared("", TypeLiteralType::String); -// lastLiteral->append(to(item)->text()); -// templateType->types.push_back(lastLiteral); -// } -// } else { -// hasPlaceholder = true; -// lastLiteral = nullptr; -//// item.parent = template; -// templateType->types.push_back(item); -// } -// } -// -// if (hasPlaceholder) { -// if (templateType->types.size() == 1 && templateType->types[0]->kind == TypeKind::String) { -//// templateType->types[0].parent = result; -// result->types.push_back(templateType->types[0]); -// } else { -//// templateType->parent = result; -// result->types.push_back(templateType); -// } -// } else if (lastLiteral) { -//// lastLiteral.parent = result; -// result->types.push_back(lastLiteral); -// } -// } -// + auto types = popFrame(); + + //short path for `{'asd'}` + if (types.size() == 1 && types[0]->kind == TypeKind::Literal) { + if (types[0]->users == 0 && types[0]->flag & TypeFlag::StringLiteral) { + //reuse it + push(types[0]); + } else { + //create new one + auto res = allocate(TypeKind::Literal); + res->fromLiteral(types[0]); + res->flag = TypeFlag::StringLiteral; //convert number to string literal if necessary + push(res); + } + return; + } + + CartesianProduct cartesian; + for (auto &&type: types) { + cartesian.add(type); + } + auto product = cartesian.calculate(); + + auto result = allocate(TypeKind::Union); + for (auto &&combination: product) { + auto templateType = allocate(TypeKind::TemplateLiteral); + bool hasPlaceholder = false; +// let lastLiteral: { kind: TypeKind::literal, literal: string, parent?: Type } | undefined = undefined; + Type *lastLiteral = nullptr; + + //merge a combination of types, e.g. [string, 'abc', '3'] as template literal => `${string}abc3`. + for (auto &&item: combination) { + if (item->kind == TypeKind::Never) { + //template literals that contain a never like `prefix.${never}` are completely ignored + goto next; + } + + if (item->kind == TypeKind::Literal) { + if (lastLiteral) { + lastLiteral->appendLiteral(item); + } else { + lastLiteral = allocate(TypeKind::Literal); + lastLiteral->setLiteral(TypeFlag::StringLiteral, item->text); + templateType->appendChild(useAsRef(lastLiteral)); + } + } else { + hasPlaceholder = true; + lastLiteral = nullptr; + templateType->appendChild(useAsRef(item)); + } + } + + if (hasPlaceholder) { + // `${string}` -> string + if (templateType->singleChild() && templateType->child()->kind == TypeKind::String) { + result->appendChild(useAsRef(templateType->child())); + gc(templateType); + } else { + result->appendChild(useAsRef(templateType)); + } + } else if (lastLiteral) { + result->appendChild(useAsRef(lastLiteral)); + } + next: void; + } + // auto t = vm::unboxUnion(result); -//// if (t.kind == TypeKind::union) for (const member of t.types) member.parent = t; -//// debug("handleTemplateLiteral: {}", stringify(t)); -// push(t); +// if (t.kind == TypeKind::union) for (const member of t.types) member.parent = t; +// debug("handleTemplateLiteral: {}", stringify(t)); + push(result); } inline void mapFrameToChildren(Type *container) { auto i = frame->initialSp + frame->variables; auto current = (TypeRef *) (container->type = useAsRef(stack[i++])); - for (; inext = useAsRef(stack[i]); current = current->next; } @@ -483,7 +496,7 @@ namespace ts::vm2 { //the current frame could not only have the return value, but variables and other stuff, //which we don't want. So if size is bigger than 1, we move last stack entry to first // | [T] [T] [R] | - if (frame->size()>1) { + if (frame->size() > 1) { stack[frame->initialSp] = stack[sp - 1]; } sp = frame->initialSp + 1; @@ -562,7 +575,7 @@ namespace ts::vm2 { push(types[0]); } else { auto result = allocate(TypeKind::Union); - TypeRef *current = (TypeRef *)(result->type = useAsRef(types[0])); + TypeRef *current = (TypeRef *) (result->type = useAsRef(types[0])); for_each(++types.begin(), types.end(), [¤t](auto v) { current->next = useAsRef(v); current = current->next; @@ -592,18 +605,14 @@ namespace ts::vm2 { const auto varIndex = activeSubroutine->parseUint16(); if (frameOffset == 0) { push(stack[frame->initialSp + varIndex]); - } else if (frameOffset == 1) { - push(stack[frames.at(frames.i - 2)->initialSp + varIndex]); - } else if (frameOffset == 2) { - push(stack[frames.at(frames.i - 2)->initialSp + varIndex]); } else { - throw std::runtime_error("frame offset not implement"); + push(stack[frames.at(frames.i - frameOffset)->initialSp + varIndex]); } // debug("load var {}/{}", frameOffset, varIndex); break; } case OP::TypeArgument: { - if (frame->size()<=activeSubroutine->typeArguments) { + if (frame->size() <= activeSubroutine->typeArguments) { auto unknown = allocate(TypeKind::Unknown); unknown->flag |= TypeFlag::UnprovidedArgument; push(unknown); @@ -613,11 +622,8 @@ namespace ts::vm2 { break; } case OP::TypeArgumentDefault: { - auto t = stack[frame->initialSp + activeSubroutine->typeArguments - 1]; - //t is always set because TypeArgument ensures that - if (t->flag & TypeFlag::UnprovidedArgument) { - gc(stack[sp]); - sp--; //remove unknown type from stack + if (frame->size() <= activeSubroutine->typeArguments) { + //load default value const auto address = activeSubroutine->parseUint32(); if (call(address, 0)) { //the result is pushed on the stack goto start; @@ -625,7 +631,23 @@ namespace ts::vm2 { } else { activeSubroutine->ip += 4; //jump over address } + activeSubroutine->typeArguments++; + frame->variables++; break; + +// auto t = stack[frame->initialSp + activeSubroutine->typeArguments - 1]; +// //t is always set because TypeArgument ensures that +// if (t->flag & TypeFlag::UnprovidedArgument) { +// //gc(stack[sp - 1]); +// sp--; //remove unknown type from stack +// const auto address = activeSubroutine->parseUint32(); +// if (call(address, 0)) { //the result is pushed on the stack +// goto start; +// } +// } else { +// activeSubroutine->ip += 4; //jump over address +// } +// break; } case OP::IndexAccess: { auto right = pop(); @@ -718,13 +740,15 @@ namespace ts::vm2 { } item->type = useAsRef(types[0]); - auto current = (TypeRef *) item->type; - for_each(++types.begin(), types.end(), [¤t](auto v) { - current->next = useAsRef(v); - current = current->next; - }); - current->next = nullptr; - stack[sp++] = item; + 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::Union: { @@ -750,33 +774,46 @@ namespace ts::vm2 { } else { item->type = useAsRef(type); } - auto current = (TypeRef *) item->type; - - //set current to the end of the list - while (current->next) current = current->next; - - for_each(++types.begin(), types.end(), [¤t](auto v) { - if (v->kind == TypeKind::Union) { - //if type has no owner, we can steal its children - if (v->users == 0) { - current->next = (TypeRef *) v->type; - v->type = nullptr; - //since we stole its children, we want it to GC but without its children. their 'users' count belongs now to us. - gcWithoutChildren(v); - //set current to the end of the list - while (current->next) current = current->next; + if (types.size() > 1) { + auto current = (TypeRef *) item->type; + //set current to the end of the list + while (current->next) current = current->next; + + for_each(++types.begin(), types.end(), [¤t](auto v) { + if (v->kind == TypeKind::Union) { + //if type has no owner, we can steal its children + if (v->users == 0) { + current->next = (TypeRef *) v->type; + v->type = nullptr; + //since we stole its children, we want it to GC but without its children. their 'users' count belongs now to us. + gcWithoutChildren(v); + //set current to the end of the list + while (current->next) current = current->next; + } else { + throw std::runtime_error("Can not merge used union"); + } } else { - throw std::runtime_error("Can not merge used union"); + current->next = useAsRef(v); + current = current->next; } - } else { - current->next = useAsRef(v); - current = current->next; - } - }); - current->next = nullptr; + }); + current->next = nullptr; + } push(item); break; } + case OP::Array: { + auto item = allocate(TypeKind::Array); + item->type = pop(); + stack[sp++] = item; + break; + } + case OP::Rest: { + auto item = allocate(TypeKind::Rest); + item->type = pop(); + stack[sp++] = item; + break; + } case OP::TupleMember: { auto item = allocate(TypeKind::TupleMember); item->type = pop(); @@ -784,21 +821,93 @@ namespace ts::vm2 { break; } case OP::Tuple: { - auto item = allocate(TypeKind::Tuple); auto types = popFrame(); if (types.empty()) { + auto item = allocate(TypeKind::Tuple); item->type = nullptr; push(item); break; } - item->type = useAsRef(types[0]); - auto current = (TypeRef *) item->type; - for_each(++types.begin(), types.end(), [¤t](auto v) { - current->next = useAsRef(v); - current = current->next; - }); - current->next = nullptr; + Type *item; + auto first = types[0]; + auto firstType = (Type *) first->type; + if (firstType->kind == TypeKind::Rest) { + //[...T, x] + Type *T = (Type *) firstType->type; + //if type has no owner, we can just use it as the new type + if (T->users == 0) { + item = T; + } else { + item = allocate(TypeKind::Tuple); + if (T->kind == TypeKind::Array) { + //type T = number[]; + //type New = [...T, x]; => [...number[], x]; + throw std::runtime_error("assigning array rest in tuple not supported"); + } else if (T->kind == TypeKind::Tuple) { + //type T = [y, z]; + //type New = [...T, x]; => [y, z, x]; + TypeRef *newCurrent = nullptr; + auto oldCurrent = (TypeRef *) T->type; + while (oldCurrent) { + //we reuse the tuple member and increase its `users`. + auto tupleMember = useAsRef(oldCurrent->type); + + if (newCurrent) { + newCurrent->next = tupleMember; + } else { + newCurrent = (TypeRef *) (item->type = tupleMember); + } + + oldCurrent = oldCurrent->next; + } + } else { + debug("Error: [...T] where T is not an array/tuple."); + } + } + } else { + item = allocate(TypeKind::Tuple); + item->type = useAsRef(types[0]); + } + if (types.size() > 1) { + //note item->type could still be null, when T is empty for [...T, 0] + auto current = (TypeRef *) item->type; + //set current to the end of the list + while (current && current->next) current = current->next; + + for_each(++types.begin(), types.end(), [¤t, &item](auto tupleMember) { + auto type = (Type *) tupleMember->type; + if (type->kind == TypeKind::Rest) { + //[x, ...T] + Type *T = (Type *) type->type; + if (T->kind == TypeKind::Array) { + //type T = number[]; + //type New = [...T, x]; => [...number[], x]; + throw std::runtime_error("assigning array rest in tuple not supported"); + } else if (T->kind == TypeKind::Tuple) { + //type T = [y, z]; + //type New = [...T, x]; => [y, z, x]; + auto oldCurrent = (TypeRef *) T->type; + while (oldCurrent) { + //we reuse the tuple member and increase its `users`. + auto tupleMember = useAsRef(oldCurrent->type); + current->next = tupleMember; + + oldCurrent = oldCurrent->next; + } + } else { + debug("Error: [...T] where T is not an array/tuple."); + } + } else { + if (current) { + current->next = useAsRef(tupleMember); + } else { + current = (TypeRef *) (item->type = useAsRef(tupleMember)); + } + } + current = current->next; + }); + } push(item); break; } diff --git a/src/checker/vm2.h b/src/checker/vm2.h index 9e32347..1d784d7 100644 --- a/src/checker/vm2.h +++ b/src/checker/vm2.h @@ -125,6 +125,7 @@ namespace ts::vm2 { } T *push() { + if (i >= Size) throw std::runtime_error("Stack overflow"); return &values[++i]; } @@ -159,6 +160,8 @@ namespace ts::vm2 { // Garbage collect whatever is left on the stack void gcStack(); void gcStackAndFlush(); + TypeRef *useAsRef(Type *type); + Type *allocate(TypeKind kind); std::span popFrame(); @@ -173,4 +176,97 @@ namespace ts::vm2 { } void call(shared &module, unsigned int index = 0, unsigned int arguments = 0); + + struct CStack { + vector iterator; + unsigned int i; + unsigned int round; + }; + + class CartesianProduct { + vector stack; + public: + + Type *current(CStack &s) { + return s.iterator[s.i]; + } + + bool next(CStack &s) { + return (++s.i == s.iterator.size()) ? (s.i = 0, false) : true; + } + + vector toGroup(Type *type) { + if (type->kind == TypeKind::Boolean) { + return {allocate(TypeKind::Literal)->setFlag(TypeFlag::True), allocate(TypeKind::Literal)->setFlag(TypeFlag::False)}; + } else if (type->kind == TypeKind::Null) { + return {allocate(TypeKind::Literal)->setLiteral(TypeFlag::StringLiteral, "null")}; + } else if (type->kind == TypeKind::Undefined) { + return {allocate(TypeKind::Literal)->setLiteral(TypeFlag::StringLiteral, "undefined")}; + // } else if (type->kind == TypeKind::templateLiteral) { + // // //todo: this is wrong + // // return type.types; + // const result: Type[] = []; + // for (const s of type.types) { + // const g = this.toGroup(s); + // result.push(...g); + // } + // + // return result; + } else if (type->kind == TypeKind::Union) { + vector result; + auto current = (TypeRef *) type->type; + while (current) { + auto g = toGroup(current->type); + for (auto &&s: g) result.push_back(s); + current = current->next; + } + + return result; + } else { + return {type}; + } + } + + void add(Type *item) { + stack.push_back({.iterator=toGroup(item), .i= 0, .round= 0}); + } + + vector> calculate() { + vector> result; + + outer: + while (true) { + vector row; + for (auto &&s: stack) { + auto item = current(s); + if (item->kind == TypeKind::TemplateLiteral) { + auto current = (TypeRef *) item->type; + while (current) { + row.push_back(current->type); + current = current->next; + } + } else { + row.push_back(item); + } + } + result.push_back(row); + + for (unsigned int i = stack.size() - 1; i >= 0; i--) { + auto active = next(stack[i]); + //when that i stack is active, continue in main loop + if (active) goto outer; + + //i stack was rewinded. If it's the first, it means we are done + if (i == 0) { + goto done; + } + } + break; + } + + done: + return result; + } + }; + } \ No newline at end of file diff --git a/src/checker/vm2_utils.h b/src/checker/vm2_utils.h new file mode 100644 index 0000000..7a9515a --- /dev/null +++ b/src/checker/vm2_utils.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "./types2.h" + +namespace ts::vm2 { +// template +// _GLIBCXX20_CONSTEXPR +// _Function +// for_each_child(void *start, _Function __f) +// { +// auto current = (TypeRef *)start; +// while (current) { +// __f(current->type); +// current = current->next; +// } +// } + +} \ No newline at end of file diff --git a/src/core.h b/src/core.h index 3ee01de..c3d5f9c 100644 --- a/src/core.h +++ b/src/core.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -250,7 +251,7 @@ namespace ts { inline void bench(string title, int iterations, const function &callback) { auto took = benchRun(iterations, callback); - fmt::print("{} {} iterations took {:.12f}ms, {:.12f}ms per iteration\n", title, iterations, took.count(), took.count()/iterations); + fmt::print("{} {} iterations took {:.9f}ms, {:.9f}ms per iteration\n", title, iterations, took.count(), took.count()/iterations); } inline void bench(int iterations, const function &callback) { diff --git a/src/hash.h b/src/hash.h index 379cf0b..a20249e 100644 --- a/src/hash.h +++ b/src/hash.h @@ -91,9 +91,9 @@ namespace ts::hash { return xxh64::hash(input, constLength(input), 0); } - inline consteval uint64_t const_hash(const std::string &input) { - return const_hash(input.c_str()); - } +// inline uint64_t const_hash(const std::string &input) { +// return const_hash(input.c_str()); +// } inline uint64_t runtime_hash(const std::string &input) { return xxh64::hash(input.c_str(), input.size(), 0); diff --git a/src/scanner.cpp b/src/scanner.cpp index 0ba92e0..780a48a 100644 --- a/src/scanner.cpp +++ b/src/scanner.cpp @@ -550,7 +550,7 @@ namespace ts { } /* @internal */ - int ts::skipTrivia(string &text, int pos, optional stopAfterLineBreak, optional stopAtComments, optional inJSDoc) { + int skipTrivia(string &text, int pos, optional stopAfterLineBreak, optional stopAtComments, optional inJSDoc) { ZoneScoped; if (positionIsSynthesized(pos)) { return pos; @@ -944,7 +944,7 @@ namespace ts { tokenValue = result; auto type = checkBigIntSuffix(); // if value is an integer, check whether it is a bigint checkForIdentifierStartAfterNumericLiteral(start); - return {type, .value = tokenValue}; + return {.type = type, .value = tokenValue}; } } diff --git a/src/scanner.h b/src/scanner.h index ed18f9c..677a13f 100644 --- a/src/scanner.h +++ b/src/scanner.h @@ -17,6 +17,7 @@ namespace ts { SyntaxKind type; string value; }; + using ErrorCallback = function< void(const shared &message, diff --git a/src/tests/test_checker.cpp b/src/tests/test_checker.cpp index 60652d2..bc4fe99 100644 --- a/src/tests/test_checker.cpp +++ b/src/tests/test_checker.cpp @@ -8,44 +8,6 @@ using namespace ts; -void test(string code, unsigned int expectedErrors = 0) { - auto bin = compile(code); - auto module = make_shared(bin, "app.ts", code); - vm::VM vm; - vm.run(module); - vm.printErrors(); - EXPECT_EQ(expectedErrors, vm.getErrors()); -} - -void testBench(string code, unsigned int expectedErrors = 0) { - auto bin = compile(code); - auto module = make_shared(bin, "app.ts", code); - vm::VM vm; - vm.run(module); - vm.printErrors(); - EXPECT_EQ(expectedErrors, vm.getErrors()); - - auto iterations = 10; - - auto warmTime = benchRun(iterations, [&module] { - module->clear(); - vm::VM vm; - vm.run(module); - }); - - auto compileTime = benchRun(iterations, [&code] { - compile(code, false); - }); - - auto coldTime = benchRun(iterations, [&code] { - vm::VM vm; - auto module = make_shared(compile(code, false), "app.ts", code); - vm.run(module); - }); - - fmt::print("{} iterations: compile {}/op, cold {}/op, warm {}/op", iterations, compileTime.count() / iterations, coldTime.count() / iterations, warmTime.count() / iterations); -} - TEST(checker, program) { checker::Program program; diff --git a/src/tests/test_vm2.cpp b/src/tests/test_vm2.cpp index c31657b..891eb47 100644 --- a/src/tests/test_vm2.cpp +++ b/src/tests/test_vm2.cpp @@ -48,7 +48,6 @@ const v2: number = 123; } TEST(vm2, allocator) { - } TEST(vm2, vm2Union) { @@ -67,11 +66,7 @@ const v3: a = false; //only v1, v2, v3 plus for each 4 union (true | string | number) EXPECT_EQ(ts::vm2::pool.active, 3 * 4); - ts::bench("first", 1000, [&] { - ts::vm2::clear(module); -// module->clear(); - run(module); - }); + testBench(code, 1); } TEST(vm2, vm2Base2) { @@ -90,10 +85,7 @@ const v3: a = 'nope'; ts::vm2::gcStackAndFlush(); EXPECT_EQ(ts::vm2::pool.active, 5); - ts::bench("first", 1000, [&] { - module->clear(); - run(module); - }); + testBench(code, 1); } TEST(vm2, vm2Base22) { @@ -158,7 +150,7 @@ const var1: a = false; TEST(vm2, gcUnion) { ts::checker::Program program; program.pushOp(OP::Frame); - for (auto i = 0; i<10; i++) { + for (auto i = 0; i < 10; i++) { program.pushOp(OP::StringLiteral); program.pushStorage("a" + to_string(i)); } @@ -175,7 +167,7 @@ TEST(vm2, gcUnion) { TEST(vm2, gcTuple) { ts::checker::Program program; program.pushOp(OP::Frame); - for (auto i = 0; i<10; i++) { + for (auto i = 0; i < 10; i++) { program.pushOp(OP::String); program.pushOp(OP::TupleMember); } @@ -192,7 +184,7 @@ TEST(vm2, gcTuple) { TEST(vm2, gcObject) { ts::checker::Program program; program.pushOp(OP::Frame); - for (auto i = 0; i<10; i++) { + for (auto i = 0; i < 10; i++) { program.pushOp(OP::StringLiteral); program.pushStorage("a"); program.pushOp(OP::StringLiteral); @@ -209,53 +201,205 @@ TEST(vm2, gcObject) { EXPECT_EQ(ts::vm2::pool.active, 0); } -TEST(vm2, vm2Base3) { +TEST(vm2, vm2TemplateLiteral1) { string code = R"( -type StringToNum = `${A['length']}` extends T ? A['length'] : StringToNum; -const var1: StringToNum<'999'> = 1002; +type L = `${string}`; +const var1: L = 'abc'; +const var2: L = 22; )"; - auto module = std::make_shared(ts::compile(code), "app.ts", code); - run(module); - module->printErrors(); + ts::testBench(code, 1); +} - EXPECT_EQ(module->errors.size(), 1); +TEST(vm2, vm2TemplateLiteral2) { + string code = R"( +type L = `${34}`; +const var1: L = '34'; +const var2: L = 34; +)"; + ts::testBench(code, 1); +} - ts::bench("first", 1000, [&] { - module->clear(); - run(module); - }); +TEST(vm2, vm2TemplateLiteral3) { + string code = R"( +type L = `a${string}`; +const var1: L = 'abc'; +const var2: L = 'bbc'; +)"; + ts::testBench(code, 1); +} + +TEST(vm2, vm2TemplateLiteralSize) { + string code = R"( +type A = [1]; +type L = `${A['length']}`; +const var1: L = "1"; +const var2: L = "10"; +)"; + ts::testBench(code, 1); +} + +TEST(vm2, vm2TupleMerge) { + string code = R"( +type A = [1, 2]; +type L = [...A, 3]; +const var1: L = [1, 2, 3]; +const var2: L = [1, 2]; // Error +const var3: A = [1, 2]; +)"; + test(code, 1); + debug("active {}", ts::vm2::pool.active); + + ts::testBench(code, 1); +} + +TEST(vm2, vm2Tuple2) { + string code = R"( +type A = [1, 2]; +const var1: A = [1, 2]; +)"; + test(code, 0); + ts::vm2::gcFlush(); + EXPECT_EQ(ts::vm2::pool.active, 1 + (2 * 2)); +} + +TEST(vm2, vm2Tuple3) { + string code = R"( +type T = [1]; +type A = [...T, 2]; +const var1: A = [1, 2]; +)"; + test(code, 0); + ts::vm2::gcFlush(); + EXPECT_EQ(ts::vm2::pool.active, (1 + 2) + (1 + (2 * 2))); +} + +TEST(vm2, vm2Fn1) { + string code = R"( +type F = T; +const var1: F = 'abc'; +)"; + test(code, 0); + EXPECT_EQ(ts::vm2::pool.active, 2); + ts::vm2::gcFlush(); + EXPECT_EQ(ts::vm2::pool.active, 1); +} + +TEST(vm2, vm2Fn2) { + string code = R"( +type F = T; +const var1: F = 'abc'; +)"; + //todo extends not added yet + test(code, 0); + EXPECT_EQ(ts::vm2::pool.active, 3); + ts::vm2::gcFlush(); + EXPECT_EQ(ts::vm2::pool.active, 1); +} + +TEST(vm2, vm2Fn3) { + string code = R"( +type F = T; +const var1: F = 'abc'; +)"; + test(code, 0); + EXPECT_EQ(ts::vm2::pool.active, 2); + ts::vm2::gcFlush(); + EXPECT_EQ(ts::vm2::pool.active, 1); +} + +TEST(vm2, vm2Cartesian) { + { + vm2::CartesianProduct cartesian; + //`${'a'}${'b'}` => StringLiteral|StringLiteral + cartesian.add(vm2::allocate(TypeKind::Literal)->setLiteral(TypeFlag::StringLiteral, "a")); + cartesian.add(vm2::allocate(TypeKind::Literal)->setLiteral(TypeFlag::StringLiteral, "b")); + auto product = cartesian.calculate(); + EXPECT_EQ(product.size(), 1); + auto first = product[0]; + EXPECT_EQ(first.size(), 2); + EXPECT_EQ(stringify(first[0]), "\"a\""); + EXPECT_EQ(stringify(first[1]), "\"b\""); + } + { + vm2::CartesianProduct cartesian; + //`${'a'}${'b'|'c'}` => ('a'|'b')|('a'|'c') + cartesian.add(vm2::allocate(TypeKind::Literal)->setLiteral(TypeFlag::StringLiteral, "a")); + auto unionType = allocate(TypeKind::Union); + unionType->appendChild(useAsRef(vm2::allocate(TypeKind::Literal)->setLiteral(TypeFlag::StringLiteral, "b"))); + unionType->appendChild(useAsRef(vm2::allocate(TypeKind::Literal)->setLiteral(TypeFlag::StringLiteral, "c"))); + cartesian.add(unionType); + auto product = cartesian.calculate(); + EXPECT_EQ(product.size(), 2); + auto first = product[0]; + EXPECT_EQ(first.size(), 2); + EXPECT_EQ(stringify(first[0]), "\"a\""); + EXPECT_EQ(stringify(first[1]), "\"b\""); + + auto second = product[1]; + EXPECT_EQ(second.size(), 2); + EXPECT_EQ(stringify(second[0]), "\"a\""); + EXPECT_EQ(stringify(second[1]), "\"c\""); + } +} + +TEST(vm2, vm2Complex1) { + //todo: this breaks when T is 4, also memory usage is way too big + string code = R"( +type StringToNum = `${A['length']}` extends T ? A['length'] : StringToNum; +const var1: StringToNum<'1', []> = 1; +//const var2: StringToNum<'999'> = 1002; +)"; + test(code, 0); + debug("active {}", ts::vm2::pool.active); + ts::vm2::gcFlush(); + debug("active gc {}", ts::vm2::pool.active); +// EXPECT_EQ(ts::vm2::pool.active, (1 + 2) + (1 + (2 * 2))); + +// ts::bench("first", 1000, [&] { +// module->clear(); +// run(module); +// }); } TEST(vm2, bigUnion) { ts::checker::Program program; - program.pushOp(OP::Frame); - for (auto i = 0; i<300; i++) { - program.pushOp(OP::StringLiteral); - program.pushStorage("a" + to_string(i)); - } - program.pushOp(OP::Union); program.pushOp(OP::Frame); - for (auto i = 0; i<300; i++) { + for (auto i = 0; i < 300; i++) { program.pushOp(OP::Frame); program.pushOp(OP::StringLiteral); - program.pushStorage("a"); + program.pushStorage((new string("foo"))->append(to_string(i))); program.pushOp(OP::StringLiteral); - program.pushStorage("foo1"); + program.pushStorage("a"); program.pushOp(OP::PropertySignature); program.pushOp(OP::ObjectLiteral); program.pushOp(OP::TupleMember); } program.pushOp(OP::Tuple); + + program.pushOp(OP::Frame); + program.pushOp(OP::Frame); + for (auto i = 0; i < 300; i++) { + program.pushOp(OP::StringLiteral); + program.pushStorage((new string("foo"))->append(to_string(i))); + } + program.pushOp(OP::Union); + program.pushOp(OP::StringLiteral); + program.pushStorage("a"); + program.pushOp(OP::PropertySignature); + program.pushOp(OP::ObjectLiteral); + program.pushOp(OP::Array); + + program.pushOp(OP::Assign); program.pushOp(OP::Halt); auto module = std::make_shared(program.build(), "app.ts", ""); run(module); + module->printErrors(); EXPECT_EQ(module->errors.size(), 0); ts::vm2::clear(module); - ts::vm2::gcStack(); - ts::vm2::gcFlush(); + ts::vm2::gcStackAndFlush(); EXPECT_EQ(ts::vm2::pool.active, 0); ts::bench("first", 1000, [&] { diff --git a/src/tests/utils.h b/src/tests/utils.h index fa8074c..3f135be 100644 --- a/src/tests/utils.h +++ b/src/tests/utils.h @@ -1,6 +1,7 @@ #include "../parser2.h" #include "../checker/compiler.h" #include "../checker/debug.h" +#include "../checker/vm2.h" namespace ts { std::string compile(std::string code, bool print = true) { @@ -12,4 +13,40 @@ namespace ts { if (print) checker::printBin(bin); return bin; } + + void test(string code, unsigned int expectedErrors = 0) { + auto bin = compile(code); + auto module = make_shared(bin, "app.ts", code); + vm2::run(module); + module->printErrors(); + EXPECT_EQ(expectedErrors, module->errors.size()); + } + + void testBench(string code, unsigned int expectedErrors = 0) { + auto bin = compile(code); + auto module = make_shared(bin, "app.ts", code); + vm2::run(module); + module->printErrors(); + EXPECT_EQ(expectedErrors, module->errors.size()); + if (expectedErrors != module->errors.size()) return; + + auto iterations = 1000; + + auto warmTime = benchRun(iterations, [&module] { + module->clear(); + vm2::run(module); + }); + + auto compileTime = benchRun(iterations, [&code] { + compile(code, false); + }); + + auto coldTime = benchRun(iterations, [&code] { + auto module = make_shared(compile(code, false), "app.ts", code); + vm2::run(module); + }); + + fmt::print("{} iterations (it): compile {:.9f}ms/it, cold {:.9f}ms/it, warm {:.9f}ms/it", iterations, compileTime.count() / iterations, coldTime.count() / iterations, warmTime.count() / iterations); + } + } \ No newline at end of file diff --git a/src/types.h b/src/types.h index b851fc4..9ec3979 100644 --- a/src/types.h +++ b/src/types.h @@ -134,13 +134,27 @@ namespace ts::types { bool reportsDeprecated = false; string source; optional> relatedInformation; - /* @internal */ CompilerOptions *skippedOn = nullptr; +// /* @internal */ CompilerOptions *skippedOn = nullptr; }; struct DiagnosticWithDetachedLocation: Diagnostic { string fileName; int start = 0; int length = 0; + + DiagnosticWithDetachedLocation() {} + + explicit DiagnosticWithDetachedLocation( + const string &fileName, const string_view &messageText, DiagnosticCategory category, int code, + bool reportsUnnecessary, int start, int length + ): fileName(fileName) { + this->category = category; + this->code = code; + this->messageText = messageText; + this->reportsUnnecessary = reportsUnnecessary; + this->start = start; + this->length = length; + } }; enum class ImportsNotUsedAsValues { @@ -359,24 +373,24 @@ namespace ts::types { enum TokenFlags { None = 0, /* @internal */ - PrecedingLineBreak = 1 << 0, + PrecedingLineBreak = 1<<0, /* @internal */ - PrecedingJSDocComment = 1 << 1, + PrecedingJSDocComment = 1<<1, /* @internal */ - Unterminated = 1 << 2, + Unterminated = 1<<2, /* @internal */ - ExtendedUnicodeEscape = 1 << 3, - Scientific = 1 << 4, // e.g. `10e2` - Octal = 1 << 5, // e.g. `0777` - HexSpecifier = 1 << 6, // e.g. `0x00000000` - BinarySpecifier = 1 << 7, // e.g. `0b0110010000000000` - OctalSpecifier = 1 << 8, // e.g. `0o777` + ExtendedUnicodeEscape = 1<<3, + Scientific = 1<<4, // e.g. `10e2` + Octal = 1<<5, // e.g. `0777` + HexSpecifier = 1<<6, // e.g. `0x00000000` + BinarySpecifier = 1<<7, // e.g. `0b0110010000000000` + OctalSpecifier = 1<<8, // e.g. `0o777` /* @internal */ - ContainsSeparator = 1 << 9, // e.g. `0b1100_0101` + ContainsSeparator = 1<<9, // e.g. `0b1100_0101` /* @internal */ - UnicodeEscape = 1 << 10, + UnicodeEscape = 1<<10, /* @internal */ - ContainsInvalidEscape = 1 << 11, // e.g. `\uhello` + ContainsInvalidEscape = 1<<11, // e.g. `\uhello` /* @internal */ BinaryOrOctalSpecifier = BinarySpecifier | OctalSpecifier, /* @internal */ @@ -829,27 +843,27 @@ namespace ts::types { enum class NodeFlags { None = 0, - Let = 1 << 0, // Variable declaration - Const = 1 << 1, // Variable declaration - NestedNamespace = 1 << 2, // Namespace declaration - Synthesized = 1 << 3, // BaseNode was synthesized during transformation - Namespace = 1 << 4, // Namespace declaration - OptionalChain = 1 << 5, // Chained MemberExpression rooted to a pseudo-OptionalExpression - ExportContext = 1 << 6, // Export context (initialized by binding) - ContainsThis = 1 << 7, // Interface contains references to "this" - HasImplicitReturn = 1 << 8, // If function implicitly returns on one of codepaths (initialized by binding) - HasExplicitReturn = 1 << 9, // If function has explicit reachable return on one of codepaths (initialized by binding) - GlobalAugmentation = 1 << 10, // Set if module declaration is an augmentation for the global scope - HasAsyncFunctions = 1 << 11, // If the file has async functions (initialized by binding) - DisallowInContext = 1 << 12, // If BaseNode was parsed in a context where 'in-expressions' are not allowed - YieldContext = 1 << 13, // If BaseNode was parsed in the 'yield' context created when parsing a generator - DecoratorContext = 1 << 14, // If BaseNode was parsed as part of a decorator - AwaitContext = 1 << 15, // If BaseNode was parsed in the 'await' context created when parsing an async function - DisallowConditionalTypesContext = 1 << 16, // If BaseNode was parsed in a context where conditional types are not allowed - ThisNodeHasError = 1 << 17, // If the parser encountered an error when parsing the code that created this node - JavaScriptFile = 1 << 18, // If BaseNode was parsed in a JavaScript - ThisNodeOrAnySubNodesHasError = 1 << 19, // If this BaseNode or any of its children had an error - HasAggregatedChildData = 1 << 20, // If we've computed data from children and cached it in this node + Let = 1<<0, // Variable declaration + Const = 1<<1, // Variable declaration + NestedNamespace = 1<<2, // Namespace declaration + Synthesized = 1<<3, // BaseNode was synthesized during transformation + Namespace = 1<<4, // Namespace declaration + OptionalChain = 1<<5, // Chained MemberExpression rooted to a pseudo-OptionalExpression + ExportContext = 1<<6, // Export context (initialized by binding) + ContainsThis = 1<<7, // Interface contains references to "this" + HasImplicitReturn = 1<<8, // If function implicitly returns on one of codepaths (initialized by binding) + HasExplicitReturn = 1<<9, // If function has explicit reachable return on one of codepaths (initialized by binding) + GlobalAugmentation = 1<<10, // Set if module declaration is an augmentation for the global scope + HasAsyncFunctions = 1<<11, // If the file has async functions (initialized by binding) + DisallowInContext = 1<<12, // If BaseNode was parsed in a context where 'in-expressions' are not allowed + YieldContext = 1<<13, // If BaseNode was parsed in the 'yield' context created when parsing a generator + DecoratorContext = 1<<14, // If BaseNode was parsed as part of a decorator + AwaitContext = 1<<15, // If BaseNode was parsed in the 'await' context created when parsing an async function + DisallowConditionalTypesContext = 1<<16, // If BaseNode was parsed in a context where conditional types are not allowed + ThisNodeHasError = 1<<17, // If the parser encountered an error when parsing the code that created this node + JavaScriptFile = 1<<18, // If BaseNode was parsed in a JavaScript + ThisNodeOrAnySubNodesHasError = 1<<19, // If this BaseNode or any of its children had an error + HasAggregatedChildData = 1<<20, // If we've computed data from children and cached it in this node // These flags will be set when the parser encounters a dynamic import expression or 'import.meta' to avoid // walking the tree if the flags are not set. However, these flags are just a approximation @@ -860,15 +874,15 @@ namespace ts::types { // removal, it is likely that users will add the import anyway. // The advantage of this approach is its simplicity. For the case of batch compilation, // we guarantee that users won't have to pay the price of walking the tree if a dynamic import isn't used. - /* @internal */ PossiblyContainsDynamicImport = 1 << 21, - /* @internal */ PossiblyContainsImportMeta = 1 << 22, + /* @internal */ PossiblyContainsDynamicImport = 1<<21, + /* @internal */ PossiblyContainsImportMeta = 1<<22, - JSDoc = 1 << 23, // If BaseNode was parsed inside jsdoc - /* @internal */ Ambient = 1 << 24, // If BaseNode was inside an ambient context -- a declaration file, or inside something with the `declare` modifier. - /* @internal */ InWithStatement = 1 << 25, // If any ancestor of BaseNode was the `statement` of a WithStatement (not the `expression`) - JsonFile = 1 << 26, // If BaseNode was parsed in a Json - /* @internal */ TypeCached = 1 << 27, // If a type was cached for BaseNode at any point - /* @internal */ Deprecated = 1 << 28, // If has '@deprecated' JSDoc tag + JSDoc = 1<<23, // If BaseNode was parsed inside jsdoc + /* @internal */ Ambient = 1<<24, // If BaseNode was inside an ambient context -- a declaration file, or inside something with the `declare` modifier. + /* @internal */ InWithStatement = 1<<25, // If any ancestor of BaseNode was the `statement` of a WithStatement (not the `expression`) + JsonFile = 1<<26, // If BaseNode was parsed in a Json + /* @internal */ TypeCached = 1<<27, // If a type was cached for BaseNode at any point + /* @internal */ Deprecated = 1<<28, // If has '@deprecated' JSDoc tag BlockScoped = Let | Const, @@ -889,61 +903,61 @@ namespace ts::types { enum class EmitFlags { None = 0, - SingleLine = 1 << 0, // The contents of this node should be emitted on a single line. - AdviseOnEmitNode = 1 << 1, // The printer should invoke the onEmitNode callback when printing this node. - NoSubstitution = 1 << 2, // Disables further substitution of an expression. - CapturesThis = 1 << 3, // The function captures a lexical `this` - NoLeadingSourceMap = 1 << 4, // Do not emit a leading source map location for this node. - NoTrailingSourceMap = 1 << 5, // Do not emit a trailing source map location for this node. + SingleLine = 1<<0, // The contents of this node should be emitted on a single line. + AdviseOnEmitNode = 1<<1, // The printer should invoke the onEmitNode callback when printing this node. + NoSubstitution = 1<<2, // Disables further substitution of an expression. + CapturesThis = 1<<3, // The function captures a lexical `this` + NoLeadingSourceMap = 1<<4, // Do not emit a leading source map location for this node. + NoTrailingSourceMap = 1<<5, // Do not emit a trailing source map location for this node. NoSourceMap = NoLeadingSourceMap | NoTrailingSourceMap, // Do not emit a source map location for this node. - NoNestedSourceMaps = 1 << 6, // Do not emit source map locations for children of this node. - NoTokenLeadingSourceMaps = 1 << 7, // Do not emit leading source map location for token nodes. - NoTokenTrailingSourceMaps = 1 << 8, // Do not emit trailing source map location for token nodes. + NoNestedSourceMaps = 1<<6, // Do not emit source map locations for children of this node. + NoTokenLeadingSourceMaps = 1<<7, // Do not emit leading source map location for token nodes. + NoTokenTrailingSourceMaps = 1<<8, // Do not emit trailing source map location for token nodes. NoTokenSourceMaps = NoTokenLeadingSourceMaps | NoTokenTrailingSourceMaps, // Do not emit source map locations for tokens of this node. - NoLeadingComments = 1 << 9, // Do not emit leading comments for this node. - NoTrailingComments = 1 << 10, // Do not emit trailing comments for this node. + NoLeadingComments = 1<<9, // Do not emit leading comments for this node. + NoTrailingComments = 1<<10, // Do not emit trailing comments for this node. NoComments = NoLeadingComments | NoTrailingComments, // Do not emit comments for this node. - NoNestedComments = 1 << 11, - HelperName = 1 << 12, // The Identifier refers to an *unscoped* emit helper (one that is emitted at the top of the file) - ExportName = 1 << 13, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal). - LocalName = 1 << 14, // Ensure an export prefix is not added for an identifier that points to an exported declaration. - InternalName = 1 << 15, // The name is internal to an ES5 class body function. - Indented = 1 << 16, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter). - NoIndentation = 1 << 17, // Do not indent the node. - AsyncFunctionBody = 1 << 18, - ReuseTempVariableScope = 1 << 19, // Reuse the existing temp variable scope during emit. - CustomPrologue = 1 << 20, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed). - NoHoisting = 1 << 21, // Do not hoist this declaration in --module system - HasEndOfDeclarationMarker = 1 << 22, // Declaration has an associated NotEmittedStatement to mark the end of the declaration - Iterator = 1 << 23, // The expression to a `yield*` should be treated as an Iterator when down-leveling, not an Iterable. - NoAsciiEscaping = 1 << 24, // When synthesizing nodes that lack an original node or textSourceNode, we want to write the text on the node with ASCII escaping substitutions. - /*@internal*/ TypeScriptClassWrapper = 1 << 25, // The node is an IIFE class wrapper created by the ts transform. - /*@internal*/ NeverApplyImportHelper = 1 << 26, // Indicates the node should never be wrapped with an import star helper (because, for example, it imports tslib itself) - /*@internal*/ IgnoreSourceNewlines = 1 << 27, // Overrides `printerOptions.preserveSourceNewlines` to print this node (and all descendants) with default whitespace. - /*@internal*/ Immutable = 1 << 28, // Indicates a node is a singleton intended to be reused in multiple locations. Any attempt to make further changes to the node will result in an error. - /*@internal*/ IndirectCall = 1 << 29, // Emit CallExpression as an indirect call: `(0, f)()` + NoNestedComments = 1<<11, + HelperName = 1<<12, // The Identifier refers to an *unscoped* emit helper (one that is emitted at the top of the file) + ExportName = 1<<13, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal). + LocalName = 1<<14, // Ensure an export prefix is not added for an identifier that points to an exported declaration. + InternalName = 1<<15, // The name is internal to an ES5 class body function. + Indented = 1<<16, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter). + NoIndentation = 1<<17, // Do not indent the node. + AsyncFunctionBody = 1<<18, + ReuseTempVariableScope = 1<<19, // Reuse the existing temp variable scope during emit. + CustomPrologue = 1<<20, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed). + NoHoisting = 1<<21, // Do not hoist this declaration in --module system + HasEndOfDeclarationMarker = 1<<22, // Declaration has an associated NotEmittedStatement to mark the end of the declaration + Iterator = 1<<23, // The expression to a `yield*` should be treated as an Iterator when down-leveling, not an Iterable. + NoAsciiEscaping = 1<<24, // When synthesizing nodes that lack an original node or textSourceNode, we want to write the text on the node with ASCII escaping substitutions. + /*@internal*/ TypeScriptClassWrapper = 1<<25, // The node is an IIFE class wrapper created by the ts transform. + /*@internal*/ NeverApplyImportHelper = 1<<26, // Indicates the node should never be wrapped with an import star helper (because, for example, it imports tslib itself) + /*@internal*/ IgnoreSourceNewlines = 1<<27, // Overrides `printerOptions.preserveSourceNewlines` to print this node (and all descendants) with default whitespace. + /*@internal*/ Immutable = 1<<28, // Indicates a node is a singleton intended to be reused in multiple locations. Any attempt to make further changes to the node will result in an error. + /*@internal*/ IndirectCall = 1<<29, // Emit CallExpression as an indirect call: `(0, f)()` }; enum class ModifierFlags { None = 0, - Export = 1 << 0, // Declarations - Ambient = 1 << 1, // Declarations - Public = 1 << 2, // Property/Method - Private = 1 << 3, // Property/Method - Protected = 1 << 4, // Property/Method - Static = 1 << 5, // Property/Method - Readonly = 1 << 6, // Property/Method - Abstract = 1 << 7, // Class/Method/ConstructSignature - Async = 1 << 8, // Property/Method/Function - Default = 1 << 9, // Function/Class (export default declaration) - Const = 1 << 11, // Const enum - HasComputedJSDocModifiers = 1 << 12, // Indicates the computed modifier flags include modifiers from JSDoc. - - Deprecated = 1 << 13, // Deprecated tag. - Override = 1 << 14, // Override method. - In = 1 << 15, // Contravariance modifier - Out = 1 << 16, // Covariance modifier - HasComputedFlags = 1 << 29, // Modifier flags have been computed + Export = 1<<0, // Declarations + Ambient = 1<<1, // Declarations + Public = 1<<2, // Property/Method + Private = 1<<3, // Property/Method + Protected = 1<<4, // Property/Method + Static = 1<<5, // Property/Method + Readonly = 1<<6, // Property/Method + Abstract = 1<<7, // Class/Method/ConstructSignature + Async = 1<<8, // Property/Method/Function + Default = 1<<9, // Function/Class (export default declaration) + Const = 1<<11, // Const enum + HasComputedJSDocModifiers = 1<<12, // Indicates the computed modifier flags include modifiers from JSDoc. + + Deprecated = 1<<13, // Deprecated tag. + Override = 1<<14, // Override method. + In = 1<<15, // Contravariance modifier + Out = 1<<16, // Covariance modifier + HasComputedFlags = 1<<29, // Modifier flags have been computed AccessibilityModifier = Public | Private | Protected, // Accessibility modifiers and 'readonly' can be attached to a parameter in a constructor to make it a property. @@ -960,41 +974,41 @@ namespace ts::types { // Facts // - Flags used to indicate that a BaseNode or subtree contains syntax that requires transformation. - ContainsTypeScript = 1 << 0, - ContainsJsx = 1 << 1, - ContainsESNext = 1 << 2, - ContainsES2022 = 1 << 3, - ContainsES2021 = 1 << 4, - ContainsES2020 = 1 << 5, - ContainsES2019 = 1 << 6, - ContainsES2018 = 1 << 7, - ContainsES2017 = 1 << 8, - ContainsES2016 = 1 << 9, - ContainsES2015 = 1 << 10, - ContainsGenerator = 1 << 11, - ContainsDestructuringAssignment = 1 << 12, + ContainsTypeScript = 1<<0, + ContainsJsx = 1<<1, + ContainsESNext = 1<<2, + ContainsES2022 = 1<<3, + ContainsES2021 = 1<<4, + ContainsES2020 = 1<<5, + ContainsES2019 = 1<<6, + ContainsES2018 = 1<<7, + ContainsES2017 = 1<<8, + ContainsES2016 = 1<<9, + ContainsES2015 = 1<<10, + ContainsGenerator = 1<<11, + ContainsDestructuringAssignment = 1<<12, // Markers // - Flags used to indicate that a subtree contains a specific transformation. - ContainsTypeScriptClassSyntax = 1 << 12, // Decorators, Property Initializers, Parameter Property Initializers - ContainsLexicalThis = 1 << 13, - ContainsRestOrSpread = 1 << 14, - ContainsObjectRestOrSpread = 1 << 15, - ContainsComputedPropertyName = 1 << 16, - ContainsBlockScopedBinding = 1 << 17, - ContainsBindingPattern = 1 << 18, - ContainsYield = 1 << 19, - ContainsAwait = 1 << 20, - ContainsHoistedDeclarationOrCompletion = 1 << 21, - ContainsDynamicImport = 1 << 22, - ContainsClassFields = 1 << 23, - ContainsPossibleTopLevelAwait = 1 << 24, - ContainsLexicalSuper = 1 << 25, - ContainsUpdateExpressionForIdentifier = 1 << 26, + ContainsTypeScriptClassSyntax = 1<<12, // Decorators, Property Initializers, Parameter Property Initializers + ContainsLexicalThis = 1<<13, + ContainsRestOrSpread = 1<<14, + ContainsObjectRestOrSpread = 1<<15, + ContainsComputedPropertyName = 1<<16, + ContainsBlockScopedBinding = 1<<17, + ContainsBindingPattern = 1<<18, + ContainsYield = 1<<19, + ContainsAwait = 1<<20, + ContainsHoistedDeclarationOrCompletion = 1<<21, + ContainsDynamicImport = 1<<22, + ContainsClassFields = 1<<23, + ContainsPossibleTopLevelAwait = 1<<24, + ContainsLexicalSuper = 1<<25, + ContainsUpdateExpressionForIdentifier = 1<<26, // Please leave this as 1 << 29. // It is the maximum bit we can set before we outgrow the size of a v8 small integer (SMI) on an x86 system. // It is a good reminder of how much room we have left - HasComputedFlags = 1 << 29, // Transform flags have been computed. + HasComputedFlags = 1<<29, // Transform flags have been computed. // Assertions // - Bitmasks that are used to assert facts about the syntax of a BaseNode and its subtree. @@ -1043,20 +1057,22 @@ namespace ts::types { }; enum class OuterExpressionKinds { - Parentheses = 1 << 0, - TypeAssertions = 1 << 1, - NonNullAssertions = 1 << 2, - PartiallyEmittedExpressions = 1 << 3, + Parentheses = 1<<0, + TypeAssertions = 1<<1, + NonNullAssertions = 1<<2, + PartiallyEmittedExpressions = 1<<3, Assertions = TypeAssertions | NonNullAssertions, All = Parentheses | Assertions | PartiallyEmittedExpressions, - ExcludeJSDocTypeAssertion = 1 << 4, + ExcludeJSDocTypeAssertion = 1<<4, }; } -template <> struct fmt::formatter : formatter { - template auto format(ts::types::SyntaxKind p, FormatContext &ctx) { +template<> +struct fmt::formatter: formatter { + template + auto format(ts::types::SyntaxKind p, FormatContext &ctx) { return formatter::format(magic_enum::enum_name(p), ctx); } }; @@ -1215,15 +1231,16 @@ namespace ts { if (pos != other.pos || end != other.end || hasTrailingComma != other.hasTrailingComma || isMissingList != other.isMissingList) return false; if (transformFlags != other.transformFlags) return false; if (list.size() != other.list.size()) return false; - for (int i = 0; i < list.size(); i++) { + for (int i = 0; i> slice(int start, int end = 0) { - return slice < shared> - (list, start, end); + if (!end) end = list.size(); + return std::vector>(list.begin() + start, list.begin() + end); +// return slice>(list, start, end); } }; @@ -1574,10 +1591,10 @@ namespace ts { /*@internal*/ KindMask = 7, // Mask to extract the kind of identifier from its flags. // Flags - ReservedInNestedScopes = 1 << 3, // Reserve the generated name in nested scopes - Optimistic = 1 << 4, // First instance won't use '_#' if there's no conflict - FileLevel = 1 << 5, // Use only the file identifiers list and not generated names to search for conflicts - AllowNameSubstitution = 1 << 6, // Used by `module.ts` to indicate generated nodes which can have substitutions performed upon them (as they were generated by an earlier transform phase) + ReservedInNestedScopes = 1<<3, // Reserve the generated name in nested scopes + Optimistic = 1<<4, // First instance won't use '_#' if there's no conflict + FileLevel = 1<<5, // Use only the file identifiers list and not generated names to search for conflicts + AllowNameSubstitution = 1<<6, // Used by `module.ts` to indicate generated nodes which can have substitutions performed upon them (as they were generated by an earlier transform phase) }; struct Identifier: BrandKind { @@ -1596,19 +1613,19 @@ namespace ts { }; enum class FlowFlags { - Unreachable = 1 << 0, // Unreachable code - Start = 1 << 1, // Start of flow graph - BranchLabel = 1 << 2, // Non-looping junction - LoopLabel = 1 << 3, // Looping junction - Assignment = 1 << 4, // Assignment - TrueCondition = 1 << 5, // Condition known to be true - FalseCondition = 1 << 6, // Condition known to be false - SwitchClause = 1 << 7, // Switch statement clause - ArrayMutation = 1 << 8, // Potential array mutation - Call = 1 << 9, // Potential assertion call - ReduceLabel = 1 << 10, // Temporarily reduce antecedents of label - Referenced = 1 << 11, // Referenced as antecedent once - Shared = 1 << 12, // Referenced as antecedent more than once + Unreachable = 1<<0, // Unreachable code + Start = 1<<1, // Start of flow graph + BranchLabel = 1<<2, // Non-looping junction + LoopLabel = 1<<3, // Looping junction + Assignment = 1<<4, // Assignment + TrueCondition = 1<<5, // Condition known to be true + FalseCondition = 1<<6, // Condition known to be false + SwitchClause = 1<<7, // Switch statement clause + ArrayMutation = 1<<8, // Potential array mutation + Call = 1<<9, // Potential assertion call + ReduceLabel = 1<<10, // Temporarily reduce antecedents of label + Referenced = 1<<11, // Referenced as antecedent once + Shared = 1<<12, // Referenced as antecedent more than once Label = BranchLabel | LoopLabel, Condition = TrueCondition | FalseCondition, @@ -2240,7 +2257,6 @@ namespace ts { Property(type, TypeNode); }; - #define NamedImportsOrExports NamedImports, NamedExports #define ImportOrExportSpecifier ImportSpecifier, ExportSpecifier #define TypeOnlyCompatibleAliasDeclaration ImportClause, ImportEqualsDeclaration, NamespaceImport, ImportOrExportSpecifier; diff --git a/src/utilities.cpp b/src/utilities.cpp index 6ce6233..37d8feb 100644 --- a/src/utilities.cpp +++ b/src/utilities.cpp @@ -283,19 +283,20 @@ namespace ts { // text = formatStringFromArg(text, 0, v); // } - return DiagnosticWithDetachedLocation{ - { - { - .messageText = text, - .category = message->category, - .code = message->code, - }, - .reportsUnnecessary = message->reportsUnnecessary, - }, - .fileName = fileName, - .start = start, - .length = length, - }; + return DiagnosticWithDetachedLocation(fileName, text, message->category, message->code, message->reportsUnnecessary, start, length); +// { +// { +// { +// .messageText = text, +// .category = message->category, +// .code = message->code, +// }, +// .reportsUnnecessary = message->reportsUnnecessary, +// }, +// .fileName = fileName, +// .start = start, +// .length = length, +// }; } DiagnosticWithDetachedLocation &addRelatedInfo(DiagnosticWithDetachedLocation &diagnostic, vector relatedInformation) {