トップ 追記

Route 477

過去の日記


2016-07-19

[zsh] ^[ h でmanを表示する(run-help)のをやめる

ergodox ezを使い始めてから、なんか変なタイミングでmanが表示される事故が起きるようになった。たぶん何らかのショートカットを誤入力してるんだろうなぁとは思ってたけど、http://syohex.hatenablog.com/entry/20121201/1354348504 によると、zshの機能だったようだ。

bindkeyコマンドで一覧を確認すると、^[ hがrun-helpに割り当てられている。.zshrcに以下を足して解決。

bindkey -r '^[h'

2016-07-15

[junk] 安いトイレットペーパーを買って失敗した話

http://nlab.itmedia.co.jp/nl/articles/1607/03/news019.html

うちのアパートがまさにこのタイプなんだけど、このタイプのトイレットペーパーホルダーには思わぬ罠があって、トイレットペーパーを適当に買うと、幅が足らなくて微妙に引っかからないことがあるのだ。結果として、紙を引き出そうとするたびにトイレットペーパーが勢い良く飛び出してくるというユーザ・エクスペリエンスを体験する羽目になる。

てかこんなもんJIS規格で統一しといてくれよと思ったのだが、なんと実際に114mm±2mmという規定があるらしい。こないだ買ったやつ、107mmなんだけどどうなってるんだ。JISマーク付いてないからいいのか。そうか。

[scheme] Fomentを読む(4)

前回からの続き。前回はsyntax-rulesを構成していそうな関数をリストアップしたのだった。

関数一覧

一覧を再掲する。ただし引数がFObjectであるものは型名を省略した。

static FObject LiteralFind(se, list, obj)
static FObject CopyLiterals(se, obj, ellip)
static FObject PatternVariableFind(se, list, var)
static FObject CompilePatternVariables(se, form, lits, pat, ellip, pvars, int_t rd)
static void AssignVariableIndexes(pvars, int_t idx)
static int_t CountPatternsAfterRepeat(pat)
static FObject RepeatPatternVariables(se, pvars, pat, rvars)
static FObject CompilePattern(se, lits, pvars, ellip, pat)
static int_t ListFind(list, obj)
static int_t AddVarToTemplateRepeat(var, trs)
static FObject CompileTemplate(se, form, pvars, ellip, tpl, trs, int_t qea)
static FObject CompileRule(se, form, lits, rule, ellip)
FObject CompileSyntaxRules(se, obj)
static void InitRepeatVariables(vars, vals[], rvals[])
static void GatherRepeatVariables(vars, vals[], rvals[])
static int_t MatchPattern(se, cpat, vals[], expr)
static int_t CheckRepeatVariables(vars, vals[], expr)
static void SetRepeatVariables(vars, vals[], rvals[])
static FObject ExpandTemplateRepeat(tse, use, ctpl, int_t nv, vals[], int_t rc, ret, expr)
static FObject ExpandTemplate(tse, use, ctpl, int_t nv, vals[], expr)
FObject ExpandSyntaxRules(se, sr, expr)

よく見るとstaticな関数とそうでないものが存在する。なんだっけ、staticを付けると関数のスコープがこのファイル内だけになるんだっけか。

ExpandSyntaxRulesの他にはCompileSyntaxRulesが、ファイル外にも公開されているようだ。ExpandとCompileはどう違うのだろうか。引数も返り値もFObjectだらけなので、実際に期待されている型がわかりにくいな...。

CompileSyntaxRules, ExpandSyntaxRules

CompileSyntaxRulesは最後の行がこれなので、FSyntaxRulesを返すことが分かる。

return(MakeSyntaxRules(lits, ReverseListModify(nr), se));

ExpandSyntaxRulesの最後の行はこれ。あれ、値を返さないのか?

return(NoValueObject);

いや、途中にもreturnがあった。

        return(ExpandTemplate(MakeSyntacticEnv(AsSyntaxRules(sr)->SyntacticEnv), se,
        ...略

ExpandTemplateの返り値を見てみる。

static FObject ExpandTemplate(FObject tse, FObject use, FObject ctpl, int_t nv, FObject vals[],
    FObject expr)
{
    if (PairP(ctpl))
        return(MakePair(ExpandTemplate(tse, use, First(ctpl), nv, vals, expr),
                ExpandTemplate(tse, use, Rest(ctpl), nv, vals, expr)));

    if (VectorP(ctpl))
        return(ListToVector(ExpandTemplate(tse, use, AsVector(ctpl)->Vector[0], nv, vals, expr)));
...略

最初のreturnを見るとPairを返しているようだが、次のreturnはVectorを返すはず。ああそうか、引数によって違うものが返るのか。Cだけど、Schemeのコードに近いな。

NoValueObject

さっき出てきたNoValueObject、どういうものなのか気になるので調べてみる。foment.hppに定義があった。

#define EndOfFileObject MakeImmediate(3, MiscellaneousTag)
#define EndOfFileObjectP(obj) ((obj) == EndOfFileObject)

#define NoValueObject MakeImmediate(4, MiscellaneousTag)
#define NoValueObjectP(obj) ((obj) == NoValueObject)

#define WantValuesObject MakeImmediate(5, MiscellaneousTag)
#define WantValuesObjectP(obj) ((obj) == WantValuesObject)

MakeImmediateが何をするのかは分からないが、EOFオブジェクトと定義の仕方が同じなので、たぶん他の値と絶対にeqにならない定数値を定義しているのだろう。Gaucheでいう#<undef>相当のものかな?


2016-07-13

[vim][mac] Escapeを押したら日本語入力もオフにしたい

http://atarimae.hatenablog.com/entry/2014/05/21/173856

Google日本語入力の「キャンセル後IMEを無効化」が効かなくて困っていたのだが、Terminal.appをやめてiTerm2にしたら効くようになった。

[scheme] Fomentを読む(3)

前回からの続き。前回はレコード型がstructと対応していることを確認したのだった。

なんとなくデータ構造が把握できたので、次はシーケンス、つまり「何がどの順で実行されるか」の調査に入る。

まずは、syntax-rulesが呼ばれた時に最初に実行される関数を見つけたいが、synrules.cppの冒頭も末尾もどうやら違いそうだ。名前的には、下から二番目のExpandSyntaxRulesがそれっぽい感じがする。

ExpandSyntaxRules

使われている場所をgit grepで確認する。

src/compile.hpp:FObject ExpandSyntaxRules(FObject se, FObject sr, FObject expr);
src/library.cpp:            return(CompileEvalExpr(ExpandSyntaxRules(MakeSyntacticEnv(env), op, Rest(obj)), env,
src/synpass.cpp:            return(SPassExpression(enc, se, ExpandSyntaxRules(se, val, Rest(expr))));
src/synpass.cpp://            return(SPassExpression(enc, se, ExpandSyntaxRules(MakeSyntacticEnv(se), val, Rest(expr))));
src/synpass.cpp:            return(SPassBodyExpression(se, ExpandSyntaxRules(se, val, Rest(expr))));
src/synpass.cpp://            return(SPassBodyExpression(se, ExpandSyntaxRules(MakeSyntacticEnv(se), val,
src/synrules.cpp:FObject ExpandSyntaxRules(FObject se, FObject sr, FObject expr)

library.cppとsynpass.cppで使われているようだ。ふーむ。

SyntaxRulesをExpandしたあとのことについてはあまり興味がないのでここを追ってもしかたないかなと思いつつ、呼び出し元の関数名だけ控えておく。

  • library.cpp
    • static FObject CompileEvalExpr(FObject obj, FObject env, FObject body)
  • synpass.cpp
    • static FObject SPassExpression(FObject enc, FObject se, FObject expr)
    • static FObject SPassBodyExpression(FObject se, FObject expr)

synrules.cpp

ファイル内のエントリポイントが分かったので、次はファイル内の概要を把握しよう。
関数宣言の一覧を作る。

static FObject MakeSyntaxRules(FObject lits, FObject rules, FObject se)
static FObject MakePatternVariable(int_t rd, FObject var)
static FObject MakePatternRepeat(int_t lc, FObject ellip, FObject vars, FObject pat,
static FObject MakeTemplateRepeat(FObject ellip, int_t rc)
static FObject MakeSyntaxRule(int_t nv, FObject vars, FObject pat, FObject tpl)

このへんまでは単なるデータのコンストラクタだろう。したがってこれ以降がsyntax-rulesの本体と思われる。

static FObject LiteralFind(FObject se, FObject list, FObject obj)
static FObject CopyLiterals(FObject se, FObject obj, FObject ellip)
static FObject PatternVariableFind(FObject se, FObject list, FObject var)
static FObject CompilePatternVariables(FObject se, FObject form, FObject lits, FObject pat, FObject ellip, FObject pvars, int_t rd)
static void AssignVariableIndexes(FObject pvars, int_t idx)
static int_t CountPatternsAfterRepeat(FObject pat)
static FObject RepeatPatternVariables(FObject se, FObject pvars, FObject pat, FObject rvars)
static FObject CompilePattern(FObject se, FObject lits, FObject pvars, FObject ellip, FObject pat)
static int_t ListFind(FObject list, FObject obj)
static int_t AddVarToTemplateRepeat(FObject var, FObject trs)
static FObject CompileTemplate(FObject se, FObject form, FObject pvars, FObject ellip, FObject tpl, FObject trs, int_t qea)
static FObject CompileRule(FObject se, FObject form, FObject lits, FObject rule, FObject ellip)
FObject CompileSyntaxRules(FObject se, FObject obj)
static void InitRepeatVariables(FObject vars, FObject vals[], FObject rvals[])
static void GatherRepeatVariables(FObject vars, FObject vals[], FObject rvals[])
static int_t MatchPattern(FObject se, FObject cpat, FObject vals[], FObject expr)
static int_t CheckRepeatVariables(FObject vars, FObject vals[], FObject expr)
static void SetRepeatVariables(FObject vars, FObject vals[], FObject rvals[])
static FObject ExpandTemplateRepeat(FObject tse, FObject use, FObject ctpl, int_t nv, FObject vals[], int_t rc, FObject ret, FObject expr)
static FObject ExpandTemplate(FObject tse, FObject use, FObject ctpl, int_t nv, FObject vals[], FObject expr)
FObject ExpandSyntaxRules(FObject se, FObject sr, FObject expr)

長い!こんなに関数があったのか。でも幸い、めちゃめちゃ長い関数はなかったので、次回は関数同士の呼び出し関係を調べるところからやる(かもしれないし、気分次第で別のことをするかもしれない)。


2016-07-10

[scheme] Fomentを読む(2)

前回からの続き。前回はリビジョンを書くのを忘れたが、現時点のgit masterを手元にダウンロードして読んでいる。

昨日はsyntax-rulesからだいぶ離れてしまったので、またsynrules.cppから見ていこう。

void SetupSyntaxRules()
{
    R.SyntaxRulesRecordType = MakeRecordTypeC("syntax-rules",
            sizeof(SyntaxRulesFieldsC) / sizeof(char *), SyntaxRulesFieldsC);
    R.PatternVariableRecordType = MakeRecordTypeC("pattern-variable",
            sizeof(PatternVariableFieldsC) / sizeof(char *), PatternVariableFieldsC);
    R.PatternRepeatRecordType = MakeRecordTypeC("pattern-repeat",
            sizeof(PatternRepeatFieldsC) / sizeof(char *), PatternRepeatFieldsC);
    R.TemplateRepeatRecordType = MakeRecordTypeC("template-repeat",
            sizeof(TemplateRepeatFieldsC) / sizeof(char *), TemplateRepeatFieldsC);
    R.SyntaxRuleRecordType = MakeRecordTypeC("syntax-rule",
            sizeof(SyntaxRuleFieldsC) / sizeof(char *), SyntaxRuleFieldsC);
}

レコード型

ここでは5つのレコード型を定義している。

  • SyntaxRuleRecordType
  • PatternVariableRecordType
  • PatternRepeatRecordType
  • TemplateRepeatRecordType
  • SyntaxRuleRecordType

型名は分かったが、フィールドの定義はどこにあるのだろう。それぞれSyntaxRulesFieldsCのようなシンボルを渡しているので、これを確認してみよう。

同じファイルを「FieldsC」で検索すると以下が見つかった。

static const char * SyntaxRulesFieldsC[] = {"literals", "rules", "syntactic-env"};
static const char * PatternVariableFieldsC[] = {"repeat-depth", "index", "variable"};
static const char * PatternRepeatFieldsC[] = {"leave-count", "ellipsis", "variables", "pattern",
static const char * TemplateRepeatFieldsC[] = {"ellipsis", "repeat-count", "variables", "template",
    "rest"};
static const char * SyntaxRuleFieldsC[] = {"num-variables", "variables", "pattern", "template"};

整理しておこう。

  • SyntaxRules = (literals, rules, syntactic-env)
  • PatternVariable = (repeat-depth, index, variable)
  • PatternRepeat = (leave-count, ellipsis, variables, pattern)
  • TemplateRepeat = (ellipsis, repeat-count, variables, template, rest)
  • SyntaxRule = (num-variables, variables, pattern, template)

メンバ名から察するに、以下の様な階層構造だと思われる。

  • SyntaxRules = (literals, rules, syntactic-env)
    • SyntaxRule = (num-variables, variables, pattern, template)
      • PatternVariable = (repeat-depth, index, variable)
      • PatternRepeat = (leave-count, ellipsis, variables, pattern)
      • TemplateRepeat = (ellipsis, repeat-count, variables, template, rest)

struct

一方でsynrules.cppでは以下のようなstructも定義されている。これとレコード型はどのような関係なのだろうか?

typedef struct
{
    FRecord Record;
    FObject Literals;
    FObject Rules;
    FObject SyntacticEnv;
} FSyntaxRules;

synrules.cppで定義されているstructは以下。全部のレコード型にstructがあるわけではないようだ。

  • FSyntaxRules
  • FSyntaxRule

FSyntaxRulesの定義の下に、MakeSyntaxRulesという関数がある。これによると、MakeRecordで生成したレコードを、struct FSyntaxRulesで受けられるらしい。

FSyntaxRules * sr = (FSyntaxRules *) MakeRecord(R.SyntaxRulesRecordType);
sr->Literals = lits;
sr->Rules = rules;
sr->SyntacticEnv = se;

synrules.cppをMakeRecordで検索すると、FPatternVariable, FPatternRepeat, FTemplateRepeatという識別子も存在することがわかる。これらの定義はsynrules.cppには無いが、compile.hppにあることが分かった。例えばFTemplateRepeatの定義は以下である。

typedef struct
{
    FRecord Record;
    FObject Ellipsis;
    FObject RepeatCount;
    FObject Variables;
    FObject Template;
    FObject Rest;
} FTemplateRepeat;

一方でTemplateRepeatFieldsCの定義は以下だったので、見比べると、最初のFRecord Record以外はレコード型のフィールド名と対応していることがわかる。

static const char * TemplateRepeatFieldsC[] = {"ellipsis", "repeat-count", "variables", "template",

ということで最初の疑問の答えだが、レコード型の値をMakeRecordで作成すると対応するstructの値が得られる、ということで良さそうだ。


2016-07-09

[scheme] Fomentを読む(1)

syntax-rulesの実装方法について調べるために、FomentというR7RS処理系のソースを読んでみることにした。

せっかくなので、コードリーティングの際に何をしているかを実況のような感じで書いてみる。

ファイル構成

まず最初に、全体のファイル構成を把握する。それぞれのファイルをちらっとだけ見て、何が書いてありそうかをメモしておく。

Fomentの場合はファイル数が少ないので楽だった。

- src/
  - main.cpp
  - base.scm
  - txt2cpp.cpp
  - license.txt

  - コア
    - compile.cpp
    - compile.hpp
    - execute.cpp
    - execute.hpp
    - foment.cpp
    - foment.hpp
    - gc.cpp
    - io.cpp
    - io.hpp
    - library.cpp
    - genpass.cpp
    - midpass.cpp
    - synpass.cpp
    - mini-gmp.c
    - mini-gmp.h
    - synrules.cpp
    - unicode.cpp
    - unicode.hpp
    - unidata.hpp

  - ライブラリ
    - chars.cpp
    - compare.cpp
    - filesys.cpp
    - hcontain.cpp
    - numbers.cpp
    - numbers.hpp
    - pairs.cpp
    - read.cpp
    - strings.cpp
    - syncthrd.cpp
    - syncthrd.hpp
    - vectors.cpp
    - write.cpp
- test/
  - テスト
- unix/
  - makefile
- windows/
  - makefile等
- LICENSE
- README.md

src以下に全ての.cpp, .hppが入っているようだが、できればcoreとlibraryとかに分けてほしいな。

synrules.cpp

名前からすると、src/synrules.cppというファイルにsyntax-rulesのソースがありそうだ。拡張子がcppなのでC++だと思うが、わりと独自の世界観というか、普通のC++の書き方ではないようで面白い。

ファイル末尾で、構造体を定義している。構造体と言ってもCのstructというよりは、R7RS Recordの可能性が高そうだ。

R.SyntaxRulesRecordType = MakeRecordTypeC("syntax-rules",
        sizeof(SyntaxRulesFieldsC) / sizeof(char *), SyntaxRulesFieldsC);

Rというのは何だろうか。ちょっと検索がしづらいが、ソース内でグローバルに見える変数だろうから、foment.cppあたりを探してみると、以下の定義があった。

FRoots R;

Rの中身はsyntax-rulesの実装には直接関係ないような予感がするけれど、正体が分かっているものがたくさんあった方がソースは読みやすいので、ちょっと調べてみよう。

FRoots R

git grep FRootsでFRootsの定義を探すとfoment.hppにあった。このファイルの冒頭にはTODOリストがある。いちばん最初は1ファイルで開発していたのかもしれない。

FRootsは以下のような構造体だった。グローバルな変数の置き場なのだろうか。

// ---- Roots ----

typedef struct
{
    FObject Bedrock;
    FObject BedrockLibrary;
    FObject Features;
    FObject CommandLine;
略...
    FObject DatumReferenceRecordType;
} FRoots;

余談だが「// ---- Roots ----」のように要所要所に区切りが書いてあるのを見つけると嬉しくなる。単に見やすいだけでなく、コードを整理しようという意志が感じられるからだ。

FObject

ついでなのでFObjectの定義も確認してみる。が、単にvoid *のtypedefだった。へえ。

typedef void * FObject;

MakeRecordTypeC

synrules.cppの以下の行を見ていたのだった。

R.SyntaxRulesRecordType = MakeRecordTypeC("syntax-rules",
        sizeof(SyntaxRulesFieldsC) / sizeof(char *), SyntaxRulesFieldsC);

Rがどういうものかなんとなく分かったので、次はMakeRecordTypeCを調べてみる。これもfoment.cppに定義があった。

FObject MakeRecordTypeC(const char * nam, uint_t nf, const char * flds[])
{
    FObject oflds[32];

    FAssert(nf <= sizeof(oflds) / sizeof(FObject));

    for (uint_t fdx = 0; fdx < nf; fdx++)
        oflds[fdx] = StringCToSymbol(flds[fdx]);

    return(MakeRecordType(StringCToSymbol(nam), nf, oflds));
}

すぐ上にはMakeRecordTypeという関数がある。Cというsuffixは何かなと思ったが、第一引数の型が違うようだ。C++のCか、CharのCあたりだろうか。

  • FObject MakeRecordType(FObject nam, uint_t nf, FObject flds[])
  • FObject MakeRecordTypeC(const char * nam, uint_t nf, const char * flds[])

すぐ下では%make-record-typeという何かを定義している。「%」を頭に付けるのはSchemeのコードでわりと見られる命名なので、おそらくDefineというマクロを使うとSchemeレベルの関数が定義されるのであろう。

Define("%make-record-type", MakeRecordTypePrimitive)(int_t argc, FObject argv[])
{
    // (%make-record-type <record-type-name> (<field> ...))

    FMustBe(argc == 2);

    SymbolArgCheck("define-record-type", argv[0]);
...略
    return(MakeRecordType(argv[0], VectorLength(flds), AsVector(flds)->Vector));
}

R7RSにはdefine-record-typeという構文があるので、きっとそこからこの%make-record-typeが呼ばれそうな気がする。git grepでdefine-record-typeを探すと、src/base.scmにそのような定義があった。

    (define-syntax define-record-type
        (syntax-rules ()
            ((define-record-type type (maker arg ...) predicate field ...)
                (begin
                    (define type (%make-record-type 'type '(field ...)))
                    (define maker (define-record-maker type () () arg ...))
                    (define (predicate obj) (%record-predicate type obj))
                    (define-record-field type field) ...))))

Define

このDefineというのは構文みたいに見えるけど、C++にはこんな記法はないので、どこかにマクロ定義があるはず。きっとfoment.cppかhppだろう。

Define("%make-record-type", MakeRecordTypePrimitive)(int_t argc, FObject argv[])

hppの方だった。この例だとnameが"%make-record-type"、primがMakeRecordTypePrimitiveに対応する。

#define Define(name, prim) \
    EternalSymbol(prim ## Symbol, name); \
    static FObject prim ## Fn(int_t argc, FObject argv[]);\
    static FEternalPrimitive prim ## Object = { \
        .ObjHdr.FlagsAndSize = OBJHDR_GEN_ETERNAL | sizeof(FPrimitive), \
        .ObjHdr.TagAndSlotCount = (PrimitiveTag << OBJHDR_TAG_SHIFT) | 1, \
        .Primitive.Name = prim ## Symbol, \
        .Primitive.PrimitiveFn = prim ## Fn, \
        .Primitive.Filename = __FILE__, \
        .Primitive.LineNumber =__LINE__, \
        .ObjFtr.Feet = {OBJFTR_FEET, OBJFTR_FEET} \
    }; \
    FObject prim = &prim ## Object.Primitive; \
    static FObject prim ## Fn

Define("foo", FooPrimitive)のようにすると、以下のようなものが定義されるようだ。

  • EternalSymbol(FooPrimitiveSymbol, "foo");
  • FEternalPrimitive FooPrimitiveObject
  • FObject FooPrimitive = &FooPrimitiveObject.Primitive;
  • FObject FooPrimitiveFn

こうやって対象領域のために言語を拡張するのはLispプログラマらしい感じがする。

EternalSymbolの定義もfoment.hppにある。たぶん、Schemeプログラムの最初から最後まで存在するシンボルオブジェクトがEternalSymbolなのだろう。

今日のところはここまで。


過去の日記