Compare commits
3 Commits
bd84f3bb3d
...
4f6f3253c2
Author | SHA1 | Date |
---|---|---|
bog | 4f6f3253c2 | |
bog | 29c363765f | |
bog | 975a089dbd |
12
doc/roza.bnf
12
doc/roza.bnf
|
@ -4,7 +4,13 @@ EXPR ::=
|
||||||
| EQNE
|
| EQNE
|
||||||
ASSERT ::= assert EXPR
|
ASSERT ::= assert EXPR
|
||||||
EQNE ::=
|
EQNE ::=
|
||||||
| BUILTIN
|
| CMP
|
||||||
| BUILTIN eq BUILTIN
|
| CMP eq CMP
|
||||||
| BUILTIN ne BUILTIN
|
| CMP ne CMP
|
||||||
|
CMP ::= TERM ((lt | le | ge | gt) TERM)?
|
||||||
|
TERM ::= FACTOR ((add | sub) FACTOR)*
|
||||||
|
FACTOR ::= POWER ((mul | div | mod) POWER)*
|
||||||
|
POWER ::= UNARY (pow UNARY)?
|
||||||
|
UNARY ::= sub? GROUP
|
||||||
|
GROUP ::= BUILTIN | opar EXPR cpar
|
||||||
BUILTIN ::= num | bool | str
|
BUILTIN ::= num | bool | str
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef RZ_COMMONS_H
|
#ifndef RZ_COMMONS_H
|
||||||
#define RZ_COMMONS_H
|
#define RZ_COMMONS_H
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
|
@ -77,6 +77,67 @@ void compiler_run(compiler_t* compiler, node_t* node)
|
||||||
node->type == NODE_EQ ? OP_EQ : OP_NE, RZ_NO_PARAM);
|
node->type == NODE_EQ ? OP_EQ : OP_NE, RZ_NO_PARAM);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case NODE_LT:
|
||||||
|
case NODE_LE:
|
||||||
|
case NODE_GT:
|
||||||
|
case NODE_GE: {
|
||||||
|
assert(node->children.size == 2);
|
||||||
|
|
||||||
|
for (size_t i=0; i<node->children.size; i++)
|
||||||
|
{
|
||||||
|
compiler_run(compiler, node_child(node, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
Opcode op;
|
||||||
|
switch (node->type)
|
||||||
|
{
|
||||||
|
case NODE_LT: op = OP_LT; break;
|
||||||
|
case NODE_LE: op = OP_LE; break;
|
||||||
|
case NODE_GT: op = OP_GT; break;
|
||||||
|
case NODE_GE: op = OP_GE; break;
|
||||||
|
default: assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod_push_instr(compiler->mod, op, RZ_NO_PARAM);
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_ADD:
|
||||||
|
case NODE_SUB:
|
||||||
|
case NODE_MUL:
|
||||||
|
case NODE_DIV:
|
||||||
|
case NODE_MODULO:
|
||||||
|
case NODE_POW: {
|
||||||
|
for (size_t i=0; i<node->children.size; i++)
|
||||||
|
{
|
||||||
|
compiler_run(compiler, node_child(node, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
Opcode op;
|
||||||
|
|
||||||
|
if (node->type == NODE_SUB
|
||||||
|
&& node->children.size == 1)
|
||||||
|
{
|
||||||
|
op = OP_USUB;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (node->type)
|
||||||
|
{
|
||||||
|
case NODE_ADD: op = OP_ADD; break;
|
||||||
|
case NODE_SUB: op = OP_SUB; break;
|
||||||
|
case NODE_MUL: op = OP_MUL; break;
|
||||||
|
case NODE_DIV: op = OP_DIV; break;
|
||||||
|
case NODE_MODULO: op = OP_MODULO; break;
|
||||||
|
case NODE_POW: op = OP_POW; break;
|
||||||
|
default: assert(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod_push_instr(compiler->mod, op, RZ_NO_PARAM);
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
fprintf(stderr, "Cannot compile unknown node '%s'",
|
fprintf(stderr, "Cannot compile unknown node '%s'",
|
||||||
NodeTypeStr[node->type]);
|
NodeTypeStr[node->type]);
|
||||||
|
|
50
lib/lexer.c
50
lib/lexer.c
|
@ -36,10 +36,37 @@ node_t* lexer_try_new_next(lexer_t* lexer)
|
||||||
|
|
||||||
lexer_skip_spaces(lexer);
|
lexer_skip_spaces(lexer);
|
||||||
|
|
||||||
|
// Comments
|
||||||
|
// ========
|
||||||
|
while (lexer->cursor < len
|
||||||
|
&& lexer->source[lexer->cursor] == '#')
|
||||||
|
{
|
||||||
|
while (lexer->cursor < len
|
||||||
|
&& lexer->source[lexer->cursor] != '\n')
|
||||||
|
{
|
||||||
|
lexer->cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
lexer_skip_spaces(lexer);
|
||||||
|
}
|
||||||
|
|
||||||
// Text
|
// Text
|
||||||
// ====
|
// ====
|
||||||
|
RZ_TEXT("(", NODE_OPAR, 0);
|
||||||
|
RZ_TEXT(")", NODE_CPAR, 0);
|
||||||
|
|
||||||
|
RZ_TEXT("+", NODE_ADD, 0);
|
||||||
|
RZ_TEXT("*", NODE_MUL, 0);
|
||||||
|
RZ_TEXT("/", NODE_DIV, 0);
|
||||||
|
RZ_TEXT("%", NODE_MODULO, 0);
|
||||||
|
RZ_TEXT("^", NODE_POW, 0);
|
||||||
|
|
||||||
RZ_TEXT("==", NODE_EQ, 0);
|
RZ_TEXT("==", NODE_EQ, 0);
|
||||||
RZ_TEXT("!=", NODE_NE, 0);
|
RZ_TEXT("!=", NODE_NE, 0);
|
||||||
|
RZ_TEXT("<=", NODE_LE, 0);
|
||||||
|
RZ_TEXT(">=", NODE_GE, 0);
|
||||||
|
RZ_TEXT("<", NODE_LT, 0);
|
||||||
|
RZ_TEXT(">", NODE_GT, 0);
|
||||||
|
|
||||||
// Keywords
|
// Keywords
|
||||||
// ========
|
// ========
|
||||||
|
@ -92,7 +119,9 @@ node_t* lexer_try_new_next(lexer_t* lexer)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res_str.size > 0
|
if (res_str.size > 0
|
||||||
&& (cursor >= len || isspace(lexer->source[cursor])))
|
&& res_str.data[res_str.size - 1] != '-'
|
||||||
|
&& (cursor >= len || !(isalnum(lexer->source[cursor])
|
||||||
|
|| lexer->source[cursor] == '.')))
|
||||||
{
|
{
|
||||||
node_t* tok = malloc(sizeof(node_t));
|
node_t* tok = malloc(sizeof(node_t));
|
||||||
node_init(tok, NODE_NUM, res_str.data, lexer->line);
|
node_init(tok, NODE_NUM, res_str.data, lexer->line);
|
||||||
|
@ -107,6 +136,8 @@ node_t* lexer_try_new_next(lexer_t* lexer)
|
||||||
str_free(&res_str);
|
str_free(&res_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RZ_TEXT("-", NODE_SUB, 0);
|
||||||
|
|
||||||
if (lexer->cursor < len && lexer->err)
|
if (lexer->cursor < len && lexer->err)
|
||||||
{
|
{
|
||||||
size_t const SZ = RZ_STR_LIMIT;
|
size_t const SZ = RZ_STR_LIMIT;
|
||||||
|
@ -166,6 +197,14 @@ void lexer_skip_spaces(lexer_t* lexer)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lexer_skip_next(lexer_t* lexer)
|
||||||
|
{
|
||||||
|
assert(lexer);
|
||||||
|
node_t* node = lexer_try_new_next(lexer);
|
||||||
|
node_free(node);
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
|
||||||
node_t* lexer_try_new_keyword(lexer_t* lexer, char* kw,
|
node_t* lexer_try_new_keyword(lexer_t* lexer, char* kw,
|
||||||
NodeType type, int has_value,
|
NodeType type, int has_value,
|
||||||
int is_kw)
|
int is_kw)
|
||||||
|
@ -301,6 +340,13 @@ int lexer_is_sep(lexer_t* lexer, size_t idx)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return c == '='
|
return
|
||||||
|
c == '='
|
||||||
|
|| c == '+'
|
||||||
|
|| c == '-'
|
||||||
|
|| c == '*'
|
||||||
|
|| c == '/'
|
||||||
|
|| c == '%'
|
||||||
|
|| c == '^'
|
||||||
|| c == '!';
|
|| c == '!';
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ void lexer_free(lexer_t* lexer);
|
||||||
node_t* lexer_try_new_next(lexer_t* lexer);
|
node_t* lexer_try_new_next(lexer_t* lexer);
|
||||||
NodeType lexer_peek(lexer_t* lexer, int lookahead);
|
NodeType lexer_peek(lexer_t* lexer, int lookahead);
|
||||||
void lexer_skip_spaces(lexer_t* lexer);
|
void lexer_skip_spaces(lexer_t* lexer);
|
||||||
|
void lexer_skip_next(lexer_t* lexer);
|
||||||
|
|
||||||
node_t* lexer_try_new_keyword(lexer_t* lexer, char* kw,
|
node_t* lexer_try_new_keyword(lexer_t* lexer, char* kw,
|
||||||
NodeType type, int has_value,
|
NodeType type, int has_value,
|
||||||
|
|
13
lib/node.h
13
lib/node.h
|
@ -3,11 +3,14 @@
|
||||||
|
|
||||||
#include "commons.h"
|
#include "commons.h"
|
||||||
|
|
||||||
#define NODE_TYPE(G) \
|
#define NODE_TYPE(G) \
|
||||||
G(NODE_MOD), \
|
G(NODE_MOD), \
|
||||||
G(NODE_NUM), G(NODE_BOOL), G(NODE_STR), \
|
G(NODE_NUM), G(NODE_BOOL), G(NODE_STR), \
|
||||||
G(NODE_ASSERT), \
|
G(NODE_ASSERT), \
|
||||||
G(NODE_EQ), G(NODE_NE)
|
G(NODE_EQ), G(NODE_NE), \
|
||||||
|
G(NODE_LT), G(NODE_LE), G(NODE_GT), G(NODE_GE), \
|
||||||
|
G(NODE_ADD), G(NODE_SUB), G(NODE_MUL), G(NODE_DIV), \
|
||||||
|
G(NODE_MODULO), G(NODE_POW), G(NODE_OPAR), G(NODE_CPAR)
|
||||||
|
|
||||||
RZ_ENUM_H(NodeType, NODE_TYPE);
|
RZ_ENUM_H(NodeType, NODE_TYPE);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,10 @@
|
||||||
#define OPCODES(G) \
|
#define OPCODES(G) \
|
||||||
G(OP_ASSERT), \
|
G(OP_ASSERT), \
|
||||||
G(OP_PUSH), G(OP_POP), \
|
G(OP_PUSH), G(OP_POP), \
|
||||||
G(OP_EQ), G(OP_NE)
|
G(OP_EQ), G(OP_NE), \
|
||||||
|
G(OP_LT), G(OP_LE), G(OP_GT), G(OP_GE), \
|
||||||
|
G(OP_ADD), G(OP_SUB), G(OP_MUL), G(OP_DIV), \
|
||||||
|
G(OP_MODULO), G(OP_POW), G(OP_USUB)
|
||||||
|
|
||||||
RZ_ENUM_H(Opcode, OPCODES);
|
RZ_ENUM_H(Opcode, OPCODES);
|
||||||
|
|
||||||
|
|
130
lib/parser.c
130
lib/parser.c
|
@ -80,7 +80,7 @@ node_t* parser_try_new_assert(parser_t* parser)
|
||||||
node_t* parser_try_new_eqne(parser_t* parser)
|
node_t* parser_try_new_eqne(parser_t* parser)
|
||||||
{
|
{
|
||||||
assert(parser);
|
assert(parser);
|
||||||
node_t* lhs = parser_try_new_builtin(parser);
|
node_t* lhs = parser_try_new_cmp(parser);
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
|
@ -91,7 +91,7 @@ node_t* parser_try_new_eqne(parser_t* parser)
|
||||||
node_t* node = lexer_try_new_next(parser->lexer);
|
node_t* node = lexer_try_new_next(parser->lexer);
|
||||||
|
|
||||||
node_add_new_child(node, lhs);
|
node_add_new_child(node, lhs);
|
||||||
node_add_new_child(node, parser_try_new_builtin(parser));
|
node_add_new_child(node, parser_try_new_cmp(parser));
|
||||||
|
|
||||||
lhs = node;
|
lhs = node;
|
||||||
}
|
}
|
||||||
|
@ -104,6 +104,132 @@ node_t* parser_try_new_eqne(parser_t* parser)
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node_t* parser_try_new_cmp(parser_t* parser)
|
||||||
|
{
|
||||||
|
assert(parser);
|
||||||
|
node_t* lhs = parser_try_new_term(parser);
|
||||||
|
|
||||||
|
int next = lexer_peek(parser->lexer, 1);
|
||||||
|
|
||||||
|
if (next == NODE_LT
|
||||||
|
|| next == NODE_GT
|
||||||
|
|| next == NODE_LE
|
||||||
|
|| next == NODE_GE)
|
||||||
|
{
|
||||||
|
node_t* node = parser_try_new_consume(parser, next);
|
||||||
|
node_add_new_child(node, lhs);
|
||||||
|
node_add_new_child(node, parser_try_new_term(parser));
|
||||||
|
lhs = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
node_t* parser_try_new_term(parser_t* parser)
|
||||||
|
{
|
||||||
|
assert(parser);
|
||||||
|
node_t* lhs = parser_try_new_factor(parser);
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
NodeType next = lexer_peek(parser->lexer, 1);
|
||||||
|
|
||||||
|
if (next == NODE_ADD
|
||||||
|
|| next == NODE_SUB)
|
||||||
|
{
|
||||||
|
node_t* node = parser_try_new_consume(parser, next);
|
||||||
|
node_add_new_child(node, lhs);
|
||||||
|
node_add_new_child(node, parser_try_new_factor(parser));
|
||||||
|
|
||||||
|
lhs = node;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
node_t* parser_try_new_factor(parser_t* parser)
|
||||||
|
{
|
||||||
|
assert(parser);
|
||||||
|
node_t* lhs = parser_try_new_power(parser);
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
NodeType next = lexer_peek(parser->lexer, 1);
|
||||||
|
|
||||||
|
if (next == NODE_MUL
|
||||||
|
|| next == NODE_DIV
|
||||||
|
|| next == NODE_MODULO)
|
||||||
|
{
|
||||||
|
node_t* node = parser_try_new_consume(parser, next);
|
||||||
|
node_add_new_child(node, lhs);
|
||||||
|
node_add_new_child(node, parser_try_new_power(parser));
|
||||||
|
|
||||||
|
lhs = node;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
node_t* parser_try_new_power(parser_t* parser)
|
||||||
|
{
|
||||||
|
assert(parser);
|
||||||
|
node_t* lhs = parser_try_new_unary(parser);
|
||||||
|
|
||||||
|
NodeType next = lexer_peek(parser->lexer, 1);
|
||||||
|
|
||||||
|
if (next == NODE_POW)
|
||||||
|
{
|
||||||
|
node_t* node = parser_try_new_consume(parser, next);
|
||||||
|
node_add_new_child(node, lhs);
|
||||||
|
node_add_new_child(node, parser_try_new_unary(parser));
|
||||||
|
lhs = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
node_t* parser_try_new_unary(parser_t* parser)
|
||||||
|
{
|
||||||
|
assert(parser);
|
||||||
|
NodeType next = lexer_peek(parser->lexer, 1);
|
||||||
|
|
||||||
|
if (next == NODE_SUB)
|
||||||
|
{
|
||||||
|
node_t* node = parser_try_new_consume(parser, next);
|
||||||
|
node_add_new_child(node, parser_try_new_group(parser));
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parser_try_new_group(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
node_t* parser_try_new_group(parser_t* parser)
|
||||||
|
{
|
||||||
|
assert(parser);
|
||||||
|
NodeType next = lexer_peek(parser->lexer, 1);
|
||||||
|
|
||||||
|
if (next == NODE_OPAR)
|
||||||
|
{
|
||||||
|
lexer_skip_next(parser->lexer);
|
||||||
|
node_t* node = parser_try_new_expr(parser);
|
||||||
|
lexer_skip_next(parser->lexer);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parser_try_new_builtin(parser);
|
||||||
|
}
|
||||||
|
|
||||||
node_t* parser_try_new_builtin(parser_t* parser)
|
node_t* parser_try_new_builtin(parser_t* parser)
|
||||||
{
|
{
|
||||||
assert(parser);
|
assert(parser);
|
||||||
|
|
|
@ -18,6 +18,12 @@ node_t* parser_try_new_mod(parser_t* parser);
|
||||||
node_t* parser_try_new_expr(parser_t* parser);
|
node_t* parser_try_new_expr(parser_t* parser);
|
||||||
node_t* parser_try_new_assert(parser_t* parser);
|
node_t* parser_try_new_assert(parser_t* parser);
|
||||||
node_t* parser_try_new_eqne(parser_t* parser);
|
node_t* parser_try_new_eqne(parser_t* parser);
|
||||||
|
node_t* parser_try_new_cmp(parser_t* parser);
|
||||||
|
node_t* parser_try_new_term(parser_t* parser);
|
||||||
|
node_t* parser_try_new_factor(parser_t* parser);
|
||||||
|
node_t* parser_try_new_power(parser_t* parser);
|
||||||
|
node_t* parser_try_new_unary(parser_t* parser);
|
||||||
|
node_t* parser_try_new_group(parser_t* parser);
|
||||||
node_t* parser_try_new_builtin(parser_t* parser);
|
node_t* parser_try_new_builtin(parser_t* parser);
|
||||||
|
|
||||||
node_t* parser_try_new_consume(parser_t* parser, NodeType type);
|
node_t* parser_try_new_consume(parser_t* parser, NodeType type);
|
||||||
|
|
|
@ -26,7 +26,18 @@ void prepass_run(prepass_t* prepass, node_t* node)
|
||||||
case NODE_BOOL:
|
case NODE_BOOL:
|
||||||
case NODE_ASSERT:
|
case NODE_ASSERT:
|
||||||
case NODE_EQ:
|
case NODE_EQ:
|
||||||
case NODE_NE: {
|
case NODE_NE:
|
||||||
|
case NODE_LT:
|
||||||
|
case NODE_LE:
|
||||||
|
case NODE_GT:
|
||||||
|
case NODE_GE:
|
||||||
|
|
||||||
|
case NODE_ADD:
|
||||||
|
case NODE_SUB:
|
||||||
|
case NODE_MUL:
|
||||||
|
case NODE_DIV:
|
||||||
|
case NODE_MODULO:
|
||||||
|
case NODE_POW:{
|
||||||
for (size_t i=0; i<node->children.size; i++)
|
for (size_t i=0; i<node->children.size; i++)
|
||||||
{
|
{
|
||||||
prepass_run(prepass, (node_t*) node->children.data[i]);
|
prepass_run(prepass, (node_t*) node->children.data[i]);
|
||||||
|
|
106
lib/vm.c
106
lib/vm.c
|
@ -2,6 +2,7 @@
|
||||||
#include "lib/commons.h"
|
#include "lib/commons.h"
|
||||||
#include "lib/mod.h"
|
#include "lib/mod.h"
|
||||||
#include "lib/type.h"
|
#include "lib/type.h"
|
||||||
|
#include "lib/tysy.h"
|
||||||
|
|
||||||
void vm_init(vm_t* vm, tysy_t* tysy, err_t* err)
|
void vm_init(vm_t* vm, tysy_t* tysy, err_t* err)
|
||||||
{
|
{
|
||||||
|
@ -109,12 +110,7 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param)
|
||||||
|
|
||||||
if (value->type->kind != TYPE_BOOL)
|
if (value->type->kind != TYPE_BOOL)
|
||||||
{
|
{
|
||||||
char msg[RZ_STR_LIMIT];
|
vm_ensure_type(vm, value, TYPE_BOOL);
|
||||||
snprintf(msg, RZ_STR_LIMIT,
|
|
||||||
"type mismatch: BOOL expected, got %s.",
|
|
||||||
TypeKindStr[value->type->kind] + strlen("TYPE_"));
|
|
||||||
|
|
||||||
err_fatal(vm->err, msg, value->line);
|
|
||||||
|
|
||||||
vm->pc++;
|
vm->pc++;
|
||||||
break;
|
break;
|
||||||
|
@ -152,6 +148,85 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param)
|
||||||
|
|
||||||
vm_push_value(vm, mod, res);
|
vm_push_value(vm, mod, res);
|
||||||
|
|
||||||
|
vm->pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_LT:
|
||||||
|
case OP_LE:
|
||||||
|
case OP_GT:
|
||||||
|
case OP_GE: {
|
||||||
|
int r = vm_pop(vm);
|
||||||
|
int l = vm_pop(vm);
|
||||||
|
|
||||||
|
value_t* lhs = mod->values.data[l];
|
||||||
|
value_t* rhs = mod->values.data[r];
|
||||||
|
|
||||||
|
vm_ensure_type(vm, lhs, TYPE_NUM);
|
||||||
|
vm_ensure_type(vm, rhs, TYPE_NUM);
|
||||||
|
|
||||||
|
int val = 0;
|
||||||
|
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case OP_LT: val = lhs->value.num < rhs->value.num; break;
|
||||||
|
case OP_LE: val = lhs->value.num <= rhs->value.num; break;
|
||||||
|
case OP_GT: val = lhs->value.num > rhs->value.num; break;
|
||||||
|
case OP_GE: val = lhs->value.num >= rhs->value.num; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
value_t* res = tysy_new_bool(vm->tysy, val, lhs->line);
|
||||||
|
vm_push_value(vm, mod, res);
|
||||||
|
|
||||||
|
vm->pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_ADD:
|
||||||
|
case OP_SUB:
|
||||||
|
case OP_MUL:
|
||||||
|
case OP_DIV:
|
||||||
|
case OP_MODULO:
|
||||||
|
case OP_POW: {
|
||||||
|
int r = vm_pop(vm);
|
||||||
|
int l = vm_pop(vm);
|
||||||
|
|
||||||
|
value_t* lhs = mod->values.data[l];
|
||||||
|
value_t* rhs = mod->values.data[r];
|
||||||
|
|
||||||
|
vm_ensure_type(vm, lhs, TYPE_NUM);
|
||||||
|
vm_ensure_type(vm, rhs, TYPE_NUM);
|
||||||
|
|
||||||
|
double val = 0;
|
||||||
|
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case OP_ADD: val = lhs->value.num + rhs->value.num; break;
|
||||||
|
case OP_SUB: val = lhs->value.num - rhs->value.num; break;
|
||||||
|
case OP_MUL: val = lhs->value.num * rhs->value.num; break;
|
||||||
|
case OP_DIV: val = lhs->value.num / rhs->value.num; break;
|
||||||
|
case OP_MODULO: val = fmod(lhs->value.num, rhs->value.num); break;
|
||||||
|
case OP_POW: val = powf(lhs->value.num, rhs->value.num); break;
|
||||||
|
default: assert(0); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
value_t* res = tysy_new_num(vm->tysy, val, lhs->line);
|
||||||
|
vm_push_value(vm, mod, res);
|
||||||
|
|
||||||
|
vm->pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_USUB: {
|
||||||
|
int l = vm_pop(vm);
|
||||||
|
|
||||||
|
value_t* lhs = mod->values.data[l];
|
||||||
|
|
||||||
|
vm_ensure_type(vm, lhs, TYPE_NUM);
|
||||||
|
|
||||||
|
double val = -lhs->value.num;
|
||||||
|
|
||||||
|
value_t* res = tysy_new_num(vm->tysy, val, lhs->line);
|
||||||
|
vm_push_value(vm, mod, res);
|
||||||
|
|
||||||
vm->pc++;
|
vm->pc++;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
@ -164,3 +239,22 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void vm_ensure_type(vm_t* vm, value_t* value, TypeKind want)
|
||||||
|
{
|
||||||
|
assert(vm);
|
||||||
|
assert(value);
|
||||||
|
TypeKind got = value->type->kind;
|
||||||
|
int line = value->line;
|
||||||
|
|
||||||
|
if (got != want)
|
||||||
|
{
|
||||||
|
char msg[RZ_STR_LIMIT];
|
||||||
|
snprintf(msg, RZ_STR_LIMIT,
|
||||||
|
"type mismatch: %s expected, got %s.",
|
||||||
|
TypeKindStr[want] + strlen("TYPE_"),
|
||||||
|
TypeKindStr[got] + strlen("TYPE_"));
|
||||||
|
|
||||||
|
err_fatal(vm->err, msg, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
1
lib/vm.h
1
lib/vm.h
|
@ -28,5 +28,6 @@ size_t vm_stack_str(vm_t* vm, char* buffer, size_t size);
|
||||||
|
|
||||||
int vm_exec_mod(vm_t* vm, mod_t* mod);
|
int vm_exec_mod(vm_t* vm, mod_t* mod);
|
||||||
int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param);
|
int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param);
|
||||||
|
void vm_ensure_type(vm_t* vm, value_t* value, TypeKind want);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -4,6 +4,9 @@ project(
|
||||||
version: '0.0.0'
|
version: '0.0.0'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc = meson.get_compiler('c')
|
||||||
|
m_dep = cc.find_library('m', required: true)
|
||||||
|
|
||||||
conf = configuration_data()
|
conf = configuration_data()
|
||||||
conf.set('version', meson.project_version())
|
conf.set('version', meson.project_version())
|
||||||
|
|
||||||
|
@ -39,6 +42,10 @@ roza_lib = static_library(
|
||||||
|
|
||||||
# exec
|
# exec
|
||||||
'lib/vm.c',
|
'lib/vm.c',
|
||||||
|
],
|
||||||
|
|
||||||
|
dependencies: [
|
||||||
|
m_dep
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,34 @@
|
||||||
|
# Equality
|
||||||
|
# ========
|
||||||
|
# for num
|
||||||
assert 5 == 5
|
assert 5 == 5
|
||||||
assert 3 != 5
|
assert 3 != 5
|
||||||
|
|
||||||
|
# for bool
|
||||||
assert true == true
|
assert true == true
|
||||||
assert false == false
|
assert false == false
|
||||||
assert false != true
|
assert false != true
|
||||||
|
|
||||||
|
# for str
|
||||||
assert "hello" == "hello"
|
assert "hello" == "hello"
|
||||||
assert "world" != "hello"
|
assert "world" != "hello"
|
||||||
|
|
||||||
|
# different types
|
||||||
assert 7 != "hello"
|
assert 7 != "hello"
|
||||||
assert false != 23
|
assert false != 23
|
||||||
assert "bim" != true
|
assert "bim" != true
|
||||||
|
|
||||||
|
# binary comparison operators
|
||||||
|
assert 3 < 5 == true
|
||||||
|
assert 5 < 5 == false
|
||||||
|
assert 7 < 5 == false
|
||||||
|
assert 3 <= 5 == true
|
||||||
|
assert 5 <= 5 == true
|
||||||
|
assert 7 <= 5 == false
|
||||||
|
|
||||||
|
assert 3 > 5 == false
|
||||||
|
assert 5 > 5 == false
|
||||||
|
assert 7 > 5 == true
|
||||||
|
assert 3 >= 5 == false
|
||||||
|
assert 5 >= 5 == true
|
||||||
|
assert 7 >= 5 == true
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Num Arithmetic
|
||||||
|
# ==============
|
||||||
|
assert 5 + 2 == 7
|
||||||
|
assert 5 - 2 == 3
|
||||||
|
assert 2 - 5 == -3
|
||||||
|
assert - 32 == -32
|
||||||
|
assert 6 * 7 == 42
|
||||||
|
assert 12 / 4 == 3
|
||||||
|
assert 5 / 2 == 2.5
|
||||||
|
assert 35 % 8 == 3
|
||||||
|
assert 2^6 == 64
|
||||||
|
|
||||||
|
assert 1 + 2 * 3 == 7
|
||||||
|
assert (1 + 2) * 3 == 9
|
||||||
|
assert 5 == (3 + 2)
|
|
@ -77,7 +77,7 @@ Test(lexer, num) {
|
||||||
"NUM[4.1]",
|
"NUM[4.1]",
|
||||||
"NUM[6]");
|
"NUM[6]");
|
||||||
|
|
||||||
test_lexer_ko("-3..14");
|
test_lexer_ko("3..14");
|
||||||
test_lexer_ko("..2");
|
test_lexer_ko("..2");
|
||||||
test_lexer_ko("2..");
|
test_lexer_ko("2..");
|
||||||
}
|
}
|
||||||
|
@ -124,4 +124,25 @@ Test(lexer, cmp) {
|
||||||
"BOOL[false]",
|
"BOOL[false]",
|
||||||
"NE",
|
"NE",
|
||||||
"BOOL[true]");
|
"BOOL[true]");
|
||||||
|
|
||||||
|
test_lexer("1< <= > >=5", 6,
|
||||||
|
"NUM[1]",
|
||||||
|
"LT",
|
||||||
|
"LE",
|
||||||
|
"GT",
|
||||||
|
"GE",
|
||||||
|
"NUM[5]");
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(lexer, num_arith) {
|
||||||
|
test_lexer("()+-*/^%", 8,
|
||||||
|
"OPAR",
|
||||||
|
"CPAR",
|
||||||
|
"ADD",
|
||||||
|
"SUB",
|
||||||
|
"MUL",
|
||||||
|
"DIV",
|
||||||
|
"POW",
|
||||||
|
"MODULO");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,3 +76,18 @@ Test(parser, eqne) {
|
||||||
test_parser_ok("MOD(EQ(NUM[3],NUM[3]))", " 3 == 3 ");
|
test_parser_ok("MOD(EQ(NUM[3],NUM[3]))", " 3 == 3 ");
|
||||||
test_parser_ok("MOD(NE(NUM[3],NUM[3]))", " 3 != 3 ");
|
test_parser_ok("MOD(NE(NUM[3],NUM[3]))", " 3 != 3 ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Test(parser, cmp) {
|
||||||
|
test_parser_ok("MOD(LT(NUM[3],NUM[3]))", " 3 < 3 ");
|
||||||
|
test_parser_ok("MOD(LE(NUM[3],NUM[3]))", " 3 <= 3 ");
|
||||||
|
test_parser_ok("MOD(GT(NUM[3],NUM[3]))", " 3 > 3 ");
|
||||||
|
test_parser_ok("MOD(GE(NUM[3],NUM[3]))", " 3 >= 3 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(parser, num_arith) {
|
||||||
|
test_parser_ok("MOD(ADD(NUM[1],MUL(NUM[2],NUM[3])))", " 1 + 2 * 3 ");
|
||||||
|
test_parser_ok("MOD(MUL(ADD(NUM[1],NUM[2]),NUM[3]))", " (1 + 2) * 3 ");
|
||||||
|
test_parser_ok("MOD(SUB(NUM[1],DIV(NUM[2],NUM[3])))", " 1 - 2 / 3 ");
|
||||||
|
test_parser_ok("MOD(DIV(SUB(NUM[1],NUM[2]),NUM[3]))", " (1 - 2) / 3 ");
|
||||||
|
test_parser_ok("MOD(POW(NUM[2],ADD(NUM[1],NUM[2])))", " 2^(1+2) ");
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue