300 lines
7.8 KiB
Plaintext
300 lines
7.8 KiB
Plaintext
|
/*
|
||
|
* Copyright (C) 2007 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
%{
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include "ast.h"
|
||
|
#include "lexer.h"
|
||
|
#include "parser.h"
|
||
|
|
||
|
const char *tokenToString(int token)
|
||
|
{
|
||
|
static char scratch[128];
|
||
|
|
||
|
switch (token) {
|
||
|
case TOK_AND:
|
||
|
return "&&";
|
||
|
case TOK_OR:
|
||
|
return "||";
|
||
|
case TOK_EQ:
|
||
|
return "==";
|
||
|
case TOK_NE:
|
||
|
return "!=";
|
||
|
case TOK_GE:
|
||
|
return ">=";
|
||
|
case TOK_LE:
|
||
|
return "<=";
|
||
|
case TOK_EOF:
|
||
|
return "EOF";
|
||
|
case TOK_EOL:
|
||
|
return "EOL\n";
|
||
|
case TOK_STRING:
|
||
|
snprintf(scratch, sizeof(scratch),
|
||
|
"STRING<%s>", yylval.literalString);
|
||
|
return scratch;
|
||
|
case TOK_IDENTIFIER:
|
||
|
snprintf(scratch, sizeof(scratch), "IDENTIFIER<%s>",
|
||
|
yylval.literalString);
|
||
|
return scratch;
|
||
|
case TOK_WORD:
|
||
|
snprintf(scratch, sizeof(scratch), "WORD<%s>",
|
||
|
yylval.literalString);
|
||
|
return scratch;
|
||
|
default:
|
||
|
if (token > ' ' && token <= '~') {
|
||
|
scratch[0] = (char)token;
|
||
|
scratch[1] = '\0';
|
||
|
} else {
|
||
|
snprintf(scratch, sizeof(scratch), "??? <%d>", token);
|
||
|
}
|
||
|
return scratch;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
typedef struct {
|
||
|
char *value;
|
||
|
char *nextc;
|
||
|
unsigned int alloc_size;
|
||
|
} AmString;
|
||
|
|
||
|
static int addCharToString(AmString *str, char c)
|
||
|
{
|
||
|
if ((unsigned int)(str->nextc - str->value) >= str->alloc_size) {
|
||
|
char *new_value;
|
||
|
unsigned int new_size;
|
||
|
|
||
|
new_size = (str->alloc_size + 1) * 2;
|
||
|
if (new_size < 64) {
|
||
|
new_size = 64;
|
||
|
}
|
||
|
|
||
|
new_value = (char *)realloc(str->value, new_size);
|
||
|
if (new_value == NULL) {
|
||
|
yyerror("out of memory");
|
||
|
return -1;
|
||
|
}
|
||
|
str->nextc = str->nextc - str->value + new_value;
|
||
|
str->value = new_value;
|
||
|
str->alloc_size = new_size;
|
||
|
}
|
||
|
*str->nextc++ = c;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int setString(AmString *str, const char *p)
|
||
|
{
|
||
|
str->nextc = str->value;
|
||
|
while (*p != '\0') {
|
||
|
//TODO: add the whole string at once
|
||
|
addCharToString(str, *p++);
|
||
|
}
|
||
|
return addCharToString(str, '\0');
|
||
|
}
|
||
|
|
||
|
static AmString gStr = { NULL, NULL, 0 };
|
||
|
static int gLineNumber = 1;
|
||
|
static AmArgumentType gArgumentType = AM_UNKNOWN_ARGS;
|
||
|
static const char *gErrorMessage = NULL;
|
||
|
|
||
|
#if AMEND_LEXER_BUFFER_INPUT
|
||
|
static const char *gInputBuffer;
|
||
|
static const char *gInputBufferNext;
|
||
|
static const char *gInputBufferEnd;
|
||
|
|
||
|
# define YY_INPUT(buf, result, max_size) \
|
||
|
do { \
|
||
|
int nbytes = gInputBufferEnd - gInputBufferNext; \
|
||
|
if (nbytes > 0) { \
|
||
|
if (nbytes > max_size) { \
|
||
|
nbytes = max_size; \
|
||
|
} \
|
||
|
memcpy(buf, gInputBufferNext, nbytes); \
|
||
|
gInputBufferNext += nbytes; \
|
||
|
result = nbytes; \
|
||
|
} else { \
|
||
|
result = YY_NULL; \
|
||
|
} \
|
||
|
} while (false)
|
||
|
#endif // AMEND_LEXER_BUFFER_INPUT
|
||
|
|
||
|
%}
|
||
|
|
||
|
%option noyywrap
|
||
|
|
||
|
%x QUOTED_STRING BOOLEAN WORDS
|
||
|
|
||
|
ident [a-zA-Z_][a-zA-Z_0-9]*
|
||
|
word [^ \t\r\n"]+
|
||
|
|
||
|
%%
|
||
|
/* This happens at the beginning of each call to yylex().
|
||
|
*/
|
||
|
if (gArgumentType == AM_WORD_ARGS) {
|
||
|
BEGIN(WORDS);
|
||
|
} else if (gArgumentType == AM_BOOLEAN_ARGS) {
|
||
|
BEGIN(BOOLEAN);
|
||
|
}
|
||
|
|
||
|
/*xxx require everything to be 7-bit-clean, printable characters */
|
||
|
<INITIAL>{
|
||
|
{ident}/[ \t\r\n] {
|
||
|
/* The only token we recognize in the initial
|
||
|
* state is an identifier followed by whitespace.
|
||
|
*/
|
||
|
setString(&gStr, yytext);
|
||
|
yylval.literalString = gStr.value;
|
||
|
return TOK_IDENTIFIER;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
<BOOLEAN>{
|
||
|
{ident} {
|
||
|
/* Non-quoted identifier-style string */
|
||
|
setString(&gStr, yytext);
|
||
|
yylval.literalString = gStr.value;
|
||
|
return TOK_IDENTIFIER;
|
||
|
}
|
||
|
"&&" return TOK_AND;
|
||
|
"||" return TOK_OR;
|
||
|
"==" return TOK_EQ;
|
||
|
"!=" return TOK_NE;
|
||
|
">=" return TOK_GE;
|
||
|
"<=" return TOK_LE;
|
||
|
[<>()!,] return yytext[0];
|
||
|
}
|
||
|
|
||
|
/* Double-quoted string handling */
|
||
|
|
||
|
<WORDS,BOOLEAN>\" {
|
||
|
/* Initial quote */
|
||
|
gStr.nextc = gStr.value;
|
||
|
BEGIN(QUOTED_STRING);
|
||
|
}
|
||
|
|
||
|
<QUOTED_STRING>{
|
||
|
\" {
|
||
|
/* Closing quote */
|
||
|
BEGIN(INITIAL);
|
||
|
addCharToString(&gStr, '\0');
|
||
|
yylval.literalString = gStr.value;
|
||
|
if (gArgumentType == AM_WORD_ARGS) {
|
||
|
return TOK_WORD;
|
||
|
} else {
|
||
|
return TOK_STRING;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
<<EOF>> |
|
||
|
\n {
|
||
|
/* Unterminated string */
|
||
|
yyerror("unterminated string");
|
||
|
return TOK_ERROR;
|
||
|
}
|
||
|
|
||
|
\\\" {
|
||
|
/* Escaped quote */
|
||
|
addCharToString(&gStr, '"');
|
||
|
}
|
||
|
|
||
|
\\\\ {
|
||
|
/* Escaped backslash */
|
||
|
addCharToString(&gStr, '\\');
|
||
|
}
|
||
|
|
||
|
\\. {
|
||
|
/* No other escapes allowed. */
|
||
|
gErrorMessage = "illegal escape";
|
||
|
return TOK_ERROR;
|
||
|
}
|
||
|
|
||
|
[^\\\n\"]+ {
|
||
|
/* String contents */
|
||
|
char *p = yytext;
|
||
|
while (*p != '\0') {
|
||
|
/* TODO: add the whole string at once */
|
||
|
addCharToString(&gStr, *p++);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
<WORDS>{
|
||
|
/*xxx look out for backslashes; escape backslashes and quotes */
|
||
|
/*xxx if a quote is right against a char, we should append */
|
||
|
{word} {
|
||
|
/* Whitespace-separated word */
|
||
|
setString(&gStr, yytext);
|
||
|
yylval.literalString = gStr.value;
|
||
|
return TOK_WORD;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
<INITIAL,WORDS,BOOLEAN>{
|
||
|
\n {
|
||
|
/* Count lines */
|
||
|
gLineNumber++;
|
||
|
gArgumentType = AM_UNKNOWN_ARGS;
|
||
|
BEGIN(INITIAL);
|
||
|
return TOK_EOL;
|
||
|
}
|
||
|
|
||
|
/*xxx backslashes to extend lines? */
|
||
|
/* Skip whitespace and comments.
|
||
|
*/
|
||
|
[ \t\r]+ ;
|
||
|
#.* ;
|
||
|
|
||
|
. {
|
||
|
/* Fail on anything we didn't expect. */
|
||
|
gErrorMessage = "unexpected character";
|
||
|
return TOK_ERROR;
|
||
|
}
|
||
|
}
|
||
|
%%
|
||
|
|
||
|
void
|
||
|
yyerror(const char *msg)
|
||
|
{
|
||
|
if (!strcmp(msg, "syntax error") && gErrorMessage != NULL) {
|
||
|
msg = gErrorMessage;
|
||
|
gErrorMessage = NULL;
|
||
|
}
|
||
|
fprintf(stderr, "line %d: %s at '%s'\n", gLineNumber, msg, yytext);
|
||
|
}
|
||
|
|
||
|
#if AMEND_LEXER_BUFFER_INPUT
|
||
|
void
|
||
|
setLexerInputBuffer(const char *buf, size_t buflen)
|
||
|
{
|
||
|
gLineNumber = 1;
|
||
|
gInputBuffer = buf;
|
||
|
gInputBufferNext = gInputBuffer;
|
||
|
gInputBufferEnd = gInputBuffer + buflen;
|
||
|
}
|
||
|
#endif // AMEND_LEXER_BUFFER_INPUT
|
||
|
|
||
|
void
|
||
|
setLexerArgumentType(AmArgumentType type)
|
||
|
{
|
||
|
gArgumentType = type;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
getLexerLineNumber(void)
|
||
|
{
|
||
|
return gLineNumber;
|
||
|
}
|