Improved syntax for optionals

This commit is contained in:
Bruce Hill 2024-04-30 13:18:47 -04:00
parent 3c0a8f0b89
commit 2e27b88c1b
12 changed files with 74 additions and 31 deletions

1
ast.c
View File

@ -140,6 +140,7 @@ CORD ast_to_xml(ast_t *ast)
T(LangDef, "<LangDef name=\"%s\">%r</LangDef>", data.name, ast_to_xml(data.namespace))
T(Index, "<Index>%r%r</Index>", optional_tagged("indexed", data.indexed), optional_tagged("index", data.index))
T(FieldAccess, "<FieldAccess field=\"%s\">%r</FieldAccess>", data.field, ast_to_xml(data.fielded))
T(Optional, "<Optional>%r</Optional>", ast_to_xml(data.value))
T(DocTest, "<DocTest>%r<output>%r</output></DocTest>", optional_tagged("expression", data.expr), xml_escape(data.output))
T(Use, "<Use>%r</Use>", xml_escape(data.raw_path))
T(LinkerDirective, "<LinkerDirective>%r</LinkerDirective>", xml_escape(data.directive))

5
ast.h
View File

@ -110,7 +110,7 @@ typedef enum {
Return,
Extern,
StructDef, EnumDef, LangDef,
Index, FieldAccess,
Index, FieldAccess, Optional,
DocTest,
Use,
LinkerDirective,
@ -255,6 +255,9 @@ struct ast_s {
ast_t *fielded;
const char *field;
} FieldAccess;
struct {
ast_t *value;
} Optional;
struct {
ast_t *expr;
const char *output;

View File

@ -22,7 +22,8 @@ public CORD Pointer$as_text(const void *x, bool colorize, const TypeInfo *type)
auto ptr_info = type->PointerInfo;
if (!x) {
CORD typename = generic_as_text(NULL, false, ptr_info.pointed);
return colorize ? CORD_asprintf("\x1b[34;1m%s%s\x1b[m", ptr_info.sigil, typename) : CORD_cat(ptr_info.sigil, typename);
CORD c = colorize ? CORD_asprintf("\x1b[34;1m%s%s\x1b[m", ptr_info.sigil, typename) : CORD_cat(ptr_info.sigil, typename);
return ptr_info.is_optional ? CORD_cat(c, "?") : c;
}
const void *ptr = *(const void**)x;
if (!ptr) {
@ -36,8 +37,11 @@ public CORD Pointer$as_text(const void *x, bool colorize, const TypeInfo *type)
int32_t depth = 0;
for (recursion_t *r = recursion; r; r = r->next) {
++depth;
if (r->ptr == ptr)
return CORD_asprintf(colorize ? "\x1b[34;1m%s..%d\x1b[m" : "%s..%d", ptr_info.sigil, depth);
if (r->ptr == ptr) {
CORD c = CORD_asprintf(colorize ? "\x1b[34;1m%s..%d\x1b[m" : "%s..%d", ptr_info.sigil, depth);
if (ptr_info.is_optional) c = CORD_cat(c, colorize ? "\x1b[34;1m?\x1b[m" : "?");
return c;
}
}
CORD pointed;
@ -47,7 +51,9 @@ public CORD Pointer$as_text(const void *x, bool colorize, const TypeInfo *type)
pointed = generic_as_text(ptr, colorize, ptr_info.pointed);
recursion = recursion->next;
}
return colorize ? CORD_asprintf("\x1b[34;1m%s\x1b[m%r", ptr_info.sigil, pointed) : CORD_cat(ptr_info.sigil, pointed);
CORD c = colorize ? CORD_asprintf("\x1b[34;1m%s\x1b[m%r", ptr_info.sigil, pointed) : CORD_cat(ptr_info.sigil, pointed);
if (ptr_info.is_optional) c = CORD_cat(c, colorize ? "\x1b[34;1m?\x1b[m" : "?");
return c;
}
public int32_t Pointer$compare(const void *x, const void *y, const TypeInfo *type) {

View File

@ -28,6 +28,7 @@ typedef struct TypeInfo {
} CustomInfo;
struct {
const char *sigil;
bool is_optional;
const struct TypeInfo *pointed;
} PointerInfo;
struct {
@ -50,8 +51,8 @@ typedef struct TypeInfo {
};
} TypeInfo;
#define $PointerInfo(sigil_expr, pointed_info) &((TypeInfo){.size=sizeof(void*), .align=__alignof__(void*), \
.tag=PointerInfo, .PointerInfo.sigil=sigil_expr, .PointerInfo.pointed=pointed_info})
#define $PointerInfo(sigil_expr, pointed_info, opt) &((TypeInfo){.size=sizeof(void*), .align=__alignof__(void*), \
.tag=PointerInfo, .PointerInfo={.sigil=sigil_expr, .pointed=pointed_info, .is_optional=opt}})
#define $ArrayInfo(item_info) &((TypeInfo){.size=sizeof(array_t), .align=__alignof__(array_t), \
.tag=ArrayInfo, .ArrayInfo.item=item_info})
#define $TableInfo(key_expr, value_expr) &((TypeInfo){.size=sizeof(table_t), .align=__alignof__(table_t), \

View File

@ -1014,6 +1014,9 @@ CORD compile(env_t *env, ast_t *ast)
return CORD_all("(&", compile(env, subject), ")");
return CORD_all("stack(", compile(env, subject), ")");
}
case Optional: {
return compile(env, Match(ast, Optional)->value);
}
case BinaryOp: {
auto binop = Match(ast, BinaryOp);
CORD lhs = compile(env, binop->lhs);
@ -1929,9 +1932,12 @@ CORD compile_type_info(env_t *env, type_t *t)
}
case PointerType: {
auto ptr = Match(t, PointerType);
CORD sigil = ptr->is_stack ? "&" : (ptr->is_optional ? "?" : "@");
CORD sigil = ptr->is_stack ? "&" : "@";
if (ptr->is_readonly) sigil = CORD_cat(sigil, "%");
return CORD_asprintf("$PointerInfo(%r, %r)", Text$quoted(sigil, false), compile_type_info(env, ptr->pointed));
return CORD_asprintf("$PointerInfo(%r, %r, %s)",
Text$quoted(sigil, false),
compile_type_info(env, ptr->pointed),
ptr->is_optional ? "yes" : "no");
}
case FunctionType: {
return CORD_asprintf("$FunctionInfo(%r)", Text$quoted(type_to_cord(t), false));

View File

@ -52,14 +52,14 @@ env_t *new_compilation_unit(void)
{"Void", Type(VoidType), "Void_t", "$Void", {}},
{"Memory", Type(MemoryType), "Memory_t", "$Memory", {}},
{"Bool", Type(BoolType), "Bool_t", "$Bool", TypedArray(ns_entry_t,
{"from_text", "Bool$from_text", "func(text:Text, success=!Bool)->Bool"},
{"from_text", "Bool$from_text", "func(text:Text, success=!&Bool)->Bool"},
)},
{"Int", Type(IntType, .bits=64), "Int_t", "$Int", TypedArray(ns_entry_t,
{"format", "Int$format", "func(i:Int, digits=0)->Text"},
{"hex", "Int$hex", "func(i:Int, digits=0, uppercase=yes, prefix=yes)->Text"},
{"octal", "Int$octal", "func(i:Int, digits=0, prefix=yes)->Text"},
{"random", "Int$random", "func(min=0, max=0xffffffff)->Int"},
{"from_text", "Int$from_text", "func(text:Text, the_rest=!Text)->Int"},
{"from_text", "Int$from_text", "func(text:Text, the_rest=!&Text)->Int"},
{"bits", "Int$bits", "func(x:Int)->[Bool]"},
{"abs", "labs", "func(i:Int)->Int"},
{"min", "Int$min", "Int"},
@ -70,7 +70,7 @@ env_t *new_compilation_unit(void)
{"hex", "Int32$hex", "func(i:Int32, digits=0, uppercase=yes, prefix=yes)->Text"},
{"octal", "Int32$octal", "func(i:Int32, digits=0, prefix=yes)->Text"},
{"random", "Int32$random", "func(min=0, max=0xffffffff)->Int32"},
{"from_text", "Int$from_text", "func(text:Text, the_rest=!Text)->Int32"},
{"from_text", "Int$from_text", "func(text:Text, the_rest=!&Text)->Int32"},
{"bits", "Int32$bits", "func(x:Int32)->[Bool]"},
{"abs", "abs", "func(i:Int32)->Int32"},
{"min", "Int32$min", "Int32"},
@ -81,7 +81,7 @@ env_t *new_compilation_unit(void)
{"hex", "Int16$hex", "func(i:Int16, digits=0, uppercase=yes, prefix=yes)->Text"},
{"octal", "Int16$octal", "func(i:Int16, digits=0, prefix=yes)->Text"},
{"random", "Int16$random", "func(min=0, max=0xffffffff)->Int16"},
{"from_text", "Int$from_text", "func(text:Text, the_rest=!Text)->Int16"},
{"from_text", "Int$from_text", "func(text:Text, the_rest=!&Text)->Int16"},
{"bits", "Int16$bits", "func(x:Int16)->[Bool]"},
{"abs", "abs", "func(i:Int16)->Int16"},
{"min", "Int16$min", "Int16"},
@ -92,7 +92,7 @@ env_t *new_compilation_unit(void)
{"hex", "Int8$hex", "func(i:Int8, digits=0, uppercase=yes, prefix=yes)->Text"},
{"octal", "Int8$octal", "func(i:Int8, digits=0, prefix=yes)->Text"},
{"random", "Int8$random", "func(min=0, max=0xffffffff)->Int8"},
{"from_text", "Int$from_text", "func(text:Text, the_rest=!Text)->Int8"},
{"from_text", "Int$from_text", "func(text:Text, the_rest=!&Text)->Int8"},
{"bits", "Int8$bits", "func(x:Int8)->[Bool]"},
{"abs", "abs", "func(i:Int8)->Int8"},
{"min", "Int8$min", "Int8"},
@ -115,7 +115,7 @@ env_t *new_compilation_unit(void)
{"TAU", "(2.*M_PI)", "Num"},
{"random", "Num$random", "func()->Num"},
{"mix", "Num$mix", "func(amount:Num, x:Num, y:Num)->Num"},
{"from_text", "Num$from_text", "func(text:Text, the_rest=!Text)->Num"},
{"from_text", "Num$from_text", "func(text:Text, the_rest=!&Text)->Num"},
{"abs", "fabs", "func(n:Num)->Num"},
F(acos), F(acosh), F(asin), F(asinh), F(atan), F(atanh), F(cbrt), F(ceil), F(cos), F(cosh), F(erf), F(erfc),
F(exp), F(exp2), F(expm1), F(floor), F(j0), F(j1), F(log), F(log10), F(log1p), F(log2), F(logb),
@ -143,7 +143,7 @@ env_t *new_compilation_unit(void)
{"TAU", "(Num32_t)(2.f*M_PI)", "Num32"},
{"random", "Num32$random", "func()->Num32"},
{"mix", "Num32$mix", "func(amount:Num32, x:Num32, y:Num32)->Num32"},
{"from_text", "Num32$from_text", "func(text:Text, the_rest=!Text)->Num32"},
{"from_text", "Num32$from_text", "func(text:Text, the_rest=!&Text)->Num32"},
{"abs", "fabsf", "func(n:Num32)->Num32"},
F(acos), F(acosh), F(asin), F(asinh), F(atan), F(atanh), F(cbrt), F(ceil), F(cos), F(cosh), F(erf), F(erfc),
F(exp), F(exp2), F(expm1), F(floor), F(j0), F(j1), F(log), F(log10), F(log1p), F(log2), F(logb),

25
parse.c
View File

@ -71,6 +71,7 @@ static ast_t *parse_method_call_suffix(parse_ctx_t *ctx, ast_t *self);
static ast_t *parse_field_suffix(parse_ctx_t *ctx, ast_t *lhs);
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_for);
static PARSER(parse_while);
@ -496,11 +497,9 @@ type_ast_t *parse_array_type(parse_ctx_t *ctx, const char *pos) {
type_ast_t *parse_pointer_type(parse_ctx_t *ctx, const char *pos) {
const char *start = pos;
bool optional = false, is_stack = false;
bool is_stack;
if (match(&pos, "@"))
optional = false;
else if (match(&pos, "?"))
optional = true;
is_stack = false;
else if (match(&pos, "&"))
is_stack = true;
else
@ -511,6 +510,7 @@ type_ast_t *parse_pointer_type(parse_ctx_t *ctx, const char *pos) {
spaces(&pos);
type_ast_t *type = expect(ctx, start, &pos, parse_type,
"I couldn't parse a pointer type after this point");
bool optional = match(&pos, "?");
return NewTypeAST(ctx->file, start, pos, PointerTypeAST, .pointed=type, .is_optional=optional, .is_stack=is_stack, .is_readonly=is_readonly);
}
@ -735,6 +735,15 @@ ast_t *parse_field_suffix(parse_ctx_t *ctx, ast_t *lhs) {
return NewAST(ctx->file, lhs->start, pos, FieldAccess, .fielded=lhs, .field=field);
}
ast_t *parse_optional_suffix(parse_ctx_t *ctx, ast_t *lhs) {
if (!lhs) return NULL;
const char *pos = lhs->end;
if (match(&pos, "?"))
return NewAST(ctx->file, lhs->start, pos, Optional, .value=lhs);
else
return NULL;
}
PARSER(parse_reduction) {
const char *start = pos;
if (!match(&pos, "(")) return NULL;
@ -956,7 +965,9 @@ PARSER(parse_heap_alloc) {
if (!match(&pos, "@")) return NULL;
spaces(&pos);
ast_t *val = expect(ctx, start, &pos, parse_term, "I expected an expression for this '@'");
return NewAST(ctx->file, start, pos, HeapAllocate, .value=val);
ast_t *ast = NewAST(ctx->file, start, pos, HeapAllocate, .value=val);
ast_t *optional = parse_optional_suffix(ctx, ast);
return optional ? optional : ast;
}
PARSER(parse_stack_reference) {
@ -964,7 +975,9 @@ PARSER(parse_stack_reference) {
if (!match(&pos, "&")) return NULL;
spaces(&pos);
ast_t *val = expect(ctx, start, &pos, parse_term, "I expected an expression for this '&'");
return NewAST(ctx->file, start, pos, StackReference, .value=val);
ast_t *ast = NewAST(ctx->file, start, pos, StackReference, .value=val);
ast_t *optional = parse_optional_suffix(ctx, ast);
return optional ? optional : ast;
}
PARSER(parse_not) {

4
repl.c
View File

@ -132,11 +132,11 @@ const TypeInfo *type_to_type_info(type_t *t)
}
case PointerType: {
auto ptr = Match(t, PointerType);
CORD sigil = ptr->is_stack ? "&" : (ptr->is_optional ? "?" : "@");
CORD sigil = ptr->is_stack ? "&" : "@";
if (ptr->is_readonly) sigil = CORD_cat(sigil, "%");
const TypeInfo *pointed_info = type_to_type_info(ptr->pointed);
const TypeInfo pointer_info = {.size=sizeof(void*), .align=__alignof__(void*),
.tag=PointerInfo, .PointerInfo.sigil=sigil, .PointerInfo.pointed=pointed_info};
.tag=PointerInfo, .PointerInfo={.sigil=sigil, .pointed=pointed_info, .is_optional=ptr->is_optional}};
return memcpy(GC_MALLOC(sizeof(TypeInfo)), &pointer_info, sizeof(TypeInfo));
}
default: errx(1, "Unsupported type: %T", t);

View File

@ -1,7 +1,7 @@
struct Pair(x,y:Int)
struct Mixed(x:Int, text:Text)
struct LinkedList(x:Int, next=!LinkedList)
struct LinkedList(x:Int, next=!@LinkedList)
struct Password(text:Text; secret)
func test_literals():

View File

@ -18,7 +18,7 @@ func main():
>> #t
= 2
>> t.default
= ?%999
= @%999?
>> t.fallback
= !{Text:Int}
@ -42,7 +42,7 @@ func main():
>> t2.default
= !Int
>> t2.fallback
= ?%{"one":1, "two":2; default=999}
= @%{"one":1, "two":2; default=999}?
t2_str := ""
for k,v in t2:

View File

@ -330,8 +330,11 @@ type_t *get_type(env_t *env, ast_t *ast)
if (!ast) return NULL;
switch (ast->tag) {
case Nil: {
type_t *pointed = parse_type_ast(env, Match(ast, Nil)->type);
return Type(PointerType, .is_optional=true, .pointed=pointed);
type_t *t = parse_type_ast(env, Match(ast, Nil)->type);
if (t->tag != PointerType)
code_err(ast, "This type is not a pointer type, so it doesn't work with a '!' nil expression");
auto ptr = Match(t, PointerType);
return Type(PointerType, .is_optional=true, .pointed=ptr->pointed, .is_stack=ptr->is_stack, .is_readonly=ptr->is_readonly);
}
case Bool: {
return Type(BoolType);
@ -385,6 +388,16 @@ type_t *get_type(env_t *env, ast_t *ast)
code_err(ast, "'&' stack references can only be used on variables or fields of variables");
}
case Optional: {
ast_t *value = Match(ast, Optional)->value;
type_t *t = get_type(env, value);
if (t->tag != PointerType)
code_err(ast, "This value is not a pointer, it has type %T, so it can't be optional", t);
auto ptr = Match(t, PointerType);
if (ptr->is_optional)
code_err(ast, "This value is already optional, it can't be converted to optional");
return Type(PointerType, .pointed=ptr->pointed, .is_optional=true, .is_stack=ptr->is_stack, .is_readonly=ptr->is_readonly);
}
case TextLiteral: return TEXT_TYPE;
case TextJoin: {
const char *lang = Match(ast, TextJoin)->lang;

View File

@ -47,9 +47,9 @@ CORD type_to_cord(type_t *t) {
}
case PointerType: {
auto ptr = Match(t, PointerType);
CORD sigil = ptr->is_stack ? "&" : (ptr->is_optional ? "?" : "@");
CORD sigil = ptr->is_stack ? "&" : "@";
if (ptr->is_readonly) sigil = CORD_cat(sigil, "%");
return CORD_cat(sigil, type_to_cord(ptr->pointed));
return CORD_all(sigil, type_to_cord(ptr->pointed), ptr->is_optional ? "?" : CORD_EMPTY);
}
case EnumType: {
auto tagged = Match(t, EnumType);