Skip to content

Commit 5b80bb1

Browse files
committed
initial support for custom operators
1 parent 69d4f1f commit 5b80bb1

File tree

5 files changed

+115
-33
lines changed

5 files changed

+115
-33
lines changed

editors/vscode/syntaxes/cspydr.tmLanguage.json

+19-1
Original file line numberDiff line numberDiff line change
@@ -392,12 +392,30 @@
392392
}
393393
}
394394
},
395+
{
396+
"name": "meta.function-operator.declaration.cspydr",
397+
"match": "(?:\\bextern\\s+)?(\\boperator)\\s+((?:\\+|\\-|\\*|/|%|=|&|<|>|\\^|\\||!|\\?|\\.|:|~)*)",
398+
"captures": {
399+
"1": {
400+
"name": "keyword.operator.cspydr"
401+
},
402+
"2": {
403+
"name": "entity.name.function.cspydr"
404+
},
405+
"3": {
406+
"name": "punctuation.begin.bracket"
407+
}
408+
}
409+
},
395410
{
396411
"name": "meta.function.argument-list.cspydr",
397-
"begin": "(?<=fn.*?)(\\()",
412+
"begin": "(?<=(fn|operator).*?)(\\()",
398413
"beginCaptures": {
399414
"1": {
400415
"name": "punctuation.begin.bracket.cspydr"
416+
},
417+
"2": {
418+
"name": "punctuation.begin.bracket"
401419
}
402420
},
403421
"end": "(\\))",

src/api/include/cspydr.h

+1
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ enum CSPYDR_TOKEN_TYPE {
160160
CSPYDR_TOKEN(USING), // using
161161
CSPYDR_TOKEN(WHILE), // while
162162
CSPYDR_TOKEN(WITH), // with
163+
CSPYDR_TOKEN(OPERATOR_KW), // operator keyword
163164

164165
CSPYDR_TOKEN(CURRENT_FN), // special token for the __func__! macro
165166

src/compiler/codegen/codegen_utils.c

+6-12
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,19 @@
99
#include "timer/timer.h"
1010

1111
#include <libgen.h>
12+
#include <ctype.h>
13+
#include <limits.h>
1214
#include <string.h>
1315
#include <glob.h>
1416
#include <libgen.h>
1517

1618
static void escape_callee(char* dst, ASTIdentifier_T* id)
1719
{
1820
for(char* c = id->callee; *c; c++)
19-
switch(*c) {
20-
case '?':
21-
strcat(dst, "$qmark");
22-
break;
23-
case '\'':
24-
strcat(dst, "$quote");
25-
break;
26-
default: {
27-
char buf[2] = {*c, '\0'};
28-
strcat(dst, buf);
29-
} break;
30-
}
21+
if(!isalnum(*c) && *c != '_')
22+
snprintf(dst + strlen(dst), 4, "$%02x", (int) *c);
23+
else
24+
dst[strlen(dst)] = *c;
3125
}
3226

3327
static List_T* get_id_path(ASTIdentifier_T* id) {

src/compiler/lexer/lexer.c

+20-4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const struct {
4545
{"namespace", TOKEN_NAMESPACE},
4646
{"nil", TOKEN_NIL},
4747
{"noop", TOKEN_NOOP},
48+
{"operator", TOKEN_OPERATOR_KW},
4849
{"ret", TOKEN_RETURN},
4950
{"sizeof", TOKEN_SIZEOF},
5051
{"struct", TOKEN_STRUCT},
@@ -77,13 +78,20 @@ const struct {
7778
{"³", TOKEN_POW_3},
7879
{"$", TOKEN_DOLLAR},
7980
{"@", TOKEN_AT},
80-
{"...", TOKEN_VA_LIST},
81-
{"<-", TOKEN_RETURN},
82-
{"=>", TOKEN_ARROW},
8381
{"`", TOKEN_INFIX_CALL},
8482
{NULL, TOKEN_EOF} // the last one has to be null as an indicator for the end of the array
8583
};
8684

85+
const struct {
86+
const char* symbol;
87+
TokenType_T type;
88+
} operator_overrides[] = {
89+
{"...", TOKEN_VA_LIST},
90+
{"<-", TOKEN_RETURN},
91+
{"=>", TOKEN_ARROW},
92+
{NULL, 0}
93+
};
94+
8795
static void lexer_skip_whitespace(Lexer_T* lexer);
8896
static void lexer_skip_comment(Lexer_T* lexer);
8997
static Token_T* lexer_get_id(Lexer_T* lexer);
@@ -429,7 +437,7 @@ static Token_T* lexer_get_char(Lexer_T* lexer)
429437
return token;
430438
}
431439

432-
static bool is_operator_char(char c)
440+
static inline bool is_operator_char(char c)
433441
{
434442
switch(c)
435443
{
@@ -466,6 +474,14 @@ static Token_T* lexer_get_operator(Lexer_T* lexer)
466474
}
467475

468476
Token_T* operator = init_token(buffer, lexer->line, lexer->pos - 1, TOKEN_OPERATOR, lexer->file);
477+
478+
for(size_t i = 0; operator_overrides[i].symbol; i++)
479+
if(strcmp(operator->value, operator_overrides[i].symbol) == 0)
480+
{
481+
operator->type = operator_overrides[i].type;
482+
break;
483+
}
484+
469485
return operator;
470486
}
471487

src/compiler/parser/parser.c

+69-16
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ typedef enum
5959

6060
PREC_ASSIGN, // x = y
6161
PREC_PIPE, // x |> y
62+
PREC_CUSTOM_OPERATOR, // custom operators
6263
PREC_LOGIC_OR, // x || y
6364
PREC_LOGIC_AND, // x && y
6465
PREC_INFIX_CALL, // x `y` z
@@ -129,6 +130,8 @@ static ASTNode_T* parse_type_expr(Parser_T* p);
129130
static ASTNode_T* parse_infix_call(Parser_T* p, ASTNode_T* left);
130131

131132
static ASTNode_T* parse_call(Parser_T* p, ASTNode_T* left);
133+
static ASTNode_T* parse_custom_infix_operator(Parser_T* p, ASTNode_T* left);
134+
static ASTNode_T* parse_custom_prefix_operator(Parser_T* p);
132135
static ASTNode_T* parse_cast(Parser_T* p, ASTNode_T* left);
133136
static ASTNode_T* parse_member(Parser_T* p, ASTNode_T* left);
134137
static ASTNode_T* parse_pipe(Parser_T* p, ASTNode_T* left);
@@ -287,10 +290,9 @@ static inline PrefixParseFn_T get_PrefixParseFn_T(Parser_T* p, Token_T* tok)
287290
if(tok->type == TOKEN_OPERATOR)
288291
{
289292
OperatorContext_T* context = hashmap_get(p->operator_context, tok->value);
290-
if(!context)
291-
throw_error(p->context, ERR_SYNTAX_ERROR, tok, "custom operators not supported yet");
292-
293-
return context->pfn;
293+
if(context)
294+
return context->pfn;
295+
return parse_custom_prefix_operator;
294296
}
295297
else
296298
return expr_parse_fns[tok->type].pfn;
@@ -301,10 +303,9 @@ static inline InfixParseFn_T get_InfixParseFn_T(Parser_T* p, Token_T* tok)
301303
if(tok->type == TOKEN_OPERATOR)
302304
{
303305
OperatorContext_T* context = hashmap_get(p->operator_context, tok->value);
304-
if(!context)
305-
throw_error(p->context, ERR_SYNTAX_ERROR, tok, "custom operators not supported yet");
306-
307-
return context->ifn;
306+
if(context)
307+
return context->ifn;
308+
return parse_custom_infix_operator;
308309
}
309310
else
310311
return expr_parse_fns[tok->type].ifn;
@@ -315,10 +316,9 @@ static inline Precedence_T get_precedence(Parser_T* p, Token_T* tok)
315316
if(tok->type == TOKEN_OPERATOR)
316317
{
317318
OperatorContext_T* context = hashmap_get(p->operator_context, tok->value);
318-
if(!context)
319-
throw_error(p->context, ERR_SYNTAX_ERROR, tok, "custom operators not supported yet");
320-
321-
return context->precedence;
319+
if(context)
320+
return context->precedence;
321+
return PREC_CUSTOM_OPERATOR;
322322
}
323323
else
324324
return expr_parse_fns[tok->type].prec;
@@ -558,7 +558,7 @@ static ASTIdentifier_T* __parse_identifier(Parser_T* p, ASTIdentifier_T* outer,
558558
if(tok_is_operator(p, STATIC_MEMBER) && !is_simple && parser_peek(p, 1)->type == TOKEN_ID)
559559
{
560560
if(is_operator(parser_peek(p, 1), "<"))
561-
return id; // :: followed by < would be a generic in a functon or -call
561+
return id; // :: followed by < would be a generic in a function call
562562

563563
parser_advance(p);
564564
return __parse_identifier(p, id, false);
@@ -917,6 +917,7 @@ static ASTObj_T* parse_extern_def(Parser_T *p, bool is_extern_c)
917917
throw_error(p->context, ERR_SYNTAX_WARNING, ext_var->value->tok, "cannot set a value to an extern variable");
918918
return ext_var;
919919
}
920+
case TOKEN_OPERATOR_KW:
920921
case TOKEN_FN:
921922
{
922923
ASTObj_T* ext_fn = parse_fn_def(p);
@@ -1017,16 +1018,34 @@ static ASTNode_T* parse_stmt(Parser_T* p, bool needs_semicolon);
10171018
static ASTObj_T* parse_fn_def(Parser_T* p)
10181019
{
10191020
ASTObj_T* fn = init_ast_obj(OBJ_FUNCTION, p->tok);
1020-
parser_consume(p, TOKEN_FN, "expect `fn` keyword for a function definition");
1021+
bool is_operator_define = false;
10211022

1022-
fn->id = parse_simple_identifier(p);
1023+
if(tok_is(p, TOKEN_OPERATOR_KW))
1024+
{
1025+
parser_advance(p);
1026+
fn->id = init_ast_identifier(p->tok, p->tok->value);
1027+
if(p->tok->type != TOKEN_OPERATOR)
1028+
throw_error(p->context, ERR_SYNTAX_ERROR, p->tok, "expect operator after `operator`");
1029+
if(hashmap_get(p->operator_context, p->tok->value))
1030+
throw_error(p->context, ERR_REDEFINITION, p->tok, "redefinition of built-in operator `%s`", p->tok->value);
1031+
parser_advance(p);
1032+
is_operator_define = true;
1033+
}
1034+
else
1035+
{
1036+
parser_consume(p, TOKEN_FN, "expect `fn` keyword for a function definition");
1037+
fn->id = parse_simple_identifier(p);
1038+
}
10231039

10241040
parser_consume(p, TOKEN_LPAREN, "expect `(` after function name");
1025-
1041+
10261042
ASTIdentifier_T* va_id = NULL;
10271043
fn->args = parse_argument_list(p, TOKEN_RPAREN, &va_id);
10281044
mem_add_list(fn->args);
10291045

1046+
if(is_operator_define && fn->args->size != 1 && fn->args->size != 2)
1047+
throw_error(p->context, ERR_SYNTAX_ERROR_UNCR, fn->tok, "`operator` functions can only take one or two arguments, got `%zu`", fn->args->size);
1048+
10301049
if(va_id)
10311050
{
10321051
fn->va_area = init_ast_obj(OBJ_LOCAL, fn->tok);
@@ -1183,6 +1202,7 @@ void parse_obj(Parser_T* p, List_T* obj_list)
11831202
list_push(obj_list, parse_global(p));
11841203
break;
11851204
case TOKEN_FN:
1205+
case TOKEN_OPERATOR_KW:
11861206
list_push(obj_list, parse_fn(p));
11871207
break;
11881208
case TOKEN_EXTERN:
@@ -2283,6 +2303,39 @@ static ASTNode_T* parse_call(Parser_T* p, ASTNode_T* left)
22832303
return call;
22842304
}
22852305

2306+
static ASTNode_T* parse_custom_prefix_operator(Parser_T* p)
2307+
{
2308+
ASTNode_T* call = init_ast_node(ND_CALL, p->tok);
2309+
call->expr = init_ast_node(ND_ID, p->tok);
2310+
call->expr->id = init_ast_identifier(p->tok, p->tok->value);
2311+
2312+
parser_consume(p, TOKEN_OPERATOR, "expect operator");
2313+
2314+
ASTNode_T* arg = parse_expr(p, PREC_UNARY, TOKEN_SEMICOLON);
2315+
2316+
call->args = init_list();
2317+
list_push(call->args, arg);
2318+
2319+
return call;
2320+
}
2321+
2322+
static ASTNode_T* parse_custom_infix_operator(Parser_T* p, ASTNode_T* left)
2323+
{
2324+
ASTNode_T* call = init_ast_node(ND_CALL, p->tok);
2325+
call->expr = init_ast_node(ND_ID, p->tok);
2326+
call->expr->id = init_ast_identifier(p->tok, p->tok->value);
2327+
2328+
parser_consume(p, TOKEN_OPERATOR, "expect operator");
2329+
2330+
ASTNode_T* right = parse_expr(p, PREC_CUSTOM_OPERATOR, TOKEN_SEMICOLON);
2331+
2332+
call->args = init_list();
2333+
list_push(call->args, left);
2334+
list_push(call->args, right);
2335+
2336+
return call;
2337+
}
2338+
22862339
static ASTNode_T* parse_index(Parser_T* p, ASTNode_T* left)
22872340
{
22882341
ASTNode_T* index = init_ast_node(ND_INDEX, p->tok);

0 commit comments

Comments
 (0)