diff --git a/compiler/Generator.cs b/compiler/Generator.cs index fc4bc5b..6966155 100644 --- a/compiler/Generator.cs +++ b/compiler/Generator.cs @@ -102,6 +102,7 @@ public class Generator #include #include #include + #include """); @@ -148,6 +149,13 @@ public class Generator ( """ #define FLAG_STRING_LITERAL 1 + #define STRING_DEBUG 1 + + #if STRING_DEBUG + #define STR_DBG(fmt, ...) fprintf(stderr, "[STR] " fmt "\n", ##__VA_ARGS__) + #else + #define STR_DBG(fmt, ...) + #endif struct nub_core_string { @@ -157,54 +165,114 @@ public class Generator uint32_t flags; }; + #if STRING_DEBUG + static size_t nub_core_string_allocs = 0; + static size_t nub_core_string_frees = 0; + + __attribute__((destructor)) + static void string_debug_report(void) + { + fprintf(stderr, "[STR] REPORT allocs=%zu frees=%zu leaks=%zu\n", nub_core_string_allocs, nub_core_string_frees, nub_core_string_allocs - nub_core_string_frees); + } + #endif + static inline void string_rc_inc(struct nub_core_string *string) { + if (string == NULL) + { + STR_DBG("INC null string"); + return; + } + if (string->flags & FLAG_STRING_LITERAL) return; string->ref += 1; + + STR_DBG("INC: str=%p ref=%u \"%s\"", (void*)string, string->ref, string->data); } static inline void string_rc_dec(struct nub_core_string *string) { if (string == NULL) + { + STR_DBG("DEC null string"); return; + } if (string->flags & FLAG_STRING_LITERAL) return; + if (string->ref == 0) + { + STR_DBG("ERROR: DEC on zero refcount str=%p", (void*)string); + return; + } + string->ref -= 1; + STR_DBG("DEC: str=%p ref=%u \"%s\"", (void*)string, string->ref, string->data); + if (string->ref == 0) { - free(string->data); - free(string); + STR_DBG("FREE: str=%p data=%p \"%s\"", (void*)string, (void*)string->data, string->data); + + char *data_ptr = string->data; + struct nub_core_string *str_ptr = string; + + #if STRING_DEBUG + nub_core_string_frees++; + + memset(data_ptr, 0xDD, str_ptr->length); + memset(str_ptr, 0xDD, sizeof(*str_ptr)); + #endif + + free(data_ptr); + free(str_ptr); } } static inline struct nub_core_string *string_concat(struct nub_core_string *left, struct nub_core_string *right) { size_t new_length = left->length + right->length; + struct nub_core_string *result = malloc(sizeof(struct nub_core_string)); result->data = malloc(new_length + 1); + memcpy(result->data, left->data, left->length); memcpy(result->data + left->length, right->data, right->length); + result->data[new_length] = '\0'; result->length = new_length; result->ref = 1; result->flags = 0; + + #if STRING_DEBUG + nub_core_string_allocs++; + STR_DBG("NEW concat: str=%p ref=%u \"%s\"", (void*)result, result->ref, result->data); + #endif + return result; } - static inline struct nub_core_string *string_from_cstr(const char* cstr) + static inline struct nub_core_string *string_from_cstr(char* cstr) { size_t len = strlen(cstr); + struct nub_core_string *result = malloc(sizeof(struct nub_core_string)); result->data = malloc(len + 1); + memcpy(result->data, cstr, len + 1); + result->length = len; result->ref = 1; result->flags = 0; + + #if STRING_DEBUG + nub_core_string_allocs++; + STR_DBG("NEW from_cstr: str=%p ref=%u \"%s\"", (void*)result, result->ref, result->data); + #endif + return result; } @@ -491,6 +559,7 @@ public class Generator TypedNodeExpressionStringLiteral expression => EmitExpressionStringLiteral(expression), TypedNodeExpressionStructLiteral expression => EmitExpressionStructLiteral(expression), TypedNodeExpressionEnumLiteral expression => EmitExpressionEnumLiteral(expression), + TypedNodeExpressionStringConstructor expression => EmitExpressionStringConstructor(expression), TypedNodeExpressionStructMemberAccess expression => EmitExpressionMemberAccess(expression), TypedNodeExpressionStringLength expression => EmitExpressionStringLength(expression), TypedNodeExpressionStringPointer expression => EmitExpressionStringPointer(expression), @@ -562,7 +631,7 @@ public class Generator { var name = Tmp(); referencedStringLiterals.Add(name, expression.Value.Value); - return $"&{name}"; + return $"(&{name})"; } private string EmitExpressionStructLiteral(TypedNodeExpressionStructLiteral expression) @@ -616,6 +685,15 @@ public class Generator return name; } + private string EmitExpressionStringConstructor(TypedNodeExpressionStringConstructor expression) + { + var name = Tmp(); + scopes.Peek().DeconstructableNames.Add((name, expression.Type)); + var value = EmitExpression(expression.Value); + writer.WriteLine($"{CType(expression.Type, name)} = string_from_cstr({value});"); + return name; + } + private string EmitExpressionMemberAccess(TypedNodeExpressionStructMemberAccess expression) { var target = EmitExpression(expression.Target); diff --git a/compiler/Parser.cs b/compiler/Parser.cs index 1dc3643..c9adcea 100644 --- a/compiler/Parser.cs +++ b/compiler/Parser.cs @@ -409,30 +409,45 @@ public class Parser { var type = ParseType(); - if (TryExpectSymbol(Symbol.OpenParen)) + if (type is NodeTypeString) { + ExpectSymbol(Symbol.OpenParen); var value = ParseExpression(); ExpectSymbol(Symbol.CloseParen); - expr = new NodeExpressionEnumLiteral(TokensFrom(startIndex), type, value); + expr = new NodeExpressionStringConstructor(TokensFrom(startIndex), value); } - else if (TryExpectSymbol(Symbol.OpenCurly)) + else if (type is NodeTypeNamed namedType) { - var initializers = new List(); - while (!TryExpectSymbol(Symbol.CloseCurly)) + if (TryExpectSymbol(Symbol.OpenParen)) { - var initializerStartIndex = startIndex; - var fieldName = ExpectIdent(); - ExpectSymbol(Symbol.Equal); - var fieldValue = ParseExpression(); - initializers.Add(new NodeExpressionStructLiteral.Initializer(TokensFrom(initializerStartIndex), fieldName, fieldValue)); - } + var value = ParseExpression(); + ExpectSymbol(Symbol.CloseParen); - expr = new NodeExpressionStructLiteral(TokensFrom(startIndex), null, initializers); + expr = new NodeExpressionEnumLiteral(TokensFrom(startIndex), namedType, value); + } + else if (TryExpectSymbol(Symbol.OpenCurly)) + { + var initializers = new List(); + while (!TryExpectSymbol(Symbol.CloseCurly)) + { + var initializerStartIndex = startIndex; + var fieldName = ExpectIdent(); + ExpectSymbol(Symbol.Equal); + var fieldValue = ParseExpression(); + initializers.Add(new NodeExpressionStructLiteral.Initializer(TokensFrom(initializerStartIndex), fieldName, fieldValue)); + } + + expr = new NodeExpressionStructLiteral(TokensFrom(startIndex), null, initializers); + } + else + { + expr = new NodeExpressionEnumLiteral(TokensFrom(startIndex), namedType, null); + } } else { - expr = new NodeExpressionEnumLiteral(TokensFrom(startIndex), type, null); + throw BasicError($"Expected named type or string", type); } } else @@ -740,6 +755,11 @@ public class Parser { return new CompileException(Diagnostic.Error(message).At(fileName, ident).Build()); } + + private CompileException BasicError(string message, Node? node) + { + return new CompileException(Diagnostic.Error(message).At(fileName, node).Build()); + } } public class Ast(string fileName, TokenIdent moduleName, List definitions) @@ -893,9 +913,9 @@ public class NodeExpressionBoolLiteral(List tokens, TokenBoolLiteral valu public TokenBoolLiteral Value { get; } = value; } -public class NodeExpressionStructLiteral(List tokens, NodeType? type, List initializers) : NodeExpression(tokens) +public class NodeExpressionStructLiteral(List tokens, NodeTypeNamed? type, List initializers) : NodeExpression(tokens) { - public NodeType? Type { get; } = type; + public NodeTypeNamed? Type { get; } = type; public List Initializers { get; } = initializers; public class Initializer(List tokens, TokenIdent name, NodeExpression value) : Node(tokens) @@ -905,12 +925,17 @@ public class NodeExpressionStructLiteral(List tokens, NodeType? type, Lis } } -public class NodeExpressionEnumLiteral(List tokens, NodeType type, NodeExpression? value) : NodeExpression(tokens) +public class NodeExpressionEnumLiteral(List tokens, NodeTypeNamed type, NodeExpression? value) : NodeExpression(tokens) { - public NodeType Type { get; } = type; + public NodeTypeNamed Type { get; } = type; public NodeExpression? Value { get; } = value; } +public class NodeExpressionStringConstructor(List tokens, NodeExpression value) : NodeExpression(tokens) +{ + public NodeExpression Value { get; } = value; +} + public class NodeExpressionMemberAccess(List tokens, NodeExpression target, TokenIdent name) : NodeExpression(tokens) { public NodeExpression Target { get; } = target; diff --git a/compiler/TypeChecker.cs b/compiler/TypeChecker.cs index 932f398..75a5e45 100644 --- a/compiler/TypeChecker.cs +++ b/compiler/TypeChecker.cs @@ -263,6 +263,7 @@ public class TypeChecker NodeExpressionStringLiteral expression => CheckExpressionStringLiteral(expression, expectedType), NodeExpressionStructLiteral expression => CheckExpressionStructLiteral(expression, expectedType), NodeExpressionEnumLiteral expression => CheckExpressionEnumLiteral(expression, expectedType), + NodeExpressionStringConstructor expression => CheckExpressionStringConstructor(expression, expectedType), _ => throw new ArgumentOutOfRangeException(nameof(node)) }; } @@ -651,6 +652,17 @@ public class TypeChecker return new TypedNodeExpressionEnumLiteral(expression.Tokens, type, value); } + private TypedNodeExpressionStringConstructor CheckExpressionStringConstructor(NodeExpressionStringConstructor expression, NubType? expectedType) + { + var stringPoitnerType = NubTypePointer.Get(NubTypeUInt.Get(8)); + + var value = CheckExpression(expression.Value, stringPoitnerType); + if (!value.Type.IsAssignableTo(stringPoitnerType)) + throw BasicError($"Value of string constructor must be assignable to {stringPoitnerType}", value); + + return new TypedNodeExpressionStringConstructor(expression.Tokens, NubTypeString.Instance, value); + } + private NubType ResolveType(NodeType node) { return node switch @@ -932,6 +944,11 @@ public class TypedNodeExpressionEnumLiteral(List tokens, NubType type, Ty public TypedNodeExpression? Value { get; } = value; } +public class TypedNodeExpressionStringConstructor(List tokens, NubType type, TypedNodeExpression value) : TypedNodeExpression(tokens, type) +{ + public TypedNodeExpression Value { get; } = value; +} + public class TypedNodeExpressionStructMemberAccess(List tokens, NubType type, TypedNodeExpression target, TokenIdent name) : TypedNodeExpression(tokens, type) { public TypedNodeExpression Target { get; } = target; diff --git a/examples/program/main.nub b/examples/program/main.nub index 983ee25..b094d08 100644 --- a/examples/program/main.nub +++ b/examples/program/main.nub @@ -16,6 +16,10 @@ func main(): i32 { let message = getMessage() + let newStr = new string("cstring".ptr) + + core::println(newStr) + match message { Quit { core::println("quit")