Use optionals for iterators
This commit is contained in:
parent
f7ff82913f
commit
3443edf760
2
Makefile
2
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))
|
||||
|
||||
|
@ -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
|
@ -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
|
@ -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)
|
||||
|
@ -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"
|
||||
|
19
compile.c
19
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"
|
||||
|
@ -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:**
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"]
|
||||
|
||||
|
19
typecheck.c
19
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);
|
||||
|
Loading…
Reference in New Issue
Block a user