/* * Get the next line. * A "current" position is passed and a "new" position is returned. * The current position is the position of the first character of * a line. The new position is the position of the first character * of the NEXT line. The line obtained is the line starting at curr_pos. */ off_t forw_line(off_t curr_pos) { off_t new_pos; int c; if (curr_pos == NULL_POSITION || ch_seek(curr_pos)) return (NULL_POSITION); c = ch_forw_get(); if (c == EOI) return (NULL_POSITION); prewind(); for (;;) { if (sigs) return (NULL_POSITION); if (c == '\n' || c == EOI) { /* * End of the line. */ new_pos = ch_tell(); break; } /* * Append the char to the line and get the next char. */ if (pappend(c)) { /* * The char won't fit in the line; the line * is too long to print in the screen width. * End the line here. */ new_pos = ch_tell() - 1; break; } c = ch_forw_get(); } (void) pappend('\0'); if (squeeze && *line == '\0') { /* * This line is blank. * Skip down to the last contiguous blank line * and pretend it is the one which we are returning. */ while ((c = ch_forw_get()) == '\n') if (sigs) return (NULL_POSITION); if (c != EOI) (void) ch_back_get(); new_pos = ch_tell(); } return (new_pos); }
/* * Get the previous line. * A "current" position is passed and a "new" position is returned. * The current position is the position of the first character of * a line. The new position is the position of the first character * of the PREVIOUS line. The line obtained is the one starting at new_pos. */ off_t back_line(off_t curr_pos) { off_t new_pos, begin_new_pos; int c; if (curr_pos == NULL_POSITION || curr_pos <= (off_t)0 || ch_seek(curr_pos-1)) return (NULL_POSITION); if (squeeze) { /* * Find out if the "current" line was blank. */ (void) ch_forw_get(); /* Skip the newline */ c = ch_forw_get(); /* First char of "current" line */ (void) ch_back_get(); /* Restore our position */ (void) ch_back_get(); if (c == '\n') { /* * The "current" line was blank. * Skip over any preceeding blank lines, * since we skipped them in forw_line(). */ while ((c = ch_back_get()) == '\n') if (sigs) return (NULL_POSITION); if (c == EOI) return (NULL_POSITION); (void) ch_forw_get(); } } /* * Scan backwards until we hit the beginning of the line. */ for (;;) { if (sigs) return (NULL_POSITION); c = ch_back_get(); if (c == '\n') { /* * This is the newline ending the previous line. * We have hit the beginning of the line. */ new_pos = ch_tell() + 1; break; } if (c == EOI) { /* * We have hit the beginning of the file. * This must be the first line in the file. * This must, of course, be the beginning of the line. */ new_pos = ch_tell(); break; } } /* * Now scan forwards from the beginning of this line. * We keep discarding "printable lines" (based on screen width) * until we reach the curr_pos. * * {{ This algorithm is pretty inefficient if the lines * are much longer than the screen width, * but I don't know of any better way. }} */ if (ch_seek(new_pos)) return (NULL_POSITION); loop: begin_new_pos = new_pos; prewind(); do { c = ch_forw_get(); if (c == EOI || sigs) return (NULL_POSITION); new_pos++; if (c == '\n') break; if (pappend(c)) { /* * Got a full printable line, but we haven't * reached our curr_pos yet. Discard the line * and start a new one. */ (void) pappend('\0'); (void) ch_back_get(); new_pos--; goto loop; } } while (new_pos < curr_pos); (void) pappend('\0'); return (begin_new_pos); }
swexp_list_node *parse_block(parser *p) { // parses a block of lines with the same indentation into a chain // (not a list) char c; swexp_list_node fakehead, *tail; fakehead.next = NULL; fakehead.content = NULL; fakehead.type = UNDEFINED; tail = &fakehead; // get initial indentation by consuming characters until we find some unsigned int current_indentation; for (current_indentation = p->indentation; is_space(pgetc(p)); current_indentation++) { } prewind(p, '\0'); while ((c = pgetc(p)) != EOF) { // if we encounter a comment in any state, strip it out IGNORE_COMMENTS() switch (p->state) { case COUNTING_INDENTATION: if (is_space(c)) { p->indentation++; } else if (is_newline(c)) { p->indentation = 0; } else { // this is a start of an atom. // parse as appropriate based on indent prewind(p, c); if (p->indentation > current_indentation) { if (tail->type == ATOM) { // make the tail a list before appending swexp_list_node *tailcont = malloc(sizeof(swexp_list_node)); tailcont->type = ATOM; tailcont->next = NULL; tailcont->content = tail->content; tailcont->location = NULL; tail->type = LIST; tail->content = tailcont; if (tail->location != NULL) { free(tail->location); tail->location = NULL; } } // append it to the list that is the last // element list_tail(tail)->next = parse_block(p); } else if (p->indentation == current_indentation) { tail->next = parse_line(p); p->state = COUNTING_INDENTATION; tail = tail->next; } else { goto clean_and_return; } } break; default: printf("unexpected state in parse_line\n"); exit(1); } } clean_and_return: return fakehead.next; }
swexp_list_node *parse_s_expr(parser *p, char opening_brace) { char c; swexp_list_node fakehead, *tail = &fakehead; fakehead.next = NULL; fakehead.content = NULL; fakehead.type = UNDEFINED; char closing_brace = brace_pair(opening_brace); while ((c = pgetc(p)) != EOF && !is_closing_brace(c)) { // if we encounter a comment in any state, strip it out IGNORE_COMMENTS() switch (p->state) { case SKIP_SPACE: if (is_space(c) || is_newline(c)) { // do nothing if it is space or newline } else if (is_opening_brace(c)) { // parse the parenthesized s expression into a list // and append it to the thing swexp_list_node *list = malloc(sizeof(swexp_list_node)); list->type = LIST; list->next = NULL; list->location = NULL; list->content = parse_s_expr(p, c); tail->next = list; tail = list; } else { // step back and start collecting the atom prewind(p, c); begin_atom(p); } break; case COLLECTING_ATOM: if (is_space(c) || is_newline(c)) { prewind(p, c); tail->next = close_atom(p); tail = tail->next; } else if (is_opening_brace(c)) { swexp_list_node *node = close_atom(p); node->next = parse_s_expr(p, c); tail->next = listof(node); tail = chain_tail(tail); } else { build_atom(p, c); } break; default: printf("unexpected state %d in parse_s_expr", p->state); exit(1); } } if (is_closing_brace(c) && c != closing_brace) { printf("mismatched braces in s expression\n"); exit(1); } if (p->state == COLLECTING_ATOM) { tail->next = close_atom(p); } if (c == EOF) { printf("unexpected EOF while parsing s expression\n"); exit(1); } p->state = SKIP_SPACE; return fakehead.next; }
swexp_list_node *parse_line(parser *p) { // parses a line of text, starting at a non-whitespace char char c; // build a list of expressions started by this // list head on the stack. swexp_list_node head, *tail; head.next = NULL; head.content = NULL; head.type = UNDEFINED; tail = &head; p->state = SKIP_SPACE; while ((c = pgetc(p)) != EOF && !is_newline(c) && !is_closing_brace(c)) { // if we encounter a comment in any state, strip it out IGNORE_COMMENTS() switch (p->state) { case COLLECTING_ATOM: if (is_space(c)) { // end atom tail->next = close_atom(p); tail = tail->next; prewind(p, c); } else if (is_opening_brace(c)) { swexp_list_node *bracehead = close_atom(p); swexp_list_node *bracecontent = parse_s_expr(p, c); bracehead->next = bracecontent; tail->next = listof(bracehead); tail = chain_tail(tail); } else { // continue to build item build_atom(p, c); } break; case SKIP_SPACE: if (is_opening_brace(c)) { swexp_list_node *brace = parse_s_expr(p, c); tail->next = brace; tail = chain_tail(tail); } else if (!is_space(c)) { begin_atom(p); prewind(p, c); } break; default: printf("unexpected state %d in parse_line\n", p->state); exit(1); } } if (is_newline(c)) { p->indentation = 0; } if (is_closing_brace(c)) { printf("encountered unmatched closing brace\n"); exit(1); } // close ongoing capture if (p->state == COLLECTING_ATOM) { tail->next = close_atom(p); } // if the number of collected atoms is more than one, // make it a list and return it if (chain_len(head.next) > 1) { swexp_list_node *listhead = malloc(sizeof(swexp_list_node)); listhead->type = LIST; listhead->next = NULL; listhead->content = head.next; listhead->location = NULL; return listhead; } else { return head.next; } }