├── .gitignore ├── docs ├── tutorials │ ├── tutorials.json │ └── getting-started.md ├── jsdoc │ ├── fonts │ │ ├── OpenSans-Bold-webfont.eot │ │ ├── OpenSans-Bold-webfont.woff │ │ ├── OpenSans-Italic-webfont.eot │ │ ├── OpenSans-Light-webfont.eot │ │ ├── OpenSans-Light-webfont.woff │ │ ├── OpenSans-Italic-webfont.woff │ │ ├── OpenSans-Regular-webfont.eot │ │ ├── OpenSans-Regular-webfont.woff │ │ ├── OpenSans-BoldItalic-webfont.eot │ │ ├── OpenSans-BoldItalic-webfont.woff │ │ ├── OpenSans-LightItalic-webfont.eot │ │ └── OpenSans-LightItalic-webfont.woff │ ├── scripts │ │ ├── linenumber.js │ │ └── prettify │ │ │ ├── lang-css.js │ │ │ ├── Apache-License-2.0.txt │ │ │ └── prettify.js │ ├── index.html │ ├── styles │ │ ├── prettify-jsdoc.css │ │ ├── prettify-tomorrow.css │ │ └── jsdoc-default.css │ ├── global.html │ └── tutorial-getting-started.html ├── jsdoc-conf.json ├── index.html ├── Makefile ├── schema_doc.min.js └── schema_doc.css ├── out ├── fonts │ ├── OpenSans-Bold-webfont.eot │ ├── OpenSans-Bold-webfont.woff │ ├── OpenSans-Italic-webfont.eot │ ├── OpenSans-Light-webfont.eot │ ├── OpenSans-Light-webfont.woff │ ├── OpenSans-Italic-webfont.woff │ ├── OpenSans-Regular-webfont.eot │ ├── OpenSans-Regular-webfont.woff │ ├── OpenSans-BoldItalic-webfont.eot │ ├── OpenSans-BoldItalic-webfont.woff │ ├── OpenSans-LightItalic-webfont.eot │ └── OpenSans-LightItalic-webfont.woff ├── scripts │ ├── linenumber.js │ └── prettify │ │ ├── lang-css.js │ │ ├── Apache-License-2.0.txt │ │ └── prettify.js ├── index.html ├── styles │ ├── prettify-jsdoc.css │ ├── prettify-tomorrow.css │ └── jsdoc-default.css └── global.html ├── grammars ├── test.js ├── sub.js ├── dungeon.js ├── dungeon.json └── level.js ├── package.json ├── Makefile ├── bin ├── search-emojis.js ├── graph2dot.js ├── transform.js ├── lattice.js └── emojis.js ├── graphs ├── 2x2.json ├── lev4.json ├── 4x4.json └── cyclic.4x4.json ├── README.md └── subgraph.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /docs/tutorials/tutorials.json: -------------------------------------------------------------------------------- 1 | { 2 | "getting-started": { 3 | "title": "Getting Started" 4 | } 5 | } -------------------------------------------------------------------------------- /out/fonts/OpenSans-Bold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/out/fonts/OpenSans-Bold-webfont.eot -------------------------------------------------------------------------------- /out/fonts/OpenSans-Bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/out/fonts/OpenSans-Bold-webfont.woff -------------------------------------------------------------------------------- /out/fonts/OpenSans-Italic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/out/fonts/OpenSans-Italic-webfont.eot -------------------------------------------------------------------------------- /out/fonts/OpenSans-Light-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/out/fonts/OpenSans-Light-webfont.eot -------------------------------------------------------------------------------- /out/fonts/OpenSans-Light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/out/fonts/OpenSans-Light-webfont.woff -------------------------------------------------------------------------------- /out/fonts/OpenSans-Italic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/out/fonts/OpenSans-Italic-webfont.woff -------------------------------------------------------------------------------- /out/fonts/OpenSans-Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/out/fonts/OpenSans-Regular-webfont.eot -------------------------------------------------------------------------------- /out/fonts/OpenSans-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/out/fonts/OpenSans-Regular-webfont.woff -------------------------------------------------------------------------------- /docs/jsdoc/fonts/OpenSans-Bold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/docs/jsdoc/fonts/OpenSans-Bold-webfont.eot -------------------------------------------------------------------------------- /out/fonts/OpenSans-BoldItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/out/fonts/OpenSans-BoldItalic-webfont.eot -------------------------------------------------------------------------------- /out/fonts/OpenSans-BoldItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/out/fonts/OpenSans-BoldItalic-webfont.woff -------------------------------------------------------------------------------- /out/fonts/OpenSans-LightItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/out/fonts/OpenSans-LightItalic-webfont.eot -------------------------------------------------------------------------------- /docs/jsdoc/fonts/OpenSans-Bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/docs/jsdoc/fonts/OpenSans-Bold-webfont.woff -------------------------------------------------------------------------------- /docs/jsdoc/fonts/OpenSans-Italic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/docs/jsdoc/fonts/OpenSans-Italic-webfont.eot -------------------------------------------------------------------------------- /docs/jsdoc/fonts/OpenSans-Light-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/docs/jsdoc/fonts/OpenSans-Light-webfont.eot -------------------------------------------------------------------------------- /docs/jsdoc/fonts/OpenSans-Light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/docs/jsdoc/fonts/OpenSans-Light-webfont.woff -------------------------------------------------------------------------------- /out/fonts/OpenSans-LightItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/out/fonts/OpenSans-LightItalic-webfont.woff -------------------------------------------------------------------------------- /docs/jsdoc-conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": { 3 | "include": [ "../index.js" ] 4 | }, 5 | "plugins": ["plugins/markdown"] 6 | } -------------------------------------------------------------------------------- /docs/jsdoc/fonts/OpenSans-Italic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/docs/jsdoc/fonts/OpenSans-Italic-webfont.woff -------------------------------------------------------------------------------- /docs/jsdoc/fonts/OpenSans-Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/docs/jsdoc/fonts/OpenSans-Regular-webfont.eot -------------------------------------------------------------------------------- /docs/jsdoc/fonts/OpenSans-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/docs/jsdoc/fonts/OpenSans-Regular-webfont.woff -------------------------------------------------------------------------------- /docs/jsdoc/fonts/OpenSans-BoldItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/docs/jsdoc/fonts/OpenSans-BoldItalic-webfont.eot -------------------------------------------------------------------------------- /docs/jsdoc/fonts/OpenSans-BoldItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/docs/jsdoc/fonts/OpenSans-BoldItalic-webfont.woff -------------------------------------------------------------------------------- /docs/jsdoc/fonts/OpenSans-LightItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/docs/jsdoc/fonts/OpenSans-LightItalic-webfont.eot -------------------------------------------------------------------------------- /docs/jsdoc/fonts/OpenSans-LightItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihh/graphgram/HEAD/docs/jsdoc/fonts/OpenSans-LightItalic-webfont.woff -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Redirecting to jsdoc/tutorial-getting-started.html...

8 | 9 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | all: schema_doc.html jsdoc 2 | 3 | clean: 4 | rm -rf schema_doc.* jsdoc/ grammar-schema.json 5 | 6 | schema_doc.html: grammar-schema.json 7 | generate-schema-doc $< 8 | 9 | grammar-schema.json: 10 | ../bin/transform.js -j $@ 11 | 12 | jsdoc: 13 | jsdoc -c jsdoc-conf.json -u tutorials -d $@ 14 | 15 | -------------------------------------------------------------------------------- /grammars/test.js: -------------------------------------------------------------------------------- 1 | { 2 | start: "START", 3 | limit: 5, 4 | rules: [ 5 | { lhs: "START", rhs: { node: ["X"], edge: [[0,0,'self1']] } }, 6 | { lhs: "X", rhs: ["F"], weight: 100, condition: "$a.label === 'X' && $$iter == 0" }, 7 | { lhs: "X", rhs: ["Y","Z"], weight: "1" }, 8 | { lhs: ["Y","Z"], rhs: ["U${a.label}","V"] }, 9 | { lhs: ["UY","V"], rhs: { node: ["R","S","C","D"], edge: [[0,1,"rs"],[0,2],[2,2,'self2'],[1,3],[2,3]] } }, 10 | { lhs: ["R","S","rx"], rhs: { node: ["A","B"], edge: [[0,1,"nope"]] } }, 11 | { lhs: ["R","S","rs"], rhs: { node: ["A","B"], edge: [[0,1,"${c.label}"]] } } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphgram", 3 | "version": "0.0.2", 4 | "description": "Simple graph grammar library", 5 | "main": "index.js", 6 | "dependencies": { 7 | "colors": "1.1.2", 8 | "extend": "^3.0.2", 9 | "graphlib": "~2.1.1", 10 | "jsonschema": "~1.1.1", 11 | "lodash": "^4.17.21", 12 | "mersennetwister": "~0.2.3", 13 | "node-getopt": "~0.3.2", 14 | "tmp": "~0.0.33" 15 | }, 16 | "scripts": {}, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/ihh/graphgram.git" 20 | }, 21 | "author": "Ian Holmes", 22 | "license": "ISC", 23 | "bugs": { 24 | "url": "https://github.com/ihh/graphgram/issues" 25 | }, 26 | "homepage": "https://github.com/ihh/graphgram#readme" 27 | } 28 | -------------------------------------------------------------------------------- /out/scripts/linenumber.js: -------------------------------------------------------------------------------- 1 | /*global document */ 2 | (() => { 3 | const source = document.getElementsByClassName('prettyprint source linenums'); 4 | let i = 0; 5 | let lineNumber = 0; 6 | let lineId; 7 | let lines; 8 | let totalLines; 9 | let anchorHash; 10 | 11 | if (source && source[0]) { 12 | anchorHash = document.location.hash.substring(1); 13 | lines = source[0].getElementsByTagName('li'); 14 | totalLines = lines.length; 15 | 16 | for (; i < totalLines; i++) { 17 | lineNumber++; 18 | lineId = `line${lineNumber}`; 19 | lines[i].id = lineId; 20 | if (lineId === anchorHash) { 21 | lines[i].className += ' selected'; 22 | } 23 | } 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /docs/jsdoc/scripts/linenumber.js: -------------------------------------------------------------------------------- 1 | /*global document */ 2 | (() => { 3 | const source = document.getElementsByClassName('prettyprint source linenums'); 4 | let i = 0; 5 | let lineNumber = 0; 6 | let lineId; 7 | let lines; 8 | let totalLines; 9 | let anchorHash; 10 | 11 | if (source && source[0]) { 12 | anchorHash = document.location.hash.substring(1); 13 | lines = source[0].getElementsByTagName('li'); 14 | totalLines = lines.length; 15 | 16 | for (; i < totalLines; i++) { 17 | lineNumber++; 18 | lineId = `line${lineNumber}`; 19 | lines[i].id = lineId; 20 | if (lineId === anchorHash) { 21 | lines[i].className += ' selected'; 22 | } 23 | } 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | grammars/%.json: grammars/%.js 3 | node -e 'console.log(JSON.stringify(eval('"'"'('"'"'+fs.readFileSync("$<").toString()+'"'"')'"'"'),null,2))' >$@ 4 | 5 | graphs/$(SIZE)x$(SIZE).json: 6 | bin/lattice.js -s $(SIZE) >$@ 7 | 8 | graphs/cyclic.$(SIZE)x$(SIZE).json: 9 | bin/lattice.js -s $(SIZE) >$@ 10 | 11 | graphs/lev$(SIZE).json: graphs/$(SIZE)x$(SIZE).json 12 | bin/transform.js -i $< -g grammars/level.json >$@ 13 | 14 | rebuild$(SIZE): 15 | rm graphs/lev$(SIZE).json 16 | biomake graphs/lev$(SIZE).json 17 | bin/graph2dot.js -o graphs/lev$(SIZE).json 18 | 19 | 20 | # README 21 | README.md: bin/transform.js 22 | bin/transform.js -h | perl -pe 's//>/g;' | perl -e 'open FILE,"){last if/
/;print}close FILE;print"
\n";while(<>){print};print"
\n"' >temp.md 23 | mv temp.md $@ 24 | -------------------------------------------------------------------------------- /out/scripts/prettify/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", 2 | /^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); 3 | -------------------------------------------------------------------------------- /docs/jsdoc/scripts/prettify/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", 2 | /^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); 3 | -------------------------------------------------------------------------------- /docs/schema_doc.min.js: -------------------------------------------------------------------------------- 1 | $(document).on("click",'a[href^="#"]',function(event){event.preventDefault();history.pushState({},"",this.href)});function flashElement(elementId){myElement=document.getElementById(elementId);myElement.classList.add("jsfh-animated-property");setTimeout(function(){myElement.classList.remove("jsfh-animated-property")},1e3)}function setAnchor(anchorLinkDestination){history.pushState({},"",anchorLinkDestination)}function anchorOnLoad(){let linkTarget=decodeURIComponent(window.location.hash.split("?")[0].split("&")[0]);if(linkTarget[0]==="#"){linkTarget=linkTarget.substr(1)}if(linkTarget.length>0){anchorLink(linkTarget)}}function anchorLink(linkTarget){const target=$("#"+linkTarget);target.parents().addBack().filter(".collapse:not(.show), .tab-pane, [role='tab']").each(function(index){if($(this).hasClass("collapse")){$(this).collapse("show")}else if($(this).hasClass("tab-pane")){const tabToShow=$("a[href='#"+$(this).attr("id")+"']");if(tabToShow){tabToShow.tab("show")}}else if($(this).attr("role")==="tab"){$(this).tab("show")}});setTimeout(function(){let targetElement=document.getElementById(linkTarget);if(targetElement){targetElement.scrollIntoView({block:"center",behavior:"smooth"});setTimeout(function(){flashElement(linkTarget)},500)}},1e3)} -------------------------------------------------------------------------------- /bin/search-emojis.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const getopt = require('node-getopt'), 4 | emojiLib = require('emojilib'), 5 | emojiSearch = require('@jukben/emoji-search').default 6 | 7 | const opt = getopt.create([ 8 | ['n' , 'name' , 'search by name'], 9 | ['r' , 'rule' , 'generate Bracery alternation rule'], 10 | ['k' , 'keyword' , 'search by keyword (default)'], 11 | ['h' , 'help' , 'display this help message'] 12 | ]) // create Getopt instance 13 | .bindHelp() // bind option 'help' to default action 14 | .parseSystem() // parse command line 15 | 16 | if (!opt.argv.length) 17 | throw new Error ("Please specify keyword(s)") 18 | 19 | opt.argv.forEach ((arg) => { 20 | if (opt.options.name) { 21 | const emoji = emojiLib.lib[arg] 22 | if (!emoji) 23 | console.log (arg + ' not found') 24 | else 25 | console.log (arg + ' ' + emoji.char) 26 | } else { 27 | const emojis = emojiSearch (arg) 28 | if (!emojis.length) 29 | console.log (arg + ' not found') 30 | else if (opt.options.rule) 31 | console.log ('[' + emojis.map((e)=>e.name).join('|') + ']') 32 | else 33 | emojis.forEach ((emoji) => { 34 | console.log (arg + ' ' + emoji.name + ' ' + emoji.char + ' ' + emoji.keywords.join(',')) 35 | }) 36 | } 37 | }) 38 | -------------------------------------------------------------------------------- /out/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Home 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Home

21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |

30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 | 52 | 55 | 56 |
57 | 58 |
59 | Documentation generated by JSDoc 4.0.2 on Tue Oct 10 2023 10:45:21 GMT-0700 (Pacific Daylight Time) 60 |
61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /docs/jsdoc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Home 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Home

21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |

30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 | 52 | 55 | 56 |
57 | 58 |
59 | Documentation generated by JSDoc 4.0.2 on Wed Oct 11 2023 15:40:44 GMT-0700 (Pacific Daylight Time) 60 |
61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /grammars/sub.js: -------------------------------------------------------------------------------- 1 | { 2 | start: "START", 3 | rules: [ 4 | { lhs: { node: [{id:"S",label:"START"}] }, rhs: { node: [{id:"H",head:["S"],label:"BEGIN"},{id:"T",tail:["S"],label:"END"}], 5 | edge: [{v:"H",w:"T"}] } }, 6 | { lhs: { node: [{id:"B",label:"BEGIN"}] }, rhs: { node: [{id:"B",label:"xBEGIN"}] } }, 7 | { lhs: { node: [{id:"E",label:"END"}] }, rhs: { node: [{id:"E",label:"xEND"}] } }, 8 | { lhs: { node: [{id:"E",label:"xEND"}] }, rhs: { node: [{id:"a",head:["E"],label:"a"},{id:"b",label:"b"},{id:"c",tail:["E"],label:"yEND"}], 9 | edge: [{v:"a",w:"b",label:"ab"},{v:"b",w:"c"}] } }, 10 | { lhs: { node: [{id:"x"},{id:"y"},{id:"z"}], 11 | edge: [{v:"x",w:"y",id:"e"},{v:"y",w:"z",id:"f"}] }, rhs: { node: [{id:"x",label:"${x.label}"},{id:"z",label:"${z.label}"}], 12 | edge: [{v:"x",w:"z",label:"${e.label}${f.label}"}] } }, 13 | { lhs: { node: [{id:"x",label:"xBEGIN"},{id:"y",label:"yEND"}], 14 | edge: [{v:"x",w:"y",id:"e"}] }, rhs: { node: [{id:"x",label:"_${x.label}"},{id:"y",label:"_${y.label}"},{id:"z",label:"zEND"}], 15 | edge: [{v:"x",w:"y",label:"${e.label}"},{v:"x",w:"z",label:"xz"}] } }, 16 | { lhs: { node: [{id:"x"},{id:"y"},{id:"z",label:"z.*"}], 17 | edge: [{v:"x",w:"y",id:"e"},{v:"x",w:"z",id:"f"}] }, rhs: { node: [{id:"x",label:"_${x.label}"},{id:"y",label:"_${y.label}"},{id:"z",label:"_${z.label}"}], 18 | edge: [{v:"x",w:"y",label:"${e.label}"},{v:"y",w:"z",label:{$eval:"$f.label"}}] } } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /out/styles/prettify-jsdoc.css: -------------------------------------------------------------------------------- 1 | /* JSDoc prettify.js theme */ 2 | 3 | /* plain text */ 4 | .pln { 5 | color: #000000; 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | 10 | /* string content */ 11 | .str { 12 | color: #006400; 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | 17 | /* a keyword */ 18 | .kwd { 19 | color: #000000; 20 | font-weight: bold; 21 | font-style: normal; 22 | } 23 | 24 | /* a comment */ 25 | .com { 26 | font-weight: normal; 27 | font-style: italic; 28 | } 29 | 30 | /* a type name */ 31 | .typ { 32 | color: #000000; 33 | font-weight: normal; 34 | font-style: normal; 35 | } 36 | 37 | /* a literal value */ 38 | .lit { 39 | color: #006400; 40 | font-weight: normal; 41 | font-style: normal; 42 | } 43 | 44 | /* punctuation */ 45 | .pun { 46 | color: #000000; 47 | font-weight: bold; 48 | font-style: normal; 49 | } 50 | 51 | /* lisp open bracket */ 52 | .opn { 53 | color: #000000; 54 | font-weight: bold; 55 | font-style: normal; 56 | } 57 | 58 | /* lisp close bracket */ 59 | .clo { 60 | color: #000000; 61 | font-weight: bold; 62 | font-style: normal; 63 | } 64 | 65 | /* a markup tag name */ 66 | .tag { 67 | color: #006400; 68 | font-weight: normal; 69 | font-style: normal; 70 | } 71 | 72 | /* a markup attribute name */ 73 | .atn { 74 | color: #006400; 75 | font-weight: normal; 76 | font-style: normal; 77 | } 78 | 79 | /* a markup attribute value */ 80 | .atv { 81 | color: #006400; 82 | font-weight: normal; 83 | font-style: normal; 84 | } 85 | 86 | /* a declaration */ 87 | .dec { 88 | color: #000000; 89 | font-weight: bold; 90 | font-style: normal; 91 | } 92 | 93 | /* a variable name */ 94 | .var { 95 | color: #000000; 96 | font-weight: normal; 97 | font-style: normal; 98 | } 99 | 100 | /* a function name */ 101 | .fun { 102 | color: #000000; 103 | font-weight: bold; 104 | font-style: normal; 105 | } 106 | 107 | /* Specify class=linenums on a pre to get line numbering */ 108 | ol.linenums { 109 | margin-top: 0; 110 | margin-bottom: 0; 111 | } 112 | -------------------------------------------------------------------------------- /docs/jsdoc/styles/prettify-jsdoc.css: -------------------------------------------------------------------------------- 1 | /* JSDoc prettify.js theme */ 2 | 3 | /* plain text */ 4 | .pln { 5 | color: #000000; 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | 10 | /* string content */ 11 | .str { 12 | color: #006400; 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | 17 | /* a keyword */ 18 | .kwd { 19 | color: #000000; 20 | font-weight: bold; 21 | font-style: normal; 22 | } 23 | 24 | /* a comment */ 25 | .com { 26 | font-weight: normal; 27 | font-style: italic; 28 | } 29 | 30 | /* a type name */ 31 | .typ { 32 | color: #000000; 33 | font-weight: normal; 34 | font-style: normal; 35 | } 36 | 37 | /* a literal value */ 38 | .lit { 39 | color: #006400; 40 | font-weight: normal; 41 | font-style: normal; 42 | } 43 | 44 | /* punctuation */ 45 | .pun { 46 | color: #000000; 47 | font-weight: bold; 48 | font-style: normal; 49 | } 50 | 51 | /* lisp open bracket */ 52 | .opn { 53 | color: #000000; 54 | font-weight: bold; 55 | font-style: normal; 56 | } 57 | 58 | /* lisp close bracket */ 59 | .clo { 60 | color: #000000; 61 | font-weight: bold; 62 | font-style: normal; 63 | } 64 | 65 | /* a markup tag name */ 66 | .tag { 67 | color: #006400; 68 | font-weight: normal; 69 | font-style: normal; 70 | } 71 | 72 | /* a markup attribute name */ 73 | .atn { 74 | color: #006400; 75 | font-weight: normal; 76 | font-style: normal; 77 | } 78 | 79 | /* a markup attribute value */ 80 | .atv { 81 | color: #006400; 82 | font-weight: normal; 83 | font-style: normal; 84 | } 85 | 86 | /* a declaration */ 87 | .dec { 88 | color: #000000; 89 | font-weight: bold; 90 | font-style: normal; 91 | } 92 | 93 | /* a variable name */ 94 | .var { 95 | color: #000000; 96 | font-weight: normal; 97 | font-style: normal; 98 | } 99 | 100 | /* a function name */ 101 | .fun { 102 | color: #000000; 103 | font-weight: bold; 104 | font-style: normal; 105 | } 106 | 107 | /* Specify class=linenums on a pre to get line numbering */ 108 | ol.linenums { 109 | margin-top: 0; 110 | margin-bottom: 0; 111 | } 112 | -------------------------------------------------------------------------------- /bin/graph2dot.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require('fs'), 4 | getopt = require('node-getopt'), 5 | graphlib = require('graphlib'), 6 | Grammar = require('../index').Grammar, 7 | tmp = require('tmp'), 8 | exec = require('child_process').exec 9 | 10 | var opt = getopt.create([ 11 | ['g' , 'graph=PATH' , 'read graphlib graph file'], 12 | ['p' , 'pdf=PATH' , 'run neato to generate PDF'], 13 | ['o' , 'open' , 'run neato, then open PDF with open'], 14 | ['n' , 'no-autolabel' , 'do not auto-label using \'pos\' & \'name\''], 15 | ['h' , 'help' , 'display this help message'] 16 | ]) // create Getopt instance 17 | .bindHelp() // bind option 'help' to default action 18 | .parseSystem() // parse command line 19 | 20 | var filename = opt.options.graph || opt.argv[0] 21 | var graph = graphlib.json.read (JSON.parse (fs.readFileSync (filename))) 22 | 23 | var label = !opt.options['no-autolabel'] 24 | if (label) { 25 | graph.nodes().forEach (function (node) { 26 | var info = graph.node(node) 27 | info.dot = info.dot || {} 28 | if (info.name) 29 | info.dot.label = info.name 30 | }) 31 | graph.edges().forEach (function (edge) { 32 | var info = graph.edge(edge) 33 | info.dot = info.dot || {} 34 | if (info.name) 35 | info.dot.label = info.name 36 | }) 37 | } 38 | 39 | var dot = Grammar.prototype.toDot (graph) 40 | 41 | var pdf = opt.options.pdf, open = opt.options.open 42 | if (pdf) 43 | makePdf (dot, pdf, open ? openPdf : null) 44 | else if (open) 45 | tmp.file ({keep:true, 46 | postfix:'.pdf'}, 47 | (err, path, fd) => { 48 | if (err) throw err 49 | makePdf (dot, path, openPdf) 50 | }) 51 | else 52 | console.log (dot) 53 | 54 | function openPdf (pdfFilename) { 55 | var cmd = 'open ' + pdfFilename 56 | console.warn(cmd) 57 | exec (cmd, (err, stdout, stderr) => {}) 58 | } 59 | 60 | function makePdf (dot, pdfFilename, callback) { 61 | tmp.file ((err, path, fd) => { 62 | if (err) throw err 63 | fs.writeSync (fd, dot) 64 | fs.closeSync (fd) 65 | var cmd = 'neato -Tpdf -n ' + path + ' >' + pdfFilename 66 | console.warn(cmd) 67 | exec (cmd, (err, stdout, stderr) => { 68 | if (err) throw err 69 | if (callback) 70 | callback (pdfFilename) 71 | }) 72 | }) 73 | } 74 | -------------------------------------------------------------------------------- /out/styles/prettify-tomorrow.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Theme */ 2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 3 | /* Pretty printing styles. Used with prettify.js. */ 4 | /* SPAN elements with the classes below are added by prettyprint. */ 5 | /* plain text */ 6 | .pln { 7 | color: #4d4d4c; } 8 | 9 | @media screen { 10 | /* string content */ 11 | .str { 12 | color: #718c00; } 13 | 14 | /* a keyword */ 15 | .kwd { 16 | color: #8959a8; } 17 | 18 | /* a comment */ 19 | .com { 20 | color: #8e908c; } 21 | 22 | /* a type name */ 23 | .typ { 24 | color: #4271ae; } 25 | 26 | /* a literal value */ 27 | .lit { 28 | color: #f5871f; } 29 | 30 | /* punctuation */ 31 | .pun { 32 | color: #4d4d4c; } 33 | 34 | /* lisp open bracket */ 35 | .opn { 36 | color: #4d4d4c; } 37 | 38 | /* lisp close bracket */ 39 | .clo { 40 | color: #4d4d4c; } 41 | 42 | /* a markup tag name */ 43 | .tag { 44 | color: #c82829; } 45 | 46 | /* a markup attribute name */ 47 | .atn { 48 | color: #f5871f; } 49 | 50 | /* a markup attribute value */ 51 | .atv { 52 | color: #3e999f; } 53 | 54 | /* a declaration */ 55 | .dec { 56 | color: #f5871f; } 57 | 58 | /* a variable name */ 59 | .var { 60 | color: #c82829; } 61 | 62 | /* a function name */ 63 | .fun { 64 | color: #4271ae; } } 65 | /* Use higher contrast and text-weight for printable form. */ 66 | @media print, projection { 67 | .str { 68 | color: #060; } 69 | 70 | .kwd { 71 | color: #006; 72 | font-weight: bold; } 73 | 74 | .com { 75 | color: #600; 76 | font-style: italic; } 77 | 78 | .typ { 79 | color: #404; 80 | font-weight: bold; } 81 | 82 | .lit { 83 | color: #044; } 84 | 85 | .pun, .opn, .clo { 86 | color: #440; } 87 | 88 | .tag { 89 | color: #006; 90 | font-weight: bold; } 91 | 92 | .atn { 93 | color: #404; } 94 | 95 | .atv { 96 | color: #060; } } 97 | /* Style */ 98 | /* 99 | pre.prettyprint { 100 | background: white; 101 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 102 | font-size: 12px; 103 | line-height: 1.5; 104 | border: 1px solid #ccc; 105 | padding: 10px; } 106 | */ 107 | 108 | /* Specify class=linenums on a pre to get line numbering */ 109 | ol.linenums { 110 | margin-top: 0; 111 | margin-bottom: 0; } 112 | 113 | /* IE indents via margin-left */ 114 | li.L0, 115 | li.L1, 116 | li.L2, 117 | li.L3, 118 | li.L4, 119 | li.L5, 120 | li.L6, 121 | li.L7, 122 | li.L8, 123 | li.L9 { 124 | /* */ } 125 | 126 | /* Alternate shading for lines */ 127 | li.L1, 128 | li.L3, 129 | li.L5, 130 | li.L7, 131 | li.L9 { 132 | /* */ } 133 | -------------------------------------------------------------------------------- /docs/jsdoc/styles/prettify-tomorrow.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Theme */ 2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 3 | /* Pretty printing styles. Used with prettify.js. */ 4 | /* SPAN elements with the classes below are added by prettyprint. */ 5 | /* plain text */ 6 | .pln { 7 | color: #4d4d4c; } 8 | 9 | @media screen { 10 | /* string content */ 11 | .str { 12 | color: #718c00; } 13 | 14 | /* a keyword */ 15 | .kwd { 16 | color: #8959a8; } 17 | 18 | /* a comment */ 19 | .com { 20 | color: #8e908c; } 21 | 22 | /* a type name */ 23 | .typ { 24 | color: #4271ae; } 25 | 26 | /* a literal value */ 27 | .lit { 28 | color: #f5871f; } 29 | 30 | /* punctuation */ 31 | .pun { 32 | color: #4d4d4c; } 33 | 34 | /* lisp open bracket */ 35 | .opn { 36 | color: #4d4d4c; } 37 | 38 | /* lisp close bracket */ 39 | .clo { 40 | color: #4d4d4c; } 41 | 42 | /* a markup tag name */ 43 | .tag { 44 | color: #c82829; } 45 | 46 | /* a markup attribute name */ 47 | .atn { 48 | color: #f5871f; } 49 | 50 | /* a markup attribute value */ 51 | .atv { 52 | color: #3e999f; } 53 | 54 | /* a declaration */ 55 | .dec { 56 | color: #f5871f; } 57 | 58 | /* a variable name */ 59 | .var { 60 | color: #c82829; } 61 | 62 | /* a function name */ 63 | .fun { 64 | color: #4271ae; } } 65 | /* Use higher contrast and text-weight for printable form. */ 66 | @media print, projection { 67 | .str { 68 | color: #060; } 69 | 70 | .kwd { 71 | color: #006; 72 | font-weight: bold; } 73 | 74 | .com { 75 | color: #600; 76 | font-style: italic; } 77 | 78 | .typ { 79 | color: #404; 80 | font-weight: bold; } 81 | 82 | .lit { 83 | color: #044; } 84 | 85 | .pun, .opn, .clo { 86 | color: #440; } 87 | 88 | .tag { 89 | color: #006; 90 | font-weight: bold; } 91 | 92 | .atn { 93 | color: #404; } 94 | 95 | .atv { 96 | color: #060; } } 97 | /* Style */ 98 | /* 99 | pre.prettyprint { 100 | background: white; 101 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 102 | font-size: 12px; 103 | line-height: 1.5; 104 | border: 1px solid #ccc; 105 | padding: 10px; } 106 | */ 107 | 108 | /* Specify class=linenums on a pre to get line numbering */ 109 | ol.linenums { 110 | margin-top: 0; 111 | margin-bottom: 0; } 112 | 113 | /* IE indents via margin-left */ 114 | li.L0, 115 | li.L1, 116 | li.L2, 117 | li.L3, 118 | li.L4, 119 | li.L5, 120 | li.L6, 121 | li.L7, 122 | li.L8, 123 | li.L9 { 124 | /* */ } 125 | 126 | /* Alternate shading for lines */ 127 | li.L1, 128 | li.L3, 129 | li.L5, 130 | li.L7, 131 | li.L9 { 132 | /* */ } 133 | -------------------------------------------------------------------------------- /graphs/2x2.json: -------------------------------------------------------------------------------- 1 | { 2 | "options": { 3 | "directed": true, 4 | "multigraph": false, 5 | "compound": false 6 | }, 7 | "nodes": [ 8 | { 9 | "v": "x0y0", 10 | "value": { 11 | "x": 0, 12 | "y": 0, 13 | "dot": { 14 | "pos": "0,128", 15 | "label": "node" 16 | }, 17 | "name": "node" 18 | } 19 | }, 20 | { 21 | "v": "x0y1", 22 | "value": { 23 | "x": 0, 24 | "y": 1, 25 | "dot": { 26 | "pos": "0,0", 27 | "label": "node" 28 | }, 29 | "name": "node" 30 | } 31 | }, 32 | { 33 | "v": "x1y0", 34 | "value": { 35 | "x": 1, 36 | "y": 0, 37 | "dot": { 38 | "pos": "128,128", 39 | "label": "node" 40 | }, 41 | "name": "node" 42 | } 43 | }, 44 | { 45 | "v": "x1y1", 46 | "value": { 47 | "x": 1, 48 | "y": 1, 49 | "dot": { 50 | "pos": "128,0", 51 | "label": "init" 52 | }, 53 | "name": "init" 54 | } 55 | } 56 | ], 57 | "edges": [ 58 | { 59 | "v": "x0y0", 60 | "w": "x1y0", 61 | "value": { 62 | "dir": "e", 63 | "dot": { 64 | "label": "edge" 65 | }, 66 | "name": "edge" 67 | } 68 | }, 69 | { 70 | "v": "x0y0", 71 | "w": "x0y1", 72 | "value": { 73 | "dir": "n", 74 | "dot": { 75 | "label": "edge" 76 | }, 77 | "name": "edge" 78 | } 79 | }, 80 | { 81 | "v": "x0y1", 82 | "w": "x1y1", 83 | "value": { 84 | "dir": "e", 85 | "dot": { 86 | "label": "edge" 87 | }, 88 | "name": "edge" 89 | } 90 | }, 91 | { 92 | "v": "x0y1", 93 | "w": "x0y0", 94 | "value": { 95 | "dir": "s", 96 | "dot": { 97 | "label": "edge" 98 | }, 99 | "name": "edge" 100 | } 101 | }, 102 | { 103 | "v": "x1y0", 104 | "w": "x1y1", 105 | "value": { 106 | "dir": "n", 107 | "dot": { 108 | "label": "edge" 109 | }, 110 | "name": "edge" 111 | } 112 | }, 113 | { 114 | "v": "x1y0", 115 | "w": "x0y0", 116 | "value": { 117 | "dir": "w", 118 | "dot": { 119 | "label": "edge" 120 | }, 121 | "name": "edge" 122 | } 123 | }, 124 | { 125 | "v": "x1y1", 126 | "w": "x0y1", 127 | "value": { 128 | "dir": "w", 129 | "dot": { 130 | "label": "edge" 131 | }, 132 | "name": "edge" 133 | } 134 | }, 135 | { 136 | "v": "x1y1", 137 | "w": "x1y0", 138 | "value": { 139 | "dir": "s", 140 | "dot": { 141 | "label": "edge" 142 | }, 143 | "name": "edge" 144 | } 145 | } 146 | ] 147 | } 148 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # graphgram 2 | More complete documentation is available at the following links: 3 | - [Getting started guide](https://ihh.github.io/graphgram/jsdoc/tutorial-getting-started.html) 4 | - [API documentation](https://ihh.github.io/graphgram/jsdoc/Grammar.html) 5 | - [JSON schema documentation](https://ihh.github.io/graphgram/schema_doc.html) 6 | 7 | `graphgram` is a graph grammar library. 8 | It transforms [graphlib](https://github.com/dagrejs/graphlib) graphs 9 | using a configurable, JSON-described graph grammar 10 | (see e.g. [these slides by Matilde Marcolli](http://www.its.caltech.edu/~matilde/GraphGrammarsLing.pdf), 11 | or [this RPS article](https://www.rockpapershotgun.com/2017/03/10/how-unexplored-generates-great-roguelike-dungeons/) about Joris Dormans' _Unexplored_ (which uses the technique to generate "cyclic" levels), 12 | or [this Wikipedia page](https://en.wikipedia.org/wiki/Graph_rewriting)). 13 | 14 | It can be used for game levels, procedural content, simulations, etc. 15 | 16 | ## Scripts 17 | 18 | - [bin/transform.js](https://github.com/ihh/graphgram/blob/master/bin/transform.js): transform a graph using a graph grammar 19 | - [bin/lattice.js](https://github.com/ihh/graphgram/blob/master/bin/lattice.js): create an N*N square lattice 20 | - [bin/graph2dot.js](https://github.com/ihh/graphgram/blob/master/bin/graph2dot.js): the `transform.js` graphlib-to-Graphviz feature, as a separate script 21 | 22 | ## Example grammars 23 | 24 | - [grammars/dungeon.js](https://github.com/ihh/graphgram/blob/master/grammars/dungeon.js): choose-your-own dungeon 25 | - [grammars/level.js](https://github.com/ihh/graphgram/blob/master/grammars/level.js): a roguelike level (should be initialized with a [square lattice](https://github.com/ihh/graphgram/blob/master/bin/lattice.js)) 26 | - [grammars/test.js](https://github.com/ihh/graphgram/blob/master/grammars/test.js): a test grammar 27 | 28 | ## API usage 29 | 30 | ~~~~ 31 | var graphlib = require('graphlib'), 32 | Grammar = require('../graphgram').Grammar, 33 | fs = require('fs') 34 | 35 | var grammarFile = 'grammars/dungeon.json' 36 | var grammarJson = JSON.parse (fs.readFileSync (grammarFile).toString()) 37 | 38 | var grammar = new Grammar (grammarJson) 39 | var graph = grammar.evolve().graph 40 | ~~~~ 41 | 42 | The resulting `graph` is a [graphlib](https://github.com/dagrejs/graphlib) object. 43 | 44 | ## Command-line usage 45 | 46 |

47 | Usage: node transform.js
48 | 
49 |   -g, --grammar=PATH   read grammar file (default "grammars/dungeon.js")
50 |   -c, --canonical      use canonical schema (no syntactic sugar)
51 |   -j, --schema=PATH    save JSON schema to file
52 |   -C, --canonize=PATH  save canonical grammar to file
53 |   -i, --input=PATH     read graphlib JSON file
54 |   -o, --output=PATH    write graphlib JSON file
55 |   -d, --dot=PATH       write graphviz DOT file
56 |   -L, --limit=N        limit number of rule applications
57 |   -S, --stage=N        only run one stage
58 |   -s, --seed=N         seed random number generator
59 |   -q, --quiet          do not print pretty log messages
60 |   -v, --verbose        print MORE pretty log messages
61 |   -h, --help           display this help message
62 | 
63 | 
64 | -------------------------------------------------------------------------------- /bin/transform.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require('fs'), 4 | extend = require('extend'), 5 | path = require('path'), 6 | getopt = require('node-getopt'), 7 | graphlib = require('graphlib'), 8 | jsonschema = require('jsonschema'), 9 | colors = require('colors'), 10 | Grammar = require('../index').Grammar 11 | 12 | var defaultGrammarFilename = 'grammars/dungeon.js' 13 | var defaultLLM = 'llm' 14 | 15 | var opt = getopt.create([ 16 | ['g' , 'grammar=PATH' , 'read grammar file (default "' + defaultGrammarFilename + '")'], 17 | ['c' , 'canonical' , 'use canonical schema (no syntactic sugar)'], 18 | ['j' , 'schema=PATH' , 'save JSON schema to file'], 19 | ['C' , 'canonize=PATH' , 'save canonical grammar to file'], 20 | ['i' , 'input=PATH' , 'read graphlib JSON file'], 21 | ['o' , 'output=PATH' , 'write graphlib JSON file'], 22 | ['d' , 'dot=PATH' , 'write graphviz DOT file'], 23 | ['L' , 'limit=N' , 'limit number of rule applications'], 24 | ['S' , 'stage=N' , 'only run one stage'], 25 | ['m' , 'llm=COMMAND' , 'command-line interface to LLM (default "' + defaultLLM + '")'], 26 | ['s' , 'seed=N' , 'seed random number generator'], 27 | ['q' , 'quiet' , 'do not print pretty log messages'], 28 | ['v' , 'verbose' , 'print MORE pretty log messages'], 29 | ['h' , 'help' , 'display this help message'] 30 | ]) // create Getopt instance 31 | .bindHelp() // bind option 'help' to default action 32 | .parseSystem() // parse command line 33 | 34 | var verbosity = opt.options.quiet ? 0 : (opt.options.verbose ? 2 : 1) 35 | var grammarOpts = { canonical: opt.options.canonical, llm: opt.options.llm || defaultLLM, verbose: verbosity } 36 | function makeGrammar (json) { 37 | let g = new Grammar (json, grammarOpts) 38 | g.registerRhsLabelExecFunction ('llm', opt.options.llm || 'llm', "Generate text using command-line LLM interface. This option is only enabled in the command-line tool. The default LLM toolname is llm, which must be separately installed: https://github.com/simonw/llm", true) 39 | return g 40 | } 41 | 42 | if (opt.options.schema) { 43 | fs.writeFileSync (opt.options.schema, JSON.stringify (makeGrammar(null).makeSchema(), null, 2)) 44 | process.exit() 45 | } 46 | 47 | var grammarFilename = opt.options.grammar || defaultGrammarFilename 48 | var grammarText = fs.readFileSync(grammarFilename).toString() 49 | var grammarJson = eval ('(' + grammarText + ')') 50 | var grammar = makeGrammar (grammarJson) 51 | 52 | if (opt.options.canonize) 53 | fs.writeFileSync (opt.options.canonize, JSON.stringify (grammar.canonicalJson(), null, 2)) 54 | 55 | var graph 56 | if (opt.options.input) 57 | graph = graphlib.json.read (JSON.parse (fs.readFileSync (opt.options.input))) 58 | 59 | var seed = opt.options.seed 60 | if (typeof(seed) === 'undefined') { 61 | seed = new Date().getTime() 62 | console.warn ("Random number seed: " + seed) 63 | } 64 | 65 | var info = grammar.evolve ({ graph: graph, 66 | verbose: verbosity, 67 | limit: opt.options.limit, 68 | stage: opt.options.stage, 69 | seed: seed }) 70 | graph = info.graph 71 | 72 | var dotFilename = opt.options.dot 73 | if (dotFilename) 74 | fs.writeFileSync (dotFilename, grammar.toDot(graph)) 75 | 76 | var output = JSON.stringify (graphlib.json.write (graph), null, 2) 77 | if (opt.options.output) 78 | fs.writeFileSync (opt.options.output, output) 79 | else if (!dotFilename) 80 | console.log (output) 81 | 82 | -------------------------------------------------------------------------------- /bin/lattice.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require('fs'), 4 | getopt = require('node-getopt'), 5 | graphlib = require('graphlib'), 6 | Grammar = require('../index').Grammar 7 | 8 | var defaultSize = 4 9 | var defaultNodeName = 'node', defaultInitNodeName = 'init', defaultEdgeName = 'edge' 10 | 11 | var opt = getopt.create([ 12 | ['s' , 'size=N' , 'specify size of lattice (default: ' + defaultSize + ')'], 13 | ['u' , 'undirected' , 'undirected: single edges between adjacent nodes'], 14 | ['p' , 'periodic' , 'periodic boundary conditions'], 15 | ['e' , 'edge=STRING' , 'edge name'], 16 | ['n' , 'node=STRING' , 'node name'], 17 | ['i' , 'initial=STRING' , 'initial node name'], 18 | ['d' , 'dot' , 'graphviz dot output'], 19 | ['v' , 'verbose' , 'print debugging messages'], 20 | ['h' , 'help' , 'display this help message'] 21 | ]) // create Getopt instance 22 | .bindHelp() // bind option 'help' to default action 23 | .parseSystem() // parse command line 24 | 25 | var size = parseInt(opt.options.size) || defaultSize 26 | var periodic = opt.options.periodic 27 | var isDirected = !opt.options.undirected 28 | 29 | if (periodic && size <= 2) { 30 | throw new Error ("Periodic boundary conditions on a 2x2 lattice yield a multigraph. Declining to output this.") 31 | } 32 | 33 | var g = new graphlib.Graph ({ directed: isDirected }) 34 | 35 | var xInit, yInit 36 | if (periodic) 37 | xInit = yInit = 0 38 | else 39 | xInit = yInit = Math.floor (size / 2) 40 | 41 | // nodes 42 | function xy(x,y) { 43 | x = (x + size) % size 44 | y = (y + size) % size 45 | return 'x' + x + 'y' + y 46 | } 47 | 48 | var nodePoints = 128 // size of a node, in points, for (neato) layout 49 | for (var x = 0; x < size; ++x) 50 | for (var y = 0; y < size; ++y) { 51 | var name = ((x === xInit && y === yInit) 52 | ? (opt.options.initial || defaultInitNodeName) 53 | : (opt.options.node || defaultNodeName)) 54 | g.setNode (xy(x,y), { x: x, 55 | y: y, 56 | dot: { pos: (x*nodePoints) + ',' + ((size-1-y)*nodePoints), 57 | label: name }, 58 | name: name }) 59 | } 60 | 61 | // edges 62 | var edgeName = opt.options.edge || defaultEdgeName 63 | function addEdge (src, dest, label) { 64 | if (opt.options.verbose) 65 | console.warn ('adding edge from ' + src + ' to ' + dest + ': ' + JSON.stringify(label)) 66 | g.setEdge (src, dest, label) 67 | } 68 | for (var x = 0; x < size; ++x) 69 | for (var y = 0; y < size; ++y) { 70 | 71 | if (x + 1 < size || periodic) 72 | addEdge (xy(x,y), 73 | xy(x+1,y), 74 | { dir: isDirected ? 'e' : 'h', 75 | dot: { label: edgeName }, 76 | name: edgeName }) 77 | 78 | if (y + 1 < size || periodic) 79 | addEdge (xy(x,y), 80 | xy(x,y+1), 81 | { dir: isDirected ? 's' : 'v', 82 | dot: { label: edgeName }, 83 | name: edgeName }) 84 | 85 | if (isDirected) { 86 | if (x > 0 || periodic) 87 | addEdge (xy(x,y), 88 | xy(x-1,y), 89 | { dir: 'w', 90 | dot: { label: edgeName }, 91 | name: edgeName }) 92 | 93 | if (y > 0 || periodic) 94 | addEdge (xy(x,y), 95 | xy(x,y-1), 96 | { dir: 'n', 97 | dot: { label: edgeName }, 98 | name: edgeName }) 99 | } 100 | } 101 | 102 | // output 103 | if (opt.options.dot) 104 | console.log (Grammar.prototype.toDot(g)) 105 | else 106 | console.log (JSON.stringify (graphlib.json.write (g), null, 2)) 107 | -------------------------------------------------------------------------------- /grammars/dungeon.js: -------------------------------------------------------------------------------- 1 | // https://www.reddit.com/r/DnD/comments/20cges/classic_tropes_of_dd/ 2 | { 3 | name: 'dungeon-grammar', 4 | start: 'START', 5 | stages: 6 | [{ name: 'generation-stage', 7 | rules: 8 | [{ lhs: 'START', rhs: ['entrance', 'x', 'boss'] }, 9 | { lhs: 'x', rhs: ['x','x'], limit: 3 }, 10 | { lhs: 'x', rhs: { node: ['fork', 'x', 'die', 'x'], edge: [[0,1],[1,2],[0,3]] }, type: 'ending', limit: 3 }, 11 | { lhs: 'x', rhs: { node: ['fork', 'x', 'live', 'x'], edge: [[0,1],[1,2],[0,3]] }, type: 'ending', limit: 3 }, 12 | { lhs: 'x', rhs: { node: ['fork', 'x', 'x', 'x'], edge: [[0,1],[0,2],[1,3],[2,3]] }, type: 'fork', limit: 2, delay: 2 }, 13 | { lhs: 'x', rhs: { node: ['crossroads', 'x', 'x', 'x', 'x'], edge: [[0,1],[0,2],[0,3],[1,4],[2,4],[3,4]] }, type: 'fork', limit: 1, delay: 2 }, 14 | { lhs: 'x', rhs: { node: ['door', 'x1', 'x'], edge: [[0,1,'enter'],[1,2,'exit'],[0,2,'bypass']] }, limit: 3 }, 15 | { lhs: 'x', rhs: { node: ['fork', 'x', 'x', 'x', 'rescue', 'x', 'x'], edge: [[0,1],[1,2],[2,3],[0,3],[3,4],[4,5],[5,6],[3,6],[1,4,'rumor']] }, type: 'rescue', limit: 1 }, 16 | { lhs: 'x', rhs: { node: ['door', 'x1', 'x', 'rescue', 'x', 'x'], edge: [[0,1,'enter'],[1,2,'exit'],[0,2,'bypass'],[2,3],[3,4],[4,5],[2,5],[1,3,'rumor']] }, type: 'rescue', limit: 1 }, 17 | { lhs: 'x', rhs: { node: ['chest', 'chest_contents', 'x'], edge: [[0,1,'open'],[1,2],[0,2,'ignore']] }, limit: 3 }, 18 | { lhs: 'chest_contents', rhs: 'trap', weight: 2 }, 19 | { lhs: 'chest_contents', rhs: 'treasure' }, 20 | { lhs: 'chest_contents', rhs: 'weapon' }, 21 | { lhs: 'x', rhs: { node: ['vial', 'vial_contents', 'x'], edge: [[0,1,'drink'],[1,2],[0,2,'ignore']] }, limit: 3 }, 22 | { lhs: 'vial_contents', rhs: 'potion' }, 23 | { lhs: 'vial_contents', rhs: 'poison' }, 24 | { lhs: 'x', rhs: 'x1', delay: 10 }, 25 | { lhs: 'x1', rhs: 'trap' }, 26 | { lhs: 'x1', rhs: 'monster' }, 27 | { lhs: 'x1', rhs: 'weapon', limit: 2 }, 28 | { lhs: 'x1', rhs: 'treasure', limit: 3 }, 29 | { lhs: 'x1', rhs: 'scenery', weight: 2 } 30 | ] 31 | }, 32 | 33 | // Second stage is mostly here to illustrate graphviz styling and JSON pattern-matching 34 | // This is pretty advanced usage, the whole stage can be deleted without substantially changing anything 35 | { name: 'decoration-stage', 36 | rules: 37 | // First rule gives 'rumor' edges dotted-line styling in graphviz 38 | // Note that this rule drastically slows things down, 39 | // since it matches every edge in the graph at every iteration; 40 | // that is why we put these decoration rules in a separate stage. 41 | [{ name: 'dot-rumor-edge', 42 | lhs: { node: [{id:'a'},{id:'b'}], edge: [['a','b','rumor']] }, 43 | rhs: { node: [{id:'a'},{id:'b'}], edge: [['a','b',{dot:{label:'rumor',style:'dotted'}}]] } }, 44 | // This rule flags endpoints 45 | { name: 'flag-endpoints', 46 | lhs: '(die|live|boss)', // first node on LHS automatically assigned id 'a' 47 | rhs: { node: [{ label: { endpoint: '${a.match[1]}' } }] } }, 48 | // This rule gives previously-flagged endpoints a rectangular node-shape styling in graphviz 49 | // The '$equals' ensures that the JSON is an exact match at that level, and prevents repeat applications 50 | { name: 'make-endpoints-rectangular', 51 | lhs: { node: [{ id: 'a', label: { $equals: { endpoint: '.*' } }}] }, 52 | // lhs: { node: [{ id: 'a', label: { $and: [{ endpoint: '.*' }, {$test:'(label)=>!label.dot'}]}}] }, 53 | rhs: { node: [{ id: 'a', update: { dot: { label: '${a.match.endpoint[0]}', shape: 'rect' } } }] } } 54 | ] 55 | } 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /out/global.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Global 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Global

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | 31 |

32 | 33 | 34 |
35 | 36 |
37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
78 | 79 | 80 | 81 | 82 |
83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 |

Type Definitions

102 | 103 | 104 | 105 |

EvolveReturnValue

106 | 107 | 108 | 109 | 110 | 111 | 112 |
Type:
113 |
    114 |
  • 115 | 116 | Object 117 | 118 | 119 |
  • 120 |
121 | 122 | 123 | 124 | 125 | 126 |
Properties:
127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 |
NameTypeDescription
iterations 156 | 157 | 158 | Integer 159 | 160 | 161 | 162 |

The number of times a transformation rule was applied

graph 179 | 180 | 181 | Graph 182 | 183 | 184 | 185 |

The transformed graphlib graph

197 | 198 | 199 | 200 | 201 |
202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 |
Source:
229 |
232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 |
240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 |
251 | 252 |
253 | 254 | 255 | 256 | 257 |
258 | 259 | 262 | 263 |
264 | 265 |
266 | Documentation generated by JSDoc 4.0.2 on Tue Oct 10 2023 10:45:21 GMT-0700 (Pacific Daylight Time) 267 |
268 | 269 | 270 | 271 | 272 | -------------------------------------------------------------------------------- /docs/jsdoc/global.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Global 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Global

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | 31 |

32 | 33 | 34 |
35 | 36 |
37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
78 | 79 | 80 | 81 | 82 |
83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 |

Type Definitions

102 | 103 | 104 | 105 |

EvolveReturnValue

106 | 107 | 108 | 109 | 110 | 111 | 112 |
Type:
113 |
    114 |
  • 115 | 116 | Object 117 | 118 | 119 |
  • 120 |
121 | 122 | 123 | 124 | 125 | 126 |
Properties:
127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 |
NameTypeDescription
iterations 156 | 157 | 158 | Integer 159 | 160 | 161 | 162 |

The number of times a transformation rule was applied

graph 179 | 180 | 181 | Graph 182 | 183 | 184 | 185 |

The transformed graphlib graph

197 | 198 | 199 | 200 | 201 |
202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 |
Source:
229 |
232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 |
240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 |
251 | 252 |
253 | 254 | 255 | 256 | 257 |
258 | 259 | 262 | 263 |
264 | 265 |
266 | Documentation generated by JSDoc 4.0.2 on Wed Oct 11 2023 15:40:44 GMT-0700 (Pacific Daylight Time) 267 |
268 | 269 | 270 | 271 | 272 | -------------------------------------------------------------------------------- /subgraph.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | extend = require('extend'), 3 | graphlib = require('graphlib') 4 | 5 | // Implementation of Ullmann (1976) 6 | // via http://stackoverflow.com/questions/13537716/how-to-partially-compare-two-graphs/13537776#13537776 7 | function SubgraphSearch (graph, subgraph, opts) { 8 | opts = opts || {} 9 | extend (this, { graph, subgraph }) 10 | this.mapping = { assign: {}, label: {}, match: {} } 11 | this.subnodes = subgraph.nodes() 12 | this.subedges = subgraph.edges() 13 | 14 | this.labelMatch = opts.labelMatch || function(gLabel,sLabel) { return gLabel === sLabel } 15 | this.nodeLabelMatch = opts.nodeLabelMatch || this.labelMatch 16 | this.edgeLabelMatch = opts.edgeLabelMatch || this.labelMatch 17 | 18 | var possibleAssignments = {} 19 | this.subnodes.forEach (function (sid) { 20 | var pa = {} 21 | graph.nodes().forEach (function (gid) { 22 | pa[gid] = true 23 | }) 24 | possibleAssignments[sid] = pa 25 | }) 26 | 27 | this.isomorphisms = this.search (possibleAssignments) 28 | } 29 | 30 | SubgraphSearch.prototype.testEdgeMatch = function (v, w, label) { 31 | return this.graph.hasEdge(v,w) && this.edgeLabelMatch (this.graph.edge(v,w), label) 32 | } 33 | 34 | SubgraphSearch.prototype.updatePossibleAssignments = function (possibleAssignments) { 35 | var search = this, subgraph = this.subgraph 36 | var changed 37 | do { 38 | changed = false 39 | this.subnodes.forEach (function (i) { 40 | Object.keys(possibleAssignments[i]).forEach (function (j) { 41 | var pred = subgraph.predecessors(j), succ = subgraph.successors(j) 42 | if (succ) 43 | succ.forEach (function (x) { 44 | var foundMatch = false, label = subgraph.edge(i,x) 45 | Object.keys(possibleAssignments[x]).forEach (function (y) { 46 | foundMatch = foundMatch || search.testEdgeMatch (j, y, label) 47 | }) 48 | if (!foundMatch) { 49 | delete possibleAssignments[i][j] 50 | changed = true 51 | } 52 | }) 53 | if (pred) 54 | pred.forEach (function (x) { 55 | var foundMatch = false, label = subgraph.edge(x,i) 56 | Object.keys(possibleAssignments[x]).forEach (function (y) { 57 | foundMatch = foundMatch || search.testEdgeMatch (y, j, label) 58 | }) 59 | if (!foundMatch) { 60 | delete possibleAssignments[i][j] 61 | changed = true 62 | } 63 | }) 64 | }) 65 | }) 66 | } while (changed) 67 | } 68 | 69 | SubgraphSearch.prototype.search = function (possibleAssignments) { 70 | var ss = this, mapping = this.mapping, graph = this.graph, subgraph = this.subgraph, subnodes = this.subnodes, subedges = this.subedges 71 | this.updatePossibleAssignments (possibleAssignments) 72 | var nAssigned = Object.keys(mapping.assign).length 73 | var edgeMatch 74 | if (nAssigned) { 75 | var edgeNotFound = false 76 | edgeMatch = subedges.map (function (edge) { 77 | var match 78 | if (!edgeNotFound && mapping.assign[edge.v] && mapping.assign[edge.w]) { 79 | match = ss.testEdgeMatch (mapping.assign[edge.v], mapping.assign[edge.w], subgraph.edge(edge)) 80 | if (!match) 81 | edgeNotFound = edge || true 82 | } 83 | return match 84 | }) 85 | if (edgeNotFound) 86 | return [] 87 | } 88 | if (nAssigned == subnodes.length) { 89 | var result = _.cloneDeep(mapping) 90 | result.edgeMatch = edgeMatch 91 | return [result] 92 | } 93 | var nextToAssign = subnodes[nAssigned] 94 | var sLabel = subgraph.node(nextToAssign) 95 | var results = [] 96 | Object.keys(possibleAssignments[nextToAssign]).forEach (function (j) { 97 | var jUsed = false 98 | Object.keys(mapping.assign).forEach (function (i) { 99 | if (mapping.assign[i] === j) 100 | jUsed = true 101 | }) 102 | if (!jUsed) { 103 | var gLabel = graph.node(j) 104 | var match = ss.nodeLabelMatch (gLabel, sLabel) 105 | if (match) { 106 | mapping.label[nextToAssign] = gLabel 107 | mapping.match[nextToAssign] = match 108 | mapping.assign[nextToAssign] = j 109 | var newPossibleAssignments = _.cloneDeep (possibleAssignments) 110 | newPossibleAssignments[nextToAssign] = {} 111 | newPossibleAssignments[nextToAssign][j] = true 112 | results = results.concat (ss.search (newPossibleAssignments)) 113 | delete mapping.assign[nextToAssign] 114 | delete mapping.match[nextToAssign] 115 | delete mapping.label[nextToAssign] 116 | delete possibleAssignments[nextToAssign][j] 117 | ss.updatePossibleAssignments (possibleAssignments) 118 | } 119 | } 120 | }) 121 | return results 122 | } 123 | 124 | module.exports = { SubgraphSearch } 125 | -------------------------------------------------------------------------------- /bin/emojis.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const fs = require('fs'), 4 | extend = require('extend'), 5 | path = require('path'), 6 | MersenneTwister = require('mersennetwister'), 7 | getopt = require('node-getopt'), 8 | emojiLib = require('emojilib'), 9 | emojiSearch = require('@jukben/emoji-search').default, 10 | Bracery = require('bracery').Bracery 11 | 12 | // Bracery dialect: 13 | // For node labels, $node is set to the node ID ($src, $dest are empty) 14 | // For edge labels, $src and $dest are the end node IDs ($node is empty) 15 | // #keyword gets replaced with a random emoji that matches keyword 16 | // @name gets replaced with a specific named emoji 17 | 18 | const defaultEmojis = { 19 | theme: { 20 | standard: { 21 | rules: {}, 22 | labels: { 23 | live: '#happy', 24 | enter: '[@thinking|@wave|@tickets|@arrow_forward]', 25 | ignore: '[@expressionless|@running_man|@running_woman]', 26 | bypass: '[@eyes|@roll_eyes|@unamused|@no_entry]', 27 | scenery: '[#building|#plant|#flower|#nature]', 28 | x: '#nature', 29 | crossroads: '[crossroads|junction][#plus|#x|#road]', 30 | fork: '[#y|#road]', 31 | rumor: '#secret', 32 | treasure: '#money', 33 | door: '@door' 34 | } 35 | } 36 | } 37 | }; 38 | 39 | const opt = getopt.create([ 40 | ['d' , 'dot=PATH' , 'GraphViz file (required)'], 41 | ['e' , 'emojis=PATH' , 'JSON emojis file (optional)'], 42 | ['i' , 'import=PATH' , 'JSON file from Bracery import directory (optional)'], 43 | ['t' , 'theme=STRING', 'theme from emojis file (optional)'], 44 | ['k' , 'keep' , 'keep original label'], 45 | ['f' , 'fitzpatrick' , 'use Fitzpatrick modifiers'], 46 | ['s' , 'seed=N' , 'seed random number generator'], 47 | ['h' , 'help' , 'display this help message'] 48 | ]) // create Getopt instance 49 | .bindHelp() // bind option 'help' to default action 50 | .parseSystem() // parse command line 51 | 52 | const dotFile = opt.options.dot 53 | if (!dotFile) 54 | throw new Error ("Please specify --dot file") 55 | 56 | const emojisFile = opt.options.emojis 57 | const emojis = (emojisFile 58 | ? eval ('(()=>{return (' + fs.readFileSync(emojisFile).toString() + ')})()') 59 | : defaultEmojis) 60 | 61 | let seed = opt.options.seed 62 | if (typeof(seed) === 'undefined') { 63 | seed = new Date().getTime() 64 | console.warn ("Random number seed: " + seed) 65 | } 66 | const mt = new MersenneTwister (seed) 67 | const randElement = (array) => array[Math.floor (mt.rnd() * array.length)]; 68 | 69 | const theme = emojis.theme[opt.options.theme || randElement (Object.keys(emojis.theme))] 70 | let extraRules = {} 71 | if (opt.options.import) { 72 | const imported = JSON.parse(fs.readFileSync(opt.options.import).toString()) 73 | imported.forEach ((entry) => { extraRules[entry.name] = entry.rules.map (rhs => rhs.join("")) }) 74 | } 75 | const bracery = new Bracery (extend (theme.rules, extraRules)) 76 | 77 | const toEmoji = (str) => { 78 | let result = str 79 | const emoji = (str[0] === '@' 80 | ? emojiLib.lib[str.substr(1)] 81 | : randElement (emojiSearch (str.substr(1)).filter ((emoji) => emoji.keywords.filter ((keyword) => keyword === str.substr(1)).length))) 82 | if (emoji) { 83 | result = emoji.char 84 | if (opt.options.fitzpatrick && emoji.fitzpatrick_scale) { 85 | const mod = randElement (emojiLib.fitzpatrick_scale_modifiers) 86 | const modResult = result + mod 87 | result = modResult.length < 6 ? modResult : result // hack to avoid double characters 88 | } 89 | } 90 | return result 91 | } 92 | 93 | let vars = {} 94 | const expandBracery = (template) => { 95 | let expansion = bracery.expand(template,{vars}) 96 | vars = expansion.vars 97 | return expansion.text.replace (/([@#][a-z_0-9\-]+)/g, toEmoji) 98 | } 99 | expandBracery ('~init') 100 | 101 | const labelRegex = /label="(.*?)"/g; 102 | const nodeRegex = /^ *([0-9]+) *\[/; 103 | const edgeRegex = /^ *([0-9]+) *-> *([0-9]+) *\[/; 104 | fs.readFileSync(dotFile).toString() 105 | .split ("\n") 106 | .forEach ((line) => { 107 | let node = '', src = '', dest = '', type, match 108 | if (match = edgeRegex.exec(line)) { 109 | src = match[1]; 110 | dest = match[2]; 111 | type = 'taillabel' 112 | } else if (match = nodeRegex.exec(line)) { 113 | node = match[1]; 114 | type = 'label' 115 | } 116 | line = line.replace (labelRegex, (_m, label) => { 117 | vars = extend (vars, { node, src, dest }); 118 | const expansion = label && expandBracery (theme.labels[label] || ('#' + label)) 119 | console.warn ("Replacing " + label + " with " + expansion) 120 | return type + '="' + (opt.options.keep ? label : "") + expansion + '"' 121 | }) 122 | console.log (line) 123 | }) 124 | -------------------------------------------------------------------------------- /graphs/lev4.json: -------------------------------------------------------------------------------- 1 | { 2 | "options": { 3 | "directed": true, 4 | "multigraph": false, 5 | "compound": false 6 | }, 7 | "nodes": [ 8 | { 9 | "v": "7", 10 | "value": { 11 | "name": "room", 12 | "dot": { 13 | "pos": "384,0" 14 | } 15 | } 16 | }, 17 | { 18 | "v": "11", 19 | "value": { 20 | "name": "room", 21 | "dot": { 22 | "pos": "256,0" 23 | } 24 | } 25 | }, 26 | { 27 | "v": "20", 28 | "value": { 29 | "name": "room", 30 | "dot": { 31 | "pos": "384,256" 32 | } 33 | } 34 | }, 35 | { 36 | "v": "22", 37 | "value": { 38 | "name": "room", 39 | "dot": { 40 | "pos": "384,384" 41 | } 42 | } 43 | }, 44 | { 45 | "v": "24", 46 | "value": { 47 | "name": "room", 48 | "dot": { 49 | "pos": "256,384" 50 | } 51 | } 52 | }, 53 | { 54 | "v": "28", 55 | "value": { 56 | "name": "room", 57 | "dot": { 58 | "pos": "128,384" 59 | } 60 | } 61 | }, 62 | { 63 | "v": "30", 64 | "value": { 65 | "name": "key", 66 | "dot": { 67 | "pos": "0,384" 68 | } 69 | } 70 | }, 71 | { 72 | "v": "33", 73 | "value": { 74 | "name": "start", 75 | "dot": { 76 | "pos": "256,256" 77 | } 78 | } 79 | }, 80 | { 81 | "v": "41", 82 | "value": { 83 | "name": "room", 84 | "dot": { 85 | "pos": "0,256" 86 | } 87 | } 88 | }, 89 | { 90 | "v": "42", 91 | "value": { 92 | "name": "room", 93 | "dot": { 94 | "pos": "0,128" 95 | } 96 | } 97 | }, 98 | { 99 | "v": "43", 100 | "value": { 101 | "name": "room", 102 | "dot": { 103 | "pos": "384,128" 104 | } 105 | } 106 | }, 107 | { 108 | "v": "44", 109 | "value": { 110 | "name": "end", 111 | "endpoint": true, 112 | "dot": { 113 | "pos": "256,128" 114 | } 115 | } 116 | }, 117 | { 118 | "v": "45", 119 | "value": { 120 | "name": "herring", 121 | "endpoint": true, 122 | "dot": { 123 | "pos": "0,0" 124 | } 125 | } 126 | }, 127 | { 128 | "v": "46", 129 | "value": { 130 | "name": "room", 131 | "dot": { 132 | "pos": "128,0" 133 | } 134 | } 135 | }, 136 | { 137 | "v": "49", 138 | "value": { 139 | "name": "room", 140 | "dot": { 141 | "pos": "128,128" 142 | } 143 | } 144 | }, 145 | { 146 | "v": "50", 147 | "value": { 148 | "name": "room", 149 | "dot": { 150 | "pos": "128,256" 151 | } 152 | } 153 | } 154 | ], 155 | "edges": [ 156 | { 157 | "v": "7", 158 | "w": "11", 159 | "value": { 160 | "name": "path" 161 | } 162 | }, 163 | { 164 | "v": "22", 165 | "w": "20", 166 | "value": { 167 | "name": "path" 168 | } 169 | }, 170 | { 171 | "v": "24", 172 | "w": "22", 173 | "value": { 174 | "name": "path" 175 | } 176 | }, 177 | { 178 | "v": "28", 179 | "w": "24", 180 | "value": { 181 | "name": "path" 182 | } 183 | }, 184 | { 185 | "v": "33", 186 | "w": "20", 187 | "value": { 188 | "name": "shortcut", 189 | "dot": { 190 | "style": "dotted" 191 | } 192 | } 193 | }, 194 | { 195 | "v": "33", 196 | "w": "24", 197 | "value": { 198 | "name": "shortcut", 199 | "dot": { 200 | "style": "dotted" 201 | } 202 | } 203 | }, 204 | { 205 | "v": "41", 206 | "w": "30", 207 | "value": { 208 | "name": "path" 209 | } 210 | }, 211 | { 212 | "v": "43", 213 | "w": "7", 214 | "value": { 215 | "name": "path" 216 | } 217 | }, 218 | { 219 | "v": "20", 220 | "w": "43", 221 | "value": { 222 | "name": "path" 223 | } 224 | }, 225 | { 226 | "v": "11", 227 | "w": "44", 228 | "value": { 229 | "name": "shortcut", 230 | "dot": { 231 | "style": "dotted" 232 | } 233 | } 234 | }, 235 | { 236 | "v": "42", 237 | "w": "45", 238 | "value": { 239 | "name": "path" 240 | } 241 | }, 242 | { 243 | "v": "11", 244 | "w": "46", 245 | "value": { 246 | "name": "path" 247 | } 248 | }, 249 | { 250 | "v": "49", 251 | "w": "42", 252 | "value": { 253 | "name": "path" 254 | } 255 | }, 256 | { 257 | "v": "49", 258 | "w": "44", 259 | "value": { 260 | "name": "path" 261 | } 262 | }, 263 | { 264 | "v": "46", 265 | "w": "49", 266 | "value": { 267 | "name": "path" 268 | } 269 | }, 270 | { 271 | "v": "50", 272 | "w": "28", 273 | "value": { 274 | "name": "lock", 275 | "dot": { 276 | "style": "dotted" 277 | } 278 | } 279 | }, 280 | { 281 | "v": "50", 282 | "w": "41", 283 | "value": { 284 | "name": "path" 285 | } 286 | }, 287 | { 288 | "v": "30", 289 | "w": "50", 290 | "value": { 291 | "name": "valve", 292 | "dot": { 293 | "style": "dashed" 294 | } 295 | } 296 | }, 297 | { 298 | "v": "33", 299 | "w": "50", 300 | "value": { 301 | "name": "path" 302 | } 303 | } 304 | ] 305 | } 306 | -------------------------------------------------------------------------------- /docs/schema_doc.css: -------------------------------------------------------------------------------- 1 | body { 2 | font: 16px/1.5em "Overpass", "Open Sans", Helvetica, sans-serif; 3 | color: #333; 4 | font-weight: 300; 5 | padding: 40px; 6 | } 7 | 8 | .btn.btn-link { 9 | font-size: 18px; 10 | user-select: text; 11 | } 12 | 13 | .jsfh-animated-property { 14 | animation: eclair; 15 | animation-iteration-count: 1; 16 | animation-fill-mode: forwards; 17 | animation-duration: .75s; 18 | 19 | } 20 | 21 | @keyframes eclair { 22 | 0%,100% { 23 | transform: scale(1); 24 | } 25 | 50% { 26 | transform: scale(1.03); 27 | } 28 | } 29 | 30 | .btn.btn-primary { 31 | margin: 10px; 32 | } 33 | 34 | .btn.example-show.collapsed:before { 35 | content: "show" 36 | } 37 | 38 | .btn.example-show:before { 39 | content: "hide" 40 | } 41 | 42 | .description.collapse:not(.show) { 43 | max-height: 100px !important; 44 | overflow: hidden; 45 | 46 | display: -webkit-box; 47 | -webkit-line-clamp: 2; 48 | -webkit-box-orient: vertical; 49 | } 50 | 51 | .description.collapsing { 52 | min-height: 100px !important; 53 | } 54 | 55 | .collapse-description-link.collapsed:after { 56 | content: '+ Read More'; 57 | } 58 | 59 | .collapse-description-link:not(.collapsed):after { 60 | content: '- Read Less'; 61 | } 62 | 63 | .badge { 64 | font-size: 100%; 65 | margin-bottom: 0.5rem; 66 | margin-top: 0.5rem; 67 | } 68 | 69 | .badge.value-type { 70 | font-size: 120%; 71 | margin-right: 5px; 72 | margin-bottom: 10px; 73 | } 74 | 75 | 76 | .badge.default-value { 77 | font-size: 120%; 78 | margin-left: 5px; 79 | margin-bottom: 10px; 80 | } 81 | 82 | .badge.restriction { 83 | display: inline-block; 84 | } 85 | 86 | .badge.required-property,.badge.deprecated-property,.badge.pattern-property,.badge.no-additional { 87 | font-size: 100%; 88 | margin-left: 10px; 89 | } 90 | 91 | .accordion div.card:only-child { 92 | border-bottom: 1px solid rgba(0, 0, 0, 0.125); 93 | } 94 | 95 | .examples { 96 | padding: 1rem !important; 97 | } 98 | 99 | .examples pre { 100 | margin-bottom: 0; 101 | } 102 | 103 | .highlight.jumbotron { 104 | padding: 1rem !important; 105 | } 106 | 107 | .generated-by-footer { 108 | margin-top: 1em; 109 | text-align: right; 110 | } 111 | 112 | /* From https://github.com/richleland/pygments-css/blob/master/friendly.css, see https://github.com/trentm/python-markdown2/wiki/fenced-code-blocks */ 113 | .highlight { background: #e9ecef; } /* Changed from #f0f0f0 in the original style to be the same as bootstrap's jumbotron */ 114 | .highlight .hll { background-color: #ffffcc } 115 | .highlight .c { color: #60a0b0; font-style: italic } /* Comment */ 116 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 117 | .highlight .k { color: #007020; font-weight: bold } /* Keyword */ 118 | .highlight .o { color: #666666 } /* Operator */ 119 | .highlight .ch { color: #60a0b0; font-style: italic } /* Comment.Hashbang */ 120 | .highlight .cm { color: #60a0b0; font-style: italic } /* Comment.Multiline */ 121 | .highlight .cp { color: #007020 } /* Comment.Preproc */ 122 | .highlight .cpf { color: #60a0b0; font-style: italic } /* Comment.PreprocFile */ 123 | .highlight .c1 { color: #60a0b0; font-style: italic } /* Comment.Single */ 124 | .highlight .cs { color: #60a0b0; background-color: #fff0f0 } /* Comment.Special */ 125 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 126 | .highlight .ge { font-style: italic } /* Generic.Emph */ 127 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 128 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 129 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 130 | .highlight .go { color: #888888 } /* Generic.Output */ 131 | .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ 132 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 133 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 134 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 135 | .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ 136 | .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ 137 | .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ 138 | .highlight .kp { color: #007020 } /* Keyword.Pseudo */ 139 | .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ 140 | .highlight .kt { color: #902000 } /* Keyword.Type */ 141 | .highlight .m { color: #40a070 } /* Literal.Number */ 142 | .highlight .s { color: #4070a0 } /* Literal.String */ 143 | .highlight .na { color: #4070a0 } /* Name.Attribute */ 144 | .highlight .nb { color: #007020 } /* Name.Builtin */ 145 | .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ 146 | .highlight .no { color: #60add5 } /* Name.Constant */ 147 | .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ 148 | .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ 149 | .highlight .ne { color: #007020 } /* Name.Exception */ 150 | .highlight .nf { color: #06287e } /* Name.Function */ 151 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ 152 | .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ 153 | .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ 154 | .highlight .nv { color: #bb60d5 } /* Name.Variable */ 155 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ 156 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 157 | .highlight .mb { color: #40a070 } /* Literal.Number.Bin */ 158 | .highlight .mf { color: #40a070 } /* Literal.Number.Float */ 159 | .highlight .mh { color: #40a070 } /* Literal.Number.Hex */ 160 | .highlight .mi { color: #40a070 } /* Literal.Number.Integer */ 161 | .highlight .mo { color: #40a070 } /* Literal.Number.Oct */ 162 | .highlight .sa { color: #4070a0 } /* Literal.String.Affix */ 163 | .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ 164 | .highlight .sc { color: #4070a0 } /* Literal.String.Char */ 165 | .highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */ 166 | .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ 167 | .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ 168 | .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ 169 | .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ 170 | .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ 171 | .highlight .sx { color: #c65d09 } /* Literal.String.Other */ 172 | .highlight .sr { color: #235388 } /* Literal.String.Regex */ 173 | .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ 174 | .highlight .ss { color: #517918 } /* Literal.String.Symbol */ 175 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ 176 | .highlight .fm { color: #06287e } /* Name.Function.Magic */ 177 | .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ 178 | .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ 179 | .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ 180 | .highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */ 181 | .highlight .il { color: #40a070 } /* Literal.Number.Integer.Long */ 182 | -------------------------------------------------------------------------------- /docs/tutorials/getting-started.md: -------------------------------------------------------------------------------- 1 | `graphgram` is a library for generating graphs using a transformative graph grammar. 2 | This is useful if you want random graphs that have predictable structural motifs or patterns in them. 3 | The motivating use case is procedural generation of story paths or maps in interactive fiction (i.e. games), 4 | and the pioneering example of this is Joris Dormans' roguelike _Unexplored_ 5 | as described in [this RPS article](https://www.rockpapershotgun.com/2017/03/10/how-unexplored-generates-great-roguelike-dungeons/). 6 | (A formal mathematical introduction to graph grammars can be found in [these slides by Matilde Marcolli](http://www.its.caltech.edu/~matilde/GraphGrammarsLing.pdf).) 7 | 8 | The way that `graphgram` works is as follows. A basic graph grammar consists of a set of _transformation rules_. 9 | Each such rule contains, at minimum, a _left-hand side_ and a _right-hand side_: the rule has the form _LHS → RHS_. 10 | - The left-hand side is a pattern that is used to match a subgraph of the current graph, using [Ullman's 1976 subgraph isomorphism search algorithm](https://en.wikipedia.org/wiki/Subgraph_isomorphism_problem). 11 | - The right-hand side specifies a subgraph that will be pasted in as a replacement for the matching subgraph. 12 | 13 | Beyond simply matching subgraph topologies, `graphgram` offers considerable flexibility in the way that the LHS pattern-matching occurs. 14 | Each node and edge in the graph can have a _label_ which can be a string or an arbitrary JSON object. 15 | There is a simple query language for matching labels (or the user can define their own label-matching function). 16 | The grammar specification also allows for constraints limiting the number and type of transformation rules that can be applied to generate any single graph. 17 | 18 | The library can be called via the API of the [`Grammar`](Grammar.html) class, or using the command-line tool `bin/transform.js`. 19 | 20 | Grammars are specified using a [JSON schema that is documented here](../schema_doc.html). 21 | 22 | All graphs generated by `graphgram` are directed. 23 | Undirected edges can be represented using two directed edges, one pointing forwards and one backwards. 24 | 25 | ## Dungeon grammar 26 | 27 | The default grammar for the CLI, for demo purposes, is a dungeon-generation grammar that is fairly close to the following: 28 | 29 | ``` 30 | { 31 | name: 'dungeon-grammar', 32 | start: 'START', 33 | rules: 34 | [{ lhs: 'START', rhs: ['entrance', 'x', 'boss'] }, 35 | { lhs: 'x', rhs: ['x','x'], limit: 3 }, 36 | { lhs: 'x', rhs: { node: ['fork', 'x', 'die', 'x'], edge: [[0,1],[1,2],[0,3]] }, type: 'ending', limit: 3 }, 37 | { lhs: 'x', rhs: { node: ['fork', 'x', 'live', 'x'], edge: [[0,1],[1,2],[0,3]] }, type: 'ending', limit: 3 }, 38 | { lhs: 'x', rhs: { node: ['fork', 'x', 'x', 'x'], edge: [[0,1],[0,2],[1,3],[2,3]] }, type: 'fork', limit: 2, delay: 2 }, 39 | { lhs: 'x', rhs: { node: ['crossroads', 'x', 'x', 'x', 'x'], edge: [[0,1],[0,2],[0,3],[1,4],[2,4],[3,4]] }, type: 'fork', limit: 1, delay: 2 }, 40 | { lhs: 'x', rhs: { node: ['door', 'x1', 'x'], edge: [[0,1,'enter'],[1,2,'exit'],[0,2,'bypass']] }, limit: 3 }, 41 | { lhs: 'x', rhs: { node: ['fork', 'x', 'x', 'x', 'rescue', 'x', 'x'], edge: [[0,1],[1,2],[2,3],[0,3],[3,4],[4,5],[5,6],[3,6],[1,4,'rumor']] }, type: 'rescue', limit: 1 }, 42 | { lhs: 'x', rhs: { node: ['door', 'x1', 'x', 'rescue', 'x', 'x'], edge: [[0,1,'enter'],[1,2,'exit'],[0,2,'bypass'],[2,3],[3,4],[4,5],[2,5],[1,3,'rumor']] }, type: 'rescue', limit: 1 }, 43 | { lhs: 'x', rhs: { node: ['chest', 'chest_contents', 'x'], edge: [[0,1,'open'],[1,2],[0,2,'ignore']] }, limit: 3 }, 44 | { lhs: 'chest_contents', rhs: 'trap', weight: 2 }, 45 | { lhs: 'chest_contents', rhs: 'treasure' }, 46 | { lhs: 'chest_contents', rhs: 'weapon' }, 47 | { lhs: 'x', rhs: { node: ['vial', 'vial_contents', 'x'], edge: [[0,1,'drink'],[1,2],[0,2,'ignore']] }, limit: 3 }, 48 | { lhs: 'vial_contents', rhs: 'potion' }, 49 | { lhs: 'vial_contents', rhs: 'poison' }, 50 | { lhs: 'x', rhs: 'x1', delay: 10 }, 51 | { lhs: 'x1', rhs: 'trap' }, 52 | { lhs: 'x1', rhs: 'monster' }, 53 | { lhs: 'x1', rhs: 'weapon', limit: 2 }, 54 | { lhs: 'x1', rhs: 'treasure', limit: 3 }, 55 | { lhs: 'x1', rhs: 'scenery', weight: 2 } 56 | ] 57 | } 58 | ``` 59 | 60 | The full flexibility of matching any subgraph and replacing it with another subgraph of arbitrary topology (possibly with some overlap/reuse of nodes and/or edges from the matched subgraph) can be quite daunting and is probably overkill for most purposes. 61 | In recognition of this, `graphgram` offers a few syntactical shortcuts for common types of transformation rule and graph 62 | (corresponding to sequence grammars, context-free grammars, and so on). 63 | These are fully described in the documentation for the [JSON schema](../schema_doc.html) for the `Grammar`` class. 64 | 65 | One of these syntactical shortcuts is heavily used by the above dungeon grammar: 66 | all the transformation rules in that grammar have a single node on the left-hand side. 67 | This means that the subgraph matching algorithm is not being heavily exercised by this grammar; 68 | it is effectively a context-free node replacement grammar. 69 | 70 | The simplest kind of rule in this grammar has the form: 71 | ``` 72 | { lhs: 'chest_contents', rhs: 'weapon' }, 73 | ``` 74 | This simply means "replace a node whose label is `chest_contents` with a node whose label is `weapon`". 75 | 76 | An example of a slightly more elaborate rule is 77 | ``` 78 | { lhs: 'START', rhs: ['entrance', 'x', 'boss'] } 79 | ``` 80 | This replaces the node labeled `START` with a sequence of three nodes: 81 | `entrance`→`x`→`boss`. 82 | 83 | The edges connecting consecutive nodes are added by default when an array of node labels is given. 84 | This corresponds to inserting a sequence of nodes at a particular point (as in a string transformation grammar). 85 | For an example of a rule where the replacement subgraph is not just a linear chain, so that its topology must be specified explicitly, 86 | consider the following: 87 | ``` 88 | { lhs: 'x', rhs: { node: ['fork', 'x', 'die', 'x'], edge: [[0,1],[1,2],[0,3]] } }, 89 | ``` 90 | 91 | This replaces a node labeled `x` with a subgraph containing four nodes labeled (respectively) 92 | `fork` (node 0), `x` (node 1), `die` (node 2) and `x` (node 3). 93 | The `edge` clause in the `rhs` of this rule contains three tuples of the form `[v,w]` where `v` and `w` represent the indices of 94 | (respectively) the _source_ and _target_ nodes in the replacement subgraph. 95 | Thus, the subgraph contains a `fork` node that has edges to two new `x` nodes, the first of which then leads to a `die` node. 96 | The first node on the RHS inherits the edges that are incoming to the LHS node, and the last node on the RHS inherits its outgoing edges 97 | (this behavior can be overridden, as described in the JSON schema). 98 | 99 | The actual rule that appears in the example grammar is slightly different from this: 100 | ``` 101 | { lhs: 'x', rhs: { node: ['fork', 'x', 'die', 'x'], edge: [[0,1],[1,2],[0,3]] }, type: 'ending', limit: 3 }, 102 | ``` 103 | The `type` and `limit` fields indicate that this rule has type "`ending`" and can only be used if fewer than 3 rules with that type have already been applied. 104 | 105 | For examples of more sophisticated subgraph replacement rules that include context and nontrivial topology on the LHS, 106 | as well as compound expressions for matching/replacing node and edge labels, 107 | see the [level.js](https://github.com/ihh/graphgram/blob/master/grammars/level.js) example grammar 108 | which generates levels for the "roguelike snakelike" game [nemato.de](http://nemato.de/). 109 | -------------------------------------------------------------------------------- /out/styles/jsdoc-default.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Open Sans'; 3 | font-weight: normal; 4 | font-style: normal; 5 | src: url('../fonts/OpenSans-Regular-webfont.eot'); 6 | src: 7 | local('Open Sans'), 8 | local('OpenSans'), 9 | url('../fonts/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'), 10 | url('../fonts/OpenSans-Regular-webfont.woff') format('woff'), 11 | url('../fonts/OpenSans-Regular-webfont.svg#open_sansregular') format('svg'); 12 | } 13 | 14 | @font-face { 15 | font-family: 'Open Sans Light'; 16 | font-weight: normal; 17 | font-style: normal; 18 | src: url('../fonts/OpenSans-Light-webfont.eot'); 19 | src: 20 | local('Open Sans Light'), 21 | local('OpenSans Light'), 22 | url('../fonts/OpenSans-Light-webfont.eot?#iefix') format('embedded-opentype'), 23 | url('../fonts/OpenSans-Light-webfont.woff') format('woff'), 24 | url('../fonts/OpenSans-Light-webfont.svg#open_sanslight') format('svg'); 25 | } 26 | 27 | html 28 | { 29 | overflow: auto; 30 | background-color: #fff; 31 | font-size: 14px; 32 | } 33 | 34 | body 35 | { 36 | font-family: 'Open Sans', sans-serif; 37 | line-height: 1.5; 38 | color: #4d4e53; 39 | background-color: white; 40 | } 41 | 42 | a, a:visited, a:active { 43 | color: #0095dd; 44 | text-decoration: none; 45 | } 46 | 47 | a:hover { 48 | text-decoration: underline; 49 | } 50 | 51 | header 52 | { 53 | display: block; 54 | padding: 0px 4px; 55 | } 56 | 57 | tt, code, kbd, samp { 58 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 59 | } 60 | 61 | .class-description { 62 | font-size: 130%; 63 | line-height: 140%; 64 | margin-bottom: 1em; 65 | margin-top: 1em; 66 | } 67 | 68 | .class-description:empty { 69 | margin: 0; 70 | } 71 | 72 | #main { 73 | float: left; 74 | width: 70%; 75 | } 76 | 77 | article dl { 78 | margin-bottom: 40px; 79 | } 80 | 81 | article img { 82 | max-width: 100%; 83 | } 84 | 85 | section 86 | { 87 | display: block; 88 | background-color: #fff; 89 | padding: 12px 24px; 90 | border-bottom: 1px solid #ccc; 91 | margin-right: 30px; 92 | } 93 | 94 | .variation { 95 | display: none; 96 | } 97 | 98 | .signature-attributes { 99 | font-size: 60%; 100 | color: #aaa; 101 | font-style: italic; 102 | font-weight: lighter; 103 | } 104 | 105 | nav 106 | { 107 | display: block; 108 | float: right; 109 | margin-top: 28px; 110 | width: 30%; 111 | box-sizing: border-box; 112 | border-left: 1px solid #ccc; 113 | padding-left: 16px; 114 | } 115 | 116 | nav ul { 117 | font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif; 118 | font-size: 100%; 119 | line-height: 17px; 120 | padding: 0; 121 | margin: 0; 122 | list-style-type: none; 123 | } 124 | 125 | nav ul a, nav ul a:visited, nav ul a:active { 126 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 127 | line-height: 18px; 128 | color: #4D4E53; 129 | } 130 | 131 | nav h3 { 132 | margin-top: 12px; 133 | } 134 | 135 | nav li { 136 | margin-top: 6px; 137 | } 138 | 139 | footer { 140 | display: block; 141 | padding: 6px; 142 | margin-top: 12px; 143 | font-style: italic; 144 | font-size: 90%; 145 | } 146 | 147 | h1, h2, h3, h4 { 148 | font-weight: 200; 149 | margin: 0; 150 | } 151 | 152 | h1 153 | { 154 | font-family: 'Open Sans Light', sans-serif; 155 | font-size: 48px; 156 | letter-spacing: -2px; 157 | margin: 12px 24px 20px; 158 | } 159 | 160 | h2, h3.subsection-title 161 | { 162 | font-size: 30px; 163 | font-weight: 700; 164 | letter-spacing: -1px; 165 | margin-bottom: 12px; 166 | } 167 | 168 | h3 169 | { 170 | font-size: 24px; 171 | letter-spacing: -0.5px; 172 | margin-bottom: 12px; 173 | } 174 | 175 | h4 176 | { 177 | font-size: 18px; 178 | letter-spacing: -0.33px; 179 | margin-bottom: 12px; 180 | color: #4d4e53; 181 | } 182 | 183 | h5, .container-overview .subsection-title 184 | { 185 | font-size: 120%; 186 | font-weight: bold; 187 | letter-spacing: -0.01em; 188 | margin: 8px 0 3px 0; 189 | } 190 | 191 | h6 192 | { 193 | font-size: 100%; 194 | letter-spacing: -0.01em; 195 | margin: 6px 0 3px 0; 196 | font-style: italic; 197 | } 198 | 199 | table 200 | { 201 | border-spacing: 0; 202 | border: 0; 203 | border-collapse: collapse; 204 | } 205 | 206 | td, th 207 | { 208 | border: 1px solid #ddd; 209 | margin: 0px; 210 | text-align: left; 211 | vertical-align: top; 212 | padding: 4px 6px; 213 | display: table-cell; 214 | } 215 | 216 | thead tr 217 | { 218 | background-color: #ddd; 219 | font-weight: bold; 220 | } 221 | 222 | th { border-right: 1px solid #aaa; } 223 | tr > th:last-child { border-right: 1px solid #ddd; } 224 | 225 | .ancestors, .attribs { color: #999; } 226 | .ancestors a, .attribs a 227 | { 228 | color: #999 !important; 229 | text-decoration: none; 230 | } 231 | 232 | .clear 233 | { 234 | clear: both; 235 | } 236 | 237 | .important 238 | { 239 | font-weight: bold; 240 | color: #950B02; 241 | } 242 | 243 | .yes-def { 244 | text-indent: -1000px; 245 | } 246 | 247 | .type-signature { 248 | color: #aaa; 249 | } 250 | 251 | .name, .signature { 252 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 253 | } 254 | 255 | .details { margin-top: 14px; border-left: 2px solid #DDD; } 256 | .details dt { width: 120px; float: left; padding-left: 10px; padding-top: 6px; } 257 | .details dd { margin-left: 70px; } 258 | .details ul { margin: 0; } 259 | .details ul { list-style-type: none; } 260 | .details li { margin-left: 30px; padding-top: 6px; } 261 | .details pre.prettyprint { margin: 0 } 262 | .details .object-value { padding-top: 0; } 263 | 264 | .description { 265 | margin-bottom: 1em; 266 | margin-top: 1em; 267 | } 268 | 269 | .code-caption 270 | { 271 | font-style: italic; 272 | font-size: 107%; 273 | margin: 0; 274 | } 275 | 276 | .source 277 | { 278 | border: 1px solid #ddd; 279 | width: 80%; 280 | overflow: auto; 281 | } 282 | 283 | .prettyprint.source { 284 | width: inherit; 285 | } 286 | 287 | .source code 288 | { 289 | font-size: 100%; 290 | line-height: 18px; 291 | display: block; 292 | padding: 4px 12px; 293 | margin: 0; 294 | background-color: #fff; 295 | color: #4D4E53; 296 | } 297 | 298 | .prettyprint code span.line 299 | { 300 | display: inline-block; 301 | } 302 | 303 | .prettyprint.linenums 304 | { 305 | padding-left: 70px; 306 | -webkit-user-select: none; 307 | -moz-user-select: none; 308 | -ms-user-select: none; 309 | user-select: none; 310 | } 311 | 312 | .prettyprint.linenums ol 313 | { 314 | padding-left: 0; 315 | } 316 | 317 | .prettyprint.linenums li 318 | { 319 | border-left: 3px #ddd solid; 320 | } 321 | 322 | .prettyprint.linenums li.selected, 323 | .prettyprint.linenums li.selected * 324 | { 325 | background-color: lightyellow; 326 | } 327 | 328 | .prettyprint.linenums li * 329 | { 330 | -webkit-user-select: text; 331 | -moz-user-select: text; 332 | -ms-user-select: text; 333 | user-select: text; 334 | } 335 | 336 | .params .name, .props .name, .name code { 337 | color: #4D4E53; 338 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 339 | font-size: 100%; 340 | } 341 | 342 | .params td.description > p:first-child, 343 | .props td.description > p:first-child 344 | { 345 | margin-top: 0; 346 | padding-top: 0; 347 | } 348 | 349 | .params td.description > p:last-child, 350 | .props td.description > p:last-child 351 | { 352 | margin-bottom: 0; 353 | padding-bottom: 0; 354 | } 355 | 356 | .disabled { 357 | color: #454545; 358 | } 359 | -------------------------------------------------------------------------------- /docs/jsdoc/styles/jsdoc-default.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Open Sans'; 3 | font-weight: normal; 4 | font-style: normal; 5 | src: url('../fonts/OpenSans-Regular-webfont.eot'); 6 | src: 7 | local('Open Sans'), 8 | local('OpenSans'), 9 | url('../fonts/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'), 10 | url('../fonts/OpenSans-Regular-webfont.woff') format('woff'), 11 | url('../fonts/OpenSans-Regular-webfont.svg#open_sansregular') format('svg'); 12 | } 13 | 14 | @font-face { 15 | font-family: 'Open Sans Light'; 16 | font-weight: normal; 17 | font-style: normal; 18 | src: url('../fonts/OpenSans-Light-webfont.eot'); 19 | src: 20 | local('Open Sans Light'), 21 | local('OpenSans Light'), 22 | url('../fonts/OpenSans-Light-webfont.eot?#iefix') format('embedded-opentype'), 23 | url('../fonts/OpenSans-Light-webfont.woff') format('woff'), 24 | url('../fonts/OpenSans-Light-webfont.svg#open_sanslight') format('svg'); 25 | } 26 | 27 | html 28 | { 29 | overflow: auto; 30 | background-color: #fff; 31 | font-size: 14px; 32 | } 33 | 34 | body 35 | { 36 | font-family: 'Open Sans', sans-serif; 37 | line-height: 1.5; 38 | color: #4d4e53; 39 | background-color: white; 40 | } 41 | 42 | a, a:visited, a:active { 43 | color: #0095dd; 44 | text-decoration: none; 45 | } 46 | 47 | a:hover { 48 | text-decoration: underline; 49 | } 50 | 51 | header 52 | { 53 | display: block; 54 | padding: 0px 4px; 55 | } 56 | 57 | tt, code, kbd, samp { 58 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 59 | } 60 | 61 | .class-description { 62 | font-size: 130%; 63 | line-height: 140%; 64 | margin-bottom: 1em; 65 | margin-top: 1em; 66 | } 67 | 68 | .class-description:empty { 69 | margin: 0; 70 | } 71 | 72 | #main { 73 | float: left; 74 | width: 70%; 75 | } 76 | 77 | article dl { 78 | margin-bottom: 40px; 79 | } 80 | 81 | article img { 82 | max-width: 100%; 83 | } 84 | 85 | section 86 | { 87 | display: block; 88 | background-color: #fff; 89 | padding: 12px 24px; 90 | border-bottom: 1px solid #ccc; 91 | margin-right: 30px; 92 | } 93 | 94 | .variation { 95 | display: none; 96 | } 97 | 98 | .signature-attributes { 99 | font-size: 60%; 100 | color: #aaa; 101 | font-style: italic; 102 | font-weight: lighter; 103 | } 104 | 105 | nav 106 | { 107 | display: block; 108 | float: right; 109 | margin-top: 28px; 110 | width: 30%; 111 | box-sizing: border-box; 112 | border-left: 1px solid #ccc; 113 | padding-left: 16px; 114 | } 115 | 116 | nav ul { 117 | font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif; 118 | font-size: 100%; 119 | line-height: 17px; 120 | padding: 0; 121 | margin: 0; 122 | list-style-type: none; 123 | } 124 | 125 | nav ul a, nav ul a:visited, nav ul a:active { 126 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 127 | line-height: 18px; 128 | color: #4D4E53; 129 | } 130 | 131 | nav h3 { 132 | margin-top: 12px; 133 | } 134 | 135 | nav li { 136 | margin-top: 6px; 137 | } 138 | 139 | footer { 140 | display: block; 141 | padding: 6px; 142 | margin-top: 12px; 143 | font-style: italic; 144 | font-size: 90%; 145 | } 146 | 147 | h1, h2, h3, h4 { 148 | font-weight: 200; 149 | margin: 0; 150 | } 151 | 152 | h1 153 | { 154 | font-family: 'Open Sans Light', sans-serif; 155 | font-size: 48px; 156 | letter-spacing: -2px; 157 | margin: 12px 24px 20px; 158 | } 159 | 160 | h2, h3.subsection-title 161 | { 162 | font-size: 30px; 163 | font-weight: 700; 164 | letter-spacing: -1px; 165 | margin-bottom: 12px; 166 | } 167 | 168 | h3 169 | { 170 | font-size: 24px; 171 | letter-spacing: -0.5px; 172 | margin-bottom: 12px; 173 | } 174 | 175 | h4 176 | { 177 | font-size: 18px; 178 | letter-spacing: -0.33px; 179 | margin-bottom: 12px; 180 | color: #4d4e53; 181 | } 182 | 183 | h5, .container-overview .subsection-title 184 | { 185 | font-size: 120%; 186 | font-weight: bold; 187 | letter-spacing: -0.01em; 188 | margin: 8px 0 3px 0; 189 | } 190 | 191 | h6 192 | { 193 | font-size: 100%; 194 | letter-spacing: -0.01em; 195 | margin: 6px 0 3px 0; 196 | font-style: italic; 197 | } 198 | 199 | table 200 | { 201 | border-spacing: 0; 202 | border: 0; 203 | border-collapse: collapse; 204 | } 205 | 206 | td, th 207 | { 208 | border: 1px solid #ddd; 209 | margin: 0px; 210 | text-align: left; 211 | vertical-align: top; 212 | padding: 4px 6px; 213 | display: table-cell; 214 | } 215 | 216 | thead tr 217 | { 218 | background-color: #ddd; 219 | font-weight: bold; 220 | } 221 | 222 | th { border-right: 1px solid #aaa; } 223 | tr > th:last-child { border-right: 1px solid #ddd; } 224 | 225 | .ancestors, .attribs { color: #999; } 226 | .ancestors a, .attribs a 227 | { 228 | color: #999 !important; 229 | text-decoration: none; 230 | } 231 | 232 | .clear 233 | { 234 | clear: both; 235 | } 236 | 237 | .important 238 | { 239 | font-weight: bold; 240 | color: #950B02; 241 | } 242 | 243 | .yes-def { 244 | text-indent: -1000px; 245 | } 246 | 247 | .type-signature { 248 | color: #aaa; 249 | } 250 | 251 | .name, .signature { 252 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 253 | } 254 | 255 | .details { margin-top: 14px; border-left: 2px solid #DDD; } 256 | .details dt { width: 120px; float: left; padding-left: 10px; padding-top: 6px; } 257 | .details dd { margin-left: 70px; } 258 | .details ul { margin: 0; } 259 | .details ul { list-style-type: none; } 260 | .details li { margin-left: 30px; padding-top: 6px; } 261 | .details pre.prettyprint { margin: 0 } 262 | .details .object-value { padding-top: 0; } 263 | 264 | .description { 265 | margin-bottom: 1em; 266 | margin-top: 1em; 267 | } 268 | 269 | .code-caption 270 | { 271 | font-style: italic; 272 | font-size: 107%; 273 | margin: 0; 274 | } 275 | 276 | .source 277 | { 278 | border: 1px solid #ddd; 279 | width: 80%; 280 | overflow: auto; 281 | } 282 | 283 | .prettyprint.source { 284 | width: inherit; 285 | } 286 | 287 | .source code 288 | { 289 | font-size: 100%; 290 | line-height: 18px; 291 | display: block; 292 | padding: 4px 12px; 293 | margin: 0; 294 | background-color: #fff; 295 | color: #4D4E53; 296 | } 297 | 298 | .prettyprint code span.line 299 | { 300 | display: inline-block; 301 | } 302 | 303 | .prettyprint.linenums 304 | { 305 | padding-left: 70px; 306 | -webkit-user-select: none; 307 | -moz-user-select: none; 308 | -ms-user-select: none; 309 | user-select: none; 310 | } 311 | 312 | .prettyprint.linenums ol 313 | { 314 | padding-left: 0; 315 | } 316 | 317 | .prettyprint.linenums li 318 | { 319 | border-left: 3px #ddd solid; 320 | } 321 | 322 | .prettyprint.linenums li.selected, 323 | .prettyprint.linenums li.selected * 324 | { 325 | background-color: lightyellow; 326 | } 327 | 328 | .prettyprint.linenums li * 329 | { 330 | -webkit-user-select: text; 331 | -moz-user-select: text; 332 | -ms-user-select: text; 333 | user-select: text; 334 | } 335 | 336 | .params .name, .props .name, .name code { 337 | color: #4D4E53; 338 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 339 | font-size: 100%; 340 | } 341 | 342 | .params td.description > p:first-child, 343 | .props td.description > p:first-child 344 | { 345 | margin-top: 0; 346 | padding-top: 0; 347 | } 348 | 349 | .params td.description > p:last-child, 350 | .props td.description > p:last-child 351 | { 352 | margin-bottom: 0; 353 | padding-bottom: 0; 354 | } 355 | 356 | .disabled { 357 | color: #454545; 358 | } 359 | -------------------------------------------------------------------------------- /docs/jsdoc/tutorial-getting-started.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Tutorial: Getting Started 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Tutorial: Getting Started

21 | 22 |
23 | 24 |
25 | 26 | 27 |

Getting Started

28 |
29 | 30 |
31 |

graphgram is a library for generating graphs using a transformative graph grammar. 32 | This is useful if you want random graphs that have predictable structural motifs or patterns in them. 33 | The motivating use case is procedural generation of story paths or maps in interactive fiction (i.e. games), 34 | and the pioneering example of this is Joris Dormans' roguelike Unexplored 35 | as described in this RPS article. 36 | (A formal mathematical introduction to graph grammars can be found in these slides by Matilde Marcolli.)

37 |

The way that graphgram works is as follows. A basic graph grammar consists of a set of transformation rules. 38 | Each such rule contains, at minimum, a left-hand side and a right-hand side: the rule has the form LHS → RHS.

39 |
    40 |
  • The left-hand side is a pattern that is used to match a subgraph of the current graph, using Ullman's 1976 subgraph isomorphism search algorithm.
  • 41 |
  • The right-hand side specifies a subgraph that will be pasted in as a replacement for the matching subgraph.
  • 42 |
43 |

Beyond simply matching subgraph topologies, graphgram offers considerable flexibility in the way that the LHS pattern-matching occurs. 44 | Each node and edge in the graph can have a label which can be a string or an arbitrary JSON object. 45 | There is a simple query language for matching labels (or the user can define their own label-matching function). 46 | The grammar specification also allows for constraints limiting the number and type of transformation rules that can be applied to generate any single graph.

47 |

The library can be called via the API of the Grammar class, or using the command-line tool bin/transform.js.

48 |

Grammars are specified using a JSON schema that is documented here.

49 |

All graphs generated by graphgram are directed. 50 | Undirected edges can be represented using two directed edges, one pointing forwards and one backwards.

51 |

Dungeon grammar

52 |

The default grammar for the CLI, for demo purposes, is a dungeon-generation grammar that is fairly close to the following:

53 |
{
 54 |   name: 'dungeon-grammar',
 55 |   start: 'START',
 56 |     rules:
 57 |     [{ lhs: 'START', rhs: ['entrance', 'x', 'boss'] },
 58 |     { lhs: 'x', rhs: ['x','x'], limit: 3 },
 59 |     { lhs: 'x', rhs: { node: ['fork', 'x', 'die', 'x'], edge: [[0,1],[1,2],[0,3]] }, type: 'ending', limit: 3 },
 60 |     { lhs: 'x', rhs: { node: ['fork', 'x', 'live', 'x'], edge: [[0,1],[1,2],[0,3]] }, type: 'ending', limit: 3 },
 61 |     { lhs: 'x', rhs: { node: ['fork', 'x', 'x', 'x'], edge: [[0,1],[0,2],[1,3],[2,3]] }, type: 'fork', limit: 2, delay: 2 },
 62 |     { lhs: 'x', rhs: { node: ['crossroads', 'x', 'x', 'x', 'x'], edge: [[0,1],[0,2],[0,3],[1,4],[2,4],[3,4]] }, type: 'fork', limit: 1, delay: 2 },
 63 |     { lhs: 'x', rhs: { node: ['door', 'x1', 'x'], edge: [[0,1,'enter'],[1,2,'exit'],[0,2,'bypass']] }, limit: 3 },
 64 |     { lhs: 'x', rhs: { node: ['fork', 'x', 'x', 'x', 'rescue', 'x', 'x'], edge: [[0,1],[1,2],[2,3],[0,3],[3,4],[4,5],[5,6],[3,6],[1,4,'rumor']] }, type: 'rescue', limit: 1 },
 65 |     { lhs: 'x', rhs: { node: ['door', 'x1', 'x', 'rescue', 'x', 'x'], edge: [[0,1,'enter'],[1,2,'exit'],[0,2,'bypass'],[2,3],[3,4],[4,5],[2,5],[1,3,'rumor']] }, type: 'rescue', limit: 1 },
 66 |     { lhs: 'x', rhs: { node: ['chest', 'chest_contents', 'x'], edge: [[0,1,'open'],[1,2],[0,2,'ignore']] }, limit: 3 },
 67 |     { lhs: 'chest_contents', rhs: 'trap', weight: 2 },
 68 |     { lhs: 'chest_contents', rhs: 'treasure' },
 69 |     { lhs: 'chest_contents', rhs: 'weapon' },
 70 |     { lhs: 'x', rhs: { node: ['vial', 'vial_contents', 'x'], edge: [[0,1,'drink'],[1,2],[0,2,'ignore']] }, limit: 3 },
 71 |     { lhs: 'vial_contents', rhs: 'potion' },
 72 |     { lhs: 'vial_contents', rhs: 'poison' },
 73 |     { lhs: 'x', rhs: 'x1', delay: 10 },
 74 |     { lhs: 'x1', rhs: 'trap' },
 75 |     { lhs: 'x1', rhs: 'monster' },
 76 |     { lhs: 'x1', rhs: 'weapon', limit: 2 },
 77 |     { lhs: 'x1', rhs: 'treasure', limit: 3 },
 78 |     { lhs: 'x1', rhs: 'scenery', weight: 2 }
 79 |     ]
 80 | }
 81 | 
82 |

The full flexibility of matching any subgraph and replacing it with another subgraph of arbitrary topology (possibly with some overlap/reuse of nodes and/or edges from the matched subgraph) can be quite daunting and is probably overkill for most purposes. 83 | In recognition of this, graphgram offers a few syntactical shortcuts for common types of transformation rule and graph 84 | (corresponding to sequence grammars, context-free grammars, and so on). 85 | These are fully described in the documentation for the JSON schema for the `Grammar`` class.

86 |

One of these syntactical shortcuts is heavily used by the above dungeon grammar: 87 | all the transformation rules in that grammar have a single node on the left-hand side. 88 | This means that the subgraph matching algorithm is not being heavily exercised by this grammar; 89 | it is effectively a context-free node replacement grammar.

90 |

The simplest kind of rule in this grammar has the form:

91 |
{ lhs: 'chest_contents', rhs: 'weapon' },
 92 | 
93 |

This simply means "replace a node whose label is chest_contents with a node whose label is weapon".

94 |

An example of a slightly more elaborate rule is

95 |
{ lhs: 'START', rhs: ['entrance', 'x', 'boss'] }
 96 | 
97 |

This replaces the node labeled START with a sequence of three nodes: 98 | entrancexboss.

99 |

The edges connecting consecutive nodes are added by default when an array of node labels is given. 100 | This corresponds to inserting a sequence of nodes at a particular point (as in a string transformation grammar). 101 | For an example of a rule where the replacement subgraph is not just a linear chain, so that its topology must be specified explicitly, 102 | consider the following:

103 |
{ lhs: 'x', rhs: { node: ['fork', 'x', 'die', 'x'], edge: [[0,1],[1,2],[0,3]] } },
104 | 
105 |

This replaces a node labeled x with a subgraph containing four nodes labeled (respectively) 106 | fork (node 0), x (node 1), die (node 2) and x (node 3). 107 | The edge clause in the rhs of this rule contains three tuples of the form [v,w] where v and w represent the indices of 108 | (respectively) the source and target nodes in the replacement subgraph. 109 | Thus, the subgraph contains a fork node that has edges to two new x nodes, the first of which then leads to a die node. 110 | The first node on the RHS inherits the edges that are incoming to the LHS node, and the last node on the RHS inherits its outgoing edges 111 | (this behavior can be overridden, as described in the JSON schema).

112 |

The actual rule that appears in the example grammar is slightly different from this:

113 |
{ lhs: 'x', rhs: { node: ['fork', 'x', 'die', 'x'], edge: [[0,1],[1,2],[0,3]] }, type: 'ending', limit: 3 },
114 | 
115 |

The type and limit fields indicate that this rule has type "ending" and can only be used if fewer than 3 rules with that type have already been applied.

116 |

For examples of more sophisticated subgraph replacement rules that include context and nontrivial topology on the LHS, 117 | as well as compound expressions for matching/replacing node and edge labels, 118 | see the level.js example grammar 119 | which generates levels for the "roguelike snakelike" game nemato.de.

120 |
121 | 122 |
123 | 124 |
125 | 126 | 129 | 130 |
131 | 132 |
133 | Documentation generated by JSDoc 4.0.2 on Wed Oct 11 2023 15:40:44 GMT-0700 (Pacific Daylight Time) 134 |
135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /grammars/dungeon.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dungeon-grammar", 3 | "start": "START", 4 | "stages": [ 5 | { 6 | "name": "generation-stage", 7 | "rules": [ 8 | { 9 | "lhs": "START", 10 | "rhs": [ 11 | "entrance", 12 | "x", 13 | "boss" 14 | ] 15 | }, 16 | { 17 | "lhs": "x", 18 | "rhs": [ 19 | "x", 20 | "x" 21 | ], 22 | "limit": 3 23 | }, 24 | { 25 | "lhs": "x", 26 | "rhs": { 27 | "node": [ 28 | "fork", 29 | "x", 30 | "die", 31 | "x" 32 | ], 33 | "edge": [ 34 | [ 35 | 0, 36 | 1 37 | ], 38 | [ 39 | 1, 40 | 2 41 | ], 42 | [ 43 | 0, 44 | 3 45 | ] 46 | ] 47 | }, 48 | "type": "ending", 49 | "limit": 3 50 | }, 51 | { 52 | "lhs": "x", 53 | "rhs": { 54 | "node": [ 55 | "fork", 56 | "x", 57 | "live", 58 | "x" 59 | ], 60 | "edge": [ 61 | [ 62 | 0, 63 | 1 64 | ], 65 | [ 66 | 1, 67 | 2 68 | ], 69 | [ 70 | 0, 71 | 3 72 | ] 73 | ] 74 | }, 75 | "type": "ending", 76 | "limit": 3 77 | }, 78 | { 79 | "lhs": "x", 80 | "rhs": { 81 | "node": [ 82 | "fork", 83 | "x", 84 | "x", 85 | "x" 86 | ], 87 | "edge": [ 88 | [ 89 | 0, 90 | 1 91 | ], 92 | [ 93 | 0, 94 | 2 95 | ], 96 | [ 97 | 1, 98 | 3 99 | ], 100 | [ 101 | 2, 102 | 3 103 | ] 104 | ] 105 | }, 106 | "type": "fork", 107 | "limit": 2, 108 | "delay": 2 109 | }, 110 | { 111 | "lhs": "x", 112 | "rhs": { 113 | "node": [ 114 | "crossroads", 115 | "x", 116 | "x", 117 | "x", 118 | "x" 119 | ], 120 | "edge": [ 121 | [ 122 | 0, 123 | 1 124 | ], 125 | [ 126 | 0, 127 | 2 128 | ], 129 | [ 130 | 0, 131 | 3 132 | ], 133 | [ 134 | 1, 135 | 4 136 | ], 137 | [ 138 | 2, 139 | 4 140 | ], 141 | [ 142 | 3, 143 | 4 144 | ] 145 | ] 146 | }, 147 | "type": "fork", 148 | "limit": 1, 149 | "delay": 2 150 | }, 151 | { 152 | "lhs": "x", 153 | "rhs": { 154 | "node": [ 155 | "door", 156 | "x1", 157 | "x" 158 | ], 159 | "edge": [ 160 | [ 161 | 0, 162 | 1, 163 | "enter" 164 | ], 165 | [ 166 | 1, 167 | 2, 168 | "exit" 169 | ], 170 | [ 171 | 0, 172 | 2, 173 | "bypass" 174 | ] 175 | ] 176 | }, 177 | "limit": 3 178 | }, 179 | { 180 | "lhs": "x", 181 | "rhs": { 182 | "node": [ 183 | "fork", 184 | "x", 185 | "x", 186 | "x", 187 | "rescue", 188 | "x", 189 | "x" 190 | ], 191 | "edge": [ 192 | [ 193 | 0, 194 | 1 195 | ], 196 | [ 197 | 1, 198 | 2 199 | ], 200 | [ 201 | 2, 202 | 3 203 | ], 204 | [ 205 | 0, 206 | 3 207 | ], 208 | [ 209 | 3, 210 | 4 211 | ], 212 | [ 213 | 4, 214 | 5 215 | ], 216 | [ 217 | 5, 218 | 6 219 | ], 220 | [ 221 | 3, 222 | 6 223 | ], 224 | [ 225 | 1, 226 | 4, 227 | "rumor" 228 | ] 229 | ] 230 | }, 231 | "type": "rescue", 232 | "limit": 1 233 | }, 234 | { 235 | "lhs": "x", 236 | "rhs": { 237 | "node": [ 238 | "door", 239 | "x1", 240 | "x", 241 | "rescue", 242 | "x", 243 | "x" 244 | ], 245 | "edge": [ 246 | [ 247 | 0, 248 | 1, 249 | "enter" 250 | ], 251 | [ 252 | 1, 253 | 2, 254 | "exit" 255 | ], 256 | [ 257 | 0, 258 | 2, 259 | "bypass" 260 | ], 261 | [ 262 | 2, 263 | 3 264 | ], 265 | [ 266 | 3, 267 | 4 268 | ], 269 | [ 270 | 4, 271 | 5 272 | ], 273 | [ 274 | 2, 275 | 5 276 | ], 277 | [ 278 | 1, 279 | 3, 280 | "rumor" 281 | ] 282 | ] 283 | }, 284 | "type": "rescue", 285 | "limit": 1 286 | }, 287 | { 288 | "lhs": "x", 289 | "rhs": { 290 | "node": [ 291 | "chest", 292 | "chest_contents", 293 | "x" 294 | ], 295 | "edge": [ 296 | [ 297 | 0, 298 | 1, 299 | "open" 300 | ], 301 | [ 302 | 1, 303 | 2 304 | ], 305 | [ 306 | 0, 307 | 2, 308 | "ignore" 309 | ] 310 | ] 311 | }, 312 | "limit": 3 313 | }, 314 | { 315 | "lhs": "chest_contents", 316 | "rhs": "trap", 317 | "weight": 2 318 | }, 319 | { 320 | "lhs": "chest_contents", 321 | "rhs": "treasure" 322 | }, 323 | { 324 | "lhs": "chest_contents", 325 | "rhs": "weapon" 326 | }, 327 | { 328 | "lhs": "x", 329 | "rhs": { 330 | "node": [ 331 | "vial", 332 | "vial_contents", 333 | "x" 334 | ], 335 | "edge": [ 336 | [ 337 | 0, 338 | 1, 339 | "drink" 340 | ], 341 | [ 342 | 1, 343 | 2 344 | ], 345 | [ 346 | 0, 347 | 2, 348 | "ignore" 349 | ] 350 | ] 351 | }, 352 | "limit": 3 353 | }, 354 | { 355 | "lhs": "vial_contents", 356 | "rhs": "potion" 357 | }, 358 | { 359 | "lhs": "vial_contents", 360 | "rhs": "poison" 361 | }, 362 | { 363 | "lhs": "x", 364 | "rhs": "x1", 365 | "delay": 10 366 | }, 367 | { 368 | "lhs": "x1", 369 | "rhs": "trap" 370 | }, 371 | { 372 | "lhs": "x1", 373 | "rhs": "monster" 374 | }, 375 | { 376 | "lhs": "x1", 377 | "rhs": "weapon", 378 | "limit": 2 379 | }, 380 | { 381 | "lhs": "x1", 382 | "rhs": "treasure", 383 | "limit": 3 384 | }, 385 | { 386 | "lhs": "x1", 387 | "rhs": "scenery", 388 | "weight": 2 389 | } 390 | ] 391 | }, 392 | { 393 | "name": "decoration-stage", 394 | "rules": [ 395 | { 396 | "name": "dot-rumor-edge", 397 | "lhs": { 398 | "node": [ 399 | { 400 | "id": "a" 401 | }, 402 | { 403 | "id": "b" 404 | } 405 | ], 406 | "edge": [ 407 | [ 408 | "a", 409 | "b", 410 | "rumor" 411 | ] 412 | ] 413 | }, 414 | "rhs": { 415 | "node": [ 416 | { 417 | "id": "a" 418 | }, 419 | { 420 | "id": "b" 421 | } 422 | ], 423 | "edge": [ 424 | [ 425 | "a", 426 | "b", 427 | { 428 | "dot": { 429 | "label": "rumor", 430 | "style": "dotted" 431 | } 432 | } 433 | ] 434 | ] 435 | } 436 | }, 437 | { 438 | "name": "flag-endpoints", 439 | "lhs": "(die|live|boss)", 440 | "rhs": { 441 | "node": [ 442 | { 443 | "label": { 444 | "endpoint": "${a.match[1]}" 445 | } 446 | } 447 | ] 448 | } 449 | }, 450 | { 451 | "name": "make-endpoints-rectangular", 452 | "lhs": { 453 | "node": [ 454 | { 455 | "id": "a", 456 | "label": { 457 | "$equals": { 458 | "endpoint": ".*" 459 | } 460 | } 461 | } 462 | ] 463 | }, 464 | "rhs": { 465 | "node": [ 466 | { 467 | "id": "a", 468 | "update": { 469 | "dot": { 470 | "label": "${a.match.endpoint[0]}", 471 | "shape": "rect" 472 | } 473 | } 474 | } 475 | ] 476 | } 477 | } 478 | ] 479 | } 480 | ] 481 | } 482 | -------------------------------------------------------------------------------- /out/scripts/prettify/Apache-License-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /docs/jsdoc/scripts/prettify/Apache-License-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /grammars/level.js: -------------------------------------------------------------------------------- 1 | { 2 | name: 'level-grammar', 3 | stages: 4 | [{ name: 'init', 5 | limit: 1, 6 | rules: [ 7 | // replace init--node with start-->end 8 | { lhs: { node: [{ id: "init", label: { name: "init" } }, 9 | { id: "node", label: { name: "node" } }], 10 | edge: [{ v: "init", w: "node", label: { name: "edge" } }, 11 | { v: "node", w: "init", label: { name: "edge" } }] }, 12 | rhs: { node: [{ id: "start", head:["init"], tail:["init"], label: { $merge: [ { $eval: '$init.label' }, 13 | { name: "start" }] } }, 14 | { id: "end", head:["node"], tail:["node"], label: { $merge: [ { $eval: '$node.label' }, 15 | { name: "end", 16 | endpoint: true }] } }], 17 | edge: [{ v: "start", w: "end", label: { name: "path" } }] } } 18 | ] }, 19 | 20 | { name: 'extend', 21 | rules: [ 22 | // replace b--x with mid-->end where b is an endpoint 23 | { weight: 2, 24 | lhs: { node: [{ id: "b", label: { endpoint: true } }, 25 | { id: "x", label: { name: "node" } }], 26 | edge: [{ v: "b", w: "x", label: { name: "edge" } }, 27 | { v: "x", w: "b", label: { name: "edge" } }] }, 28 | rhs: { node: [{ id: "mid", head:["b"], tail:["b"], label: { $merge: [ { $eval: '$b.label' }, 29 | { name: "room" } ] } }, 30 | { id: "end", head:["x"], tail:["x"], label: { $merge: [ { $eval: '$x.label' }, 31 | { name: '${b.label.name}' } ] } }], 32 | edge: [{ v: "mid", w: "end", label: { name: "path" } }] } }, 33 | 34 | // replace x--a-->... with herring<--a-->... 35 | { weight: .25, 36 | limit: 2, 37 | lhs: { node: [{ id: "a", label: { $and: [{ $not: { name: "node" } }, 38 | { $not: { endpoint: true } }] } }, 39 | { id: "x", label: { name: "node" } }], 40 | edge: [{ v: "a", w: "x", label: { name: "edge" } }, 41 | { v: "x", w: "a", label: { name: "edge" } }] }, 42 | rhs: { node: [{ id: "a" }, 43 | { id: "side", head:["x"], tail:["x"], label: { $merge: [ { $eval: '$x.label' }, 44 | { name: 'herring', 45 | endpoint: true } ] } }], 46 | edge: [{ v: "a", w: "side", label: { name: "path" } }] } }, 47 | 48 | // replace a->d with a->d 49 | // | | | ^ 50 | // | | v | 51 | // x--y b->c 52 | 53 | // wrinkle in path (no a->d connection on rhs) 54 | { lhs: { node: [{ id: "a", label: { $not: { name: "node" } } }, 55 | { id: "d", label: { $not: { name: "node" } } }, 56 | { id: "x", label: { name: "node" } }, 57 | { id: "y", label: { name: "node" } }], 58 | edge: [{ v: "a", w: "d", label: { name: "path" } }, 59 | { v: "a", w: "x", label: { name: "edge" } }, 60 | { v: "x", w: "a", label: { name: "edge" } }, 61 | { v: "x", w: "y", label: { name: "edge" } }, 62 | { v: "y", w: "x", label: { name: "edge" } }, 63 | { v: "y", w: "d", label: { name: "edge" } }, 64 | { v: "d", w: "y", label: { name: "edge" } }] }, 65 | rhs: { node: [{ id: "a" }, 66 | { id: "d" }, 67 | { id: "b", head:["x"], tail:["x"], label: { $merge: [{ $eval: '$x.label' }, 68 | { name: "room" }] } }, 69 | { id: "c", head:["y"], tail:["y"], label: { $merge: [{ $eval: '$y.label' }, 70 | { name: "room" }] } }], 71 | edge: [{ v: "a", w: "b", label: { name: "path" } }, 72 | { v: "b", w: "c", label: { name: "path" } }, 73 | { v: "c", w: "d", label: { name: "path" } }] } }, 74 | 75 | // fork: a->d splits to a->d->c and a->b->c. endpoint moves from d to c 76 | { weight: .1, 77 | limit: 4, 78 | lhs: { node: [{ id: "a", label: { $not: { name: "node" } } }, 79 | { id: "d", label: { endpoint: true } }, 80 | { id: "x", label: { name: "node" } }, 81 | { id: "y", label: { name: "node" } }], 82 | edge: [{ v: "a", w: "d", label: { name: "path" } }, 83 | { v: "a", w: "x", label: { name: "edge" } }, 84 | { v: "x", w: "a", label: { name: "edge" } }, 85 | { v: "x", w: "y", label: { name: "edge" } }, 86 | { v: "y", w: "x", label: { name: "edge" } }, 87 | { v: "y", w: "d", label: { name: "edge" } }, 88 | { v: "d", w: "y", label: { name: "edge" } }] }, 89 | rhs: { node: [{ id: "a" }, 90 | { id: "d2", head:["d"], tail:["d"], label: { $merge: [ { $eval: '$d.label' }, 91 | { name: "room" } ] } }, 92 | { id: "b", head:["x"], tail:["x"], label: { $merge: [ { $eval: '$x.label' }, 93 | { name: "room" } ] } }, 94 | { id: "c", head:["y"], tail:["y"], label: { $merge: [ { $eval: '$y.label' }, 95 | { name: '${d.label.name}' } ] } }], 96 | edge: [{ v: "a", w: "b", label: { name: "fork", dot: { style: "dotted" } } }, 97 | { v: "b", w: "c", label: { name: "path" } }, 98 | { v: "a", w: "d2", label: { name: "fork", dot: { style: "dotted" } } }, 99 | { v: "d2", w: "c", label: { name: "path" } }] } }, 100 | 101 | 102 | // cave: a->b->c adds a corner d 103 | { weight: 1, 104 | limit: 4, 105 | lhs: { node: [{ id: "a", label: { $not: { name: "node" } } }, 106 | { id: "b", label: { $not: { name: "node" } } }, 107 | { id: "c", label: { $not: { name: "node" } } }, 108 | { id: "x", label: { name: "node" } }], 109 | edge: [{ v: "a", w: "b", label: { $or: [ { name: "path" }, { name: "cave" } ] } }, 110 | { v: "b", w: "c", label: { $or: [ { name: "path" }, { name: "cave" } ] } }, 111 | { v: "a", w: "x", label: { name: "edge" } }, 112 | { v: "x", w: "a", label: { name: "edge" } }, 113 | { v: "c", w: "x", label: { name: "edge" } }, 114 | { v: "x", w: "c", label: { name: "edge" } }] }, 115 | rhs: { node: [{ id: "a" }, 116 | { id: "b" }, 117 | { id: "c" }, 118 | { id: "d", head:["x"], tail:["x"], label: { $merge: [ { $eval: '$x.label' }, 119 | { name: "room" } ] } }], 120 | edge: [{ v: "a", w: "b", label: { name: "cave", dot: { style: "dotted" } } }, 121 | { v: "b", w: "c", label: { name: "cave", dot: { style: "dotted" } } }, 122 | { v: "a", w: "d", label: { name: "cave", dot: { style: "dotted" } } }, 123 | { v: "d", w: "c", label: { name: "cave", dot: { style: "dotted" } } }, 124 | { v: "a", w: "c", label: { dot: { style: "dotted" } } }, 125 | { v: "b", w: "d", label: { dot: { style: "dotted" } } }] } }, 126 | 127 | // shortcut 128 | { limit: 3, 129 | lhs: { node: [{ id: "a", label: { $not: { name: "node" } } }, 130 | { id: "d", label: { $not: { name: "node" } } }, 131 | { id: "x", label: { name: "node" } }, 132 | { id: "y", label: { name: "node" } }], 133 | edge: [{ v: "a", w: "d", label: { name: "path" } }, 134 | { v: "a", w: "x", label: { name: "edge" } }, 135 | { v: "x", w: "a", label: { name: "edge" } }, 136 | { v: "x", w: "y", label: { name: "edge" } }, 137 | { v: "y", w: "x", label: { name: "edge" } }, 138 | { v: "y", w: "d", label: { name: "edge" } }, 139 | { v: "d", w: "y", label: { name: "edge" } }] }, 140 | rhs: { node: [{ id: "a" }, 141 | { id: "d" }, 142 | { id: "b", head:["x"], tail:["x"], label: { $merge: [ { $eval: '$x.label' }, 143 | { name: "room" } ] } }, 144 | { id: "c", head:["y"], tail:["y"], label: { $merge: [ { $eval: '$y.label' }, 145 | { name: "room" } ] } }], 146 | edge: [{ v: "a", w: "d", label: { name: "shortcut", dot: { style: "dotted" } } }, 147 | { v: "a", w: "b", label: { name: "path" } }, 148 | { v: "b", w: "c", label: { name: "path" } }, 149 | { v: "c", w: "d", label: { name: "path" } }] } }, 150 | 151 | // valves to side quest 152 | { limit: 3, 153 | lhs: { node: [{ id: "a", label: { $not: { name: "node" } } }, 154 | { id: "d", label: { $not: { name: "node" } } }, 155 | { id: "x", label: { name: "node" } }, 156 | { id: "y", label: { name: "node" } }], 157 | edge: [{ v: "a", w: "d", label: { name: "path" } }, 158 | { v: "a", w: "x", label: { name: "edge" } }, 159 | { v: "x", w: "a", label: { name: "edge" } }, 160 | { v: "x", w: "y", label: { name: "edge" } }, 161 | { v: "y", w: "x", label: { name: "edge" } }, 162 | { v: "y", w: "d", label: { name: "edge" } }, 163 | { v: "d", w: "y", label: { name: "edge" } }] }, 164 | rhs: { node: [{ id: "a" }, 165 | { id: "d" }, 166 | { id: "b", head:["x"], tail:["x"], label: { $merge: [ { $eval: '$x.label' }, 167 | { name: "room" } ] } }, 168 | { id: "c", head:["y"], tail:["y"], label: { $merge: [ { $eval: '$y.label' }, 169 | { name: "room" } ] } }], 170 | edge: [{ v: "a", w: "d", label: { name: "path" } }, 171 | { v: "a", w: "b", label: { name: "valve", dot: { style: "dotted" } } }, 172 | { v: "b", w: "c", label: { name: "path" } }, 173 | { v: "c", w: "d", label: { name: "valve", dot: { style: "dotted" } } }] } }, 174 | 175 | // key-door 176 | { limit: 1, 177 | weight: .5, 178 | lhs: { node: [{ id: "a", label: { $not: { name: "node" } } }, 179 | { id: "d", label: { $not: { name: "node" } } }, 180 | { id: "x", label: { name: "node" } }, 181 | { id: "y", label: { name: "node" } }], 182 | edge: [{ v: "a", w: "d", label: { name: "path" } }, 183 | { v: "a", w: "x", label: { name: "edge" } }, 184 | { v: "x", w: "a", label: { name: "edge" } }, 185 | { v: "x", w: "y", label: { name: "edge" } }, 186 | { v: "y", w: "x", label: { name: "edge" } }, 187 | { v: "y", w: "d", label: { name: "edge" } }, 188 | { v: "d", w: "y", label: { name: "edge" } }] }, 189 | rhs: { node: [{ id: "a" }, 190 | { id: "d" }, 191 | { id: "b", head:["x"], tail:["x"], label: { $merge: [ { $eval: '$x.label' }, 192 | { name: "room" } ] } }, 193 | { id: "c", head:["y"], tail:["y"], label: { $merge: [ { $eval: '$y.label' }, 194 | { name: "room" } ] } }], 195 | edge: [{ v: "a", w: "d", label: { name: "lock", dot: { style: "dotted" } } }, 196 | { v: "a", w: "c", label: { name: "key", dot: { style: "dotted" } } }, 197 | { v: "a", w: "b", label: { name: "path" } }, 198 | { v: "b", w: "c", label: { name: "path" } }, 199 | { v: "c", w: "a", label: { name: "valve", dot: { style: "dashed" } } }] } }, 200 | 201 | ] }, 202 | 203 | { name: 'trim', 204 | rules: [ 205 | { lhs: { node: [{ id: "a" }, 206 | { id: "b" }], 207 | edge: [{ v: "a", w: "b", label: { name: "edge" } }] }, 208 | rhs: { node: [{ id: "a" }, 209 | { id: "b" }], 210 | edge: [] } } 211 | ] } 212 | ] 213 | } 214 | -------------------------------------------------------------------------------- /out/scripts/prettify/prettify.js: -------------------------------------------------------------------------------- 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= 3 | [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), 9 | l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, 11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, 12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), 13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} 14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ 21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), 22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", 23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), 24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", 25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= 26 | !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), 9 | l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, 11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, 12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), 13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} 14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ 21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), 22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", 23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), 24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", 25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= 26 | !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p