From 595cae98a1d953fb9ad77f5734cbc22d2b05f102 Mon Sep 17 00:00:00 2001 From: Gene Stark Date: Sat, 15 Jan 2022 16:47:59 -0500 Subject: [PATCH] Bring in basecode from development repo. --- .gitlab-ci.yml | 29 + hw1/.gitignore | 6 + hw1/Makefile | 61 + hw1/hw1.sublime-project | 50 + hw1/include/argo.h | 242 ++++ hw1/include/debug.h | 88 ++ hw1/include/global.h | 87 ++ hw1/lib/argo.a | Bin 0 -> 19100 bytes hw1/rsrc/numbers.json | 17 + hw1/rsrc/package-lock.json | 2419 ++++++++++++++++++++++++++++++++ hw1/rsrc/strings.json | 11 + hw1/src/argo.c | 148 ++ hw1/src/const.c | 47 + hw1/src/main.c | 33 + hw1/src/validargs.c | 26 + hw1/tests/basecode_tests.c | 95 ++ hw1/tests/rsrc/strings_-c.json | 1 + 17 files changed, 3360 insertions(+) create mode 100644 .gitlab-ci.yml create mode 100644 hw1/.gitignore create mode 100644 hw1/Makefile create mode 100644 hw1/hw1.sublime-project create mode 100644 hw1/include/argo.h create mode 100644 hw1/include/debug.h create mode 100644 hw1/include/global.h create mode 100644 hw1/lib/argo.a create mode 100644 hw1/rsrc/numbers.json create mode 100644 hw1/rsrc/package-lock.json create mode 100644 hw1/rsrc/strings.json create mode 100644 hw1/src/argo.c create mode 100644 hw1/src/const.c create mode 100644 hw1/src/main.c create mode 100644 hw1/src/validargs.c create mode 100644 hw1/tests/basecode_tests.c create mode 100644 hw1/tests/rsrc/strings_-c.json diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..d3bb8f3 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,29 @@ +image: hwrunner:latest +variables: + GIT_SSL_NO_VERIFY: "true" + EXEC: argo + HW_DIR: hw1 + CPU_LIMIT: 60 + FILE_LIMIT: 1000000 +before_script: + - make clean all -C ${HW_DIR} +stages: + - build + - run + - test +build: + stage: build + script: + - echo "Build done" +run: + stage: run + script: + - ulimit -t ${CPU_LIMIT} + - ulimit -f ${FILE_LIMIT} + - cd ${HW_DIR} && bin/${EXEC} -h +test: + stage: test + script: + - ulimit -t ${CPU_LIMIT} + - ulimit -f ${FILE_LIMIT} + - cd ${HW_DIR} && bin/${EXEC}_tests -S --verbose=0 --timeout 30 diff --git a/hw1/.gitignore b/hw1/.gitignore new file mode 100644 index 0000000..665cbf9 --- /dev/null +++ b/hw1/.gitignore @@ -0,0 +1,6 @@ +bin/ +build/ +test_output/ +*~ +*.out +*.bak diff --git a/hw1/Makefile b/hw1/Makefile new file mode 100644 index 0000000..e3efe98 --- /dev/null +++ b/hw1/Makefile @@ -0,0 +1,61 @@ +CC := gcc +SRCD := src +TSTD := tests +BLDD := build +BIND := bin +INCD := include +LIBD := lib + +EXEC := argo +TEST_EXEC := $(EXEC)_tests + +MAIN := $(BLDD)/main.o +LIB := $(LIBD)/$(EXEC).a + +ALL_SRCF := $(shell find $(SRCD) -type f -name *.c) +ALL_OBJF := $(patsubst $(SRCD)/%,$(BLDD)/%,$(ALL_SRCF:.c=.o)) +ALL_FUNCF := $(filter-out $(MAIN) $(AUX), $(ALL_OBJF)) + +TEST_ALL_SRCF := $(shell find $(TSTD) -type f -name *.c) +TEST_SRCF := $(filter-out $(TEST_REF_SRCF), $(TEST_ALL_SRCF)) + +INC := -I $(INCD) + +CFLAGS := -Wall -Werror -Wno-unused-variable -Wno-unused-function -MMD -fcommon +COLORF := -DCOLOR +DFLAGS := -g -DDEBUG -DCOLOR +PRINT_STAMENTS := -DERROR -DSUCCESS -DWARN -DINFO + +STD := -std=gnu11 +TEST_LIB := -lcriterion +LIBS := $(LIB) + +CFLAGS += $(STD) + +.PHONY: clean all setup debug + +all: setup $(BIND)/$(EXEC) $(BIND)/$(TEST_EXEC) + +debug: CFLAGS += $(DFLAGS) $(PRINT_STAMENTS) $(COLORF) +debug: all + +setup: $(BIND) $(BLDD) +$(BIND): + mkdir -p $(BIND) +$(BLDD): + mkdir -p $(BLDD) + +$(BIND)/$(EXEC): $(ALL_OBJF) + $(CC) $^ -o $@ $(LIBS) + +$(BIND)/$(TEST_EXEC): $(ALL_FUNCF) $(TEST_SRCF) + $(CC) $(CFLAGS) $(INC) $(ALL_FUNCF) $(TEST_SRCF) $(TEST_LIB) $(LIBS) -o $@ + +$(BLDD)/%.o: $(SRCD)/%.c + $(CC) $(CFLAGS) $(INC) -c -o $@ $< + +clean: + rm -rf $(BLDD) $(BIND) + +.PRECIOUS: $(BLDD)/*.d +-include $(BLDD)/*.d diff --git a/hw1/hw1.sublime-project b/hw1/hw1.sublime-project new file mode 100644 index 0000000..5465569 --- /dev/null +++ b/hw1/hw1.sublime-project @@ -0,0 +1,50 @@ +{ + "folders": + [ + { + "path":".", + "name":"Project Base" + }, + { + "path": "src", + "name": "C Source", + "follow_symlinks": false, + "file_include_patterns":["*.c"], + }, + { + "path": "include", + "name": "C Headers", + "follow_symlinks": false, + "file_include_patterns":["*.h"], + }, + { + "path": "tests", + "name": "Tests", + } + { + "path": "rsrc", + "name": "Resource Files", + } + ], + "settings": + { + }, + "build_systems": + [ + { + "name": "Release (full build)", + "working_dir":"$project_path", + "shell_cmd": "make clean all", + }, + { + "name": "Debug (full build)", + "working_dir":"$project_path", + "shell_cmd": "make clean debug", + }, + { + "name": "Test", + "working_dir":"$project_path", + "shell_cmd": "bin/${project_base_name}_tests}", + } + ] +} diff --git a/hw1/include/argo.h b/hw1/include/argo.h new file mode 100644 index 0000000..005f6b6 --- /dev/null +++ b/hw1/include/argo.h @@ -0,0 +1,242 @@ +/* + * DO NOT MODIFY THE CONTENTS OF THIS FILE. + * IT WILL BE REPLACED DURING GRADING + */ +#ifndef ARGO_H +#define ARGO_H + +/* + * Definitions for "Argo" (aka JSON). + */ + +/* + * USAGE macro to be called from main() to print a help message and exit + * with a specified exit status. + */ +#define USAGE(program_name, retcode) do { \ +fprintf(stderr, "USAGE: %s %s\n", program_name, \ +"[-h] [-c|-v] [-p INDENT]\n" \ +" -h Help: displays this help menu.\n" \ +" -v Validate: the program reads from standard input and checks whether\n" \ +" it is syntactically correct JSON. If there is any error, then a message\n" \ +" describing the error is printed to standard error before termination.\n" \ +" No other output is produced.\n" \ +" -c Canonicalize: once the input has been read and validated, it is\n" \ +" re-emitted to standard output in 'canonical form'. Unless -p has been\n" \ +" specified, the canonicalized output contains no whitespace (except within\n" \ +" strings that contain whitespace characters).\n" \ +" -p Pretty-print: This option is only permissible if -c has also been specified.\n" \ +" In that case, newlines and spaces are used to format the canonical output\n" \ +" in a more human-friendly way. For the precise requirements on where this\n" \ +" whitespace must appear, see the assignment handout.\n" \ +" The INDENT is an optional nonnegative integer argument that specifies the\n" \ +" number of additional spaces to be output at the beginning of a line for each\n" \ +" for each increase in indentation level. If no value is specified, then a\n" \ +" default value of 4 is used.\n" \ +); \ +exit(retcode); \ +} while(0) + +/* + * Type used to represent an input character. It is intended to + * represent a Unicode code point (4 bytes max), so the C type + * "char" is not used. It is signed, so that we can represent + * the out-of-band value EOF (-1) as a value of this type. + */ +typedef int ARGO_CHAR; + +/* + * Type codes for Argo values. + */ +typedef enum { + ARGO_NO_TYPE = 0, + ARGO_BASIC_TYPE = 1, + ARGO_NUMBER_TYPE = 2, + ARGO_STRING_TYPE = 3, + ARGO_OBJECT_TYPE = 4, + ARGO_ARRAY_TYPE = 5 +} ARGO_VALUE_TYPE; + +/* + * Basic Argo values, represented by the (unquoted) tokens + * "true", "false", or "null" in Argo code. + */ +typedef enum { + ARGO_NULL, ARGO_TRUE, ARGO_FALSE +} ARGO_BASIC; + +/* + * Structure used to hold a string value. + * The content field is maintained as an array of char, which is not null-terminated + * and which might contain '\0' characters. This data is interpreted as Unicode text, + * represented as an array of ARGO_CHAR values, each of which represents a single + * Unicode code point. The length field gives the length in bytes of the data. + * The capacity field records the actual size of the data area. This is included so + * that the size can be dynamically increased while the string is being read. + */ +typedef struct argo_string { + size_t capacity; // Current total size of space in the content. + size_t length; // Current length of the content. + ARGO_CHAR *content; // Unicode code points (not null terminated). +} ARGO_STRING; + +/* + * Structure used to hold a number. + * The "text_value" field holds a printable/parseable representation of the number + * as Unicode text, conforming to the Argo standard. + * The "int_value" field holds the value of the number in integer format, if the + * number can be exactly represented as such. + * The "float_value" field holds the value of the number in floating-point format. + * The "valid_text" field is nonzero if the "text_valid" field contains a valid + * representation of the value. + * The "valid_int" field is nonzero if the "int_value" field contains a valid + * representation of the value. + * The "valid_float" field is nonzero if the "float_value" field contains a valid + * representation of the value. + * + * If multiple representations of the value of the number are present, they should + * agree with each other. + * It is up to an application to determine which representation is the appropriate + * one to use, based on the semantics of the data being represented. + */ +typedef struct argo_number { + struct argo_string string_value; // Value represented in textual format. + long int_value; // Value represented in integer format. + double float_value; // Value represented in floating-point format. + char valid_string; // Nonzero if string representation is valid. + char valid_int; // Nonzero if integer representation is valid. + char valid_float; // Nonzero if floating point representation is valid. +} ARGO_NUMBER; + +/* + * An "object" has a list of members, each of which has a name and a value. + * To store the members, we use a circular, doubly linked list, with the next and + * previous pointers stored in the "next" and "prev" fields of the ARGO_VALUE structure + * and the member name stored in the "name" field of the ARGO_VALUE structure. + * The "member_list" field of the ARGO_OBJECT structure serves as the sentinel at + * the head of the list. This element does not represent one of the members; + * rather, its "next" field points to the first member and its "prev" field points + * to the last member. An empty list of members is represented by the situation in + * which both the "next" and "prev" fields point back to the sentinel object itself. + * + * Note that the collection of members of an object is supposed to be regarded as unordered, + * which would permit it to be represented using a hash map or similar data structure, + * which we are not doing here. + */ +typedef struct argo_object { + struct argo_value *member_list; +} ARGO_OBJECT; + +/* + * An "array" has an ordered sequence of elements, each of which is just a value. + * Here we represent the elements as a circular, doubly linked list, in the same + * way as for the members of an object. The "element_list" field in the ARGO_ARRAY + * structure serves as the sentinel at the head of the list. + * + * Note that elements of an array do not have any name, so the "name" field in each + * of the elements will be NULL. Arrays could be represented as actual arrays, + * but we are not doing that here. + */ +typedef struct argo_array { + struct argo_value *element_list; +} ARGO_ARRAY; + +/* + * The ARGO_VALUE structure is used to represent all kinds of Argo values. + * The "type" field tells what type of value it represents. + * It has "next" and "prev" fields so that it can be linked into "members" + * or "elements" lists. It has a "name" field which will hold the name in case + * it is a member of an object. The "content" field is the union of the structures + * that represent the various Argo types. Depending on the value of the "type" field, + * one of the "object", "array", or "string", "number", or "basic" variants of this union + * will be valid. + */ +typedef struct argo_value { + ARGO_VALUE_TYPE type; + struct argo_value *next; // Next value in list of members or elements. + struct argo_value *prev; // Previous value in list of members or element. + struct argo_string name; // NULL unless value is an object member. + union { + struct argo_object object; + struct argo_array array; + struct argo_string string; + struct argo_number number; + ARGO_BASIC basic; + } content; +} ARGO_VALUE; + +/* + * The following value is the maximum number of digits that will be printed + * for a floating point value. + */ +#define ARGO_PRECISION 15 + +/* + * Macros that define particular character values mentioned in the Argo standard. + * You should use these macros where reference to these character values is required, + * rather than "hard-coding" the values as C character constants. + */ +#define ARGO_COLON ':' +#define ARGO_LBRACE '{' +#define ARGO_RBRACE '}' +#define ARGO_LBRACK '[' +#define ARGO_RBRACK ']' +#define ARGO_QUOTE '"' +#define ARGO_BSLASH '\\' +#define ARGO_FSLASH '/' +#define ARGO_COMMA ',' +#define ARGO_PERIOD '.' +#define ARGO_PLUS '+' +#define ARGO_MINUS '-' +#define ARGO_DIGIT0 '0' +#define ARGO_B 'b' +#define ARGO_E 'e' +#define ARGO_F 'f' +#define ARGO_N 'n' +#define ARGO_R 'r' +#define ARGO_T 't' +#define ARGO_U 'u' +#define ARGO_BS '\b' +#define ARGO_FF '\f' +#define ARGO_LF '\n' +#define ARGO_CR '\r' +#define ARGO_HT '\t' +#define ARGO_SPACE ' ' + +/* + * Macros that define particular classes of characters mentioned in the Argo standard. + * You should use these macros when it is necessary to test whether a character belongs + * to a particular class, rather than "hard-coding" expressions involving C character + * constants. + */ +#define argo_is_whitespace(c) ((c) == ' ' || (c) == '\n' || (c) == '\r' || c == '\t') +#define argo_is_exponent(c) ((c) == 'e' || (c) == 'E') +#define argo_is_digit(c) ((c) >= '0' && (c) <= '9') +#define argo_is_hex(c) (argo_is_digit(c) || ((c) >= 'A' && (c) <= 'F') || ((c) >= 'a' && (c) <= 'f')) +#define argo_is_control(c) ((c) >= 0 && (c) < ' ') + +/* + * Macros that define the tokens used to represent the basic values + * "true", "false", and "null", defined by the Argo standard. + * You should use these macros rather than "hard-coding" C string literals + * into your program. + */ +#define ARGO_TRUE_TOKEN "true" +#define ARGO_FALSE_TOKEN "false" +#define ARGO_NULL_TOKEN "null" + +/* + * Variable that is reset to zero at the beginning of each line and is + * incremented each time a character is read by function argo_read_char(). + * It is intended to be used for error messages and debugging. It can be + * assigned to if it is necessary to reset the value for some reason, + * such as if reading from multiple sources is done. + */ + +/* + * The following function is used to append a character to a string. + * An implementation has been provided for you. + */ +int argo_append_char(ARGO_STRING *, ARGO_CHAR); + +#endif diff --git a/hw1/include/debug.h b/hw1/include/debug.h new file mode 100644 index 0000000..e8fc8b6 --- /dev/null +++ b/hw1/include/debug.h @@ -0,0 +1,88 @@ +#ifndef DEBUG_H +#define DEBUG_H + +#include + +#define NL "\n" + +#ifdef COLOR +#define KNRM "\033[0m" +#define KRED "\033[1;31m" +#define KGRN "\033[1;32m" +#define KYEL "\033[1;33m" +#define KBLU "\033[1;34m" +#define KMAG "\033[1;35m" +#define KCYN "\033[1;36m" +#define KWHT "\033[1;37m" +#define KBWN "\033[0;33m" +#else +#define KNRM "" +#define KRED "" +#define KGRN "" +#define KYEL "" +#define KBLU "" +#define KMAG "" +#define KCYN "" +#define KWHT "" +#define KBWN "" +#endif + +#ifdef VERBOSE +#define DEBUG +#define INFO +#define WARN +#define ERROR +#define SUCCESS +#endif + +#ifdef DEBUG +#define debug(S, ...) \ + do { \ + fprintf(stderr, KMAG "DEBUG: %s:%s:%d " KNRM S NL, __FILE__, \ + __extension__ __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + } while (0) +#else +#define debug(S, ...) +#endif + +#ifdef INFO +#define info(S, ...) \ + do { \ + fprintf(stderr, KBLU "INFO: %s:%s:%d " KNRM S NL, __FILE__, \ + __extension__ __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + } while (0) +#else +#define info(S, ...) +#endif + +#ifdef WARN +#define warn(S, ...) \ + do { \ + fprintf(stderr, KYEL "WARN: %s:%s:%d " KNRM S NL, __FILE__, \ + __extension__ __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + } while (0) +#else +#define warn(S, ...) +#endif + +#ifdef SUCCESS +#define success(S, ...) \ + do { \ + fprintf(stderr, KGRN "SUCCESS: %s:%s:%d " KNRM S NL, __FILE__, \ + __extension__ __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + } while (0) +#else +#define success(S, ...) +#endif + +#ifdef ERROR +#define error(S, ...) \ + do { \ + fprintf(stderr, KRED "ERROR: %s:%s:%d " KNRM S NL, __FILE__, \ + __extension__ __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + } while (0) +#else +#define error(S, ...) +#endif + +#endif /* DEBUG_H */ diff --git a/hw1/include/global.h b/hw1/include/global.h new file mode 100644 index 0000000..6cac919 --- /dev/null +++ b/hw1/include/global.h @@ -0,0 +1,87 @@ +/* + * DO NOT MODIFY THE CONTENTS OF THIS FILE. + * IT WILL BE REPLACED DURING GRADING + */ +#ifndef GLOBAL_H +#define GLOBAL_H + +#include + +/* + * Options info, set by validargs. + * If -h is specified, then the HELP_OPTION bit is set. + * If -v is specified, then the VALIDATE_OPTION bit is set. + * If -c is specified, then the CANONICALIZE_OPTION bit is set. + * If -p is specified, then the PRETTY_PRINT_OPTION bit is set. + * If PRETTY_PRINT_OPTION is set, then CANONICALIZE_OPTION must also be set. + * The least-significant byte contains the number of additional spaces + * to add at the beginning of each output line, for each increase + * in the indentation level of the value being output. + */ +int global_options; + +#define HELP_OPTION (0x80000000) +#define VALIDATE_OPTION (0x40000000) +#define CANONICALIZE_OPTION (0x20000000) +#define PRETTY_PRINT_OPTION (0x10000000) + +/* + * Variables that keep track of the current amount of input data that has been + * read. Variable "argo_lines_read" starts at zero and is incremented each time + * a newline character is read by function argo_read_char(). Variable + * "argo_chars_read" starts at zero, is reset to zero at the beginning of each + * line, and is incremented each time a character is read by function argo_read_char(). + * These variables are intended to be used for error messages and debugging. + * They can be assigned to if it is necessary to reset their values for some reason, + * such as if reading from multiple sources is done. + */ +int argo_lines_read; +int argo_chars_read; + +/* + * Variable that keeps track of the current indent level while pretty printing. + * See the assignment handout for a description of how the indent level is to + * be maintained and used. + */ +int indent_level; + +/* + * The following array contains statically allocated space for Argo values. + * You *must* use the elements of this array to store your values. + * The "argo_next_value" variable contains the index of the next unused slot + * in the array. As you use the elements of the array, you should increment + * this value. Pay attention to when all elements are used, so that you don't + * use a "value" beyond the end of the array and corrupt something else in memory! + */ +#define NUM_ARGO_VALUES 100000 +ARGO_VALUE argo_value_storage[NUM_ARGO_VALUES]; +int argo_next_value; + +/* + * The following array contains storage to hold digits of an integer during + * output conversion (the digits are naturally generated in the reverse order + * from which they will be output). You *must* use this array to hold the + * digits; you may not declare your own arrays! + */ + +#define ARGO_MAX_DIGITS 10 +ARGO_CHAR argo_digits[ARGO_MAX_DIGITS]; + +/* + * Prototypes for functions that you must implement. + * For detailed specifications of these functions, refer to the + * stubs in the file "argo.c", as well as to the assignment handout. + * Note that you will almost certainly want to implement additional + * functions besides those specified here. + */ +ARGO_VALUE *argo_read_value(FILE *); +int argo_read_string(ARGO_STRING *s, FILE *); +int argo_read_number(ARGO_NUMBER *n, FILE *); + +int argo_write_value(ARGO_VALUE *, FILE *); +int argo_write_string(ARGO_STRING *, FILE *); +int argo_write_number(ARGO_NUMBER *, FILE *); + +int validargs(int argc, char **argv); + +#endif diff --git a/hw1/lib/argo.a b/hw1/lib/argo.a new file mode 100644 index 0000000000000000000000000000000000000000..dfc9ca1d88b501c538ce6c38c68fec63dbce669a GIT binary patch literal 19100 zcmc&+4|r77m48V!hH*0drM~QM zL$IOBAFOQHWY;x>8V!GVLqlb-E);023+Aprf^}6T4YK8`HP~$4(n}UB5T2NK z>B6#$L~eb-HsA7Pxj8w|T$yr=%Z+qNp@#9KFP-;_tk>`up#me*X{qhUUO4EupB>+uWu`Z!I z<{OGO4Gl-ewGTr|E`mZq#3N0KcI3oFqX0DR(~x8r!t+%7BzT@2DUfWf9riPE-;j%* zE+0k`S}63{pdscPj5ZApds_1HkWi6``v$Gpn`8};rg~Zip^nHsa80hr-Q{_3iOj9> zJTM<}P$`;N6HS?(`=>!M)?w|O*eX=*6b8q~W4C7yJXcnqRa+6cC|NxUOpO8ggKls4{Dxx~$G5`W}Kgp8E!Eu}2<)*)-> z62OhbX27uVmGG8=t5q)X#gI$muLiO-6*5XNv<|ae@Hs$6!6z$m{RJwQc-&2JQi&5o zRH*Ql0;UUo2_O{wQ+P{@pM@L*)Etc7!MV*V;aRrrIzda6xvAO?6=WO8aJ>U(!&C`z zf3uL0yvb#_$D0P@k-??0&z8mpvJd*nZOPNyl0|LFQ`?dSZOL(M$uVuooHo?$GM-|Q zKIueXDmm7Q`cgT_=-F$ublB%Wwhu@3cxVueGNO3)ca-@C?bj7`;1A9iv}?c*P4;c@ zTxYL>=WP21c$)Tdcox{p;CZU;h3CokqCcg9*!{^fV2t-=jB(+ztc?>*xK0>}6VqMx z5?9qj@yO5#e2c34?}Ddl_Gh1Cv*lDb&qbnZ4+$w3!&63z2V~%kWyNwqEX<;Y)!G4* zh+rEpl?fDi;-^_LoQGvL?k%!nSLQ>5n$)cGjtDXI%}{#&OI9g^Eyv)d$m!CDobM(O zHQRPyW@e*gw`B$mO>R`SL%)DxjF_7MhhT0|y1PG9&T7h{qUnSx#*XY_FRVLHUu}S&&65d#z|wuMsJ6k(S-dtF8EoUg1v>FiR&{v6Uv22dvoA8Sp-A$yK^nMhTW7 zMB)`#&X;tN2Mi2|SAg%z^MzZjcqCP0&0ip#Xpa?(_=!i;7oV!`T=QodP5@11TI&9D4WuFZw+NyRE5NLN5j3{ACmQFJKYw z68k!w!Gt+oUfAzYK29daaIhrQR=9sH)I!CcmU@{pwk(12x!c_41!0BzIxVL6MN-6C zPwSwZ=!M(=elXoU*jl94;>I{NH*yAg3v7{-8XDUF8dL*XRRk79Ut&L6uR5;(pCzr9 zXoZ~ieMt)u4NquJy*ni>Of)=lIJDa&%_bV2>KxjYk{0o_rU35p4Zss#xa~Ij55ogY z>_hGsK346U+l(zMYp#H&SLBy9Ukmy5B469=zDkd#bNfIa2%6hG9u;iMhYd|MQJS11 z6d|^Zu!YI9ptN4_)e?3KjSd*wFt>L0jfowE^)#m^<{OCm26JHVlIQvB5;%6mD+V$< zB9r%RZe>BxN_34r!@{+ZP>rbXoZ%^twY8jDTl$^4ol&lE?*CFboq|R zS1-P5J;yqZdnSPYyL@792Q}>w(<^gYQr1uQmAnw7g zO#L3M?2a~d&xuTn`g)Mi995N7f-x!?#0gRhI1U0AS7UZgJ~#>(Pxb?de3poGt-w4z629WJgskod!xQ1 z*;{^OK>6tFx4Z-9d>15gkTaWQb*b6clCp@Gn}8k~OMc;AHDnaba z+S*wo4E;N3V!$qdr_a+m1IM?oH>b;Y6hn%efzy(Ex4(5vHGUS(Vh|>misxzR>bLn) zXmoZN4>>Zvz61XoERR_km~Fo&d482V;$0MD!T;Wb4y)5I|7*V^6c_w_49F>8>HM6TBvx zQ)4~%zX9fq;+6)SBx=!d;4mzOc#_`* zwdFDECCH&zx=$Iviwd}*`K`E5vG0^jyWN>ymWPW~(q+vqV(OqGc)Cn-taagNgP{c+ zlO@L@`I?_`c9;Hv*g6yHgMnR%X>jWVm8h@2d_Q;Sz+GTSA|P6C(*biLXcj$|xDGF| zXoY?m)*BE_Jh+ep1*S=OdGp1Nkcwq+PtfuYK=0ovnBee-Zj{r19!yqgDgVf%KsEbD z!KrpxAiC^eq)?jjTU_p@NMjBiz=LPvC#MKm)L6fybtOz`D8b*9B1f9oT7|ce@?tL0 z0(F#ZJaHy&%#b}AN3OwYQ)IOfj?8uBcO|yNtx4Jif2t#w$nYRdU%`=&f_lx7wJ^@p zQR6uBEw~#=!=rQLWq8LWBIeKmJZvNiGGx0rGGh$GV8dxk4YqqgLfV4sh;jfA)`@3f zqnW17RWuW6-^-xkr3N4Cz+q(Ulb8u7W)yQJ%r_X(ux&kbU@B_6P->&DH~YmsFfv0U%4{E)GPkmdbxvSo7}mMx4Wj4YkR;&E6i7>kW8j}gl;C`el_Vk{A4 zxl6LN{tODzRh_BAXrITq!}shRQv^4eX{TRHHST9M@=sXfOi?2O7covAS= zN@Nyw+=)!tFV~(+Mcb0A+ma=1$ysg58EwhwZAsksW<*PyFxFQvs zl0gWW){rwXQ83>MwK4`rxjICEeB62zw?G8qHv}w0WKV*~PQb-g;*Xw(J3MceYn;eIg0b~{;N64cWe)bir?A??$ffbJ97?$R5uD--DL6}!^Or#DzW^RxzHTUk z$`uLg`3`ryp9KpXXw(Gj)Yke9(gLSm|wjai4Swdwn^lSQS<@9p}Se@>-hvk9G*!Dty3uGxeW@2;{Q5m+a zUQ--C=m3gG&RWl0-@qLgXGFvYa=%30Sp+5A@eb&F;Q?8{#5Zu~DNfd|7rgMGee>MT z#@yswJa}dhoP$sX3f6QszL6}B)(iF(czA+%LjzXJbCUvu-J9Q1c8mC&lVSJ0a!`oy zEdddU@fqZ5AkzsPWV&m@bJ=VdLU3~ElV1i=8f-gXfXZp#Pl6JhrBM=2*0;$MCegDd$9CW&q9HkRcmC<{{%&oKOUl7_?VrF#RnseY&ov%h!v( zim&LgsynT^4jh?QXTppfgh3fQ+?hHTckXnoD(;NEvh$k|y}fR6i>JuA59IV%b+1b~ zADSSAWW;W05|O?){O3m+?!kwtw$S2nx8RB&H}^I z`;zbAv25F+Bz%kzv*!%>ezp?xa(f_-muh>EoCX9rg@fO5!T%*VH!W*AP2cG=<*&rv zI9VvCc$du;->@X&-qL6)*FGB_n@d6LxzSWkOs zj9P!NF<^uu!61kdN!7nL1V03+t1rkU5H>HIUVR}}FE;%)2=<3hgFonMe{DlB*s!@Sw88Y7LHHR<82Idl+XJ}ea_Wz={S8&$4phS~OVz7@ zAJJ?A1YGUAewn#>V_h&{;*V!gonmHsR@5~%B1w3+DqwF81VY4T)(7gV0^vsCR3-m< zq0bC8*d`cWUl;PDs;aF*fQ#Iazdm5rX(Cz|I5a2D1tGTZpYR5n=hCzD273B zEslTqk=bl!VIxFoW_w1~0~rk==m+w&tD~xNdx@wU* zqoOJjvLj|$$^4SiIrA1qL~&m8MWrRB3yLKyYvK=&ir^17IoEuv!hmJH12NdDh%}Mj zId|S*ijEFd_4B5TQTDAR`W+}l(R1q^`F9h2DyOd&WZ|D( zM4!g#Ytr<6M8{LFBR{V=-F~89n?d(F^r0`IPk~^;6vc;Q|DBHgi-}&$>Fd+>;dfF@ zgZNhU^OCv9F$JKziT(==PKW;7SViAO^dAD*q33?r(Z7%A3pqU`^&^xm<+=V66UT%a z6`3{j#LxFKF)ZbYQRRlijetwaGhpDjq&)G{*pY-h11{6Fgk3hlI6jOahe^G0jJx4x z797trZv0CK$9f{&{ax`Yho>9=dcsY3W11mf25!92COzTm<%Z+Ev>Sd8;HN=<;jf@D ziMa&ce*=GR{5Y1~@Taoi&jXI~3r>d&Br%rY{VM#q$ua(dKTvM8YXaf0QM(D2WV_a= z-4L*=m{s;#d8Ta?pPY7r#x0GU6Ds3cnKam=K!)e+s`IK}fni zt0X)EZ(ulH3@ADK2}k*-G5j}#qkL}vds%SxQ*PwPSwhKQPH~CyaV=H&!-OM0((u9( z)9x(ze;^|y#XnsZjfFHNDSRg3Z5m!mcmXPdg#5g}iWtt@dnUtqf6Zh#W*4JSNDB$a z7w@kmckUS034gzN2H&v34H1H-xA z5W`W2+C$-o228r%-He~N_X&n`z0WY5>wTW#s006T0;Vp)b-nK}9G5mVFZ~ab!|fSl zIJf5y4CnUb(pE-|)8%B3hj87VD8unzn<#spV{*7X&oi9c)5&md&k=^BZkz1+Pr`M3 zPJ)kkn2?R@Eo3;?dpg6p-pLF{9Z!>9{78sN*ZTv8^Kp7G3*N)zb35N;IJfg1hI2bV zWH{P%lIsH(d!Au9>Q-_4JmI=Mb5R*2-47Qtoaw1HXA6ZkhA8Thg zW>tIlGo0J=62rMYuQMEV2g#my3D@nJUIO~zxWd9k2>-H~X zIJc)M3w|&Q{vP4_IPGURw|{`)-2OZ)KvMQEK!YI_5w6=mRpUQG{0lVvDB-s=IlR43 zW#K=Ng+HP3E4_KRDTjpWxcq5^>;9R^aPFUZ3`bQep08#&>IhQ1RuYay%qsp?hI4y< z&2YXBenhzP|3@T$Js#d5A&yIylK&jTajH=GYYgZ7Z!w(nzncXg$bx^ua4tWO?h(*V zE`Jfju}QnBUCXlIXP{Fc>2j_+Q{q@1v#Pyz7CbUZ@+*I~liqu>;5BEZ`LT|Y|NSiZ zi&^kvS@4x~p@6b+2rBuZEclKr_<4AN2uYU{&Vr{HjxJDfH4VRELQ?)w<7kP7tMPlI zhAaM{hAaLi4Id)^zp3Gh|Lt?q_Hh5)a;`i6NEUppndV28(`C`9)9{&u-=X29g#V6k zlz}0m`fCf((QkP4Qsn-y{4v4gW8~+ci8%_&yCk zMtFyYe@ys`8vYpJhc)~Ugm-KBFyTEKo`(Sg>4)x56s9w&UC#vh*~%#U%|>hA*Bd?Gz4%tg^pV{1+dF zVIsir*Kkt?$*bWjX+N=E!|x<~yM~7*OF6qWe3mKk4h=sBgGdi}7g~tEfI+GvGN#uaB4>I{+EI +#include + +#include "argo.h" +#include "global.h" +#include "debug.h" + +/** + * @brief Read JSON input from a specified input stream, parse it, + * and return a data structure representing the corresponding value. + * @details This function reads a sequence of 8-bit bytes from + * a specified input stream and attempts to parse it as a JSON value, + * according to the JSON syntax standard. If the input can be + * successfully parsed, then a pointer to a data structure representing + * the corresponding value is returned. See the assignment handout for + * information on the JSON syntax standard and how parsing can be + * accomplished. As discussed in the assignment handout, the returned + * pointer must be to one of the elements of the argo_value_storage + * array that is defined in the const.h header file. + * In case of an error (these include failure of the input to conform + * to the JSON standard, premature EOF on the input stream, as well as + * other I/O errors), a one-line error message is output to standard error + * and a NULL pointer value is returned. + * + * @param f Input stream from which JSON is to be read. + * @return Zero if the operation is completely successful, + * nonzero if there is any error. + */ +ARGO_VALUE *argo_read_value(FILE *f) { + // TO BE IMPLEMENTED. + abort(); +} + +/** + * @brief Read JSON input from a specified input stream, attempt to + * parse it as a JSON string literal, and return a data structure + * representing the corresponding string. + * @details This function reads a sequence of 8-bit bytes from + * a specified input stream and attempts to parse it as a JSON string + * literal, according to the JSON syntax standard. If the input can be + * successfully parsed, then a pointer to a data structure representing + * the corresponding value is returned. + * In case of an error (these include failure of the input to conform + * to the JSON standard, premature EOF on the input stream, as well as + * other I/O errors), a one-line error message is output to standard error + * and a NULL pointer value is returned. + * + * @param f Input stream from which JSON is to be read. + * @return Zero if the operation is completely successful, + * nonzero if there is any error. + */ +int argo_read_string(ARGO_STRING *s, FILE *f) { + // TO BE IMPLEMENTED. + abort(); +} + +/** + * @brief Read JSON input from a specified input stream, attempt to + * parse it as a JSON number, and return a data structure representing + * the corresponding number. + * @details This function reads a sequence of 8-bit bytes from + * a specified input stream and attempts to parse it as a JSON numeric + * literal, according to the JSON syntax standard. If the input can be + * successfully parsed, then a pointer to a data structure representing + * the corresponding value is returned. The returned value must contain + * (1) a string consisting of the actual sequence of characters read from + * the input stream; (2) a floating point representation of the corresponding + * value; and (3) an integer representation of the corresponding value, + * in case the input literal did not contain any fraction or exponent parts. + * In case of an error (these include failure of the input to conform + * to the JSON standard, premature EOF on the input stream, as well as + * other I/O errors), a one-line error message is output to standard error + * and a NULL pointer value is returned. + * + * @param f Input stream from which JSON is to be read. + * @return Zero if the operation is completely successful, + * nonzero if there is any error. + */ +int argo_read_number(ARGO_NUMBER *n, FILE *f) { + // TO BE IMPLEMENTED. + abort(); +} + +/** + * @brief Write canonical JSON representing a specified value to + * a specified output stream. + * @details Write canonical JSON representing a specified value + * to specified output stream. See the assignment document for a + * detailed discussion of the data structure and what is meant by + * canonical JSON. + * + * @param v Data structure representing a value. + * @param f Output stream to which JSON is to be written. + * @return Zero if the operation is completely successful, + * nonzero if there is any error. + */ +int argo_write_value(ARGO_VALUE *v, FILE *f) { + // TO BE IMPLEMENTED. + abort(); +} + +/** + * @brief Write canonical JSON representing a specified string + * to a specified output stream. + * @details Write canonical JSON representing a specified string + * to specified output stream. See the assignment document for a + * detailed discussion of the data structure and what is meant by + * canonical JSON. The argument string may contain any sequence of + * Unicode code points and the output is a JSON string literal, + * represented using only 8-bit bytes. Therefore, any Unicode code + * with a value greater than or equal to U+00FF cannot appear directly + * in the output and must be represented by an escape sequence. + * There are other requirements on the use of escape sequences; + * see the assignment handout for details. + * + * @param v Data structure representing a string (a sequence of + * Unicode code points). + * @param f Output stream to which JSON is to be written. + * @return Zero if the operation is completely successful, + * nonzero if there is any error. + */ +int argo_write_string(ARGO_STRING *s, FILE *f) { + // TO BE IMPLEMENTED. + abort(); +} + +/** + * @brief Write canonical JSON representing a specified number + * to a specified output stream. + * @details Write canonical JSON representing a specified number + * to specified output stream. See the assignment document for a + * detailed discussion of the data structure and what is meant by + * canonical JSON. The argument number may contain representations + * of the number as any or all of: string conforming to the + * specification for a JSON number (but not necessarily canonical), + * integer value, or floating point value. This function should + * be able to work properly regardless of which subset of these + * representations is present. + * + * @param v Data structure representing a number. + * @param f Output stream to which JSON is to be written. + * @return Zero if the operation is completely successful, + * nonzero if there is any error. + */ +int argo_write_number(ARGO_NUMBER *n, FILE *f) { + // TO BE IMPLEMENTED. + abort(); +} diff --git a/hw1/src/const.c b/hw1/src/const.c new file mode 100644 index 0000000..72d31c7 --- /dev/null +++ b/hw1/src/const.c @@ -0,0 +1,47 @@ +/* + * DO NOT MODIFY THE CONTENTS OF THIS FILE. + * IT WILL BE REPLACED DURING GRADING + */ + +#include + +#include "argo.h" +#include "global.h" +#include "debug.h" + +/** + * @brief Append a specified character to a specified string. + * @details This function takes a pointer to an ARGO_STRING structure, + * which is assumed previously to have been initialized (an ARGO_STRING + * is initialized to empty by zeroing its fields) and an ARGO_CHAR + * value, representing an arbitrary Unicode code point, and it appends + * the character to the string, taking care of reallocating the string + * content into a new memory area if its length has grown to exceed + * the area already allocated for it. + * + * @return Zero if the operation completes without error, nonzero if + * there was a failure to allocate additional memory required to + * accomodate the string. + */ +int argo_append_char(ARGO_STRING *s, ARGO_CHAR c) { + if(s->capacity == 0) { + s->capacity = 10; + s->content = malloc(s->capacity * sizeof(ARGO_CHAR)); + if(!s->content) { + fprintf(stderr, "[%d] Failed to allocate space for string text", + argo_lines_read); + return 1; + } + } + if(s->length == s->capacity) { + s->capacity *= 2; + s->content = realloc(s->content, s->capacity * sizeof(ARGO_CHAR)); + if(!s->content) { + fprintf(stderr, "[%d] Failed to allocate space for string text", + argo_lines_read); + return 1; + } + } + s->content[s->length++] = c; + return 0; +} diff --git a/hw1/src/main.c b/hw1/src/main.c new file mode 100644 index 0000000..62631ad --- /dev/null +++ b/hw1/src/main.c @@ -0,0 +1,33 @@ +#include +#include + +#include "argo.h" +#include "global.h" +#include "debug.h" + +#ifdef _STRING_H +#error "Do not #include . You will get a ZERO." +#endif + +#ifdef _STRINGS_H +#error "Do not #include . You will get a ZERO." +#endif + +#ifdef _CTYPE_H +#error "Do not #include . You will get a ZERO." +#endif + +int main(int argc, char **argv) +{ + if(validargs(argc, argv)) + USAGE(*argv, EXIT_FAILURE); + if(global_options == HELP_OPTION) + USAGE(*argv, EXIT_SUCCESS); + // TO BE IMPLEMENTED + return EXIT_FAILURE; +} + +/* + * Just a reminder: All non-main functions should + * be in another file not named main.c + */ diff --git a/hw1/src/validargs.c b/hw1/src/validargs.c new file mode 100644 index 0000000..c218d35 --- /dev/null +++ b/hw1/src/validargs.c @@ -0,0 +1,26 @@ +#include + +#include "argo.h" +#include "global.h" +#include "debug.h" + +/** + * @brief Validates command line arguments passed to the program. + * @details This function will validate all the arguments passed to the + * program, returning 0 if validation succeeds and -1 if validation fails. + * Upon successful return, the various options that were specified will be + * encoded in the global variable 'global_options', where it will be + * accessible elsewhere in the program. For details of the required + * encoding, see the assignment handout. + * + * @param argc The number of arguments passed to the program from the CLI. + * @param argv The argument strings passed to the program from the CLI. + * @return 0 if validation succeeds and -1 if validation fails. + * @modifies global variable "global_options" to contain an encoded representation + * of the selected program options. + */ + +int validargs(int argc, char **argv) { + // TO BE IMPLEMENTED + abort(); +} diff --git a/hw1/tests/basecode_tests.c b/hw1/tests/basecode_tests.c new file mode 100644 index 0000000..d8cf83b --- /dev/null +++ b/hw1/tests/basecode_tests.c @@ -0,0 +1,95 @@ +#include +#include + +#include "argo.h" +#include "global.h" + +static char *progname = "bin/argo"; + +Test(basecode_suite, validargs_help_test) { + char *argv[] = {progname, "-h", NULL}; + int argc = (sizeof(argv) / sizeof(char *)) - 1; + int ret = validargs(argc, argv); + int exp_ret = 0; + int opt = global_options; + int flag = HELP_OPTION; + cr_assert_eq(ret, exp_ret, "Invalid return for validargs. Got: %d | Expected: %d", + ret, exp_ret); + cr_assert_eq(opt & flag, flag, "Correct bit (0x%x) not set for -h. Got: %x", + flag, opt); +} + +Test(basecode_suite, validargs_validate_test) { + char *argv[] = {progname, "-v", NULL}; + int argc = (sizeof(argv) / sizeof(char *)) - 1; + int ret = validargs(argc, argv); + int exp_ret = 0; + int opt = global_options; + int exp_opt = VALIDATE_OPTION; + cr_assert_eq(ret, exp_ret, "Invalid return for validargs. Got: %d | Expected: %d", + ret, exp_ret); + cr_assert_eq(opt, exp_opt, "Invalid options settings. Got: 0x%x | Expected: 0x%x", + opt, exp_opt); +} + +Test(basecode_suite, validargs_canonicalize_test) { + char *argv[] = {progname, "-c", NULL}; + int argc = (sizeof(argv) / sizeof(char *)) - 1; + int ret = validargs(argc, argv); + int exp_ret = 0; + int opt = global_options; + int exp_opt = CANONICALIZE_OPTION | 0x4; + cr_assert_eq(ret, exp_ret, "Invalid return for validargs. Got: %d | Expected: %d", + ret, exp_ret); + cr_assert_eq(opt, exp_opt, "Invalid options settings. Got: 0x%x | Expected: 0x%x", + opt, exp_opt); +} + +Test(basecode_suite, validargs_pretty_print_test) { + char *argv[] = {progname, "-c", "-p", "13", NULL}; + int argc = (sizeof(argv) / sizeof(char *)) - 1; + int ret = validargs(argc, argv); + int exp_ret = 0; + int opt = global_options; + int exp_opt = CANONICALIZE_OPTION | PRETTY_PRINT_OPTION | 13; + cr_assert_eq(ret, exp_ret, "Invalid return for validargs. Got: %d | Expected: %d", + ret, exp_ret); + cr_assert_eq(opt, exp_opt, "Invalid options settings. Got: 0x%x | Expected: 0x%x", + opt, exp_opt); +} + +Test(basecode_suite, validargs_error_test) { + char *argv[] = {progname, "-v", "-p", NULL}; + int argc = (sizeof(argv) / sizeof(char *)) - 1; + int exp_ret = -1; + int ret = validargs(argc, argv); + cr_assert_eq(ret, exp_ret, "Invalid return for validargs. Got: %d | Expected: %d", + ret, exp_ret); +} + +Test(basecode_suite, help_system_test) { + char *cmd = "bin/argo -h > /dev/null 2>&1"; + + // system is a syscall defined in stdlib.h + // it takes a shell command as a string and runs it + // we use WEXITSTATUS to get the return code from the run + // use 'man 3 system' to find out more + int return_code = WEXITSTATUS(system(cmd)); + + cr_assert_eq(return_code, EXIT_SUCCESS, + "Program exited with 0x%x instead of EXIT_SUCCESS", + return_code); +} + +Test(basecode_suite, argo_basic_test) { + char *cmd = "bin/argo -c < rsrc/strings.json > test_output/strings_-c.json"; + char *cmp = "cmp test_output/strings_-c.json tests/rsrc/strings_-c.json"; + + int return_code = WEXITSTATUS(system(cmd)); + cr_assert_eq(return_code, EXIT_SUCCESS, + "Program exited with 0x%x instead of EXIT_SUCCESS", + return_code); + return_code = WEXITSTATUS(system(cmp)); + cr_assert_eq(return_code, EXIT_SUCCESS, + "Program output did not match reference output."); +} diff --git a/hw1/tests/rsrc/strings_-c.json b/hw1/tests/rsrc/strings_-c.json new file mode 100644 index 0000000..1e650e4 --- /dev/null +++ b/hw1/tests/rsrc/strings_-c.json @@ -0,0 +1 @@ +["","a"," ","\\","\"","\\"," ","\b\f\n\t\r","\b\f\n\t\u000b"] \ No newline at end of file