본문 바로가기
C 언어

C 언어로 만든 Basic Interpreter

by treeCoder 2024. 1. 21.

C 언어로 만든 Basic Interpreter

가져온 곳은 여기이다.

 

https://grail.eecs.csuohio.edu/~somos/ddsbasic.c

/* DDS-BASIC interpreter in "C" annotated by Michael Somos 1997 */
/* INPUT bug fix 07Sep2005 Somos */
/* original by Diomidis Spinellis for 1990 IOCCC <URL:http://reality.sgi.com/csp/ioccc/1990/dds.c> */

#define O(b,f,u,s,c,a) b() { int o = f(); switch (*p++) { X u: _ o s b(); X c: _ o a b(); default: p--; _ o; } }
#define t(e,d,_,C) X e: f = fopen(B + d, _); C; fclose(f)
#define U(y,z) while (p = Q(s, y)) *p++ = z, *p = ' '
#define N for (i = 0; i < 11 * R; i++) m[i] &&
#define I "%d %s\n", i, m[i]
#define X ; break; case
#define _ return
#define R 999
typedef char *A;
int *C, E[R], L[R], M[R], P[R], l, i, j;
char B[R], F[2];
A m[12 * R], malloc(), p, q, x, y, z, s, d, f, fopen();
A Q(s, o) A s, o;
{
    for (x = s; *x; x++) {
        for (y = x, z = o; *z && *y == *z; y++) z++;
        if (z > o && !*z) _ x;
    }
    _ 0;
}

main()
{
    m[11 * R] = "E";
    while (puts("Ok"), gets(B)) switch (*B) {
    X'R': C = E; l = 1; for (i = 0; i < R; P[i++] = 0);
        while (l) {
            while (!(s = m[l])) l++;
            if (!Q(s, "\"")) {
                U("<>", '#');
                U("<=", '$');
                U(">=", '!');
            }
            d = B;
            while (*F = *s) {
                *s == '"' && j++;
                if (j & 1 || !Q(" \t", F)) *d++ = *s;
                s++;
            }
            *d-- = j = 0;
            if (B[1] != '=') switch (*B) {
            X'E': l = -1
            X'R': B[2] != 'M' && (l = *--C)
            X'I': B[1] == 'N' ? gets(p = B), P[*d] = S() : (*(q = Q(B, "TH")) = 0, p = B + 2, S() && (p = q + 4, l = S() - 1))
            X'P': B[5] == '"' ? *d = 0, puts(B + 6) : (p = B + 5, printf("%d\n", S()))
            X'G': p = B + 4, B[2] == 'S' && (*C++ = l, p++), l = S() - 1
            X'F': *(q = Q(B, "TO")) = 0; p = B + 5; P[i = B[3]] = S(); p = q + 2; M[i] = S(); L[i] = l
            X'N': ++P[*d] <= M[*d] && (l = L[*d]);
            } else p = B + 2, P[*B] = S();
            l++;
        }
    X'L': N printf(I)
    X'N': N free(m[i]), m[i] = 0
    X'B': _ 0
    t('S', 5, "w", N fprintf(f, I))
    t('O', 4, "r", while (fgets(B, R, f)) (*Q(B, "\n") = 0, G()))
    X 0: default: G();
    }
    _ 0;
}

G()
{
    l = atoi(B);
    m[l] && free(m[l]);
    (p = Q(B, " ")) ? strcpy(m[l] = malloc(strlen(p)), p + 1) : (m[l] = 0, 0);
}

O(S, J, '=', ==, '#', !=)
O(J, K, '<', <, '>', >)
O(K, V, '$', <=, '!', >=) 
O(V, W, '+', +, '-', -) 
O(W, Y, '*', *, '/', /)
Y() { int o; _ *p == '-' ? p++, -Y() : *p >= '0' && *p <= '9' ? strtol(p, &p, 0) : *p == '(' ? p++, o = S(), p++, o : P[*p++]; }

 

//-------------------------------------------- ************* ---------------------------------------

알기쉽게 풀어서 쓴 버전은 아래와 같다. (http://computer-programming-forum.com/47-c-language/4304b233749863b8.htm)

/*

 * DDS BASIC Interpreter V1.0(a)
 *

 * for the 1990 international obfuscated c code competition.

 *
 *      Notes
 *
 *  -   Please Note that I use 4 space tabstops, so for optimum readability
 *      I suggest you change you editor/file browser to use these too.
 *
 * [1]  The ;break; results from a #define from the original code:
 *              #define X ;break;case   used to save space.
 * [2]  The reason for the strange precedence of < > and <= >=
 *              is that there was a single #define statement for all the
 *              S(), J(), K(), V() and W() routines to save space.
 *
 * The following were "fairly" normal space savers and do not give rise
 * to any strange consequences.
 *
 * [3]  The fopen... fclose sequence was also #defined
 * [4]  The for() statments here were also partially #defined
 * [5]  This format statement was #defined
 *
 * I shall not mention the other defines as they are ordinary space
 * saving devices.
 */
typedef char * A;
int* C,                 /* line number stack pointer used for GOSUB's */
        E[999],         /* line number stack used for GOSUB's */
        /* the following two arrays are for FOR loops only */
        L[999],         /* line number start of FOR loop store */
        M[999],         /* loop termination constant values */
        P[999],         /* contains integer variable values */
        l,                      /* current line no of program */
        i,                      /* used as for loop counter for LIST, NEW and SAVE */
        j;                      /* boolean to indicate if line contains a string */
char B[999],    /* temporary input buffer and execution buffer */
         F[2];          /* used for stripping out spaces and tabs */
A m[12*999],    /* array of ptrs to lines of code */
        malloc(),
        p,                      /* used by S() for expression parsing */
        q,                      /* used to help find second token in IF and FOR statements */
        x,                      /* used as temp var in Q() */
        y,                      /* used as temp var in Q() */
        z,                      /* used as temp var in Q() */
        s,                      /* points to the current line being executed as loaded in */
        d,                      /* used to point to last char on current line */
        f,                      /* holds file descriptor of currently loaded file */
        fopen();
/*
 * this routine (originally called strstr) searches for string 'o' in
 * string 's' and returns a ptr to the first occurence of the string or
 * zero if not found
 */
A Q(s,o)
A s,o;
{
        for(x=s; *x; x++)
        {
                /* do search */
                for(y=x,z=o; *z && *y == *z; y++)
                        z++;
                /* return ptr if we found one */
                if(z > o && !*z)
                        return x;
        }
        /* otherwise return null */
        return  0;
} /* end of Q */

main()
{
        /*
         * initialise the very last possible line to END so
         * we never die if we try to run an empty program
         */
        m[11*999]="E";
        /* output prompt and get a command */
        while(puts("Ok"),gets(B))
                /* interpret the command */
                switch(*B)
                {
                ;break;         /* [1] */
                /*
                 * interpret away :-)
                 */
                case'R':        /* RUN */
                        C=E;    /* initialise stack pointer */
                        l=1;    /* initialise line counter */
                        /* initialise variables */
                        for(i=0; i<999; P[i++]=0);
                        while(l)
                        {
                                /* get next non null line and assign it to s */
                                while(!(s=m[l]))
                                        l++;
                                /*
                                 * check for expression with two letter operators
                                 * in current line, and convert to a single letter operator
                                 * to allow easier parsing later
                                 */
                                if (!Q(s,"\""))
                                {
                                        /* not equals */
                                        while(p=Q(s,"<>"))
                                                *p++='#',*p=' ';                /* convert to # */
                                        /* less than or equals */
                                        while(p=Q(s,"<="))
                                                *p++='$',*p=' ';                /* convert to $ */
                                        /* greater than or equals */
                                        while(p=Q(s,">="))
                                                *p++='!',*p=' ';                /* convert to ! */
                                }
                                /* assign d to start of temp buffer B */
                                d=B;
                                /*
                                 * strip out all the spaces and tabs for this line
                                 * moving the line from s to the temp buffer B ready
                                 * for execution
                                 */
                                while(*F = *s)
                                {
                                        /*
                                         * if its a string then set j
                                         * Note how the code after the && is only executed
                                         * if the code before it is true!  This is used a lot
                                         * as a space saving 'if' statement.
                                         */
                                        *s=='"'
                                                && j++;
                                        /*
                                         * if j == 1 then we have a string and we must get
                                         * d to point to the end of it so assign *s to *d
                                         * otherwise if the char is not a space or tab then
                                         * copy it too
                                         * Note all strings go to end of line
                                         */
                                        if (j&1||!Q(" \t",F))
                                                *d++ = *s;
                                        /* get next char */
                                        s++;
                                }
                                /*
                                 * d points to the last char of line here
                                 * so set that to null and point d to the previous
                                 * char on the line, so eg for a NEXT statement d
                                 * will point to the count variable.
                                 * For a PRINT, it will point to the last ", so
                                 * it is set to null later.
                                 * Note j is reset here too
                                 */
                                *d-- = j = 0;
                                /* check we are not doing a straight assignment */
                                if(B[1] != '=')
                                        switch(*B)      /* no so execute line */
                                        {
                                        ;break;         /* [1] */
                                        /* E... => END */
                                        case'E':
                                                l = -1;
                                                break;
                                        /* R.M => REM  otherwise its a RETURN */
                                        case'R':
                                                B[2] != 'M'
                                                        /* Not a REM, must be RETURN */
                                                        && (l = *--C);  /* pop line off stack */
                                                break;
                                        /* I... => IF or INPUT */
                                        case'I':
                                                B[1]=='N'
                                                ?
                                                        /* INPUT */
                                                        gets(p=B),      /* read it in */
                                                        P[*d]=S()
                                                :
                                                        /* must be IF so find the THEN */
                                                        (*(q=Q(B,"TH"))=0,
                                                        p=B+2,  /* get to end of THEN */
                                                        /* evaluate expression in IF */
                                                        S()
                                                                /* it's true so jump */
                                                                && (p=q+4,              /* move p to line number str */
                                                                        l=S()-1)        /* update line no */
                                                        );
                                                break;
                                        /* P...." => PRINT */
                                        case'P':
                                                B[5]=='"'
                                                ?
                                                        /* print a string */
                                                        *d=0,           /* clear out the final " */
                                                        puts(B+6)       /* do the print */
                                                :
                                                        /* print a number */
                                                        (p=B+5,printf("%d\n",S()));
                                                break;
                                        /*
                                         * G.S => GOSUB otherwise its a goto
                                         * Note how the code after the && is only executed
                                         * if the code before it is true!
                                         */
                                        case'G':
                                                p=B+4,  /* presume a GOTO make p point to new line */
                                                B[2]=='S'
                                                        /* actually a GOSUB! */
                                                        && (*C++=l /* store current line on stack */,
                                                        p++),   /* in counter to get line no */
                                                l=S()-1;        /* set new line no */
                                                break;
                                        /* F... => FOR */
                                        case'F':
                                                /* lets find the TO */
                                                *(q=Q(B,"TO"))=0;
                                                /* get the variable name */
                                                p=B+5;
                                                /*
                                                 * get the counter var and initialise it
                                                 * to the start value, assign i the ascii val
                                                 * of the var, this gives us a FOR nesting
                                                 * capability of 26!
                                                 */
                                                P[i=B[3]]=S();
                                                /* get value after TO */
                                                p=q+2;
                                                M[i]=S();
                                                /* store current line no */
                                                L[i]=l;
                                                break;
                                        /* N... => NEXT */
                                        case'N':
                                                /* inc count and check if we have finished the loop */
                                                ++P[*d]<=M[*d]
                                                        /* not finished so goto start of FOR */
                                                        &&(l=L[*d]);
                                        }
                                else
                                        /* do assignment here */
                                        p=B+2,
                                        P[*B]=S();
                                /* get next line */
                                l++;
                        }
                        ;break;         /* [1] */
                case'L':        /* LIST [4] */
                        for(i=0; i<11*999; i++)
                                m[i]
                                        /* only print if we have a line there! */
                                        && printf("%d %s\n",i,m[i]);  /* [5] */
                        break;
                case'N':        /* NEW [4] */
                        for(i=0; i<11*999; i++)
                                m[i]
                                        /* only delete if we have a line there! */
                                        && free(m[i]),m[i]=0;
                        break;
                case'B':        /* BYE */
                        return 0 ;
                        break;
                /*
                 * this is the "save a program to a file" code, note that this
                 * interpreter presumes that you have typed "SAVE fname" or at least
                 * "Sxxx fname" :-)
                 */
                case 'S':       /* SAVE [4] */
                        /* [3] */
                        f=fopen(B+5,"w");     /* open file for writing */
                        /* output the lines */
                        for(i=0; i<11*999; i++)
                                m[i]
                                        /* only save if we have a line there! */
                                        && fprintf(f,"%d %s\n",i,m[i]);               /* [5] */
                        fclose(f);
                        break;
                /*
                 * this is the "load a prgram from a file into memory" code,
                 * note that this interpreter presumes that you have typed
                 * "OLD fname" or at least "Oxx fname" :-)
                 */
                case 'O':       /* OLD */
                        /* [3] */
                        f=fopen(B+4,"r");     /* open the source file */
                        /* read it in */
                        while(fgets(B,999,f))
                                (*Q(B,"\n")=0,G());           /* insert lines into memory */
                        fclose(f);
                        break;
                case 0:         /* anything else */
                default:
                        G();
                }
        return 0;
} /* end of main */

/*
 * this routine (originally called enterline) inserts a new line into
 * the line buffer, the line inserted is currently held in the temporary
 * input buffer named B
 */
G()
{
        /* get the line number */
        l=atoi(B);
        /* clear out any previous incarnation of that line */
        m[l] && free(m[l]);
        /* do we have a valid line ? */
        (p=Q(B," "))
        ?
                /* copy the string into a newly created buffer */
                strcpy(m[l]=malloc(strlen(p)),p+1)
        :
                /* null out this line, ie empty */
                (m[l]=0,0);
} /* end of G */

/*
 * This routine causes all the routines below it to be called in turn
 * until no further expressions are found, the order of cascading gives
 * rise to operator precedence.
 * Together these routines form the expression parser.
 * This technique is called recusive descent parsing.
 *
 * S() does equivalence and non equivalence expressions
 */
S()
{
int o=J();      /* call J */
        switch(*p++)
        {
                ;break;         /* [1] */
        case '=':                                       /* IF equivalence */
                return o == S();                /* return boolean */
                ;break;         /* [1] */
        case '#':                                       /* IF non equivalence */
                return o != S();                /* return boolean */
        default:
                p--;
        return o;
        }
} /* end of S */

/*
 * J() handles less than and greater than expressions
 * higher precedence than S() expressions
 */
J()
{
int o=K();      /* call K */
        switch(*p++)
        {
                ;break;         /* [1] */
        case '<':                                    /* less than */
                return o < J();                      /* return boolean */
                ;break;         /* [1] */
        case '>':                                    /* greater than */
                return o > J();                      /* return boolean */
        default:
                p--;
                return o;
        }
} /* end of J */

/*
 * K() handles less than or equal to and greater than or equal to expressions
 * higher precedence than J() expressions [2].
 */
K()
{
int o=V();      /* call V */
        switch(*p++)
        {
                ;break;         /* [1] */
        case '$':                                       /* less than or equal */
                return o <= K();             /* return boolean */
                ;break;         /* [1] */
        case '!':                                       /* greater than or equal to */
                return o >= K();             /* return boolean */
        default:
                p--;
                return o;
        }
} /* end of K */

/*
 * V() handles addition and subtraction
 * higher precedence than K() expressions
 */
V()
{
int o=W();      /* call W */
        switch(*p++)
        {
                ;break;         /* [1] */
        case '+':
                return o + V();                 /* return added value */
                ;break;         /* [1] */
        case '-':
                return o - V();                 /* return subtracted value */
        default:
                p--;
                return o;
        }
} /* end of V */

/*
 * W() handles multiplication and division
 * higher precedence than V() expressions
 */
W()
{
int o=Y();      /* call Y */
        switch(*p++)
        {
                ;break;         /* [1] */
        case '*':
                return o * W();                 /* return multiplied value */
                ;break;         /* [1] */
        case '/':
                return o / W();                 /* return divided value */
        default:
                p--;
                return o;
        }
} /* end of W */

/*
 * this routine (originally called basic) does the nitty gritty work of
 * handling the lowest level of an expression, whether it be a number,
 * variable or another expression nested in brackets
 */
Y()
{
int o;
        return *p=='-'          /* are we after a negation? */
        ?
                p++,
                -Y()                            /* negate return value */
        :
                *p>='0' && *p<='9'
                ?
                        /* number, so return its value */
                        strtol(p,&p,0)      /* return value */
                :
                        *p=='('
                        ?
                                p++,            /* skip a start bracket if we have one */
                                o=S(),          /* evaluate the expression */
                                p++,            /* skip an end bracket if we have one */
                                o                       /* set return value! */
                        :
                                /* no further expressions just a var */
                                P[*p++];        /* set return value! */
} /* end of Y */

'C 언어' 카테고리의 다른 글

1. Chocolatey 설치 방법  (0) 2025.01.10
리습 1  (0) 2024.05.06
Tax calculator  (0) 2023.10.07
C언어로 Array of struct 만들기  (0) 2023.07.04
C언어로 만든 Hash Table  (0) 2023.05.12

댓글