aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--builtins/nextline.c108
-rw-r--r--builtins/nextline.h31
-rw-r--r--builtins/path.c10
-rw-r--r--builtins/tomo.h1
-rw-r--r--compile.c19
-rw-r--r--docs/paths.md2
-rw-r--r--environment.c32
-rw-r--r--test/iterators.tm24
-rw-r--r--typecheck.c19
10 files changed, 44 insertions, 204 deletions
diff --git a/Makefile b/Makefile
index 89eaa619..1fd0dd4f 100644
--- a/Makefile
+++ b/Makefile
@@ -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"
diff --git a/compile.c b/compile.c
index 2f72fc59..8d017a42 100644
--- a/compile.c
+++ b/compile.c
@@ -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);