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