shex 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +8 -8
- data/VERSION +1 -1
- data/etc/doap.ttl +1 -1
- data/lib/shex.rb +11 -11
- data/lib/shex/algebra.rb +43 -33
- data/lib/shex/algebra/node_constraint.rb +12 -3
- data/lib/shex/algebra/not.rb +1 -1
- data/lib/shex/algebra/operator.rb +20 -13
- data/lib/shex/algebra/schema.rb +106 -33
- data/lib/shex/algebra/shape_expression.rb +1 -1
- data/lib/shex/algebra/stem.rb +46 -2
- data/lib/shex/algebra/stem_range.rb +70 -2
- data/lib/shex/extensions/extension.rb +2 -2
- data/lib/shex/meta.rb +4282 -2334
- data/lib/shex/parser.rb +223 -72
- data/lib/shex/shex_context.rb +67 -68
- data/lib/shex/terminals.rb +36 -21
- metadata +2 -2
    
        data/lib/shex/parser.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # -*- encoding: utf-8 -*-
         | 
| 1 2 | 
             
            require 'ebnf'
         | 
| 2 3 | 
             
            require 'ebnf/ll1/parser'
         | 
| 3 4 | 
             
            require 'shex/meta'
         | 
| @@ -102,6 +103,18 @@ module ShEx | |
| 102 103 | 
             
                terminal(:LANGTAG,              LANGTAG) do |prod, token, input|
         | 
| 103 104 | 
             
                  input[:language] = token.value[1..-1]
         | 
| 104 105 | 
             
                end
         | 
| 106 | 
            +
                terminal(:LANG_STRING_LITERAL_LONG1, LANG_STRING_LITERAL_LONG1, unescape: true) do |prod, token, input|
         | 
| 107 | 
            +
                  input[:string], _, input[:language] = token.value[3..-1].rpartition("'''@")
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
                terminal(:LANG_STRING_LITERAL_LONG2, LANG_STRING_LITERAL_LONG2, unescape: true) do |prod, token, input|
         | 
| 110 | 
            +
                  input[:string], _, input[:language] = token.value[3..-1].rpartition('"""@')
         | 
| 111 | 
            +
                end
         | 
| 112 | 
            +
                terminal(:LANG_STRING_LITERAL1,      LANG_STRING_LITERAL1, unescape: true) do |prod, token, input|
         | 
| 113 | 
            +
                  input[:string], _, input[:language] = token.value[1..-1].rpartition("'@")
         | 
| 114 | 
            +
                end
         | 
| 115 | 
            +
                terminal(:LANG_STRING_LITERAL2,      LANG_STRING_LITERAL2, unescape: true) do |prod, token, input|
         | 
| 116 | 
            +
                  input[:string], _, input[:language] = token.value[1..-1].rpartition('"@')
         | 
| 117 | 
            +
                end
         | 
| 105 118 | 
             
                terminal(:STRING_LITERAL_LONG1, STRING_LITERAL_LONG1, unescape: true) do |prod, token, input|
         | 
| 106 119 | 
             
                  input[:string] = token.value[3..-4]
         | 
| 107 120 | 
             
                end
         | 
| @@ -114,6 +127,9 @@ module ShEx | |
| 114 127 | 
             
                terminal(:STRING_LITERAL2,      STRING_LITERAL2, unescape: true) do |prod, token, input|
         | 
| 115 128 | 
             
                  input[:string] = token.value[1..-2]
         | 
| 116 129 | 
             
                end
         | 
| 130 | 
            +
                terminal(:REGEXP,               REGEXP) do |prod, token, input|
         | 
| 131 | 
            +
                  input[:regexp] = token.value
         | 
| 132 | 
            +
                end
         | 
| 117 133 | 
             
                terminal(:RDF_TYPE,             RDF_TYPE) do |prod, token, input|
         | 
| 118 134 | 
             
                  input[:iri] = (a = RDF.type.dup; a.lexical = 'a'; a)
         | 
| 119 135 | 
             
                end
         | 
| @@ -143,8 +159,7 @@ module ShEx | |
| 143 159 | 
             
                       'MINEXCLUSIVE',
         | 
| 144 160 | 
             
                       'MAXINCLUSIVE',
         | 
| 145 161 | 
             
                       'MAXEXCLUSIVE'  then input[:numericRange] = token.value.downcase.to_sym
         | 
| 146 | 
            -
             | 
| 147 | 
            -
                  when 'PATTERN'       then input[:pattern] = token.value.downcase.to_sym
         | 
| 162 | 
            +
                  when 'NOT'           then input[:not] = token.value.downcase.to_sym
         | 
| 148 163 | 
             
                  when 'START'         then input[:start] = token.value.downcase.to_sym
         | 
| 149 164 | 
             
                  else
         | 
| 150 165 | 
             
                    #raise "Unexpected MC terminal: #{token.inspect}"
         | 
| @@ -184,7 +199,7 @@ module ShEx | |
| 184 199 | 
             
                # [5]     notStartAction        ::= start | shapeExprDecl
         | 
| 185 200 | 
             
                # [6]     start                 ::= "start" '=' shapeExpression
         | 
| 186 201 | 
             
                production(:start) do |input, data, callback|
         | 
| 187 | 
            -
                  input[:start] = data[:shapeExpression]
         | 
| 202 | 
            +
                  input[:start] = Array(data[:shapeExpression]).first || data[:shape]
         | 
| 188 203 | 
             
                end
         | 
| 189 204 | 
             
                # [7]     startActions          ::= codeDecl+
         | 
| 190 205 |  | 
| @@ -193,9 +208,9 @@ module ShEx | |
| 193 208 | 
             
                # [9]     shapeExprDecl         ::= shapeLabel (shapeExpression|"EXTERNAL")
         | 
| 194 209 | 
             
                production(:shapeExprDecl) do |input, data, callback|
         | 
| 195 210 | 
             
                  id = Array(data[:shapeLabel]).first
         | 
| 196 | 
            -
                  expression = case data[:shapeExpression]
         | 
| 211 | 
            +
                  expression = case Array(data[:shapeExpression]).first
         | 
| 197 212 | 
             
                  when Algebra::NodeConstraint, Algebra::Or, Algebra::And, Algebra::Not, Algebra::Shape, RDF::Resource
         | 
| 198 | 
            -
                    data[:shapeExpression]
         | 
| 213 | 
            +
                    Array(data[:shapeExpression]).first
         | 
| 199 214 | 
             
                  else
         | 
| 200 215 | 
             
                    data[:external] ? Algebra::External.new() : Algebra::Shape.new()
         | 
| 201 216 | 
             
                  end
         | 
| @@ -204,13 +219,36 @@ module ShEx | |
| 204 219 | 
             
                  (input[:shapes] ||= []) << expression
         | 
| 205 220 | 
             
                end
         | 
| 206 221 |  | 
| 207 | 
            -
                # [10]    shapeExpression | 
| 208 | 
            -
                #  | 
| 222 | 
            +
                # [10]    shapeExpression  ::= shapeAtomNoRef shapeOr?
         | 
| 223 | 
            +
                #                            | "NOT" (shapeAtomNoRef | shapeRef) shapeOr?
         | 
| 224 | 
            +
                #                            | shapeRef shapeOr
         | 
| 225 | 
            +
                production(:shapeExpression) do |input, data, callback|
         | 
| 226 | 
            +
                  expression = Array(data[:shapeExpression]).first || data[:shape]
         | 
| 227 | 
            +
                  expression = Algebra::Not.new(expression) if data[:not]
         | 
| 228 | 
            +
                  (input[:shapeExpression] ||= []) << expression
         | 
| 229 | 
            +
                end
         | 
| 209 230 |  | 
| 210 | 
            -
                # [ | 
| 211 | 
            -
                 | 
| 231 | 
            +
                # [11]    inlineShapeExpression ::= inlineShapeOr
         | 
| 232 | 
            +
                # [12]    shapeOr               ::= shapeOrA | shapeOrB shapeOrA?
         | 
| 233 | 
            +
                # [12a]   shapeOrA              ::= ("OR" shapeAnd)+
         | 
| 234 | 
            +
                start_production(:shapeOrA) do |input, data, callback|
         | 
| 235 | 
            +
                  data[:shapeExpression] = input.delete(:shapeExpression)
         | 
| 236 | 
            +
                  data[:shapeExpression] ||=  Array(input.delete(:shape)) if input[:shape]
         | 
| 237 | 
            +
                  data[:shapeExpression] = [Algebra::Not.new(*data[:shapeExpression])] if input.delete(:not)
         | 
| 238 | 
            +
                end
         | 
| 239 | 
            +
                production(:shapeOrA) do |input, data, callback|
         | 
| 212 240 | 
             
                  shape_or(input, data)
         | 
| 213 241 | 
             
                end
         | 
| 242 | 
            +
                # [12b]   shapeOrB              ::= ("AND" shapeNot)+
         | 
| 243 | 
            +
                start_production(:shapeOrB) do |input, data, callback|
         | 
| 244 | 
            +
                  data[:shapeExpression] = input.delete(:shapeExpression)
         | 
| 245 | 
            +
                  data[:shapeExpression] ||=  Array(input.delete(:shape)) if input[:shape]
         | 
| 246 | 
            +
                  data[:shapeExpression] = [Algebra::Not.new(*data[:shapeExpression])] if input.delete(:not)
         | 
| 247 | 
            +
                end
         | 
| 248 | 
            +
                production(:shapeOrB) do |input, data, callback|
         | 
| 249 | 
            +
                  shape_and(input, data)
         | 
| 250 | 
            +
                end
         | 
| 251 | 
            +
             | 
| 214 252 | 
             
                # [13]    inlineShapeOr         ::= inlineShapeAnd ("OR" inlineShapeAnd)*
         | 
| 215 253 | 
             
                production(:inlineShapeOr) do |input, data, callback|
         | 
| 216 254 | 
             
                  shape_or(input, data)
         | 
| @@ -222,7 +260,7 @@ module ShEx | |
| 222 260 | 
             
                  else
         | 
| 223 261 | 
             
                    Array(data[:shapeExpression]).first
         | 
| 224 262 | 
             
                  end
         | 
| 225 | 
            -
                  input[:shapeExpression]  | 
| 263 | 
            +
                  (input[:shapeExpression] ||= []) << expression if expression
         | 
| 226 264 | 
             
                rescue ArgumentError => e
         | 
| 227 265 | 
             
                  error(nil, "Argument Error on OR: #{e.message}")
         | 
| 228 266 | 
             
                end
         | 
| @@ -262,7 +300,7 @@ module ShEx | |
| 262 300 | 
             
                end
         | 
| 263 301 | 
             
                def shape_not(input, data)
         | 
| 264 302 | 
             
                  input.merge!(data.dup.keep_if {|k, v| [:closed, :extraPropertySet, :codeDecl].include?(k)})
         | 
| 265 | 
            -
                  expression = data[:shapeExpression]
         | 
| 303 | 
            +
                  expression = Array(data[:shapeExpression]).first
         | 
| 266 304 | 
             
                  expression = Algebra::Not.new(expression) if data[:not]
         | 
| 267 305 | 
             
                  #error(nil, "Expected an atom for NOT") unless expression
         | 
| 268 306 | 
             
                  (input[:shapeExpression] ||= []) << expression if expression
         | 
| @@ -276,7 +314,16 @@ module ShEx | |
| 276 314 | 
             
                production(:shapeAtom) do |input, data, callback|
         | 
| 277 315 | 
             
                  shape_atom(input, data)
         | 
| 278 316 | 
             
                end
         | 
| 279 | 
            -
             | 
| 317 | 
            +
             | 
| 318 | 
            +
                # [19]   shapeAtomNoRef        ::= nodeConstraint shapeOrRef?
         | 
| 319 | 
            +
                #                                 | shapeDefinition
         | 
| 320 | 
            +
                #                                 | "(" shapeExpression ")"
         | 
| 321 | 
            +
                #                                 | '.'  # no constraint
         | 
| 322 | 
            +
                production(:shapeAtomNoRef) do |input, data, callback|
         | 
| 323 | 
            +
                  shape_atom(input, data)
         | 
| 324 | 
            +
                end
         | 
| 325 | 
            +
             | 
| 326 | 
            +
                # [20]    inlineShapeAtom       ::= nodeConstraint inlineShapeOrRef?
         | 
| 280 327 | 
             
                #                                 | inlineShapeOrRef nodeConstraint?
         | 
| 281 328 | 
             
                #                                 | "(" shapeExpression ")"
         | 
| 282 329 | 
             
                #                                 | '.'  # no constraint
         | 
| @@ -285,7 +332,7 @@ module ShEx | |
| 285 332 | 
             
                end
         | 
| 286 333 | 
             
                def shape_atom(input, data)
         | 
| 287 334 | 
             
                  constraint = data[:nodeConstraint]
         | 
| 288 | 
            -
                  shape = data[:shapeOrRef] || data[:shapeExpression]
         | 
| 335 | 
            +
                  shape = data[:shapeOrRef] || Array(data[:shapeExpression]).first || data[:shape]
         | 
| 289 336 | 
             
                  input.merge!(data.dup.keep_if {|k, v| [:closed, :extraPropertySet, :codeDecl].include?(k)})
         | 
| 290 337 |  | 
| 291 338 | 
             
                  expression = [constraint, shape].compact
         | 
| @@ -295,34 +342,36 @@ module ShEx | |
| 295 342 | 
             
                  else        Algebra::And.new(*expression, {})
         | 
| 296 343 | 
             
                  end
         | 
| 297 344 |  | 
| 298 | 
            -
                  input[:shapeExpression]  | 
| 345 | 
            +
                  (input[:shapeExpression] ||= []) << expression if expression
         | 
| 299 346 | 
             
                end
         | 
| 300 347 | 
             
                private :shape_atom
         | 
| 301 348 |  | 
| 302 | 
            -
                # [ | 
| 349 | 
            +
                # [21]    shapeOrRef            ::= shapeDefinition | shapeRef
         | 
| 303 350 | 
             
                production(:shapeOrRef) do |input, data, callback|
         | 
| 304 351 | 
             
                  shape_or_ref(input, data)
         | 
| 305 352 | 
             
                end
         | 
| 306 | 
            -
                # [ | 
| 353 | 
            +
                # [22]    inlineShapeOrRef      ::= inlineShapeDefinition | shapeRef
         | 
| 307 354 | 
             
                production(:inlineShapeOrRef) do |input, data, callback|
         | 
| 308 355 | 
             
                  shape_or_ref(input, data)
         | 
| 309 356 | 
             
                end
         | 
| 310 357 | 
             
                def shape_or_ref(input, data)
         | 
| 311 358 | 
             
                  input.merge!(data.dup.keep_if {|k, v| [:closed, :extraPropertySet, :codeDecl].include?(k)})
         | 
| 312 | 
            -
                   | 
| 313 | 
            -
                    input[:shapeOrRef] = data[:shape] || Array(data[:shapeLabel]).first
         | 
| 314 | 
            -
                  end
         | 
| 359 | 
            +
                  input[:shapeOrRef] = data[:shape] if data[:shape]
         | 
| 315 360 | 
             
                rescue ArgumentError => e
         | 
| 316 361 | 
             
                  error(nil, "Argument Error on ShapeOrRef: #{e.message}")
         | 
| 317 362 | 
             
                end
         | 
| 318 363 | 
             
                private :shape_or_ref
         | 
| 319 364 |  | 
| 320 | 
            -
                # [ | 
| 321 | 
            -
                 | 
| 365 | 
            +
                # [23]    shapeRef              ::= ATPNAME_LN | ATPNAME_NS | '@' shapeLabel
         | 
| 366 | 
            +
                production(:shapeRef) do |input, data, callback|
         | 
| 367 | 
            +
                  input[:shape] = Array(data[:shapeLabel]).first
         | 
| 368 | 
            +
                end
         | 
| 369 | 
            +
             | 
| 370 | 
            +
                # [24]    litNodeConstraint     ::= "LITERAL" xsFacet*
         | 
| 322 371 | 
             
                #                                 | datatype xsFacet*
         | 
| 323 372 | 
             
                #                                 | valueSet xsFacet*
         | 
| 324 | 
            -
                #                                 |  | 
| 325 | 
            -
                production(: | 
| 373 | 
            +
                #                                 | numericFacet+
         | 
| 374 | 
            +
                production(:litNodeConstraint) do |input, data, callback|
         | 
| 326 375 | 
             
                  # Semantic validate (A Syntax error)
         | 
| 327 376 | 
             
                  case
         | 
| 328 377 | 
             
                  when data[:datatype] && data[:numericFacet]
         | 
| @@ -333,7 +382,7 @@ module ShEx | |
| 333 382 |  | 
| 334 383 | 
             
                  attrs = []
         | 
| 335 384 | 
             
                  attrs << [:datatype, data[:datatype]] if data [:datatype]
         | 
| 336 | 
            -
                  attrs +=  | 
| 385 | 
            +
                  attrs += Array(data[:shapeAtomLiteral])
         | 
| 337 386 | 
             
                  attrs += Array(data[:valueSetValue])
         | 
| 338 387 | 
             
                  attrs += Array(data[:numericFacet])
         | 
| 339 388 | 
             
                  attrs += Array(data[:stringFacet])
         | 
| @@ -341,12 +390,23 @@ module ShEx | |
| 341 390 | 
             
                  input[:nodeConstraint] = Algebra::NodeConstraint.new(*attrs.compact, {})
         | 
| 342 391 | 
             
                end
         | 
| 343 392 |  | 
| 344 | 
            -
                # [ | 
| 393 | 
            +
                # [25]    nonLitNodeConstraint  ::= nonLiteralKind stringFacet*
         | 
| 394 | 
            +
                #                                 | stringFacet+
         | 
| 395 | 
            +
                production(:nonLitNodeConstraint) do |input, data, callback|
         | 
| 396 | 
            +
                  # Semantic validate (A Syntax error)
         | 
| 397 | 
            +
             | 
| 398 | 
            +
                  attrs = []
         | 
| 399 | 
            +
                  attrs += Array(data[:nonLiteralKind])
         | 
| 400 | 
            +
                  attrs += Array(data[:stringFacet])
         | 
| 345 401 |  | 
| 346 | 
            -
             | 
| 347 | 
            -
                 | 
| 348 | 
            -
             | 
| 349 | 
            -
                # | 
| 402 | 
            +
                  input[:nodeConstraint] = Algebra::NodeConstraint.new(*attrs.compact, {})
         | 
| 403 | 
            +
                end
         | 
| 404 | 
            +
             | 
| 405 | 
            +
                # [26]    nonLiteralKind        ::= "IRI" | "BNODE" | "NONLITERAL"
         | 
| 406 | 
            +
             | 
| 407 | 
            +
                # [27]    xsFacet               ::= stringFacet | numericFacet
         | 
| 408 | 
            +
                # [28]    stringFacet           ::= stringLength INTEGER
         | 
| 409 | 
            +
                #                                 | REGEXP
         | 
| 350 410 | 
             
                production(:stringFacet) do |input, data, callback|
         | 
| 351 411 | 
             
                  input[:stringFacet] ||= []
         | 
| 352 412 | 
             
                  input[:stringFacet] << if data[:stringLength]
         | 
| @@ -354,14 +414,27 @@ module ShEx | |
| 354 414 | 
             
                      error(nil, "#{data[:stringLength]} constraint may only be used once in a Node Constraint", production: :stringFacet)
         | 
| 355 415 | 
             
                    end
         | 
| 356 416 | 
             
                    [data[:stringLength], data[:literal]]
         | 
| 357 | 
            -
                  elsif data[: | 
| 358 | 
            -
                     | 
| 417 | 
            +
                  elsif re = data[:regexp]
         | 
| 418 | 
            +
                    unless re =~ %r(^/(.*)/([smix]*)$)
         | 
| 419 | 
            +
                      error(nil, "#{re.inspect} regular expression must be in the form /pattern/flags?", production: :stringFacet)
         | 
| 420 | 
            +
                    end
         | 
| 421 | 
            +
                    flags = $2 unless $2.to_s.empty?
         | 
| 422 | 
            +
                    pattern = $1.gsub('\\/', '/').gsub(UCHAR) do
         | 
| 423 | 
            +
                      [($1 || $2).hex].pack('U*')
         | 
| 424 | 
            +
                    end.force_encoding(Encoding::UTF_8)
         | 
| 425 | 
            +
             | 
| 426 | 
            +
                    # Any other escaped character is a syntax error
         | 
| 427 | 
            +
                    if pattern.match(%r([^\\]\\[^nrt/\\|\.?*+\[\]\(\){}$#x2D#x5B#x5D#x5E-]))
         | 
| 428 | 
            +
                      error(nil, "Regexp contains illegal escape: #{pattern.inspect}", production: :stringFacet)
         | 
| 429 | 
            +
                    end
         | 
| 430 | 
            +
             | 
| 431 | 
            +
                    [:pattern, pattern, flags].compact
         | 
| 359 432 | 
             
                  end
         | 
| 360 433 | 
             
                end
         | 
| 361 434 |  | 
| 362 | 
            -
                # [ | 
| 435 | 
            +
                # [29]    stringLength          ::= "LENGTH" | "MINLENGTH" | "MAXLENGTH"
         | 
| 363 436 |  | 
| 364 | 
            -
                # [ | 
| 437 | 
            +
                # [30]    numericFacet          ::= numericRange (numericLiteral | string '^^' datatype )
         | 
| 365 438 | 
             
                #                                 | numericLength INTEGER
         | 
| 366 439 | 
             
                production(:numericFacet) do |input, data, callback|
         | 
| 367 440 | 
             
                  input[:numericFacet] ||= []
         | 
| @@ -374,18 +447,19 @@ module ShEx | |
| 374 447 | 
             
                  end
         | 
| 375 448 | 
             
                end
         | 
| 376 449 |  | 
| 377 | 
            -
                # [ | 
| 378 | 
            -
                # [ | 
| 450 | 
            +
                # [31]    numericRange          ::= "MININCLUSIVE" | "MINEXCLUSIVE" | "MAXINCLUSIVE" | "MAXEXCLUSIVE"
         | 
| 451 | 
            +
                # [32]    numericLength         ::= "TOTALDIGITS" | "FRACTIONDIGITS"
         | 
| 379 452 |  | 
| 380 | 
            -
                # [ | 
| 453 | 
            +
                # [33]    shapeDefinition       ::= (includeSet | extraPropertySet | "CLOSED")* '{' tripleExpression? '}' annotation* semanticActions
         | 
| 381 454 | 
             
                production(:shapeDefinition) do |input, data, callback|
         | 
| 382 455 | 
             
                  shape_definition(input, data)
         | 
| 383 456 | 
             
                end
         | 
| 384 | 
            -
                # [ | 
| 457 | 
            +
                # [34]    inlineShapeDefinition ::= (includeSet | extraPropertySet | "CLOSED")* '{' tripleExpression? '}'
         | 
| 385 458 | 
             
                production(:inlineShapeDefinition) do |input, data, callback|
         | 
| 386 459 | 
             
                  shape_definition(input, data)
         | 
| 387 460 | 
             
                end
         | 
| 388 461 | 
             
                def shape_definition(input, data)
         | 
| 462 | 
            +
                  # FIXME: includeSet
         | 
| 389 463 | 
             
                  expression = data[:tripleExpression]
         | 
| 390 464 | 
             
                  attrs = Array(data[:extraPropertySet])
         | 
| 391 465 | 
             
                  attrs << :closed if data[:closed]
         | 
| @@ -397,13 +471,13 @@ module ShEx | |
| 397 471 | 
             
                end
         | 
| 398 472 | 
             
                private :shape_definition
         | 
| 399 473 |  | 
| 400 | 
            -
                # [ | 
| 474 | 
            +
                # [35]     extraPropertySet       ::= "EXTRA" predicate+
         | 
| 401 475 | 
             
                production(:extraPropertySet) do |input, data, callback|
         | 
| 402 476 | 
             
                  (input[:extraPropertySet] ||= []) << data[:predicate].unshift(:extra)
         | 
| 403 477 | 
             
                end
         | 
| 404 478 |  | 
| 405 | 
            -
                # [ | 
| 406 | 
            -
                # [ | 
| 479 | 
            +
                # [36]    tripleExpression      ::= oneOfTripleExpr
         | 
| 480 | 
            +
                # [37]    oneOfTripleExpr           ::= groupTripleExpr ('|' groupTripleExpr)*
         | 
| 407 481 | 
             
                production(:oneOfTripleExpr) do |input, data, callback|
         | 
| 408 482 | 
             
                  expression = if Array(data[:tripleExpression]).length > 1
         | 
| 409 483 | 
             
                    Algebra::OneOf.new(*data[:tripleExpression], {})
         | 
| @@ -413,7 +487,7 @@ module ShEx | |
| 413 487 | 
             
                  input[:tripleExpression] = expression if expression
         | 
| 414 488 | 
             
                end
         | 
| 415 489 |  | 
| 416 | 
            -
                # [ | 
| 490 | 
            +
                # [40]    groupTripleExpr            ::= unaryTripleExpr (';' unaryTripleExpr?)*
         | 
| 417 491 | 
             
                production(:groupTripleExpr) do |input, data, callback|
         | 
| 418 492 | 
             
                  expression = if Array(data[:tripleExpression]).length > 1
         | 
| 419 493 | 
             
                    Algebra::EachOf.new(*data[:tripleExpression], {})
         | 
| @@ -423,7 +497,7 @@ module ShEx | |
| 423 497 | 
             
                  (input[:tripleExpression] ||= []) << expression if expression
         | 
| 424 498 | 
             
                end
         | 
| 425 499 |  | 
| 426 | 
            -
                # [ | 
| 500 | 
            +
                # [43]    unaryTripleExpr            ::= productionLabel? (tripleConstraint | bracketedTripleExpr) | include
         | 
| 427 501 | 
             
                production(:unaryTripleExpr) do |input, data, callback|
         | 
| 428 502 | 
             
                  expression = data[:tripleExpression]
         | 
| 429 503 | 
             
                  expression.id = data[:productionLabel] if expression && data[:productionLabel]
         | 
| @@ -431,7 +505,12 @@ module ShEx | |
| 431 505 | 
             
                  (input[:tripleExpression] ||= []) << expression if expression
         | 
| 432 506 | 
             
                end
         | 
| 433 507 |  | 
| 434 | 
            -
                # [ | 
| 508 | 
            +
                # [43a]    productionLabel       ::= '$' (iri | blankNode)
         | 
| 509 | 
            +
                production(:productionLabel) do |input, data, callback|
         | 
| 510 | 
            +
                  input[:productionLabel] = data[:iri] || data[:blankNode]
         | 
| 511 | 
            +
                end
         | 
| 512 | 
            +
             | 
| 513 | 
            +
                # [44]    bracketedTripleExpr   ::= '(' oneOfTripleExpr ')' cardinality? annotation* semanticActions
         | 
| 435 514 | 
             
                production(:bracketedTripleExpr) do |input, data, callback|
         | 
| 436 515 | 
             
                  # XXX cardinality? annotation* semanticActions
         | 
| 437 516 | 
             
                  case expression = data[:tripleExpression]
         | 
| @@ -451,18 +530,13 @@ module ShEx | |
| 451 530 | 
             
                  input[:tripleExpression] = expression
         | 
| 452 531 | 
             
                end
         | 
| 453 532 |  | 
| 454 | 
            -
                # [ | 
| 455 | 
            -
                production(:productionLabel) do |input, data, callback|
         | 
| 456 | 
            -
                  input[:productionLabel] = data[:iri] || data[:blankNode]
         | 
| 457 | 
            -
                end
         | 
| 458 | 
            -
             | 
| 459 | 
            -
                # [43]    tripleConstraint      ::= senseFlags? predicate shapeExpression cardinality? annotation* semanticActions
         | 
| 533 | 
            +
                # [45]    tripleConstraint      ::= senseFlags? predicate shapeExpression cardinality? annotation* semanticActions
         | 
| 460 534 | 
             
                production(:tripleConstraint) do |input, data, callback|
         | 
| 461 535 | 
             
                  cardinality = data.fetch(:cardinality, {})
         | 
| 462 536 | 
             
                  attrs = [
         | 
| 463 537 | 
             
                    (:inverse if data[:inverse] || data[:not]),
         | 
| 464 538 | 
             
                    [:predicate, Array(data[:predicate]).first],
         | 
| 465 | 
            -
                    data[:shapeExpression],
         | 
| 539 | 
            +
                    Array(data[:shapeExpression]).first,
         | 
| 466 540 | 
             
                    ([:min, cardinality[:min]] if cardinality[:min]),
         | 
| 467 541 | 
             
                    ([:max, cardinality[:max]] if cardinality[:max])
         | 
| 468 542 | 
             
                  ].compact
         | 
| @@ -472,71 +546,146 @@ module ShEx | |
| 472 546 | 
             
                  input[:tripleExpression] = Algebra::TripleConstraint.new(*attrs, {}) unless attrs.empty?
         | 
| 473 547 | 
             
                end
         | 
| 474 548 |  | 
| 475 | 
            -
                # [ | 
| 476 | 
            -
                # [ | 
| 477 | 
            -
                # [ | 
| 549 | 
            +
                # [46]    cardinality            ::= '*' | '+' | '?' | REPEAT_RANGE
         | 
| 550 | 
            +
                # [47]    senseFlags             ::= '^'
         | 
| 551 | 
            +
                # [48]    valueSet              ::= '[' valueSetValue* ']'
         | 
| 478 552 |  | 
| 479 | 
            -
                # [ | 
| 553 | 
            +
                # [49]    valueSetValue         ::= iriRange | literalRange | languageRange | '.' exclusion+
         | 
| 480 554 | 
             
                production(:valueSetValue) do |input, data, callback|
         | 
| 481 | 
            -
                   | 
| 555 | 
            +
                  range = data[:iriRange] || data[:literalRange] || data[:languageRange]
         | 
| 556 | 
            +
                  if !range
         | 
| 557 | 
            +
                    # All exclusions must be consistent IRI/Literal/Language
         | 
| 558 | 
            +
                    case data[:exclusion].first
         | 
| 559 | 
            +
                    when Algebra::IriStem, RDF::URI
         | 
| 560 | 
            +
                      unless data[:exclusion].all? {|e| e.is_a?(Algebra::IriStem) || e.is_a?(RDF::URI)}
         | 
| 561 | 
            +
                        error(nil, "Exclusions must all be IRI type")
         | 
| 562 | 
            +
                      end
         | 
| 563 | 
            +
                      range = Algebra::IriStemRange.new(:wildcard, data[:exclusion].unshift(:exclusions))
         | 
| 564 | 
            +
                    when Algebra::LiteralStem, RDF::Literal
         | 
| 565 | 
            +
                      unless data[:exclusion].all? {|e| e.is_a?(Algebra::LiteralStem) || e.is_a?(RDF::Literal)}
         | 
| 566 | 
            +
                        error(nil, "Exclusions must all be Literal type")
         | 
| 567 | 
            +
                      end
         | 
| 568 | 
            +
                      range = Algebra::LiteralStemRange.new(:wildcard, data[:exclusion].unshift(:exclusions))
         | 
| 569 | 
            +
                    else
         | 
| 570 | 
            +
                      unless data[:exclusion].all? {|e| e.is_a?(Algebra::LanguageStem) || e.is_a?(String)}
         | 
| 571 | 
            +
                        error(nil, "Exclusions must all be Language type")
         | 
| 572 | 
            +
                      end
         | 
| 573 | 
            +
                      range = Algebra::LanguageStemRange.new(:wildcard, data[:exclusion].unshift(:exclusions))
         | 
| 574 | 
            +
                    end
         | 
| 575 | 
            +
                  end
         | 
| 576 | 
            +
                  (input[:valueSetValue] ||= []) << Algebra::Value.new(range)
         | 
| 482 577 | 
             
                end
         | 
| 483 578 |  | 
| 484 | 
            -
                # [ | 
| 579 | 
            +
                # [50]    exclusion             ::= '-' (iri | literal | LANGTAG) '~'?
         | 
| 580 | 
            +
                production(:exclusion) do |input, data, callback|
         | 
| 581 | 
            +
                  (input[:exclusion] ||= []) << if data[:pattern]
         | 
| 582 | 
            +
                    case
         | 
| 583 | 
            +
                    when data[:iri] then Algebra::IriStem.new(data[:iri])
         | 
| 584 | 
            +
                    when data[:literal] then Algebra::LiteralStem.new(data[:literal])
         | 
| 585 | 
            +
                    when data[:language] then Algebra::LanguageStem.new(data[:language])
         | 
| 586 | 
            +
                    end
         | 
| 587 | 
            +
                  else
         | 
| 588 | 
            +
                    data[:iri] || data[:literal] || data[:language]
         | 
| 589 | 
            +
                  end
         | 
| 590 | 
            +
                end
         | 
| 591 | 
            +
             | 
| 592 | 
            +
                # [51]    iriRange              ::= iri ('~' iriExclusion*)?
         | 
| 485 593 | 
             
                production(:iriRange) do |input, data, callback|
         | 
| 486 594 | 
             
                  exclusions = data[:exclusion].unshift(:exclusions) if data[:exclusion]
         | 
| 487 595 | 
             
                  input[:iriRange] = if data[:pattern] && exclusions
         | 
| 488 | 
            -
                    Algebra:: | 
| 596 | 
            +
                    Algebra::IriStemRange.new(data[:iri], exclusions)
         | 
| 489 597 | 
             
                  elsif data[:pattern]
         | 
| 490 | 
            -
                    Algebra:: | 
| 598 | 
            +
                    Algebra::IriStem.new(data[:iri])
         | 
| 491 599 | 
             
                  elsif data[:dot]
         | 
| 492 | 
            -
                    Algebra:: | 
| 600 | 
            +
                    Algebra::IriStemRange.new(:wildcard, exclusions)
         | 
| 493 601 | 
             
                  else
         | 
| 494 602 | 
             
                    data[:iri]
         | 
| 495 603 | 
             
                  end
         | 
| 496 604 | 
             
                end
         | 
| 497 605 |  | 
| 498 | 
            -
                # [ | 
| 499 | 
            -
                production(: | 
| 500 | 
            -
                   | 
| 606 | 
            +
                # [52]    iriExclusion             ::= '-' iri '~'?
         | 
| 607 | 
            +
                production(:iriExclusion) do |input, data, callback|
         | 
| 608 | 
            +
                  val = data[:iri]
         | 
| 609 | 
            +
                  (input[:exclusion] ||= []) << (data[:pattern] ? Algebra::IriStem.new(val) : val)
         | 
| 610 | 
            +
                end
         | 
| 611 | 
            +
             | 
| 612 | 
            +
                # [53]    literalRange              ::= literal ('~' literalExclusion*)?
         | 
| 613 | 
            +
                production(:literalRange) do |input, data, callback|
         | 
| 614 | 
            +
                  exclusions = data[:exclusion].unshift(:exclusions) if data[:exclusion]
         | 
| 615 | 
            +
                  input[:literalRange] = if data[:pattern] && exclusions
         | 
| 616 | 
            +
                    Algebra::LiteralStemRange.new(data[:literal], exclusions)
         | 
| 617 | 
            +
                  elsif data[:pattern]
         | 
| 618 | 
            +
                    Algebra::LiteralStem.new(data[:literal])
         | 
| 619 | 
            +
                  elsif data[:dot]
         | 
| 620 | 
            +
                    Algebra::LiteralStemRange.new(:wildcard, exclusions)
         | 
| 621 | 
            +
                  else
         | 
| 622 | 
            +
                    data[:literal]
         | 
| 623 | 
            +
                  end
         | 
| 624 | 
            +
                end
         | 
| 625 | 
            +
             | 
| 626 | 
            +
                # [54]    literalExclusion             ::= '-' literal '~'?
         | 
| 627 | 
            +
                production(:literalExclusion) do |input, data, callback|
         | 
| 628 | 
            +
                  val = data[:literal]
         | 
| 629 | 
            +
                  (input[:exclusion] ||= []) << (data[:pattern] ? Algebra::LiteralStem.new(val) : val)
         | 
| 630 | 
            +
                end
         | 
| 631 | 
            +
             | 
| 632 | 
            +
                # [55]    languageRange              ::= LANGTAG ('~' languageExclusion*)?
         | 
| 633 | 
            +
                production(:languageRange) do |input, data, callback|
         | 
| 634 | 
            +
                  exclusions = data[:exclusion].unshift(:exclusions) if data[:exclusion]
         | 
| 635 | 
            +
                  input[:languageRange] = if data[:pattern] && exclusions
         | 
| 636 | 
            +
                    Algebra::LanguageStemRange.new(data[:language], exclusions)
         | 
| 637 | 
            +
                  elsif data[:pattern]
         | 
| 638 | 
            +
                    Algebra::LanguageStem.new(data[:language])
         | 
| 639 | 
            +
                  elsif data[:dot]
         | 
| 640 | 
            +
                    Algebra::LanguageStemRange.new(:wildcard, exclusions)
         | 
| 641 | 
            +
                  else
         | 
| 642 | 
            +
                    data[:language]
         | 
| 643 | 
            +
                  end
         | 
| 644 | 
            +
                end
         | 
| 645 | 
            +
             | 
| 646 | 
            +
                # [56]    languageExclusion             ::= '-' literal '~'?
         | 
| 647 | 
            +
                production(:languageExclusion) do |input, data, callback|
         | 
| 648 | 
            +
                  val = data[:language]
         | 
| 649 | 
            +
                  (input[:exclusion] ||= []) << (data[:pattern] ? Algebra::LanguageStem.new(val) : val)
         | 
| 501 650 | 
             
                end
         | 
| 502 651 |  | 
| 503 | 
            -
                # [ | 
| 652 | 
            +
                # [57]     include               ::= '&' shapeLabel
         | 
| 504 653 | 
             
                production(:include) do |input, data, callback|
         | 
| 505 654 | 
             
                  input[:tripleExpression] = data[:shapeLabel].first
         | 
| 506 655 | 
             
                end
         | 
| 507 656 |  | 
| 508 | 
            -
                # [ | 
| 657 | 
            +
                # [58]    annotation            ::= '//' predicate (iri | literal)
         | 
| 509 658 | 
             
                production(:annotation) do |input, data, callback|
         | 
| 510 659 | 
             
                  annotation = Algebra::Annotation.new([:predicate, data[:predicate].first], (data[:iri] || data[:literal]))
         | 
| 511 660 | 
             
                  (input[:annotation] ||= []) << annotation
         | 
| 512 661 | 
             
                end
         | 
| 513 662 |  | 
| 514 | 
            -
                # [ | 
| 663 | 
            +
                # [59]    semanticActions       ::= codeDecl*
         | 
| 515 664 |  | 
| 516 | 
            -
                # [ | 
| 665 | 
            +
                # [60]    codeDecl              ::= '%' iri (CODE | "%")
         | 
| 517 666 | 
             
                production(:codeDecl) do |input, data, callback|
         | 
| 518 667 | 
             
                  (input[:codeDecl] ||= []) <<  Algebra::SemAct.new(*[data[:iri], data[:code]].compact, {})
         | 
| 519 668 | 
             
                end
         | 
| 520 669 |  | 
| 521 670 | 
             
                # [13t]   literal               ::= rdfLiteral | numericLiteral | booleanLiteral
         | 
| 522 671 |  | 
| 523 | 
            -
                # [ | 
| 672 | 
            +
                # [61]    predicate             ::= iri | RDF_TYPE
         | 
| 524 673 | 
             
                production(:predicate) do |input, data, callback|
         | 
| 525 674 | 
             
                  (input[:predicate] ||= []) << data[:iri]
         | 
| 526 675 | 
             
                end
         | 
| 527 676 |  | 
| 528 | 
            -
                # [ | 
| 677 | 
            +
                # [62]    datatype              ::= iri
         | 
| 529 678 | 
             
                production(:datatype) do |input, data, callback|
         | 
| 530 679 | 
             
                  input[:datatype] = data[:iri]
         | 
| 531 680 | 
             
                end
         | 
| 532 681 |  | 
| 533 | 
            -
                # [ | 
| 682 | 
            +
                # [63]    shapeLabel            ::= iri | blankNode
         | 
| 534 683 | 
             
                production(:shapeLabel) do |input, data, callback|
         | 
| 535 684 | 
             
                  (input[:shapeLabel] ||= []) << (data[:iri] || data[:blankNode])
         | 
| 536 685 | 
             
                end
         | 
| 537 686 |  | 
| 538 687 | 
             
                # [16t]   numericLiteral        ::= INTEGER | DECIMAL | DOUBLE
         | 
| 539 | 
            -
                # [129s]  rdfLiteral            ::= string ( | 
| 688 | 
            +
                # [129s]  rdfLiteral            ::= langString | string ('^^' datatype)?
         | 
| 540 689 | 
             
                production(:rdfLiteral) do |input, data, callback|
         | 
| 541 690 | 
             
                  input[:literal] = literal(data[:string], data)
         | 
| 542 691 | 
             
                end
         | 
| @@ -544,8 +693,10 @@ module ShEx | |
| 544 693 | 
             
                # [134s]  booleanLiteral        ::= 'true' | 'false'
         | 
| 545 694 | 
             
                # [135s]  string                ::= STRING_LITERAL1 | STRING_LITERAL_LONG1
         | 
| 546 695 | 
             
                #                                 | STRING_LITERAL2 | STRING_LITERAL_LONG2
         | 
| 696 | 
            +
                # [66]   langString            ::= LANG_STRING_LITERAL1 | LANG_STRING_LITERAL_LONG1
         | 
| 697 | 
            +
                #                                | LANG_STRING_LITERAL2 | LANG_STRING_LITERAL_LONG2
         | 
| 547 698 | 
             
                # [136s]  iri                   ::= IRIREF | prefixedName
         | 
| 548 | 
            -
                # [ | 
| 699 | 
            +
                # [1372]  prefixedName          ::= PNAME_LN | PNAME_NS
         | 
| 549 700 | 
             
                # [138s]  blankNode             ::= BLANK_NODE_LABEL
         | 
| 550 701 |  | 
| 551 702 | 
             
                ##
         | 
| @@ -638,7 +789,7 @@ module ShEx | |
| 638 789 | 
             
                        when 0
         | 
| 639 790 | 
             
                          log_error(*args, depth: depth, lineno: lineno)
         | 
| 640 791 | 
             
                        when 1
         | 
| 641 | 
            -
                           | 
| 792 | 
            +
                          log_warn(*args, depth: depth, lineno: lineno)
         | 
| 642 793 | 
             
                        when 2
         | 
| 643 794 | 
             
                          log_info(*args, depth: depth, lineno: lineno)
         | 
| 644 795 | 
             
                        else
         |