diff --git a/compile.c b/compile.c index de5531c..3aeebfd 100644 --- a/compile.c +++ b/compile.c @@ -2817,6 +2817,16 @@ CORD compile(env_t *env, ast_t *ast) (long)(call->self->end - call->self->file->text), compile_arguments(env, ast, arg_spec, call->args)), optional_into_nonnull(self_value_t, "opt"), "; })"); + } else if (streq(call->name, "or_exit")) { + CORD self = compile_to_pointer_depth(env, call->self, 0, false); + arg_t *arg_spec = new(arg_t, .name="message", .type=Type(OptionalType, .type=TEXT_TYPE), + .default_val=FakeAST(Null, .type=new(type_ast_t, .tag=VarTypeAST, .__data.VarTypeAST.name="Text")), + .next=new(arg_t, .name="code", .type=Type(IntType, .bits=TYPE_IBITS32), + .default_val=FakeAST(Int, .bits=IBITS32, .str="1"))); + return CORD_all("({ ", compile_declaration(self_value_t, "opt"), " = ", self, "; ", + "if (", check_null(self_value_t, "opt"), ")\n", + "tomo_exit(", compile_arguments(env, ast, arg_spec, call->args), ");\n", + optional_into_nonnull(self_value_t, "opt"), "; })"); } code_err(ast, "There is no '%s' method for optional %T values", call->name, nonnull); } diff --git a/docs/README.md b/docs/README.md index 642293c..13401f3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -76,14 +76,15 @@ Exits the program with a given status and optionally prints a message. **Usage:** ```markdown -ask(message:Text = "", status:Int32 = 0[32]) -> Void +ask(message:Text? = !Text, status:Int32 = 1[32]) -> Void ``` **Parameters:** - `message`: If nonempty, this message will be printed (with a newline) before exiting. -- `status`: The status code that the program with exit with. +- `status`: The status code that the program with exit with (default: 1, which + is a failure status). **Returns:** This function never returns. diff --git a/docs/optionals.md b/docs/optionals.md index 1414ca6..54fe5f5 100644 --- a/docs/optionals.md +++ b/docs/optionals.md @@ -30,7 +30,7 @@ having to use a more generalized form of `enum` which may have different naming conventions and which would generate a lot of unnecessary code. In addition to using conditionals to check for null values, you can also use -`:or_else(fallback)` or `:or_fail()`: +`:or_else(fallback)` or `:or_fail()` or `:or_exit()`: ```tomo maybe_x := 5? @@ -42,6 +42,13 @@ maybe_x := 5? maybe_x = !Int >> maybe_x:or_else(-1) = -1 : Int ->> maybe_x:or_fail() +>> maybe_x:or_fail("No value!") # Failure! + + +maybe_x = !Int +>> maybe_x:or_exit() += -1 : Int +>> maybe_x:or_exit("No value!") +# Exit! ``` diff --git a/environment.c b/environment.c index 6810f37..10aa0b5 100644 --- a/environment.c +++ b/environment.c @@ -42,9 +42,11 @@ env_t *new_compilation_unit(CORD *libname) .default_val=FakeAST(Bool, true)))), .ret=TEXT_TYPE)}}, {"exit", {.code="tomo_exit", - .type=Type(FunctionType, .args=new(arg_t, .name="message", .type=Type(TextType), .default_val=FakeAST(TextLiteral), - .next=new(arg_t, .name="code", .type=Type(IntType, .bits=TYPE_IBITS32), - .default_val=FakeAST(Int, .bits=IBITS32, .str="0"))), .ret=Type(AbortType))}}, + .type=Type(FunctionType, .args=new( + 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")), + .next=new(arg_t, .name="code", .type=Type(IntType, .bits=TYPE_IBITS32), + .default_val=FakeAST(Int, .bits=IBITS32, .str="1"))), .ret=Type(AbortType))}}, {"fail", {.code="fail", .type=Type(FunctionType, .args=new(arg_t, .name="message", .type=Type(CStringType)), .ret=Type(AbortType))}}, {"sleep", {.code="sleep_num", .type=Type(FunctionType, .args=new(arg_t, .name="seconds", .type=Type(NumType, .bits=TYPE_NBITS64)), .ret=Type(VoidType))}}, {"USE_COLOR", {.code="USE_COLOR", .type=Type(BoolType)}}, diff --git a/examples/game/game.tm b/examples/game/game.tm index c7020f5..8c5124d 100644 --- a/examples/game/game.tm +++ b/examples/game/game.tm @@ -9,9 +9,7 @@ func main(map=(./map.txt)): extern InitWindow:func(w:Int32, h:Int32, title:CString)->Void InitWindow(1600, 900, "raylib [core] example - 2d camera") - map_contents := if contents := map:read(): - contents - else: exit(code=1, "Could not find the game map: $map") + map_contents := map:read():or_exit("Could not find the game map: $map") World.CURRENT:load_map(map_contents) diff --git a/examples/ini.tm b/examples/ini.tm index 5066879..afd71a3 100644 --- a/examples/ini.tm +++ b/examples/ini.tm @@ -32,13 +32,13 @@ func parse_ini(path:Path)->{Text:{Text:Text}}: func main(path:Path, key:Text): keys := key:split($Pattern"/") if keys.length > 2: - exit(1, message=" + exit(" Too many arguments! $_USAGE ") if not path:is_file() or path:is_pipe(): - exit(code=1, "Could not read file: $(path.text_content)") + exit("Could not read file: $(path.text_content)") data := parse_ini(path) if keys.length < 1 or keys[1] == '*': @@ -47,7 +47,7 @@ func main(path:Path, key:Text): section := keys[1]:lower() if not data:has(section): - exit(1, message="Invalid section name: $section; valid names: $(", ":join([k:quoted() for k in data.keys]))") + exit("Invalid section name: $section; valid names: $(", ":join([k:quoted() for k in data.keys]))") section_data := data:get(section) if keys.length < 2 or keys[2] == '*': @@ -56,6 +56,6 @@ func main(path:Path, key:Text): section_key := keys[2]:lower() if not section_data:has(section_key): - exit(1, message="Invalid key: $section_key; valid keys: $(", ":join(section_data.keys))") + exit("Invalid key: $section_key; valid keys: $(", ":join(section_data.keys))") say(section_data:get(section_key)) diff --git a/examples/tomodeps.tm b/examples/tomodeps.tm index 209f14e..a427c5f 100644 --- a/examples/tomodeps.tm +++ b/examples/tomodeps.tm @@ -103,7 +103,7 @@ func draw_tree(dep:Dependency, dependencies:{Dependency:{Dependency}}): func main(files:[Text]): if files.length == 0: - exit(1, message=" + exit(" Please provide at least one file! $_USAGE ") diff --git a/test/optionals.tm b/test/optionals.tm index ced4227..05008e2 100644 --- a/test/optionals.tm +++ b/test/optionals.tm @@ -89,6 +89,9 @@ func main(): >> (5?):or_fail() = 5 : Int + >> (5?):or_exit() + = 5 : Int + >> (!Int):or_else(-1) = -1 : Int diff --git a/typecheck.c b/typecheck.c index 933f1f9..5d74a3c 100644 --- a/typecheck.c +++ b/typecheck.c @@ -845,6 +845,7 @@ type_t *get_type(env_t *env, ast_t *ast) type_t *nonnull = Match(self_value_t, OptionalType)->type; if (streq(call->name, "or_else")) return nonnull; else if (streq(call->name, "or_fail")) return nonnull; + else if (streq(call->name, "or_exit")) return nonnull; code_err(ast, "There is no '%s' method for optional %T values", call->name, nonnull); } default: {