自由帳

@_nibral の技術ブログ

Writing An Interpreter In Go その1

その0はこちら

1.3: Lexer(字句解析器)の実装

1.3: Implement lexer · nibral/monkey_interpreter@66eb6e3 · GitHub

lexer/lexer.go

ソースコードを文字列として受け取って、一文字ずつ読み進めながらトークンの配列に変換する処理を作成。

1文字の識別子だったらそのままトークンとして追加、複数文字の場合はそれが単語なのか数値なのかを判定して対応するトークンを追加していく。範囲を指定して配列の一部を取り出せるgolang最高じゃんという気持ちになった。

func (l *Lexer) readNumber() string {
    position := l.position
    for isDigit(l.ch) {
        l.readChar()
    }
    return l.input[position:l.position] <-便利
}

文字種の判定 (isLetter, isDigit) と空白文字読み捨ての処理 (skipWhitespace) もシンプルで、これで十分なんだと安心。

あと、本に書かれているLexerの処理結果がわかりやすかった。

let x = 5 + 5;
↓
[
  LET,
  IDENTIFIER("x"),
  EQUAL_SIGN,
  INTEGER(5),
  PLUS_SIGN,
  INTEGER(5),
  SEMICOLON
]

lexer/lexer_test.go

Lexerのテストコードを作成。

満たすべき条件を配列として用意しておいて、ループでチェックする。「テーブルドリブンテスト」といってgolangでは標準的な書き方らしい。

本の内容とは関係ないけど、golangは標準でフォーマットとテストの仕組みがあるのがいいなと思ったり。たとえばJavaScriptだとESLint入れてルール設定したりmocha入れたりで面倒なので…

token/token.go

トークンの構造と種類を定義した。

1文字の識別子 (=, +, etc.) をそのまま扱うのはいいとして、複数文字の場合は事前に定義したテーブルに存在するかチェックして、存在すれば予約語、しなければユーザが定義した識別子として扱うところがなるほどねという感じ。予約語を増やしたければここに追加していけばいいんだなというのがすぐわかる。

1.4: 1文字識別子とキーワードの追加

1.4: Add single character tokens and keywords · nibral/monkey_interpreter@cf28a6d · GitHub

lexer/lexer.go

1文字の識別子に -, !, *, /, <, >, カンマ を追加。caseを増やすだけなのですぐできる。

lexer/lexer_test.go

新たに処理できるようになった識別子のテストコードを追加。

機能が増えるにしたがってテスト内容のテーブルがどんどん長くなるけどこれでいいんだろうか。適当なところで分割とかするべきなのか。

token/token.go

トークンの種類と予約語を追加。* と / を MULTIPLE, DIVIDE ではなく ASTERISK, SLASH にするのはほかの用途でも使うから?

if/else や return を追加したが、現時点ではトークンとして認識できるというだけで制御構文としての意味は一切処理されていない。

Writing An Interpreter In Go その0

コンパイラの勉強がしたくなったので、Thorsten Ballの"Writing An Interpreter In Go"を読むことにした。オライリーから「Go言語でつくるインタプリタ」として日本語版の書籍も出ているが、著者のサイトから続編にあたる"Writing A Compiler In Go"とセットで買うとちょっと安くなるのでいい機会だと思って英語版を入手。Interpreterを読み終わったらCompilerも読む予定。

interpreterbook.com

www.oreilly.co.jp

なぜコンパイラ

足掛け10年ほどコードを書いているが、自分の書いたコードがどうやってバイナリになって実行されているのかの知識が欠落しているという自覚があったため。学生の頃に近しい内容の講義を取ったような記憶もあるが、試験終了と共にさっぱり忘れてしまった。

最初は、Rui Ueyama氏のWeb本(低レイヤを知りたい人のための Cコンパイラ作成入門)とGitHubを参考にCコンパイラを書いていた。ただ、最近C言語書いてなくてコーディング速度が上がらないし、メモリを手動確保する関係で油断するとSEGVするしでだんだんつらくなってきて、結局四則演算ができたところで断念した。最後までやりきれば相当力がつくと思うが…

どんな本なのか

Monkeyという架空の言語で書かれたソースコードを解析し、実行するインタプリタを作成する。架空の言語といっても、変数宣言や制御構文などプログラミング言語として一通りの機能は揃っている模様。

// Bind values to names with let-statements
let version = 1;
let name = "Monkey programming language";
let myArray = [1, 2, 3, 4, 5];
let coolBooleanLiteral = true;

// Use expressions to produce values
let awesomeValue = (10 / 2) * 5 + 30;
let arrayWithValues = [1 + 1, 2 * 2, 3];

// Define a `fibonacci` function
let fibonacci = fn(x) {
  if (x == 0) {
    0                // Monkey supports implicit returning of values
  } else {
    if (x == 1) {
      return 1;      // ... and explicit return statements
    } else {
      fibonacci(x - 1) + fibonacci(x - 2); // Recursion! Yay!
    }
  }
};

https://interpreterbook.com/#the-monkey-programming-language より

モチベーション維持のために、その日取り組んだ内容のメモを書くことにする。書いたコードはGitHubに置く。

github.com

CircleCIでflutter testする

Qiitaからの移転記事です https://qiita.com/nibral/items/93e4052b41c2f477ae2a

ポイント

設定例

version: 2
jobs:
  build:
    environment:
      - LANG: en_US.UTF-8
    docker:
      - image: circleci/android:api-27-alpha
    steps:
      - checkout
      - run:
          name: Install Flutter SDK
          command: git clone -b beta https://github.com/flutter/flutter.git ~/flutter
      - run:
          name: run tests
          command: ~/flutter/bin/flutter test

Arch LinuxにChrome Remote Desktopをインストールする

Qiitaからの移転記事です https://qiita.com/nibral/items/ee82e244117f487a1032

Arch LinuxChrome Remote Desktopを入れたときのメモ。

環境

  • デスクトップ環境はMATE、日本語入力はfcitx-mozc
  • Google Chromeインストール済(yaourt -S google-chrome)

Chrome Remote Desktopのインストール

AURのパッケージをインストール

$ yaourt -S chrome-remote-desktop

初期設定

$ crd --setup
  • 作業中のユーザがchrome-remote-desktopグループに追加される
  • セッションの設定と画面解像度の設定で都合2回nanoが立ち上がるので、必要な編集をしたあとCtrl+XYで保存する。
    • セッションの設定では# exec mate-session#を消す。
    • 解像度の設定では好みの解像度を入力する。(コメントは書けないらしい)

Chrome Remote Desktopの有効化

Chromeウェブストアから「Chromeリモートデスクトップ」をインストールし、設定画面を開いて「リモート接続を有効にする」をクリック。PINを設定する。

セッション設定の追加

この段階でリモート接続してみると、Failed to connect to socket /tmp/dbus-**********: Connection refusedというエラーが出て画面が表示されない。MATEがdbusを認識できないのが原因なので、セッションの設定を追加する必要がある。ついでに日本語入力も使えるようにする。

$ vim ~/.chrome-remote-desktop-session
# You will have to...
# Remove...
#
eval $(dbus-launch --sh-syntax) ←ここから
export GTK_IM_MODULE=fcitx 
export QT_IM_MODULE=fcitx
export XMODIFIERS="@im=fcitx" ←ここまで追記
exec mate-session

サービス有効化&起動

systemdのサービスを有効化して起動する。

$ systemctl --user enable chrome-remote-desktop
$ systemctl --user start chrome-remote-desktop

ユーザがログアウトした状態でもサービスを起動したままにするため、lingerを設定。

$ loginctl enable-linger <ユーザ名>

動作確認

別のマシンにもChromeウェブストアからChromeリモートデスクトップをインストールして、設定したマシンに接続してみる。サーバ側のサービス起動直後は「サーバが応答しない」というエラーが出ることがあるので、1分ほど待ってから接続先マシンのリストを更新するといいかもしれない。

あと、VirtualBox上のマシンにChrome Remote Desktopをインストールした場合で、マウスカーソルが出ない時はVirtualBoxのマウス統合をオフにする。

参考

Amazon LightSailでお手軽にNode.jsを走らせてみた

Qiitaからの移転記事です https://qiita.com/drafts/6165498aa451bb8dc6a9/edit

はじめに

AWS re:Invent 2016でAmazon LightSailが発表された。 $5/monthからVPSが使えるとのことで、早速インスタンスを立ててNode.jsでHello,worldしてみた記録。

Let's get started

マネジメントコンソールを開き、サービスからLightsailをクリック

スクリーンショット_2016-12-01_9_19_25.png

新キャラ登場。Let's get startedをクリックすると、そのままLightsailインスタンスの新規作成画面が開く。

スクリーンショット 2016-12-01 9.19.48.png

イメージ選択

まずは作成するインスタンスのイメージ選択。各種アプリケーションがインストール済みの「Apps + OS」とOSのみの「Base OS」がある。WordPressLAMPスタックといったWeb環境から、GitLabやRedmineなどの管理系も用意されている。デフォルトではWordPressが選択されているが、今回はNode.jsでやってみる。

スクリーンショット 2016-12-01 9.22.26.png

ちなみに、公式ブログ(Amazon Lightsail – AWSの力、VPSの簡単さ)には

好きなオペレーティングシステム(Amazon Linux AMI,Ubuntu,CentOS,FreeBSD,Debian)、開発環境(LAMP, LEMP, MEAN,Node.js)やアプリケーション(Drupal,Joomla,Redmine,GitLabなど)を立ち上げることができます。

とあるが、現時点ではBase OSを選ぶとAmazon LinuxUbuntuしか選べない。慣れの問題があるので、Cent OSが使えるようになるとうれしいところ。

スクリーンショット 2016-12-01 9.26.30.png

料金プラン

続いて料金プランの選択。スペック的には安い方からt2.nano、t2.micro、small、medium、largeといった感じ。$5/monthのプランは最初の一ヶ月(750時間)が無料になるので、とりあえず使い勝手を試すならこのプランでいいと思う。 リージョンはいまのところバージニア(us-east-1)しかないのでそのまま。

スクリーンショット 2016-12-01 9.27.02.png

インスタンス作成

最後にインスタンスの名前と数量を選ぶ。デフォルトで「<イメージ名>-<スペック>-<リージョン>」な感じの名前が入力されているので、そのままCreate。

スクリーンショット 2016-12-01 9.29.16.png

リソース一覧画面

インスタンスの作成が始まると自動的に飛ばされる。インスタンスの管理や新しいインスタンスの追加はこの画面から行う。

スクリーンショット_2016-12-01_9_29_48.png

インスタンスが動き出すと左下のステータスがPendingからRunningになって、アイコンに色がつく。Createを押してからRunningになるまでの時間は10秒くらい。

スクリーンショット_2016-12-01_9_31_08.png

インスタンス詳細画面

リソース一覧でインスタンス名をクリックすると、インスタンスの詳細が出てくる。インスタンスの停止・再起動、ブラウザ経由のコンソールなどVPSによくある機能は一通り揃っている感じ。この時点でグローバルIPが割り当てられているので、インターネットからアクセスできる。

スクリーンショット_2016-12-01_9_32_05.png

ファイヤウォールはデフォルトでSSH(22)・HTTP(80)・HTTPS(443)が開いていた。この後Node.jsを動かすので、追加で3000番も開けておく。

スクリーンショット 2016-12-01 9.51.46.png

ブラウザコンソール

インスタンス詳細画面の「Connect using SSH」というオレンジのボタンを押すと、別ウィンドウでコンソールが開く。

Node.jsのイメージではUbuntu 14.04.5 LTSが使われていた。基本的にAmazon Linuxだと思っていたのでちょっと意外。あと、OSのタイムゾーンGMTになっているのでログとか漁るときは意識したほうがいいかもしれない。 スクリーンショット_2016-12-01_9_39_46.png

太平洋の反対側なのでラグはそれなりにあるが、いきなり反応しなくなってコマンド入力にストレスが溜まるといったことはなかった。ログインしているユーザでsudoも使える。ただ、コンソールの文字が妙に大きく、しかもブラウザの縮小表示が効かないのでちょっとしたメンテナンス以上のことをやるならSSHクライアントを使ったほうが良い。

Hello, LightSail

あとはサーバを書いて、node hello.jsで動かす。

const http = require('http');
http.createServer((req, res) => {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('hello, lightsail\n');
}).listen(3000);
console.log('Server running at http://localhost:3000/');
````

ブラウザで`http://<インスタンスのグローバルIP>:3000`を開くと、ちゃんと見える。

![スクリーンショット 2016-12-01 9.54.19.png](https://qiita-image-store.s3.amazonaws.com/0/116906/575f31d2-2407-c7a9-fcba-927d1409a82b.png)

まとめ
----
* EC2だと、ちょっとした環境立てるだけでもインスタンス立てて、Elastic IP貼って、セキュリティグループ設定して…という感じでやることが多い
* 売り文句通り、LightSailはそのあたりの細かいことを自動でいい感じにやってくれる
* 開発とかお遊びでちょっとインターネットから見えるサーバが欲しいときにはいいのではないか(もちろんセキュリティの設定は必要だけど)
* ap-northeast-1にも来て欲しい
  * (2017/5/31追記)[東京リージョンでもLightsailが使えるようになりました](https://aws.amazon.com/jp/blogs/news/amazon-lightsail-tokyo-region-launch/)

Amazon LightSailでお手軽にNode.jsを走らせてみた

Qiitaからの移転記事です https://qiita.com/drafts/6165498aa451bb8dc6a9

はじめに

AWS re:Invent 2016でAmazon LightSailが発表された。 $5/monthからVPSが使えるとのことで、早速インスタンスを立ててNode.jsでHello,worldしてみた記録。

Let's get started

マネジメントコンソールを開き、サービスからLightsailをクリック

スクリーンショット_2016-12-01_9_19_25.png

新キャラ登場。Let's get startedをクリックすると、そのままLightsailインスタンスの新規作成画面が開く。

スクリーンショット 2016-12-01 9.19.48.png

イメージ選択

まずは作成するインスタンスのイメージ選択。各種アプリケーションがインストール済みの「Apps + OS」とOSのみの「Base OS」がある。WordPressLAMPスタックといったWeb環境から、GitLabやRedmineなどの管理系も用意されている。デフォルトではWordPressが選択されているが、今回はNode.jsでやってみる。

スクリーンショット 2016-12-01 9.22.26.png

ちなみに、公式ブログ(Amazon Lightsail – AWSの力、VPSの簡単さ)には

好きなオペレーティングシステム(Amazon Linux AMI,Ubuntu,CentOS,FreeBSD,Debian)、開発環境(LAMP, LEMP, MEAN,Node.js)やアプリケーション(Drupal,Joomla,Redmine,GitLabなど)を立ち上げることができます。

とあるが、現時点ではBase OSを選ぶとAmazon LinuxUbuntuしか選べない。慣れの問題があるので、Cent OSが使えるようになるとうれしいところ。

スクリーンショット 2016-12-01 9.26.30.png

料金プラン

続いて料金プランの選択。スペック的には安い方からt2.nano、t2.micro、small、medium、largeといった感じ。$5/monthのプランは最初の一ヶ月(750時間)が無料になるので、とりあえず使い勝手を試すならこのプランでいいと思う。 リージョンはいまのところバージニア(us-east-1)しかないのでそのまま。

スクリーンショット 2016-12-01 9.27.02.png

インスタンス作成

最後にインスタンスの名前と数量を選ぶ。デフォルトで「<イメージ名>-<スペック>-<リージョン>」な感じの名前が入力されているので、そのままCreate。

スクリーンショット 2016-12-01 9.29.16.png

リソース一覧画面

インスタンスの作成が始まると自動的に飛ばされる。インスタンスの管理や新しいインスタンスの追加はこの画面から行う。

スクリーンショット_2016-12-01_9_29_48.png

インスタンスが動き出すと左下のステータスがPendingからRunningになって、アイコンに色がつく。Createを押してからRunningになるまでの時間は10秒くらい。

スクリーンショット_2016-12-01_9_31_08.png

インスタンス詳細画面

リソース一覧でインスタンス名をクリックすると、インスタンスの詳細が出てくる。インスタンスの停止・再起動、ブラウザ経由のコンソールなどVPSによくある機能は一通り揃っている感じ。この時点でグローバルIPが割り当てられているので、インターネットからアクセスできる。

スクリーンショット_2016-12-01_9_32_05.png

ファイヤウォールはデフォルトでSSH(22)・HTTP(80)・HTTPS(443)が開いていた。この後Node.jsを動かすので、追加で3000番も開けておく。

スクリーンショット 2016-12-01 9.51.46.png

ブラウザコンソール

インスタンス詳細画面の「Connect using SSH」というオレンジのボタンを押すと、別ウィンドウでコンソールが開く。

Node.jsのイメージではUbuntu 14.04.5 LTSが使われていた。基本的にAmazon Linuxだと思っていたのでちょっと意外。あと、OSのタイムゾーンGMTになっているのでログとか漁るときは意識したほうがいいかもしれない。 スクリーンショット_2016-12-01_9_39_46.png

太平洋の反対側なのでラグはそれなりにあるが、いきなり反応しなくなってコマンド入力にストレスが溜まるといったことはなかった。ログインしているユーザでsudoも使える。ただ、コンソールの文字が妙に大きく、しかもブラウザの縮小表示が効かないのでちょっとしたメンテナンス以上のことをやるならSSHクライアントを使ったほうが良い。

Hello, LightSail

あとはサーバを書いて、node hello.jsで動かす。

const http = require('http');
http.createServer((req, res) => {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('hello, lightsail\n');
}).listen(3000);
console.log('Server running at http://localhost:3000/');
````

ブラウザで`http://<インスタンスのグローバルIP>:3000`を開くと、ちゃんと見える。

![スクリーンショット 2016-12-01 9.54.19.png](https://qiita-image-store.s3.amazonaws.com/0/116906/575f31d2-2407-c7a9-fcba-927d1409a82b.png)

まとめ
----
* EC2だと、ちょっとした環境立てるだけでもインスタンス立てて、Elastic IP貼って、セキュリティグループ設定して…という感じでやることが多い
* 売り文句通り、LightSailはそのあたりの細かいことを自動でいい感じにやってくれる
* 開発とかお遊びでちょっとインターネットから見えるサーバが欲しいときにはいいのではないか(もちろんセキュリティの設定は必要だけど)
* ap-northeast-1にも来て欲しい
  * (2017/5/31追記)[東京リージョンでもLightsailが使えるようになりました](https://aws.amazon.com/jp/blogs/news/amazon-lightsail-tokyo-region-launch/)

CentOS 7にMaria DBとPHP 7.0を入れる

Qiitaからの移転記事です https://qiita.com/nibral/items/b3bd01672bcbfe80054f

環境

MariaDB

yumリポジトリを追加

[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.1/centos7-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1

インストール

$ sudo yum install MariaDB-server MariaDB-client

サービスを有効化&起動

$ sudo systemctl enable mariadb
$ sudo systemctl start mariadb

最小構成の設定ファイルをコピーして、文字コードを設定

$ sudo cp -p /usr/share/mysql/my-small.cnf /etc/my.cnf.d/server.cnf
[client]
default-character-set = utf8

[mysqld]
character-set-server = utf8

初期設定

$ sudo /usr/bin/mysql_secure_installation

PHP 7.0

yumリポジトリにepelとremiを追加

$ sudo yum -y install epel-release
$ wget http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
$ sudo rpm -ivh ./remi-release-7.rpm

インストール

$ sudo yum install --enablerepo=remi,remi-php70 php php-devel php-mbstring php-pdo php-gd

MariaDBと接続するドライバのインストール

$ sudo yum install --enablerepo=remi,remi-php70 php-mysqlnd

ドライバを認識してるか確認

$ php -r "phpinfo();" | grep -i PDO
/etc/php.d/20-pdo.ini,
/etc/php.d/30-pdo_mysql.ini,
/etc/php.d/30-pdo_sqlite.ini
API Extensions => mysqli,pdo_mysql
PDO
PDO support => enabled
PDO drivers => mysql, sqlite
pdo_mysql
PDO Driver for MySQL => enabled
pdo_mysql.default_socket => /var/lib/mysql/mysql.sock => /var/lib/mysql/mysql.sock
pdo_sqlite
PDO Driver for SQLite 3.x => enabled