yaccSample

이영규·2024년 7월 13일

Compiler

목록 보기
10/10

yylex() 함수는 Lex/Flex와 Yacc/Bison을 함께 사용할 때 핵심적인 역할을 합니다. 구문 분석기(yyparse())가 이 함수를 호출하여 입력 스트림에서 토큰을 하나씩 가져와 파싱을 수행합니다.

yylex()는 Lex/Flex에 의해 자동으로 생성되는 어휘 분석기(lexical analyzer) 함수입니다. 이 함수의 주요 역할과 특징은 다음과 같습니다:
토큰 인식: 입력 스트림에서 다음 토큰을 읽고 인식합니다.
Yacc/Bison과의 연동: Yacc/Bison이 생성한 구문 분석기(yyparse())와 함께 작동하여, 구문 분석에 필요한 토큰을 제공합니다.
자동 생성: Lex/Flex 입력 파일(.l)에 정의된 규칙에 따라 자동으로 생성됩니다.
토큰 반환: 인식된 토큰의 유형을 나타내는 정수 값을 반환합니다.
yylval 설정: 토큰과 관련된 값을 yylval 변수에 저장합니다. 이 값은 Yacc/Bison에서 사용됩니다.
입력 처리: 기본적으로 표준 입력에서 읽지만, yyin 파일 포인터를 통해 다른 입력 소스를 지정할 수 있습니다.
종료 조건: 입력의 끝에 도달하면 0을 반환하여 파싱 종료를 알립니다.

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

#define HASHSIZE 31

typedef struct nlist {
    struct nlist *next;
    char *name;
    double value;
}Node;

static Node *hashtab[HASHSIZE];

unsigned hash(char *s) {
    unsigned hashval;
    for (hashval = 0; *s != '\0'; s++)
        hashval = *s + 31 * hashval;
    return hashval % HASHSIZE;
}

Node *find(char *s) {
    Node *np;
    for (np = hashtab[hash(s)]; np != NULL; np = np->next)
        if (strcmp(s, np->name) == 0)
            return np;
    return NULL;
}

Node *store(char *name, double value) {
    Node *np;
    unsigned hashval;
    if ((np = find(name)) == NULL) {
        np = (Node *) malloc(sizeof(*np));
        if (np == NULL || (np->name = strdup(name)) == NULL)
            return NULL;
        hashval = hash(name);
        np->next = hashtab[hashval];
        hashtab[hashval] = np;
    } else
        free((void *) np->name);
    np->name = strdup(name);
    np->value = value;
    return np;
}

void print_symbol_table() {
    Node *np;
    printf("심볼 테이블:\n");
    for (int i = 0; i < HASHSIZE; i++) {
        if (hashtab[i] != NULL) {
            printf("해시 인덱스 %d:\n", i);
            for (np = hashtab[i]; np != NULL; np = np->next) {
                printf("%s = %g\n", np->name, np->value);
            }
        }
    }
}

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

int yylex();

%}

%union {
    int ival;
    double fval;
    char *sval;
}

%token <ival> INT
%token <fval> FLOAT
%token <sval> ID
%type <fval> E

%left '+' '-'
%left '*' '/'

%%

S: S L
  | L
  ;

L: E '\n'        { printf("결과: %g\n", $1); }
 | ID '=' E '\n' { store($1, $3); printf("할당: %s = %g\n", $1, $3); free($1);}
 | '$' '\n'      { print_symbol_table(); exit(0); }
 ;

E: E '+' E    { $$ = $1 + $3; }
 | E '-' E    { $$ = $1 - $3; }
 | E '*' E    { $$ = $1 * $3; }
 | E '/' E    { 
     if ($3 == 0) {
         yyerror("0으로 나누기 오류");
         YYERROR;
     }
     $$ = $1 / $3; 
 }
 | '(' E ')'  { $$ = $2; }
 | ID         { 
     Node *np = find($1);
     if (np == NULL) {
         yyerror("정의되지 않은 변수");
         YYERROR;
     }
     $$ = np->value;
     free($1);
 }
 | INT        { $$ = (double)$1; }
 | FLOAT      { $$ = $1; }
 ;

%%

int main() {
    yyparse();
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define HASHSIZE 101
typedef struct nlist {
    struct nlist *next;
    char *name;
    int value;
} Node;

Node nlist *hashtab[HASHSIZE];

필요한 헤더 파일들을 포함합니다.
HASHSIZE를 101로 정의합니다. 이는 해시 테이블의 크기입니다.
nlist 구조체를 정의합니다. 이는 해시 테이블의 각 항목을 나타냅니다.
next: 같은 해시 값을 가진 다음 항목을 가리키는 포인터 (충돌 해결을 위한 체이닝)
name: 변수 이름을 저장하는 문자열 포인터
value: 변수의 값을 저장하는 정수
hashtab은 HASHSIZE 크기의 nlist 포인터 배열로, 실제 해시 테이블입니다.
해시 함수

unsigned hash(char *s) {
    unsigned hashval;
    for (hashval = 0; *s != '\0'; s++)
        hashval = *s + 31 * hashval;
    return hashval % HASHSIZE;
}

이 함수는 문자열을 입력받아 해시 값을 생성합니다:
hashval을 0으로 초기화합니다.
문자열의 각 문자에 대해 루프를 돕니다.
각 단계에서 hashval에 31을 곱하고 현재 문자의 ASCII 값을 더합니다.
최종 hashval을 HASHSIZE로 나눈 나머지를 반환합니다.
이 방식은 간단하면서도 효과적인 해시 함수입니다. 31을 곱하는 이유는 이 값이 경험적으로 좋은 분포를 만드는 것으로 알려져 있기 때문입니다.
검색 함수

Node *find(char *s) {
    struct nlist *np;
    for (np = hashtab[hash(s)]; np != NULL; np = np->next)
        if (strcmp(s, np->name) == 0)
            return np;
    return NULL;
}

이 함수는 주어진 이름의 항목을 해시 테이블에서 찾습니다:
먼저 hash(s)를 호출하여 해당 문자열의 해시 값을 얻습니다.
해당 해시 값에 해당하는 연결 리스트를 순회합니다.
일치하는 이름을 찾으면 해당 nlist 구조체의 포인터를 반환합니다.
찾지 못하면 NULL을 반환합니다.
설치(삽입/갱신) 함수

Node *install(char *name, int value) {
    struct nlist *np;
    unsigned hashval;
    if ((np = lookup(name)) == NULL) {
        np = (struct nlist *) malloc(sizeof(*np));
        if (np == NULL || (np->name = strdup(name)) == NULL)
            return NULL;
        hashval = hash(name);
        np->next = hashtab[hashval];
        hashtab[hashval] = np;
    } else
        free((void *) np->name);
    np->name = strdup(name);
    np->value = value;
    return np;
}

이 함수는 새 항목을 삽입하거나 기존 항목을 갱신합니다:
먼저 lookup을 호출하여 이름이 이미 존재하는지 확인합니다.
존재하지 않으면 새 nlist 구조체를 할당하고 해시 테이블에 삽입합니다.
이미 존재하면 기존 항목의 이름을 해제합니다.
새 이름과 값을 설정합니다.
삽입/갱신된 항목의 포인터를 반환합니다.
이 구현은 체이닝 방식으로 충돌을 해결합니다. 새 항목은 항상 리스트의 앞에 추가됩니다.
이 코드는 심볼 테이블을 구현하는 간단한 인터프리터의 일부로 보입니다. 변수를 저장하고 검색하는 데 해시 테이블을 사용하여 효율적인 연산을 가능하게 합니다.

Yacc/Bison 문법에서 $$는 현재 규칙의 결과값을 나타냅니다.
예를 들어, 코드의 다음 부분에서:
c
E: E '+' E { $$ = $1 + $3; }

$$는 이 규칙의 결과값을 의미하며, $1과 $3은 각각 첫 번째와 세 번째 요소의 값을 나타냅니다.
이 문법은 산술 표현식을 파싱하고 계산하는데 사용됩니다. 위의 예에서는 두 표현식의 합을 계산하고 그 결과를 규칙의 값으로 설정합니다.
Yacc/Bison은 이러한 규칙들을 사용하여 입력을 파싱하고 원하는 동작을 수행하는 파서를 생성합니다.

%union {
    int ival;
    double fval;
    char *sval;
}

이 부분은 토큰과 비단말 기호의 값을 저장하기 위한 union을 정의합니다. 정수(ival), 부동소수점(fval), 문자열(sval) 타입을 포함합니다.

%token <ival> INT
%token <fval> FLOAT
%token <sval> ID
%type <fval> E

토큰과 비단말 기호의 타입을 지정합니다. INT는 정수, FLOAT는 부동소수점, ID는 문자열, E(표현식)는 부동소수점 타입입니다.

%left '+' '-'
%left '*' '/'

연산자의 우선순위와 결합성을 정의합니다. '*'와 '/'가 '+'와 '-'보다 우선순위가 높습니다.

%%

문법 규칙의 시작을 나타냅니다.

S: S L
  | L
  ;

시작 기호 S를 정의합니다. 여러 개의 L(라인)로 구성됩니다.

L: E '\n'        { printf("결과: %g\n", $1); }
 | ID '=' E '\n' { store($1, $3); printf("할당: %s = %g\n", $1, $3); free($1);}
 | '$' '\n'      { print_symbol_table(); exit(0); }
 ;

L(라인)에 대한 규칙을 정의합니다. 표현식 계산, 변수 할당, 심볼 테이블 출력 기능을 포함합니다.

E: E '+' E    { $$ = $1 + $3; }
 | E '-' E    { $$ = $1 - $3; }
 | E '*' E    { $$ = $1 * $3; }
 | E '/' E    { 
     if ($3 == 0) {
         yyerror("0으로 나누기 오류");
         YYERROR;
     }
     $$ = $1 / $3; 
 }
 | '(' E ')'  { $$ = $2; }
 | ID         { 
     Node *np = find($1);
     if (np == NULL) {
         yyerror("정의되지 않은 변수");
         YYERROR;
     }
     $$ = np->value;
     free($1);
 }
 | INT        { $$ = (double)$1; }
 | FLOAT      { $$ = $1; }
 ;

E(표현식)에 대한 규칙을 정의합니다. 사칙연산, 괄호, 변수 참조, 정수와 부동소수점 상수를 처리합니다.

%%

문법 규칙의 끝을 나타냅니다.

int main() {
    yyparse();
    return 0;
}

메인 함수에서 구문 분석을 시작합니다.
이 코드는 간단한 계산기 기능을 구현하며, 변수 할당, 사칙연산, 오류 처리 등의 기능을 포함하고 있습니다.

profile
슥슥

0개의 댓글