`(Hello ,world)

ツッコミ、添削大歓迎です。いろいろ教えてください。

2010-08-03

オートインデント2

前回xyzzyでオートインデントをどうやってるかを追おうと見てみたけど、主要な関数だと思われる up-list がビルトインでXyzzyLispで行われてなかったので、C++のソースを見てみる。

Lisp側のup-listと関連付けられているビルトインのC関数

どうやって関連付けられているのかわからなかった…

  DEFCMD3 (up-list, 0, 2, 0, "p"),
  • たぶんFup_list()
Fup_list()

syntax.cc:1884

lisp
Fup_list (lisp arg, lisp noerror)
{
  return up_down_list (arg, noerror, 0);
}
  • 単に up_down_list() 関数の呼び出し
up_down_list()

syntax.cc:1863

static lisp
up_down_list (lisp arg, lisp noerror, int downp)
{
  int n = (!arg || arg == Qnil) ? 1 : fixnum_value (arg);
  if (!n)
    return Qnil;

  Window *wp = selected_window ();
  wp->w_disp_flags |= Window::WDF_GOAL_COLUMN;
  Buffer *bp = wp->w_bufp;
  Point &point = wp->w_point;
  point_t opoint = point.p_point;
  int status = bp->up_down_list (point, n, downp);
  if (!status)
    return Qt;
  bp->goto_char (point, opoint);
  if (!noerror || noerror == Qnil)
    sexp_error (status);
  return Qnil;
}
  • 現在のバッファ、ポイントに対して Buffer::up_down_list() メソッドの呼び出し
Buffer::up_down_list()

syntax.cc:1791

int
Buffer::up_down_list (Point &point, int n, int downp) const
{
  const syntax_table *tab = xsyntax_table (lsyntax_table);
  int i = 0;
  int (Buffer::*skip_white)(Point &, int) const;
  int (Buffer::*skip_sexp)(Point &) const;
  int dir;

  if (n > 0)
    {
      dir = 1;
      skip_white = &Buffer::skip_white_forward;
      skip_sexp = &Buffer::skip_sexp_forward;
    }
  else
    {
      dir = -1;
      n = -n;
      skip_white = &Buffer::skip_white_backward;
      skip_sexp = &Buffer::skip_sexp_backward;
    }

  while (1)
    {
      if (dir < 0
          && ((!eobp (point) && bolp (point)
               && (syntax (tab, point) == SCopen
                   || syntax (tab, point) == SCtag_start))
              || !forward_char (point, -1)))
        return Send_sexp;

      int status;
      point_t before;
      do
        {
          before = point.p_point;
          status = (this->*skip_white)(point, 0);
        }
      while (status == Sin_comment && point.p_point != before);
      if (status && (dir > 0 || status != Sbob))
        return status;

      if (dir > 0 && !eobp (point)
          && (syntax (tab, point) == SCopen
              || syntax (tab, point) == SCtag_start))
        {
          if (downp)
            {
              forward_char (point, 1);
              if (++i == n)
                return 0;
            }
          else if (bolp (point))
            return Sunmatched_paren;
        }

      status = (this->*skip_sexp)(point);
      if (!downp && status == Send_sexp)
        {
          if (dir > 0)
            forward_char (point, 1);
          if (++i == n)
            return 0;
        }
      else if (status)
        return status;
      if (dir < 0 && bobp (point))
        return Sbob;
    }
}
  • 結果は引数の point に返る
  • 方向によって、skip_sexp でS式を飛ばす
  • ネストしたS式は skip_sexp 側で処理するだろうから、このメソッドでは指定個の開き括弧または閉じ括弧を数えるだけ
  • skip_sexp の戻り値は、リストの終端だったら Send_sexp が返る?
Buffer::skip_sexp_forward(), Buffer::skip_sexp_backward()

syntax.cc:1490

int
Buffer::skip_sexp_forward (Point &point) const
{
  const syntax_table *tab = xsyntax_table (lsyntax_table);
  int status;

  if (eobp (point))
    return Seob;

  while (1)
    {
      Char c = point.ch ();
      if (!SBCP (c))
        return skip_symbol_forward (point);

      switch (xchar_syntax (tab, c))
        {
        case SCclose:
        case SCtag_end:
          if (escaped_char_p (point))
            goto symbol;
          return Send_sexp;

        case SCopen:
        case SCtag_start:
        case SCmath:
          if (escaped_char_p (point))
            goto symbol;
          status = goto_matched_close (point, c);
          if (!status)
            forward_char (point, 1);
          return status;

        case SCstring:
          if (escaped_char_p (point))
            goto symbol;
          status = skip_string_forward (point, c);
          if (!status)
            forward_char (point, 1);
          return status;

        case SCword:
        case SCsymbol:
        case SCsymbol_prefix:
        case SCkanji:
        case SCkana:
        symbol:
          return skip_symbol_forward (point);

        case SCescape:
        case SCquote:
          break;

        default:
          return 0;
        }

      if (!forward_char (point, 1) || eobp (point))
        return Seob;
    }
}

int
Buffer::skip_sexp_backward (Point &point) const
{
  const syntax_table *tab = xsyntax_table (lsyntax_table);

  if (eobp (point) && !forward_char (point, -1))
    return Sbob;

  while (1)
    {
      Char c = point.ch ();
      if (!SBCP (c))
        return skip_symbol_backward (point);

      switch (xchar_syntax (tab, c))
        {
        case SCopen:
        case SCtag_start:
          if (escaped_char_p (point))
            goto symbol;
          return Send_sexp;

        case SCclose:
        case SCmath:
        case SCtag_end:
          if (escaped_char_p (point))
            goto symbol;
          return goto_matched_open (point, c);

        case SCstring:
          if (escaped_char_p (point))
            goto symbol;
          return skip_string_backward (point, c);

        case SCword:
        case SCsymbol:
        case SCsymbol_prefix:
        case SCkanji:
        case SCkana:
        symbol:
          return skip_symbol_backward (point);

        case SCescape:
        case SCquote:
          break;

        default:
          return 0;
        }

      if (!forward_char (point, -1))
        return Sbob;
    }
}
  • 文字によってスキップ
  • リストを前方に飛ばすところだけ見ようか。goto_matched_close()
Buffer::goto_matched_close()

syntax.cc:1155

int
Buffer::goto_matched_close (Point &point, Char openc) const
{
  const syntax_table *tab = xsyntax_table (lsyntax_table);
  int depth = 1, status;
  Char prev_ch = 0;
  while (1)
    {
      if (tab->comment_column >= 0 && !eobp (point))
        prev_ch = point.ch ();
      if (!forward_char (point, 1) || eobp (point))
        return Sunmatched_paren;
      if (tab->comment_column >= 0 && prev_ch == '\n'
          && column_comment_p (tab, point))
        {
          status = skip_single_char_comment_forward (point);
          if (status)
            return status;
          continue;
        }
      Char c = point.ch ();
      if (!SBCP (c))
        continue;

      if (xcomment_end_first_char_p (tab, c)
          && forward_comment_end_p (point))
        return Sin_comment;
      if (xcomment_start_first_char_p (tab, c)
          && forward_comment_start_p (point))
        {
          status = skip_multi_chars_comment_forward (point);
          if (status)
            return status;
          continue;
        }
      if (xcplusplus_comment_char_p (tab, c)
          && !xparse_sexp_ignore_comment_p (tab, c)
          && forward_cplusplus_comment_p (point))
        {
          status = skip_cplusplus_comment_forward (point);
          if (status)
            return status;
          continue;
        }

      switch (xchar_syntax (tab, c))
        {
        case SCclose:
        case SCtag_end:
          if (escaped_char_p (point))
            break;
          if (c == xchar_match (tab, openc))
            {
              if (!--depth)
                return 0;
            }
          else
            return Sunbalanced_paren;
          break;

        case SCopen:
        case SCtag_start:
          if (escaped_char_p (point))
            break;
          if (c == openc)
            depth++;
          else
            {
              status = goto_matched_close (point, c);
              if (status)
                return status;
            }
          break;

        case SCmath:
          if (escaped_char_p (point))
            break;
          if (c == openc)
            return 0;
          status = goto_matched_close (point, c);
          if (status)
            return status;
          break;

        case SCstring:
          if (escaped_char_p (point))
            break;
          status = skip_string_forward (point, c);
          if (status)
            return status;
          break;

        case SCcomment_start:
          if (escaped_char_p (point))
            break;
          if (xparse_sexp_ignore_comment_p (tab, c))
            break;
          status = skip_single_char_comment_forward (point);
          if (status)
            return status;
          break;

        default:
          break;
        }
    }
}
syntax()

syntax.cc:745

static inline syntax_code
syntax (const syntax_table *tab, const Point &point)
{
  return syntax (tab, point.ch ());
}
  • ポイントのキャラを取り出して、別名の syntax() 呼び出し

syntax.cc:737

static inline syntax_code
syntax (const syntax_table *tab, Char c)
{
  if (SBCP (c))
    return syntax_code (xchar_syntax (tab, c));
  return SCkanji;
}
  • 文字の種類を返す
  • syntax_code は単なる enum。この形式のキャストは関数呼び出しと紛らわしいね…
SBCP()
inline int SBCP (Char c) {return c < 256;}
  • 多バイト文字じゃないか?
syntax_code

syntax.h:43

enum syntax_code
{
  SCwhite,
  SCpunct,
  SCopen,
  SCclose,
  SCmath,
  SCstring,
  SCcomment_start,
  SCcomment_end,
  SCcplusplus_comment_end,
  SCescape,
  SCquote,
  SCsymbol,
  SCword,
  SCkana,
  SCkanji,
  SCjunk,
  SCtag_start,
  SCtag_end,
  SCsymbol_prefix,
  SCmax
};
xchar_syntax()
# define xchar_syntax(t, c) ((t)->type[(c)])
  • 単なるテーブルからの取り出し
Buffer::forward_char()

move.cc:37

int
Buffer::forward_char (Point &point, long nchars) const
{
  long d = min (max (point.p_point + nchars, b_contents.p1),
                b_contents.p2) - point.p_point;
  int f = d == nchars;
  const Chunk *cp = point.p_chunk;

  if (d > 0)
    {
      while (1)
        {
          int size = cp->c_used - point.p_offset;
          if (d <= size)
            {
              point.p_point += d;
              if (d == size && cp->c_next)
                {
                  cp = cp->c_next;
                  point.p_offset = 0;
                }
              else
                point.p_offset += d;
              point.p_chunk = (Chunk *)cp;
              break;
            }
          d -= size;
          point.p_point += size;
          point.p_offset = 0;
          cp = cp->c_next;
          assert (cp);
        }
    }
  else if (d < 0)
    {
      while (1)
        {
          if (point.p_offset + d >= 0)
            {
              point.p_offset += d;
              point.p_point += d;
              point.p_chunk = (Chunk *)cp;
              break;
            }
          d += point.p_offset + 1;
          point.p_point -= point.p_offset + 1;
          cp = cp->c_prev;
          assert (cp);
          point.p_offset = cp->c_used - 1;
        }
    }
  return f;
}
Buffer::goto_char()

move.cc:91

void
Buffer::goto_char (Point &point, point_t goal) const
{
  goal = min (max (goal, b_contents.p1), b_contents.p2);
  if (goal < point.p_point / 2)
    {
      point.p_point = 0;
      point.p_chunk = b_chunkb;
      point.p_offset = 0;
    }
  else if (goal > (point.p_point + b_nchars) / 2)
    {
      point.p_point = b_nchars;
      point.p_chunk = b_chunke;
      point.p_offset = b_chunke->c_used;
    }
  forward_char (point, goal - point.p_point);
}
結局
  • 地道にやるより他ない
  • xyzzyのCソースは、クラス別じゃなくて機能別に分かれてて面白いなと思った
トラックバック - http://cadr.g.hatena.ne.jp/mokehehe/20100803