diff --git a/README.md b/README.md index 543f40755..91361407b 100644 --- a/README.md +++ b/README.md @@ -225,4 +225,8 @@ The generated binary will be called `a.out`. #### Licensing The C3 compiler is licensed under LGPL 3.0, the standard library itself is -MIT licensed. \ No newline at end of file +MIT licensed. + +#### Editor plugins + +Editor plugins can be found at https://github.com/c3lang/editor-plugins \ No newline at end of file diff --git a/resources/editor_plugins/iro/c3.iro b/resources/editor_plugins/iro/c3.iro deleted file mode 100644 index 55af611df..000000000 --- a/resources/editor_plugins/iro/c3.iro +++ /dev/null @@ -1,377 +0,0 @@ - -name = C3 -file_extensions [] = c3; - -################################################################ -## Constants -################################################################ - -__CONSTANT \= (_*[A-Z][A-Za-z0-9_]*) -__IDENT \= _*[a-z][A-Za-z0-9_]* -__TYPE \= _*[A-Z][A-Za-z0-9_]*[a-z][A-Za-z0-9_]* -__SCOPE \= ([a-z]+::)* -__USERTYPE \= _*[A-Z][A-Za-z0-9_]*[a-z][A-Za-z0-9_]* -__BUILTIN_TYPE \= void|float|half|double|quad|long|ulong|int|short|char|uint|ushort|ichar|usize|isize|iptr|uptr|iptrdiff|uptrdiff -################################################################ -## Styles -################################################################ - -styles [] { - -.comment : style { - color = #5F5A60 - italic = true - ace_scope = comment - textmate_scope = comment - pygments_scope = Comment -} - -.type : style { - color = #AE81FF - pygments_scope = Name.Class - textmate_scope = entity.name.type -} - -.string : style { - color = #E6DB74 - pygments_scope = String.Double - textmate_scope = string.quoted.double -} - -.escaped_text : style { - color = #AE81FF - pygments_scope = String.Escape -} -.function_decl : style { - color = #A6E22E - pygments_scope = Name.Entity -} - -.declare : style { - color = #66D9EF - pygments_scope = Keyword.Declaration -} - - -.function_call : style { - color = #66D9EF - pygments_scope = Name.Function -} - -.type_builtin : style { - color = #f92672 - ace_scope = keyword - textmate_scope = keyword - pygments_scope = Keyword.Type -} - -.keyword_constant : style -{ - color = #f92672 - ace_scope = keyword - textmate_scope = keyword - pygments_scope = Keyword.Constant -} - -.keyword : style { - color = #f92672 - ace_scope = keyword - textmate_scope = keyword - pygments_scope = Keyword -} - -.constant : style { - color = #AE81FF - textmate_scope = constant - pygments_scope = Name.Constant -} - - - -.numeric : style { - color = #AE81FF - ace_scope = constant.numeric - textmate_scope = constant.numeric - pygments_scope = Number -} - -.punctuation : style { - color = #FD971F - ace_scope = punctuation - textmate_scope = punctuation - pygments_scope = Punctuation -} - -.compile_time : style { - color = #908B25 - pygments_scope = Keyword.Pseudo -} - -.variable : style { - color = #f8f8f2 - background_color = #272822 - pygments_scope = Name -} - - -.text : style { - color = #f8f8f2 - background_color = #272822 - ace_scope = text - textmate_scope = text - pygments_scope = String -} - -.illegal : style { - color = white - background_color = red - ace_scope = invalid - textmate_scope = invalid - pygments_scope = Generic.Error -} - -} - -################################################# -## Parse contexts -################################################# - -contexts [] { - - top_level : context { - : include "parens"; - : include "comment"; - : include "constants"; - : include "ct_keywords"; - : include "decls"; - : include "path"; - : include "type"; - : include "keywords"; - : include "storage"; - : include "call"; - : include "identifier"; - : include "kw_operators"; - : include "operators"; - : pattern { - regex \= ([\.\;]) - styles [] = .punctuation; - } - : pattern { - regex \= ([\@]) - styles [] = .keyword; - } - } - - builtin_constants : context { - : pattern { - regex \= (true|false|null) - styles [] = .keyword_constant; - } - } - constants : context { - : pattern { - regex \= ($${__CONSTANT}) - styles [] = .constant; - } - } - type : context { - : pattern { - regex \= ($${__SCOPE})?($${__USERTYPE}) - styles [] = .type; - } - : pattern { - regex \= ($${__BUILTIN_TYPE}) - styles [] = .type_builtin; - } - } - path : context { - : pattern { - regex \= ([a-z][a-z_0-9]*)(\s*)(::) - styles [] = .text, .text, .keyword; - } - } - - call : context { - : pattern { - regex \= ($${__IDENT}\s*)(\() - styles [] = .function_call, .text; - } - } - - keywords : context { - : pattern { - regex \= (import|for|foreach|return|else|if|default|switch|while|do|module|assert|distinct|break|nextcase|case|continue|defer) - styles [] = .keyword; - } - } - ct_keywords : context { - : pattern { - regex \= (\$[a-zA-Z][a-z_A-Z0-9]*) - styles [] = .compile_time; - } - } - storage : context { - : pattern { - regex \= (const|extern|static|private) - styles [] = .keyword; - } - } - identifier : context { - : pattern { - regex \= ($${__IDENT}) - styles [] = .variable; - } - } - - operators : context { - : pattern { - regex \= ([\*\=\/\%\<\>\,\:\^\&\!\|\~]) - styles [] = .text; - } - } - kw_operators : context { - : pattern { - regex \= ([\+\-]) - styles [] = .text; - } - } - - parens : context { - : pattern { - regex \= ([\[\]\(\)\{\}]) - styles [] = .text; - } - } - - decls : context - { - : pattern { - regex \= (fn\s+)($${__SCOPE})?($${__USERTYPE})?($${__BUILTIN_TYPE})?(\s+$${__IDENT}) - styles [] = .declare, .text, .text, .type, .type_builtin, .function_decl; - } - : pattern { - regex \= (fn\s+)($${__SCOPE})?($${__USERTYPE})?($${__BUILTIN_TYPE})?(\s+$${__IDENT}) - styles [] = .declare, .text, .text, .type, .type_builtin, .function_decl; - } - : pattern { - regex \= (enum|struct|errtype|union)(\s+)($${__USERTYPE}) - styles[] = .declare, .text, .function_decl; - } - : pattern { - regex \= (struct|union|define) - styles[] = .declare; - } - } - - nested_comment : context { - : inline_push { - regex \= (/\*) - styles [] = .comment; - : pop { - regex \= (.*\*/) - styles [] = .comment; - } - : include "nested_comment"; - } - } - - comment : context { - : pattern { - regex \= (//.*) - styles [] = .comment; - } - : include "nested_comment"; - } - - -main : context { - - - : include "top_level"; - : pattern { - regex \= (define|extern|if|module|import|fn|struct|while|do|return|union|errtype) - styles [] = .keyword; - } - - : include "numeric" ; - - : inline_push { - regex \= (\{) - styles [] = .punctuation; - : pop { - regex \= (\}) - styles [] = .punctuation; - } - : include "main" ; - } - - : pattern { - regex \= (;) - styles [] = .punctuation; - } - - : inline_push { - regex \= (\") - styles [] = .punctuation; - : pop { - regex \= (\") - styles [] = .punctuation; - } - : pattern { - regex \= (\\(?:\\|")) - styles [] = .escaped_text; - } - : pattern { - regex \= ([^"\\]+) - styles [] = .string; - } - } - - - : include "multi_line_comment" ; - - : pattern { - regex \= (//.*) - styles [] = .comment; - } - - : pattern { - regex \= ([^\s]) - styles [] = .illegal; - } - -} - -################################################# -## End of Contexts -################################################# - -########################################### -## Numeric Context -########################################### - -numeric : context { - : pattern { - regex \= (\b\d+) - styles [] = .numeric; - } -} - -########################################### -## Multi Line Comment Context -########################################### - -multi_line_comment : context { - description = multiline - : inline_push { - regex \= (/\*) - styles [] = .comment; - default_style = .comment - : pop { - regex \= (\*/) - styles [] = .comment; - } - } -} - -} diff --git a/resources/editor_plugins/kakoune/c3.kak b/resources/editor_plugins/kakoune/c3.kak deleted file mode 100644 index 0fdd22686..000000000 --- a/resources/editor_plugins/kakoune/c3.kak +++ /dev/null @@ -1,26 +0,0 @@ -hook global BufCreate .*[.]c3 %{ - set-option buffer filetype c3 -} - -addhl shared/c3 regions -addhl shared/c3/code default-region group -addhl shared/c3/comment-line region '//' '$' fill comment -addhl shared/c3/comment-block region /\* \*/ fill comment -addhl shared/c3/double-string region 'c?"' (?|<|=|\+|-|\*|/|%|&|^|\||!|:|\?|;|,|@)=?' 0:default -addhl shared/c3/code/num regex '\b[0-9]+(.[0-9]+)=([eE][+-]?[0-9]+)=' 0:value - -hook -group c3-highlight global WinSetOption filetype=c3 %{ add-highlighter window/ ref c3 } -hook -group c3-highlight global WinSetOption filetype=(?!c3).* %{ remove-highlighter window/c3 } diff --git a/resources/editor_plugins/nano/README.md b/resources/editor_plugins/nano/README.md deleted file mode 100644 index 9611b8c7d..000000000 --- a/resources/editor_plugins/nano/README.md +++ /dev/null @@ -1,4 +0,0 @@ - -This .nanorc file adds syntax highlighting support for C3 to nano. - -It can either be installed under `/usr/share/nano` or included into `~/.nanorc`. diff --git a/resources/editor_plugins/nano/c3.nanorc b/resources/editor_plugins/nano/c3.nanorc deleted file mode 100644 index 22a912925..000000000 --- a/resources/editor_plugins/nano/c3.nanorc +++ /dev/null @@ -1,43 +0,0 @@ -## C3 syntax highlighting support for nano -## https://github.com/c3lang/c3c - -syntax c3 "\.(c3|c3t)$" -comment "//" - -# Default color -color white ".*" - -# Constants -#color brightred "\<[A-Z_][0-9A-Z_]+\>" - -# Function names -color magenta "\<[A-Za-z_]+\>\(" -color normal "\(|\)" - -# Types -color green "\<(anyerr|void|bool|quad|double|float|long|ulong|int|uint|short|ushort|ichar|char|isize|usize|iptr|uptr|iptrdiff|uptrdiff|half)\>" - -# Keywords -color yellow "\<(alias|as|asm|assert|attribute|break|case|catch|const|continue|default|defer|define|do|else|enum|extern|errtype|false|for|foreach|fn|generic|if|import|macro|module|nextcase|null|private|return|static|struct|switch|true|try|typeid|typeof|union|while|var|volatile|yield)\>" - -# $ Statements -color brightyellow "\$\<(assert|case|default|elif|else|endif|endswitch|for|if|switch|unreachable)\>" - -# @ Attributes -color brightred "@\<[A-Za-z_]+\>" - -# Strings -color brightblack "\"[^"]*\"" - -# Everything down from here should stay in the current order - -# Comments -color cyan "//.*" -color cyan start="/\+" end="\+/" -color cyan start="/\*" end="\*/" - -# Reminders -color brightwhite,red "\<(FIXME|TODO|XXX)\>" - -# Trailing whitespace -color ,green "[[:space:]]+$" diff --git a/resources/editor_plugins/vscode/.vscodeignore b/resources/editor_plugins/vscode/.vscodeignore deleted file mode 100644 index f369b5e55..000000000 --- a/resources/editor_plugins/vscode/.vscodeignore +++ /dev/null @@ -1,4 +0,0 @@ -.vscode/** -.vscode-test/** -.gitignore -vsc-extension-quickstart.md diff --git a/resources/editor_plugins/vscode/example.c3 b/resources/editor_plugins/vscode/example.c3 deleted file mode 100644 index d4670d485..000000000 --- a/resources/editor_plugins/vscode/example.c3 +++ /dev/null @@ -1,35 +0,0 @@ -module stack ; -// Above: the parameterized type is applied to the entire module. -import std::mem; - -struct Stack -{ - usize capacity; - usize size; - Type* elems; -} - -// The type methods offers dot syntax calls, -// so this function can either be called -// Stack.push(&my_stack, ...) or -// my_stack.push(...) -fn void Stack.push(Stack* this, Type element) -{ - if (this.capacity == this.size) - { - this.capacity *= 2; - this.elems = mem::realloc(this.elems, $sizeof(Type) * this.capacity); - } - this.elems[this.size++] = element; -} - -fn Type Stack.pop(Stack* this) -{ - assert(this.size > 0); - return this.elems[--this.size]; -} - -fn bool Stack.empty(Stack* this) -{ - return !this.size; -} diff --git a/resources/editor_plugins/vscode/language-configuration.json b/resources/editor_plugins/vscode/language-configuration.json deleted file mode 100644 index de79e9798..000000000 --- a/resources/editor_plugins/vscode/language-configuration.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "comments": { - // symbol used for single line comment. Remove this entry if your language does not support line comments - "lineComment": "//", - // symbols used for start and end a block comment. Remove this entry if your language does not support block comments - "blockComment": [ "/*", "*/" ] - }, - // symbols used as brackets - "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["{|", "|}"] - ], - // symbols that are auto closed when typing - "autoClosingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["{|", "|}"], - ["\"", "\""], - ["'", "'"] - ], - // symbols that can be used to surround a selection - "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"] - ] -} \ No newline at end of file diff --git a/resources/editor_plugins/vscode/package.json b/resources/editor_plugins/vscode/package.json deleted file mode 100644 index a41f9825b..000000000 --- a/resources/editor_plugins/vscode/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "c3-vscode", - "displayName": "c3-vscode", - "description": "C3 Language Support for VSCode", - "version": "0.0.1", - "engines": { - "vscode": "^1.63.0" - }, - "categories": [ - "Programming Languages" - ], - "contributes": { - "languages": [{ - "id": "c3", - "aliases": ["C3", "c3"], - "extensions": ["c3"], - "configuration": "./language-configuration.json" - }], - "grammars": [{ - "language": "c3", - "scopeName": "source.c3", - "path": "./syntaxes/c3.tmLanguage.json" - }] - } -} \ No newline at end of file diff --git a/resources/editor_plugins/vscode/syntaxes/c3.tmLanguage.json b/resources/editor_plugins/vscode/syntaxes/c3.tmLanguage.json deleted file mode 100644 index 246aaaed1..000000000 --- a/resources/editor_plugins/vscode/syntaxes/c3.tmLanguage.json +++ /dev/null @@ -1,158 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", - "name": "C3", - "patterns": [ - { "include": "#keywords" }, - { "include": "#comments" }, - { "include": "#strings" }, - { "include": "#numbers" }, - { "include": "#types" }, - { "include": "#support" }, - { "include": "#operators" } - ], - "repository": { - "keywords": { - "patterns": [ - { - "name": "keyword", - "match": "\\b(extern|break|case|const|continue|default)\\b" - }, - { - "name": "keyword", - "match": "\\b(do|else|for|goto|if|return|null|macro)\\b" - }, - { - "name": "keyword", - "match": "\\b(define|local|errtype|module|as|import)\\b" - }, - { - "name": "keyword", - "match": "\\b(generic|switch|typedef|volatile|private)\\b" - }, - { - "name": "keyword", - "match": "\\b(while|fn|nil|next|in|$for|$case)\\b" - }, - { - "name": "keyword", - "match": "\\b(%switch|$default|$if|$elif|$else)\\b" - }, - { - "name": "keyword", - "match": "\\b(true|false)\\b" - } - ] - }, - "comments": { - "patterns": [ - { - "name": "comment.line", - "begin": "//", - "end": "$" - }, - { - "name": "comment.block", - "begin": "/\\*", - "end": "\\*/" - } - ] - }, - "strings": { - "patterns": [ - { - "name": "string.quoted.double", - "patterns": [ {"include": "#stringcontent"} ], - "begin": "\"", - "end": "\"" - }, - { - "name": "string.quoted.double", - "patterns": [ {"include": "#stringcontent"} ], - "begin": "b64\"", - "end": "\"" - }, - { - "name": "string.quoted.double", - "patterns": [ {"include": "#stringcontent"} ], - "begin": "x\"", - "end": "\"" - }, - { - "name": "string.quoted.single", - "patterns": [ {"include": "#stringcontent"} ], - "begin": "'", - "end": "'" - }, - { - "name": "string.quoted.single", - "patterns": [ {"include": "#stringcontent"} ], - "begin": "b64'", - "end": "'" - }, - { - "name": "string.quoted.single", - "patterns": [ {"include": "#stringcontent"} ], - "begin": "x'", - "end": "'" - } - ] - }, - "stringcontent": { - "patterns": [ - { - "name": "constant.character.escape", - "match": "\\\\([nrt'\"\\\\]|(x[0-9a-fA-F]{2})|(u\\{[0-9a-fA-F]+\\}))" - }, - { - "name": "invalid.illegal.unrecognized-string-escape", - "match": "\\\\." - } - ] - }, - "numbers": { - "patterns": [ - { - "name": "constant.numeric", - "match": "\\b[0-9]\\.[0-9]+\\b" - }, - { - "name": "constant.numeric", - "match": "\\b[0-9]+\\b" - }, - { - "name": "constant.numeric", - "match": "\\b0x[a-fA-F0-9_]+\\b" - }, - { - "name": "constant.numeric", - "match": "\\b0o[0-7_]+\\b" - }, - { - "name": "constant.numeric", - "match": "\\b0b[01_]+\\b" - } - ] - }, - "types": { - "name": "storage.type", - "match": "\\b(double|usize|type|Type|bool|char|enum|float|int|uint|long|ulong|short|ushort|struct|void)\\b" - }, - "support": { - "patterns": [ - { - "name": "support.function.builtin", - "match": "\\$[_a-zA-Z][_a-zA-Z0-9]*" - }, - { - "name": "support.function.macro", - "match": "@[_a-zA-Z][_a-zA-Z0-9]*" - } - ] - }, - "operators": { - "name": "keyword.operator", - "match": "(\\+|-|\\.|%|&|\\|(?!})|=|<|>|!|\\^|\\*|/|::)=?" - } - }, - "scopeName": "source.c3" -} diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 63f3a16e6..212ffc898 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -237,6 +237,7 @@ bool expr_is_pure(Expr *expr) case EXPR_IDENTIFIER: case EXPR_NOP: case EXPR_PTR: + case EXPR_STRINGIFY: return true; case EXPR_ARGV_TO_SUBARRAY: case EXPR_BITASSIGN: diff --git a/src/compiler/copying.c b/src/compiler/copying.c index f5b351b73..7ea1e5911 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -136,6 +136,7 @@ Expr *copy_expr(Expr *source_expr) case EXPR_GROUP: case EXPR_TYPEOFANY: case EXPR_PTR: + case EXPR_STRINGIFY: MACRO_COPY_EXPR(expr->inner_expr); return expr; case EXPR_COND: diff --git a/src/compiler/enums.h b/src/compiler/enums.h index e92760b10..b53840084 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -210,6 +210,7 @@ typedef enum EXPR_SLICE_ASSIGN, EXPR_SUBSCRIPT, EXPR_SUBSCRIPT_ADDR, + EXPR_STRINGIFY, EXPR_ARGV_TO_SUBARRAY, EXPR_TERNARY, EXPR_TRY, @@ -487,6 +488,7 @@ typedef enum TOKEN_CT_OFFSETOF, // $offsetof TOKEN_CT_QNAMEOF, // $qnameof TOKEN_CT_SIZEOF, // $sizeof + TOKEN_CT_STRINGIFY, // $stringify TOKEN_CT_SWITCH, // $switch TOKEN_CT_TYPEOF, // $typeof TOKEN_CT_UNREACHABLE, // $unreachable diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 45ad6f72c..379d8a076 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -5462,6 +5462,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case EXPR_CT_CALL: case EXPR_FLATPATH: case EXPR_VARIANTSWITCH: + case EXPR_STRINGIFY: UNREACHABLE case EXPR_ARGV_TO_SUBARRAY: llvm_emit_argv_to_subarray(c, value, expr); diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 2ded1c42f..bea6363b4 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -444,6 +444,17 @@ static Expr *parse_typeof_expr(ParseContext *context, Expr *left) return expr; } +static Expr *parse_ct_stringify(ParseContext *context, Expr *left) +{ + assert(!left && "Unexpected left hand side"); + Expr *expr = EXPR_NEW_TOKEN(EXPR_STRINGIFY, context->tok); + advance(context); + CONSUME_OR(TOKEN_LPAREN, poisoned_expr); + ASSIGN_EXPR_ELSE(expr->inner_expr, parse_expr(context), poisoned_expr); + CONSUME_OR(TOKEN_RPAREN, poisoned_expr); + return expr; +} + static Expr *parse_unary_expr(ParseContext *context, Expr *left) { @@ -1676,6 +1687,6 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_CT_NAMEOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_QNAMEOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_TYPEOF] = { parse_typeof_expr, NULL, PREC_NONE }, - + [TOKEN_CT_STRINGIFY] = { parse_ct_stringify, NULL, PREC_NONE }, [TOKEN_LBRACE] = { parse_initializer_list, NULL, PREC_NONE }, }; diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 200c8b2aa..1bc1ca888 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -913,6 +913,7 @@ Ast *parse_stmt(ParseContext *context) case TOKEN_CT_QNAMEOF: case TOKEN_CT_NAMEOF: case TOKEN_CT_DEFINED: + case TOKEN_CT_STRINGIFY: case TOKEN_TRY: case TOKEN_CATCH: case TOKEN_BYTES: diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 21bec7041..7477924a9 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -818,6 +818,7 @@ Expr *recursive_may_narrow_float(Expr *expr, Type *type) case EXPR_VARIANTSWITCH: case EXPR_ARGV_TO_SUBARRAY: case EXPR_COMPILER_CONST: + case EXPR_STRINGIFY: UNREACHABLE case EXPR_POST_UNARY: return recursive_may_narrow_float(expr->unary_expr.expr, type); @@ -973,6 +974,7 @@ Expr *recursive_may_narrow_int(Expr *expr, Type *type) case EXPR_ARGV_TO_SUBARRAY: case EXPR_VARIANTSWITCH: case EXPR_COMPILER_CONST: + case EXPR_STRINGIFY: UNREACHABLE case EXPR_POST_UNARY: return recursive_may_narrow_int(expr->unary_expr.expr, type); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 31bc58062..8626a1cc6 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -301,6 +301,7 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) case EXPR_CAST: return expr_cast_is_constant_eval(expr, eval_kind); case EXPR_CONST: + case EXPR_STRINGIFY: return true; case EXPR_CONST_IDENTIFIER: expr = expr->identifier_expr.decl->var.init_expr; @@ -6757,6 +6758,27 @@ NOT_DEFINED: return true; } +static inline bool sema_expr_analyse_ct_stringify(SemaContext *context, Expr *expr) +{ + Expr *inner = expr->inner_expr; + // Special handling of #foo + if (inner->expr_kind == EXPR_HASH_IDENT) + { + Decl *decl = sema_resolve_normal_symbol(context, inner->ct_ident_expr.identifier, NULL, true); + if (!decl_ok(decl)) return false; + inner = decl->var.init_expr; + } + SourceLocation *start = TOKLOC(inner->span.loc); + SourceLocation *end = TOKLOC(inner->span.end_loc); + File *file = source_file_by_id(start->file_id); + const char *begin_char = &file->contents[start->start]; + const char *end_char = &file->contents[end->start + end->length]; + int len = (int)(end_char - begin_char); + const char *res = strformat("%.*s", len, begin_char); + expr_rewrite_to_string(expr, res); + return true; +} + static inline bool sema_expr_analyse_ct_offsetof(SemaContext *context, Expr *expr) { @@ -6839,6 +6861,7 @@ static inline bool sema_expr_analyse_ct_call(SemaContext *context, Expr *expr) } } + static inline BuiltinFunction builtin_by_name(const char *name) { for (unsigned i = 0; i < NUMBER_OF_BUILTINS; i++) @@ -6880,6 +6903,9 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) case EXPR_PTR: case EXPR_VARIANTSWITCH: UNREACHABLE + case EXPR_STRINGIFY: + if (!sema_expr_analyse_ct_stringify(context, expr)) return false; + return true; case EXPR_ARGV_TO_SUBARRAY: expr->type = type_get_subarray(type_get_subarray(type_char)); return true; diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index 9aa867a4b..d8e596237 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -381,6 +381,8 @@ const char *token_type_to_string(TokenType type) return "$switch"; case TOKEN_CT_TYPEOF: return "$typeof"; + case TOKEN_CT_STRINGIFY: + return "$stringify"; case TOKEN_CT_UNREACHABLE: return "$unreachable"; case TOKEN_EOF: diff --git a/src/version.h b/src/version.h index 8b8a47f9d..1ed7954ca 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "PRE.21" \ No newline at end of file +#define COMPILER_VERSION "PRE.22" \ No newline at end of file diff --git a/test/test_suite/compile_time/stringify.c3t b/test/test_suite/compile_time/stringify.c3t new file mode 100644 index 000000000..d68c37c8f --- /dev/null +++ b/test/test_suite/compile_time/stringify.c3t @@ -0,0 +1,115 @@ +// #target: x64-darwin +module test; +import libc; +import std::io; + +macro timeit(#call) +{ + long t = (long)libc::clock(); + $Type = $typeof(#call); + var $is_void = $Type.typeid == void.typeid; +$if ($is_void): + #call; +$else: + $Type result = #call; +$endif; + long diff = (long)libc::clock() - t; + io::printf("'%s' took %lld us\n", $stringify(#call), diff); +$if (!$is_void): + return result; +$endif; +} + +fn void test() +{ + for (int i = 0; i < 1000; i++) io::printf("%d\n", i); +} + +fn void main() +{ + @timeit(test()); + int a = 100; + int x = @timeit(1 + 3 + a); + io::printf("Result was %d\n", x); +} + +/* #expect: test.ll + +@.str = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1 +@.str.1 = private unnamed_addr constant [19 x i8] c"'%s' took %lld us\0A\00", align 1 +@.str.2 = private unnamed_addr constant [7 x i8] c"test()\00", align 1 +@.str.3 = private unnamed_addr constant [19 x i8] c"'%s' took %lld us\0A\00", align 1 +@.str.4 = private unnamed_addr constant [10 x i8] c"1 + 3 + a\00", align 1 +@.str.5 = private unnamed_addr constant [15 x i8] c"Result was %d\0A\00", align 1 + +define void @test.test() #0 { +entry: + %i = alloca i32, align 4 + store i32 0, i32* %i, align 4 + br label %loop.cond + +loop.cond: ; preds = %loop.body, %entry + %0 = load i32, i32* %i, align 4 + %lt = icmp slt i32 %0, 1000 + br i1 %lt, label %loop.body, label %loop.exit + +loop.body: ; preds = %loop.cond + %1 = load i32, i32* %i, align 4 + %2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %1) + %3 = load i32, i32* %i, align 4 + %add = add i32 %3, 1 + store i32 %add, i32* %i, align 4 + br label %loop.cond + +loop.exit: ; preds = %loop.cond + ret void +} + +define void @test.main() #0 { +entry: + %t = alloca i64, align 8 + %diff = alloca i64, align 8 + %a = alloca i32, align 4 + %x = alloca i32, align 4 + %blockret = alloca i32, align 4 + %t1 = alloca i64, align 8 + %result = alloca i32, align 4 + %diff2 = alloca i64, align 8 + %0 = call i64 @clock() + store i64 %0, i64* %t, align 8 + call void @test.test() + %1 = call i64 @clock() + %2 = load i64, i64* %t, align 8 + %sub = sub i64 %1, %2 + store i64 %sub, i64* %diff, align 8 + %3 = load i64, i64* %diff, align 8 + %4 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* @.str.1, i32 0, i32 0), [6 x i8]* bitcast ([7 x i8]* @.str.2 to [6 x i8]*), i64 %3) + store i32 100, i32* %a, align 4 + %5 = call i64 @clock() + store i64 %5, i64* %t1, align 8 + %6 = load i32, i32* %a, align 4 + %add = add i32 4, %6 + store i32 %add, i32* %result, align 4 + %7 = call i64 @clock() + %8 = load i64, i64* %t1, align 8 + %sub3 = sub i64 %7, %8 + store i64 %sub3, i64* %diff2, align 8 + %9 = load i64, i64* %diff2, align 8 + %10 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* @.str.3, i32 0, i32 0), [9 x i8]* bitcast ([10 x i8]* @.str.4 to [9 x i8]*), i64 %9) + %11 = load i32, i32* %result, align 4 + store i32 %11, i32* %blockret, align 4 + br label %expr_block.exit + +expr_block.exit: ; preds = %entry + %12 = load i32, i32* %blockret, align 4 + store i32 %12, i32* %x, align 4 + %13 = load i32, i32* %x, align 4 + %14 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str.5, i32 0, i32 0), i32 %13) + ret void +} + +define i32 @main(i32 %0, i8** %1) #0 { +entry: + call void @test.main() + ret i32 0 +}