| /** |
| * Takes a token stream from the lexer, and parses it into an abstract syntax tree. |
| * |
| * Specification: $(LINK2 https://dlang.org/spec/grammar.html, D Grammar) |
| * |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved |
| * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/parse.d, _parse.d) |
| * Documentation: https://dlang.org/phobos/dmd_parse.html |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/parse.d |
| */ |
| |
| module dmd.parse; |
| |
| import core.stdc.stdio; |
| import core.stdc.string; |
| import dmd.astenums; |
| import dmd.globals; |
| import dmd.id; |
| import dmd.identifier; |
| import dmd.lexer; |
| import dmd.errors; |
| import dmd.root.filename; |
| import dmd.common.outbuffer; |
| import dmd.root.rmem; |
| import dmd.root.rootobject; |
| import dmd.root.string; |
| import dmd.tokens; |
| |
| /*********************************************************** |
| */ |
| class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer |
| { |
| AST.ModuleDeclaration* md; |
| |
| protected |
| { |
| AST.Module mod; |
| LINK linkage; |
| Loc linkLoc; |
| CPPMANGLE cppmangle; |
| Loc endloc; // set to location of last right curly |
| int inBrackets; // inside [] of array index or slice |
| Loc lookingForElse; // location of lonely if looking for an else |
| } |
| |
| /********************* |
| * Use this constructor for string mixins. |
| * Input: |
| * loc location in source file of mixin |
| */ |
| extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, bool doDocComment) |
| { |
| super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false); |
| |
| //printf("Parser::Parser()\n"); |
| scanloc = loc; |
| |
| if (!writeMixin(input, scanloc) && loc.filename) |
| { |
| /* Create a pseudo-filename for the mixin string, as it may not even exist |
| * in the source file. |
| */ |
| char* filename = cast(char*)mem.xmalloc(strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1); |
| sprintf(filename, "%s-mixin-%d", loc.filename, cast(int)loc.linnum); |
| scanloc.filename = filename; |
| } |
| |
| mod = _module; |
| linkage = LINK.d; |
| //nextToken(); // start up the scanner |
| } |
| |
| extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment) |
| { |
| super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false); |
| |
| //printf("Parser::Parser()\n"); |
| mod = _module; |
| linkage = LINK.d; |
| //nextToken(); // start up the scanner |
| } |
| |
| /++ |
| + Parse a module, i.e. the optional `module x.y.z` declaration and all declarations |
| + found in the current file. |
| + |
| + Returns: the list of declarations or an empty list in case of malformed declarations, |
| + the module declaration will be stored as `this.md` if found |
| +/ |
| AST.Dsymbols* parseModule() |
| { |
| if (!parseModuleDeclaration()) |
| return errorReturn(); |
| |
| return parseModuleContent(); |
| } |
| |
| /++ |
| + Parse the optional module declaration |
| + |
| + Returns: false if a malformed module declaration was found |
| +/ |
| final bool parseModuleDeclaration() |
| { |
| const comment = token.blockComment; |
| bool isdeprecated = false; |
| AST.Expression msg = null; |
| |
| // Parse optional module attributes |
| parseModuleAttributes(msg, isdeprecated); |
| |
| // ModuleDeclaration leads off |
| if (token.value == TOK.module_) |
| { |
| const loc = token.loc; |
| nextToken(); |
| |
| /* parse ModuleFullyQualifiedName |
| * https://dlang.org/spec/module.html#ModuleFullyQualifiedName |
| */ |
| |
| if (token.value != TOK.identifier) |
| { |
| error("identifier expected following `module`"); |
| return false; |
| } |
| |
| Identifier[] a; |
| Identifier id = token.ident; |
| |
| while (nextToken() == TOK.dot) |
| { |
| a ~= id; |
| nextToken(); |
| if (token.value != TOK.identifier) |
| { |
| error("identifier expected following `package`"); |
| return false; |
| } |
| id = token.ident; |
| } |
| |
| md = new AST.ModuleDeclaration(loc, a, id, msg, isdeprecated); |
| |
| if (token.value != TOK.semicolon) |
| error("`;` expected following module declaration instead of `%s`", token.toChars()); |
| nextToken(); |
| addComment(mod, comment); |
| } |
| return true; |
| } |
| |
| /++ |
| + Parse the content of a module, i.e. all declarations found until the end of file. |
| + |
| + Returns: the list of declarations or an empty list in case of malformed declarations |
| +/ |
| final AST.Dsymbols* parseModuleContent() |
| { |
| AST.Dsymbol lastDecl = mod; |
| AST.Dsymbols* decldefs = parseDeclDefs(0, &lastDecl); |
| |
| if (token.value == TOK.rightCurly) |
| { |
| error(token.loc, "unmatched closing brace"); |
| return errorReturn(); |
| } |
| |
| if (token.value != TOK.endOfFile) |
| { |
| error(token.loc, "unrecognized declaration"); |
| return errorReturn(); |
| } |
| return decldefs; |
| } |
| |
| /++ |
| + Skips to the end of the current declaration - denoted by either `;` or EOF |
| + |
| + Returns: An empty list of Dsymbols |
| +/ |
| private AST.Dsymbols* errorReturn() |
| { |
| while (token.value != TOK.semicolon && token.value != TOK.endOfFile) |
| nextToken(); |
| nextToken(); |
| return new AST.Dsymbols(); |
| } |
| |
| /********************************** |
| * Parse the ModuleAttributes preceding a module declaration. |
| * ModuleDeclaration: |
| * ModuleAttributes(opt) module ModuleFullyQualifiedName ; |
| * https://dlang.org/spec/module.html#ModuleAttributes |
| * Params: |
| * msg = set to the AssignExpression from DeprecatedAttribute https://dlang.org/spec/module.html#DeprecatedAttribute |
| * isdeprecated = set to true if a DeprecatedAttribute is seen |
| */ |
| private |
| void parseModuleAttributes(out AST.Expression msg, out bool isdeprecated) |
| { |
| Token* tk; |
| if (!(skipAttributes(&token, &tk) && tk.value == TOK.module_)) |
| return; // no module attributes |
| |
| AST.Expressions* udas = null; |
| while (token.value != TOK.module_) |
| { |
| switch (token.value) |
| { |
| case TOK.deprecated_: |
| { |
| // deprecated (...) module ... |
| if (isdeprecated) |
| error("there is only one deprecation attribute allowed for module declaration"); |
| isdeprecated = true; |
| nextToken(); |
| if (token.value == TOK.leftParenthesis) |
| { |
| check(TOK.leftParenthesis); |
| msg = parseAssignExp(); |
| check(TOK.rightParenthesis); |
| } |
| break; |
| } |
| case TOK.at: |
| { |
| AST.Expressions* exps = null; |
| const stc = parseAttribute(exps); |
| if (stc & atAttrGroup) |
| { |
| error("`@%s` attribute for module declaration is not supported", token.toChars()); |
| } |
| else |
| { |
| udas = AST.UserAttributeDeclaration.concat(udas, exps); |
| } |
| if (stc) |
| nextToken(); |
| break; |
| } |
| default: |
| { |
| error("`module` expected instead of `%s`", token.toChars()); |
| nextToken(); |
| break; |
| } |
| } |
| } |
| |
| if (udas) |
| { |
| auto a = new AST.Dsymbols(); |
| auto udad = new AST.UserAttributeDeclaration(udas, a); |
| mod.userAttribDecl = udad; |
| } |
| } |
| |
| final: |
| |
| /** |
| * Parses a `deprecated` declaration |
| * |
| * Params: |
| * msg = Deprecated message, if any. |
| * Used to support overriding a deprecated storage class with |
| * a deprecated declaration with a message, but to error |
| * if both declaration have a message. |
| * |
| * Returns: |
| * Whether the deprecated declaration has a message |
| */ |
| private bool parseDeprecatedAttribute(ref AST.Expression msg) |
| { |
| if (peekNext() != TOK.leftParenthesis) |
| return false; |
| |
| nextToken(); |
| check(TOK.leftParenthesis); |
| AST.Expression e = parseAssignExp(); |
| check(TOK.rightParenthesis); |
| if (msg) |
| { |
| error("conflicting storage class `deprecated(%s)` and `deprecated(%s)`", msg.toChars(), e.toChars()); |
| } |
| msg = e; |
| return true; |
| } |
| |
| /************************************ |
| * Parse declarations and definitions |
| * Params: |
| * once = !=0 means parse exactly one decl or def |
| * pLastDecl = set to last decl or def parsed |
| * pAttrs = keep track of attributes |
| * Returns: |
| * array of declared symbols |
| */ |
| AST.Dsymbols* parseDeclDefs(int once, AST.Dsymbol* pLastDecl = null, PrefixAttributes!AST* pAttrs = null) |
| { |
| AST.Dsymbol lastDecl = null; // used to link unittest to its previous declaration |
| if (!pLastDecl) |
| pLastDecl = &lastDecl; |
| |
| const linksave = linkage; // save global state |
| |
| //printf("Parser::parseDeclDefs()\n"); |
| auto decldefs = new AST.Dsymbols(); |
| do |
| { |
| // parse result |
| AST.Dsymbol s = null; |
| AST.Dsymbols* a = null; |
| |
| PrefixAttributes!AST attrs; |
| if (!once || !pAttrs) |
| { |
| pAttrs = &attrs; |
| pAttrs.comment = token.blockComment.ptr; |
| } |
| AST.Visibility.Kind prot; |
| StorageClass stc; |
| AST.Condition condition; |
| |
| linkage = linksave; |
| |
| Loc startloc; |
| |
| switch (token.value) |
| { |
| case TOK.enum_: |
| { |
| /* Determine if this is a manifest constant declaration, |
| * or a conventional enum. |
| */ |
| const tv = peekNext(); |
| if (tv == TOK.leftCurly || tv == TOK.colon) |
| s = parseEnum(); |
| else if (tv != TOK.identifier) |
| goto Ldeclaration; |
| else |
| { |
| const nextv = peekNext2(); |
| if (nextv == TOK.leftCurly || nextv == TOK.colon || nextv == TOK.semicolon) |
| s = parseEnum(); |
| else |
| goto Ldeclaration; |
| } |
| break; |
| } |
| case TOK.import_: |
| a = parseImport(); |
| // keep pLastDecl |
| break; |
| |
| case TOK.template_: |
| s = cast(AST.Dsymbol)parseTemplateDeclaration(); |
| break; |
| |
| case TOK.mixin_: |
| { |
| const loc = token.loc; |
| switch (peekNext()) |
| { |
| case TOK.leftParenthesis: |
| { |
| // MixinType |
| if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null)) |
| goto Ldeclaration; |
| // mixin(string) |
| nextToken(); |
| auto exps = parseArguments(); |
| check(TOK.semicolon); |
| s = new AST.CompileDeclaration(loc, exps); |
| break; |
| } |
| case TOK.template_: |
| // mixin template |
| nextToken(); |
| s = cast(AST.Dsymbol)parseTemplateDeclaration(true); |
| break; |
| |
| default: |
| s = parseMixin(); |
| break; |
| } |
| break; |
| } |
| case TOK.wchar_: |
| case TOK.dchar_: |
| case TOK.bool_: |
| case TOK.char_: |
| case TOK.int8: |
| case TOK.uns8: |
| case TOK.int16: |
| case TOK.uns16: |
| case TOK.int32: |
| case TOK.uns32: |
| case TOK.int64: |
| case TOK.uns64: |
| case TOK.int128: |
| case TOK.uns128: |
| case TOK.float32: |
| case TOK.float64: |
| case TOK.float80: |
| case TOK.imaginary32: |
| case TOK.imaginary64: |
| case TOK.imaginary80: |
| case TOK.complex32: |
| case TOK.complex64: |
| case TOK.complex80: |
| case TOK.void_: |
| case TOK.alias_: |
| case TOK.identifier: |
| case TOK.super_: |
| case TOK.typeof_: |
| case TOK.dot: |
| case TOK.vector: |
| case TOK.struct_: |
| case TOK.union_: |
| case TOK.class_: |
| case TOK.interface_: |
| case TOK.traits: |
| Ldeclaration: |
| a = parseDeclarations(false, pAttrs, pAttrs.comment); |
| if (a && a.dim) |
| *pLastDecl = (*a)[a.dim - 1]; |
| break; |
| |
| case TOK.this_: |
| if (peekNext() == TOK.dot) |
| goto Ldeclaration; |
| s = parseCtor(pAttrs); |
| break; |
| |
| case TOK.tilde: |
| s = parseDtor(pAttrs); |
| break; |
| |
| case TOK.invariant_: |
| const tv = peekNext(); |
| if (tv == TOK.leftParenthesis || tv == TOK.leftCurly) |
| { |
| // invariant { statements... } |
| // invariant() { statements... } |
| // invariant (expression); |
| s = parseInvariant(pAttrs); |
| break; |
| } |
| error("invariant body expected, not `%s`", token.toChars()); |
| goto Lerror; |
| |
| case TOK.unittest_: |
| /** |
| * Ignore unittests in non-root modules. |
| * |
| * This mainly means that unittests *inside templates* are only |
| * ever instantiated if the module lexically declaring the |
| * template is one of the root modules. |
| * |
| * E.g., compiling some project with `-unittest` does NOT |
| * compile and later run any unittests in instantiations of |
| * templates declared in other libraries. |
| * |
| * Declaring unittests *inside* templates is considered an anti- |
| * pattern. In almost all cases, the unittests don't depend on |
| * the template parameters, but instantiate the template with |
| * fixed arguments (e.g., Nullable!T unittests instantiating |
| * Nullable!int), so compiling and running identical tests for |
| * each template instantiation is hardly desirable. |
| * But adding a unittest right below some function being tested |
| * is arguably good for locality, so unittests end up inside |
| * templates. |
| * To make sure a template's unittests are run, it should be |
| * instantiated in the same module, e.g., some module-level |
| * unittest. |
| * |
| * Another reason for ignoring unittests in templates from non- |
| * root modules is for template codegen culling via |
| * TemplateInstance.needsCodegen(). If the compiler decides not |
| * to emit some Nullable!bool because there's an existing |
| * instantiation in some non-root module, it has no idea whether |
| * that module was compiled with -unittest too, and so whether |
| * Nullable!int (instantiated in some unittest inside the |
| * Nullable template) can be culled too. By ignoring unittests |
| * in non-root modules, the compiler won't consider any |
| * template instantiations in these unittests as candidates for |
| * further codegen culling. |
| */ |
| if (mod.isRoot() && (global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput)) |
| { |
| s = parseUnitTest(pAttrs); |
| if (*pLastDecl) |
| (*pLastDecl).ddocUnittest = cast(AST.UnitTestDeclaration)s; |
| } |
| else |
| { |
| // Skip over unittest block by counting { } |
| Loc loc = token.loc; |
| int braces = 0; |
| while (1) |
| { |
| nextToken(); |
| switch (token.value) |
| { |
| case TOK.leftCurly: |
| ++braces; |
| continue; |
| |
| case TOK.rightCurly: |
| if (--braces) |
| continue; |
| nextToken(); |
| break; |
| |
| case TOK.endOfFile: |
| /* { */ |
| error(loc, "closing `}` of unittest not found before end of file"); |
| goto Lerror; |
| |
| default: |
| continue; |
| } |
| break; |
| } |
| // Workaround 14894. Add an empty unittest declaration to keep |
| // the number of symbols in this scope independent of -unittest. |
| s = new AST.UnitTestDeclaration(loc, token.loc, STC.undefined_, null); |
| } |
| break; |
| |
| case TOK.new_: |
| s = parseNew(pAttrs); |
| break; |
| |
| case TOK.colon: |
| case TOK.leftCurly: |
| error("declaration expected, not `%s`", token.toChars()); |
| goto Lerror; |
| |
| case TOK.rightCurly: |
| case TOK.endOfFile: |
| if (once) |
| error("declaration expected, not `%s`", token.toChars()); |
| return decldefs; |
| |
| case TOK.static_: |
| { |
| const next = peekNext(); |
| if (next == TOK.this_) |
| s = parseStaticCtor(pAttrs); |
| else if (next == TOK.tilde) |
| s = parseStaticDtor(pAttrs); |
| else if (next == TOK.assert_) |
| s = parseStaticAssert(); |
| else if (next == TOK.if_) |
| { |
| const Loc loc = token.loc; |
| condition = parseStaticIfCondition(); |
| AST.Dsymbols* athen; |
| if (token.value == TOK.colon) |
| athen = parseBlock(pLastDecl); |
| else |
| { |
| const lookingForElseSave = lookingForElse; |
| lookingForElse = token.loc; |
| athen = parseBlock(pLastDecl); |
| lookingForElse = lookingForElseSave; |
| } |
| AST.Dsymbols* aelse = null; |
| if (token.value == TOK.else_) |
| { |
| const elseloc = token.loc; |
| nextToken(); |
| aelse = parseBlock(pLastDecl); |
| checkDanglingElse(elseloc); |
| } |
| s = new AST.StaticIfDeclaration(loc, condition, athen, aelse); |
| } |
| else if (next == TOK.import_) |
| { |
| a = parseImport(); |
| // keep pLastDecl |
| } |
| else if (next == TOK.foreach_ || next == TOK.foreach_reverse_) |
| { |
| s = parseForeach!(AST.StaticForeachDeclaration)(token.loc, pLastDecl); |
| } |
| else |
| { |
| stc = STC.static_; |
| goto Lstc; |
| } |
| break; |
| } |
| case TOK.const_: |
| if (peekNext() == TOK.leftParenthesis) |
| goto Ldeclaration; |
| stc = STC.const_; |
| goto Lstc; |
| |
| case TOK.immutable_: |
| if (peekNext() == TOK.leftParenthesis) |
| goto Ldeclaration; |
| stc = STC.immutable_; |
| goto Lstc; |
| |
| case TOK.shared_: |
| { |
| const next = peekNext(); |
| if (next == TOK.leftParenthesis) |
| goto Ldeclaration; |
| if (next == TOK.static_) |
| { |
| TOK next2 = peekNext2(); |
| if (next2 == TOK.this_) |
| { |
| s = parseSharedStaticCtor(pAttrs); |
| break; |
| } |
| if (next2 == TOK.tilde) |
| { |
| s = parseSharedStaticDtor(pAttrs); |
| break; |
| } |
| } |
| stc = STC.shared_; |
| goto Lstc; |
| } |
| case TOK.inout_: |
| if (peekNext() == TOK.leftParenthesis) |
| goto Ldeclaration; |
| stc = STC.wild; |
| goto Lstc; |
| |
| case TOK.final_: |
| stc = STC.final_; |
| goto Lstc; |
| |
| case TOK.auto_: |
| stc = STC.auto_; |
| goto Lstc; |
| |
| case TOK.scope_: |
| stc = STC.scope_; |
| goto Lstc; |
| |
| case TOK.override_: |
| stc = STC.override_; |
| goto Lstc; |
| |
| case TOK.abstract_: |
| stc = STC.abstract_; |
| goto Lstc; |
| |
| case TOK.synchronized_: |
| stc = STC.synchronized_; |
| goto Lstc; |
| |
| case TOK.nothrow_: |
| stc = STC.nothrow_; |
| goto Lstc; |
| |
| case TOK.pure_: |
| stc = STC.pure_; |
| goto Lstc; |
| |
| case TOK.ref_: |
| stc = STC.ref_; |
| goto Lstc; |
| |
| case TOK.gshared: |
| stc = STC.gshared; |
| goto Lstc; |
| |
| case TOK.at: |
| { |
| AST.Expressions* exps = null; |
| stc = parseAttribute(exps); |
| if (stc) |
| goto Lstc; // it's a predefined attribute |
| // no redundant/conflicting check for UDAs |
| pAttrs.udas = AST.UserAttributeDeclaration.concat(pAttrs.udas, exps); |
| goto Lautodecl; |
| } |
| Lstc: |
| pAttrs.storageClass = appendStorageClass(pAttrs.storageClass, stc); |
| nextToken(); |
| |
| Lautodecl: |
| |
| /* Look for auto initializers: |
| * storage_class identifier = initializer; |
| * storage_class identifier(...) = initializer; |
| */ |
| if (token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign)) |
| { |
| a = parseAutoDeclarations(getStorageClass!AST(pAttrs), pAttrs.comment); |
| if (a && a.dim) |
| *pLastDecl = (*a)[a.dim - 1]; |
| if (pAttrs.udas) |
| { |
| s = new AST.UserAttributeDeclaration(pAttrs.udas, a); |
| pAttrs.udas = null; |
| } |
| break; |
| } |
| |
| /* Look for return type inference for template functions. |
| */ |
| Token* tk; |
| if (token.value == TOK.identifier && skipParens(peek(&token), &tk) && skipAttributes(tk, &tk) && |
| (tk.value == TOK.leftParenthesis || tk.value == TOK.leftCurly || tk.value == TOK.in_ || |
| tk.value == TOK.out_ || tk.value == TOK.do_ || tk.value == TOK.goesTo || |
| tk.value == TOK.identifier && tk.ident == Id._body)) |
| { |
| // @@@DEPRECATED_2.117@@@ |
| // https://github.com/dlang/DIPs/blob/1f5959abe482b1f9094f6484a7d0a3ade77fc2fc/DIPs/accepted/DIP1003.md |
| // Deprecated in 2.097 - Can be removed from 2.117 |
| // The deprecation period is longer than usual as `body` |
| // was quite widely used. |
| if (tk.value == TOK.identifier && tk.ident == Id._body) |
| deprecation("usage of the `body` keyword is deprecated. Use `do` instead."); |
| |
| a = parseDeclarations(true, pAttrs, pAttrs.comment); |
| if (a && a.dim) |
| *pLastDecl = (*a)[a.dim - 1]; |
| if (pAttrs.udas) |
| { |
| s = new AST.UserAttributeDeclaration(pAttrs.udas, a); |
| pAttrs.udas = null; |
| } |
| break; |
| } |
| |
| a = parseBlock(pLastDecl, pAttrs); |
| auto stc2 = getStorageClass!AST(pAttrs); |
| if (stc2 != STC.undefined_) |
| { |
| s = new AST.StorageClassDeclaration(stc2, a); |
| } |
| if (pAttrs.udas) |
| { |
| if (s) |
| { |
| a = new AST.Dsymbols(); |
| a.push(s); |
| } |
| s = new AST.UserAttributeDeclaration(pAttrs.udas, a); |
| pAttrs.udas = null; |
| } |
| break; |
| |
| case TOK.deprecated_: |
| { |
| stc |= STC.deprecated_; |
| if (!parseDeprecatedAttribute(pAttrs.depmsg)) |
| goto Lstc; |
| |
| a = parseBlock(pLastDecl, pAttrs); |
| s = new AST.DeprecatedDeclaration(pAttrs.depmsg, a); |
| pAttrs.depmsg = null; |
| break; |
| } |
| case TOK.leftBracket: |
| { |
| if (peekNext() == TOK.rightBracket) |
| error("empty attribute list is not allowed"); |
| error("use `@(attributes)` instead of `[attributes]`"); |
| AST.Expressions* exps = parseArguments(); |
| // no redundant/conflicting check for UDAs |
| |
| pAttrs.udas = AST.UserAttributeDeclaration.concat(pAttrs.udas, exps); |
| a = parseBlock(pLastDecl, pAttrs); |
| if (pAttrs.udas) |
| { |
| s = new AST.UserAttributeDeclaration(pAttrs.udas, a); |
| pAttrs.udas = null; |
| } |
| break; |
| } |
| case TOK.extern_: |
| { |
| if (peekNext() != TOK.leftParenthesis) |
| { |
| stc = STC.extern_; |
| goto Lstc; |
| } |
| |
| const linkLoc = token.loc; |
| auto res = parseLinkage(); |
| if (pAttrs.link != LINK.default_) |
| { |
| if (pAttrs.link != res.link) |
| { |
| error("conflicting linkage `extern (%s)` and `extern (%s)`", AST.linkageToChars(pAttrs.link), AST.linkageToChars(res.link)); |
| } |
| else if (res.idents || res.identExps || res.cppmangle != CPPMANGLE.def) |
| { |
| // Allow: |
| // extern(C++, foo) extern(C++, bar) void foo(); |
| // to be equivalent with: |
| // extern(C++, foo.bar) void foo(); |
| // Allow also: |
| // extern(C++, "ns") extern(C++, class) struct test {} |
| // extern(C++, class) extern(C++, "ns") struct test {} |
| } |
| else |
| error("redundant linkage `extern (%s)`", AST.linkageToChars(pAttrs.link)); |
| } |
| pAttrs.link = res.link; |
| this.linkage = res.link; |
| this.linkLoc = linkLoc; |
| a = parseBlock(pLastDecl, pAttrs); |
| if (res.idents) |
| { |
| assert(res.link == LINK.cpp); |
| assert(res.idents.dim); |
| for (size_t i = res.idents.dim; i;) |
| { |
| Identifier id = (*res.idents)[--i]; |
| if (s) |
| { |
| a = new AST.Dsymbols(); |
| a.push(s); |
| } |
| s = new AST.Nspace(linkLoc, id, null, a); |
| } |
| pAttrs.link = LINK.default_; |
| } |
| else if (res.identExps) |
| { |
| assert(res.link == LINK.cpp); |
| assert(res.identExps.dim); |
| for (size_t i = res.identExps.dim; i;) |
| { |
| AST.Expression exp = (*res.identExps)[--i]; |
| if (s) |
| { |
| a = new AST.Dsymbols(); |
| a.push(s); |
| } |
| s = new AST.CPPNamespaceDeclaration(linkLoc, exp, a); |
| } |
| pAttrs.link = LINK.default_; |
| } |
| else if (res.cppmangle != CPPMANGLE.def) |
| { |
| assert(res.link == LINK.cpp); |
| s = new AST.CPPMangleDeclaration(linkLoc, res.cppmangle, a); |
| } |
| else if (pAttrs.link != LINK.default_) |
| { |
| s = new AST.LinkDeclaration(linkLoc, pAttrs.link, a); |
| pAttrs.link = LINK.default_; |
| } |
| break; |
| } |
| |
| case TOK.private_: |
| prot = AST.Visibility.Kind.private_; |
| goto Lprot; |
| |
| case TOK.package_: |
| prot = AST.Visibility.Kind.package_; |
| goto Lprot; |
| |
| case TOK.protected_: |
| prot = AST.Visibility.Kind.protected_; |
| goto Lprot; |
| |
| case TOK.public_: |
| prot = AST.Visibility.Kind.public_; |
| goto Lprot; |
| |
| case TOK.export_: |
| prot = AST.Visibility.Kind.export_; |
| goto Lprot; |
| Lprot: |
| { |
| if (pAttrs.visibility.kind != AST.Visibility.Kind.undefined) |
| { |
| if (pAttrs.visibility.kind != prot) |
| error("conflicting visibility attribute `%s` and `%s`", AST.visibilityToChars(pAttrs.visibility.kind), AST.visibilityToChars(prot)); |
| else |
| error("redundant visibility attribute `%s`", AST.visibilityToChars(prot)); |
| } |
| pAttrs.visibility.kind = prot; |
| const attrloc = token.loc; |
| |
| nextToken(); |
| |
| // optional qualified package identifier to bind |
| // visibility to |
| Identifier[] pkg_prot_idents; |
| if (pAttrs.visibility.kind == AST.Visibility.Kind.package_ && token.value == TOK.leftParenthesis) |
| { |
| pkg_prot_idents = parseQualifiedIdentifier("protection package"); |
| if (pkg_prot_idents) |
| check(TOK.rightParenthesis); |
| else |
| { |
| while (token.value != TOK.semicolon && token.value != TOK.endOfFile) |
| nextToken(); |
| nextToken(); |
| break; |
| } |
| } |
| |
| a = parseBlock(pLastDecl, pAttrs); |
| if (pAttrs.visibility.kind != AST.Visibility.Kind.undefined) |
| { |
| if (pAttrs.visibility.kind == AST.Visibility.Kind.package_ && pkg_prot_idents) |
| s = new AST.VisibilityDeclaration(attrloc, pkg_prot_idents, a); |
| else |
| s = new AST.VisibilityDeclaration(attrloc, pAttrs.visibility, a); |
| |
| pAttrs.visibility = AST.Visibility(AST.Visibility.Kind.undefined); |
| } |
| break; |
| } |
| case TOK.align_: |
| { |
| const attrLoc = token.loc; |
| |
| nextToken(); |
| |
| AST.Expression e = null; // default |
| if (token.value == TOK.leftParenthesis) |
| { |
| nextToken(); |
| e = parseAssignExp(); |
| check(TOK.rightParenthesis); |
| } |
| |
| if (pAttrs.setAlignment) |
| { |
| if (e) |
| error("redundant alignment attribute `align(%s)`", e.toChars()); |
| else |
| error("redundant alignment attribute `align`"); |
| } |
| |
| pAttrs.setAlignment = true; |
| pAttrs.ealign = e; |
| a = parseBlock(pLastDecl, pAttrs); |
| if (pAttrs.setAlignment) |
| { |
| s = new AST.AlignDeclaration(attrLoc, pAttrs.ealign, a); |
| pAttrs.setAlignment = false; |
| pAttrs.ealign = null; |
| } |
| break; |
| } |
| case TOK.pragma_: |
| { |
| AST.Expressions* args = null; |
| const loc = token.loc; |
| |
| nextToken(); |
| check(TOK.leftParenthesis); |
| if (token.value != TOK.identifier) |
| { |
| error("`pragma(identifier)` expected"); |
| goto Lerror; |
| } |
| Identifier ident = token.ident; |
| nextToken(); |
| if (token.value == TOK.comma && peekNext() != TOK.rightParenthesis) |
| args = parseArguments(); // pragma(identifier, args...) |
| else |
| check(TOK.rightParenthesis); // pragma(identifier) |
| |
| AST.Dsymbols* a2 = null; |
| if (token.value == TOK.semicolon) |
| { |
| /* https://issues.dlang.org/show_bug.cgi?id=2354 |
| * Accept single semicolon as an empty |
| * DeclarationBlock following attribute. |
| * |
| * Attribute DeclarationBlock |
| * Pragma DeclDef |
| * ; |
| */ |
| nextToken(); |
| } |
| else |
| a2 = parseBlock(pLastDecl); |
| s = new AST.PragmaDeclaration(loc, ident, args, a2); |
| break; |
| } |
| case TOK.debug_: |
| startloc = token.loc; |
| nextToken(); |
| if (token.value == TOK.assign) |
| { |
| s = parseDebugSpecification(); |
| break; |
| } |
| condition = parseDebugCondition(); |
| goto Lcondition; |
| |
| case TOK.version_: |
| startloc = token.loc; |
| nextToken(); |
| if (token.value == TOK.assign) |
| { |
| s = parseVersionSpecification(); |
| break; |
| } |
| condition = parseVersionCondition(); |
| goto Lcondition; |
| |
| Lcondition: |
| { |
| AST.Dsymbols* athen; |
| if (token.value == TOK.colon) |
| athen = parseBlock(pLastDecl); |
| else |
| { |
| const lookingForElseSave = lookingForElse; |
| lookingForElse = token.loc; |
| athen = parseBlock(pLastDecl); |
| lookingForElse = lookingForElseSave; |
| } |
| AST.Dsymbols* aelse = null; |
| if (token.value == TOK.else_) |
| { |
| const elseloc = token.loc; |
| nextToken(); |
| aelse = parseBlock(pLastDecl); |
| checkDanglingElse(elseloc); |
| } |
| s = new AST.ConditionalDeclaration(startloc, condition, athen, aelse); |
| break; |
| } |
| case TOK.semicolon: |
| // empty declaration |
| //error("empty declaration"); |
| nextToken(); |
| continue; |
| |
| default: |
| error("declaration expected, not `%s`", token.toChars()); |
| Lerror: |
| while (token.value != TOK.semicolon && token.value != TOK.endOfFile) |
| nextToken(); |
| nextToken(); |
| s = null; |
| continue; |
| } |
| |
| if (s) |
| { |
| if (!s.isAttribDeclaration()) |
| *pLastDecl = s; |
| decldefs.push(s); |
| addComment(s, pAttrs.comment); |
| } |
| else if (a && a.dim) |
| { |
| decldefs.append(a); |
| } |
| } |
| while (!once); |
| |
| linkage = linksave; |
| |
| return decldefs; |
| } |
| |
| /***************************************** |
| * Parse auto declarations of the form: |
| * storageClass ident = init, ident = init, ... ; |
| * and return the array of them. |
| * Starts with token on the first ident. |
| * Ends with scanner past closing ';' |
| */ |
| private AST.Dsymbols* parseAutoDeclarations(StorageClass storageClass, const(char)* comment) |
| { |
| //printf("parseAutoDeclarations\n"); |
| auto a = new AST.Dsymbols(); |
| |
| while (1) |
| { |
| const loc = token.loc; |
| Identifier ident = token.ident; |
| nextToken(); // skip over ident |
| |
| AST.TemplateParameters* tpl = null; |
| if (token.value == TOK.leftParenthesis) |
| tpl = parseTemplateParameterList(); |
| |
| check(TOK.assign); // skip over '=' |
| AST.Initializer _init = parseInitializer(); |
| auto v = new AST.VarDeclaration(loc, null, ident, _init, storageClass); |
| |
| AST.Dsymbol s = v; |
| if (tpl) |
| { |
| auto a2 = new AST.Dsymbols(); |
| a2.push(v); |
| auto tempdecl = new AST.TemplateDeclaration(loc, ident, tpl, null, a2, 0); |
| s = tempdecl; |
| } |
| a.push(s); |
| switch (token.value) |
| { |
| case TOK.semicolon: |
| nextToken(); |
| addComment(s, comment); |
| break; |
| |
| case TOK.comma: |
| nextToken(); |
| if (!(token.value == TOK.identifier && hasOptionalParensThen(peek(&token), TOK.assign))) |
| { |
| error("identifier expected following comma"); |
| break; |
| } |
| addComment(s, comment); |
| continue; |
| |
| default: |
| error("semicolon expected following auto declaration, not `%s`", token.toChars()); |
| break; |
| } |
| break; |
| } |
| return a; |
| } |
| |
| /******************************************** |
| * Parse declarations after an align, visibility, or extern decl. |
| */ |
| private AST.Dsymbols* parseBlock(AST.Dsymbol* pLastDecl, PrefixAttributes!AST* pAttrs = null) |
| { |
| AST.Dsymbols* a = null; |
| |
| //printf("parseBlock()\n"); |
| switch (token.value) |
| { |
| case TOK.semicolon: |
| error("declaration expected following attribute, not `;`"); |
| nextToken(); |
| break; |
| |
| case TOK.endOfFile: |
| error("declaration expected following attribute, not end of file"); |
| break; |
| |
| case TOK.leftCurly: |
| { |
| const lookingForElseSave = lookingForElse; |
| lookingForElse = Loc(); |
| |
| nextToken(); |
| a = parseDeclDefs(0, pLastDecl); |
| if (token.value != TOK.rightCurly) |
| { |
| /* { */ |
| error("matching `}` expected, not `%s`", token.toChars()); |
| } |
| else |
| nextToken(); |
| lookingForElse = lookingForElseSave; |
| break; |
| } |
| case TOK.colon: |
| nextToken(); |
| a = parseDeclDefs(0, pLastDecl); // grab declarations up to closing curly bracket |
| break; |
| |
| default: |
| a = parseDeclDefs(1, pLastDecl, pAttrs); |
| break; |
| } |
| return a; |
| } |
| |
| /** |
| * Provide an error message if `added` contains storage classes which are |
| * redundant with those in `orig`; otherwise, return the combination. |
| * |
| * Params: |
| * orig = The already applied storage class. |
| * added = The new storage class to add to `orig`. |
| * |
| * Returns: |
| * The combination of both storage classes (`orig | added`). |
| */ |
| private StorageClass appendStorageClass(StorageClass orig, StorageClass added) |
| { |
| void checkConflictSTCGroup(bool at = false)(StorageClass group) |
| { |
| if (added & group && orig & group & ((orig & group) - 1)) |
| error( |
| at ? "conflicting attribute `@%s`" |
| : "conflicting attribute `%s`", |
| token.toChars()); |
| } |
| |
| if (orig & added) |
| { |
| OutBuffer buf; |
| AST.stcToBuffer(&buf, added); |
| error("redundant attribute `%s`", buf.peekChars()); |
| return orig | added; |
| } |
| |
| const Redundant = (STC.const_ | STC.scope_ | |
| (global.params.previewIn ? STC.ref_ : 0)); |
| orig |= added; |
| |
| if ((orig & STC.in_) && (added & Redundant)) |
| { |
| if (added & STC.const_) |
| error("attribute `const` is redundant with previously-applied `in`"); |
| else if (global.params.previewIn) |
| { |
| error("attribute `%s` is redundant with previously-applied `in`", |
| (orig & STC.scope_) ? "scope".ptr : "ref".ptr); |
| } |
| else |
| error("attribute `scope` cannot be applied with `in`, use `-preview=in` instead"); |
| return orig; |
| } |
| |
| if ((added & STC.in_) && (orig & Redundant)) |
| { |
| if (orig & STC.const_) |
| error("attribute `in` cannot be added after `const`: remove `const`"); |
| else if (global.params.previewIn) |
| { |
| // Windows `printf` does not support `%1$s` |
| const(char*) stc_str = (orig & STC.scope_) ? "scope".ptr : "ref".ptr; |
| error("attribute `in` cannot be added after `%s`: remove `%s`", |
| stc_str, stc_str); |
| } |
| else |
| error("attribute `in` cannot be added after `scope`: remove `scope` and use `-preview=in`"); |
| return orig; |
| } |
| |
| checkConflictSTCGroup(STC.const_ | STC.immutable_ | STC.manifest); |
| checkConflictSTCGroup(STC.gshared | STC.shared_); |
| checkConflictSTCGroup!true(STC.safeGroup); |
| |
| return orig; |
| } |
| |
| /*********************************************** |
| * Parse attribute(s), lexer is on '@'. |
| * |
| * Attributes can be builtin (e.g. `@safe`, `@nogc`, etc...), |
| * or be user-defined (UDAs). In the former case, we return the storage |
| * class via the return value, while in thelater case we return `0` |
| * and set `pudas`. |
| * |
| * Params: |
| * pudas = An array of UDAs to append to |
| * |
| * Returns: |
| * If the attribute is builtin, the return value will be non-zero. |
| * Otherwise, 0 is returned, and `pudas` will be appended to. |
| */ |
| private StorageClass parseAttribute(ref AST.Expressions* udas) |
| { |
| nextToken(); |
| if (token.value == TOK.identifier) |
| { |
| // If we find a builtin attribute, we're done, return immediately. |
| if (StorageClass stc = isBuiltinAtAttribute(token.ident)) |
| return stc; |
| |
| // Allow identifier, template instantiation, or function call |
| // for `@Argument` (single UDA) form. |
| AST.Expression exp = parsePrimaryExp(); |
| if (token.value == TOK.leftParenthesis) |
| { |
| const loc = token.loc; |
| exp = new AST.CallExp(loc, exp, parseArguments()); |
| } |
| |
| if (udas is null) |
| udas = new AST.Expressions(); |
| udas.push(exp); |
| return 0; |
| } |
| |
| if (token.value == TOK.leftParenthesis) |
| { |
| // Multi-UDAs ( `@( ArgumentList )`) form, concatenate with existing |
| if (peekNext() == TOK.rightParenthesis) |
| error("empty attribute list is not allowed"); |
| udas = AST.UserAttributeDeclaration.concat(udas, parseArguments()); |
| return 0; |
| } |
| |
| if (token.isKeyword()) |
| error("`%s` is a keyword, not an `@` attribute", token.toChars()); |
| else |
| error("`@identifier` or `@(ArgumentList)` expected, not `@%s`", token.toChars()); |
| |
| return 0; |
| } |
| |
| /*********************************************** |
| * Parse const/immutable/shared/inout/nothrow/pure postfix |
| */ |
| private StorageClass parsePostfix(StorageClass storageClass, AST.Expressions** pudas) |
| { |
| while (1) |
| { |
| StorageClass stc; |
| switch (token.value) |
| { |
| case TOK.const_: |
| stc = STC.const_; |
| break; |
| |
| case TOK.immutable_: |
| stc = STC.immutable_; |
| break; |
| |
| case TOK.shared_: |
| stc = STC.shared_; |
| break; |
| |
| case TOK.inout_: |
| stc = STC.wild; |
| break; |
| |
| case TOK.nothrow_: |
| stc = STC.nothrow_; |
| break; |
| |
| case TOK.pure_: |
| stc = STC.pure_; |
| break; |
| |
| case TOK.return_: |
| stc = STC.return_; |
| if (peekNext() == TOK.scope_) |
| stc |= STC.returnScope; // recognize `return scope` |
| break; |
| |
| case TOK.scope_: |
| stc = STC.scope_; |
| break; |
| |
| case TOK.at: |
| { |
| AST.Expressions* udas = null; |
| stc = parseAttribute(udas); |
| if (udas) |
| { |
| if (pudas) |
| *pudas = AST.UserAttributeDeclaration.concat(*pudas, udas); |
| else |
| { |
| // Disallow: |
| // void function() @uda fp; |
| // () @uda { return 1; } |
| error("user-defined attributes cannot appear as postfixes"); |
| } |
| continue; |
| } |
| break; |
| } |
| default: |
| return storageClass; |
| } |
| storageClass = appendStorageClass(storageClass, stc); |
| nextToken(); |
| } |
| } |
| |
| private StorageClass parseTypeCtor() |
| { |
| StorageClass storageClass = STC.undefined_; |
| |
| while (1) |
| { |
| if (peekNext() == TOK.leftParenthesis) |
| return storageClass; |
| |
| StorageClass stc; |
| switch (token.value) |
| { |
| case TOK.const_: |
| stc = STC.const_; |
| break; |
| |
| case TOK.immutable_: |
| stc = STC.immutable_; |
| break; |
| |
| case TOK.shared_: |
| stc = STC.shared_; |
| break; |
| |
| case TOK.inout_: |
| stc = STC.wild; |
| break; |
| |
| default: |
| return storageClass; |
| } |
| storageClass = appendStorageClass(storageClass, stc); |
| nextToken(); |
| } |
| } |
| |
| /************************************** |
| * Parse constraint. |
| * Constraint is of the form: |
| * if ( ConstraintExpression ) |
| */ |
| private AST.Expression parseConstraint() |
| { |
| AST.Expression e = null; |
| if (token.value == TOK.if_) |
| { |
| nextToken(); // skip over 'if' |
| check(TOK.leftParenthesis); |
| e = parseExpression(); |
| check(TOK.rightParenthesis); |
| } |
| return e; |
| } |
| |
| /************************************** |
| * Parse a TemplateDeclaration. |
| */ |
| private AST.TemplateDeclaration parseTemplateDeclaration(bool ismixin = false) |
| { |
| AST.TemplateDeclaration tempdecl; |
| Identifier id; |
| AST.TemplateParameters* tpl; |
| AST.Dsymbols* decldefs; |
| AST.Expression constraint = null; |
| const loc = token.loc; |
| |
| nextToken(); |
| if (token.value != TOK.identifier) |
| { |
| error("identifier expected following `template`"); |
| goto Lerr; |
| } |
| id = token.ident; |
| nextToken(); |
| tpl = parseTemplateParameterList(); |
| if (!tpl) |
| goto Lerr; |
| |
| constraint = parseConstraint(); |
| |
| if (token.value != TOK.leftCurly) |
| { |
| error("`{` expected after template parameter list, not `%s`", token.toChars()); |
| goto Lerr; |
| } |
| decldefs = parseBlock(null); |
| |
| tempdecl = new AST.TemplateDeclaration(loc, id, tpl, constraint, decldefs, ismixin); |
| return tempdecl; |
| |
| Lerr: |
| return null; |
| } |
| |
| /****************************************** |
| * Parse template parameter list. |
| * Input: |
| * flag 0: parsing "( list )" |
| * 1: parsing non-empty "list $(RPAREN)" |
| */ |
| private AST.TemplateParameters* parseTemplateParameterList(int flag = 0) |
| { |
| auto tpl = new AST.TemplateParameters(); |
| |
| if (!flag && token.value != TOK.leftParenthesis) |
| { |
| error("parenthesized template parameter list expected following template identifier"); |
| goto Lerr; |
| } |
| nextToken(); |
| |
| // Get array of TemplateParameters |
| if (flag || token.value != TOK.rightParenthesis) |
| { |
| while (token.value != TOK.rightParenthesis) |
| { |
| AST.TemplateParameter tp; |
| Loc loc; |
| Identifier tp_ident = null; |
| AST.Type tp_spectype = null; |
| AST.Type tp_valtype = null; |
| AST.Type tp_defaulttype = null; |
| AST.Expression tp_specvalue = null; |
| AST.Expression tp_defaultvalue = null; |
| |
| // Get TemplateParameter |
| |
| // First, look ahead to see if it is a TypeParameter or a ValueParameter |
| const tv = peekNext(); |
| if (token.value == TOK.alias_) |
| { |
| // AliasParameter |
| nextToken(); |
| loc = token.loc; // todo |
| AST.Type spectype = null; |
| if (isDeclaration(&token, NeedDeclaratorId.must, TOK.reserved, null)) |
| { |
| spectype = parseType(&tp_ident); |
| } |
| else |
| { |
| if (token.value != TOK.identifier) |
| { |
| error("identifier expected for template `alias` parameter"); |
| goto Lerr; |
| } |
| tp_ident = token.ident; |
| nextToken(); |
| } |
| RootObject spec = null; |
| if (token.value == TOK.colon) // : Type |
| { |
| nextToken(); |
| if (isDeclaration(&token, NeedDeclaratorId.no, TOK.reserved, null)) |
| spec = parseType(); |
| else |
| spec = parseCondExp(); |
| } |
| RootObject def = null; |
| if (token.value == TOK.assign) // = Type |
| { |
| nextToken(); |
| if (isDeclaration(&token, NeedDeclaratorId.no, TOK.reserved, null)) |
| def = parseType(); |
| else |
| def = parseCondExp(); |
| } |
| tp = new AST.TemplateAliasParameter(loc, tp_ident, spectype, spec, def); |
| } |
| else if (tv == TOK.colon || tv == TOK.assign || tv == TOK.comma || tv == TOK.rightParenthesis) |
| { |
| // TypeParameter |
| if (token.value != TOK.identifier) |
| { |
| error("identifier expected for template type parameter"); |
| goto Lerr; |
| } |
| loc = token.loc; |
| tp_ident = token.ident; |
| nextToken(); |
| if (token.value == TOK.colon) // : Type |
| { |
| nextToken(); |
| tp_spectype = parseType(); |
| } |
| if (token.value == TOK.assign) // = Type |
| { |
| nextToken(); |
| tp_defaulttype = parseType(); |
| } |
| tp = new AST.TemplateTypeParameter(loc, tp_ident, tp_spectype, tp_defaulttype); |
| } |
| else if (token.value == TOK.identifier && tv == TOK.dotDotDot) |
| { |
| // ident... |
| loc = token.loc; |
| tp_ident = token.ident; |
| nextToken(); |
| nextToken(); |
| tp = new AST.TemplateTupleParameter(loc, tp_ident); |
| } |
| else if (token.value == TOK.this_) |
| { |
| // ThisParameter |
| nextToken(); |
| if (token.value != TOK.identifier) |
| { |
| error("identifier expected for template `this` parameter"); |
| goto Lerr; |
| } |
| loc = token.loc; |
| tp_ident = token.ident; |
| nextToken(); |
| if (token.value == TOK.colon) // : Type |
| { |
| nextToken(); |
| tp_spectype = parseType(); |
| } |
| if (token.value == TOK.assign) // = Type |
| { |
| nextToken(); |
| tp_defaulttype = parseType(); |
| } |
| tp = new AST.TemplateThisParameter(loc, tp_ident, tp_spectype, tp_defaulttype); |
| } |
| else |
| { |
| // ValueParameter |
| loc = token.loc; // todo |
| tp_valtype = parseType(&tp_ident); |
| if (!tp_ident) |
| { |
| error("identifier expected for template value parameter"); |
| tp_ident = Identifier.idPool("error"); |
| } |
| if (token.value == TOK.colon) // : CondExpression |
| { |
| nextToken(); |
| tp_specvalue = parseCondExp(); |
| } |
| if (token.value == TOK.assign) // = CondExpression |
| { |
| nextToken(); |
| tp_defaultvalue = parseDefaultInitExp(); |
| } |
| tp = new AST.TemplateValueParameter(loc, tp_ident, tp_valtype, tp_specvalue, tp_defaultvalue); |
| } |
| tpl.push(tp); |
| if (token.value != TOK.comma) |
| break; |
| nextToken(); |
| } |
| } |
| check(TOK.rightParenthesis); |
| |
| Lerr: |
| return tpl; |
| } |
| |
| /****************************************** |
| * Parse template mixin. |
| * mixin Foo; |
| * mixin Foo!(args); |
| * mixin a.b.c!(args).Foo!(args); |
| * mixin Foo!(args) identifier; |
| * mixin typeof(expr).identifier!(args); |
| */ |
| private AST.Dsymbol parseMixin() |
| { |
| AST.TemplateMixin tm; |
| Identifier id; |
| AST.Objects* tiargs; |
| |
| //printf("parseMixin()\n"); |
| const locMixin = token.loc; |
| nextToken(); // skip 'mixin' |
| |
| auto loc = token.loc; |
| AST.TypeQualified tqual = null; |
| if (token.value == TOK.dot) |
| { |
| id = Id.empty; |
| } |
| else |
| { |
| if (token.value == TOK.typeof_) |
| { |
| tqual = parseTypeof(); |
| check(TOK.dot); |
| } |
| if (token.value != TOK.identifier) |
| { |
| error("identifier expected, not `%s`", token.toChars()); |
| id = Id.empty; |
| } |
| else |
| id = token.ident; |
| nextToken(); |
| } |
| |
| while (1) |
| { |
| tiargs = null; |
| if (token.value == TOK.not) |
| { |
| tiargs = parseTemplateArguments(); |
| } |
| |
| if (tiargs && token.value == TOK.dot) |
| { |
| auto tempinst = new AST.TemplateInstance(loc, id, tiargs); |
| if (!tqual) |
| tqual = new AST.TypeInstance(loc, tempinst); |
| else |
| tqual.addInst(tempinst); |
| tiargs = null; |
| } |
| else |
| { |
| if (!tqual) |
| tqual = new AST.TypeIdentifier(loc, id); |
| else |
| tqual.addIdent(id); |
| } |
| |
| if (token.value != TOK.dot) |
| break; |
| |
| nextToken(); |
| if (token.value != TOK.identifier) |
| { |
| error("identifier expected following `.` instead of `%s`", token.toChars()); |
| break; |
| } |
| loc = token.loc; |
| id = token.ident; |
| nextToken(); |
| } |
| |
| id = null; |
| if (token.value == TOK.identifier) |
| { |
| id = token.ident; |
| nextToken(); |
| } |
| |
| tm = new AST.TemplateMixin(locMixin, id, tqual, tiargs); |
| if (token.value != TOK.semicolon) |
| error("`;` expected after `mixin`"); |
| nextToken(); |
| |
| return tm; |
| } |
| |
| /****************************************** |
| * Parse template arguments. |
| * Input: |
| * current token is opening '!' |
| * Output: |
| * current token is one after closing '$(RPAREN)' |
| */ |
| private AST.Objects* parseTemplateArguments() |
| { |
| AST.Objects* tiargs; |
| |
| nextToken(); |
| if (token.value == TOK.leftParenthesis) |
| { |
| // ident!(template_arguments) |
| tiargs = parseTemplateArgumentList(); |
| } |
| else |
| { |
| // ident!template_argument |
| tiargs = parseTemplateSingleArgument(); |
| } |
| if (token.value == TOK.not) |
| { |
| TOK tok = peekNext(); |
| if (tok != TOK.is_ && tok != TOK.in_) |
| { |
| error("multiple ! arguments are not allowed"); |
| Lagain: |
| nextToken(); |
| if (token.value == TOK.leftParenthesis) |
| parseTemplateArgumentList(); |
| else |
| parseTemplateSingleArgument(); |
| if (token.value == TOK.not && (tok = peekNext()) != TOK.is_ && tok != TOK.in_) |
| goto Lagain; |
| } |
| } |
| return tiargs; |
| } |
| |
| /****************************************** |
| * Parse template argument list. |
| * Input: |
| * current token is opening '$(LPAREN)', |
| * or ',' for __traits |
| * Output: |
| * current token is one after closing '$(RPAREN)' |
| */ |
| private AST.Objects* parseTemplateArgumentList() |
| { |
| //printf("Parser::parseTemplateArgumentList()\n"); |
| auto tiargs = new AST.Objects(); |
| TOK endtok = TOK.rightParenthesis; |
| assert(token.value == TOK.leftParenthesis || token.value == TOK.comma); |
| nextToken(); |
| |
| // Get TemplateArgumentList |
| while (token.value != endtok) |
| { |
| tiargs.push(parseTypeOrAssignExp()); |
| if (token.value != TOK.comma) |
| break; |
| nextToken(); |
| } |
| check(endtok, "template argument list"); |
| return tiargs; |
| } |
| |
| /*************************************** |
| * Parse a Type or an Expression |
| * Returns: |
| * RootObject representing the AST |
| */ |
| RootObject parseTypeOrAssignExp(TOK endtoken = TOK.reserved) |
| { |
| return isDeclaration(&token, NeedDeclaratorId.no, endtoken, null) |
| ? parseType() // argument is a type |
| : parseAssignExp(); // argument is an expression |
| } |
| |
| /***************************** |
| * Parse single template argument, to support the syntax: |
| * foo!arg |
| * Input: |
| * current token is the arg |
| */ |
| private AST.Objects* parseTemplateSingleArgument() |
| { |
| //printf("parseTemplateSingleArgument()\n"); |
| auto tiargs = new AST.Objects(); |
| AST.Type ta; |
| switch (token.value) |
| { |
| case TOK.identifier: |
| ta = new AST.TypeIdentifier(token.loc, token.ident); |
| goto LabelX; |
| |
| case TOK.vector: |
| ta = parseVector(); |
| goto LabelX; |
| |
| case TOK.void_: |
| ta = AST.Type.tvoid; |
| goto LabelX; |
| |
| case TOK.int8: |
| ta = AST.Type.tint8; |
| goto LabelX; |
| |
| case TOK.uns8: |
| ta = AST.Type.tuns8; |
| goto LabelX; |
| |
| case TOK.int16: |
| ta = AST.Type.tint16; |
| goto LabelX; |
| |
| case TOK.uns16: |
| ta = AST.Type.tuns16; |
| goto LabelX; |
| |
| case TOK.int32: |
| ta = AST.Type.tint32; |
| goto LabelX; |
| |
| case TOK.uns32: |
| ta = AST.Type.tuns32; |
| goto LabelX; |
| |
| case TOK.int64: |
| ta = AST.Type.tint64; |
| goto LabelX; |
| |
| case TOK.uns64: |
| ta = AST.Type.tuns64; |
| goto LabelX; |
| |
| case TOK.int128: |
| ta = AST.Type.tint128; |
| goto LabelX; |
| |
| case TOK.uns128: |
| ta = AST.Type.tuns128; |
| goto LabelX; |
| |
| case TOK.float32: |
| ta = AST.Type.tfloat32; |
| goto LabelX; |
| |
| case TOK.float64: |
| ta = AST.Type.tfloat64; |
| goto LabelX; |
| |
| case TOK.float80: |
| ta = AST.Type.tfloat80; |
| goto LabelX; |
| |
| case TOK.imaginary32: |
| ta = AST.Type.timaginary32; |
| goto LabelX; |
| |
| case TOK.imaginary64: |
| ta = AST.Type.timaginary64; |
| goto LabelX; |
| |
| case TOK.imaginary80: |
| ta = AST.Type.timaginary80; |
| goto LabelX; |
| |
| case TOK.complex32: |
| ta = AST.Type.tcomplex32; |
| goto LabelX; |
| |
| case TOK.complex64: |
| ta = AST.Type.tcomplex64; |
| goto LabelX; |
| |
| case TOK.complex80: |
| ta = AST.Type.tcomplex80; |
| goto LabelX; |
| |
| case TOK.bool_: |
| ta = AST.Type.tbool; |
| goto LabelX; |
| |
| case TOK.char_: |
| ta = AST.Type.tchar; |
| goto LabelX; |
| |
| case TOK.wchar_: |
| ta = AST.Type.twchar; |
| goto LabelX; |
| |
| case TOK.dchar_: |
| ta = AST.Type.tdchar; |
| goto LabelX; |
| LabelX: |
| tiargs.push(ta); |
| nextToken(); |
| break; |
| |
| case TOK.int32Literal: |
| case TOK.uns32Literal: |
| case TOK.int64Literal: |
| case TOK.uns64Literal: |
| case TOK.int128Literal: |
| case TOK.uns128Literal: |
| case TOK.float32Literal: |
| case TOK.float64Literal: |
| case TOK.float80Literal: |
| case TOK.imaginary32Literal: |
| case TOK.imaginary64Literal: |
| case TOK.imaginary80Literal: |
| case TOK.null_: |
| case TOK.true_: |
| case TOK.false_: |
| case TOK.charLiteral: |
| case TOK.wcharLiteral: |
| case TOK.dcharLiteral: |
| case TOK.string_: |
| case TOK.file: |
| case TOK.fileFullPath: |
| case TOK.line: |
| case TOK.moduleString: |
| case TOK.functionString: |
| case TOK.prettyFunction: |
| case TOK.this_: |
| { |
| // Template argument is an expression |
| AST.Expression ea = parsePrimaryExp(); |
| tiargs.push(ea); |
| break; |
| } |
| default: |
| error("template argument expected following `!`"); |
| break; |
| } |
| return tiargs; |
| } |
| |
| /********************************** |
| * Parse a static assertion. |
| * Current token is 'static'. |
| */ |
| private AST.StaticAssert parseStaticAssert() |
| { |
| const loc = token.loc; |
| AST.Expression exp; |
| AST.Expression msg = null; |
| |
| //printf("parseStaticAssert()\n"); |
| nextToken(); |
| nextToken(); |
| check(TOK.leftParenthesis); |
| exp = parseAssignExp(); |
| if (token.value == TOK.comma) |
| { |
| nextToken(); |
| if (token.value != TOK.rightParenthesis) |
| { |
| msg = parseAssignExp(); |
| if (token.value == TOK.comma) |
| nextToken(); |
| } |
| } |
| check(TOK.rightParenthesis); |
| check(TOK.semicolon, "static assert"); |
| return new AST.StaticAssert(loc, exp, msg); |
| } |
| |
| /*********************************** |
| * Parse typeof(expression). |
| * Current token is on the 'typeof'. |
| */ |
| private AST.TypeQualified parseTypeof() |
| { |
| AST.TypeQualified t; |
| const loc = token.loc; |
| |
| nextToken(); |
| check(TOK.leftParenthesis); |
| if (token.value == TOK.return_) // typeof(return) |
| { |
| nextToken(); |
| t = new AST.TypeReturn(loc); |
| } |
| else |
| { |
| AST.Expression exp = parseExpression(); // typeof(expression) |
| t = new AST.TypeTypeof(loc, exp); |
| } |
| check(TOK.rightParenthesis); |
| return t; |
| } |
| |
| /*********************************** |
| * Parse __vector(type). |
| * Current token is on the '__vector'. |
| */ |
| private AST.Type parseVector() |
| { |
| nextToken(); |
| check(TOK.leftParenthesis); |
| AST.Type tb = parseType(); |
| check(TOK.rightParenthesis); |
| return new AST.TypeVector(tb); |
| } |
| |
| /*********************************** |
| * Parse: |
| * extern (linkage) |
| * extern (C++, namespaces) |
| * extern (C++, "namespace", "namespaces", ...) |
| * extern (C++, (StringExp)) |
| * The parser is on the 'extern' token. |
| */ |
| private ParsedLinkage!(AST) parseLinkage() |
| { |
| ParsedLinkage!(AST) result; |
| nextToken(); |
| assert(token.value == TOK.leftParenthesis); |
| nextToken(); |
| ParsedLinkage!(AST) returnLinkage(LINK link) |
| { |
| check(TOK.rightParenthesis); |
| result.link = link; |
| return result; |
| } |
| ParsedLinkage!(AST) invalidLinkage() |
| { |
| error("valid linkage identifiers are `D`, `C`, `C++`, `Objective-C`, `Windows`, `System`"); |
| return returnLinkage(LINK.d); |
| } |
| |
| if (token.value != TOK.identifier) |
| return returnLinkage(LINK.d); |
| |
| Identifier id = token.ident; |
| nextToken(); |
| if (id == Id.Windows) |
| return returnLinkage(LINK.windows); |
| else if (id == Id.D) |
| return returnLinkage(LINK.d); |
| else if (id == Id.System) |
| return returnLinkage(LINK.system); |
| else if (id == Id.Objective) // Looking for tokens "Objective-C" |
| { |
| if (token.value != TOK.min) |
| return invalidLinkage(); |
| |
| nextToken(); |
| if (token.ident != Id.C) |
| return invalidLinkage(); |
| |
| nextToken(); |
| return returnLinkage(LINK.objc); |
| } |
| else if (id != Id.C) |
| return invalidLinkage(); |
| |
| if (token.value != TOK.plusPlus) |
| return returnLinkage(LINK.c); |
| |
| nextToken(); |
| if (token.value != TOK.comma) // , namespaces or class or struct |
| return returnLinkage(LINK.cpp); |
| |
| nextToken(); |
| |
| if (token.value == TOK.rightParenthesis) |
| return returnLinkage(LINK.cpp); // extern(C++,) |
| |
| if (token.value == TOK.class_ || token.value == TOK.struct_) |
| { |
| result.cppmangle = token.value == TOK.class_ ? CPPMANGLE.asClass : CPPMANGLE.asStruct; |
| nextToken(); |
| } |
| else if (token.value == TOK.identifier) // named scope namespace |
| { |
| result.idents = new AST.Identifiers(); |
| while (1) |
| { |
| Identifier idn = token.ident; |
| result.idents.push(idn); |
| nextToken(); |
| if (token.value == TOK.dot) |
| { |
| nextToken(); |
| if (token.value == TOK.identifier) |
| continue; |
| error("identifier expected for C++ namespace"); |
| result.idents = null; // error occurred, invalidate list of elements. |
| } |
| break; |
| } |
| } |
| else // non-scoped StringExp namespace |
| { |
| result.identExps = new AST.Expressions(); |
| while (1) |
| { |
| result.identExps.push(parseCondExp()); |
| if (token.value != TOK.comma) |
| break; |
| nextToken(); |
| // Allow trailing commas as done for argument lists, arrays, ... |
| if (token.value == TOK.rightParenthesis) |
| break; |
| } |
| } |
| return returnLinkage(LINK.cpp); |
| } |
| |
| /*********************************** |
| * Parse ident1.ident2.ident3 |
| * |
| * Params: |
| * entity = what qualified identifier is expected to resolve into. |
| * Used only for better error message |
| * |
| * Returns: |
| * array of identifiers with actual qualified one stored last |
| */ |
| private Identifier[] parseQualifiedIdentifier(const(char)* entity) |
| { |
| Identifier[] qualified; |
| |
| do |
| { |
| nextToken(); |
| if (token.value != TOK.identifier) |
| { |
| error("`%s` expected as dot-separated identifiers, got `%s`", entity, token.toChars()); |
| return qualified; |
| } |
| |
| Identifier id = token.ident; |
| qualified ~= id; |
| |
| nextToken(); |
| } |
| while (token.value == TOK.dot); |
| |
| return qualified; |
| } |
| |
| private AST.DebugSymbol parseDebugSpecification() |
| { |
| AST.DebugSymbol s; |
| nextToken(); |
| if (token.value == TOK.identifier) |
| s = new AST.DebugSymbol(token.loc, token.ident); |
| else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal) |
| { |
| // @@@DEPRECATED_2.111@@@ |
| // Deprecated in 2.101, remove in 2.111 |
| deprecation("`debug = <integer>` is deprecated, use debug identifiers instead"); |
| |
| s = new AST.DebugSymbol(token.loc, cast(uint)token.unsvalue); |
| } |
| else |
| { |
| error("identifier or integer expected, not `%s`", token.toChars()); |
| s = null; |
| } |
| nextToken(); |
| if (token.value != TOK.semicolon) |
| error("semicolon expected"); |
| nextToken(); |
| return s; |
| } |
| |
| /************************************** |
| * Parse a debug conditional |
| */ |
| private AST.Condition parseDebugCondition() |
| { |
| uint level = 1; |
| Identifier id = null; |
| Loc loc = token.loc; |
| |
| if (token.value == TOK.leftParenthesis) |
| { |
| nextToken(); |
| |
| if (token.value == TOK.identifier) |
| id = token.ident; |
| else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal) |
| { |
| // @@@DEPRECATED_2.111@@@ |
| // Deprecated in 2.101, remove in 2.111 |
| deprecation("`debug( <integer> )` is deprecated, use debug identifiers instead"); |
| |
| level = cast(uint)token.unsvalue; |
| } |
| else |
| error("identifier or integer expected inside `debug(...)`, not `%s`", token.toChars()); |
| loc = token.loc; |
| nextToken(); |
| check(TOK.rightParenthesis); |
| } |
| return new AST.DebugCondition(loc, mod, level, id); |
| } |
| |
| /************************************** |
| * Parse a version specification |
| */ |
| private AST.VersionSymbol parseVersionSpecification() |
| { |
| AST.VersionSymbol s; |
| nextToken(); |
| if (token.value == TOK.identifier) |
| s = new AST.VersionSymbol(token.loc, token.ident); |
| else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal) |
| { |
| // @@@DEPRECATED_2.111@@@ |
| // Deprecated in 2.101, remove in 2.111 |
| deprecation("`version = <integer>` is deprecated, use version identifiers instead"); |
| s = new AST.VersionSymbol(token.loc, cast(uint)token.unsvalue); |
| } |
| else |
| { |
| error("identifier or integer expected, not `%s`", token.toChars()); |
| s = null; |
| } |
| nextToken(); |
| if (token.value != TOK.semicolon) |
| error("semicolon expected"); |
| nextToken(); |
| return s; |
| } |
| |
| /************************************** |
| * Parse a version conditional |
| */ |
| private AST.Condition parseVersionCondition() |
| { |
| uint level = 1; |
| Identifier id = null; |
| Loc loc; |
| |
| if (token.value == TOK.leftParenthesis) |
| { |
| nextToken(); |
| /* Allow: |
| * version (unittest) |
| * version (assert) |
| * even though they are keywords |
| */ |
| loc = token.loc; |
| if (token.value == TOK.identifier) |
| id = token.ident; |
| else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal) |
| { |
| // @@@DEPRECATED_2.111@@@ |
| // Deprecated in 2.101, remove in 2.111 |
| deprecation("`version( <integer> )` is deprecated, use version identifiers instead"); |
| |
| level = cast(uint)token.unsvalue; |
| } |
| else if (token.value == TOK.unittest_) |
| id = Identifier.idPool(Token.toString(TOK.unittest_)); |
| else if (token.value == TOK.assert_) |
| id = Identifier.idPool(Token.toString(TOK.assert_)); |
| else |
| error("identifier or integer expected inside `version(...)`, not `%s`", token.toChars()); |
| nextToken(); |
| check(TOK.rightParenthesis); |
| } |
| else |
| error("(condition) expected following `version`"); |
| return new AST.VersionCondition(loc, mod, level, id); |
| } |
| |
| /*********************************************** |
| * static if (expression) |
| * body |
| * else |
| * body |
| * Current token is 'static'. |
| */ |
| private AST.Condition parseStaticIfCondition() |
| { |
| AST.Expression exp; |
| AST.Condition condition; |
| const loc = token.loc; |
| |
| nextToken(); |
| nextToken(); |
| if (token.value == TOK.leftParenthesis) |
| { |
| nextToken(); |
| exp = parseAssignExp(); |
| check(TOK.rightParenthesis); |
| } |
| else |
| { |
| error("(expression) expected following `static if`"); |
| exp = null; |
| } |
| condition = new AST.StaticIfCondition(loc, exp); |
| return condition; |
| } |
| |
| /***************************************** |
| * Parse a constructor definition: |
| * this(parameters) { body } |
| * or postblit: |
| * this(this) { body } |
| * or constructor template: |
| * this(templateparameters)(parameters) { body } |
| * Current token is 'this'. |
| */ |
| private AST.Dsymbol parseCtor(PrefixAttributes!AST* pAttrs) |
| { |
| AST.Expressions* udas = null; |
| const loc = token.loc; |
| StorageClass stc = getStorageClass!AST(pAttrs); |
| |
| nextToken(); |
| if (token.value == TOK.leftParenthesis && peekNext() == TOK.this_ && peekNext2() == TOK.rightParenthesis) |
| { |
| // this(this) { ... } |
| nextToken(); |
| nextToken(); |
| check(TOK.rightParenthesis); |
| |
| stc = parsePostfix(stc, &udas); |
| if (stc & STC.immutable_) |
| deprecation("`immutable` postblit is deprecated. Please use an unqualified postblit."); |
| if (stc & STC.shared_) |
| deprecation("`shared` postblit is deprecated. Please use an unqualified postblit."); |
| if (stc & STC.const_) |
| deprecation("`const` postblit is deprecated. Please use an unqualified postblit."); |
| if (stc & STC.static_) |
| error(loc, "postblit cannot be `static`"); |
| |
| auto f = new AST.PostBlitDeclaration(loc, Loc.initial, stc, Id.postblit); |
| AST.Dsymbol s = parseContracts(f); |
| if (udas) |
| { |
| auto a = new AST.Dsymbols(); |
| a.push(f); |
| s = new AST.UserAttributeDeclaration(udas, a); |
| } |
| return s; |
| } |
| |
| /* Look ahead to see if: |
| * this(...)(...) |
| * which is a constructor template |
| */ |
| AST.TemplateParameters* tpl = null; |
| if (token.value == TOK.leftParenthesis && peekPastParen(&token).value == TOK.leftParenthesis) |
| { |
| tpl = parseTemplateParameterList(); |
| } |
| |
| /* Just a regular constructor |
| */ |
| auto parameterList = parseParameterList(null); |
| stc = parsePostfix(stc, &udas); |
| |
| if (parameterList.varargs != VarArg.none || AST.Parameter.dim(parameterList.parameters) != 0) |
| { |
| if (stc & STC.static_) |
| error(loc, "constructor cannot be static"); |
| } |
| else if (StorageClass ss = stc & (STC.shared_ | STC.static_)) // this() |
| { |
| if (ss == STC.static_) |
| error(loc, "use `static this()` to declare a static constructor"); |
| else if (ss == (STC.shared_ | STC.static_)) |
| error(loc, "use `shared static this()` to declare a shared static constructor"); |
| } |
| |
| AST.Expression constraint = tpl ? parseConstraint() : null; |
| |
| AST.Type tf = new AST.TypeFunction(parameterList, null, linkage, stc); // RetrunType -> auto |
| tf = tf.addSTC(stc); |
| |
| auto f = new AST.CtorDeclaration(loc, Loc.initial, stc, tf); |
| AST.Dsymbol s = parseContracts(f); |
| if (udas) |
| { |
| auto a = new AST.Dsymbols(); |
| a.push(f); |
| s = new AST.UserAttributeDeclaration(udas, a); |
| } |
| |
| if (tpl) |
| { |
| // Wrap a template around it |
| auto decldefs = new AST.Dsymbols(); |
| decldefs.push(s); |
| s = new AST.TemplateDeclaration(loc, f.ident, tpl, constraint, decldefs); |
| } |
| |
| return s; |
| } |
| |
| /***************************************** |
| * Parse a destructor definition: |
| * ~this() { body } |
| * Current token is '~'. |
| */ |
| private AST.Dsymbol parseDtor(PrefixAttributes!AST* pAttrs) |
| { |
| AST.Expressions* udas = null; |
| const loc = token.loc; |
| StorageClass stc = getStorageClass!AST(pAttrs); |
| |
| nextToken(); |
| check(TOK.this_); |
| check(TOK.leftParenthesis); |
| check(TOK.rightParenthesis); |
| |
| stc = parsePostfix(stc, &udas); |
| if (StorageClass ss = stc & (STC.shared_ | STC.static_)) |
| { |
| if (ss == STC.static_) |
| error(loc, "use `static ~this()` to declare a static destructor"); |
| else if (ss == (STC.shared_ | STC.static_)) |
| error(loc, "use `shared static ~this()` to declare a shared static destructor"); |
| } |
| |
| auto f = new AST.DtorDeclaration(loc, Loc.initial, stc, Id.dtor); |
| AST.Dsymbol s = parseContracts(f); |
| if (udas) |
| { |
| auto a = new AST.Dsymbols(); |
| a.push(f); |
| s = new AST.UserAttributeDeclaration(udas, a); |
| } |
| return s; |
| } |
| |
| /***************************************** |
| * Parse a static constructor definition: |
| * static this() { body } |
| * Current token is 'static'. |
| */ |
| private AST.Dsymbol parseStaticCtor(PrefixAttributes!AST* pAttrs) |
| { |
| //Expressions *udas = NULL; |
| const loc = token.loc; |
| StorageClass stc = getStorageClass!AST(pAttrs); |
| |
| nextToken(); |
| nextToken(); |
| check(TOK.leftParenthesis); |
| check(TOK.rightParenthesis); |
| |
| stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc; |
| if (stc & STC.shared_) |
| error(loc, "use `shared static this()` to declare a shared static constructor"); |
| else if (stc & STC.static_) |
| appendStorageClass(stc, STC.static_); // complaint for the redundancy |
| else if (StorageClass modStc = stc & STC.TYPECTOR) |
| { |
| OutBuffer buf; |
| AST.stcToBuffer(&buf, modStc); |
| error(loc, "static constructor cannot be `%s`", buf.peekChars()); |
| } |
| stc &= ~(STC.static_ | STC.TYPECTOR); |
| |
| auto f = new AST.StaticCtorDeclaration(loc, Loc.initial, stc); |
| AST.Dsymbol s = parseContracts(f); |
| return s; |
| } |
| |
| /***************************************** |
| * Parse a static destructor definition: |
| * static ~this() { body } |
| * Current token is 'static'. |
| */ |
| private AST.Dsymbol parseStaticDtor(PrefixAttributes!AST* pAttrs) |
| { |
| AST.Expressions* udas = null; |
| const loc = token.loc; |
| StorageClass stc = getStorageClass!AST(pAttrs); |
| |
| nextToken(); |
| nextToken(); |
| check(TOK.this_); |
| check(TOK.leftParenthesis); |
| check(TOK.rightParenthesis); |
| |
| stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc; |
| if (stc & STC.shared_) |
| error(loc, "use `shared static ~this()` to declare a shared static destructor"); |
| else if (stc & STC.static_) |
| appendStorageClass(stc, STC.static_); // complaint for the redundancy |
| else if (StorageClass modStc = stc & STC.TYPECTOR) |
| { |
| OutBuffer buf; |
| AST.stcToBuffer(&buf, modStc); |
| error(loc, "static destructor cannot be `%s`", buf.peekChars()); |
| } |
| stc &= ~(STC.static_ | STC.TYPECTOR); |
| |
| auto f = new AST.StaticDtorDeclaration(loc, Loc.initial, stc); |
| AST.Dsymbol s = parseContracts(f); |
| if (udas) |
| { |
| auto a = new AST.Dsymbols(); |
| a.push(f); |
| s = new AST.UserAttributeDeclaration(udas, a); |
| } |
| return s; |
| } |
| |
| /***************************************** |
| * Parse a shared static constructor definition: |
| * shared static this() { body } |
| * Current token is 'shared'. |
| */ |
| private AST.Dsymbol parseSharedStaticCtor(PrefixAttributes!AST* pAttrs) |
| { |
| //Expressions *udas = NULL; |
| const loc = token.loc; |
| StorageClass stc = getStorageClass!AST(pAttrs); |
| |
| nextToken(); |
| nextToken(); |
| nextToken(); |
| check(TOK.leftParenthesis); |
| check(TOK.rightParenthesis); |
| |
| stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc; |
| if (StorageClass ss = stc & (STC.shared_ | STC.static_)) |
| appendStorageClass(stc, ss); // complaint for the redundancy |
| else if (StorageClass modStc = stc & STC.TYPECTOR) |
| { |
| OutBuffer buf; |
| AST.stcToBuffer(&buf, modStc); |
| error(loc, "shared static constructor cannot be `%s`", buf.peekChars()); |
| } |
| stc &= ~(STC.static_ | STC.TYPECTOR); |
| |
| auto f = new AST.SharedStaticCtorDeclaration(loc, Loc.initial, stc); |
| AST.Dsymbol s = parseContracts(f); |
| return s; |
| } |
| |
| /***************************************** |
| * Parse a shared static destructor definition: |
| * shared static ~this() { body } |
| * Current token is 'shared'. |
| */ |
| private AST.Dsymbol parseSharedStaticDtor(PrefixAttributes!AST* pAttrs) |
| { |
| AST.Expressions* udas = null; |
| const loc = token.loc; |
| StorageClass stc = getStorageClass!AST(pAttrs); |
| |
| nextToken(); |
| nextToken(); |
| nextToken(); |
| check(TOK.this_); |
| check(TOK.leftParenthesis); |
| check(TOK.rightParenthesis); |
| |
| stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc; |
| if (StorageClass ss = stc & (STC.shared_ | STC.static_)) |
| appendStorageClass(stc, ss); // complaint for the redundancy |
| else if (StorageClass modStc = stc & STC.TYPECTOR) |
| { |
| OutBuffer buf; |
| AST.stcToBuffer(&buf, modStc); |
| error(loc, "shared static destructor cannot be `%s`", buf.peekChars()); |
| } |
| stc &= ~(STC.static_ | STC.TYPECTOR); |
| |
| auto f = new AST.SharedStaticDtorDeclaration(loc, Loc.initial, stc); |
| AST.Dsymbol s = parseContracts(f); |
| if (udas) |
| { |
| auto a = new AST.Dsymbols(); |
| a.push(f); |
| s = new AST.UserAttributeDeclaration(udas, a); |
| } |
| return s; |
| } |
| |
| /***************************************** |
| * Parse an invariant definition: |
| * invariant { statements... } |
| * invariant() { statements... } |
| * invariant (expression); |
| * Current token is 'invariant'. |
| */ |
| private AST.Dsymbol parseInvariant(PrefixAttributes!AST* pAttrs) |
| { |
| const loc = token.loc; |
| StorageClass stc = getStorageClass!AST(pAttrs); |
| |
| nextToken(); |
| if (token.value == TOK.leftParenthesis) // optional () or invariant (expression); |
| { |
| nextToken(); |
| if (token.value != TOK.rightParenthesis) // invariant (expression); |
| { |
| AST.Expression e = parseAssignExp(), msg = null; |
| if (token.value == TOK.comma) |
| { |
| nextToken(); |
| if (token.value != TOK.rightParenthesis) |
| { |
| msg = parseAssignExp(); |
| if (token.value == TOK.comma) |
| nextToken(); |
| } |
| } |
| check(TOK.rightParenthesis); |
| check(TOK.semicolon, "invariant"); |
| e = new AST.AssertExp(loc, e, msg); |
| auto fbody = new AST.ExpStatement(loc, e); |
| auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody); |
| return f; |
| } |
| nextToken(); |
| } |
| |
| auto fbody = parseStatement(ParseStatementFlags.curly); |
| auto f = new AST.InvariantDeclaration(loc, token.loc, stc, null, fbody); |
| return f; |
| } |
| |
| /***************************************** |
| * Parse a unittest definition: |
| * unittest { body } |
| * Current token is 'unittest'. |
| */ |
| private AST.Dsymbol parseUnitTest(PrefixAttributes!AST* pAttrs) |
| { |
| const loc = token.loc; |
| StorageClass stc = getStorageClass!AST(pAttrs); |
| |
| nextToken(); |
| |
| const(char)* begPtr = token.ptr + 1; // skip '{' |
| const(char)* endPtr = null; |
| AST.Statement sbody = parseStatement(ParseStatementFlags.curly, &endPtr); |
| |
| /** Extract unittest body as a string. Must be done eagerly since memory |
| will be released by the lexer before doc gen. */ |
| char* docline = null; |
| if (global.params.ddoc.doOutput && endPtr > begPtr) |
| { |
| /* Remove trailing whitespaces */ |
| for (const(char)* p = endPtr - 1; begPtr <= p && (*p == ' ' || *p == '\r' || *p == '\n' || *p == '\t'); --p) |
| { |
| endPtr = p; |
| } |
| |
| size_t len = endPtr - begPtr; |
| if (len > 0) |
| { |
| docline = cast(char*)mem.xmalloc_noscan(len + 2); |
| memcpy(docline, begPtr, len); |
| docline[len] = '\n'; // Terminate all lines by LF |
| docline[len + 1] = '\0'; |
| } |
| } |
| |
| auto f = new AST.UnitTestDeclaration(loc, token.loc, stc, docline); |
| f.fbody = sbody; |
| return f; |
| } |
| |
| /***************************************** |
| * Parse a new definition: |
| * @disable new(); |
| * Current token is 'new'. |
| */ |
| private AST.Dsymbol parseNew(PrefixAttributes!AST* pAttrs) |
| { |
| const loc = token.loc; |
| StorageClass stc = getStorageClass!AST(pAttrs); |
| if (!(stc & STC.disable)) |
| { |
| error("`new` allocator must be annotated with `@disabled`"); |
| } |
| nextToken(); |
| |
| /* @@@DEPRECATED_2.108@@@ |
| * After deprecation period (2.108), remove all code in the version(all) block. |
| */ |
| version (all) |
| { |
| auto parameterList = parseParameterList(null); // parameterList ignored |
| if (parameterList.parameters.length > 0 || parameterList.varargs != VarArg.none) |
| deprecation("`new` allocator with non-empty parameter list is deprecated"); |
| auto f = new AST.NewDeclaration(loc, stc); |
| if (token.value != TOK.semicolon) |
| { |
| deprecation("`new` allocator with function definition is deprecated"); |
| parseContracts(f); // body ignored |
| f.fbody = null; |
| f.fensures = null; |
| f.frequires = null; |
| } |
| else |
| nextToken(); |
| return f; |
| } |
| else |
| { |
| check(TOK.leftParenthesis); |
| check(TOK.rightParenthesis); |
| check(TOK.semicolon); |
| return new AST.NewDeclaration(loc, stc); |
| } |
| } |
| |
| /********************************************** |
| * Parse parameter list. |
| */ |
| private AST.ParameterList parseParameterList(AST.TemplateParameters** tpl) |
| { |
| auto parameters = new AST.Parameters(); |
| VarArg varargs = VarArg.none; |
| StorageClass varargsStc; |
| |
| // Attributes allowed for ... |
| enum VarArgsStc = STC.const_ | STC.immutable_ | STC.shared_ | STC.scope_ | STC.return_ | STC.returnScope; |
| |
| check(TOK.leftParenthesis); |
| while (1) |
| { |
| Identifier ai = null; |
| AST.Type at; |
| StorageClass storageClass = 0; |
| StorageClass stc; |
| AST.Expression ae; |
| AST.Expressions* udas = null; |
| for (; 1; nextToken()) |
| { |
| L3: |
| switch (token.value) |
| { |
| case TOK.rightParenthesis: |
| if (storageClass != 0 || udas !is null) |
| error("basic type expected, not `)`"); |
| break; |
| |
| case TOK.dotDotDot: |
| varargs = VarArg.variadic; |
| varargsStc = storageClass; |
| if (varargsStc & ~VarArgsStc) |
| { |
| OutBuffer buf; |
| AST.stcToBuffer(&buf, varargsStc & ~VarArgsStc); |
| error("variadic parameter cannot have attributes `%s`", buf.peekChars()); |
| varargsStc &= VarArgsStc; |
| } |
| nextToken(); |
| break; |
| |
| case TOK.const_: |
| if (peekNext() == TOK.leftParenthesis) |
| goto default; |
| stc = STC.const_; |
| goto L2; |
| |
| case TOK.immutable_: |
| if (peekNext() == TOK.leftParenthesis) |
| goto default; |
| stc = STC.immutable_; |
| goto L2; |
| |
| case TOK.shared_: |
| if (peekNext() == TOK.leftParenthesis) |
| goto default; |
| stc = STC.shared_; |
| goto L2; |
| |
| case TOK.inout_: |
| if (peekNext() == TOK.leftParenthesis) |
| goto default; |
| stc = STC.wild; |
| goto L2; |
| case TOK.at: |
| { |
| AST.Expressions* exps = null; |
| StorageClass stc2 = parseAttribute(exps); |
| if (stc2 & atAttrGroup) |
| { |
| error("`@%s` attribute for function parameter is not supported", token.toChars()); |
| } |
| else |
| { |
| udas = AST.UserAttributeDeclaration.concat(udas, exps); |
| } |
| if (token.value == TOK.dotDotDot) |
| error("variadic parameter cannot have user-defined attributes"); |
| if (stc2) |
| nextToken(); |
| goto L3; |
| // Don't call nextToken again. |
| } |
| case TOK.in_: |
| if (global.params.vin) |
| message(scanloc, "Usage of 'in' on parameter"); |
| stc = STC.in_; |
| goto L2; |
| |
| case TOK.out_: |
| stc = STC.out_; |
| goto L2; |
| |
| case TOK.ref_: |
| stc = STC.ref_; |
| goto L2; |
| |
| case TOK.lazy_: |
| stc = STC.lazy_; |
| goto L2; |
| |
| case TOK.scope_: |
| stc = STC.scope_; |
| goto L2; |
| |
| case TOK.final_: |
| stc = STC.final_; |
| goto L2; |
| |
| case TOK.auto_: |
| stc = STC.auto_; |
| goto L2; |
| |
| case TOK.return_: |
| stc = STC.return_; |
| if (peekNext() == TOK.scope_) |
| stc |= STC.returnScope; |
| goto L2; |
| L2: |
| storageClass = appendStorageClass(storageClass, stc); |
| continue; |
| |
| version (none) |
| { |
| case TOK.static_: |
| stc = STC.static_; |
| goto L2; |
| |
| case TOK.auto_: |
| storageClass = STC.auto_; |
| goto L4; |
| |
| case TOK.alias_: |
| storageClass = STC.alias_; |
| goto L4; |
| L4: |
| nextToken(); |
| ai = null; |
| if (token.value == TOK.identifier) |
| { |
| ai = token.ident; |
| nextToken(); |
| } |
| |
| at = null; // no type |
| ae = null; // no default argument |
| if (token.value == TOK.assign) // = defaultArg |
| { |
| nextToken(); |
| ae = parseDefaultInitExp(); |
| hasdefault = 1; |
| } |
| else |
| { |
| if (hasdefault) |
| error("default argument expected for `alias %s`", ai ? ai.toChars() : ""); |
| } |
| goto L3; |
| } |
| default: |
| { |
| stc = storageClass & (STC.IOR | STC.lazy_); |
| // if stc is not a power of 2 |
| if (stc & (stc - 1) && !(stc == (STC.in_ | STC.ref_))) |
| error("incompatible parameter storage classes"); |
| //if ((storageClass & STC.scope_) && (storageClass & (STC.ref_ | STC.out_))) |
| //error("scope cannot be ref or out"); |
| |
| const tv = peekNext(); |
| if (tpl && token.value == TOK.identifier && |
| (tv == TOK.comma || tv == TOK.rightParenthesis || tv == TOK.dotDotDot)) |
| { |
| Identifier id = Identifier.generateId("__T"); |
| const loc = token.loc; |
| at = new AST.TypeIdentifier(loc, id); |
| if (!*tpl) |
| *tpl = new AST.TemplateParameters(); |
| AST.TemplateParameter tp = new AST.TemplateTypeParameter(loc, id, null, null); |
| (*tpl).push(tp); |
| |
| ai = token.ident; |
| nextToken(); |
| } |
| else |
| { |
| at = parseType(&ai); |
| } |
| ae = null; |
| if (token.value == TOK.assign) // = defaultArg |
| { |
| nextToken(); |
| ae = parseDefaultInitExp(); |
| } |
| auto param = new AST.Parameter(storageClass | STC.parameter, at, ai, ae, null); |
| if (udas) |
| { |
| auto a = new AST.Dsymbols(); |
| auto udad = new AST.UserAttributeDeclaration(udas, a); |
| param.userAttribDecl = udad; |
| } |
| if (token.value == TOK.at) |
| { |
| AST.Expressions* exps = null; |
| StorageClass stc2 = parseAttribute(exps); |
| if (stc2 & atAttrGroup) |
| { |
| error("`@%s` attribute for function parameter is not supported", token.toChars()); |
| } |
| else |
| { |
| error("user-defined attributes cannot appear as postfixes", token.toChars()); |
| } |
| if (stc2) |
| nextToken(); |
| } |
| if (token.value == TOK.dotDotDot) |
| { |
| /* This is: |
| * at ai ... |
| */ |
| if (storageClass & (STC.out_ | STC.ref_)) |
| error("variadic argument cannot be `out` or `ref`"); |
| varargs = VarArg.typesafe; |
| parameters.push(param); |
| nextToken(); |
| break; |
| } |
| parameters.push(param); |
| if (token.value == TOK.comma) |
| { |
| nextToken(); |
| goto L1; |
| } |
| break; |
| } |
| } |
| break; |
| } |
| break; |
| |
| L1: |
| } |
| check(TOK.rightParenthesis); |
| return AST.ParameterList(parameters, varargs, varargsStc); |
| } |
| |
| /************************************* |
| */ |
| private AST.EnumDeclaration parseEnum() |
| { |
| AST.EnumDeclaration e; |
| Identifier id; |
| AST.Type memtype; |
| auto loc = token.loc; |
| |
| // printf("Parser::parseEnum()\n"); |
| nextToken(); |
| id = null; |
| if (token.value == TOK.identifier) |
|