bbi_prot.h

/**************************************/
/*  filename:bbi_prot.h 全関数のプロトタイプ*/
/**************************************/

// --------------bbi_pars.cpp構文解析
void init();
void convert_to_internalCode(char *fname);
void convert();
void convert_block_set();
void convert_block();
void convert_rest();
void optionSet();
void varDec1();
void var_namechk(const Token& tk);
void set_name();
void set_aryLen();
void fncDec1();
void backPatch(int line, int n);
void setCode(int cd);
int setCode(int cd, int nbr);
void setCode_rest();
void setCode_End();
void setCode_EofLine();
void push_intercode();
bool is_localScope();

// ------------------bbi_tkn.cppトークン処理
void initChTyp();
void fileOpen(char *fname);
void nextLine();
Token nextLine_tkn();
Token nextTkn();
bool is_ope2(char c1, char c2);
TknKind get_kind(const string& s);
Token chk_nextTkn(const Token& tk, int kind2);
void set_token_p(char *p);
string kind_to_s(int kd);
string kind_to_s(const CodeSet& cd);
int get_lineNo();

// ------------------bbi_tbl.cpp記号表処理
int enter(SymTbl& tb, SymKind kind);
void set_startLtable();
bool is_localName(const string& name, SymKind kind);
int searchName(const string& s, int mode);
vector<SymTbl>::iterator tableP(const CodeSet& cd);

// ------------------bbi_code.cppメモリ管理と構文チェックと実行
void syntaxChk();
void set_startPc(int n);
void execute();
void statement();
void block();
double get_expression(int kind1=0, int kind2=0);
void expression(int kind1, int kind2);
void expression();
void term(int n);
void factor();
int opOrder(TknKind kd);
void binaryExpr(TknKind kd);
void post_if_set(bool& flg);
void funcCall_syntax(int funcNbr);
void funcCall(int fncNbr);
void fncExec(int fncNbr);
void sysFncExec_syntax(TknKind kd);
void sysFncExec(TknKind kd);
int get_memAdrs(const CodeSet& cd);
int get_topAdrs(const CodeSet& cd);
int endline_of_If(int line);
void chk_EofLine();
TknKind lockCode(int line);
CodeSet chk_nextCode(const CodeSet& cd, int kind2);
CodeSet firstCode(int line);
CodeSet nextCode();
void chk_dtTyp(const CodeSet& cd);
void set_dtTyp(const CodeSet& cd, char typ);
int set_LITERAL(double d);
int set_LITERAL(const string& s);
// ------------------bbi_misc.cpp雑関数
string dbl_to_s(double d);
string err_msg(const string& a, const string& b);
void err_exit(Tobj a="\1", Tobj b="\1", Tobj c="\1", Tobj d="\1");

bbi.h

/**************************************/
/*  filename:bbi.h 共通ヘッダ           */
/**************************************/
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <stack>

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>

using namespace std;

/* ---------------- define */
#define SHORT_SIZ sizeof(short int)
#define SHORT_P(p) (short int *)(p)
#define UCHAR_P(p) (unsigned char *)(p)
#define LIN_SIZ 255

/* ---------------- enum struct etc */
enum TknKind {
    Lparen='(', Rparen=')', Lbracket='[', Rbracket=']', Plus='+', Minus='-',
    Multi='*', Divi='/', Mod='%', Not='!', Ifsub='?', Assign='=',
    IntDivi='\\', Comma=',', DblQ='"',
    Func=150, Var, If, Elif, Else, For, To, Step, While,
    End, Break, Return, Option, Print, Println, Input, Toint,
    Exit, Equal, NotEq, Less, LessEq, Great, GreatEq, And, Or,
    END_KeyList,
    Ident, IntNum, DblNum, String, Leter, Doll, Digit,
    Gvar, Lvar, Fcall, EofProg, EofLine, Others
};

struct Token {
    TknKind kind;
    string text;
    double dblVal;
    Token() { kind = Others; text=""; dblVal=0.0; }
    Token (TknKind k) { kind=k; text=""; dblVal=0.0; }
    Token (TknKind k, double d){ kind=k; text=""; dblVal=d; }
    Token (TknKind k, const string& s) { kind=k; text=s; dblVal=0.0; }
    Token (TknKind k, const string& s, double d){ kind=k; text=s; dblVal=d; }
};

enum SymKind { noId, varId, fncId, paraId };
enum DtType { NON_T, DBL_T };

struct SymTbl{
    string name;
    SymKind nmKind;
    char dtTyp;
    int aryLen;
    short args;
    int adrs;
    int frame;
    SymTbl() { clear(); }
    void clear() {
        name=""; nmKind=noId; dtTyp=NON_T;
        aryLen=0; args=0; adrs=0; frame=0;
    }
};

struct CodeSet{
    TknKind kind;
    const char *text;
    double dblVal;
    int symNbr;
    int jmpAdrs;
    CodeSet() { clear(); }
    CodeSet(TknKind k) { clear(); kind=k; }
    CodeSet(TknKind k, double d ){ clear(); kind=k; dblVal=d; }
    CodeSet(TknKind k, const char *s) { clear(); kind=k; text=s; }
    CodeSet(TknKind k, int sym, int jmp){
        clear(); kind=k; symNbr=sym; jmpAdrs=jmp;
    }
    void clear() { kind=Others; text=""; dblVal=0.0; jmpAdrs=0; symNbr=-1; }
};

struct Tobj {
    char type;
    double d;
    string s;
    Tobj() { type = '-'; d = 0.0; s = ""; }
    Tobj(double dt) { type = 'd'; d = dt; s = ""; }
    Tobj(const string& st){ type = 's'; d = 0.0; s= st; }
    Tobj(const char *st){ type = 's'; d= 0.0; s = st; }
};

class Mymemory{
private:
    vector<double> mem;
public:
    void auto_resize(int n){
        if (n >= (int)mem.size()) { n = (n/256 + 1) * 256; mem.resize(n); }
    }
    void set(int adrs, double dt){mem[adrs] = dt; }
    void add(int adrs, double dt){ mem[adrs] += dt; }
    double get(int adrs) { return mem[adrs]; }
    int size() { return (int)mem.size(); }
    void resize(unsigned int n){ mem.resize(n); }
};

電卓プログラム

構文解析ルーチンを利用して、小さな電卓プログラムをつくります。字句解析や式解析の基本手法が含まれています。

/*--------------------------------*/
/*   電卓プログラム minicalc.cpp    */
/*--------------------------------*/
#include 
#include 
#include 
using namespace std;

enum TknKind {
    Print, Lparen, Rparen, Plus, Minus, Multi, Divi,
    Assign, VarName, IntNum, EofTkn, Others
};

struct Token {
    TknKind kind;
    int intVal;
    Token() { kind = Others; intVal = 0; }
    Token(TknKind k, int d=0) { kind = k; intVal = d; }
};

void input();
void statement();
void expression();
void term();
void factor();
Token nextTkn();
int nextCh();
void operate(TknKind op);
void push(int n);
int pop();
void chkTkn(TknKind kd);

#define STK_SIZ 20
int stack[STK_SIZ+1];
int stkct;
Token token;
char buf[80], *bufp;
int ch;
int var[26];
int errF;

int main()
{
    while(1){
        input();
        token = nextTkn();
        if (token.kind == EofTkn) exit(1);
        statement();
        if (errF) cout << " --err--\n";
    }
    return 0;
}

void input()
{
    errF = 0; stkct = 0;
    cin.getline(buf, 80);
    bufp = buf;
    ch = nextCh();
}

void statement()
{
    int vNbr;
    
    switch (token.kind){
        case VarName:
            vNbr = token.intVal;
            token = nextTkn();
            chkTkn(Assign); if (errF) break;
            token = nextTkn();
            expression();
            var[vNbr] = pop();
            break;
        case Print:
            token = nextTkn();
            expression();
            chkTkn(EofTkn); if (errF) break;
            cout << " " << pop() << endl;
            return;
        default:
            errF = 1;
    }
    chkTkn(EofTkn);
}

void expression()
{
    TknKind op;
    
    term();
    while (token.kind == Plus || token.kind==Minus){
        op = token.kind;
        token = nextTkn(); term(); operate(op);
    }
}

void term()
{
    TknKind op;
    
    factor();
    while(token.kind==Multi || token.kind==Divi){
        op = token.kind;
        token = nextTkn(); factor(); operate(op);
    }
}

void factor()
{
    switch (token.kind){
        case VarName:
            push(var[token.intVal]);
            break;
        case IntNum:
            push(token.intVal);
            break;
        case Lparen:
            token = nextTkn();
            expression();
            chkTkn(Rparen);
            break;
        default:
            errF = 1;
    }
    token = nextTkn();
}

Token nextTkn()
{
    TknKind kd = Others;
    int num;
    
    while(isspace(ch))
        ch = nextCh();
    if (isdigit(ch)){
        for (num=0; isdigit(ch); ch = nextCh())
            num = num*10 + (ch-'0');
        return Token(IntNum, num);
    }
    else if (islower(ch)){
        num = ch - 'a';
        ch = nextCh();
        return Token(VarName, num);
    }
    else {
        switch (ch){
            case '(': kd = Lparen; break;
            case ')': kd = Rparen; break;
            case '+': kd = Plus; break;
            case '-': kd = Minus; break;
            case '*': kd = Multi; break;
            case '/': kd = Divi; break;
            case '=': kd = Assign; break;
            case '?': kd = Print; break;
            case '\0': kd = EofTkn; break;
        }
        ch = nextCh();
        return Token(kd);
    }
}

int nextCh()
{
    if (*bufp == '\0') return '\0'; else return *bufp++;
}

void operate(TknKind op)
{
    int d2 = pop(), d1 = pop();
    
    if (op==Divi && d2==0) { cout << " division by 0\n"; errF = 1; }
    if(errF) return;
    switch (op){
        case Plus: push(d1+d2); break;
        case Minus: push(d1-d2); break;
        case Multi: push(d1*d2); break;
        case Divi: push(d1/d2); break;
    }
}

void push(int n)
{
    if (errF)return;
    if (stkct+1 > STK_SIZ) { cout << "stack overflow\n"; exit(1); }
    stack[++stkct] = n;
}

int pop()
{
    if (errF) return 1;
    if (stkct < 1) { cout << "stack underflow\n"; exit(1); }
    return stack[stkct--];
}

void chkTkn(TknKind kd)
{
    if (token.kind != kd) errF = 1;
}

逆ポーランド記法

aからzまでの文字変数名と、一桁の数字を入力できます。

/*--------------------------------*/
/* 逆ポーランド記法の評価polish_p.cpp */
/*--------------------------------*/
#include 
#include 
#include 
using namespace std;

void polish(char *s);
int execute();
int getvalue(int ch);
int order(int ch);
void push(int n);
int pop();

#define STK_SIZ 20
int stack[STK_SIZ+1];
int stkct;
char plsh_out[80];

int main()
{
    char siki[80];
    int ans;
    
    cout << "入力:"; cin.getline(siki, 80);
    polish(siki);
    if (plsh_out[0] == '\n') exit(1);
    ans = execute();
    cout << "変換:" << plsh_out << endl;
    cout << "結果:" << ans << endl;
    return 0;
}

void polish(char *s)
{
    int wk;
    char *out = plsh_out;
    
    stkct = 0;
    for(;;) {
        while (isspace(*s)){ ++s; }
        if (*s == '\0'){
            while (stkct > 0){
                if ((*out++ = pop()) == '('){
                    puts("'('が不正\n"); exit(1);
                }
            }
            break;
        }
        if (islower(*s) || isdigit(*s)){
            *out++ = *s++; continue;
        }
        switch(*s) {
                case '(':
                push(*s);
                break;
                case ')':
                while ((wk = pop()) != '('){
                    *out++ = wk;
                    if(stkct == 0){ puts("'('が不足\n"); exit(1);}
                }
                break;
            default:
                if (order(*s) == -1){
                    cout << "不正な文字: " << *s << endl; exit(1);
                }
                while (stkct>0 && (order(stack[stkct]) >= order(*s))){
                    *out++ = pop();
                }
                push(*s);
        }
        s++;
    }
    *out = '\0';
}

int execute()
{
    int d1, d2;
    char *s;
    
    stkct = 0;
    for(s=plsh_out; *s; s++){
        if(islower(*s))
            push(getvalue(*s));
        else if (isdigit(*s))
            push(*s - '0');
        else {
            d2 = pop(); d1 = pop();
            switch(*s){
                case '+': push(d1+d2); break;
                case '-': push(d1-d2); break;
                case '*': push(d1*d2); break;
                case '/': if (d2 == 0){ puts("ゼロ除算"); exit(1); }
                    push(d1/d2); break;
            }
        }
    }
    if (stkct != 1){ cout << "ERROR\n"; exit(1); }
    return pop();
}

int getvalue (int ch)
{
    if (islower(ch)) return ch-'a' + 1;
    return 0;
}

int order(int ch)
{
    switch (ch){
        case '*': case '/': return 3;
        case '+': case '-': return 2;
        case '(': return 1;
    }
    return -1;
}

void push(int n)
{
    if (stkct+1 > STK_SIZ) { puts("stack overflow"); exit(1); }
    stack[++stkct] = n;
}

int pop()
{
    if (stkct < 1){ puts("stack underflow"); exit(1); }
    return stack[stkct--];
}

インデクサのオーバーロード

クラスまたは構造体にインデクサを定義すると、a[i]のように、配列同様に、「インデックス」によるアクセスが可能になります。

同じクラスに複数のインデクサを定義することも可能です。その場合は、メソッドのオーバーロードと同様に、パラメータのシグネチャを別々にする必要があります。パラメータの型が同じで、「インデクサの型」だけが異なるインデクサを定義することはできません。

using System;
using System.Collections.Generic;
using System.Text;

namespace Gushwell.Sample {
	public class Book {
		public string Name { get; set; }
		public string Author { get; set; }
		public string Publisher { get; set; }
		public string ISBN { get; set; }
		public Book(string name, string author, 
			string publisher, string isbn) {
			this.Name = name;
			this.Author = author;
			this.Publisher = publisher;
			this.ISBN = isbn;
		}
	}

	public class BookList {
		private List<Book> books = new List<Book>();
		public void Add(Book book){
			books.Add (book);
		}

		// インデクサの定義
		public Book this[int index]{
			get {
				return books [index];
			}
		}
		public Book this[string ISBN]{
			get {
				foreach (Book b in books)
					if (b.ISBN == ISBN)
						return b;
				return null;
			}
		}
	}

	class Program {
		static void Main(string[] args){
			BookList books = new BookList ();
			books.Add(new Book("こころ", "夏目漱石",
				"新潮社", "978-4101010137"));
			books.Add (new Book ("人間失格", "太宰治", "角川書店", "978-4041099124"));
			Book book1 = books [0];
			Console.WriteLine (book1.Name);
			Book book2 = books ["978-4041099124"];
			Console.WriteLine (book2.Name);
			Console.ReadLine ();
		}
	}
}

例外の再スロー

キャッチした例外を、そのまま上位層に再度投げることができます。これを例外の再スローといいます。例外を再スローする場合は、「throw」とだけ記述します。例外オブジェクトは指定しません。

using System;
using System.IO;

namespace ExceptionSample {
	class Program {
		static void Main(string[] args){
			try {
				Foo ();
			} catch (System.ArgumentException) {
				Console.WriteLine ("FooでArgumentException発生");
			}
			try {
				Bar ();
			} catch (System.ArgumentException) {
				Console.WriteLine ("BarでArgumentException発生");
			}
			Console.ReadLine ();
		}

		public static void Foo() {
			try {
				Exec ();
			} catch (System.ArgumentException) {
				Console.WriteLine ("ExecでArgumentException発生");
			}
			Console.WriteLine ("Foo終了");
		}

		public static void Bar() {
			try {
				Exec ();
			} catch (System.ArgumentException) {
				Console.WriteLine ("ExecでArgumentException発生");
				throw;
			}
			Console.WriteLine ("Bar終了");
		}

		public static void Exec(){
			throw new System.ArgumentException ();
		}
	}
}

MainメソッドではFooメソッドとBarメソッドの呼び出し時の例外をキャッチするようにしています。Fooメソッド、Barメソッドの中でも、try-catchで例外処理をしています。

C#の例外処理

例外とは、プログラム実行時に発生するエラーのことです。C#の例外処理機能を使えば、実行中に発生する予期しないエラーや例外的な状況に対処できます。

C#では、「try」「catch」「finally」のキーワードを使って、例外処理を記述します。

最も基本的な書き方を以下に示します。

try {
  // なんらかの処理
 // この中で、例外が発生する可能性あり
} catch {
  // 例外が発生したときに処理したいコード
}

例外の発生を検出したい場合は、tryブロック内に記述します。tryブロック内で何らかの例外が発生すると、処理が中断され、catchブロックに処理が遷移します。次のコードで確認してみてください。

using System;

namespace Gushwell.Sample {
	class Program {
		static void Main(string[] args){
			try {
				int n = 100;
				string s = Console.ReadLine ();
				int m = int.Parse (s);
				int ans = n / m;
				Console.WriteLine (ans);
			} catch {
				Console.WriteLine ("エラーが発生");
			}
			Console.ReadLine ();
		}
	}
}

ポリモーフィズム

ポリモーフィズムとは、異なる型を同一視することによって、複数のクラスを同じ操作で制御できる言語特性のことです。「オブジェクト指向」において、もっとも重要な機能の一つと言えるでしょう。

多くのデザインパターンは、このポリモーフィズムの機能を利用しています。

ポリモーフィズムを使わない例

using System;
using System.Collections.Generic;

namespace PolySample {
	class Program {
		static void Main(string[] args){
			List<object> list = new List<object>();
			list.Add(new CSharper());
			list.Add(new Rubyist());
			foreach(object p in list){
				if (p is CSharper){
					(p as CSharper).Work();
				} else if (p is Rubyist){
					(p as Rubyist).Work();
				}
			}
			Console.ReadLine();
		}
	}

	public class CSharper {
		public void Work() {
			Console.WriteLine ("C#でプログラムを書きます");
		}
	}

	public class Rubyist {
		public void Work () {
			Console.WriteLine ("Rubyでプログラムを書きます");
		}
	}
}

ポリモーフィズムを利用した例

using System;
using System.Collections.Generic;

namespace PolySample {
	class Program {
		static void Main(string[] args){
			List<Programmer> list = new List<Programmer> ();
			list.Add(new CSharper());
			list.Add(new Rubyist());
			foreach(Programmer p in list){
				p.Work ();
				}
			Console.ReadLine();
		}
	}

public abstract class Programmer {
	public abstract void Work ();
}

	public class CSharper : Programmer {
		public override void Work() {
			Console.WriteLine ("C#でプログラムを書きます");
		}
	}

	public class Rubyist : Programmer {
		public override void Work () {
			Console.WriteLine ("Rubyでプログラムを書きます");
		}
	}
}

静的メンバーと静的クラス

プログラムが開始されるときは、どのクラスのインスタンスもnewされていない状態です。

using System;

class Program {
	static void Main() {
		Console.Write ("ヤード ->");
		string s = Console.ReadLine ();
		double yard = double.Parse (s);
		double meter = YardToMeter (yard);
		Console.WriteLine ("{0}ヤードは{1}メートルです。", yard, meter);
		Console.ReadLine ();
	}

	// 静的メソッドとして定義
	static double YardToMeter (double yd)
	{
		double meter = yd * 0.9144;
		return meter;
	}
}

mono

C#のアクセス修飾子

クラスのメンバーへのアクセスを制御するためのものがアクセス修飾子です。C#には、「private」「public」「protected」「internal」の4つが用意されています。
publicなメンバーは、そのメンバーが定義されているクラスの外側から自由にアクセスすることが可能です。一方、privateなメンバーは、そのクラスの中からのみアクセスすることができます。

using System;
using System.IO;

namespace Sample {
	class Program {
		static void Main(string[] args){
			NumericsPicker np = new NumericsPicker ("Sample.txt");
			string line = np.GetNext ();
			while (line != null){
				Console.WriteLine (line);
				line = np.GetNext ();
			}
			Console.ReadLine ();
		}
	}

	class NumericsPicker {
		private StreamReader reader;
		public NumericsPicker(string filepath){
			reader = new StreamReader(filepath);
		}

		private bool IsAllDigits(string line){
			foreach (var c in line) {
				if (!char.IsDigit (c))
					return false;
			}
			return true;
		}

		public string GetNext(){
			string line = reader.ReadLine ();
			while (line != null && !IsAllDigits (line))
				line = reader.ReadLine ();
			return line;
		}
	}
}