hogashi.*

日記から何から

 小さい頃から、ある曲を好きになるとその曲だけ聞き続ける、という暮らしをしていて、今も変わっていない。特に父親の車に乗って帰省するときには、よくそういうことをしていた。

 小学生くらいまでは、父親の車に乗ったときは必ず布袋寅泰のベスト CD 、それも 1~3曲目だけをひたすら流していた。いつから聞き始めていつから気に入ったのかわからないものの、父親の車の運転席と助手席との間に置かれていた CD が沢山入った箱からすぐにその CD を探し出してプレイヤに入れていた。乗ってすぐ聞く 1曲目である「スリル」はもはや魂に刻まれた曲となっていて、前奏の出だしからきめ細かに脳内で再生できる。芸人やテレビ番組で使われると、一瞬でビビッと来て、耳に馴染んで安心する(この曲で安心???と思われることはよくわかるものの、もうそうなってしまっている。そして前奏が短く編集されていたりするとすぐにわかる)。ちなみに歌詞はちゃんと見て聞いていなかったので完全にうろ覚えで、後々読んでこんな歌詞だったのかと思った覚えがある。この CD は後に父親から「流石に飽きたからやめてくれ」と言われる。

 中学生くらいで、祖母から 70's ベスト盤をもらい、 21曲目(最後の曲)から 1~3曲目をひたすら流していた。特に 21曲目である「Beautiful Sunday」(Daniel Boone) は自分でも驚くほど気に入って、すぐに歌詞全部を諳んじて歌えるようになった。父親からビートルズを教えてもらって聞き始めてからは、買ってもらったアルバム CD を父親の車に持ち込んで、全曲リピートでエンドレスに聞いていた。全曲の歌詞を全部覚える勢いのハマり方だった。父親もビートルズを好きだったようで、ここがかっこいいとか、この曲を学校で流したとか、懐かしむように聞いている様が印象的で、ひとりで聞くよりずっと楽しかったと思う。

 ちなみに、ある曲ひとつ好きだからといって、そのアーティストの曲が全部好きということはなく、むしろその曲しか気に入らなかったということは多い。これは母親譲りであることが、少し前に会話していて判明した。言葉の少ない母親だが、帰省のときに一緒に乗りながら、受け継がれた音楽の好み方を感じていたかもしれない。

カエライフ×はてなブログ 特別お題キャンペーン #ドライブと音楽

カエライフ×はてなブログ 特別お題キャンペーン #ドライブと音楽
by ホンダアクセス

 夢、それなりに知っているらしい女性と現代的な曲でダンスを踊っていた、前半は手を取って回るタイプ、後半は少し離れてステップを踏むタイプで、下手だったけど恥をかくことはなく一曲こなして目が覚めた。分析としては寝る前になぜか M:I 5 とシャーロック・ホームズ シャドウゲームを思い出したりしていたからかもしれないし、好きな漫画の好きなキャラのデフォルメ絵が手を取るダンスだったのを見返したからかもしれない。数年くらい夢見が悪かったのだけど(覚えていないだけかもしれないが、覚えていないのだから夢見が良いとは言わない)、今朝はすこぶるよかった。欲を言えば忙しいことには変わりなかった(例えば逃げる夢とか)ので、もう少しゆったりした夢を見たいし、なんなら自分じゃない人たちを空から眺める何でもない何かで居たい。

冬休みの自由研究(3)

 帰省したりでかけたりしていて全く進んでいなかったので、ロスタイムとして Yacc をちょこっとやった。ちょこっとやっただけで YaccJSON をパースできたので簡単ですごい。 RFC に沿うと、値とかは前後に空白があってもなくてもよいのだけど、それをうまく表現する何かをさっと見つけられなくて、前後の有無で 4パターン書いたりしている。
 これを yacc コマンドにかけると y.tab.c という構文解析器のコードが生成されるので、 main.c とかを作って main 関数内で yyparse() する、という感じでパースできる (Lex ではそれぞれの字句で return L_CBR; みたいなことをすると Yacc が拾ってくれることになっているっぽい)。構文がおかしいときは素朴に syntax error とだけ出力され、正しいときは何も出力されない。

%token L_CBR;
%token R_CBR;
%token L_SBR;
%token R_SBR;
%token COLON;
%token COMMA;
%token STRING;
%token NUM;
%token V_TRUE;
%token V_FALSE;
%token V_NULL;
%token WHITE;
%token CHAR;
%%
text :
  value |
  WHITE value |
  value WHITE |
  WHITE value WHITE
  ;
begin_array :
  L_SBR |
  WHITE L_SBR |
  L_SBR WHITE |
  WHITE L_SBR WHITE
  ;
end_array :
  R_SBR |
  WHITE R_SBR |
  R_SBR WHITE |
  WHITE R_SBR WHITE
  ;
begin_object :
  L_CBR |
  WHITE L_CBR |
  L_CBR WHITE |
  WHITE L_CBR WHITE
  ;
end_object :
  R_CBR |
  WHITE R_CBR |
  R_CBR WHITE |
  WHITE R_CBR WHITE
  ;
name_separator :
  COLON |
  WHITE COLON |
  COLON WHITE |
  WHITE COLON WHITE
  ;
value_separator :
  COMMA |
  WHITE COMMA |
  COMMA WHITE |
  WHITE COMMA WHITE
  ;
value :
  V_FALSE |
  V_NULL |
  V_TRUE |
  object |
  array |
  NUM |
  STRING
  ;
object :
  begin_object end_object |
  begin_object members end_object
  ;
members :
  member |
  members value_separator member
  ;
member :
  STRING name_separator value
  ;
array :
  begin_array end_array |
  begin_array values end_array
  ;
values :
  value |
  values value_separator value
  ;

 大学でやったけど完全に風化していた記憶をちゃんと思い出すという点でも、構文解析という知っていて損のない知識をそれなりに学べたという点でも良かった。
 完全に自分用でおもてなし皆無だけど、やった内容はこのリポジトリにまとめてある: GitHub - hogashi/sketch-lang-parser: 構文解析の練習

冬休みの自由研究(2)

 昨日は大掃除をして疲れ果てたので The LEX & YACC Pageプログラミング言語を作る/yaccとlex などを読んでなるほど〜となっていた。 Lex の処理部分を改行しつつ書くと、正規表現にマッチしたら何かの処理をするという書き方が AWK に似ているということに気づいてひとりで納得したりした。
 今日は JSON でパースの練習をするために、そのさきがけとしてまず JSON の字句解析をできる君を Lex で書いていた。 JSON の仕様は RFC8259 を斜め読みしている。 Strings の定義が以外と単純で、そうなのか、という感じだった。 Lex で書くのも楽。 Numbers は完全に忘れていたけど 1e3 みたいな表記もできたのだった。

unescaped [ !#-\[\]-􏿿]
escaped \\["\\/bfnrt]
escapedunicode \\u[0-9a-fA-F]{4}
minus -
plus \+
dot \.
napiere [eE]
digit19 [1-9]
digit [0-9]
%%
\{ { printf("L_CBR\n"); }
\} { printf("R_CBR\n"); }
\[ { printf("L_SBR\n"); }
\] { printf("R_SBR\n"); }
: { printf("COLON\n"); }
, { printf("COMMA\n"); }
\"({unescaped}|{escaped}|{escapedunicode})*\" { printf("STRING\n"); }
{minus}?(0|{digit19}{digit}*)({dot}{digit}+)?({napiere}({minus}|{plus})?{digit}+)? { printf("NUM\n"); }
true { printf("V_TRUE\n"); }
false { printf("V_FALSE\n"); }
null { printf("V_NULL\n"); }
" " { printf("WHITE\n"); }
. { printf("CHAR\n"); }

 余談だけど U+10FFFF は手元で上手く出せなくて U+10FFFF Unicode Character からコピペした (UTF8 では 0x10FFFF なんて無理で 0xF4 0x8F 0xBF 0xBF とかになるという話だった……)。

f:id:hogashi:20191230220110p:plain
0x10FFFFは大きい閉じ中括弧らしいことが端末で偶然わかった様子

 この lex ファイルを lex コマンドにかけて gcc コマンドにかけて、とやって得た実行ファイルを実行してやると、入力した JSON の文字を順番に読んでいって字句を解析する様子が見られる。改行を.でマッチできていないのかよくわからないけど、 JSON ファイルに改行があるとそのまま出力にも改行で入ってしまう。壊れた JSON を渡すと、全く予期していないところで文字列が終わったりすることになるので、急に CHAR とかにフォールバック(?)して面白い。

$ cat in2.json 
{
  "クオートMAX": "\\s\\\"\\\"\\\"\\\\\"",
  "any array": [null, { "str": 0e2 }]
}
$ ./ajson1.out < in2.json 
L_CBR

WHITE
WHITE
STRING
COLON
WHITE
STRING
COMMA

WHITE
WHITE
STRING
COLON
WHITE
L_SBR
V_NULL
COMMA
WHITE
L_CBR
WHITE
STRING
COLON
WHITE
NUM
WHITE
R_CBR
R_SBR

R_CBR

 あと、大学でやった実験のテキストの PDF を見つけてきて読みつつ、確かに定義部とかがあって定義しておけたのだった、とか、 Yacc のやることは Lex ができない括弧の対応とか入れ子になっていてとかそういう表現をカバーすることだったな、とか、 Lex と Yacc それぞれを実行して出てくる C言語のファイル lex.yy.c と y.tab.c をそのまま include して使うことができるのだった、とか色々思い出していた。
 ちなみに Lex で頑張ってやった Strings や Numbers のつくりは、おそらく字句解析では部品だけにしておいて構文解析でカバーするという手もありそうで、そこは好きに境界を動かしてよさそう。今の感覚では正規表現でできるならできるだけやってしまったほうが BNF が単純になっていいのかな〜という感じ。 Yacc で定義すべき BNF のイメージはなんとなくついたし、そもそも RFC があるのでやはりそこに載っている通りにつくればできるはずなので、続きをやりたい。

(追記 201912302215) 改行は . でマッチしないの仕様ですと教えてもらえたのと、そういえば RFC にちゃんと空白系はまとめてあったのでちゃんとやるとこうなった:

unescaped [ !#-\[\]-􏿿]
escaped \\["\\/bfnrt]
escapedunicode \\u[0-9a-fA-F]{4}
minus -
plus \+
dot \.
napiere [eE]
digit19 [1-9]
digit [0-9]
%%
\{ { printf("L_CBR\n"); }
\} { printf("R_CBR\n"); }
\[ { printf("L_SBR\n"); }
\] { printf("R_SBR\n"); }
: { printf("COLON\n"); }
, { printf("COMMA\n"); }
\"({unescaped}|{escaped}|{escapedunicode})*\" { printf("STRING\n"); }
{minus}?(0|{digit19}{digit}*)({dot}{digit}+)?({napiere}({minus}|{plus})?{digit}+)? { printf("NUM\n"); }
true { printf("V_TRUE\n"); }
false { printf("V_FALSE\n"); }
null { printf("V_NULL\n"); }
[ \t\n\r]+ { printf("WHITE\n"); }
. { printf("CHAR\n"); }
$ ./ajson1.out < in2.json
L_CBR
WHITE
STRING
COLON
WHITE
STRING
COMMA
WHITE
STRING
COLON
WHITE
L_SBR
V_NULL
COMMA
WHITE
L_CBR
WHITE
STRING
COLON
WHITE
NUM
WHITE
R_CBR
R_SBR
WHITE
R_CBR
WHITE

冬休みの自由研究始めた

 雑談で構文解析面白いよ〜と教えてもらったのでやってみるかというので冬休みの自由研究している。大学で lex と yacc で自作言語を作る実験をやったことはあるものの、名前とコードの見た目以外全部忘れていて、手の付け方もわからなかったので、今日はひとまず lex から勉強し始めた。

 lex というのは、 Plan 9 /sys/man/1/lex を見ると「generator of lexical analysis programs」 だそうで、日本語を探すと字句解析プログラム生成器という感じ。一緒に例が書いてあり、 lex だけでお試しできるいいやつだったのでお試しした。

ex1.lex

%%
[A-Z] putchar(yytext[0]+'a'-'A');
[ ]+$
[ ]+  putchar(' ');

 入力文字列を見つつ、上から順にパターンにマッチするか見ていって、マッチしたらそのときの処理をして次に進む、という流れ。例では、大文字アルファベットは1文字ずつ小文字にし、末尾空白は消し、空白が続いていたら1つにする、という意味。これは lex ファイルで、これを lex コマンドに渡すと C言語のプログラムを書いてくれる。そのプログラムを gcc コマンドでコンパイルして、実行しつつ文字列を入力すると、 lex ファイルに書いたとおりに文字列が出力される。

$ cat in1.txt 
abcABC
123
0A89FE44    
A  B   C
$ ./aex1.out < in1.txt 
abcabc
123
0a89fe44
a b c

 普段こういうことをしたいときは、 cat in1.txt | perl -pe 's/ +$//g' | perl -pe 's/ +/ /g' とかやっているので、ちゃんとコードとして書けるのは面白いな〜という感想。 C言語なので 'a'-'A' で小文字にする、のような処理も書けて便利。何をするにも三日坊主になりがちなので続くかわからないけど、ゆっくりやっていけると楽しそう。