ソースコードの推敲
[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, *p;
106     int i;
107     bool quoting = false;
108     CMDLINE *cmdl = NULL;
109
110     if(!line[0]) {
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         } else if(quoting == false && p[i] == ';') {
120             p[i] = '\0';
121             break;
122         }
123     }
124     if(!p[0] || p[0] == '\n') {
125         goto linetokfin;
126     }
127     cmdl = malloc_chk(sizeof(CMDLINE), "cmdl");
128     /* ラベルの取得。行の先頭が空白またはタブの場合、ラベルは空 */
129     if((i = strcspn(p, " \t\n")) == 0){
130         cmdl->label = strdup_chk("", "cmdl->label");
131     } else {        /* ラベルを取得 */
132         /* 文字列が長すぎる場合はエラー */
133         if(i > LABELSIZE) {
134             p[i] = '\0';
135             setcerr(104, p);    /* label length is too long */
136         } else {
137             cmdl->label = strndup_chk(p, i, "cmdl->label");
138         }
139         p += i + 1;
140     }
141     /* ラベルと命令の間の空白をスキップ */
142     while(p[0] == ' ' || p[0] == '\t') {
143         p++;
144     }
145     /* 命令とオペランドの取得 */
146     if(!p[0] || p[0] == '\n') {        /* 命令がない場合は、終了 */
147         if(cmdl->label) {      /* ラベルが定義されていて命令がない場合はエラー */
148             setcerr(105, "");    /* no command in the line */
149         }
150         FREE(cmdl->label);
151         FREE(cmdl);
152         goto linetokfin;
153     }
154     /* 命令の取得 */
155     i = strcspn(p, " \t\n");
156     p[i] = '\0';
157     cmdl->cmd = strdup_chk(p, "cmdl.cmd");
158     p += i + 1;
159     /* 命令とオペランドの間の空白をスキップ */
160     while(p[0] == ' ' || p[0] == '\t') {
161         p++;
162     }
163     /* 改行かタブまでの文字列を取得 */
164     /* 「'」で囲まれた文字列に含まれる場合があるため、空白は無視 */
165     if((i = strcspn(p, "\t\n")) > 0) {
166         p[i] = '\0';
167         cmdl->opd = opdtok(p);
168     } else {
169         cmdl->opd = malloc_chk(sizeof(OPD), "cmdl.opd");
170         cmdl->opd->opdc = 0;
171     }
172 linetokfin:
173     FREE(tok);
174     return cmdl;
175 }