hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 028e1619fa69ef69509622d24bac5231521b393c
parent b706c298c936e918e1ec7aa8349f014e40e0efab
Author: Alexey Yerin <yyp@disroot.org>
Date:   Thu, 22 Apr 2021 20:35:55 +0300

haredoc/html: highlight most of the types

* Highlight builtin types in tagged unions
* Highlight builtin types in tuple types
* Highlight pointer types
* Highlight function types
* Emit builtin types without hare::unparse
* Handle const and error types
* Highlight enums
* Highlight list types (slices, etc)
* Highlight struct types

Diffstat:
Mcmd/haredoc/html.ha | 204+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 200 insertions(+), 4 deletions(-)

diff --git a/cmd/haredoc/html.ha b/cmd/haredoc/html.ha @@ -357,6 +357,120 @@ fn unparse_html(out: *io::stream, d: ast::decl) (size | io::error) = { return n; }; +// Forked from [[hare::unparse]]. +fn builtin_type(b: ast::builtin_type) str = switch (b) { + ast::builtin_type::BOOL => "bool", + ast::builtin_type::CHAR => "char", + ast::builtin_type::F32 => "f32", + ast::builtin_type::F64 => "f64", + ast::builtin_type::FCONST => abort("FCONST has no lexical representation"), + ast::builtin_type::I16 => "i16", + ast::builtin_type::I32 => "i32", + ast::builtin_type::I64 => "i64", + ast::builtin_type::I8 => "i8", + ast::builtin_type::ICONST => abort("ICONST has no lexical representation"), + ast::builtin_type::INT => "int", + ast::builtin_type::NULL => "null", + ast::builtin_type::RUNE => "rune", + ast::builtin_type::SIZE => "size", + ast::builtin_type::STR => "str", + ast::builtin_type::U16 => "u16", + ast::builtin_type::U32 => "u32", + ast::builtin_type::U64 => "u64", + ast::builtin_type::U8 => "u8", + ast::builtin_type::UINT => "uint", + ast::builtin_type::UINTPTR => "uintptr", + ast::builtin_type::VOID => "void", +}; + +// Forked from [[hare::unparse]]. +fn newline(out: *io::stream, indent: size) (size | io::error) = { + let n = 0z; + n += fmt::fprint(out, "\n")?; + for (let i = 0z; i < indent; i += 1) { + n += fmt::fprint(out, "\t")?; + }; + return n; +}; + +fn enum_html( + out: *io::stream, + indent: size, + t: ast::enum_type +) (size | io::error) = { + let z = 0z; + + z += fmt::fprint(out, "<span class='type'>enum</span> ")?; + if (t.storage != ast::builtin_type::INT) { + z += fmt::fprintf(out, "<span class='type'>{}</span> ", + builtin_type(t.storage))?; + }; + z += fmt::fprint(out, "{")?; + for (let i = 0z; i < len(t.values); i += 1) { + const val = t.values[i]; + + z += newline(out, indent + 1)?; + z += fmt::fprint(out, val.name)?; + + match (val.value) { + null => void, + expr: *ast::expr => { + z += fmt::fprint(out, " = ")?; + z += unparse::expr(out, indent, *expr)?; + }, + }; + + z += fmt::fprint(out, ",")?; + }; + z += newline(out, indent)?; + z += fmt::fprint(out, "}")?; + + return z; +}; + +fn struct_html( + out: *io::stream, + indent: size, + t: ast::struct_type, + brief: bool, +) (size | io::error) = { + let z = 0z; + z += fmt::fprint(out, "<span class='keyword'>struct</span> {")?; + + for (let i = 0z; i < len(t); i += 1) { + const member = t[i]; + + z += newline(out, indent + 1)?; + match (member._offset) { + null => void, + expr: *ast::expr => { + z += fmt::fprint(out, "@offset(")?; + z += unparse::expr(out, indent, *expr)?; + z += fmt::fprint(out, ") ")?; + }, + }; + + match (member.member) { + f: ast::struct_field => { + z += fmt::fprintf(out, "{}: ", f.name)?; + z += type_html(out, indent, *f._type, brief)?; + }, + embed: ast::struct_embedded => { + z += type_html(out, indent + 1, *embed, brief)?; + }, + indent: ast::struct_alias => { + z += unparse::ident(out, indent)?; + }, + }; + z += fmt::fprint(out, ",")?; + }; + + z += newline(out, indent)?; + z += fmt::fprint(out, "}")?; + + return z; +}; + fn type_html( out: *io::stream, indent: size, @@ -373,14 +487,96 @@ fn type_html( // TODO: More detailed formatter which can find aliases nested deeper in // other types and highlight more keywords, like const let z = 0z; + + if (_type.flags & ast::type_flags::CONST != 0 + && !(_type._type is ast::func_type)) { + z += fmt::fprint(out, "<span class='keyword'>const</span> ")?; + }; + match (_type._type) { - _: ast::builtin_type => { - z += fmt::fprint(out, "<span class='type'>")?; - z += html::escape(out, strio::string(buf))?; - z += fmt::fprintf(out, "</span>")?; + t: ast::builtin_type => { + z += fmt::fprintf(out, "<span class='type'>{}</span>", + builtin_type(t))?; + }, + t: ast::tagged_type => { + z += fmt::fprint(out, "(")?; + for (let i = 0z; i < len(t); i += 1) { + z += type_html(out, indent, *t[i], brief)?; + if (i < len(t) - 1) { + z += fmt::fprint(out, " | ")?; + }; + }; + z += fmt::fprint(out, ")")?; + }, + t: ast::tuple_type => { + z += fmt::fprint(out, "(")?; + for (let i = 0z; i < len(t); i += 1) { + z += type_html(out, indent, *t[i], brief)?; + if (i < len(t) - 1) { + z += fmt::fprint(out, ", ")?; + }; + }; + z += fmt::fprint(out, ")")?; + }, + t: ast::pointer_type => { + if (t.flags & ast::pointer_flags::NULLABLE != 0) { + z += fmt::fprint(out, "<span class='type'>nullable</span> ")?; + }; + z += fmt::fprint(out, "*")?; + z += type_html(out, indent, *t.referent, brief)?; + }, + t: ast::func_type => { + if (t.attrs & ast::func_attrs::NORETURN == ast::func_attrs::NORETURN) { + z += fmt::fprint(out, "@noreturn ")?; + }; + + z += fmt::fprint(out, "<span class='keyword'>fn</span>(")?; + for (let i = 0z; i < len(t.params); i += 1) { + const param = t.params[i]; + z += fmt::fprintf(out, "{}: ", + if (len(param.name) == 0) "_" else param.name)?; + z += type_html(out, indent, *param._type, brief)?; + + if (i + 1 == len(t.params) + && t.variadism == ast::variadism::HARE) { + // TODO: Highlight that as well + z += fmt::fprint(out, "...")?; + }; + if (i + 1 < len(t.params)) { + z += fmt::fprint(out, ", ")?; + }; + }; + if (t.variadism == ast::variadism::C) { + z += fmt::fprint(out, ", ...")?; + }; + z += fmt::fprint(out, ") ")?; + z += type_html(out, indent, *t.result, brief)?; }, + t: ast::enum_type => z += enum_html(out, indent, t)?, + t: ast::list_type => { + z += fmt::fprint(out, "[")?; + z += match (t.length) { + expr: *ast::expr => unparse::expr(out, indent, *expr)?, + _: ast::len_slice => 0, + _: ast::len_unbounded => fmt::fprintf(out, "*")?, + _: ast::len_contextual => fmt::fprintf(out, "_")?, + }; + z += fmt::fprint(out, "]")?; + + z += type_html(out, indent, *t.members, brief)?; + }, + t: ast::struct_type => z += struct_html(out, indent, t, brief)?, * => z += html::escape(out, strio::string(buf))?, }; + + if (_type.flags & ast::type_flags::ERROR != 0) { + if (_type._type is ast::builtin_type) { + z += fmt::fprint(out, "<span class='type'>!</span>")?; + } else { + z += fmt::fprint(out, "!")?; + }; + }; + z; };