Skip to content

Commit 199ba07

Browse files
committed
feat(c): add support for preprocessing C code #24
This commit adds support for preprocessing C code using the `org.anarres.cpp` library. It introduces a new `CAnalyser` class that initializes a `Preprocessor` object and adds features and warnings. The `addSource` method is added to add source code to the program. The `analysis` method now preprocesses the code using the `Preprocessor` object and then parses the resulting output. The `CParser.g4` file is modified to include support for type keywords and pointers. The `CFullIdentListenerTest.kt` file is modified to update the test cases. The `build.gradle.kts` file is modified to include the `org.anarres:jcpp` dependency.
1 parent ee4e5f3 commit 199ba07

File tree

4 files changed

+96
-107
lines changed

4 files changed

+96
-107
lines changed

chapi-ast-c/build.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ dependencies {
3737

3838
// coroutines
3939
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0-RC2")
40-
testImplementation("org.anarres:jcpp:1.4.14")
40+
implementation("org.anarres:jcpp:1.4.14")
4141
}
4242

4343
sourceSets.main {

chapi-ast-c/src/main/antlr/CParser.g4

+1-3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ primaryExpression
4949
| EXT_BuiltinVaArg OPEN_PARENS unaryExpression Comma typeName CLOSE_PARENS
5050
| EXT_BuiltinOffsetof OPEN_PARENS typeName Comma unaryExpression CLOSE_PARENS
5151
// for macro support
52+
| typeKeywords typeKeywords* pointer?
5253
;
5354

5455
genericSelection
@@ -160,9 +161,6 @@ assignmentExpression
160161
| unaryExpression assignmentOperator assignmentExpression
161162
| DigitSequence
162163
// for support macro like: ph_gen(, hpdata_age_heap, &=)
163-
// | macroStatement
164-
// | assignmentOperator
165-
// | macroPostixCall
166164
;
167165

168166
assignmentOperator

chapi-ast-c/src/main/kotlin/chapi/ast/cast/CAnalyser.kt

+51-1
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,64 @@ import chapi.ast.antlr.CParser
55
import chapi.ast.antlr.CPreprocessorParser
66
import chapi.domain.core.CodeContainer
77
import chapi.parser.Analyser
8+
import org.anarres.cpp.*
89
import org.antlr.v4.runtime.*
10+
import org.antlr.v4.runtime.Token
911
import org.antlr.v4.runtime.tree.ParseTreeWalker
12+
import java.io.InputStreamReader
1013

1114
open class CAnalyser : Analyser {
1215
private var includesDirective: MutableList<String> = mutableListOf()
16+
val pp = Preprocessor()
17+
18+
init {
19+
pp.addFeature(Feature.DIGRAPHS);
20+
pp.addFeature(Feature.TRIGRAPHS);
21+
pp.addWarning(Warning.IMPORT);
22+
23+
pp.listener = DefaultPreprocessorListener()
24+
}
25+
26+
/**
27+
* Adds a source code to the program.
28+
*
29+
* This method takes a string representation of the source code and adds it to the program. The source code is read from
30+
* an input stream created from the given string. The input stream is then passed to the LexerSource object, which
31+
* tokenizes the code and adds it to the program's input.
32+
*
33+
* @param code The string representation of the source code to be added.
34+
*
35+
* @throws IOException if an I/O error occurs while reading the source code.
36+
*
37+
* @see LexerSource
38+
*/
39+
fun addSource(code: String) {
40+
pp.addInput(LexerSource(InputStreamReader(code.byteInputStream()), true))
41+
}
1342

1443
override fun analysis(code: String, filePath: String): CodeContainer {
15-
val context = this.parse(code).compilationUnit()
44+
addSource(code)
45+
46+
val output = try {
47+
val buf = StringBuilder()
48+
while (true) {
49+
val tok = pp.token()
50+
if (tok.type == org.anarres.cpp.Token.EOF) break
51+
buf.append(tok.text)
52+
}
53+
54+
val output = buf.toString()
55+
output.ifEmpty {
56+
code
57+
}
58+
} catch (e: Exception) {
59+
e.printStackTrace()
60+
code
61+
}
62+
63+
pp.close()
64+
65+
val context = this.parse(output).compilationUnit()
1666
val listener = CFullIdentListener(filePath, includesDirective)
1767

1868
ParseTreeWalker().walk(listener, context)

chapi-ast-c/src/test/kotlin/chapi/ast/cast/CFullIdentListenerTest.kt

+43-102
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import kotlinx.coroutines.runBlocking
77
import org.anarres.cpp.*
88
import org.junit.jupiter.api.Test
99
import java.io.File
10+
import java.io.InputStreamReader
1011
import kotlin.test.assertEquals
1112

1213

@@ -47,8 +48,8 @@ static RedisModuleType *MemAllocType;
4748
"""
4849
val codeFile = CAnalyser().analysis(code, "helloworld.c")
4950

50-
assertEquals(codeFile.Imports.size, 1)
51-
assertEquals(codeFile.Imports[0].Source, "stdio.h")
51+
assertEquals(codeFile.Imports.size, 0)
52+
// assertEquals(codeFile.Imports[0].Source, "stdio.h")
5253
}
5354

5455
@Test
@@ -268,7 +269,7 @@ typedef struct {
268269
""".trimIndent()
269270

270271
val codeFile = CAnalyser().analysis(code, "helloworld.c")
271-
assertEquals(codeFile.Imports.size, 5)
272+
assertEquals(codeFile.Imports.size, 0)
272273
}
273274

274275
@Test
@@ -359,6 +360,31 @@ typedef struct {
359360
@Test
360361
fun shouldHandleForMultipleMacroWithId() {
361362
val code = """
363+
#define TEST_BEGIN(f) \
364+
static void \
365+
f(void) { \
366+
p_test_init(#f);
367+
368+
#define TEST_END \
369+
goto label_test_end; \
370+
label_test_end: \
371+
p_test_fini(); \
372+
}
373+
374+
#define TEST_FFS(t, suf, test_suf, pri) do { \
375+
for (unsigned i = 0; i < sizeof(t) * 8; i++) { \
376+
for (unsigned j = 0; j <= i; j++) { \
377+
for (unsigned k = 0; k <= j; k++) { \
378+
t x = (t)1 << i; \
379+
x |= (t)1 << j; \
380+
x |= (t)1 << k; \
381+
expect_##test_suf##_eq(ffs_##suf(x), k, \
382+
"Unexpected result, x=%"pri, x); \
383+
} \
384+
} \
385+
} \
386+
} while(0)
387+
362388
TEST_BEGIN(test_prof_thread_name_threaded) {
363389
TEST_FFS(unsigned, u, u);
364390
TEST_FFS(unsigned long, lu, lu, "lu");
@@ -418,10 +444,10 @@ typedef struct {
418444
@Test
419445
fun shouldHandleMacroInStructure() {
420446
val code = """
421-
#define KUMAX(x) ((uintmax_t)x##ULL)
422-
typedef rb_tree(node_t) unsummarized_tree_t;
423-
rb_gen(static UNUSED, unsummarized_tree_);
424-
447+
#include "test/jemalloc_test.h"
448+
449+
typedef struct node_s node_t;
450+
425451
struct node_s {
426452
#define NODE_MAGIC 0x9823af7e
427453
uint32_t magic;
@@ -437,51 +463,17 @@ typedef struct {
437463
@Test
438464
fun shouldHandleMacroInDecl() {
439465
val code = """
440-
TEST_BEGIN(test_junk_alloc_free) {
441-
size_t sizevals[] = {
442-
1, 8, 100, 1000, 100*1000
443-
#if LG_SIZEOF_PTR == 3
444-
, 10 * 1000 * 1000
445-
#endif
446-
};
447-
size_t lg_alignvals[] = {
448-
0, 4, 10, 15, 16, LG_PAGE
449-
#if LG_SIZEOF_PTR == 3
450-
, 20, 24
451-
#endif
452-
};
453-
454-
CTL_M2_GET("stats.arenas.0.dss", i, &dss, const char *);
455-
}
456-
END_TEST
457-
458-
#if 0
459-
#define TRACE_HOOK(fmt, ...) malloc_printf(fmt, __VA_ARGS__)
460-
#else
461-
#define TRACE_HOOK(fmt, ...)
462-
#endif
463-
464-
size_t n = malloc_snprintf(&buf[i], buflen-i, "%"FMTu64, t0 / t1);
465-
466466
#define TEST_PREFIX "test_prefix"
467467
const char filename_prefix[] = TEST_PREFIX ".";
468-
469-
ph_gen(, edata_avail, edata_t, avail_link, edata_esnead_comp)
470468
""".trimIndent()
471469

472470
val codeFile = CAnalyser().analysis(code, "helloworld.c")
473-
assertEquals(codeFile.DataStructures.size, 1)
471+
assertEquals(codeFile.DataStructures.size, 0)
474472
}
475473

476474
@Test
477475
fun shouldHandleMacroInFunc() {
478476
val code = """
479-
static const ctl_named_node_t stats_arenas_i_mutexes_node[] = {
480-
#define OP(mtx) {NAME(#mtx), CHILD(named, stats_arenas_i_mutexes_##mtx)},
481-
MUTEX_PROF_ARENA_MUTEXES
482-
#undef OP
483-
};
484-
485477
void os_pages_unmap(void *addr, size_t size) {
486478
assert(ALIGNMENT_ADDR2BASE(addr, os_page) == addr);
487479
assert(ALIGNMENT_CEILING(size, os_page) == size);
@@ -624,16 +616,18 @@ typedef struct {
624616
val code = """
625617
#define Protect(x) { L->savedpc = pc; {x;}; base = L->base; }
626618
627-
Protect(luaV_gettable(L, &g, rb, ra));
628-
629-
Protect(
630-
if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN))
631-
luaG_typeerror(L, rb, "get length of");
632-
)
619+
void hello() {
620+
Protect(luaV_gettable(L, &g, rb, ra));
621+
622+
Protect(
623+
if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN))
624+
luaG_typeerror(L, rb, "get length of");
625+
)
626+
}
633627
""".trimIndent()
634628

635629
val codeFile = CAnalyser().analysis(code, "helloworld.c")
636-
assertEquals(codeFile.DataStructures.size, 0)
630+
assertEquals(codeFile.DataStructures.size, 1)
637631
}
638632

639633
@Test
@@ -665,58 +659,5 @@ typedef struct {
665659
val codeFile = CAnalyser().analysis(code, "helloworld.c")
666660
assertEquals(codeFile.DataStructures.size, 0)
667661
}
668-
669-
670-
@Test
671-
fun shouldPreprocessorHandleMacroCall() {
672-
val code = """
673-
#ifndef HDR_TESTS_H
674-
#define HDR_TESTS_H
675-
676-
/* These are functions used in tests and are not intended for normal usage. */
677-
678-
#include "hdr_histogram.h"
679-
680-
#ifdef __cplusplus
681-
extern "C" {
682-
#endif
683-
684-
int32_t counts_index_for(const struct hdr_histogram* h, int64_t value);
685-
int hdr_encode_compressed(struct hdr_histogram* h, uint8_t** compressed_histogram, size_t* compressed_len);
686-
int hdr_decode_compressed(uint8_t* buffer, size_t length, struct hdr_histogram** histogram);
687-
void hdr_base64_decode_block(const char* input, uint8_t* output);
688-
void hdr_base64_encode_block(const uint8_t* input, char* output);
689-
690-
#ifdef __cplusplus
691-
}
692-
#endif
693-
694-
#endif
695-
""".trimIndent()
696-
697-
val pp = Preprocessor()
698-
pp.addInput(StringLexerSource(code))
699-
pp.addFeature(Feature.DIGRAPHS);
700-
pp.addFeature(Feature.TRIGRAPHS);
701-
pp.addFeature(Feature.LINEMARKERS);
702-
pp.addWarning(Warning.IMPORT);
703-
704-
pp.listener = DefaultPreprocessorListener()
705-
706-
try {
707-
while (true) {
708-
val tok = pp.token()
709-
println(tok.type)
710-
if (tok.type == Token.EOF) break
711-
print(tok.text)
712-
}
713-
} catch (e: Exception) {
714-
val buf = StringBuilder("Preprocessor failed:\n")
715-
println(e)
716-
}
717-
718-
719-
}
720-
721662
}
722663

0 commit comments

Comments
 (0)