Skip to content

feat(parser): support N3 has, is-of, and inverted predicates - #694

Merged
jeswr merged 4 commits into
rdfjs:mainfrom
jeswr:codex/n3-verb-keywords
Sep 1, 2026
Merged

feat(parser): support N3 has, is-of, and inverted predicates#694
jeswr merged 4 commits into
rdfjs:mainfrom
jeswr:codex/n3-verb-keywords

Conversation

@jeswr

@jeswr jeswr commented Aug 24, 2026

Copy link
Copy Markdown
Member

Implements the current N3 grammar's bare has expression, is expression of, and <- expression inverse-predicate forms.

The historical @has / @is ... @of spellings are deliberately out of scope. They are absent from the generated current grammar and collide with language-tag tokenization for literal subjects. Regression tests verify that the core forms work with literal subjects and that unambiguous uses of the historical spellings are rejected.

The inverse forms reuse the parser's existing subject/object-swapping emission. Pending of state is scoped with parser contexts, so compound predicates and nested property lists cannot leak or overwrite it. The lexer keeps keyword-like prefixes such as is:p as prefixed names, distinguishes standalone <- from IRIs such as <-s>, and waits correctly across stream chunk boundaries. Turtle and TriG remain strict.

This also fixes the previously masked inverted_properties case tracked in #698.

Tests: full Jest suite (6,865 tests, 100% coverage); ESLint; Node and browser bundle builds.

Closes #688
Closes #698

@jeswr jeswr added ai-generated Authored or prepared by an AI coding agent needs-author-review Awaiting author review; agent has done its part (author removes if follow-ups remain) labels Aug 24, 2026
@jeswr jeswr changed the title feat(parser): support N3 has and is-of verbs feat(parser): support N3 has, is-of, and inverted predicates Sep 1, 2026
@jeswr
jeswr marked this pull request as ready for review September 1, 2026 01:06
Copilot AI lite review requested due to automatic review settings September 1, 2026 01:06
@jeswr
jeswr enabled auto-merge September 1, 2026 01:07
@jeswr
jeswr disabled auto-merge September 1, 2026 01:07
@jeswr
jeswr enabled auto-merge September 1, 2026 01:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for current N3 verb/inverse predicate surface syntax by extending the lexer with N3-specific tokens and updating the parser to interpret has, is … of, and <- forms (plus regression tests for parsing/tokenization and chunk-boundary handling).

Changes:

  • Extend src/N3Lexer.js to tokenize has / is / of and the standalone <- inverted-predicate marker in N3 mode (including streaming chunk splits).
  • Extend src/N3Parser.js to parse has and is … of as verb forms and to reuse the existing subject/object swap emission for inverse predicates, while scoping pending of via parser contexts.
  • Add Jest coverage in test/N3Lexer-test.js and test/N3Parser-test.js for the new syntax and rejection of historical @has / @is … @of.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
test/N3Parser-test.js Adds parse-level tests for has, is … of, <- and error/rejection cases.
test/N3Lexer-test.js Adds lexer tokenization tests for verb keywords, <-, chunk splits, and non-N3-mode strictness.
src/N3Parser.js Implements verb/inverse predicate parsing and of expectation scoping via contexts.
src/N3Lexer.js Implements N3-mode tokenization for verb keywords and the <- marker.
Suppressed comments (1)

src/N3Parser.js:360

  • Inversion is applied when quads are emitted via _readPunctuation, but some context-tail emit paths bypass that swap. For example, when a blank node property list ends directly with ], _readBlankNodeTail emits (_subject, _predicate, _object) without checking _inversePredicate, so [ is <p> of <o> ] / [ <- <p> <o> ] would emit in the non-inverted direction. Emission sites like _readBlankNodeTail (and any similar “tail” emitters) should respect _inversePredicate the same way _readPunctuation does and clear it afterwards.
    case 'inversePredicate':
      this._inversePredicate = true;
      return this._readPredicateAfterVerb;

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/N3Lexer.js
this._boolean = /^(?:true|false)(?=[.,;!\^\s#()\[\]\{\}"'<>])/;
this._atKeyword = /^@[a-z]+(?=[\s#<:])/i;
this._keyword = /^(?:PREFIX|BASE|VERSION|GRAPH)(?=[\s#<])/i;
this._n3Verb = /^(?:has|is|of)(?=[\s#()\[\]\{\}"'<>?_+\-0-9])/;
Comment thread test/N3Lexer-test.js
Comment on lines +1080 to +1087
it(
'should keep keyword-like prefixes as prefixed names',
shouldTokenize('has:p is:p of:p',
{ type: 'prefixed', prefix: 'has', value: 'p', line: 1 },
{ type: 'prefixed', prefix: 'is', value: 'p', line: 1 },
{ type: 'prefixed', prefix: 'of', value: 'p', line: 1 },
{ type: 'eof', line: 1 }),
);
Comment thread src/N3Parser.js
Comment on lines +352 to +360
case 'has':
return this._readPredicateAfterVerb;
case 'is':
this._inversePredicate = true;
this._expectOf = true;
return this._readPredicateAfterVerb;
case 'inversePredicate':
this._inversePredicate = true;
return this._readPredicateAfterVerb;
Comment thread test/N3Parser-test.js
Comment on lines +2897 to +2901
it(
'should parse the is-of verb',
shouldParse(parser, '<s> is <p> of <o>.', ['o', 'p', 's']),
);

@jeswr
jeswr merged commit 210a1f8 into rdfjs:main Sep 1, 2026
55 checks passed
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.7.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@jeswr
jeswr deleted the codex/n3-verb-keywords branch September 1, 2026 01:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-generated Authored or prepared by an AI coding agent needs-author-review Awaiting author review; agent has done its part (author removes if follow-ups remain) released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support inverted predicates with the <- marker N3 parser does not support the has and is … of verb forms

2 participants