blob: 3f952e658248dca55e482556f1577fedc7b5858d [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include <string>
#include <vector>
#include "types.h"
#include "syscall_parser.h"
using std::string;
using std::vector;
bool is_identifier_keyword(const string& iden) {
if (iden == "syscall" ||
iden == "returns" ||
iden == "optional" ||
iden == "IN" || iden == "OUT" || iden == "INOUT") {
return true;
}
return false;
}
bool vet_identifier(const string& iden, const FileCtx& fc) {
if (iden.empty()) {
fc.print_error("expecting idenfier", "");
return false;
}
if (is_identifier_keyword(iden)) {
fc.print_error("identifier cannot be keyword or attribute", iden);
return false;
}
if (!isalpha(iden[0])) {
fc.print_error("identifier should start with a-z|A-Z", string(iden));
return false;
}
return true;
}
bool parse_param_attributes(TokenStream* ts, vector<string>* attrs) {
while (ts->peek_next() != ")" && ts->peek_next() != ",") {
auto attr = ts->next();
attrs->push_back(attr);
}
return true;
}
bool parse_product_of_identifiers(TokenStream* ts, TypeSpec* type_spec,
std::vector<std::string>* identifiers) {
while (true) {
if (!vet_identifier(ts->curr(), ts->filectx()))
return false;
if (ts->curr() == type_spec->name) {
ts->filectx().print_error("invalid name for an array specifier", ts->curr());
return false;
}
identifiers->push_back(ts->curr());
if (ts->next() == "]") {
return true;
}
if (ts->curr() != "*") {
ts->filectx().print_error("expected ']' or '*'", "");
return false;
}
ts->next(); // consume '*'
}
}
bool parse_arrayspec(TokenStream* ts, TypeSpec* type_spec) {
uint32_t count = 0;
std::vector<std::string> multipliers;
if (ts->next() != "[")
return false;
if (ts->next().empty())
return false;
auto c = ts->curr()[0];
if (isalpha(c)) {
if (!parse_product_of_identifiers(ts, type_spec, &multipliers)) {
return false;
}
} else if (isdigit(c)) {
count = c - '0';
if (ts->curr().size() > 1 || count == 0 || count > 9) {
ts->filectx().print_error("only 1-9 explicit array count allowed", "");
return false;
}
if (ts->next() != "]") {
ts->filectx().print_error("expected", "]");
return false;
}
} else {
ts->filectx().print_error("expected array specifier", "");
return false;
}
type_spec->arr_spec.reset(new ArraySpec{ArraySpec::IN, count, multipliers});
return true;
}
bool parse_typespec(TokenStream* ts, TypeSpec* type_spec) {
if (ts->peek_next() == ":") {
auto name = ts->curr();
if (!vet_identifier(name, ts->filectx()))
return false;
type_spec->name = name;
ts->next();
if (ts->next().empty())
return false;
}
auto type = ts->curr();
if (!vet_identifier(type, ts->filectx()))
return false;
type_spec->type = type;
if (ts->peek_next() == "[" && !parse_arrayspec(ts, type_spec)) {
return false;
}
if (!parse_param_attributes(ts, &type_spec->attributes)) {
return false;
}
if (type_spec->arr_spec && !type_spec->arr_spec->assign_kind(type_spec->attributes)) {
ts->filectx().print_error("expected", "IN, INOUT or OUT");
return false;
}
return true;
}
bool parse_argpack(TokenStream* ts, vector<TypeSpec>* v) {
if (ts->curr() != "(") {
ts->filectx().print_error("expected", "(");
return false;
}
while (true) {
if (ts->next() == ")")
break;
if (v->size() > 0) {
if (ts->curr() != ",") {
ts->filectx().print_error("expected", ", or :");
return false;
}
ts->next();
}
TypeSpec type_spec;
if (!parse_typespec(ts, &type_spec))
return false;
v->emplace_back(std::move(type_spec));
}
return true;
}
bool process_comment(AbigenGenerator* parser, TokenStream& ts) {
if (ts.peek_next() == "!") {
ts.next(); // '!'
Requirement req;
for (;;) {
req.emplace_back(ts.next());
if (ts.peek_next() == std::string())
break;
}
parser->AppendRequirement(std::move(req));
} else if (ts.peek_next() == "^") {
ts.next(); // '^'
TopDescription td;
for (;;) {
td.emplace_back(ts.next());
if (ts.peek_next() == std::string())
break;
}
parser->SetTopDescription(std::move(td));
}
return true;
}
bool process_syscall(AbigenGenerator* parser, TokenStream& ts) {
auto name = ts.next();
if (!vet_identifier(name, ts.filectx()))
return false;
Syscall syscall{ts.filectx(), name};
// Every entry gets the special catch-all "*" attribute.
syscall.attributes.push_back("*");
while (true) {
auto maybe_attr = ts.next();
if (maybe_attr[0] != '(') {
syscall.attributes.push_back(maybe_attr);
} else {
break;
}
}
if (!parse_argpack(&ts, &syscall.arg_spec))
return false;
auto return_spec = ts.next();
if (return_spec == "returns") {
ts.next();
if (!parse_argpack(&ts, &syscall.ret_spec)) {
return false;
}
if (syscall.ret_spec.size() > 1) {
std::for_each(syscall.ret_spec.begin() + 1, syscall.ret_spec.end(),
[](TypeSpec& type_spec) {
type_spec.arr_spec.reset(
new ArraySpec{ArraySpec::OUT, 1, {}});
});
}
} else if (return_spec != ";") {
ts.filectx().print_error("expected", ";");
return false;
}
return parser->AddSyscall(std::move(syscall));
}