{"_id":"@codincod/codemirror-lang-fishlang","_rev":"3-90ae8ea9d6097447916305c80e27802c","name":"@codincod/codemirror-lang-fishlang","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@codincod/codemirror-lang-fishlang","version":"0.1.0","keywords":["code","codemirror","editor","esolang","fish","fishlang"],"author":{"name":"CodinCod","email":"hello@codincod.com"},"license":"MIT","_id":"@codincod/codemirror-lang-fishlang@0.1.0","maintainers":[{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"}],"homepage":"https://codincod.com/","dist":{"shasum":"f173abb9560a60eb49a426c853a76e8b77ffd3f8","tarball":"https://registry.npmjs.org/@codincod/codemirror-lang-fishlang/-/codemirror-lang-fishlang-0.1.0.tgz","fileCount":12,"integrity":"sha512-LQoY/nT7tqw1hzzIUULPvUQjXQaScETvRIWJ3PcgWBOxkrgV+2OQvotg4WbIHZqZj89uLxiZrNo4aaeZbMbmXg==","signatures":[{"sig":"MEUCIQDTRrP4fZZw+NUOOWC9M4mOYpur5jlh5LOcFuTDWmEDZgIgH07keoCpiAGwtTLdhzAnsBWQjG5WYV7nRFr73foaa34=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":26251},"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":"1adbb2994275d34844bbdd7cd70b2fc06027e2d7","scripts":{"gaps":"npm run build && node ../lezer-survey/src/cli.ts gaps --group top","test":"npm run build && node --test \"test/*.test.ts\"","build":"rm -rf dist && lezer-generator src/fishlang.grammar -o src/parser.js && cm-buildhelper src/fishlang.ts","corpus":"npm run build && node ../lezer-survey/src/cli.ts measure --group top","prepare":"npm run build","build-debug":"lezer-generator src/fishlang.grammar --names -o src/parser.js && cm-buildhelper src/fishlang.ts"},"_npmUser":{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"},"repository":{"url":"https://codeberg.org/reeven/codemirror-lang-fishlang","type":"git"},"_npmVersion":"11.6.2","description":"><> (Fish) 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"},"_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-fishlang_0.1.0_1786472140306_0.14030841699096874","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@codincod/codemirror-lang-fishlang","version":"0.1.1","keywords":["code","codemirror","editor","esolang","fish","fishlang"],"author":{"name":"CodinCod","email":"hello@codincod.com"},"license":"MIT","_id":"@codincod/codemirror-lang-fishlang@0.1.1","maintainers":[{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"}],"homepage":"https://codincod.com/","dist":{"shasum":"17ca6334c588d8884384c6f39e212e0b946b1f2a","tarball":"https://registry.npmjs.org/@codincod/codemirror-lang-fishlang/-/codemirror-lang-fishlang-0.1.1.tgz","fileCount":12,"integrity":"sha512-KLb6ERuLl9jtRTcyohjCGCldB/QVK6Hb8SOWn4fDRorGkEzJBGG1UUk70niP6mgXFeyKkOUrE2eS7OY4+hlBmg==","signatures":[{"sig":"MEQCIDcg2C6BCQvp1zX5B9KLT2LQdJ6fJXHdo19YtNLfkZbsAiBcZT+RKHbNSIbEQRm5OmB+g/ztg84ymKgZ+n7J+naMMQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":27675},"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":"9984faf290b09fae2784d0af2a6579ca36530c8a","scripts":{"gaps":"npm run build && node ../lezer-survey/src/cli.ts gaps --group top","test":"npm run build && node --test \"test/*.test.ts\"","build":"rm -rf dist && lezer-generator src/fishlang.grammar -o src/parser.js && cm-buildhelper src/fishlang.ts","corpus":"npm run build && node ../lezer-survey/src/cli.ts measure --group top","prepare":"npm run build","build-debug":"lezer-generator src/fishlang.grammar --names -o src/parser.js && cm-buildhelper src/fishlang.ts"},"_npmUser":{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"},"repository":{"url":"https://codeberg.org/reeven/codemirror-lang-fishlang","type":"git"},"_npmVersion":"11.6.2","description":"><> (Fish) 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"},"_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-fishlang_0.1.1_1786473212973_0.010682788940465926","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@codincod/codemirror-lang-fishlang","version":"0.1.2","description":"><> (Fish) language support for the CodeMirror code editor","keywords":["code","codemirror","editor","esolang","fish","fishlang"],"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":{"build":"rm -rf dist && lezer-generator src/fishlang.grammar -o src/parser.js && cm-buildhelper src/fishlang.ts","build-debug":"lezer-generator src/fishlang.grammar --names -o src/parser.js && cm-buildhelper src/fishlang.ts","prepare":"npm run build","test":"npm run build && node --test \"test/*.test.ts\"","corpus":"npm run build && node ../lezer-survey/src/cli.ts measure --group top","gaps":"npm run build && node ../lezer-survey/src/cli.ts gaps --group top"},"dependencies":{"@codemirror/language":"^6.0.0","@lezer/common":"^1.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-fishlang"},"gitHead":"b39ca370ae3d757114b6fb8e96ae85267b8ecbdc","_id":"@codincod/codemirror-lang-fishlang@0.1.2","_nodeVersion":"25.0.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-C9zzfWyv+Bf+XqZKPbTPr1yvytCkBG2uWuJLJBLAIgxFy3c22xxjT8pFdzb+ozlTMIer+9V884x0G/W+FDyc/Q==","shasum":"4aaab134710a159723fe48bd4bd1619630892f53","tarball":"https://registry.npmjs.org/@codincod/codemirror-lang-fishlang/-/codemirror-lang-fishlang-0.1.2.tgz","fileCount":12,"unpackedSize":28058,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDdDW+1ZRZ/CifUJ7A9B11QfmbPxbbDjXoDH0F+krq0lAiATxc/JxhU3pfIjgKJ2koyabKeOKyze5NYXN/cNhioeGg=="}]},"_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-fishlang_0.1.2_1786473312339_0.12023248484942117"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-11T18:15:40.069Z","modified":"2026-08-11T18:35:12.629Z","0.1.0":"2026-08-11T18:15:40.455Z","0.1.1":"2026-08-11T18:33:33.121Z","0.1.2":"2026-08-11T18:35:12.489Z"},"author":{"name":"CodinCod","email":"hello@codincod.com"},"license":"MIT","homepage":"https://codincod.com/","keywords":["code","codemirror","editor","esolang","fish","fishlang"],"repository":{"type":"git","url":"https://codeberg.org/reeven/codemirror-lang-fishlang"},"description":"><> (Fish) language support for the CodeMirror code editor","maintainers":[{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"}],"readme":"# @codincod/codemirror-lang-fishlang\n\nThis package implements [><>](https://esolangs.org/wiki/Fish) language support\nfor the [CodeMirror](https://codemirror.net/) code editor: a\n[Lezer](https://lezer.codemirror.net/) grammar and highlighting.\n\n><> is pronounced Fish. **It is not the fish shell**, which is a different\nlanguage with the same spoken name, and the two are confused often enough that\nthis package is called `fishlang` rather than `fish` to keep an import from\nbeing a guess. If you came here for the shell, you want\n[@codincod/codemirror-lang-shell](https://www.npmjs.com/package/@codincod/codemirror-lang-shell),\nwhich reads fish along with sh, bash, zsh, ksh93, csh and tcsh.\n\nNo CodeMirror grammar for ><> existed anywhere before this one, in either the\nLezer generation or the CodeMirror 5 modes.\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-fishlang/src/branch/master/LICENSE).\n\n## Usage\n\n```javascript\nimport {EditorView, basicSetup} from \"codemirror\"\nimport {fishlang} from \"@codincod/codemirror-lang-fishlang\"\n\nconst view = new EditorView({\n  parent: document.body,\n  doc: `!v\"Hello world!\"r!\n >l?!;o\n`,\n  extensions: [basicSetup, fishlang()]\n})\n```\n\n## What it reads\n\nA ><> program is a grid and the instruction pointer swims it, in whichever\ndirection the last arrow or mirror sent it. None of that is a tree, so the tree\nthis builds is flat and its error count is worth nothing. Feed this parser a C\nprogram, a JSON document or a page of English and it returns a tree with no\nerror in it, because every character in the grid is either an instruction or a\ncell holding something else, and there is no third answer it could give. A\npackage that quoted a clean percentage here would be quoting the fact that it\nnever says no.\n\nSo the number that means something is the colour. On CodinCod's\nlanguage-guessing pool, 19 snippets of ><> from Rosetta Code:\n\n| | Tagged | Colours | Busiest colour |\n| --- | --: | --: | --: |\n| this | 100.00% | 10 | 30.05% |\n| [the fish shell grammar](https://www.npmjs.com/package/@codincod/codemirror-lang-shell), which is what it had | 66.22% | 6 | 32.93% |\n\nThe shell grammar was not merely worse at colouring it: it failed to parse 15\nof the 19 snippets outright, which is the one place a clean percentage means\nsomething here. It is the only reading in this README taken from a grammar that\ncan actually say no to ><>, and it said no four times in five. No fault of its\nown, and it is a good grammar for the language it was written for; it was simply\nbeing handed the wrong Fish, which is the whole reason this package exists.\n\n## Corpus\n\nEvery other grammar published here was measured against thousands of files of\nreal code, because a figure taken from a snippet pool has flattered a grammar\nbefore. ><> cannot offer thousands. It is a small language and very little of\nit has ever been published, so the corpus is 52 files and 3017 instruction\ncells, gathered from the interpreters that ship examples and from the wiki:\n[icthyologist](https://github.com/antixk/icthyologist) 20,\n[the esolangs wiki](https://esolangs.org/wiki/Fish) 16,\n[fishr](https://github.com/noirotm/fishr) 12 and\n[toberge/esoteric](https://github.com/toberge/esoteric) 4, deduplicated by\ncontent. That is close to all of it.\n\n| | |\n| --- | --: |\n| Files parsed | 52 |\n| Coloured as an instruction or a string | 99.73% |\n| Node kinds the corpus actually uses | 14 of 14 |\n| Cells holding a character outside ASCII | 0 |\n\nSmall as it is, it settled the one decision a table cannot. A quote looks for\nits partner on its own line and gives up at the end of it, and real ><> leans on\nthat: 58 strings pair, and 4 quotes have no partner at all. Every one of the\nfour is deliberate. Two are the quine `\"r00gol?!;40.`, which opens string mode\nand closes it by wrapping the instruction pointer around the codebox rather than\nby writing a second quote. Nothing that reads a line at a time can pair those,\nand reading forward to the next quote anywhere in the file would swallow the\nprogram. The same corpus carries `/\"'\"<`, where a single quote sits inside a\ndouble-quoted string, which is the other half of the same rule.\n\n## Why the corpus is not the evidence\n\nIt cannot be, for this grammar. Every cell's node is a pure lookup on one\ncharacter, so a corpus can only show that the cells somebody happened to write\nare read. What is wanted is that every cell the language *has* is read, and ><>\nis small enough to simply check: 54 single-cell instructions and two quotes, and\nthat is the whole language.\n\n`test/alphabet.test.ts` holds the dispatch switch of\n[go-fish](https://github.com/redstarcoder/go-fish), an interpreter, read\ncharacter for character. It fails if this grammar goes quiet about an\ninstruction, gives one the wrong node, or colours a character the language never\nclaimed. Exhaustive beats representative wherever a language is small enough to\nallow it, and the corpus above is then free to do what only real code can, which\nis to argue about layout.\n\n## What took the work\n\nThree decisions, and two of them are places where a reader who knows Befunge\nwill read a ><> program wrong.\n\n* **The halt is `;`, and `@` is not it.** In Befunge, `@` ends the program. In\n  ><> it rotates the top three of the stack and the semicolon is the halt.\n  Both languages are grids of single-character instructions, both are read by\n  the same kind of grammar, and this is the cell they disagree about most\n  quietly.\n* **><> spends most of the lowercase alphabet, so it has no room for prose.**\n  This is the decision the colour rests on, and it goes the opposite way to\n  the [Befunge-93 grammar](https://codeberg.org/reeven/codemirror-lang-befunge)\n  this one is shaped like. Befunge-93 leaves the letters alone, which is what\n  lets a program carry its explanation in the half of the grid the pointer\n  never reaches and have it stay quiet on the page. ><> cannot: `a` to `f` are\n  the digits ten to fifteen, `o`, `n` and `i` are the whole of input and\n  output, `r` and `l` work the stack, `g` and `p` read and write the grid, and\n  `x` picks a direction at random. A grammar that left the letters alone to\n  keep the commentary quiet would be quiet about exactly the cells that run.\n  What is left inert, and coloured as the comment it serves as, is the\n  uppercase alphabet and the punctuation the language never claimed.\n* **A string stops at the end of its line.** Either quote turns string mode on\n  and the text between two of them is where every legible word in a ><>\n  program lives. But a program that swims upwards can open a string on one row\n  and close it on another, and reading a quote to the next one wherever it is\n  would paint the rest of the program as a string. A quote with no partner on\n  its line is left as one ordinary cell, which is wrong about one character\n  instead of wrong about four hundred.\n\nTwo smaller ones. Each quote closes on its own kind, so `\"it's\"` is a single\nstring rather than two. And a digit does not join the digit beside it: `55` in\n><> is two pushes, and grouping the run would put a number in the tree that the\nlanguage never had.\n\nThe exception nobody can remove: `v`, `<`, `>` and `^` are instructions and\nthey are also letters and punctuation people write. The `v` in \"over\" really\nwould send the pointer downward if it ever arrived there, so it is coloured\nlike the instruction it is.\n\n## Why not read ><> as Befunge\n\nIt is the obvious shortcut, the two languages look alike on a page, and it was\nmeasured before this package was written rather than argued about. Over the\n1577 instruction cells in the pool's ><> snippets, the Befunge grammar gives\n55.8% of them the same kind of name this one does. It gets the arrows, the\ndigits `0` to `9`, `+-*%`, `g`, `p` and the double-quoted strings right, and\nthen it reads ><>'s two commonest mirrors `\\` and `/` as a stack move and a\ndivision, says nothing about the 116 cells of input and output or the 110 hex\ndigits, and calls `@` the end of the program when it is a stack rotate.\n\nBoth grammars tag 100% of a ><> file and land within one colour of each other\nwhile doing it, which is the useful warning in this: the surface numbers a\ngrammar is usually ranked by cannot tell those two apart at all.\n\n## Development\n\n```bash\nnpm install\nnpm test            # the parse specs, the colours, and the alphabet\nnpm run corpus      # how much of a directory of ><> parses clean\nnpm run gaps        # what it could not parse, grouped by shape\n```\n\n`corpus` and `gaps` delegate to\n[lezer-survey](https://codeberg.org/reeven/lezer-survey), which is expected to\nsit beside this checkout. Both are there for consistency with the other\npackages, and both report 100% on the corpus above, which as explained is not a\nmeasurement of anything: a `Data` catch-all can never fail. `npm test` is the\none that can say no.\n","readmeFilename":"README.md"}