diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..902b281 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +node_modules/ +data/ diff --git a/compose.override.yaml b/compose.override.yaml index 5aca121..b06223a 100644 --- a/compose.override.yaml +++ b/compose.override.yaml @@ -8,6 +8,8 @@ services: - "127.0.0.1:${UI_PORT:-8000}:8000" volumes: - "./:/app" + - "/app/node_modules" + container_name: frontend pocketbase: image: spectado/pocketbase:${POCKETBASE_VERSION_TAG:-latest} @@ -16,3 +18,4 @@ services: restart: "unless-stopped" volumes: - "./data:/pb_data" + container_name: pocketbase diff --git a/package-lock.json b/package-lock.json index 80c5a7b..af9a414 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "adr-manager", "version": "3.0.0", "dependencies": { + "antlr4": "^4.13.1-patch-1", "pinia": "^2.1.7", "vue": "^3.4.21", "vue-router": "^4.3.0" @@ -17,6 +18,8 @@ "@tsconfig/node20": "^20.1.4", "@types/jsdom": "^21.1.6", "@types/node": "^20.12.5", + "@typescript-eslint/eslint-plugin": "^7.13.0", + "@typescript-eslint/parser": "^7.13.0", "@vitejs/plugin-vue": "^5.0.4", "@vue/eslint-config-prettier": "^9.0.0", "@vue/eslint-config-typescript": "^13.0.0", @@ -1124,15 +1127,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.11.0", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.0.tgz", + "integrity": "sha512-FX1X6AF0w8MdVFLSdqwqN/me2hyhuQg4ykN6ZpVhh1ij/80pTvDKclX1sZB9iqex8SjQfVhwMKs3JtnnMLzG9w==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.11.0", - "@typescript-eslint/type-utils": "7.11.0", - "@typescript-eslint/utils": "7.11.0", - "@typescript-eslint/visitor-keys": "7.11.0", + "@typescript-eslint/scope-manager": "7.13.0", + "@typescript-eslint/type-utils": "7.13.0", + "@typescript-eslint/utils": "7.13.0", + "@typescript-eslint/visitor-keys": "7.13.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1156,14 +1160,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.11.0", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.13.0.tgz", + "integrity": "sha512-EjMfl69KOS9awXXe83iRN7oIEXy9yYdqWfqdrFAYAAr6syP8eLEFI7ZE4939antx2mNgPRW/o1ybm2SFYkbTVA==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "7.11.0", - "@typescript-eslint/types": "7.11.0", - "@typescript-eslint/typescript-estree": "7.11.0", - "@typescript-eslint/visitor-keys": "7.11.0", + "@typescript-eslint/scope-manager": "7.13.0", + "@typescript-eslint/types": "7.13.0", + "@typescript-eslint/typescript-estree": "7.13.0", + "@typescript-eslint/visitor-keys": "7.13.0", "debug": "^4.3.4" }, "engines": { @@ -1183,12 +1188,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.11.0", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.0.tgz", + "integrity": "sha512-ZrMCe1R6a01T94ilV13egvcnvVJ1pxShkE0+NDjDzH4nvG1wXpwsVI5bZCvE7AEDH1mXEx5tJSVR68bLgG7Dng==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.11.0", - "@typescript-eslint/visitor-keys": "7.11.0" + "@typescript-eslint/types": "7.13.0", + "@typescript-eslint/visitor-keys": "7.13.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1199,12 +1205,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.11.0", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.13.0.tgz", + "integrity": "sha512-xMEtMzxq9eRkZy48XuxlBFzpVMDurUAfDu5Rz16GouAtXm0TaAoTFzqWUFPPuQYXI/CDaH/Bgx/fk/84t/Bc9A==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "7.11.0", - "@typescript-eslint/utils": "7.11.0", + "@typescript-eslint/typescript-estree": "7.13.0", + "@typescript-eslint/utils": "7.13.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1225,9 +1232,10 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.11.0", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.0.tgz", + "integrity": "sha512-QWuwm9wcGMAuTsxP+qz6LBBd3Uq8I5Nv8xb0mk54jmNoCyDspnMvVsOxI6IsMmway5d1S9Su2+sCKv1st2l6eA==", "dev": true, - "license": "MIT", "engines": { "node": "^18.18.0 || >=20.0.0" }, @@ -1237,12 +1245,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.11.0", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.0.tgz", + "integrity": "sha512-cAvBvUoobaoIcoqox1YatXOnSl3gx92rCZoMRPzMNisDiM12siGilSM4+dJAekuuHTibI2hVC2fYK79iSFvWjw==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "7.11.0", - "@typescript-eslint/visitor-keys": "7.11.0", + "@typescript-eslint/types": "7.13.0", + "@typescript-eslint/visitor-keys": "7.13.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1264,14 +1273,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.11.0", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.0.tgz", + "integrity": "sha512-jceD8RgdKORVnB4Y6BqasfIkFhl4pajB1wVxrF4akxD2QPM8GNYjgGwEzYS+437ewlqqrg7Dw+6dhdpjMpeBFQ==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.11.0", - "@typescript-eslint/types": "7.11.0", - "@typescript-eslint/typescript-estree": "7.11.0" + "@typescript-eslint/scope-manager": "7.13.0", + "@typescript-eslint/types": "7.13.0", + "@typescript-eslint/typescript-estree": "7.13.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1285,11 +1295,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.11.0", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.0.tgz", + "integrity": "sha512-nxn+dozQx+MK61nn/JP+M4eCkHDSxSLDpgE3WcQo0+fkjEolnaB5jswvIKC4K56By8MMgIho7f1PVxERHEo8rw==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.11.0", + "@typescript-eslint/types": "7.13.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -1828,6 +1839,14 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/antlr4": { + "version": "4.13.1-patch-1", + "resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.13.1-patch-1.tgz", + "integrity": "sha512-OjFLWWLzDMV9rdFhpvroCWR4ooktNg9/nvVYSA5z28wuVpU36QUNuioR1XLnQtcjVlf8npjyz593PxnU/f/Cow==", + "engines": { + "node": ">=16" + } + }, "node_modules/arch": { "version": "2.2.0", "dev": true, @@ -1859,8 +1878,9 @@ }, "node_modules/array-union": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -2013,8 +2033,9 @@ }, "node_modules/braces": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -2627,8 +2648,9 @@ }, "node_modules/dir-glob": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, - "license": "MIT", "dependencies": { "path-type": "^4.0.0" }, @@ -2830,8 +2852,9 @@ }, "node_modules/eslint": { "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -3154,8 +3177,9 @@ }, "node_modules/fast-glob": { "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -3169,8 +3193,9 @@ }, "node_modules/fast-glob/node_modules/glob-parent": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -3239,8 +3264,9 @@ }, "node_modules/fill-range": { "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, - "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -3505,8 +3531,9 @@ }, "node_modules/globby": { "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, - "license": "MIT", "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -3852,8 +3879,9 @@ }, "node_modules/is-number": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -4361,16 +4389,18 @@ }, "node_modules/merge2": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/micromatch": { "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "dev": true, - "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -4758,8 +4788,9 @@ }, "node_modules/path-type": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -5421,8 +5452,9 @@ }, "node_modules/slash": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -5752,8 +5784,9 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, diff --git a/package.json b/package.json index 40396d3..692e777 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,12 @@ "test:e2e:dev": "start-server-and-test 'vite dev --port 8000' http://localhost:8000 'cypress open --e2e'", "build-only": "vite build", "type-check": "vue-tsc --build --force", - "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", + "lint": "eslint . --ext .ts", + "lint:fix": "eslint . --ext .ts --fix", "format": "prettier --write src/" }, "dependencies": { + "antlr4": "^4.13.1-patch-1", "pinia": "^2.1.7", "vue": "^3.4.21", "vue-router": "^4.3.0" @@ -25,6 +27,8 @@ "@tsconfig/node20": "^20.1.4", "@types/jsdom": "^21.1.6", "@types/node": "^20.12.5", + "@typescript-eslint/eslint-plugin": "^7.13.0", + "@typescript-eslint/parser": "^7.13.0", "@vitejs/plugin-vue": "^5.0.4", "@vue/eslint-config-prettier": "^9.0.0", "@vue/eslint-config-typescript": "^13.0.0", diff --git a/src/__tests__/adr.spec.ts b/src/__tests__/adr.spec.ts new file mode 100644 index 0000000..71add3b --- /dev/null +++ b/src/__tests__/adr.spec.ts @@ -0,0 +1,15 @@ +import { describe, it, expect } from 'vitest' +import { ArchitecturalDecisionRecord } from '../infrastructure/domain/model/ArchitecturalDecisionRecord' + +describe('Test ADR Model', () => { + it('renders properly', () => { + const adr = new ArchitecturalDecisionRecord( + ['Option One'], + 'Context and problem statement', + '20-2-2020', + 'Deciders' + ) + expect(adr.consideredOptions[0]).toBe('Option One') + expect(adr.deciders).toBe('Deciders') + }) +}) diff --git a/src/__tests__/constants.ts b/src/__tests__/constants.ts new file mode 100644 index 0000000..d6858ac --- /dev/null +++ b/src/__tests__/constants.ts @@ -0,0 +1,1514 @@ +import { ArchitecturalDecisionRecord } from '../infrastructure/service/parser/classes' + +export const validMarkdownADRs = [ + // madr/master/docs/adr/0000-use-markdown-architectural-decision-records.md + `# Use Markdown Architectural Decision Records + +## Context and Problem Statement + +We want to record architectural decisions made in this project. +Which format and structure should these records follow? + +## Considered Options + +* [MADR](https://adr.github.io/madr/) 2.1.2 – The Markdown Architectural Decision Records +* [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR" +* [Sustainable Architectural Decisions](https://www.infoq.com/articles/sustainable-architectural-design-decisions) – The Y-Statements +* Other templates listed at +* Formless – No conventions for file format and structure + +## Decision Outcome + +Chosen option: "MADR 2.1.2", because + +* Implicit assumptions should be made explicit. + Design documentation is important to enable people understanding the decisions later on. + See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940). +* The MADR format is lean and fits our development style. +* The MADR structure is comprehensible and facilitates usage & maintenance. +* The MADR project is vivid. +* Version 2.1.2 is the latest one available when starting to document ADRs. +`, + + // madr/master/docs/adr/0006-use-names-as-identifier.md + `# Use names as identifier + +## Context and Problem Statement + +An option is listed at "Considered Options" and repeated at "Pros and Cons of the Options". Finally, the chosen option is stated at "Decision Outcome". + +## Decision Drivers + +* Easy to read +* Easy to write +* Avoid copy and paste errors + +## Considered Options + +* Repeat all option names if they occur +* Assign an identifier to an option, e.g., \`[A] Use gradle as build tool\` + +## Decision Outcome + +Chosen option: "Assign an identifier to an option", because 1) there is no markdown standard for identifiers, 2) the document is harder to read if there are multiple options. +`, + + // madr/master/docs/adr/0007-do-not-emphasize-line-headings.md + `# Do not emphasize line headings + +## Context and Problem Statement + +MADR contains lines such as \`Chosen option: "[option 1]"\`. Should "Chosen option" be emphasised? + +## Decision Drivers + +* MADR should be easy to read +* MADR should be easy to write + +## Considered Options + +* Do not emphasize line headings +* Emphysize line headings + +## Decision Outcome + +Chosen option: "Do not emphasize line headings", because 1) these headings always are put at the beginning of a line and followed by a colon. Thus, they are already easy to identified as line heading. 2) Readers not familiar with markdown might be confused by stars in the text. +`, + + // madr/master/docs/adr/0008-add-status-field.md + `# Add status field + +Technical Story: + +## Context and Problem Statement + +ADRs have a status. Should this be tracked? And if it should, how should we track it? + +## Considered Options + +* Use badge +* Use text line +* Use separate heading +* Use table +* Do not add status + +## Decision Outcome + +Chosen option: "Use text line", because [justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force force | ... | comes out best (see below)]. + +## Pros and Cons of the Options + +### Use badge + +#### Examples + +* ![grafik](https://user-images.githubusercontent.com/1366654/36786999-ca368324-1c88-11e8-966d-56f25980fd76.png) +* [![status-superseeded](https://img.shields.io/badge/status-superseeded_by_ADR_0001-orange.svg?style=flat-square)](https://github.com/adr/madr/blob/master/docs/adr/0001-use-CC0-as-license.md) + +#### Pros/cons + +* Good, because plain markdown +* Good, because looks good +* Bad, because hard to read in markdown source +* Bad, because relies on the online service https://shields.io or [local badges have to be generated](https://github.com/badges/shields#using-the-badge-library) +* Bad, because at local usages, many badges have to be generated (superseeded-by-ADR-0006, for each ADR number) +* Bad, because not easy to write + +### Use text line + +Example: \`Status: Accepted\` + +* Good, because plain markdown +* Good, because easy to read +* Good, because easy to write +* Good, because looks OK in both markdown-source (MD) and in rendered versions (HTML, PDF) +* Good, because no dependencies on external tools +* Good, because single line indicates the current state +* Bad, because "Status" line needs to be maintained +* Bad, because uses space at the beginning. When users read MADR, they should directly dive into the context and problem and not into the status + +### Use separate heading + +Example: ![grafik](https://user-images.githubusercontent.com/1366654/36787029-f5ea246c-1c88-11e8-9082-8e9531e4fac7.png) + +* Good, because plain markdown +* Good, because easy to write +* Bad, because it uses much space: At least three lines: heading, status, separating empty line + +### Use table + +Example: ![grafik](https://user-images.githubusercontent.com/1366654/36787043-0339a53e-1c89-11e8-8ebe-fb2a5752448c.png) + +* Good, because history can be included +* Good, because multiple entries can be made +* Good, because already implemented in adr-tools fork +* Bad, because not covered by the [CommonMark specification 0.28 (2017-08-01)](http://spec.commonmark.org/0.28/) +* Bad, because hard to read +* Bad, because outdated entries cannot be easily identified +* Bad, because needs more markdown training + +### Do not add status + +* Good, because MADR is kept lean +* Bad, because users demand state field +* Bad, because not in line with other ADR templates +`, + + // madr/master/docs/adr/0009-support-links-between-adrs-inside-an-adrs.md + `# Support links between ADRs inside an ADRs + +Technical Story: https://github.com/adr/madr/issues/9 + +## Considered Options + +* Use tables +* Use heading together with a bullet list directly after status +* Use heading together with a bullet list directly after "Decision Outcome" +* Use heading together with a bullet list at the end +* Don't add links + +## Decision Outcome + +Chosen option: "Use heading together with a bullet list at the end", because comes out best (see below). + +## Pros and Cons of the Options + +### Use tables + +* Good, because easy to write +* Good, because history is shown (enabled by concept) +* Good, because current adr-tools support (https://github.com/npryce/adr-tools/pull/43) uses tables to describe links. +* Bad, because not supported by the CommonMark spec +* Bad, because unclear whether a link was superseeded by another one +* Bad, because valid links not clear at first sight (there might be outdated links shown) + +### Use heading together with a bullet list directly after status + +Example: +![grafik](https://user-images.githubusercontent.com/1366654/36787434-6a63e318-1c8a-11e8-8824-4dd7b3d0f2c6.png) + +* Good, because easy to write +* Good, because supported by the CommonMark spec +* Bad, because not consistent with the status label (refs https://github.com/adr/madr/issues/2) + +### Use heading together with a bullet list directly after "Decision Outcome" + +* Good, because easy to write +* Good, because supported by the CommonMark spec +* Good, because the options are first introduced and then the links +* Good, because consistent with position of "Decision Outcome" +* Bad, because reader might get distracted: He might expect explanation of the options instead of links to something else +* Bad, because not consistent with scientific papers, where related work and future work are coming after the discussion of the content. + +### Use heading together with a bullet list at the end + +* Good, because easy to write +* Good, because supported by the CommonMark spec +* Good, because the options and pros/cons are kept together with the option list. +* Good, because consistent with pattern format + +### Don't add links + +* Good, because template stays minimal +`, + + // madr/docs/adr/0011-use-asterisk-as-list-marker.md + `# Use asterisk as list marker + +## Context and Problem Statement + +Lists in markdown can be indicated by \`*\` (asterisk) or \`-\` (hypen). + +## Considered Options + +* Use an asterisk +* Use a hyphen + +## Decision Outcome + +Chosen option: "Use an asterisk", because an asterisk does not have a meaning of "good" or "bad", whereas a hypen \`-\` could be read as indicator of something negative (in contrast to \`+\`, which could be more be read as "good"). + +According to the [Markdown Style Guide](http://www.cirosantilli.com/markdown-style-guide/), an asterisk as list marker is more readble (see [readability profile](http://www.cirosantilli.com/markdown-style-guide/#readability-profile)). +`, + // An ADR that uses every field + `# Example ADR + +* Status: proposed +* Deciders: Decider +* Date: 2020-12-03 + +Technical Story: Proposed in Issue [#30](https://github.com/koppor/adr-manager/issues/30) + +## Context and Problem Statement + +Context and + +## Decision Drivers + +* ADR-Manager should be lightweight and easy to implement. +* ADR-Manager should be easy to maintain. + +## Considered Options + +* Don't use any global store +* Only use local storage in combination with Events/Props +* Implement a state manager from scratch. +* Use the Vue-State-Manager Vuex + +## Decision Outcome + +Chosen option: "Implement a state manager from scratch", because comes out best. The data can additionally be stored in Local Storage but this should be managed by the global store as well. + +### Positive Consequences + +* New functionality will be easier to add. + +### Negative Consequences + +* asd + +## Pros and Cons of the Options + +### Don't use any global store + +Just "cascade" updates between Vue-Components via Events and Props. +E.g. each editor tab has a prop (v-model) for the displayed ADR. Whenever the ADR is changed the Sup-Component (currently TheEditor.vue) updates the ADR in each tab. +When a new ADR is created via a toolbar menu, the event needs to cascade down to each related Editor-Component. + +* Good, because it's easy to implement. It is currently done that way and requires no further actions. +* Bad, because it's a debugging nightmare. +* Bad, because GUI and functionality is more directly connected. Changes to the GUI often require updating functionality and vice versa. + +### Only use local storage in combination with Events/Props + +Use local storage (i.e. persistent storage) to store the state and use events (e. g. a global event bus) to communicate changes to the state. + +* Good, because it's easy to implement. +* Good, because Most data should be stored in persistent storage anyway. + +### Implement a state manager from scratch. + +Implement a state manager from scratch as described at https://vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch. + +* Good, because GUI and functionality are split better. Debugging is easier. +* Good, because Dialogs can be moved around between components without having to update props and events every time. + +### Use the Vue-State-Manager Vuex + +Docs can be found at https://vuex.vuejs.org/. + +* Good, because best long-term maintainability. +* Good, because prepares for extensions like 'Undo-Redo'. +* Good, because the development team can gather experience with Vuex. +* Bad, because of more concepts and boilerplate. +* Bad, because does not fit in our project. We assume that ADR-Manager is a small-to-medium project and not a medium-to-large project + +## Links + +* [This is a link](example.org) +` +] + +/** + * Pairs describing the expected behaviour of the md2adr parser. When given the markdown it should output the ADR. + */ +export const MD_ParsedMADR_Pairs = [ + // madr/docs/adr/0000-use-markdown-architectural-decision-records.md + { + md: `# Use Markdown Architectural Decision Records + +## Context and Problem Statement + +We want to record architectural decisions made in this project. +Which format and structure should these records follow? + +## Considered Options + +* [MADR](https://adr.github.io/madr/) 2.1.2 – The Markdown Architectural Decision Records +* [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR" +* [Sustainable Architectural Decisions](https://www.infoq.com/articles/sustainable-architectural-design-decisions) – The Y-Statements +* Other templates listed at +* Formless – No conventions for file format and structure + +## Decision Outcome + +Chosen option: "MADR 2.1.2", because + +* Implicit assumptions should be made explicit. + Design documentation is important to enable people understanding the decisions later on. + See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940). +* The MADR format is lean and fits our development style. +* The MADR structure is comprehensible and facilitates usage & maintenance. +* The MADR project is vivid. +* Version 2.1.2 is the latest one available when starting to document ADRs. +`, + adr: new ArchitecturalDecisionRecord({ + title: 'Use Markdown Architectural Decision Records', + contextAndProblemStatement: `We want to record architectural decisions made in this project. +Which format and structure should these records follow?`, + consideredOptions: [ + { + title: + '[MADR](https://adr.github.io/madr/) 2.1.2 – The Markdown Architectural Decision Records', + description: '', + pros: [], + cons: [] + }, + { + title: + '[Michael Nygard\'s template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR"', + description: '', + pros: [], + cons: [] + }, + { + title: + '[Sustainable Architectural Decisions](https://www.infoq.com/articles/sustainable-architectural-design-decisions) – The Y-Statements', + description: '', + pros: [], + cons: [] + }, + { + title: + 'Other templates listed at ', + description: '', + pros: [], + cons: [] + }, + { + title: 'Formless – No conventions for file format and structure', + description: '', + pros: [], + cons: [] + } + ], + decisionOutcome: { + chosenOption: 'MADR 2.1.2', + explanation: `* Implicit assumptions should be made explicit. + Design documentation is important to enable people understanding the decisions later on. + See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940). +* The MADR format is lean and fits our development style. +* The MADR structure is comprehensible and facilitates usage & maintenance. +* The MADR project is vivid. +* Version 2.1.2 is the latest one available when starting to document ADRs.`, + positiveConsequences: [], + negativeConsequences: [] + } + }) + }, + + // madr/docs/adr/0001-use-CC0-as-license.md + { + md: ` +# Use CC0 as license + +Everything needs to be licensed, otherwise the default copyright laws apply. +For instance, in Germany that means users may not alter anything without explicitly asking for permission. +For more information see . + +We want to have MADR used without any hassle and that users can just go ahead and write MADRs. + +## Considered Options + +* [CC0](https://creativecommons.org/share-your-work/public-domain/cc0/) +* No license +* Other open source licenses + +## Decision Outcome + +Chosen option: "CC0", because this license donates the content to "public domain" and does so as legally as possible. +`, + adr: new ArchitecturalDecisionRecord({ + title: 'Use CC0 as license', + contextAndProblemStatement: `Everything needs to be licensed, otherwise the default copyright laws apply. +For instance, in Germany that means users may not alter anything without explicitly asking for permission. +For more information see . + +We want to have MADR used without any hassle and that users can just go ahead and write MADRs.`, + consideredOptions: [ + { + title: '[CC0](https://creativecommons.org/share-your-work/public-domain/cc0/)', + description: '', + pros: [], + cons: [] + }, + { + title: 'No license', + description: '', + pros: [], + cons: [] + }, + { + title: 'Other open source licenses', + description: '', + pros: [], + cons: [] + } + ], + decisionOutcome: { + chosenOption: 'CC0', + explanation: `this license donates the content to "public domain" and does so as legally as possible.`, + positiveConsequences: [], + negativeConsequences: [] + } + }) + }, + + // madr/docs/adr/0002-do-not-use-numbers-in-headings.md + // Misses the '"' around the chose option and the heading 'Context and Problem Statement' + { + md: `# Do not use numbers in headings + +How to render the first line in an ADR? +ADRs have to take a unique identifier. + +## Considered Options + +* Use the title only +* Add the ADR number in front of the title (e.g., "# 2. Do not use numbers in headings") + +## Decision Outcome + +Chosen option: Use the title only, because + +* This is common in other markdown files, too. + One does not add numbering manually at the markdown files, but tries to get the numbers injected by the rendering framework or CSS. +* Enables renaming of ADRs (before publication) easily +* Allows copy'n'paste of ADRs from other repositories without having to worry about the numbers. +`, + adr: new ArchitecturalDecisionRecord({ + title: 'Do not use numbers in headings', + contextAndProblemStatement: `How to render the first line in an ADR? +ADRs have to take a unique identifier.`, + consideredOptions: [ + { + title: 'Use the title only', + description: '', + pros: [], + cons: [] + }, + { + title: + 'Add the ADR number in front of the title (e.g., "# 2. Do not use numbers in headings")', + description: '', + pros: [], + cons: [] + } + ], + decisionOutcome: { + chosenOption: 'Use the title only', + explanation: `* This is common in other markdown files, too. + One does not add numbering manually at the markdown files, but tries to get the numbers injected by the rendering framework or CSS. +* Enables renaming of ADRs (before publication) easily +* Allows copy'n'paste of ADRs from other repositories without having to worry about the numbers.`, + positiveConsequences: [], + negativeConsequences: [] + } + }) + }, + + // madr/master/docs/adr/0005-use-dashes-in-filenames.md + { + md: `# Use dashes in filenames + +What is the pattern of the filename where an ADR is stored? + +## Considered Options + +* \`NNNN-title-with-dashes.md\` - format used by [adr-tools](https://github.com/npryce/adr-tools) +* \`YYYY-MM-DD Title\` - see https://github.com/joelparkerhenderson/architecture_decision_record#adr-file-name-conventions + +## Decision Outcome + +Chosen option: \`NNNN-title-with-dashes.md\`, because + +* \`NNNN\` provides a unique number, which can be used for referencing in the forms + * \`ADR-0001\` in plain text and + * by \`@ADR(1)\` Java code (enabled by [e-adr](https://adr.github.io/e-adr/)) +* The creation time of an ADR is of historical interest only, if it gets updated somehow. + The arguments are similar than the ones by [Does Git have keyword expansion?](https://git.wiki.kernel.org/index.php/GitFaq#Does_Git_have_keyword_expansion.3F) +* Having no spaces in filenames eases working in the command line +* This is exactly the format offered by [adr-tools](https://github.com/npryce/adr-tools) +`, + adr: new ArchitecturalDecisionRecord({ + title: 'Use dashes in filenames', + contextAndProblemStatement: `What is the pattern of the filename where an ADR is stored?`, + consideredOptions: [ + { + title: + '`NNNN-title-with-dashes.md` - format used by [adr-tools](https://github.com/npryce/adr-tools)', + description: '', + pros: [], + cons: [] + }, + { + title: + '`YYYY-MM-DD Title` - see https://github.com/joelparkerhenderson/architecture_decision_record#adr-file-name-conventions', + description: '', + pros: [], + cons: [] + } + ], + decisionOutcome: { + chosenOption: 'NNNN-title-with-dashes.md', + explanation: `* \`NNNN\` provides a unique number, which can be used for referencing in the forms + * \`ADR-0001\` in plain text and + * by \`@ADR(1)\` Java code (enabled by [e-adr](https://adr.github.io/e-adr/)) +* The creation time of an ADR is of historical interest only, if it gets updated somehow. + The arguments are similar than the ones by [Does Git have keyword expansion?](https://git.wiki.kernel.org/index.php/GitFaq#Does_Git_have_keyword_expansion.3F) +* Having no spaces in filenames eases working in the command line +* This is exactly the format offered by [adr-tools](https://github.com/npryce/adr-tools)`, + positiveConsequences: [], + negativeConsequences: [] + } + }) + }, + + // madr/master/docs/adr/0010-support-categories.md + { + md: `# Support categories + +## Context and Problem Statement + +ADRs are recorded. The number of ADRs grows and the context/topic/scope of ADRs might be different (e.g., frontend, backend) + +## Decision Drivers + +* Easy to find groups ADRs in hundreds of ADRs +* Easy to group +* Easy to create +* Good finding without external tooling +* Keep newcomers in mind (should be doable in <10 minutes) +* Keep template lean + +## Considered Options + +* Use labels +* Add \`* Category: CATEGORY\` directly under the heading (similar to https://gist.github.com/FaKeller/2f9c63b6e1d436abb7358b68bf396f57) +* Use YAML frontmatter +* Encode category in filename +* Use subfolders with local ids +* Use subfolders with global ids +* Don't do it. + +## Decision Outcome + +Chosen option: "Use subfolders with local ids" + +## Pros and Cons of the Options + +### Use labels + +Example: + +Use Angular ![category-frontend](https://img.shields.io/badge/category-frontend-blue.svg?style=flat-square) + +\`![category-frontend](https://img.shields.io/badge/category-frontend-blue.svg?style=flat-square)\` + +* Good, because full markdown +* Good, because linking to an overview page is possible (using markdown) +* Bad, because not straight-forward to parse +* Bad, because no simple filtering using \`ls\` or Windows Explorer is possible + +### Add \`* Category: CATEGORY\` directly under the heading + +* Good, because full markdown +* Good, because linking to an overview page is possible (using markdown) +* Good, because straight-forward to parse +* Bad, because no simple filtering using \`ls\` or Windows Explorer is possible + +### Use YAML frontmatter + +Example: + +\`\`\`yaml +--- +category: frontend +--- +\`\`\` + +* Good, because nearly straight-forward to parse +* Good, because Jekyll supports it +* Bad, because YAML frontmatter is not part of the [CommonMarc Spec](http://spec.commonmark.org/) +* Bad, because no simple filtering using \`ls\` or Windows Explorer is possible + +### Encode category in filename + +Example: \`0050--frontend--title-with-dashes.md\` + +* Good, because programmatic filtering is possible +* Good, because \`ls -la | grep --category--\` works +* Bad, because plain file list in Windows explorer cannot be filtered +* Bad, because as bad as [TagSpaces](https://www.tagspaces.org/), which stores the tags in the filenames in brackets. E.g., \`demo[demotag secondtag].md\`. + +### Use subfolders with local ids + +Optionally "to-be-categorized" folder. + +One level of subfolder, not nested + +#### Examples + +* \`docs/adr/smar/0000-secure-entities.md\` +* \`docs/adr/smar/0001-flexible-properties-selection.md\` + +#### Pros/cons + +* Good, because grouping is done by folders (which are natural for grouping) +* Good, because typos can easily be spotted +* Bad, because there is no unique number identifying an ADR +* Bad, because two indices have to be maintained (adr-log needs to be updated) +* Bad, because e-adr needs to be adapted to \`@ADR("category", number)\` (not that bad) +* Bad, because when category is unknown it is hard to find the right folder +* Bad, because using categories might be hampering newcomers + +### Use subfolders with global ids + +#### Examples + +* \`docs/adr/smar/0005-secure-entities.md\` +* \`docs/adr/smar/0047-flexible-properties-selection.md\` +`, + + adr: new ArchitecturalDecisionRecord({ + title: 'Support categories', + contextAndProblemStatement: `ADRs are recorded. The number of ADRs grows and the context/topic/scope of ADRs might be different (e.g., frontend, backend)`, + decisionDrivers: [ + 'Easy to find groups ADRs in hundreds of ADRs', + 'Easy to group', + 'Easy to create', + 'Good finding without external tooling', + 'Keep newcomers in mind (should be doable in <10 minutes)', + 'Keep template lean' + ], + consideredOptions: [ + { + title: 'Use labels', + description: `Example: + +Use Angular ![category-frontend](https://img.shields.io/badge/category-frontend-blue.svg?style=flat-square) + +\`![category-frontend](https://img.shields.io/badge/category-frontend-blue.svg?style=flat-square)\``, + pros: ['full markdown', 'linking to an overview page is possible (using markdown)'], + cons: [ + 'not straight-forward to parse', + 'no simple filtering using `ls` or Windows Explorer is possible' + ] + }, + { + title: + 'Add `* Category: CATEGORY` directly under the heading (similar to https://gist.github.com/FaKeller/2f9c63b6e1d436abb7358b68bf396f57)', + description: '', + pros: [ + 'full markdown', + 'linking to an overview page is possible (using markdown)', + 'straight-forward to parse' + ], + cons: ['no simple filtering using `ls` or Windows Explorer is possible'] + }, + { + title: 'Use YAML frontmatter', + description: `Example: + +\`\`\`yaml +--- +category: frontend +--- +\`\`\``, + pros: ['nearly straight-forward to parse', 'Jekyll supports it'], + cons: [ + 'YAML frontmatter is not part of the [CommonMarc Spec](http://spec.commonmark.org/)', + 'no simple filtering using `ls` or Windows Explorer is possible' + ] + }, + { + title: 'Encode category in filename', + description: 'Example: `0050--frontend--title-with-dashes.md`', + pros: ['programmatic filtering is possible', '`ls -la | grep --category--` works'], + cons: [ + 'plain file list in Windows explorer cannot be filtered', + 'as bad as [TagSpaces](https://www.tagspaces.org/), which stores the tags in the filenames in brackets. E.g., `demo[demotag secondtag].md`.' + ] + }, + { + title: 'Use subfolders with local ids', + description: `Optionally "to-be-categorized" folder. + +One level of subfolder, not nested + +#### Examples + +* \`docs/adr/smar/0000-secure-entities.md\` +* \`docs/adr/smar/0001-flexible-properties-selection.md\` + +#### Pros/cons`, + pros: [ + 'grouping is done by folders (which are natural for grouping)', + 'typos can easily be spotted' + ], + cons: [ + 'there is no unique number identifying an ADR', + 'two indices have to be maintained (adr-log needs to be updated)', + 'e-adr needs to be adapted to `@ADR("category", number)` (not that bad)', + 'when category is unknown it is hard to find the right folder', + 'using categories might be hampering newcomers' + ] + }, + { + title: 'Use subfolders with global ids', + description: `#### Examples + +* \`docs/adr/smar/0005-secure-entities.md\` +* \`docs/adr/smar/0047-flexible-properties-selection.md\``, + pros: [], + cons: [] + }, + { + title: "Don't do it.", + description: '', + pros: [], + cons: [] + } + ], + decisionOutcome: { + chosenOption: 'Use subfolders with local ids', + explanation: ``, + positiveConsequences: [], + negativeConsequences: [] + } + }) + }, + + // Option list misses one option of "Pros and Cons of the Options" (D) + // Option names have only prefix matching (A <-> As, B <-> Bs) + { + md: `# Heading + +## Context and Problem Statement + +Context + +## Considered Options + +* A +* Bs +* C + +## Decision Outcome + +Chosen option: "ABC", because comes out best. + +### Positive Consequences + +* positive consequence + +## Pros and Cons of the Options + +### As + +A description + + +### B + +B description + + +### D + +D description + +`, + adr: new ArchitecturalDecisionRecord({ + title: 'Heading', + contextAndProblemStatement: `Context`, + consideredOptions: [ + { + title: 'A', + description: 'A description', + pros: [], + cons: [] + }, + { + title: 'Bs', + description: 'B description', + pros: [], + cons: [] + }, + { + title: 'C', + description: '', + pros: [], + cons: [] + }, + { + title: 'D', + description: 'D description', + pros: [], + cons: [] + } + ], + decisionOutcome: { + chosenOption: 'ABC', + explanation: `comes out best.`, + positiveConsequences: ['positive consequence'], + negativeConsequences: [] + } + }) + } +] + +/** + * These strings are used to test if the parser does always accept its own output after parsing it once. + * The actual result does not matter, just the immediate "convergence" of the result. + */ +export const randomStrings = [ + // Really just some random nonsense + '', + '# ABC', + `# This is a title + +Hey my name is Peter Parker. + +# Pros and Cons of Options +`, + `## This one has no title. +`, + `# Title +1. Ein Punkt in einer geordneten Liste +2. Ein weiterer Punkt; bei der Eingabe muss nicht auf irgendeine Reihenfolge geachtet werden, sondern nur darauf, dass es beliebige Ziffern sind +1. Noch ein Punkt, der zeigt, dass auch die mehrfache Angabe derselben Ziffer möglich ist + + + +`, + // An actual ADR + `# Heading + +* Status: proposed +* Deciders: Peter Parker +* Date: 2021-01-05 + +Technical Story: Long story + +## Context and Problem Statement + +context + +## Decision Drivers + +* driver + +## Considered Options + +* Test Option + +## Decision Outcome + +Chosen option: "undefined", because comes out best (see below). + +### Positive Consequences + +* abc + +### Negative Consequences + +* :) + +## Pros and Cons of the Options + +### Test Option + +Test argument + +* Good, because yay +* Bad, because nay + +## Links + +* ~~ +` +] + +/** + * Data for mocking the GitHub API + */ + +/** First two result pages of 'https://api.github.com/repositories' on 06-01-2020 */ + +export const searchTermRepoPairs = [ + { + searchTerm: 'ap', + results: [ + { + id: 647818505, + node_id: 'R_kgDOJpztCQ', + name: 'foodapp', + full_name: 'tasbihaasim/foodapp', + private: true, + owner: { + login: 'tasbihaasim', + id: 54742672, + node_id: 'MDQ6VXNlcjU0NzQyNjcy', + avatar_url: 'https://avatars.githubusercontent.com/u/54742672?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/tasbihaasim', + html_url: 'https://github.com/tasbihaasim', + followers_url: 'https://api.github.com/users/tasbihaasim/followers', + following_url: 'https://api.github.com/users/tasbihaasim/following{/other_user}', + gists_url: 'https://api.github.com/users/tasbihaasim/gists{/gist_id}', + starred_url: 'https://api.github.com/users/tasbihaasim/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/tasbihaasim/subscriptions', + organizations_url: 'https://api.github.com/users/tasbihaasim/orgs', + repos_url: 'https://api.github.com/users/tasbihaasim/repos', + events_url: 'https://api.github.com/users/tasbihaasim/events{/privacy}', + received_events_url: 'https://api.github.com/users/tasbihaasim/received_events', + type: 'User', + site_admin: false + }, + html_url: 'https://github.com/tasbihaasim/foodapp', + description: null, + fork: false, + url: 'https://api.github.com/repos/tasbihaasim/foodapp', + forks_url: 'https://api.github.com/repos/tasbihaasim/foodapp/forks', + keys_url: 'https://api.github.com/repos/tasbihaasim/foodapp/keys{/key_id}', + collaborators_url: + 'https://api.github.com/repos/tasbihaasim/foodapp/collaborators{/collaborator}', + teams_url: 'https://api.github.com/repos/tasbihaasim/foodapp/teams', + hooks_url: 'https://api.github.com/repos/tasbihaasim/foodapp/hooks', + issue_events_url: 'https://api.github.com/repos/tasbihaasim/foodapp/issues/events{/number}', + events_url: 'https://api.github.com/repos/tasbihaasim/foodapp/events', + assignees_url: 'https://api.github.com/repos/tasbihaasim/foodapp/assignees{/user}', + branches_url: 'https://api.github.com/repos/tasbihaasim/foodapp/branches{/branch}', + tags_url: 'https://api.github.com/repos/tasbihaasim/foodapp/tags', + blobs_url: 'https://api.github.com/repos/tasbihaasim/foodapp/git/blobs{/sha}', + git_tags_url: 'https://api.github.com/repos/tasbihaasim/foodapp/git/tags{/sha}', + git_refs_url: 'https://api.github.com/repos/tasbihaasim/foodapp/git/refs{/sha}', + trees_url: 'https://api.github.com/repos/tasbihaasim/foodapp/git/trees{/sha}', + statuses_url: 'https://api.github.com/repos/tasbihaasim/foodapp/statuses/{sha}', + languages_url: 'https://api.github.com/repos/tasbihaasim/foodapp/languages', + stargazers_url: 'https://api.github.com/repos/tasbihaasim/foodapp/stargazers', + contributors_url: 'https://api.github.com/repos/tasbihaasim/foodapp/contributors', + subscribers_url: 'https://api.github.com/repos/tasbihaasim/foodapp/subscribers', + subscription_url: 'https://api.github.com/repos/tasbihaasim/foodapp/subscription', + commits_url: 'https://api.github.com/repos/tasbihaasim/foodapp/commits{/sha}', + git_commits_url: 'https://api.github.com/repos/tasbihaasim/foodapp/git/commits{/sha}', + comments_url: 'https://api.github.com/repos/tasbihaasim/foodapp/comments{/number}', + issue_comment_url: + 'https://api.github.com/repos/tasbihaasim/foodapp/issues/comments{/number}', + contents_url: 'https://api.github.com/repos/tasbihaasim/foodapp/contents/{+path}', + compare_url: 'https://api.github.com/repos/tasbihaasim/foodapp/compare/{base}...{head}', + merges_url: 'https://api.github.com/repos/tasbihaasim/foodapp/merges', + archive_url: 'https://api.github.com/repos/tasbihaasim/foodapp/{archive_format}{/ref}', + downloads_url: 'https://api.github.com/repos/tasbihaasim/foodapp/downloads', + issues_url: 'https://api.github.com/repos/tasbihaasim/foodapp/issues{/number}', + pulls_url: 'https://api.github.com/repos/tasbihaasim/foodapp/pulls{/number}', + milestones_url: 'https://api.github.com/repos/tasbihaasim/foodapp/milestones{/number}', + notifications_url: + 'https://api.github.com/repos/tasbihaasim/foodapp/notifications{?since,all,participating}', + labels_url: 'https://api.github.com/repos/tasbihaasim/foodapp/labels{/name}', + releases_url: 'https://api.github.com/repos/tasbihaasim/foodapp/releases{/id}', + deployments_url: 'https://api.github.com/repos/tasbihaasim/foodapp/deployments', + created_at: '2023-05-31T15:26:03Z', + updated_at: '2023-05-31T15:26:12Z', + pushed_at: '2023-05-31T16:31:10Z', + git_url: 'git://github.com/tasbihaasim/foodapp.git', + ssh_url: 'git@github.com:tasbihaasim/foodapp.git', + clone_url: 'https://github.com/tasbihaasim/foodapp.git', + svn_url: 'https://github.com/tasbihaasim/foodapp', + homepage: null, + size: 201, + stargazers_count: 0, + watchers_count: 0, + language: 'JavaScript', + has_issues: true, + has_projects: true, + has_downloads: true, + has_wiki: false, + has_pages: false, + has_discussions: false, + forks_count: 0, + mirror_url: null, + archived: false, + disabled: false, + open_issues_count: 0, + license: null, + allow_forking: true, + is_template: false, + web_commit_signoff_required: false, + topics: [], + visibility: 'private', + forks: 0, + open_issues: 0, + watchers: 0, + default_branch: 'master', + permissions: { + admin: false, + maintain: false, + push: true, + triage: true, + pull: true + } + }, + { + id: 359202375, + node_id: 'MDEwOlJlcG9zaXRvcnkzNTkyMDIzNzU=', + name: 'ChatApplication', + full_name: 'Moneexa/Chatapplication', + private: false, + owner: { + login: 'Moneexa', + id: 24852044, + node_id: 'MDQ6VXNlcjI0ODUyMDQ0', + avatar_url: 'https://avatars.githubusercontent.com/u/24852044?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/Moneexa', + html_url: 'https://github.com/Moneexa', + followers_url: 'https://api.github.com/users/Moneexa/followers', + following_url: 'https://api.github.com/users/Moneexa/following{/other_user}', + gists_url: 'https://api.github.com/users/Moneexa/gists{/gist_id}', + starred_url: 'https://api.github.com/users/Moneexa/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/Moneexa/subscriptions', + organizations_url: 'https://api.github.com/users/Moneexa/orgs', + repos_url: 'https://api.github.com/users/Moneexa/repos', + events_url: 'https://api.github.com/users/Moneexa/events{/privacy}', + received_events_url: 'https://api.github.com/users/Moneexa/received_events', + type: 'User', + site_admin: false + }, + html_url: 'https://github.com/Moneexa/ChatApplication', + description: null, + fork: false, + url: 'https://api.github.com/repos/Moneexa/ChatApplication', + forks_url: 'https://api.github.com/repos/Moneexa/ChatApplication/forks', + keys_url: 'https://api.github.com/repos/Moneexa/ChatApplication/keys{/key_id}', + collaborators_url: + 'https://api.github.com/repos/Moneexa/ChatApplication/collaborators{/collaborator}', + teams_url: 'https://api.github.com/repos/Moneexa/ChatApplication/teams', + hooks_url: 'https://api.github.com/repos/Moneexa/ChatApplication/hooks', + issue_events_url: + 'https://api.github.com/repos/Moneexa/ChatApplication/issues/events{/number}', + events_url: 'https://api.github.com/repos/Moneexa/ChatApplication/events', + assignees_url: 'https://api.github.com/repos/Moneexa/ChatApplication/assignees{/user}', + branches_url: 'https://api.github.com/repos/Moneexa/ChatApplication/branches{/branch}', + tags_url: 'https://api.github.com/repos/Moneexa/ChatApplication/tags', + blobs_url: 'https://api.github.com/repos/Moneexa/ChatApplication/git/blobs{/sha}', + git_tags_url: 'https://api.github.com/repos/Moneexa/ChatApplication/git/tags{/sha}', + git_refs_url: 'https://api.github.com/repos/Moneexa/ChatApplication/git/refs{/sha}', + trees_url: 'https://api.github.com/repos/Moneexa/ChatApplication/git/trees{/sha}', + statuses_url: 'https://api.github.com/repos/Moneexa/ChatApplication/statuses/{sha}', + languages_url: 'https://api.github.com/repos/Moneexa/ChatApplication/languages', + stargazers_url: 'https://api.github.com/repos/Moneexa/ChatApplication/stargazers', + contributors_url: 'https://api.github.com/repos/Moneexa/ChatApplication/contributors', + subscribers_url: 'https://api.github.com/repos/Moneexa/ChatApplication/subscribers', + subscription_url: 'https://api.github.com/repos/Moneexa/ChatApplication/subscription', + commits_url: 'https://api.github.com/repos/Moneexa/ChatApplication/commits{/sha}', + git_commits_url: 'https://api.github.com/repos/Moneexa/ChatApplication/git/commits{/sha}', + comments_url: 'https://api.github.com/repos/Moneexa/ChatApplication/comments{/number}', + issue_comment_url: + 'https://api.github.com/repos/Moneexa/ChatApplication/issues/comments{/number}', + contents_url: 'https://api.github.com/repos/Moneexa/ChatApplication/contents/{+path}', + compare_url: 'https://api.github.com/repos/Moneexa/ChatApplication/compare/{base}...{head}', + merges_url: 'https://api.github.com/repos/Moneexa/ChatApplication/merges', + archive_url: 'https://api.github.com/repos/Moneexa/ChatApplication/{archive_format}{/ref}', + downloads_url: 'https://api.github.com/repos/Moneexa/ChatApplication/downloads', + issues_url: 'https://api.github.com/repos/Moneexa/ChatApplication/issues{/number}', + pulls_url: 'https://api.github.com/repos/Moneexa/ChatApplication/pulls{/number}', + milestones_url: 'https://api.github.com/repos/Moneexa/ChatApplication/milestones{/number}', + notifications_url: + 'https://api.github.com/repos/Moneexa/ChatApplication/notifications{?since,all,participating}', + labels_url: 'https://api.github.com/repos/Moneexa/ChatApplication/labels{/name}', + releases_url: 'https://api.github.com/repos/Moneexa/ChatApplication/releases{/id}', + deployments_url: 'https://api.github.com/repos/Moneexa/ChatApplication/deployments', + created_at: '2021-04-18T16:52:46Z', + updated_at: '2021-05-17T11:25:46Z', + pushed_at: '2021-05-17T11:25:43Z', + git_url: 'git://github.com/Moneexa/ChatApplication.git', + ssh_url: 'git@github.com:Moneexa/ChatApplication.git', + clone_url: 'https://github.com/Moneexa/ChatApplication.git', + svn_url: 'https://github.com/Moneexa/ChatApplication', + homepage: null, + size: 552, + stargazers_count: 0, + watchers_count: 0, + language: 'TypeScript', + has_issues: true, + has_projects: true, + has_downloads: true, + has_wiki: true, + has_pages: false, + has_discussions: false, + forks_count: 0, + mirror_url: null, + archived: false, + disabled: false, + open_issues_count: 0, + license: null, + allow_forking: true, + is_template: false, + web_commit_signoff_required: false, + topics: [], + visibility: 'public', + forks: 0, + open_issues: 0, + watchers: 0, + default_branch: 'master', + permissions: { + admin: true, + maintain: true, + push: true, + triage: true, + pull: true + } + } + ] + } +] + +export const mockedValues = { + data: [ + { + id: 304013826, + node_id: 'MDEwOlJlcG9zaXRvcnkzMDQwMTM4MjY=', + name: 'adr-manager', + full_name: 'adr/adr-manager', + private: false, + owner: { + login: 'adr', + id: 30127167, + node_id: 'MDEyOk9yZ2FuaXphdGlvbjMwMTI3MTY3', + avatar_url: 'https://avatars.githubusercontent.com/u/30127167?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/adr', + html_url: 'https://github.com/adr', + followers_url: 'https://api.github.com/users/adr/followers', + following_url: 'https://api.github.com/users/adr/following{/other_user}', + gists_url: 'https://api.github.com/users/adr/gists{/gist_id}', + starred_url: 'https://api.github.com/users/adr/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/adr/subscriptions', + organizations_url: 'https://api.github.com/users/adr/orgs', + repos_url: 'https://api.github.com/users/adr/repos', + events_url: 'https://api.github.com/users/adr/events{/privacy}', + received_events_url: 'https://api.github.com/users/adr/received_events', + type: 'Organization', + site_admin: false + }, + html_url: 'https://github.com/adr/adr-manager', + description: 'ADR-Manager', + fork: false, + url: 'https://api.github.com/repos/adr/adr-manager', + forks_url: 'https://api.github.com/repos/adr/adr-manager/forks', + keys_url: 'https://api.github.com/repos/adr/adr-manager/keys{/key_id}', + collaborators_url: + 'https://api.github.com/repos/adr/adr-manager/collaborators{/collaborator}', + teams_url: 'https://api.github.com/repos/adr/adr-manager/teams', + hooks_url: 'https://api.github.com/repos/adr/adr-manager/hooks', + issue_events_url: 'https://api.github.com/repos/adr/adr-manager/issues/events{/number}', + events_url: 'https://api.github.com/repos/adr/adr-manager/events', + assignees_url: 'https://api.github.com/repos/adr/adr-manager/assignees{/user}', + branches_url: 'https://api.github.com/repos/adr/adr-manager/branches{/branch}', + tags_url: 'https://api.github.com/repos/adr/adr-manager/tags', + blobs_url: 'https://api.github.com/repos/adr/adr-manager/git/blobs{/sha}', + git_tags_url: 'https://api.github.com/repos/adr/adr-manager/git/tags{/sha}', + git_refs_url: 'https://api.github.com/repos/adr/adr-manager/git/refs{/sha}', + trees_url: 'https://api.github.com/repos/adr/adr-manager/git/trees{/sha}', + statuses_url: 'https://api.github.com/repos/adr/adr-manager/statuses/{sha}', + languages_url: 'https://api.github.com/repos/adr/adr-manager/languages', + stargazers_url: 'https://api.github.com/repos/adr/adr-manager/stargazers', + contributors_url: 'https://api.github.com/repos/adr/adr-manager/contributors', + subscribers_url: 'https://api.github.com/repos/adr/adr-manager/subscribers', + subscription_url: 'https://api.github.com/repos/adr/adr-manager/subscription', + commits_url: 'https://api.github.com/repos/adr/adr-manager/commits{/sha}', + git_commits_url: 'https://api.github.com/repos/adr/adr-manager/git/commits{/sha}', + comments_url: 'https://api.github.com/repos/adr/adr-manager/comments{/number}', + issue_comment_url: 'https://api.github.com/repos/adr/adr-manager/issues/comments{/number}', + contents_url: 'https://api.github.com/repos/adr/adr-manager/contents/{+path}', + compare_url: 'https://api.github.com/repos/adr/adr-manager/compare/{base}...{head}', + merges_url: 'https://api.github.com/repos/adr/adr-manager/merges', + archive_url: 'https://api.github.com/repos/adr/adr-manager/{archive_format}{/ref}', + downloads_url: 'https://api.github.com/repos/adr/adr-manager/downloads', + issues_url: 'https://api.github.com/repos/adr/adr-manager/issues{/number}', + pulls_url: 'https://api.github.com/repos/adr/adr-manager/pulls{/number}', + milestones_url: 'https://api.github.com/repos/adr/adr-manager/milestones{/number}', + notifications_url: + 'https://api.github.com/repos/adr/adr-manager/notifications{?since,all,participating}', + labels_url: 'https://api.github.com/repos/adr/adr-manager/labels{/name}', + releases_url: 'https://api.github.com/repos/adr/adr-manager/releases{/id}', + deployments_url: 'https://api.github.com/repos/adr/adr-manager/deployments', + created_at: '2020-10-14T12:51:16Z', + updated_at: '2023-12-03T18:23:48Z', + pushed_at: '2023-12-02T21:11:01Z', + git_url: 'git://github.com/adr/adr-manager.git', + ssh_url: 'git@github.com:adr/adr-manager.git', + clone_url: 'https://github.com/adr/adr-manager.git', + svn_url: 'https://github.com/adr/adr-manager', + homepage: 'https://adr.github.io/adr-manager/', + size: 6721, + stargazers_count: 87, + watchers_count: 87, + language: 'JavaScript', + has_issues: true, + has_projects: true, + has_downloads: true, + has_wiki: false, + has_pages: true, + has_discussions: false, + forks_count: 14, + mirror_url: null, + archived: false, + disabled: false, + open_issues_count: 54, + license: { + key: 'mit', + name: 'MIT License', + spdx_id: 'MIT', + url: 'https://api.github.com/licenses/mit', + node_id: 'MDc6TGljZW5zZTEz' + }, + allow_forking: true, + is_template: false, + web_commit_signoff_required: false, + topics: [ + 'adr', + 'architectural-decision-records', + 'architecure', + 'github', + 'madr', + 'madr-editor' + ], + visibility: 'public', + forks: 14, + open_issues: 54, + watchers: 87, + default_branch: 'main', + permissions: { + admin: true, + maintain: true, + push: true, + triage: true, + pull: true + } + }, + { + id: 647818505, + node_id: 'R_kgDOJpztCQ', + name: 'foodapp', + full_name: 'tasbihaasim/foodapp', + private: true, + owner: { + login: 'tasbihaasim', + id: 54742672, + node_id: 'MDQ6VXNlcjU0NzQyNjcy', + avatar_url: 'https://avatars.githubusercontent.com/u/54742672?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/tasbihaasim', + html_url: 'https://github.com/tasbihaasim', + followers_url: 'https://api.github.com/users/tasbihaasim/followers', + following_url: 'https://api.github.com/users/tasbihaasim/following{/other_user}', + gists_url: 'https://api.github.com/users/tasbihaasim/gists{/gist_id}', + starred_url: 'https://api.github.com/users/tasbihaasim/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/tasbihaasim/subscriptions', + organizations_url: 'https://api.github.com/users/tasbihaasim/orgs', + repos_url: 'https://api.github.com/users/tasbihaasim/repos', + events_url: 'https://api.github.com/users/tasbihaasim/events{/privacy}', + received_events_url: 'https://api.github.com/users/tasbihaasim/received_events', + type: 'User', + site_admin: false + }, + html_url: 'https://github.com/tasbihaasim/foodapp', + description: null, + fork: false, + url: 'https://api.github.com/repos/tasbihaasim/foodapp', + forks_url: 'https://api.github.com/repos/tasbihaasim/foodapp/forks', + keys_url: 'https://api.github.com/repos/tasbihaasim/foodapp/keys{/key_id}', + collaborators_url: + 'https://api.github.com/repos/tasbihaasim/foodapp/collaborators{/collaborator}', + teams_url: 'https://api.github.com/repos/tasbihaasim/foodapp/teams', + hooks_url: 'https://api.github.com/repos/tasbihaasim/foodapp/hooks', + issue_events_url: 'https://api.github.com/repos/tasbihaasim/foodapp/issues/events{/number}', + events_url: 'https://api.github.com/repos/tasbihaasim/foodapp/events', + assignees_url: 'https://api.github.com/repos/tasbihaasim/foodapp/assignees{/user}', + branches_url: 'https://api.github.com/repos/tasbihaasim/foodapp/branches{/branch}', + tags_url: 'https://api.github.com/repos/tasbihaasim/foodapp/tags', + blobs_url: 'https://api.github.com/repos/tasbihaasim/foodapp/git/blobs{/sha}', + git_tags_url: 'https://api.github.com/repos/tasbihaasim/foodapp/git/tags{/sha}', + git_refs_url: 'https://api.github.com/repos/tasbihaasim/foodapp/git/refs{/sha}', + trees_url: 'https://api.github.com/repos/tasbihaasim/foodapp/git/trees{/sha}', + statuses_url: 'https://api.github.com/repos/tasbihaasim/foodapp/statuses/{sha}', + languages_url: 'https://api.github.com/repos/tasbihaasim/foodapp/languages', + stargazers_url: 'https://api.github.com/repos/tasbihaasim/foodapp/stargazers', + contributors_url: 'https://api.github.com/repos/tasbihaasim/foodapp/contributors', + subscribers_url: 'https://api.github.com/repos/tasbihaasim/foodapp/subscribers', + subscription_url: 'https://api.github.com/repos/tasbihaasim/foodapp/subscription', + commits_url: 'https://api.github.com/repos/tasbihaasim/foodapp/commits{/sha}', + git_commits_url: 'https://api.github.com/repos/tasbihaasim/foodapp/git/commits{/sha}', + comments_url: 'https://api.github.com/repos/tasbihaasim/foodapp/comments{/number}', + issue_comment_url: + 'https://api.github.com/repos/tasbihaasim/foodapp/issues/comments{/number}', + contents_url: 'https://api.github.com/repos/tasbihaasim/foodapp/contents/{+path}', + compare_url: 'https://api.github.com/repos/tasbihaasim/foodapp/compare/{base}...{head}', + merges_url: 'https://api.github.com/repos/tasbihaasim/foodapp/merges', + archive_url: 'https://api.github.com/repos/tasbihaasim/foodapp/{archive_format}{/ref}', + downloads_url: 'https://api.github.com/repos/tasbihaasim/foodapp/downloads', + issues_url: 'https://api.github.com/repos/tasbihaasim/foodapp/issues{/number}', + pulls_url: 'https://api.github.com/repos/tasbihaasim/foodapp/pulls{/number}', + milestones_url: 'https://api.github.com/repos/tasbihaasim/foodapp/milestones{/number}', + notifications_url: + 'https://api.github.com/repos/tasbihaasim/foodapp/notifications{?since,all,participating}', + labels_url: 'https://api.github.com/repos/tasbihaasim/foodapp/labels{/name}', + releases_url: 'https://api.github.com/repos/tasbihaasim/foodapp/releases{/id}', + deployments_url: 'https://api.github.com/repos/tasbihaasim/foodapp/deployments', + created_at: '2023-05-31T15:26:03Z', + updated_at: '2023-05-31T15:26:12Z', + pushed_at: '2023-05-31T16:31:10Z', + git_url: 'git://github.com/tasbihaasim/foodapp.git', + ssh_url: 'git@github.com:tasbihaasim/foodapp.git', + clone_url: 'https://github.com/tasbihaasim/foodapp.git', + svn_url: 'https://github.com/tasbihaasim/foodapp', + homepage: null, + size: 201, + stargazers_count: 0, + watchers_count: 0, + language: 'JavaScript', + has_issues: true, + has_projects: true, + has_downloads: true, + has_wiki: false, + has_pages: false, + has_discussions: false, + forks_count: 0, + mirror_url: null, + archived: false, + disabled: false, + open_issues_count: 0, + license: null, + allow_forking: true, + is_template: false, + web_commit_signoff_required: false, + topics: [], + visibility: 'private', + forks: 0, + open_issues: 0, + watchers: 0, + default_branch: 'master', + permissions: { + admin: false, + maintain: false, + push: true, + triage: true, + pull: true + } + }, + { + id: 359202375, + node_id: 'MDEwOlJlcG9zaXRvcnkzNTkyMDIzNzU=', + name: 'ChatApplication', + full_name: 'Moneexa/Chatapplication', + private: false, + owner: { + login: 'Moneexa', + id: 24852044, + node_id: 'MDQ6VXNlcjI0ODUyMDQ0', + avatar_url: 'https://avatars.githubusercontent.com/u/24852044?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/Moneexa', + html_url: 'https://github.com/Moneexa', + followers_url: 'https://api.github.com/users/Moneexa/followers', + following_url: 'https://api.github.com/users/Moneexa/following{/other_user}', + gists_url: 'https://api.github.com/users/Moneexa/gists{/gist_id}', + starred_url: 'https://api.github.com/users/Moneexa/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/Moneexa/subscriptions', + organizations_url: 'https://api.github.com/users/Moneexa/orgs', + repos_url: 'https://api.github.com/users/Moneexa/repos', + events_url: 'https://api.github.com/users/Moneexa/events{/privacy}', + received_events_url: 'https://api.github.com/users/Moneexa/received_events', + type: 'User', + site_admin: false + }, + html_url: 'https://github.com/Moneexa/ChatApplication', + description: null, + fork: false, + url: 'https://api.github.com/repos/Moneexa/ChatApplication', + forks_url: 'https://api.github.com/repos/Moneexa/ChatApplication/forks', + keys_url: 'https://api.github.com/repos/Moneexa/ChatApplication/keys{/key_id}', + collaborators_url: + 'https://api.github.com/repos/Moneexa/ChatApplication/collaborators{/collaborator}', + teams_url: 'https://api.github.com/repos/Moneexa/ChatApplication/teams', + hooks_url: 'https://api.github.com/repos/Moneexa/ChatApplication/hooks', + issue_events_url: + 'https://api.github.com/repos/Moneexa/ChatApplication/issues/events{/number}', + events_url: 'https://api.github.com/repos/Moneexa/ChatApplication/events', + assignees_url: 'https://api.github.com/repos/Moneexa/ChatApplication/assignees{/user}', + branches_url: 'https://api.github.com/repos/Moneexa/ChatApplication/branches{/branch}', + tags_url: 'https://api.github.com/repos/Moneexa/ChatApplication/tags', + blobs_url: 'https://api.github.com/repos/Moneexa/ChatApplication/git/blobs{/sha}', + git_tags_url: 'https://api.github.com/repos/Moneexa/ChatApplication/git/tags{/sha}', + git_refs_url: 'https://api.github.com/repos/Moneexa/ChatApplication/git/refs{/sha}', + trees_url: 'https://api.github.com/repos/Moneexa/ChatApplication/git/trees{/sha}', + statuses_url: 'https://api.github.com/repos/Moneexa/ChatApplication/statuses/{sha}', + languages_url: 'https://api.github.com/repos/Moneexa/ChatApplication/languages', + stargazers_url: 'https://api.github.com/repos/Moneexa/ChatApplication/stargazers', + contributors_url: 'https://api.github.com/repos/Moneexa/ChatApplication/contributors', + subscribers_url: 'https://api.github.com/repos/Moneexa/ChatApplication/subscribers', + subscription_url: 'https://api.github.com/repos/Moneexa/ChatApplication/subscription', + commits_url: 'https://api.github.com/repos/Moneexa/ChatApplication/commits{/sha}', + git_commits_url: 'https://api.github.com/repos/Moneexa/ChatApplication/git/commits{/sha}', + comments_url: 'https://api.github.com/repos/Moneexa/ChatApplication/comments{/number}', + issue_comment_url: + 'https://api.github.com/repos/Moneexa/ChatApplication/issues/comments{/number}', + contents_url: 'https://api.github.com/repos/Moneexa/ChatApplication/contents/{+path}', + compare_url: 'https://api.github.com/repos/Moneexa/ChatApplication/compare/{base}...{head}', + merges_url: 'https://api.github.com/repos/Moneexa/ChatApplication/merges', + archive_url: 'https://api.github.com/repos/Moneexa/ChatApplication/{archive_format}{/ref}', + downloads_url: 'https://api.github.com/repos/Moneexa/ChatApplication/downloads', + issues_url: 'https://api.github.com/repos/Moneexa/ChatApplication/issues{/number}', + pulls_url: 'https://api.github.com/repos/Moneexa/ChatApplication/pulls{/number}', + milestones_url: 'https://api.github.com/repos/Moneexa/ChatApplication/milestones{/number}', + notifications_url: + 'https://api.github.com/repos/Moneexa/ChatApplication/notifications{?since,all,participating}', + labels_url: 'https://api.github.com/repos/Moneexa/ChatApplication/labels{/name}', + releases_url: 'https://api.github.com/repos/Moneexa/ChatApplication/releases{/id}', + deployments_url: 'https://api.github.com/repos/Moneexa/ChatApplication/deployments', + created_at: '2021-04-18T16:52:46Z', + updated_at: '2021-05-17T11:25:46Z', + pushed_at: '2021-05-17T11:25:43Z', + git_url: 'git://github.com/Moneexa/ChatApplication.git', + ssh_url: 'git@github.com:Moneexa/ChatApplication.git', + clone_url: 'https://github.com/Moneexa/ChatApplication.git', + svn_url: 'https://github.com/Moneexa/ChatApplication', + homepage: null, + size: 552, + stargazers_count: 0, + watchers_count: 0, + language: 'TypeScript', + has_issues: true, + has_projects: true, + has_downloads: true, + has_wiki: true, + has_pages: false, + has_discussions: false, + forks_count: 0, + mirror_url: null, + archived: false, + disabled: false, + open_issues_count: 0, + license: null, + allow_forking: true, + is_template: false, + web_commit_signoff_required: false, + topics: [], + visibility: 'public', + forks: 0, + open_issues: 0, + watchers: 0, + default_branch: 'master', + permissions: { + admin: true, + maintain: true, + push: true, + triage: true, + pull: true + } + } + ] +} diff --git a/src/__tests__/parser.spec.ts b/src/__tests__/parser.spec.ts new file mode 100644 index 0000000..9a0571d --- /dev/null +++ b/src/__tests__/parser.spec.ts @@ -0,0 +1,80 @@ +import { it, expect } from 'vitest' +import { + md2adr, + adr2md +} from '../infrastructure/service/parser' + +const randomStrings = [ + // Really just some random nonsense + '', + '# ABC', + `# This is a title + +Hey my name is Peter Parker. + +# Pros and Cons of Options +`, + `## This one has no title. +`, + `# Title +1. Ein Punkt in einer geordneten Liste +2. Ein weiterer Punkt; bei der Eingabe muss nicht auf irgendeine Reihenfolge geachtet werden, sondern nur darauf, dass es beliebige Ziffern sind +1. Noch ein Punkt, der zeigt, dass auch die mehrfache Angabe derselben Ziffer möglich ist + + + +`, + // An actual ADR + `# Heading + +* Status: proposed +* Deciders: Peter Parker +* Date: 2021-01-05 + +Technical Story: Long story + +## Context and Problem Statement + +context + +## Decision Drivers + +* driver + +## Considered Options + +* Test Option + +## Decision Outcome + +Chosen option: "undefined", because comes out best (see below). + +### Positive Consequences + +* abc + +### Negative Consequences + +* :) + +## Pros and Cons of the Options + +### Test Option + +Test argument + +* Good, because yay +* Bad, because nay + +## Links + +* ~~ +` +] +for (let i = 0; i < randomStrings.length; i++) { + it('Test parser convergence of random strings.', (): void => { + const result1 = adr2md(md2adr(randomStrings[i])) + const result2 = adr2md(md2adr(result1)) + expect(result2).toBe(result1) + }) +} diff --git a/src/infrastructure/domain/model/ArchitecturalDecisionRecord.ts b/src/infrastructure/domain/model/ArchitecturalDecisionRecord.ts new file mode 100644 index 0000000..6238fcc --- /dev/null +++ b/src/infrastructure/domain/model/ArchitecturalDecisionRecord.ts @@ -0,0 +1,118 @@ +import { DecisionOutcome } from '@/infrastructure/domain/model/DecisionOutcome' + +export class ArchitecturalDecisionRecord { + private _consideredOptions: any[] + private _contextAndProblemStatement: string + private _date: string + private _deciders: string + private _decisionDrivers: string[] + private _decisionOutcome: DecisionOutcome + private _links: string[] + private _status: string + private _technicalStory: string + private _title: string + + constructor( + consideredOptions: any[] = [], + contextAndProblemStatement: string = '', + date: string = '', + deciders: string = '', + decisionDrivers: string[] = [], + decisionOutcome: DecisionOutcome = null, + links: string[] = [], + status: string = '', + technicalStory: string = '', + title: string = '' + ) { + this._consideredOptions = consideredOptions + this._contextAndProblemStatement = contextAndProblemStatement + this._date = date + this._deciders = deciders + this._decisionDrivers = decisionDrivers + this._decisionOutcome = decisionOutcome + this._links = links + this._status = status + this._technicalStory = technicalStory + this._title = title + } + + get consideredOptions(): any[] { + return this._consideredOptions + } + + set consideredOptions(value: any[]) { + this._consideredOptions = value + } + + get contextAndProblemStatement(): string { + return this._contextAndProblemStatement + } + + set contextAndProblemStatement(value: string) { + this._contextAndProblemStatement = value + } + + get date(): string { + return this._date + } + + set date(value: string) { + this._date = value + } + + get deciders(): string { + return this._deciders + } + + set deciders(value: string) { + this._deciders = value + } + + get decisionDrivers(): string[] { + return this._decisionDrivers + } + + set decisionDrivers(value: string[]) { + this._decisionDrivers = value + } + + get decisionOutcome(): DecisionOutcome { + return this._decisionOutcome + } + + set decisionOutcome(value: DecisionOutcome) { + this._decisionOutcome = value + } + + get links(): string[] { + return this._links + } + + set links(value: string[]) { + this._links = value + } + + get status(): string { + return this._status + } + + set status(value: string) { + this._status = value + } + + get technicalStory(): string { + return this._technicalStory + } + + set technicalStory(value: string) { + this._technicalStory = value + } + + get title(): string { + return this._title + } + + set title(value: string) { + this._title = value + } +} diff --git a/src/infrastructure/domain/model/ConsideredOption.ts b/src/infrastructure/domain/model/ConsideredOption.ts new file mode 100644 index 0000000..49e33df --- /dev/null +++ b/src/infrastructure/domain/model/ConsideredOption.ts @@ -0,0 +1,47 @@ +export class ConsideredOption { + private _cons: string[] + private _description: string + private _pros: string[] + private _title: string + + + constructor(cons: string[], description: string, pros: string[], title: string) { + this._cons = cons; + this._description = description; + this._pros = pros; + this._title = title; + } + + + get cons(): string[] { + return this._cons; + } + + set cons(value: string[]) { + this._cons = value; + } + + get description(): string { + return this._description; + } + + set description(value: string) { + this._description = value; + } + + get pros(): string[] { + return this._pros; + } + + set pros(value: string[]) { + this._pros = value; + } + + get title(): string { + return this._title; + } + + set title(value: string) { + this._title = value; + } +} diff --git a/src/infrastructure/domain/model/DecisionOutcome.ts b/src/infrastructure/domain/model/DecisionOutcome.ts new file mode 100644 index 0000000..e7a709e --- /dev/null +++ b/src/infrastructure/domain/model/DecisionOutcome.ts @@ -0,0 +1,50 @@ +export class DecisionOutcome { + private _chosenOption: string + private _explanation: string + private _positiveConsequences: string[] + private _negativeConsequences: string[] + + constructor( + chosenOption: string = '', + explanation: string = '', + positiveConsequences: string[] = [], + negativeConsequences: string[] = [] + ) { + this._chosenOption = chosenOption + this._explanation = explanation + this._positiveConsequences = positiveConsequences + this._negativeConsequences = negativeConsequences + } + + get chosenOption(): string { + return this._chosenOption + } + + set chosenOption(value: string) { + this._chosenOption = value + } + + get explanation(): string { + return this._explanation + } + + set explanation(value: string) { + this._explanation = value + } + + get positiveConsequences(): string[] { + return this._positiveConsequences + } + + set positiveConsequences(value: string[]) { + this._positiveConsequences = value + } + + get negativeConsequences(): string[] { + return this._negativeConsequences + } + + set negativeConsequences(value: string[]) { + this._negativeConsequences = value + } +} diff --git a/src/infrastructure/service/parser.ts b/src/infrastructure/service/parser.ts new file mode 100644 index 0000000..5ba9645 --- /dev/null +++ b/src/infrastructure/service/parser.ts @@ -0,0 +1,362 @@ +import { cloneDeep } from 'lodash' + +import antlr4 from 'antlr4' +import MADRLexer from '@/infrastructure/service/parser/MADRLexer' +import MADRParser from '@/infrastructure/service/parser/MADRParser' +import MADRListener from '@/infrastructure/service/parser/MADRListener' + +import { + ArchitecturalDecisionRecord, + createShortTitle +} from '@/infrastructure/service/parser/classes' + +/** + * Creates an ADR from a ParseTree by listening to a ParseTreeWalker. + * + * Use with '''antlr4.tree.ParseTreeWalker.DEFAULT.walk(generator, parseTree);''' + * The parsed ADR is saved in the attribute 'adr'. + * + * Local variables: + * + * - currentOption: The current option, either the considered one or the current one handled at "Pros and Cons of the Options" + */ +class MADRGenerator extends MADRListener { + constructor() { + super() + this.adr = new ArchitecturalDecisionRecord() + } + + enterTitle(ctx) { + this.adr.title = ctx.getText() + } + + enterStatus(ctx) { + this.adr.status = ctx.getText() + } + + enterDeciders(ctx) { + this.adr.deciders = ctx.getText() + } + + enterDate(ctx) { + this.adr.date = ctx.getText() + } + + enterTechnicalStory(ctx) { + this.adr.technicalStory = ctx.getText() + } + + enterContextAndProblemStatement(ctx) { + this.adr.contextAndProblemStatement = ctx.getText() + } + + enterDecisionDrivers(ctx) { + this.addListItemsFromListToList(ctx.children[0], this.adr.decisionDrivers) + } + + enterConsideredOptions(ctx) { + const tmpOptionList = [] + this.addListItemsFromListToList(ctx.children[0], tmpOptionList) + tmpOptionList.forEach((opt) => { + if (opt.trim() !== '') { + this.adr.addOption({ title: opt }) + } + }) + } + + /** + * Handles "Decision outcome" + */ + enterChosenOptionAndExplanation(ctx) { + let rawDecisionOutcome = ctx.getText() + + if (rawDecisionOutcome.startsWith('Chosen option: ')) { + rawDecisionOutcome = rawDecisionOutcome.split(/, because */) + rawDecisionOutcome[0] = rawDecisionOutcome[0].substring('Chosen option: '.length).trim() // Remove 'Chosen option: ' + const delim = rawDecisionOutcome[0].charAt(0) + let chosenOption = '' + + if (delim === rawDecisionOutcome[0].charAt(rawDecisionOutcome[0].length - 1)) { + chosenOption = rawDecisionOutcome[0].substring(1, rawDecisionOutcome[0].length - 1) + } else { + chosenOption = rawDecisionOutcome[0] + } + const explanation = rawDecisionOutcome.slice(1).join() + this.adr.decisionOutcome.chosenOption = chosenOption + this.adr.decisionOutcome.explanation = explanation.trim() + } else { + console.log("Couldn't find chosen option.") + } + } + + enterPositiveConsequences(ctx) { + this.addListItemsFromListToList(ctx.children[0], this.adr.decisionOutcome.positiveConsequences) + } + + enterNegativeConsequences(ctx) { + this.addListItemsFromListToList(ctx.children[0], this.adr.decisionOutcome.negativeConsequences) + } + + /** + * Header after "Pros and Cons of the Options" + */ + enterOptionTitle(ctx) { + const title = ctx.getText() + this.currentOption = this.getMostSimilarOptionTo(title) + if (!this.currentOption) { + // No matching option found? + // Create a new one (otherwise the content of the pro/con list will get missing) + this.currentOption = this.adr.addOption({ title: title }) + } + } + + enterOptionDescription(ctx) { + if (this.currentOption) { + this.currentOption.description = ctx.getText() + } + } + + enterProlist(ctx) { + if (this.currentOption) { + this.addListItemsFromListToList(ctx, this.currentOption.pros) + } + } + + enterConlist(ctx) { + if (this.currentOption) { + this.addListItemsFromListToList(ctx, this.currentOption.cons) + } + } + + enterLinks(ctx) { + this.addListItemsFromListToList(ctx.children[0], this.adr.links) + } + /** + * + * @param {string} optTitle the title in the "Chosen option" part + */ + getMostSimilarOptionTo(optTitle) { + // Find the option with a very similar title. + let opt = this.adr.consideredOptions.find(function (opt) { + return this.matchOptionTitleAlmostExactly(opt.title, optTitle) + }, this) + if (opt) { + // If a fitting option was found, return it. + return opt + } + // Else check if there is another (less) similar title. + opt = this.adr.consideredOptions.find(function (opt) { + return matchOptionTitleMoreRelaxed(opt.title, optTitle) + }, this) + if (opt) { + // If a fitting option was found, return it. + return opt + } + // just set the option to not found + return null + } + + /** + * Option titles are similar, iff they are equal after + * (1) removing all white spaces + * (2) lower-casing them. + * + * @param {string} optTitle1 + * @param {string} optTitle2 + * @returns {boolean} True, iff the option titles are very similar. + */ + matchOptionTitleAlmostExactly(optTitle1, optTitle2) { + const trimmed1 = optTitle1.replace(/ /g, '').toLowerCase() // Remove whitespaces and lower-case heading + const trimmed2 = optTitle2.replace(/ /g, '').toLowerCase() + return trimmed1 === trimmed2 + } + + /** + * + * @param {} parseTreeList - a list node in the parse tree. + * @param {string[]} targetList - a js array, where each list entry is copied into. + */ + addListItemsFromListToList(parseTreeList, targetList) { + for (let i = 0; i < parseTreeList.children.length; i++) { + if ( + parseTreeList.children[i].ruleIndex === MADRParser.ruleNames.indexOf('textLine') && // if it is a text line + parseTreeList.children[i].getText().trim() !== '' + ) { + targetList.push(parseTreeList.children[i].getText()) + } + } + } +} + +/** + * Converts a markdown into a MADR object. + * @param {string} md + * @returns {ArchitecturalDecisionRecord} + */ +export function md2adr(md) { + const chars = new antlr4.InputStream(md) + const lexer = new MADRLexer(chars) + const tokens = new antlr4.CommonTokenStream(lexer) + const parser = new MADRParser(tokens) + parser.buildParseTrees = true + parser.removeErrorListeners() + + const tree = parser.start() // 'start' is the name of the starting rule. + // console.log('Created Parse Tree! ', tree) + const printer = new MADRGenerator() + antlr4.tree.ParseTreeWalker.DEFAULT.walk(printer, tree) + // console.log('Result ADR ', printer.adr) + printer.adr.cleanUp() + return printer.adr +} + +export function adr2md(adrToParse) { + const adr = cloneDeep(adrToParse) + adr.cleanUp() + let md = '# ' + adr.title + '\n' + + if ((adr.status !== '' && adr.status !== 'null') || adr.deciders.length > 0 || adr.date !== '') { + if (adr.status !== '' && adr.status !== 'null') { + md = md.concat('\n* Status: ' + adr.status.trim()) + } + if (adr.deciders.length > 0) { + md = md.concat('\n* Deciders: ' + adr.deciders) + } + if (adr.date !== '') { + md = md.concat('\n* Date: ' + adr.date) + } + md = md.concat('\n') + } + + if (adr.technicalStory !== '') { + md = md.concat('\nTechnical Story: ' + adr.technicalStory + '\n') + } + + if (adr.contextAndProblemStatement !== '') { + md = md.concat('\n## Context and Problem Statement\n\n' + adr.contextAndProblemStatement + '\n') + } + + if (adr.decisionDrivers.length > 0) { + md = md.concat('\n## Decision Drivers\n\n') + for (const i in adr.decisionDrivers) { + md = md.concat('* ' + adr.decisionDrivers[i] + '\n') + } + } + + if (adr.consideredOptions.length > 0) { + md = md.concat('\n## Considered Options\n\n') + md = adr.consideredOptions.reduce((total, opt) => total + '* ' + opt.title + '\n', md) + } + + md = md.concat('\n## Decision Outcome\n\nChosen option: "' + adr.decisionOutcome.chosenOption) + + if (adr.decisionOutcome.explanation.trim() !== '') { + const isList = adr.decisionOutcome.explanation.trim().match(/^[*-+]/) + if (isList) { + md = md.concat('", because\n\n' + adr.decisionOutcome.explanation + '\n') + } else { + md = md.concat('", because ' + adr.decisionOutcome.explanation + '\n') + } + } else { + md = md.concat('"\n') + } + + if (adr.decisionOutcome.positiveConsequences.length > 0) { + md = md.concat('\n### Positive Consequences\n\n') + md = adr.decisionOutcome.positiveConsequences.reduce( + (total, con) => total + '* ' + con + '\n', + md + ) + } + if (adr.decisionOutcome.negativeConsequences.length > 0) { + md = md.concat('\n### Negative Consequences\n\n') + md = adr.decisionOutcome.negativeConsequences.reduce( + (total, con) => total + '* ' + con + '\n', + md + ) + } + + if ( + adr.consideredOptions.some( + (opt) => opt.description !== '' || opt.pros.length > 0 || opt.cons.length > 0 + ) + ) { + md = md.concat('\n## Pros and Cons of the Options\n') + md = adr.consideredOptions.reduce((total, opt) => { + if (opt.description !== '' || opt.pros.length > 0 || opt.cons.length > 0) { + let res = total.concat('\n### ' + createShortTitle(opt.title) + '\n') + if (opt.description !== '') { + res = res.concat('\n' + opt.description + '\n') + } + res = opt.pros.reduce((total, arg) => total.concat('\n* Good, because ' + arg), res) + res = opt.cons.reduce((total, arg) => total.concat('\n* Bad, because ' + arg), res) + if (opt.pros.length > 0 || opt.cons.length > 0) { + // insert final new line + res = res + '\n' + } + return res + } else { + return total + } + }, md) + } + if (adr.links.length > 0) { + md = md.concat('\n## Links\n\n') + md = adr.links.reduce((total, link) => total + '* ' + link + '\n', md) + } + return md +} + +/** + * Converts an string in snake case into an natural-language-like string. + * + * Example: '0001-add-status-field' is converted to '0001 Add Status Field' + * + * @param {string} snake + */ +export function snakeCase2naturalCase(snake) { + return snake.replace(/([-_][a-z])/g, (group) => + group.toUpperCase().replace('-', ' ').replace('_', ' ') + ) +} + +/** + * Converts an string in natural case into an snake case string. + * + * Can be used to generate a file name from the title of an ADR. + * + * Example: 'Add status Field' is converted to 'add-status-field' + * + * @param {string} snake + */ +export function naturalCase2snakeCase(natural) { + return natural.toLowerCase().split(' ').join('-') +} + +/** + * Option titles are similar, iff + * a) they are equal after + * (1) removing all white spaces + * (2) lower-casing them + * or + * b) one of these normalized titles is a prefix of the other title. + * or + * c) the chosen option is a sub title of the given option + * + * @param {string} titleFromOptionList + * @param {string} titleFromChosenOption + * @returns {boolean} True, iff the option titles are similar + */ +export function matchOptionTitleMoreRelaxed(titleFromOptionList, titleFromChosenOption) { + const trimmedTitleFromOptionList = titleFromOptionList.replace(/ /g, '').toLowerCase() // Remove whitespaces and lower-case heading + const trimmedTitleFromChosenOption = titleFromChosenOption.replace(/ /g, '').toLowerCase() + const res = + trimmedTitleFromOptionList === trimmedTitleFromChosenOption || + trimmedTitleFromOptionList.startsWith(trimmedTitleFromChosenOption) || + trimmedTitleFromChosenOption.startsWith(trimmedTitleFromOptionList) || + titleFromChosenOption === createShortTitle(titleFromOptionList) || + // in case we have issues with the short title generation, we at least check for a match of the first letters + // Example: "Include in [adr-tools](https://github.com/npryce/adr-tools), 924 stars as of 2018-06-14", we currently don't strip ", ..." + createShortTitle(titleFromOptionList).startsWith(titleFromChosenOption) + return res +} diff --git a/src/infrastructure/service/parser/MADR.g4 b/src/infrastructure/service/parser/MADR.g4 new file mode 100644 index 0000000..fa200fd --- /dev/null +++ b/src/infrastructure/service/parser/MADR.g4 @@ -0,0 +1,99 @@ +// Define a grammar called Hello +grammar MADR; + +start : + HEADING_PREFIX title NEWLINE wslbs + ( STATUS_MARKER status (WS OPTIONAL_MAKER)? wslbs )? + ( DECIDERS_MARKER deciders (WS OPTIONAL_MAKER)? wslbs )? + ( DATE_MARKER date (WS OPTIONAL_MAKER)? NEWLINE wslbs )? + ( TECHNICAL_STORY_MARKER technicalStory (WS OPTIONAL_MAKER)? wslbs )? + ( CONTEXT_AND_PROBLEM_STATEMENT wslbs )? + ( NEWLINE contextAndProblemStatement wslbs )? // Text without a heading is interpreted as context and problem statement + ( DECISION_DRIVERS_HEADING (WS OPTIONAL_MAKER)? wslbs decisionDrivers wslbs )? + ( CONSIDERED_OPTIONS_HEADING wslbs consideredOptions wslbs )? + ( DECISION_OUTCOME_HEADING wslbs decisionOutcome wslbs )? + ( PROS_AND_CONS_OF_THE_OPTIONS_HEADING (WS OPTIONAL_MAKER)? wslbs prosAndConsOfOptions wslbs )? + ( LINKS_HEADING (WS OPTIONAL_MAKER)? wslbs links wslbs )? + EOF + ; + + +title : textLine ; + +status: textLine ; + +deciders : textLine ; + +date : textLine ; + +technicalStory : textLine ; + +contextAndProblemStatement : multilineText ; + +decisionDrivers : list ; + +consideredOptions : list ; + +decisionOutcome : wslbs chosenOptionAndExplanation + (wslbs POSITIVE_CONSEQUENCES_HEADING (WS OPTIONAL_MAKER)? positiveConsequences)? + (wslbs NEGATIVE_CONSEQUENCES_HEADING (WS OPTIONAL_MAKER)? negativeConsequences)? ; + +prosAndConsOfOptions : (optionSection wslbs)+ ; + +optionSection : + SUBSUBHEADING_PREFIX optionTitle NEWLINE + (wslbs optionDescription)? + (wslbs prolist)? + (wslbs conlist)? + ; + +chosenOptionAndExplanation : multilineText ; +positiveConsequences : list ; +negativeConsequences : list ; + +optionTitle : textLine ; +optionDescription : multilineText ; +prolist : (wslbs LIST_MARKER 'Good, because ' textLine) + ; +conlist : (wslbs LIST_MARKER 'Bad, because ' textLine) + ; + +links : list wslbs ; + +list : (wslbs LIST_MARKER textLine?) + ; + +textLine : (any | WS) +? ; + +multilineText : (any | wslb) +? ; // Any (possibly multi-line) text + +any : ( WORD | CHARACTER | LIST_MARKER | HEADING_PREFIX | SUBSUBSUBHEADING_PREFIX) ; + +wslb : ( WS | NEWLINE ) ; +wslbs : wslb *; + +/// Tokenization / Lexer rules + +WORD : CHARACTER+; +CHARACTER : (~[\n\t\r\f ] ) ; + +WS : [\f\t ] ; // White Space +NEWLINE : [\r]?[\n] ; // Line Breaks + +LIST_MARKER : NEWLINE ('* ' | '- '); +STATUS_MARKER : LIST_MARKER 'Status: '; +DATE_MARKER : LIST_MARKER 'Date: '; +DECIDERS_MARKER : LIST_MARKER 'Deciders: '; +OPTIONAL_MAKER: ''; +TECHNICAL_STORY_MARKER : NEWLINE 'Technical Story: '; + +HEADING_PREFIX : '# ' ; // Start of a Heading +SUBSUBHEADING_PREFIX : NEWLINE '### ' ; // Start of a Sub sub heading (e. g. an option section) +SUBSUBSUBHEADING_PREFIX : '###' '#'+ ' ' ; // Start of a sub sub sub heading (no special meaning, should be accepted in multiline text) + +// Headings +CONTEXT_AND_PROBLEM_STATEMENT : NEWLINE ( '## Context and Problem Statement' | '## Context and problem statement' ); +DECISION_DRIVERS_HEADING : NEWLINE ( '## Decision Drivers' | '## Decision drivers' ) ; +CONSIDERED_OPTIONS_HEADING : NEWLINE ( '## Considered Options' | '## Considered options' ) ; +DECISION_OUTCOME_HEADING : NEWLINE ( '## Decision Outcome' | '## Decision outcome' ) ; +POSITIVE_CONSEQUENCES_HEADING : NEWLINE ( '### Positive Consequences' | '### Positive consequences') ; +NEGATIVE_CONSEQUENCES_HEADING : NEWLINE ( '### Negative Consequences' | '### Negative consequences') ; +PROS_AND_CONS_OF_THE_OPTIONS_HEADING : NEWLINE ( '## Pros and Cons of the Options' | '## Pros and cons of the options') ; +LINKS_HEADING : NEWLINE '## Links' ; diff --git a/src/infrastructure/service/parser/MADRLexer.ts b/src/infrastructure/service/parser/MADRLexer.ts new file mode 100644 index 0000000..59682c4 --- /dev/null +++ b/src/infrastructure/service/parser/MADRLexer.ts @@ -0,0 +1,224 @@ +// Generated from MADR.g4 by ANTLR 4.13.0 +// jshint ignore: start +import antlr4 from 'antlr4' + +const serializedATN = [ + 4, 0, 23, 559, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, + 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, + 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, + 20, 2, 21, 7, 21, 2, 22, 7, 22, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 4, 2, 78, 8, 2, 11, 2, 12, 2, 79, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 3, 5, 87, 8, 5, 1, + 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 96, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, + 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, + 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, + 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, + 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, + 11, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, + 14, 4, 14, 179, 8, 14, 11, 14, 12, 14, 180, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, + 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, + 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, + 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, + 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, + 15, 1, 15, 1, 15, 1, 15, 3, 15, 250, 8, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, + 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, + 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, + 16, 1, 16, 1, 16, 1, 16, 3, 16, 291, 8, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, + 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, + 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, + 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 3, 17, 336, 8, 17, 1, 18, 1, 18, 1, 18, 1, + 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, + 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, + 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 3, 18, 377, 8, 18, 1, 19, 1, 19, 1, 19, 1, + 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, + 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, + 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, + 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 3, 19, 430, 8, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, + 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, + 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, + 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, + 20, 1, 20, 1, 20, 1, 20, 3, 20, 483, 8, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, + 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, + 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, + 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, + 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 3, + 21, 548, 8, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 0, 0, 23, 1, + 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, + 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 1, 0, 4, 3, 0, 9, 10, 12, 13, + 32, 32, 3, 0, 9, 9, 12, 12, 32, 32, 1, 0, 13, 13, 1, 0, 10, 10, 569, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, + 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, + 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, + 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, + 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, + 0, 0, 0, 1, 47, 1, 0, 0, 0, 3, 62, 1, 0, 0, 0, 5, 77, 1, 0, 0, 0, 7, 81, 1, 0, 0, 0, 9, 83, 1, 0, + 0, 0, 11, 86, 1, 0, 0, 0, 13, 90, 1, 0, 0, 0, 15, 97, 1, 0, 0, 0, 17, 107, 1, 0, 0, 0, 19, 115, 1, + 0, 0, 0, 21, 127, 1, 0, 0, 0, 23, 145, 1, 0, 0, 0, 25, 164, 1, 0, 0, 0, 27, 167, 1, 0, 0, 0, 29, + 173, 1, 0, 0, 0, 31, 184, 1, 0, 0, 0, 33, 251, 1, 0, 0, 0, 35, 292, 1, 0, 0, 0, 37, 337, 1, 0, 0, + 0, 39, 378, 1, 0, 0, 0, 41, 431, 1, 0, 0, 0, 43, 484, 1, 0, 0, 0, 45, 549, 1, 0, 0, 0, 47, 48, 5, + 71, 0, 0, 48, 49, 5, 111, 0, 0, 49, 50, 5, 111, 0, 0, 50, 51, 5, 100, 0, 0, 51, 52, 5, 44, 0, 0, + 52, 53, 5, 32, 0, 0, 53, 54, 5, 98, 0, 0, 54, 55, 5, 101, 0, 0, 55, 56, 5, 99, 0, 0, 56, 57, 5, + 97, 0, 0, 57, 58, 5, 117, 0, 0, 58, 59, 5, 115, 0, 0, 59, 60, 5, 101, 0, 0, 60, 61, 5, 32, 0, 0, + 61, 2, 1, 0, 0, 0, 62, 63, 5, 66, 0, 0, 63, 64, 5, 97, 0, 0, 64, 65, 5, 100, 0, 0, 65, 66, 5, 44, + 0, 0, 66, 67, 5, 32, 0, 0, 67, 68, 5, 98, 0, 0, 68, 69, 5, 101, 0, 0, 69, 70, 5, 99, 0, 0, 70, 71, + 5, 97, 0, 0, 71, 72, 5, 117, 0, 0, 72, 73, 5, 115, 0, 0, 73, 74, 5, 101, 0, 0, 74, 75, 5, 32, 0, + 0, 75, 4, 1, 0, 0, 0, 76, 78, 3, 7, 3, 0, 77, 76, 1, 0, 0, 0, 78, 79, 1, 0, 0, 0, 79, 77, 1, 0, 0, + 0, 79, 80, 1, 0, 0, 0, 80, 6, 1, 0, 0, 0, 81, 82, 8, 0, 0, 0, 82, 8, 1, 0, 0, 0, 83, 84, 7, 1, 0, + 0, 84, 10, 1, 0, 0, 0, 85, 87, 7, 2, 0, 0, 86, 85, 1, 0, 0, 0, 86, 87, 1, 0, 0, 0, 87, 88, 1, 0, + 0, 0, 88, 89, 7, 3, 0, 0, 89, 12, 1, 0, 0, 0, 90, 95, 3, 11, 5, 0, 91, 92, 5, 42, 0, 0, 92, 96, 5, + 32, 0, 0, 93, 94, 5, 45, 0, 0, 94, 96, 5, 32, 0, 0, 95, 91, 1, 0, 0, 0, 95, 93, 1, 0, 0, 0, 96, + 14, 1, 0, 0, 0, 97, 98, 3, 13, 6, 0, 98, 99, 5, 83, 0, 0, 99, 100, 5, 116, 0, 0, 100, 101, 5, 97, + 0, 0, 101, 102, 5, 116, 0, 0, 102, 103, 5, 117, 0, 0, 103, 104, 5, 115, 0, 0, 104, 105, 5, 58, 0, + 0, 105, 106, 5, 32, 0, 0, 106, 16, 1, 0, 0, 0, 107, 108, 3, 13, 6, 0, 108, 109, 5, 68, 0, 0, 109, + 110, 5, 97, 0, 0, 110, 111, 5, 116, 0, 0, 111, 112, 5, 101, 0, 0, 112, 113, 5, 58, 0, 0, 113, 114, + 5, 32, 0, 0, 114, 18, 1, 0, 0, 0, 115, 116, 3, 13, 6, 0, 116, 117, 5, 68, 0, 0, 117, 118, 5, 101, + 0, 0, 118, 119, 5, 99, 0, 0, 119, 120, 5, 105, 0, 0, 120, 121, 5, 100, 0, 0, 121, 122, 5, 101, 0, + 0, 122, 123, 5, 114, 0, 0, 123, 124, 5, 115, 0, 0, 124, 125, 5, 58, 0, 0, 125, 126, 5, 32, 0, 0, + 126, 20, 1, 0, 0, 0, 127, 128, 5, 60, 0, 0, 128, 129, 5, 33, 0, 0, 129, 130, 5, 45, 0, 0, 130, + 131, 5, 45, 0, 0, 131, 132, 5, 32, 0, 0, 132, 133, 5, 111, 0, 0, 133, 134, 5, 112, 0, 0, 134, 135, + 5, 116, 0, 0, 135, 136, 5, 105, 0, 0, 136, 137, 5, 111, 0, 0, 137, 138, 5, 110, 0, 0, 138, 139, 5, + 97, 0, 0, 139, 140, 5, 108, 0, 0, 140, 141, 5, 32, 0, 0, 141, 142, 5, 45, 0, 0, 142, 143, 5, 45, + 0, 0, 143, 144, 5, 62, 0, 0, 144, 22, 1, 0, 0, 0, 145, 146, 3, 11, 5, 0, 146, 147, 5, 84, 0, 0, + 147, 148, 5, 101, 0, 0, 148, 149, 5, 99, 0, 0, 149, 150, 5, 104, 0, 0, 150, 151, 5, 110, 0, 0, + 151, 152, 5, 105, 0, 0, 152, 153, 5, 99, 0, 0, 153, 154, 5, 97, 0, 0, 154, 155, 5, 108, 0, 0, 155, + 156, 5, 32, 0, 0, 156, 157, 5, 83, 0, 0, 157, 158, 5, 116, 0, 0, 158, 159, 5, 111, 0, 0, 159, 160, + 5, 114, 0, 0, 160, 161, 5, 121, 0, 0, 161, 162, 5, 58, 0, 0, 162, 163, 5, 32, 0, 0, 163, 24, 1, 0, + 0, 0, 164, 165, 5, 35, 0, 0, 165, 166, 5, 32, 0, 0, 166, 26, 1, 0, 0, 0, 167, 168, 3, 11, 5, 0, + 168, 169, 5, 35, 0, 0, 169, 170, 5, 35, 0, 0, 170, 171, 5, 35, 0, 0, 171, 172, 5, 32, 0, 0, 172, + 28, 1, 0, 0, 0, 173, 174, 5, 35, 0, 0, 174, 175, 5, 35, 0, 0, 175, 176, 5, 35, 0, 0, 176, 178, 1, + 0, 0, 0, 177, 179, 5, 35, 0, 0, 178, 177, 1, 0, 0, 0, 179, 180, 1, 0, 0, 0, 180, 178, 1, 0, 0, 0, + 180, 181, 1, 0, 0, 0, 181, 182, 1, 0, 0, 0, 182, 183, 5, 32, 0, 0, 183, 30, 1, 0, 0, 0, 184, 249, + 3, 11, 5, 0, 185, 186, 5, 35, 0, 0, 186, 187, 5, 35, 0, 0, 187, 188, 5, 32, 0, 0, 188, 189, 5, 67, + 0, 0, 189, 190, 5, 111, 0, 0, 190, 191, 5, 110, 0, 0, 191, 192, 5, 116, 0, 0, 192, 193, 5, 101, 0, + 0, 193, 194, 5, 120, 0, 0, 194, 195, 5, 116, 0, 0, 195, 196, 5, 32, 0, 0, 196, 197, 5, 97, 0, 0, + 197, 198, 5, 110, 0, 0, 198, 199, 5, 100, 0, 0, 199, 200, 5, 32, 0, 0, 200, 201, 5, 80, 0, 0, 201, + 202, 5, 114, 0, 0, 202, 203, 5, 111, 0, 0, 203, 204, 5, 98, 0, 0, 204, 205, 5, 108, 0, 0, 205, + 206, 5, 101, 0, 0, 206, 207, 5, 109, 0, 0, 207, 208, 5, 32, 0, 0, 208, 209, 5, 83, 0, 0, 209, 210, + 5, 116, 0, 0, 210, 211, 5, 97, 0, 0, 211, 212, 5, 116, 0, 0, 212, 213, 5, 101, 0, 0, 213, 214, 5, + 109, 0, 0, 214, 215, 5, 101, 0, 0, 215, 216, 5, 110, 0, 0, 216, 250, 5, 116, 0, 0, 217, 218, 5, + 35, 0, 0, 218, 219, 5, 35, 0, 0, 219, 220, 5, 32, 0, 0, 220, 221, 5, 67, 0, 0, 221, 222, 5, 111, + 0, 0, 222, 223, 5, 110, 0, 0, 223, 224, 5, 116, 0, 0, 224, 225, 5, 101, 0, 0, 225, 226, 5, 120, 0, + 0, 226, 227, 5, 116, 0, 0, 227, 228, 5, 32, 0, 0, 228, 229, 5, 97, 0, 0, 229, 230, 5, 110, 0, 0, + 230, 231, 5, 100, 0, 0, 231, 232, 5, 32, 0, 0, 232, 233, 5, 112, 0, 0, 233, 234, 5, 114, 0, 0, + 234, 235, 5, 111, 0, 0, 235, 236, 5, 98, 0, 0, 236, 237, 5, 108, 0, 0, 237, 238, 5, 101, 0, 0, + 238, 239, 5, 109, 0, 0, 239, 240, 5, 32, 0, 0, 240, 241, 5, 115, 0, 0, 241, 242, 5, 116, 0, 0, + 242, 243, 5, 97, 0, 0, 243, 244, 5, 116, 0, 0, 244, 245, 5, 101, 0, 0, 245, 246, 5, 109, 0, 0, + 246, 247, 5, 101, 0, 0, 247, 248, 5, 110, 0, 0, 248, 250, 5, 116, 0, 0, 249, 185, 1, 0, 0, 0, 249, + 217, 1, 0, 0, 0, 250, 32, 1, 0, 0, 0, 251, 290, 3, 11, 5, 0, 252, 253, 5, 35, 0, 0, 253, 254, 5, + 35, 0, 0, 254, 255, 5, 32, 0, 0, 255, 256, 5, 68, 0, 0, 256, 257, 5, 101, 0, 0, 257, 258, 5, 99, + 0, 0, 258, 259, 5, 105, 0, 0, 259, 260, 5, 115, 0, 0, 260, 261, 5, 105, 0, 0, 261, 262, 5, 111, 0, + 0, 262, 263, 5, 110, 0, 0, 263, 264, 5, 32, 0, 0, 264, 265, 5, 68, 0, 0, 265, 266, 5, 114, 0, 0, + 266, 267, 5, 105, 0, 0, 267, 268, 5, 118, 0, 0, 268, 269, 5, 101, 0, 0, 269, 270, 5, 114, 0, 0, + 270, 291, 5, 115, 0, 0, 271, 272, 5, 35, 0, 0, 272, 273, 5, 35, 0, 0, 273, 274, 5, 32, 0, 0, 274, + 275, 5, 68, 0, 0, 275, 276, 5, 101, 0, 0, 276, 277, 5, 99, 0, 0, 277, 278, 5, 105, 0, 0, 278, 279, + 5, 115, 0, 0, 279, 280, 5, 105, 0, 0, 280, 281, 5, 111, 0, 0, 281, 282, 5, 110, 0, 0, 282, 283, 5, + 32, 0, 0, 283, 284, 5, 100, 0, 0, 284, 285, 5, 114, 0, 0, 285, 286, 5, 105, 0, 0, 286, 287, 5, + 118, 0, 0, 287, 288, 5, 101, 0, 0, 288, 289, 5, 114, 0, 0, 289, 291, 5, 115, 0, 0, 290, 252, 1, 0, + 0, 0, 290, 271, 1, 0, 0, 0, 291, 34, 1, 0, 0, 0, 292, 335, 3, 11, 5, 0, 293, 294, 5, 35, 0, 0, + 294, 295, 5, 35, 0, 0, 295, 296, 5, 32, 0, 0, 296, 297, 5, 67, 0, 0, 297, 298, 5, 111, 0, 0, 298, + 299, 5, 110, 0, 0, 299, 300, 5, 115, 0, 0, 300, 301, 5, 105, 0, 0, 301, 302, 5, 100, 0, 0, 302, + 303, 5, 101, 0, 0, 303, 304, 5, 114, 0, 0, 304, 305, 5, 101, 0, 0, 305, 306, 5, 100, 0, 0, 306, + 307, 5, 32, 0, 0, 307, 308, 5, 79, 0, 0, 308, 309, 5, 112, 0, 0, 309, 310, 5, 116, 0, 0, 310, 311, + 5, 105, 0, 0, 311, 312, 5, 111, 0, 0, 312, 313, 5, 110, 0, 0, 313, 336, 5, 115, 0, 0, 314, 315, 5, + 35, 0, 0, 315, 316, 5, 35, 0, 0, 316, 317, 5, 32, 0, 0, 317, 318, 5, 67, 0, 0, 318, 319, 5, 111, + 0, 0, 319, 320, 5, 110, 0, 0, 320, 321, 5, 115, 0, 0, 321, 322, 5, 105, 0, 0, 322, 323, 5, 100, 0, + 0, 323, 324, 5, 101, 0, 0, 324, 325, 5, 114, 0, 0, 325, 326, 5, 101, 0, 0, 326, 327, 5, 100, 0, 0, + 327, 328, 5, 32, 0, 0, 328, 329, 5, 111, 0, 0, 329, 330, 5, 112, 0, 0, 330, 331, 5, 116, 0, 0, + 331, 332, 5, 105, 0, 0, 332, 333, 5, 111, 0, 0, 333, 334, 5, 110, 0, 0, 334, 336, 5, 115, 0, 0, + 335, 293, 1, 0, 0, 0, 335, 314, 1, 0, 0, 0, 336, 36, 1, 0, 0, 0, 337, 376, 3, 11, 5, 0, 338, 339, + 5, 35, 0, 0, 339, 340, 5, 35, 0, 0, 340, 341, 5, 32, 0, 0, 341, 342, 5, 68, 0, 0, 342, 343, 5, + 101, 0, 0, 343, 344, 5, 99, 0, 0, 344, 345, 5, 105, 0, 0, 345, 346, 5, 115, 0, 0, 346, 347, 5, + 105, 0, 0, 347, 348, 5, 111, 0, 0, 348, 349, 5, 110, 0, 0, 349, 350, 5, 32, 0, 0, 350, 351, 5, 79, + 0, 0, 351, 352, 5, 117, 0, 0, 352, 353, 5, 116, 0, 0, 353, 354, 5, 99, 0, 0, 354, 355, 5, 111, 0, + 0, 355, 356, 5, 109, 0, 0, 356, 377, 5, 101, 0, 0, 357, 358, 5, 35, 0, 0, 358, 359, 5, 35, 0, 0, + 359, 360, 5, 32, 0, 0, 360, 361, 5, 68, 0, 0, 361, 362, 5, 101, 0, 0, 362, 363, 5, 99, 0, 0, 363, + 364, 5, 105, 0, 0, 364, 365, 5, 115, 0, 0, 365, 366, 5, 105, 0, 0, 366, 367, 5, 111, 0, 0, 367, + 368, 5, 110, 0, 0, 368, 369, 5, 32, 0, 0, 369, 370, 5, 111, 0, 0, 370, 371, 5, 117, 0, 0, 371, + 372, 5, 116, 0, 0, 372, 373, 5, 99, 0, 0, 373, 374, 5, 111, 0, 0, 374, 375, 5, 109, 0, 0, 375, + 377, 5, 101, 0, 0, 376, 338, 1, 0, 0, 0, 376, 357, 1, 0, 0, 0, 377, 38, 1, 0, 0, 0, 378, 429, 3, + 11, 5, 0, 379, 380, 5, 35, 0, 0, 380, 381, 5, 35, 0, 0, 381, 382, 5, 35, 0, 0, 382, 383, 5, 32, 0, + 0, 383, 384, 5, 80, 0, 0, 384, 385, 5, 111, 0, 0, 385, 386, 5, 115, 0, 0, 386, 387, 5, 105, 0, 0, + 387, 388, 5, 116, 0, 0, 388, 389, 5, 105, 0, 0, 389, 390, 5, 118, 0, 0, 390, 391, 5, 101, 0, 0, + 391, 392, 5, 32, 0, 0, 392, 393, 5, 67, 0, 0, 393, 394, 5, 111, 0, 0, 394, 395, 5, 110, 0, 0, 395, + 396, 5, 115, 0, 0, 396, 397, 5, 101, 0, 0, 397, 398, 5, 113, 0, 0, 398, 399, 5, 117, 0, 0, 399, + 400, 5, 101, 0, 0, 400, 401, 5, 110, 0, 0, 401, 402, 5, 99, 0, 0, 402, 403, 5, 101, 0, 0, 403, + 430, 5, 115, 0, 0, 404, 405, 5, 35, 0, 0, 405, 406, 5, 35, 0, 0, 406, 407, 5, 35, 0, 0, 407, 408, + 5, 32, 0, 0, 408, 409, 5, 80, 0, 0, 409, 410, 5, 111, 0, 0, 410, 411, 5, 115, 0, 0, 411, 412, 5, + 105, 0, 0, 412, 413, 5, 116, 0, 0, 413, 414, 5, 105, 0, 0, 414, 415, 5, 118, 0, 0, 415, 416, 5, + 101, 0, 0, 416, 417, 5, 32, 0, 0, 417, 418, 5, 99, 0, 0, 418, 419, 5, 111, 0, 0, 419, 420, 5, 110, + 0, 0, 420, 421, 5, 115, 0, 0, 421, 422, 5, 101, 0, 0, 422, 423, 5, 113, 0, 0, 423, 424, 5, 117, 0, + 0, 424, 425, 5, 101, 0, 0, 425, 426, 5, 110, 0, 0, 426, 427, 5, 99, 0, 0, 427, 428, 5, 101, 0, 0, + 428, 430, 5, 115, 0, 0, 429, 379, 1, 0, 0, 0, 429, 404, 1, 0, 0, 0, 430, 40, 1, 0, 0, 0, 431, 482, + 3, 11, 5, 0, 432, 433, 5, 35, 0, 0, 433, 434, 5, 35, 0, 0, 434, 435, 5, 35, 0, 0, 435, 436, 5, 32, + 0, 0, 436, 437, 5, 78, 0, 0, 437, 438, 5, 101, 0, 0, 438, 439, 5, 103, 0, 0, 439, 440, 5, 97, 0, + 0, 440, 441, 5, 116, 0, 0, 441, 442, 5, 105, 0, 0, 442, 443, 5, 118, 0, 0, 443, 444, 5, 101, 0, 0, + 444, 445, 5, 32, 0, 0, 445, 446, 5, 67, 0, 0, 446, 447, 5, 111, 0, 0, 447, 448, 5, 110, 0, 0, 448, + 449, 5, 115, 0, 0, 449, 450, 5, 101, 0, 0, 450, 451, 5, 113, 0, 0, 451, 452, 5, 117, 0, 0, 452, + 453, 5, 101, 0, 0, 453, 454, 5, 110, 0, 0, 454, 455, 5, 99, 0, 0, 455, 456, 5, 101, 0, 0, 456, + 483, 5, 115, 0, 0, 457, 458, 5, 35, 0, 0, 458, 459, 5, 35, 0, 0, 459, 460, 5, 35, 0, 0, 460, 461, + 5, 32, 0, 0, 461, 462, 5, 78, 0, 0, 462, 463, 5, 101, 0, 0, 463, 464, 5, 103, 0, 0, 464, 465, 5, + 97, 0, 0, 465, 466, 5, 116, 0, 0, 466, 467, 5, 105, 0, 0, 467, 468, 5, 118, 0, 0, 468, 469, 5, + 101, 0, 0, 469, 470, 5, 32, 0, 0, 470, 471, 5, 99, 0, 0, 471, 472, 5, 111, 0, 0, 472, 473, 5, 110, + 0, 0, 473, 474, 5, 115, 0, 0, 474, 475, 5, 101, 0, 0, 475, 476, 5, 113, 0, 0, 476, 477, 5, 117, 0, + 0, 477, 478, 5, 101, 0, 0, 478, 479, 5, 110, 0, 0, 479, 480, 5, 99, 0, 0, 480, 481, 5, 101, 0, 0, + 481, 483, 5, 115, 0, 0, 482, 432, 1, 0, 0, 0, 482, 457, 1, 0, 0, 0, 483, 42, 1, 0, 0, 0, 484, 547, + 3, 11, 5, 0, 485, 486, 5, 35, 0, 0, 486, 487, 5, 35, 0, 0, 487, 488, 5, 32, 0, 0, 488, 489, 5, 80, + 0, 0, 489, 490, 5, 114, 0, 0, 490, 491, 5, 111, 0, 0, 491, 492, 5, 115, 0, 0, 492, 493, 5, 32, 0, + 0, 493, 494, 5, 97, 0, 0, 494, 495, 5, 110, 0, 0, 495, 496, 5, 100, 0, 0, 496, 497, 5, 32, 0, 0, + 497, 498, 5, 67, 0, 0, 498, 499, 5, 111, 0, 0, 499, 500, 5, 110, 0, 0, 500, 501, 5, 115, 0, 0, + 501, 502, 5, 32, 0, 0, 502, 503, 5, 111, 0, 0, 503, 504, 5, 102, 0, 0, 504, 505, 5, 32, 0, 0, 505, + 506, 5, 116, 0, 0, 506, 507, 5, 104, 0, 0, 507, 508, 5, 101, 0, 0, 508, 509, 5, 32, 0, 0, 509, + 510, 5, 79, 0, 0, 510, 511, 5, 112, 0, 0, 511, 512, 5, 116, 0, 0, 512, 513, 5, 105, 0, 0, 513, + 514, 5, 111, 0, 0, 514, 515, 5, 110, 0, 0, 515, 548, 5, 115, 0, 0, 516, 517, 5, 35, 0, 0, 517, + 518, 5, 35, 0, 0, 518, 519, 5, 32, 0, 0, 519, 520, 5, 80, 0, 0, 520, 521, 5, 114, 0, 0, 521, 522, + 5, 111, 0, 0, 522, 523, 5, 115, 0, 0, 523, 524, 5, 32, 0, 0, 524, 525, 5, 97, 0, 0, 525, 526, 5, + 110, 0, 0, 526, 527, 5, 100, 0, 0, 527, 528, 5, 32, 0, 0, 528, 529, 5, 99, 0, 0, 529, 530, 5, 111, + 0, 0, 530, 531, 5, 110, 0, 0, 531, 532, 5, 115, 0, 0, 532, 533, 5, 32, 0, 0, 533, 534, 5, 111, 0, + 0, 534, 535, 5, 102, 0, 0, 535, 536, 5, 32, 0, 0, 536, 537, 5, 116, 0, 0, 537, 538, 5, 104, 0, 0, + 538, 539, 5, 101, 0, 0, 539, 540, 5, 32, 0, 0, 540, 541, 5, 111, 0, 0, 541, 542, 5, 112, 0, 0, + 542, 543, 5, 116, 0, 0, 543, 544, 5, 105, 0, 0, 544, 545, 5, 111, 0, 0, 545, 546, 5, 110, 0, 0, + 546, 548, 5, 115, 0, 0, 547, 485, 1, 0, 0, 0, 547, 516, 1, 0, 0, 0, 548, 44, 1, 0, 0, 0, 549, 550, + 3, 11, 5, 0, 550, 551, 5, 35, 0, 0, 551, 552, 5, 35, 0, 0, 552, 553, 5, 32, 0, 0, 553, 554, 5, 76, + 0, 0, 554, 555, 5, 105, 0, 0, 555, 556, 5, 110, 0, 0, 556, 557, 5, 107, 0, 0, 557, 558, 5, 115, 0, + 0, 558, 46, 1, 0, 0, 0, 12, 0, 79, 86, 95, 180, 249, 290, 335, 376, 429, 482, 547, 0 +] + +const atn = new antlr4.atn.ATNDeserializer().deserialize(serializedATN) + +const decisionsToDFA = atn.decisionToState.map((ds, index) => new antlr4.dfa.DFA(ds, index)) + +export default class MADRLexer extends antlr4.Lexer { + constructor(input) { + super(input) + this._interp = new antlr4.atn.LexerATNSimulator( + this, + atn, + decisionsToDFA, + new antlr4.atn.PredictionContextCache() + ) + } +} + +MADRLexer.EOF = antlr4.Token.EOF +MADRLexer.T__0 = 1 +MADRLexer.T__1 = 2 +MADRLexer.WORD = 3 +MADRLexer.CHARACTER = 4 +MADRLexer.WS = 5 +MADRLexer.NEWLINE = 6 +MADRLexer.LIST_MARKER = 7 +MADRLexer.STATUS_MARKER = 8 +MADRLexer.DATE_MARKER = 9 +MADRLexer.DECIDERS_MARKER = 10 +MADRLexer.OPTIONAL_MAKER = 11 +MADRLexer.TECHNICAL_STORY_MARKER = 12 +MADRLexer.HEADING_PREFIX = 13 +MADRLexer.SUBSUBHEADING_PREFIX = 14 +MADRLexer.SUBSUBSUBHEADING_PREFIX = 15 +MADRLexer.CONTEXT_AND_PROBLEM_STATEMENT = 16 +MADRLexer.DECISION_DRIVERS_HEADING = 17 +MADRLexer.CONSIDERED_OPTIONS_HEADING = 18 +MADRLexer.DECISION_OUTCOME_HEADING = 19 +MADRLexer.POSITIVE_CONSEQUENCES_HEADING = 20 +MADRLexer.NEGATIVE_CONSEQUENCES_HEADING = 21 +MADRLexer.PROS_AND_CONS_OF_THE_OPTIONS_HEADING = 22 +MADRLexer.LINKS_HEADING = 23 diff --git a/src/infrastructure/service/parser/MADRListener.ts b/src/infrastructure/service/parser/MADRListener.ts new file mode 100644 index 0000000..c83be4c --- /dev/null +++ b/src/infrastructure/service/parser/MADRListener.ts @@ -0,0 +1,162 @@ +// Generated from MADR.g4 by ANTLR 4.13.0 +// jshint ignore: start +import antlr4 from 'antlr4' + +// This class defines a complete listener for a parse tree produced by MADRParser. +export default class MADRListener extends antlr4.tree.ParseTreeListener { + // Enter a parse tree produced by MADRParser#start. + enterStart(ctx) {} + + // Exit a parse tree produced by MADRParser#start. + exitStart(ctx) {} + + // Enter a parse tree produced by MADRParser#title. + enterTitle(ctx) {} + + // Exit a parse tree produced by MADRParser#title. + exitTitle(ctx) {} + + // Enter a parse tree produced by MADRParser#status. + enterStatus(ctx) {} + + // Exit a parse tree produced by MADRParser#status. + exitStatus(ctx) {} + + // Enter a parse tree produced by MADRParser#deciders. + enterDeciders(ctx) {} + + // Exit a parse tree produced by MADRParser#deciders. + exitDeciders(ctx) {} + + // Enter a parse tree produced by MADRParser#date. + enterDate(ctx) {} + + // Exit a parse tree produced by MADRParser#date. + exitDate(ctx) {} + + // Enter a parse tree produced by MADRParser#technicalStory. + enterTechnicalStory(ctx) {} + + // Exit a parse tree produced by MADRParser#technicalStory. + exitTechnicalStory(ctx) {} + + // Enter a parse tree produced by MADRParser#contextAndProblemStatement. + enterContextAndProblemStatement(ctx) {} + + // Exit a parse tree produced by MADRParser#contextAndProblemStatement. + exitContextAndProblemStatement(ctx) {} + + // Enter a parse tree produced by MADRParser#decisionDrivers. + enterDecisionDrivers(ctx) {} + + // Exit a parse tree produced by MADRParser#decisionDrivers. + exitDecisionDrivers(ctx) {} + + // Enter a parse tree produced by MADRParser#consideredOptions. + enterConsideredOptions(ctx) {} + + // Exit a parse tree produced by MADRParser#consideredOptions. + exitConsideredOptions(ctx) {} + + // Enter a parse tree produced by MADRParser#decisionOutcome. + enterDecisionOutcome(ctx) {} + + // Exit a parse tree produced by MADRParser#decisionOutcome. + exitDecisionOutcome(ctx) {} + + // Enter a parse tree produced by MADRParser#prosAndConsOfOptions. + enterProsAndConsOfOptions(ctx) {} + + // Exit a parse tree produced by MADRParser#prosAndConsOfOptions. + exitProsAndConsOfOptions(ctx) {} + + // Enter a parse tree produced by MADRParser#optionSection. + enterOptionSection(ctx) {} + + // Exit a parse tree produced by MADRParser#optionSection. + exitOptionSection(ctx) {} + + // Enter a parse tree produced by MADRParser#chosenOptionAndExplanation. + enterChosenOptionAndExplanation(ctx) {} + + // Exit a parse tree produced by MADRParser#chosenOptionAndExplanation. + exitChosenOptionAndExplanation(ctx) {} + + // Enter a parse tree produced by MADRParser#positiveConsequences. + enterPositiveConsequences(ctx) {} + + // Exit a parse tree produced by MADRParser#positiveConsequences. + exitPositiveConsequences(ctx) {} + + // Enter a parse tree produced by MADRParser#negativeConsequences. + enterNegativeConsequences(ctx) {} + + // Exit a parse tree produced by MADRParser#negativeConsequences. + exitNegativeConsequences(ctx) {} + + // Enter a parse tree produced by MADRParser#optionTitle. + enterOptionTitle(ctx) {} + + // Exit a parse tree produced by MADRParser#optionTitle. + exitOptionTitle(ctx) {} + + // Enter a parse tree produced by MADRParser#optionDescription. + enterOptionDescription(ctx) {} + + // Exit a parse tree produced by MADRParser#optionDescription. + exitOptionDescription(ctx) {} + + // Enter a parse tree produced by MADRParser#prolist. + enterProlist(ctx) {} + + // Exit a parse tree produced by MADRParser#prolist. + exitProlist(ctx) {} + + // Enter a parse tree produced by MADRParser#conlist. + enterConlist(ctx) {} + + // Exit a parse tree produced by MADRParser#conlist. + exitConlist(ctx) {} + + // Enter a parse tree produced by MADRParser#links. + enterLinks(ctx) {} + + // Exit a parse tree produced by MADRParser#links. + exitLinks(ctx) {} + + // Enter a parse tree produced by MADRParser#list. + enterList(ctx) {} + + // Exit a parse tree produced by MADRParser#list. + exitList(ctx) {} + + // Enter a parse tree produced by MADRParser#textLine. + enterTextLine(ctx) {} + + // Exit a parse tree produced by MADRParser#textLine. + exitTextLine(ctx) {} + + // Enter a parse tree produced by MADRParser#multilineText. + enterMultilineText(ctx) {} + + // Exit a parse tree produced by MADRParser#multilineText. + exitMultilineText(ctx) {} + + // Enter a parse tree produced by MADRParser#any. + enterAny(ctx) {} + + // Exit a parse tree produced by MADRParser#any. + exitAny(ctx) {} + + // Enter a parse tree produced by MADRParser#wslb. + enterWslb(ctx) {} + + // Exit a parse tree produced by MADRParser#wslb. + exitWslb(ctx) {} + + // Enter a parse tree produced by MADRParser#wslbs. + enterWslbs(ctx) {} + + // Exit a parse tree produced by MADRParser#wslbs. + exitWslbs(ctx) {} +} diff --git a/src/infrastructure/service/parser/MADRParser.ts b/src/infrastructure/service/parser/MADRParser.ts new file mode 100644 index 0000000..ba668cc --- /dev/null +++ b/src/infrastructure/service/parser/MADRParser.ts @@ -0,0 +1,2422 @@ +// Generated from MADR.g4 by ANTLR 4.13.0 +// jshint ignore: start +import antlr4 from 'antlr4' +import MADRListener from './MADRListener.js' +const serializedATN = [ + 4, 1, 23, 282, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, + 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, + 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, + 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 3, 0, 61, 8, 0, 1, 0, 1, 0, 3, 0, 65, 8, 0, 1, 0, 1, 0, 1, 0, 1, 0, 3, 0, 71, 8, 0, 1, + 0, 1, 0, 3, 0, 75, 8, 0, 1, 0, 1, 0, 1, 0, 1, 0, 3, 0, 81, 8, 0, 1, 0, 1, 0, 1, 0, 3, 0, 86, 8, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 3, 0, 92, 8, 0, 1, 0, 1, 0, 3, 0, 96, 8, 0, 1, 0, 1, 0, 3, 0, 100, 8, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 3, 0, 106, 8, 0, 1, 0, 1, 0, 1, 0, 3, 0, 111, 8, 0, 1, 0, 1, 0, 1, 0, 1, + 0, 3, 0, 117, 8, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 3, 0, 124, 8, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 3, 0, 131, 8, 0, 1, 0, 1, 0, 1, 0, 3, 0, 136, 8, 0, 1, 0, 1, 0, 1, 0, 1, 0, 3, 0, 142, 8, 0, 1, 0, + 1, 0, 1, 0, 3, 0, 147, 8, 0, 1, 0, 1, 0, 1, 0, 1, 0, 3, 0, 153, 8, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, + 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, + 1, 9, 1, 9, 1, 9, 3, 9, 179, 8, 9, 1, 9, 1, 9, 3, 9, 183, 8, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 189, + 8, 9, 1, 9, 1, 9, 3, 9, 193, 8, 9, 1, 10, 1, 10, 1, 10, 4, 10, 198, 8, 10, 11, 10, 12, 10, 199, 1, + 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 3, 11, 208, 8, 11, 1, 11, 1, 11, 1, 11, 3, 11, 213, 8, 11, + 1, 11, 1, 11, 1, 11, 3, 11, 218, 8, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, + 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 4, 17, 235, 8, 17, 11, 17, 12, 17, 236, 1, 18, 1, + 18, 1, 18, 1, 18, 1, 18, 4, 18, 244, 8, 18, 11, 18, 12, 18, 245, 1, 19, 1, 19, 1, 19, 1, 20, 1, + 20, 1, 20, 3, 20, 254, 8, 20, 4, 20, 256, 8, 20, 11, 20, 12, 20, 257, 1, 21, 1, 21, 4, 21, 262, 8, + 21, 11, 21, 12, 21, 263, 1, 22, 1, 22, 4, 22, 268, 8, 22, 11, 22, 12, 22, 269, 1, 23, 1, 23, 1, + 24, 1, 24, 1, 25, 5, 25, 277, 8, 25, 10, 25, 12, 25, 280, 9, 25, 1, 25, 2, 263, 269, 0, 26, 0, 2, + 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 0, 2, + 4, 0, 3, 4, 7, 7, 13, 13, 15, 15, 1, 0, 5, 6, 290, 0, 52, 1, 0, 0, 0, 2, 156, 1, 0, 0, 0, 4, 158, + 1, 0, 0, 0, 6, 160, 1, 0, 0, 0, 8, 162, 1, 0, 0, 0, 10, 164, 1, 0, 0, 0, 12, 166, 1, 0, 0, 0, 14, + 168, 1, 0, 0, 0, 16, 170, 1, 0, 0, 0, 18, 172, 1, 0, 0, 0, 20, 197, 1, 0, 0, 0, 22, 201, 1, 0, 0, + 0, 24, 219, 1, 0, 0, 0, 26, 221, 1, 0, 0, 0, 28, 223, 1, 0, 0, 0, 30, 225, 1, 0, 0, 0, 32, 227, 1, + 0, 0, 0, 34, 234, 1, 0, 0, 0, 36, 243, 1, 0, 0, 0, 38, 247, 1, 0, 0, 0, 40, 255, 1, 0, 0, 0, 42, + 261, 1, 0, 0, 0, 44, 267, 1, 0, 0, 0, 46, 271, 1, 0, 0, 0, 48, 273, 1, 0, 0, 0, 50, 278, 1, 0, 0, + 0, 52, 53, 5, 13, 0, 0, 53, 54, 3, 2, 1, 0, 54, 55, 5, 6, 0, 0, 55, 64, 3, 50, 25, 0, 56, 57, 5, + 8, 0, 0, 57, 60, 3, 4, 2, 0, 58, 59, 5, 5, 0, 0, 59, 61, 5, 11, 0, 0, 60, 58, 1, 0, 0, 0, 60, 61, + 1, 0, 0, 0, 61, 62, 1, 0, 0, 0, 62, 63, 3, 50, 25, 0, 63, 65, 1, 0, 0, 0, 64, 56, 1, 0, 0, 0, 64, + 65, 1, 0, 0, 0, 65, 74, 1, 0, 0, 0, 66, 67, 5, 10, 0, 0, 67, 70, 3, 6, 3, 0, 68, 69, 5, 5, 0, 0, + 69, 71, 5, 11, 0, 0, 70, 68, 1, 0, 0, 0, 70, 71, 1, 0, 0, 0, 71, 72, 1, 0, 0, 0, 72, 73, 3, 50, + 25, 0, 73, 75, 1, 0, 0, 0, 74, 66, 1, 0, 0, 0, 74, 75, 1, 0, 0, 0, 75, 85, 1, 0, 0, 0, 76, 77, 5, + 9, 0, 0, 77, 80, 3, 8, 4, 0, 78, 79, 5, 5, 0, 0, 79, 81, 5, 11, 0, 0, 80, 78, 1, 0, 0, 0, 80, 81, + 1, 0, 0, 0, 81, 82, 1, 0, 0, 0, 82, 83, 5, 6, 0, 0, 83, 84, 3, 50, 25, 0, 84, 86, 1, 0, 0, 0, 85, + 76, 1, 0, 0, 0, 85, 86, 1, 0, 0, 0, 86, 95, 1, 0, 0, 0, 87, 88, 5, 12, 0, 0, 88, 91, 3, 10, 5, 0, + 89, 90, 5, 5, 0, 0, 90, 92, 5, 11, 0, 0, 91, 89, 1, 0, 0, 0, 91, 92, 1, 0, 0, 0, 92, 93, 1, 0, 0, + 0, 93, 94, 3, 50, 25, 0, 94, 96, 1, 0, 0, 0, 95, 87, 1, 0, 0, 0, 95, 96, 1, 0, 0, 0, 96, 99, 1, 0, + 0, 0, 97, 98, 5, 16, 0, 0, 98, 100, 3, 50, 25, 0, 99, 97, 1, 0, 0, 0, 99, 100, 1, 0, 0, 0, 100, + 105, 1, 0, 0, 0, 101, 102, 5, 6, 0, 0, 102, 103, 3, 12, 6, 0, 103, 104, 3, 50, 25, 0, 104, 106, 1, + 0, 0, 0, 105, 101, 1, 0, 0, 0, 105, 106, 1, 0, 0, 0, 106, 116, 1, 0, 0, 0, 107, 110, 5, 17, 0, 0, + 108, 109, 5, 5, 0, 0, 109, 111, 5, 11, 0, 0, 110, 108, 1, 0, 0, 0, 110, 111, 1, 0, 0, 0, 111, 112, + 1, 0, 0, 0, 112, 113, 3, 50, 25, 0, 113, 114, 3, 14, 7, 0, 114, 115, 3, 50, 25, 0, 115, 117, 1, 0, + 0, 0, 116, 107, 1, 0, 0, 0, 116, 117, 1, 0, 0, 0, 117, 123, 1, 0, 0, 0, 118, 119, 5, 18, 0, 0, + 119, 120, 3, 50, 25, 0, 120, 121, 3, 16, 8, 0, 121, 122, 3, 50, 25, 0, 122, 124, 1, 0, 0, 0, 123, + 118, 1, 0, 0, 0, 123, 124, 1, 0, 0, 0, 124, 130, 1, 0, 0, 0, 125, 126, 5, 19, 0, 0, 126, 127, 3, + 50, 25, 0, 127, 128, 3, 18, 9, 0, 128, 129, 3, 50, 25, 0, 129, 131, 1, 0, 0, 0, 130, 125, 1, 0, 0, + 0, 130, 131, 1, 0, 0, 0, 131, 141, 1, 0, 0, 0, 132, 135, 5, 22, 0, 0, 133, 134, 5, 5, 0, 0, 134, + 136, 5, 11, 0, 0, 135, 133, 1, 0, 0, 0, 135, 136, 1, 0, 0, 0, 136, 137, 1, 0, 0, 0, 137, 138, 3, + 50, 25, 0, 138, 139, 3, 20, 10, 0, 139, 140, 3, 50, 25, 0, 140, 142, 1, 0, 0, 0, 141, 132, 1, 0, + 0, 0, 141, 142, 1, 0, 0, 0, 142, 152, 1, 0, 0, 0, 143, 146, 5, 23, 0, 0, 144, 145, 5, 5, 0, 0, + 145, 147, 5, 11, 0, 0, 146, 144, 1, 0, 0, 0, 146, 147, 1, 0, 0, 0, 147, 148, 1, 0, 0, 0, 148, 149, + 3, 50, 25, 0, 149, 150, 3, 38, 19, 0, 150, 151, 3, 50, 25, 0, 151, 153, 1, 0, 0, 0, 152, 143, 1, + 0, 0, 0, 152, 153, 1, 0, 0, 0, 153, 154, 1, 0, 0, 0, 154, 155, 5, 0, 0, 1, 155, 1, 1, 0, 0, 0, + 156, 157, 3, 42, 21, 0, 157, 3, 1, 0, 0, 0, 158, 159, 3, 42, 21, 0, 159, 5, 1, 0, 0, 0, 160, 161, + 3, 42, 21, 0, 161, 7, 1, 0, 0, 0, 162, 163, 3, 42, 21, 0, 163, 9, 1, 0, 0, 0, 164, 165, 3, 42, 21, + 0, 165, 11, 1, 0, 0, 0, 166, 167, 3, 44, 22, 0, 167, 13, 1, 0, 0, 0, 168, 169, 3, 40, 20, 0, 169, + 15, 1, 0, 0, 0, 170, 171, 3, 40, 20, 0, 171, 17, 1, 0, 0, 0, 172, 173, 3, 50, 25, 0, 173, 182, 3, + 24, 12, 0, 174, 175, 3, 50, 25, 0, 175, 178, 5, 20, 0, 0, 176, 177, 5, 5, 0, 0, 177, 179, 5, 11, + 0, 0, 178, 176, 1, 0, 0, 0, 178, 179, 1, 0, 0, 0, 179, 180, 1, 0, 0, 0, 180, 181, 3, 26, 13, 0, + 181, 183, 1, 0, 0, 0, 182, 174, 1, 0, 0, 0, 182, 183, 1, 0, 0, 0, 183, 192, 1, 0, 0, 0, 184, 185, + 3, 50, 25, 0, 185, 188, 5, 21, 0, 0, 186, 187, 5, 5, 0, 0, 187, 189, 5, 11, 0, 0, 188, 186, 1, 0, + 0, 0, 188, 189, 1, 0, 0, 0, 189, 190, 1, 0, 0, 0, 190, 191, 3, 28, 14, 0, 191, 193, 1, 0, 0, 0, + 192, 184, 1, 0, 0, 0, 192, 193, 1, 0, 0, 0, 193, 19, 1, 0, 0, 0, 194, 195, 3, 22, 11, 0, 195, 196, + 3, 50, 25, 0, 196, 198, 1, 0, 0, 0, 197, 194, 1, 0, 0, 0, 198, 199, 1, 0, 0, 0, 199, 197, 1, 0, 0, + 0, 199, 200, 1, 0, 0, 0, 200, 21, 1, 0, 0, 0, 201, 202, 5, 14, 0, 0, 202, 203, 3, 30, 15, 0, 203, + 207, 5, 6, 0, 0, 204, 205, 3, 50, 25, 0, 205, 206, 3, 32, 16, 0, 206, 208, 1, 0, 0, 0, 207, 204, + 1, 0, 0, 0, 207, 208, 1, 0, 0, 0, 208, 212, 1, 0, 0, 0, 209, 210, 3, 50, 25, 0, 210, 211, 3, 34, + 17, 0, 211, 213, 1, 0, 0, 0, 212, 209, 1, 0, 0, 0, 212, 213, 1, 0, 0, 0, 213, 217, 1, 0, 0, 0, + 214, 215, 3, 50, 25, 0, 215, 216, 3, 36, 18, 0, 216, 218, 1, 0, 0, 0, 217, 214, 1, 0, 0, 0, 217, + 218, 1, 0, 0, 0, 218, 23, 1, 0, 0, 0, 219, 220, 3, 44, 22, 0, 220, 25, 1, 0, 0, 0, 221, 222, 3, + 40, 20, 0, 222, 27, 1, 0, 0, 0, 223, 224, 3, 40, 20, 0, 224, 29, 1, 0, 0, 0, 225, 226, 3, 42, 21, + 0, 226, 31, 1, 0, 0, 0, 227, 228, 3, 44, 22, 0, 228, 33, 1, 0, 0, 0, 229, 230, 3, 50, 25, 0, 230, + 231, 5, 7, 0, 0, 231, 232, 5, 1, 0, 0, 232, 233, 3, 42, 21, 0, 233, 235, 1, 0, 0, 0, 234, 229, 1, + 0, 0, 0, 235, 236, 1, 0, 0, 0, 236, 234, 1, 0, 0, 0, 236, 237, 1, 0, 0, 0, 237, 35, 1, 0, 0, 0, + 238, 239, 3, 50, 25, 0, 239, 240, 5, 7, 0, 0, 240, 241, 5, 2, 0, 0, 241, 242, 3, 42, 21, 0, 242, + 244, 1, 0, 0, 0, 243, 238, 1, 0, 0, 0, 244, 245, 1, 0, 0, 0, 245, 243, 1, 0, 0, 0, 245, 246, 1, 0, + 0, 0, 246, 37, 1, 0, 0, 0, 247, 248, 3, 40, 20, 0, 248, 249, 3, 50, 25, 0, 249, 39, 1, 0, 0, 0, + 250, 251, 3, 50, 25, 0, 251, 253, 5, 7, 0, 0, 252, 254, 3, 42, 21, 0, 253, 252, 1, 0, 0, 0, 253, + 254, 1, 0, 0, 0, 254, 256, 1, 0, 0, 0, 255, 250, 1, 0, 0, 0, 256, 257, 1, 0, 0, 0, 257, 255, 1, 0, + 0, 0, 257, 258, 1, 0, 0, 0, 258, 41, 1, 0, 0, 0, 259, 262, 3, 46, 23, 0, 260, 262, 5, 5, 0, 0, + 261, 259, 1, 0, 0, 0, 261, 260, 1, 0, 0, 0, 262, 263, 1, 0, 0, 0, 263, 264, 1, 0, 0, 0, 263, 261, + 1, 0, 0, 0, 264, 43, 1, 0, 0, 0, 265, 268, 3, 46, 23, 0, 266, 268, 3, 48, 24, 0, 267, 265, 1, 0, + 0, 0, 267, 266, 1, 0, 0, 0, 268, 269, 1, 0, 0, 0, 269, 270, 1, 0, 0, 0, 269, 267, 1, 0, 0, 0, 270, + 45, 1, 0, 0, 0, 271, 272, 7, 0, 0, 0, 272, 47, 1, 0, 0, 0, 273, 274, 7, 1, 0, 0, 274, 49, 1, 0, 0, + 0, 275, 277, 3, 48, 24, 0, 276, 275, 1, 0, 0, 0, 277, 280, 1, 0, 0, 0, 278, 276, 1, 0, 0, 0, 278, + 279, 1, 0, 0, 0, 279, 51, 1, 0, 0, 0, 280, 278, 1, 0, 0, 0, 35, 60, 64, 70, 74, 80, 85, 91, 95, + 99, 105, 110, 116, 123, 130, 135, 141, 146, 152, 178, 182, 188, 192, 199, 207, 212, 217, 236, 245, + 253, 257, 261, 263, 267, 269, 278 +] + +const atn = new antlr4.atn.ATNDeserializer().deserialize(serializedATN) + +const decisionsToDFA = atn.decisionToState.map((ds, index) => new antlr4.dfa.DFA(ds, index)) + +const sharedContextCache = new antlr4.atn.PredictionContextCache() + +export default class MADRParser extends antlr4.Parser { + static grammarFileName = 'MADR.g4' + static literalNames = [ + null, + "'Good, because '", + "'Bad, because '", + null, + null, + null, + null, + null, + null, + null, + null, + "''", + null, + "'# '" + ] + static symbolicNames = [ + null, + null, + null, + 'WORD', + 'CHARACTER', + 'WS', + 'NEWLINE', + 'LIST_MARKER', + 'STATUS_MARKER', + 'DATE_MARKER', + 'DECIDERS_MARKER', + 'OPTIONAL_MAKER', + 'TECHNICAL_STORY_MARKER', + 'HEADING_PREFIX', + 'SUBSUBHEADING_PREFIX', + 'SUBSUBSUBHEADING_PREFIX', + 'CONTEXT_AND_PROBLEM_STATEMENT', + 'DECISION_DRIVERS_HEADING', + 'CONSIDERED_OPTIONS_HEADING', + 'DECISION_OUTCOME_HEADING', + 'POSITIVE_CONSEQUENCES_HEADING', + 'NEGATIVE_CONSEQUENCES_HEADING', + 'PROS_AND_CONS_OF_THE_OPTIONS_HEADING', + 'LINKS_HEADING' + ] + static ruleNames = [ + 'start', + 'title', + 'status', + 'deciders', + 'date', + 'technicalStory', + 'contextAndProblemStatement', + 'decisionDrivers', + 'consideredOptions', + 'decisionOutcome', + 'prosAndConsOfOptions', + 'optionSection', + 'chosenOptionAndExplanation', + 'positiveConsequences', + 'negativeConsequences', + 'optionTitle', + 'optionDescription', + 'prolist', + 'conlist', + 'links', + 'list', + 'textLine', + 'multilineText', + 'any', + 'wslb', + 'wslbs' + ] + + constructor(input) { + super(input) + this._interp = new antlr4.atn.ParserATNSimulator(this, atn, decisionsToDFA, sharedContextCache) + this.ruleNames = MADRParser.ruleNames + this.literalNames = MADRParser.literalNames + this.symbolicNames = MADRParser.symbolicNames + } + + start() { + const localctx = new StartContext(this, this._ctx, this.state) + this.enterRule(localctx, 0, MADRParser.RULE_start) + let _la = 0 + try { + this.enterOuterAlt(localctx, 1) + this.state = 52 + this.match(MADRParser.HEADING_PREFIX) + this.state = 53 + this.title() + this.state = 54 + this.match(MADRParser.NEWLINE) + this.state = 55 + this.wslbs() + this.state = 64 + this._errHandler.sync(this) + _la = this._input.LA(1) + if (_la === 8) { + this.state = 56 + this.match(MADRParser.STATUS_MARKER) + this.state = 57 + this.status() + this.state = 60 + this._errHandler.sync(this) + var la_ = this._interp.adaptivePredict(this._input, 0, this._ctx) + if (la_ === 1) { + this.state = 58 + this.match(MADRParser.WS) + this.state = 59 + this.match(MADRParser.OPTIONAL_MAKER) + } + this.state = 62 + this.wslbs() + } + + this.state = 74 + this._errHandler.sync(this) + _la = this._input.LA(1) + if (_la === 10) { + this.state = 66 + this.match(MADRParser.DECIDERS_MARKER) + this.state = 67 + this.deciders() + this.state = 70 + this._errHandler.sync(this) + var la_ = this._interp.adaptivePredict(this._input, 2, this._ctx) + if (la_ === 1) { + this.state = 68 + this.match(MADRParser.WS) + this.state = 69 + this.match(MADRParser.OPTIONAL_MAKER) + } + this.state = 72 + this.wslbs() + } + + this.state = 85 + this._errHandler.sync(this) + _la = this._input.LA(1) + if (_la === 9) { + this.state = 76 + this.match(MADRParser.DATE_MARKER) + this.state = 77 + this.date() + this.state = 80 + this._errHandler.sync(this) + _la = this._input.LA(1) + if (_la === 5) { + this.state = 78 + this.match(MADRParser.WS) + this.state = 79 + this.match(MADRParser.OPTIONAL_MAKER) + } + + this.state = 82 + this.match(MADRParser.NEWLINE) + this.state = 83 + this.wslbs() + } + + this.state = 95 + this._errHandler.sync(this) + _la = this._input.LA(1) + if (_la === 12) { + this.state = 87 + this.match(MADRParser.TECHNICAL_STORY_MARKER) + this.state = 88 + this.technicalStory() + this.state = 91 + this._errHandler.sync(this) + var la_ = this._interp.adaptivePredict(this._input, 6, this._ctx) + if (la_ === 1) { + this.state = 89 + this.match(MADRParser.WS) + this.state = 90 + this.match(MADRParser.OPTIONAL_MAKER) + } + this.state = 93 + this.wslbs() + } + + this.state = 99 + this._errHandler.sync(this) + _la = this._input.LA(1) + if (_la === 16) { + this.state = 97 + this.match(MADRParser.CONTEXT_AND_PROBLEM_STATEMENT) + this.state = 98 + this.wslbs() + } + + this.state = 105 + this._errHandler.sync(this) + _la = this._input.LA(1) + if (_la === 6) { + this.state = 101 + this.match(MADRParser.NEWLINE) + this.state = 102 + this.contextAndProblemStatement() + this.state = 103 + this.wslbs() + } + + this.state = 116 + this._errHandler.sync(this) + _la = this._input.LA(1) + if (_la === 17) { + this.state = 107 + this.match(MADRParser.DECISION_DRIVERS_HEADING) + this.state = 110 + this._errHandler.sync(this) + var la_ = this._interp.adaptivePredict(this._input, 10, this._ctx) + if (la_ === 1) { + this.state = 108 + this.match(MADRParser.WS) + this.state = 109 + this.match(MADRParser.OPTIONAL_MAKER) + } + this.state = 112 + this.wslbs() + this.state = 113 + this.decisionDrivers() + this.state = 114 + this.wslbs() + } + + this.state = 123 + this._errHandler.sync(this) + _la = this._input.LA(1) + if (_la === 18) { + this.state = 118 + this.match(MADRParser.CONSIDERED_OPTIONS_HEADING) + this.state = 119 + this.wslbs() + this.state = 120 + this.consideredOptions() + this.state = 121 + this.wslbs() + } + + this.state = 130 + this._errHandler.sync(this) + _la = this._input.LA(1) + if (_la === 19) { + this.state = 125 + this.match(MADRParser.DECISION_OUTCOME_HEADING) + this.state = 126 + this.wslbs() + this.state = 127 + this.decisionOutcome() + this.state = 128 + this.wslbs() + } + + this.state = 141 + this._errHandler.sync(this) + _la = this._input.LA(1) + if (_la === 22) { + this.state = 132 + this.match(MADRParser.PROS_AND_CONS_OF_THE_OPTIONS_HEADING) + this.state = 135 + this._errHandler.sync(this) + var la_ = this._interp.adaptivePredict(this._input, 14, this._ctx) + if (la_ === 1) { + this.state = 133 + this.match(MADRParser.WS) + this.state = 134 + this.match(MADRParser.OPTIONAL_MAKER) + } + this.state = 137 + this.wslbs() + this.state = 138 + this.prosAndConsOfOptions() + this.state = 139 + this.wslbs() + } + + this.state = 152 + this._errHandler.sync(this) + _la = this._input.LA(1) + if (_la === 23) { + this.state = 143 + this.match(MADRParser.LINKS_HEADING) + this.state = 146 + this._errHandler.sync(this) + var la_ = this._interp.adaptivePredict(this._input, 16, this._ctx) + if (la_ === 1) { + this.state = 144 + this.match(MADRParser.WS) + this.state = 145 + this.match(MADRParser.OPTIONAL_MAKER) + } + this.state = 148 + this.wslbs() + this.state = 149 + this.links() + this.state = 150 + this.wslbs() + } + + this.state = 154 + this.match(MADRParser.EOF) + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + title() { + const localctx = new TitleContext(this, this._ctx, this.state) + this.enterRule(localctx, 2, MADRParser.RULE_title) + try { + this.enterOuterAlt(localctx, 1) + this.state = 156 + this.textLine() + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + status() { + const localctx = new StatusContext(this, this._ctx, this.state) + this.enterRule(localctx, 4, MADRParser.RULE_status) + try { + this.enterOuterAlt(localctx, 1) + this.state = 158 + this.textLine() + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + deciders() { + const localctx = new DecidersContext(this, this._ctx, this.state) + this.enterRule(localctx, 6, MADRParser.RULE_deciders) + try { + this.enterOuterAlt(localctx, 1) + this.state = 160 + this.textLine() + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + date() { + const localctx = new DateContext(this, this._ctx, this.state) + this.enterRule(localctx, 8, MADRParser.RULE_date) + try { + this.enterOuterAlt(localctx, 1) + this.state = 162 + this.textLine() + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + technicalStory() { + const localctx = new TechnicalStoryContext(this, this._ctx, this.state) + this.enterRule(localctx, 10, MADRParser.RULE_technicalStory) + try { + this.enterOuterAlt(localctx, 1) + this.state = 164 + this.textLine() + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + contextAndProblemStatement() { + const localctx = new ContextAndProblemStatementContext(this, this._ctx, this.state) + this.enterRule(localctx, 12, MADRParser.RULE_contextAndProblemStatement) + try { + this.enterOuterAlt(localctx, 1) + this.state = 166 + this.multilineText() + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + decisionDrivers() { + const localctx = new DecisionDriversContext(this, this._ctx, this.state) + this.enterRule(localctx, 14, MADRParser.RULE_decisionDrivers) + try { + this.enterOuterAlt(localctx, 1) + this.state = 168 + this.list() + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + consideredOptions() { + const localctx = new ConsideredOptionsContext(this, this._ctx, this.state) + this.enterRule(localctx, 16, MADRParser.RULE_consideredOptions) + try { + this.enterOuterAlt(localctx, 1) + this.state = 170 + this.list() + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + decisionOutcome() { + const localctx = new DecisionOutcomeContext(this, this._ctx, this.state) + this.enterRule(localctx, 18, MADRParser.RULE_decisionOutcome) + try { + this.enterOuterAlt(localctx, 1) + this.state = 172 + this.wslbs() + this.state = 173 + this.chosenOptionAndExplanation() + this.state = 182 + this._errHandler.sync(this) + var la_ = this._interp.adaptivePredict(this._input, 19, this._ctx) + if (la_ === 1) { + this.state = 174 + this.wslbs() + this.state = 175 + this.match(MADRParser.POSITIVE_CONSEQUENCES_HEADING) + this.state = 178 + this._errHandler.sync(this) + var la_ = this._interp.adaptivePredict(this._input, 18, this._ctx) + if (la_ === 1) { + this.state = 176 + this.match(MADRParser.WS) + this.state = 177 + this.match(MADRParser.OPTIONAL_MAKER) + } + this.state = 180 + this.positiveConsequences() + } + this.state = 192 + this._errHandler.sync(this) + var la_ = this._interp.adaptivePredict(this._input, 21, this._ctx) + if (la_ === 1) { + this.state = 184 + this.wslbs() + this.state = 185 + this.match(MADRParser.NEGATIVE_CONSEQUENCES_HEADING) + this.state = 188 + this._errHandler.sync(this) + var la_ = this._interp.adaptivePredict(this._input, 20, this._ctx) + if (la_ === 1) { + this.state = 186 + this.match(MADRParser.WS) + this.state = 187 + this.match(MADRParser.OPTIONAL_MAKER) + } + this.state = 190 + this.negativeConsequences() + } + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + prosAndConsOfOptions() { + const localctx = new ProsAndConsOfOptionsContext(this, this._ctx, this.state) + this.enterRule(localctx, 20, MADRParser.RULE_prosAndConsOfOptions) + let _la = 0 + try { + this.enterOuterAlt(localctx, 1) + this.state = 197 + this._errHandler.sync(this) + _la = this._input.LA(1) + do { + this.state = 194 + this.optionSection() + this.state = 195 + this.wslbs() + this.state = 199 + this._errHandler.sync(this) + _la = this._input.LA(1) + } while (_la === 14) + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + optionSection() { + const localctx = new OptionSectionContext(this, this._ctx, this.state) + this.enterRule(localctx, 22, MADRParser.RULE_optionSection) + try { + this.enterOuterAlt(localctx, 1) + this.state = 201 + this.match(MADRParser.SUBSUBHEADING_PREFIX) + this.state = 202 + this.optionTitle() + this.state = 203 + this.match(MADRParser.NEWLINE) + this.state = 207 + this._errHandler.sync(this) + var la_ = this._interp.adaptivePredict(this._input, 23, this._ctx) + if (la_ === 1) { + this.state = 204 + this.wslbs() + this.state = 205 + this.optionDescription() + } + this.state = 212 + this._errHandler.sync(this) + var la_ = this._interp.adaptivePredict(this._input, 24, this._ctx) + if (la_ === 1) { + this.state = 209 + this.wslbs() + this.state = 210 + this.prolist() + } + this.state = 217 + this._errHandler.sync(this) + var la_ = this._interp.adaptivePredict(this._input, 25, this._ctx) + if (la_ === 1) { + this.state = 214 + this.wslbs() + this.state = 215 + this.conlist() + } + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + chosenOptionAndExplanation() { + const localctx = new ChosenOptionAndExplanationContext(this, this._ctx, this.state) + this.enterRule(localctx, 24, MADRParser.RULE_chosenOptionAndExplanation) + try { + this.enterOuterAlt(localctx, 1) + this.state = 219 + this.multilineText() + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + positiveConsequences() { + const localctx = new PositiveConsequencesContext(this, this._ctx, this.state) + this.enterRule(localctx, 26, MADRParser.RULE_positiveConsequences) + try { + this.enterOuterAlt(localctx, 1) + this.state = 221 + this.list() + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + negativeConsequences() { + const localctx = new NegativeConsequencesContext(this, this._ctx, this.state) + this.enterRule(localctx, 28, MADRParser.RULE_negativeConsequences) + try { + this.enterOuterAlt(localctx, 1) + this.state = 223 + this.list() + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + optionTitle() { + const localctx = new OptionTitleContext(this, this._ctx, this.state) + this.enterRule(localctx, 30, MADRParser.RULE_optionTitle) + try { + this.enterOuterAlt(localctx, 1) + this.state = 225 + this.textLine() + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + optionDescription() { + const localctx = new OptionDescriptionContext(this, this._ctx, this.state) + this.enterRule(localctx, 32, MADRParser.RULE_optionDescription) + try { + this.enterOuterAlt(localctx, 1) + this.state = 227 + this.multilineText() + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + prolist() { + const localctx = new ProlistContext(this, this._ctx, this.state) + this.enterRule(localctx, 34, MADRParser.RULE_prolist) + try { + this.enterOuterAlt(localctx, 1) + this.state = 234 + this._errHandler.sync(this) + let _alt = 1 + do { + switch (_alt) { + case 1: + this.state = 229 + this.wslbs() + this.state = 230 + this.match(MADRParser.LIST_MARKER) + this.state = 231 + this.match(MADRParser.T__0) + this.state = 232 + this.textLine() + break + default: + throw new antlr4.error.NoViableAltException(this) + } + this.state = 236 + this._errHandler.sync(this) + _alt = this._interp.adaptivePredict(this._input, 26, this._ctx) + } while (_alt != 2 && _alt != antlr4.atn.ATN.INVALID_ALT_NUMBER) + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + conlist() { + const localctx = new ConlistContext(this, this._ctx, this.state) + this.enterRule(localctx, 36, MADRParser.RULE_conlist) + try { + this.enterOuterAlt(localctx, 1) + this.state = 243 + this._errHandler.sync(this) + let _alt = 1 + do { + switch (_alt) { + case 1: + this.state = 238 + this.wslbs() + this.state = 239 + this.match(MADRParser.LIST_MARKER) + this.state = 240 + this.match(MADRParser.T__1) + this.state = 241 + this.textLine() + break + default: + throw new antlr4.error.NoViableAltException(this) + } + this.state = 245 + this._errHandler.sync(this) + _alt = this._interp.adaptivePredict(this._input, 27, this._ctx) + } while (_alt != 2 && _alt != antlr4.atn.ATN.INVALID_ALT_NUMBER) + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + links() { + const localctx = new LinksContext(this, this._ctx, this.state) + this.enterRule(localctx, 38, MADRParser.RULE_links) + try { + this.enterOuterAlt(localctx, 1) + this.state = 247 + this.list() + this.state = 248 + this.wslbs() + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + list() { + const localctx = new ListContext(this, this._ctx, this.state) + this.enterRule(localctx, 40, MADRParser.RULE_list) + try { + this.enterOuterAlt(localctx, 1) + this.state = 255 + this._errHandler.sync(this) + let _alt = 1 + do { + switch (_alt) { + case 1: + this.state = 250 + this.wslbs() + this.state = 251 + this.match(MADRParser.LIST_MARKER) + this.state = 253 + this._errHandler.sync(this) + var la_ = this._interp.adaptivePredict(this._input, 28, this._ctx) + if (la_ === 1) { + this.state = 252 + this.textLine() + } + break + default: + throw new antlr4.error.NoViableAltException(this) + } + this.state = 257 + this._errHandler.sync(this) + _alt = this._interp.adaptivePredict(this._input, 29, this._ctx) + } while (_alt != 2 && _alt != antlr4.atn.ATN.INVALID_ALT_NUMBER) + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + textLine() { + const localctx = new TextLineContext(this, this._ctx, this.state) + this.enterRule(localctx, 42, MADRParser.RULE_textLine) + try { + this.enterOuterAlt(localctx, 1) + this.state = 261 + this._errHandler.sync(this) + let _alt = 1 + 1 + do { + switch (_alt) { + case 1 + 1: + this.state = 261 + this._errHandler.sync(this) + switch (this._input.LA(1)) { + case 3: + case 4: + case 7: + case 13: + case 15: + this.state = 259 + this.any() + break + case 5: + this.state = 260 + this.match(MADRParser.WS) + break + default: + throw new antlr4.error.NoViableAltException(this) + } + break + default: + throw new antlr4.error.NoViableAltException(this) + } + this.state = 263 + this._errHandler.sync(this) + _alt = this._interp.adaptivePredict(this._input, 31, this._ctx) + } while (_alt != 1 && _alt != antlr4.atn.ATN.INVALID_ALT_NUMBER) + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + multilineText() { + const localctx = new MultilineTextContext(this, this._ctx, this.state) + this.enterRule(localctx, 44, MADRParser.RULE_multilineText) + try { + this.enterOuterAlt(localctx, 1) + this.state = 267 + this._errHandler.sync(this) + let _alt = 1 + 1 + do { + switch (_alt) { + case 1 + 1: + this.state = 267 + this._errHandler.sync(this) + switch (this._input.LA(1)) { + case 3: + case 4: + case 7: + case 13: + case 15: + this.state = 265 + this.any() + break + case 5: + case 6: + this.state = 266 + this.wslb() + break + default: + throw new antlr4.error.NoViableAltException(this) + } + break + default: + throw new antlr4.error.NoViableAltException(this) + } + this.state = 269 + this._errHandler.sync(this) + _alt = this._interp.adaptivePredict(this._input, 33, this._ctx) + } while (_alt != 1 && _alt != antlr4.atn.ATN.INVALID_ALT_NUMBER) + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + any() { + const localctx = new AnyContext(this, this._ctx, this.state) + this.enterRule(localctx, 46, MADRParser.RULE_any) + let _la = 0 + try { + this.enterOuterAlt(localctx, 1) + this.state = 271 + _la = this._input.LA(1) + if (!((_la & ~0x1f) === 0 && ((1 << _la) & 41112) !== 0)) { + this._errHandler.recoverInline(this) + } else { + this._errHandler.reportMatch(this) + this.consume() + } + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + wslb() { + const localctx = new WslbContext(this, this._ctx, this.state) + this.enterRule(localctx, 48, MADRParser.RULE_wslb) + let _la = 0 + try { + this.enterOuterAlt(localctx, 1) + this.state = 273 + _la = this._input.LA(1) + if (!(_la === 5 || _la === 6)) { + this._errHandler.recoverInline(this) + } else { + this._errHandler.reportMatch(this) + this.consume() + } + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } + + wslbs() { + const localctx = new WslbsContext(this, this._ctx, this.state) + this.enterRule(localctx, 50, MADRParser.RULE_wslbs) + try { + this.enterOuterAlt(localctx, 1) + this.state = 278 + this._errHandler.sync(this) + let _alt = this._interp.adaptivePredict(this._input, 34, this._ctx) + while (_alt != 2 && _alt != antlr4.atn.ATN.INVALID_ALT_NUMBER) { + if (_alt === 1) { + this.state = 275 + this.wslb() + } + this.state = 280 + this._errHandler.sync(this) + _alt = this._interp.adaptivePredict(this._input, 34, this._ctx) + } + } catch (re) { + if (re instanceof antlr4.error.RecognitionException) { + localctx.exception = re + this._errHandler.reportError(this, re) + this._errHandler.recover(this, re) + } else { + throw re + } + } finally { + this.exitRule() + } + return localctx + } +} + +MADRParser.EOF = antlr4.Token.EOF +MADRParser.T__0 = 1 +MADRParser.T__1 = 2 +MADRParser.WORD = 3 +MADRParser.CHARACTER = 4 +MADRParser.WS = 5 +MADRParser.NEWLINE = 6 +MADRParser.LIST_MARKER = 7 +MADRParser.STATUS_MARKER = 8 +MADRParser.DATE_MARKER = 9 +MADRParser.DECIDERS_MARKER = 10 +MADRParser.OPTIONAL_MAKER = 11 +MADRParser.TECHNICAL_STORY_MARKER = 12 +MADRParser.HEADING_PREFIX = 13 +MADRParser.SUBSUBHEADING_PREFIX = 14 +MADRParser.SUBSUBSUBHEADING_PREFIX = 15 +MADRParser.CONTEXT_AND_PROBLEM_STATEMENT = 16 +MADRParser.DECISION_DRIVERS_HEADING = 17 +MADRParser.CONSIDERED_OPTIONS_HEADING = 18 +MADRParser.DECISION_OUTCOME_HEADING = 19 +MADRParser.POSITIVE_CONSEQUENCES_HEADING = 20 +MADRParser.NEGATIVE_CONSEQUENCES_HEADING = 21 +MADRParser.PROS_AND_CONS_OF_THE_OPTIONS_HEADING = 22 +MADRParser.LINKS_HEADING = 23 + +MADRParser.RULE_start = 0 +MADRParser.RULE_title = 1 +MADRParser.RULE_status = 2 +MADRParser.RULE_deciders = 3 +MADRParser.RULE_date = 4 +MADRParser.RULE_technicalStory = 5 +MADRParser.RULE_contextAndProblemStatement = 6 +MADRParser.RULE_decisionDrivers = 7 +MADRParser.RULE_consideredOptions = 8 +MADRParser.RULE_decisionOutcome = 9 +MADRParser.RULE_prosAndConsOfOptions = 10 +MADRParser.RULE_optionSection = 11 +MADRParser.RULE_chosenOptionAndExplanation = 12 +MADRParser.RULE_positiveConsequences = 13 +MADRParser.RULE_negativeConsequences = 14 +MADRParser.RULE_optionTitle = 15 +MADRParser.RULE_optionDescription = 16 +MADRParser.RULE_prolist = 17 +MADRParser.RULE_conlist = 18 +MADRParser.RULE_links = 19 +MADRParser.RULE_list = 20 +MADRParser.RULE_textLine = 21 +MADRParser.RULE_multilineText = 22 +MADRParser.RULE_any = 23 +MADRParser.RULE_wslb = 24 +MADRParser.RULE_wslbs = 25 + +class StartContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_start + } + + HEADING_PREFIX() { + return this.getToken(MADRParser.HEADING_PREFIX, 0) + } + + title() { + return this.getTypedRuleContext(TitleContext, 0) + } + + NEWLINE = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTokens(MADRParser.NEWLINE) + } else { + return this.getToken(MADRParser.NEWLINE, i) + } + } + + wslbs = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTypedRuleContexts(WslbsContext) + } else { + return this.getTypedRuleContext(WslbsContext, i) + } + } + + EOF() { + return this.getToken(MADRParser.EOF, 0) + } + + STATUS_MARKER() { + return this.getToken(MADRParser.STATUS_MARKER, 0) + } + + status() { + return this.getTypedRuleContext(StatusContext, 0) + } + + DECIDERS_MARKER() { + return this.getToken(MADRParser.DECIDERS_MARKER, 0) + } + + deciders() { + return this.getTypedRuleContext(DecidersContext, 0) + } + + DATE_MARKER() { + return this.getToken(MADRParser.DATE_MARKER, 0) + } + + date() { + return this.getTypedRuleContext(DateContext, 0) + } + + TECHNICAL_STORY_MARKER() { + return this.getToken(MADRParser.TECHNICAL_STORY_MARKER, 0) + } + + technicalStory() { + return this.getTypedRuleContext(TechnicalStoryContext, 0) + } + + CONTEXT_AND_PROBLEM_STATEMENT() { + return this.getToken(MADRParser.CONTEXT_AND_PROBLEM_STATEMENT, 0) + } + + contextAndProblemStatement() { + return this.getTypedRuleContext(ContextAndProblemStatementContext, 0) + } + + DECISION_DRIVERS_HEADING() { + return this.getToken(MADRParser.DECISION_DRIVERS_HEADING, 0) + } + + decisionDrivers() { + return this.getTypedRuleContext(DecisionDriversContext, 0) + } + + CONSIDERED_OPTIONS_HEADING() { + return this.getToken(MADRParser.CONSIDERED_OPTIONS_HEADING, 0) + } + + consideredOptions() { + return this.getTypedRuleContext(ConsideredOptionsContext, 0) + } + + DECISION_OUTCOME_HEADING() { + return this.getToken(MADRParser.DECISION_OUTCOME_HEADING, 0) + } + + decisionOutcome() { + return this.getTypedRuleContext(DecisionOutcomeContext, 0) + } + + PROS_AND_CONS_OF_THE_OPTIONS_HEADING() { + return this.getToken(MADRParser.PROS_AND_CONS_OF_THE_OPTIONS_HEADING, 0) + } + + prosAndConsOfOptions() { + return this.getTypedRuleContext(ProsAndConsOfOptionsContext, 0) + } + + LINKS_HEADING() { + return this.getToken(MADRParser.LINKS_HEADING, 0) + } + + links() { + return this.getTypedRuleContext(LinksContext, 0) + } + + WS = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTokens(MADRParser.WS) + } else { + return this.getToken(MADRParser.WS, i) + } + } + + OPTIONAL_MAKER = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTokens(MADRParser.OPTIONAL_MAKER) + } else { + return this.getToken(MADRParser.OPTIONAL_MAKER, i) + } + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterStart(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitStart(this) + } + } +} + +class TitleContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_title + } + + textLine() { + return this.getTypedRuleContext(TextLineContext, 0) + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterTitle(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitTitle(this) + } + } +} + +class StatusContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_status + } + + textLine() { + return this.getTypedRuleContext(TextLineContext, 0) + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterStatus(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitStatus(this) + } + } +} + +class DecidersContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_deciders + } + + textLine() { + return this.getTypedRuleContext(TextLineContext, 0) + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterDeciders(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitDeciders(this) + } + } +} + +class DateContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_date + } + + textLine() { + return this.getTypedRuleContext(TextLineContext, 0) + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterDate(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitDate(this) + } + } +} + +class TechnicalStoryContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_technicalStory + } + + textLine() { + return this.getTypedRuleContext(TextLineContext, 0) + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterTechnicalStory(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitTechnicalStory(this) + } + } +} + +class ContextAndProblemStatementContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_contextAndProblemStatement + } + + multilineText() { + return this.getTypedRuleContext(MultilineTextContext, 0) + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterContextAndProblemStatement(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitContextAndProblemStatement(this) + } + } +} + +class DecisionDriversContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_decisionDrivers + } + + list() { + return this.getTypedRuleContext(ListContext, 0) + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterDecisionDrivers(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitDecisionDrivers(this) + } + } +} + +class ConsideredOptionsContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_consideredOptions + } + + list() { + return this.getTypedRuleContext(ListContext, 0) + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterConsideredOptions(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitConsideredOptions(this) + } + } +} + +class DecisionOutcomeContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_decisionOutcome + } + + wslbs = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTypedRuleContexts(WslbsContext) + } else { + return this.getTypedRuleContext(WslbsContext, i) + } + } + + chosenOptionAndExplanation() { + return this.getTypedRuleContext(ChosenOptionAndExplanationContext, 0) + } + + POSITIVE_CONSEQUENCES_HEADING() { + return this.getToken(MADRParser.POSITIVE_CONSEQUENCES_HEADING, 0) + } + + positiveConsequences() { + return this.getTypedRuleContext(PositiveConsequencesContext, 0) + } + + NEGATIVE_CONSEQUENCES_HEADING() { + return this.getToken(MADRParser.NEGATIVE_CONSEQUENCES_HEADING, 0) + } + + negativeConsequences() { + return this.getTypedRuleContext(NegativeConsequencesContext, 0) + } + + WS = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTokens(MADRParser.WS) + } else { + return this.getToken(MADRParser.WS, i) + } + } + + OPTIONAL_MAKER = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTokens(MADRParser.OPTIONAL_MAKER) + } else { + return this.getToken(MADRParser.OPTIONAL_MAKER, i) + } + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterDecisionOutcome(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitDecisionOutcome(this) + } + } +} + +class ProsAndConsOfOptionsContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_prosAndConsOfOptions + } + + optionSection = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTypedRuleContexts(OptionSectionContext) + } else { + return this.getTypedRuleContext(OptionSectionContext, i) + } + } + + wslbs = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTypedRuleContexts(WslbsContext) + } else { + return this.getTypedRuleContext(WslbsContext, i) + } + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterProsAndConsOfOptions(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitProsAndConsOfOptions(this) + } + } +} + +class OptionSectionContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_optionSection + } + + SUBSUBHEADING_PREFIX() { + return this.getToken(MADRParser.SUBSUBHEADING_PREFIX, 0) + } + + optionTitle() { + return this.getTypedRuleContext(OptionTitleContext, 0) + } + + NEWLINE() { + return this.getToken(MADRParser.NEWLINE, 0) + } + + wslbs = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTypedRuleContexts(WslbsContext) + } else { + return this.getTypedRuleContext(WslbsContext, i) + } + } + + optionDescription() { + return this.getTypedRuleContext(OptionDescriptionContext, 0) + } + + prolist() { + return this.getTypedRuleContext(ProlistContext, 0) + } + + conlist() { + return this.getTypedRuleContext(ConlistContext, 0) + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterOptionSection(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitOptionSection(this) + } + } +} + +class ChosenOptionAndExplanationContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_chosenOptionAndExplanation + } + + multilineText() { + return this.getTypedRuleContext(MultilineTextContext, 0) + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterChosenOptionAndExplanation(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitChosenOptionAndExplanation(this) + } + } +} + +class PositiveConsequencesContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_positiveConsequences + } + + list() { + return this.getTypedRuleContext(ListContext, 0) + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterPositiveConsequences(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitPositiveConsequences(this) + } + } +} + +class NegativeConsequencesContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_negativeConsequences + } + + list() { + return this.getTypedRuleContext(ListContext, 0) + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterNegativeConsequences(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitNegativeConsequences(this) + } + } +} + +class OptionTitleContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_optionTitle + } + + textLine() { + return this.getTypedRuleContext(TextLineContext, 0) + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterOptionTitle(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitOptionTitle(this) + } + } +} + +class OptionDescriptionContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_optionDescription + } + + multilineText() { + return this.getTypedRuleContext(MultilineTextContext, 0) + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterOptionDescription(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitOptionDescription(this) + } + } +} + +class ProlistContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_prolist + } + + wslbs = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTypedRuleContexts(WslbsContext) + } else { + return this.getTypedRuleContext(WslbsContext, i) + } + } + + LIST_MARKER = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTokens(MADRParser.LIST_MARKER) + } else { + return this.getToken(MADRParser.LIST_MARKER, i) + } + } + + textLine = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTypedRuleContexts(TextLineContext) + } else { + return this.getTypedRuleContext(TextLineContext, i) + } + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterProlist(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitProlist(this) + } + } +} + +class ConlistContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_conlist + } + + wslbs = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTypedRuleContexts(WslbsContext) + } else { + return this.getTypedRuleContext(WslbsContext, i) + } + } + + LIST_MARKER = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTokens(MADRParser.LIST_MARKER) + } else { + return this.getToken(MADRParser.LIST_MARKER, i) + } + } + + textLine = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTypedRuleContexts(TextLineContext) + } else { + return this.getTypedRuleContext(TextLineContext, i) + } + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterConlist(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitConlist(this) + } + } +} + +class LinksContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_links + } + + list() { + return this.getTypedRuleContext(ListContext, 0) + } + + wslbs() { + return this.getTypedRuleContext(WslbsContext, 0) + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterLinks(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitLinks(this) + } + } +} + +class ListContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_list + } + + wslbs = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTypedRuleContexts(WslbsContext) + } else { + return this.getTypedRuleContext(WslbsContext, i) + } + } + + LIST_MARKER = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTokens(MADRParser.LIST_MARKER) + } else { + return this.getToken(MADRParser.LIST_MARKER, i) + } + } + + textLine = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTypedRuleContexts(TextLineContext) + } else { + return this.getTypedRuleContext(TextLineContext, i) + } + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterList(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitList(this) + } + } +} + +class TextLineContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_textLine + } + + any = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTypedRuleContexts(AnyContext) + } else { + return this.getTypedRuleContext(AnyContext, i) + } + } + + WS = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTokens(MADRParser.WS) + } else { + return this.getToken(MADRParser.WS, i) + } + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterTextLine(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitTextLine(this) + } + } +} + +class MultilineTextContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_multilineText + } + + any = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTypedRuleContexts(AnyContext) + } else { + return this.getTypedRuleContext(AnyContext, i) + } + } + + wslb = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTypedRuleContexts(WslbContext) + } else { + return this.getTypedRuleContext(WslbContext, i) + } + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterMultilineText(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitMultilineText(this) + } + } +} + +class AnyContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_any + } + + WORD() { + return this.getToken(MADRParser.WORD, 0) + } + + CHARACTER() { + return this.getToken(MADRParser.CHARACTER, 0) + } + + LIST_MARKER() { + return this.getToken(MADRParser.LIST_MARKER, 0) + } + + HEADING_PREFIX() { + return this.getToken(MADRParser.HEADING_PREFIX, 0) + } + + SUBSUBSUBHEADING_PREFIX() { + return this.getToken(MADRParser.SUBSUBSUBHEADING_PREFIX, 0) + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterAny(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitAny(this) + } + } +} + +class WslbContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_wslb + } + + WS() { + return this.getToken(MADRParser.WS, 0) + } + + NEWLINE() { + return this.getToken(MADRParser.NEWLINE, 0) + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterWslb(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitWslb(this) + } + } +} + +class WslbsContext extends antlr4.ParserRuleContext { + constructor(parser, parent, invokingState) { + if (parent === undefined) { + parent = null + } + if (invokingState === undefined || invokingState === null) { + invokingState = -1 + } + super(parent, invokingState) + this.parser = parser + this.ruleIndex = MADRParser.RULE_wslbs + } + + wslb = function (i) { + if (i === undefined) { + i = null + } + if (i === null) { + return this.getTypedRuleContexts(WslbContext) + } else { + return this.getTypedRuleContext(WslbContext, i) + } + } + + enterRule(listener) { + if (listener instanceof MADRListener) { + listener.enterWslbs(this) + } + } + + exitRule(listener) { + if (listener instanceof MADRListener) { + listener.exitWslbs(this) + } + } +} + +MADRParser.StartContext = StartContext +MADRParser.TitleContext = TitleContext +MADRParser.StatusContext = StatusContext +MADRParser.DecidersContext = DecidersContext +MADRParser.DateContext = DateContext +MADRParser.TechnicalStoryContext = TechnicalStoryContext +MADRParser.ContextAndProblemStatementContext = ContextAndProblemStatementContext +MADRParser.DecisionDriversContext = DecisionDriversContext +MADRParser.ConsideredOptionsContext = ConsideredOptionsContext +MADRParser.DecisionOutcomeContext = DecisionOutcomeContext +MADRParser.ProsAndConsOfOptionsContext = ProsAndConsOfOptionsContext +MADRParser.OptionSectionContext = OptionSectionContext +MADRParser.ChosenOptionAndExplanationContext = ChosenOptionAndExplanationContext +MADRParser.PositiveConsequencesContext = PositiveConsequencesContext +MADRParser.NegativeConsequencesContext = NegativeConsequencesContext +MADRParser.OptionTitleContext = OptionTitleContext +MADRParser.OptionDescriptionContext = OptionDescriptionContext +MADRParser.ProlistContext = ProlistContext +MADRParser.ConlistContext = ConlistContext +MADRParser.LinksContext = LinksContext +MADRParser.ListContext = ListContext +MADRParser.TextLineContext = TextLineContext +MADRParser.MultilineTextContext = MultilineTextContext +MADRParser.AnyContext = AnyContext +MADRParser.WslbContext = WslbContext +MADRParser.WslbsContext = WslbsContext diff --git a/src/infrastructure/service/parser/README.md b/src/infrastructure/service/parser/README.md new file mode 100644 index 0000000..1b97912 --- /dev/null +++ b/src/infrastructure/service/parser/README.md @@ -0,0 +1,17 @@ +# Parser with Antlr4 + +- The required version of Antlr is 4.9. +- Installation guides of Antlr4: + + - Windows: + - UNIX: + +- Explanation for using Antlr4 with JavaScript: + - The shell command to build the parser is as follows: `antlr4 -Dlanguage=JavaScript MADR.g4`. + This should create the files `MADRLexer.js`, `MADRListener.js`, and `MADRParser.js`. + +## Windows + +1. Install Java +2. Download the `jar` from +3. `java -jar c:\Users\{username}\Downloads\antlr-4.9.2-complete.jar -Dlanguage=JavaScript MADR.g4` diff --git a/src/infrastructure/service/parser/classes.ts b/src/infrastructure/service/parser/classes.ts new file mode 100644 index 0000000..294a7c7 --- /dev/null +++ b/src/infrastructure/service/parser/classes.ts @@ -0,0 +1,294 @@ +/** + * This models a single MADR. A MADR is parsed using `MADR.g4` and parser.js + */ +export class ArchitecturalDecisionRecord { + constructor({ + title, + status, + deciders, + date, + technicalStory, + contextAndProblemStatement, + decisionDrivers, + consideredOptions, + decisionOutcome, + links + } = {}) { + this.title = title || '' + this.status = status || '' + this.deciders = deciders || '' + this.date = date || '' + this.technicalStory = technicalStory || '' + this.contextAndProblemStatement = contextAndProblemStatement || '' + this.decisionDrivers = decisionDrivers || [] + this.highestOptionId = 0 + this.consideredOptions = [] + if (consideredOptions && consideredOptions.length > 0) { + for (let i = 0; i < consideredOptions.length; i++) { + this.addOption(consideredOptions[i]) + } + } + this.decisionOutcome = decisionOutcome || { + chosenOption: '', + explanation: '', + positiveConsequences: [], + negativeConsequences: [] + } + this.links = links || [] + + // Assure invariants for decisionOutcome attribute + if (!Object.prototype.hasOwnProperty.call(this.decisionOutcome, 'chosenOption')) { + this.decisionOutcome.decisionOutcome = '' + } + if (!Object.prototype.hasOwnProperty.call(this.decisionOutcome, 'explanation')) { + this.decisionOutcome.explanation = '' + } + if (!Object.prototype.hasOwnProperty.call(this.decisionOutcome, 'positiveConsequences')) { + this.decisionOutcome.positiveConsequences = [] + } + if (!Object.prototype.hasOwnProperty.call(this.decisionOutcome, 'negativeConsequences')) { + this.decisionOutcome.negativeConsequences = [] + } + + this.cleanUp() + } + + /** + * Creates, adds and returns a new option. + * + * @param {{title: string, description: string, pros: string[], cons: string[]}} + * optionData - an object with optional attributes title, description, pros, cons + */ + addOption({ title, description, pros, cons } = {}) { + const id = this.highestOptionId + this.highestOptionId = this.highestOptionId + 1 + const newOpt = { + title: title || '', + description: description || '', + pros: pros || [], + cons: cons || [], + id: id // needed as key/id (for referencing an option or as key in v-for or drag'n'drop) + } + this.consideredOptions.push(newOpt) + return newOpt + } + getOptionByTitle(title) { + return this.consideredOptions.find((el) => { + return el.title.startsWith(title) + }) + } + + /** + * Cleans up the ADR: + * - Asserts that all string attributes contain a string value. + * - Trims all strings. + */ + cleanUp() { + const stringFieldNames = [ + 'title', + 'status', + 'date', + 'deciders', + 'technicalStory', + 'contextAndProblemStatement' + ] + + stringFieldNames.forEach((attr) => { + this[attr] = cleanUpString(this[attr]) + }) + + this.decisionDrivers.forEach((el, idx) => { + this.decisionDrivers[idx] = cleanUpString(el) + }) + this.decisionDrivers = this.decisionDrivers.filter((el) => el !== '') + + this.consideredOptions.forEach((opt) => { + opt.title = cleanUpString(opt.title) + opt.description = cleanUpString(opt.description) + opt.pros.forEach((el, idx) => { + opt.pros[idx] = cleanUpString(el) + }) + opt.pros = opt.pros.filter((el) => el !== '') + opt.cons.forEach((el, idx) => { + opt.cons[idx] = cleanUpString(el) + }) + opt.cons = opt.cons.filter((el) => el !== '') + }) + + this.decisionOutcome.chosenOption = cleanUpString(this.decisionOutcome.chosenOption) + this.decisionOutcome.explanation = cleanUpString(this.decisionOutcome.explanation) + this.decisionOutcome.positiveConsequences.forEach((el, idx) => { + this.decisionOutcome.positiveConsequences[idx] = cleanUpString(el) + }) + this.decisionOutcome.positiveConsequences.forEach((el, idx) => { + this.decisionOutcome.positiveConsequences[idx] = cleanUpString(el) + }) + + this.links.forEach((el, idx) => { + this.links[idx] = cleanUpString(el) + }) + this.links.filter((el) => el !== '') + } + + /** + * Creates a new ADR with default values already set. + */ + static createNewAdr() { + return new ArchitecturalDecisionRecord({ + status: 'proposed', + date: new Date().toISOString().substr(0, 10), + decisionOutcome: { + explanation: 'comes out best.' + } + }) + } +} + +/** + * Helper function for clean up. + * If a string is passed, it is trimmed. Otherwise, an empty string is returned. + * @param {string|undefined|null} string + * @returns {string} the trimmed string or an empty string + */ +function cleanUpString(string) { + if (typeof string === 'string') { + return string.trim() + } else { + return '' + } +} + +export class Repository { + constructor({ fullName, activeBranch, branches, adrs, adrPath }) { + if (adrs && adrs.length > 0 && !adrPath) { + console.warn('There are ADRs but no adr path is given. ADRs:', adrs, ' ADR path:', adrPath) + } + this.fullName = fullName || '' + this.activeBranch = activeBranch || '' + this.branches = branches || [] + this.adrs = adrs || [] + this.adrPath = adrPath || 'docs/decisions/' + this.addedAdrs = [] + this.deletedAdrs = [] + } + + /** + * Constructs a repository object from a string. + * Useful when loading repositories from LocalStorage + * + * @param {string} json + */ + static constructFromString(json) { + const repoData = JSON.parse(json) + const repo = new Repository(repoData) + repoData.addedAdrs.forEach((adr) => { + const equalAdr = repoData.adrs.find( + (el) => + el.path === adr.path && el.editedMd === adr.editedMd && el.originalMd === adr.originalMd + ) + if (equalAdr) { + repo.addedAdrs.push(equalAdr) + } else { + throw "There was an added adr in the parameter string that didn't match!" + } + }) + return repo + } + + /**Returns the changed files in the repository. + * + * @returns {{ added: ADR[], changed: ADR[], deleted: ADR[] }} the changed ADRs + */ + getChanges() { + return { + added: this.addedAdrs, + changed: this.adrs.filter( + (adr) => adr.originalMd !== adr.editedMd && !this.addedAdrs.includes(adr) + ), + deleted: this.deletedAdrs + } + } + + /**Returns true iff the repository contains changed ADRs. + * + * @param {string} repoFullName + * @returns {boolean} true, if the ADRs in the repository changed, else false. + */ + hasChanges() { + const changes = this.getChanges() + return ( + changes.changed.length !== 0 || changes.added.length !== 0 || changes.deleted.length !== 0 + ) + } + + addAdr(newAdr) { + this.adrs.push(newAdr) + this.addedAdrs.push(newAdr) + } +} + +export function createShortTitle(title) { + if (!title) { + return '' + } + let result = title + + // Strip off short description text in the title + // Example: + // In: [MADR](https://adr.github.io/madr/) 2.1.2 – The Markdown Architectural Decision Records + // Out: [MADR](https://adr.github.io/madr/) 2.1.2 + let idx = title.indexOf(' - ') + if (idx > 0) { + result = title.substr(0, idx) + } else { + idx = title.indexOf(' – ') + if (idx > 0) { + result = title.substr(0, idx) + } else { + idx = title.indexOf(' | ') + if (idx > 0) { + result = title.substr(0, idx) + } else { + idx = title.indexOf(', e.g.') + if (idx > 0) { + result = title.substr(0, idx) + } else { + // Handle case "Add `* Category: CATEGORY` directly under the heading (similar to https://gist.github.com/FaKeller/2f9c63b6e1d436abb7358b68bf396f57)" + // --> content of braces should be removed for short title + idx = title.indexOf(' (') + const idxClosing = title.indexOf(')') + if (idx > 0 && idxClosing == title.length - 1 && (idx = title.lastIndexOf(' ('))) { + result = title.substr(0, idx) + } + } + } + } + } + + // Strip out markdown link + // Example: + // In: [MADR](https://adr.github.io/madr/) 2.1.2 + // Out: MADR 2.1.2 + // Example 2: + // In: Include in [adr-tools](https://github.com/npryce/adr-tools) + // Out: Include in adr-tools + // Quick solution; better: Use RegEx or ANTLR + const idxOpeningBracket = result.indexOf('[') + const idxClosingBracket = result.indexOf(']') + const idxOpeningRoundedBracket = result.indexOf('(') + const idxClosingRoundedBracket = result.indexOf(')') + if ( + idxOpeningBracket >= 0 && + idxOpeningBracket < idxClosingBracket && + idxOpeningRoundedBracket == idxClosingBracket + 1 && + idxClosingRoundedBracket > idxOpeningRoundedBracket + ) { + result = + (idxOpeningBracket > 0 ? result.substr(0, idxOpeningBracket) : '') + + result.substr(idxOpeningBracket + 1, idxClosingBracket - idxOpeningBracket - 1) + + (result.length > idxClosingRoundedBracket + 1 + ? result.substr(idxClosingRoundedBracket + 1) + : '') + } + return result +} diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json new file mode 100644 index 0000000..0eb6a3c --- /dev/null +++ b/tsconfig.eslint.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "moduleResolution": "node", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true + } +}