//------------------------------------------------------------------------------ // odata-abnf-construction-rules //------------------------------------------------------------------------------ // // OData Version 4.0 Plus Errata 03 // OASIS Standard incorporating Approved Errata 03 // 02 June 2016 // Copyright (c) OASIS Open 2016. All Rights Reserved. // Source: http://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/abnf/ // Link to latest version of narrative specification: http://docs.oasis-open.org/odata/odata/v4.0/errata03/odata-v4.0-errata03-part1-protocol-complete.html // // Technical Committee: // OASIS Open Data Protocol (OData) TC // https://www.oasis-open.org/committees/odata // // Chairs: // - Barbara Hartel (barbara.hartel@sap.com), SAP SE // - Ram Jeyaraman (Ram.Jeyaraman@microsoft.com), Microsoft // // Editors: // - Ralf Handl (ralf.handl@sap.com), SAP SE // - Michael Pizzo (mikep@microsoft.com), Microsoft // - Martin Zurmuehl (martin.zurmuehl@sap.com), SAP SE // // Additional artifacts: // This grammar is one component of a Work Product which consists of: // - OData Version 4.0 Part 1: Protocol // - OData Version 4.0 Part 2: URL Conventions // - OData Version 4.0 Part 3: Common Schema Definition Language (CSDL) // - OData ABNF Construction Rules Version 4.0 (this document) // - OData ABNF Test Cases // - OData Core Vocabulary // - OData Capabilities Vocabulary // - OData Measures Vocabulary // - OData Metadata Service Entity Model // - OData EDMX XML Schema // - OData EDM XML Schema // // Related work: // This work product is related to the following two Work Products, each of // which define alternate formats for OData payloads // - OData Atom Format Version 4.0 // - OData JSON Format Version 4.0 // This specification replaces or supersedes: // - None // // Declared XML namespaces: // - http://docs.oasis-open.org/odata/ns/edmx // - http://docs.oasis-open.org/odata/ns/edm // // Abstract: // The Open Data Protocol (OData) enables the creation of REST-based data // services, which allow resources, identified using Uniform Resource // Identifiers (URLs) and defined in a data model, to be published and // edited by Web clients using simple HTTP messages. This document defines // the URL syntax for requests and the serialization format for primitive // literals in request and response payloads. // // Overview: // This grammar uses the ABNF defined in RFC5234 with one extension: literals // enclosed in single quotes (e.g. '$metadata') are treated case-sensitive. // // The following rules assume that URIs have been percent-encoding normalized // as described in section 6.2.2.2 of RFC3986 // (http://tools.ietf.org/html/rfc3986#section-6.2.2.2) // before applying the grammar to them, i.e. all characters in the unreserved // set (see rule "unreserved" below) are plain literals and NOT // percent-encoded. // // For characters outside the unreserved set the rules explicitly state // whether the percent-encoded representation is treated identical to the // plain literal representation. // // One prominent example is the single quote that delimits OData primitive // type literals: %27 and ' are treated identically, so a single quote within // a string literal is "encoded" as two consecutive single quotes in either // literal or percent-encoded representation. // // Contents: // 1. Resource Path // 2. Query Options // 3. Context URL Fragments // 4. Expressions // 5. JSON format for function parameters // 6. Names and identifiers // 7. Literal Data Values // 8. Header values // 9. Punctuation // // A. URI syntax [RFC3986] // B. IRI syntax [RFC3986] // C. ABNF core definitions [RFC5234] // { const $=Object.assign const stack=[] let SELECT, columns const select = (col) => { if (!columns) columns = SELECT.columns = [] columns.push(col) } const expand = (col) => { select (col) stack.push (SELECT) SELECT = col columns = col.expand = [] } const end = () => { if (columns.length === 0) columns.push('*') SELECT = stack.pop() columns = SELECT.columns || SELECT.expand } const limit = (o) => { $(SELECT.limit || (SELECT.limit={}), o) } const functions = { toupper: 'upper', tolower: 'lower', indexof: 'locate', time: 'to_time', } const comparators = { eq: '=', ne: '!=', lt: '<', gt: '>', le: '<=', ge: '>=', } } //------------------------------------------------------------------------------ dummyStartRule = odataRelativeUri // just to please the test parser //------------------------------------------------------------------------------ // odataUri = serviceRoot ( odataRelativeUri )? // serviceRoot = ( "https" / "http" ) // Note: case-insensitive // "://" [^:]+ ( ":" DIGIT+ )? // "/" ( [^/]+ "/" )* odataRelativeUri = '$batch' // Note: case-sensitive! / '$entity' "?" entityOptions / '$entity' "/" qualifiedEntityTypeName "?" entityCastOptions / '$metadata' ( "?" format )? ( context )? / resourcePath ( "?" queryOptions )? //------------------------------------------------------------------------------ // 1. Resource Path //------------------------------------------------------------------------------ resourcePath = entitySetName ( collectionNavigation )? / singletonEntity ( singleNavigation )? / actionImportCall / entityColFunctionImportCall ( collectionNavigation )? / entityFunctionImportCall ( singleNavigation )? / complexColFunctionImportCall ( complexColPath )? / complexFunctionImportCall ( complexPath )? / primitiveColFunctionImportCall ( primitiveColPath )? / primitiveFunctionImportCall ( primitivePath )? / crossjoin / '$all' ( "/" qualifiedEntityTypeName )? collectionNavigation = ( "/" qualifiedEntityTypeName )? ( collectionNavPath )? collectionNavPath = keyPredicate ( singleNavigation )? / boundOperation / count / ref keyPredicate = simpleKey / compoundKey // / keyPathSegments simpleKey = OPEN ( parameterAlias / keyPropertyValue ) CLOSE compoundKey = OPEN keyValuePair ( COMMA keyValuePair )* CLOSE keyValuePair = ( primitiveKeyProperty / keyPropertyAlias ) EQ ( parameterAlias / keyPropertyValue ) keyPropertyValue = primitiveLiteral keyPropertyAlias = odataIdentifier //keyPathSegments = ( "/" keyPathLiteral )+ //keyPathLiteral = pchar* singleNavigation = ( "/" qualifiedEntityTypeName )? ( "/" propertyPath / boundOperation / ref / value // request the media resource of a media entity )? propertyPath = entityColNavigationProperty ( collectionNavigation )? / entityNavigationProperty ( singleNavigation )? / complexColProperty ( complexColPath )? / complexProperty ( complexPath )? / primitiveColProperty ( primitiveColPath )? / primitiveProperty ( primitivePath )? / streamProperty ( boundOperation )? primitiveColPath = count / boundOperation primitivePath = value / boundOperation complexColPath = ( "/" qualifiedComplexTypeName )? ( count / boundOperation )? complexPath = ( "/" qualifiedComplexTypeName )? ( "/" propertyPath / boundOperation )? count = '/$count' ref = '/$ref' value = '/$value' // boundOperation segments can only be composed if the type of the previous segment // matches the type of the first parameter of the action or function being called. // Note that the rule name reflects the return type of the function. boundOperation = "/" ( boundActionCall / boundEntityColFunctionCall ( collectionNavigation )? / boundEntityFunctionCall ( singleNavigation )? / boundComplexColFunctionCall ( complexColPath )? / boundComplexFunctionCall ( complexPath )? / boundPrimitiveColFunctionCall ( primitiveColPath )? / boundPrimitiveFunctionCall ( primitivePath )? ) actionImportCall = actionImport boundActionCall = namespace "." action // with the added restriction that the binding parameter MUST be either an entity or collection of entities // and is specified by reference using the URI immediately preceding (to the left) of the boundActionCall // The following boundXxxFunctionCall rules have the added restrictions that // - the function MUST support binding, and // - the binding parameter type MUST match the type of resource identified by the // URI immediately preceding (to the left) of the boundXxxFunctionCall, and // - the functionParameters MUST NOT include the bindingParameter. boundEntityFunctionCall = namespace "." entityFunction functionParameters boundEntityColFunctionCall = namespace "." entityColFunction functionParameters boundComplexFunctionCall = namespace "." complexFunction functionParameters boundComplexColFunctionCall = namespace "." complexColFunction functionParameters boundPrimitiveFunctionCall = namespace "." primitiveFunction functionParameters boundPrimitiveColFunctionCall = namespace "." primitiveColFunction functionParameters entityFunctionImportCall = entityFunctionImport functionParameters entityColFunctionImportCall = entityColFunctionImport functionParameters complexFunctionImportCall = complexFunctionImport functionParameters complexColFunctionImportCall = complexColFunctionImport functionParameters primitiveFunctionImportCall = primitiveFunctionImport functionParameters primitiveColFunctionImportCall = primitiveColFunctionImport functionParameters functionParameters = OPEN ( functionParameter ( COMMA functionParameter )* )? CLOSE functionParameter = parameterName EQ ( parameterAlias / primitiveLiteral ) parameterName = odataIdentifier parameterAlias = AT odataIdentifier crossjoin = '$crossjoin' OPEN entitySetName ( COMMA entitySetName )* CLOSE //------------------------------------------------------------------------------ // 2. Query Options //------------------------------------------------------------------------------ queryOptions = queryOption ( "&" queryOption )* queryOption = systemQueryOption / aliasAndValue / customQueryOption entityOptions = ( entityIdOption "&" )* id ( "&" entityIdOption )* entityIdOption = format / customQueryOption entityCastOptions = ( entityCastOption "&" )* id ( "&" entityCastOption )* entityCastOption = entityIdOption / expand / select id = '$id' EQ $[^&]+ // was: IRI-in-query systemQueryOption = deltatoken / expand / filter / format / id / inlinecount / orderby / search / select / skip / skiptoken / top expand = '$expand' EQ expandItem ( COMMA expandItem )* expandItem = STAR ( ref / OPEN levels CLOSE )? / expandPath ( ref ( OPEN expandRefOption ( SEMI expandRefOption )* CLOSE )? / count ( OPEN expandCountOption ( SEMI expandCountOption )* CLOSE )? / OPEN expandOption ( SEMI expandOption )* CLOSE )? expandPath = ( ( qualifiedEntityTypeName / qualifiedComplexTypeName ) "/" )? ( ( complexProperty / complexColProperty ) "/" ( qualifiedComplexTypeName "/" )? )* ( STAR / navigationProperty ( "/" qualifiedEntityTypeName )? ) expandCountOption = filter / search expandRefOption = expandCountOption / orderby / skip / top / inlinecount expandOption = expandRefOption / select / expand / levels levels = '$levels' EQ ( oneToNine *DIGIT / 'max' ) filter = '$filter' EQ boolCommonExpr orderby = '$orderby' EQ orderbyItem ( COMMA orderbyItem )* orderbyItem = commonExpr ( RWS ( 'asc' / 'desc' ) )? skip = '$skip' EQ DIGIT* top = '$top' EQ DIGIT* format = '$format' EQ ( "atom" / "json" / "xml" / pchar* "/" pchar* // or // inlinecount = '$count' EQ booleanValue search = '$search' EQ BWS searchExpr searchExpr = ( OPEN BWS searchExpr BWS CLOSE / searchTerm ) ( searchOrExpr / searchAndExpr )? searchOrExpr = RWS 'OR' RWS searchExpr searchAndExpr = RWS ( 'AND' RWS )? searchExpr searchTerm = ( 'NOT' RWS )? ( searchPhrase / searchWord ) searchPhrase = quotation_mark ( !DQUOTE qchar_no_AMP )+ quotation_mark searchWord = ALPHA+ // Actually: any character from the Unicode categories L or Nl, // but not the words AND, OR, and NOT select = '$select' EQ selectItem ( COMMA selectItem )* selectItem = STAR / allOperationsInSchema / ( ( qualifiedEntityTypeName / qualifiedComplexTypeName ) "/" )? ( selectProperty / qualifiedActionName / qualifiedFunctionName ) selectProperty = primitiveProperty / primitiveColProperty / navigationProperty / selectPath ( "/" selectProperty )? selectPath = ( complexProperty / complexColProperty ) ( "/" qualifiedComplexTypeName )? allOperationsInSchema = namespace "." STAR // The parameterNames uniquely identify the bound function overload // only if it has overloads. qualifiedActionName = namespace "." action qualifiedFunctionName = namespace "." function ( OPEN parameterNames CLOSE )? // The names of all non-binding parameters, separated by commas parameterNames = parameterName ( COMMA parameterName )* deltatoken = '$deltatoken' EQ qchar_no_AMP+ skiptoken = '$skiptoken' EQ qchar_no_AMP+ aliasAndValue = parameterAlias EQ parameterValue parameterValue = arrayOrObject / commonExpr customQueryOption = customName ( EQ customValue )? customName = !EQ !AT !"$" qchar_no_AMP ( !EQ qchar_no_AMP )* customValue = qchar_no_AMP* //------------------------------------------------------------------------------ // 3. Context URL Fragments //------------------------------------------------------------------------------ context = "#" contextFragment contextFragment = 'Collection($ref)' / '$ref' / 'Collection(Edm.EntityType)' / 'Collection(Edm.ComplexType)' / singletonEntity ( navigation ( containmentNavigation )* ( "/" qualifiedEntityTypeName )? )? ( selectList )? / qualifiedTypeName ( selectList )? / entitySet ( '/$deletedEntity' / '/$link' / '/$deletedLink' ) / entitySet keyPredicate "/" contextPropertyPath ( selectList )? / entitySet ( selectList )? ( '/$entity' / '/$delta' )? entitySet = entitySetName ( containmentNavigation )* ( "/" qualifiedEntityTypeName )? containmentNavigation = keyPredicate ( "/" qualifiedEntityTypeName )? navigation navigation = ( "/" complexProperty ( "/" qualifiedComplexTypeName )? )* "/" navigationProperty selectList = OPEN selectListItem ( COMMA selectListItem )* CLOSE selectListItem = STAR // all structural properties / allOperationsInSchema / ( qualifiedEntityTypeName "/" )? ( qualifiedActionName / qualifiedFunctionName / selectListProperty ) selectListProperty = primitiveProperty / primitiveColProperty / navigationProperty ( '+' )? ( selectList )? / selectPath ( "/" selectListProperty )? contextPropertyPath = primitiveProperty / primitiveColProperty / complexColProperty / complexProperty ( ( "/" qualifiedComplexTypeName )? "/" contextPropertyPath )? //------------------------------------------------------------------------------ // 4. Expressions //------------------------------------------------------------------------------ // Note: a boolCommonExpr is also a commonExpr, e.g. sort by Boolean commonExpr = ( primitiveLiteral / parameterAlias / arrayOrObject / rootExpr / firstMemberExpr / functionExpr / negateExpr / methodCallExpr / parenExpr / castExpr ) ( addExpr / subExpr / mulExpr / divExpr / modExpr )? boolCommonExpr = ( isofExpr / boolMethodCallExpr / notExpr / commonExpr ( eqExpr / neExpr / ltExpr / leExpr / gtExpr / geExpr / hasExpr )? / boolParenExpr ) ( andExpr / orExpr )? rootExpr = '$root/' ( entitySetName keyPredicate / singletonEntity ) ( singleNavigationExpr )? firstMemberExpr = memberExpr / inscopeVariableExpr ( "/" memberExpr )? memberExpr = ( qualifiedEntityTypeName "/" )? ( propertyPathExpr / boundFunctionExpr ) propertyPathExpr = ( entityColNavigationProperty ( collectionNavigationExpr )? / entityNavigationProperty ( singleNavigationExpr )? / complexColProperty ( complexColPathExpr )? / complexProperty ( complexPathExpr )? / primitiveColProperty ( collectionPathExpr )? / primitiveProperty ( primitivePathExpr )? / streamProperty ( primitivePathExpr )? ) inscopeVariableExpr = implicitVariableExpr / lambdaVariableExpr // only allowed inside a lambdaPredicateExpr implicitVariableExpr = '$it' // references the unnamed outer variable of the query lambdaVariableExpr = odataIdentifier collectionNavigationExpr = ( "/" qualifiedEntityTypeName )? ( keyPredicate ( singleNavigationExpr )? / collectionPathExpr )? singleNavigationExpr = "/" memberExpr complexColPathExpr = ( "/" qualifiedComplexTypeName )? ( collectionPathExpr )? collectionPathExpr = count / "/" boundFunctionExpr / "/" anyExpr / "/" allExpr complexPathExpr = ( "/" qualifiedComplexTypeName )? ( "/" propertyPathExpr / "/" boundFunctionExpr )? primitivePathExpr = "/" boundFunctionExpr boundFunctionExpr = functionExpr // boundFunction segments can only be composed if the type of the // previous segment matches the type of the first function parameter functionExpr = namespace "." ( entityColFunction functionExprParameters ( collectionNavigationExpr )? / entityFunction functionExprParameters ( singleNavigationExpr )? / complexColFunction functionExprParameters ( complexColPathExpr )? / complexFunction functionExprParameters ( complexPathExpr )? / primitiveColFunction functionExprParameters ( collectionPathExpr )? / primitiveFunction functionExprParameters ( primitivePathExpr )? ) functionExprParameters = OPEN ( functionExprParameter ( COMMA functionExprParameter )* )? CLOSE functionExprParameter = parameterName EQ ( parameterAlias / parameterValue ) anyExpr = 'any' OPEN BWS ( lambdaVariableExpr BWS COLON BWS lambdaPredicateExpr )? BWS CLOSE allExpr = 'all' OPEN BWS lambdaVariableExpr BWS COLON BWS lambdaPredicateExpr BWS CLOSE lambdaPredicateExpr = boolCommonExpr // containing at least one lambdaVariableExpr methodCallExpr = indexOfMethodCallExpr / toLowerMethodCallExpr / toUpperMethodCallExpr / trimMethodCallExpr / substringMethodCallExpr / concatMethodCallExpr / lengthMethodCallExpr / yearMethodCallExpr / monthMethodCallExpr / dayMethodCallExpr / hourMethodCallExpr / minuteMethodCallExpr / secondMethodCallExpr / fractionalsecondsMethodCallExpr / totalsecondsMethodCallExpr / dateMethodCallExpr / timeMethodCallExpr / roundMethodCallExpr / floorMethodCallExpr / ceilingMethodCallExpr / distanceMethodCallExpr / geoLengthMethodCallExpr / totalOffsetMinutesMethodCallExpr / minDateTimeMethodCallExpr / maxDateTimeMethodCallExpr / nowMethodCallExpr boolMethodCallExpr = endsWithMethodCallExpr / startsWithMethodCallExpr / containsMethodCallExpr / intersectsMethodCallExpr containsMethodCallExpr = 'contains' OPEN BWS commonExpr BWS COMMA BWS commonExpr BWS CLOSE startsWithMethodCallExpr = 'startswith' OPEN BWS commonExpr BWS COMMA BWS commonExpr BWS CLOSE endsWithMethodCallExpr = 'endswith' OPEN BWS commonExpr BWS COMMA BWS commonExpr BWS CLOSE lengthMethodCallExpr = 'length' OPEN BWS commonExpr BWS CLOSE indexOfMethodCallExpr = 'indexof' OPEN BWS commonExpr BWS COMMA BWS commonExpr BWS CLOSE substringMethodCallExpr = 'substring' OPEN BWS commonExpr BWS COMMA BWS commonExpr BWS ( COMMA BWS commonExpr BWS )? CLOSE toLowerMethodCallExpr = 'tolower' OPEN BWS commonExpr BWS CLOSE toUpperMethodCallExpr = 'toupper' OPEN BWS commonExpr BWS CLOSE trimMethodCallExpr = 'trim' OPEN BWS commonExpr BWS CLOSE concatMethodCallExpr = 'concat' OPEN BWS commonExpr BWS COMMA BWS commonExpr BWS CLOSE yearMethodCallExpr = 'year' OPEN BWS commonExpr BWS CLOSE monthMethodCallExpr = 'month' OPEN BWS commonExpr BWS CLOSE dayMethodCallExpr = 'day' OPEN BWS commonExpr BWS CLOSE hourMethodCallExpr = 'hour' OPEN BWS commonExpr BWS CLOSE minuteMethodCallExpr = 'minute' OPEN BWS commonExpr BWS CLOSE secondMethodCallExpr = 'second' OPEN BWS commonExpr BWS CLOSE fractionalsecondsMethodCallExpr = 'fractionalseconds' OPEN BWS commonExpr BWS CLOSE totalsecondsMethodCallExpr = 'totalseconds' OPEN BWS commonExpr BWS CLOSE dateMethodCallExpr = 'date' OPEN BWS commonExpr BWS CLOSE timeMethodCallExpr = 'time' OPEN BWS commonExpr BWS CLOSE totalOffsetMinutesMethodCallExpr = 'totaloffsetminutes' OPEN BWS commonExpr BWS CLOSE minDateTimeMethodCallExpr = 'mindatetime' OPEN BWS CLOSE maxDateTimeMethodCallExpr = 'maxdatetime' OPEN BWS CLOSE nowMethodCallExpr = 'now' OPEN BWS CLOSE roundMethodCallExpr = 'round' OPEN BWS commonExpr BWS CLOSE floorMethodCallExpr = 'floor' OPEN BWS commonExpr BWS CLOSE ceilingMethodCallExpr = 'ceiling' OPEN BWS commonExpr BWS CLOSE distanceMethodCallExpr = 'geo.distance' OPEN BWS commonExpr BWS COMMA BWS commonExpr BWS CLOSE geoLengthMethodCallExpr = 'geo.length' OPEN BWS commonExpr BWS CLOSE intersectsMethodCallExpr = 'geo.intersects' OPEN BWS commonExpr BWS COMMA BWS commonExpr BWS CLOSE boolParenExpr = OPEN BWS boolCommonExpr BWS CLOSE parenExpr = OPEN BWS commonExpr BWS CLOSE andExpr = RWS 'and' RWS boolCommonExpr orExpr = RWS 'or' RWS boolCommonExpr eqExpr = RWS 'eq' RWS commonExpr neExpr = RWS 'ne' RWS commonExpr ltExpr = RWS 'lt' RWS commonExpr leExpr = RWS 'le' RWS commonExpr gtExpr = RWS 'gt' RWS commonExpr geExpr = RWS 'ge' RWS commonExpr hasExpr = RWS 'has' RWS enum addExpr = RWS 'add' RWS commonExpr subExpr = RWS 'sub' RWS commonExpr mulExpr = RWS 'mul' RWS commonExpr divExpr = RWS 'div' RWS commonExpr modExpr = RWS 'mod' RWS commonExpr negateExpr = "-" BWS commonExpr notExpr = 'not' RWS boolCommonExpr isofExpr = 'isof' OPEN BWS ( commonExpr BWS COMMA BWS )? qualifiedTypeName BWS CLOSE castExpr = 'cast' OPEN BWS ( commonExpr BWS COMMA BWS )? qualifiedTypeName BWS CLOSE //------------------------------------------------------------------------------ // 5. JSON format for function parameters //------------------------------------------------------------------------------ // Note: the query part of a URI needs to be partially percent-decoded before // applying these rules, see comment at the top of this file //------------------------------------------------------------------------------ arrayOrObject = complexColInUri / complexInUri / rootExprCol / primitiveColInUri complexColInUri = begin_array ( complexInUri ( value_separator complexInUri )* )? end_array complexInUri = begin_object ( ( annotationInUri / primitivePropertyInUri / complexPropertyInUri / collectionPropertyInUri / navigationPropertyInUri ) ( value_separator ( annotationInUri / primitivePropertyInUri / complexPropertyInUri / collectionPropertyInUri / navigationPropertyInUri ) )* )? end_object collectionPropertyInUri = ( quotation_mark primitiveColProperty quotation_mark name_separator primitiveColInUri ) / ( quotation_mark complexColProperty quotation_mark name_separator complexColInUri ) primitiveColInUri = begin_array ( primitiveLiteralInJSON ( value_separator primitiveLiteralInJSON )* )? end_array complexPropertyInUri = quotation_mark complexProperty quotation_mark name_separator complexInUri annotationInUri = quotation_mark AT namespace "." termName quotation_mark name_separator ( complexInUri / complexColInUri / primitiveLiteralInJSON / primitiveColInUri ) primitivePropertyInUri = quotation_mark primitiveProperty quotation_mark name_separator primitiveLiteralInJSON navigationPropertyInUri = singleNavPropInJSON / collectionNavPropInJSON singleNavPropInJSON = quotation_mark entityNavigationProperty quotation_mark name_separator rootExpr collectionNavPropInJSON = quotation_mark entityColNavigationProperty quotation_mark name_separator rootExprCol rootExprCol = begin_array ( rootExpr ( value_separator rootExpr )* )? end_array // JSON syntax: adapted to URI restrictions from (RFC4627)? begin_object = BWS ( "{" / "%7B" ) BWS end_object = BWS ( "}" / "%7D" ) BWS begin_array = BWS ( "(" / "%5B" ) BWS end_array = BWS ( ")?" / "%5D" ) BWS quotation_mark = DQUOTE / "%22" name_separator = BWS COLON BWS value_separator = BWS COMMA BWS primitiveLiteralInJSON = stringInJSON / numberInJSON / 'true' / 'false' / 'null' stringInJSON = quotation_mark charInJSON* quotation_mark charInJSON = qchar_unescaped / qchar_JSON_special / escape ( quotation_mark / escape / ( "/" / "%2F" ) // solidus U+002F - literal form is allowed in the query part of a URL / 'b' // backspace U+0008 / 'f' // form feed U+000C / 'n' // line feed U+000A / 'r' // carriage return U+000D / 't' // tab U+0009 / 'u' HEXDIG HEXDIG HEXDIG HEXDIG // U+XXXX ) qchar_JSON_special = SP / ":" / "{" / "}" / "(" / ")" // some agents put these unencoded into the query part of a URL qchar_unescaped = unreserved / pct_encoded_unescaped / other_delims / ":" / "@" / "/" / "?" / "$" / "'" / "=" escape = "\\" / "%5C" // reverse solidus U+005C unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" pct_encoded_unescaped = "%" ( "0" / "1" / "3" / "4" / "6" / "7" / "8" / "9" / A_to_F ) HEXDIG / "%" "2" ( "0" / "1" / "3" / "4" / "5" / "6" / "7" / "8" / "9" / A_to_F ) / "%" "5" ( DIGIT / "A" / "B" / "D" / "E" / "F" ) numberInJSON = "-"? int frac? exp? int = "0" / ( oneToNine *DIGIT ) frac = "." DIGIT+ exp = "e" ( "-" / "+" )? DIGIT+ //------------------------------------------------------------------------------ // 6. Names and identifiers //------------------------------------------------------------------------------ singleQualifiedTypeName = qualifiedEntityTypeName / qualifiedComplexTypeName / qualifiedTypeDefinitionName / qualifiedEnumTypeName / primitiveTypeName qualifiedTypeName = singleQualifiedTypeName / 'Collection' OPEN singleQualifiedTypeName CLOSE qualifiedEntityTypeName = namespace "." entityTypeName qualifiedComplexTypeName = namespace "." complexTypeName qualifiedTypeDefinitionName = namespace "." typeDefinitionName qualifiedEnumTypeName = namespace "." enumerationTypeName // an alias is just a single-part namespace namespace = namespacePart ( "." namespacePart )* namespacePart = odataIdentifier entitySetName = odataIdentifier singletonEntity = odataIdentifier entityTypeName = odataIdentifier complexTypeName = odataIdentifier typeDefinitionName = odataIdentifier enumerationTypeName = odataIdentifier enumerationMember = odataIdentifier termName = odataIdentifier // Note: this pattern is overly restrictive, the normative definition is type TSimpleIdentifier in OData EDM XML Schema odataIdentifier = $(identifierLeadingCharacter identifierCharacter*) identifierLeadingCharacter = ALPHA / "_" // plus Unicode characters from the categories L or Nl identifierCharacter = ALPHA / "_" / DIGIT // plus Unicode characters from the categories L, Nl, Nd, Mn, Mc, Pc, or Cf primitiveTypeName = 'Edm.' ( 'Binary' / 'Boolean' / 'Byte' / 'Date' / 'DateTimeOffset' / 'Decimal' / 'Double' / 'Duration' / 'Guid' / 'Int16' / 'Int32' / 'Int64' / 'SByte' / 'Single' / 'Stream' / 'String' / 'TimeOfDay' / abstractSpatialTypeName ( concreteSpatialTypeName )? ) abstractSpatialTypeName = 'Geography' / 'Geometry' concreteSpatialTypeName = 'Collection' / 'LineString' / 'MultiLineString' / 'MultiPoint' / 'MultiPolygon' / 'Point' / 'Polygon' primitiveProperty = primitiveKeyProperty / primitiveNonKeyProperty primitiveKeyProperty = odataIdentifier primitiveNonKeyProperty = odataIdentifier primitiveColProperty = odataIdentifier complexProperty = odataIdentifier complexColProperty = odataIdentifier streamProperty = odataIdentifier navigationProperty = entityNavigationProperty / entityColNavigationProperty entityNavigationProperty = odataIdentifier entityColNavigationProperty = odataIdentifier action = odataIdentifier actionImport = odataIdentifier function = entityFunction / entityColFunction / complexFunction / complexColFunction / primitiveFunction / primitiveColFunction entityFunction = odataIdentifier entityColFunction = odataIdentifier complexFunction = odataIdentifier complexColFunction = odataIdentifier primitiveFunction = odataIdentifier primitiveColFunction = odataIdentifier entityFunctionImport = odataIdentifier entityColFunctionImport = odataIdentifier complexFunctionImport = odataIdentifier complexColFunctionImport = odataIdentifier primitiveFunctionImport = odataIdentifier primitiveColFunctionImport = odataIdentifier //------------------------------------------------------------------------------ // 7. Literal Data Values //------------------------------------------------------------------------------ // in URLs primitiveLiteral = nullValue // plain values up to int64Value / booleanValue / guidValue / dateValue / dateTimeOffsetValue / timeOfDayValue / decimalValue / doubleValue / singleValue / sbyteValue / byteValue / int16Value / int32Value / int64Value / string // single-quoted / duration // all others are quoted and prefixed / binary / enum / geographyCollection / geographyLineString / geographyMultiLineString / geographyMultiPoint / geographyMultiPolygon / geographyPoint / geographyPolygon / geometryCollection / geometryLineString / geometryMultiLineString / geometryMultiPoint / geometryMultiPolygon / geometryPoint / geometryPolygon // in Atom and JSON message bodies and CSDL DefaultValue attributes primitiveValue = booleanValue / guidValue / durationValue / dateValue / dateTimeOffsetValue / timeOfDayValue / enumValue / fullCollectionLiteral / fullLineStringLiteral / fullMultiPointLiteral / fullMultiLineStringLiteral / fullMultiPolygonLiteral / fullPointLiteral / fullPolygonLiteral / decimalValue / doubleValue / singleValue / sbyteValue / byteValue / int16Value / int32Value / int64Value / binaryValue // also valid are: // - any XML string for strings in Atom and CSDL documents // - any JSON string for JSON documents nullValue = 'null' {return {val:null}} // base64url encoding according to http://tools.ietf.org/html/rfc4648#section-5 binary = "binary" SQUOTE binaryValue SQUOTE binaryValue = (base64char base64char base64char base64char)* ( base64b16 / base64b8 )? base64b16 = base64char base64char ( 'A' / 'E' / 'I' / 'M' / 'Q' / 'U' / 'Y' / 'c' / 'g' / 'k' / 'o' / 's' / 'w' / '0' / '4' / '8' ) ( "=" )? base64b8 = base64char ( 'A' / 'Q' / 'g' / 'w' ) ( "==" )? base64char = ALPHA / DIGIT / "-" / "_" booleanValue = val:( true / false ) {return {val}} true = "true" {return true} false = "false" {return false} decimalValue = s:$((SIGN)? DIGIT+ ("." DIGIT+)?) {return {val:Number(s)}} doubleValue = s:$(decimalValue ( "e" (SIGN)? DIGIT+ )? / nanInfinity) // IEEE 754 binary64 floating-point number (15-17 decimal digits) {return {val:Number(s)}} singleValue = doubleValue // IEEE 754 binary32 floating-point number (6-9 decimal digits) nanInfinity = 'NaN' / '-INF' / 'INF' guidValue = $( /* 8 */ HEXDIG HEXDIG HEXDIG HEXDIG HEXDIG HEXDIG HEXDIG HEXDIG /* 4 */ "-" HEXDIG HEXDIG HEXDIG HEXDIG /* 4 */ "-" HEXDIG HEXDIG HEXDIG HEXDIG /* 4 */ "-" HEXDIG HEXDIG HEXDIG HEXDIG /* 12 */ "-" HEXDIG HEXDIG HEXDIG HEXDIG HEXDIG HEXDIG HEXDIG HEXDIG HEXDIG HEXDIG HEXDIG HEXDIG ) byteValue = ( DIGIT DIGIT DIGIT )+ // numbers in the range from 0 to 255 sbyteValue = sign? byteValue // numbers in the range from -128 to 127 int16Value = sign? ( DIGIT DIGIT DIGIT DIGIT DIGIT )+ // numbers in the range from -32768 to 32767 int32Value = sign? ( DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT )+ // numbers in the range from -2147483648 to 2147483647 int64Value = sign? ( DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT )+ // numbers in the range from -9223372036854775808 to 9223372036854775807 sign = "+" / "-" string = SQUOTE ( SQUOTE_in_string / !SQUOTE pchar )* SQUOTE SQUOTE_in_string = SQUOTE SQUOTE // two consecutive single quotes represent one within a string literal qchar_no_AMP = unreserved / pct_encoded / other_delims / ":" / "@" / "/" / "?" / "$" / "'" / "=" pchar = unreserved / pct_encoded / sub_delims / ":" / "@" pct_encoded = "%" HEXDIG HEXDIG sub_delims = "$" / "&" / "'" / "=" / other_delims other_delims = "!" / "(" / ")" / "*" / "+" / "," / ";" dateValue = year "-" month "-" day dateTimeOffsetValue = year "-" month "-" day "T" hour ":" minute ( ":" second ( "." fractionalSeconds )? )? ( "Z" / sign hour ":" minute ) duration = "duration" SQUOTE durationValue SQUOTE durationValue = sign? "P" ( DIGIT+ "D" )? ( "T" ( DIGIT+ "H" )? ( DIGIT+ "M" )? ( DIGIT+ ( "." DIGIT+ )? "S" )? )? // the above is an approximation of the rules for an xml dayTimeDuration. // see the lexical representation for dayTimeDuration in http://www.w3.org/TR/xmlschema11-2#dayTimeDuration for more information timeOfDayValue = hour ":" minute ( ":" second ( "." fractionalSeconds )? )? oneToNine = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" zeroToFiftyNine = ( "0" / "1" / "2" / "3" / "4" / "5" ) DIGIT year = ( "-" )? ( "0" ( DIGIT DIGIT DIGIT ) / oneToNine DIGIT DIGIT DIGIT ) month = "0" oneToNine / "1" ( "0" / "1" / "2" ) day = "0" oneToNine / ( "1" / "2" ) DIGIT / "3" ( "0" / "1" ) hour = ( "0" / "1" ) DIGIT / "2" ( "0" / "1" / "2" / "3" ) minute = zeroToFiftyNine second = zeroToFiftyNine fractionalSeconds = DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT DIGIT enum = qualifiedEnumTypeName SQUOTE enumValue SQUOTE enumValue = singleEnumValue ( COMMA singleEnumValue )* singleEnumValue = enumerationMember / enumMemberValue enumMemberValue = int64Value geographyCollection = geographyPrefix SQUOTE fullCollectionLiteral SQUOTE fullCollectionLiteral = sridLiteral collectionLiteral collectionLiteral = "Collection(" geoLiteral ( COMMA geoLiteral )* CLOSE geoLiteral = collectionLiteral / lineStringLiteral / multiPointLiteral / multiLineStringLiteral / multiPolygonLiteral / pointLiteral / polygonLiteral geographyLineString = geographyPrefix SQUOTE fullLineStringLiteral SQUOTE fullLineStringLiteral = sridLiteral lineStringLiteral lineStringLiteral = "LineString" lineStringData lineStringData = OPEN positionLiteral ( COMMA positionLiteral )+ CLOSE geographyMultiLineString = geographyPrefix SQUOTE fullMultiLineStringLiteral SQUOTE fullMultiLineStringLiteral = sridLiteral multiLineStringLiteral multiLineStringLiteral = "MultiLineString(" ( lineStringData ( COMMA lineStringData )* )? CLOSE geographyMultiPoint = geographyPrefix SQUOTE fullMultiPointLiteral SQUOTE fullMultiPointLiteral = sridLiteral multiPointLiteral multiPointLiteral = "MultiPoint(" ( pointData ( COMMA pointData )* )? CLOSE geographyMultiPolygon = geographyPrefix SQUOTE fullMultiPolygonLiteral SQUOTE fullMultiPolygonLiteral = sridLiteral multiPolygonLiteral multiPolygonLiteral = "MultiPolygon(" ( polygonData ( COMMA polygonData )* )? CLOSE geographyPoint = geographyPrefix SQUOTE fullPointLiteral SQUOTE fullPointLiteral = sridLiteral pointLiteral sridLiteral = "SRID" EQ DIGIT DIGIT DIGIT DIGIT DIGIT SEMI pointLiteral ="Point" pointData pointData = OPEN positionLiteral CLOSE positionLiteral = doubleValue SP doubleValue // longitude, then latitude geographyPolygon = geographyPrefix SQUOTE fullPolygonLiteral SQUOTE fullPolygonLiteral = sridLiteral polygonLiteral polygonLiteral = "Polygon" polygonData polygonData = OPEN ringLiteral ( COMMA ringLiteral )* CLOSE ringLiteral = OPEN positionLiteral ( COMMA positionLiteral )* CLOSE // Within each ringLiteral, the first and last positionLiteral elements MUST be an exact syntactic match to each other. // Within the polygonData, the ringLiterals MUST specify their points in appropriate winding order. // In order of traversal, points to the left side of the ring are interpreted as being in the polygon. geometryCollection = geometryPrefix SQUOTE fullCollectionLiteral SQUOTE geometryLineString = geometryPrefix SQUOTE fullLineStringLiteral SQUOTE geometryMultiLineString = geometryPrefix SQUOTE fullMultiLineStringLiteral SQUOTE geometryMultiPoint = geometryPrefix SQUOTE fullMultiPointLiteral SQUOTE geometryMultiPolygon = geometryPrefix SQUOTE fullMultiPolygonLiteral SQUOTE geometryPoint = geometryPrefix SQUOTE fullPointLiteral SQUOTE geometryPolygon = geometryPrefix SQUOTE fullPolygonLiteral SQUOTE geographyPrefix = "geography" geometryPrefix = "geometry" //------------------------------------------------------------------------------ // 8. Header values //------------------------------------------------------------------------------ header = content_id / odata_entityid / odata_isolation / odata_maxversion / odata_version / prefer content_id = "Content-ID" ":" OWS unreserved+ odata_entityid = "OData-EntityID" ":" OWS ![^&]+ //was: IRI-in-header odata_isolation = "OData-Isolation" ":" OWS "snapshot" odata_maxversion = "OData-MaxVersion" ":" OWS DIGIT+ "." DIGIT+ odata_version = "OData-Version" ":" OWS "4.0" prefer = "Prefer" ":" OWS preference ( COMMA preference )* preference = allowEntityReferencesPreference / callbackPreference / continueOnErrorPreference / includeAnnotationsPreference / maxpagesizePreference / respondAsyncPreference / returnPreference / trackChangesPreference / waitPreference // and everything allowed by http://tools.ietf.org/html/draft-snell-http-prefer-18 // / token ( EQ_h word )? ( OWS ";" ( OWS parameter )? )* allowEntityReferencesPreference = "odata.allow-entityreferences" callbackPreference = "odata.callback" OWS ";" OWS "url" EQ_h DQUOTE [^"]* DQUOTE continueOnErrorPreference = "odata.continue-on-error" includeAnnotationsPreference = "odata.include-annotations" EQ_h DQUOTE annotationsList DQUOTE annotationsList = annotationIdentifier (COMMA annotationIdentifier)* annotationIdentifier = ( excludeOperator )? ( STAR / namespace "." ( termName / STAR ) ) ( "#" odataIdentifier )? excludeOperator = "-" maxpagesizePreference = "odata.maxpagesize" EQ_h oneToNine *DIGIT respondAsyncPreference = "respond-async" returnPreference = "return" EQ_h ( 'representation' / 'minimal' ) trackChangesPreference = "odata.track-changes" waitPreference = "wait" EQ_h DIGIT+ //parameter = token ( EQ_h word )? //word = token / quoted-string //token = 1*tchar //tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" // / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" // / DIGIT / ALPHA //quoted-string = DQUOTE ( qdtext / quoted-pair )* DQUOTE //qdtext = %x21 / %x23-5B / %x5D-7E / obs_text / OWS //obs_text = %x80-FF //quoted-pair = "\" ( HTAB / SP / VCHAR / obs_text ) OWS = ( SP / HTAB )* // "optional" whitespace BWS_h = ( SP / HTAB )* // "bad" whitespace in header values EQ_h = BWS_h EQ BWS_h //------------------------------------------------------------------------------ // 9. Punctuation //------------------------------------------------------------------------------ RWS = ( SP / HTAB / "%20" / "%09" )+ // "required" whitespace BWS = ( SP / HTAB / "%20" / "%09" )* // "bad" whitespace AT = "@" / "%40" COLON = ":" / "%3A" COMMA = "," / "%2C" EQ = "=" SIGN = "+" / "%2B" / "-" SEMI = ";" / "%3B" STAR = "*" / "%2A" SQUOTE = "'" / "%27" OPEN = "(" / "%28" CLOSE = ")" / "%29" //------------------------------------------------------------------------------ // C. ABNF core definitions [RFC5234] //------------------------------------------------------------------------------ ALPHA = [A-Za-z] DIGIT = [0-9] HEXDIG = DIGIT / A_to_F A_to_F = "A" / "B" / "C" / "D" / "E" / "F" DQUOTE = '"' SP = " " HTAB = "\t" //WSP = SP / HTAB //LWSP = (WSP / CRLF WSP)* VCHAR = [!-~] //CHAR = %x01-7F //LOCTET = %x00-FF //CR = %x0D //LF = %x0A //CRLF = CR LF //BIT = "0" / "1" //------------------------------------------------------------------------------ // End of odata-abnf-construction-rules //------------------------------------------------------------------------------