{"_id":"@codincod/codemirror-lang-euphoria","_rev":"2-53e187d68ceb492d90b32102506dc019","name":"@codincod/codemirror-lang-euphoria","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@codincod/codemirror-lang-euphoria","version":"0.1.0","keywords":["codemirror","code","editor","euphoria","openeuphoria","phix"],"author":{"name":"CodinCod","email":"hello@codincod.com"},"license":"MIT","_id":"@codincod/codemirror-lang-euphoria@0.1.0","maintainers":[{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"}],"homepage":"https://codincod.com/","dist":{"shasum":"4f8577c9e8dcb8bb57d98b211bd7fa6feae2e34a","tarball":"https://registry.npmjs.org/@codincod/codemirror-lang-euphoria/-/codemirror-lang-euphoria-0.1.0.tgz","fileCount":13,"integrity":"sha512-ZvrH1ieZRuwSQRjRtid6QdHXxU9m21r68MMDFkG+E4S4DdypvsNosOcGLzzfvsnUhsygEtcralj0ccrhFjm0hQ==","signatures":[{"sig":"MEUCIQDxZ+28q0G28GFoox+Gn5NCvymkQwTmPSha2qLYsJDD7gIgCKMJTPabypZq7I1mCmvgm1yZjk1TOeJ5MaIkhqmFlHc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":213543},"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":"731ffcab28a79e84f159a58c24a20ef38e42174b","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 && lezer-generator src/euphoria.grammar -o src/parser.js && cm-buildhelper src/euphoria.ts","corpus":"npm run build && node ../lezer-survey/src/cli.ts measure --group parent","prepare":"npm run build","build-debug":"lezer-generator src/euphoria.grammar --names -o src/parser.js && cm-buildhelper src/euphoria.ts"},"_npmUser":{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"},"repository":{"url":"https://codeberg.org/reeven/codemirror-lang-euphoria","type":"git"},"_npmVersion":"11.6.2","description":"Euphoria and Phix 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-euphoria_0.1.0_1786235605252_0.2597523144960783","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@codincod/codemirror-lang-euphoria","version":"0.2.0","description":"Euphoria and Phix language support for the CodeMirror code editor","keywords":["codemirror","code","editor","euphoria","openeuphoria","phix"],"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/euphoria.grammar -o src/parser.js && cm-buildhelper src/euphoria.ts","build-debug":"lezer-generator src/euphoria.grammar --names -o src/parser.js && cm-buildhelper src/euphoria.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"},"dependencies":{"@codemirror/autocomplete":"^6.0.0","@codemirror/language":"^6.0.0","@codemirror/state":"^6.7.1","@lezer/common":"^1.0.0","@lezer/highlight":"^1.0.0","@lezer/lr":"^1.0.0"},"devDependencies":{"@codemirror/buildhelper":"^1.0.2","@lezer/generator":"^1.7.0","typescript":"^5.6.0"},"homepage":"https://codincod.com/","repository":{"type":"git","url":"https://codeberg.org/reeven/codemirror-lang-euphoria"},"gitHead":"28c50e610f62cbb5ed42fefa070b1212b7ba3a0a","_id":"@codincod/codemirror-lang-euphoria@0.2.0","_nodeVersion":"25.0.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-A/WRLEbzR6PRKa5bwaudmsGnPdQoBvJCslz8R0eGwK9IAMWc5kWWyClns0Pp5hrscCHCSat1vuyIpIcaCZ8Njg==","shasum":"43cac19de25eb6f0dcb707428a790adbeac37a5f","tarball":"https://registry.npmjs.org/@codincod/codemirror-lang-euphoria/-/codemirror-lang-euphoria-0.2.0.tgz","fileCount":13,"unpackedSize":217985,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBjf4L8LPVc+yUm+HoO8MZyGWL7qn8McOOUS6rCNrhwJAiEA6tGyysEGEQk/uyUrOc7nUyD5kX4Lp+lYyDK8sMH6J2E="}]},"_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-euphoria_0.2.0_1786759606120_0.005869420580756568"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-09T00:33:25.121Z","modified":"2026-08-15T02:06:46.470Z","0.1.0":"2026-08-09T00:33:25.420Z","0.2.0":"2026-08-15T02:06:46.291Z"},"author":{"name":"CodinCod","email":"hello@codincod.com"},"license":"MIT","homepage":"https://codincod.com/","keywords":["codemirror","code","editor","euphoria","openeuphoria","phix"],"repository":{"type":"git","url":"https://codeberg.org/reeven/codemirror-lang-euphoria"},"description":"Euphoria and Phix language support for the CodeMirror code editor","maintainers":[{"name":"juicemitapfelndrin","email":"juicemitapfelndrin@gmail.com"}],"readme":"# @codincod/codemirror-lang-euphoria [![NPM version](https://img.shields.io/npm/v/@codincod/codemirror-lang-euphoria.svg)](https://www.npmjs.org/package/@codincod/codemirror-lang-euphoria)\n\n[ [**CHANGELOG**](https://codeberg.org/reeven/codemirror-lang-euphoria/src/branch/master/CHANGELOG.md) ]\n\nThis package implements Euphoria and Phix 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\nNeither language had a grammar anywhere. There is no Lezer parser, no\ntree-sitter grammar and no CodeMirror 5 stream mode for either, so a `.exw` file\nin an editor was plain text, or at best the comments and strings a generic\nfallback could find.\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-euphoria/src/branch/master/LICENSE).\n\n## Usage\n\n```javascript\nimport {EditorView, basicSetup} from \"codemirror\"\nimport {phix} from \"@codincod/codemirror-lang-euphoria\"\n\nconst view = new EditorView({\n  parent: document.body,\n  doc: `include std/math.e\n\nconstant NAMES = {\"ann\",\"bo\",\"cy\"}\n\nfunction greet(string name, integer times = 1)\n    sequence out = {}\n    for i=1 to times do\n        out = append(out,sprintf(\"hello, %s\",{name}))\n    end for\n    return out\nend function\n\nfor name in NAMES do\n    puts(1,join(greet(name),\"\\\\n\"))\nend for\n`,\n  extensions: [basicSetup, phix()]\n})\n```\n\nImport `euphoria()` instead for Euphoria. The two are one grammar under two\ndialects; the section below says what the dialect decides.\n\n## One package, two languages\n\nPhix is a reimplementation of Euphoria that keeps roughly 999 lines in every\nthousand compiling unchanged. Phix's own tooling takes the same view: its\neditor colours Phix files with a syntax definition named `Euphoria.syn`, and\nthat file, together with OpenEuphoria's `include/euphoria/keywords.e`, is where\nthis grammar's word lists were read from.\n\nWhat the dialect settles is which words a program may use as names, and it\nmatters in both directions. `label`, `loop`, `entry`, `retry` and `routine` are\nreserved in Euphoria and ordinary identifiers in Phix; `class`, `struct`, `try`,\n`catch`, `forward`, `default`, `from` and `in` are the other way round.\nReserving the union would break real files in both languages, so neither\ndialect is shown the other's words.\n\nThe dialect also selects the constructs only one of them has. Euphoria gets\n`loop ... until ... end loop`, `label`/`goto` by string, and the `entry` clause\nof a `while`. Phix gets `for x in s`, `class` and `struct`, `try`/`catch`, the\n`cond?a:b` operator, the `?` that discards an element of an unpacking, and\n`#ilASM` blocks.\n\nEverything else is shared and needs no dialect: declarations, routines, `if`,\n`for`, `while`, `switch`, `ifdef`, `enum`, `include`, `with`/`without`, every\nnumber and string spelling, and all five ways of writing a comment.\n\n## Coverage\n\nMeasured on the source of both implementations and on 1735 Rosetta Code\nsnippets, counting files that parse with no error node:\n\n| Corpus | Files | Clean |\n| --- | ---: | ---: |\n| Rosetta Code, Euphoria | 270 | 97.78% |\n| Rosetta Code, Phix | 1465 | 97.68% |\n| OpenEuphoria | 469 | 96.80% |\n| Phix | 1506 | 97.14% |\n\nThe two implementation corpora are the hard end of the range and are the reason\nthey are quoted. They are compilers written in their own language: OpenEuphoria\nholds the interpreter, its standard library and its conformance tests, and the\nPhix tree holds the whole compiler, its builtins and its demos, including files\nthat keep C and assembler beside the Phix for reference. Ordinary programs, of\nthe kind an editor is usually shown, are the Rosetta rows.\n\nThe corpora do not ship with this package. Both are somebody else's, so\n`npm run corpus` takes the path to your own checkout.\n\n## What a tokenizer has to settle\n\nMost of this language can be written down as grammar rules. What is left over\nis the whole of `tokens.ts`.\n\n**An include names a file, not an expression.** `include std/math.e` has a\nslash and a dot in it that are not operators, and `include builtins\\machine` has\na backslash. The path is one token, and it is only offered where the parser can\nalready take one.\n\n**`with` and `without` take the rest of their line.** The clauses are open\nended: `with define BITS64`, `with warning {none}`, `without inline`,\n`format PE32`. A rule that read them as a list of identifiers would eat the\nfirst word of the next line, because nothing in this language terminates a\nstatement.\n\nBoth of those work by falling back rather than by competing. Lezer reads a\ngrammar's tokenizers in the order they are written and stops at the first that\nhands the parser something it can use, so a path would otherwise arrive as the\nidentifier `math` with `/std.e` left over. What makes that not happen is that no\nordinary token can be shifted after `include` or `with`: the parser has nothing\nto do with the identifier, the run carries on, and the line is read instead.\n\nA comment goes the other way and is declared in front of everything, because\n`--`, `/` and `#` are all an operator or a number until you have read further.\n\n**A comment counts how deep it is.** Both compilers nest: OpenEuphoria's\nscanner keeps a level and Phix's calls itself, and either closer shuts either\nopener. That is what the dashed form is for. A file says *this line is code in\nPhix and a comment in Euphoria* by putting an opener and a closer in front of\nit, and it says *this whole passage is Euphoria's* by opening alone. A scanner\nthat stopped at the first closer would take the second of those for the first\nand read the rest of the file at the wrong level, which is most of what a\ngrammar for these languages gets wrong.\n\n**A hash is a number and a block of assembler.** `#ab` is hexadecimal and\n`#ilASM{ ... }` holds another language, with its own uses for `[`, `%` and `;`.\nThe brace is what tells them apart and it lies past the word, so the tokenizer\nreads ahead before claiming either.\n\n**A colon ends a case label and joins a qualified name.** `case 1: x = 1` and\n`case reserved:MULTIPLY then` put the same character in the same place, and one\ntoken of lookahead sees no difference. What tells them apart is the space around\nit: a qualifier is written tight against the name it qualifies, and a label's\ncolon is followed by the end of the line or by the statement it introduces.\n\n**A question mark is a statement and an operator.** `?x` prints x and `a?b:c`\nchooses between b and c. After a statement that was itself an expression both\nare possible, and the line under `?sum(v)` may well open with a `?` of its own,\nwhich a parser reading it as a conditional would join to the line above. Where\nonly one fits, that is the answer; where both do, the one that opens a line is\nthe statement, because nobody writes the middle of an expression in the first\ncolumn. A semicolon counts as opening a line, so `x = f(); ?x` reads as two\nstatements.\n\n## What the grammar decides\n\n**Two identifiers in a row is a declaration.** A type in these languages is an\nordinary function that a declaration happens to stand in front of, so `integer i`\nand `my_colour c` are the same shape and neither word is reserved. That is the\nwhole of how a declaration is told from a call, and it is why a statement that\nis only an expression has to be a call: a statement that could be a bare name\nwould make `atom x` two of them.\n\n**A name being declared may be one the runtime already uses.** Both standard\nlibraries define routines over the names of their own runtime, so\n`function insert`, `function get_position` and `sequence length` all have to be\nreadable. A name in a declaration is the one place where no other reading is\npossible, so that is where the built-in words step aside.\n\n**Five statements end where their line does, and nothing says so.** `return`,\nand the four that may name a label, all end without an operand often enough that\nthe parser has to guess. Each is followed by `end` far more often than by a\nstatement that could have been its operand, so a value is read wherever one\ncould start. The cost is unreachable code directly under a bare `return`, which\nnobody writes.\n\n**A class outweighs a declaration of one.** `class item` opens a class, and\n`class food = box.contents[i]` declares a variable of the base type, so a whole\nclass body is read both ways and one reading is thrown away at the end. Lezer\nkeeps two readings side by side only while they are close in weight, so each\nmember of a class is worth a point to the reading that took `class` for a\nkeyword. Without them a body of any length outlives the split and the\ndeclaration wins by having got there first.\n\n**Inside a conditional, `a?b:c` beats `b:c`.** A colon separates a namespace\nfrom the name it qualifies, and it also separates the two branches of the\nconditional operator, and `a?b:c` is both. The conditional wins, because a\nbranch is rarely a qualified name and a qualified name is common everywhere\nelse. Outside a conditional nothing changes: `math:floor(x)` reads as it should.\n\n## What the tree does not say\n\n**A call is not made on a value.** `f(a)(b)` does not parse, because closing the\nstatement and reading a call of a call are the same two tokens, and closing it\nwould leave `(b)` to be read as a statement of its own. Neither language can\ncall the result of a call, so nothing is lost.\n\n**`iff(cond?a,b)` is not read.** Phix will take a comma in place of the colon\ninside `iff`, and inside an argument list that comma is also the separator. The\ncolon spelling parses; the comma one does not.\n\n**The top level is looser than a compiler's.** An editor is shown fragments as\noften as whole files, so a bare statement, a lone declaration and a routine cut\noff halfway all parse. A line holding nothing but `...` parses too: Phix writes\none where a parameter list goes on, and a published snippet writes one where the\ncode it left out went.\n","readmeFilename":"README.md"}