Repository navigation
Parte II Triad Core
Ogni capitolo di questa parte usa solo Triad-Core. Gli esempi con Output: sono stati eseguiti con l'interprete JS; il caso di conformance indicato in fondo a ogni capitolo è il riferimento normativo da leggere e far girare.
| elemento | forma | esempi |
|---|---|---|
| identificatore | lettera o _, poi lettere/cifre/_; un ! finale è ammesso |
x, somma_2, push!
|
| intero | cifre decimali, _ come separatore; il segno è l'operatore unario |
42, 1_000_000, -5
|
| booleano |
true, false
|
|
| nothing | nothing |
|
| stringa |
"..." con escape \n \t \" \\; nessuna interpolazione
|
"ciao\n" |
| simbolo |
: seguito da un nome o da un operatore |
:alice, :+
|
| commento |
# fino a fine riga; #= ... =# su più righe |
|
| separatore | a capo oppure ;
|
println(1); println(2) |
Operatori di Core: + - * % ^ == != < <= > >= && || ! = += -= *= -> ?:.
Parole riservate usate da Core: function end if elseif else while let begin return break continue true false nothing.
Non fanno parte di Core: letterali Float (1.5), variabili logiche ?x, macro @nome,
interpolazione $x, l'operatore /.
#= commento
su più righe =#
x = 1_000 # commento di riga
println(x); println("tab:\t|", " virgolette: \"", " backslash: \\")
println(:alice, " ", :+)Output:
1000
tab: | virgolette: " backslash: \
alice +
Attenzione.
"x = $x"in Core stampa letteralmentex = $x: l'interpolazione è solo Triad-Full. In Core si concatena:"x = " * string(x).I commenti
#= … =#non si annidano nell'interprete JS (SPEC Full richiede l'annidamento).
Riferimento: conformance/core/cases/aritmetica, simboli_base, stringhe_base.
| tipo | valori | typeof(v) |
|---|---|---|
Int |
interi con segno a 64 bit, aritmetica con wrap (complemento a due) | Int |
Bool |
true, false
|
Bool |
String |
sequenza immutabile di byte UTF-8 | String |
Symbol |
:nome; uguali se hanno lo stesso nome |
Symbol |
List |
lista eterogenea mutabile, indici da 1 | List |
Function |
funzione nominata, lambda o chiusura | Function |
Nothing |
nothing |
Nothing |
typeof restituisce il nome del tipo come simbolo.
- concatenano gli argomenti senza separatori;
printlnaggiunge\n; -
Intin decimale,Boolcometrue/false,Stringcosì com'è,Nothingcomenothing; - un
Symbolpassato direttamente è stampato senza:; dentro una lista con:; - una
Listè stampata come[a, b]; le stringhe dentro una lista sono stampate senza virgolette.
xs = [1, :a, "s", true, nothing, [2, 3]]
println(xs)
println(:alice)
println(typeof(1), " ", typeof("s"), " ", typeof(:a), " ", typeof(xs))
println(typeof(nothing), " ", typeof(true), " ", typeof(x -> x))
print("senza ", "a capo")
println()Output:
[1, :a, s, true, nothing, [2, 3]]
alice
Int String Symbol List
Nothing Bool Function
senza a capo
== e != confrontano numeri, stringhe e simboli per valore, le liste elemento per
elemento (anche annidate), le funzioni per identità.
println([1, [2, [3]]] == [1, [2, [3]]])
println("ab" == "a" * "b", " ", :a == :a, " ", 1 == 2)Output:
true
true true false
Riferimento: conformance/core/cases/valori, nothing_base, confronti.
-
nome = esprlega o aggiorna un nome; l'assegnamento è un'espressione e vale il valore assegnato. -
+=,-=,*=aggiornano una variabile esistente (il lato sinistro è valutato una volta). - Usare un nome mai assegnato è un errore di esecuzione (
undefined variable). - Non ci sono dichiarazioni né tipi: le variabili sono dinamiche.
x = 42
y = x + 8
x = x + 1
z = (x + y) * 2
println(x, " ", y, " ", z)
n = 10
n += 5
n -= 3
n *= 2
println(n)Output:
43 50 186
24
Le regole di visibilità (dentro funzioni, let, lambda) sono nel capitolo 10.
Riferimento: conformance/core/cases/variabili.
| livello | operatori | associatività |
|---|---|---|
| 1 |
= += -= *=
|
destra |
| 2 | c ? a : b |
destra |
| 3 | || |
sinistra, cortocircuito |
| 4 | && |
sinistra, cortocircuito |
| 5 |
== != < <= > >=
|
sinistra |
| 6 |
+ -
|
sinistra |
| 7 |
* %
|
sinistra |
| 8 | ^ |
destra |
| 9 | unari - !
|
prefissi |
| 10 |
f(args) xs[i]
|
postfissi |
-
+ - * ^suIntcon wrap a 64 bit; -
%è il resto con il segno del dividendo (comeremdi Julia):-7 % 3 == -1; -
^con esponente negativo è un errore;%per zero è un errore; -
non esiste
/in Core (la divisione è di Triad-Full).
println(2 + 3 * 4, " ", (2 + 3) * 4, " ", 2 ^ 3 ^ 2)
println(7 % 3, " ", -7 % 3, " ", 10 % 4)
println(9223372036854775807 + 1)Output:
14 20 512
1 -1 2
-9223372036854775808
- Le condizioni (
if,while, ternario,&&,||,!) accettano soloBool: non c'è "truthiness" (if 1è un errore). -
&&e||cortocircuitano. - I confronti non si concatenano:
1 < 2 < 3è un errore (si confrontaBool < Int); scrivi1 < 2 && 2 < 3.
println(!false, " ", true && false, " ", false || true)
println(1 < 2 && 2 < 3)
x = 3
println(x > 2 ? "grande" : "piccolo")Output:
true false true
true
grande
Riferimento: conformance/core/cases/aritmetica, operatori, interi, confronti.
In Triad tutto è un'espressione: if, while, let, begin hanno un valore (quello
dell'ultima istruzione eseguita, nothing se nessuna).
function classifica(x)
if x < 0
"negativo"
elseif x == 0
"zero"
else
"positivo"
end
end
println(classifica(-3), " ", classifica(0), " ", classifica(7))
v = if 2 > 1 :si else :no end
println(v)Output:
negativo zero positivo
si
i = 0
somma_dispari = 0
while i < 10
i += 1
if i % 2 == 0
continue
end
somma_dispari += i
end
println(somma_dispari)
while true
break
end
println(:fine)Output:
25
fine
break e continue agiscono sul while più interno. Non esiste for in Core: si itera con un
contatore e while.
let introduce un nuovo scope con legami locali; begin … end raggruppa istruzioni senza
creare legami nuovi.
x = 1
let x = 10
let x = 20
println(x)
end
println(x)
end
println(x)
r = begin
a = 2
a * 21
end
println(r)Output:
20
10
1
42
Riferimento: conformance/core/cases/controllo, if_else, if_elseif, if_nested,
if_no_else, while_loop, let_annidato, begin_block.
# forma breve: il corpo è un'espressione
quadrato(x) = x * x
# forma blocco: il valore è quello dell'ultima espressione
function calcola(n)
m = n + 1
m * 2
end
# funzione anonima assegnata a un nome
moltiplica = function (a, b)
a * b
end
println(quadrato(4), " ", calcola(3), " ", moltiplica(6, 7))Output:
16 8 42
-
return eesce subito dalla funzione;returnda solo restituiscenothing. - Una funzione nominata è visibile nel proprio corpo: la ricorsione diretta funziona.
- Il numero di argomenti deve coincidere con i parametri (altrimenti
arity mismatch).
function taglia(n)
if n > 100
return :grande
end
:piccola
end
fattoriale(n) = n < 2 ? 1 : n * fattoriale(n - 1)
fib(n) = n < 2 ? n : fib(n - 1) + fib(n - 2)
mcd(a, b) = b == 0 ? a : mcd(b, a % b)
println(taglia(500), " ", taglia(5))
println(fattoriale(10), " ", fib(15), " ", mcd(48, 18))Output:
grande piccola
3628800 610 6
In Triad-Full una funzione che stampa deve dichiarare !io (capitolo 28). Core
accetta l'annotazione e la ignora: scriverla rende il codice già pronto per Full.
function mostra() !io
println("hello from block")
20 + 22
end
println(mostra())Output:
hello from block
42
Riferimento: conformance/core/cases/funzioni, funzioni_semplici, funzioni_blocco,
funzioni_io, ricorsione, gcd, formula.
doppio = x -> x * 2
somma = (a, b) -> a + b
applica = f -> f(3)
println(doppio(5), " ", somma(1, 2), " ", applica(x -> x * x))Output:
10 3 9
Una lambda con corpo a più istruzioni si scrive con begin … end o con function (…) … end.
Le lambda senza parametri (() -> …) non sono supportate: usa function () … end.
Una lambda cattura l'ambiente in cui è creata. Secondo SPEC-CORE §5 le variabili catturate sono condivise: le mutazioni sono visibili.
function add(n)
x -> x + n
end
f = add(10)
g = add(100)
println(f(5), " ", g(5))
function contatore()
v = 0
x -> begin
v += x
v
end
end
acc = contatore()
acc(5)
println(acc(10))Output:
15 105
15
Il seed Windows cattura per valore e copre solo l'ultimo legame del frame: un accumulatore come
contatorepuò non funzionare sul seed (capitolo 15).
-
function, lambda,letewhileintroducono ambienti; un nome è visibile nell'ambiente in cui è legato e in quelli interni (scope lessicale). - Dentro una funzione nominata l'assegnamento lega nel frame locale: una funzione non modifica i globali.
- Dentro una lambda l'assegnamento a un nome già legato fuori aggiorna quel legame (è così che funziona l'accumulatore sopra).
- Al top level i cicli aggiornano i globali.
-
let x = …crea un nuovox; assegnarexdentro illetnon tocca l'xesterno.
x = 1
function f()
x = 2
x
end
println(f(), " ", x)
c = 0
function incrementa()
c += 1
end
incrementa()
println(c)Output:
2 1
0
Per far "uscire" un risultato da una funzione restituiscilo, oppure usa una lista (che è mutabile, capitolo 11).
Riferimento: conformance/core/cases/lambda, closure, chiusure, chiusure_local,
chiusura_in_if, let_chiusura, scope.
- Letterale:
[1, 2, 3],[], eterogenee e annidabili. - Indici da 1:
xs[1]è il primo elemento; fuori range è un errore. - Assegnamento per indice:
xs[i] = v, anchexs[i] += v. -
length(xs)numero di elementi;push!(xs, v)aggiunge in coda, muta la lista e la restituisce. - Le liste sono riferimenti: due nomi possono indicare la stessa lista.
xs = [1, 2, 3]
println(xs[1], " ", length(xs))
push!(xs, 4)
xs[1] += 100
println(xs)
alias = xs
push!(alias, 5)
println(xs)
tot = 0
i = 1
while i <= length(xs)
tot += xs[i]
i += 1
end
println(tot)Output:
1 3
[101, 2, 3, 4]
[101, 2, 3, 4, 5]
115
Core non ha map, filter, reduce, sum, sort: si scrivono in Core (vedi
capitolo 16).
Sul seed Windows: liste solo di interi,
push!sì,xs[i]elength(xs)no; c'è invece l'helper non standardget(xs, i)con indici da 0 (capitolo 15).
Riferimento: conformance/core/cases/liste, liste_base, liste_push.
| operazione | significato |
|---|---|
a * b |
concatenazione di stringhe |
length(s) |
numero di byte UTF-8 (length("é") == 2) |
string(x) |
rappresentazione testuale di qualsiasi valore (string(:a) == "a", string([1, 2]) == "[1, 2]") |
Int(s) |
da stringa a intero (Int("42") == 42); da Bool: 0/1
|
Symbol(s) |
da stringa a simbolo (Symbol("ciao") == :ciao) |
typeof(x) |
tipo come simbolo |
nome = "Triad"
saluto = "ciao " * nome
println(saluto, " (", length(saluto), " byte)")
println("risposta: " * string(40 + 2))
println(Int("41") + 1, " ", Symbol("bob"), " ", string(true))
println(:ok == Symbol("ok"))Output:
ciao Triad (10 byte)
risposta: 42
42 bob true
true
Per accedere ai singoli byte di una stringa servono i primitivi di bootstrap del capitolo successivo.
Riferimento: conformance/core/cases/stringhe, stringhe_concat, stringhe_multi,
simboli_base.
Servono a S1 per leggere e analizzare sorgente Triad. Non fanno parte di Triad-Full (SPEC-CORE §8): usali solo nel codice di bootstrap.
| primitiva | semantica |
|---|---|
readfile(path) |
contenuto del file come String; errore se non leggibile |
strlen(s) |
numero di byte |
strbyte(s, i) |
byte (Int 0–255) alla posizione i, da 1 |
strsub(s, a, b) |
sottostringa dai byte a a b inclusi, da 1 |
exit(n) |
termina il processo con codice n
|
Esempio: contare le cifre di una stringa e separare la prima parola.
s = "abc 123 xyz"
cifre = 0
i = 1
while i <= strlen(s)
b = strbyte(s, i)
if b >= 48 && b <= 57
cifre += 1
end
i += 1
end
println(cifre)
fine = 1
while fine <= strlen(s) && strbyte(s, fine) != 32
fine += 1
end
println(strsub(s, 1, fine - 1))Output:
3
abc
Un programma può leggere se stesso o un altro sorgente con readfile(ARGS[1]): è esattamente
il primo passo di un interprete self-hosted (capitolo 37).
Core non ha try/catch né valori Err (SPEC-CORE §9): un errore termina il processo con
exit code 1 e stampa un messaggio diagnostico su stderr. Quello che era già stato stampato su
stdout resta.
println("prima")
println(non_esiste)
println("dopo")stdout: prima
stderr: error: undefined variable non_esiste
exit: 1
Messaggi dell'interprete JS (stderr) e cause tipiche:
| messaggio | causa |
|---|---|
parse error: unexpected token … / parse error: expected …
|
sintassi non Core (es. 1.5, /, () -> …, end mancante) |
error: undefined variable NOME |
nome mai assegnato, o funzione chiamata prima di essere definita |
error: not a function |
si chiama un valore che non è una funzione (x = 5; x(1)) |
error: arity mismatch |
numero di argomenti diverso dai parametri |
error: index out of range |
xs[i] con i < 1 o i > length(xs)
|
error: + type error (e simili per - * % < …) |
operandi di tipo sbagliato (1 + "a") |
error: if cond / while cond / ternary cond / && expects bool
|
condizione non Bool
|
error: div by zero / negative exponent
|
x % 0, 2 ^ -1
|
error: unknown function NOME |
builtin riservato usato in modo non supportato |
cannot read file: … |
file inesistente o non leggibile |
usage: triad FILE [ARG...] (exit 2) |
nessun file passato |
Il seed Windows non stampa messaggi dettagliati: esce con codice 1 (sintassi, variabile o
funzione non definita, arità, lettura) o 2 (nessun argomento).
Per propagare errori senza terminare il processo (come farà S1 per i programmi Full) si usa
una convenzione con liste: [:Err, kind, msg].
err(kind, msg) = [:Err, kind, msg]
is_err(v) = typeof(v) == :List && length(v) == 3 && v[1] == :Err
function div_intera(a, b)
if b == 0
return err(:domain, "divisione per zero")
end
q = 0
while a >= b
a -= b
q += 1
end
q
end
r = div_intera(7, 0)
if is_err(r)
println("errore ", r[2], ": ", r[3])
end
println(div_intera(17, 5))Output:
errore domain: divisione per zero
3
Nota: && cortocircuita, quindi v[1] è valutato solo se v è una lista di 3 elementi.
Il contratto è conformance/core/: entrambi i runtime devono produrre gli stessi golden. Oggi
l'interprete JS li passa tutti, il seed Windows i 33 casi elencati nel job seed-windows della
CI. Fuori da quei casi i due runtime differiscono:
| funzionalità | interprete JS (seed/dev/triad_core.cjs) |
seed Windows (seed.exe) |
|---|---|---|
| interi, Bool, operatori, confronti, ternario | ✅ | ✅ |
if/elseif/else, while, let, begin
|
✅ | ✅ |
| funzioni forma breve e blocco, ricorsione | ✅ | ✅ (max 4 parametri) |
return, break, continue
|
✅ | non elencati in seed/windows-x86_64/README.md
|
| lambda | ✅ più parametri | ✅ un solo parametro |
| chiusure | condivise (mutazioni visibili, SPEC-CORE §5) | per valore, solo l'ultimo legame del frame |
stringhe, *, length(s)
|
✅ | ✅ |
string(x) |
✅ tutti i tipi | solo Int e String
|
simboli, nothing
|
✅ | ✅ |
| liste | eterogenee, xs[i], xs[i] = v, length, push!
|
solo interi; costruzione, stampa e push! (max 64 elementi) |
get(xs, i) |
non esiste | helper non standard, indici da 0 |
-=, *=, Int, Symbol, typeof
|
✅ | non elencati nel README del seed |
ARGS, primitivi di bootstrap |
✅ | non elencati nel README del seed |
| stampa di una funzione | #lambda(x) |
fun |
triad - (stdin) |
✅ | no |
| messaggi d'errore | su stderr | solo exit code |
Regola pratica: scrivi codice che rispetta SPEC-CORE e verificalo con l'interprete JS; se
deve girare sul seed (come selfhost/), restringiti alla colonna di destra e verificalo su
Windows.
Core non ha map/filter/reduce: si scrivono una volta con while e lambda.
function mappa(f, xs)
out = []
i = 1
while i <= length(xs)
push!(out, f(xs[i]))
i += 1
end
out
end
function filtra(p, xs)
out = []
i = 1
while i <= length(xs)
if p(xs[i])
push!(out, xs[i])
end
i += 1
end
out
end
function riduci(f, acc, xs)
i = 1
while i <= length(xs)
acc = f(acc, xs[i])
i += 1
end
acc
end
xs = [1, 2, 3, 4, 5, 6]
println(mappa(x -> x * x, xs))
println(filtra(x -> x % 2 == 0, xs))
println(riduci((a, b) -> a + b, 0, xs))Output:
[1, 4, 9, 16, 25, 36]
[2, 4, 6]
21
Core non ha struct: un record è una lista con un simbolo come tag nella prima posizione.
punto(x, y) = [:Punto, x, y]
px(p) = p[2]
py(p) = p[3]
somma_punti(a, b) = punto(px(a) + px(b), py(a) + py(b))
p = somma_punti(punto(1, 2), punto(10, 20))
println(p, " ", p[1] == :Punto)Output:
[:Punto, 11, 22] true
È la struttura usata dagli interpreti in selfhost/: una lista piatta [nome1, valore1, nome2, valore2, …] (in Core puoi anche usare coppie annidate).
function env_get(env, nome)
i = 1
while i < length(env)
if env[i] == nome
return env[i + 1]
end
i += 2
end
nothing
end
function env_set(env, nome, valore)
i = 1
while i < length(env)
if env[i] == nome
env[i + 1] = valore
return env
end
i += 2
end
push!(env, nome)
push!(env, valore)
end
env = []
env_set(env, :x, 10)
env_set(env, :y, 20)
env_set(env, :x, 11)
println(env, " ", env_get(env, :x), " ", env_get(env, :z))Output:
[:x, 11, :y, 20] 11 nothing
Poiché una funzione nominata non può riassegnare un globale, lo stato condiviso si tiene in una lista (mutabile) o in una chiusura.
stato = [0]
function tick()
stato[1] += 1
end
tick()
tick()
println(stato[1])Output:
2
function ripeti(s, n)
out = ""
while n > 0
out = out * s
n -= 1
end
out
end
function unisci(xs, sep)
out = ""
i = 1
while i <= length(xs)
if i > 1
out = out * sep
end
out = out * string(xs[i])
i += 1
end
out
end
println(ripeti("ab", 3))
println(unisci([1, 2, 3], ", "))Output:
ababab
1, 2, 3
- Funzioni piccole con nomi chiari (in italiano, come nel resto del repository).
- Caso base esplicito in ogni ricorsione; per profondità grandi preferisci
while. - Cicli con contatori espliciti (
i = 1 … while i <= length(xs)). - Non usare
fact,rule,solve,structe le altre parole chiave di Full come nomi: il codice deve restare valido anche in Triad-Full. - Scrivi
!iosulle funzioni che stampano: Core lo ignora, Full lo richiede.
Ogni esercizio ha una soluzione verificata (aprila solo dopo averci provato). Per controllare
la tua soluzione: salvala in un file ed eseguila con node seed/dev/triad_core.cjs.
Senza usare ^, scrivi potenza(b, e) ricorsiva e somma_fino(n) (somma 1 + … + n) con
while. Stampa potenza(3, 13), potenza(2, 10) e somma_fino(100).
Soluzione
potenza(b, e) = e == 0 ? 1 : b * potenza(b, e - 1)
function somma_fino(n)
s = 0
i = 1
while i <= n
s += i
i += 1
end
s
end
println(potenza(3, 13), " ", potenza(2, 10), " ", somma_fino(100))Output:
1594323 1024 5050
Stampa la lista dei numeri primi minori di 30.
Soluzione
function primo(n)
if n < 2
return false
end
d = 2
while d * d <= n
if n % d == 0
return false
end
d += 1
end
true
end
primi = []
n = 2
while n < 30
if primo(n)
push!(primi, n)
end
n += 1
end
println(primi)Output:
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
Scrivi inverti(xs) che restituisce una nuova lista con gli elementi in ordine inverso.
Soluzione
function inverti(xs)
out = []
i = length(xs)
while i >= 1
push!(out, xs[i])
i -= 1
end
out
end
println(inverti([1, :due, "tre"]))Output:
[tre, :due, 1]
Ordina in loco [5, 2, 9, 1, 7].
Soluzione
function ordina!(xs)
i = 2
while i <= length(xs)
k = xs[i]
j = i - 1
while j >= 1 && xs[j] > k
xs[j + 1] = xs[j]
j -= 1
end
xs[j + 1] = k
i += 1
end
xs
end
println(ordina!([5, 2, 9, 1, 7]))Output:
[1, 2, 5, 7, 9]
Il ! finale nel nome segnala, per convenzione, una funzione che muta il suo argomento.
Conta le parole (separate da spazi) di "il linguaggio Triad " usando strbyte.
Soluzione
function conta_parole(s)
n = 0
dentro = false
i = 1
while i <= strlen(s)
spazio = strbyte(s, i) == 32
if !spazio && !dentro
n += 1
end
dentro = !spazio
i += 1
end
n
end
println(conta_parole("il linguaggio Triad "))Output:
3
Trasforma "12+x*3" in una lista di token [tipo, testo], con tipi :num, :id, :op.
È il primo passo di un interprete (vedi selfhost/tokenizer.td).
Soluzione
cifra(b) = b >= 48 && b <= 57
lettera(b) = (b >= 97 && b <= 122) || (b >= 65 && b <= 90) || b == 95
function tokenizza(s)
toks = []
i = 1
while i <= strlen(s)
b = strbyte(s, i)
if cifra(b)
j = i
while j <= strlen(s) && cifra(strbyte(s, j))
j += 1
end
push!(toks, [:num, strsub(s, i, j - 1)])
i = j
elseif lettera(b)
j = i
while j <= strlen(s) && (lettera(strbyte(s, j)) || cifra(strbyte(s, j)))
j += 1
end
push!(toks, [:id, strsub(s, i, j - 1)])
i = j
else
push!(toks, [:op, strsub(s, i, i)])
i += 1
end
end
toks
end
println(tokenizza("12+x*3"))Output:
[[:num, 12], [:op, +], [:id, x], [:op, *], [:num, 3]]
Usando i token dell'esercizio 6, scrivi un valutatore a discesa ricorsiva per + e * con
precedenza, dove le variabili vengono da una lista di associazioni. Suggerimento: tieni la
posizione corrente in una lista pos = [1] per poterla aggiornare dalle funzioni.
Soluzione
cifra(b) = b >= 48 && b <= 57
lettera(b) = (b >= 97 && b <= 122) || b == 95
function tokenizza(s)
toks = []
i = 1
while i <= strlen(s)
b = strbyte(s, i)
if cifra(b)
j = i
while j <= strlen(s) && cifra(strbyte(s, j))
j += 1
end
push!(toks, [:num, strsub(s, i, j - 1)])
i = j
elseif lettera(b)
j = i
while j <= strlen(s) && lettera(strbyte(s, j))
j += 1
end
push!(toks, [:id, strsub(s, i, j - 1)])
i = j
else
push!(toks, [:op, strsub(s, i, i)])
i += 1
end
end
toks
end
function cerca(env, nome)
i = 1
while i < length(env)
if env[i] == nome
return env[i + 1]
end
i += 2
end
0
end
function primario(toks, pos, env)
t = toks[pos[1]]
pos[1] += 1
if t[1] == :num
Int(t[2])
else
cerca(env, t[2])
end
end
function prodotto(toks, pos, env)
v = primario(toks, pos, env)
while pos[1] <= length(toks) && toks[pos[1]][2] == "*"
pos[1] += 1
v = v * primario(toks, pos, env)
end
v
end
function somma(toks, pos, env)
v = prodotto(toks, pos, env)
while pos[1] <= length(toks) && toks[pos[1]][2] == "+"
pos[1] += 1
v = v + prodotto(toks, pos, env)
end
v
end
valuta(s, env) = somma(tokenizza(s), [1], env)
println(valuta("12+x*3", ["x", 10]))
println(valuta("2*3+4*5", []))Output:
42
26
Parte prima
Parte seconda 4. Le parole del linguaggio 5. Valori e tipi 6. Espressioni, operatori e variabili 7. Decidere e ripetere 8. Funzioni e ricorsione 9. Lambda, chiusure e visibilità dei nomi 10. Liste e dati strutturati 11. Stringhe, simboli e byte 12. Quando le cose vanno storte 13. Progetto: un piccolo interprete
Parte terza 14. Tipi, struct e multiple dispatch 15. Programmazione logica 16. Codice come dato e macro 17. Garanzie, costrutti Full e interfaccia
Parte quarta 18. Anatomia del progetto e contratto di conformità 19. L'interprete di riferimento 20. Il seed in codice macchina 21. Verso il self-hosting 22. Gli strumenti di sviluppo
Appendici
- Riferimento dei builtin Core
- Catalogo conformance Core
- Catalogo conformance Full
- Sorgenti degli esempi
- Sorgenti self-hosted
- Documenti di riferimento
- Indice analitico
Manuale di consultazione