diff --git a/src/MapleFE/ast2cpp/runtime/include/builtins.h b/src/MapleFE/ast2cpp/runtime/include/builtins.h index 8eea79172d8fdbd248775f8b05e69930648a06e5..d5aa30f5ec7827faa994092f19701ec5357f0595 100644 --- a/src/MapleFE/ast2cpp/runtime/include/builtins.h +++ b/src/MapleFE/ast2cpp/runtime/include/builtins.h @@ -118,40 +118,99 @@ public: static Ctor ctor; }; -class IteratorProto : public Object { -public: + +// 20.5 Error objects for execptions +class Error : public Object { // TODO - IteratorProto(Function* ctor, Object* proto) : Object(ctor, proto) { } }; -class GeneratorFunctionPrototype : public Function { -public: - GeneratorFunctionPrototype(Function* ctor, Object* proto, Object* prototype_proto) : Function(ctor, proto, prototype_proto) { } +// JavaScript generators and generator functions +// - The builtin GeneratorFunction is the constructor for all generator functions. +// - Generator functions are called directly to return generators (with closure). +// - Generators are iterators that calls corresponding generator function with +// data captured in closure to iterate for results. + +// ecma-262 section references are based on ecma-262 edition 12.0 + +// ecma262 27.1.1.5 IteratorResult interface: +struct IteratorResult { + bool _done; // status of iterator next() call + JS_Val _value; // done=false: current iteration element value + // done=true: return value of the iterator, undefined if none returned + IteratorResult() : _done(true), _value(undefined) { } + IteratorResult(bool done, JS_Val val) : _done(done), _value(val) { } + ~IteratorResult() { } }; +// ecma262 27.1.1.1 Iterable interface: +// To be iterable, an object or one of the objects up its prototype chain must +// have a property with a @@iterator key ([Symbol.iterator], the value of +// which is a function that returns iterators (i.e objects with Iterator interace +// methods next/return/throw). +// +// Note: For iterable objects such as arrays and strings, [Symbol.iterator]() +// returns a new iteraor object. But for the intrinsic object %IteratorPrototype% +// (27.1.2.1) it returns the current iterator instance, which +// means for all iterators, [Sumbol.iterator]() returns itself. + +// ecma262 27.1.2.1 %IteratorPrototype%: +// 1) All objects that implement iterator interface also inherit from %IteratorPrototype% +// 2) %IteratorPrototype% provides shared props for all iterator objects +// 3) %IteratorPrototype%[Symbol.iterator]() = this (current iterator instance) - used in for loops +class IteratorProto : public Object { +public: + IteratorProto(Function* ctor, Object* proto) : Object(ctor, proto) { } + ~IteratorProto() { } + // note: the arg on an iterator's 1st next() call is ignored per spec 27.5.1.2 + virtual IteratorResult _next(JS_Val* arg) { return IteratorResult(); } + virtual IteratorResult _return(JS_Val* val) { return IteratorResult(); } + virtual IteratorResult _throw(Error exception) { return IteratorResult(); } + // TODO: %IteratorPrototype%[Symbol.iterator]() = this (current iterator instance) +}; + +// 27.5.1 Generator Prototype Object +// - in ecma edition 11: named %GeneratorPrototype% (25.4.1) +// - in ecma edition 12: named %GeneratorFunction.prototype.prototype% (27.5.1) but +// labelled as %GeneratoPrototype% in 27.3 Figure 5. +// Label corrected in version at tc39. class GeneratorProto : public IteratorProto { public: - // TODO GeneratorProto(Function* ctor, Object* proto) : IteratorProto(ctor, proto) { } + ~GeneratorProto() { } + void* _yield = nullptr; // pointer to yield label to resume execution + bool _finished = false; // flag if generator is in finished state + bool _firstNext = true; // flag if first next has been called on iterator (27.5.1.2) + JS_Val _retval = undefined; // save optional arg from iterator's return(arg) method }; +// 27.3.1 GeneratorFunction Constructor class GeneratorFunc : public Function::Ctor { public: - // TODO GeneratorFunc(Function* ctor, Object* proto, Object* prototype_proto, Function* prototype_obj) : Function::Ctor(ctor, proto, prototype_proto, prototype_obj) { } + ~GeneratorFunc() {} +}; + +// 27.3.3 GeneratorFunction Prototype Obejct +// - in ecma edition 11: named %Generator% (25.2.3) +// - in ecma edition 12: named %GeneratorFunction.prorotype% (27.3.3) but +// labelled as %Generator% in 27.3 Figure 5. +// Label corrected in tc39 version. +class GeneratorFuncPrototype : public Function { +public: + GeneratorFuncPrototype(Function* ctor, Object* proto, Object* prototype_proto) : Function(ctor, proto, prototype_proto) { } }; -// GeneratorFunction objects. (Ref: ECMA spec 27.3) +// Generator related intrinsic objects. (ecma 27.3) // IteratorPrototype: It is not a prototype object of any constructor func, but holds shared properties for iterators -// GeneratorFunction: A builtin function used as the constructor for generators (i.e. generator functions).// -// Generator: (a.k.a. GeneratorFuncion.prototype in 2022 spec) is the prototype object of GeneratorFunction, +// GeneratorFunction: A builtin function used as the constructor for generator functions. +// Generator: (GeneratorFuncion.prototype in edition 12.0) is the prototype object of GeneratorFunction, // It is a special object used as both prototype object and constructor - as prototype for sharing // properties between generator functions, and as constructor whose prototype object (GeneratorPrototype -// in pre-2022 spec) holds shared properties for instances returned by generator functions. +// in edition 11) holds shared properties for generators (i.e. instances returned by generator functions. extern IteratorProto IteratorPrototype; extern GeneratorFunc GeneratorFunction; -extern GeneratorFunctionPrototype Generator; +extern GeneratorFuncPrototype Generator; extern Object* GeneratorPrototype; } // namespace t2crt diff --git a/src/MapleFE/ast2cpp/runtime/include/ts2cpp.h b/src/MapleFE/ast2cpp/runtime/include/ts2cpp.h index e88bce6fca22e7f7a4cea07c11b6da50188d81e9..5318a3c22fe48126c78948382ca4963b6c6882d2 100644 --- a/src/MapleFE/ast2cpp/runtime/include/ts2cpp.h +++ b/src/MapleFE/ast2cpp/runtime/include/ts2cpp.h @@ -82,6 +82,9 @@ struct JS_Val { JS_Type type; bool IsCxxProp() { return type & TY_CXX; } // true if a cxx field + bool IsNone() { return type == TY_None; } + bool IsNull() { return type & TY_Null; } + bool IsUndef() { return type & TY_Undef; } JS_Val() { x.val_long = 0l; type = TY_Undef; } JS_Val(int64_t l, JS_Type t, bool c) { x.val_long = l; type = t; } @@ -445,6 +448,11 @@ void GenerateDOTGraph( std::vector&obj, std::vector&name) } // namespace t2crt +extern std::ostream& operator<< (std::ostream& out, const t2crt::JS_Val& v); +extern std::ostream& operator<< (std::ostream& out, t2crt::Object* obj); +extern const t2crt::JS_Val undefined; +extern const t2crt::JS_Val null; + #include "builtins.h" template @@ -491,10 +499,7 @@ std::ostream& operator<< (std::ostream& out, const t2crt::Array& v) { } return out; } -extern std::ostream& operator<< (std::ostream& out, const t2crt::JS_Val& v); -extern std::ostream& operator<< (std::ostream& out, t2crt::Object* obj); -extern const t2crt::JS_Val undefined; -extern const t2crt::JS_Val null; + #define debugger (0) using t2crt::Object; diff --git a/src/MapleFE/ast2cpp/runtime/src/builtins.cpp b/src/MapleFE/ast2cpp/runtime/src/builtins.cpp index 8537db12950b5d2f5376514acd2b19844e5b5a59..43f6dd65fbd1bf433a3576824b3745121c340ef2 100644 --- a/src/MapleFE/ast2cpp/runtime/src/builtins.cpp +++ b/src/MapleFE/ast2cpp/runtime/src/builtins.cpp @@ -8,7 +8,7 @@ Number::Ctor Number::ctor (&Function::ctor, Function::ctor.prototype, Object: RegExp::Ctor RegExp::ctor (&Function::ctor, Function::ctor.prototype, Object::ctor.prototype); IteratorProto IteratorPrototype(&Object::ctor, Object::ctor.prototype); -GeneratorFunctionPrototype Generator(&GeneratorFunction, Function::ctor.prototype, &IteratorPrototype); +GeneratorFuncPrototype Generator(&GeneratorFunction, Function::ctor.prototype, &IteratorPrototype); GeneratorFunc GeneratorFunction(&Function::ctor, &Function::ctor, Function::ctor.prototype, &Generator); Object* GeneratorPrototype = Generator.prototype; diff --git a/src/MapleFE/astopt/include/ast_adj.h b/src/MapleFE/astopt/include/ast_adj.h index 04be9e8d3671959a3acad903fd4c85f9d5b6851c..5674e6678dab4f008ff0bc19db9230f9d44307c9 100644 --- a/src/MapleFE/astopt/include/ast_adj.h +++ b/src/MapleFE/astopt/include/ast_adj.h @@ -78,6 +78,7 @@ class AdjustASTVisitor : public AstVisitor { TypeAliasNode *VisitTypeAliasNode(TypeAliasNode *node); LiteralNode *VisitLiteralNode(LiteralNode *node); UnaOperatorNode *VisitUnaOperatorNode(UnaOperatorNode *node); + PrimArrayTypeNode *VisitPrimArrayTypeNode(PrimArrayTypeNode *node); }; } diff --git a/src/MapleFE/astopt/include/ast_handler.h b/src/MapleFE/astopt/include/ast_handler.h index fea8de93f6af055dab06965ade73fcca723869bf..fdeaa249df760cbcc02d81d69024506f6049a036 100644 --- a/src/MapleFE/astopt/include/ast_handler.h +++ b/src/MapleFE/astopt/include/ast_handler.h @@ -133,6 +133,8 @@ class Module_Handler { std::unordered_map mArrayDeclId2EleTypeIdxMap; // array literal's dim: decl node id to dim std::unordered_map mArrayDeclId2DimMap; + // nodeid to used generator + std::unordered_map mGeneratorUsedMap; // fields' nodeid set std::unordered_set mDirectFieldSet; // alias type, identifier node id @@ -239,42 +241,18 @@ class Module_Handler { Module_Handler *GetModuleHandler(TreeNode *node) {return mASTHandler->GetModuleHandler(node);} // array's element typeid - TypeId GetArrayElemTypeId(unsigned nid) { - TypeId tid = TY_None; - if (mArrayDeclId2EleTypeIdMap.find(nid) != mArrayDeclId2EleTypeIdMap.end()) { - tid = mArrayDeclId2EleTypeIdMap[nid]; - } - return tid; - } - - void SetArrayElemTypeId(unsigned nid, TypeId tid) { - mArrayDeclId2EleTypeIdMap[nid] = tid; - } - - // array's element typeidx - unsigned GetArrayElemTypeIdx(unsigned nid) { - unsigned tidx = 0; - if (mArrayDeclId2EleTypeIdxMap.find(nid) != mArrayDeclId2EleTypeIdxMap.end()) { - tidx = mArrayDeclId2EleTypeIdxMap[nid]; - } - return tidx; - } - - void SetArrayElemTypeIdx(unsigned nid, unsigned tidx) { - mArrayDeclId2EleTypeIdxMap[nid] = tidx; - } - - DimensionNode *GetArrayDim(unsigned nid) { - DimensionNode *dim = NULL; - if (mArrayDeclId2DimMap.find(nid) != mArrayDeclId2DimMap.end()) { - dim = mArrayDeclId2DimMap[nid]; - } - return dim; - } - - void SetArrayDim(unsigned nid, DimensionNode *dim) { - mArrayDeclId2DimMap[nid] = dim; - } + TypeId GetArrayElemTypeId(unsigned nid); + void SetArrayElemTypeId(unsigned nid, TypeId tid); + unsigned GetArrayElemTypeIdx(unsigned nid); + void SetArrayElemTypeIdx(unsigned nid, unsigned tidx); + DimensionNode *GetArrayDim(unsigned nid); + void SetArrayDim(unsigned nid, DimensionNode *dim); + + // used generator + void AddGeneratorUsed(unsigned nid, FunctionNode *func); + bool IsGeneratorUsed(unsigned nid); + FunctionNode *GetGeneratorUsed(unsigned nid); + void UpdateGeneratorUsed(unsigned target, unsigned src); // API to check a node is c++ field which satisfy both: // 1. direct field @@ -282,6 +260,7 @@ class Module_Handler { bool IsCppField(TreeNode *node); void Dump(char *msg); + void DumpArrayElemTypeIdMap(); }; } diff --git a/src/MapleFE/astopt/src/ast_adj.cpp b/src/MapleFE/astopt/src/ast_adj.cpp index f5948760a4523ad1c031c260116ea9566f54bbcc..3a36e80d0fe0de7b765e0e6de151fffd46c0cc19 100644 --- a/src/MapleFE/astopt/src/ast_adj.cpp +++ b/src/MapleFE/astopt/src/ast_adj.cpp @@ -169,6 +169,17 @@ StructNode *AdjustASTVisitor::VisitStructNode(StructNode *node) { return (StructNode*)newnode; } +// convert prim array node to array type node +PrimArrayTypeNode *AdjustASTVisitor::VisitPrimArrayTypeNode(PrimArrayTypeNode *node) { + (void) AstVisitor::VisitPrimArrayTypeNode(node); + ArrayTypeNode *arr = mHandler->NewTreeNode(); + DimensionNode *dim = node->GetDims(); + arr->SetDims(dim); + arr->SetElemType(node->GetPrim()); + + return (PrimArrayTypeNode *)arr; +} + // set UserTypeNode's mStrIdx to be its mId's UserTypeNode *AdjustASTVisitor::VisitUserTypeNode(UserTypeNode *node) { (void) AstVisitor::VisitUserTypeNode(node); @@ -179,16 +190,33 @@ UserTypeNode *AdjustASTVisitor::VisitUserTypeNode(UserTypeNode *node) { // use array type node DimensionNode *dim = node->GetDims(); - TreeNode *p = node->GetParent(); - if (dim && p->IsIdentifier()) { + bool isarr = (dim != NULL); + // element type + TreeNode *etype = NULL; + + if (isarr) { + etype = node; + } else if (id && id->IsIdentifier()) { + IdentifierNode *idnode = static_cast(id); + isarr = (idnode->GetStrIdx() == gStringPool.GetStrIdx("Array")); + if (isarr) { + if (unsigned s = node->GetTypeGenericsNum()) { + if (s == 1) { + etype = node->GetTypeGeneric(0); + } else { + NOTYETIMPL("array usertype with multiple generic type"); + } + } + } + } + + if (isarr) { ArrayTypeNode *arr = mHandler->NewTreeNode(); arr->SetDims(dim); - arr->SetElemType(node); - node->SetDims(NULL); - IdentifierNode *inode = static_cast(p); - inode->SetType(arr); - mHandler->SetArrayElemTypeId(inode->GetNodeId(), id->GetTypeId()); + arr->SetElemType(etype); + node = (UserTypeNode *)arr; } + return node; } diff --git a/src/MapleFE/astopt/src/ast_handler.cpp b/src/MapleFE/astopt/src/ast_handler.cpp index 6d6bd0b0ce9b376efc5e69ddff67470c1d396179..205cb110c67743b98ad8bdd926e985db9ec55110 100644 --- a/src/MapleFE/astopt/src/ast_handler.cpp +++ b/src/MapleFE/astopt/src/ast_handler.cpp @@ -27,6 +27,7 @@ #include "ast_xxport.h" #include "astopt.h" #include "typetable.h" +#include "gen_astdump.h" namespace maplefe { @@ -200,6 +201,10 @@ TreeNode *Module_Handler::FindDecl(IdentifierNode *node, bool deep) { decl = scope->FindDeclOf(stridx); } + if (!decl && deep) { + decl = scope->FindExportedDeclOf(stridx); + } + if (decl) { AddNodeId2DeclMap(node->GetNodeId(), decl); } @@ -244,6 +249,65 @@ bool Module_Handler::IsDirectField(TreeNode *node) { return mDirectFieldSet.find(node->GetNodeId()) != mDirectFieldSet.end(); } +// array's element typeid +TypeId Module_Handler::GetArrayElemTypeId(unsigned nid) { + TypeId tid = TY_None; + if (mArrayDeclId2EleTypeIdMap.find(nid) != mArrayDeclId2EleTypeIdMap.end()) { + tid = mArrayDeclId2EleTypeIdMap[nid]; + } + return tid; +} + +void Module_Handler::SetArrayElemTypeId(unsigned nid, TypeId tid) { + mArrayDeclId2EleTypeIdMap[nid] = tid; +} + +// array's element typeidx +unsigned Module_Handler::GetArrayElemTypeIdx(unsigned nid) { + unsigned tidx = 0; + if (mArrayDeclId2EleTypeIdxMap.find(nid) != mArrayDeclId2EleTypeIdxMap.end()) { + tidx = mArrayDeclId2EleTypeIdxMap[nid]; + } + return tidx; +} + +void Module_Handler::SetArrayElemTypeIdx(unsigned nid, unsigned tidx) { + mArrayDeclId2EleTypeIdxMap[nid] = tidx; +} + +DimensionNode *Module_Handler::GetArrayDim(unsigned nid) { + DimensionNode *dim = NULL; + if (mArrayDeclId2DimMap.find(nid) != mArrayDeclId2DimMap.end()) { + dim = mArrayDeclId2DimMap[nid]; + } + return dim; +} + +void Module_Handler::SetArrayDim(unsigned nid, DimensionNode *dim) { + mArrayDeclId2DimMap[nid] = dim; +} + +void Module_Handler::AddGeneratorUsed(unsigned nid, FunctionNode *func) { + mGeneratorUsedMap[nid] = func; +} + +bool Module_Handler::IsGeneratorUsed(unsigned nid) { + return (mGeneratorUsedMap.find(nid) != mGeneratorUsedMap.end()); +} + +FunctionNode *Module_Handler::GetGeneratorUsed(unsigned nid) { + if (mGeneratorUsedMap.find(nid) != mGeneratorUsedMap.end()) { + return mGeneratorUsedMap[nid]; + } + return NULL; +} + +void Module_Handler::UpdateGeneratorUsed(unsigned target, unsigned src) { + if (mGeneratorUsedMap.find(src) != mGeneratorUsedMap.end()) { + mGeneratorUsedMap[target] = mGeneratorUsedMap[src]; + } +} + bool Module_Handler::IsFromLambda(TreeNode *node) { if (!node) { return false; @@ -266,4 +330,12 @@ void Module_Handler::Dump(char *msg) { func->Dump(); } +void Module_Handler::DumpArrayElemTypeIdMap() { + std::cout << "================= ArrayDeclId2EleTypeIdMap ==========" << std::endl; + for (auto it : mArrayDeclId2EleTypeIdMap) { + std::cout << "nodeid : " << it.first << " " + << AstDump::GetEnumTypeId(it.second) << std::endl; + } +} + } diff --git a/src/MapleFE/astopt/src/ast_ti.cpp b/src/MapleFE/astopt/src/ast_ti.cpp index cc2d1c69d62e6196393cb9cc94f758ec9e296fad..d69b053d62d0877b86ec92335343e3657c2cec12 100644 --- a/src/MapleFE/astopt/src/ast_ti.cpp +++ b/src/MapleFE/astopt/src/ast_ti.cpp @@ -70,6 +70,10 @@ void TypeInfer::TypeInference() { MSGNOLOC0("============== Check Type =============="); CheckTypeVisitor visitor_check(mHandler, mFlags, true); visitor_check.Visit(module); + + if (mFlags & FLG_trace_3) { + mHandler->DumpArrayElemTypeIdMap(); + } } // build up mNodeId2Decl by visiting each Identifier @@ -1052,6 +1056,10 @@ CallNode *TypeInferVisitor::VisitCallNode(CallNode *node) { if (decl) { if (decl->IsFunction()) { FunctionNode *func = static_cast(decl); + // check if called a generator + if (func->IsGenerator()) { + mHandler->AddGeneratorUsed(node->GetNodeId(), func); + } // update call's return type if (func->GetType()) { UpdateTypeId(node, func->GetType()->GetTypeId()); @@ -1255,6 +1263,11 @@ DeclNode *TypeInferVisitor::VisitDeclNode(DeclNode *node) { elemTypeId = GetArrayElemTypeId(init); elemTypeIdx = GetArrayElemTypeIdx(init); isArray = (elemTypeId != TY_None); + // pass IsGeneratorUsed + mHandler->UpdateGeneratorUsed(node->GetNodeId(), init->GetNodeId()); + if (var) { + mHandler->UpdateGeneratorUsed(var->GetNodeId(), init->GetNodeId()); + } } if (var) { // normal cases diff --git a/src/MapleFE/test/typescript/unit_tests/semicolon-missing14.ts b/src/MapleFE/test/typescript/unit_tests/semicolon-missing14.ts new file mode 100644 index 0000000000000000000000000000000000000000..2311369e565c20dfa593325081aab7dc46b80c02 --- /dev/null +++ b/src/MapleFE/test/typescript/unit_tests/semicolon-missing14.ts @@ -0,0 +1,21 @@ +class Car { + private _make: string; + constructor(make: string) { + this._make = make; + } + public getMake(): string { + return this._make; + } +} + +class Model extends Car { + private _model: string; + constructor(make: string, model: string) {super(make) this._model = super.getMake() + model; + } + public getModel(): string { + return this._model; + } +} + +let passat: Model = new Model("VW", "Passat"); +console.log(passat.getMake(), passat.getModel()); diff --git a/src/MapleFE/test/typescript/unit_tests/semicolon-missing14.ts.result b/src/MapleFE/test/typescript/unit_tests/semicolon-missing14.ts.result new file mode 100644 index 0000000000000000000000000000000000000000..5368fc30da365058f5d1934d73ee3dfecd5b2e61 --- /dev/null +++ b/src/MapleFE/test/typescript/unit_tests/semicolon-missing14.ts.result @@ -0,0 +1,38 @@ +Matched 36 tokens. +Matched 88 tokens. +Matched 101 tokens. +Matched 118 tokens. +============= Module =========== +== Sub Tree == +class Car + Fields: + _make + Instance Initializer: + Constructors: + constructor (make) throws: + this._make Assign make + Methods: + func getMake() throws: + return this._make + LocalClasses: + LocalInterfaces: + +== Sub Tree == +class Model + Fields: + _model + Instance Initializer: + Constructors: + constructor (make,model) throws: + super(make) + this._model Assign super.getMake() Add model + Methods: + func getModel() throws: + return this._model + LocalClasses: + LocalInterfaces: + +== Sub Tree == +js_let Decl: passat=new Model("VW","Passat") +== Sub Tree == +console.log(passat.getMake(),passat.getModel()) diff --git a/src/MapleFE/typescript/src/lang_spec.cpp b/src/MapleFE/typescript/src/lang_spec.cpp index b585ae76c752976d78ae3ef568af7d242c5b64c6..b2022817b90b2ef798909c39710dcb1cc7649f29 100644 --- a/src/MapleFE/typescript/src/lang_spec.cpp +++ b/src/MapleFE/typescript/src/lang_spec.cpp @@ -585,14 +585,24 @@ bool TypescriptParser::TraverseASI(RuleTable *rule_table, // case 3. This is a special case we want to catch: // foo(x) a=b; <-- , is missing before a=b + // { foo(x) a=b; } <-- , is missing before a=b // There could be many more similar cases, but we just match this special one in our // unit test. We don't encourage people write weird code. if ( (curr_token->IsIdentifier() || curr_token->IsKeyword()) && - (prev_token->IsSeparator() && (prev_token->GetSepId() == SEP_Rparen)) ){ + (prev_token->IsSeparator() && (prev_token->GetSepId() == SEP_Rparen)) && + mActiveTokens.GetNum() > 4 ){ Token *prev_2_token = GetActiveToken(mCurToken - 2); Token *prev_3_token = GetActiveToken(mCurToken - 3); Token *prev_4_token = GetActiveToken(mCurToken - 4); - if ( prev_4_token->mLineBegin && + + bool lbrace_ok = false; + if (mActiveTokens.GetNum() > 5){ + Token *prev_5_token = GetActiveToken(mCurToken - 5); + if (prev_5_token->IsSeparator() && (prev_5_token->GetSepId() == SEP_Lbrace)) + lbrace_ok = true; + } + + if ( (prev_4_token->mLineBegin || lbrace_ok) && (prev_4_token->IsIdentifier() || prev_4_token->IsKeyword()) && (prev_2_token->IsIdentifier() || prev_2_token->IsKeyword()) && (prev_3_token->IsSeparator() && (prev_3_token->GetSepId() == SEP_Lparen)) ){ diff --git a/src/MapleFE/typescript/stmt.spec b/src/MapleFE/typescript/stmt.spec index 9e9f222ae6d466042297b41552d36166d03e2ffd..cd2e72c5a85f6cf91d1befd4716a742ded540c86 100644 --- a/src/MapleFE/typescript/stmt.spec +++ b/src/MapleFE/typescript/stmt.spec @@ -390,7 +390,7 @@ rule MemberExpression : ONEOF( MemberExpression + '[' + KeyOf + ']', MemberExpression + '!', MemberExpression + '.' + JSIdentifier + "as" + "const", - '<' + Type + '>' + PrimaryExpression, + '<' + Type + '>' + MemberExpression, PrimaryExpression + "as" + "const", MemberExpression + '.' + KeywordPropName) attr.action.%1 : AddAsType(%1, %2)