Tiny Basic

제약 조건

1. 변수명은 A-Z만 가능.

2. 자료형은 정수형만 가능.

3. 레이블은 100개까지 사용 가능하며, 레이블 명은 최대 10글자, 숫자로만 써야됨.

4. 레이블은 함수 호출 및 GOTO 문에 쓰임

5. for 문 및 함수 호출은 25개까지 가능


문법종류 및 사용법

PRINT : 출력
1) PRINT "내용"
- 내용을 출력
2) PRINT "내용", 수식
- 내용과 수식 결과를 출력
3) PRINT 변수
- 변수 값 출력
4) PRINT 정수
- 정수 출력

INPUT: 입력
1) INPUT 변수
- ?가 출력 되며 입력 기다림
2) INPUT "내용", 변수
- 내용이 출력되며 입력 기다림

IF: 분기
1) IF 비교문 THEN 명령
- IF 뒤의 비교문은 >, <, =, <>가 가능. <>는 같지 않음을 의미.

GOTO: 이동
1) GOTO 레이블명
- 레이블 명으로 이동.

FOR/NEXT: 반복
1) FOR 변수=1 TO 10
PRINT X
NEXT

- 1부터 10까지의 숫자 출력

GOSUB/RETURN: 함수 호출, 반환
1) GOSUB 레이블명
레이블명 내용
RETURN

- GOSUB은 GOTO문과 비슷하지만 RETURN을 만나면 다시 GOSUB의 다음 줄로 돌아와 명령 수행.

/* A tiny BASIC interpreter */
#include <stdio.h>
#include <setjmp.h>
#include <math.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>

#define NUM_LAB 100
#define LAB_LEN 10
#define FOR_NEST 25
#define SUB_NEST 25
#define PROG_SIZE 10000

#define DELIMITER 1
#define VARIABLE 2
#define NUMBER 3
#define COMMAND 4
#define STRING 5
#define QUOTE 6

#define PRINT 1
#define INPUT 2
#define IF 3
#define THEN 4
#define FOR 5
#define NEXT 6
#define TO 7
#define GOTO 8
#define EOL 9
#define FINISHED 10
#define GOSUB 11
#define RETURN 12
#define END 13
#define PRINTB   14

char *prog; /* holds expression to be analyzed */
jmp_buf e_buf; /* hold environment for longjmp() */

int variables[26]={ /* 26 user variables, A-Z */
    0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0
};

struct commands { /* keyword lookup table */
    char command[20];
    char tok;
} table[] = { /* Commands must be entered lowercase */
    "print",PRINT, /* in this table */
    "input",INPUT,
    "if",IF,
    "then",THEN,
    "goto",GOTO,
    "for",FOR,
    "next",NEXT,
    "to",TO,
    "gosub",GOSUB,
    "return",RETURN,
    "end",END,
    "printb",PRINTB,
    "",END /* mark end of table */
};

char token[80];
char token_type,tok;

struct label {
    char name[LAB_LEN];
    char *p; /* points to place to go in source file */
};
struct label label_table[NUM_LAB];

char *find_label(), *gpop();

struct for_stack {
    int var; /* counter variable */
    int target; /* target value */
    char *loc;
} fstack[FOR_NEST]; /* stack for FOR/NEXT loop */
struct for_stack fpop();

char *gstack[SUB_NEST]; /* stack for gosub */

int ftos; /* index to top of FOR stack */
int gtos; /* index to top of GOSUB stack */

char get_token(),look_up(char *s);
void print(),scan_labels(),find_eol(),exec_goto();
void exec_if(),exec_for(),next(),fpush(),input();
void gosub(),greturn(),gpush(),label_init();
void putback(), assignment(),serror(int error);
void get_exp(int *result), printb();
int load_program(char *p, char *fname);
int get_next_label(char *s),iswhite(char c);
int find_var(char *s),isdelim(char c);

void main(int argc,char *argv[])
{
    char *p_buf;
    if(argc!=2) {
        printf("usage: run <filename>\n");
        exit(1);
    }

    /* allocate memory for the program */
    if(!(p_buf=(char *) malloc(PROG_SIZE))) {
        printf("allocation failure");
        exit(1);
    }

    /* load the program to execute */
    if(!load_program(p_buf,argv[1])) exit(1);

    if(setjmp(e_buf)) exit(1); /* initialize the long jump buffer */

    prog=p_buf;
    scan_labels(); /* find the labels in the program */
    ftos = 0; /* initialize the FOR stack index */
    gtos = 0; /* initialize the GOSUB stack index */
    do {
        token_type=get_token();
        /* check for assignment statement */
        if(token_type == VARIABLE) {
            putback(); /*return the var to the input stream */
            assignment(); /* must be assignment statement */
        }
        else /* is command */
            switch(tok) {
                case PRINT:
                    print();
                    break;
                case PRINTB:
                    printb();
                    break;
                case GOTO:
                    exec_goto();
                    break;
                case IF:
                    exec_if();
                    break;
                case FOR:
                    exec_for();
                    break;
                case NEXT:
                    next();
                    break;
                case INPUT:
                    input();
                    break;
                case GOSUB:
                    gosub();
                    break;
                case RETURN:
                    greturn();
                    break;
                case END:
                    exit(0);
            }
    } while (tok != FINISHED);
}

/* Load a program. */
int load_program(char *p, char *fname)
{
    FILE *fp;
    int i = 0;

    if(!(fp=fopen(fname,"r"))) return 0;

    i=0;
    do {
        *p = getc(fp);
        p++; i++;
    } while(!feof(fp) && i<PROG_SIZE);
    *(p-1)='\0'; /* null terminate the program */
    fclose(fp);
    return 1;
}

/* Assign a variable a value. */
void assignment()
{
    int var, value;

    /* get the variable a value */
    get_token();
    if(!isalpha(*token)) {
        serror(4);
        return;
    }

    var = toupper(*token)-'A';

    /* get the equals sign */
    get_token();
    if(*token!='=') {
        serror(3);
        return;
    }

    /* get the value to assign to var */
    get_exp(&value);

    /* assign the value */
    variables[var] = value;
}

/* Execute a simple version of the BASIC PRINT statement */
void print()
{
    int answer;
    int len=0,spaces;
    char last_delim;

    do {
        get_token(); /* get next list item */
        if(tok==EOL || tok==FINISHED) break;
        if(token_type==QUOTE) { /* is string */
            printf(token);
            len += strlen(token);
            get_token();
        }
        else { /* is expression */
            putback();
            get_exp(&answer);
            get_token();
            len += printf("%d",answer);
        }
        last_delim = *token;

        if(*token==';') {
            /* compute number of spaces to move to next tab */
            spaces = 8 - (len % 8);
            len += spaces; /* add in the tabbing position */
            while(spaces) {
                printf(" ");
                spaces--;
            }
        }
        else if(*token==','); /* do nothing */
        else if(tok!=EOL && tok!=FINISHED) serror(0);
    } while (*token==';' || *token==',');

    if(tok==EOL || tok==FINISHED) {
        if(last_delim != ';' && last_delim != ',') printf("\n");
    }
    else serror(0); /* error is not,or; */
}

void printb()
{
    int i, n=1;
    int arr[100];
    char var;

    get_token();
    if(token_type==VARIABLE)
    {
        var=toupper(*token)-'A';
        while((variables[var]/2)!=0)
        {
            arr[n] = variables[var] % 2;
            variables[var] = variables[var] / 2;
            n++;
        }

        arr[n] = variables[var]%2;
        for(i=0;i<n;i++)
            printf("%d", arr[n-i]);

        printf("\n");
    }
    else
        serror(4); /*not variables */
}

/* Find all labels. */
void scan_labels()
{
    int addr;
    char *temp;

    label_init(); /* zero all labels */
    temp = prog; /* save pointer to top of program */

    /* if the first token in the file is a label */
    get_token();
    if(token_type==NUMBER) {
        strcpy(label_table[0].name,token);
        label_table[0].p=prog;
    }

    find_eol();
    do {
        get_token();
        if(token_type==NUMBER) {
            addr = get_next_label(token);
            if(addr== -1 || addr== -2) {
                (addr== -1) ?serror(5):serror(6);
            }
            strcpy(label_table[addr].name,token);
            label_table[addr].p=prog; /* current point in program */
        }
        /* if not on a blank line,find next line */
        if(tok!=EOL) find_eol();
    } while(tok!=FINISHED);
    prog = temp; /* restore to original */
}

/* Find the start of next line */
void find_eol()
{
    while(*prog!='\n' && *prog!='\0') ++prog;
    if(*prog) prog++;
}

/* Return index of next free position in label array.
   A -1 is returned if the array is full.
   A -2 is returned when duplicate label is found.
 */
int get_next_label(char *s)
{
    register int t;

    for(t=0; t<NUM_LAB; ++t) {
        if(label_table[t].name[0]==0) return t;
        if(!strcmp(label_table[t].name,s)) return -2; /* dup */
    }

    return -1;
}

/* Find location of given label. A null is returned if
   label is not found; otherwise a pointer to the position
   of the label is returned */
char *find_label(char *s)
{
    register int t;

    for(t=0; t<NUM_LAB; ++t)
        if(!strcmp(label_table[t].name,s)) return label_table[t].p;
    return '\0'; /* error condition */
}

/* Execute a GOTO statement. */
void exec_goto()
{
    char *loc;

    get_token(); /* get label to go to */
    /* find the location of the label */
    loc = find_label(token);
    if(loc=='\0')
        serror(7); /* label not defined */

    else prog=loc; /* start program running at that loc */
}

/* Initialize the array that holds the labels.
   By convention,a null label name indicates that */
void label_init()
{
    register int t;

    for(t=0; t<NUM_LAB; ++t) label_table[t].name[0]='\0';
}

/* Execute an IF statement. */
void exec_if()
{
    int x,y,cond;
    char op;

    get_exp(&x); /* get left expression */

    get_token(); /* get the operator */
    if(!strchr("=<>",*token)) {
        serror(0); /* not a legal operator */
        return;
    }
    op=*token;

    get_exp(&y); /* get right expression */
    /* determine the outcome */
    cond = 0;
    switch(op) {
        case '<':
            if(x<y) cond=1;
            break;
        case '>':
            if(x>y) cond=1;
            break;
        case '=':
            if(x==y) cond=1;
            break;
    }
    if(cond) { /* is true so process target of IF */
        get_token();
        if(tok!=THEN) {
            serror(8);
            return;
        } /* else program execution starts on next line */
    }
    else find_eol(); /* find start of next line */
}

/* Execute a for loop */
void exec_for()
{
    struct for_stack i;
    int value;

    get_token(); /* read the control variable */
    if(!isalpha(*token)) {
        serror(4);
        return;
    }

    i.var=toupper(*token)-'A'; /* save its index */

    get_token(); /* read the equal sign */
    if(*token!='=') {
        serror(3);
        return;
    }

    get_exp(&value); /* get initial value */

    variables[i.var]=value;

    get_token();
    if(tok!=TO) serror(9); /* read and discard the TO */

    get_exp(&i.target); /* get target value */

    /* if loop can execute at least once,push info on stack */
    if(value>=variables[i.var]) {
        i.loc = prog;
        fpush(i);
    }
    else /* otherwise,skip loop code altogether */
        while(tok!=NEXT) get_token();
}

/* Execute a NEXT statement. */
void next()
{
    struct for_stack i;

    i=fpop(); /* read the info */

    variables[i.var]++; /* increment control variable */
    if(variables[i.var]>i.target) return; /* all don */
    fpush(i); /* otherwise, restore the info */
    prog = i.loc; /* loop */
}

/* Push function for the FOR stack */
void fpush(i)
    struct for_stack i;
{
    if(ftos>FOR_NEST)
        serror(10);

    fstack[ftos]=i;
    ftos++;
}

struct for_stack fpop()
{
    ftos--;
    if(ftos<0) serror(11);
    return(fstack[ftos]);
}

/* Execute a simple from of the BASIC INPUT command */
void input()
{
    char var;
    int i;

    get_token(); /* see if prompt string is present */
    if(token_type==QUOTE) {
        printf(token); /* if so,print it and check for comma */
        get_token();
        if(*token!=',') serror(1);
        get_token();
    }
    else printf("? "); /* otherwise,prompt with / */
    var = toupper(*token)-'A'; /* get the input var */

    scanf("%d",&i); /*read input */

    variables[var] = i; /* store it */
}
/* Execute a GOSUB command. */
void gosub()
{
    char *loc;

    get_token();
    /* find the label to call */
    loc = find_label(token);
    if(loc=='\0')
        serror(7); /* label not defined */
    else {
        gpush(prog); /* save place to return to */
        prog = loc; /* start program running at that loc */
    }
}

/* Return from GOSUB */
void greturn()
{
    prog = gpop();
}

/* GOSUB stack push function. */
void gpush(char *s)
{
    gtos++;

    if(gtos==SUB_NEST) {
        serror(12);
        return;
    }

    gstack[gtos]=s;
}

/* GOSUB stack pop function. */
char *gpop()
{
    if(gtos==0) {
        serror(13);
        return 0;
    }
    return(gstack[gtos--]);
}
/* recursive descent parser for integer expressions
   which may include variables */

#define DELIMITER 1
#define VARIABLE 2
#define NUMBER 3
#define COMMAND 4
#define STRING 5
#define QUOTE 6

#define EOL 9
#define FINISHED 10

void level2(),level3(),level4(),level5();
void level6(),primitive(),arith(),unary();

/* Entry point into parser. */
void get_exp(int *result)
{
    get_token();
    if(! *token) {
        serror(2);
        return;
    }
    level2(result);
    putback();
}
/* Add or subtract two terms. */
void level2(int *result)
{
    register char op;
    int hold;

    level3(result);
    while((op = *token) == '+' || op == '-') {
        get_token();
        level3(&hold);
        arith(op,result,&hold);
    }
}

/* Multiply or divide two factors. */
void level3(int *result)
{
    register char op;
    int hold;

    level4(result);
    while((op = *token) == '*' || op == '/' || op == '%' || op == '@') {
        get_token();
        level4(&hold);
        arith(op,result,&hold);
    }
}

/* Process integer exponent. */
void level4(int *result)
{
    int hold;

    level5(result);
    if(*token == '^') {
        get_token();
        level4(&hold);
        arith('^',result,&hold);
    }
}

/* Is a unary + or -. */
void level5(int *result)
{
    register char op;
    op = 0;
    if((token_type==DELIMITER) && *token == '+' || *token == '-') {
        op = *token;
        get_token();
    }
    level6(result);
    if(op)
        unary(op,result);
}

/* Process parenthesized expression. */
void level6(int *result)
{
    if((*token == '(') && (token_type == DELIMITER)) {
        get_token();
        level2(result);
        if(*token != ')')
            serror(1);
        get_token();
    }
    else
        primitive(result);
}

/* Find value of number or variable. */
void primitive(int *result)
{
    switch(token_type) {
        case VARIABLE:
            *result = find_var(token);
            get_token();
            return;
        case NUMBER:
            *result = atoi(token);
            get_token();
            return;
        default:
            serror(0);
    }
}

/* Perform the specified arithmetic. */
void arith(char o,int *r,int *h)
{
    register int t, ex;

    switch(o) {
        case '-':
            *r = *r - *h;
            break;
        case '+':
            *r = *r + *h;
            break;
        case '*':
            *r = *r * *h;
            break;
        case '/':
            *r = (*r) / (*h);
            break;
        case '%':
            t = (*r) / (*h);
            *r = *r - (t*(*h));
            break;
        case '@':
            *r = *r * *h + 1;
            break;
        case '^':
            ex = *r;
            if(*h == 0) {
                *r = 1;
                break;
            }
            for(t = *h-1; t>0; --t) *r= (*r) * ex;
            break;
    }
}
/* Reverce the sign. */
void unary(char o,int *r)
{
    if(o == '-') *r = -(*r);
}

/* Find the value of a variable */
int find_var(char *s)
{
    if(!isalpha(*s)) {
        serror(4); /* not a variable */
        return 0;
    }
    return variables[toupper(*token) - 'A'];
}

/* Display an error message */
void serror(int error)
{
    static char *e[] = {
        "syntex error",
        "unbalenced parentheses",
        "no expression present",
        "equal sign expected",
        "not a variable",
        "label table full",
        "duplicate label",
        "undefined label",
        "THEN expected",
        "TO expected",
        "too many nested FOR loops",
        "NEXT without FOR",
        "too many nested GOSUBs",
        "RETURN without GOSUB"
    };
    printf("%s\n",e[error]);
    longjmp(e_buf,1); /* return to save point */
}

/* Get a token. */
char get_token()
{
    register char *temp;

    token_type=0; tok=0;
    temp = token;
    if(*prog == '\0') { /* end of file */
        *token = 0;
        tok = FINISHED;
        return(token_type=DELIMITER);
    }

    while(iswhite(*prog)) ++prog; /* skip over white space */

    if(*prog=='\n') { /* crLf */
        ++prog;
        tok = EOL; *token='\n';
        token[1]='\0';
        return(token_type = DELIMITER);
    }

    if(strchr("+-*^/%=;(),><@",*prog)) { /* delimiter */
        *temp = *prog;
        prog++; /* advanced to next position */
        temp++;
        *temp=0;
        return(token_type=DELIMITER);
    }

    if(*prog=='"') { /* quoted string */
        prog++;
        while(*prog!='"' && *prog!='\n') *temp++ = *prog++;
        if(*prog=='\n') serror(1);
        prog++; *temp=0;
        return(token_type=QUOTE);
    }

    if(isdigit(*prog)) { /* number */
        while(!isdelim(*prog)) *temp++ = *prog++;
        *temp='\0';
        return(token_type=NUMBER);
    }

    if(isalpha(*prog)) { /* var or command */
        while(!isdelim(*prog)) *temp++ = *prog++;
        token_type=STRING;
    }

    *temp='\0';

    /* see if a string is a command or a variable */
    if(token_type==STRING) {
        tok=look_up(token); /* convert to internal rep */
        if(!tok) token_type=VARIABLE;
        else token_type=COMMAND; /* is a command */
    }
    return token_type;
}

/* Return a token to input stream. */
void putback()
{
    char *t;

    t = token;
    for(; *t; t++) prog--;
}

/* Look up a token's internal representation in the
   token table */
char look_up(char *s)
{
    register int i;
    char *p;

    /* convert to lowercase */
    p = s;
    while(*p){ *p = tolower(*p); p++; }

    /* see if token is in table */
    for(i=0; *table[i].command; i++)
        if(!strcmp(table[i].command,s)) return table[i].tok;
    return 0; /* unknown command */
}

/* Return true if c is a delimiter. */
int isdelim(char c)
{
    if(strchr(" ;,+-<>/*%^=()@",c) || c==9 || c=='\n' || c==0)
        return 1;
    return 0;
}

/* Return 1 if c is space or tab */
int iswhite(char c)
{
    if(c==' ' || c=='\t') return 1;
    else return 0;
}

테스트 코드

PRINT "TEST PROGORAM"
INPUT "ENTER A NUMBER:", I

GOSUB 100

END

100 FOR F=1 TO I
    X=X+1
    GOSUB 150
    IF F=2 THEN PRINT "F = 2"
    NEXT
RETURN

150 PRINT "X = ", X
RETURN
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License