# FratmScript reference for AI agents FratmScript is JavaScript with Neapolitan keywords. Source files end in `.fratm`; the compiler (written in Rust) turns them into plain modern JavaScript (ES modules) that runs on Node.js or in the browser. Only the keywords change: operators, literals, numbers, strings, regexes, method calls and every JavaScript built-in (`Math`, `JSON`, `Array`, `Promise`, `Map`, `Set`, `fetch`, `process`, `console`, `Object`, `Date`...) are written exactly as in JavaScript. Playground: https://fratmscript.kkratter.com/ · Repo: https://github.com/kkratterf/fratmscript ## Rules that trip up agents 1. **JavaScript keywords are not keywords here.** Never write `const`, `let`, `var`, `function`, `return`, `if`, `else`, `for`, `while`, `new`, `class`, `this`, `true`, `false`, `null`, `try`, `catch`, `throw`, `async`, `await`, `import`, `export`. Use the dialect words in the table below. (`typeof` and `instanceof` are accepted as aliases.) 2. **One statement per line**, or separate statements with `;`. Semicolons are optional, and a line that starts with `[` or `(` always starts a new statement (no JavaScript ASI trap; do not put a defensive `;` at the start of a line). 3. **A line ends the statement unless the expression clearly goes on**: a line may end with a binary operator, `=`, `?`, `:` or `=>`, or the next line may start with `.method()`, `?.`, `?`, `:`, `&&`, `||`, `??` or a comparison; inside `( )` and `[ ]` line breaks never matter. `e`/`o` (the words) at the end of a line do not continue it: use `&&`/`||` there, or put `e`/`o` at the start of an expression inside parentheses. 4. **Braces are required** after `si`, `sinnò`, `pe`, `mentre che`: `si (x) { ... }`. 5. `chist è` (const) cannot be reassigned; declaring the same name twice in a scope is a compile error. Use `tien` (let) for values that change. 6. Labels and tagged templates work as in JavaScript: `fore: pe (...) { rompe fore }`, `` String.raw`a\nb` ``. 7. Compiler and runtime error messages are in Neapolitan, with line and column. See "Reading errors" below; most errors also carry a hint that starts with 💡. 8. **Web pages:** write JSX tags with their Neapolitan names (``, ``, table in "JSX" below; never invent one: an unknown lowercase name stays as it is in the DOM). Attributes and events of HTML tags have Neapolitan names too (`classe`, `esempio`, `quanno-tuocche`; the React names `className`, `onClick` work as well); props of a component are never translated. Class names, ids and Tailwind classes are never translated: `classe="mt-4"` and `.pizza` stay as they are. In `` stile`…` `` only the words of the CSS tables change; check a block with `fratm_stile`. ## Keywords | FratmScript | JavaScript | | --- | --- | | `chist è` | `const` | | `tien` | `let` | | `facc` | `function` | | `piglie` | `return` | | `si` / `sinnò` | `if` / `else` | | `vedimm` / `caso` / `predefinit` | `switch` / `case` / `default` | | `mentre che` | `while` | | `primm fa { } mentre che (c)` | `do { } while (c)` | | `pe` (also `pe ogni`) | `for` | | `pe aspett` | `for await` | | `dint` / `'ncopp` | `of` / `in` (only inside a `pe` header) | | `rompe` / `salta` | `break` / `continue` | | `overo` / `sfòls` | `true` / `false` | | `nisciun` / `boh` | `null` / `undefined` | | `stamm a dì(x)` | `console.log(x)` | | `avvis a dì(x)` / `scrive a dì(x)` | `console.warn(x)` / `console.error(x)` | | `mo vir` / `aspett` | `async` / `await` | | `facc*` / `caccia` / `caccia*` | `function*` / `yield` / `yield*` | | `pruvamm` / `e si schiatta (e)` / `aropp` | `try` / `catch (e)` / `finally` | | `iett` | `throw` | | `nu bell` | `new` | | `na famiglie` / `figlio 'e` | `class` / `extends` | | `costruttore` | `constructor` | | `'o pate` | `super` | | `stu cos` | `this` | | `fisso` | `static` | | `damme` / `miette` | `get` / `set` (class members) | | `chiamm { a } da "m"` | `import { a } from "m"` | | `chiamm X da "m"` / `chiamm * comme ns da "m"` | `import X from "m"` / `import * as ns from "m"` | | `comme` (also `as`) | `as` (in imports and exports) | | `mann for` / `mann for predefinit` | `export` / `export default` | | `e` (also `pure`, `&&`) | `&&` | | `o` (also `\|\|`) | `\|\|` | | `no` (also `manco`, `!`) | `!` | | `chè è x` | `typeof x` | | `x è uno T` | `x instanceof T` | | `"k" dint'a o` | `"k" in o` | | `leva` | `delete` | | `fermete` | `debugger` | No accents needed: `sinno`, `sfols`, `stamm a di`, `chist e` work too. Short words such as `e`, `o`, `no`, `da`, `caso` are only keywords where their construct starts, so they also work as names (`e si schiatta (e)`). Names reserved in JavaScript are renamed in the output (`tien for` becomes `let $for`). `$` works in names as in JavaScript (`$`, `$el`), except `$` followed by a reserved word, which is that renamed spelling. ## Variables and values ```fratm chist è nome = "Gennaro" tien contatore = 0 tien x = 1, y = 2 contatore += 1 contatore++ chist è attivo = overo chist è vuoto = nisciun chist è boh_che_è = boh chist è saluto = `Uè ${nome}, tieni ${contatore} pizze` chist è lista = [1, 2, 3] chist è pizza = { nome: "Margherita", prezzo: 6.5 } chist è tipo = chè è nome stamm a dì(saluto, tipo, lista è uno Array) ``` ## Functions ```fratm facc somma(a, b) { piglie a + b } chist è doppio = (x) => x * 2 chist è saluta = (nome = "Gennà", ...altri) => `Uè ${nome}` chist è quadrati = [1, 2, 3].map((n) => { piglie n * n }) stamm a dì(somma(2, 3), doppio(4), saluta(), quadrati) // function expressions (anonymous or named, also mo vir facc and facc*) chist è triplo = facc (x) { piglie x * 3 } chist è fatt = facc f(n) { piglie n <= 1 ? 1 : n * f(n - 1) } stamm a dì(triplo(2), fatt(5)) ``` ## Control flow ```fratm chist è voto = 7 si (voto >= 6 e voto < 9) { stamm a dì("promosso") } sinnò si (voto >= 9) { stamm a dì("bravo assai") } sinnò { stamm a dì("bocciato") } chist è esito = voto > 5 ? "ok" : "no" vedimm (voto) { caso 10: stamm a dì("perfetto") rompe predefinit: stamm a dì("normale") } pe (tien i = 0; i < 3; i++) { si (i == 1) { salta } stamm a dì(i) } fore: pe (tien i = 0; i < 3; i++) { pe (tien j = 0; j < 3; j++) { si (j == 2) { salta fore } si (i == 2) { rompe fore } } } pe ogni (chist è frutto dint ["mela", "pera"]) { stamm a dì(frutto) } pe ogni (chist è chiave 'ncopp { a: 1, b: 2 }) { stamm a dì(chiave) } tien n = 0 mentre che (n < 3) { n++ } primm fa { n-- } mentre che (n > 0) ``` ## Objects, destructuring, spread ```fratm chist è nome = "Margherita" chist è campo = "prezzo" chist è pizza = { nome, [campo]: 6.5, ingredienti: ["pummarola", "muzzarella"], descrivi() { piglie `${stu cos.nome}: ${stu cos.ingredienti.join(", ")}` }, damme quanti() { piglie stu cos.ingredienti.length }, // getter (miette = setter) } chist è { prezzo, forno = "a legna", ...altro } = pizza chist è [primo, ...ato] = pizza.ingredienti tien a = 1 tien b = 2 [a, b] = [b, a] // swap; objects: ({ x, y } = punto) chist è copia = { ...pizza, prezzo: 7 } chist è tutti = [...ato, "basilico"] stamm a dì(pizza.descrivi(), prezzo, forno, primo, copia.prezzo, tutti) stamm a dì(pizza.cliente?.nome ?? "anonimo") leva copia.prezzo ``` ## Classes ```fratm na famiglie Persona { costruttore(nome) { stu cos.nome = nome } saluta() { piglie "Uè, so' " + stu cos.nome } fisso specie() { piglie "Essere umano" } } na famiglie Pizzaiolo figlio 'e Persona { #forno = "a legna" pizze = 0 fisso aperte = 0 costruttore(nome) { 'o pate(nome) } damme forno() { piglie stu cos.#forno } miette forno(tipo) { stu cos.#forno = tipo } } chist è ciro = nu bell Pizzaiolo("Ciro") ciro.forno = "elettrico" stamm a dì(ciro.saluta(), ciro.forno, Persona.specie(), ciro è uno Persona) // static block, and a class used as a value na famiglie Menu { fisso pizze = [] fisso { Menu.pizze.push("Margherita") } } chist è Anonima = na famiglie figlio 'e Persona { } stamm a dì(Menu.pizze, nu bell Anonima("Totò").saluta()) ``` ## Errors ```fratm facc dividi(a, b) { si (b == 0) { iett nu bell Error("Nun se divide pe zero!") } piglie a / b } pruvamm { dividi(1, 0) } e si schiatta (errore) { scrive a dì(errore.message) } aropp { stamm a dì("fatto") } ``` ## Async and generators ```fratm chist è aspettaUnPoco = (ms) => nu bell Promise((risolvi) => setTimeout(risolvi, ms)) mo vir facc carica() { aspett aspettaUnPoco(10) piglie "dati" } facc* conta(n) { pe (tien i = 1; i <= n; i++) { caccia i } } mo vir facc main() { chist è dati = aspett carica() chist è tutti = aspett Promise.all([carica(), carica()]) stamm a dì(dati, tutti, [...conta(3)]) } main() ``` Top-level `aspett` works too, because the output is an ES module. ## Modules ```fratm chiamm { readFileSync } da "node:fs" chiamm * comme path da "node:path" chiamm os da "node:os" chiamm { a_caso comme dado } da "fratm" mann for facc leggi(nome) { piglie readFileSync(path.join(".", nome), "utf8") } chist è VERSIONE = "1.0" mann for { VERSIONE, leggi comme read } mann for predefinit leggi // dynamic import: chiamm("…") gives a Promise chist è url = aspett chiamm("node:url") stamm a dì(os.platform(), dado(1, 6), chè è url.pathToFileURL) ``` Also: `chiamm "./stile.css"` (side effects only), `mann for { a } da "./m.js"` and `mann for * da "./m.js"` (re-exports), `chiamm { predefinit comme App } da "./app.js"` (`predefinit` means `default` on the module side). `fratm lancia` compiles one file: to import another `.fratm` file, build each file with `fratm build` and import the `.js`, or use the Vite plugin. ## JSX: components and web pages `.fratm` files can contain JSX, so React (or Preact) components are written in FratmScript. HTML tags have Neapolitan names (`` is `div`), the same as in htmlemmurt pages, translated at compile time; HTML names (`div`, `button`) work too. A capitalised or dotted tag is a component. The words of v0.11 and v0.12 (``, ``, `quanno-se-clicca`, `stile=`) still compile, with a warning that names the new word. ``, `` and `` changed meaning: they are now `title`, `td` and `head`, and the compiler warns. Write ``, `` and `` for `h1`, `input` and `header`. Inside `{ }` goes any FratmScript expression. ```fratm chiamm { useState } da "react" chiamm "./stile.css" facc Pizza({ nomme, prezzo }) { piglie {nomme}: {prezzo} € } mann for predefinit facc Menu({ pizze }) { tien [scelte, setScelte] = useState(0) piglie ( Pizze scelte: {scelte} {pizze.map((p) => )} {scelte > 0 ? Bravo! : nisciun} setScelte(scelte + 1)}>Scegli ) } ``` - Supported: elements, self-closing tags, fragments `<>…`, `{expr}` attributes and children, `{...props}`, boolean attributes, `{/* comments */}`, HTML entities (`&`, ` `). - Neapolitan tags (the output always has the HTML name): | FratmScript | HTML | | --- | --- | | `cristian` | `html` | | `capa` | `head` | | `panz` | `body` | | `titolo` | `title` | | `inciucc` | `meta` | | `attacca` | `link` | | `basa` | `base` | | `cess` | `style` | | `fratm` | `script` | | `senzafratm` | `noscript` | | `cappiell` | `header` | | `pere` | `footer` | | `tomtom` | `nav` | | `core` | `main` | | `piezz` | `section` | | `fattariello` | `article` | | `allato` | `aside` | | `indirizzo` | `address` | | `luccolone` | `h1` | | `luccolo` | `h2` | | `luccoliello` | `h3` | | `luccolillo` | `h4` | | `luccolino` | `h5` | | `luccolicchio` | `h6` | | `cascett` | `div` | | `chiacchier` | `p` | | `stacc` | `hr` | | `accussì` | `pre` | | `accussi` | `pre` | | `citazione` | `blockquote` | | `list` | `ul` | | `elenco` | `ol` | | `voce` | `li` | | `vocabolario` | `dl` | | `parola` | `dt` | | `significato` | `dd` | | `quadro` | `figure` | | `didascalia` | `figcaption` | | `vattenn` | `a` | | `scatulell` | `span` | | `acap` | `br` | | `fort` | `strong` | | `corsivo` | `em` | | `guapp` | `b` | | `stuorto` | `i` | | `sottolineato` | `u` | | `cancellato` | `s` | | `piccirill` | `small` | | `evidenziato` | `mark` | | `codice` | `code` | | `fonte` | `cite` | | `virgulette` | `q` | | `abbreviazione` | `abbr` | | `tiempo` | `time` | | `sotto` | `sub` | | `ncoppa` | `sup` | | `tastiera` | `kbd` | | `levato` | `del` | | `aggiunto` | `ins` | | `cartulin` | `img` | | `cornice` | `picture` | | `funtana` | `source` | | `filmato` | `video` | | `suono` | `audio` | | `fenestella` | `iframe` | | `tela` | `canvas` | | `disegno` | `svg` | | `tavulat` | `table` | | `cartello` | `caption` | | `capotavula` | `thead` | | `cuorpotavula` | `tbody` | | `peretavula` | `tfoot` | | `fila` | `tr` | | `casella` | `td` | | `capocasella` | `th` | | `modulo` | `form` | | `miett` | `input` | | `citofono` | `button` | | `etichetta` | `label` | | `scegli` | `select` | | `opzione` | `option` | | `gruppo` | `optgroup` | | `ragionamento` | `textarea` | | `recinto` | `fieldset` | | `leggenda` | `legend` | | `suggerimenti` | `datalist` | | `risultato` | `output` | | `avanzamento` | `progress` | | `misura` | `meter` | | `spoiler` | `details` | | `anteprim` | `summary` | | `dialogo` | `dialog` | | `stampo` | `template` | | `fessura` | `slot` | - Attributes and events of HTML tags have Neapolitan names (the output always has the React name; React names, `class`, `for`, `data-*` and `aria-*` work as written). On a component (``) every prop is passed as written. - A quoted value from an attribute's list is translated too, as in htmlemmurt pages: `` is `type: "password"`, `` is `type: "submit"`. HTML values and other strings stay as written. | FratmScript | React | | --- | --- | | `addo` | `action` | | `tipo` | `type` | | `classe` | `className` | | `chi` | `id` | | `cess` | `style` | | `titolo` | `title` | | `lingua` | `lang` | | `annascuso` | `hidden` | | `turno` | `tabIndex` | | `ruolo` | `role` | | `scrivibile` | `contentEditable` | | `trascinabile` | `draggable` | | `correggi` | `spellCheck` | | `addo` | `href` | | `da` | `src` | | `spiegazione` | `alt` | | `larghezza` | `width` | | `altezza` | `height` | | `arape` | `target` | | `rapporto` | `rel` | | `scarica` | `download` | | `codifica` | `charSet` | | `nomme` | `name` | | `cuntenuto` | `content` | | `rrobba` | `property` | | `carica` | `loading` | | `aropp` | `defer` | | `ampressa` | `async` | | `furastiero` | `crossOrigin` | | `verace` | `integrity` | | `valore` | `value` | | `esempio` | `placeholder` | | `stutato` | `disabled` | | `spuntato` | `checked` | | `scigliuto` | `selected` | | `obbligatorio` | `required` | | `nunsetocca` | `readOnly` | | `assaje` | `multiple` | | `subbeto` | `autoFocus` | | `minimo` | `min` | | `massimo` | `max` | | `passo` | `step` | | `lunghezzaminima` | `minLength` | | `lunghezzamassima` | `maxLength` | | `modello` | `pattern` | | `pe` | `htmlFor` | | `suggerimenti` | `list` | | `accetta` | `accept` | | `etichetta` | `label` | | `righe` | `rows` | | `colonne` | `cols` | | `metodo` | `method` | | `ricorda` | `autoComplete` | | `allarga` | `colSpan` | | `allonga` | `rowSpan` | | `partesubbeto` | `autoPlay` | | `comandi` | `controls` | | `ripete` | `loop` | | `muto` | `muted` | | `copertina` | `poster` | | `quanno` | `dateTime` | | `araputo` | `open` | | `chiave` | `key` | | `valore-iniziale` | `defaultValue` | | `quanno-tuocche` | `onClick` | | `quanno-tuocche-doje-vote` | `onDoubleClick` | | `quanno-scrive` | `onInput` | | `quanno-cagne` | `onChange` | | `quanno-manne` | `onSubmit` | | `quanno-carica` | `onLoad` | | `quanno-premi` | `onKeyDown` | | `quanno-aize` | `onKeyUp` | | `quanno-trase` | `onMouseEnter` | | `quanno-esce` | `onMouseLeave` | | `quanno-piglie` | `onFocus` | | `quanno-perde` | `onBlur` | | `quanno-scorre` | `onScroll` | `addo` is `action` on `` and `href` everywhere else. `cess={{ … }}` takes a style object whose keys and string values can be Neapolitan CSS, as in `` stile`…` ``: `cess={{ "sfunno-culore": "russo", spazio: "8px" }}` is `style: { backgroundColor: "red", padding: "8px" }`. React keys (`backgroundColor`) and plain CSS values work too; selectors and `:passanno` rules go in `` stile`…` ``. - The output uses the automatic JSX runtime: `import { jsx as _jsx, ... } from "react/jsx-runtime"` is added at the top. Write `// @jsxImportSource preact` at the top of the file to use Preact; the npm package's `compile` takes `jsxImportSource` too. - Hooks and React APIs keep their names: `useState`, `useEffect`, `createRoot`. - Styles: import plain CSS (`chiamm "./stile.css"`) or CSS modules (`chiamm stile da "./App.module.css"`, then `classe={stile.titolo}`) through Vite; inline styles are objects: `cess={{ color: "red" }}`. - Tailwind CSS: `fratm nuovo --tailwind` sets it up (Vite plugin, `@import "tailwindcss"` in `src/stile.css`); it finds classes in `.fratm` files. `chiamm "tailwindcss/index.css"` in a `.fratm` file turns it on too, and is what switches Tailwind on in the playground preview. - A `<` starts JSX only where a value can start (after `=`, `(`, `,`, `piglie`, `=>`, ...) and when a tag name or `>` follows right away, so `a < b` and `i stamm a dì("giro", i)) aspett aspetta(10) ``` | Helper | Does | | --- | --- | | `a_caso(min, max)` | random integer, both ends included | | `uno_a_caso(lista)` | random item of a list | | `mischia(lista)` | shuffled copy | | `quanti(cosa)` | length of a list or string, size of a Set/Map, number of object keys | | `arrotonda(n, cifre = 0)` | round to `cifre` decimals | | `ripeti(volte, fa)` | call `fa(0)` ... `fa(volte - 1)` | | `aspetta(ms)` | Promise resolved after `ms` milliseconds | | ``gomorra`…` `` | SQL query in Neapolitan (next section) | | `apri_db(file = ":memory:")` | SQLite database on `node:sqlite` (Node 22.13+; next section) | | ``stile`…` `` | CSS in Neapolitan (section "CSS: `stile`") | ## CSS: `stile` `` stile`…` `` (imported from `"fratm"`) is CSS written in Neapolitan, translated at compile time. In a browser the rules are added to the page once (a `