Mercurial > projects > dil
changeset 578:c769bc239006
Moved Parser.d to new package 'parser'.
author | Aziz K?ksal <aziz.koeksal@gmail.com> |
---|---|
date | Sat, 05 Jan 2008 17:29:47 +0100 |
parents | 9e811db780a6 |
children | 90d0cff4107e |
files | trunk/src/cmd/Generate.d trunk/src/dil/ImportParser.d trunk/src/dil/Module.d trunk/src/dil/Parser.d trunk/src/dil/parser/Parser.d trunk/src/main.d |
diffstat | 6 files changed, 4238 insertions(+), 4239 deletions(-) [+] |
line wrap: on
line diff
--- a/trunk/src/cmd/Generate.d Sat Jan 05 17:01:15 2008 +0100 +++ b/trunk/src/cmd/Generate.d Sat Jan 05 17:29:47 2008 +0100 @@ -3,9 +3,11 @@ License: GPL3 +/ module cmd.Generate; + import dil.SyntaxTree; import dil.Token; -import dil.Parser, dil.lexer.Lexer; +import dil.parser.Parser; +import dil.lexer.Lexer; import dil.File; import tango.io.Print; import common;
--- a/trunk/src/dil/ImportParser.d Sat Jan 05 17:01:15 2008 +0100 +++ b/trunk/src/dil/ImportParser.d Sat Jan 05 17:29:47 2008 +0100 @@ -3,7 +3,8 @@ License: GPL3 +/ module dil.ImportParser; -import dil.Parser; + +import dil.parser.Parser; import dil.Token; import dil.Enums; import dil.Declarations;
--- a/trunk/src/dil/Module.d Sat Jan 05 17:01:15 2008 +0100 +++ b/trunk/src/dil/Module.d Sat Jan 05 17:29:47 2008 +0100 @@ -6,7 +6,7 @@ import dil.SyntaxTree; import dil.Declarations; -import dil.Parser; +import dil.parser.Parser; import dil.ImportParser; import dil.lexer.Lexer; import dil.File;
--- a/trunk/src/dil/Parser.d Sat Jan 05 17:01:15 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4220 +0,0 @@ -/++ - Author: Aziz Köksal - License: GPL3 -+/ -module dil.Parser; -import dil.lexer.Lexer; -import dil.SyntaxTree; -import dil.Token; -import dil.Messages; -import dil.Information; -import dil.Declarations; -import dil.Statements; -import dil.Expressions; -import dil.Types; -import dil.Enums; -import dil.CompilerInfo; -import dil.IdTable; -import common; - -/++ - The Parser produces an abstract syntax tree (AST) by analyzing - the tokens of the provided source code. -+/ -class Parser -{ - Lexer lx; - Token* token; /// Current non-whitespace token. - Token* prevToken; /// Previous non-whitespace token. - - InfoManager infoMan; - ParserError[] errors; - - ImportDeclaration[] imports; /// ImportDeclarations in the source text. - - LinkageType linkageType; - Protection protection; - StorageClass storageClass; - uint alignSize = DEFAULT_ALIGN_SIZE; - - private alias TOK T; - private alias TypeNode Type; - - /++ - Construct a Parser object. - Params: - text = the UTF-8 source code. - filePath = the path to the source code; used for error messages. - +/ - this(char[] srcText, string filePath, InfoManager infoMan = null) - { - this.infoMan = infoMan; - lx = new Lexer(srcText, filePath, infoMan); - } - - protected void init() - { - nT(); - prevToken = token; - } - - void nT() - { - prevToken = token; - do - { - lx.nextToken(); - token = lx.token; - } while (token.isWhitespace) // Skip whitespace - } - - /++ - Start the parser and return the parsed Declarations. - +/ - Declarations start() - { - init(); - auto begin = token; - auto decls = new Declarations; - if (token.type == T.Module) - decls ~= parseModuleDeclaration(); - decls.addOptChildren(parseDeclarationDefinitions()); - set(decls, begin); - return decls; - } - - /++ - Start the parser and return the parsed Expression. - +/ - Expression start2() - { - init(); - return parseExpression(); - } - - uint trying; - uint errorCount; - - /++ - This method executes the delegate parseMethod and when an error occurred - the state of the lexer and parser are restored. - +/ - ReturnType try_(ReturnType)(ReturnType delegate() parseMethod, out bool success) - { - auto oldToken = this.token; - auto oldPrevToken = this.prevToken; - auto oldCount = this.errorCount; - - ++trying; - auto result = parseMethod(); - --trying; - // Check if an error occurred. - if (errorCount != oldCount) - { - // Restore members. - token = oldToken; - prevToken = oldPrevToken; - lx.token = oldToken; - errorCount = oldCount; - success = false; - } - else - success = true; - return result; - } - - /++ - Sets the begin and end tokens of an AST node. - +/ - Class set(Class)(Class node, Token* begin) - { - node.setTokens(begin, this.prevToken); - return node; - } - - /++ - Returns true if set() has been called on a node. - +/ - bool isNodeSet(Node node) - { - return node.begin !is null && node.end !is null; - } - - TOK peekNext() - { - Token* next = token; - do - lx.peek(next); - while (next.isWhitespace) // Skip whitespace - return next.type; - } - - TOK peekAfter(ref Token* next) - { - assert(next !is null); - do - lx.peek(next); - while (next.isWhitespace) // Skip whitespace - return next.type; - } - - /// Skips the current token if its type matches tok and returns true. - bool skipped()(TOK tok) // Templatized, so it's inlined. - { - return token.type == tok ? (nT(), true) : false; - } - - /++++++++++++++++++++++++++++++ - + Declaration parsing methods + - ++++++++++++++++++++++++++++++/ - - Declaration parseModuleDeclaration() - { - auto begin = token; - ModuleFQN moduleFQN; - do - { - nT(); - moduleFQN ~= requireIdentifier(MSG.ExpectedModuleIdentifier); - } while (token.type == T.Dot) - require(T.Semicolon); - return set(new ModuleDeclaration(moduleFQN), begin); - } - - /++ - Parse DeclarationDefinitions until the end of file is hit. - DeclDefs: - DeclDef - DeclDefs - +/ - Declaration[] parseDeclarationDefinitions() - { - Declaration[] decls; - while (token.type != T.EOF) - decls ~= parseDeclarationDefinition(); - return decls; - } - - /++ - Parse the body of a template, class, interface, struct or union. - DeclDefsBlock: - { } - { DeclDefs } - +/ - Declarations parseDeclarationDefinitionsBody() - { - // Save attributes. - auto linkageType = this.linkageType; - auto protection = this.protection; - auto storageClass = this.storageClass; - // Clear attributes. - this.linkageType = LinkageType.None; - this.protection = Protection.None; - this.storageClass = StorageClass.None; - - // Parse body. - auto begin = token; - auto decls = new Declarations; - require(T.LBrace); - while (token.type != T.RBrace && token.type != T.EOF) - decls ~= parseDeclarationDefinition(); - require(T.RBrace); - set(decls, begin); - - // Restore original values. - this.linkageType = linkageType; - this.protection = protection; - this.storageClass = storageClass; - - return decls; - } - - Declaration parseDeclarationDefinition() - out(decl) - { assert(isNodeSet(decl)); } - body - { - auto begin = token; - Declaration decl; - switch (token.type) - { - case T.Align, - T.Pragma, - // Protection attributes - T.Export, - T.Private, - T.Package, - T.Protected, - T.Public: - decl = parseAttributeSpecifier(); - break; - // Storage classes - case T.Extern, - T.Deprecated, - T.Override, - T.Abstract, - T.Synchronized, - //T.Static, - T.Final, - T.Const, - //T.Invariant, // D 2.0 - T.Auto, - T.Scope: - case_StaticAttribute: - case_InvariantAttribute: // D 2.0 - return parseStorageAttribute(); - case T.Alias: - nT(); - // TODO: parse StorageClasses? - decl = new AliasDeclaration(parseVariableOrFunction()); - break; - case T.Typedef: - nT(); - // TODO: parse StorageClasses? - decl = new TypedefDeclaration(parseVariableOrFunction()); - break; - case T.Static: - switch (peekNext()) - { - case T.Import: - goto case_Import; - case T.This: - decl = parseStaticConstructorDeclaration(); - break; - case T.Tilde: - decl = parseStaticDestructorDeclaration(); - break; - case T.If: - decl = parseStaticIfDeclaration(); - break; - case T.Assert: - decl = parseStaticAssertDeclaration(); - break; - default: - goto case_StaticAttribute; - } - break; - case T.Import: - case_Import: - decl = parseImportDeclaration(); - imports ~= CastTo!(ImportDeclaration)(decl); - // Handle specially. StorageClass mustn't be set. - decl.setProtection(this.protection); - return set(decl, begin); - case T.Enum: - decl = parseEnumDeclaration(); - break; - case T.Class: - decl = parseClassDeclaration(); - break; - case T.Interface: - decl = parseInterfaceDeclaration(); - break; - case T.Struct, T.Union: - decl = parseAggregateDeclaration(); - break; - case T.This: - decl = parseConstructorDeclaration(); - break; - case T.Tilde: - decl = parseDestructorDeclaration(); - break; - case T.Invariant: - version(D2) - { - auto next = token; - if (peekAfter(next) == T.LParen) - { - if (peekAfter(next) != T.RParen) - goto case_Declaration; - } - else - goto case_InvariantAttribute; - } - decl = parseInvariantDeclaration(); - break; - case T.Unittest: - decl = parseUnittestDeclaration(); - break; - case T.Debug: - decl = parseDebugDeclaration(); - break; - case T.Version: - decl = parseVersionDeclaration(); - break; - case T.Template: - decl = parseTemplateDeclaration(); - break; - case T.New: - decl = parseNewDeclaration(); - break; - case T.Delete: - decl = parseDeleteDeclaration(); - break; - case T.Mixin: - decl = parseMixin!(MixinDeclaration)(); - break; - case T.Semicolon: - nT(); - decl = new EmptyDeclaration(); - break; - // Declaration - case T.Identifier, T.Dot, T.Typeof: - case_Declaration: - return parseVariableOrFunction(this.storageClass, this.protection, this.linkageType); - /+case T.Module: - // TODO: Error: module is optional and can appear only once at the top of the source file. - break;+/ - default: - if (token.isIntegralType) - goto case_Declaration; - - decl = new IllegalDeclaration(); - // Skip to next valid token. - do - nT(); - while (!token.isDeclDefStart && - token.type != T.RBrace && - token.type != T.EOF) - auto text = Token.textSpan(begin, this.prevToken); - error(begin, MSG.IllegalDeclaration ~ text); - } - decl.setProtection(this.protection); - decl.setStorageClass(this.storageClass); - assert(!isNodeSet(decl)); - set(decl, begin); - return decl; - } - - /++ - DeclarationsBlock: - : DeclDefs - { } - { DeclDefs } - DeclDef - +/ - Declaration parseDeclarationsBlock(bool noColon = false) - { - Declaration d; - switch (token.type) - { - case T.LBrace: - auto begin = token; - nT(); - auto decls = new Declarations; - while (token.type != T.RBrace && token.type != T.EOF) - decls ~= parseDeclarationDefinition(); - require(T.RBrace); - d = set(decls, begin); - break; - case T.Colon: - if (noColon == true) - goto default; - nT(); - auto begin = token; - auto decls = new Declarations; - while (token.type != T.RBrace && token.type != T.EOF) - decls ~= parseDeclarationDefinition(); - d = set(decls, begin); - break; - default: - d = parseDeclarationDefinition(); - } - assert(isNodeSet(d)); - return d; - } - - Declaration parseDeclarationsBlockNoColon() - { - return parseDeclarationsBlock(true); - } - - /++ - Parses either a VariableDeclaration or a FunctionDeclaration. - Params: - stc = previously parsed storage classes - protection = previously parsed protection attribute - linkType = previously parsed linkage type - testAutoDeclaration = whether to check for an AutoDeclaration - optionalParameterList = a hint for how to parse C-style function pointers - +/ - Declaration parseVariableOrFunction(StorageClass stc = StorageClass.None, - Protection protection = Protection.None, - LinkageType linkType = LinkageType.None, - bool testAutoDeclaration = false, - bool optionalParameterList = true) - { - auto begin = token; - Type type; - Identifier* ident; - - // Check for AutoDeclaration: StorageClasses Identifier = - if (testAutoDeclaration && - token.type == T.Identifier && - peekNext() == T.Assign) - { - ident = token.ident; - nT(); - } - else - { - type = parseType(); // VariableType or ReturnType - if (token.type == T.LParen) - { - // C-style function pointers make the grammar ambiguous. - // We have to treat them specially at function scope. - // Example: - // void foo() { - // // A pointer to a function taking an integer and returning 'some_type'. - // some_type (*p_func)(int); - // // In the following case precedence is given to a CallExpression. - // something(*p); // 'something' may be a function/method or an object having opCall overloaded. - // } - // // A pointer to a function taking no parameters and returning 'something'. - // something(*p); - type = parseCFunctionPointerType(type, ident, optionalParameterList); - } - else if (peekNext() == T.LParen) - { // Type FunctionName ( ParameterList ) FunctionBody - ident = requireIdentifier(MSG.ExpectedFunctionName); - ident || nT(); // Skip non-identifier token. - assert(token.type == T.LParen); - // It's a function declaration - TemplateParameters tparams; - if (tokenAfterParenIs(T.LParen)) - { - // ( TemplateParameterList ) ( ParameterList ) - tparams = parseTemplateParameterList(); - } - - auto params = parseParameterList(); - version(D2) - { - switch (token.type) - { - case T.Const: - stc |= StorageClass.Const; - nT(); - break; - case T.Invariant: - stc |= StorageClass.Invariant; - nT(); - break; - default: - } - } - // ReturnType FunctionName ( ParameterList ) - auto funcBody = parseFunctionBody(); - auto d = new FunctionDeclaration(type, ident, tparams, params, funcBody); - d.setStorageClass(stc); - d.setLinkageType(linkType); - d.setProtection(protection); - return set(d, begin); - } - else - { - // Type VariableName DeclaratorSuffix - ident = requireIdentifier(MSG.ExpectedVariableName); - type = parseDeclaratorSuffix(type); - } - } - - // It's a variable declaration. - Identifier*[] idents = [ident]; - Expression[] values; - goto LenterLoop; // We've already parsed an identifier. Jump to if statement and check for initializer. - while (token.type == T.Comma) - { - nT(); - idents ~= requireIdentifier(MSG.ExpectedVariableName); - LenterLoop: - if (skipped(T.Assign)) - values ~= parseInitializer(); - else - values ~= null; - } - require(T.Semicolon); - auto d = new VariableDeclaration(type, idents, values); - d.setStorageClass(stc); - d.setLinkageType(linkType); - d.setProtection(protection); - return set(d, begin); - } - - Expression parseInitializer() - { - if (token.type == T.Void) - { - auto begin = token; - auto next = peekNext(); - if (next == T.Comma || next == T.Semicolon) - { - nT(); - return set(new VoidInitializer(), begin); - } - } - return parseNonVoidInitializer(); - } - - Expression parseNonVoidInitializer() - { - auto begin = token; - Expression init; - switch (token.type) - { - case T.LBracket: - // ArrayInitializer: - // [ ] - // [ ArrayMemberInitializations ] - Expression[] keys; - Expression[] values; - - nT(); - while (token.type != T.RBracket) - { - auto e = parseNonVoidInitializer(); - if (skipped(T.Colon)) - { - keys ~= e; - values ~= parseNonVoidInitializer(); - } - else - { - keys ~= null; - values ~= e; - } - - if (token.type != T.Comma) - break; - nT(); - } - require(T.RBracket); - init = new ArrayInitializer(keys, values); - break; - case T.LBrace: - // StructInitializer: - // { } - // { StructMemberInitializers } - Expression parseStructInitializer() - { - Identifier*[] idents; - Expression[] values; - - nT(); - while (token.type != T.RBrace) - { - if (token.type == T.Identifier && - // Peek for colon to see if this is a member identifier. - peekNext() == T.Colon) - { - idents ~= token.ident; - nT(), nT(); // Skip Identifier : - } - else - idents ~= null; - - // NonVoidInitializer - values ~= parseNonVoidInitializer(); - - if (token.type != T.Comma) - break; - nT(); - } - require(T.RBrace); - return new StructInitializer(idents, values); - } - - bool success; - auto si = try_(&parseStructInitializer, success); - if (success) - { - init = si; - break; - } - assert(token.type == T.LBrace); - //goto default; - default: - init = parseAssignExpression(); - } - set(init, begin); - return init; - } - - FunctionBody parseFunctionBody() - { - auto begin = token; - auto func = new FunctionBody; - while (1) - { - switch (token.type) - { - case T.LBrace: - func.funcBody = parseStatements(); - break; - case T.Semicolon: - nT(); - break; - case T.In: - if (func.inBody) - error(MID.InContract); - nT(); - func.inBody = parseStatements(); - continue; - case T.Out: - if (func.outBody) - error(MID.OutContract); - nT(); - if (skipped(T.LParen)) - { - func.outIdent = requireIdentifier(MSG.ExpectedAnIdentifier); - require(T.RParen); - } - func.outBody = parseStatements(); - continue; - case T.Body: - nT(); - goto case T.LBrace; - default: - error(token, MSG.ExpectedFunctionBody, token.srcText); - } - break; // Exit loop. - } - set(func, begin); - func.finishConstruction(); - return func; - } - - LinkageType parseLinkageType() - { - LinkageType linkageType; - if (token.type != T.LParen) - return linkageType; - - nT(); // Skip ( - if (token.type == T.RParen) - { - nT(); - error(MID.MissingLinkageType); - return linkageType; - } - - auto identTok = requireId(); - - ID identID = identTok ? identTok.ident.identID : ID.Null; - - switch (identID) - { - case ID.C: - if (skipped(T.PlusPlus)) - { - linkageType = LinkageType.Cpp; - break; - } - linkageType = LinkageType.C; - break; - case ID.D: - linkageType = LinkageType.D; - break; - case ID.Windows: - linkageType = LinkageType.Windows; - break; - case ID.Pascal: - linkageType = LinkageType.Pascal; - break; - case ID.System: - linkageType = LinkageType.System; - break; - default: - error(MID.UnrecognizedLinkageType, token.srcText); - } - require(T.RParen); - return linkageType; - } - - void checkLinkageType(ref LinkageType prev_lt, LinkageType lt, Token* begin) - { - if (prev_lt == LinkageType.None) - prev_lt = lt; - else - // TODO: create new msg RedundantLinkageType. - error(begin, MSG.RedundantLinkageType ~ Token.textSpan(begin, this.prevToken)); - } - - Declaration parseStorageAttribute() - { - StorageClass stc, stc_tmp; - LinkageType prev_linkageType; - - auto saved_storageClass = this.storageClass; // Save. - // Nested function. - Declaration parse() - { - Declaration decl; - auto begin = token; - switch (token.type) - { - case T.Extern: - if (peekNext() != T.LParen) - { - stc_tmp = StorageClass.Extern; - goto Lcommon; - } - - nT(); - auto linkageType = parseLinkageType(); - checkLinkageType(prev_linkageType, linkageType, begin); - - auto saved = this.linkageType; // Save. - this.linkageType = linkageType; // Set. - decl = new LinkageDeclaration(linkageType, parse()); - set(decl, begin); - this.linkageType = saved; // Restore. - break; - case T.Override: - stc_tmp = StorageClass.Override; - goto Lcommon; - case T.Deprecated: - stc_tmp = StorageClass.Deprecated; - goto Lcommon; - case T.Abstract: - stc_tmp = StorageClass.Abstract; - goto Lcommon; - case T.Synchronized: - stc_tmp = StorageClass.Synchronized; - goto Lcommon; - case T.Static: - stc_tmp = StorageClass.Static; - goto Lcommon; - case T.Final: - stc_tmp = StorageClass.Final; - goto Lcommon; - case T.Const: - version(D2) - { - if (peekNext() == T.LParen) - goto case_Declaration; - } - stc_tmp = StorageClass.Const; - goto Lcommon; - version(D2) - { - case T.Invariant: // D 2.0 - auto next = token; - if (peekAfter(next) == T.LParen) - { - if (peekAfter(next) != T.RParen) - goto case_Declaration; // invariant ( Type ) - decl = parseDeclarationDefinition(); // invariant ( ) - decl.setStorageClass(stc); - break; - } - // invariant as StorageClass. - stc_tmp = StorageClass.Invariant; - goto Lcommon; - } - case T.Auto: - stc_tmp = StorageClass.Auto; - goto Lcommon; - case T.Scope: - stc_tmp = StorageClass.Scope; - goto Lcommon; - Lcommon: - // Issue error if redundant. - if (stc & stc_tmp) - error(MID.RedundantStorageClass, token.srcText); - else - stc |= stc_tmp; - - auto tok = token.type; - nT(); - decl = new StorageClassDeclaration(stc_tmp, tok, parse()); - set(decl, begin); - break; - case T.Identifier: - case_Declaration: - // This could be a normal Declaration or an AutoDeclaration - decl = parseVariableOrFunction(stc, this.protection, prev_linkageType, true); - break; - default: - this.storageClass = stc; // Set. - decl = parseDeclarationsBlock(); - this.storageClass = saved_storageClass; // Reset. - } - assert(isNodeSet(decl)); - return decl; - } - return parse(); - } - - uint parseAlignAttribute() - { - assert(token.type == T.Align); - nT(); // Skip align keyword. - uint size = DEFAULT_ALIGN_SIZE; // Global default. - if (skipped(T.LParen)) - { - if (token.type == T.Int32) - (size = token.int_), nT(); - else - expected(T.Int32); - require(T.RParen); - } - return size; - } - - Declaration parseAttributeSpecifier() - { - Declaration decl; - - switch (token.type) - { - case T.Align: - uint alignSize = parseAlignAttribute(); - auto saved = this.alignSize; // Save. - this.alignSize = alignSize; // Set. - decl = new AlignDeclaration(alignSize, parseDeclarationsBlock()); - this.alignSize = saved; // Restore. - break; - case T.Pragma: - // Pragma: - // pragma ( Identifier ) - // pragma ( Identifier , ExpressionList ) - nT(); - Identifier* ident; - Expression[] args; - - require(T.LParen); - ident = requireIdentifier(MSG.ExpectedPragmaIdentifier); - - if (skipped(T.Comma)) - args = parseExpressionList(); - require(T.RParen); - - decl = new PragmaDeclaration(ident, args, parseDeclarationsBlock()); - break; - default: - // Protection attributes - Protection prot; - switch (token.type) - { - case T.Private: - prot = Protection.Private; break; - case T.Package: - prot = Protection.Package; break; - case T.Protected: - prot = Protection.Protected; break; - case T.Public: - prot = Protection.Public; break; - case T.Export: - prot = Protection.Export; break; - default: - assert(0); - } - nT(); - auto saved = this.protection; // Save. - this.protection = prot; // Set. - decl = new ProtectionDeclaration(prot, parseDeclarationsBlock()); - this.protection = saved; // Restore. - } - return decl; - } - - Declaration parseImportDeclaration() - { - assert(token.type == T.Import || token.type == T.Static); - bool isStatic = skipped(T.Static); - assert(token.type == T.Import); - nT(); // Skip import keyword. - - ModuleFQN[] moduleFQNs; - Identifier*[] moduleAliases; - Identifier*[] bindNames; - Identifier*[] bindAliases; - - while (1) - { - ModuleFQN moduleFQN; - Identifier* moduleAlias; - - // AliasName = ModuleName - if (peekNext() == T.Assign) - { - moduleAlias = requireIdentifier(MSG.ExpectedAliasModuleName); - nT(); // Skip = - } - - // Identifier(.Identifier)* - while (1) - { - moduleFQN ~= requireIdentifier(MSG.ExpectedModuleIdentifier); - if (token.type != T.Dot) - break; - nT(); - } - - // Push identifiers. - moduleFQNs ~= moduleFQN; - moduleAliases ~= moduleAlias; - - if (token.type != T.Comma) - break; - nT(); - } - - if (token.type == T.Colon) - { - // BindAlias = BindName(, BindAlias = BindName)*; - // BindName(, BindName)*; - do - { - nT(); - Identifier* bindAlias; - // BindAlias = BindName - if (peekNext() == T.Assign) - { - bindAlias = requireIdentifier(MSG.ExpectedAliasImportName); - nT(); // Skip = - } - // Push identifiers. - bindNames ~= requireIdentifier(MSG.ExpectedImportName); - bindAliases ~= bindAlias; - } while (token.type == T.Comma) - } - - require(T.Semicolon); - - return new ImportDeclaration(moduleFQNs, moduleAliases, bindNames, bindAliases, isStatic); - } - - Declaration parseEnumDeclaration() - { - assert(token.type == T.Enum); - nT(); // Skip enum keyword. - - Identifier* enumName; - Type baseType; - EnumMember[] members; - bool hasBody; - - enumName = optionalIdentifier(); - - if (skipped(T.Colon)) - baseType = parseBasicType(); - - if (skipped(T.Semicolon)) - { - if (enumName is null) - expected(T.Identifier); - } - else if (skipped(T.LBrace)) - { - hasBody = true; - while (token.type != T.RBrace) - { - auto begin = token; - auto name = requireIdentifier(MSG.ExpectedEnumMember); - Expression value; - - if (skipped(T.Assign)) - value = parseAssignExpression(); - else - value = null; - - members ~= set(new EnumMember(name, value), begin); - - if (token.type != T.Comma) - break; - nT(); // Skip , - } - require(T.RBrace); - } - else - error(token, MSG.ExpectedEnumBody, token.srcText); - - return new EnumDeclaration(enumName, baseType, members, hasBody); - } - - Declaration parseClassDeclaration() - { - assert(token.type == T.Class); - nT(); // Skip class keyword. - - Identifier* className; - TemplateParameters tparams; - BaseClass[] bases; - Declarations decls; - - className = requireIdentifier(MSG.ExpectedClassName); - - if (token.type == T.LParen) - tparams = parseTemplateParameterList(); - - if (token.type == T.Colon) - bases = parseBaseClasses(); - - if (skipped(T.Semicolon)) - { - if (bases.length != 0) - error(MID.BaseClassInForwardDeclaration); - } - else if (token.type == T.LBrace) - decls = parseDeclarationDefinitionsBody(); - else - error(token, MSG.ExpectedClassBody, token.srcText); - - return new ClassDeclaration(className, tparams, bases, decls); - } - - BaseClass[] parseBaseClasses(bool colonLeadsOff = true) - { - if (colonLeadsOff) - { - assert(token.type == T.Colon); - nT(); // Skip colon - } - - BaseClass[] bases; - - while (1) - { - Protection prot = Protection.Public; - switch (token.type) - { - case T.Identifier, T.Dot, T.Typeof: goto LparseBasicType; - case T.Private: prot = Protection.Private; break; - case T.Protected: prot = Protection.Protected; break; - case T.Package: prot = Protection.Package; break; - case T.Public: /*prot = Protection.Public;*/ break; - default: - error(MID.ExpectedBaseClasses, token.srcText); - return bases; - } - nT(); // Skip protection attribute. - LparseBasicType: - auto begin = token; - auto type = parseBasicType(); - //if (type.tid != TID.DotList) - // TODO: issue error msg. base classes can only be one or more identifiers or template instances separated by dots. - bases ~= set(new BaseClass(prot, type), begin); - if (token.type != T.Comma) - break; - nT(); - } - return bases; - } - - Declaration parseInterfaceDeclaration() - { - assert(token.type == T.Interface); - nT(); // Skip interface keyword. - - Identifier* name; - TemplateParameters tparams; - BaseClass[] bases; - Declarations decls; - - name = requireIdentifier(MSG.ExpectedInterfaceName); - - if (token.type == T.LParen) - tparams = parseTemplateParameterList(); - - if (token.type == T.Colon) - bases = parseBaseClasses(); - - if (skipped(T.Semicolon)) - { - if (bases.length != 0) - error(MID.BaseClassInForwardDeclaration); - } - else if (token.type == T.LBrace) - decls = parseDeclarationDefinitionsBody(); - else - error(token, MSG.ExpectedInterfaceBody, token.srcText); - - return new InterfaceDeclaration(name, tparams, bases, decls); - } - - Declaration parseAggregateDeclaration() - { - assert(token.type == T.Struct || token.type == T.Union); - TOK tok = token.type; - nT(); // Skip struct or union keyword. - - Identifier* name; - TemplateParameters tparams; - Declarations decls; - - name = optionalIdentifier(); - - if (name && token.type == T.LParen) - tparams = parseTemplateParameterList(); - - if (skipped(T.Semicolon)) - { - //if (name.length == 0) - // TODO: error: forward declarations must have a name. - } - else if (token.type == T.LBrace) - decls = parseDeclarationDefinitionsBody(); - else - expected(T.LBrace); // TODO: better error msg - - if (tok == T.Struct) - { - auto sd = new StructDeclaration(name, tparams, decls); - sd.setAlignSize(this.alignSize); - return sd; - } - else - return new UnionDeclaration(name, tparams, decls); - } - - Declaration parseConstructorDeclaration() - { - assert(token.type == T.This); - nT(); // Skip 'this' keyword. - auto parameters = parseParameterList(); - auto funcBody = parseFunctionBody(); - return new ConstructorDeclaration(parameters, funcBody); - } - - Declaration parseDestructorDeclaration() - { - assert(token.type == T.Tilde); - nT(); // Skip ~ - require(T.This); - require(T.LParen); - require(T.RParen); - auto funcBody = parseFunctionBody(); - return new DestructorDeclaration(funcBody); - } - - Declaration parseStaticConstructorDeclaration() - { - assert(token.type == T.Static); - nT(); // Skip static keyword. - nT(); // Skip 'this' keyword. - require(T.LParen); - require(T.RParen); - auto funcBody = parseFunctionBody(); - return new StaticConstructorDeclaration(funcBody); - } - - Declaration parseStaticDestructorDeclaration() - { - assert(token.type == T.Static); - nT(); // Skip static keyword. - nT(); // Skip ~ - require(T.This); - require(T.LParen); - require(T.RParen); - auto funcBody = parseFunctionBody(); - return new StaticDestructorDeclaration(funcBody); - } - - Declaration parseInvariantDeclaration() - { - assert(token.type == T.Invariant); - nT(); // Skip invariant keyword. - // Optional () for getting ready porting to D 2.0 - if (token.type == T.LParen) - requireNext(T.RParen); - auto funcBody = parseFunctionBody(); - return new InvariantDeclaration(funcBody); - } - - Declaration parseUnittestDeclaration() - { - assert(token.type == T.Unittest); - nT(); // Skip unittest keyword. - auto funcBody = parseFunctionBody(); - return new UnittestDeclaration(funcBody); - } - - Token* parseIdentOrInt() - { - if (skipped(T.Int32) || skipped(T.Identifier)) - return this.prevToken; - error(token, MSG.ExpectedIdentOrInt, token.srcText); - return null; - } - - Declaration parseDebugDeclaration() - { - assert(token.type == T.Debug); - nT(); // Skip debug keyword. - - Token* spec; - Token* cond; - Declaration decls, elseDecls; - - if (skipped(T.Assign)) - { // debug = Integer ; - // debug = Identifier ; - spec = parseIdentOrInt(); - require(T.Semicolon); - } - else - { // ( Condition ) - if (skipped(T.LParen)) - { - cond = parseIdentOrInt(); - require(T.RParen); - } - // debug DeclarationsBlock - // debug ( Condition ) DeclarationsBlock - decls = parseDeclarationsBlockNoColon(); - // else DeclarationsBlock - if (skipped(T.Else)) - elseDecls = parseDeclarationsBlockNoColon(); - } - - return new DebugDeclaration(spec, cond, decls, elseDecls); - } - - Declaration parseVersionDeclaration() - { - assert(token.type == T.Version); - nT(); // Skip version keyword. - - Token* spec; - Token* cond; - Declaration decls, elseDecls; - - if (skipped(T.Assign)) - { // version = Integer ; - // version = Identifier ; - spec = parseIdentOrInt(); - require(T.Semicolon); - } - else - { // ( Condition ) - require(T.LParen); - cond = parseIdentOrInt(); - require(T.RParen); - // version ( Condition ) DeclarationsBlock - decls = parseDeclarationsBlockNoColon(); - // else DeclarationsBlock - if (skipped(T.Else)) - elseDecls = parseDeclarationsBlockNoColon(); - } - - return new VersionDeclaration(spec, cond, decls, elseDecls); - } - - Declaration parseStaticIfDeclaration() - { - assert(token.type == T.Static); - nT(); // Skip static keyword. - nT(); // Skip if keyword. - - Expression condition; - Declaration ifDecls, elseDecls; - - require(T.LParen); - condition = parseAssignExpression(); - require(T.RParen); - - ifDecls = parseDeclarationsBlockNoColon(); - - if (skipped(T.Else)) - elseDecls = parseDeclarationsBlockNoColon(); - - return new StaticIfDeclaration(condition, ifDecls, elseDecls); - } - - Declaration parseStaticAssertDeclaration() - { - assert(token.type == T.Static); - nT(); // Skip static keyword. - nT(); // Skip assert keyword. - Expression condition, message; - require(T.LParen); - condition = parseAssignExpression(); - if (skipped(T.Comma)) - message = parseAssignExpression(); - require(T.RParen); - require(T.Semicolon); - return new StaticAssertDeclaration(condition, message); - } - - Declaration parseTemplateDeclaration() - { - assert(token.type == T.Template); - nT(); // Skip template keyword. - auto templateName = requireIdentifier(MSG.ExpectedTemplateName); - auto templateParams = parseTemplateParameterList(); - auto decls = parseDeclarationDefinitionsBody(); - return new TemplateDeclaration(templateName, templateParams, decls); - } - - Declaration parseNewDeclaration() - { - assert(token.type == T.New); - nT(); // Skip new keyword. - auto parameters = parseParameterList(); - auto funcBody = parseFunctionBody(); - return new NewDeclaration(parameters, funcBody); - } - - Declaration parseDeleteDeclaration() - { - assert(token.type == T.Delete); - nT(); // Skip delete keyword. - auto parameters = parseParameterList(); - // TODO: only one parameter of type void* allowed. Check in parsing or semantic phase? - auto funcBody = parseFunctionBody(); - return new DeleteDeclaration(parameters, funcBody); - } - - Type parseTypeofType() - { - assert(token.type == T.Typeof); - Type type; - requireNext(T.LParen); - switch (token.type) - { - version(D2) - { - case T.Return: - nT(); - type = new TypeofType(); - break; - } - default: - type = new TypeofType(parseExpression()); - } - require(T.RParen); - return type; - } - - /+ - DotListExpression: - . DotListItems - DotListItems - Typeof - Typeof . DotListItems - DotListItems: - DotListItem - DotListItem . DotListItems - DotListItem: - Identifier - TemplateInstance - NewExpression - TemplateInstance: - Identifier !( TemplateArguments ) - +/ - DotListExpression parseDotListExpression() - { - assert(token.type == T.Identifier || token.type == T.Dot || token.type == T.Typeof); - auto begin = token; - Expression[] identList; - if (skipped(T.Dot)) - identList ~= set(new DotExpression(), begin); - else if (token.type == T.Typeof) - { - auto type = parseTypeofType(); - set(type, begin); - identList ~= set(new TypeofExpression(type), begin); - if (!skipped(T.Dot)) - goto Lreturn; - } - - while (1) - { - begin = token; - auto ident = requireIdentifier(MSG.ExpectedAnIdentifier); - Expression e; - if (token.type == T.Not && peekNext() == T.LParen) // Identifier !( TemplateArguments ) - { - nT(); // Skip !. - auto tparams = parseTemplateArguments(); - e = new TemplateInstanceExpression(ident, tparams); - } - else // Identifier - e = new IdentifierExpression(ident); - - identList ~= set(e, begin); - - LnewExpressionLoop: - if (!skipped(T.Dot)) - break; - - if (token.type == T.New) - { - identList ~= parseNewExpression(); - goto LnewExpressionLoop; - } - } - - Lreturn: - return new DotListExpression(identList); - } - - /+ - DotListType: - . TypeItems - TypeItems - Typeof - Typeof . TypeItems - TypeItems: - TypeItem - TypeItem . TypeItems - TypeItem: - Identifier - TemplateInstance - TemplateInstance: - Identifier !( TemplateArguments ) - +/ - DotListType parseDotListType() - { - auto begin = token; - Type[] identList; - if (skipped(T.Dot)) - identList ~= set(new DotType(), begin); - else if (token.type == T.Typeof) - { - identList ~= set(parseTypeofType(), begin); - if (!skipped(T.Dot)) - goto Lreturn; - } - - do - { - begin = token; - auto ident = requireIdentifier(MSG.ExpectedAnIdentifier); - Type t; - // NB.: Currently Types can't be followed by "!=" - // so no need to peek for "(" when parsing TemplateInstances. - if (skipped(T.Not)/+ && peekNext() == T.LParen+/) // Identifier !( TemplateArguments ) - t = new TemplateInstanceType(ident, parseTemplateArguments()); - else // Identifier - t = new IdentifierType(ident); - identList ~= set(t, begin); - } while(skipped(T.Dot)) - Lreturn: - return new DotListType(identList); - } - - /* - TemplateMixin: - mixin ( AssignExpression ) ; - mixin TemplateIdentifier ; - mixin TemplateIdentifier MixinIdentifier ; - mixin TemplateIdentifier !( TemplateArguments ) ; - mixin TemplateIdentifier !( TemplateArguments ) MixinIdentifier ; - */ - Class parseMixin(Class)() - { - assert(token.type == T.Mixin); - nT(); // Skip mixin keyword. - - static if (is(Class == MixinDeclaration)) - { - if (skipped(T.LParen)) - { - // TODO: What about mixin(...).ident;? - auto e = parseAssignExpression(); - require(T.RParen); - require(T.Semicolon); - return new MixinDeclaration(e); - } - } - - auto begin = token; - Expression[] templateIdent; - Identifier* mixinIdent; - - // This code is similar to parseDotListType(). - if (skipped(T.Dot)) - templateIdent ~= set(new DotExpression(), begin); - - do - { - begin = token; - auto ident = requireIdentifier(MSG.ExpectedAnIdentifier); - Expression e; - if (skipped(T.Not)) // Identifier !( TemplateArguments ) - { - // No need to peek for T.LParen. This must be a template instance. - auto tparams = parseTemplateArguments(); - e = new TemplateInstanceExpression(ident, tparams); - } - else // Identifier - e = new IdentifierExpression(ident); - templateIdent ~= set(e, begin); - } while (skipped(T.Dot)) - - mixinIdent = optionalIdentifier(); - require(T.Semicolon); - - return new Class(templateIdent, mixinIdent); - } - - /+++++++++++++++++++++++++++++ - + Statement parsing methods + - +++++++++++++++++++++++++++++/ - - Statements parseStatements() - { - auto begin = token; - require(T.LBrace); - auto statements = new Statements(); - while (token.type != T.RBrace && token.type != T.EOF) - statements ~= parseStatement(); - require(T.RBrace); - return set(statements, begin); - } - - Statement parseStatement() - { - auto begin = token; - Statement s; - Declaration d; - - if (token.isIntegralType) - { - d = parseVariableOrFunction(); - goto LreturnDeclarationStatement; - } - - switch (token.type) - { - case T.Align: - uint size = parseAlignAttribute(); - // Restrict align attribute to structs in parsing phase. - StructDeclaration structDecl; - if (token.type == T.Struct) - { - auto begin2 = token; - structDecl = CastTo!(StructDeclaration)(parseAggregateDeclaration()); - structDecl.setAlignSize(size); - set(structDecl, begin2); - } - else - expected(T.Struct); - - d = new AlignDeclaration(size, structDecl ? cast(Declaration)structDecl : new Declarations); - goto LreturnDeclarationStatement; - /+ Not applicable for statements. - T.Private, T.Package, T.Protected, T.Public, T.Export, - T.Deprecated, T.Override, T.Abstract,+/ - case T.Extern, - T.Final, - T.Const, - T.Auto: - //T.Scope - //T.Static - case_parseAttribute: - s = parseAttributeStatement(); - return s; - case T.Identifier: - if (peekNext() == T.Colon) - { - auto ident = token.ident; - nT(), nT(); // Skip Identifier : - s = new LabeledStatement(ident, parseNoScopeOrEmptyStatement()); - break; - } - goto case T.Dot; - case T.Dot, T.Typeof: - bool success; - d = try_(delegate { - return parseVariableOrFunction(StorageClass.None, - Protection.None, - LinkageType.None, false, false); - }, success - ); - if (success) - goto LreturnDeclarationStatement; // Declaration - else - goto case_parseExpressionStatement; // Expression - - case T.If: - s = parseIfStatement(); - break; - case T.While: - s = parseWhileStatement(); - break; - case T.Do: - s = parseDoWhileStatement(); - break; - case T.For: - s = parseForStatement(); - break; - case T.Foreach, T.Foreach_reverse: - s = parseForeachStatement(); - break; - case T.Switch: - s = parseSwitchStatement(); - break; - case T.Case: - s = parseCaseStatement(); - break; - case T.Default: - s = parseDefaultStatement(); - break; - case T.Continue: - s = parseContinueStatement(); - break; - case T.Break: - s = parseBreakStatement(); - break; - case T.Return: - s = parseReturnStatement(); - break; - case T.Goto: - s = parseGotoStatement(); - break; - case T.With: - s = parseWithStatement(); - break; - case T.Synchronized: - s = parseSynchronizedStatement(); - break; - case T.Try: - s = parseTryStatement(); - break; - case T.Throw: - s = parseThrowStatement(); - break; - case T.Scope: - if (peekNext() != T.LParen) - goto case_parseAttribute; - s = parseScopeGuardStatement(); - break; - case T.Volatile: - s = parseVolatileStatement(); - break; - case T.Asm: - s = parseAsmStatement(); - break; - case T.Pragma: - s = parsePragmaStatement(); - break; - case T.Mixin: - if (peekNext() == T.LParen) - goto case_parseExpressionStatement; // Parse as expression. - s = parseMixin!(MixinStatement)(); - break; - case T.Static: - switch (peekNext()) - { - case T.If: - s = parseStaticIfStatement(); - break; - case T.Assert: - s = parseStaticAssertStatement(); - break; - default: - goto case_parseAttribute; - } - break; - case T.Debug: - s = parseDebugStatement(); - break; - case T.Version: - s = parseVersionStatement(); - break; - // DeclDef - case T.Alias, T.Typedef: - d = parseDeclarationDefinition(); - goto LreturnDeclarationStatement; - case T.Enum: - d = parseEnumDeclaration(); - goto LreturnDeclarationStatement; - case T.Class: - d = parseClassDeclaration(); - goto LreturnDeclarationStatement; - case T.Interface: - d = parseInterfaceDeclaration(); - goto LreturnDeclarationStatement; - case T.Struct, T.Union: - d = parseAggregateDeclaration(); - // goto LreturnDeclarationStatement; - LreturnDeclarationStatement: - set(d, begin); - s = new DeclarationStatement(d); - break; - case T.LBrace: - s = parseScopeStatement(); - break; - case T.Semicolon: - nT(); - s = new EmptyStatement(); - break; - /+ - Parse ExpressionStatement: - +/ - // Tokens that start a PrimaryExpression. - // case T.Identifier, T.Dot, T.Typeof: - case T.This: - case T.Super: - case T.Null: - case T.True, T.False: - // case T.Dollar: - case T.Int32, T.Int64, T.Uint32, T.Uint64: - case T.Float32, T.Float64, T.Float80, - T.Imaginary32, T.Imaginary64, T.Imaginary80: - case T.CharLiteral: - case T.String: - case T.LBracket: - // case T.LBrace: - case T.Function, T.Delegate: - case T.Assert: - // case T.Mixin: - case T.Import: - case T.Typeid: - case T.Is: - case T.LParen: - case T.Traits: // D2.0 - // Tokens that can start a UnaryExpression: - case T.AndBinary, T.PlusPlus, T.MinusMinus, T.Mul, T.Minus, - T.Plus, T.Not, T.Tilde, T.New, T.Delete, T.Cast: - case_parseExpressionStatement: - s = new ExpressionStatement(parseExpression()); - require(T.Semicolon); - break; - default: - if (token.isSpecialToken) - goto case_parseExpressionStatement; - - if (token.type != T.Dollar) - // Assert that this isn't a valid expression. - assert(delegate bool(){ - bool success; - auto expression = try_(&parseExpression, success); - return success; - }() == false, "Didn't expect valid expression." - ); - - // Report error: it's an illegal statement. - s = new IllegalStatement(); - // Skip to next valid token. - do - nT(); - while (!token.isStatementStart && - token.type != T.RBrace && - token.type != T.EOF) - auto text = Token.textSpan(begin, this.prevToken); - error(begin, MSG.IllegalStatement ~ text); - } - assert(s !is null); - set(s, begin); - return s; - } - - /++ - ScopeStatement: - NoScopeStatement - +/ - Statement parseScopeStatement() - { - return new ScopeStatement(parseNoScopeStatement()); - } - - /++ - NoScopeStatement: - NonEmptyStatement - BlockStatement - BlockStatement: - { } - { StatementList } - +/ - Statement parseNoScopeStatement() - { - auto begin = token; - Statement s; - if (skipped(T.LBrace)) - { - auto ss = new Statements(); - while (token.type != T.RBrace && token.type != T.EOF) - ss ~= parseStatement(); - require(T.RBrace); - s = set(ss, begin); - } - else if (token.type == T.Semicolon) - { - error(token, MSG.ExpectedNonEmptyStatement); - nT(); - s = set(new EmptyStatement(), begin); - } - else - s = parseStatement(); - return s; - } - - /++ - NoScopeOrEmptyStatement: - ; - NoScopeStatement - +/ - Statement parseNoScopeOrEmptyStatement() - { - if (skipped(T.Semicolon)) - return set(new EmptyStatement(), this.prevToken); - else - return parseNoScopeStatement(); - } - - Statement parseAttributeStatement() - { - StorageClass stc, stc_tmp; - LinkageType prev_linkageType; - - // Nested function. - Declaration parse() - { - auto begin = token; - Declaration d; - switch (token.type) - { - case T.Extern: - if (peekNext() != T.LParen) - { - stc_tmp = StorageClass.Extern; - goto Lcommon; - } - - nT(); - auto linkageType = parseLinkageType(); - checkLinkageType(prev_linkageType, linkageType, begin); - - d = new LinkageDeclaration(linkageType, parse()); - break; - case T.Static: - stc_tmp = StorageClass.Static; - goto Lcommon; - case T.Final: - stc_tmp = StorageClass.Final; - goto Lcommon; - case T.Const: - version(D2) - { - if (peekNext() == T.LParen) - goto case_Declaration; - } - stc_tmp = StorageClass.Const; - goto Lcommon; - version(D2) - { - case T.Invariant: // D 2.0 - if (peekNext() == T.LParen) - goto case_Declaration; - stc_tmp = StorageClass.Invariant; - goto Lcommon; - } - case T.Auto: - stc_tmp = StorageClass.Auto; - goto Lcommon; - case T.Scope: - stc_tmp = StorageClass.Scope; - goto Lcommon; - Lcommon: - // Issue error if redundant. - if (stc & stc_tmp) - error(MID.RedundantStorageClass, token.srcText); - else - stc |= stc_tmp; - - auto tok = token.type; - nT(); - d = new StorageClassDeclaration(stc_tmp, tok, parse()); - break; - // TODO: allow "scope class", "abstract scope class" in function bodies? - //case T.Class: - default: - case_Declaration: - return parseVariableOrFunction(stc, Protection.None, prev_linkageType, true); - } - return set(d, begin); - } - return new DeclarationStatement(parse()); - } - - Statement parseIfStatement() - { - assert(token.type == T.If); - nT(); - - Statement variable; - Expression condition; - Statement ifBody, elseBody; - - require(T.LParen); - - Identifier* ident; - auto begin = token; // For start of AutoDeclaration or normal Declaration. - // auto Identifier = Expression - if (skipped(T.Auto)) - { - ident = requireIdentifier(MSG.ExpectedVariableName); - require(T.Assign); - auto init = parseExpression(); - auto v = new VariableDeclaration(null, [ident], [init]); - set(v, begin.nextNWS); - auto d = new StorageClassDeclaration(StorageClass.Auto, T.Auto, v); - set(d, begin); - variable = new DeclarationStatement(d); - set(variable, begin); - } - else - { - // Declarator = Expression - Type parseDeclaratorAssign() - { - auto type = parseDeclarator(ident); - require(T.Assign); - return type; - } - bool success; - auto type = try_(&parseDeclaratorAssign, success); - if (success) - { - auto init = parseExpression(); - auto v = new VariableDeclaration(type, [ident], [init]); - set(v, begin); - variable = new DeclarationStatement(v); - set(variable, begin); - } - else - condition = parseExpression(); - } - require(T.RParen); - ifBody = parseScopeStatement(); - if (skipped(T.Else)) - elseBody = parseScopeStatement(); - return new IfStatement(variable, condition, ifBody, elseBody); - } - - Statement parseWhileStatement() - { - assert(token.type == T.While); - nT(); - require(T.LParen); - auto condition = parseExpression(); - require(T.RParen); - return new WhileStatement(condition, parseScopeStatement()); - } - - Statement parseDoWhileStatement() - { - assert(token.type == T.Do); - nT(); - auto doBody = parseScopeStatement(); - require(T.While); - require(T.LParen); - auto condition = parseExpression(); - require(T.RParen); - return new DoWhileStatement(condition, doBody); - } - - Statement parseForStatement() - { - assert(token.type == T.For); - nT(); - require(T.LParen); - - Statement init, forBody; - Expression condition, increment; - - if (token.type != T.Semicolon) - init = parseNoScopeStatement(); - else - nT(); // Skip ; - if (token.type != T.Semicolon) - condition = parseExpression(); - require(T.Semicolon); - if (token.type != T.RParen) - increment = parseExpression(); - require(T.RParen); - forBody = parseScopeStatement(); - return new ForStatement(init, condition, increment, forBody); - } - - Statement parseForeachStatement() - { - assert(token.type == T.Foreach || token.type == T.Foreach_reverse); - TOK tok = token.type; - nT(); - - auto params = new Parameters; - Expression e; // Aggregate or LwrExpression - - require(T.LParen); - while (1) - { - auto paramBegin = token; - StorageClass stc; - Type type; - Identifier* ident; - - switch (token.type) - { - case T.Ref, T.Inout: - stc = StorageClass.Ref; - nT(); - // fall through - case T.Identifier: - auto next = peekNext(); - if (next == T.Comma || next == T.Semicolon || next == T.RParen) - { - ident = requireIdentifier(MSG.ExpectedVariableName); - break; - } - // fall through - default: - type = parseDeclarator(ident); - } - - params ~= set(new Parameter(stc, type, ident, null), paramBegin); - - if (token.type != T.Comma) - break; - nT(); - } - require(T.Semicolon); - e = parseExpression(); - version(D2) - { //Foreach (ForeachType; LwrExpression .. UprExpression ) ScopeStatement - if (skipped(T.Slice)) - { - // if (params.length != 1) - // error(MID.XYZ); // TODO: issue error msg - auto upper = parseExpression(); - require(T.RParen); - auto forBody = parseScopeStatement(); - return new ForeachRangeStatement(tok, params, e, upper, forBody); - } - } - // Foreach (ForeachTypeList; Aggregate) ScopeStatement - require(T.RParen); - auto forBody = parseScopeStatement(); - return new ForeachStatement(tok, params, e, forBody); - } - - Statement parseSwitchStatement() - { - assert(token.type == T.Switch); - nT(); - require(T.LParen); - auto condition = parseExpression(); - require(T.RParen); - auto switchBody = parseScopeStatement(); - return new SwitchStatement(condition, switchBody); - } - - /++ - Helper function for parsing the body of - a default or case statement. - +/ - Statement parseCaseOrDefaultBody() - { - // This function is similar to parseNoScopeStatement() - auto begin = token; - auto s = new Statements(); - while (token.type != T.Case && - token.type != T.Default && - token.type != T.RBrace && - token.type != T.EOF) - s ~= parseStatement(); - return set(new ScopeStatement(s), begin); - } - - Statement parseCaseStatement() - { - assert(token.type == T.Case); - nT(); - auto values = parseExpressionList(); - require(T.Colon); - auto caseBody = parseCaseOrDefaultBody(); - return new CaseStatement(values, caseBody); - } - - Statement parseDefaultStatement() - { - assert(token.type == T.Default); - nT(); - require(T.Colon); - auto defaultBody = parseCaseOrDefaultBody(); - return new DefaultStatement(defaultBody); - } - - Statement parseContinueStatement() - { - assert(token.type == T.Continue); - nT(); - auto ident = optionalIdentifier(); - require(T.Semicolon); - return new ContinueStatement(ident); - } - - Statement parseBreakStatement() - { - assert(token.type == T.Break); - nT(); - auto ident = optionalIdentifier(); - require(T.Semicolon); - return new BreakStatement(ident); - } - - Statement parseReturnStatement() - { - assert(token.type == T.Return); - nT(); - Expression expr; - if (token.type != T.Semicolon) - expr = parseExpression(); - require(T.Semicolon); - return new ReturnStatement(expr); - } - - Statement parseGotoStatement() - { - assert(token.type == T.Goto); - nT(); - Identifier* ident; - Expression caseExpr; - switch (token.type) - { - case T.Case: - nT(); - if (token.type == T.Semicolon) - break; - caseExpr = parseExpression(); - break; - case T.Default: - nT(); - break; - default: - ident = requireIdentifier(MSG.ExpectedAnIdentifier); - } - require(T.Semicolon); - return new GotoStatement(ident, caseExpr); - } - - Statement parseWithStatement() - { - assert(token.type == T.With); - nT(); - require(T.LParen); - auto expr = parseExpression(); - require(T.RParen); - return new WithStatement(expr, parseScopeStatement()); - } - - Statement parseSynchronizedStatement() - { - assert(token.type == T.Synchronized); - nT(); - Expression expr; - if (skipped(T.LParen)) - { - expr = parseExpression(); - require(T.RParen); - } - return new SynchronizedStatement(expr, parseScopeStatement()); - } - - Statement parseTryStatement() - { - assert(token.type == T.Try); - nT(); - - auto tryBody = parseScopeStatement(); - CatchBody[] catchBodies; - FinallyBody finBody; - - while (skipped(T.Catch)) - { - Parameter param; - if (skipped(T.LParen)) - { - auto begin = token; - Identifier* ident; - auto type = parseDeclarator(ident, true); - param = new Parameter(StorageClass.None, type, ident, null); - set(param, begin); - require(T.RParen); - } - catchBodies ~= new CatchBody(param, parseNoScopeStatement()); - if (param is null) - break; // This is a LastCatch - } - - if (token.type == T.Finally) - { - auto begin = token; - nT(); - finBody = new FinallyBody(parseNoScopeStatement()); - set(finBody, begin); - } - - if (catchBodies.length == 0 && finBody is null) - { - // TODO: issue error msg. - } - - return new TryStatement(tryBody, catchBodies, finBody); - } - - Statement parseThrowStatement() - { - assert(token.type == T.Throw); - nT(); - auto expr = parseExpression(); - require(T.Semicolon); - return new ThrowStatement(expr); - } - - Statement parseScopeGuardStatement() - { - assert(token.type == T.Scope); - nT(); - assert(token.type == T.LParen); - nT(); - auto condition = requireIdentifier(MSG.ExpectedScopeIdentifier); - if (condition) - switch (condition.identID) - { - case ID.exit, ID.success, ID.failure: - break; - default: - // TODO: create MID.InvalidScopeIdentifier - error(this.prevToken, MSG.InvalidScopeIdentifier, this.prevToken.srcText); - } - require(T.RParen); - Statement scopeBody; - if (token.type == T.LBrace) - scopeBody = parseScopeStatement(); - else - scopeBody = parseNoScopeStatement(); - return new ScopeGuardStatement(condition, scopeBody); - } - - Statement parseVolatileStatement() - { - assert(token.type == T.Volatile); - nT(); - Statement volatileBody; - if (token.type == T.Semicolon) - nT(); - else if (token.type == T.LBrace) - volatileBody = parseScopeStatement(); - else - volatileBody = parseStatement(); - return new VolatileStatement(volatileBody); - } - - Statement parsePragmaStatement() - { - assert(token.type == T.Pragma); - nT(); - - Identifier* ident; - Expression[] args; - Statement pragmaBody; - - require(T.LParen); - ident = requireIdentifier(MSG.ExpectedPragmaIdentifier); - - if (skipped(T.Comma)) - args = parseExpressionList(); - require(T.RParen); - - pragmaBody = parseNoScopeOrEmptyStatement(); - - return new PragmaStatement(ident, args, pragmaBody); - } - - Statement parseStaticIfStatement() - { - assert(token.type == T.Static); - nT(); - assert(token.type == T.If); - nT(); - Expression condition; - Statement ifBody, elseBody; - - require(T.LParen); - condition = parseExpression(); - require(T.RParen); - ifBody = parseNoScopeStatement(); - if (skipped(T.Else)) - elseBody = parseNoScopeStatement(); - return new StaticIfStatement(condition, ifBody, elseBody); - } - - Statement parseStaticAssertStatement() - { - assert(token.type == T.Static); - nT(); - assert(token.type == T.Assert); - nT(); - Expression condition, message; - require(T.LParen); - condition = parseAssignExpression(); // Condition. - if (skipped(T.Comma)) - message = parseAssignExpression(); // Error message. - require(T.RParen); - require(T.Semicolon); - return new StaticAssertStatement(condition, message); - } - - Statement parseDebugStatement() - { - assert(token.type == T.Debug); - nT(); // Skip debug keyword. - - Token* cond; - Statement debugBody, elseBody; - - // ( Condition ) - if (skipped(T.LParen)) - { - cond = parseIdentOrInt(); - require(T.RParen); - } - // debug Statement - // debug ( Condition ) Statement - debugBody = parseNoScopeStatement(); - // else Statement - if (skipped(T.Else)) - elseBody = parseNoScopeStatement(); - - return new DebugStatement(cond, debugBody, elseBody); - } - - Statement parseVersionStatement() - { - assert(token.type == T.Version); - nT(); // Skip version keyword. - - Token* cond; - Statement versionBody, elseBody; - - // ( Condition ) - require(T.LParen); - cond = parseIdentOrInt(); - require(T.RParen); - // version ( Condition ) Statement - versionBody = parseNoScopeStatement(); - // else Statement - if (skipped(T.Else)) - elseBody = parseNoScopeStatement(); - - return new VersionStatement(cond, versionBody, elseBody); - } - - /+++++++++++++++++++++++++++++ - + Assembler parsing methods + - +++++++++++++++++++++++++++++/ - - Statement parseAsmStatement() - { - assert(token.type == T.Asm); - nT(); // Skip asm keyword. - require(T.LBrace); - auto ss = new Statements; - while (token.type != T.RBrace && token.type != T.EOF) - ss ~= parseAsmInstruction(); - require(T.RBrace); - return new AsmStatement(ss); - } - - Statement parseAsmInstruction() - { - auto begin = token; - Statement s; - Identifier* ident; - switch (token.type) - { - // Keywords that are valid opcodes. - case T.In, T.Int, T.Out: - ident = token.ident; - nT(); - goto LOpcode; - case T.Identifier: - ident = token.ident; - nT(); // Skip Identifier - if (skipped(T.Colon)) - { - // Identifier : AsmInstruction - s = new LabeledStatement(ident, parseAsmInstruction()); - break; - } - - LOpcode: - // Opcode ; - // Opcode Operands ; - // Opcode - // Identifier - Expression[] es; - if (token.type != T.Semicolon) - { - while (1) - { - es ~= parseAsmExpression(); - if (token.type != T.Comma) - break; - nT(); - } - } - require(T.Semicolon); - s = new AsmInstruction(ident, es); - break; - case T.Align: - // align Integer; - nT(); - int number = -1; - if (token.type == T.Int32) - (number = token.int_), nT(); - else - error(token, MSG.ExpectedIntegerAfterAlign, token.srcText); - require(T.Semicolon); - s = new AsmAlignStatement(number); - break; - case T.Semicolon: - s = new EmptyStatement(); - nT(); - break; - default: - s = new IllegalAsmInstruction(); - // Skip to next valid token. - do - nT(); - while (!token.isAsmInstructionStart && - token.type != T.RBrace && - token.type != T.EOF) - auto text = Token.textSpan(begin, this.prevToken); - error(begin, MSG.IllegalAsmInstructino ~ text); - } - set(s, begin); - return s; - } - - Expression parseAsmExpression() - { - auto begin = token; - auto e = parseAsmOrOrExpression(); - if (skipped(T.Question)) - { - auto tok = this.prevToken; - auto iftrue = parseAsmExpression(); - require(T.Colon); - auto iffalse = parseAsmExpression(); - e = new CondExpression(e, iftrue, iffalse, tok); - set(e, begin); - } - // TODO: create AsmExpression that contains e? - return e; - } - - Expression parseAsmOrOrExpression() - { - alias parseAsmAndAndExpression parseNext; - auto begin = token; - auto e = parseNext(); - while (token.type == T.OrLogical) - { - auto tok = token; - nT(); - e = new OrOrExpression(e, parseNext(), tok); - set(e, begin); - } - return e; - } - - Expression parseAsmAndAndExpression() - { - alias parseAsmOrExpression parseNext; - auto begin = token; - auto e = parseNext(); - while (token.type == T.AndLogical) - { - auto tok = token; - nT(); - e = new AndAndExpression(e, parseNext(), tok); - set(e, begin); - } - return e; - } - - Expression parseAsmOrExpression() - { - alias parseAsmXorExpression parseNext; - auto begin = token; - auto e = parseNext(); - while (token.type == T.OrBinary) - { - auto tok = token; - nT(); - e = new OrExpression(e, parseNext(), tok); - set(e, begin); - } - return e; - } - - Expression parseAsmXorExpression() - { - alias parseAsmAndExpression parseNext; - auto begin = token; - auto e = parseNext(); - while (token.type == T.Xor) - { - auto tok = token; - nT(); - e = new XorExpression(e, parseNext(), tok); - set(e, begin); - } - return e; - } - - Expression parseAsmAndExpression() - { - alias parseAsmCmpExpression parseNext; - auto begin = token; - auto e = parseNext(); - while (token.type == T.AndBinary) - { - auto tok = token; - nT(); - e = new AndExpression(e, parseNext(), tok); - set(e, begin); - } - return e; - } - - Expression parseAsmCmpExpression() - { - alias parseAsmShiftExpression parseNext; - auto begin = token; - auto e = parseNext(); - - auto operator = token; - switch (operator.type) - { - case T.Equal, T.NotEqual: - nT(); - e = new EqualExpression(e, parseNext(), operator); - break; - case T.LessEqual, T.Less, T.GreaterEqual, T.Greater: - nT(); - e = new RelExpression(e, parseNext(), operator); - break; - default: - return e; - } - set(e, begin); - return e; - } - - Expression parseAsmShiftExpression() - { - alias parseAsmAddExpression parseNext; - auto begin = token; - auto e = parseNext(); - while (1) - { - auto operator = token; - switch (operator.type) - { - case T.LShift: nT(); e = new LShiftExpression(e, parseNext(), operator); break; - case T.RShift: nT(); e = new RShiftExpression(e, parseNext(), operator); break; - case T.URShift: nT(); e = new URShiftExpression(e, parseNext(), operator); break; - default: - return e; - } - set(e, begin); - } - assert(0); - } - - Expression parseAsmAddExpression() - { - alias parseAsmMulExpression parseNext; - auto begin = token; - auto e = parseNext(); - while (1) - { - auto operator = token; - switch (operator.type) - { - case T.Plus: nT(); e = new PlusExpression(e, parseNext(), operator); break; - case T.Minus: nT(); e = new MinusExpression(e, parseNext(), operator); break; - // Not allowed in asm - //case T.Tilde: nT(); e = new CatExpression(e, parseNext(), operator); break; - default: - return e; - } - set(e, begin); - } - assert(0); - } - - Expression parseAsmMulExpression() - { - alias parseAsmPostExpression parseNext; - auto begin = token; - auto e = parseNext(); - while (1) - { - auto operator = token; - switch (operator.type) - { - case T.Mul: nT(); e = new MulExpression(e, parseNext(), operator); break; - case T.Div: nT(); e = new DivExpression(e, parseNext(), operator); break; - case T.Mod: nT(); e = new ModExpression(e, parseNext(), operator); break; - default: - return e; - } - set(e, begin); - } - assert(0); - } - - Expression parseAsmPostExpression() - { - auto begin = token; - auto e = parseAsmUnaryExpression(); - while (token.type == T.LBracket) - { - nT(); - e = parseAsmExpression(); - e = new AsmPostBracketExpression(e); - require(T.RBracket); - set(e, begin); - } - return e; - } - - Expression parseAsmUnaryExpression() - { - auto begin = token; - Expression e; - switch (token.type) - { - case T.Byte, T.Short, T.Int, - T.Float, T.Double, T.Real: - goto LAsmTypePrefix; - case T.Identifier: - switch (token.ident.identID) - { - case ID.near, ID.far,/* "byte", "short", "int",*/ - ID.word, ID.dword, ID.qword/*, "float", "double", "real"*/: - LAsmTypePrefix: - nT(); - if (token.type == T.Identifier && token.ident is Ident.ptr) - nT(); - else - error(MID.ExpectedButFound, "ptr", token.srcText); - e = new AsmTypeExpression(parseAsmExpression()); - break; - case ID.offset: - nT(); - e = new AsmOffsetExpression(parseAsmExpression()); - break; - case ID.seg: - nT(); - e = new AsmSegExpression(parseAsmExpression()); - break; - default: - goto LparseAsmPrimaryExpression; - } - break; - case T.Minus: - case T.Plus: - nT(); - e = new SignExpression(parseAsmUnaryExpression()); - break; - case T.Not: - nT(); - e = new NotExpression(parseAsmUnaryExpression()); - break; - case T.Tilde: - nT(); - e = new CompExpression(parseAsmUnaryExpression()); - default: - LparseAsmPrimaryExpression: - e = parseAsmPrimaryExpression(); - return e; - } - set(e, begin); - return e; - } - - Expression parseAsmPrimaryExpression() - { - auto begin = token; - Expression e; - switch (token.type) - { - case T.Int32, T.Int64, T.Uint32, T.Uint64: - e = new IntExpression(token); - nT(); - break; - case T.Float32, T.Float64, T.Float80, - T.Imaginary32, T.Imaginary64, T.Imaginary80: - e = new RealExpression(token); - nT(); - break; - case T.Dollar: - e = new DollarExpression(); - nT(); - break; - case T.LBracket: - // [ AsmExpression ] - nT(); - e = parseAsmExpression(); - require(T.RBracket); - e = new AsmBracketExpression(e); - break; - case T.Identifier: - auto register = token.ident; - switch (register.identID) - { - // __LOCAL_SIZE - case ID.__LOCAL_SIZE: - nT(); - e = new AsmLocalSizeExpression(); - break; - // Register - case ID.ST: - nT(); - // (1) - (7) - int number = -1; - if (skipped(T.LParen)) - { - if (token.type == T.Int32) - (number = token.int_), nT(); - else - expected(T.Int32); - require(T.RParen); - } - e = new AsmRegisterExpression(register, number); - break; - case ID.FS: - nT(); - // TODO: is the colon-number part optional? - int number = -1; - if (skipped(T.Colon)) - { - // :0, :4, :8 - if (token.type == T.Int32) - (number = token.int_), nT(); - if (number != 0 && number != 4 && number != 8) - error(MID.ExpectedButFound, "0, 4 or 8", token.srcText); - } - e = new AsmRegisterExpression(register, number); - break; - case ID.AL, ID.AH, ID.AX, ID.EAX, - ID.BL, ID.BH, ID.BX, ID.EBX, - ID.CL, ID.CH, ID.CX, ID.ECX, - ID.DL, ID.DH, ID.DX, ID.EDX, - ID.BP, ID.EBP, ID.SP, ID.ESP, - ID.DI, ID.EDI, ID.SI, ID.ESI, - ID.ES, ID.CS, ID.SS, ID.DS, ID.GS, - ID.CR0, ID.CR2, ID.CR3, ID.CR4, - ID.DR0, ID.DR1, ID.DR2, ID.DR3, ID.DR6, ID.DR7, - ID.TR3, ID.TR4, ID.TR5, ID.TR6, ID.TR7, - ID.MM0, ID.MM1, ID.MM2, ID.MM3, - ID.MM4, ID.MM5, ID.MM6, ID.MM7, - ID.XMM0, ID.XMM1, ID.XMM2, ID.XMM3, - ID.XMM4, ID.XMM5, ID.XMM6, ID.XMM7: - nT(); - e = new AsmRegisterExpression(register); - break; - default: - // DotIdentifier - Expression[] identList; - while (1) - { - auto begin2 = token; - auto ident = requireIdentifier(MSG.ExpectedAnIdentifier); - e = new IdentifierExpression(ident); - set(e, begin2); - identList ~= e; - if (token.type != T.Dot) - break; - nT(); // Skip dot. - } - e = new DotListExpression(identList); - } - break; - default: - error(MID.ExpectedButFound, "Expression", token.srcText); - e = new EmptyExpression(); - if (!trying) - { - // Insert a dummy token and don't consume current one. - begin = lx.insertEmptyTokenBefore(token); - this.prevToken = begin; - } - } - set(e, begin); - return e; - } - - /+++++++++++++++++++++++++++++ - + Expression parsing methods + - +++++++++++++++++++++++++++++/ - - Expression parseExpression() - { - auto begin = token; - auto e = parseAssignExpression(); - while (token.type == T.Comma) - { - auto comma = token; - nT(); - e = new CommaExpression(e, parseAssignExpression(), comma); - set(e, begin); - } - return e; - } - - Expression parseAssignExpression() - { - auto begin = token; - auto e = parseCondExpression(); - while (1) - { - switch (token.type) - { - case T.Assign: - nT(); e = new AssignExpression(e, parseAssignExpression()); - break; - case T.LShiftAssign: - nT(); e = new LShiftAssignExpression(e, parseAssignExpression()); - break; - case T.RShiftAssign: - nT(); e = new RShiftAssignExpression(e, parseAssignExpression()); - break; - case T.URShiftAssign: - nT(); e = new URShiftAssignExpression(e, parseAssignExpression()); - break; - case T.OrAssign: - nT(); e = new OrAssignExpression(e, parseAssignExpression()); - break; - case T.AndAssign: - nT(); e = new AndAssignExpression(e, parseAssignExpression()); - break; - case T.PlusAssign: - nT(); e = new PlusAssignExpression(e, parseAssignExpression()); - break; - case T.MinusAssign: - nT(); e = new MinusAssignExpression(e, parseAssignExpression()); - break; - case T.DivAssign: - nT(); e = new DivAssignExpression(e, parseAssignExpression()); - break; - case T.MulAssign: - nT(); e = new MulAssignExpression(e, parseAssignExpression()); - break; - case T.ModAssign: - nT(); e = new ModAssignExpression(e, parseAssignExpression()); - break; - case T.XorAssign: - nT(); e = new XorAssignExpression(e, parseAssignExpression()); - break; - case T.CatAssign: - nT(); e = new CatAssignExpression(e, parseAssignExpression()); - break; - default: - return e; - } - set(e, begin); - } - return e; - } - - Expression parseCondExpression() - { - auto begin = token; - auto e = parseOrOrExpression(); - if (token.type == T.Question) - { - auto tok = token; - nT(); - auto iftrue = parseExpression(); - require(T.Colon); - auto iffalse = parseCondExpression(); - e = new CondExpression(e, iftrue, iffalse, tok); - set(e, begin); - } - return e; - } - - Expression parseOrOrExpression() - { - alias parseAndAndExpression parseNext; - auto begin = token; - auto e = parseNext(); - while (token.type == T.OrLogical) - { - auto tok = token; - nT(); - e = new OrOrExpression(e, parseNext(), tok); - set(e, begin); - } - return e; - } - - Expression parseAndAndExpression() - { - alias parseOrExpression parseNext; - auto begin = token; - auto e = parseNext(); - while (token.type == T.AndLogical) - { - auto tok = token; - nT(); - e = new AndAndExpression(e, parseNext(), tok); - set(e, begin); - } - return e; - } - - Expression parseOrExpression() - { - alias parseXorExpression parseNext; - auto begin = token; - auto e = parseNext(); - while (token.type == T.OrBinary) - { - auto tok = token; - nT(); - e = new OrExpression(e, parseNext(), tok); - set(e, begin); - } - return e; - } - - Expression parseXorExpression() - { - alias parseAndExpression parseNext; - auto begin = token; - auto e = parseNext(); - while (token.type == T.Xor) - { - auto tok = token; - nT(); - e = new XorExpression(e, parseNext(), tok); - set(e, begin); - } - return e; - } - - Expression parseAndExpression() - { - alias parseCmpExpression parseNext; - auto begin = token; - auto e = parseNext(); - while (token.type == T.AndBinary) - { - auto tok = token; - nT(); - e = new AndExpression(e, parseNext(), tok); - set(e, begin); - } - return e; - } - - Expression parseCmpExpression() - { - alias parseShiftExpression parseNext; - auto begin = token; - auto e = parseShiftExpression(); - - auto operator = token; - switch (operator.type) - { - case T.Equal, T.NotEqual: - nT(); - e = new EqualExpression(e, parseNext(), operator); - break; - case T.Not: - if (peekNext() != T.Is) - break; - nT(); - // fall through - case T.Is: - nT(); - e = new IdentityExpression(e, parseNext(), operator); - break; - case T.LessEqual, T.Less, T.GreaterEqual, T.Greater, - T.Unordered, T.UorE, T.UorG, T.UorGorE, - T.UorL, T.UorLorE, T.LorEorG, T.LorG: - nT(); - e = new RelExpression(e, parseNext(), operator); - break; - case T.In: - nT(); - e = new InExpression(e, parseNext(), operator); - break; - default: - return e; - } - set(e, begin); - return e; - } - - Expression parseShiftExpression() - { - alias parseAddExpression parseNext; - auto begin = token; - auto e = parseNext(); - while (1) - { - auto operator = token; - switch (operator.type) - { - case T.LShift: nT(); e = new LShiftExpression(e, parseNext(), operator); break; - case T.RShift: nT(); e = new RShiftExpression(e, parseNext(), operator); break; - case T.URShift: nT(); e = new URShiftExpression(e, parseNext(), operator); break; - default: - return e; - } - set(e, begin); - } - assert(0); - } - - Expression parseAddExpression() - { - alias parseMulExpression parseNext; - auto begin = token; - auto e = parseNext(); - while (1) - { - auto operator = token; - switch (operator.type) - { - case T.Plus: nT(); e = new PlusExpression(e, parseNext(), operator); break; - case T.Minus: nT(); e = new MinusExpression(e, parseNext(), operator); break; - case T.Tilde: nT(); e = new CatExpression(e, parseNext(), operator); break; - default: - return e; - } - set(e, begin); - } - assert(0); - } - - Expression parseMulExpression() - { - alias parsePostExpression parseNext; - auto begin = token; - auto e = parseNext(); - while (1) - { - auto operator = token; - switch (operator.type) - { - case T.Mul: nT(); e = new MulExpression(e, parseNext(), operator); break; - case T.Div: nT(); e = new DivExpression(e, parseNext(), operator); break; - case T.Mod: nT(); e = new ModExpression(e, parseNext(), operator); break; - default: - return e; - } - set(e, begin); - } - assert(0); - } - - Expression parsePostExpression() - { - auto begin = token; - auto e = parseUnaryExpression(); - while (1) - { - switch (token.type) - { - case T.Dot: - e = new PostDotListExpression(e, parseDotListExpression()); - goto Lset; - case T.PlusPlus: - e = new PostIncrExpression(e); - break; - case T.MinusMinus: - e = new PostDecrExpression(e); - break; - case T.LParen: - e = new CallExpression(e, parseArguments()); - goto Lset; - case T.LBracket: - // parse Slice- and IndexExpression - nT(); - // [] is a SliceExpression - if (token.type == T.RBracket) - { - e = new SliceExpression(e, null, null); - break; - } - - Expression[] es = [parseAssignExpression()]; - - // [ AssignExpression .. AssignExpression ] - if (skipped(T.Slice)) - { - e = new SliceExpression(e, es[0], parseAssignExpression()); - require(T.RBracket); - goto Lset; - } - - // [ ExpressionList ] - if (skipped(T.Comma)) - es ~= parseExpressionList(); - require(T.RBracket); - - e = new IndexExpression(e, es); - goto Lset; - default: - return e; - } - nT(); - Lset: // Jumped here to skip nT(). - set(e, begin); - } - assert(0); - } - - Expression parseUnaryExpression() - { - auto begin = token; - Expression e; - switch (token.type) - { - case T.AndBinary: - nT(); - e = new AddressExpression(parseUnaryExpression()); - break; - case T.PlusPlus: - nT(); - e = new PreIncrExpression(parseUnaryExpression()); - break; - case T.MinusMinus: - nT(); - e = new PreDecrExpression(parseUnaryExpression()); - break; - case T.Mul: - nT(); - e = new DerefExpression(parseUnaryExpression()); - break; - case T.Minus: - case T.Plus: - nT(); - e = new SignExpression(parseUnaryExpression()); - break; - case T.Not: - nT(); - e = new NotExpression(parseUnaryExpression()); - break; - case T.Tilde: - nT(); - e = new CompExpression(parseUnaryExpression()); - break; - case T.New: - e = parseNewExpression(); - return e; - case T.Delete: - nT(); - e = new DeleteExpression(parseUnaryExpression()); - break; - case T.Cast: - requireNext(T.LParen); - Type type; - switch (token.type) - { - version(D2) - { - auto begin2 = token; - case T.Const: - type = new ConstType(null); - goto case_break; - case T.Invariant: - type = new InvariantType(null); - case_break: - nT(); - set(type, begin2); - break; - } - default: - type = parseType(); - } - require(T.RParen); - e = new CastExpression(parseUnaryExpression(), type); - break; - case T.LParen: - // ( Type ) . Identifier - Type parseType_() - { - nT(); - auto type = parseType(); - require(T.RParen); - require(T.Dot); - return type; - } - bool success; - auto type = try_(&parseType_, success); - if (success) - { - auto ident = requireIdentifier(MSG.ExpectedIdAfterTypeDot); - e = new TypeDotIdExpression(type, ident); - break; - } - goto default; - default: - e = parsePrimaryExpression(); - return e; - } - assert(e !is null); - set(e, begin); - return e; - } - - Expression parsePrimaryExpression() - { - auto begin = token; - Expression e; - switch (token.type) - { - case T.Identifier, T.Dot, T.Typeof: - e = parseDotListExpression(); - break; - case T.This: - nT(); - e = new ThisExpression(); - break; - case T.Super: - nT(); - e = new SuperExpression(); - break; - case T.Null: - nT(); - e = new NullExpression(); - break; - case T.True, T.False: - nT(); - e = new BoolExpression(); - break; - case T.Dollar: - nT(); - e = new DollarExpression(); - break; - case T.Int32, T.Int64, T.Uint32, T.Uint64: - e = new IntExpression(token); - nT(); - break; - case T.Float32, T.Float64, T.Float80, - T.Imaginary32, T.Imaginary64, T.Imaginary80: - e = new RealExpression(token); - nT(); - break; - case T.CharLiteral: - e = new CharExpression(token.dchar_); - nT(); - break; - case T.String: - Token*[] stringLiterals; - do - { - stringLiterals ~= token; - nT(); - } while (token.type == T.String) - e = new StringExpression(stringLiterals); - break; - case T.LBracket: - Expression[] values; - - nT(); - if (!skipped(T.RBracket)) - { - e = parseAssignExpression(); - if (token.type == T.Colon) - goto LparseAssocArray; - if (skipped(T.Comma)) - values = [e] ~ parseExpressionList(); - require(T.RBracket); - } - - e = new ArrayLiteralExpression(values); - break; - - LparseAssocArray: - Expression[] keys; - - keys ~= e; - nT(); // Skip colon. - goto LenterLoop; - - while (1) - { - keys ~= parseAssignExpression(); - require(T.Colon); - LenterLoop: - values ~= parseAssignExpression(); - if (token.type != T.Comma) - break; - nT(); - } - require(T.RBracket); - e = new AArrayLiteralExpression(keys, values); - break; - case T.LBrace: - // DelegateLiteral := { Statements } - auto funcBody = parseFunctionBody(); - e = new FunctionLiteralExpression(funcBody); - break; - case T.Function, T.Delegate: - // FunctionLiteral := (function|delegate) Type? '(' ArgumentList ')' '{' Statements '}' - nT(); // Skip function|delegate token. - Type returnType; - Parameters parameters; - if (token.type != T.LBrace) - { - if (token.type != T.LParen) // Optional return type - returnType = parseType(); - parameters = parseParameterList(); - } - auto funcBody = parseFunctionBody(); - e = new FunctionLiteralExpression(returnType, parameters, funcBody); - break; - case T.Assert: - Expression msg; - requireNext(T.LParen); - e = parseAssignExpression(); - if (skipped(T.Comma)) - msg = parseAssignExpression(); - require(T.RParen); - e = new AssertExpression(e, msg); - break; - case T.Mixin: - requireNext(T.LParen); - e = parseAssignExpression(); - require(T.RParen); - e = new MixinExpression(e); - break; - case T.Import: - requireNext(T.LParen); - e = parseAssignExpression(); - require(T.RParen); - e = new ImportExpression(e); - break; - case T.Typeid: - requireNext(T.LParen); - auto type = parseType(); - require(T.RParen); - e = new TypeidExpression(type); - break; - case T.Is: - requireNext(T.LParen); - - Type type, specType; - Identifier* ident; // optional Identifier - Token* opTok, specTok; - - type = parseDeclarator(ident, true); - - switch (token.type) - { - case T.Colon, T.Equal: - opTok = token; - nT(); - switch (token.type) - { - case T.Typedef, - T.Struct, - T.Union, - T.Class, - T.Interface, - T.Enum, - T.Function, - T.Delegate, - T.Super, - T.Return: - case_Const_Invariant: - specTok = token; - nT(); - break; - case T.Const, T.Invariant: - if (peekNext() != T.LParen) - goto case_Const_Invariant; - // Fall through. It's a type. - default: - specType = parseType(); - } - default: - } - - TemplateParameters tparams; - version(D2) - { - // is ( Type Identifier : TypeSpecialization , TemplateParameterList ) - // is ( Type Identifier == TypeSpecialization , TemplateParameterList ) - if (ident && specType && token.type == T.Comma) - tparams = parseTemplateParameterList2(); - } - require(T.RParen); - e = new IsExpression(type, ident, opTok, specTok, specType, tparams); - break; - case T.LParen: - if (tokenAfterParenIs(T.LBrace)) - { - auto parameters = parseParameterList(); - // ( ParameterList ) FunctionBody - auto funcBody = parseFunctionBody(); - e = new FunctionLiteralExpression(null, parameters, funcBody); - } - else - { - // ( Expression ) - nT(); - e = parseExpression(); - require(T.RParen); - // TODO: create ParenExpression? - } - break; - version(D2) - { - case T.Traits: - nT(); - require(T.LParen); - auto id = requireIdentifier(MSG.ExpectedAnIdentifier); - TemplateArguments args; - if (token.type == T.Comma) - args = parseTemplateArguments2(); - else - require(T.RParen); - e = new TraitsExpression(id, args); - break; - } - default: - if (token.isIntegralType) - { // IntegralType . Identifier - auto type = new IntegralType(token.type); - nT(); - set(type, begin); - require(T.Dot); - auto ident = requireIdentifier(MSG.ExpectedIdAfterTypeDot); - e = new TypeDotIdExpression(type, ident); - } - else if (token.isSpecialToken) - { - e = new SpecialTokenExpression(token); - nT(); - } - else - { - error(MID.ExpectedButFound, "Expression", token.srcText); - e = new EmptyExpression(); - if (!trying) - { - // Insert a dummy token and don't consume current one. - begin = lx.insertEmptyTokenBefore(token); - this.prevToken = begin; - } - } - } - set(e, begin); - return e; - } - - Expression parseNewExpression(/*Expression e*/) - { - auto begin = token; - assert(token.type == T.New); - nT(); // Skip new keyword. - - Expression[] newArguments; - Expression[] ctorArguments; - - if (token.type == T.LParen) - newArguments = parseArguments(); - - // NewAnonClassExpression: - // new (ArgumentList)opt class (ArgumentList)opt SuperClassopt InterfaceClassesopt ClassBody - if (skipped(T.Class)) - { - if (token.type == T.LParen) - ctorArguments = parseArguments(); - - BaseClass[] bases = token.type != T.LBrace ? parseBaseClasses(false) : null ; - - auto decls = parseDeclarationDefinitionsBody(); - return set(new NewAnonClassExpression(/*e, */newArguments, bases, ctorArguments, decls), begin); - } - - // NewExpression: - // NewArguments Type [ AssignExpression ] - // NewArguments Type ( ArgumentList ) - // NewArguments Type - auto type = parseType(); - - if (token.type == T.LParen) - ctorArguments = parseArguments(); - - return set(new NewExpression(/*e, */newArguments, type, ctorArguments), begin); - } - - Type parseType() - { - return parseBasicType2(parseBasicType()); - } - - Type parseBasicType() - { - auto begin = token; - Type t; - - if (token.isIntegralType) - { - t = new IntegralType(token.type); - nT(); - } - else - switch (token.type) - { - case T.Identifier, T.Typeof, T.Dot: - t = parseDotListType(); - assert(!isNodeSet(t)); - break; - version(D2) - { - case T.Const: - // const ( Type ) - nT(); - require(T.LParen); - t = parseType(); - require(T.RParen); - t = new ConstType(t); - break; - case T.Invariant: - // invariant ( Type ) - nT(); - require(T.LParen); - t = parseType(); - require(T.RParen); - t = new InvariantType(t); - break; - } // version(D2) - default: - error(MID.ExpectedButFound, "BasicType", token.srcText); - t = new UndefinedType(); - nT(); - } - return set(t, begin); - } - - Type parseBasicType2(Type t) - { - typeof(token) begin; - while (1) - { - begin = token; - switch (token.type) - { - case T.Mul: - t = new PointerType(t); - nT(); - break; - case T.LBracket: - t = parseArrayType(t); - continue; - case T.Function, T.Delegate: - TOK tok = token.type; - nT(); - auto parameters = parseParameterList(); - if (tok == T.Function) - t = new FunctionType(t, parameters); - else - t = new DelegateType(t, parameters); - break; - default: - return t; - } - set(t, begin); - } - assert(0); - } - - bool tokenAfterParenIs(TOK tok) - { - // We count nested parentheses tokens because template types may appear inside parameter lists; e.g. (int x, Foo!(int) y). - assert(token.type == T.LParen); - Token* next = token; - uint level = 1; - Loop: - while (1) - { - lx.peek(next); - switch (next.type) - { - case T.RParen: - if (--level == 0) - { // Last, closing parentheses found. - do - lx.peek(next); - while (next.isWhitespace) - break Loop; - } - break; - case T.LParen: - ++level; - break; - case T.EOF: - break Loop; - default: - } - } - return next.type == tok; - } - - Type parseDeclaratorSuffix(Type t) - { - switch (token.type) - { - case T.LBracket: - // Type Identifier ArrayType - // ArrayType := [] or [Type] or [Expression..Expression] - do - t = parseArrayType(t); - while (token.type == T.LBracket) - break; -/+ // parsed in parseDeclaration() - case T.LParen: - TemplateParameters tparams; - if (tokenAfterParenIs(T.LParen)) - { - // ( TemplateParameterList ) ( ParameterList ) - tparams = parseTemplateParameterList(); - } - - auto params = parseParameterList(); - // ReturnType FunctionName ( ParameterList ) - t = new FunctionType(t, params, tparams); - break; -+/ - default: - break; - } - return t; - } - - Type parseArrayType(Type t) - { - assert(token.type == T.LBracket); - auto begin = token; - nT(); - if (skipped(T.RBracket)) - t = new ArrayType(t); - else - { - bool success; - Type parseAAType() - { - auto type = parseType(); - require(T.RBracket); - return type; - } - auto assocType = try_(&parseAAType, success); - if (success) - t = new ArrayType(t, assocType); - else - { - Expression e = parseExpression(), e2; - if (skipped(T.Slice)) - e2 = parseExpression(); - t = new ArrayType(t, e, e2); - require(T.RBracket); - } - } - set(t, begin); - return t; - } - - Type parseCFunctionPointerType(Type type, ref Identifier* ident, bool optionalParamList) - { - assert(token.type == T.LParen); - assert(type !is null); - auto begin = token; - nT(); // Skip ( - type = parseBasicType2(type); - if (token.type == T.LParen) - { - // Can be nested. - type = parseCFunctionPointerType(type, ident, true); - } - else if (token.type == T.Identifier) - { - // The identifier of the function pointer and the declaration. - ident = token.ident; - nT(); - type = parseDeclaratorSuffix(type); - } - require(T.RParen); - - Parameters params; - if (optionalParamList) - params = token.type == T.LParen ? parseParameterList() : null; - else - params = parseParameterList(); - - type = new CFuncPointerType(type, params); - return set(type, begin); - } - - Type parseDeclarator(ref Identifier* ident, bool identOptional = false) - { - auto t = parseType(); - - if (token.type == T.LParen) - t = parseCFunctionPointerType(t, ident, true); - else if (token.type == T.Identifier) - { - ident = token.ident; - nT(); - t = parseDeclaratorSuffix(t); - } - - if (ident is null && !identOptional) - error(token, MSG.ExpectedDeclaratorIdentifier, token.srcText); - - return t; - } - - /++ - Parse a list of AssignExpressions. - ExpressionList: - AssignExpression - AssignExpression , ExpressionList - +/ - Expression[] parseExpressionList() - { - Expression[] expressions; - do - expressions ~= parseAssignExpression(); - while(skipped(T.Comma)) - return expressions; - } - - /++ - Arguments: - ( ) - ( ExpressionList ) - +/ - Expression[] parseArguments() - { - assert(token.type == T.LParen); - nT(); - Expression[] args; - if (token.type != TOK.RParen) - args = parseExpressionList(); - require(TOK.RParen); - return args; - } - - Parameters parseParameterList() - out(params) - { - if (params.length > 1) - foreach (param; params.items[0..$-1]) - { - if (param.isVariadic()) - assert(0, "variadic arguments can only appear at the end of the parameter list."); - } - } - body - { - auto begin = token; - require(T.LParen); - - auto params = new Parameters(); - - if (skipped(T.RParen)) - return set(params, begin); - - Loop: - while (1) - { - auto paramBegin = token; - StorageClass stc, tmp; - Type type; - Identifier* ident; - Expression defValue; - - void pushParameter() - { - params ~= set(new Parameter(stc, type, ident, defValue), paramBegin); - } - - if (skipped(T.Ellipses)) - { - stc = StorageClass.Variadic; - pushParameter(); // type, ident and defValue will be null. - break Loop; - } - - Lstc_loop: - switch (token.type) - { - version(D2) - { - case T.Invariant: // D2.0 - if (peekNext() == T.LParen) - goto default; - tmp = StorageClass.Invariant; - goto Lcommon; - case T.Const: // D2.0 - if (peekNext() == T.LParen) - goto default; - tmp = StorageClass.Const; - goto Lcommon; - case T.Final: // D2.0 - tmp = StorageClass.Final; - goto Lcommon; - case T.Scope: // D2.0 - tmp = StorageClass.Scope; - goto Lcommon; - case T.Static: // D2.0 - tmp = StorageClass.Static; - goto Lcommon; - } - case T.In: - tmp = StorageClass.In; - goto Lcommon; - case T.Out: - tmp = StorageClass.Out; - goto Lcommon; - case T.Inout, T.Ref: - tmp = StorageClass.Ref; - goto Lcommon; - case T.Lazy: - tmp = StorageClass.Lazy; - goto Lcommon; - Lcommon: - // Check for redundancy. - if (stc & tmp) - error(MID.RedundantStorageClass, token.srcText); - else - stc |= tmp; - nT(); - version(D2) - goto Lstc_loop; - else - goto default; // In D1.0 only one stc per parameter is allowed. - default: - type = parseDeclarator(ident, true); - - if (skipped(T.Assign)) - defValue = parseAssignExpression(); - - if (skipped(T.Ellipses)) - { - stc |= StorageClass.Variadic; - pushParameter(); - break Loop; - } - - pushParameter(); - - if (token.type != T.Comma) - break Loop; - nT(); - } - } - require(T.RParen); - return set(params, begin); - } - - TemplateArguments parseTemplateArguments() - { - TemplateArguments targs; - require(T.LParen); - if (token.type != T.RParen) - targs = parseTemplateArguments_(); - require(T.RParen); - return targs; - } - -version(D2) -{ - TemplateArguments parseTemplateArguments2() - { - assert(token.type == T.Comma); - nT(); - TemplateArguments targs; - if (token.type != T.RParen) - targs = parseTemplateArguments_(); - else - error(token, MSG.ExpectedTypeOrExpression); - require(T.RParen); - return targs; - } -} // version(D2) - - TemplateArguments parseTemplateArguments_() - { - auto begin = token; - auto targs = new TemplateArguments; - while (1) - { - Type parseType_() - { - auto type = parseType(); - if (token.type == T.Comma || token.type == T.RParen) - return type; - ++errorCount; // Cause try_() to fail. - return null; - } - bool success; - auto typeArgument = try_(&parseType_, success); - if (success) - // TemplateArgument: - // Type - // Symbol - targs ~= typeArgument; - else - // TemplateArgument: - // AssignExpression - targs ~= parseAssignExpression(); - if (token.type != T.Comma) - break; // Exit loop. - nT(); - } - set(targs, begin); - return targs; - } - - TemplateParameters parseTemplateParameterList() - { - TemplateParameters tparams; - require(T.LParen); - if (token.type != T.RParen) - tparams = parseTemplateParameterList_(); - require(T.RParen); - return tparams; - } - -version(D2) -{ - TemplateParameters parseTemplateParameterList2() - { - assert(token.type == T.Comma); - nT(); - TemplateParameters tparams; - if (token.type != T.RParen) - tparams = parseTemplateParameterList_(); - else - error(token, MSG.ExpectedTemplateParameters); - return tparams; - } -} // version(D2) - - TemplateParameters parseTemplateParameterList_() - { - auto begin = token; - auto tparams = new TemplateParameters; - - while (1) - { - auto paramBegin = token; - TemplateParameter tp; - Identifier* ident; - Type specType, defType; - - void parseSpecAndOrDefaultType() - { - // : SpecializationType - if (skipped(T.Colon)) - specType = parseType(); - // = DefaultType - if (skipped(T.Assign)) - defType = parseType(); - } - - switch (token.type) - { - case T.Alias: - // TemplateAliasParameter: - // alias Identifier - nT(); // Skip alias keyword. - ident = requireIdentifier(MSG.ExpectedAliasTemplateParam); - parseSpecAndOrDefaultType(); - tp = new TemplateAliasParameter(ident, specType, defType); - break; - case T.Identifier: - ident = token.ident; - switch (peekNext()) - { - case T.Ellipses: - // TemplateTupleParameter: - // Identifier ... - nT(); // Skip Identifier. - nT(); // Skip Ellipses. - if (token.type == T.Comma) - error(MID.TemplateTupleParameter); - tp = new TemplateTupleParameter(ident); - break; - case T.Comma, T.RParen, T.Colon, T.Assign: - // TemplateTypeParameter: - // Identifier - nT(); // Skip Identifier. - parseSpecAndOrDefaultType(); - tp = new TemplateTypeParameter(ident, specType, defType); - break; - default: - // TemplateValueParameter: - // Declarator - ident = null; - goto LTemplateValueParameter; - } - break; - version(D2) - { - case T.This: - // TemplateThisParameter - // this TemplateTypeParameter - nT(); // Skip 'this' keyword. - ident = requireIdentifier(MSG.ExpectedNameForThisTempParam); - parseSpecAndOrDefaultType(); - tp = new TemplateThisParameter(ident, specType, defType); - break; - } - default: - LTemplateValueParameter: - // TemplateValueParameter: - // Declarator - Expression specValue, defValue; - auto valueType = parseDeclarator(ident); - // : SpecializationValue - if (skipped(T.Colon)) - specValue = parseCondExpression(); - // = DefaultValue - if (skipped(T.Assign)) - defValue = parseCondExpression(); - tp = new TemplateValueParameter(valueType, ident, specValue, defValue); - } - - // Push template parameter. - tparams ~= set(tp, paramBegin); - - if (token.type != T.Comma) - break; - nT(); - } - set(tparams, begin); - return tparams; - } - - void expected(TOK tok) - { - if (token.type != tok) - error(MID.ExpectedButFound, Token.toString(tok), token.srcText); - } - - void require(TOK tok) - { - if (token.type == tok) - nT(); - else - error(MID.ExpectedButFound, Token.toString(tok), token.srcText); - } - - void requireNext(TOK tok) - { - nT(); - require(tok); - } - - Identifier* optionalIdentifier() - { - Identifier* id; - if (token.type == T.Identifier) - (id = token.ident), nT(); - return id; - } - - Identifier* requireIdentifier() - { - Identifier* id; - if (token.type == T.Identifier) - (id = token.ident), nT(); - else - error(MID.ExpectedButFound, "Identifier", token.srcText); - return id; - } - - /++ - Params: - errorMsg = an error that has no message ID yet. - +/ - Identifier* requireIdentifier(char[] errorMsg) - { - Identifier* id; - if (token.type == T.Identifier) - (id = token.ident), nT(); - else - error(token, errorMsg, token.srcText); - return id; - } - - Identifier* requireIdentifier(MID mid) - { - Identifier* id; - if (token.type == T.Identifier) - (id = token.ident), nT(); - else - error(mid, token.srcText); - return id; - } - - Token* requireId() - { - if (token.type == T.Identifier) - { - auto id = token; - nT(); - return id; - } - else - error(MID.ExpectedButFound, "Identifier", token.srcText); - return null; - } - - Token* requireIdToken(char[] errorMsg) - { - Token* idtok; - if (token.type == T.Identifier) - (idtok = token), nT(); - else - error(token, errorMsg, token.srcText); - return idtok; - } - - /// Reports an error that has no message ID yet. - void error(Token* token, char[] formatMsg, ...) - { - error_(token, formatMsg, _arguments, _argptr); - } - - void error(MID mid, ...) - { - error_(this.token, GetMsg(mid), _arguments, _argptr); - } - - void error_(Token* token, char[] formatMsg, TypeInfo[] _arguments, void* _argptr) - { - if (trying) - { - ++errorCount; - return; - } - auto location = token.getLocation(); - auto msg = Format(_arguments, _argptr, formatMsg); - auto error = new ParserError(location, msg); - errors ~= error; - if (infoMan !is null) - infoMan ~= error; - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/src/dil/parser/Parser.d Sat Jan 05 17:29:47 2008 +0100 @@ -0,0 +1,4221 @@ +/++ + Author: Aziz Köksal + License: GPL3 ++/ +module dil.parser.Parser; + +import dil.lexer.Lexer; +import dil.SyntaxTree; +import dil.Token; +import dil.Messages; +import dil.Information; +import dil.Declarations; +import dil.Statements; +import dil.Expressions; +import dil.Types; +import dil.Enums; +import dil.CompilerInfo; +import dil.IdTable; +import common; + +/++ + The Parser produces an abstract syntax tree (AST) by analyzing + the tokens of the provided source code. ++/ +class Parser +{ + Lexer lx; + Token* token; /// Current non-whitespace token. + Token* prevToken; /// Previous non-whitespace token. + + InfoManager infoMan; + ParserError[] errors; + + ImportDeclaration[] imports; /// ImportDeclarations in the source text. + + LinkageType linkageType; + Protection protection; + StorageClass storageClass; + uint alignSize = DEFAULT_ALIGN_SIZE; + + private alias TOK T; + private alias TypeNode Type; + + /++ + Construct a Parser object. + Params: + text = the UTF-8 source code. + filePath = the path to the source code; used for error messages. + +/ + this(char[] srcText, string filePath, InfoManager infoMan = null) + { + this.infoMan = infoMan; + lx = new Lexer(srcText, filePath, infoMan); + } + + protected void init() + { + nT(); + prevToken = token; + } + + void nT() + { + prevToken = token; + do + { + lx.nextToken(); + token = lx.token; + } while (token.isWhitespace) // Skip whitespace + } + + /++ + Start the parser and return the parsed Declarations. + +/ + Declarations start() + { + init(); + auto begin = token; + auto decls = new Declarations; + if (token.type == T.Module) + decls ~= parseModuleDeclaration(); + decls.addOptChildren(parseDeclarationDefinitions()); + set(decls, begin); + return decls; + } + + /++ + Start the parser and return the parsed Expression. + +/ + Expression start2() + { + init(); + return parseExpression(); + } + + uint trying; + uint errorCount; + + /++ + This method executes the delegate parseMethod and when an error occurred + the state of the lexer and parser are restored. + +/ + ReturnType try_(ReturnType)(ReturnType delegate() parseMethod, out bool success) + { + auto oldToken = this.token; + auto oldPrevToken = this.prevToken; + auto oldCount = this.errorCount; + + ++trying; + auto result = parseMethod(); + --trying; + // Check if an error occurred. + if (errorCount != oldCount) + { + // Restore members. + token = oldToken; + prevToken = oldPrevToken; + lx.token = oldToken; + errorCount = oldCount; + success = false; + } + else + success = true; + return result; + } + + /++ + Sets the begin and end tokens of an AST node. + +/ + Class set(Class)(Class node, Token* begin) + { + node.setTokens(begin, this.prevToken); + return node; + } + + /++ + Returns true if set() has been called on a node. + +/ + bool isNodeSet(Node node) + { + return node.begin !is null && node.end !is null; + } + + TOK peekNext() + { + Token* next = token; + do + lx.peek(next); + while (next.isWhitespace) // Skip whitespace + return next.type; + } + + TOK peekAfter(ref Token* next) + { + assert(next !is null); + do + lx.peek(next); + while (next.isWhitespace) // Skip whitespace + return next.type; + } + + /// Skips the current token if its type matches tok and returns true. + bool skipped()(TOK tok) // Templatized, so it's inlined. + { + return token.type == tok ? (nT(), true) : false; + } + + /++++++++++++++++++++++++++++++ + + Declaration parsing methods + + ++++++++++++++++++++++++++++++/ + + Declaration parseModuleDeclaration() + { + auto begin = token; + ModuleFQN moduleFQN; + do + { + nT(); + moduleFQN ~= requireIdentifier(MSG.ExpectedModuleIdentifier); + } while (token.type == T.Dot) + require(T.Semicolon); + return set(new ModuleDeclaration(moduleFQN), begin); + } + + /++ + Parse DeclarationDefinitions until the end of file is hit. + DeclDefs: + DeclDef + DeclDefs + +/ + Declaration[] parseDeclarationDefinitions() + { + Declaration[] decls; + while (token.type != T.EOF) + decls ~= parseDeclarationDefinition(); + return decls; + } + + /++ + Parse the body of a template, class, interface, struct or union. + DeclDefsBlock: + { } + { DeclDefs } + +/ + Declarations parseDeclarationDefinitionsBody() + { + // Save attributes. + auto linkageType = this.linkageType; + auto protection = this.protection; + auto storageClass = this.storageClass; + // Clear attributes. + this.linkageType = LinkageType.None; + this.protection = Protection.None; + this.storageClass = StorageClass.None; + + // Parse body. + auto begin = token; + auto decls = new Declarations; + require(T.LBrace); + while (token.type != T.RBrace && token.type != T.EOF) + decls ~= parseDeclarationDefinition(); + require(T.RBrace); + set(decls, begin); + + // Restore original values. + this.linkageType = linkageType; + this.protection = protection; + this.storageClass = storageClass; + + return decls; + } + + Declaration parseDeclarationDefinition() + out(decl) + { assert(isNodeSet(decl)); } + body + { + auto begin = token; + Declaration decl; + switch (token.type) + { + case T.Align, + T.Pragma, + // Protection attributes + T.Export, + T.Private, + T.Package, + T.Protected, + T.Public: + decl = parseAttributeSpecifier(); + break; + // Storage classes + case T.Extern, + T.Deprecated, + T.Override, + T.Abstract, + T.Synchronized, + //T.Static, + T.Final, + T.Const, + //T.Invariant, // D 2.0 + T.Auto, + T.Scope: + case_StaticAttribute: + case_InvariantAttribute: // D 2.0 + return parseStorageAttribute(); + case T.Alias: + nT(); + // TODO: parse StorageClasses? + decl = new AliasDeclaration(parseVariableOrFunction()); + break; + case T.Typedef: + nT(); + // TODO: parse StorageClasses? + decl = new TypedefDeclaration(parseVariableOrFunction()); + break; + case T.Static: + switch (peekNext()) + { + case T.Import: + goto case_Import; + case T.This: + decl = parseStaticConstructorDeclaration(); + break; + case T.Tilde: + decl = parseStaticDestructorDeclaration(); + break; + case T.If: + decl = parseStaticIfDeclaration(); + break; + case T.Assert: + decl = parseStaticAssertDeclaration(); + break; + default: + goto case_StaticAttribute; + } + break; + case T.Import: + case_Import: + decl = parseImportDeclaration(); + imports ~= CastTo!(ImportDeclaration)(decl); + // Handle specially. StorageClass mustn't be set. + decl.setProtection(this.protection); + return set(decl, begin); + case T.Enum: + decl = parseEnumDeclaration(); + break; + case T.Class: + decl = parseClassDeclaration(); + break; + case T.Interface: + decl = parseInterfaceDeclaration(); + break; + case T.Struct, T.Union: + decl = parseAggregateDeclaration(); + break; + case T.This: + decl = parseConstructorDeclaration(); + break; + case T.Tilde: + decl = parseDestructorDeclaration(); + break; + case T.Invariant: + version(D2) + { + auto next = token; + if (peekAfter(next) == T.LParen) + { + if (peekAfter(next) != T.RParen) + goto case_Declaration; + } + else + goto case_InvariantAttribute; + } + decl = parseInvariantDeclaration(); + break; + case T.Unittest: + decl = parseUnittestDeclaration(); + break; + case T.Debug: + decl = parseDebugDeclaration(); + break; + case T.Version: + decl = parseVersionDeclaration(); + break; + case T.Template: + decl = parseTemplateDeclaration(); + break; + case T.New: + decl = parseNewDeclaration(); + break; + case T.Delete: + decl = parseDeleteDeclaration(); + break; + case T.Mixin: + decl = parseMixin!(MixinDeclaration)(); + break; + case T.Semicolon: + nT(); + decl = new EmptyDeclaration(); + break; + // Declaration + case T.Identifier, T.Dot, T.Typeof: + case_Declaration: + return parseVariableOrFunction(this.storageClass, this.protection, this.linkageType); + /+case T.Module: + // TODO: Error: module is optional and can appear only once at the top of the source file. + break;+/ + default: + if (token.isIntegralType) + goto case_Declaration; + + decl = new IllegalDeclaration(); + // Skip to next valid token. + do + nT(); + while (!token.isDeclDefStart && + token.type != T.RBrace && + token.type != T.EOF) + auto text = Token.textSpan(begin, this.prevToken); + error(begin, MSG.IllegalDeclaration ~ text); + } + decl.setProtection(this.protection); + decl.setStorageClass(this.storageClass); + assert(!isNodeSet(decl)); + set(decl, begin); + return decl; + } + + /++ + DeclarationsBlock: + : DeclDefs + { } + { DeclDefs } + DeclDef + +/ + Declaration parseDeclarationsBlock(bool noColon = false) + { + Declaration d; + switch (token.type) + { + case T.LBrace: + auto begin = token; + nT(); + auto decls = new Declarations; + while (token.type != T.RBrace && token.type != T.EOF) + decls ~= parseDeclarationDefinition(); + require(T.RBrace); + d = set(decls, begin); + break; + case T.Colon: + if (noColon == true) + goto default; + nT(); + auto begin = token; + auto decls = new Declarations; + while (token.type != T.RBrace && token.type != T.EOF) + decls ~= parseDeclarationDefinition(); + d = set(decls, begin); + break; + default: + d = parseDeclarationDefinition(); + } + assert(isNodeSet(d)); + return d; + } + + Declaration parseDeclarationsBlockNoColon() + { + return parseDeclarationsBlock(true); + } + + /++ + Parses either a VariableDeclaration or a FunctionDeclaration. + Params: + stc = previously parsed storage classes + protection = previously parsed protection attribute + linkType = previously parsed linkage type + testAutoDeclaration = whether to check for an AutoDeclaration + optionalParameterList = a hint for how to parse C-style function pointers + +/ + Declaration parseVariableOrFunction(StorageClass stc = StorageClass.None, + Protection protection = Protection.None, + LinkageType linkType = LinkageType.None, + bool testAutoDeclaration = false, + bool optionalParameterList = true) + { + auto begin = token; + Type type; + Identifier* ident; + + // Check for AutoDeclaration: StorageClasses Identifier = + if (testAutoDeclaration && + token.type == T.Identifier && + peekNext() == T.Assign) + { + ident = token.ident; + nT(); + } + else + { + type = parseType(); // VariableType or ReturnType + if (token.type == T.LParen) + { + // C-style function pointers make the grammar ambiguous. + // We have to treat them specially at function scope. + // Example: + // void foo() { + // // A pointer to a function taking an integer and returning 'some_type'. + // some_type (*p_func)(int); + // // In the following case precedence is given to a CallExpression. + // something(*p); // 'something' may be a function/method or an object having opCall overloaded. + // } + // // A pointer to a function taking no parameters and returning 'something'. + // something(*p); + type = parseCFunctionPointerType(type, ident, optionalParameterList); + } + else if (peekNext() == T.LParen) + { // Type FunctionName ( ParameterList ) FunctionBody + ident = requireIdentifier(MSG.ExpectedFunctionName); + ident || nT(); // Skip non-identifier token. + assert(token.type == T.LParen); + // It's a function declaration + TemplateParameters tparams; + if (tokenAfterParenIs(T.LParen)) + { + // ( TemplateParameterList ) ( ParameterList ) + tparams = parseTemplateParameterList(); + } + + auto params = parseParameterList(); + version(D2) + { + switch (token.type) + { + case T.Const: + stc |= StorageClass.Const; + nT(); + break; + case T.Invariant: + stc |= StorageClass.Invariant; + nT(); + break; + default: + } + } + // ReturnType FunctionName ( ParameterList ) + auto funcBody = parseFunctionBody(); + auto d = new FunctionDeclaration(type, ident, tparams, params, funcBody); + d.setStorageClass(stc); + d.setLinkageType(linkType); + d.setProtection(protection); + return set(d, begin); + } + else + { + // Type VariableName DeclaratorSuffix + ident = requireIdentifier(MSG.ExpectedVariableName); + type = parseDeclaratorSuffix(type); + } + } + + // It's a variable declaration. + Identifier*[] idents = [ident]; + Expression[] values; + goto LenterLoop; // We've already parsed an identifier. Jump to if statement and check for initializer. + while (token.type == T.Comma) + { + nT(); + idents ~= requireIdentifier(MSG.ExpectedVariableName); + LenterLoop: + if (skipped(T.Assign)) + values ~= parseInitializer(); + else + values ~= null; + } + require(T.Semicolon); + auto d = new VariableDeclaration(type, idents, values); + d.setStorageClass(stc); + d.setLinkageType(linkType); + d.setProtection(protection); + return set(d, begin); + } + + Expression parseInitializer() + { + if (token.type == T.Void) + { + auto begin = token; + auto next = peekNext(); + if (next == T.Comma || next == T.Semicolon) + { + nT(); + return set(new VoidInitializer(), begin); + } + } + return parseNonVoidInitializer(); + } + + Expression parseNonVoidInitializer() + { + auto begin = token; + Expression init; + switch (token.type) + { + case T.LBracket: + // ArrayInitializer: + // [ ] + // [ ArrayMemberInitializations ] + Expression[] keys; + Expression[] values; + + nT(); + while (token.type != T.RBracket) + { + auto e = parseNonVoidInitializer(); + if (skipped(T.Colon)) + { + keys ~= e; + values ~= parseNonVoidInitializer(); + } + else + { + keys ~= null; + values ~= e; + } + + if (token.type != T.Comma) + break; + nT(); + } + require(T.RBracket); + init = new ArrayInitializer(keys, values); + break; + case T.LBrace: + // StructInitializer: + // { } + // { StructMemberInitializers } + Expression parseStructInitializer() + { + Identifier*[] idents; + Expression[] values; + + nT(); + while (token.type != T.RBrace) + { + if (token.type == T.Identifier && + // Peek for colon to see if this is a member identifier. + peekNext() == T.Colon) + { + idents ~= token.ident; + nT(), nT(); // Skip Identifier : + } + else + idents ~= null; + + // NonVoidInitializer + values ~= parseNonVoidInitializer(); + + if (token.type != T.Comma) + break; + nT(); + } + require(T.RBrace); + return new StructInitializer(idents, values); + } + + bool success; + auto si = try_(&parseStructInitializer, success); + if (success) + { + init = si; + break; + } + assert(token.type == T.LBrace); + //goto default; + default: + init = parseAssignExpression(); + } + set(init, begin); + return init; + } + + FunctionBody parseFunctionBody() + { + auto begin = token; + auto func = new FunctionBody; + while (1) + { + switch (token.type) + { + case T.LBrace: + func.funcBody = parseStatements(); + break; + case T.Semicolon: + nT(); + break; + case T.In: + if (func.inBody) + error(MID.InContract); + nT(); + func.inBody = parseStatements(); + continue; + case T.Out: + if (func.outBody) + error(MID.OutContract); + nT(); + if (skipped(T.LParen)) + { + func.outIdent = requireIdentifier(MSG.ExpectedAnIdentifier); + require(T.RParen); + } + func.outBody = parseStatements(); + continue; + case T.Body: + nT(); + goto case T.LBrace; + default: + error(token, MSG.ExpectedFunctionBody, token.srcText); + } + break; // Exit loop. + } + set(func, begin); + func.finishConstruction(); + return func; + } + + LinkageType parseLinkageType() + { + LinkageType linkageType; + if (token.type != T.LParen) + return linkageType; + + nT(); // Skip ( + if (token.type == T.RParen) + { + nT(); + error(MID.MissingLinkageType); + return linkageType; + } + + auto identTok = requireId(); + + ID identID = identTok ? identTok.ident.identID : ID.Null; + + switch (identID) + { + case ID.C: + if (skipped(T.PlusPlus)) + { + linkageType = LinkageType.Cpp; + break; + } + linkageType = LinkageType.C; + break; + case ID.D: + linkageType = LinkageType.D; + break; + case ID.Windows: + linkageType = LinkageType.Windows; + break; + case ID.Pascal: + linkageType = LinkageType.Pascal; + break; + case ID.System: + linkageType = LinkageType.System; + break; + default: + error(MID.UnrecognizedLinkageType, token.srcText); + } + require(T.RParen); + return linkageType; + } + + void checkLinkageType(ref LinkageType prev_lt, LinkageType lt, Token* begin) + { + if (prev_lt == LinkageType.None) + prev_lt = lt; + else + // TODO: create new msg RedundantLinkageType. + error(begin, MSG.RedundantLinkageType ~ Token.textSpan(begin, this.prevToken)); + } + + Declaration parseStorageAttribute() + { + StorageClass stc, stc_tmp; + LinkageType prev_linkageType; + + auto saved_storageClass = this.storageClass; // Save. + // Nested function. + Declaration parse() + { + Declaration decl; + auto begin = token; + switch (token.type) + { + case T.Extern: + if (peekNext() != T.LParen) + { + stc_tmp = StorageClass.Extern; + goto Lcommon; + } + + nT(); + auto linkageType = parseLinkageType(); + checkLinkageType(prev_linkageType, linkageType, begin); + + auto saved = this.linkageType; // Save. + this.linkageType = linkageType; // Set. + decl = new LinkageDeclaration(linkageType, parse()); + set(decl, begin); + this.linkageType = saved; // Restore. + break; + case T.Override: + stc_tmp = StorageClass.Override; + goto Lcommon; + case T.Deprecated: + stc_tmp = StorageClass.Deprecated; + goto Lcommon; + case T.Abstract: + stc_tmp = StorageClass.Abstract; + goto Lcommon; + case T.Synchronized: + stc_tmp = StorageClass.Synchronized; + goto Lcommon; + case T.Static: + stc_tmp = StorageClass.Static; + goto Lcommon; + case T.Final: + stc_tmp = StorageClass.Final; + goto Lcommon; + case T.Const: + version(D2) + { + if (peekNext() == T.LParen) + goto case_Declaration; + } + stc_tmp = StorageClass.Const; + goto Lcommon; + version(D2) + { + case T.Invariant: // D 2.0 + auto next = token; + if (peekAfter(next) == T.LParen) + { + if (peekAfter(next) != T.RParen) + goto case_Declaration; // invariant ( Type ) + decl = parseDeclarationDefinition(); // invariant ( ) + decl.setStorageClass(stc); + break; + } + // invariant as StorageClass. + stc_tmp = StorageClass.Invariant; + goto Lcommon; + } + case T.Auto: + stc_tmp = StorageClass.Auto; + goto Lcommon; + case T.Scope: + stc_tmp = StorageClass.Scope; + goto Lcommon; + Lcommon: + // Issue error if redundant. + if (stc & stc_tmp) + error(MID.RedundantStorageClass, token.srcText); + else + stc |= stc_tmp; + + auto tok = token.type; + nT(); + decl = new StorageClassDeclaration(stc_tmp, tok, parse()); + set(decl, begin); + break; + case T.Identifier: + case_Declaration: + // This could be a normal Declaration or an AutoDeclaration + decl = parseVariableOrFunction(stc, this.protection, prev_linkageType, true); + break; + default: + this.storageClass = stc; // Set. + decl = parseDeclarationsBlock(); + this.storageClass = saved_storageClass; // Reset. + } + assert(isNodeSet(decl)); + return decl; + } + return parse(); + } + + uint parseAlignAttribute() + { + assert(token.type == T.Align); + nT(); // Skip align keyword. + uint size = DEFAULT_ALIGN_SIZE; // Global default. + if (skipped(T.LParen)) + { + if (token.type == T.Int32) + (size = token.int_), nT(); + else + expected(T.Int32); + require(T.RParen); + } + return size; + } + + Declaration parseAttributeSpecifier() + { + Declaration decl; + + switch (token.type) + { + case T.Align: + uint alignSize = parseAlignAttribute(); + auto saved = this.alignSize; // Save. + this.alignSize = alignSize; // Set. + decl = new AlignDeclaration(alignSize, parseDeclarationsBlock()); + this.alignSize = saved; // Restore. + break; + case T.Pragma: + // Pragma: + // pragma ( Identifier ) + // pragma ( Identifier , ExpressionList ) + nT(); + Identifier* ident; + Expression[] args; + + require(T.LParen); + ident = requireIdentifier(MSG.ExpectedPragmaIdentifier); + + if (skipped(T.Comma)) + args = parseExpressionList(); + require(T.RParen); + + decl = new PragmaDeclaration(ident, args, parseDeclarationsBlock()); + break; + default: + // Protection attributes + Protection prot; + switch (token.type) + { + case T.Private: + prot = Protection.Private; break; + case T.Package: + prot = Protection.Package; break; + case T.Protected: + prot = Protection.Protected; break; + case T.Public: + prot = Protection.Public; break; + case T.Export: + prot = Protection.Export; break; + default: + assert(0); + } + nT(); + auto saved = this.protection; // Save. + this.protection = prot; // Set. + decl = new ProtectionDeclaration(prot, parseDeclarationsBlock()); + this.protection = saved; // Restore. + } + return decl; + } + + Declaration parseImportDeclaration() + { + assert(token.type == T.Import || token.type == T.Static); + bool isStatic = skipped(T.Static); + assert(token.type == T.Import); + nT(); // Skip import keyword. + + ModuleFQN[] moduleFQNs; + Identifier*[] moduleAliases; + Identifier*[] bindNames; + Identifier*[] bindAliases; + + while (1) + { + ModuleFQN moduleFQN; + Identifier* moduleAlias; + + // AliasName = ModuleName + if (peekNext() == T.Assign) + { + moduleAlias = requireIdentifier(MSG.ExpectedAliasModuleName); + nT(); // Skip = + } + + // Identifier(.Identifier)* + while (1) + { + moduleFQN ~= requireIdentifier(MSG.ExpectedModuleIdentifier); + if (token.type != T.Dot) + break; + nT(); + } + + // Push identifiers. + moduleFQNs ~= moduleFQN; + moduleAliases ~= moduleAlias; + + if (token.type != T.Comma) + break; + nT(); + } + + if (token.type == T.Colon) + { + // BindAlias = BindName(, BindAlias = BindName)*; + // BindName(, BindName)*; + do + { + nT(); + Identifier* bindAlias; + // BindAlias = BindName + if (peekNext() == T.Assign) + { + bindAlias = requireIdentifier(MSG.ExpectedAliasImportName); + nT(); // Skip = + } + // Push identifiers. + bindNames ~= requireIdentifier(MSG.ExpectedImportName); + bindAliases ~= bindAlias; + } while (token.type == T.Comma) + } + + require(T.Semicolon); + + return new ImportDeclaration(moduleFQNs, moduleAliases, bindNames, bindAliases, isStatic); + } + + Declaration parseEnumDeclaration() + { + assert(token.type == T.Enum); + nT(); // Skip enum keyword. + + Identifier* enumName; + Type baseType; + EnumMember[] members; + bool hasBody; + + enumName = optionalIdentifier(); + + if (skipped(T.Colon)) + baseType = parseBasicType(); + + if (skipped(T.Semicolon)) + { + if (enumName is null) + expected(T.Identifier); + } + else if (skipped(T.LBrace)) + { + hasBody = true; + while (token.type != T.RBrace) + { + auto begin = token; + auto name = requireIdentifier(MSG.ExpectedEnumMember); + Expression value; + + if (skipped(T.Assign)) + value = parseAssignExpression(); + else + value = null; + + members ~= set(new EnumMember(name, value), begin); + + if (token.type != T.Comma) + break; + nT(); // Skip , + } + require(T.RBrace); + } + else + error(token, MSG.ExpectedEnumBody, token.srcText); + + return new EnumDeclaration(enumName, baseType, members, hasBody); + } + + Declaration parseClassDeclaration() + { + assert(token.type == T.Class); + nT(); // Skip class keyword. + + Identifier* className; + TemplateParameters tparams; + BaseClass[] bases; + Declarations decls; + + className = requireIdentifier(MSG.ExpectedClassName); + + if (token.type == T.LParen) + tparams = parseTemplateParameterList(); + + if (token.type == T.Colon) + bases = parseBaseClasses(); + + if (skipped(T.Semicolon)) + { + if (bases.length != 0) + error(MID.BaseClassInForwardDeclaration); + } + else if (token.type == T.LBrace) + decls = parseDeclarationDefinitionsBody(); + else + error(token, MSG.ExpectedClassBody, token.srcText); + + return new ClassDeclaration(className, tparams, bases, decls); + } + + BaseClass[] parseBaseClasses(bool colonLeadsOff = true) + { + if (colonLeadsOff) + { + assert(token.type == T.Colon); + nT(); // Skip colon + } + + BaseClass[] bases; + + while (1) + { + Protection prot = Protection.Public; + switch (token.type) + { + case T.Identifier, T.Dot, T.Typeof: goto LparseBasicType; + case T.Private: prot = Protection.Private; break; + case T.Protected: prot = Protection.Protected; break; + case T.Package: prot = Protection.Package; break; + case T.Public: /*prot = Protection.Public;*/ break; + default: + error(MID.ExpectedBaseClasses, token.srcText); + return bases; + } + nT(); // Skip protection attribute. + LparseBasicType: + auto begin = token; + auto type = parseBasicType(); + //if (type.tid != TID.DotList) + // TODO: issue error msg. base classes can only be one or more identifiers or template instances separated by dots. + bases ~= set(new BaseClass(prot, type), begin); + if (token.type != T.Comma) + break; + nT(); + } + return bases; + } + + Declaration parseInterfaceDeclaration() + { + assert(token.type == T.Interface); + nT(); // Skip interface keyword. + + Identifier* name; + TemplateParameters tparams; + BaseClass[] bases; + Declarations decls; + + name = requireIdentifier(MSG.ExpectedInterfaceName); + + if (token.type == T.LParen) + tparams = parseTemplateParameterList(); + + if (token.type == T.Colon) + bases = parseBaseClasses(); + + if (skipped(T.Semicolon)) + { + if (bases.length != 0) + error(MID.BaseClassInForwardDeclaration); + } + else if (token.type == T.LBrace) + decls = parseDeclarationDefinitionsBody(); + else + error(token, MSG.ExpectedInterfaceBody, token.srcText); + + return new InterfaceDeclaration(name, tparams, bases, decls); + } + + Declaration parseAggregateDeclaration() + { + assert(token.type == T.Struct || token.type == T.Union); + TOK tok = token.type; + nT(); // Skip struct or union keyword. + + Identifier* name; + TemplateParameters tparams; + Declarations decls; + + name = optionalIdentifier(); + + if (name && token.type == T.LParen) + tparams = parseTemplateParameterList(); + + if (skipped(T.Semicolon)) + { + //if (name.length == 0) + // TODO: error: forward declarations must have a name. + } + else if (token.type == T.LBrace) + decls = parseDeclarationDefinitionsBody(); + else + expected(T.LBrace); // TODO: better error msg + + if (tok == T.Struct) + { + auto sd = new StructDeclaration(name, tparams, decls); + sd.setAlignSize(this.alignSize); + return sd; + } + else + return new UnionDeclaration(name, tparams, decls); + } + + Declaration parseConstructorDeclaration() + { + assert(token.type == T.This); + nT(); // Skip 'this' keyword. + auto parameters = parseParameterList(); + auto funcBody = parseFunctionBody(); + return new ConstructorDeclaration(parameters, funcBody); + } + + Declaration parseDestructorDeclaration() + { + assert(token.type == T.Tilde); + nT(); // Skip ~ + require(T.This); + require(T.LParen); + require(T.RParen); + auto funcBody = parseFunctionBody(); + return new DestructorDeclaration(funcBody); + } + + Declaration parseStaticConstructorDeclaration() + { + assert(token.type == T.Static); + nT(); // Skip static keyword. + nT(); // Skip 'this' keyword. + require(T.LParen); + require(T.RParen); + auto funcBody = parseFunctionBody(); + return new StaticConstructorDeclaration(funcBody); + } + + Declaration parseStaticDestructorDeclaration() + { + assert(token.type == T.Static); + nT(); // Skip static keyword. + nT(); // Skip ~ + require(T.This); + require(T.LParen); + require(T.RParen); + auto funcBody = parseFunctionBody(); + return new StaticDestructorDeclaration(funcBody); + } + + Declaration parseInvariantDeclaration() + { + assert(token.type == T.Invariant); + nT(); // Skip invariant keyword. + // Optional () for getting ready porting to D 2.0 + if (token.type == T.LParen) + requireNext(T.RParen); + auto funcBody = parseFunctionBody(); + return new InvariantDeclaration(funcBody); + } + + Declaration parseUnittestDeclaration() + { + assert(token.type == T.Unittest); + nT(); // Skip unittest keyword. + auto funcBody = parseFunctionBody(); + return new UnittestDeclaration(funcBody); + } + + Token* parseIdentOrInt() + { + if (skipped(T.Int32) || skipped(T.Identifier)) + return this.prevToken; + error(token, MSG.ExpectedIdentOrInt, token.srcText); + return null; + } + + Declaration parseDebugDeclaration() + { + assert(token.type == T.Debug); + nT(); // Skip debug keyword. + + Token* spec; + Token* cond; + Declaration decls, elseDecls; + + if (skipped(T.Assign)) + { // debug = Integer ; + // debug = Identifier ; + spec = parseIdentOrInt(); + require(T.Semicolon); + } + else + { // ( Condition ) + if (skipped(T.LParen)) + { + cond = parseIdentOrInt(); + require(T.RParen); + } + // debug DeclarationsBlock + // debug ( Condition ) DeclarationsBlock + decls = parseDeclarationsBlockNoColon(); + // else DeclarationsBlock + if (skipped(T.Else)) + elseDecls = parseDeclarationsBlockNoColon(); + } + + return new DebugDeclaration(spec, cond, decls, elseDecls); + } + + Declaration parseVersionDeclaration() + { + assert(token.type == T.Version); + nT(); // Skip version keyword. + + Token* spec; + Token* cond; + Declaration decls, elseDecls; + + if (skipped(T.Assign)) + { // version = Integer ; + // version = Identifier ; + spec = parseIdentOrInt(); + require(T.Semicolon); + } + else + { // ( Condition ) + require(T.LParen); + cond = parseIdentOrInt(); + require(T.RParen); + // version ( Condition ) DeclarationsBlock + decls = parseDeclarationsBlockNoColon(); + // else DeclarationsBlock + if (skipped(T.Else)) + elseDecls = parseDeclarationsBlockNoColon(); + } + + return new VersionDeclaration(spec, cond, decls, elseDecls); + } + + Declaration parseStaticIfDeclaration() + { + assert(token.type == T.Static); + nT(); // Skip static keyword. + nT(); // Skip if keyword. + + Expression condition; + Declaration ifDecls, elseDecls; + + require(T.LParen); + condition = parseAssignExpression(); + require(T.RParen); + + ifDecls = parseDeclarationsBlockNoColon(); + + if (skipped(T.Else)) + elseDecls = parseDeclarationsBlockNoColon(); + + return new StaticIfDeclaration(condition, ifDecls, elseDecls); + } + + Declaration parseStaticAssertDeclaration() + { + assert(token.type == T.Static); + nT(); // Skip static keyword. + nT(); // Skip assert keyword. + Expression condition, message; + require(T.LParen); + condition = parseAssignExpression(); + if (skipped(T.Comma)) + message = parseAssignExpression(); + require(T.RParen); + require(T.Semicolon); + return new StaticAssertDeclaration(condition, message); + } + + Declaration parseTemplateDeclaration() + { + assert(token.type == T.Template); + nT(); // Skip template keyword. + auto templateName = requireIdentifier(MSG.ExpectedTemplateName); + auto templateParams = parseTemplateParameterList(); + auto decls = parseDeclarationDefinitionsBody(); + return new TemplateDeclaration(templateName, templateParams, decls); + } + + Declaration parseNewDeclaration() + { + assert(token.type == T.New); + nT(); // Skip new keyword. + auto parameters = parseParameterList(); + auto funcBody = parseFunctionBody(); + return new NewDeclaration(parameters, funcBody); + } + + Declaration parseDeleteDeclaration() + { + assert(token.type == T.Delete); + nT(); // Skip delete keyword. + auto parameters = parseParameterList(); + // TODO: only one parameter of type void* allowed. Check in parsing or semantic phase? + auto funcBody = parseFunctionBody(); + return new DeleteDeclaration(parameters, funcBody); + } + + Type parseTypeofType() + { + assert(token.type == T.Typeof); + Type type; + requireNext(T.LParen); + switch (token.type) + { + version(D2) + { + case T.Return: + nT(); + type = new TypeofType(); + break; + } + default: + type = new TypeofType(parseExpression()); + } + require(T.RParen); + return type; + } + + /+ + DotListExpression: + . DotListItems + DotListItems + Typeof + Typeof . DotListItems + DotListItems: + DotListItem + DotListItem . DotListItems + DotListItem: + Identifier + TemplateInstance + NewExpression + TemplateInstance: + Identifier !( TemplateArguments ) + +/ + DotListExpression parseDotListExpression() + { + assert(token.type == T.Identifier || token.type == T.Dot || token.type == T.Typeof); + auto begin = token; + Expression[] identList; + if (skipped(T.Dot)) + identList ~= set(new DotExpression(), begin); + else if (token.type == T.Typeof) + { + auto type = parseTypeofType(); + set(type, begin); + identList ~= set(new TypeofExpression(type), begin); + if (!skipped(T.Dot)) + goto Lreturn; + } + + while (1) + { + begin = token; + auto ident = requireIdentifier(MSG.ExpectedAnIdentifier); + Expression e; + if (token.type == T.Not && peekNext() == T.LParen) // Identifier !( TemplateArguments ) + { + nT(); // Skip !. + auto tparams = parseTemplateArguments(); + e = new TemplateInstanceExpression(ident, tparams); + } + else // Identifier + e = new IdentifierExpression(ident); + + identList ~= set(e, begin); + + LnewExpressionLoop: + if (!skipped(T.Dot)) + break; + + if (token.type == T.New) + { + identList ~= parseNewExpression(); + goto LnewExpressionLoop; + } + } + + Lreturn: + return new DotListExpression(identList); + } + + /+ + DotListType: + . TypeItems + TypeItems + Typeof + Typeof . TypeItems + TypeItems: + TypeItem + TypeItem . TypeItems + TypeItem: + Identifier + TemplateInstance + TemplateInstance: + Identifier !( TemplateArguments ) + +/ + DotListType parseDotListType() + { + auto begin = token; + Type[] identList; + if (skipped(T.Dot)) + identList ~= set(new DotType(), begin); + else if (token.type == T.Typeof) + { + identList ~= set(parseTypeofType(), begin); + if (!skipped(T.Dot)) + goto Lreturn; + } + + do + { + begin = token; + auto ident = requireIdentifier(MSG.ExpectedAnIdentifier); + Type t; + // NB.: Currently Types can't be followed by "!=" + // so no need to peek for "(" when parsing TemplateInstances. + if (skipped(T.Not)/+ && peekNext() == T.LParen+/) // Identifier !( TemplateArguments ) + t = new TemplateInstanceType(ident, parseTemplateArguments()); + else // Identifier + t = new IdentifierType(ident); + identList ~= set(t, begin); + } while(skipped(T.Dot)) + Lreturn: + return new DotListType(identList); + } + + /* + TemplateMixin: + mixin ( AssignExpression ) ; + mixin TemplateIdentifier ; + mixin TemplateIdentifier MixinIdentifier ; + mixin TemplateIdentifier !( TemplateArguments ) ; + mixin TemplateIdentifier !( TemplateArguments ) MixinIdentifier ; + */ + Class parseMixin(Class)() + { + assert(token.type == T.Mixin); + nT(); // Skip mixin keyword. + + static if (is(Class == MixinDeclaration)) + { + if (skipped(T.LParen)) + { + // TODO: What about mixin(...).ident;? + auto e = parseAssignExpression(); + require(T.RParen); + require(T.Semicolon); + return new MixinDeclaration(e); + } + } + + auto begin = token; + Expression[] templateIdent; + Identifier* mixinIdent; + + // This code is similar to parseDotListType(). + if (skipped(T.Dot)) + templateIdent ~= set(new DotExpression(), begin); + + do + { + begin = token; + auto ident = requireIdentifier(MSG.ExpectedAnIdentifier); + Expression e; + if (skipped(T.Not)) // Identifier !( TemplateArguments ) + { + // No need to peek for T.LParen. This must be a template instance. + auto tparams = parseTemplateArguments(); + e = new TemplateInstanceExpression(ident, tparams); + } + else // Identifier + e = new IdentifierExpression(ident); + templateIdent ~= set(e, begin); + } while (skipped(T.Dot)) + + mixinIdent = optionalIdentifier(); + require(T.Semicolon); + + return new Class(templateIdent, mixinIdent); + } + + /+++++++++++++++++++++++++++++ + + Statement parsing methods + + +++++++++++++++++++++++++++++/ + + Statements parseStatements() + { + auto begin = token; + require(T.LBrace); + auto statements = new Statements(); + while (token.type != T.RBrace && token.type != T.EOF) + statements ~= parseStatement(); + require(T.RBrace); + return set(statements, begin); + } + + Statement parseStatement() + { + auto begin = token; + Statement s; + Declaration d; + + if (token.isIntegralType) + { + d = parseVariableOrFunction(); + goto LreturnDeclarationStatement; + } + + switch (token.type) + { + case T.Align: + uint size = parseAlignAttribute(); + // Restrict align attribute to structs in parsing phase. + StructDeclaration structDecl; + if (token.type == T.Struct) + { + auto begin2 = token; + structDecl = CastTo!(StructDeclaration)(parseAggregateDeclaration()); + structDecl.setAlignSize(size); + set(structDecl, begin2); + } + else + expected(T.Struct); + + d = new AlignDeclaration(size, structDecl ? cast(Declaration)structDecl : new Declarations); + goto LreturnDeclarationStatement; + /+ Not applicable for statements. + T.Private, T.Package, T.Protected, T.Public, T.Export, + T.Deprecated, T.Override, T.Abstract,+/ + case T.Extern, + T.Final, + T.Const, + T.Auto: + //T.Scope + //T.Static + case_parseAttribute: + s = parseAttributeStatement(); + return s; + case T.Identifier: + if (peekNext() == T.Colon) + { + auto ident = token.ident; + nT(), nT(); // Skip Identifier : + s = new LabeledStatement(ident, parseNoScopeOrEmptyStatement()); + break; + } + goto case T.Dot; + case T.Dot, T.Typeof: + bool success; + d = try_(delegate { + return parseVariableOrFunction(StorageClass.None, + Protection.None, + LinkageType.None, false, false); + }, success + ); + if (success) + goto LreturnDeclarationStatement; // Declaration + else + goto case_parseExpressionStatement; // Expression + + case T.If: + s = parseIfStatement(); + break; + case T.While: + s = parseWhileStatement(); + break; + case T.Do: + s = parseDoWhileStatement(); + break; + case T.For: + s = parseForStatement(); + break; + case T.Foreach, T.Foreach_reverse: + s = parseForeachStatement(); + break; + case T.Switch: + s = parseSwitchStatement(); + break; + case T.Case: + s = parseCaseStatement(); + break; + case T.Default: + s = parseDefaultStatement(); + break; + case T.Continue: + s = parseContinueStatement(); + break; + case T.Break: + s = parseBreakStatement(); + break; + case T.Return: + s = parseReturnStatement(); + break; + case T.Goto: + s = parseGotoStatement(); + break; + case T.With: + s = parseWithStatement(); + break; + case T.Synchronized: + s = parseSynchronizedStatement(); + break; + case T.Try: + s = parseTryStatement(); + break; + case T.Throw: + s = parseThrowStatement(); + break; + case T.Scope: + if (peekNext() != T.LParen) + goto case_parseAttribute; + s = parseScopeGuardStatement(); + break; + case T.Volatile: + s = parseVolatileStatement(); + break; + case T.Asm: + s = parseAsmStatement(); + break; + case T.Pragma: + s = parsePragmaStatement(); + break; + case T.Mixin: + if (peekNext() == T.LParen) + goto case_parseExpressionStatement; // Parse as expression. + s = parseMixin!(MixinStatement)(); + break; + case T.Static: + switch (peekNext()) + { + case T.If: + s = parseStaticIfStatement(); + break; + case T.Assert: + s = parseStaticAssertStatement(); + break; + default: + goto case_parseAttribute; + } + break; + case T.Debug: + s = parseDebugStatement(); + break; + case T.Version: + s = parseVersionStatement(); + break; + // DeclDef + case T.Alias, T.Typedef: + d = parseDeclarationDefinition(); + goto LreturnDeclarationStatement; + case T.Enum: + d = parseEnumDeclaration(); + goto LreturnDeclarationStatement; + case T.Class: + d = parseClassDeclaration(); + goto LreturnDeclarationStatement; + case T.Interface: + d = parseInterfaceDeclaration(); + goto LreturnDeclarationStatement; + case T.Struct, T.Union: + d = parseAggregateDeclaration(); + // goto LreturnDeclarationStatement; + LreturnDeclarationStatement: + set(d, begin); + s = new DeclarationStatement(d); + break; + case T.LBrace: + s = parseScopeStatement(); + break; + case T.Semicolon: + nT(); + s = new EmptyStatement(); + break; + /+ + Parse ExpressionStatement: + +/ + // Tokens that start a PrimaryExpression. + // case T.Identifier, T.Dot, T.Typeof: + case T.This: + case T.Super: + case T.Null: + case T.True, T.False: + // case T.Dollar: + case T.Int32, T.Int64, T.Uint32, T.Uint64: + case T.Float32, T.Float64, T.Float80, + T.Imaginary32, T.Imaginary64, T.Imaginary80: + case T.CharLiteral: + case T.String: + case T.LBracket: + // case T.LBrace: + case T.Function, T.Delegate: + case T.Assert: + // case T.Mixin: + case T.Import: + case T.Typeid: + case T.Is: + case T.LParen: + case T.Traits: // D2.0 + // Tokens that can start a UnaryExpression: + case T.AndBinary, T.PlusPlus, T.MinusMinus, T.Mul, T.Minus, + T.Plus, T.Not, T.Tilde, T.New, T.Delete, T.Cast: + case_parseExpressionStatement: + s = new ExpressionStatement(parseExpression()); + require(T.Semicolon); + break; + default: + if (token.isSpecialToken) + goto case_parseExpressionStatement; + + if (token.type != T.Dollar) + // Assert that this isn't a valid expression. + assert(delegate bool(){ + bool success; + auto expression = try_(&parseExpression, success); + return success; + }() == false, "Didn't expect valid expression." + ); + + // Report error: it's an illegal statement. + s = new IllegalStatement(); + // Skip to next valid token. + do + nT(); + while (!token.isStatementStart && + token.type != T.RBrace && + token.type != T.EOF) + auto text = Token.textSpan(begin, this.prevToken); + error(begin, MSG.IllegalStatement ~ text); + } + assert(s !is null); + set(s, begin); + return s; + } + + /++ + ScopeStatement: + NoScopeStatement + +/ + Statement parseScopeStatement() + { + return new ScopeStatement(parseNoScopeStatement()); + } + + /++ + NoScopeStatement: + NonEmptyStatement + BlockStatement + BlockStatement: + { } + { StatementList } + +/ + Statement parseNoScopeStatement() + { + auto begin = token; + Statement s; + if (skipped(T.LBrace)) + { + auto ss = new Statements(); + while (token.type != T.RBrace && token.type != T.EOF) + ss ~= parseStatement(); + require(T.RBrace); + s = set(ss, begin); + } + else if (token.type == T.Semicolon) + { + error(token, MSG.ExpectedNonEmptyStatement); + nT(); + s = set(new EmptyStatement(), begin); + } + else + s = parseStatement(); + return s; + } + + /++ + NoScopeOrEmptyStatement: + ; + NoScopeStatement + +/ + Statement parseNoScopeOrEmptyStatement() + { + if (skipped(T.Semicolon)) + return set(new EmptyStatement(), this.prevToken); + else + return parseNoScopeStatement(); + } + + Statement parseAttributeStatement() + { + StorageClass stc, stc_tmp; + LinkageType prev_linkageType; + + // Nested function. + Declaration parse() + { + auto begin = token; + Declaration d; + switch (token.type) + { + case T.Extern: + if (peekNext() != T.LParen) + { + stc_tmp = StorageClass.Extern; + goto Lcommon; + } + + nT(); + auto linkageType = parseLinkageType(); + checkLinkageType(prev_linkageType, linkageType, begin); + + d = new LinkageDeclaration(linkageType, parse()); + break; + case T.Static: + stc_tmp = StorageClass.Static; + goto Lcommon; + case T.Final: + stc_tmp = StorageClass.Final; + goto Lcommon; + case T.Const: + version(D2) + { + if (peekNext() == T.LParen) + goto case_Declaration; + } + stc_tmp = StorageClass.Const; + goto Lcommon; + version(D2) + { + case T.Invariant: // D 2.0 + if (peekNext() == T.LParen) + goto case_Declaration; + stc_tmp = StorageClass.Invariant; + goto Lcommon; + } + case T.Auto: + stc_tmp = StorageClass.Auto; + goto Lcommon; + case T.Scope: + stc_tmp = StorageClass.Scope; + goto Lcommon; + Lcommon: + // Issue error if redundant. + if (stc & stc_tmp) + error(MID.RedundantStorageClass, token.srcText); + else + stc |= stc_tmp; + + auto tok = token.type; + nT(); + d = new StorageClassDeclaration(stc_tmp, tok, parse()); + break; + // TODO: allow "scope class", "abstract scope class" in function bodies? + //case T.Class: + default: + case_Declaration: + return parseVariableOrFunction(stc, Protection.None, prev_linkageType, true); + } + return set(d, begin); + } + return new DeclarationStatement(parse()); + } + + Statement parseIfStatement() + { + assert(token.type == T.If); + nT(); + + Statement variable; + Expression condition; + Statement ifBody, elseBody; + + require(T.LParen); + + Identifier* ident; + auto begin = token; // For start of AutoDeclaration or normal Declaration. + // auto Identifier = Expression + if (skipped(T.Auto)) + { + ident = requireIdentifier(MSG.ExpectedVariableName); + require(T.Assign); + auto init = parseExpression(); + auto v = new VariableDeclaration(null, [ident], [init]); + set(v, begin.nextNWS); + auto d = new StorageClassDeclaration(StorageClass.Auto, T.Auto, v); + set(d, begin); + variable = new DeclarationStatement(d); + set(variable, begin); + } + else + { + // Declarator = Expression + Type parseDeclaratorAssign() + { + auto type = parseDeclarator(ident); + require(T.Assign); + return type; + } + bool success; + auto type = try_(&parseDeclaratorAssign, success); + if (success) + { + auto init = parseExpression(); + auto v = new VariableDeclaration(type, [ident], [init]); + set(v, begin); + variable = new DeclarationStatement(v); + set(variable, begin); + } + else + condition = parseExpression(); + } + require(T.RParen); + ifBody = parseScopeStatement(); + if (skipped(T.Else)) + elseBody = parseScopeStatement(); + return new IfStatement(variable, condition, ifBody, elseBody); + } + + Statement parseWhileStatement() + { + assert(token.type == T.While); + nT(); + require(T.LParen); + auto condition = parseExpression(); + require(T.RParen); + return new WhileStatement(condition, parseScopeStatement()); + } + + Statement parseDoWhileStatement() + { + assert(token.type == T.Do); + nT(); + auto doBody = parseScopeStatement(); + require(T.While); + require(T.LParen); + auto condition = parseExpression(); + require(T.RParen); + return new DoWhileStatement(condition, doBody); + } + + Statement parseForStatement() + { + assert(token.type == T.For); + nT(); + require(T.LParen); + + Statement init, forBody; + Expression condition, increment; + + if (token.type != T.Semicolon) + init = parseNoScopeStatement(); + else + nT(); // Skip ; + if (token.type != T.Semicolon) + condition = parseExpression(); + require(T.Semicolon); + if (token.type != T.RParen) + increment = parseExpression(); + require(T.RParen); + forBody = parseScopeStatement(); + return new ForStatement(init, condition, increment, forBody); + } + + Statement parseForeachStatement() + { + assert(token.type == T.Foreach || token.type == T.Foreach_reverse); + TOK tok = token.type; + nT(); + + auto params = new Parameters; + Expression e; // Aggregate or LwrExpression + + require(T.LParen); + while (1) + { + auto paramBegin = token; + StorageClass stc; + Type type; + Identifier* ident; + + switch (token.type) + { + case T.Ref, T.Inout: + stc = StorageClass.Ref; + nT(); + // fall through + case T.Identifier: + auto next = peekNext(); + if (next == T.Comma || next == T.Semicolon || next == T.RParen) + { + ident = requireIdentifier(MSG.ExpectedVariableName); + break; + } + // fall through + default: + type = parseDeclarator(ident); + } + + params ~= set(new Parameter(stc, type, ident, null), paramBegin); + + if (token.type != T.Comma) + break; + nT(); + } + require(T.Semicolon); + e = parseExpression(); + version(D2) + { //Foreach (ForeachType; LwrExpression .. UprExpression ) ScopeStatement + if (skipped(T.Slice)) + { + // if (params.length != 1) + // error(MID.XYZ); // TODO: issue error msg + auto upper = parseExpression(); + require(T.RParen); + auto forBody = parseScopeStatement(); + return new ForeachRangeStatement(tok, params, e, upper, forBody); + } + } + // Foreach (ForeachTypeList; Aggregate) ScopeStatement + require(T.RParen); + auto forBody = parseScopeStatement(); + return new ForeachStatement(tok, params, e, forBody); + } + + Statement parseSwitchStatement() + { + assert(token.type == T.Switch); + nT(); + require(T.LParen); + auto condition = parseExpression(); + require(T.RParen); + auto switchBody = parseScopeStatement(); + return new SwitchStatement(condition, switchBody); + } + + /++ + Helper function for parsing the body of + a default or case statement. + +/ + Statement parseCaseOrDefaultBody() + { + // This function is similar to parseNoScopeStatement() + auto begin = token; + auto s = new Statements(); + while (token.type != T.Case && + token.type != T.Default && + token.type != T.RBrace && + token.type != T.EOF) + s ~= parseStatement(); + return set(new ScopeStatement(s), begin); + } + + Statement parseCaseStatement() + { + assert(token.type == T.Case); + nT(); + auto values = parseExpressionList(); + require(T.Colon); + auto caseBody = parseCaseOrDefaultBody(); + return new CaseStatement(values, caseBody); + } + + Statement parseDefaultStatement() + { + assert(token.type == T.Default); + nT(); + require(T.Colon); + auto defaultBody = parseCaseOrDefaultBody(); + return new DefaultStatement(defaultBody); + } + + Statement parseContinueStatement() + { + assert(token.type == T.Continue); + nT(); + auto ident = optionalIdentifier(); + require(T.Semicolon); + return new ContinueStatement(ident); + } + + Statement parseBreakStatement() + { + assert(token.type == T.Break); + nT(); + auto ident = optionalIdentifier(); + require(T.Semicolon); + return new BreakStatement(ident); + } + + Statement parseReturnStatement() + { + assert(token.type == T.Return); + nT(); + Expression expr; + if (token.type != T.Semicolon) + expr = parseExpression(); + require(T.Semicolon); + return new ReturnStatement(expr); + } + + Statement parseGotoStatement() + { + assert(token.type == T.Goto); + nT(); + Identifier* ident; + Expression caseExpr; + switch (token.type) + { + case T.Case: + nT(); + if (token.type == T.Semicolon) + break; + caseExpr = parseExpression(); + break; + case T.Default: + nT(); + break; + default: + ident = requireIdentifier(MSG.ExpectedAnIdentifier); + } + require(T.Semicolon); + return new GotoStatement(ident, caseExpr); + } + + Statement parseWithStatement() + { + assert(token.type == T.With); + nT(); + require(T.LParen); + auto expr = parseExpression(); + require(T.RParen); + return new WithStatement(expr, parseScopeStatement()); + } + + Statement parseSynchronizedStatement() + { + assert(token.type == T.Synchronized); + nT(); + Expression expr; + if (skipped(T.LParen)) + { + expr = parseExpression(); + require(T.RParen); + } + return new SynchronizedStatement(expr, parseScopeStatement()); + } + + Statement parseTryStatement() + { + assert(token.type == T.Try); + nT(); + + auto tryBody = parseScopeStatement(); + CatchBody[] catchBodies; + FinallyBody finBody; + + while (skipped(T.Catch)) + { + Parameter param; + if (skipped(T.LParen)) + { + auto begin = token; + Identifier* ident; + auto type = parseDeclarator(ident, true); + param = new Parameter(StorageClass.None, type, ident, null); + set(param, begin); + require(T.RParen); + } + catchBodies ~= new CatchBody(param, parseNoScopeStatement()); + if (param is null) + break; // This is a LastCatch + } + + if (token.type == T.Finally) + { + auto begin = token; + nT(); + finBody = new FinallyBody(parseNoScopeStatement()); + set(finBody, begin); + } + + if (catchBodies.length == 0 && finBody is null) + { + // TODO: issue error msg. + } + + return new TryStatement(tryBody, catchBodies, finBody); + } + + Statement parseThrowStatement() + { + assert(token.type == T.Throw); + nT(); + auto expr = parseExpression(); + require(T.Semicolon); + return new ThrowStatement(expr); + } + + Statement parseScopeGuardStatement() + { + assert(token.type == T.Scope); + nT(); + assert(token.type == T.LParen); + nT(); + auto condition = requireIdentifier(MSG.ExpectedScopeIdentifier); + if (condition) + switch (condition.identID) + { + case ID.exit, ID.success, ID.failure: + break; + default: + // TODO: create MID.InvalidScopeIdentifier + error(this.prevToken, MSG.InvalidScopeIdentifier, this.prevToken.srcText); + } + require(T.RParen); + Statement scopeBody; + if (token.type == T.LBrace) + scopeBody = parseScopeStatement(); + else + scopeBody = parseNoScopeStatement(); + return new ScopeGuardStatement(condition, scopeBody); + } + + Statement parseVolatileStatement() + { + assert(token.type == T.Volatile); + nT(); + Statement volatileBody; + if (token.type == T.Semicolon) + nT(); + else if (token.type == T.LBrace) + volatileBody = parseScopeStatement(); + else + volatileBody = parseStatement(); + return new VolatileStatement(volatileBody); + } + + Statement parsePragmaStatement() + { + assert(token.type == T.Pragma); + nT(); + + Identifier* ident; + Expression[] args; + Statement pragmaBody; + + require(T.LParen); + ident = requireIdentifier(MSG.ExpectedPragmaIdentifier); + + if (skipped(T.Comma)) + args = parseExpressionList(); + require(T.RParen); + + pragmaBody = parseNoScopeOrEmptyStatement(); + + return new PragmaStatement(ident, args, pragmaBody); + } + + Statement parseStaticIfStatement() + { + assert(token.type == T.Static); + nT(); + assert(token.type == T.If); + nT(); + Expression condition; + Statement ifBody, elseBody; + + require(T.LParen); + condition = parseExpression(); + require(T.RParen); + ifBody = parseNoScopeStatement(); + if (skipped(T.Else)) + elseBody = parseNoScopeStatement(); + return new StaticIfStatement(condition, ifBody, elseBody); + } + + Statement parseStaticAssertStatement() + { + assert(token.type == T.Static); + nT(); + assert(token.type == T.Assert); + nT(); + Expression condition, message; + require(T.LParen); + condition = parseAssignExpression(); // Condition. + if (skipped(T.Comma)) + message = parseAssignExpression(); // Error message. + require(T.RParen); + require(T.Semicolon); + return new StaticAssertStatement(condition, message); + } + + Statement parseDebugStatement() + { + assert(token.type == T.Debug); + nT(); // Skip debug keyword. + + Token* cond; + Statement debugBody, elseBody; + + // ( Condition ) + if (skipped(T.LParen)) + { + cond = parseIdentOrInt(); + require(T.RParen); + } + // debug Statement + // debug ( Condition ) Statement + debugBody = parseNoScopeStatement(); + // else Statement + if (skipped(T.Else)) + elseBody = parseNoScopeStatement(); + + return new DebugStatement(cond, debugBody, elseBody); + } + + Statement parseVersionStatement() + { + assert(token.type == T.Version); + nT(); // Skip version keyword. + + Token* cond; + Statement versionBody, elseBody; + + // ( Condition ) + require(T.LParen); + cond = parseIdentOrInt(); + require(T.RParen); + // version ( Condition ) Statement + versionBody = parseNoScopeStatement(); + // else Statement + if (skipped(T.Else)) + elseBody = parseNoScopeStatement(); + + return new VersionStatement(cond, versionBody, elseBody); + } + + /+++++++++++++++++++++++++++++ + + Assembler parsing methods + + +++++++++++++++++++++++++++++/ + + Statement parseAsmStatement() + { + assert(token.type == T.Asm); + nT(); // Skip asm keyword. + require(T.LBrace); + auto ss = new Statements; + while (token.type != T.RBrace && token.type != T.EOF) + ss ~= parseAsmInstruction(); + require(T.RBrace); + return new AsmStatement(ss); + } + + Statement parseAsmInstruction() + { + auto begin = token; + Statement s; + Identifier* ident; + switch (token.type) + { + // Keywords that are valid opcodes. + case T.In, T.Int, T.Out: + ident = token.ident; + nT(); + goto LOpcode; + case T.Identifier: + ident = token.ident; + nT(); // Skip Identifier + if (skipped(T.Colon)) + { + // Identifier : AsmInstruction + s = new LabeledStatement(ident, parseAsmInstruction()); + break; + } + + LOpcode: + // Opcode ; + // Opcode Operands ; + // Opcode + // Identifier + Expression[] es; + if (token.type != T.Semicolon) + { + while (1) + { + es ~= parseAsmExpression(); + if (token.type != T.Comma) + break; + nT(); + } + } + require(T.Semicolon); + s = new AsmInstruction(ident, es); + break; + case T.Align: + // align Integer; + nT(); + int number = -1; + if (token.type == T.Int32) + (number = token.int_), nT(); + else + error(token, MSG.ExpectedIntegerAfterAlign, token.srcText); + require(T.Semicolon); + s = new AsmAlignStatement(number); + break; + case T.Semicolon: + s = new EmptyStatement(); + nT(); + break; + default: + s = new IllegalAsmInstruction(); + // Skip to next valid token. + do + nT(); + while (!token.isAsmInstructionStart && + token.type != T.RBrace && + token.type != T.EOF) + auto text = Token.textSpan(begin, this.prevToken); + error(begin, MSG.IllegalAsmInstructino ~ text); + } + set(s, begin); + return s; + } + + Expression parseAsmExpression() + { + auto begin = token; + auto e = parseAsmOrOrExpression(); + if (skipped(T.Question)) + { + auto tok = this.prevToken; + auto iftrue = parseAsmExpression(); + require(T.Colon); + auto iffalse = parseAsmExpression(); + e = new CondExpression(e, iftrue, iffalse, tok); + set(e, begin); + } + // TODO: create AsmExpression that contains e? + return e; + } + + Expression parseAsmOrOrExpression() + { + alias parseAsmAndAndExpression parseNext; + auto begin = token; + auto e = parseNext(); + while (token.type == T.OrLogical) + { + auto tok = token; + nT(); + e = new OrOrExpression(e, parseNext(), tok); + set(e, begin); + } + return e; + } + + Expression parseAsmAndAndExpression() + { + alias parseAsmOrExpression parseNext; + auto begin = token; + auto e = parseNext(); + while (token.type == T.AndLogical) + { + auto tok = token; + nT(); + e = new AndAndExpression(e, parseNext(), tok); + set(e, begin); + } + return e; + } + + Expression parseAsmOrExpression() + { + alias parseAsmXorExpression parseNext; + auto begin = token; + auto e = parseNext(); + while (token.type == T.OrBinary) + { + auto tok = token; + nT(); + e = new OrExpression(e, parseNext(), tok); + set(e, begin); + } + return e; + } + + Expression parseAsmXorExpression() + { + alias parseAsmAndExpression parseNext; + auto begin = token; + auto e = parseNext(); + while (token.type == T.Xor) + { + auto tok = token; + nT(); + e = new XorExpression(e, parseNext(), tok); + set(e, begin); + } + return e; + } + + Expression parseAsmAndExpression() + { + alias parseAsmCmpExpression parseNext; + auto begin = token; + auto e = parseNext(); + while (token.type == T.AndBinary) + { + auto tok = token; + nT(); + e = new AndExpression(e, parseNext(), tok); + set(e, begin); + } + return e; + } + + Expression parseAsmCmpExpression() + { + alias parseAsmShiftExpression parseNext; + auto begin = token; + auto e = parseNext(); + + auto operator = token; + switch (operator.type) + { + case T.Equal, T.NotEqual: + nT(); + e = new EqualExpression(e, parseNext(), operator); + break; + case T.LessEqual, T.Less, T.GreaterEqual, T.Greater: + nT(); + e = new RelExpression(e, parseNext(), operator); + break; + default: + return e; + } + set(e, begin); + return e; + } + + Expression parseAsmShiftExpression() + { + alias parseAsmAddExpression parseNext; + auto begin = token; + auto e = parseNext(); + while (1) + { + auto operator = token; + switch (operator.type) + { + case T.LShift: nT(); e = new LShiftExpression(e, parseNext(), operator); break; + case T.RShift: nT(); e = new RShiftExpression(e, parseNext(), operator); break; + case T.URShift: nT(); e = new URShiftExpression(e, parseNext(), operator); break; + default: + return e; + } + set(e, begin); + } + assert(0); + } + + Expression parseAsmAddExpression() + { + alias parseAsmMulExpression parseNext; + auto begin = token; + auto e = parseNext(); + while (1) + { + auto operator = token; + switch (operator.type) + { + case T.Plus: nT(); e = new PlusExpression(e, parseNext(), operator); break; + case T.Minus: nT(); e = new MinusExpression(e, parseNext(), operator); break; + // Not allowed in asm + //case T.Tilde: nT(); e = new CatExpression(e, parseNext(), operator); break; + default: + return e; + } + set(e, begin); + } + assert(0); + } + + Expression parseAsmMulExpression() + { + alias parseAsmPostExpression parseNext; + auto begin = token; + auto e = parseNext(); + while (1) + { + auto operator = token; + switch (operator.type) + { + case T.Mul: nT(); e = new MulExpression(e, parseNext(), operator); break; + case T.Div: nT(); e = new DivExpression(e, parseNext(), operator); break; + case T.Mod: nT(); e = new ModExpression(e, parseNext(), operator); break; + default: + return e; + } + set(e, begin); + } + assert(0); + } + + Expression parseAsmPostExpression() + { + auto begin = token; + auto e = parseAsmUnaryExpression(); + while (token.type == T.LBracket) + { + nT(); + e = parseAsmExpression(); + e = new AsmPostBracketExpression(e); + require(T.RBracket); + set(e, begin); + } + return e; + } + + Expression parseAsmUnaryExpression() + { + auto begin = token; + Expression e; + switch (token.type) + { + case T.Byte, T.Short, T.Int, + T.Float, T.Double, T.Real: + goto LAsmTypePrefix; + case T.Identifier: + switch (token.ident.identID) + { + case ID.near, ID.far,/* "byte", "short", "int",*/ + ID.word, ID.dword, ID.qword/*, "float", "double", "real"*/: + LAsmTypePrefix: + nT(); + if (token.type == T.Identifier && token.ident is Ident.ptr) + nT(); + else + error(MID.ExpectedButFound, "ptr", token.srcText); + e = new AsmTypeExpression(parseAsmExpression()); + break; + case ID.offset: + nT(); + e = new AsmOffsetExpression(parseAsmExpression()); + break; + case ID.seg: + nT(); + e = new AsmSegExpression(parseAsmExpression()); + break; + default: + goto LparseAsmPrimaryExpression; + } + break; + case T.Minus: + case T.Plus: + nT(); + e = new SignExpression(parseAsmUnaryExpression()); + break; + case T.Not: + nT(); + e = new NotExpression(parseAsmUnaryExpression()); + break; + case T.Tilde: + nT(); + e = new CompExpression(parseAsmUnaryExpression()); + default: + LparseAsmPrimaryExpression: + e = parseAsmPrimaryExpression(); + return e; + } + set(e, begin); + return e; + } + + Expression parseAsmPrimaryExpression() + { + auto begin = token; + Expression e; + switch (token.type) + { + case T.Int32, T.Int64, T.Uint32, T.Uint64: + e = new IntExpression(token); + nT(); + break; + case T.Float32, T.Float64, T.Float80, + T.Imaginary32, T.Imaginary64, T.Imaginary80: + e = new RealExpression(token); + nT(); + break; + case T.Dollar: + e = new DollarExpression(); + nT(); + break; + case T.LBracket: + // [ AsmExpression ] + nT(); + e = parseAsmExpression(); + require(T.RBracket); + e = new AsmBracketExpression(e); + break; + case T.Identifier: + auto register = token.ident; + switch (register.identID) + { + // __LOCAL_SIZE + case ID.__LOCAL_SIZE: + nT(); + e = new AsmLocalSizeExpression(); + break; + // Register + case ID.ST: + nT(); + // (1) - (7) + int number = -1; + if (skipped(T.LParen)) + { + if (token.type == T.Int32) + (number = token.int_), nT(); + else + expected(T.Int32); + require(T.RParen); + } + e = new AsmRegisterExpression(register, number); + break; + case ID.FS: + nT(); + // TODO: is the colon-number part optional? + int number = -1; + if (skipped(T.Colon)) + { + // :0, :4, :8 + if (token.type == T.Int32) + (number = token.int_), nT(); + if (number != 0 && number != 4 && number != 8) + error(MID.ExpectedButFound, "0, 4 or 8", token.srcText); + } + e = new AsmRegisterExpression(register, number); + break; + case ID.AL, ID.AH, ID.AX, ID.EAX, + ID.BL, ID.BH, ID.BX, ID.EBX, + ID.CL, ID.CH, ID.CX, ID.ECX, + ID.DL, ID.DH, ID.DX, ID.EDX, + ID.BP, ID.EBP, ID.SP, ID.ESP, + ID.DI, ID.EDI, ID.SI, ID.ESI, + ID.ES, ID.CS, ID.SS, ID.DS, ID.GS, + ID.CR0, ID.CR2, ID.CR3, ID.CR4, + ID.DR0, ID.DR1, ID.DR2, ID.DR3, ID.DR6, ID.DR7, + ID.TR3, ID.TR4, ID.TR5, ID.TR6, ID.TR7, + ID.MM0, ID.MM1, ID.MM2, ID.MM3, + ID.MM4, ID.MM5, ID.MM6, ID.MM7, + ID.XMM0, ID.XMM1, ID.XMM2, ID.XMM3, + ID.XMM4, ID.XMM5, ID.XMM6, ID.XMM7: + nT(); + e = new AsmRegisterExpression(register); + break; + default: + // DotIdentifier + Expression[] identList; + while (1) + { + auto begin2 = token; + auto ident = requireIdentifier(MSG.ExpectedAnIdentifier); + e = new IdentifierExpression(ident); + set(e, begin2); + identList ~= e; + if (token.type != T.Dot) + break; + nT(); // Skip dot. + } + e = new DotListExpression(identList); + } + break; + default: + error(MID.ExpectedButFound, "Expression", token.srcText); + e = new EmptyExpression(); + if (!trying) + { + // Insert a dummy token and don't consume current one. + begin = lx.insertEmptyTokenBefore(token); + this.prevToken = begin; + } + } + set(e, begin); + return e; + } + + /+++++++++++++++++++++++++++++ + + Expression parsing methods + + +++++++++++++++++++++++++++++/ + + Expression parseExpression() + { + auto begin = token; + auto e = parseAssignExpression(); + while (token.type == T.Comma) + { + auto comma = token; + nT(); + e = new CommaExpression(e, parseAssignExpression(), comma); + set(e, begin); + } + return e; + } + + Expression parseAssignExpression() + { + auto begin = token; + auto e = parseCondExpression(); + while (1) + { + switch (token.type) + { + case T.Assign: + nT(); e = new AssignExpression(e, parseAssignExpression()); + break; + case T.LShiftAssign: + nT(); e = new LShiftAssignExpression(e, parseAssignExpression()); + break; + case T.RShiftAssign: + nT(); e = new RShiftAssignExpression(e, parseAssignExpression()); + break; + case T.URShiftAssign: + nT(); e = new URShiftAssignExpression(e, parseAssignExpression()); + break; + case T.OrAssign: + nT(); e = new OrAssignExpression(e, parseAssignExpression()); + break; + case T.AndAssign: + nT(); e = new AndAssignExpression(e, parseAssignExpression()); + break; + case T.PlusAssign: + nT(); e = new PlusAssignExpression(e, parseAssignExpression()); + break; + case T.MinusAssign: + nT(); e = new MinusAssignExpression(e, parseAssignExpression()); + break; + case T.DivAssign: + nT(); e = new DivAssignExpression(e, parseAssignExpression()); + break; + case T.MulAssign: + nT(); e = new MulAssignExpression(e, parseAssignExpression()); + break; + case T.ModAssign: + nT(); e = new ModAssignExpression(e, parseAssignExpression()); + break; + case T.XorAssign: + nT(); e = new XorAssignExpression(e, parseAssignExpression()); + break; + case T.CatAssign: + nT(); e = new CatAssignExpression(e, parseAssignExpression()); + break; + default: + return e; + } + set(e, begin); + } + return e; + } + + Expression parseCondExpression() + { + auto begin = token; + auto e = parseOrOrExpression(); + if (token.type == T.Question) + { + auto tok = token; + nT(); + auto iftrue = parseExpression(); + require(T.Colon); + auto iffalse = parseCondExpression(); + e = new CondExpression(e, iftrue, iffalse, tok); + set(e, begin); + } + return e; + } + + Expression parseOrOrExpression() + { + alias parseAndAndExpression parseNext; + auto begin = token; + auto e = parseNext(); + while (token.type == T.OrLogical) + { + auto tok = token; + nT(); + e = new OrOrExpression(e, parseNext(), tok); + set(e, begin); + } + return e; + } + + Expression parseAndAndExpression() + { + alias parseOrExpression parseNext; + auto begin = token; + auto e = parseNext(); + while (token.type == T.AndLogical) + { + auto tok = token; + nT(); + e = new AndAndExpression(e, parseNext(), tok); + set(e, begin); + } + return e; + } + + Expression parseOrExpression() + { + alias parseXorExpression parseNext; + auto begin = token; + auto e = parseNext(); + while (token.type == T.OrBinary) + { + auto tok = token; + nT(); + e = new OrExpression(e, parseNext(), tok); + set(e, begin); + } + return e; + } + + Expression parseXorExpression() + { + alias parseAndExpression parseNext; + auto begin = token; + auto e = parseNext(); + while (token.type == T.Xor) + { + auto tok = token; + nT(); + e = new XorExpression(e, parseNext(), tok); + set(e, begin); + } + return e; + } + + Expression parseAndExpression() + { + alias parseCmpExpression parseNext; + auto begin = token; + auto e = parseNext(); + while (token.type == T.AndBinary) + { + auto tok = token; + nT(); + e = new AndExpression(e, parseNext(), tok); + set(e, begin); + } + return e; + } + + Expression parseCmpExpression() + { + alias parseShiftExpression parseNext; + auto begin = token; + auto e = parseShiftExpression(); + + auto operator = token; + switch (operator.type) + { + case T.Equal, T.NotEqual: + nT(); + e = new EqualExpression(e, parseNext(), operator); + break; + case T.Not: + if (peekNext() != T.Is) + break; + nT(); + // fall through + case T.Is: + nT(); + e = new IdentityExpression(e, parseNext(), operator); + break; + case T.LessEqual, T.Less, T.GreaterEqual, T.Greater, + T.Unordered, T.UorE, T.UorG, T.UorGorE, + T.UorL, T.UorLorE, T.LorEorG, T.LorG: + nT(); + e = new RelExpression(e, parseNext(), operator); + break; + case T.In: + nT(); + e = new InExpression(e, parseNext(), operator); + break; + default: + return e; + } + set(e, begin); + return e; + } + + Expression parseShiftExpression() + { + alias parseAddExpression parseNext; + auto begin = token; + auto e = parseNext(); + while (1) + { + auto operator = token; + switch (operator.type) + { + case T.LShift: nT(); e = new LShiftExpression(e, parseNext(), operator); break; + case T.RShift: nT(); e = new RShiftExpression(e, parseNext(), operator); break; + case T.URShift: nT(); e = new URShiftExpression(e, parseNext(), operator); break; + default: + return e; + } + set(e, begin); + } + assert(0); + } + + Expression parseAddExpression() + { + alias parseMulExpression parseNext; + auto begin = token; + auto e = parseNext(); + while (1) + { + auto operator = token; + switch (operator.type) + { + case T.Plus: nT(); e = new PlusExpression(e, parseNext(), operator); break; + case T.Minus: nT(); e = new MinusExpression(e, parseNext(), operator); break; + case T.Tilde: nT(); e = new CatExpression(e, parseNext(), operator); break; + default: + return e; + } + set(e, begin); + } + assert(0); + } + + Expression parseMulExpression() + { + alias parsePostExpression parseNext; + auto begin = token; + auto e = parseNext(); + while (1) + { + auto operator = token; + switch (operator.type) + { + case T.Mul: nT(); e = new MulExpression(e, parseNext(), operator); break; + case T.Div: nT(); e = new DivExpression(e, parseNext(), operator); break; + case T.Mod: nT(); e = new ModExpression(e, parseNext(), operator); break; + default: + return e; + } + set(e, begin); + } + assert(0); + } + + Expression parsePostExpression() + { + auto begin = token; + auto e = parseUnaryExpression(); + while (1) + { + switch (token.type) + { + case T.Dot: + e = new PostDotListExpression(e, parseDotListExpression()); + goto Lset; + case T.PlusPlus: + e = new PostIncrExpression(e); + break; + case T.MinusMinus: + e = new PostDecrExpression(e); + break; + case T.LParen: + e = new CallExpression(e, parseArguments()); + goto Lset; + case T.LBracket: + // parse Slice- and IndexExpression + nT(); + // [] is a SliceExpression + if (token.type == T.RBracket) + { + e = new SliceExpression(e, null, null); + break; + } + + Expression[] es = [parseAssignExpression()]; + + // [ AssignExpression .. AssignExpression ] + if (skipped(T.Slice)) + { + e = new SliceExpression(e, es[0], parseAssignExpression()); + require(T.RBracket); + goto Lset; + } + + // [ ExpressionList ] + if (skipped(T.Comma)) + es ~= parseExpressionList(); + require(T.RBracket); + + e = new IndexExpression(e, es); + goto Lset; + default: + return e; + } + nT(); + Lset: // Jumped here to skip nT(). + set(e, begin); + } + assert(0); + } + + Expression parseUnaryExpression() + { + auto begin = token; + Expression e; + switch (token.type) + { + case T.AndBinary: + nT(); + e = new AddressExpression(parseUnaryExpression()); + break; + case T.PlusPlus: + nT(); + e = new PreIncrExpression(parseUnaryExpression()); + break; + case T.MinusMinus: + nT(); + e = new PreDecrExpression(parseUnaryExpression()); + break; + case T.Mul: + nT(); + e = new DerefExpression(parseUnaryExpression()); + break; + case T.Minus: + case T.Plus: + nT(); + e = new SignExpression(parseUnaryExpression()); + break; + case T.Not: + nT(); + e = new NotExpression(parseUnaryExpression()); + break; + case T.Tilde: + nT(); + e = new CompExpression(parseUnaryExpression()); + break; + case T.New: + e = parseNewExpression(); + return e; + case T.Delete: + nT(); + e = new DeleteExpression(parseUnaryExpression()); + break; + case T.Cast: + requireNext(T.LParen); + Type type; + switch (token.type) + { + version(D2) + { + auto begin2 = token; + case T.Const: + type = new ConstType(null); + goto case_break; + case T.Invariant: + type = new InvariantType(null); + case_break: + nT(); + set(type, begin2); + break; + } + default: + type = parseType(); + } + require(T.RParen); + e = new CastExpression(parseUnaryExpression(), type); + break; + case T.LParen: + // ( Type ) . Identifier + Type parseType_() + { + nT(); + auto type = parseType(); + require(T.RParen); + require(T.Dot); + return type; + } + bool success; + auto type = try_(&parseType_, success); + if (success) + { + auto ident = requireIdentifier(MSG.ExpectedIdAfterTypeDot); + e = new TypeDotIdExpression(type, ident); + break; + } + goto default; + default: + e = parsePrimaryExpression(); + return e; + } + assert(e !is null); + set(e, begin); + return e; + } + + Expression parsePrimaryExpression() + { + auto begin = token; + Expression e; + switch (token.type) + { + case T.Identifier, T.Dot, T.Typeof: + e = parseDotListExpression(); + break; + case T.This: + nT(); + e = new ThisExpression(); + break; + case T.Super: + nT(); + e = new SuperExpression(); + break; + case T.Null: + nT(); + e = new NullExpression(); + break; + case T.True, T.False: + nT(); + e = new BoolExpression(); + break; + case T.Dollar: + nT(); + e = new DollarExpression(); + break; + case T.Int32, T.Int64, T.Uint32, T.Uint64: + e = new IntExpression(token); + nT(); + break; + case T.Float32, T.Float64, T.Float80, + T.Imaginary32, T.Imaginary64, T.Imaginary80: + e = new RealExpression(token); + nT(); + break; + case T.CharLiteral: + e = new CharExpression(token.dchar_); + nT(); + break; + case T.String: + Token*[] stringLiterals; + do + { + stringLiterals ~= token; + nT(); + } while (token.type == T.String) + e = new StringExpression(stringLiterals); + break; + case T.LBracket: + Expression[] values; + + nT(); + if (!skipped(T.RBracket)) + { + e = parseAssignExpression(); + if (token.type == T.Colon) + goto LparseAssocArray; + if (skipped(T.Comma)) + values = [e] ~ parseExpressionList(); + require(T.RBracket); + } + + e = new ArrayLiteralExpression(values); + break; + + LparseAssocArray: + Expression[] keys; + + keys ~= e; + nT(); // Skip colon. + goto LenterLoop; + + while (1) + { + keys ~= parseAssignExpression(); + require(T.Colon); + LenterLoop: + values ~= parseAssignExpression(); + if (token.type != T.Comma) + break; + nT(); + } + require(T.RBracket); + e = new AArrayLiteralExpression(keys, values); + break; + case T.LBrace: + // DelegateLiteral := { Statements } + auto funcBody = parseFunctionBody(); + e = new FunctionLiteralExpression(funcBody); + break; + case T.Function, T.Delegate: + // FunctionLiteral := (function|delegate) Type? '(' ArgumentList ')' '{' Statements '}' + nT(); // Skip function|delegate token. + Type returnType; + Parameters parameters; + if (token.type != T.LBrace) + { + if (token.type != T.LParen) // Optional return type + returnType = parseType(); + parameters = parseParameterList(); + } + auto funcBody = parseFunctionBody(); + e = new FunctionLiteralExpression(returnType, parameters, funcBody); + break; + case T.Assert: + Expression msg; + requireNext(T.LParen); + e = parseAssignExpression(); + if (skipped(T.Comma)) + msg = parseAssignExpression(); + require(T.RParen); + e = new AssertExpression(e, msg); + break; + case T.Mixin: + requireNext(T.LParen); + e = parseAssignExpression(); + require(T.RParen); + e = new MixinExpression(e); + break; + case T.Import: + requireNext(T.LParen); + e = parseAssignExpression(); + require(T.RParen); + e = new ImportExpression(e); + break; + case T.Typeid: + requireNext(T.LParen); + auto type = parseType(); + require(T.RParen); + e = new TypeidExpression(type); + break; + case T.Is: + requireNext(T.LParen); + + Type type, specType; + Identifier* ident; // optional Identifier + Token* opTok, specTok; + + type = parseDeclarator(ident, true); + + switch (token.type) + { + case T.Colon, T.Equal: + opTok = token; + nT(); + switch (token.type) + { + case T.Typedef, + T.Struct, + T.Union, + T.Class, + T.Interface, + T.Enum, + T.Function, + T.Delegate, + T.Super, + T.Return: + case_Const_Invariant: + specTok = token; + nT(); + break; + case T.Const, T.Invariant: + if (peekNext() != T.LParen) + goto case_Const_Invariant; + // Fall through. It's a type. + default: + specType = parseType(); + } + default: + } + + TemplateParameters tparams; + version(D2) + { + // is ( Type Identifier : TypeSpecialization , TemplateParameterList ) + // is ( Type Identifier == TypeSpecialization , TemplateParameterList ) + if (ident && specType && token.type == T.Comma) + tparams = parseTemplateParameterList2(); + } + require(T.RParen); + e = new IsExpression(type, ident, opTok, specTok, specType, tparams); + break; + case T.LParen: + if (tokenAfterParenIs(T.LBrace)) + { + auto parameters = parseParameterList(); + // ( ParameterList ) FunctionBody + auto funcBody = parseFunctionBody(); + e = new FunctionLiteralExpression(null, parameters, funcBody); + } + else + { + // ( Expression ) + nT(); + e = parseExpression(); + require(T.RParen); + // TODO: create ParenExpression? + } + break; + version(D2) + { + case T.Traits: + nT(); + require(T.LParen); + auto id = requireIdentifier(MSG.ExpectedAnIdentifier); + TemplateArguments args; + if (token.type == T.Comma) + args = parseTemplateArguments2(); + else + require(T.RParen); + e = new TraitsExpression(id, args); + break; + } + default: + if (token.isIntegralType) + { // IntegralType . Identifier + auto type = new IntegralType(token.type); + nT(); + set(type, begin); + require(T.Dot); + auto ident = requireIdentifier(MSG.ExpectedIdAfterTypeDot); + e = new TypeDotIdExpression(type, ident); + } + else if (token.isSpecialToken) + { + e = new SpecialTokenExpression(token); + nT(); + } + else + { + error(MID.ExpectedButFound, "Expression", token.srcText); + e = new EmptyExpression(); + if (!trying) + { + // Insert a dummy token and don't consume current one. + begin = lx.insertEmptyTokenBefore(token); + this.prevToken = begin; + } + } + } + set(e, begin); + return e; + } + + Expression parseNewExpression(/*Expression e*/) + { + auto begin = token; + assert(token.type == T.New); + nT(); // Skip new keyword. + + Expression[] newArguments; + Expression[] ctorArguments; + + if (token.type == T.LParen) + newArguments = parseArguments(); + + // NewAnonClassExpression: + // new (ArgumentList)opt class (ArgumentList)opt SuperClassopt InterfaceClassesopt ClassBody + if (skipped(T.Class)) + { + if (token.type == T.LParen) + ctorArguments = parseArguments(); + + BaseClass[] bases = token.type != T.LBrace ? parseBaseClasses(false) : null ; + + auto decls = parseDeclarationDefinitionsBody(); + return set(new NewAnonClassExpression(/*e, */newArguments, bases, ctorArguments, decls), begin); + } + + // NewExpression: + // NewArguments Type [ AssignExpression ] + // NewArguments Type ( ArgumentList ) + // NewArguments Type + auto type = parseType(); + + if (token.type == T.LParen) + ctorArguments = parseArguments(); + + return set(new NewExpression(/*e, */newArguments, type, ctorArguments), begin); + } + + Type parseType() + { + return parseBasicType2(parseBasicType()); + } + + Type parseBasicType() + { + auto begin = token; + Type t; + + if (token.isIntegralType) + { + t = new IntegralType(token.type); + nT(); + } + else + switch (token.type) + { + case T.Identifier, T.Typeof, T.Dot: + t = parseDotListType(); + assert(!isNodeSet(t)); + break; + version(D2) + { + case T.Const: + // const ( Type ) + nT(); + require(T.LParen); + t = parseType(); + require(T.RParen); + t = new ConstType(t); + break; + case T.Invariant: + // invariant ( Type ) + nT(); + require(T.LParen); + t = parseType(); + require(T.RParen); + t = new InvariantType(t); + break; + } // version(D2) + default: + error(MID.ExpectedButFound, "BasicType", token.srcText); + t = new UndefinedType(); + nT(); + } + return set(t, begin); + } + + Type parseBasicType2(Type t) + { + typeof(token) begin; + while (1) + { + begin = token; + switch (token.type) + { + case T.Mul: + t = new PointerType(t); + nT(); + break; + case T.LBracket: + t = parseArrayType(t); + continue; + case T.Function, T.Delegate: + TOK tok = token.type; + nT(); + auto parameters = parseParameterList(); + if (tok == T.Function) + t = new FunctionType(t, parameters); + else + t = new DelegateType(t, parameters); + break; + default: + return t; + } + set(t, begin); + } + assert(0); + } + + bool tokenAfterParenIs(TOK tok) + { + // We count nested parentheses tokens because template types may appear inside parameter lists; e.g. (int x, Foo!(int) y). + assert(token.type == T.LParen); + Token* next = token; + uint level = 1; + Loop: + while (1) + { + lx.peek(next); + switch (next.type) + { + case T.RParen: + if (--level == 0) + { // Last, closing parentheses found. + do + lx.peek(next); + while (next.isWhitespace) + break Loop; + } + break; + case T.LParen: + ++level; + break; + case T.EOF: + break Loop; + default: + } + } + return next.type == tok; + } + + Type parseDeclaratorSuffix(Type t) + { + switch (token.type) + { + case T.LBracket: + // Type Identifier ArrayType + // ArrayType := [] or [Type] or [Expression..Expression] + do + t = parseArrayType(t); + while (token.type == T.LBracket) + break; +/+ // parsed in parseDeclaration() + case T.LParen: + TemplateParameters tparams; + if (tokenAfterParenIs(T.LParen)) + { + // ( TemplateParameterList ) ( ParameterList ) + tparams = parseTemplateParameterList(); + } + + auto params = parseParameterList(); + // ReturnType FunctionName ( ParameterList ) + t = new FunctionType(t, params, tparams); + break; ++/ + default: + break; + } + return t; + } + + Type parseArrayType(Type t) + { + assert(token.type == T.LBracket); + auto begin = token; + nT(); + if (skipped(T.RBracket)) + t = new ArrayType(t); + else + { + bool success; + Type parseAAType() + { + auto type = parseType(); + require(T.RBracket); + return type; + } + auto assocType = try_(&parseAAType, success); + if (success) + t = new ArrayType(t, assocType); + else + { + Expression e = parseExpression(), e2; + if (skipped(T.Slice)) + e2 = parseExpression(); + t = new ArrayType(t, e, e2); + require(T.RBracket); + } + } + set(t, begin); + return t; + } + + Type parseCFunctionPointerType(Type type, ref Identifier* ident, bool optionalParamList) + { + assert(token.type == T.LParen); + assert(type !is null); + auto begin = token; + nT(); // Skip ( + type = parseBasicType2(type); + if (token.type == T.LParen) + { + // Can be nested. + type = parseCFunctionPointerType(type, ident, true); + } + else if (token.type == T.Identifier) + { + // The identifier of the function pointer and the declaration. + ident = token.ident; + nT(); + type = parseDeclaratorSuffix(type); + } + require(T.RParen); + + Parameters params; + if (optionalParamList) + params = token.type == T.LParen ? parseParameterList() : null; + else + params = parseParameterList(); + + type = new CFuncPointerType(type, params); + return set(type, begin); + } + + Type parseDeclarator(ref Identifier* ident, bool identOptional = false) + { + auto t = parseType(); + + if (token.type == T.LParen) + t = parseCFunctionPointerType(t, ident, true); + else if (token.type == T.Identifier) + { + ident = token.ident; + nT(); + t = parseDeclaratorSuffix(t); + } + + if (ident is null && !identOptional) + error(token, MSG.ExpectedDeclaratorIdentifier, token.srcText); + + return t; + } + + /++ + Parse a list of AssignExpressions. + ExpressionList: + AssignExpression + AssignExpression , ExpressionList + +/ + Expression[] parseExpressionList() + { + Expression[] expressions; + do + expressions ~= parseAssignExpression(); + while(skipped(T.Comma)) + return expressions; + } + + /++ + Arguments: + ( ) + ( ExpressionList ) + +/ + Expression[] parseArguments() + { + assert(token.type == T.LParen); + nT(); + Expression[] args; + if (token.type != TOK.RParen) + args = parseExpressionList(); + require(TOK.RParen); + return args; + } + + Parameters parseParameterList() + out(params) + { + if (params.length > 1) + foreach (param; params.items[0..$-1]) + { + if (param.isVariadic()) + assert(0, "variadic arguments can only appear at the end of the parameter list."); + } + } + body + { + auto begin = token; + require(T.LParen); + + auto params = new Parameters(); + + if (skipped(T.RParen)) + return set(params, begin); + + Loop: + while (1) + { + auto paramBegin = token; + StorageClass stc, tmp; + Type type; + Identifier* ident; + Expression defValue; + + void pushParameter() + { + params ~= set(new Parameter(stc, type, ident, defValue), paramBegin); + } + + if (skipped(T.Ellipses)) + { + stc = StorageClass.Variadic; + pushParameter(); // type, ident and defValue will be null. + break Loop; + } + + Lstc_loop: + switch (token.type) + { + version(D2) + { + case T.Invariant: // D2.0 + if (peekNext() == T.LParen) + goto default; + tmp = StorageClass.Invariant; + goto Lcommon; + case T.Const: // D2.0 + if (peekNext() == T.LParen) + goto default; + tmp = StorageClass.Const; + goto Lcommon; + case T.Final: // D2.0 + tmp = StorageClass.Final; + goto Lcommon; + case T.Scope: // D2.0 + tmp = StorageClass.Scope; + goto Lcommon; + case T.Static: // D2.0 + tmp = StorageClass.Static; + goto Lcommon; + } + case T.In: + tmp = StorageClass.In; + goto Lcommon; + case T.Out: + tmp = StorageClass.Out; + goto Lcommon; + case T.Inout, T.Ref: + tmp = StorageClass.Ref; + goto Lcommon; + case T.Lazy: + tmp = StorageClass.Lazy; + goto Lcommon; + Lcommon: + // Check for redundancy. + if (stc & tmp) + error(MID.RedundantStorageClass, token.srcText); + else + stc |= tmp; + nT(); + version(D2) + goto Lstc_loop; + else + goto default; // In D1.0 only one stc per parameter is allowed. + default: + type = parseDeclarator(ident, true); + + if (skipped(T.Assign)) + defValue = parseAssignExpression(); + + if (skipped(T.Ellipses)) + { + stc |= StorageClass.Variadic; + pushParameter(); + break Loop; + } + + pushParameter(); + + if (token.type != T.Comma) + break Loop; + nT(); + } + } + require(T.RParen); + return set(params, begin); + } + + TemplateArguments parseTemplateArguments() + { + TemplateArguments targs; + require(T.LParen); + if (token.type != T.RParen) + targs = parseTemplateArguments_(); + require(T.RParen); + return targs; + } + +version(D2) +{ + TemplateArguments parseTemplateArguments2() + { + assert(token.type == T.Comma); + nT(); + TemplateArguments targs; + if (token.type != T.RParen) + targs = parseTemplateArguments_(); + else + error(token, MSG.ExpectedTypeOrExpression); + require(T.RParen); + return targs; + } +} // version(D2) + + TemplateArguments parseTemplateArguments_() + { + auto begin = token; + auto targs = new TemplateArguments; + while (1) + { + Type parseType_() + { + auto type = parseType(); + if (token.type == T.Comma || token.type == T.RParen) + return type; + ++errorCount; // Cause try_() to fail. + return null; + } + bool success; + auto typeArgument = try_(&parseType_, success); + if (success) + // TemplateArgument: + // Type + // Symbol + targs ~= typeArgument; + else + // TemplateArgument: + // AssignExpression + targs ~= parseAssignExpression(); + if (token.type != T.Comma) + break; // Exit loop. + nT(); + } + set(targs, begin); + return targs; + } + + TemplateParameters parseTemplateParameterList() + { + TemplateParameters tparams; + require(T.LParen); + if (token.type != T.RParen) + tparams = parseTemplateParameterList_(); + require(T.RParen); + return tparams; + } + +version(D2) +{ + TemplateParameters parseTemplateParameterList2() + { + assert(token.type == T.Comma); + nT(); + TemplateParameters tparams; + if (token.type != T.RParen) + tparams = parseTemplateParameterList_(); + else + error(token, MSG.ExpectedTemplateParameters); + return tparams; + } +} // version(D2) + + TemplateParameters parseTemplateParameterList_() + { + auto begin = token; + auto tparams = new TemplateParameters; + + while (1) + { + auto paramBegin = token; + TemplateParameter tp; + Identifier* ident; + Type specType, defType; + + void parseSpecAndOrDefaultType() + { + // : SpecializationType + if (skipped(T.Colon)) + specType = parseType(); + // = DefaultType + if (skipped(T.Assign)) + defType = parseType(); + } + + switch (token.type) + { + case T.Alias: + // TemplateAliasParameter: + // alias Identifier + nT(); // Skip alias keyword. + ident = requireIdentifier(MSG.ExpectedAliasTemplateParam); + parseSpecAndOrDefaultType(); + tp = new TemplateAliasParameter(ident, specType, defType); + break; + case T.Identifier: + ident = token.ident; + switch (peekNext()) + { + case T.Ellipses: + // TemplateTupleParameter: + // Identifier ... + nT(); // Skip Identifier. + nT(); // Skip Ellipses. + if (token.type == T.Comma) + error(MID.TemplateTupleParameter); + tp = new TemplateTupleParameter(ident); + break; + case T.Comma, T.RParen, T.Colon, T.Assign: + // TemplateTypeParameter: + // Identifier + nT(); // Skip Identifier. + parseSpecAndOrDefaultType(); + tp = new TemplateTypeParameter(ident, specType, defType); + break; + default: + // TemplateValueParameter: + // Declarator + ident = null; + goto LTemplateValueParameter; + } + break; + version(D2) + { + case T.This: + // TemplateThisParameter + // this TemplateTypeParameter + nT(); // Skip 'this' keyword. + ident = requireIdentifier(MSG.ExpectedNameForThisTempParam); + parseSpecAndOrDefaultType(); + tp = new TemplateThisParameter(ident, specType, defType); + break; + } + default: + LTemplateValueParameter: + // TemplateValueParameter: + // Declarator + Expression specValue, defValue; + auto valueType = parseDeclarator(ident); + // : SpecializationValue + if (skipped(T.Colon)) + specValue = parseCondExpression(); + // = DefaultValue + if (skipped(T.Assign)) + defValue = parseCondExpression(); + tp = new TemplateValueParameter(valueType, ident, specValue, defValue); + } + + // Push template parameter. + tparams ~= set(tp, paramBegin); + + if (token.type != T.Comma) + break; + nT(); + } + set(tparams, begin); + return tparams; + } + + void expected(TOK tok) + { + if (token.type != tok) + error(MID.ExpectedButFound, Token.toString(tok), token.srcText); + } + + void require(TOK tok) + { + if (token.type == tok) + nT(); + else + error(MID.ExpectedButFound, Token.toString(tok), token.srcText); + } + + void requireNext(TOK tok) + { + nT(); + require(tok); + } + + Identifier* optionalIdentifier() + { + Identifier* id; + if (token.type == T.Identifier) + (id = token.ident), nT(); + return id; + } + + Identifier* requireIdentifier() + { + Identifier* id; + if (token.type == T.Identifier) + (id = token.ident), nT(); + else + error(MID.ExpectedButFound, "Identifier", token.srcText); + return id; + } + + /++ + Params: + errorMsg = an error that has no message ID yet. + +/ + Identifier* requireIdentifier(char[] errorMsg) + { + Identifier* id; + if (token.type == T.Identifier) + (id = token.ident), nT(); + else + error(token, errorMsg, token.srcText); + return id; + } + + Identifier* requireIdentifier(MID mid) + { + Identifier* id; + if (token.type == T.Identifier) + (id = token.ident), nT(); + else + error(mid, token.srcText); + return id; + } + + Token* requireId() + { + if (token.type == T.Identifier) + { + auto id = token; + nT(); + return id; + } + else + error(MID.ExpectedButFound, "Identifier", token.srcText); + return null; + } + + Token* requireIdToken(char[] errorMsg) + { + Token* idtok; + if (token.type == T.Identifier) + (idtok = token), nT(); + else + error(token, errorMsg, token.srcText); + return idtok; + } + + /// Reports an error that has no message ID yet. + void error(Token* token, char[] formatMsg, ...) + { + error_(token, formatMsg, _arguments, _argptr); + } + + void error(MID mid, ...) + { + error_(this.token, GetMsg(mid), _arguments, _argptr); + } + + void error_(Token* token, char[] formatMsg, TypeInfo[] _arguments, void* _argptr) + { + if (trying) + { + ++errorCount; + return; + } + auto location = token.getLocation(); + auto msg = Format(_arguments, _argptr, formatMsg); + auto error = new ParserError(location, msg); + errors ~= error; + if (infoMan !is null) + infoMan ~= error; + } +}
--- a/trunk/src/main.d Sat Jan 05 17:01:15 2008 +0100 +++ b/trunk/src/main.d Sat Jan 05 17:29:47 2008 +0100 @@ -4,7 +4,7 @@ +/ module main; -import dil.Parser; +import dil.parser.Parser; import dil.lexer.Lexer; import dil.Token; import dil.Messages; @@ -286,22 +286,17 @@ void parse(string fileName) { - auto sourceText = loadFile(fileName); - auto parser = new Parser(sourceText, fileName); - auto root = parser.start(); + auto mod = new Module(fileName); + mod.parse(); -void print(Node[] decls, char[] indent) -{ - foreach(decl; decls) + void print(Node[] decls, char[] indent) { - assert(decl !is null); - Stdout.formatln("{}{}: begin={} end={}", indent, decl.classinfo.name, decl.begin ? decl.begin.srcText : "\33[31mnull\33[0m", decl.end ? decl.end.srcText : "\33[31mnull\33[0m"); - print(decl.children, indent ~ " "); + foreach(decl; decls) + { + assert(decl !is null); + Stdout.formatln("{}{}: begin={} end={}", indent, decl.classinfo.name, decl.begin ? decl.begin.srcText : "\33[31mnull\33[0m", decl.end ? decl.end.srcText : "\33[31mnull\33[0m"); + print(decl.children, indent ~ " "); + } } + print(mod.root.children, ""); } -print(root.children, ""); -foreach (error; parser.errors) -{ - Stdout.format(`{0}({1})P: {2}`, error.filePath, error.loc, error.getMsg); -} -}