LCOV - code coverage report
Current view: top level - src/regex - instr.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 6 244 2.5 %
Date: 2016-12-21 02:12:01 Functions: 1 11 9.1 %

          Line data    Source code
       1             : /***************************************************************************//**
       2             : 
       3             :   @file         instr.c
       4             : 
       5             :   @author       Stephen Brennan
       6             : 
       7             :   @date         Created Thursday, 21 January 2016
       8             : 
       9             :   @brief        Functions for reading and writing virtual machine instructions.
      10             : 
      11             :   @copyright    Copyright (c) 2016, Stephen Brennan.  Released under the Revised
      12             :                 BSD License.  See LICENSE.txt for details.
      13             : 
      14             : *******************************************************************************/
      15             : 
      16             : #include <stdlib.h>
      17             : #include <stdint.h>
      18             : #include <string.h>
      19             : #include <ctype.h>
      20             : #include <stdbool.h>
      21             : #include <unistd.h>
      22             : 
      23             : #include "libstephen/re.h"
      24             : #include "libstephen/re_internals.h"
      25             : 
      26             : #define COMMENT ';'
      27             : 
      28             : /*
      29             :   Declarations for parsing.
      30             :  */
      31             : 
      32             : enum linetype {
      33             :   Blank, Label, Code
      34             : };
      35             : typedef enum linetype linetype;
      36             : 
      37             : char *Opcodes[] = {
      38             :   "char", "match", "jump", "split", "save", "any", "range", "nrange"
      39             : };
      40             : 
      41             : /*
      42             :   Utilities for input/output
      43             :  */
      44           0 : char *char_to_string(char c)
      45             : {
      46             :   #define CTS_BUFSIZE 5
      47             :   static char buffer[CTS_BUFSIZE];
      48           0 :   if (c == ' ') {
      49           0 :     buffer[0] = '\\';
      50           0 :     buffer[1] = 'x';
      51           0 :     buffer[2] = '2';
      52           0 :     buffer[3] = '0';
      53           0 :     buffer[4] = '\0'; // space = 0x20
      54           0 :   } else if (isspace(c)) {
      55           0 :     switch (c) {
      56             :     case '\n':
      57           0 :       buffer[1] = 'n';
      58           0 :       break;
      59             :     case '\f':
      60           0 :       buffer[1] = 'n';
      61           0 :       break;
      62             :     case '\r':
      63           0 :       buffer[1]= 'r';
      64           0 :       break;
      65             :     case '\t':
      66           0 :       buffer[1] = 't';
      67           0 :       break;
      68             :     case '\v':
      69           0 :       buffer[1] = 'v';
      70           0 :       break;
      71             :     }
      72           0 :     buffer[0] = '\\';
      73           0 :     buffer[2] = '\0';
      74           0 :   } else if (c == '\0') {
      75           0 :     buffer[0] = '\0'; // empty string...
      76             :   } else {
      77           0 :     buffer[0] = c;
      78           0 :     buffer[1] = '\0';
      79             :   }
      80           0 :   return buffer;
      81             : }
      82             : 
      83           0 : void fprintc(FILE *f, char c)
      84             : {
      85           0 :   fprintf(f, "%s", char_to_string(c));
      86           0 : }
      87             : 
      88           0 : char string_to_char(char *s) {
      89             :   unsigned int c;
      90           0 :   if (s[0] == '\\') {
      91           0 :     switch (s[1]) {
      92             :     case 'n':
      93           0 :       return '\n';
      94             :     case 'f':
      95           0 :       return '\f';
      96             :     case 'r':
      97           0 :       return '\r';
      98             :     case 't':
      99           0 :       return '\t';
     100             :     case 'v':
     101           0 :       return '\v';
     102             :     case 'x':
     103           0 :       sscanf(s + 2, "%x", &c);
     104           0 :       return (char)c;
     105             :     default:
     106           0 :       return s[1];
     107             :     }
     108             :   } else {
     109           0 :     return s[0];
     110             :   }
     111             : }
     112             : 
     113             : 
     114             : /**
     115             :    @brief Remove leading and trailing whitespace and comments from a line.
     116             :  */
     117           0 : static char *trim(char *line, char *lastchar_out)
     118             : {
     119           0 :   ssize_t firstnonwhite = -1;
     120           0 :   ssize_t lastnonwhite = -1;
     121             :   size_t i;
     122             : 
     123           0 :   for (i = 0; line[i]; i++) {
     124           0 :     if (isspace(line[i])) {
     125           0 :       continue;
     126             :     }
     127             : 
     128           0 :     if (line[i] == COMMENT) {
     129           0 :       line[i] = '\0';
     130           0 :       break;
     131             :     }
     132             : 
     133           0 :     if (firstnonwhite == -1) {
     134           0 :       firstnonwhite = i;
     135             :     }
     136           0 :     lastnonwhite = i;
     137             :   }
     138             : 
     139           0 :   if (firstnonwhite == -1) { // all whitespace
     140           0 :     *lastchar_out = '\0';
     141           0 :     return line + i;
     142             :   } else {
     143           0 :     line[lastnonwhite + 1] = '\0';
     144           0 :     *lastchar_out = line[lastnonwhite];
     145           0 :     return line + firstnonwhite;
     146             :   }
     147             : }
     148             : 
     149           0 : static char **tokenize(char *line, size_t *ntok)
     150             : {
     151             :   #define SEP " \n\t\v\f"
     152           0 :   size_t alloc = 16;
     153           0 :   char **buf = calloc(alloc, sizeof(char*));
     154             : 
     155           0 :   buf[0] = strtok(line, SEP);
     156           0 :   *ntok = 0;
     157           0 :   while (buf[*ntok] != NULL) {
     158           0 :     *ntok += 1;
     159             : 
     160           0 :     if (*ntok >= alloc) {
     161           0 :       alloc *= 2;
     162           0 :       buf = realloc(buf, alloc * sizeof(char*));
     163             :     }
     164             : 
     165           0 :     buf[*ntok] = strtok(NULL, SEP);
     166             :   }
     167           0 :   return buf;
     168             : }
     169             : 
     170             : /**
     171             :    @brief Parse and return an instruction from a line.
     172             : 
     173             :    Input line should have no preceding or trailing whitespace (see trim()).
     174             :    This function will not return on error - it will just fprintf() the error
     175             :    message and exit with an error code.  This will change eventually.
     176             :    @param line Text to parse.
     177             :    @param lineno Line number (just used for error msg).
     178             :  */
     179           0 : static Instr read_instr(char *line, int lineno)
     180             : {
     181             :   // First, we're going to tokenize the string into a statically allocated
     182             :   // buffer.  We know we don't need more than like TODO
     183             :   size_t ntok;
     184           0 :   char **tokens = tokenize(line, &ntok);
     185           0 :   Instr inst = {.code=0, .c=0, .s=0, .x=NULL, .y=NULL, .lastidx=0};
     186             : 
     187           0 :   if (strcmp(tokens[0], Opcodes[Char]) == 0) {
     188           0 :     if (ntok != 2) {
     189           0 :       fprintf(stderr, "line %d: require 2 tokens for char\n", lineno);
     190           0 :       exit(1);
     191             :     }
     192           0 :     inst.code = Char;
     193           0 :     inst.c = tokens[1][0];
     194           0 :   } else if (strcmp(tokens[0], Opcodes[Match]) == 0) {
     195           0 :     if (ntok != 1) {
     196           0 :       fprintf(stderr, "line %d: require 1 token for match\n", lineno);
     197           0 :       exit(1);
     198             :     }
     199           0 :     inst.code = Match;
     200           0 :   } else if (strcmp(tokens[0], Opcodes[Jump]) == 0) {
     201           0 :     if (ntok != 2) {
     202           0 :       fprintf(stderr, "line %d: require 2 tokens for jump\n", lineno);
     203           0 :       exit(1);
     204             :     }
     205           0 :     inst.code = Jump;
     206           0 :     inst.x = (Instr*)tokens[1];
     207           0 :   } else if (strcmp(tokens[0], Opcodes[Split]) == 0) {
     208           0 :     if (ntok != 3) {
     209           0 :       fprintf(stderr, "line %d: require 3 tokens for split\n", lineno);
     210           0 :       exit(1);
     211             :     }
     212           0 :     inst.code = Split;
     213           0 :     inst.x = (Instr*)tokens[1];
     214           0 :     inst.y = (Instr*)tokens[2];
     215           0 :   } else if (strcmp(tokens[0], Opcodes[Save]) == 0) {
     216           0 :     if (ntok != 2) {
     217           0 :       fprintf(stderr, "line %d: require 2 tokens for save\n", lineno);
     218           0 :       exit(1);
     219             :     }
     220           0 :     inst.code = Save;
     221           0 :     sscanf(tokens[1], "%zu", &inst.s);
     222           0 :   } else if (strcmp(tokens[0], Opcodes[Any]) == 0) {
     223           0 :     if (ntok != 1) {
     224           0 :       fprintf(stderr, "line %d: require 1 token for any\n", lineno);
     225           0 :       exit(1);
     226             :     }
     227           0 :     inst.code = Any;
     228           0 :   } else if (strcmp(tokens[0], Opcodes[ Range]) == 0 ||
     229           0 :              strcmp(tokens[0], Opcodes[NRange]) == 0) {
     230           0 :     if (ntok % 2 == 0) {
     231           0 :       fprintf(stderr, "line %d, require even number of character tokens\n",
     232             :               lineno);
     233           0 :       exit(1);
     234             :     }
     235           0 :     inst.code = (strcmp(tokens[0], Opcodes[Range]) == 0) ? Range : NRange;
     236           0 :     inst.s = (size_t) (ntok - 1) / 2;
     237           0 :     char *block = calloc(ntok - 1, sizeof(char));
     238           0 :     inst.x = (Instr*)block;
     239           0 :     for (size_t i = 0; i < ntok - 1; i++) {
     240           0 :       block[i] = string_to_char(tokens[i+1]);
     241             :     }
     242             :   } else {
     243           0 :     fprintf(stderr, "line %d: unknown opcode \"%s\"\n", lineno, tokens[0]);
     244             :   }
     245           0 :   return inst;
     246             : }
     247             : 
     248             : /**
     249             :    @brief Return the instruction index corresponding to a label.
     250             :    @param labels Array of labels
     251             :    @param lablindices Corresponding indices for the labels
     252             :    @param nlabels Number of label/index pairs in the arrays
     253             :    @param label Label to search for
     254             :    @param line Line number (for error messages)
     255             :  */
     256           0 : static size_t gettarget(char **labels, size_t *labelindices, size_t nlabels,
     257             :                         char *label, size_t line)
     258             : {
     259           0 :   for (size_t i = 0; i < nlabels; i++) {
     260           0 :     if (strcmp(labels[i], label) == 0) {
     261           0 :       return labelindices[i];
     262             :     }
     263             :   }
     264           0 :   fprintf(stderr, "line %zu: label \"%s\" not found\n", line, label);
     265           0 :   exit(1);
     266             : }
     267             : 
     268             : /**
     269             :    @brief Return a block of instructions from some text code.
     270             :    @param str Code
     271             :    @param[out] ninstr Where to put the number of instructions
     272             :  */
     273           0 : Regex reread(char *str)
     274             : {
     275           0 :   size_t nlines = 1;
     276           0 :   Instr *rv = NULL;
     277             : 
     278             :   // Count newlines.
     279           0 :   for (size_t i = 0; str[i]; i++) {
     280           0 :     if (str[i] == '\n') {
     281           0 :       nlines++;
     282             :     }
     283             :   }
     284             : 
     285             :   // Create an array of lines.
     286           0 :   char **lines = calloc(nlines, sizeof(char*));
     287           0 :   size_t j = 0;
     288           0 :   lines[j++] = str;
     289           0 :   for (size_t i = 0; str[i]; i++) {
     290           0 :     if (str[i] == '\n') {
     291           0 :       str[i] = '\0';
     292           0 :       lines[j++] = str + i + 1;
     293             :     }
     294             :   }
     295             : 
     296             :   // Trim whitespace of each line, and categorize the lines.
     297           0 :   size_t nlabels = 0;
     298           0 :   size_t ncode = 0;
     299           0 :   linetype *types = calloc(nlines, sizeof(linetype));
     300           0 :   for (size_t i = 0; i < nlines; i++) {
     301             :     char last;
     302           0 :     lines[i] = trim(lines[i], &last);
     303           0 :     if (last == '\0') {
     304           0 :       types[i] = Blank;
     305           0 :     } else if (last == ':') {
     306           0 :       types[i] = Label;
     307           0 :       nlabels++;
     308             :     } else {
     309           0 :       types[i] = Code;
     310           0 :       ncode++;
     311             :     }
     312             :   }
     313             : 
     314             :   // Associate each label with the next line of code.
     315           0 :   char **labels = calloc(nlabels, sizeof(char *));
     316           0 :   size_t *labelindices = calloc(nlabels, sizeof(size_t));
     317           0 :   ssize_t codeidx = -1;
     318           0 :   size_t labelidx = 0;
     319           0 :   for (size_t i = 0; i < nlines; i++) {
     320           0 :     if (types[i] == Label) {
     321           0 :       size_t len = strlen(lines[i]);
     322           0 :       lines[i][len-1] = '\0'; // remove the colon, we don't want it
     323           0 :       labels[labelidx] = lines[i];
     324           0 :       labelindices[labelidx] = codeidx + 1;
     325           0 :       labelidx++;
     326           0 :     } else if (types[i] == Code) {
     327           0 :       codeidx++;
     328             :     }
     329             :   }
     330             :   // we'll assume that the labels are valid for now
     331             : 
     332           0 :   rv = calloc(ncode, sizeof(Instr));
     333           0 :   codeidx = 0;
     334           0 :   for (size_t i = 0; i < nlines; i++) {
     335           0 :     if (types[i] != Code) {
     336           0 :       continue;
     337             :     }
     338             : 
     339           0 :     rv[codeidx] = read_instr(lines[i], i+1);
     340             : 
     341             :     // lookup labels and point them correctly
     342           0 :     if (rv[codeidx].code == Jump || rv[codeidx].code == Split) {
     343           0 :       rv[codeidx].x = rv + gettarget(labels, labelindices, nlabels, (char*)rv[codeidx].x, i+1);
     344             :     }
     345           0 :     if (rv[codeidx].code == Split) {
     346           0 :       rv[codeidx].y = rv + gettarget(labels, labelindices, nlabels, (char*)rv[codeidx].y, i+1);
     347             :     }
     348             : 
     349           0 :     codeidx++;
     350             :   }
     351             : 
     352           0 :   free(types);
     353           0 :   free(lines);
     354           0 :   free(labels);
     355           0 :   free(labelindices);
     356           0 :   return (Regex){.n=codeidx, .i=rv};
     357             : }
     358             : 
     359             : /**
     360             :    @brief Read a program from a file.
     361             :  */
     362           0 : Regex refread(FILE *f) {
     363           0 :   size_t alloc = 4096;
     364           0 :   char *buf = malloc(alloc);
     365           0 :   size_t start = 0;
     366             :   while (true) {
     367           0 :     start += fread(buf, 1, alloc - start, f);
     368           0 :     if (start == alloc) {
     369           0 :       alloc *= 2;
     370           0 :       buf = realloc(buf, alloc);
     371             :     } else {
     372           0 :       break;
     373             :     }
     374           0 :   }
     375             : 
     376           0 :   Regex rv = reread(buf);
     377           0 :   free(buf);
     378           0 :   return rv;
     379             : }
     380             : 
     381             : /**
     382             :    @brief Write a program to a file.
     383             :  */
     384           0 : void rewrite(Regex r, FILE *f)
     385             : {
     386           0 :   size_t *labels = calloc(r.n, sizeof(size_t));
     387             : 
     388             :   // Find every instruction that needs a label.
     389           0 :   for (size_t i = 0; i < r.n; i++) {
     390           0 :     if (r.i[i].code == Jump || r.i[i].code == Split) {
     391           0 :       labels[r.i[i].x - r.i] = 1;
     392             :     }
     393           0 :     if (r.i[i].code == Split) {
     394           0 :       labels[r.i[i].y - r.i] = 1;
     395             :     }
     396             :   }
     397             : 
     398           0 :   size_t l = 1;
     399           0 :   for (size_t i = 0; i < r.n; i++) {
     400           0 :     if (labels[i] > 0) {
     401           0 :       labels[i] = l++;
     402             :     }
     403             :   }
     404             : 
     405           0 :   for (size_t i = 0; i < r.n; i++) {
     406           0 :     if (labels[i] > 0) {
     407           0 :       fprintf(f, "L%zu:\n", labels[i]);
     408             :     }
     409           0 :     char *block = (char*) r.i[i].x;
     410           0 :     switch (r.i[i].code) {
     411             :     case Char:
     412           0 :       fprintf(f, "    char %s\n", char_to_string(r.i[i].c));
     413           0 :       break;
     414             :     case Match:
     415           0 :       fprintf(f, "    match\n");
     416           0 :       break;
     417             :     case Jump:
     418           0 :       fprintf(f, "    jump L%zu\n", labels[r.i[i].x - r.i]);
     419           0 :       break;
     420             :     case Split:
     421           0 :       fprintf(f, "    split L%zu L%zu\n", labels[r.i[i].x - r.i],
     422           0 :               labels[r.i[i].y - r.i]);
     423           0 :       break;
     424             :     case Save:
     425           0 :       fprintf(f, "    save %zu\n", r.i[i].s);
     426           0 :       break;
     427             :     case Any:
     428           0 :       fprintf(f, "    any\n");
     429           0 :       break;
     430             :     case NRange:
     431           0 :       fprintf(f, "    nrange");
     432           0 :       for (size_t j = 0; j < r.i[i].s; j++) {
     433           0 :         fprintf(f, " %s", char_to_string(block[2*j]));
     434           0 :         fprintf(f, " %s", char_to_string(block[2*j + 1]));
     435             :       }
     436           0 :       fprintf(f, "\n");
     437           0 :       break;
     438             :     case Range:
     439           0 :       fprintf(f, "    range");
     440           0 :       for (size_t j = 0; j < r.i[i].s; j++) {
     441           0 :         fprintf(f, " %s", char_to_string(block[2*j]));
     442           0 :         fprintf(f, " %s", char_to_string(block[2*j + 1]));
     443             :       }
     444           0 :       fprintf(f, "\n");
     445           0 :       break;
     446             :     }
     447             :   }
     448             : 
     449           0 :   free(labels);
     450           0 : }
     451             : 
     452          37 : void refree(Regex r)
     453             : {
     454         175 :   for (size_t i = 0; i < r.n; i++) {
     455         138 :     if (r.i[i].code == Range || r.i[i].code == NRange) {
     456          12 :       free(r.i[i].x);
     457             :     }
     458             :   }
     459          37 :   free(r.i);
     460          37 : }

Generated by: LCOV version 1.11