From 382da0ada99a2874052f0e6ccb2c25e012d47fc9 Mon Sep 17 00:00:00 2001 From: gramanas Date: Sat, 13 Nov 2021 21:05:38 +0200 Subject: Update Makefile and add list ingredients draft --- Makefile | 44 +++---- eval.c | 29 ----- eval.h | 8 -- food.c | 137 ++++++++++++++++++++++ lib/include.rcp | 1 + lib/pasta-red-sauce.rcp | 2 +- lib/simple_omelette.rcp | 2 +- main.c | 128 --------------------- parser.c | 263 ------------------------------------------ parser.h | 14 --- src/eval.c | 21 ++++ src/eval.h | 8 ++ src/parser.c | 263 ++++++++++++++++++++++++++++++++++++++++++ src/parser.h | 11 ++ src/types.c | 299 ++++++++++++++++++++++++++++++++++++++++++++++++ src/types.h | 73 ++++++++++++ src/util.c | 54 +++++++++ src/util.h | 21 ++++ types.c | 220 ----------------------------------- types.h | 51 --------- util.c | 54 --------- util.h | 21 ---- 22 files changed, 913 insertions(+), 811 deletions(-) delete mode 100644 eval.c delete mode 100644 eval.h create mode 100644 food.c delete mode 100644 main.c delete mode 100644 parser.c delete mode 100644 parser.h create mode 100644 src/eval.c create mode 100644 src/eval.h create mode 100644 src/parser.c create mode 100644 src/parser.h create mode 100644 src/types.c create mode 100644 src/types.h create mode 100644 src/util.c create mode 100644 src/util.h delete mode 100644 types.c delete mode 100644 types.h delete mode 100644 util.c delete mode 100644 util.h diff --git a/Makefile b/Makefile index 82c9da1..3b011df 100644 --- a/Makefile +++ b/Makefile @@ -1,33 +1,35 @@ -TARGET=food -SRC=*.c CC=gcc -CFLAGS=-D_GNU_SOURCE -std=c99 -pedantic -REL_FLAGS=-O3 -DBG_FLAGS=-Wall -g3 -make: $(SRC) - $(CC) $(REL_FLAGS) $(CFLAGS) -o $(TARGET) $^ $(LIBS) +CFLAGS=-D_GNU_SOURCE -std=c99 -pedantic -O3 +DBG_FLAGS=-Wall -g3 -D_FOOD_DEBUG -fsanitize=address -Og -debug: $(SRC) - $(CC) $(DBG_FLAGS) -D_FOOD_DEBUG -fsanitize=address $(CFLAGS) -o $(TARGET) $^ $(LIBS) +SRC=src/*.c -noasan: $(SRC) - $(CC) $(DBG_FLAGS) $(CFLAGS) -o $(TARGET) $^ $(LIBS) +.PHONY: all debug clean -tests: - $(MAKE) -C test +all: food #eval -clean-tests: - $(MAKE) clean -C test +food: $(SRC) food.c + $(CC) $(CFLAGS) -o food $^ $(LIBS) -.PHONY: clean +eval: $(SRC) eval.c + $(CC) $(CFLAGS) -o eval $^ $(LIBS) + +debug: CFLAGS += $(DBG_FLAGS) +debug: all clean: rm -f *.o - rm -f $(TARGET) + rm -f food eval + +# tests: +# $(MAKE) -C test + +# clean-tests: +# $(MAKE) clean -C test -install: $(TARGET) - @cp -v $(TARGET) /usr/local/bin/$(TARGET) +# install: $(TARGET) +# @cp -v $(TARGET) /usr/local/bin/$(TARGET) -uninstall: - @rm -v /usr/local/bin/$(TARGET) +# uninstall: +# @rm -v /usr/local/bin/$(TARGET) diff --git a/eval.c b/eval.c deleted file mode 100644 index 12fe45c..0000000 --- a/eval.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "eval.h" -#include "util.h" - -static void -merge_items(recipe * dst, recipe * src) -{ - if (!dst || !src) return; - for (int i = 0; i < src->in; i++) { - new_item(dst); - dst->i[dst->in - 1]->name = strdup(src->i[i]->name); - dst->i[dst->in - 1]->qty = strdup(src->i[i]->qty); - } -} - -recipe * -eval(recipe * r) -{ - if (!r) return NULL; - recipe * eve = new_recipe(); - /* /\* Copy filename, title, etc *\/ */ - /* copy_metadata(eve, r); */ - /* attempt to merge items (adding qtys) */ - merge_items(eve, r); - /* /\* Resolve step type, variables, duration and step output (if any) *\/ */ - /* finalize_steps(eve, r); */ - - return r; -} - diff --git a/eval.h b/eval.h deleted file mode 100644 index 2d4f6a1..0000000 --- a/eval.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __EVAL_H -#define __EVAL_H - -#include "types.h" - -recipe * eval(recipe * r); - -#endif /* __EVAL_H */ diff --git a/food.c b/food.c new file mode 100644 index 0000000..90ce489 --- /dev/null +++ b/food.c @@ -0,0 +1,137 @@ +#include + +#include "src/util.h" +#include "src/parser.h" +#include "src/eval.h" + +recipe ** cookbook; + +static struct opts { + int json; + int html; + int rcp; + char *query; + int list; + int help; +} opt = { + .json = 0, + .html = 0, + .rcp = 0, + .query = NULL, + .list = 0, + .help = 0, +}; + +void +print_help(char * argv0) { + printf("%s [OPTION] FILE ...\n", argv0); + printf("OPTIONS:\n"); + printf("--format json,html,rcp\n"); + printf("--to-{json,html,rcp}\n"); + printf("-j json\n"); + printf("-w html\n"); + printf("-r rcp\n"); + printf("-l, --list-ingredients\n"); +} + +int +main(int argc, char * argv[]) +{ + fdebug("--- Debug mode is on ---\n"); + + int c; + + while (1) { + static struct option long_options[] = + { + /* These options set a flag. */ + // {"verbose", no_argument, &verbose_flag, 1}, + // {"brief", no_argument, &verbose_flag, 0}, + {"help", no_argument, 0, 'h'}, + {"to-json", no_argument, 0, 'j'}, + {"to-html", no_argument, 0, 'w'}, + {"to-rcp", no_argument, 0, 'r'}, + {"format", required_argument, 0, 'f'}, + {"list-ingredients", no_argument, 0, 'l'}, + // {"to-rcp", required_argument, 0, 'r'}, + {0, 0, 0, 0} + }; + + int option_index = 0; + + c = getopt_long (argc, argv, "jlhrwf:", + long_options, &option_index); + + if (c == -1) + break; + + switch (c) { + case 0: + if (long_options[option_index].flag != 0) + break; + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + case 'f': + if (!strcmp(optarg, "json")) + opt.json = 1; + else if (!strcmp(optarg, "rcp")) + opt.rcp = 1; + else if (!strcmp(optarg, "html")) + opt.html = 1; + else + fprintf(stderr, "invalid format: %s\n", optarg); + break; + case 'j': + opt.json = 1; + break; + case 'w': + opt.html = 1; + break; + case 'r': + opt.rcp = 1; + break; + case 'l': + opt.list = 1; + break; + case 'h': + opt.help = 1; + break; + case '?': + return -1; + break; + default: + abort (); + } + } + + if (opt.help) { + print_help(argv[0]); + return 0; + } + + if (optind < argc) { + while (optind < argc) { + recipe * r = parse(argv[optind++], NULL); + if (r && opt.list) { + /* recipe * r_merged = new_recipe(); */ + /* merge_items(r_merged, r); */ + recipe * r_merged = eval(r); + pprint_items(r_merged); + free_recipe(r_merged); + } + else if (r) { + if (opt.json) tojson(r); + if (opt.html) tohtml(r); + if (opt.rcp) torcp(r); + } + free_recipe(r); + } + } else { + fprintf(stderr, "Specify filenames\n"); + } + + return 0; +} diff --git a/lib/include.rcp b/lib/include.rcp index b4b2814..e7c6954 100644 --- a/lib/include.rcp +++ b/lib/include.rcp @@ -2,3 +2,4 @@ poops = 5 crouches = 2 +#!simple_omelette.rcp \ No newline at end of file diff --git a/lib/pasta-red-sauce.rcp b/lib/pasta-red-sauce.rcp index bef84c1..8b98626 100644 --- a/lib/pasta-red-sauce.rcp +++ b/lib/pasta-red-sauce.rcp @@ -1,6 +1,6 @@ @pasta with red sauce -2!pa sta.rcp +3!pasta.rcp salt = * sugar = 1 teaspoon onion, garlic = 1 diff --git a/lib/simple_omelette.rcp b/lib/simple_omelette.rcp index c867e47..f278e1c 100644 --- a/lib/simple_omelette.rcp +++ b/lib/simple_omelette.rcp @@ -7,7 +7,7 @@ # 1b. local path # 2. default library path # !pasta.rcp = 3 -!~/code/food_compiler/lib/include.rcp +# !~/code/food_compiler/lib/include.rcp eggs = 3 milk = 2tbsp salt, pepper = * diff --git a/main.c b/main.c deleted file mode 100644 index 68de644..0000000 --- a/main.c +++ /dev/null @@ -1,128 +0,0 @@ -#include "getopt.h" - -#include "util.h" -#include "parser.h" - -recipe ** cookbook; - -static struct opts { - int json; - int html; - int rcp; - char *query; - int help; -} opt = { - .json = 0, - .html = 0, - .rcp = 0, - .query = NULL, - .help = 0, -}; - -void -print_help(char * argv0) { - printf("%s [OPTION] FILE ...\n", argv0); - printf("OPTIONS:\n"); - printf("--format json,html,rcp\n"); - printf("--to-{json,html,rcp}\n"); - printf("-j json\n"); - printf("-w html\n"); - printf("-r rcp\n"); -} - -int -main(int argc, char * argv[]) -{ - fdebug("--- Debug mode is on ---\n"); - - int c; - - while (1) { - static struct option long_options[] = - { - /* These options set a flag. */ - // {"verbose", no_argument, &verbose_flag, 1}, - // {"brief", no_argument, &verbose_flag, 0}, - {"help", no_argument, 0, 'h'}, - {"to-json", no_argument, 0, 'j'}, - {"to-html", no_argument, 0, 'w'}, - {"to-rcp", no_argument, 0, 'r'}, - {"format", required_argument, 0, 'f'}, - // {"to-rcp", required_argument, 0, 'r'}, - {0, 0, 0, 0} - }; - - int option_index = 0; - - c = getopt_long (argc, argv, "jhrwf:", - long_options, &option_index); - - if (c == -1) - break; - - switch (c) { - case 0: - if (long_options[option_index].flag != 0) - break; - printf ("option %s", long_options[option_index].name); - if (optarg) - printf (" with arg %s", optarg); - printf ("\n"); - break; - case 'f': - if (!strcmp(optarg, "json")) - opt.json = 1; - else if (!strcmp(optarg, "rcp")) - opt.rcp = 1; - else if (!strcmp(optarg, "html")) - opt.html = 1; - else - fprintf(stderr, "invalid format: %s\n", optarg); - break; - case 'j': - opt.json = 1; - break; - case 'w': - opt.html = 1; - break; - case 'r': - opt.rcp = 1; - break; - case 'h': - opt.help = 1; - break; - case '?': - return -1; - break; - default: - abort (); - } - } - - if (opt.help) { - print_help(argv[0]); - return 0; - } - - if (optind < argc) { - while (optind < argc) { - recipe * r = parse(argv[optind++], NULL); - if (r) { - if (opt.json) tojson(r); - if (opt.html) tohtml(r); - if (opt.rcp) torcp(r); - } - free_recipe(r); - } - } else { - fprintf(stderr, "Specify filenames\n"); - } - - return 0; -} - - - - - - diff --git a/parser.c b/parser.c deleted file mode 100644 index df8d80b..0000000 --- a/parser.c +++ /dev/null @@ -1,263 +0,0 @@ -#include - -#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; -} diff --git a/parser.h b/parser.h deleted file mode 100644 index 3d704c3..0000000 --- a/parser.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __PARSER_H -#define __PARSER_H - -#include -#include - -#include "types.h" - -#define LINE_SIZE 4096 - -recipe * -parse(char * path, const char * prev); - -#endif /* __PARSER_H */ diff --git a/src/eval.c b/src/eval.c new file mode 100644 index 0000000..a26cc21 --- /dev/null +++ b/src/eval.c @@ -0,0 +1,21 @@ +#include "eval.h" +#include "util.h" + +recipe * +eval(recipe * r) +{ + if (!r) return NULL; + recipe * r1 = new_recipe(); + recipe * r2 = new_recipe(); + /* attempt to merge items (adding qtys) */ + merge_items(r1, r); + distinct_sum_items(r2, r1); + free_recipe(r1); + + copy_metadata(r2, r); + /* /\* Resolve step type, variables, duration and step output (if any) *\/ */ + /* finalize_steps(eve, r); */ + + return r2; +} + diff --git a/src/eval.h b/src/eval.h new file mode 100644 index 0000000..2d4f6a1 --- /dev/null +++ b/src/eval.h @@ -0,0 +1,8 @@ +#ifndef __EVAL_H +#define __EVAL_H + +#include "types.h" + +recipe * eval(recipe * r); + +#endif /* __EVAL_H */ 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 + +#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; +} diff --git a/src/parser.h b/src/parser.h new file mode 100644 index 0000000..2680da5 --- /dev/null +++ b/src/parser.h @@ -0,0 +1,11 @@ +#ifndef __PARSER_H +#define __PARSER_H + +#include "types.h" + +#define LINE_SIZE 4096 + +recipe * +parse(char * path, const char * prev); + +#endif /* __PARSER_H */ diff --git a/src/types.c b/src/types.c new file mode 100644 index 0000000..d9253af --- /dev/null +++ b/src/types.c @@ -0,0 +1,299 @@ +#include + +#include "types.h" +#include "util.h" + +recipe * +new_recipe() +{ + recipe * r; + r = (recipe *) malloc(sizeof(recipe)); + if (!r) + die("Couldn't allocate memory for recipe"); + + r->n = 1; + r->i = NULL; + r->in = 0; + r->s = NULL; + r->sn = 0; + r->r = NULL; + r->rn = 0; + r->filename = NULL; + r->path = NULL; + r->title = NULL; + return r; +} + +void +new_subrecipe(recipe * r, recipe * src) +{ + r->r = (recipe **)realloc(r->r, (r->rn + 1) * sizeof(recipe *)); + if (!r->r) + die("Couldn't allocate memory for subricepie"); + + r->r[r->rn] = src; + r->rn++; +} + +void +new_item(recipe * r) +{ + r->i = (item **)realloc(r->i, (r->in + 1) * sizeof(item *)); + if (!r->i) + die("Couldn't allocate memory for item"); + + r->i[r->in] = (item *)malloc(sizeof(item)); + if (!r->i[r->in]) + die("Couldn't allocate memory for item"); + r->i[r->in]->name = NULL; + r->i[r->in]->qty = NULL; + r->in++; +} + +void +free_item(item * i) +{ + if (!i) + return; + if (i->name) + free(i->name); + if (i->qty) + free(i->qty); + free(i); +} + +void +new_step(recipe * r) +{ + r->s = (step **)realloc(r->s, (r->sn + 1) * sizeof(step *)); + if (!r->s) + die("Couldn't allocate memory for step"); + + r->s[r->sn] = (step *)malloc(sizeof(step)); + if (!r->s[r->sn]) + die("Couldn't allocate memory for item"); + r->s[r->sn]->inst = NULL; + r->s[r->sn]->duration = NULL; + r->s[r->sn]->result = NULL; + r->s[r->sn]->type = 0; + r->sn++; +} + +void +free_step(step * s) +{ + if (!s) + return; + if (s->inst) + free(s->inst); + if (s->duration) + free(s->duration); + if (s->result) + free(s->result); + free(s); +} + +void +free_recipe(recipe * r) +{ + if (!r) + return; + + if (r->i) { + for (int i = 0; i < r->in; i++) + free_item(r->i[i]); + free(r->i); + } + + if (r->s) { + for (int i = 0; i < r->sn; i++) + free_step(r->s[i]); + free(r->s); + } + + if (r->r) { + for (int i = 0; i < r->rn; i++) + free_recipe(r->r[i]); + free(r->r); + } + + if (r->filename) + free(r->filename); + if (r->path) + free(r->path); + if (r->title) + free(r->title); + + free(r); +} + +void +pprint_items(recipe * r) +{ + printf("Ingredients for %s:\n", r->title); + for (int i = 0; i < r->in; i++) + printf("%s: %s\n", r->i[i]->name, r->i[i]->qty); + printf("\n"); +} + +void +show(recipe * r) +{ + printf("Filename\t%s\n", r->filename); + printf("Dirname\t\t%s\n", r->path); + printf("Title\t\t%s\n\n", r->title); + + for (int i = 0; i < r->in; i++) + printf("I:%s\t%s\n", r->i[i]->name, r->i[i]->qty); + if (r->sn) { + printf("\nSteps:\n~~~~~~\n\n"); + for (int i = 0; i < r->sn; i++) + printf("%s: %s\n", r->s[i]->type == PREP ? "PREP" : "COOK", r->s[i]->inst); + } +} + +void +tojson(recipe * r) +{ + printf("{\"filename\":\"%s\",", r->filename); + printf("\"dirname\":\"%s\",", r->path); + printf("\"title\":\"%s\",", r->title); + printf("\"n\":\"%d\"", r->n); + if (r->rn) { + printf(",\"subrecipes\":["); + int i = 0; + for (; i < r->rn - 1; i++) { + tojson(r->r[i]); + printf(","); + } + tojson(r->r[i]); + printf("]"); + } + if (r->in) { + printf(",\"ingredients\":{"); + int i = 0; + for (; i < r->in - 1; i++) + printf("\"%s\":\"%s\",", r->i[i]->name, r->i[i]->qty); + printf("\"%s\":\"%s\"}", r->i[i]->name, r->i[i]->qty); + } + if (r->sn) { + printf(",\"steps\":["); + int i = 0; + for (; i < r->sn - 1; i++) + printf("\"%s\",", r->s[i]->inst); + printf("\"%s\"]", r->s[i]->inst); + } + printf("}"); +} + +void +tohtml(recipe * r) +{ + printf("todo\n"); +} + +void +torcp(recipe * r) +{ + printf("# %s/%s\n\n", r->path, r->filename); + printf("@%s\n\n", r->title); + for (int i = 0; i < r->rn; i++) { + printf("!%s/%s\n", r->r[i]->path, r->r[i]->filename); + } + for (int i = 0; i < r->in; i++) + printf("%s = %s\n", r->i[i]->name, r->i[i]->qty); + if (r->sn) { + printf("\n---\n\n"); + for (int i = 0; i < r->sn; i++) { + char c; + if (r->s[i]->type == PREP) + c = '-'; + else if (r->s[i]->type == COOK) + c = '>'; + else + c = '+'; + printf("%c %s\n", c, r->s[i]->inst); + } + } +} + +void +copy_metadata(recipe * dst, recipe * src) +{ + if (!dst || !src) return; + dst->filename = strdup(src->filename); + dst->path = strdup(src->path); + dst->title = strdup(src->title); +} + +void +copy_items(recipe * dst, recipe * src) +{ + if (!dst || !src) return; + for (int i = 0; i < src->in; i++) { + new_item(dst); + dst->i[dst->in - 1]->name = strdup(src->i[i]->name); + dst->i[dst->in - 1]->qty = strdup(src->i[i]->qty); + } +} + +static void +join_subrecipe_items(recipe * dst, recipe * src) +{ + if (!src || !dst) return; + for (int i = 0; i < src->rn; i++) { + join_subrecipe_items(dst, src->r[i]); + for (int j = 0; j < src->r[i]->in; j++) { + new_item(dst); + dst->i[dst->in - 1]->name = strdup(src->r[i]->i[j]->name); + if (src->r[i]->n > 1) { + char tmp[2048] = ""; + sprintf(tmp, "%d X ( %s )", src->r[i]->n, src->r[i]->i[j]->qty); + dst->i[dst->in - 1]->qty = strdup(tmp); + } + else { + dst->i[dst->in - 1]->qty = strdup(src->r[i]->i[j]->qty); + } + } + } +} + +void +merge_items(recipe * dst, recipe * src) +{ + /* Join all items in src's subrecipes to dst */ + join_subrecipe_items(dst, src); + /* Copy src items as well to dst */ + copy_items(dst, src); +} + +static int +item_exists(const char * name, const recipe * r) +{ + for (int i = 0; i < r->in; i++) + if (!strcmp(r->i[i]->name, name)) + return i; + return -1; +} + +void +distinct_sum_items(recipe * dst, recipe * src) +{ + if (!dst || !src) return; + for (int i = 0; i < src->in; i++) { + int n = item_exists(src->i[i]->name, dst); + if (n != -1) { + char tmp[2048] = ""; + strcat(tmp, dst->i[n]->qty); + strcat(tmp, " + "); + strcat(tmp, src->i[i]->qty); + free(dst->i[n]->qty); + dst->i[n]->qty = strdup(tmp); + } + else { + new_item(dst); + dst->i[dst->in - 1]->name = strdup(src->i[i]->name); + dst->i[dst->in - 1]->qty = strdup(src->i[i]->qty); + } + } +} diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..0d870cf --- /dev/null +++ b/src/types.h @@ -0,0 +1,73 @@ +#ifndef __TYPES_H +#define __TYPES_H + +typedef struct item_t { + char *name; + char *qty; +} item; + +enum step_type { + PREP = 0, + COOK, + SERVE, +}; + +typedef struct step_t { + char *inst; + char *duration; + char *result; + enum step_type type; +} step; + +typedef struct recipe_t { + char * filename; + char * path; + char * title; + int n; + item **i; + int in; + step **s; + int sn; + struct recipe_t **r; + int rn; +} recipe; + +recipe * new_recipe(); +void new_subrecipe(recipe * r, recipe * src); +void new_item(recipe * r); +void new_step(recipe * r); + +void free_recipe(recipe * r); +void free_item(item * i); +void free_step(step * s); + +void pprint_items(recipe * r); +void show(recipe * r); +void tojson(recipe * r); +void tohtml(recipe * r); +void torcp(recipe * r); + +/* Operations */ + +/** + * Copy metadata from `src` to `dst` + */ +void copy_metadata(recipe * dst, recipe * src); + +/** + * Copy top level items from `src` to `dst` + */ +void copy_items(recipe * dst, recipe * src); + +/** + * Merge all items from `src` and add them to `dst` + */ +void merge_items(recipe * dst, recipe * src); + +/** + * Sum all top level item qtys in `src` and add them to `dst` + * making sure each item name exists only once. + */ +void distinct_sum_items(recipe * dst, recipe * src); + +#endif /* __TYPES_H */ diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..6eda622 --- /dev/null +++ b/src/util.c @@ -0,0 +1,54 @@ +#include "util.h" + +void +fdebug(const char *fmt, ...) +{ +#ifdef _FOOD_DEBUG + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +#else + return; +#endif +} + +void +die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + + exit(1); +} + +void +trim(char * str) +{ + int i; + int begin = 0; + int end = strlen(str) - 1; + + while (isspace((unsigned char) str[begin])) + begin++; + + while (isspace((unsigned char) str[end]) && (end >= begin)) + end--; + + /* Shift all characters back to the start of the string array. */ + for (i = begin; i <= end; i++) + str[i - begin] = str[i]; + + str[i - begin] = '\0'; +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..fe09145 --- /dev/null +++ b/src/util.h @@ -0,0 +1,21 @@ +#ifndef __UTIL_H +#define __UTIL_H + +#include +#include +#include +#include +#include + +#define MAX(A, B) ((A) > (B) ? (A) : (B)) + +void +fdebug(const char *fmt, ...); + +void +die(const char *fmt, ...); + +void +trim(char * str); + +#endif /* __UTIL_H */ diff --git a/types.c b/types.c deleted file mode 100644 index 091ff8d..0000000 --- a/types.c +++ /dev/null @@ -1,220 +0,0 @@ -#include - -#include "types.h" -#include "util.h" - -recipe * -new_recipe() -{ - recipe * r; - r = (recipe *) malloc(sizeof(recipe)); - if (!r) - die("Couldn't allocate memory for recipe"); - - r->n = 1; - r->i = NULL; - r->in = 0; - r->s = NULL; - r->sn = 0; - r->r = NULL; - r->rn = 0; - r->filename = NULL; - r->path = NULL; - r->title = NULL; - return r; -} - -void -new_subrecipe(recipe * r, recipe * src) -{ - r->r = (recipe **)realloc(r->r, (r->rn + 1) * sizeof(recipe *)); - if (!r->r) - die("Couldn't allocate memory for subricepie"); - - r->r[r->rn] = src; - r->rn++; -} - -void -new_item(recipe * r) -{ - r->i = (item **)realloc(r->i, (r->in + 1) * sizeof(item *)); - if (!r->i) - die("Couldn't allocate memory for item"); - - r->i[r->in] = (item *)malloc(sizeof(item)); - if (!r->i[r->in]) - die("Couldn't allocate memory for item"); - r->i[r->in]->name = NULL; - r->i[r->in]->qty = NULL; - r->in++; -} - -void -free_item(item * i) -{ - if (!i) - return; - if (i->name) - free(i->name); - if (i->qty) - free(i->qty); - free(i); -} - -void -new_step(recipe * r) -{ - r->s = (step **)realloc(r->s, (r->sn + 1) * sizeof(step *)); - if (!r->s) - die("Couldn't allocate memory for step"); - - r->s[r->sn] = (step *)malloc(sizeof(step)); - if (!r->s[r->sn]) - die("Couldn't allocate memory for item"); - r->s[r->sn]->inst = NULL; - r->s[r->sn]->duration = NULL; - r->s[r->sn]->result = NULL; - r->s[r->sn]->type = 0; - r->sn++; -} - -void -free_step(step * s) -{ - if (!s) - return; - if (s->inst) - free(s->inst); - if (s->duration) - free(s->duration); - if (s->result) - free(s->result); - free(s); -} - -void -free_recipe(recipe * r) -{ - if (!r) - return; - - if (r->i) { - for (int i = 0; i < r->in; i++) - free_item(r->i[i]); - free(r->i); - } - - if (r->s) { - for (int i = 0; i < r->sn; i++) - free_step(r->s[i]); - free(r->s); - } - - if (r->r) { - for (int i = 0; i < r->rn; i++) - free_recipe(r->r[i]); - free(r->r); - } - - if (r->filename) - free(r->filename); - if (r->path) - free(r->path); - if (r->title) - free(r->title); - - free(r); -} - -void -show(recipe * r) -{ - printf("Filename\t%s\n", r->filename); - printf("Dirname\t\t%s\n", r->path); - printf("Title\t\t%s\n\n", r->title); - - for (int i = 0; i < r->in; i++) - printf("I:%s\t%s\n", r->i[i]->name, r->i[i]->qty); - if (r->sn) { - printf("\nSteps:\n~~~~~~\n\n"); - for (int i = 0; i < r->sn; i++) - printf("%s: %s\n", r->s[i]->type == PREP ? "PREP" : "COOK", r->s[i]->inst); - } -} - -void -tojson(recipe * r) -{ - printf("{\"filename\":\"%s\",", r->filename); - printf("\"dirname\":\"%s\",", r->path); - printf("\"title\":\"%s\",", r->title); - printf("\"n\":\"%d\"", r->n); - if (r->rn) { - printf(",\"subrecipes\":["); - int i = 0; - for (; i < r->rn - 1; i++) { - tojson(r->r[i]); - printf(","); - } - tojson(r->r[i]); - printf("]"); - } - if (r->in) { - printf(",\"ingredients\":{"); - int i = 0; - for (; i < r->in - 1; i++) - printf("\"%s\":\"%s\",", r->i[i]->name, r->i[i]->qty); - printf("\"%s\":\"%s\"}", r->i[i]->name, r->i[i]->qty); - } - if (r->sn) { - printf(",\"steps\":["); - int i = 0; - for (; i < r->sn - 1; i++) - printf("\"%s\",", r->s[i]->inst); - printf("\"%s\"]", r->s[i]->inst); - } - printf("}"); -} - -void -tohtml(recipe * r) -{ - printf("todo\n"); -} - -void -torcp(recipe * r) -{ - printf("# %s/%s\n\n", r->path, r->filename); - printf("@%s\n\n", r->title); - for (int i = 0; i < r->rn; i++) { - printf("!%s/%s\n", r->r[i]->path, r->r[i]->filename); - } - for (int i = 0; i < r->in; i++) - printf("%s = %s\n", r->i[i]->name, r->i[i]->qty); - if (r->sn) { - printf("\n---\n\n"); - for (int i = 0; i < r->sn; i++) { - char c; - if (r->s[i]->type == PREP) - c = '-'; - else if (r->s[i]->type == COOK) - c = '>'; - else - c = '+'; - printf("%c %s\n", c, r->s[i]->inst); - } - } -} - -void copy_items(recipe * dst, recipe * src) -{ - if (!dst || !src) return; - for (int i = 0; i < src->in; i++) { - new_item(dst); - dst->i[dst->in - 1]->name = strdup(src->i[i]->name); - dst->i[dst->in - 1]->qty = strdup(src->i[i]->qty); - } -} - diff --git a/types.h b/types.h deleted file mode 100644 index ea8fa47..0000000 --- a/types.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef __TYPES_H -#define __TYPES_H - -typedef struct item_t { - char *name; - char *qty; -} item; - -enum step_type { - PREP = 0, - COOK, - SERVE, -}; - -typedef struct step_t { - char *inst; - char *duration; - char *result; - enum step_type type; -} step; - -typedef struct recipe_t { - char * filename; - char * path; - char * title; - int n; - item **i; - int in; - step **s; - int sn; - struct recipe_t **r; - int rn; -} recipe; - -recipe * new_recipe(); -void new_subrecipe(recipe * r, recipe * src); -void new_item(recipe * r); -void new_step(recipe * r); - -void free_recipe(recipe * r); -void free_item(item * i); -void free_step(step * s); - -void show(recipe * r); -void tojson(recipe * r); -void tohtml(recipe * r); -void torcp(recipe * r); - -void copy_items(recipe * dst, recipe * src); - -#endif /* __TYPES_H */ diff --git a/util.c b/util.c deleted file mode 100644 index 6eda622..0000000 --- a/util.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "util.h" - -void -fdebug(const char *fmt, ...) -{ -#ifdef _FOOD_DEBUG - va_list ap; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); -#else - return; -#endif -} - -void -die(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - - if (fmt[0] && fmt[strlen(fmt)-1] == ':') { - fputc(' ', stderr); - perror(NULL); - } else { - fputc('\n', stderr); - } - - exit(1); -} - -void -trim(char * str) -{ - int i; - int begin = 0; - int end = strlen(str) - 1; - - while (isspace((unsigned char) str[begin])) - begin++; - - while (isspace((unsigned char) str[end]) && (end >= begin)) - end--; - - /* Shift all characters back to the start of the string array. */ - for (i = begin; i <= end; i++) - str[i - begin] = str[i]; - - str[i - begin] = '\0'; -} diff --git a/util.h b/util.h deleted file mode 100644 index fe09145..0000000 --- a/util.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef __UTIL_H -#define __UTIL_H - -#include -#include -#include -#include -#include - -#define MAX(A, B) ((A) > (B) ? (A) : (B)) - -void -fdebug(const char *fmt, ...); - -void -die(const char *fmt, ...); - -void -trim(char * str); - -#endif /* __UTIL_H */ -- cgit v1.2.3