view WindowsOnly/WinScript/compiler.cpp @ 0:db40c85cad7a default tip

upload sample source
author nobuyasu <dimolto@cr.ie.u-ryukyu.ac.jp>
date Mon, 09 May 2011 03:11:59 +0900
parents
children
line wrap: on
line source

#include <iostream>
#include <iomanip>
#include <memory>
#include <sstream>
#include "compiler.h"
#include "script-parser.hh"

// コンストラクタ

compiler::compiler()
	: break_index(-1), error_count(0)
{
}

// デストラクタ

compiler::~compiler()
{
}

// コンパイル

bool compiler::compile(const std::string &f, vm::data &data)
{
	// システムコールの設定
	add_function(vm::SYS_TOSTR, TYPE_STRING, "str", "i");
	add_function(vm::SYS_MESSAGE_BOX, TYPE_INTEGER, "MessageBox", "ss");
	add_function(vm::SYS_LINE_TO, TYPE_VOID, "LineTo", "ii");
	add_function(vm::SYS_MOVE_TO, TYPE_VOID, "MoveTo", "ii");
	add_function(vm::SYS_SET_COLOR, TYPE_VOID, "SetColor", "iii");
	add_function(vm::SYS_REFRESH, TYPE_VOID, "Refresh", "");

	// グローバル変数用、変数テーブルをセット
	variables.push_back(CValueTable());
	variables[0].set_global();

	// 先頭はHALT命令にしておく
	OpHalt();

	file = f;
	scan_begin();								// スキャナー初期化
	yy::script_parser parser(*this);			// パーサー構築
	int result = parser.parse();				// 構文解析
	scan_end();									// スキャナー終了

	if (result != 0)
		return false;							// パーサーエラー

	int code_size = LabelSetting();				// ラベルにアドレスを設定
	CraeteData(data, code_size);				// バイナリ生成
	return error_count == 0;
}

// エラーメッセージを出力

void compiler::error(const yy::location& l, const std::string& m)
{
//	std::cerr << l << ": " << m << std::endl;

	std::stringstream ss;
	ss << l << ": " << m;

	message.push_back(ss.str());
	error_count++;
}

// エラーメッセージを出力

void compiler::error(const std::string& m)
{
//	std::cerr << m << std::endl;

	message.push_back(m);
	error_count++;
}

// 内部関数の定義

bool compiler::add_function(int index, int type, const char *name, const char *args)
{
	CFunctionTag func(type);
	if (!func.SetArgs(args))		// 引数を設定
		return false;

	func.SetDeclaration();			// 宣言済み
	func.SetSystem();				// Systemフラグセット
	func.SetIndex(index);			// システムコール番号を設定
	if (functions.add(name, func) == 0) {
		return false;
	}
	return true;
}

// 外部変数の定義

struct define_value {
	compiler *comp_;
	int type_;
	define_value(compiler *comp, int type): comp_(comp), type_(type)
	{
	}

	void operator()(CValueNode *node) const
	{
		comp_->AddValue(node->location(), type_, node->string(), node->left());
	}
} ;

void compiler::DefineValue(const yy::location& l, int type, CValueList *value_list)
{
	std::auto_ptr<CValueList> value_list_(value_list);

	value_list->for_each(define_value(this, type));
}

// 関数宣言

void compiler::DefineFunction(const yy::location& l, int type, const std::string *name, CArgList *args)
{
	std::auto_ptr<const std::string> name_(name);
	std::auto_ptr<CArgList> args_(args);

	const CFunctionTag *tag = functions.find(*name);
	if (tag) {			// 既に宣言済み
		if (!tag->ChkArgList(args)) {
			error(l, "関数 " + *name + " に異なる型の引数が指定されています");
			return;
		}
	}
	else {
		CFunctionTag func(type);
		func.SetArgs(args);				// 引数を設定
		func.SetDeclaration();			// 宣言済み
		func.SetIndex(MakeLabel());		// ラベル登録
		if (functions.add(*name, func) == 0) {
			error(l, "内部エラー:関数テーブルに登録できません");
		}
	}
}

// 関数定義
//
//	関数が呼ばれた時点のスタック
//
//	+--------------+
//	|     arg2     | -5
//	+--------------+
//	|     arg1     | -4
//	+--------------+
//	|  arg count   | -3
//	+--------------+
//	| base_pointer | -2
//	+--------------+
//	| return addr  | -1
//	+--------------+
//
//	したがって、引数の開始アドレスは-4となり、デクリメントしていく。

// 引数の変数名を登録
struct add_value {
	compiler *comp_;
	CValueTable &values_;
	mutable int addr_;
	add_value(compiler *comp, CValueTable &values): comp_(comp), values_(values), addr_(-4)
	{
	}

	void operator()(CArgDef *arg) const
	{
		if (!values_.add_arg(arg->type(), arg->name(), addr_)) {
			comp_->error(arg->location(), "引数 " + arg->name() + " は既に登録されています。");
		}
		addr_--;
	}
} ;

void compiler::AddFunction(const yy::location& l, int type, const std::string *name, CArgList *args, CStateBlock *block)
{
	std::auto_ptr<const std::string> name_(name);
	std::auto_ptr<CArgList> args_(args);
	std::auto_ptr<CStateBlock> block_(block);

	CFunctionTag *tag = functions.find(*name);
	if (tag) {
		if (tag->IsDefinition()) {
			error(l, "関数 " + *name + " は既に定義されています");
			return;
		}
		if (tag->IsDeclaration() && !tag->ChkArgList(args)) {
			error(l, "関数 " + *name + " に異なる型の引数が指定されています");
			return;
		}
		tag->SetDefinition();	// 定義済みに設定
	}
	else {
		CFunctionTag func(type);
		func.SetArgs(args);				// 引数を設定
		func.SetDefinition();			// 定義済み
		func.SetIndex(MakeLabel());		// ラベル登録
		tag = functions.add(*name, func);
		if (tag == 0)
			error(l, "内部エラー:関数テーブルに登録できません");
	}

	current_function_name = *name;		// 処理中の関数名を登録しておく
	current_function_type = type;		// 処理中の関数型を登録しておく
										// 関数内関数(入れ子構造)は無いので、
										// グローバル変数1つでよい

	// 関数のエントリーポイントにラベルを置く

	SetLabel(tag->GetIndex());

	BlockIn();		// 変数スタックを増やす

	// 引数があれば、引数リストを登録
	if (args) {
		args->for_each_rev(add_value(this, variables.back()));
	}

	// 文があれば、文を登録
	if (block) {
		block->analyze(this);
	}

	const CVMCode &code = statement.back();
	if (type == TYPE_VOID) {			// 戻り値無し
		if (code.op_ != VM_RETURN)		// returnが無いならば
			OpReturn();					// returnを追加
	}
	else {
		if (code.op_ != VM_RETURNV) {	// returnが無いならば
			error(l, "関数 " + *name + " の最後にreturn文が有りません。");
		}
	}

	BlockOut();		// 変数スタックを減らす

	current_function_name.clear();		// 処理中の関数名を消去
}

// 変数の登録

void compiler::AddValue(const yy::location& l, int type, const std::string &name, const CNode *node)
{
	int size = 1;
	if (node) {
		if (node->op() != OP_CONST) {
			error(l, "配列のサイズは定数で指定してください。");
		}
		else if (node->value() <= 0) {
			error(l, "配列のサイズは1以上の定数が必要です。");
		}
		size = node->value();
	}

	CValueTable &values = variables.back();
	if (!values.add(type, name, size)) {
		error(l, "変数 " + name + " は既に登録されています。");
	}
}

// ラベル生成

int compiler::MakeLabel()
{
	int index = (int)labels.size();
	labels.push_back(CLabel(index));
	return index;
}

// ラベルのダミーコマンドをステートメントリストに登録する

void compiler::SetLabel(int label)
{
	statement.push_back(CVMCode(VM_MAXCOMMAND, label));
}

// 文字列定数をpush

void compiler::PushString(const std::string &str)
{
	PushString((int)text_table.size());
	text_table.insert(text_table.end(), str.begin(), str.end());
	text_table.push_back('\0');
}

// break文に対応したJmpコマンド生成

bool compiler::JmpBreakLabel()
{
	if (break_index < 0)
		return false;
	OpJmp(break_index);
	return true;
}

// ブロック内では、新しい変数セットに変数を登録する

void compiler::BlockIn()
{
	int start_addr = 0;					// 変数アドレスの開始位置
	if (variables.size() > 1) {			// ブロックの入れ子は、開始アドレスを続きからにする。
		start_addr = variables.back().size();
	}
	variables.push_back(CValueTable(start_addr));
}

// ブロックの終了で、変数スコープが消える(変数セットを削除する)

void compiler::BlockOut()
{
	variables.pop_back();
}

// ローカル変数用にスタックを確保

void compiler::AllocStack()
{
	OpAllocStack(variables.back().size());
}

// ラベル解決
//
// 1.アドレスを生成する
// 2.ダミーのラベルコマンドが有ったアドレスを、ラベルテーブルに登録する
// 3.Jmpコマンドの飛び先をラベルテーブルに登録されたアドレスにする

// アドレス計算
struct calc_addr {
	std::vector<CLabel> &labels_;
	int &pos_;
	calc_addr(std::vector<CLabel> &labels, int &pos): labels_(labels), pos_(pos)
	{
	}
	void operator()(const CVMCode &code)
	{
		if (code.op_ == VM_MAXCOMMAND) {			// ラベルのダミーコマンド
			labels_[code.arg1_].pos_ = pos_;
		}
		else {
			pos_ += code.size_;
		}
	}
} ;

// ジャンプアドレス設定
struct set_addr {
	std::vector<CLabel> &labels_;
	set_addr(std::vector<CLabel> &labels): labels_(labels)
	{
	}
	void operator()(CVMCode &code)
	{
		switch (code.op_) {
		  case VM_JMP:
		  case VM_JMPC:
		  case VM_JMPNC:
		  case VM_TEST:
		  case VM_CALL:
			code.arg1_ = labels_[code.arg1_].pos_;
			break;
		}
	}
} ;

int compiler::LabelSetting()
{
	// アドレス計算
	int pos = 0;
	std::for_each(statement.begin(), statement.end(), calc_addr(labels, pos));
	// ジャンプアドレス設定
	std::for_each(statement.begin(), statement.end(), set_addr(labels));

	return pos;
}

// バイナリデータ生成

struct copy_code {
	unsigned char *p;
	copy_code(unsigned char *code): p(code)
	{
	}
	void operator()(const CVMCode &code)
	{
		p = code.Get(p);
	}
} ;

bool compiler::CraeteData(vm::data &data, int code_size)
{
	const CFunctionTag *tag = GetFunctionTag("main");	// 開始位置
	if (tag == 0) {
		error("関数 \"main\" が見つかりません。");
		return false;
	}

	data.command_ = new unsigned char[code_size];
	data.text_buffer_ = new char[text_table.size()];
	data.command_size_ = code_size;
	data.text_size_ = (int)text_table.size();
	data.value_size_ = (int)variables[0].size();
	data.entry_point_ = labels[tag->index_].pos_;

	if (data.text_size_ != 0)
		memcpy(data.text_buffer_, &text_table[0], data.text_size_);

	std::for_each(statement.begin(), statement.end(), copy_code(data.command_));

	return true;
}

// デバッグダンプ
#ifdef	_DEBUG
void compiler::debug_dump()
{
	std::cout << "---variables---" << std::endl;
	size_t vsize = variables.size();
	std::cout << "value stack = " << vsize << std::endl;
	for (size_t i=0; i<vsize; i++) {
		variables[i].dump();
	}
	std::cout << "---code---" << std::endl;

	static const char *op_name[] = {
#define	VM_NAMETABLE
#include "vm_code.h"
#undef	VM_NAMETABLE
		"LABEL",
	} ;

	int	pos = 0;
	size_t size = statement.size();
	for (size_t i=0; i < size; i++) {
		std::cout << std::setw(6) << pos << ": " << op_name[statement[i].op_];
		if (statement[i].size_ > 1) {
			std::cout << ", " << statement[i].arg1_;
		}
		std::cout << std::endl;

		if (statement[i].op_ != VM_MAXCOMMAND) {
			pos += statement[i].size_;
		}
	}
}
#endif