SATySFiのエラーメッセージと対処法

SATySFiの強みは静的な型付けおよび静的解析です. 静的解析によって実際に文書を組む前に誤りを発見することができ, エラーの原因に関する情報を具体的に知ることができます.

事前に誤りを発見するということは,逆に言えばユーザが正しいコードを書くまで永遠に文書が生成されないということでもあります. SATySFiをはじめたばかりでまだ構文や型の区別に慣れていないユーザにとっては, よくわからないエラーばかり出てきてちっとも望む出力が得られない, という不満につながってしまうかもしれません. しかしそれは勿体無いことです. ある程度正確にコードが書けるようになった人にとってコンパイルエラーはありがたいものであり, 自分が混入させてしまったバグを素早く取り除くための有益な情報となります. この章では,典型的なエラーメッセージと考えられる原因,対処法を紹介します.

SATySFi のエラーの見方

エラーの見方を学ぶには,実際に誤ったソースコードをコンパイルしてエラー表示を眺めるのが確実です. たとえば,以下のコードをコンパイルしてみましょう.

@require: stdja

document(| title = {}; author = {}; show-title = true; show-toc = false |) '<

  +command-not-defined{
    Hello, World!
  }

>

このコードに登場する +command-not-defined というコマンドは,それまでのどこにも定義されていません. このコードをコンパイルすると以下のようなエラーが出力され,処理が停止します.

 ---- ---- ---- ----
  target file: 'example1.pdf'
  dump file: 'example1.satysfi-aux' (already exists)

(中略)

 ---- ---- ---- ----
  type checking 'example1.saty' ...
! [Type Error] at "example1.saty", line 5, characters 2-22:
    undefined variable '+command-not-defined'.

ここで大事なのは ! から始まる行と,それ以降です. エラーの文言はたいてい以下の3要素からなります.

  • エラーの種類:ここでは [Type Error] です. つまり,字句解析の段階で生じた文法エラーであることを表しています.
  • エラーの位置:ここでは "example1.saty", line 5, characters 2-22 です. example1.saty という名前のファイルの8行目の2文字目から22文字目のところでエラーが見つかったことを示しています. なお文字は 0-index でカウントし,終端を含みません. 1からカウントを始める人がもしいれば,3文字目から22文字目にかけて,と読むのが良いでしょう.
  • エラーの具体的な内容:ここでは undefined variable '+command-not-defined'. です. つまり, +command-not-defined という変数(コマンド)は存在しない,と書かれています.

代表的なエラーの種類には以下のようなものがあります.

  • モジュールのパス解決エラー
  • 構文エラー
  • 型エラー
  • 実行時エラー

構文エラー

SATySFi 処理系では, ソースコードを処理するとき最初に字句解析器 (lexer) と構文解析器 (parser) に通すことで構文のパースを行います. この時点で生じるエラーは 構文エラー として処理され, 以下のメッセージから始まるエラー文を出力します.

  • [Syntax Error at Lexer]
  • [Syntax Error at Parser]

両者には字句解析の段階で失敗したか,構文解析の段階で失敗したかという違いがあるものの, どちらもユーザが何らかの文法ミスを犯したときに出るエラーという点では共通しているため, 普段はそこまで違いを気にする必要はありません.

構文エラーの原因

SATySFi で犯しやすい構文エラーは以下のとおりです.

  • インラインテキストとブロックテキストの混同. SATySFi では,インラインテキストとブロックテキストをしっかり区別する必要がある. インラインテキストでは通常の文字を書くことができるが,ブロックテキストではできない. たとえば以下のコードはエラーになる.

    +section{テスト}<
      本文
    >
    

    「本文」と書かれている箇所はブロックテキストの入る箇所であり, 直接文を書くと構文解析ができず構文エラーとなる.以下の例などのように書けば問題ない.

    +section{テスト}<
      +p{
        本文
      }
    >
    
  • コマンドの構文誤り. たとえば,+p{} とすべきところを +p{}; としたり, \eqn(${\pi}); とすべきところを \eqn(${\pi}) としたりすると構文エラーとなる.

  • 括弧の対応ミス. 当然ながら,()[]{} といった括弧の対応が正しく取れていないコードは正しく処理できない. また,リストやレコードの区切り文字は , ではなく ; であることにも注意 (カンマ区切りの他言語などを書いているとつい忘れがち).

  • ヘッダの構文誤り. SATySFi では,冒頭に @import:, @require: から始まる宣言を付けることで他のヘッダファイルやパッケージを読み込むことができる. ヘッダは常にファイルの冒頭に固めて記述する必要があり, また @ の後や : の直前にスペースを入れることは許されない.

Vim, Emacs, VSCode といった著名なテキストエディタでは, 有志の手によって SATySFi のシンタックスハイライトを行ってくれる拡張機能が開発されています. 単にコードが見やすくなるだけでなく,構文エラーを引き起こす記述にも気づきやすくなるため, 可能であればそれらを活用するのがおすすめです.

依存解決エラー

依存解決エラーの原因

  • @require@import の混同

    @require は「SATySFi にインストールされている(=特定のディレクトリに格納されている)パッケージ」を指定する際に, @import は「読み込みたいファイルへの相対パス」を指定する際に用いる.

  • .satyh.satyg のファイルパスが正しくない

  • 必要なフォントファイルがSATySFiの読める場所にない

  • フォントファイルは適切な場所にあるものの,ハッシュファイルに情報が記載されていない

型エラー

SATySFi は,型推論に基づいて静的な型付けを行う言語です. ユーザが指定した型や型推論の結果に不整合があったなどの原因で正しく型推論を行えなかった場合, 静的解析の段階でそれがエラーとして表出されます.

型エラーの代表的な原因

  • 未定義の変数やコマンド. 他言語と同様に未定義の変数やコマンドがあればエラーとなる.

  • 用いる演算子のミス. SATySFi は OCaml と同様に,演算子のオーバーロードが存在しない. たとえば + という演算子はint型とint型同士の加算にしか用いることができず, float型(浮動小数点数)同士の加算やlength型(長さ,寸法)同士の加算には用いられない. 2.0 + 3.0 のように書くと,以下のような型エラーが生じる.

      type checking 'example1.saty' ...
    ! [Type Error] at "example1.saty", line 3, characters 8-11:
        this expression has type
          float,
        but is expected of type
          int.
    

    演算子 + によってint型が来ることが期待されているにも関わらず float 型の数値が来たことを意味する. float型同士なら 2.0 +. 3.0,length型同士なら 2pt +' 3mm のように,演算子を区別する必要がある.

  • 浮動小数点にはマイナスを付けられない. 上と関連しているが, SATySFi では現状 -2.0 のように書くことができない. そのため -2.0 と書くと「単項演算子 - の後ろには int が期待されている」という型エラーになる. なお長さの場合は,リテラルに限り -2pt-2.5pt と書くことができる (マイナスと数値の間にはスペースを空けてはいけない). ただし,length 型を持つ変数 x に対して -x とするとやはり型エラーとなるので注意.

  • プリアンブルと本文の間に in がない. これは本来ユーザが誤った文法で書いてしまったことが原因で生じるエラーであるものの, SATySFiの文法の都合上文法エラーではなく型エラーとして扱われる.

  • その他,様々な型のミス.

実行時エラー

静的型付け言語であるSATySFiは,多くのミスを静的解析の段階で検出できるようになっています. しかし,実際に値を評価するときに初めて表出するエラーもあります.

実行時エラーの代表的な原因

  • 画像のファイルパスが正しくないとき
  • ユーザまたはパッケージ開発者が abort-with-message プリミティヴを用いて意図的にエラーを出したとき
  • ゼロ除算を行ったとき
  • 部分文字列を求める際の範囲外アクセスを行ったとき