diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2024-09-11 22:28:43 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2024-09-11 22:28:43 -0400 |
| commit | 3443edf760bd4d53aafb2079f7ab67d98ee5013e (patch) | |
| tree | ec7d77db2a266d27cfb19ad30f4892e84ed2593b | |
| parent | f7ff82913fccde369fcbc666100570e7cad066db (diff) | |
Use optionals for iterators
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | builtins/nextline.c | 108 | ||||
| -rw-r--r-- | builtins/nextline.h | 31 | ||||
| -rw-r--r-- | builtins/path.c | 10 | ||||
| -rw-r--r-- | builtins/tomo.h | 1 | ||||
| -rw-r--r-- | compile.c | 19 | ||||
| -rw-r--r-- | docs/paths.md | 2 | ||||
| -rw-r--r-- | environment.c | 32 | ||||
| -rw-r--r-- | test/iterators.tm | 24 | ||||
| -rw-r--r-- | typecheck.c | 19 |
10 files changed, 44 insertions, 204 deletions
@@ -30,7 +30,7 @@ CFLAGS_PLACEHOLDER="$$(echo -e '\033[2m<flags...>\033[m')" LDLIBS=-lgc -lcord -lm -lunistring -lgmp -ldl BUILTIN_OBJS=builtins/siphash.o builtins/array.o builtins/bool.o builtins/channel.o builtins/nums.o builtins/functions.o builtins/integers.o \ builtins/pointer.o builtins/memory.o builtins/text.o builtins/thread.o builtins/c_string.o builtins/table.o \ - builtins/types.o builtins/util.o builtins/files.o builtins/range.o builtins/shell.o builtins/path.o builtins/nextline.o \ + builtins/types.o builtins/util.o builtins/files.o builtins/range.o builtins/shell.o builtins/path.o \ builtins/optionals.o TESTS=$(patsubst %.tm,%.tm.testresult,$(wildcard test/*.tm)) diff --git a/builtins/nextline.c b/builtins/nextline.c deleted file mode 100644 index b7928939..00000000 --- a/builtins/nextline.c +++ /dev/null @@ -1,108 +0,0 @@ -// An enum used for iterating over lines in a file -// Most of the code here was generated by compiling: -// enum NextLine(Done, Next(line:Text)) - -#include <stdbool.h> -#include <stdint.h> - -#include "siphash.h" -#include "datatypes.h" -#include "nextline.h" -#include "text.h" -#include "util.h" - -static Text_t NextLine$Next$as_text(NextLine$Next_t *obj, bool use_color) -{ - if (!obj) - return Text("Next"); - return Text$concat(use_color ? Text("\x1b[0;1mNext\x1b[m(") : Text("Next("), Text("line="), - Text$as_text((Text_t[1]){obj->$line}, use_color, &Text$info), Text(")")); -} - -public inline NextLine_t NextLine$tagged$Next(Text_t $line) -{ - return (NextLine_t) { - .tag = NextLine$tag$Next,.$Next = { $line } - }; -} - -static Text_t NextLine$as_text(NextLine_t *obj, bool use_color) -{ - if (!obj) - return Text("NextLine"); - switch (obj->tag) { - case NextLine$tag$Done: - return use_color ? Text("\x1b[36;1mNextLine.Done\x1b[m") : Text("NextLine.Done"); - case NextLine$tag$Next: - return Text$concat(use_color ? Text("\x1b[36;1mNextLine.Next\x1b[m(") : - Text("NextLine.Next("), Text("line="), - Text$as_text((Text_t[1]){obj->$Next.$line}, use_color, &Text$info), Text(")")); - default: - return (Text_t) { - .length = 0}; - } -} - -static bool NextLine$equal(const NextLine_t *x, const NextLine_t *y, - const TypeInfo *info) -{ - (void) info; - if (x->tag != y->tag) - return false; - switch (x->tag) { - case NextLine$tag$Done: - return false; - case NextLine$tag$Next: - return generic_equal(&x->$Next, &y->$Next, (&NextLine$Next)); - default: - return 0; - } -} - -static int NextLine$compare(const NextLine_t *x, const NextLine_t *y, - const TypeInfo *info) -{ - (void) info; - int diff = (int)x->tag - (int)y->tag; - if (diff) - return diff; - switch (x->tag) { - case NextLine$tag$Done: - return 0; - case NextLine$tag$Next: - return generic_compare(&x->$Next, &y->$Next, (&NextLine$Next)); - default: - return 0; - } -} - -static uint64_t NextLine$hash(const NextLine_t *obj, const TypeInfo *info) -{ - (void) info; - uint64_t hashes[2] = { (uint64_t) obj->tag, 0 }; - switch (obj->tag) { - case NextLine$tag$Done: - break; - case NextLine$tag$Next: - hashes[1] = generic_hash(&obj->$Next, (&NextLine$Next)); - break; - default: break; - } - return siphash24((void *) &hashes, sizeof(hashes)); -} - -public const TypeInfo NextLine$Done = { 0, 0, {.tag = EmptyStructInfo,.EmptyStructInfo.name = - "NextLine$Done" } }; -public const TypeInfo NextLine$Next = { 24, 8, {.tag = CustomInfo,.CustomInfo = - {.as_text = - (void *) NextLine$Next$as_text,.hash = - (void *) Text$hash,.compare = - (void *) Text$compare,.equal = - (void *) Text$equal,} } }; -public const TypeInfo NextLine = { 32, 8, {.tag = CustomInfo,.CustomInfo = - {.as_text = (void *) NextLine$as_text,.equal = - (void *) NextLine$equal,.hash = - (void *) NextLine$hash,.compare = - (void *) NextLine$compare} } }; - -// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/builtins/nextline.h b/builtins/nextline.h deleted file mode 100644 index ce4c9fab..00000000 --- a/builtins/nextline.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "datatypes.h" -#include "types.h" - -// An enum used for iterating over lines in a file -// Most of the code here was generated by compiling: -// enum NextLine(Done, Next(line:Text)) - -typedef struct NextLine_s NextLine_t; -typedef struct NextLine$Done_s NextLine$Done_t; -#pragma GCC diagnostic ignored "-Wpedantic" -struct NextLine$Done_s { -}; -typedef struct NextLine$Next_s NextLine$Next_t; -struct NextLine$Next_s { - Text_t $line; -}; -struct NextLine_s { - enum { NextLine$tag$Done = 0, NextLine$tag$Next = 1 } tag; - union { - NextLine$Done_t $Done; - NextLine$Next_t $Next; - }; -}; -extern const TypeInfo NextLine; -extern const TypeInfo NextLine$Done; -extern const TypeInfo NextLine$Next; -NextLine_t NextLine$tagged$Next(Text_t $line); - -// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/builtins/path.c b/builtins/path.c index ab032927..8864b7de 100644 --- a/builtins/path.c +++ b/builtins/path.c @@ -16,7 +16,7 @@ #include "files.h" #include "functions.h" #include "integers.h" -#include "nextline.h" +#include "optionals.h" #include "path.h" #include "text.h" #include "types.h" @@ -433,16 +433,16 @@ static void _line_reader_cleanup(FILE **f) } } -static NextLine_t _next_line(FILE **f) +static Text_t _next_line(FILE **f) { - if (!f || !*f) return (NextLine_t){NextLine$tag$Done}; + if (!f || !*f) return NULL_TEXT; char *line = NULL; size_t size = 0; ssize_t len = getline(&line, &size, *f); if (len <= 0) { _line_reader_cleanup(f); - return (NextLine_t){NextLine$tag$Done}; + return NULL_TEXT; } while (len > 0 && (line[len-1] == '\r' || line[len-1] == '\n')) @@ -453,7 +453,7 @@ static NextLine_t _next_line(FILE **f) Text_t line_text = Text$format("%.*s", len, line); free(line); - return NextLine$tagged$Next(line_text); + return line_text; } public Closure_t Path$by_line(Path_t path) diff --git a/builtins/tomo.h b/builtins/tomo.h index 4a52da7c..9354ccab 100644 --- a/builtins/tomo.h +++ b/builtins/tomo.h @@ -17,7 +17,6 @@ #include "datatypes.h" #include "functions.h" #include "integers.h" -#include "nextline.h" #include "macros.h" #include "memory.h" #include "nums.h" @@ -318,7 +318,9 @@ static CORD compile_inline_block(env_t *env, ast_t *ast) static CORD optional_var_into_nonnull(binding_t *b) { - switch (b->type->tag) { + type_t *t = b->type->tag == OptionalType ? Match(b->type, OptionalType)->type : b->type; + switch (t->tag) { + case OptionalType: case IntType: return CORD_all(b->code, ".i"); case StructType: @@ -1212,6 +1214,7 @@ CORD compile_statement(env_t *env, ast_t *ast) code = CORD_all(code, compile_declaration(iter_t, "next"), " = ", compile(env, for_->iter), ";\n"); auto fn = iter_t->tag == ClosureType ? Match(Match(iter_t, ClosureType)->fn, FunctionType) : Match(iter_t, FunctionType); + assert(fn->ret->tag == OptionalType); code = CORD_all(code, compile_declaration(fn->ret, "cur"), ";\n"); // Iteration enum CORD next_fn; @@ -1228,9 +1231,17 @@ CORD compile_statement(env_t *env, ast_t *ast) next_fn = "next"; } - env_t *enum_env = Match(fn->ret, EnumType)->env; - next_fn = CORD_all("(cur=", next_fn, iter_t->tag == ClosureType ? "(next.userdata)" : "()", ").tag == ", - namespace_prefix(enum_env->libname, enum_env->namespace), "tag$Next"); + env_t *tmp_env = fresh_scope(env); + set_binding(tmp_env, "cur", new(binding_t, .type=fn->ret, .code="cur")); + next_fn = CORD_all("(cur=", next_fn, iter_t->tag == ClosureType ? "(next.userdata)" : "()", ", ", + compile_optional_check(tmp_env, FakeAST(Var, "cur")), ")"); + + if (for_->vars) { + naked_body = CORD_all( + compile_declaration(Match(fn->ret, OptionalType)->type, CORD_all("$", Match(for_->vars->ast, Var)->name)), + " = ", optional_var_into_nonnull(new(binding_t, .type=fn->ret, .code="cur")), ";\n", + naked_body); + } if (for_->empty) { code = CORD_all(code, "if (", next_fn, ") {\n" diff --git a/docs/paths.md b/docs/paths.md index a2b7c519..de483168 100644 --- a/docs/paths.md +++ b/docs/paths.md @@ -95,7 +95,7 @@ Returns an iterator that can be used to iterate over a file one line at a time. **Usage:** ```markdown -by_line(path: Path) -> func()->NextLine +by_line(path: Path) -> func()->Text? ``` **Parameters:** diff --git a/environment.c b/environment.c index d28e509d..324021b2 100644 --- a/environment.c +++ b/environment.c @@ -509,32 +509,14 @@ env_t *for_scope(env_t *env, ast_t *ast) } case FunctionType: case ClosureType: { auto fn = iter_t->tag == ClosureType ? Match(Match(iter_t, ClosureType)->fn, FunctionType) : Match(iter_t, FunctionType); - if (fn->ret->tag != EnumType) - code_err(for_->iter, "Iterator functions must return an enum with a Done and Next field"); - auto iter_enum = Match(fn->ret, EnumType); - type_t *next_type = NULL; - for (tag_t *tag = iter_enum->tags; tag; tag = tag->next) { - if (streq(tag->name, "Done")) { - if (Match(tag->type, StructType)->fields) - code_err(for_->iter, "This iterator function returns an enum with a Done field that has values, when none are allowed"); - } else if (streq(tag->name, "Next")) { - next_type = tag->type; - } else { - code_err(for_->iter, "This iterator function returns an enum with a value that isn't Done or Next: %s", tag->name); - } - } + if (fn->ret->tag != OptionalType) + code_err(for_->iter, "Iterator functions must return an optional type, not %T", fn->ret); - if (!next_type) - code_err(for_->iter, "This iterator function returns an enum that doesn't have a Next field"); - - arg_t *iter_field = Match(next_type, StructType)->fields; - for (ast_list_t *var = for_->vars; var; var = var->next) { - if (!iter_field) - code_err(var->ast, "This is one variable too many for this iterator, which returns a %T", fn->ret); - const char *name = Match(var->ast, Var)->name; - type_t *t = get_arg_type(env, iter_field); - set_binding(scope, name, new(binding_t, .type=t, .code=CORD_cat("cur.$Next.$", iter_field->name))); - iter_field = iter_field->next; + if (for_->vars) { + if (for_->vars->next) + code_err(for_->vars->next->ast, "This is too many variables for this loop"); + const char *var = Match(for_->vars->ast, Var)->name; + set_binding(scope, var, new(binding_t, .type=Match(fn->ret, OptionalType)->type, .code=CORD_cat("$", var))); } return scope; } diff --git a/test/iterators.tm b/test/iterators.tm index 0a2d2818..6893c595 100644 --- a/test/iterators.tm +++ b/test/iterators.tm @@ -1,33 +1,33 @@ -enum PairIteration(Done, Next(x:Text, y:Text)) -func pairwise(strs:[Text])->func()->PairIteration: +struct Pair(x:Text, y:Text) + +func pairwise(strs:[Text])->func()->Pair?: i := 1 return func(): - if i + 1 > strs.length: return PairIteration.Done + if i + 1 > strs.length: return !Pair i += 1 - return PairIteration.Next(strs[i-1], strs[i]) + return Pair(strs[i-1], strs[i])? -enum RangeIteration(Done, Next(i:Int)) -func range(first:Int, last:Int)->func()->RangeIteration: +func range(first:Int, last:Int)->func()->Int?: i := first return func(): if i > last: - return RangeIteration.Done + return !Int i += 1 - return RangeIteration.Next(i-1) + return (i-1)? func main(): values := ["A", "B", "C", "D"] - >> ((++) "($(foo)$(baz))" for foo, baz in pairwise(values)) + >> ((++) "($(foo.x)$(foo.y))" for foo in pairwise(values)) = "(AB)(BC)(CD)" - >> ["$(foo)$(baz)" for foo, baz in pairwise(values)] + >> ["$(foo.x)$(foo.y)" for foo in pairwise(values)] = ["AB", "BC", "CD"] do: result := [:Text] - for foo, baz in pairwise(values): - result:insert("$(foo)$(baz)") + for foo in pairwise(values): + result:insert("$(foo.x)$(foo.y)") >> result = ["AB", "BC", "CD"] diff --git a/typecheck.c b/typecheck.c index 49ed7577..e500dfa7 100644 --- a/typecheck.c +++ b/typecheck.c @@ -1081,22 +1081,9 @@ type_t *get_type(env_t *env, ast_t *ast) Match(Match(iter_value_t, ClosureType)->fn, FunctionType) : Match(iter_value_t, FunctionType); if (fn->args) code_err(reduction->iter, "I expected this iterator function to not take any arguments, but it's %T", iter_value_t); - if (fn->ret->tag != EnumType) - code_err(reduction->iter, "I expected this iterator function to return an enum, but it's %T", iter_value_t); - value_t = NULL; - for (tag_t *tag = Match(fn->ret, EnumType)->tags; tag; tag = tag->next) { - if (streq(tag->name, "Next")) { - arg_t *fields = Match(tag->type, StructType)->fields; - if (!fields || fields->next) - code_err(reduction->iter, - "I expected this iterator function to return an enum with a Next() that has exactly one value, not %T", - tag->type); - value_t = fields->type; - break; - } - } - if (!value_t) - code_err(reduction->iter, "This iterator function doesn't return an enum with a Next() value"); + if (fn->ret->tag != OptionalType) + code_err(reduction->iter, "I expected this iterator function to return an optional value, but it's %T", iter_value_t); + value_t = Match(fn->ret, OptionalType)->type; break; } default: code_err(reduction->iter, "I don't know how to do a reduction over %T values", iter_t); |
