mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-26 15:01:31 +00:00
Merge branch 'odin-lang:master' into master
This commit is contained in:
@@ -146,6 +146,7 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
|
||||
for i in 0..<count {
|
||||
write_rune(&builder, decoded[i])
|
||||
}
|
||||
prev = decoded[count - 1]
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,6 +134,7 @@ register_user_marshaler :: proc(id: typeid, marshaler: User_Marshaler) -> Regist
|
||||
return .None
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Marshal_Error) {
|
||||
b := strings.builder_make(allocator, loc)
|
||||
defer if err != nil {
|
||||
|
||||
68
core/encoding/json/match.odin
Normal file
68
core/encoding/json/match.odin
Normal file
@@ -0,0 +1,68 @@
|
||||
package encoding_json
|
||||
|
||||
Match_Key_Variant :: union #no_nil {
|
||||
int, // Index
|
||||
string, // Key
|
||||
}
|
||||
|
||||
Match_Error :: enum {
|
||||
None,
|
||||
Invalid_Argument,
|
||||
Invalid_Type_For_Index,
|
||||
Invalid_Type_For_Key,
|
||||
Key_Not_Found,
|
||||
Out_Of_Bounds_Index,
|
||||
}
|
||||
|
||||
Match_Flags :: distinct bit_set[Match_Flag]
|
||||
Match_Flag :: enum {
|
||||
Ignore_Key_Not_Found,
|
||||
Allow_String_Indexing_By_Byte,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
match :: proc(value: Value, args: ..Match_Key_Variant, flags: Match_Flags = nil) -> (found: Value, err: Match_Error) {
|
||||
found = value
|
||||
arg_loop: for arg in args {
|
||||
switch k in arg {
|
||||
case int:
|
||||
#partial switch v in found {
|
||||
case Array:
|
||||
if 0 <= k && k < len(v) {
|
||||
found = v[k]
|
||||
continue arg_loop
|
||||
}
|
||||
err = .Out_Of_Bounds_Index
|
||||
return
|
||||
case String:
|
||||
if .Allow_String_Indexing_By_Byte in flags {
|
||||
if 0 <= k && k < len(v) {
|
||||
found = Integer(v[k])
|
||||
continue arg_loop
|
||||
}
|
||||
err = .Out_Of_Bounds_Index
|
||||
return
|
||||
}
|
||||
}
|
||||
err = .Invalid_Type_For_Index
|
||||
return
|
||||
case string:
|
||||
v, ok := found.(Object)
|
||||
if !ok {
|
||||
err = .Invalid_Type_For_Key
|
||||
return
|
||||
}
|
||||
vfound, vok := v[k]
|
||||
if vok || (.Ignore_Key_Not_Found in flags) {
|
||||
found = vfound
|
||||
continue arg_loop
|
||||
}
|
||||
err = .Key_Not_Found
|
||||
return
|
||||
case:
|
||||
err = .Invalid_Argument
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -14,9 +14,16 @@ Parser :: struct {
|
||||
parse_integers: bool,
|
||||
}
|
||||
|
||||
make_parser :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> Parser {
|
||||
make_parser :: proc{
|
||||
make_parser_from_bytes,
|
||||
make_parser_from_string,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
make_parser_from_bytes :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> Parser {
|
||||
return make_parser_from_string(string(data), spec, parse_integers, allocator)
|
||||
}
|
||||
@(require_results)
|
||||
make_parser_from_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> Parser {
|
||||
p: Parser
|
||||
p.tok = make_tokenizer(data, spec, parse_integers)
|
||||
@@ -27,11 +34,18 @@ make_parser_from_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, par
|
||||
return p
|
||||
}
|
||||
|
||||
parse :: proc{
|
||||
parse_bytes,
|
||||
parse_string,
|
||||
}
|
||||
|
||||
parse :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) {
|
||||
|
||||
@(require_results)
|
||||
parse_bytes :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) {
|
||||
return parse_string(string(data), spec, parse_integers, allocator, loc)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) {
|
||||
context.allocator = allocator
|
||||
p := make_parser_from_string(data, spec, parse_integers, allocator)
|
||||
@@ -51,6 +65,7 @@ parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers
|
||||
return parse_object(&p, loc)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
token_end_pos :: proc(tok: Token) -> Pos {
|
||||
end := tok.pos
|
||||
end.offset += len(tok.text)
|
||||
@@ -65,6 +80,7 @@ advance_token :: proc(p: ^Parser) -> (Token, Error) {
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
allow_token :: proc(p: ^Parser, kind: Token_Kind) -> bool {
|
||||
if p.curr_token.kind == kind {
|
||||
advance_token(p)
|
||||
@@ -73,6 +89,7 @@ allow_token :: proc(p: ^Parser, kind: Token_Kind) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
expect_token :: proc(p: ^Parser, kind: Token_Kind) -> Error {
|
||||
prev := p.curr_token
|
||||
advance_token(p)
|
||||
@@ -83,6 +100,7 @@ expect_token :: proc(p: ^Parser, kind: Token_Kind) -> Error {
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
parse_colon :: proc(p: ^Parser) -> (err: Error) {
|
||||
colon_err := expect_token(p, .Colon)
|
||||
if colon_err == nil {
|
||||
@@ -91,6 +109,7 @@ parse_colon :: proc(p: ^Parser) -> (err: Error) {
|
||||
return .Expected_Colon_After_Key
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
parse_comma :: proc(p: ^Parser) -> (do_break: bool) {
|
||||
switch p.spec {
|
||||
case .JSON5, .MJSON:
|
||||
@@ -106,6 +125,7 @@ parse_comma :: proc(p: ^Parser) -> (do_break: bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
parse_value :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) {
|
||||
err = .None
|
||||
token := p.curr_token
|
||||
@@ -176,6 +196,7 @@ parse_value :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err:
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
parse_array :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) {
|
||||
err = .None
|
||||
expect_token(p, .Open_Bracket) or_return
|
||||
@@ -203,7 +224,7 @@ parse_array :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err:
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
bytes_make :: proc(size, alignment: int, allocator: mem.Allocator, loc := #caller_location) -> (bytes: []byte, err: Error) {
|
||||
b, berr := mem.alloc_bytes(size, alignment, allocator, loc)
|
||||
if berr != nil {
|
||||
@@ -217,6 +238,7 @@ bytes_make :: proc(size, alignment: int, allocator: mem.Allocator, loc := #calle
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
clone_string :: proc(s: string, allocator: mem.Allocator, loc := #caller_location) -> (str: string, err: Error) {
|
||||
n := len(s)
|
||||
b := bytes_make(n+1, 1, allocator, loc) or_return
|
||||
@@ -228,6 +250,7 @@ clone_string :: proc(s: string, allocator: mem.Allocator, loc := #caller_locatio
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
parse_object_key :: proc(p: ^Parser, key_allocator: mem.Allocator, loc := #caller_location) -> (key: string, err: Error) {
|
||||
tok := p.curr_token
|
||||
if p.spec != .JSON {
|
||||
@@ -242,6 +265,7 @@ parse_object_key :: proc(p: ^Parser, key_allocator: mem.Allocator, loc := #calle
|
||||
return unquote_string(tok, p.spec, key_allocator, loc)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
parse_object_body :: proc(p: ^Parser, end_token: Token_Kind, loc := #caller_location) -> (obj: Object, err: Error) {
|
||||
obj = make(Object, allocator=p.allocator, loc=loc)
|
||||
|
||||
@@ -282,6 +306,7 @@ parse_object_body :: proc(p: ^Parser, end_token: Token_Kind, loc := #caller_loca
|
||||
return obj, .None
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
parse_object :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) {
|
||||
expect_token(p, .Open_Brace) or_return
|
||||
obj := parse_object_body(p, .Close_Brace, loc) or_return
|
||||
@@ -291,6 +316,7 @@ parse_object :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err:
|
||||
|
||||
|
||||
// IMPORTANT NOTE(bill): unquote_string assumes a mostly valid string
|
||||
@(require_results)
|
||||
unquote_string :: proc(token: Token, spec: Specification, allocator := context.allocator, loc := #caller_location) -> (value: string, err: Error) {
|
||||
get_u2_rune :: proc(s: string) -> rune {
|
||||
if len(s) < 4 || s[0] != '\\' || s[1] != 'x' {
|
||||
|
||||
@@ -49,11 +49,12 @@ Tokenizer :: struct {
|
||||
curr_line_offset: int,
|
||||
spec: Specification,
|
||||
parse_integers: bool,
|
||||
insert_comma: bool,
|
||||
insert_comma: bool,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(require_results)
|
||||
make_tokenizer :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false) -> Tokenizer {
|
||||
t := Tokenizer{pos = {line=1}, data = data, spec = spec, parse_integers = parse_integers}
|
||||
next_rune(&t)
|
||||
@@ -78,6 +79,7 @@ next_rune :: proc(t: ^Tokenizer) -> rune #no_bounds_check {
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
skip_digits :: proc(t: ^Tokenizer) {
|
||||
for t.offset < len(t.data) {
|
||||
@@ -101,6 +103,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
scan_escape :: proc(t: ^Tokenizer) -> bool {
|
||||
switch t.r {
|
||||
case '"', '\'', '\\', '/', 'b', 'n', 'r', 't', 'f':
|
||||
@@ -125,7 +128,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
return false
|
||||
}
|
||||
|
||||
skip_whitespace :: proc(t: ^Tokenizer, on_newline: bool) -> rune {
|
||||
skip_whitespace :: proc(t: ^Tokenizer, on_newline: bool) {
|
||||
loop: for t.offset < len(t.data) {
|
||||
switch t.r {
|
||||
case ' ', '\t', '\v', '\f', '\r':
|
||||
@@ -149,7 +152,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
break loop
|
||||
}
|
||||
}
|
||||
return t.r
|
||||
return
|
||||
}
|
||||
|
||||
skip_to_next_line :: proc(t: ^Tokenizer) {
|
||||
@@ -310,7 +313,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
break
|
||||
}
|
||||
if r == '\\' {
|
||||
scan_escape(t)
|
||||
_ = scan_escape(t)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,10 +350,8 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
case '*':
|
||||
// None-nested multi-line comments
|
||||
for t.offset < len(t.data) {
|
||||
next_rune(t)
|
||||
if t.r == '*' {
|
||||
next_rune(t)
|
||||
if t.r == '/' {
|
||||
if next_rune(t) == '*' {
|
||||
if next_rune(t) == '/' {
|
||||
next_rune(t)
|
||||
return get_token(t)
|
||||
}
|
||||
@@ -385,6 +386,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
|
||||
|
||||
|
||||
@(require_results)
|
||||
is_valid_number :: proc(str: string, spec: Specification) -> bool {
|
||||
s := str
|
||||
if s == "" {
|
||||
@@ -473,6 +475,7 @@ is_valid_number :: proc(str: string, spec: Specification) -> bool {
|
||||
return s == ""
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_valid_string_literal :: proc(str: string, spec: Specification) -> bool {
|
||||
s := str
|
||||
if len(s) < 2 {
|
||||
|
||||
@@ -112,6 +112,7 @@ destroy_value :: proc(value: Value, allocator := context.allocator, loc := #call
|
||||
}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
clone_value :: proc(value: Value, allocator := context.allocator) -> Value {
|
||||
value := value
|
||||
context.allocator = allocator
|
||||
|
||||
@@ -635,7 +635,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
defer p.allocator = allocator
|
||||
p.allocator = mem.nil_allocator()
|
||||
|
||||
parse_value(p) or_return
|
||||
_ = parse_value(p) or_return
|
||||
if parse_comma(p) {
|
||||
break struct_loop
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package encoding_json
|
||||
import "core:mem"
|
||||
|
||||
// NOTE(bill): is_valid will not check for duplicate keys
|
||||
@(require_results)
|
||||
is_valid :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false) -> bool {
|
||||
p := make_parser(data, spec, parse_integers, mem.nil_allocator())
|
||||
|
||||
@@ -21,6 +22,7 @@ is_valid :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers :=
|
||||
return validate_object(&p)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
validate_object_key :: proc(p: ^Parser) -> bool {
|
||||
if p.spec != .JSON {
|
||||
if allow_token(p, .Ident) {
|
||||
@@ -31,6 +33,7 @@ validate_object_key :: proc(p: ^Parser) -> bool {
|
||||
return err == .None
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
validate_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> bool {
|
||||
for p.curr_token.kind != end_token {
|
||||
if !validate_object_key(p) {
|
||||
@@ -48,6 +51,7 @@ validate_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
validate_object :: proc(p: ^Parser) -> bool {
|
||||
if err := expect_token(p, .Open_Brace); err != .None {
|
||||
return false
|
||||
@@ -61,6 +65,7 @@ validate_object :: proc(p: ^Parser) -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
validate_array :: proc(p: ^Parser) -> bool {
|
||||
if err := expect_token(p, .Open_Bracket); err != .None {
|
||||
return false
|
||||
@@ -83,6 +88,7 @@ validate_array :: proc(p: ^Parser) -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
validate_value :: proc(p: ^Parser) -> bool {
|
||||
token := p.curr_token
|
||||
|
||||
|
||||
@@ -296,8 +296,7 @@ gb_internal void big_int_from_string(BigInt *dst, String const &s, bool *success
|
||||
|
||||
|
||||
gb_internal bool big_int_can_be_represented_in_64_bits(BigInt const *x) {
|
||||
int bits_used = (x->used-1) * MP_DIGIT_BIT;
|
||||
return bits_used <= 64;
|
||||
return mp_count_bits(x) <= 64;
|
||||
}
|
||||
|
||||
gb_internal u64 big_int_to_u64(BigInt const *x) {
|
||||
|
||||
@@ -3421,6 +3421,8 @@ gb_internal void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *nod
|
||||
x->expr = node;
|
||||
x->value = exact_value_shift(be->op.kind, exact_value_to_integer(x->value), exact_value_to_integer(y->value));
|
||||
|
||||
check_is_expressible(c, x, x->type);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5361,7 +5363,8 @@ gb_internal bool check_index_value(CheckerContext *c, Type *main_type, bool open
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
String idx_str = big_int_to_string(temporary_allocator(), &i);
|
||||
gbString expr_str = expr_to_string(operand.expr, temporary_allocator());
|
||||
error(operand.expr, "Index '%s' is out of bounds range 0..<%lld, got %.*s", expr_str, max_count, LIT(idx_str));
|
||||
char range_type = open_range ? '=' : '<';
|
||||
error(operand.expr, "Index '%s' is out of bounds range 0..%c%lld, got %.*s", expr_str, range_type, max_count, LIT(idx_str));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -6894,7 +6894,10 @@ gb_internal void check_deferred_procedures(Checker *c) {
|
||||
continue;
|
||||
}
|
||||
|
||||
GB_ASSERT(is_type_proc(src->type));
|
||||
if (!is_type_proc(src->type)) {
|
||||
error(src->token, "Invalid procedure type found during deferred procedure checking");
|
||||
continue;
|
||||
}
|
||||
GB_ASSERT(is_type_proc(dst->type));
|
||||
Type *src_params = base_type(src->type)->Proc.params;
|
||||
Type *src_results = base_type(src->type)->Proc.results;
|
||||
|
||||
@@ -32,6 +32,22 @@ enum ExactValueKind {
|
||||
ExactValue_Count,
|
||||
};
|
||||
|
||||
gb_global char const *exact_value_kind_string[ExactValue_Count] = {
|
||||
"Invalid",
|
||||
|
||||
"Bool",
|
||||
"String",
|
||||
"Integer",
|
||||
"Float",
|
||||
"Complex",
|
||||
"Quaternion",
|
||||
"Pointer",
|
||||
"Compound",
|
||||
"Procedure",
|
||||
"Typeid",
|
||||
"String16",
|
||||
};
|
||||
|
||||
struct ExactValue {
|
||||
ExactValueKind kind;
|
||||
union {
|
||||
|
||||
@@ -595,7 +595,7 @@ gb_internal lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, As
|
||||
gb_internal lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *false_block);
|
||||
|
||||
gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValueRef *values, isize value_count_);
|
||||
gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_);
|
||||
gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_, bool force_non_named=false);
|
||||
gb_internal void lb_set_entity_from_other_modules_linkage_correctly(lbModule *other_module, Entity *e, String const &name);
|
||||
|
||||
gb_internal lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t);
|
||||
|
||||
@@ -99,23 +99,47 @@ gb_internal LLVMValueRef llvm_const_cast(lbModule *m, LLVMValueRef val, LLVMType
|
||||
return LLVMConstNull(dst);
|
||||
}
|
||||
|
||||
GB_ASSERT_MSG(lb_sizeof(dst) == lb_sizeof(src), "%s vs %s", LLVMPrintTypeToString(dst), LLVMPrintTypeToString(src));
|
||||
LLVMTypeKind kind = LLVMGetTypeKind(dst);
|
||||
switch (kind) {
|
||||
case LLVMPointerTypeKind: {
|
||||
GB_ASSERT_MSG(lb_sizeof(dst) == lb_sizeof(src), "dst:%s vs src:%s (dst:%lld vs src:%lld)", LLVMPrintTypeToString(dst), LLVMPrintTypeToString(src),
|
||||
cast(long long)lb_sizeof(dst),
|
||||
cast(long long)lb_sizeof(src));
|
||||
|
||||
return LLVMConstPointerCast(val, dst);
|
||||
}
|
||||
case LLVMStructTypeKind: {
|
||||
GB_ASSERT_MSG(lb_sizeof(dst) == lb_sizeof(src), "dst:%s vs src:%s (dst:%lld vs src:%lld)", LLVMPrintTypeToString(dst), LLVMPrintTypeToString(src),
|
||||
cast(long long)lb_sizeof(dst),
|
||||
cast(long long)lb_sizeof(src));
|
||||
|
||||
unsigned src_n = LLVMCountStructElementTypes(src);
|
||||
unsigned dst_n = LLVMCountStructElementTypes(dst);
|
||||
if (src_n != dst_n) goto failure;
|
||||
|
||||
if (LLVM_VERSION_MAJOR > 14) {
|
||||
goto failure;
|
||||
}
|
||||
|
||||
LLVMValueRef *field_vals = temporary_alloc_array<LLVMValueRef>(dst_n);
|
||||
for (unsigned i = 0; i < dst_n; i++) {
|
||||
LLVMValueRef field_val = llvm_const_extract_value(m, val, i);
|
||||
if (field_val == nullptr) goto failure;
|
||||
|
||||
LLVMTypeRef dst_elem_ty = LLVMStructGetTypeAtIndex(dst, i);
|
||||
LLVMTypeRef src_elem_ty = LLVMTypeOf(field_val);
|
||||
|
||||
if (lb_sizeof(dst_elem_ty) != lb_sizeof(src_elem_ty)) {
|
||||
goto failure;
|
||||
}
|
||||
|
||||
GB_ASSERT_MSG(lb_sizeof(dst_elem_ty) == lb_sizeof(src_elem_ty), "dst:%s vs src:%s (dst:%lld vs src:%lld) to %s from %s", LLVMPrintTypeToString(dst_elem_ty), LLVMPrintTypeToString(src_elem_ty),
|
||||
cast(long long)lb_sizeof(dst_elem_ty),
|
||||
cast(long long)lb_sizeof(src_elem_ty),
|
||||
LLVMPrintTypeToString(dst),
|
||||
LLVMPrintTypeToString(src)
|
||||
);
|
||||
|
||||
field_vals[i] = llvm_const_cast(m, field_val, dst_elem_ty, failure_);
|
||||
if (failure_ && *failure_) goto failure;
|
||||
}
|
||||
@@ -126,6 +150,9 @@ gb_internal LLVMValueRef llvm_const_cast(lbModule *m, LLVMValueRef val, LLVMType
|
||||
return LLVMConstStructInContext(m->ctx, field_vals, dst_n, LLVMIsPackedStruct(dst));
|
||||
}
|
||||
}
|
||||
case LLVMArrayTypeKind: {
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
|
||||
failure:
|
||||
@@ -212,10 +239,15 @@ gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValue
|
||||
return llvm_const_named_struct_internal(m, struct_type, values_with_padding, values_with_padding_count);
|
||||
}
|
||||
|
||||
gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_) {
|
||||
gb_internal LLVMValueRef llvm_const_named_struct_internal(lbModule *m, LLVMTypeRef t, LLVMValueRef *values, isize value_count_, bool force_non_named) {
|
||||
unsigned value_count = cast(unsigned)value_count_;
|
||||
unsigned elem_count = LLVMCountStructElementTypes(t);
|
||||
GB_ASSERT_MSG(value_count == elem_count, "%s %u %u", LLVMPrintTypeToString(t), value_count, elem_count);
|
||||
|
||||
if (force_non_named) {
|
||||
return LLVMConstStructInContext(m->ctx, values, value_count, true);
|
||||
}
|
||||
|
||||
bool failure = false;
|
||||
for (unsigned i = 0; i < elem_count; i++) {
|
||||
LLVMTypeRef elem_type = LLVMStructGetTypeAtIndex(t, i);
|
||||
@@ -911,6 +943,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
|
||||
|
||||
bool is_local = cc.allow_local && m->curr_procedure != nullptr;
|
||||
|
||||
|
||||
if (is_type_union(type) && is_type_union_constantable(type)) {
|
||||
Type *bt = base_type(type);
|
||||
GB_ASSERT(bt->kind == Type_Union);
|
||||
@@ -945,21 +978,26 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
|
||||
res.type = original_type;
|
||||
return res;
|
||||
} else {
|
||||
LLVMValueRef values[4] = {};
|
||||
isize value_count = 0;
|
||||
|
||||
// Payload
|
||||
values[value_count++] = cv.value;
|
||||
|
||||
unsigned tag_value = 1;
|
||||
if (bt->Union.kind == UnionType_no_nil) {
|
||||
tag_value = 0;
|
||||
}
|
||||
LLVMValueRef tag = LLVMConstInt(LLVMStructGetTypeAtIndex(llvm_type, 1), tag_value, false);
|
||||
LLVMValueRef padding = nullptr;
|
||||
|
||||
isize value_count = 2;
|
||||
// Tag
|
||||
values[value_count++] = LLVMConstInt(LLVMStructGetTypeAtIndex(llvm_type, 1), tag_value, false);;
|
||||
|
||||
if (LLVMCountStructElementTypes(llvm_type) > 2) {
|
||||
value_count = 3;
|
||||
padding = LLVMConstNull(LLVMStructGetTypeAtIndex(llvm_type, 2));
|
||||
GB_ASSERT(LLVMCountStructElementTypes(llvm_type) == 3);
|
||||
// Padding
|
||||
values[value_count++] = LLVMConstNull(LLVMStructGetTypeAtIndex(llvm_type, 2));
|
||||
}
|
||||
|
||||
LLVMValueRef values[3] = {cv.value, tag, padding};
|
||||
res.value = llvm_const_named_struct_internal(m, llvm_type, values, value_count);
|
||||
res.type = original_type;
|
||||
return res;
|
||||
@@ -971,6 +1009,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
|
||||
if (cl->elems.count == 0) {
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
value_type = type_of_expr(value.value_compound);
|
||||
} else if (value.kind == ExactValue_Invalid) {
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
@@ -982,18 +1021,31 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
|
||||
|
||||
i64 block_size = bt->Union.variant_block_size;
|
||||
|
||||
if (are_types_identical(value_type, original_type)) {
|
||||
while (are_types_identical(value_type, original_type)) {
|
||||
if (value.kind == ExactValue_Compound) {
|
||||
ast_node(cl, CompoundLit, value.value_compound);
|
||||
if (cl->elems.count == 0) {
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
|
||||
value_type = type_of_expr(value.value_compound);
|
||||
if (!are_types_identical(value_type, original_type)) {
|
||||
break;
|
||||
}
|
||||
|
||||
GB_PANIC("%s --> %s vs %s",
|
||||
expr_to_string(value.value_compound),
|
||||
temp_canonical_string(value_type), temp_canonical_string(original_type));
|
||||
|
||||
} else if (value.kind == ExactValue_Invalid) {
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
|
||||
GB_PANIC("%s vs %s", type_to_string(value_type), type_to_string(original_type));
|
||||
GB_PANIC("(value.kind=%s) %s vs %s",
|
||||
exact_value_kind_string[value.kind],
|
||||
temp_canonical_string(value_type), temp_canonical_string(original_type));
|
||||
}
|
||||
// union_multiple_allow_compound:;
|
||||
|
||||
lbValue cv = lb_const_value(m, value_type, value, value_type, cc);
|
||||
Type *variant_type = cv.type;
|
||||
@@ -1001,16 +1053,16 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
|
||||
LLVMValueRef values[4] = {};
|
||||
unsigned value_count = 0;
|
||||
|
||||
#if LLVM_VERSION_MAJOR == 14
|
||||
#if LLVM_VERSION_MAJOR == 14
|
||||
LLVMTypeRef block_type = lb_type_internal_union_block_type(m, bt);
|
||||
values[value_count++] = llvm_const_pad_to_size(m, cv.value, block_type);
|
||||
#else
|
||||
#else
|
||||
values[value_count++] = cv.value;
|
||||
if (type_size_of(variant_type) != block_size) {
|
||||
if (block_size != type_size_of(variant_type)) {
|
||||
LLVMTypeRef padding_type = lb_type_padding_filler(m, block_size - type_size_of(variant_type), 1);
|
||||
values[value_count++] = LLVMConstNull(padding_type);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Type *tag_type = union_tag_type(bt);
|
||||
LLVMTypeRef llvm_tag_type = lb_type(m, tag_type);
|
||||
@@ -1026,6 +1078,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
|
||||
}
|
||||
|
||||
res.value = LLVMConstStructInContext(m->ctx, values, value_count, true);
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -2031,6 +2084,8 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
|
||||
|
||||
LLVMTypeRef struct_type = lb_type(m, original_type);
|
||||
|
||||
bool force_non_named = false;
|
||||
|
||||
auto field_remapping = lb_get_struct_remapping(m, type);
|
||||
unsigned value_count = LLVMCountStructElementTypes(struct_type);
|
||||
|
||||
@@ -2067,7 +2122,6 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
|
||||
visited[index] = true;
|
||||
}
|
||||
|
||||
|
||||
unsigned idx_list_len = cast(unsigned)sel.index.count-1;
|
||||
unsigned *idx_list = gb_alloc_array(temporary_allocator(), unsigned, idx_list_len);
|
||||
|
||||
@@ -2103,9 +2157,14 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
|
||||
}
|
||||
}
|
||||
if (is_constant) {
|
||||
LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, tav.type, cc).value;
|
||||
LLVMValueRef elem_value = lb_const_value(m, cv_type, tav.value, tav.type, cc).value;
|
||||
if (LLVMIsConstant(elem_value) && LLVMIsConstant(values[index])) {
|
||||
values[index] = llvm_const_insert_value(m, values[index], elem_value, idx_list, idx_list_len);
|
||||
if (is_type_union(cv_type) || is_type_raw_union(cv_type)) {
|
||||
force_non_named = true;
|
||||
values[index] = llvm_const_insert_value_with_rebuild(m, values[index], elem_value, idx_list, idx_list_len);
|
||||
} else {
|
||||
values[index] = llvm_const_insert_value(m, values[index], elem_value, idx_list, idx_list_len);
|
||||
}
|
||||
} else if (is_local) {
|
||||
#if 1
|
||||
lbProcedure *p = m->curr_procedure;
|
||||
@@ -2152,13 +2211,6 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
|
||||
Entity *f = type->Struct.fields[i+multiple_return_offset];
|
||||
TypeAndValue tav = cl->elems[i]->tav;
|
||||
|
||||
// INFO: dead code:
|
||||
|
||||
// ExactValue val = {};
|
||||
// if (tav.mode != Addressing_Invalid) {
|
||||
// val = tav.value;
|
||||
// }
|
||||
|
||||
if (is_type_tuple(tav.type)){
|
||||
multiple_return_offset += tav.type->Tuple.variables.count-1;
|
||||
}
|
||||
@@ -2198,7 +2250,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty
|
||||
}
|
||||
|
||||
if (is_constant) {
|
||||
res.value = llvm_const_named_struct_internal(m, struct_type, values, cast(unsigned)value_count);
|
||||
res.value = llvm_const_named_struct_internal(m, struct_type, values, cast(unsigned)value_count, force_non_named);
|
||||
LLVMTypeRef res_type = LLVMTypeOf(res.value);
|
||||
GB_ASSERT(lb_sizeof(res_type) == lb_sizeof(struct_type));
|
||||
return res;
|
||||
|
||||
@@ -461,6 +461,27 @@ gb_internal LLVMValueRef llvm_const_insert_value(lbModule *m, LLVMValueRef agg,
|
||||
return extracted_value;
|
||||
}
|
||||
|
||||
gb_internal LLVMValueRef llvm_const_insert_value_with_rebuild(lbModule *m, LLVMValueRef agg, LLVMValueRef val, unsigned *indices, isize count) {
|
||||
GB_ASSERT(LLVMIsConstant(agg));
|
||||
GB_ASSERT(LLVMIsConstant(val));
|
||||
GB_ASSERT(count > 0);
|
||||
|
||||
unsigned value_count = LLVMCountStructElementTypes(LLVMTypeOf(agg));
|
||||
LLVMValueRef *values = gb_alloc_array(heap_allocator(), LLVMValueRef, count);
|
||||
defer (gb_free(heap_allocator(), values));
|
||||
|
||||
for (unsigned i = 0; i < value_count; i++) {
|
||||
values[i] = llvm_const_extract_value(m, agg, i);
|
||||
}
|
||||
if (count == 1) {
|
||||
values[indices[0]] = val;
|
||||
} else {
|
||||
values[indices[0]] = llvm_const_insert_value_with_rebuild(m, values[indices[0]], val, indices+1, count-1);
|
||||
}
|
||||
|
||||
return LLVMConstStructInContext(m->ctx, values, value_count, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1647,12 +1668,14 @@ gb_internal lbValue lb_emit_union_tag_ptr(lbProcedure *p, lbValue u) {
|
||||
unsigned element_count = LLVMCountStructElementTypes(uvt);
|
||||
GB_ASSERT_MSG(element_count >= 2, "element_count=%u (%s) != (%s)", element_count, type_to_string(ut), LLVMPrintTypeToString(uvt));
|
||||
|
||||
|
||||
LLVMValueRef ptr = u.value;
|
||||
ptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(uvt, 0), "");
|
||||
|
||||
lbValue tag_ptr = {};
|
||||
tag_ptr.value = LLVMBuildStructGEP2(p->builder, uvt, ptr, 1, "");
|
||||
tag_ptr.type = alloc_type_pointer(tag_type);
|
||||
tag_ptr.value = LLVMBuildPointerCast(p->builder, tag_ptr.value, lb_type(p->module, tag_ptr.type), "");
|
||||
return tag_ptr;
|
||||
}
|
||||
|
||||
@@ -2402,8 +2425,9 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
i64 full_type_align = type_align_of(type);
|
||||
GB_ASSERT(full_type_size % full_type_align == 0);
|
||||
|
||||
if (type->Struct.is_raw_union) {
|
||||
bool requires_packing = type->Struct.is_packed;
|
||||
|
||||
if (type->Struct.is_raw_union) {
|
||||
lbStructFieldRemapping field_remapping = {};
|
||||
slice_init(&field_remapping, permanent_allocator(), 1);
|
||||
|
||||
@@ -2411,7 +2435,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
fields[0] = lb_type_padding_filler(m, full_type_size, full_type_align);
|
||||
field_remapping[0] = 0;
|
||||
|
||||
LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), false);
|
||||
LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), requires_packing);
|
||||
map_set(&m->struct_field_remapping, cast(void *)struct_type, field_remapping);
|
||||
map_set(&m->struct_field_remapping, cast(void *)type, field_remapping);
|
||||
return struct_type;
|
||||
@@ -2431,7 +2455,6 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
}
|
||||
|
||||
i64 prev_offset = 0;
|
||||
bool requires_packing = type->Struct.is_packed;
|
||||
for (i32 field_index : struct_fields_index_by_increasing_offset(temporary_allocator(), type)) {
|
||||
Entity *field = type->Struct.fields[field_index];
|
||||
i64 offset = type->Struct.offsets[field_index];
|
||||
@@ -2497,8 +2520,9 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
LLVMTypeRef fields[] = {lb_type(m, type->Union.variants[0])};
|
||||
return LLVMStructTypeInContext(ctx, fields, gb_count_of(fields), false);
|
||||
}
|
||||
bool is_packed = false;
|
||||
|
||||
auto fields = array_make<LLVMTypeRef>(temporary_allocator(), 0, 3);
|
||||
auto fields = array_make<LLVMTypeRef>(temporary_allocator(), 0, 4);
|
||||
if (is_type_union_maybe_pointer(type)) {
|
||||
LLVMTypeRef variant = lb_type(m, type->Union.variants[0]);
|
||||
array_add(&fields, variant);
|
||||
@@ -2514,6 +2538,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, align);
|
||||
array_add(&fields, padding_type);
|
||||
}
|
||||
is_packed = true;
|
||||
} else {
|
||||
LLVMTypeRef block_type = lb_type_internal_union_block_type(m, type);
|
||||
|
||||
@@ -2526,9 +2551,10 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, align);
|
||||
array_add(&fields, padding_type);
|
||||
}
|
||||
is_packed = true;
|
||||
}
|
||||
|
||||
return LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, false);
|
||||
return LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, is_packed);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -2639,6 +2665,13 @@ gb_internal LLVMTypeRef lb_type(lbModule *m, Type *type) {
|
||||
m->internal_type_level += 1;
|
||||
llvm_type = lb_type_internal(m, type);
|
||||
m->internal_type_level -= 1;
|
||||
|
||||
// {
|
||||
// i64 tsz = type_size_of(type);
|
||||
// i64 lsz = lb_sizeof(llvm_type);
|
||||
// GB_ASSERT_MSG(tsz == lsz, "%s %lld vs %lld %s", type_to_string(type), cast(long long)tsz, cast(long long)lsz, LLVMPrintTypeToString(llvm_type));
|
||||
// }
|
||||
|
||||
if (m->internal_type_level == 0) {
|
||||
map_set(&m->types, type, llvm_type);
|
||||
}
|
||||
|
||||
@@ -4762,14 +4762,22 @@ gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr, lbValue *sret_
|
||||
return res;
|
||||
}
|
||||
|
||||
gb_internal void lb_add_values_to_array(lbProcedure *p, Array<lbValue> *args, lbValue value) {
|
||||
gb_internal void lb_add_values_to_array(lbProcedure *p, Array<lbValue> *args, lbValue value, Type *c_vararg_type = nullptr) {
|
||||
if (is_type_tuple(value.type)) {
|
||||
for_array(i, value.type->Tuple.variables) {
|
||||
lbValue sub_value = lb_emit_struct_ev(p, value, cast(i32)i);
|
||||
array_add(args, sub_value);
|
||||
if (c_vararg_type) {
|
||||
array_add(args, lb_emit_c_vararg(p, sub_value, c_vararg_type));
|
||||
} else {
|
||||
array_add(args, sub_value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
array_add(args, value);
|
||||
if (c_vararg_type) {
|
||||
array_add(args, lb_emit_c_vararg(p, value, c_vararg_type));
|
||||
} else {
|
||||
array_add(args, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4890,9 +4898,9 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr, lbVal
|
||||
if (is_type_untyped_nil(arg.type)) {
|
||||
arg = lb_const_nil(p->module, t_rawptr);
|
||||
}
|
||||
array_add(&args, lb_emit_c_vararg(p, arg, arg.type));
|
||||
lb_add_values_to_array(p, &args, arg, arg.type);
|
||||
} else {
|
||||
array_add(&args, lb_emit_c_vararg(p, arg, elem_type));
|
||||
lb_add_values_to_array(p, &args, arg, elem_type);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -5018,15 +5026,15 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr, lbVal
|
||||
if (is_type_untyped_nil(arg.type)) {
|
||||
arg = lb_const_nil(p->module, t_rawptr);
|
||||
}
|
||||
array_add(&args, lb_emit_c_vararg(p, arg, arg.type));
|
||||
lb_add_values_to_array(p, &args, arg, arg.type);
|
||||
} else {
|
||||
array_add(&args, lb_emit_c_vararg(p, arg, elem_type));
|
||||
lb_add_values_to_array(p, &args, arg, elem_type);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
lbValue value = lb_build_expr(p, fv->value);
|
||||
GB_ASSERT(!is_type_tuple(value.type));
|
||||
array_add(&args, lb_emit_c_vararg(p, value, value.type));
|
||||
lb_add_values_to_array(p, &args, value, value.type);
|
||||
}
|
||||
} else {
|
||||
lbValue value = lb_build_expr(p, fv->value);
|
||||
|
||||
@@ -761,17 +761,27 @@ gb_internal void find_visual_studio_paths_from_env_vars(Find_Result *result) {
|
||||
gb_internal Find_Result find_visual_studio_and_windows_sdk() {
|
||||
Find_Result r = {};
|
||||
find_windows_kit_paths(&r);
|
||||
find_visual_studio_by_fighting_through_microsoft_craziness(&r);
|
||||
|
||||
// Prefer the toolset exported into the environment (e.g. by vsdevcmd) so the
|
||||
// linker uses the same MSVC toolset of other externally compiled C++ dependencies.
|
||||
// The COM autodetection grabs the first installed Visual Studio, which may be an older
|
||||
// toolset whose runtime libraries lack symbols that the newer-built libraries
|
||||
// reference. Fall back to autodetection when the environment is bare.
|
||||
find_visual_studio_paths_from_env_vars(&r);
|
||||
|
||||
bool vs_found =
|
||||
r.vs_exe_path.len &&
|
||||
r.vs_library_path.len ;
|
||||
|
||||
if (!vs_found) {
|
||||
find_visual_studio_by_fighting_through_microsoft_craziness(&r);
|
||||
}
|
||||
|
||||
bool sdk_found =
|
||||
r.windows_sdk_bin_path.len &&
|
||||
r.windows_sdk_um_library_path.len &&
|
||||
r.windows_sdk_ucrt_library_path.len ;
|
||||
|
||||
bool vs_found =
|
||||
r.vs_exe_path.len &&
|
||||
r.vs_library_path.len ;
|
||||
|
||||
if (!sdk_found) {
|
||||
find_windows_kit_paths_from_env_vars(&r);
|
||||
}
|
||||
|
||||
@@ -1471,6 +1471,10 @@ gb_internal bool is_type_constant_type(Type *t) {
|
||||
return is_type_constant_type(t->Array.elem);
|
||||
case Type_EnumeratedArray:
|
||||
return is_type_constant_type(t->EnumeratedArray.elem);
|
||||
case Type_SimdVector:
|
||||
return is_type_constant_type(t->SimdVector.elem);
|
||||
case Type_Matrix:
|
||||
return is_type_constant_type(t->Matrix.elem);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -2699,8 +2703,6 @@ gb_internal bool is_type_union_constantable(Type *type) {
|
||||
|
||||
if (bt->Union.variants.count == 0) {
|
||||
return true;
|
||||
} else if (bt->Union.variants.count == 1) {
|
||||
return is_type_constant_type(bt->Union.variants[0]);
|
||||
}
|
||||
|
||||
for (Type *v : bt->Union.variants) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package test_core_xml
|
||||
|
||||
import "core:encoding/entity"
|
||||
import "core:encoding/xml"
|
||||
import "core:testing"
|
||||
import "core:strings"
|
||||
@@ -217,6 +218,29 @@ run_test :: proc(t: ^testing.T, test: TEST, loc := #caller_location) {
|
||||
}
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_normalize_whitespace :: proc(t: ^testing.T) {
|
||||
s := "A & B"
|
||||
normalized_entity_decode, _ := entity.decode_xml(s, {.Normalize_Whitespace})
|
||||
defer delete(normalized_entity_decode)
|
||||
|
||||
testing.expect_value(t, normalized_entity_decode, "A & B")
|
||||
|
||||
s = `<hellope attr="A & B">A & B</hellope>`
|
||||
|
||||
opts := xml.Options{
|
||||
flags = {.Ignore_Unsupported, .Decode_SGML_Entities},
|
||||
}
|
||||
|
||||
doc, err := xml.parse_bytes(transmute([]byte)s, opts)
|
||||
defer xml.destroy(doc)
|
||||
assert(err == .None)
|
||||
|
||||
testing.expect_value(t, doc.elements[0].value[0], "A & B")
|
||||
attr := doc.elements[0].attribs
|
||||
testing.expect_value(t, attr[0].val, "A & B")
|
||||
}
|
||||
|
||||
@(private)
|
||||
doc_to_string :: proc(doc: ^xml.Document) -> (result: string) {
|
||||
/*
|
||||
@@ -298,4 +322,4 @@ doc_to_string :: proc(doc: ^xml.Document) -> (result: string) {
|
||||
|
||||
print(strings.to_writer(&buf), doc)
|
||||
return strings.clone(strings.to_string(buf))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user