Clean up some more null->none renames and fix the documentation. Also

change the literal syntax to `NONE:T` instead of `!T`
This commit is contained in:
Bruce Hill 2024-11-24 16:36:27 -05:00
parent 1e3fb8a2c0
commit d4b10514fb
14 changed files with 97 additions and 91 deletions

View File

@ -69,6 +69,9 @@ clean:
%: %.md %: %.md
pandoc --lua-filter=.pandoc/bold-code.lua -s $< -t man -o $@ pandoc --lua-filter=.pandoc/bold-code.lua -s $< -t man -o $@
examples:
tomo -IL examples/vectors examples/base64 examples/log examples/ini examples/game examples/http examples/threads examples/tomodeps examples/tomo-install examples/wrap
install: tomo libtomo.so tomo.1 install: tomo libtomo.so tomo.1
mkdir -p -m 755 "$(PREFIX)/man/man1" "$(PREFIX)/bin" "$(PREFIX)/include/tomo" "$(PREFIX)/lib" "$(PREFIX)/share/tomo/modules" mkdir -p -m 755 "$(PREFIX)/man/man1" "$(PREFIX)/bin" "$(PREFIX)/include/tomo" "$(PREFIX)/lib" "$(PREFIX)/share/tomo/modules"
cp -v stdlib/*.h "$(PREFIX)/include/tomo/" cp -v stdlib/*.h "$(PREFIX)/include/tomo/"
@ -81,4 +84,4 @@ uninstall:
rm -rvf "$(PREFIX)/bin/tomo" "$(PREFIX)/include/tomo" "$(PREFIX)/lib/libtomo.so" "$(PREFIX)/share/tomo"; \ rm -rvf "$(PREFIX)/bin/tomo" "$(PREFIX)/include/tomo" "$(PREFIX)/lib/libtomo.so" "$(PREFIX)/share/tomo"; \
.SUFFIXES: .SUFFIXES:
.PHONY: all clean install uninstall test tags .PHONY: all clean install uninstall test tags examples

4
ast.c
View File

@ -101,7 +101,7 @@ CORD ast_to_xml(ast_t *ast)
switch (ast->tag) { switch (ast->tag) {
#define T(type, ...) case type: { auto data = ast->__data.type; (void)data; return CORD_asprintf(__VA_ARGS__); } #define T(type, ...) case type: { auto data = ast->__data.type; (void)data; return CORD_asprintf(__VA_ARGS__); }
T(Unknown, "<Unknown>") T(Unknown, "<Unknown>")
T(Null, "<Null>%r</Null>", type_ast_to_xml(data.type)) T(None, "<None>%r</None>", type_ast_to_xml(data.type))
T(Bool, "<Bool value=\"%s\" />", data.b ? "yes" : "no") T(Bool, "<Bool value=\"%s\" />", data.b ? "yes" : "no")
T(Var, "<Var>%s</Var>", data.name) T(Var, "<Var>%s</Var>", data.name)
T(Int, "<Int>%s</Int>", data.str) T(Int, "<Int>%s</Int>", data.str)
@ -203,7 +203,7 @@ int printf_ast(FILE *stream, const struct printf_info *info, const void *const a
PUREFUNC bool is_idempotent(ast_t *ast) PUREFUNC bool is_idempotent(ast_t *ast)
{ {
switch (ast->tag) { switch (ast->tag) {
case Int: case Bool: case Num: case Var: case Null: case TextLiteral: return true; case Int: case Bool: case Num: case Var: case None: case TextLiteral: return true;
case Index: { case Index: {
auto index = Match(ast, Index); auto index = Match(ast, Index);
return is_idempotent(index->indexed) && index->index != NULL && is_idempotent(index->index); return is_idempotent(index->indexed) && index->index != NULL && is_idempotent(index->index);

4
ast.h
View File

@ -122,7 +122,7 @@ struct type_ast_s {
typedef enum { typedef enum {
Unknown = 0, Unknown = 0,
Null, Bool, Var, None, Bool, Var,
Int, Num, Int, Num,
TextLiteral, TextJoin, PrintStatement, TextLiteral, TextJoin, PrintStatement,
Declare, Assign, Declare, Assign,
@ -155,7 +155,7 @@ struct ast_s {
struct {} Unknown; struct {} Unknown;
struct { struct {
type_ast_t *type; type_ast_t *type;
} Null; } None;
struct { struct {
bool b; bool b;
} Bool; } Bool;

View File

@ -1557,7 +1557,7 @@ CORD compile_to_type(env_t *env, ast_t *ast, type_t *t)
{ {
if (ast->tag == Int && is_numeric_type(t)) if (ast->tag == Int && is_numeric_type(t))
return compile_int_to_type(env, ast, t); return compile_int_to_type(env, ast, t);
if (ast->tag == Null && Match(ast, Null)->type == NULL) if (ast->tag == None && Match(ast, None)->type == NULL)
return compile_null(t); return compile_null(t);
CORD code = compile(env, ast); CORD code = compile(env, ast);
type_t *actual = get_type(env, ast); type_t *actual = get_type(env, ast);
@ -1890,7 +1890,7 @@ CORD compile_null(type_t *t)
env_t *enum_env = Match(t, EnumType)->env; env_t *enum_env = Match(t, EnumType)->env;
return CORD_all("((", compile_type(t), "){", namespace_prefix(enum_env, enum_env->namespace), "null})"); return CORD_all("((", compile_type(t), "){", namespace_prefix(enum_env, enum_env->namespace), "null})");
} }
default: compiler_err(NULL, NULL, NULL, "Null isn't implemented for this type: %T", t); default: compiler_err(NULL, NULL, NULL, "NONE isn't implemented for this type: %T", t);
} }
} }
@ -1930,10 +1930,10 @@ static CORD compile_num_to_type(ast_t *ast, type_t *type)
CORD compile(env_t *env, ast_t *ast) CORD compile(env_t *env, ast_t *ast)
{ {
switch (ast->tag) { switch (ast->tag) {
case Null: { case None: {
if (!Match(ast, Null)->type) if (!Match(ast, None)->type)
code_err(ast, "This 'NULL' needs to specify what type it is using `!Type` syntax"); code_err(ast, "This 'NONE' needs to specify what type it is using `NONE:Type` syntax");
type_t *t = parse_type_ast(env, Match(ast, Null)->type); type_t *t = parse_type_ast(env, Match(ast, None)->type);
return compile_null(t); return compile_null(t);
} }
case Bool: return Match(ast, Bool)->b ? "yes" : "no"; case Bool: return Match(ast, Bool)->b ? "yes" : "no";
@ -2746,7 +2746,7 @@ CORD compile(env_t *env, ast_t *ast)
self = compile_to_pointer_depth(env, call->self, 0, false); self = compile_to_pointer_depth(env, call->self, 0, false);
arg_t *arg_spec = new(arg_t, .name="count", .type=INT_TYPE, arg_t *arg_spec = new(arg_t, .name="count", .type=INT_TYPE,
.next=new(arg_t, .name="weights", .type=Type(ArrayType, .item_type=Type(NumType)), .next=new(arg_t, .name="weights", .type=Type(ArrayType, .item_type=Type(NumType)),
.default_val=FakeAST(Null, .type=new(type_ast_t, .tag=ArrayTypeAST, .default_val=FakeAST(None, .type=new(type_ast_t, .tag=ArrayTypeAST,
.__data.ArrayTypeAST.item=new(type_ast_t, .tag=VarTypeAST, .__data.VarTypeAST.name="Num"))), .__data.ArrayTypeAST.item=new(type_ast_t, .tag=VarTypeAST, .__data.VarTypeAST.name="Num"))),
.next=new(arg_t, .name="rng", .type=RNG_TYPE, .default_val=default_rng))); .next=new(arg_t, .name="rng", .type=RNG_TYPE, .default_val=default_rng)));
return CORD_all("Array$sample(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", return CORD_all("Array$sample(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ",

View File

@ -35,36 +35,36 @@ Optional types are written using a `?` after the type name. So, an optional
integer would be written as `Int?` and an optional array of texts would be integer would be written as `Int?` and an optional array of texts would be
written as `[Text]?`. written as `[Text]?`.
Null values can be written explicitly using the `!` prefix operator and the None can be written explicitly using `NONE` with a type annotation. For
type of null value. For example, if you wanted to declare a variable that could example, if you wanted to declare a variable that could be either an integer
be either an integer value or a null value and initialize it as a null value, value or `NONE` and initialize it as none, you would write it as:
you would write it as:
```tomo ```tomo
x := !Int x := NONE:Int
``` ```
Similarly, if you wanted to declare a variable that could be an array of texts Similarly, if you wanted to declare a variable that could be an array of texts
or null and initialize it as null, you would write: or none and initialize it as none, you would write:
```tomo ```tomo
x := ![Text] x := ![Text]
``` ```
If you want to declare a variable and initialize it with a non-null value, but If you want to declare a variable and initialize it with a non-none value, but
keep open the possibility of assigning a null value later, you can use the keep open the possibility of assigning `NONE` later, you can use the postfix
postfix `?` operator to indicate that a value is optional: `?` operator to indicate that a value is optional:
```tomo ```tomo
x := 5? x := 5?
# Later on, assign null: # Later on, assign none:
x = !Int x = !Int
``` ```
## Type Inference ## Type Inference
For convenience, null values can also be written as `NONE` for any type in For convenience, `NONE` can also be written without the explicit type
situations where the compiler knows what type of optional value is expected: annotation for any type in situations where the compiler knows what type of
optional value is expected:
- When assigning to a variable that has already been declared as optional. - When assigning to a variable that has already been declared as optional.
- When returning from a function with an explicit optional return type. - When returning from a function with an explicit optional return type.
@ -82,7 +82,7 @@ func doop(arg:Int?)->Text?:
doop(NONE) doop(NONE)
``` ```
Non-null values can also be automatically promoted to optional values without Non-none values can also be automatically promoted to optional values without
the need for an explicit `?` operator in the cases listed above: the need for an explicit `?` operator in the cases listed above:
```tomo ```tomo
@ -95,12 +95,12 @@ func doop(arg:Int?)->Text?:
doop(123) doop(123)
``` ```
## Null Checking ## None Checking
In addition to using conditionals to check for null values, you can also use In addition to using conditionals to check for `NONE`, you can also use `or` to
`or` to get a non-null value by either providing an alternative non-null value get a non-none value by either providing an alternative non-none value or by
or by providing an early out statement like `return`/`skip`/`stop` or a function providing an early out statement like `return`/`skip`/`stop` or a function with
with an `Abort` type like `fail()` or `exit()`: an `Abort` type like `fail()` or `exit()`:
```tomo ```tomo
maybe_x := 5? maybe_x := 5?
@ -120,7 +120,7 @@ func do_stuff(matches:[Text]):
for line in lines: for line in lines:
matches := line:matches($/{..},{..}/) or skip matches := line:matches($/{..},{..}/) or skip
# The `or skip` above means that if we're here, `matches` is non-null: # The `or skip` above means that if we're here, `matches` is non-none:
do_stuff(matches) do_stuff(matches)
``` ```
@ -132,9 +132,9 @@ booleans, texts, enums, nums, or integers (`Int` type only). This is done by
using carefully chosen values, such as `0` for pointers, `2` for booleans, or a using carefully chosen values, such as `0` for pointers, `2` for booleans, or a
negative length for arrays. However, for fixed-size integers (`Int64`, `Int32`, negative length for arrays. However, for fixed-size integers (`Int64`, `Int32`,
`Int16`, and `Int8`), bytes, and structs, an additional byte is required for `Int16`, and `Int8`), bytes, and structs, an additional byte is required for
out-of-band information about whether the value is null or not. out-of-band information about whether the value is none or not.
Floating point numbers (`Num` and `Num32`) use `NaN` to represent null, so Floating point numbers (`Num` and `Num32`) use `NaN` to represent none, so
optional nums should be careful to avoid using `NaN` as a non-null value. This optional nums should be careful to avoid using `NaN` as a non-none value. This
option was chosen to minimize the memory overhead of optional nums and because option was chosen to minimize the memory overhead of optional nums and because
`NaN` literally means "not a number". `NaN` literally means "not a number".

View File

@ -47,7 +47,7 @@ env_t *new_compilation_unit(CORD libname)
{"exit", {.code="tomo_exit", {"exit", {.code="tomo_exit",
.type=Type(FunctionType, .args=new( .type=Type(FunctionType, .args=new(
arg_t, .name="message", .type=Type(OptionalType, .type=Type(TextType)), arg_t, .name="message", .type=Type(OptionalType, .type=Type(TextType)),
.default_val=FakeAST(Null, .type=new(type_ast_t, .tag=VarTypeAST, .__data.VarTypeAST.name="Text")), .default_val=FakeAST(None, .type=new(type_ast_t, .tag=VarTypeAST, .__data.VarTypeAST.name="Text")),
.next=new(arg_t, .name="code", .type=Type(IntType, .bits=TYPE_IBITS32), .next=new(arg_t, .name="code", .type=Type(IntType, .bits=TYPE_IBITS32),
.default_val=FakeAST(InlineCCode, .code="1", .type=Type(IntType, .bits=TYPE_IBITS32)))), .default_val=FakeAST(InlineCCode, .code="1", .type=Type(IntType, .bits=TYPE_IBITS32)))),
.ret=Type(AbortType))}}, .ret=Type(AbortType))}},
@ -295,29 +295,29 @@ env_t *new_compilation_unit(CORD libname)
// Used as a default for functions below: // Used as a default for functions below:
{"now", "Moment$now", "func(->Moment)"}, {"now", "Moment$now", "func(->Moment)"},
{"after", "Moment$after", "func(moment:Moment,seconds,minutes,hours=0.0,days,weeks,months,years=0,timezone=!Text -> Moment)"}, {"after", "Moment$after", "func(moment:Moment,seconds,minutes,hours=0.0,days,weeks,months,years=0,timezone=NONE:Text -> Moment)"},
{"date", "Moment$date", "func(moment:Moment,timezone=!Text -> Text)"}, {"date", "Moment$date", "func(moment:Moment,timezone=NONE:Text -> Text)"},
{"day_of_month", "Moment$day_of_month", "func(moment:Moment,timezone=!Text -> Int)"}, {"day_of_month", "Moment$day_of_month", "func(moment:Moment,timezone=NONE:Text -> Int)"},
{"day_of_week", "Moment$day_of_week", "func(moment:Moment,timezone=!Text -> Int)"}, {"day_of_week", "Moment$day_of_week", "func(moment:Moment,timezone=NONE:Text -> Int)"},
{"day_of_year", "Moment$day_of_year", "func(moment:Moment,timezone=!Text -> Int)"}, {"day_of_year", "Moment$day_of_year", "func(moment:Moment,timezone=NONE:Text -> Int)"},
{"format", "Moment$format", "func(moment:Moment,format=\"%Y-%m-%dT%H:%M:%S%z\",timezone=!Text -> Text)"}, {"format", "Moment$format", "func(moment:Moment,format=\"%Y-%m-%dT%H:%M:%S%z\",timezone=NONE:Text -> Text)"},
{"from_unix_timestamp", "Moment$from_unix_timestamp", "func(timestamp:Int64 -> Moment)"}, {"from_unix_timestamp", "Moment$from_unix_timestamp", "func(timestamp:Int64 -> Moment)"},
{"get_local_timezone", "Moment$get_local_timezone", "func(->Text)"}, {"get_local_timezone", "Moment$get_local_timezone", "func(->Text)"},
{"hour", "Moment$hour", "func(moment:Moment,timezone=!Text -> Int)"}, {"hour", "Moment$hour", "func(moment:Moment,timezone=NONE:Text -> Int)"},
{"hours_till", "Moment$hours_till", "func(now,then:Moment -> Num)"}, {"hours_till", "Moment$hours_till", "func(now,then:Moment -> Num)"},
{"minute", "Moment$minute", "func(moment:Moment,timezone=!Text -> Int)"}, {"minute", "Moment$minute", "func(moment:Moment,timezone=NONE:Text -> Int)"},
{"minutes_till", "Moment$minutes_till", "func(now,then:Moment -> Num)"}, {"minutes_till", "Moment$minutes_till", "func(now,then:Moment -> Num)"},
{"month", "Moment$month", "func(moment:Moment,timezone=!Text -> Int)"}, {"month", "Moment$month", "func(moment:Moment,timezone=NONE:Text -> Int)"},
{"nanosecond", "Moment$nanosecond", "func(moment:Moment,timezone=!Text -> Int)"}, {"nanosecond", "Moment$nanosecond", "func(moment:Moment,timezone=NONE:Text -> Int)"},
{"new", "Moment$new", "func(year,month,day:Int,hour,minute=0,second=0.0,timezone=!Text -> Moment)"}, {"new", "Moment$new", "func(year,month,day:Int,hour,minute=0,second=0.0,timezone=NONE:Text -> Moment)"},
{"parse", "Moment$parse", "func(text:Text, format=\"%Y-%m-%dT%H:%M:%S%z\" -> Moment?)"}, {"parse", "Moment$parse", "func(text:Text, format=\"%Y-%m-%dT%H:%M:%S%z\" -> Moment?)"},
{"relative", "Moment$relative", "func(moment:Moment,relative_to=Moment.now(),timezone=!Text -> Text)"}, {"relative", "Moment$relative", "func(moment:Moment,relative_to=Moment.now(),timezone=NONE:Text -> Text)"},
{"second", "Moment$second", "func(moment:Moment,timezone=!Text -> Int)"}, {"second", "Moment$second", "func(moment:Moment,timezone=NONE:Text -> Int)"},
{"seconds_till", "Moment$seconds_till", "func(now:Moment,then:Moment -> Num)"}, {"seconds_till", "Moment$seconds_till", "func(now:Moment,then:Moment -> Num)"},
{"set_local_timezone", "Moment$set_local_timezone", "func(timezone=!Text)"}, {"set_local_timezone", "Moment$set_local_timezone", "func(timezone=NONE:Text)"},
{"time", "Moment$time", "func(moment:Moment,seconds=no,am_pm=yes,timezone=!Text -> Text)"}, {"time", "Moment$time", "func(moment:Moment,seconds=no,am_pm=yes,timezone=NONE:Text -> Text)"},
{"unix_timestamp", "Moment$unix_timestamp", "func(moment:Moment -> Int64)"}, {"unix_timestamp", "Moment$unix_timestamp", "func(moment:Moment -> Int64)"},
{"year", "Moment$year", "func(moment:Moment,timezone=!Text -> Int)"}, {"year", "Moment$year", "func(moment:Moment,timezone=NONE:Text -> Int)"},
)}, )},
{"Path", Type(TextType, .lang="Path", .env=namespace_env(env, "Path")), "Text_t", "Text$info", TypedArray(ns_entry_t, {"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=Int32(0o644))"}, {"append", "Path$append", "func(path:Path, text:Text, permissions=Int32(0o644))"},
@ -340,7 +340,7 @@ env_t *new_compilation_unit(CORD libname)
{"is_symlink", "Path$is_symlink", "func(path:Path -> Bool)"}, {"is_symlink", "Path$is_symlink", "func(path:Path -> Bool)"},
{"parent", "Path$parent", "func(path:Path -> Path)"}, {"parent", "Path$parent", "func(path:Path -> Path)"},
{"read", "Path$read", "func(path:Path -> Text?)"}, {"read", "Path$read", "func(path:Path -> Text?)"},
{"read_bytes", "Path$read_bytes", "func(path:Path, limit=!Int -> [Byte]?)"}, {"read_bytes", "Path$read_bytes", "func(path:Path, limit=NONE:Int -> [Byte]?)"},
{"relative", "Path$relative", "func(path:Path, relative_to=(./) -> Path)"}, {"relative", "Path$relative", "func(path:Path, relative_to=(./) -> Path)"},
{"remove", "Path$remove", "func(path:Path, ignore_missing=no)"}, {"remove", "Path$remove", "func(path:Path, ignore_missing=no)"},
{"resolved", "Path$resolved", "func(path:Path, relative_to=(./) -> Path)"}, {"resolved", "Path$resolved", "func(path:Path, relative_to=(./) -> Path)"},

View File

@ -59,10 +59,10 @@ lang Base64:
output[dest+2] = _EQUAL_BYTE output[dest+2] = _EQUAL_BYTE
output[dest+3] = _EQUAL_BYTE output[dest+3] = _EQUAL_BYTE
return Base64.without_escaping(Text.from_bytes(output) or return !Base64) return Base64.without_escaping(Text.from_bytes(output) or return NONE)
func decode_text(b64:Base64 -> Text?): func decode_text(b64:Base64 -> Text?):
return Text.from_bytes(b64:decode_bytes() or return !Text) return Text.from_bytes(b64:decode_bytes() or return NONE)
func decode_bytes(b64:Base64 -> [Byte]?): func decode_bytes(b64:Base64 -> [Byte]?):
bytes := b64.text_content:bytes() bytes := b64.text_content:bytes()

View File

@ -78,7 +78,7 @@ func _send(method:_Method, url:Text, data:Text?, headers=[:Text] -> HTTPResponse
return HTTPResponse(Int(code), "":join(chunks)) return HTTPResponse(Int(code), "":join(chunks))
func get(url:Text, headers=[:Text] -> HTTPResponse): func get(url:Text, headers=[:Text] -> HTTPResponse):
return _send(GET, url, !Text, headers) return _send(GET, url, NONE, headers)
func post(url:Text, data="", headers=["Content-Type: application/json", "Accept: application/json"] -> HTTPResponse): func post(url:Text, data="", headers=["Content-Type: application/json", "Accept: application/json"] -> HTTPResponse):
return _send(POST, url, data, headers) return _send(POST, url, data, headers)
@ -89,7 +89,7 @@ func put(url:Text, data="", headers=["Content-Type: application/json", "Accept:
func patch(url:Text, data="", headers=["Content-Type: application/json", "Accept: application/json"] -> HTTPResponse): func patch(url:Text, data="", headers=["Content-Type: application/json", "Accept: application/json"] -> HTTPResponse):
return _send(PATCH, url, data, headers) return _send(PATCH, url, data, headers)
func delete(url:Text, data=!Text, headers=["Content-Type: application/json", "Accept: application/json"] -> HTTPResponse): func delete(url:Text, data=NONE:Text, headers=["Content-Type: application/json", "Accept: application/json"] -> HTTPResponse):
return _send(DELETE, url, data, headers) return _send(DELETE, url, data, headers)
func main(): func main():

17
parse.c
View File

@ -116,7 +116,7 @@ static PARSER(parse_lang_def);
static PARSER(parse_namespace); static PARSER(parse_namespace);
static PARSER(parse_negative); static PARSER(parse_negative);
static PARSER(parse_not); static PARSER(parse_not);
static PARSER(parse_null); static PARSER(parse_none);
static PARSER(parse_num); static PARSER(parse_num);
static PARSER(parse_parens); static PARSER(parse_parens);
static PARSER(parse_pass); static PARSER(parse_pass);
@ -1530,14 +1530,17 @@ PARSER(parse_lambda) {
return NewAST(ctx->file, start, pos, Lambda, .id=ctx->next_lambda_id++, .args=args, .ret_type=ret, .body=body); return NewAST(ctx->file, start, pos, Lambda, .id=ctx->next_lambda_id++, .args=args, .ret_type=ret, .body=body);
} }
PARSER(parse_null) { PARSER(parse_none) {
const char *start = pos; const char *start = pos;
if (match_word(&pos, "NONE")) if (!match_word(&pos, "NONE"))
return NewAST(ctx->file, start, pos, Null, .type=NULL); return NULL;
if (!match(&pos, "!")) return NULL;
if (!match(&pos, ":"))
return NewAST(ctx->file, start, pos, None, .type=NULL);
type_ast_t *type = parse_type(ctx, pos); type_ast_t *type = parse_type(ctx, pos);
if (!type) return NULL; if (!type) return NULL;
return NewAST(ctx->file, start, type->end, Null, .type=type); return NewAST(ctx->file, start, type->end, None, .type=type);
} }
PARSER(parse_var) { PARSER(parse_var) {
@ -1553,7 +1556,7 @@ PARSER(parse_term_no_suffix) {
(void)( (void)(
false false
|| (term=parse_moment(ctx, pos)) // Must come before num/int || (term=parse_moment(ctx, pos)) // Must come before num/int
|| (term=parse_null(ctx, pos)) || (term=parse_none(ctx, pos))
|| (term=parse_num(ctx, pos)) // Must come before int || (term=parse_num(ctx, pos)) // Must come before int
|| (term=parse_int(ctx, pos)) || (term=parse_int(ctx, pos))
|| (term=parse_negative(ctx, pos)) // Must come after num/int || (term=parse_negative(ctx, pos)) // Must come after num/int

2
repl.c
View File

@ -337,7 +337,7 @@ void eval(env_t *env, ast_t *ast, void *dest)
type_t *t = get_type(env, ast); type_t *t = get_type(env, ast);
size_t size = type_size(t); size_t size = type_size(t);
switch (ast->tag) { switch (ast->tag) {
case Null: case None:
if (dest) *(void**)dest = 0; if (dest) *(void**)dest = 0;
break; break;
case Bool: case Bool:

View File

@ -4,7 +4,7 @@ struct Pair(x:Text, y:Text)
func pairwise(strs:[Text] -> func(->Pair?)): func pairwise(strs:[Text] -> func(->Pair?)):
i := 1 i := 1
return func(): return func():
if i + 1 > strs.length: return !Pair if i + 1 > strs.length: return NONE:Pair
i += 1 i += 1
return Pair(strs[i-1], strs[i])? return Pair(strs[i-1], strs[i])?
@ -12,7 +12,7 @@ func range(first:Int, last:Int -> func(->Int?)):
i := first i := first
return func(): return func():
if i > last: if i > last:
return !Int return NONE:Int
i += 1 i += 1
return (i-1)? return (i-1)?

View File

@ -11,74 +11,74 @@ enum Enum(X, Y(y:Int)):
if should_i: if should_i:
return Enum.Y(123) return Enum.Y(123)
else: else:
return !Enum return NONE
func maybe_int(should_i:Bool->Int?): func maybe_int(should_i:Bool->Int?):
if should_i: if should_i:
return 123 return 123
else: else:
return !Int return NONE
func maybe_int64(should_i:Bool->Int64?): func maybe_int64(should_i:Bool->Int64?):
if should_i: if should_i:
return Int64(123) return Int64(123)
else: else:
return !Int64 return NONE
func maybe_array(should_i:Bool->[Int]?): func maybe_array(should_i:Bool->[Int]?):
if should_i: if should_i:
return [10, 20, 30] return [10, 20, 30]
else: else:
return ![Int] return NONE
func maybe_bool(should_i:Bool->Bool?): func maybe_bool(should_i:Bool->Bool?):
if should_i: if should_i:
return no return no
else: else:
return !Bool return NONE
func maybe_text(should_i:Bool->Text?): func maybe_text(should_i:Bool->Text?):
if should_i: if should_i:
return "Hello" return "Hello"
else: else:
return !Text return NONE
func maybe_num(should_i:Bool->Num?): func maybe_num(should_i:Bool->Num?):
if should_i: if should_i:
return 12.3 return 12.3
else: else:
return !Num return NONE
func maybe_lambda(should_i:Bool-> func()?): func maybe_lambda(should_i:Bool-> func()?):
if should_i: if should_i:
return func(): say("hi!") return func(): say("hi!")
else: else:
return !func() return NONE
func maybe_c_string(should_i:Bool->CString?): func maybe_c_string(should_i:Bool->CString?):
if should_i: if should_i:
return ("hi":as_c_string())? return ("hi":as_c_string())?
else: else:
return !CString return NONE
func maybe_channel(should_i:Bool->|Int|?): func maybe_channel(should_i:Bool->|Int|?):
if should_i: if should_i:
return |:Int|? return |:Int|?
else: else:
return !|Int| return NONE
func maybe_thread(should_i:Bool->Thread?): func maybe_thread(should_i:Bool->Thread?):
if should_i: if should_i:
return Thread.new(func(): pass) return Thread.new(func(): pass)
else: else:
return !Thread return NONE
func main(): func main():
>> 5? >> 5?
= 5 : Int? = 5 : Int?
>> if no: >> if no:
!Int NONE:Int
else: else:
5 5
= 5 : Int? = 5 : Int?
@ -92,7 +92,7 @@ func main():
>> 5? or exit("Non-null is falsey") >> 5? or exit("Non-null is falsey")
= 5 : Int = 5 : Int
>> (!Int) or -1 >> (NONE:Int) or -1
= -1 : Int = -1 : Int
do: do:
@ -279,13 +279,13 @@ func main():
= 123 : Int = 123 : Int
# Test comparisons, hashing, equality: # Test comparisons, hashing, equality:
>> (!Int == 5?) >> (NONE:Int == 5?)
= no = no
>> (5? == 5?) >> (5? == 5?)
= yes = yes
>> {!Int, !Int} >> {NONE:Int, NONE:Int}
= {NONE} = {NONE}
>> [5?, !Int, !Int, 6?]:sorted() >> [5?, NONE:Int, NONE:Int, 6?]:sorted()
= [NONE, NONE, 5, 6] = [NONE, NONE, 5, 6]
do: do:
@ -296,7 +296,7 @@ func main():
= 5 = 5
do: do:
>> value := if var := !Int: >> value := if var := NONE:Int:
var var
else: else:
0 0
@ -310,7 +310,7 @@ func main():
>> opt >> opt
do: do:
>> opt := !Int >> opt := NONE:Int
>> if opt: >> if opt:
>> opt >> opt
else: else:
@ -319,7 +319,7 @@ func main():
>> not 5? >> not 5?
= no = no
>> not !Int >> not NONE:Int
= yes = yes
>> [Struct(5,"A")?, Struct(6,"B"), Struct(7,"C")] >> [Struct(5,"A")?, Struct(6,"B"), Struct(7,"C")]

View File

@ -2,11 +2,11 @@
struct Single(x:Int) struct Single(x:Int)
struct Pair(x,y:Int) struct Pair(x,y:Int)
struct Mixed(x:Int, text:Text) struct Mixed(x:Int, text:Text)
struct LinkedList(x:Int, next=!@LinkedList) struct LinkedList(x:Int, next=NONE:@LinkedList)
struct Password(text:Text; secret) struct Password(text:Text; secret)
struct CorecursiveA(other:@CorecursiveB?) struct CorecursiveA(other:@CorecursiveB?)
struct CorecursiveB(other=!@CorecursiveA) struct CorecursiveB(other=NONE:@CorecursiveA)
func test_literals(): func test_literals():
>> Single(123) >> Single(123)

View File

@ -505,10 +505,10 @@ type_t *get_type(env_t *env, ast_t *ast)
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch-default" #pragma GCC diagnostic ignored "-Wswitch-default"
switch (ast->tag) { switch (ast->tag) {
case Null: { case None: {
if (!Match(ast, Null)->type) if (!Match(ast, None)->type)
return Type(OptionalType, .type=NULL); return Type(OptionalType, .type=NULL);
type_t *t = parse_type_ast(env, Match(ast, Null)->type); type_t *t = parse_type_ast(env, Match(ast, None)->type);
return Type(OptionalType, .type=t); return Type(OptionalType, .type=t);
} }
case Bool: { case Bool: {
@ -1310,7 +1310,7 @@ type_t *parse_type_string(env_t *env, const char *str)
PUREFUNC bool is_constant(env_t *env, ast_t *ast) PUREFUNC bool is_constant(env_t *env, ast_t *ast)
{ {
switch (ast->tag) { switch (ast->tag) {
case Bool: case Num: case Null: return true; case Bool: case Num: case None: return true;
case Int: { case Int: {
auto info = Match(ast, Int); auto info = Match(ast, Int);
Int_t int_val = Int$parse(Text$from_str(info->str)); Int_t int_val = Int$parse(Text$from_str(info->str));