Tip/Linux2025. 9. 12. 21:16

사칙연산과 변수명을 지원하는 간단한 계산기 예제

calc.l

%{
#include <string.h>
%}

EXIT      [quit()|exit()]
MSG       \"[a-zA-Z0-9_\-\$:; ]{0,128}\"
NAME      [a-zA-Z][_\-\$a-zA-Z0-9]{0,19}
NUMBER    [0-9]+

%%
{EXIT}    { exit(0); }
{MSG}     {
            int len = yyleng - 2;
            strncpy(yylval.sMsg, yytext + 1, len);
            yylval.sMsg[len] = '\0';
            return MSG;
          }
{NAME}    { strcpy(yylval.sVal, yytext); return NAME; }
{NUMBER}  { yylval.iVal = atoi(yytext); return NUMBER; }
[ \t]     ;
"?"       { return PRINT; }
"\n"      { return '\n'; }
"\r"      { return '\n'; }
.         { return yytext[0]; }

calc.y

%{
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

extern int yylex();
void yyerror(const char *s);
int getVarIndex(const char *name);

#define MAX_VARS     100
#define MAX_NAME_LEN 20

typedef struct {
    char name[MAX_NAME_LEN + 1];
    int value;
} Variable;

Variable vars[MAX_VARS];
int varCount = 0;

char **fileList;
unsigned int currentFile = 0;
unsigned int nFiles = 0;
%}

%union {
    int iVal;
    char sVal[40];
    char sMsg[128];
}

%token <iVal> NUMBER
%token <sVal> NAME
%token <sMsg> MSG
%token PRINT
%type <iVal> expr

%left  '-' '+'
%left  '*' '/'
%nonassoc UMINUS
%%

stat_list:
        | stat_list stat '\n'
        ;
stat:
        | PRINT MSG NAME {
            int idx = getVarIndex($3);
            int value = (idx != -1) ? vars[idx].value : 0;
            printf("%s %d\n", $2, value);
          }
        | PRINT MSG  { printf("%s\n", $2); }
        | NAME '=' expr {
            int idx = getVarIndex($1);
            if (idx != -1) {
                vars[idx].value = $3;
            }
          }
        | expr { printf("= %d\n", $1); }
        ;

expr: NUMBER { $$ = $1; }
      | NAME {
          int idx = getVarIndex($1);
          $$ = (idx != -1) ? vars[idx].value : 0;
        }
      | expr '+' expr { $$ = $1 + $3; }
      | expr '-' expr {  $$ = $1 - $3; }
      | expr '*' expr { $$ = $1 * $3; }
      | expr '/' expr {
            if ($3 == 0) {
                yyerror("Divide by zero");
                $$ = 0;
            } else {
                $$ = $1 / $3;
            }
        }
    | '-' expr %prec UMINUS { $$ = -$2; }
    | '(' expr ')' { $$ = $2; }
    ;
%%

#include "lex.yy.c"

void yyerror(const char *s){
    fprintf(stderr, "Error: %s\n", s);
}

int getVarIndex(const char *name) {
    for (int i = 0; i < varCount; i++) {
        if (strcmp(name, vars[i].name) == 0) {
            return i;
        }
    }
    if (varCount < MAX_VARS) {
        strcpy(vars[varCount].name, name);
        vars[varCount].value = 0;
        return varCount++;
    }
    yyerror("Variable table full");
    return -1;
}

int main(int argc, char **argv) {
    printf("Chobocho'c Calc V0.1\n");

    FILE *file = NULL;
    fileList = argv + 1;
    nFiles = argc - 1;

    if (argc == 2) {
    	currentFile = 1;
    	file = fopen(argv[1], "r");
    	if (!file) {
    		fprintf(stderr, "Could not open %s\n", argv[1]);
    		exit(1);
    	}
    	yyin = file;
    }

    return yyparse();
}

makefile

CC = gcc
LIBS = -lfl
LEX = flex
YACC = yacc

all: calc2

calc2: y.tab.c lex.yy.c
	$(CC) -o calc2 y.tab.c $(LIBS)

y.tab.c: calc.y
	$(YACC) -dv calc.y

lex.yy.c: calc.l
	$(LEX) calc.l

clean:
	rm y.tab.h
	rm y.tab.c
	rm y.tab.h.pch
	rm lex.yy.c
	rm calc2

Test Sample

10 + 20
rabbit = 20
dog = 30
cat = 10
chicken = 10
total_legs = dog * 4 + rabbit * 4 + chicken * 2 + cat * 4
? "Total legs:" total_legs
totalAnimals = rabbit + dog + cat + chicken
? "Total Animals:" totalAnimals

Test 결과

'Tip > Linux' 카테고리의 다른 글

LEX 기초  (0) 2025.09.09
Lex & Yacc로 간단한 사칙연산 계산기 만들기  (0) 2025.09.02
컴파일러 관련 기초 서적 정리 (한글판만)  (0) 2025.01.18
Posted by chobocho