Add string type support: literals, concatenation, println, and if eval

- Implement string literal tokenization and parsing (lexer + parser)
- Add string concatenation with + operator in evaluator
- Add println keyword for printing with newline
- Add NODE_IF evaluation in VM
- Fix null terminator bug in string concat buffer
This commit is contained in:
Jose Luis Montañes Ojados
2026-02-16 17:39:52 +01:00
parent 667e5564b8
commit dd67537598
6 changed files with 88 additions and 10 deletions

12
projects/str.j Normal file
View File

@@ -0,0 +1,12 @@
x = 0
while x < 10:
x = x + 1
if x > 9:
println "fin"
x = "a"
y = x * 1
z = y + 2
println "a" + z

3
projects/test.j Normal file
View File

@@ -0,0 +1,3 @@
x = 5
z = x > 2
print x + z

View File

@@ -16,6 +16,7 @@ typedef enum {
// Identificadores y keywords
TOK_ID, // x, foo, mi_var
TOK_PRINT, // print
TOK_PRINTLN, // println
TOK_IF, // if
TOK_WHILE, // while
@@ -117,6 +118,14 @@ Token *tokenize(const char *source, int *token_count) {
} else if (c == ':') {
tokens[count++] = make_token(TOK_COLON, ":");
pos++;
} else if (c == '"') {
// Leer todo hasta el proximo '"'
pos++; // consumir '"'
int start = pos;
while (source[pos] != '"')
pos++;
tokens[count++] = make_token(TOK_STRING, substr(source, start, pos));
pos++; // consumir '"'
} else if (c >= '0' && c <= '9') {
// Leer todos los digitos consecutivos
int start = pos;
@@ -137,6 +146,8 @@ Token *tokenize(const char *source, int *token_count) {
tokens[count++] = make_token(TOK_WHILE, word);
} else if (strcmp(word, "if") == 0) {
tokens[count++] = make_token(TOK_IF, word);
} else if (strcmp(word, "println") == 0) {
tokens[count++] = make_token(TOK_PRINTLN, word);
} else {
tokens[count++] = make_token(TOK_ID, word);
}

View File

@@ -13,6 +13,7 @@ typedef enum {
NODE_ASSIGN, // asignacion: x = expr
NODE_BINOP, // operacion binaria: a + b
NODE_PRINT, // print(expr)
NODE_PRINTLN,
NODE_IF, // if cond: bloque
NODE_WHILE, // while cond: bloque
NODE_BLOCK, // secuencia de statements
@@ -64,6 +65,11 @@ ASTNode *parse_term(Token *tokens) {
node->data.int_val = atoi(tokens[pos].value);
pos++;
return node;
} else if (tokens[pos].type == TOK_STRING) {
ASTNode *node = make_node(NODE_STRING_LIT);
node->data.string_val = tokens[pos].value;
pos++;
return node;
} else if (tokens[pos].type == TOK_ID) {
ASTNode *node = make_node(NODE_VAR);
node->data.string_val = tokens[pos].value;
@@ -79,7 +85,7 @@ ASTNode *parse_term(Token *tokens) {
neg->data.binop.right = term;
return neg;
}
printf("ERROR: esperaba INT o ID, encontré tipo %d\n", tokens[pos].type);
printf("ERROR: esperaba INT o ID, encontre tipo %d\n", tokens[pos].type);
exit(1);
}
@@ -123,6 +129,15 @@ ASTNode *parse_statement(Token *tokens) {
return node;
}
if (tokens[pos].type == TOK_PRINTLN) {
pos++; // consumir "println"
ASTNode *expr = parse_expr(tokens);
ASTNode *node = make_node(NODE_PRINTLN);
node->data.print.expr = expr;
return node;
}
if (tokens[pos].type == TOK_WHILE) {
pos++; // consumir while
ASTNode *cond = parse_expr(tokens);
@@ -247,6 +262,11 @@ void ast_print(ASTNode *node, const char *prefix, int is_last) {
ast_print(node->data.print.expr, new_prefix, 1);
break;
case NODE_PRINTLN:
printf("NODE_PRINTLN\n");
ast_print(node->data.print.expr, new_prefix, 1);
break;
case NODE_BLOCK:
printf("NODE_BLOCK\n");
for (int i = 0; i < node->data.block.count; i++) {

View File

@@ -37,7 +37,7 @@ int main(int argc, char **argv) {
eval(block, &env, allocPtr, 0, 1);
printf("heapSize=%zu\n", allocPtr->size);
// JLANG_visualize(allocPtr);
JLANG_visualize(allocPtr);
return 0;
}

View File

@@ -54,6 +54,8 @@ size_t eval(ASTNode *node, Environment *env, void *allocator, int debug,
}
switch (node->type) {
case NODE_STRING_LIT:
return obj_new_string(allocator, node->data.string_val);
case NODE_INT_LIT:
return obj_new_int(allocator, node->data.int_val);
case NODE_VAR:
@@ -73,6 +75,22 @@ size_t eval(ASTNode *node, Environment *env, void *allocator, int debug,
// Operar (ints por ahora)
if (node->data.binop.op == '+') {
if (l->type == OBJ_STRING) {
int n = l->data.string_val.length + r->data.string_val.length;
char *tempBuff = (char *)malloc(n + 1);
// Copy left text
memcpy(tempBuff, JLANG_RESOLVE(allocator, l->data.string_val.chars),
l->data.string_val.length);
// Copy right text
memcpy(tempBuff + l->data.string_val.length, JLANG_RESOLVE(allocator, r->data.string_val.chars),
r->data.string_val.length);
tempBuff[n] = '\0';
size_t newObj = obj_new_string(allocator, tempBuff);
free(tempBuff);
return newObj;
}
return obj_new_int(allocator, l->data.int_val + r->data.int_val);
} else if (node->data.binop.op == '-') {
return obj_new_int(allocator, l->data.int_val - r->data.int_val);
@@ -87,6 +105,11 @@ size_t eval(ASTNode *node, Environment *env, void *allocator, int debug,
}
}
case NODE_PRINT: {
size_t val = eval(node->data.print.expr, env, allocator, debug, gc);
obj_print(allocator, val, "");
return val;
}
case NODE_PRINTLN: {
size_t val = eval(node->data.print.expr, env, allocator, debug, gc);
obj_print(allocator, val, "");
printf("\n");
@@ -113,6 +136,15 @@ size_t eval(ASTNode *node, Environment *env, void *allocator, int debug,
break;
eval(node->data.while_loop.body, env, allocator, debug, gc);
}
return 0;
case NODE_IF: {
size_t cond = eval(node->data.while_loop.cond, env, allocator, debug, gc);
Object *obj = (Object *)JLANG_RESOLVE(allocator, cond);
if (obj->data.int_val > 0) {
eval(node->data.while_loop.body, env, allocator, debug, gc);
}
break;
}
default:
break;
}