├── .github └── workflows │ ├── docs.yml │ └── node.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── THIRD_PARTY_NOTICES ├── api-documenter.json ├── api-extractor.json ├── baselines └── reference │ ├── api-report │ └── grammarkdown.api.md │ ├── assertions │ ├── constraints │ │ ├── basic.grammar.diagnostics │ │ ├── basic.grammar.emu.html │ │ ├── invalid.grammar.diagnostics │ │ └── invalid.grammar.emu.html │ ├── lookaheadAssertion.grammar.emu.html │ ├── noSymbolHere.grammar.diagnostics │ ├── noSymbolHere.grammar.emu.html │ ├── proseAssertion.2.grammar.emu.html │ ├── proseAssertion.3.grammar.emu.html │ └── proseAssertion.grammar.emu.html │ ├── collapsed │ ├── multiple.grammar.emu.html │ ├── multiple.grammar.grammar │ ├── multiple.grammar.html │ ├── multiple.grammar.md │ ├── multiple.grammar.nodes │ ├── multiple.grammar.tokens │ ├── single.grammar.emu.html │ ├── single.grammar.grammar │ ├── single.grammar.html │ ├── single.grammar.md │ ├── single.grammar.nodes │ └── single.grammar.tokens │ ├── ecmarkup │ └── grammarTest.grammar.emu.html │ ├── html │ ├── aroundProduction.grammar.diagnostics │ ├── aroundProduction.grammar.emu.html │ ├── aroundProduction.grammar.grammar │ ├── aroundProduction.grammar.nodes │ ├── aroundSymbols.grammar.emu.html │ ├── aroundTerminalInOneOf.grammar.emu.html │ ├── gh80.grammar.grammar │ ├── htmlComments.grammar.emu.html │ ├── htmlComments.grammar.grammar │ ├── htmlComments.grammar.html │ ├── mixed.grammar.emu.html │ ├── mixed.grammar.html │ └── mixed.grammar.md │ ├── meta │ ├── define │ │ └── byRegion.grammar.diagnostics │ └── line │ │ ├── lineMeta.grammar.diagnostics │ │ └── lineMeta.grammar.emu.html │ ├── nonterminals │ ├── withArguments.grammar.diagnostics │ └── withArguments.grammar.emu.html │ ├── oneOfList │ └── oneOfList.grammar.grammar │ ├── parameters │ ├── noUnusedParameters.grammar.diagnostics │ └── noUnusedParameters.grammar.emu.html │ ├── placeholder │ └── placeholder.grammar.emu.html │ ├── rightHandSide │ └── complex.grammar.emu.html │ ├── rightHandSideList │ ├── multipleItems.grammar.emu.html │ ├── multipleItems.grammar.grammar │ ├── multipleItems.grammar.html │ ├── multipleItems.grammar.md │ ├── multipleItems.grammar.nodes │ ├── multipleItems.grammar.tokens │ ├── singleItem.grammar.emu.html │ ├── singleItem.grammar.grammar │ ├── singleItem.grammar.html │ ├── singleItem.grammar.md │ ├── singleItem.grammar.nodes │ └── singleItem.grammar.tokens │ ├── specs │ ├── es2020.grammar.emu.html │ ├── es6.grammar.emu.html │ ├── es6.grammar.grammar │ ├── es6.grammar.html │ ├── es6.grammar.md │ ├── es6.grammar.nodes │ ├── es6.grammar.tokens │ ├── grammarkdown.grammar.emu.html │ ├── grammarkdown.grammar.grammar │ ├── grammarkdown.grammar.html │ ├── grammarkdown.grammar.md │ ├── grammarkdown.grammar.nodes │ ├── grammarkdown.grammar.tokens │ ├── test.grammar.diagnostics │ ├── test.grammar.emu.html │ ├── test.grammar.grammar │ ├── test.grammar.html │ ├── test.grammar.md │ ├── test.grammar.nodes │ ├── test.grammar.tokens │ ├── typescript.grammar.emu.html │ ├── typescript.grammar.grammar │ ├── typescript.grammar.html │ ├── typescript.grammar.md │ ├── typescript.grammar.nodes │ └── typescript.grammar.tokens │ └── terminals │ ├── unicodeCharacterLiteral.grammar.diagnostics │ ├── unicodeCharacterLiteral.grammar.emu.html │ ├── unicodeCharacterLiteral.grammar.grammar │ ├── unicodeCharacterLiteral.grammar.html │ ├── unicodeCharacterLiteral.grammar.md │ ├── unicodeCharacterLiteral.grammar.nodes │ ├── unicodeCharacterLiteral.grammar.tokens │ ├── unicodeCharacterLiteralRange.grammar.emu.html │ ├── unicodeCharacterLiteralRange.grammar.grammar │ ├── unicodeCharacterLiteralRange.grammar.html │ ├── unicodeCharacterLiteralRange.grammar.md │ ├── unicodeCharacterLiteralRange.grammar.nodes │ └── unicodeCharacterLiteralRange.grammar.tokens ├── bin └── grammarkdown ├── docfx.json ├── docs ├── api │ ├── bind.html │ ├── check.html │ ├── compiler.html │ ├── emit.html │ ├── grammarkdown.html │ ├── grammarkdown │ │ ├── argument.html │ │ ├── argumentlist.html │ │ ├── assertionbase.html │ │ ├── assertiontypes.html │ │ ├── asynchost.html │ │ ├── asynchostoptions.html │ │ ├── asyncsinglefilehost.html │ │ ├── binder.html │ │ ├── bindingtable.html │ │ ├── butnotsymbol.html │ │ ├── charactercodes.html │ │ ├── checker.html │ │ ├── commenttriviabase.html │ │ ├── commenttriviatypes.html │ │ ├── compileroptions.html │ │ ├── constraints.html │ │ ├── coreasynchost.html │ │ ├── coreasynchostoptions.html │ │ ├── coresynchost.html │ │ ├── coresynchostoptions.html │ │ ├── declarationtypes.html │ │ ├── define.html │ │ ├── diagnostic.html │ │ ├── diagnosticinfo.html │ │ ├── diagnosticmessages.html │ │ ├── ecmarkupemitter.html │ │ ├── emitformat.html │ │ ├── emitter.html │ │ ├── emptyassertion.html │ │ ├── grammar.html │ │ ├── grammarkdownemitter.html │ │ ├── host.html │ │ ├── hostbase.html │ │ ├── hostbaseoptions.html │ │ ├── hostoptions.html │ │ ├── htmlclosetagtrivia.html │ │ ├── htmlcommenttrivia.html │ │ ├── htmlemitter.html │ │ ├── htmlopentagtrivia.html │ │ ├── htmltagtriviabase.html │ │ ├── htmltriviabase.html │ │ ├── htmltriviatypes.html │ │ ├── identifier.html │ │ ├── import.html │ │ ├── invalidassertion.html │ │ ├── invalidsymbol.html │ │ ├── knownoption.html │ │ ├── knownoptions.html │ │ ├── lexicalgoalassertion.html │ │ ├── lexicalsymbolbase.html │ │ ├── lexicalsymboltypes.html │ │ ├── line.html │ │ ├── lineinfo.html │ │ ├── linemap.html │ │ ├── lineoffsetmap.html │ │ ├── linkreference.html │ │ ├── lookaheadassertion.html │ │ ├── markdownemitter.html │ │ ├── metaelementbase.html │ │ ├── metaelementtypes.html │ │ ├── multilinecommenttrivia.html │ │ ├── newlinekind.html │ │ ├── node.html │ │ ├── node_2.html │ │ ├── nodeasynchost.html │ │ ├── nodeasynchostoptions.html │ │ ├── nodenavigator.html │ │ ├── nodesynchost.html │ │ ├── nodesynchostoptions.html │ │ ├── nodevisitor.html │ │ ├── nonterminal.html │ │ ├── nosymbolhereassertion.html │ │ ├── nulldiagnosticmessages.html │ │ ├── numberliteral.html │ │ ├── oneoflist.html │ │ ├── oneofsymbol.html │ │ ├── optionalsymbolbase.html │ │ ├── optionalsymboltypes.html │ │ ├── parameter.html │ │ ├── parameterlist.html │ │ ├── parsedarguments.html │ │ ├── parser.html │ │ ├── placeholdersymbol.html │ │ ├── position-interface.html │ │ ├── position-namespace.html │ │ ├── position_2-interface.html │ │ ├── position_2-namespace.html │ │ ├── primarysymbolbase.html │ │ ├── primarysymboltypes.html │ │ ├── production.html │ │ ├── productionbodybase.html │ │ ├── productionbodytypes.html │ │ ├── prose.html │ │ ├── proseassertion.html │ │ ├── prosefragmentliteral.html │ │ ├── prosefragmenttypes.html │ │ ├── range-interface.html │ │ ├── range-namespace.html │ │ ├── range_2-interface.html │ │ ├── range_2-namespace.html │ │ ├── rawargument.html │ │ ├── rawarguments.html │ │ ├── resolver.html │ │ ├── righthandside.html │ │ ├── righthandsidelist.html │ │ ├── scanner.html │ │ ├── singlefilehost.html │ │ ├── singlelinecommenttrivia.html │ │ ├── sourceelementbase.html │ │ ├── sourceelementtypes.html │ │ ├── sourcefile.html │ │ ├── stringasynchost.html │ │ ├── stringliteral.html │ │ ├── stringsynchost.html │ │ ├── stringwriter.html │ │ ├── symbol.html │ │ ├── symbol_2.html │ │ ├── symbolkind.html │ │ ├── symbolset.html │ │ ├── symbolspan.html │ │ ├── symboltable.html │ │ ├── synchost.html │ │ ├── synchostoptions.html │ │ ├── syncsinglefilehost.html │ │ ├── syntaxkind.html │ │ ├── terminal.html │ │ ├── terminalliteral.html │ │ ├── textchange-interface.html │ │ ├── textchange-namespace.html │ │ ├── textcontent.html │ │ ├── textrange.html │ │ ├── token.html │ │ ├── triviabase.html │ │ ├── triviatypes.html │ │ ├── unicodecharacterliteral.html │ │ ├── unicodecharacterrange.html │ │ └── usagewriter.html │ ├── hosts.html │ ├── nodes.html │ ├── parse.html │ └── toc.html ├── cli.html ├── elements.css ├── es2020.html ├── es6.html ├── favicon.ico ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── index.html ├── index.json ├── logo.svg ├── manifest.json ├── menu.js ├── overview │ ├── cli.html │ └── toc.html ├── search-stopwords.json ├── styles │ ├── docfx.css │ ├── docfx.js │ ├── docfx.vendor.css │ ├── docfx.vendor.js │ ├── lunr.js │ ├── lunr.min.js │ ├── main.css │ ├── main.js │ └── search-worker.js ├── toc.html ├── typescript.html ├── usage │ ├── cli.html │ ├── syntax.html │ └── toc.html └── xrefmap.yml ├── grammars ├── es2015.grammar ├── es2020.grammar └── typescript.grammar ├── gulpfile.js ├── jest.config.js ├── package-lock.json ├── package.json ├── resources └── grammar.css ├── schema └── diagnostics.schema.json ├── scripts ├── build.js ├── exec.js ├── generateDiagnostics.js └── grammarTest.js ├── spec ├── es2020.grammar ├── es2020.html ├── es6.grammar ├── es6.html ├── grammarkdown.grammar ├── styles.css ├── typescript.grammar └── typescript.html ├── src ├── __tests__ │ ├── README.md │ ├── checker.test.ts │ ├── cli.test.ts │ ├── diff.ts │ ├── emitter.test.ts │ ├── index.ts │ ├── lineOffsetMap.test.ts │ ├── matchers.ts │ ├── navigator.test.ts │ ├── navigatorUtils.ts │ ├── parser.test.ts │ ├── resources.ts │ ├── resources │ │ ├── assertions │ │ │ ├── constraints │ │ │ │ ├── basic.grammar │ │ │ │ └── invalid.grammar │ │ │ ├── lookaheadAssertion.grammar │ │ │ ├── noSymbolHere.grammar │ │ │ ├── proseAssertion.2.grammar │ │ │ ├── proseAssertion.3.grammar │ │ │ └── proseAssertion.grammar │ │ ├── collapsed │ │ │ ├── multiple.grammar │ │ │ └── single.grammar │ │ ├── ecmarkup │ │ │ └── grammarTest.grammar │ │ ├── html │ │ │ ├── aroundProduction.grammar │ │ │ ├── aroundSymbols.grammar │ │ │ ├── aroundTerminalInOneOf.grammar │ │ │ ├── gh80.grammar │ │ │ ├── htmlComments.grammar │ │ │ └── mixed.grammar │ │ ├── meta │ │ │ ├── define │ │ │ │ └── byRegion.grammar │ │ │ └── line │ │ │ │ └── lineMeta.grammar │ │ ├── nonterminals │ │ │ └── withArguments.grammar │ │ ├── oneOfList │ │ │ └── oneOfList.grammar │ │ ├── parameters │ │ │ └── noUnusedParameters.grammar │ │ ├── placeholder │ │ │ └── placeholder.grammar │ │ ├── rightHandSide │ │ │ └── complex.grammar │ │ ├── rightHandSideList │ │ │ ├── multipleItems.grammar │ │ │ └── singleItem.grammar │ │ ├── specs │ │ │ ├── es2020.grammar │ │ │ ├── es6.grammar │ │ │ ├── grammarkdown.grammar │ │ │ ├── test.grammar │ │ │ └── typescript.grammar │ │ └── terminals │ │ │ ├── unicodeCharacterLiteral.grammar │ │ │ └── unicodeCharacterLiteralRange.grammar │ └── scanner.test.ts ├── binder.ts ├── checker.ts ├── cli.ts ├── core.ts ├── diagnostics.generated.ts ├── diagnostics.json ├── diagnostics.ts ├── docs │ ├── content │ │ ├── api │ │ │ ├── bind.md │ │ │ ├── check.md │ │ │ ├── compiler.md │ │ │ ├── emit.md │ │ │ ├── hosts.md │ │ │ ├── nodes.md │ │ │ └── parse.md │ │ ├── index.md │ │ ├── toc.yml │ │ └── usage │ │ │ ├── cli.md │ │ │ ├── syntax.md │ │ │ └── toc.yml │ ├── overwrite │ │ ├── compileroptions.md │ │ ├── coreasynchost.md │ │ ├── coreasynchostoptions.md │ │ ├── coresynchost.md │ │ ├── coresynchostoptions.md │ │ ├── grammarkdown.md │ │ ├── hostbase.md │ │ ├── hostbaseoptions.md │ │ ├── nodeasynchost.md │ │ ├── nodeasynchostoptions.md │ │ ├── nodesynchost.md │ │ ├── nodesynchostoptions.md │ │ ├── stringasynchost.md │ │ └── stringsynchost.md │ ├── template │ │ ├── UniversalReference.common.js │ │ ├── UniversalReference.html.primary.tmpl │ │ ├── partials │ │ │ ├── deprecated.tmpl.partial │ │ │ ├── namespaceSubtitle.tmpl.partial │ │ │ └── uref │ │ │ │ ├── class.header.tmpl.partial │ │ │ │ ├── class.tmpl.partial │ │ │ │ ├── enum.tmpl.partial │ │ │ │ └── namespace.tmpl.partial │ │ ├── styles │ │ │ ├── docfx.js │ │ │ └── main.css │ │ └── token.json │ └── xrefmap.yml ├── emitter │ ├── ecmarkup.ts │ ├── emitter.ts │ ├── grammarkdown.ts │ ├── html.ts │ ├── index.ts │ └── markdown.ts ├── grammar.ts ├── host.ts ├── hosts │ └── node.ts ├── index.ts ├── lineOffsetMap.ts ├── navigator.ts ├── nodeInternal.ts ├── nodes.ts ├── options.ts ├── parser.ts ├── performance.ts ├── read-package.ts ├── regionMap.ts ├── scanner.ts ├── sortedUniqueList.ts ├── stringwriter.ts ├── symbols.ts ├── tokens.ts ├── types.ts └── visitor.ts └── tsconfig.json /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Generate Documentation 2 | 3 | on: 4 | workflow_dispatch: 5 | workflow_run: 6 | workflows: [ 'CI' ] 7 | branches: [ main ] 8 | types: 9 | - completed 10 | 11 | jobs: 12 | docs: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Use NodeJS 20.x 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: '20.x' 20 | 21 | - run: npm ci 22 | 23 | - name: Prepare documentation 24 | run: npm run prepare-docs 25 | 26 | - name: Generate documentation 27 | uses: nikeee/docfx-action@v1.0.0 28 | with: 29 | args: docfx.json 30 | 31 | - name: Check for changes 32 | id: check_changes 33 | run: | 34 | git diff --quiet . || echo "changed=true" >> $GITHUB_OUTPUT 35 | 36 | - name: Update branch 37 | if: steps.check_changes.outputs.changed == 'true' 38 | run: | 39 | git config user.name github-actions 40 | git config user.email github-actions@github.com 41 | git add docs 42 | git commit -m "Updated /docs" 43 | git push 44 | -------------------------------------------------------------------------------- /.github/workflows/node.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths-ignore: 7 | - 'docs/**' 8 | pull_request: 9 | branches: [ main ] 10 | paths-ignore: 11 | - 'docs/**' 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [16.x, 18.x, 20.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | 25 | - name: Use NodeJS ${{ matrix.node-version }} 26 | uses: actions/setup-node@v1 27 | with: 28 | node-version: ${{ matrix.node-version }} 29 | 30 | - run: npm ci 31 | - run: npm run build 32 | - run: npm test 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | out 3 | dist 4 | temp 5 | obj 6 | baselines/local 7 | spec/*.grammar.html 8 | _exported_templates -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .git 2 | .gitignore 3 | .gitmodules 4 | .vscode 5 | .travis.yml 6 | node_modules 7 | pages 8 | spec 9 | src 10 | obj 11 | dist/tests 12 | baselines 13 | gulpfile.js -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - 'stable' 5 | 6 | cache: 7 | directories: 8 | - node_modules 9 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Attach by Process ID", 6 | "processId": "${command:PickProcess}", 7 | "request": "attach", 8 | "skipFiles": [ 9 | "/**" 10 | ], 11 | "type": "pwa-node", 12 | "outFiles": ["${workspaceFolder}/dist/**/*"], 13 | "sourceMaps": true, 14 | "smartStep": true, 15 | }, 16 | { 17 | "type": "node", 18 | "request": "launch", 19 | "name": "Jest All", 20 | "program": "${workspaceFolder}/node_modules/.bin/jest", 21 | "args": [ 22 | "--runInBand", 23 | ], 24 | "console": "internalConsole", 25 | "disableOptimisticBPs": true, 26 | "windows": { 27 | "program": "${workspaceFolder}/node_modules/jest/bin/jest", 28 | }, 29 | "smartStep": true, 30 | "outFiles": ["${workspaceFolder}/dist/**/*"], 31 | "skipFiles": [ 32 | "/**", 33 | "async_hooks.js", 34 | "${workspaceFolder}/node_modules/jest/**", 35 | "${workspaceFolder}/node_modules/jest-util/**" 36 | ] 37 | }, 38 | { 39 | "type": "node", 40 | "request": "launch", 41 | "name": "Jest Current File", 42 | "program": "${workspaceFolder}/node_modules/.bin/jest", 43 | "args": [ 44 | "--runInBand", 45 | "--no-cache", 46 | "--testPathPattern", 47 | "${fileBasename}" 48 | ], 49 | "console": "internalConsole", 50 | "windows": { 51 | "program": "${workspaceFolder}/node_modules/jest/bin/jest", 52 | }, 53 | "smartStep": false, 54 | "sourceMaps": true, 55 | "outFiles": ["${workspaceFolder}/dist/**/*"], 56 | "resolveSourceMapLocations": [ 57 | "${workspaceFolder}/**", 58 | "!**/node_modules/**" 59 | ], 60 | "skipFiles": [ 61 | "/**", 62 | "async_hooks.js", 63 | "${workspaceFolder}/node_modules/jest/**", 64 | "${workspaceFolder}/node_modules/jest-util/**", 65 | "${workspaceFolder}/node_modules/expect/**", 66 | "${workspaceFolder}/src/__tests__/navigatorUtils.*", 67 | "${workspaceFolder}/src/__tests__/matchers.*", 68 | ] 69 | } 70 | ] 71 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "editor.insertSpaces": true, 4 | "files.trimTrailingWhitespace": true, 5 | "editor.renderWhitespace": "boundary", 6 | "typescript.tsdk": "node_modules/typescript/lib", 7 | "json.schemas": [ 8 | { "fileMatch": ["diagnostics.json", "diagnostics.jsonc"], "url": "./schema/diagnostics.schema.json" } 9 | ], 10 | "files.associations": { 11 | "diagnostics.json": "jsonc" 12 | } 13 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "gulp", 8 | "task": "build", 9 | "group": { 10 | "kind": "build", 11 | "isDefault": true 12 | } 13 | }, 14 | { 15 | "type": "gulp", 16 | "task": "test", 17 | "group": { 18 | "kind": "test", 19 | "isDefault": true 20 | } 21 | }, 22 | { 23 | "type": "gulp", 24 | "task": "pre-test" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Grammarkdown 2 | 3 | You may contribute to Grammarkdown either by filing issues or through PRs. 4 | 5 | Please also ensure that your contributions follow both the [GitHub Terms of Service](https://docs.github.com/en/articles/github-terms-of-service) and [GitHub Community Guidelines](https://docs.github.com/articles/github-community-guidelines). 6 | 7 | ## Building from Source 8 | 9 | To build Grammarkdown from source you will need the following dependencies: 10 | 11 | - [Git](https://git-scm.com/) 12 | - [NodeJS](https://nodejs.org/) LTS (which includes `npm` and `npx`) 13 | - Either a global installation of [`gulp`](https://gulpjs.com/) (i.e., `npm install -g gulp`), or you can execute `gulp` via `npx` for any of the build tasks below (i.e., `npx gulp ...`). 14 | 15 | Build steps: 16 | 17 | ```sh 18 | # clone the repository 19 | git clone https://github.com/rbuckton/grammarkdown.git 20 | cd grammarkdown 21 | 22 | # install dependencies 23 | npm install 24 | 25 | # build the package and run tests 26 | gulp 27 | ``` 28 | 29 | You can also run the build in "watch" mode, which will recompile and run all tests whenever a file change is detected: 30 | 31 | ```sh 32 | gulp watch 33 | ``` 34 | 35 | ## Adding or Modifying Sources 36 | 37 | Grammarkdown is developed using [TypeScript](https://typescriptlang.org). 38 | 39 | ## Running Tests 40 | 41 | To run the tests you can use the following command: 42 | 43 | ```sh 44 | gulp test 45 | ``` 46 | 47 | To view any differences in output from our baseline comparison tests, ensure your `DIFF` environment variable is set to a 48 | valid executable capable of handling directory diffs and use the following command: 49 | 50 | ```sh 51 | gulp diff 52 | ``` 53 | 54 | If all of the baseline tests are acceptable, you can accept the new baselines using the following command: 55 | 56 | ```sh 57 | gulp accept-baselines 58 | ``` 59 | 60 | ## Adding Tests 61 | 62 | ### Grammar Tests 63 | 64 | Grammarkdown primarily uses baseline comparison tests that exercise the entire compiler and are written as `.grammar` files 65 | under the `~/src/tests/resources` folder. Related functionality is generally grouped together in a subfolder. 66 | These tests are evaluated by `~/src/tests/grammar-tests.ts`. Reference baselines can be found in `~/baselines/reference` and 67 | generated outputs can be found in `~/baselines/local`. 68 | 69 | You are able to control some of the behavior of `.grammar` tests with `// @directive: value` comments at the top of the file. 70 | The supported test comment directives can be found in the following table: 71 | 72 | | directive | value | description | 73 | |:--------------|:--------------------------------------:|:------------| 74 | | `full` | `true`, `false` | When `true`, enables all of the test directives and output formats. Default: `false` | 75 | | `tokens` | `true`, `false` | When `true`, emits a `.tokens` file that contains the tokens parsed from the file. Default: `false` | 76 | | `nodes` | `true`, `false` | When `true`, emits a `.nodes` file that contains the AST nodes parsed from the file. Default: `false` | 77 | | `diagnostics` | `true`, `false` | When `true`, emits a `.diagnostics` file that contains any diagnostic messages that result from parsing and checking the grammar. Default: `true` | 78 | | `emit` | `ecmarkup`, `markdown`, `html`, `none` | Indicates the output format for the tests. Multiple formats can be combined using `,`. Default: `ecmarkup` | 79 | 80 | ### Unit Tests 81 | 82 | In addition, Grammarkdown also uses unit tests found underneath `~/src/tests/`. Unit tests are authored 83 | using [Mocha](https://mochajs.org/). 84 | 85 | ## Generating Documentation 86 | 87 | The documentation for Grammarkdown is generated using [`api-extractor`](https://api-extractor.com/), 88 | [`api-documenter`](https://api-extractor.com/pages/setup/generating_docs/), and [`docfx`](https://dotnet.github.io/docfx/). 89 | While `api-extractor` and `api-documenter` are development dendencies that will be installed for you, `docfx` is an 90 | external executable that must be installed manually. Instructions for installation can be found on the [DocFX website](https://dotnet.github.io/docfx/). 91 | 92 | To generate documentation use the following command: 93 | 94 | ```sh 95 | gulp docs 96 | ``` 97 | 98 | ## Localization 99 | 100 | Grammarkdown is not currently localized. All documentation and diagnostics are currently only provided in English. We will consider adding 101 | localization support if there is enough interest. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ron Buckton (rbuckton@chronicles.org) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /api-documenter.json: -------------------------------------------------------------------------------- 1 | { 2 | "outputTarget": "docfx", 3 | "newDocfxNamespaces": true, 4 | "tableOfContents": { 5 | "tocConfig": { 6 | "items": [ 7 | { 8 | "name": "grammarkdown", 9 | "uid": "grammarkdown!", 10 | "items": [ 11 | { "name": "Services", "items": [ 12 | { "name": "Compiler", "uid": "compiler", "items": [] }, 13 | { "name": "Hosts", "uid": "hosts", "items": [] }, 14 | { "name": "Parse", "uid": "parse", "items": [] }, 15 | { "name": "Bind", "uid": "bind", "items": [] }, 16 | { "name": "Check", "uid": "check", "items": [] }, 17 | { "name": "Emit", "uid": "emit", "items": [] } 18 | ] }, 19 | { "name": "Nodes", "uid": "nodes", "items": [] }, 20 | { "name": "Other", "items": [] } 21 | ] 22 | } 23 | ] 24 | }, 25 | "catchAllCategory": "Other", 26 | "categoryInlineTag": "docCategory" 27 | } 28 | } -------------------------------------------------------------------------------- /api-extractor.json: -------------------------------------------------------------------------------- 1 | { 2 | "projectFolder": ".", 3 | "mainEntryPointFilePath": "/dist/index.d.ts", 4 | "compiler": { "tsconfigFilePath": "/tsconfig.json" }, 5 | "apiReport": { 6 | "enabled": false, 7 | "reportFolder": "/baselines/reference/api-report", 8 | "reportTempFolder": "/baselines/local/api-report" 9 | }, 10 | "dtsRollup": { "enabled": false }, 11 | "tsdocMetadata": { 12 | "enabled": true, 13 | "tsdocMetadataFilePath": "/dist/tsdoc-metadata.json" 14 | }, 15 | "docModel": { 16 | "enabled": true, 17 | "apiJsonFilePath": "/obj/json/grammarkdown.api.json" 18 | }, 19 | "messages": { 20 | "extractorMessageReporting": { 21 | "ae-missing-release-tag": { 22 | "logLevel": "none" 23 | } 24 | }, 25 | "tsdocMessageReporting": { 26 | "tsdoc-param-tag-with-invalid-name": { 27 | "logLevel": "none" 28 | }, 29 | "tsdoc-param-tag-missing-hyphen": { 30 | "logLevel": "none" 31 | }, 32 | "tsdoc-undefined-tag": { 33 | "logLevel": "none" 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /baselines/reference/assertions/constraints/basic.grammar.diagnostics: -------------------------------------------------------------------------------- 1 | /// assertions/constraints/basic.grammar: 2 | assertions/constraints/basic.grammar(4,4): error GM1001: 'lookahead', 'lexical', 'no', '~', or '+' expected 3 | assertions/constraints/basic.grammar(5,12): error GM1001: 'lookahead', 'lexical', or 'no' expected 4 | 5 | /// [assertions/constraints/basic.grammar] 2 errors 6 | Prod[A] : 7 | [~A] `ok` 8 | [+A] `ok` 9 | [?A] `error` 10 | ~ 11 | !!! error GM1001: 'lookahead', 'lexical', 'no', '~', or '+' expected 12 | `error` [~A] 13 | ~ 14 | !!! error GM1001: 'lookahead', 'lexical', or 'no' expected 15 | -------------------------------------------------------------------------------- /baselines/reference/assertions/constraints/basic.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ok 4 | 5 | 6 | ok 7 | 8 | 9 | error 10 | 11 | 12 | error 13 | 14 | 15 | -------------------------------------------------------------------------------- /baselines/reference/assertions/constraints/invalid.grammar.diagnostics: -------------------------------------------------------------------------------- 1 | /// assertions/constraints/invalid.grammar: 2 | assertions/constraints/invalid.grammar(3,5): error GM2004: Production 'A' does not have a parameter named 'Two' 3 | 4 | /// [assertions/constraints/invalid.grammar] 1 error 5 | // https://github.com/rbuckton/grammarkdown/issues/42 6 | A[One] :: 7 | [+Two] `x` 8 | ~~~ 9 | !!! error GM2004: Production 'A' does not have a parameter named 'Two' 10 | -------------------------------------------------------------------------------- /baselines/reference/assertions/constraints/invalid.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | x 4 | 5 | 6 | -------------------------------------------------------------------------------- /baselines/reference/assertions/lookaheadAssertion.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | a 3 | 4 | 5 | 6 | lookahead ∉ A 7 | 8 | 9 | -------------------------------------------------------------------------------- /baselines/reference/assertions/noSymbolHere.grammar.diagnostics: -------------------------------------------------------------------------------- 1 | /// assertions/noSymbolHere.grammar: 2 | assertions/noSymbolHere.grammar(2,16): error GM1001: here expected 3 | 4 | /// [assertions/noSymbolHere.grammar] 1 error 5 | ErrorNoSymbolHere: 6 | `a` [no `b`] `c` 7 | ~ 8 | !!! error GM1001: here expected 9 | -------------------------------------------------------------------------------- /baselines/reference/assertions/noSymbolHere.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | a 4 | no b here 5 | c 6 | 7 | 8 | -------------------------------------------------------------------------------- /baselines/reference/assertions/proseAssertion.2.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | empty 3 | 4 | 5 | 6 | HexDigits 7 | but only if MV of HexDigits > 0x10FFFF 8 | 9 | 10 | -------------------------------------------------------------------------------- /baselines/reference/assertions/proseAssertion.3.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | DecimalEscape 4 | 5 | 6 | DecimalEscape 7 | but only if the CapturingGroupNumber of DecimalEscape is <= _NcapturingParens_ 8 | 9 | 10 | DecimalEscape 11 | but only if the CapturingGroupNumber of DecimalEscape is <= _NcapturingParens_ 12 | 13 | 14 | CharacterClassEscape 15 | 16 | 17 | CharacterEscape 18 | 19 | 20 | k 21 | GroupName 22 | 23 | 24 | -------------------------------------------------------------------------------- /baselines/reference/assertions/proseAssertion.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | A 4 | full prose span 5 | 6 | 7 | 8 | 9 | A 10 | head terminal middle B tail 11 | 12 | 13 | -------------------------------------------------------------------------------- /baselines/reference/collapsed/multiple.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | a 3 | 4 | 5 | b 6 | 7 | -------------------------------------------------------------------------------- /baselines/reference/collapsed/multiple.grammar.grammar: -------------------------------------------------------------------------------- 1 | A : `a` 2 | 3 | B : `b` 4 | -------------------------------------------------------------------------------- /baselines/reference/collapsed/multiple.grammar.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | A : 4 | a 5 |
6 |
7 | B : 8 | b 9 |
10 |
-------------------------------------------------------------------------------- /baselines/reference/collapsed/multiple.grammar.md: -------------------------------------------------------------------------------- 1 |   *A* **:** `` a `` 2 | 3 |   *B* **:** `` b `` 4 | 5 | -------------------------------------------------------------------------------- /baselines/reference/collapsed/multiple.grammar.nodes: -------------------------------------------------------------------------------- 1 | /// collapsed/multiple.grammar: 2 | (1,1)SyntaxKind[SourceFile](filename = "multiple.grammar") 3 | (1,1)SyntaxKind[Production] 4 | (1,1)SyntaxKind[Identifier](text = "A") 5 | (1,2)SyntaxKind[ColonToken] 6 | (1,4)SyntaxKind[RightHandSide] 7 | (1,4)SyntaxKind[SymbolSpan] 8 | (1,4)SyntaxKind[Terminal] 9 | (1,4)SyntaxKind[TerminalLiteral](text = "a") 10 | (2,1)SyntaxKind[Production] 11 | (2,1)SyntaxKind[Identifier](text = "B") 12 | (2,2)SyntaxKind[ColonToken] 13 | (2,4)SyntaxKind[RightHandSide] 14 | (2,4)SyntaxKind[SymbolSpan] 15 | (2,4)SyntaxKind[Terminal] 16 | (2,4)SyntaxKind[TerminalLiteral](text = "b") 17 | -------------------------------------------------------------------------------- /baselines/reference/collapsed/multiple.grammar.tokens: -------------------------------------------------------------------------------- 1 | /// collapsed/multiple.grammar: 2 | SyntaxKind[Identifier](1,1): A 3 | SyntaxKind[ColonToken](1,2): : 4 | SyntaxKind[TerminalLiteral](1,4): `a` 5 | SyntaxKind[Identifier](2,1): B 6 | SyntaxKind[ColonToken](2,2): : 7 | SyntaxKind[TerminalLiteral](2,4): `b` 8 | SyntaxKind[EndOfFileToken](2,7): «EndOfFileToken» 9 | -------------------------------------------------------------------------------- /baselines/reference/collapsed/single.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | a 3 | 4 | -------------------------------------------------------------------------------- /baselines/reference/collapsed/single.grammar.grammar: -------------------------------------------------------------------------------- 1 | A : `a` 2 | -------------------------------------------------------------------------------- /baselines/reference/collapsed/single.grammar.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | A : 4 | a 5 |
6 |
-------------------------------------------------------------------------------- /baselines/reference/collapsed/single.grammar.md: -------------------------------------------------------------------------------- 1 |   *A* **:** `` a `` 2 | 3 | -------------------------------------------------------------------------------- /baselines/reference/collapsed/single.grammar.nodes: -------------------------------------------------------------------------------- 1 | /// collapsed/single.grammar: 2 | (1,1)SyntaxKind[SourceFile](filename = "single.grammar") 3 | (1,1)SyntaxKind[Production] 4 | (1,1)SyntaxKind[Identifier](text = "A") 5 | (1,2)SyntaxKind[ColonToken] 6 | (1,4)SyntaxKind[RightHandSide] 7 | (1,4)SyntaxKind[SymbolSpan] 8 | (1,4)SyntaxKind[Terminal] 9 | (1,4)SyntaxKind[TerminalLiteral](text = "a") 10 | -------------------------------------------------------------------------------- /baselines/reference/collapsed/single.grammar.tokens: -------------------------------------------------------------------------------- 1 | /// collapsed/single.grammar: 2 | SyntaxKind[Identifier](1,1): A 3 | SyntaxKind[ColonToken](1,2): : 4 | SyntaxKind[TerminalLiteral](1,4): `a` 5 | SyntaxKind[EndOfFileToken](1,7): «EndOfFileToken» 6 | -------------------------------------------------------------------------------- /baselines/reference/ecmarkup/grammarTest.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Inserting 4 | anRHS 5 | 6 | 7 | Deleting 8 | anRHS 9 | 10 | 11 | Modifying 12 | Existing 13 | Old 14 | Production 15 | 16 | 17 | 18 | 19 | t 20 | 21 | 22 | 23 | 24 | && 25 | n 26 | lookahead ∉ { 1, 3, 5, 7, 9 } 27 | DecimalDigits 28 | 29 | 30 | DecimalDigit 31 | lookahead ∉ DecimalDigit 32 | 33 | 34 | 35 | 36 | This is technically a “production” 37 | 38 | 39 | -------------------------------------------------------------------------------- /baselines/reference/html/aroundProduction.grammar.diagnostics: -------------------------------------------------------------------------------- 1 | /// html/aroundProduction.grammar: 2 | html/aroundProduction.grammar(9,10): error GM1010: HTML trivia not allowed here 3 | html/aroundProduction.grammar(12,10): error GM1010: HTML trivia not allowed here 4 | 5 | /// [html/aroundProduction.grammar] 2 errors 6 | A :: 7 | `b` 8 | 9 | 10 | A :: 11 | `b` 12 | 13 | 14 | A :: 15 | ~~~~~~ 16 | !!! error GM1010: HTML trivia not allowed here 17 | `b` 18 | 19 | A :: 20 | ~~~~~~ 21 | !!! error GM1010: HTML trivia not allowed here 22 | `b` 23 | -------------------------------------------------------------------------------- /baselines/reference/html/aroundProduction.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | b 4 | 5 | 6 | 7 | 8 | b 9 | 10 | 11 | 12 | 13 | b 14 | 15 | 16 | 17 | 18 | b 19 | 20 | 21 | -------------------------------------------------------------------------------- /baselines/reference/html/aroundProduction.grammar.grammar: -------------------------------------------------------------------------------- 1 | A :: 2 | `b` 3 | 4 | 5 | A :: 6 | `b` 7 | 8 | 9 | A :: 10 | `b` 11 | 12 | A :: 13 | `b` 14 | -------------------------------------------------------------------------------- /baselines/reference/html/aroundProduction.grammar.nodes: -------------------------------------------------------------------------------- 1 | /// html/aroundProduction.grammar: 2 | (1,6)SyntaxKind[SourceFile](filename = "aroundProduction.grammar") 3 | (1,6)SyntaxKind[Production] 4 | [leadingTrivia] 5 | (1,6)SyntaxKind[HtmlOpenTagTrivia](tagName = "ins") 6 | (1,6)SyntaxKind[Identifier](text = "A") 7 | (1,8)SyntaxKind[ColonColonToken] 8 | (2,5)SyntaxKind[RightHandSideList] 9 | (2,5)SyntaxKind[RightHandSide] 10 | (2,5)SyntaxKind[SymbolSpan] 11 | (2,5)SyntaxKind[Terminal] 12 | (2,5)SyntaxKind[TerminalLiteral](text = "b") 13 | [trailingTrivia] 14 | (2,14)SyntaxKind[HtmlCloseTagTrivia](tagName = "ins") 15 | (5,1)SyntaxKind[Production] 16 | [leadingTrivia] 17 | (4,6)SyntaxKind[HtmlOpenTagTrivia](tagName = "ins") 18 | (5,1)SyntaxKind[Identifier](text = "A") 19 | (5,3)SyntaxKind[ColonColonToken] 20 | (6,5)SyntaxKind[RightHandSideList] 21 | (6,5)SyntaxKind[RightHandSide] 22 | (6,5)SyntaxKind[SymbolSpan] 23 | (6,5)SyntaxKind[Terminal] 24 | (6,5)SyntaxKind[TerminalLiteral](text = "b") 25 | [trailingTrivia] 26 | (7,7)SyntaxKind[HtmlCloseTagTrivia](tagName = "ins") 27 | (9,6)SyntaxKind[Production] 28 | [leadingTrivia] 29 | (9,6)SyntaxKind[HtmlOpenTagTrivia](tagName = "ins") 30 | (9,6)SyntaxKind[Identifier](text = "A") 31 | (9,8)SyntaxKind[ColonColonToken] 32 | [trailingTrivia] 33 | (9,16)SyntaxKind[HtmlCloseTagTrivia](tagName = "ins") 34 | (10,10)SyntaxKind[RightHandSideList] 35 | (10,10)SyntaxKind[RightHandSide] 36 | [leadingTrivia] 37 | (10,10)SyntaxKind[HtmlOpenTagTrivia](tagName = "ins") 38 | (10,10)SyntaxKind[SymbolSpan] 39 | (10,10)SyntaxKind[Terminal] 40 | (10,10)SyntaxKind[TerminalLiteral](text = "b") 41 | [trailingTrivia] 42 | (10,19)SyntaxKind[HtmlCloseTagTrivia](tagName = "ins") 43 | (12,6)SyntaxKind[Production] 44 | [leadingTrivia] 45 | (12,6)SyntaxKind[HtmlOpenTagTrivia](tagName = "ins") 46 | (12,6)SyntaxKind[Identifier](text = "A") 47 | (12,8)SyntaxKind[ColonColonToken] 48 | [trailingTrivia] 49 | (12,16)SyntaxKind[HtmlCloseTagTrivia](tagName = "ins") 50 | (13,5)SyntaxKind[RightHandSideList] 51 | (13,5)SyntaxKind[RightHandSide] 52 | (13,5)SyntaxKind[SymbolSpan] 53 | (13,5)SyntaxKind[Terminal] 54 | (13,5)SyntaxKind[TerminalLiteral](text = "b") 55 | -------------------------------------------------------------------------------- /baselines/reference/html/aroundSymbols.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | b 3 | 4 | 5 | 6 | a 7 | 8 | 9 | a 10 | b 11 | 12 | 13 | a 14 | b 15 | 16 | 17 | a 18 | b 19 | c 20 | 21 | 22 | a 23 | b 24 | c 25 | 26 | 27 | a 28 | A 29 | c 30 | 31 | 32 | a 33 | 34 | 35 | -------------------------------------------------------------------------------- /baselines/reference/html/aroundTerminalInOneOf.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | a b c 3 | 4 | -------------------------------------------------------------------------------- /baselines/reference/html/gh80.grammar.grammar: -------------------------------------------------------------------------------- 1 | X :: 2 | Y 3 | 4 | 5 | X :: 6 | Y 7 | 8 | 9 | X :: 10 | Y 11 | -------------------------------------------------------------------------------- /baselines/reference/html/htmlComments.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | Y 3 | 4 | 5 | Y 6 | 7 | 8 | Y 9 | 10 | -------------------------------------------------------------------------------- /baselines/reference/html/htmlComments.grammar.grammar: -------------------------------------------------------------------------------- 1 | X : Y 2 | 3 | 4 | 5 | 6 | 7 | X : Y 8 | 9 | 10 | 11 | 12 | 13 | X : Y 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /baselines/reference/html/htmlComments.grammar.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | X : 4 | Y 5 |
6 |
7 | X : 8 | Y 9 |
10 |
11 | X : 12 | Y 13 |
14 |
-------------------------------------------------------------------------------- /baselines/reference/html/mixed.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | a 3 | 4 | 5 | 6 | a 7 | 8 | 9 | 10 | 11 | a 12 | 13 | 14 | 15 | 16 | a 17 | 18 | 19 | a 20 | b 21 | 22 | 23 | a 24 | b 25 | 26 | 27 | a 28 | b 29 | c 30 | 31 | 32 | a 33 | b 34 | c 35 | 36 | 37 | a 38 | A 39 | c 40 | 41 | 42 | a 43 | 44 | 45 | -------------------------------------------------------------------------------- /baselines/reference/html/mixed.grammar.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | A : 4 | a 5 |
6 |
7 | B : 8 |
9 |
10 | a 11 |
12 |
13 |
14 |
15 | C : 16 |
17 |
18 | a 19 |
20 |
21 |
22 |
23 | D[U] : 24 |
25 |
26 | a 27 |
28 |
29 | a b 30 |
31 |
32 | a b 33 |
34 |
35 | a b c 36 |
37 |
38 | a bopt c 39 |
40 |
41 | a A c 42 |
43 |
44 | [~U]a 45 |
46 |
47 |
48 |
-------------------------------------------------------------------------------- /baselines/reference/html/mixed.grammar.md: -------------------------------------------------------------------------------- 1 |   *A* **:** `` a `` 2 | 3 | 4 |   *B* **:** 5 |    `` a `` 6 | 7 | 8 |   *C* **:** 9 |    `` a `` 10 | 11 | 12 |   *D*[U] **:** 13 |    `` a `` 14 |    `` a `` `` b `` 15 |    `` a `` `` b `` 16 |    `` a `` `` b `` `` c `` 17 |    `` a `` `` b ``opt `` c `` 18 |    `` a `` *[A](#A)* `` c `` 19 |    [~U] `` a `` 20 | 21 | -------------------------------------------------------------------------------- /baselines/reference/meta/define/byRegion.grammar.diagnostics: -------------------------------------------------------------------------------- 1 | /// meta/define/byRegion.grammar: 2 | meta/define/byRegion.grammar(5,3): error GM2008: Parameter 'A' is unused 3 | meta/define/byRegion.grammar(11,3): error GM2008: Parameter 'A' is unused 4 | 5 | /// [meta/define/byRegion.grammar] 2 errors 6 | // default for 'noUnusedParameters' is 'false' 7 | A[A] : `a` // ok 8 | 9 | @define noUnusedParameters true 10 | B[A] :: `a` // error 11 | ~ 12 | !!! error GM2008: Parameter 'A' is unused 13 | 14 | @define noUnusedParameters false 15 | C[A] :: `a` // ok 16 | 17 | @define noUnusedParameters true 18 | D[A] :: `a` // error 19 | ~ 20 | !!! error GM2008: Parameter 'A' is unused 21 | 22 | @define noUnusedParameters default 23 | E[A] :: `a` // ok 24 | -------------------------------------------------------------------------------- /baselines/reference/meta/line/lineMeta.grammar.diagnostics: -------------------------------------------------------------------------------- 1 | /// meta/line/lineMeta.grammar: 2 | foo(5,5): error GM2000: Cannot find name: 'B' 3 | meta/line/lineMeta.grammar(25,9): error GM2000: Cannot find name: 'D' 4 | meta/line/lineMeta.grammar(8,5): error GM2000: Cannot find name: 'F' 5 | 6 | /// [meta/line/lineMeta.grammar] 3 errors 7 | @line 5 "foo" 8 | A : B 9 | ~ 10 | !!! error GM2000: Cannot find name: 'B' 11 | 12 | @line 25 13 | C : D 14 | ~ 15 | !!! error GM2000: Cannot find name: 'D' 16 | 17 | @line default 18 | E : F 19 | ~ 20 | !!! error GM2000: Cannot find name: 'F' 21 | -------------------------------------------------------------------------------- /baselines/reference/meta/line/lineMeta.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | B 3 | 4 | 5 | D 6 | 7 | 8 | F 9 | 10 | -------------------------------------------------------------------------------- /baselines/reference/nonterminals/withArguments.grammar.diagnostics: -------------------------------------------------------------------------------- 1 | /// nonterminals/withArguments.grammar: 2 | nonterminals/withArguments.grammar(4,5): error GM2007: There is no argument given for parameter 'A' 3 | nonterminals/withArguments.grammar(5,7): error GM1001: '?', '+', or '~' expected 4 | nonterminals/withArguments.grammar(6,7): error GM1001: '?', '+', or '~' expected 5 | nonterminals/withArguments.grammar(6,10): error GM1001: '?', '+', or '~' expected 6 | nonterminals/withArguments.grammar(6,10): error GM2004: Production 'E' does not have a parameter named 'B' 7 | nonterminals/withArguments.grammar(7,5): error GM2007: There is no argument given for parameter 'A' 8 | nonterminals/withArguments.grammar(7,8): error GM2004: Production 'E' does not have a parameter named 'B' 9 | nonterminals/withArguments.grammar(7,8): error GM2004: Production 'F' does not have a parameter named 'B' 10 | 11 | /// [nonterminals/withArguments.grammar] 8 errors 12 | A: `a` 13 | E[A] : A 14 | F[A] : 15 | E 16 | ~ 17 | !!! error GM2007: There is no argument given for parameter 'A' 18 | E[A] 19 | ~ 20 | !!! error GM1001: '?', '+', or '~' expected 21 | E[A, B] 22 | ~ 23 | !!! error GM1001: '?', '+', or '~' expected 24 | ~ 25 | !!! error GM1001: '?', '+', or '~' expected 26 | ~ 27 | !!! error GM2004: Production 'E' does not have a parameter named 'B' 28 | E[?B] 29 | ~ 30 | !!! error GM2007: There is no argument given for parameter 'A' 31 | ~ 32 | !!! error GM2004: Production 'E' does not have a parameter named 'B' 33 | ~ 34 | !!! error GM2004: Production 'F' does not have a parameter named 'B' 35 | E[+A] 36 | E[~A] 37 | E[?A] 38 | -------------------------------------------------------------------------------- /baselines/reference/nonterminals/withArguments.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | a 3 | 4 | 5 | A 6 | 7 | 8 | 9 | E 10 | 11 | 12 | E 13 | 14 | 15 | E 16 | 17 | 18 | E 19 | 20 | 21 | E 22 | 23 | 24 | E 25 | 26 | 27 | E 28 | 29 | 30 | -------------------------------------------------------------------------------- /baselines/reference/oneOfList/oneOfList.grammar.grammar: -------------------------------------------------------------------------------- 1 | @define noStrictParametricProductions true 2 | 3 | Keyword :: one of 4 | `break` `do` `in` `typeof` 5 | `case` `else` `instanceof` `var` 6 | `catch` `export` `new` `void` 7 | `class` `extends` `return` `while` 8 | `const` `finally` `super` `with` 9 | `continue` `for` `switch` `yield` 10 | `debugger` `function` `this` `default` 11 | `if` `throw` `delete` `import` 12 | `try` 13 | 14 | FutureReservedWord :: one of 15 | `enum` 16 | // `await` is only treated as a |FutureReservedWord| when |Module| is the goal symbol of the syntactic grammar. 17 | `await` 18 | // The following tokens are also considered to be |FutureReservedWord|s when parsing strict mode code (see 10.2.1). 19 | `implements` `package` `protected` `interface` 20 | `private` `public` 21 | -------------------------------------------------------------------------------- /baselines/reference/parameters/noUnusedParameters.grammar.diagnostics: -------------------------------------------------------------------------------- 1 | /// parameters/noUnusedParameters.grammar: 2 | parameters/noUnusedParameters.grammar(4,5): error GM2008: Parameter 'A' is unused 3 | parameters/noUnusedParameters.grammar(7,5): error GM2008: Parameter 'A' is unused 4 | 5 | /// [parameters/noUnusedParameters.grammar] 2 errors 6 | // https://github.com/rbuckton/grammarkdown/issues/46 7 | @define noUnusedParameters true 8 | 9 | Foo[A] :: 10 | ~ 11 | !!! error GM2008: Parameter 'A' is unused 12 | Bar[+A] 13 | 14 | Bar[A] :: 15 | ~ 16 | !!! error GM2008: Parameter 'A' is unused 17 | `x` 18 | 19 | Baz[A] :: 20 | `x` 21 | 22 | Baz[A] :: 23 | [~A] `y` 24 | 25 | Quxx[A] :: 26 | `x` 27 | 28 | Quxx[A] :: 29 | Baz[?A] 30 | -------------------------------------------------------------------------------- /baselines/reference/parameters/noUnusedParameters.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Bar 4 | 5 | 6 | 7 | 8 | x 9 | 10 | 11 | 12 | 13 | x 14 | 15 | 16 | 17 | 18 | y 19 | 20 | 21 | 22 | 23 | x 24 | 25 | 26 | 27 | 28 | Baz 29 | 30 | 31 | -------------------------------------------------------------------------------- /baselines/reference/placeholder/placeholder.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | A 4 | @ 5 | A 6 | 7 | 8 | -------------------------------------------------------------------------------- /baselines/reference/rightHandSide/complex.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | B but not one of " or \ or U+0000 through U+001F 3 | 4 | -------------------------------------------------------------------------------- /baselines/reference/rightHandSideList/multipleItems.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | a 4 | 5 | 6 | b 7 | 8 | 9 | -------------------------------------------------------------------------------- /baselines/reference/rightHandSideList/multipleItems.grammar.grammar: -------------------------------------------------------------------------------- 1 | A : 2 | `a` 3 | `b` 4 | -------------------------------------------------------------------------------- /baselines/reference/rightHandSideList/multipleItems.grammar.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | A : 4 |
5 |
6 | a 7 |
8 |
9 | b 10 |
11 |
12 |
13 |
-------------------------------------------------------------------------------- /baselines/reference/rightHandSideList/multipleItems.grammar.md: -------------------------------------------------------------------------------- 1 |   *A* **:** 2 |    `` a `` 3 |    `` b `` 4 | 5 | -------------------------------------------------------------------------------- /baselines/reference/rightHandSideList/multipleItems.grammar.nodes: -------------------------------------------------------------------------------- 1 | /// rightHandSideList/multipleItems.grammar: 2 | (1,1)SyntaxKind[SourceFile](filename = "multipleItems.grammar") 3 | (1,1)SyntaxKind[Production] 4 | (1,1)SyntaxKind[Identifier](text = "A") 5 | (1,2)SyntaxKind[ColonToken] 6 | (2,5)SyntaxKind[RightHandSideList] 7 | (2,5)SyntaxKind[RightHandSide] 8 | (2,5)SyntaxKind[SymbolSpan] 9 | (2,5)SyntaxKind[Terminal] 10 | (2,5)SyntaxKind[TerminalLiteral](text = "a") 11 | (3,5)SyntaxKind[RightHandSide] 12 | (3,5)SyntaxKind[SymbolSpan] 13 | (3,5)SyntaxKind[Terminal] 14 | (3,5)SyntaxKind[TerminalLiteral](text = "b") 15 | -------------------------------------------------------------------------------- /baselines/reference/rightHandSideList/multipleItems.grammar.tokens: -------------------------------------------------------------------------------- 1 | /// rightHandSideList/multipleItems.grammar: 2 | SyntaxKind[Identifier](1,1): A 3 | SyntaxKind[ColonToken](1,2): : 4 | SyntaxKind[TerminalLiteral](2,5): `a` 5 | SyntaxKind[TerminalLiteral](3,5): `b` 6 | SyntaxKind[EndOfFileToken](3,8): «EndOfFileToken» 7 | -------------------------------------------------------------------------------- /baselines/reference/rightHandSideList/singleItem.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | a 4 | 5 | 6 | -------------------------------------------------------------------------------- /baselines/reference/rightHandSideList/singleItem.grammar.grammar: -------------------------------------------------------------------------------- 1 | A : 2 | `a` 3 | -------------------------------------------------------------------------------- /baselines/reference/rightHandSideList/singleItem.grammar.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | A : 4 |
5 |
6 | a 7 |
8 |
9 |
10 |
-------------------------------------------------------------------------------- /baselines/reference/rightHandSideList/singleItem.grammar.md: -------------------------------------------------------------------------------- 1 |   *A* **:** 2 |    `` a `` 3 | 4 | -------------------------------------------------------------------------------- /baselines/reference/rightHandSideList/singleItem.grammar.nodes: -------------------------------------------------------------------------------- 1 | /// rightHandSideList/singleItem.grammar: 2 | (1,1)SyntaxKind[SourceFile](filename = "singleItem.grammar") 3 | (1,1)SyntaxKind[Production] 4 | (1,1)SyntaxKind[Identifier](text = "A") 5 | (1,2)SyntaxKind[ColonToken] 6 | (2,5)SyntaxKind[RightHandSideList] 7 | (2,5)SyntaxKind[RightHandSide] 8 | (2,5)SyntaxKind[SymbolSpan] 9 | (2,5)SyntaxKind[Terminal] 10 | (2,5)SyntaxKind[TerminalLiteral](text = "a") 11 | -------------------------------------------------------------------------------- /baselines/reference/rightHandSideList/singleItem.grammar.tokens: -------------------------------------------------------------------------------- 1 | /// rightHandSideList/singleItem.grammar: 2 | SyntaxKind[Identifier](1,1): A 3 | SyntaxKind[ColonToken](1,2): : 4 | SyntaxKind[TerminalLiteral](2,5): `a` 5 | SyntaxKind[EndOfFileToken](2,8): «EndOfFileToken» 6 | -------------------------------------------------------------------------------- /baselines/reference/specs/test.grammar.diagnostics: -------------------------------------------------------------------------------- 1 | /// specs/test.grammar: 2 | specs/test.grammar(7,1): error GM2006: Production 'D' is missing parameter 'B'. All definitions of production 'D' must specify the same formal parameters 3 | specs/test.grammar(8,1): error GM2006: Production 'D' is missing parameter 'A'. All definitions of production 'D' must specify the same formal parameters 4 | specs/test.grammar(12,5): error GM2007: There is no argument given for parameter 'A' 5 | specs/test.grammar(13,7): error GM1001: '?', '+', or '~' expected 6 | specs/test.grammar(14,7): error GM1001: '?', '+', or '~' expected 7 | specs/test.grammar(14,10): error GM1001: '?', '+', or '~' expected 8 | specs/test.grammar(14,10): error GM2004: Production 'E' does not have a parameter named 'B' 9 | specs/test.grammar(15,5): error GM2007: There is no argument given for parameter 'A' 10 | specs/test.grammar(15,8): error GM2004: Production 'E' does not have a parameter named 'B' 11 | specs/test.grammar(15,8): error GM2004: Production 'F' does not have a parameter named 'B' 12 | 13 | /// [specs/test.grammar] 10 errors 14 | A : A @ A 15 | B : |A| but not one of `"` or `\` or U+0000 through U+001F 16 | 17 | C : A [>full prose span] 18 | C : A [>head `terminal` middle |B| tail] 19 | 20 | D[A] : A 21 | ~~~~~~~~ 22 | !!! error GM2006: Production 'D' is missing parameter 'B'. All definitions of production 'D' must specify the same formal parameters 23 | D[B] : A 24 | ~ 25 | !!! error GM2006: Production 'D' is missing parameter 'A'. All definitions of production 'D' must specify the same formal parameters 26 | 27 | E[A] : A 28 | F[A] : 29 | E 30 | ~ 31 | !!! error GM2007: There is no argument given for parameter 'A' 32 | E[A] 33 | ~ 34 | !!! error GM1001: '?', '+', or '~' expected 35 | E[A, B] 36 | ~ 37 | !!! error GM1001: '?', '+', or '~' expected 38 | ~ 39 | !!! error GM1001: '?', '+', or '~' expected 40 | ~ 41 | !!! error GM2004: Production 'E' does not have a parameter named 'B' 42 | E[?B] 43 | ~ 44 | !!! error GM2007: There is no argument given for parameter 'A' 45 | ~ 46 | !!! error GM2004: Production 'E' does not have a parameter named 'B' 47 | ~ 48 | !!! error GM2004: Production 'F' does not have a parameter named 'B' 49 | E[+A] 50 | E[~A] 51 | E[?A] 52 | 53 | G : 54 | <LF> 55 | <CR> 56 | 57 | Z : 58 | A 59 | B 60 | 61 | LookaheadRestriction: 62 | [lookahead 2 | 3 | A 4 | @ 5 | A 6 | 7 | 8 | 9 | A but not one of " or \ or U+0000 through U+001F 10 | 11 | 12 | 13 | A 14 | full prose span 15 | 16 | 17 | 18 | 19 | A 20 | head terminal middle B tail 21 | 22 | 23 | 24 | A 25 | 26 | 27 | A 28 | 29 | 30 | A 31 | 32 | 33 | 34 | E 35 | 36 | 37 | E 38 | 39 | 40 | E 41 | 42 | 43 | E 44 | 45 | 46 | E 47 | 48 | 49 | E 50 | 51 | 52 | E 53 | 54 | 55 | 56 | 57 | <LF> 58 | 59 | 60 | <CR> 61 | 62 | 63 | 64 | 65 | A 66 | 67 | 68 | B 69 | 70 | 71 | 72 | 73 | lookahead ∉ A 74 | 75 | 76 | -------------------------------------------------------------------------------- /baselines/reference/specs/test.grammar.grammar: -------------------------------------------------------------------------------- 1 | A : A @ A 2 | 3 | B : A but not one of `"` or `\` or U+0000 through U+001F 4 | 5 | C : A [> full prose span] 6 | C : A [> head `terminal` middle |B| tail] 7 | 8 | D[A] : A 9 | D[B] : A 10 | 11 | E[A] : A 12 | 13 | F[A] : 14 | E 15 | E[A] 16 | E[A, B] 17 | E[?B] 18 | E[+A] 19 | E[~A] 20 | E[?A] 21 | 22 | G : 23 | 24 | 25 | 26 | Z : 27 | A 28 | B 29 | 30 | LookaheadRestriction : 31 | [lookahead *A* **:** *[A](#A)* @ *[A](#A)* 2 | 3 |   *B* **:** *[A](#A)* **but not** **one of** `` " `` **or** `` \ `` **or** U+0000 **through** U+001F 4 | 5 |   *C* **:** *[A](#A)* full prose span 6 | 7 |   *C* **:** *[A](#A)* head `` terminal `` middle *[B](#B)* tail 8 | 9 |   *D*[A] **:** *[A](#A)* 10 | 11 |   *D*[B] **:** *[A](#A)* 12 | 13 |   *E*[A] **:** *[A](#A)* 14 | 15 |   *F*[A] **:** 16 |    *[E](#E)* 17 |    *[E](#E)*[A] 18 |    *[E](#E)*[A, B] 19 |    *[E](#E)*[?B] 20 |    *[E](#E)*[+A] 21 |    *[E](#E)*[~A] 22 |    *[E](#E)*[?A] 23 | 24 |   *G* **:** 25 |    <LF> 26 |    <CR> 27 | 28 |   *Z* **:** 29 |    *[A](#A)* 30 |    *[B](#B)* 31 | 32 |   *LookaheadRestriction* **:** 33 |    [lookahead ∉ *[A](#A)*] 34 | 35 | -------------------------------------------------------------------------------- /baselines/reference/specs/test.grammar.tokens: -------------------------------------------------------------------------------- 1 | /// specs/test.grammar: 2 | SyntaxKind[Identifier](1,1): A 3 | SyntaxKind[ColonToken](1,3): : 4 | SyntaxKind[Identifier](1,5): A 5 | SyntaxKind[AtToken](1,7): @ 6 | SyntaxKind[Identifier](1,9): A 7 | SyntaxKind[Identifier](2,1): B 8 | SyntaxKind[ColonToken](2,3): : 9 | SyntaxKind[Identifier](2,5): A 10 | SyntaxKind[ButKeyword](2,9): but 11 | SyntaxKind[NotKeyword](2,13): not 12 | SyntaxKind[OneKeyword](2,17): one 13 | SyntaxKind[OfKeyword](2,21): of 14 | SyntaxKind[TerminalLiteral](2,24): `"` 15 | SyntaxKind[OrKeyword](2,28): or 16 | SyntaxKind[TerminalLiteral](2,31): `\` 17 | SyntaxKind[OrKeyword](2,35): or 18 | SyntaxKind[UnicodeCharacterLiteral](2,38): U+0000 19 | SyntaxKind[ThroughKeyword](2,45): through 20 | SyntaxKind[UnicodeCharacterLiteral](2,53): U+001F 21 | SyntaxKind[Identifier](4,1): C 22 | SyntaxKind[ColonToken](4,3): : 23 | SyntaxKind[Identifier](4,5): A 24 | SyntaxKind[OpenBracketGreaterThanToken](4,7): [> 25 | SyntaxKind[ProseFull](4,9): full prose span 26 | SyntaxKind[CloseBracketToken](4,24): ] 27 | SyntaxKind[Identifier](5,1): C 28 | SyntaxKind[ColonToken](5,3): : 29 | SyntaxKind[Identifier](5,5): A 30 | SyntaxKind[OpenBracketGreaterThanToken](5,7): [> 31 | SyntaxKind[ProseHead](5,9): head 32 | SyntaxKind[TerminalLiteral](5,14): `terminal` 33 | SyntaxKind[ProseMiddle](5,24): middle 34 | SyntaxKind[Identifier](5,32): B 35 | SyntaxKind[ProseTail](5,35): tail 36 | SyntaxKind[CloseBracketToken](5,40): ] 37 | SyntaxKind[Identifier](7,1): D 38 | SyntaxKind[OpenBracketToken](7,2): [ 39 | SyntaxKind[Identifier](7,3): A 40 | SyntaxKind[CloseBracketToken](7,4): ] 41 | SyntaxKind[ColonToken](7,6): : 42 | SyntaxKind[Identifier](7,8): A 43 | SyntaxKind[Identifier](8,1): D 44 | SyntaxKind[OpenBracketToken](8,2): [ 45 | SyntaxKind[Identifier](8,3): B 46 | SyntaxKind[CloseBracketToken](8,4): ] 47 | SyntaxKind[ColonToken](8,6): : 48 | SyntaxKind[Identifier](8,8): A 49 | SyntaxKind[Identifier](10,1): E 50 | SyntaxKind[OpenBracketToken](10,2): [ 51 | SyntaxKind[Identifier](10,3): A 52 | SyntaxKind[CloseBracketToken](10,4): ] 53 | SyntaxKind[ColonToken](10,6): : 54 | SyntaxKind[Identifier](10,8): A 55 | SyntaxKind[Identifier](11,1): F 56 | SyntaxKind[OpenBracketToken](11,2): [ 57 | SyntaxKind[Identifier](11,3): A 58 | SyntaxKind[CloseBracketToken](11,4): ] 59 | SyntaxKind[ColonToken](11,6): : 60 | SyntaxKind[Identifier](12,5): E 61 | SyntaxKind[Identifier](13,5): E 62 | SyntaxKind[OpenBracketToken](13,6): [ 63 | SyntaxKind[Identifier](13,7): A 64 | SyntaxKind[CloseBracketToken](13,8): ] 65 | SyntaxKind[Identifier](14,5): E 66 | SyntaxKind[OpenBracketToken](14,6): [ 67 | SyntaxKind[Identifier](14,7): A 68 | SyntaxKind[CommaToken](14,8): , 69 | SyntaxKind[Identifier](14,10): B 70 | SyntaxKind[CloseBracketToken](14,11): ] 71 | SyntaxKind[Identifier](15,5): E 72 | SyntaxKind[OpenBracketToken](15,6): [ 73 | SyntaxKind[QuestionToken](15,7): ? 74 | SyntaxKind[Identifier](15,8): B 75 | SyntaxKind[CloseBracketToken](15,9): ] 76 | SyntaxKind[Identifier](16,5): E 77 | SyntaxKind[OpenBracketToken](16,6): [ 78 | SyntaxKind[PlusToken](16,7): + 79 | SyntaxKind[Identifier](16,8): A 80 | SyntaxKind[CloseBracketToken](16,9): ] 81 | SyntaxKind[Identifier](17,5): E 82 | SyntaxKind[OpenBracketToken](17,6): [ 83 | SyntaxKind[TildeToken](17,7): ~ 84 | SyntaxKind[Identifier](17,8): A 85 | SyntaxKind[CloseBracketToken](17,9): ] 86 | SyntaxKind[Identifier](18,5): E 87 | SyntaxKind[OpenBracketToken](18,6): [ 88 | SyntaxKind[QuestionToken](18,7): ? 89 | SyntaxKind[Identifier](18,8): A 90 | SyntaxKind[CloseBracketToken](18,9): ] 91 | SyntaxKind[Identifier](20,1): G 92 | SyntaxKind[ColonToken](20,3): : 93 | SyntaxKind[UnicodeCharacterLiteral](21,5): <LF> 94 | SyntaxKind[UnicodeCharacterLiteral](22,5): <CR> 95 | SyntaxKind[Identifier](24,1): Z 96 | SyntaxKind[ColonToken](24,3): : 97 | SyntaxKind[Identifier](25,10): A 98 | SyntaxKind[Identifier](26,10): B 99 | SyntaxKind[Identifier](28,1): LookaheadRestriction 100 | SyntaxKind[ColonToken](28,21): : 101 | SyntaxKind[OpenBracketToken](29,5): [ 102 | SyntaxKind[LookaheadKeyword](29,6): lookahead 103 | SyntaxKind[LessThanExclamationToken](29,16): 2 | 3 | U+0000 4 | 5 | 6 | U+FFFF 7 | 8 | 9 | U+10000 10 | 11 | 12 | U+1FFFF 13 | 14 | 15 | U+F0000 16 | 17 | 18 | U+FFFFF 19 | 20 | 21 | U+100000 22 | 23 | 24 | U+10FFFF 25 | 26 | 27 | U+0000 28 | 29 | 30 | 31 | 32 | U+0 33 | 34 | 35 | U+00 36 | 37 | 38 | U+000 39 | 40 | 41 | U+00000 42 | 43 | 44 | U+000000 45 | 46 | 47 | U+110000 48 | 49 | 50 | 51 | 52 | u+0000 53 | 54 | 55 | U+ffff 56 | 57 | 58 | 59 | 60 | <NBSP> 61 | 62 | 63 | <U+2212 MINUS SIGN> 64 | 65 | 66 | <U+0000 A> 67 | 68 | 69 | <U+FFFF A> 70 | 71 | 72 | <U+10000 A> 73 | 74 | 75 | <U+1FFFF A> 76 | 77 | 78 | <U+F0000 A> 79 | 80 | 81 | <U+FFFFF A> 82 | 83 | 84 | <U+100000 A> 85 | 86 | 87 | <U+10FFFF A> 88 | 89 | 90 | <U+0000 A> 91 | 92 | 93 | 94 | 95 | <foo 96 | <bar> 97 | 98 | 99 | <foo\<bar> 100 | 101 | 102 | <U+> 103 | 104 | 105 | <U+A> 106 | 107 | 108 | <U+0> 109 | 110 | 111 | <U+0000> 112 | 113 | 114 | <U+0000 > 115 | 116 | 117 | <U+00000 A> 118 | 119 | 120 | <U+000000 A> 121 | 122 | 123 | <U+110000 A> 124 | 125 | 126 | <U+0000 ©> 127 | 128 | 129 | <A B> 130 | 131 | 132 | 133 | 134 | <u+ffff A> 135 | 136 | 137 | <U+ffff A> 138 | 139 | 140 | -------------------------------------------------------------------------------- /baselines/reference/terminals/unicodeCharacterLiteral.grammar.grammar: -------------------------------------------------------------------------------- 1 | CodePoints : 2 | U+0000 3 | U+FFFF 4 | U+10000 5 | U+1FFFF 6 | U+F0000 7 | U+FFFFF 8 | U+100000 9 | U+10FFFF 10 | U+0000 11 | 12 | ErrorCodePoints : 13 | U+0 14 | U+00 15 | U+000 16 | U+00000 17 | U+000000 18 | U+110000 19 | 20 | WarningCodePoints : 21 | u+0000 22 | U+ffff 23 | 24 | CharacterNames : 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ErrorCharacterNames : 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | WarningCharacterNames : 52 | 53 | 54 | -------------------------------------------------------------------------------- /baselines/reference/terminals/unicodeCharacterLiteral.grammar.md: -------------------------------------------------------------------------------- 1 |   *CodePoints* **:** 2 |    U+0000 3 |    U+FFFF 4 |    U+10000 5 |    U+1FFFF 6 |    U+F0000 7 |    U+FFFFF 8 |    U+100000 9 |    U+10FFFF 10 |    U+0000 11 | 12 |   *ErrorCodePoints* **:** 13 |    U+0 14 |    U+00 15 |    U+000 16 |    U+00000 17 |    U+000000 18 |    U+110000 19 | 20 |   *WarningCodePoints* **:** 21 |    u+0000 22 |    U+ffff 23 | 24 |   *CharacterNames* **:** 25 |    <NBSP> 26 |    <U+2212 MINUS SIGN> 27 |    <U+0000 A> 28 |    <U+FFFF A> 29 |    <U+10000 A> 30 |    <U+1FFFF A> 31 |    <U+F0000 A> 32 |    <U+FFFFF A> 33 |    <U+100000 A> 34 |    <U+10FFFF A> 35 |    <U+0000 A> 36 | 37 |   *ErrorCharacterNames* **:** 38 |    <foo <bar> 39 |    <foo\<bar> 40 |    <U+> 41 |    <U+A> 42 |    <U+0> 43 |    <U+0000> 44 |    <U+0000 > 45 |    <U+00000 A> 46 |    <U+000000 A> 47 |    <U+110000 A> 48 |    <U+0000 ©> 49 |    <A B> 50 | 51 |   *WarningCharacterNames* **:** 52 |    <u+ffff A> 53 |    <U+ffff A> 54 | 55 | -------------------------------------------------------------------------------- /baselines/reference/terminals/unicodeCharacterLiteral.grammar.tokens: -------------------------------------------------------------------------------- 1 | /// terminals/unicodeCharacterLiteral.grammar: 2 | SyntaxKind[Identifier](1,1): CodePoints 3 | SyntaxKind[ColonToken](1,11): : 4 | SyntaxKind[UnicodeCharacterLiteral](2,5): U+0000 5 | SyntaxKind[UnicodeCharacterLiteral](3,5): U+FFFF 6 | SyntaxKind[UnicodeCharacterLiteral](4,5): U+10000 7 | SyntaxKind[UnicodeCharacterLiteral](5,5): U+1FFFF 8 | SyntaxKind[UnicodeCharacterLiteral](6,5): U+F0000 9 | SyntaxKind[UnicodeCharacterLiteral](7,5): U+FFFFF 10 | SyntaxKind[UnicodeCharacterLiteral](8,5): U+100000 11 | SyntaxKind[UnicodeCharacterLiteral](9,5): U+10FFFF 12 | SyntaxKind[UnicodeCharacterLiteral](10,5): U+0000 13 | SyntaxKind[Identifier](12,1): ErrorCodePoints 14 | SyntaxKind[ColonToken](12,16): : 15 | SyntaxKind[UnicodeCharacterLiteral](13,5): U+0 16 | SyntaxKind[UnicodeCharacterLiteral](14,5): U+00 17 | SyntaxKind[UnicodeCharacterLiteral](15,5): U+000 18 | SyntaxKind[UnicodeCharacterLiteral](16,5): U+00000 19 | SyntaxKind[UnicodeCharacterLiteral](17,5): U+000000 20 | SyntaxKind[UnicodeCharacterLiteral](18,5): U+110000 21 | SyntaxKind[Identifier](20,1): WarningCodePoints 22 | SyntaxKind[ColonToken](20,18): : 23 | SyntaxKind[UnicodeCharacterLiteral](21,5): u+0000 24 | SyntaxKind[UnicodeCharacterLiteral](22,5): U+ffff 25 | SyntaxKind[Identifier](24,1): CharacterNames 26 | SyntaxKind[ColonToken](24,15): : 27 | SyntaxKind[UnicodeCharacterLiteral](25,5): 28 | SyntaxKind[UnicodeCharacterLiteral](26,5): 29 | SyntaxKind[UnicodeCharacterLiteral](27,5): 30 | SyntaxKind[UnicodeCharacterLiteral](28,5): 31 | SyntaxKind[UnicodeCharacterLiteral](29,5): 32 | SyntaxKind[UnicodeCharacterLiteral](30,5): 33 | SyntaxKind[UnicodeCharacterLiteral](31,5): 34 | SyntaxKind[UnicodeCharacterLiteral](32,5): 35 | SyntaxKind[UnicodeCharacterLiteral](33,5): 36 | SyntaxKind[UnicodeCharacterLiteral](34,5): 37 | SyntaxKind[UnicodeCharacterLiteral](35,5): 38 | SyntaxKind[Identifier](37,1): ErrorCharacterNames 39 | SyntaxKind[ColonToken](37,20): : 40 | SyntaxKind[UnicodeCharacterLiteral](38,5): <foo 41 | SyntaxKind[UnicodeCharacterLiteral](38,12): <bar> 42 | SyntaxKind[UnicodeCharacterLiteral](39,5): <foo\<bar> 43 | SyntaxKind[UnicodeCharacterLiteral](40,5): 44 | SyntaxKind[UnicodeCharacterLiteral](41,5): 45 | SyntaxKind[UnicodeCharacterLiteral](42,5): 46 | SyntaxKind[UnicodeCharacterLiteral](43,5): 47 | SyntaxKind[UnicodeCharacterLiteral](44,5): 48 | SyntaxKind[UnicodeCharacterLiteral](45,5): 49 | SyntaxKind[UnicodeCharacterLiteral](46,5): 50 | SyntaxKind[UnicodeCharacterLiteral](47,5): 51 | SyntaxKind[UnicodeCharacterLiteral](48,5): 52 | SyntaxKind[UnicodeCharacterLiteral](49,5): 53 | SyntaxKind[Identifier](51,1): WarningCharacterNames 54 | SyntaxKind[ColonToken](51,22): : 55 | SyntaxKind[UnicodeCharacterLiteral](52,5): 56 | SyntaxKind[UnicodeCharacterLiteral](53,5): 57 | SyntaxKind[EndOfFileToken](54,1): «EndOfFileToken» 58 | -------------------------------------------------------------------------------- /baselines/reference/terminals/unicodeCharacterLiteralRange.grammar.emu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | U+0000 through U+FFFF 4 | 5 | 6 | U+0000 through U+FFFF but not > 7 | 8 | 9 | -------------------------------------------------------------------------------- /baselines/reference/terminals/unicodeCharacterLiteralRange.grammar.grammar: -------------------------------------------------------------------------------- 1 | CodePoints : 2 | U+0000 through U+FFFF 3 | U+0000 through U+FFFF but not `>` 4 | -------------------------------------------------------------------------------- /baselines/reference/terminals/unicodeCharacterLiteralRange.grammar.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | CodePoints : 4 |
5 |
6 | U+0000 through U+FFFF 7 |
8 |
9 | U+0000 through U+FFFF but not > 10 |
11 |
12 |
13 |
-------------------------------------------------------------------------------- /baselines/reference/terminals/unicodeCharacterLiteralRange.grammar.md: -------------------------------------------------------------------------------- 1 |   *CodePoints* **:** 2 |    U+0000 **through** U+FFFF 3 |    U+0000 **through** U+FFFF **but not** `` > `` 4 | 5 | -------------------------------------------------------------------------------- /baselines/reference/terminals/unicodeCharacterLiteralRange.grammar.nodes: -------------------------------------------------------------------------------- 1 | /// terminals/unicodeCharacterLiteralRange.grammar: 2 | (1,1)SyntaxKind[SourceFile](filename = "unicodeCharacterLiteralRange.grammar") 3 | (1,1)SyntaxKind[Production] 4 | (1,1)SyntaxKind[Identifier](text = "CodePoints") 5 | (1,11)SyntaxKind[ColonToken] 6 | (2,5)SyntaxKind[RightHandSideList] 7 | (2,5)SyntaxKind[RightHandSide] 8 | (2,5)SyntaxKind[SymbolSpan] 9 | (2,5)SyntaxKind[UnicodeCharacterRange] 10 | (2,5)SyntaxKind[UnicodeCharacterLiteral](text = U+0000) 11 | (2,12)SyntaxKind[ThroughKeyword] 12 | (2,20)SyntaxKind[UnicodeCharacterLiteral](text = U+FFFF) 13 | (3,5)SyntaxKind[RightHandSide] 14 | (3,5)SyntaxKind[SymbolSpan] 15 | (3,5)SyntaxKind[ButNotSymbol] 16 | (3,5)SyntaxKind[UnicodeCharacterRange] 17 | (3,5)SyntaxKind[UnicodeCharacterLiteral](text = U+0000) 18 | (3,12)SyntaxKind[ThroughKeyword] 19 | (3,20)SyntaxKind[UnicodeCharacterLiteral](text = U+FFFF) 20 | (3,27)SyntaxKind[ButKeyword] 21 | (3,31)SyntaxKind[NotKeyword] 22 | (3,35)SyntaxKind[Terminal] 23 | (3,35)SyntaxKind[TerminalLiteral](text = ">") 24 | -------------------------------------------------------------------------------- /baselines/reference/terminals/unicodeCharacterLiteralRange.grammar.tokens: -------------------------------------------------------------------------------- 1 | /// terminals/unicodeCharacterLiteralRange.grammar: 2 | SyntaxKind[Identifier](1,1): CodePoints 3 | SyntaxKind[ColonToken](1,11): : 4 | SyntaxKind[UnicodeCharacterLiteral](2,5): U+0000 5 | SyntaxKind[ThroughKeyword](2,12): through 6 | SyntaxKind[UnicodeCharacterLiteral](2,20): U+FFFF 7 | SyntaxKind[UnicodeCharacterLiteral](3,5): U+0000 8 | SyntaxKind[ThroughKeyword](3,12): through 9 | SyntaxKind[UnicodeCharacterLiteral](3,20): U+FFFF 10 | SyntaxKind[ButKeyword](3,27): but 11 | SyntaxKind[NotKeyword](3,31): not 12 | SyntaxKind[TerminalLiteral](3,35): `>` 13 | SyntaxKind[EndOfFileToken](4,1): «EndOfFileToken» 14 | -------------------------------------------------------------------------------- /bin/grammarkdown: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../dist/cli.js') 3 | -------------------------------------------------------------------------------- /docfx.json: -------------------------------------------------------------------------------- 1 | { 2 | "build": { 3 | "template": ["default", "src/docs/template"], 4 | "content": [ 5 | { "files": ["**/*.yml"], "src": "obj/yaml", "dest": "api" }, 6 | { "files": ["**/*.{md,yml}"], "src": "src/docs/content" } 7 | ], 8 | "resource": [ 9 | { "files": ["**"], "src": "src/docs/resources" }, 10 | { "files": ["**"], "src": "obj/resources" } 11 | ], 12 | "overwrite": [ 13 | { "files": ["**/*.{md,yml}"], "src": "src/docs/overwrite" } 14 | ], 15 | "globalMetadata": { 16 | "_appTitle": "Grammarkdown API Reference", 17 | "_enableSearch": true, 18 | "_enableNewTab": true 19 | }, 20 | "exportRawModel": true, 21 | "rawModelOutputFolder": "obj/raw", 22 | "exportViewModel": true, 23 | "viewModelOutputFolder": "obj/view", 24 | "xref": [ 25 | "src/docs/xrefmap.yml", 26 | "https://esfx.js.org/esfx/xrefmap.yml" 27 | ], 28 | "dest": "docs" 29 | } 30 | } -------------------------------------------------------------------------------- /docs/api/bind.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | Binder API | Grammarkdown API Reference 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 | 27 | 50 | 51 | 58 |
59 |
60 | 61 |
62 |
63 |
64 |

65 |
66 |
    67 |
    68 |
    69 | 103 | 104 |
    105 |
    106 | 115 |
    116 |
    117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /docs/api/check.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | Checker API | Grammarkdown API Reference 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
    25 |
    26 | 27 | 50 | 51 | 58 |
    59 |
    60 | 61 |
    62 |
    63 |
    64 |

    65 |
    66 |
      67 |
      68 |
      69 | 103 | 104 |
      105 |
      106 | 115 |
      116 |
      117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /docs/api/emit.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | Emitter API | Grammarkdown API Reference 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
      25 |
      26 | 27 | 50 | 51 | 58 |
      59 |
      60 | 61 |
      62 |
      63 |
      64 |

      65 |
      66 |
        67 |
        68 |
        69 | 103 | 104 |
        105 |
        106 | 115 |
        116 |
        117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /docs/api/parse.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | Parser API | Grammarkdown API Reference 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
        25 |
        26 | 27 | 50 | 51 | 58 |
        59 |
        60 | 61 |
        62 |
        63 |
        64 |

        65 |
        66 |
          67 |
          68 |
          69 | 103 | 104 |
          105 |
          106 | 115 |
          116 |
          117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbuckton/grammarkdown/0f4521a75b343da5a7dffe2be26d91b89639d11a/docs/favicon.ico -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbuckton/grammarkdown/0f4521a75b343da5a7dffe2be26d91b89639d11a/docs/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbuckton/grammarkdown/0f4521a75b343da5a7dffe2be26d91b89639d11a/docs/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbuckton/grammarkdown/0f4521a75b343da5a7dffe2be26d91b89639d11a/docs/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbuckton/grammarkdown/0f4521a75b343da5a7dffe2be26d91b89639d11a/docs/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /docs/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by Docfx 9 | 10 | 12 | 15 | 21 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /docs/overview/toc.html: -------------------------------------------------------------------------------- 1 |  2 |
          3 |
          4 |
          5 |
          6 | 7 | 8 | 9 |
          10 |
          11 |
          12 |
          13 | 14 | 19 |
          20 |
          21 |
          22 |
          -------------------------------------------------------------------------------- /docs/search-stopwords.json: -------------------------------------------------------------------------------- 1 | [ 2 | "a", 3 | "able", 4 | "about", 5 | "across", 6 | "after", 7 | "all", 8 | "almost", 9 | "also", 10 | "am", 11 | "among", 12 | "an", 13 | "and", 14 | "any", 15 | "are", 16 | "as", 17 | "at", 18 | "be", 19 | "because", 20 | "been", 21 | "but", 22 | "by", 23 | "can", 24 | "cannot", 25 | "could", 26 | "dear", 27 | "did", 28 | "do", 29 | "does", 30 | "either", 31 | "else", 32 | "ever", 33 | "every", 34 | "for", 35 | "from", 36 | "get", 37 | "got", 38 | "had", 39 | "has", 40 | "have", 41 | "he", 42 | "her", 43 | "hers", 44 | "him", 45 | "his", 46 | "how", 47 | "however", 48 | "i", 49 | "if", 50 | "in", 51 | "into", 52 | "is", 53 | "it", 54 | "its", 55 | "just", 56 | "least", 57 | "let", 58 | "like", 59 | "likely", 60 | "may", 61 | "me", 62 | "might", 63 | "most", 64 | "must", 65 | "my", 66 | "neither", 67 | "no", 68 | "nor", 69 | "not", 70 | "of", 71 | "off", 72 | "often", 73 | "on", 74 | "only", 75 | "or", 76 | "other", 77 | "our", 78 | "own", 79 | "rather", 80 | "said", 81 | "say", 82 | "says", 83 | "she", 84 | "should", 85 | "since", 86 | "so", 87 | "some", 88 | "than", 89 | "that", 90 | "the", 91 | "their", 92 | "them", 93 | "then", 94 | "there", 95 | "these", 96 | "they", 97 | "this", 98 | "tis", 99 | "to", 100 | "too", 101 | "twas", 102 | "us", 103 | "wants", 104 | "was", 105 | "we", 106 | "were", 107 | "what", 108 | "when", 109 | "where", 110 | "which", 111 | "while", 112 | "who", 113 | "whom", 114 | "why", 115 | "will", 116 | "with", 117 | "would", 118 | "yet", 119 | "you", 120 | "your" 121 | ] 122 | -------------------------------------------------------------------------------- /docs/styles/main.css: -------------------------------------------------------------------------------- 1 | .alert { 2 | display: block; 3 | position: relative; 4 | border-radius: 6px; 5 | font-size: 1rem; 6 | padding: 1rem; 7 | margin-top: 1rem; 8 | background-color: hsla(0,0%,95%,1); 9 | color: hsla(0,0%,9%,1); 10 | -webkit-transition: height .5s ease-in,opacity .5s ease-in; 11 | transition: height .5s ease-in,opacity .5s ease-in; 12 | word-wrap: break-word; 13 | word-break: break-word; 14 | border: 1px solid hsla(0,0%,100%,0); 15 | } 16 | 17 | .alert.is-warning { 18 | background: hsla(44,100%,90%,1) !important; 19 | border: 1px solid hsla(44,100%,90%,.999); 20 | } 21 | 22 | div.property { 23 | overflow: hidden; 24 | word-wrap: break-word; 25 | white-space: normal; 26 | word-break: break-word; 27 | margin-top: 24px; 28 | } 29 | 30 | div.property:first-of-type { 31 | margin-top: 0px; 32 | } 33 | 34 | dl.parameter { 35 | display: flex; 36 | max-width: 100%; 37 | margin: 0; 38 | } 39 | 40 | dl.parameter dt { 41 | background-color: #fafafa; 42 | display: inline-block; 43 | font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace; 44 | font-weight: 600; 45 | padding: 0 6px 2px; 46 | } 47 | 48 | dl.parameter dd { 49 | margin-left: 12px; 50 | overflow: hidden; 51 | word-wrap: break-word; 52 | white-space: normal; 53 | word-break: break-word; 54 | } 55 | 56 | .affix > ul.level1 { 57 | overflow-y: auto; 58 | } 59 | 60 | .decalaration, .fieldValue, .parameters, .returns, .propertyValue, .typeParameters { 61 | color: #a2a2a2; 62 | margin-top: 24px; 63 | } 64 | 65 | article * { 66 | scroll-margin-top: 120px; 67 | } 68 | 69 | article h1:first-of-type { 70 | scroll-margin-top: 145px; 71 | } 72 | 73 | article h5 { 74 | margin-top: 1.5em; 75 | margin-bottom: .5em; 76 | } -------------------------------------------------------------------------------- /docs/styles/main.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information. 2 | -------------------------------------------------------------------------------- /docs/styles/search-worker.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | importScripts('lunr.min.js'); 3 | 4 | var lunrIndex; 5 | 6 | var stopWords = null; 7 | var searchData = {}; 8 | 9 | lunr.tokenizer.separator = /[\s\-\.\(\)]+/; 10 | 11 | var stopWordsRequest = new XMLHttpRequest(); 12 | stopWordsRequest.open('GET', '../search-stopwords.json'); 13 | stopWordsRequest.onload = function () { 14 | if (this.status != 200) { 15 | return; 16 | } 17 | stopWords = JSON.parse(this.responseText); 18 | buildIndex(); 19 | } 20 | stopWordsRequest.send(); 21 | 22 | var searchDataRequest = new XMLHttpRequest(); 23 | 24 | searchDataRequest.open('GET', '../index.json'); 25 | searchDataRequest.onload = function () { 26 | if (this.status != 200) { 27 | return; 28 | } 29 | searchData = JSON.parse(this.responseText); 30 | 31 | buildIndex(); 32 | 33 | postMessage({ e: 'index-ready' }); 34 | } 35 | searchDataRequest.send(); 36 | 37 | onmessage = function (oEvent) { 38 | var q = oEvent.data.q; 39 | var hits = lunrIndex.search(q); 40 | var results = []; 41 | hits.forEach(function (hit) { 42 | var item = searchData[hit.ref]; 43 | results.push({ 'href': item.href, 'title': item.title, 'keywords': item.keywords }); 44 | }); 45 | postMessage({ e: 'query-ready', q: q, d: results }); 46 | } 47 | 48 | function buildIndex() { 49 | if (stopWords !== null && !isEmpty(searchData)) { 50 | lunrIndex = lunr(function () { 51 | this.pipeline.remove(lunr.stopWordFilter); 52 | this.ref('href'); 53 | this.field('title', { boost: 50 }); 54 | this.field('keywords', { boost: 20 }); 55 | 56 | for (var prop in searchData) { 57 | if (searchData.hasOwnProperty(prop)) { 58 | this.add(searchData[prop]); 59 | } 60 | } 61 | 62 | var docfxStopWordFilter = lunr.generateStopWordFilter(stopWords); 63 | lunr.Pipeline.registerFunction(docfxStopWordFilter, 'docfxStopWordFilter'); 64 | this.pipeline.add(docfxStopWordFilter); 65 | this.searchPipeline.add(docfxStopWordFilter); 66 | }); 67 | } 68 | } 69 | 70 | function isEmpty(obj) { 71 | if(!obj) return true; 72 | 73 | for (var prop in obj) { 74 | if (obj.hasOwnProperty(prop)) 75 | return false; 76 | } 77 | 78 | return true; 79 | } 80 | })(); 81 | -------------------------------------------------------------------------------- /docs/toc.html: -------------------------------------------------------------------------------- 1 |  2 |
          3 |
          4 |
          5 |
          6 | 7 | 8 | 9 |
          10 |
          11 |
          12 |
          13 | 14 | 22 |
          23 |
          24 |
          25 |
          -------------------------------------------------------------------------------- /docs/usage/toc.html: -------------------------------------------------------------------------------- 1 |  2 |
          3 |
          4 |
          5 |
          6 | 7 | 8 | 9 |
          10 |
          11 |
          12 |
          13 | 14 | 25 |
          26 |
          27 |
          28 |
          -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | moduleFileExtensions: [ 5 | "js", 6 | "jsx", 7 | "ts", 8 | "tsx", 9 | "json", 10 | "node", 11 | "grammar" 12 | ], 13 | transform: { 14 | "^.+\\.tsx?": "ts-jest", 15 | "^.+\\.grammar": require.resolve("./scripts/grammarTest.js") 16 | }, 17 | testRegex: "/__tests__/.*(\\.test\\.ts|\\.grammar)$", 18 | globals: { 19 | 'ts-jest': { 20 | tsconfig: 'tsconfig.json', 21 | compiler: require.resolve('typescript') 22 | }, 23 | } 24 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grammarkdown", 3 | "version": "3.3.2", 4 | "description": "Markdown-like DSL for defining syntactic grammars for programming languages.", 5 | "license": "MIT", 6 | "keywords": [ 7 | "grammar", 8 | "language", 9 | "syntax", 10 | "dsl", 11 | "javascript", 12 | "typescript", 13 | "ecmascript" 14 | ], 15 | "author": { 16 | "name": "Ron Buckton", 17 | "email": "rbuckton@chronicles.org", 18 | "url": "http://github.com/rbuckton" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/rbuckton/grammarkdown.git" 23 | }, 24 | "bugs": { 25 | "url": "https://github.com/rbuckton/grammarkdown/issues" 26 | }, 27 | "homepage": "https://github.com/rbuckton/grammarkdown", 28 | "main": "./dist/index.js", 29 | "typings": "./dist/index.d.ts", 30 | "bin": { 31 | "grammarkdown": "./bin/grammarkdown" 32 | }, 33 | "scripts": { 34 | "test": "gulp test", 35 | "build": "gulp build", 36 | "prepublishOnly": "gulp --series build test", 37 | "prepare-docs": "gulp prepare-docs" 38 | }, 39 | "devDependencies": { 40 | "@microsoft/api-documenter": "^7.8.55", 41 | "@microsoft/api-extractor": "^7.9.21", 42 | "@microsoft/api-extractor-model": "^7.9.6", 43 | "@microsoft/tsdoc": "^0.12.21", 44 | "@types/jest": "^26.0.14", 45 | "@types/node": "^14.11.1", 46 | "@types/source-map-support": "^0.5.1", 47 | "@types/yargs": "^15.0.5", 48 | "ajv": "^6.12.5", 49 | "chalk": "^4.0.0", 50 | "del": "^5.1.0", 51 | "ecmarkup": "^3.19.1", 52 | "fancy-log": "^1.3.3", 53 | "gulp": "^4.0.2", 54 | "gulp-insert": "^0.5.0", 55 | "jest": "^26.4.2", 56 | "jest-matcher-utils": "^26.4.2", 57 | "jsonc-parser": "^2.3.1", 58 | "source-map-support": "^0.5.16", 59 | "ts-jest": "^26.4.0", 60 | "typescript": "^4.4.4", 61 | "yargs": "^15.4.1" 62 | }, 63 | "dependencies": { 64 | "@esfx/async-canceltoken": "^1.0.0-pre.13", 65 | "@esfx/cancelable": "^1.0.0-pre.13", 66 | "@esfx/disposable": "^1.0.0-pre.13" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /resources/grammar.css: -------------------------------------------------------------------------------- 1 | .grammar { 2 | font-family: Cambria, Palatino Linotype, Palatino, Liberation Serif, serif; 3 | font-size: 18px; 4 | line-height: 1.5; 5 | color: #333; 6 | } 7 | .grammar .production { 8 | margin-top: 1em; 9 | margin-bottom: 1em; 10 | padding-left: 50px; 11 | } 12 | .grammar .production > .nonterminal { 13 | margin-left: -50px; 14 | } 15 | .grammar .argument-list { 16 | vertical-align: sub; 17 | font-size: .85em; 18 | color: #2aa198; 19 | vertical-align: sub; 20 | font-size: .85em; 21 | } 22 | .grammar .rhs > .argument-list { 23 | margin-right: .5ex; 24 | } 25 | .grammar .punctuation { 26 | font-weight: bold; 27 | } 28 | .grammar .rhs { 29 | white-space: nowrap; 30 | } 31 | .grammar .terminal { 32 | font-family: monospace; 33 | font-weight: bold; 34 | white-space: nowrap; 35 | color: #333; 36 | } 37 | .grammar .one-of-list > .terminal { 38 | margin-left: .5ex; 39 | margin-right: .5ex; 40 | } 41 | .grammar .rhs > .terminal { 42 | margin-left: .5ex; 43 | margin-right: .5ex; 44 | } 45 | .grammar .nonterminal { 46 | font-style: italic; 47 | white-space: nowrap; 48 | color: #333; 49 | } 50 | .grammar .rhs > .nonterminal { 51 | margin-left: .5ex; 52 | margin-right: .5ex; 53 | } 54 | .grammar .nonterminal a, .grammar .nonterminal a:visited { 55 | color: #333; 56 | text-decoration: none; 57 | } 58 | .grammar .unicode-character-literal { 59 | color: #333; 60 | } 61 | .grammar .parameter-list { 62 | vertical-align: sub; 63 | font-size: .85em; 64 | font-family: monospace; 65 | color: #2aa198; 66 | margin-right: .5ex; 67 | } 68 | .grammar .opt { 69 | vertical-align: sub; 70 | font-size: .85em; 71 | font-family: monospace; 72 | color: #b58900; 73 | margin-right: .5ex; 74 | } 75 | .grammar .assertion { 76 | color: #2aa198; 77 | } 78 | .grammar .rhs > .assertion { 79 | margin-left: .5ex; 80 | margin-right: .5ex; 81 | } 82 | .grammar .rhs > .prose { 83 | margin-left: .5ex; 84 | margin-right: .5ex; 85 | } -------------------------------------------------------------------------------- /schema/diagnostics.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://rbuckton.github.io/grammarkdown/localization/diagnostics", 4 | "description": "Localized diagnostic messages", 5 | "type": "object", 6 | "properties": { 7 | "version": { "type": "string", "const": "1.0", "enum": ["1.0"], "description": "The version of the schema (1.0)" }, 8 | "lang": { "type": "string", "description": "The language for the messages." }, 9 | "messages": { "$ref": "#/definitions/DiagnosticMessages" } 10 | }, 11 | "required": ["version", "lang", "messages"], 12 | "definitions": { 13 | "DiagnosticMessages": { 14 | "$id": "#DiagnosticMessages", 15 | "description": "A collection of localized diagnostic messages. The key is the message in en, or the code for an obsolete message. The value is the details of the message.", 16 | "type": "object", 17 | "patternProperties": { 18 | "^\\d+$": { "$ref": "#/definitions/Obsolete" }, 19 | "^(?!\\d+$)": { "$ref": "#/definitions/Diagnostic" } 20 | } 21 | }, 22 | "Obsolete": { 23 | "$id": "#Obsolete", 24 | "description": "Information about an obsolete diagnostic code.", 25 | "default": { "since": "0.0.0" }, 26 | "type": "object", 27 | "properties": { 28 | "since": { "type": "string", "description": "The version the code became obsolete." } 29 | }, 30 | "required": ["since"], 31 | "additionalProperties": false 32 | }, 33 | "Diagnostic": { 34 | "$id": "#Diagnostic", 35 | "oneOf": [ 36 | { "$ref": "#/definitions/Error" }, 37 | { "$ref": "#/definitions/Warning" }, 38 | { "$ref": "#/definitions/Message" }, 39 | { "$ref": "#/definitions/LocalizedString" } 40 | ] 41 | }, 42 | "Error": { 43 | "$id": "#Error", 44 | "description": "A localizable error message.", 45 | "default": { "kind": "error", "code": 1000 }, 46 | "type": "object", 47 | "properties": { 48 | "kind": { "type": "string", "const": "error", "enum": ["error"], "description": "The type of diagnostic message." }, 49 | "code": { "type": "integer", "exclusiveMinimum": 0, "description": "The diagnostic code." } 50 | }, 51 | "required": ["code"] 52 | }, 53 | "Warning": { 54 | "$id": "#Warning", 55 | "description": "A localizable warning message.", 56 | "default": { "kind": "warning", "code": 1000 }, 57 | "type": "object", 58 | "properties": { 59 | "kind": { "type": "string", "const": "warning", "enum": ["warning"], "description": "The type of diagnostic message." }, 60 | "code": { "type": "integer", "exclusiveMinimum": 0, "description": "The diagnostic code." } 61 | }, 62 | "required": ["code", "kind"] 63 | }, 64 | "Message": { 65 | "$id": "#Message", 66 | "description": "A localizable informational message.", 67 | "default": { "kind": "message" }, 68 | "type": "object", 69 | "properties": { 70 | "kind": { "type": "string", "const": "message", "enum": ["message"], "description": "The type of diagnostic message." }, 71 | "code": { "type": "integer", "minimum": 0, "description": "The diagnostic code." } 72 | }, 73 | "required": ["kind"] 74 | }, 75 | "LocalizedString": { 76 | "$id": "#LocalizedString", 77 | "description": "A localized message override.", 78 | "default": "", 79 | "type": "string" 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /scripts/build.js: -------------------------------------------------------------------------------- 1 | const { exec } = require("./exec"); 2 | 3 | /** 4 | * @param {number} timeout 5 | * @param {() => Promise} action 6 | */ 7 | function debounce(timeout, action) { 8 | /** @type {{ promise: Promise, resolve: (value: any) => void, reject: (value: any) => void }} */ 9 | let deferred; 10 | let timer; 11 | 12 | function enqueue() { 13 | if (timer) { 14 | clearTimeout(timer); 15 | timer = undefined; 16 | } 17 | if (!deferred) { 18 | deferred = {}; 19 | deferred.promise = new Promise((resolve, reject) => { 20 | deferred.resolve = resolve; 21 | deferred.reject = reject; 22 | }); 23 | } 24 | timer = setTimeout(run, timeout); 25 | return deferred.promise; 26 | } 27 | 28 | function run() { 29 | if (timer) { 30 | clearTimeout(timer); 31 | timer = undefined; 32 | } 33 | const currentDeferred = deferred; 34 | deferred = undefined; 35 | try { 36 | currentDeferred.resolve(action()); 37 | } 38 | catch (e) { 39 | currentDeferred.reject(e); 40 | } 41 | } 42 | 43 | return enqueue; 44 | } 45 | 46 | /** 47 | * @param {(projects: readonly string[]) => Promise} action 48 | */ 49 | function createProjectQueue(action) { 50 | /** @type {string[]} */ 51 | const projects = []; 52 | const debouncer = debounce(100, async () => { 53 | const currentProjects = projects.slice(); 54 | projects.length = 0; 55 | return action(currentProjects); 56 | }); 57 | /** 58 | * @param {string} project 59 | */ 60 | function enqueue(project) { 61 | projects.push(project); 62 | return debouncer(); 63 | } 64 | return enqueue; 65 | } 66 | 67 | const buildProject = createProjectQueue(async projects => { 68 | await exec(process.execPath, [require.resolve("typescript/lib/tsc.js"), "-b", ...projects]); 69 | }); 70 | 71 | const forceBuildProject = createProjectQueue(async projects => { 72 | await exec(process.execPath, [require.resolve("typescript/lib/tsc.js"), "-b", "--force", ...projects]); 73 | }); 74 | 75 | /** 76 | * Build a project. 77 | * @param {string} project 78 | * @param {object} options 79 | * @param {boolean} [options.force] 80 | */ 81 | exports.buildProject = (project, {force} = {}) => force 82 | ? forceBuildProject(project) 83 | : buildProject(project); 84 | 85 | /** 86 | * Clean a project's outputs. 87 | * @param {string} project 88 | */ 89 | exports.cleanProject = createProjectQueue(async projects => { 90 | await exec(process.execPath, [require.resolve("typescript/lib/tsc.js"), "-b", "--clean", ...projects]); 91 | }); 92 | 93 | /** 94 | * Watch a project for changes. 95 | * @param {string} project 96 | */ 97 | exports.watchProject = createProjectQueue(async projects => { 98 | await exec(process.execPath, [require.resolve("typescript/lib/tsc.js"), "-b", "--watch", ...projects]); 99 | }); 100 | -------------------------------------------------------------------------------- /scripts/exec.js: -------------------------------------------------------------------------------- 1 | const { spawn } = require("child_process"); 2 | const { default: chalk = require("chalk") } = require("chalk"); 3 | const log = require("fancy-log"); 4 | const isWindows = /^win/.test(process.platform); 5 | 6 | /** 7 | * @param {string} cmd 8 | * @param {string[]} args 9 | * @param {object} options 10 | * @param {boolean} [options.ignoreExitCode] 11 | * @param {boolean} [options.waitForExit] 12 | * @param {boolean} [options.verbose] 13 | * @param {string} [options.cwd] 14 | * @returns {Promise<{exitCode: number}>} 15 | */ 16 | function exec(cmd, args = [], { ignoreExitCode, verbose, cwd, waitForExit = true } = {}) { 17 | return new Promise((resolve, reject) => { 18 | const shell = isWindows ? "cmd" : "/bin/sh"; 19 | const shellArgs = isWindows ? ["/c", cmd.includes(" ") >= 0 ? `"${cmd}"` : cmd, ...args] : ["-c", `${cmd} ${args.join(" ")}`]; 20 | if (verbose) log(`> ${chalk.green(cmd)} ${args.join(" ")}`); 21 | const child = spawn(shell, shellArgs, { stdio: waitForExit ? "inherit" : "ignore", cwd, windowsVerbatimArguments: true }); 22 | const done = (exitCode) => { 23 | child.removeAllListeners(); 24 | if (exitCode === 0 || ignoreExitCode || exitCode === undefined && !waitForExit) { 25 | resolve({ exitCode }); 26 | } 27 | else { 28 | reject(new Error(`Process exited with code: ${exitCode}`)); 29 | } 30 | }; 31 | child.on("exit", done); 32 | child.on("error", error => { 33 | child.removeAllListeners(); 34 | reject(error); 35 | }); 36 | if (!waitForExit) { 37 | child.unref(); 38 | setTimeout(() => done(undefined), 100); 39 | } 40 | }); 41 | } 42 | exports.exec = exec; 43 | 44 | class ArgsBuilder { 45 | constructor(args = []) { 46 | this.args = args; 47 | } 48 | addValue(value) { 49 | if (value === undefined) return; 50 | if (Array.isArray(value)) { 51 | for (const v of value) { 52 | this.addValue(v); 53 | } 54 | } 55 | else { 56 | this.args.push(value); 57 | } 58 | } 59 | addSwitch(name, value, defaultValue) { 60 | if (!name || value === undefined || value === defaultValue) return; 61 | if (Array.isArray(value)) { 62 | for (const v of value) { 63 | this.addSwitch(name, v, defaultValue); 64 | } 65 | } 66 | else if (typeof name === "object") { 67 | for (const key of Object.keys(name)) { 68 | this.addSwitch(key, name[key], defaultValue && typeof defaultValue === "object" ? defaultValue[key] : defaultValue); 69 | } 70 | } 71 | else if (typeof name === "string") { 72 | const [prefix, suffix] = 73 | name.startsWith("--") ? ["--", name.slice(2)] : 74 | name.startsWith("-") ? ["-", name.slice(1)] : 75 | name.startsWith("//") ? ["//", name.slice(2)] : 76 | name.startsWith("/") ? ["/", name.slice(1)] : 77 | name.length === "1" ? ["-", name] : 78 | ["--", name]; 79 | if (typeof value === "boolean") { 80 | name = `${prefix}${value ? "" : prefix.startsWith("/") ? "no" : "no-"}${suffix}`; 81 | this.args.push(name); 82 | } 83 | else { 84 | name = `${prefix}${suffix}`; 85 | this.args.push(name, value); 86 | } 87 | } 88 | } 89 | [Symbol.iterator]() { 90 | return this.args.values(); 91 | } 92 | } 93 | exports.ArgsBuilder = ArgsBuilder; -------------------------------------------------------------------------------- /spec/es2020.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ECMAScript 2020 Language Specification 4 | 5 | 6 | 7 | 11 |

          ECMAScript 2020 Language Specification

          12 | 13 |

          Grammar

          14 | 15 |
          -------------------------------------------------------------------------------- /spec/es6.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ECMA-262 6th Edition Grammar 4 | 5 | 6 | 7 | 11 |

          ECMA-262 6th Edition Grammar

          12 | 13 |

          Grammar

          14 | 15 |
          -------------------------------------------------------------------------------- /spec/styles.css: -------------------------------------------------------------------------------- 1 | dl.grammar > dt { 2 | line-height: 3em; 3 | } 4 | dl.grammar > dt .keyword { 5 | font-weight: bold; 6 | } 7 | dl.grammar > dd { 8 | line-height: 1.2em; 9 | } 10 | dl.grammar > dd kbd { 11 | font-weight: bold; 12 | } 13 | dl.grammar > dd var > sub { 14 | font-style: normal; 15 | } 16 | dl.grammar > dd span.symbol { 17 | margin-right: .5em; 18 | } -------------------------------------------------------------------------------- /spec/typescript.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | TypeScript 1.5 Supplemental Grammar 4 | 5 | 6 | 7 | 12 |

          TypeScript 1.5 Supplemental Grammar

          13 | 14 |

          TypeScript Supplemental Grammar

          15 | 16 |
          -------------------------------------------------------------------------------- /src/__tests__/README.md: -------------------------------------------------------------------------------- 1 | ## Grammar tests using `.grammar` files 2 | Grammar tests are handled by `scripts/grammarTest.js`, which is a Jest transformer that generates a set of tests from a `.grammar` file. This transformer is referenced in `jest.config.js`: 3 | 4 | ```js 5 | module.exports = { 6 | ..., 7 | moduleFileExtensions: [ 8 | "js", 9 | "jsx", 10 | "ts", 11 | "tsx", 12 | "json", 13 | "node", 14 | "grammar" // added here to be recognized as a test module by Jest 15 | ], 16 | transform: { 17 | ..., 18 | "^.+\\.grammar": require.resolve("./scripts/grammarTest.js") // specifies transformer for .grammar 19 | }, 20 | testRegex: "/__tests__/.*(\\.test\\.ts|\\.grammar)$", // pick up .grammar files for tests 21 | ... 22 | }; 23 | ``` -------------------------------------------------------------------------------- /src/__tests__/checker.test.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) 2020 Ron Buckton (rbuckton@chronicles.org) 3 | * 4 | * This file is licensed to you under the terms of the MIT License, found in the LICENSE file 5 | * in the root of this repository or package. 6 | */ 7 | 8 | import { CancelToken } from "@esfx/async-canceltoken"; 9 | import { Grammar } from "../grammar"; 10 | import { CoreAsyncHost } from "../host"; 11 | 12 | describe("Checker", () => { 13 | it("cancelable", async () => { 14 | const cts = CancelToken.source(); 15 | const grammar = new Grammar(["cancelable.grammar"], {}, CoreAsyncHost.from({ 16 | async readFile(file: string) { return ""; }, 17 | async writeFile(file: string, content: string) { } 18 | })); 19 | cts.cancel(); 20 | try { 21 | await grammar.check(/*sourceFile*/ undefined, cts.token); 22 | fail("Expected grammar.check() to throw an error."); 23 | } 24 | catch (e) { 25 | } 26 | }); 27 | }); -------------------------------------------------------------------------------- /src/__tests__/cli.test.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) 2020 Ron Buckton (rbuckton@chronicles.org) 3 | * 4 | * This file is licensed to you under the terms of the MIT License, found in the LICENSE file 5 | * in the root of this repository or package. 6 | */ 7 | 8 | import * as path from "path"; 9 | import { spawnSync } from "child_process"; 10 | 11 | describe("cli", () => { 12 | const bin = path.resolve(__dirname, "../../bin/grammarkdown"); 13 | it("exits cleanly when given good input", () => { 14 | const spec = path.resolve(__dirname, "./resources/specs/es6.grammar"); 15 | const { status } = spawnSync(process.execPath, [bin, spec, "--noEmit"], { stdio: "ignore", encoding: "utf8" }); 16 | expect(status).toEqual(0); 17 | }); 18 | 19 | it("exits with an error when given bad input", () => { 20 | const spec = path.resolve(__dirname, "./resources/specs/test.grammar"); 21 | const { status } = spawnSync(process.execPath, [bin, spec, "--noEmit"], { stdio: "ignore", encoding: "utf8" }); 22 | expect(status).not.toEqual(0); 23 | }); 24 | 25 | it("prints help given --help", () => { 26 | const { stdout: help } = spawnSync(process.execPath, [bin, "--help"], { encoding: "utf8" }); 27 | expect(help).toMatch(/Prints this message/); 28 | }); 29 | 30 | it("prints version given --version", () => { 31 | const { stdout: version } = spawnSync(process.execPath, [bin, "--version"], { encoding: "utf8" }); 32 | const packageVersion = require("../../package.json").version; 33 | expect(version.trim()).toEqual(packageVersion.trim()); 34 | }); 35 | 36 | it("prints diagnostics given --diagnostics", () => { 37 | const spec = path.resolve(__dirname, "./resources/specs/es6.grammar"); 38 | const { stderr } = spawnSync(process.execPath, [bin, spec, "--noEmit", "--diagnostics"], { stdio: "pipe", encoding: "utf8" }); 39 | expect(stderr).toMatch(/^ioRead:\s+\d+ms$/m); 40 | expect(stderr).toMatch(/^ioWrite:\s+\d+ms$/m); 41 | expect(stderr).toMatch(/^parse:\s+\d+ms$/m); 42 | expect(stderr).toMatch(/^bind:\s+\d+ms$/m); 43 | expect(stderr).toMatch(/^check:\s+\d+ms$/m); 44 | expect(stderr).toMatch(/^emit:\s+\d+ms$/m); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /src/__tests__/emitter.test.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) 2020 Ron Buckton (rbuckton@chronicles.org) 3 | * 4 | * This file is licensed to you under the terms of the MIT License, found in the LICENSE file 5 | * in the root of this repository or package. 6 | */ 7 | 8 | import { CancelToken } from "@esfx/async-canceltoken"; 9 | import { Grammar } from "../grammar"; 10 | import { CoreAsyncHost } from "../host"; 11 | 12 | describe("Emitter", () => { 13 | it("cancelable", async () => { 14 | const cts = CancelToken.source(); 15 | const grammar = new Grammar(["cancelable.grammar"], {}, CoreAsyncHost.from({ 16 | resolveFile: file => file, 17 | async readFile(file: string) { return ""; }, 18 | async writeFile(file: string, content: string) { } 19 | })); 20 | await grammar.check(/*sourceFile*/ undefined, cts.token); 21 | cts.cancel(); 22 | try { 23 | await grammar.emit(/*sourceFile*/ undefined, /*writeFile*/ undefined, cts.token); 24 | fail("Expected grammar.emit() to throw an error."); 25 | } 26 | catch (e) { 27 | } 28 | }); 29 | }); -------------------------------------------------------------------------------- /src/__tests__/index.ts: -------------------------------------------------------------------------------- 1 | // /*! 2 | // * Copyright (c) 2020 Ron Buckton (rbuckton@chronicles.org) 3 | // * 4 | // * This file is licensed to you under the terms of the MIT License, found in the LICENSE file 5 | // * in the root of this repository or package. 6 | // */ 7 | 8 | // import "source-map-support/register"; 9 | 10 | // import "./scanner-tests"; 11 | // import "./parser-tests"; 12 | // import "./navigator-tests"; 13 | // import "./checker-tests"; 14 | // import "./emitter-tests"; 15 | // import "./grammar-tests"; 16 | // import "./cli-tests"; 17 | // import "./lineOffsetMap-tests"; 18 | 19 | export {}; -------------------------------------------------------------------------------- /src/__tests__/lineOffsetMap.test.ts: -------------------------------------------------------------------------------- 1 | import { LineOffsetMap } from "../lineOffsetMap"; 2 | import { Position } from "../types"; 3 | 4 | describe("LineOffsetMap", () => { 5 | it("getEffectiveFilenameAtPosition", () => { 6 | const map = new LineOffsetMap(); 7 | map.addLineOffset("a.grammar", 0, { file: "a.html", line: 10 }); 8 | map.addLineOffset("b.grammar", 20, { file: "a.html", line: 30 }); 9 | const filename1 = map.getEffectiveFilenameAtPosition("a.grammar", Position.create(0, 0)); 10 | const filename2 = map.getEffectiveFilenameAtPosition("a.grammar", Position.create(11, 0)); 11 | const filename3 = map.getEffectiveFilenameAtPosition("b.grammar", Position.create(0, 0)); 12 | const filename4 = map.getEffectiveFilenameAtPosition("b.grammar", Position.create(31, 0)); 13 | expect(filename1).toEqual("a.html"); 14 | expect(filename2).toEqual("a.html"); 15 | expect(filename3).toEqual("b.grammar"); 16 | expect(filename4).toEqual("a.html"); 17 | }); 18 | it("getEffectivePosition", () => { 19 | const map = new LineOffsetMap(); 20 | map.addLineOffset("a.grammar", 0, { file: "a.html", line: 10 }); 21 | map.addLineOffset("b.grammar", 20, { file: "a.html", line: 30 }); 22 | const position1 = map.getEffectivePosition("a.grammar", Position.create(1, 0)); 23 | const position2 = map.getEffectivePosition("a.grammar", Position.create(11, 0)); 24 | const position3 = map.getEffectivePosition("b.grammar", Position.create(0, 0)); 25 | const position4 = map.getEffectivePosition("b.grammar", Position.create(31, 0)); 26 | expect(position1).toEqual(Position.create(11, 0)); 27 | expect(position2).toEqual(Position.create(21, 0)); 28 | expect(position3).toEqual(Position.create(0, 0)); 29 | expect(position4).toEqual(Position.create(41, 0)); 30 | }); 31 | it("getRawFilenameAtEffectivePosition", () => { 32 | const map = new LineOffsetMap(); 33 | map.addLineOffset("a.grammar", 0, { file: "a.html", line: 10 }); 34 | map.addLineOffset("b.grammar", 20, { file: "a.html", line: 30 }); 35 | const filename1 = map.getRawFilenameAtEffectivePosition("a.html", Position.create(0, 0)); 36 | const filename2 = map.getRawFilenameAtEffectivePosition("a.html", Position.create(11, 0)); 37 | const filename3 = map.getRawFilenameAtEffectivePosition("a.html", Position.create(31, 0)); 38 | expect(filename1).toEqual("a.html"); 39 | expect(filename2).toEqual("a.grammar"); 40 | expect(filename3).toEqual("b.grammar"); 41 | }); 42 | it("getRawPositionFromEffectivePosition", () => { 43 | const map = new LineOffsetMap(); 44 | map.addLineOffset("a.grammar", 0, { file: "a.html", line: 10 }); 45 | map.addLineOffset("b.grammar", 20, { file: "a.html", line: 30 }); 46 | const position1 = map.getRawPositionFromEffectivePosition("a.html", Position.create(0, 0)); 47 | const position2 = map.getRawPositionFromEffectivePosition("a.html", Position.create(11, 0)); 48 | const position3 = map.getRawPositionFromEffectivePosition("a.html", Position.create(31, 0)); 49 | expect(position1).toEqual(Position.create(0, 0)); 50 | expect(position2).toEqual(Position.create(1, 0)); 51 | expect(position3).toEqual(Position.create(21, 0)); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /src/__tests__/matchers.ts: -------------------------------------------------------------------------------- 1 | import { matcherHint, EXPECTED_COLOR, RECEIVED_COLOR } from "jest-matcher-utils"; 2 | import { SyntaxKind, tokenToString } from "../tokens"; 3 | 4 | const printKind = (kind: SyntaxKind) => `${kind} (${tokenToString(kind)})`; 5 | 6 | const passMessage = (received: SyntaxKind, expected: SyntaxKind) => () => `${matcherHint('.not.toBeSyntaxKind')} 7 | 8 | Expected: ${EXPECTED_COLOR(printKind(expected))} 9 | Received: ${RECEIVED_COLOR(printKind(received))} 10 | `; 11 | 12 | const failMessage = (received: SyntaxKind, expected: SyntaxKind) => () => `${matcherHint('.toBeSyntaxKind')} 13 | 14 | Expected: ${EXPECTED_COLOR(printKind(expected))} 15 | Received: ${RECEIVED_COLOR(printKind(received))} 16 | `; 17 | 18 | expect.extend({ 19 | toBeSyntaxKind(received: SyntaxKind, expected: SyntaxKind) { 20 | const pass = received === expected; 21 | const message = (pass ? passMessage : failMessage)(received, expected) 22 | return { pass, message }; 23 | } 24 | }); 25 | 26 | declare global { 27 | namespace jest { 28 | interface Matchers { 29 | toBeSyntaxKind(this: Matchers, actual: SyntaxKind): R; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/__tests__/navigatorUtils.ts: -------------------------------------------------------------------------------- 1 | import "./matchers"; 2 | import { NodeNavigator } from "../navigator"; 3 | import { SyntaxKind } from "../tokens"; 4 | 5 | export function makeExpectToken(navigator: NodeNavigator) { 6 | return { firstToken, lastToken, nextToken, prevToken, bof, eof }; 7 | 8 | function firstToken(kind?: SyntaxKind, text?: string) { 9 | try { 10 | expect(navigator.moveToFirstToken()).toBe(true); 11 | expectTokenRest(kind, text); 12 | } 13 | catch (e: any) { 14 | Error.captureStackTrace(e, firstToken); 15 | throw e; 16 | } 17 | } 18 | 19 | function lastToken(kind?: SyntaxKind, text?: string) { 20 | try { 21 | expect(navigator.moveToLastToken()).toBe(true); 22 | expectTokenRest(kind, text); 23 | } 24 | catch (e: any) { 25 | Error.captureStackTrace(e, lastToken); 26 | throw e; 27 | } 28 | } 29 | 30 | function nextToken(kind?: SyntaxKind, text?: string) { 31 | try { 32 | expect(navigator.moveToNextToken()).toBe(true); 33 | expectTokenRest(kind, text); 34 | } 35 | catch (e: any) { 36 | Error.captureStackTrace(e, nextToken); 37 | throw e; 38 | } 39 | } 40 | 41 | function prevToken(kind?: SyntaxKind, text?: string) { 42 | try { 43 | expect(navigator.moveToPreviousToken()).toBe(true); 44 | expectTokenRest(kind, text); 45 | } 46 | catch (e: any) { 47 | Error.captureStackTrace(e, prevToken); 48 | throw e; 49 | } 50 | } 51 | 52 | function bof() { 53 | try { 54 | expect(navigator.moveToPreviousToken()).toBe(false); 55 | } 56 | catch (e: any) { 57 | Error.captureStackTrace(e, bof); 58 | throw e; 59 | } 60 | } 61 | 62 | function eof() { 63 | try { 64 | expect(navigator.moveToNextToken()).toBe(false); 65 | } 66 | catch (e: any) { 67 | Error.captureStackTrace(e, eof); 68 | throw e; 69 | } 70 | } 71 | 72 | function expectTokenRest(kind?: SyntaxKind, text?: string) { 73 | if (kind !== undefined) expect(navigator.getKind()).toBeSyntaxKind(kind); 74 | if (text !== undefined) expect(navigator.getTextContent()).toBe(text); 75 | } 76 | } -------------------------------------------------------------------------------- /src/__tests__/parser.test.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) 2020 Ron Buckton (rbuckton@chronicles.org) 3 | * 4 | * This file is licensed to you under the terms of the MIT License, found in the LICENSE file 5 | * in the root of this repository or package. 6 | */ 7 | 8 | import { CancelToken } from "@esfx/async-canceltoken"; 9 | import { Parser } from "../parser"; 10 | 11 | describe("Parser", () => { 12 | it("cancelable", () => { 13 | const cts = CancelToken.source(); 14 | const parser = new Parser(); 15 | cts.cancel(); 16 | expect(() => parser.parseSourceFile("cancelable.grammar", "", cts.token)).toThrow(); 17 | }); 18 | }); -------------------------------------------------------------------------------- /src/__tests__/resources.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) 2020 Ron Buckton (rbuckton@chronicles.org) 3 | * 4 | * This file is licensed to you under the terms of the MIT License, found in the LICENSE file 5 | * in the root of this repository or package. 6 | */ 7 | 8 | import { readdirSync, statSync, existsSync, readFileSync } from "fs"; 9 | import { resolve, extname, posix } from "path"; 10 | import { Cancelable } from "@esfx/cancelable"; 11 | import { NodeAsyncHost, NodeAsyncHostOptions } from "../hosts/node"; 12 | 13 | let grammarFiles: TestFile[]; 14 | 15 | export interface TestFile { 16 | readonly basename: string; 17 | readonly path: string; 18 | readonly relative: string; 19 | readonly content: string; 20 | readonly options: Readonly>; 21 | } 22 | 23 | export class TestFileHost extends NodeAsyncHost { 24 | private file: TestFile; 25 | 26 | constructor(file: TestFile, options?: NodeAsyncHostOptions) { 27 | super(options); 28 | this.file = file; 29 | } 30 | 31 | isTestFile(file: string) { 32 | return file === this.file.path || file === this.file.relative; 33 | } 34 | 35 | normalizeFile(file: string) { 36 | return this.isTestFile(file) ? file : super.normalizeFile(file); 37 | } 38 | 39 | resolveFile(file: string, referer?: string) { 40 | if (referer === this.file.relative) referer = this.file.path; 41 | return this.isTestFile(file) ? file : super.resolveFile(file, referer); 42 | } 43 | 44 | async readFile(file: string, cancelable?: Cancelable) { 45 | return this.isTestFile(file) ? this.file.content : super.readFile(file, cancelable); 46 | } 47 | } 48 | 49 | export function getGrammarFiles(): ReadonlyArray { 50 | if (!grammarFiles) { 51 | grammarFiles = []; 52 | collectFilesInPath("../../src/tests/resources", "", grammarFiles); 53 | } 54 | return grammarFiles; 55 | } 56 | 57 | const nonOptionLineRegExp = /(^|\r?\n)(?!\/\/\s*@)/; 58 | 59 | function getOffsetToFirstNonOptionLine(text: string) { 60 | const match = nonOptionLineRegExp.exec(text); 61 | return match ? match.index + match[1].length : text.length; 62 | } 63 | 64 | const optionLineRegExp = /^\/\/\s*@\s*(\w+)\s*:\s*(.*?)\s*$/gm; 65 | 66 | function parseTestFile(content: string) { 67 | const offset = getOffsetToFirstNonOptionLine(content); 68 | const optionContent = content.slice(0, offset); 69 | const options: Record = {}; 70 | for (let match = optionLineRegExp.exec(optionContent); match; match = optionLineRegExp.exec(optionContent)) { 71 | options[match[1]] = match[2]; 72 | } 73 | const nonOptionContent = content.slice(offset); 74 | return { options, content: nonOptionContent }; 75 | } 76 | 77 | function collectFilesInPath(path: string, relative: string, output: TestFile[]) { 78 | path = resolve(__dirname, path); 79 | if (existsSync(path)) { 80 | for (let file of readdirSync(path)) { 81 | const filePath = resolve(path, file); 82 | const fileRelative = relative ? posix.join(relative, file) : file; 83 | const stats = statSync(filePath); 84 | if (stats.isFile()) { 85 | if (extname(file) === ".grammar") { 86 | const { options, content } = parseTestFile(readFileSync(filePath, "utf8")); 87 | output.push({ basename: file, path: filePath, relative: fileRelative, content, options }); 88 | } 89 | } 90 | else if (stats.isDirectory()) { 91 | collectFilesInPath(filePath, fileRelative, output); 92 | } 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /src/__tests__/resources/assertions/constraints/basic.grammar: -------------------------------------------------------------------------------- 1 | Prod[A] : 2 | [~A] `ok` 3 | [+A] `ok` 4 | [?A] `error` 5 | `error` [~A] -------------------------------------------------------------------------------- /src/__tests__/resources/assertions/constraints/invalid.grammar: -------------------------------------------------------------------------------- 1 | // https://github.com/rbuckton/grammarkdown/issues/42 2 | A[One] :: 3 | [+Two] `x` 4 | -------------------------------------------------------------------------------- /src/__tests__/resources/assertions/lookaheadAssertion.grammar: -------------------------------------------------------------------------------- 1 | A: `a` 2 | LookaheadRestriction: 3 | [lookahead but only if MV of |HexDigits| > 0x10FFFF] 3 | -------------------------------------------------------------------------------- /src/__tests__/resources/assertions/proseAssertion.3.grammar: -------------------------------------------------------------------------------- 1 | // @diagnostics: false 2 | 3 | AtomEscape[U, N] :: 4 | [+U] DecimalEscape 5 | [~U] DecimalEscape [> but only if the CapturingGroupNumber of |DecimalEscape| is <= _NcapturingParens_] 6 | [~U] DecimalEscape [> but only if the CapturingGroupNumber of |DecimalEscape| is <= _NcapturingParens_] 7 | CharacterClassEscape[?U] 8 | CharacterEscape[~U, ?N] 9 | [+N] `k` GroupName[?U] 10 | -------------------------------------------------------------------------------- /src/__tests__/resources/assertions/proseAssertion.grammar: -------------------------------------------------------------------------------- 1 | // @diagnostics: false 2 | C : A [>full prose span] 3 | C : A [>head `terminal` middle |B| tail] 4 | -------------------------------------------------------------------------------- /src/__tests__/resources/collapsed/multiple.grammar: -------------------------------------------------------------------------------- 1 | // @full: true 2 | A: `a` 3 | B: `b` -------------------------------------------------------------------------------- /src/__tests__/resources/collapsed/single.grammar: -------------------------------------------------------------------------------- 1 | // @full: true 2 | A: `a` -------------------------------------------------------------------------------- /src/__tests__/resources/ecmarkup/grammarTest.grammar: -------------------------------------------------------------------------------- 1 | // @diagnostics: false 2 | 3 | FooTerminal : 4 | Inserting `anRHS` 5 | Deleting `anRHS` 6 | Modifying Existing Old Production 7 | 8 | 9 | NewTerminal : 10 | `t` 11 | 12 | 13 | LookaheadExample :: 14 | `&&` `n` [lookahead <! {`1`, `3`, `5`, `7`, `9`}] DecimalDigits 15 | DecimalDigit [lookahead <! DecimalDigit] 16 | 17 | HTMLEntitiesExample :: 18 | > This is technically a “production” 19 | -------------------------------------------------------------------------------- /src/__tests__/resources/html/aroundProduction.grammar: -------------------------------------------------------------------------------- 1 | // @emit: ecmarkup, grammarkdown 2 | // @nodes: true 3 | A :: 4 | `b` 5 | 6 | 7 | A :: 8 | `b` 9 | 10 | 11 | A :: 12 | `b` 13 | 14 | A :: 15 | `b` -------------------------------------------------------------------------------- /src/__tests__/resources/html/aroundSymbols.grammar: -------------------------------------------------------------------------------- 1 | A: `b` 2 | D[U]: 3 | `a` 4 | `a` `b` 5 | `a` `b` 6 | `a` `b` `c` 7 | `a` `b`? `c` 8 | `a` A `c` 9 | [~U] `a` 10 | -------------------------------------------------------------------------------- /src/__tests__/resources/html/aroundTerminalInOneOf.grammar: -------------------------------------------------------------------------------- 1 | A :: one of 2 | `a` `b` `c` 3 | -------------------------------------------------------------------------------- /src/__tests__/resources/html/gh80.grammar: -------------------------------------------------------------------------------- 1 | // @issue: https://github.com/rbuckton/grammarkdown/issues/80 2 | // @emit: grammarkdown 3 | // @diagnostics: false 4 | X :: 5 | Y 6 | 7 | 8 | X :: 9 | Y 10 | 11 | 12 | X :: 13 | Y -------------------------------------------------------------------------------- /src/__tests__/resources/html/htmlComments.grammar: -------------------------------------------------------------------------------- 1 | // @issue: https://github.com/rbuckton/grammarkdown/issues/80 2 | // @emit: grammarkdown,ecmarkup,html 3 | // @diagnostics: false 4 | 5 | X : Y 6 | 7 | 8 | 9 | 10 | 11 | X : Y 12 | 13 | 14 | 15 | 16 | 17 | X : Y 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/__tests__/resources/html/mixed.grammar: -------------------------------------------------------------------------------- 1 | // @emit: ecmarkup, html, markdown 2 | // wrapping collapsed production 3 | A: `a` 4 | 5 | // wrapping expanded production on same lines 6 | B: 7 | `a` 8 | 9 | // wrapping expanded production on different lines 10 | 11 | C: 12 | `a` 13 | 14 | 15 | // on right-hand-side 16 | D[U]: 17 | `a` 18 | `a` `b` 19 | `a` `b` 20 | `a` `b` `c` 21 | `a` `b`? `c` 22 | `a` A `c` 23 | [~U] `a` 24 | -------------------------------------------------------------------------------- /src/__tests__/resources/meta/define/byRegion.grammar: -------------------------------------------------------------------------------- 1 | // @emit: none 2 | // default for 'noUnusedParameters' is 'false' 3 | A[A] : `a` // ok 4 | 5 | @define noUnusedParameters true 6 | B[A] :: `a` // error 7 | 8 | @define noUnusedParameters false 9 | C[A] :: `a` // ok 10 | 11 | @define noUnusedParameters true 12 | D[A] :: `a` // error 13 | 14 | @define noUnusedParameters default 15 | E[A] :: `a` // ok 16 | -------------------------------------------------------------------------------- /src/__tests__/resources/meta/line/lineMeta.grammar: -------------------------------------------------------------------------------- 1 | @line 5 "foo" 2 | A : B 3 | 4 | @line 25 5 | C : D 6 | 7 | @line default 8 | E : F -------------------------------------------------------------------------------- /src/__tests__/resources/nonterminals/withArguments.grammar: -------------------------------------------------------------------------------- 1 | A: `a` 2 | E[A] : A 3 | F[A] : 4 | E 5 | E[A] 6 | E[A, B] 7 | E[?B] 8 | E[+A] 9 | E[~A] 10 | E[?A] 11 | -------------------------------------------------------------------------------- /src/__tests__/resources/oneOfList/oneOfList.grammar: -------------------------------------------------------------------------------- 1 | // @emit: grammarkdown 2 | // @preserveSourceLineTerminators: true 3 | @define noStrictParametricProductions true 4 | 5 | Keyword :: one of 6 | `break` `do` `in` `typeof` 7 | `case` `else` `instanceof` `var` 8 | `catch` `export` `new` `void` 9 | `class` `extends` `return` `while` 10 | `const` `finally` `super` `with` 11 | `continue` `for` `switch` `yield` 12 | `debugger` `function` `this` 13 | `default` `if` `throw` 14 | `delete` `import` `try` 15 | 16 | FutureReservedWord :: one of 17 | `enum` 18 | // `await` is only treated as a |FutureReservedWord| when |Module| is the goal symbol of the syntactic grammar. 19 | `await` 20 | // The following tokens are also considered to be |FutureReservedWord|s when parsing strict mode code (see 10.2.1). 21 | `implements` `package` `protected` 22 | `interface` `private` `public` 23 | -------------------------------------------------------------------------------- /src/__tests__/resources/parameters/noUnusedParameters.grammar: -------------------------------------------------------------------------------- 1 | // https://github.com/rbuckton/grammarkdown/issues/46 2 | @define noUnusedParameters true 3 | 4 | Foo[A] :: 5 | Bar[+A] 6 | 7 | Bar[A] :: 8 | `x` 9 | 10 | Baz[A] :: 11 | `x` 12 | 13 | Baz[A] :: 14 | [~A] `y` 15 | 16 | Quxx[A] :: 17 | `x` 18 | 19 | Quxx[A] :: 20 | Baz[?A] 21 | -------------------------------------------------------------------------------- /src/__tests__/resources/placeholder/placeholder.grammar: -------------------------------------------------------------------------------- 1 | A: A @ A -------------------------------------------------------------------------------- /src/__tests__/resources/rightHandSide/complex.grammar: -------------------------------------------------------------------------------- 1 | // @diagnostics: false 2 | A : |B| but not one of `"` or `\` or U+0000 through U+001F 3 | -------------------------------------------------------------------------------- /src/__tests__/resources/rightHandSideList/multipleItems.grammar: -------------------------------------------------------------------------------- 1 | // @full: true 2 | A: 3 | `a` 4 | `b` -------------------------------------------------------------------------------- /src/__tests__/resources/rightHandSideList/singleItem.grammar: -------------------------------------------------------------------------------- 1 | // @full: true 2 | A: 3 | `a` -------------------------------------------------------------------------------- /src/__tests__/resources/specs/test.grammar: -------------------------------------------------------------------------------- 1 | // @full: true 2 | A : A @ A 3 | B : |A| but not one of `"` or `\` or U+0000 through U+001F 4 | 5 | C : A [>full prose span] 6 | C : A [>head `terminal` middle |B| tail] 7 | 8 | D[A] : A 9 | D[B] : A 10 | 11 | E[A] : A 12 | F[A] : 13 | E 14 | E[A] 15 | E[A, B] 16 | E[?B] 17 | E[+A] 18 | E[~A] 19 | E[?A] 20 | 21 | G : 22 | <LF> 23 | <CR> 24 | 25 | Z : 26 | A 27 | B 28 | 29 | LookaheadRestriction: 30 | [lookahead 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | ErrorCharacterNames: 39 | <foo<bar> 40 | <foo\<bar> 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | WarningCharacterNames: 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/__tests__/resources/terminals/unicodeCharacterLiteralRange.grammar: -------------------------------------------------------------------------------- 1 | // @full: true 2 | CodePoints: 3 | U+0000 through U+FFFF 4 | U+0000 through U+FFFF but not `>` 5 | -------------------------------------------------------------------------------- /src/__tests__/scanner.test.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) 2020 Ron Buckton (rbuckton@chronicles.org) 3 | * 4 | * This file is licensed to you under the terms of the MIT License, found in the LICENSE file 5 | * in the root of this repository or package. 6 | */ 7 | 8 | import { CancelToken } from "@esfx/async-canceltoken"; 9 | import { DiagnosticMessages } from "../diagnostics"; 10 | import { SourceFile } from "../nodes"; 11 | import { Scanner } from "../scanner"; 12 | 13 | describe("Scanner", () => { 14 | it("cancelable", () => { 15 | const sourceFile = new SourceFile("cancelable.grammar", "", []); 16 | const diagnostics = new DiagnosticMessages(); 17 | diagnostics.setSourceFile(sourceFile); 18 | const cts = CancelToken.source(); 19 | const scanner = new Scanner(sourceFile.filename, sourceFile.text, diagnostics, cts.token); 20 | cts.cancel(); 21 | expect(() => scanner.scan()).toThrow(); 22 | }); 23 | }); -------------------------------------------------------------------------------- /src/diagnostics.generated.ts: -------------------------------------------------------------------------------- 1 | import type { Diagnostic } from "./diagnostics"; 2 | export const Diagnostics = { 3 | _0_or_1_: { code: 0, message: "{0} or {1}" } as Diagnostic, 4 | Constant_expected: { code: 1000, message: "Constant expected" } as Diagnostic, 5 | _0_expected: { code: 1001, message: "{0} expected" } as Diagnostic, 6 | Unexpected_token_0_: { code: 1002, message: "Unexpected token {0}" } as Diagnostic, 7 | Invalid_character: { code: 1003, message: "Invalid character" } as Diagnostic, 8 | Unterminated_string_literal: { code: 1004, message: "Unterminated string literal" } as Diagnostic, 9 | Invalid_escape_sequence: { code: 1005, message: "Invalid escape sequence" } as Diagnostic, 10 | Digit_expected: { code: 1006, message: "Digit expected" } as Diagnostic, 11 | Production_expected: { code: 1007, message: "Production expected" } as Diagnostic, 12 | Unterminated_identifier_literal: { code: 1008, message: "Unterminated identifier literal" } as Diagnostic, 13 | Obsolete_0_: { code: 1009, message: "Obsolete: {0}", warning: true } as Diagnostic, 14 | HTML_trivia_not_allowed_here: { code: 1010, message: "HTML trivia not allowed here" } as Diagnostic, 15 | Unicode_code_point_literals_must_have_at_least_four_hexadecimal_digits: { code: 1011, message: "Unicode code point literals must have at least four hexadecimal digits" } as Diagnostic, 16 | Unicode_code_point_literals_with_more_than_four_digits_may_not_have_leading_zeros: { code: 1012, message: "Unicode code point literals with more than four digits may not have leading zeros" } as Diagnostic, 17 | Unicode_code_point_literals_should_use_uppercase_U_prefix: { code: 1013, message: "Unicode code point literals should use uppercase 'U+' prefix", warning: true } as Diagnostic, 18 | Unicode_code_point_literals_should_use_uppercase_hexadecimal_digits: { code: 1014, message: "Unicode code point literals should use uppercase hexadecimal digits", warning: true } as Diagnostic, 19 | Unicode_code_point_literal_value_is_outside_of_the_allowed_range: { code: 1015, message: "Unicode code point literal value is outside of the allowed range" } as Diagnostic, 20 | Unicode_character_name_literal_may_not_start_with_U_unless_it_is_a_valid_code_point: { code: 1016, message: "Unicode character name literal may not start with 'U+' unless it is a valid code point" } as Diagnostic, 21 | Unicode_character_name_literal_that_includes_a_code_point_must_have_a_description: { code: 1017, message: "Unicode character name literal that includes a code point must have a description" } as Diagnostic, 22 | Unicode_character_name_literal_code_point_and_description_must_be_separated_by_whitespace: { code: 1018, message: "Unicode character name literal code point and description must be separated by whitespace" } as Diagnostic, 23 | Unicode_character_name_literal_code_point_description_may_only_contain_printable_ASCII_characters: { code: 1019, message: "Unicode character name literal code point description may only contain printable ASCII characters" } as Diagnostic, 24 | Unicode_character_name_literal_must_be_an_ASCII_identifier: { code: 1020, message: "Unicode character name literal must be an ASCII identifier" } as Diagnostic, 25 | Cannot_find_name_0_: { code: 2000, message: "Cannot find name: '{0}'" } as Diagnostic, 26 | Duplicate_identifier_0_: { code: 2001, message: "Duplicate identifier: '{0}'" } as Diagnostic, 27 | Duplicate_terminal_0_: { code: 2002, message: "Duplicate terminal: `{0}`" } as Diagnostic, 28 | Argument_0_cannot_be_specified_multiple_times: { code: 2003, message: "Argument '{0}' cannot be specified multiple times" } as Diagnostic, 29 | Production_0_does_not_have_a_parameter_named_1_: { code: 2004, message: "Production '{0}' does not have a parameter named '{1}'" } as Diagnostic, 30 | Production_0_is_missing_parameter_1_All_definitions_of_production_0_must_specify_the_same_formal_parameters: { code: 2006, message: "Production '{0}' is missing parameter '{1}'. All definitions of production '{0}' must specify the same formal parameters" } as Diagnostic, 31 | There_is_no_argument_given_for_parameter_0_: { code: 2007, message: "There is no argument given for parameter '{0}'" } as Diagnostic, 32 | Parameter_0_is_unused: { code: 2008, message: "Parameter '{0}' is unused" } as Diagnostic, 33 | }; 34 | -------------------------------------------------------------------------------- /src/diagnostics.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "lang": "en", 4 | "messages": { 5 | // Messages 6 | "{0} or {1}": { "kind": "message" }, 7 | 8 | // Error codes from 1000-1999 are reserved for grammar diagnostics 9 | "Constant expected": { "code": 1000 }, 10 | "{0} expected": { "code": 1001 }, 11 | "Unexpected token {0}": { "code": 1002 }, 12 | "Invalid character": { "code": 1003, "kind": "error" }, 13 | "Unterminated string literal": { "code": 1004, "kind": "error" }, 14 | "Invalid escape sequence": { "code": 1005, "kind": "error" }, 15 | "Digit expected": { "code": 1006, "kind": "error" }, 16 | "Production expected": { "code": 1007, "kind": "error" }, 17 | "Unterminated identifier literal": { "code": 1008, "kind": "error" }, 18 | "Obsolete: {0}": { "code": 1009, "kind": "warning" }, 19 | "HTML trivia not allowed here": { "code": 1010, "kind": "error" }, 20 | "Unicode code point literals must have at least four hexadecimal digits": { "code": 1011, "kind": "error" }, 21 | "Unicode code point literals with more than four digits may not have leading zeros": { "code": 1012, "kind": "error" }, 22 | "Unicode code point literals should use uppercase 'U+' prefix": { "code": 1013, "kind": "warning" }, 23 | "Unicode code point literals should use uppercase hexadecimal digits": { "code": 1014, "kind": "warning" }, 24 | "Unicode code point literal value is outside of the allowed range": { "code": 1015, "kind": "error" }, 25 | "Unicode character name literal may not start with 'U+' unless it is a valid code point": { "code": 1016, "kind": "error" }, 26 | "Unicode character name literal that includes a code point must have a description": { "code": 1017, "kind": "error" }, 27 | "Unicode character name literal code point and description must be separated by whitespace": { "code": 1018, "kind": "error" }, 28 | "Unicode character name literal code point description may only contain printable ASCII characters": { "code": 1019, "kind": "error" }, 29 | "Unicode character name literal must be an ASCII identifier": { "code": 1020, "kind": "error" }, 30 | 31 | // Error codes from 2000-2999 are reserved for checker diagnostics 32 | "Cannot find name: '{0}'": { "code": 2000, "kind": "error" }, 33 | "Duplicate identifier: '{0}'": { "code": 2001, "kind": "error" }, 34 | "Duplicate terminal: `{0}`": { "code": 2002, "kind": "error" }, 35 | "Argument '{0}' cannot be specified multiple times": { "code": 2003, "kind": "error" }, 36 | "Production '{0}' does not have a parameter named '{1}'": { "code": 2004, "kind": "error" }, 37 | "Production '{0}' is missing parameter '{1}'. All definitions of production '{0}' must specify the same formal parameters": { "code": 2006, "kind": "error" }, 38 | "There is no argument given for parameter '{0}'": { "code": 2007, "kind": "error" }, 39 | "Parameter '{0}' is unused": { "code": 2008, "kind": "error" }, 40 | } 41 | } -------------------------------------------------------------------------------- /src/docs/content/api/bind.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: bind 3 | title: Binder API 4 | --- 5 | 6 | # Binder API 7 | This section contains the various services Grammarkdown uses to bind Grammarkdown documents. -------------------------------------------------------------------------------- /src/docs/content/api/check.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: check 3 | title: Checker API 4 | --- 5 | 6 | # Checker API 7 | This section contains the various services Grammarkdown uses to check Grammarkdown documents. -------------------------------------------------------------------------------- /src/docs/content/api/compiler.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: compiler 3 | title: Compiler API 4 | --- 5 | 6 | # Compiler API 7 | The Compiler API provides the common services used to validate a @grammarkdown!Grammar:class. 8 | 9 | # Related Links 10 | - @grammarkdown!Grammar:class class 11 | - @grammarkdown!CompilerOptions:interface interface -------------------------------------------------------------------------------- /src/docs/content/api/emit.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: emit 3 | title: Emitter API 4 | --- 5 | 6 | # Emitter API 7 | This section contains the various services Grammarkdown uses for emit. -------------------------------------------------------------------------------- /src/docs/content/api/hosts.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: hosts 3 | title: Host API 4 | --- 5 | 6 | # Host API 7 | The Host API provides the mechanism through which other Grammarkdown Service APIs can interact with the file system asynchronously. 8 | API operations using an asynchronous host use the native ECMAScript `Promise` to signal the completion of operations. 9 | 10 | # Host Class Hierarchy 11 | 12 | - @grammarkdown!CoreAsyncHost:class 13 | - @grammarkdown!NodeAsyncHost:class 14 | - @grammarkdown!StringAsyncHost:class 15 | -------------------------------------------------------------------------------- /src/docs/content/api/nodes.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: nodes 3 | title: Syntax Nodes 4 | --- 5 | 6 | # Syntax Nodes 7 | This section contains the various [Nodes](xref:grammarkdown!Node:class) defined by the Grammarkdown language. 8 | -------------------------------------------------------------------------------- /src/docs/content/api/parse.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: parse 3 | title: Parser API 4 | --- 5 | 6 | # Parser API 7 | This section contains the various services Grammarkdown uses to parse Grammarkdown syntax. -------------------------------------------------------------------------------- /src/docs/content/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Overview 3 | --- 4 | 5 | # Grammarkdown 6 | 7 | `grammarkdown` is a markdown-style parser for syntactic grammars, designed to make it easily to rapidly prototype a grammar and statically verify its consistency. 8 | The grammar supported by `grammarkdown` is based on the parametric grammar used by ECMA-262 (the JavaScript language standard). 9 | 10 | # Related 11 | 12 | * [ecmarkup](https://bterlson.github.io/ecmarkup) 13 | 14 | [npm-image]: https://img.shields.io/npm/v/grammarkdown.svg 15 | [npm-url]: https://npmjs.org/package/grammarkdown 16 | 17 | [travis-image]: https://travis-ci.org/rbuckton/grammarkdown.svg?branch=master 18 | [travis-url]: https://travis-ci.org/rbuckton/grammarkdown 19 | -------------------------------------------------------------------------------- /src/docs/content/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Usage 2 | href: usage/ 3 | homepage: index.md 4 | - name: API Reference 5 | uid: 'grammarkdown!' 6 | href: ../../../obj/yaml/ -------------------------------------------------------------------------------- /src/docs/content/usage/cli.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | ``` 4 | Syntax: grammarkdown [options] [...files] 5 | 6 | Examples: grammarkdown es6.grammar 7 | grammarkdown --out es6.md --format markdown es6.grammar 8 | 9 | Options: 10 | -f, --format FORMAT The output format. 11 | -h, --help Prints this message. 12 | --noChecks Does not perform static checking of the grammar. 13 | --noEmit Does not emit output. 14 | --noEmitOnError Does not emit output if there are errors. 15 | -o, --out FILE Specify the output file. 16 | -v, --version Prints the version. 17 | ``` -------------------------------------------------------------------------------- /src/docs/content/usage/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Overview 2 | href: ../index.md 3 | - name: Command Line 4 | href: cli.md 5 | - name: Syntax 6 | href: syntax.md -------------------------------------------------------------------------------- /src/docs/overwrite/compileroptions.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: 'grammarkdown!CompilerOptions:interface' 3 | --- 4 | -------------------------------------------------------------------------------- /src/docs/overwrite/coreasynchost.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: 'grammarkdown!CoreAsyncHost:class' 3 | example: 4 | - *content 5 | --- 6 | 7 | The following example shows how you can create a @grammarkdown!CoreAsyncHost:class from a `Map`: 8 | 9 | ## [JavaScript](#tab/js) 10 | ```ts 11 | const files = new Map([ 12 | ["a.grammar", "..."], 13 | ["b.grammar", "..."] 14 | ]); 15 | const host = new CoreAsyncHost({ 16 | ignoreCase: false, 17 | useBuiltinGrammers: false, 18 | resolveFile: file => file, 19 | readFile: file => files.get(file) 20 | }); 21 | ``` 22 | *** 23 | 24 | --- 25 | uid: 'grammarkdown!CoreAsyncHost.forFile:member(1)' 26 | example: 27 | - *content 28 | --- 29 | 30 | The following example shows how you can create a @grammarkdown!CoreAsyncHost:class for a `string`: 31 | 32 | ## [JavaScript](#tab/js) 33 | ```ts 34 | const content = "..."; 35 | const host = CoreAsyncHost.forFile(content); 36 | ``` 37 | *** -------------------------------------------------------------------------------- /src/docs/overwrite/coreasynchostoptions.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: 'grammarkdown!CoreAsyncHostOptions:interface' 3 | --- 4 | -------------------------------------------------------------------------------- /src/docs/overwrite/coresynchost.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: 'grammarkdown!CoreSyncHost:class' 3 | example: 4 | - *content 5 | --- 6 | 7 | The following example shows how you can create a @grammarkdown!CoreSyncHost:class from a `Map`: 8 | 9 | ```ts 10 | const files = new Map([ 11 | ["a.grammar", "..."], 12 | ["b.grammar", "..."] 13 | ]); 14 | const host = new CoreSyncHost({ 15 | ignoreCase: false, 16 | useBuiltinGrammers: false, 17 | resolveFile: file => file, 18 | readFileSync: file => files.get(file) 19 | }); 20 | ``` 21 | 22 | --- 23 | uid: 'grammarkdown!CoreSyncHost.forFile:member(1)' 24 | example: 25 | - *content 26 | --- 27 | 28 | The following example shows how you can create a @grammarkdown!CoreSyncHost:class for a `string`: 29 | 30 | ```ts 31 | const content = "..."; 32 | const host = CoreSyncHost.forFile(content); 33 | ``` 34 | -------------------------------------------------------------------------------- /src/docs/overwrite/coresynchostoptions.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: 'grammarkdown!CoreSyncHostOptions:interface' 3 | --- 4 | -------------------------------------------------------------------------------- /src/docs/overwrite/grammarkdown.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: 'grammarkdown!' 3 | title: API Reference 4 | --- 5 | 6 | `grammarkdown` has an API that can be consumed: 7 | 8 | ```js 9 | var grammarkdown = require("grammarkdown") 10 | , Grammar = grammarkdown.Grammar 11 | , EmitFormat = grammarkdown.EmitFormat 12 | 13 | var filename = "..."; 14 | var source = "..."; 15 | var output; 16 | 17 | // parse 18 | var grammar = new Grammar( 19 | [filename], 20 | { format: EmitFormat.markdown }, 21 | function () { return source; }); 22 | 23 | // bind (optional, bind happens automatically during check) 24 | grammar.bindSync(); 25 | 26 | // check (optional, check happens automatically during emit) 27 | grammar.checkSync(); 28 | 29 | // emit 30 | grammar.emitSync(undefined, function (file, text) { output = text; }); 31 | 32 | console.log(output); 33 | ``` -------------------------------------------------------------------------------- /src/docs/overwrite/hostbase.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: 'grammarkdown!HostBase:class' 3 | --- 4 | -------------------------------------------------------------------------------- /src/docs/overwrite/hostbaseoptions.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: 'grammarkdown!HostBaseOptions:interface' 3 | --- 4 | -------------------------------------------------------------------------------- /src/docs/overwrite/nodeasynchost.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: 'grammarkdown!NodeAsyncHost:class' 3 | --- 4 | -------------------------------------------------------------------------------- /src/docs/overwrite/nodeasynchostoptions.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: 'grammarkdown!NodeAsyncHostOptions:interface' 3 | --- 4 | -------------------------------------------------------------------------------- /src/docs/overwrite/nodesynchost.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: 'grammarkdown!NodeSyncHost:class' 3 | --- 4 | -------------------------------------------------------------------------------- /src/docs/overwrite/nodesynchostoptions.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: 'grammarkdown!SyncHostOptions:interface' 3 | --- 4 | -------------------------------------------------------------------------------- /src/docs/overwrite/stringasynchost.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: 'grammarkdown!StringAsyncHost:class' 3 | --- 4 | -------------------------------------------------------------------------------- /src/docs/overwrite/stringsynchost.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: 'grammarkdown!StringSyncHost:class' 3 | --- 4 | -------------------------------------------------------------------------------- /src/docs/template/UniversalReference.html.primary.tmpl: -------------------------------------------------------------------------------- 1 | {{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} 2 | {{!master(layout/_master.tmpl)}} 3 | 4 | {{#isNamespace}} 5 | {{>partials/uref/namespace}} 6 | {{/isNamespace}} 7 | {{#isClass}} 8 | {{>partials/uref/class}} 9 | {{/isClass}} 10 | {{#isEnum}} 11 | {{>partials/uref/enum}} 12 | {{/isEnum}} 13 | -------------------------------------------------------------------------------- /src/docs/template/partials/deprecated.tmpl.partial: -------------------------------------------------------------------------------- 1 | {{#deprecated}} 2 |
          3 | {{{__global.warning}}} 4 |

          {{__global.deprecated}}

          5 | {{#content}} 6 | {{{content}}} 7 | {{/content}} 8 |
          9 | {{/deprecated}} -------------------------------------------------------------------------------- /src/docs/template/partials/namespaceSubtitle.tmpl.partial: -------------------------------------------------------------------------------- 1 | {{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} 2 | {{#inNamespace}} 3 | {{__global.namespacesInSubtitle}} 4 | {{/inNamespace}} 5 | {{#inClass}} 6 | {{__global.classesInSubtitle}} 7 | {{/inClass}} 8 | {{#inStruct}} 9 | {{__global.structsInSubtitle}} 10 | {{/inStruct}} 11 | {{#inInterface}} 12 | {{__global.interfacesInSubtitle}} 13 | {{/inInterface}} 14 | {{#inEnum}} 15 | {{__global.enumsInSubtitle}} 16 | {{/inEnum}} 17 | {{#inDelegate}} 18 | {{__global.delegatesInSubtitle}} 19 | {{/inDelegate}} 20 | {{#inFunction}} 21 | {{__global.functionsInSubtitle}} 22 | {{/inFunction}} 23 | {{#inVariable}} 24 | {{__global.variablesInSubtitle}} 25 | {{/inVariable}} 26 | {{#inTypeAlias}} 27 | {{__global.typeAliasesInSubtitle}} 28 | {{/inTypeAlias}} 29 | -------------------------------------------------------------------------------- /src/docs/template/partials/uref/class.header.tmpl.partial: -------------------------------------------------------------------------------- 1 | {{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} 2 | 3 |

          {{>partials/title}}

          4 | {{#namespace}} 5 |
          {{__global.namespace}}: {{{value.specName.0.value}}}
          6 | {{/namespace}} 7 | {{#package}} 8 |
          {{__global.package}}: {{{value.specName.0.value}}}
          9 | {{/package}} 10 | {{>partials/deprecated}} 11 |
          {{{summary}}}
          12 |
          {{{conceptual}}}
          13 | {{#inheritance.0}} 14 |
          15 |
          {{__global.inheritance}}
          16 | {{/inheritance.0}} 17 | {{#inheritance.0.value}} 18 | {{>partials/uref/inheritance}} 19 | {{/inheritance.0.value}} 20 | {{#inheritance.0}} 21 |
          {{name.0.value}}
          22 |
          23 | {{/inheritance.0}} 24 | {{#inheritedMembers.0}} 25 |
          26 |
          {{__global.inheritedMembers}}
          27 | {{/inheritedMembers.0}} 28 | {{#inheritedMembers}} 29 |
          30 | {{#definition}} 31 | 32 | {{/definition}} 33 | {{^definition}} 34 | 35 | {{/definition}} 36 |
          37 | {{/inheritedMembers}} 38 | {{#inheritedMembers.0}} 39 |
          40 | {{/inheritedMembers.0}} 41 | {{#remarks}} 42 |
          {{__global.remarks}}
          43 |
          {{{remarks}}}
          44 | {{/remarks}} 45 | {{#example.0}} 46 |
          {{__global.examples}}
          47 | {{/example.0}} 48 | {{#example}} 49 | {{{.}}} 50 | {{/example}} -------------------------------------------------------------------------------- /src/docs/template/partials/uref/enum.tmpl.partial: -------------------------------------------------------------------------------- 1 | {{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} 2 | 3 | {{>partials/uref/class.header}} 4 | {{#children}} 5 |

          {{>partials/classSubtitle}}

          6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | {{#children}} 16 | 17 | 18 | 19 | 20 | 21 | {{/children}} 22 | 23 |
          {{__global.name}}{{__global.value}}{{__global.description}}
          {{name.0.value}}{{{numericValue}}}{{{summary}}}
          24 | {{/children}} 25 | {{#extensionMethods.0}} 26 |

          {{__global.extensionMethods}}

          27 | {{/extensionMethods.0}} 28 | {{#extensionMethods}} 29 |
          30 | {{#definition}} 31 | 32 | {{/definition}} 33 | {{^definition}} 34 | 35 | {{/definition}} 36 |
          37 | {{/extensionMethods}} 38 | -------------------------------------------------------------------------------- /src/docs/template/styles/main.css: -------------------------------------------------------------------------------- 1 | .alert { 2 | display: block; 3 | position: relative; 4 | border-radius: 6px; 5 | font-size: 1rem; 6 | padding: 1rem; 7 | margin-top: 1rem; 8 | background-color: hsla(0,0%,95%,1); 9 | color: hsla(0,0%,9%,1); 10 | -webkit-transition: height .5s ease-in,opacity .5s ease-in; 11 | transition: height .5s ease-in,opacity .5s ease-in; 12 | word-wrap: break-word; 13 | word-break: break-word; 14 | border: 1px solid hsla(0,0%,100%,0); 15 | } 16 | 17 | .alert.is-warning { 18 | background: hsla(44,100%,90%,1) !important; 19 | border: 1px solid hsla(44,100%,90%,.999); 20 | } 21 | 22 | div.property { 23 | overflow: hidden; 24 | word-wrap: break-word; 25 | white-space: normal; 26 | word-break: break-word; 27 | margin-top: 24px; 28 | } 29 | 30 | div.property:first-of-type { 31 | margin-top: 0px; 32 | } 33 | 34 | dl.parameter { 35 | display: flex; 36 | max-width: 100%; 37 | margin: 0; 38 | } 39 | 40 | dl.parameter dt { 41 | background-color: #fafafa; 42 | display: inline-block; 43 | font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace; 44 | font-weight: 600; 45 | padding: 0 6px 2px; 46 | } 47 | 48 | dl.parameter dd { 49 | margin-left: 12px; 50 | overflow: hidden; 51 | word-wrap: break-word; 52 | white-space: normal; 53 | word-break: break-word; 54 | } 55 | 56 | .affix > ul.level1 { 57 | overflow-y: auto; 58 | } 59 | 60 | .decalaration, .fieldValue, .parameters, .returns, .propertyValue, .typeParameters { 61 | color: #a2a2a2; 62 | margin-top: 24px; 63 | } 64 | 65 | article * { 66 | scroll-margin-top: 120px; 67 | } 68 | 69 | article h1:first-of-type { 70 | scroll-margin-top: 145px; 71 | } 72 | 73 | article h5 { 74 | margin-top: 1.5em; 75 | margin-bottom: .5em; 76 | } -------------------------------------------------------------------------------- /src/docs/template/token.json: -------------------------------------------------------------------------------- 1 | { 2 | "namespacesInSubtitle": "Namespaces", 3 | "classesInSubtitle": "Classes", 4 | "structsInSubtitle": "Structs", 5 | "interfacesInSubtitle": "Interfaces", 6 | "enumsInSubtitle": "Enums", 7 | "delegatesInSubtitle": "Delegates", 8 | "constructorsInSubtitle": "Constructors", 9 | "fieldsInSubtitle": "Fields", 10 | "propertiesInSubtitle": "Properties", 11 | "methodsInSubtitle": "Methods", 12 | "eventsInSubtitle": "Events", 13 | "operatorsInSubtitle": "Operators", 14 | "eiisInSubtitle": "Explicit Interface Implementations", 15 | "functionsInSubtitle": "Functions", 16 | "variablesInSubtitle": "Variables", 17 | "typeAliasesInSubtitle": "Type Aliases", 18 | "membersInSubtitle": "Members", 19 | "improveThisDoc": "Improve this Doc", 20 | "viewSource": "View Source", 21 | "inheritance": "Inheritance", 22 | "inheritedMembers": "Inherited Members", 23 | "package": "Package", 24 | "namespace": "Namespace", 25 | "assembly": "Assembly", 26 | "syntax": "Syntax", 27 | "overrides": "Overrides", 28 | "implements": "Implements", 29 | "remarks": "Remarks", 30 | "examples": "Examples", 31 | "seealso": "See Also", 32 | "declaration": "Declaration", 33 | "parameters": "Parameters", 34 | "typeParameters": "Type Parameters", 35 | "type": "Type", 36 | "name": "Name", 37 | "value": "Value", 38 | "description": "Description", 39 | "returns": "Returns", 40 | "fieldValue": "Field Value", 41 | "propertyValue": "Property Value", 42 | "eventType": "Event Type", 43 | "variableValue": "Variable Value", 44 | "typeAliasType": "Type Alias Type", 45 | "exceptions": "Exceptions", 46 | "condition": "Condition", 47 | "extensionMethods": "Extension Methods", 48 | "note": "
          Note
          ", 49 | "warning": "
          Warning
          ", 50 | "tip": "
          Tip
          ", 51 | "important": "
          Important
          ", 52 | "caution": "
          Caution
          ", 53 | "deprecated": "Deprecated" 54 | } -------------------------------------------------------------------------------- /src/docs/xrefmap.yml: -------------------------------------------------------------------------------- 1 | ### YamlMime:XRefMap 2 | sorted: false 3 | references: 4 | - uid: 'prex!' 5 | name: 'prex' 6 | fullName: 'prex' 7 | href: https://github.com/rbuckton/prex/#readme 8 | - uid: 'prex!CancellationToken:class' 9 | name: 'CancellationToken' 10 | fullName: 'CancellationToken' 11 | href: https://github.com/rbuckton/prex/blob/master/docs/cancellation.md#class-cancellationtoken 12 | -------------------------------------------------------------------------------- /src/emitter/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) 2020 Ron Buckton (rbuckton@chronicles.org) 3 | * 4 | * This file is licensed to you under the terms of the MIT License, found in the LICENSE file 5 | * in the root of this repository or package. 6 | */ 7 | 8 | export * from "./emitter"; 9 | export * from "./ecmarkup"; 10 | export * from "./html"; 11 | export * from "./markdown"; 12 | export * from "./grammarkdown"; 13 | -------------------------------------------------------------------------------- /src/hosts/node.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) 2020 Ron Buckton (rbuckton@chronicles.org) 3 | * 4 | * This file is licensed to you under the terms of the MIT License, found in the LICENSE file 5 | * in the root of this repository or package. 6 | */ 7 | 8 | import * as path from "path"; 9 | import * as fs from "fs"; 10 | import * as os from "os"; 11 | import * as url from "url"; 12 | import { isUri, isFileUri, getLocalPath } from "../core"; 13 | import { ReadFileCallback, WriteFileCallback, CoreAsyncHost } from "../host"; 14 | 15 | /** {@docCategory Hosts} */ 16 | export interface NodeAsyncHostOptions { 17 | /** 18 | * Indicates whether the host is case-insensitive (`true`) or case-sensitive (`false`). 19 | */ 20 | ignoreCase?: boolean; 21 | /** 22 | * A set of known grammars in the form `{ "name": "path" }` 23 | */ 24 | knownGrammars?: Record; 25 | /** 26 | * Indicates whether to include builtin grammars in the set of known grammars. 27 | */ 28 | useBuiltinGrammars?: boolean; 29 | readFile?: ReadFileCallback | false; 30 | writeFile?: WriteFileCallback | false; 31 | allowUris?: false; 32 | } 33 | 34 | /** {@docCategory Hosts} */ 35 | export class NodeAsyncHost extends CoreAsyncHost { 36 | constructor(options: NodeAsyncHostOptions = {}) { 37 | const { 38 | ignoreCase = ignoreCaseFallback(), 39 | readFile = readFileFallback, 40 | writeFile = writeFileFallback, 41 | ...baseOptions 42 | } = options; 43 | super({ 44 | ...baseOptions, 45 | ignoreCase, 46 | resolveFile: resolveFileFallback, 47 | readFile: readFile ? (file, cancelToken) => readFile(file, cancelToken) : cannotComplete, 48 | writeFile: writeFile ? (file, content, cancelToken) => writeFile(file, content, cancelToken) : cannotComplete 49 | }); 50 | } 51 | } 52 | 53 | function resolveFileFallback(file: string, referer?: string) { 54 | if (isFileUri(file) || path.isAbsolute(file)) { 55 | return file; 56 | } 57 | else if (referer) { 58 | return isUri(referer) 59 | ? url.resolve(referer, file) 60 | : path.resolve(path.dirname(referer), file); 61 | } 62 | else { 63 | return path.resolve(file); 64 | } 65 | } 66 | 67 | function readFileFallback(file: string) { 68 | return new Promise((resolve, reject) => { 69 | file = getLocalPath(file); 70 | if (isUri(file)) return undefined; 71 | fs.readFile(file, null, (error, buffer) => resolve(error ? undefined : decodeBuffer(buffer))); 72 | }); 73 | } 74 | 75 | function decodeBuffer(buffer: Buffer) { 76 | if (buffer[0] === 0xff && buffer[1] === 0xfe) { 77 | // UTF-16 LE BOM 78 | return buffer.slice(2).toString("utf16le"); 79 | } 80 | else if (buffer[0] === 0xfe && buffer[1] === 0xff) { 81 | // UTF-16 BE BOM 82 | return buffer.slice(2).swap16().toString("utf16le"); 83 | } 84 | else if (buffer[0] === 0xef && buffer[1] === 0xbb && buffer[2] === 0xbf) { 85 | // UTF-8 with BOM 86 | return buffer.slice(3).toString("utf8"); 87 | } 88 | else { 89 | // plain UTF8 90 | return buffer.toString("utf8"); 91 | } 92 | } 93 | 94 | function writeFileFallback(file: string, content: string) { 95 | return new Promise((resolve, reject) => { 96 | file = getLocalPath(file); 97 | if (isUri(file)) throw new Error("Cannot write to a non-file URI."); 98 | fs.writeFile(file, content, "utf8", (error) => error ? reject(error) : resolve()); 99 | }); 100 | } 101 | 102 | function cannotComplete(): never { 103 | throw new Error("Operation cannot be completed"); 104 | } 105 | 106 | function ignoreCaseFallback() { 107 | return /^(win32|win64|darwin)$/.test(os.platform()); 108 | } 109 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) 2020 Ron Buckton (rbuckton@chronicles.org) 3 | * 4 | * This file is licensed to you under the terms of the MIT License, found in the LICENSE file 5 | * in the root of this repository or package. 6 | */ 7 | 8 | export { TextRange, Position, Range } from "./types"; 9 | export { 10 | ReadFileCallback, 11 | WriteFileCallback, 12 | CoreAsyncHostOptions, 13 | CoreAsyncHost, 14 | StringAsyncHost, 15 | } from "./host"; 16 | export { 17 | NodeAsyncHost, 18 | NodeAsyncHostOptions, 19 | } from "./hosts/node"; 20 | export * from "./diagnostics"; 21 | export { 22 | CompilerOptions, 23 | EmitFormat, 24 | NewLineKind, 25 | getDefaultOptions, 26 | } from "./options"; 27 | export type { 28 | LineOffsetMap 29 | } from "./lineOffsetMap"; 30 | export * from "./tokens"; 31 | export * from "./nodes"; 32 | export * from "./symbols"; 33 | export * from "./scanner"; 34 | export * from "./parser"; 35 | export * from "./binder"; 36 | export * from "./checker"; 37 | export * from "./emitter"; 38 | export * from "./stringwriter"; 39 | export * from "./grammar"; 40 | export * from "./navigator"; 41 | export * from "./visitor"; -------------------------------------------------------------------------------- /src/nodeInternal.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) 2021 Ron Buckton (rbuckton@chronicles.org) 3 | * 4 | * This file is licensed to you under the terms of the MIT License, found in the LICENSE file 5 | * in the root of this repository or package. 6 | */ 7 | 8 | import type { TriviaKind } from "./tokens"; 9 | import type { Node, Trivia, TriviaBase, SourceFile } from "./nodes"; 10 | import type { NodeVisitor } from "./visitor"; 11 | import type { DiagnosticMessages } from "./diagnostics"; 12 | 13 | interface NodeAccessor { 14 | setTextRange(node: Node, pos: number, end: number): void; 15 | setDetachedTrivia(node: Node, trivia: readonly Trivia[] | undefined): void; 16 | setLeadingTrivia(node: Node, trivia: readonly Trivia[] | undefined): void; 17 | setTrailingTrivia(node: Node, trivia: readonly Trivia[] | undefined): void; 18 | edgeCount(node: Node): number; 19 | edgeName(node: Node, offset: number): string | undefined; 20 | edgeValue(node: Node, offset: number): Node | ReadonlyArray | undefined; 21 | accept(node: Node, visitor: NodeVisitor): Node; 22 | } 23 | 24 | interface TriviaAccessor { 25 | setPrecedingFields( 26 | node: TriviaBase, 27 | hasPrecedingLineTerminator: boolean, 28 | hasPrecedingBlankLine: boolean, 29 | hasPrecedingWhiteSpace: boolean, 30 | ): void; 31 | setFollowingFields( 32 | node: TriviaBase, 33 | hasFollowingLineTerminator: boolean, 34 | hasFollowingBlankLine: boolean, 35 | hasFollowingWhiteSpace: boolean, 36 | ): void; 37 | } 38 | 39 | interface SourceFileAccessor { 40 | setImports(node: SourceFile, imports: readonly string[] | undefined): void; 41 | setParseDiagnostics(node: SourceFile, parseDiagnostics: DiagnosticMessages | undefined): void; 42 | getParseDiagnostics(node: SourceFile): DiagnosticMessages | undefined; 43 | } 44 | 45 | let nodeAccessor: NodeAccessor | undefined; 46 | let triviaAccessor: TriviaAccessor | undefined; 47 | let sourceFileAccessor: SourceFileAccessor | undefined; 48 | 49 | /* @internal */ 50 | export function setNodeAccessor(accessor: NodeAccessor) { nodeAccessor = accessor; } 51 | 52 | /* @internal */ 53 | export function getNodeAccessor() { 54 | if (!nodeAccessor) throw new TypeError("Accessor not set"); 55 | return nodeAccessor; 56 | } 57 | 58 | 59 | /* @internal */ 60 | export function setTriviaAccessor(accessor: TriviaAccessor) { triviaAccessor = accessor; } 61 | 62 | /* @internal */ 63 | export function getTriviaAccessor() { 64 | if (!triviaAccessor) throw new TypeError("Accessor not set"); 65 | return triviaAccessor; 66 | } 67 | 68 | 69 | /* @internal */ 70 | export function setSourceFileAccessor(accessor: SourceFileAccessor) { sourceFileAccessor = accessor; } 71 | 72 | /* @internal */ 73 | export function getSourceFileAccessor() { 74 | if (!sourceFileAccessor) throw new TypeError("Accessor not set"); 75 | return sourceFileAccessor; 76 | } -------------------------------------------------------------------------------- /src/read-package.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) 2020 Ron Buckton (rbuckton@chronicles.org) 3 | * 4 | * This file is licensed to you under the terms of the MIT License, found in the LICENSE file 5 | * in the root of this repository or package. 6 | */ 7 | 8 | import { readFile, readFileSync } from "fs"; 9 | 10 | export interface Person { 11 | name?: string; 12 | email?: string; 13 | } 14 | 15 | export interface Package { 16 | name?: string; 17 | author?: string | Person; 18 | homepage?: string; 19 | version?: string; 20 | license?: string; 21 | description?: string; 22 | keywords?: string[]; 23 | main?: string; 24 | typings?: string; 25 | dependencies?: { [name: string]: string; }; 26 | devDependencies?: { [name: string]: string; }; 27 | } 28 | 29 | export function readPackage(packagePath: string, done: (err: any, pkg: Package | undefined) => void): void { 30 | readFile(packagePath, "utf8", (err, data) => { 31 | try { 32 | if (err) throw err; 33 | done(undefined, JSON.parse(data)); 34 | } 35 | catch (e) { 36 | done(err, undefined); 37 | } 38 | }); 39 | } 40 | 41 | export function readPackageSync(packagePath: string): Package { 42 | let data = readFileSync(packagePath, "utf8"); 43 | return JSON.parse(data); 44 | } -------------------------------------------------------------------------------- /src/regionMap.ts: -------------------------------------------------------------------------------- 1 | import { binarySearchBy, compare, compareNumbers } from "./core"; 2 | import { SourceFile } from "./nodes"; 3 | import { SortedUniqueList } from "./sortedUniqueList"; 4 | 5 | function compareRegions(a: Region, b: Region) { 6 | return compare(a.line, b.line); 7 | } 8 | 9 | function selectLine(a: Region) { 10 | return a.line; 11 | } 12 | 13 | export interface Region { 14 | readonly line: number; 15 | readonly value: T; 16 | } 17 | 18 | export interface ReadonlyRegionMap { 19 | findRegion(sourceFile: SourceFile, line: number): Region | undefined; 20 | regions(sourceFile: SourceFile, line: number): IterableIterator>; 21 | } 22 | 23 | export class RegionMap implements ReadonlyRegionMap { 24 | private _sourceFileRegions?: Map>>; 25 | private _equateRegions: (a: Region, b: Region) => boolean; 26 | 27 | constructor(equateValues: (a: T, b: T) => boolean) { 28 | this._equateRegions = (a, b) => a.line === b.line && equateValues(a.value, b.value); 29 | } 30 | 31 | /** 32 | * Adds a `Region` for a source file. 33 | * @param sourceFile The source file in which to add a region 34 | * @param line The line number of the region start 35 | * @param value The value for the region 36 | */ 37 | addRegion(sourceFile: SourceFile | string, line: number, value: T) { 38 | this._sourceFileRegions ??= new Map(); 39 | const filename = typeof sourceFile === "string" ? sourceFile : sourceFile.filename; 40 | let regions = this._sourceFileRegions.get(filename); 41 | if (!regions) this._sourceFileRegions.set(filename, regions = new SortedUniqueList(compareRegions, this._equateRegions)); 42 | regions.push({ line, value }); 43 | } 44 | 45 | /** 46 | * Adds or updates a `Region` for a source file. 47 | * @param sourceFile The source file in which to add a region 48 | * @param line The line number of the region start 49 | * @param value The value for the region 50 | */ 51 | upsertRegion(sourceFile: SourceFile | string, line: number, upsert: (value: T | undefined) => T) { 52 | this._sourceFileRegions ??= new Map(); 53 | const filename = typeof sourceFile === "string" ? sourceFile : sourceFile.filename; 54 | let regions = this._sourceFileRegions.get(filename); 55 | if (!regions) this._sourceFileRegions.set(filename, regions = new SortedUniqueList(compareRegions, this._equateRegions)); 56 | const array = regions.toArray(); 57 | let index = binarySearchBy(array, line, selectLine, compareNumbers); 58 | if (index >= 0 && index < array.length) { 59 | const region = array[index]; 60 | regions.mutate((array) => { 61 | if (array[index] === region) { 62 | array[index] = { line, value: upsert(region.value) }; 63 | } 64 | }); 65 | } 66 | else { 67 | regions.push({ line, value: upsert(undefined) }); 68 | } 69 | } 70 | 71 | /** 72 | * Finds the nearest `Region` that starts at or prior to the provided `line`. 73 | * @param sourceFile The source file in which to find a region. 74 | * @param line The line number from which to start searching. 75 | */ 76 | findRegion(sourceFile: SourceFile | string, line: number) { 77 | const filename = typeof sourceFile === "string" ? sourceFile : sourceFile.filename; 78 | const regions = this._sourceFileRegions?.get(filename)?.toArray(); 79 | if (!regions) return; 80 | let index = binarySearchBy(regions, line, selectLine, compareNumbers); 81 | if (index < 0) index = ~index - 1; 82 | if (index >= 0 && index < regions.length) return regions[index]; 83 | } 84 | 85 | /** 86 | * Yields each `Region` that starts at or prior to the provided `line`, starting with the nearest `Region` first. 87 | * @param sourceFile The source file in which to find a region. 88 | * @param line The line number from which to start searching. 89 | */ 90 | * regions(sourceFile: SourceFile | string, line: number) { 91 | const filename = typeof sourceFile === "string" ? sourceFile : sourceFile.filename; 92 | const regions = this._sourceFileRegions?.get(filename)?.toArray(); 93 | if (!regions) return; 94 | let index = binarySearchBy(regions, line, selectLine, compareNumbers); 95 | if (index < 0) index = ~index - 1; 96 | while (index >= 0 && index < regions.length) { 97 | yield regions[index]; 98 | index--; 99 | } 100 | } 101 | 102 | copyFrom(other: RegionMap) { 103 | if (other._sourceFileRegions) { 104 | this._sourceFileRegions ||= new Map(); 105 | for (const [filename, regions] of other._sourceFileRegions) { 106 | this._sourceFileRegions.set(filename, regions.clone()); 107 | } 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /src/sortedUniqueList.ts: -------------------------------------------------------------------------------- 1 | import { stableSort, deduplicateSorted } from "./core"; 2 | 3 | /* @internal */ 4 | export class SortedUniqueList { 5 | private relationalComparer: (a: T, b: T) => number; 6 | private equalityComparer: (a: T, b: T) => boolean; 7 | private sortedAndUnique = true; 8 | private copyOnWrite = false; 9 | private unsafeArray: T[] = []; 10 | private unsafeLast: T | undefined = undefined; 11 | 12 | constructor(relationalComparer: (a: T, b: T) => number, equalityComparer: (a: T, b: T) => boolean) { 13 | this.relationalComparer = relationalComparer; 14 | this.equalityComparer = equalityComparer; 15 | } 16 | 17 | get size() { 18 | return this.unsafeArray.length; 19 | } 20 | 21 | get last() { 22 | this.ensureSortedAndUnique(); 23 | return this.unsafeLast === undefined 24 | ? this.unsafeLast = (this.unsafeArray?.length ? this.unsafeArray[this.unsafeArray.length - 1] : undefined) 25 | : this.unsafeLast; 26 | } 27 | 28 | clone() { 29 | const copy = new SortedUniqueList(this.relationalComparer, this.equalityComparer); 30 | copy.sortedAndUnique = this.sortedAndUnique; 31 | copy.copyOnWrite = false; 32 | copy.unsafeArray = this.unsafeArray.slice(); 33 | copy.unsafeLast = this.unsafeLast; 34 | return copy; 35 | } 36 | 37 | valueAt(index: number) { 38 | this.ensureSortedAndUnique(); 39 | return this.unsafeArray[index]; 40 | } 41 | 42 | push(...values: T[]) { 43 | for (const value of values) { 44 | if (this.sortedAndUnique) { 45 | const last = this.last; 46 | if (last === undefined || this.relationalComparer(value, last) > 0) { 47 | this.unsafeAdd(value, /*sortedAndUnique*/ true); 48 | continue; 49 | } 50 | if (this.equalityComparer(value, last)) { 51 | continue; 52 | } 53 | } 54 | this.unsafeAdd(value, /*sortedAndUnique*/ false); 55 | } 56 | } 57 | 58 | mutate(cb: (array: T[]) => void) { 59 | if (this.copyOnWrite) { 60 | this.unsafeArray = this.unsafeArray.slice(); 61 | } 62 | this.copyOnWrite = true; 63 | cb(this.unsafeArray); 64 | this.sortedAndUnique = false; 65 | this.unsafeLast = undefined; 66 | } 67 | 68 | toArray(): readonly T[] { 69 | this.ensureSortedAndUnique(); 70 | this.copyOnWrite = true; 71 | return this.unsafeArray; 72 | } 73 | 74 | private unsafeAdd(value: T, sortedAndUnique: boolean) { 75 | if (this.copyOnWrite) { 76 | this.unsafeArray = this.unsafeArray.slice(); 77 | this.copyOnWrite = false; 78 | } 79 | 80 | this.unsafeArray.push(value); 81 | this.unsafeLast = sortedAndUnique ? value : undefined; 82 | this.sortedAndUnique = sortedAndUnique; 83 | } 84 | 85 | private ensureSortedAndUnique() { 86 | if (!this.sortedAndUnique) { 87 | this.unsafeArray = deduplicateSorted(stableSort(this.unsafeArray, this.relationalComparer), this.equalityComparer); 88 | this.unsafeLast = undefined; 89 | this.sortedAndUnique = true; 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /src/symbols.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) 2020 Ron Buckton (rbuckton@chronicles.org) 3 | * 4 | * This file is licensed to you under the terms of the MIT License, found in the LICENSE file 5 | * in the root of this repository or package. 6 | */ 7 | 8 | let nextSymbolId = 0; 9 | 10 | /** {@docCategory Bind} */ 11 | export enum SymbolKind { 12 | SourceFile, 13 | Production, 14 | Parameter 15 | } 16 | 17 | /** {@docCategory Bind} */ 18 | export class Symbol { 19 | public id: number = ++nextSymbolId; 20 | public name: string; 21 | public kind: SymbolKind; 22 | public parent: Symbol | undefined; 23 | public locals: SymbolTable | undefined; 24 | 25 | constructor(kind: SymbolKind, name: string) { 26 | this.kind = kind; 27 | this.name = name; 28 | } 29 | } 30 | 31 | /** {@docCategory Bind} */ 32 | export class SymbolTable { 33 | private nameMap: Map> | undefined; 34 | 35 | get isEmpty() { return !this.nameMap; } 36 | 37 | public resolveSymbol(name: string, kind: SymbolKind): Symbol | undefined { 38 | if (name) { 39 | return this.getSymbols(kind, /*create*/ false)?.get(name); 40 | } 41 | 42 | return undefined; 43 | } 44 | 45 | public * symbolsOfKind(kind: SymbolKind) { 46 | const symbols = this.nameMap?.get(kind); 47 | if (symbols) { 48 | yield* symbols.values(); 49 | } 50 | } 51 | 52 | /* @internal */ 53 | public copyFrom(other: SymbolTable) { 54 | if (other === this || !other.nameMap) return; 55 | for (const [kind, otherSymbols] of other.nameMap) { 56 | this.nameMap ??= new Map>(); 57 | let symbols = this.nameMap.get(kind); 58 | if (!symbols) this.nameMap.set(kind, symbols = new Map()); 59 | for (const [name, symbol] of otherSymbols) { 60 | symbols.set(name, symbol); 61 | } 62 | } 63 | } 64 | 65 | /* @internal */ 66 | public declareSymbol(name: string | undefined, kind: SymbolKind, parent?: Symbol): Symbol { 67 | let symbol: Symbol | undefined; 68 | if (name) { 69 | const symbols = this.getSymbols(kind, /*create*/ true); 70 | symbol = symbols.get(name); 71 | if (!symbol) { 72 | symbol = new Symbol(kind, name); 73 | symbols.set(name, symbol); 74 | } 75 | } 76 | else { 77 | symbol = new Symbol(kind, "*missing*"); 78 | } 79 | 80 | symbol.parent = parent; 81 | return symbol; 82 | } 83 | 84 | private getSymbols(kind: SymbolKind, create: true): Map; 85 | private getSymbols(kind: SymbolKind, create: boolean): Map | undefined; 86 | private getSymbols(kind: SymbolKind, create: boolean): Map | undefined { 87 | let symbols = this.nameMap?.get(kind); 88 | if (!symbols && create) (this.nameMap ??= new Map()).set(kind, symbols = new Map()); 89 | return symbols; 90 | } 91 | } -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright (c) 2020 Ron Buckton (rbuckton@chronicles.org) 3 | * 4 | * This file is licensed to you under the terms of the MIT License, found in the LICENSE file 5 | * in the root of this repository or package. 6 | */ 7 | 8 | /** {@docCategory Other} */ 9 | export interface TextRange { 10 | readonly pos: number; 11 | readonly end: number; 12 | } 13 | 14 | /** {@docCategory Other} */ 15 | export interface Position { 16 | readonly line: number; 17 | readonly character: number; 18 | } 19 | 20 | /** {@docCategory Other} */ 21 | export namespace Position { 22 | export function create(line: number, character: number): Position { 23 | return { line, character }; 24 | } 25 | 26 | export function clone(position: Position): Position { 27 | return create(position.line, position.character); 28 | } 29 | 30 | export function compare(left: Position, right: Position) { 31 | if (left.line < right.line) return -1; 32 | if (left.line > right.line) return +1; 33 | if (left.character < right.character) return -1; 34 | if (left.character > right.character) return +1; 35 | return 0; 36 | } 37 | 38 | export function equals(left: Position, right: Position) { 39 | return left.line === right.line 40 | && left.character === right.character; 41 | } 42 | } 43 | 44 | /** {@docCategory Other} */ 45 | export interface Range { 46 | readonly start: Position; 47 | readonly end: Position; 48 | } 49 | 50 | /** {@docCategory Other} */ 51 | export namespace Range { 52 | export function create(start: Position, end: Position): Range { 53 | return { start, end }; 54 | } 55 | 56 | export function clone(range: Range): Range { 57 | return create(Position.clone(range.start), Position.clone(range.end)); 58 | } 59 | 60 | export function collapseToStart(range: Range): Range { 61 | return create(range.start, range.start); 62 | } 63 | 64 | export function collapseToEnd(range: Range): Range { 65 | return create(range.end, range.end); 66 | } 67 | 68 | export function isCollapsed(range: Range): boolean { 69 | return Position.compare(range.start, range.end) >= 0; 70 | } 71 | 72 | export function contains(left: Range, right: Range): boolean { 73 | return Position.compare(left.start, right.start) <= 0 74 | && Position.compare(left.end, right.end) >= 0; 75 | } 76 | 77 | export function containsPosition(range: Range, position: Position): boolean { 78 | return Position.compare(range.start, position) <= 0 79 | && Position.compare(range.end, position) >= 0; 80 | } 81 | 82 | export function intersects(left: Range, right: Range): boolean { 83 | return containsPosition(left, right.start) 84 | || containsPosition(left, right.end); 85 | } 86 | 87 | export function equals(left: Range, right: Range): boolean { 88 | return Position.equals(left.start, right.start) 89 | && Position.equals(left.end, right.end) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "lib": ["es2018"], 5 | "module": "commonjs", 6 | "outDir": "dist", 7 | "rootDir": "src", 8 | "types": ["node", "jest"], 9 | "sourceMap": true, 10 | "experimentalDecorators": true, 11 | "emitDecoratorMetadata": true, 12 | "declaration": true, 13 | "strict": true, 14 | "stripInternal": true, 15 | "noUnusedLocals": true, 16 | "inlineSources": false, 17 | }, 18 | "include": [ 19 | "src/**/*.ts" 20 | ] 21 | } --------------------------------------------------------------------------------