/*
 * $Id: visitor.c,v 1.2 2012/02/09 10:43:29 vrsieh Exp $
 *
 * Copyright (C) 2007-2009 FAUcc Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#include <assert.h>
#include <stdio.h>

#include "visitor.h"

static int
visitor_expr_do(
	struct stmt *block,
	struct stmt *stmt,
	struct expr *expr,
	int (*ev)(int, struct stmt *, struct stmt *, struct expr *)
)
{
	int retval;
	struct expr *ce;

	retval = 0;

	retval |= (*ev)(0, block, stmt, expr);
	if (expr->expr0) {
		retval |= visitor_expr_do(block, stmt, expr->expr0, ev);
	}
	if (expr->expr1) {
		retval |= visitor_expr_do(block, stmt, expr->expr1, ev);
	}
	if (expr->expr2) {
		retval |= visitor_expr_do(block, stmt, expr->expr2, ev);
	}
	for (ce = expr->first; ce; ce = ce->next) {
		retval |= visitor_expr_do(block, stmt, ce, ev);
	}
	retval |= (*ev)(1, block, stmt, expr);

	return retval;
}

static int
visitor_stmt_do(
	int repeat,
	struct stmt *block,
	struct stmt *stmt,
	int (*sv)(int, struct stmt *, struct stmt *),
	int (*ev)(int, struct stmt *, struct stmt *, struct expr *)
)
{
	int retval;
	struct stmt *cs;

	retval = 0;

	if (sv) {
		retval |= (*sv)(0, block, stmt);
	}
	if (ev
	 && stmt->expr0) {
		retval |= visitor_expr_do(block, stmt, stmt->expr0, ev);
	}
	if (ev
	 && stmt->expr1) {
		retval |= visitor_expr_do(block, stmt, stmt->expr1, ev);
	}
	if (ev
	 && stmt->expr2) {
		retval |= visitor_expr_do(block, stmt, stmt->expr2, ev);
	}
	if (stmt->stmt0) {
		retval |= visitor_stmt_do(repeat, block, stmt->stmt0, sv, ev);
	}
	if (stmt->stmt1) {
		retval |= visitor_stmt_do(repeat, block, stmt->stmt1, sv, ev);
	}
again:	;
	for (cs = stmt->stmt_first; cs; ) {
		struct stmt *next;

		next = cs->next;
		if (visitor_stmt_do(repeat, stmt, cs, sv, ev)) {
			retval |= 1;
			if (repeat) {
				goto again;
			}
		}
		cs = next;
	}
	if (sv) {
		retval |= (*sv)(1, block, stmt);
	}

	return retval;
}

static int
visitor_scope(
	struct scope *scope,
	int repeat,
	int (*sv)(int, struct stmt *, struct stmt *),
	int (*ev)(int, struct stmt *, struct stmt *, struct expr *)
)
{
	struct declaration *dion;
	int retval;
	struct stmt *cs;

	retval = 0;

	for (dion = scope->declaration_first; dion; dion = dion->next) {
		if (! dion->stmt) {
			continue;
		}

	again:	;
		for (cs = dion->stmt->stmt_first; cs; ) {
			struct stmt *next;

			next = cs->next;
			if (visitor_stmt_do(repeat, dion->stmt, cs, sv, ev)) {
				retval |= 1;
				if (repeat) {
					goto again;
				}
			}
			cs = next;
		}
	}

	return retval;
}

int
visitor_stmt(
	struct scope *scope,
	int repeat,
	int (*sv)(int, struct stmt *, struct stmt *)
)
{
	return visitor_scope(scope, repeat, sv, NULL);
}

int
visitor_expr(
	struct scope *scope,
	int repeat,
	int (*ev)(int, struct stmt *, struct stmt *, struct expr *)
)
{
	return visitor_scope(scope, repeat, NULL, ev);
}
