Add a Byte datatype

This commit is contained in:
Bruce Hill 2024-09-15 15:33:47 -04:00
parent 259c7efcf8
commit e422079fcc
26 changed files with 368 additions and 122 deletions

View File

@ -28,7 +28,7 @@ O=-Og
CFLAGS=$(CCONFIG) $(EXTRA) $(CWARN) $(G) $(O) $(OSFLAGS)
CFLAGS_PLACEHOLDER="$$(echo -e '\033[2m<flags...>\033[m')"
LDLIBS=-lgc -lcord -lm -lunistring -lgmp -ldl
BUILTIN_OBJS=stdlib/siphash.o stdlib/arrays.o stdlib/bools.o stdlib/channels.o stdlib/nums.o stdlib/integers.o \
BUILTIN_OBJS=stdlib/siphash.o stdlib/arrays.o stdlib/bools.o stdlib/bytes.o stdlib/channels.o stdlib/nums.o stdlib/integers.o \
stdlib/pointers.o stdlib/memory.o stdlib/text.o stdlib/threads.o stdlib/c_strings.o stdlib/tables.o \
stdlib/types.o stdlib/util.o stdlib/files.o stdlib/ranges.o stdlib/shell.o stdlib/paths.o \
stdlib/optionals.o stdlib/patterns.o stdlib/metamethods.o stdlib/functiontype.o stdlib/stdlib.o

2
ast.h
View File

@ -149,7 +149,7 @@ struct ast_s {
} Var;
struct {
const char *str;
enum { IBITS_UNSPECIFIED=0, IBITS8=8, IBITS16=16, IBITS32=32, IBITS64=64 } bits;
enum { IBITS_UNSPECIFIED=0, IBITS_BYTE=7, IBITS8=8, IBITS16=16, IBITS32=32, IBITS64=64 } bits;
} Int;
struct {
double n;

View File

@ -41,6 +41,8 @@ CORD promote_to_optional(type_t *t, CORD code)
case TYPE_IBITS64: return CORD_all("((OptionalInt64_t){", code, "})");
default: errx(1, "Unsupported in type: %T", t);
}
} else if (t->tag == ByteType) {
return CORD_all("((OptionalByte_t){", code, "})");
} else if (t->tag == StructType) {
return CORD_all("((", compile_type(Type(OptionalType, .type=t)), "){", code, "})");
} else {
@ -202,6 +204,7 @@ CORD compile_type(type_t *t)
case VoidType: return "void";
case MemoryType: return "void";
case BoolType: return "Bool_t";
case ByteType: return "Byte_t";
case CStringType: return "char*";
case BigIntType: return "Int_t";
case IntType: return CORD_asprintf("Int%ld_t", Match(t, IntType)->bits);
@ -239,6 +242,7 @@ CORD compile_type(type_t *t)
type_t *nonnull = Match(t, OptionalType)->type;
switch (nonnull->tag) {
case BoolType: return "OptionalBool_t";
case ByteType: return "OptionalByte_t";
case CStringType: case BigIntType: case NumType: case TextType:
case ArrayType: case SetType: case TableType: case FunctionType: case ClosureType:
case PointerType: case EnumType: case ChannelType:
@ -366,9 +370,7 @@ static CORD check_null(type_t *t, CORD value)
return CORD_all("((", value, ") == NULL_BOOL)");
else if (t->tag == TextType)
return CORD_all("((", value, ").length < 0)");
else if (t->tag == IntType)
return CORD_all("(", value, ").is_null");
else if (t->tag == StructType)
else if (t->tag == IntType || t->tag == ByteType || t->tag == StructType)
return CORD_all("(", value, ").is_null");
else if (t->tag == EnumType)
return CORD_all("((", value, ").tag == 0)");
@ -635,27 +637,27 @@ CORD compile_statement(env_t *env, ast_t *ast)
switch (update->op) {
case BINOP_MULT:
if (lhs_t->tag != IntType && lhs_t->tag != NumType)
if (lhs_t->tag != IntType && lhs_t->tag != NumType && lhs_t->tag != ByteType)
code_err(ast, "I can't do a multiply assignment with this operator between %T and %T", lhs_t, rhs_t);
return CORD_all(lhs, " *= ", rhs, ";");
case BINOP_DIVIDE:
if (lhs_t->tag != IntType && lhs_t->tag != NumType)
if (lhs_t->tag != IntType && lhs_t->tag != NumType && lhs_t->tag != ByteType)
code_err(ast, "I can't do a divide assignment with this operator between %T and %T", lhs_t, rhs_t);
return CORD_all(lhs, " /= ", rhs, ";");
case BINOP_MOD:
if (lhs_t->tag != IntType && lhs_t->tag != NumType)
if (lhs_t->tag != IntType && lhs_t->tag != NumType && lhs_t->tag != ByteType)
code_err(ast, "I can't do a mod assignment with this operator between %T and %T", lhs_t, rhs_t);
return CORD_all(lhs, " = ", lhs, " % ", rhs);
case BINOP_MOD1:
if (lhs_t->tag != IntType && lhs_t->tag != NumType)
if (lhs_t->tag != IntType && lhs_t->tag != NumType && lhs_t->tag != ByteType)
code_err(ast, "I can't do a mod assignment with this operator between %T and %T", lhs_t, rhs_t);
return CORD_all(lhs, " = (((", lhs, ") - 1) % ", rhs, ") + 1;");
case BINOP_PLUS:
if (lhs_t->tag != IntType && lhs_t->tag != NumType)
if (lhs_t->tag != IntType && lhs_t->tag != NumType && lhs_t->tag != ByteType)
code_err(ast, "I can't do an addition assignment with this operator between %T and %T", lhs_t, rhs_t);
return CORD_all(lhs, " += ", rhs, ";");
case BINOP_MINUS:
if (lhs_t->tag != IntType && lhs_t->tag != NumType)
if (lhs_t->tag != IntType && lhs_t->tag != NumType && lhs_t->tag != ByteType)
code_err(ast, "I can't do a subtraction assignment with this operator between %T and %T", lhs_t, rhs_t);
return CORD_all(lhs, " -= ", rhs, ";");
case BINOP_POWER: {
@ -667,17 +669,17 @@ CORD compile_statement(env_t *env, ast_t *ast)
return CORD_all(lhs, " = pow(", lhs, ", ", rhs, ");");
}
case BINOP_LSHIFT:
if (lhs_t->tag != IntType)
if (lhs_t->tag != IntType && lhs_t->tag != ByteType)
code_err(ast, "I can't do a shift assignment with this operator between %T and %T", lhs_t, rhs_t);
return CORD_all(lhs, " <<= ", rhs, ";");
case BINOP_RSHIFT:
if (lhs_t->tag != IntType)
if (lhs_t->tag != IntType && lhs_t->tag != ByteType)
code_err(ast, "I can't do a shift assignment with this operator between %T and %T", lhs_t, rhs_t);
return CORD_all(lhs, " >>= ", rhs, ";");
case BINOP_AND: {
if (lhs_t->tag == BoolType)
return CORD_all("if (", lhs, ") ", lhs, " = ", rhs, ";");
else if (lhs_t->tag == IntType)
else if (lhs_t->tag == IntType || lhs_t->tag == ByteType)
return CORD_all(lhs, " &= ", rhs, ";");
else
code_err(ast, "'or=' is not implemented for %T types", lhs_t);
@ -685,13 +687,13 @@ CORD compile_statement(env_t *env, ast_t *ast)
case BINOP_OR: {
if (lhs_t->tag == BoolType)
return CORD_all("if (!(", lhs, ")) ", lhs, " = ", rhs, ";");
else if (lhs_t->tag == IntType)
else if (lhs_t->tag == IntType || lhs_t->tag == ByteType)
return CORD_all(lhs, " |= ", rhs, ";");
else
code_err(ast, "'or=' is not implemented for %T types", lhs_t);
}
case BINOP_XOR:
if (lhs_t->tag != IntType && lhs_t->tag != BoolType)
if (lhs_t->tag != IntType && lhs_t->tag != BoolType && lhs_t->tag != ByteType)
code_err(ast, "I can't do an xor assignment with this operator between %T and %T", lhs_t, rhs_t);
return CORD_all(lhs, " ^= ", rhs, ";");
case BINOP_CONCAT: {
@ -1398,11 +1400,7 @@ CORD expr_as_text(env_t *env, CORD expr, type_t *t, CORD color)
// NOTE: this cannot use stack(), since bools may actually be bit fields:
return CORD_asprintf("Bool$as_text((Bool_t[1]){%r}, %r, &Bool$info)", expr, color);
case CStringType: return CORD_asprintf("CString$as_text(stack(%r), %r, &CString$info)", expr, color);
case BigIntType: case IntType: {
CORD name = type_to_cord(t);
return CORD_asprintf("%r$as_text(stack(%r), %r, &%r$info)", name, expr, color, name);
}
case NumType: {
case BigIntType: case IntType: case ByteType: case NumType: {
CORD name = type_to_cord(t);
return CORD_asprintf("%r$as_text(stack(%r), %r, &%r$info)", name, expr, color, name);
}
@ -1757,6 +1755,7 @@ CORD compile_null(type_t *t)
break;
}
case BoolType: return "NULL_BOOL";
case ByteType: return "NULL_BYTE";
case ArrayType: return "NULL_ARRAY";
case TableType: return "NULL_TABLE";
case SetType: return "NULL_TABLE";
@ -1825,21 +1824,25 @@ CORD compile(env_t *env, ast_t *ast)
return CORD_asprintf("Int$from_str(\"%s\")", str);
}
case IBITS64:
if ((mpz_cmp_si(i, INT64_MAX) < 0) && (mpz_cmp_si(i, INT64_MIN) > 0))
if ((mpz_cmp_si(i, INT64_MAX) <= 0) && (mpz_cmp_si(i, INT64_MIN) >= 0))
return CORD_asprintf("I64(%ldl)", mpz_get_si(i));
code_err(ast, "This value cannot fit in a 64-bit integer");
case IBITS32:
if ((mpz_cmp_si(i, INT32_MAX) < 0) && (mpz_cmp_si(i, INT32_MIN) > 0))
if ((mpz_cmp_si(i, INT32_MAX) <= 0) && (mpz_cmp_si(i, INT32_MIN) >= 0))
return CORD_asprintf("I32(%ld)", mpz_get_si(i));
code_err(ast, "This value cannot fit in a 32-bit integer");
case IBITS16:
if ((mpz_cmp_si(i, INT16_MAX) < 0) && (mpz_cmp_si(i, INT16_MIN) > 0))
if ((mpz_cmp_si(i, INT16_MAX) <= 0) && (mpz_cmp_si(i, INT16_MIN) >= 0))
return CORD_asprintf("I16(%ld)", mpz_get_si(i));
code_err(ast, "This value cannot fit in a 16-bit integer");
case IBITS8:
if ((mpz_cmp_si(i, INT8_MAX) < 0) && (mpz_cmp_si(i, INT8_MIN) > 0))
if ((mpz_cmp_si(i, INT8_MAX) <= 0) && (mpz_cmp_si(i, INT8_MIN) >= 0))
return CORD_asprintf("I8(%ld)", mpz_get_si(i));
code_err(ast, "This value cannot fit in a 8-bit integer");
case IBITS_BYTE:
if ((mpz_cmp_si(i, UINT8_MAX) <= 0) && (mpz_cmp_si(i, 0) >= 0))
return CORD_asprintf("Byte(%ld)", mpz_get_si(i));
code_err(ast, "This value cannot fit in a byte");
default: code_err(ast, "Not a valid integer bit width");
}
}
@ -1865,7 +1868,7 @@ CORD compile(env_t *env, ast_t *ast)
if (t->tag == BoolType)
return CORD_all("!(", compile(env, value), ")");
else if (t->tag == IntType)
else if (t->tag == IntType || t->tag == ByteType)
return CORD_all("~(", compile(env, value), ")");
else if (t->tag == ArrayType)
return CORD_all("((", compile(env, value), ").length == 0)");
@ -1948,42 +1951,42 @@ CORD compile(env_t *env, ast_t *ast)
return CORD_all("pow(", lhs, ", ", rhs, ")");
}
case BINOP_MULT: {
if (operand_t->tag != IntType && operand_t->tag != NumType)
if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
code_err(ast, "Math operations are only supported for numeric types");
return CORD_all("(", lhs, " * ", rhs, ")");
}
case BINOP_DIVIDE: {
if (operand_t->tag != IntType && operand_t->tag != NumType)
if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
code_err(ast, "Math operations are only supported for numeric types");
return CORD_all("(", lhs, " / ", rhs, ")");
}
case BINOP_MOD: {
if (operand_t->tag != IntType && operand_t->tag != NumType)
if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
code_err(ast, "Math operations are only supported for numeric types");
return CORD_all("(", lhs, " % ", rhs, ")");
}
case BINOP_MOD1: {
if (operand_t->tag != IntType && operand_t->tag != NumType)
if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
code_err(ast, "Math operations are only supported for numeric types");
return CORD_all("((((", lhs, ")-1) % (", rhs, ")) + 1)");
}
case BINOP_PLUS: {
if (operand_t->tag != IntType && operand_t->tag != NumType)
if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
code_err(ast, "Math operations are only supported for numeric types");
return CORD_all("(", lhs, " + ", rhs, ")");
}
case BINOP_MINUS: {
if (operand_t->tag != IntType && operand_t->tag != NumType)
if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
code_err(ast, "Math operations are only supported for numeric types");
return CORD_all("(", lhs, " - ", rhs, ")");
}
case BINOP_LSHIFT: {
if (operand_t->tag != IntType && operand_t->tag != NumType)
if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
code_err(ast, "Math operations are only supported for numeric types");
return CORD_all("(", lhs, " << ", rhs, ")");
}
case BINOP_RSHIFT: {
if (operand_t->tag != IntType && operand_t->tag != NumType)
if (operand_t->tag != IntType && operand_t->tag != NumType && operand_t->tag != ByteType)
code_err(ast, "Math operations are only supported for numeric types");
return CORD_all("(", lhs, " >> ", rhs, ")");
}
@ -1991,7 +1994,7 @@ CORD compile(env_t *env, ast_t *ast)
switch (operand_t->tag) {
case BigIntType:
return CORD_all("Int$equal_value(", lhs, ", ", rhs, ")");
case BoolType: case IntType: case NumType: case PointerType: case FunctionType:
case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType:
return CORD_all("(", lhs, " == ", rhs, ")");
default:
return CORD_asprintf("generic_equal(stack(%r), stack(%r), %r)", lhs, rhs, compile_type_info(env, operand_t));
@ -2001,7 +2004,7 @@ CORD compile(env_t *env, ast_t *ast)
switch (operand_t->tag) {
case BigIntType:
return CORD_all("!Int$equal_value(", lhs, ", ", rhs, ")");
case BoolType: case IntType: case NumType: case PointerType: case FunctionType:
case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType:
return CORD_all("(", lhs, " != ", rhs, ")");
default:
return CORD_asprintf("!generic_equal(stack(%r), stack(%r), %r)", lhs, rhs, compile_type_info(env, operand_t));
@ -2011,7 +2014,7 @@ CORD compile(env_t *env, ast_t *ast)
switch (operand_t->tag) {
case BigIntType:
return CORD_all("(Int$compare_value(", lhs, ", ", rhs, ") < 0)");
case BoolType: case IntType: case NumType: case PointerType: case FunctionType:
case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType:
return CORD_all("(", lhs, " < ", rhs, ")");
default:
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) < 0)", lhs, rhs, compile_type_info(env, operand_t));
@ -2021,7 +2024,7 @@ CORD compile(env_t *env, ast_t *ast)
switch (operand_t->tag) {
case BigIntType:
return CORD_all("(Int$compare_value(", lhs, ", ", rhs, ") <= 0)");
case BoolType: case IntType: case NumType: case PointerType: case FunctionType:
case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType:
return CORD_all("(", lhs, " <= ", rhs, ")");
default:
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) <= 0)", lhs, rhs, compile_type_info(env, operand_t));
@ -2031,7 +2034,7 @@ CORD compile(env_t *env, ast_t *ast)
switch (operand_t->tag) {
case BigIntType:
return CORD_all("(Int$compare_value(", lhs, ", ", rhs, ") > 0)");
case BoolType: case IntType: case NumType: case PointerType: case FunctionType:
case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType:
return CORD_all("(", lhs, " > ", rhs, ")");
default:
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) > 0)", lhs, rhs, compile_type_info(env, operand_t));
@ -2041,7 +2044,7 @@ CORD compile(env_t *env, ast_t *ast)
switch (operand_t->tag) {
case BigIntType:
return CORD_all("(Int$compare_value(", lhs, ", ", rhs, ") >= 0)");
case BoolType: case IntType: case NumType: case PointerType: case FunctionType:
case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType:
return CORD_all("(", lhs, " >= ", rhs, ")");
default:
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) >= 0)", lhs, rhs, compile_type_info(env, operand_t));
@ -2050,7 +2053,7 @@ CORD compile(env_t *env, ast_t *ast)
case BINOP_AND: {
if (operand_t->tag == BoolType)
return CORD_all("(", lhs, " && ", rhs, ")");
else if (operand_t->tag == IntType)
else if (operand_t->tag == IntType || operand_t->tag == ByteType)
return CORD_all("(", lhs, " & ", rhs, ")");
else
code_err(ast, "Boolean operators are only supported for Bool and integer types");
@ -2061,13 +2064,13 @@ CORD compile(env_t *env, ast_t *ast)
case BINOP_OR: {
if (operand_t->tag == BoolType)
return CORD_all("(", lhs, " || ", rhs, ")");
else if (operand_t->tag == IntType)
else if (operand_t->tag == IntType || operand_t->tag == ByteType)
return CORD_all("(", lhs, " | ", rhs, ")");
else
code_err(ast, "Boolean operators are only supported for Bool and integer types");
}
case BINOP_XOR: {
if (operand_t->tag == BoolType || operand_t->tag == IntType)
if (operand_t->tag == BoolType || operand_t->tag == IntType || operand_t->tag == ByteType)
return CORD_all("(", lhs, " ^ ", rhs, ")");
else
code_err(ast, "Boolean operators are only supported for Bool and integer types");
@ -2200,7 +2203,7 @@ CORD compile(env_t *env, ast_t *ast)
CORD comparison;
if (key_t->tag == BigIntType)
comparison = CORD_all("(Int$compare_value(", lhs_key, ", ", rhs_key, ")", (ast->tag == Min ? "<=" : ">="), "0)");
else if (key_t->tag == IntType || key_t->tag == NumType || key_t->tag == BoolType || key_t->tag == PointerType)
else if (key_t->tag == IntType || key_t->tag == NumType || key_t->tag == BoolType || key_t->tag == PointerType || key_t->tag == ByteType)
comparison = CORD_all("((", lhs_key, ")", (ast->tag == Min ? "<=" : ">="), "(", rhs_key, "))");
else
comparison = CORD_all("generic_compare(stack(", lhs_key, "), stack(", rhs_key, "), ", compile_type_info(env, key_t), ")",
@ -3277,7 +3280,7 @@ CORD compile_namespace_definitions(env_t *env, const char *ns_name, ast_t *block
CORD compile_type_info(env_t *env, type_t *t)
{
switch (t->tag) {
case BoolType: case IntType: case BigIntType: case NumType: case CStringType:
case BoolType: case ByteType: case IntType: case BigIntType: case NumType: case CStringType:
return CORD_all("&", type_to_cord(t), "$info");
case TextType: {
auto text = Match(t, TextType);

View File

@ -76,7 +76,7 @@ Exits the program with a given status and optionally prints a message.
**Usage:**
```markdown
ask(message:Text = "", status:Int32 = 0_i32) -> Void
ask(message:Text = "", status:Int32 = 0[32]) -> Void
```
**Parameters:**

View File

@ -8,9 +8,9 @@ Tomo has five types of integers:
the GNU MP library. These integers are fast for small numbers and guaranteed
to always be correct and never overflow.
- `Int8`/`Int16`/`Int32`/`Int64`: Fixed-size integers that take up `N` bits.
These integers must be manually specified with their suffix (e.g. `5_i64`)
and are subject to overflowing. If an overflow occurs, a runtime error will
be raised.
These integers must be manually specified with their bits in square brackets
(e.g. `5[64]`) and are subject to overflowing. If an overflow occurs, a
runtime error will be raised.
Conversion between integer types can be done by calling the target type as a
function: `Int32(x)`. For fixed-width types, the conversion function also

View File

@ -25,11 +25,11 @@ value to the user.
```tomo
>> 0 <> 99
= -1_i32
= -1[32]
>> 5 <> 5
= 0_i32
= 0[32]
>> 99 <> 0
= 1_i32
= 1[32]
```
It's particularly handy for using the array `sort()` method, which takes a

View File

@ -40,11 +40,12 @@ intended. Paths can be created from text with slashes using
### `append`
**Description:**
Appends the given text to the file at the specified path, creating the file if it doesn't already exist. Failure to write will result in a runtime error.
Appends the given text to the file at the specified path, creating the file if
it doesn't already exist. Failure to write will result in a runtime error.
**Usage:**
```markdown
append(path: Path, text: Text, permissions: Int32 = 0o644_i32) -> Void
append(path: Path, text: Text, permissions: Int32 = 0o644[32]) -> Void
```
**Parameters:**
@ -63,6 +64,33 @@ Nothing.
---
### `append_bytes`
**Description:**
Appends the given bytes to the file at the specified path, creating the file if
it doesn't already exist. Failure to write will result in a runtime error.
**Usage:**
```markdown
append_bytes(path: Path, bytes: [Byte], permissions: Int32 = 0o644[32]) -> Void
```
**Parameters:**
- `path`: The path of the file to append to.
- `bytes`: The bytes to append to the file.
- `permissions` (optional): The permissions to set on the file if it is being created (default is `0o644`).
**Returns:**
Nothing.
**Example:**
```markdown
(./log.txt):append_bytes([104[B], 105[B]])
```
---
### `base_name`
**Description:**
@ -146,7 +174,7 @@ Creates a new directory at the specified path with the given permissions.
**Usage:**
```markdown
create_directory(path: Path, permissions=0o644_i32) -> Void
create_directory(path: Path, permissions=0o644[32]) -> Void
```
**Parameters:**
@ -404,6 +432,29 @@ The contents of the file. If the file does not exist, an error will be raised.
```markdown
content := (./file.txt):read()
```
---
### `read_bytes`
**Description:**
Reads the contents of the file at the specified path.
**Usage:**
```markdown
read_bytes(path: Path) -> [Byte]
```
**Parameters:**
- `path`: The path of the file to read.
**Returns:**
The byte contents of the file. If the file does not exist, an error will be raised.
**Example:**
```markdown
content := (./file.txt):read_bytes()
```
---
@ -554,7 +605,7 @@ writing cannot be successfully completed, a runtime error is raised.
**Usage:**
```markdown
write(path: Path, text: Text, permissions=0o644_i32) -> Void
write(path: Path, text: Text, permissions=0o644[32]) -> Void
```
**Parameters:**
@ -573,6 +624,34 @@ Nothing.
---
### `write_bytes`
**Description:**
Writes the given bytes to the file at the specified path, creating the file if
it doesn't already exist. Sets the file permissions as specified. If the file
writing cannot be successfully completed, a runtime error is raised.
**Usage:**
```markdown
write(path: Path, bytes: [Byte], permissions=0o644[32]) -> Void
```
**Parameters:**
- `path`: The path of the file to write to.
- `bytes`: An array of bytes to write to the file.
- `permissions` (optional): The permissions to set on the file if it is created (default is `0o644`).
**Returns:**
Nothing.
**Example:**
```markdown
(./file.txt):write_bytes([104[B], 105[B]])
```
---
### `write_unique`
**Description:**
@ -602,3 +681,35 @@ The path of the newly created unique file.
= "Hello, world!"
created:remove()
```
---
### `write_unique_bytes`
**Description:**
Writes the given bytes to a unique file path based on the specified path. The
file is created if it doesn't exist. This is useful for creating temporary
files.
**Usage:**
```markdown
write_unique_bytes(path: Path, bytes: [Byte]) -> Path
```
**Parameters:**
- `path`: The base path for generating the unique file. This path must include
the string `XXXXXX` in the file base name.
- `bytes`: The bytes to write to the file.
**Returns:**
The path of the newly created unique file.
**Example:**
```markdown
>> created := (./file-XXXXXX.txt):write_unique_bytes([1[B], 2[B], 3[B]])
= (./file-27QHtq.txt)
>> created:read()
= [1[B], 2[B], 3[B]]
created:remove()
```

View File

@ -441,7 +441,7 @@ An array of bytes (`[Int8]`) representing the text in UTF8 encoding.
**Example:**
```tomo
>> "Amélie":utf8_bytes()
= [65_i8, 109_i8, -61_i8, -87_i8, 108_i8, 105_i8, 101_i8] : [Int8]
= [65[B], 109[B], 195[B], 169[B], 108[B], 105[B], 101[B]] : [Byte]
```
---
@ -491,7 +491,7 @@ An array of 32-bit integer Unicode code points (`[Int32]`).
**Example:**
```tomo
>> "Amélie":utf32_codepoints()
= [65_i32, 109_i32, 233_i32, 108_i32, 105_i32, 101_i32] : [Int32]
= [65[32], 109[32], 233[32], 108[32], 105[32], 101[32]] : [Int32]
```
---
@ -601,7 +601,7 @@ A new text with the specified codepoints after normalization has been applied.
**Example:**
```tomo
>> Text.from_codepoints([197_i32, 107_i32, 101_i32])
>> Text.from_codepoints([197[32], 107[32], 101[32]])
= "Åke"
```
@ -628,7 +628,7 @@ A new text based on the input UTF8 bytes after normalization has been applied.
**Example:**
```tomo
>> Text.from_bytes([-61_i8, -123_i8, 107_i8, 101_i8])
>> Text.from_bytes([195[B], 133[B], 107[B], 101[B]])
= "Åke"
```
@ -668,11 +668,11 @@ found.
>> " one two three ":find("{id}", start=5)
= 8
>> len := 0_i64
>> len := 0[64]
>> " one ":find("{id}", length=&len)
= 4
>> len
= 3_i64
= 3[64]
```
---

View File

@ -88,6 +88,11 @@ env_t *new_compilation_unit(CORD *libname)
{"from_text", "Bool$from_text", "func(text:Text)->Bool?"},
{"random", "Bool$random", "func(p=0.5)->Bool"},
)},
{"Byte", Type(ByteType), "Byte_t", "Byte$info", TypedArray(ns_entry_t,
{"max", "Byte$max", "Byte"},
{"min", "Byte$min", "Byte"},
{"random", "Byte$random", "func(min=Byte.min, max=Byte.max)->Byte"},
)},
{"Int", Type(BigIntType), "Int_t", "Int$info", TypedArray(ns_entry_t,
{"abs", "Int$abs", "func(x:Int)->Int"},
{"bit_and", "Int$bit_and", "func(x:Int,y:Int)->Int"},
@ -129,8 +134,9 @@ env_t *new_compilation_unit(CORD *libname)
{"modulo", "Int64$modulo", "func(x:Int64,y:Int64)->Int64"},
{"modulo1", "Int64$modulo1", "func(x:Int64,y:Int64)->Int64"},
{"octal", "Int64$octal", "func(i:Int64, digits=0, prefix=yes)->Text"},
{"random", "Int64$random", "func(min=-0x8000000000000000_i64, max=0x7FFFFFFFFFFFFFFF_i64)->Int64"},
{"to", "Int64$to", "func(from:Int64,to:Int64)->Range"},
// Must be defined after min/max:
{"random", "Int64$random", "func(min=Int64.min, max=Int64.max)->Int64"},
)},
{"Int32", Type(IntType, .bits=TYPE_IBITS32), "Int32_t", "Int32$info", TypedArray(ns_entry_t,
{"abs", "abs", "func(i:Int32)->Int32"},
@ -145,8 +151,9 @@ env_t *new_compilation_unit(CORD *libname)
{"modulo", "Int32$modulo", "func(x:Int32,y:Int32)->Int32"},
{"modulo1", "Int32$modulo1", "func(x:Int32,y:Int32)->Int32"},
{"octal", "Int32$octal", "func(i:Int32, digits=0, prefix=yes)->Text"},
{"random", "Int32$random", "func(min=-0x80000000_i32, max=0x7FFFFFFF_i32)->Int32"},
{"to", "Int32$to", "func(from:Int32,to:Int32)->Range"},
// Must be defined after min/max:
{"random", "Int32$random", "func(min=Int32.min, max=Int32.max)->Int32"},
)},
{"Int16", Type(IntType, .bits=TYPE_IBITS16), "Int16_t", "Int16$info", TypedArray(ns_entry_t,
{"abs", "abs", "func(i:Int16)->Int16"},
@ -161,8 +168,9 @@ env_t *new_compilation_unit(CORD *libname)
{"modulo", "Int16$modulo", "func(x:Int16,y:Int16)->Int16"},
{"modulo1", "Int16$modulo1", "func(x:Int16,y:Int16)->Int16"},
{"octal", "Int16$octal", "func(i:Int16, digits=0, prefix=yes)->Text"},
{"random", "Int16$random", "func(min=-0x8000_i16, max=0x7FFF_i16)->Int16"},
{"to", "Int16$to", "func(from:Int16,to:Int16)->Range"},
// Must be defined after min/max:
{"random", "Int16$random", "func(min=Int16.min, max=Int16.max)->Int16"},
)},
{"Int8", Type(IntType, .bits=TYPE_IBITS8), "Int8_t", "Int8$info", TypedArray(ns_entry_t,
{"abs", "abs", "func(i:Int8)->Int8"},
@ -177,8 +185,9 @@ env_t *new_compilation_unit(CORD *libname)
{"modulo", "Int8$modulo", "func(x:Int8,y:Int8)->Int8"},
{"modulo1", "Int8$modulo1", "func(x:Int8,y:Int8)->Int8"},
{"octal", "Int8$octal", "func(i:Int8, digits=0, prefix=yes)->Text"},
{"random", "Int8$random", "func(min=-0x80_i8, max=0x7F_i8)->Int8"},
{"to", "Int8$to", "func(from:Int8,to:Int8)->Range"},
// Must be defined after min/max:
{"random", "Int8$random", "func(min=Int8.min, max=Int8.max)->Int8"},
)},
#define C(name) {#name, "M_"#name, "Num"}
#define F(name) {#name, #name, "func(n:Num)->Num"}
@ -250,11 +259,12 @@ env_t *new_compilation_unit(CORD *libname)
{"escape_text", "Pattern$escape_text", "func(text:Text)->Pattern"},
)},
{"Path", Type(TextType, .lang="Path", .env=namespace_env(env, "Path")), "Text_t", "Text$info", TypedArray(ns_entry_t,
{"append", "Path$append", "func(path:Path, text:Text, permissions=0o644_i32)"},
{"append", "Path$append", "func(path:Path, text:Text, permissions=0o644[32])"},
{"append_bytes", "Path$append_bytes", "func(path:Path, bytes:[Byte], permissions=0o644[32])"},
{"base_name", "Path$base_name", "func(path:Path)->Text"},
{"by_line", "Path$by_line", "func(path:Path)->func()->Text?"},
{"children", "Path$children", "func(path:Path, include_hidden=no)->[Path]"},
{"create_directory", "Path$create_directory", "func(path:Path, permissions=0o644_i32)"},
{"create_directory", "Path$create_directory", "func(path:Path, permissions=0o644[32])"},
{"escape_int", "Int$value_as_text", "func(i:Int)->Path"},
{"escape_path", "Path$escape_path", "func(path:Path)->Path"},
{"escape_text", "Path$escape_text", "func(text:Text)->Path"},
@ -268,13 +278,16 @@ env_t *new_compilation_unit(CORD *libname)
{"is_symlink", "Path$is_symlink", "func(path:Path)->Bool"},
{"parent", "Path$parent", "func(path:Path)->Path"},
{"read", "Path$read", "func(path:Path)->Text"},
{"read_bytes", "Path$read_bytes", "func(path:Path)->[Byte]"},
{"relative", "Path$relative", "func(path:Path, relative_to=(./))->Path"},
{"remove", "Path$remove", "func(path:Path, ignore_missing=no)"},
{"resolved", "Path$resolved", "func(path:Path, relative_to=(./))->Path"},
{"subdirectories", "Path$children", "func(path:Path, include_hidden=no)->[Path]"},
{"unique_directory", "Path$unique_directory", "func(path:Path)->Path"},
{"write", "Path$write", "func(path:Path, text:Text, permissions=0o644_i32)"},
{"write", "Path$write", "func(path:Path, text:Text, permissions=0o644[32])"},
{"write_bytes", "Path$write_bytes", "func(path:Path, bytes:[Byte], permissions=0o644[32])"},
{"write_unique", "Path$write_unique", "func(path:Path, text:Text)->Path"},
{"write_unique_bytes", "Path$write_unique_bytes", "func(path:Path, bytes:[Byte])->Path"},
)},
{"Shell", Type(TextType, .lang="Shell", .env=namespace_env(env, "Shell")), "Shell_t", "Shell$info", TypedArray(ns_entry_t,
{"escape_int", "Int$value_as_text", "func(i:Int)->Shell"},
@ -287,7 +300,7 @@ env_t *new_compilation_unit(CORD *libname)
{"ends_with", "Text$ends_with", "func(text:Text, suffix:Text)->Bool"},
{"find", "Text$find", "func(text:Text, pattern:Pattern, start=1, length=!&Int64)->Int"},
{"find_all", "Text$find_all", "func(text:Text, pattern:Pattern)->[Text]"},
{"from_bytes", "Text$from_bytes", "func(bytes:[Int8])->Text"},
{"from_bytes", "Text$from_bytes", "func(bytes:[Byte])->Text"},
{"from_c_string", "Text$from_str", "func(str:CString)->Text"},
{"from_codepoint_names", "Text$from_codepoint_names", "func(codepoint_names:[Text])->Text"},
{"from_codepoints", "Text$from_codepoints", "func(codepoints:[Int32])->Text"},
@ -309,7 +322,7 @@ env_t *new_compilation_unit(CORD *libname)
{"trim", "Text$trim", "func(text:Text, pattern=$/{whitespace}/, trim_left=yes, trim_right=yes)->Text"},
{"upper", "Text$upper", "func(text:Text)->Text"},
{"utf32_codepoints", "Text$utf32_codepoints", "func(text:Text)->[Int32]"},
{"utf8_bytes", "Text$utf8_bytes", "func(text:Text)->[Int8]"},
{"utf8_bytes", "Text$utf8_bytes", "func(text:Text)->[Byte]"},
)},
{"Thread", THREAD_TYPE, "pthread_t*", "Thread", TypedArray(ns_entry_t,
{"new", "Thread$new", "func(fn:func())->Thread"},

View File

@ -58,7 +58,7 @@ func _send(method:_Method, url:Text, data:Text?, headers=[:Text])->HTTPResponse:
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
}
code := 0_i64
code := 0[64]
inline C {
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK)

10
parse.c
View File

@ -506,12 +506,12 @@ PARSER(parse_int) {
return NewAST(ctx->file, start, pos, Num, .n=n, .bits=64);
}
match(&pos, "_");
auto bits = IBITS_UNSPECIFIED;
if (match(&pos, "i64")) bits = IBITS64;
else if (match(&pos, "i32")) bits = IBITS32;
else if (match(&pos, "i16")) bits = IBITS16;
else if (match(&pos, "i8")) bits = IBITS8;
if (match(&pos, "[64]")) bits = IBITS64;
else if (match(&pos, "[32]")) bits = IBITS32;
else if (match(&pos, "[16]")) bits = IBITS16;
else if (match(&pos, "[8]")) bits = IBITS8;
else if (match(&pos, "[B]")) bits = IBITS_BYTE;
// else if (match(&pos, ".") || match(&pos, "e")) return NULL; // looks like a float

38
stdlib/bytes.c Normal file
View File

@ -0,0 +1,38 @@
// The logic for unsigned bytes
#include <stdbool.h>
#include <stdint.h>
#include "bytes.h"
#include "stdlib.h"
#include "text.h"
#include "util.h"
public const Byte_t Byte$min = 0;
public const Byte_t Byte$max = UINT8_MAX;
PUREFUNC public Text_t Byte$as_text(const Byte_t *b, bool colorize, const TypeInfo *type)
{
(void)type;
if (!b) return Text("Byte");
return Text$format(colorize ? "\x1b[35m%u[B]\x1b[m" : "%u[B]", *b);
}
public Byte_t Byte$random(Byte_t min, Byte_t max)
{
if (min > max)
fail("Random minimum value (%u) is larger than the maximum value (%u)", min, max);
if (min == max)
return min;
uint32_t r = arc4random_uniform((uint32_t)max - (uint32_t)min + 1u);
return (Byte_t)(min + r);
}
public const TypeInfo Byte$info = {
.size=sizeof(Byte_t),
.align=__alignof__(Byte_t),
.tag=CustomInfo,
.CustomInfo={.as_text=(void*)Byte$as_text},
};
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

29
stdlib/bytes.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
// An unsigned byte datatype
#include <stdbool.h>
#include <stdint.h>
#include "types.h"
#include "util.h"
#define Byte_t uint8_t
#define Byte(b) ((Byte_t)(b))
PUREFUNC Text_t Byte$as_text(const Byte_t *b, bool colorize, const TypeInfo *type);
Byte_t Byte$random(Byte_t min, Byte_t max);
extern const Byte_t Byte$min;
extern const Byte_t Byte$max;
extern const TypeInfo Byte$info;
typedef struct {
Byte_t value;
bool is_null:1;
} OptionalByte_t;
#define NULL_BYTE ((OptionalByte_t){.is_null=true})
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -481,10 +481,10 @@ public const TypeInfo Int$info = {
.CustomInfo={.compare=(void*)KindOfInt##$compare, .as_text=(void*)KindOfInt##$as_text}, \
};
DEFINE_INT_TYPE(int64_t, Int64, "ld_i64", INT64_MIN, INT64_MAX)
DEFINE_INT_TYPE(int32_t, Int32, "d_i32", INT32_MIN, INT32_MAX)
DEFINE_INT_TYPE(int16_t, Int16, "d_i16", INT16_MIN, INT16_MAX)
DEFINE_INT_TYPE(int8_t, Int8, "d_i8", INT8_MIN, INT8_MAX)
DEFINE_INT_TYPE(int64_t, Int64, "ld[64]", INT64_MIN, INT64_MAX)
DEFINE_INT_TYPE(int32_t, Int32, "d[32]", INT32_MIN, INT32_MAX)
DEFINE_INT_TYPE(int16_t, Int16, "d[16]", INT16_MIN, INT16_MAX)
DEFINE_INT_TYPE(int8_t, Int8, "d[8]", INT8_MIN, INT8_MAX)
#undef DEFINE_INT_TYPE
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -207,7 +207,7 @@ public bool Path$is_symlink(Path_t path)
return (sb.st_mode & S_IFMT) == S_IFLNK;
}
static void _write(Path_t path, Text_t text, int mode, int permissions)
static void _write(Path_t path, Array_t bytes, int mode, int permissions)
{
path = Path$_expand_home(path);
const char *path_str = Text$as_c_string(path);
@ -215,24 +215,36 @@ static void _write(Path_t path, Text_t text, int mode, int permissions)
if (fd == -1)
fail("Could not write to file: %s\n%s", path_str, strerror(errno));
const char *str = Text$as_c_string(text);
size_t len = strlen(str);
ssize_t written = write(fd, str, len);
if (written != (ssize_t)len)
if (bytes.stride != 1)
Array$compact(&bytes, 1);
ssize_t written = write(fd, bytes.data, (size_t)bytes.length);
if (written != (ssize_t)bytes.length)
fail("Could not write to file: %s\n%s", path_str, strerror(errno));
}
public void Path$write(Path_t path, Text_t text, int permissions)
{
_write(path, text, O_WRONLY | O_CREAT, permissions);
Array_t bytes = Text$utf8_bytes(text);
_write(path, bytes, O_WRONLY | O_CREAT, permissions);
}
public void Path$write_bytes(Path_t path, Array_t bytes, int permissions)
{
_write(path, bytes, O_WRONLY | O_CREAT, permissions);
}
public void Path$append(Path_t path, Text_t text, int permissions)
{
_write(path, text, O_WRONLY | O_APPEND | O_CREAT, permissions);
Array_t bytes = Text$utf8_bytes(text);
_write(path, bytes, O_WRONLY | O_APPEND | O_CREAT, permissions);
}
public Text_t Path$read(Path_t path)
public void Path$append_bytes(Path_t path, Array_t bytes, int permissions)
{
_write(path, bytes, O_WRONLY | O_APPEND | O_CREAT, permissions);
}
public Array_t Path$read_bytes(Path_t path)
{
path = Path$_expand_home(path);
int fd = open(Text$as_c_string(path), O_RDONLY);
@ -245,11 +257,11 @@ public Text_t Path$read(Path_t path)
if ((sb.st_mode & S_IFMT) == S_IFREG) { // Use memory mapping if it's a real file:
const char *mem = mmap(NULL, (size_t)sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
char *gc_mem = GC_MALLOC_ATOMIC((size_t)sb.st_size+1);
memcpy(gc_mem, mem, (size_t)sb.st_size);
gc_mem[sb.st_size] = '\0';
char *content = GC_MALLOC_ATOMIC((size_t)sb.st_size+1);
memcpy(content, mem, (size_t)sb.st_size);
content[sb.st_size] = '\0';
close(fd);
return Text$from_strn(gc_mem, (size_t)sb.st_size);
return (Array_t){.data=content, .atomic=1, .stride=1, .length=(int64_t)sb.st_size};
} else {
size_t capacity = 256, len = 0;
char *content = GC_MALLOC_ATOMIC(capacity);
@ -279,10 +291,16 @@ public Text_t Path$read(Path_t path)
if (u8_check((uint8_t*)content, len) != NULL)
fail("File does not contain valid UTF8 data!");
return Text$from_strn(content, len);
return (Array_t){.data=content, .atomic=1, .stride=1, .length=len};
}
}
public Text_t Path$read(Path_t path)
{
Array_t bytes = Path$read_bytes(path);
return Text$from_bytes(bytes);
}
public void Path$remove(Path_t path, bool ignore_missing)
{
path = Path$_expand_home(path);
@ -375,7 +393,7 @@ public Path_t Path$unique_directory(Path_t path)
return Text$format("%s/", created);
}
public Text_t Path$write_unique(Path_t path, Text_t text)
public Text_t Path$write_unique_bytes(Path_t path, Array_t bytes)
{
path = Path$_expand_home(path);
const char *path_str = Text$as_c_string(path);
@ -392,14 +410,20 @@ public Text_t Path$write_unique(Path_t path, Text_t text)
if (fd == -1)
fail("Could not write to unique file: %s\n%s", buf, strerror(errno));
const char *str = Text$as_c_string(text);
size_t write_len = strlen(str);
ssize_t written = write(fd, str, write_len);
if (written != (ssize_t)write_len)
if (bytes.stride != 1)
Array$compact(&bytes, 1);
ssize_t written = write(fd, bytes.data, (size_t)bytes.length);
if (written != (ssize_t)bytes.length)
fail("Could not write to file: %s\n%s", buf, strerror(errno));
return Text$format("%s", buf);
}
public Text_t Path$write_unique(Path_t path, Text_t text)
{
return Path$write_unique_bytes(path, Text$utf8_bytes(text));
}
public Path_t Path$parent(Path_t path)
{
return Path$cleanup(Text$concat(path, Path("/../")));

View File

@ -26,8 +26,11 @@ bool Path$is_pipe(Path_t path, bool follow_symlinks);
bool Path$is_socket(Path_t path, bool follow_symlinks);
bool Path$is_symlink(Path_t path);
void Path$write(Path_t path, Text_t text, int permissions);
void Path$write_bytes(Path_t path, Array_t bytes, int permissions);
void Path$append(Path_t path, Text_t text, int permissions);
void Path$append_bytes(Path_t path, Array_t bytes, int permissions);
Text_t Path$read(Path_t path);
Array_t Path$read_bytes(Path_t path);
void Path$remove(Path_t path, bool ignore_missing);
void Path$create_directory(Path_t path, int permissions);
Array_t Path$children(Path_t path, bool include_hidden);
@ -35,6 +38,7 @@ Array_t Path$files(Path_t path, bool include_hidden);
Array_t Path$subdirectories(Path_t path, bool include_hidden);
Path_t Path$unique_directory(Path_t path);
Text_t Path$write_unique(Path_t path, Text_t text);
Text_t Path$write_unique_bytes(Path_t path, Array_t bytes);
Path_t Path$parent(Path_t path);
Text_t Path$base_name(Path_t path);
Text_t Path$extension(Path_t path, bool full);

View File

@ -11,6 +11,7 @@
#include "arrays.h"
#include "bools.h"
#include "bytes.h"
#include "c_strings.h"
#include "channels.h"
#include "datatypes.h"

8
test/bytes.tm Normal file
View File

@ -0,0 +1,8 @@
func main():
!! Test bytes:
>> 100[B]
= 100[B]
>> 0xFF[B]
= 255[B]

View File

@ -11,8 +11,8 @@ func main():
>> 2 * 3 + 4
= 10
>> 1i8 + 2i16
= 3_i16
>> 1[8] + 2[16]
= 3[16]
>> 1 << 10
= 1024
@ -32,7 +32,7 @@ func main():
>> nums
= "1,2,3,4,5,"
>> x := 123i64
>> x := 123[64]
>> x:format(digits=5)
= "00123"
>> x:hex()
@ -42,16 +42,16 @@ func main():
>> Int.random(1, 100)
>> Int64.min
= -9223372036854775808_i64
= -9223372036854775808[64]
>> Int64.max
= 9223372036854775807_i64
= 9223372036854775807[64]
>> 123_i32:hex()
>> 123[32]:hex()
= "0x7B"
>> 123_i16:hex()
>> 123[16]:hex()
= "0x7B"
>> 123_i8:hex()
>> 123[8]:hex()
= "0x7B"
>> Int(2.1)

View File

@ -33,7 +33,7 @@ func main():
>> $HTML"$(1 + 2)"
= $HTML"3"
>> $HTML"$(3_i8)"
>> $HTML"$(3[8])"
= $HTML"3"
>> html:paragraph()

View File

@ -21,7 +21,7 @@ func maybe_int(should_i:Bool)->Int?:
func maybe_int64(should_i:Bool)->Int64?:
if should_i:
return 123_i64
return 123[64]
else:
return !Int64
@ -110,12 +110,12 @@ func main():
!! ...
!! Int64s:
>> yep := maybe_int64(yes)
= 123_i64?
= 123[64]?
>> nope := maybe_int64(no)
= !Int64
>> if yep:
>> yep
= 123_i64
= 123[64]
else: fail("Falsey: $yep")
>> if nope:
fail("Truthy: $nope")

View File

@ -24,6 +24,8 @@ func main():
>> tmpfile:append("!")
>> tmpfile:read()
= "Hello world!"
>> tmpfile:read_bytes()
= [72[B], 101[B], 108[B], 108[B], 111[B], 32[B], 119[B], 111[B], 114[B], 108[B], 100[B], 33[B]]
>> tmpdir:files():has(tmpfile)
= yes

View File

@ -29,17 +29,19 @@ func main():
>> amelie:split()
= ["A", "m", "é", "l", "i", "e"] : [Text]
>> amelie:utf32_codepoints()
= [65_i32, 109_i32, 233_i32, 108_i32, 105_i32, 101_i32] : [Int32]
= [65[32], 109[32], 233[32], 108[32], 105[32], 101[32]] : [Int32]
>> amelie:utf8_bytes()
= [65_i8, 109_i8, -61_i8, -87_i8, 108_i8, 105_i8, 101_i8] : [Int8]
= [65[B], 109[B], 195[B], 169[B], 108[B], 105[B], 101[B]] : [Byte]
>> Text.from_bytes([65[B], 109[B], 195[B], 169[B], 108[B], 105[B], 101[B]])
= "Amélie"
>> amelie2 := "Am$(\U65\U301)lie"
>> amelie2:split()
= ["A", "m", "é", "l", "i", "e"] : [Text]
>> amelie2:utf32_codepoints()
= [65_i32, 109_i32, 233_i32, 108_i32, 105_i32, 101_i32] : [Int32]
= [65[32], 109[32], 233[32], 108[32], 105[32], 101[32]] : [Int32]
>> amelie2:utf8_bytes()
= [65_i8, 109_i8, -61_i8, -87_i8, 108_i8, 105_i8, 101_i8] : [Int8]
= [65[B], 109[B], 195[B], 169[B], 108[B], 105[B], 101[B]] : [Byte]
>> amelie:codepoint_names()
= ["LATIN CAPITAL LETTER A", "LATIN SMALL LETTER M", "LATIN SMALL LETTER E WITH ACUTE", "LATIN SMALL LETTER L", "LATIN SMALL LETTER I", "LATIN SMALL LETTER E"]
@ -193,11 +195,11 @@ func main():
>> " one two three ":find($/{id}/, start=5)
= 8
>> len := 0_i64
>> len := 0[64]
>> " one ":find($/{id}/, length=&len)
= 4
>> len
= 3_i64
= 3[64]
!! Test text slicing:
>> "abcdef":slice()
@ -218,7 +220,7 @@ func main():
>> house:codepoint_names()
= ["CJK Unified Ideographs-5BB6"]
>> house:utf32_codepoints()
= [23478_i32]
= [23478[32]]
>> "🐧":codepoint_names()
= ["PENGUIN"]

View File

@ -503,6 +503,7 @@ type_t *get_type(env_t *env, ast_t *ast)
auto i = Match(ast, Int);
switch (i->bits) {
case IBITS_UNSPECIFIED: return Type(BigIntType);
case IBITS_BYTE: return Type(ByteType);
case IBITS8: return Type(IntType, .bits=TYPE_IBITS8);
case IBITS16: return Type(IntType, .bits=TYPE_IBITS16);
case IBITS32: return Type(IntType, .bits=TYPE_IBITS32);
@ -1006,7 +1007,8 @@ type_t *get_type(env_t *env, ast_t *ast)
auto rhs_ptr = Match(rhs_t, PointerType);
if (type_eq(lhs_ptr->pointed, rhs_ptr->pointed))
return Type(PointerType, .pointed=lhs_ptr->pointed, .is_readonly=lhs_ptr->is_readonly || rhs_ptr->is_readonly);
} else if (is_int_type(lhs_t) && is_int_type(rhs_t)) {
} else if ((is_int_type(lhs_t) && is_int_type(rhs_t))
|| (lhs_t->tag == ByteType && rhs_t->tag == ByteType)) {
return get_math_type(env, ast, lhs_t, rhs_t);
}
code_err(ast, "I can't figure out the type of this `and` expression because the left side is a %T, but the right side is a %T",
@ -1017,7 +1019,8 @@ type_t *get_type(env_t *env, ast_t *ast)
return lhs_t;
} else if (lhs_t->tag == BoolType && (rhs_t->tag == AbortType || rhs_t->tag == ReturnType)) {
return lhs_t;
} else if (is_int_type(lhs_t) && is_int_type(rhs_t)) {
} else if ((is_int_type(lhs_t) && is_int_type(rhs_t))
|| (lhs_t->tag == ByteType && rhs_t->tag == ByteType)) {
return get_math_type(env, ast, lhs_t, rhs_t);
} else if (lhs_t->tag == OptionalType) {
if (can_promote(rhs_t, lhs_t))
@ -1038,7 +1041,8 @@ type_t *get_type(env_t *env, ast_t *ast)
case BINOP_XOR: {
if (lhs_t->tag == BoolType && rhs_t->tag == BoolType) {
return lhs_t;
} else if (is_int_type(lhs_t) && is_int_type(rhs_t)) {
} else if ((is_int_type(lhs_t) && is_int_type(rhs_t))
|| (lhs_t->tag == ByteType && rhs_t->tag == ByteType)) {
return get_math_type(env, ast, lhs_t, rhs_t);
}

View File

@ -23,6 +23,7 @@ CORD type_to_cord(type_t *t) {
case VoidType: return "Void";
case MemoryType: return "Memory";
case BoolType: return "Bool";
case ByteType: return "Byte";
case CStringType: return "CString";
case TextType: return Match(t, TextType)->lang ? Match(t, TextType)->lang : "Text";
case BigIntType: return "Int";
@ -165,6 +166,7 @@ static PUREFUNC inline double type_min_magnitude(type_t *t)
{
switch (t->tag) {
case BoolType: return (double)false;
case ByteType: return 0;
case BigIntType: return -1./0.;
case IntType: {
switch (Match(t, IntType)->bits) {
@ -184,6 +186,7 @@ static PUREFUNC inline double type_max_magnitude(type_t *t)
{
switch (t->tag) {
case BoolType: return (double)true;
case ByteType: return (double)UINT8_MAX;
case BigIntType: return 1./0.;
case IntType: {
switch (Match(t, IntType)->bits) {
@ -396,7 +399,7 @@ PUREFUNC bool is_numeric_type(type_t *t)
PUREFUNC bool supports_optionals(type_t *t)
{
switch (t->tag) {
case BoolType: case CStringType: case BigIntType: case NumType: case TextType:
case BoolType: case ByteType: case CStringType: case BigIntType: case NumType: case TextType:
case ArrayType: case SetType: case TableType: case FunctionType: case ClosureType:
case PointerType: case IntType:
return true;
@ -411,6 +414,7 @@ PUREFUNC size_t type_size(type_t *t)
case UnknownType: case AbortType: case ReturnType: case VoidType: return 0;
case MemoryType: errx(1, "Memory has undefined type size");
case BoolType: return sizeof(bool);
case ByteType: return sizeof(uint8_t);
case CStringType: return sizeof(char*);
case BigIntType: return sizeof(Int_t);
case IntType: {
@ -489,6 +493,7 @@ PUREFUNC size_t type_align(type_t *t)
case UnknownType: case AbortType: case ReturnType: case VoidType: return 0;
case MemoryType: errx(1, "Memory has undefined type alignment");
case BoolType: return __alignof__(bool);
case ByteType: return __alignof__(uint8_t);
case CStringType: return __alignof__(char*);
case BigIntType: return __alignof__(Int_t);
case IntType: {

View File

@ -43,6 +43,7 @@ struct type_s {
VoidType,
MemoryType,
BoolType,
ByteType,
BigIntType,
IntType,
NumType,
@ -72,6 +73,7 @@ struct type_s {
struct {
enum { TYPE_IBITS8=8, TYPE_IBITS16=16, TYPE_IBITS32=32, TYPE_IBITS64=64 } bits;
} IntType;
struct {} ByteType;
struct {
enum { TYPE_NBITS32=32, TYPE_NBITS64=64 } bits;
} NumType;