ADD: integers.

ADD: more tests.
ADD: static checker.
main
bog 2023-08-23 17:25:28 +02:00
parent 6592f2cfe5
commit 32c0bb8352
20 changed files with 739 additions and 61 deletions

View File

@ -26,6 +26,10 @@ bison_target = custom_target(
bison, '-d', '-o', '@OUTPUT@', '@INPUT@' bison, '-d', '-o', '@OUTPUT@', '@INPUT@'
] ]
) )
cc = meson.get_compiler('c')
m_dep = cc.find_library('m')
executable( executable(
'wuz', 'wuz',
sources: [ sources: [
@ -39,8 +43,12 @@ executable(
'src/value.c', 'src/value.c',
'src/type.c', 'src/type.c',
'src/compiler.c', 'src/compiler.c',
'src/cstatic.c',
'src/vm.c', 'src/vm.c',
], ],
dependencies: [
m_dep
],
install: true install: true
) )

View File

@ -1,6 +1,7 @@
#ifndef COMMONS_H #ifndef COMMONS_H
#define COMMON_H #define COMMON_H
#include <math.h>
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>

View File

@ -26,11 +26,61 @@ void compile_node(compiler* self, node* root, program* prog)
program_add_instr(prog, OP_PUSH, idx); program_add_instr(prog, OP_PUSH, idx);
value_free(&val); value_free(&val);
} }
else if (root->type == NODE_INTEGER)
{
value val;
value_init_integer(&val,
atoi(root->value),
root->lineno);
size_t idx = program_add_pool(prog, &val);
program_add_instr(prog, OP_PUSH, idx);
value_free(&val);
}
else if (root->type == NODE_ASSERT) else if (root->type == NODE_ASSERT)
{ {
compile_children(self, root, prog); compile_children(self, root, prog);
program_add_instr(prog, OP_ASSERT, NO_PARAM); program_add_instr(prog, OP_ASSERT, NO_PARAM);
} }
else if (root->type == NODE_UADD)
{
compile_children(self, root, prog);
program_add_instr(prog, OP_IUADD, NO_PARAM);
}
else if (root->type == NODE_USUB)
{
compile_children(self, root, prog);
program_add_instr(prog, OP_IUSUB, NO_PARAM);
}
else if (root->type == NODE_ADD)
{
compile_children(self, root, prog);
program_add_instr(prog, OP_IADD, NO_PARAM);
}
else if (root->type == NODE_SUB)
{
compile_children(self, root, prog);
program_add_instr(prog, OP_ISUB, NO_PARAM);
}
else if (root->type == NODE_MUL)
{
compile_children(self, root, prog);
program_add_instr(prog, OP_IMUL, NO_PARAM);
}
else if (root->type == NODE_DIV)
{
compile_children(self, root, prog);
program_add_instr(prog, OP_IDIV, NO_PARAM);
}
else if (root->type == NODE_MOD)
{
compile_children(self, root, prog);
program_add_instr(prog, OP_IMOD, NO_PARAM);
}
else if (root->type == NODE_POW)
{
compile_children(self, root, prog);
program_add_instr(prog, OP_IPOW, NO_PARAM);
}
else if (root->type == NODE_EQ) else if (root->type == NODE_EQ)
{ {
compile_children(self, root, prog); compile_children(self, root, prog);

210
src/cstatic.c Normal file
View File

@ -0,0 +1,210 @@
#include "cstatic.h"
#include "type.h"
void cstatic_init(cstatic* self)
{
assert(self);
}
void cstatic_free(cstatic* self)
{
assert(self);
}
int cstatic_resolve(cstatic* self, node* ast)
{
assert(self);
assert(ast);
if (ast->type == NODE_INTEGER)
{
return TY_INTEGER;
}
else if (ast->type == NODE_BOOLEAN
|| ast->type == NODE_EQ
|| ast->type == NODE_NE)
{
return TY_BOOLEAN;
}
else
{
for (size_t i=0; i<ast->children.size; i++)
{
int ty = cstatic_resolve(self, ast->children.data[i]);
if (ty != TY_NIL)
{
return ty;
}
}
}
return TY_NIL;
}
int cstatic_check(cstatic* self, node* ast, char* msg, size_t size)
{
assert(self);
assert(ast);
// Children
for (size_t i=0; i<ast->children.size; i++)
{
int status = cstatic_check(self, ast->children.data[i],
msg, size);
if (!status)
{
return 0;
}
}
// Integer Binops
if (ast->type == NODE_EQ
|| ast->type == NODE_NE)
{
int status = cstatic_check_same_type(self,
ast->children.data[0],
ast->children.data[1],
msg,
size);
if (!status)
{
return status;
}
}
// Boolean Unops
else if (ast->type == NODE_ASSERT
|| ast->type == NODE_NOT)
{
int status = cstatic_check_type(self,
ast->children.data[0],
TY_BOOLEAN,
msg,
size);
if (!status)
{
return status;
}
}
// Boolean Binops
else if (ast->type == NODE_AND
|| ast->type == NODE_OR)
{
int status = cstatic_check_same_type(self,
ast->children.data[0],
ast->children.data[1],
msg,
size);
if (!status)
{
return status;
}
status = cstatic_check_type(self,
ast->children.data[0],
TY_BOOLEAN,
msg,
size);
if (!status)
{
return status;
}
}
// Integer Unops
else if (ast->type == NODE_UADD
|| ast->type == NODE_USUB)
{
int status = cstatic_check_type(self,
ast->children.data[0],
TY_INTEGER,
msg,
size);
if (!status)
{
return status;
}
}
// Integer Binops
else if (ast->type == NODE_ADD
|| ast->type == NODE_SUB
|| ast->type == NODE_MUL
|| ast->type == NODE_DIV
|| ast->type == NODE_MOD
|| ast->type == NODE_POW
)
{
int status = cstatic_check_same_type(self,
ast->children.data[0],
ast->children.data[1],
msg,
size);
if (!status)
{
return status;
}
status = cstatic_check_type(self,
ast->children.data[0],
TY_INTEGER,
msg,
size);
if (!status)
{
return status;
}
}
return 1;
}
int cstatic_check_type(cstatic* self, node* lhs, int rhs,
char* msg, size_t size)
{
assert(self);
assert(lhs);
int left = cstatic_resolve(self, lhs);
if (left != rhs)
{
snprintf(msg, size, "E(%d): expected '%s', got '%s'.",
lhs->lineno,
TypesStr[left],
TypesStr[rhs]);
return 0;
}
return 1;
}
int cstatic_check_same_type(cstatic* self, node* lhs, node* rhs,
char* msg, size_t size)
{
assert(self);
assert(lhs);
assert(rhs);
int left = cstatic_resolve(self, lhs);
int right = cstatic_resolve(self, rhs);
if (left != right)
{
snprintf(msg, size, "E(%d): expected '%s', got '%s'.",
lhs->lineno,
TypesStr[left],
TypesStr[right]);
return 0;
}
return 1;
}

23
src/cstatic.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef CSTATIC_H
#define CSTATIC_H
#include "commons.h"
#include "node.h"
typedef struct {
int _unused;
} cstatic;
void cstatic_init(cstatic* self);
void cstatic_free(cstatic* self);
int cstatic_resolve(cstatic* self, node* ast);
int cstatic_check(cstatic* self, node* ast, char* msg, size_t size);
int cstatic_check_type(cstatic* self, node* lhs, int rhs,
char* msg, size_t size);
int cstatic_check_same_type(cstatic* self, node* lhs, node* rhs,
char* msg, size_t size);
#endif

View File

@ -10,6 +10,7 @@
COMMENT ::[^\n]* COMMENT ::[^\n]*
WHITESPACES [ \t]+ WHITESPACES [ \t]+
BOOLEAN true|false BOOLEAN true|false
INTEGER -?[0-9]+
%% %%
"\n" { line++; } "\n" { line++; }
@ -17,6 +18,13 @@ BOOLEAN true|false
{WHITESPACES} {} {WHITESPACES} {}
"assert" { return ASSERT; } "assert" { return ASSERT; }
"+" { return ADD; }
"-" { return SUB; }
"*" { return MUL; }
"/" { return DIV; }
"%" { return MOD; }
"^" { return POW; }
"==" { return EQ; } "==" { return EQ; }
"!=" { return NE; } "!=" { return NE; }
"&&" { return AND; } "&&" { return AND; }
@ -26,9 +34,16 @@ BOOLEAN true|false
")" { return CPAR; } ")" { return CPAR; }
{BOOLEAN} { {BOOLEAN} {
yylval.boolean = yytext; yylval.str = yytext;
return BOOLEAN; return BOOLEAN;
} }
{INTEGER} {
yylval.str = yytext;
return INTEGER;
}
. { . {
fprintf(stderr, "E(%d): Lexical error near '%s'.\n", line, yytext); fprintf(stderr, "E(%d): Lexical error near '%s'.\n", line, yytext);
exit(-1); exit(-1);

View File

@ -3,6 +3,7 @@
#include "parser.h" #include "parser.h"
#include "compiler.h" #include "compiler.h"
#include "vm.h" #include "vm.h"
#include "cstatic.h"
extern FILE* yyin; extern FILE* yyin;
extern node* ast; extern node* ast;
@ -15,56 +16,77 @@ int main(int argc, char** argv)
if (argc > 1) if (argc > 1)
{ {
yyin = fopen(argv[1], "r"); yyin = fopen(argv[1], "r");
}
node* n = malloc(sizeof(node)); else
node_init(n, NODE_PROG, "", 1); {
stack_push(n); yyin = stdin;
yyparse();
ast = stack_pop();
stack_free();
compiler comp;
compiler_init(&comp);
program prog;
program_init(&prog);
compile_node(&comp, ast, &prog);
vm v;
vm_init(&v);
vm_exec(&v, &prog);
if (0) // DEBUG
{
size_t const BUF = 1024;
char buffer[BUF];
node_str(ast, buffer, BUF);
printf("-- ast ---\n%s\n\n", buffer);
program_str(&prog, buffer, BUF);
printf("--- program ---\n%s\n", buffer);
vm_str(&v, buffer, BUF);
printf("--- stack ---\n%s\n", buffer);
}
vm_free(&v);
program_free(&prog);
compiler_free(&comp);
node_free(ast);
fclose(yyin);
return 0;
} }
fprintf(stderr, "Usage: wuz <filename>\n"); node* n = malloc(sizeof(node));
return -1; node_init(n, NODE_PROG, "", 1);
stack_push(n);
yyparse();
ast = stack_pop();
stack_free();
// Static checking
cstatic cs;
cstatic_init(&cs);
size_t const SZ = 512;
char msg[SZ];
int status = cstatic_check(&cs, ast, msg, SZ);
if (!status)
{
fprintf(stderr, "%s\n", msg);
exit(-1);
}
// Compilation
compiler comp;
compiler_init(&comp);
program prog;
program_init(&prog);
compile_node(&comp, ast, &prog);
// Execution
vm v;
vm_init(&v);
vm_exec(&v, &prog);
if (0) // DEBUG
{
size_t const BUF = 1024;
char buffer[BUF];
node_str(ast, buffer, BUF);
printf("-- ast ---\n%s\n\n", buffer);
program_str(&prog, buffer, BUF);
printf("--- program ---\n%s\n", buffer);
vm_str(&v, buffer, BUF);
printf("--- stack ---\n%s\n", buffer);
}
vm_free(&v);
program_free(&prog);
compiler_free(&comp);
cstatic_free(&cs);
node_free(ast);
if (argc > 1)
{
fclose(yyin);
}
return 0;
} }

View File

@ -3,10 +3,13 @@
#define NODE_TYPE(G) \ #define NODE_TYPE(G) \
G(NODE_PROG), \ G(NODE_PROG), \
G(NODE_BOOLEAN), \ G(NODE_BOOLEAN), G(NODE_INTEGER), \
G(NODE_AND), G(NODE_OR), G(NODE_NOT), \ G(NODE_AND), G(NODE_OR), G(NODE_NOT), \
G(NODE_EQ), G(NODE_NE), \ G(NODE_EQ), G(NODE_NE), \
G(NODE_ASSERT) G(NODE_ASSERT), \
G(NODE_UADD), G(NODE_USUB), G(NODE_ADD), G(NODE_SUB), \
G(NODE_MUL), G(NODE_DIV), G(NODE_MOD), G(NODE_POW)
#include "mutils.h" #include "mutils.h"
#include "commons.h" #include "commons.h"

View File

@ -5,8 +5,19 @@
#define OPCODES(G) \ #define OPCODES(G) \
G(OP_PUSH), \ G(OP_PUSH), \
G(OP_AND), G(OP_OR), G(OP_NOT), \ G(OP_AND), \
G(OP_EQ), G(OP_ASSERT) G(OP_OR), \
G(OP_NOT), \
G(OP_EQ), \
G(OP_ASSERT), \
G(OP_IADD), \
G(OP_ISUB), \
G(OP_IMUL), \
G(OP_IDIV), \
G(OP_IMOD), \
G(OP_IPOW), \
G(OP_IUADD), \
G(OP_IUSUB),
enum Opcodes { enum Opcodes {
OPCODES(GEN_ENUM) OPCODES(GEN_ENUM)

View File

@ -17,24 +17,28 @@
%} %}
%union { %union {
char* boolean; char* str;
void* node_val; void* node_val;
}; };
%left ASSERT %left ASSERT
%token <boolean> BOOLEAN %token <str> BOOLEAN
%token <str> INTEGER
%left EQ NE %left EQ NE
%left AND %left AND
%left OR %left OR
%left ADD SUB
%left MUL DIV MOD
%left POW
%left NOT %left NOT
%type <node_val> expr; %type <node_val> expr;
%token OPAR CPAR %token OPAR CPAR
%% %%
prog: exprs { prog:
| exprs {
} }
; ;
exprs: exprs:
@ -58,6 +62,67 @@ expr:
$$ = n; $$ = n;
} }
| ADD expr {
node *n = malloc(sizeof(node));
node_init(n, NODE_UADD, "", line);
node_add_child(n, $2);
$$ = n;
}
| SUB expr {
node *n = malloc(sizeof(node));
node_init(n, NODE_USUB, "", line);
node_add_child(n, $2);
$$ = n;
}
| expr ADD expr {
node *n = malloc(sizeof(node));
node_init(n, NODE_ADD, "", line);
node_add_child(n, $1);
node_add_child(n, $3);
$$ = n;
}
| expr SUB expr {
node *n = malloc(sizeof(node));
node_init(n, NODE_SUB, "", line);
node_add_child(n, $1);
node_add_child(n, $3);
$$ = n;
}
| expr MUL expr {
node *n = malloc(sizeof(node));
node_init(n, NODE_MUL, "", line);
node_add_child(n, $1);
node_add_child(n, $3);
$$ = n;
}
| expr DIV expr {
node *n = malloc(sizeof(node));
node_init(n, NODE_DIV, "", line);
node_add_child(n, $1);
node_add_child(n, $3);
$$ = n;
}
| expr MOD expr {
node *n = malloc(sizeof(node));
node_init(n, NODE_MOD, "", line);
node_add_child(n, $1);
node_add_child(n, $3);
$$ = n;
}
| expr POW expr {
node *n = malloc(sizeof(node));
node_init(n, NODE_POW, "", line);
node_add_child(n, $1);
node_add_child(n, $3);
$$ = n;
}
| expr EQ expr { | expr EQ expr {
node *n = malloc(sizeof(node)); node *n = malloc(sizeof(node));
@ -108,6 +173,12 @@ expr:
node_init(n, NODE_BOOLEAN, $1, line); node_init(n, NODE_BOOLEAN, $1, line);
$$ = n; $$ = n;
} }
| INTEGER {
node* n = malloc(sizeof(node));
node_init(n, NODE_INTEGER, $1, line);
$$ = n;
}
; ;
%% %%

View File

@ -4,7 +4,9 @@
#include "commons.h" #include "commons.h"
#define TYPES(G) \ #define TYPES(G) \
G(TY_BOOLEAN) G(TY_NIL), \
G(TY_BOOLEAN), \
G(TY_INTEGER),
enum Types { enum Types {
TYPES(GEN_ENUM) TYPES(GEN_ENUM)

View File

@ -10,6 +10,16 @@ void value_init_boolean(value* self, int boolean, int lineno)
type_init(self->type, TY_BOOLEAN); type_init(self->type, TY_BOOLEAN);
} }
void value_init_integer(value* self, int integer, int lineno)
{
assert(self);
self->val.integer = integer;
self->lineno = lineno;
self->type = malloc(sizeof(type));
type_init(self->type, TY_INTEGER);
}
void value_free(value* self) void value_free(value* self)
{ {
assert(self); assert(self);
@ -45,6 +55,11 @@ int value_equals(value* self, value* rhs)
return self->val.boolean == rhs->val.boolean; return self->val.boolean == rhs->val.boolean;
} }
if (self->type->base_type == TY_INTEGER)
{
return self->val.integer == rhs->val.integer;
}
size_t const SZ = 512; size_t const SZ = 512;
char ty_str[SZ]; char ty_str[SZ];
@ -65,6 +80,9 @@ size_t value_str(value* self, char* buffer, size_t size)
return snprintf(buffer, size, "%s", return snprintf(buffer, size, "%s",
self->val.boolean == 0 self->val.boolean == 0
? "false" : "true"); ? "false" : "true");
case TY_INTEGER:
return snprintf(buffer, size, "%d",
self->val.integer);
default: { default: {
fprintf(stderr, "E: unknown value"); fprintf(stderr, "E: unknown value");
exit(-1); exit(-1);

View File

@ -9,10 +9,12 @@ typedef struct {
int lineno; int lineno;
union { union {
int boolean; int boolean;
int integer;
} val; } val;
} value; } value;
void value_init_boolean(value* self, int boolean, int lineno); void value_init_boolean(value* self, int boolean, int lineno);
void value_init_integer(value* self, int integer, int lineno);
void value_free(value* self); void value_free(value* self);
value* value_new_clone(value* self); value* value_new_clone(value* self);

138
src/vm.c
View File

@ -46,6 +46,14 @@ void vm_exec(vm* self, program* prog)
case OP_OR: vm_or(self); break; case OP_OR: vm_or(self); break;
case OP_NOT: vm_not(self); break; case OP_NOT: vm_not(self); break;
case OP_EQ: vm_eq(self); break; case OP_EQ: vm_eq(self); break;
case OP_IADD: vm_iadd(self); break;
case OP_IUADD: vm_iuadd(self); break;
case OP_ISUB: vm_isub(self); break;
case OP_IUSUB: vm_iusub(self); break;
case OP_IMUL: vm_imul(self); break;
case OP_IDIV: vm_idiv(self); break;
case OP_IMOD: vm_imod(self); break;
case OP_IPOW: vm_ipow(self); break;
case OP_ASSERT: vm_assert(self); break; case OP_ASSERT: vm_assert(self); break;
default: { default: {
fprintf(stderr, "unknown opcode %s\n", fprintf(stderr, "unknown opcode %s\n",
@ -165,6 +173,136 @@ void vm_not(vm* self)
self->pc++; self->pc++;
} }
void vm_iadd(vm* self)
{
value* rhs = vm_pop_value(self);
value* lhs = vm_pop_value(self);
value* val = malloc(sizeof(value));
value_init_integer(val, lhs->val.integer + rhs->val.integer, lhs->lineno);
vm_push_value(self, val);
value_free(rhs);
free(rhs);
value_free(lhs);
free(lhs);
self->pc++;
}
void vm_iuadd(vm* self)
{
value* lhs = vm_pop_value(self);
value* val = malloc(sizeof(value));
value_init_integer(val, lhs->val.integer, lhs->lineno);
vm_push_value(self, val);
value_free(lhs);
free(lhs);
self->pc++;
}
void vm_isub(vm* self)
{
value* rhs = vm_pop_value(self);
value* lhs = vm_pop_value(self);
value* val = malloc(sizeof(value));
value_init_integer(val, lhs->val.integer - rhs->val.integer, lhs->lineno);
vm_push_value(self, val);
value_free(rhs);
free(rhs);
value_free(lhs);
free(lhs);
self->pc++;
}
void vm_iusub(vm* self)
{
value* lhs = vm_pop_value(self);
value* val = malloc(sizeof(value));
value_init_integer(val, -lhs->val.integer, lhs->lineno);
vm_push_value(self, val);
value_free(lhs);
free(lhs);
self->pc++;
}
void vm_imul(vm* self)
{
value* rhs = vm_pop_value(self);
value* lhs = vm_pop_value(self);
value* val = malloc(sizeof(value));
value_init_integer(val, lhs->val.integer * rhs->val.integer, lhs->lineno);
vm_push_value(self, val);
value_free(rhs);
free(rhs);
value_free(lhs);
free(lhs);
self->pc++;
}
void vm_idiv(vm* self)
{
value* rhs = vm_pop_value(self);
value* lhs = vm_pop_value(self);
value* val = malloc(sizeof(value));
value_init_integer(val, lhs->val.integer / rhs->val.integer, lhs->lineno);
vm_push_value(self, val);
value_free(rhs);
free(rhs);
value_free(lhs);
free(lhs);
self->pc++;
}
void vm_imod(vm* self)
{
value* rhs = vm_pop_value(self);
value* lhs = vm_pop_value(self);
value* val = malloc(sizeof(value));
value_init_integer(val, lhs->val.integer % rhs->val.integer, lhs->lineno);
vm_push_value(self, val);
value_free(rhs);
free(rhs);
value_free(lhs);
free(lhs);
self->pc++;
}
void vm_ipow(vm* self)
{
value* rhs = vm_pop_value(self);
value* lhs = vm_pop_value(self);
value* val = malloc(sizeof(value));
value_init_integer(val, pow(lhs->val.integer, rhs->val.integer), lhs->lineno);
vm_push_value(self, val);
value_free(rhs);
free(rhs);
value_free(lhs);
free(lhs);
self->pc++;
}
void vm_eq(vm* self) void vm_eq(vm* self)
{ {
assert(self); assert(self);

View File

@ -29,6 +29,16 @@ void vm_and(vm* self);
void vm_or(vm* self); void vm_or(vm* self);
void vm_not(vm* self); void vm_not(vm* self);
void vm_iadd(vm* self);
void vm_iuadd(vm* self);
void vm_isub(vm* self);
void vm_iusub(vm* self);
void vm_imul(vm* self);
void vm_idiv(vm* self);
void vm_imod(vm* self);
void vm_ipow(vm* self);
void vm_eq(vm* self); void vm_eq(vm* self);
void vm_assert(vm* self); void vm_assert(vm* self);

30
tests/err_ty_int_bool.wuz Normal file
View File

@ -0,0 +1,30 @@
+ true
- true
1 + true
1 - true
1 * true
1 / true
1 % true
1 ^ true
true + 1
true - 1
true * 1
true / 1
true % 1
true ^ 1
true + false
true - false
true * false
true / false
true % false
true ^ false
1 && true
2 || false
!5
true && 1
false || -2
!!5

37
tests/errors.sh Executable file
View File

@ -0,0 +1,37 @@
#!/usr/bin/sh
OK=0
KO=0
COUNTER=0
LINE=1
echo -e "\e[34m=== Error Testing ===\e[0m"
for file in $(find . -name "err_*.wuz")
do
LINE=1
while read line;
do
echo $line | wuz &> /dev/null
RET="$?"
if [ $RET -ne 0 ] || [ "$line" == "" ]
then
OK=$(($OK+1))
else
KO=$(($KO+1))
echo -e "\e[33m\tE($file:$LINE) assertion failed\e[0m"
fi
COUNTER=$(($COUNTER + 1))
LINE=$(($LINE + 1))
done < $file
if [ "$KO" == "0" ]
then
echo -e "$file ... \e[32mok\e[0m"
else
echo -e "$file ... \e[31mko\e[0m"
fi
done

View File

@ -3,7 +3,9 @@
OK=0 OK=0
KO=0 KO=0
for file in $(find . -name "*.wuz") echo -e "\e[34m=== Unit Testing ===\e[0m"
for file in $(find . -name "test_*.wuz")
do do
wuz $file wuz $file
RES="$?" RES="$?"
@ -29,3 +31,6 @@ else
echo -e "\e[31m$KO tests failed [$TOTAL]\e[0m" echo -e "\e[31m$KO tests failed [$TOTAL]\e[0m"
fi fi
echo
./errors.sh

22
tests/test_integers.wuz Normal file
View File

@ -0,0 +1,22 @@
assert 1 == 1
assert 2 != 3
assert -1 == 2 + -3
assert -8 == -(3 + 1) * 2
assert 7 == 1 + 2 * 3
assert 9 == (1 + 2) * 3
assert 8 == 2^3
assert -8 == -2^3
assert 1 == 5 % 2
assert 2 == 5 / 2
assert 3 == 6 / 2
assert 64 == 2^(3+3)
assert 1 == -2 - -3
assert 5 + 7 == 12