~sircmpwn/hare unlisted

b706c298c936e918e1ec7aa8349f014e40e0efab — Drew DeVault 18 days ago 7a3f017
hare::parse: separate constants and globals

Fixes #390
5 files changed, 112 insertions(+), 22 deletions(-)

M cmd/haredoc/html.ha
M cmd/haredoc/sort.ha
M hare/ast/decl.ha
M hare/parse/decl.ha
M hare/unparse/decl.ha
M cmd/haredoc/html.ha => cmd/haredoc/html.ha +28 -1
@@ 71,6 71,7 @@ fn emit_html(ctx: *context) (void | error) = {

	if (len(decls.types) == 0
			&& len(decls.errors) == 0
			&& len(decls.constants) == 0
			&& len(decls.globals) == 0
			&& len(decls.funcs) == 0) {
		return;


@@ 79,6 80,7 @@ fn emit_html(ctx: *context) (void | error) = {
	fmt::println("<h3>Index</h3>")?;
	tocentries(decls.types, "Types", "types")?;
	tocentries(decls.errors, "Errors", "Errors")?;
	tocentries(decls.constants, "Constants", "constants")?;
	tocentries(decls.globals, "Globals", "globals")?;
	tocentries(decls.funcs, "Functions", "functions")?;



@@ 96,6 98,13 @@ fn emit_html(ctx: *context) (void | error) = {
		};
	};

	if (len(decls.constants) != 0) {
		fmt::println("<h3>Constants</h3>")?;
		for (let i = 0z; i < len(decls.constants); i += 1) {
			details(ctx, decls.constants[i])?;
		};
	};

	if (len(decls.globals) != 0) {
		fmt::println("<h3>Globals</h3>")?;
		for (let i = 0z; i < len(decls.globals); i += 1) {


@@ 137,6 146,7 @@ fn tocentry(decl: ast::decl) (void | error) = {
		match (decl.decl) {
			_: ast::decl_func => "fn",
			_: []ast::decl_type => "type",
			_: []ast::decl_const => "const",
			_: []ast::decl_global => "let",
		});
	fmt::printf("<a href='#");


@@ 151,6 161,11 @@ fn tocentry(decl: ast::decl) (void | error) = {
			fmt::print(": ")?;
			type_html(os::stdout, 0, g._type, true)?;
		},
		c: []ast::decl_const => {
			let c = c[0];
			fmt::print(": ")?;
			type_html(os::stdout, 0, c._type, true)?;
		},
		t: []ast::decl_type => void,
		f: ast::decl_func => prototype_html(os::stdout, 0,
			f.prototype._type as ast::func_type,


@@ 169,6 184,7 @@ fn details(ctx: *context, decl: ast::decl) (void | error) = {
		match (decl.decl) {
			_: ast::decl_func => "fn",
			_: []ast::decl_type => "type",
			_: []ast::decl_const => "def",
			_: []ast::decl_global => "let",
		});
	unparse::ident(os::stdout, decl_ident(decl))?;


@@ 285,9 301,20 @@ fn markup_html(ctx: *context, in: *io::stream) (void | io::error) = {
fn unparse_html(out: *io::stream, d: ast::decl) (size | io::error) = {
	let n = 0z;
	match (d.decl) {
		c: []ast::decl_const => {
			n += fmt::fprintf(out, "<span class='keyword'>def</span> ")?;
			for (let i = 0z; i < len(c); i += 1) {
				n += unparse::ident(out, c[i].ident)?;
				n += fmt::fprint(out, ": ")?;
				n += type_html(out, 0, c[i]._type, false)?;
				if (i + 1 < len(c)) {
					n += fmt::fprint(out, ", ")?;
				};
			};
		},
		g: []ast::decl_global => {
			n += fmt::fprintf(out, "<span class='keyword'>{}</span>",
				if (g[0].is_const) "def " else "let ")?;
				if (g[0].is_const) "const " else "let ")?;
			for (let i = 0z; i < len(g); i += 1) {
				n += unparse::ident(out, g[i].ident)?;
				n += fmt::fprint(out, ": ")?;

M cmd/haredoc/sort.ha => cmd/haredoc/sort.ha +20 -1
@@ 3,7 3,7 @@ use hare::ast;
use sort;

type summary = struct {
	// TODO: Constants
	constants: []ast::decl,
	errors: []ast::decl,
	types: []ast::decl,
	globals: []ast::decl,


@@ 43,6 43,20 @@ fn sort_decls(decls: []ast::decl) summary = {
						docs = decl.docs,
					});
				},
			c: []ast::decl_const =>
				for (let j = 0z; j < len(c); j += 1) {
					append(sorted.constants, ast::decl {
						exported = true,
						loc = decl.loc,
						decl = {
							// XXX: Kind of bad
							let new: []ast::decl_const = [];
							append(new, c[j]);
							new;
						},
						docs = decl.docs,
					});
				},
			g: []ast::decl_global =>
				for (let j = 0z; j < len(g); j += 1) {
					append(sorted.globals, ast::decl {


@@ 60,6 74,7 @@ fn sort_decls(decls: []ast::decl) summary = {
		};
	};

	sort::sort(sorted.constants, size(ast::decl), &decl_cmp);
	sort::sort(sorted.errors, size(ast::decl), &decl_cmp);
	sort::sort(sorted.types, size(ast::decl), &decl_cmp);
	sort::sort(sorted.globals, size(ast::decl), &decl_cmp);


@@ 85,6 100,10 @@ fn decl_ident(decl: ast::decl) ast::ident = match (decl.decl) {
		assert(len(t) == 1);
		t[0].ident;
	},
	c: []ast::decl_const => {
		assert(len(c) == 1);
		c[0].ident;
	},
	g: []ast::decl_global => {
		assert(len(g) == 1);
		g[0].ident;

M hare/ast/decl.ha => hare/ast/decl.ha +10 -1
@@ 1,5 1,14 @@
use hare::lex;

// A constant declaration.
//
// 	def foo: int = 0;
export type decl_const = struct {
	ident: ident,
	_type: _type,
	init: *expr,
};

// A global declaration.
//
// 	let foo: int = 0;


@@ 43,7 52,7 @@ export type decl_func = struct {
export type decl = struct {
	exported: bool,
	loc: lex::location,
	decl: ([]decl_global | []decl_type | decl_func),
	decl: ([]decl_const | []decl_global | []decl_type | decl_func),

	// Only valid if the lexer has comments enabled
	docs: str,

M hare/parse/decl.ha => hare/parse/decl.ha +39 -15
@@ 26,24 26,44 @@ fn attr_symbol(lexer: *lex::lexer) (str | error) = {
	return s;
};

fn decl_const(
	lexer: *lex::lexer,
	tok: ltok,
) ([]ast::decl_const | error) = {
	let decl: []ast::decl_const = [];
	for (true) {
		const ident = ident(lexer)?;
		want(lexer, ltok::COLON)?;
		const _type = _type(lexer)?;
		want(lexer, ltok::EQUAL)?;
		const init: *ast::expr = alloc(expression(lexer)?);
		append(decl, ast::decl_const {
			ident = ident,
			_type = _type,
			init = init,
		});

		if (try(lexer, ltok::COMMA)? is void) {
			break;
		};
	};
	return decl;

};

fn decl_global(
	lexer: *lex::lexer,
	tok: ltok
	tok: ltok,
) ([]ast::decl_global | error) = {
	let decl: []ast::decl_global = [];
	for (true) {
		let symbol = if (tok == ltok::CONST || tok == ltok::LET) {
			match (try(lexer, ltok::ATTR_SYMBOL)?) {
				_: void => "",
				_: lex::token => attr_symbol(lexer)?,
			};
		} else "";
		const symbol = match (try(lexer, ltok::ATTR_SYMBOL)?) {
			_: void => "",
			_: lex::token => attr_symbol(lexer)?,
		};
		const ident = ident(lexer)?;
		want(lexer, ltok::COLON)?;
		let _type = _type(lexer)?;
		if (tok == ltok::CONST) {
			_type.flags |= ast::type_flags::CONST;
		};
		const _type = _type(lexer)?;
		const init: nullable *ast::expr =
			match (try(lexer, ltok::EQUAL)?) {
				_: lex::token => alloc(expression(lexer)?),


@@ 51,7 71,7 @@ fn decl_global(
			};
		const btok = try(lexer, ltok::COMMA)?;
		append(decl, ast::decl_global {
			is_const = tok == ltok::DEF,
			is_const = tok == ltok::CONST,
			symbol = symbol,
			ident = ident,
			_type = _type,


@@ 160,9 180,13 @@ export fn decls(lexer: *lex::lexer) ([]ast::decl | error) = {
		};
		let decl = match (next) {
			_: void => decl_func(lexer)?,
			t: lex::token =>
				if (t.0 == ltok::TYPE) decl_type(lexer)?
				else decl_global(lexer, t.0)?,
			t: lex::token => switch (t.0) {
				ltok::TYPE => decl_type(lexer)?,
				ltok::LET, ltok::CONST =>
					decl_global(lexer, t.0)?,
				ltok::DEF => decl_const(lexer, t.0)?,
				* => abort(),
			},
		};
		append(decls, ast::decl {
			exported = exported,

M hare/unparse/decl.ha => hare/unparse/decl.ha +15 -4
@@ 10,9 10,22 @@ export fn decl(out: *io::stream, d: ast::decl) (size | io::error) = {
		n += fmt::fprint(out, "export ")?;
	};
	match (d.decl) {
		c: []ast::decl_const => {
			n += fmt::fprint(out, "def ")?;
			for (let i = 0z; i < len(c); i += 1) {
				n += ident(out, c[i].ident)?;
				n += fmt::fprint(out, ": ")?;
				n += _type(out, 0, c[i]._type)?;
				n += fmt::fprint(out, " = ")?;
				n += expr(out, 0, *c[i].init)?;
				if (i + 1 < len(c)) {
					n += fmt::fprint(out, ", ")?;
				};
			};
		},
		g: []ast::decl_global => {
			n += fmt::fprint(out,
				if (g[0].is_const) "def " else "let ")?;
				if (g[0].is_const) "const " else "let ")?;
			for (let i = 0z; i < len(g); i += 1) {
				if (len(g[i].symbol) != 0) {
					n += fmt::fprintf(out,


@@ 142,12 155,10 @@ fn decl_test(d: ast::decl, expected: str) bool = {

	d.exported = true;
	d.decl = [
		ast::decl_global {
			is_const = true,
		ast::decl_const {
			ident = ["foo"],
			_type = type_int,
			init = alloc(expr_void),
			...
		},
	];
	assert(decl_test(d, "export def foo: int = void;"));