ttki's diary

プログラミング好きな人の日記です

インタプリタを作ってみた

こんにちは。 今回は、インタプリタを作ってみたので紹介したいともいます。

名前は CON です。まだ、開発を始めたばかりなので出来ることが少ないですが、for や if を使うことはできます。

構文

//Hello world!
println "Hello world!"
print "Hello world!\n";//セミコロンはなくてもよし

//変数 英文字で始まり A-Z a-z 0-9 _ を使っていれば変数です
//今のところ、数字しかサポートしていません
0var //NG
var1 //OK
arr[0] //OK
arr2[0, 0] //OK 多次元配列の場合は、コンマでくぎる。
//代入
i = 12
n = i + 2 * (i / 2)
// 変数の宣言は必要なし。

//if文 elseも使えます. ifの最後には end を使ってださい.
i = 12
if i == 12
 println "True"
else
 println "False"
end

//for文 forと言っていますが、ほとんど while ですw
i = 0
for i < 10
 println i
 i = i + 1 //明示的に加算する必要あり
endfor // for の最後は endfor で閉じる

// 標準関数たち
print "Strings" //改行なしで画面に出力
println "Strings" //改行あり 出力
input i //画面から入力し i に代入
now = clock // clock - CPU時間の取得
random = rand // rand - 乱数の取得 xorshift

//演算子
+ - * / % == != >= <= > < //ぐらいかな?

説明が雑ですいません。 とりあえず FizzBuzz してみます。

s = clock
i = 0
for i < 30
    if i % 15 == 0
        println "FizzBuzz"
    else
        if i % 5 == 0
        println "Buzz"
    else
        if i % 3 == 0
        println "Fizz"
    else
        println i
    end
    i = i + 1
endfor
e = clock

print "FizzBuzz にかかった時間"; println (e - s) / clock_sec;
//こんな感じ

ソースは GitHub で公開中 (スパゲッティww

http://www.github.com/maekawatoshiki/project-con-interpreter-/

逆ポーランド法-その後のその後...

こんにちは。もう、2015年になってから一ヶ月になってしまいますね。

何回も取り上げてきた逆ポーランド法ですが、ようやくコードがきれいに(一から書き直した笑)なったので、紹介しようと思います。

なんできれいになったのかというと、最近はじめた Javascript の逆ポーランド法ソース(ネットにあったもの) を C++ に移植したからです(笑

さすがに自分できれいに作るには、無理がありました。

さっそくソースです。

Light_RPN_calc

簡単な、クラス説明

class RPN//逆ポーランド法計算クラス。
RPN::expr//計算式を代入 2*(12+32/431) etc...
double RPN::calc()// exprの式を計算。Double で返します。

#逆ポーランド法について

逆ポーランド法は、中間記法(普段使ってる式の表し方。12*15, 5/1-12 とか)とは違い、演算子を後ろに置きます。(12 21 + とか)

どうやって変換するのかというと、
12+43なら、まず先頭の12をストックします。(stack 12)
+がきたら、演算子は後ろに置く決まりなので、+はとりあえずほっといておきます。
43がきたので12の後ろに ストックします(stack 12 43) 数字がなくなったので、ほっといておいた+をストックします(stack 12 43 +) 結果的に、12 43 +となります。

12*43+1なら、*と+がありますが、乗算と除算は優先順位が高いので、+かーがきたら、その時点でストックします。説明が下手ですね笑
12*43+1
stack 12

stack 12 43

+ 乗算より優先順位の低い+がきたのでストックします。

stack 12 43 * +
stack 12 43 * + 1
よって、 12 43 * + 1 となります。

逆ポーランド法を計算して、答えを出すには、
日本語のような、計算をします。
分かり易く言うと、12と43をかけて1を足す。
日本人には、分かり易いですね。

#余談
最近,HTML/CSS/Javascript を始めました。
きっかけは、Firefox OSのアプリを作ってみたかったからです。
今は、ペイントソフトを作っています。 HTML5 ってすごいですね!

逆ポーランド法-その後

こんにちは。
今回は、前回の逆ポーランド法ソースに括弧の解析機能をつけました。

相変わらずのスパゲッティ(笑

括弧を解析できるようになったので、そこそこの電卓は作れそうですね。
そこで考えた、電卓のメイン関数です。

#include <iostream>
#include "calc.h"//Gist の逆ポーランド法クラスヘッダー
using namespace std;

int main()
{
	CALC cl;
	char str[1024] = { 0 };

	while(1)
	{
		memset(str,0,sizeof(str));

		cin.getline(str,sizeof(str));
		cout << cl.calc(str) << endl;
	
	}
}

入力の数式を計算して、計算結果を出力します。

#余談
Visual Studio Community 2013 をダウンロードしました。
重たいですが、リソースを扱えたり、とにかく6万円が0円になるのはすごいことですね。

逆ポーランド法電卓

お久しぶりです。

今回は、前から作りたかった逆ポーランド法を実装しました。
逆ポーランド法は前から作っていたのですが、std::stack という便利な存在について知ったので、書き換えました。

#逆ポーランド法とは?

・正直、Wikipediaが一番詳しいですが要するに、 
 1 + 5 * 3 を
 1 5 3 * + のように表す方法です。

逆ポーランド法は、コンピューターに理解しやすい表し方で、ソースも簡単にかけます。
それに対し、普通の表記(1+1)を逆ポーランド法に変換するソースは難しかったです。

それでは、今回のコードです。あくまでも一例ですし、スパゲッティなのであまり参考にしないほうがいいかも(笑

CALC::calc 引数の文字列を計算して、double で返す。

すいませんね、変なソースで(笑

余談

Github登録しました。やっぱり気分変わりますね~ 
というか、githubより、gistのほうがよく使ってます^^

Brainfuck + ちょっと拡張

今回は、Brainfuck について...

Brinfuck は8つしか命令がないチューリング完全な難解プログラミング言語です。
8つとは、< > + - , . [ ] のことです。それぞれ,ポインタ移動、加減、1文字入力、1文字出力、ループとか... になっています。詳しくは Brainfuck - Wikipediaなどを参考にしてください。

今回は、Brainfuck の interpreter を作りました。+少し拡張して、# という命令を入れました。
# はポインタの指す値を、.では、文字(ASCIIとか)で表示しますが、# では、数字として表示します。
要するに、

                1. [>++++++++<-]>+.

とすると
A
と表示されますが、

                1. [>++++++++<-]>+#

とすると
65
と表示されます。(すこしデバッグをやりやすくするために追加しました)

というわけで、今回のコードです。

#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;

class BFI
{
private:

public:
	char *s;
	int v[1024], pos;
	int loop;
	int slen;

	BFI():v(),pos(0),loop(0),s(){}; 
	~BFI(){ delete[] s; };
	int set(char *str);
	int run();
};

int BFI::set(char *str)
{
	slen = strlen(str) + 1;//\0
	s = new char[slen]();
	strcpy(s, str);
	return 0;
}


int BFI::run()
{
	for(int i=0; i < slen; i++)
	{
		switch(s[i])
		{
		case '>':
			pos++;
			break;

		case '<':
			pos--;
			break;

		case '+':
			v[pos]++;
			break;

		case '-':
			v[pos]--;
			break;

		case '.':
			printf("%c", v[pos]);
			break;

		case '#':
			cout << v[pos] << endl;
			break;

		case ',':
			v[pos] = getchar();
			break;

		case '[':
			if(v[pos] == 0)
			{
				for(i++; loop > 0 || s[i] != ']'; i++){
					if(s[i] == '[')
						loop++;
					if(s[i] == ']')
						loop--;
				}
			}
			break;

		case ']':
			if(v[pos] != 0)
			{
				for(i--; loop > 0 || s[i] != '['; i--){
					if(s[i] == ']')
							loop++;
					if(s[i] == '[')
							loop--;
					}
				i--;
			}
			break;
		}
	}
	return 0;
}

int main()
{
	BFI brainfuckinterpreter;
	char *code =	"++++++++[>+++++++++<-]>."//72 h
			"---."//69 e
			"+++++++.."//76 ll
			"+++."//79 HELLO o
			"<+++++[>---------<-]>--."//32 
			"<+++++[>+++++++++++<-]>."//87 w
			"--------."//79 o
			"+++."//82 r
			"------."// 76 l
			"<++++[>--<-]>."// 68 d
			"<+++++[>-------<-]>."//33 !
			"<+++++[>----<-]>---.";// 10 \n

	brainfuckinterpreter.set(code);
	

	brainfuckinterpreter.run();

}

/*
BFI ... Brainfuckinterpreter Object
BFI::set() ... BrainfuckのコードをBFI::sにセットします。
BFI::run() ... BFI::sのコードを実行時します。
*/

Visual Stdio 2012 様がきたー

きましたよ~~
ついに~~
Visual stdio 2012 が~~
きた~~~~~

2013じゃないのかといわれそうですが、MeのパソコンはWin7 なので、インストールできません...

本当にうれしいですよ~~
今は、まだ、インストールしていますが、終わったら、早速使ってみます!!!!!!

で、何でインストールしたかというと、Windows7 をクリーンインストールしたので、
ついでに、Visual をインストールしました!

ハッシュ関数をつくってみた (完成形

今回は、この前の適当なハッシュ関数の説明をします。

まずハッシュ関数とは、主に暗号化通信(通信販売など amazon 楽天...etc)に使われています。
例えば、クライアントがサーバーにパスワードを送るとします。
その時、生のままのパスワードをサーバーに送っては危険ですね...
そんなとき、ハッシュ関数(有名なものに MD5 SHA などがある MD5 は Mac Linux などで MD5コマンドを使うことができるので知っている方も多いと思う) を使います。
ハッシュ関数は主に、文字列などから特定のビット数(128, 160, 256...) のハッシュ値を計算します。
ハッシュ関数には、
   ・ハッシュ値から、元の数値を計算できない。
   ・同じハッシュ値を持つ文字列などを2つ以上生成することがとても難しい。
などの特性があります。

このことから、ハッシュ関数を使うと
・クライアントはユーザーのパスワードのハッシュ値を計算。
   ・サーバーは送られてきたハッシュ値とサーバー内のハッシュ値とを比較。
することで、安全にユーザー認証できるというわけです。
それに、サーバー内に生のパスワードがあったら危険ですからね。

というわけで、このようなハッシュ関数を作ってみました。
最初は、SHA-1 を実装してみようと思ったのですが、いい情報がなかったので自分で作りました。
かーなり、安全性に欠けているかも知れませんが、ファイル比較程度には使えるはずです。

なにかあったら、コメントお願いします^^

コード

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

const int BIT = 8;
const int prime[9] = { 11, 13, 17, 19, 23, 29, 31, 37};
const int NUM = 16777931;

class HD
{
public:
	HD();
	char digest[33];
	int hash(char [], int, unsigned int[]);
	int shash(char []);
	void clear();
	void operator = (char *ch);
	char* operator ()();
};

HD::HD()
{
	memset(digest, 0, 33);
}

void HD::operator = (char *ch)
{
	shash(ch);
}

char* HD::operator ()()
{
	return digest;
}

void HD::clear()
{
	memset(digest, 0, 33);
}

int HD::hash(char word1[],int wordsz1, unsigned int data[])
{
	unsigned int i=0, j=0, k = 2166136261, l = 0, data2[BIT] = {0};
	char word[32] = { 0 };
	unsigned long A, B, C, D, E, F, G, H;// Hash Register

	memset(data,0,BIT);

	for(l = 0; l < 2; l++)
	{
		k = (k * NUM) ^ word[i % 32];
		for(i = 0; i < BIT; i++)
		{
			word[i % 32] |= k << 2;
			word[i % 32] += word1[i];
			word[i % 32] ^= word1[i] * k;
		}
	}

	for(i = 0; word1[i] != 0; i++)
	{
		k = (k * NUM) + word1[i]  + word[i % 32];
		data[i % BIT] *= word[i % 32];
		data[i % BIT] += k;
	}

	for(i = 0; i < BIT; i++)
	{
		k = (k * NUM) + data[i];
		data[i] ^= prime[i] * k;
		data[i] &= prime[i] + k;
		data[i] += prime[i];
		data[i] += (data[i] & k) ^ ~k;
	}

	data2[0] = data[0];		A = data2[7];
	data2[1] = data[1];		B = data2[0];
	data2[2] = data[2];		C = data2[1];
	data2[3] = data[3];		D = data2[2];
	data2[4] = data[4];		E = data2[3];
	data2[5] = data[5];		F = data2[4];
	data2[6] = data[6];		G = data2[5];
	data2[7] = data[7];		H = data2[6];

	memset(data, 0, BIT);

	for(j = 0; j < 64; j++)
	{
		k = (k * NUM) + A + B + C + D + E + F + G + H;

		A = (A = A ^ H * ~k) * (A = A ^ (k * A) ^ ~H);
		B = (B = B ^ G * ~k) * (B = B ^ (k * B) ^ ~G);
		C = (C = C ^ F * ~k) * (C = C ^ (k * C) ^ ~F);
		D = (D = D ^ E * ~k) * (D = D ^ (k * D) ^ ~E);
		E = (E = E ^ D * ~k) * (E = E ^ (k * E) ^ ~D);
		F = (F = F ^ C * ~k) * (F = F ^ (k * F) ^ ~C);
		G = (G = G ^ B * ~k) * (G = G ^ (k * G) ^ ~B);
		H = (H = H ^ A * ~k) * (H = H ^ (k * H) ^ ~A);

		A = ((H = A ^ (A * (A % k))) | (k * A)) ^ (k + H) ^ (~A);
		B = ((G = B ^ (B * (B % k))) | (k * B)) ^ (k + G) ^ (~B);
		C = ((F = C ^ (C * (C % k))) | (k * C)) ^ (k + F) ^ (~C);
		D = ((E = D ^ (D * (D % k))) | (k * D)) ^ (k + E) ^ (~D);
		E = ((D = E ^ (E * (E % k))) | (k * E)) ^ (k + D) ^ (~E);
		F = ((C = F ^ (F * (F % k))) | (k * F)) ^ (k + C) ^ (~F);
		G = ((B = G ^ (G * (G % k))) | (k * G)) ^ (k + B) ^ (~G);
		H = ((A = H ^ (H * (H % k))) | (k * H)) ^ (k + A) ^ (~H);

		A ^= (A | ~A); A += (B | ~H);
		B ^= (B | ~B); B += (C | ~G);
		C ^= (C | ~C); C += (D | ~F);
		D ^= (D | ~D); D += (E | ~E);
		E ^= (E | ~E); E += (F | ~D);
		F ^= (F | ~F); F += (G | ~C);
		G ^= (G | ~G); G += (H | ~B);
		H ^= (H | ~H); H += (A | ~A);

		data2[0] = A;	A = data2[7];
		data2[1] = B;	B = data2[0];
		data2[2] = C;	C = data2[1];
		data2[3] = D;	D = data2[2];
		data2[4] = E;	E = data2[3];
		data2[5] = F;	F = data2[4];
		data2[6] = G;	G = data2[5];
		data2[7] = H;	H = data2[6];

	}

	{// Copy
		data[0] = A;
		data[1] = B;
		data[2] = C;
		data[3] = D;
		data[4] = E;
		data[5] = F;
		data[6] = G;
		data[7] = H;
	}

	for(i = 0; i < BIT; i++)
	{
		data[i] %= 65535;
		if(data[i] < 0) data[i] = -data[i];
	}

	return 0;
}

int HD::shash(char word[])
{
	unsigned int data[BIT] = { 0 },i = 0, wordsz = 0;
	wordsz = strlen(word);
	memset(digest, 0, 33);
	hash(word,wordsz,data);
	for(i = 0; i < BIT; i++)
	{
		sprintf(digest, "%s%04x", digest, data[i]);
	}
	return 0;
}


int main()
{
	char str[512] = { 0 };
	HD hash;// 自作のハッシュ関数

	hash = str;// 文字列を HD object に代入

	cout << str << " : " << hash()// Hash 後の文字列 32文字 + \0
	 		 << endl;

	strcpy(str, "The quick brown fox jumps lazy dog");
	hash = str;

	cout << (char *)str << " : " << hash() << endl;

	strcpy(str, "The quick brown fox jumps lazy cog");
	hash = str;

	cout << (char *)str << " : " << hash() << endl;

	return 0;
}
/* HD ハッシュ関数のクラス
   HD のオブジェクトに文字列を代入するとハッシュ化します (operator =)
   HD のオブジェクトに () をつけて関数形式にすると、ハッシュ化した文字列を返します。
*/

ちょっと見にくいですね...

## 余談

最近 GCC と GitHub Atom Editor をインストールしました。
GCC 持ってなかったの?? と突っ込まれそうですが Visual C++ 2010 を使っていたので持っていなかったんです...
やっぱ持っているといいですね^^ Atom editor も GitHub が作ったと聞いてダウンロードしてみました。
ちょっと、重いかもしれませんが、色々設定すれば短形選択できたりデフォルトで C/C++ のシンタックスカラーリングをサポートしていたりととてもよいものでした。
とりあえず USB Memory にいれて、環境の整っていない PC などでコンパイルできるようになりました!!
うれしい!!