summaryrefslogtreecommitdiffstats
path: root/src/parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser.c')
-rw-r--r--src/parser.c263
1 files changed, 263 insertions, 0 deletions
diff --git a/src/parser.c b/src/parser.c
new file mode 100644
index 0000000..df8d80b
--- /dev/null
+++ b/src/parser.c
@@ -0,0 +1,263 @@
+#include <libgen.h>
+
+#include "parser.h"
+#include "util.h"
+
+typedef enum parser_type {
+ TITLE = 0,
+ ITEMS,
+ STEPS,
+} pt;
+
+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;
+ }
+ *type = ITEMS;
+ return rc;
+}
+
+static const int
+parse_item(const char * s, recipe * r, pt * type, char * error)
+{
+ fdebug("^ item\n");
+ if (!strcmp(s, "---")) {
+ *type = STEPS;
+ return 0;
+ }
+
+ /* 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;
+ }
+
+ if (tmp[0] == '/' || tmp[0] == '~') {
+ strcpy(path, tmp);
+ } else {
+ strcpy(path, r->path);
+ strcat(path, "/");
+ strcat(path, tmp);
+ }
+
+ recipe * rr = parse(path, r->filename);
+ 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;
+ }
+
+ 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] == '=') {
+ 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;
+ }
+
+ 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;
+}
+
+recipe *
+parse(char * path, const char * prev)
+{
+ fdebug("Parsing: %s\n", 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");
+ if (!f) 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;
+}