aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-15 15:00:36 +0330
committerGravatar zinc <zinc@downsta.ir> 2026-10-15 15:00:36 +0330
commita5a5029a80a14be46c244b5bcca63e5e2cd9026f (patch)
tree9ba84a7d2c5c0f8bdbba65eb9df57b6129c132e7
parentTODO: implicit namespace at the top (diff)
std.Io.print/println take any mix of printable arguments (compiler expands per type); docs, conformance
-rw-r--r--docs/LEARN.md2
-rw-r--r--docs/SPEC.md2
-rw-r--r--src/compiler/expr.zc33
-rw-r--r--src/lib/std.zc13
-rw-r--r--tests/conformance/3-expressions/print_varargs.zc14
-rw-r--r--tests/conformance/3-expressions/print_varargs_fresh_object.zc6
6 files changed, 63 insertions, 7 deletions
diff --git a/docs/LEARN.md b/docs/LEARN.md
index 35ee914..8e0d369 100644
--- a/docs/LEARN.md
+++ b/docs/LEARN.md
@@ -533,7 +533,7 @@ output:
### terminal input and output
-`std.Io` groups the terminal: `print(s)` and `println(s)` write a string, `nl()` a line break, and `put_c`, `put_s`, `put_n`, `put_hex`, `put_bs` and `put_f` write a character, a string, a decimal or hexadecimal integer, a packed byte string and a float. `get_c()`, `eof()`, `read(buf, n)` and `getline(buf, n)` read the program input; `getline` drops the line break and returns -1 at the end of the input. zc has no varargs or overloading, so `print` takes a string only and numbers use their own writers. the writers go through `rt.put_c`, so `std.capture` sees them, and the `rt` names (`rt.put_n` and the rest) keep working.
+`std.Io` groups the terminal: `print(s)` and `println(s)` write a string, `nl()` a line break, and `put_c`, `put_s`, `put_n`, `put_hex`, `put_bs` and `put_f` write a character, a string, a decimal or hexadecimal integer, a packed byte string and a float. `get_c()`, `eof()`, `read(buf, n)` and `getline(buf, n)` read the program input; `getline` drops the line break and returns -1 at the end of the input. `print` and `println` take any number of arguments of any printable type, such as `std.Io.println("total: ", n, " avg: ", x);`; the compiler picks the writer for each argument's type (string, byte string, integer or float), and a fresh owned string argument is freed after it is written. `println()` alone writes a line break. other functions have no varargs or overloading. the writers go through `rt.put_c`, so `std.capture` sees them, and the `rt` names (`rt.put_n` and the rest) keep working.
[example source](../examples/learn/35-io.zc):
diff --git a/docs/SPEC.md b/docs/SPEC.md
index b2aa5ea..266a1af 100644
--- a/docs/SPEC.md
+++ b/docs/SPEC.md
@@ -469,6 +469,8 @@ pn((int)(p + 1) - (int)p); // 1: p + 1 moves one word
3.11.6 `heap(e)` copies the value of `e` into a fresh owned heap block sized to it and returns a pointer to the block (a fresh object, 6.1): one word for a scalar or pointer (`heap(10)` is an `int*`), the characters and the terminating 0 for a `char*` word string (`heap("hi")` is a `char*`), and `sizeof(T)` words for a struct or array value (`heap(v)` with `v` a `foo` is a `foo*`). pointer fields of a copied struct are copied as plain words. a struct whose body declares `drop` gives the block its type drop (6.15). `heap` is found only when no variable or function of that name is in scope.
+3.11.7 `std.Io.print(args)` and `std.Io.println(args)` take any number of arguments, including none. each argument is evaluated left to right and written by the writer for its type: a byte string (`bstr`) by `put_bs`, a float by `put_f`, a `char*` or `char[]` by `put_s`, anything else by `put_n`. a fresh owned string argument is written and then freed; another fresh object is error: a new object can't be printed; bind it to a name first. `println` then writes a line break. the call's value is 0. these two are the only calls with a variable number of arguments.
+
### 3.12 `sizeof` and `typeof`
3.12.1 `sizeof(T)` and `sizeof(e)` give the size in words. the expression form is compiled but never run: `int i = 0; int n = sizeof(i++);` leaves `i` 0 and `n` 1. a `u8[100]` is 13 words, a struct of four `u8` fields is 1.
diff --git a/src/compiler/expr.zc b/src/compiler/expr.zc
index a6cc55c..2cb36cf 100644
--- a/src/compiler/expr.zc
+++ b/src/compiler/expr.zc
@@ -409,9 +409,42 @@ int shifthint(int p, int v) {
// function isn't defined yet)
// 6. the result's type and class are set: .u functions return C_FRESH, which
// is how the analyzer knows the caller now owns a new object
+// std.Io.print(a, b, ...) and println: any mix of strings, byte strings,
+// numbers and floats. each argument goes to the writer for its type; a fresh
+// owned string is written and then freed. println ends with a line break.
+int printcall(int line) {
+ int c; char* w;
+ expect(s_lp);
+ if (!is_op(s_rp)) {
+ while (1) {
+ c = expr(); rval();
+ if (xty == tbstr || xstr) w = "std.Io.put_bs";
+ else if (xfloat) w = "std.Io.put_f";
+ else if (xty[0] == TY_PTR && xty[4] == tchar) w = "std.Io.put_s";
+ else if (xty[0] == TY_ARR && xty[4] == tchar) w = "std.Io.put_s";
+ else w = "std.Io.put_n";
+ if (c == C_FRESH && fchk) {
+ if (!rt.eq_w(w, "std.Io.put_s") && !rt.eq_w(w, "std.Io.put_bs")) cerr("a new object can't be printed; bind it to a name first");
+ lit(0); op(O_PICK); callname(intern(w)); op(O_DROP); callname(s_free); op(O_DROP);
+ } else {
+ if (c >= C_VAR && tracked(c - C_VAR)) touch(c - C_VAR);
+ callname(intern(w)); op(O_DROP);
+ }
+ if (!accept(s_comma)) break;
+ }
+ }
+ expect(s_rp);
+ if (line) { lit(10); callname(intern("rt.put_c")); op(O_DROP); }
+ lit(0);
+ lv = 0; lvk = LV_NONE; xty = tint; xzero = 0; xbor = -1; xconst = 0;
+ xfloat = 0; xstr = 0; xlenloc = -1; xstamp = 0; xaf = 0;
+ return C_PLAIN;
+}
int call(int s) {
int np, tags = 0, ret = 0; word ft = stype[s]; word[8] bs; int rs, rhs_type = 0, kop, cp = callpos, original = s; word storage; int origin, temporary, owner; word origins, owner_fact;
word requirements = effect_requirements, edges = effect_edges;
+ if (rt.eq_w(sname[s], "std.Io.print")) return printcall(0);
+ if (rt.eq_w(sname[s], "std.Io.println")) return printcall(1);
borrow_depth++;
if (sname[s] == s_new && skind[s] == K_FN) {
peek();
diff --git a/src/lib/std.zc b/src/lib/std.zc
index b206888..5c86aa6 100644
--- a/src/lib/std.zc
+++ b/src/lib/std.zc
@@ -421,15 +421,16 @@ struct Host {
// called as std.Io.name(args). the writers go through rt.put_c, so a capture
// (std.capture) sees their text too. the rt names (rt.put_n, rt.put_s and the
// rest) stay; these are the same operations, grouped with the library.
-// zc has no varargs or overloading, so print takes a string, and numbers have
-// their own writers: std.Io.print("n = "); std.Io.put_n(n); std.Io.nl();
+// print and println take any mix of strings, byte strings, numbers and floats,
+// each written by its type: std.Io.println("n = ", n, ", x = ", x);
struct Io {
- // writes a string.
- // s: the text
+ // writes its arguments in order: any mix of strings, byte strings, numbers
+ // and floats (the compiler picks the writer for each argument's type).
+ // s: the first value; more may follow
// returns: 0
static int print(char* s) { return rt.put_s(s); }
- // writes a string and a line break.
- // s: the text
+ // writes its arguments like print, then a line break. no arguments: just the line break.
+ // s: the first value; more may follow
// returns: 0
static int println(char* s) { rt.put_s(s); return rt.put_c(10); }
// writes a line break.
diff --git a/tests/conformance/3-expressions/print_varargs.zc b/tests/conformance/3-expressions/print_varargs.zc
new file mode 100644
index 0000000..6918c8c
--- /dev/null
+++ b/tests/conformance/3-expressions/print_varargs.zc
@@ -0,0 +1,14 @@
+// expect: a b 42 -7 1.500000//name: zc/6/
+// spec 3.11.7
+include rt, std;
+char* make_name() { char* s = rt.new(3); s[0] = 'z'; s[1] = 'c'; s[2] = 0; return s; }
+fn main() {
+ float f = 1.5;
+ std.Io.print("a");
+ std.Io.println(" b ", 42, " ", -7, " ", f);
+ std.Io.println();
+ std.Io.println("name: ", make_name());
+ int x = 3;
+ std.Io.println(x * 2);
+}
+// zc0: skip
diff --git a/tests/conformance/3-expressions/print_varargs_fresh_object.zc b/tests/conformance/3-expressions/print_varargs_fresh_object.zc
new file mode 100644
index 0000000..5cba2a4
--- /dev/null
+++ b/tests/conformance/3-expressions/print_varargs_fresh_object.zc
@@ -0,0 +1,6 @@
+// expect error: a new object can't be printed
+// spec 3.11.7
+include rt, std;
+struct node { int v; };
+fn main() { std.Io.println(rt.new(node)); }
+// zc0: skip