~tomleb/harepls

b99fea7dc3e771c73e059db7872a07dc9879118b — Tom Lebreux 5 months ago 1bf254f
Add scope_enter and scope_exit to visitor
A hare/traversal/+test/scope.ha => hare/traversal/+test/scope.ha +103 -0
@@ 0,0 1,103 @@
use fmt;

type direction = enum {
	ENTER,
	EXIT,
};

type scope_visitor = struct {
	visitor: visitor,
	depth: int,
	depth_change: [](int, direction),
};

const scope_visitor_vt: vtable = vtable {
	scope_enter = &scope_visitor_enter,
	scope_exit = &scope_visitor_exit,
	...
};

fn new_scope_visitor() scope_visitor = {
	return scope_visitor {
		visitor = &scope_visitor_vt,
		depth = 0,
		depth_change = [],
	};
};

fn scope_visitor_enter(v: *visitor) step = {
	let v = v: *scope_visitor;
	v.depth += 1;
	append(v.depth_change, (v.depth, direction::ENTER));
	return step::OK;
};

fn scope_visitor_exit(v: *visitor) step = {
	let v = v: *scope_visitor;
	append(v.depth_change, (v.depth, direction::EXIT));
	v.depth -= 1;
	return step::OK;
};

@test fn scope() void = {
	let src = `
export fn main() void = {
	for (let i = 0z; i < 10; i += 1) {
		if (i == 0) {
			match (foo) {
			case bar => void;
			case baz => void;
			case toto => void;
			};
		};

		switch (e) {
		case e::OK => void;
		};

		toto::do();
		// Shouldn't count towards new scope
		let foo = foo {
			bar = strings::dup("HI"),
		};
	};
};
`;
	let v = new_scope_visitor();
	const expected = [
		(1, direction::ENTER),
		(2, direction::ENTER),
		(3, direction::ENTER),
		(4, direction::ENTER),
		(5, direction::ENTER),
		(6, direction::ENTER),
		(6, direction::EXIT),
		(6, direction::ENTER),
		(6, direction::EXIT),
		(6, direction::ENTER),
		(6, direction::EXIT),
		(5, direction::EXIT),
		(4, direction::EXIT),
		(4, direction::ENTER),
		(4, direction::EXIT),
		(3, direction::EXIT),
		(2, direction::EXIT),
		(1, direction::EXIT),
	];
	run_visit(src, &v);

	if (len(v.depth_change) != len(expected)) {
		fmt::errorln("expected len {}\ngot len {}", len(expected), len(v.depth_change))!;
		abort();
	};

	for (let i = 0z; i < len(expected); i += 1) {
		if (v.depth_change[i].0 != expected[i].0) {
			abort();
		};

		if (v.depth_change[i].1 != expected[i].1) {
			abort();
		};
	};
};

M hare/traversal/declfinder.ha => hare/traversal/declfinder.ha +0 -1
@@ 219,7 219,6 @@ fn unparse_enum_field(out: io::handle, field: ast::enum_field) (size | io::error
};

export fn declfinder_finish(declfinder: *declfinder) void = {
	// TODO:
	return;
};


M hare/traversal/dfs.ha => hare/traversal/dfs.ha +77 -3
@@ 45,9 45,22 @@ export type decl_type = fn (v: *visitor, decl: *ast::decl_type) step;

export type _type = fn (v: *visitor, _type: *ast::_type) step;

export type scope = fn (v: *visitor) step;

export type vtable = struct {
	subunit: nullable *subunit,

	// scope_enter and scope_exit are called whenever a new scope is entered
	// and exited.
	// A new scope is entered for:
	// - any compound expression (eg: { ... })
	// - inside for loops (the whole loop is a scope, not just its body)
	// - inside each case for a match expression, including the case
	// - inside each case for a switch expression
	// - inside each case for an if expression
	scope_enter: nullable *scope,
	scope_exit: nullable *scope,

	decl: nullable *decl,
	decl_const: nullable *decl_const,
	decl_func: nullable *decl_func,


@@ 466,6 479,10 @@ fn visit_cast_expr(v: *visitor, cast: *ast::cast_expr) step = {
};

fn visit_compound_expr(v: *visitor, compound: *ast::compound_expr) step = {
	if (visit_scope_enter(v) == step::DONE) {
		return step::DONE;
	};

	match (v.compound_expr) {
	case let f: *compound_expr =>
		if (f(v, compound) == step::DONE) {


@@ 474,7 491,11 @@ fn visit_compound_expr(v: *visitor, compound: *ast::compound_expr) step = {
	case null => void;
	};

	return visit_exprs(v, compound.exprs);
	if (visit_exprs(v, compound.exprs) == step::DONE) {
		return step::DONE;
	};

	return visit_scope_exit(v);
};

fn visit_exprs(v: *visitor, exprs: []*ast::expr) step = {


@@ 555,6 576,10 @@ fn visit_delete_expr(v: *visitor, expr: *ast::delete_expr) step = {
};

fn visit_for_expr(v: *visitor, expr: *ast::for_expr) step = {
	if (visit_scope_enter(v) == step::DONE) {
		return step::DONE;
	};

	match (v.for_expr) {
	case let f: *for_expr =>
		if (f(v, expr) == step::DONE) {


@@ 583,10 608,18 @@ fn visit_for_expr(v: *visitor, expr: *ast::for_expr) step = {
	case null => void;
	};

	return visit_expr(v, expr.body);
	if (visit_expr(v, expr.body) == step::DONE) {
		return step::DONE;
	};

	return visit_scope_exit(v);
};

fn visit_if_expr(v: *visitor, expr: *ast::if_expr) step = {
	if (visit_scope_enter(v) == step::DONE) {
		return step::DONE;
	};

	match (v.if_expr) {
	case let f: *if_expr =>
		if (f(v, expr) == step::DONE) {


@@ 611,7 644,7 @@ fn visit_if_expr(v: *visitor, expr: *ast::if_expr) step = {
	case null => void;
	};

	return step::OK;
	return visit_scope_exit(v);
};

fn visit_match_expr(v: *visitor, expr: *ast::match_expr) step = {


@@ 629,6 662,11 @@ fn visit_match_expr(v: *visitor, expr: *ast::match_expr) step = {

	for (let i = 0z; i < len(expr.cases); i += 1) {
		const _case = expr.cases[i];

		if (visit_scope_enter(v) == step::DONE) {
			return step::DONE;
		};

		match (_case._type) {
		case let _type: *ast::_type =>
			if (visit_type(v, _type) == step::DONE) {


@@ 636,9 674,14 @@ fn visit_match_expr(v: *visitor, expr: *ast::match_expr) step = {
			};
		case null => void;
		};

		if (visit_exprs(v, _case.exprs) == step::DONE) {
			return step::DONE;
		};

		if (visit_scope_exit(v) == step::DONE) {
			return step::DONE;
		};
	};

	return visit_exprs(v, expr.default);


@@ 695,6 738,11 @@ fn visit_switch_expr(v: *visitor, expr: *ast::switch_expr) step = {

	for (let i = 0z; i < len(expr.cases); i += 1) {
		const _case = expr.cases[i];

		if (visit_scope_enter(v) == step::DONE) {
			return step::DONE;
		};

		for (let j = 0z; j < len(_case.options); j += 1) {
			if (visit_expr(v, _case.options[j]) == step::DONE) {
				return step::DONE;


@@ 705,6 753,10 @@ fn visit_switch_expr(v: *visitor, expr: *ast::switch_expr) step = {
				return step::DONE;
			};
		};

		if (visit_scope_exit(v) == step::DONE) {
			return step::DONE;
		};
	};

	return step::OK;


@@ 745,3 797,25 @@ fn visit_yield_expr(v: *visitor, expr: *ast::yield_expr) step = {

	return step::OK;
};

fn visit_scope_enter(v: *visitor) step = {
	let scope_enter = match (v.scope_enter) {
	case let f: *scope =>
		yield f;
	case null =>
		return step::OK;
	};

	return scope_enter(v);
};

fn visit_scope_exit(v: *visitor) step = {
	let scope_exit = match (v.scope_exit) {
	case let f: *scope =>
		yield f;
	case null =>
		return step::OK;
	};

	return scope_exit(v);
};

M lsp/protocol/parse.ha => lsp/protocol/parse.ha +1 -1
@@ 121,7 121,7 @@ export fn message_finish(m: message) void = {

@test fn multi_try_parse() void = {
	const p = newparser();
	defer finish(&p);
	defer finish(p);

	append(p.buf, strings::toutf8("Content-L")...);
	assert(next(&p) is more);