2021-10-02 07:23:21 +08:00
|
|
|
#define _GNU_SOURCE
|
|
|
|
|
2018-01-31 08:02:53 +08:00
|
|
|
#include <assert.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <err.h>
|
parser: report input read errors
In qmi_parse(), the main loop accepts tokens until it sees one with
id 0. If an unrecognized (or unexpected) token is encountered, it
reports an error and exits using yyerror().
Normally, input() returns the next character to be processed while
parsing. If the read() call in input() encounters an error, input()
will return -1 instead (which happens to be EOF).
The only caller of input() is yylex(). It recognizes tokens
comprised of alphanumeric characters and '_'. If input() returns
any other value, it uses that value as the token identifier. And in
particular, if input() returns -1, that will be the value stored in
the id field of the token structure yylex() returns.
So, back to the top, qmi_parse() will not consider -1 a valid token
id, and will report the read error as an "unexpected symbol".
Instead, report a read error immediately when it occurs and exit.
Move the definition of yyerror() earlier in the source file so it
can be called from yylex() (and all other functions) without needing
a declaration.
Signed-off-by: Alex Elder <elder@linaro.org>
Message-Id: <20211001232338.769309-8-elder@linaro.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2021-10-02 07:23:11 +08:00
|
|
|
#include <errno.h>
|
2018-01-31 08:02:53 +08:00
|
|
|
#include <fcntl.h>
|
2021-10-02 07:23:15 +08:00
|
|
|
#include <limits.h>
|
2018-01-31 08:02:53 +08:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "list.h"
|
|
|
|
#include "qmic.h"
|
|
|
|
|
2021-10-02 07:23:20 +08:00
|
|
|
/* Allocate and zero a block of memory; and exit if it fails */
|
|
|
|
#define memalloc(size) ({ \
|
|
|
|
void *__p = malloc(size); \
|
|
|
|
\
|
|
|
|
if (!__p) \
|
|
|
|
errx(1, "malloc() failed in %s(), line %d\n", \
|
|
|
|
__func__, __LINE__); \
|
|
|
|
memset(__p, 0, size); \
|
|
|
|
__p; \
|
|
|
|
})
|
|
|
|
|
2021-10-02 07:23:31 +08:00
|
|
|
#define TOKEN_BUF_SIZE 128 /* TOKEN_BUF_MIN or more */
|
|
|
|
#define TOKEN_BUF_MIN 24 /* Enough for a 64-bit octal number */
|
|
|
|
|
2018-01-31 08:18:13 +08:00
|
|
|
const char *qmi_package;
|
|
|
|
|
|
|
|
struct list_head qmi_consts = LIST_INIT(qmi_consts);
|
|
|
|
struct list_head qmi_messages = LIST_INIT(qmi_messages);
|
|
|
|
struct list_head qmi_structs = LIST_INIT(qmi_structs);
|
|
|
|
|
2021-10-02 07:23:15 +08:00
|
|
|
enum token_id {
|
|
|
|
/* Also any non-NUL (7-bit) ASCII character */
|
|
|
|
TOK_CONST = CHAR_MAX + 1,
|
2018-01-31 08:18:13 +08:00
|
|
|
TOK_ID,
|
|
|
|
TOK_MESSAGE,
|
|
|
|
TOK_NUM,
|
2021-10-02 07:23:38 +08:00
|
|
|
TOK_VALUE,
|
2018-01-31 08:18:13 +08:00
|
|
|
TOK_PACKAGE,
|
|
|
|
TOK_STRUCT,
|
|
|
|
TOK_TYPE,
|
|
|
|
TOK_REQUIRED,
|
|
|
|
TOK_OPTIONAL,
|
2021-10-02 07:23:14 +08:00
|
|
|
TOK_EOF,
|
2018-01-31 08:18:13 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct token {
|
2021-10-02 07:23:22 +08:00
|
|
|
enum token_id id;
|
2021-10-02 07:23:08 +08:00
|
|
|
char *str;
|
2021-10-02 07:23:26 +08:00
|
|
|
unsigned long long num;
|
2018-01-31 08:18:13 +08:00
|
|
|
struct qmi_struct *qmi_struct;
|
|
|
|
};
|
2018-01-31 08:02:53 +08:00
|
|
|
|
|
|
|
static int yyline = 1;
|
|
|
|
|
parser: report input read errors
In qmi_parse(), the main loop accepts tokens until it sees one with
id 0. If an unrecognized (or unexpected) token is encountered, it
reports an error and exits using yyerror().
Normally, input() returns the next character to be processed while
parsing. If the read() call in input() encounters an error, input()
will return -1 instead (which happens to be EOF).
The only caller of input() is yylex(). It recognizes tokens
comprised of alphanumeric characters and '_'. If input() returns
any other value, it uses that value as the token identifier. And in
particular, if input() returns -1, that will be the value stored in
the id field of the token structure yylex() returns.
So, back to the top, qmi_parse() will not consider -1 a valid token
id, and will report the read error as an "unexpected symbol".
Instead, report a read error immediately when it occurs and exit.
Move the definition of yyerror() earlier in the source file so it
can be called from yylex() (and all other functions) without needing
a declaration.
Signed-off-by: Alex Elder <elder@linaro.org>
Message-Id: <20211001232338.769309-8-elder@linaro.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2021-10-02 07:23:11 +08:00
|
|
|
static void yyerror(const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
|
2021-10-02 07:23:21 +08:00
|
|
|
fprintf(stderr, "%s: parse error on line %u:\n\t",
|
|
|
|
program_invocation_short_name, yyline);
|
2021-10-02 07:23:19 +08:00
|
|
|
vfprintf(stderr, fmt, ap);
|
|
|
|
fprintf(stderr, "\n");
|
parser: report input read errors
In qmi_parse(), the main loop accepts tokens until it sees one with
id 0. If an unrecognized (or unexpected) token is encountered, it
reports an error and exits using yyerror().
Normally, input() returns the next character to be processed while
parsing. If the read() call in input() encounters an error, input()
will return -1 instead (which happens to be EOF).
The only caller of input() is yylex(). It recognizes tokens
comprised of alphanumeric characters and '_'. If input() returns
any other value, it uses that value as the token identifier. And in
particular, if input() returns -1, that will be the value stored in
the id field of the token structure yylex() returns.
So, back to the top, qmi_parse() will not consider -1 a valid token
id, and will report the read error as an "unexpected symbol".
Instead, report a read error immediately when it occurs and exit.
Move the definition of yyerror() earlier in the source file so it
can be called from yylex() (and all other functions) without needing
a declaration.
Signed-off-by: Alex Elder <elder@linaro.org>
Message-Id: <20211001232338.769309-8-elder@linaro.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2021-10-02 07:23:11 +08:00
|
|
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2021-10-02 07:23:12 +08:00
|
|
|
static char input()
|
2018-01-31 08:02:53 +08:00
|
|
|
{
|
2021-10-02 07:23:17 +08:00
|
|
|
int ch;
|
2018-01-31 08:02:53 +08:00
|
|
|
|
2021-10-02 07:23:17 +08:00
|
|
|
ch = fgetc(stdin);
|
|
|
|
if (ch < 0)
|
parser: report input read errors
In qmi_parse(), the main loop accepts tokens until it sees one with
id 0. If an unrecognized (or unexpected) token is encountered, it
reports an error and exits using yyerror().
Normally, input() returns the next character to be processed while
parsing. If the read() call in input() encounters an error, input()
will return -1 instead (which happens to be EOF).
The only caller of input() is yylex(). It recognizes tokens
comprised of alphanumeric characters and '_'. If input() returns
any other value, it uses that value as the token identifier. And in
particular, if input() returns -1, that will be the value stored in
the id field of the token structure yylex() returns.
So, back to the top, qmi_parse() will not consider -1 a valid token
id, and will report the read error as an "unexpected symbol".
Instead, report a read error immediately when it occurs and exit.
Move the definition of yyerror() earlier in the source file so it
can be called from yylex() (and all other functions) without needing
a declaration.
Signed-off-by: Alex Elder <elder@linaro.org>
Message-Id: <20211001232338.769309-8-elder@linaro.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2021-10-02 07:23:11 +08:00
|
|
|
return 0; /* End of input */
|
2018-01-31 08:02:53 +08:00
|
|
|
|
2021-10-02 07:23:17 +08:00
|
|
|
if (ch == '\n')
|
|
|
|
yyline++;
|
|
|
|
else if (!isascii(ch))
|
2021-10-02 07:23:13 +08:00
|
|
|
yyerror("invalid non-ASCII character");
|
2021-10-02 07:23:14 +08:00
|
|
|
else if (!ch)
|
|
|
|
yyerror("invalid NUL character");
|
2021-10-02 07:23:17 +08:00
|
|
|
|
|
|
|
return (char)ch;
|
2018-01-31 08:02:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void unput(int ch)
|
|
|
|
{
|
|
|
|
if (ch == '\n')
|
|
|
|
yyline--;
|
2021-10-02 07:23:17 +08:00
|
|
|
if (ungetc(ch, stdin) != ch)
|
|
|
|
yyerror("ungetc error");
|
2018-01-31 08:02:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
struct symbol {
|
2021-10-02 07:23:22 +08:00
|
|
|
enum token_id token_id;
|
2018-01-31 08:02:53 +08:00
|
|
|
const char *name;
|
|
|
|
|
2021-10-02 07:23:23 +08:00
|
|
|
union {
|
|
|
|
enum message_type message_type; /* TOK_MESSAGE */
|
|
|
|
struct { /* TOK_TYPE */
|
|
|
|
enum symbol_type symbol_type;
|
|
|
|
/* TYPE_STRUCT also has a struct pointer */
|
|
|
|
struct qmi_struct *qmi_struct;
|
|
|
|
};
|
2021-10-02 07:23:38 +08:00
|
|
|
unsigned long long value; /* TOK_VALUE */
|
2021-10-02 07:23:23 +08:00
|
|
|
};
|
2018-01-31 08:02:53 +08:00
|
|
|
|
|
|
|
struct list_head node;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct list_head symbols = LIST_INIT(symbols);
|
|
|
|
|
2021-10-02 07:23:34 +08:00
|
|
|
static struct symbol *symbol_find(const char *name)
|
|
|
|
{
|
|
|
|
struct symbol *sym;
|
|
|
|
|
|
|
|
list_for_each_entry(sym, &symbols, node)
|
|
|
|
if (!strcmp(name, sym->name))
|
|
|
|
return sym;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-10-02 07:23:37 +08:00
|
|
|
static const char *token_name(enum token_id token_id)
|
|
|
|
{
|
|
|
|
struct symbol *sym;
|
|
|
|
|
|
|
|
switch (token_id) {
|
|
|
|
case TOK_ID:
|
|
|
|
return "identifier";
|
|
|
|
case TOK_MESSAGE:
|
|
|
|
return "(message)";
|
|
|
|
case TOK_NUM:
|
|
|
|
return "(number)";
|
|
|
|
case TOK_EOF:
|
|
|
|
return "(EOF)";
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_for_each_entry(sym, &symbols, node)
|
|
|
|
if (token_id == sym->token_id)
|
|
|
|
return sym->name;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-10-02 07:23:32 +08:00
|
|
|
static bool symbol_valid(const char *name)
|
|
|
|
{
|
|
|
|
const char *p = name;
|
|
|
|
char ch;
|
|
|
|
|
|
|
|
/* Symbol name must start with an alphabetic character */
|
|
|
|
if (!p || !isalpha(*p++))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Remainder of the name is alphanumeric or underscore */
|
|
|
|
while ((ch = *p++))
|
|
|
|
if (!(isalnum(ch) || ch == '_'))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Symbol name must fit in the token buffer */
|
|
|
|
if (p - name > TOKEN_BUF_SIZE)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Finally, symbol names must be unique */
|
2021-10-02 07:23:34 +08:00
|
|
|
if (symbol_find(name))
|
|
|
|
return false;
|
2021-10-02 07:23:32 +08:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-10-02 07:23:22 +08:00
|
|
|
static void symbol_add(const char *name, enum token_id token_id, ...)
|
2018-01-31 08:02:53 +08:00
|
|
|
{
|
|
|
|
struct symbol *sym;
|
|
|
|
va_list ap;
|
|
|
|
|
2021-10-02 07:23:32 +08:00
|
|
|
assert(symbol_valid(name));
|
|
|
|
|
2021-10-02 07:23:22 +08:00
|
|
|
va_start(ap, token_id);
|
2018-01-31 08:02:53 +08:00
|
|
|
|
2021-10-02 07:23:20 +08:00
|
|
|
sym = memalloc(sizeof(struct symbol));
|
2021-10-02 07:23:22 +08:00
|
|
|
sym->token_id = token_id;
|
2018-01-31 08:02:53 +08:00
|
|
|
sym->name = name;
|
|
|
|
|
2021-10-02 07:23:22 +08:00
|
|
|
switch (token_id) {
|
2018-01-31 08:02:53 +08:00
|
|
|
case TOK_MESSAGE:
|
2021-10-02 07:23:23 +08:00
|
|
|
sym->message_type = va_arg(ap, enum message_type);
|
2018-01-31 08:02:53 +08:00
|
|
|
break;
|
|
|
|
case TOK_TYPE:
|
2021-10-02 07:23:23 +08:00
|
|
|
sym->symbol_type = va_arg(ap, enum symbol_type);
|
|
|
|
if (sym->symbol_type == TYPE_STRUCT)
|
2018-01-31 08:02:53 +08:00
|
|
|
sym->qmi_struct = va_arg(ap, struct qmi_struct *);
|
|
|
|
break;
|
2021-10-02 07:23:38 +08:00
|
|
|
case TOK_VALUE:
|
|
|
|
sym->value = va_arg(ap, unsigned long long);
|
|
|
|
break;
|
2021-10-02 07:23:22 +08:00
|
|
|
default:
|
|
|
|
break; /* Most tokens are standalone */
|
2018-01-31 08:02:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
list_add(&symbols, &sym->node);
|
|
|
|
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2021-10-02 07:23:24 +08:00
|
|
|
/* Skip over white space and comments (which start with '#', end with '\n') */
|
|
|
|
static bool skip(char ch)
|
|
|
|
{
|
|
|
|
static bool in_comment = false;
|
|
|
|
|
|
|
|
if (in_comment) {
|
|
|
|
if (ch == '\n')
|
|
|
|
in_comment = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isspace(ch))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (ch == '#')
|
|
|
|
in_comment = true;
|
|
|
|
|
|
|
|
return in_comment;
|
|
|
|
}
|
|
|
|
|
2021-10-02 07:23:35 +08:00
|
|
|
/* Extract an identifier from input into the given buffer */
|
|
|
|
static struct symbol *qmi_identifier_parse(char *buf, size_t size, char ch)
|
|
|
|
{
|
|
|
|
char *p = buf;
|
|
|
|
|
|
|
|
/* First character is known to be alphabetic */
|
|
|
|
*p++ = ch;
|
|
|
|
while ((ch = input()) && (isalnum(ch) || ch == '_')) {
|
|
|
|
if (p - buf == size) {
|
|
|
|
buf[TOKEN_BUF_MIN] = '\0';
|
|
|
|
yyerror("token too long: \"%s...\"", buf);
|
|
|
|
}
|
|
|
|
*p++ = ch;
|
|
|
|
}
|
|
|
|
unput(ch);
|
|
|
|
*p = '\0';
|
|
|
|
|
|
|
|
return symbol_find(buf);
|
|
|
|
}
|
|
|
|
|
parser: be more restrictive when parsing numbers
When a number is parsed, the leading one or two characters are used
to indicate whether the number should be interpreted as hexadecimal,
octal or decimal.
But because the parser accepts any digits regardless of the base, it
allows things like 039 to be treated as an octal number, despite '9'
not being a valid digit. The previous commit makes matters even
worse, allowing [a-fA-F] to be accepted for octal or decimal values.
Such errors are caught (but ignored) later when converting the
accepted string into a number in strtoull().
We are already looking at the first character or two to determine
the base, *after* scanning the number. Instead, determine the base
when the first one or two characters are first input, and restrict
which characters are accepted in the number based on that.
As a consequence, strtoul() will examine all of the characters
comprising the number (whereas previously it would stop if it
encountered invalid character for the base).
Finally, accept either "0x" or "0X" to indicate hexadecimal.
This doesn't actually change behavior much, but as long as we're
checking every character in a number for validity we might as well
be restrictive.
Signed-off-by: Alex Elder <elder@linaro.org>
Message-Id: <20211001232338.769309-25-elder@linaro.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2021-10-02 07:23:28 +08:00
|
|
|
/* Used for parsing octal numbers */
|
|
|
|
static int isodigit(int c)
|
|
|
|
{
|
2021-10-09 05:27:18 +08:00
|
|
|
return isdigit(c) && c < '8';
|
parser: be more restrictive when parsing numbers
When a number is parsed, the leading one or two characters are used
to indicate whether the number should be interpreted as hexadecimal,
octal or decimal.
But because the parser accepts any digits regardless of the base, it
allows things like 039 to be treated as an octal number, despite '9'
not being a valid digit. The previous commit makes matters even
worse, allowing [a-fA-F] to be accepted for octal or decimal values.
Such errors are caught (but ignored) later when converting the
accepted string into a number in strtoull().
We are already looking at the first character or two to determine
the base, *after* scanning the number. Instead, determine the base
when the first one or two characters are first input, and restrict
which characters are accepted in the number based on that.
As a consequence, strtoul() will examine all of the characters
comprising the number (whereas previously it would stop if it
encountered invalid character for the base).
Finally, accept either "0x" or "0X" to indicate hexadecimal.
This doesn't actually change behavior much, but as long as we're
checking every character in a number for validity we might as well
be restrictive.
Signed-off-by: Alex Elder <elder@linaro.org>
Message-Id: <20211001232338.769309-25-elder@linaro.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2021-10-02 07:23:28 +08:00
|
|
|
}
|
|
|
|
|
2021-10-02 07:23:35 +08:00
|
|
|
/* Extract a number from input into the given buffer; return base */
|
|
|
|
static unsigned qmi_number_parse(char *buf, size_t size, char ch)
|
|
|
|
{
|
2022-05-25 00:39:31 +08:00
|
|
|
int (*isvalid)(int) = isdigit;
|
2021-10-02 07:23:35 +08:00
|
|
|
char *p = buf;
|
2022-05-25 00:39:31 +08:00
|
|
|
unsigned base = 10;
|
2021-10-02 07:23:35 +08:00
|
|
|
|
2022-05-25 00:39:31 +08:00
|
|
|
/* First character is known to be a digit 0-9 */
|
|
|
|
*p++ = ch;
|
|
|
|
|
|
|
|
/* Determine base and valid character set */
|
2021-10-02 07:23:35 +08:00
|
|
|
if (ch == '0') {
|
|
|
|
ch = input();
|
|
|
|
if (ch == 'x' || ch == 'X') {
|
|
|
|
*p++ = ch;
|
|
|
|
ch = input();
|
|
|
|
isvalid = isxdigit;
|
|
|
|
base = 16;
|
2022-05-25 00:39:31 +08:00
|
|
|
} else if (isodigit(ch)) {
|
2021-10-02 07:23:35 +08:00
|
|
|
isvalid = isodigit;
|
|
|
|
base = 8;
|
|
|
|
}
|
2022-05-25 00:39:31 +08:00
|
|
|
unput(ch);
|
2021-10-02 07:23:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
while ((ch = input()) && isvalid(ch)) {
|
|
|
|
if (p - buf == size) {
|
|
|
|
buf[TOKEN_BUF_MIN] = '\0';
|
|
|
|
yyerror("number too long: \"%s...\"", buf);
|
|
|
|
}
|
|
|
|
*p++ = ch;
|
|
|
|
}
|
|
|
|
unput(ch);
|
|
|
|
*p = '\0';
|
|
|
|
|
|
|
|
return base;
|
|
|
|
}
|
|
|
|
|
2018-01-31 08:02:53 +08:00
|
|
|
static struct token yylex()
|
|
|
|
{
|
|
|
|
struct symbol *sym;
|
|
|
|
struct token token = {};
|
2021-10-02 07:23:26 +08:00
|
|
|
unsigned long long num;
|
2021-10-02 07:23:31 +08:00
|
|
|
char buf[TOKEN_BUF_SIZE];
|
2018-01-31 08:02:53 +08:00
|
|
|
int base;
|
2021-10-02 07:23:12 +08:00
|
|
|
char ch;
|
2018-01-31 08:02:53 +08:00
|
|
|
|
2021-10-02 07:23:24 +08:00
|
|
|
while ((ch = input()) && skip(ch))
|
2018-01-31 08:02:53 +08:00
|
|
|
;
|
|
|
|
|
|
|
|
if (isalpha(ch)) {
|
2021-10-02 07:23:35 +08:00
|
|
|
sym = qmi_identifier_parse(buf, sizeof(buf), ch);
|
2018-01-31 08:02:53 +08:00
|
|
|
|
|
|
|
token.str = strdup(buf);
|
2021-10-02 07:23:20 +08:00
|
|
|
if (!token.str)
|
2021-10-02 07:23:21 +08:00
|
|
|
yyerror("strdup() failed in %s(), line %d\n",
|
2021-10-02 07:23:20 +08:00
|
|
|
__func__, __LINE__);
|
2021-10-02 07:23:34 +08:00
|
|
|
if (sym) {
|
2021-10-02 07:23:23 +08:00
|
|
|
token.id = sym->token_id;
|
|
|
|
switch (token.id) {
|
|
|
|
case TOK_MESSAGE:
|
|
|
|
token.num = sym->message_type;
|
|
|
|
break;
|
|
|
|
case TOK_TYPE:
|
|
|
|
token.num = sym->symbol_type;
|
2018-01-31 08:02:53 +08:00
|
|
|
token.qmi_struct = sym->qmi_struct;
|
2021-10-02 07:23:23 +08:00
|
|
|
break;
|
2021-10-02 07:23:38 +08:00
|
|
|
case TOK_VALUE:
|
|
|
|
/* Override token id; use numeric value */
|
|
|
|
token.id = TOK_NUM;
|
|
|
|
token.num = sym->value;
|
|
|
|
break;
|
2021-10-02 07:23:23 +08:00
|
|
|
default:
|
2021-10-02 07:23:34 +08:00
|
|
|
break; /* Others just have id and string */
|
2018-01-31 08:02:53 +08:00
|
|
|
}
|
2021-10-02 07:23:34 +08:00
|
|
|
} else {
|
|
|
|
token.id = TOK_ID; /* Just an identifier */
|
2018-01-31 08:02:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return token;
|
|
|
|
} else if (isdigit(ch)) {
|
2021-10-02 07:23:35 +08:00
|
|
|
base = qmi_number_parse(buf, sizeof(buf), ch);
|
2018-01-31 08:02:53 +08:00
|
|
|
|
2021-10-02 07:23:26 +08:00
|
|
|
errno = 0;
|
|
|
|
num = strtoull(buf, NULL, base);
|
|
|
|
if (errno)
|
|
|
|
yyerror("number %s out of range", buf);
|
|
|
|
|
|
|
|
token.num = num;
|
2018-01-31 08:02:53 +08:00
|
|
|
token.id = TOK_NUM;
|
2021-10-02 07:23:26 +08:00
|
|
|
|
2021-10-02 07:23:14 +08:00
|
|
|
return token;
|
|
|
|
} else if (!ch) {
|
|
|
|
token.id = TOK_EOF;
|
|
|
|
|
2018-01-31 08:02:53 +08:00
|
|
|
return token;
|
|
|
|
}
|
|
|
|
|
|
|
|
token.id = ch;
|
|
|
|
return token;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct token curr_token;
|
|
|
|
|
|
|
|
static void token_init(void)
|
|
|
|
{
|
|
|
|
curr_token = yylex();
|
|
|
|
}
|
|
|
|
|
2021-10-02 07:23:36 +08:00
|
|
|
static bool token_accept(enum token_id token_id, struct token *tok)
|
2018-01-31 08:02:53 +08:00
|
|
|
{
|
2021-10-02 07:23:36 +08:00
|
|
|
if (curr_token.id != token_id)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Be sure to free the token string if caller won't be doing it */
|
|
|
|
if (tok)
|
|
|
|
*tok = curr_token;
|
|
|
|
else if (curr_token.str)
|
|
|
|
free(curr_token.str);
|
|
|
|
|
|
|
|
curr_token = yylex();
|
|
|
|
|
|
|
|
return true;
|
2018-01-31 08:02:53 +08:00
|
|
|
}
|
|
|
|
|
2021-10-02 07:23:22 +08:00
|
|
|
static void token_expect(enum token_id token_id, struct token *tok)
|
2018-01-31 08:02:53 +08:00
|
|
|
{
|
2021-10-02 07:23:37 +08:00
|
|
|
const char *want;
|
|
|
|
|
|
|
|
if (token_accept(token_id, tok))
|
|
|
|
return;
|
|
|
|
|
|
|
|
want = token_name(token_id);
|
|
|
|
if (want)
|
|
|
|
yyerror("expected %s", want);
|
|
|
|
else
|
|
|
|
yyerror("expected '%c'", token_id);
|
2018-01-31 08:02:53 +08:00
|
|
|
}
|
|
|
|
|
2021-10-02 07:23:29 +08:00
|
|
|
static void qmi_package_parse(void)
|
2018-01-31 08:02:53 +08:00
|
|
|
{
|
|
|
|
struct token tok;
|
|
|
|
|
2021-10-02 07:23:18 +08:00
|
|
|
token_expect(TOK_ID, &tok);
|
2018-01-31 08:02:53 +08:00
|
|
|
token_expect(';', NULL);
|
2021-10-02 07:23:29 +08:00
|
|
|
|
|
|
|
if (qmi_package)
|
|
|
|
yyerror("package may only be specified once");
|
|
|
|
qmi_package = tok.str;
|
2018-01-31 08:02:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void qmi_const_parse()
|
|
|
|
{
|
2021-10-02 07:23:30 +08:00
|
|
|
struct qmi_const *qcm;
|
2018-01-31 08:02:53 +08:00
|
|
|
struct qmi_const *qc;
|
|
|
|
struct token num_tok;
|
|
|
|
struct token id_tok;
|
|
|
|
|
|
|
|
token_expect(TOK_ID, &id_tok);
|
|
|
|
token_expect('=', NULL);
|
|
|
|
token_expect(TOK_NUM, &num_tok);
|
|
|
|
token_expect(';', NULL);
|
|
|
|
|
2021-10-02 07:23:30 +08:00
|
|
|
list_for_each_entry(qcm, &qmi_consts, node)
|
|
|
|
if (!strcmp(qcm->name, id_tok.str))
|
|
|
|
yyerror("duplicate constant \"%s\"", qcm->name);
|
|
|
|
|
2021-10-02 07:23:20 +08:00
|
|
|
qc = memalloc(sizeof(struct qmi_const));
|
2018-01-31 08:02:53 +08:00
|
|
|
qc->name = id_tok.str;
|
|
|
|
qc->value = num_tok.num;
|
|
|
|
|
|
|
|
list_add(&qmi_consts, &qc->node);
|
2021-10-02 07:23:38 +08:00
|
|
|
|
|
|
|
symbol_add(qc->name, TOK_VALUE, qc->value);
|
2018-01-31 08:02:53 +08:00
|
|
|
}
|
|
|
|
|
2018-01-31 08:18:13 +08:00
|
|
|
static void qmi_message_parse(enum message_type message_type)
|
2018-01-31 08:10:54 +08:00
|
|
|
{
|
|
|
|
struct qmi_message_member *qmm;
|
|
|
|
struct qmi_message *qm;
|
|
|
|
struct token msg_id_tok;
|
|
|
|
struct token type_tok;
|
|
|
|
struct token num_tok;
|
|
|
|
struct token id_tok;
|
2021-10-02 07:23:25 +08:00
|
|
|
unsigned array_size;
|
|
|
|
bool array_fixed;
|
2018-01-31 08:10:54 +08:00
|
|
|
bool required;
|
|
|
|
|
|
|
|
token_expect(TOK_ID, &msg_id_tok);
|
|
|
|
token_expect('{', NULL);
|
|
|
|
|
2021-10-02 07:23:20 +08:00
|
|
|
qm = memalloc(sizeof(struct qmi_message));
|
2018-01-31 08:10:54 +08:00
|
|
|
qm->name = msg_id_tok.str;
|
|
|
|
qm->type = message_type;
|
|
|
|
list_init(&qm->members);
|
|
|
|
|
|
|
|
while (!token_accept('}', NULL)) {
|
|
|
|
if (token_accept(TOK_REQUIRED, NULL))
|
|
|
|
required = true;
|
|
|
|
else if (token_accept(TOK_OPTIONAL, NULL))
|
|
|
|
required = false;
|
|
|
|
else
|
|
|
|
yyerror("expected required, optional or '}'");
|
|
|
|
|
|
|
|
token_expect(TOK_TYPE, &type_tok);
|
|
|
|
token_expect(TOK_ID, &id_tok);
|
|
|
|
|
|
|
|
if (token_accept('[', NULL)) {
|
2018-02-01 04:59:05 +08:00
|
|
|
token_expect(TOK_NUM, &num_tok);
|
|
|
|
array_size = num_tok.num;
|
2018-01-31 08:10:54 +08:00
|
|
|
token_expect(']', NULL);
|
2018-02-01 04:59:05 +08:00
|
|
|
array_fixed = true;
|
2021-10-02 07:23:25 +08:00
|
|
|
} else if (token_accept('(', NULL)) {
|
2018-02-01 04:59:05 +08:00
|
|
|
token_expect(TOK_NUM, &num_tok);
|
|
|
|
array_size = num_tok.num;
|
|
|
|
token_expect(')', NULL);
|
2021-10-02 07:23:25 +08:00
|
|
|
array_fixed = false;
|
2018-02-01 04:59:05 +08:00
|
|
|
} else {
|
|
|
|
array_size = 0;
|
2021-10-02 07:23:25 +08:00
|
|
|
array_fixed = false;
|
2018-01-31 08:10:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
token_expect('=', NULL);
|
|
|
|
token_expect(TOK_NUM, &num_tok);
|
|
|
|
token_expect(';', NULL);
|
|
|
|
|
2021-10-02 07:23:30 +08:00
|
|
|
list_for_each_entry(qmm, &qm->members, node) {
|
|
|
|
if (!strcmp(qmm->name, id_tok.str))
|
|
|
|
yyerror("duplicate message member \"%s\"",
|
|
|
|
qmm->name);
|
2021-10-09 05:04:48 +08:00
|
|
|
if (qmm->id == num_tok.num)
|
2021-10-02 07:23:30 +08:00
|
|
|
yyerror("duplicate message member number %u",
|
|
|
|
qmm->id);
|
|
|
|
}
|
|
|
|
|
2021-10-02 07:23:20 +08:00
|
|
|
qmm = memalloc(sizeof(struct qmi_message_member));
|
2018-01-31 08:10:54 +08:00
|
|
|
qmm->name = id_tok.str;
|
|
|
|
qmm->type = type_tok.num;
|
2021-10-02 07:23:09 +08:00
|
|
|
if (type_tok.str)
|
|
|
|
free(type_tok.str);
|
2018-01-31 08:10:54 +08:00
|
|
|
qmm->qmi_struct = type_tok.qmi_struct;
|
|
|
|
qmm->id = num_tok.num;
|
|
|
|
qmm->required = required;
|
2018-02-01 04:59:05 +08:00
|
|
|
qmm->array_size = array_size;
|
|
|
|
qmm->array_fixed = array_fixed;
|
2018-01-31 08:10:54 +08:00
|
|
|
|
|
|
|
list_add(&qm->members, &qmm->node);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (token_accept('=', NULL)) {
|
|
|
|
token_expect(TOK_NUM, &num_tok);
|
|
|
|
|
|
|
|
qm->msg_id = num_tok.num;
|
|
|
|
}
|
|
|
|
|
|
|
|
token_expect(';', NULL);
|
|
|
|
|
|
|
|
list_add(&qmi_messages, &qm->node);
|
|
|
|
}
|
|
|
|
|
2018-01-31 08:18:13 +08:00
|
|
|
static void qmi_struct_parse(void)
|
2018-01-31 08:13:06 +08:00
|
|
|
{
|
|
|
|
struct qmi_struct_member *qsm;
|
|
|
|
struct token struct_id_tok;
|
|
|
|
struct qmi_struct *qs;
|
|
|
|
struct token type_tok;
|
|
|
|
struct token id_tok;
|
|
|
|
|
|
|
|
token_expect(TOK_ID, &struct_id_tok);
|
|
|
|
token_expect('{', NULL);
|
|
|
|
|
2021-10-02 07:23:20 +08:00
|
|
|
qs = memalloc(sizeof(struct qmi_struct));
|
2018-01-31 08:13:06 +08:00
|
|
|
qs->name = struct_id_tok.str;
|
|
|
|
list_init(&qs->members);
|
|
|
|
|
|
|
|
while (token_accept(TOK_TYPE, &type_tok)) {
|
|
|
|
token_expect(TOK_ID, &id_tok);
|
|
|
|
token_expect(';', NULL);
|
|
|
|
|
2021-10-02 07:23:30 +08:00
|
|
|
list_for_each_entry(qsm, &qs->members, node)
|
|
|
|
if (!strcmp(qsm->name, id_tok.str))
|
|
|
|
yyerror("duplicate struct member \"%s\"",
|
|
|
|
qsm->name);
|
|
|
|
|
2021-10-02 07:23:20 +08:00
|
|
|
qsm = memalloc(sizeof(struct qmi_struct_member));
|
2018-01-31 08:13:06 +08:00
|
|
|
qsm->name = id_tok.str;
|
|
|
|
qsm->type = type_tok.num;
|
2021-10-02 07:23:09 +08:00
|
|
|
if (type_tok.str)
|
|
|
|
free(type_tok.str);
|
2018-01-31 08:13:06 +08:00
|
|
|
|
|
|
|
list_add(&qs->members, &qsm->node);
|
|
|
|
}
|
|
|
|
|
|
|
|
token_expect('}', NULL);
|
|
|
|
token_expect(';', NULL);
|
|
|
|
|
|
|
|
list_add(&qmi_structs, &qs->node);
|
|
|
|
|
|
|
|
symbol_add(qs->name, TOK_TYPE, TYPE_STRUCT, qs);
|
|
|
|
}
|
2018-01-31 08:10:54 +08:00
|
|
|
|
2018-01-31 08:02:53 +08:00
|
|
|
void qmi_parse(void)
|
|
|
|
{
|
|
|
|
struct token tok;
|
|
|
|
|
|
|
|
/* PACKAGE ID<string> ';' */
|
|
|
|
/* CONST ID<string> '=' NUM<num> ';' */
|
|
|
|
/* STRUCT ID<string> '{' ... '}' ';' */
|
|
|
|
/* TYPE<type*> ID<string> ';' */
|
|
|
|
/* MESSAGE ID<string> '{' ... '}' ';' */
|
|
|
|
/* (REQUIRED | OPTIONAL) TYPE<type*> ID<string> '=' NUM<num> ';' */
|
|
|
|
|
|
|
|
symbol_add("const", TOK_CONST);
|
|
|
|
symbol_add("optional", TOK_OPTIONAL);
|
|
|
|
symbol_add("message", TOK_MESSAGE, MESSAGE_RESPONSE); /* backward compatible with early hacking */
|
|
|
|
symbol_add("request", TOK_MESSAGE, MESSAGE_REQUEST);
|
|
|
|
symbol_add("response", TOK_MESSAGE, MESSAGE_RESPONSE);
|
|
|
|
symbol_add("indication", TOK_MESSAGE, MESSAGE_INDICATION);
|
|
|
|
symbol_add("package", TOK_PACKAGE);
|
|
|
|
symbol_add("required", TOK_REQUIRED);
|
|
|
|
symbol_add("struct", TOK_STRUCT);
|
|
|
|
symbol_add("string", TOK_TYPE, TYPE_STRING);
|
|
|
|
symbol_add("u8", TOK_TYPE, TYPE_U8);
|
|
|
|
symbol_add("u16", TOK_TYPE, TYPE_U16);
|
|
|
|
symbol_add("u32", TOK_TYPE, TYPE_U32);
|
|
|
|
symbol_add("u64", TOK_TYPE, TYPE_U64);
|
|
|
|
|
|
|
|
token_init();
|
2021-10-02 07:23:14 +08:00
|
|
|
while (!token_accept(TOK_EOF, NULL)) {
|
2018-01-31 08:02:53 +08:00
|
|
|
if (token_accept(TOK_PACKAGE, NULL)) {
|
2021-10-02 07:23:29 +08:00
|
|
|
qmi_package_parse();
|
2018-01-31 08:02:53 +08:00
|
|
|
} else if (token_accept(TOK_CONST, NULL)) {
|
|
|
|
qmi_const_parse();
|
|
|
|
} else if (token_accept(TOK_STRUCT, NULL)) {
|
|
|
|
qmi_struct_parse();
|
|
|
|
} else if (token_accept(TOK_MESSAGE, &tok)) {
|
|
|
|
qmi_message_parse(tok.num);
|
2021-10-02 07:23:10 +08:00
|
|
|
free(tok.str);
|
2018-01-31 08:02:53 +08:00
|
|
|
} else {
|
|
|
|
yyerror("unexpected symbol");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-10-02 07:23:29 +08:00
|
|
|
|
|
|
|
/* The package name must have been specified */
|
|
|
|
if (!qmi_package)
|
|
|
|
yyerror("package not specified");
|
2018-01-31 08:02:53 +08:00
|
|
|
}
|