해야할것
1. expression statement 정의
2. go에서 String 역할을 할 함수포인터 구현
3. 메모리 free 시킬 방법
1번
ast.h
typedef struct _expressionStatement {
Expression expression;
Token* token;
int is_valid;
}ExpressionStatement;
ast.c
ExpressionStatement* newExpressionStatement()
{
ExpressionStatement* es = (ExpressionStatement*)malloc(sizeof(ExpressionStatement));
es->expression.node.TokenLiteral = expressionTokenLiteral;
es->expression.node.statementType = EXPRESSIONSTMT;
es->is_valid = 1;
es->expression.node.PrintString = printExpressionStmt;
return es;
}
이미 코드가 나와있었기에 만들기 쉬웠음
다만 expression이 포인터가아닌 그냥 구조체로 일단 사용했음
따라서 NULL 비교가 불가하기에 is_valid 추가시킴
2번
const char* printProgram(void* self)
{
Program* p = (Program*)self;
int len = p->statementsNum;
char* str = NULL;
for (int i = 0; i < len; i++) {
append_string(&str, p->statements[i]->node.PrintString(p->statements[i]));
}
return str;
}
const char* printLetStmt(void* self)
{
LetStatement* ls = (LetStatement*)self;
char* str = NULL;
append_string(&str, ls->statement.node.TokenLiteral(ls));
append_string(&str, " ");
append_string(&str, ls->name->node.TokenLiteral(ls->name));
append_string(&str, " = ");
if (ls->value != NULL) {
append_string(&str,ls->value->node.TokenLiteral(ls->value));
}
append_string(&str, ";");
return str;
}
const char* printReturnStmt(void* self)
{
ReturnStatement* rs = (ReturnStatement*)self;
char* str = NULL;
append_string(&str, rs->statement.node.TokenLiteral(rs));
append_string(&str, " ");
if (rs->returnValue != NULL) {
append_string(&str, rs->returnValue->node.TokenLiteral(rs->returnValue));
}
append_string(&str, ";");
return str;
}
const char* printExpressionStmt(void* self)
{
ExpressionStatement* es = (ExpressionStatement*)self;
if (es->is_valid == 1) {
return es->expression.node.TokenLiteral(es);
}
return "";
}
const char* printIdentifier(void* self)
{
Identifier* i = (Identifier*)self;
return i->value;
}
void append_string(char** dest, const char* src) {
if (!src || !dest) return;
size_t old_len = (*dest ? strlen(*dest) : 0);
size_t src_len = strlen(src);
size_t new_len = old_len + src_len + 1;
char* new_str = (char*)realloc(*dest, new_len);
if (!new_str) {
perror("Memory allocation failed");
exit(1);
}
if (old_len == 0) {
if (strcpy_s(new_str, new_len, src) != 0) {
perror("strcpy_s failed");
exit(1);
}
}
else {
if (strcat_s(new_str, new_len, src) != 0) {
perror("strcat_s failed");
exit(1);
}
}
*dest = new_str;
}
문자열을 동적으로 추가해서 리턴하게 끔 만들어줌
책 내용 그대로 따라친거라 딱히 말할 내용없는듯
다만 문자열을 동적으로 추가하는 함수는 하나 추가해줌
String 관련 함수들이 없는게 참 불편한듯
3번
처음에 동적할당을 막 시켜놓으니 메모리를 해제 시키는 것에 좀 애를 먹었음
트리 구조이니 타고타고 들어가서 후위순회 방식으로 해야한다고 생각
일단 구조부터 파악을 하자 싶어서 코드를 다시 살펴봄
1. parser에는 errors와 lexer가 붙어있다.
2. lexer에는 input이 동적할당 되어있음
이 둘은 이렇게 따로 처리를 하고
program을 루트 노드로 Let, Return 등이 붙어있음
이를 비교해서 Let일때 name,token,expressionStmt를 free해줘야함
void freeToken(Token* token)
{
if (token->literal != NULL)
free(token->literal);
free(token);
}
void freeLexer(Lexer* l)
{
if (l->input != NULL)
free(l->input);
free(l);
}
void freeParser(Parser* p)
{
if(p->l!=NULL)
freeLexer(p->l);
for (int i = 0; i < p->errorsNum; i++) {
free(p->errors[i]);
}
free(p->errors);
}
void freeProgram(Program* p)
{
for (int i = 0; i < p->statementsNum; i++) {
int type = p->statements[i]->node.statementType;
switch (type)
{
case LET:
freeLetStmt((LetStatement*)p->statements[i]);
break;
case RETURN:
break;
case IDENTIFIER:
break;
case EXPRESSIONSTMT:
break;
}
}
free(p->statements);
free(p);
}
void freeLetStmt(LetStatement* ls)
{
freeToken(ls->token);
if (ls->name != NULL)
freeIdentifier(ls->name);
if (ls->value != NULL)
freeExpressionStmt(ls->value);
free(ls);
}
void freeIdentifier(Identifier* i)
{
freeToken(i->token);
free(i);
}
void freeExpressionStmt(ExpressionStatement* es)
{
free(es->token);
free(es);
}