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