Files
cloud-cap-samples/odata/etc/odata-url.pegjs
2021-02-01 09:37:06 +01:00

1162 lines
46 KiB
JavaScript

//------------------------------------------------------------------------------
// 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* // <a data service specific value indicating a
) // format specific to the specific data service> or
// <An IANA-defined [IANA-MMT] content type>
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
//------------------------------------------------------------------------------