Hello, Lrama!
手を動かして振り返る RubyKaigi 2023
2023-05-18 ima1zumi
ima1zumi ima1zumi.icon
文字コードが好き
IRB, Reline committer
フィードバックいつでも大募集
パーサーに興味あり
パーサーへのお気持ち
パーサーのパの字もわからない
RubyKaigiで様々なセッションを聞いた
熱気にあてられた
世はまさにパーサー元年
手を動かしてみよう!
間違ってたら教えてください!
Bisonについて知っていたこと
Rubyの依存ライブラリの1つ
Bisonのバージョンが古いとRubyがバージョンアップできない
GNUのマスコットキャラクター?
GNUのマスコットキャラクターはGNU
not Bison
GNU、Bison、Llamaについて調べた
Gnu
https://upload.wikimedia.org/wikipedia/commons/b/b8/Blue_wildebeest.jpg
哺乳綱鯨偶蹄目ウシ科ヌー属
アフリカ大陸南部に生息
Bison
https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/Bison_bison_Wichita_Mountain_Oklahoma.jpg/2560px-Bison_bison_Wichita_Mountain_Oklahoma.jpg
哺乳綱偶蹄目ウシ科バイソン属
森林や草原に生息
Llama
https://upload.wikimedia.org/wikipedia/commons/1/1a/Dos_llamas_en_Bolivia_%28deciembre_2001%29.jpg
鯨偶蹄目ラクダ科ラマ属
南アメリカのアンデス地方に多く生息
険しい山岳地帯
はい
BisonとLrama
パーサージェネレーター
ruby/rubyからBison依存が外れた
LramaがBisonを置き換えた
パーサーとパーサージェネレーターの違い
パーサーをつくるのがパーサージェネレーター
コードを見る
パーサー
Cのコード
これがLramaの生成物、出力
code:c
// snip
/* Identify Bison output, and Bison version. */
/* Bison version string. */
/* Skeleton name. */
/* Pure parsers. */
/* Push parsers. */
/* Pull parsers. */
/* First part of user prologue. */
static int yylex(YYSTYPE *val, YYLTYPE *loc);
static int yyerror(YYLTYPE *loc, const char *str);
// snip
int
yyparse ()
{
/* Lookahead token kind. */
int yychar;
/* The semantic value of the lookahead symbol. */
/* Default value used for initialization, for pacifying older GCCs
or non-GCC compilers. */
YY_INITIAL_VALUE (static YYSTYPE yyval_default;)
YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default);
/* Location data for the lookahead symbol. */
static YYLTYPE yyloc_default
# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
= { 1, 1, 1, 1 }
# endif
;
YYLTYPE yylloc = yyloc_default;
パーサージェネレーター
パーサーのプログラムを生成するためのファイル
構文規則を記述する
これをLramaに食べさせるとCのコードが出力される
lrama -d calc.y -o calc.c && gcc -Wall calc.c -o calc && ./calc
code:c
/*
* How to build and run:
*
* $ lrama -d calc.y -o calc.c && gcc -Wall calc.c -o calc && ./calc
* 1
* => 1
* 1+2*3
* => 7
* (1+2)*3
* => 9
*
*/
%{
static int yylex(YYSTYPE *val, YYLTYPE *loc);
static int yyerror(YYLTYPE *loc, const char *str);
%}
%union {
int val;
}
%token LF
%token <val> NUM
%type <val> expr
%left '+' '-'
%left '*' '/'
%%
list : /* empty */
| list LF
| list expr LF { printf("=> %d\n", $2); }
;
expr : NUM
| expr '+' expr { $$ = $1 + $3; }
| expr '-' expr { $$ = $1 - $3; }
| expr '*' expr { $$ = $1 * $3; }
| expr '/' expr { $$ = $1 / $3; }
| '(' expr ')' { $$ = $2; }
;
%%
static int yylex(YYSTYPE *yylval, YYLTYPE *loc) {
int c = getchar();
int val;
switch (c) {
case ' ': case '\t':
return yylex(yylval, loc);
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
val = c - '0';
while (1) {
c = getchar();
if (isdigit(c)) {
val = val * 10 + (c - '0');
}
else {
ungetc(c, stdin);
break;
}
}
yylval->val = val;
return NUM;
case '\n':
return LF;
case '+': case '-': case '*': case '/': case '(': case ')':
return c;
case EOF:
exit(0);
default:
fprintf(stderr, "unknown character: %c\n", c);
exit(1);
}
}
static int yyerror(YYLTYPE *loc, const char *str) {
fprintf(stderr, "parse error: %s\n", str);
return 0;
}
int main() {
printf("Enter the formula:\n");
yyparse();
return 0;
}
手を動かしてみよう
Bisonの定番サンプルプログラムは電卓
mameさんがすでに作成&マージ済
calc.y
見てもわからない
さらにシンプルなコードを書いてみよう
Hello, Lrama! (ascii.y)
入力文字のASCIIコードを返すecho的なプログラム
本当はechoにしたかったが文字列を返す方法が分からなかった
https://gyazo.com/50f9f68772514271ff83929fbb7d2f9f
C言語のはなし
関数を定義する前にプロトタイプを宣言することが多い
C言語はmain関数からすべてがはじまる
文法ファイル(なんとか.y)はC言語とBison(Lrama)パートが混在
ascii.y
うえ:C言語の宣言部
まんなか1:Bison宣言部
まんなか2:文法規則部
した:C宣言部
うえ
C言語の宣言部
code:ascii.y
%{
#include "ascii.h" // 生成したヘッダファイルのinclude必須 static int yylex(YYSTYPE *val, YYLTYPE *loc); // 宣言必須
static int yyerror(YYLTYPE *loc, const char *str); // 宣言必須
%}
まんなか1
Bisonの宣言
code:ascii.y
%union {
int val;
} // unionの定義は必須
%token <val> NUM // tokenの定義は必須
まんなか2
本当はもっと複雑な文法規則を書くところ
ここでは最小コードを目指したのでprintfするだけ
code:ascii.y
%%
expr : NUM { printf("ASCII: %d", $1);};
%%
C宣言部
main->yyparse->yylex
errorは今回使わないが定義しないと落ちるので書いてる
code:ascii.y
static int yylex(YYSTYPE *yylval, YYLTYPE *loc) {
yylval->val = getchar();
return NUM;
}
static int yyerror(YYLTYPE *loc, const char *str) {
return 0;
}
int main() {
yyparse();
return 0;
}
実行
https://upload.wikimedia.org/wikipedia/commons/1/1a/Dos_llamas_en_Bolivia_%28deciembre_2001%29.jpg