READMEのCASL II仕様書へのリンクを修正
[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 CMDLINE *linetok(const char *line)
98 {
99     char *tok = NULL, *p = NULL;
100     int i;
101     CMDLINE *cmdl = NULL;
102
103     assert(line);
104     if(!line[0] || line[0] == '\n') {
105         return NULL;
106     }
107     tok = p = strdup_chk(line, "tok");
108     /* コメントを削除 */
109     strip_casl2_comment(p);
110     /* 文字列末尾の改行と空白を削除 */
111     strip_end(p);
112     /* 空行の場合、終了 */
113     if(!p[0]) {
114         goto linetokfin;
115     }
116     cmdl = malloc_chk(sizeof(CMDLINE), "cmdl");
117
118     /* ラベルの取得 */
119     /* 行の先頭が空白またはタブの場合、ラベルは空 */
120     if((i = strcspn(p, " \t")) == 0) {
121         cmdl->label = strdup_chk("", "cmdl->label");
122     } else {
123         cmdl->label = strndup_chk(p, i, "cmdl->label");
124         /* ラベルの文字列が長すぎる場合はエラー */
125         if(i > LABELSIZE) {
126             setcerr(104, cmdl->label);    /* label length is too long */
127             FREE(cmdl->label);
128             goto linetokfin;
129         }
130     }
131
132     /* 命令の取得 */
133     /* 文字列先頭をラベルの次の文字に移動 */
134     p += i;
135     /* 文字列先頭を、ラベルと命令の間の空白の後ろに移動 */
136     p += strspn(p, " \t");
137     /* 命令がない場合は、終了 */
138     if(!p[0]) {
139         if(cmdl->label) {      /* ラベルが定義されていて命令がない場合はエラー */
140             setcerr(105, "");    /* no command in the line */
141         }
142         FREE(cmdl->label);
143         FREE(cmdl);
144         goto linetokfin;
145     }
146     /* 命令取得の実行 */
147     i = strcspn(p, " \t");
148     cmdl->cmd = strndup_chk(p, i, "cmdl.cmd");
149
150     /* オペランドの取得 */
151     /* 文字列先頭を、命令の次の文字に移動 */
152     p += i;
153     /* 文字列先頭を、命令とオペランドの間の空白の後ろに移動 */
154     p += strspn(p, " \t");
155     /* オペランド取得の実行 */
156     cmdl->opd = opdtok(p);
157 linetokfin:
158     FREE(tok);
159     return cmdl;
160 }