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:
parent
1e3fb8a2c0
commit
d4b10514fb
5
Makefile
5
Makefile
@ -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
4
ast.c
@ -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
4
ast.h
@ -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;
|
||||||
|
14
compile.c
14
compile.c
@ -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), ", ",
|
||||||
|
@ -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".
|
||||||
|
@ -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)"},
|
||||||
|
@ -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()
|
||||||
|
@ -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
17
parse.c
@ -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
2
repl.c
@ -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:
|
||||||
|
@ -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)?
|
||||||
|
|
||||||
|
@ -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")]
|
||||||
|
@ -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)
|
||||||
|
@ -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));
|
||||||
|
Loading…
Reference in New Issue
Block a user