#encoding: utf-8
grammar N3Grammer
  # Entry point to grammar
  rule document
    statements
  end

  rule alpha
    [A-Z_a-z\p{Alpha}]
  end

  rule alphanumeric
    alpha / [0-9]
  end

  rule barename_csl
    space+ barename barename_csl_tail
    / ""
  end

  rule barename_csl_tail
    space* "," space* barename space* barename_csl_tail
    / ""
  end

  rule boolean
    "@"* ("true" / "false")
  end
  
  rule comment
    '#' (![\n\r] .)*
  end

  rule declaration
    "@"? 'keywords' barename_csl {
      def declaration; true; end
      def keywords; true;end
    }
    / "@"? 'base' space+ explicituri:explicituri {
      def declaration; true; end
      def base; true; end
    }
    / "@"? 'prefix' space+ nprefix:nprefix? ':' space* explicituri:explicituri {
      def declaration; true; end
    }
  end
  
  rule decimal
    integer '.' [0-9]+
  end
  
  rule double
    decimal [eE] integer
  end
  
  rule existential
    "@"? "forSome" space+ symbol_csl
  end
  
  rule explicituri
   "<" uri:URI_Reference ">"
  end
  
  rule expression
    pathitem space* '.' expression  
    / pathitem space* "!" space* expression
    / pathitem space* "^" space* expression { def reverse; true; end }
    / pathitem
  end

  rule barename
    alpha (alphanumeric / '-')*
  end

  rule hexdigit
    [0-9a-fA-F]
  end
  
  rule integer
     [+-]? [0-9]+
  end
  
  rule language
    [a-zA-Z]+ ( "-" [a-zA-Z0-9]+ )*
  end

  rule literal
    (string_single / string_multi) ("^^" symbol / "@" language )?
  end
  
  rule localname
    alpha (alphanumeric / '-')*
  end
  
  rule nprefix
    alpha (alphanumeric / '-')*
  end

  rule numericliteral
    double { def numericliteral; "double"; end}
    / decimal { def numericliteral; "decimal"; end}
    / integer { def numericliteral; "integer"; end}
  end
  
  rule object
    expression
  end
  
  rule object_list
    object space* "," space* object_list
    / object
  end

  rule pathitem
    boolean { def boolean; true; end }
    / literal { def literal; true; end }
    / numericliteral
    #/ quickvariable
    / symbol
    / "[" space* "]" { def anonnode; true; end }
    / "[" space* property_list space* "]" { def anonnode; true; end }
    / "{" space* statements space* "}" { def anonnode; true; end }
    / "(" space* path_list space* ")" { def anonnode; true; end }
  end
  
  rule path_list
    expression space* path_list
    / ""
  end
  
  rule prop
    expression
  end
  
  rule property_list
    verb space* object_list space* ";"+ space* property_list
    / verb space* object_list space* ";"*
    / verb space* ";"* {def object_missing; true; end}
    / ""
  end

  rule qname
    nprefix ":" localname
    / nprefix ':' { def text_value; ""; end }
    / ':' localname*
    / localname { def barename; true; end }
  end
  
  rule simpleStatement
    subject space+ property_list
    / subject # For [] and a.b
  end
  
  rule space
    [ \t\n\r]+ / comment
  end
  
  rule statement
    declaration
    / existential
    / simpleStatement
    / universal
  end
  
  rule statements
    (space / statement space* ('.' space*)? )*
  end
  
  # " constant-value-with-escaping "
  rule string_single
    '""' !["] / '"' string_single_char+ '"'
  end

  rule string_single_char
    !["\n\r] (
      ("\\"
        [\\\"bfnrt]
      / ( "u" hexdigit hexdigit hexdigit hexdigit )
      / ( "U" "00" hexdigit hexdigit hexdigit hexdigit hexdigit hexdigit)
      )
    / .)
  end

  # """ constant value with escaping including single or double occurrences of quotes and/or newlines """ 
  rule string_multi
    '"""' string_multi_single_char* '"""'
  end

  rule string_multi_single_char
    "\\\""
    / !('"""') .
  end

  rule subject
    expression
  end
  
  rule symbol
    qname / explicituri
  end
  
  rule symbol_csl
    symbol space* "," space* symbol_csl
    / symbol
  end
  
  rule verb
    "@"? "has" space+ prop                    # has xxx
    / "@"? "is" space+ prop space+ "@"? "of" { # is xxx of
      def invert; true; end
    }
    / "@"? "a" !":"                           # has rdf:type
    / "=>"                                    # has log:implies
    / "<=" { def invert; true; end }          # is log:implies of
    / "="                                     # has owl:sameAs
    / prop                                    # has xxx of -- shorthand
  end
  
  rule universal
    "@"? "forAll" space+ symbol_csl
  end
  
  rule URI_Reference
    [^{}<>]*
  end
  
end