aboutsummaryrefslogtreecommitdiff
path: root/src
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 /src
parentTODO: implicit namespace at the top (diff)
std.Io.print/println take any mix of printable arguments (compiler expands per type); docs, conformance
Diffstat (limited to 'src')
-rw-r--r--src/compiler/expr.zc33
-rw-r--r--src/lib/std.zc13
2 files changed, 40 insertions, 6 deletions
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.