).
+ */
+ function waitForSaveDialog() {
+ cy.get('#open-dialog').closest('.eXide-dialog', { timeout: 5000 })
+ .should('have.attr', 'open')
+ }
+
+ function saveDialogShouldBeClosed() {
+ cy.get('#open-dialog').closest('.eXide-dialog')
+ .should('not.have.attr', 'open')
+ }
+
+ function clickDialogButton(label) {
+ cy.get('#open-dialog').closest('.eXide-dialog')
+ .find('.eXide-dialog-buttons button').contains(label).click()
+ }
+
+ function openFileDirectly(path) {
+ var name = path.split('/').pop()
+ cy.window().then((win) => {
+ win.eXide.app.$doOpenDocument({ path: path, name: name })
+ })
+ }
+
+ after(() => {
+ cy.cleanupTestFiles()
+ })
+
+ it('opens the save dialog for a new document', () => {
+ setEditorContent('1 + 1')
+ cy.get('#save').click()
+
+ waitForSaveDialog()
+ cy.get('#open-dialog input[name="resource"]').should('exist')
+
+ // Close without saving
+ clickDialogButton('Cancel')
+ saveDialogShouldBeClosed()
+ })
+
+ it('saves a new XQuery file to the database', () => {
+ setEditorContent('(: test file :)\n1 + 1')
+ cy.get('#save').click()
+
+ waitForSaveDialog()
+
+ // The dialog starts in /db, type filename and save
+ cy.get('#open-dialog input[name="resource"]').clear().type(testFile)
+ clickDialogButton('Save')
+
+ // Dialog should close and path should update
+ saveDialogShouldBeClosed()
+ cy.get('.path', { timeout: 5000 }).should('contain', testFile)
+ })
+
+ it('saves an existing document without opening dialog', () => {
+ // Open the file we saved in the previous test directly
+ openFileDirectly(testCollection + '/' + testFile)
+ cy.get('.path', { timeout: 10000 }).should('contain', testFile)
+
+ // Modify the content
+ setEditorContent('(: modified :)\n2 + 2')
+
+ // Save — should NOT open dialog since file already exists
+ cy.get('#save').click()
+
+ // Toast should confirm save
+ cy.get('.eXide-toast', { timeout: 5000 }).should('contain', 'stored')
+
+ // Dialog should not have opened
+ saveDialogShouldBeClosed()
+ })
+
+ it('warns when saving XQuery with non-XQuery extension', () => {
+ setEditorContent('1 + 1')
+ cy.get('#save').click()
+
+ waitForSaveDialog()
+
+ // Try to save with .txt extension
+ cy.get('#open-dialog input[name="resource"]').clear().type('bad-name.txt')
+ clickDialogButton('Save')
+
+ // Warning dialog should appear about wrong extension
+ cy.get('.eXide-dialog[open]', { timeout: 5000 })
+ .should('contain', 'non-XQuery file extension')
+ })
+
+ it('can open a saved file and verify content', () => {
+ // Open the file we saved and modified earlier
+ openFileDirectly(testCollection + '/' + testFile)
+ cy.get('.path', { timeout: 10000 }).should('contain', testFile)
+
+ // Verify the document was loaded with the modified content
+ cy.window().then((win) => {
+ var text = win.eXide.app.getEditor().getActiveDocument().getText()
+ expect(text).to.contain('modified')
+ })
+ })
+})
diff --git a/cypress/e2e/find_replace_spec.cy.js b/cypress/e2e/find_replace_spec.cy.js
new file mode 100644
index 00000000..5d2453b1
--- /dev/null
+++ b/cypress/e2e/find_replace_spec.cy.js
@@ -0,0 +1,89 @@
+describe('Find and Replace', () => {
+ beforeEach(() => {
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ })
+
+ function setEditorContent(text) {
+ cy.window().then((win) => {
+ var doc = win.eXide.app.getEditor().getActiveDocument()
+ doc.setText(text)
+ })
+ }
+
+ function getEditorContent() {
+ return cy.window().then((win) => {
+ return win.eXide.app.getEditor().getActiveDocument().getText()
+ })
+ }
+
+ it('opens find panel via menu', () => {
+ cy.get('#menu-edit-find').click({ force: true })
+ // CM6 search panel should appear
+ cy.get('.cm-search').should('be.visible')
+ })
+
+ it('finds text in document', () => {
+ setEditorContent('let $x := 1\nlet $y := 2\nreturn $x + $y')
+ cy.get('#menu-edit-find').click({ force: true })
+ cy.get('.cm-search').should('be.visible')
+
+ // Type search query
+ cy.get('.cm-search input[name="search"]').clear().type('$x')
+
+ // Should show match count or highlight matches
+ cy.get('.cm-editor .cm-searchMatch').should('have.length.at.least', 1)
+ })
+
+ it('navigates between matches', () => {
+ setEditorContent('apple banana apple cherry apple')
+ cy.get('#menu-edit-find').click({ force: true })
+ cy.get('.cm-search input[name="search"]').clear().type('apple')
+
+ // Multiple matches should be highlighted
+ cy.get('.cm-editor .cm-searchMatch').should('have.length.at.least', 2)
+
+ // Click next to navigate
+ cy.get('.cm-search button[name="next"]').click()
+ })
+
+ it('opens find-replace panel via menu', () => {
+ cy.get('#menu-edit-find-replace').click({ force: true })
+ cy.get('.cm-search').should('be.visible')
+ // Replace input should be visible
+ cy.get('.cm-search input[name="replace"]').should('be.visible')
+ })
+
+ it('replaces text', () => {
+ setEditorContent('hello world hello')
+ cy.get('#menu-edit-find-replace').click({ force: true })
+
+ cy.get('.cm-search input[name="search"]').clear().type('hello')
+ cy.get('.cm-search input[name="replace"]').clear().type('goodbye')
+
+ // Click replace all
+ cy.get('.cm-search button[name="replaceAll"]').click()
+
+ getEditorContent().should('contain', 'goodbye').and('not.contain', 'hello')
+ })
+
+ it('closes search panel with Escape', () => {
+ cy.get('#menu-edit-find').click({ force: true })
+ cy.get('.cm-search').should('be.visible')
+ cy.get('.cm-search input[name="search"]').type('{esc}')
+ cy.get('.cm-search').should('not.exist')
+ })
+
+ it('case-sensitive search', () => {
+ setEditorContent('Hello hello HELLO')
+ cy.get('#menu-edit-find').click({ force: true })
+ cy.get('.cm-search input[name="search"]').clear().type('Hello')
+
+ // Toggle case sensitivity (checkbox labeled "match case")
+ cy.get('.cm-search input[name="case"]').check({ force: true })
+
+ // Should match only exact case
+ cy.get('.cm-editor .cm-searchMatch').should('have.length', 1)
+ })
+})
diff --git a/cypress/e2e/goto_references_spec.cy.js b/cypress/e2e/goto_references_spec.cy.js
new file mode 100644
index 00000000..39c66b80
--- /dev/null
+++ b/cypress/e2e/goto_references_spec.cy.js
@@ -0,0 +1,88 @@
+describe('Go-to-definition and Find References', () => {
+ beforeEach(() => {
+ cy.loginXHR('admin', '')
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ cy.get('#user', { timeout: 15000 }).should('not.have.text', 'Login')
+ })
+
+ function setEditorContent(text, cursorPos) {
+ cy.window().then((win) => {
+ var editor = win.eXide.app.getEditor()
+ var view = editor.editor
+ var doc = editor.getActiveDocument()
+ var anchor = cursorPos !== undefined ? cursorPos : 0
+ editor.validator.setEnabled(false)
+ view.dispatch({
+ changes: { from: 0, to: view.state.doc.length, insert: text },
+ selection: { anchor: anchor }
+ })
+ doc.lastValidation = 0
+ doc.getModeHelper().parseXQuery(doc)
+ view.focus()
+ editor.validator.setEnabled(true)
+ })
+ }
+
+ describe('Go-to-definition', () => {
+ it('jumps to function declaration via F3', () => {
+ var code = 'declare function local:greet($name) {\n "Hello " || $name\n};\n\nlocal:greet("world")'
+ // Place cursor on "local:greet" call at line 5 (offset ~62)
+ setEditorContent(code, 62)
+
+ cy.window().then((win) => {
+ win.eXide.app.getEditor().exec('gotoDefinition')
+ })
+
+ // Cursor should move to the function declaration (line 1)
+ cy.get('#status-cursor', { timeout: 5000 })
+ .invoke('text')
+ .should('contain', 'Ln 1')
+ })
+
+ it('adds cm-goto-clickable class when Cmd/Ctrl is held', () => {
+ setEditorContent('let $x := 1 return $x')
+
+ // The cm-content should get clickable class on modifier key
+ cy.get('.cm-content').trigger('keydown', { metaKey: true, key: 'Meta' })
+ cy.get('.cm-content.cm-goto-clickable, .cm-editor .cm-goto-clickable')
+ .should('exist')
+
+ cy.get('.cm-content').trigger('keyup', { key: 'Meta' })
+ })
+ })
+
+ describe('Find All References', () => {
+ it('opens QuickPicker with references via findReferences command', () => {
+ // Use a function call — references work best on function names
+ var code = 'declare function local:greet($name) {\n "Hello " || $name\n};\n\nlocal:greet("a"),\nlocal:greet("b")'
+ // Place cursor on "local:greet" in the call at line 5 (after the newlines)
+ setEditorContent(code, 56)
+
+ cy.window().then((win) => {
+ win.eXide.app.getEditor().exec('findReferences')
+ })
+
+ cy.get('.quick-picker', { timeout: 10000 })
+ .should('be.visible')
+
+ // Should show at least one reference
+ cy.get('.quick-picker-list li')
+ .should('have.length.at.least', 1)
+ })
+
+ it('closes QuickPicker on Escape', () => {
+ var code = 'declare function local:greet($name) {\n "Hello " || $name\n};\n\nlocal:greet("a"),\nlocal:greet("b")'
+ setEditorContent(code, 56)
+
+ cy.window().then((win) => {
+ win.eXide.app.getEditor().exec('findReferences')
+ })
+
+ cy.get('.quick-picker', { timeout: 10000 }).should('be.visible')
+ cy.get('.quick-picker-filter').type('{esc}')
+ cy.get('.quick-picker').should('not.be.visible')
+ })
+ })
+})
diff --git a/cypress/e2e/layout_spec.cy.js b/cypress/e2e/layout_spec.cy.js
new file mode 100644
index 00000000..ee0b9578
--- /dev/null
+++ b/cypress/e2e/layout_spec.cy.js
@@ -0,0 +1,56 @@
+describe('Layout fills viewport', () => {
+ beforeEach(() => {
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ })
+
+ it('layout-horizontal extends to the bottom of the viewport', () => {
+ cy.window().then((win) => {
+ const viewportHeight = win.innerHeight
+ cy.get('.layout-horizontal').then(($el) => {
+ const rect = $el[0].getBoundingClientRect()
+ expect(rect.bottom).to.be.closeTo(viewportHeight, 2)
+ })
+ })
+ })
+
+ it('west panel tabs align with editor tabs in light mode', () => {
+ cy.get('#tabs-outline-container').then(($west) => {
+ cy.get('#tabs-container').then(($editor) => {
+ const westTop = $west[0].getBoundingClientRect().top
+ const editorTop = $editor[0].getBoundingClientRect().top
+ expect(westTop).to.be.closeTo(editorTop, 1)
+ })
+ })
+ })
+
+ it('west panel tabs align with editor tabs in dark mode', () => {
+ // Switch to dark mode
+ cy.get('#toggle-dark-mode').click()
+ // Wait for theme to apply
+ cy.get('body').should('have.class', 'dark')
+ // Small wait for any reflow
+
+ cy.get('#tabs-outline-container').then(($west) => {
+ cy.get('#tabs-container').then(($editor) => {
+ const westTop = $west[0].getBoundingClientRect().top
+ const editorTop = $editor[0].getBoundingClientRect().top
+ expect(westTop).to.be.closeTo(editorTop, 1)
+ })
+ })
+ })
+
+ it('layout fills viewport in dark mode', () => {
+ cy.get('#toggle-dark-mode').click()
+ cy.get('body').should('have.class', 'dark')
+
+ cy.window().then((win) => {
+ const viewportHeight = win.innerHeight
+ cy.get('.layout-horizontal').then(($el) => {
+ const rect = $el[0].getBoundingClientRect()
+ expect(rect.bottom).to.be.closeTo(viewportHeight, 2)
+ })
+ })
+ })
+})
diff --git a/cypress/e2e/local_files_spec.cy.js b/cypress/e2e/local_files_spec.cy.js
new file mode 100644
index 00000000..9d18a924
--- /dev/null
+++ b/cypress/e2e/local_files_spec.cy.js
@@ -0,0 +1,161 @@
+describe('Local files pane (desktop simulation)', () => {
+ beforeEach(() => {
+ cy.loginXHR('admin', '')
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.cm-editor', { timeout: 10000 }).should('exist')
+ cy.get('#user', { timeout: 15000 }).should('not.have.text', 'Login')
+ })
+
+ function injectLocalFiles() {
+ return cy.readFile('src-tauri/local-files.js').then((script) => {
+ cy.window().then((win) => {
+ win.eval(script)
+ })
+ })
+ }
+
+ function injectWithFolder(folderPath, entries) {
+ return cy.readFile('src-tauri/local-files.js').then((script) => {
+ cy.window().then((win) => {
+ win.eval(script)
+ if (win.__exideLocalFiles) {
+ win.__exideLocalFiles.openFolderWithData(folderPath, entries)
+ }
+ })
+ })
+ }
+
+ var mockEntries = [
+ { name: 'src', path: '/mock/project/src', is_dir: true, children: [
+ { name: 'app.xq', path: '/mock/project/src/app.xq', is_dir: false, children: null },
+ { name: 'lib.xqm', path: '/mock/project/src/lib.xqm', is_dir: false, children: null }
+ ]},
+ { name: 'test', path: '/mock/project/test', is_dir: true, children: [] },
+ { name: 'README.md', path: '/mock/project/README.md', is_dir: false, children: null },
+ { name: 'pom.xml', path: '/mock/project/pom.xml', is_dir: false, children: null }
+ ]
+
+ it('injects LOCAL tab into the tab bar', () => {
+ injectLocalFiles()
+
+ cy.get('#tabs-outline li').should('have.length.at.least', 3)
+ cy.get('#tabs-outline').invoke('text').should('contain', 'local')
+ })
+
+ it('shows local pane when LOCAL tab is clicked', () => {
+ injectWithFolder('/mock/project', mockEntries)
+
+ cy.get('#local-files-body').should('be.visible')
+ })
+
+ it('renders directory tree with folders and files', () => {
+ injectWithFolder('/mock/project', mockEntries)
+
+ // Check top-level entries only (direct children of #local-directory)
+ cy.get('#local-directory > li.collection').should('have.length', 2)
+ cy.get('#local-directory > li.resource').should('have.length', 2)
+ cy.get('#local-directory > li.collection span').first().should('contain', 'src')
+ cy.get('#local-directory > li.resource span').first().should('contain', 'README.md')
+ })
+
+ it('expands and collapses folders on click', () => {
+ injectWithFolder('/mock/project', mockEntries)
+
+ // src folder's children should be hidden initially
+ cy.get('#local-directory > li.collection').first().find('> ul').should('not.be.visible')
+
+ // Click the folder's label span to expand
+ cy.get('#local-directory > li.collection').first().find('> span').click()
+ cy.get('#local-directory > li.collection').first().find('> ul').should('be.visible')
+ cy.get('#local-directory > li.collection').first().find('> ul > li.resource').should('have.length', 2)
+
+ // Click the folder's label span again to collapse
+ cy.get('#local-directory > li.collection').first().find('> span').click()
+ cy.get('#local-directory > li.collection').first().find('> ul').should('not.be.visible')
+ })
+
+ it('switches between COLLECTIONS and LOCAL tabs', () => {
+ injectWithFolder('/mock/project', mockEntries)
+
+ // LOCAL should be visible
+ cy.get('#local-files-body').should('be.visible')
+
+ // Click COLLECTIONS
+ cy.get('#tabs-outline li').first().click()
+ cy.get('#local-files-body').should('not.be.visible')
+
+ // Click LOCAL again
+ cy.get('#tabs-outline').contains('local').click()
+ cy.get('#local-files-body').should('be.visible')
+ })
+
+ it('filters local files', () => {
+ injectWithFolder('/mock/project', mockEntries)
+
+ cy.get('#local-filter').type('README')
+
+ // Only README.md should be visible at root level
+ cy.get('#local-directory > li.resource').filter(':visible').should('have.length', 1)
+ cy.get('#local-directory > li.resource').filter(':visible').invoke('text').should('contain', 'README')
+ })
+
+ it('LOCAL tab shows active state when selected', () => {
+ injectWithFolder('/mock/project', mockEntries)
+
+ // LOCAL tab should be active
+ cy.get('#tabs-outline').contains('a.tab', 'local')
+ .should('have.class', 'active')
+
+ // Other tabs should not be active
+ cy.get('#tabs-outline').contains('a.tab', 'collections')
+ .should('not.have.class', 'active')
+ })
+
+ it('LOCAL tab loses active when switching to COLLECTIONS', () => {
+ injectWithFolder('/mock/project', mockEntries)
+
+ // Click COLLECTIONS
+ cy.get('#tabs-outline').contains('a.tab', 'collections').click()
+
+ // LOCAL should no longer be active
+ cy.get('#tabs-outline').contains('a.tab', 'local')
+ .should('not.have.class', 'active')
+
+ // COLLECTIONS should be active
+ cy.get('#tabs-outline').contains('a.tab', 'collections')
+ .should('have.class', 'active')
+ })
+
+ it('all three tabs cycle active state correctly', () => {
+ injectWithFolder('/mock/project', mockEntries)
+
+ // LOCAL is active
+ cy.get('#tabs-outline').contains('a.tab', 'local').should('have.class', 'active')
+
+ // Switch to COLLECTIONS
+ cy.get('#tabs-outline').contains('a.tab', 'collections').click()
+ cy.get('#tabs-outline').contains('a.tab', 'collections').should('have.class', 'active')
+ cy.get('#tabs-outline').contains('a.tab', 'local').should('not.have.class', 'active')
+ cy.get('#tabs-outline').contains('a.tab', 'outline').should('not.have.class', 'active')
+
+ // Switch to OUTLINE
+ cy.get('#tabs-outline').contains('a.tab', 'outline').click()
+ cy.get('#tabs-outline').contains('a.tab', 'outline').should('have.class', 'active')
+ cy.get('#tabs-outline').contains('a.tab', 'collections').should('not.have.class', 'active')
+ cy.get('#tabs-outline').contains('a.tab', 'local').should('not.have.class', 'active')
+
+ // Switch back to LOCAL
+ cy.get('#tabs-outline').contains('a.tab', 'local').click()
+ cy.get('#tabs-outline').contains('a.tab', 'local').should('have.class', 'active')
+ cy.get('#tabs-outline').contains('a.tab', 'outline').should('not.have.class', 'active')
+ cy.get('#tabs-outline').contains('a.tab', 'collections').should('not.have.class', 'active')
+ })
+
+ it('has correct folder icon classes', () => {
+ injectWithFolder('/mock/project', mockEntries)
+
+ cy.get('#local-directory li.collection i').first().should('have.class', 'fa-folder')
+ cy.get('#local-directory li.resource i').first().should('have.class', 'fa-file-o')
+ })
+})
diff --git a/cypress/e2e/lsp/diagnostics_panel_spec.cy.js b/cypress/e2e/lsp/diagnostics_panel_spec.cy.js
new file mode 100644
index 00000000..b1255605
--- /dev/null
+++ b/cypress/e2e/lsp/diagnostics_panel_spec.cy.js
@@ -0,0 +1,88 @@
+describe('Diagnostics panel', () => {
+ beforeEach(() => {
+ cy.intercept('POST', '**/api/query/compile').as('compile')
+ cy.loginXHR('admin', '')
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ cy.get('#user', { timeout: 15000 }).should('not.have.text', 'Login')
+ // Wait for initial compile to settle
+ cy.wait('@compile', { timeout: 10000 })
+ })
+
+ function setEditorContent(text) {
+ cy.window().then((win) => {
+ var view = win.eXide.app.getEditor().editor
+ view.dispatch({
+ changes: { from: 0, to: view.state.doc.length, insert: text }
+ })
+ view.focus()
+ })
+ }
+
+ it('opens lint panel via Navigate menu', () => {
+ cy.get('.cm-panel-lint').should('not.exist')
+
+ cy.get('#menu-navigate-diagnostics').click({ force: true })
+
+ cy.get('.cm-panel-lint', { timeout: 3000 }).should('be.visible')
+ })
+
+ it('toggles panel closed on second click', () => {
+ cy.get('#menu-navigate-diagnostics').click({ force: true })
+ cy.get('.cm-panel-lint', { timeout: 3000 }).should('be.visible')
+
+ cy.get('#menu-navigate-diagnostics').click({ force: true })
+ cy.get('.cm-panel-lint').should('not.exist')
+ })
+
+ it('shows diagnostics for invalid XQuery', () => {
+ setEditorContent('declare function local:test() {\n $undefined\n};')
+ // Wait for validation
+ cy.wait('@compile', { timeout: 10000 })
+ cy.wait(500)
+
+ cy.get('#menu-navigate-diagnostics').click({ force: true })
+ cy.get('.cm-panel-lint', { timeout: 3000 }).should('be.visible')
+
+ // Lint panel should list at least one diagnostic
+ cy.get('.cm-panel-lint li').should('have.length.at.least', 1)
+ })
+
+ it('clears diagnostics when code is fixed', () => {
+ // First introduce an error
+ setEditorContent('declare function local:test() {\n $undefined\n};')
+ cy.wait('@compile', { timeout: 10000 })
+ cy.wait(500)
+
+ // Open panel and verify error exists
+ cy.get('#menu-navigate-diagnostics').click({ force: true })
+ cy.get('.cm-panel-lint', { timeout: 3000 }).should('be.visible')
+ cy.get('.cm-panel-lint li').should('have.length.at.least', 1)
+
+ // Fix the code
+ setEditorContent('1 + 1')
+ cy.wait('@compile', { timeout: 10000 })
+ cy.wait(500)
+
+ // Diagnostics should be cleared — panel shows "No diagnostics" text
+ cy.get('.cm-panel-lint').invoke('text').should('contain', 'No diagnostics')
+ })
+
+ it('error clearing: error pill disappears when code is fixed', () => {
+ // Trigger an error by setting invalid content
+ setEditorContent('declare function local:broken() { $x };\nlocal:broken()')
+ // Wait for compile to return the error
+ cy.wait('@compile', { timeout: 10000 })
+ cy.wait(500)
+
+ // Verify error appears
+ cy.get('#exide-err-pill', { timeout: 5000 }).should('have.class', 'has-error')
+
+ // Fix the error with valid code
+ setEditorContent('1 + 1')
+
+ // The error pill should eventually clear after re-validation
+ cy.get('#exide-err-pill', { timeout: 15000 }).should('not.have.class', 'has-error')
+ })
+})
diff --git a/cypress/e2e/lsp/error_status_spec.cy.js b/cypress/e2e/lsp/error_status_spec.cy.js
new file mode 100644
index 00000000..50f9147c
--- /dev/null
+++ b/cypress/e2e/lsp/error_status_spec.cy.js
@@ -0,0 +1,134 @@
+describe('Error status UI', () => {
+ /**
+ * Simulate eXide's editor.updateStatus() by writing to #error-status.
+ * Uses the same textContent approach the real code uses, then waits
+ * a tick for the MutationObserver callback to fire.
+ */
+ function setError(msg) {
+ cy.window().then((win) => {
+ const el = win.document.getElementById('error-status')
+ el.textContent = msg
+ })
+ // Give the MutationObserver microtask a chance to run
+ cy.wait(50)
+ }
+
+ /**
+ * Wait for eXide to finish its initial compilation cycle.
+ * The editor compiles the default document on load, which writes to
+ * #error-status. We need to wait for that to settle before testing.
+ */
+ function waitForInitialLoad() {
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ // Wait for the initial compilation cycle to finish
+ cy.wait('@compile', { timeout: 10000 })
+ // Clear any existing error state before each test
+ cy.window().then((win) => {
+ const el = win.document.getElementById('error-status')
+ el.textContent = ''
+ })
+ cy.wait(50)
+ }
+
+ beforeEach(() => {
+ cy.intercept('POST', '**/api/query/compile').as('compile')
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ waitForInitialLoad()
+ })
+
+ it('pill and panel are hidden when there is no error', () => {
+ cy.get('#exide-err-pill').should('not.have.class', 'has-error')
+ cy.get('#exide-err-panel').should('not.have.class', 'ep-open')
+ })
+
+ it('shows pill and panel when an error is written to #error-status', () => {
+ setError('err:XPST0017 Call to undeclared function [at line 5, column 12]')
+
+ cy.get('#exide-err-pill')
+ .should('have.class', 'has-error')
+ .and('be.visible')
+
+ cy.get('#exide-err-pill-label')
+ .invoke('text')
+ .should('have.length.greaterThan', 0)
+
+ cy.get('#exide-err-panel')
+ .should('have.class', 'ep-open')
+ .and('be.visible')
+
+ cy.get('#exide-err-panel-body')
+ .should('contain', 'XPST0017')
+ })
+
+ it('toggles panel closed and open when clicking the pill', () => {
+ setError('err:XPST0003 Unexpected token [at line 1, column 1]')
+
+ cy.get('#exide-err-panel').should('have.class', 'ep-open')
+
+ cy.get('#exide-err-pill').click()
+ cy.get('#exide-err-panel').should('not.have.class', 'ep-open')
+
+ cy.get('#exide-err-pill').click()
+ cy.get('#exide-err-panel').should('have.class', 'ep-open')
+ })
+
+ it('closes panel when clicking the close button', () => {
+ setError('Cannot compile xquery: syntax error')
+
+ cy.get('#exide-err-panel').should('have.class', 'ep-open')
+ cy.get('#exide-err-panel-close').click()
+ cy.get('#exide-err-panel').should('not.have.class', 'ep-open')
+ })
+
+ it('closes panel on Escape key', () => {
+ setError('err:XPST0003 Unexpected end of input')
+
+ cy.get('#exide-err-panel').should('have.class', 'ep-open')
+ cy.get('body').type('{esc}')
+ cy.get('#exide-err-panel').should('not.have.class', 'ep-open')
+ })
+
+ it('hides pill and panel when error is cleared', () => {
+ setError('err:XPST0003 Some error')
+
+ cy.get('#exide-err-pill').should('have.class', 'has-error')
+ cy.get('#exide-err-panel').should('have.class', 'ep-open')
+
+ setError('')
+
+ cy.get('#exide-err-pill').should('not.have.class', 'has-error')
+ cy.get('#exide-err-panel').should('not.have.class', 'ep-open')
+ })
+
+ it('clears error when switching to a different tab', () => {
+ setError('err:XPST0003 Some error in first tab')
+
+ cy.get('#exide-err-pill').should('have.class', 'has-error')
+ cy.get('#exide-err-panel').should('have.class', 'ep-open')
+
+ // Create a new tab via the New XQuery button
+ cy.get('#new-xquery').click()
+ // Confirm we switched to a new tab
+ cy.get('.path').should('contain', 'untitled-2')
+
+ // Error should be cleared
+ cy.get('#exide-err-pill').should('not.have.class', 'has-error')
+ cy.get('#exide-err-panel').should('not.have.class', 'ep-open')
+ })
+
+ it('copy button copies error text to clipboard', () => {
+ const errorMsg = 'err:XPST0017 Call to undeclared function: local:foo'
+
+ // Stub clipboard before triggering the error
+ cy.window().then((win) => {
+ cy.stub(win.navigator.clipboard, 'writeText').as('clipWrite').resolves()
+ })
+
+ setError(errorMsg)
+ cy.get('#exide-err-panel').should('have.class', 'ep-open')
+
+ cy.get('#exide-err-panel-copy').click()
+ cy.get('@clipWrite').should('have.been.calledWith', errorMsg)
+ })
+})
diff --git a/cypress/e2e/monitor_spec.cy.js b/cypress/e2e/monitor_spec.cy.js
new file mode 100644
index 00000000..e97d2958
--- /dev/null
+++ b/cypress/e2e/monitor_spec.cy.js
@@ -0,0 +1,108 @@
+describe('Monitor panel', () => {
+ beforeEach(() => {
+ cy.loginXHR('admin', '')
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ cy.get('#user', { timeout: 10000 }).should('not.have.text', 'Login')
+ })
+
+ it('token endpoint returns server status for admin user', () => {
+ cy.window().then((win) => {
+ return win.fetch('api/admin/status')
+ .then((r) => r.json())
+ }).then((data) => {
+ expect(data).to.have.property('version')
+ })
+ })
+
+ it('app.login.isAdmin is true after login', () => {
+ cy.window().then((win) => {
+ expect(win.eXide.app.login).to.not.be.null
+ expect(win.eXide.app.login.isAdmin).to.be.true
+ })
+ })
+
+ it('starts polling when toggled open by admin', () => {
+ cy.get('#toggle-monitor').click()
+ cy.get('.panel-east').should('be.visible')
+ cy.get('.mon-title').should('contain', 'MONITOR')
+
+ cy.get('#mon-mem-nums', { timeout: 15000 }).invoke('text').should('match', /\d+ \/ \d+ MB/)
+ })
+
+ it('updates memory bar on poll', () => {
+ cy.get('#toggle-monitor').click()
+ cy.get('.panel-east').should('be.visible')
+
+ cy.get('#mon-mem-used', { timeout: 15000 }).invoke('css', 'width').should('not.eq', '0px')
+ })
+
+ it('shows uptime after polling', () => {
+ cy.get('#toggle-monitor').click()
+
+ cy.get('#mon-chip-uptime', { timeout: 15000 }).invoke('text').should('match', /Up .+/)
+ })
+
+ it('stops polling when toggled closed', () => {
+ cy.intercept('POST', '**/api/ws/monitor').as('wsPoll')
+
+ cy.get('#toggle-monitor').click()
+ // Wait until at least one poll has fired (data is visible)
+ cy.get('#mon-mem-nums', { timeout: 15000 }).invoke('text').should('match', /\d+ \/ \d+ MB/)
+
+ cy.get('#toggle-monitor').click()
+ cy.get('.panel-east').should('not.be.visible')
+
+ cy.get('@wsPoll.all').then((interceptions) => {
+ var count = interceptions.length
+ // Need a real wait here — we're asserting nothing NEW happens
+ cy.wait(3000)
+ cy.get('@wsPoll.all').should('have.length', count)
+ })
+ })
+
+ it('shows running query and allows killing it', () => {
+ cy.get('#toggle-monitor').click()
+ cy.get('.panel-east').should('be.visible')
+ // Wait for first poll to complete before running query
+ cy.get('#mon-mem-nums', { timeout: 15000 }).invoke('text').should('match', /\d+ \/ \d+ MB/)
+
+ // Run a slow query in the background via the editor
+ cy.window().then((win) => {
+ var editor = win.eXide.app.getEditor()
+ var view = editor.editor
+ view.dispatch({
+ changes: { from: 0, to: view.state.doc.length, insert: 'util:wait(10000)' }
+ })
+ })
+
+ // Click Run to start the query
+ cy.get('#run').click()
+
+ // Wait for the running query to appear in the monitor
+ cy.get('#mon-running-body .mon-running', { timeout: 5000 })
+ .should('have.length.at.least', 1)
+
+ // Click the kill button
+ cy.get('#mon-running-body .mon-kill').first().click()
+
+ // Wait for the query to disappear after the next poll cycle
+ cy.get('#mon-running-body .mon-empty', { timeout: 15000 })
+ .should('exist')
+ })
+
+ it('restores monitor on reload if it was open', () => {
+ cy.get('#toggle-monitor').click()
+ // Ensure monitor is actually running before reload
+ cy.get('#mon-mem-nums', { timeout: 15000 }).invoke('text').should('match', /\d+ \/ \d+ MB/)
+
+ cy.reload(true)
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ cy.get('#user', { timeout: 10000 }).should('not.have.text', 'Login')
+
+ // Monitor panel should be visible again with data
+ cy.get('.panel-east', { timeout: 5000 }).should('be.visible')
+ cy.get('#mon-mem-nums', { timeout: 15000 }).invoke('text').should('match', /\d+ \/ \d+ MB/)
+ })
+})
diff --git a/cypress/e2e/native_autocomplete_spec.cy.js b/cypress/e2e/native_autocomplete_spec.cy.js
new file mode 100644
index 00000000..e25ad796
--- /dev/null
+++ b/cypress/e2e/native_autocomplete_spec.cy.js
@@ -0,0 +1,119 @@
+describe('Native autocomplete for non-XQuery modes', () => {
+ beforeEach(() => {
+ cy.loginXHR('admin', '')
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.cm-editor', { timeout: 10000 }).should('exist')
+ cy.get('#user', { timeout: 15000 }).should('not.have.text', 'Login')
+ })
+
+ /**
+ * Create a new document with the given file type and content.
+ * Types are: html, css, javascript, json, xml, less, markdown
+ */
+ function newDocument(type, content) {
+ cy.window().then((win) => {
+ var editor = win.eXide.app.getEditor()
+ editor.newDocument(null, type)
+ })
+ cy.wait(300)
+ if (content) {
+ cy.window().then((win) => {
+ var view = win.eXide.app.getEditor().editor
+ view.dispatch({
+ changes: { from: 0, to: view.state.doc.length, insert: content },
+ selection: { anchor: content.length }
+ })
+ view.focus()
+ })
+ }
+ }
+
+ function triggerCompletion() {
+ cy.window().then((win) => {
+ var view = win.eXide.app.getEditor().editor
+ view.focus()
+ win.CM6.startCompletion(view)
+ })
+ }
+
+ function typeInEditor(text) {
+ cy.get('.cm-content').type(text, { delay: 50 })
+ }
+
+ describe('HTML completions', () => {
+ it('shows tag completions when typing <', () => {
+ newDocument('html', '')
+ typeInEditor('
{
+ newDocument('html', ' {
+ it('shows property completions inside a rule', () => {
+ newDocument('css', 'body {\n co')
+ triggerCompletion()
+
+ cy.get('.cm-tooltip-autocomplete', { timeout: 5000 })
+ .should('be.visible')
+ .invoke('text')
+ .should('contain', 'color')
+ })
+
+ it('shows multiple property suggestions', () => {
+ newDocument('css', 'body {\n b')
+ triggerCompletion()
+
+ cy.get('.cm-tooltip-autocomplete', { timeout: 5000 })
+ .should('be.visible')
+ .invoke('text')
+ .should('match', /background|border|bottom/)
+ })
+ })
+
+ describe('JavaScript completions', () => {
+ it('shows local variable completions', () => {
+ newDocument('javascript', 'var myLongVariable = 42;\nmy')
+ triggerCompletion()
+
+ cy.get('.cm-tooltip-autocomplete', { timeout: 5000 })
+ .should('be.visible')
+ .invoke('text')
+ .should('contain', 'myLongVariable')
+ })
+ })
+
+ describe('JSON linting', () => {
+ it('shows parse error for invalid JSON', () => {
+ newDocument('json', '{ foo: bar }')
+ cy.wait(500)
+
+ // Lint gutter should show an error marker
+ cy.get('.cm-lint-marker-error', { timeout: 5000 })
+ .should('have.length.at.least', 1)
+ })
+
+ it('no errors for valid JSON', () => {
+ newDocument('json', '{\n "name": "test",\n "version": "1.0"\n}')
+ cy.wait(500)
+
+ cy.get('.cm-lint-marker-error').should('have.length', 0)
+ })
+ })
+
+})
diff --git a/cypress/e2e/navbar.spec.cy.js b/cypress/e2e/navbar.spec.cy.js
index 4134abe0..398dfd41 100644
--- a/cypress/e2e/navbar.spec.cy.js
+++ b/cypress/e2e/navbar.spec.cy.js
@@ -7,9 +7,9 @@ describe('Navbar', function () {
cy.get("div.ui-dialog-buttonset button").click()
})
cy.wait(500)
- cy.get("div.ui-dialog div.ui-dialog-buttonset button").filter(':visible').click()
+ cy.dismissDialog()
cy.get('#editor > div.ace_scroller > div').type("
")
- cy.get("#eval").click()
+ cy.get("#run").click()
cy.wait(1300)
cy.get("#copy-all-clipboard").click({ force: true })
@@ -24,9 +24,9 @@ describe('Navbar', function () {
cy.get("div.ui-dialog-buttonset button").click()
})
cy.wait(500)
- cy.get("div.ui-dialog div.ui-dialog-buttonset button").filter(':visible').click()
+ cy.dismissDialog()
cy.get('#editor > div.ace_scroller > div').type("
")
- cy.get("#eval").click()
+ cy.get("#run").click()
cy.wait(1300)
cy.get("#copy-all-clipboard").click({ force: true })
diff --git a/cypress/e2e/outline_modes_spec.cy.js b/cypress/e2e/outline_modes_spec.cy.js
new file mode 100644
index 00000000..fd26c038
--- /dev/null
+++ b/cypress/e2e/outline_modes_spec.cy.js
@@ -0,0 +1,155 @@
+// TODO: remove LSP skip once lsp:* module is available on CI's eXist-db
+describe('Outline modes and filter', () => {
+ var lspAvailable = false
+
+ before(() => {
+ cy.loginXHR('admin', '')
+ cy.request({
+ method: 'POST',
+ url: '/eXide/api/query/symbols',
+ body: { query: 'declare function local:test() { 1 };', base: 'xmldb:exist:///db' },
+ headers: { 'Content-Type': 'application/json' },
+ failOnStatusCode: false
+ }).then((resp) => {
+ try {
+ var body = typeof resp.body === 'string' ? JSON.parse(resp.body) : resp.body
+ lspAvailable = resp.status === 200 && Array.isArray(body) && body.length > 0 && body[0].name !== undefined
+ } catch (e) {
+ lspAvailable = false
+ }
+ })
+ })
+
+ function setup() {
+ cy.loginXHR('admin', '')
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ cy.get('#user', { timeout: 15000 }).should('not.have.text', 'Login')
+
+ cy.get('#directory li').contains('span', 'apps', { timeout: 5000 }).click()
+ cy.get('#directory li').contains('span', 'eXide', { timeout: 5000 }).click()
+ cy.get('#directory li').contains('span', 'modules', { timeout: 5000 }).click()
+ cy.get('#directory li').contains('span', 'config.xqm', { timeout: 5000 }).click()
+ cy.get('.path', { timeout: 10000 }).should('contain', '/db/apps/eXide/modules/config.xqm')
+
+ cy.get('#tabs-outline').contains('outline').click()
+ cy.get('#outline li', { timeout: 10000 }).should('have.length.at.least', 2)
+ }
+
+ it('shows toolbar with three mode buttons', function () {
+ if (!lspAvailable) this.skip()
+ setup()
+ cy.get('#outline-toolbar').should('exist')
+ cy.get('[data-outline-mode="nested-doc"]').should('exist')
+ cy.get('[data-outline-mode="flat-doc"]').should('exist')
+ cy.get('[data-outline-mode="flat-alpha"]').should('exist')
+ })
+
+ it('nested-doc mode is active by default', function () {
+ if (!lspAvailable) this.skip()
+ setup()
+ cy.get('[data-outline-mode="nested-doc"]').should('have.class', 'active')
+ cy.get('[data-outline-mode="flat-doc"]').should('not.have.class', 'active')
+ cy.get('[data-outline-mode="flat-alpha"]').should('not.have.class', 'active')
+ })
+
+ it('switches to flat-doc mode', function () {
+ if (!lspAvailable) this.skip()
+ setup()
+ cy.get('[data-outline-mode="flat-doc"]').click()
+ cy.get('[data-outline-mode="flat-doc"]').should('have.class', 'active')
+ cy.get('[data-outline-mode="nested-doc"]').should('not.have.class', 'active')
+
+ cy.get('#outline li', { timeout: 5000 }).should('have.length.at.least', 2)
+ })
+
+ it('switches to flat-alpha mode and sorts alphabetically', function () {
+ if (!lspAvailable) this.skip()
+ setup()
+ cy.get('[data-outline-mode="flat-alpha"]').click()
+ cy.get('[data-outline-mode="flat-alpha"]').should('have.class', 'active')
+
+ cy.get('#outline li a .outline-name').should('have.length.at.least', 2)
+ .then(($names) => {
+ var names = []
+ $names.each(function () {
+ names.push(Cypress.$(this).text().trim().replace(/^\$/, '').toLowerCase())
+ })
+ for (var i = 1; i < names.length; i++) {
+ expect(names[i] >= names[i - 1], names[i - 1] + ' <= ' + names[i]).to.be.true
+ }
+ })
+ })
+
+ it('flat-doc mode preserves document order', function () {
+ if (!lspAvailable) this.skip()
+ setup()
+ var nestedOrder = []
+ cy.get('#outline li a .outline-name').then(($names) => {
+ $names.each(function () {
+ nestedOrder.push(Cypress.$(this).text().trim())
+ })
+ })
+
+ cy.get('[data-outline-mode="flat-doc"]').click()
+ cy.get('#outline li', { timeout: 5000 }).should('have.length.at.least', 2)
+
+ cy.get('#outline li a .outline-name').then(($names) => {
+ var flatOrder = []
+ $names.each(function () {
+ flatOrder.push(Cypress.$(this).text().trim())
+ })
+ expect(flatOrder).to.deep.eq(nestedOrder)
+ })
+ })
+
+ it('filter input narrows visible items', function () {
+ if (!lspAvailable) this.skip()
+ setup()
+ var totalCount
+ cy.get('#outline li').then(($items) => {
+ totalCount = $items.length
+ })
+
+ cy.get('#outline-filter').clear().type('access')
+
+ cy.get('#outline li').then(($items) => {
+ var visible = $items.filter(function () {
+ return Cypress.$(this).css('display') !== 'none'
+ })
+ expect(visible.length).to.be.below(totalCount)
+ expect(visible.text()).to.contain('access')
+ })
+ })
+
+ it('clearing filter shows all items again', function () {
+ if (!lspAvailable) this.skip()
+ setup()
+ var totalCount
+ cy.get('#outline li').then(($items) => {
+ totalCount = $items.length
+ })
+
+ cy.get('#outline-filter').clear().type('access')
+
+ cy.get('#outline-filter').clear()
+
+ cy.get('#outline li').then(($items) => {
+ var visible = $items.filter(function () {
+ return Cypress.$(this).css('display') !== 'none'
+ })
+ expect(visible.length).to.eq(totalCount)
+ })
+ })
+
+ it('clicking an outline item navigates the editor', function () {
+ if (!lspAvailable) this.skip()
+ setup()
+ cy.on('uncaught:exception', () => false)
+
+ cy.get('#outline li a').first().click()
+
+ cy.get('#editor .cm-editor', { timeout: 5000 }).should('exist')
+ })
+})
diff --git a/cypress/e2e/outline_navigation_spec.cy.js b/cypress/e2e/outline_navigation_spec.cy.js
new file mode 100644
index 00000000..b8940035
--- /dev/null
+++ b/cypress/e2e/outline_navigation_spec.cy.js
@@ -0,0 +1,209 @@
+describe('Outline navigation', () => {
+ beforeEach(() => {
+ cy.loginXHR('admin', '')
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ cy.get('#user', { timeout: 15000 }).should('not.have.text', 'Login')
+ })
+
+ function setEditorContent(text) {
+ cy.window().then((win) => {
+ var view = win.eXide.app.getEditor().editor
+ view.dispatch({
+ changes: { from: 0, to: view.state.doc.length, insert: text }
+ })
+ view.focus()
+ })
+ }
+
+ function newDocument(type, content) {
+ cy.window().then((win) => {
+ var editor = win.eXide.app.getEditor()
+ editor.newDocument(null, type)
+ })
+ cy.wait(300)
+ if (content) {
+ setEditorContent(content)
+ }
+ }
+
+ function showOutline() {
+ cy.get('#tabs-outline').contains('outline').click()
+ cy.wait(300)
+ }
+
+ describe('XML outline', () => {
+ var xml = [
+ '
',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ''
+ ].join('\n')
+
+ it('shows XML elements as nested tree', () => {
+ newDocument('xml', xml)
+ showOutline()
+
+ cy.get('#outline li', { timeout: 5000 }).should('have.length.at.least', 5)
+ cy.get('#outline .outline-name').first().should('contain', 'root')
+ })
+
+ it('shows XPath signatures in tooltips', () => {
+ newDocument('xml', xml)
+ showOutline()
+
+ // Outline items should have XPath-like title attributes
+ cy.get('#outline li a', { timeout: 5000 }).first()
+ .should('have.attr', 'title')
+ .and('match', /\/root/)
+ })
+
+ it('shows hint attributes for elements with id', () => {
+ newDocument('xml', xml)
+ showOutline()
+
+ cy.get('#outline .outline-hint', { timeout: 5000 })
+ .should('have.length.at.least', 1)
+ .first()
+ .invoke('text')
+ .should('contain', 'intro')
+ })
+
+ it('uses nested
structure for hierarchy', () => {
+ newDocument('xml', xml)
+ showOutline()
+
+ // Nested mode should produce children inside - elements
+ cy.get('#outline li ul', { timeout: 5000 })
+ .should('have.length.at.least', 1)
+ })
+
+ it('navigates to element and centers viewport on click', () => {
+ newDocument('xml', xml)
+ showOutline()
+
+ // Click the last element to trigger scroll
+ cy.get('#outline li a .outline-name').contains('section').first().parent().click()
+
+ // Editor should exist and have the cursor on or near the section element
+ cy.get('#editor .cm-editor', { timeout: 5000 }).should('exist')
+ })
+ })
+
+ describe('XML gotoSymbol', () => {
+ var xml = '\n \n \n'
+
+ it('opens QuickPicker with XPath items', () => {
+ newDocument('xml', xml)
+ // Wait for outline to populate (needs ensureSyntaxTree + requestAnimationFrame)
+ showOutline()
+ cy.get('#outline li', { timeout: 5000 }).should('have.length.at.least', 1)
+
+ cy.window().then((win) => {
+ win.eXide.app.getEditor().exec('gotoSymbol')
+ })
+
+ cy.get('.quick-picker', { timeout: 5000 })
+ .should('be.visible')
+
+ // Should show XPath-style items
+ cy.get('.quick-picker-list li', { timeout: 3000 })
+ .should('have.length.at.least', 1)
+ .invoke('text')
+ .should('contain', '/')
+ })
+ })
+
+ describe('JSON outline', () => {
+ var json = '{\n "name": "test",\n "version": "1.0",\n "scripts": {\n "build": "npm run build"\n }\n}'
+
+ it('shows property keys in outline', () => {
+ newDocument('json', json)
+ showOutline()
+
+ cy.get('#outline li', { timeout: 5000 }).should('have.length.at.least', 3)
+ cy.get('#outline .outline-name').invoke('text').should('contain', 'name')
+ })
+
+ it('opens QuickPicker via gotoSymbol', () => {
+ newDocument('json', json)
+ showOutline()
+ cy.get('#outline li', { timeout: 5000 }).should('have.length.at.least', 1)
+
+ cy.window().then((win) => {
+ win.eXide.app.getEditor().exec('gotoSymbol')
+ })
+
+ cy.get('.quick-picker', { timeout: 5000 }).should('be.visible')
+ })
+ })
+
+ describe('CSS outline', () => {
+ var css = 'body {\n color: red;\n}\n\n.header {\n display: flex;\n}\n\n#main {\n padding: 10px;\n}'
+
+ it('shows selectors in outline', () => {
+ newDocument('css', css)
+ showOutline()
+
+ cy.get('#outline li', { timeout: 5000 }).should('have.length.at.least', 2)
+ cy.get('#outline .outline-name').invoke('text').should('contain', 'body')
+ })
+ })
+
+ describe('JavaScript outline', () => {
+ var js = 'function greet(name) {\n return "Hello " + name;\n}\n\nfunction add(a, b) {\n return a + b;\n}'
+
+ it('shows functions in outline', () => {
+ newDocument('javascript', js)
+ showOutline()
+
+ cy.get('#outline li', { timeout: 5000 }).should('have.length.at.least', 2)
+ cy.get('#outline .outline-name').invoke('text').should('contain', 'greet')
+ })
+ })
+
+ describe('Markdown outline', () => {
+ var md = '# Title\n\n## Section 1\n\nContent\n\n## Section 2\n\nMore content\n\n### Subsection'
+
+ it('shows headings in outline', () => {
+ newDocument('markdown', md)
+ showOutline()
+
+ cy.get('#outline li', { timeout: 5000 }).should('have.length.at.least', 3)
+ cy.get('#outline .outline-name').invoke('text').should('contain', 'Title')
+ })
+ })
+
+ describe('Navigation flash', () => {
+ it('flashes target line on gotoLine', () => {
+ // Open config.xqm which has enough lines
+ cy.get('#directory li').contains('span', 'apps', { timeout: 5000 }).click()
+ cy.get('#directory li').contains('span', 'eXide', { timeout: 5000 }).click()
+ cy.get('#directory li').contains('span', 'modules', { timeout: 5000 }).click()
+ cy.get('#directory li').contains('span', 'config.xqm', { timeout: 5000 }).click()
+ cy.get('.path', { timeout: 10000 }).should('contain', 'config.xqm')
+
+ // Navigate to a line programmatically
+ cy.window().then((win) => {
+ var view = win.eXide.app.getEditor().editor
+ win.editorUtils.gotoLine(view, 10, 0, true)
+ })
+
+ // Flash decoration should appear briefly
+ cy.get('.cm-goto-flash', { timeout: 1000 }).should('exist')
+
+ // Flash should fade and be removed
+ cy.get('.cm-goto-flash', { timeout: 2000 }).should('not.exist')
+ })
+ })
+})
diff --git a/cypress/e2e/outline_spec.cy.js b/cypress/e2e/outline_spec.cy.js
new file mode 100644
index 00000000..757d3ba9
--- /dev/null
+++ b/cypress/e2e/outline_spec.cy.js
@@ -0,0 +1,48 @@
+// TODO: remove LSP skip once lsp:* module is available on CI's eXist-db
+describe('Outline view', () => {
+ var lspAvailable = false
+
+ before(() => {
+ cy.loginXHR('admin', '')
+ cy.request({
+ method: 'POST',
+ url: '/eXide/api/query/symbols',
+ body: { query: 'declare function local:test() { 1 };', base: 'xmldb:exist:///db' },
+ headers: { 'Content-Type': 'application/json' },
+ failOnStatusCode: false
+ }).then((resp) => {
+ try {
+ var body = typeof resp.body === 'string' ? JSON.parse(resp.body) : resp.body
+ lspAvailable = resp.status === 200 && Array.isArray(body) && body.length > 0 && body[0].name !== undefined
+ } catch (e) {
+ lspAvailable = false
+ }
+ })
+ })
+
+ it('should show XQuery functions after opening a library module via collections pane', function () {
+ if (!lspAvailable) this.skip()
+
+ cy.loginXHR('admin', '')
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ cy.get('#user', { timeout: 15000 }).should('not.have.text', 'Login')
+
+ cy.get('#directory li').contains('span', 'apps', { timeout: 5000 }).click()
+ cy.get('#directory li').contains('span', 'eXide', { timeout: 5000 }).click()
+ cy.get('#directory li').contains('span', 'modules', { timeout: 5000 }).click()
+ cy.get('#directory li').contains('span', 'config.xqm', { timeout: 5000 }).click()
+
+ cy.get('.path', { timeout: 10000 }).should('contain', '/db/apps/eXide/modules/config.xqm')
+
+ cy.get('#tabs-outline').contains('outline').click()
+
+ cy.get('#outline li', { timeout: 10000 }).should('have.length.at.least', 1)
+
+ cy.get('#outline li a .outline-name').contains('config:access-allowed').should('exist')
+ cy.get('#outline li a .outline-name').contains('config:repo-descriptor').should('exist')
+ cy.get('#outline li a .outline-name').contains('config:expath-descriptor').should('exist')
+ cy.get('#outline li a .outline-name').contains('config:app-info').should('exist')
+ })
+})
diff --git a/cypress/e2e/panels_spec.cy.js b/cypress/e2e/panels_spec.cy.js
new file mode 100644
index 00000000..6a35ef45
--- /dev/null
+++ b/cypress/e2e/panels_spec.cy.js
@@ -0,0 +1,119 @@
+describe('Panel resize handles', () => {
+ beforeEach(() => {
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ // Wait for eXide to fully initialize
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ })
+
+ function runQuery() {
+ // Select all content and replace with a valid query
+ cy.get('#editor .cm-content').click()
+ cy.get('#editor .cm-content').type('{ctrl+a}{backspace}1 + 1', { delay: 0 })
+ cy.get('#run').click()
+ // Wait for results to appear
+ cy.get('.panel-south .results', { timeout: 5000 }).should('not.be.empty')
+ }
+
+ describe('West panel (outline)', () => {
+ it('toggles closed and open by clicking the resize handle', () => {
+ cy.get('.panel-west').should('be.visible')
+ cy.get('.panel-west').invoke('outerWidth').should('be.gt', 50)
+
+ // Click the resize handle to collapse
+ cy.get('.panel-west .resize-handle').click()
+ cy.get('.panel-west').invoke('outerWidth').should('be.lte', 12)
+
+ // Click again to expand
+ cy.get('.panel-west .minimized').click()
+ cy.get('.panel-west').invoke('outerWidth').should('be.gt', 50)
+ })
+ })
+
+ describe('South panel (results)', () => {
+ it('shows results after running a query', () => {
+ runQuery()
+ cy.get('.panel-south').invoke('outerHeight').should('be.gt', 50)
+ })
+
+ it('toggles closed and open by clicking the resize handle', () => {
+ runQuery()
+ cy.get('.panel-south').invoke('outerHeight').should('be.gt', 50)
+
+ // Click the resize handle to collapse
+ cy.get('.panel-south .resize-handle').click()
+ cy.get('.panel-south').invoke('outerHeight').should('be.lte', 12)
+
+ // Click again to expand
+ cy.get('.panel-south .minimized').click()
+ cy.get('.panel-south').invoke('outerHeight').should('be.gt', 50)
+ })
+ })
+
+ describe('Drag to resize', () => {
+ it('resizes the west panel by dragging its handle', () => {
+ cy.get('.panel-west').invoke('outerWidth').then((initialWidth) => {
+ // Drag the west handle 80px to the right to widen
+ cy.get('.panel-west .resize-handle').then(($handle) => {
+ const rect = $handle[0].getBoundingClientRect()
+ const startX = rect.left + rect.width / 2
+ const startY = rect.top + rect.height / 2
+
+ cy.get('.panel-west .resize-handle')
+ .trigger('mousedown', { pageX: startX, pageY: startY, which: 1 })
+ cy.get('.layout')
+ .trigger('mousemove', { pageX: startX + 80, pageY: startY, which: 1 })
+ cy.document().trigger('mouseup')
+
+ cy.get('.panel-west').invoke('outerWidth').should('be.gt', initialWidth + 40)
+ })
+ })
+ })
+
+ it('resizes the south panel by dragging its handle', () => {
+ runQuery()
+ cy.get('.panel-south').invoke('outerHeight').then((initialHeight) => {
+ // Drag the south handle 100px up to make it taller
+ cy.get('.panel-south .resize-handle').then(($handle) => {
+ const rect = $handle[0].getBoundingClientRect()
+ const startX = rect.left + rect.width / 2
+ const startY = rect.top + rect.height / 2
+
+ cy.get('.panel-south .resize-handle')
+ .trigger('mousedown', { pageX: startX, pageY: startY, which: 1 })
+ cy.get('.layout')
+ .trigger('mousemove', { pageX: startX, pageY: startY - 100, which: 1 })
+ cy.document().trigger('mouseup')
+
+ cy.get('.panel-south').invoke('outerHeight').should('be.gt', initialHeight + 50)
+ })
+ })
+ })
+ })
+
+ describe('East panel (monitor)', () => {
+ it('toggles open and closed via the monitor toolbar button', () => {
+ // Monitor panel should be hidden initially
+ cy.get('.panel-east').should('not.be.visible')
+
+ // Click the monitor toggle button
+ cy.get('#toggle-monitor').click()
+
+ // Monitor content should be present and visible
+ cy.get('.mon-title', { timeout: 5000 }).should('be.visible').and('contain', 'MONITOR')
+ cy.get('.panel-east').invoke('outerWidth').should('be.gt', 50)
+
+ // Click again to close
+ cy.get('#toggle-monitor').click()
+ cy.get('.panel-east').should('not.be.visible')
+ })
+
+ it('closes via the close button in the monitor bar', () => {
+ cy.get('#toggle-monitor').click()
+ cy.get('.mon-title', { timeout: 5000 }).should('be.visible')
+
+ cy.get('#monitor-close').click()
+ cy.get('.panel-east').should('not.be.visible')
+ })
+ })
+})
diff --git a/cypress/e2e/preferences_spec.cy.js b/cypress/e2e/preferences_spec.cy.js
new file mode 100644
index 00000000..2f8d5ffe
--- /dev/null
+++ b/cypress/e2e/preferences_spec.cy.js
@@ -0,0 +1,88 @@
+describe('Preferences dialog', () => {
+ beforeEach(() => {
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ })
+
+ function openPreferences() {
+ cy.get('#menu-edit-preferences').click({ force: true })
+ cy.get('#preferences-dialog').should('be.visible')
+ }
+
+ it('opens via Edit menu', () => {
+ openPreferences()
+ cy.get('#preferences-dialog form').should('exist')
+ // Check fieldsets are present
+ cy.get('#preferences-dialog legend').should('have.length.at.least', 3)
+ })
+
+ it('shows current theme setting', () => {
+ openPreferences()
+ cy.get('#theme').should('have.value', 'light')
+ })
+
+ it('changes theme to dark', () => {
+ openPreferences()
+ cy.get('#theme').select('dark')
+ // Body should get dark class
+ cy.get('body').should('have.class', 'dark')
+ })
+
+ it('changes font size', () => {
+ openPreferences()
+ cy.get('#pref-font-size').select('16')
+ // CM6 editor should reflect the new font size
+ cy.get('.cm-editor .cm-content').should('have.css', 'font-size', '16px')
+ })
+
+ it('toggles show invisibles', () => {
+ openPreferences()
+ // Check current state
+ cy.get('#pref-show-invisibles').then(($cb) => {
+ var wasChecked = $cb.prop('checked')
+ // Toggle it
+ cy.get('#pref-show-invisibles').click()
+ // Verify it changed
+ cy.get('#pref-show-invisibles').should(wasChecked ? 'not.be.checked' : 'be.checked')
+ })
+ })
+
+ it('persists settings across reload', () => {
+ openPreferences()
+ cy.get('#pref-font-size').select('16')
+ // Close dialog
+ cy.get('#preferences-dialog').closest('.eXide-dialog').find('.eXide-dialog-buttons button').contains('Close').click()
+
+ // Reload and check persistence
+ cy.reload(true)
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ openPreferences()
+ cy.get('#pref-font-size').should('have.value', '16')
+
+ // Reset to default
+ cy.get('#pref-font-size').select('14')
+ })
+
+ it('changes indent mode', () => {
+ openPreferences()
+ cy.get('#pref-indent').select('Tabs')
+ cy.get('#pref-indent').should('have.value', 'Tabs')
+ })
+
+ it('changes prettier print width', () => {
+ openPreferences()
+ cy.get('#pref-prettier-print-width').select('120')
+ cy.get('#pref-prettier-print-width').should('have.value', '120')
+ // Reset
+ cy.get('#pref-prettier-print-width').select('80')
+ })
+
+ it('reverts theme on close when changed to dark then back', () => {
+ openPreferences()
+ cy.get('#theme').select('dark')
+ cy.get('body').should('have.class', 'dark')
+ cy.get('#theme').select('light')
+ cy.get('body').should('not.have.class', 'dark')
+ })
+})
diff --git a/cypress/e2e/prettier_format_spec.cy.js b/cypress/e2e/prettier_format_spec.cy.js
new file mode 100644
index 00000000..07ad3808
--- /dev/null
+++ b/cypress/e2e/prettier_format_spec.cy.js
@@ -0,0 +1,83 @@
+describe('Prettier formatting', () => {
+ beforeEach(() => {
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ })
+
+ function setEditorContent(text) {
+ cy.window().then((win) => {
+ var doc = win.eXide.app.getEditor().getActiveDocument()
+ doc.setText(text)
+ })
+ }
+
+ function getEditorContent() {
+ return cy.window().then((win) => {
+ return win.eXide.app.getEditor().getActiveDocument().getText()
+ })
+ }
+
+ it('formats XQuery code via menu', () => {
+ // Badly formatted XQuery
+ var ugly = 'for $x in (1,2,3) let $y:=$x*2 return
- {$y}
'
+ setEditorContent(ugly)
+
+ // Trigger format via the Edit > Format Code menu item
+ cy.get('#menu-edit-format').click({ force: true })
+
+ // Content should change (Prettier reformats it)
+ cy.wait(500)
+ getEditorContent().should('not.eq', ugly)
+ })
+
+ it('formats XML code', () => {
+ // Create a new XML document
+ cy.window().then((win) => {
+ win.eXide.app.newDocument('text', 'xml')
+ })
+ cy.get('.path', { timeout: 5000 }).should('contain', 'untitled-')
+
+ var before
+ getEditorContent().then((text) => {
+ before = text
+ })
+
+ cy.get('#menu-edit-format').click({ force: true })
+ cy.wait(500)
+
+ // XML should be indented/reformatted
+ getEditorContent().then((text) => {
+ expect(text).to.not.eq(before)
+ // Formatted XML should have newlines (indentation)
+ expect(text).to.contain('\n')
+ })
+ })
+
+ it('formats CSS code', () => {
+ cy.window().then((win) => {
+ win.eXide.app.newDocument('body{color:red;margin:0;padding:0}h1{font-size:2em}', 'css')
+ })
+ cy.get('.path', { timeout: 5000 }).should('contain', 'untitled-')
+
+ cy.get('#menu-edit-format').click({ force: true })
+ cy.wait(500)
+
+ getEditorContent().then((text) => {
+ // Formatted CSS should have newlines and proper structure
+ expect(text).to.contain('\n')
+ expect(text).to.match(/color:\s*red/)
+ })
+ })
+
+ it('shows error toast for unparseable code', () => {
+ // Completely broken XQuery that Prettier can't parse
+ setEditorContent('{{{{{')
+
+ cy.get('#menu-edit-format').click({ force: true })
+
+ // Error toast should appear
+ cy.get('.eXide-toast', { timeout: 5000 })
+ .should('contain', 'could not be formatted')
+ })
+})
diff --git a/cypress/e2e/query_execution_spec.cy.js b/cypress/e2e/query_execution_spec.cy.js
new file mode 100644
index 00000000..df787592
--- /dev/null
+++ b/cypress/e2e/query_execution_spec.cy.js
@@ -0,0 +1,264 @@
+describe('Query execution', () => {
+ beforeEach(() => {
+ cy.loginXHR('admin', '')
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ cy.get('#user', { timeout: 10000 }).should('not.have.text', 'Login')
+ })
+
+ /**
+ * Set the editor content by calling the eXide API directly.
+ */
+ function setEditorContent(text) {
+ cy.window().then((win) => {
+ var doc = win.eXide.app.getEditor().getActiveDocument()
+ doc.setText(text)
+ })
+ }
+
+ it('runs a simple XQuery and shows results', () => {
+ setEditorContent('for $i in 1 to 3 return {$i}')
+ cy.get('#run').click()
+
+ // Results panel should appear with content
+ cy.get('.panel-south .results', { timeout: 10000 })
+ .should('not.be.empty')
+ cy.get('.panel-south .results .content', { timeout: 10000 })
+ .should('have.length.at.least', 1)
+
+ // Status message should mention results
+ cy.get('.panel-south .current')
+ .invoke('text')
+ .should('match', /Showing results/)
+ })
+
+ it('shows an error for invalid XQuery', () => {
+ setEditorContent('for $x in return')
+ cy.get('#run').click()
+
+ // Error should appear in the error status area
+ cy.get('#error-status', { timeout: 10000 })
+ .invoke('text')
+ .should('have.length.greaterThan', 0)
+ })
+
+ it('displays correct result count', () => {
+ setEditorContent('for $i in 1 to 5 return $i')
+ cy.get('#run').click()
+
+ cy.get('.panel-south .current', { timeout: 10000 })
+ .invoke('text')
+ .should('contain', '5')
+ })
+
+ it('evaluates a string expression', () => {
+ setEditorContent('"Hello, eXide!"')
+ cy.get('#run').click()
+
+ cy.get('.panel-south .results .content', { timeout: 10000 })
+ .first()
+ .should('contain', 'Hello, eXide!')
+ })
+
+ it('clears previous results before running new query', () => {
+ setEditorContent('1 + 1')
+ cy.get('#run').click()
+ cy.get('.panel-south .results .content', { timeout: 10000 })
+ .should('have.length.at.least', 1)
+
+ // Run a different query
+ setEditorContent('"second query"')
+ cy.get('#run').click()
+ cy.get('.panel-south .results .content', { timeout: 10000 })
+ .first()
+ .should('contain', 'second query')
+ })
+
+ it('shows a toast notification with result count', () => {
+ setEditorContent('1')
+ cy.get('#run').click()
+
+ // util.message() shows a toast with "Query returned N item(s) in Xs"
+ cy.get('.eXide-toast', { timeout: 10000 })
+ .should('contain', 'returned')
+ .and('contain', 'item')
+ })
+
+ it('shows timing info after query completes', () => {
+ setEditorContent('for $i in 1 to 3 return $i')
+ cy.get('#run').click()
+
+ cy.get('#query-timing', { timeout: 10000 })
+ .should('be.visible')
+ .invoke('text')
+ .should('contain', 'Items:')
+ // Timing bar shows either detailed (Compile/Eval/Total) or simple (Elapsed)
+ .and('match', /Compile:|Elapsed:/)
+ })
+
+ it('shows cancel button during execution and hides after', () => {
+ // Cancel button should be hidden initially
+ cy.get('#cancel-query').should('not.be.visible')
+
+ // Run a query — cancel button should appear briefly then hide
+ setEditorContent('1 + 1')
+ cy.get('#run').click()
+
+ // After completion, cancel button should be hidden
+ cy.get('.panel-south .results .content', { timeout: 10000 })
+ .should('have.length.at.least', 1)
+ cy.get('#cancel-query').should('not.be.visible')
+ })
+
+ it('cancel button exists and is hidden when no query is running', () => {
+ cy.get('#cancel-query').should('exist').and('not.be.visible')
+ })
+
+ it('falls back to HTTP when WebSocket unavailable', () => {
+ // Disconnect the ws-eval WebSocket
+ cy.window().then((win) => {
+ win.eXide.wsEval.disconnect()
+ })
+
+ setEditorContent('1 + 1')
+ cy.get('#run').click()
+
+ // Should still produce results via HTTP fallback
+ cy.get('.panel-south .results .content', { timeout: 10000 })
+ .should('have.length.at.least', 1)
+ })
+
+ it('supports adaptive serialization mode', () => {
+ // First run a simple query to make results panel visible
+ setEditorContent('1')
+ cy.get('#run').click()
+ cy.get('.panel-south .results .content', { timeout: 10000 })
+ .should('have.length.at.least', 1)
+
+ // Now switch serialization mode and run again
+ cy.get('.panel-south #serialization-mode').select('adaptive')
+ setEditorContent('"adaptive output"')
+ cy.get('#run').click()
+
+ cy.get('.panel-south .results .content', { timeout: 10000 })
+ .should('have.length.at.least', 1)
+ })
+
+ it('paginates results with next/previous buttons', () => {
+ // Query with 25 results, default page size 10
+ setEditorContent('for $i in 1 to 25 return $i')
+ cy.get('#run').click()
+
+ // First page: 1 to 10
+ cy.get('.panel-south .current', { timeout: 10000 })
+ .invoke('text')
+ .should('contain', '1 to 10 of 25')
+ cy.get('.panel-south .results .content')
+ .should('have.length', 10)
+
+ // Click next page
+ cy.get('.panel-south .next').click()
+ cy.get('.panel-south .current')
+ .invoke('text')
+ .should('contain', '11 to 20 of 25')
+
+ // Click next again — partial page
+ cy.get('.panel-south .next').click()
+ cy.get('.panel-south .current')
+ .invoke('text')
+ .should('contain', '21 to 25 of 25')
+
+ // Click previous
+ cy.get('.panel-south .previous').click()
+ cy.get('.panel-south .current')
+ .invoke('text')
+ .should('contain', '11 to 20 of 25')
+
+ // Click first
+ cy.get('.panel-south .first-page').click()
+ cy.get('.panel-south .current')
+ .invoke('text')
+ .should('contain', '1 to 10 of 25')
+
+ // Click last
+ cy.get('.panel-south .last-page').click()
+ cy.get('.panel-south .current')
+ .invoke('text')
+ .should('contain', '21 to 25 of 25')
+ })
+
+ it('handles large result sets without browser memory issues', () => {
+ // 10,000 items — should return quickly with cursor, only first page rendered
+ setEditorContent('for $i in 1 to 10000 return $i')
+ cy.get('#run').click()
+
+ cy.get('.panel-south .current', { timeout: 30000 })
+ .invoke('text')
+ .should('contain', '10000')
+ // Only 10 items rendered in DOM (not 10,000)
+ cy.get('.panel-south .results .content')
+ .should('have.length', 10)
+ })
+
+ it('re-fetches page when serialization mode changes without re-executing', () => {
+ setEditorContent('')
+ cy.get('#run').click()
+ cy.get('.panel-south .results .content', { timeout: 10000 })
+ .should('have.length.at.least', 1)
+
+ // Switch to XML mode — should re-fetch current page, not re-run query
+ cy.intercept('GET', '**/api/query/*/results*').as('fetchPage')
+ cy.intercept('POST', '**/api/query').as('execQuery')
+
+ cy.get('.panel-south #serialization-mode').select('xml')
+
+ // Should fetch results but NOT post a new query
+ cy.wait('@fetchPage')
+ cy.get('@execQuery.all').should('have.length', 0)
+ })
+
+ it('adaptive serialization quotes xs:string values', () => {
+ // Run any query first so the results panel becomes visible
+ setEditorContent('1')
+ cy.get('#run').click()
+ cy.get('.panel-south .results .content', { timeout: 10000 }).should('have.length.at.least', 1)
+
+ cy.get('.panel-south #serialization-mode').select('adaptive')
+ setEditorContent('"hello"')
+ cy.get('#run').click()
+
+ cy.get('.panel-south .results .content', { timeout: 10000 })
+ .first()
+ .invoke('text')
+ .should('eq', '"hello"')
+ })
+
+ it('adaptive serialization does not quote xs:integer values', () => {
+ setEditorContent('1')
+ cy.get('#run').click()
+ cy.get('.panel-south .results .content', { timeout: 10000 }).should('have.length.at.least', 1)
+
+ cy.get('.panel-south #serialization-mode').select('adaptive')
+ setEditorContent('42')
+ cy.get('#run').click()
+
+ cy.get('.panel-south .results .content', { timeout: 10000 })
+ .first()
+ .invoke('text')
+ .should('eq', '42')
+ })
+
+ it('re-fetches page when indent toggle changes', () => {
+ setEditorContent('')
+ cy.get('#run').click()
+ cy.get('.panel-south .results .content', { timeout: 10000 })
+ .should('have.length.at.least', 1)
+
+ // Toggle indent off
+ cy.intercept('GET', '**/api/query/*/results*').as('fetchPage')
+ cy.get('#indent-results-btn').click()
+
+ cy.wait('@fetchPage')
+ })
+})
diff --git a/cypress/e2e/rename_spec.cy.js b/cypress/e2e/rename_spec.cy.js
new file mode 100644
index 00000000..72259548
--- /dev/null
+++ b/cypress/e2e/rename_spec.cy.js
@@ -0,0 +1,75 @@
+describe('Rename (multi-cursor)', () => {
+ beforeEach(() => {
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ // Dismiss any startup toasts
+ cy.wait(500)
+ })
+
+ function setEditorContent(text) {
+ cy.window().then((win) => {
+ var doc = win.eXide.app.getEditor().getActiveDocument()
+ doc.setText(text)
+ })
+ }
+
+ function placeCursorAndRename(line, col) {
+ // Place cursor and trigger rename in a single window call
+ // to avoid focus-change side effects between Cypress commands
+ cy.window().then((win) => {
+ var editor = win.eXide.app.getEditor()
+ var view = editor.editor
+ var editorUtils = win.editorUtils
+ var offset = editorUtils.rowColToOffset(view.state, line, col)
+ view.dispatch({ selection: { anchor: offset } })
+ view.focus()
+ var doc = editor.getActiveDocument()
+ doc.helper.rename(doc)
+ })
+ }
+
+ it('creates multi-cursor selection for variable rename', () => {
+ setEditorContent('let $count := 1\nlet $other := $count + 1\nreturn $count')
+ // Place cursor on 'count' (EQName inside VarName) — line 2, col 9
+ placeCursorAndRename(2, 9)
+
+ cy.get('.eXide-toast', { timeout: 5000 })
+ .should('contain', 'Editing 3 occurrence')
+ })
+
+ it('renames variable from $ sign position', () => {
+ setEditorContent('let $x := 1 return $x')
+ // Place cursor on $ of $x in 'return $x' — line 0, col 20
+ placeCursorAndRename(0, 20)
+
+ cy.get('.eXide-toast', { timeout: 5000 })
+ .should('contain', 'Editing 2 occurrence')
+ })
+
+ it('creates multi-cursor selection for function rename', () => {
+ setEditorContent('declare function local:greet($name) { "Hello " || $name };\nlocal:greet("world")')
+ // Place cursor on 'greet' in the function call — line 1, col 8
+ placeCursorAndRename(1, 8)
+
+ cy.get('.eXide-toast', { timeout: 5000 })
+ .should('contain', 'Editing 2 occurrence')
+ })
+
+ it('creates multi-cursor selection for XML element tag rename', () => {
+ setEditorContent('hello
')
+ // Place cursor on 'div' in opening tag — line 0, col 2
+ placeCursorAndRename(0, 2)
+
+ cy.get('.eXide-toast', { timeout: 5000 })
+ .should('contain', 'Editing 2 occurrence')
+ })
+
+ it('shows message when cursor is not on a renameable symbol', () => {
+ setEditorContent('1 + 2')
+ placeCursorAndRename(0, 2)
+
+ cy.get('.eXide-toast', { timeout: 5000 })
+ .should('contain', 'cursor')
+ })
+})
diff --git a/cypress/e2e/search_spec.cy.js b/cypress/e2e/search_spec.cy.js
new file mode 100644
index 00000000..b29a8e21
--- /dev/null
+++ b/cypress/e2e/search_spec.cy.js
@@ -0,0 +1,162 @@
+describe('Find in Files (search API)', () => {
+ beforeEach(() => {
+ cy.loginXHR('admin', '')
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ cy.get('#user', { timeout: 10000 }).should('not.have.text', 'Login')
+ })
+
+ it('POST /api/search returns JSON results', () => {
+ cy.request({
+ method: 'POST',
+ url: '/eXide/api/search',
+ headers: { 'Content-Type': 'application/json' },
+ body: {
+ query: 'function',
+ collection: '/db/apps/eXide/modules',
+ type: 'xquery'
+ }
+ }).then((response) => {
+ expect(response.status).to.eq(200)
+ expect(response.body).to.have.property('query', 'function')
+ expect(response.body).to.have.property('collection', '/db/apps/eXide/modules')
+ expect(response.body).to.have.property('hits')
+ expect(response.body.hits).to.be.an('array')
+ expect(response.body.hits.length).to.be.greaterThan(0)
+
+ const hit = response.body.hits[0]
+ expect(hit).to.have.property('resource')
+ expect(hit).to.have.property('name')
+ expect(hit).to.have.property('line')
+ expect(hit).to.have.property('text')
+ })
+ })
+
+ it('returns 400 for empty query', () => {
+ cy.request({
+ method: 'POST',
+ url: '/eXide/api/search',
+ headers: { 'Content-Type': 'application/json' },
+ body: { query: '', collection: '/db' },
+ failOnStatusCode: false
+ }).then((response) => {
+ expect(response.status).to.eq(400)
+ expect(response.body).to.have.property('error')
+ })
+ })
+
+ it('filters by type', () => {
+ cy.request({
+ method: 'POST',
+ url: '/eXide/api/search',
+ headers: { 'Content-Type': 'application/json' },
+ body: {
+ query: 'import',
+ collection: '/db/apps/eXide/modules',
+ type: 'xquery'
+ }
+ }).then((response) => {
+ expect(response.status).to.eq(200)
+ response.body.hits.forEach((hit) => {
+ expect(hit.resource).to.match(/\.(xq|xqm|xql|xquery)$/)
+ })
+ })
+ })
+
+ it('supports case-sensitive search', () => {
+ cy.request({
+ method: 'POST',
+ url: '/eXide/api/search',
+ headers: { 'Content-Type': 'application/json' },
+ body: {
+ query: 'FUNCTION',
+ collection: '/db/apps/eXide/modules',
+ type: 'xquery',
+ caseSensitive: true
+ }
+ }).then((response) => {
+ expect(response.status).to.eq(200)
+ // XQuery uses lowercase 'function', so case-sensitive 'FUNCTION' should find fewer/no hits
+ expect(response.body.hits.length).to.eq(0)
+ })
+ })
+
+ it('supports regex search', () => {
+ cy.request({
+ method: 'POST',
+ url: '/eXide/api/search',
+ headers: { 'Content-Type': 'application/json' },
+ body: {
+ query: 'declare\\s+function',
+ collection: '/db/apps/eXide/modules',
+ type: 'xquery',
+ regex: true
+ }
+ }).then((response) => {
+ expect(response.status).to.eq(200)
+ expect(response.body.hits.length).to.be.greaterThan(0)
+ response.body.hits.forEach((hit) => {
+ expect(hit.text).to.match(/declare\s+function/)
+ })
+ })
+ })
+
+ it('opens Find in Files dialog and renders results in iframe', () => {
+ // Intercept the API call before any UI interaction
+ cy.intercept('POST', '**/api/search').as('searchApi')
+
+ // Open the dialog via menu
+ cy.get('#menu-edit-find-files').click({ force: true })
+ cy.get('#find-dialog', { timeout: 5000 }).should('be.visible')
+
+ // Fill in search form
+ cy.get('#find-dialog input[name="search"]').clear().type('function')
+ cy.get('#find-dialog select[name="type"]').select('xquery')
+ cy.get('#find-dialog input[name="target"][value="all"]').check()
+
+ // Click Search button (in the dialog's button bar, sibling of #find-dialog)
+ cy.get('#find-dialog').closest('.eXide-dialog').find('.eXide-dialog-buttons button').contains('Search').click()
+
+ // Verify API was called and results rendered
+ cy.wait('@searchApi').then((interception) => {
+ expect(interception.response.statusCode).to.eq(200)
+ expect(interception.response.body.hits.length).to.be.greaterThan(0)
+ })
+
+ // Results should appear in the iframe (re-query body to avoid detached DOM)
+ cy.get('#results-iframe', { timeout: 10000 }).should('be.visible')
+ cy.get('#results-iframe').its('0.contentDocument.body', { timeout: 10000 })
+ .should('not.be.empty')
+ cy.get('#results-iframe').its('0.contentDocument.body')
+ .find('.sourceinfo')
+ .should('have.length.greaterThan', 0)
+ })
+
+ it('clicking a search result opens the document', () => {
+ // Intercept before UI interaction
+ cy.intercept('POST', '**/api/search').as('searchApi')
+
+ // Open dialog and search
+ cy.get('#menu-edit-find-files').click({ force: true })
+ cy.get('#find-dialog', { timeout: 5000 }).should('be.visible')
+ cy.get('#find-dialog input[name="search"]').clear().type('declare function')
+ cy.get('#find-dialog select[name="type"]').select('xquery')
+ cy.get('#find-dialog input[name="target"][value="all"]').check()
+
+ cy.get('#find-dialog').closest('.eXide-dialog').find('.eXide-dialog-buttons button').contains('Search').click()
+ cy.wait('@searchApi')
+
+ // Click the first result link inside the iframe
+ cy.get('#results-iframe', { timeout: 10000 })
+ .its('0.contentDocument.body')
+ .find('.resource')
+ .first()
+ .click()
+
+ // A new tab should open with the file
+ cy.get('.path', { timeout: 10000 })
+ .invoke('text')
+ .should('not.eq', 'untitled-1')
+ })
+})
diff --git a/cypress/e2e/semantic_highlight_spec.cy.js b/cypress/e2e/semantic_highlight_spec.cy.js
new file mode 100644
index 00000000..35fcc00e
--- /dev/null
+++ b/cypress/e2e/semantic_highlight_spec.cy.js
@@ -0,0 +1,74 @@
+describe('Semantic highlighting', () => {
+ beforeEach(() => {
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ })
+
+ function setEditorContent(text) {
+ cy.window().then((win) => {
+ var doc = win.eXide.app.getEditor().getActiveDocument()
+ doc.setText(text)
+ })
+ }
+
+ function triggerParse() {
+ // Force a parse by calling the helper's validate method
+ cy.window().then((win) => {
+ var editor = win.eXide.app.getEditor()
+ var doc = editor.getActiveDocument()
+ var helper = editor.getActiveDocument().helper
+ if (helper && helper.parseXQuery) {
+ helper.parseXQuery(doc)
+ }
+ })
+ }
+
+ function semSpans(cls) {
+ return cy.get('.cm-editor .cm-content').find('.' + cls, { timeout: 5000 })
+ }
+
+ it('highlights function names in declarations and calls', () => {
+ setEditorContent('declare function local:test() { local:test() };\nlocal:test()')
+ triggerParse()
+ semSpans('sem-function-name').should('have.length.at.least', 2)
+ })
+
+ it('highlights variables', () => {
+ setEditorContent('let $x := 1 return $x')
+ triggerParse()
+ semSpans('sem-variable').should('have.length.at.least', 1)
+ })
+
+ it('highlights namespace prefixes in declarations', () => {
+ setEditorContent('module namespace demo = "http://example.com/demo";\ndeclare function demo:f() { 1 };')
+ triggerParse()
+ semSpans('sem-namespace-prefix').should('have.length.at.least', 1)
+ })
+
+ it('highlights annotations', () => {
+ setEditorContent('declare %rest:GET %rest:path("/api") function local:f() { 1 };')
+ triggerParse()
+ semSpans('sem-annotation').should('have.length.at.least', 2)
+ })
+
+ it('highlights type names', () => {
+ setEditorContent('let $x as xs:string := "hello" return $x')
+ triggerParse()
+ semSpans('sem-type-name').should('have.length.at.least', 1)
+ })
+
+ it('updates highlights when content changes', () => {
+ setEditorContent('let $x := 1 return $x')
+ triggerParse()
+ semSpans('sem-variable').should('have.length.at.least', 1)
+
+ // Change content to have no variables
+ setEditorContent('1 + 2')
+ triggerParse()
+
+ // Give a moment for decorations to clear
+ cy.wait(200)
+ cy.get('.cm-editor .cm-content').find('.sem-variable').should('not.exist')
+ })
+})
diff --git a/cypress/e2e/split_pane_spec.cy.js b/cypress/e2e/split_pane_spec.cy.js
new file mode 100644
index 00000000..1f9aafcb
--- /dev/null
+++ b/cypress/e2e/split_pane_spec.cy.js
@@ -0,0 +1,70 @@
+describe('West panel split/tab toggle', () => {
+ beforeEach(() => {
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ // Ensure we start in tabbed mode (default)
+ cy.get('.panel-west').should('not.have.class', 'split-pane')
+ })
+
+ it('switches from tabbed to split when collections tab is active', () => {
+ // Collections tab is active by default (index 0)
+ cy.get('#tabs-outline a.tab').first().should('have.class', 'active')
+ cy.get('#directory-body').should('have.css', 'visibility', 'visible')
+ cy.get('#outline-body').should('have.css', 'visibility', 'hidden')
+
+ // Toggle to split
+ cy.get('#toggle-split-pane').click()
+ cy.get('.panel-west').should('have.class', 'split-pane')
+
+ // Both panels should be visible
+ cy.get('#directory-body').should('have.css', 'visibility', 'visible')
+ cy.get('#outline-body').should('have.css', 'visibility', 'visible')
+ })
+
+ it('switches from tabbed to split when outline tab is active', () => {
+ // Click the outline tab
+ cy.get('#tabs-outline a.tab').last().click()
+ cy.get('#tabs-outline a.tab').last().should('have.class', 'active')
+ cy.get('#outline-body').should('have.css', 'visibility', 'visible')
+ cy.get('#directory-body').should('have.css', 'visibility', 'hidden')
+
+ // Toggle to split
+ cy.get('#toggle-split-pane').click()
+ cy.get('.panel-west').should('have.class', 'split-pane')
+
+ // Both panels should be visible
+ cy.get('#directory-body').should('have.css', 'visibility', 'visible')
+ cy.get('#outline-body').should('have.css', 'visibility', 'visible')
+ })
+
+ it('switches from split back to tabbed, restoring the active tab', () => {
+ // Click the outline tab, then toggle to split
+ cy.get('#tabs-outline a.tab').last().click()
+ cy.get('#toggle-split-pane').click()
+ cy.get('.panel-west').should('have.class', 'split-pane')
+
+ // Toggle back to tabbed
+ cy.get('#toggle-split-pane').click()
+ cy.get('.panel-west').should('not.have.class', 'split-pane')
+
+ // Outline tab was active, so outline should be visible and collections hidden
+ cy.get('#tabs-outline a.tab').last().should('have.class', 'active')
+ cy.get('#outline-body').should('have.css', 'visibility', 'visible')
+ cy.get('#directory-body').should('have.css', 'visibility', 'hidden')
+ })
+
+ it('switches from split back to tabbed with collections tab active', () => {
+ // Collections tab is active by default, toggle to split then back
+ cy.get('#toggle-split-pane').click()
+ cy.get('.panel-west').should('have.class', 'split-pane')
+
+ cy.get('#toggle-split-pane').click()
+ cy.get('.panel-west').should('not.have.class', 'split-pane')
+
+ // Collections tab was active, so collections should be visible and outline hidden
+ cy.get('#tabs-outline a.tab').first().should('have.class', 'active')
+ cy.get('#directory-body').should('have.css', 'visibility', 'visible')
+ cy.get('#outline-body').should('have.css', 'visibility', 'hidden')
+ })
+})
diff --git a/cypress/e2e/tab_overflow_spec.cy.js b/cypress/e2e/tab_overflow_spec.cy.js
new file mode 100644
index 00000000..12b59a59
--- /dev/null
+++ b/cypress/e2e/tab_overflow_spec.cy.js
@@ -0,0 +1,149 @@
+describe('Tab bar overflow controls', () => {
+ beforeEach(() => {
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ })
+
+ describe('control buttons exist', () => {
+ it('should render scroll-left, scroll-right, and list buttons', () => {
+ cy.get('#tab-scroll-left').should('exist').and('be.visible')
+ cy.get('#tab-scroll-right').should('exist').and('be.visible')
+ cy.get('#tab-list-btn').should('exist').and('be.visible')
+ })
+
+ it('scroll-left should be disabled initially (single tab)', () => {
+ cy.get('#tab-scroll-left').should('be.disabled')
+ })
+ })
+
+ describe('dropdown menu', () => {
+ it('tab-list-menu element should exist in DOM', () => {
+ cy.get('#tab-list-menu').should('exist')
+ })
+
+ it('tab-list-menu should be hidden initially', () => {
+ cy.get('#tab-list-menu').should('not.have.class', 'open')
+ cy.get('#tab-list-menu').should('have.css', 'display', 'none')
+ })
+
+ it('clicking the ▾ button should add .open class to menu', () => {
+ cy.get('#tab-list-btn').click()
+ cy.get('#tab-list-menu').should('have.class', 'open')
+ })
+
+ it('clicking the ▾ button should make menu visible', () => {
+ cy.get('#tab-list-btn').click()
+ cy.get('#tab-list-menu')
+ .should('have.class', 'open')
+ .and('have.css', 'display', 'block')
+ })
+
+ it('menu should contain at least one tab entry', () => {
+ cy.get('#tab-list-btn').click()
+ cy.get('#tab-list-menu li').should('have.length.at.least', 1)
+ })
+
+ it('active tab should be marked in the dropdown', () => {
+ cy.get('#tab-list-btn').click()
+ cy.get('#tab-list-menu li.active').should('have.length', 1)
+ })
+
+ it('menu should have non-zero dimensions when open', () => {
+ cy.get('#tab-list-btn').click()
+ cy.get('#tab-list-menu').should('have.class', 'open')
+ cy.get('#tab-list-menu').then(($menu) => {
+ const rect = $menu[0].getBoundingClientRect()
+ expect(rect.width, 'menu width').to.be.gt(50)
+ expect(rect.height, 'menu height').to.be.gt(10)
+ })
+ })
+
+ it('menu should be within the viewport when open', () => {
+ cy.get('#tab-list-btn').click()
+ cy.get('#tab-list-menu').should('have.class', 'open')
+ cy.window().then((win) => {
+ cy.get('#tab-list-menu').then(($menu) => {
+ const rect = $menu[0].getBoundingClientRect()
+ expect(rect.top, 'top within viewport').to.be.at.least(0)
+ expect(rect.bottom, 'bottom within viewport').to.be.at.most(win.innerHeight)
+ expect(rect.left, 'left within viewport').to.be.at.least(0)
+ expect(rect.right, 'right within viewport').to.be.at.most(win.innerWidth)
+ })
+ })
+ })
+
+ it('menu should not be clipped by parent overflow', () => {
+ cy.get('#tab-list-btn').click()
+ cy.get('#tab-list-menu').should('have.class', 'open')
+ // Walk up the DOM checking no ancestor clips the menu
+ cy.get('#tab-list-menu').then(($menu) => {
+ let el = $menu[0].parentElement
+ const menuRect = $menu[0].getBoundingClientRect()
+ const ancestors = []
+ while (el) {
+ const style = window.getComputedStyle(el)
+ const overflow = style.overflow + ' ' + style.overflowX + ' ' + style.overflowY
+ if (/hidden|clip/.test(overflow)) {
+ const parentRect = el.getBoundingClientRect()
+ ancestors.push({
+ tag: el.tagName,
+ id: el.id,
+ class: el.className,
+ overflow,
+ clips: menuRect.bottom > parentRect.bottom ||
+ menuRect.top < parentRect.top ||
+ menuRect.right > parentRect.right ||
+ menuRect.left < parentRect.left
+ })
+ }
+ el = el.parentElement
+ }
+ // Log all ancestors with overflow hidden/clip
+ ancestors.forEach(a => {
+ cy.log(`${a.tag}#${a.id}.${a.class} overflow="${a.overflow}" clips=${a.clips}`)
+ })
+ // Fail if any ancestor actually clips the menu
+ const clippers = ancestors.filter(a => a.clips)
+ const desc = clippers.map(c => `${c.tag}#${c.id}.${c.class} overflow="${c.overflow}"`).join('; ')
+ expect(clippers, 'no ancestor should clip the menu [' + desc + ']').to.have.length(0)
+ })
+ })
+
+ it('clicking ▾ again should close the menu (toggle)', () => {
+ cy.get('#tab-list-btn').click()
+ cy.get('#tab-list-menu').should('have.class', 'open')
+ cy.get('#tab-list-btn').click()
+ cy.get('#tab-list-menu').should('not.have.class', 'open')
+ })
+
+ it('pressing Escape should close the menu', () => {
+ cy.get('#tab-list-btn').click()
+ cy.get('#tab-list-menu').should('have.class', 'open')
+ cy.get('body').type('{esc}')
+ cy.get('#tab-list-menu').should('not.have.class', 'open')
+ })
+
+ it('clicking outside should close the menu', () => {
+ cy.get('#tab-list-btn').click()
+ cy.get('#tab-list-menu').should('have.class', 'open')
+ cy.get('#editor').click({ force: true })
+ cy.get('#tab-list-menu').should('not.have.class', 'open')
+ })
+
+ it('clicking a tab entry should switch to that tab and close menu', () => {
+ cy.get('#tab-list-btn').click()
+ cy.get('#tab-list-menu li').first().click()
+ cy.get('#tab-list-menu').should('not.have.class', 'open')
+ })
+ })
+
+ describe('ui-init.js script loading', () => {
+ it('should have loaded ui-init.js (button has click handler)', () => {
+ // Verify the script attached handlers by checking aria-expanded toggles
+ cy.get('#tab-list-btn').should('have.attr', 'aria-expanded', 'false')
+ cy.get('#tab-list-btn').click()
+ cy.get('#tab-list-btn').should('have.attr', 'aria-expanded', 'true')
+ })
+ })
+})
diff --git a/cypress/e2e/tag_scope_selection_spec.cy.js b/cypress/e2e/tag_scope_selection_spec.cy.js
new file mode 100644
index 00000000..29ea6235
--- /dev/null
+++ b/cypress/e2e/tag_scope_selection_spec.cy.js
@@ -0,0 +1,158 @@
+describe('Tag matching, scope breadcrumb, and selection match', () => {
+ beforeEach(() => {
+ cy.loginXHR('admin', '')
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.path', { timeout: 10000 }).should('contain', 'untitled-1')
+ cy.get('#user', { timeout: 15000 }).should('not.have.text', 'Login')
+ })
+
+ function newDocument(type, content) {
+ cy.window().then((win) => {
+ var editor = win.eXide.app.getEditor()
+ editor.newDocument(null, type)
+ })
+ cy.wait(300)
+ if (content) {
+ cy.window().then((win) => {
+ var view = win.eXide.app.getEditor().editor
+ view.dispatch({
+ changes: { from: 0, to: view.state.doc.length, insert: content }
+ })
+ view.focus()
+ })
+ }
+ }
+
+ function setCursorAt(line, col) {
+ cy.window().then((win) => {
+ var view = win.eXide.app.getEditor().editor
+ var lineInfo = view.state.doc.line(line)
+ var offset = lineInfo.from + col
+ view.dispatch({ selection: { anchor: offset } })
+ view.focus()
+ })
+ }
+
+ describe('Tag matching (XML/HTML)', () => {
+ var xml = '\n text\n \n'
+
+ it('has tag matching CSS styles defined', () => {
+ // Verify the cm-matchingTag CSS class is defined in the stylesheet
+ // (Tag matching works visually but is difficult to test in Cypress because
+ // CM6's syntax tree parsing and ViewPlugin update timing is unpredictable
+ // in headless browsers)
+ cy.document().then((doc) => {
+ var sheets = doc.styleSheets
+ var found = false
+ for (var i = 0; i < sheets.length; i++) {
+ try {
+ var rules = sheets[i].cssRules
+ for (var j = 0; j < rules.length; j++) {
+ if (rules[j].selectorText && rules[j].selectorText.includes('cm-matchingTag')) {
+ found = true
+ break
+ }
+ }
+ } catch (e) { /* cross-origin stylesheet */ }
+ if (found) break
+ }
+ expect(found).to.be.true
+ })
+ })
+ })
+
+ describe('Scope breadcrumb', () => {
+ it('shows XPath scope for XML documents', () => {
+ var xml = '\n \n'
+ newDocument('xml', xml)
+
+ // Place cursor inside content
+ setCursorAt(3, 12)
+ cy.wait(300)
+
+ cy.get('#status-scope', { timeout: 3000 })
+ .should('be.visible')
+ .invoke('text')
+ .should('contain', 'root')
+ .and('contain', 'title')
+ })
+
+ it('shows scope for XQuery function body', () => {
+ var code = 'declare function local:test() {\n let $x := 1\n return $x\n};'
+ cy.window().then((win) => {
+ var editor = win.eXide.app.getEditor()
+ var view = editor.editor
+ var doc = editor.getActiveDocument()
+ editor.validator.setEnabled(false)
+ view.dispatch({
+ changes: { from: 0, to: view.state.doc.length, insert: code }
+ })
+ doc.lastValidation = 0
+ doc.getModeHelper().parseXQuery(doc)
+ view.focus()
+ editor.validator.setEnabled(true)
+ })
+
+ // Place cursor inside the function body (line 2, col 5 = inside "let")
+ setCursorAt(2, 5)
+ cy.wait(500)
+
+ // Scope should show the function context
+ cy.get('#status-scope', { timeout: 5000 })
+ .should('be.visible')
+ .invoke('text')
+ .should('have.length.greaterThan', 0)
+ })
+
+ it('hides scope when not inside a named context', () => {
+ // Default untitled document with version declaration
+ cy.get('#status-scope')
+ .should('not.be.visible')
+ })
+ })
+
+ describe('Selection match highlighting', () => {
+ it('highlights matching text when a word is selected', () => {
+ var code = 'let $total := 42\nlet $other := $total\nreturn $total'
+ cy.window().then((win) => {
+ var view = win.eXide.app.getEditor().editor
+ view.dispatch({
+ changes: { from: 0, to: view.state.doc.length, insert: code }
+ })
+ // Select "$total" at the first occurrence (offset 4-10)
+ view.dispatch({ selection: { anchor: 4, head: 10 } })
+ view.focus()
+ })
+
+ cy.wait(300)
+
+ // CM6 highlightSelectionMatches should mark other occurrences
+ cy.get('.cm-selectionMatch', { timeout: 3000 })
+ .should('have.length.at.least', 1)
+ })
+
+ it('removes highlights when selection is cleared', () => {
+ var code = 'let $total := 42\nreturn $total'
+ cy.window().then((win) => {
+ var view = win.eXide.app.getEditor().editor
+ view.dispatch({
+ changes: { from: 0, to: view.state.doc.length, insert: code }
+ })
+ // Select "$total"
+ view.dispatch({ selection: { anchor: 4, head: 10 } })
+ view.focus()
+ })
+ cy.wait(300)
+
+ // Clear selection by clicking
+ cy.window().then((win) => {
+ var view = win.eXide.app.getEditor().editor
+ view.dispatch({ selection: { anchor: 0 } })
+ })
+ cy.wait(300)
+
+ cy.get('.cm-selectionMatch').should('not.exist')
+ })
+ })
+})
diff --git a/cypress/e2e/websocket_spec.cy.js b/cypress/e2e/websocket_spec.cy.js
new file mode 100644
index 00000000..73cefd51
--- /dev/null
+++ b/cypress/e2e/websocket_spec.cy.js
@@ -0,0 +1,115 @@
+describe('WebSocket transport', () => {
+ beforeEach(() => {
+ cy.loginXHR('admin', '')
+ cy.visit('/eXide/index.html')
+ cy.reload(true)
+ cy.get('.cm-editor', { timeout: 10000 }).should('exist')
+ cy.get('#user', { timeout: 15000 }).should('not.have.text', 'Login')
+ })
+
+ /** Retry until ws.isConnected() returns true, or skip the test. */
+ function waitForWs(skipCtx) {
+ return cy.window().should((win) => {
+ if (!win.eXide.ws || !win.eXide.ws.isConnected()) {
+ throw new Error('WebSocket not yet connected')
+ }
+ }).then(() => {
+ return cy.window()
+ }, () => {
+ // If it never connected, skip
+ skipCtx.skip()
+ })
+ }
+
+ it('auto-connects to WebSocket on startup', () => {
+ cy.window().then((win) => {
+ expect(win.eXide.ws).to.exist
+ expect(win.eXide.ws.isConnected).to.be.a('function')
+ })
+ })
+
+ it('connects to /exist/ws endpoint', function () {
+ waitForWs(this)
+ cy.window().then((win) => {
+ expect(win.eXide.ws.isConnected()).to.be.true
+ })
+ })
+
+ it('handles ping messages without errors', function () {
+ waitForWs(this)
+ cy.window().then((win) => {
+ expect(win.eXide.ws.isConnected()).to.be.true
+ })
+ })
+
+ it('exposes send and notify methods', () => {
+ cy.window().then((win) => {
+ expect(win.eXide.ws.send).to.be.a('function')
+ expect(win.eXide.ws.notify).to.be.a('function')
+ expect(win.eXide.ws.on).to.be.a('function')
+ expect(win.eXide.ws.off).to.be.a('function')
+ })
+ })
+
+ it('receives monitoring data via WebSocket push', function () {
+ waitForWs(this)
+ cy.window().then((win) => {
+ var received = null
+ win.eXide.ws.on("exist/metrics", function (data) {
+ received = data
+ })
+
+ // Trigger the monitoring push via HTTP (which pushes to WebSocket)
+ cy.request({
+ method: 'POST',
+ url: '/eXide/api/ws/monitor',
+ headers: { 'Content-Type': 'application/json' },
+ failOnStatusCode: false
+ }).then(() => {
+ // Retry until WebSocket delivers the message
+ cy.wrap(null, { timeout: 5000 }).should(() => {
+ expect(received).to.not.be.null
+ expect(received.type).to.eq("exist/metrics")
+ expect(received.version).to.be.a("string")
+ })
+ })
+ })
+ })
+
+ it('receives diagnostics push after compilation', function () {
+ waitForWs(this)
+ cy.window().then((win) => {
+ var received = null
+ win.eXide.ws.on("textDocument/publishDiagnostics", function (data) {
+ received = data
+ })
+
+ // Trigger compilation with invalid code
+ cy.request({
+ method: 'POST',
+ url: '/eXide/api/query/compile',
+ headers: { 'Content-Type': 'application/json' },
+ body: { query: 'let $x := retrun $x', base: 'xmldb:exist:///db', uri: 'test.xq' },
+ failOnStatusCode: false
+ }).then(() => {
+ cy.wrap(null, { timeout: 5000 }).should(() => {
+ expect(received).to.not.be.null
+ expect(received.type).to.eq("textDocument/publishDiagnostics")
+ expect(received.uri).to.eq("test.xq")
+ expect(received.diagnostics).to.be.an("array")
+ expect(received.diagnostics.length).to.be.greaterThan(0)
+ })
+ })
+ })
+ })
+
+ it('falls back gracefully when WebSocket unavailable', () => {
+ cy.get('.cm-editor').should('exist')
+ cy.get('#status-bar').should('exist')
+ cy.window().then((win) => {
+ var editor = win.eXide.app.getEditor()
+ expect(editor).to.exist
+ expect(editor.editor).to.exist
+ })
+ })
+})
diff --git a/cypress/support/commands.js b/cypress/support/commands.js
index 882eda0d..d336273d 100644
--- a/cypress/support/commands.js
+++ b/cypress/support/commands.js
@@ -28,7 +28,7 @@ Cypress.Commands.add("loginXHR", (user, password) => {
cy.session(['xhr', user, password], () => {
cy.request({
method: 'POST',
- url: '/eXide/login',
+ url: '/eXide/api/auth/session',
form: true,
body: { user, password },
headers: { 'Accept': 'application/json' }
@@ -36,6 +36,33 @@ Cypress.Commands.add("loginXHR", (user, password) => {
})
})
+// cy.dismissDialog() -- wait for editor to load and login to complete, then dismiss any visible dialog
+Cypress.Commands.add("dismissDialog", () => {
+ // Wait for the editor to be initialized
+ cy.get('#editor .cm-editor', { timeout: 10000 }).should('exist')
+ // Wait for the login check to complete (user text changes from default)
+ cy.get('#user', { timeout: 10000 }).should('not.have.text', 'Login')
+ // Dismiss any startup dialogs
+ cy.get('body').then(($body) => {
+ const btn = $body.find('.eXide-dialog[open] .eXide-dialog-buttons button')
+ if (btn.length) {
+ btn[0].click()
+ }
+ })
+})
+
+// cy.cleanupTestFiles() -- removes cypress-test-* files from /db
+Cypress.Commands.add("cleanupTestFiles", () => {
+ cy.loginXHR('admin', '')
+ cy.request({
+ method: 'POST',
+ url: '/exist/rest/db',
+ headers: { 'Content-Type': 'application/xquery' },
+ body: 'for $r in xmldb:get-child-resources("/db") where starts-with($r, "cypress-test-") return xmldb:remove("/db", $r)',
+ failOnStatusCode: false
+ })
+})
+
// cy.logout() -- does not work reliably
Cypress.Commands.add("logout", () => cy.request('/eXide/index.html', {logout: true}))
@@ -52,18 +79,15 @@ Cypress.Commands.add("setConf", function (executeQuery, restrictAccess) {
const body = getConf(executeQuery, restrictAccess);
const confFilePath = "/apps/eXide/configuration.xml"
cy.request({
- method: 'POST',
- url: `/eXide/store/db${confFilePath}`,
+ method: 'PUT',
+ url: `/eXide/api/storage${confFilePath}`,
headers: {
- 'Content-Type': 'application/xml',
- 'Content-length': body.length
+ 'Content-Type': 'application/xml'
},
body
})
.then((response) => {
- const parsed = JSON.parse(response.body)
- expect(parsed).to.have.property('status', 'ok')
- expect(parsed).to.have.property('externalLink', `/exist${confFilePath}`)
+ expect(response.body).to.have.property('status', 'ok')
})
})
diff --git a/expath-pkg.xml b/expath-pkg.xml
new file mode 100644
index 00000000..c2db0535
--- /dev/null
+++ b/expath-pkg.xml
@@ -0,0 +1,7 @@
+
+
+ eXide - XQuery IDE
+
+
+
+
diff --git a/expath-pkg.xml.tmpl b/expath-pkg.xml.tmpl
index 6a8f39eb..a65ae974 100644
--- a/expath-pkg.xml.tmpl
+++ b/expath-pkg.xml.tmpl
@@ -2,4 +2,6 @@
eXide - XQuery IDE
+
+
diff --git a/grammars/README.md b/grammars/README.md
new file mode 100644
index 00000000..f4f12633
--- /dev/null
+++ b/grammars/README.md
@@ -0,0 +1,133 @@
+# XQuery Grammars for eXide
+
+eXide uses a [REx](https://www.bottlecaps.de/rex/)-generated parser built from W3C EBNF grammars. This directory contains the source grammars and their combined variants.
+
+## Grammar Files
+
+### Reference grammars (individual)
+
+| File | Description |
+|------|-------------|
+| `XQuery-31.ebnf` | Base XQuery 3.1 |
+| `XQuery-40.ebnf` | Base XQuery 4.0 (from REx repo) |
+| `XQuery-Update-30.ebnf` | W3C XQuery Update Facility 3.0 |
+| `XQuery-FullText-10.ebnf` | XQuery Full Text 1.0 |
+| `XQuery-Update-eXist-Legacy.ebnf` | XQUFEL (eXist-db proprietary update syntax) |
+
+### Combined grammars
+
+| File | Description |
+|------|-------------|
+| `XQuery-31-Family-XQUFEL.ebnf` | **Current parser source** — XQ 3.1 + Update 3.0 + Full Text 1.0 + XQUFEL |
+| `XQuery-40-Family-XQUFEL.ebnf` | Future upgrade — XQ 4.0 + Update 3.0 + Full Text 1.0 + XQUFEL |
+
+The "Family" grammars are created by merging the individual grammars. See [Merging Extension Grammars](#merging-extension-grammars) below.
+
+## Generating the Parser
+
+Parser generation is automated via `tools/generate-parser.js`:
+
+```bash
+npm run generate-parser
+```
+
+This runs REx with the correct options, applies post-generation patches (Nonterminal constructor fix for REx v6.1), and appends export boilerplate. The output is written to `src/parser/XQueryParser.js`.
+
+To generate from a different grammar:
+
+```bash
+node tools/generate-parser.js --grammar grammars/XQuery-40-Family-XQUFEL.ebnf
+```
+
+### REx Options
+
+eXide uses REx in **LL(3) mode** with these flags:
+
+```
+-ll 3 -backtrack -tree -javascript -name XQueryParser
+```
+
+| Flag | Purpose |
+|------|---------|
+| `-ll 3` | LL parser with lookahead depth 3 |
+| `-backtrack` | Enable backtracking for ambiguous constructs in the XQuery grammar |
+| `-tree` | Generate `TopDownTreeBuilder` for AST access |
+| `-javascript` | JavaScript output |
+| `-name XQueryParser` | Constructor/class name |
+
+**Why LL mode?** The `-tree` flag (TopDownTreeBuilder) requires LL mode, and LL parsers produce top-down parse trees that map directly to eXide's expected AST node shape.
+
+### Testing a Grammar
+
+To verify a grammar generates without errors before running the full pipeline:
+
+```bash
+java -cp tools REx grammars/.ebnf -ll 3 -backtrack -tree -javascript -name XQueryParser
+```
+
+Exit code 0 with no output means success. REx prints errors to stderr if the grammar has issues.
+
+## Preparing New Grammars
+
+The base grammars from the [REx repository](https://github.com/GuntherRademworker/rex-parser-generator) are **reference grammars** written in standard W3C EBNF notation. They may use left-recursion, which is valid EBNF but **incompatible with LL parsing**. Before a new grammar can be used with eXide, two steps are typically needed:
+
+### 1. Eliminate Left-Recursion
+
+LL parsers cannot handle left-recursive productions. The pattern to fix is:
+
+```
+(* Left-recursive — REx will reject this in LL mode *)
+A ::= B
+ | A Suffix
+
+(* Iterative equivalent — LL-compatible *)
+A ::= B Suffix*
+```
+
+**Example: XQuery 4.0 PostfixExpr**
+
+The XQ 4.0 grammar defines `PostfixExpr` via five separate left-recursive productions:
+
+```
+PostfixExpr ::= PrimaryExpr | FilterExpr | DynamicFunctionCall
+ | LookupExpr | MethodCall | FilterExprAM
+FilterExpr ::= PostfixExpr Predicate
+DynamicFunctionCall ::= PostfixExpr PositionalArgumentList
+LookupExpr ::= PostfixExpr Lookup
+MethodCall ::= PostfixExpr '=?>' NCName PositionalArgumentList
+FilterExprAM ::= PostfixExpr '?[' Expr ']'
+```
+
+The fix folds all suffixes into an iterative loop on `PostfixExpr` and replaces the left-recursive productions with suffix-only variants:
+
+```
+PostfixExpr ::= PrimaryExpr ( Predicate | PositionalArgumentList
+ | Lookup | MethodCallSuffix | FilterExprAMSuffix )*
+MethodCallSuffix ::= '=?>' NCName PositionalArgumentList
+FilterExprAMSuffix ::= '?[' Expr ']'
+```
+
+`FilterExpr`, `DynamicFunctionCall`, and `LookupExpr` are deleted entirely (their suffixes — `Predicate`, `PositionalArgumentList`, `Lookup` — are already defined elsewhere).
+
+Similarly, `PositionalArguments` needed the same treatment:
+
+```
+(* Left-recursive *)
+PositionalArguments ::= Argument | PositionalArguments ',' Argument
+
+(* Iterative *)
+PositionalArguments ::= Argument ( ',' Argument )*
+```
+
+### 2. Merge Extension Grammars
+
+To build a combined grammar (e.g., XQuery + Update + Full Text + XQUFEL):
+
+1. Start with the base grammar (e.g., `XQuery-40.ebnf`)
+2. Add productions from each extension grammar, inserting new alternatives into existing productions where the extension spec indicates
+3. Resolve any naming conflicts between extensions
+4. Test the combined grammar with REx
+
+### Post-Generation Patches
+
+REx v6.1 generates a `Nonterminal` constructor that doesn't expose `name` and `children` as object properties (they're only closure variables). `tools/generate-parser.js` patches this automatically — see the `patches` array in its CONFIG object.
diff --git a/grammars/XQuery-31-Family-XQUFEL.ebnf b/grammars/XQuery-31-Family-XQUFEL.ebnf
new file mode 100644
index 00000000..1adfb066
--- /dev/null
+++ b/grammars/XQuery-31-Family-XQUFEL.ebnf
@@ -0,0 +1,1045 @@
+/* Combined grammar: XQuery 3.1 + XQUF 3.0 + XQFT 1.0 + XQUFEL (XQuery Update Facility eXist Legacy) */
+
+XQuery ::= Module EOF
+Module ::= VersionDecl? ( LibraryModule | MainModule )
+VersionDecl
+ ::= 'xquery' ( 'encoding' StringLiteral | 'version' StringLiteral ( 'encoding' StringLiteral )? ) Separator
+MainModule
+ ::= Prolog QueryBody
+LibraryModule
+ ::= ModuleDecl Prolog
+ModuleDecl
+ ::= 'module' 'namespace' NCName '=' URILiteral Separator
+Prolog ::= ( ( DefaultNamespaceDecl | Setter | NamespaceDecl | Import | FTOptionDecl ) Separator )* ( ( ContextItemDecl | AnnotatedDecl | OptionDecl ) Separator )*
+Separator
+ ::= ';'
+Setter ::= BoundarySpaceDecl
+ | DefaultCollationDecl
+ | BaseURIDecl
+ | ConstructionDecl
+ | OrderingModeDecl
+ | EmptyOrderDecl
+ | CopyNamespacesDecl
+ | DecimalFormatDecl
+ | RevalidationDecl
+RevalidationDecl
+ ::= 'declare' 'revalidation' ( 'strict' | 'lax' | 'skip' )
+BoundarySpaceDecl
+ ::= 'declare' 'boundary-space' ( 'preserve' | 'strip' )
+DefaultCollationDecl
+ ::= 'declare' 'default' 'collation' URILiteral
+BaseURIDecl
+ ::= 'declare' 'base-uri' URILiteral
+ConstructionDecl
+ ::= 'declare' 'construction' ( 'strip' | 'preserve' )
+OrderingModeDecl
+ ::= 'declare' 'ordering' ( 'ordered' | 'unordered' )
+EmptyOrderDecl
+ ::= 'declare' 'default' 'order' 'empty' ( 'greatest' | 'least' )
+CopyNamespacesDecl
+ ::= 'declare' 'copy-namespaces' PreserveMode ',' InheritMode
+PreserveMode
+ ::= 'preserve'
+ | 'no-preserve'
+InheritMode
+ ::= 'inherit'
+ | 'no-inherit'
+DecimalFormatDecl
+ ::= 'declare' ( 'decimal-format' EQName | 'default' 'decimal-format' ) ( DFPropertyName '=' StringLiteral )*
+DFPropertyName
+ ::= 'decimal-separator'
+ | 'grouping-separator'
+ | 'infinity'
+ | 'minus-sign'
+ | 'NaN'
+ | 'percent'
+ | 'per-mille'
+ | 'zero-digit'
+ | 'digit'
+ | 'pattern-separator'
+ | 'exponent-separator'
+Import ::= SchemaImport
+ | ModuleImport
+SchemaImport
+ ::= 'import' 'schema' SchemaPrefix? URILiteral ( 'at' URILiteral ( ',' URILiteral )* )?
+SchemaPrefix
+ ::= 'namespace' NCName '='
+ | 'default' 'element' 'namespace'
+ModuleImport
+ ::= 'import' 'module' ( 'namespace' NCName '=' )? URILiteral ( 'at' URILiteral ( ',' URILiteral )* )?
+NamespaceDecl
+ ::= 'declare' 'namespace' NCName '=' URILiteral
+DefaultNamespaceDecl
+ ::= 'declare' 'default' ( 'element' | 'function' ) 'namespace' URILiteral
+AnnotatedDecl
+ ::= 'declare' Annotation* ( VarDecl | FunctionDecl )
+Annotation
+ ::= '%' EQName ( '(' Literal ( ',' Literal )* ')' )?
+VarDecl ::= 'variable' '$' VarName TypeDeclaration? ( ':=' VarValue | 'external' ( ':=' VarDefaultValue )? )
+VarValue ::= ExprSingle
+VarDefaultValue
+ ::= ExprSingle
+ContextItemDecl
+ ::= 'declare' 'context' 'item' ( 'as' ItemType )? ( ':=' VarValue | 'external' ( ':=' VarDefaultValue )? )
+FunctionDecl
+ ::= 'function' EQName '(' ParamList? ')' ( 'as' SequenceType )? ( FunctionBody | 'external' )
+ParamList
+ ::= Param ( ',' Param )*
+Param ::= '$' EQName TypeDeclaration?
+FunctionBody
+ ::= EnclosedExpr
+EnclosedExpr
+ ::= '{' Expr? '}'
+OptionDecl
+ ::= 'declare' 'option' EQName StringLiteral
+QueryBody
+ ::= Expr
+Expr ::= ExprSingle ( ',' ExprSingle )*
+ExprSingle
+ ::= FLWORExpr
+ | QuantifiedExpr
+ | SwitchExpr
+ | TypeswitchExpr
+ | IfExpr
+ | TryCatchExpr
+ | InsertExpr
+ | DeleteExpr
+ | RenameExpr
+ | ReplaceExpr
+ | TransformExpr
+ | ExistUpdateExpr
+ | OrExpr
+FLWORExpr
+ ::= InitialClause IntermediateClause* ReturnClause
+InitialClause
+ ::= ForClause
+ | LetClause
+ | WindowClause
+IntermediateClause
+ ::= InitialClause
+ | WhereClause
+ | GroupByClause
+ | OrderByClause
+ | CountClause
+ForClause
+ ::= 'for' ForBinding ( ',' ForBinding )*
+ForBinding
+ ::= '$' VarName TypeDeclaration? AllowingEmpty? PositionalVar? FTScoreVar? 'in' ExprSingle
+AllowingEmpty
+ ::= 'allowing' 'empty'
+PositionalVar
+ ::= 'at' '$' VarName
+LetClause
+ ::= 'let' LetBinding ( ',' LetBinding )*
+LetBinding
+ ::= ( '$' VarName TypeDeclaration? | FTScoreVar ) ':=' ExprSingle
+WindowClause
+ ::= 'for' ( TumblingWindowClause | SlidingWindowClause )
+TumblingWindowClause
+ ::= 'tumbling' 'window' '$' VarName TypeDeclaration? 'in' ExprSingle WindowStartCondition WindowEndCondition?
+SlidingWindowClause
+ ::= 'sliding' 'window' '$' VarName TypeDeclaration? 'in' ExprSingle WindowStartCondition WindowEndCondition
+WindowStartCondition
+ ::= 'start' WindowVars 'when' ExprSingle
+WindowEndCondition
+ ::= 'only'? 'end' WindowVars 'when' ExprSingle
+WindowVars
+ ::= ( '$' CurrentItem )? PositionalVar? ( 'previous' '$' PreviousItem )? ( 'next' '$' NextItem )?
+CurrentItem
+ ::= EQName
+PreviousItem
+ ::= EQName
+NextItem ::= EQName
+CountClause
+ ::= 'count' '$' VarName
+WhereClause
+ ::= 'where' ExprSingle
+GroupByClause
+ ::= 'group' 'by' GroupingSpecList
+GroupingSpecList
+ ::= GroupingSpec ( ',' GroupingSpec )*
+GroupingSpec
+ ::= GroupingVariable ( TypeDeclaration? ':=' ExprSingle )? ( 'collation' URILiteral )?
+GroupingVariable
+ ::= '$' VarName
+OrderByClause
+ ::= ( 'order' 'by' | 'stable' 'order' 'by' ) OrderSpecList
+OrderSpecList
+ ::= OrderSpec ( ',' OrderSpec )*
+OrderSpec
+ ::= ExprSingle OrderModifier
+OrderModifier
+ ::= ( 'ascending' | 'descending' )? ( 'empty' ( 'greatest' | 'least' ) )? ( 'collation' URILiteral )?
+ReturnClause
+ ::= 'return' ExprSingle
+QuantifiedExpr
+ ::= ( 'some' | 'every' ) '$' VarName TypeDeclaration? 'in' ExprSingle ( ',' '$' VarName TypeDeclaration? 'in' ExprSingle )* 'satisfies' ExprSingle
+SwitchExpr
+ ::= 'switch' '(' Expr ')' SwitchCaseClause+ 'default' 'return' ExprSingle
+SwitchCaseClause
+ ::= ( 'case' SwitchCaseOperand )+ 'return' ExprSingle
+SwitchCaseOperand
+ ::= ExprSingle
+TypeswitchExpr
+ ::= 'typeswitch' '(' Expr ')' CaseClause+ 'default' ( '$' VarName )? 'return' ExprSingle
+CaseClause
+ ::= 'case' ( '$' VarName 'as' )? SequenceTypeUnion 'return' ExprSingle
+SequenceTypeUnion
+ ::= SequenceType ( '|' SequenceType )*
+IfExpr ::= 'if' '(' Expr ')' 'then' ExprSingle 'else' ExprSingle
+TryCatchExpr
+ ::= TryClause CatchClause+
+TryClause
+ ::= 'try' EnclosedTryTargetExpr
+EnclosedTryTargetExpr
+ ::= EnclosedExpr
+CatchClause
+ ::= 'catch' CatchErrorList EnclosedExpr
+CatchErrorList
+ ::= NameTest ( '|' NameTest )*
+InsertExpr
+ ::= 'insert' ( 'node' | 'nodes' ) SourceExpr InsertExprTargetChoice TargetExpr
+InsertExprTargetChoice
+ ::= ( 'as' ( 'first' | 'last' ) )? 'into'
+ | 'after'
+ | 'before'
+SourceExpr
+ ::= ExprSingle
+TargetExpr
+ ::= ExprSingle
+DeleteExpr
+ ::= 'delete' ( 'node' | 'nodes' ) TargetExpr
+ReplaceExpr
+ ::= 'replace' ( 'value' 'of' )? 'node' TargetExpr 'with' ExprSingle
+RenameExpr
+ ::= 'rename' 'node' TargetExpr 'as' NewNameExpr
+NewNameExpr
+ ::= ExprSingle
+TransformExpr
+ ::= 'copy' '$' VarName ':=' ExprSingle ( ',' '$' VarName ':=' ExprSingle )* 'modify' ExprSingle 'return' ExprSingle
+ExistUpdateExpr
+ ::= 'update' ( ExistInsertExpr | ExistReplaceExpr | ExistValueExpr | ExistDeleteExpr | ExistRenameExpr )
+ExistInsertExpr
+ ::= 'insert' ExprSingle ( 'into' | 'following' | 'preceding' ) ExprSingle
+ExistReplaceExpr
+ ::= 'replace' ExprSingle 'with' ExprSingle
+ExistValueExpr
+ ::= 'value' ExprSingle 'with' ExprSingle
+ExistDeleteExpr
+ ::= 'delete' ExprSingle
+ExistRenameExpr
+ ::= 'rename' ExprSingle 'as' ExprSingle
+OrExpr ::= AndExpr ( 'or' AndExpr )*
+AndExpr ::= ComparisonExpr ( 'and' ComparisonExpr )*
+ComparisonExpr
+ ::= FTContainsExpr ( ( ValueComp | GeneralComp | NodeComp ) FTContainsExpr )?
+FTContainsExpr
+ ::= StringConcatExpr ( 'contains' 'text' FTSelection FTIgnoreOption? )?
+StringConcatExpr
+ ::= RangeExpr ( '||' RangeExpr )*
+RangeExpr
+ ::= AdditiveExpr ( 'to' AdditiveExpr )?
+AdditiveExpr
+ ::= MultiplicativeExpr ( ( '+' | '-' ) MultiplicativeExpr )*
+MultiplicativeExpr
+ ::= UnionExpr ( ( '*' | 'div' | 'idiv' | 'mod' ) UnionExpr )*
+UnionExpr
+ ::= IntersectExceptExpr ( ( 'union' | '|' ) IntersectExceptExpr )*
+IntersectExceptExpr
+ ::= InstanceofExpr ( ( 'intersect' | 'except' ) InstanceofExpr )*
+InstanceofExpr
+ ::= TreatExpr ( 'instance' 'of' SequenceType )?
+TreatExpr
+ ::= CastableExpr ( 'treat' 'as' SequenceType )?
+CastableExpr
+ ::= CastExpr ( 'castable' 'as' SingleType )?
+CastExpr ::= ArrowExpr ( 'cast' 'as' SingleType )?
+ArrowExpr
+ ::= UnaryExpr ( '=>' ArrowFunctionSpecifier ArgumentList )*
+UnaryExpr
+ ::= ( '-' | '+' )* ValueExpr
+ValueExpr
+ ::= ValidateExpr
+ | ExtensionExpr
+ | SimpleMapExpr
+GeneralComp
+ ::= '='
+ | '!='
+ | '<'
+ | '<='
+ | '>'
+ | '>='
+ValueComp
+ ::= 'eq'
+ | 'ne'
+ | 'lt'
+ | 'le'
+ | 'gt'
+ | 'ge'
+NodeComp ::= 'is'
+ | '<<'
+ | '>>'
+ValidateExpr
+ ::= 'validate' ( ValidationMode | 'type' TypeName )? '{' Expr '}'
+ValidationMode
+ ::= 'lax'
+ | 'strict'
+ExtensionExpr
+ ::= Pragma+ '{' Expr? '}'
+Pragma ::= '(#' S? EQName ( S PragmaContents )? '#)'
+ /* ws: explicit */
+SimpleMapExpr
+ ::= PathExpr ( '!' PathExpr )*
+PathExpr ::= '/' ( RelativePathExpr / )
+ | '//' RelativePathExpr
+ | RelativePathExpr
+RelativePathExpr
+ ::= StepExpr ( ( '/' | '//' ) StepExpr )*
+StepExpr ::= PostfixExpr
+ | AxisStep
+AxisStep ::= ( ReverseStep | ForwardStep ) PredicateList
+ForwardStep
+ ::= ForwardAxis NodeTest
+ | AbbrevForwardStep
+ForwardAxis
+ ::= 'child' '::'
+ | 'descendant' '::'
+ | 'attribute' '::'
+ | 'self' '::'
+ | 'descendant-or-self' '::'
+ | 'following-sibling' '::'
+ | 'following' '::'
+AbbrevForwardStep
+ ::= '@'? NodeTest
+ReverseStep
+ ::= ReverseAxis NodeTest
+ | AbbrevReverseStep
+ReverseAxis
+ ::= 'parent' '::'
+ | 'ancestor' '::'
+ | 'preceding-sibling' '::'
+ | 'preceding' '::'
+ | 'ancestor-or-self' '::'
+AbbrevReverseStep
+ ::= '..'
+NodeTest ::= KindTest
+ | NameTest
+NameTest ::= EQName
+ | Wildcard
+PostfixExpr
+ ::= PrimaryExpr ( Predicate | ArgumentList | Lookup )*
+ArgumentList
+ ::= '(' ( Argument ( ',' Argument )* )? ')'
+PredicateList
+ ::= Predicate*
+Predicate
+ ::= '[' Expr ']'
+Lookup ::= '?' KeySpecifier
+KeySpecifier
+ ::= NCName
+ | IntegerLiteral
+ | ParenthesizedExpr
+ | '*'
+ArrowFunctionSpecifier
+ ::= EQName
+ | VarRef
+ | ParenthesizedExpr
+PrimaryExpr
+ ::= Literal
+ | VarRef
+ | ParenthesizedExpr
+ | ContextItemExpr
+ | FunctionCall
+ | OrderedExpr
+ | UnorderedExpr
+ | NodeConstructor
+ | FunctionItemExpr
+ | MapConstructor
+ | ArrayConstructor
+ | StringConstructor
+ | UnaryLookup
+Literal ::= NumericLiteral
+ | StringLiteral
+NumericLiteral
+ ::= IntegerLiteral
+ | DecimalLiteral
+ | DoubleLiteral
+VarRef ::= '$' VarName
+VarName ::= EQName
+ParenthesizedExpr
+ ::= '(' Expr? ')'
+ContextItemExpr
+ ::= '.'
+OrderedExpr
+ ::= 'ordered' EnclosedExpr
+UnorderedExpr
+ ::= 'unordered' EnclosedExpr
+FunctionCall
+ ::= FunctionEQName ArgumentList
+Argument ::= ExprSingle
+ | ArgumentPlaceholder
+ArgumentPlaceholder
+ ::= '?'
+NodeConstructor
+ ::= DirectConstructor
+ | ComputedConstructor
+DirectConstructor
+ ::= DirElemConstructor
+ | DirCommentConstructor
+ | DirPIConstructor
+DirElemConstructor
+ ::= '<' QName DirAttributeList ( '/>' | '>' DirElemContent* '' QName S? '>' )
+ /* ws: explicit */
+DirAttributeList
+ ::= ( S ( QName S? '=' S? DirAttributeValue )? )*
+ /* ws: explicit */
+DirAttributeValue
+ ::= '"' ( EscapeQuot | QuotAttrValueContent )* '"'
+ | "'" ( EscapeApos | AposAttrValueContent )* "'"
+ /* ws: explicit */
+QuotAttrValueContent
+ ::= QuotAttrContentChar
+ | CommonContent
+AposAttrValueContent
+ ::= AposAttrContentChar
+ | CommonContent
+DirElemContent
+ ::= DirectConstructor
+ | CDataSection
+ | CommonContent
+ | ElementContentChar
+CommonContent
+ ::= PredefinedEntityRef
+ | CharRef
+ | '{{'
+ | '}}'
+ | EnclosedExpr
+DirCommentConstructor
+ ::= ''
+ /* ws: explicit */
+DirPIConstructor
+ ::= '' PITarget ( S DirPIContents )? '?>'
+ /* ws: explicit */
+CDataSection
+ ::= ''
+ /* ws: explicit */
+ComputedConstructor
+ ::= CompDocConstructor
+ | CompElemConstructor
+ | CompAttrConstructor
+ | CompNamespaceConstructor
+ | CompTextConstructor
+ | CompCommentConstructor
+ | CompPIConstructor
+CompDocConstructor
+ ::= 'document' EnclosedExpr
+CompElemConstructor
+ ::= 'element' ( EQName | '{' Expr '}' ) EnclosedContentExpr
+EnclosedContentExpr
+ ::= EnclosedExpr
+CompAttrConstructor
+ ::= 'attribute' ( EQName | '{' Expr '}' ) EnclosedExpr
+CompNamespaceConstructor
+ ::= 'namespace' ( Prefix | EnclosedPrefixExpr ) EnclosedURIExpr
+Prefix ::= NCName
+EnclosedPrefixExpr
+ ::= EnclosedExpr
+EnclosedURIExpr
+ ::= EnclosedExpr
+CompTextConstructor
+ ::= 'text' EnclosedExpr
+CompCommentConstructor
+ ::= 'comment' EnclosedExpr
+CompPIConstructor
+ ::= 'processing-instruction' ( NCName | '{' Expr '}' ) EnclosedExpr
+FunctionItemExpr
+ ::= NamedFunctionRef
+ | InlineFunctionExpr
+NamedFunctionRef
+ ::= EQName '#' IntegerLiteral
+InlineFunctionExpr
+ ::= Annotation* 'function' '(' ParamList? ')' ( 'as' SequenceType )? FunctionBody
+MapConstructor
+ ::= 'map' '{' ( MapConstructorEntry ( ',' MapConstructorEntry )* )? '}'
+MapConstructorEntry
+ ::= MapKeyExpr ':' MapValueExpr
+MapKeyExpr
+ ::= ExprSingle
+MapValueExpr
+ ::= ExprSingle
+ArrayConstructor
+ ::= SquareArrayConstructor
+ | CurlyArrayConstructor
+SquareArrayConstructor
+ ::= '[' ( ExprSingle ( ',' ExprSingle )* )? ']'
+CurlyArrayConstructor
+ ::= 'array' EnclosedExpr
+StringConstructor
+ ::= '``[' StringConstructorContent ']``'
+ /* ws: explicit */
+StringConstructorContent
+ ::= StringConstructorChars ( StringConstructorInterpolation StringConstructorChars )*
+ /* ws: explicit */
+StringConstructorInterpolation
+ ::= '`{' Expr? '}`'
+UnaryLookup
+ ::= '?' KeySpecifier
+SingleType
+ ::= SimpleTypeName '?'?
+TypeDeclaration
+ ::= 'as' SequenceType
+SequenceType
+ ::= 'empty-sequence' '(' ')'
+ | ItemType ( OccurrenceIndicator / )
+OccurrenceIndicator
+ ::= '?'
+ | '*'
+ | '+'
+ItemType ::= KindTest
+ | 'item' '(' ')'
+ | FunctionTest
+ | MapTest
+ | ArrayTest
+ | AtomicOrUnionType
+ | ParenthesizedItemType
+AtomicOrUnionType
+ ::= EQName
+KindTest ::= DocumentTest
+ | ElementTest
+ | AttributeTest
+ | SchemaElementTest
+ | SchemaAttributeTest
+ | PITest
+ | CommentTest
+ | TextTest
+ | NamespaceNodeTest
+ | AnyKindTest
+AnyKindTest
+ ::= 'node' '(' ')'
+DocumentTest
+ ::= 'document-node' '(' ( ElementTest | SchemaElementTest )? ')'
+TextTest ::= 'text' '(' ')'
+CommentTest
+ ::= 'comment' '(' ')'
+NamespaceNodeTest
+ ::= 'namespace-node' '(' ')'
+PITest ::= 'processing-instruction' '(' ( NCName | StringLiteral )? ')'
+AttributeTest
+ ::= 'attribute' '(' ( AttribNameOrWildcard ( ',' TypeName )? )? ')'
+AttribNameOrWildcard
+ ::= AttributeName
+ | '*'
+SchemaAttributeTest
+ ::= 'schema-attribute' '(' AttributeDeclaration ')'
+AttributeDeclaration
+ ::= AttributeName
+ElementTest
+ ::= 'element' '(' ( ElementNameOrWildcard ( ',' TypeName '?'? )? )? ')'
+ElementNameOrWildcard
+ ::= ElementName
+ | '*'
+SchemaElementTest
+ ::= 'schema-element' '(' ElementDeclaration ')'
+ElementDeclaration
+ ::= ElementName
+AttributeName
+ ::= EQName
+ElementName
+ ::= EQName
+SimpleTypeName
+ ::= TypeName
+TypeName ::= EQName
+FunctionTest
+ ::= Annotation* ( AnyFunctionTest | TypedFunctionTest )
+AnyFunctionTest
+ ::= 'function' '(' '*' ')'
+TypedFunctionTest
+ ::= 'function' '(' ( SequenceType ( ',' SequenceType )* )? ')' 'as' SequenceType
+MapTest ::= AnyMapTest
+ | TypedMapTest
+AnyMapTest
+ ::= 'map' '(' '*' ')'
+TypedMapTest
+ ::= 'map' '(' AtomicOrUnionType ',' SequenceType ')'
+ArrayTest
+ ::= AnyArrayTest
+ | TypedArrayTest
+AnyArrayTest
+ ::= 'array' '(' '*' ')'
+TypedArrayTest
+ ::= 'array' '(' SequenceType ')'
+ParenthesizedItemType
+ ::= '(' ItemType ')'
+FTOptionDecl
+ ::= 'declare' 'ft-option' FTMatchOptions
+FTScoreVar
+ ::= 'score' '$' VarName
+FTSelection
+ ::= FTOr FTPosFilter*
+FTWeight ::= 'weight' '{' Expr '}'
+FTOr ::= FTAnd ( 'ftor' FTAnd )*
+FTAnd ::= FTMildNot ( 'ftand' FTMildNot )*
+FTMildNot
+ ::= FTUnaryNot ( 'not' 'in' FTUnaryNot )*
+FTUnaryNot
+ ::= 'ftnot'? FTPrimaryWithOptions
+FTPrimaryWithOptions
+ ::= FTPrimary FTMatchOptions? FTWeight?
+FTPrimary
+ ::= FTWords FTTimes?
+ | '(' FTSelection ')'
+ | FTExtensionSelection
+FTWords ::= FTWordsValue FTAnyallOption?
+FTWordsValue
+ ::= StringLiteral
+ | '{' Expr '}'
+FTExtensionSelection
+ ::= Pragma+ '{' FTSelection? '}'
+FTAnyallOption
+ ::= 'any' 'word'?
+ | 'all' 'words'?
+ | 'phrase'
+FTTimes ::= 'occurs' FTRange 'times'
+FTRange ::= 'exactly' AdditiveExpr
+ | 'at' 'least' AdditiveExpr
+ | 'at' 'most' AdditiveExpr
+ | 'from' AdditiveExpr 'to' AdditiveExpr
+FTPosFilter
+ ::= FTOrder
+ | FTWindow
+ | FTDistance
+ | FTScope
+ | FTContent
+FTOrder ::= 'ordered'
+FTWindow ::= 'window' AdditiveExpr FTUnit
+FTDistance
+ ::= 'distance' FTRange FTUnit
+FTUnit ::= 'words'
+ | 'sentences'
+ | 'paragraphs'
+FTScope ::= ( 'same' | 'different' ) FTBigUnit
+FTBigUnit
+ ::= 'sentence'
+ | 'paragraph'
+FTContent
+ ::= 'at' 'start'
+ | 'at' 'end'
+ | 'entire' 'content'
+FTMatchOptions
+ ::= ( 'using' FTMatchOption )+
+FTMatchOption
+ ::= FTLanguageOption
+ | FTWildCardOption
+ | FTThesaurusOption
+ | FTStemOption
+ | FTCaseOption
+ | FTDiacriticsOption
+ | FTStopWordOption
+ | FTExtensionOption
+FTCaseOption
+ ::= 'case' 'insensitive'
+ | 'case' 'sensitive'
+ | 'lowercase'
+ | 'uppercase'
+FTDiacriticsOption
+ ::= 'diacritics' 'insensitive'
+ | 'diacritics' 'sensitive'
+FTStemOption
+ ::= 'stemming'
+ | 'no' 'stemming'
+FTThesaurusOption
+ ::= 'thesaurus' ( FTThesaurusID | 'default' )
+ | 'thesaurus' '(' ( FTThesaurusID | 'default' ) ( ',' FTThesaurusID )* ')'
+ | 'no' 'thesaurus'
+FTThesaurusID
+ ::= 'at' URILiteral ( 'relationship' StringLiteral )? ( FTLiteralRange 'levels' )?
+FTLiteralRange
+ ::= 'exactly' IntegerLiteral
+ | 'at' 'least' IntegerLiteral
+ | 'at' 'most' IntegerLiteral
+ | 'from' IntegerLiteral 'to' IntegerLiteral
+FTStopWordOption
+ ::= 'stop' 'words' FTStopWords FTStopWordsInclExcl*
+ | 'stop' 'words' 'default' FTStopWordsInclExcl*
+ | 'no' 'stop' 'words'
+FTStopWords
+ ::= 'at' URILiteral
+ | '(' StringLiteral ( ',' StringLiteral )* ')'
+FTStopWordsInclExcl
+ ::= ( 'union' | 'except' ) FTStopWords
+FTLanguageOption
+ ::= 'language' StringLiteral
+FTWildCardOption
+ ::= 'wildcards'
+ | 'no' 'wildcards'
+FTExtensionOption
+ ::= 'option' EQName StringLiteral
+FTIgnoreOption
+ ::= 'without' 'content' UnionExpr
+URILiteral
+ ::= StringLiteral
+EQName ::= QName
+ | URIQualifiedName
+FunctionEQName
+ ::= FunctionName
+ | URIQualifiedName
+QName ::= FunctionName
+ | 'array'
+ | 'attribute'
+ | 'comment'
+ | 'document-node'
+ | 'element'
+ | 'empty-sequence'
+ | 'function'
+ | 'if'
+ | 'item'
+ | 'map'
+ | 'namespace-node'
+ | 'node'
+ | 'processing-instruction'
+ | 'schema-attribute'
+ | 'schema-element'
+ | 'switch'
+ | 'text'
+ | 'typeswitch'
+FunctionName
+ ::= QName^Token
+ | 'after'
+ | 'ancestor'
+ | 'ancestor-or-self'
+ | 'and'
+ | 'ascending'
+ | 'before'
+ | 'case'
+ | 'cast'
+ | 'castable'
+ | 'child'
+ | 'collation'
+ | 'contains'
+ | 'content'
+ | 'copy'
+ | 'count'
+ | 'declare'
+ | 'default'
+ | 'delete'
+ | 'descendant'
+ | 'descendant-or-self'
+ | 'descending'
+ | 'diacritics'
+ | 'different'
+ | 'distance'
+ | 'div'
+ | 'document'
+ | 'else'
+ | 'empty'
+ | 'end'
+ | 'entire'
+ | 'eq'
+ | 'every'
+ | 'exactly'
+ | 'except'
+ | 'first'
+ | 'following'
+ | 'following-sibling'
+ | 'for'
+ | 'ft-option'
+ | 'ftand'
+ | 'ftnot'
+ | 'ftor'
+ | 'ge'
+ | 'group'
+ | 'gt'
+ | 'idiv'
+ | 'import'
+ | 'insensitive'
+ | 'insert'
+ | 'instance'
+ | 'intersect'
+ | 'into'
+ | 'is'
+ | 'language'
+ | 'last'
+ | 'le'
+ | 'least'
+ | 'let'
+ | 'levels'
+ | 'lowercase'
+ | 'lt'
+ | 'mod'
+ | 'modify'
+ | 'module'
+ | 'most'
+ | 'namespace'
+ | 'ne'
+ | 'no'
+ | 'nodes'
+ | 'not'
+ | 'occurs'
+ | 'only'
+ | 'or'
+ | 'order'
+ | 'ordered'
+ | 'paragraph'
+ | 'paragraphs'
+ | 'parent'
+ | 'phrase'
+ | 'preceding'
+ | 'preceding-sibling'
+ | 'relationship'
+ | 'rename'
+ | 'replace'
+ | 'return'
+ | 'revalidation'
+ | 'same'
+ | 'satisfies'
+ | 'score'
+ | 'self'
+ | 'sensitive'
+ | 'sentence'
+ | 'sentences'
+ | 'skip'
+ | 'some'
+ | 'stable'
+ | 'start'
+ | 'stemming'
+ | 'stop'
+ | 'thesaurus'
+ | 'times'
+ | 'to'
+ | 'treat'
+ | 'try'
+ | 'union'
+ | 'unordered'
+ | 'update'
+ | 'uppercase'
+ | 'using'
+ | 'validate'
+ | 'value'
+ | 'weight'
+ | 'where'
+ | 'wildcards'
+ | 'window'
+ | 'with'
+ | 'without'
+ | 'word'
+ | 'words'
+ | 'xquery'
+NCName ::= NCName^Token
+ | 'after'
+ | 'and'
+ | 'ascending'
+ | 'before'
+ | 'case'
+ | 'cast'
+ | 'castable'
+ | 'collation'
+ | 'contains'
+ | 'content'
+ | 'copy'
+ | 'count'
+ | 'default'
+ | 'delete'
+ | 'descending'
+ | 'diacritics'
+ | 'different'
+ | 'distance'
+ | 'div'
+ | 'else'
+ | 'empty'
+ | 'end'
+ | 'entire'
+ | 'eq'
+ | 'exactly'
+ | 'except'
+ | 'first'
+ | 'for'
+ | 'ftand'
+ | 'ftnot'
+ | 'ftor'
+ | 'ge'
+ | 'group'
+ | 'gt'
+ | 'idiv'
+ | 'insensitive'
+ | 'insert'
+ | 'instance'
+ | 'intersect'
+ | 'into'
+ | 'is'
+ | 'language'
+ | 'last'
+ | 'le'
+ | 'least'
+ | 'let'
+ | 'levels'
+ | 'lowercase'
+ | 'lt'
+ | 'mod'
+ | 'modify'
+ | 'most'
+ | 'ne'
+ | 'no'
+ | 'nodes'
+ | 'not'
+ | 'occurs'
+ | 'only'
+ | 'or'
+ | 'order'
+ | 'paragraph'
+ | 'paragraphs'
+ | 'phrase'
+ | 'relationship'
+ | 'rename'
+ | 'replace'
+ | 'return'
+ | 'revalidation'
+ | 'same'
+ | 'satisfies'
+ | 'score'
+ | 'sensitive'
+ | 'sentence'
+ | 'sentences'
+ | 'skip'
+ | 'stable'
+ | 'start'
+ | 'stemming'
+ | 'stop'
+ | 'thesaurus'
+ | 'times'
+ | 'to'
+ | 'treat'
+ | 'union'
+ | 'update'
+ | 'uppercase'
+ | 'using'
+ | 'value'
+ | 'weight'
+ | 'where'
+ | 'wildcards'
+ | 'with'
+ | 'without'
+ | 'word'
+ | 'words'
+Whitespace
+ ::= S^WS
+ | Comment
+ /* ws: definition */
+Comment ::= '(:' ( CommentContents | Comment )* ':)'
+ /* ws: explicit */
+
+
+
+IntegerLiteral
+ ::= Digits
+DecimalLiteral
+ ::= '.' Digits
+ | Digits '.' [0-9]*
+ /* ws: explicit */
+DoubleLiteral
+ ::= ( '.' Digits | Digits ( '.' [0-9]* )? ) [eE] [+#x002D]? Digits
+ /* ws: explicit */
+StringLiteral
+ ::= '"' ( PredefinedEntityRef | CharRef | EscapeQuot | [^"&] )* '"'
+ | "'" ( PredefinedEntityRef | CharRef | EscapeApos | [^'&] )* "'"
+ /* ws: explicit */
+URIQualifiedName
+ ::= BracedURILiteral NCName
+ /* ws: explicit */
+BracedURILiteral
+ ::= 'Q' '{' ( PredefinedEntityRef | CharRef | [^&{}] )* '}'
+ /* ws: explicit */
+PredefinedEntityRef
+ ::= '&' ( 'lt' | 'gt' | 'amp' | 'quot' | 'apos' ) ';'
+ /* ws: explicit */
+EscapeQuot
+ ::= '""'
+EscapeApos
+ ::= "''"
+ElementContentChar
+ ::= Char - [{}<&]
+QuotAttrContentChar
+ ::= Char - ["{}<&]
+AposAttrContentChar
+ ::= Char - ['{}<&]
+PITarget ::= NCName - ( ( 'X' | 'x' ) ( 'M' | 'm' ) ( 'L' | 'l' ) )
+NameStartChar
+ ::= ':'
+ | [A-Z]
+ | '_'
+ | [a-z]
+ | [#x00C0-#x00D6]
+ | [#x00D8-#x00F6]
+ | [#x00F8-#x02FF]
+ | [#x0370-#x037D]
+ | [#x037F-#x1FFF]
+ | [#x200C-#x200D]
+ | [#x2070-#x218F]
+ | [#x2C00-#x2FEF]
+ | [#x3001-#xD7FF]
+ | [#xF900-#xFDCF]
+ | [#xFDF0-#xFFFD]
+ | [#x10000-#xEFFFF]
+NameChar ::= NameStartChar
+ | '-'
+ | '.'
+ | [0-9]
+ | #x00B7
+ | [#x0300-#x036F]
+ | [#x203F-#x2040]
+Name ::= NameStartChar NameChar*
+CharRef ::= '' [0-9]+ ';'
+ | '' [0-9a-fA-F]+ ';'
+NCName ::= Name - ( Char* ':' Char* )
+QName ::= PrefixedName
+ | UnprefixedName
+PrefixedName
+ ::= Prefix ':' LocalPart
+UnprefixedName
+ ::= LocalPart
+Prefix ::= NCName
+LocalPart
+ ::= NCName
+StringConstructorChars
+ ::= ( Char* - ( Char* ( '`{' | ']``' ) Char* ) ) &( '`{' | ']`' )
+ /* ws: explicit */
+S ::= ( #x0020 | #x0009 | #x000D | #x000A )+
+Char ::= #x0009
+ | #x000A
+ | #x000D
+ | [#x0020-#xD7FF]
+ | [#xE000-#xFFFD]
+ | [#x10000-#x10FFFF]
+Digits ::= [0-9]+
+CommentContents
+ ::= ( ( Char+ - ( Char* ( '(:' | ':)' ) Char* ) ) - ( Char* '(' ) ) &':'
+ | ( Char+ - ( Char* ( '(:' | ':)' ) Char* ) ) &'('
+PragmaContents
+ ::= ( Char* - ( Char* '#)' Char* ) ) &'#'
+Wildcard ::= '*'
+ | NCName ':*'
+ | '*:' NCName
+ | BracedURILiteral '*'
+DirCommentContents
+ ::= ( Char - '-' | '-' ( Char - '-' ) )*
+DirPIContents
+ ::= ( Char* - ( Char* '?>' Char* ) ) &'?'
+CDataSectionContents
+ ::= ( Char* - ( Char* ']]>' Char* ) ) &']]'
+EOF ::= $
+NonNCNameChar
+ ::= $
+ | ':'
+ | Char - NameChar
+DelimitingChar
+ ::= NonNCNameChar
+ | '-'
+ | '.'
+DelimitingChar
+ \\ DecimalLiteral DoubleLiteral IntegerLiteral
+NonNCNameChar
+ \\ URIQualifiedName NCName^Token QName^Token 'NaN' 'after' 'all' 'allowing' 'ancestor' 'ancestor-or-self' 'and' 'any' 'array' 'as' 'ascending' 'at' 'attribute' 'base-uri' 'before' 'boundary-space' 'by' 'case' 'cast' 'castable' 'catch' 'child' 'collation' 'comment' 'construction' 'contains' 'content' 'context' 'copy' 'copy-namespaces' 'count' 'decimal-format' 'decimal-separator' 'declare' 'default' 'delete' 'descendant' 'descendant-or-self' 'descending' 'diacritics' 'different' 'digit' 'distance' 'div' 'document' 'document-node' 'element' 'else' 'empty' 'empty-sequence' 'encoding' 'end' 'entire' 'eq' 'every' 'exactly' 'except' 'exponent-separator' 'external' 'first' 'following' 'following-sibling' 'for' 'from' 'ft-option' 'ftand' 'ftnot' 'ftor' 'function' 'ge' 'greatest' 'group' 'grouping-separator' 'gt' 'idiv' 'if' 'import' 'in' 'infinity' 'inherit' 'insensitive' 'insert' 'instance' 'intersect' 'into' 'is' 'item' 'language' 'last' 'lax' 'le' 'least' 'let' 'levels' 'lowercase' 'lt' 'map' 'minus-sign' 'mod' 'modify' 'module' 'most' 'namespace' 'namespace-node' 'ne' 'next' 'no' 'no-inherit' 'no-preserve' 'node' 'nodes' 'not' 'occurs' 'of' 'only' 'option' 'or' 'order' 'ordered' 'ordering' 'paragraph' 'paragraphs' 'parent' 'pattern-separator' 'per-mille' 'percent' 'phrase' 'preceding' 'preceding-sibling' 'preserve' 'previous' 'processing-instruction' 'relationship' 'rename' 'replace' 'return' 'revalidation' 'same' 'satisfies' 'schema' 'schema-attribute' 'schema-element' 'score' 'self' 'sensitive' 'sentence' 'sentences' 'skip' 'sliding' 'some' 'stable' 'start' 'stemming' 'stop' 'strict' 'strip' 'switch' 'text' 'then' 'thesaurus' 'times' 'to' 'treat' 'try' 'tumbling' 'type' 'typeswitch' 'union' 'unordered' 'update' 'uppercase' 'using' 'validate' 'value' 'variable' 'version' 'weight' 'when' 'where' 'wildcards' 'window' 'with' 'without' 'word' 'words' 'xquery' 'zero-digit'
+'*' << Wildcard
+NCName^Token
+ << 'after' 'and' 'as' 'ascending' 'before' 'case' 'cast' 'castable' 'collation' 'contains' 'content' 'copy' 'count' 'default' 'delete' 'descending' 'diacritics' 'different' 'distance' 'div' 'else' 'empty' 'end' 'entire' 'eq' 'exactly' 'except' 'first' 'following' 'for' 'ftand' 'ftnot' 'ftor' 'ge' 'group' 'gt' 'idiv' 'insensitive' 'insert' 'instance' 'intersect' 'into' 'is' 'language' 'last' 'le' 'least' 'let' 'levels' 'lowercase' 'lt' 'mod' 'modify' 'most' 'ne' 'no' 'nodes' 'not' 'occurs' 'only' 'or' 'order' 'paragraph' 'paragraphs' 'phrase' 'preceding' 'relationship' 'rename' 'replace' 'return' 'revalidation' 'same' 'satisfies' 'score' 'sensitive' 'sentence' 'sentences' 'skip' 'stable' 'start' 'stemming' 'stop' 'thesaurus' 'times' 'to' 'treat' 'union' 'update' 'uppercase' 'using' 'value' 'weight' 'where' 'wildcards' 'with' 'without' 'word' 'words'
+QName^Token
+ << 'after' 'ancestor' 'ancestor-or-self' 'and' 'array' 'as' 'ascending' 'attribute' 'before' 'case' 'cast' 'castable' 'child' 'collation' 'comment' 'contains' 'content' 'copy' 'count' 'declare' 'default' 'delete' 'descendant' 'descendant-or-self' 'descending' 'diacritics' 'different' 'distance' 'div' 'document' 'document-node' 'element' 'else' 'empty' 'empty-sequence' 'end' 'entire' 'eq' 'every' 'exactly' 'except' 'first' 'following' 'following-sibling' 'for' 'ft-option' 'ftand' 'ftnot' 'ftor' 'function' 'ge' 'group' 'gt' 'idiv' 'if' 'import' 'insensitive' 'insert' 'instance' 'intersect' 'into' 'is' 'item' 'language' 'last' 'le' 'least' 'let' 'levels' 'lowercase' 'lt' 'map' 'mod' 'modify' 'module' 'most' 'namespace' 'namespace-node' 'ne' 'no' 'node' 'nodes' 'not' 'occurs' 'only' 'or' 'order' 'ordered' 'paragraph' 'paragraphs' 'parent' 'phrase' 'preceding' 'preceding-sibling' 'processing-instruction' 'relationship' 'rename' 'replace' 'return' 'revalidation' 'same' 'satisfies' 'schema-attribute' 'schema-element' 'score' 'self' 'sensitive' 'sentence' 'sentences' 'skip' 'some' 'stable' 'start' 'stemming' 'stop' 'switch' 'text' 'then' 'thesaurus' 'times' 'to' 'treat' 'try' 'typeswitch' 'union' 'unordered' 'update' 'uppercase' 'using' 'validate' 'value' 'weight' 'where' 'wildcards' 'window' 'with' 'without' 'word' 'words' 'xquery'
diff --git a/grammars/XQuery-31-Update-FullText.ebnf b/grammars/XQuery-31-Update-FullText.ebnf
new file mode 100644
index 00000000..d59fa517
--- /dev/null
+++ b/grammars/XQuery-31-Update-FullText.ebnf
@@ -0,0 +1,1028 @@
+XQuery ::= Module EOF
+Module ::= VersionDecl? ( LibraryModule | MainModule )
+VersionDecl
+ ::= 'xquery' ( 'encoding' StringLiteral | 'version' StringLiteral ( 'encoding' StringLiteral )? ) Separator
+MainModule
+ ::= Prolog QueryBody
+LibraryModule
+ ::= ModuleDecl Prolog
+ModuleDecl
+ ::= 'module' 'namespace' NCName '=' URILiteral Separator
+Prolog ::= ( ( DefaultNamespaceDecl | Setter | NamespaceDecl | Import | FTOptionDecl ) Separator )* ( ( ContextItemDecl | AnnotatedDecl | OptionDecl ) Separator )*
+Separator
+ ::= ';'
+Setter ::= BoundarySpaceDecl
+ | DefaultCollationDecl
+ | BaseURIDecl
+ | ConstructionDecl
+ | OrderingModeDecl
+ | EmptyOrderDecl
+ | CopyNamespacesDecl
+ | DecimalFormatDecl
+ | RevalidationDecl
+RevalidationDecl
+ ::= 'declare' 'revalidation' ( 'strict' | 'lax' | 'skip' )
+BoundarySpaceDecl
+ ::= 'declare' 'boundary-space' ( 'preserve' | 'strip' )
+DefaultCollationDecl
+ ::= 'declare' 'default' 'collation' URILiteral
+BaseURIDecl
+ ::= 'declare' 'base-uri' URILiteral
+ConstructionDecl
+ ::= 'declare' 'construction' ( 'strip' | 'preserve' )
+OrderingModeDecl
+ ::= 'declare' 'ordering' ( 'ordered' | 'unordered' )
+EmptyOrderDecl
+ ::= 'declare' 'default' 'order' 'empty' ( 'greatest' | 'least' )
+CopyNamespacesDecl
+ ::= 'declare' 'copy-namespaces' PreserveMode ',' InheritMode
+PreserveMode
+ ::= 'preserve'
+ | 'no-preserve'
+InheritMode
+ ::= 'inherit'
+ | 'no-inherit'
+DecimalFormatDecl
+ ::= 'declare' ( 'decimal-format' EQName | 'default' 'decimal-format' ) ( DFPropertyName '=' StringLiteral )*
+DFPropertyName
+ ::= 'decimal-separator'
+ | 'grouping-separator'
+ | 'infinity'
+ | 'minus-sign'
+ | 'NaN'
+ | 'percent'
+ | 'per-mille'
+ | 'zero-digit'
+ | 'digit'
+ | 'pattern-separator'
+ | 'exponent-separator'
+Import ::= SchemaImport
+ | ModuleImport
+SchemaImport
+ ::= 'import' 'schema' SchemaPrefix? URILiteral ( 'at' URILiteral ( ',' URILiteral )* )?
+SchemaPrefix
+ ::= 'namespace' NCName '='
+ | 'default' 'element' 'namespace'
+ModuleImport
+ ::= 'import' 'module' ( 'namespace' NCName '=' )? URILiteral ( 'at' URILiteral ( ',' URILiteral )* )?
+NamespaceDecl
+ ::= 'declare' 'namespace' NCName '=' URILiteral
+DefaultNamespaceDecl
+ ::= 'declare' 'default' ( 'element' | 'function' ) 'namespace' URILiteral
+AnnotatedDecl
+ ::= 'declare' Annotation* ( VarDecl | FunctionDecl )
+Annotation
+ ::= '%' EQName ( '(' Literal ( ',' Literal )* ')' )?
+VarDecl ::= 'variable' '$' VarName TypeDeclaration? ( ':=' VarValue | 'external' ( ':=' VarDefaultValue )? )
+VarValue ::= ExprSingle
+VarDefaultValue
+ ::= ExprSingle
+ContextItemDecl
+ ::= 'declare' 'context' 'item' ( 'as' ItemType )? ( ':=' VarValue | 'external' ( ':=' VarDefaultValue )? )
+FunctionDecl
+ ::= 'function' EQName '(' ParamList? ')' ( 'as' SequenceType )? ( FunctionBody | 'external' )
+ParamList
+ ::= Param ( ',' Param )*
+Param ::= '$' EQName TypeDeclaration?
+FunctionBody
+ ::= EnclosedExpr
+EnclosedExpr
+ ::= '{' Expr? '}'
+OptionDecl
+ ::= 'declare' 'option' EQName StringLiteral
+QueryBody
+ ::= Expr
+Expr ::= ExprSingle ( ',' ExprSingle )*
+ExprSingle
+ ::= FLWORExpr
+ | QuantifiedExpr
+ | SwitchExpr
+ | TypeswitchExpr
+ | IfExpr
+ | TryCatchExpr
+ | InsertExpr
+ | DeleteExpr
+ | RenameExpr
+ | ReplaceExpr
+ | TransformExpr
+ | OrExpr
+FLWORExpr
+ ::= InitialClause IntermediateClause* ReturnClause
+InitialClause
+ ::= ForClause
+ | LetClause
+ | WindowClause
+IntermediateClause
+ ::= InitialClause
+ | WhereClause
+ | GroupByClause
+ | OrderByClause
+ | CountClause
+ForClause
+ ::= 'for' ForBinding ( ',' ForBinding )*
+ForBinding
+ ::= '$' VarName TypeDeclaration? AllowingEmpty? PositionalVar? FTScoreVar? 'in' ExprSingle
+AllowingEmpty
+ ::= 'allowing' 'empty'
+PositionalVar
+ ::= 'at' '$' VarName
+LetClause
+ ::= 'let' LetBinding ( ',' LetBinding )*
+LetBinding
+ ::= ( '$' VarName TypeDeclaration? | FTScoreVar ) ':=' ExprSingle
+WindowClause
+ ::= 'for' ( TumblingWindowClause | SlidingWindowClause )
+TumblingWindowClause
+ ::= 'tumbling' 'window' '$' VarName TypeDeclaration? 'in' ExprSingle WindowStartCondition WindowEndCondition?
+SlidingWindowClause
+ ::= 'sliding' 'window' '$' VarName TypeDeclaration? 'in' ExprSingle WindowStartCondition WindowEndCondition
+WindowStartCondition
+ ::= 'start' WindowVars 'when' ExprSingle
+WindowEndCondition
+ ::= 'only'? 'end' WindowVars 'when' ExprSingle
+WindowVars
+ ::= ( '$' CurrentItem )? PositionalVar? ( 'previous' '$' PreviousItem )? ( 'next' '$' NextItem )?
+CurrentItem
+ ::= EQName
+PreviousItem
+ ::= EQName
+NextItem ::= EQName
+CountClause
+ ::= 'count' '$' VarName
+WhereClause
+ ::= 'where' ExprSingle
+GroupByClause
+ ::= 'group' 'by' GroupingSpecList
+GroupingSpecList
+ ::= GroupingSpec ( ',' GroupingSpec )*
+GroupingSpec
+ ::= GroupingVariable ( TypeDeclaration? ':=' ExprSingle )? ( 'collation' URILiteral )?
+GroupingVariable
+ ::= '$' VarName
+OrderByClause
+ ::= ( 'order' 'by' | 'stable' 'order' 'by' ) OrderSpecList
+OrderSpecList
+ ::= OrderSpec ( ',' OrderSpec )*
+OrderSpec
+ ::= ExprSingle OrderModifier
+OrderModifier
+ ::= ( 'ascending' | 'descending' )? ( 'empty' ( 'greatest' | 'least' ) )? ( 'collation' URILiteral )?
+ReturnClause
+ ::= 'return' ExprSingle
+QuantifiedExpr
+ ::= ( 'some' | 'every' ) '$' VarName TypeDeclaration? 'in' ExprSingle ( ',' '$' VarName TypeDeclaration? 'in' ExprSingle )* 'satisfies' ExprSingle
+SwitchExpr
+ ::= 'switch' '(' Expr ')' SwitchCaseClause+ 'default' 'return' ExprSingle
+SwitchCaseClause
+ ::= ( 'case' SwitchCaseOperand )+ 'return' ExprSingle
+SwitchCaseOperand
+ ::= ExprSingle
+TypeswitchExpr
+ ::= 'typeswitch' '(' Expr ')' CaseClause+ 'default' ( '$' VarName )? 'return' ExprSingle
+CaseClause
+ ::= 'case' ( '$' VarName 'as' )? SequenceTypeUnion 'return' ExprSingle
+SequenceTypeUnion
+ ::= SequenceType ( '|' SequenceType )*
+IfExpr ::= 'if' '(' Expr ')' 'then' ExprSingle 'else' ExprSingle
+TryCatchExpr
+ ::= TryClause CatchClause+
+TryClause
+ ::= 'try' EnclosedTryTargetExpr
+EnclosedTryTargetExpr
+ ::= EnclosedExpr
+CatchClause
+ ::= 'catch' CatchErrorList EnclosedExpr
+CatchErrorList
+ ::= NameTest ( '|' NameTest )*
+InsertExpr
+ ::= 'insert' ( 'node' | 'nodes' ) SourceExpr InsertExprTargetChoice TargetExpr
+InsertExprTargetChoice
+ ::= ( 'as' ( 'first' | 'last' ) )? 'into'
+ | 'after'
+ | 'before'
+SourceExpr
+ ::= ExprSingle
+TargetExpr
+ ::= ExprSingle
+DeleteExpr
+ ::= 'delete' ( 'node' | 'nodes' ) TargetExpr
+ReplaceExpr
+ ::= 'replace' ( 'value' 'of' )? 'node' TargetExpr 'with' ExprSingle
+RenameExpr
+ ::= 'rename' 'node' TargetExpr 'as' NewNameExpr
+NewNameExpr
+ ::= ExprSingle
+TransformExpr
+ ::= 'copy' '$' VarName ':=' ExprSingle ( ',' '$' VarName ':=' ExprSingle )* 'modify' ExprSingle 'return' ExprSingle
+OrExpr ::= AndExpr ( 'or' AndExpr )*
+AndExpr ::= ComparisonExpr ( 'and' ComparisonExpr )*
+ComparisonExpr
+ ::= FTContainsExpr ( ( ValueComp | GeneralComp | NodeComp ) FTContainsExpr )?
+FTContainsExpr
+ ::= StringConcatExpr ( 'contains' 'text' FTSelection FTIgnoreOption? )?
+StringConcatExpr
+ ::= RangeExpr ( '||' RangeExpr )*
+RangeExpr
+ ::= AdditiveExpr ( 'to' AdditiveExpr )?
+AdditiveExpr
+ ::= MultiplicativeExpr ( ( '+' | '-' ) MultiplicativeExpr )*
+MultiplicativeExpr
+ ::= UnionExpr ( ( '*' | 'div' | 'idiv' | 'mod' ) UnionExpr )*
+UnionExpr
+ ::= IntersectExceptExpr ( ( 'union' | '|' ) IntersectExceptExpr )*
+IntersectExceptExpr
+ ::= InstanceofExpr ( ( 'intersect' | 'except' ) InstanceofExpr )*
+InstanceofExpr
+ ::= TreatExpr ( 'instance' 'of' SequenceType )?
+TreatExpr
+ ::= CastableExpr ( 'treat' 'as' SequenceType )?
+CastableExpr
+ ::= CastExpr ( 'castable' 'as' SingleType )?
+CastExpr ::= ArrowExpr ( 'cast' 'as' SingleType )?
+ArrowExpr
+ ::= UnaryExpr ( '=>' ArrowFunctionSpecifier ArgumentList )*
+UnaryExpr
+ ::= ( '-' | '+' )* ValueExpr
+ValueExpr
+ ::= ValidateExpr
+ | ExtensionExpr
+ | SimpleMapExpr
+GeneralComp
+ ::= '='
+ | '!='
+ | '<'
+ | '<='
+ | '>'
+ | '>='
+ValueComp
+ ::= 'eq'
+ | 'ne'
+ | 'lt'
+ | 'le'
+ | 'gt'
+ | 'ge'
+NodeComp ::= 'is'
+ | '<<'
+ | '>>'
+ValidateExpr
+ ::= 'validate' ( ValidationMode | 'type' TypeName )? '{' Expr '}'
+ValidationMode
+ ::= 'lax'
+ | 'strict'
+ExtensionExpr
+ ::= Pragma+ '{' Expr? '}'
+Pragma ::= '(#' S? EQName ( S PragmaContents )? '#)'
+ /* ws: explicit */
+SimpleMapExpr
+ ::= PathExpr ( '!' PathExpr )*
+PathExpr ::= '/' ( RelativePathExpr / )
+ | '//' RelativePathExpr
+ | RelativePathExpr
+RelativePathExpr
+ ::= StepExpr ( ( '/' | '//' ) StepExpr )*
+StepExpr ::= PostfixExpr
+ | AxisStep
+AxisStep ::= ( ReverseStep | ForwardStep ) PredicateList
+ForwardStep
+ ::= ForwardAxis NodeTest
+ | AbbrevForwardStep
+ForwardAxis
+ ::= 'child' '::'
+ | 'descendant' '::'
+ | 'attribute' '::'
+ | 'self' '::'
+ | 'descendant-or-self' '::'
+ | 'following-sibling' '::'
+ | 'following' '::'
+AbbrevForwardStep
+ ::= '@'? NodeTest
+ReverseStep
+ ::= ReverseAxis NodeTest
+ | AbbrevReverseStep
+ReverseAxis
+ ::= 'parent' '::'
+ | 'ancestor' '::'
+ | 'preceding-sibling' '::'
+ | 'preceding' '::'
+ | 'ancestor-or-self' '::'
+AbbrevReverseStep
+ ::= '..'
+NodeTest ::= KindTest
+ | NameTest
+NameTest ::= EQName
+ | Wildcard
+PostfixExpr
+ ::= PrimaryExpr ( Predicate | ArgumentList | Lookup )*
+ArgumentList
+ ::= '(' ( Argument ( ',' Argument )* )? ')'
+PredicateList
+ ::= Predicate*
+Predicate
+ ::= '[' Expr ']'
+Lookup ::= '?' KeySpecifier
+KeySpecifier
+ ::= NCName
+ | IntegerLiteral
+ | ParenthesizedExpr
+ | '*'
+ArrowFunctionSpecifier
+ ::= EQName
+ | VarRef
+ | ParenthesizedExpr
+PrimaryExpr
+ ::= Literal
+ | VarRef
+ | ParenthesizedExpr
+ | ContextItemExpr
+ | FunctionCall
+ | OrderedExpr
+ | UnorderedExpr
+ | NodeConstructor
+ | FunctionItemExpr
+ | MapConstructor
+ | ArrayConstructor
+ | StringConstructor
+ | UnaryLookup
+Literal ::= NumericLiteral
+ | StringLiteral
+NumericLiteral
+ ::= IntegerLiteral
+ | DecimalLiteral
+ | DoubleLiteral
+VarRef ::= '$' VarName
+VarName ::= EQName
+ParenthesizedExpr
+ ::= '(' Expr? ')'
+ContextItemExpr
+ ::= '.'
+OrderedExpr
+ ::= 'ordered' EnclosedExpr
+UnorderedExpr
+ ::= 'unordered' EnclosedExpr
+FunctionCall
+ ::= FunctionEQName ArgumentList
+Argument ::= ExprSingle
+ | ArgumentPlaceholder
+ArgumentPlaceholder
+ ::= '?'
+NodeConstructor
+ ::= DirectConstructor
+ | ComputedConstructor
+DirectConstructor
+ ::= DirElemConstructor
+ | DirCommentConstructor
+ | DirPIConstructor
+DirElemConstructor
+ ::= '<' QName DirAttributeList ( '/>' | '>' DirElemContent* '' QName S? '>' )
+ /* ws: explicit */
+DirAttributeList
+ ::= ( S ( QName S? '=' S? DirAttributeValue )? )*
+ /* ws: explicit */
+DirAttributeValue
+ ::= '"' ( EscapeQuot | QuotAttrValueContent )* '"'
+ | "'" ( EscapeApos | AposAttrValueContent )* "'"
+ /* ws: explicit */
+QuotAttrValueContent
+ ::= QuotAttrContentChar
+ | CommonContent
+AposAttrValueContent
+ ::= AposAttrContentChar
+ | CommonContent
+DirElemContent
+ ::= DirectConstructor
+ | CDataSection
+ | CommonContent
+ | ElementContentChar
+CommonContent
+ ::= PredefinedEntityRef
+ | CharRef
+ | '{{'
+ | '}}'
+ | EnclosedExpr
+DirCommentConstructor
+ ::= ''
+ /* ws: explicit */
+DirPIConstructor
+ ::= '' PITarget ( S DirPIContents )? '?>'
+ /* ws: explicit */
+CDataSection
+ ::= ''
+ /* ws: explicit */
+ComputedConstructor
+ ::= CompDocConstructor
+ | CompElemConstructor
+ | CompAttrConstructor
+ | CompNamespaceConstructor
+ | CompTextConstructor
+ | CompCommentConstructor
+ | CompPIConstructor
+CompDocConstructor
+ ::= 'document' EnclosedExpr
+CompElemConstructor
+ ::= 'element' ( EQName | '{' Expr '}' ) EnclosedContentExpr
+EnclosedContentExpr
+ ::= EnclosedExpr
+CompAttrConstructor
+ ::= 'attribute' ( EQName | '{' Expr '}' ) EnclosedExpr
+CompNamespaceConstructor
+ ::= 'namespace' ( Prefix | EnclosedPrefixExpr ) EnclosedURIExpr
+Prefix ::= NCName
+EnclosedPrefixExpr
+ ::= EnclosedExpr
+EnclosedURIExpr
+ ::= EnclosedExpr
+CompTextConstructor
+ ::= 'text' EnclosedExpr
+CompCommentConstructor
+ ::= 'comment' EnclosedExpr
+CompPIConstructor
+ ::= 'processing-instruction' ( NCName | '{' Expr '}' ) EnclosedExpr
+FunctionItemExpr
+ ::= NamedFunctionRef
+ | InlineFunctionExpr
+NamedFunctionRef
+ ::= EQName '#' IntegerLiteral
+InlineFunctionExpr
+ ::= Annotation* 'function' '(' ParamList? ')' ( 'as' SequenceType )? FunctionBody
+MapConstructor
+ ::= 'map' '{' ( MapConstructorEntry ( ',' MapConstructorEntry )* )? '}'
+MapConstructorEntry
+ ::= MapKeyExpr ':' MapValueExpr
+MapKeyExpr
+ ::= ExprSingle
+MapValueExpr
+ ::= ExprSingle
+ArrayConstructor
+ ::= SquareArrayConstructor
+ | CurlyArrayConstructor
+SquareArrayConstructor
+ ::= '[' ( ExprSingle ( ',' ExprSingle )* )? ']'
+CurlyArrayConstructor
+ ::= 'array' EnclosedExpr
+StringConstructor
+ ::= '``[' StringConstructorContent ']``'
+ /* ws: explicit */
+StringConstructorContent
+ ::= StringConstructorChars ( StringConstructorInterpolation StringConstructorChars )*
+ /* ws: explicit */
+StringConstructorInterpolation
+ ::= '`{' Expr? '}`'
+UnaryLookup
+ ::= '?' KeySpecifier
+SingleType
+ ::= SimpleTypeName '?'?
+TypeDeclaration
+ ::= 'as' SequenceType
+SequenceType
+ ::= 'empty-sequence' '(' ')'
+ | ItemType ( OccurrenceIndicator / )
+OccurrenceIndicator
+ ::= '?'
+ | '*'
+ | '+'
+ItemType ::= KindTest
+ | 'item' '(' ')'
+ | FunctionTest
+ | MapTest
+ | ArrayTest
+ | AtomicOrUnionType
+ | ParenthesizedItemType
+AtomicOrUnionType
+ ::= EQName
+KindTest ::= DocumentTest
+ | ElementTest
+ | AttributeTest
+ | SchemaElementTest
+ | SchemaAttributeTest
+ | PITest
+ | CommentTest
+ | TextTest
+ | NamespaceNodeTest
+ | AnyKindTest
+AnyKindTest
+ ::= 'node' '(' ')'
+DocumentTest
+ ::= 'document-node' '(' ( ElementTest | SchemaElementTest )? ')'
+TextTest ::= 'text' '(' ')'
+CommentTest
+ ::= 'comment' '(' ')'
+NamespaceNodeTest
+ ::= 'namespace-node' '(' ')'
+PITest ::= 'processing-instruction' '(' ( NCName | StringLiteral )? ')'
+AttributeTest
+ ::= 'attribute' '(' ( AttribNameOrWildcard ( ',' TypeName )? )? ')'
+AttribNameOrWildcard
+ ::= AttributeName
+ | '*'
+SchemaAttributeTest
+ ::= 'schema-attribute' '(' AttributeDeclaration ')'
+AttributeDeclaration
+ ::= AttributeName
+ElementTest
+ ::= 'element' '(' ( ElementNameOrWildcard ( ',' TypeName '?'? )? )? ')'
+ElementNameOrWildcard
+ ::= ElementName
+ | '*'
+SchemaElementTest
+ ::= 'schema-element' '(' ElementDeclaration ')'
+ElementDeclaration
+ ::= ElementName
+AttributeName
+ ::= EQName
+ElementName
+ ::= EQName
+SimpleTypeName
+ ::= TypeName
+TypeName ::= EQName
+FunctionTest
+ ::= Annotation* ( AnyFunctionTest | TypedFunctionTest )
+AnyFunctionTest
+ ::= 'function' '(' '*' ')'
+TypedFunctionTest
+ ::= 'function' '(' ( SequenceType ( ',' SequenceType )* )? ')' 'as' SequenceType
+MapTest ::= AnyMapTest
+ | TypedMapTest
+AnyMapTest
+ ::= 'map' '(' '*' ')'
+TypedMapTest
+ ::= 'map' '(' AtomicOrUnionType ',' SequenceType ')'
+ArrayTest
+ ::= AnyArrayTest
+ | TypedArrayTest
+AnyArrayTest
+ ::= 'array' '(' '*' ')'
+TypedArrayTest
+ ::= 'array' '(' SequenceType ')'
+ParenthesizedItemType
+ ::= '(' ItemType ')'
+FTOptionDecl
+ ::= 'declare' 'ft-option' FTMatchOptions
+FTScoreVar
+ ::= 'score' '$' VarName
+FTSelection
+ ::= FTOr FTPosFilter*
+FTWeight ::= 'weight' '{' Expr '}'
+FTOr ::= FTAnd ( 'ftor' FTAnd )*
+FTAnd ::= FTMildNot ( 'ftand' FTMildNot )*
+FTMildNot
+ ::= FTUnaryNot ( 'not' 'in' FTUnaryNot )*
+FTUnaryNot
+ ::= 'ftnot'? FTPrimaryWithOptions
+FTPrimaryWithOptions
+ ::= FTPrimary FTMatchOptions? FTWeight?
+FTPrimary
+ ::= FTWords FTTimes?
+ | '(' FTSelection ')'
+ | FTExtensionSelection
+FTWords ::= FTWordsValue FTAnyallOption?
+FTWordsValue
+ ::= StringLiteral
+ | '{' Expr '}'
+FTExtensionSelection
+ ::= Pragma+ '{' FTSelection? '}'
+FTAnyallOption
+ ::= 'any' 'word'?
+ | 'all' 'words'?
+ | 'phrase'
+FTTimes ::= 'occurs' FTRange 'times'
+FTRange ::= 'exactly' AdditiveExpr
+ | 'at' 'least' AdditiveExpr
+ | 'at' 'most' AdditiveExpr
+ | 'from' AdditiveExpr 'to' AdditiveExpr
+FTPosFilter
+ ::= FTOrder
+ | FTWindow
+ | FTDistance
+ | FTScope
+ | FTContent
+FTOrder ::= 'ordered'
+FTWindow ::= 'window' AdditiveExpr FTUnit
+FTDistance
+ ::= 'distance' FTRange FTUnit
+FTUnit ::= 'words'
+ | 'sentences'
+ | 'paragraphs'
+FTScope ::= ( 'same' | 'different' ) FTBigUnit
+FTBigUnit
+ ::= 'sentence'
+ | 'paragraph'
+FTContent
+ ::= 'at' 'start'
+ | 'at' 'end'
+ | 'entire' 'content'
+FTMatchOptions
+ ::= ( 'using' FTMatchOption )+
+FTMatchOption
+ ::= FTLanguageOption
+ | FTWildCardOption
+ | FTThesaurusOption
+ | FTStemOption
+ | FTCaseOption
+ | FTDiacriticsOption
+ | FTStopWordOption
+ | FTExtensionOption
+FTCaseOption
+ ::= 'case' 'insensitive'
+ | 'case' 'sensitive'
+ | 'lowercase'
+ | 'uppercase'
+FTDiacriticsOption
+ ::= 'diacritics' 'insensitive'
+ | 'diacritics' 'sensitive'
+FTStemOption
+ ::= 'stemming'
+ | 'no' 'stemming'
+FTThesaurusOption
+ ::= 'thesaurus' ( FTThesaurusID | 'default' )
+ | 'thesaurus' '(' ( FTThesaurusID | 'default' ) ( ',' FTThesaurusID )* ')'
+ | 'no' 'thesaurus'
+FTThesaurusID
+ ::= 'at' URILiteral ( 'relationship' StringLiteral )? ( FTLiteralRange 'levels' )?
+FTLiteralRange
+ ::= 'exactly' IntegerLiteral
+ | 'at' 'least' IntegerLiteral
+ | 'at' 'most' IntegerLiteral
+ | 'from' IntegerLiteral 'to' IntegerLiteral
+FTStopWordOption
+ ::= 'stop' 'words' FTStopWords FTStopWordsInclExcl*
+ | 'stop' 'words' 'default' FTStopWordsInclExcl*
+ | 'no' 'stop' 'words'
+FTStopWords
+ ::= 'at' URILiteral
+ | '(' StringLiteral ( ',' StringLiteral )* ')'
+FTStopWordsInclExcl
+ ::= ( 'union' | 'except' ) FTStopWords
+FTLanguageOption
+ ::= 'language' StringLiteral
+FTWildCardOption
+ ::= 'wildcards'
+ | 'no' 'wildcards'
+FTExtensionOption
+ ::= 'option' EQName StringLiteral
+FTIgnoreOption
+ ::= 'without' 'content' UnionExpr
+URILiteral
+ ::= StringLiteral
+EQName ::= QName
+ | URIQualifiedName
+FunctionEQName
+ ::= FunctionName
+ | URIQualifiedName
+QName ::= FunctionName
+ | 'array'
+ | 'attribute'
+ | 'comment'
+ | 'document-node'
+ | 'element'
+ | 'empty-sequence'
+ | 'function'
+ | 'if'
+ | 'item'
+ | 'map'
+ | 'namespace-node'
+ | 'node'
+ | 'processing-instruction'
+ | 'schema-attribute'
+ | 'schema-element'
+ | 'switch'
+ | 'text'
+ | 'typeswitch'
+FunctionName
+ ::= QName^Token
+ | 'after'
+ | 'ancestor'
+ | 'ancestor-or-self'
+ | 'and'
+ | 'ascending'
+ | 'before'
+ | 'case'
+ | 'cast'
+ | 'castable'
+ | 'child'
+ | 'collation'
+ | 'contains'
+ | 'content'
+ | 'copy'
+ | 'count'
+ | 'declare'
+ | 'default'
+ | 'delete'
+ | 'descendant'
+ | 'descendant-or-self'
+ | 'descending'
+ | 'diacritics'
+ | 'different'
+ | 'distance'
+ | 'div'
+ | 'document'
+ | 'else'
+ | 'empty'
+ | 'end'
+ | 'entire'
+ | 'eq'
+ | 'every'
+ | 'exactly'
+ | 'except'
+ | 'first'
+ | 'following'
+ | 'following-sibling'
+ | 'for'
+ | 'ft-option'
+ | 'ftand'
+ | 'ftnot'
+ | 'ftor'
+ | 'ge'
+ | 'group'
+ | 'gt'
+ | 'idiv'
+ | 'import'
+ | 'insensitive'
+ | 'insert'
+ | 'instance'
+ | 'intersect'
+ | 'into'
+ | 'is'
+ | 'language'
+ | 'last'
+ | 'le'
+ | 'least'
+ | 'let'
+ | 'levels'
+ | 'lowercase'
+ | 'lt'
+ | 'mod'
+ | 'modify'
+ | 'module'
+ | 'most'
+ | 'namespace'
+ | 'ne'
+ | 'no'
+ | 'nodes'
+ | 'not'
+ | 'occurs'
+ | 'only'
+ | 'or'
+ | 'order'
+ | 'ordered'
+ | 'paragraph'
+ | 'paragraphs'
+ | 'parent'
+ | 'phrase'
+ | 'preceding'
+ | 'preceding-sibling'
+ | 'relationship'
+ | 'rename'
+ | 'replace'
+ | 'return'
+ | 'revalidation'
+ | 'same'
+ | 'satisfies'
+ | 'score'
+ | 'self'
+ | 'sensitive'
+ | 'sentence'
+ | 'sentences'
+ | 'skip'
+ | 'some'
+ | 'stable'
+ | 'start'
+ | 'stemming'
+ | 'stop'
+ | 'thesaurus'
+ | 'times'
+ | 'to'
+ | 'treat'
+ | 'try'
+ | 'union'
+ | 'unordered'
+ | 'uppercase'
+ | 'using'
+ | 'validate'
+ | 'value'
+ | 'weight'
+ | 'where'
+ | 'wildcards'
+ | 'window'
+ | 'with'
+ | 'without'
+ | 'word'
+ | 'words'
+ | 'xquery'
+NCName ::= NCName^Token
+ | 'after'
+ | 'and'
+ | 'ascending'
+ | 'before'
+ | 'case'
+ | 'cast'
+ | 'castable'
+ | 'collation'
+ | 'contains'
+ | 'content'
+ | 'copy'
+ | 'count'
+ | 'default'
+ | 'delete'
+ | 'descending'
+ | 'diacritics'
+ | 'different'
+ | 'distance'
+ | 'div'
+ | 'else'
+ | 'empty'
+ | 'end'
+ | 'entire'
+ | 'eq'
+ | 'exactly'
+ | 'except'
+ | 'first'
+ | 'for'
+ | 'ftand'
+ | 'ftnot'
+ | 'ftor'
+ | 'ge'
+ | 'group'
+ | 'gt'
+ | 'idiv'
+ | 'insensitive'
+ | 'insert'
+ | 'instance'
+ | 'intersect'
+ | 'into'
+ | 'is'
+ | 'language'
+ | 'last'
+ | 'le'
+ | 'least'
+ | 'let'
+ | 'levels'
+ | 'lowercase'
+ | 'lt'
+ | 'mod'
+ | 'modify'
+ | 'most'
+ | 'ne'
+ | 'no'
+ | 'nodes'
+ | 'not'
+ | 'occurs'
+ | 'only'
+ | 'or'
+ | 'order'
+ | 'paragraph'
+ | 'paragraphs'
+ | 'phrase'
+ | 'relationship'
+ | 'rename'
+ | 'replace'
+ | 'return'
+ | 'revalidation'
+ | 'same'
+ | 'satisfies'
+ | 'score'
+ | 'sensitive'
+ | 'sentence'
+ | 'sentences'
+ | 'skip'
+ | 'stable'
+ | 'start'
+ | 'stemming'
+ | 'stop'
+ | 'thesaurus'
+ | 'times'
+ | 'to'
+ | 'treat'
+ | 'union'
+ | 'uppercase'
+ | 'using'
+ | 'value'
+ | 'weight'
+ | 'where'
+ | 'wildcards'
+ | 'with'
+ | 'without'
+ | 'word'
+ | 'words'
+Whitespace
+ ::= S^WS
+ | Comment
+ /* ws: definition */
+Comment ::= '(:' ( CommentContents | Comment )* ':)'
+ /* ws: explicit */
+
+
+
+IntegerLiteral
+ ::= Digits
+DecimalLiteral
+ ::= '.' Digits
+ | Digits '.' [0-9]*
+ /* ws: explicit */
+DoubleLiteral
+ ::= ( '.' Digits | Digits ( '.' [0-9]* )? ) [eE] [+#x002D]? Digits
+ /* ws: explicit */
+StringLiteral
+ ::= '"' ( PredefinedEntityRef | CharRef | EscapeQuot | [^"&] )* '"'
+ | "'" ( PredefinedEntityRef | CharRef | EscapeApos | [^'&] )* "'"
+ /* ws: explicit */
+URIQualifiedName
+ ::= BracedURILiteral NCName
+ /* ws: explicit */
+BracedURILiteral
+ ::= 'Q' '{' ( PredefinedEntityRef | CharRef | [^&{}] )* '}'
+ /* ws: explicit */
+PredefinedEntityRef
+ ::= '&' ( 'lt' | 'gt' | 'amp' | 'quot' | 'apos' ) ';'
+ /* ws: explicit */
+EscapeQuot
+ ::= '""'
+EscapeApos
+ ::= "''"
+ElementContentChar
+ ::= Char - [{}<&]
+QuotAttrContentChar
+ ::= Char - ["{}<&]
+AposAttrContentChar
+ ::= Char - ['{}<&]
+PITarget ::= NCName - ( ( 'X' | 'x' ) ( 'M' | 'm' ) ( 'L' | 'l' ) )
+NameStartChar
+ ::= ':'
+ | [A-Z]
+ | '_'
+ | [a-z]
+ | [#x00C0-#x00D6]
+ | [#x00D8-#x00F6]
+ | [#x00F8-#x02FF]
+ | [#x0370-#x037D]
+ | [#x037F-#x1FFF]
+ | [#x200C-#x200D]
+ | [#x2070-#x218F]
+ | [#x2C00-#x2FEF]
+ | [#x3001-#xD7FF]
+ | [#xF900-#xFDCF]
+ | [#xFDF0-#xFFFD]
+ | [#x10000-#xEFFFF]
+NameChar ::= NameStartChar
+ | '-'
+ | '.'
+ | [0-9]
+ | #x00B7
+ | [#x0300-#x036F]
+ | [#x203F-#x2040]
+Name ::= NameStartChar NameChar*
+CharRef ::= '' [0-9]+ ';'
+ | '' [0-9a-fA-F]+ ';'
+NCName ::= Name - ( Char* ':' Char* )
+QName ::= PrefixedName
+ | UnprefixedName
+PrefixedName
+ ::= Prefix ':' LocalPart
+UnprefixedName
+ ::= LocalPart
+Prefix ::= NCName
+LocalPart
+ ::= NCName
+StringConstructorChars
+ ::= ( Char* - ( Char* ( '`{' | ']``' ) Char* ) ) &( '`{' | ']`' )
+ /* ws: explicit */
+S ::= ( #x0020 | #x0009 | #x000D | #x000A )+
+Char ::= #x0009
+ | #x000A
+ | #x000D
+ | [#x0020-#xD7FF]
+ | [#xE000-#xFFFD]
+ | [#x10000-#x10FFFF]
+Digits ::= [0-9]+
+CommentContents
+ ::= ( ( Char+ - ( Char* ( '(:' | ':)' ) Char* ) ) - ( Char* '(' ) ) &':'
+ | ( Char+ - ( Char* ( '(:' | ':)' ) Char* ) ) &'('
+PragmaContents
+ ::= ( Char* - ( Char* '#)' Char* ) ) &'#'
+Wildcard ::= '*'
+ | NCName ':*'
+ | '*:' NCName
+ | BracedURILiteral '*'
+DirCommentContents
+ ::= ( Char - '-' | '-' ( Char - '-' ) )*
+DirPIContents
+ ::= ( Char* - ( Char* '?>' Char* ) ) &'?'
+CDataSectionContents
+ ::= ( Char* - ( Char* ']]>' Char* ) ) &']]'
+EOF ::= $
+NonNCNameChar
+ ::= $
+ | ':'
+ | Char - NameChar
+DelimitingChar
+ ::= NonNCNameChar
+ | '-'
+ | '.'
+DelimitingChar
+ \\ DecimalLiteral DoubleLiteral IntegerLiteral
+NonNCNameChar
+ \\ URIQualifiedName NCName^Token QName^Token 'NaN' 'after' 'all' 'allowing' 'ancestor' 'ancestor-or-self' 'and' 'any' 'array' 'as' 'ascending' 'at' 'attribute' 'base-uri' 'before' 'boundary-space' 'by' 'case' 'cast' 'castable' 'catch' 'child' 'collation' 'comment' 'construction' 'contains' 'content' 'context' 'copy' 'copy-namespaces' 'count' 'decimal-format' 'decimal-separator' 'declare' 'default' 'delete' 'descendant' 'descendant-or-self' 'descending' 'diacritics' 'different' 'digit' 'distance' 'div' 'document' 'document-node' 'element' 'else' 'empty' 'empty-sequence' 'encoding' 'end' 'entire' 'eq' 'every' 'exactly' 'except' 'exponent-separator' 'external' 'first' 'following' 'following-sibling' 'for' 'from' 'ft-option' 'ftand' 'ftnot' 'ftor' 'function' 'ge' 'greatest' 'group' 'grouping-separator' 'gt' 'idiv' 'if' 'import' 'in' 'infinity' 'inherit' 'insensitive' 'insert' 'instance' 'intersect' 'into' 'is' 'item' 'language' 'last' 'lax' 'le' 'least' 'let' 'levels' 'lowercase' 'lt' 'map' 'minus-sign' 'mod' 'modify' 'module' 'most' 'namespace' 'namespace-node' 'ne' 'next' 'no' 'no-inherit' 'no-preserve' 'node' 'nodes' 'not' 'occurs' 'of' 'only' 'option' 'or' 'order' 'ordered' 'ordering' 'paragraph' 'paragraphs' 'parent' 'pattern-separator' 'per-mille' 'percent' 'phrase' 'preceding' 'preceding-sibling' 'preserve' 'previous' 'processing-instruction' 'relationship' 'rename' 'replace' 'return' 'revalidation' 'same' 'satisfies' 'schema' 'schema-attribute' 'schema-element' 'score' 'self' 'sensitive' 'sentence' 'sentences' 'skip' 'sliding' 'some' 'stable' 'start' 'stemming' 'stop' 'strict' 'strip' 'switch' 'text' 'then' 'thesaurus' 'times' 'to' 'treat' 'try' 'tumbling' 'type' 'typeswitch' 'union' 'unordered' 'uppercase' 'using' 'validate' 'value' 'variable' 'version' 'weight' 'when' 'where' 'wildcards' 'window' 'with' 'without' 'word' 'words' 'xquery' 'zero-digit'
+'*' << Wildcard
+NCName^Token
+ << 'after' 'and' 'as' 'ascending' 'before' 'case' 'cast' 'castable' 'collation' 'contains' 'content' 'copy' 'count' 'default' 'delete' 'descending' 'diacritics' 'different' 'distance' 'div' 'else' 'empty' 'end' 'entire' 'eq' 'exactly' 'except' 'first' 'for' 'ftand' 'ftnot' 'ftor' 'ge' 'group' 'gt' 'idiv' 'insensitive' 'insert' 'instance' 'intersect' 'into' 'is' 'language' 'last' 'le' 'least' 'let' 'levels' 'lowercase' 'lt' 'mod' 'modify' 'most' 'ne' 'no' 'nodes' 'not' 'occurs' 'only' 'or' 'order' 'paragraph' 'paragraphs' 'phrase' 'relationship' 'rename' 'replace' 'return' 'revalidation' 'same' 'satisfies' 'score' 'sensitive' 'sentence' 'sentences' 'skip' 'stable' 'start' 'stemming' 'stop' 'thesaurus' 'times' 'to' 'treat' 'union' 'uppercase' 'using' 'value' 'weight' 'where' 'wildcards' 'with' 'without' 'word' 'words'
+QName^Token
+ << 'after' 'ancestor' 'ancestor-or-self' 'and' 'array' 'as' 'ascending' 'attribute' 'before' 'case' 'cast' 'castable' 'child' 'collation' 'comment' 'contains' 'content' 'copy' 'count' 'declare' 'default' 'delete' 'descendant' 'descendant-or-self' 'descending' 'diacritics' 'different' 'distance' 'div' 'document' 'document-node' 'element' 'else' 'empty' 'empty-sequence' 'end' 'entire' 'eq' 'every' 'exactly' 'except' 'first' 'following' 'following-sibling' 'for' 'ft-option' 'ftand' 'ftnot' 'ftor' 'function' 'ge' 'group' 'gt' 'idiv' 'if' 'import' 'insensitive' 'insert' 'instance' 'intersect' 'into' 'is' 'item' 'language' 'last' 'le' 'least' 'let' 'levels' 'lowercase' 'lt' 'map' 'mod' 'modify' 'module' 'most' 'namespace' 'namespace-node' 'ne' 'no' 'node' 'nodes' 'not' 'occurs' 'only' 'or' 'order' 'ordered' 'paragraph' 'paragraphs' 'parent' 'phrase' 'preceding' 'preceding-sibling' 'processing-instruction' 'relationship' 'rename' 'replace' 'return' 'revalidation' 'same' 'satisfies' 'schema-attribute' 'schema-element' 'score' 'self' 'sensitive' 'sentence' 'sentences' 'skip' 'some' 'stable' 'start' 'stemming' 'stop' 'switch' 'text' 'then' 'thesaurus' 'times' 'to' 'treat' 'try' 'typeswitch' 'union' 'unordered' 'uppercase' 'using' 'validate' 'value' 'weight' 'where' 'wildcards' 'window' 'with' 'without' 'word' 'words' 'xquery'
\ No newline at end of file
diff --git a/grammars/XQuery-31.ebnf b/grammars/XQuery-31.ebnf
new file mode 100644
index 00000000..67defc25
--- /dev/null
+++ b/grammars/XQuery-31.ebnf
@@ -0,0 +1,840 @@
+XQuery ::= Module EOF
+Module ::= VersionDecl? ( LibraryModule | MainModule )
+VersionDecl
+ ::= 'xquery' ( 'encoding' StringLiteral | 'version' StringLiteral ( 'encoding' StringLiteral )? ) Separator
+MainModule
+ ::= Prolog QueryBody
+LibraryModule
+ ::= ModuleDecl Prolog
+ModuleDecl
+ ::= 'module' 'namespace' NCName '=' URILiteral Separator
+Prolog ::= ( ( DefaultNamespaceDecl | Setter | NamespaceDecl | Import ) Separator )* ( ( ContextItemDecl | AnnotatedDecl | OptionDecl ) Separator )*
+Separator
+ ::= ';'
+Setter ::= BoundarySpaceDecl
+ | DefaultCollationDecl
+ | BaseURIDecl
+ | ConstructionDecl
+ | OrderingModeDecl
+ | EmptyOrderDecl
+ | CopyNamespacesDecl
+ | DecimalFormatDecl
+ | RevalidationDecl
+RevalidationDecl
+ ::= 'declare' 'revalidation' ( 'strict' | 'lax' | 'skip' )
+BoundarySpaceDecl
+ ::= 'declare' 'boundary-space' ( 'preserve' | 'strip' )
+DefaultCollationDecl
+ ::= 'declare' 'default' 'collation' URILiteral
+BaseURIDecl
+ ::= 'declare' 'base-uri' URILiteral
+ConstructionDecl
+ ::= 'declare' 'construction' ( 'strip' | 'preserve' )
+OrderingModeDecl
+ ::= 'declare' 'ordering' ( 'ordered' | 'unordered' )
+EmptyOrderDecl
+ ::= 'declare' 'default' 'order' 'empty' ( 'greatest' | 'least' )
+CopyNamespacesDecl
+ ::= 'declare' 'copy-namespaces' PreserveMode ',' InheritMode
+PreserveMode
+ ::= 'preserve'
+ | 'no-preserve'
+InheritMode
+ ::= 'inherit'
+ | 'no-inherit'
+DecimalFormatDecl
+ ::= 'declare' ( 'decimal-format' EQName | 'default' 'decimal-format' ) ( DFPropertyName '=' StringLiteral )*
+DFPropertyName
+ ::= 'decimal-separator'
+ | 'grouping-separator'
+ | 'infinity'
+ | 'minus-sign'
+ | 'NaN'
+ | 'percent'
+ | 'per-mille'
+ | 'zero-digit'
+ | 'digit'
+ | 'pattern-separator'
+ | 'exponent-separator'
+Import ::= SchemaImport
+ | ModuleImport
+SchemaImport
+ ::= 'import' 'schema' SchemaPrefix? URILiteral ( 'at' URILiteral ( ',' URILiteral )* )?
+SchemaPrefix
+ ::= 'namespace' NCName '='
+ | 'default' 'element' 'namespace'
+ModuleImport
+ ::= 'import' 'module' ( 'namespace' NCName '=' )? URILiteral ( 'at' URILiteral ( ',' URILiteral )* )?
+NamespaceDecl
+ ::= 'declare' 'namespace' NCName '=' URILiteral
+DefaultNamespaceDecl
+ ::= 'declare' 'default' ( 'element' | 'function' ) 'namespace' URILiteral
+AnnotatedDecl
+ ::= 'declare' Annotation* ( VarDecl | FunctionDecl )
+Annotation
+ ::= '%' EQName ( '(' Literal ( ',' Literal )* ')' )?
+VarDecl ::= 'variable' '$' VarName TypeDeclaration? ( ':=' VarValue | 'external' ( ':=' VarDefaultValue )? )
+VarValue ::= ExprSingle
+VarDefaultValue
+ ::= ExprSingle
+ContextItemDecl
+ ::= 'declare' 'context' 'item' ( 'as' ItemType )? ( ':=' VarValue | 'external' ( ':=' VarDefaultValue )? )
+FunctionDecl
+ ::= 'function' EQName '(' ParamList? ')' ( 'as' SequenceType )? ( FunctionBody | 'external' )
+ParamList
+ ::= Param ( ',' Param )*
+Param ::= '$' EQName TypeDeclaration?
+FunctionBody
+ ::= EnclosedExpr
+EnclosedExpr
+ ::= '{' Expr? '}'
+OptionDecl
+ ::= 'declare' 'option' EQName StringLiteral
+QueryBody
+ ::= Expr
+Expr ::= ExprSingle ( ',' ExprSingle )*
+ExprSingle
+ ::= FLWORExpr
+ | QuantifiedExpr
+ | SwitchExpr
+ | TypeswitchExpr
+ | IfExpr
+ | TryCatchExpr
+ | InsertExpr
+ | DeleteExpr
+ | RenameExpr
+ | ReplaceExpr
+ | TransformExpr
+ | OrExpr
+FLWORExpr
+ ::= InitialClause IntermediateClause* ReturnClause
+InitialClause
+ ::= ForClause
+ | LetClause
+ | WindowClause
+IntermediateClause
+ ::= InitialClause
+ | WhereClause
+ | GroupByClause
+ | OrderByClause
+ | CountClause
+ForClause
+ ::= 'for' ForBinding ( ',' ForBinding )*
+ForBinding
+ ::= '$' VarName TypeDeclaration? AllowingEmpty? PositionalVar? 'in' ExprSingle
+AllowingEmpty
+ ::= 'allowing' 'empty'
+PositionalVar
+ ::= 'at' '$' VarName
+LetClause
+ ::= 'let' LetBinding ( ',' LetBinding )*
+LetBinding
+ ::= '$' VarName TypeDeclaration? ':=' ExprSingle
+WindowClause
+ ::= 'for' ( TumblingWindowClause | SlidingWindowClause )
+TumblingWindowClause
+ ::= 'tumbling' 'window' '$' VarName TypeDeclaration? 'in' ExprSingle WindowStartCondition WindowEndCondition?
+SlidingWindowClause
+ ::= 'sliding' 'window' '$' VarName TypeDeclaration? 'in' ExprSingle WindowStartCondition WindowEndCondition
+WindowStartCondition
+ ::= 'start' WindowVars 'when' ExprSingle
+WindowEndCondition
+ ::= 'only'? 'end' WindowVars 'when' ExprSingle
+WindowVars
+ ::= ( '$' CurrentItem )? PositionalVar? ( 'previous' '$' PreviousItem )? ( 'next' '$' NextItem )?
+CurrentItem
+ ::= EQName
+PreviousItem
+ ::= EQName
+NextItem ::= EQName
+CountClause
+ ::= 'count' '$' VarName
+WhereClause
+ ::= 'where' ExprSingle
+GroupByClause
+ ::= 'group' 'by' GroupingSpecList
+GroupingSpecList
+ ::= GroupingSpec ( ',' GroupingSpec )*
+GroupingSpec
+ ::= GroupingVariable ( TypeDeclaration? ':=' ExprSingle )? ( 'collation' URILiteral )?
+GroupingVariable
+ ::= '$' VarName
+OrderByClause
+ ::= ( 'order' 'by' | 'stable' 'order' 'by' ) OrderSpecList
+OrderSpecList
+ ::= OrderSpec ( ',' OrderSpec )*
+OrderSpec
+ ::= ExprSingle OrderModifier
+OrderModifier
+ ::= ( 'ascending' | 'descending' )? ( 'empty' ( 'greatest' | 'least' ) )? ( 'collation' URILiteral )?
+ReturnClause
+ ::= 'return' ExprSingle
+QuantifiedExpr
+ ::= ( 'some' | 'every' ) '$' VarName TypeDeclaration? 'in' ExprSingle ( ',' '$' VarName TypeDeclaration? 'in' ExprSingle )* 'satisfies' ExprSingle
+SwitchExpr
+ ::= 'switch' '(' Expr ')' SwitchCaseClause+ 'default' 'return' ExprSingle
+SwitchCaseClause
+ ::= ( 'case' SwitchCaseOperand )+ 'return' ExprSingle
+SwitchCaseOperand
+ ::= ExprSingle
+TypeswitchExpr
+ ::= 'typeswitch' '(' Expr ')' CaseClause+ 'default' ( '$' VarName )? 'return' ExprSingle
+CaseClause
+ ::= 'case' ( '$' VarName 'as' )? SequenceTypeUnion 'return' ExprSingle
+SequenceTypeUnion
+ ::= SequenceType ( '|' SequenceType )*
+IfExpr ::= 'if' '(' Expr ')' 'then' ExprSingle 'else' ExprSingle
+TryCatchExpr
+ ::= TryClause CatchClause+
+TryClause
+ ::= 'try' EnclosedTryTargetExpr
+EnclosedTryTargetExpr
+ ::= EnclosedExpr
+CatchClause
+ ::= 'catch' CatchErrorList EnclosedExpr
+CatchErrorList
+ ::= NameTest ( '|' NameTest )*
+InsertExpr
+ ::= 'insert' ( 'node' | 'nodes' ) SourceExpr InsertExprTargetChoice TargetExpr
+InsertExprTargetChoice
+ ::= ( 'as' ( 'first' | 'last' ) )? 'into'
+ | 'after'
+ | 'before'
+SourceExpr
+ ::= ExprSingle
+TargetExpr
+ ::= ExprSingle
+DeleteExpr
+ ::= 'delete' ( 'node' | 'nodes' ) TargetExpr
+ReplaceExpr
+ ::= 'replace' ( 'value' 'of' )? 'node' TargetExpr 'with' ExprSingle
+RenameExpr
+ ::= 'rename' 'node' TargetExpr 'as' NewNameExpr
+NewNameExpr
+ ::= ExprSingle
+TransformExpr
+ ::= 'copy' '$' VarName ':=' ExprSingle ( ',' '$' VarName ':=' ExprSingle )* 'modify' ExprSingle 'return' ExprSingle
+OrExpr ::= AndExpr ( 'or' AndExpr )*
+AndExpr ::= ComparisonExpr ( 'and' ComparisonExpr )*
+ComparisonExpr
+ ::= StringConcatExpr ( ( ValueComp | GeneralComp | NodeComp ) StringConcatExpr )?
+StringConcatExpr
+ ::= RangeExpr ( '||' RangeExpr )*
+RangeExpr
+ ::= AdditiveExpr ( 'to' AdditiveExpr )?
+AdditiveExpr
+ ::= MultiplicativeExpr ( ( '+' | '-' ) MultiplicativeExpr )*
+MultiplicativeExpr
+ ::= UnionExpr ( ( '*' | 'div' | 'idiv' | 'mod' ) UnionExpr )*
+UnionExpr
+ ::= IntersectExceptExpr ( ( 'union' | '|' ) IntersectExceptExpr )*
+IntersectExceptExpr
+ ::= InstanceofExpr ( ( 'intersect' | 'except' ) InstanceofExpr )*
+InstanceofExpr
+ ::= TreatExpr ( 'instance' 'of' SequenceType )?
+TreatExpr
+ ::= CastableExpr ( 'treat' 'as' SequenceType )?
+CastableExpr
+ ::= CastExpr ( 'castable' 'as' SingleType )?
+CastExpr ::= ArrowExpr ( 'cast' 'as' SingleType )?
+ArrowExpr
+ ::= UnaryExpr ( '=>' ArrowFunctionSpecifier ArgumentList )*
+UnaryExpr
+ ::= ( '-' | '+' )* ValueExpr
+ValueExpr
+ ::= ValidateExpr
+ | ExtensionExpr
+ | SimpleMapExpr
+GeneralComp
+ ::= '='
+ | '!='
+ | '<'
+ | '<='
+ | '>'
+ | '>='
+ValueComp
+ ::= 'eq'
+ | 'ne'
+ | 'lt'
+ | 'le'
+ | 'gt'
+ | 'ge'
+NodeComp ::= 'is'
+ | '<<'
+ | '>>'
+ValidateExpr
+ ::= 'validate' ( ValidationMode | 'type' TypeName )? '{' Expr '}'
+ValidationMode
+ ::= 'lax'
+ | 'strict'
+ExtensionExpr
+ ::= Pragma+ '{' Expr? '}'
+Pragma ::= '(#' S? EQName ( S PragmaContents )? '#)'
+ /* ws: explicit */
+SimpleMapExpr
+ ::= PathExpr ( '!' PathExpr )*
+PathExpr ::= '/' ( RelativePathExpr / )
+ | '//' RelativePathExpr
+ | RelativePathExpr
+RelativePathExpr
+ ::= StepExpr ( ( '/' | '//' ) StepExpr )*
+StepExpr ::= PostfixExpr
+ | AxisStep
+AxisStep ::= ( ReverseStep | ForwardStep ) PredicateList
+ForwardStep
+ ::= ForwardAxis NodeTest
+ | AbbrevForwardStep
+ForwardAxis
+ ::= 'child' '::'
+ | 'descendant' '::'
+ | 'attribute' '::'
+ | 'self' '::'
+ | 'descendant-or-self' '::'
+ | 'following-sibling' '::'
+ | 'following' '::'
+AbbrevForwardStep
+ ::= '@'? NodeTest
+ReverseStep
+ ::= ReverseAxis NodeTest
+ | AbbrevReverseStep
+ReverseAxis
+ ::= 'parent' '::'
+ | 'ancestor' '::'
+ | 'preceding-sibling' '::'
+ | 'preceding' '::'
+ | 'ancestor-or-self' '::'
+AbbrevReverseStep
+ ::= '..'
+NodeTest ::= KindTest
+ | NameTest
+NameTest ::= EQName
+ | Wildcard
+PostfixExpr
+ ::= PrimaryExpr ( Predicate | ArgumentList | Lookup )*
+ArgumentList
+ ::= '(' ( Argument ( ',' Argument )* )? ')'
+PredicateList
+ ::= Predicate*
+Predicate
+ ::= '[' Expr ']'
+Lookup ::= '?' KeySpecifier
+KeySpecifier
+ ::= NCName
+ | IntegerLiteral
+ | ParenthesizedExpr
+ | '*'
+ArrowFunctionSpecifier
+ ::= EQName
+ | VarRef
+ | ParenthesizedExpr
+PrimaryExpr
+ ::= Literal
+ | VarRef
+ | ParenthesizedExpr
+ | ContextItemExpr
+ | FunctionCall
+ | OrderedExpr
+ | UnorderedExpr
+ | NodeConstructor
+ | FunctionItemExpr
+ | MapConstructor
+ | ArrayConstructor
+ | StringConstructor
+ | UnaryLookup
+Literal ::= NumericLiteral
+ | StringLiteral
+NumericLiteral
+ ::= IntegerLiteral
+ | DecimalLiteral
+ | DoubleLiteral
+VarRef ::= '$' VarName
+VarName ::= EQName
+ParenthesizedExpr
+ ::= '(' Expr? ')'
+ContextItemExpr
+ ::= '.'
+OrderedExpr
+ ::= 'ordered' EnclosedExpr
+UnorderedExpr
+ ::= 'unordered' EnclosedExpr
+FunctionCall
+ ::= FunctionEQName ArgumentList
+Argument ::= ExprSingle
+ | ArgumentPlaceholder
+ArgumentPlaceholder
+ ::= '?'
+NodeConstructor
+ ::= DirectConstructor
+ | ComputedConstructor
+DirectConstructor
+ ::= DirElemConstructor
+ | DirCommentConstructor
+ | DirPIConstructor
+DirElemConstructor
+ ::= '<' QName DirAttributeList ( '/>' | '>' DirElemContent* '' QName S? '>' )
+ /* ws: explicit */
+DirAttributeList
+ ::= ( S ( QName S? '=' S? DirAttributeValue )? )*
+ /* ws: explicit */
+DirAttributeValue
+ ::= '"' ( EscapeQuot | QuotAttrValueContent )* '"'
+ | "'" ( EscapeApos | AposAttrValueContent )* "'"
+ /* ws: explicit */
+QuotAttrValueContent
+ ::= QuotAttrContentChar
+ | CommonContent
+AposAttrValueContent
+ ::= AposAttrContentChar
+ | CommonContent
+DirElemContent
+ ::= DirectConstructor
+ | CDataSection
+ | CommonContent
+ | ElementContentChar
+CommonContent
+ ::= PredefinedEntityRef
+ | CharRef
+ | '{{'
+ | '}}'
+ | EnclosedExpr
+DirCommentConstructor
+ ::= ''
+ /* ws: explicit */
+DirPIConstructor
+ ::= '' PITarget ( S DirPIContents )? '?>'
+ /* ws: explicit */
+CDataSection
+ ::= ''
+ /* ws: explicit */
+ComputedConstructor
+ ::= CompDocConstructor
+ | CompElemConstructor
+ | CompAttrConstructor
+ | CompNamespaceConstructor
+ | CompTextConstructor
+ | CompCommentConstructor
+ | CompPIConstructor
+CompDocConstructor
+ ::= 'document' EnclosedExpr
+CompElemConstructor
+ ::= 'element' ( EQName | '{' Expr '}' ) EnclosedContentExpr
+EnclosedContentExpr
+ ::= EnclosedExpr
+CompAttrConstructor
+ ::= 'attribute' ( EQName | '{' Expr '}' ) EnclosedExpr
+CompNamespaceConstructor
+ ::= 'namespace' ( Prefix | EnclosedPrefixExpr ) EnclosedURIExpr
+Prefix ::= NCName
+EnclosedPrefixExpr
+ ::= EnclosedExpr
+EnclosedURIExpr
+ ::= EnclosedExpr
+CompTextConstructor
+ ::= 'text' EnclosedExpr
+CompCommentConstructor
+ ::= 'comment' EnclosedExpr
+CompPIConstructor
+ ::= 'processing-instruction' ( NCName | '{' Expr '}' ) EnclosedExpr
+FunctionItemExpr
+ ::= NamedFunctionRef
+ | InlineFunctionExpr
+NamedFunctionRef
+ ::= EQName '#' IntegerLiteral
+InlineFunctionExpr
+ ::= Annotation* 'function' '(' ParamList? ')' ( 'as' SequenceType )? FunctionBody
+MapConstructor
+ ::= 'map' '{' ( MapConstructorEntry ( ',' MapConstructorEntry )* )? '}'
+MapConstructorEntry
+ ::= MapKeyExpr ':' MapValueExpr
+MapKeyExpr
+ ::= ExprSingle
+MapValueExpr
+ ::= ExprSingle
+ArrayConstructor
+ ::= SquareArrayConstructor
+ | CurlyArrayConstructor
+SquareArrayConstructor
+ ::= '[' ( ExprSingle ( ',' ExprSingle )* )? ']'
+CurlyArrayConstructor
+ ::= 'array' EnclosedExpr
+StringConstructor
+ ::= '``[' StringConstructorContent ']``'
+ /* ws: explicit */
+StringConstructorContent
+ ::= StringConstructorChars ( StringConstructorInterpolation StringConstructorChars )*
+ /* ws: explicit */
+StringConstructorInterpolation
+ ::= '`{' Expr? '}`'
+UnaryLookup
+ ::= '?' KeySpecifier
+SingleType
+ ::= SimpleTypeName '?'?
+TypeDeclaration
+ ::= 'as' SequenceType
+SequenceType
+ ::= 'empty-sequence' '(' ')'
+ | ItemType ( OccurrenceIndicator / )
+OccurrenceIndicator
+ ::= '?'
+ | '*'
+ | '+'
+ItemType ::= KindTest
+ | 'item' '(' ')'
+ | FunctionTest
+ | MapTest
+ | ArrayTest
+ | AtomicOrUnionType
+ | ParenthesizedItemType
+AtomicOrUnionType
+ ::= EQName
+KindTest ::= DocumentTest
+ | ElementTest
+ | AttributeTest
+ | SchemaElementTest
+ | SchemaAttributeTest
+ | PITest
+ | CommentTest
+ | TextTest
+ | NamespaceNodeTest
+ | AnyKindTest
+AnyKindTest
+ ::= 'node' '(' ')'
+DocumentTest
+ ::= 'document-node' '(' ( ElementTest | SchemaElementTest )? ')'
+TextTest ::= 'text' '(' ')'
+CommentTest
+ ::= 'comment' '(' ')'
+NamespaceNodeTest
+ ::= 'namespace-node' '(' ')'
+PITest ::= 'processing-instruction' '(' ( NCName | StringLiteral )? ')'
+AttributeTest
+ ::= 'attribute' '(' ( AttribNameOrWildcard ( ',' TypeName )? )? ')'
+AttribNameOrWildcard
+ ::= AttributeName
+ | '*'
+SchemaAttributeTest
+ ::= 'schema-attribute' '(' AttributeDeclaration ')'
+AttributeDeclaration
+ ::= AttributeName
+ElementTest
+ ::= 'element' '(' ( ElementNameOrWildcard ( ',' TypeName '?'? )? )? ')'
+ElementNameOrWildcard
+ ::= ElementName
+ | '*'
+SchemaElementTest
+ ::= 'schema-element' '(' ElementDeclaration ')'
+ElementDeclaration
+ ::= ElementName
+AttributeName
+ ::= EQName
+ElementName
+ ::= EQName
+SimpleTypeName
+ ::= TypeName
+TypeName ::= EQName
+FunctionTest
+ ::= Annotation* ( AnyFunctionTest | TypedFunctionTest )
+AnyFunctionTest
+ ::= 'function' '(' '*' ')'
+TypedFunctionTest
+ ::= 'function' '(' ( SequenceType ( ',' SequenceType )* )? ')' 'as' SequenceType
+MapTest ::= AnyMapTest
+ | TypedMapTest
+AnyMapTest
+ ::= 'map' '(' '*' ')'
+TypedMapTest
+ ::= 'map' '(' AtomicOrUnionType ',' SequenceType ')'
+ArrayTest
+ ::= AnyArrayTest
+ | TypedArrayTest
+AnyArrayTest
+ ::= 'array' '(' '*' ')'
+TypedArrayTest
+ ::= 'array' '(' SequenceType ')'
+ParenthesizedItemType
+ ::= '(' ItemType ')'
+URILiteral
+ ::= StringLiteral
+EQName ::= QName
+ | URIQualifiedName
+FunctionEQName
+ ::= FunctionName
+ | URIQualifiedName
+QName ::= FunctionName
+ | 'array'
+ | 'attribute'
+ | 'comment'
+ | 'document-node'
+ | 'element'
+ | 'empty-sequence'
+ | 'function'
+ | 'if'
+ | 'item'
+ | 'map'
+ | 'namespace-node'
+ | 'node'
+ | 'processing-instruction'
+ | 'schema-attribute'
+ | 'schema-element'
+ | 'switch'
+ | 'text'
+ | 'typeswitch'
+FunctionName
+ ::= QName^Token
+ | 'after'
+ | 'ancestor'
+ | 'ancestor-or-self'
+ | 'and'
+ | 'ascending'
+ | 'before'
+ | 'case'
+ | 'cast'
+ | 'castable'
+ | 'child'
+ | 'collation'
+ | 'copy'
+ | 'count'
+ | 'declare'
+ | 'default'
+ | 'delete'
+ | 'descendant'
+ | 'descendant-or-self'
+ | 'descending'
+ | 'div'
+ | 'document'
+ | 'else'
+ | 'empty'
+ | 'end'
+ | 'eq'
+ | 'every'
+ | 'except'
+ | 'first'
+ | 'following'
+ | 'following-sibling'
+ | 'for'
+ | 'ge'
+ | 'group'
+ | 'gt'
+ | 'idiv'
+ | 'import'
+ | 'insert'
+ | 'instance'
+ | 'intersect'
+ | 'into'
+ | 'is'
+ | 'last'
+ | 'le'
+ | 'let'
+ | 'lt'
+ | 'mod'
+ | 'modify'
+ | 'module'
+ | 'namespace'
+ | 'ne'
+ | 'nodes'
+ | 'only'
+ | 'or'
+ | 'order'
+ | 'ordered'
+ | 'parent'
+ | 'preceding'
+ | 'preceding-sibling'
+ | 'rename'
+ | 'replace'
+ | 'return'
+ | 'revalidation'
+ | 'satisfies'
+ | 'self'
+ | 'skip'
+ | 'some'
+ | 'stable'
+ | 'start'
+ | 'to'
+ | 'treat'
+ | 'try'
+ | 'union'
+ | 'unordered'
+ | 'validate'
+ | 'value'
+ | 'where'
+ | 'with'
+ | 'xquery'
+NCName ::= NCName^Token
+ | 'after'
+ | 'and'
+ | 'ascending'
+ | 'before'
+ | 'case'
+ | 'cast'
+ | 'castable'
+ | 'collation'
+ | 'copy'
+ | 'count'
+ | 'default'
+ | 'delete'
+ | 'descending'
+ | 'div'
+ | 'else'
+ | 'empty'
+ | 'end'
+ | 'eq'
+ | 'except'
+ | 'first'
+ | 'for'
+ | 'ge'
+ | 'group'
+ | 'gt'
+ | 'idiv'
+ | 'insert'
+ | 'instance'
+ | 'intersect'
+ | 'into'
+ | 'is'
+ | 'last'
+ | 'le'
+ | 'let'
+ | 'lt'
+ | 'mod'
+ | 'modify'
+ | 'ne'
+ | 'nodes'
+ | 'only'
+ | 'or'
+ | 'order'
+ | 'rename'
+ | 'replace'
+ | 'return'
+ | 'revalidation'
+ | 'satisfies'
+ | 'skip'
+ | 'stable'
+ | 'start'
+ | 'to'
+ | 'treat'
+ | 'union'
+ | 'value'
+ | 'where'
+ | 'with'
+Whitespace
+ ::= S^WS
+ | Comment
+ /* ws: definition */
+Comment ::= '(:' ( CommentContents | Comment )* ':)'
+ /* ws: explicit */
+
+
+
+IntegerLiteral
+ ::= Digits
+DecimalLiteral
+ ::= '.' Digits
+ | Digits '.' [0-9]*
+ /* ws: explicit */
+DoubleLiteral
+ ::= ( '.' Digits | Digits ( '.' [0-9]* )? ) [eE] [+#x002D]? Digits
+ /* ws: explicit */
+StringLiteral
+ ::= '"' ( PredefinedEntityRef | CharRef | EscapeQuot | [^"&] )* '"'
+ | "'" ( PredefinedEntityRef | CharRef | EscapeApos | [^'&] )* "'"
+ /* ws: explicit */
+URIQualifiedName
+ ::= BracedURILiteral NCName
+ /* ws: explicit */
+BracedURILiteral
+ ::= 'Q' '{' ( PredefinedEntityRef | CharRef | [^&{}] )* '}'
+ /* ws: explicit */
+PredefinedEntityRef
+ ::= '&' ( 'lt' | 'gt' | 'amp' | 'quot' | 'apos' ) ';'
+ /* ws: explicit */
+EscapeQuot
+ ::= '""'
+EscapeApos
+ ::= "''"
+ElementContentChar
+ ::= Char - [{}<&]
+QuotAttrContentChar
+ ::= Char - ["{}<&]
+AposAttrContentChar
+ ::= Char - ['{}<&]
+PITarget ::= NCName - ( ( 'X' | 'x' ) ( 'M' | 'm' ) ( 'L' | 'l' ) )
+NameStartChar
+ ::= ':'
+ | [A-Z]
+ | '_'
+ | [a-z]
+ | [#x00C0-#x00D6]
+ | [#x00D8-#x00F6]
+ | [#x00F8-#x02FF]
+ | [#x0370-#x037D]
+ | [#x037F-#x1FFF]
+ | [#x200C-#x200D]
+ | [#x2070-#x218F]
+ | [#x2C00-#x2FEF]
+ | [#x3001-#xD7FF]
+ | [#xF900-#xFDCF]
+ | [#xFDF0-#xFFFD]
+ | [#x10000-#xEFFFF]
+NameChar ::= NameStartChar
+ | '-'
+ | '.'
+ | [0-9]
+ | #x00B7
+ | [#x0300-#x036F]
+ | [#x203F-#x2040]
+Name ::= NameStartChar NameChar*
+CharRef ::= '' [0-9]+ ';'
+ | '' [0-9a-fA-F]+ ';'
+NCName ::= Name - ( Char* ':' Char* )
+QName ::= PrefixedName
+ | UnprefixedName
+PrefixedName
+ ::= Prefix ':' LocalPart
+UnprefixedName
+ ::= LocalPart
+Prefix ::= NCName
+LocalPart
+ ::= NCName
+StringConstructorChars
+ ::= ( Char* - ( Char* ( '`{' | ']``' ) Char* ) ) &( '`{' | ']`' )
+ /* ws: explicit */
+S ::= ( #x0020 | #x0009 | #x000D | #x000A )+
+Char ::= #x0009
+ | #x000A
+ | #x000D
+ | [#x0020-#xD7FF]
+ | [#xE000-#xFFFD]
+ | [#x10000-#x10FFFF]
+Digits ::= [0-9]+
+CommentContents
+ ::= ( ( Char+ - ( Char* ( '(:' | ':)' ) Char* ) ) - ( Char* '(' ) ) &':'
+ | ( Char+ - ( Char* ( '(:' | ':)' ) Char* ) ) &'('
+PragmaContents
+ ::= ( Char* - ( Char* '#)' Char* ) ) &'#'
+Wildcard ::= '*'
+ | NCName ':*'
+ | '*:' NCName
+ | BracedURILiteral '*'
+DirCommentContents
+ ::= ( Char - '-' | '-' ( Char - '-' ) )*
+DirPIContents
+ ::= ( Char* - ( Char* '?>' Char* ) ) &'?'
+CDataSectionContents
+ ::= ( Char* - ( Char* ']]>' Char* ) ) &']]'
+EOF ::= $
+NonNCNameChar
+ ::= $
+ | ':'
+ | Char - NameChar
+DelimitingChar
+ ::= NonNCNameChar
+ | '-'
+ | '.'
+DelimitingChar
+ \\ DecimalLiteral DoubleLiteral IntegerLiteral
+NonNCNameChar
+ \\ URIQualifiedName NCName^Token QName^Token 'NaN' 'after' 'allowing' 'ancestor' 'ancestor-or-self' 'and' 'array' 'as' 'ascending' 'at' 'attribute' 'base-uri' 'before' 'boundary-space' 'by' 'case' 'cast' 'castable' 'catch' 'child' 'collation' 'comment' 'construction' 'context' 'copy' 'copy-namespaces' 'count' 'decimal-format' 'decimal-separator' 'declare' 'default' 'delete' 'descendant' 'descendant-or-self' 'descending' 'digit' 'div' 'document' 'document-node' 'element' 'else' 'empty' 'empty-sequence' 'encoding' 'end' 'eq' 'every' 'except' 'exponent-separator' 'external' 'first' 'following' 'following-sibling' 'for' 'function' 'ge' 'greatest' 'group' 'grouping-separator' 'gt' 'idiv' 'if' 'import' 'in' 'infinity' 'inherit' 'insert' 'instance' 'intersect' 'into' 'is' 'item' 'last' 'lax' 'le' 'least' 'let' 'lt' 'map' 'minus-sign' 'mod' 'modify' 'module' 'namespace' 'namespace-node' 'ne' 'next' 'no-inherit' 'no-preserve' 'node' 'nodes' 'of' 'only' 'option' 'or' 'order' 'ordered' 'ordering' 'parent' 'pattern-separator' 'per-mille' 'percent' 'preceding' 'preceding-sibling' 'preserve' 'previous' 'processing-instruction' 'rename' 'replace' 'return' 'revalidation' 'satisfies' 'schema' 'schema-attribute' 'schema-element' 'self' 'skip' 'sliding' 'some' 'stable' 'start' 'strict' 'strip' 'switch' 'text' 'then' 'to' 'treat' 'try' 'tumbling' 'type' 'typeswitch' 'union' 'unordered' 'validate' 'value' 'variable' 'version' 'when' 'where' 'window' 'with' 'xquery' 'zero-digit'
+'*' << Wildcard
+NCName^Token
+ << 'after' 'and' 'as' 'ascending' 'before' 'case' 'cast' 'castable' 'collation' 'copy' 'count' 'default' 'delete' 'descending' 'div' 'else' 'empty' 'end' 'eq' 'except' 'first' 'for' 'ge' 'group' 'gt' 'idiv' 'insert' 'instance' 'intersect' 'into' 'is' 'last' 'le' 'let' 'lt' 'mod' 'modify' 'ne' 'nodes' 'only' 'or' 'order' 'rename' 'replace' 'return' 'revalidation' 'satisfies' 'skip' 'stable' 'start' 'to' 'treat' 'union' 'value' 'where' 'with'
+QName^Token
+ << 'after' 'ancestor' 'ancestor-or-self' 'and' 'array' 'as' 'ascending' 'attribute' 'before' 'case' 'cast' 'castable' 'child' 'collation' 'comment' 'copy' 'count' 'declare' 'default' 'delete' 'descendant' 'descendant-or-self' 'descending' 'div' 'document' 'document-node' 'element' 'else' 'empty' 'empty-sequence' 'end' 'eq' 'every' 'except' 'first' 'following' 'following-sibling' 'for' 'function' 'ge' 'group' 'gt' 'idiv' 'if' 'import' 'insert' 'instance' 'intersect' 'into' 'is' 'item' 'last' 'le' 'let' 'lt' 'map' 'mod' 'modify' 'module' 'namespace' 'namespace-node' 'ne' 'node' 'nodes' 'only' 'or' 'order' 'ordered' 'parent' 'preceding' 'preceding-sibling' 'processing-instruction' 'rename' 'replace' 'return' 'revalidation' 'satisfies' 'schema-attribute' 'schema-element' 'self' 'skip' 'some' 'stable' 'start' 'switch' 'text' 'to' 'treat' 'try' 'typeswitch' 'union' 'unordered' 'validate' 'value' 'where' 'with' 'xquery'
\ No newline at end of file
diff --git a/grammars/XQuery-40-Family-XQUFEL.ebnf b/grammars/XQuery-40-Family-XQUFEL.ebnf
new file mode 100644
index 00000000..12b599c3
--- /dev/null
+++ b/grammars/XQuery-40-Family-XQUFEL.ebnf
@@ -0,0 +1,1611 @@
+/* Combined grammar: XQuery 4.0 + XQUF 3.0 + XQFT 1.0 + XQUFEL (XQuery Update Facility eXist Legacy) */
+
+/* XQuery 4.0: An XML Query Language
+ * version https://qt4cg.org/specifications/xquery-40/
+ * extracted from https://qt4cg.org/specifications/xquery-40/xquery-40.html on Tue Feb 17, 2026, 21:31 (UTC+01)
+ * with references to https://www.w3.org/TR/REC-xml/ resolved and inlined
+ * with references to https://www.w3.org/TR/REC-xml-names/ resolved and inlined
+ * reordered into depth-first order
+ * adapted for REx by rexify-xquery-40.xq
+ */
+
+XQuery ::= Module EOF
+Module ::= VersionDecl? ( LibraryModule | MainModule )
+VersionDecl
+ ::= 'xquery' ( 'encoding' StringLiteral | 'version' StringLiteral ( 'encoding' StringLiteral )? ) Separator
+Separator
+ ::= ';'
+LibraryModule
+ ::= ModuleDecl Prolog
+ModuleDecl
+ ::= 'module' 'namespace' NCName '=' URILiteral Separator
+NCName ::= UnreservedNCName
+ | 'NaN'
+ | 'after'
+ | 'all'
+ | 'allowing'
+ | 'ancestor'
+ | 'ancestor-or-self'
+ | 'and'
+ | 'any'
+ | 'array'
+ | 'as'
+ | 'ascending'
+ | 'at'
+ | 'attribute'
+ | 'base-uri'
+ | 'before'
+ | 'boundary-space'
+ | 'by'
+ | 'case'
+ | 'cast'
+ | 'castable'
+ | 'catch'
+ | 'child'
+ | 'collation'
+ | 'comment'
+ | 'construction'
+ | 'contains'
+ | 'content'
+ | 'context'
+ | 'copy'
+ | 'copy-namespaces'
+ | 'count'
+ | 'decimal-format'
+ | 'decimal-separator'
+ | 'declare'
+ | 'default'
+ | 'delete'
+ | 'descendant'
+ | 'descendant-or-self'
+ | 'descending'
+ | 'diacritics'
+ | 'different'
+ | 'digit'
+ | 'distance'
+ | 'div'
+ | 'document'
+ | 'document-node'
+ | 'element'
+ | 'else'
+ | 'empty'
+ | 'empty-sequence'
+ | 'encoding'
+ | 'end'
+ | 'entire'
+ | 'enum'
+ | 'eq'
+ | 'every'
+ | 'exactly'
+ | 'except'
+ | 'exponent-separator'
+ | 'external'
+ | 'false'
+ | 'finally'
+ | 'first'
+ | 'fixed'
+ | 'fn'
+ | 'following'
+ | 'following-or-self'
+ | 'following-sibling'
+ | 'following-sibling-or-self'
+ | 'follows'
+ | 'follows-or-is'
+ | 'for'
+ | 'from'
+ | 'ft-option'
+ | 'ftand'
+ | 'ftnot'
+ | 'ftor'
+ | 'function'
+ | 'ge'
+ | 'get'
+ | 'gnode'
+ | 'greatest'
+ | 'group'
+ | 'grouping-separator'
+ | 'gt'
+ | 'idiv'
+ | 'if'
+ | 'import'
+ | 'in'
+ | 'infinity'
+ | 'inherit'
+ | 'insert'
+ | 'insensitive'
+ | 'instance'
+ | 'intersect'
+ | 'into'
+ | 'is'
+ | 'is-not'
+ | 'item'
+ | 'jnode'
+ | 'key'
+ | 'language'
+ | 'last'
+ | 'lax'
+ | 'le'
+ | 'least'
+ | 'let'
+ | 'levels'
+ | 'lowercase'
+ | 'lt'
+ | 'map'
+ | 'member'
+ | 'minus-sign'
+ | 'mod'
+ | 'modify'
+ | 'module'
+ | 'most'
+ | 'namespace'
+ | 'namespace-node'
+ | 'ne'
+ | 'next'
+ | 'no'
+ | 'no-inherit'
+ | 'no-preserve'
+ | 'node'
+ | 'nodes'
+ | 'not'
+ | 'occurs'
+ | 'of'
+ | 'only'
+ | 'option'
+ | 'or'
+ | 'order'
+ | 'ordered'
+ | 'ordering'
+ | 'otherwise'
+ | 'paragraph'
+ | 'paragraphs'
+ | 'parent'
+ | 'pattern-separator'
+ | 'per-mille'
+ | 'percent'
+ | 'phrase'
+ | 'precedes'
+ | 'precedes-or-is'
+ | 'preceding'
+ | 'preceding-or-self'
+ | 'preceding-sibling'
+ | 'preceding-sibling-or-self'
+ | 'preserve'
+ | 'previous'
+ | 'processing-instruction'
+ | 'record'
+ | 'relationship'
+ | 'rename'
+ | 'replace'
+ | 'return'
+ | 'revalidation'
+ | 'same'
+ | 'satisfies'
+ | 'schema'
+ | 'schema-attribute'
+ | 'schema-element'
+ | 'score'
+ | 'self'
+ | 'sensitive'
+ | 'sentence'
+ | 'sentences'
+ | 'skip'
+ | 'sliding'
+ | 'some'
+ | 'stable'
+ | 'start'
+ | 'stemming'
+ | 'stop'
+ | 'strict'
+ | 'strip'
+ | 'switch'
+ | 'text'
+ | 'then'
+ | 'thesaurus'
+ | 'times'
+ | 'to'
+ | 'treat'
+ | 'true'
+ | 'try'
+ | 'tumbling'
+ | 'type'
+ | 'typeswitch'
+ | 'union'
+ | 'unordered'
+ | 'update'
+ | 'uppercase'
+ | 'using'
+ | 'validate'
+ | 'value'
+ | 'variable'
+ | 'version'
+ | 'weight'
+ | 'when'
+ | 'where'
+ | 'while'
+ | 'wildcards'
+ | 'window'
+ | 'with'
+ | 'without'
+ | 'word'
+ | 'words'
+ | 'xquery'
+ | 'zero-digit'
+URILiteral
+ ::= StringLiteral
+Prolog ::= ( ( DefaultNamespaceDecl | Setter | NamespaceDecl | Import | FTOptionDecl ) Separator )* ( ( ContextValueDecl | VarDecl | FunctionDecl | ItemTypeDecl | NamedRecordTypeDecl | OptionDecl ) Separator )*
+DefaultNamespaceDecl
+ ::= 'declare' 'fixed'? 'default' ( 'element' | 'function' ) 'namespace' URILiteral
+Setter ::= BoundarySpaceDecl
+ | DefaultCollationDecl
+ | BaseURIDecl
+ | ConstructionDecl
+ | OrderingModeDecl
+ | EmptyOrderDecl
+ | CopyNamespacesDecl
+ | DecimalFormatDecl
+ | RevalidationDecl
+RevalidationDecl
+ ::= 'declare' 'revalidation' ( 'strict' | 'lax' | 'skip' )
+BoundarySpaceDecl
+ ::= 'declare' 'boundary-space' ( 'preserve' | 'strip' )
+DefaultCollationDecl
+ ::= 'declare' 'default' 'collation' URILiteral
+BaseURIDecl
+ ::= 'declare' 'base-uri' URILiteral
+ConstructionDecl
+ ::= 'declare' 'construction' ( 'strip' | 'preserve' )
+OrderingModeDecl
+ ::= 'declare' 'ordering' ( 'ordered' | 'unordered' )
+EmptyOrderDecl
+ ::= 'declare' 'default' 'order' 'empty' ( 'greatest' | 'least' )
+CopyNamespacesDecl
+ ::= 'declare' 'copy-namespaces' PreserveMode ',' InheritMode
+PreserveMode
+ ::= 'preserve'
+ | 'no-preserve'
+InheritMode
+ ::= 'inherit'
+ | 'no-inherit'
+DecimalFormatDecl
+ ::= 'declare' ( 'decimal-format' EQName | 'default' 'decimal-format' ) ( DFPropertyName '=' StringLiteral )*
+EQName ::= QName
+ | URIQualifiedName
+QName ::= UnreservedFunctionQName
+ | 'attribute'
+ | 'comment'
+ | 'document-node'
+ | 'element'
+ | 'namespace-node'
+ | 'node'
+ | 'processing-instruction'
+ | 'schema-attribute'
+ | 'schema-element'
+ | 'text'
+ | 'array'
+ | 'enum'
+ | 'fn'
+ | 'function'
+ | 'gnode'
+ | 'get'
+ | 'if'
+ | 'item'
+ | 'jnode'
+ | 'map'
+ | 'record'
+ | 'switch'
+ | 'type'
+ | 'typeswitch'
+UnreservedFunctionQName
+ ::= UnreservedQName
+ | 'NaN'
+ | 'after'
+ | 'all'
+ | 'allowing'
+ | 'ancestor'
+ | 'ancestor-or-self'
+ | 'and'
+ | 'any'
+ | 'as'
+ | 'ascending'
+ | 'at'
+ | 'base-uri'
+ | 'before'
+ | 'boundary-space'
+ | 'by'
+ | 'case'
+ | 'cast'
+ | 'castable'
+ | 'catch'
+ | 'child'
+ | 'collation'
+ | 'construction'
+ | 'contains'
+ | 'content'
+ | 'context'
+ | 'copy'
+ | 'copy-namespaces'
+ | 'count'
+ | 'decimal-format'
+ | 'decimal-separator'
+ | 'declare'
+ | 'default'
+ | 'delete'
+ | 'descendant'
+ | 'descendant-or-self'
+ | 'descending'
+ | 'diacritics'
+ | 'different'
+ | 'digit'
+ | 'distance'
+ | 'div'
+ | 'document'
+ | 'else'
+ | 'empty'
+ | 'empty-sequence'
+ | 'encoding'
+ | 'end'
+ | 'entire'
+ | 'eq'
+ | 'every'
+ | 'exactly'
+ | 'except'
+ | 'exponent-separator'
+ | 'external'
+ | 'false'
+ | 'finally'
+ | 'first'
+ | 'fixed'
+ | 'following'
+ | 'following-or-self'
+ | 'following-sibling'
+ | 'following-sibling-or-self'
+ | 'follows'
+ | 'follows-or-is'
+ | 'for'
+ | 'from'
+ | 'ft-option'
+ | 'ftand'
+ | 'ftnot'
+ | 'ftor'
+ | 'ge'
+ | 'greatest'
+ | 'group'
+ | 'grouping-separator'
+ | 'gt'
+ | 'idiv'
+ | 'import'
+ | 'in'
+ | 'infinity'
+ | 'inherit'
+ | 'insert'
+ | 'insensitive'
+ | 'instance'
+ | 'intersect'
+ | 'into'
+ | 'is'
+ | 'is-not'
+ | 'key'
+ | 'language'
+ | 'last'
+ | 'lax'
+ | 'le'
+ | 'least'
+ | 'let'
+ | 'levels'
+ | 'lowercase'
+ | 'lt'
+ | 'member'
+ | 'minus-sign'
+ | 'mod'
+ | 'modify'
+ | 'module'
+ | 'most'
+ | 'namespace'
+ | 'ne'
+ | 'next'
+ | 'no'
+ | 'no-inherit'
+ | 'no-preserve'
+ | 'nodes'
+ | 'not'
+ | 'occurs'
+ | 'of'
+ | 'only'
+ | 'option'
+ | 'or'
+ | 'order'
+ | 'ordered'
+ | 'ordering'
+ | 'otherwise'
+ | 'paragraph'
+ | 'paragraphs'
+ | 'parent'
+ | 'pattern-separator'
+ | 'per-mille'
+ | 'percent'
+ | 'phrase'
+ | 'precedes'
+ | 'precedes-or-is'
+ | 'preceding'
+ | 'preceding-or-self'
+ | 'preceding-sibling'
+ | 'preceding-sibling-or-self'
+ | 'preserve'
+ | 'previous'
+ | 'relationship'
+ | 'rename'
+ | 'replace'
+ | 'return'
+ | 'revalidation'
+ | 'same'
+ | 'satisfies'
+ | 'schema'
+ | 'score'
+ | 'self'
+ | 'sensitive'
+ | 'sentence'
+ | 'sentences'
+ | 'skip'
+ | 'sliding'
+ | 'some'
+ | 'stable'
+ | 'start'
+ | 'stemming'
+ | 'stop'
+ | 'strict'
+ | 'strip'
+ | 'thesaurus'
+ | 'then'
+ | 'times'
+ | 'to'
+ | 'treat'
+ | 'true'
+ | 'try'
+ | 'tumbling'
+ | 'union'
+ | 'unordered'
+ | 'update'
+ | 'uppercase'
+ | 'using'
+ | 'validate'
+ | 'value'
+ | 'variable'
+ | 'version'
+ | 'weight'
+ | 'when'
+ | 'where'
+ | 'while'
+ | 'wildcards'
+ | 'window'
+ | 'with'
+ | 'without'
+ | 'word'
+ | 'words'
+ | 'xquery'
+ | 'zero-digit'
+DFPropertyName
+ ::= 'decimal-separator'
+ | 'grouping-separator'
+ | 'infinity'
+ | 'minus-sign'
+ | 'NaN'
+ | 'percent'
+ | 'per-mille'
+ | 'zero-digit'
+ | 'digit'
+ | 'pattern-separator'
+ | 'exponent-separator'
+NamespaceDecl
+ ::= 'declare' 'namespace' NCName '=' URILiteral
+Import ::= SchemaImport
+ | ModuleImport
+SchemaImport
+ ::= 'import' 'schema' SchemaPrefix? URILiteral ( 'at' URILiteral ( ',' URILiteral )* )?
+SchemaPrefix
+ ::= 'namespace' NCName '='
+ | 'fixed'? 'default' 'element' 'namespace'
+ModuleImport
+ ::= 'import' 'module' ( 'namespace' NCName '=' )? URILiteral ( 'at' URILiteral ( ',' URILiteral )* )?
+ContextValueDecl
+ ::= 'declare' 'context' ( 'value' ( 'as' SequenceType )? | 'item' ( 'as' ItemType )? ) ( ':=' VarValue | 'external' ( ':=' VarDefaultValue )? )
+SequenceType
+ ::= 'empty-sequence' '(' ')'
+ | ItemType ( OccurrenceIndicator / )
+ItemType ::= RegularItemType
+ | FunctionType
+ | TypeName
+ | ChoiceItemType
+RegularItemType
+ ::= AnyItemTest
+ | NodeKindTest
+ | GNodeType
+ | JNodeType
+ | MapType
+ | ArrayType
+ | RecordType
+ | EnumerationType
+AnyItemTest
+ ::= 'item' '(' ')'
+NodeKindTest
+ ::= DocumentTest
+ | ElementTest
+ | AttributeTest
+ | SchemaElementTest
+ | SchemaAttributeTest
+ | PITest
+ | CommentTest
+ | TextTest
+ | NamespaceNodeTest
+ | AnyNodeKindTest
+DocumentTest
+ ::= 'document-node' '(' ( ElementTest | SchemaElementTest | NameTestUnion )? ')'
+ElementTest
+ ::= 'element' '(' ( NameTestUnion ( ',' TypeName '?'? )? )? ')'
+NameTestUnion
+ ::= NameTest ( '|' NameTest )*
+NameTest ::= EQName
+ | Wildcard
+TypeName ::= EQName
+SchemaElementTest
+ ::= 'schema-element' '(' ElementName ')'
+ElementName
+ ::= EQName
+AttributeTest
+ ::= 'attribute' '(' ( NameTestUnion ( ',' TypeName )? )? ')'
+SchemaAttributeTest
+ ::= 'schema-attribute' '(' AttributeName ')'
+AttributeName
+ ::= EQName
+PITest ::= 'processing-instruction' '(' ( NCName | StringLiteral )? ')'
+CommentTest
+ ::= 'comment' '(' ')'
+TextTest ::= 'text' '(' ')'
+NamespaceNodeTest
+ ::= 'namespace-node' '(' ')'
+AnyNodeKindTest
+ ::= 'node' '(' ')'
+GNodeType
+ ::= 'gnode' '(' ')'
+JNodeType
+ ::= 'jnode' '(' ( ( '*' | JRootSelector | NCName | Constant ) ( ',' ( '*' | SequenceType ) )? )? ')'
+JRootSelector
+ ::= '(' ')'
+Constant ::= StringLiteral
+ | '-'? NumericLiteral
+ | QNameLiteral
+ | 'true' '(' ')'
+ | 'false' '(' ')'
+NumericLiteral
+ ::= IntegerLiteral
+ | HexIntegerLiteral
+ | BinaryIntegerLiteral
+ | DecimalLiteral
+ | DoubleLiteral
+QNameLiteral
+ ::= '#' EQName
+MapType ::= AnyMapType
+ | TypedMapType
+AnyMapType
+ ::= 'map' '(' '*' ')'
+TypedMapType
+ ::= 'map' '(' ItemType ',' SequenceType ')'
+ArrayType
+ ::= AnyArrayType
+ | TypedArrayType
+AnyArrayType
+ ::= 'array' '(' '*' ')'
+TypedArrayType
+ ::= 'array' '(' SequenceType ')'
+RecordType
+ ::= 'record' '(' ( FieldDeclaration ( ',' FieldDeclaration )* )? ')'
+FieldDeclaration
+ ::= FieldName '?'? ( 'as' SequenceType )?
+FieldName
+ ::= NCName
+ | StringLiteral
+EnumerationType
+ ::= 'enum' '(' StringLiteral ( ',' StringLiteral )* ')'
+FunctionType
+ ::= Annotation* ( AnyFunctionType | TypedFunctionType )
+Annotation
+ ::= '%' EQName ( '(' Constant ( ',' Constant )* ')' )?
+AnyFunctionType
+ ::= ( 'function' | 'fn' ) '(' '*' ')'
+TypedFunctionType
+ ::= ( 'function' | 'fn' ) '(' ( TypedFunctionParam ( ',' TypedFunctionParam )* )? ')' 'as' SequenceType
+TypedFunctionParam
+ ::= ( '$' EQName 'as' )? SequenceType
+ChoiceItemType
+ ::= '(' ItemType ( '|' ItemType )* ')'
+OccurrenceIndicator
+ ::= '?'
+ | '*'
+ | '+'
+VarValue ::= ExprSingle
+ExprSingle
+ ::= FLWORExpr
+ | QuantifiedExpr
+ | SwitchExpr
+ | TypeswitchExpr
+ | IfExpr
+ | TryCatchExpr
+ | InsertExpr
+ | DeleteExpr
+ | RenameExpr
+ | ReplaceExpr
+ | TransformExpr
+ | ExistUpdateExpr
+ | OrExpr
+FLWORExpr
+ ::= InitialClause IntermediateClause* ReturnClause
+InitialClause
+ ::= ForClause
+ | LetClause
+ | WindowClause
+ForClause
+ ::= 'for' ForBinding ( ',' ForBinding )*
+ForBinding
+ ::= ForItemBinding
+ | ForMemberBinding
+ | ForEntryBinding
+ForItemBinding
+ ::= VarNameAndType AllowingEmpty? PositionalVar? FTScoreVar? 'in' ExprSingle
+VarNameAndType
+ ::= '$' EQName TypeDeclaration?
+TypeDeclaration
+ ::= 'as' SequenceType
+AllowingEmpty
+ ::= 'allowing' 'empty'
+PositionalVar
+ ::= 'at' VarName
+VarName ::= '$' EQName
+ForMemberBinding
+ ::= 'member' VarNameAndType PositionalVar? 'in' ExprSingle
+ForEntryBinding
+ ::= ( ForEntryKeyBinding ForEntryValueBinding? | ForEntryValueBinding ) PositionalVar? 'in' ExprSingle
+ForEntryKeyBinding
+ ::= 'key' VarNameAndType
+ForEntryValueBinding
+ ::= 'value' VarNameAndType
+LetClause
+ ::= 'let' LetBinding ( ',' LetBinding )*
+LetBinding
+ ::= LetValueBinding
+ | LetSequenceBinding
+ | LetArrayBinding
+ | LetMapBinding
+ | FTScoreVar ':=' ExprSingle
+LetValueBinding
+ ::= VarNameAndType ':=' ExprSingle
+LetSequenceBinding
+ ::= '$' '(' VarNameAndType ( ',' VarNameAndType )* ')' TypeDeclaration? ':=' ExprSingle
+LetArrayBinding
+ ::= '$' '[' VarNameAndType ( ',' VarNameAndType )* ']' TypeDeclaration? ':=' ExprSingle
+LetMapBinding
+ ::= '$' '{' VarNameAndType ( ',' VarNameAndType )* '}' TypeDeclaration? ':=' ExprSingle
+WindowClause
+ ::= 'for' ( TumblingWindowClause | SlidingWindowClause )
+TumblingWindowClause
+ ::= 'tumbling' 'window' VarNameAndType 'in' ExprSingle WindowStartCondition? WindowEndCondition?
+WindowStartCondition
+ ::= 'start' WindowVars ( 'when' ExprSingle )?
+WindowVars
+ ::= CurrentVar? PositionalVar? PreviousVar? NextVar?
+CurrentVar
+ ::= VarName
+PreviousVar
+ ::= 'previous' VarName
+NextVar ::= 'next' VarName
+WindowEndCondition
+ ::= 'only'? 'end' WindowVars ( 'when' ExprSingle )?
+SlidingWindowClause
+ ::= 'sliding' 'window' VarNameAndType 'in' ExprSingle WindowStartCondition? WindowEndCondition
+IntermediateClause
+ ::= InitialClause
+ | WhereClause
+ | WhileClause
+ | GroupByClause
+ | OrderByClause
+ | CountClause
+WhereClause
+ ::= 'where' ExprSingle
+WhileClause
+ ::= 'while' ExprSingle
+GroupByClause
+ ::= 'group' 'by' GroupingSpec ( ',' GroupingSpec )*
+GroupingSpec
+ ::= VarName ( TypeDeclaration? ':=' ExprSingle )? ( 'collation' URILiteral )?
+OrderByClause
+ ::= 'stable'? 'order' 'by' OrderSpec ( ',' OrderSpec )*
+OrderSpec
+ ::= ExprSingle OrderModifier
+OrderModifier
+ ::= ( 'ascending' | 'descending' )? ( 'empty' ( 'greatest' | 'least' ) )? ( 'collation' URILiteral )?
+CountClause
+ ::= 'count' VarName
+ReturnClause
+ ::= 'return' ExprSingle
+QuantifiedExpr
+ ::= ( 'some' | 'every' ) QuantifierBinding ( ',' QuantifierBinding )* 'satisfies' ExprSingle
+QuantifierBinding
+ ::= VarNameAndType 'in' ExprSingle
+SwitchExpr
+ ::= 'switch' SwitchComparand ( SwitchCases | BracedSwitchCases )
+SwitchComparand
+ ::= '(' Expr? ')'
+Expr ::= ExprSingle ( ',' ExprSingle )*
+SwitchCases
+ ::= SwitchCaseClause+ 'default' 'return' ExprSingle
+SwitchCaseClause
+ ::= ( 'case' SwitchCaseOperand )+ 'return' ExprSingle
+SwitchCaseOperand
+ ::= Expr
+BracedSwitchCases
+ ::= '{' SwitchCases '}'
+TypeswitchExpr
+ ::= 'typeswitch' '(' Expr ')' ( TypeswitchCases | BracedTypeswitchCases )
+TypeswitchCases
+ ::= CaseClause+ 'default' VarName? 'return' ExprSingle
+CaseClause
+ ::= 'case' ( VarName 'as' )? SequenceTypeUnion 'return' ExprSingle
+SequenceTypeUnion
+ ::= SequenceType ( '|' SequenceType )*
+BracedTypeswitchCases
+ ::= '{' TypeswitchCases '}'
+IfExpr ::= 'if' '(' Expr ')' ( UnbracedActions | BracedAction )
+UnbracedActions
+ ::= 'then' ExprSingle 'else' ExprSingle
+BracedAction
+ ::= EnclosedExpr
+EnclosedExpr
+ ::= '{' Expr? '}'
+TryCatchExpr
+ ::= TryClause ( CatchClause+ FinallyClause? | FinallyClause )
+TryClause
+ ::= 'try' EnclosedExpr
+CatchClause
+ ::= 'catch' NameTestUnion EnclosedExpr
+FinallyClause
+ ::= 'finally' EnclosedExpr
+InsertExpr
+ ::= 'insert' ( 'node' | 'nodes' ) SourceExpr InsertExprTargetChoice TargetExpr
+InsertExprTargetChoice
+ ::= ( 'as' ( 'first' | 'last' ) )? 'into'
+ | 'after'
+ | 'before'
+SourceExpr
+ ::= ExprSingle
+TargetExpr
+ ::= ExprSingle
+DeleteExpr
+ ::= 'delete' ( 'node' | 'nodes' ) TargetExpr
+ReplaceExpr
+ ::= 'replace' ( 'value' 'of' )? 'node' TargetExpr 'with' ExprSingle
+RenameExpr
+ ::= 'rename' 'node' TargetExpr 'as' NewNameExpr
+NewNameExpr
+ ::= ExprSingle
+TransformExpr
+ ::= 'copy' VarName ':=' ExprSingle ( ',' VarName ':=' ExprSingle )* 'modify' ExprSingle 'return' ExprSingle
+ExistUpdateExpr
+ ::= 'update' ( ExistInsertExpr | ExistReplaceExpr | ExistValueExpr | ExistDeleteExpr | ExistRenameExpr )
+ExistInsertExpr
+ ::= 'insert' ExprSingle ( 'into' | 'following' | 'preceding' ) ExprSingle
+ExistReplaceExpr
+ ::= 'replace' ExprSingle 'with' ExprSingle
+ExistValueExpr
+ ::= 'value' ExprSingle 'with' ExprSingle
+ExistDeleteExpr
+ ::= 'delete' ExprSingle
+ExistRenameExpr
+ ::= 'rename' ExprSingle 'as' ExprSingle
+OrExpr ::= AndExpr ( 'or' AndExpr )*
+AndExpr ::= ComparisonExpr ( 'and' ComparisonExpr )*
+ComparisonExpr
+ ::= FTContainsExpr ( ( ValueComp | GeneralComp | NodeComp ) FTContainsExpr )?
+FTContainsExpr
+ ::= OtherwiseExpr ( 'contains' 'text' FTSelection FTIgnoreOption? )?
+OtherwiseExpr
+ ::= StringConcatExpr ( 'otherwise' StringConcatExpr )*
+StringConcatExpr
+ ::= RangeExpr ( '||' RangeExpr )*
+RangeExpr
+ ::= AdditiveExpr ( 'to' AdditiveExpr )?
+AdditiveExpr
+ ::= MultiplicativeExpr ( ( '+' | '-' ) MultiplicativeExpr )*
+MultiplicativeExpr
+ ::= UnionExpr ( ( '*' | '×' | 'div' | '÷' | 'idiv' | 'mod' ) UnionExpr )*
+UnionExpr
+ ::= IntersectExceptExpr ( ( 'union' | '|' ) IntersectExceptExpr )*
+IntersectExceptExpr
+ ::= InstanceofExpr ( ( 'intersect' | 'except' ) InstanceofExpr )*
+InstanceofExpr
+ ::= TreatExpr ( 'instance' 'of' SequenceType )?
+TreatExpr
+ ::= CastableExpr ( 'treat' 'as' SequenceType )?
+CastableExpr
+ ::= CastExpr ( 'castable' 'as' CastTarget '?'? )?
+CastExpr ::= PipelineExpr ( 'cast' 'as' CastTarget '?'? )?
+PipelineExpr
+ ::= ArrowExpr ( '->' ArrowExpr )*
+ArrowExpr
+ ::= UnaryExpr ( SequenceArrowTarget | MappingArrowTarget )*
+UnaryExpr
+ ::= ( '-' | '+' )* ValueExpr
+ValueExpr
+ ::= ValidateExpr
+ | ExtensionExpr
+ | SimpleMapExpr
+ValidateExpr
+ ::= 'validate' ( ValidationMode | 'type' TypeName )? '{' Expr '}'
+ValidationMode
+ ::= 'lax'
+ | 'strict'
+ExtensionExpr
+ ::= Pragma+ '{' Expr? '}'
+Pragma ::= '(#' S EQName ( S PragmaContents )? '#)'
+ /* ws: explicit */
+SimpleMapExpr
+ ::= PathExpr ( '!' PathExpr )*
+PathExpr ::= AbsolutePathExpr
+ | RelativePathExpr
+AbsolutePathExpr
+ ::= '/' ( RelativePathExpr / )
+ | '//' RelativePathExpr
+RelativePathExpr
+ ::= StepExpr ( ( '/' | '//' ) StepExpr )*
+StepExpr ::= PostfixExpr
+ | AxisStep
+PostfixExpr
+ ::= PrimaryExpr ( Predicate | PositionalArgumentList | Lookup | MethodCallSuffix | FilterExprAMSuffix )*
+PrimaryExpr
+ ::= Literal
+ | VarRef
+ | ParenthesizedExpr
+ | ContextValueRef
+ | FunctionCall
+ | OrderedExpr
+ | UnorderedExpr
+ | NodeConstructor
+ | FunctionItemExpr
+ | MapConstructor
+ | ArrayConstructor
+ | StringTemplate
+ | StringConstructor
+ | UnaryLookup
+Literal ::= NumericLiteral
+ | StringLiteral
+ | QNameLiteral
+VarRef ::= '$' EQName
+ParenthesizedExpr
+ ::= '(' Expr? ')'
+ContextValueRef
+ ::= '.'
+FunctionCall
+ ::= UnreservedFunctionEQName ArgumentList
+UnreservedFunctionEQName
+ ::= UnreservedFunctionQName
+ | URIQualifiedName
+ArgumentList
+ ::= '(' ( PositionalArguments ( ',' KeywordArguments )? | KeywordArguments )? ')'
+PositionalArguments
+ ::= Argument ( ',' Argument )*
+Argument ::= ExprSingle
+ | ArgumentPlaceholder
+ArgumentPlaceholder
+ ::= '?'
+KeywordArguments
+ ::= KeywordArgument ( ',' KeywordArgument )*
+KeywordArgument
+ ::= EQName ':=' Argument
+OrderedExpr
+ ::= 'ordered' EnclosedExpr
+UnorderedExpr
+ ::= 'unordered' EnclosedExpr
+NodeConstructor
+ ::= DirectConstructor
+ | ComputedConstructor
+DirectConstructor
+ ::= DirElemConstructor
+ | DirCommentConstructor
+ | DirPIConstructor
+DirElemConstructor
+ ::= '<'^DirElemConstructor QName DirAttributeList ( '/>' | '>' DirElemContent* '' QName S? '>' )
+ /* ws: explicit */
+DirAttributeList
+ ::= ( S ( QName S? '=' S? DirAttributeValue )? )*
+ /* ws: explicit */
+DirAttributeValue
+ ::= '"' ( EscapeQuot | QuotAttrValueContent )* '"'
+ | "'" ( EscapeApos | AposAttrValueContent )* "'"
+ /* ws: explicit */
+QuotAttrValueContent
+ ::= QuotAttrContentChar
+ | CommonContent
+CommonContent
+ ::= PredefinedEntityRef
+ | CharRef
+ | '{{'
+ | '}}'
+ | EnclosedExpr
+AposAttrValueContent
+ ::= AposAttrContentChar
+ | CommonContent
+DirElemContent
+ ::= DirectConstructor
+ | CDataSection
+ | CommonContent
+ | ElementContentChar
+CDataSection
+ ::= ''
+ /* ws: explicit */
+DirCommentConstructor
+ ::= ''
+ /* ws: explicit */
+DirPIConstructor
+ ::= '' PITarget ( S DirPIContents )? '?>'
+ /* ws: explicit */
+ComputedConstructor
+ ::= CompDocConstructor
+ | CompElemConstructor
+ | CompAttrConstructor
+ | CompNamespaceConstructor
+ | CompTextConstructor
+ | CompCommentConstructor
+ | CompPIConstructor
+CompDocConstructor
+ ::= 'document' EnclosedExpr
+CompElemConstructor
+ ::= 'element' CompNodeName EnclosedContentExpr
+CompNodeName
+ ::= QNameLiteral
+ | UnreservedName
+ | '{' Expr '}'
+UnreservedName
+ ::= UnreservedQName
+ | URIQualifiedName
+EnclosedContentExpr
+ ::= EnclosedExpr
+CompAttrConstructor
+ ::= 'attribute' CompNodeName EnclosedExpr
+CompNamespaceConstructor
+ ::= 'namespace' CompNodeNCName EnclosedExpr
+CompNodeNCName
+ ::= MarkedNCName
+ | UnreservedNCName
+ | '{' Expr '}'
+MarkedNCName
+ ::= '#' NCName
+CompTextConstructor
+ ::= 'text' EnclosedExpr
+CompCommentConstructor
+ ::= 'comment' EnclosedExpr
+CompPIConstructor
+ ::= 'processing-instruction' CompNodeNCName EnclosedExpr
+FunctionItemExpr
+ ::= NamedFunctionRef
+ | InlineFunctionExpr
+NamedFunctionRef
+ ::= UnreservedFunctionEQName '#' IntegerLiteral
+InlineFunctionExpr
+ ::= Annotation* ( 'function' | 'fn' ) FunctionSignature? FunctionBody
+FunctionSignature
+ ::= '(' ParamList ')' TypeDeclaration?
+ParamList
+ ::= ( VarNameAndType ( ',' VarNameAndType )* )?
+FunctionBody
+ ::= EnclosedExpr
+MapConstructor
+ ::= 'map'? '{' ( MapConstructorEntry ( ',' MapConstructorEntry )* )? '}'
+MapConstructorEntry
+ ::= ExprSingle ( ':' ExprSingle )?
+ArrayConstructor
+ ::= SquareArrayConstructor
+ | CurlyArrayConstructor
+SquareArrayConstructor
+ ::= '[' ( ExprSingle ( ',' ExprSingle )* )? ']'
+CurlyArrayConstructor
+ ::= 'array' EnclosedExpr
+StringTemplate
+ ::= '`' ( StringTemplateFixedPart | StringTemplateVariablePart )* '`'
+ /* ws: explicit */
+StringTemplateVariablePart
+ ::= EnclosedExpr
+StringConstructor
+ ::= '``[' StringConstructorContent ']``'
+ /* ws: explicit */
+StringConstructorContent
+ ::= StringConstructorChars ( StringInterpolation StringConstructorChars )*
+ /* ws: explicit */
+StringInterpolation
+ ::= '`' EnclosedExpr '`'
+ /* ws: explicit */
+UnaryLookup
+ ::= Lookup
+Lookup ::= '?' KeySpecifier
+KeySpecifier
+ ::= NCName
+ | Literal
+ | ContextValueRef
+ | VarRef
+ | ParenthesizedExpr
+ | LookupWildcard
+LookupWildcard
+ ::= '*'
+Predicate
+ ::= '[' Expr ']'
+PositionalArgumentList
+ ::= '(' PositionalArguments? ')'
+MethodCallSuffix
+ ::= '=?>' NCName PositionalArgumentList
+FilterExprAMSuffix
+ ::= '?[' Expr ']'
+AxisStep ::= ( AbbreviatedStep | FullStep ) Predicate*
+AbbreviatedStep
+ ::= '..'
+ | '@' NodeTest
+ | SimpleNodeTest
+NodeTest ::= UnionNodeTest
+ | SimpleNodeTest
+UnionNodeTest
+ ::= '(' SimpleNodeTest ( '|' SimpleNodeTest )* ')'
+SimpleNodeTest
+ ::= TypeTest
+ | Selector
+TypeTest ::= GNodeType
+ | NodeKindTest
+ | JNodeType
+Selector ::= EQName
+ | Wildcard
+ | 'get' '(' ExprSingle ')'
+FullStep ::= Axis NodeTest
+Axis ::= ( 'ancestor' | 'ancestor-or-self' | 'attribute' | 'child' | 'descendant' | 'descendant-or-self' | 'following' | 'following-or-self' | 'following-sibling' | 'following-sibling-or-self' | 'parent' | 'preceding' | 'preceding-or-self' | 'preceding-sibling' | 'preceding-sibling-or-self' | 'self' ) '::'
+SequenceArrowTarget
+ ::= '=>' ArrowTarget
+ArrowTarget
+ ::= FunctionCall
+ | RestrictedDynamicCall
+RestrictedDynamicCall
+ ::= ( VarRef | ParenthesizedExpr | FunctionItemExpr | MapConstructor | ArrayConstructor ) PositionalArgumentList
+MappingArrowTarget
+ ::= '=!>' ArrowTarget
+CastTarget
+ ::= TypeName
+ | ChoiceItemType
+ | EnumerationType
+ValueComp
+ ::= 'eq'
+ | 'ne'
+ | 'lt'
+ | 'le'
+ | 'gt'
+ | 'ge'
+GeneralComp
+ ::= '='
+ | '!='
+ | '<'^GeneralComp
+ | '<='
+ | '>'
+ | '>='
+NodeComp ::= 'is'
+ | 'is-not'
+ | NodePrecedes
+ | NodeFollows
+ | 'precedes-or-is'
+ | 'follows-or-is'
+NodePrecedes
+ ::= '<<'
+ | 'precedes'
+NodeFollows
+ ::= '>>'
+ | 'follows'
+VarDefaultValue
+ ::= ExprSingle
+VarDecl ::= 'declare' Annotation* 'variable' VarNameAndType ( ':=' VarValue | 'external' ( ':=' VarDefaultValue )? )
+FunctionDecl
+ ::= 'declare' Annotation* 'function' UnreservedFunctionEQName '(' ParamListWithDefaults? ')' TypeDeclaration? ( FunctionBody | 'external' )
+ParamListWithDefaults
+ ::= ParamWithDefault ( ',' ParamWithDefault )*
+ParamWithDefault
+ ::= VarNameAndType ( ':=' ExprSingle )?
+ItemTypeDecl
+ ::= 'declare' Annotation* 'type' EQName 'as' ItemType
+NamedRecordTypeDecl
+ ::= 'declare' Annotation* 'record' EQName '(' ( ExtendedFieldDeclaration ( ',' ExtendedFieldDeclaration )* )? ')'
+ExtendedFieldDeclaration
+ ::= FieldDeclaration ( ':=' ExprSingle )?
+OptionDecl
+ ::= 'declare' 'option' EQName StringLiteral
+MainModule
+ ::= Prolog QueryBody
+QueryBody
+ ::= Expr
+FTOptionDecl
+ ::= 'declare' 'ft-option' FTMatchOptions
+FTScoreVar
+ ::= 'score' VarName
+FTSelection
+ ::= FTOr FTPosFilter*
+FTWeight ::= 'weight' '{' Expr '}'
+FTOr ::= FTAnd ( 'ftor' FTAnd )*
+FTAnd ::= FTMildNot ( 'ftand' FTMildNot )*
+FTMildNot
+ ::= FTUnaryNot ( 'not' 'in' FTUnaryNot )*
+FTUnaryNot
+ ::= 'ftnot'? FTPrimaryWithOptions
+FTPrimaryWithOptions
+ ::= FTPrimary FTMatchOptions? FTWeight?
+FTPrimary
+ ::= FTWords FTTimes?
+ | '(' FTSelection ')'
+ | FTExtensionSelection
+FTWords ::= FTWordsValue FTAnyallOption?
+FTWordsValue
+ ::= StringLiteral
+ | '{' Expr '}'
+FTExtensionSelection
+ ::= Pragma+ '{' FTSelection? '}'
+FTAnyallOption
+ ::= 'any' 'word'?
+ | 'all' 'words'?
+ | 'phrase'
+FTTimes ::= 'occurs' FTRange 'times'
+FTRange ::= 'exactly' AdditiveExpr
+ | 'at' 'least' AdditiveExpr
+ | 'at' 'most' AdditiveExpr
+ | 'from' AdditiveExpr 'to' AdditiveExpr
+FTPosFilter
+ ::= FTOrder
+ | FTWindow
+ | FTDistance
+ | FTScope
+ | FTContent
+FTOrder ::= 'ordered'
+FTWindow ::= 'window' AdditiveExpr FTUnit
+FTDistance
+ ::= 'distance' FTRange FTUnit
+FTUnit ::= 'words'
+ | 'sentences'
+ | 'paragraphs'
+FTScope ::= ( 'same' | 'different' ) FTBigUnit
+FTBigUnit
+ ::= 'sentence'
+ | 'paragraph'
+FTContent
+ ::= 'at' 'start'
+ | 'at' 'end'
+ | 'entire' 'content'
+FTMatchOptions
+ ::= ( 'using' FTMatchOption )+
+FTMatchOption
+ ::= FTLanguageOption
+ | FTWildCardOption
+ | FTThesaurusOption
+ | FTStemOption
+ | FTCaseOption
+ | FTDiacriticsOption
+ | FTStopWordOption
+ | FTExtensionOption
+FTCaseOption
+ ::= 'case' 'insensitive'
+ | 'case' 'sensitive'
+ | 'lowercase'
+ | 'uppercase'
+FTDiacriticsOption
+ ::= 'diacritics' 'insensitive'
+ | 'diacritics' 'sensitive'
+FTStemOption
+ ::= 'stemming'
+ | 'no' 'stemming'
+FTThesaurusOption
+ ::= 'thesaurus' ( FTThesaurusID | 'default' )
+ | 'thesaurus' '(' ( FTThesaurusID | 'default' ) ( ',' FTThesaurusID )* ')'
+ | 'no' 'thesaurus'
+FTThesaurusID
+ ::= 'at' URILiteral ( 'relationship' StringLiteral )? ( FTLiteralRange 'levels' )?
+FTLiteralRange
+ ::= 'exactly' IntegerLiteral
+ | 'at' 'least' IntegerLiteral
+ | 'at' 'most' IntegerLiteral
+ | 'from' IntegerLiteral 'to' IntegerLiteral
+FTStopWordOption
+ ::= 'stop' 'words' FTStopWords FTStopWordsInclExcl*
+ | 'stop' 'words' 'default' FTStopWordsInclExcl*
+ | 'no' 'stop' 'words'
+FTStopWords
+ ::= 'at' URILiteral
+ | '(' StringLiteral ( ',' StringLiteral )* ')'
+FTStopWordsInclExcl
+ ::= ( 'union' | 'except' ) FTStopWords
+FTLanguageOption
+ ::= 'language' StringLiteral
+FTWildCardOption
+ ::= 'wildcards'
+ | 'no' 'wildcards'
+FTExtensionOption
+ ::= 'option' EQName StringLiteral
+FTIgnoreOption
+ ::= 'without' 'content' UnionExpr
+Whitespace
+ ::= S^WS
+ | Comment
+ /* ws: definition */
+Comment ::= '(:' ( CommentContents | Comment )* ':)'
+ /* ws: explicit */
+
+
+
+EOF ::= $
+StringLiteral
+ ::= AposStringLiteral
+ | QuotStringLiteral
+ /* ws: explicit */
+AposStringLiteral
+ ::= "'" ( PredefinedEntityRef | CharRef | EscapeApos | [^'&] )* "'"
+ /* ws: explicit */
+PredefinedEntityRef
+ ::= '&' ( 'lt' | 'gt' | 'amp' | 'quot' | 'apos' ) ';'
+ /* ws: explicit */
+CharRef ::= '' [0-9]+ ';'
+ | '' [0-9a-fA-F]+ ';'
+EscapeApos
+ ::= "''"
+ /* ws: explicit */
+QuotStringLiteral
+ ::= '"' ( PredefinedEntityRef | CharRef | EscapeQuot | [^"&] )* '"'
+ /* ws: explicit */
+EscapeQuot
+ ::= '""'
+ /* ws: explicit */
+UnreservedNCName
+ ::= NCName - ReservedName
+NCName ::= Name - ( Char* ':' Char* )
+Name ::= NameStartChar NameChar*
+NameStartChar
+ ::= ':'
+ | [A-Z]
+ | '_'
+ | [a-z]
+ | [#xC0-#xD6]
+ | [#xD8-#xF6]
+ | [#xF8-#x2FF]
+ | [#x370-#x37D]
+ | [#x37F-#x1FFF]
+ | [#x200C-#x200D]
+ | [#x2070-#x218F]
+ | [#x2C00-#x2FEF]
+ | [#x3001-#xD7FF]
+ | [#xF900-#xFDCF]
+ | [#xFDF0-#xFFFD]
+ | [#x10000-#xEFFFF]
+NameChar ::= NameStartChar
+ | '-'
+ | '.'
+ | [0-9]
+ | #xB7
+ | [#x0300-#x036F]
+ | [#x203F-#x2040]
+Char ::= #x9
+ | #xA
+ | #xD
+ | [#x20-#xD7FF]
+ | [#xE000-#xFFFD]
+ | [#x10000-#x10FFFF]
+ReservedName
+ ::= 'NaN'
+ | 'after'
+ | 'all'
+ | 'allowing'
+ | 'ancestor'
+ | 'ancestor-or-self'
+ | 'and'
+ | 'any'
+ | 'array'
+ | 'as'
+ | 'ascending'
+ | 'at'
+ | 'attribute'
+ | 'base-uri'
+ | 'before'
+ | 'boundary-space'
+ | 'by'
+ | 'case'
+ | 'cast'
+ | 'castable'
+ | 'catch'
+ | 'child'
+ | 'collation'
+ | 'comment'
+ | 'construction'
+ | 'contains'
+ | 'content'
+ | 'context'
+ | 'copy'
+ | 'copy-namespaces'
+ | 'count'
+ | 'decimal-format'
+ | 'decimal-separator'
+ | 'declare'
+ | 'default'
+ | 'delete'
+ | 'descendant'
+ | 'descendant-or-self'
+ | 'descending'
+ | 'diacritics'
+ | 'different'
+ | 'digit'
+ | 'distance'
+ | 'div'
+ | 'document'
+ | 'document-node'
+ | 'element'
+ | 'else'
+ | 'empty'
+ | 'empty-sequence'
+ | 'encoding'
+ | 'end'
+ | 'entire'
+ | 'enum'
+ | 'eq'
+ | 'every'
+ | 'exactly'
+ | 'except'
+ | 'exponent-separator'
+ | 'external'
+ | 'false'
+ | 'finally'
+ | 'first'
+ | 'fixed'
+ | 'fn'
+ | 'following'
+ | 'following-or-self'
+ | 'following-sibling'
+ | 'following-sibling-or-self'
+ | 'follows'
+ | 'follows-or-is'
+ | 'for'
+ | 'from'
+ | 'ft-option'
+ | 'ftand'
+ | 'ftnot'
+ | 'ftor'
+ | 'function'
+ | 'ge'
+ | 'get'
+ | 'gnode'
+ | 'greatest'
+ | 'group'
+ | 'grouping-separator'
+ | 'gt'
+ | 'idiv'
+ | 'if'
+ | 'import'
+ | 'in'
+ | 'infinity'
+ | 'inherit'
+ | 'insert'
+ | 'insensitive'
+ | 'instance'
+ | 'intersect'
+ | 'into'
+ | 'is'
+ | 'is-not'
+ | 'item'
+ | 'jnode'
+ | 'key'
+ | 'language'
+ | 'last'
+ | 'lax'
+ | 'le'
+ | 'least'
+ | 'let'
+ | 'levels'
+ | 'lowercase'
+ | 'lt'
+ | 'map'
+ | 'member'
+ | 'minus-sign'
+ | 'mod'
+ | 'modify'
+ | 'module'
+ | 'most'
+ | 'namespace'
+ | 'namespace-node'
+ | 'ne'
+ | 'next'
+ | 'no'
+ | 'no-inherit'
+ | 'no-preserve'
+ | 'node'
+ | 'nodes'
+ | 'not'
+ | 'occurs'
+ | 'of'
+ | 'only'
+ | 'option'
+ | 'or'
+ | 'order'
+ | 'ordered'
+ | 'ordering'
+ | 'otherwise'
+ | 'paragraph'
+ | 'paragraphs'
+ | 'parent'
+ | 'pattern-separator'
+ | 'per-mille'
+ | 'percent'
+ | 'phrase'
+ | 'precedes'
+ | 'precedes-or-is'
+ | 'preceding'
+ | 'preceding-or-self'
+ | 'preceding-sibling'
+ | 'preceding-sibling-or-self'
+ | 'preserve'
+ | 'previous'
+ | 'processing-instruction'
+ | 'record'
+ | 'relationship'
+ | 'rename'
+ | 'replace'
+ | 'return'
+ | 'revalidation'
+ | 'same'
+ | 'satisfies'
+ | 'schema'
+ | 'schema-attribute'
+ | 'schema-element'
+ | 'score'
+ | 'self'
+ | 'sensitive'
+ | 'sentence'
+ | 'sentences'
+ | 'skip'
+ | 'sliding'
+ | 'some'
+ | 'stable'
+ | 'start'
+ | 'stemming'
+ | 'stop'
+ | 'strict'
+ | 'strip'
+ | 'switch'
+ | 'text'
+ | 'then'
+ | 'thesaurus'
+ | 'times'
+ | 'to'
+ | 'treat'
+ | 'true'
+ | 'try'
+ | 'tumbling'
+ | 'type'
+ | 'typeswitch'
+ | 'union'
+ | 'unordered'
+ | 'update'
+ | 'uppercase'
+ | 'using'
+ | 'validate'
+ | 'value'
+ | 'variable'
+ | 'version'
+ | 'weight'
+ | 'when'
+ | 'where'
+ | 'while'
+ | 'wildcards'
+ | 'window'
+ | 'with'
+ | 'without'
+ | 'word'
+ | 'words'
+ | 'xquery'
+ | 'zero-digit'
+URIQualifiedName
+ ::= BracedURILiteral ( NCName ':' )? NCName
+ /* ws: explicit */
+BracedURILiteral
+ ::= 'Q' '{' ( PredefinedEntityRef | CharRef | [^&{}] )* '}'
+ /* ws: explicit */
+UnreservedQName
+ ::= QName - ReservedName
+QName ::= PrefixedName
+ | UnprefixedName
+PrefixedName
+ ::= Prefix ':' LocalPart
+Prefix ::= NCName
+LocalPart
+ ::= NCName
+UnprefixedName
+ ::= LocalPart
+Wildcard ::= '*'
+ | NCName ':*'
+ | '*:' NCName
+ | BracedURILiteral '*'
+ /* ws: explicit */
+IntegerLiteral
+ ::= Digits
+ /* ws: explicit */
+Digits ::= DecDigit ( ( DecDigit | '_' )* DecDigit )?
+ /* ws: explicit */
+DecDigit ::= [0-9]
+ /* ws: explicit */
+HexIntegerLiteral
+ ::= '0x' HexDigits
+ /* ws: explicit */
+HexDigits
+ ::= HexDigit ( ( HexDigit | '_' )* HexDigit )?
+ /* ws: explicit */
+HexDigit ::= [0-9a-fA-F]
+ /* ws: explicit */
+BinaryIntegerLiteral
+ ::= '0b' BinaryDigits
+ /* ws: explicit */
+BinaryDigits
+ ::= BinaryDigit ( ( BinaryDigit | '_' )* BinaryDigit )?
+ /* ws: explicit */
+BinaryDigit
+ ::= [0-1]
+ /* ws: explicit */
+DecimalLiteral
+ ::= '.' Digits
+ | Digits '.' Digits?
+ /* ws: explicit */
+DoubleLiteral
+ ::= ( '.' Digits | Digits ( '.' Digits? )? ) [eE] [+#x2D]? Digits
+ /* ws: explicit */
+S ::= ( #x20 | #x9 | #xD | #xA )+
+PragmaContents
+ ::= Char* - ( Char* '#)' Char* ) & '#'
+QuotAttrContentChar
+ ::= Char - ["{}<&]
+AposAttrContentChar
+ ::= Char - ['{}<&]
+ElementContentChar
+ ::= Char - [{}<&]
+CDataSectionContents
+ ::= Char* - ( Char* ']]>' Char* ) & ']]'
+DirCommentContents
+ ::= ( Char - '-' | '-' ( Char - '-' ) )*
+ /* ws: explicit */
+PITarget ::= NCName - ( ( 'X' | 'x' ) ( 'M' | 'm' ) ( 'L' | 'l' ) )
+DirPIContents
+ ::= Char* - ( Char* '?>' Char* ) & '?'
+StringTemplateFixedPart
+ ::= ( Char - ( '{' | '}' | '`' ) | '{{' | '}}' | '``' )+
+ /* ws: explicit */
+StringConstructorChars
+ ::= Char* - ( Char* ( '`{' | ']``' ) Char* ) & ( '`{' | ']`' )
+CommentContents
+ ::= ( Char+ - ( Char* ( '(:' | ':)' ) Char* ) ) - ( Char* '(' ) & ':'
+ | Char+ - ( Char* ( '(:' | ':)' ) Char* ) & '('
+ /* ws: explicit */
+QNameOrKeywordDelimiter
+ ::= $
+ | ':'
+ | Char - NameChar
+NCNameDelimiter
+ ::= $ $
+ | ( Char - NameChar ) ( $ | Char )
+ | ':' ( Char - NameStartChar )
+NumericLiteralDelimiter
+ ::= QNameOrKeywordDelimiter
+ | '-'
+GeneralCompDelimiter
+ ::= [^?]
+DirElemConstructorDelimiter
+ ::= QName ( S QName S? '=' | S? [/>] )
+'*' << Wildcard
+QNameOrKeywordDelimiter
+ \\ UnreservedQName 'NaN' 'after' 'all' 'allowing' 'ancestor' 'ancestor-or-self' 'and' 'any' 'array' 'as' 'ascending' 'at' 'attribute' 'base-uri' 'before' 'boundary-space' 'by' 'case' 'cast' 'castable' 'catch' 'child' 'collation' 'comment' 'construction' 'contains' 'content' 'context' 'copy' 'copy-namespaces' 'count' 'decimal-format' 'decimal-separator' 'declare' 'default' 'delete' 'descendant' 'descendant-or-self' 'descending' 'diacritics' 'different' 'digit' 'distance' 'div' 'document' 'document-node' 'element' 'else' 'empty' 'empty-sequence' 'encoding' 'end' 'entire' 'enum' 'eq' 'every' 'exactly' 'except' 'exponent-separator' 'external' 'false' 'finally' 'first' 'fixed' 'fn' 'following' 'following-or-self' 'following-sibling' 'following-sibling-or-self' 'follows' 'follows-or-is' 'for' 'from' 'ft-option' 'ftand' 'ftnot' 'ftor' 'function' 'ge' 'get' 'gnode' 'greatest' 'group' 'grouping-separator' 'gt' 'idiv' 'if' 'import' 'in' 'infinity' 'inherit' 'insert' 'insensitive' 'instance' 'intersect' 'into' 'is' 'is-not' 'item' 'jnode' 'key' 'language' 'last' 'lax' 'le' 'least' 'let' 'levels' 'lowercase' 'lt' 'map' 'member' 'minus-sign' 'mod' 'modify' 'module' 'most' 'namespace' 'namespace-node' 'ne' 'next' 'no' 'no-inherit' 'no-preserve' 'node' 'nodes' 'not' 'occurs' 'of' 'only' 'option' 'or' 'order' 'ordered' 'ordering' 'otherwise' 'paragraph' 'paragraphs' 'parent' 'pattern-separator' 'per-mille' 'percent' 'phrase' 'precedes' 'precedes-or-is' 'preceding' 'preceding-or-self' 'preceding-sibling' 'preceding-sibling-or-self' 'preserve' 'previous' 'processing-instruction' 'record' 'relationship' 'rename' 'replace' 'return' 'revalidation' 'same' 'satisfies' 'schema' 'schema-attribute' 'schema-element' 'score' 'self' 'sensitive' 'sentence' 'sentences' 'skip' 'sliding' 'some' 'stable' 'start' 'stemming' 'stop' 'strict' 'strip' 'switch' 'text' 'then' 'thesaurus' 'times' 'to' 'treat' 'true' 'try' 'tumbling' 'type' 'typeswitch' 'union' 'unordered' 'update' 'uppercase' 'using' 'validate' 'value' 'variable' 'version' 'weight' 'when' 'where' 'while' 'wildcards' 'window' 'with' 'without' 'word' 'words' 'xquery' 'zero-digit'
+Char \\ '' '<<' '<='
+NCNameDelimiter
+ \\ UnreservedNCName
+NumericLiteralDelimiter
+ \\ DecimalLiteral DoubleLiteral IntegerLiteral
+GeneralCompDelimiter
+ \\ '<'^GeneralComp
+DirElemConstructorDelimiter
+ \\ '<'^DirElemConstructor
+S \\ '(#'
diff --git a/grammars/XQuery-40.ebnf b/grammars/XQuery-40.ebnf
new file mode 100644
index 00000000..8d6f01c6
--- /dev/null
+++ b/grammars/XQuery-40.ebnf
@@ -0,0 +1,1285 @@
+/* XQuery 4.0: An XML Query Language
+ * version https://qt4cg.org/specifications/xquery-40/
+ * extracted from https://qt4cg.org/specifications/xquery-40/xquery-40.html on Tue Feb 17, 2026, 21:31 (UTC+01)
+ * with references to https://www.w3.org/TR/REC-xml/ resolved and inlined
+ * with references to https://www.w3.org/TR/REC-xml-names/ resolved and inlined
+ * reordered into depth-first order
+ * adapted for REx by rexify-xquery-40.xq
+ */
+
+XQuery ::= Module EOF
+Module ::= VersionDecl? ( LibraryModule | MainModule )
+VersionDecl
+ ::= 'xquery' ( 'encoding' StringLiteral | 'version' StringLiteral ( 'encoding' StringLiteral )? ) Separator
+Separator
+ ::= ';'
+LibraryModule
+ ::= ModuleDecl Prolog
+ModuleDecl
+ ::= 'module' 'namespace' NCName '=' URILiteral Separator
+NCName ::= UnreservedNCName
+ | 'NaN'
+ | 'allowing'
+ | 'ancestor'
+ | 'ancestor-or-self'
+ | 'and'
+ | 'array'
+ | 'as'
+ | 'ascending'
+ | 'at'
+ | 'attribute'
+ | 'base-uri'
+ | 'boundary-space'
+ | 'by'
+ | 'case'
+ | 'cast'
+ | 'castable'
+ | 'catch'
+ | 'child'
+ | 'collation'
+ | 'comment'
+ | 'construction'
+ | 'context'
+ | 'copy-namespaces'
+ | 'count'
+ | 'decimal-format'
+ | 'decimal-separator'
+ | 'declare'
+ | 'default'
+ | 'descendant'
+ | 'descendant-or-self'
+ | 'descending'
+ | 'digit'
+ | 'div'
+ | 'document'
+ | 'document-node'
+ | 'element'
+ | 'else'
+ | 'empty'
+ | 'empty-sequence'
+ | 'encoding'
+ | 'end'
+ | 'enum'
+ | 'eq'
+ | 'every'
+ | 'except'
+ | 'exponent-separator'
+ | 'external'
+ | 'false'
+ | 'finally'
+ | 'fixed'
+ | 'fn'
+ | 'following'
+ | 'following-or-self'
+ | 'following-sibling'
+ | 'following-sibling-or-self'
+ | 'follows'
+ | 'follows-or-is'
+ | 'for'
+ | 'function'
+ | 'ge'
+ | 'get'
+ | 'gnode'
+ | 'greatest'
+ | 'group'
+ | 'grouping-separator'
+ | 'gt'
+ | 'idiv'
+ | 'if'
+ | 'import'
+ | 'in'
+ | 'infinity'
+ | 'inherit'
+ | 'instance'
+ | 'intersect'
+ | 'is'
+ | 'is-not'
+ | 'item'
+ | 'jnode'
+ | 'key'
+ | 'lax'
+ | 'le'
+ | 'least'
+ | 'let'
+ | 'lt'
+ | 'map'
+ | 'member'
+ | 'minus-sign'
+ | 'mod'
+ | 'module'
+ | 'namespace'
+ | 'namespace-node'
+ | 'ne'
+ | 'next'
+ | 'no-inherit'
+ | 'no-preserve'
+ | 'node'
+ | 'of'
+ | 'only'
+ | 'option'
+ | 'or'
+ | 'order'
+ | 'ordered'
+ | 'ordering'
+ | 'otherwise'
+ | 'parent'
+ | 'pattern-separator'
+ | 'per-mille'
+ | 'percent'
+ | 'precedes'
+ | 'precedes-or-is'
+ | 'preceding'
+ | 'preceding-or-self'
+ | 'preceding-sibling'
+ | 'preceding-sibling-or-self'
+ | 'preserve'
+ | 'previous'
+ | 'processing-instruction'
+ | 'record'
+ | 'return'
+ | 'satisfies'
+ | 'schema'
+ | 'schema-attribute'
+ | 'schema-element'
+ | 'self'
+ | 'sliding'
+ | 'some'
+ | 'stable'
+ | 'start'
+ | 'strict'
+ | 'strip'
+ | 'switch'
+ | 'text'
+ | 'then'
+ | 'to'
+ | 'treat'
+ | 'true'
+ | 'try'
+ | 'tumbling'
+ | 'type'
+ | 'typeswitch'
+ | 'union'
+ | 'unordered'
+ | 'validate'
+ | 'value'
+ | 'variable'
+ | 'version'
+ | 'when'
+ | 'where'
+ | 'while'
+ | 'window'
+ | 'xquery'
+ | 'zero-digit'
+URILiteral
+ ::= StringLiteral
+Prolog ::= ( ( DefaultNamespaceDecl | Setter | NamespaceDecl | Import ) Separator )* ( ( ContextValueDecl | VarDecl | FunctionDecl | ItemTypeDecl | NamedRecordTypeDecl | OptionDecl ) Separator )*
+DefaultNamespaceDecl
+ ::= 'declare' 'fixed'? 'default' ( 'element' | 'function' ) 'namespace' URILiteral
+Setter ::= BoundarySpaceDecl
+ | DefaultCollationDecl
+ | BaseURIDecl
+ | ConstructionDecl
+ | OrderingModeDecl
+ | EmptyOrderDecl
+ | CopyNamespacesDecl
+ | DecimalFormatDecl
+BoundarySpaceDecl
+ ::= 'declare' 'boundary-space' ( 'preserve' | 'strip' )
+DefaultCollationDecl
+ ::= 'declare' 'default' 'collation' URILiteral
+BaseURIDecl
+ ::= 'declare' 'base-uri' URILiteral
+ConstructionDecl
+ ::= 'declare' 'construction' ( 'strip' | 'preserve' )
+OrderingModeDecl
+ ::= 'declare' 'ordering' ( 'ordered' | 'unordered' )
+EmptyOrderDecl
+ ::= 'declare' 'default' 'order' 'empty' ( 'greatest' | 'least' )
+CopyNamespacesDecl
+ ::= 'declare' 'copy-namespaces' PreserveMode ',' InheritMode
+PreserveMode
+ ::= 'preserve'
+ | 'no-preserve'
+InheritMode
+ ::= 'inherit'
+ | 'no-inherit'
+DecimalFormatDecl
+ ::= 'declare' ( 'decimal-format' EQName | 'default' 'decimal-format' ) ( DFPropertyName '=' StringLiteral )*
+EQName ::= QName
+ | URIQualifiedName
+QName ::= UnreservedFunctionQName
+ | 'attribute'
+ | 'comment'
+ | 'document-node'
+ | 'element'
+ | 'namespace-node'
+ | 'node'
+ | 'processing-instruction'
+ | 'schema-attribute'
+ | 'schema-element'
+ | 'text'
+ | 'array'
+ | 'enum'
+ | 'fn'
+ | 'function'
+ | 'gnode'
+ | 'get'
+ | 'if'
+ | 'item'
+ | 'jnode'
+ | 'map'
+ | 'record'
+ | 'switch'
+ | 'type'
+ | 'typeswitch'
+UnreservedFunctionQName
+ ::= UnreservedQName
+ | 'NaN'
+ | 'allowing'
+ | 'ancestor'
+ | 'ancestor-or-self'
+ | 'and'
+ | 'as'
+ | 'ascending'
+ | 'at'
+ | 'base-uri'
+ | 'boundary-space'
+ | 'by'
+ | 'case'
+ | 'cast'
+ | 'castable'
+ | 'catch'
+ | 'child'
+ | 'collation'
+ | 'construction'
+ | 'context'
+ | 'copy-namespaces'
+ | 'count'
+ | 'decimal-format'
+ | 'decimal-separator'
+ | 'declare'
+ | 'default'
+ | 'descendant'
+ | 'descendant-or-self'
+ | 'descending'
+ | 'digit'
+ | 'div'
+ | 'document'
+ | 'else'
+ | 'empty'
+ | 'empty-sequence'
+ | 'encoding'
+ | 'end'
+ | 'eq'
+ | 'every'
+ | 'except'
+ | 'exponent-separator'
+ | 'external'
+ | 'false'
+ | 'finally'
+ | 'fixed'
+ | 'following'
+ | 'following-or-self'
+ | 'following-sibling'
+ | 'following-sibling-or-self'
+ | 'follows'
+ | 'follows-or-is'
+ | 'for'
+ | 'ge'
+ | 'greatest'
+ | 'group'
+ | 'grouping-separator'
+ | 'gt'
+ | 'idiv'
+ | 'import'
+ | 'in'
+ | 'infinity'
+ | 'inherit'
+ | 'instance'
+ | 'intersect'
+ | 'is'
+ | 'is-not'
+ | 'key'
+ | 'lax'
+ | 'le'
+ | 'least'
+ | 'let'
+ | 'lt'
+ | 'member'
+ | 'minus-sign'
+ | 'mod'
+ | 'module'
+ | 'namespace'
+ | 'ne'
+ | 'next'
+ | 'no-inherit'
+ | 'no-preserve'
+ | 'of'
+ | 'only'
+ | 'option'
+ | 'or'
+ | 'order'
+ | 'ordered'
+ | 'ordering'
+ | 'otherwise'
+ | 'parent'
+ | 'pattern-separator'
+ | 'per-mille'
+ | 'percent'
+ | 'precedes'
+ | 'precedes-or-is'
+ | 'preceding'
+ | 'preceding-or-self'
+ | 'preceding-sibling'
+ | 'preceding-sibling-or-self'
+ | 'preserve'
+ | 'previous'
+ | 'return'
+ | 'satisfies'
+ | 'schema'
+ | 'self'
+ | 'sliding'
+ | 'some'
+ | 'stable'
+ | 'start'
+ | 'strict'
+ | 'strip'
+ | 'then'
+ | 'to'
+ | 'treat'
+ | 'true'
+ | 'try'
+ | 'tumbling'
+ | 'union'
+ | 'unordered'
+ | 'validate'
+ | 'value'
+ | 'variable'
+ | 'version'
+ | 'when'
+ | 'where'
+ | 'while'
+ | 'window'
+ | 'xquery'
+ | 'zero-digit'
+DFPropertyName
+ ::= 'decimal-separator'
+ | 'grouping-separator'
+ | 'infinity'
+ | 'minus-sign'
+ | 'NaN'
+ | 'percent'
+ | 'per-mille'
+ | 'zero-digit'
+ | 'digit'
+ | 'pattern-separator'
+ | 'exponent-separator'
+NamespaceDecl
+ ::= 'declare' 'namespace' NCName '=' URILiteral
+Import ::= SchemaImport
+ | ModuleImport
+SchemaImport
+ ::= 'import' 'schema' SchemaPrefix? URILiteral ( 'at' URILiteral ( ',' URILiteral )* )?
+SchemaPrefix
+ ::= 'namespace' NCName '='
+ | 'fixed'? 'default' 'element' 'namespace'
+ModuleImport
+ ::= 'import' 'module' ( 'namespace' NCName '=' )? URILiteral ( 'at' URILiteral ( ',' URILiteral )* )?
+ContextValueDecl
+ ::= 'declare' 'context' ( 'value' ( 'as' SequenceType )? | 'item' ( 'as' ItemType )? ) ( ':=' VarValue | 'external' ( ':=' VarDefaultValue )? )
+SequenceType
+ ::= 'empty-sequence' '(' ')'
+ | ItemType ( OccurrenceIndicator / )
+ItemType ::= RegularItemType
+ | FunctionType
+ | TypeName
+ | ChoiceItemType
+RegularItemType
+ ::= AnyItemTest
+ | NodeKindTest
+ | GNodeType
+ | JNodeType
+ | MapType
+ | ArrayType
+ | RecordType
+ | EnumerationType
+AnyItemTest
+ ::= 'item' '(' ')'
+NodeKindTest
+ ::= DocumentTest
+ | ElementTest
+ | AttributeTest
+ | SchemaElementTest
+ | SchemaAttributeTest
+ | PITest
+ | CommentTest
+ | TextTest
+ | NamespaceNodeTest
+ | AnyNodeKindTest
+DocumentTest
+ ::= 'document-node' '(' ( ElementTest | SchemaElementTest | NameTestUnion )? ')'
+ElementTest
+ ::= 'element' '(' ( NameTestUnion ( ',' TypeName '?'? )? )? ')'
+NameTestUnion
+ ::= NameTest ( '|' NameTest )*
+NameTest ::= EQName
+ | Wildcard
+TypeName ::= EQName
+SchemaElementTest
+ ::= 'schema-element' '(' ElementName ')'
+ElementName
+ ::= EQName
+AttributeTest
+ ::= 'attribute' '(' ( NameTestUnion ( ',' TypeName )? )? ')'
+SchemaAttributeTest
+ ::= 'schema-attribute' '(' AttributeName ')'
+AttributeName
+ ::= EQName
+PITest ::= 'processing-instruction' '(' ( NCName | StringLiteral )? ')'
+CommentTest
+ ::= 'comment' '(' ')'
+TextTest ::= 'text' '(' ')'
+NamespaceNodeTest
+ ::= 'namespace-node' '(' ')'
+AnyNodeKindTest
+ ::= 'node' '(' ')'
+GNodeType
+ ::= 'gnode' '(' ')'
+JNodeType
+ ::= 'jnode' '(' ( ( '*' | JRootSelector | NCName | Constant ) ( ',' ( '*' | SequenceType ) )? )? ')'
+JRootSelector
+ ::= '(' ')'
+Constant ::= StringLiteral
+ | '-'? NumericLiteral
+ | QNameLiteral
+ | 'true' '(' ')'
+ | 'false' '(' ')'
+NumericLiteral
+ ::= IntegerLiteral
+ | HexIntegerLiteral
+ | BinaryIntegerLiteral
+ | DecimalLiteral
+ | DoubleLiteral
+QNameLiteral
+ ::= '#' EQName
+MapType ::= AnyMapType
+ | TypedMapType
+AnyMapType
+ ::= 'map' '(' '*' ')'
+TypedMapType
+ ::= 'map' '(' ItemType ',' SequenceType ')'
+ArrayType
+ ::= AnyArrayType
+ | TypedArrayType
+AnyArrayType
+ ::= 'array' '(' '*' ')'
+TypedArrayType
+ ::= 'array' '(' SequenceType ')'
+RecordType
+ ::= 'record' '(' ( FieldDeclaration ( ',' FieldDeclaration )* )? ')'
+FieldDeclaration
+ ::= FieldName '?'? ( 'as' SequenceType )?
+FieldName
+ ::= NCName
+ | StringLiteral
+EnumerationType
+ ::= 'enum' '(' StringLiteral ( ',' StringLiteral )* ')'
+FunctionType
+ ::= Annotation* ( AnyFunctionType | TypedFunctionType )
+Annotation
+ ::= '%' EQName ( '(' Constant ( ',' Constant )* ')' )?
+AnyFunctionType
+ ::= ( 'function' | 'fn' ) '(' '*' ')'
+TypedFunctionType
+ ::= ( 'function' | 'fn' ) '(' ( TypedFunctionParam ( ',' TypedFunctionParam )* )? ')' 'as' SequenceType
+TypedFunctionParam
+ ::= ( '$' EQName 'as' )? SequenceType
+ChoiceItemType
+ ::= '(' ItemType ( '|' ItemType )* ')'
+OccurrenceIndicator
+ ::= '?'
+ | '*'
+ | '+'
+VarValue ::= ExprSingle
+ExprSingle
+ ::= FLWORExpr
+ | QuantifiedExpr
+ | SwitchExpr
+ | TypeswitchExpr
+ | IfExpr
+ | TryCatchExpr
+ | OrExpr
+FLWORExpr
+ ::= InitialClause IntermediateClause* ReturnClause
+InitialClause
+ ::= ForClause
+ | LetClause
+ | WindowClause
+ForClause
+ ::= 'for' ForBinding ( ',' ForBinding )*
+ForBinding
+ ::= ForItemBinding
+ | ForMemberBinding
+ | ForEntryBinding
+ForItemBinding
+ ::= VarNameAndType AllowingEmpty? PositionalVar? 'in' ExprSingle
+VarNameAndType
+ ::= '$' EQName TypeDeclaration?
+TypeDeclaration
+ ::= 'as' SequenceType
+AllowingEmpty
+ ::= 'allowing' 'empty'
+PositionalVar
+ ::= 'at' VarName
+VarName ::= '$' EQName
+ForMemberBinding
+ ::= 'member' VarNameAndType PositionalVar? 'in' ExprSingle
+ForEntryBinding
+ ::= ( ForEntryKeyBinding ForEntryValueBinding? | ForEntryValueBinding ) PositionalVar? 'in' ExprSingle
+ForEntryKeyBinding
+ ::= 'key' VarNameAndType
+ForEntryValueBinding
+ ::= 'value' VarNameAndType
+LetClause
+ ::= 'let' LetBinding ( ',' LetBinding )*
+LetBinding
+ ::= LetValueBinding
+ | LetSequenceBinding
+ | LetArrayBinding
+ | LetMapBinding
+LetValueBinding
+ ::= VarNameAndType ':=' ExprSingle
+LetSequenceBinding
+ ::= '$' '(' VarNameAndType ( ',' VarNameAndType )* ')' TypeDeclaration? ':=' ExprSingle
+LetArrayBinding
+ ::= '$' '[' VarNameAndType ( ',' VarNameAndType )* ']' TypeDeclaration? ':=' ExprSingle
+LetMapBinding
+ ::= '$' '{' VarNameAndType ( ',' VarNameAndType )* '}' TypeDeclaration? ':=' ExprSingle
+WindowClause
+ ::= 'for' ( TumblingWindowClause | SlidingWindowClause )
+TumblingWindowClause
+ ::= 'tumbling' 'window' VarNameAndType 'in' ExprSingle WindowStartCondition? WindowEndCondition?
+WindowStartCondition
+ ::= 'start' WindowVars ( 'when' ExprSingle )?
+WindowVars
+ ::= CurrentVar? PositionalVar? PreviousVar? NextVar?
+CurrentVar
+ ::= VarName
+PreviousVar
+ ::= 'previous' VarName
+NextVar ::= 'next' VarName
+WindowEndCondition
+ ::= 'only'? 'end' WindowVars ( 'when' ExprSingle )?
+SlidingWindowClause
+ ::= 'sliding' 'window' VarNameAndType 'in' ExprSingle WindowStartCondition? WindowEndCondition
+IntermediateClause
+ ::= InitialClause
+ | WhereClause
+ | WhileClause
+ | GroupByClause
+ | OrderByClause
+ | CountClause
+WhereClause
+ ::= 'where' ExprSingle
+WhileClause
+ ::= 'while' ExprSingle
+GroupByClause
+ ::= 'group' 'by' GroupingSpec ( ',' GroupingSpec )*
+GroupingSpec
+ ::= VarName ( TypeDeclaration? ':=' ExprSingle )? ( 'collation' URILiteral )?
+OrderByClause
+ ::= 'stable'? 'order' 'by' OrderSpec ( ',' OrderSpec )*
+OrderSpec
+ ::= ExprSingle OrderModifier
+OrderModifier
+ ::= ( 'ascending' | 'descending' )? ( 'empty' ( 'greatest' | 'least' ) )? ( 'collation' URILiteral )?
+CountClause
+ ::= 'count' VarName
+ReturnClause
+ ::= 'return' ExprSingle
+QuantifiedExpr
+ ::= ( 'some' | 'every' ) QuantifierBinding ( ',' QuantifierBinding )* 'satisfies' ExprSingle
+QuantifierBinding
+ ::= VarNameAndType 'in' ExprSingle
+SwitchExpr
+ ::= 'switch' SwitchComparand ( SwitchCases | BracedSwitchCases )
+SwitchComparand
+ ::= '(' Expr? ')'
+Expr ::= ExprSingle ( ',' ExprSingle )*
+SwitchCases
+ ::= SwitchCaseClause+ 'default' 'return' ExprSingle
+SwitchCaseClause
+ ::= ( 'case' SwitchCaseOperand )+ 'return' ExprSingle
+SwitchCaseOperand
+ ::= Expr
+BracedSwitchCases
+ ::= '{' SwitchCases '}'
+TypeswitchExpr
+ ::= 'typeswitch' '(' Expr ')' ( TypeswitchCases | BracedTypeswitchCases )
+TypeswitchCases
+ ::= CaseClause+ 'default' VarName? 'return' ExprSingle
+CaseClause
+ ::= 'case' ( VarName 'as' )? SequenceTypeUnion 'return' ExprSingle
+SequenceTypeUnion
+ ::= SequenceType ( '|' SequenceType )*
+BracedTypeswitchCases
+ ::= '{' TypeswitchCases '}'
+IfExpr ::= 'if' '(' Expr ')' ( UnbracedActions | BracedAction )
+UnbracedActions
+ ::= 'then' ExprSingle 'else' ExprSingle
+BracedAction
+ ::= EnclosedExpr
+EnclosedExpr
+ ::= '{' Expr? '}'
+TryCatchExpr
+ ::= TryClause ( CatchClause+ FinallyClause? | FinallyClause )
+TryClause
+ ::= 'try' EnclosedExpr
+CatchClause
+ ::= 'catch' NameTestUnion EnclosedExpr
+FinallyClause
+ ::= 'finally' EnclosedExpr
+OrExpr ::= AndExpr ( 'or' AndExpr )*
+AndExpr ::= ComparisonExpr ( 'and' ComparisonExpr )*
+ComparisonExpr
+ ::= OtherwiseExpr ( ( ValueComp | GeneralComp | NodeComp ) OtherwiseExpr )?
+OtherwiseExpr
+ ::= StringConcatExpr ( 'otherwise' StringConcatExpr )*
+StringConcatExpr
+ ::= RangeExpr ( '||' RangeExpr )*
+RangeExpr
+ ::= AdditiveExpr ( 'to' AdditiveExpr )?
+AdditiveExpr
+ ::= MultiplicativeExpr ( ( '+' | '-' ) MultiplicativeExpr )*
+MultiplicativeExpr
+ ::= UnionExpr ( ( '*' | '×' | 'div' | '÷' | 'idiv' | 'mod' ) UnionExpr )*
+UnionExpr
+ ::= IntersectExceptExpr ( ( 'union' | '|' ) IntersectExceptExpr )*
+IntersectExceptExpr
+ ::= InstanceofExpr ( ( 'intersect' | 'except' ) InstanceofExpr )*
+InstanceofExpr
+ ::= TreatExpr ( 'instance' 'of' SequenceType )?
+TreatExpr
+ ::= CastableExpr ( 'treat' 'as' SequenceType )?
+CastableExpr
+ ::= CastExpr ( 'castable' 'as' CastTarget '?'? )?
+CastExpr ::= PipelineExpr ( 'cast' 'as' CastTarget '?'? )?
+PipelineExpr
+ ::= ArrowExpr ( '->' ArrowExpr )*
+ArrowExpr
+ ::= UnaryExpr ( SequenceArrowTarget | MappingArrowTarget )*
+UnaryExpr
+ ::= ( '-' | '+' )* ValueExpr
+ValueExpr
+ ::= ValidateExpr
+ | ExtensionExpr
+ | SimpleMapExpr
+ValidateExpr
+ ::= 'validate' ( ValidationMode | 'type' TypeName )? '{' Expr '}'
+ValidationMode
+ ::= 'lax'
+ | 'strict'
+ExtensionExpr
+ ::= Pragma+ '{' Expr? '}'
+Pragma ::= '(#' S EQName ( S PragmaContents )? '#)'
+ /* ws: explicit */
+SimpleMapExpr
+ ::= PathExpr ( '!' PathExpr )*
+PathExpr ::= AbsolutePathExpr
+ | RelativePathExpr
+AbsolutePathExpr
+ ::= '/' ( RelativePathExpr / )
+ | '//' RelativePathExpr
+RelativePathExpr
+ ::= StepExpr ( ( '/' | '//' ) StepExpr )*
+StepExpr ::= PostfixExpr
+ | AxisStep
+PostfixExpr
+ ::= PrimaryExpr ( Predicate | PositionalArgumentList | Lookup | MethodCallSuffix | FilterExprAMSuffix )*
+PrimaryExpr
+ ::= Literal
+ | VarRef
+ | ParenthesizedExpr
+ | ContextValueRef
+ | FunctionCall
+ | OrderedExpr
+ | UnorderedExpr
+ | NodeConstructor
+ | FunctionItemExpr
+ | MapConstructor
+ | ArrayConstructor
+ | StringTemplate
+ | StringConstructor
+ | UnaryLookup
+Literal ::= NumericLiteral
+ | StringLiteral
+ | QNameLiteral
+VarRef ::= '$' EQName
+ParenthesizedExpr
+ ::= '(' Expr? ')'
+ContextValueRef
+ ::= '.'
+FunctionCall
+ ::= UnreservedFunctionEQName ArgumentList
+UnreservedFunctionEQName
+ ::= UnreservedFunctionQName
+ | URIQualifiedName
+ArgumentList
+ ::= '(' ( PositionalArguments ( ',' KeywordArguments )? | KeywordArguments )? ')'
+PositionalArguments
+ ::= Argument ( ',' Argument )*
+Argument ::= ExprSingle
+ | ArgumentPlaceholder
+ArgumentPlaceholder
+ ::= '?'
+KeywordArguments
+ ::= KeywordArgument ( ',' KeywordArgument )*
+KeywordArgument
+ ::= EQName ':=' Argument
+OrderedExpr
+ ::= 'ordered' EnclosedExpr
+UnorderedExpr
+ ::= 'unordered' EnclosedExpr
+NodeConstructor
+ ::= DirectConstructor
+ | ComputedConstructor
+DirectConstructor
+ ::= DirElemConstructor
+ | DirCommentConstructor
+ | DirPIConstructor
+DirElemConstructor
+ ::= '<'^DirElemConstructor QName DirAttributeList ( '/>' | '>' DirElemContent* '' QName S? '>' )
+ /* ws: explicit */
+DirAttributeList
+ ::= ( S ( QName S? '=' S? DirAttributeValue )? )*
+ /* ws: explicit */
+DirAttributeValue
+ ::= '"' ( EscapeQuot | QuotAttrValueContent )* '"'
+ | "'" ( EscapeApos | AposAttrValueContent )* "'"
+ /* ws: explicit */
+QuotAttrValueContent
+ ::= QuotAttrContentChar
+ | CommonContent
+CommonContent
+ ::= PredefinedEntityRef
+ | CharRef
+ | '{{'
+ | '}}'
+ | EnclosedExpr
+AposAttrValueContent
+ ::= AposAttrContentChar
+ | CommonContent
+DirElemContent
+ ::= DirectConstructor
+ | CDataSection
+ | CommonContent
+ | ElementContentChar
+CDataSection
+ ::= ''
+ /* ws: explicit */
+DirCommentConstructor
+ ::= ''
+ /* ws: explicit */
+DirPIConstructor
+ ::= '' PITarget ( S DirPIContents )? '?>'
+ /* ws: explicit */
+ComputedConstructor
+ ::= CompDocConstructor
+ | CompElemConstructor
+ | CompAttrConstructor
+ | CompNamespaceConstructor
+ | CompTextConstructor
+ | CompCommentConstructor
+ | CompPIConstructor
+CompDocConstructor
+ ::= 'document' EnclosedExpr
+CompElemConstructor
+ ::= 'element' CompNodeName EnclosedContentExpr
+CompNodeName
+ ::= QNameLiteral
+ | UnreservedName
+ | '{' Expr '}'
+UnreservedName
+ ::= UnreservedQName
+ | URIQualifiedName
+EnclosedContentExpr
+ ::= EnclosedExpr
+CompAttrConstructor
+ ::= 'attribute' CompNodeName EnclosedExpr
+CompNamespaceConstructor
+ ::= 'namespace' CompNodeNCName EnclosedExpr
+CompNodeNCName
+ ::= MarkedNCName
+ | UnreservedNCName
+ | '{' Expr '}'
+MarkedNCName
+ ::= '#' NCName
+CompTextConstructor
+ ::= 'text' EnclosedExpr
+CompCommentConstructor
+ ::= 'comment' EnclosedExpr
+CompPIConstructor
+ ::= 'processing-instruction' CompNodeNCName EnclosedExpr
+FunctionItemExpr
+ ::= NamedFunctionRef
+ | InlineFunctionExpr
+NamedFunctionRef
+ ::= UnreservedFunctionEQName '#' IntegerLiteral
+InlineFunctionExpr
+ ::= Annotation* ( 'function' | 'fn' ) FunctionSignature? FunctionBody
+FunctionSignature
+ ::= '(' ParamList ')' TypeDeclaration?
+ParamList
+ ::= ( VarNameAndType ( ',' VarNameAndType )* )?
+FunctionBody
+ ::= EnclosedExpr
+MapConstructor
+ ::= 'map'? '{' ( MapConstructorEntry ( ',' MapConstructorEntry )* )? '}'
+MapConstructorEntry
+ ::= ExprSingle ( ':' ExprSingle )?
+ArrayConstructor
+ ::= SquareArrayConstructor
+ | CurlyArrayConstructor
+SquareArrayConstructor
+ ::= '[' ( ExprSingle ( ',' ExprSingle )* )? ']'
+CurlyArrayConstructor
+ ::= 'array' EnclosedExpr
+StringTemplate
+ ::= '`' ( StringTemplateFixedPart | StringTemplateVariablePart )* '`'
+ /* ws: explicit */
+StringTemplateVariablePart
+ ::= EnclosedExpr
+StringConstructor
+ ::= '``[' StringConstructorContent ']``'
+ /* ws: explicit */
+StringConstructorContent
+ ::= StringConstructorChars ( StringInterpolation StringConstructorChars )*
+ /* ws: explicit */
+StringInterpolation
+ ::= '`' EnclosedExpr '`'
+ /* ws: explicit */
+UnaryLookup
+ ::= Lookup
+Lookup ::= '?' KeySpecifier
+KeySpecifier
+ ::= NCName
+ | Literal
+ | ContextValueRef
+ | VarRef
+ | ParenthesizedExpr
+ | LookupWildcard
+LookupWildcard
+ ::= '*'
+Predicate
+ ::= '[' Expr ']'
+PositionalArgumentList
+ ::= '(' PositionalArguments? ')'
+MethodCallSuffix
+ ::= '=?>' NCName PositionalArgumentList
+FilterExprAMSuffix
+ ::= '?[' Expr ']'
+AxisStep ::= ( AbbreviatedStep | FullStep ) Predicate*
+AbbreviatedStep
+ ::= '..'
+ | '@' NodeTest
+ | SimpleNodeTest
+NodeTest ::= UnionNodeTest
+ | SimpleNodeTest
+UnionNodeTest
+ ::= '(' SimpleNodeTest ( '|' SimpleNodeTest )* ')'
+SimpleNodeTest
+ ::= TypeTest
+ | Selector
+TypeTest ::= GNodeType
+ | NodeKindTest
+ | JNodeType
+Selector ::= EQName
+ | Wildcard
+ | 'get' '(' ExprSingle ')'
+FullStep ::= Axis NodeTest
+Axis ::= ( 'ancestor' | 'ancestor-or-self' | 'attribute' | 'child' | 'descendant' | 'descendant-or-self' | 'following' | 'following-or-self' | 'following-sibling' | 'following-sibling-or-self' | 'parent' | 'preceding' | 'preceding-or-self' | 'preceding-sibling' | 'preceding-sibling-or-self' | 'self' ) '::'
+SequenceArrowTarget
+ ::= '=>' ArrowTarget
+ArrowTarget
+ ::= FunctionCall
+ | RestrictedDynamicCall
+RestrictedDynamicCall
+ ::= ( VarRef | ParenthesizedExpr | FunctionItemExpr | MapConstructor | ArrayConstructor ) PositionalArgumentList
+MappingArrowTarget
+ ::= '=!>' ArrowTarget
+CastTarget
+ ::= TypeName
+ | ChoiceItemType
+ | EnumerationType
+ValueComp
+ ::= 'eq'
+ | 'ne'
+ | 'lt'
+ | 'le'
+ | 'gt'
+ | 'ge'
+GeneralComp
+ ::= '='
+ | '!='
+ | '<'^GeneralComp
+ | '<='
+ | '>'
+ | '>='
+NodeComp ::= 'is'
+ | 'is-not'
+ | NodePrecedes
+ | NodeFollows
+ | 'precedes-or-is'
+ | 'follows-or-is'
+NodePrecedes
+ ::= '<<'
+ | 'precedes'
+NodeFollows
+ ::= '>>'
+ | 'follows'
+VarDefaultValue
+ ::= ExprSingle
+VarDecl ::= 'declare' Annotation* 'variable' VarNameAndType ( ':=' VarValue | 'external' ( ':=' VarDefaultValue )? )
+FunctionDecl
+ ::= 'declare' Annotation* 'function' UnreservedFunctionEQName '(' ParamListWithDefaults? ')' TypeDeclaration? ( FunctionBody | 'external' )
+ParamListWithDefaults
+ ::= ParamWithDefault ( ',' ParamWithDefault )*
+ParamWithDefault
+ ::= VarNameAndType ( ':=' ExprSingle )?
+ItemTypeDecl
+ ::= 'declare' Annotation* 'type' EQName 'as' ItemType
+NamedRecordTypeDecl
+ ::= 'declare' Annotation* 'record' EQName '(' ( ExtendedFieldDeclaration ( ',' ExtendedFieldDeclaration )* )? ')'
+ExtendedFieldDeclaration
+ ::= FieldDeclaration ( ':=' ExprSingle )?
+OptionDecl
+ ::= 'declare' 'option' EQName StringLiteral
+MainModule
+ ::= Prolog QueryBody
+QueryBody
+ ::= Expr
+Whitespace
+ ::= S^WS
+ | Comment
+ /* ws: definition */
+Comment ::= '(:' ( CommentContents | Comment )* ':)'
+ /* ws: explicit */
+
+
+
+EOF ::= $
+StringLiteral
+ ::= AposStringLiteral
+ | QuotStringLiteral
+ /* ws: explicit */
+AposStringLiteral
+ ::= "'" ( PredefinedEntityRef | CharRef | EscapeApos | [^'&] )* "'"
+ /* ws: explicit */
+PredefinedEntityRef
+ ::= '&' ( 'lt' | 'gt' | 'amp' | 'quot' | 'apos' ) ';'
+ /* ws: explicit */
+CharRef ::= '' [0-9]+ ';'
+ | '' [0-9a-fA-F]+ ';'
+EscapeApos
+ ::= "''"
+ /* ws: explicit */
+QuotStringLiteral
+ ::= '"' ( PredefinedEntityRef | CharRef | EscapeQuot | [^"&] )* '"'
+ /* ws: explicit */
+EscapeQuot
+ ::= '""'
+ /* ws: explicit */
+UnreservedNCName
+ ::= NCName - ReservedName
+NCName ::= Name - ( Char* ':' Char* )
+Name ::= NameStartChar NameChar*
+NameStartChar
+ ::= ':'
+ | [A-Z]
+ | '_'
+ | [a-z]
+ | [#xC0-#xD6]
+ | [#xD8-#xF6]
+ | [#xF8-#x2FF]
+ | [#x370-#x37D]
+ | [#x37F-#x1FFF]
+ | [#x200C-#x200D]
+ | [#x2070-#x218F]
+ | [#x2C00-#x2FEF]
+ | [#x3001-#xD7FF]
+ | [#xF900-#xFDCF]
+ | [#xFDF0-#xFFFD]
+ | [#x10000-#xEFFFF]
+NameChar ::= NameStartChar
+ | '-'
+ | '.'
+ | [0-9]
+ | #xB7
+ | [#x0300-#x036F]
+ | [#x203F-#x2040]
+Char ::= #x9
+ | #xA
+ | #xD
+ | [#x20-#xD7FF]
+ | [#xE000-#xFFFD]
+ | [#x10000-#x10FFFF]
+ReservedName
+ ::= 'NaN'
+ | 'allowing'
+ | 'ancestor'
+ | 'ancestor-or-self'
+ | 'and'
+ | 'array'
+ | 'as'
+ | 'ascending'
+ | 'at'
+ | 'attribute'
+ | 'base-uri'
+ | 'boundary-space'
+ | 'by'
+ | 'case'
+ | 'cast'
+ | 'castable'
+ | 'catch'
+ | 'child'
+ | 'collation'
+ | 'comment'
+ | 'construction'
+ | 'context'
+ | 'copy-namespaces'
+ | 'count'
+ | 'decimal-format'
+ | 'decimal-separator'
+ | 'declare'
+ | 'default'
+ | 'descendant'
+ | 'descendant-or-self'
+ | 'descending'
+ | 'digit'
+ | 'div'
+ | 'document'
+ | 'document-node'
+ | 'element'
+ | 'else'
+ | 'empty'
+ | 'empty-sequence'
+ | 'encoding'
+ | 'end'
+ | 'enum'
+ | 'eq'
+ | 'every'
+ | 'except'
+ | 'exponent-separator'
+ | 'external'
+ | 'false'
+ | 'finally'
+ | 'fixed'
+ | 'fn'
+ | 'following'
+ | 'following-or-self'
+ | 'following-sibling'
+ | 'following-sibling-or-self'
+ | 'follows'
+ | 'follows-or-is'
+ | 'for'
+ | 'function'
+ | 'ge'
+ | 'get'
+ | 'gnode'
+ | 'greatest'
+ | 'group'
+ | 'grouping-separator'
+ | 'gt'
+ | 'idiv'
+ | 'if'
+ | 'import'
+ | 'in'
+ | 'infinity'
+ | 'inherit'
+ | 'instance'
+ | 'intersect'
+ | 'is'
+ | 'is-not'
+ | 'item'
+ | 'jnode'
+ | 'key'
+ | 'lax'
+ | 'le'
+ | 'least'
+ | 'let'
+ | 'lt'
+ | 'map'
+ | 'member'
+ | 'minus-sign'
+ | 'mod'
+ | 'module'
+ | 'namespace'
+ | 'namespace-node'
+ | 'ne'
+ | 'next'
+ | 'no-inherit'
+ | 'no-preserve'
+ | 'node'
+ | 'of'
+ | 'only'
+ | 'option'
+ | 'or'
+ | 'order'
+ | 'ordered'
+ | 'ordering'
+ | 'otherwise'
+ | 'parent'
+ | 'pattern-separator'
+ | 'per-mille'
+ | 'percent'
+ | 'precedes'
+ | 'precedes-or-is'
+ | 'preceding'
+ | 'preceding-or-self'
+ | 'preceding-sibling'
+ | 'preceding-sibling-or-self'
+ | 'preserve'
+ | 'previous'
+ | 'processing-instruction'
+ | 'record'
+ | 'return'
+ | 'satisfies'
+ | 'schema'
+ | 'schema-attribute'
+ | 'schema-element'
+ | 'self'
+ | 'sliding'
+ | 'some'
+ | 'stable'
+ | 'start'
+ | 'strict'
+ | 'strip'
+ | 'switch'
+ | 'text'
+ | 'then'
+ | 'to'
+ | 'treat'
+ | 'true'
+ | 'try'
+ | 'tumbling'
+ | 'type'
+ | 'typeswitch'
+ | 'union'
+ | 'unordered'
+ | 'validate'
+ | 'value'
+ | 'variable'
+ | 'version'
+ | 'when'
+ | 'where'
+ | 'while'
+ | 'window'
+ | 'xquery'
+ | 'zero-digit'
+URIQualifiedName
+ ::= BracedURILiteral ( NCName ':' )? NCName
+ /* ws: explicit */
+BracedURILiteral
+ ::= 'Q' '{' ( PredefinedEntityRef | CharRef | [^&{}] )* '}'
+ /* ws: explicit */
+UnreservedQName
+ ::= QName - ReservedName
+QName ::= PrefixedName
+ | UnprefixedName
+PrefixedName
+ ::= Prefix ':' LocalPart
+Prefix ::= NCName
+LocalPart
+ ::= NCName
+UnprefixedName
+ ::= LocalPart
+Wildcard ::= '*'
+ | NCName ':*'
+ | '*:' NCName
+ | BracedURILiteral '*'
+ /* ws: explicit */
+IntegerLiteral
+ ::= Digits
+ /* ws: explicit */
+Digits ::= DecDigit ( ( DecDigit | '_' )* DecDigit )?
+ /* ws: explicit */
+DecDigit ::= [0-9]
+ /* ws: explicit */
+HexIntegerLiteral
+ ::= '0x' HexDigits
+ /* ws: explicit */
+HexDigits
+ ::= HexDigit ( ( HexDigit | '_' )* HexDigit )?
+ /* ws: explicit */
+HexDigit ::= [0-9a-fA-F]
+ /* ws: explicit */
+BinaryIntegerLiteral
+ ::= '0b' BinaryDigits
+ /* ws: explicit */
+BinaryDigits
+ ::= BinaryDigit ( ( BinaryDigit | '_' )* BinaryDigit )?
+ /* ws: explicit */
+BinaryDigit
+ ::= [0-1]
+ /* ws: explicit */
+DecimalLiteral
+ ::= '.' Digits
+ | Digits '.' Digits?
+ /* ws: explicit */
+DoubleLiteral
+ ::= ( '.' Digits | Digits ( '.' Digits? )? ) [eE] [+#x2D]? Digits
+ /* ws: explicit */
+S ::= ( #x20 | #x9 | #xD | #xA )+
+PragmaContents
+ ::= Char* - ( Char* '#)' Char* ) & '#'
+QuotAttrContentChar
+ ::= Char - ["{}<&]
+AposAttrContentChar
+ ::= Char - ['{}<&]
+ElementContentChar
+ ::= Char - [{}<&]
+CDataSectionContents
+ ::= Char* - ( Char* ']]>' Char* ) & ']]'
+DirCommentContents
+ ::= ( Char - '-' | '-' ( Char - '-' ) )*
+ /* ws: explicit */
+PITarget ::= NCName - ( ( 'X' | 'x' ) ( 'M' | 'm' ) ( 'L' | 'l' ) )
+DirPIContents
+ ::= Char* - ( Char* '?>' Char* ) & '?'
+StringTemplateFixedPart
+ ::= ( Char - ( '{' | '}' | '`' ) | '{{' | '}}' | '``' )+
+ /* ws: explicit */
+StringConstructorChars
+ ::= Char* - ( Char* ( '`{' | ']``' ) Char* ) & ( '`{' | ']`' )
+CommentContents
+ ::= ( Char+ - ( Char* ( '(:' | ':)' ) Char* ) ) - ( Char* '(' ) & ':'
+ | Char+ - ( Char* ( '(:' | ':)' ) Char* ) & '('
+ /* ws: explicit */
+QNameOrKeywordDelimiter
+ ::= $
+ | ':'
+ | Char - NameChar
+NCNameDelimiter
+ ::= $ $
+ | ( Char - NameChar ) ( $ | Char )
+ | ':' ( Char - NameStartChar )
+NumericLiteralDelimiter
+ ::= QNameOrKeywordDelimiter
+ | '-'
+GeneralCompDelimiter
+ ::= [^?]
+DirElemConstructorDelimiter
+ ::= QName ( S QName S? '=' | S? [/>] )
+'*' << Wildcard
+QNameOrKeywordDelimiter
+ \\ UnreservedQName 'NaN' 'allowing' 'ancestor' 'ancestor-or-self' 'and' 'array' 'as' 'ascending' 'at' 'attribute' 'base-uri' 'boundary-space' 'by' 'case' 'cast' 'castable' 'catch' 'child' 'collation' 'comment' 'construction' 'context' 'copy-namespaces' 'count' 'decimal-format' 'decimal-separator' 'declare' 'default' 'descendant' 'descendant-or-self' 'descending' 'digit' 'div' 'document' 'document-node' 'element' 'else' 'empty' 'empty-sequence' 'encoding' 'end' 'enum' 'eq' 'every' 'except' 'exponent-separator' 'external' 'false' 'finally' 'fixed' 'fn' 'following' 'following-or-self' 'following-sibling' 'following-sibling-or-self' 'follows' 'follows-or-is' 'for' 'function' 'ge' 'get' 'gnode' 'greatest' 'group' 'grouping-separator' 'gt' 'idiv' 'if' 'import' 'in' 'infinity' 'inherit' 'instance' 'intersect' 'is' 'is-not' 'item' 'jnode' 'key' 'lax' 'le' 'least' 'let' 'lt' 'map' 'member' 'minus-sign' 'mod' 'module' 'namespace' 'namespace-node' 'ne' 'next' 'no-inherit' 'no-preserve' 'node' 'of' 'only' 'option' 'or' 'order' 'ordered' 'ordering' 'otherwise' 'parent' 'pattern-separator' 'per-mille' 'percent' 'precedes' 'precedes-or-is' 'preceding' 'preceding-or-self' 'preceding-sibling' 'preceding-sibling-or-self' 'preserve' 'previous' 'processing-instruction' 'record' 'return' 'satisfies' 'schema' 'schema-attribute' 'schema-element' 'self' 'sliding' 'some' 'stable' 'start' 'strict' 'strip' 'switch' 'text' 'then' 'to' 'treat' 'true' 'try' 'tumbling' 'type' 'typeswitch' 'union' 'unordered' 'validate' 'value' 'variable' 'version' 'when' 'where' 'while' 'window' 'xquery' 'zero-digit'
+Char \\ '' '<<' '<='
+NCNameDelimiter
+ \\ UnreservedNCName
+NumericLiteralDelimiter
+ \\ DecimalLiteral DoubleLiteral IntegerLiteral
+GeneralCompDelimiter
+ \\ '<'^GeneralComp
+DirElemConstructorDelimiter
+ \\ '<'^DirElemConstructor
+S \\ '(#'
diff --git a/grammars/XQuery-FullText-10.ebnf b/grammars/XQuery-FullText-10.ebnf
new file mode 100644
index 00000000..4e6f4bf8
--- /dev/null
+++ b/grammars/XQuery-FullText-10.ebnf
@@ -0,0 +1,196 @@
+(: ======================================================================
+ XQuery and XPath Full Text 1.0 — Additional Productions
+ https://www.w3.org/TR/xpath-full-text-10/
+
+ This is a REFERENCE file, not a standalone parseable grammar.
+ It documents only the productions that Full Text 1.0 adds to or
+ modifies in the base XQuery grammar. For a complete parseable
+ grammar that includes these productions, see
+ XQuery-31-Update-FullText.ebnf.
+
+ The base XQuery 3.1 grammar is in XQuery-31.ebnf.
+ ====================================================================== :)
+
+(: ======================================================================
+ MODIFIED PRODUCTIONS
+ These exist in the base grammar but are changed by Full Text.
+ ====================================================================== :)
+
+(: Changed: adds FTOptionDecl to the first group of prolog declarations.
+ Base grammar has: ( DefaultNamespaceDecl | Setter | NamespaceDecl | Import )
+ FT adds: FTOptionDecl to that group. :)
+Prolog ::= ( ( DefaultNamespaceDecl | Setter | NamespaceDecl | Import | FTOptionDecl ) Separator )* ( ( ContextItemDecl | AnnotatedDecl | OptionDecl ) Separator )*
+
+(: Changed: operand type changed from StringConcatExpr to FTContainsExpr.
+ Base grammar has: StringConcatExpr ( ( ValueComp | GeneralComp | NodeComp ) StringConcatExpr )?
+ FT replaces StringConcatExpr with FTContainsExpr to insert the
+ 'contains text' expression into the precedence chain. :)
+ComparisonExpr
+ ::= FTContainsExpr ( ( ValueComp | GeneralComp | NodeComp ) FTContainsExpr )?
+
+(: Changed: adds optional FTScoreVar? before 'in'.
+ Base grammar has: '$' VarName TypeDeclaration? AllowingEmpty? PositionalVar? 'in' ExprSingle
+ FT adds: FTScoreVar? after PositionalVar?. :)
+ForBinding
+ ::= '$' VarName TypeDeclaration? AllowingEmpty? PositionalVar? FTScoreVar? 'in' ExprSingle
+
+(: Changed: adds FTScoreVar as an alternative binding target.
+ Base grammar has: '$' VarName TypeDeclaration? ':=' ExprSingle
+ FT adds: ( '$' VarName TypeDeclaration? | FTScoreVar ) as alternatives. :)
+LetBinding
+ ::= ( '$' VarName TypeDeclaration? | FTScoreVar ) ':=' ExprSingle
+
+(: ======================================================================
+ NEW PRODUCTIONS
+ These are entirely new nonterminals added by Full Text 1.0.
+ ====================================================================== :)
+
+(: --- Prolog Declaration --- :)
+
+FTOptionDecl
+ ::= 'declare' 'ft-option' FTMatchOptions
+
+(: --- Score Variable --- :)
+
+FTScoreVar
+ ::= 'score' '$' VarName
+
+(: --- Contains Expression --- :)
+
+FTContainsExpr
+ ::= StringConcatExpr ( 'contains' 'text' FTSelection FTIgnoreOption? )?
+
+(: --- Full-Text Selection (Boolean Operators) --- :)
+
+FTSelection
+ ::= FTOr FTPosFilter*
+FTWeight ::= 'weight' '{' Expr '}'
+FTOr ::= FTAnd ( 'ftor' FTAnd )*
+FTAnd ::= FTMildNot ( 'ftand' FTMildNot )*
+FTMildNot
+ ::= FTUnaryNot ( 'not' 'in' FTUnaryNot )*
+FTUnaryNot
+ ::= 'ftnot'? FTPrimaryWithOptions
+FTPrimaryWithOptions
+ ::= FTPrimary FTMatchOptions? FTWeight?
+
+(: --- Primary Full-Text Expressions --- :)
+
+FTPrimary
+ ::= FTWords FTTimes?
+ | '(' FTSelection ')'
+ | FTExtensionSelection
+FTWords ::= FTWordsValue FTAnyallOption?
+FTWordsValue
+ ::= StringLiteral
+ | '{' Expr '}'
+FTExtensionSelection
+ ::= Pragma+ '{' FTSelection? '}'
+FTAnyallOption
+ ::= 'any' 'word'?
+ | 'all' 'words'?
+ | 'phrase'
+
+(: --- Occurrence Indicator --- :)
+
+FTTimes ::= 'occurs' FTRange 'times'
+FTRange ::= 'exactly' AdditiveExpr
+ | 'at' 'least' AdditiveExpr
+ | 'at' 'most' AdditiveExpr
+ | 'from' AdditiveExpr 'to' AdditiveExpr
+
+(: --- Positional Filters --- :)
+
+FTPosFilter
+ ::= FTOrder
+ | FTWindow
+ | FTDistance
+ | FTScope
+ | FTContent
+FTOrder ::= 'ordered'
+FTWindow ::= 'window' AdditiveExpr FTUnit
+FTDistance
+ ::= 'distance' FTRange FTUnit
+FTUnit ::= 'words'
+ | 'sentences'
+ | 'paragraphs'
+FTScope ::= ( 'same' | 'different' ) FTBigUnit
+FTBigUnit
+ ::= 'sentence'
+ | 'paragraph'
+FTContent
+ ::= 'at' 'start'
+ | 'at' 'end'
+ | 'entire' 'content'
+
+(: --- Match Options --- :)
+
+FTMatchOptions
+ ::= ( 'using' FTMatchOption )+
+FTMatchOption
+ ::= FTLanguageOption
+ | FTWildCardOption
+ | FTThesaurusOption
+ | FTStemOption
+ | FTCaseOption
+ | FTDiacriticsOption
+ | FTStopWordOption
+ | FTExtensionOption
+FTCaseOption
+ ::= 'case' 'insensitive'
+ | 'case' 'sensitive'
+ | 'lowercase'
+ | 'uppercase'
+FTDiacriticsOption
+ ::= 'diacritics' 'insensitive'
+ | 'diacritics' 'sensitive'
+FTStemOption
+ ::= 'stemming'
+ | 'no' 'stemming'
+FTThesaurusOption
+ ::= 'thesaurus' ( FTThesaurusID | 'default' )
+ | 'thesaurus' '(' ( FTThesaurusID | 'default' ) ( ',' FTThesaurusID )* ')'
+ | 'no' 'thesaurus'
+FTThesaurusID
+ ::= 'at' URILiteral ( 'relationship' StringLiteral )? ( FTLiteralRange 'levels' )?
+FTLiteralRange
+ ::= 'exactly' IntegerLiteral
+ | 'at' 'least' IntegerLiteral
+ | 'at' 'most' IntegerLiteral
+ | 'from' IntegerLiteral 'to' IntegerLiteral
+FTStopWordOption
+ ::= 'stop' 'words' FTStopWords FTStopWordsInclExcl*
+ | 'stop' 'words' 'default' FTStopWordsInclExcl*
+ | 'no' 'stop' 'words'
+FTStopWords
+ ::= 'at' URILiteral
+ | '(' StringLiteral ( ',' StringLiteral )* ')'
+FTStopWordsInclExcl
+ ::= ( 'union' | 'except' ) FTStopWords
+FTLanguageOption
+ ::= 'language' StringLiteral
+FTWildCardOption
+ ::= 'wildcards'
+ | 'no' 'wildcards'
+FTExtensionOption
+ ::= 'option' EQName StringLiteral
+
+(: --- Ignore Option --- :)
+
+FTIgnoreOption
+ ::= 'without' 'content' UnionExpr
+
+(: ======================================================================
+ NEW KEYWORDS
+ Full Text 1.0 adds the following keywords that must appear in the
+ FunctionName and NCName disambiguation productions (and related
+ token/lookahead lines) of the combined grammar.
+
+ 'all', 'any', 'contains', 'content', 'diacritics', 'different',
+ 'distance', 'entire', 'exactly', 'from', 'ft-option', 'ftand',
+ 'ftnot', 'ftor', 'insensitive', 'language', 'levels', 'lowercase',
+ 'most', 'no', 'not', 'occurs', 'paragraph', 'paragraphs', 'phrase',
+ 'relationship', 'same', 'score', 'sensitive', 'sentence', 'sentences',
+ 'stemming', 'stop', 'thesaurus', 'times', 'uppercase', 'using',
+ 'weight', 'wildcards', 'without', 'word', 'words'
+ ====================================================================== :)
diff --git a/grammars/XQuery-Update-30.ebnf b/grammars/XQuery-Update-30.ebnf
new file mode 100644
index 00000000..a2dd561a
--- /dev/null
+++ b/grammars/XQuery-Update-30.ebnf
@@ -0,0 +1,768 @@
+XQuery ::= Module EOF
+Module ::= VersionDecl? ( LibraryModule | MainModule )
+VersionDecl
+ ::= 'xquery' ( 'encoding' StringLiteral | 'version' StringLiteral ( 'encoding' StringLiteral )? ) Separator
+MainModule
+ ::= Prolog QueryBody
+LibraryModule
+ ::= ModuleDecl Prolog
+ModuleDecl
+ ::= 'module' 'namespace' NCName '=' URILiteral Separator
+Prolog ::= ( ( DefaultNamespaceDecl | Setter | NamespaceDecl | Import ) Separator )* ( ( ContextItemDecl | AnnotatedDecl | OptionDecl ) Separator )*
+Separator
+ ::= ';'
+Setter ::= BoundarySpaceDecl
+ | DefaultCollationDecl
+ | BaseURIDecl
+ | ConstructionDecl
+ | OrderingModeDecl
+ | EmptyOrderDecl
+ | RevalidationDecl
+ | CopyNamespacesDecl
+ | DecimalFormatDecl
+BoundarySpaceDecl
+ ::= 'declare' 'boundary-space' ( 'preserve' | 'strip' )
+DefaultCollationDecl
+ ::= 'declare' 'default' 'collation' URILiteral
+BaseURIDecl
+ ::= 'declare' 'base-uri' URILiteral
+ConstructionDecl
+ ::= 'declare' 'construction' ( 'strip' | 'preserve' )
+OrderingModeDecl
+ ::= 'declare' 'ordering' ( 'ordered' | 'unordered' )
+EmptyOrderDecl
+ ::= 'declare' 'default' 'order' 'empty' ( 'greatest' | 'least' )
+CopyNamespacesDecl
+ ::= 'declare' 'copy-namespaces' PreserveMode ',' InheritMode
+PreserveMode
+ ::= 'preserve'
+ | 'no-preserve'
+InheritMode
+ ::= 'inherit'
+ | 'no-inherit'
+DecimalFormatDecl
+ ::= 'declare' ( 'decimal-format' EQName | 'default' 'decimal-format' ) ( DFPropertyName '=' StringLiteral )*
+DFPropertyName
+ ::= 'decimal-separator'
+ | 'grouping-separator'
+ | 'infinity'
+ | 'minus-sign'
+ | 'NaN'
+ | 'percent'
+ | 'per-mille'
+ | 'zero-digit'
+ | 'digit'
+ | 'pattern-separator'
+Import ::= SchemaImport
+ | ModuleImport
+SchemaImport
+ ::= 'import' 'schema' SchemaPrefix? URILiteral ( 'at' URILiteral ( ',' URILiteral )* )?
+SchemaPrefix
+ ::= 'namespace' NCName '='
+ | 'default' 'element' 'namespace'
+ModuleImport
+ ::= 'import' 'module' ( 'namespace' NCName '=' )? URILiteral ( 'at' URILiteral ( ',' URILiteral )* )?
+NamespaceDecl
+ ::= 'declare' 'namespace' NCName '=' URILiteral
+DefaultNamespaceDecl
+ ::= 'declare' 'default' ( 'element' | 'function' ) 'namespace' URILiteral
+AnnotatedDecl
+ ::= 'declare' ( CompatibilityAnnotation | Annotation )* ( VarDecl | FunctionDecl )
+CompatibilityAnnotation
+ ::= 'updating'
+Annotation
+ ::= '%' EQName ( '(' Literal ( ',' Literal )* ')' )?
+VarDecl ::= 'variable' '$' VarName TypeDeclaration? ( ':=' VarValue | 'external' ( ':=' VarDefaultValue )? )
+VarValue ::= ExprSingle
+VarDefaultValue
+ ::= ExprSingle
+ContextItemDecl
+ ::= 'declare' 'context' 'item' ( 'as' ItemType )? ( ':=' VarValue | 'external' ( ':=' VarDefaultValue )? )
+FunctionDecl
+ ::= 'function' EQName '(' ParamList? ')' ( 'as' SequenceType )? ( FunctionBody | 'external' )
+ParamList
+ ::= Param ( ',' Param )*
+Param ::= '$' EQName TypeDeclaration?
+FunctionBody
+ ::= EnclosedExpr
+EnclosedExpr
+ ::= '{' Expr '}'
+OptionDecl
+ ::= 'declare' 'option' EQName StringLiteral
+QueryBody
+ ::= Expr
+Expr ::= ExprSingle ( ',' ExprSingle )*
+ExprSingle
+ ::= FLWORExpr
+ | QuantifiedExpr
+ | SwitchExpr
+ | TypeswitchExpr
+ | IfExpr
+ | TryCatchExpr
+ | InsertExpr
+ | DeleteExpr
+ | RenameExpr
+ | ReplaceExpr
+ | UpdatingFunctionCall
+ | CopyModifyExpr
+ | OrExpr
+FLWORExpr
+ ::= InitialClause IntermediateClause* ReturnClause
+InitialClause
+ ::= ForClause
+ | LetClause
+ | WindowClause
+IntermediateClause
+ ::= InitialClause
+ | WhereClause
+ | GroupByClause
+ | OrderByClause
+ | CountClause
+ForClause
+ ::= 'for' ForBinding ( ',' ForBinding )*
+ForBinding
+ ::= '$' VarName TypeDeclaration? AllowingEmpty? PositionalVar? 'in' ExprSingle
+AllowingEmpty
+ ::= 'allowing' 'empty'
+PositionalVar
+ ::= 'at' '$' VarName
+LetClause
+ ::= 'let' LetBinding ( ',' LetBinding )*
+LetBinding
+ ::= '$' VarName TypeDeclaration? ':=' ExprSingle
+WindowClause
+ ::= 'for' ( TumblingWindowClause | SlidingWindowClause )
+TumblingWindowClause
+ ::= 'tumbling' 'window' '$' VarName TypeDeclaration? 'in' ExprSingle WindowStartCondition WindowEndCondition?
+SlidingWindowClause
+ ::= 'sliding' 'window' '$' VarName TypeDeclaration? 'in' ExprSingle WindowStartCondition WindowEndCondition
+WindowStartCondition
+ ::= 'start' WindowVars 'when' ExprSingle
+WindowEndCondition
+ ::= 'only'? 'end' WindowVars 'when' ExprSingle
+WindowVars
+ ::= ( '$' CurrentItem )? PositionalVar? ( 'previous' '$' PreviousItem )? ( 'next' '$' NextItem )?
+CurrentItem
+ ::= EQName
+PreviousItem
+ ::= EQName
+NextItem ::= EQName
+CountClause
+ ::= 'count' '$' VarName
+WhereClause
+ ::= 'where' ExprSingle
+GroupByClause
+ ::= 'group' 'by' GroupingSpecList
+GroupingSpecList
+ ::= GroupingSpec ( ',' GroupingSpec )*
+GroupingSpec
+ ::= GroupingVariable ( TypeDeclaration? ':=' ExprSingle )? ( 'collation' URILiteral )?
+GroupingVariable
+ ::= '$' VarName
+OrderByClause
+ ::= ( 'order' 'by' | 'stable' 'order' 'by' ) OrderSpecList
+OrderSpecList
+ ::= OrderSpec ( ',' OrderSpec )*
+OrderSpec
+ ::= ExprSingle OrderModifier
+OrderModifier
+ ::= ( 'ascending' | 'descending' )? ( 'empty' ( 'greatest' | 'least' ) )? ( 'collation' URILiteral )?
+ReturnClause
+ ::= 'return' ExprSingle
+QuantifiedExpr
+ ::= ( 'some' | 'every' ) '$' VarName TypeDeclaration? 'in' ExprSingle ( ',' '$' VarName TypeDeclaration? 'in' ExprSingle )* 'satisfies' ExprSingle
+SwitchExpr
+ ::= 'switch' '(' Expr ')' SwitchCaseClause+ 'default' 'return' ExprSingle
+SwitchCaseClause
+ ::= ( 'case' SwitchCaseOperand )+ 'return' ExprSingle
+SwitchCaseOperand
+ ::= ExprSingle
+TypeswitchExpr
+ ::= 'typeswitch' '(' Expr ')' CaseClause+ 'default' ( '$' VarName )? 'return' ExprSingle
+CaseClause
+ ::= 'case' ( '$' VarName 'as' )? SequenceTypeUnion 'return' ExprSingle
+SequenceTypeUnion
+ ::= SequenceType ( '|' SequenceType )*
+IfExpr ::= 'if' '(' Expr ')' 'then' ExprSingle 'else' ExprSingle
+TryCatchExpr
+ ::= TryClause CatchClause+
+TryClause
+ ::= 'try' '{' TryTargetExpr '}'
+TryTargetExpr
+ ::= Expr
+CatchClause
+ ::= 'catch' CatchErrorList '{' Expr '}'
+CatchErrorList
+ ::= NameTest ( '|' NameTest )*
+OrExpr ::= AndExpr ( 'or' AndExpr )*
+AndExpr ::= ComparisonExpr ( 'and' ComparisonExpr )*
+ComparisonExpr
+ ::= StringConcatExpr ( ( ValueComp | GeneralComp | NodeComp ) StringConcatExpr )?
+StringConcatExpr
+ ::= RangeExpr ( '||' RangeExpr )*
+RangeExpr
+ ::= AdditiveExpr ( 'to' AdditiveExpr )?
+AdditiveExpr
+ ::= MultiplicativeExpr ( ( '+' | '-' ) MultiplicativeExpr )*
+MultiplicativeExpr
+ ::= UnionExpr ( ( '*' | 'div' | 'idiv' | 'mod' ) UnionExpr )*
+UnionExpr
+ ::= IntersectExceptExpr ( ( 'union' | '|' ) IntersectExceptExpr )*
+IntersectExceptExpr
+ ::= InstanceofExpr ( ( 'intersect' | 'except' ) InstanceofExpr )*
+InstanceofExpr
+ ::= TreatExpr ( 'instance' 'of' SequenceType )?
+TreatExpr
+ ::= CastableExpr ( 'treat' 'as' SequenceType )?
+CastableExpr
+ ::= CastExpr ( 'castable' 'as' SingleType )?
+CastExpr ::= TransformWithExpr ( 'cast' 'as' SingleType )?
+TransformWithExpr
+ ::= UnaryExpr ( 'transform' 'with' '{' Expr? '}' )?
+UnaryExpr
+ ::= ( '-' | '+' )* ValueExpr
+ValueExpr
+ ::= ValidateExpr
+ | ExtensionExpr
+ | SimpleMapExpr
+GeneralComp
+ ::= '='
+ | '!='
+ | '<'
+ | '<='
+ | '>'
+ | '>='
+ValueComp
+ ::= 'eq'
+ | 'ne'
+ | 'lt'
+ | 'le'
+ | 'gt'
+ | 'ge'
+NodeComp ::= 'is'
+ | '<<'
+ | '>>'
+ValidateExpr
+ ::= 'validate' ( ValidationMode | 'type' TypeName )? '{' Expr '}'
+ValidationMode
+ ::= 'lax'
+ | 'strict'
+ExtensionExpr
+ ::= Pragma+ '{' Expr? '}'
+Pragma ::= '(#' S? EQName ( S PragmaContents )? '#)'
+ /* ws: explicit */
+SimpleMapExpr
+ ::= PathExpr ( '!' PathExpr )*
+PathExpr ::= '/' ( RelativePathExpr / )
+ | '//' RelativePathExpr
+ | RelativePathExpr
+RelativePathExpr
+ ::= StepExpr ( ( '/' | '//' ) StepExpr )*
+StepExpr ::= PostfixExpr
+ | AxisStep
+AxisStep ::= ( ReverseStep | ForwardStep ) PredicateList
+ForwardStep
+ ::= ForwardAxis NodeTest
+ | AbbrevForwardStep
+ForwardAxis
+ ::= 'child' '::'
+ | 'descendant' '::'
+ | 'attribute' '::'
+ | 'self' '::'
+ | 'descendant-or-self' '::'
+ | 'following-sibling' '::'
+ | 'following' '::'
+AbbrevForwardStep
+ ::= '@'? NodeTest
+ReverseStep
+ ::= ReverseAxis NodeTest
+ | AbbrevReverseStep
+ReverseAxis
+ ::= 'parent' '::'
+ | 'ancestor' '::'
+ | 'preceding-sibling' '::'
+ | 'preceding' '::'
+ | 'ancestor-or-self' '::'
+AbbrevReverseStep
+ ::= '..'
+NodeTest ::= KindTest
+ | NameTest
+NameTest ::= EQName
+ | Wildcard
+PostfixExpr
+ ::= PrimaryExpr ( Predicate | ArgumentList )*
+ArgumentList
+ ::= '(' ( Argument ( ',' Argument )* )? ')'
+PredicateList
+ ::= Predicate*
+Predicate
+ ::= '[' Expr ']'
+PrimaryExpr
+ ::= Literal
+ | VarRef
+ | ParenthesizedExpr
+ | ContextItemExpr
+ | FunctionCall
+ | OrderedExpr
+ | UnorderedExpr
+ | Constructor
+ | FunctionItemExpr
+Literal ::= NumericLiteral
+ | StringLiteral
+NumericLiteral
+ ::= IntegerLiteral
+ | DecimalLiteral
+ | DoubleLiteral
+VarRef ::= '$' VarName
+VarName ::= EQName
+ParenthesizedExpr
+ ::= '(' Expr? ')'
+ContextItemExpr
+ ::= '.'
+OrderedExpr
+ ::= 'ordered' '{' Expr '}'
+UnorderedExpr
+ ::= 'unordered' '{' Expr '}'
+FunctionCall
+ ::= FunctionEQName ArgumentList
+Argument ::= ExprSingle
+ | ArgumentPlaceholder
+ArgumentPlaceholder
+ ::= '?'
+Constructor
+ ::= DirectConstructor
+ | ComputedConstructor
+DirectConstructor
+ ::= DirElemConstructor
+ | DirCommentConstructor
+ | DirPIConstructor
+DirElemConstructor
+ ::= '<' QName DirAttributeList ( '/>' | '>' DirElemContent* '' QName S? '>' )
+ /* ws: explicit */
+DirAttributeList
+ ::= ( S ( QName S? '=' S? DirAttributeValue )? )*
+ /* ws: explicit */
+DirAttributeValue
+ ::= '"' ( EscapeQuot | QuotAttrValueContent )* '"'
+ | "'" ( EscapeApos | AposAttrValueContent )* "'"
+ /* ws: explicit */
+QuotAttrValueContent
+ ::= QuotAttrContentChar
+ | CommonContent
+AposAttrValueContent
+ ::= AposAttrContentChar
+ | CommonContent
+DirElemContent
+ ::= DirectConstructor
+ | CDataSection
+ | CommonContent
+ | ElementContentChar
+CommonContent
+ ::= PredefinedEntityRef
+ | CharRef
+ | '{{'
+ | '}}'
+ | EnclosedExpr
+DirCommentConstructor
+ ::= ''
+ /* ws: explicit */
+DirPIConstructor
+ ::= '' PITarget ( S DirPIContents )? '?>'
+ /* ws: explicit */
+CDataSection
+ ::= ''
+ /* ws: explicit */
+ComputedConstructor
+ ::= CompDocConstructor
+ | CompElemConstructor
+ | CompAttrConstructor
+ | CompNamespaceConstructor
+ | CompTextConstructor
+ | CompCommentConstructor
+ | CompPIConstructor
+CompDocConstructor
+ ::= 'document' '{' Expr '}'
+CompElemConstructor
+ ::= 'element' ( EQName | '{' Expr '}' ) '{' ContentExpr? '}'
+ContentExpr
+ ::= Expr
+CompAttrConstructor
+ ::= 'attribute' ( EQName | '{' Expr '}' ) '{' Expr? '}'
+CompNamespaceConstructor
+ ::= 'namespace' ( Prefix | '{' PrefixExpr '}' ) '{' URIExpr '}'
+Prefix ::= NCName
+PrefixExpr
+ ::= Expr
+URIExpr ::= Expr
+CompTextConstructor
+ ::= 'text' '{' Expr '}'
+CompCommentConstructor
+ ::= 'comment' '{' Expr '}'
+CompPIConstructor
+ ::= 'processing-instruction' ( NCName | '{' Expr '}' ) '{' Expr? '}'
+FunctionItemExpr
+ ::= NamedFunctionRef
+ | InlineFunctionExpr
+NamedFunctionRef
+ ::= EQName '#' IntegerLiteral
+InlineFunctionExpr
+ ::= Annotation* 'function' '(' ParamList? ')' ( 'as' SequenceType )? FunctionBody
+SingleType
+ ::= SimpleTypeName '?'?
+TypeDeclaration
+ ::= 'as' SequenceType
+SequenceType
+ ::= 'empty-sequence' '(' ')'
+ | ItemType ( OccurrenceIndicator / )
+OccurrenceIndicator
+ ::= '?'
+ | '*'
+ | '+'
+ItemType ::= KindTest
+ | 'item' '(' ')'
+ | FunctionTest
+ | AtomicOrUnionType
+ | ParenthesizedItemType
+AtomicOrUnionType
+ ::= EQName
+KindTest ::= DocumentTest
+ | ElementTest
+ | AttributeTest
+ | SchemaElementTest
+ | SchemaAttributeTest
+ | PITest
+ | CommentTest
+ | TextTest
+ | NamespaceNodeTest
+ | AnyKindTest
+AnyKindTest
+ ::= 'node' '(' ')'
+DocumentTest
+ ::= 'document-node' '(' ( ElementTest | SchemaElementTest )? ')'
+TextTest ::= 'text' '(' ')'
+CommentTest
+ ::= 'comment' '(' ')'
+NamespaceNodeTest
+ ::= 'namespace-node' '(' ')'
+PITest ::= 'processing-instruction' '(' ( NCName | StringLiteral )? ')'
+AttributeTest
+ ::= 'attribute' '(' ( AttribNameOrWildcard ( ',' TypeName )? )? ')'
+AttribNameOrWildcard
+ ::= AttributeName
+ | '*'
+SchemaAttributeTest
+ ::= 'schema-attribute' '(' AttributeDeclaration ')'
+AttributeDeclaration
+ ::= AttributeName
+ElementTest
+ ::= 'element' '(' ( ElementNameOrWildcard ( ',' TypeName '?'? )? )? ')'
+ElementNameOrWildcard
+ ::= ElementName
+ | '*'
+SchemaElementTest
+ ::= 'schema-element' '(' ElementDeclaration ')'
+ElementDeclaration
+ ::= ElementName
+AttributeName
+ ::= EQName
+ElementName
+ ::= EQName
+SimpleTypeName
+ ::= TypeName
+TypeName ::= EQName
+FunctionTest
+ ::= Annotation* ( AnyFunctionTest | TypedFunctionTest )
+AnyFunctionTest
+ ::= 'function' '(' '*' ')'
+TypedFunctionTest
+ ::= 'function' '(' ( SequenceType ( ',' SequenceType )* )? ')' 'as' SequenceType
+ParenthesizedItemType
+ ::= '(' ItemType ')'
+URILiteral
+ ::= StringLiteral
+RevalidationDecl
+ ::= 'declare' 'revalidation' ( 'strict' | 'lax' | 'skip' )
+InsertExprTargetChoice
+ ::= ( 'as' ( 'first' | 'last' ) )? 'into'
+ | 'after'
+ | 'before'
+InsertExpr
+ ::= 'insert' ( 'node' | 'nodes' ) SourceExpr InsertExprTargetChoice TargetExpr
+DeleteExpr
+ ::= 'delete' ( 'node' | 'nodes' ) TargetExpr
+ReplaceExpr
+ ::= 'replace' ( 'value' 'of' )? 'node' TargetExpr 'with' ExprSingle
+RenameExpr
+ ::= 'rename' 'node' TargetExpr 'as' NewNameExpr
+SourceExpr
+ ::= ExprSingle
+TargetExpr
+ ::= ExprSingle
+NewNameExpr
+ ::= ExprSingle
+UpdatingFunctionCall
+ ::= 'invoke' 'updating' PrimaryExpr '(' ( ExprSingle ( ',' ExprSingle )* )? ')'
+CopyModifyExpr
+ ::= 'copy' '$' VarName ':=' ExprSingle ( ',' '$' VarName ':=' ExprSingle )* 'modify' ExprSingle 'return' ExprSingle
+EQName ::= QName
+ | URIQualifiedName
+Whitespace
+ ::= S^WS
+ | Comment
+ /* ws: definition */
+Comment ::= '(:' ( CommentContents | Comment )* ':)'
+ /* ws: explicit */
+FunctionEQName
+ ::= FunctionName
+ | URIQualifiedName
+QName ::= FunctionName
+ | 'attribute'
+ | 'comment'
+ | 'document-node'
+ | 'element'
+ | 'empty-sequence'
+ | 'function'
+ | 'if'
+ | 'item'
+ | 'namespace-node'
+ | 'node'
+ | 'processing-instruction'
+ | 'schema-attribute'
+ | 'schema-element'
+ | 'switch'
+ | 'text'
+ | 'typeswitch'
+FunctionName
+ ::= QName^Token
+ | 'after'
+ | 'ancestor'
+ | 'ancestor-or-self'
+ | 'and'
+ | 'as'
+ | 'ascending'
+ | 'before'
+ | 'case'
+ | 'cast'
+ | 'castable'
+ | 'child'
+ | 'collation'
+ | 'copy'
+ | 'count'
+ | 'declare'
+ | 'default'
+ | 'delete'
+ | 'descendant'
+ | 'descendant-or-self'
+ | 'descending'
+ | 'div'
+ | 'document'
+ | 'else'
+ | 'empty'
+ | 'end'
+ | 'eq'
+ | 'every'
+ | 'except'
+ | 'first'
+ | 'following'
+ | 'following-sibling'
+ | 'for'
+ | 'ge'
+ | 'group'
+ | 'gt'
+ | 'idiv'
+ | 'import'
+ | 'insert'
+ | 'instance'
+ | 'intersect'
+ | 'into'
+ | 'invoke'
+ | 'is'
+ | 'last'
+ | 'le'
+ | 'let'
+ | 'lt'
+ | 'mod'
+ | 'modify'
+ | 'module'
+ | 'namespace'
+ | 'ne'
+ | 'only'
+ | 'or'
+ | 'order'
+ | 'ordered'
+ | 'parent'
+ | 'preceding'
+ | 'preceding-sibling'
+ | 'rename'
+ | 'replace'
+ | 'return'
+ | 'satisfies'
+ | 'self'
+ | 'some'
+ | 'stable'
+ | 'start'
+ | 'to'
+ | 'transform'
+ | 'treat'
+ | 'try'
+ | 'union'
+ | 'unordered'
+ | 'validate'
+ | 'where'
+ | 'with'
+ | 'xquery'
+NCName ::= NCName^Token
+ | 'after'
+ | 'and'
+ | 'as'
+ | 'ascending'
+ | 'before'
+ | 'case'
+ | 'cast'
+ | 'castable'
+ | 'collation'
+ | 'count'
+ | 'default'
+ | 'descending'
+ | 'div'
+ | 'else'
+ | 'empty'
+ | 'end'
+ | 'eq'
+ | 'except'
+ | 'for'
+ | 'ge'
+ | 'group'
+ | 'gt'
+ | 'idiv'
+ | 'instance'
+ | 'intersect'
+ | 'into'
+ | 'is'
+ | 'le'
+ | 'let'
+ | 'lt'
+ | 'mod'
+ | 'modify'
+ | 'ne'
+ | 'only'
+ | 'or'
+ | 'order'
+ | 'return'
+ | 'satisfies'
+ | 'stable'
+ | 'start'
+ | 'to'
+ | 'transform'
+ | 'treat'
+ | 'union'
+ | 'where'
+ | 'with'
+
+
+
+IntegerLiteral
+ ::= Digits
+DecimalLiteral
+ ::= '.' Digits
+ | Digits '.' [0-9]*
+DoubleLiteral
+ ::= ( '.' Digits | Digits ( '.' [0-9]* )? ) [eE] [+#x2D]? Digits
+StringLiteral
+ ::= '"' ( PredefinedEntityRef | CharRef | EscapeQuot | [^"&] )* '"'
+ | "'" ( PredefinedEntityRef | CharRef | EscapeApos | [^'&] )* "'"
+URIQualifiedName
+ ::= BracedURILiteral NCName
+BracedURILiteral
+ ::= 'Q' '{' ( PredefinedEntityRef | CharRef | [^&{}] )* '}'
+PredefinedEntityRef
+ ::= '&' ( 'lt' | 'gt' | 'amp' | 'quot' | 'apos' ) ';'
+EscapeQuot
+ ::= '""'
+EscapeApos
+ ::= "''"
+ElementContentChar
+ ::= Char - [{}<&]
+QuotAttrContentChar
+ ::= Char - ["{}<&]
+AposAttrContentChar
+ ::= Char - ['{}<&]
+PITarget ::= NCName - ( ( 'X' | 'x' ) ( 'M' | 'm' ) ( 'L' | 'l' ) )
+Name ::= NameStartChar NameChar*
+NameStartChar
+ ::= ':'
+ | [A-Z]
+ | '_'
+ | [a-z]
+ | [#xC0-#xD6]
+ | [#xD8-#xF6]
+ | [#xF8-#x2FF]
+ | [#x370-#x37D]
+ | [#x37F-#x1FFF]
+ | [#x200C-#x200D]
+ | [#x2070-#x218F]
+ | [#x2C00-#x2FEF]
+ | [#x3001-#xD7FF]
+ | [#xF900-#xFDCF]
+ | [#xFDF0-#xFFFD]
+ | [#x10000-#xEFFFF]
+NameChar ::= NameStartChar
+ | '-'
+ | '.'
+ | [0-9]
+ | #xB7
+ | [#x0300-#x036F]
+ | [#x203F-#x2040]
+CharRef ::= '' [0-9]+ ';'
+ | '' [0-9a-fA-F]+ ';'
+QName ::= PrefixedName
+ | UnprefixedName
+PrefixedName
+ ::= Prefix ':' LocalPart
+UnprefixedName
+ ::= LocalPart
+Prefix ::= NCName
+LocalPart
+ ::= NCName
+NCName ::= Name - ( Char* ':' Char* )
+S ::= ( #x20 | #x9 | #xD | #xA )+
+Char ::= #x9
+ | #xA
+ | #xD
+ | [#x20-#xD7FF]
+ | [#xE000-#xFFFD]
+ | [#x10000-#x10FFFF]
+Digits ::= [0-9]+
+CommentContents
+ ::= ( ( Char+ - ( Char* ( '(:' | ':)' ) Char* ) ) - ( Char* '(' ) ) &':'
+ | ( Char+ - ( Char* ( '(:' | ':)' ) Char* ) ) &'('
+PragmaContents
+ ::= ( Char* - ( Char* '#)' Char* ) ) &'#'
+DirCommentContents
+ ::= ( Char - '-' | '-' ( Char - '-' ) )*
+DirPIContents
+ ::= ( Char* - ( Char* '?>' Char* ) ) &'?'
+CDataSectionContents
+ ::= ( Char* - ( Char* ']]>' Char* ) ) &']]'
+Wildcard ::= '*'
+ | NCName ':' '*'
+ | '*' ':' NCName
+ | BracedURILiteral '*'
+EOF ::= $
+NonNCNameChar
+ ::= $
+ | ':'
+ | Char - NameChar
+DelimitingChar
+ ::= NonNCNameChar
+ | '-'
+ | '.'
+DelimitingChar
+ \\ DecimalLiteral DoubleLiteral IntegerLiteral
+NonNCNameChar
+ \\ NCName^Token QName^Token URIQualifiedName 'NaN' 'after' 'allowing' 'ancestor' 'ancestor-or-self' 'and' 'as' 'ascending' 'at' 'attribute' 'base-uri' 'before' 'boundary-space' 'by' 'case' 'cast' 'castable' 'catch' 'child' 'collation' 'comment' 'construction' 'context' 'copy' 'copy-namespaces' 'count' 'decimal-format' 'decimal-separator' 'declare' 'default' 'delete' 'descendant' 'descendant-or-self' 'descending' 'digit' 'div' 'document' 'document-node' 'element' 'else' 'empty' 'empty-sequence' 'encoding' 'end' 'eq' 'every' 'except' 'external' 'first' 'following' 'following-sibling' 'for' 'function' 'ge' 'greatest' 'group' 'grouping-separator' 'gt' 'idiv' 'if' 'import' 'in' 'infinity' 'inherit' 'insert' 'instance' 'intersect' 'into' 'is' 'item' 'last' 'lax' 'le' 'least' 'let' 'lt' 'minus-sign' 'mod' 'modify' 'module' 'namespace' 'namespace-node' 'ne' 'next' 'no-inherit' 'no-preserve' 'node' 'nodes' 'of' 'only' 'option' 'or' 'order' 'ordered' 'ordering' 'parent' 'pattern-separator' 'per-mille' 'percent' 'preceding' 'preceding-sibling' 'preserve' 'previous' 'processing-instruction' 'rename' 'replace' 'return' 'revalidation' 'satisfies' 'schema' 'schema-attribute' 'schema-element' 'self' 'skip' 'sliding' 'some' 'stable' 'start' 'strict' 'strip' 'switch' 'text' 'then' 'to' 'treat' 'try' 'tumbling' 'type' 'typeswitch' 'union' 'unordered' 'updating' 'validate' 'value' 'variable' 'version' 'when' 'where' 'window' 'with' 'xquery' 'zero-digit'
+QName^Token
+ << 'after' 'ancestor' 'ancestor-or-self' 'and' 'as' 'ascending' 'attribute' 'before' 'case' 'cast' 'castable' 'child' 'collation' 'comment' 'copy' 'count' 'declare' 'default' 'delete' 'descendant' 'descendant-or-self' 'descending' 'div' 'document' 'document-node' 'element' 'else' 'empty' 'empty-sequence' 'end' 'eq' 'every' 'except' 'first' 'following' 'following-sibling' 'for' 'function' 'ge' 'group' 'gt' 'idiv' 'if' 'import' 'insert' 'instance' 'intersect' 'into' 'invoke' 'is' 'item' 'last' 'le' 'let' 'lt' 'mod' 'modify' 'module' 'namespace' 'namespace-node' 'ne' 'node' 'only' 'or' 'order' 'ordered' 'parent' 'preceding' 'preceding-sibling' 'processing-instruction' 'rename' 'replace' 'return' 'satisfies' 'schema-attribute' 'schema-element' 'self' 'some' 'stable' 'start' 'switch' 'text' 'to' 'transform' 'treat' 'try' 'typeswitch' 'union' 'unordered' 'validate' 'where' 'with' 'xquery'
+NCName^Token
+ << 'after' 'and' 'as' 'ascending' 'before' 'case' 'cast' 'castable' 'collation' 'count' 'default' 'descending' 'div' 'else' 'empty' 'end' 'eq' 'except' 'for' 'ge' 'group' 'gt' 'idiv' 'instance' 'intersect' 'into' 'is' 'le' 'let' 'lt' 'mod' 'modify' 'ne' 'only' 'or' 'order' 'return' 'satisfies' 'stable' 'start' 'to' 'transform' 'treat' 'union' 'where' 'with'
+'*' << Wildcard
\ No newline at end of file
diff --git a/grammars/XQuery-Update-eXist-Legacy.ebnf b/grammars/XQuery-Update-eXist-Legacy.ebnf
new file mode 100644
index 00000000..34df757f
--- /dev/null
+++ b/grammars/XQuery-Update-eXist-Legacy.ebnf
@@ -0,0 +1,35 @@
+/* XQuery Update Facility — eXist Legacy (XQUFEL)
+ * Reference grammar for eXist-db's proprietary update syntax.
+ * See: http://exist-db.org/exist/apps/doc/update_ext.xml
+ *
+ * These are eXist-db specific extensions, NOT part of any W3C specification.
+ * They predate the W3C XQuery Update Facility and use a different syntax.
+ *
+ * Integration: ExistUpdateExpr should be added as an alternative to ExprSingle.
+ * Keywords: 'update' must be added to FunctionName, NCName, and disambiguation lines.
+ */
+
+/* MODIFIED PRODUCTION:
+ * ExprSingle: add ExistUpdateExpr as alternative
+ * ExprSingle ::= ... | ExistUpdateExpr | OrExpr
+ */
+
+/* NEW PRODUCTIONS */
+ExistUpdateExpr
+ ::= 'update' ( ExistInsertExpr | ExistReplaceExpr | ExistValueExpr | ExistDeleteExpr | ExistRenameExpr )
+ExistInsertExpr
+ ::= 'insert' ExprSingle ( 'into' | 'following' | 'preceding' ) ExprSingle
+ExistReplaceExpr
+ ::= 'replace' ExprSingle 'with' ExprSingle
+ExistValueExpr
+ ::= 'value' ExprSingle 'with' ExprSingle
+ExistDeleteExpr
+ ::= 'delete' ExprSingle
+ExistRenameExpr
+ ::= 'rename' ExprSingle 'as' ExprSingle
+
+/* NEW KEYWORDS to add to FunctionName, NCName, and disambiguation lines:
+ * 'update'
+ * Note: 'insert', 'delete', 'replace', 'rename', 'into', 'with', 'value',
+ * 'following', 'preceding' are already keywords from W3C Update Facility 3.0.
+ */
diff --git a/index.html.tmpl b/index.html.tmpl
index ca1c6a0d..9e24f656 100755
--- a/index.html.tmpl
+++ b/index.html.tmpl
@@ -3,65 +3,16 @@
eXide
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
@@ -77,10 +28,18 @@
with support for CSS3 flexbox.
+
@@ -340,24 +324,45 @@
-
-
-
-
-
Drop files here or click on the button below to upload them
- to the selected collection. Maximum file size is 100MB.
-
-
-
-
+
-
@@ -540,7 +654,53 @@
+
+
Drop files here or click on the button below to upload them
+ to the selected collection. Maximum file size is 100MB.
+
+
+
-
+
Storing resource
@@ -588,49 +748,50 @@
@@ -762,8 +1020,8 @@
Copyright (C) 2011-2014 Wolfgang Meier, eXist Solutions GmbH
Licensed under the GPLv3 license
-
Uses the Ajax.org Code Editor (ACE), originally developed by Ajax.org
- and licensed under the tri-license MPL/LGPL/GPL.
+
Uses CodeMirror 6 by Marijn Haverbeke,
+ licensed under the MIT license.
-