Mercurial > hg > Members > nobuyasu > SampleSource
diff 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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WindowsOnly/WinScript/compiler.cpp Mon May 09 03:11:59 2011 +0900 @@ -0,0 +1,447 @@ +#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