diff WindowsOnly/WinScript2/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 diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WindowsOnly/WinScript2/compiler.cpp	Mon May 09 03:11:59 2011 +0900
@@ -0,0 +1,449 @@
+#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)
+{
+	// グローバル変数用、変数テーブルをセット
+	variables.push_back(CValueTable());
+	variables[0].set_global();
+
+	// 先頭はHALT命令にしておく
+	OpHalt();
+
+	file = f;
+	if (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::stringstream ss;
+	ss << l << ": " << m;
+
+	message.push_back(ss.str());
+	error_count++;
+}
+
+// エラーメッセージを出力
+
+void compiler::error(const std::string& m)
+{
+	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)
+{
+	static char *EntryName[] = {
+		"OnCreate",				// ON_CREATE
+		"OnDestroy",			// ON_DESTROY
+		"OnPaint",				// ON_PAINT
+		"OnLButtonDown",		// ON_LBUTTONDOWN
+		"OnLButtonUp",			// ON_LBUTTONUP
+		"OnMouseMove",			// ON_MOUSEMOVE
+	} ;
+
+	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();
+
+	// 開始位置の設定
+	for (int i=0; i<vm::data::MAX_ENTRY_POINT; i++) {
+		const CFunctionTag *tag = GetFunctionTag(EntryName[i]);
+		if (tag != 0) {
+			data.entry_point_[i] = labels[tag->index_].pos_;
+		}
+		else {
+			data.entry_point_[i] = -1;	// エントリーなしとする。
+		}
+	}
+
+	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