Add channels and threads
This commit is contained in:
parent
3bf8ea8e12
commit
2ecb5fe885
4
Makefile
4
Makefile
@ -24,8 +24,8 @@ G=-ggdb
|
||||
O=-Og
|
||||
CFLAGS=$(CCONFIG) $(EXTRA) $(CWARN) $(G) $(O) $(OSFLAGS)
|
||||
LDLIBS=-lgc -lcord -lm -lunistring -ldl
|
||||
BUILTIN_OBJS=builtins/array.o builtins/bool.o builtins/nums.o builtins/functions.o builtins/integers.o \
|
||||
builtins/pointer.o builtins/memory.o builtins/text.o builtins/where.o builtins/c_string.o builtins/table.o \
|
||||
BUILTIN_OBJS=builtins/array.o builtins/bool.o builtins/channel.o builtins/nums.o builtins/functions.o builtins/integers.o \
|
||||
builtins/pointer.o builtins/memory.o builtins/text.o builtins/thread.o builtins/where.o builtins/c_string.o builtins/table.o \
|
||||
builtins/types.o builtins/util.o builtins/files.o builtins/range.o
|
||||
TESTS=$(patsubst %.tm,%.tm.testresult,$(wildcard test/*.tm))
|
||||
|
||||
|
5
ast.c
5
ast.c
@ -113,7 +113,7 @@ CORD ast_to_xml(ast_t *ast)
|
||||
T(StackReference, "<StackReference>%r</StackReference>", ast_to_xml(data.value))
|
||||
T(Min, "<Min>%r%r%r</Min>", ast_to_xml(data.lhs), ast_to_xml(data.rhs), optional_tagged("key", data.key))
|
||||
T(Max, "<Max>%r%r%r</Max>", ast_to_xml(data.lhs), ast_to_xml(data.rhs), optional_tagged("key", data.key))
|
||||
T(Array, "<Array>%r%r</Array>", optional_tagged_type("item-type", data.type), ast_list_to_xml(data.items))
|
||||
T(Array, "<Array>%r%r</Array>", optional_tagged_type("item-type", data.item_type), ast_list_to_xml(data.items))
|
||||
T(Set, "<Set>%r%r</Set>",
|
||||
optional_tagged_type("item-type", data.item_type),
|
||||
ast_list_to_xml(data.items))
|
||||
@ -121,6 +121,7 @@ CORD ast_to_xml(ast_t *ast)
|
||||
optional_tagged_type("key-type", data.key_type), optional_tagged_type("value-type", data.value_type),
|
||||
ast_list_to_xml(data.entries), optional_tagged("fallback", data.fallback))
|
||||
T(TableEntry, "<TableEntry>%r%r</TableEntry>", ast_to_xml(data.key), ast_to_xml(data.value))
|
||||
T(Channel, "<Channel>%r</Channel>", type_ast_to_xml(data.item_type))
|
||||
T(Comprehension, "<Comprehension>%r%r%r%r%r</Comprehension>", optional_tagged("expr", data.expr),
|
||||
ast_list_to_xml(data.vars), optional_tagged("iter", data.iter),
|
||||
optional_tagged("filter", data.filter))
|
||||
@ -172,6 +173,7 @@ CORD type_ast_to_xml(type_ast_t *t)
|
||||
data.is_optional ? "yes" : "no", data.is_stack ? "yes" : "no", data.is_readonly ? "yes" : "no", type_ast_to_xml(data.pointed))
|
||||
T(ArrayTypeAST, "<ArrayType>%r</ArrayType>", type_ast_to_xml(data.item))
|
||||
T(SetTypeAST, "<TableType>%r</TableType>", type_ast_to_xml(data.item))
|
||||
T(ChannelTypeAST, "<ChannelType>%r</ChannelType>", type_ast_to_xml(data.item))
|
||||
T(TableTypeAST, "<TableType>%r %r</TableType>", type_ast_to_xml(data.key), type_ast_to_xml(data.value))
|
||||
T(FunctionTypeAST, "<FunctionType>%r %r</FunctionType>", arg_list_to_xml(data.args), type_ast_to_xml(data.ret))
|
||||
#undef T
|
||||
@ -224,6 +226,7 @@ bool type_ast_eq(type_ast_t *x, type_ast_t *y)
|
||||
}
|
||||
case ArrayTypeAST: return type_ast_eq(Match(x, ArrayTypeAST)->item, Match(y, ArrayTypeAST)->item);
|
||||
case SetTypeAST: return type_ast_eq(Match(x, SetTypeAST)->item, Match(y, SetTypeAST)->item);
|
||||
case ChannelTypeAST: return type_ast_eq(Match(x, ChannelTypeAST)->item, Match(y, ChannelTypeAST)->item);
|
||||
case TableTypeAST: {
|
||||
auto tx = Match(x, TableTypeAST);
|
||||
auto ty = Match(y, TableTypeAST);
|
||||
|
10
ast.h
10
ast.h
@ -58,6 +58,7 @@ typedef enum {
|
||||
PointerTypeAST,
|
||||
ArrayTypeAST,
|
||||
SetTypeAST,
|
||||
ChannelTypeAST,
|
||||
TableTypeAST,
|
||||
FunctionTypeAST,
|
||||
} type_ast_e;
|
||||
@ -85,7 +86,7 @@ struct type_ast_s {
|
||||
} PointerTypeAST;
|
||||
struct {
|
||||
type_ast_t *item;
|
||||
} ArrayTypeAST;
|
||||
} ArrayTypeAST, ChannelTypeAST;
|
||||
struct {
|
||||
type_ast_t *key, *value;
|
||||
} TableTypeAST;
|
||||
@ -108,7 +109,7 @@ typedef enum {
|
||||
BinaryOp, UpdateAssign,
|
||||
Length, Not, Negative, HeapAllocate, StackReference,
|
||||
Min, Max,
|
||||
Array, Set, Table, TableEntry, Comprehension,
|
||||
Array, Channel, Set, Table, TableEntry, Comprehension,
|
||||
FunctionDef, Lambda,
|
||||
FunctionCall, MethodCall,
|
||||
Block,
|
||||
@ -178,9 +179,12 @@ struct ast_s {
|
||||
ast_t *lhs, *rhs, *key;
|
||||
} Min, Max;
|
||||
struct {
|
||||
type_ast_t *type;
|
||||
type_ast_t *item_type;
|
||||
ast_list_t *items;
|
||||
} Array;
|
||||
struct {
|
||||
type_ast_t *item_type;
|
||||
} Channel;
|
||||
struct {
|
||||
type_ast_t *item_type;
|
||||
ast_list_t *items;
|
||||
|
102
builtins/channel.c
Normal file
102
builtins/channel.c
Normal file
@ -0,0 +1,102 @@
|
||||
// Functions that operate on channels
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <gc.h>
|
||||
#include <gc/cord.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include "array.h"
|
||||
#include "functions.h"
|
||||
#include "halfsiphash.h"
|
||||
#include "types.h"
|
||||
#include "util.h"
|
||||
|
||||
public channel_t *Channel$new(void)
|
||||
{
|
||||
channel_t *channel = new(channel_t);
|
||||
channel->items = (array_t){};
|
||||
channel->mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
|
||||
channel->cond = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
|
||||
return channel;
|
||||
}
|
||||
|
||||
public void Channel$push(channel_t *channel, const void *item, int64_t padded_item_size)
|
||||
{
|
||||
(void)pthread_mutex_lock(&channel->mutex);
|
||||
Array$insert(&channel->items, item, 0, padded_item_size);
|
||||
(void)pthread_mutex_unlock(&channel->mutex);
|
||||
(void)pthread_cond_signal(&channel->cond);
|
||||
}
|
||||
|
||||
public void Channel$push_all(channel_t *channel, array_t to_push, int64_t padded_item_size)
|
||||
{
|
||||
(void)pthread_mutex_lock(&channel->mutex);
|
||||
Array$insert_all(&channel->items, to_push, 0, padded_item_size);
|
||||
(void)pthread_mutex_unlock(&channel->mutex);
|
||||
(void)pthread_cond_signal(&channel->cond);
|
||||
}
|
||||
|
||||
public void Channel$pop(channel_t *channel, void *out, int64_t item_size, int64_t padded_item_size)
|
||||
{
|
||||
(void)pthread_mutex_lock(&channel->mutex);
|
||||
while (channel->items.length == 0)
|
||||
pthread_cond_wait(&channel->cond, &channel->mutex);
|
||||
memcpy(out, channel->items.data, item_size);
|
||||
Array$remove(&channel->items, 1, 1, padded_item_size);
|
||||
(void)pthread_mutex_unlock(&channel->mutex);
|
||||
}
|
||||
|
||||
public array_t Channel$view(channel_t *channel)
|
||||
{
|
||||
(void)pthread_mutex_lock(&channel->mutex);
|
||||
ARRAY_INCREF(channel->items);
|
||||
array_t ret = channel->items;
|
||||
(void)pthread_mutex_unlock(&channel->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void Channel$clear(channel_t *channel)
|
||||
{
|
||||
(void)pthread_mutex_lock(&channel->mutex);
|
||||
Array$clear(&channel->items);
|
||||
(void)pthread_mutex_unlock(&channel->mutex);
|
||||
}
|
||||
|
||||
public uint32_t Channel$hash(const channel_t **channel, const TypeInfo *type)
|
||||
{
|
||||
(void)type;
|
||||
uint32_t hash;
|
||||
halfsiphash(*channel, sizeof(channel_t*), TOMO_HASH_KEY, (uint8_t*)&hash, sizeof(hash));
|
||||
return hash;
|
||||
}
|
||||
|
||||
public int32_t Channel$compare(const channel_t **x, const channel_t **y, const TypeInfo *type)
|
||||
{
|
||||
(void)type;
|
||||
return (*x > *y) - (*x < *y);
|
||||
}
|
||||
|
||||
bool Channel$equal(const channel_t **x, const channel_t **y, const TypeInfo *type)
|
||||
{
|
||||
(void)type;
|
||||
return (*x == *y);
|
||||
}
|
||||
|
||||
CORD Channel$as_text(const channel_t **channel, bool colorize, const TypeInfo *type)
|
||||
{
|
||||
const TypeInfo *item_type = type->ChannelInfo.item;
|
||||
if (!channel) {
|
||||
CORD typename = generic_as_text(NULL, false, item_type);
|
||||
return colorize ? CORD_asprintf("\x1b[34;1m|:%s|\x1b[m", typename) : CORD_all("|:", typename, "|");
|
||||
}
|
||||
CORD typename = generic_as_text(NULL, false, item_type);
|
||||
return CORD_asprintf(colorize ? "\x1b[34;1m|:%s|<%p>\x1b[m" : "|:%s|<%p>", typename, *channel);
|
||||
}
|
||||
|
||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
25
builtins/channel.h
Normal file
25
builtins/channel.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
// Functions that operate on channels (thread-safe arrays)
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <gc/cord.h>
|
||||
|
||||
#include "datatypes.h"
|
||||
#include "types.h"
|
||||
#include "util.h"
|
||||
|
||||
channel_t *Channel$new(void);
|
||||
void Channel$push(channel_t *channel, const void *item, int64_t padded_item_size);
|
||||
#define Channel$push_value(channel, item, padded_item_size) ({ __typeof(item) _item = item; Channel$push(channel, &_item, padded_item_size); })
|
||||
void Channel$push_all(channel_t *channel, array_t to_push, int64_t padded_item_size);
|
||||
void Channel$pop(channel_t *channel, void *out, int64_t item_size, int64_t padded_item_size);
|
||||
#define Channel$pop_value(channel, t, padded_item_size) ({ t _val; Channel$pop(channel, &_val, sizeof(t), padded_item_size); _val; })
|
||||
void Channel$clear(channel_t *channel);
|
||||
array_t Channel$view(channel_t *channel);
|
||||
uint32_t Channel$hash(const channel_t **channel, const TypeInfo *type);
|
||||
int32_t Channel$compare(const channel_t **x, const channel_t **y, const TypeInfo *type);
|
||||
bool Channel$equal(const channel_t **x, const channel_t **y, const TypeInfo *type);
|
||||
CORD Channel$as_text(const channel_t **channel, bool colorize, const TypeInfo *type);
|
||||
|
||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define ARRAY_LENGTH_BITS 42
|
||||
#define ARRAY_FREE_BITS 6
|
||||
@ -57,4 +58,10 @@ typedef struct Range_s {
|
||||
int64_t first, last, step;
|
||||
} Range_t;
|
||||
|
||||
typedef struct {
|
||||
array_t items;
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
} channel_t;
|
||||
|
||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include "array.h"
|
||||
#include "bool.h"
|
||||
#include "channel.h"
|
||||
#include "files.h"
|
||||
#include "functions.h"
|
||||
#include "halfsiphash.h"
|
||||
@ -101,6 +102,7 @@ public uint32_t generic_hash(const void *obj, const TypeInfo *type)
|
||||
case PointerInfo: case FunctionInfo: return Pointer$hash(obj, type);
|
||||
case TextInfo: return Text$hash(obj);
|
||||
case ArrayInfo: return Array$hash(obj, type);
|
||||
case ChannelInfo: return Channel$hash((const channel_t**)obj, type);
|
||||
case TableInfo: return Table$hash(obj, type);
|
||||
case EmptyStruct: return 0;
|
||||
case CustomInfo:
|
||||
@ -122,6 +124,7 @@ public int32_t generic_compare(const void *x, const void *y, const TypeInfo *typ
|
||||
case PointerInfo: case FunctionInfo: return Pointer$compare(x, y, type);
|
||||
case TextInfo: return Text$compare(x, y);
|
||||
case ArrayInfo: return Array$compare(x, y, type);
|
||||
case ChannelInfo: return Channel$compare((const channel_t**)x, (const channel_t**)y, type);
|
||||
case TableInfo: return Table$compare(x, y, type);
|
||||
case EmptyStruct: return 0;
|
||||
case CustomInfo:
|
||||
@ -140,6 +143,7 @@ public bool generic_equal(const void *x, const void *y, const TypeInfo *type)
|
||||
case PointerInfo: case FunctionInfo: return Pointer$equal(x, y, type);
|
||||
case TextInfo: return Text$equal(x, y);
|
||||
case ArrayInfo: return Array$equal(x, y, type);
|
||||
case ChannelInfo: return Channel$equal((const channel_t**)x, (const channel_t**)y, type);
|
||||
case TableInfo: return Table$equal(x, y, type);
|
||||
case EmptyStruct: return true;
|
||||
case CustomInfo:
|
||||
@ -159,6 +163,7 @@ public CORD generic_as_text(const void *obj, bool colorize, const TypeInfo *type
|
||||
case FunctionInfo: return Func$as_text(obj, colorize, type);
|
||||
case TextInfo: return Text$as_text(obj, colorize, type);
|
||||
case ArrayInfo: return Array$as_text(obj, colorize, type);
|
||||
case ChannelInfo: return Channel$as_text((const channel_t**)obj, colorize, type);
|
||||
case TableInfo: return Table$as_text(obj, colorize, type);
|
||||
case TypeInfoInfo: return Type$as_text(obj, colorize, type);
|
||||
case EmptyStruct: return colorize ? CORD_all("\x1b[0;1m", type->EmptyStruct.name, "\x1b[m()") : CORD_all(type->EmptyStruct.name, "()");
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "datatypes.h"
|
||||
#include "types.h"
|
||||
|
||||
extern uint8_t TOMO_HASH_KEY[8];
|
||||
@ -26,6 +27,7 @@ uint32_t generic_hash(const void *obj, const TypeInfo *type);
|
||||
int32_t generic_compare(const void *x, const void *y, const TypeInfo *type);
|
||||
bool generic_equal(const void *x, const void *y, const TypeInfo *type);
|
||||
CORD generic_as_text(const void *obj, bool colorize, const TypeInfo *type);
|
||||
closure_t spawn(closure_t fn);
|
||||
bool pop_flag(char **argv, int *i, const char *flag, CORD *result);
|
||||
|
||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
||||
|
57
builtins/thread.c
Normal file
57
builtins/thread.c
Normal file
@ -0,0 +1,57 @@
|
||||
// Logic for the Thread type, representing a pthread
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <gc.h>
|
||||
#include <gc/cord.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include "array.h"
|
||||
#include "functions.h"
|
||||
#include "halfsiphash.h"
|
||||
#include "types.h"
|
||||
#include "util.h"
|
||||
|
||||
public pthread_t *Thread$new(closure_t fn)
|
||||
{
|
||||
pthread_t *thread = new(pthread_t);
|
||||
pthread_create(thread, NULL, fn.fn, fn.userdata);
|
||||
return thread;
|
||||
}
|
||||
|
||||
public void Thread$join(pthread_t *thread)
|
||||
{
|
||||
pthread_join(*thread, NULL);
|
||||
}
|
||||
|
||||
public void Thread$cancel(pthread_t *thread)
|
||||
{
|
||||
pthread_cancel(*thread);
|
||||
}
|
||||
|
||||
public void Thread$detach(pthread_t *thread)
|
||||
{
|
||||
pthread_detach(*thread);
|
||||
}
|
||||
|
||||
CORD Thread$as_text(const pthread_t **thread, bool colorize, const TypeInfo *type)
|
||||
{
|
||||
(void)type;
|
||||
if (!thread) {
|
||||
return colorize ? "\x1b[34;1mThread\x1b[m" : "Thread";
|
||||
}
|
||||
return CORD_asprintf(colorize ? "\x1b[34;1mThread(%p)\x1b[m" : "Thread(%p)", *thread);
|
||||
}
|
||||
|
||||
public const TypeInfo Thread = {
|
||||
.size=sizeof(pthread_t*), .align=__alignof(pthread_t*),
|
||||
.tag=CustomInfo,
|
||||
.CustomInfo={.as_text=(void*)Thread$as_text},
|
||||
};
|
||||
|
||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
21
builtins/thread.h
Normal file
21
builtins/thread.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
// Logic for the Thread type, representing a pthread
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <gc/cord.h>
|
||||
|
||||
#include "datatypes.h"
|
||||
#include "types.h"
|
||||
#include "util.h"
|
||||
|
||||
pthread_t *Thread$new(closure_t fn);
|
||||
void Thread$cancel(pthread_t *thread);
|
||||
void Thread$join(pthread_t *thread);
|
||||
void Thread$detach(pthread_t *thread);
|
||||
CORD Thread$as_text(const pthread_t **thread, bool colorize, const TypeInfo *type);
|
||||
|
||||
extern TypeInfo Thread;
|
||||
|
||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
@ -12,6 +12,7 @@
|
||||
#include "array.h"
|
||||
#include "bool.h"
|
||||
#include "c_string.h"
|
||||
#include "channel.h"
|
||||
#include "datatypes.h"
|
||||
#include "functions.h"
|
||||
#include "halfsiphash.h"
|
||||
@ -23,6 +24,7 @@
|
||||
#include "range.h"
|
||||
#include "table.h"
|
||||
#include "text.h"
|
||||
#include "thread.h"
|
||||
#include "types.h"
|
||||
|
||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
||||
|
@ -18,7 +18,7 @@ typedef CORD (*str_fn_t)(const void*, bool, const struct TypeInfo*);
|
||||
typedef struct TypeInfo {
|
||||
int64_t size, align;
|
||||
struct { // Anonymous tagged union for convenience
|
||||
enum { CustomInfo, PointerInfo, TextInfo, ArrayInfo, TableInfo, FunctionInfo, TypeInfoInfo, OpaqueInfo, EmptyStruct } tag;
|
||||
enum { CustomInfo, PointerInfo, TextInfo, ArrayInfo, ChannelInfo, TableInfo, FunctionInfo, TypeInfoInfo, OpaqueInfo, EmptyStruct } tag;
|
||||
union {
|
||||
struct {
|
||||
equal_fn_t equal;
|
||||
@ -36,7 +36,7 @@ typedef struct TypeInfo {
|
||||
} TextInfo;
|
||||
struct {
|
||||
const struct TypeInfo *item;
|
||||
} ArrayInfo;
|
||||
} ArrayInfo, ChannelInfo;
|
||||
struct {
|
||||
const struct TypeInfo *key, *value;
|
||||
} TableInfo;
|
||||
@ -60,6 +60,8 @@ typedef struct TypeInfo {
|
||||
.tag=ArrayInfo, .ArrayInfo.item=item_info})
|
||||
#define $SetInfo(item_info) &((TypeInfo){.size=sizeof(table_t), .align=__alignof__(table_t), \
|
||||
.tag=TableInfo, .TableInfo.key=item_info, .TableInfo.value=&$Void})
|
||||
#define $ChannelInfo(item_info) &((TypeInfo){.size=sizeof(channel_t), .align=__alignof__(channel_t), \
|
||||
.tag=ChannelInfo, .ChannelInfo.item=item_info})
|
||||
#define $TableInfo(key_expr, value_expr) &((TypeInfo){.size=sizeof(table_t), .align=__alignof__(table_t), \
|
||||
.tag=TableInfo, .TableInfo.key=key_expr, .TableInfo.value=value_expr})
|
||||
#define $FunctionInfo(typestr) &((TypeInfo){.size=sizeof(void*), .align=__alignof__(void*), \
|
||||
|
45
compile.c
45
compile.c
@ -134,6 +134,7 @@ CORD compile_type(type_t *t)
|
||||
}
|
||||
case ArrayType: return "array_t";
|
||||
case SetType: return "table_t";
|
||||
case ChannelType: return "channel_t*";
|
||||
case TableType: return "table_t";
|
||||
case FunctionType: {
|
||||
auto fn = Match(t, FunctionType);
|
||||
@ -148,6 +149,8 @@ CORD compile_type(type_t *t)
|
||||
case PointerType: return CORD_cat(compile_type(Match(t, PointerType)->pointed), "*");
|
||||
case StructType: {
|
||||
auto s = Match(t, StructType);
|
||||
if (t == THREAD_TYPE)
|
||||
return "pthread_t*";
|
||||
return CORD_all("struct ", namespace_prefix(s->env->libname, s->env->namespace->parent), s->name, "_s");
|
||||
}
|
||||
case EnumType: {
|
||||
@ -1103,6 +1106,7 @@ CORD expr_as_text(env_t *env, CORD expr, type_t *t, CORD color)
|
||||
}
|
||||
case ArrayType: return CORD_asprintf("Array$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t));
|
||||
case SetType: return CORD_asprintf("Table$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t));
|
||||
case ChannelType: return CORD_asprintf("Channel$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t));
|
||||
case TableType: return CORD_asprintf("Table$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t));
|
||||
case FunctionType: case ClosureType: return CORD_asprintf("Func$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t));
|
||||
case PointerType: return CORD_asprintf("Pointer$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t));
|
||||
@ -1742,6 +1746,12 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
return code;
|
||||
}
|
||||
}
|
||||
case Channel: {
|
||||
type_t *item_t = parse_type_ast(env, Match(ast, Channel)->item_type);
|
||||
if (!can_send_over_channel(item_t))
|
||||
code_err(ast, "This item type can't be sent over a channel because it contains reference to memory that may not be thread-safe.");
|
||||
return "Channel$new()";
|
||||
}
|
||||
case Table: {
|
||||
auto table = Match(ast, Table);
|
||||
if (!table->entries) {
|
||||
@ -1952,7 +1962,7 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
case ArrayType: {
|
||||
// TODO: check for readonly
|
||||
type_t *item_t = Match(self_value_t, ArrayType)->item_type;
|
||||
CORD padded_item_size = CORD_asprintf("%ld", padded_type_size(Match(self_value_t, ArrayType)->item_type));
|
||||
CORD padded_item_size = CORD_asprintf("%ld", padded_type_size(item_t));
|
||||
if (streq(call->name, "insert")) {
|
||||
CORD self = compile_to_pointer_depth(env, call->self, 1, false);
|
||||
arg_t *arg_spec = new(arg_t, .name="item", .type=item_t,
|
||||
@ -1979,7 +1989,7 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
CORD self = compile_to_pointer_depth(env, call->self, 0, false);
|
||||
arg_t *arg_spec = new(arg_t, .name="count", .type=Type(IntType, .bits=64),
|
||||
.next=new(arg_t, .name="weights", .type=Type(ArrayType, .item_type=Type(NumType, .bits=64)),
|
||||
.default_val=FakeAST(Array, .type=new(type_ast_t, .tag=VarTypeAST, .__data.VarTypeAST.name="Num"))));
|
||||
.default_val=FakeAST(Array, .item_type=new(type_ast_t, .tag=VarTypeAST, .__data.VarTypeAST.name="Num"))));
|
||||
return CORD_all("Array$sample(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
|
||||
padded_item_size, ")");
|
||||
} else if (streq(call->name, "shuffle")) {
|
||||
@ -2124,6 +2134,33 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
", ", compile_type_info(env, self_value_t), ")");
|
||||
} else code_err(ast, "There is no '%s' method for tables", call->name);
|
||||
}
|
||||
case ChannelType: {
|
||||
type_t *item_t = Match(self_value_t, ChannelType)->item_type;
|
||||
CORD padded_item_size = CORD_asprintf("%ld", padded_type_size(item_t));
|
||||
if (streq(call->name, "push")) {
|
||||
CORD self = compile_to_pointer_depth(env, call->self, 0, false);
|
||||
arg_t *arg_spec = new(arg_t, .name="item", .type=item_t);
|
||||
return CORD_all("Channel$push_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
|
||||
padded_item_size, ")");
|
||||
} else if (streq(call->name, "push_all")) {
|
||||
CORD self = compile_to_pointer_depth(env, call->self, 0, false);
|
||||
arg_t *arg_spec = new(arg_t, .name="to_push", .type=Type(ArrayType, .item_type=item_t));
|
||||
return CORD_all("Channel$push_all(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",
|
||||
padded_item_size, ")");
|
||||
} else if (streq(call->name, "pop")) {
|
||||
CORD self = compile_to_pointer_depth(env, call->self, 0, false);
|
||||
(void)compile_arguments(env, ast, NULL, call->args);
|
||||
return CORD_all("Channel$pop_value(", self, ", ", compile_type(item_t), ", ", padded_item_size, ")");
|
||||
} else if (streq(call->name, "clear")) {
|
||||
CORD self = compile_to_pointer_depth(env, call->self, 0, false);
|
||||
(void)compile_arguments(env, ast, NULL, call->args);
|
||||
return CORD_all("Channel$clear(", self, ")");
|
||||
} else if (streq(call->name, "view")) {
|
||||
CORD self = compile_to_pointer_depth(env, call->self, 0, false);
|
||||
(void)compile_arguments(env, ast, NULL, call->args);
|
||||
return CORD_all("Channel$view(", self, ")");
|
||||
} else code_err(ast, "There is no '%s' method for channels", call->name);
|
||||
}
|
||||
case TableType: {
|
||||
auto table = Match(self_value_t, TableType);
|
||||
if (streq(call->name, "get")) {
|
||||
@ -2499,6 +2536,10 @@ CORD compile_type_info(env_t *env, type_t *t)
|
||||
type_t *item_type = Match(t, SetType)->item_type;
|
||||
return CORD_all("$SetInfo(", compile_type_info(env, item_type), ")");
|
||||
}
|
||||
case ChannelType: {
|
||||
type_t *item_t = Match(t, ChannelType)->item_type;
|
||||
return CORD_asprintf("$ChannelInfo(%r)", compile_type_info(env, item_t));
|
||||
}
|
||||
case TableType: {
|
||||
type_t *key_type = Match(t, TableType)->key_type;
|
||||
type_t *value_type = Match(t, TableType)->value_type;
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
type_t *TEXT_TYPE = NULL;
|
||||
type_t *RANGE_TYPE = NULL;
|
||||
public type_t *THREAD_TYPE = NULL;
|
||||
|
||||
env_t *new_compilation_unit(CORD *libname)
|
||||
{
|
||||
@ -68,6 +69,11 @@ env_t *new_compilation_unit(CORD *libname)
|
||||
.next=new(arg_t, .name="step", .type=INT_TYPE, .default_val=FakeAST(Int, .i=1, .bits=64)))));
|
||||
}
|
||||
|
||||
{
|
||||
env_t *thread_env = namespace_env(env, "Thread");
|
||||
THREAD_TYPE = Type(StructType, .name="Thread", .env=thread_env, .opaque=true);
|
||||
}
|
||||
|
||||
struct {
|
||||
const char *name;
|
||||
type_t *type;
|
||||
@ -215,6 +221,12 @@ env_t *new_compilation_unit(CORD *libname)
|
||||
{"upper", "Text$upper", "func(text:Text)->Text"},
|
||||
{"without", "Text$without", "func(text:Text, target:Text, where=Where.Anywhere)->Text"},
|
||||
)},
|
||||
{"Thread", THREAD_TYPE, "pthread_t*", "Thread", TypedArray(ns_entry_t,
|
||||
{"new", "Thread$new", "func(fn:func()->Void)->Thread"},
|
||||
{"cancel", "Thread$cancel", "func(thread:Thread)->Void"},
|
||||
{"join", "Thread$join", "func(thread:Thread)->Void"},
|
||||
{"detach", "Thread$detach", "func(thread:Thread)->Void"},
|
||||
)},
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(global_types)/sizeof(global_types[0]); i++) {
|
||||
|
@ -75,5 +75,6 @@ binding_t *get_namespace_binding(env_t *env, ast_t *self, const char *name);
|
||||
#define code_err(ast, ...) compiler_err((ast)->file, (ast)->start, (ast)->end, __VA_ARGS__)
|
||||
extern type_t *TEXT_TYPE;
|
||||
extern type_t *RANGE_TYPE;
|
||||
extern type_t *THREAD_TYPE;
|
||||
|
||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
||||
|
38
parse.c
38
parse.c
@ -82,6 +82,8 @@ static ast_t *parse_index_suffix(parse_ctx_t *ctx, ast_t *lhs);
|
||||
static ast_t *parse_comprehension_suffix(parse_ctx_t *ctx, ast_t *lhs);
|
||||
static ast_t *parse_optional_suffix(parse_ctx_t *ctx, ast_t *lhs);
|
||||
static arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos, bool allow_unnamed);
|
||||
static PARSER(parse_array);
|
||||
static PARSER(parse_channel);
|
||||
static PARSER(parse_for);
|
||||
static PARSER(parse_do);
|
||||
static PARSER(parse_while);
|
||||
@ -371,10 +373,12 @@ const char *get_id(const char **inout) {
|
||||
}
|
||||
|
||||
bool comment(const char **pos) {
|
||||
if (!match(pos, "//"))
|
||||
if ((*pos)[0] == '/' && (*pos)[1] == '/' && (*pos)[2] != '!') {
|
||||
*pos += 2 + strcspn(*pos, "\r\n");
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
some_not(pos, "\r\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool indent(parse_ctx_t *ctx, const char **out) {
|
||||
@ -547,6 +551,15 @@ type_ast_t *parse_array_type(parse_ctx_t *ctx, const char *pos) {
|
||||
return NewTypeAST(ctx->file, start, pos, ArrayTypeAST, .item=type);
|
||||
}
|
||||
|
||||
type_ast_t *parse_channel_type(parse_ctx_t *ctx, const char *pos) {
|
||||
const char *start = pos;
|
||||
if (!match(&pos, "|")) return NULL;
|
||||
type_ast_t *type = expect(ctx, start, &pos, parse_type,
|
||||
"I couldn't parse a channel item type after this point");
|
||||
expect_closing(ctx, &pos, "|", "I wasn't able to parse the rest of this channel");
|
||||
return NewTypeAST(ctx->file, start, pos, ChannelTypeAST, .item=type);
|
||||
}
|
||||
|
||||
type_ast_t *parse_pointer_type(parse_ctx_t *ctx, const char *pos) {
|
||||
const char *start = pos;
|
||||
bool is_stack;
|
||||
@ -588,6 +601,7 @@ type_ast_t *parse_type(parse_ctx_t *ctx, const char *pos) {
|
||||
bool success = (false
|
||||
|| (type=parse_pointer_type(ctx, pos))
|
||||
|| (type=parse_array_type(ctx, pos))
|
||||
|| (type=parse_channel_type(ctx, pos))
|
||||
|| (type=parse_table_type(ctx, pos))
|
||||
|| (type=parse_set_type(ctx, pos))
|
||||
|| (type=parse_type_name(ctx, pos))
|
||||
@ -662,6 +676,14 @@ static inline bool match_separator(const char **pos) { // Either comma or newlin
|
||||
}
|
||||
}
|
||||
|
||||
PARSER(parse_channel) {
|
||||
const char *start = pos;
|
||||
if (!match(&pos, "|:")) return NULL;
|
||||
type_ast_t *item_type = expect(ctx, pos-1, &pos, parse_type, "I couldn't parse a type for this channel");
|
||||
expect_closing(ctx, &pos, "|", "I wasn't able to parse the rest of this channel");
|
||||
return NewAST(ctx->file, start, pos, Channel, .item_type=item_type);
|
||||
}
|
||||
|
||||
PARSER(parse_array) {
|
||||
const char *start = pos;
|
||||
if (!match(&pos, "[")) return NULL;
|
||||
@ -696,7 +718,7 @@ PARSER(parse_array) {
|
||||
parser_err(ctx, start, pos, "Empty arrays must specify what type they would contain (e.g. [:Int])");
|
||||
|
||||
REVERSE_LIST(items);
|
||||
return NewAST(ctx->file, start, pos, Array, .type=item_type, .items=items);
|
||||
return NewAST(ctx->file, start, pos, Array, .item_type=item_type, .items=items);
|
||||
}
|
||||
|
||||
PARSER(parse_table) {
|
||||
@ -765,7 +787,7 @@ PARSER(parse_table) {
|
||||
whitespace(&pos);
|
||||
expect_closing(ctx, &pos, "}", "I wasn't able to parse the rest of this table");
|
||||
|
||||
return NewAST(ctx->file, start, pos, Table, .key_type=key_type, .value_type=value_type, .entries=entries, .fallback=fallback);
|
||||
return NewAST(ctx->file, start, pos, Table, .key_type=key_type, .value_type=value_type, .entries=entries);
|
||||
}
|
||||
|
||||
PARSER(parse_set) {
|
||||
@ -1002,6 +1024,9 @@ PARSER(parse_when) {
|
||||
}
|
||||
}
|
||||
|
||||
tmp = pos;
|
||||
if (!match(&tmp, ":"))
|
||||
parser_err(ctx, tmp, tmp, "I expected a colon ':' after this clause");
|
||||
ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'when' clause");
|
||||
clauses = new(when_clause_t, .tag_name=tag_name, .args=args, .body=body, .next=clauses);
|
||||
tmp = pos;
|
||||
@ -1342,6 +1367,7 @@ PARSER(parse_term_no_suffix) {
|
||||
|| (term=parse_set(ctx, pos))
|
||||
|| (term=parse_var(ctx, pos))
|
||||
|| (term=parse_array(ctx, pos))
|
||||
|| (term=parse_channel(ctx, pos))
|
||||
|| (term=parse_reduction(ctx, pos))
|
||||
|| (term=parse_pass(ctx, pos))
|
||||
|| (term=parse_defer(ctx, pos))
|
||||
@ -2109,7 +2135,7 @@ PARSER(parse_doctest) {
|
||||
|
||||
PARSER(parse_say) {
|
||||
const char *start = pos;
|
||||
if (!match(&pos, "|")) return NULL;
|
||||
if (!match(&pos, "//!")) return NULL;
|
||||
spaces(&pos);
|
||||
|
||||
ast_list_t *chunks = NULL;
|
||||
|
@ -1,6 +1,6 @@
|
||||
func main():
|
||||
>> str := "Hello Amélie!"
|
||||
| Testing strings like {str}
|
||||
//! Testing strings like {str}
|
||||
|
||||
>> str:upper()
|
||||
= "HELLO AMÉLIE!"
|
||||
|
32
test/threads.tm
Normal file
32
test/threads.tm
Normal file
@ -0,0 +1,32 @@
|
||||
enum Job(Increment(x:Int), Decrement(x:Int))
|
||||
|
||||
func main():
|
||||
jobs := |:Job|
|
||||
results := |:Int|
|
||||
>> thread := Thread.new(func():
|
||||
//! In another thread!
|
||||
while yes:
|
||||
when jobs:pop() is Increment(x):
|
||||
>> results:push(x+1)
|
||||
is Decrement(x):
|
||||
>> results:push(x-1)
|
||||
)
|
||||
|
||||
>> jobs:push(Increment(5))
|
||||
>> jobs:push(Decrement(100))
|
||||
|
||||
>> results:pop()
|
||||
= 6
|
||||
|
||||
>> jobs:push(Increment(1000))
|
||||
>> results:pop()
|
||||
= 99
|
||||
|
||||
>> results:pop()
|
||||
= 1001
|
||||
|
||||
//! Canceling...
|
||||
>> thread:cancel()
|
||||
//! Joining...
|
||||
>> thread:join()
|
||||
//! Done!
|
28
typecheck.c
28
typecheck.c
@ -65,6 +65,17 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast)
|
||||
padded_type_size(item_t), ARRAY_MAX_STRIDE);
|
||||
return Type(SetType, .item_type=item_t);
|
||||
}
|
||||
case ChannelTypeAST: {
|
||||
type_ast_t *item_type = Match(ast, ChannelTypeAST)->item;
|
||||
type_t *item_t = parse_type_ast(env, item_type);
|
||||
if (!item_t) code_err(item_type, "I can't figure out what this type is.");
|
||||
if (!can_send_over_channel(item_t))
|
||||
code_err(ast, "This item type can't be sent over a channel because it contains reference to memory that may not be thread-safe.");
|
||||
if (padded_type_size(item_t) > ARRAY_MAX_STRIDE)
|
||||
code_err(ast, "This channel holds items that take up %ld bytes, but the maximum supported size is %ld bytes.",
|
||||
padded_type_size(item_t), ARRAY_MAX_STRIDE);
|
||||
return Type(ChannelType, .item_type=item_t);
|
||||
}
|
||||
case TableTypeAST: {
|
||||
type_ast_t *key_type_ast = Match(ast, TableTypeAST)->key;
|
||||
type_t *key_type = parse_type_ast(env, key_type_ast);
|
||||
@ -515,8 +526,8 @@ type_t *get_type(env_t *env, ast_t *ast)
|
||||
case Array: {
|
||||
auto array = Match(ast, Array);
|
||||
type_t *item_type = NULL;
|
||||
if (array->type) {
|
||||
item_type = parse_type_ast(env, array->type);
|
||||
if (array->item_type) {
|
||||
item_type = parse_type_ast(env, array->item_type);
|
||||
} else if (array->items) {
|
||||
for (ast_list_t *item = array->items; item; item = item->next) {
|
||||
ast_t *item_ast = item->ast;
|
||||
@ -571,6 +582,11 @@ type_t *get_type(env_t *env, ast_t *ast)
|
||||
code_err(ast, "Sets cannot hold stack references because the set may outlive the reference's stack frame.");
|
||||
return Type(SetType, .item_type=item_type);
|
||||
}
|
||||
case Channel: {
|
||||
auto channel = Match(ast, Channel);
|
||||
type_t *item_type = parse_type_ast(env, channel->item_type);
|
||||
return Type(ChannelType, .item_type=item_type);
|
||||
}
|
||||
case Table: {
|
||||
auto table = Match(ast, Table);
|
||||
type_t *key_type = NULL, *value_type = NULL;
|
||||
@ -727,6 +743,14 @@ type_t *get_type(env_t *env, ast_t *ast)
|
||||
else if (streq(call->name, "is_superset_of")) return Type(BoolType);
|
||||
else code_err(ast, "There is no '%s' method for sets", call->name);
|
||||
}
|
||||
case ChannelType: {
|
||||
if (streq(call->name, "push")) return Type(VoidType);
|
||||
else if (streq(call->name, "push_all")) return Type(VoidType);
|
||||
else if (streq(call->name, "pop")) return Match(self_value_t, ChannelType)->item_type;
|
||||
else if (streq(call->name, "clear")) return Type(VoidType);
|
||||
else if (streq(call->name, "view")) return Type(ArrayType, .item_type=Match(self_value_t, ChannelType)->item_type);
|
||||
else code_err(ast, "There is no '%s' method for arrays", call->name);
|
||||
}
|
||||
case TableType: {
|
||||
auto table = Match(self_value_t, TableType);
|
||||
if (streq(call->name, "get")) return table->value_type;
|
||||
|
35
types.c
35
types.c
@ -25,6 +25,10 @@ CORD type_to_cord(type_t *t) {
|
||||
auto array = Match(t, ArrayType);
|
||||
return CORD_asprintf("[%r]", type_to_cord(array->item_type));
|
||||
}
|
||||
case ChannelType: {
|
||||
auto array = Match(t, ChannelType);
|
||||
return CORD_asprintf("||%r", type_to_cord(array->item_type));
|
||||
}
|
||||
case TableType: {
|
||||
auto table = Match(t, TableType);
|
||||
return CORD_asprintf("{%r:%r}", type_to_cord(table->key_type), type_to_cord(table->value_type));
|
||||
@ -205,6 +209,7 @@ bool has_heap_memory(type_t *t)
|
||||
{
|
||||
switch (t->tag) {
|
||||
case ArrayType: return true;
|
||||
case ChannelType: return true;
|
||||
case TableType: return true;
|
||||
case SetType: return true;
|
||||
case PointerType: return true;
|
||||
@ -226,6 +231,31 @@ bool has_heap_memory(type_t *t)
|
||||
}
|
||||
}
|
||||
|
||||
bool can_send_over_channel(type_t *t)
|
||||
{
|
||||
switch (t->tag) {
|
||||
case ArrayType: return true;
|
||||
case ChannelType: return true;
|
||||
case TableType: return true;
|
||||
case PointerType: return false;
|
||||
case StructType: {
|
||||
for (arg_t *field = Match(t, StructType)->fields; field; field = field->next) {
|
||||
if (!can_send_over_channel(field->type))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case EnumType: {
|
||||
for (tag_t *tag = Match(t, EnumType)->tags; tag; tag = tag->next) {
|
||||
if (tag->type && !can_send_over_channel(tag->type))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool has_stack_memory(type_t *t)
|
||||
{
|
||||
switch (t->tag) {
|
||||
@ -313,6 +343,7 @@ bool can_leave_uninitialized(type_t *t)
|
||||
case PointerType: return Match(t, PointerType)->is_optional;
|
||||
case ArrayType: case IntType: case NumType: case BoolType:
|
||||
return true;
|
||||
case ChannelType: return false;
|
||||
case StructType: {
|
||||
for (arg_t *field = Match(t, StructType)->fields; field; field = field->next) {
|
||||
if (!can_leave_uninitialized(field->type))
|
||||
@ -335,6 +366,7 @@ static bool _can_have_cycles(type_t *t, table_t *seen)
|
||||
{
|
||||
switch (t->tag) {
|
||||
case ArrayType: return _can_have_cycles(Match(t, ArrayType)->item_type, seen);
|
||||
case ChannelType: return _can_have_cycles(Match(t, ChannelType)->item_type, seen);
|
||||
case TableType: {
|
||||
auto table = Match(t, TableType);
|
||||
return _can_have_cycles(table->key_type, seen) || _can_have_cycles(table->value_type, seen);
|
||||
@ -375,6 +407,7 @@ type_t *replace_type(type_t *t, type_t *target, type_t *replacement)
|
||||
switch (t->tag) {
|
||||
case ArrayType: return REPLACED_MEMBER(t, ArrayType, item_type);
|
||||
case SetType: return REPLACED_MEMBER(t, SetType, item_type);
|
||||
case ChannelType: return REPLACED_MEMBER(t, ChannelType, item_type);
|
||||
case TableType: {
|
||||
t = REPLACED_MEMBER(t, TableType, key_type);
|
||||
t = REPLACED_MEMBER(t, TableType, value_type);
|
||||
@ -420,6 +453,7 @@ size_t type_size(type_t *t)
|
||||
case TextType: return sizeof(CORD);
|
||||
case ArrayType: return sizeof(array_t);
|
||||
case SetType: return sizeof(table_t);
|
||||
case ChannelType: return sizeof(channel_t*);
|
||||
case TableType: return sizeof(table_t);
|
||||
case FunctionType: return sizeof(void*);
|
||||
case ClosureType: return sizeof(struct {void *fn, *userdata;});
|
||||
@ -473,6 +507,7 @@ size_t type_align(type_t *t)
|
||||
case TextType: return __alignof__(CORD);
|
||||
case SetType: return __alignof__(table_t);
|
||||
case ArrayType: return __alignof__(array_t);
|
||||
case ChannelType: return __alignof__(channel_t*);
|
||||
case TableType: return __alignof__(table_t);
|
||||
case FunctionType: return __alignof__(void*);
|
||||
case ClosureType: return __alignof__(struct {void *fn, *userdata;});
|
||||
|
4
types.h
4
types.h
@ -48,6 +48,7 @@ struct type_s {
|
||||
CStringType,
|
||||
TextType,
|
||||
ArrayType,
|
||||
ChannelType,
|
||||
SetType,
|
||||
TableType,
|
||||
FunctionType,
|
||||
@ -78,7 +79,7 @@ struct type_s {
|
||||
} TextType;
|
||||
struct {
|
||||
type_t *item_type;
|
||||
} ArrayType;
|
||||
} ArrayType, ChannelType;
|
||||
struct {
|
||||
type_t *item_type;
|
||||
} SetType;
|
||||
@ -134,6 +135,7 @@ typedef enum {NUM_PRECISION_EQUAL, NUM_PRECISION_LESS, NUM_PRECISION_MORE, NUM_P
|
||||
precision_cmp_e compare_precision(type_t *a, type_t *b);
|
||||
bool has_heap_memory(type_t *t);
|
||||
bool has_stack_memory(type_t *t);
|
||||
bool can_send_over_channel(type_t *t);
|
||||
bool can_promote(type_t *actual, type_t *needed);
|
||||
bool can_leave_uninitialized(type_t *t);
|
||||
bool can_have_cycles(type_t *t);
|
||||
|
Loading…
Reference in New Issue
Block a user