This is page 135 of 144. Use http://codebase.md/xmlui-org/xmlui/assets/img/%7BfaviconUrl%7D?format=md&page={x} to view the full context.

# Directory Structure

```
├── .changeset
│   └── config.json
├── .eslintrc.cjs
├── .github
│   ├── build-checklist.png
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows
│       ├── deploy-blog-optimized.yml
│       ├── deploy-blog-swa.yml
│       ├── deploy-blog.yml
│       ├── deploy-docs-optimized.yml
│       ├── deploy-docs-swa.yml
│       ├── deploy-docs.yml
│       ├── prepare-versions.yml
│       ├── release-packages.yml
│       ├── run-all-tests.yml
│       └── run-smoke-tests.yml
├── .gitignore
├── .prettierrc.js
├── .vscode
│   ├── launch.json
│   └── settings.json
├── blog
│   ├── .gitignore
│   ├── .gitkeep
│   ├── CHANGELOG.md
│   ├── extensions.ts
│   ├── index.html
│   ├── index.ts
│   ├── package.json
│   ├── public
│   │   ├── blog
│   │   │   ├── images
│   │   │   │   ├── an-advanced-codefence.gif
│   │   │   │   ├── an-advanced-codefence.mp4
│   │   │   │   ├── blog-page-component.png
│   │   │   │   ├── blog-scrabble.png
│   │   │   │   ├── codefence-runner.png
│   │   │   │   ├── integrated-blog-search.png
│   │   │   │   ├── lorem-ipsum.png
│   │   │   │   ├── playground-checkbox-source.png
│   │   │   │   ├── playground.png
│   │   │   │   ├── use-xmlui-mcp-to-find-a-howto.png
│   │   │   │   └── xmlui-demo-gallery.png
│   │   │   ├── introducing-xmlui.md
│   │   │   ├── lorem-ipsum.md
│   │   │   ├── newest-post.md
│   │   │   ├── older-post.md
│   │   │   ├── xmlui-playground.md
│   │   │   └── xmlui-powered-blog.md
│   │   ├── mockServiceWorker.js
│   │   ├── resources
│   │   │   ├── favicon.ico
│   │   │   ├── files
│   │   │   │   └── for-download
│   │   │   │       └── xmlui
│   │   │   │           └── xmlui-standalone.umd.js
│   │   │   ├── github.svg
│   │   │   ├── llms.txt
│   │   │   ├── logo-dark.svg
│   │   │   ├── logo.svg
│   │   │   ├── pg-popout.svg
│   │   │   ├── rss.svg
│   │   │   └── xmlui-logo.svg
│   │   ├── serve.json
│   │   ├── staticwebapp.config.json
│   │   └── web.config
│   ├── scripts
│   │   ├── download-latest-xmlui.js
│   │   ├── generate-rss.js
│   │   ├── get-releases.js
│   │   └── utils.js
│   ├── src
│   │   ├── components
│   │   │   ├── BlogOverview.xmlui
│   │   │   ├── BlogPage.xmlui
│   │   │   └── PageNotFound.xmlui
│   │   ├── config.ts
│   │   ├── Main.xmlui
│   │   └── themes
│   │       └── blog-theme.ts
│   └── tsconfig.json
├── CONTRIBUTING.md
├── docs
│   ├── .gitignore
│   ├── CHANGELOG.md
│   ├── ComponentRefLinks.txt
│   ├── content
│   │   ├── _meta.json
│   │   ├── components
│   │   │   ├── _meta.json
│   │   │   ├── _overview.md
│   │   │   ├── APICall.md
│   │   │   ├── App.md
│   │   │   ├── AppHeader.md
│   │   │   ├── AppState.md
│   │   │   ├── AutoComplete.md
│   │   │   ├── Avatar.md
│   │   │   ├── Backdrop.md
│   │   │   ├── Badge.md
│   │   │   ├── BarChart.md
│   │   │   ├── Bookmark.md
│   │   │   ├── Breakout.md
│   │   │   ├── Button.md
│   │   │   ├── Card.md
│   │   │   ├── Carousel.md
│   │   │   ├── ChangeListener.md
│   │   │   ├── Checkbox.md
│   │   │   ├── CHStack.md
│   │   │   ├── ColorPicker.md
│   │   │   ├── Column.md
│   │   │   ├── ContentSeparator.md
│   │   │   ├── CVStack.md
│   │   │   ├── DataSource.md
│   │   │   ├── DateInput.md
│   │   │   ├── DatePicker.md
│   │   │   ├── DonutChart.md
│   │   │   ├── DropdownMenu.md
│   │   │   ├── EmojiSelector.md
│   │   │   ├── ExpandableItem.md
│   │   │   ├── FileInput.md
│   │   │   ├── FileUploadDropZone.md
│   │   │   ├── FlowLayout.md
│   │   │   ├── Footer.md
│   │   │   ├── Form.md
│   │   │   ├── FormItem.md
│   │   │   ├── FormSection.md
│   │   │   ├── Fragment.md
│   │   │   ├── H1.md
│   │   │   ├── H2.md
│   │   │   ├── H3.md
│   │   │   ├── H4.md
│   │   │   ├── H5.md
│   │   │   ├── H6.md
│   │   │   ├── Heading.md
│   │   │   ├── HSplitter.md
│   │   │   ├── HStack.md
│   │   │   ├── Icon.md
│   │   │   ├── IFrame.md
│   │   │   ├── Image.md
│   │   │   ├── Items.md
│   │   │   ├── LabelList.md
│   │   │   ├── Legend.md
│   │   │   ├── LineChart.md
│   │   │   ├── Link.md
│   │   │   ├── List.md
│   │   │   ├── Logo.md
│   │   │   ├── Markdown.md
│   │   │   ├── MenuItem.md
│   │   │   ├── MenuSeparator.md
│   │   │   ├── ModalDialog.md
│   │   │   ├── NavGroup.md
│   │   │   ├── NavLink.md
│   │   │   ├── NavPanel.md
│   │   │   ├── NoResult.md
│   │   │   ├── NumberBox.md
│   │   │   ├── Option.md
│   │   │   ├── Page.md
│   │   │   ├── PageMetaTitle.md
│   │   │   ├── Pages.md
│   │   │   ├── Pagination.md
│   │   │   ├── PasswordInput.md
│   │   │   ├── PieChart.md
│   │   │   ├── ProgressBar.md
│   │   │   ├── Queue.md
│   │   │   ├── RadioGroup.md
│   │   │   ├── RealTimeAdapter.md
│   │   │   ├── Redirect.md
│   │   │   ├── Select.md
│   │   │   ├── Slider.md
│   │   │   ├── Slot.md
│   │   │   ├── SpaceFiller.md
│   │   │   ├── Spinner.md
│   │   │   ├── Splitter.md
│   │   │   ├── Stack.md
│   │   │   ├── StickyBox.md
│   │   │   ├── SubMenuItem.md
│   │   │   ├── Switch.md
│   │   │   ├── TabItem.md
│   │   │   ├── Table.md
│   │   │   ├── TableOfContents.md
│   │   │   ├── Tabs.md
│   │   │   ├── Text.md
│   │   │   ├── TextArea.md
│   │   │   ├── TextBox.md
│   │   │   ├── Theme.md
│   │   │   ├── TimeInput.md
│   │   │   ├── Timer.md
│   │   │   ├── ToneChangerButton.md
│   │   │   ├── ToneSwitch.md
│   │   │   ├── Tooltip.md
│   │   │   ├── Tree.md
│   │   │   ├── VSplitter.md
│   │   │   ├── VStack.md
│   │   │   ├── xmlui-animations
│   │   │   │   ├── _meta.json
│   │   │   │   ├── _overview.md
│   │   │   │   ├── Animation.md
│   │   │   │   ├── FadeAnimation.md
│   │   │   │   ├── FadeInAnimation.md
│   │   │   │   ├── FadeOutAnimation.md
│   │   │   │   ├── ScaleAnimation.md
│   │   │   │   └── SlideInAnimation.md
│   │   │   ├── xmlui-pdf
│   │   │   │   ├── _meta.json
│   │   │   │   ├── _overview.md
│   │   │   │   └── Pdf.md
│   │   │   ├── xmlui-spreadsheet
│   │   │   │   ├── _meta.json
│   │   │   │   ├── _overview.md
│   │   │   │   └── Spreadsheet.md
│   │   │   └── xmlui-website-blocks
│   │   │       ├── _meta.json
│   │   │       ├── _overview.md
│   │   │       ├── Carousel.md
│   │   │       ├── HelloMd.md
│   │   │       ├── HeroSection.md
│   │   │       └── ScrollToTop.md
│   │   └── extensions
│   │       ├── _meta.json
│   │       ├── xmlui-animations
│   │       │   ├── _meta.json
│   │       │   ├── _overview.md
│   │       │   ├── Animation.md
│   │       │   ├── FadeAnimation.md
│   │       │   ├── FadeInAnimation.md
│   │       │   ├── FadeOutAnimation.md
│   │       │   ├── ScaleAnimation.md
│   │       │   └── SlideInAnimation.md
│   │       └── xmlui-website-blocks
│   │           ├── _meta.json
│   │           ├── _overview.md
│   │           ├── Carousel.md
│   │           ├── HelloMd.md
│   │           ├── HeroSection.md
│   │           └── ScrollToTop.md
│   ├── extensions.ts
│   ├── index.html
│   ├── index.ts
│   ├── package.json
│   ├── public
│   │   ├── feed.rss
│   │   ├── mockServiceWorker.js
│   │   ├── pages
│   │   │   ├── _meta.json
│   │   │   ├── app-structure.md
│   │   │   ├── build-editor-component.md
│   │   │   ├── build-hello-world-component.md
│   │   │   ├── components-intro.md
│   │   │   ├── context-variables.md
│   │   │   ├── forms.md
│   │   │   ├── globals.md
│   │   │   ├── glossary.md
│   │   │   ├── helper-tags.md
│   │   │   ├── hosted-deployment.md
│   │   │   ├── howto
│   │   │   │   ├── assign-a-complex-json-literal-to-a-component-variable.md
│   │   │   │   ├── chain-a-refetch.md
│   │   │   │   ├── control-cache-invalidation.md
│   │   │   │   ├── debounce-user-input-for-api-calls.md
│   │   │   │   ├── debounce-with-changelistener.md
│   │   │   │   ├── debug-a-component.md
│   │   │   │   ├── delay-a-datasource-until-another-datasource-is-ready.md
│   │   │   │   ├── delegate-a-method.md
│   │   │   │   ├── do-custom-form-validation.md
│   │   │   │   ├── expose-a-method-from-a-component.md
│   │   │   │   ├── filter-and-transform-data-from-an-api.md
│   │   │   │   ├── group-items-in-list-by-a-property.md
│   │   │   │   ├── handle-background-operations.md
│   │   │   │   ├── hide-an-element-until-its-datasource-is-ready.md
│   │   │   │   ├── make-a-set-of-equal-width-cards.md
│   │   │   │   ├── make-a-table-responsive.md
│   │   │   │   ├── make-navpanel-width-responsive.md
│   │   │   │   ├── modify-a-value-reported-in-a-column.md
│   │   │   │   ├── paginate-a-list.md
│   │   │   │   ├── pass-data-to-a-modal-dialog.md
│   │   │   │   ├── react-to-button-click-not-keystrokes.md
│   │   │   │   ├── set-the-initial-value-of-a-select-from-fetched-data.md
│   │   │   │   ├── share-a-modaldialog-across-components.md
│   │   │   │   ├── sync-selections-between-table-and-list-views.md
│   │   │   │   ├── update-ui-optimistically.md
│   │   │   │   ├── use-built-in-form-validation.md
│   │   │   │   └── use-the-same-modaldialog-to-add-or-edit.md
│   │   │   ├── howto.md
│   │   │   ├── intro.md
│   │   │   ├── layout.md
│   │   │   ├── markup.md
│   │   │   ├── mcp.md
│   │   │   ├── modal-dialogs.md
│   │   │   ├── news-and-reviews.md
│   │   │   ├── reactive-intro.md
│   │   │   ├── refactoring.md
│   │   │   ├── routing-and-links.md
│   │   │   ├── samples
│   │   │   │   ├── color-palette.xmlui
│   │   │   │   ├── color-values.xmlui
│   │   │   │   ├── shadow-sizes.xmlui
│   │   │   │   ├── spacing-sizes.xmlui
│   │   │   │   ├── swatch.xmlui
│   │   │   │   ├── theme-gallery-brief.xmlui
│   │   │   │   └── theme-gallery.xmlui
│   │   │   ├── scoping.md
│   │   │   ├── scripting.md
│   │   │   ├── styles-and-themes
│   │   │   │   ├── common-units.md
│   │   │   │   ├── layout-props.md
│   │   │   │   ├── theme-variable-defaults.md
│   │   │   │   ├── theme-variables.md
│   │   │   │   └── themes.md
│   │   │   ├── template-properties.md
│   │   │   ├── test.md
│   │   │   ├── tutorial-01.md
│   │   │   ├── tutorial-02.md
│   │   │   ├── tutorial-03.md
│   │   │   ├── tutorial-04.md
│   │   │   ├── tutorial-05.md
│   │   │   ├── tutorial-06.md
│   │   │   ├── tutorial-07.md
│   │   │   ├── tutorial-08.md
│   │   │   ├── tutorial-09.md
│   │   │   ├── tutorial-10.md
│   │   │   ├── tutorial-11.md
│   │   │   ├── tutorial-12.md
│   │   │   ├── universal-properties.md
│   │   │   ├── user-defined-components.md
│   │   │   ├── vscode.md
│   │   │   ├── working-with-markdown.md
│   │   │   ├── working-with-text.md
│   │   │   ├── xmlui-animations
│   │   │   │   ├── _meta.json
│   │   │   │   ├── _overview.md
│   │   │   │   ├── Animation.md
│   │   │   │   ├── FadeAnimation.md
│   │   │   │   ├── FadeInAnimation.md
│   │   │   │   ├── FadeOutAnimation.md
│   │   │   │   ├── ScaleAnimation.md
│   │   │   │   └── SlideInAnimation.md
│   │   │   ├── xmlui-charts
│   │   │   │   ├── _meta.json
│   │   │   │   ├── _overview.md
│   │   │   │   ├── BarChart.md
│   │   │   │   ├── DonutChart.md
│   │   │   │   ├── LabelList.md
│   │   │   │   ├── Legend.md
│   │   │   │   ├── LineChart.md
│   │   │   │   └── PieChart.md
│   │   │   ├── xmlui-pdf
│   │   │   │   ├── _meta.json
│   │   │   │   ├── _overview.md
│   │   │   │   └── Pdf.md
│   │   │   └── xmlui-spreadsheet
│   │   │       ├── _meta.json
│   │   │       ├── _overview.md
│   │   │       └── Spreadsheet.md
│   │   ├── resources
│   │   │   ├── devdocs
│   │   │   │   ├── debug-proxy-object-2.png
│   │   │   │   ├── debug-proxy-object.png
│   │   │   │   ├── table_editor_01.png
│   │   │   │   ├── table_editor_02.png
│   │   │   │   ├── table_editor_03.png
│   │   │   │   ├── table_editor_04.png
│   │   │   │   ├── table_editor_05.png
│   │   │   │   ├── table_editor_06.png
│   │   │   │   ├── table_editor_07.png
│   │   │   │   ├── table_editor_08.png
│   │   │   │   ├── table_editor_09.png
│   │   │   │   ├── table_editor_10.png
│   │   │   │   ├── table_editor_11.png
│   │   │   │   ├── table-editor-01.png
│   │   │   │   ├── table-editor-02.png
│   │   │   │   ├── table-editor-03.png
│   │   │   │   ├── table-editor-04.png
│   │   │   │   ├── table-editor-06.png
│   │   │   │   ├── table-editor-07.png
│   │   │   │   ├── table-editor-08.png
│   │   │   │   ├── table-editor-09.png
│   │   │   │   └── xmlui-rendering-of-tiptap-markdown.png
│   │   │   ├── favicon.ico
│   │   │   ├── files
│   │   │   │   ├── clients.json
│   │   │   │   ├── daily-revenue.json
│   │   │   │   ├── dashboard-stats.json
│   │   │   │   ├── demo.xmlui
│   │   │   │   ├── demo.xmlui.xs
│   │   │   │   ├── downloads
│   │   │   │   │   └── downloads.json
│   │   │   │   ├── for-download
│   │   │   │   │   ├── index-with-api.html
│   │   │   │   │   ├── index.html
│   │   │   │   │   ├── mockApi.js
│   │   │   │   │   ├── start-darwin.sh
│   │   │   │   │   ├── start-linux.sh
│   │   │   │   │   ├── start.bat
│   │   │   │   │   └── xmlui
│   │   │   │   │       └── xmlui-standalone.umd.js
│   │   │   │   ├── getting-started
│   │   │   │   │   ├── cl-tutorial-final.zip
│   │   │   │   │   ├── cl-tutorial.zip
│   │   │   │   │   ├── cl-tutorial2.zip
│   │   │   │   │   ├── cl-tutorial3.zip
│   │   │   │   │   ├── cl-tutorial4.zip
│   │   │   │   │   ├── cl-tutorial5.zip
│   │   │   │   │   ├── cl-tutorial6.zip
│   │   │   │   │   ├── getting-started.zip
│   │   │   │   │   ├── hello-xmlui.zip
│   │   │   │   │   ├── xmlui-empty.zip
│   │   │   │   │   └── xmlui-starter.zip
│   │   │   │   ├── howto
│   │   │   │   │   └── component-icons
│   │   │   │   │       └── up-arrow.svg
│   │   │   │   ├── invoices.json
│   │   │   │   ├── monthly-status.json
│   │   │   │   ├── news-and-reviews.json
│   │   │   │   ├── products.json
│   │   │   │   ├── releases.json
│   │   │   │   ├── tutorials
│   │   │   │   │   ├── datasource
│   │   │   │   │   │   └── api.ts
│   │   │   │   │   └── p2do
│   │   │   │   │       ├── api.ts
│   │   │   │   │       └── todo-logo.svg
│   │   │   │   └── xmlui.json
│   │   │   ├── github.svg
│   │   │   ├── images
│   │   │   │   ├── apiaction-tutorial
│   │   │   │   │   ├── add-success.png
│   │   │   │   │   ├── apiaction-param.png
│   │   │   │   │   ├── change-completed.png
│   │   │   │   │   ├── change-in-progress.png
│   │   │   │   │   ├── confirm-delete.png
│   │   │   │   │   ├── data-error.png
│   │   │   │   │   ├── data-progress.png
│   │   │   │   │   ├── data-success.png
│   │   │   │   │   ├── display-1.png
│   │   │   │   │   ├── item-deleted.png
│   │   │   │   │   ├── item-updated.png
│   │   │   │   │   ├── missing-api-key.png
│   │   │   │   │   ├── new-item-added.png
│   │   │   │   │   └── test-message.png
│   │   │   │   ├── chat-api
│   │   │   │   │   └── domain-model.svg
│   │   │   │   ├── components
│   │   │   │   │   ├── image
│   │   │   │   │   │   └── breakfast.jpg
│   │   │   │   │   ├── markdown
│   │   │   │   │   │   └── colors.png
│   │   │   │   │   └── modal
│   │   │   │   │       ├── deep_link_dialog_1.jpg
│   │   │   │   │       └── deep_link_dialog_2.jpg
│   │   │   │   ├── create-apps
│   │   │   │   │   ├── collapsed-vertical.png
│   │   │   │   │   ├── using-forms-warning-dialog.png
│   │   │   │   │   └── using-forms.png
│   │   │   │   ├── datasource-tutorial
│   │   │   │   │   ├── data-with-header.png
│   │   │   │   │   ├── filtered-data.png
│   │   │   │   │   ├── filtered-items.png
│   │   │   │   │   ├── initial-page-items.png
│   │   │   │   │   ├── list-items.png
│   │   │   │   │   ├── next-page-items.png
│   │   │   │   │   ├── no-data.png
│   │   │   │   │   ├── pagination-1.jpg
│   │   │   │   │   ├── pagination-1.png
│   │   │   │   │   ├── polling-1.png
│   │   │   │   │   ├── refetch-data.png
│   │   │   │   │   ├── slow-loading.png
│   │   │   │   │   ├── test-message.png
│   │   │   │   │   ├── Thumbs.db
│   │   │   │   │   ├── unconventional-data.png
│   │   │   │   │   └── unfiltered-items.png
│   │   │   │   ├── flower.jpg
│   │   │   │   ├── get-started
│   │   │   │   │   ├── add-new-contact.png
│   │   │   │   │   ├── app-modified.png
│   │   │   │   │   ├── app-start.png
│   │   │   │   │   ├── app-with-boxes.png
│   │   │   │   │   ├── app-with-toast.png
│   │   │   │   │   ├── boilerplate-structure.png
│   │   │   │   │   ├── cl-initial.png
│   │   │   │   │   ├── cl-start.png
│   │   │   │   │   ├── contact-counts.png
│   │   │   │   │   ├── contact-dialog-title.png
│   │   │   │   │   ├── contact-dialog.png
│   │   │   │   │   ├── contact-menus.png
│   │   │   │   │   ├── contact-predicates.png
│   │   │   │   │   ├── context-menu.png
│   │   │   │   │   ├── dashboard-numbers.png
│   │   │   │   │   ├── default-contact-list.png
│   │   │   │   │   ├── delete-contact.png
│   │   │   │   │   ├── delete-task.png
│   │   │   │   │   ├── detailed-template.png
│   │   │   │   │   ├── edit-contact-details.png
│   │   │   │   │   ├── edited-contact-saved.png
│   │   │   │   │   ├── empty-sections.png
│   │   │   │   │   ├── filter-completed.png
│   │   │   │   │   ├── fullwidth-desktop.png
│   │   │   │   │   ├── fullwidth-mobile.png
│   │   │   │   │   ├── initial-table.png
│   │   │   │   │   ├── items-and-badges.png
│   │   │   │   │   ├── loading-message.png
│   │   │   │   │   ├── new-contact-button.png
│   │   │   │   │   ├── new-contact-saved.png
│   │   │   │   │   ├── no-empty-sections.png
│   │   │   │   │   ├── personal-todo-initial.png
│   │   │   │   │   ├── piechart.png
│   │   │   │   │   ├── review-today.png
│   │   │   │   │   ├── rudimentary-dashboard.png
│   │   │   │   │   ├── section-collapsed.png
│   │   │   │   │   ├── sectioned-items.png
│   │   │   │   │   ├── sections-ordered.png
│   │   │   │   │   ├── spacex-list-with-links.png
│   │   │   │   │   ├── spacex-list.png
│   │   │   │   │   ├── start-personal-todo-1.png
│   │   │   │   │   ├── submit-new-contact.png
│   │   │   │   │   ├── submit-new-task.png
│   │   │   │   │   ├── syntax-highlighting.png
│   │   │   │   │   ├── table-with-badge.png
│   │   │   │   │   ├── template-with-card.png
│   │   │   │   │   ├── test-emulated-api.png
│   │   │   │   │   ├── Thumbs.db
│   │   │   │   │   ├── todo-logo.png
│   │   │   │   │   └── xmlui-tools.png
│   │   │   │   ├── HelloApp.png
│   │   │   │   ├── HelloApp2.png
│   │   │   │   ├── logos
│   │   │   │   │   ├── xmlui1.svg
│   │   │   │   │   ├── xmlui2.svg
│   │   │   │   │   ├── xmlui3.svg
│   │   │   │   │   ├── xmlui4.svg
│   │   │   │   │   ├── xmlui5.svg
│   │   │   │   │   ├── xmlui6.svg
│   │   │   │   │   └── xmlui7.svg
│   │   │   │   ├── pdf
│   │   │   │   │   └── dummy-pdf.jpg
│   │   │   │   ├── rendering-engine
│   │   │   │   │   ├── AppEngine-flow.svg
│   │   │   │   │   ├── Component.svg
│   │   │   │   │   ├── CompoundComponent.svg
│   │   │   │   │   ├── RootComponent.svg
│   │   │   │   │   └── tree-with-containers.svg
│   │   │   │   ├── reviewers-guide
│   │   │   │   │   ├── AppEngine-flow.svg
│   │   │   │   │   └── incbutton-in-action.png
│   │   │   │   ├── tools
│   │   │   │   │   └── boilerplate-structure.png
│   │   │   │   ├── try.svg
│   │   │   │   ├── tutorial
│   │   │   │   │   ├── app-chat-history.png
│   │   │   │   │   ├── app-content-placeholder.png
│   │   │   │   │   ├── app-header-and-content.png
│   │   │   │   │   ├── app-links-channel-selected.png
│   │   │   │   │   ├── app-links-click.png
│   │   │   │   │   ├── app-navigation.png
│   │   │   │   │   ├── finished-ex01.png
│   │   │   │   │   ├── finished-ex02.png
│   │   │   │   │   ├── hello.png
│   │   │   │   │   ├── splash-screen-advanced.png
│   │   │   │   │   ├── splash-screen-after-click.png
│   │   │   │   │   ├── splash-screen-centered.png
│   │   │   │   │   ├── splash-screen-events.png
│   │   │   │   │   ├── splash-screen-expression.png
│   │   │   │   │   ├── splash-screen-reuse-after.png
│   │   │   │   │   ├── splash-screen-reuse-before.png
│   │   │   │   │   └── splash-screen.png
│   │   │   │   └── tutorial-01.png
│   │   │   ├── llms.txt
│   │   │   ├── logo-dark.svg
│   │   │   ├── logo.svg
│   │   │   ├── pg-popout.svg
│   │   │   └── xmlui-logo.svg
│   │   ├── serve.json
│   │   └── web.config
│   ├── scripts
│   │   ├── download-latest-xmlui.js
│   │   ├── generate-rss.js
│   │   ├── get-releases.js
│   │   └── utils.js
│   ├── src
│   │   ├── components
│   │   │   ├── BlogOverview.xmlui
│   │   │   ├── BlogPage.xmlui
│   │   │   ├── Boxes.xmlui
│   │   │   ├── Breadcrumb.xmlui
│   │   │   ├── ChangeLog.xmlui
│   │   │   ├── ColorPalette.xmlui
│   │   │   ├── DocumentLinks.xmlui
│   │   │   ├── DocumentPage.xmlui
│   │   │   ├── DocumentPageNoTOC.xmlui
│   │   │   ├── Icons.xmlui
│   │   │   ├── IncButton.xmlui
│   │   │   ├── IncButton2.xmlui
│   │   │   ├── NameValue.xmlui
│   │   │   ├── PageNotFound.xmlui
│   │   │   ├── PaletteItem.xmlui
│   │   │   ├── Palettes.xmlui
│   │   │   ├── SectionHeader.xmlui
│   │   │   ├── TBD.xmlui
│   │   │   ├── Test.xmlui
│   │   │   ├── ThemesIntro.xmlui
│   │   │   ├── ThousandThemes.xmlui
│   │   │   ├── TubeStops.xmlui
│   │   │   ├── TubeStops.xmlui.xs
│   │   │   └── TwoColumnCode.xmlui
│   │   ├── config.ts
│   │   ├── Main.xmlui
│   │   └── themes
│   │       ├── docs-theme.ts
│   │       ├── earthtone.ts
│   │       ├── xmlui-gray-on-default.ts
│   │       ├── xmlui-green-on-default.ts
│   │       └── xmlui-orange-on-default.ts
│   └── tsconfig.json
├── LICENSE
├── package-lock.json
├── package.json
├── packages
│   ├── tsconfig.json
│   ├── xmlui-animations
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── demo
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── Animation.tsx
│   │       ├── AnimationNative.tsx
│   │       ├── FadeAnimation.tsx
│   │       ├── FadeInAnimation.tsx
│   │       ├── FadeOutAnimation.tsx
│   │       ├── index.tsx
│   │       ├── ScaleAnimation.tsx
│   │       └── SlideInAnimation.tsx
│   ├── xmlui-devtools
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── demo
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── devtools
│   │   │   │   ├── DevTools.tsx
│   │   │   │   ├── DevToolsNative.module.scss
│   │   │   │   ├── DevToolsNative.tsx
│   │   │   │   ├── ModalDialog.module.scss
│   │   │   │   ├── ModalDialog.tsx
│   │   │   │   ├── ModalVisibilityContext.tsx
│   │   │   │   ├── Tooltip.module.scss
│   │   │   │   ├── Tooltip.tsx
│   │   │   │   └── utils.ts
│   │   │   ├── editor
│   │   │   │   └── Editor.tsx
│   │   │   └── index.tsx
│   │   └── vite.config-overrides.ts
│   ├── xmlui-hello-world
│   │   ├── .gitignore
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── HelloWorld.module.scss
│   │       ├── HelloWorld.tsx
│   │       ├── HelloWorldNative.tsx
│   │       └── index.tsx
│   ├── xmlui-os-frames
│   │   ├── .gitignore
│   │   ├── demo
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── index.tsx
│   │       ├── IPhoneFrame.module.scss
│   │       ├── IPhoneFrame.tsx
│   │       ├── MacOSAppFrame.module.scss
│   │       ├── MacOSAppFrame.tsx
│   │       ├── WindowsAppFrame.module.scss
│   │       └── WindowsAppFrame.tsx
│   ├── xmlui-pdf
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── demo
│   │   │   ├── components
│   │   │   │   └── Pdf.xmlui
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── index.tsx
│   │       ├── LazyPdfNative.tsx
│   │       ├── Pdf.module.scss
│   │       └── Pdf.tsx
│   ├── xmlui-playground
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── demo
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── hooks
│   │       │   ├── usePlayground.ts
│   │       │   └── useToast.ts
│   │       ├── index.tsx
│   │       ├── playground
│   │       │   ├── Box.module.scss
│   │       │   ├── Box.tsx
│   │       │   ├── CodeSelector.module.scss
│   │       │   ├── CodeSelector.tsx
│   │       │   ├── ConfirmationDialog.module.scss
│   │       │   ├── ConfirmationDialog.tsx
│   │       │   ├── Editor.tsx
│   │       │   ├── Header.module.scss
│   │       │   ├── Header.tsx
│   │       │   ├── Playground.tsx
│   │       │   ├── PlaygroundContent.module.scss
│   │       │   ├── PlaygroundContent.tsx
│   │       │   ├── PlaygroundNative.module.scss
│   │       │   ├── PlaygroundNative.tsx
│   │       │   ├── Preview.tsx
│   │       │   ├── StandalonePlayground.tsx
│   │       │   ├── StandalonePlaygroundNative.module.scss
│   │       │   ├── StandalonePlaygroundNative.tsx
│   │       │   ├── ThemeSwitcher.module.scss
│   │       │   ├── ThemeSwitcher.tsx
│   │       │   └── utils.ts
│   │       ├── providers
│   │       │   ├── Toast.module.scss
│   │       │   └── ToastProvider.tsx
│   │       ├── state
│   │       │   └── store.ts
│   │       ├── themes
│   │       │   └── theme.ts
│   │       └── utils
│   │           └── helpers.ts
│   ├── xmlui-search
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── demo
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── index.tsx
│   │       ├── Search.module.scss
│   │       └── Search.tsx
│   ├── xmlui-spreadsheet
│   │   ├── .gitignore
│   │   ├── demo
│   │   │   └── Main.xmlui
│   │   ├── index.html
│   │   ├── index.ts
│   │   ├── meta
│   │   │   └── componentsMetadata.ts
│   │   ├── package.json
│   │   └── src
│   │       ├── index.tsx
│   │       ├── Spreadsheet.tsx
│   │       └── SpreadsheetNative.tsx
│   └── xmlui-website-blocks
│       ├── .gitignore
│       ├── CHANGELOG.md
│       ├── demo
│       │   ├── components
│       │   │   ├── HeroBackgroundBreakoutPage.xmlui
│       │   │   ├── HeroBackgroundsPage.xmlui
│       │   │   ├── HeroContentsPage.xmlui
│       │   │   ├── HeroTextAlignPage.xmlui
│       │   │   ├── HeroTextPage.xmlui
│       │   │   └── HeroTonesPage.xmlui
│       │   ├── Main.xmlui
│       │   └── themes
│       │       └── default.ts
│       ├── index.html
│       ├── index.ts
│       ├── meta
│       │   └── componentsMetadata.ts
│       ├── package.json
│       ├── public
│       │   └── resources
│       │       ├── building.jpg
│       │       └── xmlui-logo.svg
│       └── src
│           ├── Carousel
│           │   ├── Carousel.module.scss
│           │   ├── Carousel.tsx
│           │   ├── CarouselContext.tsx
│           │   └── CarouselNative.tsx
│           ├── FancyButton
│           │   ├── FancyButton.module.scss
│           │   ├── FancyButton.tsx
│           │   └── FancyButton.xmlui
│           ├── Hello
│           │   ├── Hello.tsx
│           │   ├── Hello.xmlui
│           │   └── Hello.xmlui.xs
│           ├── HeroSection
│           │   ├── HeroSection.module.scss
│           │   ├── HeroSection.spec.ts
│           │   ├── HeroSection.tsx
│           │   └── HeroSectionNative.tsx
│           ├── index.tsx
│           ├── ScrollToTop
│           │   ├── ScrollToTop.module.scss
│           │   ├── ScrollToTop.tsx
│           │   └── ScrollToTopNative.tsx
│           └── vite-env.d.ts
├── playwright.config.ts
├── README.md
├── tools
│   ├── codefence
│   │   └── xmlui-code-fence-docs.md
│   ├── create-app
│   │   ├── .gitignore
│   │   ├── CHANGELOG.md
│   │   ├── create-app.ts
│   │   ├── helpers
│   │   │   ├── copy.ts
│   │   │   ├── get-pkg-manager.ts
│   │   │   ├── git.ts
│   │   │   ├── install.ts
│   │   │   ├── is-folder-empty.ts
│   │   │   ├── is-writeable.ts
│   │   │   ├── make-dir.ts
│   │   │   └── validate-pkg.ts
│   │   ├── index.ts
│   │   ├── package.json
│   │   ├── templates
│   │   │   ├── default
│   │   │   │   └── ts
│   │   │   │       ├── gitignore
│   │   │   │       ├── index.html
│   │   │   │       ├── index.ts
│   │   │   │       ├── public
│   │   │   │       │   ├── mockServiceWorker.js
│   │   │   │       │   ├── resources
│   │   │   │       │   │   ├── favicon.ico
│   │   │   │       │   │   └── xmlui-logo.svg
│   │   │   │       │   └── serve.json
│   │   │   │       └── src
│   │   │   │           ├── components
│   │   │   │           │   ├── ApiAware.xmlui
│   │   │   │           │   ├── Home.xmlui
│   │   │   │           │   ├── IncButton.xmlui
│   │   │   │           │   └── PagePanel.xmlui
│   │   │   │           ├── config.ts
│   │   │   │           └── Main.xmlui
│   │   │   ├── index.ts
│   │   │   └── types.ts
│   │   └── tsconfig.json
│   ├── create-xmlui-hello-world
│   │   ├── index.js
│   │   └── package.json
│   └── vscode
│       ├── .gitignore
│       ├── .vscode
│       │   ├── launch.json
│       │   └── tasks.json
│       ├── .vscodeignore
│       ├── build.sh
│       ├── CHANGELOG.md
│       ├── esbuild.js
│       ├── eslint.config.mjs
│       ├── formatter-docs.md
│       ├── generate-test-sample.sh
│       ├── LICENSE.md
│       ├── package-lock.json
│       ├── package.json
│       ├── README.md
│       ├── resources
│       │   ├── xmlui-logo.png
│       │   └── xmlui-markup-syntax-highlighting.png
│       ├── src
│       │   ├── extension.ts
│       │   └── server.ts
│       ├── syntaxes
│       │   └── xmlui.tmLanguage.json
│       ├── test-samples
│       │   └── sample.xmlui
│       ├── tsconfig.json
│       └── tsconfig.tsbuildinfo
├── turbo.json
└── xmlui
    ├── .gitignore
    ├── bin
    │   ├── bootstrap.cjs
    │   ├── bootstrap.js
    │   ├── build-lib.ts
    │   ├── build.ts
    │   ├── index.ts
    │   ├── preview.ts
    │   ├── start.ts
    │   ├── vite-xmlui-plugin.ts
    │   └── viteConfig.ts
    ├── CHANGELOG.md
    ├── conventions
    │   ├── component-qa-checklist.md
    │   ├── copilot-conventions.md
    │   ├── create-xmlui-components.md
    │   ├── mermaid.md
    │   ├── testing-conventions.md
    │   └── xmlui-in-a-nutshell.md
    ├── dev-docs
    │   ├── accessibility.md
    │   ├── build-system.md
    │   ├── build-xmlui.md
    │   ├── component-behaviors.md
    │   ├── component-metadata.md
    │   ├── components-with-options.md
    │   ├── containers.md
    │   ├── data-operations.md
    │   ├── glossary.md
    │   ├── index.md
    │   ├── next
    │   │   ├── component-dev-guide.md
    │   │   ├── configuration-management-enhancement-summary.md
    │   │   ├── documentation-scripts-refactoring-complete-summary.md
    │   │   ├── documentation-scripts-refactoring-plan.md
    │   │   ├── duplicate-pattern-extraction-summary.md
    │   │   ├── error-handling-standardization-summary.md
    │   │   ├── generating-component-reference.md
    │   │   ├── index.md
    │   │   ├── logging-consistency-implementation-summary.md
    │   │   ├── project-build.md
    │   │   ├── project-structure.md
    │   │   ├── theme-context.md
    │   │   ├── tiptap-design-considerations.md
    │   │   ├── working-with-code.md
    │   │   ├── xmlui-runtime-architecture
    │   │   └── xmlui-wcag-accessibility-report.md
    │   ├── react-fundamentals.md
    │   ├── release-method.md
    │   ├── standalone-app.md
    │   ├── theme-variables-refactoring.md
    │   ├── ud-components.md
    │   └── xmlui-repo.md
    ├── package.json
    ├── scripts
    │   ├── coverage-only.js
    │   ├── e2e-test-summary.js
    │   ├── extract-component-metadata.js
    │   ├── generate-docs
    │   │   ├── build-downloads-map.mjs
    │   │   ├── build-pages-map.mjs
    │   │   ├── components-config.json
    │   │   ├── configuration-management.mjs
    │   │   ├── constants.mjs
    │   │   ├── create-theme-files.mjs
    │   │   ├── DocsGenerator.mjs
    │   │   ├── error-handling.mjs
    │   │   ├── extensions-config.json
    │   │   ├── folders.mjs
    │   │   ├── generate-summary-files.mjs
    │   │   ├── get-docs.mjs
    │   │   ├── input-handler.mjs
    │   │   ├── logger.mjs
    │   │   ├── logging-standards.mjs
    │   │   ├── MetadataProcessor.mjs
    │   │   ├── pattern-utilities.mjs
    │   │   └── utils.mjs
    │   ├── generate-metadata-markdown.js
    │   ├── get-langserver-metadata.js
    │   ├── inline-links.mjs
    │   └── README-e2e-summary.md
    ├── src
    │   ├── abstractions
    │   │   ├── _conventions.md
    │   │   ├── ActionDefs.ts
    │   │   ├── AppContextDefs.ts
    │   │   ├── ComponentDefs.ts
    │   │   ├── ContainerDefs.ts
    │   │   ├── ExtensionDefs.ts
    │   │   ├── FunctionDefs.ts
    │   │   ├── RendererDefs.ts
    │   │   ├── scripting
    │   │   │   ├── BlockScope.ts
    │   │   │   ├── Compilation.ts
    │   │   │   ├── LogicalThread.ts
    │   │   │   ├── LoopScope.ts
    │   │   │   ├── modules.ts
    │   │   │   ├── ScriptParserError.ts
    │   │   │   ├── Token.ts
    │   │   │   ├── TryScope.ts
    │   │   │   └── TryScopeExp.ts
    │   │   └── ThemingDefs.ts
    │   ├── components
    │   │   ├── _conventions.md
    │   │   ├── abstractions.ts
    │   │   ├── Accordion
    │   │   │   ├── Accordion.md
    │   │   │   ├── Accordion.module.scss
    │   │   │   ├── Accordion.spec.ts
    │   │   │   ├── Accordion.tsx
    │   │   │   ├── AccordionContext.tsx
    │   │   │   ├── AccordionItem.tsx
    │   │   │   ├── AccordionItemNative.tsx
    │   │   │   └── AccordionNative.tsx
    │   │   ├── Animation
    │   │   │   └── AnimationNative.tsx
    │   │   ├── APICall
    │   │   │   ├── APICall.md
    │   │   │   ├── APICall.spec.ts
    │   │   │   ├── APICall.tsx
    │   │   │   └── APICallNative.tsx
    │   │   ├── App
    │   │   │   ├── App.md
    │   │   │   ├── App.module.scss
    │   │   │   ├── App.spec.ts
    │   │   │   ├── App.tsx
    │   │   │   ├── AppLayoutContext.ts
    │   │   │   ├── AppNative.tsx
    │   │   │   ├── AppStateContext.ts
    │   │   │   ├── doc-resources
    │   │   │   │   ├── condensed-sticky.xmlui
    │   │   │   │   ├── condensed.xmlui
    │   │   │   │   ├── horizontal-sticky.xmlui
    │   │   │   │   ├── horizontal.xmlui
    │   │   │   │   ├── vertical-full-header.xmlui
    │   │   │   │   ├── vertical-sticky.xmlui
    │   │   │   │   └── vertical.xmlui
    │   │   │   ├── IndexerContext.ts
    │   │   │   ├── LinkInfoContext.ts
    │   │   │   ├── SearchContext.tsx
    │   │   │   ├── Sheet.module.scss
    │   │   │   └── Sheet.tsx
    │   │   ├── AppHeader
    │   │   │   ├── AppHeader.md
    │   │   │   ├── AppHeader.module.scss
    │   │   │   ├── AppHeader.spec.ts
    │   │   │   ├── AppHeader.tsx
    │   │   │   └── AppHeaderNative.tsx
    │   │   ├── AppState
    │   │   │   ├── AppState.md
    │   │   │   ├── AppState.spec.ts
    │   │   │   ├── AppState.tsx
    │   │   │   └── AppStateNative.tsx
    │   │   ├── AutoComplete
    │   │   │   ├── AutoComplete.md
    │   │   │   ├── AutoComplete.module.scss
    │   │   │   ├── AutoComplete.spec.ts
    │   │   │   ├── AutoComplete.tsx
    │   │   │   ├── AutoCompleteContext.tsx
    │   │   │   └── AutoCompleteNative.tsx
    │   │   ├── Avatar
    │   │   │   ├── Avatar.md
    │   │   │   ├── Avatar.module.scss
    │   │   │   ├── Avatar.spec.ts
    │   │   │   ├── Avatar.tsx
    │   │   │   └── AvatarNative.tsx
    │   │   ├── Backdrop
    │   │   │   ├── Backdrop.md
    │   │   │   ├── Backdrop.module.scss
    │   │   │   ├── Backdrop.spec.ts
    │   │   │   ├── Backdrop.tsx
    │   │   │   └── BackdropNative.tsx
    │   │   ├── Badge
    │   │   │   ├── Badge.md
    │   │   │   ├── Badge.module.scss
    │   │   │   ├── Badge.spec.ts
    │   │   │   ├── Badge.tsx
    │   │   │   └── BadgeNative.tsx
    │   │   ├── Bookmark
    │   │   │   ├── Bookmark.md
    │   │   │   ├── Bookmark.module.scss
    │   │   │   ├── Bookmark.spec.ts
    │   │   │   ├── Bookmark.tsx
    │   │   │   └── BookmarkNative.tsx
    │   │   ├── Breakout
    │   │   │   ├── Breakout.module.scss
    │   │   │   ├── Breakout.spec.ts
    │   │   │   ├── Breakout.tsx
    │   │   │   └── BreakoutNative.tsx
    │   │   ├── Button
    │   │   │   ├── Button-style.spec.ts
    │   │   │   ├── Button.md
    │   │   │   ├── Button.module.scss
    │   │   │   ├── Button.spec.ts
    │   │   │   ├── Button.tsx
    │   │   │   └── ButtonNative.tsx
    │   │   ├── Card
    │   │   │   ├── Card.md
    │   │   │   ├── Card.module.scss
    │   │   │   ├── Card.spec.ts
    │   │   │   ├── Card.tsx
    │   │   │   └── CardNative.tsx
    │   │   ├── Carousel
    │   │   │   ├── Carousel.md
    │   │   │   ├── Carousel.module.scss
    │   │   │   ├── Carousel.spec.ts
    │   │   │   ├── Carousel.tsx
    │   │   │   ├── CarouselContext.tsx
    │   │   │   ├── CarouselItem.tsx
    │   │   │   ├── CarouselItemNative.tsx
    │   │   │   └── CarouselNative.tsx
    │   │   ├── ChangeListener
    │   │   │   ├── ChangeListener.md
    │   │   │   ├── ChangeListener.spec.ts
    │   │   │   ├── ChangeListener.tsx
    │   │   │   └── ChangeListenerNative.tsx
    │   │   ├── chart-color-schemes.ts
    │   │   ├── Charts
    │   │   │   ├── AreaChart
    │   │   │   │   ├── AreaChart.md
    │   │   │   │   ├── AreaChart.spec.ts
    │   │   │   │   ├── AreaChart.tsx
    │   │   │   │   └── AreaChartNative.tsx
    │   │   │   ├── BarChart
    │   │   │   │   ├── BarChart.md
    │   │   │   │   ├── BarChart.module.scss
    │   │   │   │   ├── BarChart.spec.ts
    │   │   │   │   ├── BarChart.tsx
    │   │   │   │   └── BarChartNative.tsx
    │   │   │   ├── DonutChart
    │   │   │   │   ├── DonutChart.spec.ts
    │   │   │   │   └── DonutChart.tsx
    │   │   │   ├── LabelList
    │   │   │   │   ├── LabelList.module.scss
    │   │   │   │   ├── LabelList.spec.ts
    │   │   │   │   ├── LabelList.tsx
    │   │   │   │   └── LabelListNative.tsx
    │   │   │   ├── Legend
    │   │   │   │   ├── Legend.spec.ts
    │   │   │   │   ├── Legend.tsx
    │   │   │   │   └── LegendNative.tsx
    │   │   │   ├── LineChart
    │   │   │   │   ├── LineChart.md
    │   │   │   │   ├── LineChart.module.scss
    │   │   │   │   ├── LineChart.spec.ts
    │   │   │   │   ├── LineChart.tsx
    │   │   │   │   └── LineChartNative.tsx
    │   │   │   ├── PieChart
    │   │   │   │   ├── PieChart.md
    │   │   │   │   ├── PieChart.spec.ts
    │   │   │   │   ├── PieChart.tsx
    │   │   │   │   ├── PieChartNative.module.scss
    │   │   │   │   └── PieChartNative.tsx
    │   │   │   ├── RadarChart
    │   │   │   │   ├── RadarChart.md
    │   │   │   │   ├── RadarChart.spec.ts
    │   │   │   │   ├── RadarChart.tsx
    │   │   │   │   └── RadarChartNative.tsx
    │   │   │   ├── Tooltip
    │   │   │   │   ├── TooltipContent.module.scss
    │   │   │   │   ├── TooltipContent.spec.ts
    │   │   │   │   └── TooltipContent.tsx
    │   │   │   └── utils
    │   │   │       ├── abstractions.ts
    │   │   │       └── ChartProvider.tsx
    │   │   ├── Checkbox
    │   │   │   ├── Checkbox.md
    │   │   │   ├── Checkbox.spec.ts
    │   │   │   └── Checkbox.tsx
    │   │   ├── CodeBlock
    │   │   │   ├── CodeBlock.module.scss
    │   │   │   ├── CodeBlock.spec.ts
    │   │   │   ├── CodeBlock.tsx
    │   │   │   ├── CodeBlockNative.tsx
    │   │   │   └── highlight-code.ts
    │   │   ├── collectedComponentMetadata.ts
    │   │   ├── ColorPicker
    │   │   │   ├── ColorPicker.md
    │   │   │   ├── ColorPicker.module.scss
    │   │   │   ├── ColorPicker.spec.ts
    │   │   │   ├── ColorPicker.tsx
    │   │   │   └── ColorPickerNative.tsx
    │   │   ├── Column
    │   │   │   ├── Column.md
    │   │   │   ├── Column.tsx
    │   │   │   ├── ColumnNative.tsx
    │   │   │   ├── doc-resources
    │   │   │   │   └── list-component-data.js
    │   │   │   └── TableContext.tsx
    │   │   ├── component-utils.ts
    │   │   ├── ComponentProvider.tsx
    │   │   ├── ComponentRegistryContext.tsx
    │   │   ├── container-helpers.tsx
    │   │   ├── ContentSeparator
    │   │   │   ├── ContentSeparator.md
    │   │   │   ├── ContentSeparator.module.scss
    │   │   │   ├── ContentSeparator.spec.ts
    │   │   │   ├── ContentSeparator.tsx
    │   │   │   ├── ContentSeparatorNative.tsx
    │   │   │   └── test-padding.xmlui
    │   │   ├── DataSource
    │   │   │   ├── DataSource.md
    │   │   │   └── DataSource.tsx
    │   │   ├── DateInput
    │   │   │   ├── DateInput.md
    │   │   │   ├── DateInput.module.scss
    │   │   │   ├── DateInput.spec.ts
    │   │   │   ├── DateInput.tsx
    │   │   │   └── DateInputNative.tsx
    │   │   ├── DatePicker
    │   │   │   ├── DatePicker.md
    │   │   │   ├── DatePicker.module.scss
    │   │   │   ├── DatePicker.spec.ts
    │   │   │   ├── DatePicker.tsx
    │   │   │   └── DatePickerNative.tsx
    │   │   ├── DropdownMenu
    │   │   │   ├── DropdownMenu.md
    │   │   │   ├── DropdownMenu.module.scss
    │   │   │   ├── DropdownMenu.spec.ts
    │   │   │   ├── DropdownMenu.tsx
    │   │   │   ├── DropdownMenuNative.tsx
    │   │   │   ├── MenuItem.md
    │   │   │   └── SubMenuItem.md
    │   │   ├── EmojiSelector
    │   │   │   ├── EmojiSelector.md
    │   │   │   ├── EmojiSelector.spec.ts
    │   │   │   ├── EmojiSelector.tsx
    │   │   │   └── EmojiSelectorNative.tsx
    │   │   ├── ExpandableItem
    │   │   │   ├── ExpandableItem.module.scss
    │   │   │   ├── ExpandableItem.spec.ts
    │   │   │   ├── ExpandableItem.tsx
    │   │   │   └── ExpandableItemNative.tsx
    │   │   ├── FileInput
    │   │   │   ├── FileInput.md
    │   │   │   ├── FileInput.module.scss
    │   │   │   ├── FileInput.spec.ts
    │   │   │   ├── FileInput.tsx
    │   │   │   └── FileInputNative.tsx
    │   │   ├── FileUploadDropZone
    │   │   │   ├── FileUploadDropZone.md
    │   │   │   ├── FileUploadDropZone.module.scss
    │   │   │   ├── FileUploadDropZone.spec.ts
    │   │   │   ├── FileUploadDropZone.tsx
    │   │   │   └── FileUploadDropZoneNative.tsx
    │   │   ├── FlowLayout
    │   │   │   ├── FlowLayout.md
    │   │   │   ├── FlowLayout.module.scss
    │   │   │   ├── FlowLayout.spec.ts
    │   │   │   ├── FlowLayout.spec.ts-snapshots
    │   │   │   │   └── Edge-cases-boxShadow-is-not-clipped-1-non-smoke-darwin.png
    │   │   │   ├── FlowLayout.tsx
    │   │   │   └── FlowLayoutNative.tsx
    │   │   ├── Footer
    │   │   │   ├── Footer.md
    │   │   │   ├── Footer.module.scss
    │   │   │   ├── Footer.spec.ts
    │   │   │   ├── Footer.tsx
    │   │   │   └── FooterNative.tsx
    │   │   ├── Form
    │   │   │   ├── Form.md
    │   │   │   ├── Form.module.scss
    │   │   │   ├── Form.spec.ts
    │   │   │   ├── Form.tsx
    │   │   │   ├── formActions.ts
    │   │   │   ├── FormContext.ts
    │   │   │   └── FormNative.tsx
    │   │   ├── FormItem
    │   │   │   ├── FormItem.md
    │   │   │   ├── FormItem.module.scss
    │   │   │   ├── FormItem.spec.ts
    │   │   │   ├── FormItem.tsx
    │   │   │   ├── FormItemNative.tsx
    │   │   │   ├── HelperText.module.scss
    │   │   │   ├── HelperText.tsx
    │   │   │   ├── ItemWithLabel.tsx
    │   │   │   └── Validations.ts
    │   │   ├── FormSection
    │   │   │   ├── FormSection.md
    │   │   │   ├── FormSection.ts
    │   │   │   └── FormSection.xmlui
    │   │   ├── Fragment
    │   │   │   ├── Fragment.spec.ts
    │   │   │   └── Fragment.tsx
    │   │   ├── Heading
    │   │   │   ├── abstractions.ts
    │   │   │   ├── H1.md
    │   │   │   ├── H1.spec.ts
    │   │   │   ├── H2.md
    │   │   │   ├── H2.spec.ts
    │   │   │   ├── H3.md
    │   │   │   ├── H3.spec.ts
    │   │   │   ├── H4.md
    │   │   │   ├── H4.spec.ts
    │   │   │   ├── H5.md
    │   │   │   ├── H5.spec.ts
    │   │   │   ├── H6.md
    │   │   │   ├── H6.spec.ts
    │   │   │   ├── Heading.md
    │   │   │   ├── Heading.module.scss
    │   │   │   ├── Heading.spec.ts
    │   │   │   ├── Heading.tsx
    │   │   │   └── HeadingNative.tsx
    │   │   ├── HoverCard
    │   │   │   ├── HoverCard.tsx
    │   │   │   └── HovercardNative.tsx
    │   │   ├── HtmlTags
    │   │   │   ├── HtmlTags.module.scss
    │   │   │   ├── HtmlTags.spec.ts
    │   │   │   └── HtmlTags.tsx
    │   │   ├── Icon
    │   │   │   ├── AdmonitionDanger.tsx
    │   │   │   ├── AdmonitionInfo.tsx
    │   │   │   ├── AdmonitionNote.tsx
    │   │   │   ├── AdmonitionTip.tsx
    │   │   │   ├── AdmonitionWarning.tsx
    │   │   │   ├── ApiIcon.tsx
    │   │   │   ├── ArrowDropDown.module.scss
    │   │   │   ├── ArrowDropDown.tsx
    │   │   │   ├── ArrowDropUp.module.scss
    │   │   │   ├── ArrowDropUp.tsx
    │   │   │   ├── ArrowLeft.module.scss
    │   │   │   ├── ArrowLeft.tsx
    │   │   │   ├── ArrowRight.module.scss
    │   │   │   ├── ArrowRight.tsx
    │   │   │   ├── Attach.tsx
    │   │   │   ├── Binding.module.scss
    │   │   │   ├── Binding.tsx
    │   │   │   ├── BoardIcon.tsx
    │   │   │   ├── BoxIcon.tsx
    │   │   │   ├── CheckIcon.tsx
    │   │   │   ├── ChevronDownIcon.tsx
    │   │   │   ├── ChevronLeft.tsx
    │   │   │   ├── ChevronRight.tsx
    │   │   │   ├── ChevronUpIcon.tsx
    │   │   │   ├── CodeFileIcon.tsx
    │   │   │   ├── CodeSandbox.tsx
    │   │   │   ├── CompactListIcon.tsx
    │   │   │   ├── ContentCopyIcon.tsx
    │   │   │   ├── DarkToLightIcon.tsx
    │   │   │   ├── DatabaseIcon.module.scss
    │   │   │   ├── DatabaseIcon.tsx
    │   │   │   ├── DocFileIcon.tsx
    │   │   │   ├── DocIcon.tsx
    │   │   │   ├── DotMenuHorizontalIcon.tsx
    │   │   │   ├── DotMenuIcon.tsx
    │   │   │   ├── EmailIcon.tsx
    │   │   │   ├── EmptyFolderIcon.tsx
    │   │   │   ├── ErrorIcon.tsx
    │   │   │   ├── ExpressionIcon.tsx
    │   │   │   ├── FillPlusCricleIcon.tsx
    │   │   │   ├── FilterIcon.tsx
    │   │   │   ├── FolderIcon.tsx
    │   │   │   ├── GlobeIcon.tsx
    │   │   │   ├── HomeIcon.tsx
    │   │   │   ├── HyperLinkIcon.tsx
    │   │   │   ├── Icon.md
    │   │   │   ├── Icon.module.scss
    │   │   │   ├── Icon.spec.ts
    │   │   │   ├── Icon.tsx
    │   │   │   ├── IconNative.tsx
    │   │   │   ├── ImageFileIcon.tsx
    │   │   │   ├── Inspect.tsx
    │   │   │   ├── LightToDark.tsx
    │   │   │   ├── LinkIcon.tsx
    │   │   │   ├── ListIcon.tsx
    │   │   │   ├── LooseListIcon.tsx
    │   │   │   ├── MoonIcon.tsx
    │   │   │   ├── MoreOptionsIcon.tsx
    │   │   │   ├── NoSortIcon.tsx
    │   │   │   ├── PDFIcon.tsx
    │   │   │   ├── PenIcon.tsx
    │   │   │   ├── PhoneIcon.tsx
    │   │   │   ├── PhotoIcon.tsx
    │   │   │   ├── PlusIcon.tsx
    │   │   │   ├── SearchIcon.tsx
    │   │   │   ├── ShareIcon.tsx
    │   │   │   ├── SortAscendingIcon.tsx
    │   │   │   ├── SortDescendingIcon.tsx
    │   │   │   ├── StarsIcon.tsx
    │   │   │   ├── SunIcon.tsx
    │   │   │   ├── svg
    │   │   │   │   ├── admonition_danger.svg
    │   │   │   │   ├── admonition_info.svg
    │   │   │   │   ├── admonition_note.svg
    │   │   │   │   ├── admonition_tip.svg
    │   │   │   │   ├── admonition_warning.svg
    │   │   │   │   ├── api.svg
    │   │   │   │   ├── arrow-dropdown.svg
    │   │   │   │   ├── arrow-left.svg
    │   │   │   │   ├── arrow-right.svg
    │   │   │   │   ├── arrow-up.svg
    │   │   │   │   ├── attach.svg
    │   │   │   │   ├── binding.svg
    │   │   │   │   ├── box.svg
    │   │   │   │   ├── bulb.svg
    │   │   │   │   ├── code-file.svg
    │   │   │   │   ├── code-sandbox.svg
    │   │   │   │   ├── dark_to_light.svg
    │   │   │   │   ├── database.svg
    │   │   │   │   ├── doc.svg
    │   │   │   │   ├── empty-folder.svg
    │   │   │   │   ├── expression.svg
    │   │   │   │   ├── eye-closed.svg
    │   │   │   │   ├── eye-dark.svg
    │   │   │   │   ├── eye.svg
    │   │   │   │   ├── file-text.svg
    │   │   │   │   ├── filter.svg
    │   │   │   │   ├── folder.svg
    │   │   │   │   ├── img.svg
    │   │   │   │   ├── inspect.svg
    │   │   │   │   ├── light_to_dark.svg
    │   │   │   │   ├── moon.svg
    │   │   │   │   ├── pdf.svg
    │   │   │   │   ├── photo.svg
    │   │   │   │   ├── share.svg
    │   │   │   │   ├── stars.svg
    │   │   │   │   ├── sun.svg
    │   │   │   │   ├── trending-down.svg
    │   │   │   │   ├── trending-level.svg
    │   │   │   │   ├── trending-up.svg
    │   │   │   │   ├── txt.svg
    │   │   │   │   ├── unknown-file.svg
    │   │   │   │   ├── unlink.svg
    │   │   │   │   └── xls.svg
    │   │   │   ├── TableDeleteColumnIcon.tsx
    │   │   │   ├── TableDeleteRowIcon.tsx
    │   │   │   ├── TableInsertColumnIcon.tsx
    │   │   │   ├── TableInsertRowIcon.tsx
    │   │   │   ├── TrashIcon.tsx
    │   │   │   ├── TrendingDownIcon.tsx
    │   │   │   ├── TrendingLevelIcon.tsx
    │   │   │   ├── TrendingUpIcon.tsx
    │   │   │   ├── TxtIcon.tsx
    │   │   │   ├── UnknownFileIcon.tsx
    │   │   │   ├── UnlinkIcon.tsx
    │   │   │   ├── UserIcon.tsx
    │   │   │   ├── WarningIcon.tsx
    │   │   │   └── XlsIcon.tsx
    │   │   ├── IconProvider.tsx
    │   │   ├── IconRegistryContext.tsx
    │   │   ├── IFrame
    │   │   │   ├── IFrame.md
    │   │   │   ├── IFrame.module.scss
    │   │   │   ├── IFrame.spec.ts
    │   │   │   ├── IFrame.tsx
    │   │   │   └── IFrameNative.tsx
    │   │   ├── Image
    │   │   │   ├── Image.md
    │   │   │   ├── Image.module.scss
    │   │   │   ├── Image.spec.ts
    │   │   │   ├── Image.tsx
    │   │   │   └── ImageNative.tsx
    │   │   ├── Input
    │   │   │   ├── index.ts
    │   │   │   ├── InputAdornment.module.scss
    │   │   │   ├── InputAdornment.tsx
    │   │   │   ├── InputDivider.module.scss
    │   │   │   ├── InputDivider.tsx
    │   │   │   ├── InputLabel.module.scss
    │   │   │   ├── InputLabel.tsx
    │   │   │   ├── PartialInput.module.scss
    │   │   │   └── PartialInput.tsx
    │   │   ├── InspectButton
    │   │   │   ├── InspectButton.module.scss
    │   │   │   └── InspectButton.tsx
    │   │   ├── Items
    │   │   │   ├── Items.md
    │   │   │   ├── Items.spec.ts
    │   │   │   ├── Items.tsx
    │   │   │   └── ItemsNative.tsx
    │   │   ├── Link
    │   │   │   ├── Link.md
    │   │   │   ├── Link.module.scss
    │   │   │   ├── Link.spec.ts
    │   │   │   ├── Link.tsx
    │   │   │   └── LinkNative.tsx
    │   │   ├── List
    │   │   │   ├── doc-resources
    │   │   │   │   └── list-component-data.js
    │   │   │   ├── List.md
    │   │   │   ├── List.module.scss
    │   │   │   ├── List.spec.ts
    │   │   │   ├── List.tsx
    │   │   │   └── ListNative.tsx
    │   │   ├── Logo
    │   │   │   ├── doc-resources
    │   │   │   │   └── xmlui-logo.svg
    │   │   │   ├── Logo.md
    │   │   │   ├── Logo.tsx
    │   │   │   └── LogoNative.tsx
    │   │   ├── Markdown
    │   │   │   ├── CodeText.module.scss
    │   │   │   ├── CodeText.tsx
    │   │   │   ├── Markdown.md
    │   │   │   ├── Markdown.module.scss
    │   │   │   ├── Markdown.spec.ts
    │   │   │   ├── Markdown.tsx
    │   │   │   ├── MarkdownNative.tsx
    │   │   │   ├── parse-binding-expr.ts
    │   │   │   └── utils.ts
    │   │   ├── metadata-helpers.ts
    │   │   ├── ModalDialog
    │   │   │   ├── ConfirmationModalContextProvider.tsx
    │   │   │   ├── Dialog.module.scss
    │   │   │   ├── Dialog.tsx
    │   │   │   ├── ModalDialog.md
    │   │   │   ├── ModalDialog.module.scss
    │   │   │   ├── ModalDialog.spec.ts
    │   │   │   ├── ModalDialog.tsx
    │   │   │   ├── ModalDialogNative.tsx
    │   │   │   └── ModalVisibilityContext.tsx
    │   │   ├── NavGroup
    │   │   │   ├── NavGroup.md
    │   │   │   ├── NavGroup.module.scss
    │   │   │   ├── NavGroup.spec.ts
    │   │   │   ├── NavGroup.tsx
    │   │   │   ├── NavGroupContext.ts
    │   │   │   └── NavGroupNative.tsx
    │   │   ├── NavLink
    │   │   │   ├── NavLink.md
    │   │   │   ├── NavLink.module.scss
    │   │   │   ├── NavLink.spec.ts
    │   │   │   ├── NavLink.tsx
    │   │   │   └── NavLinkNative.tsx
    │   │   ├── NavPanel
    │   │   │   ├── NavPanel.md
    │   │   │   ├── NavPanel.module.scss
    │   │   │   ├── NavPanel.spec.ts
    │   │   │   ├── NavPanel.tsx
    │   │   │   └── NavPanelNative.tsx
    │   │   ├── NestedApp
    │   │   │   ├── AppWithCodeView.module.scss
    │   │   │   ├── AppWithCodeView.tsx
    │   │   │   ├── AppWithCodeViewNative.tsx
    │   │   │   ├── defaultProps.tsx
    │   │   │   ├── logo.svg
    │   │   │   ├── NestedApp.module.scss
    │   │   │   ├── NestedApp.tsx
    │   │   │   ├── NestedAppNative.tsx
    │   │   │   ├── Tooltip.module.scss
    │   │   │   ├── Tooltip.tsx
    │   │   │   └── utils.ts
    │   │   ├── NoResult
    │   │   │   ├── NoResult.md
    │   │   │   ├── NoResult.module.scss
    │   │   │   ├── NoResult.spec.ts
    │   │   │   ├── NoResult.tsx
    │   │   │   └── NoResultNative.tsx
    │   │   ├── NumberBox
    │   │   │   ├── numberbox-abstractions.ts
    │   │   │   ├── NumberBox.md
    │   │   │   ├── NumberBox.module.scss
    │   │   │   ├── NumberBox.spec.ts
    │   │   │   ├── NumberBox.tsx
    │   │   │   └── NumberBoxNative.tsx
    │   │   ├── Option
    │   │   │   ├── Option.md
    │   │   │   ├── Option.spec.ts
    │   │   │   ├── Option.tsx
    │   │   │   ├── OptionNative.tsx
    │   │   │   └── OptionTypeProvider.tsx
    │   │   ├── PageMetaTitle
    │   │   │   ├── PageMetaTilteNative.tsx
    │   │   │   ├── PageMetaTitle.md
    │   │   │   ├── PageMetaTitle.spec.ts
    │   │   │   └── PageMetaTitle.tsx
    │   │   ├── Pages
    │   │   │   ├── Page.md
    │   │   │   ├── Pages.md
    │   │   │   ├── Pages.module.scss
    │   │   │   ├── Pages.tsx
    │   │   │   └── PagesNative.tsx
    │   │   ├── Pagination
    │   │   │   ├── Pagination.md
    │   │   │   ├── Pagination.module.scss
    │   │   │   ├── Pagination.spec.ts
    │   │   │   ├── Pagination.tsx
    │   │   │   └── PaginationNative.tsx
    │   │   ├── PositionedContainer
    │   │   │   ├── PositionedContainer.module.scss
    │   │   │   ├── PositionedContainer.tsx
    │   │   │   └── PositionedContainerNative.tsx
    │   │   ├── ProfileMenu
    │   │   │   ├── ProfileMenu.module.scss
    │   │   │   └── ProfileMenu.tsx
    │   │   ├── ProgressBar
    │   │   │   ├── ProgressBar.md
    │   │   │   ├── ProgressBar.module.scss
    │   │   │   ├── ProgressBar.spec.ts
    │   │   │   ├── ProgressBar.tsx
    │   │   │   └── ProgressBarNative.tsx
    │   │   ├── Queue
    │   │   │   ├── Queue.md
    │   │   │   ├── Queue.spec.ts
    │   │   │   ├── Queue.tsx
    │   │   │   ├── queueActions.ts
    │   │   │   └── QueueNative.tsx
    │   │   ├── RadioGroup
    │   │   │   ├── RadioGroup.md
    │   │   │   ├── RadioGroup.module.scss
    │   │   │   ├── RadioGroup.spec.ts
    │   │   │   ├── RadioGroup.tsx
    │   │   │   ├── RadioGroupNative.tsx
    │   │   │   ├── RadioItem.tsx
    │   │   │   └── RadioItemNative.tsx
    │   │   ├── RealTimeAdapter
    │   │   │   ├── RealTimeAdapter.tsx
    │   │   │   └── RealTimeAdapterNative.tsx
    │   │   ├── Redirect
    │   │   │   ├── Redirect.md
    │   │   │   ├── Redirect.spec.ts
    │   │   │   └── Redirect.tsx
    │   │   ├── ResponsiveBar
    │   │   │   ├── README.md
    │   │   │   ├── ResponsiveBar.md
    │   │   │   ├── ResponsiveBar.module.scss
    │   │   │   ├── ResponsiveBar.spec.ts
    │   │   │   ├── ResponsiveBar.tsx
    │   │   │   └── ResponsiveBarNative.tsx
    │   │   ├── Select
    │   │   │   ├── HiddenOption.tsx
    │   │   │   ├── OptionContext.ts
    │   │   │   ├── Select.md
    │   │   │   ├── Select.module.scss
    │   │   │   ├── Select.spec.ts
    │   │   │   ├── Select.tsx
    │   │   │   ├── SelectContext.tsx
    │   │   │   └── SelectNative.tsx
    │   │   ├── SelectionStore
    │   │   │   ├── SelectionStore.md
    │   │   │   ├── SelectionStore.tsx
    │   │   │   └── SelectionStoreNative.tsx
    │   │   ├── Slider
    │   │   │   ├── Slider.md
    │   │   │   ├── Slider.module.scss
    │   │   │   ├── Slider.spec.ts
    │   │   │   ├── Slider.tsx
    │   │   │   └── SliderNative.tsx
    │   │   ├── Slot
    │   │   │   ├── Slot.md
    │   │   │   ├── Slot.spec.ts
    │   │   │   └── Slot.ts
    │   │   ├── SlotItem.tsx
    │   │   ├── SpaceFiller
    │   │   │   ├── SpaceFiller.md
    │   │   │   ├── SpaceFiller.module.scss
    │   │   │   ├── SpaceFiller.spec.ts
    │   │   │   ├── SpaceFiller.tsx
    │   │   │   └── SpaceFillerNative.tsx
    │   │   ├── Spinner
    │   │   │   ├── Spinner.md
    │   │   │   ├── Spinner.module.scss
    │   │   │   ├── Spinner.spec.ts
    │   │   │   ├── Spinner.tsx
    │   │   │   └── SpinnerNative.tsx
    │   │   ├── Splitter
    │   │   │   ├── HSplitter.md
    │   │   │   ├── HSplitter.spec.ts
    │   │   │   ├── Splitter.md
    │   │   │   ├── Splitter.module.scss
    │   │   │   ├── Splitter.spec.ts
    │   │   │   ├── Splitter.tsx
    │   │   │   ├── SplitterNative.tsx
    │   │   │   ├── utils.ts
    │   │   │   ├── VSplitter.md
    │   │   │   └── VSplitter.spec.ts
    │   │   ├── Stack
    │   │   │   ├── CHStack.md
    │   │   │   ├── CHStack.spec.ts
    │   │   │   ├── CVStack.md
    │   │   │   ├── CVStack.spec.ts
    │   │   │   ├── HStack.md
    │   │   │   ├── HStack.spec.ts
    │   │   │   ├── Stack.md
    │   │   │   ├── Stack.module.scss
    │   │   │   ├── Stack.spec.ts
    │   │   │   ├── Stack.tsx
    │   │   │   ├── StackNative.tsx
    │   │   │   ├── VStack.md
    │   │   │   └── VStack.spec.ts
    │   │   ├── StickyBox
    │   │   │   ├── StickyBox.md
    │   │   │   ├── StickyBox.module.scss
    │   │   │   ├── StickyBox.tsx
    │   │   │   └── StickyBoxNative.tsx
    │   │   ├── Switch
    │   │   │   ├── Switch.md
    │   │   │   ├── Switch.spec.ts
    │   │   │   └── Switch.tsx
    │   │   ├── Table
    │   │   │   ├── doc-resources
    │   │   │   │   └── list-component-data.js
    │   │   │   ├── react-table-config.d.ts
    │   │   │   ├── Table.md
    │   │   │   ├── Table.module.scss
    │   │   │   ├── Table.spec.ts
    │   │   │   ├── Table.tsx
    │   │   │   ├── TableNative.tsx
    │   │   │   └── useRowSelection.tsx
    │   │   ├── TableOfContents
    │   │   │   ├── TableOfContents.module.scss
    │   │   │   ├── TableOfContents.spec.ts
    │   │   │   ├── TableOfContents.tsx
    │   │   │   └── TableOfContentsNative.tsx
    │   │   ├── Tabs
    │   │   │   ├── TabContext.tsx
    │   │   │   ├── TabItem.md
    │   │   │   ├── TabItem.tsx
    │   │   │   ├── TabItemNative.tsx
    │   │   │   ├── Tabs.md
    │   │   │   ├── Tabs.module.scss
    │   │   │   ├── Tabs.spec.ts
    │   │   │   ├── Tabs.tsx
    │   │   │   └── TabsNative.tsx
    │   │   ├── Text
    │   │   │   ├── Text.md
    │   │   │   ├── Text.module.scss
    │   │   │   ├── Text.spec.ts
    │   │   │   ├── Text.tsx
    │   │   │   └── TextNative.tsx
    │   │   ├── TextArea
    │   │   │   ├── TextArea.md
    │   │   │   ├── TextArea.module.scss
    │   │   │   ├── TextArea.spec.ts
    │   │   │   ├── TextArea.tsx
    │   │   │   ├── TextAreaNative.tsx
    │   │   │   ├── TextAreaResizable.tsx
    │   │   │   └── useComposedRef.ts
    │   │   ├── TextBox
    │   │   │   ├── TextBox.md
    │   │   │   ├── TextBox.module.scss
    │   │   │   ├── TextBox.spec.ts
    │   │   │   ├── TextBox.tsx
    │   │   │   └── TextBoxNative.tsx
    │   │   ├── Theme
    │   │   │   ├── NotificationToast.tsx
    │   │   │   ├── Theme.md
    │   │   │   ├── Theme.module.scss
    │   │   │   ├── Theme.spec.ts
    │   │   │   ├── Theme.tsx
    │   │   │   └── ThemeNative.tsx
    │   │   ├── TimeInput
    │   │   │   ├── TimeInput.md
    │   │   │   ├── TimeInput.module.scss
    │   │   │   ├── TimeInput.spec.ts
    │   │   │   ├── TimeInput.tsx
    │   │   │   ├── TimeInputNative.tsx
    │   │   │   └── utils.ts
    │   │   ├── Timer
    │   │   │   ├── Timer.md
    │   │   │   ├── Timer.spec.ts
    │   │   │   ├── Timer.tsx
    │   │   │   └── TimerNative.tsx
    │   │   ├── Toggle
    │   │   │   ├── Toggle.module.scss
    │   │   │   └── Toggle.tsx
    │   │   ├── ToneChangerButton
    │   │   │   ├── ToneChangerButton.md
    │   │   │   ├── ToneChangerButton.spec.ts
    │   │   │   └── ToneChangerButton.tsx
    │   │   ├── ToneSwitch
    │   │   │   ├── ToneSwitch.md
    │   │   │   ├── ToneSwitch.module.scss
    │   │   │   ├── ToneSwitch.spec.ts
    │   │   │   ├── ToneSwitch.tsx
    │   │   │   └── ToneSwitchNative.tsx
    │   │   ├── Tooltip
    │   │   │   ├── Tooltip.md
    │   │   │   ├── Tooltip.module.scss
    │   │   │   ├── Tooltip.spec.ts
    │   │   │   ├── Tooltip.tsx
    │   │   │   └── TooltipNative.tsx
    │   │   ├── Tree
    │   │   │   ├── testData.ts
    │   │   │   ├── Tree-dynamic.spec.ts
    │   │   │   ├── Tree-icons.spec.ts
    │   │   │   ├── Tree.md
    │   │   │   ├── Tree.spec.ts
    │   │   │   ├── TreeComponent.module.scss
    │   │   │   ├── TreeComponent.tsx
    │   │   │   └── TreeNative.tsx
    │   │   ├── TreeDisplay
    │   │   │   ├── TreeDisplay.md
    │   │   │   ├── TreeDisplay.module.scss
    │   │   │   ├── TreeDisplay.tsx
    │   │   │   └── TreeDisplayNative.tsx
    │   │   ├── ValidationSummary
    │   │   │   ├── ValidationSummary.module.scss
    │   │   │   └── ValidationSummary.tsx
    │   │   └── VisuallyHidden.tsx
    │   ├── components-core
    │   │   ├── abstractions
    │   │   │   ├── ComponentRenderer.ts
    │   │   │   ├── LoaderRenderer.ts
    │   │   │   ├── standalone.ts
    │   │   │   └── treeAbstractions.ts
    │   │   ├── action
    │   │   │   ├── actions.ts
    │   │   │   ├── APICall.tsx
    │   │   │   ├── FileDownloadAction.tsx
    │   │   │   ├── FileUploadAction.tsx
    │   │   │   ├── NavigateAction.tsx
    │   │   │   └── TimedAction.tsx
    │   │   ├── ApiBoundComponent.tsx
    │   │   ├── appContext
    │   │   │   ├── date-functions.ts
    │   │   │   ├── math-function.ts
    │   │   │   └── misc-utils.ts
    │   │   ├── AppContext.tsx
    │   │   ├── behaviors
    │   │   │   ├── Behavior.tsx
    │   │   │   └── CoreBehaviors.tsx
    │   │   ├── component-hooks.ts
    │   │   ├── ComponentDecorator.tsx
    │   │   ├── ComponentViewer.tsx
    │   │   ├── CompoundComponent.tsx
    │   │   ├── constants.ts
    │   │   ├── DebugViewProvider.tsx
    │   │   ├── descriptorHelper.ts
    │   │   ├── devtools
    │   │   │   ├── InspectorDialog.module.scss
    │   │   │   ├── InspectorDialog.tsx
    │   │   │   └── InspectorDialogVisibilityContext.tsx
    │   │   ├── EngineError.ts
    │   │   ├── event-handlers.ts
    │   │   ├── InspectorButton.module.scss
    │   │   ├── InspectorContext.tsx
    │   │   ├── interception
    │   │   │   ├── abstractions.ts
    │   │   │   ├── ApiInterceptor.ts
    │   │   │   ├── ApiInterceptorProvider.tsx
    │   │   │   ├── apiInterceptorWorker.ts
    │   │   │   ├── Backend.ts
    │   │   │   ├── Errors.ts
    │   │   │   ├── IndexedDb.ts
    │   │   │   ├── initMock.ts
    │   │   │   ├── InMemoryDb.ts
    │   │   │   ├── ReadonlyCollection.ts
    │   │   │   └── useApiInterceptorContext.tsx
    │   │   ├── loader
    │   │   │   ├── ApiLoader.tsx
    │   │   │   ├── DataLoader.tsx
    │   │   │   ├── ExternalDataLoader.tsx
    │   │   │   ├── Loader.tsx
    │   │   │   ├── MockLoaderRenderer.tsx
    │   │   │   └── PageableLoader.tsx
    │   │   ├── LoaderComponent.tsx
    │   │   ├── markup-check.ts
    │   │   ├── parts.ts
    │   │   ├── renderers.ts
    │   │   ├── rendering
    │   │   │   ├── AppContent.tsx
    │   │   │   ├── AppRoot.tsx
    │   │   │   ├── AppWrapper.tsx
    │   │   │   ├── buildProxy.ts
    │   │   │   ├── collectFnVarDeps.ts
    │   │   │   ├── ComponentAdapter.tsx
    │   │   │   ├── ComponentWrapper.tsx
    │   │   │   ├── Container.tsx
    │   │   │   ├── containers.ts
    │   │   │   ├── ContainerWrapper.tsx
    │   │   │   ├── ErrorBoundary.module.scss
    │   │   │   ├── ErrorBoundary.tsx
    │   │   │   ├── InvalidComponent.module.scss
    │   │   │   ├── InvalidComponent.tsx
    │   │   │   ├── nodeUtils.ts
    │   │   │   ├── reducer.ts
    │   │   │   ├── renderChild.tsx
    │   │   │   ├── StandaloneComponent.tsx
    │   │   │   ├── StateContainer.tsx
    │   │   │   ├── UnknownComponent.module.scss
    │   │   │   ├── UnknownComponent.tsx
    │   │   │   └── valueExtractor.ts
    │   │   ├── reportEngineError.ts
    │   │   ├── RestApiProxy.ts
    │   │   ├── script-runner
    │   │   │   ├── asyncProxy.ts
    │   │   │   ├── AttributeValueParser.ts
    │   │   │   ├── bannedFunctions.ts
    │   │   │   ├── BindingTreeEvaluationContext.ts
    │   │   │   ├── eval-tree-async.ts
    │   │   │   ├── eval-tree-common.ts
    │   │   │   ├── eval-tree-sync.ts
    │   │   │   ├── ParameterParser.ts
    │   │   │   ├── process-statement-async.ts
    │   │   │   ├── process-statement-common.ts
    │   │   │   ├── process-statement-sync.ts
    │   │   │   ├── ScriptingSourceTree.ts
    │   │   │   ├── simplify-expression.ts
    │   │   │   ├── statement-queue.ts
    │   │   │   └── visitors.ts
    │   │   ├── StandaloneApp.tsx
    │   │   ├── StandaloneExtensionManager.ts
    │   │   ├── TableOfContentsContext.tsx
    │   │   ├── theming
    │   │   │   ├── _themes.scss
    │   │   │   ├── component-layout-resolver.ts
    │   │   │   ├── extendThemeUtils.ts
    │   │   │   ├── hvar.ts
    │   │   │   ├── layout-resolver.ts
    │   │   │   ├── parse-layout-props.ts
    │   │   │   ├── StyleContext.tsx
    │   │   │   ├── StyleRegistry.ts
    │   │   │   ├── ThemeContext.tsx
    │   │   │   ├── ThemeProvider.tsx
    │   │   │   ├── themes
    │   │   │   │   ├── base-utils.ts
    │   │   │   │   ├── palette.ts
    │   │   │   │   ├── root.ts
    │   │   │   │   ├── solid.ts
    │   │   │   │   ├── theme-colors.ts
    │   │   │   │   └── xmlui.ts
    │   │   │   ├── themeVars.module.scss
    │   │   │   ├── themeVars.ts
    │   │   │   ├── transformThemeVars.ts
    │   │   │   └── utils.ts
    │   │   ├── utils
    │   │   │   ├── actionUtils.ts
    │   │   │   ├── audio-utils.ts
    │   │   │   ├── base64-utils.ts
    │   │   │   ├── compound-utils.ts
    │   │   │   ├── css-utils.ts
    │   │   │   ├── DataLoaderQueryKeyGenerator.ts
    │   │   │   ├── date-utils.ts
    │   │   │   ├── extractParam.ts
    │   │   │   ├── hooks.tsx
    │   │   │   ├── LruCache.ts
    │   │   │   ├── mergeProps.ts
    │   │   │   ├── misc.ts
    │   │   │   ├── request-params.ts
    │   │   │   ├── statementUtils.ts
    │   │   │   └── treeUtils.ts
    │   │   └── xmlui-parser.ts
    │   ├── index-standalone.ts
    │   ├── index.scss
    │   ├── index.ts
    │   ├── language-server
    │   │   ├── server-common.ts
    │   │   ├── server-web-worker.ts
    │   │   ├── server.ts
    │   │   ├── services
    │   │   │   ├── common
    │   │   │   │   ├── docs-generation.ts
    │   │   │   │   ├── lsp-utils.ts
    │   │   │   │   ├── metadata-utils.ts
    │   │   │   │   └── syntax-node-utilities.ts
    │   │   │   ├── completion.ts
    │   │   │   ├── diagnostic.ts
    │   │   │   ├── format.ts
    │   │   │   └── hover.ts
    │   │   └── xmlui-metadata-generated.js
    │   ├── logging
    │   │   ├── LoggerContext.tsx
    │   │   ├── LoggerInitializer.tsx
    │   │   ├── LoggerService.ts
    │   │   └── xmlui.ts
    │   ├── logo.svg
    │   ├── parsers
    │   │   ├── common
    │   │   │   ├── GenericToken.ts
    │   │   │   ├── InputStream.ts
    │   │   │   └── utils.ts
    │   │   ├── scripting
    │   │   │   ├── code-behind-collect.ts
    │   │   │   ├── Lexer.ts
    │   │   │   ├── modules.ts
    │   │   │   ├── Parser.ts
    │   │   │   ├── ParserError.ts
    │   │   │   ├── ScriptingNodeTypes.ts
    │   │   │   ├── TokenTrait.ts
    │   │   │   ├── TokenType.ts
    │   │   │   └── tree-visitor.ts
    │   │   ├── style-parser
    │   │   │   ├── errors.ts
    │   │   │   ├── source-tree.ts
    │   │   │   ├── StyleInputStream.ts
    │   │   │   ├── StyleLexer.ts
    │   │   │   ├── StyleParser.ts
    │   │   │   └── tokens.ts
    │   │   └── xmlui-parser
    │   │       ├── CharacterCodes.ts
    │   │       ├── diagnostics.ts
    │   │       ├── fileExtensions.ts
    │   │       ├── index.ts
    │   │       ├── lint.ts
    │   │       ├── parser.ts
    │   │       ├── ParserError.ts
    │   │       ├── scanner.ts
    │   │       ├── syntax-kind.ts
    │   │       ├── syntax-node.ts
    │   │       ├── transform.ts
    │   │       ├── utils.ts
    │   │       ├── xmlui-serializer.ts
    │   │       └── xmlui-tree.ts
    │   ├── react-app-env.d.ts
    │   ├── syntax
    │   │   ├── monaco
    │   │   │   ├── grammar.monacoLanguage.ts
    │   │   │   ├── index.ts
    │   │   │   ├── xmlui-dark.ts
    │   │   │   ├── xmlui-light.ts
    │   │   │   └── xmluiscript.monacoLanguage.ts
    │   │   └── textMate
    │   │       ├── index.ts
    │   │       ├── xmlui-dark.json
    │   │       ├── xmlui-light.json
    │   │       ├── xmlui.json
    │   │       └── xmlui.tmLanguage.json
    │   ├── testing
    │   │   ├── assertions.ts
    │   │   ├── component-test-helpers.ts
    │   │   ├── ComponentDrivers.ts
    │   │   ├── drivers
    │   │   │   ├── DateInputDriver.ts
    │   │   │   ├── index.ts
    │   │   │   ├── ModalDialogDriver.ts
    │   │   │   ├── NumberBoxDriver.ts
    │   │   │   ├── TextBoxDriver.ts
    │   │   │   ├── TimeInputDriver.ts
    │   │   │   ├── TimerDriver.ts
    │   │   │   └── TreeDriver.ts
    │   │   ├── fixtures.ts
    │   │   ├── index.ts
    │   │   ├── infrastructure
    │   │   │   ├── index.html
    │   │   │   ├── main.tsx
    │   │   │   ├── public
    │   │   │   │   ├── mockServiceWorker.js
    │   │   │   │   ├── resources
    │   │   │   │   │   ├── bell.svg
    │   │   │   │   │   ├── box.svg
    │   │   │   │   │   ├── doc.svg
    │   │   │   │   │   ├── eye.svg
    │   │   │   │   │   ├── flower-640x480.jpg
    │   │   │   │   │   ├── sun.svg
    │   │   │   │   │   ├── test-image-100x100.jpg
    │   │   │   │   │   └── txt.svg
    │   │   │   │   └── serve.json
    │   │   │   └── TestBed.tsx
    │   │   └── themed-app-test-helpers.ts
    │   └── vite-env.d.ts
    ├── tests
    │   ├── components
    │   │   ├── CodeBlock
    │   │   │   └── hightlight-code.test.ts
    │   │   ├── playground-pattern.test.ts
    │   │   └── Tree
    │   │       └── Tree-states.test.ts
    │   ├── components-core
    │   │   ├── abstractions
    │   │   │   └── treeAbstractions.test.ts
    │   │   ├── container
    │   │   │   └── buildProxy.test.ts
    │   │   ├── interception
    │   │   │   ├── orderBy.test.ts
    │   │   │   ├── ReadOnlyCollection.test.ts
    │   │   │   └── request-param-converter.test.ts
    │   │   ├── scripts-runner
    │   │   │   ├── AttributeValueParser.test.ts
    │   │   │   ├── eval-tree-arrow-async.test.ts
    │   │   │   ├── eval-tree-arrow.test.ts
    │   │   │   ├── eval-tree-func-decl-async.test.ts
    │   │   │   ├── eval-tree-func-decl.test.ts
    │   │   │   ├── eval-tree-pre-post.test.ts
    │   │   │   ├── eval-tree-regression.test.ts
    │   │   │   ├── eval-tree.test.ts
    │   │   │   ├── function-proxy.test.ts
    │   │   │   ├── parser-regression.test.ts
    │   │   │   ├── process-event.test.ts
    │   │   │   ├── process-function.test.ts
    │   │   │   ├── process-implicit-context.test.ts
    │   │   │   ├── process-statement-asgn.test.ts
    │   │   │   ├── process-statement-destruct.test.ts
    │   │   │   ├── process-statement-regs.test.ts
    │   │   │   ├── process-statement-sync.test.ts
    │   │   │   ├── process-statement.test.ts
    │   │   │   ├── process-switch-sync.test.ts
    │   │   │   ├── process-switch.test.ts
    │   │   │   ├── process-try-sync.test.ts
    │   │   │   ├── process-try.test.ts
    │   │   │   └── test-helpers.ts
    │   │   ├── test-metadata-handler.ts
    │   │   ├── theming
    │   │   │   ├── border-segments.test.ts
    │   │   │   ├── component-layout.resolver.test.ts
    │   │   │   ├── layout-property-parser.test.ts
    │   │   │   ├── layout-resolver.test.ts
    │   │   │   ├── layout-resolver2.test.ts
    │   │   │   ├── layout-vp-override.test.ts
    │   │   │   └── padding-segments.test.ts
    │   │   └── utils
    │   │       ├── date-utils.test.ts
    │   │       ├── format-human-elapsed-time.test.ts
    │   │       └── LruCache.test.ts
    │   ├── language-server
    │   │   ├── completion.test.ts
    │   │   ├── format.test.ts
    │   │   ├── hover.test.ts
    │   │   └── mockData.ts
    │   └── parsers
    │       ├── common
    │       │   └── input-stream.test.ts
    │       ├── markdown
    │       │   └── parse-binding-expression.test.ts
    │       ├── parameter-parser.test.ts
    │       ├── paremeter-parser.test.ts
    │       ├── scripting
    │       │   ├── eval-tree-arrow.test.ts
    │       │   ├── eval-tree-pre-post.test.ts
    │       │   ├── eval-tree.test.ts
    │       │   ├── function-proxy.test.ts
    │       │   ├── lexer-literals.test.ts
    │       │   ├── lexer-misc.test.ts
    │       │   ├── module-parse.test.ts
    │       │   ├── parser-arrow.test.ts
    │       │   ├── parser-assignments.test.ts
    │       │   ├── parser-binary.test.ts
    │       │   ├── parser-destructuring.test.ts
    │       │   ├── parser-errors.test.ts
    │       │   ├── parser-expressions.test.ts
    │       │   ├── parser-function.test.ts
    │       │   ├── parser-literals.test.ts
    │       │   ├── parser-primary.test.ts
    │       │   ├── parser-regex.test.ts
    │       │   ├── parser-statements.test.ts
    │       │   ├── parser-unary.test.ts
    │       │   ├── process-event.test.ts
    │       │   ├── process-implicit-context.test.ts
    │       │   ├── process-statement-asgn.test.ts
    │       │   ├── process-statement-destruct.test.ts
    │       │   ├── process-statement-regs.test.ts
    │       │   ├── process-statement-sync.test.ts
    │       │   ├── process-statement.test.ts
    │       │   ├── process-switch-sync.test.ts
    │       │   ├── process-switch.test.ts
    │       │   ├── process-try-sync.test.ts
    │       │   ├── process-try.test.ts
    │       │   ├── simplify-expression.test.ts
    │       │   ├── statement-hooks.test.ts
    │       │   └── test-helpers.ts
    │       ├── style-parser
    │       │   ├── generateHvarChain.test.ts
    │       │   ├── parseHVar.test.ts
    │       │   ├── parser.test.ts
    │       │   └── tokens.test.ts
    │       └── xmlui
    │           ├── lint.test.ts
    │           ├── parser.test.ts
    │           ├── scanner.test.ts
    │           ├── transform.attr.test.ts
    │           ├── transform.circular.test.ts
    │           ├── transform.element.test.ts
    │           ├── transform.errors.test.ts
    │           ├── transform.escape.test.ts
    │           ├── transform.regression.test.ts
    │           ├── transform.script.test.ts
    │           ├── transform.test.ts
    │           └── xmlui.ts
    ├── tests-e2e
    │   ├── api-bound-component-regression.spec.ts
    │   ├── api-call-as-extracted-component.spec.ts
    │   ├── assign-to-object-or-array-regression.spec.ts
    │   ├── binding-regression.spec.ts
    │   ├── children-as-template-context-vars.spec.ts
    │   ├── compound-component.spec.ts
    │   ├── context-vars-regression.spec.ts
    │   ├── data-bindings.spec.ts
    │   ├── datasource-and-api-usage-in-var.spec.ts
    │   ├── datasource-direct-binding.spec.ts
    │   ├── datasource-onLoaded-regression.spec.ts
    │   ├── modify-array-item-regression.spec.ts
    │   ├── namespaces.spec.ts
    │   ├── push-to-array-regression.spec.ts
    │   ├── screen-breakpoints.spec.ts
    │   ├── scripting.spec.ts
    │   ├── state-scope-in-pages.spec.ts
    │   └── state-var-scopes.spec.ts
    ├── tsconfig.json
    ├── tsdown.config.ts
    ├── vite.config.ts
    └── vitest.config.ts
```

# Files

--------------------------------------------------------------------------------
/xmlui/src/parsers/scripting/Parser.ts:
--------------------------------------------------------------------------------

```typescript
import {
  type ArrayDestructure,
  type ArrayLiteral,
  type ArrowExpression,
  type AssignmentExpression,
  type ScripNodeBase,
  type BinaryExpression,
  type BlockStatement,
  type BreakStatement,
  type CalculatedMemberAccessExpression,
  type ConditionalExpression,
  type ConstStatement,
  type ContinueStatement,
  type Destructure,
  type DoWhileStatement,
  type EmptyStatement,
  type Expression,
  type ExpressionStatement,
  type ForInStatement,
  type ForOfStatement,
  type ForStatement,
  type ForVarBinding,
  type FunctionDeclaration,
  type FunctionInvocationExpression,
  type Identifier,
  type IfStatement,
  type LetStatement,
  type Literal,
  type MemberAccessExpression,
  type NoArgExpression,
  type ObjectDestructure,
  type ObjectLiteral,
  type PostfixOpExpression,
  type PrefixOpExpression,
  type ReactiveVarDeclaration,
  type ReturnStatement,
  type SequenceExpression,
  type SpreadExpression,
  type Statement,
  type SwitchCase,
  type SwitchStatement,
  type ThrowStatement,
  type TryStatement,
  type UnaryExpression,
  type VarDeclaration,
  type VarStatement,
  type WhileStatement,
  type TemplateLiteralExpression,
  T_EMPTY_STATEMENT,
  T_BREAK_STATEMENT,
  T_CONTINUE_STATEMENT,
  T_EXPRESSION_STATEMENT,
  T_VAR_DECLARATION,
  T_LET_STATEMENT,
  T_CONST_STATEMENT,
  T_REACTIVE_VAR_DECLARATION,
  T_VAR_STATEMENT,
  T_ARRAY_DESTRUCTURE,
  T_BLOCK_STATEMENT,
  T_IF_STATEMENT,
  T_WHILE_STATEMENT,
  T_OBJECT_DESTRUCTURE,
  T_DO_WHILE_STATEMENT,
  T_RETURN_STATEMENT,
  T_FOR_STATEMENT,
  T_FOR_IN_STATEMENT,
  T_FOR_OF_STATEMENT,
  T_IDENTIFIER,
  T_THROW_STATEMENT,
  T_TRY_STATEMENT,
  T_SWITCH_CASE,
  T_SWITCH_STATEMENT,
  T_NO_ARG_EXPRESSION,
  T_SEQUENCE_EXPRESSION,
  T_OBJECT_LITERAL,
  T_ARRAY_LITERAL,
  T_SPREAD_EXPRESSION,
  T_FUNCTION_DECLARATION,
  T_ARROW_EXPRESSION,
  T_CONDITIONAL_EXPRESSION,
  T_ASSIGNMENT_EXPRESSION,
  T_BINARY_EXPRESSION,
  T_UNARY_EXPRESSION,
  T_FUNCTION_INVOCATION_EXPRESSION,
  T_MEMBER_ACCESS_EXPRESSION,
  T_CALCULATED_MEMBER_ACCESS_EXPRESSION,
  T_POSTFIX_OP_EXPRESSION,
  T_PREFIX_OP_EXPRESSION,
  T_LITERAL,
  T_TEMPLATE_LITERAL_EXPRESSION,
  T_DESTRUCTURE,
} from "../../components-core/script-runner/ScriptingSourceTree";
import type { GenericToken } from "../common/GenericToken";
import { InputStream } from "../common/InputStream";
import { Lexer } from "./Lexer";
import { ParserError, errorMessages } from "./ParserError";
import { tokenTraits } from "./TokenTrait";
import { TokenType } from "./TokenType";
import type { ErrorCodes, ParserErrorMessage } from "./ParserError";

type Token = GenericToken<TokenType>;

/**
 * States of the string parsing
 */
enum StrParseState {
  Normal,
  Backslash,
  X,
  Xh,
  UX1,
  UX2,
  UX3,
  UX4,
  Ucp1,
  Ucp2,
  Ucp3,
  Ucp4,
  Ucp5,
  Ucp6,
  UcpTail,
}

let lastNodeId = 0;

export function createXmlUiTreeNodeId(): number {
  return ++lastNodeId;
}

/**
 * This class parses a binding expression and transforms it into an evaluable expression tree
 */
export class Parser {
  // --- Keep track of error messages
  private _parseErrors: ParserErrorMessage[] = [];

  // --- Use this lexer
  private _lexer: Lexer;

  // --- Indicates the parsing level
  private _statementLevel = 0;

  /**
   * Initializes the parser with the specified source code
   * @param source Source code to parse
   */
  constructor(source?: string) {
    this._lexer = new Lexer(new InputStream(source ?? ""));
  }

  /**
   * Sets the source code to parse
   * @param source Source code to parse
   */
  setSource(source: string): void {
    this._lexer = new Lexer(new InputStream(source));
  }

  /**
   * The errors raised during the parse phase
   */
  get errors(): ParserErrorMessage[] {
    return this._parseErrors;
  }

  /**
   * Gets the current token
   */
  get current(): Token {
    return this._lexer.peek();
  }

  /**
   * Checks if we're at the end of the expression
   */
  get isEof(): boolean {
    return this._lexer.peek().type === TokenType.Eof;
  }

  /**
   * Gets the characters remaining after parsing
   */
  getTail(): string {
    return this._lexer.getTail();
  }

  // ==========================================================================
  // Statement parsing

  /**
   * Parses a list of statements:
   *
   * statements
   *   : statement*
   *   ;
   *
   * statement
   *   : emptyStatement
   *   | expressionStatement
   *   | letStatement
   *   | returnStatement
   *   ;
   */
  parseStatements(): Statement[] | null {
    this._statementLevel = 0;
    const statements: Statement[] = [];
    while (!this.isEof) {
      const statement = this.parseStatement();
      if (!statement) return null;
      statements.push(statement);
      if (statement.type !== T_EMPTY_STATEMENT) {
        this.skipToken(TokenType.Semicolon);
      }
    }
    return statements;
  }

  /**
   * Parses a single statement
   */
  private parseStatement(allowSequence = true): Statement | null {
    this._statementLevel++;
    try {
      const startToken = this._lexer.peek();
      switch (startToken.type) {
        case TokenType.Semicolon:
          return this.parseEmptyStatement();
        case TokenType.Let:
          return this.parseLetStatement();
        case TokenType.Const:
          return this.parseConstStatement();
        case TokenType.Var:
          return this.parseVarStatement();
        case TokenType.LBrace:
          return this.parseBlockStatement();
        case TokenType.If:
          return this.parseIfStatement();
        case TokenType.Do:
          return this.parseDoWhileStatement();
        case TokenType.While:
          return this.parseWhileStatement();
        case TokenType.Return:
          return this.parseReturnStatement();
        case TokenType.Break:
          this._lexer.get();
          return this.createStatementNode<BreakStatement>(
            T_BREAK_STATEMENT,
            {},
            startToken,
            startToken,
          );
        case TokenType.Continue:
          this._lexer.get();
          return this.createStatementNode<ContinueStatement>(
            T_CONTINUE_STATEMENT,
            {},
            startToken,
            startToken,
          );
        case TokenType.For:
          return this.parseForStatement();
        case TokenType.Throw:
          return this.parseThrowStatement();
        case TokenType.Try:
          return this.parseTryStatement();
        case TokenType.Switch:
          return this.parseSwitchStatement();
        case TokenType.Function:
          return this.parseFunctionDeclaration();
        default:
          if (this.isExpressionStart(startToken)) {
            return this.parseExpressionStatement(allowSequence);
          }
          this.reportError("W002", startToken, startToken.text);
          return null;
      }
    } finally {
      this._statementLevel--;
    }
  }

  /**
   * Parses an empty statement
   *
   * emptyStatement
   *   : ";"
   *   ;
   */
  private parseEmptyStatement(): EmptyStatement | null {
    const startToken = this._lexer.get();
    return this.createStatementNode<EmptyStatement>(T_EMPTY_STATEMENT, {}, startToken, startToken);
  }

  /**
   * Parses an expression statement
   *
   * expressionStatement
   *   : expression
   *   ;
   */
  private parseExpressionStatement(allowSequence = true): ExpressionStatement | null {
    const startToken = this._lexer.peek();
    const expr = this.getExpression(allowSequence);
    return expr
      ? this.createStatementNode<ExpressionStatement>(
          T_EXPRESSION_STATEMENT,
          {
            expr,
          },
          startToken,
          expr.endToken,
        )
      : null;
  }

  /**
   * Parses a let statement
   *
   * letStatement
   *   : "let" id ["=" expression] ("," id ["=" expression])*
   *   ;
   */
  private parseLetStatement(): LetStatement | null {
    const startToken = this._lexer.get();
    let endToken: Token | undefined = startToken;
    const decls: VarDeclaration[] = [];
    while (true) {
      const declStart = this._lexer.peek();
      let declarationProps: any = {};
      if (declStart.type === TokenType.LBrace) {
        // --- This is object destructure
        endToken = this._lexer.ahead(1);
        const oDestr = this.parseObjectDestructure();
        if (oDestr === null) return null;
        declarationProps = {
          oDestr,
        };
        endToken = oDestr.length > 0 ? oDestr[oDestr.length - 1].endToken : endToken;
      } else if (declStart.type === TokenType.LSquare) {
        // --- This is array destructure
        endToken = this._lexer.ahead(1);
        const aDestr = this.parseArrayDestructure();
        if (aDestr === null) return null;
        declarationProps = {
          aDestr,
        };
        endToken = aDestr.length > 0 ? aDestr[aDestr.length - 1].endToken : endToken;
      } else if (declStart.type === TokenType.Identifier) {
        if (declStart.text.startsWith("$")) {
          this.reportError("W031");
          return null;
        }
        endToken = this._lexer.get();
        declarationProps = {
          id: declStart.text,
        };
      } else {
        this.reportError("W003");
        return null;
      }

      // --- Optional initialization
      const initToken = this._lexer.peek();
      let expr: Expression | null = null;
      if (initToken.type === TokenType.Assignment) {
        this._lexer.get();
        expr = this.getExpression(false);
        if (expr === null) return null;
        declarationProps.expr = expr;
        endToken = expr.endToken;
      } else if (declarationProps.aDestr || declarationProps.oDestr) {
        this.reportError("W009", initToken);
        return null;
      }

      // --- New declaration reached
      decls.push(
        this.createExpressionNode<VarDeclaration>(
          T_VAR_DECLARATION,
          declarationProps,
          declStart,
          endToken,
        ),
      );

      // --- Check for more declarations
      if (this._lexer.peek().type !== TokenType.Comma) break;
      this._lexer.get();
    }

    // --- Done
    return this.createStatementNode<LetStatement>(
      T_LET_STATEMENT,
      {
        decls,
      },
      startToken,
      endToken,
    );
  }

  /**
   * Parses a const statement
   *
   * constStatement
   *   : "const" id "=" expression
   *   ;
   */
  private parseConstStatement(): ConstStatement | null {
    const startToken = this._lexer.get();
    let endToken: Token | undefined = startToken;
    const decls: VarDeclaration[] = [];
    while (true) {
      const declStart = this._lexer.peek();
      let declarationProps: any = {};
      if (declStart.type === TokenType.LBrace) {
        // --- This is object destructure
        endToken = this._lexer.ahead(1);
        const oDestr = this.parseObjectDestructure();
        if (oDestr === null) return null;
        declarationProps = {
          oDestr,
        };
        endToken = oDestr.length > 0 ? oDestr[oDestr.length - 1].endToken : endToken;
      } else if (declStart.type === TokenType.LSquare) {
        // --- This is array destructure
        endToken = this._lexer.ahead(1);
        const aDestr = this.parseArrayDestructure();
        if (aDestr === null) return null;
        declarationProps = {
          aDestr,
        };
        endToken = aDestr.length > 0 ? aDestr[aDestr.length - 1].endToken : endToken;
      } else if (declStart.type === TokenType.Identifier) {
        if (declStart.text.startsWith("$")) {
          this.reportError("W031");
          return null;
        }
        endToken = this._lexer.get();
        declarationProps = {
          id: declStart.text,
        };
      } else {
        this.reportError("W003");
        return null;
      }

      this.expectToken(TokenType.Assignment);
      const expr = this.getExpression(false);
      if (expr === null) return null;
      declarationProps.expr = expr;
      endToken = expr.endToken;

      // --- New declaration reached
      decls.push(
        this.createExpressionNode<VarDeclaration>(
          T_VAR_DECLARATION,
          declarationProps,
          declStart,
          endToken,
        ),
      );

      // --- Check for more declarations
      if (this._lexer.peek().type !== TokenType.Comma) break;
      this._lexer.get();
    }

    // --- Done
    return this.createStatementNode<ConstStatement>(
      T_CONST_STATEMENT,
      {
        decls,
      },
      startToken,
      endToken,
    );
  }

  /**
   * Parses a var statement
   *
   * constStatement
   *   : "var" id "=" expression
   *   ;
   */
  private parseVarStatement(): VarStatement | null {
    const startToken = this._lexer.get();
    let endToken: Token | undefined = startToken;
    const decls: ReactiveVarDeclaration[] = [];
    while (true) {
      const declStart = this._lexer.peek();
      let declarationProps: any = {};
      if (declStart.type === TokenType.Identifier) {
        if (declStart.text.startsWith("$")) {
          this.reportError("W031");
          return null;
        }
        endToken = this._lexer.get();
        declarationProps = {
          id: {
            type: T_IDENTIFIER,
            name: declStart.text,
            startToken: declStart,
            endToken,
          },
        };
      } else {
        this.reportError("W003");
        return null;
      }

      // --- Mandatory initialization
      this.expectToken(TokenType.Assignment);
      const expr = this.getExpression(false);
      if (expr === null) return null;
      declarationProps.expr = expr;
      endToken = expr.endToken;
      // --- New declaration reached
      decls.push(
        this.createExpressionNode<ReactiveVarDeclaration>(
          T_REACTIVE_VAR_DECLARATION,
          declarationProps,
          declStart,
          endToken,
        ),
      );

      // --- Check for more declarations
      if (this._lexer.peek().type !== TokenType.Comma) break;
      this._lexer.get();
    }

    // --- Done
    return this.createStatementNode<VarStatement>(
      T_VAR_STATEMENT,
      {
        decls,
      },
      startToken,
      endToken,
    );
  }

  /**
   * Parses an object destructure expression
   */
  private parseObjectDestructure(): ObjectDestructure[] | null {
    const result: ObjectDestructure[] = [];

    // --- Skip the starting left brace
    const startToken = this._lexer.get();
    let endToken: Token | undefined = startToken;

    let nextToken = this._lexer.peek();
    while (nextToken.type === TokenType.Identifier) {
      // --- Obtain the ID
      const id = nextToken.text;
      if (id.startsWith("$")) {
        this.reportError("W031");
        return null;
      }

      let alias: string | undefined;
      let aDestr: ArrayDestructure[] | undefined | null;
      let oDestr: ObjectDestructure[] | undefined | null;
      this._lexer.get();

      nextToken = this._lexer.peek();
      if (nextToken.type === TokenType.Colon) {
        // --- Check the available cases
        this._lexer.get();
        nextToken = this._lexer.peek();
        if (nextToken.type === TokenType.Identifier) {
          alias = nextToken.text;
          endToken = nextToken;
          this._lexer.get();
        } else if (nextToken.type === TokenType.LSquare) {
          aDestr = this.parseArrayDestructure();
          if (aDestr === null) return null;
          endToken = aDestr[aDestr.length - 1].endToken;
        } else if (nextToken.type === TokenType.LBrace) {
          oDestr = this.parseObjectDestructure();
          if (oDestr === null) return null;
          endToken = oDestr[oDestr.length - 1].endToken;
        }
      }

      // --- Check for next segment
      nextToken = this._lexer.peek();
      if (nextToken.type === TokenType.Comma || nextToken.type === TokenType.RBrace) {
        result.push(
          this.createExpressionNode<ObjectDestructure>(
            T_OBJECT_DESTRUCTURE,
            { id, alias, aDestr, oDestr },
            startToken,
            endToken,
          ),
        );

        if (nextToken.type === TokenType.Comma) {
          // --- Skip the delimiter comma
          this._lexer.get();
          nextToken = this._lexer.peek();
        }
      }
    }

    // --- Expect a closing right brace
    this.expectToken(TokenType.RBrace, "W004");
    return result;
  }

  private parseArrayDestructure(): ArrayDestructure[] | null {
    const result: ArrayDestructure[] = [];

    // --- Skip the starting left square
    const startToken = this._lexer.get();
    let endToken: Token | undefined = startToken;

    do {
      let nextToken = this._lexer.peek();
      let id: string | undefined;
      let aDestr: ArrayDestructure[] | undefined | null;
      let oDestr: ObjectDestructure[] | undefined | null;
      if (nextToken.type === TokenType.Identifier) {
        id = nextToken.text;
        if (id.startsWith("$")) {
          this.reportError("W031");
          return null;
        }

        endToken = nextToken;
        nextToken = this._lexer.get();
      } else if (nextToken.type === TokenType.LSquare) {
        aDestr = this.parseArrayDestructure();
        if (aDestr === null) return null;
        endToken = aDestr[aDestr.length - 1].endToken;
      } else if (nextToken.type === TokenType.LBrace) {
        oDestr = this.parseObjectDestructure();
        if (oDestr === null) return null;
        endToken = oDestr[oDestr.length - 1].endToken;
      }

      nextToken = this._lexer.peek();
      if (nextToken.type === TokenType.Comma) {
        // --- Store the segment
        result.push(
          this.createExpressionNode<ArrayDestructure>(
            T_ARRAY_DESTRUCTURE,
            { id, aDestr, oDestr },
            startToken,
            endToken,
          ),
        );
        this._lexer.get();
      } else if (nextToken.type === TokenType.RSquare) {
        if (id || aDestr || oDestr) {
          // --- Store a non-empty the segment
          result.push(
            this.createExpressionNode<ArrayDestructure>(
              T_ARRAY_DESTRUCTURE,
              { id, aDestr, oDestr },
              startToken,
              endToken,
            ),
          );
        }
        break;
      } else {
        this.reportError("W002", nextToken);
        return null;
      }
    } while (true);

    // --- Expect a closing right square
    this.expectToken(TokenType.RSquare, "W005");
    return result;
  }

  /**
   * Parses a block statement
   *
   * blockStatement
   *   : "{" (statement [";"])* "}"
   *   ;
   */
  private parseBlockStatement(): BlockStatement | null {
    const startToken = this._lexer.get();
    const stmts: Statement[] = [];
    while (this._lexer.peek().type !== TokenType.RBrace) {
      const statement = this.parseStatement();
      if (!statement) return null;
      stmts.push(statement);
      if (statement.type !== T_EMPTY_STATEMENT) {
        this.skipToken(TokenType.Semicolon);
      }
    }
    const endToken = this._lexer.get();
    return this.createStatementNode<BlockStatement>(
      T_BLOCK_STATEMENT,
      { stmts },
      startToken,
      endToken,
    );
  }

  /**
   * Parses an if statement
   *
   * ifStatement
   *   : "if" "(" expression ")" statement ["else" statement]
   *   ;
   */
  private parseIfStatement(): IfStatement | null {
    const startToken = this._lexer.get();
    let endToken: Token | undefined = startToken;
    this.expectToken(TokenType.LParent, "W014");
    const cond = this.getExpression();
    if (!cond) return null;
    this.expectToken(TokenType.RParent, "W006");
    const thenB = this.parseStatement();
    if (!thenB) return null;
    endToken = thenB.endToken;
    let elseCanFollow = true;
    if (thenB.type !== T_BLOCK_STATEMENT) {
      // --- We need a closing semicolon before else
      if (this._lexer.peek().type === TokenType.Semicolon) {
        this._lexer.get();
      } else {
        elseCanFollow = false;
      }
    }
    let elseB: Statement | null = null;
    if (elseCanFollow && this._lexer.peek().type === TokenType.Else) {
      this._lexer.get();
      elseB = this.parseStatement();
      if (!elseB) return null;
      endToken = elseB.endToken;
    }
    return this.createStatementNode<IfStatement>(
      T_IF_STATEMENT,
      {
        cond,
        thenB,
        elseB,
      },
      startToken,
      endToken,
    );
  }

  /**
   * Parses a while statement
   *
   * whileStatement
   *   : "while" "(" condition ")" statement
   *   ;
   */
  private parseWhileStatement(): WhileStatement | null {
    const startToken = this._lexer.get();
    this.expectToken(TokenType.LParent, "W014");
    const cond = this.getExpression();
    if (!cond) return null;
    this.expectToken(TokenType.RParent, "W006");
    const body = this.parseStatement();
    if (!body) return null;

    return this.createStatementNode<WhileStatement>(
      T_WHILE_STATEMENT,
      {
        cond,
        body,
      },
      startToken,
      body.endToken,
    );
  }

  /**
   * Parses a do-while statement
   *
   * doWhileStatement
   *   : "do" statement "while" "(" condition ")"
   *   ;
   */
  private parseDoWhileStatement(): DoWhileStatement | null {
    const startToken = this._lexer.get();
    const body = this.parseStatement();
    if (!body) return null;
    if (body.type !== T_BLOCK_STATEMENT && body.type !== T_EMPTY_STATEMENT) {
      this.expectToken(TokenType.Semicolon);
    }
    this.expectToken(TokenType.While);
    this.expectToken(TokenType.LParent, "W014");
    const cond = this.getExpression();
    if (!cond) return null;
    const endToken = this._lexer.peek();
    this.expectToken(TokenType.RParent, "W006");

    return this.createStatementNode<DoWhileStatement>(
      T_DO_WHILE_STATEMENT,
      {
        cond,
        body,
      },
      startToken,
      endToken,
    );
  }

  /**
   * Parses an expression statement
   *
   * returnStatement
   *   : "return" expression?
   *   ;
   */
  private parseReturnStatement(): ReturnStatement | null {
    const startToken = this._lexer.peek();
    let endToken: Token | undefined = this._lexer.get();
    let expr: Expression | undefined | null;
    if (tokenTraits[this._lexer.peek().type].expressionStart) {
      expr = this.getExpression();
      if (expr === null) return null;
      endToken = expr.endToken;
    }
    return this.createStatementNode<ReturnStatement>(
      T_RETURN_STATEMENT,
      {
        expr,
      },
      startToken,
      endToken,
    );
  }

  /**
   * forStatement
   *   : "for" "(" initStatement? ";" expression? ";" expression? ")" statement
   *   | forInOfStatement
   *   ;
   */
  private parseForStatement(): ForStatement | ForInStatement | ForOfStatement | null {
    const startToken = this._lexer.peek();
    this._lexer.get();
    this.expectToken(TokenType.LParent, "W014");

    // --- Check for..in and for..of
    let nextToken = this._lexer.peek();
    if (nextToken.type === TokenType.Identifier) {
      if (this._lexer.ahead(1).type === TokenType.In) {
        return this.parseForInOfStatement(startToken, "none", nextToken, T_FOR_IN_STATEMENT);
      } else if (this._lexer.ahead(1).type === TokenType.Of) {
        return this.parseForInOfStatement(startToken, "none", nextToken, T_FOR_OF_STATEMENT);
      }
    } else if (nextToken.type === TokenType.Let) {
      const idToken = this._lexer.ahead(1);
      if (idToken.type === TokenType.Identifier) {
        const inOfToken = this._lexer.ahead(2);
        if (inOfToken.type === TokenType.In) {
          return this.parseForInOfStatement(startToken, "let", idToken, T_FOR_IN_STATEMENT);
        } else if (inOfToken.type === TokenType.Of) {
          return this.parseForInOfStatement(startToken, "let", idToken, T_FOR_OF_STATEMENT);
        }
      }
    } else if (nextToken.type === TokenType.Const) {
      const idToken = this._lexer.ahead(1);
      if (idToken.type === TokenType.Identifier) {
        const inOfToken = this._lexer.ahead(2);
        if (inOfToken.type === TokenType.In) {
          return this.parseForInOfStatement(startToken, "const", idToken, T_FOR_IN_STATEMENT);
        } else if (inOfToken.type === TokenType.Of) {
          return this.parseForInOfStatement(startToken, "const", idToken, T_FOR_OF_STATEMENT);
        }
      }
    }

    // --- We accept only let statement, empty statement, and expression
    let init: ExpressionStatement | LetStatement | undefined;
    nextToken = this._lexer.peek();
    if (nextToken.type === TokenType.Semicolon) {
      // --- Empty statement: no init in the for loop
      this._lexer.get();
    } else if (nextToken.type === TokenType.Let) {
      // --- Let statement
      const letStmt = this.parseLetStatement();
      if (letStmt === null) {
        return null;
      }
      init = letStmt;
      if (init.decls.some((d) => !d.expr)) {
        this.reportError("W011");
        return null;
      }
      this.expectToken(TokenType.Semicolon);
    } else if (tokenTraits[nextToken.type].expressionStart) {
      // --- Expression statement
      const exprStmt = this.parseExpressionStatement();
      if (exprStmt === null) {
        return null;
      }
      init = exprStmt;
      this.expectToken(TokenType.Semicolon);
    }

    // --- Parse the condition
    let cond: Expression | null | undefined;
    nextToken = this._lexer.peek();
    if (nextToken.type === TokenType.Semicolon) {
      // --- No condition
      this._lexer.get();
    } else {
      cond = this.getExpression();
      if (cond === null) {
        return null;
      }
      this.expectToken(TokenType.Semicolon);
    }

    // --- Parse the update expression
    let upd: Expression | null | undefined;
    nextToken = this._lexer.peek();
    if (nextToken.type !== TokenType.RParent) {
      upd = this.getExpression();
      if (upd === null) {
        return null;
      }
    }

    // --- Close the declaration part
    this.expectToken(TokenType.RParent, "W006");

    // --- Get the body
    const body = this.parseStatement();
    if (!body) return null;

    return this.createStatementNode<ForStatement>(
      T_FOR_STATEMENT,
      {
        init,
        cond,
        upd,
        body,
      },
      startToken,
      body.endToken,
    );
  }

  /**
   * forInOfStatement
   *   : "for" "(" [ "let" | "const" ] identifier ( "in" | "of" ) expression? ")" statement
   *   | forInOfStatement
   *   ;
   *
   * @param startToken Statement start token
   * @param varB Variable binding of the for..in/of statement
   * @param id ID name
   * @param type Is it a for..in or a for..of?
   */
  private parseForInOfStatement(
    startToken: Token,
    varB: ForVarBinding,
    idToken: Token,
    type: number,
  ): ForInStatement | ForOfStatement | null {
    if (varB !== "none") {
      // --- Skip variable binding type
      if (idToken.text.startsWith("$")) {
        this.reportError("W031");
        return null;
      }
      this._lexer.get();
    }

    // --- Skip variable name, in/of token
    this._lexer.get();
    this._lexer.get();

    // --- Get the expression
    const expr = this.getExpression(true);

    // --- Close the declaration part
    this.expectToken(TokenType.RParent, "W006");

    // --- Get the body
    const body = this.parseStatement();
    if (!body) return null;

    return type === T_FOR_IN_STATEMENT
      ? this.createStatementNode<ForInStatement>(
          T_FOR_IN_STATEMENT,
          {
            varB,
            id: {
              type: T_IDENTIFIER,
              name: idToken.text,
              startToken: idToken,
              endToken: idToken,
            },
            expr,
            body,
          },
          startToken,
          body.endToken,
        )
      : this.createStatementNode<ForOfStatement>(
          T_FOR_OF_STATEMENT,
          {
            varB,
            id: {
              type: T_IDENTIFIER,
              name: idToken.text,
              startToken: idToken,
              endToken: idToken,
            },
            expr,
            body,
          },
          startToken,
          body.endToken,
        );
  }

  /**
   * Parses a throw statement
   *
   * throwStatement
   *   : "throw" expression
   *   ;
   */
  private parseThrowStatement(): ThrowStatement | null {
    const startToken = this._lexer.peek();
    this._lexer.get();
    let expr: Expression | undefined | null;
    expr = this.getExpression();
    if (expr === null) return null;
    return this.createStatementNode<ThrowStatement>(
      T_THROW_STATEMENT,
      {
        expr,
      },
      startToken,
      expr.endToken,
    );
  }

  /**
   * Parses a try..catch..finally statement
   *
   * tryStatement
   *   : "try" blockStatement catchClause finallyClause?
   *   | "try" blockStatement catchClause? finallyClause
   *   ;
   *
   * catchClause
   *   : "catch" [ "(" identifier ") ]? blockStatement
   *   ;
   *
   * finallyClause
   *   : "finally" blockStatement
   */
  private parseTryStatement(): TryStatement | null {
    const getBlock: () => BlockStatement | null = () => {
      const nextToken = this._lexer.peek();
      if (nextToken.type !== TokenType.LBrace) {
        this.reportError("W012", nextToken);
        return null;
      }
      return this.parseBlockStatement();
    };

    const startToken = this._lexer.peek();
    let endToken: Token | undefined = this._lexer.get();

    // --- Get "try" block
    const tryB = getBlock()!;
    let catchB: BlockStatement | undefined;
    let catchV: Identifier | undefined;
    let finallyB: BlockStatement | undefined;
    let nextToken = this._lexer.peek();
    if (nextToken.type === TokenType.Catch) {
      // --- Catch found
      this._lexer.get();
      nextToken = this._lexer.peek();
      if (nextToken.type === TokenType.LParent) {
        // --- Catch variable found
        this._lexer.get();
        nextToken = this._lexer.peek();
        if (nextToken.type !== TokenType.Identifier) {
          this.reportError("W003", nextToken);
          return null;
        }
        catchV = {
          type: T_IDENTIFIER,
          nodeId: createXmlUiTreeNodeId(),
          name: nextToken.text,
          startToken: nextToken,
          endToken: nextToken,
        };
        this._lexer.get();
        this.expectToken(TokenType.RParent, "W006");
      }

      // --- Get catch block
      catchB = getBlock()!;
      endToken = catchB.endToken;
      if (this._lexer.peek().type === TokenType.Finally) {
        // --- Finally after catch
        this._lexer.get();
        finallyB = getBlock()!;
        endToken = finallyB.endToken;
      }
    } else if (nextToken.type === TokenType.Finally) {
      // --- Finally found
      this._lexer.get();
      finallyB = getBlock()!;
      endToken = finallyB.endToken;
    } else {
      this.reportError("W013", nextToken);
      return null;
    }

    return this.createStatementNode<TryStatement>(
      T_TRY_STATEMENT,
      {
        tryB,
        catchB,
        catchV,
        finallyB,
      },
      startToken,
      endToken,
    );
  }

  /**
   * Parses a switch statement
   *
   * switchStatement
   *   : "switch" "(" expression ")" "{" caseClauses "}"
   *   ;
   *
   * caseClauses
   *   : "case" expression ":" statement*
   *   | "default" ":" statement*
   *   ;
   */
  private parseSwitchStatement(): SwitchStatement | null {
    const startToken = this._lexer.get();

    // --- Parse the switch expression
    this.expectToken(TokenType.LParent, "W014");
    const expr = this.getExpression();
    if (!expr) return null;
    this.expectToken(TokenType.RParent, "W006");

    // --- Parse the case blocks
    this.expectToken(TokenType.LBrace, "W012");
    const cases: SwitchCase[] = [];
    let defaultCaseFound = false;
    while (true) {
      let nextToken = this._lexer.peek();
      let caseE: Expression | null | undefined;
      if (nextToken.type === TokenType.Case) {
        // --- Process "case"
        this._lexer.get();
        caseE = this.getExpression();
        if (!caseE) return null;
      } else if (nextToken.type === TokenType.Default) {
        // --- Process "default"
        if (defaultCaseFound) {
          this.reportError("W016");
          return null;
        }
        defaultCaseFound = true;
        this._lexer.get();
      } else if (nextToken.type === TokenType.RBrace) {
        break;
      } else {
        this.reportError("W015");
        return null;
      }

      // --- Process label statements
      this.expectToken(TokenType.Colon, "W008");
      let stmts: Statement[] = [];
      let collected = false;
      while (!collected) {
        const stmtToken = this._lexer.peek();
        switch (stmtToken.type) {
          case TokenType.Case:
          case TokenType.Default:
          case TokenType.RBrace:
            // --- No more case to process
            collected = true;
            break;
          default:
            // --- Try to get the next statement
            const stmt = this.parseStatement();
            if (stmt === null) {
              collected = true;
              break;
            }
            stmts.push(stmt);
            if (stmt.type !== T_EMPTY_STATEMENT) {
              this.skipToken(TokenType.Semicolon);
            }
        }
      }

      cases.push(
        this.createNode(
          T_SWITCH_CASE,
          {
            caseE,
            stmts,
          },
          startToken,
        ),
      );
    }

    // --- Closing
    const endToken = this._lexer.peek();
    this.expectToken(TokenType.RBrace, "W004");
    return this.createStatementNode<SwitchStatement>(
      T_SWITCH_STATEMENT,
      {
        expr,
        cases,
      },
      startToken,
      endToken,
    );
  }

  /**
   * Parses a function declaration
   *
   * functionDeclaration
   *   : "function" identifier "(" [parameterList] ")" blockStatement
   *   ;
   */
  private parseFunctionDeclaration(allowNoName = false): FunctionDeclaration | null {
    const startToken = this._lexer.get();

    // --- Get the function name
    let functionName: string | undefined;
    const funcId = this._lexer.peek();
    if (allowNoName) {
      if (funcId.type !== TokenType.LParent) {
        if (funcId.type !== TokenType.Identifier) {
          this.reportError("W003", funcId);
          return null;
        }
        functionName = funcId.text;
        this._lexer.get();
      }
    } else {
      if (funcId.type !== TokenType.Identifier) {
        this.reportError("W003", funcId);
        return null;
      }
      functionName = funcId.text;
      this._lexer.get();
    }

    // --- Get the parameter list;
    const nextToken = this._lexer.peek();
    if (nextToken.type !== TokenType.LParent) {
      this.reportError("W014", nextToken);
      return null;
    }

    // --- Now, get the parameter list as an expression
    const exprList = this.getExpression(true);

    // --- We turn the expression into a parameter list
    let isValid: boolean;
    const args: Expression[] = [];
    switch (exprList!.type) {
      case T_NO_ARG_EXPRESSION:
        isValid = true;
        break;
      case T_IDENTIFIER:
        isValid = (exprList.parenthesized ?? 0) <= 1;
        args.push(exprList);
        break;
      case T_SEQUENCE_EXPRESSION:
        isValid = exprList.parenthesized === 1;
        let spreadFound = false;
        if (isValid) {
          for (const expr of exprList.exprs) {
            // --- Spread operator can be used only in the last position
            if (spreadFound) {
              isValid = false;
              break;
            }
            switch (expr.type) {
              case T_IDENTIFIER:
                isValid = !expr.parenthesized;
                args.push(expr);
                break;
              case T_OBJECT_LITERAL: {
                isValid = !expr.parenthesized;
                if (isValid) {
                  const des = this.convertToObjectDestructure(expr);
                  if (des) args.push(des);
                }
                break;
              }
              case T_ARRAY_LITERAL: {
                isValid = !expr.parenthesized;
                if (isValid) {
                  const des = this.convertToArrayDestructure(expr);
                  if (des) args.push(des);
                }
                break;
              }
              case T_SPREAD_EXPRESSION: {
                spreadFound = true;
                if (expr.expr.type !== T_IDENTIFIER) {
                  isValid = false;
                  break;
                }
                args.push(expr);
                break;
              }
              default:
                isValid = false;
                break;
            }
          }
        }
        break;
      case T_OBJECT_LITERAL:
        isValid = exprList.parenthesized === 1;
        if (isValid) {
          const des = this.convertToObjectDestructure(exprList);
          if (des) args.push(des);
        }
        break;
      case T_ARRAY_LITERAL:
        isValid = exprList.parenthesized === 1;
        if (isValid) {
          const des = this.convertToArrayDestructure(exprList);
          if (des) args.push(des);
        }
        break;
      case T_SPREAD_EXPRESSION:
        if (exprList.expr.type !== T_IDENTIFIER) {
          isValid = false;
          break;
        }
        isValid = true;
        args.push(exprList);
        break;
      default:
        isValid = false;
    }
    if (!isValid) {
      this.reportError("W010", startToken);
      return null;
    }

    // --- Get the function body (statement block)
    if (this._lexer.peek().type !== TokenType.LBrace) {
      this.reportError("W012", this._lexer.peek());
      return null;
    }

    const stmt = this.parseBlockStatement();
    if (!stmt) return null;

    // --- Done.
    return this.createStatementNode<FunctionDeclaration>(
      T_FUNCTION_DECLARATION,
      {
        id: { type: T_IDENTIFIER, name: functionName },
        args,
        stmt,
      },
      startToken,
      stmt.endToken,
    );
  }

  // ==========================================================================
  // Expression parsing

  /**
   * Parses an expression:
   *
   * expr
   *   : sequenceExpr
   *   ;
   */
  parseExpr(allowSequence = true): Expression | null {
    return allowSequence
      ? this.parseSequenceExpression()
      : this.parseCondOrSpreadOrAsgnOrArrowExpr();
  }

  /**
   * sequenceExpr
   *   : conditionalExpr ( "," conditionalExpr )?
   */
  private parseSequenceExpression(): Expression | null {
    const start = this._lexer.peek();
    let endToken: Token | undefined = start;
    let leftExpr = this.parseCondOrSpreadOrAsgnOrArrowExpr();
    if (!leftExpr) {
      return null;
    }
    endToken = leftExpr.endToken;
    const exprs: Expression[] = [];
    let loose = false;

    if (this._lexer.peek().type === TokenType.Comma) {
      exprs.push(leftExpr);
      while (this.skipToken(TokenType.Comma)) {
        if (this._lexer.peek().type === TokenType.Comma) {
          loose = true;
          endToken = this._lexer.peek();
          exprs.push(
            this.createExpressionNode<NoArgExpression>(T_NO_ARG_EXPRESSION, {}, endToken, endToken),
          );
        } else {
          const nextExpr = this.parseCondOrSpreadOrAsgnOrArrowExpr();
          if (!nextExpr) {
            break;
          }
          endToken = nextExpr.endToken;
          exprs.push(nextExpr);
        }
      }
    }

    if (!exprs.length) {
      // --- No sequence
      return leftExpr;
    }

    // --- Create the sequence expression
    leftExpr = this.createExpressionNode<SequenceExpression>(
      T_SEQUENCE_EXPRESSION,
      {
        exprs,
        loose,
      },
      start,
      endToken,
    );

    // --- Check for "loose" sequence expression
    if (loose) {
      leftExpr = this.convertToArrayDestructure(leftExpr);
    }

    // --- Done.
    return leftExpr;
  }

  /**
   * conditionalOrSpreadOrAsgnOrArrowExpr
   *   : nullCoalescingExpr ( "?" expr ":" expr )?
   *   | "..." nullCoalescingExpr
   *   | identifier "=" expr
   *   ;
   */
  private parseCondOrSpreadOrAsgnOrArrowExpr(): Expression | null {
    const startToken = this._lexer.peek();
    if (startToken.type === TokenType.Spread) {
      // --- Spread expression
      this._lexer.get();
      const spreadOperand = this.parseNullCoalescingExpr();
      return spreadOperand
        ? this.createExpressionNode<SpreadExpression>(
            T_SPREAD_EXPRESSION,
            {
              expr: spreadOperand,
            },
            startToken,
            spreadOperand.endToken,
          )
        : null;
    }

    if (startToken.type === TokenType.Function) {
      const funcDecl = this.parseFunctionDeclaration(true);
      return funcDecl
        ? this.createExpressionNode<ArrowExpression>(
            T_ARROW_EXPRESSION,
            {
              name: funcDecl.id.name,
              args: funcDecl.args,
              statement: funcDecl.stmt,
            },
            startToken,
            funcDecl.endToken,
          )
        : null;
    }

    const otherExpr = this.parseNullCoalescingExpr();
    if (!otherExpr) {
      return null;
    }

    const nextToken = this._lexer.peek();
    if (nextToken.type === TokenType.Arrow) {
      // --- It is an arrow expression
      return this.parseArrowExpression(startToken, otherExpr);
    }

    if (nextToken.type === TokenType.QuestionMark) {
      this._lexer.get();
      // --- Conditional expression
      const trueExpr = this.getExpression(false);
      this.expectToken(TokenType.Colon);
      const falseExpr = this.getExpression(false);

      return this.createExpressionNode<ConditionalExpression>(
        T_CONDITIONAL_EXPRESSION,
        {
          cond: otherExpr,
          thenE: trueExpr,
          elseE: falseExpr,
        },
        startToken,
        falseExpr!.endToken,
      );
    }

    if (tokenTraits[nextToken.type].isAssignment) {
      // --- Assignment
      this._lexer.get();
      const expr = this.getExpression();
      return expr
        ? this.createExpressionNode<AssignmentExpression>(
            T_ASSIGNMENT_EXPRESSION,
            {
              leftValue: otherExpr,
              op: nextToken.text,
              expr,
            },
            startToken,
            expr.endToken,
          )
        : null;
    }

    return otherExpr;
  }

  /**
   * Parses an arrow expression
   * @param start Start token
   * @param left Expression to the left from the arrow
   */
  private parseArrowExpression(start: Token, left: Expression): ArrowExpression | null {
    // --- Check the left expression
    let isValid: boolean;
    const args: Expression[] = [];
    switch (left.type) {
      case T_NO_ARG_EXPRESSION:
        isValid = true;
        break;
      case T_IDENTIFIER:
        isValid = (left.parenthesized ?? 0) <= 1;
        args.push(left);
        break;
      case T_SEQUENCE_EXPRESSION:
        isValid = left.parenthesized === 1;
        let spreadFound = false;
        if (isValid) {
          for (const expr of left.exprs) {
            if (spreadFound) {
              isValid = false;
              break;
            }
            switch (expr.type) {
              case T_IDENTIFIER:
                isValid = !expr.parenthesized;
                args.push(expr);
                break;
              case T_OBJECT_LITERAL: {
                isValid = !expr.parenthesized;
                if (isValid) {
                  const des = this.convertToObjectDestructure(expr);
                  if (des) args.push(des);
                }
                break;
              }
              case T_ARRAY_LITERAL: {
                isValid = !expr.parenthesized;
                if (isValid) {
                  const des = this.convertToArrayDestructure(expr);
                  if (des) args.push(des);
                }
                break;
              }
              case T_SPREAD_EXPRESSION: {
                spreadFound = true;
                if (expr.expr.type !== T_IDENTIFIER) {
                  isValid = false;
                  break;
                }
                args.push(expr);
                break;
              }
              default:
                isValid = false;
                break;
            }
          }
        }
        break;
      case T_OBJECT_LITERAL:
        isValid = left.parenthesized === 1;
        if (isValid) {
          const des = this.convertToObjectDestructure(left);
          if (des) args.push(des);
        }
        break;
      case T_ARRAY_LITERAL:
        isValid = left.parenthesized === 1;
        if (isValid) {
          const des = this.convertToArrayDestructure(left);
          if (des) args.push(des);
        }
        break;
      case T_SPREAD_EXPRESSION:
        isValid = left.expr.type === T_IDENTIFIER;
        if (isValid) {
          args.push(left);
        }
        break;
      default:
        isValid = false;
    }
    if (!isValid) {
      this.reportError("W010", start);
      return null;
    }

    // --- Skip the arrow token
    this._lexer.get();

    // --- Get arrow expression statements
    const statement = this.parseStatement(false);
    return statement
      ? this.createExpressionNode<ArrowExpression>(
          T_ARROW_EXPRESSION,
          {
            args,
            statement,
          },
          start,
          statement.endToken,
        )
      : null;
  }

  /**
   * nullCoalescingExpr
   *   : logicalOrExpr ( "??" logicalOrExpr )?
   *   ;
   */
  private parseNullCoalescingExpr(): Expression | null {
    const startToken = this._lexer.peek();
    let leftExpr = this.parseLogicalOrExpr();
    if (!leftExpr) {
      return null;
    }
    while (this.skipToken(TokenType.NullCoalesce)) {
      const rightExpr = this.parseLogicalOrExpr();
      if (!rightExpr) {
        this.reportError("W001");
        return null;
      }
      let endToken = rightExpr.endToken;
      leftExpr = this.createExpressionNode<BinaryExpression>(
        T_BINARY_EXPRESSION,
        {
          op: "??",
          left: leftExpr,
          right: rightExpr,
        },
        startToken,
        endToken,
      );
    }
    return leftExpr;
  }

  /**
   * logicalOrExpr
   *   : logicalAndExpr ( "||" logicalAndExpr )?
   *   ;
   */
  private parseLogicalOrExpr(): Expression | null {
    const startToken = this._lexer.peek();
    let leftExpr = this.parseLogicalAndExpr();
    if (!leftExpr) {
      return null;
    }

    while (this.skipToken(TokenType.LogicalOr)) {
      const rightExpr = this.parseLogicalAndExpr();
      if (!rightExpr) {
        this.reportError("W001");
        return null;
      }
      let endToken = rightExpr.endToken;
      leftExpr = this.createExpressionNode<BinaryExpression>(
        T_BINARY_EXPRESSION,
        {
          op: "||",
          left: leftExpr,
          right: rightExpr,
        },
        startToken,
        endToken,
      );
    }
    return leftExpr;
  }

  /**
   * logicalAndExpr
   *   : bitwiseOrExpr ( "&&" bitwiseOrExpr )?
   *   ;
   */
  private parseLogicalAndExpr(): Expression | null {
    const startToken = this._lexer.peek();
    let leftExpr = this.parseBitwiseOrExpr();
    if (!leftExpr) {
      return null;
    }

    while (this.skipToken(TokenType.LogicalAnd)) {
      const rightExpr = this.parseBitwiseOrExpr();
      if (!rightExpr) {
        this.reportError("W001");
        return null;
      }
      let endToken = rightExpr.endToken;
      leftExpr = this.createExpressionNode<BinaryExpression>(
        T_BINARY_EXPRESSION,
        {
          op: "&&",
          left: leftExpr,
          right: rightExpr,
        },
        startToken,
        endToken,
      );
    }
    return leftExpr;
  }

  /**
   * bitwiseOrExpr
   *   : bitwiseXorExpr ( "|" bitwiseXorExpr )?
   *   ;
   */
  private parseBitwiseOrExpr(): Expression | null {
    const startToken = this._lexer.peek();
    let leftExpr = this.parseBitwiseXorExpr();
    if (!leftExpr) {
      return null;
    }

    while (this.skipToken(TokenType.BitwiseOr)) {
      const rightExpr = this.parseBitwiseXorExpr();
      if (!rightExpr) {
        this.reportError("W001");
        return null;
      }
      let endToken = rightExpr.endToken;
      leftExpr = this.createExpressionNode<BinaryExpression>(
        T_BINARY_EXPRESSION,
        {
          op: "|",
          left: leftExpr,
          right: rightExpr,
        },
        startToken,
        endToken,
      );
    }
    return leftExpr;
  }

  /**
   * bitwiseXorExpr
   *   : bitwiseAndExpr ( "^" bitwiseAndExpr )?
   *   ;
   */
  private parseBitwiseXorExpr(): Expression | null {
    const startToken = this._lexer.peek();
    let leftExpr = this.parseBitwiseAndExpr();
    if (!leftExpr) {
      return null;
    }

    while (this.skipToken(TokenType.BitwiseXor)) {
      const rightExpr = this.parseBitwiseAndExpr();
      if (!rightExpr) {
        this.reportError("W001");
        return null;
      }
      let endToken = rightExpr.endToken;
      leftExpr = this.createExpressionNode<BinaryExpression>(
        T_BINARY_EXPRESSION,
        {
          op: "^",
          left: leftExpr,
          right: rightExpr,
        },
        startToken,
        endToken,
      );
    }
    return leftExpr;
  }

  /**
   * bitwiseAndExpr
   *   : equExpr ( "&" equExpr )?
   *   ;
   */
  private parseBitwiseAndExpr(): Expression | null {
    const startToken = this._lexer.peek();
    let leftExpr = this.parseEquExpr();
    if (!leftExpr) {
      return null;
    }

    while (this.skipToken(TokenType.BitwiseAnd)) {
      const rightExpr = this.parseEquExpr();
      if (!rightExpr) {
        this.reportError("W001");
        return null;
      }
      let endToken = rightExpr.endToken;
      leftExpr = this.createExpressionNode<BinaryExpression>(
        T_BINARY_EXPRESSION,
        {
          op: "&",
          left: leftExpr,
          right: rightExpr,
        },
        startToken,
        endToken,
      );
    }
    return leftExpr;
  }

  /**
   * equExpr
   *   : relOrInExpr ( ( "==" | "!=" | "===" | "!==" ) relOrInExpr )?
   *   ;
   */
  private parseEquExpr(): Expression | null {
    const startToken = this._lexer.peek();
    let leftExpr = this.parseRelOrInExpr();
    if (!leftExpr) {
      return null;
    }

    let opType: Token | null;
    while (
      (opType = this.skipTokens(
        TokenType.Equal,
        TokenType.StrictEqual,
        TokenType.NotEqual,
        TokenType.StrictNotEqual,
      ))
    ) {
      const rightExpr = this.parseRelOrInExpr();
      if (!rightExpr) {
        this.reportError("W001");
        return null;
      }
      let endToken = rightExpr.endToken;
      leftExpr = this.createExpressionNode<BinaryExpression>(
        T_BINARY_EXPRESSION,
        {
          op: opType.text,
          left: leftExpr,
          right: rightExpr,
        },
        startToken,
        endToken,
      );
    }
    return leftExpr;
  }

  /**
   * relOrInExpr
   *   : shiftExpr ( ( "<" | "<=" | ">" | ">=", "in" ) shiftExpr )?
   *   ;
   */
  private parseRelOrInExpr(): Expression | null {
    const startToken = this._lexer.peek();
    let leftExpr = this.parseShiftExpr();
    if (!leftExpr) {
      return null;
    }

    let opType: Token | null;
    while (
      (opType = this.skipTokens(
        TokenType.LessThan,
        TokenType.LessThanOrEqual,
        TokenType.GreaterThan,
        TokenType.GreaterThanOrEqual,
        TokenType.In,
      ))
    ) {
      const rightExpr = this.parseShiftExpr();
      if (!rightExpr) {
        this.reportError("W001");
        return null;
      }
      let endToken = rightExpr.endToken;
      leftExpr = this.createExpressionNode<BinaryExpression>(
        T_BINARY_EXPRESSION,
        {
          op: opType.text,
          left: leftExpr,
          right: rightExpr,
        },
        startToken,
        endToken,
      );
    }
    return leftExpr;
  }

  /**
   * shiftExpr
   *   : addExpr ( ( "<<" | ">>" | ">>>" ) addExpr )?
   *   ;
   */
  private parseShiftExpr(): Expression | null {
    const startToken = this._lexer.peek();
    let leftExpr = this.parseAddExpr();
    if (!leftExpr) {
      return null;
    }

    let opType: Token | null;
    while (
      (opType = this.skipTokens(
        TokenType.ShiftLeft,
        TokenType.ShiftRight,
        TokenType.SignedShiftRight,
      ))
    ) {
      const rightExpr = this.parseAddExpr();
      if (!rightExpr) {
        this.reportError("W001");
        return null;
      }
      let endToken = rightExpr.endToken;
      leftExpr = this.createExpressionNode<BinaryExpression>(
        T_BINARY_EXPRESSION,
        {
          op: opType.text,
          left: leftExpr,
          right: rightExpr,
        },
        startToken,
        endToken,
      );
    }
    return leftExpr;
  }

  /**
   * addExpr
   *   : multExpr ( ( "+" | "-" ) multExpr )?
   *   ;
   */
  private parseAddExpr(): Expression | null {
    const startToken = this._lexer.peek();
    let leftExpr = this.parseMultExpr();
    if (!leftExpr) {
      return null;
    }

    let opType: Token | null;
    while ((opType = this.skipTokens(TokenType.Plus, TokenType.Minus))) {
      const rightExpr = this.parseMultExpr();
      if (!rightExpr) {
        this.reportError("W001");
        return null;
      }
      let endToken = rightExpr.endToken;
      leftExpr = this.createExpressionNode<BinaryExpression>(
        T_BINARY_EXPRESSION,
        {
          op: opType.text,
          left: leftExpr,
          right: rightExpr,
        },
        startToken,
        endToken,
      );
    }
    return leftExpr;
  }

  /**
   * multExpr
   *   : exponentialExpr ( ( "*" | "/" | "%") exponentialExpr )?
   *   ;
   */
  private parseMultExpr(): Expression | null {
    const startToken = this._lexer.peek();
    let leftExpr = this.parseExponentialExpr();
    if (!leftExpr) {
      return null;
    }

    let opType: Token | null;
    while ((opType = this.skipTokens(TokenType.Multiply, TokenType.Divide, TokenType.Remainder))) {
      const rightExpr = this.parseExponentialExpr();
      if (!rightExpr) {
        this.reportError("W001");
        return null;
      }
      let endToken = rightExpr.endToken;
      leftExpr = this.createExpressionNode<BinaryExpression>(
        T_BINARY_EXPRESSION,
        {
          op: opType.text,
          left: leftExpr,
          right: rightExpr,
        },
        startToken,
        endToken,
      );
    }
    return leftExpr;
  }

  /**
   * exponentialExpr
   *   : unaryExpr ( "**" unaryExpr )?
   *   ;
   */
  private parseExponentialExpr(): Expression | null {
    const startToken = this._lexer.peek();
    let leftExpr = this.parseUnaryOrPrefixExpr();
    if (!leftExpr) {
      return null;
    }

    let opType: Token | null;
    let count = 0;
    while ((opType = this.skipToken(TokenType.Exponent))) {
      let rightExpr = this.parseUnaryOrPrefixExpr();
      if (!rightExpr) {
        this.reportError("W001");
        return null;
      }
      let endToken = rightExpr.endToken;
      if (count === 0) {
        leftExpr = this.createExpressionNode<BinaryExpression>(
          T_BINARY_EXPRESSION,
          {
            op: opType.text,
            left: leftExpr,
            right: rightExpr,
          },
          startToken,
          endToken,
        );
      } else {
        const prevLeft = leftExpr as BinaryExpression;
        leftExpr = this.createExpressionNode<BinaryExpression>(
          T_BINARY_EXPRESSION,
          {
            op: opType.text,
            left: prevLeft.left,
            right: {
              type: T_BINARY_EXPRESSION,
              op: opType.text,
              left: prevLeft.right,
              right: rightExpr,
            },
          },
          startToken,
          endToken,
        );
      }
      count++;
    }
    return leftExpr;
  }

  /**
   * unaryExpr
   *   : ( "typeof" | "delete" | "+" | "-" | "~" | "!" ) memberOrInvocationExpr
   *   | memberOrInvocationExpr
   *   ;
   */
  private parseUnaryOrPrefixExpr(): Expression | null {
    const startToken = this._lexer.peek();
    if (tokenTraits[startToken.type].canBeUnary) {
      this._lexer.get();
      const unaryOperand = this.parseUnaryOrPrefixExpr();
      if (!unaryOperand) {
        return null;
      }
      return this.createExpressionNode<UnaryExpression>(
        T_UNARY_EXPRESSION,
        {
          op: startToken.text,
          expr: unaryOperand,
        },
        startToken,
        unaryOperand.endToken,
      );
    }

    if (startToken.type === TokenType.IncOp || startToken.type === TokenType.DecOp) {
      this._lexer.get();
      const prefixOperand = this.parseMemberOrInvocationExpr();
      if (!prefixOperand) {
        return null;
      }
      return this.createExpressionNode<PrefixOpExpression>(
        T_PREFIX_OP_EXPRESSION,
        {
          op: startToken.text,
          expr: prefixOperand,
        },
        startToken,
        prefixOperand.endToken,
      );
    }

    // --- Not unary or prefix
    return this.parseMemberOrInvocationExpr();
  }

  /**
   * memberOrInvocationExpr
   *   : primaryExpr "(" functionArgs ")"
   *   | primaryExpr "." identifier
   *   | primaryExpr "?." identifier
   *   | primaryExpr "[" expr "]"
   *   ;
   */
  private parseMemberOrInvocationExpr(): Expression | null {
    const startToken = this._lexer.peek();
    let primary = this.parsePrimaryExpr();
    if (!primary) {
      return null;
    }

    let exitLoop = false;
    do {
      const currentStart = this._lexer.peek();

      switch (currentStart.type) {
        case TokenType.LParent: {
          this._lexer.get();
          let args: Expression[] = [];
          if (this._lexer.peek().type !== TokenType.RParent) {
            const expr = this.parseExpr();
            if (!expr) {
              this.reportError("W001");
              return null;
            }
            args = expr.type === T_SEQUENCE_EXPRESSION ? expr.exprs : [expr];
          }
          const endToken = this._lexer.peek();
          this.expectToken(TokenType.RParent, "W006");
          primary = this.createExpressionNode<FunctionInvocationExpression>(
            T_FUNCTION_INVOCATION_EXPRESSION,
            {
              obj: primary,
              arguments: args,
            },
            startToken,
            endToken,
          );
          break;
        }

        case TokenType.Dot:
        case TokenType.OptionalChaining:
          this._lexer.get();
          const member = this._lexer.get();
          const memberTrait = tokenTraits[member.type];
          if (!memberTrait.keywordLike) {
            this.reportError("W003");
            return null;
          }
          primary = this.createExpressionNode<MemberAccessExpression>(
            T_MEMBER_ACCESS_EXPRESSION,
            {
              obj: primary,
              member: member.text,
              opt: currentStart.type === TokenType.OptionalChaining,
            },
            startToken,
            member,
          );
          break;

        case TokenType.LSquare:
          this._lexer.get();
          const memberExpr = this.getExpression();
          if (!memberExpr) {
            return null;
          }
          const endToken = this._lexer.peek();
          this.expectToken(TokenType.RSquare, "W005");
          primary = this.createExpressionNode<CalculatedMemberAccessExpression>(
            T_CALCULATED_MEMBER_ACCESS_EXPRESSION,
            {
              obj: primary,
              member: memberExpr,
            },
            startToken,
            endToken,
          );
          break;

        default:
          exitLoop = true;
          break;
      }
    } while (!exitLoop);

    // --- Check for postfix operators
    const nextToken = this._lexer.peek();
    if (nextToken.type === TokenType.IncOp || nextToken.type === TokenType.DecOp) {
      this._lexer.get();
      return this.createExpressionNode<PostfixOpExpression>(
        T_POSTFIX_OP_EXPRESSION,
        {
          op: nextToken.text,
          expr: primary,
        },
        startToken,
        nextToken,
      );
    }

    return primary;
  }

  /**
   * primaryExpr
   *   : literal
   *   | identifier
   *   | "::" identifier
   *   | "$item"
   *   | "(" expr ")"
   *   ;
   */
  private parsePrimaryExpr(): Expression | null {
    const start = this._lexer.peek();
    switch (start.type) {
      case TokenType.LParent:
        // --- Parenthesised or no-arg expression
        this._lexer.get();
        if (this._lexer.peek().type === TokenType.RParent) {
          // --- No-arg
          const endToken = this._lexer.get();
          return this.createExpressionNode<NoArgExpression>(
            T_NO_ARG_EXPRESSION,
            {},
            start,
            endToken,
          );
        }

        // --- Parenthesized
        let parenthesizedExpr = this.parseExpr();
        if (!parenthesizedExpr) {
          return null;
        }
        const endToken = this._lexer.peek();
        this.expectToken(TokenType.RParent, "W006");
        return {
          ...parenthesizedExpr,
          parenthesized: (parenthesizedExpr.parenthesized ?? 0) + 1,
          startToken: start,
          endToken,
        };

      case TokenType.Identifier: {
        const idToken = this._lexer.get();
        return this.createExpressionNode<Identifier>(
          T_IDENTIFIER,
          {
            name: idToken.text,
          },
          idToken,
          idToken,
        );
      }

      case TokenType.Global: {
        this._lexer.get();
        const idToken = this._lexer.get();
        if (idToken.type !== TokenType.Identifier) {
          this.reportError("W003");
          return null;
        }
        return this.createExpressionNode<Identifier>(
          T_IDENTIFIER,
          {
            name: idToken.text,
            isGlobal: true,
          },
          idToken,
          idToken,
        );
      }
      case TokenType.Backtick:
        return this.parseTemplateLiteral();

      case TokenType.False:
      case TokenType.True:
        this._lexer.get();
        return this.createExpressionNode<Literal>(
          T_LITERAL,
          {
            value: start.type === TokenType.True,
          },
          start,
          start,
        );

      case TokenType.BinaryLiteral:
        this._lexer.get();
        return this.parseBinaryLiteral(start);

      case TokenType.DecimalLiteral:
        this._lexer.get();
        return this.parseDecimalLiteral(start);

      case TokenType.HexadecimalLiteral:
        this._lexer.get();
        return this.parseHexadecimalLiteral(start);

      case TokenType.RealLiteral:
        this._lexer.get();
        return this.parseRealLiteral(start);

      case TokenType.StringLiteral:
        this._lexer.get();
        return this.parseStringLiteral(start);

      case TokenType.Infinity:
        this._lexer.get();
        return this.createExpressionNode<Literal>(
          T_LITERAL,
          {
            value: Infinity,
          },
          start,
          start,
        );

      case TokenType.NaN:
        this._lexer.get();
        return this.createExpressionNode<Literal>(
          T_LITERAL,
          {
            value: NaN,
          },
          start,
          start,
        );

      case TokenType.Null:
        this._lexer.get();
        return this.createExpressionNode<Literal>(
          T_LITERAL,
          {
            value: null,
          },
          start,
          start,
        );

      case TokenType.Undefined:
        this._lexer.get();
        return this.createExpressionNode<Literal>(
          T_LITERAL,
          {
            value: undefined,
          },
          start,
          start,
        );

      case TokenType.LSquare:
        return this.parseArrayLiteral();

      case TokenType.LBrace:
        return this.parseObjectLiteral();

      case TokenType.Divide:
        return this.parseRegExpLiteral();
    }

    return null;
  }

  private parseTemplateLiteral(): TemplateLiteralExpression {
    const startToken = this._lexer.get();
    this._lexer.setStartingPhaseToTemplateLiteral();
    const segments: (Literal | Expression)[] = [];
    loop: while (true) {
      let nextToken = this._lexer.peek();
      switch (nextToken.type) {
        case TokenType.StringLiteral:
          this._lexer.get();
          const str = this.parseStringLiteral(nextToken, false);
          segments.push(str);
          break;
        case TokenType.DollarLBrace:
          this._lexer.get();
          const innerExpr = this.parseExpr();
          segments.push(innerExpr);
          this.expectToken(TokenType.RBrace, "W004");
          this._lexer.setStartingPhaseToTemplateLiteral();
          break;
        case TokenType.Backtick:
          break loop;
        default:
          this.reportError("W004");
      }
    }
    const endToken = this._lexer.get();
    return this.createExpressionNode<TemplateLiteralExpression>(
      T_TEMPLATE_LITERAL_EXPRESSION,
      { segments },
      startToken,
      endToken,
    );
  }

  /**
   * Parses an array literal
   */
  private parseArrayLiteral(): ArrayLiteral | null {
    const start = this._lexer.get();
    let expressions: Expression[] = [];
    if (this._lexer.peek().type !== TokenType.RSquare) {
      const expr = this.getExpression();
      if (expr) {
        expressions = expr.type === T_SEQUENCE_EXPRESSION ? expr.exprs : [expr];
      }
    }
    const endToken = this._lexer.peek();
    this.expectToken(TokenType.RSquare);
    return this.createExpressionNode<ArrayLiteral>(
      T_ARRAY_LITERAL,
      {
        items: expressions,
      },
      start,
      endToken,
    );
  }

  /**
   * Parses an object literal
   */
  private parseObjectLiteral(): ObjectLiteral | null {
    const start = this._lexer.get();
    let props: (SpreadExpression | [Expression, Expression])[] = [];
    if (this._lexer.peek().type !== TokenType.RBrace) {
      while (this._lexer.peek().type !== TokenType.RBrace) {
        // --- Check the next token
        const nextToken = this._lexer.peek();
        const traits = tokenTraits[nextToken.type];
        let nameExpr: Expression | null;

        // --- Get property name or calculated property name
        if (traits.expressionStart) {
          if (nextToken.type === TokenType.LSquare) {
            this._lexer.get();
            nameExpr = this.getExpression();
            if (!nameExpr) {
              return null;
            }
            this.expectToken(TokenType.RSquare, "W005");
            nameExpr = this.createExpressionNode<SequenceExpression>(
              T_SEQUENCE_EXPRESSION,
              {
                exprs: [nameExpr],
              },
              start,
            );
          } else if (traits.isPropLiteral) {
            nameExpr = this.getExpression(false);
            if (!nameExpr) {
              return null;
            }
            if (
              nameExpr.type !== T_IDENTIFIER &&
              nameExpr.type !== T_LITERAL &&
              nameExpr.type !== T_SPREAD_EXPRESSION
            ) {
              this.reportError("W007");
              return null;
            }
          } else {
            this.reportError("W007");
            return null;
          }
        } else if (traits.keywordLike) {
          nameExpr = {
            type: T_IDENTIFIER,
            nodeId: createXmlUiTreeNodeId(),
            name: nextToken.text,
            startToken: nextToken,
            endToken: nextToken,
          };
          this._lexer.get();
        } else {
          this.reportError("W001");
          return null;
        }

        const nameType = nameExpr.type;
        if (nameType === T_SPREAD_EXPRESSION) {
          props.push(nameExpr);
        } else {
          if (nameType === T_LITERAL) {
            const val = nameExpr.value;
            if (typeof val !== "number" && typeof val !== "string") {
              this.expectToken(TokenType.RBrace, "W007");
              return null;
            }
          }

          // --- Value is optional, when we have a name
          let valueExpr: Expression | null = null;

          if (nameType === T_IDENTIFIER) {
            const nameFollowerToken = this._lexer.peek();
            if (
              nameFollowerToken.type === TokenType.Comma ||
              nameFollowerToken.type === TokenType.RBrace
            ) {
              valueExpr = { ...nameExpr };
            }
          }

          // --- Move to property value
          if (!valueExpr) {
            this.expectToken(TokenType.Colon, "W008");
            valueExpr = this.getExpression(false);
            if (!valueExpr) {
              return null;
            }
          }

          props.push([nameExpr, valueExpr]);
        }

        // --- Test property termination
        const next = this._lexer.peek().type;
        if (next === TokenType.Comma) {
          this._lexer.get();
        } else {
          if (next !== TokenType.RBrace) {
            break;
          }
        }
      }
    }

    const endToken = this._lexer.peek();
    this.expectToken(TokenType.RBrace, "W004");
    return this.createExpressionNode<ObjectLiteral>(
      T_OBJECT_LITERAL,
      {
        props,
      },
      start,
      endToken,
    );
  }

  private parseRegExpLiteral(): Literal | null {
    const startToken = this._lexer.peek();
    const result = this._lexer.getRegEx();
    if (result.success) {
      return this.createExpressionNode<Literal>(
        T_LITERAL,
        {
          value: new RegExp(result.pattern!, result.flags),
        },
        startToken,
        this._lexer.peek(),
      );
    }
    this.reportError("W002", startToken, result.pattern ?? "");
    return null;
  }

  /**
   * Gets an expression
   */
  private getExpression(allowSequence = true): Expression | null {
    const expr = this.parseExpr(allowSequence);
    if (expr) {
      return expr;
    }
    this.reportError("W001");
    return null;
  }

  // ==========================================================================
  // Helpers

  /**
   * Tests the type of the next token
   * @param type Expected token type
   * @param errorCode Error to raise if the next token is not expected
   * @param allowEof Allow an EOF instead of the expected token?
   */
  private expectToken(type: TokenType, errorCode?: ErrorCodes, allowEof?: boolean): Token | null {
    const next = this._lexer.peek();
    if (next.type === type || (allowEof && next.type === TokenType.Eof)) {
      // --- Skip the expected token
      return this._lexer.get();
    }
    this.reportError(errorCode ?? "W002", next, next.text);
    return null;
  }

  /**
   * Skips the next token if the type is the specified one
   * @param type Token type to check
   */
  private skipToken(type: TokenType): Token | null {
    const next = this._lexer.peek();
    if (next.type === type) {
      this._lexer.get();
      return next;
    }
    return null;
  }

  /**
   * Skips the next token if the type is the specified one
   * @param types Token types to check
   */
  private skipTokens(...types: TokenType[]): Token | null {
    const next = this._lexer.peek();
    for (const type of types) {
      if (next.type === type) {
        this._lexer.get();
        return next;
      }
    }
    return null;
  }

  /**
   * Reports the specified error
   * @param errorCode Error code
   * @param token Token that represents the error's position
   * @param options Error message options
   */
  private reportError(errorCode: ErrorCodes, token?: Token, ...options: any[]): void {
    let errorText: string = errorMessages[errorCode] ?? "Unkonwn error";
    if (options) {
      options.forEach(
        (o, idx) => (errorText = replace(errorText, `{${idx}}`, options[idx].toString())),
      );
    }
    if (!token) {
      token = this._lexer.peek();
    }
    this._parseErrors.push({
      code: errorCode,
      text: errorText,
      line: token.startLine,
      column: token.startColumn,
    });
    throw new ParserError(errorText, errorCode);

    function replace(input: string, placeholder: string, replacement: string): string {
      do {
        input = input.replace(placeholder, replacement);
      } while (input.includes(placeholder));
      return input;
    }
  }

  /**
   * Creates an expression node
   * @param type Expression type
   * @param stump Stump properties
   * @param startToken The token that starts the expression
   * @param endToken The token that ends the expression
   * @param source Expression source code to store to the node
   */
  private createNode<T extends ScripNodeBase>(
    type: ScripNodeBase["type"],
    stump: any,
    startToken: Token,
    endToken?: Token,
  ): T {
    if (!endToken) {
      endToken = this._lexer.peek();
    }
    return Object.assign({}, stump, {
      type,
      startToken,
      endToken,
    } as ScripNodeBase);
  }

  /**
   * Creates an expression node
   * @param type Expression type
   * @param stump Stump properties
   * @param startToken The token that starts the expression
   * @param endToken The token that ends the expression
   * @param source Expression source code to store to the node
   */
  private createExpressionNode<T extends Expression>(
    type: Expression["type"],
    stump: any = {},
    startToken?: Token,
    endToken?: Token,
  ): T {
    if (!endToken) {
      endToken = this._lexer.peek();
    }
    if (!startToken) {
      startToken = endToken;
    }
    return Object.assign({}, stump, {
      type,
      nodeId: createXmlUiTreeNodeId(),
      startToken,
      endToken,
    });
  }

  /**
   * Creates a statement node
   * @param type Statement type
   * @param stump Stump properties
   * @param startToken The token that starts the statement
   * @param endToken The token that ends the statement
   */
  private createStatementNode<T extends Statement>(
    type: Statement["type"],
    stump: any,
    startToken?: Token,
    endToken?: Token,
  ): T {
    return Object.assign({}, stump, {
      type,
      nodeId: createXmlUiTreeNodeId(),
      startToken,
      endToken,
    } as Statement);
  }

  /**
   * Parses a binary literal
   * @param token Literal token
   */
  private parseBinaryLiteral(token: Token): Literal {
    let value: number | bigint;
    const bigValue = BigInt(token.text.replace(/[_']/g, ""));
    if (bigValue < Number.MIN_SAFE_INTEGER || bigValue > Number.MAX_SAFE_INTEGER) {
      value = bigValue;
    } else {
      value = parseInt(token.text.substring(2).replace(/[_']/g, ""), 2);
    }
    return this.createExpressionNode<Literal>(
      T_LITERAL,
      {
        value,
      },
      token,
      token,
    );
  }

  /**
   * Parses a decimal literal
   * @param token Literal token
   */
  private parseDecimalLiteral(token: Token): Literal {
    let value: number | bigint;
    const bigValue = BigInt(token.text.replace(/[_']/g, ""));
    if (bigValue < Number.MIN_SAFE_INTEGER || bigValue > Number.MAX_SAFE_INTEGER) {
      value = bigValue;
    } else {
      value = parseInt(token.text.replace(/[_']/g, ""), 10);
    }
    return this.createExpressionNode<Literal>(
      T_LITERAL,
      {
        value,
      },
      token,
      token,
    );
  }

  /**
   * Parses a hexadecimal literal
   * @param token Literal token
   */
  private parseHexadecimalLiteral(token: Token): Literal {
    let value: number | bigint;
    const bigValue = BigInt(token.text.replace(/[_']/g, ""));
    if (bigValue < Number.MIN_SAFE_INTEGER || bigValue > Number.MAX_SAFE_INTEGER) {
      value = bigValue;
    } else {
      value = parseInt(token.text.substring(2).replace(/[_']/g, ""), 16);
    }
    return this.createExpressionNode<Literal>(
      T_LITERAL,
      {
        value,
      },
      token,
      token,
    );
  }

  /**
   * Parses a real literal
   * @param token Literal token
   */
  private parseRealLiteral(token: Token): Literal {
    let value = parseFloat(token.text.replace(/[_']/g, ""));
    return this.createExpressionNode<Literal>(
      T_LITERAL,
      {
        value,
      },
      token,
      token,
    );
  }

  /**
   * Converts a string token to intrinsic string
   * @param token Literal token
   */
  private parseStringLiteral(token: Token, quoteSurrounded: boolean = true): Literal {
    let input = token.text;
    if (quoteSurrounded) {
      input = token.text.length < 2 ? "" : input.substring(1, input.length - 1);
    }
    let result = "";
    let state: StrParseState = StrParseState.Normal;
    let collect = 0;
    for (const ch of input) {
      switch (state) {
        case StrParseState.Normal:
          if (ch === "\\") {
            state = StrParseState.Backslash;
          } else {
            result += ch;
          }
          break;

        case StrParseState.Backslash:
          state = StrParseState.Normal;
          switch (ch) {
            case "b":
              result += "\b";
              break;
            case "f":
              result += "\f";
              break;
            case "n":
              result += "\n";
              break;
            case "r":
              result += "\r";
              break;
            case "t":
              result += "\t";
              break;
            case "v":
              result += "\v";
              break;
            case "S":
              result += "\xa0";
              break;
            case "0":
              result += String.fromCharCode(0x00);
              break;
            case "'":
              result += "'";
              break;
            case '"':
              result += '"';
              break;
            case "\\":
              result += "\\";
              break;
            case "x":
              state = StrParseState.X;
              break;
            case "u":
              state = StrParseState.UX1;
              break;
            default:
              result += ch;
              break;
          }
          break;

        case StrParseState.X:
          if (isHexaDecimal(ch)) {
            collect = parseInt(ch, 16);
            state = StrParseState.Xh;
          } else {
            result += "x";
            state = StrParseState.Normal;
          }
          break;

        case StrParseState.Xh:
          if (isHexaDecimal(ch)) {
            collect = collect * 0x10 + parseInt(ch, 16);
            result += String.fromCharCode(collect);
            state = StrParseState.Normal;
          } else {
            result += String.fromCharCode(collect);
            result += ch;
            state = StrParseState.Normal;
          }
          break;

        case StrParseState.UX1:
          if (ch === "{") {
            state = StrParseState.Ucp1;
            break;
          }
          if (isHexaDecimal(ch)) {
            collect = parseInt(ch, 16);
            state = StrParseState.UX2;
          } else {
            result += "x";
            state = StrParseState.Normal;
          }
          break;

        case StrParseState.UX2:
          if (isHexaDecimal(ch)) {
            collect = collect * 0x10 + parseInt(ch, 16);
            state = StrParseState.UX3;
          } else {
            result += String.fromCharCode(collect);
            result += ch;
            state = StrParseState.Normal;
          }
          break;

        case StrParseState.UX3:
          if (isHexaDecimal(ch)) {
            collect = collect * 0x10 + parseInt(ch, 16);
            state = StrParseState.UX4;
          } else {
            result += String.fromCharCode(collect);
            result += ch;
            state = StrParseState.Normal;
          }
          break;

        case StrParseState.UX4:
          if (isHexaDecimal(ch)) {
            collect = collect * 0x10 + parseInt(ch, 16);
            result += String.fromCharCode(collect);
            state = StrParseState.Normal;
          } else {
            result += String.fromCharCode(collect);
            result += ch;
            state = StrParseState.Normal;
          }
          break;

        case StrParseState.Ucp1:
          if (isHexaDecimal(ch)) {
            collect = parseInt(ch, 16);
            state = StrParseState.Ucp2;
          } else {
            result += "x";
            state = StrParseState.Normal;
          }
          break;

        case StrParseState.Ucp2:
          if (isHexaDecimal(ch)) {
            collect = collect * 0x10 + parseInt(ch, 16);
            state = StrParseState.Ucp3;
          } else {
            result += String.fromCharCode(collect);
            result += ch;
            state = StrParseState.Normal;
          }
          break;

        case StrParseState.Ucp3:
          if (isHexaDecimal(ch)) {
            collect = collect * 0x10 + parseInt(ch, 16);
            state = StrParseState.Ucp4;
          } else {
            result += String.fromCharCode(collect);
            result += ch;
            state = StrParseState.Normal;
          }
          break;

        case StrParseState.Ucp4:
          if (isHexaDecimal(ch)) {
            collect = collect * 0x10 + parseInt(ch, 16);
            state = StrParseState.Ucp5;
          } else {
            result += String.fromCharCode(collect);
            result += ch;
            state = StrParseState.Normal;
          }
          break;

        case StrParseState.Ucp5:
          if (isHexaDecimal(ch)) {
            collect = collect * 0x10 + parseInt(ch, 16);
            state = StrParseState.Ucp6;
          } else {
            result += String.fromCharCode(collect);
            result += ch;
            state = StrParseState.Normal;
          }
          break;

        case StrParseState.Ucp6:
          if (isHexaDecimal(ch)) {
            collect = collect * 0x10 + parseInt(ch, 16);
            state = StrParseState.UcpTail;
          } else {
            result += String.fromCharCode(collect);
            result += ch;
            state = StrParseState.Normal;
          }
          break;

        case StrParseState.UcpTail:
          result += String.fromCharCode(collect);
          if (ch !== "}") {
            result += ch;
          }
          state = StrParseState.Normal;
          break;
      }
    }

    // --- Handle the final machine state
    switch (state) {
      case StrParseState.Backslash:
        result += "\\";
        break;
      case StrParseState.X:
        result += "x";
        break;
      case StrParseState.Xh:
        result += String.fromCharCode(collect);
        break;
    }

    // --- Done
    return this.createExpressionNode<Literal>(
      T_LITERAL,
      {
        value: result,
      },
      token,
      token,
    );

    function isHexaDecimal(ch: string): boolean {
      return (ch >= "0" && ch <= "9") || (ch >= "a" && ch <= "f") || (ch >= "A" && ch <= "F");
    }
  }

  private convertToArrayDestructure(seq: SequenceExpression | ArrayLiteral): Destructure | null {
    const items = seq.type === T_SEQUENCE_EXPRESSION ? seq.exprs : seq.items;
    const result = this.createExpressionNode<Destructure>(
      T_DESTRUCTURE,
      { aDestr: [] },
      seq.startToken,
      seq.endToken,
    );

    // --- Convert all items
    for (const item of items) {
      let arrayD: ArrayDestructure | undefined;
      switch (item.type) {
        case T_NO_ARG_EXPRESSION:
          arrayD = this.createExpressionNode<ArrayDestructure>(
            T_ARRAY_DESTRUCTURE,
            {},
            item.startToken,
            item.endToken,
          );
          break;
        case T_IDENTIFIER:
          arrayD = this.createExpressionNode<ArrayDestructure>(
            T_ARRAY_DESTRUCTURE,
            { id: item.name },
            item.startToken,
            item.endToken,
          );
          break;
        case T_DESTRUCTURE:
          result.aDestr!.push(...item.aDestr!);
          break;
        case T_ARRAY_DESTRUCTURE:
          arrayD = item;
          break;
        case T_ARRAY_LITERAL: {
          const destructure = this.convertToArrayDestructure(item);
          if (destructure) {
            arrayD = this.createExpressionNode<ArrayDestructure>(
              T_ARRAY_DESTRUCTURE,
              {
                aDestr: destructure.aDestr,
              },
              item.startToken,
              item.endToken,
            );
          }
          break;
        }
        case T_OBJECT_DESTRUCTURE:
          arrayD = this.createExpressionNode<ArrayDestructure>(
            T_ARRAY_DESTRUCTURE,
            {
              oDestr: item,
            },
            item.startToken,
            item.endToken,
          );
          break;
        case T_OBJECT_LITERAL: {
          const destructure = this.convertToObjectDestructure(item);
          if (destructure) {
            arrayD = this.createExpressionNode<ArrayDestructure>(
              T_ARRAY_DESTRUCTURE,
              {
                oDestr: destructure.oDestr,
              },
              item.startToken,
              item.endToken,
            );
          }
          break;
        }

        default:
          this.reportError("W017");
          return null;
      }
      if (arrayD) result.aDestr?.push(arrayD);
    }

    // --- Done.
    return result;
  }

  private convertToObjectDestructure(objLit: ObjectLiteral): Destructure | null {
    const result = this.createExpressionNode<Destructure>(
      T_DESTRUCTURE,
      { oDestr: [] },
      objLit.startToken,
      objLit.endToken,
    );

    // --- Convert all items
    for (const prop of objLit.props) {
      if (Array.isArray(prop)) {
      } else {
        reportError("W018");
        return null;
      }

      const [propKey, propValue] = prop;
      if (propKey.type !== T_IDENTIFIER) {
        reportError("W018");
        return null;
      }

      let objD: ObjectDestructure | undefined;
      switch (propValue.type) {
        case T_IDENTIFIER:
          if (propValue.name === propKey.name) {
            objD = this.createExpressionNode<ObjectDestructure>(
              T_OBJECT_DESTRUCTURE,
              { id: propKey.name },
              propValue.startToken,
              propValue.endToken,
            );
          } else {
            objD = this.createExpressionNode<ObjectDestructure>(
              T_OBJECT_DESTRUCTURE,
              {
                id: propKey.name,
                alias: propValue.name,
              },
              propValue.startToken,
              propValue.endToken,
            );
          }
          break;
        case T_ARRAY_DESTRUCTURE: {
          objD = this.createExpressionNode<ObjectDestructure>(
            T_OBJECT_DESTRUCTURE,
            {
              id: propKey.name,
              aDestr: propValue,
            },
            propKey.startToken,
            propValue.endToken,
          );
          break;
        }
        case T_ARRAY_LITERAL: {
          const destructure = this.convertToArrayDestructure(propValue);
          if (destructure) {
            objD = this.createExpressionNode<ObjectDestructure>(
              T_OBJECT_DESTRUCTURE,
              {
                id: propKey.name,
                aDestr: destructure.aDestr,
              },
              propKey.startToken,
              propValue.endToken,
            );
          }
          break;
        }
        case T_OBJECT_DESTRUCTURE:
          objD = propValue;
          break;
        case T_OBJECT_LITERAL: {
          const destructure = this.convertToObjectDestructure(propValue);
          if (destructure) {
            objD = this.createExpressionNode<ObjectDestructure>(
              T_OBJECT_DESTRUCTURE,
              {
                id: propKey.name,
                oDestr: destructure.oDestr,
              },
              propKey.startToken,
              propValue.endToken,
            );
          }
          break;
        }
        default:
          this.reportError("W018");
          return null;
      }
      if (objD) result.oDestr?.push(objD);
    }

    // --- Done.
    return result;
  }

  /**
   * Tests if the specified token can be the start of an expression
   */
  private isExpressionStart(token: Token): boolean {
    return tokenTraits[token.type]?.expressionStart ?? false;
  }
}

```

