diff --git a/.gitignore b/.gitignore index b0ab508..d14260a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,9 @@ *.obj *.elf +# Debugger +*.pdb + # Linker output *.ilk *.map diff --git a/include/fatals.h b/include/fatals.h index 335cd43..2230462 100644 --- a/include/fatals.h +++ b/include/fatals.h @@ -1,8 +1,10 @@ #ifndef ACC_FATALS_H #define ACC_FATALS_H -#include "noreturn.h" +#include +#include +void* malloc_or_fail(size_t s, const char *func_name); noreturn void fail_malloc(const char *func_name); noreturn void fail_ast_op(int op, const char *func_name); noreturn void fail_ce_expect(const char *expected, const char *got); diff --git a/include/noreturn.h b/include/noreturn.h deleted file mode 100644 index 169bcc8..0000000 --- a/include/noreturn.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef ACC_NORETURN_H -#define ACC_NORETURN_H - -#define noreturn _Noreturn - -#endif \ No newline at end of file diff --git a/include/token.h b/include/token.h index 54e16a6..d4fc1cc 100644 --- a/include/token.h +++ b/include/token.h @@ -19,9 +19,9 @@ enum { T_EQ, T_NE, T_LT, T_GT, T_LE, T_GE, T_INT, T_VOID, T_CHAR, T_LONG, T_PRINT, T_IF, T_ELSE, T_WHILE, T_FOR, - T_INTLIT, T_LONGLIT, T_INDENT, + T_I32_LIT, T_I64_LIT, T_INDENT, }; -extern const char *token_typename[29]; +extern const char *token_typename[63]; void token_free(struct token *t); struct token token_make_eof(void); diff --git a/include/util/linklist.h b/include/util/linklist.h index 80c95bc..c65d000 100644 --- a/include/util/linklist.h +++ b/include/util/linklist.h @@ -13,13 +13,19 @@ struct linklist { }; struct llist_node* llist_createnode(void *val); -void llist_pushback(struct linklist *l, void *val); -void llist_pushback_notnull(struct linklist *l, void *val); -void* llist_get(struct linklist *l, int x); -void llist_set(struct linklist *l, int x, void *val); + void llist_init(struct linklist *l); void llist_free(struct linklist *l); +void llist_free_full(struct linklist *l); + +void llist_pushback(struct linklist *l, void *val); +void llist_pushback_notnull(struct linklist *l, void *val); + +void* llist_get(struct linklist *l, int x); +void llist_set(struct linklist *l, int x, void *val); + void llist_insert(struct linklist *l, int x, void *val); -void llist_popfront(struct linklist *l); +void* llist_popfront(struct linklist *l); +void* llist_remove(struct linklist *l, int index); #endif diff --git a/include/util/misc.h b/include/util/misc.h index f2ddde6..008fabf 100644 --- a/include/util/misc.h +++ b/include/util/misc.h @@ -1,7 +1,9 @@ #ifndef ACC_UTIL_MISC_H #define ACC_UTIL_MISC_H -int strequal(const char *s1, const char *s2); +#include + +bool strequal(const char *s1, const char *s2); char *strclone(const char *s); #endif diff --git a/src/ast.c b/src/ast.c index 81b7e3b..436aa8f 100644 --- a/src/ast.c +++ b/src/ast.c @@ -6,10 +6,7 @@ // Build and return a binary AST node struct ASTnode* ast_make_binary(int op, struct ASTnode *left, struct ASTnode *right) { - struct ASTbinnode *x = malloc(sizeof(struct ASTbinnode)); - if (x == NULL) { - fail_malloc(__FUNCTION__); - } + struct ASTbinnode *x = malloc_or_fail(sizeof(struct ASTbinnode), __FUNCTION__); x->op = op; x->left = left; @@ -19,10 +16,7 @@ struct ASTnode* ast_make_binary(int op, struct ASTnode *left, struct ASTnode *ri // Make an AST int literal node struct ASTnode* ast_make_intlit(int val) { - struct ASTintnode *x = malloc(sizeof(struct ASTintnode)); - if (x == NULL) { - fail_malloc(__FUNCTION__); - } + struct ASTintnode *x = malloc_or_fail(sizeof(struct ASTintnode), __FUNCTION__); x->op = A_INTLIT; x->val = val; @@ -31,10 +25,7 @@ struct ASTnode* ast_make_intlit(int val) { // Make an AST variable value node struct ASTnode* ast_make_var(int id) { - struct ASTvarnode *x = malloc(sizeof(struct ASTvarnode)); - if (x == NULL) { - fail_malloc(__FUNCTION__); - } + struct ASTvarnode *x = malloc_or_fail(sizeof(struct ASTvarnode), __FUNCTION__); x->op = A_VAR; x->id = id; @@ -43,10 +34,7 @@ struct ASTnode* ast_make_var(int id) { // Make a unary AST node: only one child struct ASTnode* ast_make_unary(int op, struct ASTnode *c) { - struct ASTunnode *x = malloc(sizeof(struct ASTunnode)); - if (x == NULL) { - fail_malloc(__FUNCTION__); - } + struct ASTunnode *x = malloc_or_fail(sizeof(struct ASTunnode), __FUNCTION__); x->op = op; x->c = c; @@ -55,10 +43,7 @@ struct ASTnode* ast_make_unary(int op, struct ASTnode *c) { // Make a block ast node struct ASTnode* ast_make_block() { - struct ASTblocknode *x = malloc(sizeof(struct ASTblocknode)); - if (x == NULL) { - fail_malloc(__FUNCTION__); - } + struct ASTblocknode *x = malloc_or_fail(sizeof(struct ASTblocknode), __FUNCTION__); x->op = A_BLOCK; llist_init(&x->st); @@ -67,10 +52,7 @@ struct ASTnode* ast_make_block() { // Make a assignment ast node struct ASTnode* ast_make_assign(int op, int left, struct ASTnode *right) { - struct ASTassignnode *x = malloc(sizeof(struct ASTassignnode)); - if (x == NULL) { - fail_malloc(__FUNCTION__); - } + struct ASTassignnode *x = malloc_or_fail(sizeof(struct ASTassignnode), __FUNCTION__); x->op = op; x->left = left; @@ -80,10 +62,7 @@ struct ASTnode* ast_make_assign(int op, int left, struct ASTnode *right) { // Make a if statement ast node struct ASTnode* ast_make_if(struct ASTnode *left, struct ASTnode *right, struct ASTnode *cond) { - struct ASTifnode *x = malloc(sizeof(struct ASTifnode)); - if (x == NULL) { - fail_malloc(__FUNCTION__); - } + struct ASTifnode *x = malloc_or_fail(sizeof(struct ASTifnode), __FUNCTION__); x->op = A_IF; x->left = left; diff --git a/src/cg_llvm.c b/src/cg_llvm.c index 7a4977b..f569caa 100644 --- a/src/cg_llvm.c +++ b/src/cg_llvm.c @@ -53,7 +53,7 @@ static int cgcomp_i(int x, int y, char *op, char *ty) { int r1 = alloc_tag(); fprintf(Outfile, "\t%%%d = icmp %s %s %%%d, %%%d\n", r1, op, ty, x, y); int r2 = alloc_tag(); - if (ty[0] != 'i' || ty[1] != '1' || ty[2] != '\0') { + if (strcmp(ty, "i1") != 0) { // special case for bool fprintf(Outfile, "\t%%%d = zext i1 %%%d to %s\n", r2, r1, ty); } return (r2); diff --git a/src/fatals.c b/src/fatals.c index f9dba62..4816afe 100644 --- a/src/fatals.c +++ b/src/fatals.c @@ -7,6 +7,14 @@ void fail_malloc(const char *func_name) { exit(1); } +void* malloc_or_fail(size_t s, const char *func_name) { + void *res = malloc(s); + if (res == NULL) { + fail_malloc(func_name); + } + return (res); +} + void fail_ast_op(int op, const char *func_name) { fprintf(stderr, "%s: unknown ast operator %d.\n", func_name, op); exit(1); diff --git a/src/parse.c b/src/parse.c index 2259866..3486517 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1,5 +1,7 @@ #include #include +#include +#include #include "scan.h" #include "token.h" #include "ast.h" @@ -42,10 +44,10 @@ static int arithop(int t) { {T_GT, A_GT}, {T_GE, A_GE}, {T_ASSIGN, A_ASSIGN}, - {T_EOF} + {-1} }; - for (int i = 0; map[i][0] != T_EOF; ++i) { + for (int i = 0; map[i][0] != -1; ++i) { if (t == map[i][0]) { return map[i][1]; } @@ -54,22 +56,21 @@ static int arithop(int t) { } // operator ssociativity direction -// Return 0 if left to right, e.g. + -// 1 if right to left, e.g. = -static int direction_rtl(int t) { +// Returns false if left to right, e.g. + +// true if right to left, e.g. = +static bool direction_rtl(int t) { switch(t) { case T_ASSIGN: - return (1); + return (true); default: - return (0); + return (false); } } // Next token static void next(void) { if (Tokens.head) { - token_free(Tokens.head->val); - llist_popfront(&Tokens); + token_free(llist_popfront(&Tokens)); } } @@ -78,7 +79,8 @@ static struct token preview(int k) { if (Tokens.length <= k) { return (token_make_eof()); } - return (*((struct token*)llist_get(&Tokens, k))); + struct token* res = llist_get(&Tokens, k); + return (*res); } // return current token from input stream @@ -117,12 +119,12 @@ static struct ASTnode* primary(void) { next(); res = expression(); match(T_RP); - } else if (current().type == T_INTLIT) { - res = ast_make_intlit(*((int*)current().val)); + } else if (current().type == T_I32_LIT) { + res = ast_make_intlit(*(int32_t*)current().val); next(); - } else if (current().type == T_LONGLIT) { + } else if (current().type == T_I64_LIT) { // todo - fprintf(stderr, "TOOD.\n"); + fprintf(stderr, "TOOD: T_I64_LIT.\n"); exit(1); } else if (current().type == T_INDENT) { int id = findglob((char*)current().val); @@ -296,29 +298,24 @@ static struct ASTnode* for_statement(void) { // parse one statement static struct ASTnode* statement(void) { - if (current().type == T_SEMI) { - return (NULL); - } - else if (current().type == T_PRINT) { - return (print_statement()); - } - else if (current().type == T_INT) { - return (var_declaration()); - } - else if (current().type == T_IF) { - return (if_statement()); - } - else if (current().type == T_WHILE) { - return (while_statement()); - } - else if (current().type == T_FOR) { - return (for_statement()); - } - else { - skip_semi = 0; - struct ASTnode* res = expression(); - match(T_SEMI); - return (res); + switch (current().type) { + case T_SEMI: + return (NULL); + case T_PRINT: + return (print_statement()); + case T_INT: + return (var_declaration()); + case T_IF: + return (if_statement()); + case T_WHILE: + return (while_statement()); + case T_FOR: + return (for_statement()); + default: + skip_semi = 0; + struct ASTnode* res = expression(); + match(T_SEMI); + return (res); } } diff --git a/src/scan.c b/src/scan.c index 392cc07..d7a2c3a 100644 --- a/src/scan.c +++ b/src/scan.c @@ -1,8 +1,9 @@ #include #include #include -#include #include +#include +#include #include "token.h" #include "fatals.h" #include "util/misc.h" @@ -11,18 +12,6 @@ int Line = 1; static int Preview; static FILE *Infile; -const char *token_typename[] = { - "EOF", - ";", - "{", "}", "(", ")", - "=", - "+", "-", "*", "/", - "==", "!=", "<", ">", "<=", ">=", - "int", "void", "char", "long", - "print", "if", "else", "while", "for", - "an integer literal (type int)", "an integer literal (type long)", "an indentifier" -}; - // preview one char, not getting it out from the stream static int preview(void) { if (!Preview) { @@ -43,9 +32,7 @@ static void next(void) { // Skip past input that we don't need to deal with, // i.e. whitespace, newlines. static void skip_whitespaces(void) { - int c; - - c = preview(); + int c = preview(); while (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f') { next(); c = preview(); @@ -53,7 +40,7 @@ static void skip_whitespaces(void) { } // Scan and return an integer literal value from the input file. -static void scanint(struct token *t) { +static void scan_int(struct token *t) { long long res = 0; int c = preview(); while ('0' <= c && c <= '9') { @@ -62,40 +49,35 @@ static void scanint(struct token *t) { c = preview(); } - if (INT_MIN <= res && res <= INT_MAX) { - t->type = T_INTLIT; - t->val = malloc(sizeof(int)); - if (t->val == NULL) { - fail_malloc(__FUNCTION__); - } - *((int *)t->val) = (int)res; + if (INT32_MIN <= res && res <= INT32_MAX) { + t->type = T_I32_LIT; + t->val = malloc_or_fail(sizeof(int32_t), __FUNCTION__); + *((int32_t *)t->val) = (int)res; } else { - t->type = T_LONGLIT; - t->val = malloc(sizeof(long long)); - if (t->val == NULL) { - fail_malloc(__FUNCTION__); - } - *((long long *)t->val) = res; + t->type = T_I64_LIT; + t->val = malloc_or_fail(sizeof(int64_t), __FUNCTION__); + *((int64_t *)t->val) = res; } } // Scan an identifier from the input file and -// Return the identifier (char*) +// Return the identifier string (char*) +// Writes the length into _n_ or NULL static char* scan_indentifier(int *n) { int sz = 128, len = 0; - char *res = malloc(sz * sizeof(char)); + char *res = malloc_or_fail(sz * sizeof(char), __FUNCTION__); memset(res, 0, sz * sizeof(char)); int c = preview(); while (isdigit(c) || isalpha(c) || c == '_') { if (len >= sz - 1) { sz *= 2; - char *old = res; - res = malloc(sz * sizeof(char)); - memcpy(res, old, len * sizeof(char)); + char *res = realloc(res, sz * sizeof(char)); + if (res == NULL) { + fail_malloc(__FUNCTION__); + } memset(res + len * sizeof(char), 0, (sz - len) * sizeof(char)); - free(old); } res[len++] = c; next(); @@ -108,8 +90,9 @@ static char* scan_indentifier(int *n) { return (res); } -// Given a word from the input, scan if it is a keyword -static int scan_keyword(struct token *t, char *s) { +// Given a word from the input, scan if it is a keyword. +// Returns true if found keyword. +static bool scan_keyword(struct token *t, char *s) { static const char *map_s[] = { "print", "int", @@ -137,15 +120,15 @@ static int scan_keyword(struct token *t, char *s) { for (int i = 0; map_s[i] != NULL; ++i) { if (strequal(map_s[i], s)) { t->type = map_t[i]; - return (1); + return (true); } } - return (0); + return (false); } // Scan one char token // Return 1 if found -static int scan_1c(struct token *t) { +static bool scan_1c(struct token *t) { static const int map[][2] = { {'+', T_PLUS}, {'-', T_MINUS}, @@ -164,18 +147,15 @@ static int scan_1c(struct token *t) { if (map[i][0] == c) { t->type = map[i][1]; next(); - return (1); + return (true); } } - return (0); + return (false); } // Scan and return the next token found in the input. static struct token* scan(void) { - struct token *t = malloc(sizeof(struct token)); - if (t == NULL) { - fail_malloc(__FUNCTION__); - } + struct token *t = malloc_or_fail(sizeof(struct token), __FUNCTION__); t->val = NULL; skip_whitespaces(); @@ -204,8 +184,7 @@ static struct token* scan(void) { t->type = T_NE; next(); } else { - fprintf(stderr, "Unrecognised character %c on line %d.\n", c, Line); - exit(1); + fail_char(c); } } else if (c == '<') { t->type = T_LT; @@ -226,7 +205,7 @@ static struct token* scan(void) { } else { // If it's a digit, scan the integer literal value in if (isdigit(c)) { - scanint(t); + scan_int(t); } else if (isalpha(c) || c == '_') { t->val = scan_indentifier(NULL); if (scan_keyword(t, t->val)) { diff --git a/src/token.c b/src/token.c index fdd07c4..4e3f4da 100644 --- a/src/token.c +++ b/src/token.c @@ -2,6 +2,18 @@ #include #include "token.h" +const char *token_typename[63] = { + "EOF", + ";", + "{", "}", "(", ")", + "=", + "+", "-", "*", "/", + "==", "!=", "<", ">", "<=", ">=", + "int", "void", "char", "long", + "print", "if", "else", "while", "for", + "a signed integer literal (size 32)", "a signed integer literal (size 64)", "an indentifier" +}; + void token_free(struct token *t) { if (t->val) { free(t->val); diff --git a/src/util/linklist.c b/src/util/linklist.c index 3aba276..1a920a7 100644 --- a/src/util/linklist.c +++ b/src/util/linklist.c @@ -3,6 +3,7 @@ #include "fatals.h" #include "util/linklist.h" +// Create a linklist node using given value. struct llist_node* llist_createnode(void *val) { struct llist_node *res = malloc(sizeof(struct llist_node)); if (res == NULL) { @@ -13,6 +14,7 @@ struct llist_node* llist_createnode(void *val) { return (res); } +// Appends an element in the linklist. void llist_pushback(struct linklist *l, void *val) { l->length += 1; if (!l->tail) { @@ -23,44 +25,51 @@ void llist_pushback(struct linklist *l, void *val) { l->tail = l->tail->nxt; } +// A variant of pushback +// Only does pushback if _val_ is not null. void llist_pushback_notnull(struct linklist *l, void *val) { if (val) { llist_pushback(l, val); } } -void* llist_get(struct linklist *l, int x) { - if (x >= l->length) { +// Returns the _index_ thh element. +void* llist_get(struct linklist *l, int index) { + if (index >= l->length) { fprintf(stderr, "linklist out of range.\n"); abort(); } struct llist_node *p = l->head; - for (int i = 0; i < x; ++i) { + for (int i = 0; i < index; ++i) { p = p->nxt; } return (p->val); } -void llist_set(struct linklist *l, int x, void *val) { - if (x >= l->length) { +// Modify the _index_ thh element. +void llist_set(struct linklist *l, int index, void *val) { + if (index >= l->length) { fprintf(stderr, "linklist out of range.\n"); abort(); } struct llist_node *p = l->head; - for (int i = 0; i < x; ++i) { + for (int i = 0; i < index; ++i) { p = p->nxt; } p->val = val; } +// Init a empty linklist. void llist_init(struct linklist *l) { l->length = 0; l->head = NULL; l->tail = NULL; } +// Frees the linklist. +// Caller must make sure all elements in the linklist has already been freed. void llist_free(struct linklist *l) { struct llist_node *p = l->head; struct llist_node *nxt; @@ -72,6 +81,24 @@ void llist_free(struct linklist *l) { llist_init(l); } +// Frees the linklist. +// Callee will free all elements in the link list. +void llist_free_full(struct linklist *l) { + struct llist_node *p = l->head; + struct llist_node *nxt; + while (p) { + nxt = p->nxt; + if (p->val) { + free(p->val); + } + free(p); + p = nxt; + } + llist_init(l); +} + +// Insert _val_ into the linklist as the _index_ th element. +// If _index_ is too large, pushback only! void llist_insert(struct linklist *l, int index, void *val) { if (index >= l->length) { llist_pushback(l, val); @@ -94,18 +121,46 @@ void llist_insert(struct linklist *l, int index, void *val) { p->nxt = x; } -void llist_popfront(struct linklist *l) { +// Pop the first element of the link list +// Return the first element. +void* llist_popfront(struct linklist *l) { if (l->head == NULL) { - return; + return (NULL); } l->length -= 1; + void *res = l->head->val; if (l->length == 0) { free(l->head); l->head = l->tail = NULL; + return (res); } struct llist_node *p = l->head; l->head = p->nxt; free(p); + return (res); +} + +// Removes the _index_ th element from the linklist. +// Returns the removed value. +void* llist_remove(struct linklist *l, int index) { + if (index >= l->length) { + return (NULL); + } + + if (index == 0) { + return (llist_popfront(l)); + } + + l->length -= 1; + struct llist_node *p = l->head; + for (int i = 0; i < index - 2; ++i) { + p = p->nxt; + } + struct llist_node *q = p->nxt; + p->nxt = q->nxt; + void *res = q->val; + free(q); + return res; } diff --git a/src/util/misc.c b/src/util/misc.c index cf6a2f7..85c03ea 100644 --- a/src/util/misc.c +++ b/src/util/misc.c @@ -1,22 +1,15 @@ #include #include +#include "util/misc.h" // check if two string are the same -int strequal(const char *s1, const char *s2) { - for (int i = 1; ; ++i) { - if (s1[i] != s2[i]) { - return (0); - } - - if (s1[i] == '\0') { - break; - } - } - return (1); +bool strequal(const char *s1, const char *s2) { + return (strcmp(s1, s2) == 0); } // A impl of C23 strdup() -char* strclone(char *s) { +// Clones the given string +char* strclone(const char *s) { int n = strlen(s); char *res = malloc(n + 1); memcpy(res, s, n * sizeof(char)); diff --git a/tests/out.input01 b/tests/input01.ans similarity index 100% rename from tests/out.input01 rename to tests/input01.ans diff --git a/tests/out.input02 b/tests/input02.ans similarity index 100% rename from tests/out.input02 rename to tests/input02.ans diff --git a/tests/out.input03 b/tests/input03.ans similarity index 100% rename from tests/out.input03 rename to tests/input03.ans diff --git a/tests/out.input04 b/tests/input04.ans similarity index 100% rename from tests/out.input04 rename to tests/input04.ans diff --git a/tests/out.input05 b/tests/input05.ans similarity index 100% rename from tests/out.input05 rename to tests/input05.ans diff --git a/tests/out.input06 b/tests/input06.ans similarity index 100% rename from tests/out.input06 rename to tests/input06.ans diff --git a/tests/out.input07 b/tests/input07.ans similarity index 100% rename from tests/out.input07 rename to tests/input07.ans diff --git a/tests/test_llvm.lua b/tests/test_llvm.lua new file mode 100644 index 0000000..036b4fa --- /dev/null +++ b/tests/test_llvm.lua @@ -0,0 +1,50 @@ +local acc_path = nil +for _, path in ipairs({ "../acc", "../acc.exe", "../acc.out" }) do + if os.exists(path) then + acc_path = path + end +end + +if acc_path == nil then + error("acc executable not found.") +end + +local function compare_file(p1, p2) + local f1 = io.open(p1, "r"); + local f2 = io.open(p2, "r"); + repeat + local l1 = f1:read() + local l2 = f2:read() + if l1 ~= l2 then + f1:close() + f2:close() + return false + end + until l1 == nil + f1:close() + f2:close() + return true +end + +for _, input in ipairs(os.files("tests/input*")) do + if input:endswith(".ans") then + goto continue + end + + local ans = input .. ".ans" + if not os.exists(ans) then + cprint("${orange} [WARN] not found answer for case %s.", input) + goto continue + end + os.run("acc llvm %s out.ll", input) + os.run("clang out.ll -o test.exe") + local output = os.iorun("test.exe") + local output_path = os.tmpfile() + io.writefile(output_path, output) + if compare_file(output_path, ans) then + cprint("${green} [INFO] case %s: OK.", input) + else + cprint("${red} [INFO] case %s: FAILED.", input) + end + ::continue:: +end diff --git a/tests/test_llvm.sh b/tests/test_llvm.sh deleted file mode 100644 index dfca175..0000000 --- a/tests/test_llvm.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/sh -# Run each test and compare -# against known good output - -set -e - -echo "Testing for target llvm..." - -if [ ! -f ../acc ] -then echo "Need to build ../acc first!"; exit 1 -fi - -for i in input* -do if [ ! -f "out.$i" ] - then echo "Can't run test on $i, no answer file!" - else - echo -n $i - ../acc llvm $i - clang -o out out.ll -w - ./out > trial.$i - cmp -s "out.$i" "trial.$i" - if [ "$?" -eq "1" ] - then echo ": failed" - diff -c "out.$i" "trial.$i" - echo - else echo ": OK" - fi - rm -f out out.ll "trial.$i" - fi -done diff --git a/tests/test_x64.sh b/tests/test_x64.sh deleted file mode 100644 index f081768..0000000 --- a/tests/test_x64.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/sh -# Run each test and compare -# against known good output - -set -e - -echo "Testing for target x86_64..." - -if [ ! -f ../acc ] -then echo "Need to build ../acc first!"; exit 1 -fi - -for i in input* -do if [ ! -f "out.$i" ] - then echo "Can't run test on $i, no answer file!" - else - echo -n $i - ../acc x86_64 $i - gcc -o out out.s - ./out > trial.$i - cmp -s "out.$i" "trial.$i" - if [ "$?" -eq "1" ] - then echo ": failed" - diff -c "out.$i" "trial.$i" - echo - else echo ": OK" - fi - rm -f out out.s "trial.$i" - fi -done