diff --git a/.gitignore b/.gitignore index eb36288..802113f 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,14 @@ package-lock.json packages/playground/dist/ packages/vscode-extension/out/ packages/vscode-extension/*.vsix +examples/*.js +examples/*.js.map +Games/*/*.js +Games/*/*.js.map +PersonalScripts/**/*.js +PersonalScripts/**/*.js.map +.fratm_tmp_*.js +**/.fratm_tmp_*.js + +# Other Tools +AGENTS.md diff --git a/Games/Pomodor_e_Muzzarel/pomodor_e_muzzarel.fratm b/Games/Pomodor_e_Muzzarel/pomodor_e_muzzarel.fratm new file mode 100644 index 0000000..61a5d5e --- /dev/null +++ b/Games/Pomodor_e_Muzzarel/pomodor_e_muzzarel.fratm @@ -0,0 +1,131 @@ +// ============================================ +// Pomodor e Muzzarel +// Tic Tac Toe in versione napoletana +// Esecuzione: +// 1) fratm build Games/Pomodor_e_Muzzarel/pomodor_e_muzzarel.fratm +// 2) node Games/Pomodor_e_Muzzarel/pomodor_e_muzzarel.js +// ============================================ + +tien readline = require("readline") +tien rl = readline.createInterface({ + input: process.stdin, + output: process.stdout +}) + +tien tavola = [" ", " ", " ", " ", " ", " ", " ", " ", " "] +tien giocatore_attuale = "P" + +facc nome_giocatore(simbolo) { + si (simbolo == "P") { + piglie "Pomodor" + } + piglie "Muzzarel" +} + +facc simbolo_casella(indice) { + si (tavola[indice] == " ") { + piglie "" + (indice + 1) + } + piglie tavola[indice] +} + +facc stampa_tavola() { + stamm a dì("") + stamm a dì(" " + simbolo_casella(0) + " | " + simbolo_casella(1) + " | " + simbolo_casella(2)) + stamm a dì("---+---+---") + stamm a dì(" " + simbolo_casella(3) + " | " + simbolo_casella(4) + " | " + simbolo_casella(5)) + stamm a dì("---+---+---") + stamm a dì(" " + simbolo_casella(6) + " | " + simbolo_casella(7) + " | " + simbolo_casella(8)) + stamm a dì("") +} + +facc casella_libera(indice) { + piglie tavola[indice] == " " +} + +facc linea_vincente(a, b, c, simbolo) { + piglie tavola[a] == simbolo e tavola[b] == simbolo e tavola[c] == simbolo +} + +facc ha_vinto(simbolo) { + si (linea_vincente(0, 1, 2, simbolo) o linea_vincente(3, 4, 5, simbolo) o linea_vincente(6, 7, 8, simbolo) o linea_vincente(0, 3, 6, simbolo) o linea_vincente(1, 4, 7, simbolo) o linea_vincente(2, 5, 8, simbolo) o linea_vincente(0, 4, 8, simbolo) o linea_vincente(2, 4, 6, simbolo)) { + piglie overo + } + piglie sfòls +} + +facc e_pareggio() { + pe (tien i = 0; i < 9; i = i + 1) { + si (tavola[i] == " ") { + piglie sfòls + } + } + piglie overo +} + +facc cambia_giocatore() { + si (giocatore_attuale == "P") { + giocatore_attuale = "M" + piglie nisciun + } + giocatore_attuale = "P" +} + +facc mostra_titolo() { + stamm a dì("====================================") + stamm a dì(" POMODOR E MUZZAREL") + stamm a dì(" Tic Tac Toe napulitan edition") + stamm a dì("====================================") + stamm a dì("Pomodor = P | Muzzarel = M") +} + +facc turno() { + stampa_tavola() + stamm a dì("Turno 'e " + nome_giocatore(giocatore_attuale) + " (" + giocatore_attuale + ")") + + rl.question("Scegli na casella (1-9): ", (risposta) => { + tien mossa = parseInt(risposta, 10) + + si (isNaN(mossa)) { + avvis a dì("Mossa nun valida. Miette nu numero tra 1 e 9.") + turno() + piglie nisciun + } + + tien indice = mossa - 1 + + si (indice < 0 o indice > 8) { + avvis a dì("Casella fora campo. Scegli tra 1 e 9.") + turno() + piglie nisciun + } + + si (no casella_libera(indice)) { + avvis a dì("Chest casella è già presa.") + turno() + piglie nisciun + } + + tavola[indice] = giocatore_attuale + + si (ha_vinto(giocatore_attuale)) { + stampa_tavola() + stamm a dì("Ha vinto " + nome_giocatore(giocatore_attuale) + "! Brav fratm!") + rl.close() + piglie nisciun + } + + si (e_pareggio()) { + stampa_tavola() + stamm a dì("Pareggio! Pizza margherita pe tutte e dduje.") + rl.close() + piglie nisciun + } + + cambia_giocatore() + turno() + }) +} + +mostra_titolo() +turno() diff --git a/PersonalScripts/pdf_manager/package.json b/PersonalScripts/pdf_manager/package.json new file mode 100644 index 0000000..645ffdb --- /dev/null +++ b/PersonalScripts/pdf_manager/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "pdf-lib": "^1.17.1" + } +} diff --git a/PersonalScripts/pdf_manager/pdf_manager.fratm b/PersonalScripts/pdf_manager/pdf_manager.fratm new file mode 100644 index 0000000..f111403 --- /dev/null +++ b/PersonalScripts/pdf_manager/pdf_manager.fratm @@ -0,0 +1,353 @@ +chist è fs = require("node:fs") +chist è path = require("node:path") +chist è pdfLib = require("pdf-lib") +chist è PDFDocument = pdfLib.PDFDocument + +facc aiuto() { + stamm a dì("Gestore PDF in FratmScript") + stamm a dì("") + stamm a dì("Comandi disponibili:") + stamm a dì(" info ") + stamm a dì(" unisci [altri.pdf]") + stamm a dì(" estrai ") + stamm a dì(" taglia ") + stamm a dì(" riordina ") + stamm a dì(" dividi ") + stamm a dì(" rinomina ") + stamm a dì(" rinomina-massa ") + stamm a dì("") + stamm a dì("Formato pagine:") + stamm a dì(" 1,3,5") + stamm a dì(" 1-4") + stamm a dì(" 5,1,2,3") + stamm a dì(" 10-8") + stamm a dì("") + stamm a dì("Esempi:") + stamm a dì(" node pdf_manager.js unisci archivio.pdf a.pdf b.pdf c.pdf") + stamm a dì(" node pdf_manager.js estrai contratto.pdf pagine_pari.pdf 2,4,6,8") + stamm a dì(" node pdf_manager.js riordina scansione.pdf ordinato.pdf 3,1,2,4") + stamm a dì(" node pdf_manager.js dividi libro.pdf ./pagine") + stamm a dì(" node pdf_manager.js rinomina-massa ./documenti fattura") +} + +facc esciConErrore(messaggio) { + scrive a dì("Errore: " + messaggio) + process.exit(1) +} + +facc assicuraEsistenza(percorso) { + si (!fs.existsSync(percorso)) { + iett nu bell Error("File o cartella non trovato: " + percorso) + } +} + +facc assicuraCartellaPerFile(percorsoFile) { + chist è cartella = path.dirname(percorsoFile) + si (!fs.existsSync(cartella)) { + fs.mkdirSync(cartella, { recursive: overo }) + } +} + +facc assicuraCartella(percorsoCartella) { + si (!fs.existsSync(percorsoCartella)) { + fs.mkdirSync(percorsoCartella, { recursive: overo }) + } +} + +facc leggiBytes(percorsoFile) { + assicuraEsistenza(percorsoFile) + piglie fs.readFileSync(percorsoFile) +} + +facc salvaBytes(percorsoFile, bytes) { + assicuraCartellaPerFile(percorsoFile) + fs.writeFileSync(percorsoFile, bytes) +} + +facc zeroPad(numero, lunghezza) { + tien testo = "" + numero + mentre che (testo.length < lunghezza) { + testo = "0" + testo + } + piglie testo +} + +facc parsePagine(spec) { + si (spec == nisciun o spec.trim() == "") { + iett nu bell Error("Specifica pagine vuota") + } + + chist è pezzi = spec.split(",") + tien indici = [] + + pe (tien i = 0; i < pezzi.length; i = i + 1) { + chist è pezzoGrezz = pezzi[i] + chist è pezzo = pezzoGrezz.trim() + + si (pezzo == "") { + salta + } + + si (pezzo.includes("-")) { + chist è estremi = pezzo.split("-") + si (estremi.length != 2) { + iett nu bell Error("Intervallo non valido: " + pezzo) + } + + chist è inizio = parseInt(estremi[0].trim(), 10) + chist è fine = parseInt(estremi[1].trim(), 10) + + si (Number.isNaN(inizio) o Number.isNaN(fine)) { + iett nu bell Error("Intervallo non numerico: " + pezzo) + } + + si (inizio <= 0 o fine <= 0) { + iett nu bell Error("Le pagine partono da 1: " + pezzo) + } + + si (inizio <= fine) { + pe (tien pagina = inizio; pagina <= fine; pagina = pagina + 1) { + indici.push(pagina - 1) + } + } sinnò { + pe (tien pagina = inizio; pagina >= fine; pagina = pagina - 1) { + indici.push(pagina - 1) + } + } + } sinnò { + chist è numero = parseInt(pezzo, 10) + + si (Number.isNaN(numero)) { + iett nu bell Error("Numero pagina non valido: " + pezzo) + } + + si (numero <= 0) { + iett nu bell Error("Le pagine partono da 1: " + pezzo) + } + + indici.push(numero - 1) + } + } + + si (indici.length == 0) { + iett nu bell Error("Nessuna pagina valida trovata in: " + spec) + } + + piglie indici +} + +facc validaIndici(indici, totalePagine) { + pe (tien i = 0; i < indici.length; i = i + 1) { + chist è indice = indici[i] + si (indice < 0 o indice >= totalePagine) { + iett nu bell Error("Pagina fuori range: " + (indice + 1) + " su " + totalePagine) + } + } +} + +facc listaPdf(cartella) { + assicuraEsistenza(cartella) + chist è elementi = fs.readdirSync(cartella) + tien pdf = [] + + pe (tien i = 0; i < elementi.length; i = i + 1) { + chist è nome = elementi[i] + si (nome.toLowerCase().endsWith(".pdf")) { + pdf.push(nome) + } + } + + pdf.sort() + piglie pdf +} + +mo vir facc apriPdf(percorsoFile) { + chist è bytes = leggiBytes(percorsoFile) + piglie aspett PDFDocument.load(bytes) +} + +mo vir facc comandoInfo(percorsoFile) { + chist è pdf = aspett apriPdf(percorsoFile) + chist è totalePagine = pdf.getPageCount() + + stamm a dì("File: " + percorsoFile) + stamm a dì("Pagine: " + totalePagine) +} + +mo vir facc comandoUnisci(outputFile, inputFiles) { + si (inputFiles.length < 2) { + iett nu bell Error("Per unire servono almeno 2 PDF di input") + } + + chist è pdfFinale = aspett PDFDocument.create() + + pe (tien i = 0; i < inputFiles.length; i = i + 1) { + chist è percorso = inputFiles[i] + chist è sorgente = aspett apriPdf(percorso) + chist è totalePagine = sorgente.getPageCount() + tien indici = [] + + pe (tien p = 0; p < totalePagine; p = p + 1) { + indici.push(p) + } + + chist è pagineCopiate = aspett pdfFinale.copyPages(sorgente, indici) + + pe (tien j = 0; j < pagineCopiate.length; j = j + 1) { + pdfFinale.addPage(pagineCopiate[j]) + } + + stamm a dì("Aggiunto: " + percorso + " (" + totalePagine + " pagine)") + } + + chist è risultato = aspett pdfFinale.save() + salvaBytes(outputFile, risultato) + stamm a dì("Creato: " + outputFile) +} + +mo vir facc comandoEstrai(percorsoInput, percorsoOutput, specPagine) { + chist è sorgente = aspett apriPdf(percorsoInput) + chist è totalePagine = sorgente.getPageCount() + chist è indici = parsePagine(specPagine) + + validaIndici(indici, totalePagine) + + chist è pdfFinale = aspett PDFDocument.create() + chist è pagineCopiate = aspett pdfFinale.copyPages(sorgente, indici) + + pe (tien i = 0; i < pagineCopiate.length; i = i + 1) { + pdfFinale.addPage(pagineCopiate[i]) + } + + chist è risultato = aspett pdfFinale.save() + salvaBytes(percorsoOutput, risultato) + + stamm a dì("Creato: " + percorsoOutput) + stamm a dì("Pagine esportate: " + specPagine) +} + +mo vir facc comandoDividi(percorsoInput, cartellaOutput) { + chist è sorgente = aspett apriPdf(percorsoInput) + chist è totalePagine = sorgente.getPageCount() + chist è base = path.basename(percorsoInput, path.extname(percorsoInput)) + + assicuraCartella(cartellaOutput) + + pe (tien i = 0; i < totalePagine; i = i + 1) { + chist è pdfPagina = aspett PDFDocument.create() + chist è pagineCopiate = aspett pdfPagina.copyPages(sorgente, [i]) + pdfPagina.addPage(pagineCopiate[0]) + + chist è nomeOutput = path.join(cartellaOutput, base + "_pag_" + zeroPad(i + 1, 3) + ".pdf") + chist è risultato = aspett pdfPagina.save() + salvaBytes(nomeOutput, risultato) + + stamm a dì("Creato: " + nomeOutput) + } +} + +facc comandoRinomina(vecchioNome, nuovoNome) { + assicuraEsistenza(vecchioNome) + assicuraCartellaPerFile(nuovoNome) + fs.renameSync(vecchioNome, nuovoNome) + stamm a dì("Rinominato: " + vecchioNome + " -> " + nuovoNome) +} + +facc comandoRinominaMassa(cartella, prefisso) { + chist è files = listaPdf(cartella) + + si (files.length == 0) { + iett nu bell Error("Nessun PDF trovato in: " + cartella) + } + + chist è marcatore = "__fratm_tmp__" + Date.now() + tien tempFiles = [] + + pe (tien i = 0; i < files.length; i = i + 1) { + chist è vecchioPath = path.join(cartella, files[i]) + chist è tempPath = path.join(cartella, marcatore + "_" + zeroPad(i + 1, 3) + ".pdf") + fs.renameSync(vecchioPath, tempPath) + tempFiles.push(tempPath) + } + + pe (tien i = 0; i < tempFiles.length; i = i + 1) { + chist è nuovoPath = path.join(cartella, prefisso + "_" + zeroPad(i + 1, 3) + ".pdf") + fs.renameSync(tempFiles[i], nuovoPath) + stamm a dì("Rinominato: " + nuovoPath) + } +} + +mo vir facc main() { + pruvamm { + chist è args = process.argv.slice(2) + + si (args.length == 0) { + aiuto() + piglie + } + + chist è comando = args[0] + + si (comando == "aiuto" o comando == "help" o comando == "--help" o comando == "-h") { + aiuto() + piglie + } + + si (comando == "info") { + si (args.length != 2) { + iett nu bell Error("Uso: info ") + } + aspett comandoInfo(args[1]) + piglie + } + + si (comando == "unisci") { + si (args.length < 4) { + iett nu bell Error("Uso: unisci [altri.pdf]") + } + aspett comandoUnisci(args[1], args.slice(2)) + piglie + } + + si (comando == "estrai" o comando == "taglia" o comando == "riordina") { + si (args.length != 4) { + iett nu bell Error("Uso: " + comando + " ") + } + aspett comandoEstrai(args[1], args[2], args[3]) + piglie + } + + si (comando == "dividi") { + si (args.length != 3) { + iett nu bell Error("Uso: dividi ") + } + aspett comandoDividi(args[1], args[2]) + piglie + } + + si (comando == "rinomina") { + si (args.length != 3) { + iett nu bell Error("Uso: rinomina ") + } + comandoRinomina(args[1], args[2]) + piglie + } + + si (comando == "rinomina-massa") { + si (args.length != 3) { + iett nu bell Error("Uso: rinomina-massa ") + } + comandoRinominaMassa(args[1], args[2]) + piglie + } + + iett nu bell Error("Comando sconosciuto: " + comando) + } e si schiatta (errore) { + scrive a dì("Errore: " + errore.message) + scrive a dì("") + aiuto() + process.exit(1) + } +} + +main() diff --git a/PersonalScripts/run_personal_scripts.fratm b/PersonalScripts/run_personal_scripts.fratm new file mode 100644 index 0000000..804dcbc --- /dev/null +++ b/PersonalScripts/run_personal_scripts.fratm @@ -0,0 +1,126 @@ +// ============================================ +// Launcher script per script aggiuntivi +// Uso: +// fratm lancia PersonalScripts/run_personal_scripts.fratm +// ============================================ + +chist è fs = require("node:fs") +chist è path = require("node:path") +chist è cp = require("node:child_process") +chist è readline = require("node:readline") + +facc aiuto() { + stamm a dì("Launcher script aggiuntivi") + stamm a dì("") + stamm a dì("Scegli dal menu:") + stamm a dì(" 1 -> PDF Manager") + stamm a dì(" 2 -> Pomodor e Muzzarel") + stamm a dì(" q -> Esci") + stamm a dì("") + stamm a dì("Esempi:") + stamm a dì(" fratm lancia PersonalScripts/run_personal_scripts.fratm") +} + +facc binarioFratm() { + chist è cwd = process.cwd() + chist è releaseBin = path.join(cwd, "target", "release", "fratm") + si (fs.existsSync(releaseBin)) { + piglie releaseBin + } + + chist è debugBin = path.join(cwd, "target", "debug", "fratm") + si (fs.existsSync(debugBin)) { + piglie debugBin + } + + piglie "fratm" +} + +facc eseguiScriptFratm(scriptRelativo, argsExtra) { + chist è fratmBin = binarioFratm() + chist è scriptPath = path.join(process.cwd(), scriptRelativo) + chist è scriptDir = path.dirname(scriptPath) + chist è outputTmp = path.join(scriptDir, ".fratm_tmp_" + path.basename(scriptRelativo, ".fratm") + "_" + Date.now() + ".js") + + // 1) Compila il file Fratm in JS temporaneo + chist è buildArgs = ["build", scriptPath, "-o", outputTmp] + chist è buildRes = cp.spawnSync(fratmBin, buildArgs, { stdio: "inherit" }) + + si (buildRes.error != nisciun) { + scrive a dì("Errore build script: " + buildRes.error.message) + process.exit(1) + } + + si (buildRes.status != 0) { + process.exit(buildRes.status) + } + + // 2) Esegue con Node passando eventuali argomenti extra + chist è runArgs = [outputTmp] + pe (tien i = 0; i < argsExtra.length; i = i + 1) { + runArgs.push(argsExtra[i]) + } + + chist è risultato = cp.spawnSync("node", runArgs, { stdio: "inherit" }) + + si (risultato.error != nisciun) { + scrive a dì("Errore avvio script: " + risultato.error.message) + si (fs.existsSync(outputTmp)) { + fs.unlinkSync(outputTmp) + } + process.exit(1) + } + + si (risultato.status != 0) { + si (fs.existsSync(outputTmp)) { + fs.unlinkSync(outputTmp) + } + process.exit(risultato.status) + } + + si (fs.existsSync(outputTmp)) { + fs.unlinkSync(outputTmp) + } +} + +facc apriMenu() { + chist è rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }) + + stamm a dì("====================================") + stamm a dì(" Launcher Script Aggiuntivi") + stamm a dì("====================================") + stamm a dì("1) PDF Manager") + stamm a dì("2) Pomodor e Muzzarel") + stamm a dì("q) Esci") + stamm a dì("") + + rl.question("Scelta: ", (scelta) => { + si (scelta == "1") { + rl.question("Argomenti PDF manager (vuoto = help): ", (linea) => { + chist è pulita = linea.trim() + tien args = [] + si (pulita != "") { + args = pulita.split(" ") + } + rl.close() + eseguiScriptFratm("PersonalScripts/pdf_manager/pdf_manager.fratm", args) + }) + piglie nisciun + } + + si (scelta == "2") { + rl.close() + eseguiScriptFratm("Games/Pomodor_e_Muzzarel/pomodor_e_muzzarel.fratm", []) + piglie nisciun + } + + rl.close() + stamm a dì("Ciao fratm!") + }) +} + +aiuto() +apriMenu() diff --git a/README.md b/README.md index cfd7ff0..4a1487e 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,8 @@ fratmscript/ │ ├── vscode-extension/ # Syntax highlighting │ └── playground/ # Online editor ├── examples/ # Example programs -└── docs/ # Documentation +├── Games/ # Games written in FratmScript +└── PersonalScripts/ # Utility scripts + launcher ``` --- @@ -50,19 +51,21 @@ cd fratmscript # Build cargo build --release -# Run -./target/release/fratm run examples/01_salutatore.fratm +# Launch +./target/release/fratm lancia examples/01_salutatore.fratm # REPL ./target/release/fratm repl ``` +Per una guida completa a esempi, giochi e script: **[Run Guide](RUN_EXAMPLES.md)**. + --- ## CLI Commands ```bash -fratm run # Compile and run +fratm lancia # Compile and launch fratm build # Compile to JavaScript fratm build --sourcemap # With source map fratm repl # Interactive REPL @@ -120,11 +123,20 @@ manco // ! (alias for "no") ### Classes ```fratm na famiglie Persona { - facc costruttore(nome) { + costruttore(nome) { stu cos.nome = nome } } -chist è p = nu bell Persona("Gennaro") + +na famiglie Pizzaiolo figlio 'e Persona { + costruttore(nome) { + 'o pate(nome) + } + + fisso specie() { + piglie "Essere umano" + } +} ``` ### Try/Catch @@ -185,6 +197,9 @@ fermete // debugger | `iett` | `throw` | "throw" | | `nu bell` | `new` | "a nice" | | `na famiglie` | `class` | "a family" | +| `figlio 'e` | `extends` | "child of" | +| `'o pate` | `super` | "the father" | +| `fisso` | `static` | "fixed/static" | | `stu cos` | `this` | "this thing" | | `chiamm` | `import` | "call" | | `da` | `from` | "from" | @@ -199,20 +214,27 @@ fermete // debugger ## Development +Install `just` (Linux/macOS/Windows) and official guide: +- [RUN_EXAMPLES.md](RUN_EXAMPLES.md#install-just-optional-but-recommended) +- https://just.systems/man/en/ + ```bash -# Build all crates -cargo build +# Recommended task runner +just --list -# Run tests -cargo test +# Build and test +just build +just test -# Build WASM -cd crates/fratm-wasm -wasm-pack build --target web +# Build/launch all examples +just examples-build +just examples-lancia -# Package VSCode extension -cd packages/vscode-extension -vsce package +# Build release CLI +just release + +# Build WASM for playground +just wasm ``` --- @@ -224,7 +246,7 @@ The playground is a web-based editor to try FratmScript in your browser. ```bash # Build WASM module cd crates/fratm-wasm -wasm-pack build --target web --out-dir ../../packages/playground/pkg +wasm-pack build --target web --out-dir ../../packages/playground/public/pkg # Serve the playground cd ../../packages/playground @@ -249,8 +271,20 @@ Check the `examples/` folder for complete programs: 6. `06_funzioni.fratm` - Advanced functions 7. `07_async.fratm` - Async/await 8. `08_classi.fratm` - OOP -9. `09_moduli.fratm` - Import/export +9. `09_moduli.fratm` - Import/export without external deps 10. `10_nuove_feature.fratm` - New features +11. `11_oop_avanzato.fratm` - Inheritance, super and static methods +12. `12_trycatch_validazione.fratm` - Try/catch and validation + +--- + +## Script Launcher + +You can launch extra scripts from one entrypoint: + +```bash +fratm lancia PersonalScripts/run_personal_scripts.fratm +``` --- diff --git a/RUN_EXAMPLES.md b/RUN_EXAMPLES.md new file mode 100644 index 0000000..1ace524 --- /dev/null +++ b/RUN_EXAMPLES.md @@ -0,0 +1,108 @@ +# Run Guide: Examples, Games, Scripts + +This guide shows how to quickly run the FratmScript content in this repository. + +## Install `just` (optional but recommended) + +`just` is the task runner used in this project. Install it based on your OS: + +### Linux + +```bash +# Ubuntu/Debian (if available in your repo) +sudo apt install just + +# Fedora +sudo dnf install just + +# Arch +sudo pacman -S just +``` + +### macOS + +```bash +brew install just +``` + +### Windows + +```powershell +winget install Casey.Just +# or +scoop install just +# or +choco install just +``` + +### Universal alternative (if Rust is already installed) + +```bash +cargo install just +``` + +Official `just` documentation: +- https://just.systems/man/en/ +- https://github.com/casey/just#installation + +## Prerequisites + +```bash +# From the project root +cargo build --release -p fratm-cli +``` + +Commands below use `./target/release/fratm`. + +## Run a single example + +```bash +./target/release/fratm lancia examples/01_salutatore.fratm +./target/release/fratm lancia examples/11_oop_avanzato.fratm +``` + +## Run all examples + +```bash +for f in examples/*.fratm; do + ./target/release/fratm lancia "$f" +done +``` + +## Run games + +```bash +./target/release/fratm lancia Games/Pomodor_e_Muzzarel/pomodor_e_muzzarel.fratm +``` + +## Run extra scripts (launcher menu) + +```bash +./target/release/fratm lancia PersonalScripts/run_personal_scripts.fratm +``` + +In the menu: +- `1` opens PDF Manager (you can pass arguments, for example `help`). +- `2` starts the Pomodor e Muzzarel game. +- `q` exits. + +## Alternative with `just` + +This project’s `justfile` is OS-aware: +- it uses `bash` on Linux/macOS +- it uses `PowerShell` on Windows +- it automatically resolves the release binary (`fratm` vs `fratm.exe`) +- running `just` without arguments shows the full menu with recipe descriptions + +```bash +just +just release +just examples-lancia +just personal-lancia +just lancia-release examples/11_oop_avanzato.fratm +``` + +## Quick troubleshooting + +- If `fratm` does not start: rebuild with `cargo build --release -p fratm-cli`. +- If PDF Manager cannot find dependencies: check `PersonalScripts/pdf_manager/node_modules`. diff --git a/crates/fratm-cli/src/main.rs b/crates/fratm-cli/src/main.rs index d64612b..253eaf9 100644 --- a/crates/fratm-cli/src/main.rs +++ b/crates/fratm-cli/src/main.rs @@ -20,8 +20,9 @@ struct Cli { #[derive(Subcommand)] enum Commands { - /// Compile and run a .fratm file - Run { + /// Compile and launch a .fratm file + #[command(name = "lancia", alias = "run")] + Lancia { file: PathBuf, #[arg(long)] sourcemap: bool, @@ -45,7 +46,7 @@ enum Commands { fn main() { let cli = Cli::parse(); match cli.command { - Commands::Run { file, sourcemap } => run_file(&file, sourcemap), + Commands::Lancia { file, sourcemap } => run_file(&file, sourcemap), Commands::Build { file, output, sourcemap } => build_file(&file, output, sourcemap), Commands::Repl => run_repl(), Commands::Tokens { file } => show_tokens(&file), @@ -63,16 +64,16 @@ fn run_file(path: &PathBuf, sourcemap: bool) { match compile(&source, options) { Ok(result) => { - let temp_path = std::env::temp_dir().join("fratm_temp.js"); + let script_dir = path.parent().unwrap_or_else(|| std::path::Path::new(".")); + let temp_path = script_dir.join(format!(".fratm_temp_{}.js", std::process::id())); let mut output = result.code; if sourcemap { if let Some(sm) = &result.source_map { output.push_str("\n"); output.push_str(&sm.to_data_url()); } } if let Err(e) = fs::write(&temp_path, &output) { eprintln!("{} {}", "Error: cannot write file:".red().bold(), e); std::process::exit(1); } - let cmd_output = Command::new("node").arg(&temp_path).output(); - match cmd_output { - Ok(out) => { - io::stdout().write_all(&out.stdout).unwrap(); - io::stderr().write_all(&out.stderr).unwrap(); - if !out.status.success() { std::process::exit(out.status.code().unwrap_or(1)); } + let cmd_status = Command::new("node").arg(&temp_path).status(); + let _ = fs::remove_file(&temp_path); + match cmd_status { + Ok(status) => { + if !status.success() { std::process::exit(status.code().unwrap_or(1)); } } Err(e) => { eprintln!("{} {}", "Error: Node.js failed:".red().bold(), e); std::process::exit(1); } } diff --git a/crates/fratm-core/src/codegen/mod.rs b/crates/fratm-core/src/codegen/mod.rs index 4b86519..aa9ddac 100644 --- a/crates/fratm-core/src/codegen/mod.rs +++ b/crates/fratm-core/src/codegen/mod.rs @@ -197,29 +197,32 @@ impl CodeGen { self.emit(";"); } - Statement::ClassDecl { name, methods, span, .. } => { + Statement::ClassDecl { name, super_class, methods, span, .. } => { self.write_indent(); self.add_mapping(span.line, span.column); self.emit("class "); self.emit(name); + if let Some(parent) = super_class { + self.emit(" extends "); + self.emit(parent); + } self.emit(" {\n"); self.indent += 1; for method in methods { - if let Statement::FunctionDecl { name: method_name, params, body, is_async, .. } = method { - self.write_indent(); - if *is_async { self.emit("async "); } - // Translate "costruttore" to JavaScript "constructor" - let js_method_name = if method_name == "costruttore" { "constructor" } else { method_name }; - self.emit(js_method_name); - self.emit("("); - self.emit(¶ms.join(", ")); - self.emit(") {\n"); - self.indent += 1; - for s in body { self.gen_statement(s); self.emit("\n"); } - self.indent -= 1; - self.write_indent(); - self.emit("}\n"); - } + self.write_indent(); + if method.is_static { self.emit("static "); } + if method.is_async { self.emit("async "); } + // Translate "costruttore" to JavaScript "constructor" + let js_method_name = if method.name == "costruttore" { "constructor" } else { method.name.as_str() }; + self.emit(js_method_name); + self.emit("("); + self.emit(&method.params.join(", ")); + self.emit(") {\n"); + self.indent += 1; + for s in &method.body { self.gen_statement(s); self.emit("\n"); } + self.indent -= 1; + self.write_indent(); + self.emit("}\n"); } self.indent -= 1; self.write_indent(); @@ -300,6 +303,7 @@ impl CodeGen { Expression::Null { .. } => self.emit("null"), Expression::Undefined { .. } => self.emit("undefined"), Expression::This { .. } => self.emit("this"), + Expression::Super { .. } => self.emit("super"), Expression::Array { elements, .. } => { self.emit("["); for (i, elem) in elements.iter().enumerate() { diff --git a/crates/fratm-core/src/errors.rs b/crates/fratm-core/src/errors.rs index 36b5361..fe59fdf 100644 --- a/crates/fratm-core/src/errors.rs +++ b/crates/fratm-core/src/errors.rs @@ -8,14 +8,14 @@ use serde::{Serialize, Deserialize}; /// Main compilation error type #[derive(Debug, Error, Clone, Serialize, Deserialize)] pub enum CompileError { - #[error("Riga {line}, colonna {column}: {message}")] + #[error("Riga {line}, colonna {column}: {msg}", msg = napoletanize_error(message))] LexerError { message: String, line: usize, column: usize, }, - #[error("Riga {line}, colonna {column}: {message}")] + #[error("Riga {line}, colonna {column}: {msg}", msg = napoletanize_error(message))] ParseError { message: String, line: usize, diff --git a/crates/fratm-core/src/lexer/mod.rs b/crates/fratm-core/src/lexer/mod.rs index 07425e8..3407a7c 100644 --- a/crates/fratm-core/src/lexer/mod.rs +++ b/crates/fratm-core/src/lexer/mod.rs @@ -245,7 +245,8 @@ impl<'a> Lexer<'a> { } } - '"' | '\'' => self.scan_string(c), + '"' => self.scan_string(c), + '\'' => self.scan_apostrophe_or_string(), c if c.is_ascii_digit() => self.scan_number(), c if is_ident_start(c) => self.scan_identifier(), @@ -339,6 +340,43 @@ impl<'a> Lexer<'a> { ) } + /// Handles apostrophe-based OOP aliases before falling back to a normal string. + /// + /// Supported forms: + /// - `'e` -> extends connector (`TokenKind::De`) + /// - `'o pate` -> super (`TokenKind::OPate`) + fn scan_apostrophe_or_string(&mut self) -> Token { + let rest = &self.source[self.position..]; + + // `'e` (used in "figlio 'e Padre") + if let Some(after_e) = rest.strip_prefix('e') { + if after_e.chars().next().map(|c| c.is_whitespace()).unwrap_or(false) { + self.advance(); // consume 'e' + return self.make_token(TokenKind::De); + } + } + + // `'o pate` used for super calls + if let Some(after_o) = rest.strip_prefix('o') { + let spaces_len = after_o.len() - after_o.trim_start_matches(|c| c == ' ' || c == '\t').len(); + let trimmed = &after_o[spaces_len..]; + if spaces_len > 0 { + if let Some(after_pate) = trimmed.strip_prefix("pate") { + if after_pate.chars().next().map(|c| !is_ident_continue(c)).unwrap_or(true) { + let consumed = 1 + spaces_len + "pate".len(); + for _ in 0..consumed { + self.advance(); + } + return self.make_token(TokenKind::OPate); + } + } + } + } + + // Not an apostrophe keyword: parse as a single-quoted string. + self.scan_string('\'') + } + fn scan_string(&mut self, quote: char) -> Token { let mut value = String::new(); @@ -448,4 +486,15 @@ mod tests { assert!(matches!(tokens[0].kind, TokenKind::Number(n) if n == 42.0)); assert!(matches!(tokens[1].kind, TokenKind::Number(n) if (n - 3.14).abs() < 0.001)); } + + #[test] + fn test_apostrophe_oop_keywords() { + let mut lexer = Lexer::new("figlio 'e Padre { 'o pate(nome) }"); + let tokens = lexer.tokenize(); + + assert!(matches!(tokens[0].kind, TokenKind::Figlio)); + assert!(matches!(tokens[1].kind, TokenKind::De)); + assert!(matches!(tokens[2].kind, TokenKind::Identifier(_))); + assert!(matches!(tokens[4].kind, TokenKind::OPate)); + } } diff --git a/crates/fratm-core/src/lexer/token.rs b/crates/fratm-core/src/lexer/token.rs index c51cb8f..c1c6b23 100644 --- a/crates/fratm-core/src/lexer/token.rs +++ b/crates/fratm-core/src/lexer/token.rs @@ -419,6 +419,8 @@ pub fn lookup_keyword(ident: &str) -> Option { "caso" => Some(TokenKind::Caso), "fisso" => Some(TokenKind::Fisso), "figlio" => Some(TokenKind::Figlio), + "de" => Some(TokenKind::De), + "opate" => Some(TokenKind::OPate), "leva" => Some(TokenKind::Leva), "caccia" => Some(TokenKind::Caccia), "fermete" => Some(TokenKind::Fermete), diff --git a/crates/fratm-core/src/lib.rs b/crates/fratm-core/src/lib.rs index 7ef4362..cbeab2d 100644 --- a/crates/fratm-core/src/lib.rs +++ b/crates/fratm-core/src/lib.rs @@ -275,4 +275,42 @@ mod tests { assert!(result.code.contains("function test()")); assert!(result.code.contains("return 1")); } + + #[test] + fn test_oop_extends_super_and_static_compile() { + let source = r#" +na famiglie Persona { + costruttore(nome) { + stu cos.nome = nome + } + + saluta() { + piglie "Ue " + stu cos.nome + } + + fisso specie() { + piglie "Essere umano" + } +} + +na famiglie Pizzaiolo figlio 'e Persona { + costruttore(nome, specialita) { + 'o pate(nome) + stu cos.specialita = specialita + } +} +"#; + + let result = compile(source, Default::default()).unwrap(); + assert!(result.code.contains("class Persona")); + assert!(result.code.contains("static specie()")); + assert!(result.code.contains("class Pizzaiolo extends Persona")); + assert!(result.code.contains("super(nome)")); + } + + #[test] + fn test_single_quote_string_still_works() { + let result = compile("chist è a = 'e'\nstamm a dì(a)", Default::default()).unwrap(); + assert!(result.code.contains("const a = \"e\";")); + } } diff --git a/crates/fratm-core/src/parser/ast.rs b/crates/fratm-core/src/parser/ast.rs index adadc3b..8070d0a 100644 --- a/crates/fratm-core/src/parser/ast.rs +++ b/crates/fratm-core/src/parser/ast.rs @@ -61,7 +61,8 @@ pub enum Statement { }, ClassDecl { name: String, - methods: Vec, + super_class: Option, + methods: Vec, span: Span, }, Import { @@ -90,6 +91,16 @@ pub struct ImportSpecifier { pub local: String, } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ClassMethod { + pub name: String, + pub params: Vec, + pub body: Vec, + pub is_async: bool, + pub is_static: bool, + pub span: Span, +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub enum Expression { Identifier { name: String, span: Span }, @@ -99,6 +110,7 @@ pub enum Expression { Null { span: Span }, Undefined { span: Span }, This { span: Span }, + Super { span: Span }, Array { elements: Vec, span: Span }, Object { properties: Vec<(String, Expression)>, span: Span }, Binary { @@ -184,6 +196,7 @@ impl Expression { Expression::Null { span } => *span, Expression::Undefined { span } => *span, Expression::This { span } => *span, + Expression::Super { span } => *span, Expression::Array { span, .. } => *span, Expression::Object { span, .. } => *span, Expression::Binary { span, .. } => *span, diff --git a/crates/fratm-core/src/parser/mod.rs b/crates/fratm-core/src/parser/mod.rs index 2d515f1..67dc0d1 100644 --- a/crates/fratm-core/src/parser/mod.rs +++ b/crates/fratm-core/src/parser/mod.rs @@ -228,15 +228,61 @@ impl Parser { self.expect(&TokenKind::Na)?; self.expect(&TokenKind::Famiglie)?; let name = self.expect_identifier()?; + + // Optional inheritance: + // - "figlio 'e Base" + // - "figlio de Base" + // - "figlio Base" (shortcut) + let super_class = if self.match_token(&TokenKind::Figlio) { + // Connector is optional to preserve backwards compatibility. + self.match_token(&TokenKind::De); + Some(self.expect_identifier()?) + } else { + None + }; + self.expect(&TokenKind::LeftBrace)?; let mut methods = Vec::new(); while !self.check(&TokenKind::RightBrace) && !self.is_at_end() { while self.check(&TokenKind::Newline) { self.advance(); } if self.check(&TokenKind::RightBrace) { break; } - methods.push(self.parse_function()?); + methods.push(self.parse_class_method()?); } self.expect(&TokenKind::RightBrace)?; - Ok(Statement::ClassDecl { name, methods, span: self.span_from(start.start) }) + Ok(Statement::ClassDecl { + name, + super_class, + methods, + span: self.span_from(start.start), + }) + } + + fn parse_class_method(&mut self) -> Result { + let start = self.current_span(); + let is_static = self.match_token(&TokenKind::Fisso); + + let is_async = if self.match_token(&TokenKind::Mo) { + self.expect(&TokenKind::Vir)?; + true + } else { + false + }; + + // "facc" inside classes is optional. + self.match_token(&TokenKind::Facc); + + let name = self.expect_identifier()?; + let params = self.parse_parameters()?; + let body = self.parse_block_body()?; + + Ok(ClassMethod { + name, + params, + body, + is_async, + is_static, + span: self.span_from(start.start), + }) } fn parse_import(&mut self) -> Result { @@ -484,6 +530,7 @@ impl Parser { self.expect(&TokenKind::Cos)?; Ok(Expression::This { span: self.span_from(span.start) }) } + TokenKind::OPate => Ok(Expression::Super { span: self.span_from(span.start) }), TokenKind::Nu => { self.expect(&TokenKind::Bell)?; let callee = self.parse_call()?; @@ -534,13 +581,16 @@ impl Parser { } TokenKind::LeftBracket => { let mut elements = Vec::new(); + while self.check(&TokenKind::Newline) { self.advance(); } if !self.check(&TokenKind::RightBracket) { elements.push(self.parse_expression()?); while self.match_token(&TokenKind::Comma) { + while self.check(&TokenKind::Newline) { self.advance(); } if self.check(&TokenKind::RightBracket) { break; } elements.push(self.parse_expression()?); } } + while self.check(&TokenKind::Newline) { self.advance(); } self.expect(&TokenKind::RightBracket)?; Ok(Expression::Array { elements, span: self.span_from(span.start) }) } diff --git a/examples/09_moduli.fratm b/examples/09_moduli.fratm index b83d3e4..dbc2ed5 100644 --- a/examples/09_moduli.fratm +++ b/examples/09_moduli.fratm @@ -3,14 +3,21 @@ // chiamm (import), mann for (export) // ============================================ -// How to import -chiamm { useState, useEffect } da "react" -chiamm { Router, Route } da "react-router" +// Import from Node built-in modules (no external dependencies) +chiamm { basename, dirname } da "node:path" +chiamm { platform } da "node:os" + +facc descriviPercorso(percorso) { + piglie "file=" + basename(percorso) + ", cartella=" + dirname(percorso) +} // How to export a function mann for facc calcolaIVA(prezzo) { piglie prezzo * 1.22 } -// Export default -mann for predefinit { nome: "App", versione: "1.0" } +// Export default object +mann for predefinit { nome: "ModuloDemo", versione: "1.1", ambiente: platform() } + +stamm a dì(descriviPercorso("/tmp/fratm/esempio.js")) +stamm a dì("IVA 100 => " + calcolaIVA(100)) diff --git a/examples/11_oop_avanzato.fratm b/examples/11_oop_avanzato.fratm new file mode 100644 index 0000000..e2fa1b9 --- /dev/null +++ b/examples/11_oop_avanzato.fratm @@ -0,0 +1,48 @@ +// ============================================ +// Example 11: OOP Avanzato +// Eredità (figlio 'e), super ('o pate), metodi statici (fisso) +// ============================================ + +na famiglie Persona { + costruttore(nome) { + stu cos.nome = nome + } + + saluta() { + piglie "Ue, so' " + stu cos.nome + } + + fisso categoria() { + piglie "Essere umano" + } +} + +na famiglie Pizzaiolo figlio 'e Persona { + costruttore(nome, specialita) { + 'o pate(nome) + stu cos.specialita = specialita + stu cos.pizzeFatte = 0 + } + + faiPizza(tipo) { + stu cos.pizzeFatte = stu cos.pizzeFatte + 1 + piglie stu cos.nome + " ha sfornato na " + tipo + "!" + } + + saluta() { + piglie 'o pate.saluta() + " e faccio " + stu cos.specialita + } + + fisso salutoDiClasse() { + piglie "Nuje simm' pizzaiuoli" + } +} + +chist è gennaro = nu bell Pizzaiolo("Gennaro", "Margherita") + +stamm a dì(gennaro.saluta()) +stamm a dì(gennaro.faiPizza("Marinara")) +stamm a dì(gennaro.faiPizza("Diavola")) +stamm a dì("Pizze fatte: " + gennaro.pizzeFatte) +stamm a dì("Categoria: " + Persona.categoria()) +stamm a dì("Static subclass: " + Pizzaiolo.salutoDiClasse()) diff --git a/examples/12_trycatch_validazione.fratm b/examples/12_trycatch_validazione.fratm new file mode 100644 index 0000000..6f4f6a6 --- /dev/null +++ b/examples/12_trycatch_validazione.fratm @@ -0,0 +1,28 @@ +// ============================================ +// Example 12: Try/Catch e Validazione +// pruvamm, e si schiatta, iett +// ============================================ + +facc validaEta(eta) { + si (eta < 0) { + iett nu bell Error("L'eta nun po' essere negativa") + } + si (eta > 130) { + iett nu bell Error("L'eta è troppo alta pe' essere vera") + } + piglie "Eta valida: " + eta +} + +facc provaValore(eta) { + pruvamm { + stamm a dì(validaEta(eta)) + } e si schiatta (err) { + avvis a dì("Valore " + eta + " scartato: " + err.message) + } +} + +stamm a dì("=== Validazione eta ===") +provaValore(35) +provaValore(-2) +provaValore(200) +provaValore(90) diff --git a/justfile b/justfile new file mode 100644 index 0000000..4584746 --- /dev/null +++ b/justfile @@ -0,0 +1,80 @@ +set shell := ["bash", "-euo", "pipefail", "-c"] +set windows-shell := ["powershell.exe", "-NoLogo", "-Command"] + +fratm_release_bin := if os_family() == "windows" { ".\\target\\release\\fratm.exe" } else { "./target/release/fratm" } + +# Show the command menu (default behavior when running `just`). +[default] +[private] +menu: + @just --justfile {{justfile()}} --list --unsorted + +# Build Rust workspace (debug profile). +build: + cargo build + +# Build release CLI binary. +release: + cargo build --release -p fratm-cli + +# Run Rust tests. +test: + cargo test + +# Launch a FratmScript file with debug CLI (`cargo run`). +lancia file: + cargo run -p fratm-cli -- lancia {{file}} + +# Build a FratmScript file into JavaScript with debug CLI. +build-file file: + cargo run -p fratm-cli -- build {{file}} + +# Build all examples (`examples/*.fratm`). +examples-build: + cargo run -p fratm-cli -- build examples/01_salutatore.fratm + cargo run -p fratm-cli -- build examples/02_variabili_matematica.fratm + cargo run -p fratm-cli -- build examples/03_condizionali.fratm + cargo run -p fratm-cli -- build examples/04_loop.fratm + cargo run -p fratm-cli -- build examples/05_array_oggetti.fratm + cargo run -p fratm-cli -- build examples/06_funzioni.fratm + cargo run -p fratm-cli -- build examples/07_async.fratm + cargo run -p fratm-cli -- build examples/08_classi.fratm + cargo run -p fratm-cli -- build examples/09_moduli.fratm + cargo run -p fratm-cli -- build examples/10_nuove_feature.fratm + cargo run -p fratm-cli -- build examples/11_oop_avanzato.fratm + cargo run -p fratm-cli -- build examples/12_trycatch_validazione.fratm + +# Launch all examples (`examples/*.fratm`). +examples-lancia: + cargo run -p fratm-cli -- lancia examples/01_salutatore.fratm + cargo run -p fratm-cli -- lancia examples/02_variabili_matematica.fratm + cargo run -p fratm-cli -- lancia examples/03_condizionali.fratm + cargo run -p fratm-cli -- lancia examples/04_loop.fratm + cargo run -p fratm-cli -- lancia examples/05_array_oggetti.fratm + cargo run -p fratm-cli -- lancia examples/06_funzioni.fratm + cargo run -p fratm-cli -- lancia examples/07_async.fratm + cargo run -p fratm-cli -- lancia examples/08_classi.fratm + cargo run -p fratm-cli -- lancia examples/09_moduli.fratm + cargo run -p fratm-cli -- lancia examples/10_nuove_feature.fratm + cargo run -p fratm-cli -- lancia examples/11_oop_avanzato.fratm + cargo run -p fratm-cli -- lancia examples/12_trycatch_validazione.fratm + +# Launch a FratmScript file with the compiled release binary. +lancia-release file: + {{fratm_release_bin}} lancia {{file}} + +# Build WASM output for the playground. +wasm: + cd crates/fratm-wasm; wasm-pack build --target web --out-dir ../../packages/playground/public/pkg + +# Start playground dev server. +playground-dev: + cd packages/playground; pnpm dev + +# Build VSCode extension sources. +vscode-build: + cd packages/vscode-extension; pnpm compile + +# Open personal scripts launcher menu. +personal-lancia: + cargo run -p fratm-cli -- lancia PersonalScripts/run_personal_scripts.fratm diff --git a/packages/playground/README.md b/packages/playground/README.md index 62ad2d3..c4351a6 100644 --- a/packages/playground/README.md +++ b/packages/playground/README.md @@ -58,9 +58,11 @@ Output will be in the `dist/` folder. 1. **Hello World** - variables and output 2. **Fibonacci** - recursive functions 3. **Pizzaiolo Class** - classes and methods -4. **Async/Await** - asynchronous functions -5. **Logical Operators** - e, o, no, pure, manco -6. **Arrays and Objects** - data structures +4. **Advanced OOP** - extends, super, static methods +5. **Async/Await** - asynchronous functions +6. **Logical Operators** - e, o, no, pure, manco +7. **Arrays and Objects** - data structures +8. **Try/Catch** - throw and error handling ## Tech Stack diff --git a/packages/playground/src/App.tsx b/packages/playground/src/App.tsx index a80cf89..648f34b 100644 --- a/packages/playground/src/App.tsx +++ b/packages/playground/src/App.tsx @@ -102,9 +102,11 @@ function App() { + + diff --git a/packages/playground/src/lib/examples.ts b/packages/playground/src/lib/examples.ts index 41422dc..99d5771 100644 --- a/packages/playground/src/lib/examples.ts +++ b/packages/playground/src/lib/examples.ts @@ -45,6 +45,36 @@ stamm a dì(gennaro.faiPizza("Marinara")) stamm a dì(gennaro.faiPizza("Diavola")) stamm a dì("Pizzas made: " + gennaro.pizzeFatte)`, + oop_avanzato: `// OOP avanzato: extends, super, static +na famiglie Persona { + costruttore(nome) { + stu cos.nome = nome + } + + saluta() { + piglie "Ue, so' " + stu cos.nome + } + + fisso categoria() { + piglie "Essere umano" + } +} + +na famiglie Pizzaiolo figlio 'e Persona { + costruttore(nome, specialita) { + 'o pate(nome) + stu cos.specialita = specialita + } + + saluta() { + piglie 'o pate.saluta() + " e faccio " + stu cos.specialita + } +} + +chist è gennaro = nu bell Pizzaiolo("Gennaro", "Margherita") +stamm a dì(gennaro.saluta()) +stamm a dì("Categoria: " + Persona.categoria())`, + async: `// Async/Await in FratmScript mo vir facc caricaDati() { stamm a dì("Loading...") @@ -104,6 +134,29 @@ stamm a dì(" Customer: " + ordine.cliente) stamm a dì(" Pizza: " + ordine.pizza) stamm a dì(" Quantity: " + ordine.quantita) stamm a dì(" Delivery: " + (ordine.consegna ? "Yes" : "No"))`, + + trycatch: `// Try/Catch and Throw +facc validaEta(eta) { + si (eta < 0) { + iett nu bell Error("Eta negativa") + } + si (eta > 130) { + iett nu bell Error("Eta troppo alta") + } + piglie "Eta valida: " + eta +} + +facc prova(eta) { + pruvamm { + stamm a dì(validaEta(eta)) + } e si schiatta (err) { + avvis a dì("Errore con " + eta + ": " + err.message) + } +} + +prova(35) +prova(-1) +prova(200)`, } export const defaultCode = `// Try FratmScript!