+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "cond.tab.h"
+
+#define BUFSIZE 4096
+#define KLEN 10
+
+struct keys {
+ char* keywords;
+ enum yytokentype keytoks;
+};
+
+static int line;
+
+static struct keys k[] =
+ { {"copy", KW_COPY},
+ {"else", KW_ELSE},
+ {"if", KW_IF},
+ {"mail", KW_MAIL},
+ {"pipe", KW_PIPE}
+ };
+
+void __attribute__ ((noreturn))
+die(char* msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+ vfprintf(stderr, msg, args);
+ fputc('\n', stderr);
+ va_end(args);
+ exit(1);
+}
+
+void*
+xmalloc(size_t size)
+{
+ void* ret;
+
+ if (!(ret = malloc(size)))
+ die("Low memory");
+
+ return ret;
+}
+
+static void __attribute__ ((noreturn))
+parse_err(char* msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+ fprintf(stderr, "Line %d: ", line);
+ vfprintf(stderr, msg, args);
+ fputc('\n', stderr);
+ va_end(args);
+ exit(1);
+}
+
+static char*
+xstrdup(char* s)
+{
+ void* ret;
+
+ if (!(ret = strdup(s)))
+ die("Low memory");
+
+ return ret;
+}
+
+static char*
+get_string_out(int delim)
+{
+ int last = delim;
+ int i = 0;
+ int c;
+ char buf[BUFSIZE];
+
+ while ((c = getchar()) != delim || last == '\\'){
+ if (last=='\\' && c != delim)
+ buf[i-1] = c;
+ else {
+ buf[i] = c;
+ i++;
+ }
+ last = c;
+ if (i >= BUFSIZE-1)
+ parse_err("Too long string, max allowed length is %d",BUFSIZE-1);
+ }
+ buf[i] = '\0';
+
+ return xstrdup(buf);
+}
+
+static int
+is_var_id(int c)
+{
+ return (c >= '0' && c <= '9' ) ||
+ (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ c == '_' ||
+ c == '-';
+}
+
+static int
+is_alpha(int c)
+{
+ return (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z');
+}
+
+int
+yylex(void)
+{
+ int c, nl = 0;
+
+ while ((c = getchar ()) == ' ' || c == '\t' || c =='\n'){
+ if (c == '\n'){
+ nl = 1;
+ line++;
+ }
+ }
+
+ if (nl)
+ return '\n';
+
+ if (c == EOF)
+ return 0;
+
+#define CC(a,b) ((a<<8)|b)
+ int d = getchar();
+ if (d >= 0) {
+ switch (CC(c,d)) {
+ case CC('!','='): return NEQ;
+ case CC('!','~'): return NRE;
+ case CC('<','='): return LE;
+ case CC('>','='): return GE;
+ case CC('=','='): return EQ;
+ case CC('~','~'): return RE;
+ case CC('-','>'): return ARROW;
+ }
+ ungetc(d,stdin);
+ }
+
+ switch (c) {
+ case '!':
+ case '(':
+ case ')':
+ case '-':
+ case '+':
+ case '*':
+ case '/':
+ case '{':
+ case '}':
+ case '<':
+ case '>':
+ case '=':
+ return c;
+
+ case '"':
+ case '\'':
+ yylval.str = get_string_out(c);
+ return CONST;
+ }
+
+ if (c >= '0' && c <= '9'){
+ ungetc(c,stdin);
+ scanf("%d",&yylval.n);
+ return NUM;
+ }
+
+ if (c == '$'){
+ int i = 0;
+ char buf[BUFSIZE];
+
+ while (is_var_id(c = getchar())){
+ buf[i]=c;
+ i++;
+ if (i >= BUFSIZE-1)
+ parse_err("Too long identifier, max allowed length is %d",BUFSIZE-1);
+ }
+ buf[i] = 0;
+ yylval.str = xstrdup(buf);
+
+ return VAR;
+ }
+
+ if (is_alpha(c)){
+ char buf[KLEN];
+ int n, i = 0;
+
+ ungetc(c,stdin);
+ while (is_alpha(c = getchar())){
+ buf[i++] = c;
+ if (i >= KLEN)
+ parse_err("Keyword too long");
+ }
+ buf[i] = 0;
+
+ n = (sizeof(k)/sizeof(struct keys));
+ for (i = 0; i < n; i++){
+ if (!strcmp(buf,k[i].keywords))
+ return k[i].keytoks;
+ }
+
+ parse_err("Unknown keyword %s", buf);
+ }
+
+ parse_err("Unknown character %c", c);
+}