| package parser | 
 |  | 
 | import ( | 
 | 	"fmt" | 
 | 	"math" | 
 | 	"mojom/mojom_tool/lexer" | 
 | 	"mojom/mojom_tool/mojom" | 
 | 	"strconv" | 
 | ) | 
 |  | 
 | // The code in this file implements  recursive-descent, predictive parsing | 
 | // for the context-free grammar listed below. | 
 | // | 
 | // The grammar is similar to the grammar in the document "Mojom Language | 
 | // Specification", but it has been modified in order to make it LL(1). This | 
 | // is necessary in order to be able to use it to do predictive top-down | 
 | // parsing. (See Section 4.4.3 of "Compilers: Principles, Techniques and Tools" | 
 | // 2nd Edition, by Aho et al."). This requirement explains the slight awkwardness | 
 | // seen below regarding the handling of Mojom attributes. (Disclaimer: There are | 
 | // a few productions that technically render the grammar LL(n) for some small | 
 | // integer n rather than strictly LL(1). But this does not affect the parsing | 
 | // in any significant way.) | 
 | // | 
 | // Our recursive descent logic is implemented in the methods below with names of | 
 | // the form "parseX()" where "X" is (an altered spelling of) one of our | 
 | // non-terminal symbols. Each of the productions below has been copied to the | 
 | // piece of code responsible for implementing the logic associated with the | 
 | // production. | 
 | // | 
 | // Key: | 
 | // Upper case means non-terminals. | 
 | // Lower case means terminals and refers to the TokenKind enum in lexer/tokens.go. | 
 | // Vertical bar | means alternatives. | 
 | // Braces {} means zero or more. | 
 | // Brackets [] means zero or one. | 
 | // | 
 | // ATTR_MOJOM_FILE      -> [ATTRIBUTES] MOJOM_FILE | 
 | // MOJOM_FILE           -> MODULE_DECL {IMPORT_STMNT} {ATTR_MOJOM_DECL} | 
 | // MOJOM_FILE           -> IMPORT_STMNT {IMPORT_STMNT} {ATTR_MOJOM_DECL} | 
 | // MOJOM_FILE           -> MOJOM_DECL {ATTR_MOJOM_DECL} | 
 |  | 
 | // MODULE_DECL          -> module IDENTIFIER semi | 
 | // IMPORT_STMNT         -> import string_literal | 
 |  | 
 | // ATTR_MOJOM_DECL      -> [ATTRIBUTES] MOJOM_DECL | 
 | // MOJOM_DECL           -> INTRFC_DECL | STRUCT_DECL | UNION_DECL | ENUM_DECL | CONSTANT_DECL | 
 |  | 
 | // ATTRIBUTES           -> lbracket ATTR_ASSIGNMENT { comma, ATTR_ASSIGNMENT} | 
 | // ATTR_ASSIGNMENT      -> name equals name | name equals LITERAL_VALUE | 
 |  | 
 | // INTRFC_DECL          -> interface name lbrace INTRFC_BODY rbrace semi | 
 | // INTRFC_BODY          -> {ATTR_INTRFC_ELEMENT} | 
 | // ATTR_INTRFC_ELEMENT  -> [ATTRIBUTES] INTRFC_ELEMENT | 
 | // INTRFC_ELEMENT       -> METHOD_DECL | ENUM_DECL | CONSTANT_DECL | 
 |  | 
 | // METHOD_DECL          -> name [ordinal] lparen [PARAM_LIST] rparen [response lparen [PARAM_LIST] rparen] semi | 
 | // PARAM_LIST           -> PARAM_DECL {comma, PARAM_DECL} | 
 | // PARAM_DECL           -> [ATTRIBUTES] TYPE NAME [ordinal] | 
 |  | 
 | // STRUCT_DECL          -> struct name lbrace STRUCT_BODY rbrace semi | 
 | // STRUCT_BODY          -> {ATTR_STRUCT_ELEMENT} | 
 | // ATTR_STRUCT_ELEMENT  -> [ATTRIBUTES] STRUCT_ELEMENT | 
 | // STRUCT_ELEMENT       -> STRUCT_FIELD | ENUM_DECL | CONSTANT_DECL | 
 | // STRUCT_FIELD         -> TYPE name [ordinal] [equals DEFAULT_VALUE] semi | 
 | // DEFAULT_VALUE        -> COMPATIBLE_VALUE_REF | default | 
 | // COMPATIBLE_VALUE_REF -> VALUE_REF {{that resolves to a concrete value whose type is assignment compatible with the type of the assignee}} | 
 |  | 
 | // UNION_DECL           -> union name lbrace UNION_BODY rbrace semi | 
 | // UNION_BODY           -> {UNION_FIELD_DECL} | 
 | // UNION_FIELD_DECL     -> [ATTRIBUTES] TYPE name [ordinal] semi | 
 |  | 
 | // ENUM_DECL            -> enum name lbrace ENUM_BODY rbrace semi | 
 | // ENUM_BODY            -> [ ENUN_VALUE {, ENUM_VALUE} [,] ] | 
 | // ENUM_VALUE           -> [ATTRIBUTES] name [equals ENUM_VAL_INITIALIZER] | 
 | // ENUM_VAL_INITIALIZER -> INT32_VAL | APPROPRIATE_ENUM_VALUE_REF | 
 | // INT32_VAL            -> VALUE_REF {{that resolves to a concrete value of integer type that may be assigned to an int32.}} | 
 | // APPROPRIATE_ENUM_VALUE_REF | 
 | //                      -> USER_VALUE_REF {{that resolves to an enum value of the same enum type as the initializee | 
 | //                                          and which occurs earlier in declaration order than the initializee.}} | 
 |  | 
 | // CONSTANT_DECL        -> const CONST_OK_TYPE name equals COMPATIBLE_VALUE_REF semi | 
 | // CONST_OK_TYPE        -> SIMPLE_TYPE | string | ENUM_TYPE {{See https://github.com/domokit/mojo/issues/607}} | 
 |  | 
 | // VALUE_REF            -> USER_VALUE_REF | LITERAL_VALUE | BUILT_IN_FLOAT_CONST | 
 | // USER_VALUE_REF       -> IDENTIFIER {{that resolves to a user-defined constant or enum value}} | 
 | // BUILT_IN_FLOAT_CONST -> IDENTIFIER {{that does not resolve to a user-defined constant or enum value | 
 | //                         and that is equal to one of the following strings: | 
 | //                         "float.INFINITY", "float.NEGATIVE_INFINITY", "float.NAN", | 
 | //                         "double.INFINITY", "double.NEGATIVE_INFINITY","double.NAN"} | 
 | // LITERAL_VALUE        -> BOOL_LITERAL | string_literal | NUMBER_LITERAL | 
 | // BOOL_LITERAL         -> true | false | 
 | // NUMBER_LITERAL       -> [plus | minus] POS_NUM_LITERAL | 
 | // INTEGER_LITERAL      -> [plus | minus] POS_INT_LITERAL | 
 | // POS_NUM_LITERAL      -> POS_INT_LITERAL | POS_FLOAT_LITERAL | 
 | // POS_INT_LITERAL      -> int_const_dec | int_const_hex | 
 | // POS_FLOAT_LITERAL    -> float_const | 
 |  | 
 | // TYPE                 -> BUILT_IN_TYPE | ARRAY_TYPE | MAP_TYPE | USER_TYPE_REF | 
 | // BUILT_IN_TYPE        -> SIMPLE_TYPE | STRING_TYPE | HANDLE_TYPE | 
 | // SIMPLE_TYPE          -> bool | FLOAT_TYPE | INTEGER_TYPE | 
 | // FLOAT_TYPE           -> float | double | 
 | // HANDLE_TYPE          -> handle langle [HANDLE_KIND] rangle [qstn] | 
 | // HANDLE_KIND          -> message_pipe | data_pipe_consumer | data_pipe_producer | shared_buffer | 
 | // INTEGER_TYPE         -> int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | 
 | // STRING_TYPE          -> string [qstn] | 
 | // ARRAY_TYPE           -> array langle TYPE [comma int_const_dec] rangle [qstn] | 
 | // MAP_TYPE             -> map langle MAP_KEY_TYPE comma TYPE rangle [qstn] | 
 | // MAP_KEY_TYPE         -> SIMPLE_TYPE | string | ENUM_TYPE | 
 | // USER_TYPE_REF        -> INTERFACE_TYPE | STRUCT_TYPE | UNION_TYPE | ENUM_TYPE | 
 | // INTERFACE_TYPE       -> IDENTIFIER [amp] [qstn] {{where IDENTIFIER resolves to an interface}} | 
 | // STRUCT_TYPE          -> IDENTIFIER [qstn] {{where IDENTIFIER resolves to a struct}} | 
 | // UNION_TYPE           -> IDENTIFIER [qstn] {{where IDENTIFIER resolves to a union}} | 
 | // ENUM_TYPE            -> IDENTIFIER  {{where IDENTIFIER resolves to an enum}} | 
 |  | 
 | // IDENTIFIER           -> name {dot name} | 
 |  | 
 | //////////////////////////////////////////////////////////////////////////// | 
 | // parseX() methods follow. | 
 | //////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | // Note about the input and output of the parseX() methods: The method | 
 | // Parser.OK() indicates whether or not there has been a parsing error. | 
 | // All of the methods start with the check: | 
 | //	if !p.OK() | 
 | //		return | 
 | //	} | 
 | // and sometimes perform this check midway through their logic too. To make | 
 | // this more concise some of the methods return the value of Parser.OK() so that | 
 | // a caller can check the value without an additional 'if' clause. If a parseX() | 
 | // method has no other value it needs to return it always returns the value of | 
 | // Parser.OK(). | 
 | // | 
 | // Many of the methods construct a Mojom object and return it. For example | 
 | // parseInterfaceDecl() returns a MojomInterface. If the method is responsible | 
 | // for parsing a block containing arbitrarily many elements then instead the | 
 | // method is passed a container that it fills up. For example | 
 | // parseInterfaceBody() is passed a MojomInterface that it fills up and it | 
 | // returns the bool Parser.OK(). If a Mojom object can take optional attributes | 
 | // then the attributes are parsed first via parseAttributes() and then the | 
 | // attributes are passed into the method that parses the object. For example | 
 | // parseInterfaceDecl() takes the parameter attributes *mojom.Attributes. | 
 | // | 
 | // Some of the method names start with the word "try": for example tryParseArrayType(). | 
 | // The word "try" in the name is a hint that the corresponding structure to | 
 | // be parsed may or may not occur next and there should be no error if | 
 | // it turns out the production does not match. For example tryParseArrayType() | 
 | // returns a TypeRef that will be non-nil just in case an ARRAY_TYPE was | 
 | // successfully parsed. But if it turns out that the token "array" does not occur | 
 | // next then it is not an error and tryParseArrayType() simply returns nil without | 
 | // setting an error condition. | 
 | // | 
 | // The helper methods that read in the terminals of the grammar are given | 
 | // names of the form readFoo() instead of parseFoo(). For example there is a | 
 | // method readName() whose job is to read a name. This method is not called | 
 | // parseName() because name is a terminal of our grammar. | 
 |  | 
 | // ATTR_MOJOM_FILE  -> [ATTRIBUTES] MOJOM_FILE | 
 | // MOJOM_FILE       -> MODULE_DECL {IMPORT_STMNT} {ATTR_MOJOM_DECL} | 
 | // MOJOM_FILE       -> IMPORT_STMNT {IMPORT_STMNT} {ATTR_MOJOM_DECL} | 
 | // MOJOM_FILE       -> MOJOM_DECL {ATTR_MOJOM_DECL} | 
 | // | 
 | // Returns Parser.OK() | 
 | func (p *Parser) parseMojomFile() bool { | 
 | 	p.pushRootNode("MojomFile") | 
 | 	defer p.popNode() | 
 |  | 
 | 	initialAttributes := p.parseAttributes() | 
 |  | 
 | 	moduleNamespace := p.parseModuleDecl() | 
 |  | 
 | 	if !p.OK() { | 
 | 		return false | 
 | 	} | 
 |  | 
 | 	if moduleNamespace.Identifier != "" { | 
 | 		p.mojomFile.Attributes = initialAttributes | 
 | 		initialAttributes = nil | 
 | 	} | 
 |  | 
 | 	// Set up the root scope | 
 | 	p.pushScope(p.mojomFile.InitializeFileScope(moduleNamespace)) | 
 | 	defer p.popScope() | 
 |  | 
 | 	if p.checkEOF() { | 
 | 		if initialAttributes != nil && moduleNamespace.Identifier == "" { | 
 | 			message := "The .mojom file contains an attributes section but nothing else." | 
 | 			p.parseError(ParserErrorCodeBadAttributeLocation, message) | 
 | 			return false | 
 | 		} | 
 | 		// Accept an empty .mojom file. | 
 | 		return true | 
 | 	} | 
 |  | 
 | 	importedFiles := p.parseImportStatements() | 
 | 	if !p.OK() { | 
 | 		return false | 
 | 	} | 
 | 	for _, importedFile := range importedFiles { | 
 | 		p.mojomFile.AddImport(importedFile) | 
 | 	} | 
 |  | 
 | 	if moduleNamespace.Identifier == "" && len(importedFiles) > 0 && initialAttributes != nil { | 
 | 		message := "Attributes are not allowed before an import statement." | 
 | 		p.parseError(ParserErrorCodeBadAttributeLocation, message) | 
 | 		return false | 
 | 	} | 
 |  | 
 | 	attributes := p.parseAttributes() | 
 | 	if !p.OK() { | 
 | 		return false | 
 | 	} | 
 | 	if initialAttributes != nil { | 
 | 		if attributes != nil { | 
 | 			// This is impossible because parseModuleDecl() would have complained | 
 | 			// about not expecting to see a "[". | 
 | 			panic("Internal logic error.") | 
 | 		} else { | 
 | 			attributes = initialAttributes | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if p.metaDataOnlyMode { | 
 | 		// In meta-data-only mode we do not parse any of the mojom declarations. | 
 | 		p.discardRemaining = true | 
 | 		return true | 
 | 	} | 
 |  | 
 | 	// ATTR_MOJOM_DECL  -> [ATTRIBUTES] MOJOM_DECL | 
 | 	// MOJOM_DECL       -> INTRFC_DECL | STRUCT_DECL | UNION_DECL | ENUM_DECL | CONSTANT_DECL | 
 | 	for ; ; attributes = p.parseAttributes() { | 
 | 		if !p.OK() { | 
 | 			return false | 
 | 		} | 
 | 		if p.checkEOF() { | 
 | 			if attributes != nil { | 
 | 				message := "File ends with extraneouss attributes." | 
 | 				p.parseError(ParserErrorCodeBadAttributeLocation, message) | 
 | 			} | 
 | 			return false | 
 | 		} | 
 | 		nextToken := p.peekNextToken("") | 
 | 		var duplicateNameError error = nil | 
 | 		switch nextToken.Kind { | 
 | 		case lexer.Interface: | 
 | 			if mojomInterface := p.parseInterfaceDecl(attributes); mojomInterface != nil { | 
 | 				duplicateNameError = p.mojomFile.AddInterface(mojomInterface) | 
 | 			} | 
 | 		case lexer.Struct: | 
 | 			if mojomStruct := p.parseStructDecl(attributes); mojomStruct != nil { | 
 | 				duplicateNameError = p.mojomFile.AddStruct(mojomStruct) | 
 | 			} | 
 | 		case lexer.Union: | 
 | 			if mojomUnion := p.parseUnionDecl(attributes); mojomUnion != nil { | 
 | 				duplicateNameError = p.mojomFile.AddUnion(mojomUnion) | 
 | 			} | 
 | 		case lexer.Enum: | 
 | 			if mojomEnum := p.parseEnumDecl(attributes); mojomEnum != nil { | 
 | 				duplicateNameError = p.mojomFile.AddEnum(mojomEnum) | 
 | 			} | 
 | 		case lexer.Const: | 
 | 			if constant := p.parseConstDecl(attributes); constant != nil { | 
 | 				duplicateNameError = p.mojomFile.AddConstant(constant) | 
 | 			} | 
 | 		default: | 
 | 			p.unexpectedTokenError(nextToken, "interface, struct, union, enum or const") | 
 | 			return false | 
 | 		} | 
 |  | 
 | 		if p.OK() && duplicateNameError != nil { | 
 | 			p.err = duplicateNameError | 
 | 			return false | 
 | 		} | 
 | 	} | 
 | 	return p.OK() | 
 | } | 
 |  | 
 | // ATTRIBUTES      -> lbracket ATTR_ASSIGNMENT { comma, ATTR_ASSIGNMENT} | 
 | // ATTR_ASSIGNMENT -> name equals name | name equals literal | 
 | func (p *Parser) parseAttributes() (attributes *mojom.Attributes) { | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	if !p.tryMatch(lexer.LBracket) { | 
 | 		// There is no attributes section here | 
 | 		return | 
 | 	} | 
 |  | 
 | 	p.pushChildNode("attributes") | 
 | 	defer p.popNode() | 
 |  | 
 | 	attributes = mojom.NewAttributes(p.lastConsumed) | 
 |  | 
 | 	nextToken := p.lastConsumed | 
 | 	for nextToken.Kind != lexer.RBracket { | 
 | 		key := p.readName() | 
 | 		keyToken := p.lastConsumed | 
 | 		p.attachToken() | 
 | 		if !p.OK() { | 
 | 			return | 
 | 		} | 
 | 		if !p.match(lexer.Equals) { | 
 | 			return | 
 | 		} | 
 |  | 
 | 		var value mojom.LiteralValue | 
 | 		if p.peekNextToken("Expecting to find an attribute value.").Kind == lexer.Name { | 
 | 			text := p.readName() | 
 | 			// TODO(rudominer) Decide if we support attribute values as Names. | 
 | 			// Currently we convert a name into a string literal here. | 
 | 			//      See https://github.com/domokit/mojo/issues/561 | 
 | 			valueToken := p.lastConsumed | 
 | 			value = mojom.MakeStringLiteralValue(text, &valueToken) | 
 | 		} else { | 
 | 			value = p.parseLiteral() | 
 | 		} | 
 | 		p.attachToken() | 
 |  | 
 | 		if !p.OK() { | 
 | 			return | 
 | 		} | 
 | 		attributes.List = append(attributes.List, mojom.NewMojomAttribute(key, &keyToken, value)) | 
 |  | 
 | 		nextToken = p.peekNextToken("I was reading an attributes section.") | 
 | 		if !p.OK() { | 
 | 			return | 
 | 		} | 
 | 		p.consumeNextToken() | 
 | 		if nextToken.Kind != lexer.RBracket && nextToken.Kind != lexer.Comma { | 
 | 			switch nextToken.Kind { | 
 | 			case lexer.Module, lexer.Interface, lexer.Struct, lexer.Union, lexer.Enum: | 
 | 				message := fmt.Sprintf("The attribute section is missing a closing ] before %s.", nextToken) | 
 | 				p.parseErrorT(ParserErrorCodeUnexpectedToken, message, nextToken) | 
 | 				return | 
 | 			default: | 
 | 				p.unexpectedTokenError(nextToken, "comma or ]") | 
 | 				return | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return | 
 | } | 
 |  | 
 | //MODULE_DECL -> module identifier semi | 
 | // | 
 | // parseModuleDecl returns a pointer to a ModuleNamespace. If there is not a | 
 | // module declaration, its Identifier is an empty string. Check p.OK() for errors. | 
 | func (p *Parser) parseModuleDecl() (moduleNamespace *mojom.ModuleNamespace) { | 
 | 	moduleNamespace = mojom.NewModuleNamespace("", nil) | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 | 	if p.checkEOF() { | 
 | 		return | 
 | 	} | 
 | 	nextToken := p.peekNextToken("No Mojom declarations found.") | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 | 	switch nextToken.Kind { | 
 | 	case lexer.Module: | 
 | 		p.consumeNextToken() // Consume the MODULE token. | 
 | 		break | 
 | 	case lexer.Import, lexer.Interface, lexer.Struct, lexer.Union, lexer.Enum, lexer.Const: | 
 | 		return // There is no module declaration. | 
 | 	default: | 
 | 		p.unexpectedTokenError(nextToken, "module, import, interface, struct, union, enum or constant") | 
 | 		return | 
 | 	} | 
 |  | 
 | 	p.pushChildNode("moduleDecl") | 
 | 	defer p.popNode() | 
 |  | 
 | 	moduleIdentifier, identifierToken := p.parseIdentifier() | 
 | 	moduleNamespace.Identifier = moduleIdentifier | 
 | 	moduleNamespace.Token = &identifierToken | 
 | 	p.matchSemicolon() | 
 | 	return | 
 | } | 
 |  | 
 | // IMPORT_STMNT  -> import string_literal | 
 | // | 
 | // Returns a non-nil, but possibly empty, slice of ImportedFiles. | 
 | func (p *Parser) parseImportStatements() (imports []*mojom.ImportedFile) { | 
 | 	imports = make([]*mojom.ImportedFile, 0) | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	nextToken := p.peekNextToken("") | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 | 	for nextToken.Kind == lexer.Import { | 
 | 		p.pushChildNode("importStmnt") | 
 | 		defer p.popNode() | 
 |  | 
 | 		p.consumeNextToken() // consume the IMPORT token. | 
 |  | 
 | 		fileName := p.readStringLiteral() | 
 | 		fileNameToken := p.lastConsumed | 
 | 		p.attachToken() | 
 | 		if !p.OK() { | 
 | 			return | 
 | 		} | 
 |  | 
 | 		if !p.matchSemicolon() { | 
 | 			return | 
 | 		} | 
 |  | 
 | 		imports = append(imports, mojom.NewImportedFile(fileName, &fileNameToken)) | 
 |  | 
 | 		if p.checkEOF() { | 
 | 			// Allow a .mojom file containing only import statements. | 
 | 			return | 
 | 		} | 
 | 		nextToken = p.peekNextToken("") | 
 | 		if !p.OK() { | 
 | 			return | 
 | 		} | 
 | 	} | 
 |  | 
 | 	switch nextToken.Kind { | 
 | 	case lexer.Module: | 
 | 		message := "The module declaration must come before the import statements." | 
 | 		p.parseError(ParserErrorCodeUnexpectedToken, message) | 
 | 		return | 
 | 	case lexer.Interface, lexer.Struct, lexer.Union, lexer.Enum, lexer.Const, lexer.LBracket: | 
 | 		return | 
 | 	default: | 
 | 		p.unexpectedTokenError(nextToken, "import, attributes, interface, struct, union, enum or constant") | 
 | 		return | 
 | 	} | 
 | } | 
 |  | 
 | // INTRFC_DECL  -> interface name lbrace INTRFC_BODY rbrace semi | 
 | func (p *Parser) parseInterfaceDecl(attributes *mojom.Attributes) (mojomInterface *mojom.MojomInterface) { | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 | 	p.pushChildNode("interfaceDecl") | 
 | 	defer p.popNode() | 
 |  | 
 | 	if !p.match(lexer.Interface) { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	simpleName := p.readName() | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 | 	nameToken := p.lastConsumed | 
 | 	mojomInterface = mojom.NewMojomInterface(p.DeclData(simpleName, nameToken, attributes)) | 
 |  | 
 | 	if !p.match(lexer.LBrace) { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	if !p.parseInterfaceBody(mojomInterface) { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	if !p.match(lexer.RBrace) { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	closingBraceToken := p.lastConsumed | 
 | 	mojomInterface.SetClosingBraceToken(&closingBraceToken) | 
 |  | 
 | 	p.matchSemicolon() | 
 |  | 
 | 	return | 
 | } | 
 |  | 
 | // INTRFC_BODY          -> {ATTR_INTRFC_ELEMENT} | 
 | // ATTR_INTRFC_ELEMENT  -> [ATTRIBUTES] INTRFC_ELEMENT | 
 | // INTRFC_ELEMENT       -> METHOD_DECL | ENUM_DECL | CONSTANT_DECL | 
 | func (p *Parser) parseInterfaceBody(mojomInterface *mojom.MojomInterface) bool { | 
 | 	if !p.OK() { | 
 | 		return p.OK() | 
 | 	} | 
 | 	p.pushChildNode("interfaceBody") | 
 | 	defer p.popNode() | 
 |  | 
 | 	// The interface body forms a new scope. | 
 | 	p.pushScope(mojomInterface.InitAsScope(p.currentScope)) | 
 | 	defer p.popScope() | 
 |  | 
 | 	rbraceFound := false | 
 | 	for attributes := p.parseAttributes(); !rbraceFound; attributes = p.parseAttributes() { | 
 | 		if !p.OK() { | 
 | 			return false | 
 | 		} | 
 | 		nextToken := p.peekNextToken("I was parsing an interface body.") | 
 | 		var duplicateNameError error = nil | 
 | 		switch nextToken.Kind { | 
 | 		case lexer.Name: | 
 | 			if method := p.parseMethodDecl(attributes); p.OK() { | 
 | 				duplicateNameError = mojomInterface.AddMethod(method) | 
 | 				break | 
 | 			} | 
 | 			return false | 
 | 		case lexer.Enum: | 
 | 			if mojomEnum := p.parseEnumDecl(attributes); mojomEnum != nil { | 
 | 				duplicateNameError = mojomInterface.AddEnum(mojomEnum) | 
 | 			} | 
 | 		case lexer.Const: | 
 | 			if constant := p.parseConstDecl(attributes); constant != nil { | 
 | 				duplicateNameError = mojomInterface.AddConstant(constant) | 
 | 			} | 
 | 		case lexer.RBrace: | 
 | 			rbraceFound = true | 
 | 			if attributes != nil { | 
 | 				message := "Interface body ends with extraneouss attributes." | 
 | 				p.parseError(ParserErrorCodeBadAttributeLocation, message) | 
 | 			} | 
 | 			break | 
 | 		default: | 
 | 			p.unexpectedTokenError(nextToken, "union, enum, const or an identifier") | 
 | 			return false | 
 | 		} | 
 | 		if p.OK() && duplicateNameError != nil { | 
 | 			p.err = duplicateNameError | 
 | 			return false | 
 | 		} | 
 | 	} | 
 | 	if p.OK() { | 
 | 		if err := mojomInterface.ComputeMethodOrdinals(); err != nil { | 
 | 			p.err = err | 
 | 			return false | 
 | 		} | 
 | 	} | 
 | 	return p.OK() | 
 | } | 
 |  | 
 | // METHOD_DECL -> name [ordinal] lparen [PARAM_LIST] rparen [response lparen [PARAM_LIST] rparen]semi | 
 | func (p *Parser) parseMethodDecl(attributes *mojom.Attributes) *mojom.MojomMethod { | 
 | 	if !p.OK() { | 
 | 		return nil | 
 | 	} | 
 | 	p.pushChildNode("methodDecl") | 
 | 	defer p.popNode() | 
 |  | 
 | 	methodName := p.readName() | 
 | 	nameToken := p.lastConsumed | 
 | 	if !p.OK() { | 
 | 		return nil | 
 | 	} | 
 |  | 
 | 	ordinalValue, err := p.readOrdinal() | 
 | 	if err != nil { | 
 | 		p.invalidOrdinalError("method", methodName, nameToken, err) | 
 | 		return nil | 
 | 	} | 
 |  | 
 | 	if !p.match(lexer.LParen) { | 
 | 		return nil | 
 | 	} | 
 |  | 
 | 	params := p.parseParamList(methodName, true) | 
 | 	if !p.OK() { | 
 | 		return nil | 
 | 	} | 
 |  | 
 | 	if !p.match(lexer.RParen) { | 
 | 		return nil | 
 | 	} | 
 | 	parenBeforeSemicolon := p.lastConsumed | 
 |  | 
 | 	// Check for a response message | 
 | 	var responseParams *mojom.MojomStruct = nil | 
 | 	if p.tryMatch(lexer.Response) { | 
 | 		if !p.match(lexer.LParen) { | 
 | 			return nil | 
 | 		} | 
 |  | 
 | 		responseParams = p.parseParamList(methodName, false) | 
 | 		if !p.OK() { | 
 | 			return nil | 
 | 		} | 
 |  | 
 | 		if !p.match(lexer.RParen) { | 
 | 			return nil | 
 | 		} | 
 | 		parenBeforeSemicolon = p.lastConsumed | 
 | 	} | 
 |  | 
 | 	if !p.matchSemicolonToken(parenBeforeSemicolon) { | 
 | 		return nil | 
 | 	} | 
 |  | 
 | 	declData := p.DeclDataWithOrdinal(methodName, nameToken, attributes, ordinalValue) | 
 | 	mojomMethod := mojom.NewMojomMethod(declData, params, responseParams) | 
 | 	return mojomMethod | 
 | } | 
 |  | 
 | // PARAM_LIST -> PARAM_DECL {, PARAM_DECL} | 
 | // PARAM_DECL -> [ATTRIBUTES] TYPE name [ordinal] | 
 | // | 
 | // Returns a MojomStruct containing the list of parameters. This may | 
 | // be nil in case of an early error. Check Parser.OK(). | 
 | func (p *Parser) parseParamList(methodName string, isRequest bool) (paramStruct *mojom.MojomStruct) { | 
 | 	if !p.OK() { | 
 | 		return nil | 
 | 	} | 
 | 	p.pushChildNode("paramList") | 
 | 	defer p.popNode() | 
 |  | 
 | 	if isRequest { | 
 | 		paramStruct = mojom.NewSyntheticRequestStruct(methodName, lexer.Token{}, p.mojomFile) | 
 | 	} else { | 
 | 		paramStruct = mojom.NewSyntheticResponseStruct(methodName, lexer.Token{}, p.mojomFile) | 
 | 	} | 
 | 	nextToken := p.peekNextToken("I was parsing method parameters.") | 
 | 	for nextToken.Kind != lexer.RParen { | 
 |  | 
 | 		attributes := p.parseAttributes() | 
 | 		fieldType := p.parseType() | 
 | 		if !p.OK() { | 
 | 			return | 
 | 		} | 
 | 		name := p.readName() | 
 | 		nameToken := p.lastConsumed | 
 |  | 
 | 		if !p.OK() { | 
 | 			return | 
 | 		} | 
 |  | 
 | 		ordinalValue, err := p.readOrdinal() | 
 | 		if err != nil { | 
 | 			p.invalidOrdinalError("parameter", name, nameToken, err) | 
 | 			return | 
 | 		} | 
 |  | 
 | 		declData := p.DeclDataWithOrdinal(name, nameToken, attributes, ordinalValue) | 
 | 		if duplicateNameError := paramStruct.AddField(mojom.NewStructField( | 
 | 			declData, fieldType, nil)); duplicateNameError != nil { | 
 | 			p.err = duplicateNameError | 
 | 			return nil | 
 | 		} | 
 |  | 
 | 		nextToken = p.peekNextToken("I was parsing method parameters.") | 
 | 		switch nextToken.Kind { | 
 | 		case lexer.Comma: | 
 | 			p.consumeNextToken() | 
 | 			continue | 
 | 		case lexer.RParen: | 
 | 			continue | 
 | 		default: | 
 | 			p.unexpectedTokenError(nextToken, "comma or )") | 
 | 			return nil | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if p.OK() { | 
 | 		if err := paramStruct.ComputeFieldOrdinals(); err != nil { | 
 | 			p.err = err | 
 | 			return nil | 
 | 		} | 
 | 	} | 
 | 	return | 
 | } | 
 |  | 
 | // STRUCT_DECL   -> struct name lbrace STRUCT_BODY rbrace semi | 
 | func (p *Parser) parseStructDecl(attributes *mojom.Attributes) (mojomStruct *mojom.MojomStruct) { | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 | 	p.pushChildNode("structDecl") | 
 | 	defer p.popNode() | 
 |  | 
 | 	if !p.match(lexer.Struct) { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	simpleName := p.readName() | 
 | 	p.attachToken() | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 | 	nameToken := p.lastConsumed | 
 | 	mojomStruct = mojom.NewMojomStruct(p.DeclData(simpleName, nameToken, attributes)) | 
 |  | 
 | 	if !p.match(lexer.LBrace) { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	if !p.parseStructBody(mojomStruct) { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	if !p.match(lexer.RBrace) { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	closingBraceToken := p.lastConsumed | 
 | 	mojomStruct.SetClosingBraceToken(&closingBraceToken) | 
 |  | 
 | 	p.matchSemicolon() | 
 |  | 
 | 	return | 
 | } | 
 |  | 
 | // STRUCT_BODY          -> {ATTR_STRUCT_ELEMENT} | 
 | // ATTR_STRUCT_ELEMENT  -> [ATTRIBUTES] STRUCT_ELEMENT | 
 | // STRUCT_ELEMENT       -> STRUCT_FIELD | ENUM_DECL | CONSTANT_DECL | 
 | func (p *Parser) parseStructBody(mojomStruct *mojom.MojomStruct) bool { | 
 | 	if !p.OK() { | 
 | 		return p.OK() | 
 | 	} | 
 | 	p.pushChildNode("structBody") | 
 | 	defer p.popNode() | 
 |  | 
 | 	// The struct body forms a new scope. | 
 | 	p.pushScope(mojomStruct.InitAsScope(p.currentScope)) | 
 | 	defer p.popScope() | 
 |  | 
 | 	rbraceFound := false | 
 | 	for attributes := p.parseAttributes(); !rbraceFound; attributes = p.parseAttributes() { | 
 | 		if !p.OK() { | 
 | 			return false | 
 | 		} | 
 | 		nextToken := p.peekNextToken("I was parsing a struct body.") | 
 | 		var duplicateNameError error = nil | 
 | 		switch nextToken.Kind { | 
 | 		case lexer.Name: | 
 | 			if field := p.parseStructField(attributes); field != nil { | 
 | 				duplicateNameError = mojomStruct.AddField(field) | 
 | 			} | 
 | 		case lexer.Enum: | 
 | 			if mojomEnum := p.parseEnumDecl(attributes); mojomEnum != nil { | 
 | 				duplicateNameError = mojomStruct.AddEnum(mojomEnum) | 
 | 			} | 
 | 		case lexer.Const: | 
 | 			if constant := p.parseConstDecl(attributes); constant != nil { | 
 | 				duplicateNameError = mojomStruct.AddConstant(constant) | 
 | 			} | 
 | 		case lexer.RBrace: | 
 | 			rbraceFound = true | 
 | 			if attributes != nil { | 
 | 				message := "Struct body ends with extraneouss attributes." | 
 | 				p.parseError(ParserErrorCodeBadAttributeLocation, message) | 
 | 			} | 
 | 			break | 
 | 		default: | 
 | 			p.unexpectedTokenError(nextToken, "field, enum or constant declaration") | 
 | 			return false | 
 | 		} | 
 | 		if p.OK() && duplicateNameError != nil { | 
 | 			p.err = duplicateNameError | 
 | 			return false | 
 | 		} | 
 | 	} | 
 | 	if p.OK() { | 
 | 		if err := mojomStruct.ComputeFieldOrdinals(); err != nil { | 
 | 			p.err = err | 
 | 			return false | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return p.OK() | 
 | } | 
 |  | 
 | // STRUCT_FIELD         -> TYPE name [ordinal] [equals DEFAULT_VALUE] semi | 
 | // DEFAULT_VALUE        -> COMPATIBLE_VALUE_REF | default | 
 | func (p *Parser) parseStructField(attributes *mojom.Attributes) *mojom.StructField { | 
 | 	if !p.OK() { | 
 | 		return nil | 
 | 	} | 
 | 	p.pushChildNode("structField") | 
 | 	defer p.popNode() | 
 |  | 
 | 	fieldType := p.parseType() | 
 | 	fieldName := p.readName() | 
 | 	nameToken := p.lastConsumed | 
 | 	p.attachToken() | 
 |  | 
 | 	ordinalValue, err := p.readOrdinal() | 
 | 	if err != nil { | 
 | 		p.invalidOrdinalError("field", fieldName, nameToken, err) | 
 | 		return nil | 
 | 	} | 
 |  | 
 | 	var defaultValue mojom.ValueRef | 
 | 	var defaultValueToken lexer.Token | 
 | 	defaultKeywordUsed := false | 
 | 	if p.tryMatch(lexer.Equals) { | 
 | 		defaultValueToken = p.peekNextToken("Expecting a default value.") | 
 | 		if defaultValueToken.Kind == lexer.Default { | 
 | 			p.consumeNextToken() | 
 | 			defaultToken := p.lastConsumed | 
 | 			defaultValue = mojom.MakeDefaultLiteral(&defaultToken) | 
 | 			defaultKeywordUsed = true | 
 | 		} else { | 
 | 			assigneeSpec := mojom.AssigneeSpec{ | 
 | 				fieldName, | 
 | 				fieldType, | 
 | 			} | 
 | 			defaultValue = p.parseValue(assigneeSpec) | 
 | 		} | 
 | 	} | 
 | 	if !p.matchSemicolon() { | 
 | 		return nil | 
 | 	} | 
 |  | 
 | 	declData := p.DeclDataWithOrdinal(fieldName, nameToken, attributes, ordinalValue) | 
 | 	field := mojom.NewStructField(declData, fieldType, defaultValue) | 
 | 	if !field.ValidateDefaultValue() { | 
 | 		var message string | 
 | 		if defaultKeywordUsed { | 
 | 			message = fmt.Sprintf("Illegal assignment: The 'default' keyword may not be used with the field %s of type %s.", | 
 | 				fieldName, fieldType.TypeName()) | 
 | 		} else { | 
 | 			valueString := fmt.Sprintf("%v", defaultValue) | 
 | 			valueTypeString := "" | 
 | 			concreteValue := defaultValue.ResolvedConcreteValue() | 
 | 			if concreteValue != nil { | 
 | 				valueString = fmt.Sprintf("%v", concreteValue) | 
 | 				valueTypeString = fmt.Sprintf(" of type %s", concreteValue.ValueType()) | 
 | 			} | 
 | 			message = fmt.Sprintf("Illegal assignment: Field %s of type %s may not be assigned the value %s%s.", | 
 | 				fieldName, fieldType.TypeName(), valueString, valueTypeString) | 
 | 		} | 
 | 		p.parseErrorT(ParserErrorCodeNotAssignmentCompatible, message, defaultValueToken) | 
 | 		return nil | 
 | 	} | 
 |  | 
 | 	return field | 
 | } | 
 |  | 
 | // UNION_DECL    -> union name lbrace UNION_BODY rbrace semi | 
 | func (p *Parser) parseUnionDecl(attributes *mojom.Attributes) (union *mojom.MojomUnion) { | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 | 	p.pushChildNode("unionDecl") | 
 | 	defer p.popNode() | 
 |  | 
 | 	if !p.match(lexer.Union) { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	simpleName := p.readName() | 
 | 	p.attachToken() | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 | 	nameToken := p.lastConsumed | 
 | 	union = mojom.NewMojomUnion(p.DeclData(simpleName, nameToken, attributes)) | 
 |  | 
 | 	if !p.match(lexer.LBrace) { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	if !p.parseUnionBody(union) { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	if !p.match(lexer.RBrace) { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	closingBraceToken := p.lastConsumed | 
 | 	union.SetClosingBraceToken(&closingBraceToken) | 
 |  | 
 | 	if p.matchSemicolon() { | 
 | 		if err := union.ComputeFieldTags(); err != nil { | 
 | 			p.err = err | 
 | 			return | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return | 
 | } | 
 |  | 
 | // UNION_BODY         -> {UNION_FIELD_DECL} | 
 | // UNION_FIELD_DECL   -> [ATTRIBUTES] TYPE name [ordinal] semi | 
 | func (p *Parser) parseUnionBody(union *mojom.MojomUnion) bool { | 
 | 	if !p.OK() { | 
 | 		return p.OK() | 
 | 	} | 
 | 	p.pushChildNode("unionBody") | 
 | 	defer p.popNode() | 
 |  | 
 | 	rbraceFound := false | 
 | 	for attributes := p.parseAttributes(); !rbraceFound; attributes = p.parseAttributes() { | 
 | 		if !p.OK() { | 
 | 			return false | 
 | 		} | 
 | 		nextToken := p.peekNextToken("I was parsing a union body.") | 
 | 		switch nextToken.Kind { | 
 | 		case lexer.Name: | 
 | 			fieldType := p.parseType() | 
 | 			fieldName := p.readName() | 
 | 			nameToken := p.lastConsumed | 
 |  | 
 | 			tag, err := p.readOrdinal() | 
 | 			if err != nil { | 
 | 				p.invalidOrdinalError("union field", fieldName, nameToken, err) | 
 | 				return false | 
 | 			} | 
 |  | 
 | 			if !p.matchSemicolon() { | 
 | 				return false | 
 | 			} | 
 | 			if duplicateNameError := union.AddField(p.DeclDataWithOrdinal( | 
 | 				fieldName, nameToken, attributes, tag), fieldType); duplicateNameError != nil { | 
 | 				p.err = duplicateNameError | 
 | 				return false | 
 |  | 
 | 			} | 
 | 		case lexer.RBrace: | 
 | 			rbraceFound = true | 
 | 			if attributes != nil { | 
 | 				message := "Enum body ends with extraneouss attributes." | 
 | 				p.parseError(ParserErrorCodeBadAttributeLocation, message) | 
 | 			} | 
 | 			break | 
 | 		default: | 
 | 			p.unexpectedTokenError(nextToken, "either another union field or }") | 
 | 			return false | 
 | 		} | 
 | 	} | 
 | 	return p.OK() | 
 | } | 
 |  | 
 | // ENUM_DECL -> enum name lbrace ENUM_BODY rbrace semi | 
 | func (p *Parser) parseEnumDecl(attributes *mojom.Attributes) (enum *mojom.MojomEnum) { | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 | 	p.pushChildNode("enumDecl") | 
 | 	defer p.popNode() | 
 |  | 
 | 	if !p.match(lexer.Enum) { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	simpleName := p.readName() | 
 | 	p.attachToken() | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 | 	nameToken := p.lastConsumed | 
 | 	enum = mojom.NewMojomEnum(p.DeclData(simpleName, nameToken, attributes)) | 
 |  | 
 | 	if !p.match(lexer.LBrace) { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	if !p.parseEnumBody(enum) { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	if !p.match(lexer.RBrace) { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	closingBraceToken := p.lastConsumed | 
 | 	enum.SetClosingBraceToken(&closingBraceToken) | 
 |  | 
 | 	p.matchSemicolon() | 
 |  | 
 | 	return | 
 | } | 
 |  | 
 | // ENUM_BODY     -> [ ENUN_VALUE {, ENUM_VALUE} [,] ] | 
 | // ENUM_VALUE    -> [ATTRIBUTES] name [equals ENUM_VAL_INITIALIZER] | 
 | func (p *Parser) parseEnumBody(mojomEnum *mojom.MojomEnum) bool { | 
 | 	if !p.OK() { | 
 | 		return p.OK() | 
 | 	} | 
 | 	p.pushChildNode("enumBody") | 
 | 	defer p.popNode() | 
 |  | 
 | 	// The enum body forms a new scope in which it's enun values are defined. | 
 | 	p.pushScope(mojomEnum.InitAsScope(p.currentScope)) | 
 | 	defer p.popScope() | 
 |  | 
 | 	rbraceFound := false | 
 | 	trailingCommaFound := false | 
 | 	firstValue := true | 
 | 	for attributes := p.parseAttributes(); !rbraceFound; attributes = p.parseAttributes() { | 
 | 		if !p.OK() { | 
 | 			return false | 
 | 		} | 
 | 		nextToken := p.peekNextToken("I was parsing an enum body.") | 
 | 		var duplicateNameError error = nil | 
 | 		switch nextToken.Kind { | 
 | 		case lexer.Name: | 
 | 			if !firstValue && !trailingCommaFound { | 
 | 				message := fmt.Sprintf("Expecting a comma after %s before the next value %s.", | 
 | 					p.lastConsumed, nextToken) | 
 | 				p.parseErrorT(ParserErrorCodeUnexpectedToken, message, p.lastConsumed) | 
 | 				return false | 
 | 			} | 
 | 			firstValue = false | 
 | 			name := p.readName() | 
 | 			p.attachToken() | 
 | 			nameToken := p.lastConsumed | 
 | 			var valueRef mojom.ValueRef | 
 | 			if p.tryMatch(lexer.Equals) { | 
 | 				valueRef = p.parseEnumValueInitializer(mojomEnum, name) | 
 | 			} | 
 | 			declData := p.DeclData(name, nameToken, attributes) | 
 | 			duplicateNameError = mojomEnum.AddEnumValue(declData, valueRef) | 
 | 			trailingCommaFound = p.tryMatch(lexer.Comma) | 
 | 		case lexer.RBrace: | 
 | 			rbraceFound = true | 
 | 			if attributes != nil { | 
 | 				message := "Enum body ends with extraneouss attributes." | 
 | 				p.parseError(ParserErrorCodeBadAttributeLocation, message) | 
 | 			} | 
 | 			break | 
 | 		case lexer.Comma: | 
 | 			break | 
 | 		default: | 
 | 			p.unexpectedTokenError(nextToken, "either another enum value or }") | 
 | 			return false | 
 | 		} | 
 | 		if p.OK() && duplicateNameError != nil { | 
 | 			p.err = duplicateNameError | 
 | 			return false | 
 | 		} | 
 | 	} | 
 | 	return p.OK() | 
 | } | 
 |  | 
 | // ENUM_VAL_INITIALIZER -> INT32_VAL | APPROPRIATE_ENUM_VALUE_REF | 
 | func (p *Parser) parseEnumValueInitializer(mojoEnum *mojom.MojomEnum, valueName string) mojom.ValueRef { | 
 | 	if !p.OK() { | 
 | 		return nil | 
 | 	} | 
 | 	p.pushChildNode("enumValueInitializer") | 
 | 	defer p.popNode() | 
 |  | 
 | 	// We need to manufacture an instance of Type to act as the "assigneeType" | 
 | 	// for the new ValueSpec we are creating. This is because unlike | 
 | 	// other types of value assignment, an enum value initializer is not | 
 | 	// preceded by a type reference for the assignee. Rather the type of | 
 | 	// the assignee is implicit in the scope. | 
 | 	enumType := mojom.NewResolvedUserTypeRef(mojoEnum.FullyQualifiedName(), mojoEnum) | 
 |  | 
 | 	valueToken := p.peekNextToken("Parsing an enum value initializer type.") | 
 | 	valueRef := p.parseValue(mojom.AssigneeSpec{valueName, enumType}) | 
 | 	if valueRef == nil { | 
 | 		return nil | 
 | 	} | 
 | 	if !valueRef.MarkUsedAsEnumValueInitializer() { | 
 | 		message := fmt.Sprintf("Illegal value: %s. An enum value initializer must be a signed 32-bit integer value.", | 
 | 			valueToken) | 
 | 		p.parseErrorT(ParserErrorCodeUnexpectedToken, message, valueToken) | 
 | 		return nil | 
 | 	} | 
 |  | 
 | 	return valueRef | 
 | } | 
 |  | 
 | // CONSTANT_DECL -> const CONST_OK_TYPE name equals COMPATIBLE_VALUE_REF semi | 
 | // CONST_OK_TYPE -> SIMPLE_TYPE | string | ENUM_TYPE | 
 | func (p *Parser) parseConstDecl(attributes *mojom.Attributes) (constant *mojom.UserDefinedConstant) { | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	p.pushChildNode("constDecl") | 
 | 	defer p.popNode() | 
 |  | 
 | 	p.match(lexer.Const) | 
 | 	declaredTypeToken := p.peekNextToken("Parsing a type.") | 
 | 	declaredType := p.parseType() | 
 | 	name := p.readName() | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 | 	nameToken := p.lastConsumed | 
 | 	p.match(lexer.Equals) | 
 | 	valueToken := p.peekNextToken("Parsing a value.") | 
 | 	assigneeSpec := mojom.AssigneeSpec{ | 
 | 		name, | 
 | 		declaredType, | 
 | 	} | 
 | 	value := p.parseValue(assigneeSpec) | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	if !declaredType.MarkUsedAsConstantType() { | 
 | 		message := fmt.Sprintf("The type %s is not allowed as the type of a declared constant. "+ | 
 | 			"Only simple types, strings and enum types may be the types of constants.", declaredType) | 
 | 		p.parseErrorT(ParserErrorCodeUnexpectedToken, message, declaredTypeToken) | 
 | 		return | 
 | 	} | 
 |  | 
 | 	constant = mojom.NewUserDefinedConstant(p.DeclData(name, nameToken, attributes), declaredType, value) | 
 | 	p.matchSemicolon() | 
 |  | 
 | 	if !constant.ValidateValue() { | 
 | 		valueString := fmt.Sprintf("%v", value) | 
 | 		valueTypeString := "" | 
 | 		concreteValue := value.ResolvedConcreteValue() | 
 | 		if concreteValue != nil { | 
 | 			valueString = fmt.Sprintf("%v", concreteValue) | 
 | 			valueTypeString = fmt.Sprintf(" of type %s", concreteValue.ValueType()) | 
 | 		} | 
 | 		message := fmt.Sprintf("Illegal assignment: Constant %s of type %s may not be assigned the value %s%s.", | 
 | 			name, declaredType.TypeName(), valueString, valueTypeString) | 
 | 		p.parseErrorT(ParserErrorCodeNotAssignmentCompatible, message, valueToken) | 
 | 		return | 
 | 	} | 
 |  | 
 | 	return | 
 | } | 
 |  | 
 | // VALUE_REF -> USER_VALUE_REF | LITERAL_VALUE | BUILT_IN_FLOAT_CONST | 
 | func (p *Parser) parseValue(assigneeSpec mojom.AssigneeSpec) mojom.ValueRef { | 
 | 	if !p.OK() { | 
 | 		return nil | 
 | 	} | 
 | 	p.pushChildNode("value") | 
 | 	defer p.popNode() | 
 |  | 
 | 	nextToken := p.peekNextToken("I was parsing a value.") | 
 | 	p.attachToken() | 
 | 	if nextToken.Kind == lexer.Name { | 
 | 		// Note that we do not distinguish between a USER_VALUE_REF and a | 
 | 		// BUILT_IN_FLOAT_CONST at parsing time. At this stage we consider both | 
 | 		// to be a UserValueRef. Only at resolution time do we decide if it | 
 | 		// turns out to be a BUILT_IN_FLOAT_CONST. | 
 | 		return p.parseUserValueRef(assigneeSpec) | 
 | 	} | 
 | 	literalValue := p.parseLiteral() | 
 | 	p.attachToken() | 
 | 	if !p.OK() || literalValue.ValueType() == nil { | 
 | 		return nil | 
 | 	} | 
 | 	return literalValue | 
 | } | 
 |  | 
 | //LITERAL_VALUE  -> BOOL_LITERAL | string_literal | NUMBER_LITERAL | 
 | // BOOL_LITERAL  -> true | false | 
 | func (p *Parser) parseLiteral() mojom.LiteralValue { | 
 | 	if !p.OK() { | 
 | 		return mojom.LiteralValue{} | 
 | 	} | 
 | 	p.pushChildNode("literal") | 
 | 	defer p.popNode() | 
 |  | 
 | 	nextToken := p.peekNextToken("I was parsing a literal.") | 
 | 	switch nextToken.Kind { | 
 | 	case lexer.StringLiteral: | 
 | 		literal := p.readStringLiteral() | 
 | 		valueToken := p.lastConsumed | 
 | 		return mojom.MakeStringLiteralValue(literal, &valueToken) | 
 | 	case lexer.True: | 
 | 		p.consumeNextToken() | 
 | 		valueToken := p.lastConsumed | 
 | 		return mojom.MakeBoolLiteralValue(true, &valueToken) | 
 | 	case lexer.False: | 
 | 		p.consumeNextToken() | 
 | 		valueToken := p.lastConsumed | 
 | 		return mojom.MakeBoolLiteralValue(false, &valueToken) | 
 | 	case lexer.Plus, lexer.Minus, lexer.FloatConst, lexer.IntConstDec, lexer.IntConstHex: | 
 | 		return p.parseNumberLiteral() | 
 |  | 
 | 	default: | 
 | 		p.unexpectedTokenError(nextToken, "a string, numeric or boolean literal") | 
 | 		return mojom.LiteralValue{} | 
 | 	} | 
 | } | 
 |  | 
 | // NUMBER_LITERAL       -> [plus | minus] POS_NUM_LITERAL | 
 | // POS_NUM_LITERAL      -> POS_INT_LITERAL | POS_FLOAT_LITERAL | 
 | func (p *Parser) parseNumberLiteral() mojom.LiteralValue { | 
 | 	if !p.OK() { | 
 | 		return mojom.LiteralValue{} | 
 | 	} | 
 | 	p.pushChildNode("numberLiteral") | 
 | 	defer p.popNode() | 
 |  | 
 | 	initialMinus := p.tryMatch(lexer.Minus) | 
 | 	initialPlus := p.tryMatch(lexer.Plus) | 
 | 	if initialMinus && initialPlus { | 
 | 		p.unexpectedTokenError(p.lastConsumed, "a number") | 
 | 		return mojom.LiteralValue{} | 
 | 	} | 
 |  | 
 | 	nextToken := p.peekNextToken("I was parsing a numberliteral.") | 
 | 	switch nextToken.Kind { | 
 | 	case lexer.IntConstDec, lexer.IntConstHex: | 
 | 		absoluteValue, numBits, signed, ok := p.parsePositiveIntegerLiteral(initialMinus, true, !initialMinus, 64) | 
 | 		valueToken := p.lastConsumed | 
 | 		if !ok { | 
 | 			return mojom.LiteralValue{} | 
 | 		} | 
 | 		var value int64 | 
 | 		if signed { | 
 | 			value = int64(absoluteValue) | 
 | 			if initialMinus { | 
 | 				value = -1 * value | 
 | 			} | 
 | 		} | 
 | 		switch numBits { | 
 | 		case 8: | 
 | 			if signed { | 
 | 				return mojom.MakeInt8LiteralValue(int8(value), &valueToken) | 
 | 			} else { | 
 | 				return mojom.MakeUint8LiteralValue(uint8(absoluteValue), &valueToken) | 
 | 			} | 
 | 		case 16: | 
 | 			if signed { | 
 | 				return mojom.MakeInt16LiteralValue(int16(value), &valueToken) | 
 | 			} else { | 
 | 				return mojom.MakeUint16LiteralValue(uint16(absoluteValue), &valueToken) | 
 | 			} | 
 | 		case 32: | 
 | 			if signed { | 
 | 				return mojom.MakeInt32LiteralValue(int32(value), &valueToken) | 
 | 			} else { | 
 | 				return mojom.MakeUint32LiteralValue(uint32(absoluteValue), &valueToken) | 
 | 			} | 
 | 		case 64: | 
 | 			if signed { | 
 | 				return mojom.MakeInt64LiteralValue(value, &valueToken) | 
 | 			} else { | 
 | 				return mojom.MakeUint64LiteralValue(absoluteValue, &valueToken) | 
 | 			} | 
 | 		default: | 
 | 			panic(fmt.Sprintf("Unexpected value for numBits: %d", numBits)) | 
 | 		} | 
 | 	case lexer.FloatConst: | 
 | 		value, _ := p.parsePositiveFloatLiteral(initialMinus) | 
 | 		valueToken := p.lastConsumed | 
 | 		return mojom.MakeDoubleLiteralValue(value, &valueToken) | 
 |  | 
 | 	default: | 
 | 		p.unexpectedTokenError(nextToken, "a number") | 
 | 		return mojom.LiteralValue{} | 
 | 	} | 
 | } | 
 |  | 
 | // IDENTIFIER   -> name {dot name} | 
 | func (p *Parser) parseIdentifier() (identifier string, firstToken lexer.Token) { | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 | 	p.pushChildNode("identifier") | 
 | 	defer p.popNode() | 
 |  | 
 | 	firstToken = p.peekNextToken("Expecting an identifier.") | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 | 	if firstToken.Kind != lexer.Name { | 
 | 		p.unexpectedTokenError(firstToken, "an identifier") | 
 | 		return | 
 | 	} | 
 |  | 
 | 	for p.tryMatch(lexer.Name) { | 
 | 		identifier += p.lastConsumed.Text | 
 | 		if !p.tryMatch(lexer.Dot) { | 
 | 			return | 
 | 		} | 
 | 		identifier += "." | 
 | 	} | 
 | 	message := fmt.Sprintf("Invalid identifier: %q. Identifiers may not end with a dot.", identifier) | 
 | 	p.parseErrorT(ParserErrorCodeUnexpectedToken, message, firstToken) | 
 | 	return | 
 | } | 
 |  | 
 | // TYPE -> BUILT_IN_TYPE | ARRAY_TYPE | MAP_TYPE | USER_TYPE_REF | 
 | func (p *Parser) parseType() mojom.TypeRef { | 
 | 	if !p.OK() { | 
 | 		return nil | 
 | 	} | 
 | 	p.pushChildNode("type") | 
 | 	defer p.popNode() | 
 |  | 
 | 	mojomType := p.tryParseBuiltInType() | 
 | 	if mojomType == nil { | 
 | 		mojomType = p.tryParseArrayType() | 
 | 	} | 
 | 	if mojomType == nil { | 
 | 		mojomType = p.tryParseMapType() | 
 | 	} | 
 | 	if mojomType == nil { | 
 | 		mojomType = p.parseTypeReference() | 
 | 	} | 
 |  | 
 | 	return mojomType | 
 | } | 
 |  | 
 | ///////////////// Methods for parsing types and values //////// | 
 |  | 
 | // BUILT_IN_TYPE        -> SIMPLE_TYPE | STRING_TYPE | HANDLE_TYPE | 
 | // SIMPLE_TYPE          -> bool | FLOAT_TYPE | INTEGER_TYPE | 
 | // FLOAT_TYPE           -> float | double | 
 | // HANDLE_TYPE          -> handle langle [HANDLE_KIND] rangle [qstn] | 
 | // HANDLE_KIND          -> message_pipe | data_pipe_consumer | data_pipe_producer | shared_buffer | 
 | // INTEGER_TYPE         -> int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | 
 | // STRING_TYPE          -> string [qstn] | 
 | func (p *Parser) tryParseBuiltInType() mojom.TypeRef { | 
 | 	if !p.OK() { | 
 | 		return nil | 
 | 	} | 
 |  | 
 | 	typeNameToken := p.peekNextToken("I was reading a type.") | 
 | 	if typeNameToken.Kind != lexer.Name { | 
 | 		return nil | 
 | 	} | 
 | 	typeName := typeNameToken.Text | 
 | 	builtInType := mojom.BuiltInType(typeName) | 
 | 	if builtInType == nil { | 
 | 		return nil | 
 | 	} | 
 | 	p.consumeNextToken() | 
 |  | 
 | 	// handle<*> types | 
 | 	if typeName == "handle" { | 
 | 		if p.tryMatch(lexer.LAngle) { | 
 | 			handleType := p.readText(lexer.Name) | 
 | 			if !p.OK() { | 
 | 				token := p.lastSeen | 
 | 				p.unexpectedTokenError(token, "a type of handle") | 
 | 				return nil | 
 | 			} | 
 | 			if p.match(lexer.RAngle) { | 
 | 				typeName = fmt.Sprintf("%s<%s>", typeName, handleType) | 
 | 				if builtInType = mojom.BuiltInType(typeName); builtInType == nil { | 
 | 					message := fmt.Sprintf("Unrecognized type of handle: %s.", typeName) | 
 | 					p.parseErrorT(ParserErrorCodeUnexpectedToken, message, typeNameToken) | 
 | 					return nil | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 		if !p.OK() { | 
 | 			return nil | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// Check for nullable marker | 
 | 	if p.tryMatch(lexer.Qstn) { | 
 | 		if builtInType = mojom.BuiltInType(typeName + "?"); builtInType == nil { | 
 | 			message := fmt.Sprintf("The type %s? is invalid because the type %s may not be made nullable.", | 
 | 				typeName, typeName) | 
 | 			p.parseErrorT(ParserErrorCodeUnexpectedToken, message, typeNameToken) | 
 | 			return nil | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return builtInType | 
 | } | 
 |  | 
 | // ARRAY_TYPE  -> array langle TYPE [comma int_const_dec] rangle [qstn] | 
 | func (p *Parser) tryParseArrayType() mojom.TypeRef { | 
 | 	if !p.OK() { | 
 | 		return nil | 
 | 	} | 
 |  | 
 | 	nextToken := p.peekNextToken("Trying to read a type.") | 
 | 	if nextToken.Kind != lexer.Name || nextToken.Text != "array" { | 
 | 		return nil | 
 | 	} | 
 | 	p.consumeNextToken() | 
 | 	if !p.match(lexer.LAngle) { | 
 | 		return nil | 
 | 	} | 
 |  | 
 | 	elementType := p.parseType() | 
 | 	if !p.OK() { | 
 | 		return nil | 
 | 	} | 
 |  | 
 | 	isFixedSize := p.tryMatch(lexer.Comma) | 
 | 	fixedSize := int32(-1) | 
 | 	if isFixedSize { | 
 | 		if size, _, _, ok := p.parsePositiveIntegerLiteral(false, false, false, 32); ok { | 
 | 			fixedSize = int32(size) | 
 | 		} else { | 
 | 			return nil | 
 | 		} | 
 |  | 
 | 	} | 
 | 	if !p.match(lexer.RAngle) { | 
 | 		return nil | 
 | 	} | 
 | 	nullable := p.tryMatch(lexer.Qstn) | 
 |  | 
 | 	return mojom.NewArrayTypeRef(elementType, fixedSize, nullable) | 
 | } | 
 |  | 
 | // MAP_TYPE      -> map langle MAP_KEY_TYPE comma TYPE rangle [qstn] | 
 | // MAP_KEY_TYPE  -> SIMPLE_TYPE | string | ENUM_TYPE | 
 | func (p *Parser) tryParseMapType() mojom.TypeRef { | 
 | 	if !p.OK() { | 
 | 		return nil | 
 | 	} | 
 |  | 
 | 	nextToken := p.peekNextToken("Trying to read a type.") | 
 | 	if nextToken.Kind != lexer.Name || nextToken.Text != "map" { | 
 | 		return nil | 
 | 	} | 
 | 	p.consumeNextToken() | 
 | 	if !p.match(lexer.LAngle) { | 
 | 		return nil | 
 | 	} | 
 |  | 
 | 	keyToken := p.peekNextToken("Trying to read a type.") | 
 | 	keyType := p.parseType() | 
 | 	p.match(lexer.Comma) | 
 | 	valueType := p.parseType() | 
 | 	if !p.match(lexer.RAngle) { | 
 | 		return nil | 
 | 	} | 
 | 	nullable := p.tryMatch(lexer.Qstn) | 
 |  | 
 | 	if !keyType.MarkUsedAsMapKey() { | 
 | 		message := fmt.Sprintf("The type %s is not allowed as the key type of a map. "+ | 
 | 			"Only simple types, strings and enum types may be map keys.", keyType) | 
 | 		p.parseErrorT(ParserErrorCodeUnexpectedToken, message, keyToken) | 
 | 		return nil | 
 | 	} | 
 |  | 
 | 	return mojom.NewMapTypeRef(keyType, valueType, nullable) | 
 | } | 
 |  | 
 | // USER_TYPE_REF    -> INTERFACE_TYPE | STRUCT_TYPE | UNION_TYPE | ENUM_TYPE | 
 | // INTERFACE_TYPE   -> IDENTIFIER [amp] [qstn] {{where IDENTIFIER resolves to an interface}} | 
 | // STRUCT_TYPE      -> IDENTIFIER [qstn] {{where IDENTIFIER resolves to a struct}} | 
 | // UNION_TYPE       -> IDENTIFIER [qstn] {{where IDENTIFIER resolves to a union}} | 
 | // ENUM_TYPE        -> IDENTIFIER [qstn] {{where IDENTIFIER resolves to an interface}} | 
 | func (p *Parser) parseTypeReference() mojom.TypeRef { | 
 | 	if !p.OK() { | 
 | 		return nil | 
 | 	} | 
 | 	p.pushChildNode("typeReference") | 
 | 	defer p.popNode() | 
 |  | 
 | 	identifier, identifierToken := p.parseIdentifier() | 
 | 	if !p.OK() { | 
 | 		return nil | 
 | 	} | 
 | 	// Note we must check for '&' before we check for '?'. | 
 | 	// The other order is invalid and if it occurrs the extraneous | 
 | 	// '&' will be detected later. | 
 | 	interfaceRequest := p.tryMatch(lexer.Amp) | 
 | 	nullable := p.tryMatch(lexer.Qstn) | 
 | 	if !p.OK() { | 
 | 		return nil | 
 | 	} | 
 | 	typeReference := mojom.NewUserTypeRef(identifier, nullable, | 
 | 		interfaceRequest, p.currentScope, identifierToken) | 
 | 	p.mojomDescriptor.RegisterUnresolvedTypeReference(typeReference) | 
 | 	return typeReference | 
 | } | 
 |  | 
 | // USER_VALUE_REF -> IDENTIFIER {{that resolves to an enum value or constant}} | 
 | func (p *Parser) parseUserValueRef(assigneeSpec mojom.AssigneeSpec) *mojom.UserValueRef { | 
 | 	if !p.OK() { | 
 | 		return nil | 
 | 	} | 
 | 	p.pushChildNode("userValueRef") | 
 | 	defer p.popNode() | 
 |  | 
 | 	identifier, identifierToken := p.parseIdentifier() | 
 | 	if !p.OK() { | 
 | 		return nil | 
 | 	} | 
 | 	valueReference := mojom.NewUserValueRef(assigneeSpec, identifier, | 
 | 		p.currentScope, identifierToken) | 
 | 	p.mojomDescriptor.RegisterUnresolvedValueReference(valueReference) | 
 | 	return valueReference | 
 | } | 
 |  | 
 | // POS_INT_LITERAL -> int_const_dec | int_const_hex | 
 | // | 
 | // parsePositiveIntegerLiteral() parses the next token as an integer using the smallest | 
 | // size integer possible given the input constraints. | 
 | // | 
 | // |initialMinus| should the string to be parsed be prepended with an initial minus sign? | 
 | // If this is true then |allowUnsigned| must also be false. | 
 | // | 
 | // |acceptHex| should the string to be parsed be allowed to be expressed in "0x" notation? | 
 | // | 
 | // |allowUnsigned| If the string cannot be parsed into a signed integer with a certain number of | 
 | // bits then before trying a larger number of bits we will try an unsigned integer if this is true. | 
 | // If |allowUnsigned| is not true then the returned |signed| will never be false if |ok| is true. | 
 | // | 
 | // |maxNumBits| This value should be 8, 16, 32 or 64 and represents the maximum size of an integer | 
 | // variable into which the parsed value may fit. If |ok| is true then |numBits| <= |maxNumBits|. | 
 | // | 
 | // |absoluteValue| The absolute value of the parsed integer, stored in a uint64. If |ok| | 
 | // is true then the value, times -1 if |initialMinus| is true, is guaranteed to be | 
 | // convertible without loss to the signed or unsigned integer type given by |signed| and | 
 | // |numBits|. For example if |numBits| = 16 and |signed| is true and |initialMinus| is true | 
 | // then -1 * |absoluteValue| may be cast to an int16. If |numBits| = 64 and |signed| is false then | 
 | // |absoluteValue| may be cast to a uint64. | 
 | // | 
 | // |numBits| is the smalles number of bits, 8, 16, 32, or 64, that the parsed value | 
 | // may fit into. If |ok| is true then |numBits| <= |maxNumBits|. | 
 | // | 
 | // |signed| modifies |numBits| to indicate what type of integer variable the result may be cast to. | 
 | // | 
 | // |ok| If this is false then there was a parsing error stored in p.err. | 
 | func (p *Parser) parsePositiveIntegerLiteral(initialMinus, acceptHex, allowUnsigned bool, maxNumBits int) (absoluteValue uint64, numBits int, signed, ok bool) { | 
 | 	p.pushChildNode("positive integer literal") | 
 | 	defer p.popNode() | 
 |  | 
 | 	if maxNumBits != 8 && maxNumBits != 16 && maxNumBits != 32 && maxNumBits != 64 { | 
 | 		panic(fmt.Sprintf("maxNumBits must be 8, 16, 32 or 64: %d", maxNumBits)) | 
 | 	} | 
 |  | 
 | 	if initialMinus && allowUnsigned { | 
 | 		panic("If initialMinus is true then allowUnsigned must be false.") | 
 | 	} | 
 |  | 
 | 	nextToken := p.peekNextToken("I was parsing an integer literal.") | 
 | 	p.consumeNextToken() | 
 | 	intText := p.lastConsumed.Text | 
 |  | 
 | 	// Set the base to 10 or 16. | 
 | 	base := 10 | 
 | 	switch nextToken.Kind { | 
 | 	case lexer.IntConstDec: | 
 | 		break | 
 | 	case lexer.IntConstHex: | 
 | 		if !acceptHex { | 
 | 			message := fmt.Sprintf("Illegal value: %s. Only a decimal integer literal is allowed here. "+ | 
 | 				"Hexadecimal is not valid.", nextToken) | 
 | 			p.parseErrorT(ParserErrorCodeUnexpectedToken, message, nextToken) | 
 | 			return | 
 | 		} | 
 | 		if len(intText) < 3 { | 
 | 			message := fmt.Sprintf("Invalid hex integer literal: %q.", intText) | 
 | 			p.parseErrorT(ParserErrorCodeIntegerParseError, message, nextToken) | 
 | 			return | 
 | 		} | 
 | 		intText = intText[2:] | 
 | 		base = 16 | 
 |  | 
 | 	default: | 
 | 		p.unexpectedTokenError(nextToken, "a positive integer literal") | 
 | 		return | 
 | 	} | 
 |  | 
 | 	// Add the initial minus sign if present. | 
 | 	if initialMinus { | 
 | 		intText = "-" + intText | 
 | 	} | 
 |  | 
 | 	var parseError error | 
 | 	for _, numBits := range []int{8, 16, 32, 64} { | 
 | 		if numBits > maxNumBits { | 
 | 			break | 
 | 		} | 
 | 		// First try to parse as signed. | 
 | 		signed = true | 
 | 		intVal, err := strconv.ParseInt(intText, base, numBits) | 
 | 		if err == nil { | 
 | 			if intVal < 0 { | 
 | 				intVal = -intVal | 
 | 			} | 
 | 			return uint64(intVal), numBits, signed, true | 
 | 		} | 
 |  | 
 | 		parseError = err | 
 | 		// Then try unsigned. | 
 | 		if allowUnsigned { | 
 | 			signed = false | 
 | 			intVal, err := strconv.ParseUint(intText, base, numBits) | 
 | 			if err == nil { | 
 | 				return intVal, numBits, signed, true | 
 | 			} | 
 | 			parseError = err | 
 | 		} | 
 | 	} | 
 |  | 
 | 	switch parseError.(*strconv.NumError).Err { | 
 | 	case strconv.ErrRange: | 
 | 		message := fmt.Sprintf("Integer literal value out of range: %s.", intText) | 
 | 		p.parseErrorT(ParserErrorCodeIntegerOutOfRange, message, nextToken) | 
 | 	case strconv.ErrSyntax: | 
 | 		panic(fmt.Sprintf("Lexer allowed unparsable integer literal: %s. "+ | 
 | 			"Kind = %s. error=%s.", nextToken.Text, nextToken.Kind, parseError)) | 
 | 	} | 
 | 	return 0, 0, false, false | 
 | } | 
 |  | 
 | // POS_FLOAT_LITERAL  -> float_const | 
 | func (p *Parser) parsePositiveFloatLiteral(initialMinus bool) (float64, bool) { | 
 | 	p.pushChildNode("positive float literal") | 
 | 	defer p.popNode() | 
 |  | 
 | 	nextToken := p.peekNextToken("I was parsing a float literal.") | 
 | 	p.match(lexer.FloatConst) | 
 | 	floatText := p.lastConsumed.Text | 
 | 	if initialMinus { | 
 | 		floatText = "-" + floatText | 
 | 	} | 
 | 	floatVal, err := strconv.ParseFloat(floatText, 64) | 
 | 	if err == nil { | 
 | 		return floatVal, true | 
 | 	} | 
 | 	switch err.(*strconv.NumError).Err { | 
 | 	case strconv.ErrRange: | 
 | 		message := fmt.Sprintf("Float literal value out of range: %s.", floatText) | 
 | 		p.parseErrorT(ParserErrorCodeIntegerOutOfRange, message, nextToken) | 
 | 	case strconv.ErrSyntax: | 
 | 		panic(fmt.Sprintf("Lexer allowed unparsable float literal: %s. "+ | 
 | 			"Kind = %s. error=%s.", nextToken.Text, nextToken.Kind, err)) | 
 | 	} | 
 | 	return 0, false | 
 | } | 
 |  | 
 | ///////////////// Parsing Helper Functions //////// | 
 |  | 
 | func (p *Parser) match(expectedKind lexer.TokenKind) bool { | 
 | 	if !p.OK() { | 
 | 		return false | 
 | 	} | 
 | 	message := fmt.Sprintf("Expecting %s.", expectedKind) | 
 | 	nextToken := p.peekNextToken(message) | 
 | 	if !p.OK() { | 
 | 		return false | 
 | 	} | 
 | 	if nextToken.Kind != expectedKind { | 
 | 		expected := fmt.Sprintf("%s", expectedKind) | 
 | 		p.unexpectedTokenError(nextToken, expected) | 
 | 		return false | 
 | 	} | 
 | 	p.consumeNextToken() | 
 | 	return true | 
 | } | 
 |  | 
 | func (p *Parser) tryMatch(expectedKind lexer.TokenKind) bool { | 
 | 	if p.checkEOF() { | 
 | 		return false | 
 | 	} | 
 | 	if !p.OK() { | 
 | 		return false | 
 | 	} | 
 | 	nextToken := p.peekNextToken("") | 
 | 	if nextToken.Kind != expectedKind { | 
 | 		return false | 
 | 	} | 
 | 	p.consumeNextToken() | 
 | 	return true | 
 | } | 
 |  | 
 | func (p *Parser) matchSemicolonToken(previousToken lexer.Token) bool { | 
 | 	if !p.OK() { | 
 | 		return false | 
 | 	} | 
 | 	if p.match(lexer.Semi) { | 
 | 		return true | 
 | 	} | 
 | 	message := fmt.Sprintf("Missing semicolon after %s.", previousToken) | 
 | 	p.parseErrorT(ParserErrorCodeUnexpectedToken, message, previousToken) | 
 | 	return false | 
 | } | 
 |  | 
 | func (p *Parser) matchSemicolon() bool { | 
 | 	return p.matchSemicolonToken(p.lastConsumed) | 
 | } | 
 |  | 
 | func (p *Parser) readText(kind lexer.TokenKind) (text string) { | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	if !p.match(kind) { | 
 | 		return | 
 | 	} | 
 | 	return p.lastConsumed.Text | 
 | } | 
 |  | 
 | func (p *Parser) readStringLiteral() (literal string) { | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	if !p.match(lexer.StringLiteral) { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	return lexer.StringLiteralTokenToText(p.lastConsumed) | 
 | } | 
 |  | 
 | func (p *Parser) readName() (name string) { | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 | 	name = p.readText(lexer.Name) | 
 | 	return | 
 | } | 
 |  | 
 | // Returns (-1, nil) if there was no specified ordinal. | 
 | func (p *Parser) readOrdinal() (ordinalValue int64, err error) { | 
 | 	ordinalValue = -1 | 
 | 	if !p.OK() { | 
 | 		return | 
 | 	} | 
 |  | 
 | 	if p.tryMatch(lexer.Ordinal) { | 
 | 		intTextToParse := p.lastConsumed.Text[1:] | 
 | 		ordinalValue, err = strconv.ParseInt(intTextToParse, 10, 64) | 
 | 		if err != nil || ordinalValue < 0 || ordinalValue >= math.MaxUint32 { | 
 | 			err = fmt.Errorf("Invalid ordinal string following '@': %q. Ordinals must be decimal integers between 0 and %d.", | 
 | 				intTextToParse, math.MaxUint32-1) | 
 | 			return | 
 | 		} | 
 | 	} | 
 | 	return | 
 | } | 
 |  | 
 | func (p *Parser) DeclData(name string, nameToken lexer.Token, attributes *mojom.Attributes) mojom.DeclarationData { | 
 | 	return mojom.DeclData(name, p.mojomFile, nameToken, attributes) | 
 | } | 
 |  | 
 | func (p *Parser) DeclDataWithOrdinal(name string, nameToken lexer.Token, attributes *mojom.Attributes, declaredOrdinal int64) mojom.DeclarationData { | 
 | 	return mojom.DeclDataWithOrdinal(name, p.mojomFile, nameToken, attributes, declaredOrdinal) | 
 | } | 
 |  | 
 | ////////////////// Scope Stack ///////////////////// | 
 | /// | 
 | func (p *Parser) pushScope(scope *mojom.Scope) { | 
 | 	if p.currentScope == nil { | 
 | 		p.currentScope = scope | 
 | 	} else { | 
 | 		if scope.Parent() != p.currentScope { | 
 | 			panic("Can only push child of current scope.") | 
 | 		} | 
 | 		p.currentScope = scope | 
 | 	} | 
 | } | 
 |  | 
 | func (p *Parser) popScope() { | 
 | 	if p.currentScope != nil { | 
 | 		p.currentScope = p.currentScope.Parent() | 
 | 	} | 
 | } | 
 |  | 
 | /////////// Utility functions | 
 |  | 
 | func (p *Parser) invalidOrdinalError(objectType, objectName string, nameToken lexer.Token, err error) { | 
 | 	message := fmt.Sprintf("%s %q: %s", objectType, objectName, err.Error()) | 
 | 	p.parseErrorT(ParserErrorCodeBadOrdinal, message, nameToken) | 
 | } | 
 |  | 
 | // unexpectedTokenError() sets the parser's current error to a ParseError | 
 | // with error code |ParserErrorCodeUnexpectedToken| and an error message | 
 | // of the form: | 
 | // | 
 | //    Unexpected '}'. Expecting a semicolon. | 
 | // | 
 | // |expected| should be a noun-phrase such as "a semicolon". | 
 | func (p *Parser) unexpectedTokenError(token lexer.Token, expected string) { | 
 | 	var message string | 
 | 	switch token.Kind { | 
 | 	case lexer.ErrorUnterminatedStringLiteral, lexer.ErrorUnterminatedComment: | 
 | 		message = fmt.Sprintf("%s", token) | 
 | 	default: | 
 | 		message = fmt.Sprintf("Unexpected %s. Expecting %s.", token, expected) | 
 | 	} | 
 | 	p.parseErrorT(ParserErrorCodeUnexpectedToken, message, token) | 
 | } |