{"_id":"@codincod/codemirror-lang-crystal","_rev":"6-c733656e02104ff328a6d6f811da311b","name":"@codincod/codemirror-lang-crystal","dist-tags":{"latest":"0.5.0"},"versions":{"0.1.0":{"name":"@codincod/codemirror-lang-crystal","version":"0.1.0","keywords":["editor","code","crystal","codemirror"],"author":{"name":"CodinCod","email":"hello@codincod.com"},"license":"MIT","_id":"@codincod/codemirror-lang-crystal@0.1.0","maintainers":[{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"}],"homepage":"https://codincod.com/","dist":{"shasum":"9a92678a1a05d7a1e009195e252b4704daac1c04","tarball":"https://registry.npmjs.org/@codincod/codemirror-lang-crystal/-/codemirror-lang-crystal-0.1.0.tgz","fileCount":14,"integrity":"sha512-6d0uvkbhhvSBCSgVNBaIZTo562tqXLer7LXENNIIHcUAQX6h11Yg+7R6c7DdMeiliZ9yOYmuY0t9bjFuQ+NbCQ==","signatures":[{"sig":"MEUCIQDELmo8Pj1ycim/QJksmQ6xrny2h0V2P4uPCWKfmLlIQgIgSx7LeUkKxhgC/oq0gDiMtPiruJAlJSvHzumXAL0rVps=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":824876},"main":"dist/index.cjs","type":"module","types":"dist/index.d.ts","module":"dist/index.js","exports":{"import":"./dist/index.js","require":"./dist/index.cjs"},"gitHead":"cbb91c6679cba8c6143d42df00e363fbde464c05","scripts":{"gaps":"npm run build && node ../lezer-survey/src/cli.ts gaps --group parent","test":"npm run build && node --test \"test/*.test.ts\"","build":"rm -rf dist && NODE_OPTIONS=--max-old-space-size=8192 lezer-generator src/crystal.grammar -o src/parser.js && cm-buildhelper src/crystal.ts","corpus":"npm run build && node ../lezer-survey/src/cli.ts measure --group parent","prepare":"npm run build","build-debug":"NODE_OPTIONS=--max-old-space-size=8192 lezer-generator src/crystal.grammar --names -o src/parser.js && cm-buildhelper src/crystal.ts"},"_npmUser":{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"},"repository":{"url":"git+https://codeberg.org/reeven/codemirror-lang-crystal.git","type":"git"},"_npmVersion":"11.6.2","description":"Crystal language support for the CodeMirror code editor","directories":{},"sideEffects":false,"_nodeVersion":"25.0.0","dependencies":{"@lezer/lr":"^1.0.0","@lezer/common":"^1.0.0","@lezer/highlight":"^1.0.0","@codemirror/language":"^6.0.0","@codemirror/autocomplete":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.6.0","@lezer/generator":"^1.7.0","@codemirror/state":"^6.7.1","@codemirror/buildhelper":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/codemirror-lang-crystal_0.1.0_1785958578398_0.725213291942117","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@codincod/codemirror-lang-crystal","version":"0.1.1","keywords":["editor","code","crystal","codemirror"],"author":{"name":"CodinCod","email":"hello@codincod.com"},"license":"MIT","_id":"@codincod/codemirror-lang-crystal@0.1.1","maintainers":[{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"}],"homepage":"https://codincod.com/","dist":{"shasum":"724ecb18125887a6381cbc133096287d5b61d2f4","tarball":"https://registry.npmjs.org/@codincod/codemirror-lang-crystal/-/codemirror-lang-crystal-0.1.1.tgz","fileCount":14,"integrity":"sha512-z1Un9UvYzONsFEz9leqvGpeSzfy5jEN4J4UPNZ7YlTkDmDOz9NbuNzrwDxCSApeCvPZLgao6BZuN/+Sn2NIFHw==","signatures":[{"sig":"MEUCIQCIERThAnJD/SP+usNwe/ee6OiFRIp/sn4kI8od7zvyAwIgbscIPEoFZQ2gEsbGxq0H61M5mpIJuKtmxmSUy3yxLvI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":824868},"main":"dist/index.cjs","type":"module","types":"dist/index.d.ts","module":"dist/index.js","exports":{"import":"./dist/index.js","require":"./dist/index.cjs"},"gitHead":"cbb91c6679cba8c6143d42df00e363fbde464c05","scripts":{"gaps":"npm run build && node ../lezer-survey/src/cli.ts gaps --group parent","test":"npm run build && node --test \"test/*.test.ts\"","build":"rm -rf dist && NODE_OPTIONS=--max-old-space-size=8192 lezer-generator src/crystal.grammar -o src/parser.js && cm-buildhelper src/crystal.ts","corpus":"npm run build && node ../lezer-survey/src/cli.ts measure --group parent","prepare":"npm run build","build-debug":"NODE_OPTIONS=--max-old-space-size=8192 lezer-generator src/crystal.grammar --names -o src/parser.js && cm-buildhelper src/crystal.ts"},"_npmUser":{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"},"repository":{"url":"https://codeberg.org/reeven/codemirror-lang-crystal","type":"git"},"_npmVersion":"11.6.2","description":"Crystal language support for the CodeMirror code editor","directories":{},"sideEffects":false,"_nodeVersion":"25.0.0","dependencies":{"@lezer/lr":"^1.0.0","@lezer/common":"^1.0.0","@lezer/highlight":"^1.0.0","@codemirror/language":"^6.0.0","@codemirror/autocomplete":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.6.0","@lezer/generator":"^1.7.0","@codemirror/state":"^6.7.1","@codemirror/buildhelper":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/codemirror-lang-crystal_0.1.1_1785970352304_0.19716389344172502","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@codincod/codemirror-lang-crystal","version":"0.2.0","keywords":["editor","code","crystal","codemirror"],"author":{"name":"CodinCod","email":"hello@codincod.com"},"license":"MIT","_id":"@codincod/codemirror-lang-crystal@0.2.0","maintainers":[{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"}],"homepage":"https://codincod.com/","dist":{"shasum":"2ac3360548593d8dba5f2a31b359006816c2a2de","tarball":"https://registry.npmjs.org/@codincod/codemirror-lang-crystal/-/codemirror-lang-crystal-0.2.0.tgz","fileCount":14,"integrity":"sha512-SlRGQs29Gf+wvobjCJRcXdAFtQ/A4QK4n50OOoRqjW1q4c9/nEjvTjM4wnGSINuu/9FRku0MECIojhAJpjACdg==","signatures":[{"sig":"MEYCIQD4uZVlNRl1nOfh6EtSwScsidzUueJNIBgIkjlqCPjBuQIhAL8Uh6APQnNuSnqfwbuHBADNQMxsNB+LmMcc/hXvNMh9","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":824907},"main":"dist/index.cjs","type":"module","types":"dist/index.d.ts","module":"dist/index.js","exports":{"import":"./dist/index.js","require":"./dist/index.cjs"},"gitHead":"e720e78842466d838f57fe09b6a1d43b998e3e25","scripts":{"fuzz":"npm run build && node ../lezer-survey/src/cli.ts fuzz","gaps":"npm run build && node ../lezer-survey/src/cli.ts gaps --group parent","test":"npm run build && node --test \"test/*.test.ts\"","build":"rm -rf dist && NODE_OPTIONS=--max-old-space-size=8192 lezer-generator src/crystal.grammar -o src/parser.js && cm-buildhelper src/crystal.ts","measure":"npm run build && node ../lezer-survey/src/cli.ts measure --group parent","prepare":"npm run build","build-debug":"NODE_OPTIONS=--max-old-space-size=8192 lezer-generator src/crystal.grammar --names -o src/parser.js && cm-buildhelper src/crystal.ts"},"_npmUser":{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"},"repository":{"url":"https://codeberg.org/reeven/codemirror-lang-crystal","type":"git"},"_npmVersion":"11.6.2","description":"Crystal language support for the CodeMirror code editor","directories":{},"sideEffects":false,"_nodeVersion":"25.0.0","dependencies":{"@lezer/lr":"^1.0.0","@lezer/highlight":"^1.0.0","@codemirror/language":"^6.0.0","@codemirror/autocomplete":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.6.0","@lezer/generator":"^1.7.0","@codemirror/state":"^6.7.1","@codemirror/buildhelper":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/codemirror-lang-crystal_0.2.0_1786759583339_0.5792800354007421","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@codincod/codemirror-lang-crystal","version":"0.3.0","keywords":["editor","code","crystal","codemirror"],"author":{"name":"CodinCod","email":"hello@codincod.com"},"license":"MIT","_id":"@codincod/codemirror-lang-crystal@0.3.0","maintainers":[{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"}],"homepage":"https://codincod.com/","dist":{"shasum":"889321c900b7202273a959169964a97b68439496","tarball":"https://registry.npmjs.org/@codincod/codemirror-lang-crystal/-/codemirror-lang-crystal-0.3.0.tgz","fileCount":14,"integrity":"sha512-Ek7hFJlfkil1b2kpXzTvJViHqoNrFOrYBoikWPn58Whxw5nMdOx2IReSWHMNB/XYh8tZX6vyFqJVRjrdPnhCpw==","signatures":[{"sig":"MEUCIAu9yAUbIjW/I3fQDkgIIw5kwzvii2PzZZ4recmwhB5EAiEA8KM2v7yg2wunBQCebf1WNMUPQw7Q+O3eicys64qSCdQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":828673},"main":"dist/index.cjs","type":"module","_from":"file:codincod-codemirror-lang-crystal-0.3.0.tgz","types":"dist/index.d.ts","module":"dist/index.js","exports":{"import":"./dist/index.js","require":"./dist/index.cjs"},"scripts":{"fuzz":"npm run build && node ../lezer-survey/src/cli.ts fuzz","gaps":"npm run build && node ../lezer-survey/src/cli.ts gaps --group parent","test":"npm run build && node --test \"test/*.test.ts\"","build":"npm run parser && cm-buildhelper src/crystal.ts","parser":"test src/parser.js -nt src/crystal.grammar || NODE_OPTIONS=--max-old-space-size=8192 lezer-generator src/crystal.grammar -o src/parser.js","measure":"npm run build && node ../lezer-survey/src/cli.ts measure --group parent","rebuild":"rm -rf dist src/parser.js src/parser.terms.js && npm run build","build-debug":"NODE_OPTIONS=--max-old-space-size=8192 lezer-generator src/crystal.grammar --names -o src/parser.js && cm-buildhelper src/crystal.ts"},"_npmUser":{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"},"_resolved":"/tmp/9f3de1a11af657ef73644b18b42f4ffc/codincod-codemirror-lang-crystal-0.3.0.tgz","_integrity":"sha512-Ek7hFJlfkil1b2kpXzTvJViHqoNrFOrYBoikWPn58Whxw5nMdOx2IReSWHMNB/XYh8tZX6vyFqJVRjrdPnhCpw==","repository":{"url":"https://codeberg.org/reeven/codemirror-lang-crystal","type":"git"},"_npmVersion":"11.6.2","description":"Crystal language support for the CodeMirror code editor","directories":{},"sideEffects":false,"_nodeVersion":"25.0.0","dependencies":{"@lezer/lr":"^1.0.0","@lezer/highlight":"^1.0.0","@codemirror/language":"^6.0.0","@codemirror/autocomplete":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.6.0","@lezer/generator":"^1.7.0","@codemirror/state":"^6.7.1","@codemirror/buildhelper":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/codemirror-lang-crystal_0.3.0_1786831715353_0.544359806815635","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@codincod/codemirror-lang-crystal","version":"0.4.0","keywords":["editor","code","crystal","codemirror"],"author":{"name":"CodinCod","email":"hello@codincod.com"},"license":"MIT","_id":"@codincod/codemirror-lang-crystal@0.4.0","maintainers":[{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"}],"homepage":"https://codincod.com/","dist":{"shasum":"2ea58fc0a6a573615ad5d94ff7e8c44f0a780977","tarball":"https://registry.npmjs.org/@codincod/codemirror-lang-crystal/-/codemirror-lang-crystal-0.4.0.tgz","fileCount":14,"integrity":"sha512-1mD6byVXoNrhswOcp44e/4SC3Acl04tt8BbErByYrMP8dPgccKA8v8WcZK7qTJZwYO1+ATmmQrjXkNrtMtEgdw==","signatures":[{"sig":"MEQCIGIV0NZD9DakaKIBZTclrZBELaMNhDyYKPZTSqo1xMW0AiAzqO+T5mjAJpausxdnNocS5wsgUheFOAwQ8zvwB2fH3Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":842021},"main":"dist/index.cjs","type":"module","types":"dist/index.d.ts","module":"dist/index.js","exports":{"import":"./dist/index.js","require":"./dist/index.cjs"},"gitHead":"de2524497e61a5de9cb92de62e86a407c99cbc77","scripts":{"fuzz":"npm run build && node ../lezer-survey/src/cli.ts fuzz","gaps":"npm run build && node ../lezer-survey/src/cli.ts gaps --group parent","test":"npm run build && node --test \"test/*.test.ts\"","build":"npm run parser && cm-buildhelper src/crystal.ts","parser":"test src/parser.js -nt src/crystal.grammar || NODE_OPTIONS=--max-old-space-size=8192 lezer-generator src/crystal.grammar -o src/parser.js","measure":"npm run build && node ../lezer-survey/src/cli.ts measure --group parent","prepare":"npm run build","rebuild":"rm -rf dist src/parser.js src/parser.terms.js && npm run build","build-debug":"NODE_OPTIONS=--max-old-space-size=8192 lezer-generator src/crystal.grammar --names -o src/parser.js && cm-buildhelper src/crystal.ts"},"_npmUser":{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"},"repository":{"url":"https://codeberg.org/reeven/codemirror-lang-crystal","type":"git"},"_npmVersion":"11.6.2","description":"Crystal language support for the CodeMirror code editor","directories":{},"sideEffects":false,"_nodeVersion":"22.20.0","dependencies":{"@lezer/lr":"^1.0.0","@lezer/highlight":"^1.0.0","@codemirror/language":"^6.0.0","@codemirror/autocomplete":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.6.0","@lezer/generator":"^1.7.0","@codemirror/state":"^6.7.1","@codemirror/buildhelper":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/codemirror-lang-crystal_0.4.0_1787820642316_0.5712398570063455","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@codincod/codemirror-lang-crystal","version":"0.5.0","description":"Crystal language support for the CodeMirror code editor","keywords":["editor","code","crystal","codemirror"],"author":{"name":"CodinCod","email":"hello@codincod.com"},"license":"MIT","type":"module","main":"dist/index.cjs","exports":{"import":"./dist/index.js","require":"./dist/index.cjs"},"module":"dist/index.js","types":"dist/index.d.ts","sideEffects":false,"scripts":{"parser":"test src/parser.js -nt src/crystal.grammar || NODE_OPTIONS=--max-old-space-size=8192 lezer-generator src/crystal.grammar -o src/parser.js","build":"npm run parser && cm-buildhelper src/crystal.ts","rebuild":"rm -rf dist src/parser.js src/parser.terms.js && npm run build","build-debug":"NODE_OPTIONS=--max-old-space-size=8192 lezer-generator src/crystal.grammar --names -o src/parser.js && cm-buildhelper src/crystal.ts","prepare":"npm run build","test":"npm run build && node --test \"test/*.test.ts\"","measure":"npm run build && node ../lezer-survey/src/cli.ts measure --group parent","fuzz":"npm run build && node ../lezer-survey/src/cli.ts fuzz","gaps":"npm run build && node ../lezer-survey/src/cli.ts gaps --group parent","clean":"node ../lezer-survey/src/cli.ts clean ../crystal-corpus --oracle \"$PWD/tools/crystal-oracle.sh {}\" --reject \"crystal: shape error\" --jobs 8"},"dependencies":{"@codemirror/autocomplete":"^6.0.0","@codemirror/language":"^6.0.0","@lezer/highlight":"^1.0.0","@lezer/lr":"^1.0.0"},"devDependencies":{"@codemirror/buildhelper":"^1.0.2","@codemirror/state":"^6.7.1","@lezer/generator":"^1.7.0","typescript":"^5.6.0"},"homepage":"https://codincod.com/","repository":{"type":"git","url":"https://codeberg.org/reeven/codemirror-lang-crystal"},"gitHead":"a5b47d8cd1ca6a4ada3161880e57bedb532bb457","_id":"@codincod/codemirror-lang-crystal@0.5.0","_nodeVersion":"22.20.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-bXtN3Nn0v6HoSh8FOBGS6Ynr+IRby4zPwaPVpHIAlhffsA/fGqOcT7sSVq3GpvcffrFPY3mqz1NoKh0+/7q6Og==","shasum":"f241212fe599e06669dbef312c3850ba033200a7","tarball":"https://registry.npmjs.org/@codincod/codemirror-lang-crystal/-/codemirror-lang-crystal-0.5.0.tgz","fileCount":14,"unpackedSize":842889,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIDBeFkogo5JmyL5IybbroR5+6sYRThX1O+dBzaDg/NGIAiEA795pDmN33JRCO9N059enXGIeObzHzss9rRmmZGz+yJU="}]},"_npmUser":{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"},"directories":{},"maintainers":[{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/codemirror-lang-crystal_0.5.0_1787823826094_0.17432240663129073"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-05T19:36:18.200Z","modified":"2026-08-27T09:43:46.441Z","0.1.0":"2026-08-05T19:36:18.584Z","0.1.1":"2026-08-05T22:52:32.497Z","0.2.0":"2026-08-15T02:06:23.494Z","0.3.0":"2026-08-15T22:08:35.529Z","0.4.0":"2026-08-27T08:50:42.453Z","0.5.0":"2026-08-27T09:43:46.255Z"},"author":{"name":"CodinCod","email":"hello@codincod.com"},"license":"MIT","homepage":"https://codincod.com/","keywords":["editor","code","crystal","codemirror"],"repository":{"type":"git","url":"https://codeberg.org/reeven/codemirror-lang-crystal"},"description":"Crystal language support for the CodeMirror code editor","maintainers":[{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"}],"readme":"# @codincod/codemirror-lang-crystal [![NPM version](https://img.shields.io/npm/v/@codincod/codemirror-lang-crystal.svg)](https://www.npmjs.org/package/@codincod/codemirror-lang-crystal)\n\n[ [**CHANGELOG**](https://codeberg.org/reeven/codemirror-lang-crystal/src/branch/master/CHANGELOG.md) ]\n\nThis package implements Crystal language support for the\n[CodeMirror](https://codemirror.net/) code editor, using a\n[Lezer](https://lezer.codemirror.net/) grammar written for this package.\n\nWritten in part for [CodinCod](https://codincod.com/), a competitive coding\nplatform, where it colours the editor people solve puzzles in.\n\nThis code is released under an\n[MIT license](https://codeberg.org/reeven/codemirror-lang-crystal/src/branch/master/LICENSE).\n\n## Usage\n\n```javascript\nimport {EditorView, basicSetup} from \"codemirror\"\nimport {crystal} from \"@codincod/codemirror-lang-crystal\"\n\nconst view = new EditorView({\n  parent: document.body,\n  doc: `class Greeter\n  def initialize(@name : String)\n  end\n\n  def greet : Nil\n    puts \"Hello, #{@name}!\"\n  end\nend\n`,\n  extensions: [basicSetup, crystal()]\n})\n```\n\n## Coverage\n\nCrystal 1.x. The type language, macros, `lib` bindings and the whole of the\nRuby-shaped surface underneath them: heredocs, percent literals with any\ndelimiter, operator methods, `begin` blocks with their rescue and ensure, and\nblocks in both spellings.\n\nMeasured against the Crystal compiler, its standard library and the shards\npeople depend on, 18998 files and 65 MiB, 96.32% parse with no error node. What\nis left is a long tail: no shape in it costs more than a handful of files, and\nthe ones worth naming are under *What the tree does not say* below.\n\n`npm run clean` sifts the corpus with Crystal's own formatter.\n`crystal tool format --check` parses a file and says whether the text it would\nwrite back differs from what it read; a file it cannot parse is reported as a\nsyntax error, and that is the only answer `tools/crystal-oracle.sh` asks for. A\nfile that parses and is spelled some other way than the formatter spells it is\nCrystal, and turning it down would be a house style deciding what the language\nis. 55 files went, and the figure moved from 96.19%: Crystal parses a macro\nbody without expanding it, so it turns down far less than a compiler would,\nwhich is the right side to err on when the grammar has one file and the macro\nneeds the program.\n\n## Crystal is not Ruby\n\nCrystal borrows Ruby's surface and puts a static type language underneath it,\nso this grammar started as the one in `@codincod/codemirror-lang-ruby` and had\nthree things added.\n\n**Types are their own language.** `x : Int32`, `Array(String?)`,\n`(Int32, String) -> Bool` and `Foo::Bar*` are read by rules of their own,\nreached from the places a type may stand. Reading them as expressions would\nwork until the first `?`, which an expression reads as a ternary.\n\n**A macro is a program about the program.** `{{ … }}` holds an expression and\nis parsed as one. `{% … %}` holds a directive, is written between the\nstatements it decides about rather than around them, and is skipped like a\ncomment.\n\n**A `lib` is C.** Inside one, `fun` declares rather than defines, a struct\nholds fields and nothing else, and `type` names a type.\n\nGoing the other way, Crystal has no `=begin` comments, no `__END__`, no `?c`\ncharacter literals, no `defined?`, `undef`, `redo`, `retry` or `for`, no\nnumbered block parameters, no singleton classes, no heredocs opened without a\ndash, and none of the words `and`, `or` and `not`, which it spells `&&`, `||`\nand `!` and leaves free to be method names.\n\n## What a tokenizer has to settle\n\nAlmost all of Crystal can be written down as a grammar. What cannot is the\nwhole of `tokens.ts`, and most of it is one question asked in different places:\nthe tokenizer asks the parser what could stand here, and the answer picks the\ntoken.\n\n**A slash is a pattern or a division.** `a / b` divides and `a =~ /b/` matches.\nWhere a division cannot stand the parser has already ruled it out; where both\ncould, Crystal's own rule decides, which is that `a /b/` with a space in front\nand none behind is a call taking a pattern.\n\n**A brace is a block, a hash, or a collection with a name.** `each { |x| }`\npasses a block, `{a: 1}` is a value, and `Set{1, 2}` is a set. The first two\nare told apart by asking whether a block could stand here; the third by the\nname being written against the brace.\n\n**A parenthesis is a parameter list, an argument list, or a generic's\narguments.** `def f(x)`, `f(x)` and `Array(Int32)` all spell it the same way,\nand the three are asked about in that order. Without the third,\n`record T, x : Array(Type), y : Int32` reads the generic as an argument list\nand swallows the name in front of it.\n\n**A question mark is a nilable type, a lookup that may fail, or a ternary.**\n`String?`, `a[0]?` and `a ? b : c`. The first two are written against what they\nfollow and nothing that follows them opens an expression.\n\n**A colon is a declaration, a label, a ternary's middle, or half of a path.**\n`x : Int32`, `name: 1`, `a ? b : c` and `A::B`. A regular token takes the\nlonger match and reads `A::B` as the label `A:` followed by the symbol `:B`, so\nthe second colon is looked at before the first is believed.\n\n**A comma may belong to a type.** `&block : String, Int32 -> Nil` takes two\narguments where the parameter list it stands in takes one. Crystal settles it\nby looking at whether a type follows the comma, and so does this.\n\n**A comma may be the last one.** Every list in the language may be written with\na comma after its last thing, and `[foo \"a\",]` hands the call one argument\nwhere `[foo \"a\", b]` hands it two. Which of the two this is cannot be known\nuntil the bracket, one token further on than a parser reads, so the comma in\nfront of something that closes arrives as a token of its own. What the parser\nskips the look ahead skips too, or the comma of `{% for … %} x, {% end %})`\nwould be an ordinary one promising an argument the parenthesis never delivers.\n\n**A block belongs to the innermost call.** `each(&.foo { |x| x })` gives the\nblock to `foo` rather than to `each`, whose block the `&.` already is. Both\nreadings want a brace where the brace stands, so this one arrives as its own\ntoken as well, and only the inner call may take it. `do` is the same block\nwritten the other way and is settled the same way.\n\n**A path goes on where it was written.** `def f : String` ends at the end of\nits line, so the `::JWT.encode` on the next one begins a statement rather than\ncontinuing the name of the type returned. This grammar skips newlines, so the\ncolons that carry a path on are marked in front, and the mark is only made on\nthe line the path began on.\n\n**A newline ends a statement.** `x = 1` followed by `if y` is two statements;\n`x = 1 if y` is one. `rescue` splits the same way. So does the end of an\nargument list written without parentheses. This grammar skips newlines like any\nother space, so each of those is decided by looking at where the line began.\n\n**A heredoc's body is nowhere near its name.** `<<-SQL` promises a body that\nstarts on the next line, and one line may promise several. The body is read at\nthe newline by looking back over the line that just ended, then skipped like\nwhitespace. The name has to be the whole of the line that closes it, or a body\nopening `<<-HTTP` would be closed by its own `HTTP/1.1 200`.\n\n**A brace against a word continues that word.** `Int{{n}}` is one type per\nsize and `int{{bits}}` the method that reads one, where `{{ n }}` on its own is\na macro standing where a value goes. Whether the brace is written against what\nprecedes it is the whole of the difference.\n\n**A word may be a name after all.** `property end : Int32` calls a field after\na word the language spells out. Read as the keyword it would close something,\nso where a name may stand and a declaration's colon follows, the word is handed\nover as a name. `self?` is the same problem from the other side: a name may end\nin a question mark, so `self?` arrives as one word where `Int32?` arrives as\ntwo.\n\n**A string holds code.** `\"total: #{a + b}\"` is a string with an expression\ninside it, and that expression may hold another string. The tokenizer cuts the\nstring into pieces and the grammar parses what lies between them.\n\n## What the tree does not say\n\nSome things are deliberately shallower than the language reference.\n\n**A macro directive is skipped.** `{% if flag?(:win32) %}` is written between\nthe statements it decides about rather than around them, so a grammar that gave\nit a place in the tree would have to give it one inside every list, and\n`[{% for x in y %} {{x}}, {% end %}]` would be an error rather than a list of\none thing. Skipping it costs the directive its own colours and buys a parse\nthat holds.\n\nWhat it does not buy is a directive that chooses between two halves of one\nconstruct. Where the standard library writes\n\n```crystal\n{% if compare_versions(Crystal::VERSION, \"1.1.1\") >= 0 %}\ndef map!(& : T -> _) : self\n{% else %}\ndef map!(&)\n{% end %}\n  # ...\nend\n```\n\nskipping the directives leaves two method headers and one `end`, and the first\nof them swallows the rest of the file. Reading only the first branch would fix\nit and would grey out every `{% else %}` body in the language, which is the\nworse trade for an editor.\n\n**Inline assembly is read to its closing parenthesis.** What stands between the\ncolons of an `asm` is a constraint language of its own, and nothing that reads\nCrystal has any use for a tree of it.\n\n**A macro cannot stand where an operator does.** `@x {{op.id}} other.x` builds\nan expression around a spliced operator. A name may be spliced into and a\nmethod may be named by a macro, but an infix position asks the grammar to take\nthe macro's word for what binds to what.\n","readmeFilename":"README.md"}