summaryrefslogblamecommitdiffstats
path: root/src/parser.c
blob: a5b1a11332ffd07743bce22495d58e8df87d3f8f (plain) (tree)
1
2
3
4
5
6
7
8
9


                   



                                                  

             






                                     
           
   
                          
            

 
   
                                                     
 

                                                



                                 








                                                 





                                                 

                                    
                                         

                        
                                           




                            




                                                 



                                                  
              


                         
 

             
 















                                                               






                                      



                                                     























                                                        




                                                    
                     

                                                          
                    
   
 











                                                    


                                     
                
                                                                            




                                
 













                                                                                   
                                










                                        
 































                                                                   
 







                       

                                 
 
          
 


                           







                                  













                                             















                                        
 












                                                      
#include "parser.h"
#include "util.h"

static const int
parse_title(const char * s, recipe * r, pt * type)
{
  fdebug("^ title\n");
  int rc = 0;

  if (s[0] == '@' && strlen(s) > 1) {
    char tmp[LINE_SIZE] = "";
    strcpy(tmp, ++s);
    trim(tmp);
    r->title = strdup(tmp);
  } else {
    r->title = strdup(r->filename);
    rc = 1;
  }
  if (type) *type = ITEMS;
  return rc;
}

int
try_include(const char * s, recipe * r, char * error)
{
  /* 1 - 9 in ascii */
  if ((s[0] > 48 && s[0] < 58) || s[0] == '!') {
    fdebug("INCLUDING: %s\n", s);

    char tmp[LINE_SIZE] = "";
    char path[LINE_SIZE] = "";

    char *endptr;
    int n = (int)strtol(s, &endptr, 10);
    if (endptr[0] != '!') {
      sprintf(error, "malformed include: %s", s);
      return 1;
    }

    strcpy(tmp, ++endptr);
    trim(tmp);

    if (!strlen(tmp)) {
      sprintf(error, "malformed include: %s", s);
      return 1;
    }

    /* First search for full path */
    if (tmp[0] == '/' || tmp[0] == '~') {
      strcpy(path, tmp);
    } else {
      /* Then search for file local path */
      strcpy(path, r->path);
      strcat(path, "/");
      strcat(path, tmp);
    }

    recipe * rr = NULL;
    rr = parse(path, r->filename);

    /* TODO: Check for path from included libs */

    if (!rr) {
      sprintf(error, "Couldn't include %s", path);
      return 1;
    }
    rr->n = n;
    //copy_items(r, rr);
    new_subrecipe(r, rr);
    //free_recipe(rr);

    return 0;
  }

  return -1;
}

int
parse_item(const char * s, recipe * r, pt * type, char * error)
{
  fdebug("^ item\n");
  if (!strcmp(s, "---")) {
    if (type) *type = STEPS;
    return 0;
  }

  int rc = try_include(s, r, error);
  if (rc != -1)
      return rc;

  int l = strlen(s);
  int val = 1; /* key vs value flag */
  int itemc = 0;
  int c = 0;
  char buf[LINE_SIZE] = "";
  for (int i = 0; i < l; i++) {
    if (val) {
      if (s[i] == ',' || s[i] == '=' || i == l - 1) {
        if (i == l - 1) {
          buf[c++] = s[i];
        }
        buf[c++] = '\0';
        if (!strlen(buf)) {
          sprintf(error, "malformed ingredient: %s", s);
          return 1;
        }
        trim(buf);
        if (strlen(buf)) {
          new_item(r);
          r->i[r->in - 1]->name = strdup(buf);
          itemc++;
          buf[0] = '\0';
          c = 0;
        }
        if (s[i] == '=')
          val = 0;
        continue;
      }
    }
    buf[c++] = s[i];
  }

  buf[c] = '\0';
  trim(buf);

  if (!itemc) {
    sprintf(error, "malformed ingderient: %s", buf);
    return 1;
  }

  if (!strlen(buf)) {
    // sprintf(error, "empty ingredient quantity: %s", s);
    // return 1;
    strcpy(buf, "");
  }

  for (int i = 0; i < itemc; i++) {
    r->i[r->in - 1 - i]->qty = strdup(buf);
  }

  return 0;
}

static const int
parse_step(const char * s, recipe * r, char * error)
{
  fdebug("^ step\n");

  if ((s[0] == '>' ||
       s[0] == '-' ||
       s[0] == '+') && strlen(s+1)) {
    new_step(r);
    r->s[r->sn - 1]->type = s[0] == '>' ? COOK : s[0] == '-' ? PREP : SERVE;
    char *tmp = strdup(s+1);
    trim(tmp);
    r->s[r->sn - 1]->inst = tmp;
    return 0;
  }

  sprintf(error, "malformed step: %s", s);
  return 1;
}

static const int
handle_line(const char * s, recipe * r, char * error, pt * type, const char * prev)
{
  fdebug("Handling line: %s\n", s);

  if (strlen(s) == 0) return 0;
  if (s[0] == '#') return 0;

  switch (*type) {
  case TITLE:
    if (parse_title(s, r, type))
      if (parse_item(s, r, type, error))
        return 1;
    break;
  case ITEMS:
    if (parse_item(s, r, type, error))
      return 1;
    break;
  case STEPS:
    if (parse_step(s, r, error))
      return 1;
  }

  return 0;
}

static const int
next_escaped_line(FILE *f, char * line, int * lino, char * error) {
  if (!fgets(line, LINE_SIZE, f))
    return 1;
  (*lino)++;

  if (line[strlen(line) - 1] != '\n' && !feof(f)) {
    sprintf(error, "line exceeds %d characters", LINE_SIZE);
    return -1;
  }

  trim(line);
  int l = strlen(line);

  /* Last char is `\n` check if    */
  /* (last - 1) is `\`             */
  while (l > 0 && line[l - 1] == '\\') {
    char tmp[LINE_SIZE] = "";

    if (!fgets(tmp, LINE_SIZE, f))
      /* return line on EOF */
      break;
    (*lino)++;

    trim(tmp);
    if (strlen(tmp) + l >= LINE_SIZE) {
      sprintf(error, "line exceeds %d characters", LINE_SIZE);
      return -1;
    }

    /* remove `\` */
    line[l - 1] = '\0';
    strcat(line, tmp);
    l = strlen(line);
  }
  return 0;
}

FILE *
file_from_path(const char * path)
{
  FILE *f;

  if (!strcmp(path, "-")) {
    f = stdin;
  } else {
    char tmp[LINE_SIZE] = "";
    if (path[0] == '~') {
      strcpy(tmp, getenv("HOME"));
      strcat(tmp, path + 1);
    } else {
      strcpy(tmp, path);
    }
    f = fopen(tmp, "r");
  }

  return f;
}

recipe *
parse(char * path, const char * prev)
{
  fdebug("Parsing: %s\n", path);

  FILE *f = file_from_path(path);
  if (!f) {
    fprintf(stderr, "Can't open %s\n", path);
    return NULL;
  }

  recipe * r = new_recipe();
  char line[LINE_SIZE] = "";
  char *dirc, *bname;

  dirc = strdup(path);
  bname = strdup(path);
  r->path = strdup(dirname(dirc));
  r->filename = strdup(basename(bname));
  free(dirc); free(bname);

  /* (re)init px */
  char error[LINE_SIZE] = "";
  int lino = 0;
  enum parser_type type = TITLE;

  while (!next_escaped_line(f, line, &lino, error)) {
    if (handle_line(line, r, error, &type, prev))
      break;
  }

  if (strlen(error) > 0) {
    fprintf(stderr, "%s:%d: %s\n", path, lino, error);
    free_recipe(r);
    return NULL;
  }

  return r;
}