@sap/eslint-plugin-cds 2.4.1 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +31 -2
  2. package/README.md +2 -1
  3. package/lib/api/index.js +13 -12
  4. package/lib/conf/all.js +22 -0
  5. package/lib/conf/index.js +22 -0
  6. package/lib/conf/recommended.js +19 -0
  7. package/lib/constants.js +19 -16
  8. package/lib/index.js +11 -33
  9. package/lib/parser.js +169 -10
  10. package/lib/rules/assoc2many-ambiguous-key.js +80 -96
  11. package/lib/rules/auth-no-empty-restrictions.js +23 -30
  12. package/lib/rules/auth-use-requires.js +25 -24
  13. package/lib/rules/auth-valid-restrict-grant.js +87 -49
  14. package/lib/rules/auth-valid-restrict-keys.js +30 -23
  15. package/lib/rules/auth-valid-restrict-to.js +97 -52
  16. package/lib/rules/auth-valid-restrict-where.js +52 -42
  17. package/lib/rules/extension-restrictions.js +69 -0
  18. package/lib/rules/index.js +27 -0
  19. package/lib/rules/latest-cds-version.js +23 -21
  20. package/lib/rules/min-node-version.js +25 -24
  21. package/lib/rules/no-db-keywords.js +23 -31
  22. package/lib/rules/no-dollar-prefixed-names.js +17 -14
  23. package/lib/rules/no-join-on-draft.js +27 -0
  24. package/lib/rules/require-2many-oncond.js +11 -16
  25. package/lib/rules/sql-cast-suggestion.js +16 -29
  26. package/lib/rules/start-elements-lowercase.js +42 -44
  27. package/lib/rules/start-entities-uppercase.js +29 -31
  28. package/lib/rules/valid-csv-header.js +65 -64
  29. package/lib/{api/lint.d.ts → types.d.ts} +5 -7
  30. package/lib/utils/Cache.js +33 -0
  31. package/lib/utils/Colors.js +9 -0
  32. package/lib/utils/createRule.js +317 -0
  33. package/lib/utils/findFuzzy.js +87 -0
  34. package/lib/utils/genDocs.js +345 -0
  35. package/lib/utils/getConfigPath.js +33 -0
  36. package/lib/utils/getConfiguredFileTypes.js +10 -0
  37. package/lib/utils/getFileExtensions.js +8 -0
  38. package/lib/utils/getProjectRootPath.js +25 -0
  39. package/lib/utils/isConfiguredFileType.js +20 -0
  40. package/lib/utils/rules.js +112 -1041
  41. package/lib/utils/runRuleTester.js +116 -0
  42. package/package.json +10 -4
  43. package/lib/processor.js +0 -50
  44. package/lib/rules/no-join-on-draft-enabled-entities.js +0 -40
  45. package/lib/utils/fuzzySearch.js +0 -94
  46. package/lib/utils/helpers.js +0 -94
  47. package/lib/utils/jsonc.js +0 -1
  48. package/lib/utils/model.js +0 -393
  49. package/lib/utils/ruleHelpers.js +0 -199
  50. package/lib/utils/ruleTester.js +0 -78
  51. package/lib/utils/validate.js +0 -36
@@ -0,0 +1,116 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+
4
+ const { RuleTester } = require('eslint')
5
+ const Cache = require('./Cache')
6
+ const createRule = require('./createRule')
7
+ const isConfiguredFileType = require('./isConfiguredFileType')
8
+ const { compileModelFromDict } = require('../parser')
9
+
10
+ /**
11
+ * ESLint RuleTester (used by custom rule creator api)
12
+ * Calls ESLint's RuleTester with custom cds parser and input for
13
+ * valid/invalid checks:
14
+ * Model checks require input 'code' entries
15
+ * Env checks require input 'options' with selected parameters
16
+ * @param { CDSRuleTestOpts } options RuleTester input options
17
+ * @returns RuleTester results
18
+ */
19
+ module.exports = (options) => {
20
+ let parser
21
+ let rule = {}
22
+ Cache.set('rules', require(path.join(__dirname, '../rules')))
23
+ const rulename = path.basename(options.root)
24
+ if (options.root.startsWith(path.resolve(__dirname, '../..'))) {
25
+ // For plugin's internal tests, resolve parser from here
26
+ parser = require.resolve('../parser')
27
+ const pluginPath = path.join(path.dirname(options.root), '../..')
28
+ rule = createRule(require(`../rules/${path.basename(options.root)}`))
29
+ Cache.set('pluginpath', pluginPath)
30
+ } else {
31
+ // Otherwise from project root
32
+ const resolvedPlugin = require.resolve('@sap/eslint-plugin-cds', {
33
+ paths: [options.root]
34
+ })
35
+ parser = path.join(path.dirname(resolvedPlugin), 'parser')
36
+ rule = require(path.join(options.root, `../../rules/${path.basename(options.root)}`))
37
+ const pluginPath = path.join(path.dirname(options.root), '../../..')
38
+ Cache.set('pluginpath', pluginPath)
39
+ }
40
+ let tester = new RuleTester({})
41
+ if (parser) {
42
+ tester = new RuleTester({ parser })
43
+ }
44
+ const testerCases = {};
45
+ ['valid', 'invalid'].forEach((type) => {
46
+ const filePath = path.join(options.root, `${type}/${options.filename}`)
47
+ Cache.set('rootpath', path.dirname(filePath))
48
+ _initModelRuleTester(filePath, rule.meta?.model)
49
+ testerCases[type] = [
50
+ {
51
+ filename: filePath
52
+ }
53
+ ]
54
+ if (!isConfiguredFileType(options.filename, 'FILES')) {
55
+ const fileContents = JSON.parse(fs.readFileSync(filePath, 'utf8'))
56
+ testerCases[type][0].code = ''
57
+ testerCases[type][0].filename = '<text>'
58
+ testerCases[type][0].options = [{ environment: fileContents }]
59
+ } else {
60
+ testerCases[type][0].code = fs.readFileSync(filePath, 'utf8')
61
+ if (options.options) {
62
+ testerCases[type][0].options = options.options
63
+ }
64
+ }
65
+ if (type === 'invalid') {
66
+ testerCases[type][0].errors = options.errors
67
+ const fileFixed = path.join(options.root, `fixed/${options.filename}`)
68
+ if (fs.existsSync(fileFixed) && rule.meta.type !== 'suggestion') {
69
+ testerCases[type][0].output = fs.readFileSync(fileFixed, 'utf8')
70
+ }
71
+ }
72
+ })
73
+ if (Cache.get('testerCases')) {
74
+ Cache.set(`testerCases:${rulename}`, testerCases)
75
+ }
76
+ return tester.run(rulename, rule, testerCases)
77
+ }
78
+
79
+ /**
80
+ * Creates a model for ESLint unit tests
81
+ */
82
+ function _initModelRuleTester (filePath, flavor) {
83
+ Cache.set('test', true)
84
+ const rootPath = path.dirname(filePath)
85
+ Cache.set('rootpath', rootPath)
86
+ if (flavor !== 'none') { // not for env rules
87
+ const files = fs.readdirSync(rootPath)
88
+ const modelfiles = files.map((f) => path.join(rootPath, f)).filter((fp) => isConfiguredFileType(fp, 'MODEL_FILES'))
89
+ Cache.set(`modelfiles:${rootPath}`, modelfiles)
90
+ const dictFiles = _getDictFiles(rootPath, modelfiles)
91
+ Cache.set(`dictfiles:${rootPath}`, dictFiles)
92
+ const reflectedModel = compileModelFromDict(dictFiles, { flavor })
93
+ Cache.set(`model:${rootPath}`, reflectedModel)
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Creates or updates a dictionary of files/file contents for a given
99
+ * project path.
100
+ * @param input
101
+ * @param files
102
+ * @returns dictFiles
103
+ */
104
+ function _getDictFiles (input, files) {
105
+ let dictFiles = {}
106
+ if (Cache.has(`dictfiles:${input}`)) {
107
+ dictFiles = Cache.get(`dictfiles:${input}`)
108
+ } else {
109
+ files.forEach((file) => {
110
+ dictFiles[file] = Cache.has(`file:${file}`)
111
+ ? Cache.get(`file:${file}`)
112
+ : fs.readFileSync(file, 'utf8')
113
+ })
114
+ }
115
+ return dictFiles
116
+ }
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@sap/eslint-plugin-cds",
3
- "version": "2.4.1",
3
+ "version": "2.6.0",
4
4
  "description": "ESLint plugin including recommended SAP Cloud Application Programming model and environment rules",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [
7
7
  "eslint",
8
- "eslintplugin",
9
- "typescript",
8
+ "eslint-plugin",
10
9
  "cds",
11
- "cds-lint"
10
+ "cds-lint",
11
+ "cds-lint-plugin"
12
12
  ],
13
13
  "author": "SAP SE (https://www.sap.com)",
14
14
  "license": "See LICENSE file",
@@ -23,6 +23,12 @@
23
23
  "@sap/cds": ">=5.6.0",
24
24
  "semver": "^7.3.4"
25
25
  },
26
+ "eslintConfig": {
27
+ "extends": [
28
+ "eslint:recommended",
29
+ "standard"
30
+ ]
31
+ },
26
32
  "peerDependencies": {
27
33
  "eslint": ">=7"
28
34
  },
package/lib/processor.js DELETED
@@ -1,50 +0,0 @@
1
- /**
2
- * Custom ESLint processor:
3
- * https://eslint.org/docs/developer-guide/working-with-plugins#processors-in-plugins
4
- * This processor is used to avoid parsing errors when this plugin is extended
5
- * in ESLint alongside other plugins, such as prettier which then also try to
6
- * read the new file types exposed via globs.
7
- *
8
- * Note, that because we cache the file contents and return files contents,
9
- * the plugin's parser is bypassed so we must retrieve the file contents (see createRule()
10
- * in utils/rules.js).
11
- */
12
-
13
- const { Cache } = require("./utils/model");
14
- const { isValidFile } = require("./utils/helpers");
15
-
16
- module.exports = {
17
- preprocess: function (text, filename) {
18
- if (isValidFile(filename, 'FILES')) {
19
- Cache.set(`file:${filename}`, text);
20
- }
21
- return [{ text: "", filename }];
22
- },
23
- /**
24
- * Returns message objects as defined by ESLint:
25
- * https://eslint.org/docs/developer-guide/working-with-custom-formatters#the-message-object
26
- * @param {*} messages
27
- * @returns
28
- */
29
- postprocess: function (messages) {
30
- const messagesSanitized = [];
31
- messages.forEach(fileMessages => {
32
- const fileMessagesSanitized = [];
33
- fileMessages.forEach(r => {
34
- if (r && r.message && r.message.startsWith(`CompilationError:`)) {
35
- r.message = r.message.replace(`CompilationError: `,
36
- 'CDS model could not be compiled!\n');
37
- r.ruleId = `❗${r.ruleId}`;
38
- r.severity = 2;
39
- }
40
- fileMessagesSanitized.push(r);
41
- })
42
- messagesSanitized.push(fileMessagesSanitized);
43
- })
44
- if (Cache.has("test")) {
45
- Cache.clear();
46
- }
47
- return [].concat(...messagesSanitized);
48
- },
49
- supportsAutofix: true,
50
- };
@@ -1,40 +0,0 @@
1
- module.exports = {
2
- meta: {
3
- docs: {
4
- description: `Draft-enabled entities shall not be used in views that make use of \`JOIN\`.`,
5
- category: "Model Validation",
6
- recommended: true,
7
- version: "2.2.1",
8
- },
9
- severity: "warn",
10
- type: "suggestion",
11
- messages: {
12
- noJoinOnDraftEnabledEntities: `Do not use draft-enabled entities in views that make use of \`JOIN\`.`,
13
- },
14
- },
15
- create: function (context) {
16
-
17
- return { entity: check_nojoin_draftenabled }
18
-
19
- function check_nojoin_draftenabled(e) {
20
- if (e["@odata.draft.enabled"]) {
21
- if (e.query.SELECT.from.join) {
22
- const location = e.query.$location;
23
- if (context.sourcecode.lines[location.line - 1]) {
24
- const endCol = context.sourcecode.lines[location.line - 1].length;
25
- const loc = {
26
- start: { line: location.line, column: location.col - 1 },
27
- end: { line: location.line, column: endCol },
28
- };
29
- return {
30
- messageId: "noJoinOnDraftEnabledEntities",
31
- loc,
32
- file: e.$location.file,
33
- };
34
- }
35
- }
36
- }
37
- }
38
-
39
- },
40
- };
@@ -1,94 +0,0 @@
1
- /**
2
- * Levenshtein distance algorithm using recursive calls and cache
3
- *
4
- * @param input search the list for a best match for this string
5
- * @param list a list of strings to match input against
6
- * @param log logging method to use, might be null if no logging is wanted
7
- * @returns array with best matches, is never null but might be empty in case no search was possible
8
- */
9
-
10
-
11
- const cache = {};
12
-
13
-
14
- module.exports = (input, list, log, keepCase=false) => {
15
- let minDistWords = [];
16
-
17
- if (input.length > 50 || list.length > 50) {
18
- return minDistWords;
19
- }
20
-
21
- let minDist = Number.MAX_SAFE_INTEGER;
22
-
23
- log && log('\nword\t\tlevDist\t\ttime(ms)');
24
-
25
- let runtime = 0;
26
-
27
- for (const word of list) {
28
-
29
-
30
- const start = log && Date.now();
31
- let levDist;
32
- if (word === word.toUpperCase() && !keepCase) {
33
- levDist = levDistance(input.toUpperCase(), word);
34
- } else {
35
- levDist = levDistance(input, word);
36
- }
37
-
38
- if (log) {
39
- const duration = Date.now() - start;
40
- runtime = runtime + duration;
41
- log(`${word}\t\t${levDist}\t\t${duration}`);
42
- }
43
-
44
- if (levDist === minDist) {
45
- minDistWords.push(word);
46
- }
47
-
48
- if (levDist < minDist) {
49
- minDist = levDist;
50
- minDistWords = [word];
51
- }
52
- }
53
-
54
- log && log(`runtime: ${runtime}ms`);
55
-
56
- return minDistWords.sort();
57
- }
58
-
59
-
60
- const levDistance = (a, b) => {
61
-
62
- if (cache[a] && cache[a][b]) {
63
- return cache[a][b];
64
- }
65
-
66
- if (a.length === 0) {
67
- return addToCache(a, b, b.length);
68
- }
69
-
70
- if (b.length === 0) {
71
- return addToCache(a, b, a.length);
72
- }
73
-
74
- const tail_a = a.substring(1);
75
- const tail_b = b.substring(1);
76
-
77
- if (a[0] === b[0]) {
78
- return levDistance(tail_a, tail_b);
79
- }
80
-
81
- const lev1 = levDistance(tail_a, b);
82
- const lev2 = levDistance(a, tail_b);
83
- const lev3 = levDistance(tail_a, tail_b);
84
-
85
- const levDist = Math.min(lev1, lev2, lev3) + 1;
86
- return addToCache(a, b, levDist);
87
- }
88
-
89
-
90
- const addToCache = (a, b, value) => {
91
- cache[a] = cache[a] || {};
92
- cache[a][b] = value;
93
- return value;
94
- }
@@ -1,94 +0,0 @@
1
- const { FILES, MODEL_FILES } = require("../constants");
2
-
3
- module.exports = {
4
- /**
5
- * Checks whether the given filePath matches a regex `files`
6
- * @param {string} filePath
7
- * @returns boolean
8
- */
9
- isValidFile: function (filePath, fileType) {
10
- const genRegex = (key) => new RegExp(`${key.map((file) => file.replace("*", "")).join("$|")}$`);
11
- let isValid = false;
12
- switch(fileType) {
13
- case 'MODEL_FILES':
14
- isValid = genRegex(MODEL_FILES).test(filePath);
15
- break;
16
- case 'FILES':
17
- isValid = genRegex(FILES).test(filePath);
18
- break;
19
- }
20
- return isValid;
21
- },
22
- /**
23
- * Checks whether the plugin is run via VS Code ESLint extension
24
- * @returns boolean
25
- */
26
- isVSCodeEditor() {
27
- return process.argv.join(" ").includes("dbaeumer.vscode-eslint");
28
- },
29
-
30
- /**
31
- * Checks whether ESLint is running in debug mode
32
- * @returns boolean
33
- */
34
- hasDebugFlag() {
35
- return process.argv.includes("--debug");
36
- },
37
-
38
- /**
39
- * Returns an array of allowed file extensions
40
- * the plugin can parse of the form "*.ext"
41
- * @returns {ConfigOverrideFiles} Array of file extensions
42
- */
43
- getFileExtensions: function() {
44
- return FILES;
45
- },
46
-
47
- /**
48
- * Attempts to extract JSON from a text by looking for the longst substring
49
- * enclosed by braces or brackets. Input string can therefore either contain
50
- * a JSON object enclosed by braces, or a JSON array enclosed by brackets.
51
- * No semantic parsing is done whatsoever (aside from the final JSON.parse)
52
- * so if the input string is not sane, you will only notice from the
53
- * final parse step failing.
54
- * @param text text from which to extract JSON.
55
- * @param index the start index of the first opening "{" or "[" within text.
56
- * @returns a parsed JSON object or an empty object if not called with proper parameters.
57
- * @throws Errors when JSON contained within string is not valid.
58
- */
59
- extractJSON: function(text, index) {
60
- const [opening, closing] = {
61
- "{": ["{", "}"],
62
- "[": ["[", "]"],
63
- }[text[index]] || [undefined, undefined];
64
-
65
- // neither "[" nor "{" at beginning -> fail fast
66
- if (opening === undefined) return {};
67
-
68
- // we expect caller to call with an index of the first opening brace.
69
- // So add that brace and increment index at start.
70
- index++;
71
- let result = opening;
72
- let open = 1;
73
- while (open > 0 && index < text.length) {
74
- const char = text[index];
75
- if (char === closing) {
76
- open--;
77
- } else if (char === opening) {
78
- open++;
79
- }
80
-
81
- result += char;
82
- index++;
83
- }
84
-
85
- if (open !== 0) {
86
- throw new Error(
87
- "text does not contain proper JSON (unmatched opening or closing brace)"
88
- );
89
- }
90
-
91
- return JSON.parse(result);
92
- }
93
-
94
- };
@@ -1 +0,0 @@
1
- "use strict";function peg$subclass(t,r){function e(){this.constructor=t}e.prototype=r.prototype,t.prototype=new e}function peg$SyntaxError(t,r,e,n){this.message=t,this.expected=r,this.found=e,this.location=n,this.name="SyntaxError","function"==typeof Error.captureStackTrace&&Error.captureStackTrace(this,peg$SyntaxError)}function peg$parse(t,r){r=void 0!==r?r:{};var e,n={},a={JSON:it},c=it,u=nt("{",!1),o=function(t,r,e){t[r]=e},s=nt("}",!1),h=nt("[",!1),i=function(t,r){t.push(r)},l=nt("]",!1),f=/^[+\-]/,A=at(["+","-"],!1,!1),p=/^[0-9]/,g=at([["0","9"]],!1,!1),d=nt(".",!1),C=nt("e",!1),v=ct("string"),x='"',b=nt('"',!1),y="\\",m=nt("\\",!1),E=nt("/",!1),S=nt("n",!1),$=nt("r",!1),F=nt("t",!1),w=nt("b",!1),R=nt("f",!1),j=nt("u",!1),k=/^[0-9a-f]/,M=at([["0","9"],["a","f"]],!1,!1),N=function(t){return String.fromCharCode(parseInt(t,16))},T=/^[^"]/,I=at(['"'],!0,!1),J="null",O=nt("null",!1),U="true",q=nt("true",!1),z="false",B=nt("false",!1),D=nt(":",!1),G=nt(",",!1),H=/^[ \t\n\r]/,K=at([" ","\t","\n","\r"],!1,!1),L=nt("//",!1),P=/^[\n\r\u2028\u2029]/,Q=at(["\n","\r","\u2028","\u2029"],!1,!1),V={type:"any"},W=nt("/*",!1),X="*/",Y=nt("*/",!1),Z=0,_=[{line:1,column:1}],tt=0,rt=[],et=0;if("startRule"in r){if(!(r.startRule in a))throw new Error("Can't start parsing from rule \""+r.startRule+'".');c=a[r.startRule]}function nt(t,r){return{type:"literal",text:t,ignoreCase:r}}function at(t,r,e){return{type:"class",parts:t,inverted:r,ignoreCase:e}}function ct(t){return{type:"other",description:t}}function ut(r){var e,n=_[r];if(n)return n;for(e=r-1;!_[e];)e--;for(n={line:(n=_[e]).line,column:n.column};e<r;)10===t.charCodeAt(e)?(n.line++,n.column=1):n.column++,e++;return _[r]=n,n}function ot(t,r){var e=ut(t),n=ut(r);return{start:{offset:t,line:e.line,column:e.column},end:{offset:r,line:n.line,column:n.column}}}function st(t){Z<tt||(Z>tt&&(tt=Z,rt=[]),rt.push(t))}function ht(t,r,e){return new peg$SyntaxError(peg$SyntaxError.buildMessage(t,r),t,r,e)}function it(){var t,r;return t=Z,gt()!==n&&(r=lt())!==n&&gt()!==n?(t,t=r):(Z=t,t=n),t}function lt(){var r;return(r=function(){var r,e,a,c,h,i,l,f,A;r=Z,e=Z,(a=gt())!==n?(123===t.charCodeAt(Z)?(c="{",Z++):(c=n,0===et&&st(u)),c!==n&&(h=gt())!==n?(e,e=a={}):(Z=e,e=n)):(Z=e,e=n);if(e!==n){if(a=Z,c=Z,(h=ft())!==n&&(i=At())!==n&&(l=lt())!==n?(c,c=h=o(e,h,l)):(Z=c,c=n),c!==n){for(h=[],i=Z,(l=pt())!==n&&(f=ft())!==n&&At()!==n&&(A=lt())!==n?(i,i=l=o(e,f,A)):(Z=i,i=n);i!==n;)h.push(i),i=Z,(l=pt())!==n&&(f=ft())!==n&&At()!==n&&(A=lt())!==n?(i,i=l=o(e,f,A)):(Z=i,i=n);h!==n?((i=pt())===n&&(i=null),i!==n?a=c=[c,h,i]:(Z=a,a=n)):(Z=a,a=n)}else Z=a,a=n;a===n&&(a=null),a!==n&&(c=gt())!==n?(125===t.charCodeAt(Z)?(h="}",Z++):(h=n,0===et&&st(s)),h!==n&&(i=gt())!==n?(r,r=e=e):(Z=r,r=n)):(Z=r,r=n)}else Z=r,r=n;return r}())===n&&(r=function(){var r,e,a,c,u,o,s;r=Z,e=Z,(a=gt())!==n?(91===t.charCodeAt(Z)?(c="[",Z++):(c=n,0===et&&st(h)),c!==n&&(u=gt())!==n?(e,e=a=[]):(Z=e,e=n)):(Z=e,e=n);if(e!==n){if(a=Z,c=Z,(u=lt())!==n&&(c,u=i(e,u)),(c=u)!==n){for(u=[],o=Z,pt()!==n&&(s=lt())!==n?(o,o=i(e,s)):(Z=o,o=n);o!==n;)u.push(o),o=Z,pt()!==n&&(s=lt())!==n?(o,o=i(e,s)):(Z=o,o=n);u!==n?((o=pt())===n&&(o=null),o!==n?a=c=[c,u,o]:(Z=a,a=n)):(Z=a,a=n)}else Z=a,a=n;a===n&&(a=null),a!==n&&(c=gt())!==n?(93===t.charCodeAt(Z)?(u="]",Z++):(u=n,0===et&&st(l)),u!==n&&(o=gt())!==n?(r,r=e=e):(Z=r,r=n)):(Z=r,r=n)}else Z=r,r=n;return r}())===n&&(r=function(){var r,e;r=Z,t.substr(Z,4)===J?(e=J,Z+=4):(e=n,0===et&&st(O));e!==n&&(r,e=null);return r=e}())===n&&(r=function(){var r,e;r=Z,t.substr(Z,4)===U?(e=U,Z+=4):(e=n,0===et&&st(q));e!==n&&(r,e=!0);return r=e}())===n&&(r=function(){var r,e;r=Z,t.substr(Z,5)===z?(e=z,Z+=5):(e=n,0===et&&st(B));e!==n&&(r,e=!1);return r=e}())===n&&(r=function(){var r,e,a,c,u,o,s,h,i,l,v;r=Z,e=Z,a=Z,f.test(t.charAt(Z))?(c=t.charAt(Z),Z++):(c=n,0===et&&st(A));c===n&&(c=null);if(c!==n){if(u=[],p.test(t.charAt(Z))?(o=t.charAt(Z),Z++):(o=n,0===et&&st(g)),o!==n)for(;o!==n;)u.push(o),p.test(t.charAt(Z))?(o=t.charAt(Z),Z++):(o=n,0===et&&st(g));else u=n;if(u!==n){if(o=Z,46===t.charCodeAt(Z)?(s=".",Z++):(s=n,0===et&&st(d)),s!==n){if(h=[],p.test(t.charAt(Z))?(i=t.charAt(Z),Z++):(i=n,0===et&&st(g)),i!==n)for(;i!==n;)h.push(i),p.test(t.charAt(Z))?(i=t.charAt(Z),Z++):(i=n,0===et&&st(g));else h=n;h!==n?o=s=[s,h]:(Z=o,o=n)}else Z=o,o=n;if(o===n&&(o=null),o!==n){if(s=Z,101===t.charCodeAt(Z)?(h="e",Z++):(h=n,0===et&&st(C)),h!==n)if(f.test(t.charAt(Z))?(i=t.charAt(Z),Z++):(i=n,0===et&&st(A)),i===n&&(i=null),i!==n){if(l=[],p.test(t.charAt(Z))?(v=t.charAt(Z),Z++):(v=n,0===et&&st(g)),v!==n)for(;v!==n;)l.push(v),p.test(t.charAt(Z))?(v=t.charAt(Z),Z++):(v=n,0===et&&st(g));else l=n;l!==n?s=h=[h,i,l]:(Z=s,s=n)}else Z=s,s=n;else Z=s,s=n;s===n&&(s=null),s!==n?a=c=[c,u,o,s]:(Z=a,a=n)}else Z=a,a=n}else Z=a,a=n}else Z=a,a=n;e=a!==n?t.substring(e,Z):a;e!==n&&(r,e=Number(e));return r=e}())===n&&(r=ft()),r}function ft(){var r,e,a,c,u,o,s,h,i,l,f,A,p;if(et++,r=Z,34===t.charCodeAt(Z)?(e=x,Z++):(e=n,0===et&&st(b)),e!==n){for(a=[],c=Z,92===t.charCodeAt(Z)?(u=y,Z++):(u=n,0===et&&st(m)),u!==n?(34===t.charCodeAt(Z)?(o=x,Z++):(o=n,0===et&&st(b)),o===n&&(92===t.charCodeAt(Z)?(o=y,Z++):(o=n,0===et&&st(m)),o===n&&(47===t.charCodeAt(Z)?(o="/",Z++):(o=n,0===et&&st(E)),o===n&&(o=Z,110===t.charCodeAt(Z)?(s="n",Z++):(s=n,0===et&&st(S)),s!==n&&(o,s="\n"),(o=s)===n&&(o=Z,114===t.charCodeAt(Z)?(s="r",Z++):(s=n,0===et&&st($)),s!==n&&(o,s="\r"),(o=s)===n&&(o=Z,116===t.charCodeAt(Z)?(s="t",Z++):(s=n,0===et&&st(F)),s!==n&&(o,s="\t"),(o=s)===n&&(o=Z,98===t.charCodeAt(Z)?(s="b",Z++):(s=n,0===et&&st(w)),s!==n&&(o,s="\b"),(o=s)===n&&(o=Z,102===t.charCodeAt(Z)?(s="f",Z++):(s=n,0===et&&st(R)),s!==n&&(o,s="\f"),(o=s)===n&&(o=Z,117===t.charCodeAt(Z)?(s="u",Z++):(s=n,0===et&&st(j)),s!==n?(h=Z,i=Z,k.test(t.charAt(Z))?(l=t.charAt(Z),Z++):(l=n,0===et&&st(M)),l!==n?(k.test(t.charAt(Z))?(f=t.charAt(Z),Z++):(f=n,0===et&&st(M)),f!==n?(k.test(t.charAt(Z))?(A=t.charAt(Z),Z++):(A=n,0===et&&st(M)),A!==n?(k.test(t.charAt(Z))?(p=t.charAt(Z),Z++):(p=n,0===et&&st(M)),p!==n?i=l=[l,f,A,p]:(Z=i,i=n)):(Z=i,i=n)):(Z=i,i=n)):(Z=i,i=n),(h=i!==n?t.substring(h,Z):i)!==n?(o,o=s=N(h)):(Z=o,o=n)):(Z=o,o=n))))))))),o!==n?(c,c=u=o):(Z=c,c=n)):(Z=c,c=n),c===n&&(T.test(t.charAt(Z))?(c=t.charAt(Z),Z++):(c=n,0===et&&st(I)));c!==n;)a.push(c),c=Z,92===t.charCodeAt(Z)?(u=y,Z++):(u=n,0===et&&st(m)),u!==n?(34===t.charCodeAt(Z)?(o=x,Z++):(o=n,0===et&&st(b)),o===n&&(92===t.charCodeAt(Z)?(o=y,Z++):(o=n,0===et&&st(m)),o===n&&(47===t.charCodeAt(Z)?(o="/",Z++):(o=n,0===et&&st(E)),o===n&&(o=Z,110===t.charCodeAt(Z)?(s="n",Z++):(s=n,0===et&&st(S)),s!==n&&(o,s="\n"),(o=s)===n&&(o=Z,114===t.charCodeAt(Z)?(s="r",Z++):(s=n,0===et&&st($)),s!==n&&(o,s="\r"),(o=s)===n&&(o=Z,116===t.charCodeAt(Z)?(s="t",Z++):(s=n,0===et&&st(F)),s!==n&&(o,s="\t"),(o=s)===n&&(o=Z,98===t.charCodeAt(Z)?(s="b",Z++):(s=n,0===et&&st(w)),s!==n&&(o,s="\b"),(o=s)===n&&(o=Z,102===t.charCodeAt(Z)?(s="f",Z++):(s=n,0===et&&st(R)),s!==n&&(o,s="\f"),(o=s)===n&&(o=Z,117===t.charCodeAt(Z)?(s="u",Z++):(s=n,0===et&&st(j)),s!==n?(h=Z,i=Z,k.test(t.charAt(Z))?(l=t.charAt(Z),Z++):(l=n,0===et&&st(M)),l!==n?(k.test(t.charAt(Z))?(f=t.charAt(Z),Z++):(f=n,0===et&&st(M)),f!==n?(k.test(t.charAt(Z))?(A=t.charAt(Z),Z++):(A=n,0===et&&st(M)),A!==n?(k.test(t.charAt(Z))?(p=t.charAt(Z),Z++):(p=n,0===et&&st(M)),p!==n?i=l=[l,f,A,p]:(Z=i,i=n)):(Z=i,i=n)):(Z=i,i=n)):(Z=i,i=n),(h=i!==n?t.substring(h,Z):i)!==n?(o,o=s=N(h)):(Z=o,o=n)):(Z=o,o=n))))))))),o!==n?(c,c=u=o):(Z=c,c=n)):(Z=c,c=n),c===n&&(T.test(t.charAt(Z))?(c=t.charAt(Z),Z++):(c=n,0===et&&st(I)));a!==n?(34===t.charCodeAt(Z)?(c=x,Z++):(c=n,0===et&&st(b)),c!==n?(r,r=e=a.join("")):(Z=r,r=n)):(Z=r,r=n)}else Z=r,r=n;return et--,r===n&&(e=n,0===et&&st(v)),r}function At(){var r,e;return r=Z,gt()!==n?(58===t.charCodeAt(Z)?(e=":",Z++):(e=n,0===et&&st(D)),e!==n&&gt()!==n?(r,r=void 0):(Z=r,r=n)):(Z=r,r=n),r}function pt(){var r,e;return r=Z,gt()!==n?(44===t.charCodeAt(Z)?(e=",",Z++):(e=n,0===et&&st(G)),e!==n&&gt()!==n?(r,r=void 0):(Z=r,r=n)):(Z=r,r=n),r}function gt(){var r,e,a;for(r=Z,e=[],H.test(t.charAt(Z))?(a=t.charAt(Z),Z++):(a=n,0===et&&st(K)),a===n&&(a=dt())===n&&(a=Ct());a!==n;)e.push(a),H.test(t.charAt(Z))?(a=t.charAt(Z),Z++):(a=n,0===et&&st(K)),a===n&&(a=dt())===n&&(a=Ct());return e!==n&&(r,e=void 0),r=e}function dt(){var r,e,a,c,u,o,s;if(r=Z,e=Z,"//"===t.substr(Z,2)?(a="//",Z+=2):(a=n,0===et&&st(L)),a!==n){for(c=[],u=Z,o=Z,et++,P.test(t.charAt(Z))?(s=t.charAt(Z),Z++):(s=n,0===et&&st(Q)),et--,s===n?o=void 0:(Z=o,o=n),o!==n?(t.length>Z?(s=t.charAt(Z),Z++):(s=n,0===et&&st(V)),s!==n?u=o=[o,s]:(Z=u,u=n)):(Z=u,u=n);u!==n;)c.push(u),u=Z,o=Z,et++,P.test(t.charAt(Z))?(s=t.charAt(Z),Z++):(s=n,0===et&&st(Q)),et--,s===n?o=void 0:(Z=o,o=n),o!==n?(t.length>Z?(s=t.charAt(Z),Z++):(s=n,0===et&&st(V)),s!==n?u=o=[o,s]:(Z=u,u=n)):(Z=u,u=n);c!==n?e=a=[a,c]:(Z=e,e=n)}else Z=e,e=n;return r=e!==n?t.substring(r,Z):e}function Ct(){var r,e,a,c,u,o,s;if(r=Z,e=Z,"/*"===t.substr(Z,2)?(a="/*",Z+=2):(a=n,0===et&&st(W)),a!==n){for(c=[],u=Z,o=Z,et++,t.substr(Z,2)===X?(s=X,Z+=2):(s=n,0===et&&st(Y)),et--,s===n?o=void 0:(Z=o,o=n),o!==n?(t.length>Z?(s=t.charAt(Z),Z++):(s=n,0===et&&st(V)),s!==n?u=o=[o,s]:(Z=u,u=n)):(Z=u,u=n);u!==n;)c.push(u),u=Z,o=Z,et++,t.substr(Z,2)===X?(s=X,Z+=2):(s=n,0===et&&st(Y)),et--,s===n?o=void 0:(Z=o,o=n),o!==n?(t.length>Z?(s=t.charAt(Z),Z++):(s=n,0===et&&st(V)),s!==n?u=o=[o,s]:(Z=u,u=n)):(Z=u,u=n);c!==n?(t.substr(Z,2)===X?(u=X,Z+=2):(u=n,0===et&&st(Y)),u!==n?e=a=[a,c,u]:(Z=e,e=n)):(Z=e,e=n)}else Z=e,e=n;return r=e!==n?t.substring(r,Z):e}if((e=c())!==n&&Z===t.length)return e;throw e!==n&&Z<t.length&&st({type:"end"}),ht(rt,tt<t.length?t.charAt(tt):null,tt<t.length?ot(tt,tt+1):ot(tt,tt))}peg$subclass(peg$SyntaxError,Error),peg$SyntaxError.buildMessage=function(t,r){var e={literal:function(t){return'"'+a(t.text)+'"'},class:function(t){var r,e="";for(r=0;r<t.parts.length;r++)e+=t.parts[r]instanceof Array?c(t.parts[r][0])+"-"+c(t.parts[r][1]):c(t.parts[r]);return"["+(t.inverted?"^":"")+e+"]"},any:function(t){return"any character"},end:function(t){return"end of input"},other:function(t){return t.description}};function n(t){return t.charCodeAt(0).toString(16).toUpperCase()}function a(t){return t.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\0/g,"\\0").replace(/\t/g,"\\t").replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/[\x00-\x0F]/g,(function(t){return"\\x0"+n(t)})).replace(/[\x10-\x1F\x7F-\x9F]/g,(function(t){return"\\x"+n(t)}))}function c(t){return t.replace(/\\/g,"\\\\").replace(/\]/g,"\\]").replace(/\^/g,"\\^").replace(/-/g,"\\-").replace(/\0/g,"\\0").replace(/\t/g,"\\t").replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/[\x00-\x0F]/g,(function(t){return"\\x0"+n(t)})).replace(/[\x10-\x1F\x7F-\x9F]/g,(function(t){return"\\x"+n(t)}))}return"Expected "+function(t){var r,n,a,c=new Array(t.length);for(r=0;r<t.length;r++)c[r]=(a=t[r],e[a.type](a));if(c.sort(),c.length>0){for(r=1,n=1;r<c.length;r++)c[r-1]!==c[r]&&(c[n]=c[r],n++);c.length=n}switch(c.length){case 1:return c[0];case 2:return c[0]+" or "+c[1];default:return c.slice(0,-1).join(", ")+", or "+c[c.length-1]}}(t)+" but "+function(t){return t?'"'+a(t)+'"':"end of input"}(r)+" found."},module.exports={SyntaxError:peg$SyntaxError,parse:peg$parse};