splint debugging parse error
Asked Answered
T

2

6

This is my first time using splint (from Ubuntu repositories) and I immediately got hit by a WTF. The error message:

nightcracker@nightcracker-pc:~/c/brainfuck$ splint brainfuck.c
Splint 3.1.2 --- 03 May 2009

brainfuck.c:17:6: Parse Error. (For help on parse errors, see splint -help
               parseerrors.)
*** Cannot continue.

Now, apparently it sees something wrong on line 16, column 6. Let's check that out (posting full code):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum {
    CELL_CHUNK_SIZE = 1024,
};

typedef unsigned char cell;

int main(int argc, char *argv[]) {
    if (argc < 1) {
        fprintf(stderr, "ERROR: Not enough arguments\n");
        return 1;
    }

    FILE  *srcfile; // source file << THIS LINE APPARENTLY IS WRONG
    long srclen; // source file size
    char *bf; // brainfuck code file in memory

    char *ip; // instruction pointer
    cell *cells; // brainfuck cells
    cell *newcells; // used for creating a new chunk of cells
    cell *cp; // cell pointer
    unsigned long numcells = CELL_CHUNK_SIZE; // amount of current cells
    unsigned nest; // current nesting
    int buf; // i/o buffer

    srcfile = fopen(argv[1], "rb");
    if (srcfile == NULL) {
        fprintf(stderr, "ERROR: Couldn't open source file\n");
        return 2;
    }

    // get source file length
    fseek(srcfile, 0, SEEK_END);
    srclen = ftell(srcfile);
    fseek(srcfile, 0, SEEK_SET);

    // allocate memory for source file
    bf = malloc(srclen);
    if (bf == NULL) {
        fprintf(stderr, "ERROR: Couldn't allocate memory for source file\n");
        return 3;
    }

    // read source file in memory
    if (srclen != fread(bf, sizeof(char), srclen, srcfile)) {
        fprintf(stderr, "ERROR: Error while reading source file\n");
        free(bf);
        return 4;
    }

    fclose(srcfile);

    cells = malloc(CELL_CHUNK_SIZE * sizeof(cell));
    memset(cells, 0, CELL_CHUNK_SIZE);

    if (cells == NULL) {
        fprintf(stderr, "ERROR: Memory allocation failed\n");
        free(bf);
        free(cells);
        return 5;
    }

    cp = cells; // cell pointer initialized to most-left cell
    ip = bf; // instruction pointer initialized to first character
    nest = 0;

    while (ip >= bf && ip <= (bf + srclen)) {
        switch (*ip) {
            case '+':
                (*cp)++;
                break;
            case '-':
                (*cp)--;
                break;
            case '>':
                cp++;
                if ((cp - cells) == numcells) {
                    newcells = realloc(cells, (numcells + CELL_CHUNK_SIZE) * sizeof(cell)); // allocate memory for new chunk

                    if (newcells == NULL) {
                        fprintf(stderr, "ERROR: Memory allocation failed\n");
                        free(bf);
                        free(cells);
                        return 5;
                    }

                    cp = newcells + (cp - cells); // point cell pointer to cell in new chunk
                    cells = newcells; // point cells to new memory location (if altered)
                    memset(cp, 0, CELL_CHUNK_SIZE); // initialize new chunk
                    numcells += CELL_CHUNK_SIZE;
                }
                break;
            case '<':
                cp--;
                break;
            case '.':
                putchar(*cp);
                break;
            case ',':
                if ((buf = getchar()) != EOF) {
                    *cp = (unsigned char) buf;
                } else *cp = 0;
                break;
            case '[':
                if (!(*cp)) {
                    ip++; // move past the opening bracket
                    while (nest > 0 || *ip != ']') { // skip to matching ]
                        if (*ip == '[') nest++; // enter nest
                        if (*ip == ']') nest--; // leave nest (or main loop, in which nesting > 0 fails)

                        ip++; // move right
                    }

                }
                break;
            case ']':
                if (*cp) {
                    ip--; // move before the closing bracket
                    while (nest > 0 || *ip != '[') { // rewind to matching [
                        if (*ip == '[') nest--; // leave nest (or main loop, in which nesting > 0 fails)
                        if (*ip == ']') nest++; // enter nest

                        ip--; // move left
                    }
                    ip--; // move before the opening bracket
                }
                break;
        }

        ip++; // move to next instruction
    }


    free(cells);
    free(bf);
    return 0;
}

Note that this program compiles without errors (gcc -Wall -std=c99 brainfuck.c) and runtime behaves normal.

Note: if you are offended by the name brainfuck, live with it. It's a programming language that is named that way by the author and I respect and use that name.

Tamas answered 27/3, 2011 at 1:43 Comment(0)
S
11

Is splint C99 aware?

Try /* ... */ instead of // ... and move declarations to before any code

Smooth answered 27/3, 2011 at 1:51 Comment(10)
OMG I never knew single-line comments were introduced in c99. My compiler never complained, even without -std=c99. Though sadly this doesn't fix my issue.Tamas
I've added another C99 thing to my post (declarations mixed with code is new too)Smooth
@nightcracker: quite a few compilers accepted // as an extension, but it wasn't officially part of the language until C99.Cagle
Is it then considered a bad practice? I find it much easier and better as it doesn't block commenting out blocks of code.Tamas
Accepted. Moving declarations did the trick. Never knew that too >.< After googling I found this: bugs.debian.org/cgi-bin/bugreport.cgi?bug=369264Tamas
I think it's ok (not bad practice) to use // ... comments and declarations mixed with code. If your source is never going to see a C89 compiler (without extras) or any old-fashioned tool (like splint) you won't run into any troublesSmooth
What would you recommend for filtering out common errors instead of splint?Tamas
@nightcracker: I tend to write C89 so splint works for me ... but compilers are pretty good at catching common errors and silly mistakes nowadays. Crank up your compiler diagnostics.Smooth
My compiler diagnostics are fine (-Wall). I was thinking that maybe it didn't pick up everything, but they indeed are very good these days. Hmm, maybe I shouldn't have read K&R version 1 as my first (and ATM only) C book.Tamas
My gcc default invocation for C99 catches 2 "comparison between signed and unsigned" (lines 48 and 80) and 1 "switch missing default case" (line 71) when compiling your code.Smooth
P
2

You can also use +slashslashcomment when calling splint. In this case:

splint +slashslashcomment brainfuck.c


Splint Manual's Appendix B:

P:- slashslashcomment

A // comment is used. ISO C99 allows // comments, but earlier standards did not.

(would put this in a comment, but don't have the necessary rep)

Publicspirited answered 19/10, 2016 at 20:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.