In che modo un linguaggio abilitato a macroistruzione tiene traccia del codice sorgente per il debug?

StackOverflow https://stackoverflow.com/questions/3214603

Domanda

Questa è una domanda più teorica sulle macro (penso).So che i macro prendono il codice sorgente e producono il codice oggetto senza valutarlo, consentendo ai programmatori di creare più strutture sintattiche versatili.Se dovessi classificare questi due sistemi macro, direi che c'era la macro "C Style" e la macro "stile lisp".

Sembra che il debug dei macro possa essere un po 'complicato perché in fase di runtime, il codice che in realtà è diverso dalla sorgente.

In che modo il debugger tiene traccia dell'esecuzione del programma in termini di codice sorgente pre-preliminare?C'è una speciale "modalità di debug" che deve essere impostata per catturare dati extra sulla macro?

In C, posso capire che hai impostato un interruttore temporale di compilazione per il debug, ma come sarebbe un linguaggio interpretato, come alcune forme di lisp, fallo?

Scusa per non provarlo, ma il toolchain LISP richiede più tempo di quanto dovrò passare a capire.

È stato utile?

Soluzione

Non penso che ci sia una differenza fondamentale in macro di "c style" e "stile lisp" in come sono compilati. Entrambi trasformano la fonte prima che il compilatore sia adeguato. La grande differenza è che i macro di c utilizzano il preprocessore C (un linguaggio secondario più debole per lo più per una semplice sostituzione di stringhe), mentre le macro di Lisp sono scritte in Lisp stesso (e quindi può fare qualsiasi cosa).

(come a parte: non ho visto un liscio non compilato da un po '... certamente non dal giro del secolo. Ma se qualcosa, essendo interpretato sembrerebbe rendere il problema di debug macro più facile, non più difficile, dal momento che hai più informazioni in giro.)

Sono d'accordo con Michael: non ho visto un debugger per c che gestisce affatto i macro. Il codice che utilizza macro viene trasformato prima che succeda qualcosa. The modalità "debug" per compilare il codice C Generalmente significa memorizza i negozi Funzioni, tipi, variabili, nomi di file e tali - Non penso a nessuna di loro archiviare informazioni su Macro.

    .
  • Per i programmi di debug che utilizzano macro , Lisp è praticamente lo stesso Come c qui: il tuo debugger vede il codice compilato, non la macro applicazione. Tipicamente macro sono. tenuto semplice e debugged indipendentemente prima dell'uso, per evitare La necessità di questo, proprio come c.

  • Per il debug dei macro se stessi , prima di andare a usarlo da qualche parte, Lisp ha caratteristiche Ciò rende così più facile che in c, ad esempio, il repl e macroexpand-1 ( Anche se in C. C'è ovviamente un modo per macroexpand un intero file, completamente, a una volta). Puoi vedere il prima e dopo di una macroespensione, proprio nel tuo editor, quando scrivi it.

Non riesco a ricordare in qualsiasi momento ho corso su una situazione in cui debug di in una definizione macro sarebbe stata utile. O è un bug nella definizione della macro, nel qual caso macroexpand-1 isola immediatamente il problema, oppure è un bug in cui, nel qual caso i normali strutture di debug funzionino bene e non mi interessa che una macroeespansione sia avvenuta tra due fotogrammi della mia chiamata Stack.

Altri suggerimenti

in Lispworks Gli sviluppatori possono utilizzare http://www.lispworks.com/documentation/lw60/ide-m/html/ide-m-433.htm "rel=" nofollow noreferrer "> strumento stepper .

Lispworks fornisce uno stepper, dove si può fare un passo attraverso l'intero Macro Processo di espansione .

Dovresti davvero esaminare il tipo di supporto che racket ha per il codice di debug con le macro. Questo supporto ha due aspetti, come menziona Ken. Da un lato c'è il problema delle macro di debug: in comune lisp il modo migliore per farlo è semplicemente espandere manualmente le forme macro. Con CPP La situazione è simile ma più primitiva - eseguirai il codice attraverso solo l'espansione del CPP e ispezionare il risultato. Tuttavia, entrambi sono insufficienti per le macro più coinvolte, e questa era la motivazione per avere un macro Debugger in racket - Mostra le fasi di espansione della sintassi uno per uno, con ulteriori indicazioni basate sulla GUI per cose come identificatori legati ecc.

Sul lato di utilizzando le macro , la racchetta è sempre stata più avanzata di altri schemi e implementazioni LISP. L'idea è che ogni espressione (come oggetto sintattico) è il codice più dati aggiuntivi che contengono la sua posizione sorgente. In questo modo Quando un modulo è una macro, il codice espanso che ha parti provenienti dalla macro avrà la posizione di origine corretta, dalla definizione della macro piuttosto che dal suo uso (dove i moduli non sono realmente presenti). Alcuni schemi e implementazioni LISP implementerà un limitato per questo utilizzando l'identità delle sottoschede, come Dmitry-VK menzionato.

Non conosco le macro di Lisp (che sospetto sono probabilmente abbastanza diverse da macro) o il debug, ma molti - probabilmente la maggior parte - c / c ++ debugger non gestiscono il debug a livello di sorgente dei macro del preprocessore di C del preprocessore.

Generalmente, c / c ++ Debugger che non "passo" nella definizione della macro.Se una macro si espande in più affermazioni, il debugger di solito rimane sulla stessa riga di origine (dove viene richiamata la macro) per ogni operazione del "passo" del debugger.

Questo può rendere i debug macro un po 'più doloroso di quanto potrebbero altrimenti essere - ancora un altro motivo per evitarli in c / c ++.Se una macro sta comportando male in modo davvero misterioso, lascerò cadere nella modalità di assemblaggio per eseguire il debug o espandere la macro (manualmente o usando l'interruttore del compilatore).È piuttosto raro che devi andare a quell'estremo;Se stai scrivendo macro che è complicata, probabilmente stai prendendo l'approccio sbagliato.

Di solito nel debug a livello di sorgente C ha granularità della linea (comando "successivo") o granularità a livello di istruzione ("passo in"). I processori macro inseriscono direttività speciali in sorgente elaborata che consentono al compilatore di mappare sequenze compilate delle istruzioni della CPU alle linee del codice sorgente.

In Lisp non esiste alcuna convenzione tra macro e compilatore per tenere traccia del codice sorgente alla mappatura del codice compilata, quindi non è sempre possibile eseguire un codice mono-passo nel codice sorgente.

Opzione ovvia è quella di fare un singolo passo in codice macroexpieded. Il compilatore vede già definitivo, espanso, versione del codice e può tenere traccia del codice sorgente alla mappatura del codice macchina.

Altra opzione è usare il fatto che le espressioni LISP durante la manipolazione hanno identità. Se la macro è semplice e semplicemente distruggente e incolla il codice in template, alcune espressioni di codice espanso saranno identiche (rispetto al confronto EQ) alle espressioni che sono state lette dal codice sorgente. In questo caso il compilatore può mappare alcune espressioni dal codice espanso al codice sorgente.

La risposta semplice è che è complicato ;-) Ci sono diverse cose diverse che contribuiscono ad essere in grado di eseguire il debug di un programma e ancora di più per il monitoraggio delle macro.

In C e C ++, il preprocessore viene utilizzato per espandere macro e include nel codice sorgente effettivo. I nomi di file originari e i numeri di linea sono tracciati in questo file sorgente espanso utilizzando #line Direttive.

http://msdn.microsoft.com/ IT-US / Biblioteca / B5W2CZay (vs.80) .aspx

Quando un programma C o C ++ viene compilato con debug abilitato, l'assemblatore genera ulteriori informazioni nel file dell'oggetto che traccia le linee di origine, i nomi dei simboli, i descrittori digitali, ecc.

http://sources.redhat.com/gdb/onlinelinedocs/stabs.html

Il sistema operativo ha funzioni che rendono possibile un debugger di allegare a un processo e controllare l'esecuzione del processo; Pausa, single stepping, ecc.

Quando un debugger è collegato al programma, traduce la pila di processo e il contatore del programma in forma simbolica guardando il significato degli indirizzi del programma nelle informazioni di debug.

Lingue dinamiche in genere eseguono in una macchina virtuale, indipendentemente dal fatto che sia un interprete o un bytecode VM. È la VM che fornisce ai ganci per consentire a un debugger di controllare il flusso del programma e ispezionare lo stato del programma.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top