// $Id: Stmt.h,v 1.25 2003/08/31 00:38:11 vern Exp $
//
// Copyright (c) 1995, 1996, 1997, 1998, 1999, 2001, 2002
//      The Regents of the University of California.  All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that: (1) source code distributions
// retain the above copyright notice and this paragraph in its entirety, (2)
// distributions including binary code include the above copyright notice and
// this paragraph in its entirety in the documentation or other materials
// provided with the distribution, and (3) all advertising materials mentioning
// features or use of this software display the following acknowledgement:
// ``This product includes software developed by the University of California,
// Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
// the University nor the names of its contributors may be used to endorse
// or promote products derived from this software without specific prior
// written permission.
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

#ifndef stmt_h
#define stmt_h

// BRO statements.

#include "BroList.h"
#include "Obj.h"
#include "Expr.h"

#include "StmtEnums.h"

class StmtList;
class ForStmt;

class Stmt : public BroObj {
public:
	const BroStmtTag Tag() const	{ return tag; }

	virtual ~Stmt();

	virtual Val* Exec(Frame* f, stmt_flow_type& flow) const = 0;

	Stmt* Ref()			{ ::Ref(this); return this; }

	bool SetLocationInfo(const Location* loc)
		{ return Stmt::SetLocationInfo(loc, loc); }
	bool SetLocationInfo(const Location* start, const Location* end);

	// Returns a fully simplified version of the statement (this
	// may be the same statement, or a newly created one).
	virtual Stmt* Simplify();

	// True if the statement has no side effects, false otherwise.
	virtual int IsPure() const;

	StmtList* AsStmtList()
		{
		if ( tag != STMT_LIST )
			BadTag("Stmt::AsStmtList");
		return (StmtList*) this;
		}

	ForStmt* AsForStmt()
		{
		if ( tag != STMT_FOR )
			BadTag("Stmt::AsForStmt");
		return (ForStmt*) this;
		}

	virtual void Describe(ODesc* d) const;

	virtual void IncrBPCount()	{ ++breakpoint_count; }
	virtual void DecrBPCount()
		{
		if ( breakpoint_count )
			--breakpoint_count;
		else
			internal_error("breakpoint count decremented below 0");
		}

	virtual unsigned int BPCount() const	{ return breakpoint_count; }

protected:
	Stmt(BroStmtTag arg_tag);

	void AddTag(ODesc* d) const;
	void DescribeDone(ODesc* d) const;

	BroStmtTag tag;
	int breakpoint_count;	// how many breakpoints on this statement
};

class ExprListStmt : public Stmt {
public:
	const ListExpr* ExprList() const	{ return l; }

protected:
	ExprListStmt(BroStmtTag t, ListExpr* arg_l);

	virtual ~ExprListStmt();

	Val* Exec(Frame* f, stmt_flow_type& flow) const;
	virtual Val* DoExec(val_list* vals, stmt_flow_type& flow) const = 0;

	Stmt* Simplify();
	virtual Stmt* DoSimplify();

	void Describe(ODesc* d) const;
	void PrintVals(ODesc* d, val_list* vals, int offset) const;

	ListExpr* l;
};

class LogStmt : public ExprListStmt {
public:
	LogStmt(ListExpr* l) : ExprListStmt(STMT_LOG, l)	{ }

protected:
	Val* DoExec(val_list* vals, stmt_flow_type& flow) const;
};

class PrintStmt : public ExprListStmt {
public:
	PrintStmt(ListExpr* l) : ExprListStmt(STMT_PRINT, l)	{ }

protected:
	Val* DoExec(val_list* vals, stmt_flow_type& flow) const;
};

class ExprStmt : public Stmt {
public:
	ExprStmt(Expr* e);
	virtual ~ExprStmt();

	Val* Exec(Frame* f, stmt_flow_type& flow) const;

	const Expr* StmtExpr() const	{ return e; }

	void Describe(ODesc* d) const;

protected:
	ExprStmt(BroStmtTag t, Expr* e);

	virtual Val* DoExec(Frame* f, Val* v, stmt_flow_type& flow) const;

	Stmt* Simplify();
	int IsPure() const;

	// Called by Simplify(), after the expression's been simplified.
	virtual Stmt* DoSimplify();

	Expr* e;
};

class IfStmt : public ExprStmt {
public:
	IfStmt(Expr* test, Stmt* s1, Stmt* s2);
	~IfStmt();

	const Stmt* TrueBranch() const	{ return s1; }
	const Stmt* FalseBranch() const	{ return s2; }

	void Describe(ODesc* d) const;

protected:
	Val* DoExec(Frame* f, Val* v, stmt_flow_type& flow) const;
	Stmt* DoSimplify();
	int IsPure() const;

	Stmt* s1;
	Stmt* s2;
};

class Case : public BroObj {
public:
	Case(ListExpr* c, Stmt* arg_s)	{ cases = c; s = arg_s; }
	~Case();

	const ListExpr* Cases() const	{ return cases; }
	ListExpr* Cases()		{ return cases; }

	const Stmt* Body() const	{ return s; }
	Stmt* Body()			{ return s; }

	void Describe(ODesc* d) const;

protected:
	ListExpr* cases;
	Stmt* s;
};

class SwitchStmt : public ExprStmt {
public:
	SwitchStmt(Expr* index, case_list* cases);
	~SwitchStmt();

	const case_list* Cases() const	{ return cases; }

	void Describe(ODesc* d) const;

protected:
	Val* DoExec(Frame* f, Val* v, stmt_flow_type& flow) const;
	Stmt* DoSimplify();
	int IsPure() const;

	case_list* cases;
};

class AddStmt : public ExprStmt {
public:
	AddStmt(Expr* e);

	int IsPure() const;
	Val* Exec(Frame* f, stmt_flow_type& flow) const;
};

class DelStmt : public ExprStmt {
public:
	DelStmt(Expr* e);

	int IsPure() const;
	Val* Exec(Frame* f, stmt_flow_type& flow) const;
};

class EventStmt : public ExprStmt {
public:
	EventStmt(EventExpr* e);

	Val* Exec(Frame* f, stmt_flow_type& flow) const;

protected:
	EventExpr* event_expr;
};

class ForStmt : public ExprStmt {
public:
	ForStmt(ID* loop_var, Expr* loop_expr);
	~ForStmt();

	void AddBody(Stmt* arg_body)	{ body = arg_body; }

	const ID* LoopVar() const	{ return loop_var; }
	const Expr* LoopExpr() const	{ return e; }
	const Stmt* LoopBody() const	{ return body; }

	int IsPure() const;

	void Describe(ODesc* d) const;

protected:
	Val* DoExec(Frame* f, Val* v, stmt_flow_type& flow) const;
	Stmt* DoSimplify();

	ID* loop_var;
	Stmt* body;
};

class NextStmt : public Stmt {
public:
	NextStmt() : Stmt(STMT_NEXT)	{ }

	Val* Exec(Frame* f, stmt_flow_type& flow) const;
	int IsPure() const;

	void Describe(ODesc* d) const;
};

class BreakStmt : public Stmt {
public:
	BreakStmt() : Stmt(STMT_BREAK)	{ }

	Val* Exec(Frame* f, stmt_flow_type& flow) const;
	int IsPure() const;

	void Describe(ODesc* d) const;
};

class ReturnStmt : public ExprStmt {
public:
	ReturnStmt(Expr* e);

	Val* Exec(Frame* f, stmt_flow_type& flow) const;

	void Describe(ODesc* d) const;
};

class StmtList : public Stmt {
public:
	StmtList();
	~StmtList();

	Val* Exec(Frame* f, stmt_flow_type& flow) const;

	const stmt_list& Stmts() const	{ return stmts; }
	stmt_list& Stmts()		{ return stmts; }

	void Describe(ODesc* d) const;

protected:
	Stmt* Simplify();
	int IsPure() const;

	stmt_list stmts;
};

class EventBodyList : public StmtList {
public:
	EventBodyList() : StmtList()	{ topmost = false; }

	Val* Exec(Frame* f, stmt_flow_type& flow) const;

	void Describe(ODesc* d) const;

	// "Topmost" means that this is the main body of a function or event.
	// void SetTopmost(bool is_topmost)	{ topmost = is_topmost; }
	// bool IsTopmost()	{ return topmost; }

protected:
	Stmt* Simplify();
	bool topmost;
};

class InitStmt : public Stmt {
public:
	InitStmt(id_list* arg_inits) : Stmt(STMT_INIT)
		{
		inits = arg_inits;
		if ( arg_inits && arg_inits->length() )
			SetLocationInfo((*arg_inits)[0]->GetLocationInfo());
		}

	~InitStmt();

	Val* Exec(Frame* f, stmt_flow_type& flow) const;

	const id_list* Inits() const	{ return inits; }

	void Describe(ODesc* d) const;

protected:
	id_list* inits;
};

class NullStmt : public Stmt {
public:
	NullStmt() : Stmt(STMT_NULL)	{ }

	Val* Exec(Frame* f, stmt_flow_type& flow) const;
	int IsPure() const;

	void Describe(ODesc* d) const;
};


extern Stmt* simplify_stmt(Stmt* s);
extern int same_stmt(const Stmt* s1, const Stmt* s2);

#endif
