// This file defines how to compile integers #include #include "../ast.h" #include "../environment.h" #include "../stdlib/datatypes.h" #include "../stdlib/integers.h" #include "../stdlib/text.h" #include "../stdlib/util.h" #include "../typecheck.h" #include "../types.h" #include "compilation.h" public Text_t compile_int_to_type(env_t *env, ast_t *ast, type_t *target) { if (ast->tag != Integer) { Text_t code = compile(env, ast); type_t *actual_type = get_type(env, ast); if (!promote(env, ast, &code, actual_type, target)) code_err(ast, "I couldn't promote this ", type_to_text(actual_type), " to a ", type_to_text(target)); return code; } if (non_optional(target)->tag == BigIntType) return compile(env, ast); if (target->tag == OptionalType && Match(target, OptionalType)->type) { return Texts("((", compile_type(target), "){.value=", compile_int_to_type(env, ast, Match(target, OptionalType)->type), ", .has_value=true})"); } OptionalInt_t int_val = Match(ast, Integer)->i; Text_t source = ast_source(ast); mpz_t i; mpz_init_set_int(i, int_val); char *c_literal; if (Text$has(source, Text("0x")) || Text$has(source, Text("0X")) || Text$has(source, Text("0b"))) { gmp_asprintf(&c_literal, "0x%ZX", i); } else if (Text$has(source, Text("0o"))) { gmp_asprintf(&c_literal, "%#Zo", i); } else { gmp_asprintf(&c_literal, "%#Zd", i); } if (target->tag == ByteType) { if (mpz_cmp_si(i, UINT8_MAX) <= 0 && mpz_cmp_si(i, 0) >= 0) return Texts("(Byte_t)(", c_literal, ")"); code_err(ast, "This integer cannot fit in a byte"); } else if (target->tag == FloatType) { if (Match(target, FloatType)->bits == TYPE_NBITS64) { return Texts("F64(", c_literal, ")"); } else { return Texts("F32(", c_literal, ")"); } } else if (target->tag == IntType) { int64_t target_bits = (int64_t)Match(target, IntType)->bits; switch (target_bits) { case TYPE_IBITS64: if (mpz_cmp_si(i, INT64_MIN) == 0) return Text("I64(INT64_MIN)"); if (mpz_cmp_si(i, INT64_MAX) <= 0 && mpz_cmp_si(i, INT64_MIN) >= 0) return Texts("I64(", c_literal, "L)"); break; case TYPE_IBITS32: if (mpz_cmp_si(i, INT32_MAX) <= 0 && mpz_cmp_si(i, INT32_MIN) >= 0) return Texts("I32(", c_literal, ")"); break; case TYPE_IBITS16: if (mpz_cmp_si(i, INT16_MAX) <= 0 && mpz_cmp_si(i, INT16_MIN) >= 0) return Texts("I16(", c_literal, ")"); break; case TYPE_IBITS8: if (mpz_cmp_si(i, INT8_MAX) <= 0 && mpz_cmp_si(i, INT8_MIN) >= 0) return Texts("I8(", c_literal, ")"); break; default: break; } code_err(ast, "This integer cannot fit in a ", target_bits, "-bit value"); } else { code_err(ast, "I don't know how to compile this to a ", type_to_text(target)); } return EMPTY_TEXT; } public Text_t compile_int(ast_t *ast) { Int_t int_val = Match(ast, Integer)->i; mpz_t i; mpz_init_set_int(i, int_val); OptionalText_t source = ast_source(ast); char *c_literal; if (Text$has(source, Text("0x")) || Text$has(source, Text("0X")) || Text$has(source, Text("0b"))) { gmp_asprintf(&c_literal, "0x%ZX", i); } else if (Text$has(source, Text("0o"))) { gmp_asprintf(&c_literal, "%#Zo", i); } else { gmp_asprintf(&c_literal, "%#Zd", i); } // TODO: preserve base if (mpz_cmpabs_ui(i, BIGGEST_SMALL_INT) <= 0) { return Texts("I_small(", c_literal, ")"); } else if (mpz_cmp_si(i, INT64_MAX) <= 0 && mpz_cmp_si(i, INT64_MIN) >= 0) { return Texts("Int$from_int64(", c_literal, ")"); } else if (source.tag == TEXT_NONE) { return Texts("Int$from_str(\"", int_val, "\")"); } else { source = Text$replace(source, Text("("), Text("")); source = Text$replace(source, Text(")"), Text("")); source = Text$replace(source, Text(" "), Text("")); source = Text$replace(source, Text("_"), Text("")); return Texts("Int$from_str(\"", source, "\")"); } }