| // 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)); |
| } |