working prototype

This commit is contained in:
anon
2024-08-23 21:04:44 +02:00
commit 14083ecab4
11 changed files with 342 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
*.o
*.yy.*
*.out
*.dot
output.png
.gdb_history

47
Makefile Normal file
View File

@ -0,0 +1,47 @@
.PHONY: clean test bootstrap
.SUFFIXES:
# --- Paths / files
SOURCE.d := source
OBJECT.d := object
SOURCE := main.cpp
OBJECT := $(addprefix ${OBJECT.d}/,${SOURCE})
OBJECT := $(subst .cpp,.o,${OBJECT})
GENSOURCE := include_lexer.yy.cpp
GENSOURCE := $(addprefix ${OBJECT.d}/,${GENSOURCE})
GENOBJECT := $(subst .cpp,.o,${GENSOURCE})
# --- Tools/Flags
LDLIBS := -lgvc -lcgraph
ifeq (${DEBUG}, 1)
LFLAGS += --debug --trace
CFLAGS += -O0 -ggdb -fno-inline
CPPFLAGS += -DDEBUG
FLEXFLAGS += --trace --debug
else
CFLAGS += -O3 -flto=auto -fno-stack-protector
endif
OUT := a.out
# --- Rule Section ---
${OUT}: ${GENSOURCE} ${GENOBJECT} ${OBJECT}
${LINK.cpp} -o $@ ${OBJECT} ${GENOBJECT} ${LDLIBS}
${OBJECT.d}/%.yy.cpp: ${SOURCE.d}/%.l
flex ${FLEXFLAGS} --header-file=object/$(basename $(notdir $<)).yy.h -o $@ $<
${OBJECT.d}/%.o: ${SOURCE.d}/%.cpp
${COMPILE.cpp} -o $@ $<
clean:
#-rm ${GENSOURCE}
#-rm ${OBJECT}
test:
./script debug/dummy_c_project/*.c
nomacs output.png

View File

@ -0,0 +1,6 @@
#include "i.h"
#include <stdio.h>
#include <stdbool.h>
int i = 86;

View File

@ -0,0 +1 @@
extern int i;

View File

@ -0,0 +1,7 @@
#include <stdio.h>
#include "i.h"
signed main() {
puts("--");
exit(i);
}

0
object/.gitkeep Normal file
View File

4
script Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
a.out $@
dot -Tpng -o output.png c.dot

72
source/CSourceFile.hpp Normal file
View File

@ -0,0 +1,72 @@
#include <string>
#include <libgen.h>
#include "node_t.h"
#define DEFAULT_SYSTEM_INCLUDE_PATH "/usr/include"
class CSourceFile {
public:
virtual std::string get_name() = 0;
virtual std::string get_path() = 0;
virtual node_t get_type() = 0;
};
class CSource : public CSourceFile {
std::string base_name;
std::string path;
public:
CSource(const char * const full_path) {
base_name = basename(strdup(full_path));
path = full_path;
}
std::string get_name() { return base_name; }
std::string get_path() { return path; }
node_t get_type() { return DEFAULT; };
};
class CHeader : public CSourceFile {
static std::vector<std::string> * libpath;
std::string name;
public:
CHeader(const char * const name_) : name(name_) {
;
}
std::string get_name() { return name; }
std::string get_path() { // XXX
return name.substr(1, name.size()-2);
}
node_t get_type() { return INTERFACE; };
};
class CSystemHeader : public CSourceFile {
static std::vector<std::string> * syslibpath;
std::string name;
public:
CSystemHeader(const char * const name_) : name(name_) {
;
}
std::string get_name() { return name; }
std::string get_path() {
return std::string()
+ DEFAULT_SYSTEM_INCLUDE_PATH
"/"
+ name.substr(1, name.size()-2);
}
node_t get_type() { return SYSTEM; };
};
CSourceFile * source_factory(const char * const name) {
switch (name[0]) {
case '"': return new CHeader(name);
case '<': return new CSystemHeader(name);
default: return new CSource(name);
}
}

111
source/include_lexer.l Normal file
View File

@ -0,0 +1,111 @@
%{
#include <vector>
#include <string>
#include <algorithm>
#include <stdio.h>
#include "CSourceFile.hpp"
#include "node_t.h"
extern void append_node(const char * const name, const char * const parent, const node_t type);
char buffer[113];
char buffer_empty_top = 0;
std::vector<CSourceFile*> input_file_queue;
std::vector<std::string> done_list;
void add_new_file(const char * const name);
int next_file(void);
%}
ib \"|\<
ie \"|\>
%x IN_NEW_INCLUDE
%option nodefault
%%
yyin = NULL;
if(next_file()) {
return 1;
}
^[[:space:]]*\#[[:space:]]*include[[:space:]]*{ib} {
BEGIN IN_NEW_INCLUDE;
buffer[buffer_empty_top++] = yytext[yyleng-1];
}
<IN_NEW_INCLUDE>{
{ie} {
BEGIN INITIAL;
buffer[buffer_empty_top++] = yytext[0];
buffer[buffer_empty_top] = '\0';
add_new_file(buffer);
buffer_empty_top = 0;
}
. {
buffer[buffer_empty_top++] = yytext[0];
}
\n { BEGIN INITIAL; }
}
.|\n { ; }
%%
void add_new_file(const char * const name) {
CSourceFile * this_file = source_factory(name);
if (std::find(done_list.begin(),
done_list.end(),
name
) != done_list.end()
|| std::find_if(input_file_queue.begin(),
input_file_queue.end(),
[&](const auto& e) {
return e->get_name() == name;
}) != input_file_queue.end()) {
fprintf(stderr, "\033[33mSkipped file: '%s'\033[0m\n", name);
} else {
fprintf(stderr, "\033[32mNew file: '%s'\033[0m\n", name);
input_file_queue.push_back(this_file);
}
const char * parent = NULL;
if (this_file->get_type() != DEFAULT) { // XXX
parent = strdup(input_file_queue.front()->get_name().c_str());
}
append_node(this_file->get_name().c_str(), parent, this_file->get_type());
}
int next_file(void) {
while (!yyin) {
if (input_file_queue.empty()) { return 1; }
yyin = fopen(input_file_queue.front()->get_path().c_str(), "r");
if (!yyin) {
perror(input_file_queue.front()->get_path().c_str());
input_file_queue.erase(input_file_queue.begin());
}
}
fprintf(stderr, "\033[34mOpening file: '%s'\033[0m\n", input_file_queue.front()->get_path().c_str());
done_list.push_back(input_file_queue.front()->get_name());
return 0;
}
int yywrap(void) {
input_file_queue.erase(input_file_queue.begin());
yyin = NULL;
if(next_file()) {
return 1;
}
return 0;
}

73
source/main.cpp Normal file
View File

@ -0,0 +1,73 @@
#include <stdio.h>
#include <graphviz/cgraph.h>
#include "node_t.h"
#include "include_lexer.h"
#define OUTPUT_FILE_NAME "c.dot"
#define dagnode(o, s) { \
auto ds = strdup(s); \
o = agnode(g, ds, true); \
free(ds); \
} while (0)
Agraph_t * g;
extern void add_new_file(const char * const name);
using namespace std;
static inline
void init_graph(void) {
g = agopen("G", Agdirected, NULL);
agattr(g, AGNODE, "shape", "oval");
agattr(g, AGRAPH, "rankdir", "BT");
}
static inline
void finish_graph() {
FILE * output = fopen(OUTPUT_FILE_NAME, "w");
(void)agwrite(g, output);
fclose(output);
agclose(g);
}
void append_node(const char * const name, const char * const parent, const node_t type) {
Agnode_t * new_node;
dagnode(new_node, name);
if (parent) {
Agnode_t * parent_node;
dagnode(parent_node, parent);
agedge(g, parent_node, new_node, NULL, true);
}
switch (type) {
case SYSTEM: {
agset(new_node, "shape", "hexagon");
} break;
case INTERFACE: {
agset(new_node, "shape", "box");
} break;
}
}
signed main(const int argc, const char * const argv[]) {
if (argc < 2) {
return 1;
}
init_graph();
for (int i = 1; i < argc; i++) {
add_new_file(argv[i]);
}
yylex();
finish_graph();
return 0;
}

15
source/node_t.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef NODE_TYPE_H
#define NODE_TYPE_H
/* Yes, we do have a class hierarchy too, however thats opaque to 'main.cpp'.
* 'main.cpp' only uses these to apply styling, it does not care about anything else
* and thats how its should be.
*/
typedef enum {
DEFAULT,
INTERFACE,
SYSTEM,
} node_t;
#endif