bic : AC -Dolmetscher und API -ExplorerDies ist ein Projekt, mit dem Entwickler C-APIs mithilfe einer Read-Eval-Print-Schleife, auch als Repl bezeichnet, untersuchen und testen können.

Die Abhängigkeiten der BIC-Laufzeit sind wie folgt:
Um BIC zu bauen, brauchen Sie:
Bitte stellen Sie sicher, dass Sie diese vor dem Bau von BIC installiert haben. Der folgende Befehl sollte diese in einem Debian/Ubuntu -System installieren:
APT-GET Installieren Sie Build-Es-wesentliche libreadline-dev autoconf-archive libgmp-dev erwarten
Sie können auch den folgenden Befehl verwenden, um die erforderlichen Abhängigkeiten über Homebrew auf einem MacOS -System zu installieren.
Brew Install install Bison Flex GMP Readline Autoconf-Archive
Sie können BIC mit den folgenden Befehlen kompilieren und installieren:
AutoreConf -i ./Configure-Entzug-Debug machen Installation machen
Um auf einem MacOS -System aufzubauen, müssen Sie die Konfigurationslinie ändern auf:
Yacc = "$ (Brew -Prefix Bison)/bin/Bison -y" ./configure -enable -debug
Sie können Docker verwenden, um BIC mit dem folgenden Befehl zu erstellen und auszuführen:
Docker Build -t BIC https://github.com/hexagonal-sun/bic.git#master
Sobald das Bild erstellt ist, können Sie BIC mit:
Docker Run -i BIC
Wenn Sie Arch Linux verwenden, können Sie BIC aus AUR installieren:
yay -s bic
Beim Aufrufen von BIC ohne Argumente wird dem Benutzer eine Replement -Eingabeaufforderung dargestellt:
Bic>
Hier können Sie C -Anweisungen eingeben und verschiedene Systemüberschriften #include , um Zugriff auf verschiedene APIs auf dem System zu gewährleisten. Aussagen können direkt in die Wiederholung eingegeben werden. Es besteht keine Notwendigkeit, eine Funktion zu definieren, damit sie bewertet werden sollen. Sagen Sie, wir möchten das folgende C -Programm ausführen:
#include <stdio.h>
int main ()
{
FILE * f = fopen ( "out.txt" , "w" );
fputs ( "Hello, world!n" , f );
return 0 ;
}Wir können dies auf der Reply mit BIC mit den folgenden Befehlen tun:
BIC> #include <stdio.h>
BIC> Datei *f;
F
Bic> f = fopen ("test.txt", "w");
BIC> futs ("Hallo, Welt! N", f);
1
Bic>
Dies führt dazu, dass BIC die C-Library fopen() und fputs() -Funktionen erstellt, um eine Datei zu erstellen und die Hello World-Zeichenfolge hineinzuschreiben. Wenn Sie jetzt BIC beenden, sollten Sie im aktuellen Arbeitsverzeichnis einen test.txt mit dem in ihm enthaltenen String Hello, Worldn sehen.
Beachten Sie, dass nach der Bewertung eines Ausdrucks BIC das Ergebnis der Bewertung druckt. Dies kann nützlich sein, um einfache Ausdrücke zu testen:
BIC> 2 * 8 + Fileno (f); 19
Sie können BIC verwenden, um Informationen über eine Variable oder einen Typ zu erhalten, die durch das Präfix seines Namens mit einem deklariert wurden ? . Diese spezielle Syntax funktioniert nur in der REP, ermöglicht es Ihnen jedoch, verschiedene Eigenschaften zu Typen und Variablen zu erhalten. Zum Beispiel:
BIC> #include <stdio.h> Bic>? Stdout STDOut ist ein Zeiger auf eine Struktur _io_file. Der Wert von STDOut beträgt 0x7ff1325bc5c0. sizeof (stdout) = 8 Bytes. Stdout wurde unter: /usr/include/stdio.h:138 deklariert.
Wenn die Wiederholung beginnt, wird BIC sehen, ob ~/.bic existiert. Wenn dies der Fall ist, wird es automatisch bewertet und die resultierende Umgebung wird von der Reply verwendet. Dies kann nützlich sein, um Funktionen oder Varizen zu definieren, die üblicherweise verwendet werden. Zum Beispiel enthält unsere ~/.bic -Datei::
#include <stdio.h>
int increment ( int a )
{
return a + 1 ;
}
puts ( "Good morning, Dave." );Wenn wir die Reply starten, bekommen wir:
$ bic Guten Morgen, Dave. BIC> Inkrement (2); 3
Wenn Sie BIC eine Quelldatei zusammen mit -s als Befehlszeilenargument übergeben, wird sie bewertet, indem Sie eine main() -Funktion aufrufen. Angenommen, wir haben den test.c , der Folgendes enthält:
#include <stdio.h>
int factorial ( int n )
{
if (! n )
{
return 1 ;
}
return n * factorial ( n - 1 );
}
int main ()
{
printf ( "Factorial of 4 is: %dn" , factorial ( 4 ));
return 0 ;
} Wir können dann BIC mit -s test.c aufrufen, um es zu bewerten:
$ BIC -S -Test.c Faktor für 4 ist: 24
Wenn Sie Argumente an eine C -Datei weitergeben möchten, fügen Sie sie an die Befehlszeile von BIC hinzu. Sobald -s verarbeitet hat, werden alle anderen Argumente als Parameter behandelt, die an das Programm übergeben werden sollen. Diese Parameter werden als argc und argv -Variablen erstellt und an main() übergeben. Der Wert von argv[0] ist der Name der C -Datei, die BIC ausführt. Betrachten Sie das folgende C -Programm:
#include <stdio.h>
int main ( int argc , char * argv [])
{
for ( int i = 0 ; i < argc ; i ++ )
printf ( "argv[%d] = %sn" , i , argv [ i ]);
return 0 ;
}Wenn wir keine Argumente verabschieden:
$ BIC -S -Test.c argv [0] = test.c
Wenn wir BIC mit weiteren Argumenten aufrufen, werden sie an das Programm übergeben:
$ BIC -S -Test.c -a foo -s Bar ABC argv [0] = test.c argv [1] = -a argv [2] = foo argv [3] = -s argv [4] = bar argv [5] = a argv [6] = b argv [7] = c
Sie können auch einen speziellen Ausdruck verwenden: <REPL>; In Ihrem Quellcode, um BIC zu einem bestimmten Zeitpunkt in der Dateiauswertung in die Repon zu bringen:

Sie können BIC verwenden, um die APIs anderer Bibliotheken als LIBC zu untersuchen. Nehmen wir an, wir möchten die Capstone -Bibliothek erkunden. Wir geben eine -l -Option über, um diese Bibliothek zu Beginn dieser Bibliothek zu laden. Zum Beispiel:

Beachten Sie, dass BIC, wenn ein zusammengesetzter Datentyp (eine struct oder eine union ) gedruckt ist, alle Mitgliedsnamen und ihre entsprechenden Werte angezeigt werden.
Im Zentrum der Implementierung von BIC steht das tree . Dies sind generische Objekte, die verwendet werden können, um ein ganzes Programm sowie den aktuellen Evaluator -Zustand darzustellen. Es ist in tree.h und tree.c implementiert. Jeder Baumtyp ist in c.lang definiert. Die c.lang Datei ist eine Lisp-ähnliche Spezifikation von:
T_ADD .Addition .tADD .LHS und RHS .Der Code zum Erstellen eines Objekts mit dem oben genannten Satz von Attributen wäre:
( deftype T_ADD " Addition " " tADD "
( " LHS " " RHS " ))Nach der Definition können wir dieses Objekt auf folgende Weise in unserem C -Code verwenden:
tree make_increment ( tree number )
{
tree add = tree_make ( T_ADD );
tADD_LHS ( add ) = number ;
tADD_RHS ( add ) = tree_make_const_int ( 1 );
return add ;
} Beachten Sie, dass eine Reihe von Accessor -Makros, tADD_LHS() und tADD_RHS() für uns generiert wurden, um auf die verschiedenen Eigenschaftsstätten zuzugreifen. Wenn --enable-debug während der Kompilierung festgelegt wird, wird jeder dieser Makros zu einer Prüfung erweitert, um sicherzustellen, dass das Objekt beim Einstellen der Eigenschaft tADD_LHS eines Objekts tatsächlich eine Instanz eines T_ADD ist.
Die c.lang Datei wird von zahlreichen Quell-zu-Source-Compilern gelesen, die Code-Snippets generieren. Diese Dienstprogramme umfassen:
gentype : Erzeugt eine Liste von Baumobjekttypen.gentree : Erzeugt eine Struktur, die alle Eigenschaftsdaten für Baumobjekte enthält.genctypes : Generiert eine Liste von Bauchobjekten vom Typ C -Typ - diese stellen die grundlegenden Datentypen in C dar.genaccess : Generieren Sie Accessor -Makros für die Eigenschaften von Baumobjekten.gengc : Erzeugen Sie eine Markierungsfunktion für jedes Baumobjekt. Dadurch kann der Garbage Collector Objektbäume durchqueren.gendump : Generieren Sie Code, um Baumobjekte rekursiv auszugeben.gendot : Generieren Sie eine Punktdatei für eine bestimmte tree , sodass sie sichtbar machen kann. Die Ausgabe des Lexer & Parser ist eine tree , die dann in den Evaluator ( evaluator.c ) übergeben wird. Der Evaluator bewertet dann jedes Baumelement rekursiv und aktualisiert den internen Evaluator -Status und führt damit ein Programm aus.
Aufrufe zu Funktionen, die außerhalb des Bewerters extern sind, werden auf plattformabhängige Weise behandelt. Derzeit sind X86_64 und AARG64 die einzigen unterstützten Plattformen, und der Code, der dies verarbeitet, befindet sich in den Ordnern x86_64 bzw. aarch64 . Dies funktioniert, indem ein tree (dargestellt durch eine T_FN_CALL ) vom Evaluator mit allen bewerteten Argumenten und zu einer einfachen verknüpften Liste verwendet wird. Dies wird dann in der Assembly überquert, um den Wert gemäß X86_64 oder AARG64 in das richtige Register zu verschieben und sich dann an die Funktionsadresse zu verzweigen.
Parser und Lexer werden in parser.m4 bzw. lex.m4 implementiert. Nach dem Durchlaufen von M4 sind zwei Bison -Parser und zwei Flex -Lexer.
Der Grund für zwei Parser ist, dass die Grammatik für eine C -Reply sehr unterschiedlich ist als die einer C -Datei. Zum Beispiel möchten wir, dass der Benutzer in der Lage sein kann, Anweisungen eingeben zu können, die in der REP bewertet werden können, ohne dass sie in eine Funktion einwickelt werden müssen. Das Schreiben einer Erklärung, die außerhalb eines Funktionsorganisation ist, ist nicht gültig. Deshalb möchten wir nicht, dass der Benutzer bloße Anweisungen in eine C -Datei schreiben kann. Um dies zu erreichen, haben wir zwei verschiedene Grammatikregeln, die zwei Parser erzeugen. Die meisten Grammatikregeln überlappen sich und daher verwenden wir eine einzelne M4 -Datei, um die Unterschiede zu pflegen.