strip_casl2_commentの、''があるときの動作を修正
[YACASL2.git] / src / token.c
1 #include "token.h"
2
3 /**
4  * @brief 「,」区切りの文字列から、オペランドのトークンを取得
5  *
6  * @return オペランドのトークン
7  *
8  * @param *str 「,」区切りの文字列
9  */
10 OPD *opdtok(const char *str);
11
12 /**
13  * @brief 行トークン取得のエラー定義
14  */
15 CERR cerr_linetok[] = {
16     { 104, "label length is too long" },
17     { 105, "no command in the line" },
18 };
19
20 /**
21  * @brief オペランドトークン取得のエラー定義
22  */
23 static CERR cerr_opdtok[] = {
24     { 117, "operand too many in DC" },
25     { 118, "operand length too long" },
26     { 121, "cannot get operand token" },
27     { 123, "unclosed quote" },
28 };
29
30 OPD *opdtok(const char *str)
31 {
32     OPD *opd = malloc_chk(sizeof(OPD), "opd");
33     char *tok, *p, sepc = ',';
34     int i = 0, cnt_quote = 0;
35     bool quoting = false;
36
37     opd->opdc = 0;
38     if(!str || !str[0]) {
39         return opd;
40     }
41     tok = p = strdup_chk(str, "opdtok.p");
42     do {
43         /* オペランド数が多すぎる場合はエラー */
44         if(opd->opdc >= OPDSIZE) {
45             setcerr(117, "");    /* operand is too many */
46             break;
47         }
48         /* 先頭が等号(=)の場合 */
49         if(p[i] == '=') {
50             i++;
51         }
52         /* 「'」の場合 */
53         if(p[i] == '\'') {
54             /* 「''」以外の場合はquote値を反転する */
55             if(p[i+1] != '\'' && (quoting == false || p[i-1] != '\'')) {
56                 quoting = !quoting;
57             }
58             /* 「'」をカウントする。「''」の場合は1をカウント */
59             if(p[i+1] != '\'') {
60                 cnt_quote++;
61             }
62         }
63         if(quoting == true) {
64             /* 「'」で開いたまま文字列が終了した場合 */
65             if(!p[i]) {
66                 setcerr(123, str);    /* unclosed quote */
67                 break;
68             }
69             i++;
70         } else {
71             i += strcspn(p + i, ", ");
72             if(i == 0) {
73                 setcerr(121, "");    /* cannot get operand token */
74                 break;
75             }
76             if(i - cnt_quote > OPDSIZE) {
77                 setcerr(118, "");    /* operand length too long */
78                 break;
79             }
80             sepc = p[i];
81             opd->opdv[(opd->opdc)++] = strndup_chk(p, i, "opd->opdv[]");
82             p += i + 1;
83             i = cnt_quote = 0;
84         }
85     } while(sepc == ',');
86     FREE(tok);
87     return opd;
88 }
89
90 /* assemble.hで定義された関数群 */
91 void addcerrlist_tok()
92 {
93     addcerrlist(ARRAYSIZE(cerr_linetok), cerr_linetok);
94     addcerrlist(ARRAYSIZE(cerr_opdtok), cerr_opdtok);
95 }
96
97 char *strip_casl2_comment(char *s)
98 {
99     int i;
100     bool quoting = false;
101
102     for(i = 0; s[i]; i++) {
103         /* 「'」で囲まれた文字列の場合。「''」は無視 */
104         if(s[i] == '\'' && s[i+1] != '\'' && (quoting == false || s[i-1] != '\'')) {
105             quoting = !quoting;
106         /* 「'」で囲まれた文字列でない場合、文字列末尾の「;」以降を削除 */
107         } else if(quoting == false && s[i] == ';') {
108             s[i] = '\0';
109             break;
110         }
111     }
112     return s;
113 }
114
115 CMDLINE *linetok(const char *line)
116 {
117     char *tok = NULL, *p = NULL;
118     int i;
119     CMDLINE *cmdl = NULL;
120
121     assert(line);
122     if(!line[0] || line[0] == '\n') {
123         return NULL;
124     }
125     tok = p = strdup_chk(line, "tok");
126     /* コメントを削除 */
127     strip_casl2_comment(p);
128     /* 文字列末尾の改行と空白を削除 */
129     strip_end(p);
130     /* 空行の場合、終了 */
131     if(!p[0]) {
132         goto linetokfin;
133     }
134     cmdl = malloc_chk(sizeof(CMDLINE), "cmdl");
135
136     /* ラベルの取得 */
137     /* 行の先頭が空白またはタブの場合、ラベルは空 */
138     if((i = strcspn(p, " \t")) == 0) {
139         cmdl->label = strdup_chk("", "cmdl->label");
140     } else {
141         cmdl->label = strndup_chk(p, i, "cmdl->label");
142         /* ラベルの文字列が長すぎる場合はエラー */
143         if(i > LABELSIZE) {
144             setcerr(104, cmdl->label);    /* label length is too long */
145             FREE(cmdl->label);
146             goto linetokfin;
147         }
148     }
149
150     /* 命令の取得 */
151     /* 文字列先頭をラベルの次の文字に移動 */
152     p += i;
153     /* 文字列先頭を、ラベルと命令の間の空白の後ろに移動 */
154     p += strspn(p, " \t");
155     /* 命令がない場合は、終了 */
156     if(!p[0]) {
157         if(cmdl->label) {      /* ラベルが定義されていて命令がない場合はエラー */
158             setcerr(105, "");    /* no command in the line */
159         }
160         FREE(cmdl->label);
161         FREE(cmdl);
162         goto linetokfin;
163     }
164     /* 命令取得の実行 */
165     i = strcspn(p, " \t");
166     cmdl->cmd = strndup_chk(p, i, "cmdl.cmd");
167
168     /* オペランドの取得 */
169     /* 文字列先頭を、命令の次の文字に移動 */
170     p += i;
171     /* 文字列先頭を、命令とオペランドの間の空白の後ろに移動 */
172     p += strspn(p, " \t");
173     /* オペランド取得の実行 */
174     cmdl->opd = opdtok(p);
175 linetokfin:
176     FREE(tok);
177     return cmdl;
178 }