LoginSignup
0
0

C#に入門してみた(Mini Ruccolaコンパイラを書くための下調べ)

Last updated at Posted at 2023-07-02

C#で簡単な自作言語のコンパイラを書いた を書くために下調べしたことのメモです。

だいたいこのくらい分かれば Mini Ruccola コンパイラが書ける。

インストール

Ubuntu Linux 22.04(実際は Docker コンテナ内)で作業します。

apt-get install -y --no-install-recommends mono-devel

apt で入れるのが一番簡単そうだったので今回はこれで。

以下のように表示されるのでバージョンはたぶん 4.5 です。

  $ mcs -help | grep VERSION
   -sdk:VERSION         Specifies SDK version of referenced assemblies
                        VERSION can be one of: 2, 4, 4.5 (default) or a custom value

エディタ

Emacs

コンパイル

// sample_010.cs
using System;

public class Sample {
    static public void Main() {
        Console.WriteLine(42);
    }
}

mcs コマンドでコンパイル、mono コマンドで実行。

  $ mcs sample_010.cs
  #=> sample_010.exe ができる

  $ mono sample_010.exe
42

デバッグオプションを付けないとエラー発生時のスタックトレースに行番号が出ない。

mcs -debug
mono --debug

ファイル分割

Mini Ruccola コンパイラの実装に必須ではないけど現実的にはできないと困るので。

- ./
  - sample_020.cs
  - sample_020_sub.cs
// sample_020.cs
using System;

public class Sample {
    static public void Main() {
        Console.WriteLine(11);
        Lib.DoSomething(); // 別のファイルに書いた処理を利用する
    }
}
// sample_020_sub.cs
using System;

public class Lib {
    static public void DoSomething() {
        Console.WriteLine(22);
    }
}
  $ mcs sample_020.cs sample_020_sub.cs
  #=> sample_020.exe ができる

  $ mono sample_020.exe
11
22

出力ファイル名は -out で指定することも可能。

標準出力・標準エラー出力に print

標準エラー出力の利用は Mini Ruccola コンパイラの実装に必須ではないけど使えた方が便利。

// sample_030.cs
using System;

public class Sample {
    static public void Main() {
        Console.Write("out 1\n");
        Console.WriteLine("out 2");
        Console.WriteLine(123);

        // 標準エラー出力に出力
        Console.Error.Write("err 1\n");
    }
}

コンパイルして実行まで行う mrun.sh を用意した。

  $ ./mrun.sh sample_030.cs | cat -n
     1  out 1
     2  out 2
     3  123
err 1

標準入力から1バイトずつ読む

1バイトずつじゃなくてまとめて読むでもいいんだけど、プリミティブな機能としてはこれなのでまずはこれで。

// sample_050.cs
using System;
using System.IO;

public class Sample {
    static public void Main() {
        using (Stream inStream = Console.OpenStandardInput()) {
            byte[] bs = new byte[1];

            while (true) {
                int n = inStream.Read(bs, 0, 1);
                if (n == 0) {
                    break;
                }
                Console.WriteLine(bs[0]);
            }
        }
    }
}
  $ echo "あいAB" | ./mrun_stdin.sh sample_050.cs
227
129
130
227
129
132
65
66
10

バイト配列を文字列に変換

これができれば上のと組み合わせて標準入力から全部読んで string を得られる(もっといい方法があると思いますがまずはこれで)。

// sample_070.cs
using System;

public class Sample {
    static public void Main() {
        byte[] bytes = {
            227, 129, 130, // あ
            227, 129, 132, // い
            65, // A
            66, // B
            10, // LF
        };

        // bytes[] => string
        string str = System.Text.Encoding.UTF8.GetString(bytes);

        Console.Write(str);
    }
}
  $ ./mrun.sh sample_070.cs
あいAB

標準入力からUTF-8として全部読む

(2023-07-15 追記)

Stream, StreamReader を使ったもの。

// sample_070.cs
using System;
using System.IO;

public class Sample {
    static public void Main() {
        string str = ReadStdinAll();
        Console.Write(str);
    }

    static string ReadStdinAll() {
        using (Stream stream = Console.OpenStandardInput()) {
            using (StreamReader sr = new StreamReader(stream, System.Text.Encoding.UTF8)) {
                return sr.ReadToEnd();
            }
        }
    }
}
  $ echo "あいAB" | ./mrun_stdin.sh sample_071.cs 
あいAB

文字列処理

コマンドライン引数

List

using System;
using System.Collections.Generic;

public class Sample {
    static public void Main() {
        List<int> xs = new List<int>();
        xs.Add(11);
        xs.Add(22);
        Console.WriteLine(xs.Count); //=> 2
        Console.WriteLine(xs[1]); //=> 22

        foreach (int n in xs) {
            Console.WriteLine(n);
        }
    }
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0