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