Created
December 22, 2016 13:05
-
-
Save ptr-yudai/a732c1218c9e8c551b62d63872fc8a9f to your computer and use it in GitHub Desktop.
きつねさんとLLVMで作った電卓
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
%{ ...%}で囲まれた行はC言語そのままとして解釈されます。 | |
したがって、%{ ... %}内は解析時にスキップされ、 | |
コンパイル時にコンパイラが読むだけです。 | |
*/ | |
%{ | |
/* stdio.hはキーボードからの入力に必要です。 */ | |
#include <stdio.h> | |
/* "y.tab.h"はyaccコマンド実行時に出力されます。 */ | |
#include "y.tab.h" | |
/* 必要な関数だけど使わないので空 */ | |
int yywrap(void) { return 1; } | |
%} | |
/* | |
%% ... %%で囲まれた行はlexに解析されます。 | |
ここには自分の作りたいプログラム言語の規則を書きます。 | |
*/ | |
%% | |
/**** 次のようにyaccでは「演算子」を定義します。 | |
「足し算」とは... | |
"+" : "+"である | |
詳細な計算方法はcalc.yで定義します。 | |
*/ | |
"+" { | |
/* ここには上の定義のもと「足し算」と判断された場合の処理を書きます */ | |
return OPERATOR_ADD; | |
} | |
"-" { | |
return OPERATOR_SUB; | |
} | |
/* returnだけの場合、次のように{}を省略できます。 */ | |
"*" return OPERATOR_MUL; | |
"/" return OPERATOR_DIV; | |
"\n" return EOL; | |
/**** 次のようにyaccでは「整数とは」を定義する必要があります。 | |
整数とは... | |
[1~9] --> 文字の1~9が最初にある | |
[0-9]* --> 文字の0~9が続く('*'が繰り返しを意味する) | |
*/ | |
[1-9][0-9]* { | |
/*** ここには上の定義のもと「整数」と判断された場合の処理を書きます。 */ | |
double temp_box; | |
/* 今回は実数しか使わないので実数として読み込みます */ | |
/* 現在の文字(yytext)を整数(%d)としてtemp_boxに入力します。 */ | |
sscanf(yytext, "%lf", &temp_box); | |
/* 現在のint_valueは上で取得した整数です。 */ | |
yylval.double_value = temp_box; | |
return DOUBLE_LITERAL; | |
} | |
/* 上の定義だと0が整数にならないので定義します。 */ | |
"0" { | |
yylval.double_value = 0.0; // "0"は0.0 | |
return DOUBLE_LITERAL; | |
} | |
/**** 実数とは... | |
[0-9]* --> 文字の0~9が続く | |
\. --> 文字の"."がある('.'は正規表現で意味を持つので'\.'と書きます) | |
[0-9]* --> 文字の0~9が続く | |
*/ | |
[0-9]*\.[0-9]* { | |
/*** ここには上の定義のもと「実数」と判断された場合の処理を書きます。 */ | |
double temp_box; | |
/* 現在の文字(yytext)を実数(%lf)としてtemp_boxに入力します。 */ | |
sscanf(yytext, "%lf", &temp_box); | |
/* 現在のdouble_valueは上で取得した実数です。 */ | |
yylval.double_value = temp_box; | |
return DOUBLE_LITERAL; | |
} | |
/* | |
DOUBLE_LITERALやOPERATOR_ADDなどの定数は | |
calc.yの方で定義するので、それと同じ名前であれば | |
何でも構いません。 | |
yytextやyylvalもcalc.yの方で定義します。 | |
yylval内のdouble_valueなどもcalc.yで定義します。 | |
*/ | |
%% |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
%{ ...%}で囲まれた行はC言語そのままとして解釈されます。 | |
したがって、%{ ... %}内は解析時にスキップされ、 | |
コンパイル時にコンパイラが読むだけです。 | |
*/ | |
%{ | |
#include <stdio.h> | |
#include <stdlib.h> | |
%} | |
// 現在解析中の具体的な値が入ります | |
%union { | |
// プログラミング言語などでは型がたくさんあるので | |
// 構造体などをここに入れますが、 | |
// 今回は実数しか使わないようにしました。 | |
double double_value; | |
} | |
%token <int_value> INT_LITERAL // 例えばcalc.lで整数と判定されたら、 | |
%token <double_value> DOUBLE_LITERAL // INT_LITERALを受け取ることになります。 | |
%token OPERATOR_ADD // 演算子にも名前を付けておきます。 | |
OPERATOR_SUB // これらもcalc.lで使う用なので、 | |
OPERATOR_MUL // ここでは宣言だけをしておきます。 | |
OPERATOR_DIV | |
EOL // 行の終端すら定義します | |
// | |
// %type <int_value> | |
%type <double_value> expression term primary_expression | |
/* | |
%% ... %%で囲まれた行はlexに解析されます。 | |
ここには自分の作りたいプログラム言語の規則を書きます。 | |
*/ | |
%% | |
//// 次のようにyaccでは「ソースコードとは」すら定義する必要があります | |
// 「ソースコード」とは... | |
// line --> 1行 | |
// | --> または | |
// source_code line --> 「ソースコード」「1行」で構成される | |
source_code | |
: line | |
| source_code line | |
; | |
//// 次のようにyaccでは「行とは」すら定義する必要があります | |
// 「行」とは... | |
// expression --> 何かしらの式がある | |
// EOL --> 改行文字がある | |
line | |
: expression EOL | |
{ | |
//// ここには「行である」であると判定された際の処理を書きます。 | |
// 行判定されたら解析結果($1)を出力します。 | |
printf(">> %lf\n", $1); | |
} | |
; | |
//// 上で使った「何かしらの式」(expression)を定義します | |
// 「何かしらの式」(expression)とは... | |
// term --> 項である | |
// | --> または | |
// expression OPERATOR_ADD term --> 「何かしらの式」 + 項 である | |
// | --> または | |
// expression OPERATOR_SUB term --> 「何かしらの式」 - 項 である | |
expression | |
: term | |
| expression OPERATOR_ADD term | |
{ | |
//// ここには入力文字が「expression + term」であると判定された際の処理を書きます。 | |
// $1 : expression | |
// $2 : OPERATOR_ADD | |
// $3 : term | |
// 結果 = expression + term | |
$$ = $1 + $3; | |
} | |
| expression OPERATOR_SUB term | |
{ | |
$$ = $1 - $3; | |
} | |
; | |
//// もちろん「項」も定義します。 | |
// 「項」(term)とは... | |
// primary_expression --> 「1つの項」である | |
// | --> または | |
// term OPERATOR_MUL primary_expression --> 「項」 * 「1つの項」である | |
// | --> または | |
// term OPERATOR_DIV primary_expression --> 「項」 / 「1つの項」である | |
term | |
: primary_expression | |
| term OPERATOR_MUL primary_expression | |
{ | |
$$ = $1 * $3; | |
} | |
| term OPERATOR_DIV primary_expression | |
{ | |
$$ = $1 / $3; | |
} | |
; | |
// 「1つの項」(primary_expression)とは... | |
// DOUBLE_LITERAL --> 実数である | |
primary_expression | |
: DOUBLE_LITERAL | |
; | |
%% | |
/* | |
ここにコンパイルエラー発生時の処理を書きます。 | |
*/ | |
int yyerror(char const* str) | |
{ | |
extern char *yytext; | |
fprintf(stderr, "Error near %s\n", yytext); | |
return 0; | |
} | |
/* | |
ここにコンパイラを書きます。 | |
今回は電卓なので、標準入力から文字を受け取って計算結果を出力します。 | |
*/ | |
int main(void) | |
{ | |
// yaccが勝手に解析してくれる関数 | |
extern int yyparse(void); | |
// コンパイルすべきファイル | |
extern FILE *yyin; | |
// 今回はファイルではなくstdinからの入力 | |
yyin = stdin; | |
if (yyparse()) { | |
// エラー発生 | |
fprintf(stderr, "ERROR!\n"); | |
exit(1); | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# calc.binを出力するコマンド | |
calc.bin: y.tab.c lex.yy.c # 下のコマンドに必要なファイル | |
cc -o calc.bin y.tab.c lex.yy.c | |
# y.tab.cを出力するコマンド | |
y.tab.c: calc.y # 下のコマンドに必要なファイル | |
yacc -dv calc.y | |
# lex.yy.cを出力するコマンド | |
lex.yy.c: calc.l # 下のコマンドに必要なファイル | |
lex calc.l | |
# ゴミを削除 | |
clean: | |
rm y.output y.tab.c y.tab.h | |
rm lex.yy.c |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment