Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@

.vscode/

build/
.direnv/

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ For better user experience there is additional API for string transformations, w

There are several examples located in `examples` directory. To check them out, run the following command in root directory of the repository:

- LibClang bindings: `c3c run clang -- ./build/clang.c3i`
- Sandbox to try out library: `c3c run sandbox` - it will write to stdout
- My dummy: `c3c run dummy -- ./build/dummy.c3i` or `c3c run dummy` - it will print output to stdout
- Vulkan: `c3c run vulkan -- ./build/vulkan.c3i`
Expand All @@ -98,6 +99,7 @@ Join and contact me in [C3 discord channel](https://discord.com/channels/6503459

## TODO

- Comments: convert attributes
- Fix multiple definitions for macros
- Inline definition of records and enums inside of function parameters
- Allow user to write any attributes on an entity based on WriteAttrs
Expand Down
41 changes: 22 additions & 19 deletions bindgen.c3l/bindgen.c3i
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ extern fn void? translate_header(
BGOptions opts = {},
BGTransCallbacks trans_callbacks = {},
BGGenCallbacks gen_callbacks = {})
@extern("bg_translate_header");
@cname("bg_translate_header");


<*
Expand All @@ -44,6 +44,7 @@ extern fn void? translate_header(
"This option is a function which returns true if we want to '#include' file passed to 'name' parameter."
"Note that 'name' is either relative or absolute path to a file"
skip_errors : "Whether to skip clang's parse errors. Note, that '<...> file not found' errors are skipped always and not controlled with this flag"
generate_docs : "Whether to retrive docs and translate to C3 docs"
*>
struct BGOptions
{
Expand All @@ -58,6 +59,7 @@ struct BGOptions
{
bool skip_errors;
bool no_verbose;
bool generate_docs;
}
}

Expand All @@ -77,7 +79,8 @@ struct BGTransCallbacks
{
BGTransFn func;
BGTransFn type;
BGTransFn variable;
BGTransFn variable;
BGTransFn enum_field;
BGTransFn constant;
BGTransFn func_macro;
}
Expand Down Expand Up @@ -260,28 +263,28 @@ struct BGModuleWrap
*>
module bindgen::bgstr;

fn bool is_between(String name, String bound_1, String bound_2) @extern("bgstr_is_between");
fn bool is_between(String name, String bound_1, String bound_2) @cname("bgstr_is_between");

fn String snake_to_camel(String str, Allocator alloc) @extern("bgstr_snake_to_camel");
fn String snake_to_pascal(String str, Allocator alloc) @extern("bgstr_snake_to_pascal");
fn String snake_to_screaming(String str, Allocator alloc) @extern("bgstr_snake_to_screaming");
fn String snake_to_camel(String str, Allocator alloc) @cname("bgstr_snake_to_camel");
fn String snake_to_pascal(String str, Allocator alloc) @cname("bgstr_snake_to_pascal");
fn String snake_to_screaming(String str, Allocator alloc) @cname("bgstr_snake_to_screaming");

fn String pascal_to_screaming(String str, Allocator alloc) @extern("bgstr_pascal_to_screaming");
fn String pascal_to_snake(String str, Allocator alloc) @extern("bgstr_pascal_to_snake");
fn String pascal_to_camel(String str, Allocator alloc) @extern("bgstr_pascal_to_camel");
fn String pascal_to_screaming(String str, Allocator alloc) @cname("bgstr_pascal_to_screaming");
fn String pascal_to_snake(String str, Allocator alloc) @cname("bgstr_pascal_to_snake");
fn String pascal_to_camel(String str, Allocator alloc) @cname("bgstr_pascal_to_camel");

fn String camel_to_screaming(String str, Allocator alloc) @extern("bgstr_camel_to_screaming");
fn String camel_to_snake(String str, Allocator alloc) @extern("bgstr_camel_to_snake");
fn String camel_to_pascal(String str, Allocator alloc) @extern("bgstr_camel_to_pascal");
fn String camel_to_screaming(String str, Allocator alloc) @cname("bgstr_camel_to_screaming");
fn String camel_to_snake(String str, Allocator alloc) @cname("bgstr_camel_to_snake");
fn String camel_to_pascal(String str, Allocator alloc) @cname("bgstr_camel_to_pascal");

fn String screaming_to_snake(String str, Allocator alloc) @extern("bgstr_screaming_to_snake");
fn String screaming_to_camel(String str, Allocator alloc) @extern("bgstr_screaming_to_camel");
fn String screaming_to_pascal(String str, Allocator alloc) @extern("bgstr_screaming_to_pascal");
fn String screaming_to_snake(String str, Allocator alloc) @cname("bgstr_screaming_to_snake");
fn String screaming_to_camel(String str, Allocator alloc) @cname("bgstr_screaming_to_camel");
fn String screaming_to_pascal(String str, Allocator alloc) @cname("bgstr_screaming_to_pascal");

fn String mixed_to_camel(String str, Allocator alloc) @extern("bgstr_mixed_to_camel");
fn String mixed_to_screaming(String str, Allocator alloc) @extern("bgstr_mixed_to_screaming");
fn String mixed_to_snake(String str, Allocator alloc) @extern("bgstr_mixed_to_snake");
fn String mixed_to_pascal(String str, Allocator alloc) @extern("bgstr_mixed_to_pascal");
fn String mixed_to_camel(String str, Allocator alloc) @cname("bgstr_mixed_to_camel");
fn String mixed_to_screaming(String str, Allocator alloc) @cname("bgstr_mixed_to_screaming");
fn String mixed_to_snake(String str, Allocator alloc) @cname("bgstr_mixed_to_snake");
fn String mixed_to_pascal(String str, Allocator alloc) @cname("bgstr_mixed_to_pascal");

macro bool String.is_between(String str, String a, String b) => is_between(str, a, b);

Expand Down
50 changes: 22 additions & 28 deletions bindgen.c3l/src/bg.c3
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,15 @@ fn void? translateHeader(
if (opts.out_name != "") @pool() {
err::warn(opts.no_verbose, "BGOptions.out_name is deprecated in favour of BGOptions.out_file");

Path out_path = {
.path_string = opts.out_name,
.env = path::DEFAULT_ENV,
};
Path out_path = path::tnew(opts.out_name)!;

if (path::is_dir(out_path)) {
err::erro("The output name you've provided is an existing directory. Please provide a name of a file I should write to");
return bg::INVALID_ARGUMENTS~;
}

// If there is no parent, we are in root
Path out_parent = out_path.parent() ?? {
.path_string = "/",
.env = path::DEFAULT_ENV,
};

Path out_parent = out_path.parent() ?? path::tnew(string::tformat("%s", path::PREFERRED_SEPARATOR))!;
// Create missing directories on the path
path::mkdir(out_parent, recursive: true)!!;

Expand Down Expand Up @@ -88,12 +81,12 @@ fn void? translateHeader(
}

CXTranslationUnit tu;
CXErrorCode code = clang::parseTranslationUnit2(index, (ZString) header_name, cargs.ptr, cargs.len, null, 0, bgimpl::TRANSLATION_UNIT_PARSE_FLAGS, &tu);
CXErrorCode code = clang::parseTranslationUnit2(index, (ZString)header_name, cargs.ptr, cargs.len, null, 0, (uint)bgimpl::TRANSLATION_UNIT_PARSE_FLAGS, &tu);
checkCode(code)!;
defer clang::disposeTranslationUnit(tu);

// Print diagnostics
usz severe_count = runDiagnostics(tu, header_name, clang::DIAGNOSTIC_ERROR);
sz severe_count = runDiagnostics(tu, header_name, DIAGNOSTIC_ERROR);
if (severe_count > 0 && !opts.skip_errors) return bg::CLANG_PARSE_ERROR~;

GlobalVisitData visit_data = {
Expand All @@ -106,8 +99,9 @@ fn void? translateHeader(
.cxfile = clang::getFile(tu, (ZString)header_name),

.no_verbose = opts.no_verbose,
.use_docs = opts.generate_docs
};

// TODO: also free Strings under types_table
visit_data.types_table.init(mem);
defer visit_data.types_table.free();
Expand Down Expand Up @@ -140,15 +134,15 @@ fn void? translateHeader(
@param ignore_file_not_found : "Whether to ignore file not found error in diagnostics"
@return "Number of severe diagnostics"
*>
fn usz runDiagnostics(
fn sz runDiagnostics(
CXTranslationUnit tu,
String header_name,
CXDiagnosticSeverity min_severity,
bool ignore_file_not_found = true)
@inline
{
CUInt diagnostics_count = clang::getNumDiagnostics(tu);
usz severe_count;
sz severe_count;
for (CUInt i; i < diagnostics_count; ++i) {
CXDiagnostic diag = clang::getDiagnostic(tu, i);
defer clang::disposeDiagnostic(diag);
Expand Down Expand Up @@ -181,23 +175,23 @@ fn void? checkCode(
CXErrorCode code)
{
switch (code) {
case clang::ERROR_SUCCESS:
break;
case clang::ERROR_FAILURE:
case clang::ERROR_AST_READ_ERROR:
return bg::CLANG_PARSE_ERROR~;
case clang::ERROR_CRASHED:
return bg::CLANG_CRASH~;
case clang::ERROR_INVALID_ARGUMENTS:
return bg::INVALID_ARGUMENTS~;
case ERROR_SUCCESS:
break;
case ERROR_FAILURE:
case ERROR_AST_READ_ERROR:
return bg::CLANG_PARSE_ERROR~;
case ERROR_CRASHED:
return bg::CLANG_CRASH~;
case ERROR_INVALID_ARGUMENTS:
return bg::INVALID_ARGUMENTS~;
}
}


const TRANSLATION_UNIT_PARSE_FLAGS =
clang::TRANSLATION_UNIT_DETAILED_PREPROCESSING_RECORD
| clang::TRANSLATION_UNIT_IGNORE_NON_ERRORS_FROM_INCLUDED_FILES
| clang::TRANSLATION_UNIT_SKIP_FUNCTION_BODIES
| clang::TRANSLATION_UNIT_KEEP_GOING;
const CXTranslationUnit_Flags TRANSLATION_UNIT_PARSE_FLAGS =
CXTranslationUnit_Flags.TRANSLATION_UNIT_DETAILED_PREPROCESSING_RECORD
| CXTranslationUnit_Flags.TRANSLATION_UNIT_IGNORE_NON_ERRORS_FROM_INCLUDED_FILES
| CXTranslationUnit_Flags.TRANSLATION_UNIT_SKIP_FUNCTION_BODIES
| CXTranslationUnit_Flags.TRANSLATION_UNIT_KEEP_GOING;


40 changes: 20 additions & 20 deletions bindgen.c3l/src/bgstr.c3
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module bgimpl;
import bgimpl::err;
import std::collections::list;
import std::collections::map;
import std::ascii;
import std::core::ascii;


alias ListString = List{String};
Expand Down Expand Up @@ -153,7 +153,7 @@ fn String camelToPascal(
@export("bgstr_camel_to_pascal")
{
String res = str.copy(alloc);
usz i;
sz i;
while (i < str.len && str[i] == '_') ++i;
if (i < str.len) res[i] = str[i].to_upper();
return res;
Expand Down Expand Up @@ -193,7 +193,7 @@ fn String pascalToCamel(
@export("bgstr_pascal_to_camel")
{
String res = str.copy(alloc);
usz i;
sz i;
while (i < str.len && str[i] == '_') ++i;
if (i < str.len) res[i] = str[i].to_lower();
return res;
Expand Down Expand Up @@ -273,18 +273,18 @@ fn ListString parseSnakeCase(
tokens.init(alloc, str.len / 2);

// Treat possible beginning '_'
usz first = 0;
sz first = 0;
while (first < str.len && str[first] == '_') ++first;
if (first > 0) tokens.push(str[0..first-1]);

// Treat possible closing '_'
usz last = str.len - 1;
sz last = str.len - 1;
while (last > 0 && str[last] == '_') --last;

// Treat the rest [first..last]
for (usz i = first; i <= last;) {
for (sz i = first; i <= last;) {
while (i <= last && str[i] == '_') ++i;
usz begin = i;
sz begin = i;
while (i <= last && str[i] != '_') ++i;
tokens.push(str[begin..i-1]);
}
Expand Down Expand Up @@ -324,17 +324,17 @@ fn ListString parseCamelCase(
tokens.init(alloc, str.len / 2);

// Erase possible beginning '_'
isz first = 0;
sz first = 0;
while (first < str.len && str[first] == '_') ++first;
if (first > 0) tokens.push(str[0..first-1]);

// Erase possible closing '_'
isz last = str.len - 1;
sz last = str.len - 1;
while (last >= 0 && str[last] == '_') --last;

// Treat the rest [first..last]
for (usz i = first; i <= last;) {
usz begin = i++;
for (sz i = first; i <= last;) {
sz begin = i++;

// Handle several upper cases and digits in a row
bool is_prev_digit = false;
Expand Down Expand Up @@ -465,7 +465,7 @@ macro String convertToSnakeOrScreaming(
ListString* tokens,
bool $to_snake)
{
usz size = 0;
sz size = 0;
foreach (t : tokens) {
size += t.len;

Expand All @@ -476,11 +476,11 @@ macro String convertToSnakeOrScreaming(
--size;

// If last token is '_', we do not insert '_' after prelast token
usz last_token_index = tokens.len() - ((*tokens)[^1][0] == '_' ? 2 : 1);
sz last_token_index = tokens.len() - ((*tokens)[^1][0] == '_' ? 2 : 1);

String res = (String) allocator::alloc_array(alloc, char, size);
String res = (String)alloc::alloc_array(alloc, char, size);

usz last = 0;
sz last = 0;
foreach (i, t : tokens) {
res[last:t.len] = t[..];

Expand Down Expand Up @@ -515,22 +515,22 @@ macro String convertToCamelOrPascal(
ListString* tokens,
bool $to_camel)
{
usz size = 0;
sz size = 0;
foreach (t : tokens) {
size += t.len;
}

String res = (String) allocator::alloc_array(alloc, char, size);
String res = (String)alloc::alloc_array(alloc, char, size);

$if $to_camel:
bool has_underscore = (*tokens)[0][0] == '_';
$endif

usz last = 0;
sz last = 0;
foreach (i, t : *tokens) {
res[last:t.len] = t[..];

usz offset = 1;
sz offset = 1;
$if $to_camel:
if (i == (has_underscore ? 1 : 0)) offset = 0;
$endif
Expand All @@ -545,7 +545,7 @@ macro String convertToCamelOrPascal(
res[last] = res[last].to_upper();
$endif

for (usz j = last + offset; j < last + t.len; ++j) {
for (sz j = last + offset; j < last + t.len; ++j) {
res[j] = res[j].to_lower();
}

Expand Down
Loading