プログラミングなど

2009年5月22日金曜日

C# + Python embedding C#のクラスをPython側で使用する

C#のクラスをIronPython側で使うのは、非常に簡単だ。
(CPythonだと、結構、面倒だったような...)

---- コード(C#側) ----

using System;

using IronPython;
using IronPython.Hosting;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;

namespace python_test001
{
 class Program
 {
  public static void Main(string[] args)
  {
   try
   {
    ScriptEngine engine = Python.CreateEngine();
    ScriptScope scope = engine.CreateScope();

    // C#と変数のやり取りをするPythonのファイルを設定
    ScriptSource source =
     engine.CreateScriptSourceFromFile("test.py");

    TestNumber test = new TestNumber();
    // Python側の変数をTestDataとする
    scope.SetVariable("num", test);

    // 実行
    source.Execute(scope);

    int i = test.Number;

    // 実行後のPython側の変数値を取得

    // 結果を出力 ==> 15
    System.Console.WriteLine(test.Number);
   }
   catch (Exception ex)
   {
    System.Console.WriteLine("Error:"
     + ex.Message.ToString());
   }
  }
 }
}

// Python側に渡すクラス
class TestNumber {
 int number = 0;
 public void Plus(int i) {
  number += i;
 }
 public int GetNumber() {
  return this.number;
 }
 public void SetNumber(int i) {
  this.number = i;
 }
 public int Number {
  get {
   return this.number;
  }
  set {
   this.number = value;
  }
  
 }
}

---- コード(Python側) ----

num.Number = 10
print 'py_number:', num.GetNumber()
#プロパティのgetがprotectedメンバだとかでエラー
#print 'py_number:', num.Number
num.Plus(5)

================================

■参考
貧脚レーサーのサボり日記 - C#でIronPython2.0をホスティングする
Embedding the Dynamic Language Runtime
The IronPython Calculator and the Evaluator

2009年5月20日水曜日

boost spirit ファイル解析エラー時情報

boost spirit でファイルを解析したときのエラー時情報出力の仕方について簡単な例があまりないようなので、調べたことを記述します。

下記のコードでは、1行にINT, REAL というキーワードと値の組み合わせを含んだファイルを読み込み、文法にエラーが存在した場合には、該当個所を出力します。

(正常な文法のファイルの例)
INT 100
REAL 10.0
INT 5
REAL 7.5

---- コード ----

#include <boost/spirit.hpp>
#include <iostream>
#include <iomanip>
#include <string>

// エラー出力のためのパーサ
struct error_parser {
 typedef boost::spirit::nil_t result_t;

 std::string msg_;
 error_parser(const char* msg) : msg_(msg) {}

 template <typename ScannerT>
 int operator()(const ScannerT& scan,
         result_t& result) const {
  if (!scan.at_end()) { // 入力が最後でない場合

   // スキャナの first には、ポジションイテレータが
   // 保持される
   boost::spirit::file_position fpos
    = scan.first.get_position();
   std::cout
    << "Error: " << fpos.file << " "
    << "Line("  << fpos.line  << ") "
    << "Column(" << fpos.column << ") "
    << msg_
    << std::endl;
  }
  return (-1);
 }
};
typedef boost::spirit::functor_parser<error_parser> error_p;
error_p syntax_error_p("Syntax is wrong.");
error_p keyword_error_p("Invalid Keyword.");
error_p value_error_p("Invalid Value.");

// 文法定義
struct test_grammar : boost::spirit::grammar<test_grammar> {
 template <typename ScannerT>
 struct definition {
  typedef boost::spirit::rule<ScannerT> rule_t;

  rule_t top_, line_,
      integer_statement_, real_statement_, other_statement_;

  definition(const test_grammar& self) {
   using namespace boost::spirit;

   top_ =
    +line_ | syntax_error_p;

   line_ =
    *blank_p >> (integer_statement_ |
           real_statement_  |
           other_statement_) >>
    ((*blank_p >> eol_p)               |
     ((anychar_p - blank_p - eol_p) >> value_error_p) |
     (+blank_p >> ((anychar_p - eol_p) >> syntax_error_p)));

   // "INT (整数)"
   integer_statement_ =
    str_p("INT") >>
    +blank_p >>
    (int_p | (anychar_p >> value_error_p));

   // "REAL (実数)"
   real_statement_ =
    (str_p("REAL") | keyword_error_p) >>
    +blank_p >>
    (real_p | (anychar_p >> value_error_p));

   // "それ以外"
   other_statement_ =
    anychar_p >> syntax_error_p;
  }
  const rule_t& start() const { return top_; }
 };
};

int main(int argc, char** argv) {
 std::string file = "sample.txt";

 // ファイルイテレータ
 typedef boost::spirit::file_iterator<> file_iterator_t;
 file_iterator_t file_first(file);
 if (!file_first) {
  std::cout << "cannot open file: " << file << std::endl;
  return (1);
 }

 // ポジションイテレータ
 typedef boost::spirit::position_iterator<file_iterator_t> position_iterator_t;
 position_iterator_t position_first(file_first,
                   file_first.make_end(),
                   file);
 position_iterator_t position_last;

 boost::spirit::parse_info<position_iterator_t> info =
  boost::spirit::parse(position_first,
             position_last,
             test_grammar());

 std::cout << std::boolalpha << info.full << std::endl;
 return (0);
}

================================

parse()関数の前に、ファイルイテレータ、位置情報イテレータを作成します。
ポジションイテレータは、 error_parser 内の operator()()関数内のscanで利用できます。

error_parserは、functor_parser<>によって、他の定義済パーサと同様にエラーパーサとして利用できるようにします。
definition()内にルールを記述する際、文法エラーの発生させたい個所に、エラーパーサを使用します。

尚、上記のコードでは、エラーの種類に応じて、出力メッセージを変更できるようにしています。

■参考
Spirit v1.6.0 - ファンクタパーサ
Spirit v1.6.0 - 詳細:スキャナ
boost ライブラリを使ってみる - エラー処理

2009年5月17日日曜日

C# + Python embedding ファイルの変数のやりとり

Pythonファイルとのやりとりの例

■.NET Framework2.0 sp1
■IronPython 2.0

---- コード(Python側) ----

result = param1 + param2


---- コード(C#側) ----

using System;

using IronPython;
using IronPython.Hosting;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;

...

  public static void Main(string[] args)
  {
   try
   {
    ScriptEngine engine = Python.CreateEngine();
    ScriptScope scope = engine.CreateScope();

    // C#と変数のやり取りをするPythonのファイルを設定
    ScriptSource source =
     engine.CreateScriptSourceFromFile("test.py");

    // Python側の変数に値を設定
    scope.SetVariable("param1", 1);
    scope.SetVariable("param2", 10);
    
    // 実行
    source.Execute(scope);
    
    // 実行後のPython側の変数値を取得
    
    // 結果を出力 ==> 1
    object arg1 = scope.GetVariable("param1");
    System.Console.WriteLine(arg1);
    // 結果を出力 ==> 10
    object arg2 = scope.GetVariable("param2");
    System.Console.WriteLine(arg2);
    // 結果を出力 ==> 11
    object res = scope.GetVariable("result");
    System.Console.WriteLine(res);
   }
   catch (Exception ex)
   {
    System.Console.WriteLine("Error:"
     + ex.Message.ToString());
   }
  }

================================

C#で Pythonファイル(test.py)内の変数の値の設定、取得を行います。

Python側では、あらかじめ変数に値が設定されている必要はありません。

C#で Python側の変数param1, param2 に、それぞれ値を設定し、Python側が実行された後、ython側の計算結果のresult変数をC#で取得しています。

C# + Python embedding 簡単な計算

CC#からIronPythonに計算させる簡単な例。

■.NET Framework2.0 sp1で実行
 sp1でないと動かない点に注意
■IronPython 2.0
 以下からIronPython-2.0.1-Bin.zipをダウンロード
 http://www.codeplex.com/IronPython → Downloads

 以下を参照設定で追加
  IronPython.dll
  IronPython.Modules.dll
  Microsoft.Scripting.Core.dll
  Microsoft.Scripting.dll
  Microsoft.Scripting.ExtensionAttribute.dll

---- コード ----

using System;

using IronPython;
using IronPython.Hosting;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;

...

    public static void Main(string[] args)
    {
      try
      {
        ScriptEngine engine = Python.CreateEngine();
        ScriptScope scope = engine.CreateScope();

        // 1 + 2 を Python に計算させる
        ScriptSource source =
          engine.CreateScriptSourceFromString(
            "1 + 2", SourceCodeKind.Expression);
        object res = source.Execute();
        // 結果を出力 ==> 3
        System.Console.WriteLine(res);
      }
      catch (Exception ex)
      {
        System.Console.WriteLine("Error:" +
          ex.Message.ToString());
      }
    }