/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * This is GNU Go, a Go program. Contact gnugo@gnu.org, or see * * http://www.gnu.org/software/gnugo/ for more information. * * * * Copyright 1999, 2000, 2001, 2002, 2003 and 2004 * * by the Free Software Foundation. * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation - version 2 * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License in file COPYING for more details. * * * * You should have received a copy of the GNU General Public * * License along with this program; if not, write to the Free * * Software Foundation, Inc., 59 Temple Place - Suite 330, * * Boston, MA 02111, USA. * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* patlib.c * * A collection of routines for handling patterns in text form. */ /* See also patterns.h, and the *.db files. */ #include #include #include #include #include #include #include "liberty.h" #include "gg_utils.h" #include "patterns.h" #include "patlib.h" /* ================================================================ */ /* Miscellaneous declarations */ /* ================================================================ */ /* valid characters that can appear in a pattern * position in string is att value to store */ const char VALID_PATTERN_CHARS[] = ".XOxo,a!*?Q"; const char VALID_EDGE_CHARS[] = "+-|"; const char VALID_CONSTRAINT_LABELS[] = "abcdefghijklmnpqrstuvwyzABCDEFGHIJKLMNQRSTUVWYZ"; /* The offsets into the list are the ATT_* defined in patterns.h. * The following defns are for internal use only, and are not * written out to the compiled pattern database. */ #define ATT_star 8 #define ATT_wild 9 #define ATT_Q 10 /* ================================================================ */ /* The textpattern structure */ /* ================================================================ */ void textpattern_clear(Textpattern *tp) { tp->name = NULL; tp->comment = NULL; tp->height = 0; tp->width = 0; tp->num_elements = 0; tp->elements = NULL; tp->edge_constraints = 0; tp->explanation = NULL; tp->comment2 = NULL; tp->entry_line = NULL; tp->comment3 = NULL; tp->num_constraint_elements = 0; tp->constraint_elements = NULL; tp->comment4 = NULL; tp->constraint_lines_len = 0; tp->constraint_lines = NULL; tp->comment5 = NULL; tp->action_lines_len = 0; tp->action_lines = NULL; tp->comment6 = NULL; } static void textpattern_ns_edge_print(FILE *outfile, Textpattern *tp) { int i; if (tp->edge_constraints & WEST_EDGE) fputc('+', outfile); for (i = 0; i < tp->width; ++i) fputc('-', outfile); if (tp->edge_constraints & EAST_EDGE) fputc('+', outfile); fputc('\n', outfile); } void textpattern_print(FILE *outfile, Textpattern *tp) { int i, j; /* Print the pattern line */ fprintf(outfile, "Pattern %s\n", tp->name ? tp->name : ""); /* Print comment or similar. */ if (tp->comment) fprintf(outfile, tp->comment); /* Print the pattern diagram */ if (tp->edge_constraints & NORTH_EDGE) textpattern_ns_edge_print(outfile, tp); for (i = 0; i < tp->height; ++i) { if (tp->edge_constraints & WEST_EDGE) fputc('|', outfile); for (j = 0; j < tp->width; ++j) fputc(tp->elements[i*tp->width+j], outfile); if (tp->edge_constraints & EAST_EDGE) fputc('|', outfile); if (i == 0 && tp->explanation) fprintf(outfile, "%s", tp->explanation); fputc('\n', outfile); } if (tp->edge_constraints & SOUTH_EDGE) textpattern_ns_edge_print(outfile, tp); if (tp->comment2) fprintf(outfile, tp->comment2); fprintf(outfile, "%s", tp->entry_line); if (tp->comment3) fprintf(outfile, tp->comment3); /* Print the constraint diagram */ if (tp->num_constraint_elements > 0) { if (tp->edge_constraints & NORTH_EDGE) textpattern_ns_edge_print(outfile, tp); for (i = 0; i < tp->height; ++i) { if (tp->edge_constraints & WEST_EDGE) fputc('|', outfile); for (j = 0; j < tp->width; ++j) fputc(tp->constraint_elements[i*tp->width+j], outfile); if (tp->edge_constraints & EAST_EDGE) fputc('|', outfile); fputc('\n', outfile); } if (tp->edge_constraints & SOUTH_EDGE) textpattern_ns_edge_print(outfile, tp); if (tp->comment4) fprintf(outfile, tp->comment4); } if (tp->constraint_lines_len > 0) fprintf(outfile, "%s", tp->constraint_lines); if (tp->comment5) fprintf(outfile, tp->comment5); if (tp->action_lines_len > 0) fprintf(outfile, "%s", tp->action_lines); if (tp->comment6) fprintf(outfile, tp->comment6); } /* ================================================================ */ /* Parsing of textpatterns */ /* ================================================================ */ #define MAXTOKEN 20 #define MAXBUFFER 50000 char *infilename; int current_line = 1; FILE *infile; int next_char; int inttoken; char strbuffer[MAXBUFFER]; char whitespacebuffer[MAXBUFFER]; int strbuffer_index; static void parse_error(const char *format, ...); static void parse_warning(const char *format, ...); static int get_next_char(void); static void parse_away_whitespace(void); static int parse_identifier(void); static int parse_to_end_of_line(int cont); static int parse_pattern_line(Textpattern *tp); static int parse_pattern_diagram(Textpattern *tp); static int parse_entry_line(Textpattern *tp); static int parse_constraint_diagram(Textpattern *tp); static int parse_constraint_lines(Textpattern *tp); static int parse_action_lines(Textpattern *tp); void parse_init(char *filename, FILE *file) { infilename = strdup(filename); infile = file; get_next_char(); } static void parse_error(const char *format, ...) { va_list ap; va_start(ap, format); fprintf(stderr, "%s:%d: error: ", infilename, current_line); vfprintf(stderr, format, ap); fputc('\n', stderr); va_end(ap); } static void parse_warning(const char *format, ...) { va_list ap; va_start(ap, format); fprintf(stderr, "%s:%d: warning: ", infilename, current_line); vfprintf(stderr, format, ap); fputc('\n', stderr); va_end(ap); } static int get_next_char() { next_char = getc(infile); if (next_char == '\n') current_line++; return next_char; } int parse_whitespace(int to_end_of_line_only) { char *p = whitespacebuffer; int is_comment; if (!isspace(next_char) && next_char != '#') return 0; is_comment = 0; while ((isspace(next_char) || is_comment || next_char == '#') && next_char != EOF) { /* Switch on of off comments */ if (is_comment) { if (next_char == '\n') is_comment = 0; } else if (next_char == '#') is_comment = 1; if (to_end_of_line_only && next_char == '\n') { get_next_char(); break; } if (p-whitespacebuffer < MAXBUFFER-1) *p++ = next_char; else parse_warning("Whitespace too big (truncated)"); get_next_char(); } *p++ = '\0'; if (p == whitespacebuffer) return 0; return 1; } char * get_whitespace(void) { return whitespacebuffer; } static void parse_away_whitespace(void) { while (isspace(next_char) && next_char != '\n') get_next_char(); } #if 0 static int parse_int(void) { int val = 0; if (!isdigit(next_char)) return 0; val = next_char - '0'; while (isdigit(get_next_char())) val = val * 10 + next_char; inttoken = val; return 1; } #endif static int parse_identifier() { if (!isalpha(next_char) && next_char != '_') return 0; strbuffer_index = 0; do { strbuffer[strbuffer_index++] = next_char; get_next_char(); } while (strbuffer_index < MAXTOKEN && (isalpha(next_char) || isdigit(next_char) || next_char == '_')); strbuffer[strbuffer_index] = '\0'; if (strbuffer_index == MAXTOKEN) { parse_error("Too long identifier: %s", strbuffer); return 0; } return 1; } static int parse_to_end_of_line(int cont) { if (!cont) strbuffer_index = 0; do { strbuffer[strbuffer_index++] = next_char; get_next_char(); } while (strbuffer_index < MAXBUFFER-1 && next_char != '\n'); strbuffer[strbuffer_index++] = '\n'; strbuffer[strbuffer_index] = '\0'; if (strbuffer_index == MAXBUFFER) { parse_error("Too long line"); return 0; } else get_next_char(); return 1; } /* ---------------------------------------------------------------- */ static int parse_pattern_line(Textpattern *tp) { if (!parse_identifier()) return 0; if (strcmp(strbuffer, "Pattern")) { parse_error("Pattern expected"); return 0; } parse_away_whitespace(); if (!parse_identifier()) return 0; tp->name = strdup(strbuffer); parse_whitespace(1); return 1; } static int parse_north_south_constraint(Textpattern *tp) { int width; width = 0; while (next_char == '-') { width++; get_next_char(); } if (width == 0) { parse_error("Error in north/south constraint"); return 0; } if (tp->width > 0) { /* This has to be the south border */ if (width != tp->width || (next_char == '+' && (tp->edge_constraints & EAST_EDGE) == 0)) { parse_error("Error in south constraint"); return 0; } } else { /* North constraint -> set width. */ tp->width = width; if (next_char == '+') tp->edge_constraints |= EAST_EDGE; } /* Finish the line */ if (next_char == '+') get_next_char(); parse_whitespace(1); return 1; } static int parse_pattern_diagram(Textpattern *tp) { int width; /* See if there is a north border constraint. */ if (next_char == '+' || next_char == '-') { tp->edge_constraints |= NORTH_EDGE; if (next_char == '+') { tp->edge_constraints |= WEST_EDGE; get_next_char(); } if (parse_north_south_constraint(tp) == 0) return 0; } /* Read each line */ strbuffer_index = 0; while (next_char == '|' || strchr(VALID_PATTERN_CHARS, next_char)) { /* Handle WEST_EDGE constraint. */ if (next_char == '|') { tp->edge_constraints |= WEST_EDGE; get_next_char(); } else { if (tp->edge_constraints & WEST_EDGE) { parse_error("Missing west constraint"); return 0; } } /* Handle the pattern itself. */ width = 0; while (strchr(VALID_PATTERN_CHARS, next_char)) { strbuffer[strbuffer_index++] = next_char; width++; get_next_char(); } /* In case this happens to be the first row */ if (tp->height == 0) { tp->width = width; if (next_char == '|') tp->edge_constraints |= EAST_EDGE; } if (tp->width != width) { parse_error("Error in pattern diagram"); return 0; } tp->height++; /* Handle EAST_EDGE constraint. */ if (next_char == '|') { if (tp->edge_constraints & EAST_EDGE) get_next_char(); else { parse_error("Unexpected east constraint"); return 0; } } else { if (tp->edge_constraints & EAST_EDGE) { parse_error("Missing east constraint"); return 0; } } /* This strange sequence is because we don't want to destroy strbuffer. */ { /* FIXME: Only one row of explanation possible */ /* FIXME: Check boundaries */ char buf[1024]; char *p = buf; int only_spaces = 1; while (next_char != EOF && next_char != '\n') { *p++ = next_char; if (!isspace(next_char)) only_spaces = 0; get_next_char(); } if (!only_spaces && tp->explanation == NULL) { *p = '\0'; tp->explanation = strdup(buf); } get_next_char(); } } /* Store the pattern into the textpattern struct. */ tp->num_elements = strbuffer_index; strbuffer[strbuffer_index] = '\0'; tp->elements = strdup(strbuffer); /* Check a possible south constraint. */ if (tp->edge_constraints & WEST_EDGE) { if (next_char == '+') { tp->edge_constraints |= SOUTH_EDGE; get_next_char(); } } else { if (next_char == '+') { parse_error("Didn't expect '+' here"); return 0; } else if (next_char == '-') tp->edge_constraints |= SOUTH_EDGE; } if (tp->edge_constraints & SOUTH_EDGE) { if (parse_north_south_constraint(tp) == 0) return 0; } if (next_char == '+' || next_char == '-') { tp->edge_constraints |= SOUTH_EDGE; if (next_char == '+') { tp->edge_constraints |= WEST_EDGE; get_next_char(); } if (parse_north_south_constraint(tp) == 0) return 0; } return 1; } static int parse_entry_line(Textpattern *tp) { if (next_char != ':') return 0; /* FIXME: No syntax control is done on the entry line. */ parse_to_end_of_line(0); tp->entry_line = strdup(strbuffer); return 1; } static int parse_constraint_diagram(Textpattern *tp) { int width; /* Constraint diagram is optional. */ if (!strchr(VALID_PATTERN_CHARS, next_char) && !strchr(VALID_EDGE_CHARS, next_char) && !strchr(VALID_CONSTRAINT_LABELS, next_char)) return 1; /* See if there is a north border constraint. */ if (next_char == '+') { if ((tp->edge_constraints & NORTH_EDGE) == 0 || (tp->edge_constraints & WEST_EDGE) == 0) { parse_error("Edge error in constraint diagram"); return 0; get_next_char(); } } if (next_char == '-') { if ((tp->edge_constraints & WEST_EDGE) == 1) { parse_error("Edge error in constraint diagram"); return 0; } if (parse_north_south_constraint(tp) == 0) return 0; } /* Read each line */ strbuffer_index = 0; while (next_char == '|' || strchr(VALID_PATTERN_CHARS, next_char) || strchr(VALID_CONSTRAINT_LABELS, next_char)) { /* Handle WEST_EDGE constraint. */ if (next_char == '|') { if ((tp->edge_constraints & WEST_EDGE) == 0) { parse_error("Edge error in constraint diagram"); return 0; } get_next_char(); } else { if (tp->edge_constraints & WEST_EDGE) { parse_error("Missing west constraint"); return 0; } } /* Handle the pattern itself. */ width = 0; while (strchr(VALID_PATTERN_CHARS, next_char) || strchr(VALID_CONSTRAINT_LABELS, next_char)) { strbuffer[strbuffer_index++] = next_char; width++; get_next_char(); } if (tp->width != width) { parse_error("Error in constraint diagram"); return 0; } /* parse_whitespace() destroys strbuffer. */ parse_away_whitespace(); if (next_char != '\n') { while (next_char != EOF && next_char != '\n') get_next_char(); } get_next_char(); } /* Store the constraint diagram into the textpattern struct. */ tp->num_constraint_elements = strbuffer_index; strbuffer[strbuffer_index] = '\0'; tp->constraint_elements = strdup(strbuffer); /* Check a possible south constraint. */ if (tp->edge_constraints & WEST_EDGE) { if (next_char == '+') { tp->edge_constraints |= SOUTH_EDGE; get_next_char(); } } else { if (next_char == '+') { parse_error("Didn't expect '+' here"); return 0; } else if (next_char == '-') tp->edge_constraints |= SOUTH_EDGE; } if (tp->edge_constraints & SOUTH_EDGE) { if (parse_north_south_constraint(tp) == 0) return 0; } else { if (next_char == '+' || next_char == '-') { if (next_char == '+') get_next_char(); if (parse_north_south_constraint(tp) == 0) return 0; } } return 1; } static int parse_constraint_lines(Textpattern *tp) { if (next_char != ';') { if (tp->num_constraint_elements != 0) parse_warning("constraint diagram but no constraint"); /* Constraint lines are optional */ return 1; } /* FIXME: No syntax control is done on the constraint lines. */ parse_to_end_of_line(0); while (next_char == ';') parse_to_end_of_line(1); tp->constraint_lines_len = strbuffer_index; tp->constraint_lines = strdup(strbuffer); return 1; } static int parse_action_lines(Textpattern *tp) { if (next_char != '>') /* Action lines are optional */ return 1; /* FIXME: No syntax control is done on the action lines. */ parse_to_end_of_line(0); while (next_char == '>') parse_to_end_of_line(1); tp->action_lines_len = strbuffer_index; tp->action_lines = strdup(strbuffer); return 1; } /* ---------------------------------------------------------------- */ /* Old variables and defines. */ #define MAXLINE 500 /* Max line length */ static int pattern_type = NORMAL_PATTERNS; static int fullboard = 0; static int anchor_both = 0; static int verbose = 0; int textpattern_parse(FILE *parm_infile, int parm_pattern_type, int parm_anchor_both, int parm_fullboard, int parm_verbose, Textpattern *tp) { infile = parm_infile; pattern_type = parm_pattern_type; anchor_both = parm_anchor_both; fullboard = parm_fullboard; verbose = parm_verbose; /* Initialize pattern number and buffer for automatically generated * helper code. */ assert(tp != NULL); textpattern_clear(tp); /* Get to the pattern itself */ parse_whitespace(0); /* The line: Pattern XYZ */ if (!parse_pattern_line(tp)) return 0; if (parse_whitespace(0)) tp->comment = strdup(whitespacebuffer); /* The pattern diagram. */ if (!parse_pattern_diagram(tp)) return 0; if (parse_whitespace(0)) tp->comment2 = strdup(whitespacebuffer); /* The entry line. */ if (!parse_entry_line(tp)) return 0; if (parse_whitespace(0)) tp->comment3 = strdup(whitespacebuffer); if (!parse_constraint_diagram(tp)) return 0; if (parse_whitespace(0)) tp->comment4 = strdup(whitespacebuffer); if (!parse_constraint_lines(tp)) return 0; if (parse_whitespace(0)) tp->comment5 = strdup(whitespacebuffer); if (!parse_action_lines(tp)) return 0; if (parse_whitespace(0)) tp->comment6 = strdup(whitespacebuffer); return 1; } void textpattern_transform(Textpattern *tp, int transform) { int size, size2; char *array1; char *array2; char *p1, *p2; int mini, minj; int maxi, maxj; int r; int newedge; int i = 0, j = 0; int i1, j1; assert(0 <= transform && transform < 8); /* Let size be twice the (maximum of width and height) plus 1. */ size = tp->width; if (tp->height > size) size = tp->height; size++; size2 = size*2; array1 = malloc(size2 * size2); array2 = malloc(size2 * size2); assert(array1 != NULL && array2 != NULL); newedge = 0; for (r = 0; r < 4; ++r) { switch (tp->edge_constraints & (1<edge_constraints = newedge; mini = 9999; maxi = -9999; minj = 9999; maxj = -9999; for (r = 0; r < tp->num_elements; ++r) { i = r / tp->width; j = r % tp->width; TRANSFORM2(i, j, &i1, &j1, transform); array1[(i1+size)*size2 + j1+size] = tp->elements[r]; if (tp->num_constraint_elements > 0) array2[(i1+size)*size2 + j1+size] = tp->constraint_elements[r]; if (i1 < mini) mini = i1; if (i1 > maxi) maxi = i1; if (j1 < minj) minj = j1; if (j1 > maxj) maxj = j1; } tp->height = maxi - mini + 1; tp->width = maxj - minj + 1; p1 = tp->elements; p2 = tp->constraint_elements; for (i1 = mini; i1 <= maxi; ++i1) for (j1 = minj; j1 <= maxj; ++j1) { *p1++ = array1[(i1 + size) * size2 + size + j1]; if (tp->num_constraint_elements > 0) *p2++ = array2[(i1 + size) * size2 + size + j1]; } #if 0 /* Debug */ for (i1 = mini; i1 <= maxi; ++i1) { for (j1 = minj; j1 <= maxj; ++j1) putchar(array[(i1 + size) * size * 2 + size + j1]); putchar('\n'); } #endif free(array1); free(array2); } /* * Local Variables: * tab-width: 8 * c-basic-offset: 2 * End: */