割りと暇のある時に読んでるんですが、あまり進まない現状。

コンパイラの本は結構積まれてるんで頑張って崩していかないと。。。

書いたコード

4 章の図の通りの決定性有限オートマトンについて、yylex と同じ働きをするコードを書けとの問題があったので、問いてみた。

そんなに難しくないらしく、何も考えなければ 30 分くらいで書けるらしい。
いや、僕は普通に一時間くらい使いましたが… C になれていないせいと言い訳をしておきます。

sample.l

%{
enum { ID = 1, NUM, REAL, ADD, SUB,
       MUL, DIV, LPAR, RPAR, EQ, EX, QU,
       COMMA, SEMI, INT, FLOAT, SEPARATE, COMMENT};
%}
%%
[a-z][a-z0-9]* { return ID; }
0|[1-9][0-9]* { return NUM; }
"+"      { return ADD; }
"-"      { return SUB; }
"*"      { return MUL; }
"/"      { return DIV; }
"("      { return LPAR; }
")"      { return RPAR; }
"="      { return EQ; }
"!"      { return EX; }
"?"      { return QU; }
","      { return COMMA; }
";"      { return SEMI; }
int      { return INT; }
float      { return FLOAT; }
" "|"\n"|"\t"      { return SEPARATE; }
"/*"[a-z0-9]*"*/" { return COMMENT; }
%%
int yywrap(void) { return 1; }
int main(void) {
  int t;
  while((t = yylex()) != 0) {
    printf("number = %d, string = '%s'\n", t, yytext);
  }
}

なんかこんな感じの仕様。試したければ、下記の様な感じで試せます。

$ lex sample.l
$ cc lex.yy.c
$ ./a.out
asdf * 1 / 2

下のは問題を問いてみたコードです。
これを書いてる最中にミスに気付きましたが放置してます。

sample.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 256
enum {
  INT = 1, FLOAT, ID, NUM,
  REAL, ADD, SUB, MUL, DIV,
  LPAR, RPAR, EQ, EX, QU,
  COM, SEMI, ERR
};
char yytext[1024];
int is_separate(char c)
{
  if (c == ' ' || c == '\n' || c == '\t') return 1;
  return 0;
}
int is_int()
{
  int i = 0;
  char c;
  while ((c = fgetc(stdin)) != EOF) {
    if (is_separate(c)) break;
    yytext[i] = c;
    ++i;
  }
  yytext[i] = '\0';
  return INT;
}
int is_float()
{
  int i = 0;
  char c;
  while ((c = fgetc(stdin)) != EOF) {
    if (is_separate(c)) break;
    yytext[i] = c;
    ++i;
  }
  yytext[i] = '\0';
  return FLOAT;
}
int is_char(char c)
{
  if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) {
    return 1;
  }
  return 0;
}
int is_token(char c)
{
  switch (c) {
  case '+': case '-': case '*': case '/': case '(':
  case ')': case '=': case '!': case '?': case ',': case ';':
    ungetc(c, stdin);
    return 1;
  }
  return 0;
}
int id_or_int_or_float()
{
  int i = 0;
  char c;
  while ((c = fgetc(stdin)) != EOF) {
    if (is_separate(c) || is_token(c)) {
      yytext[i] = '\0';
      if (strcmp(yytext, "int") == 0) return INT;
      if (strcmp(yytext, "float") == 0) return FLOAT;
      break;
    }
    yytext[i] = c;
    ++i;
  }
  return ID;
}
int is_num(char c)
{
  if ('0' <= c && c <= '9') return 1;
  return 0;
}
int is_real(char c)
{
  if (c == '.') return 1;
  return 0;
}
int num_or_real()
{
  int i = 0;
  int real_flag = 0;
  char c;
  while ((c = fgetc(stdin)) != EOF) {
    if (i == 1 && yytext[0] == '0' && c != '.') {
      ungetc(c, stdin);
      return NUM;
    }
    if (real_flag && is_real(c)) {
      ungetc(c, stdin);
      yytext[i] = '\0';
      return ERR;
    } else if (is_real(c)) {
      real_flag = 1;
    }
    if (is_separate(c) || is_token(c)) {
      yytext[i] = '\0';
      break;
    }
    yytext[i] = c;
    ++i;
  }
  return (real_flag) ? REAL : NUM;
}
int yylex(void)
{
  int t = 0;
  char c;
  while ((c = fgetc(stdin)) != EOF) {
    yytext[0] = c;
    if (is_char(c)) {
      ungetc(c, stdin);
      return id_or_int_or_float();
    }
    if (is_num(c) || is_real(c)) {
      ungetc(c, stdin);
      return num_or_real();
    }
    yytext[1] = '\0';
    switch (c) {
    case '+': return ADD; break;
    case '-': return SUB; break;
    case '*': return MUL; break;
    case '/': return DIV; break;
    case '(': return LPAR; break;
    case ')': return RPAR; break;
    case '=': return EQ; break;
    case '!': return EX; break;
    case '?': return QU; break;
    case ',': return COM; break;
    case ';': return SEMI; break;
    case ' ': case '\n': case '\t': break;
    }
  }
  return t;
}
int
main(void)
{
  int t;
  while ((t = yylex()) != 0) {
    printf("number = %d, string = '%s'\n", t, yytext);
  }
}

こんな感じのコードです。実行すると下記の様になります。

(a+b*c/d);
number = 10, string = '('
number = 3, string = 'a'
number = 6, string = '+'
number = 3, string = 'b'
number = 8, string = '*'
number = 3, string = 'c'
number = 9, string = '/'
number = 3, string = 'd'
number = 11, string = ')'
number = 16, string = ';'
/* aaaa */
number = 9, string = '/'
number = 8, string = '*'
number = 3, string = 'aaaa'
number = 8, string = '*'
number = 9, string = '/'

もう気付きました? このコードコメントアウトのパース書いてないんです... 忘れてました。
気が向いたら修正しますが、めんどくさい (というか自分の中で既に終った話です) ので、多分やらないです。
気がむいた人がやってみて下さい。