Skip to content
Snippets Groups Projects
Select Git revision
  • main default protected
  • cont-frb/moberg-review
  • v0.9.26
  • v0.9.25
  • v0.9.24
  • v0.9.23
  • v0.9.22
  • v0.9.21
  • v0.9.20
  • v0.9.19
  • v0.9.18
  • v0.9.17
  • v0.9.16
  • v0.9.15
  • v0.9.14
  • v0.9.13
  • v0.9.12
  • v0.9.11
  • v0.9.10
  • v0.9.9
  • v0.9.8
  • v0.9.7
22 results

moberg_parser.c

Blame
  • moberg_parser.c 10.05 KiB
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <moberg_config.h>
    #include <moberg_parser.h>
    #include <moberg_module.h>
    #include <moberg_device.h>
    
    typedef enum moberg_parser_token_kind kind_t;
    typedef struct moberg_parser_token token_t;
    typedef struct moberg_parser_ident ident_t;
    
    #define MAX_EXPECTED 10
    
    typedef struct moberg_parser_context {
      struct moberg_config *config;
      const char *buf; /* Pointer to data to be parsed */
      const char *p;   /* current parse location */
      token_t token;
      struct {
        int n;
        const char *what[MAX_EXPECTED];
      } expected;
    } context_t;
    
    
    static inline int acceptsym(context_t *c,
    			   kind_t kind,
    			   token_t *token)
    {
      return moberg_parser_acceptsym(c, kind, token);
    }
      
    static inline int acceptkeyword(context_t *c,
    				const char *keyword)
    {
      return moberg_parser_acceptkeyword(c, keyword);
    }
     
    static const void nextsym_ident(context_t *c)
    {
      c->token.kind = tok_IDENT;
      c->token.u.idstr.length = 1;
      c->token.u.idstr.value = c->p;
      c->p++;
      while (*c->p) {
        switch (*c->p) {
          case 'a'...'z':
          case 'A'...'Z':
          case '0'...'9':
          case '_':
            c->p++;
            c->token.u.idstr.length++;
            break;
          default:
            return;
        }
      }
    }
    
    static const void nextsym_integer(context_t *c)
    {
      c->token.kind = tok_INTEGER;
      c->token.u.integer.value = 0;
      while (*c->p && '0' <= *c->p && *c->p <= '9') {
        c->token.u.integer.value *= 10;
        c->token.u.integer.value += *c->p - '0';
        c->p++;
      }
    }
    
    static const void nextsym_string(context_t *c)
    {
      c->token.kind = tok_STRING;
      c->p++;
      c->token.u.idstr.value = c->p;
      c->token.u.idstr.length = 0;
      while (*c->p && *c->p != '"') {
        if (*c->p == '\\') {
          c->token.u.idstr.length++;
          c->p++;
        }
        if (*c->p) {
          c->token.u.idstr.length++;
          c->p++;
        }
      }
      c->p++;
    }
    
    static int nextsym(context_t *c)
    {
      c->token.kind = tok_none;
      while (c->p && *c->p && c->token.kind == tok_none) {
        if (c->p[0] == '/' && c->p[1] == '*') {
          /* Skip comment */
          c->p += 2;
          while (*c->p && (c->p[0] != '*' || c->p[1] != '/')) {
            c->p++;
          }
          c->p += 2;
          continue;
        }
        switch (*c->p) {
          case ' ':
          case '\t':
          case '\n':
          case '\r':
            /* Skip whitespace */
            c->p++;
            break;
          case '(':
            c->token.kind = tok_LPAREN;
            c->p++;
            break;
          case ')':
            c->token.kind = tok_RPAREN;
            c->p++;
            break;
          case '{':
            c->token.kind = tok_LBRACE;
            c->p++;
            break;
          case '}':
            c->token.kind = tok_RBRACE;
            c->p++;
            break;
          case '[':
            c->token.kind = tok_LBRACKET;
            c->p++;
            break;
          case ']':
            c->token.kind = tok_RBRACKET;
            c->p++;
            break;
          case '=':
            c->token.kind = tok_EQUAL;
            c->p++;
            break;
          case ',':
            c->token.kind = tok_COMMA;
            c->p++;
            break;
          case ':':
            c->token.kind = tok_COLON;
            c->p++;
            break;
          case ';':
            c->token.kind = tok_SEMICOLON;
            c->p++;
            break;
          case '"':
            nextsym_string(c);
            break;
          case 'a'...'z':
          case 'A'...'Z':
          case '_':
            nextsym_ident(c);
            break;
          case '0'...'9':
            nextsym_integer(c);
            break;
          default:
            printf("UNEXPECTED %c\n\n", *c->p);
            c->p++;
            break;
        }
      }
      if (c->token.kind != tok_none) {
        return 1;
      } else {
        c->token.kind = tok_EOF;
        return 0;
      }
    }
    
    static int peeksym(context_t *c,
    		   kind_t kind,
    		   token_t *token)
    {
      if (c->token.kind == kind) {
        if (token) {
          *token = c->token;
        }
        return 1;
      }
      return 0;
    }
    
    int moberg_parser_acceptsym(context_t *c,
                                       kind_t kind,
                                       token_t *token)
    {
      if (c->token.kind == kind) {
        if (token) {
          *token = c->token;
        }
        nextsym(c);
        c->expected.n = 0;
        return 1;
      }
      if (c->expected.n < MAX_EXPECTED) {
        const char *what = NULL;
        switch (kind) {
        case tok_none: break;
          case tok_EOF: what = "<EOF>"; break;
        case tok_LPAREN: what = "("; break;
        case tok_RPAREN: what = ")"; break;
        case tok_LBRACE: what = "{"; break;
        case tok_RBRACE: what = "}"; break;
        case tok_LBRACKET: what = "["; break;
        case tok_RBRACKET: what = "]"; break;
        case tok_EQUAL: what = "="; break;
        case tok_COMMA: what = ","; break;
        case tok_COLON: what = ":"; break;
        case tok_SEMICOLON: what = ";"; break;
        case tok_INTEGER: what = "<INTEGER>"; break;
        case tok_IDENT: what = "<IDENT>"; break;
        case tok_STRING: what = "<STRING>"; break;
        }
        if (what) {
          c->expected.what[c->expected.n] = what;
          c->expected.n++;
        }
      }
      return 0;
    }
    
    int moberg_parser_acceptkeyword(context_t *c,
    				       const char *keyword)
    {
      token_t t;
      if (peeksym(c, tok_IDENT, &t) &&
          strncmp(keyword, t.u.idstr.value, t.u.idstr.length) == 0) {
        nextsym(c);
        c->expected.n = 0;
        return 1;
      }
      if (c->expected.n < MAX_EXPECTED) {
        c->expected.what[c->expected.n] = keyword;
        c->expected.n++;
      }
      return 0;
    }
    
    void moberg_parser_failed(
      struct moberg_parser_context *c,
      FILE *f)
    {
      fprintf(f, "EXPECTED ");
      for (int i = 0 ; i < c->expected.n; i++) {
        if (i > 0) {
          fprintf(f, " | ");
        }
        fprintf(f, "'%s'", c->expected.what[i]);
      }
      fprintf(f, "\nGOT: ");
      switch (c->token.kind) {
        case tok_none: break;
        case tok_EOF: fprintf(f, "<EOF>"); break;
        case tok_LPAREN: fprintf(f, "("); break;
        case tok_RPAREN: fprintf(f, ")"); break;
        case tok_LBRACE: fprintf(f, "{"); break;
        case tok_RBRACE: fprintf(f, "}"); break;
        case tok_LBRACKET: fprintf(f, "["); break;
        case tok_RBRACKET: fprintf(f, "]"); break;
        case tok_EQUAL: fprintf(f, "="); break;
        case tok_COMMA: fprintf(f, ","); break;
        case tok_COLON: fprintf(f, ":"); break;
        case tok_SEMICOLON: fprintf(f, ";"); break;
        case tok_INTEGER:
          fprintf(f, "%d (<INTEGER>)", c->token.u.integer.value);
          break;
        case tok_IDENT:
          fprintf(f, "%.*s (<IDENT>)",
                  c->token.u.idstr.length, c->token.u.idstr.value);
          break;
        case tok_STRING:
          fprintf(f, "\"%.*s'\" (<STRING>)",
                  c->token.u.idstr.length, c->token.u.idstr.value);
          break;
      }
      fprintf(f, "\n%s\n", c->p);
    }
    
    
    static int parse_map_range(context_t *c,
                               int *min,
                               int *max)
    {
      token_t tok_min, tok_max;
      if (! acceptsym(c, tok_LBRACKET, NULL)) { goto syntax_err; }
      if (! acceptsym(c, tok_INTEGER, &tok_min)) { goto syntax_err; }
      if (acceptsym(c, tok_COLON, NULL)) {
        if (! acceptsym(c, tok_INTEGER, &tok_max)) { goto syntax_err; }
      } else {
        tok_max = tok_min;
      }
      if (! acceptsym(c, tok_RBRACKET, NULL)) { goto syntax_err; }
      if (! min && max) {
        fprintf(stderr, "Min or max is NULL\n");
        goto err;
      }
      *min = tok_min.u.integer.value;
      *max = tok_max.u.integer.value;
      return 1;
    syntax_err:
      moberg_parser_failed(c, stderr);
    err:
      return 0;
    }
    
    static int parse_map(context_t *c,
                         struct moberg_device *device)
    {
      enum moberg_channel_kind kind;
      int min, max;
      
      if (acceptkeyword(c, "analog_in")) { kind = chan_ANALOGIN; }
      else if (acceptkeyword(c, "analog_out")) { kind = chan_ANALOGOUT; }
      else if (acceptkeyword(c, "digital_in")) { kind = chan_DIGITALIN; }
      else if (acceptkeyword(c, "digital_out")) { kind = chan_DIGITALOUT; }
      else if (acceptkeyword(c, "encoder_in")) { kind = chan_ENCODERIN; }
      else { goto syntax_err; }
      if (! parse_map_range(c, &min, &max)) { goto syntax_err; }
      if (! acceptsym(c, tok_EQUAL, NULL)) { goto syntax_err; }
      if (! moberg_device_parse_map(device, c, kind, min, max)) {
        goto err;
      }
      if (! acceptsym(c, tok_SEMICOLON, NULL)) { goto syntax_err; }
      return 1;
    syntax_err:
      moberg_parser_failed(c, stderr);
    err:    
      return 0;
    }
    
    static int parse_device(context_t *c,
                            struct moberg_device *device)
    {
      if (! acceptsym(c, tok_LBRACE, NULL)) { goto syntax_err; }
      for (;;) {
        if (acceptkeyword(c, "config")) {
         moberg_device_parse_config(device, c);
        } else if (acceptkeyword(c, "map")) {
          if (! parse_map(c, device)) { goto err; }
        } else if (acceptsym(c, tok_RBRACE, NULL)) {
          break;
        } else {
          goto syntax_err;
        }
      }
      return 1;
    syntax_err:
      moberg_parser_failed(c, stderr);
    err:
      return 0;
    }
    
    static int parse(struct moberg *moberg,
                     context_t *c)
    {
      for (;;) {
        if (acceptsym(c, tok_EOF, NULL)) {
          break;
        } else {
          token_t t;
          struct moberg_device *device;
          
          if (! acceptkeyword(c, "driver")) { goto syntax_err; }
          if (! acceptsym(c, tok_LPAREN, NULL)) { goto syntax_err; }
          if (! acceptsym(c, tok_IDENT, &t)) { goto syntax_err; }
          if (! acceptsym(c, tok_RPAREN, NULL)) { goto syntax_err; }
    
          char *name = strndup(t.u.idstr.value, t.u.idstr.length);
          if (! name) {
            fprintf(stderr, "Failed to allocate driver name '%.*s'\n",
                    t.u.idstr.length, t.u.idstr.value);
            goto err;
          }
          device = moberg_device_new(moberg, name);
          free(name);
          if (! device) { goto err; }
    
          if (! parse_device(c, device)) {
            goto device_free;
          }
          if (! moberg_config_add_device(c->config, device)) {
            goto device_free;
          }
          continue;
        device_free:
          moberg_device_free(device);      
          goto err;
        }
      }
      return 1;
    syntax_err:  
      moberg_parser_failed(c, stderr);
      goto err;
    err:
      return 0;
    }
    
    struct moberg_config *moberg_parse(struct moberg *moberg,
                                       const char *buf)
    {
      context_t context;
    
      context.config = moberg_config_new();
      if (context.config) {
        context.expected.n = 0;
        context.buf = buf;
        context.p = context.buf;
        nextsym(&context);
        if (! parse(moberg, &context)) {
          moberg_config_free(context.config);
          context.config = NULL;
        }
      }
    
      return context.config;
    }