working prototype
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
*.o
|
||||
*.yy.*
|
||||
*.out
|
||||
*.dot
|
||||
output.png
|
||||
.gdb_history
|
47
Makefile
Normal file
47
Makefile
Normal 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
|
6
debug/dummy_c_project/i.c
Normal file
6
debug/dummy_c_project/i.c
Normal file
@ -0,0 +1,6 @@
|
||||
#include "i.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
int i = 86;
|
1
debug/dummy_c_project/i.h
Normal file
1
debug/dummy_c_project/i.h
Normal file
@ -0,0 +1 @@
|
||||
extern int i;
|
7
debug/dummy_c_project/main.c
Normal file
7
debug/dummy_c_project/main.c
Normal file
@ -0,0 +1,7 @@
|
||||
#include <stdio.h>
|
||||
#include "i.h"
|
||||
|
||||
signed main() {
|
||||
puts("--");
|
||||
exit(i);
|
||||
}
|
0
object/.gitkeep
Normal file
0
object/.gitkeep
Normal file
4
script
Executable file
4
script
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
a.out $@
|
||||
dot -Tpng -o output.png c.dot
|
72
source/CSourceFile.hpp
Normal file
72
source/CSourceFile.hpp
Normal 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
111
source/include_lexer.l
Normal 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
73
source/main.cpp
Normal 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
15
source/node_t.h
Normal 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
|
Reference in New Issue
Block a user