translate-c file emission
This commit is contained in:
@@ -17,6 +17,7 @@ import "./compiler/source"
|
||||
import "./compiler/symbol"
|
||||
import "./compiler/target"
|
||||
import "./compiler/token"
|
||||
import "./compiler/translatec"
|
||||
import "./compiler/types"
|
||||
import "core:fmt"
|
||||
import "core:mem"
|
||||
@@ -5769,3 +5770,83 @@ native_enums_compile_and_run_across_packages :: proc(t: ^testing.T) {
|
||||
state := run_executable(output)
|
||||
testing.expect_value(t, state.exit_code, 0)
|
||||
}
|
||||
|
||||
@(test)
|
||||
translate_c_emits_native_bindings_and_round_trips :: proc(t: ^testing.T) {
|
||||
result := cimport.init_result(context.allocator)
|
||||
|
||||
// type table: [0]=c_int, [1]=c_ulong, [2]=Record(Pair),
|
||||
// [3]=Function(c_int)->c_int, [4]=Pointer->Function (callback)
|
||||
append(&result.types, cimport.Type{kind = .C_Int, child = cimport.INVALID_TYPE})
|
||||
append(&result.types, cimport.Type{kind = .C_Ulong, child = cimport.INVALID_TYPE})
|
||||
append(&result.types, cimport.Type{kind = .Record, record = 0, child = cimport.INVALID_TYPE})
|
||||
func_params := []cimport.Type_Id{cimport.Type_Id(0)}
|
||||
append(&result.types, cimport.Type{kind = .Function, params = func_params, child = cimport.Type_Id(0)})
|
||||
append(&result.types, cimport.Type{kind = .Pointer, child = cimport.Type_Id(3)})
|
||||
|
||||
// record 0: Pair { left c_int; right c_int }
|
||||
pair_fields: [dynamic]cimport.Field
|
||||
append(&pair_fields, cimport.Field{name = "left", type = cimport.Type_Id(0)})
|
||||
append(&pair_fields, cimport.Field{name = "right", type = cimport.Type_Id(0)})
|
||||
append(&result.records, cimport.Record{name = "Pair", fields = pair_fields, kind = .Struct, complete = true})
|
||||
|
||||
// record 1: union Choice -> commented (no native spelling)
|
||||
choice_fields: [dynamic]cimport.Field
|
||||
append(&choice_fields, cimport.Field{name = "tag", type = cimport.Type_Id(0)})
|
||||
append(&result.records, cimport.Record{name = "Choice", fields = choice_fields, kind = .Union, complete = true})
|
||||
|
||||
// typedef aliases: a scalar and a callback function pointer
|
||||
append(&result.aliases, cimport.Alias{name = "Size", type = cimport.Type_Id(1)})
|
||||
append(&result.aliases, cimport.Alias{name = "Mapper", type = cimport.Type_Id(4)})
|
||||
|
||||
add_params := []cimport.Type_Id{cimport.Type_Id(0), cimport.Type_Id(0)}
|
||||
append(&result.functions, cimport.Function{name = "imported_add", params = add_params, result = cimport.Type_Id(0)})
|
||||
|
||||
append(&result.macros, cimport.Macro_Constant{
|
||||
name = "MAX_LEN",
|
||||
type = cimport.Type_Id(0),
|
||||
value = {kind = .Integer, type = cimport.Type_Id(0), integer = 256},
|
||||
})
|
||||
|
||||
// external variable -> commented (no native spelling)
|
||||
append(&result.variables, cimport.Variable{name = "some_global", type = cimport.Type_Id(0), mutable = true})
|
||||
|
||||
result.available = true
|
||||
|
||||
output := translatec.emit(&result, "test.h")
|
||||
defer delete(output)
|
||||
defer {
|
||||
delete(result.types)
|
||||
delete(result.records)
|
||||
delete(result.aliases)
|
||||
delete(result.functions)
|
||||
delete(result.variables)
|
||||
delete(result.macros)
|
||||
delete(pair_fields)
|
||||
delete(choice_fields)
|
||||
}
|
||||
|
||||
testing.expect(t, strings.contains(output, "Pair :: c_struct {"))
|
||||
testing.expect(t, strings.contains(output, "\tleft c_int"))
|
||||
testing.expect(t, strings.contains(output, "\tright c_int"))
|
||||
testing.expect(t, strings.contains(output, "Size :: alias c_ulong"))
|
||||
testing.expect(t, strings.contains(output, "Mapper :: alias ?*c_func(arg0 c_int) c_int"))
|
||||
testing.expect(t, strings.contains(output, "imported_add :: c_func(arg0 c_int, arg1 c_int) c_int"))
|
||||
testing.expect(t, strings.contains(output, "MAX_LEN c_int :: 256"))
|
||||
testing.expect(t, strings.contains(output, "# unsupported in bindings: C union 'Choice'"))
|
||||
testing.expect(t, strings.contains(output, "# unsupported in bindings: external variable 'some_global'"))
|
||||
|
||||
// Round-trip: the emitted source must lex + parse with zero diagnostics.
|
||||
// This guards render_type against drift from loader.translate_c_type and
|
||||
// exercises the new `alias` declaration syntax.
|
||||
source_file := source.Source{path = "bindings.bro", text = output}
|
||||
diagnostics := source.init_diagnostics(&source_file)
|
||||
defer source.destroy_diagnostics(&diagnostics)
|
||||
symbols := symbol.init_table()
|
||||
defer symbol.destroy_table(&symbols)
|
||||
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||
defer delete(stream.items)
|
||||
module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
defer ast.destroy_module(&module)
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user