# This is a JSON Grammar (JSG) file for the ShEx JSON format.
# The form "OBJNAME  { property:PROPTYPE ... }" matches an object of type OBJTYPE
# The form "RULENAME = NAME1 | NAME2 ..." matches any of NAMEn.
# the form "TERMNAME : "RegExp" matches any literal matching RegExp
# A PROPTYPE can be:
#   TERMINAL - a terminal, all caps in this example.
#   [PROPTYPE] - an array of PROPTYPE.
#   {TERMINAL->PROPTYPE} - a map from TERMINAL to PROPTYPE.
#   (PROPTYPE1 | PROPTYPE2...) - any of PROPTYPEn.

# All objects have a type property corresponding to the production name, e.g. "Schema"
.TYPE type - ObjectLiteral;

Schema           {
  "@context":"http://www.w3.org/ns/shex.jsonld" ?
  imports:[IRIREF+] ?
  startActs:[SemAct+] ?
  start:shapeExprOrRef ?
  shapes:[ShapeDecl+] ?
}
ShapeDecl        {
  id:shapeDeclLabel
  abstract:BOOL ?
  restricts:[shapeExprOrRef+] ?
  shapeExpr:shapeExpr
}

// Shape Expressions
shapeExpr        = ShapeOr | ShapeAnd | ShapeNot | NodeConstraint | Shape | ShapeExternal;
shapeExprOrRef   = shapeExpr | shapeDeclRef;
ShapeOr          { shapeExprs:[shapeExprOrRef{2,}] }
ShapeAnd         { shapeExprs:[shapeExprOrRef{2,}] }
ShapeNot         { shapeExpr:shapeExprOrRef }
shapeDeclRef     = shapeDeclLabel ;
shapeDeclLabel   = IRIREF | BNODE ;
NodeConstraint   {
  nodeKind:("iri"|"bnode"|"nonliteral"|"literal") ?
  datatype:IRIREF ?
  xsFacet *
  values:[valueSetValue+] ?
}
ShapeExternal    { }

# XML Schema facets
xsFacet          = stringFacet | numericFacet ;
stringFacet      = (length|minlength|maxlength):INTEGER | pattern:STRING flags:STRING? ;
numericFacet     = (mininclusive|minexclusive|maxinclusive|maxexclusive):numericLiteral
                 | (totaldigits|fractiondigits):INTEGER ;

numericLiteral   = INTEGER | DECIMAL | DOUBLE ;

# Value Sets
valueSetValue    = objectValue | IriStem | IriStemRange | LiteralStem
                 | LiteralStemRange | Language | LanguageStem | LanguageStemRange ;
objectValue      = IRIREF | ObjectLiteral ;
ObjectLiteral    { value:STRING language:STRING? type:STRING? }
IriStem          { stem:IRIREF } # IriStemRange with exclusions
IriStemRange     { stem:(IRIREF | Wildcard) exclusions:[IRIREF | IriStem+]? }
LiteralStem      { stem:STRING } # LiteralStemRange with exclusions
LiteralStemRange { stem:(STRING | Wildcard) exclusions:[STRING | LiteralStem+] }
Language         { languageTag: LANGTAG }
LanguageStem     { stem:(LANGTAG | EMPTY) }
LanguageStemRange{ stem:(LANGTAG | EMPTY | Wildcard) exclusions:[LANGTAG | LanguageStem+] }
Wildcard         {  }

Shape            {
  abstract:BOOL ?
  closed:BOOL ?
  extends:[shapeExprOrRef+] ?
  extra:[IRIREF+] ?
  expression:tripleExprOrRef ?
  semActs:[SemAct+] ?
  annotations:[Annotation+] ?
}

# Triple Expressions
tripleExpr       = EachOf | OneOf | TripleConstraint ;
tripleExprOrRef  = tripleExpr | tripleExprRef ;
EachOf           {
  id:tripleExprLabel ?
  expressions:[tripleExprOrRef{2,}]
  min:INTEGER ?
  max:INTEGER ?
  semActs:[SemAct+] ?
  annotations:[Annotation+] ?
}
OneOf            {
  id:tripleExprLabel ?
  expressions:[tripleExprOrRef{2,}]
  min:INTEGER ?
  max:INTEGER ?
  semActs:[SemAct+] ?
  annotations:[Annotation+] ?
}
TripleConstraint {
  id:tripleExprLabel ?
  inverse:BOOL ?
  predicate:IRIREF
  valueExpr:shapeExprOrRef ?
  min:INTEGER ?
  max:INTEGER ?
  semActs:[SemAct+] ?
  annotations:[Annotation+] ?
}
tripleExprRef    = tripleExprLabel ;
tripleExprLabel  = IRIREF | BNODE ;

SemAct           { name:IRIREF code:STRING? }
Annotation       { predicate:IRIREF object:objectValue }

# Terminals used in productions:
                 # <http://www.w3.org/TR/turtle/#grammar-production-IRIREF> - "<>"s
IRIREF           : (IRIREF_NO_U | '_' IRIREF_NO_COLON) IRIREF_ALL*;
                 # <http://www.w3.org/TR/turtle/#grammar-production-BLANK_NODE_LABEL>
BNODE            : '_:' (PN_CHARS_U | [0-9]) ((PN_CHARS | '.')* PN_CHARS)? ;
                 # JSON boolean tokens
BOOL             : "true" | "false" ;
                 # <http://www.w3.org/TR/turtle/#grammar-production-INTEGER>
INTEGER          : [+-]? [0-9]+ ;
                 # <http://www.w3.org/TR/turtle/#grammar-production-DECIMAL>
DECIMAL          : [+-]? [0-9]* '.' [0-9]+ ;
                 # <http://www.w3.org/TR/turtle/#grammar-production-DOUBLE>
DOUBLE           : [+-]? ([0-9]+ '.' [0-9]* EXPONENT | '.' [0-9]+ EXPONENT | [0-9]+ EXPONENT) ;
                 # <https://tools.ietf.org/search/bcp47>
LANGTAG          : [a-zA-Z]+ ('-' [a-zA-Z0-9]+)* ;
STRING           : .* ;
EMPTY            : '' ;

# Terminals use only in other terminals:
IRIREF_NO_U      : (IRIREF_CHARS | ':') ;
IRIREF_NO_COLON  : (IRIREF_CHARS | '_') ;
IRIREF_ALL       : (IRIREF_CHARS | ':' | '_') ;
IRIREF_CHARS     : [!#-9;=?-\u005B\u005Da-z~] | PN_CHARS_EXT | DIACRITICALS | UCHAR ;
PN_CHARS_BASE    : [A-Z] | [a-z] | PN_CHARS_EXT ;
PN_CHARS_EXT     : [\u00C0-\u00D6] | [\u00D8-\u00F6]
                 | [\u00F8-\u02FF] | [\u0370-\u037D] | [\u037F-\u1FFF]
                 | [\u200C-\u200D] | [\u2070-\u218F] | [\u2C00-\u2FEF]
                 | [\u3001-\uD7FF] | [\uF900-\uFDCF] | [\uFDF0-\uFFFD]
                 | [\u10000-\uEFFFF] ;
PN_CHARS         : PN_CHARS_U | DIACRITICALS ;
PN_CHARS_U       : PN_CHARS_BASE | '_' ;
DIACRITICALS     : '-' | [0-9] | '\u00B7' | [\u0300-\u036F] | [\u203F-\u2040] ;
UCHAR            : '\\u' HEX HEX HEX HEX
                 | '\\U' HEX HEX HEX HEX HEX HEX HEX HEX ;
HEX              : [0-9] | [A-F] | [a-f] ;
EXPONENT 	 : [eE] [+-]? [0-9]+ ;
