Quindi, sono un po 'in ritardo sul gioco, ma queste domande (in particolare su DOES >) mi stavano anche mistificando, essendo nuove di Forth. Ecco cosa ho imparato e come l'ho implementato:
[ TL; DR: "CREATE" crea una parola con un comportamento semplice e predefinito. "SE >" non ritorna al suo chiamante. Invece, usa l'indirizzo di ritorno per inserire un "goto" nella definizione più recente.]
CREATE non prende nulla dalla pila o non restituisce nulla. Analizza una parola dall'input e crea una voce di dizionario per esso. Riempie il codice per la parola appena creata con un codice standardplanplan che spinge un indirizzo allineato nello stack e restituisce semplicemente (lo stesso indirizzo allineato che le chiamate successive "," (virgola) si riempirebbero con i dati). Nel mio sistema, il codice generato per qualcosa come CREATE NewVar
sarebbe simile a questo:
NewVar: push_data next_addr
return
next_addr:
Pertanto, potremmo definire (inizializzato) VARIABILE come:
: VARIABLE CREATE 0 , ;
o, nel codice pseudo-macchina:
VARIABLE: call CREATE
push_data 0
call comma
return
Dire qualcosa come VARIABLE NewVar
renderebbe quindi NewVar come una parola che "push_data / return". Il 0 ,
memorizza quindi uno zero all'indirizzo che NewVar mette nello stack - il "next_addr" mostrato nel frammento di codice. Fare cose come NewVar @
o 42 NewVar !
quindi legge e scrive quella posizione.
Non c'è niente di speciale (almeno nel mio sistema) sulle parole tra CREATE e DOES > o anche le parole dopo DOES > in termini di compilazione. Una parola la cui definizione usa CREATE e DOES > è compilato normalmente, assicurandosi che DOES > è "chiama" nel codice compilato. La cosa speciale che fa > fa come segue: Trova la posizione del codice dell'ultima parola creata e poi sovrascrive l'istruzione "return" con un'istruzione "jump", la cui destinazione è l'indirizzo nello stack di ritorno del DOES > routine. Questo indirizzo viene estratto dallo stack di ritorno ogni volta che DOES > è chiamato, usato per fare istruzioni di "salto". Quando DO > poi cerca di tornare al suo chiamante, in realtà sta tornando a chi ha chiamato la parola che aveva DO > in esso ... non al resto del codice. La mia implementazione di DOES > sembra un po 'come questo:
DOES>: [find second opcode of latest definition]
popr ; like "R>"
[overwrite opcode with a "jump" to TOS value]
return
Quindi, quando definiamo VALUE in questo modo:
: VALUE VARIABLE DOES> @ ;
Quello che otteniamo è qualcosa del genere:
VALUE: call VARIABLE
call DOES>
call fetch <-- return address of call to DOES>
return
Il codice chiamerà la nostra definizione di VARIABLE, data sopra, che a sua volta chiama CREATE per creare la nuova voce. Ma quando chiama DOES & gt ;, DOES > apparirà l'indirizzo di ritorno puntato sopra e regolerà la definizione di NewVar per passare a quella posizione, rendendo così NewVar in modo che spinga "next_addr" nello stack di dati come prima, ma ora salta e chiama fetch. Ciò rende anche l'esecuzione di VALUE tale che termina con la chiamata a DOES & gt ;. Quando DO > restituisce, restituisce al chiamante di VALUE, non al resto della definizione di VALUE ( @ ;
).
Si noti che CREATE non è una parola immediata. La nostra definizione di VARIABLE era CREATE 0 ,
, ma ciò non crea una parola denominata "0", poiché CREATE viene rilevato durante la definizione di VARIABLE ... viene appena inserito nella definizione. Invece, quando VARIABLE viene effettivamente eseguito, CREATE tenterà di recuperare la parola successiva dall'input e ne creerà una nuova.
Nota anche che DOES > si basa molto sulla parola più recentemente definita. Avrei potuto cercarlo diligentemente per l'opcode "return", ma invece, sapendo come CREATE crea una nuova parola, ha semplicemente usato un offset fisso in quella definizione. Mi sto appoggiando allo spec. che dice "Esiste una condizione ambigua se [la definizione più recente] non è stata definita con CREATE ...". Nel mio sistema, quella "condizione ambigua" è che alcune parole ottengono un opcode "salta" come seconda istruzione.
What does DOES> consume as input?
Consuma il proprio indirizzo di ritorno e utilizza anche una variabile globale che punta alla definizione più recente.
Is there some kind of 'default behaviour' that DOES> overrides?
Sì. È il comportamento predefinito ("push_data / return") creato da CREATE.