~smlavine/hareimports

6e9ca13b0e6ce382326bdf162d8c9b4d306b4766 — Sebastian LaVine a month ago af87043
Use stdlib import functionality

Feels meh to throw away all the code I wrote, but this way is better,
more flexible, and probably less buggy.
1 files changed, 18 insertions(+), 135 deletions(-)

M main.ha
M main.ha => main.ha +18 -135
@@ 14,151 14,34 @@
// along with this program. If not, see http://www.gnu.org/licenses/.

use fmt;
use hare::ast;
use hare::lex;
use io;
use hare::parse;
use os;
use strio;
use strings;

// Represents a module that has been imported into the sub-unit.
export type import = struct {
	// location of the first token in import after the use keyword
	location: lex::location,
	name: str,
	alias: str,
	// borrows the alias if there is one, otherwise borrows the name
	path: str,
	// TODO: handle second and third (unqualified) types of imports.
};

// Frees a slice of [[import]]s. See [[strings::freeall]].
export fn imports_freeall(imports: []import) void = {
	for (let i = 0z; i < len(imports); i += 1) {
		// TODO: imports[i].location?
		free(imports[i].name);
		free(imports[i].alias);
		// DON'T free .path; it is a borrow
	};
	free(imports);
};

// Returns a [[lex::syntax]] error from the location and string representation
// of the provided token.
fn badtoken(t: lex::token) lex::syntax = (t.2, lex::tokstr(t));

// Returns a [[import]] constructed from the provided parameters.
fn newimport(loc: lex::location, name: str, alias: str) import =
	import {
		location = loc,
		name = name,
		alias = alias,
		path = if (alias == "") name else alias,
	};

// Parses a single import statement from the provided [[hare::lex::lexer]].
// Precondition: the use keyword MUST have already been lexed.
// An alias will only be lexed if recursive_alias is void.
// Returns an [[import]] on success, or [[hare::lex::error]] on failure.
fn getimport(lexp: *lex::lexer, recursive_alias: str) (import | lex::error) = {
	// The first token has to be a name: either the beginning of
	// an import-alias or the beginning of an identifier.
	// See also §6.4 (Identifiers).
	const first = lex::lex(lexp)?;
	if (first.0 != lex::ltok::NAME)
		return badtoken(first);

	let module_builder = strio::dynamic();
	defer io::close(&module_builder)!;
export fn main() void = {
	const lexer = lex::init(os::stdin, "<stdin>", lex::flags::NONE);

	const second = lex::lex(lexp)?;
	switch (second.0) {
	case lex::ltok::SEMICOLON =>
		// top-level (i.e. not a submodule) import
		return newimport(
			first.2, // location
			strings::dup(lex::tokstr(first)),
			recursive_alias,
		);
	case lex::ltok::EQUAL =>
		// Import alias
		if (recursive_alias == "") {
			return getimport(lexp,
				strings::dup(lex::tokstr(first)));
		};
		return badtoken(second);
	case lex::ltok::DOUBLE_COLON =>
		strio::concat(&module_builder, lex::tokstr(first), "::")!;
	case =>
		return badtoken(second);
	const subunit = match (parse::subunit(&lexer)) {
	case let s: ast::subunit =>
		yield s;
	case let e: parse::error =>
		fmt::fatalf(parse::strerror(e));
	};
	defer ast::subunit_finish(subunit);

	// The previous token was a DOUBLE_COLON.

	const third = lex::lex(lexp)?;
	switch (third.0) {
	case lex::ltok::LBRACE =>
		abort("member-list import (§6.12.4) not supported yet.");
	case lex::ltok::TIMES =>
		abort("unqualified import (§6.12.6) not supported yet.");
	case lex::ltok::NAME =>
		strio::concat(&module_builder, lex::tokstr(third))!;
	case =>
		return badtoken(third);
	};
	for (let i = 0z; i < len(subunit.imports); i += 1) {
		const m = &subunit.imports[i];

	for (true) {
		const possible_dcolon = lex::lex(lexp)?;
		switch (possible_dcolon.0) {
		case lex::ltok::DOUBLE_COLON =>
			strio::concat(&module_builder, "::")!;
		case lex::ltok::SEMICOLON =>
			break;
		case =>
			return badtoken(possible_dcolon);
		};
		const joined_ident = strings::join("::", m.ident...);
		defer free(joined_ident);

		const name = lex::lex(lexp)?;
		switch (name.0) {
		case lex::ltok::NAME =>
			strio::concat(&module_builder, lex::tokstr(name))!;
		switch (m.mode) {
		case ast::import_mode::ALIAS =>
			fmt::printfln("{} (= {})", m.alias, joined_ident)!;
		case =>
			return badtoken(name);
			fmt::println(joined_ident)!;
		};
	};

	return newimport(
		first.2,
		strings::dup(strio::string(&module_builder)),
		recursive_alias,
	);
};

// Returns a slice of [[import]]s or a [[hare::lex::error]]. The slice must be
// freed using [[imports_freeall]].
//
// See §6.12 (Units) of the Hare spec for more information.
export fn getimports(lexp: *lex::lexer) ([]import | lex::error) = {
	let imports: []import = [];
	// errdefer free(imports); // currently leaks memory on lex::error

	for (lex::lex(lexp)?.0 == lex::ltok::USE)
		append(imports, getimport(lexp, "")?);

	return imports;
};

export fn main() void = {
	const lexer = lex::init(os::stdin, "<stdin>", lex::flags::COMMENTS);

	let imports = match (getimports(&lexer)) {
	case let s: []import =>
		yield s;
	case let e: lex::error =>
		fmt::fatalf(lex::strerror(e));
	};
	defer imports_freeall(imports);

	for (let i = 0z; i < len(imports); i += 1)
		fmt::printfln("{} (= {})",
			imports[i].name, imports[i].path)!;
};