commit 947954817949adb49df628cfa53ae6b3c4eb1dbe
parent 3f5cc750d181a1e173eec3046a7702a345413416
Author: Drew DeVault <sir@cmpwn.com>
Date: Mon, 19 Apr 2021 14:34:12 -0400
haredoc: basic HTML formatter
Diffstat:
7 files changed, 552 insertions(+), 115 deletions(-)
diff --git a/Makefile b/Makefile
@@ -35,8 +35,9 @@ harec_srcs=\
haredoc_srcs=\
./cmd/haredoc/main.ha \
./cmd/haredoc/errors.ha \
- ./cmd/haredoc/unparse.ha \
- ./cmd/haredoc/hare.ha
+ ./cmd/haredoc/hare.ha \
+ ./cmd/haredoc/html.ha \
+ ./cmd/haredoc/sort.ha
$(HARECACHE)/hare.ssa: $(hare_srcs) $(hare_stdlib_deps)
@printf 'HAREC\t$@\n'
diff --git a/cmd/haredoc/hare.ha b/cmd/haredoc/hare.ha
@@ -1,26 +1,120 @@
use fmt;
use hare::ast;
+use hare::lex;
+use hare::unparse;
+use io;
use os;
use strings;
+use strio;
// Formats output as Hare source code (prototypes)
-fn emit_hare(decls: []ast::decl) (void | error) = {
+fn emit_hare(summary: summary) (void | error) = {
// TODO: Should we emit the dependencies, too?
- for (let i = 0z; i < len(decls); i += 1) {
- const decl = decls[i];
- if (!decl.exported || decl.docs == "") {
- continue;
- };
+ for (let i = 0z; i < len(summary.types); i += 1) {
+ details_hare(summary.types[i])?;
+ };
+ for (let i = 0z; i < len(summary.globals); i += 1) {
+ details_hare(summary.globals[i])?;
+ };
+ for (let i = 0z; i < len(summary.funcs); i += 1) {
+ details_hare(summary.funcs[i])?;
+ };
+};
- const iter = strings::tokenize(decl.docs, "\n");
- for (true) match (strings::next_token(&iter)) {
- s: str => if (len(s) != 0) {
- fmt::printfln("//{}", s)?;
- },
- void => break,
- };
+fn details_hare(decl: ast::decl) (void | error) = {
+ const iter = strings::tokenize(decl.docs, "\n");
+ for (true) match (strings::next_token(&iter)) {
+ s: str => if (len(s) != 0) {
+ fmt::printfln("//{}", s)?;
+ },
+ void => break,
+ };
- unparse_decl(os::stdout, decl)?;
- fmt::print("\n\n")?;
+ unparse_hare(os::stdout, decl)?;
+ fmt::print("\n\n")?;
+ return;
+};
+
+// Forked from [[hare::unparse]]
+fn unparse_hare(out: *io::stream, d: ast::decl) (size | io::error) = {
+ let n = 0z;
+ match (d.decl) {
+ g: []ast::decl_global => {
+ n += fmt::fprint(out,
+ if (g[0].is_const) "def " else "let ")?;
+ for (let i = 0z; i < len(g); i += 1) {
+ if (len(g[i].symbol) != 0) {
+ n += fmt::fprintf(out,
+ "@symbol(\"{}\") ", g[i].symbol)?;
+ };
+ n += unparse::ident(out, g[i].ident)?;
+ n += fmt::fprint(out, ": ")?;
+ n += unparse::_type(out, 0, g[i]._type)?;
+ if (i + 1 < len(g)) {
+ n += fmt::fprint(out, ", ")?;
+ };
+ };
+ },
+ t: []ast::decl_type => {
+ n += fmt::fprint(out, "type ")?;
+ for (let i = 0z; i < len(t); i += 1) {
+ n += unparse::ident(out, t[i].ident)?;
+ n += fmt::fprint(out, " = ")?;
+ n += unparse::_type(out, 0, t[i]._type)?;
+ if (i + 1 < len(t)) {
+ n += fmt::fprint(out, ", ")?;
+ };
+ };
+ },
+ f: ast::decl_func => {
+ n += fmt::fprint(out, switch (f.attrs) {
+ ast::fndecl_attrs::NONE => "",
+ ast::fndecl_attrs::FINI => "@fini ",
+ ast::fndecl_attrs::INIT => "@init ",
+ ast::fndecl_attrs::TEST => "@test ",
+ })?;
+ let p = f.prototype._type as ast::func_type;
+ if (p.attrs & ast::func_attrs::NORETURN != 0) {
+ n += fmt::fprint(out, "@noreturn ")?;
+ };
+ if (len(f.symbol) != 0) {
+ n += fmt::fprintf(out, "@symbol(\"{}\") ",
+ f.symbol)?;
+ };
+ n += fmt::fprint(out, "fn ")?;
+ n += unparse::ident(out, f.ident)?;
+ n += prototype_hare(out, 0,
+ f.prototype._type as ast::func_type)?;
+ },
+ };
+ n += fmt::fprint(out, ";")?;
+ return n;
+};
+
+fn prototype_hare(
+ out: *io::stream,
+ indent: size,
+ t: ast::func_type,
+) (size | io::error) = {
+ let n = 0z;
+ n += fmt::fprint(out, "(")?;
+ for (let i = 0z; i < len(t.params); i += 1) {
+ let param = t.params[i];
+ n += fmt::fprintf(out, "{}: ",
+ if (len(param.name) == 0) "_" else param.name)?;
+ n += unparse::_type(out, indent, *param._type)?;
+ if (i + 1 == len(t.params)
+ && t.variadism == ast::variadism::HARE) {
+ n += fmt::fprintf(out, "...")?;
+ };
+ if (i + 1 < len(t.params)) {
+ n += fmt::fprint(out, ", ")?;
+ };
+ };
+ if (t.variadism == ast::variadism::C) {
+ n += fmt::fprint(out, ", ...")?;
};
+ n += fmt::fprint(out, ") ")?;
+ n += unparse::_type(out, indent, *t.result)?;
+ return n;
};
diff --git a/cmd/haredoc/html.ha b/cmd/haredoc/html.ha
@@ -0,0 +1,338 @@
+use fmt;
+use format::html;
+use hare::ast;
+use hare::lex;
+use hare::unparse;
+use io;
+use os;
+use strings;
+use strio;
+
+// Formats output as HTML
+fn emit_html(decls: summary, template: bool) (void | error) = {
+ if (template) head()?;
+
+ // TODO: Module name
+ fmt::println("<h2>os</h2>")?;
+ fmt::println("<p class='readme'>TODO: Insert module README here")?;
+ if (len(decls.types) != 0) {
+ fmt::println("<h4>Types</h4>")?;
+ fmt::println("<ol>")?;
+ for (let i = 0z; i < len(decls.types); i += 1) {
+ tocentry(decls.types[i])?;
+ };
+ fmt::println("</ol>")?;
+ };
+
+ if (len(decls.globals) != 0) {
+ fmt::println("<h4>Globals</h4>")?;
+ fmt::println("<ol>")?;
+ for (let i = 0z; i < len(decls.globals); i += 1) {
+ tocentry(decls.globals[i])?;
+ };
+ fmt::println("</ol>")?;
+ };
+
+ if (len(decls.funcs) != 0) {
+ fmt::println("<h4>Functions</h4>")?;
+ fmt::println("<ol>")?;
+ for (let i = 0z; i < len(decls.funcs); i += 1) {
+ tocentry(decls.funcs[i])?;
+ };
+ fmt::println("</ol>")?;
+ };
+ fmt::println("</ol>")?;
+
+ for (let i = 0z; i < len(decls.types); i += 1) {
+ details(decls.types[i])?;
+ };
+ for (let i = 0z; i < len(decls.globals); i += 1) {
+ details(decls.globals[i])?;
+ };
+ for (let i = 0z; i < len(decls.funcs); i += 1) {
+ details(decls.funcs[i])?;
+ };
+};
+
+fn tocentry(decl: ast::decl) (void | error) = {
+ fmt::println("<li>");
+ fmt::printf("{} ",
+ match (decl.decl) {
+ ast::decl_func => "fn",
+ []ast::decl_type => "type",
+ []ast::decl_global => "let",
+ });
+ fmt::printf("<a href='#");
+ unparse::ident(os::stdout, decl_ident(decl))?;
+ fmt::printf("'>");
+ unparse::ident(os::stdout, decl_ident(decl))?;
+ fmt::print("</a>");
+
+ match (decl.decl) {
+ g: []ast::decl_global => {
+ let g = g[0];
+ fmt::fprint(os::stdout, ": ")?;
+ type_html(os::stdout, 0, g._type, true)?;
+ },
+ t: []ast::decl_type => {
+ let t = t[0];
+ fmt::fprint(os::stdout, " = ")?;
+ type_html(os::stdout, 0, t._type, true)?;
+ },
+ f: ast::decl_func => prototype_html(os::stdout, 0,
+ f.prototype._type as ast::func_type,
+ true)?,
+ };
+ fmt::println(";");
+
+ fmt::println("</li>");
+ return;
+};
+
+fn details(decl: ast::decl) (void | error) = {
+ fmt::println("<section class='member'>");
+ fmt::print("<h3 id='")?;
+ unparse::ident(os::stdout, decl_ident(decl))?;
+ fmt::print("'>")?;
+ unparse::ident(os::stdout, decl_ident(decl))?;
+ fmt::println("</h3>")?;
+
+ fmt::println("<pre class='decl'>")?;
+ unparse_html(os::stdout, decl)?;
+ fmt::println("</pre>")?;
+
+ // TODO: Apply Hare formatting
+ fmt::println("<p class='comment'>")?;
+ const iter = strings::tokenize(decl.docs, "\n");
+ for (true) match (strings::next_token(&iter)) {
+ s: str => if (len(s) != 0) {
+ html::escape(os::stdout, s)?;
+ },
+ void => break,
+ };
+
+ fmt::println("</section>")?;
+ return;
+};
+
+// Forked from [[hare::unparse]]
+fn unparse_html(out: *io::stream, d: ast::decl) (size | io::error) = {
+ // TODO: Syntax highlighting
+ let n = 0z;
+ match (d.decl) {
+ g: []ast::decl_global => {
+ n += fmt::fprintf(out, "<span class='keyword'>{}</span>",
+ if (g[0].is_const) "def " else "let ")?;
+ for (let i = 0z; i < len(g); i += 1) {
+ n += unparse::ident(out, g[i].ident)?;
+ n += fmt::fprint(out, ": ")?;
+ n += type_html(out, 0, g[i]._type, false)?;
+ if (i + 1 < len(g)) {
+ n += fmt::fprint(out, ", ")?;
+ };
+ };
+ },
+ t: []ast::decl_type => {
+ n += fmt::fprint(out, "<span class='keyword'>type</span> ")?;
+ for (let i = 0z; i < len(t); i += 1) {
+ n += unparse::ident(out, t[i].ident)?;
+ n += fmt::fprint(out, " = ")?;
+ n += type_html(out, 0, t[i]._type, false)?;
+ if (i + 1 < len(t)) {
+ n += fmt::fprint(out, ", ")?;
+ };
+ };
+ },
+ f: ast::decl_func => {
+ n += fmt::fprint(out, switch (f.attrs) {
+ ast::fndecl_attrs::NONE => "",
+ ast::fndecl_attrs::FINI => "@fini ",
+ ast::fndecl_attrs::INIT => "@init ",
+ ast::fndecl_attrs::TEST => "@test ",
+ })?;
+ let p = f.prototype._type as ast::func_type;
+ if (p.attrs & ast::func_attrs::NORETURN != 0) {
+ n += fmt::fprint(out, "@noreturn ")?;
+ };
+ n += fmt::fprint(out, "<span class='keyword'>fn</span> ")?;
+ n += unparse::ident(out, f.ident)?;
+ n += prototype_html(out, 0,
+ f.prototype._type as ast::func_type,
+ false)?;
+ },
+ };
+ n += fmt::fprint(out, ";")?;
+ return n;
+};
+
+fn type_html(
+ out: *io::stream,
+ indent: size,
+ _type: ast::_type,
+ brief: bool,
+) (size | io::error) = {
+ let buf = strio::dynamic();
+ defer io::close(buf);
+ unparse::_type(buf, indent, _type)?;
+ if (brief) {
+ return html::escape(out, strio::string(buf))?;
+ };
+
+ // TODO: More detailed formatter which can find aliases nested deeper in
+ // other types
+ let z = 0z;
+ match (_type._type) {
+ ast::alias_type => {
+ // TODO: Make this link real
+ z += fmt::fprint(out, "<a href='#'>")?;
+ z += html::escape(out, strio::string(buf))?;
+ z += fmt::fprint(out, "</a>")?;
+ },
+ ast::builtin_type => {
+ z += fmt::fprint(out, "<span class='type'>")?;
+ z += html::escape(out, strio::string(buf))?;
+ z += fmt::fprintf(out, "</span>")?;
+ },
+ * => z += html::escape(out, strio::string(buf))?,
+ };
+ z;
+};
+
+fn prototype_html(
+ out: *io::stream,
+ indent: size,
+ t: ast::func_type,
+ brief: bool,
+) (size | io::error) = {
+ let n = 0z;
+ n += fmt::fprint(out, "(")?;
+ for (let i = 0z; i < len(t.params); i += 1) {
+ let param = t.params[i];
+ if (!brief) {
+ n += fmt::fprintf(out, "{}: ",
+ if (len(param.name) == 0) "_" else param.name)?;
+ };
+ n += type_html(out, indent, *param._type, brief)?;
+ if (i + 1 == len(t.params)
+ && t.variadism == ast::variadism::HARE) {
+ n += fmt::fprintf(out, "...")?;
+ };
+ if (i + 1 < len(t.params)) {
+ n += fmt::fprint(out, ", ")?;
+ };
+ };
+ if (t.variadism == ast::variadism::C) {
+ n += fmt::fprint(out, ", ...")?;
+ };
+ n += fmt::fprint(out, ") ")?;
+ n += type_html(out, indent, *t.result, brief)?;
+ return n;
+};
+
+fn head() (void | error) = {
+ // TODO: Get title from module name
+ // TODO: Move me to +embed?
+ fmt::println("<!doctype html>
+<html lang='en'>
+<meta charset='utf-8' />
+<title>Hare documentation</title>
+<style>
+body {
+ font-family: sans-serif;
+ line-height: 1.3;
+ margin: 0 auto;
+}
+
+nav:not(#TableOfContents) {
+ max-width: 1000px;
+ margin: 1rem auto 0;
+ display: grid;
+ grid-template-rows: auto auto 1fr;
+ grid-template-columns: auto 1fr;
+ grid-template-areas:
+ 'logo header'
+ 'logo nav'
+ 'logo none';
+}
+
+nav:not(#TableOfContents) img {
+ grid-area: logo;
+}
+
+nav:not(#TableOfContents) h1 {
+ grid-area: header;
+ margin: 0;
+ padding: 0;
+}
+
+nav:not(#TableOfContents) ul {
+ grid-area: nav;
+ margin: 0.5rem 0 0 0;
+ padding: 0;
+ list-style: none;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ flex-wrap: wrap;
+}
+
+#TableOfContents {
+ font-size: 1.1rem;
+}
+
+main {
+ padding: 0 128px;
+ max-width: 800px;
+ margin: 0 auto;
+
+}
+
+pre {
+ background-color: #eee;
+ padding: 0.25rem 1rem;
+ margin: 0 -1rem 1rem;
+}
+
+pre .keyword {
+ color: #008;
+}
+
+pre .type {
+ color: #44F;
+}
+
+ol {
+ padding-left: 0;
+ list-style: none;
+}
+
+ol li {
+ padding-left: 0;
+}
+
+@media(max-width: 1000px) {
+ main {
+ padding: 0;
+ }
+}
+</style>
+<nav>
+ <img
+ src='https://harelang.org/mascot.jpg'
+ alt='An inked drawing of the Hare mascot, a fuzzy rabbit'
+ width='128' height='128' />
+ <h1>The Hare programming language</h1>
+ <ul>
+ <li><a href='https://harelang.org/'>Home</a></li>
+ <li><a href='https://harelang.org/documentation'>Documentation</a></li>
+ <li><a href='https://harelang.org/tutorial'>Tutorials</a></li>
+ <li><a href='https://harelang.org/blog'>Blog</a></li>
+ <li><a href='https://harelang.org/community'>Community</a></li>
+ <li><a href='https://sr.ht/~sircmpwn/hare'>Source code</a></li>
+ <li><a href='https://harelang.org/specification'>Specification</a></li>
+ </ul>
+</nav>
+<main>
+")?;
+ return;
+};
diff --git a/cmd/haredoc/main.ha b/cmd/haredoc/main.ha
@@ -18,9 +18,11 @@ type format = enum {
export fn main() void = {
// TODO: Use format::TTY by default if stdout is a tty
let fmt = format::HARE;
+ let template = true;
const cmd = getopt::parse(os::args,
"reads and formats Hare documentation",
('F', "format", "specify output format (hare, tty, html, or gemtext)"),
+ ('t', "disable HTML template (requries postprocessing)"),
"[identifiers...]",
);
defer getopt::finish(&cmd);
@@ -34,6 +36,7 @@ export fn main() void = {
else if (opt.1 == "html") format::HTML
else if (opt.1 == "gemtext") format::GEMTEXT
else fmt::fatal("Invalid format {}", opt.1),
+ 't' => template = false,
* => abort(),
};
};
@@ -54,7 +57,8 @@ export fn main() void = {
};
for (let i = 0z; i < len(unit); i += 1) {
- match (emit(unit[i].decls, fmt)) {
+ let summary = sort_decls(unit[i].decls);
+ match (emit(summary, fmt, template)) {
void => void,
err: error => fmt::fatal("Error: {}", strerror(err)),
};
@@ -72,9 +76,13 @@ fn scan(path: str) (ast::subunit | error) = {
return parse::subunit(&lexer)?;
};
-fn emit(decls: []ast::decl, fmt: format) (void | error) = switch (fmt) {
- format::HARE => emit_hare(decls),
+fn emit(
+ summary: summary,
+ fmt: format,
+ template: bool,
+) (void | error) = switch (fmt) {
+ format::HARE => emit_hare(summary),
format::TTY => abort(), // TODO
- format::HTML => abort(), // TODO
+ format::HTML => emit_html(summary, template)?,
format::GEMTEXT => abort(), // TODO
};
diff --git a/cmd/haredoc/sort.ha b/cmd/haredoc/sort.ha
@@ -0,0 +1,86 @@
+use ascii;
+use hare::ast;
+use sort;
+
+type summary = struct {
+ // TODO: Constants
+ types: []ast::decl,
+ globals: []ast::decl,
+ funcs: []ast::decl,
+};
+
+// Sorts declarations by removing unexported declarations, moving undocumented
+// declarations to the end, sorting by identifier, and ensuring that only one
+// member is present in each declaration (so that "let x: int = 10, y: int = 20"
+// becomes two declarations: "let x: int = 10; let y: int = 20;").
+fn sort_decls(decls: []ast::decl) summary = {
+ let sorted = summary { ... };
+
+ for (let i = 0z; i < len(decls); i += 1) {
+ let decl = decls[i];
+ if (!decl.exported) {
+ continue;
+ };
+
+ match (decl.decl) {
+ f: ast::decl_func => append(sorted.funcs, decl),
+ t: []ast::decl_type =>
+ for (let j = 0z; j < len(t); j += 1) {
+ append(sorted.types, ast::decl {
+ exported = true,
+ loc = decl.loc,
+ decl = {
+ // XXX: Kind of bad
+ let new: []ast::decl_type = [];
+ append(new, t[j]);
+ new;
+ },
+ docs = decl.docs,
+ });
+ },
+ g: []ast::decl_global =>
+ for (let j = 0z; j < len(g); j += 1) {
+ append(sorted.globals, ast::decl {
+ exported = true,
+ loc = decl.loc,
+ decl = {
+ // XXX: Kind of bad
+ let new: []ast::decl_global = [];
+ append(new, g[j]);
+ new;
+ },
+ docs = decl.docs,
+ });
+ },
+ };
+ };
+
+ sort::sort(sorted.types, size(ast::decl), &decl_cmp);
+ sort::sort(sorted.globals, size(ast::decl), &decl_cmp);
+ sort::sort(sorted.funcs, size(ast::decl), &decl_cmp);
+ return sorted;
+};
+
+fn decl_cmp(a: const *void, b: const *void) int = {
+ const a = *(a: const *ast::decl);
+ const b = *(b: const *ast::decl);
+ if (a.docs == "" && b.docs != "") {
+ return 1;
+ } else if (a.docs != "" && b.docs == "") {
+ return -1;
+ };
+ const id_a = decl_ident(a), id_b = decl_ident(b);
+ return ascii::strcmp(id_a[len(id_a) - 1], id_b[len(id_b) - 1]) as int;
+};
+
+fn decl_ident(decl: ast::decl) ast::ident = match (decl.decl) {
+ f: ast::decl_func => f.ident,
+ t: []ast::decl_type => {
+ assert(len(t) == 1);
+ t[0].ident;
+ },
+ g: []ast::decl_global => {
+ assert(len(g) == 1);
+ g[0].ident;
+ },
+};
diff --git a/cmd/haredoc/unparse.ha b/cmd/haredoc/unparse.ha
@@ -1,90 +0,0 @@
-use io;
-use fmt;
-use hare::ast;
-use hare::lex;
-use hare::unparse;
-use strio;
-
-// Forked from [hare::unparse] to reformat the output in a more suitable form
-// for documentation.
-fn unparse_decl(out: *io::stream, d: ast::decl) (size | io::error) = {
- let n = 0z;
- match (d.decl) {
- g: []ast::decl_global => {
- n += fmt::fprint(out,
- if (g[0].is_const) "def " else "let ")?;
- for (let i = 0z; i < len(g); i += 1) {
- if (len(g[i].symbol) != 0) {
- n += fmt::fprintf(out,
- "@symbol(\"{}\") ", g[i].symbol)?;
- };
- n += unparse::ident(out, g[i].ident)?;
- n += fmt::fprint(out, ": ")?;
- n += unparse::_type(out, 0, g[i]._type)?;
- if (i + 1 < len(g)) {
- n += fmt::fprint(out, ", ")?;
- };
- };
- },
- t: []ast::decl_type => {
- n += fmt::fprint(out, "type ")?;
- for (let i = 0z; i < len(t); i += 1) {
- n += unparse::ident(out, t[i].ident)?;
- n += fmt::fprint(out, " = ")?;
- n += unparse::_type(out, 0, t[i]._type)?;
- if (i + 1 < len(t)) {
- n += fmt::fprint(out, ", ")?;
- };
- };
- },
- f: ast::decl_func => {
- n += fmt::fprint(out, switch (f.attrs) {
- ast::fndecl_attrs::NONE => "",
- ast::fndecl_attrs::FINI => "@fini ",
- ast::fndecl_attrs::INIT => "@init ",
- ast::fndecl_attrs::TEST => "@test ",
- })?;
- let p = f.prototype._type as ast::func_type;
- if (p.attrs & ast::func_attrs::NORETURN != 0) {
- n += fmt::fprint(out, "@noreturn ")?;
- };
- if (len(f.symbol) != 0) {
- n += fmt::fprintf(out, "@symbol(\"{}\") ",
- f.symbol)?;
- };
- n += fmt::fprint(out, "fn ")?;
- n += unparse::ident(out, f.ident)?;
- n += prototype(out, 0, f.prototype._type as ast::func_type)?;
- },
- };
- n += fmt::fprint(out, ";")?;
- return n;
-};
-
-fn prototype(
- out: *io::stream,
- indent: size,
- t: ast::func_type,
-) (size | io::error) = {
- let n = 0z;
- n += fmt::fprint(out, "(")?;
- for (let i = 0z; i < len(t.params); i += 1) {
- let param = t.params[i];
- n += fmt::fprintf(out, "{}: ",
- if (len(param.name) == 0) "_" else param.name)?;
- n += unparse::_type(out, indent, *param._type)?;
- if (i + 1 == len(t.params)
- && t.variadism == ast::variadism::HARE) {
- n += fmt::fprintf(out, "...")?;
- };
- if (i + 1 < len(t.params)) {
- n += fmt::fprint(out, ", ")?;
- };
- };
- if (t.variadism == ast::variadism::C) {
- n += fmt::fprint(out, ", ...")?;
- };
- n += fmt::fprint(out, ") ")?;
- n += unparse::_type(out, indent, *t.result)?;
- return n;
-};
diff --git a/format/html/escape.ha b/format/html/escape.ha
@@ -5,7 +5,7 @@ use strio;
// Prints a string to an output stream, escaping any of HTML's reserved
// characters.
-export fn escape(in: str, out: *io::stream) (size | io::error) = {
+export fn escape(out: *io::stream, in: str) (size | io::error) = {
let z = 0z;
let iter = strings::iter(in);
for (true) match (strings::next(&iter)) {
@@ -25,16 +25,16 @@ export fn escape(in: str, out: *io::stream) (size | io::error) = {
@test fn escape() void = {
let sink = strio::dynamic();
defer io::close(sink);
- escape("hello world!", sink);
+ escape(sink, "hello world!");
assert(strio::string(sink) == "hello world!");
let sink = strio::dynamic();
defer io::close(sink);
- escape("\"hello world!\"", sink);
+ escape(sink, "\"hello world!\"");
assert(strio::string(sink) == ""hello world!"");
let sink = strio::dynamic();
defer io::close(sink);
- escape("<hello & 'world'!>", sink);
+ escape(sink, "<hello & 'world'!>");
assert(strio::string(sink) == "<hello & 'world'!>");
};