1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <algorithm>
6#include <string>
7#include <vector>
8
9#include "types.h"
10
11#include "syscall_parser.h"
12
13using std::string;
14using std::vector;
15
16bool is_identifier_keyword(const string& iden) {
17    if (iden == "syscall" ||
18        iden == "returns" ||
19        iden == "optional" ||
20        iden == "IN" || iden == "OUT" || iden == "INOUT") {
21        return true;
22    }
23    return false;
24}
25
26bool vet_identifier(const string& iden, const FileCtx& fc) {
27    if (iden.empty()) {
28        fc.print_error("expecting idenfier", "");
29        return false;
30    }
31
32    if (is_identifier_keyword(iden)) {
33        fc.print_error("identifier cannot be keyword or attribute", iden);
34        return false;
35    }
36    if (!isalpha(iden[0])) {
37        fc.print_error("identifier should start with a-z|A-Z", string(iden));
38        return false;
39    }
40    return true;
41}
42
43bool parse_param_attributes(TokenStream* ts, vector<string>* attrs) {
44    while (ts->peek_next() != ")" && ts->peek_next() != ",") {
45        auto attr = ts->next();
46        attrs->push_back(attr);
47    }
48    return true;
49}
50
51bool parse_product_of_identifiers(TokenStream* ts, TypeSpec* type_spec,
52                                  std::vector<std::string>* identifiers) {
53    while (true) {
54        if (!vet_identifier(ts->curr(), ts->filectx()))
55            return false;
56        if (ts->curr() == type_spec->name) {
57            ts->filectx().print_error("invalid name for an array specifier", ts->curr());
58            return false;
59        }
60        identifiers->push_back(ts->curr());
61        if (ts->next() == "]") {
62            return true;
63        }
64        if (ts->curr() != "*") {
65            ts->filectx().print_error("expected ']' or '*'", "");
66            return false;
67        }
68        ts->next(); // consume '*'
69    }
70}
71
72bool parse_arrayspec(TokenStream* ts, TypeSpec* type_spec) {
73    uint32_t count = 0;
74    std::vector<std::string> multipliers;
75
76    if (ts->next() != "[")
77        return false;
78
79    if (ts->next().empty())
80        return false;
81
82    auto c = ts->curr()[0];
83
84    if (isalpha(c)) {
85        if (!parse_product_of_identifiers(ts, type_spec, &multipliers)) {
86            return false;
87        }
88    } else if (isdigit(c)) {
89        count = c - '0';
90        if (ts->curr().size() > 1 || count == 0 || count > 9) {
91            ts->filectx().print_error("only 1-9 explicit array count allowed", "");
92            return false;
93        }
94        if (ts->next() != "]") {
95            ts->filectx().print_error("expected", "]");
96            return false;
97        }
98    } else {
99        ts->filectx().print_error("expected array specifier", "");
100        return false;
101    }
102    type_spec->arr_spec.reset(new ArraySpec{ArraySpec::IN, count, multipliers});
103    return true;
104}
105
106bool parse_typespec(TokenStream* ts, TypeSpec* type_spec) {
107    if (ts->peek_next() == ":") {
108        auto name = ts->curr();
109        if (!vet_identifier(name, ts->filectx()))
110            return false;
111
112        type_spec->name = name;
113
114        ts->next();
115        if (ts->next().empty())
116            return false;
117    }
118
119    auto type = ts->curr();
120    if (!vet_identifier(type, ts->filectx()))
121        return false;
122
123    type_spec->type = type;
124
125    if (ts->peek_next() == "[" && !parse_arrayspec(ts, type_spec)) {
126        return false;
127    }
128
129    if (!parse_param_attributes(ts, &type_spec->attributes)) {
130        return false;
131    }
132
133    if (type_spec->arr_spec && !type_spec->arr_spec->assign_kind(type_spec->attributes)) {
134        ts->filectx().print_error("expected", "IN, INOUT or OUT");
135        return false;
136    }
137    return true;
138}
139
140bool parse_argpack(TokenStream* ts, vector<TypeSpec>* v) {
141    if (ts->curr() != "(") {
142        ts->filectx().print_error("expected", "(");
143        return false;
144    }
145
146    while (true) {
147        if (ts->next() == ")")
148            break;
149
150        if (v->size() > 0) {
151            if (ts->curr() != ",") {
152                ts->filectx().print_error("expected", ", or :");
153                return false;
154            }
155            ts->next();
156        }
157
158        TypeSpec type_spec;
159
160        if (!parse_typespec(ts, &type_spec))
161            return false;
162        v->emplace_back(std::move(type_spec));
163    }
164    return true;
165}
166
167bool process_comment(AbigenGenerator* parser, TokenStream& ts) {
168    return true;
169}
170
171bool process_syscall(AbigenGenerator* parser, TokenStream& ts) {
172    auto name = ts.next();
173
174    if (!vet_identifier(name, ts.filectx()))
175        return false;
176
177    Syscall syscall{ts.filectx(), name};
178
179    // Every entry gets the special catch-all "*" attribute.
180    syscall.attributes.push_back("*");
181
182    while (true) {
183        auto maybe_attr = ts.next();
184        if (maybe_attr[0] != '(') {
185            syscall.attributes.push_back(maybe_attr);
186        } else {
187            break;
188        }
189    }
190
191    if (!parse_argpack(&ts, &syscall.arg_spec))
192        return false;
193
194    auto return_spec = ts.next();
195
196    if (return_spec == "returns") {
197        ts.next();
198
199        if (!parse_argpack(&ts, &syscall.ret_spec)) {
200            return false;
201        }
202        if (syscall.ret_spec.size() > 1) {
203            std::for_each(syscall.ret_spec.begin() + 1, syscall.ret_spec.end(),
204                          [](TypeSpec& type_spec) {
205                              type_spec.arr_spec.reset(
206                                  new ArraySpec{ArraySpec::OUT, 1, {}});
207                          });
208        }
209    } else if (return_spec != ";") {
210        ts.filectx().print_error("expected", ";");
211        return false;
212    }
213
214    return parser->AddSyscall(std::move(syscall));
215}
216