aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-09-21 16:54:36 -0400
committerBruce Hill <bruce@bruce-hill.com>2025-09-21 16:54:36 -0400
commit1cec086a6034ad546977cae7aeaf4bb876d21970 (patch)
treecd2889aea7ca830359ba329a3f82b96e706bcdee
parent0ab878ff4f72f7f6f6f64c1fc5d8023cd19fbe82 (diff)
Deprecate `>> x ... = val` form of doctests and rename them to 'debugdoctests-to-debuglog
logs', also add multi-expression support
-rw-r--r--CHANGES.md2
-rw-r--r--README.md2
-rw-r--r--src/ast.c9
-rw-r--r--src/ast.h7
-rw-r--r--src/compile/README.md2
-rw-r--r--src/compile/compilation.h2
-rw-r--r--src/compile/debuglog.c115
-rw-r--r--src/compile/debuglog.h (renamed from src/compile/doctests.h)4
-rw-r--r--src/compile/doctests.c118
-rw-r--r--src/compile/expressions.c2
-rw-r--r--src/compile/functions.c5
-rw-r--r--src/compile/statements.c2
-rw-r--r--src/formatter/formatter.c16
-rw-r--r--src/formatter/utils.c8
-rw-r--r--src/parse/statements.c25
-rw-r--r--src/parse/statements.h2
-rw-r--r--src/stdlib/stdlib.c38
-rw-r--r--src/stdlib/stdlib.h8
-rw-r--r--src/typecheck.c12
19 files changed, 169 insertions, 210 deletions
diff --git a/CHANGES.md b/CHANGES.md
index dd015b5f..fafd3e15 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -16,6 +16,8 @@
- Use `C_code` instead.
- Deprecated the postfix `?` to make values optional.
- Explicitly optional values can be declared as `my_var : T? = value`.
+- Deprecated `>> ... = ...` form of doctests. They are now called "debug logs"
+ and you can specify multiple values: `>> a, b, c`
- Added a `--format` flag to the `tomo` binary that autoformats your code
(currently unstable, do not rely on it just yet).
- Standardized text methods for Unicode encodings:
diff --git a/README.md b/README.md
index a922aeab..ac7e91ba 100644
--- a/README.md
+++ b/README.md
@@ -78,7 +78,7 @@ are implemented in Tomo.
[text](docs/text.md).
- Full-featured [libraries/modules](docs/libraries.md)
- [Full UTF8 support](docs/text.md) for all text operations
-- Built-in doctests with syntax highlighting
+- Built-in debugging prints with syntax highlighting
- [Automatic command line argument parsing with type safety](docs/command-line-parsing.md)
- [Easy interoperability with C](docs/c-interoperability.md)
- Built-in [data serialization and deserialization](docs/serialization.md).
diff --git a/src/ast.c b/src/ast.c
index 0dcda5dc..80127cf7 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -270,7 +270,7 @@ Text_t ast_to_sexp(ast_t *ast) {
T(Index, "(Index ", ast_to_sexp(data.indexed), " ", ast_to_sexp(data.index), ")");
T(FieldAccess, "(FieldAccess ", ast_to_sexp(data.fielded), " \"", data.field, "\")");
T(NonOptional, "(NonOptional ", ast_to_sexp(data.value), ")");
- T(DocTest, "(DocTest ", ast_to_sexp(data.expr), optional_sexp("expected", data.expected), ")");
+ T(DebugLog, "(DebugLog ", ast_list_to_sexp(data.values), ")");
T(Assert, "(Assert ", ast_to_sexp(data.expr), " ", optional_sexp("message", data.message), ")");
T(Use, "(Use ", optional_sexp("var", data.var), " ", quoted_text(data.path), ")");
T(InlineCCode, "(InlineCCode ", ast_list_to_sexp(data.chunks), optional_type_sexp("type", data.type_ast), ")");
@@ -658,10 +658,9 @@ void ast_visit(ast_t *ast, void (*visitor)(ast_t *, void *), void *userdata) {
ast_visit(Match(ast, NonOptional)->value, visitor, userdata);
return;
}
- case DocTest: {
- DeclareMatch(test, ast, DocTest);
- ast_visit(test->expr, visitor, userdata);
- ast_visit(test->expected, visitor, userdata);
+ case DebugLog: {
+ DeclareMatch(show, ast, DebugLog);
+ ast_visit_list(show->values, visitor, userdata);
return;
}
case Assert: {
diff --git a/src/ast.h b/src/ast.h
index 90191219..142bea19 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -270,7 +270,7 @@ typedef enum {
Index,
FieldAccess,
NonOptional,
- DocTest,
+ DebugLog,
Assert,
Use,
InlineCCode,
@@ -439,9 +439,8 @@ struct ast_s {
ast_t *value;
} NonOptional;
struct {
- ast_t *expr, *expected;
- bool skip_source : 1;
- } DocTest;
+ ast_list_t *values;
+ } DebugLog;
struct {
ast_t *expr, *message;
} Assert;
diff --git a/src/compile/README.md b/src/compile/README.md
index df76d648..7a841231 100644
--- a/src/compile/README.md
+++ b/src/compile/README.md
@@ -11,7 +11,7 @@ This directory contains the source files for actual cross-compilation
- Comparisons (`a == b`, `a > b`, etc): [comparisons.c](comparisons.c)
- Conditionals (`if` statements): [conditionals.c](conditionals.c)
- Variable declarations: [declarations.c](declarations.c)
-- Doctests (`>> test`): [doctests.c](doctests.c)
+- Debug logs (`>> expr`): [debuglog.c](debuglog.c)
- Enums (`enum`): [enums.c](enums.c)
- General logic for compiling expressions: [expressions.c](expressions.c)
- Field accesses (`foo.baz`) [fieldaccess.c](fieldaccess.c)
diff --git a/src/compile/compilation.h b/src/compile/compilation.h
index d881684b..5c949836 100644
--- a/src/compile/compilation.h
+++ b/src/compile/compilation.h
@@ -10,8 +10,8 @@
#include "comparisons.h" // IWYU pragma: export
#include "compilation.h" // IWYU pragma: export
#include "conditionals.h" // IWYU pragma: export
+#include "debuglog.h" // IWYU pragma: export
#include "declarations.h" // IWYU pragma: export
-#include "doctests.h" // IWYU pragma: export
#include "enums.h" // IWYU pragma: export
#include "expressions.h" // IWYU pragma: export
#include "fieldaccess.h" // IWYU pragma: export
diff --git a/src/compile/debuglog.c b/src/compile/debuglog.c
new file mode 100644
index 00000000..498820ee
--- /dev/null
+++ b/src/compile/debuglog.c
@@ -0,0 +1,115 @@
+// This file defines how to compile debug_log
+
+#include "../ast.h"
+#include "../config.h"
+#include "../environment.h"
+#include "../stdlib/datatypes.h"
+#include "../stdlib/text.h"
+#include "../stdlib/util.h"
+#include "../typecheck.h"
+#include "compilation.h"
+
+public
+Text_t compile_debug_log(env_t *env, ast_t *ast) {
+ DeclareMatch(debug, ast, DebugLog);
+ Text_t code = EMPTY_TEXT;
+ for (ast_list_t *value = debug->values; value; value = value->next) {
+ type_t *expr_t = get_type(env, value->ast);
+ if (!expr_t) code_err(value->ast, "I couldn't figure out the type of this expression");
+
+ Text_t setup = EMPTY_TEXT;
+ Text_t value_code;
+ if (value->ast->tag == Declare) {
+ DeclareMatch(decl, value->ast, Declare);
+ type_t *t = decl->type ? parse_type_ast(env, decl->type) : get_type(env, decl->value);
+ if (t->tag == FunctionType) t = Type(ClosureType, t);
+ Text_t var = Texts("_$", Match(decl->var, Var)->name);
+ Text_t val_code = compile_declared_value(env, value->ast);
+ setup = Texts(compile_declaration(t, var), ";\n");
+ value_code = Texts("(", var, " = ", val_code, ")");
+ expr_t = t;
+ } else if (value->ast->tag == Assign) {
+ DeclareMatch(assign, value->ast, Assign);
+ if (!assign->targets->next && assign->targets->ast->tag == Var && is_idempotent(assign->targets->ast)) {
+ // Common case: assigning to one variable:
+ type_t *lhs_t = get_type(env, assign->targets->ast);
+ if (assign->targets->ast->tag == Index && lhs_t->tag == OptionalType
+ && value_type(get_type(env, Match(assign->targets->ast, Index)->indexed))->tag == TableType)
+ lhs_t = Match(lhs_t, OptionalType)->type;
+ if (has_stack_memory(lhs_t))
+ code_err(value->ast, "Stack references cannot be assigned "
+ "to variables because the "
+ "variable's scope may outlive the "
+ "scope of the stack memory.");
+ env_t *val_scope = with_enum_scope(env, lhs_t);
+ value_code = Texts("(",
+ compile_assignment(env, assign->targets->ast,
+ compile_to_type(val_scope, assign->values->ast, lhs_t)),
+ ")");
+ expr_t = lhs_t;
+ } else {
+ // Multi-assign or assignment to potentially non-idempotent
+ // targets
+ value_code = Text("({ // Assignment\n");
+
+ int64_t i = 1;
+ for (ast_list_t *target = assign->targets, *assign_value = assign->values; target && assign_value;
+ target = target->next, assign_value = assign_value->next) {
+ type_t *lhs_t = get_type(env, target->ast);
+ if (target->ast->tag == Index && lhs_t->tag == OptionalType
+ && value_type(get_type(env, Match(target->ast, Index)->indexed))->tag == TableType)
+ lhs_t = Match(lhs_t, OptionalType)->type;
+ if (has_stack_memory(lhs_t))
+ code_err(ast, "Stack references cannot be assigned to "
+ "variables because the "
+ "variable's scope may outlive the scope "
+ "of the stack memory.");
+ if (target == assign->targets) expr_t = lhs_t;
+ env_t *val_scope = with_enum_scope(env, lhs_t);
+ Text_t val_code = compile_to_type(val_scope, assign_value->ast, lhs_t);
+ value_code = Texts(value_code, compile_type(lhs_t), " $", i, " = ", val_code, ";\n");
+ i += 1;
+ }
+ i = 1;
+ for (ast_list_t *target = assign->targets; target; target = target->next) {
+ value_code = Texts(value_code, compile_assignment(env, target->ast, Texts("$", i)), ";\n");
+ i += 1;
+ }
+
+ value_code = Texts(value_code, "$1; })");
+ }
+ } else if (is_update_assignment(value->ast)) {
+ binary_operands_t update = UPDATE_OPERANDS(value->ast);
+ type_t *lhs_t = get_type(env, update.lhs);
+ if (update.lhs->tag == Index) {
+ type_t *indexed = value_type(get_type(env, Match(update.lhs, Index)->indexed));
+ if (indexed->tag == TableType && Match(indexed, TableType)->default_value == NULL)
+ code_err(update.lhs, "Update assignments are not currently "
+ "supported for tables");
+ }
+
+ ast_t *update_var = new (ast_t);
+ *update_var = *value->ast;
+ update_var->__data.PlusUpdate.lhs = LiteralCode(Text("(*expr)"), .type = lhs_t); // UNSAFE
+ value_code =
+ Texts("({", compile_declaration(Type(PointerType, lhs_t), Text("expr")), " = &(",
+ compile_lvalue(env, update.lhs), "); ", compile_statement(env, update_var), "; *expr; })");
+ expr_t = lhs_t;
+ } else if (expr_t->tag == VoidType || expr_t->tag == AbortType || expr_t->tag == ReturnType) {
+ value_code = Texts("({", compile_statement(env, value->ast), " NULL;})");
+ } else {
+ value_code = compile(env, value->ast);
+ }
+ if (expr_t->tag == VoidType || expr_t->tag == AbortType) {
+ value_code = Texts(setup, "inspect_void(", value_code, ", ", compile_type_info(expr_t), ", ",
+ (int64_t)(value->ast->start - value->ast->file->text), ", ",
+ (int64_t)(value->ast->end - value->ast->file->text), ");");
+ } else {
+ value_code = Texts(setup, "inspect(", compile_type(expr_t), ", ", value_code, ", ",
+ compile_type_info(expr_t), ", ", (int64_t)(value->ast->start - value->ast->file->text),
+ ", ", (int64_t)(value->ast->end - value->ast->file->text), ");");
+ }
+ code = Texts(code, value_code);
+ }
+ return code;
+}
diff --git a/src/compile/doctests.h b/src/compile/debuglog.h
index 06603e1a..30b14776 100644
--- a/src/compile/doctests.h
+++ b/src/compile/debuglog.h
@@ -1,4 +1,4 @@
-// This file defines how to compile doctests
+// This file defines how to compile debug logs
#pragma once
@@ -6,4 +6,4 @@
#include "../environment.h"
#include "../stdlib/datatypes.h"
-Text_t compile_doctest(env_t *env, ast_t *ast);
+Text_t compile_debug_log(env_t *env, ast_t *ast);
diff --git a/src/compile/doctests.c b/src/compile/doctests.c
deleted file mode 100644
index 20081ae7..00000000
--- a/src/compile/doctests.c
+++ /dev/null
@@ -1,118 +0,0 @@
-// This file defines how to compile doctests
-
-#include "../ast.h"
-#include "../config.h"
-#include "../environment.h"
-#include "../stdlib/datatypes.h"
-#include "../stdlib/text.h"
-#include "../stdlib/util.h"
-#include "../typecheck.h"
-#include "compilation.h"
-
-public
-Text_t compile_doctest(env_t *env, ast_t *ast) {
- DeclareMatch(test, ast, DocTest);
- type_t *expr_t = get_type(env, test->expr);
- if (!expr_t) code_err(test->expr, "I couldn't figure out the type of this expression");
-
- Text_t setup = EMPTY_TEXT;
- Text_t test_code;
- if (test->expr->tag == Declare) {
- DeclareMatch(decl, test->expr, Declare);
- type_t *t = decl->type ? parse_type_ast(env, decl->type) : get_type(env, decl->value);
- if (t->tag == FunctionType) t = Type(ClosureType, t);
- Text_t var = Texts("_$", Match(decl->var, Var)->name);
- Text_t val_code = compile_declared_value(env, test->expr);
- setup = Texts(compile_declaration(t, var), ";\n");
- test_code = Texts("(", var, " = ", val_code, ")");
- expr_t = t;
- } else if (test->expr->tag == Assign) {
- DeclareMatch(assign, test->expr, Assign);
- if (!assign->targets->next && assign->targets->ast->tag == Var && is_idempotent(assign->targets->ast)) {
- // Common case: assigning to one variable:
- type_t *lhs_t = get_type(env, assign->targets->ast);
- if (assign->targets->ast->tag == Index && lhs_t->tag == OptionalType
- && value_type(get_type(env, Match(assign->targets->ast, Index)->indexed))->tag == TableType)
- lhs_t = Match(lhs_t, OptionalType)->type;
- if (has_stack_memory(lhs_t))
- code_err(test->expr, "Stack references cannot be assigned "
- "to variables because the "
- "variable's scope may outlive the "
- "scope of the stack memory.");
- env_t *val_scope = with_enum_scope(env, lhs_t);
- Text_t value = compile_to_type(val_scope, assign->values->ast, lhs_t);
- test_code = Texts("(", compile_assignment(env, assign->targets->ast, value), ")");
- expr_t = lhs_t;
- } else {
- // Multi-assign or assignment to potentially non-idempotent
- // targets
- if (test->expected && assign->targets->next)
- code_err(ast, "Sorry, but doctesting with '=' is not "
- "supported for "
- "multi-assignments");
-
- test_code = Text("({ // Assignment\n");
-
- int64_t i = 1;
- for (ast_list_t *target = assign->targets, *value = assign->values; target && value;
- target = target->next, value = value->next) {
- type_t *lhs_t = get_type(env, target->ast);
- if (target->ast->tag == Index && lhs_t->tag == OptionalType
- && value_type(get_type(env, Match(target->ast, Index)->indexed))->tag == TableType)
- lhs_t = Match(lhs_t, OptionalType)->type;
- if (has_stack_memory(lhs_t))
- code_err(ast, "Stack references cannot be assigned to "
- "variables because the "
- "variable's scope may outlive the scope "
- "of the stack memory.");
- if (target == assign->targets) expr_t = lhs_t;
- env_t *val_scope = with_enum_scope(env, lhs_t);
- Text_t val_code = compile_to_type(val_scope, value->ast, lhs_t);
- test_code = Texts(test_code, compile_type(lhs_t), " $", i, " = ", val_code, ";\n");
- i += 1;
- }
- i = 1;
- for (ast_list_t *target = assign->targets; target; target = target->next) {
- test_code = Texts(test_code, compile_assignment(env, target->ast, Texts("$", i)), ";\n");
- i += 1;
- }
-
- test_code = Texts(test_code, "$1; })");
- }
- } else if (is_update_assignment(test->expr)) {
- binary_operands_t update = UPDATE_OPERANDS(test->expr);
- type_t *lhs_t = get_type(env, update.lhs);
- if (update.lhs->tag == Index) {
- type_t *indexed = value_type(get_type(env, Match(update.lhs, Index)->indexed));
- if (indexed->tag == TableType && Match(indexed, TableType)->default_value == NULL)
- code_err(update.lhs, "Update assignments are not currently "
- "supported for tables");
- }
-
- ast_t *update_var = new (ast_t);
- *update_var = *test->expr;
- update_var->__data.PlusUpdate.lhs = LiteralCode(Text("(*expr)"), .type = lhs_t); // UNSAFE
- test_code = Texts("({", compile_declaration(Type(PointerType, lhs_t), Text("expr")), " = &(",
- compile_lvalue(env, update.lhs), "); ", compile_statement(env, update_var), "; *expr; })");
- expr_t = lhs_t;
- } else if (expr_t->tag == VoidType || expr_t->tag == AbortType || expr_t->tag == ReturnType) {
- test_code = Texts("({", compile_statement(env, test->expr), " NULL;})");
- } else {
- test_code = compile(env, test->expr);
- }
- if (test->expected) {
- return Texts(setup, "test(", compile_type(expr_t), ", ", test_code, ", ",
- compile_to_type(env, test->expected, expr_t), ", ", compile_type_info(expr_t), ", ",
- (int64_t)(test->expr->start - test->expr->file->text), ", ",
- (int64_t)(test->expr->end - test->expr->file->text), ");");
- } else {
- if (expr_t->tag == VoidType || expr_t->tag == AbortType) {
- return Texts(setup, "inspect_void(", test_code, ", ", compile_type_info(expr_t), ", ",
- (int64_t)(test->expr->start - test->expr->file->text), ", ",
- (int64_t)(test->expr->end - test->expr->file->text), ");");
- }
- return Texts(setup, "inspect(", compile_type(expr_t), ", ", test_code, ", ", compile_type_info(expr_t), ", ",
- (int64_t)(test->expr->start - test->expr->file->text), ", ",
- (int64_t)(test->expr->end - test->expr->file->text), ");");
- }
-}
diff --git a/src/compile/expressions.c b/src/compile/expressions.c
index f4326b00..a4603cd5 100644
--- a/src/compile/expressions.c
+++ b/src/compile/expressions.c
@@ -249,7 +249,7 @@ Text_t compile(env_t *env, ast_t *ast) {
case Stop:
case Pass:
case Return:
- case DocTest:
+ case DebugLog:
case Assert: code_err(ast, "This is not a valid expression");
case Unknown:
default: code_err(ast, "Unknown AST: ", ast_to_sexp_str(ast));
diff --git a/src/compile/functions.c b/src/compile/functions.c
index 5f6f7e91..eea46ebf 100644
--- a/src/compile/functions.c
+++ b/src/compile/functions.c
@@ -556,8 +556,9 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t
add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, NonOptional)->value);
break;
}
- case DocTest: {
- add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, DocTest)->expr);
+ case DebugLog: {
+ for (ast_list_t *value = Match(ast, DebugLog)->values; value; value = value->next)
+ add_closed_vars(closed_vars, enclosing_scope, env, value->ast);
break;
}
case Assert: {
diff --git a/src/compile/statements.c b/src/compile/statements.c
index a7705adc..37eff680 100644
--- a/src/compile/statements.c
+++ b/src/compile/statements.c
@@ -36,7 +36,7 @@ static Text_t compile_simple_update_assignment(env_t *env, ast_t *ast, const cha
static Text_t _compile_statement(env_t *env, ast_t *ast) {
switch (ast->tag) {
case When: return compile_when_statement(env, ast);
- case DocTest: return compile_doctest(env, ast);
+ case DebugLog: return compile_debug_log(env, ast);
case Assert: return compile_assertion(env, ast);
case Declare: {
DeclareMatch(decl, ast, Declare);
diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c
index 69c1a213..42204b01 100644
--- a/src/formatter/formatter.c
+++ b/src/formatter/formatter.c
@@ -119,7 +119,7 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) {
/*inline*/ case Extend:
/*inline*/ case FunctionDef:
/*inline*/ case ConvertDef:
- /*inline*/ case DocTest:
+ /*inline*/ case DebugLog:
return NONE_TEXT;
/*inline*/ case Assert: {
DeclareMatch(assert, ast, Assert);
@@ -775,13 +775,13 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) {
return Texts(termify(call->self, comments, indent), ".", Text$from_str(call->name), "(", args,
Text$has(args, Text("\n")) ? Texts("\n", indent) : EMPTY_TEXT, ")");
}
- /*multiline*/ case DocTest: {
- DeclareMatch(test, ast, DocTest);
- Text_t expr = fmt(test->expr, comments, indent);
- Text_t code = Texts(">> ", expr);
- if (test->expected) {
- Text_t expected = fmt(test->expected, comments, indent);
- code = Texts(code, "\n", indent, "= ", expected);
+ /*multiline*/ case DebugLog: {
+ DeclareMatch(debug, ast, DebugLog);
+ Text_t code = Texts(">> ");
+ for (ast_list_t *value = debug->values; value; value = value->next) {
+ Text_t expr = fmt(value->ast, comments, indent);
+ code = Texts(code, expr);
+ if (value->next) code = Texts(code, ", ");
}
return code;
}
diff --git a/src/formatter/utils.c b/src/formatter/utils.c
index bbe74d7f..9cd0227d 100644
--- a/src/formatter/utils.c
+++ b/src/formatter/utils.c
@@ -44,16 +44,16 @@ CONSTFUNC int suggested_blank_lines(ast_t *first, ast_t *second) {
for (;;) {
if (first->tag == Declare && Match(first, Declare)->value) {
first = Match(first, Declare)->value;
- } else if (first->tag == DocTest && Match(first, DocTest)->expr && Match(first, DocTest)->expected == NULL) {
- first = Match(first, DocTest)->expr;
+ } else if (first->tag == DebugLog) {
+ return 1;
} else break;
}
for (;;) {
if (second->tag == Declare && Match(second, Declare)->value) {
second = Match(second, Declare)->value;
- } else if (second->tag == DocTest && Match(second, DocTest)->expr && Match(second, DocTest)->expected == NULL) {
- second = Match(second, DocTest)->expr;
+ } else if (second->tag == DebugLog) {
+ return 1;
} else break;
}
diff --git a/src/parse/statements.c b/src/parse/statements.c
index 9606acdc..c767ae0b 100644
--- a/src/parse/statements.c
+++ b/src/parse/statements.c
@@ -2,7 +2,6 @@
#include <gc.h>
#include <stdbool.h>
-#include <string.h>
#include "../ast.h"
#include "../stdlib/util.h"
@@ -96,20 +95,24 @@ ast_t *parse_update(parse_ctx_t *ctx, const char *pos) {
.__data.PlusUpdate.rhs = rhs);
}
-ast_t *parse_doctest(parse_ctx_t *ctx, const char *pos) {
+ast_t *parse_debug_log(parse_ctx_t *ctx, const char *pos) {
const char *start = pos;
if (!match(&pos, ">>")) return NULL;
spaces(&pos);
- ast_t *expr = expect(ctx, start, &pos, parse_statement, "I couldn't parse the expression for this doctest");
- whitespace(ctx, &pos);
- ast_t *expected = NULL;
- if (match(&pos, "=")) {
+ ast_t *first_value =
+ expect(ctx, start, &pos, parse_statement, "I couldn't parse the expression for this debugging message");
+ ast_list_t *values = new (ast_list_t, .ast = first_value, .next = NULL);
+ spaces(&pos);
+ while (match(&pos, ",")) {
+ spaces(&pos);
+ ast_t *value = optional(ctx, &pos, parse_statement);
+ if (!value) break;
+ values = new (ast_list_t, .ast = value, .next = values);
spaces(&pos);
- expected = expect(ctx, start, &pos, parse_extended_expr, "I couldn't parse the expected expression here");
- } else {
- pos = expr->end;
}
- return NewAST(ctx->file, start, pos, DocTest, .expr = expr, .expected = expected);
+ REVERSE_LIST(values);
+ whitespace(ctx, &pos);
+ return NewAST(ctx->file, start, pos, DebugLog, .values = values);
}
ast_t *parse_assert(parse_ctx_t *ctx, const char *pos) {
@@ -130,7 +133,7 @@ ast_t *parse_assert(parse_ctx_t *ctx, const char *pos) {
ast_t *parse_statement(parse_ctx_t *ctx, const char *pos) {
ast_t *stmt = NULL;
- if ((stmt = parse_declaration(ctx, pos)) || (stmt = parse_doctest(ctx, pos)) || (stmt = parse_assert(ctx, pos)))
+ if ((stmt = parse_declaration(ctx, pos)) || (stmt = parse_debug_log(ctx, pos)) || (stmt = parse_assert(ctx, pos)))
return stmt;
if (!(false || (stmt = parse_update(ctx, pos)) || (stmt = parse_assignment(ctx, pos))))
diff --git a/src/parse/statements.h b/src/parse/statements.h
index fbc1af8f..ebf90c90 100644
--- a/src/parse/statements.h
+++ b/src/parse/statements.h
@@ -6,7 +6,7 @@
ast_t *parse_assignment(parse_ctx_t *ctx, const char *pos);
ast_t *parse_declaration(parse_ctx_t *ctx, const char *pos);
-ast_t *parse_doctest(parse_ctx_t *ctx, const char *pos);
+ast_t *parse_debug_log(parse_ctx_t *ctx, const char *pos);
ast_t *parse_assert(parse_ctx_t *ctx, const char *pos);
ast_t *parse_statement(parse_ctx_t *ctx, const char *pos);
ast_t *parse_update(parse_ctx_t *ctx, const char *pos);
diff --git a/src/stdlib/stdlib.c b/src/stdlib/stdlib.c
index 5ea8cb79..0756eab3 100644
--- a/src/stdlib/stdlib.c
+++ b/src/stdlib/stdlib.c
@@ -87,6 +87,7 @@ void start_inspect(const char *filename, int64_t start, int64_t end) {
if (file) {
size_t first_line_len = strcspn(file->text + start, "\r\n");
+ if (first_line_len > (size_t)(end - start)) first_line_len = (size_t)(end - start);
const char *slash = strrchr(filename, '/');
const char *file_base = slash ? slash + 1 : filename;
@@ -132,43 +133,6 @@ void end_inspect(const void *expr, const TypeInfo_t *type) {
}
}
-__attribute__((nonnull)) public
-void test_value(const char *filename, int64_t start, int64_t end, const void *expr, const void *expected,
- const TypeInfo_t *type) {
- if (generic_equal(expr, expected, type)) return;
-
- print_stacktrace(stderr, 2);
- fprint(stderr, "");
- fflush(stderr);
-
- start_inspect(filename, start, end);
- end_inspect(expr, type);
- fflush(stdout);
-
- Text_t expr_text = generic_as_text(expr, USE_COLOR, type);
- Text_t expected_text = generic_as_text(expected, USE_COLOR, type);
- if (USE_COLOR) {
- fprint(stderr,
- "\n\x1b[31;7m ==================== TEST FAILED ==================== \x1b[0;1m\n\n"
- "You expected: \x1b[m",
- expected_text,
- "\x1b[0m\n"
- "\x1b[1m But I got:\x1b[m ",
- expr_text, "\n");
- } else {
- fprint(stderr,
- "\n==================== TEST FAILED ====================\n\n"
- "You expected: ",
- expected_text,
- "\n"
- " But I got: ",
- expr_text, "\n");
- }
-
- fflush(stderr);
- raise(SIGABRT);
-}
-
public
void say(Text_t text, bool newline) {
Text$print(stdout, text);
diff --git a/src/stdlib/stdlib.h b/src/stdlib/stdlib.h
index 9bae5a6c..e52b5cd1 100644
--- a/src/stdlib/stdlib.h
+++ b/src/stdlib/stdlib.h
@@ -69,14 +69,6 @@ void end_inspect(const void *expr, const TypeInfo_t *type);
expr; \
end_inspect(NULL, typeinfo); \
}
-__attribute__((nonnull)) void test_value(const char *filename, int64_t start, int64_t end, const void *expr,
- const void *expected, const TypeInfo_t *type);
-#define test(type, expr, expected, typeinfo, start, end) \
- { \
- type _expr = expr; \
- type _expected = expected; \
- test_value(__SOURCE_FILE__, start, end, &_expr, &_expected, typeinfo); \
- }
void say(Text_t text, bool newline);
Text_t ask(Text_t prompt, bool bold, bool force_tty);
diff --git a/src/typecheck.c b/src/typecheck.c
index b4798768..308ed259 100644
--- a/src/typecheck.c
+++ b/src/typecheck.c
@@ -263,8 +263,9 @@ static env_t *load_module(env_t *env, ast_t *use_ast) {
void prebind_statement(env_t *env, ast_t *statement) {
switch (statement->tag) {
- case DocTest: {
- prebind_statement(env, Match(statement, DocTest)->expr);
+ case DebugLog: {
+ for (ast_list_t *value = Match(statement, DebugLog)->values; value; value = value->next)
+ prebind_statement(env, value->ast);
break;
}
case Assert: {
@@ -351,8 +352,9 @@ void prebind_statement(env_t *env, ast_t *statement) {
void bind_statement(env_t *env, ast_t *statement) {
switch (statement->tag) {
- case DocTest: {
- bind_statement(env, Match(statement, DocTest)->expr);
+ case DebugLog: {
+ for (ast_list_t *value = Match(statement, DebugLog)->values; value; value = value->next)
+ prebind_statement(env, value->ast);
break;
}
case Assert: {
@@ -1105,7 +1107,7 @@ type_t *get_type(env_t *env, ast_t *ast) {
case Declare:
case Assign:
case UPDATE_CASES:
- case DocTest:
+ case DebugLog:
case Assert: {
return Type(VoidType);
}