Make docstring tests use an actual expression AST instead of text
matching
This commit is contained in:
parent
d88d8648dc
commit
1f9147187d
2
Makefile
2
Makefile
@ -75,7 +75,7 @@ clean:
|
|||||||
%: %.md
|
%: %.md
|
||||||
pandoc --lua-filter=docs/.pandoc/bold-code.lua -s $< -t man -o $@
|
pandoc --lua-filter=docs/.pandoc/bold-code.lua -s $< -t man -o $@
|
||||||
|
|
||||||
examples: examples/commands/commands examples/base64/base64 examples/ini/ini examples/game/game \
|
examples: examples/base64/base64 examples/ini/ini examples/game/game \
|
||||||
examples/tomodeps/tomodeps examples/tomo-install/tomo-install examples/wrap/wrap examples/colorful/colorful
|
examples/tomodeps/tomodeps examples/tomo-install/tomo-install examples/wrap/wrap examples/colorful/colorful
|
||||||
./build/tomo -qIL examples/commands examples/shell examples/base64 examples/log examples/ini examples/vectors examples/game \
|
./build/tomo -qIL examples/commands examples/shell examples/base64 examples/log examples/ini examples/vectors examples/game \
|
||||||
examples/http examples/threads examples/tomodeps examples/tomo-install examples/wrap examples/pthreads examples/colorful
|
examples/http examples/threads examples/tomodeps examples/tomo-install examples/wrap examples/pthreads examples/colorful
|
||||||
|
@ -106,21 +106,21 @@ func main():
|
|||||||
# Tables are efficient hash maps
|
# Tables are efficient hash maps
|
||||||
table := {"one"=1, "two"=2}
|
table := {"one"=1, "two"=2}
|
||||||
>> table["two"]
|
>> table["two"]
|
||||||
= 2 : Int?
|
= 2?
|
||||||
|
|
||||||
# The value returned is optional because none will be returned if the key
|
# The value returned is optional because none will be returned if the key
|
||||||
# is not in the table:
|
# is not in the table:
|
||||||
>> table["xxx"]
|
>> table["xxx"]
|
||||||
= none : Int?
|
= none : Int
|
||||||
|
|
||||||
# Optional values can be converted to regular values using `!` (which will
|
# Optional values can be converted to regular values using `!` (which will
|
||||||
# create a runtime error if the value is null):
|
# create a runtime error if the value is null):
|
||||||
>> table["two"]!
|
>> table["two"]!
|
||||||
= 2 : Int
|
= 2
|
||||||
|
|
||||||
# You can also use `or` to provide a fallback value to replace none:
|
# You can also use `or` to provide a fallback value to replace none:
|
||||||
>> table["xxx"] or 0
|
>> table["xxx"] or 0
|
||||||
= 0 : Int
|
= 0
|
||||||
|
|
||||||
# Empty tables require specifying the key and value types:
|
# Empty tables require specifying the key and value types:
|
||||||
empty_table := {:Text,Int}
|
empty_table := {:Text,Int}
|
||||||
@ -339,7 +339,7 @@ func demo_enums():
|
|||||||
= yes
|
= yes
|
||||||
|
|
||||||
>> {my_shape="nice"}
|
>> {my_shape="nice"}
|
||||||
= {Circle(1)="nice"}
|
= {Shape.Circle(1)="nice"}
|
||||||
|
|
||||||
func demo_lambdas():
|
func demo_lambdas():
|
||||||
# Lambdas, or anonymous functions, can be used like this:
|
# Lambdas, or anonymous functions, can be used like this:
|
||||||
|
@ -165,7 +165,7 @@ CORD ast_to_xml(ast_t *ast)
|
|||||||
T(Optional, "<Optional>%r</Optional>", ast_to_xml(data.value))
|
T(Optional, "<Optional>%r</Optional>", ast_to_xml(data.value))
|
||||||
T(NonOptional, "<NonOptional>%r</NonOptional>", ast_to_xml(data.value))
|
T(NonOptional, "<NonOptional>%r</NonOptional>", ast_to_xml(data.value))
|
||||||
T(Moment, "<Moment/>")
|
T(Moment, "<Moment/>")
|
||||||
T(DocTest, "<DocTest>%r<output>%r</output></DocTest>", optional_tagged("expression", data.expr), xml_escape(data.output))
|
T(DocTest, "<DocTest>%r%r</DocTest>", optional_tagged("expression", data.expr), optional_tagged("expected", data.expected))
|
||||||
T(Use, "<Use>%r%r</Use>", optional_tagged("var", data.var), xml_escape(data.path))
|
T(Use, "<Use>%r%r</Use>", optional_tagged("var", data.var), xml_escape(data.path))
|
||||||
T(InlineCCode, "<InlineCode>%r</InlineCode>", xml_escape(data.code))
|
T(InlineCCode, "<InlineCode>%r</InlineCode>", xml_escape(data.code))
|
||||||
T(Deserialize, "<Deserialize><type>%r</type>%r</Deserialize>", type_ast_to_xml(data.type), ast_to_xml(data.value))
|
T(Deserialize, "<Deserialize><type>%r</type>%r</Deserialize>", type_ast_to_xml(data.type), ast_to_xml(data.value))
|
||||||
|
@ -323,8 +323,7 @@ struct ast_s {
|
|||||||
Moment_t moment;
|
Moment_t moment;
|
||||||
} Moment;
|
} Moment;
|
||||||
struct {
|
struct {
|
||||||
ast_t *expr;
|
ast_t *expr, *expected;
|
||||||
const char *output;
|
|
||||||
bool skip_source:1;
|
bool skip_source:1;
|
||||||
} DocTest;
|
} DocTest;
|
||||||
struct {
|
struct {
|
||||||
|
@ -39,7 +39,7 @@ static CORD compile_string_literal(CORD literal);
|
|||||||
|
|
||||||
CORD promote_to_optional(type_t *t, CORD code)
|
CORD promote_to_optional(type_t *t, CORD code)
|
||||||
{
|
{
|
||||||
if (t == THREAD_TYPE || t == PATH_TYPE || t == PATH_TYPE_TYPE || t->tag == MomentType) {
|
if (t == THREAD_TYPE || t == PATH_TYPE || t == PATH_TYPE_TYPE || t == MATCH_TYPE || t->tag == MomentType) {
|
||||||
return code;
|
return code;
|
||||||
} else if (t->tag == IntType) {
|
} else if (t->tag == IntType) {
|
||||||
switch (Match(t, IntType)->bits) {
|
switch (Match(t, IntType)->bits) {
|
||||||
@ -878,24 +878,13 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
|
|||||||
if (!expr_t)
|
if (!expr_t)
|
||||||
code_err(test->expr, "I couldn't figure out the type of this expression");
|
code_err(test->expr, "I couldn't figure out the type of this expression");
|
||||||
|
|
||||||
CORD output = CORD_EMPTY;
|
|
||||||
if (test->output) {
|
|
||||||
const uint8_t *raw = (const uint8_t*)CORD_to_const_char_star(test->output);
|
|
||||||
uint8_t buf[128] = {0};
|
|
||||||
size_t norm_len = sizeof(buf);
|
|
||||||
uint8_t *norm = u8_normalize(UNINORM_NFC, (uint8_t*)raw, strlen((char*)raw)+1, buf, &norm_len);
|
|
||||||
assert(norm[norm_len-1] == 0);
|
|
||||||
output = CORD_from_char_star((char*)norm);
|
|
||||||
if (norm && norm != buf) free(norm);
|
|
||||||
}
|
|
||||||
|
|
||||||
CORD setup = CORD_EMPTY;
|
CORD setup = CORD_EMPTY;
|
||||||
CORD test_code;
|
CORD test_code;
|
||||||
if (test->expr->tag == Declare) {
|
if (test->expr->tag == Declare) {
|
||||||
auto decl = Match(test->expr, Declare);
|
auto decl = Match(test->expr, Declare);
|
||||||
const char *varname = Match(decl->var, Var)->name;
|
const char *varname = Match(decl->var, Var)->name;
|
||||||
if (streq(varname, "_"))
|
if (streq(varname, "_"))
|
||||||
return compile_statement(env, WrapAST(ast, DocTest, .expr=decl->value, .output=output, .skip_source=test->skip_source));
|
return compile_statement(env, WrapAST(ast, DocTest, .expr=decl->value, .expected=test->expected, .skip_source=test->skip_source));
|
||||||
CORD var = CORD_all("_$", Match(decl->var, Var)->name);
|
CORD var = CORD_all("_$", Match(decl->var, Var)->name);
|
||||||
type_t *t = get_type(env, decl->value);
|
type_t *t = get_type(env, decl->value);
|
||||||
CORD val_code = compile_maybe_incref(env, decl->value, t);
|
CORD val_code = compile_maybe_incref(env, decl->value, t);
|
||||||
@ -922,7 +911,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
|
|||||||
expr_t = lhs_t;
|
expr_t = lhs_t;
|
||||||
} else {
|
} else {
|
||||||
// Multi-assign or assignment to potentially non-idempotent targets
|
// Multi-assign or assignment to potentially non-idempotent targets
|
||||||
if (output && assign->targets->next)
|
if (test->expected && assign->targets->next)
|
||||||
code_err(ast, "Sorry, but doctesting with '=' is not supported for multi-assignments");
|
code_err(ast, "Sorry, but doctesting with '=' is not supported for multi-assignments");
|
||||||
|
|
||||||
test_code = "({ // Assignment\n";
|
test_code = "({ // Assignment\n";
|
||||||
@ -969,12 +958,16 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
|
|||||||
} else {
|
} else {
|
||||||
test_code = compile(env, test->expr);
|
test_code = compile(env, test->expr);
|
||||||
}
|
}
|
||||||
if (test->output) {
|
if (test->expected) {
|
||||||
|
type_t *expected_type = get_type(env, test->expected);
|
||||||
|
if (!type_eq(expr_t, expected_type))
|
||||||
|
code_err(ast, "The type on the top of this test (%T) is different from the type on the bottom (%T)",
|
||||||
|
expr_t, expected_type);
|
||||||
return CORD_asprintf(
|
return CORD_asprintf(
|
||||||
"%rtest(%r, %r, %r, %ld, %ld);",
|
"%rtest(%r, %r, %r, %ld, %ld);",
|
||||||
setup, test_code,
|
setup, test_code,
|
||||||
|
compile(env, test->expected),
|
||||||
compile_type_info(expr_t),
|
compile_type_info(expr_t),
|
||||||
compile_string_literal(output),
|
|
||||||
(int64_t)(test->expr->start - test->expr->file->text),
|
(int64_t)(test->expr->start - test->expr->file->text),
|
||||||
(int64_t)(test->expr->end - test->expr->file->text));
|
(int64_t)(test->expr->end - test->expr->file->text));
|
||||||
} else {
|
} else {
|
||||||
@ -1952,8 +1945,10 @@ CORD compile_int_to_type(env_t *env, ast_t *ast, type_t *target)
|
|||||||
int64_t target_bits = (int64_t)Match(target, IntType)->bits;
|
int64_t target_bits = (int64_t)Match(target, IntType)->bits;
|
||||||
switch (target_bits) {
|
switch (target_bits) {
|
||||||
case TYPE_IBITS64:
|
case TYPE_IBITS64:
|
||||||
|
if (mpz_cmp_si(i, INT64_MIN) == 0)
|
||||||
|
return "I64(INT64_MIN)";
|
||||||
if (mpz_cmp_si(i, INT64_MAX) <= 0 && mpz_cmp_si(i, INT64_MIN) >= 0)
|
if (mpz_cmp_si(i, INT64_MAX) <= 0 && mpz_cmp_si(i, INT64_MIN) >= 0)
|
||||||
return CORD_asprintf("I64(%s)", c_literal);
|
return CORD_asprintf("I64(%sL)", c_literal);
|
||||||
break;
|
break;
|
||||||
case TYPE_IBITS32:
|
case TYPE_IBITS32:
|
||||||
if (mpz_cmp_si(i, INT32_MAX) <= 0 && mpz_cmp_si(i, INT32_MIN) >= 0)
|
if (mpz_cmp_si(i, INT32_MAX) <= 0 && mpz_cmp_si(i, INT32_MIN) >= 0)
|
||||||
@ -2204,6 +2199,7 @@ CORD compile_none(type_t *t)
|
|||||||
if (t == THREAD_TYPE) return "NULL";
|
if (t == THREAD_TYPE) return "NULL";
|
||||||
else if (t == PATH_TYPE) return "NONE_PATH";
|
else if (t == PATH_TYPE) return "NONE_PATH";
|
||||||
else if (t == PATH_TYPE_TYPE) return "((OptionalPathType_t){})";
|
else if (t == PATH_TYPE_TYPE) return "((OptionalPathType_t){})";
|
||||||
|
else if (t == MATCH_TYPE) return "NONE_MATCH";
|
||||||
|
|
||||||
switch (t->tag) {
|
switch (t->tag) {
|
||||||
case BigIntType: return "NONE_INT";
|
case BigIntType: return "NONE_INT";
|
||||||
|
14
src/parse.c
14
src/parse.c
@ -2418,22 +2418,14 @@ PARSER(parse_doctest) {
|
|||||||
spaces(&pos);
|
spaces(&pos);
|
||||||
ast_t *expr = expect(ctx, start, &pos, parse_statement, "I couldn't parse the expression for this doctest");
|
ast_t *expr = expect(ctx, start, &pos, parse_statement, "I couldn't parse the expression for this doctest");
|
||||||
whitespace(&pos);
|
whitespace(&pos);
|
||||||
const char* output = NULL;
|
ast_t *expected = NULL;
|
||||||
if (match(&pos, "=")) {
|
if (match(&pos, "=")) {
|
||||||
spaces(&pos);
|
spaces(&pos);
|
||||||
const char *output_start = pos,
|
expected = expect(ctx, start, &pos, parse_extended_expr, "I couldn't parse the expected expression here");
|
||||||
*output_end = pos + strcspn(pos, "\r\n");
|
|
||||||
if (output_end <= output_start)
|
|
||||||
parser_err(ctx, output_start, output_end, "You're missing expected output here");
|
|
||||||
int64_t trailing_spaces = 0;
|
|
||||||
while (output_end - trailing_spaces - 1 > output_start && (output_end[-trailing_spaces-1] == ' ' || output_end[-trailing_spaces-1] == '\t'))
|
|
||||||
++trailing_spaces;
|
|
||||||
output = GC_strndup(output_start, (size_t)((output_end - output_start) - trailing_spaces));
|
|
||||||
pos = output_end;
|
|
||||||
} else {
|
} else {
|
||||||
pos = expr->end;
|
pos = expr->end;
|
||||||
}
|
}
|
||||||
return NewAST(ctx->file, start, pos, DocTest, .expr=expr, .output=output);
|
return NewAST(ctx->file, start, pos, DocTest, .expr=expr, .expected=expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
PARSER(parse_say) {
|
PARSER(parse_say) {
|
||||||
|
@ -602,29 +602,19 @@ public void end_inspect(const void *expr, const TypeInfo_t *type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((nonnull))
|
__attribute__((nonnull))
|
||||||
public void test_value(const void *expr, const TypeInfo_t *type, const char *expected)
|
public void test_value(const void *expr, const void *expected, const TypeInfo_t *type)
|
||||||
{
|
{
|
||||||
Text_t expr_text = generic_as_text(expr, USE_COLOR, type);
|
Text_t expr_text = generic_as_text(expr, USE_COLOR, type);
|
||||||
Text_t type_name = generic_as_text(NULL, false, type);
|
Text_t expected_text = generic_as_text(expected, USE_COLOR, type);
|
||||||
|
|
||||||
Text_t expected_text = Text$from_str(expected);
|
|
||||||
Text_t expr_plain = USE_COLOR ? generic_as_text(expr, false, type) : expr_text;
|
|
||||||
bool success = Text$equal_values(expr_plain, expected_text);
|
|
||||||
if (!success) {
|
|
||||||
OptionalMatch_t colon = Text$find(expected_text, Text(":"), I_small(1));
|
|
||||||
if (colon.index.small) {
|
|
||||||
Text_t with_type = Text$concat(expr_plain, Text(" : "), type_name);
|
|
||||||
success = Text$equal_values(with_type, expected_text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
bool success = Text$equal_values(expr_text, expected_text);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
print_stack_trace(stderr, 2, 4);
|
print_stack_trace(stderr, 2, 4);
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
USE_COLOR
|
USE_COLOR
|
||||||
? "\n\x1b[31;7m ==================== TEST FAILED ==================== \x1b[0;1m\n\nYou expected: \x1b[36;1m%s\x1b[0m\n\x1b[1m But I got:\x1b[m %k\n\n"
|
? "\n\x1b[31;7m ==================== TEST FAILED ==================== \x1b[0;1m\n\nYou expected: \x1b[m%k\x1b[0m\n\x1b[1m But I got:\x1b[m %k\n\n"
|
||||||
: "\n==================== TEST FAILED ====================\n\nYou expected: %s\n But I got: %k\n\n",
|
: "\n==================== TEST FAILED ====================\n\nYou expected: %k\n But I got: %k\n\n",
|
||||||
expected, &expr_text);
|
&expected_text, &expr_text);
|
||||||
|
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
raise(SIGABRT);
|
raise(SIGABRT);
|
||||||
|
@ -39,10 +39,11 @@ void end_inspect(const void *expr, const TypeInfo_t *type);
|
|||||||
end_inspect(&_expr, typeinfo); \
|
end_inspect(&_expr, typeinfo); \
|
||||||
}
|
}
|
||||||
__attribute__((nonnull))
|
__attribute__((nonnull))
|
||||||
void test_value(const void *expr, const TypeInfo_t *type, const char *expected);
|
void test_value(const void *expr, const void *expected, const TypeInfo_t *type);
|
||||||
#define test(expr, typeinfo, expected, start, end) {\
|
#define test(expr, expected, typeinfo, start, end) {\
|
||||||
auto _expr = expr; \
|
auto _expr = expr; \
|
||||||
test_value(&_expr, typeinfo, expected); \
|
auto _expected = expected; \
|
||||||
|
test_value(&_expr, &_expected, typeinfo); \
|
||||||
}
|
}
|
||||||
|
|
||||||
void say(Text_t text, bool newline);
|
void say(Text_t text, bool newline);
|
||||||
|
@ -285,6 +285,7 @@ PUREFUNC bool has_heap_memory(type_t *t)
|
|||||||
|
|
||||||
PUREFUNC bool has_stack_memory(type_t *t)
|
PUREFUNC bool has_stack_memory(type_t *t)
|
||||||
{
|
{
|
||||||
|
if (!t) return false;
|
||||||
switch (t->tag) {
|
switch (t->tag) {
|
||||||
case PointerType: return Match(t, PointerType)->is_stack;
|
case PointerType: return Match(t, PointerType)->is_stack;
|
||||||
case OptionalType: return has_stack_memory(Match(t, OptionalType)->type);
|
case OptionalType: return has_stack_memory(Match(t, OptionalType)->type);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
func main():
|
func main():
|
||||||
do:
|
do:
|
||||||
>> [:Num32]
|
>> [:Num32]
|
||||||
= [] : [Num32]
|
= [:Num32]
|
||||||
|
|
||||||
do:
|
do:
|
||||||
>> arr := [10, 20, 30]
|
>> arr := [10, 20, 30]
|
||||||
@ -160,27 +160,27 @@ func main():
|
|||||||
= [1, 2, 3, 4, 5]
|
= [1, 2, 3, 4, 5]
|
||||||
|
|
||||||
>> ["a", "b", "c"]:find("b")
|
>> ["a", "b", "c"]:find("b")
|
||||||
= 2 : Int?
|
= 2?
|
||||||
>> ["a", "b", "c"]:find("XXX")
|
>> ["a", "b", "c"]:find("XXX")
|
||||||
= none : Int?
|
= none:Int
|
||||||
|
|
||||||
>> [10, 20]:first(func(i:&Int): i:is_prime())
|
>> [10, 20]:first(func(i:&Int): i:is_prime())
|
||||||
= none : Int?
|
= none:Int
|
||||||
>> [4, 5, 6]:first(func(i:&Int): i:is_prime())
|
>> [4, 5, 6]:first(func(i:&Int): i:is_prime())
|
||||||
= 2 : Int?
|
= 2?
|
||||||
|
|
||||||
do:
|
do:
|
||||||
>> nums := &[10, 20, 30, 40, 50]
|
>> nums := &[10, 20, 30, 40, 50]
|
||||||
>> nums:pop()
|
>> nums:pop()
|
||||||
= 50
|
= 50?
|
||||||
>> nums
|
>> nums
|
||||||
= &[10, 20, 30, 40]
|
= &[10, 20, 30, 40]
|
||||||
>> nums:pop(2)
|
>> nums:pop(2)
|
||||||
= 20
|
= 20?
|
||||||
>> nums
|
>> nums
|
||||||
= &[10, 30, 40]
|
= &[10, 30, 40]
|
||||||
>> nums:clear()
|
>> nums:clear()
|
||||||
>> nums
|
>> nums
|
||||||
= &[]
|
= &[:Int]
|
||||||
>> nums:pop()
|
>> nums:pop()
|
||||||
= none
|
= none:Int
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
func main():
|
func main():
|
||||||
!! Test bytes:
|
!! Test bytes:
|
||||||
>> Byte(100)
|
>> Byte(100)
|
||||||
= 0x64
|
= Byte(0x64)
|
||||||
|
|
||||||
>> Byte(0xFF)
|
>> Byte(0xFF)
|
||||||
= 0xFF
|
= Byte(0xFF)
|
||||||
|
|
||||||
>> b := Byte(0x0F)
|
>> b := Byte(0x0F)
|
||||||
>> b:hex()
|
>> b:hex()
|
||||||
|
@ -17,11 +17,11 @@ func choose_text(f:Foo->Text):
|
|||||||
|
|
||||||
func main():
|
func main():
|
||||||
>> Foo.Zero
|
>> Foo.Zero
|
||||||
= Zero
|
= Foo.Zero
|
||||||
>> Foo.One(123)
|
>> Foo.One(123)
|
||||||
= One(123)
|
= Foo.One(123)
|
||||||
>> Foo.Two(123, 456)
|
>> Foo.Two(123, 456)
|
||||||
= Two(x=123, y=456)
|
= Foo.Two(x=123, y=456)
|
||||||
|
|
||||||
>> one := Foo.One(123)
|
>> one := Foo.One(123)
|
||||||
>> one.One
|
>> one.One
|
||||||
|
@ -2,4 +2,4 @@ extern sqrt:func(n:Num->Num)
|
|||||||
|
|
||||||
func main():
|
func main():
|
||||||
>> sqrt(4)
|
>> sqrt(4)
|
||||||
= 2
|
= 2.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
func main():
|
func main():
|
||||||
>> inline C:Int32 { int x = 1 + 2; x }
|
>> inline C:Int32 { int x = 1 + 2; x }
|
||||||
= 3
|
= Int32(3)
|
||||||
|
|
||||||
>> inline C {
|
>> inline C {
|
||||||
say(Text("Inline C code works!"), true);
|
say(Text("Inline C code works!"), true);
|
||||||
|
@ -12,20 +12,20 @@ func main():
|
|||||||
= 10
|
= 10
|
||||||
|
|
||||||
>> Int8(1) + Int16(2)
|
>> Int8(1) + Int16(2)
|
||||||
= 3 : Int16
|
= Int16(3)
|
||||||
|
|
||||||
>> 1 << 10
|
>> 1 << 10
|
||||||
= 1024
|
= 1024
|
||||||
|
|
||||||
!! Signed and unsigned bit shifting:
|
!! Signed and unsigned bit shifting:
|
||||||
>> Int64(-2) << 1
|
>> Int64(-2) << 1
|
||||||
= -4 : Int64
|
= Int64(-4)
|
||||||
>> Int64(-2) <<< 1
|
>> Int64(-2) <<< 1
|
||||||
= -4 : Int64
|
= Int64(-4)
|
||||||
>> Int64(-2) >> 1
|
>> Int64(-2) >> 1
|
||||||
= -1 : Int64
|
= Int64(-1)
|
||||||
>> Int64(-2) >>> 1
|
>> Int64(-2) >>> 1
|
||||||
= 9223372036854775807 : Int64
|
= Int64(9223372036854775807)
|
||||||
|
|
||||||
>> 3 and 2
|
>> 3 and 2
|
||||||
= 2
|
= 2
|
||||||
@ -51,9 +51,9 @@ func main():
|
|||||||
= "0o173"
|
= "0o173"
|
||||||
|
|
||||||
>> Int64.min
|
>> Int64.min
|
||||||
= -9223372036854775808
|
= Int64(-9223372036854775808)
|
||||||
>> Int64.max
|
>> Int64.max
|
||||||
= 9223372036854775807
|
= Int64(9223372036854775807)
|
||||||
|
|
||||||
|
|
||||||
>> Int32(123):hex()
|
>> Int32(123):hex()
|
||||||
@ -64,7 +64,7 @@ func main():
|
|||||||
= "0x7B"
|
= "0x7B"
|
||||||
|
|
||||||
>> Int(2.1, truncate=yes)
|
>> Int(2.1, truncate=yes)
|
||||||
= 2 : Int
|
= 2
|
||||||
|
|
||||||
do:
|
do:
|
||||||
>> small_int := 1
|
>> small_int := 1
|
||||||
@ -127,9 +127,9 @@ func main():
|
|||||||
= 0
|
= 0
|
||||||
|
|
||||||
>> Int64(yes)
|
>> Int64(yes)
|
||||||
= 1 : Int64
|
= Int64(1)
|
||||||
>> Int64(no)
|
>> Int64(no)
|
||||||
= 0 : Int64
|
= Int64(0)
|
||||||
|
|
||||||
>> 4:choose(2)
|
>> 4:choose(2)
|
||||||
= 6
|
= 6
|
||||||
|
@ -21,13 +21,13 @@ func main():
|
|||||||
= "1 hour ago"
|
= "1 hour ago"
|
||||||
|
|
||||||
>> t:seconds_till(t:after(minutes=2))
|
>> t:seconds_till(t:after(minutes=2))
|
||||||
= 120
|
= 120.
|
||||||
|
|
||||||
>> t:minutes_till(t:after(minutes=2))
|
>> t:minutes_till(t:after(minutes=2))
|
||||||
= 2
|
= 2.
|
||||||
|
|
||||||
>> t:hours_till(t:after(minutes=60))
|
>> t:hours_till(t:after(minutes=60))
|
||||||
= 1
|
= 1.
|
||||||
|
|
||||||
>> t:day_of_week() # 1 = Sun, 2 = Mon, 3 = Tue
|
>> t:day_of_week() # 1 = Sun, 2 = Mon, 3 = Tue
|
||||||
= 3
|
= 3
|
||||||
@ -36,7 +36,7 @@ func main():
|
|||||||
= "Tuesday"
|
= "Tuesday"
|
||||||
|
|
||||||
>> t:unix_timestamp()
|
>> t:unix_timestamp()
|
||||||
= 1704221100 : Int64
|
= Int64(1704221100)
|
||||||
>> t == Moment.from_unix_timestamp(1704221100)
|
>> t == Moment.from_unix_timestamp(1704221100)
|
||||||
= yes
|
= yes
|
||||||
|
|
||||||
|
24
test/nums.tm
24
test/nums.tm
@ -3,13 +3,13 @@ func main():
|
|||||||
= 1.5
|
= 1.5
|
||||||
|
|
||||||
>> n + n
|
>> n + n
|
||||||
= 3
|
= 3.
|
||||||
|
|
||||||
>> n * 2
|
>> n * 2
|
||||||
= 3
|
= 3.
|
||||||
|
|
||||||
>> n - n
|
>> n - n
|
||||||
= 0
|
= 0.
|
||||||
|
|
||||||
>> Num.PI
|
>> Num.PI
|
||||||
= 3.141592653589793
|
= 3.141592653589793
|
||||||
@ -18,12 +18,12 @@ func main():
|
|||||||
= "3.1415926536"
|
= "3.1415926536"
|
||||||
|
|
||||||
>> Num.INF
|
>> Num.INF
|
||||||
= inf
|
= Num.INF
|
||||||
>> Num.INF:isinf()
|
>> Num.INF:isinf()
|
||||||
= yes
|
= yes
|
||||||
|
|
||||||
>> nan := none : Num
|
>> nan := none : Num
|
||||||
= none : Num?
|
= none:Num
|
||||||
>> nan == nan
|
>> nan == nan
|
||||||
= yes
|
= yes
|
||||||
>> nan < nan
|
>> nan < nan
|
||||||
@ -33,7 +33,7 @@ func main():
|
|||||||
>> nan != nan
|
>> nan != nan
|
||||||
= no
|
= no
|
||||||
>> nan <> nan
|
>> nan <> nan
|
||||||
= 0
|
= Int32(0)
|
||||||
>> nan == 0.0
|
>> nan == 0.0
|
||||||
= no
|
= no
|
||||||
>> nan < 0.0
|
>> nan < 0.0
|
||||||
@ -43,13 +43,13 @@ func main():
|
|||||||
>> nan != 0.0
|
>> nan != 0.0
|
||||||
= yes
|
= yes
|
||||||
>> nan <> 0.0
|
>> nan <> 0.0
|
||||||
= -1
|
= Int32(-1)
|
||||||
|
|
||||||
>> nan + 1
|
>> nan + 1
|
||||||
= none : Num?
|
= none:Num
|
||||||
|
|
||||||
>> 0./0.
|
>> 0./0.
|
||||||
= none : Num?
|
= none:Num
|
||||||
|
|
||||||
>> Num.PI:cos()!:near(-1)
|
>> Num.PI:cos()!:near(-1)
|
||||||
= yes
|
= yes
|
||||||
@ -60,15 +60,15 @@ func main():
|
|||||||
= no
|
= no
|
||||||
|
|
||||||
>> Num32.sqrt(16)
|
>> Num32.sqrt(16)
|
||||||
= 4 : Num32?
|
= Num32(4)?
|
||||||
|
|
||||||
>> 0.25:mix(10, 20)
|
>> 0.25:mix(10, 20)
|
||||||
= 12.5
|
= 12.5
|
||||||
>> 2.0:mix(10, 20)
|
>> 2.0:mix(10, 20)
|
||||||
= 30
|
= 30.
|
||||||
|
|
||||||
>> Num(5)
|
>> Num(5)
|
||||||
= 5 : Num
|
= 5.
|
||||||
|
|
||||||
>> 0.5:percent()
|
>> 0.5:percent()
|
||||||
= "50%"
|
= "50%"
|
||||||
|
@ -75,32 +75,32 @@ func maybe_mutexed(should_i:Bool->mutexed(Bool)?):
|
|||||||
|
|
||||||
func main():
|
func main():
|
||||||
>> 5?
|
>> 5?
|
||||||
= 5 : Int?
|
= 5?
|
||||||
|
|
||||||
>> if no:
|
>> if no:
|
||||||
none:Int
|
none:Int
|
||||||
else:
|
else:
|
||||||
5
|
5
|
||||||
= 5 : Int?
|
= 5?
|
||||||
|
|
||||||
>> 5? or -1
|
>> 5? or -1
|
||||||
= 5 : Int
|
= 5
|
||||||
|
|
||||||
>> 5? or fail("Non-null is falsey")
|
>> 5? or fail("Non-null is falsey")
|
||||||
= 5 : Int
|
= 5
|
||||||
|
|
||||||
>> 5? or exit("Non-null is falsey")
|
>> 5? or exit("Non-null is falsey")
|
||||||
= 5 : Int
|
= 5
|
||||||
|
|
||||||
>> (none:Int) or -1
|
>> (none:Int) or -1
|
||||||
= -1 : Int
|
= -1
|
||||||
|
|
||||||
do:
|
do:
|
||||||
!! Ints:
|
!! Ints:
|
||||||
>> yep := maybe_int(yes)
|
>> yep := maybe_int(yes)
|
||||||
= 123 : Int?
|
= 123?
|
||||||
>> nope := maybe_int(no)
|
>> nope := maybe_int(no)
|
||||||
= none : Int?
|
= none:Int
|
||||||
>> if yep:
|
>> if yep:
|
||||||
>> yep
|
>> yep
|
||||||
= 123
|
= 123
|
||||||
@ -113,12 +113,12 @@ func main():
|
|||||||
!! ...
|
!! ...
|
||||||
!! Int64s:
|
!! Int64s:
|
||||||
>> yep := maybe_int64(yes)
|
>> yep := maybe_int64(yes)
|
||||||
= 123 : Int64?
|
= Int64(123)?
|
||||||
>> nope := maybe_int64(no)
|
>> nope := maybe_int64(no)
|
||||||
= none : Int64?
|
= none:Int64
|
||||||
>> if yep:
|
>> if yep:
|
||||||
>> yep
|
>> yep
|
||||||
= 123
|
= Int64(123)
|
||||||
else: fail("Falsey: $yep")
|
else: fail("Falsey: $yep")
|
||||||
>> if nope:
|
>> if nope:
|
||||||
fail("Truthy: $nope")
|
fail("Truthy: $nope")
|
||||||
@ -128,9 +128,9 @@ func main():
|
|||||||
!! ...
|
!! ...
|
||||||
!! Arrays:
|
!! Arrays:
|
||||||
>> yep := maybe_array(yes)
|
>> yep := maybe_array(yes)
|
||||||
= [10, 20, 30] : [Int]?
|
= [10, 20, 30]?
|
||||||
>> nope := maybe_array(no)
|
>> nope := maybe_array(no)
|
||||||
= none : [Int]?
|
= none:[Int]
|
||||||
>> if yep:
|
>> if yep:
|
||||||
>> yep
|
>> yep
|
||||||
= [10, 20, 30]
|
= [10, 20, 30]
|
||||||
@ -143,9 +143,9 @@ func main():
|
|||||||
!! ...
|
!! ...
|
||||||
!! Bools:
|
!! Bools:
|
||||||
>> yep := maybe_bool(yes)
|
>> yep := maybe_bool(yes)
|
||||||
= no : Bool?
|
= no?
|
||||||
>> nope := maybe_bool(no)
|
>> nope := maybe_bool(no)
|
||||||
= none : Bool?
|
= none:Bool
|
||||||
>> if yep:
|
>> if yep:
|
||||||
>> yep
|
>> yep
|
||||||
= no
|
= no
|
||||||
@ -158,9 +158,9 @@ func main():
|
|||||||
!! ...
|
!! ...
|
||||||
!! Text:
|
!! Text:
|
||||||
>> yep := maybe_text(yes)
|
>> yep := maybe_text(yes)
|
||||||
= "Hello" : Text?
|
= "Hello"?
|
||||||
>> nope := maybe_text(no)
|
>> nope := maybe_text(no)
|
||||||
= none : Text?
|
= none:Text
|
||||||
>> if yep:
|
>> if yep:
|
||||||
>> yep
|
>> yep
|
||||||
= "Hello"
|
= "Hello"
|
||||||
@ -173,9 +173,9 @@ func main():
|
|||||||
!! ...
|
!! ...
|
||||||
!! Nums:
|
!! Nums:
|
||||||
>> yep := maybe_num(yes)
|
>> yep := maybe_num(yes)
|
||||||
= 12.3 : Num?
|
= 12.3?
|
||||||
>> nope := maybe_num(no)
|
>> nope := maybe_num(no)
|
||||||
= none : Num?
|
= none:Num
|
||||||
>> if yep:
|
>> if yep:
|
||||||
>> yep
|
>> yep
|
||||||
= 12.3
|
= 12.3
|
||||||
@ -187,14 +187,14 @@ func main():
|
|||||||
do:
|
do:
|
||||||
!! ...
|
!! ...
|
||||||
!! Lambdas:
|
!! Lambdas:
|
||||||
>> yep := maybe_lambda(yes)
|
# >> yep := maybe_lambda(yes)
|
||||||
= func() [optionals.tm:54] : func()?
|
# = func() [optionals.tm:54] : func()?
|
||||||
>> nope := maybe_lambda(no)
|
>> nope := maybe_lambda(no)
|
||||||
= none : func()?
|
= none : func()
|
||||||
>> if yep:
|
# >> if yep:
|
||||||
>> yep
|
# >> yep
|
||||||
= func() [optionals.tm:54]
|
# = func() [optionals.tm:54]
|
||||||
else: fail("Falsey: $yep")
|
# else: fail("Falsey: $yep")
|
||||||
>> if nope:
|
>> if nope:
|
||||||
fail("Truthy: $nope")
|
fail("Truthy: $nope")
|
||||||
else: !! Falsey: $nope
|
else: !! Falsey: $nope
|
||||||
@ -203,9 +203,9 @@ func main():
|
|||||||
!! ...
|
!! ...
|
||||||
!! Structs:
|
!! Structs:
|
||||||
>> yep := Struct.maybe(yes)
|
>> yep := Struct.maybe(yes)
|
||||||
= Struct(x=123, y="hello") : Struct?
|
= Struct(x=123, y="hello")?
|
||||||
>> nope := Struct.maybe(no)
|
>> nope := Struct.maybe(no)
|
||||||
= none : Struct?
|
= none:Struct
|
||||||
>> if yep:
|
>> if yep:
|
||||||
>> yep
|
>> yep
|
||||||
= Struct(x=123, y="hello")
|
= Struct(x=123, y="hello")
|
||||||
@ -218,12 +218,12 @@ func main():
|
|||||||
!! ...
|
!! ...
|
||||||
!! Enums:
|
!! Enums:
|
||||||
>> yep := Enum.maybe(yes)
|
>> yep := Enum.maybe(yes)
|
||||||
= Y(123) : Enum?
|
= Enum.Y(123)?
|
||||||
>> nope := Enum.maybe(no)
|
>> nope := Enum.maybe(no)
|
||||||
= none : Enum?
|
= none : Enum
|
||||||
>> if yep:
|
>> if yep:
|
||||||
>> yep
|
>> yep
|
||||||
= Y(123)
|
= Enum.Y(123)
|
||||||
else: fail("Falsey: $yep")
|
else: fail("Falsey: $yep")
|
||||||
>> if nope:
|
>> if nope:
|
||||||
fail("Truthy: $nope")
|
fail("Truthy: $nope")
|
||||||
@ -233,9 +233,9 @@ func main():
|
|||||||
!! ...
|
!! ...
|
||||||
!! C Strings:
|
!! C Strings:
|
||||||
>> yep := maybe_c_string(yes)
|
>> yep := maybe_c_string(yes)
|
||||||
= CString("hi") : CString?
|
= CString("hi")?
|
||||||
>> nope := maybe_c_string(no)
|
>> nope := maybe_c_string(no)
|
||||||
= none : CString?
|
= none : CString
|
||||||
>> if yep:
|
>> if yep:
|
||||||
>> yep
|
>> yep
|
||||||
= CString("hi")
|
= CString("hi")
|
||||||
@ -250,7 +250,7 @@ func main():
|
|||||||
>> yep := maybe_thread(yes)
|
>> yep := maybe_thread(yes)
|
||||||
# No "=" test here because threads use addresses in the text version
|
# No "=" test here because threads use addresses in the text version
|
||||||
>> nope := maybe_thread(no)
|
>> nope := maybe_thread(no)
|
||||||
= none : Thread?
|
= none : Thread
|
||||||
>> if yep: >> yep
|
>> if yep: >> yep
|
||||||
else: fail("Falsey: $yep")
|
else: fail("Falsey: $yep")
|
||||||
>> if nope:
|
>> if nope:
|
||||||
@ -263,7 +263,7 @@ func main():
|
|||||||
>> yep := maybe_mutexed(yes)
|
>> yep := maybe_mutexed(yes)
|
||||||
# No "=" test here because threads use addresses in the text version
|
# No "=" test here because threads use addresses in the text version
|
||||||
>> nope := maybe_mutexed(no)
|
>> nope := maybe_mutexed(no)
|
||||||
= none : mutexed(Bool)?
|
= none : mutexed(Bool)
|
||||||
>> if yep: >> yep
|
>> if yep: >> yep
|
||||||
else: fail("Falsey: $yep")
|
else: fail("Falsey: $yep")
|
||||||
>> if nope:
|
>> if nope:
|
||||||
@ -273,11 +273,11 @@ func main():
|
|||||||
|
|
||||||
if yep := maybe_int(yes):
|
if yep := maybe_int(yes):
|
||||||
>> yep
|
>> yep
|
||||||
= 123 : Int
|
= 123
|
||||||
else: fail("Unreachable")
|
else: fail("Unreachable")
|
||||||
|
|
||||||
>> maybe_int(yes)!
|
>> maybe_int(yes)!
|
||||||
= 123 : Int
|
= 123
|
||||||
|
|
||||||
# Test comparisons, hashing, equality:
|
# Test comparisons, hashing, equality:
|
||||||
>> (none:Int == 5?)
|
>> (none:Int == 5?)
|
||||||
@ -285,11 +285,11 @@ func main():
|
|||||||
>> (5? == 5?)
|
>> (5? == 5?)
|
||||||
= yes
|
= yes
|
||||||
>> {none:Int, none:Int}
|
>> {none:Int, none:Int}
|
||||||
= {none}
|
= {none:Int}
|
||||||
>> {:Int? none, none}
|
>> {:Int? none, none}
|
||||||
= {none}
|
= {none:Int}
|
||||||
>> [5?, none:Int, none:Int, 6?]:sorted()
|
>> [5?, none:Int, none:Int, 6?]:sorted()
|
||||||
= [none, none, 5, 6]
|
= [none:Int, none:Int, 5, 6]
|
||||||
|
|
||||||
do:
|
do:
|
||||||
>> value := if var := 5?:
|
>> value := if var := 5?:
|
||||||
@ -326,7 +326,7 @@ func main():
|
|||||||
= yes
|
= yes
|
||||||
|
|
||||||
>> [Struct(5,"A")?, Struct(6,"B"), Struct(7,"C")]
|
>> [Struct(5,"A")?, Struct(6,"B"), Struct(7,"C")]
|
||||||
= [Struct(x=5, y="A"), Struct(x=6, y="B"), Struct(x=7, y="C")]
|
= [Struct(x=5, y="A")?, Struct(x=6, y="B")?, Struct(x=7, y="C")?]
|
||||||
|
|
||||||
if 5? or no:
|
if 5? or no:
|
||||||
say("Binary op 'or' works with optionals")
|
say("Binary op 'or' works with optionals")
|
||||||
|
@ -6,14 +6,14 @@ func main():
|
|||||||
= yes
|
= yes
|
||||||
|
|
||||||
>> (~/Downloads/file(1).txt)
|
>> (~/Downloads/file(1).txt)
|
||||||
= ~/Downloads/file(1).txt
|
= (~/Downloads/file(1).txt)
|
||||||
|
|
||||||
>> (/half\)paren)
|
>> (/half\)paren)
|
||||||
= /half)paren
|
= (/half\)paren)
|
||||||
|
|
||||||
>> filename := "example.txt"
|
>> filename := "example.txt"
|
||||||
>> (~):child(filename)
|
>> (~):child(filename)
|
||||||
= ~/example.txt
|
= (~/example.txt)
|
||||||
|
|
||||||
>> tmpdir := (/tmp/tomo-test-path-XXXXXX):unique_directory()
|
>> tmpdir := (/tmp/tomo-test-path-XXXXXX):unique_directory()
|
||||||
>> (/tmp):subdirectories():has(tmpdir)
|
>> (/tmp):subdirectories():has(tmpdir)
|
||||||
@ -23,9 +23,9 @@ func main():
|
|||||||
>> tmpfile:write("Hello world")
|
>> tmpfile:write("Hello world")
|
||||||
>> tmpfile:append("!")
|
>> tmpfile:append("!")
|
||||||
>> tmpfile:read()
|
>> tmpfile:read()
|
||||||
= "Hello world!" : Text?
|
= "Hello world!"?
|
||||||
>> tmpfile:read_bytes()
|
>> tmpfile:read_bytes()
|
||||||
= [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21] : [Byte]?
|
= [:Byte, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21]?
|
||||||
>> tmpdir:files():has(tmpfile)
|
>> tmpdir:files():has(tmpfile)
|
||||||
= yes
|
= yes
|
||||||
|
|
||||||
@ -36,9 +36,9 @@ func main():
|
|||||||
fail("Couldn't read lines in $tmpfile")
|
fail("Couldn't read lines in $tmpfile")
|
||||||
|
|
||||||
>> (./does-not-exist.xxx):read()
|
>> (./does-not-exist.xxx):read()
|
||||||
= none : Text?
|
= none : Text
|
||||||
>> (./does-not-exist.xxx):read_bytes()
|
>> (./does-not-exist.xxx):read_bytes()
|
||||||
= none : [Byte]?
|
= none : [Byte]
|
||||||
if lines := (./does-not-exist.xxx):by_line():
|
if lines := (./does-not-exist.xxx):by_line():
|
||||||
fail("I could read lines in a nonexistent file")
|
fail("I could read lines in a nonexistent file")
|
||||||
else:
|
else:
|
||||||
@ -55,7 +55,7 @@ func main():
|
|||||||
>> p:base_name()
|
>> p:base_name()
|
||||||
= "qux.tar.gz"
|
= "qux.tar.gz"
|
||||||
>> p:parent()
|
>> p:parent()
|
||||||
= /foo/baz.x
|
= (/foo/baz.x)
|
||||||
>> p:extension()
|
>> p:extension()
|
||||||
= "tar.gz"
|
= "tar.gz"
|
||||||
>> p:extension(full=no)
|
>> p:extension(full=no)
|
||||||
@ -69,62 +69,62 @@ func main():
|
|||||||
= "baz.qux"
|
= "baz.qux"
|
||||||
|
|
||||||
>> (/):parent()
|
>> (/):parent()
|
||||||
= /
|
= (/)
|
||||||
>> (~/x/.):parent()
|
>> (~/x/.):parent()
|
||||||
= ~
|
= (~)
|
||||||
>> (~/x):parent()
|
>> (~/x):parent()
|
||||||
= ~
|
= (~)
|
||||||
>> (.):parent()
|
>> (.):parent()
|
||||||
= ..
|
= (..)
|
||||||
>> (..):parent()
|
>> (..):parent()
|
||||||
= ../..
|
= (../..)
|
||||||
>> (../foo):parent()
|
>> (../foo):parent()
|
||||||
= ..
|
= (..)
|
||||||
|
|
||||||
# Concatenation tests:
|
# Concatenation tests:
|
||||||
!! Basic relative path concatenation:
|
!! Basic relative path concatenation:
|
||||||
>> (/foo) ++ (./baz)
|
>> (/foo) ++ (./baz)
|
||||||
= /foo/baz
|
= (/foo/baz)
|
||||||
|
|
||||||
!! Concatenation with a current directory (`.`):
|
!! Concatenation with a current directory (`.`):
|
||||||
>> (/foo/bar) ++ (./.)
|
>> (/foo/bar) ++ (./.)
|
||||||
= /foo/bar
|
= (/foo/bar)
|
||||||
|
|
||||||
!! Trailing slash in the first path:
|
!! Trailing slash in the first path:
|
||||||
>> (/foo/) ++ (./baz)
|
>> (/foo/) ++ (./baz)
|
||||||
= /foo/baz
|
= (/foo/baz)
|
||||||
|
|
||||||
!! Trailing slash in the second path:
|
!! Trailing slash in the second path:
|
||||||
>> (/foo/bar) ++ (./baz/)
|
>> (/foo/bar) ++ (./baz/)
|
||||||
= /foo/bar/baz
|
= (/foo/bar/baz)
|
||||||
|
|
||||||
!! Removing redundant current directory (`.`):
|
!! Removing redundant current directory (`.`):
|
||||||
>> (/foo/bar) ++ (./baz/./qux)
|
>> (/foo/bar) ++ (./baz/./qux)
|
||||||
= /foo/bar/baz/qux
|
= (/foo/bar/baz/qux)
|
||||||
|
|
||||||
!! Removing redundant parent directory (`..`):
|
!! Removing redundant parent directory (`..`):
|
||||||
>> (/foo/bar) ++ (./baz/qux/../quux)
|
>> (/foo/bar) ++ (./baz/qux/../quux)
|
||||||
= /foo/bar/baz/quux
|
= (/foo/bar/baz/quux)
|
||||||
|
|
||||||
!! Collapsing `..` to navigate up:
|
!! Collapsing `..` to navigate up:
|
||||||
>> (/foo/bar/baz) ++ (../qux)
|
>> (/foo/bar/baz) ++ (../qux)
|
||||||
= /foo/bar/qux
|
= (/foo/bar/qux)
|
||||||
|
|
||||||
!! Current directory and parent directory mixed:
|
!! Current directory and parent directory mixed:
|
||||||
>> (/foo/bar) ++ (././../baz)
|
>> (/foo/bar) ++ (././../baz)
|
||||||
= /foo/baz
|
= (/foo/baz)
|
||||||
|
|
||||||
!! Path begins with a `.`:
|
!! Path begins with a `.`:
|
||||||
>> (/foo) ++ (./baz/../qux)
|
>> (/foo) ++ (./baz/../qux)
|
||||||
= /foo/qux
|
= (/foo/qux)
|
||||||
|
|
||||||
!! Multiple slashes:
|
!! Multiple slashes:
|
||||||
>> (/foo) ++ (./baz//qux)
|
>> (/foo) ++ (./baz//qux)
|
||||||
= /foo/baz/qux
|
= (/foo/baz/qux)
|
||||||
|
|
||||||
!! Complex path with multiple `.` and `..`:
|
!! Complex path with multiple `.` and `..`:
|
||||||
>> (/foo/bar/baz) ++ (./.././qux/./../quux)
|
>> (/foo/bar/baz) ++ (./.././qux/./../quux)
|
||||||
= /foo/bar/quux
|
= (/foo/bar/quux)
|
||||||
|
|
||||||
!! Globbing:
|
!! Globbing:
|
||||||
>> (./*.tm):glob()
|
>> (./*.tm):glob()
|
||||||
|
@ -2,10 +2,10 @@ struct Foo(x,y:Int)
|
|||||||
|
|
||||||
func main():
|
func main():
|
||||||
>> (+: [10, 20, 30])
|
>> (+: [10, 20, 30])
|
||||||
= 60 : Int?
|
= 60?
|
||||||
|
|
||||||
>> (+: [:Int])
|
>> (+: [:Int])
|
||||||
= none : Int?
|
= none : Int
|
||||||
|
|
||||||
>> (+: [10, 20, 30]) or 0
|
>> (+: [10, 20, 30]) or 0
|
||||||
= 60
|
= 60
|
||||||
@ -14,10 +14,10 @@ func main():
|
|||||||
= 0
|
= 0
|
||||||
|
|
||||||
>> (_max_: [3, 5, 2, 1, 4])
|
>> (_max_: [3, 5, 2, 1, 4])
|
||||||
= 5 : Int?
|
= 5?
|
||||||
|
|
||||||
>> (_max_:abs(): [1, -10, 5])
|
>> (_max_:abs(): [1, -10, 5])
|
||||||
= -10 : Int?
|
= -10?
|
||||||
|
|
||||||
>> (_max_: [Foo(0, 0), Foo(1, 0), Foo(0, 10)])!
|
>> (_max_: [Foo(0, 0), Foo(1, 0), Foo(0, 10)])!
|
||||||
= Foo(x=1, y=0)
|
= Foo(x=1, y=0)
|
||||||
@ -37,7 +37,7 @@ func main():
|
|||||||
= yes
|
= yes
|
||||||
|
|
||||||
>> (<=: [:Int])
|
>> (<=: [:Int])
|
||||||
= none : Bool?
|
= none : Bool
|
||||||
|
|
||||||
>> (<=: [5, 4, 3, 2, 1])!
|
>> (<=: [5, 4, 3, 2, 1])!
|
||||||
= no
|
= no
|
||||||
|
16
test/rng.tm
16
test/rng.tm
@ -12,23 +12,23 @@ func main():
|
|||||||
>> rng:int(1, 1000)
|
>> rng:int(1, 1000)
|
||||||
= 921
|
= 921
|
||||||
>> rng:int64(1, 1000)
|
>> rng:int64(1, 1000)
|
||||||
= 324 : Int64
|
= Int64(324)
|
||||||
>> rng:int32(1, 1000)
|
>> rng:int32(1, 1000)
|
||||||
= 586 : Int32
|
= Int32(586)
|
||||||
>> rng:int16(1, 1000)
|
>> rng:int16(1, 1000)
|
||||||
= 453 : Int16
|
= Int16(453)
|
||||||
>> rng:int8(1, 100)
|
>> rng:int8(1, 100)
|
||||||
= 53 : Int8
|
= Int8(53)
|
||||||
>> rng:byte()
|
>> rng:byte()
|
||||||
= 0xDC : Byte
|
= Byte(0xDC)
|
||||||
>> rng:bytes(10)
|
>> rng:bytes(10)
|
||||||
= [0xA0, 0x5A, 0x10, 0x3F, 0x6C, 0xD1, 0x35, 0xC2, 0x87, 0x8C]
|
= [:Byte, 0xA0, 0x5A, 0x10, 0x3F, 0x6C, 0xD1, 0x35, 0xC2, 0x87, 0x8C]
|
||||||
>> rng:bool(p=0.8)
|
>> rng:bool(p=0.8)
|
||||||
= yes
|
= yes
|
||||||
>> rng:num()
|
>> rng:num()
|
||||||
= 0.03492503353647658 : Num
|
= 0.03492503353647658
|
||||||
>> rng:num32(1, 1000)
|
>> rng:num32(1, 1000)
|
||||||
= 761.05908 : Num32
|
= Num32(761.05908)
|
||||||
|
|
||||||
!! Random array methods:
|
!! Random array methods:
|
||||||
>> nums := [10*i for i in 10]
|
>> nums := [10*i for i in 10]
|
||||||
|
@ -67,7 +67,7 @@ func main():
|
|||||||
>> obj.next = @Foo("abcdef", next=obj)
|
>> obj.next = @Foo("abcdef", next=obj)
|
||||||
>> bytes := obj:serialized()
|
>> bytes := obj:serialized()
|
||||||
>> deserialize(bytes -> @Foo)
|
>> deserialize(bytes -> @Foo)
|
||||||
= @Foo(name="root", next=@Foo(name="abcdef", next=@~1))
|
# = @Foo(name="root", next=@Foo(name="abcdef", next=@~1))
|
||||||
|
|
||||||
do:
|
do:
|
||||||
>> obj := MyEnum.Two(123, "OKAY")
|
>> obj := MyEnum.Two(123, "OKAY")
|
||||||
|
@ -60,7 +60,7 @@ func test_text():
|
|||||||
>> a := @CorecursiveA(b)
|
>> a := @CorecursiveA(b)
|
||||||
>> b.other = a
|
>> b.other = a
|
||||||
>> a
|
>> a
|
||||||
= @CorecursiveA(@CorecursiveB(@~1))
|
# = @CorecursiveA(@CorecursiveB(@~1))
|
||||||
|
|
||||||
func main():
|
func main():
|
||||||
test_literals()
|
test_literals()
|
||||||
@ -71,9 +71,11 @@ func main():
|
|||||||
>> @LinkedList(10, @LinkedList(20))
|
>> @LinkedList(10, @LinkedList(20))
|
||||||
|
|
||||||
>> my_pass := Password("Swordfish")
|
>> my_pass := Password("Swordfish")
|
||||||
= Password(...)
|
>> "$my_pass"
|
||||||
|
= "Password(...)"
|
||||||
>> users_by_password := {my_pass="User1", Password("xxx")="User2"}
|
>> users_by_password := {my_pass="User1", Password("xxx")="User2"}
|
||||||
= {Password(...)="User1", Password(...)="User2"}
|
>> "$users_by_password"
|
||||||
|
= '{Password(...)="User1", Password(...)="User2"}'
|
||||||
>> users_by_password[my_pass]!
|
>> users_by_password[my_pass]!
|
||||||
= "User1"
|
= "User1"
|
||||||
|
|
||||||
|
@ -3,11 +3,11 @@ func main():
|
|||||||
= {"one"=1, "two"=2}
|
= {"one"=1, "two"=2}
|
||||||
|
|
||||||
>> t["one"]
|
>> t["one"]
|
||||||
= 1 : Int?
|
= 1?
|
||||||
>> t["two"]
|
>> t["two"]
|
||||||
= 2 : Int?
|
= 2?
|
||||||
>> t["???"]
|
>> t["???"]
|
||||||
= none : Int?
|
= none:Int
|
||||||
>> t["one"]!
|
>> t["one"]!
|
||||||
= 1
|
= 1
|
||||||
>> t["???"] or -1
|
>> t["???"] or -1
|
||||||
@ -22,7 +22,7 @@ func main():
|
|||||||
>> t.length
|
>> t.length
|
||||||
= 2
|
= 2
|
||||||
>> t.fallback
|
>> t.fallback
|
||||||
= none : {Text,Int}?
|
= none : {Text,Int}
|
||||||
|
|
||||||
>> t.keys
|
>> t.keys
|
||||||
= ["one", "two"]
|
= ["one", "two"]
|
||||||
@ -33,16 +33,16 @@ func main():
|
|||||||
= {"three"=3; fallback={"one"=1, "two"=2}}
|
= {"three"=3; fallback={"one"=1, "two"=2}}
|
||||||
|
|
||||||
>> t2["one"]
|
>> t2["one"]
|
||||||
= 1 : Int?
|
= 1?
|
||||||
>> t2["three"]
|
>> t2["three"]
|
||||||
= 3 : Int?
|
= 3?
|
||||||
>> t2["???"]
|
>> t2["???"]
|
||||||
= none : Int?
|
= none:Int
|
||||||
|
|
||||||
>> t2.length
|
>> t2.length
|
||||||
= 1
|
= 1
|
||||||
>> t2.fallback
|
>> t2.fallback
|
||||||
= {"one"=1, "two"=2} : {Text,Int}?
|
= {"one"=1, "two"=2}?
|
||||||
|
|
||||||
t2_str := ""
|
t2_str := ""
|
||||||
for k,v in t2:
|
for k,v in t2:
|
||||||
@ -95,11 +95,11 @@ func main():
|
|||||||
= no
|
= no
|
||||||
|
|
||||||
>> {1=1, 2=2} <> {2=2, 1=1}
|
>> {1=1, 2=2} <> {2=2, 1=1}
|
||||||
= 0
|
= Int32(0)
|
||||||
>> [{:Int,Int}, {0=0}, {99=99}, {1=1, 2=2, 3=3}, {1=1, 99=99, 3=3}, {1=1, 2=-99, 3=3}, {1=1, 99=-99, 3=4}]:sorted()
|
>> [{:Int,Int}, {0=0}, {99=99}, {1=1, 2=2, 3=3}, {1=1, 99=99, 3=3}, {1=1, 2=-99, 3=3}, {1=1, 99=-99, 3=4}]:sorted()
|
||||||
= [{}, {0=0}, {1=1, 2=-99, 3=3}, {1=1, 2=2, 3=3}, {1=1, 99=99, 3=3}, {1=1, 99=-99, 3=4}, {99=99}]
|
= [{:Int,Int}, {0=0}, {1=1, 2=-99, 3=3}, {1=1, 2=2, 3=3}, {1=1, 99=99, 3=3}, {1=1, 99=-99, 3=4}, {99=99}]
|
||||||
|
|
||||||
>> [{:Int}, {1}, {2}, {99}, {0, 3}, {1, 2}, {99}]:sorted()
|
>> [{:Int}, {1}, {2}, {99}, {0, 3}, {1, 2}, {99}]:sorted()
|
||||||
= [{}, {0, 3}, {1}, {1, 2}, {2}, {99}, {99}]
|
= [{:Int}, {0, 3}, {1}, {1, 2}, {2}, {99}, {99}]
|
||||||
|
|
||||||
|
|
||||||
|
52
test/text.tm
52
test/text.tm
@ -51,23 +51,23 @@ func main():
|
|||||||
|
|
||||||
amelie := "Am$(\UE9)lie"
|
amelie := "Am$(\UE9)lie"
|
||||||
>> amelie:split()
|
>> amelie:split()
|
||||||
= ["A", "m", "é", "l", "i", "e"] : [Text]
|
= ["A", "m", "é", "l", "i", "e"]
|
||||||
>> amelie:utf32_codepoints()
|
>> amelie:utf32_codepoints()
|
||||||
= [65, 109, 233, 108, 105, 101]
|
= [:Int32, 65, 109, 233, 108, 105, 101]
|
||||||
>> amelie:bytes()
|
>> amelie:bytes()
|
||||||
= [0x41, 0x6D, 0xC3, 0xA9, 0x6C, 0x69, 0x65]
|
= [:Byte, 0x41, 0x6D, 0xC3, 0xA9, 0x6C, 0x69, 0x65]
|
||||||
>> Text.from_bytes([:Byte 0x41, 0x6D, 0xC3, 0xA9, 0x6C, 0x69, 0x65])!
|
>> Text.from_bytes([:Byte 0x41, 0x6D, 0xC3, 0xA9, 0x6C, 0x69, 0x65])!
|
||||||
= "Amélie"
|
= "Amélie"
|
||||||
>> Text.from_bytes([Byte(0xFF)])
|
>> Text.from_bytes([Byte(0xFF)])
|
||||||
= none : Text?
|
= none:Text
|
||||||
|
|
||||||
amelie2 := "Am$(\U65\U301)lie"
|
amelie2 := "Am$(\U65\U301)lie"
|
||||||
>> amelie2:split()
|
>> amelie2:split()
|
||||||
= ["A", "m", "é", "l", "i", "e"] : [Text]
|
= ["A", "m", "é", "l", "i", "e"]
|
||||||
>> amelie2:utf32_codepoints()
|
>> amelie2:utf32_codepoints()
|
||||||
= [65, 109, 233, 108, 105, 101]
|
= [:Int32, 65, 109, 233, 108, 105, 101]
|
||||||
>> amelie2:bytes()
|
>> amelie2:bytes()
|
||||||
= [0x41, 0x6D, 0xC3, 0xA9, 0x6C, 0x69, 0x65]
|
= [:Byte, 0x41, 0x6D, 0xC3, 0xA9, 0x6C, 0x69, 0x65]
|
||||||
|
|
||||||
>> amelie:codepoint_names()
|
>> amelie:codepoint_names()
|
||||||
= ["LATIN CAPITAL LETTER A", "LATIN SMALL LETTER M", "LATIN SMALL LETTER E WITH ACUTE", "LATIN SMALL LETTER L", "LATIN SMALL LETTER I", "LATIN SMALL LETTER E"]
|
= ["LATIN CAPITAL LETTER A", "LATIN SMALL LETTER M", "LATIN SMALL LETTER E WITH ACUTE", "LATIN SMALL LETTER L", "LATIN SMALL LETTER I", "LATIN SMALL LETTER E"]
|
||||||
@ -162,7 +162,7 @@ func main():
|
|||||||
>> "one$(\r\n)two$(\r\n)three$(\r\n)":lines()
|
>> "one$(\r\n)two$(\r\n)three$(\r\n)":lines()
|
||||||
= ["one", "two", "three"]
|
= ["one", "two", "three"]
|
||||||
>> "":lines()
|
>> "":lines()
|
||||||
= []
|
= [:Text]
|
||||||
|
|
||||||
!! Test splitting and joining text:
|
!! Test splitting and joining text:
|
||||||
>> "one two three":split($/ /)
|
>> "one two three":split($/ /)
|
||||||
@ -190,7 +190,7 @@ func main():
|
|||||||
= ""
|
= ""
|
||||||
|
|
||||||
>> "":split()
|
>> "":split()
|
||||||
= []
|
= [:Text]
|
||||||
|
|
||||||
!! Test text:find_all()
|
!! Test text:find_all()
|
||||||
>> " #one #two #three ":find_all($/#{alpha}/)
|
>> " #one #two #three ":find_all($/#{alpha}/)
|
||||||
@ -200,26 +200,26 @@ func main():
|
|||||||
= [Match(text="#one", index=2, captures=["one"]), Match(text="#two", index=8, captures=["two"]), Match(text="#three", index=13, captures=["three"])]
|
= [Match(text="#one", index=2, captures=["one"]), Match(text="#two", index=8, captures=["two"]), Match(text="#three", index=13, captures=["three"])]
|
||||||
|
|
||||||
>> " ":find_all($/{alpha}/)
|
>> " ":find_all($/{alpha}/)
|
||||||
= []
|
= [:Match]
|
||||||
|
|
||||||
>> " foo(baz(), 1) doop() ":find_all($/{id}(?)/)
|
>> " foo(baz(), 1) doop() ":find_all($/{id}(?)/)
|
||||||
= [Match(text="foo(baz(), 1)", index=2, captures=["foo", "baz(), 1"]), Match(text="doop()", index=17, captures=["doop", ""])]
|
= [Match(text="foo(baz(), 1)", index=2, captures=["foo", "baz(), 1"]), Match(text="doop()", index=17, captures=["doop", ""])]
|
||||||
|
|
||||||
>> "":find_all($Pattern'')
|
>> "":find_all($Pattern'')
|
||||||
= []
|
= [:Match]
|
||||||
|
|
||||||
>> "Hello":find_all($Pattern'')
|
>> "Hello":find_all($Pattern'')
|
||||||
= []
|
= [:Match]
|
||||||
|
|
||||||
!! Test text:find()
|
!! Test text:find()
|
||||||
>> " one two three ":find($/{id}/, start=-999)
|
>> " one two three ":find($/{id}/, start=-999)
|
||||||
= none : Match?
|
= none : Match
|
||||||
>> " one two three ":find($/{id}/, start=999)
|
>> " one two three ":find($/{id}/, start=999)
|
||||||
= none : Match?
|
= none : Match
|
||||||
>> " one two three ":find($/{id}/)
|
>> " one two three ":find($/{id}/)
|
||||||
= Match(text="one", index=2, captures=["one"]) : Match?
|
= Match(text="one", index=2, captures=["one"])?
|
||||||
>> " one two three ":find($/{id}/, start=5)
|
>> " one two three ":find($/{id}/, start=5)
|
||||||
= Match(text="two", index=8, captures=["two"]) : Match?
|
= Match(text="two", index=8, captures=["two"])?
|
||||||
|
|
||||||
!! Test text slicing:
|
!! Test text slicing:
|
||||||
>> "abcdef":slice()
|
>> "abcdef":slice()
|
||||||
@ -240,13 +240,13 @@ func main():
|
|||||||
>> house:codepoint_names()
|
>> house:codepoint_names()
|
||||||
= ["CJK Unified Ideographs-5BB6"]
|
= ["CJK Unified Ideographs-5BB6"]
|
||||||
>> house:utf32_codepoints()
|
>> house:utf32_codepoints()
|
||||||
= [23478]
|
= [:Int32, 23478]
|
||||||
|
|
||||||
>> "🐧":codepoint_names()
|
>> "🐧":codepoint_names()
|
||||||
= ["PENGUIN"]
|
= ["PENGUIN"]
|
||||||
|
|
||||||
>> Text.from_codepoint_names(["not a valid name here buddy"])
|
>> Text.from_codepoint_names(["not a valid name here buddy"])
|
||||||
= none : Text?
|
= none : Text
|
||||||
|
|
||||||
>> "one two; three four":find_all($/; {..}/)
|
>> "one two; three four":find_all($/; {..}/)
|
||||||
= [Match(text="; three four", index=8, captures=["three four"])]
|
= [Match(text="; three four", index=8, captures=["three four"])]
|
||||||
@ -271,13 +271,13 @@ func main():
|
|||||||
= " good(x, fn(y), BAD(z), w) "
|
= " good(x, fn(y), BAD(z), w) "
|
||||||
|
|
||||||
>> "Hello":matches($/{id}/)
|
>> "Hello":matches($/{id}/)
|
||||||
= ["Hello"] : [Text]?
|
= ["Hello"]?
|
||||||
>> "Hello":matches($/{lower}/)
|
>> "Hello":matches($/{lower}/)
|
||||||
= none : [Text]?
|
= none : [Text]
|
||||||
>> "Hello":matches($/{upper}/)
|
>> "Hello":matches($/{upper}/)
|
||||||
= none : [Text]?
|
= none : [Text]
|
||||||
>> "Hello...":matches($/{id}/)
|
>> "Hello...":matches($/{id}/)
|
||||||
= none : [Text]?
|
= none : [Text]
|
||||||
|
|
||||||
if matches := "hello world":matches($/{id} {id}/):
|
if matches := "hello world":matches($/{id} {id}/):
|
||||||
>> matches
|
>> matches
|
||||||
@ -317,7 +317,7 @@ func main():
|
|||||||
= no
|
= no
|
||||||
|
|
||||||
>> ("hello" ++ " " ++ "Amélie"):reversed()
|
>> ("hello" ++ " " ++ "Amélie"):reversed()
|
||||||
= "eilémA olleh" : Text
|
= "eilémA olleh"
|
||||||
|
|
||||||
do:
|
do:
|
||||||
!! Testing concatenation-stability:
|
!! Testing concatenation-stability:
|
||||||
@ -369,11 +369,11 @@ func main():
|
|||||||
>> "x":middle_pad(5)
|
>> "x":middle_pad(5)
|
||||||
= " x "
|
= " x "
|
||||||
>> "1234":left_pad(8, "XYZ")
|
>> "1234":left_pad(8, "XYZ")
|
||||||
= "XYZX1234" : Text
|
= "XYZX1234"
|
||||||
>> "1234":right_pad(8, "XYZ")
|
>> "1234":right_pad(8, "XYZ")
|
||||||
= "1234XYZX" : Text
|
= "1234XYZX"
|
||||||
>> "1234":middle_pad(9, "XYZ")
|
>> "1234":middle_pad(9, "XYZ")
|
||||||
= "XY1234XYZ" : Text
|
= "XY1234XYZ"
|
||||||
|
|
||||||
>> amelie:width()
|
>> amelie:width()
|
||||||
= 6
|
= 6
|
||||||
|
@ -13,4 +13,4 @@ func main():
|
|||||||
>> when n is 1: Int64(1)
|
>> when n is 1: Int64(1)
|
||||||
is 2: Int64(2)
|
is 2: Int64(2)
|
||||||
is 21 + 2: Int64(23)
|
is 21 + 2: Int64(23)
|
||||||
= 23 : Int64?
|
= Int64(23)?
|
||||||
|
Loading…
Reference in New Issue
Block a user