%{ /* * mgllex.l - Lexer for Bootrom Menu Generation Language * * Copyright (C) 1997-2003 Gero Kuhlmann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: mgllex.l,v 1.4 2003/01/25 23:29:44 gkminix Exp $ */ #include "mknbi.h" #include "mgl.h" #include "gencode.h" #include "y.tab.h" /* ******************************************************************** * * Definition of builtin data types. We need this here for the definition * of internal functions. */ /* No type - used to catch errors in type definitions */ struct typesdef none_type = { EXPR_NONE, 0, { { 0, 0, -1 } }, NULL }; /* Integer type */ struct typesdef int_type = { EXPR_NUM, 2, { { -MAX_INT, MAX_INT, -1 } }, &none_type }; /* String type */ struct typesdef string_type = { EXPR_STRING, MAX_STR_LEN, { { 0, 0, -1 } }, &int_type }; /* Type for string index */ struct typesdef strindex_type = { EXPR_NUM, 2, { { 0, 255, -1 } }, & string_type }; /* Character type */ struct typesdef char_type = { EXPR_CHAR, 1, { { 0, MAX_CHAR, -1 } }, &strindex_type }; /* Boolean type */ struct typesdef bool_type = { EXPR_BOOL, 1, { { 0, 1, -1 } }, &char_type }; /* Table of names for predefined types */ struct { char *name; struct typesdef *t; } predeftypes[] = { { "integer", &int_type }, { "string", &string_type }, { "char", &char_type }, { "boolean", &bool_type }, { "logical", &bool_type }, { NULL, NULL } }; /* ******************************************************************** * * List of internally defined constants */ struct { char *name; char *strval; int numval; struct typesdef *t; } predefconsts[] = { { "maxint", NULL, MAX_INT, &int_type }, { "true", NULL, 1, &bool_type }, { "false", NULL, 0, &bool_type }, { "BOOTP_HOSTNAME", NULL, 12, &int_type }, { "BOOTP_DOMAIN", NULL, 15, &int_type }, { "BOOTP_ROOTPATH", NULL, 17, &int_type }, { NULL, NULL, 0, NULL } }; /* ******************************************************************** * * List of internally defined variables. */ struct { char *name; long addr; struct typesdef *t; } predefvars[] = { { "servername", ADDR_SERVERNAME, &string_type }, { "hostname", ADDR_HOSTNAME, &string_type }, { NULL, 0, NULL } }; /* ******************************************************************** * * Definition of internal functions. To add a new internal function, * simply assign a new CMD_* value to pass to the runtime module, and * add a new entry here. You might optionally add a collapsing function * into mglparse.y for constant arguments. */ struct { char *name; struct funcdef f; } functab[] = { /* This is a general scalar operation - int types are just dummies */ { "pred", { 0, 0, CMD_PRED, 1, &int_type, { &int_type }, { ATTR_NONE } } }, /* This is a general scalar operation - int types are just dummies */ { "succ", { 0, 0, CMD_SUCC, 1, &int_type, { &int_type }, { ATTR_NONE } } }, /* This is a general scalar operation - int types are just dummies */ { "ord", { 0, 0, CMD_ORD, 1, &int_type, { &int_type }, { ATTR_NONE } } }, { "odd", { 0, 0, CMD_ODD, 1, &bool_type, { &int_type }, { ATTR_NONE } } }, { "chr", { 0, 0, CMD_CHR, 1, &char_type, { &int_type }, { ATTR_NONE } } }, { "strlen", { 0, 0, CMD_STRLEN, 1, &int_type, { &string_type }, { ATTR_CONST } } }, { "strsub", { 0, 0, CMD_STRSUB, 3, &string_type, { &string_type, &int_type, &int_type }, { ATTR_CONST, ATTR_NONE, ATTR_NONE } } }, { "abs", { 0, 0, CMD_ABS, 1, &int_type, { &int_type }, { ATTR_NONE } } }, { "sqr", { 0, 0, CMD_SQR, 1, &int_type, { &int_type }, { ATTR_NONE } } }, { "cls", { 0, 0, CMD_CLS, 0, NULL, { NULL }, { ATTR_NONE } } }, { NULL, { 0, 0, CMD_NONE, 0, NULL, { NULL }, { ATTR_NONE } } } }; /* ******************************************************************** * * Some symbols are used internally by the lexer, and just return a * token to the parser. This way, we can redefine the symbols in the * user program, and still use them as tokens in the parser. */ struct { char *name; int token; } specialtab[] = { { "at", AT }, { "from", FROM }, { "gateway", GATEWAY }, { "get", GET }, { "gotoxy", GOTOXY }, { "load", LOAD }, { "menu", MENU }, { "print", PRINT }, { "timeout", TIMEOUT }, { NULL, 0 } }; /* ******************************************************************** * * Global variables */ char *curfile; /* Name of current input file */ /* ******************************************************************** * * String buffer for C style strings */ static char string_buf[MAX_STR_LEN + 1]; /* string buffer */ static char *string_pos; /* current string position */ static int string_flag; /* flag if string too long */ /* ******************************************************************** * * Routine to add a character to the constant string */ static void add_string(c) char c; { if (string_pos >= &string_buf[MAX_STR_LEN]) { if (!string_flag) { warning("string too long, truncating"); string_flag++; } } else *string_pos++ = c; } %} /* ******************************************************************** * * Define some generally useful regular expressions */ ws [ \t]+ comment #.* id [a-zA-Z][a-zA-Z0-9_]* intnum [0-9]+ hexnum [0-9a-fA-F]+ nl \n cr \r /* ******************************************************************** * * Always use a pointer for yytext */ %pointer /* ******************************************************************** * * Define an exclusive start state for scanning C style strings */ %x Cstring Comment %% /* ******************************************************************** * * Skip all whitespace and comments */ {ws} ; {comment} ; "(*" { BEGIN Comment; } /* Switch to comment mode */ . ; /* Throw away any comment text */ {nl} { lineno++; } /* We still have to count line numbers */ "*)" { BEGIN INITIAL; } /* Return to regular mode */ /* ******************************************************************** * * Parse an IP number */ {intnum}\.{intnum}\.{intnum}\.{intnum} { #ifdef HAVE_INET static struct in_addr addr; if ((unsigned long)(addr.s_addr = inet_addr(yytext)) == (unsigned long)-1) { warning("invalid IP address, assuming 0.0.0.0"); addr.s_addr = INADDR_ANY; } #else static char addr[INADDR_SIZE]; warning("no IP address support available, assuming 0.0.0.0"); memset(addr, 0, INADDR_SIZE); #endif yylval.inaddr = (char *)&addr; return(INADDR); } /* ******************************************************************** * * Parse an integer number */ {intnum} { /* Decimal number */ char *cp; unsigned long result = 0; for (cp = yytext; *cp; cp++) { result = result * 10 + *cp - '0'; if (result > MAX_INT) { warning("integer number too large, assuming maximum"); result = MAX_INT; break; } } yylval.intarg = result; return(NUM); } ${hexnum} { /* Hex number */ char *cp; unsigned long result = 0; for (cp = yytext; *cp; cp++) { if (*cp >= '0' && *cp <= '9') result = result * 16 + *cp - '0'; else if (*cp >= 'a' && *cp <= 'f') result = result * 16 + *cp + 10 - 'a'; else if (*cp >= 'A' && *cp <= 'F') result = result * 16 + *cp + 10 - 'F'; if (result > MAX_INT) { warning("integer number too large, assuming maximum"); result = MAX_INT; break; } } yylval.intarg = result; return(NUM); } /* ******************************************************************** * * Parse a C type string */ \" { string_flag = 0; string_pos = string_buf; BEGIN Cstring; } \" { /* All done at closing quote */ BEGIN INITIAL; *string_pos = '\0'; yylval.string = string_buf; return(QSTRING); } \n { /* Unterminated string constant */ BEGIN INITIAL; warning("unterminated string constant"); *string_pos = '\0'; yylval.string = string_buf; return(QSTRING); } \\[0-7]{1,3} { /* Octal escape sequence */ int result; if (sscanf(yytext + 1, "%o", &result) != 1) { warning("invalid octal number in string constant, " "assuming \\377"); result = '\377'; } if (result > 0xff) { warning("octal number in string constant too large, " "assuming \\377"); result = '\377'; } add_string(result); } \\[0-9]+ { /* Bad escape sequence */ warning("bad escape sequence, skipping"); } \\n { add_string('\n'); } \\t { add_string('\t'); } \\r { add_string('\r'); } \\b { add_string('\b'); } \\f { add_string('\f'); } \\(.|n) { add_string(yytext[1]); } [^\\\n\"]+ { char *yptr = yytext; while (*yptr) add_string(*yptr++); } /* ******************************************************************** * * Parse a C type character value */ \'\\[0-7]{1,3}\' { /* Octal escape sequence */ int result; yytext[strlen(yytext) - 1] = '\0'; if (sscanf(yytext + 2, "%o", &result) != 1) { warning("invalid octal number in character constant, " "assuming \\377"); result = '\377'; } if (result > 0xff) { warning("octal number in character constant too large, " "assuming \\377"); result = '\377'; } yylval.chrarg = result & 0xff; return(CHR); } \'\\[0-9]+\' { /* Bad escape sequence */ warning("bad escape sequence, assuming \\377"); yylval.chrarg = 0xff; return(CHR); } \'\\n\' { yylval.chrarg = '\n'; return(CHR); } \'\\t\' { yylval.chrarg = '\t'; return(CHR); } \'\\r\' { yylval.chrarg = '\r'; return(CHR); } \'\\b\' { yylval.chrarg = '\b'; return(CHR); } \'\\f\' { yylval.chrarg = '\f'; return(CHR); } \'\\(.|n)\' { yylval.chrarg = yytext[2]; return(CHR); } \'.\' { yylval.chrarg = yytext[1]; return(CHR); } \'. { warning("missing closing quote in character constant"); yylval.chrarg = yytext[1]; return(CHR); } /* ******************************************************************** * * Rules to parse reserved names */ array { return(ARRAY); } begin { return(CBEGIN); } break { return(BREAK); } const { return(CONST); } default { return(DEFAULT); } do { return(DO); } else { return(ELSE); } end { return(END); } function { return(FUNCTION); } if { return(IF); } item { return(ITEM); } of { return(OF); } procedure { return(PROCEDURE); } record { return(RECORD); } repeat { return(REPEAT); } restart { return(RESTART); } return { return(RETURN); } screen { return(SCREEN); } select { return(SELECT); } then { return(THEN); } type { return(TYPE); } until { return(UNTIL); } var { return(VAR); } while { return(WHILE); } with { return(WITH); } ":=" { return(ASSIGN); } "..." { return(DOTS); } /* ******************************************************************** * * Logical operations */ and { yylval.op = CMD_AND; return(ANDOP); } not { yylval.op = CMD_NOT; return(NOTOP); } or { yylval.op = CMD_OR; return(OROP); } xor { yylval.op = CMD_XOR; return(XOROP); } /* ******************************************************************** * * Comparison rules */ "=" { yylval.op = CMD_EQ; return(COMPARISON); } ">" { yylval.op = CMD_GT; return(COMPARISON); } ">=" { yylval.op = CMD_GE; return(COMPARISON); } "<" { yylval.op = CMD_LT; return(COMPARISON); } "<=" { yylval.op = CMD_LE; return(COMPARISON); } "<>" { yylval.op = CMD_NE; return(COMPARISON); } /* ******************************************************************** * * Arithmetic operations */ "-" { yylval.op = '-'; return(ADDOP); } "+" { yylval.op = '+'; return(ADDOP); } "*" { yylval.op = '*'; return(MULOP); } "/" { yylval.op = '/'; return(MULOP); } div { yylval.op = '/'; return(MULOP); } "%" { yylval.op = '%'; return(MULOP); } mod { yylval.op = '%'; return(MULOP); } /* ******************************************************************** * * Symbol names */ {id} { struct sym *sp; yylval.symbol = NULL; if (strlen(yytext) > MAX_ID_LEN) { warning("ID too long, truncating to 16 characters"); yytext[MAX_ID_LEN] = '\0'; } /* Search if symbol already exists */ for (sp = symtab; sp != NULL; sp = sp->next) if (!strcmp(sp->name, yytext)) break; /* If it's a special symbol, return it's token */ if (isspecialsym(sp)) return(sp->def.s.token); /* If symbol doesn't exist, create it. */ if (sp == NULL) { sp = (struct sym *)nbmalloc(sizeof(struct sym)); sp->type = nosym; sp->level = curlevel; copystr(&(sp->name), yytext); sp->next = symtab; symtab = sp; } /* Return the symbol to the parser */ yylval.symbol = sp; return(ID); } /* ******************************************************************** * * Misc. rules: count lines and ignore \r, return all other * characters as is to the parser. */ {cr} ; {nl} { lineno++; } . { return(yytext[0]); } %% /* ***************************************************************************** * * Print current token. This has to be here because the definition of yytext * is in the lexer, and it's not consistent among different lex versions. */ void print_token() { fprintf(stderr, "'%s'", yytext); } /* ***************************************************************************** * * Initialize lexer */ void yylexinit(infile) char *infile; { struct sym *sp; long size; int i; /* Open input file */ if (infile == NULL || !*infile) yyin = stdin; else if ((yyin = fopen(infile, "r")) == NULL) { perror(progname); exit(EXIT_MGL_PROGOPEN); } /* Set name of current input file */ curfile = infile; /* Preset the list of known types with builtin static type list */ typetab = &bool_type; for (i = 0; predeftypes[i].name != NULL; i++) { sp = (struct sym *)nbmalloc(sizeof(struct sym)); sp->type = typesym; sp->name = predeftypes[i].name; sp->level = -1; sp->def.t = predeftypes[i].t; sp->next = symtab; symtab = sp; } /* The string type is special */ string_type.def.a.elementnum = MAX_STR_LEN; string_type.def.a.indextype = &strindex_type; string_type.def.a.basetype = &char_type; /* Preset the list of internal functions */ for (i = 0; functab[i].name != NULL; i++) { sp = (struct sym *)nbmalloc(sizeof(struct sym)); sp->type = funcsym; sp->name = functab[i].name; sp->level = -1; sp->def.f = functab[i].f; sp->next = symtab; symtab = sp; } /* Preset the list of special symbols */ for (i = 0; specialtab[i].name != NULL; i++) { sp = (struct sym *)nbmalloc(sizeof(struct sym)); sp->type = specialsym; sp->name = specialtab[i].name; sp->level = -1; sp->def.s.token = specialtab[i].token; sp->next = symtab; symtab = sp; } /* Preset the list of internal constants */ for (i = 0; predefconsts[i].name != NULL; i++) { sp = (struct sym *)nbmalloc(sizeof(struct sym)); sp->type = constsym; sp->name = predefconsts[i].name; sp->level = -1; sp->def.c.t = predefconsts[i].t; switch (predefconsts[i].t->type) { case EXPR_NUM: sp->def.c.val.i = predefconsts[i].numval; break; case EXPR_BOOL: sp->def.c.val.b = predefconsts[i].numval; break; case EXPR_CHAR: sp->def.c.val.c = predefconsts[i].numval; break; default: break; } sp->next = symtab; symtab = sp; } /* Preset the list of internally defined variables */ for (i = 0; predefvars[i].name != NULL; i++) { #ifdef PARANOID if (dataptr != predefvars[i].addr) interror(118, "internal variable address doesn't match default"); #endif size = predefvars[i].t->size; if (predefvars[i].t->type == EXPR_STRING) size++; dataptr += (size + 1) & 0xfffe; sp = (struct sym *)nbmalloc(sizeof(struct sym)); sp->type = varsym; sp->name = predefvars[i].name; sp->addr = predefvars[i].addr; sp->level = -1; sp->def.v.t = predefvars[i].t; sp->def.v.attr = ATTR_CONST; sp->next = symtab; symtab = sp; } } /* ***************************************************************************** * * Terminate lexer */ void yylexterm() { /* Close input file */ if (curfile != NULL && *curfile) fclose(yyin); } /* ***************************************************************************** * * We don't want to need to use a library, so define yywrap here, if it's * not a macro. */ #ifndef yywrap int yywrap() { return(1); } #endif