diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2025-08-23 19:28:08 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2025-08-23 19:28:08 -0400 |
| commit | fcda36561d668f43bac91ea31cd55cbbd605d330 (patch) | |
| tree | eb74c0b17df584af0fd8154422ad924e04c96cc2 | |
| parent | 414b0c7472c87c5a013029aefef49e2dbc41e700 (diff) | |
Autoformat everything with clang-format
74 files changed, 7796 insertions, 7949 deletions
diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..e8555e59 --- /dev/null +++ b/.clang-format @@ -0,0 +1,274 @@ +--- +Language: Cpp +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseArrows: false + AlignCaseColons: false +AlignConsecutiveTableGenBreakingDAGArgColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenCondOperatorColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenDefinitionColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: Never +AllowShortBlocksOnASingleLine: Never +AllowShortCaseExpressionOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: true +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: AllIfsAndElse +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Leave +BreakAfterJavaFieldAnnotations: false +BreakAfterReturnType: None +BreakArrays: true +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakFunctionDefinitionParameters: false +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +BreakTemplateDeclarations: MultiLine +ColumnLimit: 120 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLines: + AtEndOfFile: false + AtStartOfBlock: true + AtStartOfFile: true +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MainIncludeChar: Quote +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: BinPack +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +PPIndentWidth: -1 +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterPlacementOperator: true + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + ExceptDoubleParentheses: false + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TableGenBreakInsideDAGArg: DontBreak +TabWidth: 4 +UseTab: Never +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +... + diff --git a/examples/coroutines/aco.c b/examples/coroutines/aco.c index 6057466f..258efe28 100644 --- a/examples/coroutines/aco.c +++ b/examples/coroutines/aco.c @@ -16,193 +16,194 @@ #define _GNU_SOURCE #include "aco.h" -#include <stdio.h> #include <stdint.h> +#include <stdio.h> #ifndef public -#define public __attribute__ ((visibility ("default"))) +#define public __attribute__((visibility("default"))) #endif -#define aco_size_t_safe_add_assert(a,b) aco_assert((a)+(b) >= (a)) +#define aco_size_t_safe_add_assert(a, b) aco_assert((a) + (b) >= (a)) -static void aco_default_protector_last_word(void*); +static void aco_default_protector_last_word(void *); -void* (*aco_alloc_fn)(size_t) = malloc; -void (*aco_dealloc_fn)(void*) = free; +void *(*aco_alloc_fn)(size_t) = malloc; +void (*aco_dealloc_fn)(void *) = free; -#define aco_alloc(size) ({ \ - void *_ptr = aco_alloc_fn(size); \ - if (aco_unlikely((_ptr) == NULL)) { \ - fprintf(stderr, "Aborting: failed to allocate memory: %s:%d:%s\n", \ - __FILE__, __LINE__, __PRETTY_FUNCTION__); \ - abort(); \ - } \ - _ptr; \ -}) +#define aco_alloc(size) \ + ({ \ + void *_ptr = aco_alloc_fn(size); \ + if (aco_unlikely((_ptr) == NULL)) { \ + fprintf(stderr, "Aborting: failed to allocate memory: %s:%d:%s\n", __FILE__, __LINE__, \ + __PRETTY_FUNCTION__); \ + abort(); \ + } \ + _ptr; \ + }) // aco's Global Thread Local Storage variable `co` -public __thread aco_t* aco_gtls_co; +public +__thread aco_t *aco_gtls_co; static __thread aco_cofuncp_t aco_gtls_last_word_fp = aco_default_protector_last_word; #ifdef __i386__ - static __thread void* aco_gtls_fpucw_mxcsr[2]; -#elif __x86_64__ - static __thread void* aco_gtls_fpucw_mxcsr[1]; +static __thread void *aco_gtls_fpucw_mxcsr[2]; +#elif __x86_64__ +static __thread void *aco_gtls_fpucw_mxcsr[1]; #else - #error "platform not supporteded yet" +#error "platform not supporteded yet" #endif -public void aco_runtime_test(void) { +public +void aco_runtime_test(void) { #ifdef __i386__ - _Static_assert(sizeof(void*) == 4, "require 'sizeof(void*) == 4'"); -#elif __x86_64__ - _Static_assert(sizeof(void*) == 8, "require 'sizeof(void*) == 8'"); + _Static_assert(sizeof(void *) == 4, "require 'sizeof(void*) == 4'"); +#elif __x86_64__ + _Static_assert(sizeof(void *) == 8, "require 'sizeof(void*) == 8'"); _Static_assert(sizeof(__uint128_t) == 16, "require 'sizeof(__uint128_t) == 16'"); #else - #error "platform not supporteded yet" +#error "platform not supporteded yet" #endif _Static_assert(sizeof(int) >= 4, "require 'sizeof(int) >= 4'"); aco_assert(sizeof(int) >= 4); - _Static_assert(sizeof(int) <= sizeof(size_t), - "require 'sizeof(int) <= sizeof(size_t)'"); + _Static_assert(sizeof(int) <= sizeof(size_t), "require 'sizeof(int) <= sizeof(size_t)'"); aco_assert(sizeof(int) <= sizeof(size_t)); } #ifdef __x86_64__ static inline void aco_fast_memcpy(void *dst, const void *src, size_t sz) { - if (((uintptr_t)src & 0x0f) != 0 - || ((uintptr_t)dst & 0x0f) != 0 - || (sz & 0x0f) != 0x08 - || (sz >> 4) > 8) { + if (((uintptr_t)src & 0x0f) != 0 || ((uintptr_t)dst & 0x0f) != 0 || (sz & 0x0f) != 0x08 || (sz >> 4) > 8) { memcpy(dst, src, sz); return; } - __uint128_t xmm0,xmm1,xmm2,xmm3,xmm4,xmm5,xmm6,xmm7; + __uint128_t xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7; switch (sz >> 4) { - case 0: - break; - case 1: - xmm0 = *((__uint128_t*)src + 0); - *((__uint128_t*)dst + 0) = xmm0; - break; - case 2: - xmm0 = *((__uint128_t*)src + 0); - xmm1 = *((__uint128_t*)src + 1); - *((__uint128_t*)dst + 0) = xmm0; - *((__uint128_t*)dst + 1) = xmm1; - break; - case 3: - xmm0 = *((__uint128_t*)src + 0); - xmm1 = *((__uint128_t*)src + 1); - xmm2 = *((__uint128_t*)src + 2); - *((__uint128_t*)dst + 0) = xmm0; - *((__uint128_t*)dst + 1) = xmm1; - *((__uint128_t*)dst + 2) = xmm2; - break; - case 4: - xmm0 = *((__uint128_t*)src + 0); - xmm1 = *((__uint128_t*)src + 1); - xmm2 = *((__uint128_t*)src + 2); - xmm3 = *((__uint128_t*)src + 3); - *((__uint128_t*)dst + 0) = xmm0; - *((__uint128_t*)dst + 1) = xmm1; - *((__uint128_t*)dst + 2) = xmm2; - *((__uint128_t*)dst + 3) = xmm3; - break; - case 5: - xmm0 = *((__uint128_t*)src + 0); - xmm1 = *((__uint128_t*)src + 1); - xmm2 = *((__uint128_t*)src + 2); - xmm3 = *((__uint128_t*)src + 3); - xmm4 = *((__uint128_t*)src + 4); - *((__uint128_t*)dst + 0) = xmm0; - *((__uint128_t*)dst + 1) = xmm1; - *((__uint128_t*)dst + 2) = xmm2; - *((__uint128_t*)dst + 3) = xmm3; - *((__uint128_t*)dst + 4) = xmm4; - break; - case 6: - xmm0 = *((__uint128_t*)src + 0); - xmm1 = *((__uint128_t*)src + 1); - xmm2 = *((__uint128_t*)src + 2); - xmm3 = *((__uint128_t*)src + 3); - xmm4 = *((__uint128_t*)src + 4); - xmm5 = *((__uint128_t*)src + 5); - *((__uint128_t*)dst + 0) = xmm0; - *((__uint128_t*)dst + 1) = xmm1; - *((__uint128_t*)dst + 2) = xmm2; - *((__uint128_t*)dst + 3) = xmm3; - *((__uint128_t*)dst + 4) = xmm4; - *((__uint128_t*)dst + 5) = xmm5; - break; - case 7: - xmm0 = *((__uint128_t*)src + 0); - xmm1 = *((__uint128_t*)src + 1); - xmm2 = *((__uint128_t*)src + 2); - xmm3 = *((__uint128_t*)src + 3); - xmm4 = *((__uint128_t*)src + 4); - xmm5 = *((__uint128_t*)src + 5); - xmm6 = *((__uint128_t*)src + 6); - *((__uint128_t*)dst + 0) = xmm0; - *((__uint128_t*)dst + 1) = xmm1; - *((__uint128_t*)dst + 2) = xmm2; - *((__uint128_t*)dst + 3) = xmm3; - *((__uint128_t*)dst + 4) = xmm4; - *((__uint128_t*)dst + 5) = xmm5; - *((__uint128_t*)dst + 6) = xmm6; - break; - case 8: - xmm0 = *((__uint128_t*)src + 0); - xmm1 = *((__uint128_t*)src + 1); - xmm2 = *((__uint128_t*)src + 2); - xmm3 = *((__uint128_t*)src + 3); - xmm4 = *((__uint128_t*)src + 4); - xmm5 = *((__uint128_t*)src + 5); - xmm6 = *((__uint128_t*)src + 6); - xmm7 = *((__uint128_t*)src + 7); - *((__uint128_t*)dst + 0) = xmm0; - *((__uint128_t*)dst + 1) = xmm1; - *((__uint128_t*)dst + 2) = xmm2; - *((__uint128_t*)dst + 3) = xmm3; - *((__uint128_t*)dst + 4) = xmm4; - *((__uint128_t*)dst + 5) = xmm5; - *((__uint128_t*)dst + 6) = xmm6; - *((__uint128_t*)dst + 7) = xmm7; - break; + case 0: break; + case 1: + xmm0 = *((__uint128_t *)src + 0); + *((__uint128_t *)dst + 0) = xmm0; + break; + case 2: + xmm0 = *((__uint128_t *)src + 0); + xmm1 = *((__uint128_t *)src + 1); + *((__uint128_t *)dst + 0) = xmm0; + *((__uint128_t *)dst + 1) = xmm1; + break; + case 3: + xmm0 = *((__uint128_t *)src + 0); + xmm1 = *((__uint128_t *)src + 1); + xmm2 = *((__uint128_t *)src + 2); + *((__uint128_t *)dst + 0) = xmm0; + *((__uint128_t *)dst + 1) = xmm1; + *((__uint128_t *)dst + 2) = xmm2; + break; + case 4: + xmm0 = *((__uint128_t *)src + 0); + xmm1 = *((__uint128_t *)src + 1); + xmm2 = *((__uint128_t *)src + 2); + xmm3 = *((__uint128_t *)src + 3); + *((__uint128_t *)dst + 0) = xmm0; + *((__uint128_t *)dst + 1) = xmm1; + *((__uint128_t *)dst + 2) = xmm2; + *((__uint128_t *)dst + 3) = xmm3; + break; + case 5: + xmm0 = *((__uint128_t *)src + 0); + xmm1 = *((__uint128_t *)src + 1); + xmm2 = *((__uint128_t *)src + 2); + xmm3 = *((__uint128_t *)src + 3); + xmm4 = *((__uint128_t *)src + 4); + *((__uint128_t *)dst + 0) = xmm0; + *((__uint128_t *)dst + 1) = xmm1; + *((__uint128_t *)dst + 2) = xmm2; + *((__uint128_t *)dst + 3) = xmm3; + *((__uint128_t *)dst + 4) = xmm4; + break; + case 6: + xmm0 = *((__uint128_t *)src + 0); + xmm1 = *((__uint128_t *)src + 1); + xmm2 = *((__uint128_t *)src + 2); + xmm3 = *((__uint128_t *)src + 3); + xmm4 = *((__uint128_t *)src + 4); + xmm5 = *((__uint128_t *)src + 5); + *((__uint128_t *)dst + 0) = xmm0; + *((__uint128_t *)dst + 1) = xmm1; + *((__uint128_t *)dst + 2) = xmm2; + *((__uint128_t *)dst + 3) = xmm3; + *((__uint128_t *)dst + 4) = xmm4; + *((__uint128_t *)dst + 5) = xmm5; + break; + case 7: + xmm0 = *((__uint128_t *)src + 0); + xmm1 = *((__uint128_t *)src + 1); + xmm2 = *((__uint128_t *)src + 2); + xmm3 = *((__uint128_t *)src + 3); + xmm4 = *((__uint128_t *)src + 4); + xmm5 = *((__uint128_t *)src + 5); + xmm6 = *((__uint128_t *)src + 6); + *((__uint128_t *)dst + 0) = xmm0; + *((__uint128_t *)dst + 1) = xmm1; + *((__uint128_t *)dst + 2) = xmm2; + *((__uint128_t *)dst + 3) = xmm3; + *((__uint128_t *)dst + 4) = xmm4; + *((__uint128_t *)dst + 5) = xmm5; + *((__uint128_t *)dst + 6) = xmm6; + break; + case 8: + xmm0 = *((__uint128_t *)src + 0); + xmm1 = *((__uint128_t *)src + 1); + xmm2 = *((__uint128_t *)src + 2); + xmm3 = *((__uint128_t *)src + 3); + xmm4 = *((__uint128_t *)src + 4); + xmm5 = *((__uint128_t *)src + 5); + xmm6 = *((__uint128_t *)src + 6); + xmm7 = *((__uint128_t *)src + 7); + *((__uint128_t *)dst + 0) = xmm0; + *((__uint128_t *)dst + 1) = xmm1; + *((__uint128_t *)dst + 2) = xmm2; + *((__uint128_t *)dst + 3) = xmm3; + *((__uint128_t *)dst + 4) = xmm4; + *((__uint128_t *)dst + 5) = xmm5; + *((__uint128_t *)dst + 6) = xmm6; + *((__uint128_t *)dst + 7) = xmm7; + break; } - *((uint64_t*)((uintptr_t)dst + sz - 8)) = *((uint64_t*)((uintptr_t)src + sz - 8)); + *((uint64_t *)((uintptr_t)dst + sz - 8)) = *((uint64_t *)((uintptr_t)src + sz - 8)); } #endif -void aco_default_protector_last_word(void*_) { - aco_t* co = aco_get_co(); +void aco_default_protector_last_word(void *_) { + aco_t *co = aco_get_co(); // do some log about the offending `co` - fprintf(stderr,"error: aco_default_protector_last_word triggered\n"); - fprintf(stderr, "error: co:%p should call `aco_exit()` instead of direct " - "`return` in co_fp:%p to finish its execution\n", co, (void*)co->fp); + fprintf(stderr, "error: aco_default_protector_last_word triggered\n"); + fprintf(stderr, + "error: co:%p should call `aco_exit()` instead of direct " + "`return` in co_fp:%p to finish its execution\n", + co, (void *)co->fp); aco_assert(0); } -public void aco_set_allocator(void* (*alloc)(size_t), void (*dealloc)(void*)) -{ +public +void aco_set_allocator(void *(*alloc)(size_t), void (*dealloc)(void *)) { aco_alloc_fn = alloc; aco_dealloc_fn = dealloc; } -public void aco_thread_init(aco_cofuncp_t last_word_co_fp) { +public +void aco_thread_init(aco_cofuncp_t last_word_co_fp) { aco_save_fpucw_mxcsr(aco_gtls_fpucw_mxcsr); - if ((void*)last_word_co_fp != NULL) - aco_gtls_last_word_fp = last_word_co_fp; + if ((void *)last_word_co_fp != NULL) aco_gtls_last_word_fp = last_word_co_fp; } // This function `aco_funcp_protector` should never be // called. If it's been called, that means the offending // `co` didn't call aco_exit(co) instead of `return` to // finish its execution. -public void aco_funcp_protector(void) { - if ((void*)(aco_gtls_last_word_fp) != NULL) { +public +void aco_funcp_protector(void) { + if ((void *)(aco_gtls_last_word_fp) != NULL) { aco_gtls_last_word_fp(NULL); } else { aco_default_protector_last_word(NULL); @@ -210,11 +211,11 @@ public void aco_funcp_protector(void) { aco_assert(0); } -public aco_shared_stack_t* aco_shared_stack_new(size_t sz) { - return aco_shared_stack_new2(sz, 1); -} +public +aco_shared_stack_t *aco_shared_stack_new(size_t sz) { return aco_shared_stack_new2(sz, 1); } -public aco_shared_stack_t* aco_shared_stack_new2(size_t sz, bool guard_page_enabled) { +public +aco_shared_stack_t *aco_shared_stack_new2(size_t sz, bool guard_page_enabled) { if (sz == 0) { sz = 1024 * 1024 * 2; } @@ -253,52 +254,48 @@ public aco_shared_stack_t* aco_shared_stack_new2(size_t sz, bool guard_page_enab } } - aco_shared_stack_t* p = aco_alloc(sizeof(aco_shared_stack_t)); + aco_shared_stack_t *p = aco_alloc(sizeof(aco_shared_stack_t)); memset(p, 0, sizeof(aco_shared_stack_t)); if (guard_page_enabled) { - p->real_ptr = mmap( - NULL, sz, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 - ); + p->real_ptr = mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (aco_unlikely(p->real_ptr == MAP_FAILED)) { - fprintf(stderr, "Aborting: failed to allocate memory: %s:%d:%s\n", - __FILE__, __LINE__, __PRETTY_FUNCTION__); + fprintf(stderr, "Aborting: failed to allocate memory: %s:%d:%s\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); abort(); } p->guard_page_enabled = true; aco_assert(0 == mprotect(p->real_ptr, u_pgsz, PROT_READ)); - p->ptr = (void*)(((uintptr_t)p->real_ptr) + u_pgsz); + p->ptr = (void *)(((uintptr_t)p->real_ptr) + u_pgsz); p->real_sz = sz; aco_assert(sz >= (u_pgsz << 1)); p->sz = sz - u_pgsz; } else { - //p->guard_page_enabled = 0; + // p->guard_page_enabled = 0; p->sz = sz; p->ptr = aco_alloc(sz); } p->owner = NULL; #ifdef ACO_USE_VALGRIND - p->valgrind_stk_id = VALGRIND_STACK_REGISTER( - p->ptr, (void*)((uintptr_t)p->ptr + p->sz) - ); + p->valgrind_stk_id = VALGRIND_STACK_REGISTER(p->ptr, (void *)((uintptr_t)p->ptr + p->sz)); #endif #if defined(__i386__) || defined(__x86_64__) - uintptr_t u_p = (uintptr_t)(p->sz - (sizeof(void*) << 1) + (uintptr_t)p->ptr); + uintptr_t u_p = (uintptr_t)(p->sz - (sizeof(void *) << 1) + (uintptr_t)p->ptr); u_p = (u_p >> 4) << 4; - p->align_highptr = (void*)u_p; - p->align_retptr = (void*)(u_p - sizeof(void*)); - *((void**)(p->align_retptr)) = (void*)(aco_funcp_protector_asm); - aco_assert(p->sz > (16 + (sizeof(void*) << 1) + sizeof(void*))); - p->align_limit = p->sz - 16 - (sizeof(void*) << 1); + p->align_highptr = (void *)u_p; + p->align_retptr = (void *)(u_p - sizeof(void *)); + *((void **)(p->align_retptr)) = (void *)(aco_funcp_protector_asm); + aco_assert(p->sz > (16 + (sizeof(void *) << 1) + sizeof(void *))); + p->align_limit = p->sz - 16 - (sizeof(void *) << 1); #else - #error "platform not supporteded yet" +#error "platform not supporteded yet" #endif return p; } -public void aco_shared_stack_destroy(aco_shared_stack_t* sstk) { +public +void aco_shared_stack_destroy(aco_shared_stack_t *sstk) { aco_assert(sstk != NULL && sstk->ptr != NULL); #ifdef ACO_USE_VALGRIND VALGRIND_STACK_DEREGISTER(sstk->valgrind_stk_id); @@ -314,11 +311,10 @@ public void aco_shared_stack_destroy(aco_shared_stack_t* sstk) { if (aco_dealloc_fn != NULL) aco_dealloc_fn(sstk); } -public aco_t* aco_create( - aco_t* main_co, aco_shared_stack_t* shared_stack, - size_t saved_stack_sz, aco_cofuncp_t fp, void* arg -) { - aco_t* p = aco_alloc(sizeof(aco_t)); +public +aco_t *aco_create(aco_t *main_co, aco_shared_stack_t *shared_stack, size_t saved_stack_sz, aco_cofuncp_t fp, + void *arg) { + aco_t *p = aco_alloc(sizeof(aco_t)); memset(p, 0, sizeof(aco_t)); if (main_co != NULL) { // non-main co @@ -327,21 +323,21 @@ public aco_t* aco_create( #ifdef __i386__ // POSIX.1-2008 (IEEE Std 1003.1-2008) - General Information - Data Types - Pointer Types // http://pubs.opengroup.org/onlinepubs/9699919799.2008edition/functions/V2_chap02.html#tag_15_12_03 - p->reg[ACO_REG_IDX_RETADDR] = (void*)fp; + p->reg[ACO_REG_IDX_RETADDR] = (void *)fp; // push retaddr p->reg[ACO_REG_IDX_SP] = p->shared_stack->align_retptr; - #ifndef ACO_CONFIG_SHARE_FPU_MXCSR_ENV - p->reg[ACO_REG_IDX_FPU] = aco_gtls_fpucw_mxcsr[0]; - p->reg[ACO_REG_IDX_FPU + 1] = aco_gtls_fpucw_mxcsr[1]; - #endif -#elif __x86_64__ - p->reg[ACO_REG_IDX_RETADDR] = (void*)fp; +#ifndef ACO_CONFIG_SHARE_FPU_MXCSR_ENV + p->reg[ACO_REG_IDX_FPU] = aco_gtls_fpucw_mxcsr[0]; + p->reg[ACO_REG_IDX_FPU + 1] = aco_gtls_fpucw_mxcsr[1]; +#endif +#elif __x86_64__ + p->reg[ACO_REG_IDX_RETADDR] = (void *)fp; p->reg[ACO_REG_IDX_SP] = p->shared_stack->align_retptr; - #ifndef ACO_CONFIG_SHARE_FPU_MXCSR_ENV - p->reg[ACO_REG_IDX_FPU] = aco_gtls_fpucw_mxcsr[0]; - #endif +#ifndef ACO_CONFIG_SHARE_FPU_MXCSR_ENV + p->reg[ACO_REG_IDX_FPU] = aco_gtls_fpucw_mxcsr[0]; +#endif #else - #error "platform not supporteded yet" +#error "platform not supporteded yet" #endif p->main_co = main_co; p->arg = arg; @@ -354,7 +350,7 @@ public aco_t* aco_create( #if defined(__i386__) || defined(__x86_64__) p->saved_stack.valid_sz = 0; #else - #error "platform not supporteded yet" +#error "platform not supporteded yet" #endif return p; } else { // main co @@ -368,35 +364,20 @@ public aco_t* aco_create( aco_assert(0); } -public aco_attr_no_asan -void aco_resume(aco_t* resume_co) { - aco_assert(resume_co != NULL && resume_co->main_co != NULL - && !resume_co->is_finished - ); +public +aco_attr_no_asan void aco_resume(aco_t *resume_co) { + aco_assert(resume_co != NULL && resume_co->main_co != NULL && !resume_co->is_finished); if (resume_co->shared_stack->owner != resume_co) { if (resume_co->shared_stack->owner != NULL) { - aco_t* owner_co = resume_co->shared_stack->owner; + aco_t *owner_co = resume_co->shared_stack->owner; aco_assert(owner_co->shared_stack == resume_co->shared_stack); #if defined(__i386__) || defined(__x86_64__) - aco_assert( - ( - (uintptr_t)(owner_co->shared_stack->align_retptr) - >= - (uintptr_t)(owner_co->reg[ACO_REG_IDX_SP]) - ) - && - ( - (uintptr_t)(owner_co->shared_stack->align_highptr) - - - (uintptr_t)(owner_co->shared_stack->align_limit) - <= - (uintptr_t)(owner_co->reg[ACO_REG_IDX_SP]) - ) - ); + aco_assert(((uintptr_t)(owner_co->shared_stack->align_retptr) >= (uintptr_t)(owner_co->reg[ACO_REG_IDX_SP])) + && ((uintptr_t)(owner_co->shared_stack->align_highptr) + - (uintptr_t)(owner_co->shared_stack->align_limit) + <= (uintptr_t)(owner_co->reg[ACO_REG_IDX_SP]))); owner_co->saved_stack.valid_sz = - (uintptr_t)(owner_co->shared_stack->align_retptr) - - - (uintptr_t)(owner_co->reg[ACO_REG_IDX_SP]); + (uintptr_t)(owner_co->shared_stack->align_retptr) - (uintptr_t)(owner_co->reg[ACO_REG_IDX_SP]); if (owner_co->saved_stack.sz < owner_co->saved_stack.valid_sz) { if (aco_dealloc_fn != NULL) aco_dealloc_fn(owner_co->saved_stack.ptr); owner_co->saved_stack.ptr = NULL; @@ -412,19 +393,12 @@ void aco_resume(aco_t* resume_co) { // TODO: optimize the performance penalty of memcpy function call // for very short memory span if (owner_co->saved_stack.valid_sz > 0) { - #ifdef __x86_64__ - aco_fast_memcpy( - owner_co->saved_stack.ptr, - owner_co->reg[ACO_REG_IDX_SP], - owner_co->saved_stack.valid_sz - ); - #else - memcpy( - owner_co->saved_stack.ptr, - owner_co->reg[ACO_REG_IDX_SP], - owner_co->saved_stack.valid_sz - ); - #endif +#ifdef __x86_64__ + aco_fast_memcpy(owner_co->saved_stack.ptr, owner_co->reg[ACO_REG_IDX_SP], + owner_co->saved_stack.valid_sz); +#else + memcpy(owner_co->saved_stack.ptr, owner_co->reg[ACO_REG_IDX_SP], owner_co->saved_stack.valid_sz); +#endif owner_co->saved_stack.ct_save++; } if (owner_co->saved_stack.valid_sz > owner_co->saved_stack.max_cpsz) { @@ -433,36 +407,30 @@ void aco_resume(aco_t* resume_co) { owner_co->shared_stack->owner = NULL; owner_co->shared_stack->align_validsz = 0; #else - #error "platform not supporteded yet" +#error "platform not supporteded yet" #endif } aco_assert(resume_co->shared_stack->owner == NULL); #if defined(__i386__) || defined(__x86_64__) - aco_assert( - resume_co->saved_stack.valid_sz - <= - resume_co->shared_stack->align_limit - sizeof(void*) - ); + aco_assert(resume_co->saved_stack.valid_sz <= resume_co->shared_stack->align_limit - sizeof(void *)); // TODO: optimize the performance penalty of memcpy function call // for very short memory span if (resume_co->saved_stack.valid_sz > 0) { - void *dst = (void*)( - (uintptr_t)(resume_co->shared_stack->align_retptr) - - resume_co->saved_stack.valid_sz); - #ifdef __x86_64__ + void *dst = (void *)((uintptr_t)(resume_co->shared_stack->align_retptr) - resume_co->saved_stack.valid_sz); +#ifdef __x86_64__ aco_fast_memcpy(dst, resume_co->saved_stack.ptr, resume_co->saved_stack.valid_sz); - #else +#else memcpy(dst, resume_co->saved_stack.ptr, resume_co->saved_stack.valid_sz); - #endif +#endif resume_co->saved_stack.ct_restore++; } if (resume_co->saved_stack.valid_sz > resume_co->saved_stack.max_cpsz) { resume_co->saved_stack.max_cpsz = resume_co->saved_stack.valid_sz; } - resume_co->shared_stack->align_validsz = resume_co->saved_stack.valid_sz + sizeof(void*); + resume_co->shared_stack->align_validsz = resume_co->saved_stack.valid_sz + sizeof(void *); resume_co->shared_stack->owner = resume_co; #else - #error "platform not supporteded yet" +#error "platform not supporteded yet" #endif } aco_gtls_co = resume_co; @@ -470,7 +438,8 @@ void aco_resume(aco_t* resume_co) { aco_gtls_co = resume_co->main_co; } -public void aco_destroy(aco_t* co) { +public +void aco_destroy(aco_t *co) { aco_assertptr(co); if (aco_is_main_co(co)) { if (aco_dealloc_fn != NULL) aco_dealloc_fn(co); @@ -479,14 +448,11 @@ public void aco_destroy(aco_t* co) { co->shared_stack->owner = NULL; co->shared_stack->align_validsz = 0; } - if (aco_dealloc_fn != NULL) - aco_dealloc_fn(co->saved_stack.ptr); + if (aco_dealloc_fn != NULL) aco_dealloc_fn(co->saved_stack.ptr); co->saved_stack.ptr = NULL; - if (aco_dealloc_fn != NULL) - aco_dealloc_fn(co); + if (aco_dealloc_fn != NULL) aco_dealloc_fn(co); } } -public void aco_exit_fn(void*_) { - aco_exit(); -} +public +void aco_exit_fn(void *_) { aco_exit(); } diff --git a/examples/coroutines/aco.h b/examples/coroutines/aco.h index 80d5542b..6bdfb4dc 100644 --- a/examples/coroutines/aco.h +++ b/examples/coroutines/aco.h @@ -26,7 +26,7 @@ #include <unistd.h> #ifdef ACO_USE_VALGRIND - #include <valgrind/valgrind.h> +#include <valgrind/valgrind.h> #endif #ifdef __cplusplus @@ -38,30 +38,30 @@ extern "C" { #define ACO_VERSION_PATCH 0 #ifdef __i386__ - #define ACO_REG_IDX_RETADDR 0 - #define ACO_REG_IDX_SP 1 - #define ACO_REG_IDX_BP 2 - #define ACO_REG_IDX_ARG1 0 - #define ACO_REG_IDX_FPU 6 +#define ACO_REG_IDX_RETADDR 0 +#define ACO_REG_IDX_SP 1 +#define ACO_REG_IDX_BP 2 +#define ACO_REG_IDX_ARG1 0 +#define ACO_REG_IDX_FPU 6 #elif __x86_64__ - #define ACO_REG_IDX_RETADDR 4 - #define ACO_REG_IDX_SP 5 - #define ACO_REG_IDX_BP 7 - #define ACO_REG_IDX_EDI 8 - #define ACO_REG_IDX_FPU 8 +#define ACO_REG_IDX_RETADDR 4 +#define ACO_REG_IDX_SP 5 +#define ACO_REG_IDX_BP 7 +#define ACO_REG_IDX_EDI 8 +#define ACO_REG_IDX_FPU 8 #else - #error "platform not supported yet" +#error "platform not supported yet" #endif typedef struct { - void* ptr; + void *ptr; size_t sz; size_t valid_sz; // max copy size in bytes size_t max_cpsz; // copy from shared stack to this saved stack size_t ct_save; - // copy from this saved stack to shared stack + // copy from this saved stack to shared stack size_t ct_restore; } aco_saved_stack_t; @@ -69,16 +69,16 @@ struct aco_s; typedef struct aco_s aco_t; typedef struct { - void* ptr; + void *ptr; size_t sz; - void* align_highptr; - void* align_retptr; + void *align_highptr; + void *align_retptr; size_t align_validsz; size_t align_limit; - aco_t* owner; + aco_t *owner; bool guard_page_enabled; - void* real_ptr; + void *real_ptr; size_t real_sz; #ifdef ACO_USE_VALGRIND @@ -86,128 +86,126 @@ typedef struct { #endif } aco_shared_stack_t; -typedef void (*aco_cofuncp_t)(void*); +typedef void (*aco_cofuncp_t)(void *); struct aco_s { // cpu registers' state #ifdef __i386__ - #ifdef ACO_CONFIG_SHARE_FPU_MXCSR_ENV - void* reg[6]; - #else - void* reg[8]; - #endif +#ifdef ACO_CONFIG_SHARE_FPU_MXCSR_ENV + void *reg[6]; +#else + void *reg[8]; +#endif #elif __x86_64__ - #ifdef ACO_CONFIG_SHARE_FPU_MXCSR_ENV - void* reg[8]; - #else - void* reg[9]; - #endif +#ifdef ACO_CONFIG_SHARE_FPU_MXCSR_ENV + void *reg[8]; +#else + void *reg[9]; +#endif #else - #error "platform not supported yet" +#error "platform not supported yet" #endif - aco_t* main_co; - void* arg; - bool is_finished; + aco_t *main_co; + void *arg; + bool is_finished; aco_cofuncp_t fp; - - aco_saved_stack_t saved_stack; - aco_shared_stack_t* shared_stack; + + aco_saved_stack_t saved_stack; + aco_shared_stack_t *shared_stack; }; #define aco_likely(x) (__builtin_expect(!!(x), 1)) #define aco_unlikely(x) (__builtin_expect(!!(x), 0)) -#define aco_assert(EX) ((aco_likely(EX))?((void)0):(abort())) +#define aco_assert(EX) ((aco_likely(EX)) ? ((void)0) : (abort())) -#define aco_assertptr(ptr) ((aco_likely((ptr) != NULL))?((void)0):(abort())) +#define aco_assertptr(ptr) ((aco_likely((ptr) != NULL)) ? ((void)0) : (abort())) #if defined(aco_attr_no_asan) - #error "aco_attr_no_asan already defined" +#error "aco_attr_no_asan already defined" #endif #if defined(ACO_USE_ASAN) - #if defined(__has_feature) - #if __has_feature(__address_sanitizer__) - #define aco_attr_no_asan \ - __attribute__((__no_sanitize_address__)) - #endif - #endif - #if defined(__SANITIZE_ADDRESS__) && !defined(aco_attr_no_asan) - #define aco_attr_no_asan \ - __attribute__((__no_sanitize_address__)) - #endif +#if defined(__has_feature) +#if __has_feature(__address_sanitizer__) +#define aco_attr_no_asan __attribute__((__no_sanitize_address__)) +#endif +#endif +#if defined(__SANITIZE_ADDRESS__) && !defined(aco_attr_no_asan) +#define aco_attr_no_asan __attribute__((__no_sanitize_address__)) +#endif #endif #ifndef aco_attr_no_asan - #define aco_attr_no_asan +#define aco_attr_no_asan #endif void aco_runtime_test(void); -void aco_set_allocator(void* (*alloc)(size_t), void (*dealloc)(void*)); +void aco_set_allocator(void *(*alloc)(size_t), void (*dealloc)(void *)); void aco_thread_init(aco_cofuncp_t last_word_co_fp); -void aco_yield_asm(aco_t* from_co, aco_t* to_co) __asm__("aco_yield_asm"); // asm +void aco_yield_asm(aco_t *from_co, aco_t *to_co) __asm__("aco_yield_asm"); // asm -void aco_save_fpucw_mxcsr(void* p) __asm__("aco_save_fpucw_mxcsr"); // asm +void aco_save_fpucw_mxcsr(void *p) __asm__("aco_save_fpucw_mxcsr"); // asm void aco_funcp_protector_asm(void) __asm__("aco_funcp_protector_asm"); // asm void aco_funcp_protector(void); -aco_shared_stack_t* aco_shared_stack_new(size_t sz); +aco_shared_stack_t *aco_shared_stack_new(size_t sz); -aco_shared_stack_t* aco_shared_stack_new2(size_t sz, bool guard_page_enabled); +aco_shared_stack_t *aco_shared_stack_new2(size_t sz, bool guard_page_enabled); -void aco_shared_stack_destroy(aco_shared_stack_t* sstk); +void aco_shared_stack_destroy(aco_shared_stack_t *sstk); -aco_t* aco_create( - aco_t* main_co, - aco_shared_stack_t* shared_stack, - size_t saved_stack_sz, - aco_cofuncp_t fp, void* arg -); +aco_t *aco_create(aco_t *main_co, aco_shared_stack_t *shared_stack, size_t saved_stack_sz, aco_cofuncp_t fp, void *arg); // aco's Global Thread Local Storage variable `co` #ifdef __TINYC__ - #error "TinyCC doesn't support thread-local storage!" +#error "TinyCC doesn't support thread-local storage!" #else -extern __thread aco_t* aco_gtls_co; +extern __thread aco_t *aco_gtls_co; #endif -aco_attr_no_asan -void aco_resume(aco_t* resume_co); +aco_attr_no_asan void aco_resume(aco_t *resume_co); -//void aco_yield1(aco_t* yield_co); -#define aco_yield1(yield_co) do { \ - aco_assertptr((yield_co)); \ - aco_assertptr((yield_co)->main_co); \ - aco_yield_asm((yield_co), (yield_co)->main_co); \ -} while (0) +// void aco_yield1(aco_t* yield_co); +#define aco_yield1(yield_co) \ + do { \ + aco_assertptr((yield_co)); \ + aco_assertptr((yield_co)->main_co); \ + aco_yield_asm((yield_co), (yield_co)->main_co); \ + } while (0) #define aco_yield() aco_yield1(aco_gtls_co) #define aco_get_arg() (aco_gtls_co->arg) -#define aco_get_co() ({(void)0; aco_gtls_co;}) +#define aco_get_co() \ + ({ \ + (void)0; \ + aco_gtls_co; \ + }) -void aco_destroy(aco_t* co); +void aco_destroy(aco_t *co); -#define aco_is_main_co(co) ({((co)->main_co) == NULL;}) +#define aco_is_main_co(co) ({ ((co)->main_co) == NULL; }) -#define aco_exit1(co) do { \ - (co)->is_finished = true; \ - aco_assert((co)->shared_stack->owner == (co)); \ - (co)->shared_stack->owner = NULL; \ - (co)->shared_stack->align_validsz = 0; \ - aco_yield1((co)); \ - aco_assert(0); \ -} while (0) +#define aco_exit1(co) \ + do { \ + (co)->is_finished = true; \ + aco_assert((co)->shared_stack->owner == (co)); \ + (co)->shared_stack->owner = NULL; \ + (co)->shared_stack->align_validsz = 0; \ + aco_yield1((co)); \ + aco_assert(0); \ + } while (0) #define aco_exit() aco_exit1(aco_gtls_co) -void aco_exit_fn(void*); +void aco_exit_fn(void *); #ifdef __cplusplus } diff --git a/lib/commands/commands.c b/lib/commands/commands.c index 80710387..4a6272b9 100644 --- a/lib/commands/commands.c +++ b/lib/commands/commands.c @@ -18,16 +18,14 @@ #define WRITE_END 1 static void xpipe(int fd[2]) { - if (pipe(fd) != 0) - fail("Failed to create pipe: ", strerror(errno)); + if (pipe(fd) != 0) fail("Failed to create pipe: ", strerror(errno)); } -int run_command(Text_t exe, List_t arg_list, Table_t env_table, - OptionalList_t input_bytes, List_t *output_bytes, List_t *error_bytes) -{ +int run_command(Text_t exe, List_t arg_list, Table_t env_table, OptionalList_t input_bytes, List_t *output_bytes, + List_t *error_bytes) { pthread_testcancel(); - struct sigaction sa = { .sa_handler = SIG_IGN }, oldint, oldquit; + struct sigaction sa = {.sa_handler = SIG_IGN}, oldint, oldquit; sigaction(SIGINT, &sa, &oldint); sigaction(SIGQUIT, &sa, &oldquit); sigaddset(&sa.sa_mask, SIGCHLD); @@ -40,7 +38,7 @@ int run_command(Text_t exe, List_t arg_list, Table_t env_table, posix_spawnattr_init(&attr); posix_spawnattr_setsigmask(&attr, &old); posix_spawnattr_setsigdefault(&attr, &reset); - posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGDEF|POSIX_SPAWN_SETSIGMASK); + posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK); int child_inpipe[2], child_outpipe[2], child_errpipe[2]; if (input_bytes.length >= 0) xpipe(child_inpipe); @@ -65,10 +63,11 @@ int run_command(Text_t exe, List_t arg_list, Table_t env_table, const char *exe_str = Text$as_c_string(exe); List_t arg_strs = {}; - List$insert_value(&arg_strs, exe_str, I(0), sizeof(char*)); + List$insert_value(&arg_strs, exe_str, I(0), sizeof(char *)); for (int64_t i = 0; i < arg_list.length; i++) - List$insert_value(&arg_strs, Text$as_c_string(*(Text_t*)(arg_list.data + i*arg_list.stride)), I(0), sizeof(char*)); - List$insert_value(&arg_strs, NULL, I(0), sizeof(char*)); + List$insert_value(&arg_strs, Text$as_c_string(*(Text_t *)(arg_list.data + i * arg_list.stride)), I(0), + sizeof(char *)); + List$insert_value(&arg_strs, NULL, I(0), sizeof(char *)); char **args = arg_strs.data; extern char **environ; @@ -76,24 +75,24 @@ int run_command(Text_t exe, List_t arg_list, Table_t env_table, if (env_table.entries.length > 0) { List_t env_list = {}; // List of const char* for (char **e = environ; *e; e++) - List$insert(&env_list, e, I(0), sizeof(char*)); + List$insert(&env_list, e, I(0), sizeof(char *)); for (int64_t i = 0; i < env_table.entries.length; i++) { - struct { Text_t key, value; } *entry = env_table.entries.data + env_table.entries.stride*i; + struct { + Text_t key, value; + } *entry = env_table.entries.data + env_table.entries.stride * i; const char *env_entry = String(entry->key, "=", entry->value); - List$insert(&env_list, &env_entry, I(0), sizeof(char*)); + List$insert(&env_list, &env_entry, I(0), sizeof(char *)); } - List$insert_value(&env_list, NULL, I(0), sizeof(char*)); - assert(env_list.stride == sizeof(char*)); + List$insert_value(&env_list, NULL, I(0), sizeof(char *)); + assert(env_list.stride == sizeof(char *)); env = env_list.data; } pid_t pid; - int ret = exe_str[0] == '/' ? - posix_spawn(&pid, exe_str, &actions, &attr, args, env) - : posix_spawnp(&pid, exe_str, &actions, &attr, args, env); - if (ret != 0) - return -1; + int ret = exe_str[0] == '/' ? posix_spawn(&pid, exe_str, &actions, &attr, args, env) + : posix_spawnp(&pid, exe_str, &actions, &attr, args, env); + if (ret != 0) return -1; posix_spawnattr_destroy(&attr); posix_spawn_file_actions_destroy(&actions); @@ -103,19 +102,16 @@ int run_command(Text_t exe, List_t arg_list, Table_t env_table, if (error_bytes) close(child_errpipe[WRITE_END]); struct pollfd pollfds[3] = {}; - if (input_bytes.length >= 0) pollfds[0] = (struct pollfd){.fd=child_inpipe[WRITE_END], .events=POLLOUT}; - if (output_bytes) pollfds[1] = (struct pollfd){.fd=child_outpipe[WRITE_END], .events=POLLIN}; - if (error_bytes) pollfds[2] = (struct pollfd){.fd=child_errpipe[WRITE_END], .events=POLLIN}; + if (input_bytes.length >= 0) pollfds[0] = (struct pollfd){.fd = child_inpipe[WRITE_END], .events = POLLOUT}; + if (output_bytes) pollfds[1] = (struct pollfd){.fd = child_outpipe[WRITE_END], .events = POLLIN}; + if (error_bytes) pollfds[2] = (struct pollfd){.fd = child_errpipe[WRITE_END], .events = POLLIN}; - if (input_bytes.length > 0 && input_bytes.stride != 1) - List$compact(&input_bytes, sizeof(char)); - if (output_bytes) - *output_bytes = (List_t){.atomic=1, .stride=1, .length=0}; - if (error_bytes) - *error_bytes = (List_t){.atomic=1, .stride=1, .length=0}; + if (input_bytes.length > 0 && input_bytes.stride != 1) List$compact(&input_bytes, sizeof(char)); + if (output_bytes) *output_bytes = (List_t){.atomic = 1, .stride = 1, .length = 0}; + if (error_bytes) *error_bytes = (List_t){.atomic = 1, .stride = 1, .length = 0}; while (input_bytes.length > 0 || output_bytes || error_bytes) { - (void)poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1); // Wait for data or readiness + (void)poll(pollfds, sizeof(pollfds) / sizeof(pollfds[0]), -1); // Wait for data or readiness bool did_something = false; if (input_bytes.length >= 0 && pollfds[0].revents) { if (input_bytes.length > 0) { @@ -171,10 +167,8 @@ int run_command(Text_t exe, List_t arg_list, Table_t env_table, int status = 0; if (ret == 0) { while (waitpid(pid, &status, 0) < 0 && errno == EINTR) { - if (WIFEXITED(status) || WIFSIGNALED(status)) - break; - else if (WIFSTOPPED(status)) - kill(pid, SIGCONT); + if (WIFEXITED(status) || WIFSIGNALED(status)) break; + else if (WIFSTOPPED(status)) kill(pid, SIGCONT); } } @@ -195,8 +189,7 @@ typedef struct { FILE *out; } child_info_t; -static void _line_reader_cleanup(child_info_t *child) -{ +static void _line_reader_cleanup(child_info_t *child) { if (child && child->out) { fclose(child->out); child->out = NULL; @@ -207,8 +200,7 @@ static void _line_reader_cleanup(child_info_t *child) } } -static Text_t _next_line(child_info_t *child) -{ +static Text_t _next_line(child_info_t *child) { if (!child || !child->out) return NONE_TEXT; char *line = NULL; @@ -219,19 +211,17 @@ static Text_t _next_line(child_info_t *child) return NONE_TEXT; } - while (len > 0 && (line[len-1] == '\r' || line[len-1] == '\n')) + while (len > 0 && (line[len - 1] == '\r' || line[len - 1] == '\n')) --len; - if (u8_check((uint8_t*)line, (size_t)len) != NULL) - fail("Invalid UTF8!"); + if (u8_check((uint8_t *)line, (size_t)len) != NULL) fail("Invalid UTF8!"); Text_t line_text = Text$from_strn(line, len); free(line); return line_text; } -OptionalClosure_t command_by_line(Text_t exe, List_t arg_list, Table_t env_table) -{ +OptionalClosure_t command_by_line(Text_t exe, List_t arg_list, Table_t env_table) { posix_spawnattr_t attr; posix_spawnattr_init(&attr); @@ -246,10 +236,11 @@ OptionalClosure_t command_by_line(Text_t exe, List_t arg_list, Table_t env_table const char *exe_str = Text$as_c_string(exe); List_t arg_strs = {}; - List$insert_value(&arg_strs, exe_str, I(0), sizeof(char*)); + List$insert_value(&arg_strs, exe_str, I(0), sizeof(char *)); for (int64_t i = 0; i < arg_list.length; i++) - List$insert_value(&arg_strs, Text$as_c_string(*(Text_t*)(arg_list.data + i*arg_list.stride)), I(0), sizeof(char*)); - List$insert_value(&arg_strs, NULL, I(0), sizeof(char*)); + List$insert_value(&arg_strs, Text$as_c_string(*(Text_t *)(arg_list.data + i * arg_list.stride)), I(0), + sizeof(char *)); + List$insert_value(&arg_strs, NULL, I(0), sizeof(char *)); char **args = arg_strs.data; extern char **environ; @@ -257,24 +248,24 @@ OptionalClosure_t command_by_line(Text_t exe, List_t arg_list, Table_t env_table if (env_table.entries.length > 0) { List_t env_list = {}; // List of const char* for (char **e = environ; *e; e++) - List$insert(&env_list, e, I(0), sizeof(char*)); + List$insert(&env_list, e, I(0), sizeof(char *)); for (int64_t i = 0; i < env_table.entries.length; i++) { - struct { Text_t key, value; } *entry = env_table.entries.data + env_table.entries.stride*i; + struct { + Text_t key, value; + } *entry = env_table.entries.data + env_table.entries.stride * i; const char *env_entry = String(entry->key, "=", entry->value); - List$insert(&env_list, &env_entry, I(0), sizeof(char*)); + List$insert(&env_list, &env_entry, I(0), sizeof(char *)); } - List$insert_value(&env_list, NULL, I(0), sizeof(char*)); - assert(env_list.stride == sizeof(char*)); + List$insert_value(&env_list, NULL, I(0), sizeof(char *)); + assert(env_list.stride == sizeof(char *)); env = env_list.data; } pid_t pid; - int ret = exe_str[0] == '/' ? - posix_spawn(&pid, exe_str, &actions, &attr, args, env) - : posix_spawnp(&pid, exe_str, &actions, &attr, args, env); - if (ret != 0) - return NONE_CLOSURE; + int ret = exe_str[0] == '/' ? posix_spawn(&pid, exe_str, &actions, &attr, args, env) + : posix_spawnp(&pid, exe_str, &actions, &attr, args, env); + if (ret != 0) return NONE_CLOSURE; posix_spawnattr_destroy(&attr); posix_spawn_file_actions_destroy(&actions); @@ -284,8 +275,8 @@ OptionalClosure_t command_by_line(Text_t exe, List_t arg_list, Table_t env_table child_info_t *child_info = GC_MALLOC(sizeof(child_info_t)); child_info->out = fdopen(child_outpipe[READ_END], "r"); child_info->pid = pid; - GC_register_finalizer(child_info, (void*)_line_reader_cleanup, NULL, NULL, NULL); - return (Closure_t){.fn=(void*)_next_line, .userdata=child_info}; + GC_register_finalizer(child_info, (void *)_line_reader_cleanup, NULL, NULL, NULL); + return (Closure_t){.fn = (void *)_next_line, .userdata = child_info}; } #undef READ_END diff --git a/lib/patterns/match_type.h b/lib/patterns/match_type.h index abbc4fce..911d66fc 100644 --- a/lib/patterns/match_type.h +++ b/lib/patterns/match_type.h @@ -5,4 +5,3 @@ typedef struct { Int_t index; List_t captures; } XMatch; - diff --git a/lib/patterns/patterns.c b/lib/patterns/patterns.c index 224a00a0..0bdbbf58 100644 --- a/lib/patterns/patterns.c +++ b/lib/patterns/patterns.c @@ -20,10 +20,10 @@ typedef struct { Text_t text; Int_t index; List_t captures; - bool is_none:1; + bool is_none : 1; } OptionalPatternMatch; -#define NONE_MATCH ((OptionalPatternMatch){.is_none=true}) +#define NONE_MATCH ((OptionalPatternMatch){.is_none = true}) typedef struct { int64_t index, length; @@ -45,18 +45,15 @@ typedef struct { static Text_t replace_list(Text_t text, List_t replacements, Text_t backref_pat, bool recursive); -static INLINE void skip_whitespace(TextIter_t *state, int64_t *i) -{ +static INLINE void skip_whitespace(TextIter_t *state, int64_t *i) { while (*i < state->stack[0].text.length) { int32_t grapheme = Text$get_grapheme_fast(state, *i); - if (grapheme > 0 && !uc_is_property_white_space((ucs4_t)grapheme)) - return; + if (grapheme > 0 && !uc_is_property_white_space((ucs4_t)grapheme)) return; *i += 1; } } -static INLINE bool match_grapheme(TextIter_t *state, int64_t *i, int32_t grapheme) -{ +static INLINE bool match_grapheme(TextIter_t *state, int64_t *i, int32_t grapheme) { if (*i < state->stack[0].text.length && Text$get_grapheme_fast(state, *i) == grapheme) { *i += 1; return true; @@ -64,8 +61,7 @@ static INLINE bool match_grapheme(TextIter_t *state, int64_t *i, int32_t graphem return false; } -static INLINE bool match_str(TextIter_t *state, int64_t *i, const char *str) -{ +static INLINE bool match_str(TextIter_t *state, int64_t *i, const char *str) { int64_t matched = 0; while (matched[str]) { if (*i + matched >= state->stack[0].text.length || Text$get_grapheme_fast(state, *i + matched) != str[matched]) @@ -76,21 +72,19 @@ static INLINE bool match_str(TextIter_t *state, int64_t *i, const char *str) return true; } -static int64_t parse_int(TextIter_t *state, int64_t *i) -{ +static int64_t parse_int(TextIter_t *state, int64_t *i) { int64_t value = 0; for (;; *i += 1) { uint32_t grapheme = Text$get_main_grapheme_fast(state, *i); int digit = uc_digit_value(grapheme); if (digit < 0) break; - if (value >= INT64_MAX/10) break; - value = 10*value + digit; + if (value >= INT64_MAX / 10) break; + value = 10 * value + digit; } return value; } -static const char *get_property_name(TextIter_t *state, int64_t *i) -{ +static const char *get_property_name(TextIter_t *state, int64_t *i) { skip_whitespace(state, i); char *name = GC_MALLOC_ATOMIC(UNINAME_MAX); char *dest = name; @@ -99,8 +93,7 @@ static const char *get_property_name(TextIter_t *state, int64_t *i) if (!(grapheme & ~0xFF) && (isalnum(grapheme) || grapheme == ' ' || grapheme == '_' || grapheme == '-')) { *dest = (char)grapheme; ++dest; - if (dest >= name + UNINAME_MAX - 1) - break; + if (dest >= name + UNINAME_MAX - 1) break; } else { break; } @@ -115,28 +108,36 @@ static const char *get_property_name(TextIter_t *state, int64_t *i) return name; } -#define EAT1(state, index, cond) ({\ - int32_t grapheme = Text$get_grapheme_fast(state, index); \ - bool success = (cond); \ - if (success) index += 1; \ - success; }) - -#define EAT2(state, index, cond1, cond2) ({\ - int32_t grapheme = Text$get_grapheme_fast(state, index); \ - bool success = (cond1); \ - if (success) { \ - grapheme = Text$get_grapheme_fast(state, index + 1); \ - success = (cond2); \ - if (success) \ - index += 2; \ - } \ - success; }) - - -#define EAT_MANY(state, index, cond) ({ int64_t _n = 0; while (EAT1(state, index, cond)) { _n += 1; } _n; }) - -static int64_t match_email(TextIter_t *state, int64_t index) -{ +#define EAT1(state, index, cond) \ + ({ \ + int32_t grapheme = Text$get_grapheme_fast(state, index); \ + bool success = (cond); \ + if (success) index += 1; \ + success; \ + }) + +#define EAT2(state, index, cond1, cond2) \ + ({ \ + int32_t grapheme = Text$get_grapheme_fast(state, index); \ + bool success = (cond1); \ + if (success) { \ + grapheme = Text$get_grapheme_fast(state, index + 1); \ + success = (cond2); \ + if (success) index += 2; \ + } \ + success; \ + }) + +#define EAT_MANY(state, index, cond) \ + ({ \ + int64_t _n = 0; \ + while (EAT1(state, index, cond)) { \ + _n += 1; \ + } \ + _n; \ + }) + +static int64_t match_email(TextIter_t *state, int64_t index) { // email = local "@" domain // local = 1-64 ([a-zA-Z0-9!#$%&‘*+–/=?^_`.{|}~] | non-ascii) // domain = dns-label ("." dns-label)* @@ -144,8 +145,7 @@ static int64_t match_email(TextIter_t *state, int64_t index) if (index > 0) { uint32_t prev_codepoint = Text$get_main_grapheme_fast(state, index - 1); - if (uc_is_property_alphabetic(prev_codepoint)) - return -1; + if (uc_is_property_alphabetic(prev_codepoint)) return -1; } int64_t start_index = index; @@ -153,78 +153,65 @@ static int64_t match_email(TextIter_t *state, int64_t index) // Local part: int64_t local_len = 0; static const char *allowed_local = "!#$%&‘*+–/=?^_`.{|}~"; - while (EAT1(state, index, - (grapheme & ~0x7F) || isalnum((char)grapheme) || strchr(allowed_local, (char)grapheme))) { + while (EAT1(state, index, (grapheme & ~0x7F) || isalnum((char)grapheme) || strchr(allowed_local, (char)grapheme))) { local_len += 1; if (local_len > 64) return -1; } - - if (!EAT1(state, index, grapheme == '@')) - return -1; + + if (!EAT1(state, index, grapheme == '@')) return -1; // Host int64_t host_len = 0; do { int64_t label_len = 0; - while (EAT1(state, index, - (grapheme & ~0x7F) || isalnum((char)grapheme) || grapheme == '-')) { + while (EAT1(state, index, (grapheme & ~0x7F) || isalnum((char)grapheme) || grapheme == '-')) { label_len += 1; if (label_len > 63) return -1; } - if (label_len == 0) - return -1; + if (label_len == 0) return -1; host_len += label_len; - if (host_len > 255) - return -1; + if (host_len > 255) return -1; host_len += 1; } while (EAT1(state, index, grapheme == '.')); return index - start_index; } -static int64_t match_ipv6(TextIter_t *state, int64_t index) -{ +static int64_t match_ipv6(TextIter_t *state, int64_t index) { if (index > 0) { int32_t prev_codepoint = Text$get_grapheme_fast(state, index - 1); - if ((prev_codepoint & ~0x7F) && (isxdigit(prev_codepoint) || prev_codepoint == ':')) - return -1; + if ((prev_codepoint & ~0x7F) && (isxdigit(prev_codepoint) || prev_codepoint == ':')) return -1; } int64_t start_index = index; const int NUM_CLUSTERS = 8; bool double_colon_used = false; for (int cluster = 0; cluster < NUM_CLUSTERS; cluster++) { for (int digits = 0; digits < 4; digits++) { - if (!EAT1(state, index, ~(grapheme & ~0x7F) && isxdigit((char)grapheme))) - break; + if (!EAT1(state, index, ~(grapheme & ~0x7F) && isxdigit((char)grapheme))) break; } - if (EAT1(state, index, ~(grapheme & ~0x7F) && isxdigit((char)grapheme))) - return -1; // Too many digits + if (EAT1(state, index, ~(grapheme & ~0x7F) && isxdigit((char)grapheme))) return -1; // Too many digits - if (cluster == NUM_CLUSTERS-1) { + if (cluster == NUM_CLUSTERS - 1) { break; } else if (!EAT1(state, index, grapheme == ':')) { - if (double_colon_used) - break; + if (double_colon_used) break; return -1; } if (EAT1(state, index, grapheme == ':')) { - if (double_colon_used) - return -1; + if (double_colon_used) return -1; double_colon_used = true; } } return index - start_index; } -static int64_t match_ipv4(TextIter_t *state, int64_t index) -{ +static int64_t match_ipv4(TextIter_t *state, int64_t index) { if (index > 0) { int32_t prev_codepoint = Text$get_grapheme_fast(state, index - 1); - if ((prev_codepoint & ~0x7F) && (isdigit(prev_codepoint) || prev_codepoint == '.')) - return -1; + if ((prev_codepoint & ~0x7F) && (isdigit(prev_codepoint) || prev_codepoint == '.')) return -1; } int64_t start_index = index; @@ -237,27 +224,22 @@ static int64_t match_ipv4(TextIter_t *state, int64_t index) } } - if (EAT1(state, index, ~(grapheme & ~0x7F) && isdigit((char)grapheme))) - return -1; // Too many digits + if (EAT1(state, index, ~(grapheme & ~0x7F) && isdigit((char)grapheme))) return -1; // Too many digits - if (cluster == NUM_CLUSTERS-1) - break; - else if (!EAT1(state, index, grapheme == '.')) - return -1; + if (cluster == NUM_CLUSTERS - 1) break; + else if (!EAT1(state, index, grapheme == '.')) return -1; } return (index - start_index); } -static int64_t match_ip(TextIter_t *state, int64_t index) -{ +static int64_t match_ip(TextIter_t *state, int64_t index) { int64_t len = match_ipv6(state, index); if (len >= 0) return len; len = match_ipv4(state, index); return (len >= 0) ? len : -1; } -static int64_t match_host(TextIter_t *state, int64_t index) -{ +static int64_t match_host(TextIter_t *state, int64_t index) { int64_t ip_len = match_ip(state, index); if (ip_len > 0) return ip_len; @@ -266,28 +248,24 @@ static int64_t match_host(TextIter_t *state, int64_t index) ip_len = match_ip(state, index); if (ip_len <= 0) return -1; index += ip_len; - if (match_grapheme(state, &index, ']')) - return (index - start_index); + if (match_grapheme(state, &index, ']')) return (index - start_index); return -1; } - if (!EAT1(state, index, isalpha(grapheme))) - return -1; + if (!EAT1(state, index, isalpha(grapheme))) return -1; static const char *non_host_chars = "/#?:@ \t\r\n<>[]{}\\^|\"`"; EAT_MANY(state, index, (grapheme & ~0x7F) || !strchr(non_host_chars, (char)grapheme)); return (index - start_index); } -static int64_t match_authority(TextIter_t *state, int64_t index) -{ +static int64_t match_authority(TextIter_t *state, int64_t index) { int64_t authority_start = index; static const char *non_segment_chars = "/#?:@ \t\r\n<>[]{}\\^|\"`."; // Optional user@ prefix: int64_t username_len = EAT_MANY(state, index, (grapheme & ~0x7F) || !strchr(non_segment_chars, (char)grapheme)); - if (username_len < 1 || !EAT1(state, index, grapheme == '@')) - index = authority_start; // No user@ part + if (username_len < 1 || !EAT1(state, index, grapheme == '@')) index = authority_start; // No user@ part // Host: int64_t host_len = match_host(state, index); @@ -296,14 +274,12 @@ static int64_t match_authority(TextIter_t *state, int64_t index) // Port: if (EAT1(state, index, grapheme == ':')) { - if (EAT_MANY(state, index, !(grapheme & ~0x7F) && isdigit(grapheme)) == 0) - return -1; + if (EAT_MANY(state, index, !(grapheme & ~0x7F) && isdigit(grapheme)) == 0) return -1; } return (index - authority_start); } -static int64_t match_uri(TextIter_t *state, int64_t index) -{ +static int64_t match_uri(TextIter_t *state, int64_t index) { // URI = scheme ":" ["//" authority] path ["?" query] ["#" fragment] // scheme = [a-zA-Z] [a-zA-Z0-9+.-] // authority = [userinfo "@"] host [":" port] @@ -311,25 +287,22 @@ static int64_t match_uri(TextIter_t *state, int64_t index) if (index > 0) { // Don't match if we're not at a word edge: uint32_t prev_codepoint = Text$get_main_grapheme_fast(state, index - 1); - if (uc_is_property_alphabetic(prev_codepoint)) - return -1; + if (uc_is_property_alphabetic(prev_codepoint)) return -1; } int64_t start_index = index; // Scheme: - if (!EAT1(state, index, isalpha(grapheme))) - return -1; - EAT_MANY(state, index, !(grapheme & ~0x7F) && (isalnum(grapheme) || grapheme == '+' || grapheme == '.' || grapheme == '-')); - if (!match_grapheme(state, &index, ':')) - return -1; + if (!EAT1(state, index, isalpha(grapheme))) return -1; + EAT_MANY(state, index, + !(grapheme & ~0x7F) && (isalnum(grapheme) || grapheme == '+' || grapheme == '.' || grapheme == '-')); + if (!match_grapheme(state, &index, ':')) return -1; // Authority: int64_t authority_len; if (match_str(state, &index, "//")) { authority_len = match_authority(state, index); - if (authority_len > 0) - index += authority_len; + if (authority_len > 0) index += authority_len; } else { authority_len = 0; } @@ -344,92 +317,75 @@ static int64_t match_uri(TextIter_t *state, int64_t index) static const char *non_query = " \"#<>[]{}\\^`|"; EAT_MANY(state, index, (grapheme & ~0x7F) || !strchr(non_query, (char)grapheme)); } - + if (EAT1(state, index, grapheme == '#')) { // Fragment static const char *non_fragment = " \"#<>[]{}\\^`|"; EAT_MANY(state, index, (grapheme & ~0x7F) || !strchr(non_fragment, (char)grapheme)); } } - if (authority_len <= 0 && index == path_start) - return -1; + if (authority_len <= 0 && index == path_start) return -1; return index - start_index; } -static int64_t match_url(TextIter_t *state, int64_t index) -{ +static int64_t match_url(TextIter_t *state, int64_t index) { int64_t lookahead = index; - if (!(match_str(state, &lookahead, "https:") - || match_str(state, &lookahead, "http:") - || match_str(state, &lookahead, "ftp:") - || match_str(state, &lookahead, "wss:") - || match_str(state, &lookahead, "ws:"))) + if (!(match_str(state, &lookahead, "https:") || match_str(state, &lookahead, "http:") + || match_str(state, &lookahead, "ftp:") || match_str(state, &lookahead, "wss:") + || match_str(state, &lookahead, "ws:"))) return -1; return match_uri(state, index); } -static int64_t match_id(TextIter_t *state, int64_t index) -{ - if (!EAT1(state, index, uc_is_property((ucs4_t)grapheme, UC_PROPERTY_XID_START))) - return -1; +static int64_t match_id(TextIter_t *state, int64_t index) { + if (!EAT1(state, index, uc_is_property((ucs4_t)grapheme, UC_PROPERTY_XID_START))) return -1; return 1 + EAT_MANY(state, index, uc_is_property((ucs4_t)grapheme, UC_PROPERTY_XID_CONTINUE)); } -static int64_t match_int(TextIter_t *state, int64_t index) -{ +static int64_t match_int(TextIter_t *state, int64_t index) { int64_t negative = EAT1(state, index, grapheme == '-') ? 1 : 0; int64_t len = EAT_MANY(state, index, uc_is_property((ucs4_t)grapheme, UC_PROPERTY_DECIMAL_DIGIT)); return len > 0 ? negative + len : -1; } -static int64_t match_alphanumeric(TextIter_t *state, int64_t index) -{ +static int64_t match_alphanumeric(TextIter_t *state, int64_t index) { return EAT1(state, index, uc_is_property_alphabetic((ucs4_t)grapheme) || uc_is_property_numeric((ucs4_t)grapheme)) - ? 1 : -1; + ? 1 + : -1; } -static int64_t match_num(TextIter_t *state, int64_t index) -{ +static int64_t match_num(TextIter_t *state, int64_t index) { bool negative = EAT1(state, index, grapheme == '-') ? 1 : 0; - int64_t pre_decimal = EAT_MANY(state, index, - uc_is_property((ucs4_t)grapheme, UC_PROPERTY_DECIMAL_DIGIT)); + int64_t pre_decimal = EAT_MANY(state, index, uc_is_property((ucs4_t)grapheme, UC_PROPERTY_DECIMAL_DIGIT)); bool decimal = (EAT1(state, index, grapheme == '.') == 1); - int64_t post_decimal = decimal ? EAT_MANY(state, index, - uc_is_property((ucs4_t)grapheme, UC_PROPERTY_DECIMAL_DIGIT)) : 0; - if (pre_decimal == 0 && post_decimal == 0) - return -1; + int64_t post_decimal = + decimal ? EAT_MANY(state, index, uc_is_property((ucs4_t)grapheme, UC_PROPERTY_DECIMAL_DIGIT)) : 0; + if (pre_decimal == 0 && post_decimal == 0) return -1; return negative + pre_decimal + decimal + post_decimal; } -static int64_t match_newline(TextIter_t *state, int64_t index) -{ - if (index >= state->stack[0].text.length) - return -1; +static int64_t match_newline(TextIter_t *state, int64_t index) { + if (index >= state->stack[0].text.length) return -1; uint32_t grapheme = index >= state->stack[0].text.length ? 0 : Text$get_main_grapheme_fast(state, index); - if (grapheme == '\n') - return 1; - if (grapheme == '\r' && Text$get_grapheme_fast(state, index + 1) == '\n') - return 2; + if (grapheme == '\n') return 1; + if (grapheme == '\r' && Text$get_grapheme_fast(state, index + 1) == '\n') return 2; return -1; } -static int64_t match_pat(TextIter_t *state, int64_t index, pat_t pat) -{ +static int64_t match_pat(TextIter_t *state, int64_t index, pat_t pat) { Text_t text = state->stack[0].text; int32_t grapheme = index >= text.length ? 0 : Text$get_grapheme_fast(state, index); switch (pat.tag) { case PAT_START: { - if (index == 0) - return pat.negated ? -1 : 0; + if (index == 0) return pat.negated ? -1 : 0; return pat.negated ? 0 : -1; } case PAT_END: { - if (index >= text.length) - return pat.negated ? -1 : 0; + if (index >= text.length) return pat.negated ? -1 : 0; return pat.negated ? 0 : -1; } case PAT_ANY: { @@ -437,51 +393,40 @@ static int64_t match_pat(TextIter_t *state, int64_t index, pat_t pat) return (index < text.length) ? 1 : -1; } case PAT_GRAPHEME: { - if (index >= text.length) - return -1; - else if (grapheme == pat.grapheme) - return pat.negated ? -1 : 1; + if (index >= text.length) return -1; + else if (grapheme == pat.grapheme) return pat.negated ? -1 : 1; return pat.negated ? 1 : -1; } case PAT_PROPERTY: { - if (index >= text.length) - return -1; - else if (uc_is_property((ucs4_t)grapheme, pat.property)) - return pat.negated ? -1 : 1; + if (index >= text.length) return -1; + else if (uc_is_property((ucs4_t)grapheme, pat.property)) return pat.negated ? -1 : 1; return pat.negated ? 1 : -1; } case PAT_PAIR: { // Nested punctuation: (?), [?], etc - if (index >= text.length) - return -1; + if (index >= text.length) return -1; int32_t open = pat.pair_graphemes[0]; - if (grapheme != open) - return pat.negated ? 1 : -1; + if (grapheme != open) return pat.negated ? 1 : -1; int32_t close = pat.pair_graphemes[1]; int64_t depth = 1; int64_t match_len = 1; for (; depth > 0; match_len++) { - if (index + match_len >= text.length) - return pat.negated ? 1 : -1; + if (index + match_len >= text.length) return pat.negated ? 1 : -1; int32_t c = Text$get_grapheme_fast(state, index + match_len); - if (c == open) - depth += 1; - else if (c == close) - depth -= 1; + if (c == open) depth += 1; + else if (c == close) depth -= 1; } return pat.negated ? -1 : match_len; } case PAT_QUOTE: { // Nested quotes: "?", '?', etc - if (index >= text.length) - return -1; + if (index >= text.length) return -1; int32_t open = pat.quote_graphemes[0]; - if (grapheme != open) - return pat.negated ? 1 : -1; + if (grapheme != open) return pat.negated ? 1 : -1; int32_t close = pat.quote_graphemes[1]; for (int64_t i = index + 1; i < text.length; i++) { @@ -496,8 +441,7 @@ static int64_t match_pat(TextIter_t *state, int64_t index, pat_t pat) } case PAT_FUNCTION: { int64_t match_len = pat.fn(state, index); - if (match_len >= 0) - return pat.negated ? -1 : match_len; + if (match_len >= 0) return pat.negated ? -1 : match_len; return pat.negated ? 1 : -1; } default: errx(1, "Invalid pattern"); @@ -506,37 +450,32 @@ static int64_t match_pat(TextIter_t *state, int64_t index, pat_t pat) return 0; } -static pat_t parse_next_pat(TextIter_t *state, int64_t *index) -{ - if (EAT2(state, *index, - uc_is_property((ucs4_t)grapheme, UC_PROPERTY_QUOTATION_MARK), - grapheme == '?')) { +static pat_t parse_next_pat(TextIter_t *state, int64_t *index) { + if (EAT2(state, *index, uc_is_property((ucs4_t)grapheme, UC_PROPERTY_QUOTATION_MARK), grapheme == '?')) { // Quotations: "?", '?', etc - int32_t open = Text$get_grapheme_fast(state, *index-2); + int32_t open = Text$get_grapheme_fast(state, *index - 2); int32_t close = open; - uc_mirror_char((ucs4_t)open, (ucs4_t*)&close); - if (!match_grapheme(state, index, close)) - fail("Pattern's closing quote is missing: ", state->stack[0].text); + uc_mirror_char((ucs4_t)open, (ucs4_t *)&close); + if (!match_grapheme(state, index, close)) fail("Pattern's closing quote is missing: ", state->stack[0].text); return (pat_t){ - .tag=PAT_QUOTE, - .min=1, .max=1, - .quote_graphemes={open, close}, + .tag = PAT_QUOTE, + .min = 1, + .max = 1, + .quote_graphemes = {open, close}, }; - } else if (EAT2(state, *index, - uc_is_property((ucs4_t)grapheme, UC_PROPERTY_PAIRED_PUNCTUATION), - grapheme == '?')) { + } else if (EAT2(state, *index, uc_is_property((ucs4_t)grapheme, UC_PROPERTY_PAIRED_PUNCTUATION), grapheme == '?')) { // Nested punctuation: (?), [?], etc - int32_t open = Text$get_grapheme_fast(state, *index-2); + int32_t open = Text$get_grapheme_fast(state, *index - 2); int32_t close = open; - uc_mirror_char((ucs4_t)open, (ucs4_t*)&close); - if (!match_grapheme(state, index, close)) - fail("Pattern's closing brace is missing: ", state->stack[0].text); - + uc_mirror_char((ucs4_t)open, (ucs4_t *)&close); + if (!match_grapheme(state, index, close)) fail("Pattern's closing brace is missing: ", state->stack[0].text); + return (pat_t){ - .tag=PAT_PAIR, - .min=1, .max=1, - .pair_graphemes={open, close}, + .tag = PAT_PAIR, + .min = 1, + .max = 1, + .pair_graphemes = {open, close}, }; } else if (EAT1(state, *index, grapheme == '{')) { // named patterns {id}, {2-3 hex}, etc. skip_whitespace(state, index); @@ -559,133 +498,127 @@ static pat_t parse_next_pat(TextIter_t *state, int64_t *index) skip_whitespace(state, index); bool negated = match_grapheme(state, index, '!'); -#define PAT(_tag, ...) ((pat_t){.min=min, .max=max, .negated=negated, .tag=_tag, __VA_ARGS__}) +#define PAT(_tag, ...) ((pat_t){.min = min, .max = max, .negated = negated, .tag = _tag, __VA_ARGS__}) const char *prop_name; - if (match_str(state, index, "..")) - prop_name = ".."; - else - prop_name = get_property_name(state, index); + if (match_str(state, index, "..")) prop_name = ".."; + else prop_name = get_property_name(state, index); if (!prop_name) { // Literal character, e.g. {1?} skip_whitespace(state, index); int32_t grapheme = Text$get_grapheme_fast(state, (*index)++); - if (!match_grapheme(state, index, '}')) - fail("Missing closing '}' in pattern: ", state->stack[0].text); - return PAT(PAT_GRAPHEME, .grapheme=grapheme); + if (!match_grapheme(state, index, '}')) fail("Missing closing '}' in pattern: ", state->stack[0].text); + return PAT(PAT_GRAPHEME, .grapheme = grapheme); } else if (strlen(prop_name) == 1) { // Single letter names: {1+ A} skip_whitespace(state, index); - if (!match_grapheme(state, index, '}')) - fail("Missing closing '}' in pattern: ", state->stack[0].text); - return PAT(PAT_GRAPHEME, .grapheme=prop_name[0]); + if (!match_grapheme(state, index, '}')) fail("Missing closing '}' in pattern: ", state->stack[0].text); + return PAT(PAT_GRAPHEME, .grapheme = prop_name[0]); } skip_whitespace(state, index); - if (!match_grapheme(state, index, '}')) - fail("Missing closing '}' in pattern: ", state->stack[0].text); + if (!match_grapheme(state, index, '}')) fail("Missing closing '}' in pattern: ", state->stack[0].text); switch (tolower(prop_name[0])) { case '.': if (prop_name[1] == '.') { - if (negated) - return ((pat_t){.tag=PAT_END, .min=min, .max=max, .non_capturing=true}); - else - return PAT(PAT_ANY); + if (negated) return ((pat_t){.tag = PAT_END, .min = min, .max = max, .non_capturing = true}); + else return PAT(PAT_ANY); } break; case 'a': if (strcasecmp(prop_name, "authority") == 0) { - return PAT(PAT_FUNCTION, .fn=match_authority); + return PAT(PAT_FUNCTION, .fn = match_authority); } else if (strcasecmp(prop_name, "alphanum") == 0 || strcasecmp(prop_name, "anum") == 0 || strcasecmp(prop_name, "alphanumeric") == 0) { - return PAT(PAT_FUNCTION, .fn=match_alphanumeric); + return PAT(PAT_FUNCTION, .fn = match_alphanumeric); } break; - case 'c': - if (strcasecmp(prop_name, "crlf") == 0) - return PAT(PAT_FUNCTION, .fn=match_newline); + case 'c': + if (strcasecmp(prop_name, "crlf") == 0) return PAT(PAT_FUNCTION, .fn = match_newline); break; case 'd': if (strcasecmp(prop_name, "digit") == 0) { - return PAT(PAT_PROPERTY, .property=UC_PROPERTY_DECIMAL_DIGIT); + return PAT(PAT_PROPERTY, .property = UC_PROPERTY_DECIMAL_DIGIT); } break; case 'e': if (strcasecmp(prop_name, "end") == 0) { - return PAT(PAT_END, .non_capturing=!negated); + return PAT(PAT_END, .non_capturing = !negated); } else if (strcasecmp(prop_name, "email") == 0) { - return PAT(PAT_FUNCTION, .fn=match_email); + return PAT(PAT_FUNCTION, .fn = match_email); } #if _LIBUNISTRING_VERSION >= 0x0100000 else if (strcasecmp(prop_name, "emoji") == 0) { - return PAT(PAT_PROPERTY, .property=UC_PROPERTY_EMOJI); + return PAT(PAT_PROPERTY, .property = UC_PROPERTY_EMOJI); } #endif break; case 'h': if (strcasecmp(prop_name, "host") == 0) { - return PAT(PAT_FUNCTION, .fn=match_host); + return PAT(PAT_FUNCTION, .fn = match_host); } break; case 'i': if (strcasecmp(prop_name, "id") == 0) { - return PAT(PAT_FUNCTION, .fn=match_id); + return PAT(PAT_FUNCTION, .fn = match_id); } else if (strcasecmp(prop_name, "int") == 0) { - return PAT(PAT_FUNCTION, .fn=match_int); + return PAT(PAT_FUNCTION, .fn = match_int); } else if (strcasecmp(prop_name, "ipv4") == 0) { - return PAT(PAT_FUNCTION, .fn=match_ipv4); + return PAT(PAT_FUNCTION, .fn = match_ipv4); } else if (strcasecmp(prop_name, "ipv6") == 0) { - return PAT(PAT_FUNCTION, .fn=match_ipv6); + return PAT(PAT_FUNCTION, .fn = match_ipv6); } else if (strcasecmp(prop_name, "ip") == 0) { - return PAT(PAT_FUNCTION, .fn=match_ip); + return PAT(PAT_FUNCTION, .fn = match_ip); } break; case 'n': if (strcasecmp(prop_name, "nl") == 0 || strcasecmp(prop_name, "newline") == 0) { - return PAT(PAT_FUNCTION, .fn=match_newline); + return PAT(PAT_FUNCTION, .fn = match_newline); } else if (strcasecmp(prop_name, "num") == 0) { - return PAT(PAT_FUNCTION, .fn=match_num); + return PAT(PAT_FUNCTION, .fn = match_num); } break; case 's': if (strcasecmp(prop_name, "start") == 0) { - return PAT(PAT_START, .non_capturing=!negated); + return PAT(PAT_START, .non_capturing = !negated); } break; case 'u': if (strcasecmp(prop_name, "uri") == 0) { - return PAT(PAT_FUNCTION, .fn=match_uri); + return PAT(PAT_FUNCTION, .fn = match_uri); } else if (strcasecmp(prop_name, "url") == 0) { - return PAT(PAT_FUNCTION, .fn=match_url); + return PAT(PAT_FUNCTION, .fn = match_url); } break; case 'w': if (strcasecmp(prop_name, "word") == 0) { - return PAT(PAT_FUNCTION, .fn=match_id); + return PAT(PAT_FUNCTION, .fn = match_id); } else if (strcasecmp(prop_name, "ws") == 0 || strcasecmp(prop_name, "whitespace") == 0) { - return PAT(PAT_PROPERTY, .property=UC_PROPERTY_WHITE_SPACE); + return PAT(PAT_PROPERTY, .property = UC_PROPERTY_WHITE_SPACE); } break; default: break; } uc_property_t prop = uc_property_byname(prop_name); - if (uc_property_is_valid(prop)) - return PAT(PAT_PROPERTY, .property=prop); + if (uc_property_is_valid(prop)) return PAT(PAT_PROPERTY, .property = prop); ucs4_t grapheme = unicode_name_character(prop_name); - if (grapheme == UNINAME_INVALID) - fail("Not a valid property or character name: ", prop_name); - return PAT(PAT_GRAPHEME, .grapheme=(int32_t)grapheme); + if (grapheme == UNINAME_INVALID) fail("Not a valid property or character name: ", prop_name); + return PAT(PAT_GRAPHEME, .grapheme = (int32_t)grapheme); #undef PAT } else { - return (pat_t){.tag=PAT_GRAPHEME, .non_capturing=true, .min=1, .max=1, .grapheme=Text$get_grapheme_fast(state, (*index)++)}; + return (pat_t){.tag = PAT_GRAPHEME, + .non_capturing = true, + .min = 1, + .max = 1, + .grapheme = Text$get_grapheme_fast(state, (*index)++)}; } } -static int64_t match(Text_t text, int64_t text_index, Text_t pattern, int64_t pattern_index, capture_t *captures, int64_t capture_index) -{ +static int64_t match(Text_t text, int64_t text_index, Text_t pattern, int64_t pattern_index, capture_t *captures, + int64_t capture_index) { if (pattern_index >= pattern.length) // End of the pattern return 0; @@ -713,7 +646,8 @@ static int64_t match(Text_t text, int64_t text_index, Text_t pattern, int64_t pa } if (pat.min == 0 && pattern_index < pattern.length) { - next_match_len = match(text, text_index, pattern, pattern_index, captures, capture_index + (pat.non_capturing ? 0 : 1)); + next_match_len = + match(text, text_index, pattern, pattern_index, captures, capture_index + (pat.non_capturing ? 0 : 1)); if (next_match_len >= 0) { capture_len = 0; goto success; @@ -722,17 +656,16 @@ static int64_t match(Text_t text, int64_t text_index, Text_t pattern, int64_t pa while (count < pat.max) { int64_t match_len = match_pat(&text_state, text_index, pat); - if (match_len < 0) - break; + if (match_len < 0) break; capture_len += match_len; text_index += match_len; count += 1; if (pattern_index < pattern.length) { // More stuff after this - if (count < pat.min) - next_match_len = -1; + if (count < pat.min) next_match_len = -1; else - next_match_len = match(text, text_index, pattern, pattern_index, captures, capture_index + (pat.non_capturing ? 0 : 1)); + next_match_len = match(text, text_index, pattern, pattern_index, captures, + capture_index + (pat.non_capturing ? 0 : 1)); } else { next_match_len = 0; } @@ -748,32 +681,29 @@ static int64_t match(Text_t text, int64_t text_index, Text_t pattern, int64_t pa } } - if (pattern_index < pattern.length && next_match_len >= 0) - break; // Next guy exists and wants to stop here + if (pattern_index < pattern.length && next_match_len >= 0) break; // Next guy exists and wants to stop here - if (text_index >= text.length) - break; + if (text_index >= text.length) break; } - if (count < pat.min || next_match_len < 0) - return -1; + if (count < pat.min || next_match_len < 0) return -1; - success: +success: if (captures && capture_index < MAX_BACKREFS && !pat.non_capturing) { if (pat.tag == PAT_PAIR || pat.tag == PAT_QUOTE) { assert(capture_len > 0); captures[capture_index] = (capture_t){ - .index=capture_start + 1, // Skip leading quote/paren - .length=capture_len - 2, // Skip open/close - .occupied=true, - .recursive=(pat.tag == PAT_PAIR), + .index = capture_start + 1, // Skip leading quote/paren + .length = capture_len - 2, // Skip open/close + .occupied = true, + .recursive = (pat.tag == PAT_PAIR), }; } else { captures[capture_index] = (capture_t){ - .index=capture_start, - .length=capture_len, - .occupied=true, - .recursive=false, + .index = capture_start, + .length = capture_len, + .occupied = true, + .recursive = false, }; } } @@ -784,11 +714,10 @@ static int64_t match(Text_t text, int64_t text_index, Text_t pattern, int64_t pa #undef EAT2 #undef EAT_MANY -static int64_t _find(Text_t text, Text_t pattern, int64_t first, int64_t last, int64_t *match_length, capture_t *captures) -{ +static int64_t _find(Text_t text, Text_t pattern, int64_t first, int64_t last, int64_t *match_length, + capture_t *captures) { int32_t first_grapheme = Text$get_grapheme(pattern, 0); - bool find_first = (first_grapheme != '{' - && !uc_is_property((ucs4_t)first_grapheme, UC_PROPERTY_QUOTATION_MARK) + bool find_first = (first_grapheme != '{' && !uc_is_property((ucs4_t)first_grapheme, UC_PROPERTY_QUOTATION_MARK) && !uc_is_property((ucs4_t)first_grapheme, UC_PROPERTY_PAIRED_PUNCTUATION)); TextIter_t text_state = NEW_TEXT_ITER_STATE(text); @@ -801,111 +730,97 @@ static int64_t _find(Text_t text, Text_t pattern, int64_t first, int64_t last, i int64_t m = match(text, i, pattern, 0, captures, 0); if (m >= 0) { - if (match_length) - *match_length = m; + if (match_length) *match_length = m; return i; } } - if (match_length) - *match_length = -1; + if (match_length) *match_length = -1; return -1; } -static OptionalPatternMatch find(Text_t text, Text_t pattern, Int_t from_index) -{ +static OptionalPatternMatch find(Text_t text, Text_t pattern, Int_t from_index) { int64_t first = Int64$from_int(from_index, false); if (first == 0) fail("Invalid index: 0"); if (first < 0) first = text.length + first + 1; - if (first > text.length || first < 1) - return NONE_MATCH; + if (first > text.length || first < 1) return NONE_MATCH; capture_t captures[MAX_BACKREFS] = {}; int64_t len = 0; - int64_t found = _find(text, pattern, first-1, text.length-1, &len, captures); - if (found == -1) - return NONE_MATCH; + int64_t found = _find(text, pattern, first - 1, text.length - 1, &len, captures); + if (found == -1) return NONE_MATCH; List_t capture_list = {}; for (int i = 0; captures[i].occupied; i++) { - Text_t capture = Text$slice(text, I(captures[i].index+1), I(captures[i].index+captures[i].length)); + Text_t capture = Text$slice(text, I(captures[i].index + 1), I(captures[i].index + captures[i].length)); List$insert(&capture_list, &capture, I(0), sizeof(Text_t)); } return (OptionalPatternMatch){ - .text=Text$slice(text, I(found+1), I(found+len)), - .index=I(found+1), - .captures=capture_list, + .text = Text$slice(text, I(found + 1), I(found + len)), + .index = I(found + 1), + .captures = capture_list, }; } -PUREFUNC static bool Pattern$has(Text_t text, Text_t pattern) -{ +PUREFUNC static bool Pattern$has(Text_t text, Text_t pattern) { if (Text$starts_with(pattern, Text("{start}"), &pattern)) { int64_t m = match(text, 0, pattern, 0, NULL, 0); return m >= 0; } else if (Text$ends_with(text, Text("{end}"), NULL)) { - for (int64_t i = text.length-1; i >= 0; i--) { + for (int64_t i = text.length - 1; i >= 0; i--) { int64_t match_len = match(text, i, pattern, 0, NULL, 0); - if (match_len >= 0 && i + match_len == text.length) - return true; + if (match_len >= 0 && i + match_len == text.length) return true; } return false; } else { - int64_t found = _find(text, pattern, 0, text.length-1, NULL, NULL); + int64_t found = _find(text, pattern, 0, text.length - 1, NULL, NULL); return (found >= 0); } } -static bool Pattern$matches(Text_t text, Text_t pattern) -{ +static bool Pattern$matches(Text_t text, Text_t pattern) { capture_t captures[MAX_BACKREFS] = {}; int64_t match_len = match(text, 0, pattern, 0, NULL, 0); return (match_len == text.length); } -static bool Pattern$match_at(Text_t text, Text_t pattern, Int_t pos, PatternMatch *dest) -{ +static bool Pattern$match_at(Text_t text, Text_t pattern, Int_t pos, PatternMatch *dest) { int64_t start = Int64$from_int(pos, false) - 1; capture_t captures[MAX_BACKREFS] = {}; int64_t match_len = match(text, start, pattern, 0, captures, 0); - if (match_len < 0) - return false; + if (match_len < 0) return false; List_t capture_list = {}; for (int i = 0; captures[i].occupied; i++) { - Text_t capture = Text$slice(text, I(captures[i].index+1), I(captures[i].index+captures[i].length)); + Text_t capture = Text$slice(text, I(captures[i].index + 1), I(captures[i].index + captures[i].length)); List$insert(&capture_list, &capture, I(0), sizeof(Text_t)); } - dest->text = Text$slice(text, I(start+1), I(start+match_len)); - dest->index = I(start+1); + dest->text = Text$slice(text, I(start + 1), I(start + match_len)); + dest->index = I(start + 1); dest->captures = capture_list; return true; } -static OptionalList_t Pattern$captures(Text_t text, Text_t pattern) -{ +static OptionalList_t Pattern$captures(Text_t text, Text_t pattern) { capture_t captures[MAX_BACKREFS] = {}; int64_t match_len = match(text, 0, pattern, 0, captures, 0); - if (match_len != text.length) - return NONE_LIST; + if (match_len != text.length) return NONE_LIST; List_t capture_list = {}; for (int i = 0; captures[i].occupied; i++) { - Text_t capture = Text$slice(text, I(captures[i].index+1), I(captures[i].index+captures[i].length)); + Text_t capture = Text$slice(text, I(captures[i].index + 1), I(captures[i].index + captures[i].length)); List$insert(&capture_list, &capture, I(0), sizeof(Text_t)); } return capture_list; } -static List_t Pattern$find_all(Text_t text, Text_t pattern) -{ +static List_t Pattern$find_all(Text_t text, Text_t pattern) { if (pattern.length == 0) // special case - return (List_t){.length=0}; + return (List_t){.length = 0}; List_t matches = {}; - for (int64_t i = 1; ; ) { + for (int64_t i = 1;;) { OptionalPatternMatch m = find(text, pattern, I(i)); - if (m.is_none) - break; + if (m.is_none) break; i = Int64$from_int(m.index, false) + m.text.length; List$insert(&matches, &m, I_small(0), sizeof(PatternMatch)); } @@ -918,41 +833,35 @@ typedef struct { Text_t pattern; } match_iter_state_t; -static OptionalPatternMatch next_match(match_iter_state_t *state) -{ - if (Int64$from_int(state->i, false) > state->state.stack[0].text.length) - return NONE_MATCH; +static OptionalPatternMatch next_match(match_iter_state_t *state) { + if (Int64$from_int(state->i, false) > state->state.stack[0].text.length) return NONE_MATCH; OptionalPatternMatch m = find(state->state.stack[0].text, state->pattern, state->i); if (m.is_none) // No match state->i = I(state->state.stack[0].text.length + 1); - else - state->i = Int$plus(m.index, I(MAX(1, m.text.length))); + else state->i = Int$plus(m.index, I(MAX(1, m.text.length))); return m; } -static Closure_t Pattern$by_match(Text_t text, Text_t pattern) -{ +static Closure_t Pattern$by_match(Text_t text, Text_t pattern) { return (Closure_t){ - .fn=(void*)next_match, - .userdata=new(match_iter_state_t, .state=NEW_TEXT_ITER_STATE(text), .i=I_small(1), .pattern=pattern), + .fn = (void *)next_match, + .userdata = new (match_iter_state_t, .state = NEW_TEXT_ITER_STATE(text), .i = I_small(1), .pattern = pattern), }; } -static Text_t apply_backrefs(Text_t text, List_t recursive_replacements, Text_t replacement, Text_t backref_pat, capture_t *captures) -{ - if (backref_pat.length == 0) - return replacement; +static Text_t apply_backrefs(Text_t text, List_t recursive_replacements, Text_t replacement, Text_t backref_pat, + capture_t *captures) { + if (backref_pat.length == 0) return replacement; int32_t first_grapheme = Text$get_grapheme(backref_pat, 0); - bool find_first = (first_grapheme != '{' - && !uc_is_property((ucs4_t)first_grapheme, UC_PROPERTY_QUOTATION_MARK) + bool find_first = (first_grapheme != '{' && !uc_is_property((ucs4_t)first_grapheme, UC_PROPERTY_QUOTATION_MARK) && !uc_is_property((ucs4_t)first_grapheme, UC_PROPERTY_PAIRED_PUNCTUATION)); Text_t ret = Text(""); TextIter_t replacement_state = NEW_TEXT_ITER_STATE(replacement); int64_t nonmatching_pos = 0; - for (int64_t pos = 0; pos < replacement.length; ) { + for (int64_t pos = 0; pos < replacement.length;) { // Optimization: quickly skip ahead to first char in the backref pattern: if (find_first) { while (pos < replacement.length && Text$get_grapheme_fast(&replacement_state, pos) != first_grapheme) @@ -971,22 +880,23 @@ static Text_t apply_backrefs(Text_t text, List_t recursive_replacements, Text_t pos += 1; continue; } - if (backref < 0 || backref > 9) fail("Invalid backref index: ", backref, " (only 0-", MAX_BACKREFS-1, " are allowed)"); + if (backref < 0 || backref > 9) + fail("Invalid backref index: ", backref, " (only 0-", MAX_BACKREFS - 1, " are allowed)"); backref_len = (after_backref - pos); if (Text$get_grapheme_fast(&replacement_state, pos + backref_len) == ';') backref_len += 1; // skip optional semicolon - if (!captures[backref].occupied) - fail("There is no capture number ", backref, "!"); + if (!captures[backref].occupied) fail("There is no capture number ", backref, "!"); - Text_t backref_text = Text$slice(text, I(captures[backref].index+1), I(captures[backref].index + captures[backref].length)); + Text_t backref_text = + Text$slice(text, I(captures[backref].index + 1), I(captures[backref].index + captures[backref].length)); if (captures[backref].recursive && recursive_replacements.length > 0) backref_text = replace_list(backref_text, recursive_replacements, backref_pat, true); if (pos > nonmatching_pos) { - Text_t before_slice = Text$slice(replacement, I(nonmatching_pos+1), I(pos)); + Text_t before_slice = Text$slice(replacement, I(nonmatching_pos + 1), I(pos)); ret = Text$concat(ret, before_slice, backref_text); } else { ret = Text$concat(ret, backref_text); @@ -996,31 +906,29 @@ static Text_t apply_backrefs(Text_t text, List_t recursive_replacements, Text_t nonmatching_pos = pos; } if (nonmatching_pos < replacement.length) { - Text_t last_slice = Text$slice(replacement, I(nonmatching_pos+1), I(replacement.length)); + Text_t last_slice = Text$slice(replacement, I(nonmatching_pos + 1), I(replacement.length)); ret = Text$concat(ret, last_slice); } return ret; } -static Text_t Pattern$replace(Text_t text, Text_t pattern, Text_t replacement, Text_t backref_pat, bool recursive) -{ +static Text_t Pattern$replace(Text_t text, Text_t pattern, Text_t replacement, Text_t backref_pat, bool recursive) { Text_t ret = EMPTY_TEXT; int32_t first_grapheme = Text$get_grapheme(pattern, 0); - bool find_first = (first_grapheme != '{' - && !uc_is_property((ucs4_t)first_grapheme, UC_PROPERTY_QUOTATION_MARK) + bool find_first = (first_grapheme != '{' && !uc_is_property((ucs4_t)first_grapheme, UC_PROPERTY_QUOTATION_MARK) && !uc_is_property((ucs4_t)first_grapheme, UC_PROPERTY_PAIRED_PUNCTUATION)); Text_t entries[2] = {pattern, replacement}; List_t replacements = { - .data=entries, - .length=1, - .stride=sizeof(entries), + .data = entries, + .length = 1, + .stride = sizeof(entries), }; TextIter_t text_state = NEW_TEXT_ITER_STATE(text); int64_t nonmatching_pos = 0; - for (int64_t pos = 0; pos < text.length; ) { + for (int64_t pos = 0; pos < text.length;) { // Optimization: quickly skip ahead to first char in pattern: if (find_first) { while (pos < text.length && Text$get_grapheme_fast(&text_state, pos) != first_grapheme) @@ -1034,13 +942,16 @@ static Text_t Pattern$replace(Text_t text, Text_t pattern, Text_t replacement, T continue; } captures[0] = (capture_t){ - .index = pos, .length = match_len, - .occupied = true, .recursive = false, + .index = pos, + .length = match_len, + .occupied = true, + .recursive = false, }; - Text_t replacement_text = apply_backrefs(text, recursive ? replacements : (List_t){}, replacement, backref_pat, captures); + Text_t replacement_text = + apply_backrefs(text, recursive ? replacements : (List_t){}, replacement, backref_pat, captures); if (pos > nonmatching_pos) { - Text_t before_slice = Text$slice(text, I(nonmatching_pos+1), I(pos)); + Text_t before_slice = Text$slice(text, I(nonmatching_pos + 1), I(pos)); ret = Text$concat(ret, before_slice, replacement_text); } else { ret = Text$concat(ret, replacement_text); @@ -1049,44 +960,39 @@ static Text_t Pattern$replace(Text_t text, Text_t pattern, Text_t replacement, T pos += MAX(match_len, 1); } if (nonmatching_pos < text.length) { - Text_t last_slice = Text$slice(text, I(nonmatching_pos+1), I(text.length)); + Text_t last_slice = Text$slice(text, I(nonmatching_pos + 1), I(text.length)); ret = Text$concat(ret, last_slice); } return ret; } -static Text_t Pattern$trim(Text_t text, Text_t pattern, bool trim_left, bool trim_right) -{ - int64_t first = 0, last = text.length-1; +static Text_t Pattern$trim(Text_t text, Text_t pattern, bool trim_left, bool trim_right) { + int64_t first = 0, last = text.length - 1; if (trim_left) { int64_t match_len = match(text, 0, pattern, 0, NULL, 0); - if (match_len > 0) - first = match_len; + if (match_len > 0) first = match_len; } if (trim_right) { - for (int64_t i = text.length-1; i >= first; i--) { + for (int64_t i = text.length - 1; i >= first; i--) { int64_t match_len = match(text, i, pattern, 0, NULL, 0); - if (match_len > 0 && i + match_len == text.length) - last = i-1; + if (match_len > 0 && i + match_len == text.length) last = i - 1; } } - return Text$slice(text, I(first+1), I(last+1)); + return Text$slice(text, I(first + 1), I(last + 1)); } -static Text_t Pattern$map(Text_t text, Text_t pattern, Closure_t fn, bool recursive) -{ +static Text_t Pattern$map(Text_t text, Text_t pattern, Closure_t fn, bool recursive) { Text_t ret = EMPTY_TEXT; int32_t first_grapheme = Text$get_grapheme(pattern, 0); - bool find_first = (first_grapheme != '{' - && !uc_is_property((ucs4_t)first_grapheme, UC_PROPERTY_QUOTATION_MARK) + bool find_first = (first_grapheme != '{' && !uc_is_property((ucs4_t)first_grapheme, UC_PROPERTY_QUOTATION_MARK) && !uc_is_property((ucs4_t)first_grapheme, UC_PROPERTY_PAIRED_PUNCTUATION)); TextIter_t text_state = NEW_TEXT_ITER_STATE(text); int64_t nonmatching_pos = 0; - Text_t (*text_mapper)(PatternMatch, void*) = fn.fn; + Text_t (*text_mapper)(PatternMatch, void *) = fn.fn; for (int64_t pos = 0; pos < text.length; pos++) { // Optimization: quickly skip ahead to first char in pattern: if (find_first) { @@ -1099,20 +1005,19 @@ static Text_t Pattern$map(Text_t text, Text_t pattern, Closure_t fn, bool recurs if (match_len < 0) continue; PatternMatch m = { - .text=Text$slice(text, I(pos+1), I(pos+match_len)), - .index=I(pos+1), - .captures={}, + .text = Text$slice(text, I(pos + 1), I(pos + match_len)), + .index = I(pos + 1), + .captures = {}, }; for (int i = 0; captures[i].occupied; i++) { - Text_t capture = Text$slice(text, I(captures[i].index+1), I(captures[i].index+captures[i].length)); - if (recursive) - capture = Pattern$map(capture, pattern, fn, recursive); + Text_t capture = Text$slice(text, I(captures[i].index + 1), I(captures[i].index + captures[i].length)); + if (recursive) capture = Pattern$map(capture, pattern, fn, recursive); List$insert(&m.captures, &capture, I(0), sizeof(Text_t)); } Text_t replacement = text_mapper(m, fn.userdata); if (pos > nonmatching_pos) { - Text_t before_slice = Text$slice(text, I(nonmatching_pos+1), I(pos)); + Text_t before_slice = Text$slice(text, I(nonmatching_pos + 1), I(pos)); ret = Text$concat(ret, before_slice, replacement); } else { ret = Text$concat(ret, replacement); @@ -1121,21 +1026,19 @@ static Text_t Pattern$map(Text_t text, Text_t pattern, Closure_t fn, bool recurs pos += (match_len - 1); } if (nonmatching_pos < text.length) { - Text_t last_slice = Text$slice(text, I(nonmatching_pos+1), I(text.length)); + Text_t last_slice = Text$slice(text, I(nonmatching_pos + 1), I(text.length)); ret = Text$concat(ret, last_slice); } return ret; } -static void Pattern$each(Text_t text, Text_t pattern, Closure_t fn, bool recursive) -{ +static void Pattern$each(Text_t text, Text_t pattern, Closure_t fn, bool recursive) { int32_t first_grapheme = Text$get_grapheme(pattern, 0); - bool find_first = (first_grapheme != '{' - && !uc_is_property((ucs4_t)first_grapheme, UC_PROPERTY_QUOTATION_MARK) + bool find_first = (first_grapheme != '{' && !uc_is_property((ucs4_t)first_grapheme, UC_PROPERTY_QUOTATION_MARK) && !uc_is_property((ucs4_t)first_grapheme, UC_PROPERTY_PAIRED_PUNCTUATION)); TextIter_t text_state = NEW_TEXT_ITER_STATE(text); - void (*action)(PatternMatch, void*) = fn.fn; + void (*action)(PatternMatch, void *) = fn.fn; for (int64_t pos = 0; pos < text.length; pos++) { // Optimization: quickly skip ahead to first char in pattern: if (find_first) { @@ -1148,14 +1051,13 @@ static void Pattern$each(Text_t text, Text_t pattern, Closure_t fn, bool recursi if (match_len < 0) continue; PatternMatch m = { - .text=Text$slice(text, I(pos+1), I(pos+match_len)), - .index=I(pos+1), - .captures={}, + .text = Text$slice(text, I(pos + 1), I(pos + match_len)), + .index = I(pos + 1), + .captures = {}, }; for (int i = 0; captures[i].occupied; i++) { - Text_t capture = Text$slice(text, I(captures[i].index+1), I(captures[i].index+captures[i].length)); - if (recursive) - Pattern$each(capture, pattern, fn, recursive); + Text_t capture = Text$slice(text, I(captures[i].index + 1), I(captures[i].index + captures[i].length)); + if (recursive) Pattern$each(capture, pattern, fn, recursive); List$insert(&m.captures, &capture, I(0), sizeof(Text_t)); } @@ -1164,17 +1066,16 @@ static void Pattern$each(Text_t text, Text_t pattern, Closure_t fn, bool recursi } } -Text_t replace_list(Text_t text, List_t replacements, Text_t backref_pat, bool recursive) -{ +Text_t replace_list(Text_t text, List_t replacements, Text_t backref_pat, bool recursive) { if (replacements.length == 0) return text; Text_t ret = EMPTY_TEXT; int64_t nonmatch_pos = 0; - for (int64_t pos = 0; pos < text.length; ) { + for (int64_t pos = 0; pos < text.length;) { // Find the first matching pattern at this position: for (int64_t i = 0; i < replacements.length; i++) { - Text_t pattern = *(Text_t*)(replacements.data + i*replacements.stride); + Text_t pattern = *(Text_t *)(replacements.data + i * replacements.stride); capture_t captures[MAX_BACKREFS] = {}; int64_t len = match(text, pos, pattern, 0, captures, 1); if (len < 0) continue; @@ -1183,13 +1084,14 @@ Text_t replace_list(Text_t text, List_t replacements, Text_t backref_pat, bool r // If we skipped over some non-matching text before finding a match, insert it here: if (pos > nonmatch_pos) { - Text_t before_slice = Text$slice(text, I(nonmatch_pos+1), I(pos)); + Text_t before_slice = Text$slice(text, I(nonmatch_pos + 1), I(pos)); ret = Text$concat(ret, before_slice); } // Concatenate the replacement: - Text_t replacement = *(Text_t*)(replacements.data + i*replacements.stride + sizeof(Text_t)); - Text_t replacement_text = apply_backrefs(text, recursive ? replacements : (List_t){}, replacement, backref_pat, captures); + Text_t replacement = *(Text_t *)(replacements.data + i * replacements.stride + sizeof(Text_t)); + Text_t replacement_text = + apply_backrefs(text, recursive ? replacements : (List_t){}, replacement, backref_pat, captures); ret = Text$concat(ret, replacement_text); pos += MAX(len, 1); nonmatch_pos = pos; @@ -1197,26 +1099,24 @@ Text_t replace_list(Text_t text, List_t replacements, Text_t backref_pat, bool r } pos += 1; - next_pos: + next_pos: continue; } if (nonmatch_pos <= text.length) { - Text_t last_slice = Text$slice(text, I(nonmatch_pos+1), I(text.length)); + Text_t last_slice = Text$slice(text, I(nonmatch_pos + 1), I(text.length)); ret = Text$concat(ret, last_slice); } return ret; } -static Text_t Pattern$replace_all(Text_t text, Table_t replacements, Text_t backref_pat, bool recursive) -{ +static Text_t Pattern$replace_all(Text_t text, Table_t replacements, Text_t backref_pat, bool recursive) { return replace_list(text, replacements.entries, backref_pat, recursive); } -static List_t Pattern$split(Text_t text, Text_t pattern) -{ +static List_t Pattern$split(Text_t text, Text_t pattern) { if (text.length == 0) // special case - return (List_t){.length=0}; + return (List_t){.length = 0}; if (pattern.length == 0) // special case return Text$clusters(text); @@ -1226,16 +1126,15 @@ static List_t Pattern$split(Text_t text, Text_t pattern) int64_t i = 0; for (;;) { int64_t len = 0; - int64_t found = _find(text, pattern, i, text.length-1, &len, NULL); - if (found == i && len == 0) - found = _find(text, pattern, i + 1, text.length-1, &len, NULL); + int64_t found = _find(text, pattern, i, text.length - 1, &len, NULL); + if (found == i && len == 0) found = _find(text, pattern, i + 1, text.length - 1, &len, NULL); if (found < 0) break; - Text_t chunk = Text$slice(text, I(i+1), I(found)); + Text_t chunk = Text$slice(text, I(i + 1), I(found)); List$insert(&chunks, &chunk, I_small(0), sizeof(Text_t)); i = MAX(found + len, i + 1); } - Text_t last_chunk = Text$slice(text, I(i+1), I(text.length)); + Text_t last_chunk = Text$slice(text, I(i + 1), I(text.length)); List$insert(&chunks, &last_chunk, I_small(0), sizeof(Text_t)); return chunks; @@ -1247,8 +1146,7 @@ typedef struct { Text_t pattern; } split_iter_state_t; -static OptionalText_t next_split(split_iter_state_t *state) -{ +static OptionalText_t next_split(split_iter_state_t *state) { Text_t text = state->state.stack[0].text; if (state->i >= text.length) { if (state->pattern.length > 0 && state->i == text.length) { // special case @@ -1259,37 +1157,34 @@ static OptionalText_t next_split(split_iter_state_t *state) } if (state->pattern.length == 0) { // special case - Text_t ret = Text$cluster(text, I(state->i+1)); + Text_t ret = Text$cluster(text, I(state->i + 1)); state->i += 1; return ret; } int64_t start = state->i; int64_t len = 0; - int64_t found = _find(text, state->pattern, start, text.length-1, &len, NULL); + int64_t found = _find(text, state->pattern, start, text.length - 1, &len, NULL); - if (found == start && len == 0) - found = _find(text, state->pattern, start + 1, text.length-1, &len, NULL); + if (found == start && len == 0) found = _find(text, state->pattern, start + 1, text.length - 1, &len, NULL); if (found >= 0) { state->i = MAX(found + len, state->i + 1); - return Text$slice(text, I(start+1), I(found)); + return Text$slice(text, I(start + 1), I(found)); } else { state->i = state->state.stack[0].text.length + 1; - return Text$slice(text, I(start+1), I(text.length)); + return Text$slice(text, I(start + 1), I(text.length)); } } -static Closure_t Pattern$by_split(Text_t text, Text_t pattern) -{ +static Closure_t Pattern$by_split(Text_t text, Text_t pattern) { return (Closure_t){ - .fn=(void*)next_split, - .userdata=new(split_iter_state_t, .state=NEW_TEXT_ITER_STATE(text), .i=0, .pattern=pattern), + .fn = (void *)next_split, + .userdata = new (split_iter_state_t, .state = NEW_TEXT_ITER_STATE(text), .i = 0, .pattern = pattern), }; } -static Text_t Pattern$escape_text(Text_t text) -{ +static Text_t Pattern$escape_text(Text_t text) { // TODO: optimize for spans of non-escaped text Text_t ret = EMPTY_TEXT; TextIter_t state = NEW_TEXT_ITER_STATE(text); @@ -1297,23 +1192,21 @@ static Text_t Pattern$escape_text(Text_t text) uint32_t g = Text$get_main_grapheme_fast(&state, i); if (g == '{') { ret = Text$concat(ret, Text("{1{}")); - } else if (g == '?' - || uc_is_property_quotation_mark(g) + } else if (g == '?' || uc_is_property_quotation_mark(g) || (uc_is_property_paired_punctuation(g) && uc_is_property_left_of_pair(g))) { - ret = Text$concat(ret, Text("{1"), Text$slice(text, I(i+1), I(i+1)), Text("}")); + ret = Text$concat(ret, Text("{1"), Text$slice(text, I(i + 1), I(i + 1)), Text("}")); } else { - ret = Text$concat(ret, Text$slice(text, I(i+1), I(i+1))); + ret = Text$concat(ret, Text$slice(text, I(i + 1), I(i + 1))); } } return ret; } -static Text_t Pattern$as_text(const void *obj, bool colorize, const TypeInfo_t *info) -{ +static Text_t Pattern$as_text(const void *obj, bool colorize, const TypeInfo_t *info) { (void)info; if (!obj) return Text("Pattern"); - Text_t pat = *(Text_t*)obj; + Text_t pat = *(Text_t *)obj; Text_t quote = Pattern$has(pat, Text("/")) && !Pattern$has(pat, Text("|")) ? Text("|") : Text("/"); return Text$concat(colorize ? Text("\x1b[1m$\033[m") : Text("$"), Text$quoted(pat, colorize, quote)); } diff --git a/lib/random/chacha.h b/lib/random/chacha.h index b803c24a..a98c0b87 100644 --- a/lib/random/chacha.h +++ b/lib/random/chacha.h @@ -11,9 +11,8 @@ Public domain. typedef unsigned char u8; typedef unsigned int u32; -typedef struct -{ - u32 input[16]; /* could be compressed */ +typedef struct { + u32 input[16]; /* could be compressed */ } chacha_ctx; #define KEYSZ 32 @@ -25,172 +24,167 @@ typedef struct #define U8V(v) ((u8)(v) & U8C(0xFF)) #define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF)) -#define ROTL32(v, n) \ - (U32V((v) << (n)) | ((v) >> (32 - (n)))) +#define ROTL32(v, n) (U32V((v) << (n)) | ((v) >> (32 - (n)))) -#define U8TO32_LITTLE(p) \ - (((u32)((p)[0]) ) | \ - ((u32)((p)[1]) << 8) | \ - ((u32)((p)[2]) << 16) | \ - ((u32)((p)[3]) << 24)) +#define U8TO32_LITTLE(p) (((u32)((p)[0])) | ((u32)((p)[1]) << 8) | ((u32)((p)[2]) << 16) | ((u32)((p)[3]) << 24)) -#define U32TO8_LITTLE(p, v) \ - do { \ - (p)[0] = U8V((v) ); \ - (p)[1] = U8V((v) >> 8); \ - (p)[2] = U8V((v) >> 16); \ - (p)[3] = U8V((v) >> 24); \ - } while (0) +#define U32TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v)); \ + (p)[1] = U8V((v) >> 8); \ + (p)[2] = U8V((v) >> 16); \ + (p)[3] = U8V((v) >> 24); \ + } while (0) #define ROTATE(v, c) (ROTL32(v, c)) #define XOR(v, w) ((v) ^ (w)) #define PLUS(v, w) (U32V((v) + (w))) #define PLUSONE(v) (PLUS((v), 1)) -#define QUARTERROUND(a, b, c, d) \ - a = PLUS(a, b); d = ROTATE(XOR(d, a), 16); \ - c = PLUS(c, d); b = ROTATE(XOR(b, c), 12); \ - a = PLUS(a, b); d = ROTATE(XOR(d, a), 8); \ - c = PLUS(c, d); b = ROTATE(XOR(b, c), 7); +#define QUARTERROUND(a, b, c, d) \ + a = PLUS(a, b); \ + d = ROTATE(XOR(d, a), 16); \ + c = PLUS(c, d); \ + b = ROTATE(XOR(b, c), 12); \ + a = PLUS(a, b); \ + d = ROTATE(XOR(d, a), 8); \ + c = PLUS(c, d); \ + b = ROTATE(XOR(b, c), 7); static const char sigma[16] = "expand 32-byte k"; -static void -chacha_keysetup(chacha_ctx *chacha, const u8 *k) -{ - chacha->input[0] = U8TO32_LITTLE(sigma + 0); - chacha->input[1] = U8TO32_LITTLE(sigma + 4); - chacha->input[2] = U8TO32_LITTLE(sigma + 8); - chacha->input[3] = U8TO32_LITTLE(sigma + 12); - chacha->input[4] = U8TO32_LITTLE(k + 0); - chacha->input[5] = U8TO32_LITTLE(k + 4); - chacha->input[6] = U8TO32_LITTLE(k + 8); - chacha->input[7] = U8TO32_LITTLE(k + 12); - chacha->input[8] = U8TO32_LITTLE(k + 16); - chacha->input[9] = U8TO32_LITTLE(k + 20); - chacha->input[10] = U8TO32_LITTLE(k + 24); - chacha->input[11] = U8TO32_LITTLE(k + 28); +static void chacha_keysetup(chacha_ctx *chacha, const u8 *k) { + chacha->input[0] = U8TO32_LITTLE(sigma + 0); + chacha->input[1] = U8TO32_LITTLE(sigma + 4); + chacha->input[2] = U8TO32_LITTLE(sigma + 8); + chacha->input[3] = U8TO32_LITTLE(sigma + 12); + chacha->input[4] = U8TO32_LITTLE(k + 0); + chacha->input[5] = U8TO32_LITTLE(k + 4); + chacha->input[6] = U8TO32_LITTLE(k + 8); + chacha->input[7] = U8TO32_LITTLE(k + 12); + chacha->input[8] = U8TO32_LITTLE(k + 16); + chacha->input[9] = U8TO32_LITTLE(k + 20); + chacha->input[10] = U8TO32_LITTLE(k + 24); + chacha->input[11] = U8TO32_LITTLE(k + 28); } -static void -chacha_ivsetup(chacha_ctx *chacha, const u8 *iv) -{ - chacha->input[12] = 0; - chacha->input[13] = 0; - chacha->input[14] = U8TO32_LITTLE(iv + 0); - chacha->input[15] = U8TO32_LITTLE(iv + 4); +static void chacha_ivsetup(chacha_ctx *chacha, const u8 *iv) { + chacha->input[12] = 0; + chacha->input[13] = 0; + chacha->input[14] = U8TO32_LITTLE(iv + 0); + chacha->input[15] = U8TO32_LITTLE(iv + 4); } -static void -chacha_encrypt_bytes(chacha_ctx *chacha, const u8 *m, u8 *c, u32 bytes) -{ - u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; - u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; - u8 *ctarget = (u8*)0; - u8 tmp[64]; - unsigned int i; - - if (!bytes) return; - - j0 = chacha->input[0]; - j1 = chacha->input[1]; - j2 = chacha->input[2]; - j3 = chacha->input[3]; - j4 = chacha->input[4]; - j5 = chacha->input[5]; - j6 = chacha->input[6]; - j7 = chacha->input[7]; - j8 = chacha->input[8]; - j9 = chacha->input[9]; - j10 = chacha->input[10]; - j11 = chacha->input[11]; - j12 = chacha->input[12]; - j13 = chacha->input[13]; - j14 = chacha->input[14]; - j15 = chacha->input[15]; - - for (;;) { - if (bytes < 64) { - for (i = 0;i < bytes;++i) tmp[i] = m[i]; - m = tmp; - ctarget = c; - c = tmp; +static void chacha_encrypt_bytes(chacha_ctx *chacha, const u8 *m, u8 *c, u32 bytes) { + u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + u8 *ctarget = (u8 *)0; + u8 tmp[64]; + unsigned int i; + + if (!bytes) return; + + j0 = chacha->input[0]; + j1 = chacha->input[1]; + j2 = chacha->input[2]; + j3 = chacha->input[3]; + j4 = chacha->input[4]; + j5 = chacha->input[5]; + j6 = chacha->input[6]; + j7 = chacha->input[7]; + j8 = chacha->input[8]; + j9 = chacha->input[9]; + j10 = chacha->input[10]; + j11 = chacha->input[11]; + j12 = chacha->input[12]; + j13 = chacha->input[13]; + j14 = chacha->input[14]; + j15 = chacha->input[15]; + + for (;;) { + if (bytes < 64) { + for (i = 0; i < bytes; ++i) + tmp[i] = m[i]; + m = tmp; + ctarget = c; + c = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = 20; i > 0; i -= 2) { + QUARTERROUND(x0, x4, x8, x12) + QUARTERROUND(x1, x5, x9, x13) + QUARTERROUND(x2, x6, x10, x14) + QUARTERROUND(x3, x7, x11, x15) + QUARTERROUND(x0, x5, x10, x15) + QUARTERROUND(x1, x6, x11, x12) + QUARTERROUND(x2, x7, x8, x13) + QUARTERROUND(x3, x4, x9, x14) + } + x0 = PLUS(x0, j0); + x1 = PLUS(x1, j1); + x2 = PLUS(x2, j2); + x3 = PLUS(x3, j3); + x4 = PLUS(x4, j4); + x5 = PLUS(x5, j5); + x6 = PLUS(x6, j6); + x7 = PLUS(x7, j7); + x8 = PLUS(x8, j8); + x9 = PLUS(x9, j9); + x10 = PLUS(x10, j10); + x11 = PLUS(x11, j11); + x12 = PLUS(x12, j12); + x13 = PLUS(x13, j13); + x14 = PLUS(x14, j14); + x15 = PLUS(x15, j15); + + j12 = PLUSONE(j12); + if (!j12) { + j13 = PLUSONE(j13); + /* stopping at 2^70 bytes per nonce is user's responsibility */ + } + + U32TO8_LITTLE(c + 0, x0); + U32TO8_LITTLE(c + 4, x1); + U32TO8_LITTLE(c + 8, x2); + U32TO8_LITTLE(c + 12, x3); + U32TO8_LITTLE(c + 16, x4); + U32TO8_LITTLE(c + 20, x5); + U32TO8_LITTLE(c + 24, x6); + U32TO8_LITTLE(c + 28, x7); + U32TO8_LITTLE(c + 32, x8); + U32TO8_LITTLE(c + 36, x9); + U32TO8_LITTLE(c + 40, x10); + U32TO8_LITTLE(c + 44, x11); + U32TO8_LITTLE(c + 48, x12); + U32TO8_LITTLE(c + 52, x13); + U32TO8_LITTLE(c + 56, x14); + U32TO8_LITTLE(c + 60, x15); + + if (bytes <= 64) { + if (bytes < 64) { + for (i = 0; i < bytes; ++i) + ctarget[i] = c[i]; + } + chacha->input[12] = j12; + chacha->input[13] = j13; + return; + } + bytes -= 64; + c += 64; } - x0 = j0; - x1 = j1; - x2 = j2; - x3 = j3; - x4 = j4; - x5 = j5; - x6 = j6; - x7 = j7; - x8 = j8; - x9 = j9; - x10 = j10; - x11 = j11; - x12 = j12; - x13 = j13; - x14 = j14; - x15 = j15; - for (i = 20;i > 0;i -= 2) { - QUARTERROUND( x0, x4, x8, x12) - QUARTERROUND( x1, x5, x9, x13) - QUARTERROUND( x2, x6, x10, x14) - QUARTERROUND( x3, x7, x11, x15) - QUARTERROUND( x0, x5, x10, x15) - QUARTERROUND( x1, x6, x11, x12) - QUARTERROUND( x2, x7, x8, x13) - QUARTERROUND( x3, x4, x9, x14) - } - x0 = PLUS(x0, j0); - x1 = PLUS(x1, j1); - x2 = PLUS(x2, j2); - x3 = PLUS(x3, j3); - x4 = PLUS(x4, j4); - x5 = PLUS(x5, j5); - x6 = PLUS(x6, j6); - x7 = PLUS(x7, j7); - x8 = PLUS(x8, j8); - x9 = PLUS(x9, j9); - x10 = PLUS(x10, j10); - x11 = PLUS(x11, j11); - x12 = PLUS(x12, j12); - x13 = PLUS(x13, j13); - x14 = PLUS(x14, j14); - x15 = PLUS(x15, j15); - - j12 = PLUSONE(j12); - if (!j12) { - j13 = PLUSONE(j13); - /* stopping at 2^70 bytes per nonce is user's responsibility */ - } - - U32TO8_LITTLE(c + 0, x0); - U32TO8_LITTLE(c + 4, x1); - U32TO8_LITTLE(c + 8, x2); - U32TO8_LITTLE(c + 12, x3); - U32TO8_LITTLE(c + 16, x4); - U32TO8_LITTLE(c + 20, x5); - U32TO8_LITTLE(c + 24, x6); - U32TO8_LITTLE(c + 28, x7); - U32TO8_LITTLE(c + 32, x8); - U32TO8_LITTLE(c + 36, x9); - U32TO8_LITTLE(c + 40, x10); - U32TO8_LITTLE(c + 44, x11); - U32TO8_LITTLE(c + 48, x12); - U32TO8_LITTLE(c + 52, x13); - U32TO8_LITTLE(c + 56, x14); - U32TO8_LITTLE(c + 60, x15); - - if (bytes <= 64) { - if (bytes < 64) { - for (i = 0;i < bytes;++i) ctarget[i] = c[i]; - } - chacha->input[12] = j12; - chacha->input[13] = j13; - return; - } - bytes -= 64; - c += 64; - } } diff --git a/lib/random/sysrandom.h b/lib/random/sysrandom.h index d276be45..8acec5b7 100644 --- a/lib/random/sysrandom.h +++ b/lib/random/sysrandom.h @@ -11,5 +11,5 @@ static ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) { // Use getrandom() #include <sys/random.h> #else - #error "Unsupported platform for secure random number generation" +#error "Unsupported platform for secure random number generation" #endif diff --git a/lib/time/time_defs.h b/lib/time/time_defs.h index 2ae1d9e4..214c26b9 100644 --- a/lib/time/time_defs.h +++ b/lib/time/time_defs.h @@ -7,25 +7,26 @@ typedef struct timeval Time; static OptionalText_t _local_timezone = NONE_TEXT; -static INLINE Text_t num_format(long n, const char *unit) -{ - if (n == 0) - return Text("now"); - return Text$from_str(String((int64_t)labs(n), " ", unit, (n == -1 || n == 1) ? "" : "s", n <= 0 ? " ago" : " later")); +static INLINE Text_t num_format(long n, const char *unit) { + if (n == 0) return Text("now"); + return Text$from_str( + String((int64_t)labs(n), " ", unit, (n == -1 || n == 1) ? "" : "s", n <= 0 ? " ago" : " later")); } -static void set_local_timezone(Text_t tz) -{ +static void set_local_timezone(Text_t tz) { setenv("TZ", Text$as_c_string(tz), 1); _local_timezone = tz; tzset(); } -#define WITH_TIMEZONE(tz, body) ({ if (tz.length >= 0) { \ - OptionalText_t old_timezone = _local_timezone; \ - set_local_timezone(tz); \ - body; \ - set_local_timezone(old_timezone); \ - } else { \ - body; \ - }}) +#define WITH_TIMEZONE(tz, body) \ + ({ \ + if (tz.length >= 0) { \ + OptionalText_t old_timezone = _local_timezone; \ + set_local_timezone(tz); \ + body; \ + set_local_timezone(old_timezone); \ + } else { \ + body; \ + } \ + }) @@ -7,43 +7,66 @@ #include "stdlib/tables.h" #include "stdlib/text.h" -static Text_t quoted_text(const char *text) { - return Text$quoted(Text$from_str(text), false, Text("\"")); -} +static Text_t quoted_text(const char *text) { return Text$quoted(Text$from_str(text), false, Text("\"")); } CONSTFUNC const char *binop_method_name(ast_e tag) { switch (tag) { - case Power: case PowerUpdate: return "power"; - case Multiply: case MultiplyUpdate: return "times"; - case Divide: case DivideUpdate: return "divided_by"; - case Mod: case ModUpdate: return "modulo"; - case Mod1: case Mod1Update: return "modulo1"; - case Plus: case PlusUpdate: return "plus"; - case Minus: case MinusUpdate: return "minus"; - case Concat: case ConcatUpdate: return "concatenated_with"; - case LeftShift: case LeftShiftUpdate: return "left_shifted"; - case RightShift: case RightShiftUpdate: return "right_shifted"; - case UnsignedLeftShift: case UnsignedLeftShiftUpdate: return "unsigned_left_shifted"; - case UnsignedRightShift: case UnsignedRightShiftUpdate: return "unsigned_right_shifted"; - case And: case AndUpdate: return "bit_and"; - case Or: case OrUpdate: return "bit_or"; - case Xor: case XorUpdate: return "bit_xor"; + case Power: + case PowerUpdate: return "power"; + case Multiply: + case MultiplyUpdate: return "times"; + case Divide: + case DivideUpdate: return "divided_by"; + case Mod: + case ModUpdate: return "modulo"; + case Mod1: + case Mod1Update: return "modulo1"; + case Plus: + case PlusUpdate: return "plus"; + case Minus: + case MinusUpdate: return "minus"; + case Concat: + case ConcatUpdate: return "concatenated_with"; + case LeftShift: + case LeftShiftUpdate: return "left_shifted"; + case RightShift: + case RightShiftUpdate: return "right_shifted"; + case UnsignedLeftShift: + case UnsignedLeftShiftUpdate: return "unsigned_left_shifted"; + case UnsignedRightShift: + case UnsignedRightShiftUpdate: return "unsigned_right_shifted"; + case And: + case AndUpdate: return "bit_and"; + case Or: + case OrUpdate: return "bit_or"; + case Xor: + case XorUpdate: return "bit_xor"; default: return NULL; } }; CONSTFUNC const char *binop_operator(ast_e tag) { switch (tag) { - case Multiply: case MultiplyUpdate: return "*"; - case Divide: case DivideUpdate: return "/"; - case Mod: case ModUpdate: return "%"; - case Plus: case PlusUpdate: return "+"; - case Minus: case MinusUpdate: return "-"; - case LeftShift: case LeftShiftUpdate: return "<<"; - case RightShift: case RightShiftUpdate: return ">>"; - case And: case AndUpdate: return "&"; - case Or: case OrUpdate: return "|"; - case Xor: case XorUpdate: return "^"; + case Multiply: + case MultiplyUpdate: return "*"; + case Divide: + case DivideUpdate: return "/"; + case Mod: + case ModUpdate: return "%"; + case Plus: + case PlusUpdate: return "+"; + case Minus: + case MinusUpdate: return "-"; + case LeftShift: + case LeftShiftUpdate: return "<<"; + case RightShift: + case RightShiftUpdate: return ">>"; + case And: + case AndUpdate: return "&"; + case Or: + case OrUpdate: return "|"; + case Xor: + case XorUpdate: return "^"; case Equals: return "=="; case NotEquals: return "!="; case LessThan: return "<"; @@ -62,8 +85,7 @@ static Text_t tags_to_sexp(tag_ast_t *tags); static Text_t optional_sexp(const char *tag, ast_t *ast); static Text_t optional_type_sexp(const char *tag, type_ast_t *ast); -Text_t ast_list_to_sexp(ast_list_t *asts) -{ +Text_t ast_list_to_sexp(ast_list_t *asts) { Text_t c = EMPTY_TEXT; for (; asts; asts = asts->next) { c = Texts(c, " ", ast_to_sexp(asts->ast)); @@ -74,8 +96,8 @@ Text_t ast_list_to_sexp(ast_list_t *asts) Text_t arg_defs_to_sexp(arg_ast_t *args) { Text_t c = Text("(args"); for (arg_ast_t *arg = args; arg; arg = arg->next) { - c = Texts(c, " (arg ", arg->name ? quoted_text(arg->name) : Text("nil"), - " ", type_ast_to_sexp(arg->type), " ", ast_to_sexp(arg->value), ")"); + c = Texts(c, " (arg ", arg->name ? quoted_text(arg->name) : Text("nil"), " ", type_ast_to_sexp(arg->type), " ", + ast_to_sexp(arg->value), ")"); } return Texts(c, ")"); } @@ -84,8 +106,7 @@ Text_t arg_list_to_sexp(arg_ast_t *args) { Text_t c = EMPTY_TEXT; for (arg_ast_t *arg = args; arg; arg = arg->next) { assert(arg->value && !arg->type); - if (arg->name) - c = Texts(c, " :", arg->name); + if (arg->name) c = Texts(c, " :", arg->name); c = Texts(c, " ", ast_to_sexp(arg->value)); } return c; @@ -107,118 +128,163 @@ Text_t tags_to_sexp(tag_ast_t *tags) { return c; } -Text_t type_ast_to_sexp(type_ast_t *t) -{ +Text_t type_ast_to_sexp(type_ast_t *t) { if (!t) return Text("nil"); switch (t->tag) { -#define T(type, ...) case type: { __typeof(t->__data.type) data = t->__data.type; (void)data; return Texts(__VA_ARGS__); } - T(UnknownTypeAST, "(UnknownType)") - T(VarTypeAST, "(VarType \"", data.name, "\")") - T(PointerTypeAST, "(PointerType \"", data.is_stack ? "stack" : "heap", "\" ", type_ast_to_sexp(data.pointed), ")") - T(ListTypeAST, "(ListType ", type_ast_to_sexp(data.item), ")") - T(SetTypeAST, "(SetType ", type_ast_to_sexp(data.item), ")") - T(TableTypeAST, "(TableType ", type_ast_to_sexp(data.key), " ", type_ast_to_sexp(data.value), ")") - T(FunctionTypeAST, "(FunctionType ", arg_defs_to_sexp(data.args), " ", type_ast_to_sexp(data.ret), ")") - T(OptionalTypeAST, "(OptionalType ", type_ast_to_sexp(data.type), ")") +#define T(type, ...) \ + case type: { \ + __typeof(t->__data.type) data = t->__data.type; \ + (void)data; \ + return Texts(__VA_ARGS__); \ + } + T(UnknownTypeAST, "(UnknownType)"); + T(VarTypeAST, "(VarType \"", data.name, "\")"); + T(PointerTypeAST, "(PointerType \"", data.is_stack ? "stack" : "heap", "\" ", type_ast_to_sexp(data.pointed), + ")"); + T(ListTypeAST, "(ListType ", type_ast_to_sexp(data.item), ")"); + T(SetTypeAST, "(SetType ", type_ast_to_sexp(data.item), ")"); + T(TableTypeAST, "(TableType ", type_ast_to_sexp(data.key), " ", type_ast_to_sexp(data.value), ")"); + T(FunctionTypeAST, "(FunctionType ", arg_defs_to_sexp(data.args), " ", type_ast_to_sexp(data.ret), ")"); + T(OptionalTypeAST, "(OptionalType ", type_ast_to_sexp(data.type), ")"); #undef T default: return EMPTY_TEXT; } } -Text_t optional_sexp(const char *name, ast_t *ast) -{ +Text_t optional_sexp(const char *name, ast_t *ast) { return ast ? Texts(" :", name, " ", ast_to_sexp(ast)) : EMPTY_TEXT; } -Text_t optional_type_sexp(const char *name, type_ast_t *ast) -{ +Text_t optional_type_sexp(const char *name, type_ast_t *ast) { return ast ? Texts(" :", name, " ", type_ast_to_sexp(ast)) : EMPTY_TEXT; } -Text_t ast_to_sexp(ast_t *ast) -{ +Text_t ast_to_sexp(ast_t *ast) { if (!ast) return Text("nil"); switch (ast->tag) { -#define T(type, ...) case type: { __typeof(ast->__data.type) data = ast->__data.type; (void)data; return Texts(__VA_ARGS__); } - T(Unknown, "(Unknown)") - T(None, "(None)") - T(Bool, "(Bool ", data.b ? "yes" : "no", ")") - T(Var, "(Var ", quoted_text(data.name), ")") - T(Int, "(Int ", quoted_text(ast_source(ast)), ")") - T(Num, "(Num ", quoted_text(ast_source(ast)), ")") - T(TextLiteral, Text$quoted(data.text, false, Text("\""))) - T(TextJoin, "(Text", data.lang ? Texts(" :lang ", quoted_text(data.lang)) : EMPTY_TEXT, ast_list_to_sexp(data.children), ")") - T(Path, "(Path ", quoted_text(data.path), ")") - T(Declare, "(Declare ", ast_to_sexp(data.var), " ", type_ast_to_sexp(data.type), " ", ast_to_sexp(data.value), ")") - T(Assign, "(Assign (targets ", ast_list_to_sexp(data.targets), ") (values ", ast_list_to_sexp(data.values), "))") +#define T(type, ...) \ + case type: { \ + __typeof(ast->__data.type) data = ast->__data.type; \ + (void)data; \ + return Texts(__VA_ARGS__); \ + } + T(Unknown, "(Unknown)"); + T(None, "(None)"); + T(Bool, "(Bool ", data.b ? "yes" : "no", ")"); + T(Var, "(Var ", quoted_text(data.name), ")"); + T(Int, "(Int ", quoted_text(ast_source(ast)), ")"); + T(Num, "(Num ", quoted_text(ast_source(ast)), ")"); + T(TextLiteral, Text$quoted(data.text, false, Text("\""))); + T(TextJoin, "(Text", data.lang ? Texts(" :lang ", quoted_text(data.lang)) : EMPTY_TEXT, + ast_list_to_sexp(data.children), ")"); + T(Path, "(Path ", quoted_text(data.path), ")"); + T(Declare, "(Declare ", ast_to_sexp(data.var), " ", type_ast_to_sexp(data.type), " ", ast_to_sexp(data.value), + ")"); + T(Assign, "(Assign (targets ", ast_list_to_sexp(data.targets), ") (values ", ast_list_to_sexp(data.values), + "))"); #define BINOP(name) T(name, "(" #name " ", ast_to_sexp(data.lhs), " ", ast_to_sexp(data.rhs), ")") - BINOP(Power) BINOP(PowerUpdate) BINOP(Multiply) BINOP(MultiplyUpdate) BINOP(Divide) BINOP(DivideUpdate) BINOP(Mod) BINOP(ModUpdate) - BINOP(Mod1) BINOP(Mod1Update) BINOP(Plus) BINOP(PlusUpdate) BINOP(Minus) BINOP(MinusUpdate) BINOP(Concat) BINOP(ConcatUpdate) - BINOP(LeftShift) BINOP(LeftShiftUpdate) BINOP(RightShift) BINOP(RightShiftUpdate) BINOP(UnsignedLeftShift) BINOP(UnsignedLeftShiftUpdate) - BINOP(UnsignedRightShift) BINOP(UnsignedRightShiftUpdate) BINOP(And) BINOP(AndUpdate) BINOP(Or) BINOP(OrUpdate) - BINOP(Xor) BINOP(XorUpdate) BINOP(Compare) - BINOP(Equals) BINOP(NotEquals) BINOP(LessThan) BINOP(LessThanOrEquals) BINOP(GreaterThan) BINOP(GreaterThanOrEquals) + BINOP(Power); + BINOP(PowerUpdate); + BINOP(Multiply); + BINOP(MultiplyUpdate); + BINOP(Divide); + BINOP(DivideUpdate); + BINOP(Mod); + BINOP(ModUpdate); + BINOP(Mod1); + BINOP(Mod1Update); + BINOP(Plus); + BINOP(PlusUpdate); + BINOP(Minus); + BINOP(MinusUpdate); + BINOP(Concat); + BINOP(ConcatUpdate); + BINOP(LeftShift); + BINOP(LeftShiftUpdate); + BINOP(RightShift); + BINOP(RightShiftUpdate); + BINOP(UnsignedLeftShift); + BINOP(UnsignedLeftShiftUpdate); + BINOP(UnsignedRightShift); + BINOP(UnsignedRightShiftUpdate); + BINOP(And); + BINOP(AndUpdate); + BINOP(Or); + BINOP(OrUpdate); + BINOP(Xor); + BINOP(XorUpdate); + BINOP(Compare); + BINOP(Equals); + BINOP(NotEquals); + BINOP(LessThan); + BINOP(LessThanOrEquals); + BINOP(GreaterThan); + BINOP(GreaterThanOrEquals); #undef BINOP - T(Negative, "(Negative ", ast_to_sexp(data.value), ")") - T(Not, "(Not ", ast_to_sexp(data.value), ")") - T(HeapAllocate, "(HeapAllocate ", ast_to_sexp(data.value), ")") - T(StackReference, "(StackReference ", ast_to_sexp(data.value), ")") - T(Min, "(Min ", ast_to_sexp(data.lhs), " ", ast_to_sexp(data.rhs), optional_sexp("key", data.key), ")") - T(Max, "(Max ", ast_to_sexp(data.lhs), " ", ast_to_sexp(data.rhs), optional_sexp("key", data.key), ")") - T(List, "(List", ast_list_to_sexp(data.items), ")") - T(Set, "(Set", ast_list_to_sexp(data.items), ")") - T(Table, "(Table", optional_sexp("default", data.default_value), optional_sexp("fallback", data.fallback), - ast_list_to_sexp(data.entries), ")") - T(TableEntry, "(TableEntry ", ast_to_sexp(data.key), " ", ast_to_sexp(data.value), ")") - T(Comprehension, "(Comprehension ", ast_to_sexp(data.expr), " (vars", ast_list_to_sexp(data.vars), ") ", - ast_to_sexp(data.iter), " ", optional_sexp("filter", data.filter), ")") - T(FunctionDef, "(FunctionDef ", ast_to_sexp(data.name), " ", arg_defs_to_sexp(data.args), - optional_type_sexp("return", data.ret_type), " ", ast_to_sexp(data.body), ")") - T(ConvertDef, "(ConvertDef ", arg_defs_to_sexp(data.args), " ", type_ast_to_sexp(data.ret_type), " ", ast_to_sexp(data.body), ")") - T(Lambda, "(Lambda ", arg_defs_to_sexp(data.args), optional_type_sexp("return", data.ret_type), " ", ast_to_sexp(data.body), ")") - T(FunctionCall, "(FunctionCall ", ast_to_sexp(data.fn), arg_list_to_sexp(data.args), ")") - T(MethodCall, "(MethodCall ", ast_to_sexp(data.self), " ", quoted_text(data.name), arg_list_to_sexp(data.args), ")") - T(Block, "(Block", ast_list_to_sexp(data.statements), ")") - T(For, "(For (vars", ast_list_to_sexp(data.vars), ") ", ast_to_sexp(data.iter), " ", ast_to_sexp(data.body), - " ", ast_to_sexp(data.empty), ")") - T(While, "(While ", ast_to_sexp(data.condition), " ", ast_to_sexp(data.body), ")") - T(Repeat, "(Repeat ", ast_to_sexp(data.body), ")") - T(If, "(If ", ast_to_sexp(data.condition), " ", ast_to_sexp(data.body), optional_sexp("else", data.else_body), ")") - T(When, "(When ", ast_to_sexp(data.subject), when_clauses_to_sexp(data.clauses), optional_sexp("else", data.else_body), ")") - T(Reduction, "(Reduction ", quoted_text(binop_method_name(data.op)), " ", ast_to_sexp(data.key), " ", ast_to_sexp(data.iter), ")") - T(Skip, "(Skip ", quoted_text(data.target), ")") - T(Stop, "(Stop ", quoted_text(data.target), ")") - T(Pass, "(Pass)") - T(Defer, "(Defer ", ast_to_sexp(data.body), ")") - T(Return, "(Return ", ast_to_sexp(data.value), ")") - T(Extern, "(Extern \"", data.name, "\" ", type_ast_to_sexp(data.type), ")") - T(StructDef, "(StructDef \"", data.name, "\" ", arg_defs_to_sexp(data.fields), " ", ast_to_sexp(data.namespace), ")") - T(EnumDef, "(EnumDef \"", data.name, "\" (tags ", tags_to_sexp(data.tags), ") ", ast_to_sexp(data.namespace), ")") - T(LangDef, "(LangDef \"", data.name, "\" ", ast_to_sexp(data.namespace), ")") - T(Index, "(Index ", ast_to_sexp(data.indexed), " ", ast_to_sexp(data.index), ")") - T(FieldAccess, "(FieldAccess ", ast_to_sexp(data.fielded), " \"", data.field, "\")") - T(Optional, "(Optional ", ast_to_sexp(data.value), ")") - T(NonOptional, "(NonOptional ", ast_to_sexp(data.value), ")") - T(DocTest, "(DocTest ", ast_to_sexp(data.expr), optional_sexp("expected", data.expected), ")") - T(Assert, "(Assert ", ast_to_sexp(data.expr), " ", optional_sexp("message", data.message), ")") - T(Use, "(Use ", optional_sexp("var", data.var), " ", quoted_text(data.path), ")") - T(InlineCCode, "(InlineCCode ", ast_list_to_sexp(data.chunks), optional_type_sexp("type", data.type_ast), ")") - T(Deserialize, "(Deserialize ", type_ast_to_sexp(data.type), " ", ast_to_sexp(data.value), ")") - T(Extend, "(Extend \"", data.name, "\" ", ast_to_sexp(data.body), ")") - default: errx(1, "S-expressions are not implemented for this AST"); + T(Negative, "(Negative ", ast_to_sexp(data.value), ")"); + T(Not, "(Not ", ast_to_sexp(data.value), ")"); + T(HeapAllocate, "(HeapAllocate ", ast_to_sexp(data.value), ")"); + T(StackReference, "(StackReference ", ast_to_sexp(data.value), ")"); + T(Min, "(Min ", ast_to_sexp(data.lhs), " ", ast_to_sexp(data.rhs), optional_sexp("key", data.key), ")"); + T(Max, "(Max ", ast_to_sexp(data.lhs), " ", ast_to_sexp(data.rhs), optional_sexp("key", data.key), ")"); + T(List, "(List", ast_list_to_sexp(data.items), ")"); + T(Set, "(Set", ast_list_to_sexp(data.items), ")"); + T(Table, "(Table", optional_sexp("default", data.default_value), optional_sexp("fallback", data.fallback), + ast_list_to_sexp(data.entries), ")"); + T(TableEntry, "(TableEntry ", ast_to_sexp(data.key), " ", ast_to_sexp(data.value), ")"); + T(Comprehension, "(Comprehension ", ast_to_sexp(data.expr), " (vars", ast_list_to_sexp(data.vars), ") ", + ast_to_sexp(data.iter), " ", optional_sexp("filter", data.filter), ")"); + T(FunctionDef, "(FunctionDef ", ast_to_sexp(data.name), " ", arg_defs_to_sexp(data.args), + optional_type_sexp("return", data.ret_type), " ", ast_to_sexp(data.body), ")"); + T(ConvertDef, "(ConvertDef ", arg_defs_to_sexp(data.args), " ", type_ast_to_sexp(data.ret_type), " ", + ast_to_sexp(data.body), ")"); + T(Lambda, "(Lambda ", arg_defs_to_sexp(data.args), optional_type_sexp("return", data.ret_type), " ", + ast_to_sexp(data.body), ")"); + T(FunctionCall, "(FunctionCall ", ast_to_sexp(data.fn), arg_list_to_sexp(data.args), ")"); + T(MethodCall, "(MethodCall ", ast_to_sexp(data.self), " ", quoted_text(data.name), arg_list_to_sexp(data.args), + ")") + T(Block, "(Block", ast_list_to_sexp(data.statements), ")"); + T(For, "(For (vars", ast_list_to_sexp(data.vars), ") ", ast_to_sexp(data.iter), " ", ast_to_sexp(data.body), + " ", ast_to_sexp(data.empty), ")"); + T(While, "(While ", ast_to_sexp(data.condition), " ", ast_to_sexp(data.body), ")"); + T(Repeat, "(Repeat ", ast_to_sexp(data.body), ")"); + T(If, "(If ", ast_to_sexp(data.condition), " ", ast_to_sexp(data.body), optional_sexp("else", data.else_body), + ")"); + T(When, "(When ", ast_to_sexp(data.subject), when_clauses_to_sexp(data.clauses), + optional_sexp("else", data.else_body), ")"); + T(Reduction, "(Reduction ", quoted_text(binop_method_name(data.op)), " ", ast_to_sexp(data.key), " ", + ast_to_sexp(data.iter), ")"); + T(Skip, "(Skip ", quoted_text(data.target), ")"); + T(Stop, "(Stop ", quoted_text(data.target), ")"); + T(Pass, "(Pass)"); + T(Defer, "(Defer ", ast_to_sexp(data.body), ")"); + T(Return, "(Return ", ast_to_sexp(data.value), ")"); + T(Extern, "(Extern \"", data.name, "\" ", type_ast_to_sexp(data.type), ")"); + T(StructDef, "(StructDef \"", data.name, "\" ", arg_defs_to_sexp(data.fields), " ", ast_to_sexp(data.namespace), + ")"); + T(EnumDef, "(EnumDef \"", data.name, "\" (tags ", tags_to_sexp(data.tags), ") ", ast_to_sexp(data.namespace), + ")"); + T(LangDef, "(LangDef \"", data.name, "\" ", ast_to_sexp(data.namespace), ")"); + T(Index, "(Index ", ast_to_sexp(data.indexed), " ", ast_to_sexp(data.index), ")"); + T(FieldAccess, "(FieldAccess ", ast_to_sexp(data.fielded), " \"", data.field, "\")"); + T(Optional, "(Optional ", ast_to_sexp(data.value), ")"); + T(NonOptional, "(NonOptional ", ast_to_sexp(data.value), ")"); + T(DocTest, "(DocTest ", ast_to_sexp(data.expr), optional_sexp("expected", data.expected), ")"); + T(Assert, "(Assert ", ast_to_sexp(data.expr), " ", optional_sexp("message", data.message), ")"); + T(Use, "(Use ", optional_sexp("var", data.var), " ", quoted_text(data.path), ")"); + T(InlineCCode, "(InlineCCode ", ast_list_to_sexp(data.chunks), optional_type_sexp("type", data.type_ast), ")"); + T(Deserialize, "(Deserialize ", type_ast_to_sexp(data.type), " ", ast_to_sexp(data.value), ")"); + T(Extend, "(Extend \"", data.name, "\" ", ast_to_sexp(data.body), ")"); + default: errx(1, "S-expressions are not implemented for this AST"); #undef T } } -const char *ast_to_sexp_str(ast_t *ast) -{ - return Text$as_c_string(ast_to_sexp(ast)); -} +const char *ast_to_sexp_str(ast_t *ast) { return Text$as_c_string(ast_to_sexp(ast)); } -const char *ast_source(ast_t *ast) -{ +const char *ast_source(ast_t *ast) { if (!ast) return NULL; size_t len = (size_t)(ast->end - ast->start); char *source = GC_MALLOC_ATOMIC(len + 1); @@ -227,10 +293,14 @@ const char *ast_source(ast_t *ast) return source; } -PUREFUNC bool is_idempotent(ast_t *ast) -{ +PUREFUNC bool is_idempotent(ast_t *ast) { switch (ast->tag) { - case Int: case Bool: case Num: case Var: case None: case TextLiteral: return true; + case Int: + case Bool: + case Num: + case Var: + case None: + case TextLiteral: return true; case Index: { DeclareMatch(index, ast, Index); return is_idempotent(index->indexed) && index->index != NULL && is_idempotent(index->index); @@ -243,15 +313,13 @@ PUREFUNC bool is_idempotent(ast_t *ast) } } -void _visit_topologically(ast_t *ast, Table_t definitions, Table_t *visited, Closure_t fn) -{ - void (*visit)(void*, ast_t*) = (void*)fn.fn; +void _visit_topologically(ast_t *ast, Table_t definitions, Table_t *visited, Closure_t fn) { + void (*visit)(void *, ast_t *) = (void *)fn.fn; if (ast->tag == StructDef) { DeclareMatch(def, ast, StructDef); - if (Table$str_get(*visited, def->name)) - return; + if (Table$str_get(*visited, def->name)) return; - Table$str_set(visited, def->name, (void*)_visit_topologically); + Table$str_set(visited, def->name, (void *)_visit_topologically); for (arg_ast_t *field = def->fields; field; field = field->next) { if (field->type && field->type->tag == VarTypeAST) { const char *field_type_name = Match(field->type, VarTypeAST)->name; @@ -260,15 +328,13 @@ void _visit_topologically(ast_t *ast, Table_t definitions, Table_t *visited, Clo _visit_topologically(dependency, definitions, visited, fn); } } - } visit(fn.userdata, ast); } else if (ast->tag == EnumDef) { DeclareMatch(def, ast, EnumDef); - if (Table$str_get(*visited, def->name)) - return; + if (Table$str_get(*visited, def->name)) return; - Table$str_set(visited, def->name, (void*)_visit_topologically); + Table$str_set(visited, def->name, (void *)_visit_topologically); for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { for (arg_ast_t *field = tag->fields; field; field = field->next) { if (field->type && field->type->tag == VarTypeAST) { @@ -283,16 +349,14 @@ void _visit_topologically(ast_t *ast, Table_t definitions, Table_t *visited, Clo visit(fn.userdata, ast); } else if (ast->tag == LangDef) { DeclareMatch(def, ast, LangDef); - if (Table$str_get(*visited, def->name)) - return; + if (Table$str_get(*visited, def->name)) return; visit(fn.userdata, ast); } else { visit(fn.userdata, ast); } } -void visit_topologically(ast_list_t *asts, Closure_t fn) -{ +void visit_topologically(ast_list_t *asts, Closure_t fn) { // Visit each top-level statement in topological order: // - 'use' statements first // - then typedefs @@ -313,12 +377,11 @@ void visit_topologically(ast_list_t *asts, Closure_t fn) } } - void (*visit)(void*, ast_t*) = (void*)fn.fn; + void (*visit)(void *, ast_t *) = (void *)fn.fn; Table_t visited = {}; // First: 'use' statements in order: for (ast_list_t *stmt = asts; stmt; stmt = stmt->next) { - if (stmt->ast->tag == Use) - visit(fn.userdata, stmt->ast); + if (stmt->ast->tag == Use) visit(fn.userdata, stmt->ast); } // Then typedefs in topological order: for (ast_list_t *stmt = asts; stmt; stmt = stmt->next) { @@ -334,27 +397,35 @@ void visit_topologically(ast_list_t *asts, Closure_t fn) } } -CONSTFUNC bool is_binary_operation(ast_t *ast) -{ +CONSTFUNC bool is_binary_operation(ast_t *ast) { switch (ast->tag) { case BINOP_CASES: return true; default: return false; } } -CONSTFUNC bool is_update_assignment(ast_t *ast) -{ +CONSTFUNC bool is_update_assignment(ast_t *ast) { switch (ast->tag) { - case PowerUpdate: case MultiplyUpdate: case DivideUpdate: case ModUpdate: case Mod1Update: - case PlusUpdate: case MinusUpdate: case ConcatUpdate: case LeftShiftUpdate: case UnsignedLeftShiftUpdate: - case RightShiftUpdate: case UnsignedRightShiftUpdate: case AndUpdate: case OrUpdate: case XorUpdate: - return true; + case PowerUpdate: + case MultiplyUpdate: + case DivideUpdate: + case ModUpdate: + case Mod1Update: + case PlusUpdate: + case MinusUpdate: + case ConcatUpdate: + case LeftShiftUpdate: + case UnsignedLeftShiftUpdate: + case RightShiftUpdate: + case UnsignedRightShiftUpdate: + case AndUpdate: + case OrUpdate: + case XorUpdate: return true; default: return false; } } -CONSTFUNC ast_e binop_tag(ast_e tag) -{ +CONSTFUNC ast_e binop_tag(ast_e tag) { switch (tag) { case PowerUpdate: return Power; case MultiplyUpdate: return Multiply; @@ -11,31 +11,46 @@ #include "stdlib/files.h" #include "stdlib/util.h" -#define NewAST(_file, _start, _end, ast_tag, ...) (new(ast_t, .file=_file, .start=_start, .end=_end,\ - .tag=ast_tag, .__data.ast_tag={__VA_ARGS__})) -#define NewTypeAST(_file, _start, _end, ast_tag, ...) (new(type_ast_t, .file=_file, .start=_start, .end=_end,\ - .tag=ast_tag, .__data.ast_tag={__VA_ARGS__})) -#define FakeAST(ast_tag, ...) (new(ast_t, .tag=ast_tag, .__data.ast_tag={__VA_ARGS__})) -#define WrapAST(ast, ast_tag, ...) (new(ast_t, .file=(ast)->file, .start=(ast)->start, .end=(ast)->end, .tag=ast_tag, .__data.ast_tag={__VA_ARGS__})) -#define TextAST(ast, _str) WrapAST(ast, TextLiteral, .str=GC_strdup(_str)) -#define LiteralCode(code, ...) new(ast_t, .tag=InlineCCode, .__data.InlineCCode={.chunks=new(ast_list_t, .ast=FakeAST(TextLiteral, code)), __VA_ARGS__}) -#define Match(x, _tag) ((x)->tag == _tag ? &(x)->__data._tag : (errx(1, __FILE__ ":%d This was supposed to be a " # _tag "\n", __LINE__), &(x)->__data._tag)) +#define NewAST(_file, _start, _end, ast_tag, ...) \ + (new (ast_t, .file = _file, .start = _start, .end = _end, .tag = ast_tag, .__data.ast_tag = {__VA_ARGS__})) +#define NewTypeAST(_file, _start, _end, ast_tag, ...) \ + (new (type_ast_t, .file = _file, .start = _start, .end = _end, .tag = ast_tag, .__data.ast_tag = {__VA_ARGS__})) +#define FakeAST(ast_tag, ...) (new (ast_t, .tag = ast_tag, .__data.ast_tag = {__VA_ARGS__})) +#define WrapAST(ast, ast_tag, ...) \ + (new (ast_t, .file = (ast)->file, .start = (ast)->start, .end = (ast)->end, .tag = ast_tag, \ + .__data.ast_tag = {__VA_ARGS__})) +#define TextAST(ast, _str) WrapAST(ast, TextLiteral, .str = GC_strdup(_str)) +#define LiteralCode(code, ...) \ + new (ast_t, .tag = InlineCCode, \ + .__data.InlineCCode = {.chunks = new (ast_list_t, .ast = FakeAST(TextLiteral, code)), __VA_ARGS__}) +#define Match(x, _tag) \ + ((x)->tag == _tag ? &(x)->__data._tag \ + : (errx(1, __FILE__ ":%d This was supposed to be a " #_tag "\n", __LINE__), &(x)->__data._tag)) #define DeclareMatch(var, x, _tag) __typeof((x)->__data._tag) *var = Match(x, _tag) -#define BINARY_OPERANDS(ast) ({ if (!is_binary_operation(ast)) errx(1, __FILE__ ":%d This is not a binary operation!", __LINE__); (ast)->__data.Plus; }) -#define UPDATE_OPERANDS(ast) ({ if (!is_update_assignment(ast)) errx(1, __FILE__ ":%d This is not an update assignment!", __LINE__); (ast)->__data.PlusUpdate; }) +#define BINARY_OPERANDS(ast) \ + ({ \ + if (!is_binary_operation(ast)) errx(1, __FILE__ ":%d This is not a binary operation!", __LINE__); \ + (ast)->__data.Plus; \ + }) +#define UPDATE_OPERANDS(ast) \ + ({ \ + if (!is_update_assignment(ast)) errx(1, __FILE__ ":%d This is not an update assignment!", __LINE__); \ + (ast)->__data.PlusUpdate; \ + }) -#define REVERSE_LIST(list) do { \ - __typeof(list) _prev = NULL; \ - __typeof(list) _next = NULL; \ - __typeof(list) _current = list; \ - while (_current != NULL) { \ - _next = _current->next; \ - _current->next = _prev; \ - _prev = _current; \ - _current = _next; \ - } \ - list = _prev; \ -} while(0) +#define REVERSE_LIST(list) \ + do { \ + __typeof(list) _prev = NULL; \ + __typeof(list) _next = NULL; \ + __typeof(list) _current = list; \ + while (_current != NULL) { \ + _next = _current->next; \ + _current->next = _prev; \ + _prev = _current; \ + _current = _next; \ + } \ + list = _prev; \ + } while (0) struct binding_s; typedef struct type_ast_s type_ast_t; @@ -76,7 +91,7 @@ typedef struct tag_ast_s { const char *name; arg_ast_t *fields; struct tag_ast_s *next; - bool secret:1; + bool secret : 1; } tag_ast_t; struct type_ast_s { @@ -84,13 +99,14 @@ struct type_ast_s { file_t *file; const char *start, *end; union { - struct {} UnknownTypeAST; + struct { + } UnknownTypeAST; struct { const char *name; } VarTypeAST; struct { type_ast_t *pointed; - bool is_stack:1; + bool is_stack : 1; } PointerTypeAST; struct { type_ast_t *item; @@ -112,42 +128,143 @@ struct type_ast_s { } __data; }; -#define BINOP_CASES Power: case Multiply: case Divide: case Mod: case Mod1: case Plus: case Minus: case Concat: case LeftShift: case UnsignedLeftShift: \ - case RightShift: case UnsignedRightShift: case Equals: case NotEquals: case LessThan: case LessThanOrEquals: case GreaterThan: \ - case GreaterThanOrEquals: case Compare: case And: case Or: case Xor: \ - case PowerUpdate: case MultiplyUpdate: case DivideUpdate: case ModUpdate: case Mod1Update: case PlusUpdate: case MinusUpdate: case ConcatUpdate: \ - case LeftShiftUpdate: case UnsignedLeftShiftUpdate -#define UPDATE_CASES PowerUpdate: case MultiplyUpdate: case DivideUpdate: case ModUpdate: case Mod1Update: case PlusUpdate: case MinusUpdate: \ - case ConcatUpdate: case LeftShiftUpdate: case UnsignedLeftShiftUpdate: case RightShiftUpdate: case UnsignedRightShiftUpdate: \ - case AndUpdate: case OrUpdate: case XorUpdate +#define BINOP_CASES \ + Power: \ + case Multiply: \ + case Divide: \ + case Mod: \ + case Mod1: \ + case Plus: \ + case Minus: \ + case Concat: \ + case LeftShift: \ + case UnsignedLeftShift: \ + case RightShift: \ + case UnsignedRightShift: \ + case Equals: \ + case NotEquals: \ + case LessThan: \ + case LessThanOrEquals: \ + case GreaterThan: \ + case GreaterThanOrEquals: \ + case Compare: \ + case And: \ + case Or: \ + case Xor: \ + case PowerUpdate: \ + case MultiplyUpdate: \ + case DivideUpdate: \ + case ModUpdate: \ + case Mod1Update: \ + case PlusUpdate: \ + case MinusUpdate: \ + case ConcatUpdate: \ + case LeftShiftUpdate: \ + case UnsignedLeftShiftUpdate +#define UPDATE_CASES \ + PowerUpdate: \ + case MultiplyUpdate: \ + case DivideUpdate: \ + case ModUpdate: \ + case Mod1Update: \ + case PlusUpdate: \ + case MinusUpdate: \ + case ConcatUpdate: \ + case LeftShiftUpdate: \ + case UnsignedLeftShiftUpdate: \ + case RightShiftUpdate: \ + case UnsignedRightShiftUpdate: \ + case AndUpdate: \ + case OrUpdate: \ + case XorUpdate typedef enum { Unknown = 0, - None, Bool, Var, - Int, Num, - TextLiteral, TextJoin, + None, + Bool, + Var, + Int, + Num, + TextLiteral, + TextJoin, Path, - Declare, Assign, - Power, Multiply, Divide, Mod, Mod1, Plus, Minus, Concat, LeftShift, UnsignedLeftShift, - RightShift, UnsignedRightShift, Equals, NotEquals, LessThan, LessThanOrEquals, GreaterThan, - GreaterThanOrEquals, Compare, And, Or, Xor, - PowerUpdate, MultiplyUpdate, DivideUpdate, ModUpdate, Mod1Update, PlusUpdate, MinusUpdate, ConcatUpdate, LeftShiftUpdate, UnsignedLeftShiftUpdate, - RightShiftUpdate, UnsignedRightShiftUpdate, AndUpdate, OrUpdate, XorUpdate, - Not, Negative, HeapAllocate, StackReference, - Min, Max, - List, Set, Table, TableEntry, Comprehension, - FunctionDef, Lambda, ConvertDef, - FunctionCall, MethodCall, + Declare, + Assign, + Power, + Multiply, + Divide, + Mod, + Mod1, + Plus, + Minus, + Concat, + LeftShift, + UnsignedLeftShift, + RightShift, + UnsignedRightShift, + Equals, + NotEquals, + LessThan, + LessThanOrEquals, + GreaterThan, + GreaterThanOrEquals, + Compare, + And, + Or, + Xor, + PowerUpdate, + MultiplyUpdate, + DivideUpdate, + ModUpdate, + Mod1Update, + PlusUpdate, + MinusUpdate, + ConcatUpdate, + LeftShiftUpdate, + UnsignedLeftShiftUpdate, + RightShiftUpdate, + UnsignedRightShiftUpdate, + AndUpdate, + OrUpdate, + XorUpdate, + Not, + Negative, + HeapAllocate, + StackReference, + Min, + Max, + List, + Set, + Table, + TableEntry, + Comprehension, + FunctionDef, + Lambda, + ConvertDef, + FunctionCall, + MethodCall, Block, - For, While, If, When, Repeat, + For, + While, + If, + When, + Repeat, Reduction, - Skip, Stop, Pass, + Skip, + Stop, + Pass, Defer, Return, Extern, - StructDef, EnumDef, LangDef, - Index, FieldAccess, Optional, NonOptional, - DocTest, Assert, + StructDef, + EnumDef, + LangDef, + Index, + FieldAccess, + Optional, + NonOptional, + DocTest, + Assert, Use, InlineCCode, Deserialize, @@ -160,8 +277,10 @@ struct ast_s { file_t *file; const char *start, *end; union { - struct {} Unknown; - struct {} None; + struct { + } Unknown; + struct { + } None; struct { bool b; } Bool; @@ -180,7 +299,7 @@ struct ast_s { struct { const char *lang; ast_list_t *children; - bool colorize:1; + bool colorize : 1; } TextJoin; struct { const char *path; @@ -189,16 +308,16 @@ struct ast_s { ast_t *var; type_ast_t *type; ast_t *value; - bool top_level:1; + bool top_level : 1; } Declare; struct { ast_list_t *targets, *values; } Assign; binary_operands_t Power, Multiply, Divide, Mod, Mod1, Plus, Minus, Concat, LeftShift, UnsignedLeftShift, - RightShift, UnsignedRightShift, Equals, NotEquals, LessThan, LessThanOrEquals, GreaterThan, - GreaterThanOrEquals, Compare, And, Or, Xor, - PowerUpdate, MultiplyUpdate, DivideUpdate, ModUpdate, Mod1Update, PlusUpdate, MinusUpdate, ConcatUpdate, LeftShiftUpdate, UnsignedLeftShiftUpdate, - RightShiftUpdate, UnsignedRightShiftUpdate, AndUpdate, OrUpdate, XorUpdate; + RightShift, UnsignedRightShift, Equals, NotEquals, LessThan, LessThanOrEquals, GreaterThan, + GreaterThanOrEquals, Compare, And, Or, Xor, PowerUpdate, MultiplyUpdate, DivideUpdate, ModUpdate, + Mod1Update, PlusUpdate, MinusUpdate, ConcatUpdate, LeftShiftUpdate, UnsignedLeftShiftUpdate, + RightShiftUpdate, UnsignedRightShiftUpdate, AndUpdate, OrUpdate, XorUpdate; struct { ast_t *value; } Not, Negative, HeapAllocate, StackReference; @@ -281,7 +400,8 @@ struct ast_s { struct { const char *target; } Skip, Stop; - struct {} Pass; + struct { + } Pass; struct { ast_t *body; } Defer; @@ -296,7 +416,7 @@ struct ast_s { const char *name; arg_ast_t *fields; ast_t *namespace; - bool secret:1, external:1, opaque:1; + bool secret : 1, external : 1, opaque : 1; } StructDef; struct { const char *name; @@ -320,7 +440,7 @@ struct ast_s { } Optional, NonOptional; struct { ast_t *expr, *expected; - bool skip_source:1; + bool skip_source : 1; } DocTest; struct { ast_t *expr, *message; diff --git a/src/compile.c b/src/compile.c index a747b870..438e6cce 100644 --- a/src/compile.c +++ b/src/compile.c @@ -1,14 +1,14 @@ // Compilation logic #include <ctype.h> -#include <glob.h> #include <gc.h> +#include <glob.h> #include <gmp.h> #include <stdio.h> #include <uninorm.h> #include "ast.h" -#include "config.h" #include "compile.h" +#include "config.h" #include "enums.h" #include "environment.h" #include "modules.h" @@ -22,7 +22,7 @@ #include "structs.h" #include "typecheck.h" -typedef ast_t* (*comprehension_body_t)(ast_t*, ast_t*); +typedef ast_t *(*comprehension_body_t)(ast_t *, ast_t *); static Text_t compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool needs_incref); static Text_t compile_text(env_t *env, ast_t *ast, Text_t color); @@ -47,16 +47,11 @@ static ast_t *add_to_table_comprehension(ast_t *entry, ast_t *subject); static ast_t *add_to_set_comprehension(ast_t *item, ast_t *subject); static Text_t compile_lvalue(env_t *env, ast_t *ast); -static Text_t quoted_str(const char *str) { - return Text$quoted(Text$from_str(str), false, Text("\"")); -} +static Text_t quoted_str(const char *str) { return Text$quoted(Text$from_str(str), false, Text("\"")); } -static inline Text_t quoted_text(Text_t text) { - return Text$quoted(text, false, Text("\"")); -} +static inline Text_t quoted_text(Text_t text) { return Text$quoted(text, false, Text("\"")); } -Text_t promote_to_optional(type_t *t, Text_t code) -{ +Text_t promote_to_optional(type_t *t, Text_t code) { if (t == PATH_TYPE || t == PATH_TYPE_TYPE) { return code; } else if (t->tag == IntType) { @@ -71,27 +66,23 @@ Text_t promote_to_optional(type_t *t, Text_t code) } else if (t->tag == ByteType) { return Texts("((OptionalByte_t){.value=", code, "})"); } else if (t->tag == StructType) { - return Texts("({ ", compile_type(Type(OptionalType, .type=t)), " nonnull = {.value=", code, "}; nonnull.is_none = false; nonnull; })"); + return Texts("({ ", compile_type(Type(OptionalType, .type = t)), " nonnull = {.value=", code, + "}; nonnull.is_none = false; nonnull; })"); } else { return code; } } -static Text_t with_source_info(env_t *env, ast_t *ast, Text_t code) -{ - if (code.length == 0 || !ast || !ast->file || !env->do_source_mapping) - return code; +static Text_t with_source_info(env_t *env, ast_t *ast, Text_t code) { + if (code.length == 0 || !ast || !ast->file || !env->do_source_mapping) return code; int64_t line = get_line_number(ast->file, ast->start); return Texts("\n#line ", String(line), "\n", code); } -static bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t *needed) -{ - if (type_eq(actual, needed)) - return true; +static bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t *needed) { + if (type_eq(actual, needed)) return true; - if (!can_promote(actual, needed)) - return false; + if (!can_promote(actual, needed)) return false; if (needed->tag == ClosureType && actual->tag == FunctionType) { *code = Texts("((Closure_t){", *code, ", NULL})"); @@ -100,8 +91,7 @@ static bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t // Empty promotion: type_t *more_complete = most_complete_type(actual, needed); - if (more_complete) - return true; + if (more_complete) return true; // Optional promotion: if (needed->tag == OptionalType && type_eq(actual, Match(needed, OptionalType)->type)) { @@ -116,28 +106,25 @@ static bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t } // Lang to Text_t: - if (actual->tag == TextType && needed->tag == TextType && streq(Match(needed, TextType)->lang, "Text")) - return true; + if (actual->tag == TextType && needed->tag == TextType && streq(Match(needed, TextType)->lang, "Text")) return true; // Automatic optional checking for nums: if (needed->tag == NumType && actual->tag == OptionalType && Match(actual, OptionalType)->type->tag == NumType) { int64_t line = get_line_number(ast->file, ast->start); - *code = Texts("({ ", compile_declaration(actual, Text("opt")), " = ", *code, "; ", - "if unlikely (", check_none(actual, Text("opt")), ")\n", - "#line ", String(line), "\n", - "fail_source(", quoted_str(ast->file->filename), ", ", - String((int64_t)(ast->start - ast->file->text)), ", ", - String((int64_t)(ast->end - ast->file->text)), ", ", - "\"This was expected to be a value, but it's none\");\n", - optional_into_nonnone(actual, Text("opt")), "; })"); + *code = Texts("({ ", compile_declaration(actual, Text("opt")), " = ", *code, "; ", "if unlikely (", + check_none(actual, Text("opt")), ")\n", "#line ", String(line), "\n", "fail_source(", + quoted_str(ast->file->filename), ", ", String((int64_t)(ast->start - ast->file->text)), ", ", + String((int64_t)(ast->end - ast->file->text)), ", ", + "\"This was expected to be a value, but it's none\");\n", + optional_into_nonnone(actual, Text("opt")), "; })"); return true; } // Numeric promotions/demotions if ((is_numeric_type(actual) || actual->tag == BoolType) && (is_numeric_type(needed) || needed->tag == BoolType)) { - arg_ast_t *args = new(arg_ast_t, .value=LiteralCode(*code, .type=actual)); - binding_t *constructor = get_constructor(env, needed, args, - env->current_type != NULL && type_eq(env->current_type, value_type(needed))); + arg_ast_t *args = new (arg_ast_t, .value = LiteralCode(*code, .type = actual)); + binding_t *constructor = get_constructor( + env, needed, args, env->current_type != NULL && type_eq(env->current_type, value_type(needed))); if (constructor) { DeclareMatch(fn, constructor->type, FunctionType); if (fn->args->next == NULL) { @@ -152,8 +139,7 @@ static bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t binding_t *b = get_binding(Match(needed, EnumType)->env, tag); assert(b && b->type->tag == FunctionType); // Single-value enum constructor: - if (!promote(env, ast, code, actual, Match(b->type, FunctionType)->args->type)) - return false; + if (!promote(env, ast, code, actual, Match(b->type, FunctionType)->args->type)) return false; *code = Texts(b->code, "(", *code, ")"); return true; } @@ -165,22 +151,18 @@ static bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t } // Automatic dereferencing: - if (actual->tag == PointerType - && can_promote(Match(actual, PointerType)->pointed, needed)) { + if (actual->tag == PointerType && can_promote(Match(actual, PointerType)->pointed, needed)) { *code = Texts("*(", *code, ")"); return promote(env, ast, code, Match(actual, PointerType)->pointed, needed); } // Stack ref promotion: - if (actual->tag == PointerType && needed->tag == PointerType) - return true; + if (actual->tag == PointerType && needed->tag == PointerType) return true; // Cross-promotion between tables with default values and without - if (needed->tag == TableType && actual->tag == TableType) - return true; + if (needed->tag == TableType && actual->tag == TableType) return true; - if (needed->tag == ClosureType && actual->tag == ClosureType) - return true; + if (needed->tag == ClosureType && actual->tag == ClosureType) return true; if (needed->tag == FunctionType && actual->tag == FunctionType) { *code = Texts("(", compile_type(needed), ")", *code); @@ -197,29 +179,24 @@ static bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t return false; } -Text_t compile_maybe_incref(env_t *env, ast_t *ast, type_t *t) -{ +Text_t compile_maybe_incref(env_t *env, ast_t *ast, type_t *t) { if (is_idempotent(ast) && can_be_mutated(env, ast)) { - if (t->tag == ListType) - return Texts("LIST_COPY(", compile_to_type(env, ast, t), ")"); + if (t->tag == ListType) return Texts("LIST_COPY(", compile_to_type(env, ast, t), ")"); else if (t->tag == TableType || t->tag == SetType) return Texts("TABLE_COPY(", compile_to_type(env, ast, t), ")"); } return compile_to_type(env, ast, t); } -static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t *env, ast_t *ast) -{ - if (ast == NULL) - return; +static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t *env, ast_t *ast) { + if (ast == NULL) return; switch (ast->tag) { case Var: { binding_t *b = get_binding(enclosing_scope, Match(ast, Var)->name); if (b) { binding_t *shadow = get_binding(env, Match(ast, Var)->name); - if (!shadow || shadow == b) - Table$str_set(closed_vars, Match(ast, Var)->name, b); + if (!shadow || shadow == b) Table$str_set(closed_vars, Match(ast, Var)->name, b); } break; } @@ -247,7 +224,10 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t add_closed_vars(closed_vars, enclosing_scope, env, binop.rhs); break; } - case Not: case Negative: case HeapAllocate: case StackReference: { + case Not: + case Negative: + case HeapAllocate: + case StackReference: { // UNSAFE: ast_t *value = ast->__data.Not.value; // END UNSAFE @@ -291,16 +271,15 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t case Comprehension: { DeclareMatch(comp, ast, Comprehension); if (comp->expr->tag == Comprehension) { // Nested comprehension - ast_t *body = comp->filter ? WrapAST(ast, If, .condition=comp->filter, .body=comp->expr) : comp->expr; - ast_t *loop = WrapAST(ast, For, .vars=comp->vars, .iter=comp->iter, .body=body); + ast_t *body = comp->filter ? WrapAST(ast, If, .condition = comp->filter, .body = comp->expr) : comp->expr; + ast_t *loop = WrapAST(ast, For, .vars = comp->vars, .iter = comp->iter, .body = body); return add_closed_vars(closed_vars, enclosing_scope, env, loop); } // List/Set/Table comprehension: ast_t *body = comp->expr; - if (comp->filter) - body = WrapAST(comp->expr, If, .condition=comp->filter, .body=body); - ast_t *loop = WrapAST(ast, For, .vars=comp->vars, .iter=comp->iter, .body=body); + if (comp->filter) body = WrapAST(comp->expr, If, .condition = comp->filter, .body = body); + ast_t *loop = WrapAST(ast, For, .vars = comp->vars, .iter = comp->iter, .body = body); add_closed_vars(closed_vars, enclosing_scope, env, loop); break; } @@ -356,8 +335,7 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t ast_t *var = Match(condition, Declare)->var; type_t *cond_t = get_type(truthy_scope, var); if (cond_t->tag == OptionalType) { - set_binding(truthy_scope, Match(var, Var)->name, - Match(cond_t, OptionalType)->type, EMPTY_TEXT); + set_binding(truthy_scope, Match(var, Var)->name, Match(cond_t, OptionalType)->type, EMPTY_TEXT); } add_closed_vars(closed_vars, enclosing_scope, truthy_scope, if_->body); add_closed_vars(closed_vars, enclosing_scope, env, if_->else_body); @@ -367,8 +345,7 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t type_t *cond_t = get_type(env, condition); if (condition->tag == Var && cond_t->tag == OptionalType) { truthy_scope = fresh_scope(env); - set_binding(truthy_scope, Match(condition, Var)->name, - Match(cond_t, OptionalType)->type, EMPTY_TEXT); + set_binding(truthy_scope, Match(condition, Var)->name, Match(cond_t, OptionalType)->type, EMPTY_TEXT); } add_closed_vars(closed_vars, enclosing_scope, truthy_scope, if_->body); add_closed_vars(closed_vars, enclosing_scope, env, if_->else_body); @@ -386,20 +363,17 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t add_closed_vars(closed_vars, enclosing_scope, env, clause->body); } - if (when->else_body) - add_closed_vars(closed_vars, enclosing_scope, env, when->else_body); + if (when->else_body) add_closed_vars(closed_vars, enclosing_scope, env, when->else_body); return; } DeclareMatch(enum_t, subject_t, EnumType); for (when_clause_t *clause = when->clauses; clause; clause = clause->next) { const char *clause_tag_name; - if (clause->pattern->tag == Var) - clause_tag_name = Match(clause->pattern, Var)->name; + if (clause->pattern->tag == Var) clause_tag_name = Match(clause->pattern, Var)->name; else if (clause->pattern->tag == FunctionCall && Match(clause->pattern, FunctionCall)->fn->tag == Var) clause_tag_name = Match(Match(clause->pattern, FunctionCall)->fn, Var)->name; - else - code_err(clause->pattern, "This is not a valid pattern for a ", type_to_str(subject_t), " enum"); + else code_err(clause->pattern, "This is not a valid pattern for a ", type_to_str(subject_t), " enum"); type_t *tag_type = NULL; for (tag_t *tag = enum_t->tags; tag; tag = tag->next) { @@ -412,8 +386,7 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t env_t *scope = when_clause_scope(env, subject_t, clause); add_closed_vars(closed_vars, enclosing_scope, scope, clause->body); } - if (when->else_body) - add_closed_vars(closed_vars, enclosing_scope, env, when->else_body); + if (when->else_body) add_closed_vars(closed_vars, enclosing_scope, env, when->else_body); break; } case Repeat: { @@ -424,7 +397,8 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t DeclareMatch(reduction, ast, Reduction); static int64_t next_id = 1; ast_t *item = FakeAST(Var, String("$it", next_id++)); - ast_t *loop = FakeAST(For, .vars=new(ast_list_t, .ast=item), .iter=reduction->iter, .body=FakeAST(Pass)); + ast_t *loop = + FakeAST(For, .vars = new (ast_list_t, .ast = item), .iter = reduction->iter, .body = FakeAST(Pass)); env_t *scope = for_scope(env, loop); add_closed_vars(closed_vars, enclosing_scope, scope, reduction->key ? reduction->key : item); break; @@ -472,16 +446,20 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, ExplicitlyTyped)->ast); break; } - case Use: case FunctionDef: case ConvertDef: case StructDef: case EnumDef: case LangDef: case Extend: { + case Use: + case FunctionDef: + case ConvertDef: + case StructDef: + case EnumDef: + case LangDef: + case Extend: { errx(1, "Definitions should not be reachable in a closure."); } - default: - break; + default: break; } } -static Table_t get_closed_vars(env_t *env, arg_ast_t *args, ast_t *block) -{ +static Table_t get_closed_vars(env_t *env, arg_ast_t *args, ast_t *block) { env_t *body_scope = fresh_scope(env); for (arg_ast_t *arg = args; arg; arg = arg->next) { type_t *arg_type = get_arg_ast_type(env, arg); @@ -493,8 +471,7 @@ static Table_t get_closed_vars(env_t *env, arg_ast_t *args, ast_t *block) return closed_vars; } -Text_t compile_declaration(type_t *t, Text_t name) -{ +Text_t compile_declaration(type_t *t, Text_t name) { if (t->tag == FunctionType) { DeclareMatch(fn, t, FunctionType); Text_t code = Texts(compile_type(fn->ret), " (*", name, ")("); @@ -511,10 +488,8 @@ Text_t compile_declaration(type_t *t, Text_t name) } } -static Text_t compile_update_assignment(env_t *env, ast_t *ast) -{ - if (!is_update_assignment(ast)) - code_err(ast, "This is not an update assignment"); +static Text_t compile_update_assignment(env_t *env, ast_t *ast) { + if (!is_update_assignment(ast)) code_err(ast, "This is not an update assignment"); binary_operands_t update = UPDATE_OPERANDS(ast); @@ -557,35 +532,34 @@ static Text_t compile_update_assignment(env_t *env, ast_t *ast) } case AndUpdate: { if (lhs_t->tag == BoolType) - update_assignment = Texts("if (", lhs, ") ", lhs, " = ", compile_to_type(env, update.rhs, Type(BoolType)), ";"); + update_assignment = + Texts("if (", lhs, ") ", lhs, " = ", compile_to_type(env, update.rhs, Type(BoolType)), ";"); break; } case OrUpdate: { if (lhs_t->tag == BoolType) - update_assignment = Texts("if (!", lhs, ") ", lhs, " = ", compile_to_type(env, update.rhs, Type(BoolType)), ";"); + update_assignment = + Texts("if (!", lhs, ") ", lhs, " = ", compile_to_type(env, update.rhs, Type(BoolType)), ";"); break; } default: break; } if (update_assignment.length == 0) { - ast_t *binop = new(ast_t); + ast_t *binop = new (ast_t); *binop = *ast; binop->tag = binop_tag(binop->tag); - if (needs_idemotency_fix) - binop->__data.Plus.lhs = LiteralCode(Text("*lhs"), .type=lhs_t); + if (needs_idemotency_fix) binop->__data.Plus.lhs = LiteralCode(Text("*lhs"), .type = lhs_t); update_assignment = Texts(lhs, " = ", compile_to_type(env, binop, lhs_t), ";"); } - + if (needs_idemotency_fix) - return Texts("{ ", compile_declaration(Type(PointerType, .pointed=lhs_t), Text("lhs")), " = &", compile_lvalue(env, update.lhs), "; ", - update_assignment, "; }"); - else - return update_assignment; + return Texts("{ ", compile_declaration(Type(PointerType, .pointed = lhs_t), Text("lhs")), " = &", + compile_lvalue(env, update.lhs), "; ", update_assignment, "; }"); + else return update_assignment; } -static Text_t compile_binary_op(env_t *env, ast_t *ast) -{ +static Text_t compile_binary_op(env_t *env, ast_t *ast) { binary_operands_t binop = BINARY_OPERANDS(ast); type_t *lhs_t = get_type(env, binop.lhs); type_t *rhs_t = get_type(env, binop.rhs); @@ -594,7 +568,7 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) binding_t *b = get_metamethod_binding(env, ast->tag, binop.lhs, binop.rhs, overall_t); if (!b) b = get_metamethod_binding(env, ast->tag, binop.rhs, binop.lhs, overall_t); if (b) { - arg_ast_t *args = new(arg_ast_t, .value=binop.lhs, .next=new(arg_ast_t, .value=binop.rhs)); + arg_ast_t *args = new (arg_ast_t, .value = binop.lhs, .next = new (arg_ast_t, .value = binop.rhs)); DeclareMatch(fn, b->type, FunctionType); return Texts(b->code, "(", compile_arguments(env, ast, fn->args, args), ")"); } @@ -604,7 +578,7 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) if (b && b->type->tag == FunctionType) { DeclareMatch(fn, b->type, FunctionType); if (type_eq(fn->ret, rhs_t)) { - arg_ast_t *args = new(arg_ast_t, .value=binop.rhs, .next=new(arg_ast_t, .value=binop.lhs)); + arg_ast_t *args = new (arg_ast_t, .value = binop.rhs, .next = new (arg_ast_t, .value = binop.lhs)); if (is_valid_call(env, fn->args, args, true)) return Texts(b->code, "(", compile_arguments(env, ast, fn->args, args), ")"); } @@ -614,7 +588,7 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) if (b && b->type->tag == FunctionType) { DeclareMatch(fn, b->type, FunctionType); if (type_eq(fn->ret, lhs_t)) { - arg_ast_t *args = new(arg_ast_t, .value=binop.lhs, .next=new(arg_ast_t, .value=binop.rhs)); + arg_ast_t *args = new (arg_ast_t, .value = binop.lhs, .next = new (arg_ast_t, .value = binop.rhs)); if (is_valid_call(env, fn->args, args, true)) return Texts(b->code, "(", compile_arguments(env, ast, fn->args, args), ")"); } @@ -624,7 +598,7 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) if (b && b->type->tag == FunctionType) { DeclareMatch(fn, b->type, FunctionType); if (type_eq(fn->ret, lhs_t)) { - arg_ast_t *args = new(arg_ast_t, .value=binop.lhs, .next=new(arg_ast_t, .value=binop.rhs)); + arg_ast_t *args = new (arg_ast_t, .value = binop.lhs, .next = new (arg_ast_t, .value = binop.rhs)); if (is_valid_call(env, fn->args, args, true)) return Texts(b->code, "(", compile_arguments(env, ast, fn->args, args), ")"); } @@ -634,7 +608,7 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) if (b && b->type->tag == FunctionType) { DeclareMatch(fn, b->type, FunctionType); if (type_eq(fn->ret, lhs_t)) { - arg_ast_t *args = new(arg_ast_t, .value=binop.lhs, .next=new(arg_ast_t, .value=binop.rhs)); + arg_ast_t *args = new (arg_ast_t, .value = binop.lhs, .next = new (arg_ast_t, .value = binop.rhs)); if (is_valid_call(env, fn->args, args, true)) return Texts(b->code, "(", compile_arguments(env, ast, fn->args, args), ")"); } @@ -643,29 +617,31 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) if (ast->tag == Or && lhs_t->tag == OptionalType) { if (rhs_t->tag == AbortType || rhs_t->tag == ReturnType) { - return Texts("({ ", compile_declaration(lhs_t, Text("lhs")), " = ", compile(env, binop.lhs), "; ", - "if (", check_none(lhs_t, Text("lhs")), ") ", compile_statement(env, binop.rhs), " ", - optional_into_nonnone(lhs_t, Text("lhs")), "; })"); + return Texts("({ ", compile_declaration(lhs_t, Text("lhs")), " = ", compile(env, binop.lhs), "; ", "if (", + check_none(lhs_t, Text("lhs")), ") ", compile_statement(env, binop.rhs), " ", + optional_into_nonnone(lhs_t, Text("lhs")), "; })"); } if (is_incomplete_type(rhs_t)) { type_t *complete = most_complete_type(rhs_t, Match(lhs_t, OptionalType)->type); if (complete == NULL) - code_err(binop.rhs, "I don't know how to convert a ", type_to_str(rhs_t), " to a ", type_to_str(Match(lhs_t, OptionalType)->type)); + code_err(binop.rhs, "I don't know how to convert a ", type_to_str(rhs_t), " to a ", + type_to_str(Match(lhs_t, OptionalType)->type)); rhs_t = complete; } if (rhs_t->tag == OptionalType && type_eq(lhs_t, rhs_t)) { return Texts("({ ", compile_declaration(lhs_t, Text("lhs")), " = ", compile(env, binop.lhs), "; ", - check_none(lhs_t, Text("lhs")), " ? ", compile(env, binop.rhs), " : lhs; })"); + check_none(lhs_t, Text("lhs")), " ? ", compile(env, binop.rhs), " : lhs; })"); } else if (rhs_t->tag != OptionalType && type_eq(Match(lhs_t, OptionalType)->type, rhs_t)) { return Texts("({ ", compile_declaration(lhs_t, Text("lhs")), " = ", compile(env, binop.lhs), "; ", - check_none(lhs_t, Text("lhs")), " ? ", compile(env, binop.rhs), " : ", - optional_into_nonnone(lhs_t, Text("lhs")), "; })"); + check_none(lhs_t, Text("lhs")), " ? ", compile(env, binop.rhs), " : ", + optional_into_nonnone(lhs_t, Text("lhs")), "; })"); } else if (rhs_t->tag == BoolType) { return Texts("((!", check_none(lhs_t, compile(env, binop.lhs)), ") || ", compile(env, binop.rhs), ")"); } else { - code_err(ast, "I don't know how to do an 'or' operation between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, "I don't know how to do an 'or' operation between ", type_to_str(lhs_t), " and ", + type_to_str(rhs_t)); } } @@ -678,70 +654,98 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) code_err(ast, "Exponentiation is only supported for Num types, not ", type_to_str(overall_t)); if (overall_t->tag == NumType && Match(overall_t, NumType)->bits == TYPE_NBITS32) return Texts("powf(", lhs, ", ", rhs, ")"); - else - return Texts("pow(", lhs, ", ", rhs, ")"); + else return Texts("pow(", lhs, ", ", rhs, ")"); } case Multiply: { if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("(", lhs, " * ", rhs, ")"); } case Divide: { if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("(", lhs, " / ", rhs, ")"); } case Mod: { if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("(", lhs, " % ", rhs, ")"); } case Mod1: { if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("((((", lhs, ")-1) % (", rhs, ")) + 1)"); } case Plus: { if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("(", lhs, " + ", rhs, ")"); } case Minus: { if (overall_t->tag == SetType) return Texts("Table$without(", lhs, ", ", rhs, ", ", compile_type_info(overall_t), ")"); if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("(", lhs, " - ", rhs, ")"); } case LeftShift: { if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("(", lhs, " << ", rhs, ")"); } case RightShift: { if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("(", lhs, " >> ", rhs, ")"); } case UnsignedLeftShift: { if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("(", compile_type(overall_t), ")((", compile_unsigned_type(lhs_t), ")", lhs, " << ", rhs, ")"); } case UnsignedRightShift: { if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("(", compile_type(overall_t), ")((", compile_unsigned_type(lhs_t), ")", lhs, " >> ", rhs, ")"); } case And: { - if (overall_t->tag == BoolType) - return Texts("(", lhs, " && ", rhs, ")"); - else if (overall_t->tag == IntType || overall_t->tag == ByteType) - return Texts("(", lhs, " & ", rhs, ")"); + if (overall_t->tag == BoolType) return Texts("(", lhs, " && ", rhs, ")"); + else if (overall_t->tag == IntType || overall_t->tag == ByteType) return Texts("(", lhs, " & ", rhs, ")"); else if (overall_t->tag == SetType) return Texts("Table$overlap(", lhs, ", ", rhs, ", ", compile_type_info(overall_t), ")"); else - code_err(ast, "The 'and' operator isn't supported between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), " values"); + code_err(ast, "The 'and' operator isn't supported between ", type_to_str(lhs_t), " and ", + type_to_str(rhs_t), " values"); } case Compare: { return Texts("generic_compare(stack(", lhs, "), stack(", rhs, "), ", compile_type_info(overall_t), ")"); @@ -754,7 +758,8 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) } else if (overall_t->tag == SetType) { return Texts("Table$with(", lhs, ", ", rhs, ", ", compile_type_info(overall_t), ")"); } else { - code_err(ast, "The 'or' operator isn't supported between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), " values"); + code_err(ast, "The 'or' operator isn't supported between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), + " values"); } } case Xor: { @@ -764,20 +769,22 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) else if (overall_t->tag == SetType) return Texts("Table$xor(", lhs, ", ", rhs, ", ", compile_type_info(overall_t), ")"); else - code_err(ast, "The 'xor' operator isn't supported between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), " values"); + code_err(ast, "The 'xor' operator isn't supported between ", type_to_str(lhs_t), " and ", + type_to_str(rhs_t), " values"); } case Concat: { - if (overall_t == PATH_TYPE) - return Texts("Path$concat(", lhs, ", ", rhs, ")"); + if (overall_t == PATH_TYPE) return Texts("Path$concat(", lhs, ", ", rhs, ")"); switch (overall_t->tag) { case TextType: { return Texts("Text$concat(", lhs, ", ", rhs, ")"); } case ListType: { - return Texts("List$concat(", lhs, ", ", rhs, ", sizeof(", compile_type(Match(overall_t, ListType)->item_type), "))"); + return Texts("List$concat(", lhs, ", ", rhs, ", sizeof(", + compile_type(Match(overall_t, ListType)->item_type), "))"); } default: - code_err(ast, "Concatenation isn't supported between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), " values"); + code_err(ast, "Concatenation isn't supported between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), + " values"); } } default: errx(1, "Not a valid binary operation: %s", ast_to_sexp_str(ast)); @@ -785,10 +792,8 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) return EMPTY_TEXT; } -PUREFUNC Text_t compile_unsigned_type(type_t *t) -{ - if (t->tag != IntType) - errx(1, "Not an int type, so unsigned doesn't make sense!"); +PUREFUNC Text_t compile_unsigned_type(type_t *t) { + if (t->tag != IntType) errx(1, "Not an int type, so unsigned doesn't make sense!"); switch (Match(t, IntType)->bits) { case TYPE_IBITS8: return Text("uint8_t"); case TYPE_IBITS16: return Text("uint16_t"); @@ -799,8 +804,7 @@ PUREFUNC Text_t compile_unsigned_type(type_t *t) return EMPTY_TEXT; } -Text_t compile_type(type_t *t) -{ +Text_t compile_type(type_t *t) { if (t == PATH_TYPE) return Text("Path_t"); else if (t == PATH_TYPE_TYPE) return Text("PathType_t"); @@ -814,13 +818,13 @@ Text_t compile_type(type_t *t) case CStringType: return Text("const char*"); case BigIntType: return Text("Int_t"); case IntType: return Texts("Int", String(Match(t, IntType)->bits), "_t"); - case NumType: return Match(t, NumType)->bits == TYPE_NBITS64 ? Text("Num_t") : Texts("Num", String(Match(t, NumType)->bits), "_t"); + case NumType: + return Match(t, NumType)->bits == TYPE_NBITS64 ? Text("Num_t") + : Texts("Num", String(Match(t, NumType)->bits), "_t"); case TextType: { DeclareMatch(text, t, TextType); - if (!text->lang || streq(text->lang, "Text")) - return Text("Text_t"); - else - return namespace_name(text->env, text->env->namespace, Text("$type")); + if (!text->lang || streq(text->lang, "Text")) return Text("Text_t"); + else return namespace_name(text->env, text->env->namespace, Text("$type")); } case ListType: return Text("List_t"); case SetType: return Text("Table_t"); @@ -832,8 +836,7 @@ Text_t compile_type(type_t *t) code = Texts(code, compile_type(arg->type)); if (arg->next) code = Texts(code, ", "); } - if (!fn->args) - code = Texts(code, "void"); + if (!fn->args) code = Texts(code, "void"); return Texts(code, ")"); } case ClosureType: return Text("Closure_t"); @@ -850,24 +853,27 @@ Text_t compile_type(type_t *t) case OptionalType: { type_t *nonnull = Match(t, OptionalType)->type; switch (nonnull->tag) { - case CStringType: case FunctionType: case ClosureType: - case PointerType: case EnumType: - return compile_type(nonnull); - case TextType: - return Match(nonnull, TextType)->lang ? compile_type(nonnull) : Text("OptionalText_t"); - case IntType: case BigIntType: case NumType: case BoolType: case ByteType: - case ListType: case TableType: case SetType: - return Texts("Optional", compile_type(nonnull)); + case CStringType: + case FunctionType: + case ClosureType: + case PointerType: + case EnumType: return compile_type(nonnull); + case TextType: return Match(nonnull, TextType)->lang ? compile_type(nonnull) : Text("OptionalText_t"); + case IntType: + case BigIntType: + case NumType: + case BoolType: + case ByteType: + case ListType: + case TableType: + case SetType: return Texts("Optional", compile_type(nonnull)); case StructType: { - if (nonnull == PATH_TYPE) - return Text("OptionalPath_t"); - if (nonnull == PATH_TYPE_TYPE) - return Text("OptionalPathType_t"); + if (nonnull == PATH_TYPE) return Text("OptionalPath_t"); + if (nonnull == PATH_TYPE_TYPE) return Text("OptionalPathType_t"); DeclareMatch(s, nonnull, StructType); return namespace_name(s->env, s->env->namespace->parent, Texts("$Optional", s->name, "$$type")); } - default: - compiler_err(NULL, NULL, NULL, "Optional types are not supported for: ", type_to_str(t)); + default: compiler_err(NULL, NULL, NULL, "Optional types are not supported for: ", type_to_str(t)); } } case TypeInfoType: return Text("TypeInfo_t"); @@ -876,18 +882,19 @@ Text_t compile_type(type_t *t) return EMPTY_TEXT; } -Text_t compile_lvalue(env_t *env, ast_t *ast) -{ +Text_t compile_lvalue(env_t *env, ast_t *ast) { if (!can_be_mutated(env, ast)) { if (ast->tag == Index) { ast_t *subject = Match(ast, Index)->indexed; - code_err(subject, "This is an immutable value, you can't mutate its contents"); + code_err(subject, "This is an immutable value, you can't mutate " + "its contents"); } else if (ast->tag == FieldAccess) { ast_t *subject = Match(ast, FieldAccess)->fielded; type_t *t = get_type(env, subject); code_err(subject, "This is an immutable ", type_to_str(t), " value, you can't assign to its fields"); } else { - code_err(ast, "This is a value of type ", type_to_str(get_type(env, ast)), " and can't be used as an assignment target"); + code_err(ast, "This is a value of type ", type_to_str(get_type(env, ast)), + " and can't be used as an assignment target"); } } @@ -895,47 +902,44 @@ Text_t compile_lvalue(env_t *env, ast_t *ast) DeclareMatch(index, ast, Index); type_t *container_t = get_type(env, index->indexed); if (container_t->tag == OptionalType) - code_err(index->indexed, "This value might be none, so it can't be safely used as an assignment target"); + code_err(index->indexed, "This value might be none, so it can't be " + "safely used as an assignment target"); - if (!index->index && container_t->tag == PointerType) - return compile(env, ast); + if (!index->index && container_t->tag == PointerType) return compile(env, ast); container_t = value_type(container_t); type_t *index_t = get_type(env, index->index); if (container_t->tag == ListType) { Text_t target_code = compile_to_pointer_depth(env, index->indexed, 1, false); type_t *item_type = Match(container_t, ListType)->item_type; - Text_t index_code = index->index->tag == Int - ? compile_int_to_type(env, index->index, Type(IntType, .bits=TYPE_IBITS64)) - : (index_t->tag == BigIntType ? Texts("Int64$from_int(", compile(env, index->index), ", no)") - : Texts("(Int64_t)(", compile(env, index->index), ")")); + Text_t index_code = + index->index->tag == Int + ? compile_int_to_type(env, index->index, Type(IntType, .bits = TYPE_IBITS64)) + : (index_t->tag == BigIntType ? Texts("Int64$from_int(", compile(env, index->index), ", no)") + : Texts("(Int64_t)(", compile(env, index->index), ")")); if (index->unchecked) { - return Texts("List_lvalue_unchecked(", compile_type(item_type), ", ", target_code, ", ", - index_code, ")"); + return Texts("List_lvalue_unchecked(", compile_type(item_type), ", ", target_code, ", ", index_code, + ")"); } else { - return Texts("List_lvalue(", compile_type(item_type), ", ", target_code, ", ", - index_code, - ", ", String((int)(ast->start - ast->file->text)), - ", ", String((int)(ast->end - ast->file->text)), ")"); + return Texts("List_lvalue(", compile_type(item_type), ", ", target_code, ", ", index_code, ", ", + String((int)(ast->start - ast->file->text)), ", ", + String((int)(ast->end - ast->file->text)), ")"); } } else if (container_t->tag == TableType) { DeclareMatch(table_type, container_t, TableType); if (table_type->default_value) { type_t *value_type = get_type(env, table_type->default_value); - return Texts("*Table$get_or_setdefault(", - compile_to_pointer_depth(env, index->indexed, 1, false), ", ", - compile_type(table_type->key_type), ", ", - compile_type(value_type), ", ", - compile_to_type(env, index->index, table_type->key_type), ", ", - compile_to_type(env, table_type->default_value, table_type->value_type), ", ", - compile_type_info(container_t), ")"); + return Texts("*Table$get_or_setdefault(", compile_to_pointer_depth(env, index->indexed, 1, false), ", ", + compile_type(table_type->key_type), ", ", compile_type(value_type), ", ", + compile_to_type(env, index->index, table_type->key_type), ", ", + compile_to_type(env, table_type->default_value, table_type->value_type), ", ", + compile_type_info(container_t), ")"); } - if (index->unchecked) - code_err(ast, "Table indexes cannot be unchecked"); + if (index->unchecked) code_err(ast, "Table indexes cannot be unchecked"); return Texts("*(", compile_type(Type(PointerType, table_type->value_type)), ")Table$reserve(", - compile_to_pointer_depth(env, index->indexed, 1, false), ", ", - compile_to_type(env, index->index, Type(PointerType, table_type->key_type, .is_stack=true)), ", NULL,", - compile_type_info(container_t), ")"); + compile_to_pointer_depth(env, index->indexed, 1, false), ", ", + compile_to_type(env, index->index, Type(PointerType, table_type->key_type, .is_stack = true)), + ", NULL,", compile_type_info(container_t), ")"); } else { code_err(ast, "I don't know how to assign to this target"); } @@ -947,15 +951,12 @@ Text_t compile_lvalue(env_t *env, ast_t *ast) return EMPTY_TEXT; } -static Text_t compile_assignment(env_t *env, ast_t *target, Text_t value) -{ +static Text_t compile_assignment(env_t *env, ast_t *target, Text_t value) { return Texts(compile_lvalue(env, target), " = ", value); } -static Text_t compile_inline_block(env_t *env, ast_t *ast) -{ - if (ast->tag != Block) - return compile_statement(env, ast); +static Text_t compile_inline_block(env_t *env, ast_t *ast) { + if (ast->tag != Block) return compile_statement(env, ast); Text_t code = EMPTY_TEXT; ast_list_t *stmts = Match(ast, Block)->statements; @@ -973,60 +974,44 @@ static Text_t compile_inline_block(env_t *env, ast_t *ast) return code; } -Text_t optional_into_nonnone(type_t *t, Text_t value) -{ +Text_t optional_into_nonnone(type_t *t, Text_t value) { if (t->tag == OptionalType) t = Match(t, OptionalType)->type; switch (t->tag) { - case IntType: case ByteType: - return Texts(value, ".value"); + case IntType: + case ByteType: return Texts(value, ".value"); case StructType: - if (t == PATH_TYPE || t == PATH_TYPE_TYPE) - return value; + if (t == PATH_TYPE || t == PATH_TYPE_TYPE) return value; return Texts(value, ".value"); - default: - return value; + default: return value; } } -Text_t check_none(type_t *t, Text_t value) -{ +Text_t check_none(type_t *t, Text_t value) { t = Match(t, OptionalType)->type; // NOTE: these use statement expressions ({...;}) because some compilers // complain about excessive parens around equality comparisons if (t->tag == PointerType || t->tag == FunctionType || t->tag == CStringType) return Texts("({", value, " == NULL;})"); - else if (t == PATH_TYPE) - return Texts("({(", value, ").type.$tag == PATH_NONE;})"); - else if (t == PATH_TYPE_TYPE) - return Texts("({(", value, ").$tag == PATH_NONE;})"); - else if (t->tag == BigIntType) - return Texts("({(", value, ").small == 0;})"); - else if (t->tag == ClosureType) - return Texts("({(", value, ").fn == NULL;})"); + else if (t == PATH_TYPE) return Texts("({(", value, ").type.$tag == PATH_NONE;})"); + else if (t == PATH_TYPE_TYPE) return Texts("({(", value, ").$tag == PATH_NONE;})"); + else if (t->tag == BigIntType) return Texts("({(", value, ").small == 0;})"); + else if (t->tag == ClosureType) return Texts("({(", value, ").fn == NULL;})"); else if (t->tag == NumType) return Texts(Match(t, NumType)->bits == TYPE_NBITS64 ? "Num$isnan(" : "Num32$isnan(", value, ")"); - else if (t->tag == ListType) - return Texts("({(", value, ").length < 0;})"); - else if (t->tag == TableType || t->tag == SetType) - return Texts("({(", value, ").entries.length < 0;})"); - else if (t->tag == BoolType) - return Texts("({(", value, ") == NONE_BOOL;})"); - else if (t->tag == TextType) - return Texts("({(", value, ").length < 0;})"); - else if (t->tag == IntType || t->tag == ByteType || t->tag == StructType) - return Texts("(", value, ").is_none"); + else if (t->tag == ListType) return Texts("({(", value, ").length < 0;})"); + else if (t->tag == TableType || t->tag == SetType) return Texts("({(", value, ").entries.length < 0;})"); + else if (t->tag == BoolType) return Texts("({(", value, ") == NONE_BOOL;})"); + else if (t->tag == TextType) return Texts("({(", value, ").length < 0;})"); + else if (t->tag == IntType || t->tag == ByteType || t->tag == StructType) return Texts("(", value, ").is_none"); else if (t->tag == EnumType) { - if (enum_has_fields(t)) - return Texts("({(", value, ").$tag == 0;})"); - else - return Texts("((", value, ") == 0)"); + if (enum_has_fields(t)) return Texts("({(", value, ").$tag == 0;})"); + else return Texts("((", value, ") == 0)"); } print_err("Optional check not implemented for: ", type_to_str(t)); return EMPTY_TEXT; } -static Text_t compile_condition(env_t *env, ast_t *ast) -{ +static Text_t compile_condition(env_t *env, ast_t *ast) { type_t *t = get_type(env, ast); if (t->tag == BoolType) { return compile(env, ast); @@ -1039,15 +1024,15 @@ static Text_t compile_condition(env_t *env, ast_t *ast) } else if (t->tag == OptionalType) { return Texts("!", check_none(t, compile(env, ast))); } else if (t->tag == PointerType) { - code_err(ast, "This pointer will always be non-none, so it should not be used in a conditional."); + code_err(ast, "This pointer will always be non-none, so it should not be " + "used in a conditional."); } else { code_err(ast, type_to_str(t), " values cannot be used for conditionals"); } return EMPTY_TEXT; } -static Text_t _compile_statement(env_t *env, ast_t *ast) -{ +static Text_t _compile_statement(env_t *env, ast_t *ast) { switch (ast->tag) { case When: { // Typecheck to verify exhaustiveness: @@ -1061,21 +1046,20 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) Text_t prefix = EMPTY_TEXT, suffix = EMPTY_TEXT; ast_t *subject = when->subject; if (!is_idempotent(when->subject)) { - prefix = Texts("{\n", compile_declaration(subject_t, Text("_when_subject")), " = ", compile(env, subject), ";\n"); + prefix = Texts("{\n", compile_declaration(subject_t, Text("_when_subject")), " = ", + compile(env, subject), ";\n"); suffix = Text("}\n"); - subject = LiteralCode(Text("_when_subject"), .type=subject_t); + subject = LiteralCode(Text("_when_subject"), .type = subject_t); } Text_t code = EMPTY_TEXT; for (when_clause_t *clause = when->clauses; clause; clause = clause->next) { - ast_t *comparison = WrapAST(clause->pattern, Equals, .lhs=subject, .rhs=clause->pattern); + ast_t *comparison = WrapAST(clause->pattern, Equals, .lhs = subject, .rhs = clause->pattern); (void)get_type(env, comparison); - if (code.length > 0) - code = Texts(code, "else "); + if (code.length > 0) code = Texts(code, "else "); code = Texts(code, "if (", compile(env, comparison), ")", compile_statement(env, clause->body)); } - if (when->else_body) - code = Texts(code, "else ", compile_statement(env, when->else_body)); + if (when->else_body) code = Texts(code, "else ", compile_statement(env, when->else_body)); code = Texts(prefix, code, suffix); return code; } @@ -1085,17 +1069,17 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) Text_t code; if (enum_has_fields(subject_t)) code = Texts("WHEN(", compile_type(subject_t), ", ", compile(env, when->subject), ", _when_subject, {\n"); - else - code = Texts("switch(", compile(env, when->subject), ") {\n"); + else code = Texts("switch(", compile(env, when->subject), ") {\n"); for (when_clause_t *clause = when->clauses; clause; clause = clause->next) { if (clause->pattern->tag == Var) { const char *clause_tag_name = Match(clause->pattern, Var)->name; type_t *clause_type = clause->body ? get_type(env, clause->body) : Type(VoidType); - code = Texts(code, "case ", namespace_name(enum_t->env, enum_t->env->namespace, Texts("tag$", clause_tag_name)), ": {\n", - compile_inline_block(env, clause->body), - (clause_type->tag == ReturnType || clause_type->tag == AbortType) ? EMPTY_TEXT : Text("break;\n"), - "}\n"); + code = Texts( + code, "case ", namespace_name(enum_t->env, enum_t->env->namespace, Texts("tag$", clause_tag_name)), + ": {\n", compile_inline_block(env, clause->body), + (clause_type->tag == ReturnType || clause_type->tag == AbortType) ? EMPTY_TEXT : Text("break;\n"), + "}\n"); continue; } @@ -1103,7 +1087,8 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) code_err(clause->pattern, "This is not a valid pattern for a ", type_to_str(subject_t), " enum type"); const char *clause_tag_name = Match(Match(clause->pattern, FunctionCall)->fn, Var)->name; - code = Texts(code, "case ", namespace_name(enum_t->env, enum_t->env->namespace, Texts("tag$", clause_tag_name)), ": {\n"); + code = Texts(code, "case ", + namespace_name(enum_t->env, enum_t->env->namespace, Texts("tag$", clause_tag_name)), ": {\n"); type_t *tag_type = NULL; for (tag_t *tag = enum_t->tags; tag; tag = tag->next) { if (streq(tag->name, clause_tag_name)) { @@ -1117,12 +1102,12 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) DeclareMatch(tag_struct, tag_type, StructType); arg_ast_t *args = Match(clause->pattern, FunctionCall)->args; if (args && !args->next && tag_struct->fields && tag_struct->fields->next) { - if (args->value->tag != Var) - code_err(args->value, "This is not a valid variable to bind to"); + if (args->value->tag != Var) code_err(args->value, "This is not a valid variable to bind to"); const char *var_name = Match(args->value, Var)->name; if (!streq(var_name, "_")) { Text_t var = Texts("_$", var_name); - code = Texts(code, compile_declaration(tag_type, var), " = _when_subject.", valid_c_name(clause_tag_name), ";\n"); + code = Texts(code, compile_declaration(tag_type, var), " = _when_subject.", + valid_c_name(clause_tag_name), ";\n"); scope = fresh_scope(scope); set_binding(scope, Match(args->value, Var)->name, tag_type, EMPTY_TEXT); } @@ -1131,11 +1116,10 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) arg_t *field = tag_struct->fields; for (arg_ast_t *arg = args; arg || field; arg = arg->next) { if (!arg) - code_err(ast, "The field ", type_to_str(subject_t), ".", clause_tag_name, ".", field->name, " wasn't accounted for"); - if (!field) - code_err(arg->value, "This is one more field than ", type_to_str(subject_t), " has"); - if (arg->name) - code_err(arg->value, "Named arguments are not currently supported"); + code_err(ast, "The field ", type_to_str(subject_t), ".", clause_tag_name, ".", field->name, + " wasn't accounted for"); + if (!field) code_err(arg->value, "This is one more field than ", type_to_str(subject_t), " has"); + if (arg->name) code_err(arg->value, "Named arguments are not currently supported"); const char *var_name = Match(arg->value, Var)->name; if (!streq(var_name, "_")) { @@ -1151,8 +1135,7 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) ast_list_t *statements = Match(clause->body, Block)->statements; if (!statements || (statements->ast->tag == Pass && !statements->next)) code = Texts(code, "break;\n}\n"); - else - code = Texts(code, compile_inline_block(scope, clause->body), "\nbreak;\n}\n"); + else code = Texts(code, compile_inline_block(scope, clause->body), "\nbreak;\n}\n"); } else { code = Texts(code, compile_statement(scope, clause->body), "\nbreak;\n}\n"); } @@ -1162,8 +1145,7 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) ast_list_t *statements = Match(when->else_body, Block)->statements; if (!statements || (statements->ast->tag == Pass && !statements->next)) code = Texts(code, "default: break;"); - else - code = Texts(code, "default: {\n", compile_inline_block(env, when->else_body), "\nbreak;\n}\n"); + else code = Texts(code, "default: {\n", compile_inline_block(env, when->else_body), "\nbreak;\n}\n"); } else { code = Texts(code, "default: {\n", compile_statement(env, when->else_body), "\nbreak;\n}\n"); } @@ -1176,8 +1158,7 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) case DocTest: { DeclareMatch(test, ast, DocTest); type_t *expr_t = get_type(env, test->expr); - if (!expr_t) - code_err(test->expr, "I couldn't figure out the type of this expression"); + if (!expr_t) code_err(test->expr, "I couldn't figure out the type of this expression"); Text_t setup = EMPTY_TEXT; Text_t test_code; @@ -1199,28 +1180,37 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) && value_type(get_type(env, Match(assign->targets->ast, Index)->indexed))->tag == TableType) lhs_t = Match(lhs_t, OptionalType)->type; if (has_stack_memory(lhs_t)) - code_err(test->expr, "Stack references cannot be assigned to variables because the variable's scope may outlive the scope of the stack memory."); + code_err(test->expr, "Stack references cannot be assigned " + "to variables because the " + "variable's scope may outlive the " + "scope of the stack memory."); env_t *val_scope = with_enum_scope(env, lhs_t); Text_t value = compile_to_type(val_scope, assign->values->ast, lhs_t); test_code = Texts("(", compile_assignment(env, assign->targets->ast, value), ")"); expr_t = lhs_t; } else { - // Multi-assign or assignment to potentially non-idempotent targets + // Multi-assign or assignment to potentially non-idempotent + // targets if (test->expected && assign->targets->next) - code_err(ast, "Sorry, but doctesting with '=' is not supported for multi-assignments"); + code_err(ast, "Sorry, but doctesting with '=' is not " + "supported for " + "multi-assignments"); test_code = Text("({ // Assignment\n"); int64_t i = 1; - for (ast_list_t *target = assign->targets, *value = assign->values; target && value; target = target->next, value = value->next) { + for (ast_list_t *target = assign->targets, *value = assign->values; target && value; + target = target->next, value = value->next) { type_t *lhs_t = get_type(env, target->ast); if (target->ast->tag == Index && lhs_t->tag == OptionalType && value_type(get_type(env, Match(target->ast, Index)->indexed))->tag == TableType) lhs_t = Match(lhs_t, OptionalType)->type; if (has_stack_memory(lhs_t)) - code_err(ast, "Stack references cannot be assigned to variables because the variable's scope may outlive the scope of the stack memory."); - if (target == assign->targets) - expr_t = lhs_t; + code_err(ast, "Stack references cannot be assigned to " + "variables because the " + "variable's scope may outlive the scope " + "of the stack memory."); + if (target == assign->targets) expr_t = lhs_t; env_t *val_scope = with_enum_scope(env, lhs_t); Text_t val_code = compile_to_type(val_scope, value->ast, lhs_t); test_code = Texts(test_code, compile_type(lhs_t), " $", String(i), " = ", val_code, ";\n"); @@ -1240,15 +1230,16 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) if (update.lhs->tag == Index) { type_t *indexed = value_type(get_type(env, Match(update.lhs, Index)->indexed)); if (indexed->tag == TableType && Match(indexed, TableType)->default_value == NULL) - code_err(update.lhs, "Update assignments are not currently supported for tables"); + code_err(update.lhs, "Update assignments are not currently " + "supported for tables"); } - ast_t *update_var = new(ast_t); + ast_t *update_var = new (ast_t); *update_var = *test->expr; - update_var->__data.PlusUpdate.lhs = LiteralCode(Text("(*expr)"), .type=lhs_t); // UNSAFE - test_code = Texts("({", - compile_declaration(Type(PointerType, lhs_t), Text("expr")), " = &(", compile_lvalue(env, update.lhs), "); ", - compile_statement(env, update_var), "; *expr; })"); + update_var->__data.PlusUpdate.lhs = LiteralCode(Text("(*expr)"), .type = lhs_t); // UNSAFE + test_code = + Texts("({", compile_declaration(Type(PointerType, lhs_t), Text("expr")), " = &(", + compile_lvalue(env, update.lhs), "); ", compile_statement(env, update_var), "; *expr; })"); expr_t = lhs_t; } else if (expr_t->tag == VoidType || expr_t->tag == AbortType || expr_t->tag == ReturnType) { test_code = Texts("({", compile_statement(env, test->expr), " NULL;})"); @@ -1256,27 +1247,19 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) test_code = compile(env, test->expr); } if (test->expected) { - return Texts( - setup, - "test(", compile_type(expr_t), ", ", test_code, ", ", - compile_to_type(env, test->expected, expr_t), ", ", - compile_type_info(expr_t), ", ", - String((int64_t)(test->expr->start - test->expr->file->text)), ", ", - String((int64_t)(test->expr->end - test->expr->file->text)), ");"); + return Texts(setup, "test(", compile_type(expr_t), ", ", test_code, ", ", + compile_to_type(env, test->expected, expr_t), ", ", compile_type_info(expr_t), ", ", + String((int64_t)(test->expr->start - test->expr->file->text)), ", ", + String((int64_t)(test->expr->end - test->expr->file->text)), ");"); } else { if (expr_t->tag == VoidType || expr_t->tag == AbortType) { - return Texts( - setup, - "inspect_void(", test_code, ", ", compile_type_info(expr_t), ", ", - String((int64_t)(test->expr->start - test->expr->file->text)), ", ", - String((int64_t)(test->expr->end - test->expr->file->text)), ");"); + return Texts(setup, "inspect_void(", test_code, ", ", compile_type_info(expr_t), ", ", + String((int64_t)(test->expr->start - test->expr->file->text)), ", ", + String((int64_t)(test->expr->end - test->expr->file->text)), ");"); } - return Texts( - setup, - "inspect(", compile_type(expr_t), ", ", test_code, ", ", - compile_type_info(expr_t), ", ", - String((int64_t)(test->expr->start - test->expr->file->text)), ", ", - String((int64_t)(test->expr->end - test->expr->file->text)), ");"); + return Texts(setup, "inspect(", compile_type(expr_t), ", ", test_code, ", ", compile_type_info(expr_t), + ", ", String((int64_t)(test->expr->start - test->expr->file->text)), ", ", + String((int64_t)(test->expr->end - test->expr->file->text)), ");"); } } case Assert: { @@ -1286,67 +1269,69 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) switch (expr->tag) { case And: { DeclareMatch(and_, ast, And); - return Texts( - compile_statement(env, WrapAST(ast, Assert, .expr=and_->lhs, .message=message)), - compile_statement(env, WrapAST(ast, Assert, .expr=and_->rhs, .message=message))); + return Texts(compile_statement(env, WrapAST(ast, Assert, .expr = and_->lhs, .message = message)), + compile_statement(env, WrapAST(ast, Assert, .expr = and_->rhs, .message = message))); } case Equals: failure = "!="; goto assert_comparison; case NotEquals: failure = "=="; goto assert_comparison; case LessThan: failure = ">="; goto assert_comparison; case LessThanOrEquals: failure = ">"; goto assert_comparison; case GreaterThan: failure = "<="; goto assert_comparison; - case GreaterThanOrEquals: failure = "<"; goto assert_comparison; { - assert_comparison:; - binary_operands_t cmp = BINARY_OPERANDS(expr); - type_t *lhs_t = get_type(env, cmp.lhs); - type_t *rhs_t = get_type(env, cmp.rhs); - type_t *operand_t; - if (cmp.lhs->tag == Int && is_numeric_type(rhs_t)) { - operand_t = rhs_t; - } else if (cmp.rhs->tag == Int && is_numeric_type(lhs_t)) { - operand_t = lhs_t; - } else if (can_compile_to_type(env, cmp.rhs, lhs_t)) { - operand_t = lhs_t; - } else if (can_compile_to_type(env, cmp.lhs, rhs_t)) { - operand_t = rhs_t; - } else { - code_err(ast, "I can't do comparisons between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); - } - - ast_t *lhs_var = FakeAST(InlineCCode, .chunks=new(ast_list_t, .ast=FakeAST(TextLiteral, Text("_lhs"))), .type=operand_t); - ast_t *rhs_var = FakeAST(InlineCCode, .chunks=new(ast_list_t, .ast=FakeAST(TextLiteral, Text("_rhs"))), .type=operand_t); - ast_t *var_comparison = new(ast_t, .file=expr->file, .start=expr->start, .end=expr->end, .tag=expr->tag, - .__data.Equals={.lhs=lhs_var, .rhs=rhs_var}); - int64_t line = get_line_number(ast->file, ast->start); - return Texts("{ // assertion\n", - compile_declaration(operand_t, Text("_lhs")), " = ", compile_to_type(env, cmp.lhs, operand_t), ";\n", - "\n#line ", String(line), "\n", - compile_declaration(operand_t, Text("_rhs")), " = ", compile_to_type(env, cmp.rhs, operand_t), ";\n", - "\n#line ", String(line), "\n", - "if (!(", compile_condition(env, var_comparison), "))\n", - "#line ", String(line), "\n", - Texts( - "fail_source(", quoted_str(ast->file->filename), ", ", - String((int64_t)(expr->start - expr->file->text)), ", ", - String((int64_t)(expr->end - expr->file->text)), ", ", - message ? Texts("Text$as_c_string(", compile_to_type(env, message, Type(TextType)), ")") - : Text("\"This assertion failed!\""), ", ", - "\" (\", ", expr_as_text(Text("_lhs"), operand_t, Text("no")), ", " - "\" ", failure, " \", ", expr_as_text(Text("_rhs"), operand_t, Text("no")), ", \")\");\n"), - "}\n"); + case GreaterThanOrEquals: + failure = "<"; + goto assert_comparison; + { + assert_comparison:; + binary_operands_t cmp = BINARY_OPERANDS(expr); + type_t *lhs_t = get_type(env, cmp.lhs); + type_t *rhs_t = get_type(env, cmp.rhs); + type_t *operand_t; + if (cmp.lhs->tag == Int && is_numeric_type(rhs_t)) { + operand_t = rhs_t; + } else if (cmp.rhs->tag == Int && is_numeric_type(lhs_t)) { + operand_t = lhs_t; + } else if (can_compile_to_type(env, cmp.rhs, lhs_t)) { + operand_t = lhs_t; + } else if (can_compile_to_type(env, cmp.lhs, rhs_t)) { + operand_t = rhs_t; + } else { + code_err(ast, "I can't do comparisons between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + } - } + ast_t *lhs_var = + FakeAST(InlineCCode, .chunks = new (ast_list_t, .ast = FakeAST(TextLiteral, Text("_lhs"))), + .type = operand_t); + ast_t *rhs_var = + FakeAST(InlineCCode, .chunks = new (ast_list_t, .ast = FakeAST(TextLiteral, Text("_rhs"))), + .type = operand_t); + ast_t *var_comparison = new (ast_t, .file = expr->file, .start = expr->start, .end = expr->end, + .tag = expr->tag, .__data.Equals = {.lhs = lhs_var, .rhs = rhs_var}); + int64_t line = get_line_number(ast->file, ast->start); + return Texts("{ // assertion\n", compile_declaration(operand_t, Text("_lhs")), " = ", + compile_to_type(env, cmp.lhs, operand_t), ";\n", "\n#line ", String(line), "\n", + compile_declaration(operand_t, Text("_rhs")), " = ", + compile_to_type(env, cmp.rhs, operand_t), ";\n", "\n#line ", String(line), "\n", "if (!(", + compile_condition(env, var_comparison), "))\n", "#line ", String(line), "\n", + Texts("fail_source(", quoted_str(ast->file->filename), ", ", + String((int64_t)(expr->start - expr->file->text)), ", ", + String((int64_t)(expr->end - expr->file->text)), ", ", + message + ? Texts("Text$as_c_string(", compile_to_type(env, message, Type(TextType)), ")") + : Text("\"This assertion failed!\""), + ", ", "\" (\", ", expr_as_text(Text("_lhs"), operand_t, Text("no")), + ", " + "\" ", + failure, " \", ", expr_as_text(Text("_rhs"), operand_t, Text("no")), ", \")\");\n"), + "}\n"); + } default: { int64_t line = get_line_number(ast->file, ast->start); - return Texts( - "if (!(", compile_condition(env, expr), "))\n", - "#line ", String(line), "\n", - "fail_source(", quoted_str(ast->file->filename), ", ", - String((int64_t)(expr->start - expr->file->text)), ", ", - String((int64_t)(expr->end - expr->file->text)), ", ", - message ? Texts("Text$as_c_string(", compile_to_type(env, message, Type(TextType)), ")") - : Text("\"This assertion failed!\""), - ");\n"); + return Texts("if (!(", compile_condition(env, expr), "))\n", "#line ", String(line), "\n", "fail_source(", + quoted_str(ast->file->filename), ", ", String((int64_t)(expr->start - expr->file->text)), ", ", + String((int64_t)(expr->end - expr->file->text)), ", ", + message ? Texts("Text$as_c_string(", compile_to_type(env, message, Type(TextType)), ")") + : Text("\"This assertion failed!\""), + ");\n"); } } } @@ -1354,10 +1339,8 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) DeclareMatch(decl, ast, Declare); const char *name = Match(decl->var, Var)->name; if (streq(name, "_")) { // Explicit discard - if (decl->value) - return Texts("(void)", compile(env, decl->value), ";"); - else - return EMPTY_TEXT; + if (decl->value) return Texts("(void)", compile(env, decl->value), ";"); + else return EMPTY_TEXT; } else { type_t *t = decl->type ? parse_type_ast(env, decl->type) : get_type(env, decl->value); if (t->tag == FunctionType) t = Type(ClosureType, t); @@ -1377,7 +1360,10 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) && value_type(get_type(env, Match(assign->targets->ast, Index)->indexed))->tag == TableType) lhs_t = Match(lhs_t, OptionalType)->type; if (has_stack_memory(lhs_t)) - code_err(ast, "Stack references cannot be assigned to variables because the variable's scope may outlive the scope of the stack memory."); + code_err(ast, "Stack references cannot be assigned to " + "variables because the " + "variable's scope may outlive the scope of the " + "stack memory."); env_t *val_env = with_enum_scope(env, lhs_t); Text_t val = compile_to_type(val_env, assign->values->ast, lhs_t); return Texts(compile_assignment(env, assign->targets->ast, val), ";\n"); @@ -1385,13 +1371,17 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) Text_t code = Text("{ // Assignment\n"); int64_t i = 1; - for (ast_list_t *value = assign->values, *target = assign->targets; value && target; value = value->next, target = target->next) { + for (ast_list_t *value = assign->values, *target = assign->targets; value && target; + value = value->next, target = target->next) { type_t *lhs_t = get_type(env, target->ast); if (target->ast->tag == Index && lhs_t->tag == OptionalType && value_type(get_type(env, Match(target->ast, Index)->indexed))->tag == TableType) lhs_t = Match(lhs_t, OptionalType)->type; if (has_stack_memory(lhs_t)) - code_err(ast, "Stack references cannot be assigned to variables because the variable's scope may outlive the scope of the stack memory."); + code_err(ast, "Stack references cannot be assigned to " + "variables because the " + "variable's scope may outlive the scope of the " + "stack memory."); env_t *val_env = with_enum_scope(env, lhs_t); Text_t val = compile_to_type(val_env, value->ast, lhs_t); code = Texts(code, compile_type(lhs_t), " $", String(i), " = ", val, ";\n"); @@ -1439,11 +1429,24 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) return Texts(compile_lvalue(env, update->lhs), " %= ", compile_to_type(env, update->rhs, lhs_t), ";"); return compile_update_assignment(env, ast); } - case PowerUpdate: case Mod1Update: case ConcatUpdate: case LeftShiftUpdate: case UnsignedLeftShiftUpdate: - case RightShiftUpdate: case UnsignedRightShiftUpdate: case AndUpdate: case OrUpdate: case XorUpdate: { + case PowerUpdate: + case Mod1Update: + case ConcatUpdate: + case LeftShiftUpdate: + case UnsignedLeftShiftUpdate: + case RightShiftUpdate: + case UnsignedRightShiftUpdate: + case AndUpdate: + case OrUpdate: + case XorUpdate: { return compile_update_assignment(env, ast); } - case StructDef: case EnumDef: case LangDef: case Extend: case FunctionDef: case ConvertDef: { + case StructDef: + case EnumDef: + case LangDef: + case Extend: + case FunctionDef: + case ConvertDef: { return EMPTY_TEXT; } case Skip: { @@ -1460,20 +1463,16 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) ++skip_label_count; } Text_t code = EMPTY_TEXT; - for (deferral_t *deferred = env->deferred; deferred && deferred != ctx->deferred; deferred = deferred->next) + for (deferral_t *deferred = env->deferred; deferred && deferred != ctx->deferred; + deferred = deferred->next) code = Texts(code, compile_statement(deferred->defer_env, deferred->block)); - if (code.length > 0) - return Texts("{\n", code, "goto ", ctx->skip_label, ";\n}\n"); - else - return Texts("goto ", ctx->skip_label, ";"); + if (code.length > 0) return Texts("{\n", code, "goto ", ctx->skip_label, ";\n}\n"); + else return Texts("goto ", ctx->skip_label, ";"); } } - if (env->loop_ctx) - code_err(ast, "This is not inside any loop"); - else if (target) - code_err(ast, "No loop target named '", target, "' was found"); - else - return Text("continue;"); + if (env->loop_ctx) code_err(ast, "This is not inside any loop"); + else if (target) code_err(ast, "No loop target named '", target, "' was found"); + else return Text("continue;"); } case Stop: { const char *target = Match(ast, Stop)->target; @@ -1489,20 +1488,16 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) ++stop_label_count; } Text_t code = EMPTY_TEXT; - for (deferral_t *deferred = env->deferred; deferred && deferred != ctx->deferred; deferred = deferred->next) + for (deferral_t *deferred = env->deferred; deferred && deferred != ctx->deferred; + deferred = deferred->next) code = Texts(code, compile_statement(deferred->defer_env, deferred->block)); - if (code.length > 0) - return Texts("{\n", code, "goto ", ctx->stop_label, ";\n}\n"); - else - return Texts("goto ", ctx->stop_label, ";"); + if (code.length > 0) return Texts("{\n", code, "goto ", ctx->stop_label, ";\n}\n"); + else return Texts("goto ", ctx->stop_label, ";"); } } - if (env->loop_ctx) - code_err(ast, "This is not inside any loop"); - else if (target) - code_err(ast, "No loop target named '", target, "' was found"); - else - return Text("break;"); + if (env->loop_ctx) code_err(ast, "This is not inside any loop"); + else if (target) code_err(ast, "No loop target named '", target, "' was found"); + else return Text("break;"); } case Pass: return Text(";"); case Defer: { @@ -1513,20 +1508,21 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) env_t *defer_env = fresh_scope(env); Text_t code = EMPTY_TEXT; for (int64_t i = 0; i < closed_vars.entries.length; i++) { - struct { const char *name; binding_t *b; } *entry = closed_vars.entries.data + closed_vars.entries.stride*i; - if (entry->b->type->tag == ModuleType) - continue; + struct { + const char *name; + binding_t *b; + } *entry = closed_vars.entries.data + closed_vars.entries.stride * i; + if (entry->b->type->tag == ModuleType) continue; if (Text$starts_with(entry->b->code, Text("userdata->"), NULL)) { Table$str_set(defer_env->locals, entry->name, entry->b); } else { Text_t defer_name = Texts("defer$", String(++defer_id), "$", entry->name); defer_id += 1; - code = Texts( - code, compile_declaration(entry->b->type, defer_name), " = ", entry->b->code, ";\n"); + code = Texts(code, compile_declaration(entry->b->type, defer_name), " = ", entry->b->code, ";\n"); set_binding(defer_env, entry->name, entry->b->type, defer_name); } } - env->deferred = new(deferral_t, .defer_env=defer_env, .block=body, .next=env->deferred); + env->deferred = new (deferral_t, .defer_env = defer_env, .block = body, .next = env->deferred); return code; } case Return: { @@ -1540,7 +1536,8 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) if (ret) { if (env->fn_ret->tag == VoidType || env->fn_ret->tag == AbortType) - code_err(ast, "This function is not supposed to return any values, according to its type signature"); + code_err(ast, "This function is not supposed to return any values, " + "according to its type signature"); env = with_enum_scope(env, env->fn_ret); Text_t value = compile_to_type(env, ret, env->fn_ret); @@ -1560,34 +1557,31 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) DeclareMatch(while_, ast, While); env_t *scope = fresh_scope(env); loop_ctx_t loop_ctx = (loop_ctx_t){ - .loop_name="while", - .deferred=scope->deferred, - .next=env->loop_ctx, + .loop_name = "while", + .deferred = scope->deferred, + .next = env->loop_ctx, }; scope->loop_ctx = &loop_ctx; Text_t body = compile_statement(scope, while_->body); - if (loop_ctx.skip_label.length > 0) - body = Texts(body, "\n", loop_ctx.skip_label, ": continue;"); - Text_t loop = Texts("while (", while_->condition ? compile(scope, while_->condition) : Text("yes"), ") {\n\t", body, "\n}"); - if (loop_ctx.stop_label.length > 0) - loop = Texts(loop, "\n", loop_ctx.stop_label, ":;"); + if (loop_ctx.skip_label.length > 0) body = Texts(body, "\n", loop_ctx.skip_label, ": continue;"); + Text_t loop = Texts("while (", while_->condition ? compile(scope, while_->condition) : Text("yes"), ") {\n\t", + body, "\n}"); + if (loop_ctx.stop_label.length > 0) loop = Texts(loop, "\n", loop_ctx.stop_label, ":;"); return loop; } case Repeat: { ast_t *body = Match(ast, Repeat)->body; env_t *scope = fresh_scope(env); loop_ctx_t loop_ctx = (loop_ctx_t){ - .loop_name="repeat", - .deferred=scope->deferred, - .next=env->loop_ctx, + .loop_name = "repeat", + .deferred = scope->deferred, + .next = env->loop_ctx, }; scope->loop_ctx = &loop_ctx; Text_t body_code = compile_statement(scope, body); - if (loop_ctx.skip_label.length > 0) - body_code = Texts(body_code, "\n", loop_ctx.skip_label, ": continue;"); + if (loop_ctx.skip_label.length > 0) body_code = Texts(body_code, "\n", loop_ctx.skip_label, ": continue;"); Text_t loop = Texts("for (;;) {\n\t", body_code, "\n}"); - if (loop_ctx.stop_label.length > 0) - loop = Texts(loop, "\n", loop_ctx.stop_label, ":;"); + if (loop_ctx.stop_label.length > 0) loop = Texts(loop, "\n", loop_ctx.stop_label, ":;"); return loop; } case For: { @@ -1595,50 +1589,48 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) // If we're iterating over a comprehension, that's actually just doing // one loop, we don't need to compile the comprehension as a list - // comprehension. This is a common case for reducers like `(+: i*2 for i in 5)` - // or `(and) x.is_good() for x in xs` + // comprehension. This is a common case for reducers like `(+: i*2 for i + // in 5)` or `(and) x.is_good() for x in xs` if (for_->iter->tag == Comprehension) { DeclareMatch(comp, for_->iter, Comprehension); ast_t *body = for_->body; if (for_->vars) { - if (for_->vars->next) - code_err(for_->vars->next->ast, "This is too many variables for iteration"); + if (for_->vars->next) code_err(for_->vars->next->ast, "This is too many variables for iteration"); body = WrapAST( ast, Block, - .statements=new(ast_list_t, .ast=WrapAST(ast, Declare, .var=for_->vars->ast, .value=comp->expr), - .next=body->tag == Block ? Match(body, Block)->statements : new(ast_list_t, .ast=body))); + .statements = new ( + ast_list_t, .ast = WrapAST(ast, Declare, .var = for_->vars->ast, .value = comp->expr), + .next = body->tag == Block ? Match(body, Block)->statements : new (ast_list_t, .ast = body))); } - if (comp->filter) - body = WrapAST(for_->body, If, .condition=comp->filter, .body=body); - ast_t *loop = WrapAST(ast, For, .vars=comp->vars, .iter=comp->iter, .body=body); + if (comp->filter) body = WrapAST(for_->body, If, .condition = comp->filter, .body = body); + ast_t *loop = WrapAST(ast, For, .vars = comp->vars, .iter = comp->iter, .body = body); return compile_statement(env, loop); } env_t *body_scope = for_scope(env, ast); loop_ctx_t loop_ctx = (loop_ctx_t){ - .loop_name="for", - .loop_vars=for_->vars, - .deferred=body_scope->deferred, - .next=body_scope->loop_ctx, + .loop_name = "for", + .loop_vars = for_->vars, + .deferred = body_scope->deferred, + .next = body_scope->loop_ctx, }; body_scope->loop_ctx = &loop_ctx; // Naked means no enclosing braces: Text_t naked_body = compile_inline_block(body_scope, for_->body); - if (loop_ctx.skip_label.length > 0) - naked_body = Texts(naked_body, "\n", loop_ctx.skip_label, ": continue;"); + if (loop_ctx.skip_label.length > 0) naked_body = Texts(naked_body, "\n", loop_ctx.skip_label, ": continue;"); Text_t stop = loop_ctx.stop_label.length > 0 ? Texts("\n", loop_ctx.stop_label, ":;") : EMPTY_TEXT; // Special case for improving performance for numeric iteration: - if (for_->iter->tag == MethodCall && streq(Match(for_->iter, MethodCall)->name, "to") && - is_int_type(get_type(env, Match(for_->iter, MethodCall)->self))) { + if (for_->iter->tag == MethodCall && streq(Match(for_->iter, MethodCall)->name, "to") + && is_int_type(get_type(env, Match(for_->iter, MethodCall)->self))) { // TODO: support other integer types arg_ast_t *args = Match(for_->iter, MethodCall)->args; if (!args) code_err(for_->iter, "to() needs at least one argument"); type_t *int_type = get_type(env, Match(for_->iter, MethodCall)->self); - type_t *step_type = int_type->tag == ByteType ? Type(IntType, .bits=TYPE_IBITS8) : int_type; + type_t *step_type = int_type->tag == ByteType ? Type(IntType, .bits = TYPE_IBITS8) : int_type; Text_t last = EMPTY_TEXT, step = EMPTY_TEXT, optional_step = EMPTY_TEXT; if (!args->name || streq(args->name, "last")) { @@ -1648,14 +1640,12 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) code_err(args->next->value, "Invalid argument name: ", args->next->name); if (get_type(env, args->next->value)->tag == OptionalType) optional_step = compile_to_type(env, args->next->value, Type(OptionalType, step_type)); - else - step = compile_to_type(env, args->next->value, step_type); + else step = compile_to_type(env, args->next->value, step_type); } } else if (streq(args->name, "step")) { if (get_type(env, args->value)->tag == OptionalType) optional_step = compile_to_type(env, args->value, Type(OptionalType, step_type)); - else - step = compile_to_type(env, args->value, step_type); + else step = compile_to_type(env, args->value, step_type); if (args->next) { if (args->next->name && !streq(args->next->name, "last")) code_err(args->next->value, "Invalid argument name: ", args->next->name); @@ -1663,52 +1653,57 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) } } - if (last.length == 0) - code_err(for_->iter, "No `last` argument was given"); - + if (last.length == 0) code_err(for_->iter, "No `last` argument was given"); + Text_t type_code = compile_type(int_type); Text_t value = for_->vars ? compile(body_scope, for_->vars->ast) : Text("i"); if (int_type->tag == BigIntType) { if (optional_step.length > 0) - step = Texts("({ OptionalInt_t maybe_step = ", optional_step, "; maybe_step->small == 0 ? (Int$compare_value(last, first) >= 0 ? I_small(1) : I_small(-1)) : (Int_t)maybe_step; })"); + step = Texts("({ OptionalInt_t maybe_step = ", optional_step, + "; maybe_step->small == 0 ? " + "(Int$compare_value(last, first) >= 0 " + "? I_small(1) : I_small(-1)) : (Int_t)maybe_step; " + "})"); else if (step.length == 0) - step = Text("Int$compare_value(last, first) >= 0 ? I_small(1) : I_small(-1)"); - return Texts( - "for (", type_code, " first = ", compile(env, Match(for_->iter, MethodCall)->self), ", ", - value, " = first, last = ", last, ", step = ", step, "; " - "Int$compare_value(", value, ", last) != Int$compare_value(step, I_small(0)); ", - value, " = Int$plus(", value, ", step)) {\n" - "\t", naked_body, - "}", - stop); + step = Text("Int$compare_value(last, first) >= 0 ? " + "I_small(1) : I_small(-1)"); + return Texts("for (", type_code, " first = ", compile(env, Match(for_->iter, MethodCall)->self), ", ", + value, " = first, last = ", last, ", step = ", step, + "; " + "Int$compare_value(", + value, ", last) != Int$compare_value(step, I_small(0)); ", value, " = Int$plus(", value, + ", step)) {\n" + "\t", + naked_body, "}", stop); } else { if (optional_step.length > 0) - step = Texts("({ ", compile_type(Type(OptionalType, step_type)), " maybe_step = ", optional_step, "; " - "maybe_step.is_none ? (", type_code, ")(last >= first ? 1 : -1) : maybe_step.value; })"); - else if (step.length == 0) - step = Texts("(", type_code, ")(last >= first ? 1 : -1)"); - return Texts( - "for (", type_code, " first = ", compile(env, Match(for_->iter, MethodCall)->self), ", ", - value, " = first, last = ", last, ", step = ", step, "; " - "step > 0 ? ", value, " <= last : ", value, " >= last; ", - value, " += step) {\n" - "\t", naked_body, - "}", - stop); + step = Texts("({ ", compile_type(Type(OptionalType, step_type)), " maybe_step = ", optional_step, + "; " + "maybe_step.is_none ? (", + type_code, ")(last >= first ? 1 : -1) : maybe_step.value; })"); + else if (step.length == 0) step = Texts("(", type_code, ")(last >= first ? 1 : -1)"); + return Texts("for (", type_code, " first = ", compile(env, Match(for_->iter, MethodCall)->self), ", ", + value, " = first, last = ", last, ", step = ", step, + "; " + "step > 0 ? ", + value, " <= last : ", value, " >= last; ", value, + " += step) {\n" + "\t", + naked_body, "}", stop); } - } else if (for_->iter->tag == MethodCall && streq(Match(for_->iter, MethodCall)->name, "onward") && - get_type(env, Match(for_->iter, MethodCall)->self)->tag == BigIntType) { + } else if (for_->iter->tag == MethodCall && streq(Match(for_->iter, MethodCall)->name, "onward") + && get_type(env, Match(for_->iter, MethodCall)->self)->tag == BigIntType) { // Special case for Int.onward() arg_ast_t *args = Match(for_->iter, MethodCall)->args; - arg_t *arg_spec = new(arg_t, .name="step", .type=INT_TYPE, .default_val=FakeAST(Int, .str="1"), .next=NULL); + arg_t *arg_spec = + new (arg_t, .name = "step", .type = INT_TYPE, .default_val = FakeAST(Int, .str = "1"), .next = NULL); Text_t step = compile_arguments(env, for_->iter, arg_spec, args); Text_t value = for_->vars ? compile(body_scope, for_->vars->ast) : Text("i"); - return Texts( - "for (Int_t ", value, " = ", compile(env, Match(for_->iter, MethodCall)->self), ", ", - "step = ", step, "; ; ", value, " = Int$plus(", value, ", step)) {\n" - "\t", naked_body, - "}", - stop); + return Texts("for (Int_t ", value, " = ", compile(env, Match(for_->iter, MethodCall)->self), ", ", + "step = ", step, "; ; ", value, " = Int$plus(", value, + ", step)) {\n" + "\t", + naked_body, "}", stop); } type_t *iter_t = get_type(env, for_->iter); @@ -1734,14 +1729,11 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) Text_t loop = EMPTY_TEXT; loop = Texts(loop, "for (int64_t i = 1; i <= iterating.length; ++i)"); - if (index.length > 0) - naked_body = Texts("Int_t ", index, " = I(i);\n", naked_body); + if (index.length > 0) naked_body = Texts("Int_t ", index, " = I(i);\n", naked_body); if (value.length > 0) { - loop = Texts(loop, "{\n", - compile_declaration(item_t, value), - " = *(", compile_type(item_t), "*)(iterating.data + (i-1)*iterating.stride);\n", - naked_body, "\n}"); + loop = Texts(loop, "{\n", compile_declaration(item_t, value), " = *(", compile_type(item_t), + "*)(iterating.data + (i-1)*iterating.stride);\n", naked_body, "\n}"); } else { loop = Texts(loop, "{\n", naked_body, "\n}"); } @@ -1751,38 +1743,37 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) if (iter_t->tag == PointerType) { loop = Texts("{\n" - "List_t *ptr = ", compile_to_pointer_depth(env, for_->iter, 1, false), ";\n" - "\nLIST_INCREF(*ptr);\n" - "List_t iterating = *ptr;\n", - loop, - stop, - "\nLIST_DECREF(*ptr);\n" - "}\n"); + "List_t *ptr = ", + compile_to_pointer_depth(env, for_->iter, 1, false), + ";\n" + "\nLIST_INCREF(*ptr);\n" + "List_t iterating = *ptr;\n", + loop, stop, + "\nLIST_DECREF(*ptr);\n" + "}\n"); } else { loop = Texts("{\n" - "List_t iterating = ", compile_to_pointer_depth(env, for_->iter, 0, false), ";\n", - loop, - stop, - "}\n"); + "List_t iterating = ", + compile_to_pointer_depth(env, for_->iter, 0, false), ";\n", loop, stop, "}\n"); } return loop; } - case SetType: case TableType: { + case SetType: + case TableType: { Text_t loop = Text("for (int64_t i = 0; i < iterating.length; ++i) {\n"); if (for_->vars) { if (iter_value_t->tag == SetType) { - if (for_->vars->next) - code_err(for_->vars->next->ast, "This is too many variables for this loop"); + if (for_->vars->next) code_err(for_->vars->next->ast, "This is too many variables for this loop"); Text_t item = compile(body_scope, for_->vars->ast); type_t *item_type = Match(iter_value_t, SetType)->item_type; loop = Texts(loop, compile_declaration(item_type, item), " = *(", compile_type(item_type), "*)(", - "iterating.data + i*iterating.stride);\n"); + "iterating.data + i*iterating.stride);\n"); } else { Text_t key = compile(body_scope, for_->vars->ast); type_t *key_t = Match(iter_value_t, TableType)->key_type; loop = Texts(loop, compile_declaration(key_t, key), " = *(", compile_type(key_t), "*)(", - "iterating.data + i*iterating.stride);\n"); + "iterating.data + i*iterating.stride);\n"); if (for_->vars->next) { if (for_->vars->next->next) @@ -1790,9 +1781,10 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) type_t *value_t = Match(iter_value_t, TableType)->value_type; Text_t value = compile(body_scope, for_->vars->next->ast); - Text_t value_offset = Texts("offsetof(struct { ", compile_declaration(key_t, Text("k")), "; ", compile_declaration(value_t, Text("v")), "; }, v)"); + Text_t value_offset = Texts("offsetof(struct { ", compile_declaration(key_t, Text("k")), "; ", + compile_declaration(value_t, Text("v")), "; }, v)"); loop = Texts(loop, compile_declaration(value_t, value), " = *(", compile_type(value_t), "*)(", - "iterating.data + i*iterating.stride + ", value_offset, ");\n"); + "iterating.data + i*iterating.stride + ", value_offset, ");\n"); } } } @@ -1804,20 +1796,16 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) } if (iter_t->tag == PointerType) { - loop = Texts( - "{\n", - "Table_t *t = ", compile_to_pointer_depth(env, for_->iter, 1, false), ";\n" - "LIST_INCREF(t->entries);\n" - "List_t iterating = t->entries;\n", - loop, - "LIST_DECREF(t->entries);\n" - "}\n"); + loop = Texts("{\n", "Table_t *t = ", compile_to_pointer_depth(env, for_->iter, 1, false), + ";\n" + "LIST_INCREF(t->entries);\n" + "List_t iterating = t->entries;\n", + loop, + "LIST_DECREF(t->entries);\n" + "}\n"); } else { - loop = Texts( - "{\n", - "List_t iterating = (", compile_to_pointer_depth(env, for_->iter, 0, false), ").entries;\n", - loop, - "}\n"); + loop = Texts("{\n", "List_t iterating = (", compile_to_pointer_depth(env, for_->iter, 0, false), + ").entries;\n", loop, "}\n"); } return loop; } @@ -1826,52 +1814,50 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) if (for_->iter->tag == Int) { const char *str = Match(for_->iter, Int)->str; Int_t int_val = Int$from_str(str); - if (int_val.small == 0) - code_err(for_->iter, "Failed to parse this integer"); + if (int_val.small == 0) code_err(for_->iter, "Failed to parse this integer"); mpz_t i; mpz_init_set_int(i, int_val); - if (mpz_cmpabs_ui(i, BIGGEST_SMALL_INT) <= 0) - n = Text$from_str(mpz_get_str(NULL, 10, i)); - else - goto big_n; - + if (mpz_cmpabs_ui(i, BIGGEST_SMALL_INT) <= 0) n = Text$from_str(mpz_get_str(NULL, 10, i)); + else goto big_n; if (for_->empty && mpz_cmp_si(i, 0) <= 0) { return compile_statement(env, for_->empty); } else { - return Texts( - "for (int64_t i = 1; i <= ", n, "; ++i) {\n", - for_->vars ? Texts("\tInt_t ", compile(body_scope, for_->vars->ast), " = I_small(i);\n") : EMPTY_TEXT, - "\t", naked_body, - "}\n", - stop, "\n"); + return Texts("for (int64_t i = 1; i <= ", n, "; ++i) {\n", + for_->vars + ? Texts("\tInt_t ", compile(body_scope, for_->vars->ast), " = I_small(i);\n") + : EMPTY_TEXT, + "\t", naked_body, "}\n", stop, "\n"); } } - big_n: + big_n: n = compile_to_pointer_depth(env, for_->iter, 0, false); Text_t i = for_->vars ? compile(body_scope, for_->vars->ast) : Text("i"); Text_t n_var = for_->vars ? Texts("max", i) : Text("n"); if (for_->empty) { - return Texts( - "{\n" - "Int_t ", n_var, " = ", n, ";\n" - "if (Int$compare_value(", n_var, ", I(0)) > 0) {\n" - "for (Int_t ", i, " = I(1); Int$compare_value(", i, ", ", n_var, ") <= 0; ", i, " = Int$plus(", i, ", I(1))) {\n", - "\t", naked_body, - "}\n" - "} else ", compile_statement(env, for_->empty), - stop, "\n" - "}\n"); + return Texts("{\n" + "Int_t ", + n_var, " = ", n, + ";\n" + "if (Int$compare_value(", + n_var, + ", I(0)) > 0) {\n" + "for (Int_t ", + i, " = I(1); Int$compare_value(", i, ", ", n_var, ") <= 0; ", i, " = Int$plus(", i, + ", I(1))) {\n", "\t", naked_body, + "}\n" + "} else ", + compile_statement(env, for_->empty), stop, + "\n" + "}\n"); } else { - return Texts( - "for (Int_t ", i, " = I(1), ", n_var, " = ", n, "; Int$compare_value(", i, ", ", n_var, ") <= 0; ", i, " = Int$plus(", i, ", I(1))) {\n", - "\t", naked_body, - "}\n", - stop, "\n"); + return Texts("for (Int_t ", i, " = I(1), ", n_var, " = ", n, "; Int$compare_value(", i, ", ", n_var, + ") <= 0; ", i, " = Int$plus(", i, ", I(1))) {\n", "\t", naked_body, "}\n", stop, "\n"); } } - case FunctionType: case ClosureType: { + case FunctionType: + case ClosureType: { // Iterator function: Text_t code = Text("{\n"); @@ -1879,53 +1865,63 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) if (is_idempotent(for_->iter)) { next_fn = compile_to_pointer_depth(env, for_->iter, 0, false); } else { - code = Texts(code, compile_declaration(iter_value_t, Text("next")), " = ", compile_to_pointer_depth(env, for_->iter, 0, false), ";\n"); + code = Texts(code, compile_declaration(iter_value_t, Text("next")), " = ", + compile_to_pointer_depth(env, for_->iter, 0, false), ";\n"); next_fn = Text("next"); } - __typeof(iter_value_t->__data.FunctionType) *fn = iter_value_t->tag == ClosureType ? Match(Match(iter_value_t, ClosureType)->fn, FunctionType) : Match(iter_value_t, FunctionType); + __typeof(iter_value_t->__data.FunctionType) *fn = + iter_value_t->tag == ClosureType ? Match(Match(iter_value_t, ClosureType)->fn, FunctionType) + : Match(iter_value_t, FunctionType); Text_t get_next; if (iter_value_t->tag == ClosureType) { type_t *fn_t = Match(iter_value_t, ClosureType)->fn; arg_t *closure_fn_args = NULL; for (arg_t *arg = Match(fn_t, FunctionType)->args; arg; arg = arg->next) - closure_fn_args = new(arg_t, .name=arg->name, .type=arg->type, .default_val=arg->default_val, .next=closure_fn_args); - closure_fn_args = new(arg_t, .name="userdata", .type=Type(PointerType, .pointed=Type(MemoryType)), .next=closure_fn_args); + closure_fn_args = new (arg_t, .name = arg->name, .type = arg->type, .default_val = arg->default_val, + .next = closure_fn_args); + closure_fn_args = new (arg_t, .name = "userdata", + .type = Type(PointerType, .pointed = Type(MemoryType)), .next = closure_fn_args); REVERSE_LIST(closure_fn_args); - Text_t fn_type_code = compile_type(Type(FunctionType, .args=closure_fn_args, .ret=Match(fn_t, FunctionType)->ret)); + Text_t fn_type_code = + compile_type(Type(FunctionType, .args = closure_fn_args, .ret = Match(fn_t, FunctionType)->ret)); get_next = Texts("((", fn_type_code, ")", next_fn, ".fn)(", next_fn, ".userdata)"); } else { get_next = Texts(next_fn, "()"); } if (fn->ret->tag == OptionalType) { - // Use an optional variable `cur` for each iteration step, which will be checked for none + // Use an optional variable `cur` for each iteration step, which + // will be checked for none code = Texts(code, compile_declaration(fn->ret, Text("cur")), ";\n"); get_next = Texts("(cur=", get_next, ", !", check_none(fn->ret, Text("cur")), ")"); if (for_->vars) { - naked_body = Texts( - compile_declaration(Match(fn->ret, OptionalType)->type, Texts("_$", Match(for_->vars->ast, Var)->name)), - " = ", optional_into_nonnone(fn->ret, Text("cur")), ";\n", - naked_body); + naked_body = Texts(compile_declaration(Match(fn->ret, OptionalType)->type, + Texts("_$", Match(for_->vars->ast, Var)->name)), + " = ", optional_into_nonnone(fn->ret, Text("cur")), ";\n", naked_body); } if (for_->empty) { - code = Texts(code, "if (", get_next, ") {\n" - "\tdo{\n\t\t", naked_body, "\t} while(", get_next, ");\n" - "} else {\n\t", compile_statement(env, for_->empty), "}", stop, "\n}\n"); + code = Texts(code, "if (", get_next, + ") {\n" + "\tdo{\n\t\t", + naked_body, "\t} while(", get_next, + ");\n" + "} else {\n\t", + compile_statement(env, for_->empty), "}", stop, "\n}\n"); } else { code = Texts(code, "while(", get_next, ") {\n\t", naked_body, "}\n", stop, "\n}\n"); } } else { if (for_->vars) { - naked_body = Texts( - compile_declaration(fn->ret, Texts("_$", Match(for_->vars->ast, Var)->name)), - " = ", get_next, ";\n", naked_body); + naked_body = Texts(compile_declaration(fn->ret, Texts("_$", Match(for_->vars->ast, Var)->name)), + " = ", get_next, ";\n", naked_body); } else { naked_body = Texts(get_next, ";\n", naked_body); } if (for_->empty) - code_err(for_->empty, "This iteration loop will always have values, so this block will never run"); + code_err(for_->empty, "This iteration loop will always have values, " + "so this block will never run"); code = Texts(code, "for (;;) {\n\t", naked_body, "}\n", stop, "\n}\n"); } @@ -1938,8 +1934,7 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) DeclareMatch(if_, ast, If); ast_t *condition = if_->condition; if (condition->tag == Declare) { - if (Match(condition, Declare)->value == NULL) - code_err(condition, "This declaration must have a value"); + if (Match(condition, Declare)->value == NULL) code_err(condition, "This declaration must have a value"); env_t *truthy_scope = fresh_scope(env); Text_t code = Texts("IF_DECLARE(", compile_statement(truthy_scope, condition), ", "); bind_statement(truthy_scope, condition); @@ -1947,13 +1942,11 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) code = Texts(code, compile_condition(truthy_scope, var), ", "); type_t *cond_t = get_type(truthy_scope, var); if (cond_t->tag == OptionalType) { - set_binding(truthy_scope, Match(var, Var)->name, - Match(cond_t, OptionalType)->type, + set_binding(truthy_scope, Match(var, Var)->name, Match(cond_t, OptionalType)->type, optional_into_nonnone(cond_t, compile(truthy_scope, var))); } code = Texts(code, compile_statement(truthy_scope, if_->body), ")"); - if (if_->else_body) - code = Texts(code, "\nelse ", compile_statement(env, if_->else_body)); + if (if_->else_body) code = Texts(code, "\nelse ", compile_statement(env, if_->else_body)); return code; } else { Text_t code = Texts("if (", compile_condition(env, condition), ")"); @@ -1961,13 +1954,11 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) type_t *cond_t = get_type(env, condition); if (condition->tag == Var && cond_t->tag == OptionalType) { truthy_scope = fresh_scope(env); - set_binding(truthy_scope, Match(condition, Var)->name, - Match(cond_t, OptionalType)->type, + set_binding(truthy_scope, Match(condition, Var)->name, Match(cond_t, OptionalType)->type, optional_into_nonnone(cond_t, compile(truthy_scope, condition))); } code = Texts(code, compile_statement(truthy_scope, if_->body)); - if (if_->else_body) - code = Texts(code, "\nelse ", compile_statement(env, if_->else_body)); + if (if_->else_body) code = Texts(code, "\nelse ", compile_statement(env, if_->else_body)); return code; } } @@ -1975,21 +1966,19 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) return Texts("{\n", compile_inline_block(env, ast), "}\n"); } case Comprehension: { - if (!env->comprehension_action) - code_err(ast, "I don't know what to do with this comprehension!"); + if (!env->comprehension_action) code_err(ast, "I don't know what to do with this comprehension!"); DeclareMatch(comp, ast, Comprehension); if (comp->expr->tag == Comprehension) { // Nested comprehension - ast_t *body = comp->filter ? WrapAST(ast, If, .condition=comp->filter, .body=comp->expr) : comp->expr; - ast_t *loop = WrapAST(ast, For, .vars=comp->vars, .iter=comp->iter, .body=body); + ast_t *body = comp->filter ? WrapAST(ast, If, .condition = comp->filter, .body = comp->expr) : comp->expr; + ast_t *loop = WrapAST(ast, For, .vars = comp->vars, .iter = comp->iter, .body = body); return compile_statement(env, loop); } // List/Set/Table comprehension: - comprehension_body_t get_body = (void*)env->comprehension_action->fn; + comprehension_body_t get_body = (void *)env->comprehension_action->fn; ast_t *body = get_body(comp->expr, env->comprehension_action->userdata); - if (comp->filter) - body = WrapAST(comp->expr, If, .condition=comp->filter, .body=body); - ast_t *loop = WrapAST(ast, For, .vars=comp->vars, .iter=comp->iter, .body=body); + if (comp->filter) body = WrapAST(comp->expr, If, .condition = comp->filter, .body = body); + ast_t *loop = WrapAST(ast, For, .vars = comp->vars, .iter = comp->iter, .body = body); return compile_statement(env, loop); } case Extern: return EMPTY_TEXT; @@ -2017,9 +2006,10 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) module_info_t mod = get_module_info(ast); glob_t tm_files; const char *folder = mod.version ? String(mod.name, "_", mod.version) : mod.name; - if (glob(String(TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL, &tm_files) != 0) { - if (!try_install_module(mod)) - code_err(ast, "Could not find library"); + if (glob(String(TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, + NULL, &tm_files) + != 0) { + if (!try_install_module(mod)) code_err(ast, "Could not find library"); } Text_t initialization = EMPTY_TEXT; @@ -2027,8 +2017,7 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) for (size_t i = 0; i < tm_files.gl_pathc; i++) { const char *filename = tm_files.gl_pathv[i]; initialization = Texts( - initialization, - with_source_info(env, ast, Texts("$initialize", get_id_suffix(filename), "();\n"))); + initialization, with_source_info(env, ast, Texts("$initialize", get_id_suffix(filename), "();\n"))); } globfree(&tm_files); return initialization; @@ -2037,7 +2026,8 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) } } default: - // print("Is discardable: ", ast_to_sexp_str(ast), " ==> ", is_discardable(env, ast)); + // print("Is discardable: ", ast_to_sexp_str(ast), " ==> ", + // is_discardable(env, ast)); if (!is_discardable(env, ast)) code_err(ast, "The ", type_to_str(get_type(env, ast)), " result of this statement cannot be discarded"); return Texts("(void)", compile(env, ast), ";"); @@ -2049,15 +2039,18 @@ Text_t compile_statement(env_t *env, ast_t *ast) { return with_source_info(env, ast, stmt); } -Text_t expr_as_text(Text_t expr, type_t *t, Text_t color) -{ +Text_t expr_as_text(Text_t expr, type_t *t, Text_t color) { switch (t->tag) { case MemoryType: return Texts("Memory$as_text(stack(", expr, "), ", color, ", &Memory$info)"); case BoolType: - // NOTE: this cannot use stack(), since bools may actually be bit fields: - return Texts("Bool$as_text((Bool_t[1]){", expr, "}, ", color, ", &Bool$info)"); + // NOTE: this cannot use stack(), since bools may actually be bit + // fields: + return Texts("Bool$as_text((Bool_t[1]){", expr, "}, ", color, ", &Bool$info)"); case CStringType: return Texts("CString$as_text(stack(", expr, "), ", color, ", &CString$info)"); - case BigIntType: case IntType: case ByteType: case NumType: { + case BigIntType: + case IntType: + case ByteType: + case NumType: { Text_t name = type_to_text(t); return Texts(name, "$as_text(stack(", expr, "), ", color, ", &", name, "$info)"); } @@ -2065,25 +2058,24 @@ Text_t expr_as_text(Text_t expr, type_t *t, Text_t color) case ListType: return Texts("List$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); case SetType: return Texts("Table$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); case TableType: return Texts("Table$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); - case FunctionType: case ClosureType: return Texts("Func$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); + case FunctionType: + case ClosureType: return Texts("Func$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); case PointerType: return Texts("Pointer$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); case OptionalType: return Texts("Optional$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); - case StructType: case EnumType: - return Texts("generic_as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); + case StructType: + case EnumType: return Texts("generic_as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); default: compiler_err(NULL, NULL, NULL, "Stringifying is not supported for ", type_to_str(t)); } return EMPTY_TEXT; } -Text_t compile_text(env_t *env, ast_t *ast, Text_t color) -{ +Text_t compile_text(env_t *env, ast_t *ast, Text_t color) { type_t *t = get_type(env, ast); Text_t expr = compile(env, ast); return expr_as_text(expr, t, color); } -Text_t compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool needs_incref) -{ +Text_t compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool needs_incref) { Text_t val = compile(env, ast); type_t *t = get_type(env, ast); int64_t depth = 0; @@ -2092,16 +2084,13 @@ Text_t compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bo // Passing a literal value won't trigger an incref, because it's ephemeral, // e.g. [10, 20].reversed() - if (t->tag != PointerType && needs_incref && !can_be_mutated(env, ast)) - needs_incref = false; + if (t->tag != PointerType && needs_incref && !can_be_mutated(env, ast)) needs_incref = false; while (depth != target_depth) { if (depth < target_depth) { - if (ast->tag == Var && target_depth == 1) - val = Texts("(&", val, ")"); - else - code_err(ast, "This should be a pointer, not ", type_to_str(get_type(env, ast))); - t = Type(PointerType, .pointed=t, .is_stack=true); + if (ast->tag == Var && target_depth == 1) val = Texts("(&", val, ")"); + else code_err(ast, "This should be a pointer, not ", type_to_str(get_type(env, ast))); + t = Type(PointerType, .pointed = t, .is_stack = true); ++depth; } else { DeclareMatch(ptr, t, PointerType); @@ -2116,16 +2105,13 @@ Text_t compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bo t = ptr->pointed; } - if (needs_incref && t->tag == ListType) - val = Texts("LIST_COPY(", val, ")"); - else if (needs_incref && (t->tag == TableType || t->tag == SetType)) - val = Texts("TABLE_COPY(", val, ")"); + if (needs_incref && t->tag == ListType) val = Texts("LIST_COPY(", val, ")"); + else if (needs_incref && (t->tag == TableType || t->tag == SetType)) val = Texts("TABLE_COPY(", val, ")"); return val; } -Text_t compile_to_type(env_t *env, ast_t *ast, type_t *t) -{ +Text_t compile_to_type(env_t *env, ast_t *ast, type_t *t) { assert(!is_incomplete_type(t)); if (ast->tag == Int && is_numeric_type(non_optional(t))) { return compile_int_to_type(env, ast, t); @@ -2137,10 +2123,11 @@ Text_t compile_to_type(env_t *env, ast_t *ast, type_t *t) default: code_err(ast, "This is not a valid number bit width"); } } else if (ast->tag == None) { - if (t->tag != OptionalType) - code_err(ast, "This is not supposed to be an optional type"); + if (t->tag != OptionalType) code_err(ast, "This is not supposed to be an optional type"); else if (Match(t, OptionalType)->type == NULL) - code_err(ast, "I don't know what kind of `none` this is supposed to be!\nPlease tell me by declaring a variable like `foo : Type = none`"); + code_err(ast, "I don't know what kind of `none` this is supposed to " + "be!\nPlease " + "tell me by declaring a variable like `foo : Type = none`"); return compile_none(t); } else if (t->tag == PointerType && (ast->tag == HeapAllocate || ast->tag == StackReference)) { return compile_typed_allocation(env, ast, t); @@ -2166,15 +2153,17 @@ Text_t compile_to_type(env_t *env, ast_t *ast, type_t *t) if (ast->tag == MethodCall) { DeclareMatch(methodcall, ast, MethodCall); type_t *self_type = get_type(env, methodcall->self); - // Currently, this is only implemented for cases where you have the return type - // and the self type equal to each other, because that's the main case I care - // about with list and set methods (e.g. `List.sorted()`) + // Currently, this is only implemented for cases where you have the + // return type and the self type equal to each other, because that's the + // main case I care about with list and set methods (e.g. + // `List.sorted()`) if (is_incomplete_type(self_type) && type_eq(self_type, actual)) { type_t *completed_self = most_complete_type(self_type, t); if (completed_self) { - ast_t *explicit_self = WrapAST(methodcall->self, ExplicitlyTyped, .ast=methodcall->self, .type=completed_self); - ast_t *new_methodcall = WrapAST(ast, MethodCall, .self=explicit_self, - .name=methodcall->name, .args=methodcall->args); + ast_t *explicit_self = + WrapAST(methodcall->self, ExplicitlyTyped, .ast = methodcall->self, .type = completed_self); + ast_t *new_methodcall = + WrapAST(ast, MethodCall, .self = explicit_self, .name = methodcall->name, .args = methodcall->args); return compile_to_type(env, new_methodcall, t); } } @@ -2186,11 +2175,10 @@ Text_t compile_to_type(env_t *env, ast_t *ast, type_t *t) if (!is_incomplete_type(actual)) { Text_t code = compile(env, ast); - if (promote(env, ast, &code, actual, t)) - return code; + if (promote(env, ast, &code, actual, t)) return code; } - arg_ast_t *constructor_args = new(arg_ast_t, .value=ast); + arg_ast_t *constructor_args = new (arg_ast_t, .value = ast); binding_t *constructor = get_constructor(env, t, constructor_args, true); if (constructor) { arg_t *arg_spec = Match(constructor->type, FunctionType)->args; @@ -2200,25 +2188,21 @@ Text_t compile_to_type(env_t *env, ast_t *ast, type_t *t) code_err(ast, "I expected a ", type_to_str(t), " here, but this is a ", type_to_str(actual)); } -Text_t compile_typed_list(env_t *env, ast_t *ast, type_t *list_type) -{ +Text_t compile_typed_list(env_t *env, ast_t *ast, type_t *list_type) { DeclareMatch(list, ast, List); - if (!list->items) - return Text("(List_t){.length=0}"); + if (!list->items) return Text("(List_t){.length=0}"); type_t *item_type = Match(list_type, ListType)->item_type; int64_t n = 0; for (ast_list_t *item = list->items; item; item = item->next) { ++n; - if (item->ast->tag == Comprehension) - goto list_comprehension; + if (item->ast->tag == Comprehension) goto list_comprehension; } { env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : env; - if (is_incomplete_type(item_type)) - code_err(ast, "This list's type can't be inferred!"); + if (is_incomplete_type(item_type)) code_err(ast, "This list's type can't be inferred!"); Text_t code = Texts("TypedListN(", compile_type(item_type), ", ", String(n)); for (ast_list_t *item = list->items; item; item = item->next) { code = Texts(code, ", ", compile_to_type(scope, item->ast, item_type)); @@ -2226,48 +2210,39 @@ Text_t compile_typed_list(env_t *env, ast_t *ast, type_t *list_type) return Texts(code, ")"); } - list_comprehension: - { - env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : fresh_scope(env); - static int64_t comp_num = 1; - const char *comprehension_name = String("list$", comp_num++); - ast_t *comprehension_var = LiteralCode(Texts("&", comprehension_name), - .type=Type(PointerType, .pointed=list_type, .is_stack=true)); - Closure_t comp_action = {.fn=add_to_list_comprehension, .userdata=comprehension_var}; - scope->comprehension_action = &comp_action; - Text_t code = Texts("({ List_t ", comprehension_name, " = {};"); - // set_binding(scope, comprehension_name, list_type, comprehension_name); - for (ast_list_t *item = list->items; item; item = item->next) { - if (item->ast->tag == Comprehension) - code = Texts(code, "\n", compile_statement(scope, item->ast)); - else - code = Texts(code, compile_statement(env, add_to_list_comprehension(item->ast, comprehension_var))); - } - code = Texts(code, " ", comprehension_name, "; })"); - return code; +list_comprehension: { + env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : fresh_scope(env); + static int64_t comp_num = 1; + const char *comprehension_name = String("list$", comp_num++); + ast_t *comprehension_var = + LiteralCode(Texts("&", comprehension_name), .type = Type(PointerType, .pointed = list_type, .is_stack = true)); + Closure_t comp_action = {.fn = add_to_list_comprehension, .userdata = comprehension_var}; + scope->comprehension_action = &comp_action; + Text_t code = Texts("({ List_t ", comprehension_name, " = {};"); + // set_binding(scope, comprehension_name, list_type, comprehension_name); + for (ast_list_t *item = list->items; item; item = item->next) { + if (item->ast->tag == Comprehension) code = Texts(code, "\n", compile_statement(scope, item->ast)); + else code = Texts(code, compile_statement(env, add_to_list_comprehension(item->ast, comprehension_var))); } + code = Texts(code, " ", comprehension_name, "; })"); + return code; +} } -Text_t compile_typed_set(env_t *env, ast_t *ast, type_t *set_type) -{ +Text_t compile_typed_set(env_t *env, ast_t *ast, type_t *set_type) { DeclareMatch(set, ast, Set); - if (!set->items) - return Text("((Table_t){})"); + if (!set->items) return Text("((Table_t){})"); type_t *item_type = Match(set_type, SetType)->item_type; int64_t n = 0; for (ast_list_t *item = set->items; item; item = item->next) { ++n; - if (item->ast->tag == Comprehension) - goto set_comprehension; + if (item->ast->tag == Comprehension) goto set_comprehension; } - + { // No comprehension: - Text_t code = Texts("Set(", - compile_type(item_type), ", ", - compile_type_info(item_type), ", ", - String(n)); + Text_t code = Texts("Set(", compile_type(item_type), ", ", compile_type_info(item_type), ", ", String(n)); env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : env; for (ast_list_t *item = set->items; item; item = item->next) { code = Texts(code, ", ", compile_to_type(scope, item->ast, item_type)); @@ -2275,34 +2250,29 @@ Text_t compile_typed_set(env_t *env, ast_t *ast, type_t *set_type) return Texts(code, ")"); } - set_comprehension: - { - static int64_t comp_num = 1; - env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : fresh_scope(env); - const char *comprehension_name = String("set$", comp_num++); - ast_t *comprehension_var = LiteralCode(Texts("&", comprehension_name), - .type=Type(PointerType, .pointed=set_type, .is_stack=true)); - Text_t code = Texts("({ Table_t ", comprehension_name, " = {};"); - Closure_t comp_action = {.fn=add_to_set_comprehension, .userdata=comprehension_var}; - scope->comprehension_action = &comp_action; - for (ast_list_t *item = set->items; item; item = item->next) { - if (item->ast->tag == Comprehension) - code = Texts(code, "\n", compile_statement(scope, item->ast)); - else - code = Texts(code, compile_statement(env, add_to_set_comprehension(item->ast, comprehension_var))); - } - code = Texts(code, " ", comprehension_name, "; })"); - return code; +set_comprehension: { + static int64_t comp_num = 1; + env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : fresh_scope(env); + const char *comprehension_name = String("set$", comp_num++); + ast_t *comprehension_var = + LiteralCode(Texts("&", comprehension_name), .type = Type(PointerType, .pointed = set_type, .is_stack = true)); + Text_t code = Texts("({ Table_t ", comprehension_name, " = {};"); + Closure_t comp_action = {.fn = add_to_set_comprehension, .userdata = comprehension_var}; + scope->comprehension_action = &comp_action; + for (ast_list_t *item = set->items; item; item = item->next) { + if (item->ast->tag == Comprehension) code = Texts(code, "\n", compile_statement(scope, item->ast)); + else code = Texts(code, compile_statement(env, add_to_set_comprehension(item->ast, comprehension_var))); } + code = Texts(code, " ", comprehension_name, "; })"); + return code; +} } -Text_t compile_typed_table(env_t *env, ast_t *ast, type_t *table_type) -{ +Text_t compile_typed_table(env_t *env, ast_t *ast, type_t *table_type) { DeclareMatch(table, ast, Table); if (!table->entries) { Text_t code = Text("((Table_t){"); - if (table->fallback) - code = Texts(code, ".fallback=heap(", compile(env, table->fallback),")"); + if (table->fallback) code = Texts(code, ".fallback=heap(", compile(env, table->fallback), ")"); return Texts(code, "})"); } @@ -2313,22 +2283,16 @@ Text_t compile_typed_table(env_t *env, ast_t *ast, type_t *table_type) code_err(ast, "Tables whose values are optional (", type_to_str(value_t), ") are not currently supported."); for (ast_list_t *entry = table->entries; entry; entry = entry->next) { - if (entry->ast->tag == Comprehension) - goto table_comprehension; + if (entry->ast->tag == Comprehension) goto table_comprehension; } { // No comprehension: env_t *key_scope = key_t->tag == EnumType ? with_enum_scope(env, key_t) : env; env_t *value_scope = value_t->tag == EnumType ? with_enum_scope(env, value_t) : env; - Text_t code = Texts("Table(", - compile_type(key_t), ", ", - compile_type(value_t), ", ", - compile_type_info(key_t), ", ", - compile_type_info(value_t)); - if (table->fallback) - code = Texts(code, ", /*fallback:*/ heap(", compile(env, table->fallback), ")"); - else - code = Texts(code, ", /*fallback:*/ NULL"); + Text_t code = Texts("Table(", compile_type(key_t), ", ", compile_type(value_t), ", ", compile_type_info(key_t), + ", ", compile_type_info(value_t)); + if (table->fallback) code = Texts(code, ", /*fallback:*/ heap(", compile(env, table->fallback), ")"); + else code = Texts(code, ", /*fallback:*/ NULL"); size_t n = 0; for (ast_list_t *entry = table->entries; entry; entry = entry->next) @@ -2338,40 +2302,35 @@ Text_t compile_typed_table(env_t *env, ast_t *ast, type_t *table_type) for (ast_list_t *entry = table->entries; entry; entry = entry->next) { DeclareMatch(e, entry->ast, TableEntry); code = Texts(code, ",\n\t{", compile_to_type(key_scope, e->key, key_t), ", ", - compile_to_type(value_scope, e->value, value_t), "}"); + compile_to_type(value_scope, e->value, value_t), "}"); } return Texts(code, ")"); } - table_comprehension: - { - static int64_t comp_num = 1; - env_t *scope = fresh_scope(env); - const char *comprehension_name = String("table$", comp_num++); - ast_t *comprehension_var = LiteralCode(Texts("&", comprehension_name), - .type=Type(PointerType, .pointed=table_type, .is_stack=true)); +table_comprehension: { + static int64_t comp_num = 1; + env_t *scope = fresh_scope(env); + const char *comprehension_name = String("table$", comp_num++); + ast_t *comprehension_var = + LiteralCode(Texts("&", comprehension_name), .type = Type(PointerType, .pointed = table_type, .is_stack = true)); - Text_t code = Texts("({ Table_t ", comprehension_name, " = {"); - if (table->fallback) - code = Texts(code, ".fallback=heap(", compile(env, table->fallback), "), "); + Text_t code = Texts("({ Table_t ", comprehension_name, " = {"); + if (table->fallback) code = Texts(code, ".fallback=heap(", compile(env, table->fallback), "), "); - code = Texts(code, "};"); + code = Texts(code, "};"); - Closure_t comp_action = {.fn=add_to_table_comprehension, .userdata=comprehension_var}; - scope->comprehension_action = &comp_action; - for (ast_list_t *entry = table->entries; entry; entry = entry->next) { - if (entry->ast->tag == Comprehension) - code = Texts(code, "\n", compile_statement(scope, entry->ast)); - else - code = Texts(code, compile_statement(env, add_to_table_comprehension(entry->ast, comprehension_var))); - } - code = Texts(code, " ", comprehension_name, "; })"); - return code; + Closure_t comp_action = {.fn = add_to_table_comprehension, .userdata = comprehension_var}; + scope->comprehension_action = &comp_action; + for (ast_list_t *entry = table->entries; entry; entry = entry->next) { + if (entry->ast->tag == Comprehension) code = Texts(code, "\n", compile_statement(scope, entry->ast)); + else code = Texts(code, compile_statement(env, add_to_table_comprehension(entry->ast, comprehension_var))); } + code = Texts(code, " ", comprehension_name, "; })"); + return code; +} } -Text_t compile_typed_allocation(env_t *env, ast_t *ast, type_t *pointer_type) -{ +Text_t compile_typed_allocation(env_t *env, ast_t *ast, type_t *pointer_type) { // TODO: for constructors, do new(T, ...) instead of heap((T){...}) type_t *pointed = Match(pointer_type, PointerType)->pointed; switch (ast->tag) { @@ -2382,16 +2341,14 @@ Text_t compile_typed_allocation(env_t *env, ast_t *ast, type_t *pointer_type) ast_t *subject = Match(ast, StackReference)->value; if (can_be_mutated(env, subject) && type_eq(pointed, get_type(env, subject))) return Texts("(&", compile_lvalue(env, subject), ")"); - else - return Texts("stack(", compile_to_type(env, subject, pointed), ")"); + else return Texts("stack(", compile_to_type(env, subject, pointed), ")"); } default: code_err(ast, "Not an allocation!"); } return EMPTY_TEXT; } -Text_t compile_int_to_type(env_t *env, ast_t *ast, type_t *target) -{ +Text_t compile_int_to_type(env_t *env, ast_t *ast, type_t *target) { if (ast->tag != Int) { Text_t code = compile(env, ast); type_t *actual_type = get_type(env, ast); @@ -2400,16 +2357,14 @@ Text_t compile_int_to_type(env_t *env, ast_t *ast, type_t *target) return code; } - if (target->tag == BigIntType) - return compile(env, ast); + if (target->tag == BigIntType) return compile(env, ast); if (target->tag == OptionalType && Match(target, OptionalType)->type) return compile_int_to_type(env, ast, Match(target, OptionalType)->type); const char *literal = Match(ast, Int)->str; OptionalInt_t int_val = Int$from_str(literal); - if (int_val.small == 0) - code_err(ast, "Failed to parse this integer"); + if (int_val.small == 0) code_err(ast, "Failed to parse this integer"); mpz_t i; mpz_init_set_int(i, int_val); @@ -2424,8 +2379,7 @@ Text_t compile_int_to_type(env_t *env, ast_t *ast, type_t *target) } if (target->tag == ByteType) { - if (mpz_cmp_si(i, UINT8_MAX) <= 0 && mpz_cmp_si(i, 0) >= 0) - return Texts("(Byte_t)(", c_literal, ")"); + if (mpz_cmp_si(i, UINT8_MAX) <= 0 && mpz_cmp_si(i, 0) >= 0) return Texts("(Byte_t)(", c_literal, ")"); code_err(ast, "This integer cannot fit in a byte"); } else if (target->tag == NumType) { if (Match(target, NumType)->bits == TYPE_NBITS64) { @@ -2437,22 +2391,17 @@ Text_t compile_int_to_type(env_t *env, ast_t *ast, type_t *target) int64_t target_bits = (int64_t)Match(target, IntType)->bits; switch (target_bits) { case TYPE_IBITS64: - if (mpz_cmp_si(i, INT64_MIN) == 0) - return Text("I64(INT64_MIN)"); - if (mpz_cmp_si(i, INT64_MAX) <= 0 && mpz_cmp_si(i, INT64_MIN) >= 0) - return Texts("I64(", c_literal, "L)"); + if (mpz_cmp_si(i, INT64_MIN) == 0) return Text("I64(INT64_MIN)"); + if (mpz_cmp_si(i, INT64_MAX) <= 0 && mpz_cmp_si(i, INT64_MIN) >= 0) return Texts("I64(", c_literal, "L)"); break; case TYPE_IBITS32: - if (mpz_cmp_si(i, INT32_MAX) <= 0 && mpz_cmp_si(i, INT32_MIN) >= 0) - return Texts("I32(", c_literal, ")"); + if (mpz_cmp_si(i, INT32_MAX) <= 0 && mpz_cmp_si(i, INT32_MIN) >= 0) return Texts("I32(", c_literal, ")"); break; case TYPE_IBITS16: - if (mpz_cmp_si(i, INT16_MAX) <= 0 && mpz_cmp_si(i, INT16_MIN) >= 0) - return Texts("I16(", c_literal, ")"); + if (mpz_cmp_si(i, INT16_MAX) <= 0 && mpz_cmp_si(i, INT16_MIN) >= 0) return Texts("I16(", c_literal, ")"); break; case TYPE_IBITS8: - if (mpz_cmp_si(i, INT8_MAX) <= 0 && mpz_cmp_si(i, INT8_MIN) >= 0) - return Texts("I8(", c_literal, ")"); + if (mpz_cmp_si(i, INT8_MAX) <= 0 && mpz_cmp_si(i, INT8_MIN) >= 0) return Texts("I8(", c_literal, ")"); break; default: break; } @@ -2463,13 +2412,12 @@ Text_t compile_int_to_type(env_t *env, ast_t *ast, type_t *target) return EMPTY_TEXT; } -Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t *call_args) -{ +Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t *call_args) { Table_t used_args = {}; Text_t code = EMPTY_TEXT; - env_t *default_scope = new(env_t); + env_t *default_scope = new (env_t); *default_scope = *env; - default_scope->locals = new(Table_t, .fallback=env->namespace_bindings ? env->namespace_bindings : env->globals); + default_scope->locals = new (Table_t, .fallback = env->namespace_bindings ? env->namespace_bindings : env->globals); for (arg_t *spec_arg = spec_args; spec_arg; spec_arg = spec_arg->next) { int64_t i = 1; // Find keyword: @@ -2481,12 +2429,10 @@ Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_ value = compile_int_to_type(env, call_arg->value, spec_arg->type); } else if (spec_arg->type->tag == NumType && call_arg->value->tag == Int) { OptionalInt_t int_val = Int$from_str(Match(call_arg->value, Int)->str); - if (int_val.small == 0) - code_err(call_arg->value, "Failed to parse this integer"); + if (int_val.small == 0) code_err(call_arg->value, "Failed to parse this integer"); if (Match(spec_arg->type, NumType)->bits == TYPE_NBITS64) value = Text$from_str(String(hex_double(Num$from_int(int_val, false)))); - else - value = Text$from_str(String(hex_double((double)Num32$from_int(int_val, false)), "f")); + else value = Text$from_str(String(hex_double((double)Num32$from_int(int_val, false)), "f")); } else { env_t *arg_env = with_enum_scope(env, spec_arg->type); value = compile_maybe_incref(arg_env, call_arg->value, spec_arg->type); @@ -2508,12 +2454,10 @@ Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_ value = compile_int_to_type(env, call_arg->value, spec_arg->type); } else if (spec_arg->type->tag == NumType && call_arg->value->tag == Int) { OptionalInt_t int_val = Int$from_str(Match(call_arg->value, Int)->str); - if (int_val.small == 0) - code_err(call_arg->value, "Failed to parse this integer"); + if (int_val.small == 0) code_err(call_arg->value, "Failed to parse this integer"); if (Match(spec_arg->type, NumType)->bits == TYPE_NBITS64) value = Text$from_str(String(hex_double(Num$from_int(int_val, false)))); - else - value = Text$from_str(String(hex_double((double)Num32$from_int(int_val, false)), "f")); + else value = Text$from_str(String(hex_double((double)Num32$from_int(int_val, false)), "f")); } else { env_t *arg_env = with_enum_scope(env, spec_arg->type); value = compile_maybe_incref(arg_env, call_arg->value, spec_arg->type); @@ -2534,7 +2478,8 @@ Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_ assert(spec_arg->name); code_err(call_ast, "The required argument '", spec_arg->name, "' was not provided"); - found_it: continue; + found_it: + continue; } int64_t i = 1; @@ -2544,15 +2489,13 @@ Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_ code_err(call_arg->value, "There is no argument with the name '", call_arg->name, "'"); } else { const char *pseudoname = String(i++); - if (!Table$str_get(used_args, pseudoname)) - code_err(call_arg->value, "This is one argument too many!"); + if (!Table$str_get(used_args, pseudoname)) code_err(call_arg->value, "This is one argument too many!"); } } return code; } -Text_t compile_text_literal(Text_t literal) -{ +Text_t compile_text_literal(Text_t literal) { Text_t code = Text("\""); const char *utf8 = Text$as_c_string(literal); for (const char *p = utf8; *p; p++) { @@ -2569,8 +2512,8 @@ Text_t compile_text_literal(Text_t literal) if (isprint(*p)) { code = Texts(code, Text$from_strn(p, 1)); } else { - uint8_t byte = *(uint8_t*)p; - code = Texts(code, "\\x", String(hex(byte, .no_prefix=true, .uppercase=true, .digits=2)), "\"\""); + uint8_t byte = *(uint8_t *)p; + code = Texts(code, "\\x", String(hex(byte, .no_prefix = true, .uppercase = true, .digits = 2)), "\"\""); } break; } @@ -2579,27 +2522,21 @@ Text_t compile_text_literal(Text_t literal) return Texts(code, "\""); } -PUREFUNC static bool string_literal_is_all_ascii(Text_t literal) -{ +PUREFUNC static bool string_literal_is_all_ascii(Text_t literal) { TextIter_t state = NEW_TEXT_ITER_STATE(literal); for (int64_t i = 0; i < literal.length; i++) { int32_t g = Text$get_grapheme_fast(&state, i); - if (g < 0 || g > 127 || !isascii(g)) - return false; + if (g < 0 || g > 127 || !isascii(g)) return false; } return true; } -Text_t compile_none(type_t *t) -{ - if (t == NULL) - compiler_err(NULL, NULL, NULL, "I can't compile a `none` value with no type"); +Text_t compile_none(type_t *t) { + if (t == NULL) compiler_err(NULL, NULL, NULL, "I can't compile a `none` value with no type"); - if (t->tag == OptionalType) - t = Match(t, OptionalType)->type; + if (t->tag == OptionalType) t = Match(t, OptionalType)->type; - if (t == NULL) - compiler_err(NULL, NULL, NULL, "I can't compile a `none` value with no type"); + if (t == NULL) compiler_err(NULL, NULL, NULL, "I can't compile a `none` value with no type"); if (t == PATH_TYPE) return Text("NONE_PATH"); else if (t == PATH_TYPE_TYPE) return Text("((OptionalPathType_t){})"); @@ -2626,7 +2563,7 @@ Text_t compile_none(type_t *t) case PointerType: return Texts("((", compile_type(t), ")NULL)"); case ClosureType: return Text("NONE_CLOSURE"); case NumType: return Text("nan(\"none\")"); - case StructType: return Texts("((", compile_type(Type(OptionalType, .type=t)), "){.is_none=true})"); + case StructType: return Texts("((", compile_type(Type(OptionalType, .type = t)), "){.is_none=true})"); case EnumType: { env_t *enum_env = Match(t, EnumType)->env; return Texts("((", compile_type(t), "){", namespace_name(enum_env, enum_env->namespace, Text("none")), "})"); @@ -2636,13 +2573,10 @@ Text_t compile_none(type_t *t) return EMPTY_TEXT; } -Text_t compile_empty(type_t *t) -{ - if (t == NULL) - compiler_err(NULL, NULL, NULL, "I can't compile a value with no type"); +Text_t compile_empty(type_t *t) { + if (t == NULL) compiler_err(NULL, NULL, NULL, "I can't compile a value with no type"); - if (t->tag == OptionalType) - return compile_none(t); + if (t->tag == OptionalType) return compile_none(t); if (t == PATH_TYPE) return Text("NONE_PATH"); else if (t == PATH_TYPE_TYPE) return Text("((OptionalPathType_t){})"); @@ -2662,13 +2596,15 @@ Text_t compile_empty(type_t *t) case ByteType: return Text("((Byte_t)0)"); case BoolType: return Text("((Bool_t)no)"); case ListType: return Text("((List_t){})"); - case TableType: case SetType: return Text("((Table_t){})"); + case TableType: + case SetType: return Text("((Table_t){})"); case TextType: return Text("Text(\"\")"); case CStringType: return Text("\"\""); case PointerType: { DeclareMatch(ptr, t, PointerType); Text_t empty_pointed = compile_empty(ptr->pointed); - return empty_pointed.length == 0 ? EMPTY_TEXT : Texts(ptr->is_stack ? Text("stack(") : Text("heap("), empty_pointed, ")"); + return empty_pointed.length == 0 ? EMPTY_TEXT + : Texts(ptr->is_stack ? Text("stack(") : Text("heap("), empty_pointed, ")"); } case NumType: { return Match(t, NumType)->bits == TYPE_NBITS32 ? Text("N32(0.0f)") : Text("N64(0.0)"); @@ -2677,15 +2613,12 @@ Text_t compile_empty(type_t *t) DeclareMatch(struct_, t, StructType); Text_t code = Texts("((", compile_type(t), "){"); for (arg_t *field = struct_->fields; field; field = field->next) { - Text_t empty_field = field->default_val - ? compile(struct_->env, field->default_val) - : compile_empty(field->type); - if (empty_field.length == 0) - return EMPTY_TEXT; + Text_t empty_field = + field->default_val ? compile(struct_->env, field->default_val) : compile_empty(field->type); + if (empty_field.length == 0) return EMPTY_TEXT; code = Texts(code, empty_field); - if (field->next) - code = Texts(code, ", "); + if (field->next) code = Texts(code, ", "); } return Texts(code, "})"); } @@ -2695,19 +2628,17 @@ Text_t compile_empty(type_t *t) assert(tag); assert(tag->type); if (Match(tag->type, StructType)->fields) - return Texts("((", compile_type(t), "){.$tag=", String(tag->tag_value), ", .", tag->name, "=", compile_empty(tag->type), "})"); - else if (enum_has_fields(t)) - return Texts("((", compile_type(t), "){.$tag=", String(tag->tag_value), "})"); - else - return Texts("((", compile_type(t), ")", String(tag->tag_value), ")"); + return Texts("((", compile_type(t), "){.$tag=", String(tag->tag_value), ", .", tag->name, "=", + compile_empty(tag->type), "})"); + else if (enum_has_fields(t)) return Texts("((", compile_type(t), "){.$tag=", String(tag->tag_value), "})"); + else return Texts("((", compile_type(t), ")", String(tag->tag_value), ")"); } default: return EMPTY_TEXT; } return EMPTY_TEXT; } -static Text_t compile_declared_value(env_t *env, ast_t *declare_ast) -{ +static Text_t compile_declared_value(env_t *env, ast_t *declare_ast) { DeclareMatch(decl, declare_ast, Declare); type_t *t = decl->type ? parse_type_ast(env, decl->type) : get_type(env, decl->value); @@ -2724,30 +2655,27 @@ static Text_t compile_declared_value(env_t *env, ast_t *declare_ast) } else { Text_t val_code = compile_empty(t); if (val_code.length == 0) - code_err(declare_ast, "This type (", type_to_str(t), ") cannot be uninitialized. You must provide a value."); + code_err(declare_ast, "This type (", type_to_str(t), + ") cannot be uninitialized. You must provide a value."); return val_code; } } -ast_t *add_to_table_comprehension(ast_t *entry, ast_t *subject) -{ +ast_t *add_to_table_comprehension(ast_t *entry, ast_t *subject) { DeclareMatch(e, entry, TableEntry); - return WrapAST(entry, MethodCall, .name="set", .self=subject, - .args=new(arg_ast_t, .value=e->key, .next=new(arg_ast_t, .value=e->value))); + return WrapAST(entry, MethodCall, .name = "set", .self = subject, + .args = new (arg_ast_t, .value = e->key, .next = new (arg_ast_t, .value = e->value))); } -ast_t *add_to_list_comprehension(ast_t *item, ast_t *subject) -{ - return WrapAST(item, MethodCall, .name="insert", .self=subject, .args=new(arg_ast_t, .value=item)); +ast_t *add_to_list_comprehension(ast_t *item, ast_t *subject) { + return WrapAST(item, MethodCall, .name = "insert", .self = subject, .args = new (arg_ast_t, .value = item)); } -ast_t *add_to_set_comprehension(ast_t *item, ast_t *subject) -{ - return WrapAST(item, MethodCall, .name="add", .self=subject, .args=new(arg_ast_t, .value=item)); +ast_t *add_to_set_comprehension(ast_t *item, ast_t *subject) { + return WrapAST(item, MethodCall, .name = "add", .self = subject, .args = new (arg_ast_t, .value = item)); } -Text_t compile(env_t *env, ast_t *ast) -{ +Text_t compile(env_t *env, ast_t *ast) { switch (ast->tag) { case None: { code_err(ast, "I can't figure out what this `none`'s type is!"); @@ -2755,16 +2683,14 @@ Text_t compile(env_t *env, ast_t *ast) case Bool: return Match(ast, Bool)->b ? Text("yes") : Text("no"); case Var: { binding_t *b = get_binding(env, Match(ast, Var)->name); - if (b) - return b->code.length > 0 ? b->code : Texts("_$", Match(ast, Var)->name); + if (b) return b->code.length > 0 ? b->code : Texts("_$", Match(ast, Var)->name); // return Texts("_$", Match(ast, Var)->name); code_err(ast, "I don't know of any variable by this name"); } case Int: { const char *str = Match(ast, Int)->str; OptionalInt_t int_val = Int$from_str(str); - if (int_val.small == 0) - code_err(ast, "Failed to parse this integer"); + if (int_val.small == 0) code_err(ast, "Failed to parse this integer"); mpz_t i; mpz_init_set_int(i, int_val); if (mpz_cmpabs_ui(i, BIGGEST_SMALL_INT) <= 0) { @@ -2786,21 +2712,16 @@ Text_t compile(env_t *env, ast_t *ast) if (b && b->type->tag == FunctionType) { DeclareMatch(fn, b->type, FunctionType); if (fn->args && can_compile_to_type(env, value, get_arg_type(env, fn->args))) - return Texts(b->code, "(", compile_arguments(env, ast, fn->args, new(arg_ast_t, .value=value)), ")"); + return Texts(b->code, "(", compile_arguments(env, ast, fn->args, new (arg_ast_t, .value = value)), ")"); } - if (t->tag == BoolType) - return Texts("!(", compile(env, value), ")"); - else if (t->tag == IntType || t->tag == ByteType) - return Texts("~(", compile(env, value), ")"); - else if (t->tag == ListType) - return Texts("((", compile(env, value), ").length == 0)"); + if (t->tag == BoolType) return Texts("!(", compile(env, value), ")"); + else if (t->tag == IntType || t->tag == ByteType) return Texts("~(", compile(env, value), ")"); + else if (t->tag == ListType) return Texts("((", compile(env, value), ").length == 0)"); else if (t->tag == SetType || t->tag == TableType) return Texts("((", compile(env, value), ").entries.length == 0)"); - else if (t->tag == TextType) - return Texts("(", compile(env, value), ".length == 0)"); - else if (t->tag == OptionalType) - return check_none(t, compile(env, value)); + else if (t->tag == TextType) return Texts("(", compile(env, value), ".length == 0)"); + else if (t->tag == OptionalType) return check_none(t, compile(env, value)); code_err(ast, "I don't know how to negate values of type ", type_to_str(t)); } @@ -2811,16 +2732,15 @@ Text_t compile(env_t *env, ast_t *ast) if (b && b->type->tag == FunctionType) { DeclareMatch(fn, b->type, FunctionType); if (fn->args && can_compile_to_type(env, value, get_arg_type(env, fn->args))) - return Texts(b->code, "(", compile_arguments(env, ast, fn->args, new(arg_ast_t, .value=value)), ")"); + return Texts(b->code, "(", compile_arguments(env, ast, fn->args, new (arg_ast_t, .value = value)), ")"); } - if (t->tag == IntType || t->tag == NumType) - return Texts("-(", compile(env, value), ")"); + if (t->tag == IntType || t->tag == NumType) return Texts("-(", compile(env, value), ")"); code_err(ast, "I don't know how to get the negative value of type ", type_to_str(t)); - } - case HeapAllocate: case StackReference: { + case HeapAllocate: + case StackReference: { return compile_typed_allocation(env, ast, get_type(env, ast)); } case Optional: { @@ -2833,20 +2753,32 @@ Text_t compile(env_t *env, ast_t *ast) type_t *t = get_type(env, value); Text_t value_code = compile(env, value); int64_t line = get_line_number(ast->file, ast->start); - return Texts("({ ", compile_declaration(t, Text("opt")), " = ", value_code, "; ", - "if unlikely (", check_none(t, Text("opt")), ")\n", - "#line ", String(line), "\n", - "fail_source(", quoted_str(ast->file->filename), ", ", - String((int64_t)(value->start - value->file->text)), ", ", - String((int64_t)(value->end - value->file->text)), ", ", - "\"This was expected to be a value, but it's none\");\n", - optional_into_nonnone(t, Text("opt")), "; })"); - } - case Power: case Multiply: case Divide: case Mod: case Mod1: case Plus: case Minus: case Concat: - case LeftShift: case UnsignedLeftShift: case RightShift: case UnsignedRightShift: case And: case Or: case Xor: { + return Texts("({ ", compile_declaration(t, Text("opt")), " = ", value_code, "; ", "if unlikely (", + check_none(t, Text("opt")), ")\n", "#line ", String(line), "\n", "fail_source(", + quoted_str(ast->file->filename), ", ", String((int64_t)(value->start - value->file->text)), ", ", + String((int64_t)(value->end - value->file->text)), ", ", + "\"This was expected to be a value, but it's none\");\n", optional_into_nonnone(t, Text("opt")), + "; })"); + } + case Power: + case Multiply: + case Divide: + case Mod: + case Mod1: + case Plus: + case Minus: + case Concat: + case LeftShift: + case UnsignedLeftShift: + case RightShift: + case UnsignedRightShift: + case And: + case Or: + case Xor: { return compile_binary_op(env, ast); } - case Equals: case NotEquals: { + case Equals: + case NotEquals: { binary_operands_t binop = BINARY_OPERANDS(ast); type_t *lhs_t = get_type(env, binop.lhs); @@ -2871,14 +2803,22 @@ Text_t compile(env_t *env, ast_t *ast) switch (operand_t->tag) { case BigIntType: return Texts(ast->tag == Equals ? EMPTY_TEXT : Text("!"), "Int$equal_value(", lhs, ", ", rhs, ")"); - case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType: - return Texts("(", lhs, ast->tag == Equals ? " == " : " != ", rhs, ")"); + case BoolType: + case ByteType: + case IntType: + case NumType: + case PointerType: + case FunctionType: return Texts("(", lhs, ast->tag == Equals ? " == " : " != ", rhs, ")"); default: - return Texts(ast->tag == Equals ? EMPTY_TEXT : Text("!"), - "generic_equal(stack(", lhs, "), stack(", rhs, "), ", compile_type_info(operand_t), ")"); + return Texts(ast->tag == Equals ? EMPTY_TEXT : Text("!"), "generic_equal(stack(", lhs, "), stack(", rhs, + "), ", compile_type_info(operand_t), ")"); } } - case LessThan: case LessThanOrEquals: case GreaterThan: case GreaterThanOrEquals: case Compare: { + case LessThan: + case LessThanOrEquals: + case GreaterThan: + case GreaterThanOrEquals: + case Compare: { binary_operands_t cmp = BINARY_OPERANDS(ast); type_t *lhs_t = get_type(env, cmp.lhs); @@ -2900,49 +2840,47 @@ Text_t compile(env_t *env, ast_t *ast) Text_t rhs = compile_to_type(env, cmp.rhs, operand_t); if (ast->tag == Compare) - return Texts("generic_compare(stack(", lhs, "), stack(", rhs, "), ", - compile_type_info(operand_t), ")"); + return Texts("generic_compare(stack(", lhs, "), stack(", rhs, "), ", compile_type_info(operand_t), ")"); const char *op = binop_operator(ast->tag); switch (operand_t->tag) { - case BigIntType: - return Texts("(Int$compare_value(", lhs, ", ", rhs, ") ", op, " 0)"); - case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType: - return Texts("(", lhs, " ", op, " ", rhs, ")"); + case BigIntType: return Texts("(Int$compare_value(", lhs, ", ", rhs, ") ", op, " 0)"); + case BoolType: + case ByteType: + case IntType: + case NumType: + case PointerType: + case FunctionType: return Texts("(", lhs, " ", op, " ", rhs, ")"); default: - return Texts("(generic_compare(stack(", lhs, "), stack(", rhs, "), ", - compile_type_info(operand_t), ") ", op, " 0)"); + return Texts("(generic_compare(stack(", lhs, "), stack(", rhs, "), ", compile_type_info(operand_t), ") ", + op, " 0)"); } } case TextLiteral: { - Text_t literal = Match(ast, TextLiteral)->text; - if (literal.length == 0) - return Text("EMPTY_TEXT"); + Text_t literal = Match(ast, TextLiteral)->text; + if (literal.length == 0) return Text("EMPTY_TEXT"); - if (string_literal_is_all_ascii(literal)) - return Texts("Text(", compile_text_literal(literal), ")"); - else - return Texts("Text$from_str(", compile_text_literal(literal), ")"); + if (string_literal_is_all_ascii(literal)) return Texts("Text(", compile_text_literal(literal), ")"); + else return Texts("Text$from_str(", compile_text_literal(literal), ")"); } case TextJoin: { const char *lang = Match(ast, TextJoin)->lang; Text_t colorize = Match(ast, TextJoin)->colorize ? Text("yes") : Text("no"); type_t *text_t = lang ? Table$str_get(*env->types, lang) : TEXT_TYPE; - if (!text_t || text_t->tag != TextType) - code_err(ast, quoted(lang), " is not a valid text language name"); + if (!text_t || text_t->tag != TextType) code_err(ast, quoted(lang), " is not a valid text language name"); Text_t lang_constructor; - if (!lang || streq(lang, "Text")) - lang_constructor = Text("Text"); + if (!lang || streq(lang, "Text")) lang_constructor = Text("Text"); else - lang_constructor = namespace_name(Match(text_t, TextType)->env, Match(text_t, TextType)->env->namespace->parent, Text$from_str(lang)); + lang_constructor = namespace_name(Match(text_t, TextType)->env, + Match(text_t, TextType)->env->namespace->parent, Text$from_str(lang)); ast_list_t *chunks = Match(ast, TextJoin)->children; if (!chunks) { return Texts(lang_constructor, "(\"\")"); } else if (!chunks->next && chunks->ast->tag == TextLiteral) { - Text_t literal = Match(chunks->ast, TextLiteral)->text; + Text_t literal = Match(chunks->ast, TextLiteral)->text; if (string_literal_is_all_ascii(literal)) return Texts(lang_constructor, "(", compile_text_literal(literal), ")"); return Texts("((", compile_type(text_t), ")", compile(env, chunks->ast), ")"); @@ -2954,28 +2892,26 @@ Text_t compile(env_t *env, ast_t *ast) if (chunk->ast->tag == TextLiteral || type_eq(chunk_t, text_t)) { chunk_code = compile(env, chunk->ast); } else { - binding_t *constructor = get_constructor(env, text_t, new(arg_ast_t, .value=chunk->ast), - env->current_type != NULL && type_eq(env->current_type, text_t)); + binding_t *constructor = + get_constructor(env, text_t, new (arg_ast_t, .value = chunk->ast), + env->current_type != NULL && type_eq(env->current_type, text_t)); if (constructor) { arg_t *arg_spec = Match(constructor->type, FunctionType)->args; - arg_ast_t *args = new(arg_ast_t, .value=chunk->ast); + arg_ast_t *args = new (arg_ast_t, .value = chunk->ast); chunk_code = Texts(constructor->code, "(", compile_arguments(env, ast, arg_spec, args), ")"); } else if (type_eq(text_t, TEXT_TYPE)) { - if (chunk_t->tag == TextType) - chunk_code = compile(env, chunk->ast); - else - chunk_code = compile_text(env, chunk->ast, colorize); + if (chunk_t->tag == TextType) chunk_code = compile(env, chunk->ast); + else chunk_code = compile_text(env, chunk->ast, colorize); } else { - code_err(chunk->ast, "I don't know how to convert ", type_to_str(chunk_t), " to ", type_to_str(text_t)); + code_err(chunk->ast, "I don't know how to convert ", type_to_str(chunk_t), " to ", + type_to_str(text_t)); } } code = Texts(code, chunk_code); if (chunk->next) code = Texts(code, ", "); } - if (chunks->next) - return Texts(lang_constructor, "s(", code, ")"); - else - return code; + if (chunks->next) return Texts(lang_constructor, "s(", code, ")"); + else return code; } } case Path: { @@ -2983,8 +2919,7 @@ Text_t compile(env_t *env, ast_t *ast) } case Block: { ast_list_t *stmts = Match(ast, Block)->statements; - if (stmts && !stmts->next) - return compile(env, stmts->ast); + if (stmts && !stmts->next) return compile(env, stmts->ast); Text_t code = Text("({\n"); deferral_t *prev_deferred = env->deferred; @@ -2996,7 +2931,8 @@ Text_t compile(env_t *env, ast_t *ast) code = Texts(code, compile_statement(env, stmt->ast), "\n"); } else { // TODO: put defer after evaluating block expression - for (deferral_t *deferred = env->deferred; deferred && deferred != prev_deferred; deferred = deferred->next) { + for (deferral_t *deferred = env->deferred; deferred && deferred != prev_deferred; + deferred = deferred->next) { code = Texts(code, compile_statement(deferred->defer_env, deferred->block)); } code = Texts(code, compile(env, stmt->ast), ";\n"); @@ -3006,7 +2942,8 @@ Text_t compile(env_t *env, ast_t *ast) return Texts(code, "})"); } - case Min: case Max: { + case Min: + case Max: { type_t *t = get_type(env, ast); ast_t *key = ast->tag == Min ? Match(ast, Min)->key : Match(ast, Max)->key; ast_t *lhs = ast->tag == Min ? Match(ast, Min)->lhs : Match(ast, Max)->lhs; @@ -3024,23 +2961,23 @@ Text_t compile(env_t *env, ast_t *ast) type_t *key_t = get_type(expr_env, key); Text_t comparison; if (key_t->tag == BigIntType) - comparison = Texts("(Int$compare_value(", lhs_key, ", ", rhs_key, ")", (ast->tag == Min ? "<=" : ">="), "0)"); - else if (key_t->tag == IntType || key_t->tag == NumType || key_t->tag == BoolType || key_t->tag == PointerType || key_t->tag == ByteType) + comparison = + Texts("(Int$compare_value(", lhs_key, ", ", rhs_key, ")", (ast->tag == Min ? "<=" : ">="), "0)"); + else if (key_t->tag == IntType || key_t->tag == NumType || key_t->tag == BoolType || key_t->tag == PointerType + || key_t->tag == ByteType) comparison = Texts("((", lhs_key, ")", (ast->tag == Min ? "<=" : ">="), "(", rhs_key, "))"); else - comparison = Texts("generic_compare(stack(", lhs_key, "), stack(", rhs_key, "), ", compile_type_info(key_t), ")", - (ast->tag == Min ? "<=" : ">="), "0"); + comparison = Texts("generic_compare(stack(", lhs_key, "), stack(", rhs_key, "), ", compile_type_info(key_t), + ")", (ast->tag == Min ? "<=" : ">="), "0"); - return Texts( - "({\n", - compile_type(t), " ternary$lhs = ", compile(env, lhs), ", ternary$rhs = ", compile(env, rhs), ";\n", - comparison, " ? ternary$lhs : ternary$rhs;\n" - "})"); + return Texts("({\n", compile_type(t), " ternary$lhs = ", compile(env, lhs), + ", ternary$rhs = ", compile(env, rhs), ";\n", comparison, + " ? ternary$lhs : ternary$rhs;\n" + "})"); } case List: { DeclareMatch(list, ast, List); - if (!list->items) - return Text("(List_t){.length=0}"); + if (!list->items) return Text("(List_t){.length=0}"); type_t *list_type = get_type(env, ast); return compile_typed_list(env, ast, list_type); @@ -3049,8 +2986,7 @@ Text_t compile(env_t *env, ast_t *ast) DeclareMatch(table, ast, Table); if (!table->entries) { Text_t code = Text("((Table_t){"); - if (table->fallback) - code = Texts(code, ".fallback=heap(", compile(env, table->fallback),")"); + if (table->fallback) code = Texts(code, ".fallback=heap(", compile(env, table->fallback), ")"); return Texts(code, "})"); } @@ -3059,8 +2995,7 @@ Text_t compile(env_t *env, ast_t *ast) } case Set: { DeclareMatch(set, ast, Set); - if (!set->items) - return Text("((Table_t){})"); + if (!set->items) return Text("((Table_t){})"); type_t *set_type = get_type(env, ast); return compile_typed_set(env, ast, set_type); @@ -3069,10 +3004,8 @@ Text_t compile(env_t *env, ast_t *ast) ast_t *base = Match(ast, Comprehension)->expr; while (base->tag == Comprehension) base = Match(ast, Comprehension)->expr; - if (base->tag == TableEntry) - return compile(env, WrapAST(ast, Table, .entries=new(ast_list_t, .ast=ast))); - else - return compile(env, WrapAST(ast, List, .items=new(ast_list_t, .ast=ast))); + if (base->tag == TableEntry) return compile(env, WrapAST(ast, Table, .entries = new (ast_list_t, .ast = ast))); + else return compile(env, WrapAST(ast, List, .items = new (ast_list_t, .ast = ast))); } case Lambda: { DeclareMatch(lambda, ast, Lambda); @@ -3086,13 +3019,11 @@ Text_t compile(env_t *env, ast_t *ast) } type_t *ret_t = get_type(body_scope, lambda->body); - if (ret_t->tag == ReturnType) - ret_t = Match(ret_t, ReturnType)->ret; + if (ret_t->tag == ReturnType) ret_t = Match(ret_t, ReturnType)->ret; if (lambda->ret_type) { type_t *declared = parse_type_ast(env, lambda->ret_type); - if (can_promote(ret_t, declared)) - ret_t = declared; + if (can_promote(ret_t, declared)) ret_t = declared; else code_err(ast, "This function was declared to return a value of type ", type_to_str(declared), ", but actually returns a value of type ", type_to_str(ret_t)); @@ -3104,12 +3035,15 @@ Text_t compile(env_t *env, ast_t *ast) if (Table$length(closed_vars) > 0) { // Create a typedef for the lambda's closure userdata Text_t def = Text("typedef struct {"); for (int64_t i = 0; i < closed_vars.entries.length; i++) { - struct { const char *name; binding_t *b; } *entry = closed_vars.entries.data + closed_vars.entries.stride*i; + struct { + const char *name; + binding_t *b; + } *entry = closed_vars.entries.data + closed_vars.entries.stride * i; if (has_stack_memory(entry->b->type)) code_err(ast, "This function is holding onto a reference to ", type_to_str(entry->b->type), - " stack memory in the variable `", entry->name, "`, but the function may outlive the stack memory"); - if (entry->b->type->tag == ModuleType) - continue; + " stack memory in the variable `", entry->name, + "`, but the function may outlive the stack memory"); + if (entry->b->type->tag == ModuleType) continue; set_binding(body_scope, entry->name, entry->b->type, Texts("userdata->", entry->name)); def = Texts(def, compile_declaration(entry->b->type, Text$from_str(entry->name)), "; "); } @@ -3130,18 +3064,18 @@ Text_t compile(env_t *env, ast_t *ast) } else { userdata = Texts("new(", name, "$userdata_t"); for (int64_t i = 0; i < closed_vars.entries.length; i++) { - struct { const char *name; binding_t *b; } *entry = closed_vars.entries.data + closed_vars.entries.stride*i; - if (entry->b->type->tag == ModuleType) - continue; + struct { + const char *name; + binding_t *b; + } *entry = closed_vars.entries.data + closed_vars.entries.stride * i; + if (entry->b->type->tag == ModuleType) continue; binding_t *b = get_binding(env, entry->name); assert(b); Text_t binding_code = b->code; - if (entry->b->type->tag == ListType) - userdata = Texts(userdata, ", LIST_COPY(", binding_code, ")"); + if (entry->b->type->tag == ListType) userdata = Texts(userdata, ", LIST_COPY(", binding_code, ")"); else if (entry->b->type->tag == TableType || entry->b->type->tag == SetType) userdata = Texts(userdata, ", TABLE_COPY(", binding_code, ")"); - else - userdata = Texts(userdata, ", ", binding_code); + else userdata = Texts(userdata, ", ", binding_code); } userdata = Texts(userdata, ")"); code = Texts(code, name, "$userdata_t *userdata)"); @@ -3149,10 +3083,10 @@ Text_t compile(env_t *env, ast_t *ast) Text_t body = EMPTY_TEXT; for (ast_list_t *stmt = Match(lambda->body, Block)->statements; stmt; stmt = stmt->next) { - if (stmt->next || ret_t->tag == VoidType || ret_t->tag == AbortType || get_type(body_scope, stmt->ast)->tag == ReturnType) + if (stmt->next || ret_t->tag == VoidType || ret_t->tag == AbortType + || get_type(body_scope, stmt->ast)->tag == ReturnType) body = Texts(body, compile_statement(body_scope, stmt->ast), "\n"); - else - body = Texts(body, compile_statement(body_scope, FakeAST(Return, stmt->ast)), "\n"); + else body = Texts(body, compile_statement(body_scope, FakeAST(Return, stmt->ast)), "\n"); bind_statement(body_scope, stmt->ast); } if ((ret_t->tag == VoidType || ret_t->tag == AbortType) && body_scope->deferred) @@ -3166,10 +3100,9 @@ Text_t compile(env_t *env, ast_t *ast) type_t *self_t = get_type(env, call->self); if (streq(call->name, "serialized")) { - if (call->args) - code_err(ast, ".serialized() doesn't take any arguments"); + if (call->args) code_err(ast, ".serialized() doesn't take any arguments"); return Texts("generic_serialize((", compile_declaration(self_t, Text("[1]")), "){", - compile(env, call->self), "}, ", compile_type_info(self_t), ")"); + compile(env, call->self), "}, ", compile_type_info(self_t), ")"); } int64_t pointer_depth = 0; @@ -3178,23 +3111,29 @@ Text_t compile(env_t *env, ast_t *ast) pointer_depth += 1; if (self_value_t->tag == TypeInfoType || self_value_t->tag == ModuleType) { - return compile(env, WrapAST(ast, FunctionCall, .fn=WrapAST(call->self, FieldAccess, .fielded=call->self, .field=call->name), - .args=call->args)); + return compile(env, + WrapAST(ast, FunctionCall, + .fn = WrapAST(call->self, FieldAccess, .fielded = call->self, .field = call->name), + .args = call->args)); } type_t *field_type = get_field_type(self_value_t, call->name); - if (field_type && field_type->tag == ClosureType) - field_type = Match(field_type, ClosureType)->fn; + if (field_type && field_type->tag == ClosureType) field_type = Match(field_type, ClosureType)->fn; if (field_type && field_type->tag == FunctionType) - return compile(env, WrapAST(ast, FunctionCall, .fn=WrapAST(call->self, FieldAccess, .fielded=call->self, .field=call->name), - .args=call->args)); + return compile(env, + WrapAST(ast, FunctionCall, + .fn = WrapAST(call->self, FieldAccess, .fielded = call->self, .field = call->name), + .args = call->args)); Text_t self = compile(env, call->self); -#define EXPECT_POINTER(article, name) do { \ - if (pointer_depth < 1) code_err(call->self, "I expected "article" "name" pointer here, not "article" "name" value"); \ - else if (pointer_depth > 1) code_err(call->self, "I expected "article" "name" pointer here, not a nested "name" pointer"); \ -} while (0) +#define EXPECT_POINTER(article, name) \ + do { \ + if (pointer_depth < 1) \ + code_err(call->self, "I expected " article " " name " pointer here, not " article " " name " value"); \ + else if (pointer_depth > 1) \ + code_err(call->self, "I expected " article " " name " pointer here, not a nested " name " pointer"); \ + } while (0) switch (self_value_t->tag) { case ListType: { type_t *item_t = Match(self_value_t, ListType)->item_type; @@ -3202,116 +3141,141 @@ Text_t compile(env_t *env, ast_t *ast) if (streq(call->name, "insert")) { EXPECT_POINTER("a", "list"); - arg_t *arg_spec = new(arg_t, .name="item", .type=item_t, - .next=new(arg_t, .name="at", .type=INT_TYPE, .default_val=FakeAST(Int, .str="0"))); + arg_t *arg_spec = + new (arg_t, .name = "item", .type = item_t, + .next = new (arg_t, .name = "at", .type = INT_TYPE, .default_val = FakeAST(Int, .str = "0"))); return Texts("List$insert_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - padded_item_size, ")"); + padded_item_size, ")"); } else if (streq(call->name, "insert_all")) { EXPECT_POINTER("a", "list"); - arg_t *arg_spec = new(arg_t, .name="items", .type=self_value_t, - .next=new(arg_t, .name="at", .type=INT_TYPE, .default_val=FakeAST(Int, .str="0"))); + arg_t *arg_spec = + new (arg_t, .name = "items", .type = self_value_t, + .next = new (arg_t, .name = "at", .type = INT_TYPE, .default_val = FakeAST(Int, .str = "0"))); return Texts("List$insert_all(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - padded_item_size, ")"); + padded_item_size, ")"); } else if (streq(call->name, "remove_at")) { EXPECT_POINTER("a", "list"); - arg_t *arg_spec = new(arg_t, .name="index", .type=INT_TYPE, .default_val=FakeAST(Int, .str="-1"), - .next=new(arg_t, .name="count", .type=INT_TYPE, .default_val=FakeAST(Int, .str="1"))); + arg_t *arg_spec = new ( + arg_t, .name = "index", .type = INT_TYPE, .default_val = FakeAST(Int, .str = "-1"), + .next = new (arg_t, .name = "count", .type = INT_TYPE, .default_val = FakeAST(Int, .str = "1"))); return Texts("List$remove_at(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - padded_item_size, ")"); + padded_item_size, ")"); } else if (streq(call->name, "remove_item")) { EXPECT_POINTER("a", "list"); - arg_t *arg_spec = new(arg_t, .name="item", .type=item_t, - .next=new(arg_t, .name="max_count", .type=INT_TYPE, .default_val=FakeAST(Int, .str="-1"))); - return Texts("List$remove_item_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - compile_type_info(self_value_t), ")"); + arg_t *arg_spec = new (arg_t, .name = "item", .type = item_t, + .next = new (arg_t, .name = "max_count", .type = INT_TYPE, + .default_val = FakeAST(Int, .str = "-1"))); + return Texts("List$remove_item_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), + ", ", compile_type_info(self_value_t), ")"); } else if (streq(call->name, "has")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="item", .type=item_t); + arg_t *arg_spec = new (arg_t, .name = "item", .type = item_t); return Texts("List$has_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - compile_type_info(self_value_t), ")"); + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "sample")) { type_t *random_num_type = parse_type_string(env, "func(->Num)?"); self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="count", .type=INT_TYPE, - .next=new(arg_t, .name="weights", .type=Type(ListType, .item_type=Type(NumType, .bits=TYPE_NBITS64)), - .default_val=FakeAST(None), - .next=new(arg_t, .name="random", .type=random_num_type, .default_val=FakeAST(None)))); + arg_t *arg_spec = + new (arg_t, .name = "count", .type = INT_TYPE, + .next = new (arg_t, .name = "weights", + .type = Type(ListType, .item_type = Type(NumType, .bits = TYPE_NBITS64)), + .default_val = FakeAST(None), + .next = new (arg_t, .name = "random", .type = random_num_type, + .default_val = FakeAST(None)))); return Texts("List$sample(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - padded_item_size, ")"); + padded_item_size, ")"); } else if (streq(call->name, "shuffle")) { type_t *random_int64_type = parse_type_string(env, "func(min,max:Int64->Int64)?"); EXPECT_POINTER("a", "list"); - arg_t *arg_spec = new(arg_t, .name="random", .type=random_int64_type, .default_val=FakeAST(None)); - return Texts("List$shuffle(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); + arg_t *arg_spec = + new (arg_t, .name = "random", .type = random_int64_type, .default_val = FakeAST(None)); + return Texts("List$shuffle(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + padded_item_size, ")"); } else if (streq(call->name, "shuffled")) { type_t *random_int64_type = parse_type_string(env, "func(min,max:Int64->Int64)?"); self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="random", .type=random_int64_type, .default_val=FakeAST(None)); - return Texts("List$shuffled(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); + arg_t *arg_spec = + new (arg_t, .name = "random", .type = random_int64_type, .default_val = FakeAST(None)); + return Texts("List$shuffled(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + padded_item_size, ")"); } else if (streq(call->name, "random")) { type_t *random_int64_type = parse_type_string(env, "func(min,max:Int64->Int64)?"); self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="random", .type=random_int64_type, .default_val=FakeAST(None)); - return Texts("List$random_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", compile_type(item_t), ")"); + arg_t *arg_spec = + new (arg_t, .name = "random", .type = random_int64_type, .default_val = FakeAST(None)); + return Texts("List$random_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + compile_type(item_t), ")"); } else if (streq(call->name, "sort") || streq(call->name, "sorted")) { - if (streq(call->name, "sort")) - EXPECT_POINTER("a", "list"); - else - self = compile_to_pointer_depth(env, call->self, 0, false); + if (streq(call->name, "sort")) EXPECT_POINTER("a", "list"); + else self = compile_to_pointer_depth(env, call->self, 0, false); Text_t comparison; if (call->args) { - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); - type_t *fn_t = NewFunctionType(Type(IntType, .bits=TYPE_IBITS32), {.name="x", .type=item_ptr}, {.name="y", .type=item_ptr}); - arg_t *arg_spec = new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t)); + type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true); + type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr}, + {.name = "y", .type = item_ptr}); + arg_t *arg_spec = new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t)); comparison = compile_arguments(env, ast, arg_spec, call->args); } else { - comparison = Texts("((Closure_t){.fn=generic_compare, .userdata=(void*)", compile_type_info(item_t), "})"); + comparison = Texts("((Closure_t){.fn=generic_compare, " + ".userdata=(void*)", + compile_type_info(item_t), "})"); } return Texts("List$", call->name, "(", self, ", ", comparison, ", ", padded_item_size, ")"); } else if (streq(call->name, "heapify")) { EXPECT_POINTER("a", "list"); Text_t comparison; if (call->args) { - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); - type_t *fn_t = NewFunctionType(Type(IntType, .bits=TYPE_IBITS32), {.name="x", .type=item_ptr}, {.name="y", .type=item_ptr}); - arg_t *arg_spec = new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t)); + type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true); + type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr}, + {.name = "y", .type = item_ptr}); + arg_t *arg_spec = new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t)); comparison = compile_arguments(env, ast, arg_spec, call->args); } else { - comparison = Texts("((Closure_t){.fn=generic_compare, .userdata=(void*)", compile_type_info(item_t), "})"); + comparison = Texts("((Closure_t){.fn=generic_compare, " + ".userdata=(void*)", + compile_type_info(item_t), "})"); } return Texts("List$heapify(", self, ", ", comparison, ", ", padded_item_size, ")"); } else if (streq(call->name, "heap_push")) { EXPECT_POINTER("a", "list"); - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); - type_t *fn_t = NewFunctionType(Type(IntType, .bits=TYPE_IBITS32), {.name="x", .type=item_ptr}, {.name="y", .type=item_ptr}); - ast_t *default_cmp = LiteralCode(Texts("((Closure_t){.fn=generic_compare, .userdata=(void*)", - compile_type_info(item_t), "})"), - .type=Type(ClosureType, .fn=fn_t)); - arg_t *arg_spec = new(arg_t, .name="item", .type=item_t, - .next=new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t), .default_val=default_cmp)); + type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true); + type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr}, + {.name = "y", .type = item_ptr}); + ast_t *default_cmp = LiteralCode(Texts("((Closure_t){.fn=generic_compare, " + ".userdata=(void*)", + compile_type_info(item_t), "})"), + .type = Type(ClosureType, .fn = fn_t)); + arg_t *arg_spec = new (arg_t, .name = "item", .type = item_t, + .next = new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t), + .default_val = default_cmp)); Text_t arg_code = compile_arguments(env, ast, arg_spec, call->args); return Texts("List$heap_push_value(", self, ", ", arg_code, ", ", padded_item_size, ")"); } else if (streq(call->name, "heap_pop")) { EXPECT_POINTER("a", "list"); - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); - type_t *fn_t = NewFunctionType(Type(IntType, .bits=TYPE_IBITS32), {.name="x", .type=item_ptr}, {.name="y", .type=item_ptr}); - ast_t *default_cmp = LiteralCode(Texts("((Closure_t){.fn=generic_compare, .userdata=(void*)", - compile_type_info(item_t), "})"), - .type=Type(ClosureType, .fn=fn_t)); - arg_t *arg_spec = new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t), .default_val=default_cmp); + type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true); + type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr}, + {.name = "y", .type = item_ptr}); + ast_t *default_cmp = LiteralCode(Texts("((Closure_t){.fn=generic_compare, " + ".userdata=(void*)", + compile_type_info(item_t), "})"), + .type = Type(ClosureType, .fn = fn_t)); + arg_t *arg_spec = + new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t), .default_val = default_cmp); Text_t arg_code = compile_arguments(env, ast, arg_spec, call->args); return Texts("List$heap_pop_value(", self, ", ", arg_code, ", ", compile_type(item_t), ", _, ", - promote_to_optional(item_t, Text("_")), ", ", compile_none(item_t), ")"); + promote_to_optional(item_t, Text("_")), ", ", compile_none(item_t), ")"); } else if (streq(call->name, "binary_search")) { self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL); - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); - type_t *fn_t = NewFunctionType(Type(IntType, .bits=TYPE_IBITS32), {.name="x", .type=item_ptr}, {.name="y", .type=item_ptr}); - ast_t *default_cmp = LiteralCode( - Texts("((Closure_t){.fn=generic_compare, .userdata=(void*)", - compile_type_info(item_t), "})"), - .type=Type(ClosureType, .fn=fn_t)); - arg_t *arg_spec = new(arg_t, .name="target", .type=item_t, - .next=new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t), .default_val=default_cmp)); + type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true); + type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr}, + {.name = "y", .type = item_ptr}); + ast_t *default_cmp = LiteralCode(Texts("((Closure_t){.fn=generic_compare, " + ".userdata=(void*)", + compile_type_info(item_t), "})"), + .type = Type(ClosureType, .fn = fn_t)); + arg_t *arg_spec = new (arg_t, .name = "target", .type = item_t, + .next = new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t), + .default_val = default_cmp)); Text_t arg_code = compile_arguments(env, ast, arg_spec, call->args); return Texts("List$binary_search_value(", self, ", ", arg_code, ")"); } else if (streq(call->name, "clear")) { @@ -3320,32 +3284,34 @@ Text_t compile(env_t *env, ast_t *ast) return Texts("List$clear(", self, ")"); } else if (streq(call->name, "find")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="item", .type=item_t); - return Texts("List$find_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), - ", ", compile_type_info(self_value_t), ")"); + arg_t *arg_spec = new (arg_t, .name = "item", .type = item_t); + return Texts("List$find_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "where")) { self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL); - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); - type_t *predicate_type = Type( - ClosureType, .fn=NewFunctionType(Type(BoolType), {.name="item", .type=item_ptr})); - arg_t *arg_spec = new(arg_t, .name="predicate", .type=predicate_type); + type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true); + type_t *predicate_type = + Type(ClosureType, .fn = NewFunctionType(Type(BoolType), {.name = "item", .type = item_ptr})); + arg_t *arg_spec = new (arg_t, .name = "predicate", .type = predicate_type); return Texts("List$first(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")"); } else if (streq(call->name, "from")) { self = compile_to_pointer_depth(env, call->self, 0, true); - arg_t *arg_spec = new(arg_t, .name="first", .type=INT_TYPE); + arg_t *arg_spec = new (arg_t, .name = "first", .type = INT_TYPE); return Texts("List$from(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")"); } else if (streq(call->name, "to")) { self = compile_to_pointer_depth(env, call->self, 0, true); - arg_t *arg_spec = new(arg_t, .name="last", .type=INT_TYPE); + arg_t *arg_spec = new (arg_t, .name = "last", .type = INT_TYPE); return Texts("List$to(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")"); } else if (streq(call->name, "slice")) { self = compile_to_pointer_depth(env, call->self, 0, true); - arg_t *arg_spec = new(arg_t, .name="first", .type=INT_TYPE, .next=new(arg_t, .name="last", .type=INT_TYPE)); + arg_t *arg_spec = new (arg_t, .name = "first", .type = INT_TYPE, + .next = new (arg_t, .name = "last", .type = INT_TYPE)); return Texts("List$slice(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")"); } else if (streq(call->name, "by")) { self = compile_to_pointer_depth(env, call->self, 0, true); - arg_t *arg_spec = new(arg_t, .name="stride", .type=INT_TYPE); - return Texts("List$by(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); + arg_t *arg_spec = new (arg_t, .name = "stride", .type = INT_TYPE); + return Texts("List$by(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + padded_item_size, ")"); } else if (streq(call->name, "reversed")) { self = compile_to_pointer_depth(env, call->self, 0, true); (void)compile_arguments(env, ast, NULL, call->args); @@ -3356,7 +3322,7 @@ Text_t compile(env_t *env, ast_t *ast) return Texts("Table$from_entries(", self, ", Set$info(", compile_type_info(item_t), "))"); } else if (streq(call->name, "pop")) { EXPECT_POINTER("a", "list"); - arg_t *arg_spec = new(arg_t, .name="index", .type=INT_TYPE, .default_val=FakeAST(Int, "-1")); + arg_t *arg_spec = new (arg_t, .name = "index", .type = INT_TYPE, .default_val = FakeAST(Int, "-1")); Text_t index = compile_arguments(env, ast, arg_spec, call->args); return Texts("List$pop(", self, ", ", index, ", ", compile_type(item_t), ", _, ", promote_to_optional(item_t, Text("_")), ", ", compile_none(item_t), ")"); @@ -3370,103 +3336,105 @@ Text_t compile(env_t *env, ast_t *ast) DeclareMatch(set, self_value_t, SetType); if (streq(call->name, "has")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="key", .type=set->item_type); + arg_t *arg_spec = new (arg_t, .name = "key", .type = set->item_type); return Texts("Table$has_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - compile_type_info(self_value_t), ")"); + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "add")) { EXPECT_POINTER("a", "set"); - arg_t *arg_spec = new(arg_t, .name="item", .type=set->item_type); - return Texts("Table$set_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", NULL, ", - compile_type_info(self_value_t), ")"); + arg_t *arg_spec = new (arg_t, .name = "item", .type = set->item_type); + return Texts("Table$set_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), + ", NULL, ", compile_type_info(self_value_t), ")"); } else if (streq(call->name, "add_all")) { EXPECT_POINTER("a", "set"); - arg_t *arg_spec = new(arg_t, .name="items", .type=Type(ListType, .item_type=Match(self_value_t, SetType)->item_type)); + arg_t *arg_spec = new (arg_t, .name = "items", + .type = Type(ListType, .item_type = Match(self_value_t, SetType)->item_type)); return Texts("({ Table_t *set = ", self, "; ", - "List_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ", - "for (int64_t i = 0; i < to_add.length; i++)\n" - "Table$set(set, to_add.data + i*to_add.stride, NULL, ", compile_type_info(self_value_t), ");\n", - "(void)0; })"); + "List_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ", + "for (int64_t i = 0; i < to_add.length; i++)\n" + "Table$set(set, to_add.data + i*to_add.stride, NULL, ", + compile_type_info(self_value_t), ");\n", "(void)0; })"); } else if (streq(call->name, "remove")) { EXPECT_POINTER("a", "set"); - arg_t *arg_spec = new(arg_t, .name="item", .type=set->item_type); + arg_t *arg_spec = new (arg_t, .name = "item", .type = set->item_type); return Texts("Table$remove_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - compile_type_info(self_value_t), ")"); + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "remove_all")) { EXPECT_POINTER("a", "set"); - arg_t *arg_spec = new(arg_t, .name="items", .type=Type(ListType, .item_type=Match(self_value_t, SetType)->item_type)); + arg_t *arg_spec = new (arg_t, .name = "items", + .type = Type(ListType, .item_type = Match(self_value_t, SetType)->item_type)); return Texts("({ Table_t *set = ", self, "; ", - "List_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ", - "for (int64_t i = 0; i < to_add.length; i++)\n" - "Table$remove(set, to_add.data + i*to_add.stride, ", compile_type_info(self_value_t), ");\n", - "(void)0; })"); + "List_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ", + "for (int64_t i = 0; i < to_add.length; i++)\n" + "Table$remove(set, to_add.data + i*to_add.stride, ", + compile_type_info(self_value_t), ");\n", "(void)0; })"); } else if (streq(call->name, "clear")) { EXPECT_POINTER("a", "set"); (void)compile_arguments(env, ast, NULL, call->args); return Texts("Table$clear(", self, ")"); } else if (streq(call->name, "with")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="other", .type=self_value_t); - return Texts("Table$with(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), - ", ", compile_type_info(self_value_t), ")"); + arg_t *arg_spec = new (arg_t, .name = "other", .type = self_value_t); + return Texts("Table$with(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "overlap")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="other", .type=self_value_t); - return Texts("Table$overlap(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), - ", ", compile_type_info(self_value_t), ")"); + arg_t *arg_spec = new (arg_t, .name = "other", .type = self_value_t); + return Texts("Table$overlap(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "without")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="other", .type=self_value_t); - return Texts("Table$without(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), - ", ", compile_type_info(self_value_t), ")"); + arg_t *arg_spec = new (arg_t, .name = "other", .type = self_value_t); + return Texts("Table$without(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "is_subset_of")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="other", .type=self_value_t, - .next=new(arg_t, .name="strict", .type=Type(BoolType), .default_val=FakeAST(Bool, false))); - return Texts("Table$is_subset_of(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), - ", ", compile_type_info(self_value_t), ")"); + arg_t *arg_spec = new ( + arg_t, .name = "other", .type = self_value_t, + .next = new (arg_t, .name = "strict", .type = Type(BoolType), .default_val = FakeAST(Bool, false))); + return Texts("Table$is_subset_of(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "is_superset_of")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="other", .type=self_value_t, - .next=new(arg_t, .name="strict", .type=Type(BoolType), .default_val=FakeAST(Bool, false))); + arg_t *arg_spec = new ( + arg_t, .name = "other", .type = self_value_t, + .next = new (arg_t, .name = "strict", .type = Type(BoolType), .default_val = FakeAST(Bool, false))); return Texts("Table$is_superset_of(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), - ", ", compile_type_info(self_value_t), ")"); + ", ", compile_type_info(self_value_t), ")"); } else code_err(ast, "There is no '", call->name, "' method for tables"); } case TableType: { DeclareMatch(table, self_value_t, TableType); if (streq(call->name, "get")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type); - return Texts( - "Table$get_optional(", self, ", ", compile_type(table->key_type), ", ", - compile_type(table->value_type), ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - "_, ", optional_into_nonnone(table->value_type, Text("(*_)")), ", ", compile_none(table->value_type), ", ", - compile_type_info(self_value_t), ")"); + arg_t *arg_spec = new (arg_t, .name = "key", .type = table->key_type); + return Texts("Table$get_optional(", self, ", ", compile_type(table->key_type), ", ", + compile_type(table->value_type), ", ", compile_arguments(env, ast, arg_spec, call->args), + ", ", "_, ", optional_into_nonnone(table->value_type, Text("(*_)")), ", ", + compile_none(table->value_type), ", ", compile_type_info(self_value_t), ")"); } else if (streq(call->name, "get_or_set")) { self = compile_to_pointer_depth(env, call->self, 1, false); - arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type, - .next=new(arg_t, .name="default", .type=table->value_type, .default_val=table->default_value)); - return Texts("*Table$get_or_setdefault(", - self, ", ", compile_type(table->key_type), ", ", - compile_type(table->value_type), ", ", - compile_arguments(env, ast, arg_spec, call->args), ", ", - compile_type_info(self_value_t), ")"); + arg_t *arg_spec = new (arg_t, .name = "key", .type = table->key_type, + .next = new (arg_t, .name = "default", .type = table->value_type, + .default_val = table->default_value)); + return Texts("*Table$get_or_setdefault(", self, ", ", compile_type(table->key_type), ", ", + compile_type(table->value_type), ", ", compile_arguments(env, ast, arg_spec, call->args), + ", ", compile_type_info(self_value_t), ")"); } else if (streq(call->name, "has")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type); + arg_t *arg_spec = new (arg_t, .name = "key", .type = table->key_type); return Texts("Table$has_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - compile_type_info(self_value_t), ")"); + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "set")) { EXPECT_POINTER("a", "table"); - arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type, - .next=new(arg_t, .name="value", .type=table->value_type)); + arg_t *arg_spec = new (arg_t, .name = "key", .type = table->key_type, + .next = new (arg_t, .name = "value", .type = table->value_type)); return Texts("Table$set_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - compile_type_info(self_value_t), ")"); + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "remove")) { EXPECT_POINTER("a", "table"); - arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type); + arg_t *arg_spec = new (arg_t, .name = "key", .type = table->key_type); return Texts("Table$remove_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - compile_type_info(self_value_t), ")"); + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "clear")) { EXPECT_POINTER("a", "table"); (void)compile_arguments(env, ast, NULL, call->args); @@ -3477,14 +3445,15 @@ Text_t compile(env_t *env, ast_t *ast) return Texts("Table$sorted(", self, ", ", compile_type_info(self_value_t), ")"); } else if (streq(call->name, "with_fallback")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="fallback", .type=Type(OptionalType, self_value_t)); - return Texts("Table$with_fallback(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")"); + arg_t *arg_spec = new (arg_t, .name = "fallback", .type = Type(OptionalType, self_value_t)); + return Texts("Table$with_fallback(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), + ")"); } else code_err(ast, "There is no '", call->name, "' method for tables"); } default: { DeclareMatch(methodcall, ast, MethodCall); type_t *fn_t = get_method_type(env, methodcall->self, methodcall->name); - arg_ast_t *args = new(arg_ast_t, .value=methodcall->self, .next=methodcall->args); + arg_ast_t *args = new (arg_ast_t, .value = methodcall->self, .next = methodcall->args); binding_t *b = get_namespace_binding(env, methodcall->self, methodcall->name); if (!b) code_err(ast, "No such method"); return Texts(b->code, "(", compile_arguments(env, ast, Match(fn_t, FunctionType)->args, args), ")"); @@ -3501,14 +3470,15 @@ Text_t compile(env_t *env, ast_t *ast) } else if (fn_t->tag == TypeInfoType) { type_t *t = Match(fn_t, TypeInfoType)->type; - // Literal constructors for numeric types like `Byte(123)` should not go through any conversion, just a cast: + // Literal constructors for numeric types like `Byte(123)` should + // not go through any conversion, just a cast: if (is_numeric_type(t) && call->args && !call->args->next && call->args->value->tag == Int) return compile_to_type(env, call->args->value, t); else if (t->tag == NumType && call->args && !call->args->next && call->args->value->tag == Num) return compile_to_type(env, call->args->value, t); - binding_t *constructor = get_constructor(env, t, call->args, - env->current_type != NULL && type_eq(env->current_type, t)); + binding_t *constructor = + get_constructor(env, t, call->args, env->current_type != NULL && type_eq(env->current_type, t)); if (constructor) { arg_t *arg_spec = Match(constructor->type, FunctionType)->args; return Texts(constructor->code, "(", compile_arguments(env, ast, arg_spec, call->args), ")"); @@ -3518,49 +3488,61 @@ Text_t compile(env_t *env, ast_t *ast) if (t->tag == TextType) { if (!call->args) code_err(ast, "This constructor needs a value"); if (!type_eq(t, TEXT_TYPE)) - code_err(call->fn, "I don't have a constructor defined for these arguments"); + code_err(call->fn, "I don't have a constructor defined for " + "these arguments"); // Text constructor: - if (!call->args || call->args->next) - code_err(call->fn, "This constructor takes exactly 1 argument"); - if (type_eq(actual, t)) - return compile(env, call->args->value); + if (!call->args || call->args->next) code_err(call->fn, "This constructor takes exactly 1 argument"); + if (type_eq(actual, t)) return compile(env, call->args->value); return expr_as_text(compile(env, call->args->value), actual, Text("no")); } else if (t->tag == CStringType) { // C String constructor: - if (!call->args || call->args->next) - code_err(call->fn, "This constructor takes exactly 1 argument"); + if (!call->args || call->args->next) code_err(call->fn, "This constructor takes exactly 1 argument"); if (call->args->value->tag == TextLiteral) return compile_text_literal(Match(call->args->value, TextLiteral)->text); else if (call->args->value->tag == TextJoin && Match(call->args->value, TextJoin)->children == NULL) return Text("\"\""); - else if (call->args->value->tag == TextJoin && Match(call->args->value, TextJoin)->children->next == NULL) - return compile_text_literal(Match(Match(call->args->value, TextJoin)->children->ast, TextLiteral)->text); - return Texts("Text$as_c_string(", expr_as_text(compile(env, call->args->value), actual, Text("no")), ")"); + else if (call->args->value->tag == TextJoin + && Match(call->args->value, TextJoin)->children->next == NULL) + return compile_text_literal( + Match(Match(call->args->value, TextJoin)->children->ast, TextLiteral)->text); + return Texts("Text$as_c_string(", expr_as_text(compile(env, call->args->value), actual, Text("no")), + ")"); } else if (t->tag == StructType) { DeclareMatch(struct_, t, StructType); if (!struct_->opaque && is_valid_call(env, struct_->fields, call->args, true)) { if (env->current_type == NULL || !type_eq(env->current_type, t)) { for (arg_t *field = struct_->fields; field; field = field->next) { if (field->name[0] == '_') - code_err(ast, "This struct can't be initialized directly because it has private fields (starting with underscore).\n" - "Use a `convert` or `func` to instantiate the type instead."); + code_err(ast, "This struct can't be " + "initialized directly because it " + "has private fields (starting " + "with underscore).\n" + "Use a `convert` or `func` to " + "instantiate the type " + "instead."); } } - return Texts("((", compile_type(t), "){", - compile_arguments(env, ast, struct_->fields, call->args), "})"); + return Texts("((", compile_type(t), "){", compile_arguments(env, ast, struct_->fields, call->args), + "})"); } } - code_err(ast, "I could not find a constructor matching these arguments for ", type_to_str(t)); + code_err(ast, + "I could not find a constructor matching these arguments " + "for ", + type_to_str(t)); } else if (fn_t->tag == ClosureType) { fn_t = Match(fn_t, ClosureType)->fn; arg_t *type_args = Match(fn_t, FunctionType)->args; arg_t *closure_fn_args = NULL; for (arg_t *arg = Match(fn_t, FunctionType)->args; arg; arg = arg->next) - closure_fn_args = new(arg_t, .name=arg->name, .type=arg->type, .default_val=arg->default_val, .next=closure_fn_args); - closure_fn_args = new(arg_t, .name="userdata", .type=Type(PointerType, .pointed=Type(MemoryType)), .next=closure_fn_args); + closure_fn_args = new (arg_t, .name = arg->name, .type = arg->type, .default_val = arg->default_val, + .next = closure_fn_args); + closure_fn_args = new (arg_t, .name = "userdata", .type = Type(PointerType, .pointed = Type(MemoryType)), + .next = closure_fn_args); REVERSE_LIST(closure_fn_args); - Text_t fn_type_code = compile_type(Type(FunctionType, .args=closure_fn_args, .ret=Match(fn_t, FunctionType)->ret)); + Text_t fn_type_code = + compile_type(Type(FunctionType, .args = closure_fn_args, .ret = Match(fn_t, FunctionType)->ret)); Text_t closure = compile(env, call->fn); Text_t arg_code = compile_arguments(env, ast, type_args, call->args); @@ -3568,8 +3550,8 @@ Text_t compile(env_t *env, ast_t *ast) if (call->fn->tag == Var) { return Texts("((", fn_type_code, ")", closure, ".fn)(", arg_code, closure, ".userdata)"); } else { - return Texts("({ Closure_t closure = ", closure, "; ((", fn_type_code, ")closure.fn)(", - arg_code, "closure.userdata); })"); + return Texts("({ Closure_t closure = ", closure, "; ((", fn_type_code, ")closure.fn)(", arg_code, + "closure.userdata); })"); } } else { code_err(call->fn, "This is not a function, it's a ", type_to_str(fn_t)); @@ -3581,27 +3563,30 @@ Text_t compile(env_t *env, ast_t *ast) if (!type_eq(value_type, Type(ListType, Type(ByteType)))) code_err(value, "This value should be a list of bytes, not a ", type_to_str(value_type)); type_t *t = parse_type_ast(env, Match(ast, Deserialize)->type); - return Texts("({ ", compile_declaration(t, Text("deserialized")), ";\n" - "generic_deserialize(", compile(env, value), ", &deserialized, ", compile_type_info(t), ");\n" - "deserialized; })"); + return Texts("({ ", compile_declaration(t, Text("deserialized")), + ";\n" + "generic_deserialize(", + compile(env, value), ", &deserialized, ", compile_type_info(t), + ");\n" + "deserialized; })"); } case ExplicitlyTyped: { return compile_to_type(env, Match(ast, ExplicitlyTyped)->ast, get_type(env, ast)); } case When: { DeclareMatch(original, ast, When); - ast_t *when_var = WrapAST(ast, Var, .name="when"); + ast_t *when_var = WrapAST(ast, Var, .name = "when"); when_clause_t *new_clauses = NULL; type_t *subject_t = get_type(env, original->subject); for (when_clause_t *clause = original->clauses; clause; clause = clause->next) { type_t *clause_type = get_clause_type(env, subject_t, clause); if (clause_type->tag == AbortType || clause_type->tag == ReturnType) { - new_clauses = new(when_clause_t, .pattern=clause->pattern, .body=clause->body, .next=new_clauses); + new_clauses = + new (when_clause_t, .pattern = clause->pattern, .body = clause->body, .next = new_clauses); } else { - ast_t *assign = WrapAST(clause->body, Assign, - .targets=new(ast_list_t, .ast=when_var), - .values=new(ast_list_t, .ast=clause->body)); - new_clauses = new(when_clause_t, .pattern=clause->pattern, .body=assign, .next=new_clauses); + ast_t *assign = WrapAST(clause->body, Assign, .targets = new (ast_list_t, .ast = when_var), + .values = new (ast_list_t, .ast = clause->body)); + new_clauses = new (when_clause_t, .pattern = clause->pattern, .body = assign, .next = new_clauses); } } REVERSE_LIST(new_clauses); @@ -3609,19 +3594,18 @@ Text_t compile(env_t *env, ast_t *ast) if (else_body) { type_t *clause_type = get_type(env, else_body); if (clause_type->tag != AbortType && clause_type->tag != ReturnType) { - else_body = WrapAST(else_body, Assign, - .targets=new(ast_list_t, .ast=when_var), - .values=new(ast_list_t, .ast=else_body)); + else_body = WrapAST(else_body, Assign, .targets = new (ast_list_t, .ast = when_var), + .values = new (ast_list_t, .ast = else_body)); } } type_t *t = get_type(env, ast); env_t *when_env = fresh_scope(env); set_binding(when_env, "when", t, Text("when")); - return Texts( - "({ ", compile_declaration(t, Text("when")), ";\n", - compile_statement(when_env, WrapAST(ast, When, .subject=original->subject, .clauses=new_clauses, .else_body=else_body)), - "when; })"); + return Texts("({ ", compile_declaration(t, Text("when")), ";\n", + compile_statement(when_env, WrapAST(ast, When, .subject = original->subject, + .clauses = new_clauses, .else_body = else_body)), + "when; })"); } case If: { DeclareMatch(if_, ast, If); @@ -3632,32 +3616,31 @@ Text_t compile(env_t *env, ast_t *ast) Text_t condition_code; if (condition->tag == Declare) { DeclareMatch(decl, condition, Declare); - if (decl->value == NULL) - code_err(condition, "This declaration must have a value"); - type_t *condition_type = - decl->type ? parse_type_ast(env, decl->type) - : get_type(env, Match(condition, Declare)->value); + if (decl->value == NULL) code_err(condition, "This declaration must have a value"); + type_t *condition_type = + decl->type ? parse_type_ast(env, decl->type) : get_type(env, Match(condition, Declare)->value); if (condition_type->tag != OptionalType) - code_err(condition, "This `if var := ...:` declaration should be an optional type, not ", type_to_str(condition_type)); + code_err(condition, + "This `if var := ...:` declaration should be an " + "optional " + "type, not ", + type_to_str(condition_type)); - if (is_incomplete_type(condition_type)) - code_err(condition, "This type is incomplete!"); + if (is_incomplete_type(condition_type)) code_err(condition, "This type is incomplete!"); decl_code = compile_statement(env, condition); ast_t *var = Match(condition, Declare)->var; truthy_scope = fresh_scope(env); bind_statement(truthy_scope, condition); condition_code = compile_condition(truthy_scope, var); - set_binding(truthy_scope, Match(var, Var)->name, - Match(condition_type, OptionalType)->type, + set_binding(truthy_scope, Match(var, Var)->name, Match(condition_type, OptionalType)->type, optional_into_nonnone(condition_type, compile(truthy_scope, var))); } else if (condition->tag == Var) { type_t *condition_type = get_type(env, condition); condition_code = compile_condition(env, condition); if (condition_type->tag == OptionalType) { truthy_scope = fresh_scope(env); - set_binding(truthy_scope, Match(condition, Var)->name, - Match(condition_type, OptionalType)->type, + set_binding(truthy_scope, Match(condition, Var)->name, Match(condition_type, OptionalType)->type, optional_into_nonnone(condition_type, compile(truthy_scope, condition))); } } else { @@ -3668,16 +3651,17 @@ Text_t compile(env_t *env, ast_t *ast) type_t *false_type = get_type(falsey_scope, if_->else_body); if (true_type->tag == AbortType || true_type->tag == ReturnType) return Texts("({ ", decl_code, "if (", condition_code, ") ", compile_statement(truthy_scope, if_->body), - "\n", compile(falsey_scope, if_->else_body), "; })"); + "\n", compile(falsey_scope, if_->else_body), "; })"); else if (false_type->tag == AbortType || false_type->tag == ReturnType) - return Texts("({ ", decl_code, "if (!(", condition_code, ")) ", compile_statement(falsey_scope, if_->else_body), - "\n", compile(truthy_scope, if_->body), "; })"); + return Texts("({ ", decl_code, "if (!(", condition_code, ")) ", + compile_statement(falsey_scope, if_->else_body), "\n", compile(truthy_scope, if_->body), + "; })"); else if (decl_code.length > 0) return Texts("({ ", decl_code, "(", condition_code, ") ? ", compile(truthy_scope, if_->body), " : ", - compile(falsey_scope, if_->else_body), ";})"); + compile(falsey_scope, if_->else_body), ";})"); else - return Texts("((", condition_code, ") ? ", - compile(truthy_scope, if_->body), " : ", compile(falsey_scope, if_->else_body), ")"); + return Texts("((", condition_code, ") ? ", compile(truthy_scope, if_->body), " : ", + compile(falsey_scope, if_->else_body), ")"); } case Reduction: { DeclareMatch(reduction, ast, Reduction); @@ -3685,14 +3669,16 @@ Text_t compile(env_t *env, ast_t *ast) type_t *iter_t = get_type(env, reduction->iter); type_t *item_t = get_iterated_type(iter_t); - if (!item_t) code_err(reduction->iter, "I couldn't figure out how to iterate over this type: ", type_to_str(iter_t)); + if (!item_t) + code_err(reduction->iter, "I couldn't figure out how to iterate over this type: ", type_to_str(iter_t)); static int64_t next_id = 1; ast_t *item = FakeAST(Var, String("$it", next_id++)); ast_t *body = LiteralCode(Text("{}")); // placeholder - ast_t *loop = FakeAST(For, .vars=new(ast_list_t, .ast=item), .iter=reduction->iter, .body=body); + ast_t *loop = FakeAST(For, .vars = new (ast_list_t, .ast = item), .iter = reduction->iter, .body = body); env_t *body_scope = for_scope(env, loop); - if (op == Equals || op == NotEquals || op == LessThan || op == LessThanOrEquals || op == GreaterThan || op == GreaterThanOrEquals) { + if (op == Equals || op == NotEquals || op == LessThan || op == LessThanOrEquals || op == GreaterThan + || op == GreaterThanOrEquals) { // Chained comparisons like ==, <, etc. type_t *item_value_type = item_t; ast_t *item_value = item; @@ -3702,36 +3688,34 @@ Text_t compile(env_t *env, ast_t *ast) item_value_type = get_type(body_scope, reduction->key); } - Text_t code = Texts( - "({ // Reduction:\n", - compile_declaration(item_value_type, Text("prev")), ";\n" - "OptionalBool_t result = NONE_BOOL;\n" - ); - - ast_t *comparison = new(ast_t, .file=ast->file, .start=ast->start, .end=ast->end, - .tag=op, .__data.Plus.lhs=LiteralCode(Text("prev"), .type=item_value_type), .__data.Plus.rhs=item_value); - body->__data.InlineCCode.chunks = new(ast_list_t, .ast=FakeAST(TextLiteral, Texts( - "if (result == NONE_BOOL) {\n" - " prev = ", compile(body_scope, item_value), ";\n" - " result = yes;\n" - "} else {\n" - " if (", compile(body_scope, comparison), ") {\n", - " prev = ", compile(body_scope, item_value), ";\n", - " } else {\n" - " result = no;\n", - " break;\n", - " }\n", - "}\n"))); + Text_t code = Texts("({ // Reduction:\n", compile_declaration(item_value_type, Text("prev")), + ";\n" + "OptionalBool_t result = NONE_BOOL;\n"); + + ast_t *comparison = new (ast_t, .file = ast->file, .start = ast->start, .end = ast->end, .tag = op, + .__data.Plus.lhs = LiteralCode(Text("prev"), .type = item_value_type), + .__data.Plus.rhs = item_value); + body->__data.InlineCCode.chunks = new ( + ast_list_t, .ast = FakeAST(TextLiteral, Texts("if (result == NONE_BOOL) {\n" + " prev = ", + compile(body_scope, item_value), + ";\n" + " result = yes;\n" + "} else {\n" + " if (", + compile(body_scope, comparison), ") {\n", + " prev = ", compile(body_scope, item_value), ";\n", + " } else {\n" + " result = no;\n", + " break;\n", " }\n", "}\n"))); code = Texts(code, compile_statement(env, loop), "\nresult;})"); return code; } else if (op == Min || op == Max) { // Min/max: Text_t superlative = op == Min ? Text("min") : Text("max"); - Text_t code = Texts( - "({ // Reduction:\n", - compile_declaration(item_t, superlative), ";\n" - "Bool_t has_value = no;\n" - ); + Text_t code = Texts("({ // Reduction:\n", compile_declaration(item_t, superlative), + ";\n" + "Bool_t has_value = no;\n"); Text_t item_code = compile(body_scope, item); ast_e cmp_op = op == Min ? LessThan : GreaterThan; @@ -3742,31 +3726,39 @@ Text_t compile(env_t *env, ast_t *ast) Text_t superlative_key = op == Min ? Text("min_key") : Text("max_key"); code = Texts(code, compile_declaration(key_type, superlative_key), ";\n"); - ast_t *comparison = new(ast_t, .file=ast->file, .start=ast->start, .end=ast->end, - .tag=cmp_op, .__data.Plus.lhs=LiteralCode(Text("key"), .type=key_type), - .__data.Plus.rhs=LiteralCode(superlative_key, .type=key_type)); - - body->__data.InlineCCode.chunks = new(ast_list_t, .ast=FakeAST(TextLiteral, Texts( - compile_declaration(key_type, Text("key")), " = ", compile(key_scope, reduction->key), ";\n", - "if (!has_value || ", compile(body_scope, comparison), ") {\n" - " ", superlative, " = ", compile(body_scope, item), ";\n" - " ", superlative_key, " = key;\n" - " has_value = yes;\n" - "}\n"))); + ast_t *comparison = new (ast_t, .file = ast->file, .start = ast->start, .end = ast->end, .tag = cmp_op, + .__data.Plus.lhs = LiteralCode(Text("key"), .type = key_type), + .__data.Plus.rhs = LiteralCode(superlative_key, .type = key_type)); + + body->__data.InlineCCode.chunks = new ( + ast_list_t, .ast = FakeAST(TextLiteral, Texts(compile_declaration(key_type, Text("key")), " = ", + compile(key_scope, reduction->key), ";\n", + "if (!has_value || ", compile(body_scope, comparison), + ") {\n" + " ", + superlative, " = ", compile(body_scope, item), + ";\n" + " ", + superlative_key, + " = key;\n" + " has_value = yes;\n" + "}\n"))); } else { - ast_t *comparison = new(ast_t, .file=ast->file, .start=ast->start, .end=ast->end, - .tag=cmp_op, .__data.Plus.lhs=item, - .__data.Plus.rhs=LiteralCode(superlative, .type=item_t)); - body->__data.InlineCCode.chunks = new(ast_list_t, .ast=FakeAST(TextLiteral, Texts( - "if (!has_value || ", compile(body_scope, comparison), ") {\n" - " ", superlative, " = ", compile(body_scope, item), ";\n" - " has_value = yes;\n" - "}\n"))); + ast_t *comparison = + new (ast_t, .file = ast->file, .start = ast->start, .end = ast->end, .tag = cmp_op, + .__data.Plus.lhs = item, .__data.Plus.rhs = LiteralCode(superlative, .type = item_t)); + body->__data.InlineCCode.chunks = new ( + ast_list_t, .ast = FakeAST(TextLiteral, Texts("if (!has_value || ", compile(body_scope, comparison), + ") {\n" + " ", + superlative, " = ", compile(body_scope, item), + ";\n" + " has_value = yes;\n" + "}\n"))); } - code = Texts(code, compile_statement(env, loop), "\nhas_value ? ", promote_to_optional(item_t, superlative), - " : ", compile_none(item_t), ";})"); + " : ", compile_none(item_t), ";})"); return code; } else { // Accumulator-style reductions like +, ++, *, etc. @@ -3777,43 +3769,44 @@ Text_t compile(env_t *env, ast_t *ast) item_value = reduction->key; } - Text_t code = Texts( - "({ // Reduction:\n", - compile_declaration(reduction_type, Text("reduction")), ";\n" - "Bool_t has_value = no;\n" - ); + Text_t code = Texts("({ // Reduction:\n", compile_declaration(reduction_type, Text("reduction")), + ";\n" + "Bool_t has_value = no;\n"); - // For the special case of (or)/(and), we need to early out if we can: + // For the special case of (or)/(and), we need to early out if we + // can: Text_t early_out = EMPTY_TEXT; if (op == Compare) { if (reduction_type->tag != IntType || Match(reduction_type, IntType)->bits != TYPE_IBITS32) - code_err(ast, "<> reductions are only supported for Int32 values"); + code_err(ast, "<> reductions are only supported for Int32 " + "values"); } else if (op == And) { - if (reduction_type->tag == BoolType) - early_out = Text("if (!reduction) break;"); + if (reduction_type->tag == BoolType) early_out = Text("if (!reduction) break;"); else if (reduction_type->tag == OptionalType) early_out = Texts("if (", check_none(reduction_type, Text("reduction")), ") break;"); } else if (op == Or) { - if (reduction_type->tag == BoolType) - early_out = Text("if (reduction) break;"); + if (reduction_type->tag == BoolType) early_out = Text("if (reduction) break;"); else if (reduction_type->tag == OptionalType) early_out = Texts("if (!", check_none(reduction_type, Text("reduction")), ") break;"); } - ast_t *combination = new(ast_t, .file=ast->file, .start=ast->start, .end=ast->end, - .tag=op, .__data.Plus.lhs=LiteralCode(Text("reduction"), .type=reduction_type), - .__data.Plus.rhs=item_value); - body->__data.InlineCCode.chunks = new(ast_list_t, .ast=FakeAST(TextLiteral, Texts( - "if (!has_value) {\n" - " reduction = ", compile(body_scope, item_value), ";\n" - " has_value = yes;\n" - "} else {\n" - " reduction = ", compile(body_scope, combination), ";\n", - early_out, - "}\n"))); - - code = Texts(code, compile_statement(env, loop), "\nhas_value ? ", promote_to_optional(reduction_type, Text("reduction")), - " : ", compile_none(reduction_type), ";})"); + ast_t *combination = new (ast_t, .file = ast->file, .start = ast->start, .end = ast->end, .tag = op, + .__data.Plus.lhs = LiteralCode(Text("reduction"), .type = reduction_type), + .__data.Plus.rhs = item_value); + body->__data.InlineCCode.chunks = + new (ast_list_t, + .ast = FakeAST(TextLiteral, Texts("if (!has_value) {\n" + " reduction = ", + compile(body_scope, item_value), + ";\n" + " has_value = yes;\n" + "} else {\n" + " reduction = ", + compile(body_scope, combination), ";\n", early_out, "}\n"))); + + code = Texts(code, compile_statement(env, loop), "\nhas_value ? ", + promote_to_optional(reduction_type, Text("reduction")), " : ", compile_none(reduction_type), + ";})"); return code; } } @@ -3826,7 +3819,9 @@ Text_t compile(env_t *env, ast_t *ast) DeclareMatch(info, value_t, TypeInfoType); if (f->field[0] == '_') { if (!type_eq(env->current_type, info->type)) - code_err(ast, "Fields that start with underscores are not accessible on types outside of the type definition."); + code_err(ast, "Fields that start with underscores are not " + "accessible " + "on types outside of the type definition."); } binding_t *b = get_binding(info->env, f->field); if (!b) code_err(ast, "I couldn't find the field '", f->field, "' on this type"); @@ -3834,7 +3829,7 @@ Text_t compile(env_t *env, ast_t *ast) return b->code; } case TextType: { - const char *lang = Match(value_t, TextType)->lang; + const char *lang = Match(value_t, TextType)->lang; if (lang && streq(f->field, "text")) { Text_t text = compile_to_pointer_depth(env, f->fielded, 0, false); return Texts("((Text_t)", text, ")"); @@ -3885,24 +3880,31 @@ Text_t compile(env_t *env, ast_t *ast) if (streq(f->field, "items")) return Texts("LIST_COPY((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries)"); else if (streq(f->field, "length")) - return Texts("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries.length)"); + return Texts("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), + ").entries.length)"); code_err(ast, "There is no '", f->field, "' field on sets"); } case TableType: { if (streq(f->field, "length")) { - return Texts("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries.length)"); + return Texts("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), + ").entries.length)"); } else if (streq(f->field, "keys")) { return Texts("LIST_COPY((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries)"); } else if (streq(f->field, "values")) { DeclareMatch(table, value_t, TableType); - Text_t offset = Texts("offsetof(struct { ", compile_declaration(table->key_type, Text("k")), "; ", compile_declaration(table->value_type, Text("v")), "; }, v)"); - return Texts("({ List_t *entries = &(", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries;\n" - "LIST_INCREF(*entries);\n" - "List_t values = *entries;\n" - "values.data += ", offset, ";\n" - "values; })"); + Text_t offset = Texts("offsetof(struct { ", compile_declaration(table->key_type, Text("k")), "; ", + compile_declaration(table->value_type, Text("v")), "; }, v)"); + return Texts("({ List_t *entries = &(", compile_to_pointer_depth(env, f->fielded, 0, false), + ").entries;\n" + "LIST_INCREF(*entries);\n" + "List_t values = *entries;\n" + "values.data += ", + offset, + ";\n" + "values; })"); } else if (streq(f->field, "fallback")) { - return Texts("({ Table_t *_fallback = (", compile_to_pointer_depth(env, f->fielded, 0, false), ").fallback; _fallback ? *_fallback : NONE_TABLE; })"); + return Texts("({ Table_t *_fallback = (", compile_to_pointer_depth(env, f->fielded, 0, false), + ").fallback; _fallback ? *_fallback : NONE_TABLE; })"); } code_err(ast, "There is no '", f->field, "' field on tables"); } @@ -3911,8 +3913,7 @@ Text_t compile(env_t *env, ast_t *ast) env_t *module_env = Table$str_get(*env->imports, name); return compile(module_env, WrapAST(ast, Var, f->field)); } - default: - code_err(ast, "Field accesses are not supported on ", type_to_str(fielded_t), " values"); + default: code_err(ast, "Field accesses are not supported on ", type_to_str(fielded_t), " values"); } } case Index: { @@ -3920,7 +3921,9 @@ Text_t compile(env_t *env, ast_t *ast) type_t *indexed_type = get_type(env, indexing->indexed); if (!indexing->index) { if (indexed_type->tag != PointerType) - code_err(ast, "Only pointers can use the '[]' operator to dereference the entire value."); + code_err(ast, "Only pointers can use the '[]' operator to " + "dereference " + "the entire value."); DeclareMatch(ptr, indexed_type, PointerType); if (ptr->pointed->tag == ListType) { return Texts("*({ List_t *list = ", compile(env, indexing->indexed), "; LIST_INCREF(*list); list; })"); @@ -3939,77 +3942,90 @@ Text_t compile(env_t *env, ast_t *ast) type_t *item_type = Match(container_t, ListType)->item_type; Text_t list = compile_to_pointer_depth(env, indexing->indexed, 0, false); file_t *f = indexing->index->file; - Text_t index_code = indexing->index->tag == Int - ? compile_int_to_type(env, indexing->index, Type(IntType, .bits=TYPE_IBITS64)) - : (index_t->tag == BigIntType ? Texts("Int64$from_int(", compile(env, indexing->index), ", no)") - : Texts("(Int64_t)(", compile(env, indexing->index), ")")); + Text_t index_code = + indexing->index->tag == Int + ? compile_int_to_type(env, indexing->index, Type(IntType, .bits = TYPE_IBITS64)) + : (index_t->tag == BigIntType ? Texts("Int64$from_int(", compile(env, indexing->index), ", no)") + : Texts("(Int64_t)(", compile(env, indexing->index), ")")); if (indexing->unchecked) return Texts("List_get_unchecked(", compile_type(item_type), ", ", list, ", ", index_code, ")"); else return Texts("List_get(", compile_type(item_type), ", ", list, ", ", index_code, ", ", - String((int64_t)(indexing->index->start - f->text)), ", ", - String((int64_t)(indexing->index->end - f->text)), - ")"); + String((int64_t)(indexing->index->start - f->text)), ", ", + String((int64_t)(indexing->index->end - f->text)), ")"); } else if (container_t->tag == TableType) { DeclareMatch(table_type, container_t, TableType); - if (indexing->unchecked) - code_err(ast, "Table indexes cannot be unchecked"); + if (indexing->unchecked) code_err(ast, "Table indexes cannot be unchecked"); if (table_type->default_value) { - return Texts("Table$get_or_default(", - compile_to_pointer_depth(env, indexing->indexed, 0, false), ", ", - compile_type(table_type->key_type), ", ", - compile_type(table_type->value_type), ", ", - compile(env, indexing->index), ", ", - compile_to_type(env, table_type->default_value, table_type->value_type), ", ", - compile_type_info(container_t), ")"); + return Texts("Table$get_or_default(", compile_to_pointer_depth(env, indexing->indexed, 0, false), ", ", + compile_type(table_type->key_type), ", ", compile_type(table_type->value_type), ", ", + compile(env, indexing->index), ", ", + compile_to_type(env, table_type->default_value, table_type->value_type), ", ", + compile_type_info(container_t), ")"); } else { - return Texts("Table$get_optional(", - compile_to_pointer_depth(env, indexing->indexed, 0, false), ", ", - compile_type(table_type->key_type), ", ", - compile_type(table_type->value_type), ", ", - compile(env, indexing->index), ", " - "_, ", promote_to_optional(table_type->value_type, Text("(*_)")), ", ", - compile_none(table_type->value_type), ", ", - compile_type_info(container_t), ")"); + return Texts("Table$get_optional(", compile_to_pointer_depth(env, indexing->indexed, 0, false), ", ", + compile_type(table_type->key_type), ", ", compile_type(table_type->value_type), ", ", + compile(env, indexing->index), + ", " + "_, ", + promote_to_optional(table_type->value_type, Text("(*_)")), ", ", + compile_none(table_type->value_type), ", ", compile_type_info(container_t), ")"); } } else if (container_t->tag == TextType) { - return Texts("Text$cluster(", compile_to_pointer_depth(env, indexing->indexed, 0, false), ", ", compile_to_type(env, indexing->index, Type(BigIntType)), ")"); + return Texts("Text$cluster(", compile_to_pointer_depth(env, indexing->indexed, 0, false), ", ", + compile_to_type(env, indexing->index, Type(BigIntType)), ")"); } else { code_err(ast, "Indexing is not supported for type: ", type_to_str(container_t)); } } case InlineCCode: { type_t *t = get_type(env, ast); - if (t->tag == VoidType) - return Texts("{\n", compile_statement(env, ast), "\n}"); - else - return compile_statement(env, ast); + if (t->tag == VoidType) return Texts("{\n", compile_statement(env, ast), "\n}"); + else return compile_statement(env, ast); } case Use: code_err(ast, "Compiling 'use' as expression!"); case Defer: code_err(ast, "Compiling 'defer' as expression!"); case Extern: code_err(ast, "Externs are not supported as expressions"); case TableEntry: code_err(ast, "Table entries should not be compiled directly"); - case Declare: case Assign: case UPDATE_CASES: case For: case While: case Repeat: case StructDef: case LangDef: case Extend: - case EnumDef: case FunctionDef: case ConvertDef: case Skip: case Stop: case Pass: case Return: case DocTest: case Assert: - code_err(ast, "This is not a valid expression"); - default: case Unknown: code_err(ast, "Unknown AST: ", ast_to_sexp_str(ast)); + case Declare: + case Assign: + case UPDATE_CASES: + case For: + case While: + case Repeat: + case StructDef: + case LangDef: + case Extend: + case EnumDef: + case FunctionDef: + case ConvertDef: + case Skip: + case Stop: + case Pass: + case Return: + case DocTest: + case Assert: code_err(ast, "This is not a valid expression"); + default: + case Unknown: code_err(ast, "Unknown AST: ", ast_to_sexp_str(ast)); } return EMPTY_TEXT; } -Text_t compile_type_info(type_t *t) -{ +Text_t compile_type_info(type_t *t) { if (t == NULL) compiler_err(NULL, NULL, NULL, "Attempt to compile a NULL type"); if (t == PATH_TYPE) return Text("&Path$info"); else if (t == PATH_TYPE_TYPE) return Text("&PathType$info"); switch (t->tag) { - case BoolType: case ByteType: case IntType: case BigIntType: case NumType: case CStringType: - return Texts("&", type_to_text(t), "$info"); + case BoolType: + case ByteType: + case IntType: + case BigIntType: + case NumType: + case CStringType: return Texts("&", type_to_text(t), "$info"); case TextType: { DeclareMatch(text, t, TextType); - if (!text->lang || streq(text->lang, "Text")) - return Text("&Text$info"); + if (!text->lang || streq(text->lang, "Text")) return Text("&Text$info"); return Texts("(&", namespace_name(text->env, text->env->namespace, Text("$info")), ")"); } case StructType: { @@ -4047,20 +4063,18 @@ Text_t compile_type_info(type_t *t) } case OptionalType: { type_t *non_optional = Match(t, OptionalType)->type; - return Texts("Optional$info(sizeof(", compile_type(non_optional), - "), __alignof__(", compile_type(non_optional), "), ", compile_type_info(non_optional), ")"); + return Texts("Optional$info(sizeof(", compile_type(non_optional), "), __alignof__(", compile_type(non_optional), + "), ", compile_type_info(non_optional), ")"); } case TypeInfoType: return Texts("Type$info(", quoted_text(type_to_text(Match(t, TypeInfoType)->type)), ")"); case MemoryType: return Text("&Memory$info"); case VoidType: return Text("&Void$info"); - default: - compiler_err(NULL, 0, 0, "I couldn't convert to a type info: ", type_to_str(t)); + default: compiler_err(NULL, 0, 0, "I couldn't convert to a type info: ", type_to_str(t)); } return EMPTY_TEXT; } -static Text_t get_flag_options(type_t *t, const char *separator) -{ +static Text_t get_flag_options(type_t *t, const char *separator) { if (t->tag == BoolType) { return Text("yes|no"); } else if (t->tag == EnumType) { @@ -4077,8 +4091,7 @@ static Text_t get_flag_options(type_t *t, const char *separator) } } -Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const char *version) -{ +Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const char *version) { DeclareMatch(fn_info, fn_type, FunctionType); env_t *main_env = fresh_scope(env); @@ -4106,39 +4119,32 @@ Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const c if (strlen(arg->name) == 1) { if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType)) usage = Texts(usage, "[-", flag, "]"); - else - usage = Texts(usage, "[-", flag, " ", get_flag_options(t, "|"), "]"); + else usage = Texts(usage, "[-", flag, " ", get_flag_options(t, "|"), "]"); } else { if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType)) usage = Texts(usage, "[--", flag, "]"); - else if (t->tag == ListType) - usage = Texts(usage, "[--", flag, " ", get_flag_options(t, "|"), "]"); - else - usage = Texts(usage, "[--", flag, "=", get_flag_options(t, "|"), "]"); + else if (t->tag == ListType) usage = Texts(usage, "[--", flag, " ", get_flag_options(t, "|"), "]"); + else usage = Texts(usage, "[--", flag, "=", get_flag_options(t, "|"), "]"); } } else { - if (t->tag == BoolType) - usage = Texts(usage, "<--", flag, "|--no-", flag, ">"); - else if (t->tag == EnumType) - usage = Texts(usage, get_flag_options(t, "|")); - else if (t->tag == ListType) - usage = Texts(usage, "[", flag, "...]"); - else - usage = Texts(usage, "<", flag, ">"); + if (t->tag == BoolType) usage = Texts(usage, "<--", flag, "|--no-", flag, ">"); + else if (t->tag == EnumType) usage = Texts(usage, get_flag_options(t, "|")); + else if (t->tag == ListType) usage = Texts(usage, "[", flag, "...]"); + else usage = Texts(usage, "<", flag, ">"); } } - code = Texts(code, "Text_t usage = Texts(Text(\"Usage: \"), Text$from_str(argv[0])", - usage.length == 0 ? EMPTY_TEXT : Texts(", Text(", quoted_text(usage), ")"), ");\n"); + code = Texts(code, + "Text_t usage = Texts(Text(\"Usage: \"), " + "Text$from_str(argv[0])", + usage.length == 0 ? EMPTY_TEXT : Texts(", Text(", quoted_text(usage), ")"), ");\n"); } - for (arg_t *arg = fn_info->args; arg; arg = arg->next) { - type_t *opt_type = arg->type->tag == OptionalType ? arg->type : Type(OptionalType, .type=arg->type); + type_t *opt_type = arg->type->tag == OptionalType ? arg->type : Type(OptionalType, .type = arg->type); code = Texts(code, compile_declaration(opt_type, Texts("_$", arg->name))); if (arg->default_val) { Text_t default_val = compile(env, arg->default_val); - if (arg->type->tag != OptionalType) - default_val = promote_to_optional(arg->type, default_val); + if (arg->type->tag != OptionalType) default_val = promote_to_optional(arg->type, default_val); code = Texts(code, " = ", default_val); } else { code = Texts(code, " = ", compile_none(arg->type)); @@ -4150,17 +4156,15 @@ Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const c code = Texts(code, "tomo_parse_args(argc, argv, ", usage_code, ", ", help_code, ", ", version_code); for (arg_t *arg = fn_info->args; arg; arg = arg->next) { code = Texts(code, ",\n{", quoted_text(Text$replace(Text$from_str(arg->name), Text("_"), Text("-"))), ", ", - (arg->default_val || arg->type->tag == OptionalType) ? "false" : "true", ", ", - compile_type_info(arg->type), - ", &", Texts("_$", arg->name), "}"); + (arg->default_val || arg->type->tag == OptionalType) ? "false" : "true", ", ", + compile_type_info(arg->type), ", &", Texts("_$", arg->name), "}"); } code = Texts(code, ");\n"); code = Texts(code, fn_name, "("); for (arg_t *arg = fn_info->args; arg; arg = arg->next) { Text_t arg_code = Texts("_$", arg->name); - if (arg->type->tag != OptionalType) - arg_code = optional_into_nonnone(arg->type, arg_code); + if (arg->type->tag != OptionalType) arg_code = optional_into_nonnone(arg->type, arg_code); code = Texts(code, arg_code); if (arg->next) code = Texts(code, ", "); @@ -4169,8 +4173,7 @@ Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const c return code; } -Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *staticdefs) -{ +Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *staticdefs) { bool is_private = false; const char *function_name; arg_ast_t *args; @@ -4193,7 +4196,10 @@ Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *static ret_t = convertdef->ret_type ? parse_type_ast(env, convertdef->ret_type) : Type(VoidType); function_name = get_type_name(ret_t); if (!function_name) - code_err(ast, "Conversions are only supported for text, struct, and enum types, not ", type_to_str(ret_t)); + code_err(ast, + "Conversions are only supported for text, struct, and enum " + "types, not ", + type_to_str(ret_t)); body = convertdef->body; cache = convertdef->cache; is_inline = convertdef->is_inline; @@ -4212,21 +4218,17 @@ Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *static arg_signature = Texts(arg_signature, ")"); Text_t ret_type_code = compile_type(ret_t); - if (ret_t->tag == AbortType) - ret_type_code = Texts("__attribute__((noreturn)) _Noreturn ", ret_type_code); + if (ret_t->tag == AbortType) ret_type_code = Texts("__attribute__((noreturn)) _Noreturn ", ret_type_code); - if (is_private) - *staticdefs = Texts(*staticdefs, "static ", ret_type_code, " ", name_code, arg_signature, ";\n"); + if (is_private) *staticdefs = Texts(*staticdefs, "static ", ret_type_code, " ", name_code, arg_signature, ";\n"); Text_t code; if (cache) { code = Texts("static ", ret_type_code, " ", name_code, "$uncached", arg_signature); } else { code = Texts(ret_type_code, " ", name_code, arg_signature); - if (is_inline) - code = Texts("INLINE ", code); - if (!is_private) - code = Texts("public ", code); + if (is_inline) code = Texts("INLINE ", code); + if (!is_private) code = Texts("public ", code); } env_t *body_scope = fresh_scope(env); @@ -4245,72 +4247,92 @@ Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *static type_t *body_type = get_type(body_scope, body); if (ret_t->tag == AbortType) { - if (body_type->tag != AbortType) - code_err(ast, "This function can reach the end without aborting!"); + if (body_type->tag != AbortType) code_err(ast, "This function can reach the end without aborting!"); } else if (ret_t->tag == VoidType) { if (body_type->tag == AbortType) - code_err(ast, "This function will always abort before it reaches the end, but it's declared as having a Void return. It should be declared as an Abort return instead."); + code_err(ast, "This function will always abort before it reaches the " + "end, but it's declared as having a Void return. It should " + "be declared as an Abort return instead."); } else { if (body_type->tag != ReturnType && body_type->tag != AbortType) - code_err(ast, "This function looks like it can reach the end without returning a ", type_to_str(ret_t), " value! \n " - "If this is not the case, please add a call to `fail(\"Unreachable\")` at the end of the function to help the compiler out."); + code_err(ast, + "This function looks like it can reach the end without " + "returning a ", + type_to_str(ret_t), + " value! \n " + "If this is not the case, please add a call to " + "`fail(\"Unreachable\")` at the end of the function to " + "help the " + "compiler out."); } Text_t body_code = Texts("{\n", compile_inline_block(body_scope, body), "}\n"); Text_t definition = with_source_info(env, ast, Texts(code, " ", body_code, "\n")); if (cache && args == NULL) { // no-args cache just uses a static var - Text_t wrapper = Texts( - is_private ? EMPTY_TEXT : Text("public "), ret_type_code, " ", name_code, "(void) {\n" - "static ", compile_declaration(ret_t, Text("cached_result")), ";\n", - "static bool initialized = false;\n", - "if (!initialized) {\n" - "\tcached_result = ", name_code, "$uncached();\n", - "\tinitialized = true;\n", - "}\n", - "return cached_result;\n" - "}\n"); + Text_t wrapper = + Texts(is_private ? EMPTY_TEXT : Text("public "), ret_type_code, " ", name_code, + "(void) {\n" + "static ", + compile_declaration(ret_t, Text("cached_result")), ";\n", "static bool initialized = false;\n", + "if (!initialized) {\n" + "\tcached_result = ", + name_code, "$uncached();\n", "\tinitialized = true;\n", "}\n", + "return cached_result;\n" + "}\n"); definition = Texts(definition, wrapper); } else if (cache && cache->tag == Int) { assert(args); OptionalInt64_t cache_size = Int64$parse(Text$from_str(Match(cache, Int)->str), NULL); Text_t pop_code = EMPTY_TEXT; if (cache->tag == Int && !cache_size.is_none && cache_size.value > 0) { - // FIXME: this currently just deletes the first entry, but this should be more like a - // least-recently-used cache eviction policy or least-frequently-used + // FIXME: this currently just deletes the first entry, but this + // should be more like a least-recently-used cache eviction policy + // or least-frequently-used pop_code = Texts("if (cache.entries.length > ", String(cache_size.value), - ") Table$remove(&cache, cache.entries.data + cache.entries.stride*0, table_type);\n"); + ") Table$remove(&cache, cache.entries.data + " + "cache.entries.stride*0, table_type);\n"); } if (!args->next) { // Single-argument functions have simplified caching logic type_t *arg_type = get_arg_ast_type(env, args); - Text_t wrapper = Texts( - is_private ? EMPTY_TEXT : Text("public "), ret_type_code, " ", name_code, arg_signature, "{\n" - "static Table_t cache = {};\n", - "const TypeInfo_t *table_type = Table$info(", compile_type_info(arg_type), ", ", compile_type_info(ret_t), ");\n", - compile_declaration(Type(PointerType, .pointed=ret_t), Text("cached")), " = Table$get_raw(cache, &_$", args->name, ", table_type);\n" - "if (cached) return *cached;\n", - compile_declaration(ret_t, Text("ret")), " = ", name_code, "$uncached(_$", args->name, ");\n", - pop_code, - "Table$set(&cache, &_$", args->name, ", &ret, table_type);\n" - "return ret;\n" - "}\n"); + Text_t wrapper = + Texts(is_private ? EMPTY_TEXT : Text("public "), ret_type_code, " ", name_code, arg_signature, + "{\n" + "static Table_t cache = {};\n", + "const TypeInfo_t *table_type = Table$info(", compile_type_info(arg_type), ", ", + compile_type_info(ret_t), ");\n", + compile_declaration(Type(PointerType, .pointed = ret_t), Text("cached")), + " = Table$get_raw(cache, &_$", args->name, + ", table_type);\n" + "if (cached) return *cached;\n", + compile_declaration(ret_t, Text("ret")), " = ", name_code, "$uncached(_$", args->name, ");\n", + pop_code, "Table$set(&cache, &_$", args->name, + ", &ret, table_type);\n" + "return ret;\n" + "}\n"); definition = Texts(definition, wrapper); } else { - // Multi-argument functions use a custom struct type (only defined internally) as a cache key: + // Multi-argument functions use a custom struct type (only defined + // internally) as a cache key: arg_t *fields = NULL; for (arg_ast_t *arg = args; arg; arg = arg->next) - fields = new(arg_t, .name=arg->name, .type=get_arg_ast_type(env, arg), .next=fields); + fields = new (arg_t, .name = arg->name, .type = get_arg_ast_type(env, arg), .next = fields); REVERSE_LIST(fields); - type_t *t = Type(StructType, .name=String("func$", get_line_number(ast->file, ast->start), "$args"), .fields=fields, .env=env); + type_t *t = Type(StructType, .name = String("func$", get_line_number(ast->file, ast->start), "$args"), + .fields = fields, .env = env); int64_t num_fields = used_names.entries.length; const char *metamethods = is_packed_data(t) ? "PackedData$metamethods" : "Struct$metamethods"; - Text_t args_typeinfo = Texts("((TypeInfo_t[1]){{.size=sizeof(args), .align=__alignof__(args), .metamethods=", metamethods, - ", .tag=StructInfo, .StructInfo.name=\"FunctionArguments\", " - ".StructInfo.num_fields=", String(num_fields), - ", .StructInfo.fields=(NamedType_t[", String(num_fields), "]){"); + Text_t args_typeinfo = + Texts("((TypeInfo_t[1]){{.size=sizeof(args), " + ".align=__alignof__(args), .metamethods=", + metamethods, + ", .tag=StructInfo, " + ".StructInfo.name=\"FunctionArguments\", " + ".StructInfo.num_fields=", + String(num_fields), ", .StructInfo.fields=(NamedType_t[", String(num_fields), "]){"); Text_t args_type = Text("struct { "); for (arg_t *f = fields; f; f = f->next) { args_typeinfo = Texts(args_typeinfo, "{\"", f->name, "\", ", compile_type_info(f->type), "}"); @@ -4325,14 +4347,17 @@ Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *static all_args = Texts(all_args, "_$", arg->name, arg->next ? Text(", ") : EMPTY_TEXT); Text_t wrapper = Texts( - is_private ? EMPTY_TEXT : Text("public "), ret_type_code, " ", name_code, arg_signature, "{\n" + is_private ? EMPTY_TEXT : Text("public "), ret_type_code, " ", name_code, arg_signature, + "{\n" "static Table_t cache = {};\n", - args_type, " args = {", all_args, "};\n" - "const TypeInfo_t *table_type = Table$info(", args_typeinfo, ", ", compile_type_info(ret_t), ");\n", - compile_declaration(Type(PointerType, .pointed=ret_t), Text("cached")), " = Table$get_raw(cache, &args, table_type);\n" + args_type, " args = {", all_args, + "};\n" + "const TypeInfo_t *table_type = Table$info(", + args_typeinfo, ", ", compile_type_info(ret_t), ");\n", + compile_declaration(Type(PointerType, .pointed = ret_t), Text("cached")), + " = Table$get_raw(cache, &args, table_type);\n" "if (cached) return *cached;\n", - compile_declaration(ret_t, Text("ret")), " = ", name_code, "$uncached(", all_args, ");\n", - pop_code, + compile_declaration(ret_t, Text("ret")), " = ", name_code, "$uncached(", all_args, ");\n", pop_code, "Table$set(&cache, &args, &ret, table_type);\n" "return ret;\n" "}\n"); @@ -4348,22 +4373,22 @@ Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *static text = Texts(text, type_to_text(get_arg_ast_type(env, arg))); if (arg->next) text = Texts(text, ", "); } - if (ret_t && ret_t->tag != VoidType) - text = Texts(text, "->", type_to_text(ret_t)); + if (ret_t && ret_t->tag != VoidType) text = Texts(text, "->", type_to_text(ret_t)); text = Texts(text, ")"); return definition; } -Text_t compile_top_level_code(env_t *env, ast_t *ast) -{ +Text_t compile_top_level_code(env_t *env, ast_t *ast) { if (!ast) return EMPTY_TEXT; switch (ast->tag) { case Use: { // DeclareMatch(use, ast, Use); // if (use->what == USE_C_CODE) { - // Path_t path = Path$relative_to(Path$from_str(use->path), Path(".build")); - // return Texts("#include \"", Path$as_c_string(path), "\"\n"); + // Path_t path = Path$relative_to(Path$from_str(use->path), + // Path(".build")); return Texts("#include \"", + // Path$as_c_string(path), + // "\"\n"); // } return EMPTY_TEXT; } @@ -4377,31 +4402,32 @@ Text_t compile_top_level_code(env_t *env, ast_t *ast) bool is_private = decl_name[0] == '_'; if ((decl->value && is_constant(env, decl->value)) || (!decl->value && !has_heap_memory(t))) { set_binding(env, decl_name, t, full_name); - return Texts( - is_private ? "static " : "public ", - compile_declaration(t, full_name), " = ", val_code, ";\n"); + return Texts(is_private ? "static " : "public ", compile_declaration(t, full_name), " = ", val_code, ";\n"); } else { Text_t init_var = namespace_name(env, env->namespace, Texts(decl_name, "$$initialized")); Text_t checked_access = Texts("check_initialized(", full_name, ", ", init_var, ", \"", decl_name, "\")"); set_binding(env, decl_name, t, checked_access); Text_t initialized_name = namespace_name(env, env->namespace, Texts(decl_name, "$$initialized")); - return Texts( - "static bool ", initialized_name, " = false;\n", - is_private ? "static " : "public ", - compile_declaration(t, full_name), ";\n"); + return Texts("static bool ", initialized_name, " = false;\n", is_private ? "static " : "public ", + compile_declaration(t, full_name), ";\n"); } } case FunctionDef: { - Text_t name_code = namespace_name(env, env->namespace, Text$from_str(Match(Match(ast, FunctionDef)->name, Var)->name)); + Text_t name_code = + namespace_name(env, env->namespace, Text$from_str(Match(Match(ast, FunctionDef)->name, Var)->name)); return compile_function(env, name_code, ast, &env->code->staticdefs); } case ConvertDef: { type_t *type = get_function_def_type(env, ast); const char *name = get_type_name(Match(type, FunctionType)->ret); if (!name) - code_err(ast, "Conversions are only supported for text, struct, and enum types, not ", type_to_str(Match(type, FunctionType)->ret)); - Text_t name_code = namespace_name(env, env->namespace, Texts(name, "$", String(get_line_number(ast->file, ast->start)))); + code_err(ast, + "Conversions are only supported for text, struct, and enum " + "types, not ", + type_to_str(Match(type, FunctionType)->ret)); + Text_t name_code = + namespace_name(env, env->namespace, Texts(name, "$", String(get_line_number(ast->file, ast->start)))); return compile_function(env, name_code, ast, &env->code->staticdefs); } case StructDef: { @@ -4421,9 +4447,10 @@ Text_t compile_top_level_code(env_t *env, ast_t *ast) } case LangDef: { DeclareMatch(def, ast, LangDef); - Text_t code = Texts("public const TypeInfo_t ", namespace_name(env, env->namespace, Texts(def->name, "$$info")), - " = {", String((int64_t)sizeof(Text_t)), ", ", String((int64_t)__alignof__(Text_t)), - ", .metamethods=Text$metamethods, .tag=TextInfo, .TextInfo={", quoted_str(def->name), "}};\n"); + Text_t code = + Texts("public const TypeInfo_t ", namespace_name(env, env->namespace, Texts(def->name, "$$info")), " = {", + String((int64_t)sizeof(Text_t)), ", ", String((int64_t)__alignof__(Text_t)), + ", .metamethods=Text$metamethods, .tag=TextInfo, .TextInfo={", quoted_str(def->name), "}};\n"); env_t *ns_env = namespace_env(env, def->name); return Texts(code, def->namespace ? compile_top_level_code(ns_env, def->namespace) : EMPTY_TEXT); } @@ -4433,10 +4460,10 @@ Text_t compile_top_level_code(env_t *env, ast_t *ast) if (!b || b->type->tag != TypeInfoType) code_err(ast, "'", extend->name, "' is not the name of any type I recognize."); env_t *ns_env = Match(b->type, TypeInfoType)->env; - env_t *extended = new(env_t); + env_t *extended = new (env_t); *extended = *ns_env; - extended->locals = new(Table_t, .fallback=env->locals); - extended->namespace_bindings = new(Table_t, .fallback=env->namespace_bindings); + extended->locals = new (Table_t, .fallback = env->locals); + extended->namespace_bindings = new (Table_t, .fallback = env->namespace_bindings); extended->id_suffix = env->id_suffix; return compile_top_level_code(extended, extend->body); } @@ -4452,8 +4479,7 @@ Text_t compile_top_level_code(env_t *env, ast_t *ast) } } -static void initialize_vars_and_statics(env_t *env, ast_t *ast) -{ +static void initialize_vars_and_statics(env_t *env, ast_t *ast) { if (!ast) return; for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) { @@ -4469,13 +4495,10 @@ static void initialize_vars_and_statics(env_t *env, ast_t *ast) Text_t val_code = compile_declared_value(env, stmt->ast); if ((decl->value && !is_constant(env, decl->value)) || (!decl->value && has_heap_memory(t))) { Text_t initialized_name = namespace_name(env, env->namespace, Texts(decl_name, "$$initialized")); - env->code->variable_initializers = Texts( - env->code->variable_initializers, - with_source_info( - env, stmt->ast, - Texts( - full_name, " = ", val_code, ",\n", - initialized_name, " = true;\n"))); + env->code->variable_initializers = + Texts(env->code->variable_initializers, + with_source_info(env, stmt->ast, + Texts(full_name, " = ", val_code, ",\n", initialized_name, " = true;\n"))); } } else if (stmt->ast->tag == StructDef) { initialize_vars_and_statics(namespace_env(env, Match(stmt->ast, StructDef)->name), @@ -4498,8 +4521,7 @@ static void initialize_vars_and_statics(env_t *env, ast_t *ast) } } -Text_t compile_file(env_t *env, ast_t *ast) -{ +Text_t compile_file(env_t *env, ast_t *ast) { Text_t top_level_code = compile_top_level_code(env, ast); Text_t includes = EMPTY_TEXT; Text_t use_imports = EMPTY_TEXT; @@ -4520,27 +4542,18 @@ Text_t compile_file(env_t *env, ast_t *ast) initialize_vars_and_statics(env, ast); const char *name = file_base_name(ast->file->filename); - return Texts( - env->do_source_mapping ? Texts("#line 1 ", quoted_str(ast->file->filename), "\n") : EMPTY_TEXT, - "#define __SOURCE_FILE__ ", quoted_str(ast->file->filename), "\n", - "#include <tomo_"TOMO_VERSION"/tomo.h>\n" - "#include \"", name, ".tm.h\"\n\n", - includes, - env->code->local_typedefs, "\n", - env->code->lambdas, "\n", - env->code->staticdefs, "\n", - top_level_code, - "public void ", namespace_name(env, env->namespace, Text("$initialize")), "(void) {\n", - "static bool initialized = false;\n", - "if (initialized) return;\n", - "initialized = true;\n", - use_imports, - env->code->variable_initializers, - "}\n"); + return Texts(env->do_source_mapping ? Texts("#line 1 ", quoted_str(ast->file->filename), "\n") : EMPTY_TEXT, + "#define __SOURCE_FILE__ ", quoted_str(ast->file->filename), "\n", + "#include <tomo_" TOMO_VERSION "/tomo.h>\n" + "#include \"", + name, ".tm.h\"\n\n", includes, env->code->local_typedefs, "\n", env->code->lambdas, "\n", + env->code->staticdefs, "\n", top_level_code, "public void ", + namespace_name(env, env->namespace, Text("$initialize")), "(void) {\n", + "static bool initialized = false;\n", "if (initialized) return;\n", "initialized = true;\n", + use_imports, env->code->variable_initializers, "}\n"); } -Text_t compile_statement_type_header(env_t *env, Path_t header_path, ast_t *ast) -{ +Text_t compile_statement_type_header(env_t *env, Path_t header_path, ast_t *ast) { switch (ast->tag) { case Use: { DeclareMatch(use, ast, Use); @@ -4552,9 +4565,10 @@ Text_t compile_statement_type_header(env_t *env, Path_t header_path, ast_t *ast) module_info_t mod = get_module_info(ast); glob_t tm_files; const char *folder = mod.version ? String(mod.name, "_", mod.version) : mod.name; - if (glob(String(TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL, &tm_files) != 0) { - if (!try_install_module(mod)) - code_err(ast, "Could not find library"); + if (glob(String(TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, + NULL, &tm_files) + != 0) { + if (!try_install_module(mod)) code_err(ast, "Could not find library"); } Text_t includes = EMPTY_TEXT; @@ -4581,8 +4595,7 @@ Text_t compile_statement_type_header(env_t *env, Path_t header_path, ast_t *ast) Path_t used_path = Path$resolved(Path$from_str(use->path), source_dir); return Texts("#include \"", Path$as_c_string(Path$relative_to(used_path, build_dir)), "\"\n"); } - default: - return EMPTY_TEXT; + default: return EMPTY_TEXT; } } case StructDef: { @@ -4595,23 +4608,25 @@ Text_t compile_statement_type_header(env_t *env, Path_t header_path, ast_t *ast) DeclareMatch(def, ast, LangDef); return Texts( // Constructor macro: - "#define ", namespace_name(env, env->namespace, Text$from_str(def->name)), - "(text) ((", namespace_name(env, env->namespace, Texts(def->name, "$$type")), "){.length=sizeof(text)-1, .tag=TEXT_ASCII, .ascii=\"\" text})\n" - "#define ", namespace_name(env, env->namespace, Text$from_str(def->name)), - "s(...) ((", namespace_name(env, env->namespace, Texts(def->name, "$$type")), ")Texts(__VA_ARGS__))\n" - "extern const TypeInfo_t ", namespace_name(env, env->namespace, Texts(def->name, Text("$$info"))), ";\n" - ); + "#define ", namespace_name(env, env->namespace, Text$from_str(def->name)), "(text) ((", + namespace_name(env, env->namespace, Texts(def->name, "$$type")), + "){.length=sizeof(text)-1, .tag=TEXT_ASCII, .ascii=\"\" " + "text})\n" + "#define ", + namespace_name(env, env->namespace, Text$from_str(def->name)), "s(...) ((", + namespace_name(env, env->namespace, Texts(def->name, "$$type")), + ")Texts(__VA_ARGS__))\n" + "extern const TypeInfo_t ", + namespace_name(env, env->namespace, Texts(def->name, Text("$$info"))), ";\n"); } case Extend: { return EMPTY_TEXT; } - default: - return EMPTY_TEXT; + default: return EMPTY_TEXT; } } -Text_t compile_statement_namespace_header(env_t *env, Path_t header_path, ast_t *ast) -{ +Text_t compile_statement_namespace_header(env_t *env, Path_t header_path, ast_t *ast) { env_t *ns_env = NULL; ast_t *block = NULL; switch (ast->tag) { @@ -4625,10 +4640,10 @@ Text_t compile_statement_namespace_header(env_t *env, Path_t header_path, ast_t DeclareMatch(extend, ast, Extend); ns_env = namespace_env(env, extend->name); - env_t *extended = new(env_t); + env_t *extended = new (env_t); *extended = *ns_env; - extended->locals = new(Table_t, .fallback=env->locals); - extended->namespace_bindings = new(Table_t, .fallback=env->namespace_bindings); + extended->locals = new (Table_t, .fallback = env->locals); + extended->namespace_bindings = new (Table_t, .fallback = env->namespace_bindings); extended->id_suffix = env->id_suffix; ns_env = extended; @@ -4669,19 +4684,16 @@ Text_t compile_statement_namespace_header(env_t *env, Path_t header_path, ast_t DeclareMatch(decl, ast, Declare); const char *decl_name = Match(decl->var, Var)->name; bool is_private = (decl_name[0] == '_'); - if (is_private) - return EMPTY_TEXT; + if (is_private) return EMPTY_TEXT; type_t *t = decl->type ? parse_type_ast(env, decl->type) : get_type(env, decl->value); - if (t->tag == FunctionType) - t = Type(ClosureType, t); + if (t->tag == FunctionType) t = Type(ClosureType, t); assert(t->tag != ModuleType); if (t->tag == AbortType || t->tag == VoidType || t->tag == ReturnType) code_err(ast, "You can't declare a variable with a ", type_to_str(t), " value"); - return Texts( - decl->value ? compile_statement_type_header(env, header_path, decl->value) : EMPTY_TEXT, - "extern ", compile_declaration(t, namespace_name(env, env->namespace, Text$from_str(decl_name))), ";\n"); + return Texts(decl->value ? compile_statement_type_header(env, header_path, decl->value) : EMPTY_TEXT, "extern ", + compile_declaration(t, namespace_name(env, env->namespace, Text$from_str(decl_name))), ";\n"); } case FunctionDef: { DeclareMatch(fndef, ast, FunctionDef); @@ -4698,8 +4710,7 @@ Text_t compile_statement_namespace_header(env_t *env, Path_t header_path, ast_t type_t *ret_t = fndef->ret_type ? parse_type_ast(env, fndef->ret_type) : Type(VoidType); Text_t ret_type_code = compile_type(ret_t); - if (ret_t->tag == AbortType) - ret_type_code = Texts("__attribute__((noreturn)) _Noreturn ", ret_type_code); + if (ret_t->tag == AbortType) ret_type_code = Texts("__attribute__((noreturn)) _Noreturn ", ret_type_code); Text_t name = namespace_name(env, env->namespace, Text$from_str(decl_name)); if (env->namespace && env->namespace->parent && env->namespace->name && streq(decl_name, env->namespace->name)) name = namespace_name(env, env->namespace, Text$from_str(String(get_line_number(ast->file, ast->start)))); @@ -4720,8 +4731,12 @@ Text_t compile_statement_namespace_header(env_t *env, Path_t header_path, ast_t Text_t ret_type_code = compile_type(ret_t); Text_t name = Text$from_str(get_type_name(ret_t)); if (name.length == 0) - code_err(ast, "Conversions are only supported for text, struct, and enum types, not ", type_to_str(ret_t)); - Text_t name_code = namespace_name(env, env->namespace, Texts(name, "$", String(get_line_number(ast->file, ast->start)))); + code_err(ast, + "Conversions are only supported for text, struct, and enum " + "types, not ", + type_to_str(ret_t)); + Text_t name_code = + namespace_name(env, env->namespace, Texts(name, "$", String(get_line_number(ast->file, ast->start)))); return Texts(ret_type_code, " ", name_code, arg_signature, ";\n"); } default: return EMPTY_TEXT; @@ -4740,8 +4755,7 @@ typedef struct { Path_t header_path; } compile_typedef_info_t; -static void _make_typedefs(compile_typedef_info_t *info, ast_t *ast) -{ +static void _make_typedefs(compile_typedef_info_t *info, ast_t *ast) { if (ast->tag == StructDef) { DeclareMatch(def, ast, StructDef); if (def->external) return; @@ -4762,8 +4776,10 @@ static void _make_typedefs(compile_typedef_info_t *info, ast_t *ast) for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { if (!tag->fields) continue; - Text_t tag_struct = namespace_name(info->env, info->env->namespace, Texts(def->name, "$", tag->name, "$$struct")); - Text_t tag_type = namespace_name(info->env, info->env->namespace, Texts(def->name, "$", tag->name, "$$type")); + Text_t tag_struct = + namespace_name(info->env, info->env->namespace, Texts(def->name, "$", tag->name, "$$struct")); + Text_t tag_type = + namespace_name(info->env, info->env->namespace, Texts(def->name, "$", tag->name, "$$type")); *info->header = Texts(*info->header, "typedef struct ", tag_struct, " ", tag_type, ";\n"); } } else { @@ -4773,27 +4789,25 @@ static void _make_typedefs(compile_typedef_info_t *info, ast_t *ast) } } else if (ast->tag == LangDef) { DeclareMatch(def, ast, LangDef); - *info->header = Texts(*info->header, "typedef Text_t ", namespace_name(info->env, info->env->namespace, Texts(def->name, "$$type")), ";\n"); + *info->header = Texts(*info->header, "typedef Text_t ", + namespace_name(info->env, info->env->namespace, Texts(def->name, "$$type")), ";\n"); } } -static void _define_types_and_funcs(compile_typedef_info_t *info, ast_t *ast) -{ - *info->header = Texts(*info->header, - compile_statement_type_header(info->env, info->header_path, ast), - compile_statement_namespace_header(info->env, info->header_path, ast)); +static void _define_types_and_funcs(compile_typedef_info_t *info, ast_t *ast) { + *info->header = Texts(*info->header, compile_statement_type_header(info->env, info->header_path, ast), + compile_statement_namespace_header(info->env, info->header_path, ast)); } -Text_t compile_file_header(env_t *env, Path_t header_path, ast_t *ast) -{ - Text_t header = Texts( - "#pragma once\n", - env->do_source_mapping ? Texts("#line 1 ", quoted_str(ast->file->filename), "\n") : EMPTY_TEXT, - "#include <tomo_"TOMO_VERSION"/tomo.h>\n"); +Text_t compile_file_header(env_t *env, Path_t header_path, ast_t *ast) { + Text_t header = + Texts("#pragma once\n", + env->do_source_mapping ? Texts("#line 1 ", quoted_str(ast->file->filename), "\n") : EMPTY_TEXT, + "#include <tomo_" TOMO_VERSION "/tomo.h>\n"); - compile_typedef_info_t info = {.env=env, .header=&header, .header_path=header_path}; - visit_topologically(Match(ast, Block)->statements, (Closure_t){.fn=(void*)_make_typedefs, &info}); - visit_topologically(Match(ast, Block)->statements, (Closure_t){.fn=(void*)_define_types_and_funcs, &info}); + compile_typedef_info_t info = {.env = env, .header = &header, .header_path = header_path}; + visit_topologically(Match(ast, Block)->statements, (Closure_t){.fn = (void *)_make_typedefs, &info}); + visit_topologically(Match(ast, Block)->statements, (Closure_t){.fn = (void *)_define_types_and_funcs, &info}); header = Texts(header, "void ", namespace_name(env, env->namespace, Text("$initialize")), "(void);\n"); return header; diff --git a/src/config.h b/src/config.h index f7fa63a0..4c750f19 100644 --- a/src/config.h +++ b/src/config.h @@ -1,27 +1,27 @@ // Configuration of values that will be baked into the executable: #ifndef TOMO_VERSION -# define TOMO_VERSION "v???" +#define TOMO_VERSION "v???" #endif #ifndef GIT_VERSION -# define GIT_VERSION "???" +#define GIT_VERSION "???" #endif #ifndef TOMO_PREFIX -# define TOMO_PREFIX "/usr/local" +#define TOMO_PREFIX "/usr/local" #endif #ifndef DEFAULT_C_COMPILER -# define DEFAULT_C_COMPILER "cc" +#define DEFAULT_C_COMPILER "cc" #endif #ifndef SUDO -# if defined(__OpenBSD__) -# define SUDO "doas" -# else -# define SUDO "sudo" -# endif +#if defined(__OpenBSD__) +#define SUDO "doas" +#else +#define SUDO "sudo" +#endif #endif // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/enums.c b/src/enums.c index dba6d4cb..078600e0 100644 --- a/src/enums.c +++ b/src/enums.c @@ -11,8 +11,7 @@ #include "structs.h" #include "typecheck.h" -Text_t compile_enum_typeinfo(env_t *env, ast_t *ast) -{ +Text_t compile_enum_typeinfo(env_t *env, ast_t *ast) { DeclareMatch(def, ast, EnumDef); // Compile member types and constructors: @@ -23,8 +22,8 @@ Text_t compile_enum_typeinfo(env_t *env, ast_t *ast) const char *tag_name = String(def->name, "$", tag->name); type_t *tag_type = Table$str_get(*env->types, tag_name); assert(tag_type && tag_type->tag == StructType); - member_typeinfos = Texts( - member_typeinfos, compile_struct_typeinfo(env, tag_type, tag_name, tag->fields, tag->secret, false)); + member_typeinfos = + Texts(member_typeinfos, compile_struct_typeinfo(env, tag_type, tag_name, tag->fields, tag->secret, false)); } int num_tags = 0; @@ -34,25 +33,25 @@ Text_t compile_enum_typeinfo(env_t *env, ast_t *ast) type_t *t = Table$str_get(*env->types, def->name); const char *metamethods = is_packed_data(t) ? "PackedDataEnum$metamethods" : "Enum$metamethods"; Text_t info = namespace_name(env, env->namespace, Texts(def->name, "$$info")); - Text_t typeinfo = Texts("public const TypeInfo_t ", info, " = {", - String((int64_t)type_size(t)), "u, ", String((int64_t)type_align(t)), "u, .metamethods=", metamethods, - ", {.tag=EnumInfo, .EnumInfo={.name=\"", def->name, "\", " - ".num_tags=", String((int64_t)num_tags), ", .tags=(NamedType_t[]){"); + Text_t typeinfo = Texts("public const TypeInfo_t ", info, " = {", String((int64_t)type_size(t)), "u, ", + String((int64_t)type_align(t)), "u, .metamethods=", metamethods, + ", {.tag=EnumInfo, .EnumInfo={.name=\"", def->name, + "\", " + ".num_tags=", + String((int64_t)num_tags), ", .tags=(NamedType_t[]){"); for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { const char *tag_type_name = String(def->name, "$", tag->name); type_t *tag_type = Table$str_get(*env->types, tag_type_name); if (tag_type && Match(tag_type, StructType)->fields) typeinfo = Texts(typeinfo, "{\"", tag->name, "\", ", compile_type_info(tag_type), "}, "); - else - typeinfo = Texts(typeinfo, "{\"", tag->name, "\"}, "); + else typeinfo = Texts(typeinfo, "{\"", tag->name, "\"}, "); } typeinfo = Texts(typeinfo, "}}}};\n"); return Texts(member_typeinfos, typeinfo); } -Text_t compile_enum_constructors(env_t *env, ast_t *ast) -{ +Text_t compile_enum_constructors(env_t *env, ast_t *ast) { DeclareMatch(def, ast, EnumDef); Text_t constructors = EMPTY_TEXT; for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { @@ -69,7 +68,7 @@ Text_t compile_enum_constructors(env_t *env, ast_t *ast) Text_t tagged_name = namespace_name(env, env->namespace, Texts(def->name, "$tagged$", tag->name)); Text_t tag_name = namespace_name(env, env->namespace, Texts(def->name, "$tag$", tag->name)); Text_t constructor_impl = Texts("public inline ", type_name, " ", tagged_name, "(", arg_sig, ") { return (", - type_name, "){.$tag=", tag_name, ", .", valid_c_name(tag->name), "={"); + type_name, "){.$tag=", tag_name, ", .", valid_c_name(tag->name), "={"); for (arg_ast_t *field = tag->fields; field; field = field->next) { constructor_impl = Texts(constructor_impl, "$", field->name); if (field->next) constructor_impl = Texts(constructor_impl, ", "); @@ -80,8 +79,7 @@ Text_t compile_enum_constructors(env_t *env, ast_t *ast) return constructors; } -Text_t compile_enum_header(env_t *env, ast_t *ast) -{ +Text_t compile_enum_header(env_t *env, ast_t *ast) { DeclareMatch(def, ast, EnumDef); Text_t all_defs = EMPTY_TEXT; Text_t none_name = namespace_name(env, env->namespace, Texts(def->name, "$none")); @@ -104,13 +102,17 @@ Text_t compile_enum_header(env_t *env, ast_t *ast) } Text_t struct_name = namespace_name(env, env->namespace, Texts(def->name, "$$struct")); - Text_t enum_def = Texts("struct ", struct_name, " {\n" - "enum ", enum_tags, " $tag;\n" - "union {\n"); + Text_t enum_def = Texts("struct ", struct_name, + " {\n" + "enum ", + enum_tags, + " $tag;\n" + "union {\n"); for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { if (!tag->fields) continue; Text_t field_def = compile_struct_header( - env, WrapAST(ast, StructDef, .name=Text$as_c_string(Texts(def->name, "$", tag->name)), .fields=tag->fields)); + env, + WrapAST(ast, StructDef, .name = Text$as_c_string(Texts(def->name, "$", tag->name)), .fields = tag->fields)); all_defs = Texts(all_defs, field_def); Text_t tag_type = namespace_name(env, env->namespace, Texts(def->name, "$", tag->name, "$$type")); enum_def = Texts(enum_def, tag_type, " ", valid_c_name(tag->name), ";\n"); diff --git a/src/environment.c b/src/environment.c index 22b13e65..3b3c6d0b 100644 --- a/src/environment.c +++ b/src/environment.c @@ -13,16 +13,17 @@ #include "typecheck.h" type_t *TEXT_TYPE = NULL; -public type_t *PATH_TYPE = NULL; -public type_t *PATH_TYPE_TYPE = NULL; +public +type_t *PATH_TYPE = NULL; +public +type_t *PATH_TYPE_TYPE = NULL; -static type_t *declare_type(env_t *env, const char *def_str) -{ +static type_t *declare_type(env_t *env, const char *def_str) { ast_t *ast = parse(def_str); - if (!ast) errx(1, "Couldn't not parse struct def: %s", def_str); - if (ast->tag != Block) errx(1, "Couldn't not parse struct def: %s", def_str); + if (!ast) errx(1, "Couldn't not parse struct def: %s", def_str); + if (ast->tag != Block) errx(1, "Couldn't not parse struct def: %s", def_str); ast_list_t *statements = Match(ast, Block)->statements; - if (statements == NULL || statements->next) errx(1, "Couldn't not parse struct def: %s", def_str); + if (statements == NULL || statements->next) errx(1, "Couldn't not parse struct def: %s", def_str); switch (statements->ast->tag) { case StructDef: { DeclareMatch(def, statements->ast, StructDef); @@ -41,30 +42,27 @@ static type_t *declare_type(env_t *env, const char *def_str) return NULL; } -static type_t *bind_type(env_t *env, const char *name, type_t *type) -{ - if (Table$str_get(*env->types, name)) - errx(1, "Duplicate binding for type: %s", name); +static type_t *bind_type(env_t *env, const char *name, type_t *type) { + if (Table$str_get(*env->types, name)) errx(1, "Duplicate binding for type: %s", name); Table$str_set(env->types, name, type); return type; } -env_t *global_env(bool source_mapping) -{ +env_t *global_env(bool source_mapping) { static env_t *_global_env = NULL; if (_global_env != NULL) return _global_env; - env_t *env = new(env_t); - env->code = new(compilation_unit_t); - env->types = new(Table_t); - env->globals = new(Table_t); + env_t *env = new (env_t); + env->code = new (compilation_unit_t); + env->types = new (Table_t); + env->globals = new (Table_t); env->locals = env->globals; - env->imports = new(Table_t); + env->imports = new (Table_t); env->do_source_mapping = source_mapping; - TEXT_TYPE = bind_type(env, "Text", Type(TextType, .lang="Text", .env=namespace_env(env, "Text"))); + TEXT_TYPE = bind_type(env, "Text", Type(TextType, .lang = "Text", .env = namespace_env(env, "Text"))); (void)bind_type(env, "Int", Type(BigIntType)); - (void)bind_type(env, "Int32", Type(IntType, .bits=TYPE_IBITS32)); + (void)bind_type(env, "Int32", Type(IntType, .bits = TYPE_IBITS32)); (void)bind_type(env, "Memory", Type(MemoryType)); PATH_TYPE_TYPE = declare_type(env, "enum PathType(Relative, Absolute, Home)"); PATH_TYPE = declare_type(env, "struct Path(type:PathType, components:[Text])"); @@ -83,331 +81,289 @@ env_t *global_env(bool source_mapping) {"Void", Type(VoidType), Text("Void_t"), Text("Void$info"), {}}, {"Abort", Type(AbortType), Text("void"), Text("Abort$info"), {}}, {"Memory", Type(MemoryType), Text("Memory_t"), Text("Memory$info"), {}}, - {"Bool", Type(BoolType), Text("Bool_t"), Text("Bool$info"), TypedList(ns_entry_t, - {"parse", "Bool$parse", "func(text:Text, remainder:&Text? = none -> Bool?)"}, - )}, - {"Byte", Type(ByteType), Text("Byte_t"), Text("Byte$info"), TypedList(ns_entry_t, - {"get_bit", "Byte$get_bit", "func(x:Byte, bit_index:Int -> Bool)"}, - {"hex", "Byte$hex", "func(byte:Byte, uppercase=yes, prefix=no -> Text)"}, - {"is_between", "Byte$is_between", "func(x:Byte, low:Byte, high:Byte -> Bool)"}, - {"max", "Byte$max", "Byte"}, - {"min", "Byte$min", "Byte"}, - {"parse", "Byte$parse", "func(text:Text, remainder:&Text? = none -> Byte?)"}, - {"to", "Byte$to", "func(first:Byte, last:Byte, step:Int8?=none -> func(->Byte?))"}, - )}, - {"Int", Type(BigIntType), Text("Int_t"), Text("Int$info"), TypedList(ns_entry_t, - {"abs", "Int$abs", "func(x:Int -> Int)"}, - {"bit_and", "Int$bit_and", "func(x,y:Int -> Int)"}, - {"bit_or", "Int$bit_or", "func(x,y:Int -> Int)"}, - {"bit_xor", "Int$bit_xor", "func(x,y:Int -> Int)"}, - {"choose", "Int$choose", "func(x,y:Int -> Int)"}, - {"clamped", "Int$clamped", "func(x,low,high:Int -> Int)"}, - {"divided_by", "Int$divided_by", "func(x,y:Int -> Int)"}, - {"factorial", "Int$factorial", "func(x:Int -> Int)"}, - {"gcd", "Int$gcd", "func(x,y:Int -> Int)"}, - {"get_bit", "Int$get_bit", "func(x,bit_index:Int -> Bool)"}, - {"hex", "Int$hex", "func(i:Int, digits=0, uppercase=yes, prefix=yes -> Text)"}, - {"is_between", "Int$is_between", "func(x:Int,low:Int,high:Int -> Bool)"}, - {"is_prime", "Int$is_prime", "func(x:Int,reps=50 -> Bool)"}, - {"left_shifted", "Int$left_shifted", "func(x,y:Int -> Int)"}, - {"minus", "Int$minus", "func(x,y:Int -> Int)"}, - {"modulo", "Int$modulo", "func(x,y:Int -> Int)"}, - {"modulo1", "Int$modulo1", "func(x,y:Int -> Int)"}, - {"negated", "Int$negated", "func(x:Int -> Int)"}, - {"negative", "Int$negative", "func(x:Int -> Int)"}, - {"next_prime", "Int$next_prime", "func(x:Int -> Int)"}, - {"octal", "Int$octal", "func(i:Int, digits=0, prefix=yes -> Text)"}, - {"onward", "Int$onward", "func(first:Int,step=1 -> func(->Int?))"}, - {"parse", "Int$parse", "func(text:Text, remainder:&Text? = none -> Int?)"}, - {"plus", "Int$plus", "func(x,y:Int -> Int)"}, - {"power", "Int$power", "func(base:Int,exponent:Int -> Int)"}, + {"Bool", Type(BoolType), Text("Bool_t"), Text("Bool$info"), + TypedList(ns_entry_t, {"parse", "Bool$parse", "func(text:Text, remainder:&Text? = none -> Bool?)"}, )}, + {"Byte", Type(ByteType), Text("Byte_t"), Text("Byte$info"), + TypedList(ns_entry_t, {"get_bit", "Byte$get_bit", "func(x:Byte, bit_index:Int -> Bool)"}, + {"hex", "Byte$hex", "func(byte:Byte, uppercase=yes, prefix=no -> Text)"}, + {"is_between", "Byte$is_between", "func(x:Byte, low:Byte, high:Byte -> Bool)"}, + {"max", "Byte$max", "Byte"}, {"min", "Byte$min", "Byte"}, + {"parse", "Byte$parse", "func(text:Text, remainder:&Text? = none -> Byte?)"}, + {"to", "Byte$to", "func(first:Byte, last:Byte, step:Int8?=none -> func(->Byte?))"}, )}, + {"Int", Type(BigIntType), Text("Int_t"), Text("Int$info"), + TypedList( + ns_entry_t, {"abs", "Int$abs", "func(x:Int -> Int)"}, {"bit_and", "Int$bit_and", "func(x,y:Int -> Int)"}, + {"bit_or", "Int$bit_or", "func(x,y:Int -> Int)"}, {"bit_xor", "Int$bit_xor", "func(x,y:Int -> Int)"}, + {"choose", "Int$choose", "func(x,y:Int -> Int)"}, + {"clamped", "Int$clamped", "func(x,low,high:Int -> Int)"}, + {"divided_by", "Int$divided_by", "func(x,y:Int -> Int)"}, + {"factorial", "Int$factorial", "func(x:Int -> Int)"}, {"gcd", "Int$gcd", "func(x,y:Int -> Int)"}, + {"get_bit", "Int$get_bit", "func(x,bit_index:Int -> Bool)"}, + {"hex", "Int$hex", "func(i:Int, digits=0, uppercase=yes, prefix=yes -> Text)"}, + {"is_between", "Int$is_between", "func(x:Int,low:Int,high:Int -> Bool)"}, + {"is_prime", "Int$is_prime", "func(x:Int,reps=50 -> Bool)"}, + {"left_shifted", "Int$left_shifted", "func(x,y:Int -> Int)"}, + {"minus", "Int$minus", "func(x,y:Int -> Int)"}, {"modulo", "Int$modulo", "func(x,y:Int -> Int)"}, + {"modulo1", "Int$modulo1", "func(x,y:Int -> Int)"}, {"negated", "Int$negated", "func(x:Int -> Int)"}, + {"negative", "Int$negative", "func(x:Int -> Int)"}, {"next_prime", "Int$next_prime", "func(x:Int -> Int)"}, + {"octal", "Int$octal", "func(i:Int, digits=0, prefix=yes -> Text)"}, + {"onward", "Int$onward", "func(first:Int,step=1 -> func(->Int?))"}, + {"parse", "Int$parse", "func(text:Text, remainder:&Text? = none -> Int?)"}, + {"plus", "Int$plus", "func(x,y:Int -> Int)"}, {"power", "Int$power", "func(base:Int,exponent:Int -> Int)"}, #if __GNU_MP_VERSION >= 6 #if __GNU_MP_VERSION_MINOR >= 3 - {"prev_prime", "Int$prev_prime", "func(x:Int -> Int?)"}, + {"prev_prime", "Int$prev_prime", "func(x:Int -> Int?)"}, #endif #endif - {"right_shifted", "Int$right_shifted", "func(x,y:Int -> Int)"}, - {"sqrt", "Int$sqrt", "func(x:Int -> Int?)"}, - {"times", "Int$times", "func(x,y:Int -> Int)"}, - {"to", "Int$to", "func(first:Int,last:Int,step:Int?=none -> func(->Int?))"}, - )}, - {"Int64", Type(IntType, .bits=TYPE_IBITS64), Text("Int64_t"), Text("Int64$info"), TypedList(ns_entry_t, - {"abs", "labs", "func(i:Int64 -> Int64)"}, - {"bits", "Int64$bits", "func(x:Int64 -> [Bool])"}, - {"clamped", "Int64$clamped", "func(x,low,high:Int64 -> Int64)"}, - {"divided_by", "Int64$divided_by", "func(x,y:Int64 -> Int64)"}, - {"gcd", "Int64$gcd", "func(x,y:Int64 -> Int64)"}, - {"parse", "Int64$parse", "func(text:Text, remainder:&Text? = none -> Int64?)"}, - {"get_bit", "Int64$get_bit", "func(x:Int64, bit_index:Int -> Bool)"}, - {"hex", "Int64$hex", "func(i:Int64, digits=0, uppercase=yes, prefix=yes -> Text)"}, - {"is_between", "Int64$is_between", "func(x:Int64,low:Int64,high:Int64 -> Bool)"}, - {"max", "Int64$max", "Int64"}, - {"min", "Int64$min", "Int64"}, - {"modulo", "Int64$modulo", "func(x,y:Int64 -> Int64)"}, - {"modulo1", "Int64$modulo1", "func(x,y:Int64 -> Int64)"}, - {"octal", "Int64$octal", "func(i:Int64, digits=0, prefix=yes -> Text)"}, - {"onward", "Int64$onward", "func(first:Int64,step=Int64(1) -> func(->Int64?))"}, - {"to", "Int64$to", "func(first:Int64,last:Int64,step:Int64?=none -> func(->Int64?))"}, - {"unsigned_left_shifted", "Int64$unsigned_left_shifted", "func(x:Int64,y:Int64 -> Int64)"}, - {"unsigned_right_shifted", "Int64$unsigned_right_shifted", "func(x:Int64,y:Int64 -> Int64)"}, - {"wrapping_minus", "Int64$wrapping_minus", "func(x:Int64,y:Int64 -> Int64)"}, - {"wrapping_plus", "Int64$wrapping_plus", "func(x:Int64,y:Int64 -> Int64)"}, - )}, - {"Int32", Type(IntType, .bits=TYPE_IBITS32), Text("Int32_t"), Text("Int32$info"), TypedList(ns_entry_t, - {"abs", "abs", "func(i:Int32 -> Int32)"}, - {"bits", "Int32$bits", "func(x:Int32 -> [Bool])"}, - {"clamped", "Int32$clamped", "func(x,low,high:Int32 -> Int32)"}, - {"divided_by", "Int32$divided_by", "func(x,y:Int32 -> Int32)"}, - {"gcd", "Int32$gcd", "func(x,y:Int32 -> Int32)"}, - {"parse", "Int32$parse", "func(text:Text, remainder:&Text? = none -> Int32?)"}, - {"get_bit", "Int32$get_bit", "func(x:Int32, bit_index:Int -> Bool)"}, - {"hex", "Int32$hex", "func(i:Int32, digits=0, uppercase=yes, prefix=yes -> Text)"}, - {"is_between", "Int32$is_between", "func(x:Int32,low:Int32,high:Int32 -> Bool)"}, - {"max", "Int32$max", "Int32"}, - {"min", "Int32$min", "Int32"}, - {"modulo", "Int32$modulo", "func(x,y:Int32 -> Int32)"}, - {"modulo1", "Int32$modulo1", "func(x,y:Int32 -> Int32)"}, - {"octal", "Int32$octal", "func(i:Int32, digits=0, prefix=yes -> Text)"}, - {"onward", "Int32$onward", "func(first:Int32,step=Int32(1) -> func(->Int32?))"}, - {"to", "Int32$to", "func(first:Int32,last:Int32,step:Int32?=none -> func(->Int32?))"}, - {"unsigned_left_shifted", "Int32$unsigned_left_shifted", "func(x:Int32,y:Int32 -> Int32)"}, - {"unsigned_right_shifted", "Int32$unsigned_right_shifted", "func(x:Int32,y:Int32 -> Int32)"}, - {"wrapping_minus", "Int32$wrapping_minus", "func(x:Int32,y:Int32 -> Int32)"}, - {"wrapping_plus", "Int32$wrapping_plus", "func(x:Int32,y:Int32 -> Int32)"}, - )}, - {"Int16", Type(IntType, .bits=TYPE_IBITS16), Text("Int16_t"), Text("Int16$info"), TypedList(ns_entry_t, - {"abs", "abs", "func(i:Int16 -> Int16)"}, - {"bits", "Int16$bits", "func(x:Int16 -> [Bool])"}, - {"clamped", "Int16$clamped", "func(x,low,high:Int16 -> Int16)"}, - {"divided_by", "Int16$divided_by", "func(x,y:Int16 -> Int16)"}, - {"gcd", "Int16$gcd", "func(x,y:Int16 -> Int16)"}, - {"parse", "Int16$parse", "func(text:Text, remainder:&Text? = none -> Int16?)"}, - {"get_bit", "Int16$get_bit", "func(x:Int16, bit_index:Int -> Bool)"}, - {"hex", "Int16$hex", "func(i:Int16, digits=0, uppercase=yes, prefix=yes -> Text)"}, - {"is_between", "Int16$is_between", "func(x:Int16,low:Int16,high:Int16 -> Bool)"}, - {"max", "Int16$max", "Int16"}, - {"min", "Int16$min", "Int16"}, - {"modulo", "Int16$modulo", "func(x,y:Int16 -> Int16)"}, - {"modulo1", "Int16$modulo1", "func(x,y:Int16 -> Int16)"}, - {"octal", "Int16$octal", "func(i:Int16, digits=0, prefix=yes -> Text)"}, - {"onward", "Int16$onward", "func(first:Int16,step=Int16(1) -> func(->Int16?))"}, - {"to", "Int16$to", "func(first:Int16,last:Int16,step:Int16?=none -> func(->Int16?))"}, - {"unsigned_left_shifted", "Int16$unsigned_left_shifted", "func(x:Int16,y:Int16 -> Int16)"}, - {"unsigned_right_shifted", "Int16$unsigned_right_shifted", "func(x:Int16,y:Int16 -> Int16)"}, - {"wrapping_minus", "Int16$wrapping_minus", "func(x:Int16,y:Int16 -> Int16)"}, - {"wrapping_plus", "Int16$wrapping_plus", "func(x:Int16,y:Int16 -> Int16)"}, - )}, - {"Int8", Type(IntType, .bits=TYPE_IBITS8), Text("Int8_t"), Text("Int8$info"), TypedList(ns_entry_t, - {"abs", "abs", "func(i:Int8 -> Int8)"}, - {"bits", "Int8$bits", "func(x:Int8 -> [Bool])"}, - {"clamped", "Int8$clamped", "func(x,low,high:Int8 -> Int8)"}, - {"divided_by", "Int8$divided_by", "func(x,y:Int8 -> Int8)"}, - {"gcd", "Int8$gcd", "func(x,y:Int8 -> Int8)"}, - {"parse", "Int8$parse", "func(text:Text, remainder:&Text? = none -> Int8?)"}, - {"get_bit", "Int8$get_bit", "func(x:Int8, bit_index:Int -> Bool)"}, - {"hex", "Int8$hex", "func(i:Int8, digits=0, uppercase=yes, prefix=yes -> Text)"}, - {"is_between", "Int8$is_between", "func(x:Int8,low:Int8,high:Int8 -> Bool)"}, - {"max", "Int8$max", "Int8"}, - {"min", "Int8$min", "Int8"}, - {"modulo", "Int8$modulo", "func(x,y:Int8 -> Int8)"}, - {"modulo1", "Int8$modulo1", "func(x,y:Int8 -> Int8)"}, - {"octal", "Int8$octal", "func(i:Int8, digits=0, prefix=yes -> Text)"}, - {"onward", "Int8$onward", "func(first:Int8,step=Int8(1) -> func(->Int8?))"}, - {"to", "Int8$to", "func(first:Int8,last:Int8,step:Int8?=none -> func(->Int8?))"}, - {"unsigned_left_shifted", "Int8$unsigned_left_shifted", "func(x:Int8,y:Int8 -> Int8)"}, - {"unsigned_right_shifted", "Int8$unsigned_right_shifted", "func(x:Int8,y:Int8 -> Int8)"}, - {"wrapping_minus", "Int8$wrapping_minus", "func(x:Int8,y:Int8 -> Int8)"}, - {"wrapping_plus", "Int8$wrapping_plus", "func(x:Int8,y:Int8 -> Int8)"}, - )}, -#define C(name) {#name, "M_"#name, "Num"} + {"right_shifted", "Int$right_shifted", "func(x,y:Int -> Int)"}, + {"sqrt", "Int$sqrt", "func(x:Int -> Int?)"}, {"times", "Int$times", "func(x,y:Int -> Int)"}, + {"to", "Int$to", "func(first:Int,last:Int,step:Int?=none -> func(->Int?))"}, )}, + {"Int64", Type(IntType, .bits = TYPE_IBITS64), Text("Int64_t"), Text("Int64$info"), + TypedList(ns_entry_t, {"abs", "labs", "func(i:Int64 -> Int64)"}, + {"bits", "Int64$bits", "func(x:Int64 -> [Bool])"}, + {"clamped", "Int64$clamped", "func(x,low,high:Int64 -> Int64)"}, + {"divided_by", "Int64$divided_by", "func(x,y:Int64 -> Int64)"}, + {"gcd", "Int64$gcd", "func(x,y:Int64 -> Int64)"}, + {"parse", "Int64$parse", "func(text:Text, remainder:&Text? = none -> Int64?)"}, + {"get_bit", "Int64$get_bit", "func(x:Int64, bit_index:Int -> Bool)"}, + {"hex", "Int64$hex", "func(i:Int64, digits=0, uppercase=yes, prefix=yes -> Text)"}, + {"is_between", "Int64$is_between", "func(x:Int64,low:Int64,high:Int64 -> Bool)"}, + {"max", "Int64$max", "Int64"}, {"min", "Int64$min", "Int64"}, + {"modulo", "Int64$modulo", "func(x,y:Int64 -> Int64)"}, + {"modulo1", "Int64$modulo1", "func(x,y:Int64 -> Int64)"}, + {"octal", "Int64$octal", "func(i:Int64, digits=0, prefix=yes -> Text)"}, + {"onward", "Int64$onward", "func(first:Int64,step=Int64(1) -> func(->Int64?))"}, + {"to", "Int64$to", "func(first:Int64,last:Int64,step:Int64?=none -> func(->Int64?))"}, + {"unsigned_left_shifted", "Int64$unsigned_left_shifted", "func(x:Int64,y:Int64 -> Int64)"}, + {"unsigned_right_shifted", "Int64$unsigned_right_shifted", "func(x:Int64,y:Int64 -> Int64)"}, + {"wrapping_minus", "Int64$wrapping_minus", "func(x:Int64,y:Int64 -> Int64)"}, + {"wrapping_plus", "Int64$wrapping_plus", "func(x:Int64,y:Int64 -> Int64)"}, )}, + {"Int32", Type(IntType, .bits = TYPE_IBITS32), Text("Int32_t"), Text("Int32$info"), + TypedList(ns_entry_t, {"abs", "abs", "func(i:Int32 -> Int32)"}, + {"bits", "Int32$bits", "func(x:Int32 -> [Bool])"}, + {"clamped", "Int32$clamped", "func(x,low,high:Int32 -> Int32)"}, + {"divided_by", "Int32$divided_by", "func(x,y:Int32 -> Int32)"}, + {"gcd", "Int32$gcd", "func(x,y:Int32 -> Int32)"}, + {"parse", "Int32$parse", "func(text:Text, remainder:&Text? = none -> Int32?)"}, + {"get_bit", "Int32$get_bit", "func(x:Int32, bit_index:Int -> Bool)"}, + {"hex", "Int32$hex", "func(i:Int32, digits=0, uppercase=yes, prefix=yes -> Text)"}, + {"is_between", "Int32$is_between", "func(x:Int32,low:Int32,high:Int32 -> Bool)"}, + {"max", "Int32$max", "Int32"}, {"min", "Int32$min", "Int32"}, + {"modulo", "Int32$modulo", "func(x,y:Int32 -> Int32)"}, + {"modulo1", "Int32$modulo1", "func(x,y:Int32 -> Int32)"}, + {"octal", "Int32$octal", "func(i:Int32, digits=0, prefix=yes -> Text)"}, + {"onward", "Int32$onward", "func(first:Int32,step=Int32(1) -> func(->Int32?))"}, + {"to", "Int32$to", "func(first:Int32,last:Int32,step:Int32?=none -> func(->Int32?))"}, + {"unsigned_left_shifted", "Int32$unsigned_left_shifted", "func(x:Int32,y:Int32 -> Int32)"}, + {"unsigned_right_shifted", "Int32$unsigned_right_shifted", "func(x:Int32,y:Int32 -> Int32)"}, + {"wrapping_minus", "Int32$wrapping_minus", "func(x:Int32,y:Int32 -> Int32)"}, + {"wrapping_plus", "Int32$wrapping_plus", "func(x:Int32,y:Int32 -> Int32)"}, )}, + {"Int16", Type(IntType, .bits = TYPE_IBITS16), Text("Int16_t"), Text("Int16$info"), + TypedList(ns_entry_t, {"abs", "abs", "func(i:Int16 -> Int16)"}, + {"bits", "Int16$bits", "func(x:Int16 -> [Bool])"}, + {"clamped", "Int16$clamped", "func(x,low,high:Int16 -> Int16)"}, + {"divided_by", "Int16$divided_by", "func(x,y:Int16 -> Int16)"}, + {"gcd", "Int16$gcd", "func(x,y:Int16 -> Int16)"}, + {"parse", "Int16$parse", "func(text:Text, remainder:&Text? = none -> Int16?)"}, + {"get_bit", "Int16$get_bit", "func(x:Int16, bit_index:Int -> Bool)"}, + {"hex", "Int16$hex", "func(i:Int16, digits=0, uppercase=yes, prefix=yes -> Text)"}, + {"is_between", "Int16$is_between", "func(x:Int16,low:Int16,high:Int16 -> Bool)"}, + {"max", "Int16$max", "Int16"}, {"min", "Int16$min", "Int16"}, + {"modulo", "Int16$modulo", "func(x,y:Int16 -> Int16)"}, + {"modulo1", "Int16$modulo1", "func(x,y:Int16 -> Int16)"}, + {"octal", "Int16$octal", "func(i:Int16, digits=0, prefix=yes -> Text)"}, + {"onward", "Int16$onward", "func(first:Int16,step=Int16(1) -> func(->Int16?))"}, + {"to", "Int16$to", "func(first:Int16,last:Int16,step:Int16?=none -> func(->Int16?))"}, + {"unsigned_left_shifted", "Int16$unsigned_left_shifted", "func(x:Int16,y:Int16 -> Int16)"}, + {"unsigned_right_shifted", "Int16$unsigned_right_shifted", "func(x:Int16,y:Int16 -> Int16)"}, + {"wrapping_minus", "Int16$wrapping_minus", "func(x:Int16,y:Int16 -> Int16)"}, + {"wrapping_plus", "Int16$wrapping_plus", "func(x:Int16,y:Int16 -> Int16)"}, )}, + {"Int8", Type(IntType, .bits = TYPE_IBITS8), Text("Int8_t"), Text("Int8$info"), + TypedList( + ns_entry_t, {"abs", "abs", "func(i:Int8 -> Int8)"}, {"bits", "Int8$bits", "func(x:Int8 -> [Bool])"}, + {"clamped", "Int8$clamped", "func(x,low,high:Int8 -> Int8)"}, + {"divided_by", "Int8$divided_by", "func(x,y:Int8 -> Int8)"}, {"gcd", "Int8$gcd", "func(x,y:Int8 -> Int8)"}, + {"parse", "Int8$parse", "func(text:Text, remainder:&Text? = none -> Int8?)"}, + {"get_bit", "Int8$get_bit", "func(x:Int8, bit_index:Int -> Bool)"}, + {"hex", "Int8$hex", "func(i:Int8, digits=0, uppercase=yes, prefix=yes -> Text)"}, + {"is_between", "Int8$is_between", "func(x:Int8,low:Int8,high:Int8 -> Bool)"}, {"max", "Int8$max", "Int8"}, + {"min", "Int8$min", "Int8"}, {"modulo", "Int8$modulo", "func(x,y:Int8 -> Int8)"}, + {"modulo1", "Int8$modulo1", "func(x,y:Int8 -> Int8)"}, + {"octal", "Int8$octal", "func(i:Int8, digits=0, prefix=yes -> Text)"}, + {"onward", "Int8$onward", "func(first:Int8,step=Int8(1) -> func(->Int8?))"}, + {"to", "Int8$to", "func(first:Int8,last:Int8,step:Int8?=none -> func(->Int8?))"}, + {"unsigned_left_shifted", "Int8$unsigned_left_shifted", "func(x:Int8,y:Int8 -> Int8)"}, + {"unsigned_right_shifted", "Int8$unsigned_right_shifted", "func(x:Int8,y:Int8 -> Int8)"}, + {"wrapping_minus", "Int8$wrapping_minus", "func(x:Int8,y:Int8 -> Int8)"}, + {"wrapping_plus", "Int8$wrapping_plus", "func(x:Int8,y:Int8 -> Int8)"}, )}, +#define C(name) {#name, "M_" #name, "Num"} #define F(name) {#name, #name, "func(n:Num -> Num)"} #define F_opt(name) {#name, #name, "func(n:Num -> Num?)"} #define F2(name) {#name, #name, "func(x,y:Num -> Num)"} - {"Num", Type(NumType, .bits=TYPE_NBITS64), Text("Num_t"), Text("Num$info"), TypedList(ns_entry_t, - {"near", "Num$near", "func(x,y:Num, ratio=1e-9, min_epsilon=1e-9 -> Bool)"}, - {"clamped", "Num$clamped", "func(x,low,high:Num -> Num)"}, - {"percent", "Num$percent", "func(n:Num,precision=0.01 -> Text)"}, - {"with_precision", "Num$with_precision", "func(n:Num,precision:Num -> Num)"}, - {"is_between", "Num$is_between", "func(x:Num,low:Num,high:Num -> Bool)"}, - {"isinf", "Num$isinf", "func(n:Num -> Bool)"}, - {"isfinite", "Num$isfinite", "func(n:Num -> Bool)"}, - {"modulo", "Num$mod", "func(x,y:Num -> Num)"}, - {"modulo1", "Num$mod1", "func(x,y:Num -> Num)"}, - C(2_SQRTPI), C(E), C(PI_2), C(2_PI), C(1_PI), C(LN10), C(LN2), C(LOG2E), - C(PI), C(PI_4), C(SQRT2), C(SQRT1_2), - {"INF", "(Num_t)(INFINITY)", "Num"}, - {"TAU", "(Num_t)(2.*M_PI)", "Num"}, - {"mix", "Num$mix", "func(amount,x,y:Num -> Num)"}, - {"parse", "Num$parse", "func(text:Text, remainder:&Text? = none -> Num?)"}, - {"abs", "fabs", "func(n:Num -> Num)"}, - F_opt(acos), F_opt(acosh), F_opt(asin), F(asinh), F(atan), F_opt(atanh), - F(cbrt), F(ceil), F_opt(cos), F(cosh), F(erf), F(erfc), - F(exp), F(exp2), F(expm1), F(floor), F(j0), F(j1), F_opt(log), F_opt(log10), F_opt(log1p), - F_opt(log2), F(logb), F(rint), F(round), F(significand), F_opt(sin), F(sinh), F_opt(sqrt), - F_opt(tan), F(tanh), F_opt(tgamma), F(trunc), F_opt(y0), F_opt(y1), - F2(atan2), F2(copysign), F2(fdim), F2(hypot), F2(nextafter), - )}, + {"Num", Type(NumType, .bits = TYPE_NBITS64), Text("Num_t"), Text("Num$info"), + TypedList(ns_entry_t, {"near", "Num$near", "func(x,y:Num, ratio=1e-9, min_epsilon=1e-9 -> Bool)"}, + {"clamped", "Num$clamped", "func(x,low,high:Num -> Num)"}, + {"percent", "Num$percent", "func(n:Num,precision=0.01 -> Text)"}, + {"with_precision", "Num$with_precision", "func(n:Num,precision:Num -> Num)"}, + {"is_between", "Num$is_between", "func(x:Num,low:Num,high:Num -> Bool)"}, + {"isinf", "Num$isinf", "func(n:Num -> Bool)"}, {"isfinite", "Num$isfinite", "func(n:Num -> Bool)"}, + {"modulo", "Num$mod", "func(x,y:Num -> Num)"}, {"modulo1", "Num$mod1", "func(x,y:Num -> Num)"}, + C(2_SQRTPI), C(E), C(PI_2), C(2_PI), C(1_PI), C(LN10), C(LN2), C(LOG2E), C(PI), C(PI_4), C(SQRT2), + C(SQRT1_2), {"INF", "(Num_t)(INFINITY)", "Num"}, {"TAU", "(Num_t)(2.*M_PI)", "Num"}, + {"mix", "Num$mix", "func(amount,x,y:Num -> Num)"}, + {"parse", "Num$parse", "func(text:Text, remainder:&Text? = none -> Num?)"}, + {"abs", "fabs", "func(n:Num -> Num)"}, F_opt(acos), F_opt(acosh), F_opt(asin), F(asinh), F(atan), + F_opt(atanh), F(cbrt), F(ceil), F_opt(cos), F(cosh), F(erf), F(erfc), F(exp), F(exp2), F(expm1), + F(floor), F(j0), F(j1), F_opt(log), F_opt(log10), F_opt(log1p), F_opt(log2), F(logb), F(rint), + F(round), F(significand), F_opt(sin), F(sinh), F_opt(sqrt), F_opt(tan), F(tanh), F_opt(tgamma), + F(trunc), F_opt(y0), F_opt(y1), F2(atan2), F2(copysign), F2(fdim), F2(hypot), F2(nextafter), )}, #undef F2 #undef F_opt #undef F #undef C -#define C(name) {#name, "(Num32_t)(M_"#name")", "Num32"} -#define F(name) {#name, #name"f", "func(n:Num32 -> Num32)"} -#define F_opt(name) {#name, #name"f", "func(n:Num32 -> Num32?)"} -#define F2(name) {#name, #name"f", "func(x,y:Num32 -> Num32)"} - {"Num32", Type(NumType, .bits=TYPE_NBITS32), Text("Num32_t"), Text("Num32$info"), TypedList(ns_entry_t, - {"near", "Num32$near", "func(x,y:Num32, ratio=Num32(1e-9), min_epsilon=Num32(1e-9) -> Bool)"}, - {"clamped", "Num32$clamped", "func(x,low,high:Num32 -> Num32)"}, - {"percent", "Num32$percent", "func(n:Num32,precision=Num32(.01) -> Text)"}, - {"with_precision", "Num32$with_precision", "func(n:Num32,precision:Num32 -> Num32)"}, - {"is_between", "Num32$is_between", "func(x:Num32,low:Num32,high:Num32 -> Bool)"}, - {"isinf", "Num32$isinf", "func(n:Num32 -> Bool)"}, - {"isfinite", "Num32$isfinite", "func(n:Num32 -> Bool)"}, - C(2_SQRTPI), C(E), C(PI_2), C(2_PI), C(1_PI), C(LN10), C(LN2), C(LOG2E), - C(PI), C(PI_4), C(SQRT2), C(SQRT1_2), - {"INF", "(Num32_t)(INFINITY)", "Num32"}, - {"TAU", "(Num32_t)(2.f*M_PI)", "Num32"}, - {"mix", "Num32$mix", "func(amount,x,y:Num32 -> Num32)"}, - {"parse", "Num32$parse", "func(text:Text, remainder:&Text? = none -> Num32?)"}, - {"abs", "fabsf", "func(n:Num32 -> Num32)"}, - {"modulo", "Num32$mod", "func(x,y:Num32 -> Num32)"}, - {"modulo1", "Num32$mod1", "func(x,y:Num32 -> Num32)"}, - F_opt(acos), F_opt(acosh), F_opt(asin), F(asinh), F(atan), F_opt(atanh), - F(cbrt), F(ceil), F_opt(cos), F(cosh), F(erf), F(erfc), - F(exp), F(exp2), F(expm1), F(floor), F(j0), F(j1), F_opt(log), F_opt(log10), F_opt(log1p), - F_opt(log2), F(logb), F(rint), F(round), F(significand), F_opt(sin), F(sinh), F_opt(sqrt), - F_opt(tan), F(tanh), F_opt(tgamma), F(trunc), F_opt(y0), F_opt(y1), - F2(atan2), F2(copysign), F2(fdim), F2(hypot), F2(nextafter), - )}, - {"CString", Type(CStringType), Text("char*"), Text("CString$info"), TypedList(ns_entry_t, - {"as_text", "Text$from_str", "func(str:CString -> Text)"}, - )}, +#define C(name) {#name, "(Num32_t)(M_" #name ")", "Num32"} +#define F(name) {#name, #name "f", "func(n:Num32 -> Num32)"} +#define F_opt(name) {#name, #name "f", "func(n:Num32 -> Num32?)"} +#define F2(name) {#name, #name "f", "func(x,y:Num32 -> Num32)"} + {"Num32", Type(NumType, .bits = TYPE_NBITS32), Text("Num32_t"), Text("Num32$info"), + TypedList( + ns_entry_t, {"near", "Num32$near", "func(x,y:Num32, ratio=Num32(1e-9), min_epsilon=Num32(1e-9) -> Bool)"}, + {"clamped", "Num32$clamped", "func(x,low,high:Num32 -> Num32)"}, + {"percent", "Num32$percent", "func(n:Num32,precision=Num32(.01) -> Text)"}, + {"with_precision", "Num32$with_precision", "func(n:Num32,precision:Num32 -> Num32)"}, + {"is_between", "Num32$is_between", "func(x:Num32,low:Num32,high:Num32 -> Bool)"}, + {"isinf", "Num32$isinf", "func(n:Num32 -> Bool)"}, {"isfinite", "Num32$isfinite", "func(n:Num32 -> Bool)"}, + C(2_SQRTPI), C(E), C(PI_2), C(2_PI), C(1_PI), C(LN10), C(LN2), C(LOG2E), C(PI), C(PI_4), C(SQRT2), + C(SQRT1_2), {"INF", "(Num32_t)(INFINITY)", "Num32"}, {"TAU", "(Num32_t)(2.f*M_PI)", "Num32"}, + {"mix", "Num32$mix", "func(amount,x,y:Num32 -> Num32)"}, + {"parse", "Num32$parse", "func(text:Text, remainder:&Text? = none -> Num32?)"}, + {"abs", "fabsf", "func(n:Num32 -> Num32)"}, {"modulo", "Num32$mod", "func(x,y:Num32 -> Num32)"}, + {"modulo1", "Num32$mod1", "func(x,y:Num32 -> Num32)"}, F_opt(acos), F_opt(acosh), F_opt(asin), F(asinh), + F(atan), F_opt(atanh), F(cbrt), F(ceil), F_opt(cos), F(cosh), F(erf), F(erfc), F(exp), F(exp2), F(expm1), + F(floor), F(j0), F(j1), F_opt(log), F_opt(log10), F_opt(log1p), F_opt(log2), F(logb), F(rint), F(round), + F(significand), F_opt(sin), F(sinh), F_opt(sqrt), F_opt(tan), F(tanh), F_opt(tgamma), F(trunc), F_opt(y0), + F_opt(y1), F2(atan2), F2(copysign), F2(fdim), F2(hypot), F2(nextafter), )}, + {"CString", Type(CStringType), Text("char*"), Text("CString$info"), + TypedList(ns_entry_t, {"as_text", "Text$from_str", "func(str:CString -> Text)"}, )}, #undef F2 #undef F_opt #undef F #undef C - {"PathType", PATH_TYPE_TYPE, Text("PathType_t"), Text("PathType$info"), TypedList(ns_entry_t, - {"Relative", "((PathType_t){.$tag=PATH_RELATIVE})", "PathType"}, - {"Absolute", "((PathType_t){.$tag=PATH_ABSOLUTE})", "PathType"}, - {"Home", "((PathType_t){.$tag=PATH_HOME})", "PathType"}, - )}, - {"Path", PATH_TYPE, Text("Path_t"), Text("Path$info"), TypedList(ns_entry_t, - {"accessed", "Path$accessed", "func(path:Path, follow_symlinks=yes -> Int64?)"}, - {"append", "Path$append", "func(path:Path, text:Text, permissions=Int32(0o644))"}, - {"append_bytes", "Path$append_bytes", "func(path:Path, bytes:[Byte], permissions=Int32(0o644))"}, - {"base_name", "Path$base_name", "func(path:Path -> Text)"}, - {"by_line", "Path$by_line", "func(path:Path -> func(->Text?)?)"}, - {"can_execute", "Path$can_execute", "func(path:Path -> Bool)"}, - {"can_read", "Path$can_read", "func(path:Path -> Bool)"}, - {"can_write", "Path$can_write", "func(path:Path -> Bool)"}, - {"changed", "Path$changed", "func(path:Path, follow_symlinks=yes -> Int64?)"}, - {"child", "Path$child", "func(path:Path, child:Text -> Path)"}, - {"children", "Path$children", "func(path:Path, include_hidden=no -> [Path])"}, - {"concatenated_with", "Path$concat", "func(a,b:Path -> Path)"}, - {"create_directory", "Path$create_directory", "func(path:Path, permissions=Int32(0o755))"}, - {"current_dir", "Path$current_dir", "func(->Path)"}, - {"exists", "Path$exists", "func(path:Path -> Bool)"}, - {"expand_home", "Path$expand_home", "func(path:Path -> Path)"}, - {"extension", "Path$extension", "func(path:Path, full=yes -> Text)"}, - {"files", "Path$children", "func(path:Path, include_hidden=no -> [Path])"}, - {"from_components", "Path$from_components", "func(components:[Text] -> Path)"}, - {"glob", "Path$glob", "func(path:Path -> [Path])"}, - {"group", "Path$group", "func(path:Path, follow_symlinks=yes -> Text?)"}, - {"has_extension", "Path$has_extension", "func(path:Path, extension:Text -> Bool)"}, - {"is_directory", "Path$is_directory", "func(path:Path, follow_symlinks=yes -> Bool)"}, - {"is_file", "Path$is_file", "func(path:Path, follow_symlinks=yes -> Bool)"}, - {"is_pipe", "Path$is_pipe", "func(path:Path, follow_symlinks=yes -> Bool)"}, - {"is_socket", "Path$is_socket", "func(path:Path, follow_symlinks=yes -> Bool)"}, - {"is_symlink", "Path$is_symlink", "func(path:Path -> Bool)"}, - {"modified", "Path$modified", "func(path:Path, follow_symlinks=yes -> Int64?)"}, - {"owner", "Path$owner", "func(path:Path, follow_symlinks=yes -> Text?)"}, - {"parent", "Path$parent", "func(path:Path -> Path)"}, - {"read", "Path$read", "func(path:Path -> Text?)"}, - {"read_bytes", "Path$read_bytes", "func(path:Path, limit:Int?=none -> [Byte]?)"}, - {"relative_to", "Path$relative_to", "func(path:Path, relative_to:Path -> Path)"}, - {"remove", "Path$remove", "func(path:Path, ignore_missing=no)"}, - {"resolved", "Path$resolved", "func(path:Path, relative_to=(./) -> Path)"}, - {"set_owner", "Path$set_owner", "func(path:Path, owner:Text?=none, group:Text?=none, follow_symlinks=yes)"}, - {"sibling", "Path$sibling", "func(path:Path, name:Text -> Path)"}, - {"subdirectories", "Path$children", "func(path:Path, include_hidden=no -> [Path])"}, - {"unique_directory", "Path$unique_directory", "func(path:Path -> Path)"}, - {"write", "Path$write", "func(path:Path, text:Text, permissions=Int32(0o644))"}, - {"write_bytes", "Path$write_bytes", "func(path:Path, bytes:[Byte], permissions=Int32(0o644))"}, - {"write_unique", "Path$write_unique", "func(path:Path, text:Text -> Path)"}, - {"write_unique_bytes", "Path$write_unique_bytes", "func(path:Path, bytes:[Byte] -> Path)"}, - )}, - {"Text", TEXT_TYPE, Text("Text_t"), Text("Text$info"), TypedList(ns_entry_t, - {"as_c_string", "Text$as_c_string", "func(text:Text -> CString)"}, - {"at", "Text$cluster", "func(text:Text, index:Int -> Text)"}, - {"by_line", "Text$by_line", "func(text:Text -> func(->Text?))"}, - {"by_split", "Text$by_split", "func(text:Text, delimiter='' -> func(->Text?))"}, - {"by_split_any", "Text$by_split_any", "func(text:Text, delimiters=' \\t\\r\\n' -> func(->Text?))"}, - {"bytes", "Text$utf8_bytes", "func(text:Text -> [Byte])"}, - {"caseless_equals", "Text$equal_ignoring_case", "func(a,b:Text, language='C' -> Bool)"}, - {"codepoint_names", "Text$codepoint_names", "func(text:Text -> [Text])"}, - {"ends_with", "Text$ends_with", "func(text,suffix:Text, remainder:&Text? = none -> Bool)"}, - {"from", "Text$from", "func(text:Text, first:Int -> Text)"}, - {"from_bytes", "Text$from_bytes", "func(bytes:[Byte] -> Text?)"}, - {"from_c_string", "Text$from_str", "func(str:CString -> Text?)"}, - {"from_codepoint_names", "Text$from_codepoint_names", "func(codepoint_names:[Text] -> Text?)"}, - {"from_codepoints", "Text$from_codepoints", "func(codepoints:[Int32] -> Text)"}, - {"from_text", "Path$from_text", "func(text:Text -> Path)"}, - {"has", "Text$has", "func(text:Text, target:Text -> Bool)"}, - {"join", "Text$join", "func(glue:Text, pieces:[Text] -> Text)"}, - {"layout", "Text$layout", "func(text:Text -> Text)"}, - {"left_pad", "Text$left_pad", "func(text:Text, count:Int, pad=' ', language='C' -> Text)"}, - {"lines", "Text$lines", "func(text:Text -> [Text])"}, - {"lower", "Text$lower", "func(text:Text, language='C' -> Text)"}, - {"memory_size", "Text$memory_size", "func(text:Text -> Int)"}, - {"middle_pad", "Text$middle_pad", "func(text:Text, count:Int, pad=' ', language='C' -> Text)"}, - {"quoted", "Text$quoted", "func(text:Text, color=no, quotation_mark='\"' -> Text)"}, - {"repeat", "Text$repeat", "func(text:Text, count:Int -> Text)"}, - {"replace", "Text$replace", "func(text:Text, target:Text, replacement:Text -> Text)"}, - {"reversed", "Text$reversed", "func(text:Text -> Text)"}, - {"right_pad", "Text$right_pad", "func(text:Text, count:Int, pad=' ', language='C' -> Text)"}, - {"slice", "Text$slice", "func(text:Text, from=1, to=-1 -> Text)"}, - {"split", "Text$split", "func(text:Text, delimiter='' -> [Text])"}, - {"split_any", "Text$split_any", "func(text:Text, delimiters=' \\t\\r\\n' -> [Text])"}, - {"starts_with", "Text$starts_with", "func(text,prefix:Text, remainder:&Text? = none -> Bool)"}, - {"title", "Text$title", "func(text:Text, language='C' -> Text)"}, - {"to", "Text$to", "func(text:Text, last:Int -> Text)"}, - {"translate", "Text$translate", "func(text:Text, translations:{Text=Text} -> Text)"}, - {"trim", "Text$trim", "func(text:Text, to_trim=\" \t\r\n\", left=yes, right=yes -> Text)"}, - {"upper", "Text$upper", "func(text:Text, language='C' -> Text)"}, - {"utf32_codepoints", "Text$utf32_codepoints", "func(text:Text -> [Int32])"}, - {"width", "Text$width", "func(text:Text, language='C' -> Int)"}, - {"without_prefix", "Text$without_prefix", "func(text,prefix:Text -> Text)"}, - {"without_suffix", "Text$without_suffix", "func(text,suffix:Text -> Text)"}, - )}, + {"PathType", PATH_TYPE_TYPE, Text("PathType_t"), Text("PathType$info"), + TypedList(ns_entry_t, {"Relative", "((PathType_t){.$tag=PATH_RELATIVE})", "PathType"}, + {"Absolute", "((PathType_t){.$tag=PATH_ABSOLUTE})", "PathType"}, + {"Home", "((PathType_t){.$tag=PATH_HOME})", "PathType"}, )}, + {"Path", PATH_TYPE, Text("Path_t"), Text("Path$info"), + TypedList( + ns_entry_t, {"accessed", "Path$accessed", "func(path:Path, follow_symlinks=yes -> Int64?)"}, + {"append", "Path$append", "func(path:Path, text:Text, permissions=Int32(0o644))"}, + {"append_bytes", "Path$append_bytes", "func(path:Path, bytes:[Byte], permissions=Int32(0o644))"}, + {"base_name", "Path$base_name", "func(path:Path -> Text)"}, + {"by_line", "Path$by_line", "func(path:Path -> func(->Text?)?)"}, + {"can_execute", "Path$can_execute", "func(path:Path -> Bool)"}, + {"can_read", "Path$can_read", "func(path:Path -> Bool)"}, + {"can_write", "Path$can_write", "func(path:Path -> Bool)"}, + {"changed", "Path$changed", "func(path:Path, follow_symlinks=yes -> Int64?)"}, + {"child", "Path$child", "func(path:Path, child:Text -> Path)"}, + {"children", "Path$children", "func(path:Path, include_hidden=no -> [Path])"}, + {"concatenated_with", "Path$concat", "func(a,b:Path -> Path)"}, + {"create_directory", "Path$create_directory", "func(path:Path, permissions=Int32(0o755))"}, + {"current_dir", "Path$current_dir", "func(->Path)"}, {"exists", "Path$exists", "func(path:Path -> Bool)"}, + {"expand_home", "Path$expand_home", "func(path:Path -> Path)"}, + {"extension", "Path$extension", "func(path:Path, full=yes -> Text)"}, + {"files", "Path$children", "func(path:Path, include_hidden=no -> [Path])"}, + {"from_components", "Path$from_components", "func(components:[Text] -> Path)"}, + {"glob", "Path$glob", "func(path:Path -> [Path])"}, + {"group", "Path$group", "func(path:Path, follow_symlinks=yes -> Text?)"}, + {"has_extension", "Path$has_extension", "func(path:Path, extension:Text -> Bool)"}, + {"is_directory", "Path$is_directory", "func(path:Path, follow_symlinks=yes -> Bool)"}, + {"is_file", "Path$is_file", "func(path:Path, follow_symlinks=yes -> Bool)"}, + {"is_pipe", "Path$is_pipe", "func(path:Path, follow_symlinks=yes -> Bool)"}, + {"is_socket", "Path$is_socket", "func(path:Path, follow_symlinks=yes -> Bool)"}, + {"is_symlink", "Path$is_symlink", "func(path:Path -> Bool)"}, + {"modified", "Path$modified", "func(path:Path, follow_symlinks=yes -> Int64?)"}, + {"owner", "Path$owner", "func(path:Path, follow_symlinks=yes -> Text?)"}, + {"parent", "Path$parent", "func(path:Path -> Path)"}, {"read", "Path$read", "func(path:Path -> Text?)"}, + {"read_bytes", "Path$read_bytes", "func(path:Path, limit:Int?=none -> [Byte]?)"}, + {"relative_to", "Path$relative_to", "func(path:Path, relative_to:Path -> Path)"}, + {"remove", "Path$remove", "func(path:Path, ignore_missing=no)"}, + {"resolved", "Path$resolved", "func(path:Path, relative_to=(./) -> Path)"}, + {"set_owner", "Path$set_owner", + "func(path:Path, owner:Text?=none, group:Text?=none, follow_symlinks=yes)"}, + {"sibling", "Path$sibling", "func(path:Path, name:Text -> Path)"}, + {"subdirectories", "Path$children", "func(path:Path, include_hidden=no -> [Path])"}, + {"unique_directory", "Path$unique_directory", "func(path:Path -> Path)"}, + {"write", "Path$write", "func(path:Path, text:Text, permissions=Int32(0o644))"}, + {"write_bytes", "Path$write_bytes", "func(path:Path, bytes:[Byte], permissions=Int32(0o644))"}, + {"write_unique", "Path$write_unique", "func(path:Path, text:Text -> Path)"}, + {"write_unique_bytes", "Path$write_unique_bytes", "func(path:Path, bytes:[Byte] -> Path)"}, )}, + {"Text", TEXT_TYPE, Text("Text_t"), Text("Text$info"), + TypedList(ns_entry_t, {"as_c_string", "Text$as_c_string", "func(text:Text -> CString)"}, + {"at", "Text$cluster", "func(text:Text, index:Int -> Text)"}, + {"by_line", "Text$by_line", "func(text:Text -> func(->Text?))"}, + {"by_split", "Text$by_split", "func(text:Text, delimiter='' -> func(->Text?))"}, + {"by_split_any", "Text$by_split_any", "func(text:Text, delimiters=' \\t\\r\\n' -> func(->Text?))"}, + {"bytes", "Text$utf8_bytes", "func(text:Text -> [Byte])"}, + {"caseless_equals", "Text$equal_ignoring_case", "func(a,b:Text, language='C' -> Bool)"}, + {"codepoint_names", "Text$codepoint_names", "func(text:Text -> [Text])"}, + {"ends_with", "Text$ends_with", "func(text,suffix:Text, remainder:&Text? = none -> Bool)"}, + {"from", "Text$from", "func(text:Text, first:Int -> Text)"}, + {"from_bytes", "Text$from_bytes", "func(bytes:[Byte] -> Text?)"}, + {"from_c_string", "Text$from_str", "func(str:CString -> Text?)"}, + {"from_codepoint_names", "Text$from_codepoint_names", "func(codepoint_names:[Text] -> Text?)"}, + {"from_codepoints", "Text$from_codepoints", "func(codepoints:[Int32] -> Text)"}, + {"from_text", "Path$from_text", "func(text:Text -> Path)"}, + {"has", "Text$has", "func(text:Text, target:Text -> Bool)"}, + {"join", "Text$join", "func(glue:Text, pieces:[Text] -> Text)"}, + {"layout", "Text$layout", "func(text:Text -> Text)"}, + {"left_pad", "Text$left_pad", "func(text:Text, count:Int, pad=' ', language='C' -> Text)"}, + {"lines", "Text$lines", "func(text:Text -> [Text])"}, + {"lower", "Text$lower", "func(text:Text, language='C' -> Text)"}, + {"memory_size", "Text$memory_size", "func(text:Text -> Int)"}, + {"middle_pad", "Text$middle_pad", "func(text:Text, count:Int, pad=' ', language='C' -> Text)"}, + {"quoted", "Text$quoted", "func(text:Text, color=no, quotation_mark='\"' -> Text)"}, + {"repeat", "Text$repeat", "func(text:Text, count:Int -> Text)"}, + {"replace", "Text$replace", "func(text:Text, target:Text, replacement:Text -> Text)"}, + {"reversed", "Text$reversed", "func(text:Text -> Text)"}, + {"right_pad", "Text$right_pad", "func(text:Text, count:Int, pad=' ', language='C' -> Text)"}, + {"slice", "Text$slice", "func(text:Text, from=1, to=-1 -> Text)"}, + {"split", "Text$split", "func(text:Text, delimiter='' -> [Text])"}, + {"split_any", "Text$split_any", "func(text:Text, delimiters=' \\t\\r\\n' -> [Text])"}, + {"starts_with", "Text$starts_with", "func(text,prefix:Text, remainder:&Text? = none -> Bool)"}, + {"title", "Text$title", "func(text:Text, language='C' -> Text)"}, + {"to", "Text$to", "func(text:Text, last:Int -> Text)"}, + {"translate", "Text$translate", "func(text:Text, translations:{Text=Text} -> Text)"}, + {"trim", "Text$trim", "func(text:Text, to_trim=\" \t\r\n\", left=yes, right=yes -> Text)"}, + {"upper", "Text$upper", "func(text:Text, language='C' -> Text)"}, + {"utf32_codepoints", "Text$utf32_codepoints", "func(text:Text -> [Int32])"}, + {"width", "Text$width", "func(text:Text, language='C' -> Int)"}, + {"without_prefix", "Text$without_prefix", "func(text,prefix:Text -> Text)"}, + {"without_suffix", "Text$without_suffix", "func(text,suffix:Text -> Text)"}, )}, }; - for (size_t i = 0; i < sizeof(global_types)/sizeof(global_types[0]); i++) { + for (size_t i = 0; i < sizeof(global_types) / sizeof(global_types[0]); i++) { env_t *ns_env = NULL; switch (global_types[i].type->tag) { - case TextType: - ns_env = Match(global_types[i].type, TextType)->env; - break; - case StructType: - ns_env = Match(global_types[i].type, StructType)->env; - break; - case EnumType: - ns_env = Match(global_types[i].type, EnumType)->env; - break; + case TextType: ns_env = Match(global_types[i].type, TextType)->env; break; + case StructType: ns_env = Match(global_types[i].type, StructType)->env; break; + case EnumType: ns_env = Match(global_types[i].type, EnumType)->env; break; default: break; } if (ns_env == NULL) ns_env = namespace_env(env, global_types[i].name); - binding_t *binding = new(binding_t, .type=Type(TypeInfoType, .name=global_types[i].name, .type=global_types[i].type, .env=ns_env), - .code=global_types[i].typeinfo); + binding_t *binding = + new (binding_t, + .type = Type(TypeInfoType, .name = global_types[i].name, .type = global_types[i].type, .env = ns_env), + .code = global_types[i].typeinfo); Table$str_set(env->globals, global_types[i].name, binding); Table$str_set(env->types, global_types[i].name, global_types[i].type); } - for (size_t i = 0; i < sizeof(global_types)/sizeof(global_types[0]); i++) { + for (size_t i = 0; i < sizeof(global_types) / sizeof(global_types[0]); i++) { binding_t *type_binding = Table$str_get(*env->globals, global_types[i].name); assert(type_binding); env_t *ns_env = Match(type_binding->type, TypeInfoType)->env; for (int64_t j = 0; j < global_types[i].namespace.length; j++) { - ns_entry_t *entry = global_types[i].namespace.data + j*global_types[i].namespace.stride; + ns_entry_t *entry = global_types[i].namespace.data + j * global_types[i].namespace.stride; type_t *type = parse_type_string(ns_env, entry->type_str); if (!type) compiler_err(NULL, NULL, NULL, "Couldn't parse type string: ", entry->type_str); if (type->tag == ClosureType) type = Match(type, ClosureType)->fn; @@ -415,106 +371,80 @@ env_t *global_env(bool source_mapping) } } - // Conversion constructors: -#define ADD_CONSTRUCTORS(type_name, ...) do {\ - env_t *ns_env = namespace_env(env, type_name); \ - struct { const char *c_name, *type_str; } constructor_infos[] = {__VA_ARGS__}; \ - for (size_t i = 0; i < sizeof(constructor_infos)/sizeof(constructor_infos[0]); i++) { \ - type_t *t = parse_type_string(ns_env, constructor_infos[i].type_str); \ - List$insert(&ns_env->namespace->constructors, \ - ((binding_t[1]){{.code=Text$from_str(constructor_infos[i].c_name), \ - .type=Match(t, ClosureType)->fn}}), I(0), sizeof(binding_t)); \ - } \ -} while (0) - - ADD_CONSTRUCTORS("Bool", - {"Bool$from_byte", "func(b:Byte -> Bool)"}, - {"Bool$from_int8", "func(i:Int8 -> Bool)"}, - {"Bool$from_int16", "func(i:Int16 -> Bool)"}, - {"Bool$from_int32", "func(i:Int32 -> Bool)"}, - {"Bool$from_int64", "func(i:Int64 -> Bool)"}, - {"Bool$from_int", "func(i:Int -> Bool)"}); - ADD_CONSTRUCTORS("Byte", - {"Byte$from_bool", "func(b:Bool -> Byte)"}, - {"Byte$from_int8", "func(i:Int8 -> Byte)"}, +#define ADD_CONSTRUCTORS(type_name, ...) \ + do { \ + env_t *ns_env = namespace_env(env, type_name); \ + struct { \ + const char *c_name, *type_str; \ + } constructor_infos[] = {__VA_ARGS__}; \ + for (size_t i = 0; i < sizeof(constructor_infos) / sizeof(constructor_infos[0]); i++) { \ + type_t *t = parse_type_string(ns_env, constructor_infos[i].type_str); \ + List$insert(&ns_env->namespace->constructors, \ + ((binding_t[1]){ \ + {.code = Text$from_str(constructor_infos[i].c_name), .type = Match(t, ClosureType)->fn}}), \ + I(0), sizeof(binding_t)); \ + } \ + } while (0) + + ADD_CONSTRUCTORS("Bool", {"Bool$from_byte", "func(b:Byte -> Bool)"}, {"Bool$from_int8", "func(i:Int8 -> Bool)"}, + {"Bool$from_int16", "func(i:Int16 -> Bool)"}, {"Bool$from_int32", "func(i:Int32 -> Bool)"}, + {"Bool$from_int64", "func(i:Int64 -> Bool)"}, {"Bool$from_int", "func(i:Int -> Bool)"}); + ADD_CONSTRUCTORS("Byte", {"Byte$from_bool", "func(b:Bool -> Byte)"}, {"Byte$from_int8", "func(i:Int8 -> Byte)"}, {"Byte$from_int16", "func(i:Int16, truncate=no -> Byte)"}, {"Byte$from_int32", "func(i:Int32, truncate=no -> Byte)"}, {"Byte$from_int64", "func(i:Int64, truncate=no -> Byte)"}, {"Byte$from_int", "func(i:Int, truncate=no -> Byte)"}); - ADD_CONSTRUCTORS("Int", - {"Int$from_bool", "func(b:Bool -> Int)"}, - {"Int$from_byte", "func(b:Byte -> Int)"}, - {"Int$from_int8", "func(i:Int8 -> Int)"}, - {"Int$from_int16", "func(i:Int16 -> Int)"}, - {"Int$from_int32", "func(i:Int32 -> Int)"}, - {"Int$from_int64", "func(i:Int64 -> Int)"}, + ADD_CONSTRUCTORS("Int", {"Int$from_bool", "func(b:Bool -> Int)"}, {"Int$from_byte", "func(b:Byte -> Int)"}, + {"Int$from_int8", "func(i:Int8 -> Int)"}, {"Int$from_int16", "func(i:Int16 -> Int)"}, + {"Int$from_int32", "func(i:Int32 -> Int)"}, {"Int$from_int64", "func(i:Int64 -> Int)"}, {"Int$from_num", "func(n:Num, truncate=no -> Int)"}, {"Int$from_num32", "func(n:Num32, truncate=no -> Int)"}); - ADD_CONSTRUCTORS("Int64", - {"Int64$from_bool", "func(b:Bool -> Int64)"}, - {"Int64$from_byte", "func(b:Byte -> Int64)"}, - {"Int64$from_int8", "func(i:Int8 -> Int64)"}, - {"Int64$from_int16", "func(i:Int16 -> Int64)"}, - {"Int64$from_int32", "func(i:Int32 -> Int64)"}, + ADD_CONSTRUCTORS("Int64", {"Int64$from_bool", "func(b:Bool -> Int64)"}, + {"Int64$from_byte", "func(b:Byte -> Int64)"}, {"Int64$from_int8", "func(i:Int8 -> Int64)"}, + {"Int64$from_int16", "func(i:Int16 -> Int64)"}, {"Int64$from_int32", "func(i:Int32 -> Int64)"}, {"Int64$from_int", "func(i:Int, truncate=no -> Int64)"}, {"Int64$from_num", "func(n:Num, truncate=no -> Int64)"}, {"Int64$from_num32", "func(n:Num32, truncate=no -> Int64)"}); - ADD_CONSTRUCTORS("Int32", - {"Int32$from_bool", "func(b:Bool -> Int32)"}, - {"Int32$from_byte", "func(b:Byte -> Int32)"}, - {"Int32$from_int8", "func(i:Int8 -> Int32)"}, + ADD_CONSTRUCTORS("Int32", {"Int32$from_bool", "func(b:Bool -> Int32)"}, + {"Int32$from_byte", "func(b:Byte -> Int32)"}, {"Int32$from_int8", "func(i:Int8 -> Int32)"}, {"Int32$from_int16", "func(i:Int16 -> Int32)"}, {"Int32$from_int64", "func(i:Int64, truncate=no -> Int32)"}, {"Int32$from_int", "func(i:Int, truncate=no -> Int32)"}, {"Int32$from_num", "func(n:Num, truncate=no -> Int32)"}, {"Int32$from_num32", "func(n:Num32, truncate=no -> Int32)"}); - ADD_CONSTRUCTORS("Int16", - {"Int16$from_bool", "func(b:Bool -> Int16)"}, - {"Int16$from_byte", "func(b:Byte -> Int16)"}, - {"Int16$from_int8", "func(i:Int8 -> Int16)"}, + ADD_CONSTRUCTORS("Int16", {"Int16$from_bool", "func(b:Bool -> Int16)"}, + {"Int16$from_byte", "func(b:Byte -> Int16)"}, {"Int16$from_int8", "func(i:Int8 -> Int16)"}, {"Int16$from_int32", "func(i:Int32, truncate=no -> Int16)"}, {"Int16$from_int64", "func(i:Int64, truncate=no -> Int16)"}, {"Int16$from_int", "func(i:Int, truncate=no -> Int16)"}, {"Int16$from_num", "func(n:Num, truncate=no -> Int16)"}, {"Int16$from_num32", "func(n:Num32, truncate=no -> Int16)"}); - ADD_CONSTRUCTORS("Int8", - {"Int8$from_bool", "func(b:Bool -> Int8)"}, - {"Int8$from_byte", "func(b:Byte -> Int8)"}, + ADD_CONSTRUCTORS("Int8", {"Int8$from_bool", "func(b:Bool -> Int8)"}, {"Int8$from_byte", "func(b:Byte -> Int8)"}, {"Int8$from_int16", "func(i:Int16, truncate=no -> Int8)"}, {"Int8$from_int32", "func(i:Int32, truncate=no -> Int8)"}, {"Int8$from_int64", "func(i:Int64, truncate=no -> Int8)"}, {"Int8$from_int", "func(i:Int, truncate=no -> Int8)"}, {"Int8$from_num", "func(n:Num, truncate=no -> Int8)"}, {"Int8$from_num32", "func(n:Num32, truncate=no -> Int8)"}); - ADD_CONSTRUCTORS("Num", - {"Num$from_bool", "func(b:Bool -> Num)"}, - {"Num$from_byte", "func(b:Byte -> Num)"}, - {"Num$from_int8", "func(i:Int8 -> Num)"}, - {"Num$from_int16", "func(i:Int16 -> Num)"}, + ADD_CONSTRUCTORS("Num", {"Num$from_bool", "func(b:Bool -> Num)"}, {"Num$from_byte", "func(b:Byte -> Num)"}, + {"Num$from_int8", "func(i:Int8 -> Num)"}, {"Num$from_int16", "func(i:Int16 -> Num)"}, {"Num$from_int32", "func(i:Int32 -> Num)"}, {"Num$from_int64", "func(i:Int64, truncate=no -> Num)"}, - {"Num$from_int", "func(i:Int, truncate=no -> Num)"}, - {"Num$from_num32", "func(n:Num32 -> Num)"}); - ADD_CONSTRUCTORS("Num32", - {"Num32$from_bool", "func(b:Bool -> Num32)"}, - {"Num32$from_byte", "func(b:Byte -> Num32)"}, - {"Num32$from_int8", "func(i:Int8 -> Num32)"}, - {"Num32$from_int16", "func(i:Int16 -> Num32)"}, - {"Num32$from_int32", "func(i:Int32, truncate=no -> Num32)"}, - {"Num32$from_int64", "func(i:Int64, truncate=no -> Num32)"}, - {"Num32$from_int", "func(i:Int, truncate=no -> Num32)"}, - {"Num32$from_num", "func(n:Num -> Num32)"}); - ADD_CONSTRUCTORS("Path", - {"Path$escape_text", "func(text:Text -> Path)"}, - {"Path$escape_path", "func(path:Path -> Path)"}, - {"Int$value_as_text", "func(i:Int -> Path)"}); + {"Num$from_int", "func(i:Int, truncate=no -> Num)"}, {"Num$from_num32", "func(n:Num32 -> Num)"}); + ADD_CONSTRUCTORS( + "Num32", {"Num32$from_bool", "func(b:Bool -> Num32)"}, {"Num32$from_byte", "func(b:Byte -> Num32)"}, + {"Num32$from_int8", "func(i:Int8 -> Num32)"}, {"Num32$from_int16", "func(i:Int16 -> Num32)"}, + {"Num32$from_int32", "func(i:Int32, truncate=no -> Num32)"}, + {"Num32$from_int64", "func(i:Int64, truncate=no -> Num32)"}, + {"Num32$from_int", "func(i:Int, truncate=no -> Num32)"}, {"Num32$from_num", "func(n:Num -> Num32)"}); + ADD_CONSTRUCTORS("Path", {"Path$escape_text", "func(text:Text -> Path)"}, + {"Path$escape_path", "func(path:Path -> Path)"}, {"Int$value_as_text", "func(i:Int -> Path)"}); ADD_CONSTRUCTORS("CString", {"Text$as_c_string", "func(text:Text -> CString)"}); #undef ADD_CONSTRUCTORS set_binding(namespace_env(env, "Path"), "from_text", - NewFunctionType(PATH_TYPE, {.name="text", .type=TEXT_TYPE}), - Text("Path$from_text")); + NewFunctionType(PATH_TYPE, {.name = "text", .type = TEXT_TYPE}), Text("Path$from_text")); struct { const char *name, *code, *type_str; @@ -531,46 +461,46 @@ env_t *global_env(bool source_mapping) {"sleep", "sleep_num", "func(seconds:Num)"}, }; - for (size_t i = 0; i < sizeof(global_vars)/sizeof(global_vars[0]); i++) { + for (size_t i = 0; i < sizeof(global_vars) / sizeof(global_vars[0]); i++) { type_t *type = parse_type_string(env, global_vars[i].type_str); - if (!type) compiler_err(NULL, NULL, NULL, "Couldn't parse type string for ", global_vars[i].name, ": ", global_vars[i].type_str); + if (!type) + compiler_err(NULL, NULL, NULL, "Couldn't parse type string for ", global_vars[i].name, ": ", + global_vars[i].type_str); if (type->tag == ClosureType) type = Match(type, ClosureType)->fn; - Table$str_set(env->globals, global_vars[i].name, new(binding_t, .type=type, .code=Text$from_str(global_vars[i].code))); + Table$str_set(env->globals, global_vars[i].name, + new (binding_t, .type = type, .code = Text$from_str(global_vars[i].code))); } _global_env = env; return env; } -env_t *load_module_env(env_t *env, ast_t *ast) -{ +env_t *load_module_env(env_t *env, ast_t *ast) { const char *name = ast->file->filename; env_t *cached = Table$str_get(*env->imports, name); if (cached) return cached; env_t *module_env = fresh_scope(env); - module_env->code = new(compilation_unit_t); + module_env->code = new (compilation_unit_t); module_env->namespace_bindings = module_env->locals; module_env->id_suffix = get_id_suffix(ast->file->filename); Table$str_set(module_env->imports, name, module_env); ast_list_t *statements = Match(ast, Block)->statements; - visit_topologically(statements, (Closure_t){.fn=(void*)prebind_statement, .userdata=module_env}); - visit_topologically(statements, (Closure_t){.fn=(void*)bind_statement, .userdata=module_env}); + visit_topologically(statements, (Closure_t){.fn = (void *)prebind_statement, .userdata = module_env}); + visit_topologically(statements, (Closure_t){.fn = (void *)bind_statement, .userdata = module_env}); return module_env; } -env_t *fresh_scope(env_t *env) -{ - env_t *scope = new(env_t); +env_t *fresh_scope(env_t *env) { + env_t *scope = new (env_t); *scope = *env; - scope->locals = new(Table_t, .fallback=env->locals); + scope->locals = new (Table_t, .fallback = env->locals); return scope; } -env_t *with_enum_scope(env_t *env, type_t *t) -{ +env_t *with_enum_scope(env_t *env, type_t *t) { while (t->tag == OptionalType) t = Match(t, OptionalType)->type; @@ -578,8 +508,7 @@ env_t *with_enum_scope(env_t *env, type_t *t) env = fresh_scope(env); env_t *ns_env = Match(t, EnumType)->env; for (tag_t *tag = Match(t, EnumType)->tags; tag; tag = tag->next) { - if (get_binding(env, tag->name)) - continue; + if (get_binding(env, tag->name)) continue; binding_t *b = get_binding(ns_env, tag->name); assert(b); Table$str_set(env->locals, tag->name, b); @@ -587,9 +516,7 @@ env_t *with_enum_scope(env_t *env, type_t *t) return env; } - -env_t *for_scope(env_t *env, ast_t *ast) -{ +env_t *for_scope(env_t *env, ast_t *ast) { DeclareMatch(for_, ast, For); type_t *iter_t = value_type(get_type(env, for_->iter)); env_t *scope = fresh_scope(env); @@ -600,8 +527,7 @@ env_t *for_scope(env_t *env, ast_t *ast) const char *vars[2] = {}; int64_t num_vars = 0; for (ast_list_t *var = for_->vars; var; var = var->next) { - if (num_vars >= 2) - code_err(var->ast, "This is too many variables for this loop"); + if (num_vars >= 2) code_err(var->ast, "This is too many variables for this loop"); vars[num_vars++] = Match(var->ast, Var)->name; } if (num_vars == 1) { @@ -614,8 +540,7 @@ env_t *for_scope(env_t *env, ast_t *ast) } case SetType: { if (for_->vars) { - if (for_->vars->next) - code_err(for_->vars->next->ast, "This is too many variables for this loop"); + if (for_->vars->next) code_err(for_->vars->next->ast, "This is too many variables for this loop"); type_t *item_type = Match(iter_t, SetType)->item_type; const char *name = Match(for_->vars->ast, Var)->name; set_binding(scope, name, item_type, Texts("_$", name)); @@ -626,8 +551,7 @@ env_t *for_scope(env_t *env, ast_t *ast) const char *vars[2] = {}; int64_t num_vars = 0; for (ast_list_t *var = for_->vars; var; var = var->next) { - if (num_vars >= 2) - code_err(var->ast, "This is too many variables for this loop"); + if (num_vars >= 2) code_err(var->ast, "This is too many variables for this loop"); vars[num_vars++] = Match(var->ast, Var)->name; } @@ -643,19 +567,20 @@ env_t *for_scope(env_t *env, ast_t *ast) } case BigIntType: { if (for_->vars) { - if (for_->vars->next) - code_err(for_->vars->next->ast, "This is too many variables for this loop"); + if (for_->vars->next) code_err(for_->vars->next->ast, "This is too many variables for this loop"); const char *var = Match(for_->vars->ast, Var)->name; set_binding(scope, var, INT_TYPE, Texts("_$", var)); } return scope; } - case FunctionType: case ClosureType: { - __typeof(iter_t->__data.FunctionType) *fn = iter_t->tag == ClosureType ? Match(Match(iter_t, ClosureType)->fn, FunctionType) : Match(iter_t, FunctionType); + case FunctionType: + case ClosureType: { + __typeof(iter_t->__data.FunctionType) *fn = iter_t->tag == ClosureType + ? Match(Match(iter_t, ClosureType)->fn, FunctionType) + : Match(iter_t, FunctionType); if (for_->vars) { - if (for_->vars->next) - code_err(for_->vars->next->ast, "This is too many variables for this loop"); + if (for_->vars->next) code_err(for_->vars->next->ast, "This is too many variables for this loop"); const char *var = Match(for_->vars->ast, Var)->name; type_t *non_opt_type = fn->ret->tag == OptionalType ? Match(fn->ret, OptionalType)->type : fn->ret; set_binding(scope, var, non_opt_type, Texts("_$", var)); @@ -667,14 +592,17 @@ env_t *for_scope(env_t *env, ast_t *ast) return NULL; } -env_t *get_namespace_by_type(env_t *env, type_t *t) -{ +env_t *get_namespace_by_type(env_t *env, type_t *t) { t = value_type(t); switch (t->tag) { case ListType: return NULL; case TableType: return NULL; case CStringType: - case BoolType: case IntType: case BigIntType: case NumType: case ByteType: { + case BoolType: + case IntType: + case BigIntType: + case NumType: + case ByteType: { binding_t *b = get_binding(env, Text$as_c_string(type_to_text(t))); assert(b); return Match(b->type, TypeInfoType)->env; @@ -697,84 +625,72 @@ env_t *get_namespace_by_type(env_t *env, type_t *t) return NULL; } -env_t *namespace_env(env_t *env, const char *namespace_name) -{ +env_t *namespace_env(env_t *env, const char *namespace_name) { binding_t *b = get_binding(env, namespace_name); - if (b) - return Match(b->type, TypeInfoType)->env; + if (b) return Match(b->type, TypeInfoType)->env; - env_t *ns_env = new(env_t); + env_t *ns_env = new (env_t); *ns_env = *env; - ns_env->locals = new(Table_t, .fallback=env->locals); - ns_env->namespace = new(namespace_t, .name=namespace_name, .parent=env->namespace); + ns_env->locals = new (Table_t, .fallback = env->locals); + ns_env->namespace = new (namespace_t, .name = namespace_name, .parent = env->namespace); ns_env->namespace_bindings = ns_env->locals; return ns_env; } -PUREFUNC binding_t *get_binding(env_t *env, const char *name) -{ - return Table$str_get(*env->locals, name); -} +PUREFUNC binding_t *get_binding(env_t *env, const char *name) { return Table$str_get(*env->locals, name); } -binding_t *get_namespace_binding(env_t *env, ast_t *self, const char *name) -{ +binding_t *get_namespace_binding(env_t *env, ast_t *self, const char *name) { type_t *self_type = get_type(env, self); - if (!self_type) - code_err(self, "I couldn't get this type"); + if (!self_type) code_err(self, "I couldn't get this type"); env_t *ns_env = get_namespace_by_type(env, self_type); return ns_env ? get_binding(ns_env, name) : NULL; } -PUREFUNC binding_t *get_constructor(env_t *env, type_t *t, arg_ast_t *args, bool allow_underscores) -{ +PUREFUNC binding_t *get_constructor(env_t *env, type_t *t, arg_ast_t *args, bool allow_underscores) { env_t *type_env = get_namespace_by_type(env, t); if (!type_env) return NULL; List_t constructors = type_env->namespace->constructors; // Prioritize exact matches: - for (int64_t i = constructors.length-1; i >= 0; i--) { - binding_t *b = constructors.data + i*constructors.stride; + for (int64_t i = constructors.length - 1; i >= 0; i--) { + binding_t *b = constructors.data + i * constructors.stride; DeclareMatch(fn, b->type, FunctionType); if (!allow_underscores) { for (arg_t *arg = fn->args; arg; arg = arg->next) - if (arg->name[0] == '_') - goto next_constructor; + if (arg->name[0] == '_') goto next_constructor; } - if (type_eq(fn->ret, t) && is_valid_call(env, fn->args, args, false)) - return b; - next_constructor: continue; + if (type_eq(fn->ret, t) && is_valid_call(env, fn->args, args, false)) return b; + next_constructor: + continue; } // Fall back to promotion: - for (int64_t i = constructors.length-1; i >= 0; i--) { - binding_t *b = constructors.data + i*constructors.stride; + for (int64_t i = constructors.length - 1; i >= 0; i--) { + binding_t *b = constructors.data + i * constructors.stride; DeclareMatch(fn, b->type, FunctionType); if (!allow_underscores) { for (arg_t *arg = fn->args; arg; arg = arg->next) - if (arg->name[0] == '_') - goto next_constructor2; + if (arg->name[0] == '_') goto next_constructor2; } - if (type_eq(fn->ret, t) && is_valid_call(env, fn->args, args, true)) - return b; - next_constructor2: continue; + if (type_eq(fn->ret, t) && is_valid_call(env, fn->args, args, true)) return b; + next_constructor2: + continue; } return NULL; } -PUREFUNC binding_t *get_metamethod_binding(env_t *env, ast_e tag, ast_t *lhs, ast_t *rhs, type_t *ret) -{ +PUREFUNC binding_t *get_metamethod_binding(env_t *env, ast_e tag, ast_t *lhs, ast_t *rhs, type_t *ret) { const char *method_name = binop_method_name(tag); if (!method_name) return NULL; binding_t *b = get_namespace_binding(env, lhs, method_name); if (!b || b->type->tag != FunctionType) return NULL; DeclareMatch(fn, b->type, FunctionType); if (!type_eq(fn->ret, ret)) return NULL; - arg_ast_t *args = new(arg_ast_t, .value=lhs, .next=new(arg_ast_t, .value=rhs)); + arg_ast_t *args = new (arg_ast_t, .value = lhs, .next = new (arg_ast_t, .value = rhs)); return is_valid_call(env, fn->args, args, true) ? b : NULL; } -void set_binding(env_t *env, const char *name, type_t *type, Text_t code) -{ +void set_binding(env_t *env, const char *name, type_t *type, Text_t code) { assert(name); - Table$str_set(env->locals, name, new(binding_t, .type=type, .code=code)); + Table$str_set(env->locals, name, new (binding_t, .type = type, .code = code)); } // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/environment.h b/src/environment.h index 0bff474d..ca10dbae 100644 --- a/src/environment.h +++ b/src/environment.h @@ -46,7 +46,7 @@ typedef struct env_s { loop_ctx_t *loop_ctx; deferral_t *deferred; Closure_t *comprehension_action; - bool do_source_mapping:1; + bool do_source_mapping : 1; type_t *current_type; } env_t; @@ -62,23 +62,21 @@ env_t *fresh_scope(env_t *env); env_t *for_scope(env_t *env, ast_t *ast); env_t *with_enum_scope(env_t *env, type_t *t); env_t *namespace_env(env_t *env, const char *namespace_name); -#define compiler_err(f, start, end, ...) ({ \ - file_t *_f = f; \ - if (USE_COLOR) \ - fputs("\x1b[31;7;1m ", stderr); \ - if (_f && start && end) \ - fprint_inline(stderr, _f->relative_filename, ":", get_line_number(_f, start), ".", get_line_column(_f, start), ": "); \ - fprint_inline(stderr, __VA_ARGS__); \ - if (USE_COLOR) \ - fputs(" \x1b[m", stderr); \ - fputs("\n\n", stderr); \ - if (_f && start && end) \ - highlight_error(_f, start, end, "\x1b[31;1m", 2, USE_COLOR); \ - if (getenv("TOMO_STACKTRACE")) \ - print_stacktrace(stderr, 1); \ - raise(SIGABRT); \ - exit(1); \ -}) +#define compiler_err(f, start, end, ...) \ + ({ \ + file_t *_f = f; \ + if (USE_COLOR) fputs("\x1b[31;7;1m ", stderr); \ + if (_f && start && end) \ + fprint_inline(stderr, _f->relative_filename, ":", get_line_number(_f, start), ".", \ + get_line_column(_f, start), ": "); \ + fprint_inline(stderr, __VA_ARGS__); \ + if (USE_COLOR) fputs(" \x1b[m", stderr); \ + fputs("\n\n", stderr); \ + if (_f && start && end) highlight_error(_f, start, end, "\x1b[31;1m", 2, USE_COLOR); \ + if (getenv("TOMO_STACKTRACE")) print_stacktrace(stderr, 1); \ + raise(SIGABRT); \ + exit(1); \ + }) binding_t *get_binding(env_t *env, const char *name); binding_t *get_constructor(env_t *env, type_t *t, arg_ast_t *args, bool allow_underscores); PUREFUNC binding_t *get_metamethod_binding(env_t *env, ast_e tag, ast_t *lhs, ast_t *rhs, type_t *ret); diff --git a/src/modules.c b/src/modules.c index 7512e258..d891f64a 100644 --- a/src/modules.c +++ b/src/modules.c @@ -14,40 +14,41 @@ #include "stdlib/text.h" #include "stdlib/types.h" -#define xsystem(...) ({ int _status = system(String(__VA_ARGS__)); if (!WIFEXITED(_status) || WEXITSTATUS(_status) != 0) errx(1, "Failed to run command: %s", String(__VA_ARGS__)); }) +#define xsystem(...) \ + ({ \ + int _status = system(String(__VA_ARGS__)); \ + if (!WIFEXITED(_status) || WEXITSTATUS(_status) != 0) \ + errx(1, "Failed to run command: %s", String(__VA_ARGS__)); \ + }) -static void read_modules_ini(Path_t ini_file, module_info_t *info) -{ +static void read_modules_ini(Path_t ini_file, module_info_t *info) { OptionalClosure_t by_line = Path$by_line(ini_file); if (by_line.fn == NULL) return; - OptionalText_t (*next_line)(void*) = by_line.fn; - find_section:; - for (Text_t line; (line=next_line(by_line.userdata)).length >= 0; ) { + OptionalText_t (*next_line)(void *) = by_line.fn; +find_section:; + for (Text_t line; (line = next_line(by_line.userdata)).length >= 0;) { char *line_str = Text$as_c_string(line); - if (line_str[0] == '[' && strncmp(line_str+1, info->name, strlen(info->name)) == 0 - && line_str[1+strlen(info->name)] == ']') + if (line_str[0] == '[' && strncmp(line_str + 1, info->name, strlen(info->name)) == 0 + && line_str[1 + strlen(info->name)] == ']') break; } - for (Text_t line; (line=next_line(by_line.userdata)).length >= 0; ) { + for (Text_t line; (line = next_line(by_line.userdata)).length >= 0;) { char *line_str = Text$as_c_string(line); if (line_str[0] == '[') goto find_section; - if (!strparse(line_str, "version=", &info->version) - || !strparse(line_str, "url=", &info->url) - || !strparse(line_str, "git=", &info->git) - || !strparse(line_str, "path=", &info->path) + if (!strparse(line_str, "version=", &info->version) || !strparse(line_str, "url=", &info->url) + || !strparse(line_str, "git=", &info->git) || !strparse(line_str, "path=", &info->path) || !strparse(line_str, "revision=", &info->revision)) continue; } } -module_info_t get_module_info(ast_t *use) -{ +module_info_t get_module_info(ast_t *use) { static Table_t cache = {}; TypeInfo_t *cache_type = Table$info(Pointer$info("@", &Memory$info), Pointer$info("@", &Memory$info)); module_info_t **cached = Table$get(cache, &use, cache_type); if (cached) return **cached; const char *name = Match(use, Use)->path; - module_info_t *info = new(module_info_t, .name=name); + module_info_t *info = new (module_info_t, .name = name); if (streq(name, "commands")) info->version = "v1.0"; else if (streq(name, "random")) info->version = "v1.0"; else if (streq(name, "base64")) info->version = "v1.0"; @@ -64,16 +65,14 @@ module_info_t get_module_info(ast_t *use) } Table$set(&cache, &use, &info, cache_type); return *info; - } -bool try_install_module(module_info_t mod) -{ +bool try_install_module(module_info_t mod) { if (mod.git) { - OptionalText_t answer = ask( - Texts(Text("The module \""), Text$from_str(mod.name), Text("\" is not installed.\nDo you want to install it from git URL "), - Text$from_str(mod.git), Text("? [Y/n] ")), - true, true); + OptionalText_t answer = ask(Texts(Text("The module \""), Text$from_str(mod.name), + Text("\" is not installed.\nDo you want to install it from git URL "), + Text$from_str(mod.git), Text("? [Y/n] ")), + true, true); if (!(answer.length == 0 || Text$equal_values(answer, Text("Y")) || Text$equal_values(answer, Text("y")))) return false; print("Installing ", mod.name, " from git..."); @@ -85,10 +84,10 @@ bool try_install_module(module_info_t mod) Path$remove(tmpdir, true); return true; } else if (mod.url) { - OptionalText_t answer = ask( - Texts(Text("The module "), Text$from_str(mod.name), Text(" is not installed.\nDo you want to install it from URL "), - Text$from_str(mod.url), Text("? [Y/n] ")), - true, true); + OptionalText_t answer = ask(Texts(Text("The module "), Text$from_str(mod.name), + Text(" is not installed.\nDo you want to install it from URL "), + Text$from_str(mod.url), Text("? [Y/n] ")), + true, true); if (!(answer.length == 0 || Text$equal_values(answer, Text("Y")) || Text$equal_values(answer, Text("y")))) return false; @@ -102,22 +101,19 @@ bool try_install_module(module_info_t mod) const char *extension = p + 1; Path_t tmpdir = Path$unique_directory(Path("/tmp/tomo-module-XXXXXX")); xsystem("curl ", mod.url, " -o ", tmpdir); - if (streq(extension, ".zip")) - xsystem("unzip ", tmpdir, "/", filename); - else if (streq(extension, ".tar.gz") || streq(extension, ".tar")) - xsystem("tar xf ", tmpdir, "/", filename); - else - return false; + if (streq(extension, ".zip")) xsystem("unzip ", tmpdir, "/", filename); + else if (streq(extension, ".tar.gz") || streq(extension, ".tar")) xsystem("tar xf ", tmpdir, "/", filename); + else return false; const char *basename = String(string_slice(filename, strcspn(filename, "."))); if (mod.path) xsystem("tomo -IL ", tmpdir, "/", basename, "/", mod.path); else xsystem("tomo -IL ", tmpdir, "/", basename); Path$remove(tmpdir, true); return true; } else if (mod.path) { - OptionalText_t answer = ask( - Texts(Text("The module "), Text$from_str(mod.name), Text(" is not installed.\nDo you want to install it from path "), - Text$from_str(mod.path), Text("? [Y/n] ")), - true, true); + OptionalText_t answer = ask(Texts(Text("The module "), Text$from_str(mod.name), + Text(" is not installed.\nDo you want to install it from path "), + Text$from_str(mod.path), Text("? [Y/n] ")), + true, true); if (!(answer.length == 0 || Text$equal_values(answer, Text("Y")) || Text$equal_values(answer, Text("y")))) return false; diff --git a/src/naming.c b/src/naming.c index 3bf71597..d0c7cfac 100644 --- a/src/naming.c +++ b/src/naming.c @@ -7,59 +7,108 @@ #include "stdlib/paths.h" #include "stdlib/text.h" -static const char *c_keywords[] = { // Maintain sorted order: - "_Alignas", "_Alignof", "_Atomic", "_BitInt", "_Bool", "_Complex", "_Decimal128", "_Decimal32", "_Decimal64", "_Generic", - "_Imaginary", "_Noreturn", "_Static_assert", "_Thread_local", - "alignas", "__alignof__", "auto", "bool", "break", "case", "char", "const", "constexpr", "continue", "default", "do", "double", - "else", "enum", "extern", "false", "float", "for", "goto", "if", "inline", "int", "long", "nullptr", "register", "restrict", - "return", "short", "signed", "sizeof", "static", "static_assert", "struct", "switch", "thread_local", "true", "typedef", - "typeof", "typeof_unqual", "union", "unsigned", "void", "volatile", "while", +static const char *c_keywords[] = { + // Maintain sorted order: + "_Alignas", + "_Alignof", + "_Atomic", + "_BitInt", + "_Bool", + "_Complex", + "_Decimal128", + "_Decimal32", + "_Decimal64", + "_Generic", + "_Imaginary", + "_Noreturn", + "_Static_assert", + "_Thread_local", + "alignas", + "__alignof__", + "auto", + "bool", + "break", + "case", + "char", + "const", + "constexpr", + "continue", + "default", + "do", + "double", + "else", + "enum", + "extern", + "false", + "float", + "for", + "goto", + "if", + "inline", + "int", + "long", + "nullptr", + "register", + "restrict", + "return", + "short", + "signed", + "sizeof", + "static", + "static_assert", + "struct", + "switch", + "thread_local", + "true", + "typedef", + "typeof", + "typeof_unqual", + "union", + "unsigned", + "void", + "volatile", + "while", }; static CONSTFUNC bool is_keyword(const char *word, size_t len) { - int64_t lo = 0, hi = sizeof(c_keywords)/sizeof(c_keywords[0])-1; + int64_t lo = 0, hi = sizeof(c_keywords) / sizeof(c_keywords[0]) - 1; while (lo <= hi) { int64_t mid = (lo + hi) / 2; int32_t cmp = strncmp(word, c_keywords[mid], len); - if (cmp == 0) - return true; - else if (cmp > 0) - lo = mid + 1; - else if (cmp < 0) - hi = mid - 1; + if (cmp == 0) return true; + else if (cmp > 0) lo = mid + 1; + else if (cmp < 0) hi = mid - 1; } return false; } -public Text_t valid_c_name(const char *name) -{ +public +Text_t valid_c_name(const char *name) { size_t len = strlen(name); size_t trailing_underscores = 0; - while (trailing_underscores < len && name[len-1-trailing_underscores] == '_') + while (trailing_underscores < len && name[len - 1 - trailing_underscores] == '_') trailing_underscores += 1; - if (is_keyword(name, len-trailing_underscores)) { + if (is_keyword(name, len - trailing_underscores)) { return Texts(Text$from_str(name), Text("_")); } return Text$from_str(name); } -public Text_t CONSTFUNC namespace_name(env_t *env, namespace_t *ns, Text_t name) -{ +public +Text_t CONSTFUNC namespace_name(env_t *env, namespace_t *ns, Text_t name) { for (; ns; ns = ns->parent) name = Texts(ns->name, "$", name); - if (env->id_suffix.length > 0) - name = Texts(name, env->id_suffix); + if (env->id_suffix.length > 0) name = Texts(name, env->id_suffix); return name; } -public Text_t get_id_suffix(const char *filename) -{ +public +Text_t get_id_suffix(const char *filename) { assert(filename); Path_t path = Path$from_str(filename); Path_t build_dir = Path$sibling(path, Text(".build")); if (mkdir(Path$as_c_string(build_dir), 0755) != 0) { - if (!Path$is_directory(build_dir, true)) - err(1, "Could not make .build directory"); + if (!Path$is_directory(build_dir, true)) err(1, "Could not make .build directory"); } Path_t id_file = Path$child(build_dir, Texts(Path$base_name(path), Text$from_str(".id"))); OptionalText_t id = Path$read(id_file); diff --git a/src/parse.c b/src/parse.c index 06184b97..9f6f1581 100644 --- a/src/parse.c +++ b/src/parse.c @@ -14,9 +14,9 @@ #include <unistr.h> #endif +#include <signal.h> #include <unictype.h> #include <uniname.h> -#include <signal.h> #include "ast.h" #include "stdlib/print.h" @@ -32,7 +32,7 @@ #endif static const double RADIANS_PER_DEGREE = 0.0174532925199432957692369076848861271344287188854172545609719144; -static const char closing[128] = {['(']=')', ['[']=']', ['<']='>', ['{']='}'}; +static const char closing[128] = {['('] = ')', ['['] = ']', ['<'] = '>', ['{'] = '}'}; typedef struct { file_t *file; @@ -45,26 +45,40 @@ typedef struct { #define PARSER(name) ast_t *name(parse_ctx_t *ctx, const char *pos) int op_tightness[] = { - [Power]=9, - [Multiply]=8, [Divide]=8, [Mod]=8, [Mod1]=8, - [Plus]=7, [Minus]=7, - [Concat]=6, - [LeftShift]=5, [RightShift]=5, [UnsignedLeftShift]=5, [UnsignedRightShift]=5, - [Min]=4, [Max]=4, - [Equals]=3, [NotEquals]=3, - [LessThan]=2, [LessThanOrEquals]=2, [GreaterThan]=2, [GreaterThanOrEquals]=2, - [Compare]=2, - [And]=1, [Or]=1, [Xor]=1, + [Power] = 9, + [Multiply] = 8, + [Divide] = 8, + [Mod] = 8, + [Mod1] = 8, + [Plus] = 7, + [Minus] = 7, + [Concat] = 6, + [LeftShift] = 5, + [RightShift] = 5, + [UnsignedLeftShift] = 5, + [UnsignedRightShift] = 5, + [Min] = 4, + [Max] = 4, + [Equals] = 3, + [NotEquals] = 3, + [LessThan] = 2, + [LessThanOrEquals] = 2, + [GreaterThan] = 2, + [GreaterThanOrEquals] = 2, + [Compare] = 2, + [And] = 1, + [Or] = 1, + [Xor] = 1, }; static const char *keywords[] = { - "C_code", "_max_", "_min_", "and", "assert", "break", "continue", "defer", "deserialize", "do", "else", - "enum", "extend", "extern", "for", "func", "if", "in", "lang", "mod", "mod1", "no", "none", - "not", "or", "pass", "return", "skip", "skip", "stop", "struct", "then", "unless", "use", "when", - "while", "xor", "yes", + "C_code", "_max_", "_min_", "and", "assert", "break", "continue", "defer", "deserialize", "do", + "else", "enum", "extend", "extern", "for", "func", "if", "in", "lang", "mod", + "mod1", "no", "none", "not", "or", "pass", "return", "skip", "skip", "stop", + "struct", "then", "unless", "use", "when", "while", "xor", "yes", }; -enum {NORMAL_FUNCTION=0, EXTERN_FUNCTION=1}; +enum { NORMAL_FUNCTION = 0, EXTERN_FUNCTION = 1 }; static INLINE size_t some_of(const char **pos, const char *allow); static INLINE size_t some_not(const char **pos, const char *forbid); @@ -72,8 +86,8 @@ static INLINE size_t spaces(const char **pos); static INLINE void whitespace(const char **pos); static INLINE size_t match(const char **pos, const char *target); static INLINE size_t match_word(const char **pos, const char *word); -static INLINE const char* get_word(const char **pos); -static INLINE const char* get_id(const char **pos); +static INLINE const char *get_word(const char **pos); +static INLINE const char *get_id(const char **pos); static INLINE bool comment(const char **pos); static INLINE bool indent(parse_ctx_t *ctx, const char **pos); static INLINE ast_e match_binary_operator(const char **pos); @@ -145,83 +159,83 @@ static PARSER(parse_var); static PARSER(parse_when); static PARSER(parse_while); static PARSER(parse_deserialize); -static ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, char open_quote, char close_quote, char open_interp, bool allow_escapes); +static ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, char open_quote, char close_quote, + char open_interp, bool allow_escapes); // // Print a parse error and exit (or use the on_err longjmp) // -#define parser_err(ctx, start, end, ...) ({ \ - if (USE_COLOR) \ - fputs("\x1b[31;1;7m", stderr); \ - fprint_inline(stderr, (ctx)->file->relative_filename, ":", get_line_number((ctx)->file, (start)), \ - ".", get_line_column((ctx)->file, (start)), ": ", __VA_ARGS__); \ - if (USE_COLOR) \ - fputs(" \x1b[m", stderr); \ - fputs("\n\n", stderr); \ - highlight_error((ctx)->file, (start), (end), "\x1b[31;1;7m", 2, USE_COLOR); \ - fputs("\n", stderr); \ - if (getenv("TOMO_STACKTRACE")) \ - print_stacktrace(stderr, 1); \ - if ((ctx)->on_err) \ - longjmp(*((ctx)->on_err), 1); \ - raise(SIGABRT); \ - exit(1); \ -}) +#define parser_err(ctx, start, end, ...) \ + ({ \ + if (USE_COLOR) fputs("\x1b[31;1;7m", stderr); \ + fprint_inline(stderr, (ctx)->file->relative_filename, ":", get_line_number((ctx)->file, (start)), ".", \ + get_line_column((ctx)->file, (start)), ": ", __VA_ARGS__); \ + if (USE_COLOR) fputs(" \x1b[m", stderr); \ + fputs("\n\n", stderr); \ + highlight_error((ctx)->file, (start), (end), "\x1b[31;1;7m", 2, USE_COLOR); \ + fputs("\n", stderr); \ + if (getenv("TOMO_STACKTRACE")) print_stacktrace(stderr, 1); \ + if ((ctx)->on_err) longjmp(*((ctx)->on_err), 1); \ + raise(SIGABRT); \ + exit(1); \ + }) // // Expect a string (potentially after whitespace) and emit a parser error if it's not there // -#define expect_str(ctx, start, pos, target, ...) ({ \ - spaces(pos); \ - if (!match(pos, target)) { \ - if (USE_COLOR) \ - fputs("\x1b[31;1;7m", stderr); \ - parser_err(ctx, start, *pos, __VA_ARGS__); \ - } \ - char _lastchar = target[strlen(target)-1]; \ - if (isalpha(_lastchar) || isdigit(_lastchar) || _lastchar == '_') { \ - if (is_xid_continue_next(*pos)) { \ - if (USE_COLOR) \ - fputs("\x1b[31;1;7m", stderr); \ - parser_err(ctx, start, *pos, __VA_ARGS__); \ - } \ - } \ -}) +#define expect_str(ctx, start, pos, target, ...) \ + ({ \ + spaces(pos); \ + if (!match(pos, target)) { \ + if (USE_COLOR) fputs("\x1b[31;1;7m", stderr); \ + parser_err(ctx, start, *pos, __VA_ARGS__); \ + } \ + char _lastchar = target[strlen(target) - 1]; \ + if (isalpha(_lastchar) || isdigit(_lastchar) || _lastchar == '_') { \ + if (is_xid_continue_next(*pos)) { \ + if (USE_COLOR) fputs("\x1b[31;1;7m", stderr); \ + parser_err(ctx, start, *pos, __VA_ARGS__); \ + } \ + } \ + }) // // Helper for matching closing parens with good error messages // -#define expect_closing(ctx, pos, close_str, ...) ({ \ - const char *_start = *pos; \ - spaces(pos); \ - if (!match(pos, (close_str))) { \ - const char *_eol = strchr(*pos, '\n'); \ - const char *_next = strstr(*pos, (close_str)); \ - const char *_end = _eol < _next ? _eol : _next; \ - if (USE_COLOR) \ - fputs("\x1b[31;1;7m", stderr); \ - parser_err(ctx, _start, _end, __VA_ARGS__); \ - } \ -}) - -#define expect(ctx, start, pos, parser, ...) ({ \ - const char **_pos = pos; \ - spaces(_pos); \ - __typeof(parser(ctx, *_pos)) _result = parser(ctx, *_pos); \ - if (!_result) { \ - if (USE_COLOR) \ - fputs("\x1b[31;1;7m", stderr); \ - parser_err(ctx, start, *_pos, __VA_ARGS__); \ - } \ - *_pos = _result->end; \ - _result; }) - -#define optional(ctx, pos, parser) ({ \ - const char **_pos = pos; \ - spaces(_pos); \ - __typeof(parser(ctx, *_pos)) _result = parser(ctx, *_pos); \ - if (_result) *_pos = _result->end; \ - _result; }) +#define expect_closing(ctx, pos, close_str, ...) \ + ({ \ + const char *_start = *pos; \ + spaces(pos); \ + if (!match(pos, (close_str))) { \ + const char *_eol = strchr(*pos, '\n'); \ + const char *_next = strstr(*pos, (close_str)); \ + const char *_end = _eol < _next ? _eol : _next; \ + if (USE_COLOR) fputs("\x1b[31;1;7m", stderr); \ + parser_err(ctx, _start, _end, __VA_ARGS__); \ + } \ + }) + +#define expect(ctx, start, pos, parser, ...) \ + ({ \ + const char **_pos = pos; \ + spaces(_pos); \ + __typeof(parser(ctx, *_pos)) _result = parser(ctx, *_pos); \ + if (!_result) { \ + if (USE_COLOR) fputs("\x1b[31;1;7m", stderr); \ + parser_err(ctx, start, *_pos, __VA_ARGS__); \ + } \ + *_pos = _result->end; \ + _result; \ + }) + +#define optional(ctx, pos, parser) \ + ({ \ + const char **_pos = pos; \ + spaces(_pos); \ + __typeof(parser(ctx, *_pos)) _result = parser(ctx, *_pos); \ + if (_result) *_pos = _result->end; \ + _result; \ + }) // // Convert an escape sequence like \n to a string @@ -233,7 +247,8 @@ static ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, ch static const char *unescape(parse_ctx_t *ctx, const char **out) { const char **endpos = out; const char *escape = *out; - static const char *unescapes[256] = {['a']="\a",['b']="\b",['e']="\x1b",['f']="\f",['n']="\n",['r']="\r",['t']="\t",['v']="\v",['_']=" "}; + static const char *unescapes[256] = {['a'] = "\a", ['b'] = "\b", ['e'] = "\x1b", ['f'] = "\f", ['n'] = "\n", + ['r'] = "\r", ['t'] = "\t", ['v'] = "\v", ['_'] = " "}; assert(*escape == '\\'); if (unescapes[(int)escape[1]]) { *endpos = escape + 2; @@ -241,16 +256,14 @@ static const char *unescape(parse_ctx_t *ctx, const char **out) { } else if (escape[1] == '[') { // ANSI Control Sequence Indicator: \033 [ ... m size_t len = strcspn(&escape[2], "\r\n]"); - if (escape[2+len] != ']') - parser_err(ctx, escape, escape + 2 + len, "Missing closing ']'"); + if (escape[2 + len] != ']') parser_err(ctx, escape, escape + 2 + len, "Missing closing ']'"); *endpos = escape + 3 + len; return String("\033[", string_slice(&escape[2], len), "m"); } else if (escape[1] == '{') { // Unicode codepoints by name size_t len = strcspn(&escape[2], "\r\n}"); - if (escape[2+len] != '}') - parser_err(ctx, escape, escape + 2 + len, "Missing closing '}'"); - char name[len+1]; + if (escape[2 + len] != '}') parser_err(ctx, escape, escape + 2 + len, "Missing closing '}'"); + char name[len + 1]; memcpy(name, &escape[2], len); name[len] = '\0'; @@ -260,25 +273,24 @@ static const char *unescape(parse_ctx_t *ctx, const char **out) { } // Unicode codepoints by hex char *endptr = NULL; - long codepoint = strtol(name+1, &endptr, 16); + long codepoint = strtol(name + 1, &endptr, 16); uint32_t ustr[2] = {codepoint, 0}; size_t bufsize = 8; uint8_t buf[bufsize]; (void)u32_to_u8(ustr, bufsize, buf, &bufsize); *endpos = escape + 3 + len; - return GC_strndup((char*)buf, bufsize); + return GC_strndup((char *)buf, bufsize); } - look_up_unicode_name:; + look_up_unicode_name:; uint32_t codepoint = unicode_name_character(name); if (codepoint == UNINAME_INVALID) - parser_err(ctx, escape, escape + 3 + len, - "Invalid unicode codepoint name: ", quoted(name)); + parser_err(ctx, escape, escape + 3 + len, "Invalid unicode codepoint name: ", quoted(name)); *endpos = escape + 3 + len; char *str = GC_MALLOC_ATOMIC(16); size_t u8_len = 16; - (void)u32_to_u8(&codepoint, 1, (uint8_t*)str, &u8_len); + (void)u32_to_u8(&codepoint, 1, (uint8_t *)str, &u8_len); str[u8_len] = '\0'; return str; } else if (escape[1] == 'x' && escape[2] && escape[3]) { @@ -287,14 +299,15 @@ static const char *unescape(parse_ctx_t *ctx, const char **out) { char c = (char)strtol(buf, NULL, 16); *endpos = escape + 4; return GC_strndup(&c, 1); - } else if ('0' <= escape[1] && escape[1] <= '7' && '0' <= escape[2] && escape[2] <= '7' && '0' <= escape[3] && escape[3] <= '7') { + } else if ('0' <= escape[1] && escape[1] <= '7' && '0' <= escape[2] && escape[2] <= '7' && '0' <= escape[3] + && escape[3] <= '7') { char buf[] = {escape[1], escape[2], escape[3], 0}; char c = (char)strtol(buf, NULL, 8); *endpos = escape + 4; return GC_strndup(&c, 1); } else { *endpos = escape + 2; - return GC_strndup(escape+1, 1); + return GC_strndup(escape + 1, 1); } } #ifdef __GNUC__ @@ -302,8 +315,7 @@ static const char *unescape(parse_ctx_t *ctx, const char **out) { #endif // Indent is in number of spaces (assuming that \t is 4 spaces) -PUREFUNC static INLINE int64_t get_indent(parse_ctx_t *ctx, const char *pos) -{ +PUREFUNC static INLINE int64_t get_indent(parse_ctx_t *ctx, const char *pos) { int64_t line_num = get_line_number(ctx->file, pos); const char *line = get_line(ctx->file, line_num); if (line == NULL) { @@ -311,12 +323,14 @@ PUREFUNC static INLINE int64_t get_indent(parse_ctx_t *ctx, const char *pos) } else if (*line == ' ') { int64_t spaces = (int64_t)strspn(line, " "); if (line[spaces] == '\t') - parser_err(ctx, line + spaces, line + spaces + 1, "This is a tab following spaces, and you can't mix tabs and spaces"); + parser_err(ctx, line + spaces, line + spaces + 1, + "This is a tab following spaces, and you can't mix tabs and spaces"); return spaces; } else if (*line == '\t') { int64_t indent = (int64_t)strspn(line, "\t"); if (line[indent] == ' ') - parser_err(ctx, line + indent, line + indent + 1, "This is a space following tabs, and you can't mix tabs and spaces"); + parser_err(ctx, line + indent, line + indent + 1, + "This is a space following tabs, and you can't mix tabs and spaces"); return indent * SPACES_PER_INDENT; } else { return 0; @@ -338,9 +352,7 @@ size_t some_not(const char **pos, const char *forbid) { return len; } -size_t spaces(const char **pos) { - return some_of(pos, " \t"); -} +size_t spaces(const char **pos) { return some_of(pos, " \t"); } void whitespace(const char **pos) { while (some_of(pos, " \t\r\n") || comment(pos)) @@ -349,23 +361,21 @@ void whitespace(const char **pos) { size_t match(const char **pos, const char *target) { size_t len = strlen(target); - if (strncmp(*pos, target, len) != 0) - return 0; + if (strncmp(*pos, target, len) != 0) return 0; *pos += len; return len; } static INLINE bool is_xid_continue_next(const char *pos) { ucs4_t point = 0; - u8_next(&point, (const uint8_t*)pos); + u8_next(&point, (const uint8_t *)pos); return uc_is_property_xid_continue(point); } size_t match_word(const char **out, const char *word) { const char *pos = *out; spaces(&pos); - if (!match(&pos, word) || is_xid_continue_next(pos)) - return 0; + if (!match(&pos, word) || is_xid_continue_next(pos)) return 0; *out = pos; return strlen(word); @@ -374,31 +384,26 @@ size_t match_word(const char **out, const char *word) { const char *get_word(const char **inout) { const char *word = *inout; spaces(&word); - const uint8_t *pos = (const uint8_t*)word; + const uint8_t *pos = (const uint8_t *)word; ucs4_t point; pos = u8_next(&point, pos); - if (!uc_is_property_xid_start(point) && point != '_') - return NULL; + if (!uc_is_property_xid_start(point) && point != '_') return NULL; for (const uint8_t *next; (next = u8_next(&point, pos)); pos = next) { - if (!uc_is_property_xid_continue(point)) - break; + if (!uc_is_property_xid_continue(point)) break; } - *inout = (const char*)pos; - return GC_strndup(word, (size_t)((const char*)pos - word)); + *inout = (const char *)pos; + return GC_strndup(word, (size_t)((const char *)pos - word)); } static CONSTFUNC bool is_keyword(const char *word) { - int64_t lo = 0, hi = sizeof(keywords)/sizeof(keywords[0])-1; + int64_t lo = 0, hi = sizeof(keywords) / sizeof(keywords[0]) - 1; while (lo <= hi) { int64_t mid = (lo + hi) / 2; int32_t cmp = strcmp(word, keywords[mid]); - if (cmp == 0) - return true; - else if (cmp > 0) - lo = mid + 1; - else if (cmp < 0) - hi = mid - 1; + if (cmp == 0) return true; + else if (cmp > 0) lo = mid + 1; + else if (cmp < 0) hi = mid - 1; } return false; } @@ -406,15 +411,12 @@ static CONSTFUNC bool is_keyword(const char *word) { const char *get_id(const char **inout) { const char *pos = *inout; const char *word = get_word(&pos); - if (!word || is_keyword(word)) - return NULL; + if (!word || is_keyword(word)) return NULL; *inout = pos; return word; } -static const char *eol(const char *str) { - return str + strcspn(str, "\r\n"); -} +static const char *eol(const char *str) { return str + strcspn(str, "\r\n"); } bool comment(const char **pos) { if ((*pos)[0] == '#') { @@ -430,11 +432,9 @@ bool indent(parse_ctx_t *ctx, const char **out) { int64_t starting_indent = get_indent(ctx, pos); whitespace(&pos); const char *next_line = get_line(ctx->file, get_line_number(ctx->file, pos)); - if (next_line <= *out) - return false; + if (next_line <= *out) return false; - if (get_indent(ctx, next_line) != starting_indent + SPACES_PER_INDENT) - return false; + if (get_indent(ctx, next_line) != starting_indent + SPACES_PER_INDENT) return false; *out = next_line + strspn(next_line, " \t"); return true; @@ -486,8 +486,7 @@ PARSER(parse_parens) { expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this expression"); // Update the span to include the parens: - return new(ast_t, .file=(ctx)->file, .start=start, .end=pos, - .tag=expr->tag, .__data=expr->__data); + return new (ast_t, .file = (ctx)->file, .start = start, .end = pos, .tag = expr->tag, .__data = expr->__data); } PARSER(parse_int) { @@ -505,7 +504,7 @@ PARSER(parse_int) { } char *str = GC_MALLOC_ATOMIC((size_t)(pos - start) + 1); memset(str, 0, (size_t)(pos - start) + 1); - for (char *src = (char*)start, *dest = str; src < pos; ++src) { + for (char *src = (char *)start, *dest = str; src < pos; ++src) { if (*src != '_') *(dest++) = *src; } @@ -514,13 +513,13 @@ PARSER(parse_int) { if (match(&pos, "%")) { double n = strtod(str, NULL) / 100.; - return NewAST(ctx->file, start, pos, Num, .n=n); + return NewAST(ctx->file, start, pos, Num, .n = n); } else if (match(&pos, "deg")) { double n = strtod(str, NULL) * RADIANS_PER_DEGREE; - return NewAST(ctx->file, start, pos, Num, .n=n); + return NewAST(ctx->file, start, pos, Num, .n = n); } - return NewAST(ctx->file, start, pos, Int, .str=str); + return NewAST(ctx->file, start, pos, Int, .str = str); } type_ast_t *parse_table_type(parse_ctx_t *ctx, const char *pos) { @@ -541,11 +540,13 @@ type_ast_t *parse_table_type(parse_ctx_t *ctx, const char *pos) { ast_t *default_value = NULL; if (match(&pos, ";") && match_word(&pos, "default")) { expect_str(ctx, pos, &pos, "=", "I expected an '=' here"); - default_value = expect(ctx, start, &pos, parse_extended_expr, "I couldn't parse the default value for this table"); + default_value = + expect(ctx, start, &pos, parse_extended_expr, "I couldn't parse the default value for this table"); } whitespace(&pos); expect_closing(ctx, &pos, "}", "I wasn't able to parse the rest of this table type"); - return NewTypeAST(ctx->file, start, pos, TableTypeAST, .key=key_type, .value=value_type, .default_value=default_value); + return NewTypeAST(ctx->file, start, pos, TableTypeAST, .key = key_type, .value = value_type, + .default_value = default_value); } type_ast_t *parse_set_type(parse_ctx_t *ctx, const char *pos) { @@ -557,7 +558,7 @@ type_ast_t *parse_set_type(parse_ctx_t *ctx, const char *pos) { pos = item_type->end; whitespace(&pos); expect_closing(ctx, &pos, "|", "I wasn't able to parse the rest of this set type"); - return NewTypeAST(ctx->file, start, pos, SetTypeAST, .item=item_type); + return NewTypeAST(ctx->file, start, pos, SetTypeAST, .item = item_type); } type_ast_t *parse_func_type(parse_ctx_t *ctx, const char *pos) { @@ -569,35 +570,31 @@ type_ast_t *parse_func_type(parse_ctx_t *ctx, const char *pos) { spaces(&pos); type_ast_t *ret = match(&pos, "->") ? optional(ctx, &pos, parse_type) : NULL; expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this function type"); - return NewTypeAST(ctx->file, start, pos, FunctionTypeAST, .args=args, .ret=ret); + return NewTypeAST(ctx->file, start, pos, FunctionTypeAST, .args = args, .ret = ret); } type_ast_t *parse_list_type(parse_ctx_t *ctx, const char *pos) { const char *start = pos; if (!match(&pos, "[")) return NULL; - type_ast_t *type = expect(ctx, start, &pos, parse_type, - "I couldn't parse a list item type after this point"); + type_ast_t *type = expect(ctx, start, &pos, parse_type, "I couldn't parse a list item type after this point"); expect_closing(ctx, &pos, "]", "I wasn't able to parse the rest of this list type"); - return NewTypeAST(ctx->file, start, pos, ListTypeAST, .item=type); + return NewTypeAST(ctx->file, start, pos, ListTypeAST, .item = type); } type_ast_t *parse_pointer_type(parse_ctx_t *ctx, const char *pos) { const char *start = pos; bool is_stack; - if (match(&pos, "@")) - is_stack = false; - else if (match(&pos, "&")) - is_stack = true; - else - return NULL; + if (match(&pos, "@")) is_stack = false; + else if (match(&pos, "&")) is_stack = true; + else return NULL; spaces(&pos); - type_ast_t *type = expect(ctx, start, &pos, parse_non_optional_type, - "I couldn't parse a pointer type after this point"); - type_ast_t *ptr_type = NewTypeAST(ctx->file, start, pos, PointerTypeAST, .pointed=type, .is_stack=is_stack); + type_ast_t *type = + expect(ctx, start, &pos, parse_non_optional_type, "I couldn't parse a pointer type after this point"); + type_ast_t *ptr_type = NewTypeAST(ctx->file, start, pos, PointerTypeAST, .pointed = type, .is_stack = is_stack); spaces(&pos); while (match(&pos, "?")) - ptr_type = NewTypeAST(ctx->file, start, pos, OptionalTypeAST, .type=ptr_type); + ptr_type = NewTypeAST(ctx->file, start, pos, OptionalTypeAST, .type = ptr_type); return ptr_type; } @@ -614,20 +611,15 @@ type_ast_t *parse_type_name(parse_ctx_t *ctx, const char *pos) { id = String(id, ".", next_id); pos = next; } - return NewTypeAST(ctx->file, start, pos, VarTypeAST, .name=id); + return NewTypeAST(ctx->file, start, pos, VarTypeAST, .name = id); } type_ast_t *parse_non_optional_type(parse_ctx_t *ctx, const char *pos) { const char *start = pos; type_ast_t *type = NULL; - bool success = (false - || (type=parse_pointer_type(ctx, pos)) - || (type=parse_list_type(ctx, pos)) - || (type=parse_table_type(ctx, pos)) - || (type=parse_set_type(ctx, pos)) - || (type=parse_type_name(ctx, pos)) - || (type=parse_func_type(ctx, pos)) - ); + bool success = (false || (type = parse_pointer_type(ctx, pos)) || (type = parse_list_type(ctx, pos)) + || (type = parse_table_type(ctx, pos)) || (type = parse_set_type(ctx, pos)) + || (type = parse_type_name(ctx, pos)) || (type = parse_func_type(ctx, pos))); if (!success && match(&pos, "(")) { whitespace(&pos); type = optional(ctx, &pos, parse_type); @@ -648,7 +640,7 @@ type_ast_t *parse_type(parse_ctx_t *ctx, const char *pos) { pos = type->end; spaces(&pos); while (match(&pos, "?")) - type = NewTypeAST(ctx->file, start, pos, OptionalTypeAST, .type=type); + type = NewTypeAST(ctx->file, start, pos, OptionalTypeAST, .type = type); return type; } @@ -659,21 +651,17 @@ PARSER(parse_num) { else if (*pos == '.' && !isdigit(pos[1])) return NULL; size_t len = strspn(pos, "0123456789_"); - if (strncmp(pos+len, "..", 2) == 0) - return NULL; - else if (pos[len] == '.') - len += 1 + strspn(pos + len + 1, "0123456789"); - else if (pos[len] != 'e' && pos[len] != 'f' && pos[len] != '%') - return NULL; + if (strncmp(pos + len, "..", 2) == 0) return NULL; + else if (pos[len] == '.') len += 1 + strspn(pos + len + 1, "0123456789"); + else if (pos[len] != 'e' && pos[len] != 'f' && pos[len] != '%') return NULL; if (pos[len] == 'e') { len += 1; - if (pos[len] == '-') - len += 1; + if (pos[len] == '-') len += 1; len += strspn(pos + len, "0123456789_"); } - char *buf = GC_MALLOC_ATOMIC(len+1); - memset(buf, 0, len+1); - for (char *src = (char*)pos, *dest = buf; src < pos+len; ++src) { + char *buf = GC_MALLOC_ATOMIC(len + 1); + memset(buf, 0, len + 1); + for (char *src = (char *)pos, *dest = buf; src < pos + len; ++src) { if (*src != '_') *(dest++) = *src; } double d = strtod(buf, NULL); @@ -681,22 +669,18 @@ PARSER(parse_num) { if (negative) d *= -1; - if (match(&pos, "%")) - d /= 100.; - else if (match(&pos, "deg")) - d *= RADIANS_PER_DEGREE; + if (match(&pos, "%")) d /= 100.; + else if (match(&pos, "deg")) d *= RADIANS_PER_DEGREE; - return NewAST(ctx->file, start, pos, Num, .n=d); + return NewAST(ctx->file, start, pos, Num, .n = d); } static INLINE bool match_separator(const char **pos) { // Either comma or newline const char *p = *pos; int separators = 0; for (;;) { - if (some_of(&p, "\r\n,")) - ++separators; - else if (!comment(&p) && !some_of(&p, " \t")) - break; + if (some_of(&p, "\r\n,")) ++separators; + else if (!comment(&p) && !some_of(&p, " \t")) break; } if (separators > 0) { *pos = p; @@ -722,15 +706,14 @@ PARSER(parse_list) { pos = suffixed->end; suffixed = parse_comprehension_suffix(ctx, item); } - items = new(ast_list_t, .ast=item, .next=items); - if (!match_separator(&pos)) - break; + items = new (ast_list_t, .ast = item, .next = items); + if (!match_separator(&pos)) break; } whitespace(&pos); expect_closing(ctx, &pos, "]", "I wasn't able to parse the rest of this list"); REVERSE_LIST(items); - return NewAST(ctx->file, start, pos, List, .items=items); + return NewAST(ctx->file, start, pos, List, .items = items); } PARSER(parse_table) { @@ -746,17 +729,16 @@ PARSER(parse_table) { if (!key) break; whitespace(&pos); if (!match(&pos, "=")) return NULL; - ast_t *value = expect(ctx, pos-1, &pos, parse_expr, "I couldn't parse the value for this table entry"); - ast_t *entry = NewAST(ctx->file, entry_start, pos, TableEntry, .key=key, .value=value); + ast_t *value = expect(ctx, pos - 1, &pos, parse_expr, "I couldn't parse the value for this table entry"); + ast_t *entry = NewAST(ctx->file, entry_start, pos, TableEntry, .key = key, .value = value); ast_t *suffixed = parse_comprehension_suffix(ctx, entry); while (suffixed) { entry = suffixed; pos = suffixed->end; suffixed = parse_comprehension_suffix(ctx, entry); } - entries = new(ast_list_t, .ast=entry, .next=entries); - if (!match_separator(&pos)) - break; + entries = new (ast_list_t, .ast = entry, .next = entries); + if (!match_separator(&pos)) break; } REVERSE_LIST(entries); @@ -771,14 +753,12 @@ PARSER(parse_table) { if (match_word(&pos, "fallback")) { whitespace(&pos); if (!match(&pos, "=")) parser_err(ctx, attr_start, pos, "I expected an '=' after 'fallback'"); - if (fallback) - parser_err(ctx, attr_start, pos, "This table already has a fallback"); + if (fallback) parser_err(ctx, attr_start, pos, "This table already has a fallback"); fallback = expect(ctx, attr_start, &pos, parse_expr, "I expected a fallback table"); } else if (match_word(&pos, "default")) { whitespace(&pos); if (!match(&pos, "=")) parser_err(ctx, attr_start, pos, "I expected an '=' after 'default'"); - if (default_value) - parser_err(ctx, attr_start, pos, "This table already has a default"); + if (default_value) parser_err(ctx, attr_start, pos, "This table already has a default"); default_value = expect(ctx, attr_start, &pos, parse_expr, "I expected a default value"); } else { break; @@ -791,13 +771,13 @@ PARSER(parse_table) { whitespace(&pos); expect_closing(ctx, &pos, "}", "I wasn't able to parse the rest of this table"); - return NewAST(ctx->file, start, pos, Table, .default_value=default_value, .entries=entries, .fallback=fallback); + return NewAST(ctx->file, start, pos, Table, .default_value = default_value, .entries = entries, + .fallback = fallback); } PARSER(parse_set) { const char *start = pos; - if (match(&pos, "||")) - return NewAST(ctx->file, start, pos, Set); + if (match(&pos, "||")) return NewAST(ctx->file, start, pos, Set); if (!match(&pos, "|")) return NULL; whitespace(&pos); @@ -813,9 +793,8 @@ PARSER(parse_set) { pos = suffixed->end; suffixed = parse_comprehension_suffix(ctx, item); } - items = new(ast_list_t, .ast=item, .next=items); - if (!match_separator(&pos)) - break; + items = new (ast_list_t, .ast = item, .next = items); + if (!match_separator(&pos)) break; } REVERSE_LIST(items); @@ -823,7 +802,7 @@ PARSER(parse_set) { whitespace(&pos); expect_closing(ctx, &pos, "|", "I wasn't able to parse the rest of this set"); - return NewAST(ctx->file, start, pos, Set, .items=items); + return NewAST(ctx->file, start, pos, Set, .items = items); } ast_t *parse_field_suffix(parse_ctx_t *ctx, ast_t *lhs) { @@ -834,49 +813,41 @@ ast_t *parse_field_suffix(parse_ctx_t *ctx, ast_t *lhs) { if (*pos == '.') return NULL; whitespace(&pos); bool dollar = match(&pos, "$"); - const char* field = get_id(&pos); + const char *field = get_id(&pos); if (!field) return NULL; if (dollar) field = String("$", field); - return NewAST(ctx->file, lhs->start, pos, FieldAccess, .fielded=lhs, .field=field); + return NewAST(ctx->file, lhs->start, pos, FieldAccess, .fielded = lhs, .field = field); } ast_t *parse_optional_suffix(parse_ctx_t *ctx, ast_t *lhs) { if (!lhs) return NULL; const char *pos = lhs->end; - if (match(&pos, "?")) - return NewAST(ctx->file, lhs->start, pos, Optional, .value=lhs); - else - return NULL; + if (match(&pos, "?")) return NewAST(ctx->file, lhs->start, pos, Optional, .value = lhs); + else return NULL; } ast_t *parse_non_optional_suffix(parse_ctx_t *ctx, ast_t *lhs) { if (!lhs) return NULL; const char *pos = lhs->end; - if (match(&pos, "!")) - return NewAST(ctx->file, lhs->start, pos, NonOptional, .value=lhs); - else - return NULL; + if (match(&pos, "!")) return NewAST(ctx->file, lhs->start, pos, NonOptional, .value = lhs); + else return NULL; } PARSER(parse_reduction) { const char *start = pos; if (!match(&pos, "(")) return NULL; - + whitespace(&pos); ast_e op = match_binary_operator(&pos); if (op == Unknown) return NULL; - ast_t *key = NewAST(ctx->file, pos, pos, Var, .name="$"); - for (bool progress = true; progress; ) { + ast_t *key = NewAST(ctx->file, pos, pos, Var, .name = "$"); + for (bool progress = true; progress;) { ast_t *new_term; - progress = (false - || (new_term=parse_index_suffix(ctx, key)) - || (new_term=parse_method_call_suffix(ctx, key)) - || (new_term=parse_field_suffix(ctx, key)) - || (new_term=parse_fncall_suffix(ctx, key)) - || (new_term=parse_optional_suffix(ctx, key)) - || (new_term=parse_non_optional_suffix(ctx, key)) - ); + progress = + (false || (new_term = parse_index_suffix(ctx, key)) || (new_term = parse_method_call_suffix(ctx, key)) + || (new_term = parse_field_suffix(ctx, key)) || (new_term = parse_fncall_suffix(ctx, key)) + || (new_term = parse_optional_suffix(ctx, key)) || (new_term = parse_non_optional_suffix(ctx, key))); if (progress) key = new_term; } if (key && key->tag == Var) key = NULL; @@ -897,7 +868,7 @@ PARSER(parse_reduction) { whitespace(&pos); expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this reduction"); - return NewAST(ctx->file, start, pos, Reduction, .iter=iter, .op=op, .key=key); + return NewAST(ctx->file, start, pos, Reduction, .iter = iter, .op = op, .key = key); } ast_t *parse_index_suffix(parse_ctx_t *ctx, ast_t *lhs) { @@ -910,7 +881,7 @@ ast_t *parse_index_suffix(parse_ctx_t *ctx, ast_t *lhs) { whitespace(&pos); bool unchecked = match(&pos, ";") && (spaces(&pos), match_word(&pos, "unchecked") != 0); expect_closing(ctx, &pos, "]", "I wasn't able to parse the rest of this index"); - return NewAST(ctx->file, start, pos, Index, .indexed=lhs, .index=index, .unchecked=unchecked); + return NewAST(ctx->file, start, pos, Index, .indexed = lhs, .index = index, .unchecked = unchecked); } ast_t *parse_comprehension_suffix(parse_ctx_t *ctx, ast_t *expr) { @@ -924,12 +895,10 @@ ast_t *parse_comprehension_suffix(parse_ctx_t *ctx, ast_t *expr) { ast_list_t *vars = NULL; for (;;) { ast_t *var = optional(ctx, &pos, parse_var); - if (var) - vars = new(ast_list_t, .ast=var, .next=vars); + if (var) vars = new (ast_list_t, .ast = var, .next = vars); spaces(&pos); - if (!match(&pos, ",")) - break; + if (!match(&pos, ",")) break; } REVERSE_LIST(vars); @@ -940,13 +909,13 @@ ast_t *parse_comprehension_suffix(parse_ctx_t *ctx, ast_t *expr) { ast_t *filter = NULL; if (match_word(&next_pos, "if")) { pos = next_pos; - filter = expect(ctx, pos-2, &pos, parse_expr, "I expected a condition for this 'if'"); + filter = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'if'"); } else if (match_word(&next_pos, "unless")) { pos = next_pos; - filter = expect(ctx, pos-2, &pos, parse_expr, "I expected a condition for this 'unless'"); + filter = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'unless'"); filter = WrapAST(filter, Not, filter); } - return NewAST(ctx->file, start, pos, Comprehension, .expr=expr, .vars=vars, .iter=iter, .filter=filter); + return NewAST(ctx->file, start, pos, Comprehension, .expr = expr, .vars = vars, .iter = iter, .filter = filter); } ast_t *parse_optional_conditional_suffix(parse_ctx_t *ctx, ast_t *stmt) { @@ -955,12 +924,12 @@ ast_t *parse_optional_conditional_suffix(parse_ctx_t *ctx, ast_t *stmt) { const char *start = stmt->start; const char *pos = stmt->end; if (match_word(&pos, "if")) { - ast_t *condition = expect(ctx, pos-2, &pos, parse_expr, "I expected a condition for this 'if'"); - return NewAST(ctx->file, start, pos, If, .condition=condition, .body=stmt); + ast_t *condition = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'if'"); + return NewAST(ctx->file, start, pos, If, .condition = condition, .body = stmt); } else if (match_word(&pos, "unless")) { - ast_t *condition = expect(ctx, pos-2, &pos, parse_expr, "I expected a condition for this 'unless'"); + ast_t *condition = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'unless'"); condition = WrapAST(condition, Not, condition); - return NewAST(ctx->file, start, pos, If, .condition=condition, .body=stmt); + return NewAST(ctx->file, start, pos, If, .condition = condition, .body = stmt); } else { return stmt; } @@ -972,22 +941,17 @@ PARSER(parse_if) { int64_t starting_indent = get_indent(ctx, pos); bool unless; - if (match_word(&pos, "if")) - unless = false; - else if (match_word(&pos, "unless")) - unless = true; - else - return NULL; + if (match_word(&pos, "if")) unless = false; + else if (match_word(&pos, "unless")) unless = true; + else return NULL; ast_t *condition = unless ? NULL : optional(ctx, &pos, parse_declaration); - if (!condition) - condition = expect(ctx, start, &pos, parse_expr, "I expected to find a condition for this 'if'"); + if (!condition) condition = expect(ctx, start, &pos, parse_expr, "I expected to find a condition for this 'if'"); - if (unless) - condition = WrapAST(condition, Not, condition); + if (unless) condition = WrapAST(condition, Not, condition); (void)match_word(&pos, "then"); // Optional 'then' - ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'if' statement"); + ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'if' statement"); const char *tmp = pos; whitespace(&tmp); @@ -997,10 +961,9 @@ PARSER(parse_if) { pos = tmp; spaces(&pos); else_body = optional(ctx, &pos, parse_if); - if (!else_body) - else_body = expect(ctx, else_start, &pos, parse_block, "I expected a body for this 'else'"); + if (!else_body) else_body = expect(ctx, else_start, &pos, parse_block, "I expected a body for this 'else'"); } - return NewAST(ctx->file, start, pos, If, .condition=condition, .body=body, .else_body=else_body); + return NewAST(ctx->file, start, pos, If, .condition = condition, .body = body, .else_body = else_body); } PARSER(parse_when) { @@ -1008,12 +971,10 @@ PARSER(parse_when) { const char *start = pos; int64_t starting_indent = get_indent(ctx, pos); - if (!match_word(&pos, "when")) - return NULL; + if (!match_word(&pos, "when")) return NULL; ast_t *subject = optional(ctx, &pos, parse_declaration); - if (!subject) subject = expect(ctx, start, &pos, parse_expr, - "I expected to find an expression for this 'when'"); + if (!subject) subject = expect(ctx, start, &pos, parse_expr, "I expected to find an expression for this 'when'"); when_clause_t *clauses = NULL; const char *tmp = pos; @@ -1023,14 +984,14 @@ PARSER(parse_when) { spaces(&pos); ast_t *pattern = expect(ctx, start, &pos, parse_expr, "I expected a pattern to match here"); spaces(&pos); - when_clause_t *new_clauses = new(when_clause_t, .pattern=pattern, .next=clauses); + when_clause_t *new_clauses = new (when_clause_t, .pattern = pattern, .next = clauses); while (match(&pos, ",")) { pattern = expect(ctx, start, &pos, parse_expr, "I expected a pattern to match here"); - new_clauses = new(when_clause_t, .pattern=pattern, .next=new_clauses); + new_clauses = new (when_clause_t, .pattern = pattern, .next = new_clauses); spaces(&pos); } (void)match_word(&pos, "then"); // Optional 'then' - ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'when' clause"); + ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'when' clause"); for (when_clause_t *c = new_clauses; c && c != clauses; c = c->next) { c->body = body; } @@ -1044,9 +1005,9 @@ PARSER(parse_when) { const char *else_start = pos; if (get_indent(ctx, tmp) == starting_indent && match_word(&tmp, "else")) { pos = tmp; - else_body = expect(ctx, else_start, &pos, parse_block, "I expected a body for this 'else'"); + else_body = expect(ctx, else_start, &pos, parse_block, "I expected a body for this 'else'"); } - return NewAST(ctx->file, start, pos, When, .subject=subject, .clauses=clauses, .else_body=else_body); + return NewAST(ctx->file, start, pos, When, .subject = subject, .clauses = clauses, .else_body = else_body); } PARSER(parse_for) { @@ -1058,12 +1019,10 @@ PARSER(parse_for) { ast_list_t *vars = NULL; for (;;) { ast_t *var = optional(ctx, &pos, parse_var); - if (var) - vars = new(ast_list_t, .ast=var, .next=vars); + if (var) vars = new (ast_list_t, .ast = var, .next = vars); spaces(&pos); - if (!match(&pos, ",")) - break; + if (!match(&pos, ",")) break; } spaces(&pos); @@ -1073,7 +1032,7 @@ PARSER(parse_for) { (void)match_word(&pos, "do"); // Optional 'do' - ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'for'"); + ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'for'"); const char *else_start = pos; whitespace(&else_start); @@ -1083,15 +1042,15 @@ PARSER(parse_for) { empty = expect(ctx, pos, &pos, parse_block, "I expected a body for this 'else'"); } REVERSE_LIST(vars); - return NewAST(ctx->file, start, pos, For, .vars=vars, .iter=iter, .body=body, .empty=empty); + return NewAST(ctx->file, start, pos, For, .vars = vars, .iter = iter, .body = body, .empty = empty); } PARSER(parse_do) { // do [<indent>] body const char *start = pos; if (!match_word(&pos, "do")) return NULL; - ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'do'"); - return NewAST(ctx->file, start, pos, Block, .statements=Match(body, Block)->statements); + ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'do'"); + return NewAST(ctx->file, start, pos, Block, .statements = Match(body, Block)->statements); } PARSER(parse_while) { @@ -1104,22 +1063,22 @@ PARSER(parse_while) { if (match_word(&tmp, "when")) { ast_t *when = expect(ctx, start, &pos, parse_when, "I expected a 'when' block after this"); if (!when->__data.When.else_body) when->__data.When.else_body = NewAST(ctx->file, pos, pos, Stop); - return NewAST(ctx->file, start, pos, While, .body=when); + return NewAST(ctx->file, start, pos, While, .body = when); } (void)match_word(&pos, "do"); // Optional 'do' ast_t *condition = expect(ctx, start, &pos, parse_expr, "I don't see a viable condition for this 'while'"); - ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'while'"); - return NewAST(ctx->file, start, pos, While, .condition=condition, .body=body); + ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'while'"); + return NewAST(ctx->file, start, pos, While, .condition = condition, .body = body); } PARSER(parse_repeat) { // repeat [<indent>] body const char *start = pos; if (!match_word(&pos, "repeat")) return NULL; - ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'repeat'"); - return NewAST(ctx->file, start, pos, Repeat, .body=body); + ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a body for this 'repeat'"); + return NewAST(ctx->file, start, pos, Repeat, .body = body); } PARSER(parse_heap_alloc) { @@ -1130,16 +1089,14 @@ PARSER(parse_heap_alloc) { for (;;) { ast_t *new_term; - if ((new_term=parse_index_suffix(ctx, val)) - || (new_term=parse_fncall_suffix(ctx, val)) - || (new_term=parse_method_call_suffix(ctx, val)) - || (new_term=parse_field_suffix(ctx, val))) { + if ((new_term = parse_index_suffix(ctx, val)) || (new_term = parse_fncall_suffix(ctx, val)) + || (new_term = parse_method_call_suffix(ctx, val)) || (new_term = parse_field_suffix(ctx, val))) { val = new_term; } else break; } pos = val->end; - ast_t *ast = NewAST(ctx->file, start, pos, HeapAllocate, .value=val); + ast_t *ast = NewAST(ctx->file, start, pos, HeapAllocate, .value = val); for (;;) { ast_t *next = parse_optional_suffix(ctx, ast); if (!next) next = parse_non_optional_suffix(ctx, ast); @@ -1157,16 +1114,14 @@ PARSER(parse_stack_reference) { for (;;) { ast_t *new_term; - if ((new_term=parse_index_suffix(ctx, val)) - || (new_term=parse_fncall_suffix(ctx, val)) - || (new_term=parse_method_call_suffix(ctx, val)) - || (new_term=parse_field_suffix(ctx, val))) { + if ((new_term = parse_index_suffix(ctx, val)) || (new_term = parse_fncall_suffix(ctx, val)) + || (new_term = parse_method_call_suffix(ctx, val)) || (new_term = parse_field_suffix(ctx, val))) { val = new_term; } else break; } pos = val->end; - ast_t *ast = NewAST(ctx->file, start, pos, StackReference, .value=val); + ast_t *ast = NewAST(ctx->file, start, pos, StackReference, .value = val); for (;;) { ast_t *next = parse_optional_suffix(ctx, ast); if (!next) next = parse_non_optional_suffix(ctx, ast); @@ -1181,7 +1136,7 @@ PARSER(parse_not) { if (!match_word(&pos, "not")) return NULL; spaces(&pos); ast_t *val = expect(ctx, start, &pos, parse_term, "I expected an expression for this 'not'"); - return NewAST(ctx->file, start, pos, Not, .value=val); + return NewAST(ctx->file, start, pos, Not, .value = val); } PARSER(parse_negative) { @@ -1189,21 +1144,18 @@ PARSER(parse_negative) { if (!match(&pos, "-")) return NULL; spaces(&pos); ast_t *val = expect(ctx, start, &pos, parse_term, "I expected an expression for this '-'"); - return NewAST(ctx->file, start, pos, Negative, .value=val); + return NewAST(ctx->file, start, pos, Negative, .value = val); } PARSER(parse_bool) { const char *start = pos; - if (match_word(&pos, "yes")) - return NewAST(ctx->file, start, pos, Bool, .b=true); - else if (match_word(&pos, "no")) - return NewAST(ctx->file, start, pos, Bool, .b=false); - else - return NULL; + if (match_word(&pos, "yes")) return NewAST(ctx->file, start, pos, Bool, .b = true); + else if (match_word(&pos, "no")) return NewAST(ctx->file, start, pos, Bool, .b = false); + else return NULL; } -ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, char open_quote, char close_quote, char open_interp, bool allow_escapes) -{ +ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, char open_quote, char close_quote, + char open_interp, bool allow_escapes) { const char *pos = *out_pos; int64_t starting_indent = get_indent(ctx, pos); int64_t string_indent = starting_indent + SPACES_PER_INDENT; @@ -1213,27 +1165,29 @@ ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, char open int depth = 1; bool leading_newline = false; int64_t plain_span_len = 0; -#define FLUSH_PLAIN_SPAN() do { \ - if (plain_span_len > 0) { \ - chunk = Texts(chunk, Text$from_strn(pos - plain_span_len, (size_t)plain_span_len)); \ - plain_span_len = 0; \ - } } while (0) - for (const char *end = ctx->file->text + ctx->file->len; pos < end && depth > 0; ) { +#define FLUSH_PLAIN_SPAN() \ + do { \ + if (plain_span_len > 0) { \ + chunk = Texts(chunk, Text$from_strn(pos - plain_span_len, (size_t)plain_span_len)); \ + plain_span_len = 0; \ + } \ + } while (0) + for (const char *end = ctx->file->text + ctx->file->len; pos < end && depth > 0;) { const char *after_indentation = pos; if (*pos == open_interp) { // Interpolation FLUSH_PLAIN_SPAN(); const char *interp_start = pos; if (chunk.length > 0) { - ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .text=chunk); - chunks = new(ast_list_t, .ast=literal, .next=chunks); + ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .text = chunk); + chunks = new (ast_list_t, .ast = literal, .next = chunks); chunk = EMPTY_TEXT; } ++pos; ast_t *interp; if (*pos == ' ' || *pos == '\t') - parser_err(ctx, pos, pos+1, "Whitespace is not allowed before an interpolation here"); + parser_err(ctx, pos, pos + 1, "Whitespace is not allowed before an interpolation here"); interp = expect(ctx, interp_start, &pos, parse_term_no_suffix, "I expected an interpolation term here"); - chunks = new(ast_list_t, .ast=interp, .next=chunks); + chunks = new (ast_list_t, .ast = interp, .next = chunks); chunk_start = pos; } else if (allow_escapes && *pos == '\\') { FLUSH_PLAIN_SPAN(); @@ -1248,8 +1202,7 @@ ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, char open } else if (!leading_newline && *pos == close_quote) { // Nested pair end if (get_indent(ctx, pos) == starting_indent) { --depth; - if (depth == 0) - break; + if (depth == 0) break; } plain_span_len += 1; ++pos; @@ -1270,14 +1223,14 @@ ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, char open // Multi-line split continue; } else { - parser_err(ctx, pos, eol(pos), "This multi-line string should be either indented or have '..' at the front"); + parser_err(ctx, pos, eol(pos), + "This multi-line string should be either indented or have '..' at the front"); } } else { // Plain character ucs4_t codepoint; - const char *next = (const char*)u8_next(&codepoint, (const uint8_t*)pos); + const char *next = (const char *)u8_next(&codepoint, (const uint8_t *)pos); plain_span_len += (int64_t)(next - pos); - if (next == NULL) - break; + if (next == NULL) break; pos = next; } } @@ -1286,8 +1239,8 @@ ast_list_t *_parse_text_helper(parse_ctx_t *ctx, const char **out_pos, char open #undef FLUSH_PLAIN_SPAN if (chunk.length > 0) { - ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .text=chunk); - chunks = new(ast_list_t, .ast=literal, .next=chunks); + ast_t *literal = NewAST(ctx->file, chunk_start, pos, TextLiteral, .text = chunk); + chunks = new (ast_list_t, .ast = literal, .next = chunks); chunk = EMPTY_TEXT; } @@ -1323,7 +1276,8 @@ PARSER(parse_text) { } static const char *quote_chars = "\"'`|/;([{<"; if (!strchr(quote_chars, *pos)) - parser_err(ctx, pos, pos+1, "This is not a valid string quotation character. Valid characters are: \"'`|/;([{<"); + parser_err(ctx, pos, pos + 1, + "This is not a valid string quotation character. Valid characters are: \"'`|/;([{<"); open_quote = *pos; ++pos; close_quote = closing[(int)open_quote] ? closing[(int)open_quote] : open_quote; @@ -1334,18 +1288,16 @@ PARSER(parse_text) { bool allow_escapes = (open_quote != '`'); ast_list_t *chunks = _parse_text_helper(ctx, &pos, open_quote, close_quote, open_interp, allow_escapes); bool colorize = match(&pos, "~") && match_word(&pos, "colorized"); - return NewAST(ctx->file, start, pos, TextJoin, .lang=lang, .children=chunks, .colorize=colorize); + return NewAST(ctx->file, start, pos, TextJoin, .lang = lang, .children = chunks, .colorize = colorize); } PARSER(parse_path) { // "(" ("~/" / "./" / "../" / "/") ... ")" const char *start = pos; - if (!match(&pos, "(")) - return NULL; + if (!match(&pos, "(")) return NULL; - if (!(*pos == '~' || *pos == '.' || *pos == '/')) - return NULL; + if (!(*pos == '~' || *pos == '.' || *pos == '/')) return NULL; const char *path_start = pos; size_t len = 1; @@ -1360,13 +1312,13 @@ PARSER(parse_path) { paren_depth -= 1; if (paren_depth <= 0) break; } else if (pos[len] == '\r' || pos[len] == '\n') { - parser_err(ctx, path_start, &pos[len-1], "This path was not closed"); + parser_err(ctx, path_start, &pos[len - 1], "This path was not closed"); } len += 1; } pos += len + 1; - char *path = String(string_slice(path_start, .length=len)); - for (char *src = path, *dest = path; ; ) { + char *path = String(string_slice(path_start, .length = len)); + for (char *src = path, *dest = path;;) { if (src[0] == '\\') { *(dest++) = src[1]; src += 2; @@ -1377,7 +1329,7 @@ PARSER(parse_path) { break; } } - return NewAST(ctx->file, start, pos, Path, .path=path); + return NewAST(ctx->file, start, pos, Path, .path = path); } PARSER(parse_pass) { @@ -1389,7 +1341,7 @@ PARSER(parse_defer) { const char *start = pos; if (!match_word(&pos, "defer")) return NULL; ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a block to be deferred here"); - return NewAST(ctx->file, start, pos, Defer, .body=body); + return NewAST(ctx->file, start, pos, Defer, .body = body); } PARSER(parse_skip) { @@ -1399,7 +1351,7 @@ PARSER(parse_skip) { if (match_word(&pos, "for")) target = "for"; else if (match_word(&pos, "while")) target = "while"; else target = get_id(&pos); - ast_t *skip = NewAST(ctx->file, start, pos, Skip, .target=target); + ast_t *skip = NewAST(ctx->file, start, pos, Skip, .target = target); skip = parse_optional_conditional_suffix(ctx, skip); return skip; } @@ -1411,7 +1363,7 @@ PARSER(parse_stop) { if (match_word(&pos, "for")) target = "for"; else if (match_word(&pos, "while")) target = "while"; else target = get_id(&pos); - ast_t *stop = NewAST(ctx->file, start, pos, Stop, .target=target); + ast_t *stop = NewAST(ctx->file, start, pos, Stop, .target = target); stop = parse_optional_conditional_suffix(ctx, stop); return stop; } @@ -1420,112 +1372,87 @@ PARSER(parse_return) { const char *start = pos; if (!match_word(&pos, "return")) return NULL; ast_t *value = optional(ctx, &pos, parse_expr); - ast_t *ret = NewAST(ctx->file, start, pos, Return, .value=value); + ast_t *ret = NewAST(ctx->file, start, pos, Return, .value = value); ret = parse_optional_conditional_suffix(ctx, ret); return ret; } PARSER(parse_lambda) { const char *start = pos; - if (!match_word(&pos, "func")) - return NULL; + if (!match_word(&pos, "func")) return NULL; spaces(&pos); - if (!match(&pos, "(")) - return NULL; + if (!match(&pos, "(")) return NULL; arg_ast_t *args = parse_args(ctx, &pos); spaces(&pos); type_ast_t *ret = match(&pos, "->") ? optional(ctx, &pos, parse_type) : NULL; spaces(&pos); expect_closing(ctx, &pos, ")", "I was expecting a ')' to finish this anonymous function's arguments"); ast_t *body = optional(ctx, &pos, parse_block); - if (!body) body = NewAST(ctx->file, pos, pos, Block, .statements=NULL); - return NewAST(ctx->file, start, pos, Lambda, .id=ctx->next_lambda_id++, .args=args, .ret_type=ret, .body=body); + if (!body) body = NewAST(ctx->file, pos, pos, Block, .statements = NULL); + return NewAST(ctx->file, start, pos, Lambda, .id = ctx->next_lambda_id++, .args = args, .ret_type = ret, + .body = body); } PARSER(parse_none) { const char *start = pos; - if (!match_word(&pos, "none")) - return NULL; + if (!match_word(&pos, "none")) return NULL; return NewAST(ctx->file, start, pos, None); } PARSER(parse_deserialize) { const char *start = pos; - if (!match_word(&pos, "deserialize")) - return NULL; + if (!match_word(&pos, "deserialize")) return NULL; spaces(&pos); expect_str(ctx, start, &pos, "(", "I expected arguments for this `deserialize` call"); whitespace(&pos); ast_t *value = expect(ctx, start, &pos, parse_extended_expr, "I expected an expression here"); whitespace(&pos); - expect_str(ctx, start, &pos, "->", "I expected a `-> Type` for this `deserialize` call so I know what it deserializes to"); + expect_str(ctx, start, &pos, "->", + "I expected a `-> Type` for this `deserialize` call so I know what it deserializes to"); whitespace(&pos); type_ast_t *type = expect(ctx, start, &pos, parse_type, "I couldn't parse the type for this deserialization"); whitespace(&pos); expect_closing(ctx, &pos, ")", "I expected a closing ')' for this `deserialize` call"); - return NewAST(ctx->file, start, pos, Deserialize, .value=value, .type=type); + return NewAST(ctx->file, start, pos, Deserialize, .value = value, .type = type); } PARSER(parse_var) { const char *start = pos; - const char* name = get_id(&pos); + const char *name = get_id(&pos); if (!name) return NULL; - return NewAST(ctx->file, start, pos, Var, .name=name); + return NewAST(ctx->file, start, pos, Var, .name = name); } PARSER(parse_term_no_suffix) { spaces(&pos); ast_t *term = NULL; - (void)( - false - || (term=parse_none(ctx, pos)) - || (term=parse_num(ctx, pos)) // Must come before int - || (term=parse_int(ctx, pos)) - || (term=parse_negative(ctx, pos)) // Must come after num/int - || (term=parse_heap_alloc(ctx, pos)) - || (term=parse_stack_reference(ctx, pos)) - || (term=parse_bool(ctx, pos)) - || (term=parse_text(ctx, pos)) - || (term=parse_path(ctx, pos)) - || (term=parse_lambda(ctx, pos)) - || (term=parse_parens(ctx, pos)) - || (term=parse_table(ctx, pos)) - || (term=parse_set(ctx, pos)) - || (term=parse_deserialize(ctx, pos)) - || (term=parse_var(ctx, pos)) - || (term=parse_list(ctx, pos)) - || (term=parse_reduction(ctx, pos)) - || (term=parse_pass(ctx, pos)) - || (term=parse_defer(ctx, pos)) - || (term=parse_skip(ctx, pos)) - || (term=parse_stop(ctx, pos)) - || (term=parse_return(ctx, pos)) - || (term=parse_not(ctx, pos)) - || (term=parse_extern(ctx, pos)) - || (term=parse_inline_c(ctx, pos)) - ); + (void)(false || (term = parse_none(ctx, pos)) || (term = parse_num(ctx, pos)) // Must come before int + || (term = parse_int(ctx, pos)) || (term = parse_negative(ctx, pos)) // Must come after num/int + || (term = parse_heap_alloc(ctx, pos)) || (term = parse_stack_reference(ctx, pos)) + || (term = parse_bool(ctx, pos)) || (term = parse_text(ctx, pos)) || (term = parse_path(ctx, pos)) + || (term = parse_lambda(ctx, pos)) || (term = parse_parens(ctx, pos)) || (term = parse_table(ctx, pos)) + || (term = parse_set(ctx, pos)) || (term = parse_deserialize(ctx, pos)) || (term = parse_var(ctx, pos)) + || (term = parse_list(ctx, pos)) || (term = parse_reduction(ctx, pos)) || (term = parse_pass(ctx, pos)) + || (term = parse_defer(ctx, pos)) || (term = parse_skip(ctx, pos)) || (term = parse_stop(ctx, pos)) + || (term = parse_return(ctx, pos)) || (term = parse_not(ctx, pos)) || (term = parse_extern(ctx, pos)) + || (term = parse_inline_c(ctx, pos))); return term; } PARSER(parse_term) { const char *start = pos; - if (match(&pos, "???")) - parser_err(ctx, start, pos, "This value needs to be filled in!"); + if (match(&pos, "???")) parser_err(ctx, start, pos, "This value needs to be filled in!"); ast_t *term = parse_term_no_suffix(ctx, pos); if (!term) return NULL; - for (bool progress = true; progress; ) { + for (bool progress = true; progress;) { ast_t *new_term; - progress = (false - || (new_term=parse_index_suffix(ctx, term)) - || (new_term=parse_method_call_suffix(ctx, term)) - || (new_term=parse_field_suffix(ctx, term)) - || (new_term=parse_fncall_suffix(ctx, term)) - || (new_term=parse_optional_suffix(ctx, term)) - || (new_term=parse_non_optional_suffix(ctx, term)) - ); + progress = + (false || (new_term = parse_index_suffix(ctx, term)) || (new_term = parse_method_call_suffix(ctx, term)) + || (new_term = parse_field_suffix(ctx, term)) || (new_term = parse_fncall_suffix(ctx, term)) + || (new_term = parse_optional_suffix(ctx, term)) || (new_term = parse_non_optional_suffix(ctx, term))); if (progress) term = new_term; } return term; @@ -1560,18 +1487,16 @@ ast_t *parse_method_call_suffix(parse_ctx_t *ctx, ast_t *self) { if (name) parser_err(ctx, arg_start, pos, "I expected an argument here"); break; } - args = new(arg_ast_t, .name=name, .value=arg, .next=args); - if (!match_separator(&pos)) - break; + args = new (arg_ast_t, .name = name, .value = arg, .next = args); + if (!match_separator(&pos)) break; } REVERSE_LIST(args); whitespace(&pos); - if (!match(&pos, ")")) - parser_err(ctx, start, pos, "This parenthesis is unclosed"); + if (!match(&pos, ")")) parser_err(ctx, start, pos, "This parenthesis is unclosed"); - return NewAST(ctx->file, start, pos, MethodCall, .self=self, .name=fn, .args=args); + return NewAST(ctx->file, start, pos, MethodCall, .self = self, .name = fn, .args = args); } ast_t *parse_fncall_suffix(parse_ctx_t *ctx, ast_t *fn) { @@ -1596,26 +1521,22 @@ ast_t *parse_fncall_suffix(parse_ctx_t *ctx, ast_t *fn) { ast_t *arg = optional(ctx, &pos, parse_expr); if (!arg) { - if (name) - parser_err(ctx, arg_start, pos, "I expected an argument here"); + if (name) parser_err(ctx, arg_start, pos, "I expected an argument here"); break; } - args = new(arg_ast_t, .name=name, .value=arg, .next=args); - if (!match_separator(&pos)) - break; + args = new (arg_ast_t, .name = name, .value = arg, .next = args); + if (!match_separator(&pos)) break; } whitespace(&pos); - if (!match(&pos, ")")) - parser_err(ctx, start, pos, "This parenthesis is unclosed"); + if (!match(&pos, ")")) parser_err(ctx, start, pos, "This parenthesis is unclosed"); REVERSE_LIST(args); - return NewAST(ctx->file, start, pos, FunctionCall, .fn=fn, .args=args); + return NewAST(ctx->file, start, pos, FunctionCall, .fn = fn, .args = args); } -ast_e match_binary_operator(const char **pos) -{ +ast_e match_binary_operator(const char **pos) { switch (**pos) { case '+': { *pos += 1; @@ -1633,20 +1554,18 @@ ast_e match_binary_operator(const char **pos) case '<': { *pos += 1; if (match(pos, "=")) return LessThanOrEquals; // "<=" - else if (match(pos, ">")) return Compare; // "<>" + else if (match(pos, ">")) return Compare; // "<>" else if (match(pos, "<")) { - if (match(pos, "<")) - return UnsignedLeftShift; // "<<<" - return LeftShift; // "<<" + if (match(pos, "<")) return UnsignedLeftShift; // "<<<" + return LeftShift; // "<<" } else return LessThan; } case '>': { *pos += 1; if (match(pos, "=")) return GreaterThanOrEquals; // ">=" if (match(pos, ">")) { - if (match(pos, ">")) - return UnsignedRightShift; // ">>>" - return RightShift; // ">>" + if (match(pos, ">")) return UnsignedRightShift; // ">>>" + return RightShift; // ">>" } return GreaterThan; } @@ -1672,20 +1591,17 @@ static ast_t *parse_infix_expr(parse_ctx_t *ctx, const char *pos, int min_tightn int64_t starting_line = get_line_number(ctx->file, pos); int64_t starting_indent = get_indent(ctx, pos); spaces(&pos); - for (ast_e op; (op=match_binary_operator(&pos)) != Unknown && op_tightness[op] >= min_tightness; spaces(&pos)) { + for (ast_e op; (op = match_binary_operator(&pos)) != Unknown && op_tightness[op] >= min_tightness; spaces(&pos)) { ast_t *key = NULL; if (op == Min || op == Max) { - key = NewAST(ctx->file, pos, pos, Var, .name="$"); - for (bool progress = true; progress; ) { + key = NewAST(ctx->file, pos, pos, Var, .name = "$"); + for (bool progress = true; progress;) { ast_t *new_term; - progress = (false - || (new_term=parse_index_suffix(ctx, key)) - || (new_term=parse_method_call_suffix(ctx, key)) - || (new_term=parse_field_suffix(ctx, key)) - || (new_term=parse_fncall_suffix(ctx, key)) - || (new_term=parse_optional_suffix(ctx, key)) - || (new_term=parse_non_optional_suffix(ctx, key)) - ); + progress = + (false || (new_term = parse_index_suffix(ctx, key)) + || (new_term = parse_method_call_suffix(ctx, key)) || (new_term = parse_field_suffix(ctx, key)) + || (new_term = parse_fncall_suffix(ctx, key)) || (new_term = parse_optional_suffix(ctx, key)) + || (new_term = parse_non_optional_suffix(ctx, key))); if (progress) key = new_term; } if (key && key->tag == Var) key = NULL; @@ -1699,21 +1615,20 @@ static ast_t *parse_infix_expr(parse_ctx_t *ctx, const char *pos, int min_tightn ast_t *rhs = parse_infix_expr(ctx, pos, op_tightness[op] + 1); if (!rhs) break; pos = rhs->end; - + if (op == Min) { - return NewAST(ctx->file, lhs->start, rhs->end, Min, .lhs=lhs, .rhs=rhs, .key=key); + return NewAST(ctx->file, lhs->start, rhs->end, Min, .lhs = lhs, .rhs = rhs, .key = key); } else if (op == Max) { - return NewAST(ctx->file, lhs->start, rhs->end, Max, .lhs=lhs, .rhs=rhs, .key=key); + return NewAST(ctx->file, lhs->start, rhs->end, Max, .lhs = lhs, .rhs = rhs, .key = key); } else { - lhs = new(ast_t, .file=ctx->file, .start=lhs->start, .end=rhs->end, .tag=op, .__data.Plus.lhs=lhs, .__data.Plus.rhs=rhs); + lhs = new (ast_t, .file = ctx->file, .start = lhs->start, .end = rhs->end, .tag = op, + .__data.Plus.lhs = lhs, .__data.Plus.rhs = rhs); } } return lhs; } -PARSER(parse_expr) { - return parse_infix_expr(ctx, pos, 0); -} +PARSER(parse_expr) { return parse_infix_expr(ctx, pos, 0); } PARSER(parse_declaration) { const char *start = pos; @@ -1731,17 +1646,15 @@ PARSER(parse_declaration) { if (!val) { if (optional(ctx, &pos, parse_use)) parser_err(ctx, start, pos, "'use' statements are only allowed at the top level of a file"); - else - parser_err(ctx, pos, eol(pos), "This is not a valid expression"); + else parser_err(ctx, pos, eol(pos), "This is not a valid expression"); } } - return NewAST(ctx->file, start, pos, Declare, .var=var, .type=type, .value=val); + return NewAST(ctx->file, start, pos, Declare, .var = var, .type = type, .value = val); } PARSER(parse_top_declaration) { ast_t *declaration = parse_declaration(ctx, pos); - if (declaration) - declaration->__data.Declare.top_level = true; + if (declaration) declaration->__data.Declare.top_level = true; return declaration; } @@ -1766,7 +1679,8 @@ PARSER(parse_update) { else if (match(&pos, "xor=")) op = XorUpdate; else return NULL; ast_t *rhs = expect(ctx, start, &pos, parse_extended_expr, "I expected an expression here"); - return new(ast_t, .file=ctx->file, .start=start, .end=pos, .tag=op, .__data.PlusUpdate.lhs=lhs, .__data.PlusUpdate.rhs=rhs); + return new (ast_t, .file = ctx->file, .start = start, .end = pos, .tag = op, .__data.PlusUpdate.lhs = lhs, + .__data.PlusUpdate.rhs = rhs); } PARSER(parse_assignment) { @@ -1775,7 +1689,7 @@ PARSER(parse_assignment) { for (;;) { ast_t *lhs = optional(ctx, &pos, parse_term); if (!lhs) break; - targets = new(ast_list_t, .ast=lhs, .next=targets); + targets = new (ast_list_t, .ast = lhs, .next = targets); spaces(&pos); if (!match(&pos, ",")) break; whitespace(&pos); @@ -1791,7 +1705,7 @@ PARSER(parse_assignment) { for (;;) { ast_t *rhs = optional(ctx, &pos, parse_extended_expr); if (!rhs) break; - values = new(ast_list_t, .ast=rhs, .next=values); + values = new (ast_list_t, .ast = rhs, .next = values); spaces(&pos); if (!match(&pos, ",")) break; whitespace(&pos); @@ -1800,30 +1714,23 @@ PARSER(parse_assignment) { REVERSE_LIST(targets); REVERSE_LIST(values); - return NewAST(ctx->file, start, pos, Assign, .targets=targets, .values=values); + return NewAST(ctx->file, start, pos, Assign, .targets = targets, .values = values); } PARSER(parse_statement) { ast_t *stmt = NULL; - if ((stmt=parse_declaration(ctx, pos)) - || (stmt=parse_doctest(ctx, pos)) - || (stmt=parse_assert(ctx, pos))) + if ((stmt = parse_declaration(ctx, pos)) || (stmt = parse_doctest(ctx, pos)) || (stmt = parse_assert(ctx, pos))) return stmt; - if (!(false - || (stmt=parse_update(ctx, pos)) - || (stmt=parse_assignment(ctx, pos)) - )) + if (!(false || (stmt = parse_update(ctx, pos)) || (stmt = parse_assignment(ctx, pos)))) stmt = parse_extended_expr(ctx, pos); - - for (bool progress = (stmt != NULL); progress; ) { + + for (bool progress = (stmt != NULL); progress;) { ast_t *new_stmt; progress = false; if (stmt->tag == Var) { - progress = (false - || (new_stmt=parse_method_call_suffix(ctx, stmt)) - || (new_stmt=parse_fncall_suffix(ctx, stmt)) - ); + progress = (false || (new_stmt = parse_method_call_suffix(ctx, stmt)) + || (new_stmt = parse_fncall_suffix(ctx, stmt))); } else if (stmt->tag == FunctionCall) { new_stmt = parse_optional_conditional_suffix(ctx, stmt); progress = (new_stmt != stmt); @@ -1832,20 +1739,14 @@ PARSER(parse_statement) { if (progress) stmt = new_stmt; } return stmt; - } PARSER(parse_extended_expr) { ast_t *expr = NULL; - if (false - || (expr=optional(ctx, &pos, parse_for)) - || (expr=optional(ctx, &pos, parse_while)) - || (expr=optional(ctx, &pos, parse_if)) - || (expr=optional(ctx, &pos, parse_when)) - || (expr=optional(ctx, &pos, parse_repeat)) - || (expr=optional(ctx, &pos, parse_do)) - ) + if (false || (expr = optional(ctx, &pos, parse_for)) || (expr = optional(ctx, &pos, parse_while)) + || (expr = optional(ctx, &pos, parse_if)) || (expr = optional(ctx, &pos, parse_when)) + || (expr = optional(ctx, &pos, parse_repeat)) || (expr = optional(ctx, &pos, parse_do))) return expr; return parse_expr(ctx, pos); @@ -1863,7 +1764,7 @@ PARSER(parse_block) { spaces(&pos); ast_t *stmt = optional(ctx, &pos, parse_statement); if (!stmt) break; - statements = new(ast_list_t, .ast=stmt, .next=statements); + statements = new (ast_list_t, .ast = stmt, .next = statements); spaces(&pos); if (!match(&pos, ";")) break; } @@ -1872,7 +1773,7 @@ PARSER(parse_block) { } if (indent(ctx, &pos)) { - indented:; + indented:; int64_t block_indent = get_indent(ctx, pos); whitespace(&pos); while (*pos) { @@ -1889,17 +1790,15 @@ PARSER(parse_block) { parser_err(ctx, line_start, eol(pos), "'use' statements are only allowed at the top level"); spaces(&pos); - if (*pos && *pos != '\r' && *pos != '\n') - parser_err(ctx, pos, eol(pos), "I couldn't parse this line"); + if (*pos && *pos != '\r' && *pos != '\n') parser_err(ctx, pos, eol(pos), "I couldn't parse this line"); break; } - statements = new(ast_list_t, .ast=stmt, .next=statements); + statements = new (ast_list_t, .ast = stmt, .next = statements); whitespace(&pos); // Guard against having two valid statements on the same line, separated by spaces (but no newlines): if (!memchr(stmt->end, '\n', (size_t)(pos - stmt->end))) { - if (*pos) - parser_err(ctx, pos, eol(pos), "I don't know how to parse the rest of this line"); + if (*pos) parser_err(ctx, pos, eol(pos), "I don't know how to parse the rest of this line"); pos = stmt->end; break; } @@ -1911,7 +1810,7 @@ PARSER(parse_block) { } } REVERSE_LIST(statements); - return NewAST(ctx->file, start, pos, Block, .statements=statements); + return NewAST(ctx->file, start, pos, Block, .statements = statements); } PARSER(parse_namespace) { @@ -1924,18 +1823,12 @@ PARSER(parse_namespace) { whitespace(&next); if (get_indent(ctx, next) != indent) break; ast_t *stmt; - if ((stmt=optional(ctx, &pos, parse_struct_def)) - ||(stmt=optional(ctx, &pos, parse_func_def)) - ||(stmt=optional(ctx, &pos, parse_enum_def)) - ||(stmt=optional(ctx, &pos, parse_lang_def)) - ||(stmt=optional(ctx, &pos, parse_extend)) - ||(stmt=optional(ctx, &pos, parse_convert_def)) - ||(stmt=optional(ctx, &pos, parse_use)) - ||(stmt=optional(ctx, &pos, parse_extern)) - ||(stmt=optional(ctx, &pos, parse_inline_c)) - ||(stmt=optional(ctx, &pos, parse_declaration))) - { - statements = new(ast_list_t, .ast=stmt, .next=statements); + if ((stmt = optional(ctx, &pos, parse_struct_def)) || (stmt = optional(ctx, &pos, parse_func_def)) + || (stmt = optional(ctx, &pos, parse_enum_def)) || (stmt = optional(ctx, &pos, parse_lang_def)) + || (stmt = optional(ctx, &pos, parse_extend)) || (stmt = optional(ctx, &pos, parse_convert_def)) + || (stmt = optional(ctx, &pos, parse_use)) || (stmt = optional(ctx, &pos, parse_extern)) + || (stmt = optional(ctx, &pos, parse_inline_c)) || (stmt = optional(ctx, &pos, parse_declaration))) { + statements = new (ast_list_t, .ast = stmt, .next = statements); pos = stmt->end; whitespace(&pos); // TODO: check for newline // if (!(space_types & WHITESPACE_NEWLINES)) { @@ -1949,7 +1842,7 @@ PARSER(parse_namespace) { } } REVERSE_LIST(statements); - return NewAST(ctx->file, start, pos, Block, .statements=statements); + return NewAST(ctx->file, start, pos, Block, .statements = statements); } PARSER(parse_file_body) { @@ -1961,18 +1854,12 @@ PARSER(parse_file_body) { whitespace(&next); if (get_indent(ctx, next) != 0) break; ast_t *stmt; - if ((stmt=optional(ctx, &pos, parse_struct_def)) - ||(stmt=optional(ctx, &pos, parse_func_def)) - ||(stmt=optional(ctx, &pos, parse_enum_def)) - ||(stmt=optional(ctx, &pos, parse_lang_def)) - ||(stmt=optional(ctx, &pos, parse_extend)) - ||(stmt=optional(ctx, &pos, parse_convert_def)) - ||(stmt=optional(ctx, &pos, parse_use)) - ||(stmt=optional(ctx, &pos, parse_extern)) - ||(stmt=optional(ctx, &pos, parse_inline_c)) - ||(stmt=optional(ctx, &pos, parse_top_declaration))) - { - statements = new(ast_list_t, .ast=stmt, .next=statements); + if ((stmt = optional(ctx, &pos, parse_struct_def)) || (stmt = optional(ctx, &pos, parse_func_def)) + || (stmt = optional(ctx, &pos, parse_enum_def)) || (stmt = optional(ctx, &pos, parse_lang_def)) + || (stmt = optional(ctx, &pos, parse_extend)) || (stmt = optional(ctx, &pos, parse_convert_def)) + || (stmt = optional(ctx, &pos, parse_use)) || (stmt = optional(ctx, &pos, parse_extern)) + || (stmt = optional(ctx, &pos, parse_inline_c)) || (stmt = optional(ctx, &pos, parse_top_declaration))) { + statements = new (ast_list_t, .ast = stmt, .next = statements); pos = stmt->end; whitespace(&pos); // TODO: check for newline } else { @@ -1984,7 +1871,7 @@ PARSER(parse_file_body) { parser_err(ctx, pos, eol(pos), "I expect all top-level statements to be declarations of some kind"); } REVERSE_LIST(statements); - return NewAST(ctx->file, start, pos, Block, .statements=statements); + return NewAST(ctx->file, start, pos, Block, .statements = statements); } PARSER(parse_struct_def) { @@ -1999,8 +1886,7 @@ PARSER(parse_struct_def) { if (!name) parser_err(ctx, start, pos, "I expected a name for this struct"); spaces(&pos); - if (!match(&pos, "(")) - parser_err(ctx, pos, pos, "I expected a '(' and a list of fields here"); + if (!match(&pos, "(")) parser_err(ctx, pos, pos, "I expected a '(' and a list of fields here"); arg_ast_t *fields = parse_args(ctx, &pos); @@ -2015,18 +1901,16 @@ PARSER(parse_struct_def) { external = true; } else if (match_word(&pos, "opaque")) { if (fields) - parser_err(ctx, pos-strlen("opaque"), pos, "A struct can't be opaque if it has fields defined"); + parser_err(ctx, pos - strlen("opaque"), pos, "A struct can't be opaque if it has fields defined"); opaque = true; } else { break; } - if (!match_separator(&pos)) - break; + if (!match_separator(&pos)) break; } } - expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this struct"); ast_t *namespace = NULL; @@ -2037,10 +1921,9 @@ PARSER(parse_struct_def) { pos = ns_pos; namespace = optional(ctx, &pos, parse_namespace); } - if (!namespace) - namespace = NewAST(ctx->file, pos, pos, Block, .statements=NULL); - return NewAST(ctx->file, start, pos, StructDef, .name=name, .fields=fields, .namespace=namespace, - .secret=secret, .external=external, .opaque=opaque); + if (!namespace) namespace = NewAST(ctx->file, pos, pos, Block, .statements = NULL); + return NewAST(ctx->file, start, pos, StructDef, .name = name, .fields = fields, .namespace = namespace, + .secret = secret, .external = external, .opaque = opaque); } PARSER(parse_enum_def) { @@ -2050,8 +1933,7 @@ PARSER(parse_enum_def) { int64_t starting_indent = get_indent(ctx, pos); spaces(&pos); const char *name = get_id(&pos); - if (!name) - parser_err(ctx, start, pos, "I expected a name for this enum"); + if (!name) parser_err(ctx, start, pos, "I expected a name for this enum"); spaces(&pos); if (!match(&pos, "(")) return NULL; @@ -2079,10 +1961,9 @@ PARSER(parse_enum_def) { fields = NULL; } - tags = new(tag_ast_t, .name=tag_name, .fields=fields, .secret=secret, .next=tags); + tags = new (tag_ast_t, .name = tag_name, .fields = fields, .secret = secret, .next = tags); - if (!match_separator(&pos)) - break; + if (!match_separator(&pos)) break; } whitespace(&pos); @@ -2090,8 +1971,7 @@ PARSER(parse_enum_def) { REVERSE_LIST(tags); - if (tags == NULL) - parser_err(ctx, start, pos, "This enum does not have any tags!"); + if (tags == NULL) parser_err(ctx, start, pos, "This enum does not have any tags!"); ast_t *namespace = NULL; const char *ns_pos = pos; @@ -2101,10 +1981,9 @@ PARSER(parse_enum_def) { pos = ns_pos; namespace = optional(ctx, &pos, parse_namespace); } - if (!namespace) - namespace = NewAST(ctx->file, pos, pos, Block, .statements=NULL); + if (!namespace) namespace = NewAST(ctx->file, pos, pos, Block, .statements = NULL); - return NewAST(ctx->file, start, pos, EnumDef, .name=name, .tags=tags, .namespace=namespace); + return NewAST(ctx->file, start, pos, EnumDef, .name = name, .tags = tags, .namespace = namespace); } PARSER(parse_lang_def) { @@ -2114,8 +1993,7 @@ PARSER(parse_lang_def) { int64_t starting_indent = get_indent(ctx, pos); spaces(&pos); const char *name = get_id(&pos); - if (!name) - parser_err(ctx, start, pos, "I expected a name for this lang"); + if (!name) parser_err(ctx, start, pos, "I expected a name for this lang"); spaces(&pos); ast_t *namespace = NULL; @@ -2126,10 +2004,9 @@ PARSER(parse_lang_def) { pos = ns_pos; namespace = optional(ctx, &pos, parse_namespace); } - if (!namespace) - namespace = NewAST(ctx->file, pos, pos, Block, .statements=NULL); + if (!namespace) namespace = NewAST(ctx->file, pos, pos, Block, .statements = NULL); - return NewAST(ctx->file, start, pos, LangDef, .name=name, .namespace=namespace); + return NewAST(ctx->file, start, pos, LangDef, .name = name, .namespace = namespace); } PARSER(parse_extend) { @@ -2139,8 +2016,7 @@ PARSER(parse_extend) { int64_t starting_indent = get_indent(ctx, pos); spaces(&pos); const char *name = get_id(&pos); - if (!name) - parser_err(ctx, start, pos, "I expected a name for this lang"); + if (!name) parser_err(ctx, start, pos, "I expected a name for this lang"); ast_t *body = NULL; const char *ns_pos = pos; @@ -2150,14 +2026,12 @@ PARSER(parse_extend) { pos = ns_pos; body = optional(ctx, &pos, parse_namespace); } - if (!body) - body = NewAST(ctx->file, pos, pos, Block, .statements=NULL); + if (!body) body = NewAST(ctx->file, pos, pos, Block, .statements = NULL); - return NewAST(ctx->file, start, pos, Extend, .name=name, .body=body); + return NewAST(ctx->file, start, pos, Extend, .name = name, .body = body); } -arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos) -{ +arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos) { arg_ast_t *args = NULL; for (;;) { const char *batch_start = *pos; @@ -2177,18 +2051,18 @@ arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos) whitespace(pos); if (match(pos, ":")) { - type = expect(ctx, *pos-1, pos, parse_type, "I expected a type here"); - names = new(name_list_t, .name=name, .next=names); + type = expect(ctx, *pos - 1, pos, parse_type, "I expected a type here"); + names = new (name_list_t, .name = name, .next = names); whitespace(pos); if (match(pos, "=")) - default_val = expect(ctx, *pos-1, pos, parse_term, "I expected a value after this '='"); + default_val = expect(ctx, *pos - 1, pos, parse_term, "I expected a value after this '='"); break; } else if (strncmp(*pos, "==", 2) != 0 && match(pos, "=")) { - default_val = expect(ctx, *pos-1, pos, parse_term, "I expected a value after this '='"); - names = new(name_list_t, .name=name, .next=names); + default_val = expect(ctx, *pos - 1, pos, parse_term, "I expected a value after this '='"); + names = new (name_list_t, .name = name, .next = names); break; } else if (name) { - names = new(name_list_t, .name=name, .next=names); + names = new (name_list_t, .name = name, .next = names); spaces(pos); if (!match(pos, ",")) break; } else { @@ -2197,14 +2071,15 @@ arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos) } if (!names) break; if (!default_val && !type) - parser_err(ctx, batch_start, *pos, "I expected a ':' and type, or '=' and a default value after this parameter (", names->name, ")"); + parser_err(ctx, batch_start, *pos, + "I expected a ':' and type, or '=' and a default value after this parameter (", names->name, + ")"); REVERSE_LIST(names); for (; names; names = names->next) - args = new(arg_ast_t, .name=names->name, .type=type, .value=default_val, .next=args); + args = new (arg_ast_t, .name = names->name, .type = type, .value = default_val, .next = args); - if (!match_separator(pos)) - break; + if (!match_separator(pos)) break; } REVERSE_LIST(args); @@ -2233,22 +2108,19 @@ PARSER(parse_func_def) { if (match_word(&pos, "inline")) { is_inline = true; } else if (match_word(&pos, "cached")) { - if (!cache_ast) cache_ast = NewAST(ctx->file, pos, pos, Int, .str="-1"); + if (!cache_ast) cache_ast = NewAST(ctx->file, pos, pos, Int, .str = "-1"); } else if (match_word(&pos, "cache_size")) { whitespace(&pos); - if (!match(&pos, "=")) - parser_err(ctx, flag_start, pos, "I expected a value for 'cache_size'"); + if (!match(&pos, "=")) parser_err(ctx, flag_start, pos, "I expected a value for 'cache_size'"); whitespace(&pos); cache_ast = expect(ctx, start, &pos, parse_expr, "I expected a maximum size for the cache"); } } expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this function definition"); - ast_t *body = expect(ctx, start, &pos, parse_block, - "This function needs a body block"); - return NewAST(ctx->file, start, pos, FunctionDef, - .name=name, .args=args, .ret_type=ret_type, .body=body, .cache=cache_ast, - .is_inline=is_inline); + ast_t *body = expect(ctx, start, &pos, parse_block, "This function needs a body block"); + return NewAST(ctx->file, start, pos, FunctionDef, .name = name, .args = args, .ret_type = ret_type, .body = body, + .cache = cache_ast, .is_inline = is_inline); } PARSER(parse_convert_def) { @@ -2270,33 +2142,30 @@ PARSER(parse_convert_def) { if (match_word(&pos, "inline")) { is_inline = true; } else if (match_word(&pos, "cached")) { - if (!cache_ast) cache_ast = NewAST(ctx->file, pos, pos, Int, .str="-1"); + if (!cache_ast) cache_ast = NewAST(ctx->file, pos, pos, Int, .str = "-1"); } else if (match_word(&pos, "cache_size")) { whitespace(&pos); - if (!match(&pos, "=")) - parser_err(ctx, flag_start, pos, "I expected a value for 'cache_size'"); + if (!match(&pos, "=")) parser_err(ctx, flag_start, pos, "I expected a value for 'cache_size'"); whitespace(&pos); cache_ast = expect(ctx, start, &pos, parse_expr, "I expected a maximum size for the cache"); } } expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this function definition"); - ast_t *body = expect(ctx, start, &pos, parse_block, - "This function needs a body block"); - return NewAST(ctx->file, start, pos, ConvertDef, - .args=args, .ret_type=ret_type, .body=body, .cache=cache_ast, .is_inline=is_inline); + ast_t *body = expect(ctx, start, &pos, parse_block, "This function needs a body block"); + return NewAST(ctx->file, start, pos, ConvertDef, .args = args, .ret_type = ret_type, .body = body, + .cache = cache_ast, .is_inline = is_inline); } PARSER(parse_extern) { const char *start = pos; if (!match_word(&pos, "extern")) return NULL; spaces(&pos); - const char* name = get_id(&pos); + const char *name = get_id(&pos); spaces(&pos); - if (!match(&pos, ":")) - parser_err(ctx, start, pos, "I couldn't get a type for this extern"); + if (!match(&pos, ":")) parser_err(ctx, start, pos, "I couldn't get a type for this extern"); type_ast_t *type = expect(ctx, start, &pos, parse_type, "I couldn't parse the type for this extern"); - return NewAST(ctx->file, start, pos, Extern, .name=name, .type=type); + return NewAST(ctx->file, start, pos, Extern, .name = name, .type = type); } PARSER(parse_inline_c) { @@ -2309,22 +2178,20 @@ PARSER(parse_inline_c) { if (match(&pos, ":")) { type = expect(ctx, start, &pos, parse_type, "I couldn't parse the type for this C_code code"); spaces(&pos); - if (!match(&pos, "(")) - parser_err(ctx, start, pos, "I expected a '(' here"); - chunks = new(ast_list_t, .ast=NewAST(ctx->file, pos, pos, TextLiteral, Text("({")), - .next=_parse_text_helper(ctx, &pos, '(', ')', '@', false)); + if (!match(&pos, "(")) parser_err(ctx, start, pos, "I expected a '(' here"); + chunks = new (ast_list_t, .ast = NewAST(ctx->file, pos, pos, TextLiteral, Text("({")), + .next = _parse_text_helper(ctx, &pos, '(', ')', '@', false)); if (type) { REVERSE_LIST(chunks); - chunks = new(ast_list_t, .ast=NewAST(ctx->file, pos, pos, TextLiteral, Text("; })")), .next=chunks); + chunks = new (ast_list_t, .ast = NewAST(ctx->file, pos, pos, TextLiteral, Text("; })")), .next = chunks); REVERSE_LIST(chunks); } } else { - if (!match(&pos, "{")) - parser_err(ctx, start, pos, "I expected a '{' here"); + if (!match(&pos, "{")) parser_err(ctx, start, pos, "I expected a '{' here"); chunks = _parse_text_helper(ctx, &pos, '{', '}', '@', false); } - return NewAST(ctx->file, start, pos, InlineCCode, .chunks=chunks, .type_ast=type); + return NewAST(ctx->file, start, pos, InlineCCode, .chunks = chunks, .type_ast = type); } PARSER(parse_doctest) { @@ -2340,7 +2207,7 @@ PARSER(parse_doctest) { } else { pos = expr->end; } - return NewAST(ctx->file, start, pos, DocTest, .expr=expr, .expected=expected); + return NewAST(ctx->file, start, pos, DocTest, .expr = expr, .expected = expected); } PARSER(parse_assert) { @@ -2356,7 +2223,7 @@ PARSER(parse_assert) { } else { pos = expr->end; } - return NewAST(ctx->file, start, pos, Assert, .expr=expr, .message=message); + return NewAST(ctx->file, start, pos, Assert, .expr = expr, .message = message); } PARSER(parse_use) { @@ -2373,12 +2240,12 @@ PARSER(parse_use) { if (!match_word(&pos, "use")) return NULL; spaces(&pos); size_t name_len = strcspn(pos, " \t\r\n;"); - if (name_len < 1) - parser_err(ctx, start, pos, "There is no module name here to use"); + if (name_len < 1) parser_err(ctx, start, pos, "There is no module name here to use"); char *name = GC_strndup(pos, name_len); pos += name_len; - while (match(&pos, ";")) continue; - int what; + while (match(&pos, ";")) + continue; + int what; if (name[0] == '<' || ends_with(name, ".h")) { what = USE_HEADER; } else if (starts_with(name, "-l")) { @@ -2387,17 +2254,17 @@ PARSER(parse_use) { what = USE_C_CODE; } else if (ends_with(name, ".S") || ends_with(name, ".s")) { what = USE_ASM; - } else if (starts_with(name, "./") || starts_with(name, "/") || starts_with(name, "../") || starts_with(name, "~/")) { + } else if (starts_with(name, "./") || starts_with(name, "/") || starts_with(name, "../") + || starts_with(name, "~/")) { what = USE_LOCAL; } else { what = USE_MODULE; } - return NewAST(ctx->file, start, pos, Use, .var=var, .path=name, .what=what); + return NewAST(ctx->file, start, pos, Use, .var = var, .path = name, .what = what); } ast_t *parse_file(const char *path, jmp_buf *on_err) { - if (path[0] != '<' && path[0] != '/') - fail("Path is not fully resolved: ", path); + if (path[0] != '<' && path[0] != '/') fail("Path is not fully resolved: ", path); // NOTE: this cache leaks a bounded amount of memory. The cache will never // hold more than PARSE_CACHE_SIZE entries (see below), but each entry's // AST holds onto a reference to the file it came from, so they could @@ -2417,8 +2284,8 @@ ast_t *parse_file(const char *path, jmp_buf *on_err) { } parse_ctx_t ctx = { - .file=file, - .on_err=on_err, + .file = file, + .on_err = on_err, }; const char *pos = file->text; @@ -2437,7 +2304,10 @@ ast_t *parse_file(const char *path, jmp_buf *on_err) { if (cached.entries.length > PARSE_CACHE_SIZE) { // FIXME: this currently evicts the first entry, but it should be more like // an LRU cache - struct {const char *path; ast_t *ast; } *to_remove = Table$entry(cached, 1); + struct { + const char *path; + ast_t *ast; + } *to_remove = Table$entry(cached, 1); Table$str_remove(&cached, to_remove->path); } @@ -2449,8 +2319,8 @@ ast_t *parse_file(const char *path, jmp_buf *on_err) { type_ast_t *parse_type_str(const char *str) { file_t *file = spoof_file("<type>", str); parse_ctx_t ctx = { - .file=file, - .on_err=NULL, + .file = file, + .on_err = NULL, }; const char *pos = file->text; @@ -2468,8 +2338,8 @@ type_ast_t *parse_type_str(const char *str) { ast_t *parse(const char *str) { file_t *file = spoof_file("<string>", str); parse_ctx_t ctx = { - .file=file, - .on_err=NULL, + .file = file, + .on_err = NULL, }; const char *pos = file->text; @@ -2485,8 +2355,8 @@ ast_t *parse(const char *str) { ast_t *parse_expression(const char *str) { file_t *file = spoof_file("<string>", str); parse_ctx_t ctx = { - .file=file, - .on_err=NULL, + .file = file, + .on_err = NULL, }; const char *pos = file->text; diff --git a/src/stdlib/bools.c b/src/stdlib/bools.c index 76de49e0..0231e21b 100644 --- a/src/stdlib/bools.c +++ b/src/stdlib/bools.c @@ -11,18 +11,14 @@ #include "text.h" #include "util.h" -PUREFUNC public Text_t Bool$as_text(const void *b, bool colorize, const TypeInfo_t *info) -{ +PUREFUNC public Text_t Bool$as_text(const void *b, bool colorize, const TypeInfo_t *info) { (void)info; if (!b) return Text("Bool"); - if (colorize) - return *(Bool_t*)b ? Text("\x1b[35myes\x1b[m") : Text("\x1b[35mno\x1b[m"); - else - return *(Bool_t*)b ? Text("yes") : Text("no"); + if (colorize) return *(Bool_t *)b ? Text("\x1b[35myes\x1b[m") : Text("\x1b[35mno\x1b[m"); + else return *(Bool_t *)b ? Text("yes") : Text("no"); } -static bool try_parse(Text_t text, Text_t target, bool target_value, Text_t *remainder, bool *result) -{ +static bool try_parse(Text_t text, Text_t target, bool target_value, Text_t *remainder, bool *result) { static const Text_t lang = Text("C"); if (text.length < target.length) return false; Text_t prefix = Text$to(text, Int$from_int64(target.length)); @@ -36,35 +32,33 @@ static bool try_parse(Text_t text, Text_t target, bool target_value, Text_t *rem } } -PUREFUNC public OptionalBool_t Bool$parse(Text_t text, Text_t *remainder) -{ +PUREFUNC public OptionalBool_t Bool$parse(Text_t text, Text_t *remainder) { bool result; if (try_parse(text, Text("yes"), true, remainder, &result) || try_parse(text, Text("true"), true, remainder, &result) - || try_parse(text, Text("on"), true, remainder, &result) - || try_parse(text, Text("1"), true, remainder, &result) + || try_parse(text, Text("on"), true, remainder, &result) || try_parse(text, Text("1"), true, remainder, &result) || try_parse(text, Text("no"), false, remainder, &result) || try_parse(text, Text("false"), false, remainder, &result) || try_parse(text, Text("off"), false, remainder, &result) || try_parse(text, Text("0"), false, remainder, &result)) return result; - else - return NONE_BOOL; + else return NONE_BOOL; } -static bool Bool$is_none(const void *b, const TypeInfo_t *info) -{ +static bool Bool$is_none(const void *b, const TypeInfo_t *info) { (void)info; - return *(OptionalBool_t*)b == NONE_BOOL; + return *(OptionalBool_t *)b == NONE_BOOL; } -public const TypeInfo_t Bool$info = { - .size=sizeof(bool), - .align=__alignof__(bool), - .metamethods={ - .as_text=Bool$as_text, - .is_none=Bool$is_none, - }, +public +const TypeInfo_t Bool$info = { + .size = sizeof(bool), + .align = __alignof__(bool), + .metamethods = + { + .as_text = Bool$as_text, + .is_none = Bool$is_none, + }, }; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/bools.h b/src/stdlib/bools.h index bdf80879..3eac0f0d 100644 --- a/src/stdlib/bools.h +++ b/src/stdlib/bools.h @@ -8,8 +8,8 @@ #include "types.h" #include "util.h" -#define yes (Bool_t)true -#define no (Bool_t)false +#define yes (Bool_t) true +#define no (Bool_t) false PUREFUNC Text_t Bool$as_text(const void *b, bool colorize, const TypeInfo_t *type); OptionalBool_t Bool$parse(Text_t text, Text_t *remainder); diff --git a/src/stdlib/bytes.c b/src/stdlib/bytes.c index 5a581e7e..d4bd5ef6 100644 --- a/src/stdlib/bytes.c +++ b/src/stdlib/bytes.c @@ -8,15 +8,18 @@ #include "text.h" #include "util.h" -public const Byte_t Byte$min = 0; -public const Byte_t Byte$max = UINT8_MAX; +public +const Byte_t Byte$min = 0; +public +const Byte_t Byte$max = UINT8_MAX; -PUREFUNC public Text_t Byte$as_text(const void *b, bool colorize, const TypeInfo_t *info) -{ +PUREFUNC public Text_t Byte$as_text(const void *b, bool colorize, const TypeInfo_t *info) { (void)info; if (!b) return Text("Byte"); - Byte_t byte = *(Byte_t*)b; - char digits[] = {'0', 'x', + Byte_t byte = *(Byte_t *)b; + char digits[] = { + '0', + 'x', (byte / 16) <= 9 ? '0' + (byte / 16) : 'a' + (byte / 16) - 10, (byte & 15) <= 9 ? '0' + (byte & 15) : 'a' + (byte & 15) - 10, '\0', @@ -26,52 +29,54 @@ PUREFUNC public Text_t Byte$as_text(const void *b, bool colorize, const TypeInfo return text; } -public CONSTFUNC bool Byte$is_between(const Byte_t x, const Byte_t low, const Byte_t high) { - return low <= x && x <= high; -} +public +CONSTFUNC bool Byte$is_between(const Byte_t x, const Byte_t low, const Byte_t high) { return low <= x && x <= high; } -public OptionalByte_t Byte$parse(Text_t text, Text_t *remainder) -{ +public +OptionalByte_t Byte$parse(Text_t text, Text_t *remainder) { OptionalInt_t full_int = Int$parse(text, remainder); - if (full_int.small != 0L - && Int$compare_value(full_int, I(0)) >= 0 - && Int$compare_value(full_int, I(255)) <= 0) { - return (OptionalByte_t){.value=Byte$from_int(full_int, true)}; + if (full_int.small != 0L && Int$compare_value(full_int, I(0)) >= 0 && Int$compare_value(full_int, I(255)) <= 0) { + return (OptionalByte_t){.value = Byte$from_int(full_int, true)}; } else { return NONE_BYTE; } } -public Text_t Byte$hex(Byte_t byte, bool uppercase, bool prefix) { - struct Text_s text = {.tag=TEXT_ASCII}; +public +Text_t Byte$hex(Byte_t byte, bool uppercase, bool prefix) { + struct Text_s text = {.tag = TEXT_ASCII}; text.ascii = GC_MALLOC_ATOMIC(8); - char *p = (char*)text.ascii; + char *p = (char *)text.ascii; if (prefix) { *(p++) = '0'; *(p++) = 'x'; } if (uppercase) { - *(p++) = (byte/16) > 9 ? 'A' + (byte/16) - 10 : '0' + (byte/16); + *(p++) = (byte / 16) > 9 ? 'A' + (byte / 16) - 10 : '0' + (byte / 16); *(p++) = (byte & 15) > 9 ? 'A' + (byte & 15) - 10 : '0' + (byte & 15); } else { - *(p++) = (byte/16) > 9 ? 'a' + (byte/16) - 10 : '0' + (byte/16); + *(p++) = (byte / 16) > 9 ? 'a' + (byte / 16) - 10 : '0' + (byte / 16); *(p++) = (byte & 15) > 9 ? 'a' + (byte & 15) - 10 : '0' + (byte & 15); } text.length = (int64_t)(p - text.ascii); return text; } -public bool Byte$get_bit(Byte_t x, Int_t bit_index) { - if (Int$compare_value(bit_index, I(1)) < 0) - fail("Invalid bit index (expected 1 or higher): ", bit_index); +public +bool Byte$get_bit(Byte_t x, Int_t bit_index) { + if (Int$compare_value(bit_index, I(1)) < 0) fail("Invalid bit index (expected 1 or higher): ", bit_index); if (Int$compare_value(bit_index, I(8)) > 0) fail("Bit index is too large! There are only 8 bits in a byte, but index is: ", bit_index); - return ((x & (Byte_t)(1L << (Int64$from_int(bit_index, true)-1L))) != 0); + return ((x & (Byte_t)(1L << (Int64$from_int(bit_index, true) - 1L))) != 0); } #ifdef __TINYC__ -#define __builtin_add_overflow(x, y, result) ({ *(result) = (x) + (y); false; }) +#define __builtin_add_overflow(x, y, result) \ + ({ \ + *(result) = (x) + (y); \ + false; \ + }) #endif typedef struct { @@ -82,52 +87,59 @@ typedef struct { static OptionalByte_t _next_Byte(ByteRange_t *info) { OptionalByte_t i = info->current; if (!i.is_none) { - Byte_t next; bool overflow = __builtin_add_overflow(i.value, info->step, &next); + Byte_t next; + bool overflow = __builtin_add_overflow(i.value, info->step, &next); if (overflow || (!info->last.is_none && (info->step >= 0 ? next > info->last.value : next < info->last.value))) - info->current = (OptionalByte_t){.is_none=true}; - else - info->current = (OptionalByte_t){.value=next}; + info->current = (OptionalByte_t){.is_none = true}; + else info->current = (OptionalByte_t){.value = next}; } return i; } -public CONSTFUNC Closure_t Byte$to(Byte_t first, Byte_t last, OptionalInt8_t step) { +public +CONSTFUNC Closure_t Byte$to(Byte_t first, Byte_t last, OptionalInt8_t step) { ByteRange_t *range = GC_MALLOC(sizeof(ByteRange_t)); - range->current = (OptionalByte_t){.value=first}; - range->last = (OptionalByte_t){.value=last}; + range->current = (OptionalByte_t){.value = first}; + range->last = (OptionalByte_t){.value = last}; range->step = step.is_none ? (last >= first ? 1 : -1) : step.value; - return (Closure_t){.fn=_next_Byte, .userdata=range}; + return (Closure_t){.fn = _next_Byte, .userdata = range}; } -public PUREFUNC Byte_t Byte$from_int(Int_t i, bool truncate) { +public +PUREFUNC Byte_t Byte$from_int(Int_t i, bool truncate) { if unlikely (!truncate && Int$compare_value(i, I_small(0xFF)) > 0) fail("This value is too large to convert to a byte without truncation: ", i); else if unlikely (!truncate && Int$compare_value(i, I_small(0)) < 0) fail("Negative values can't be converted to bytes: ", i); return (Byte_t)(i.small >> 2); } -public PUREFUNC Byte_t Byte$from_int64(Int64_t i, bool truncate) { +public +PUREFUNC Byte_t Byte$from_int64(Int64_t i, bool truncate) { if unlikely (!truncate && i != (Int64_t)(Byte_t)i) fail("This value can't be converted to a byte without truncation: ", i); return (Byte_t)i; } -public PUREFUNC Byte_t Byte$from_int32(Int32_t i, bool truncate) { +public +PUREFUNC Byte_t Byte$from_int32(Int32_t i, bool truncate) { if unlikely (!truncate && i != (Int32_t)(Byte_t)i) fail("This value can't be converted to a byte without truncation: ", i); return (Byte_t)i; } -public PUREFUNC Byte_t Byte$from_int16(Int16_t i, bool truncate) { +public +PUREFUNC Byte_t Byte$from_int16(Int16_t i, bool truncate) { if unlikely (!truncate && i != (Int16_t)(Byte_t)i) fail("This value can't be converted to a byte without truncation: ", i); return (Byte_t)i; } -public const TypeInfo_t Byte$info = { - .size=sizeof(Byte_t), - .align=__alignof__(Byte_t), - .metamethods={ - .as_text=Byte$as_text, - }, +public +const TypeInfo_t Byte$info = { + .size = sizeof(Byte_t), + .align = __alignof__(Byte_t), + .metamethods = + { + .as_text = Byte$as_text, + }, }; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/c_strings.c b/src/stdlib/c_strings.c index c2b8efbe..b965f04b 100644 --- a/src/stdlib/c_strings.c +++ b/src/stdlib/c_strings.c @@ -12,46 +12,40 @@ #include "text.h" #include "util.h" -public Text_t CString$as_text(const void *c_string, bool colorize, const TypeInfo_t *info) -{ +public +Text_t CString$as_text(const void *c_string, bool colorize, const TypeInfo_t *info) { (void)info; if (!c_string) return Text("CString"); - Text_t text = Text$from_str(*(const char**)c_string); - return Text$concat(colorize ? Text("\x1b[34mCString\x1b[m(") : Text("CString("), Text$quoted(text, colorize, Text("\"")), Text(")")); + Text_t text = Text$from_str(*(const char **)c_string); + return Text$concat(colorize ? Text("\x1b[34mCString\x1b[m(") : Text("CString("), + Text$quoted(text, colorize, Text("\"")), Text(")")); } -PUREFUNC public int32_t CString$compare(const void *x, const void *y, const TypeInfo_t *info) -{ +PUREFUNC public int32_t CString$compare(const void *x, const void *y, const TypeInfo_t *info) { (void)info; - if (x == y) - return 0; + if (x == y) return 0; - if (!*(const char**)x != !*(const char**)y) - return (!*(const char**)y) - (!*(const char**)x); + if (!*(const char **)x != !*(const char **)y) return (!*(const char **)y) - (!*(const char **)x); - return strcmp(*(const char**)x, *(const char**)y); + return strcmp(*(const char **)x, *(const char **)y); } -PUREFUNC public bool CString$equal(const void *x, const void *y, const TypeInfo_t *type) -{ +PUREFUNC public bool CString$equal(const void *x, const void *y, const TypeInfo_t *type) { return CString$compare(x, y, type) == 0; } -PUREFUNC public uint64_t CString$hash(const void *c_str, const TypeInfo_t *info) -{ +PUREFUNC public uint64_t CString$hash(const void *c_str, const TypeInfo_t *info) { (void)info; - if (!*(const char**)c_str) return 0; - return siphash24(*(void**)c_str, strlen(*(const char**)c_str)); + if (!*(const char **)c_str) return 0; + return siphash24(*(void **)c_str, strlen(*(const char **)c_str)); } -PUREFUNC public bool CString$is_none(const void *c_str, const TypeInfo_t *info) -{ +PUREFUNC public bool CString$is_none(const void *c_str, const TypeInfo_t *info) { (void)info; - return *(const char**)c_str == NULL; + return *(const char **)c_str == NULL; } -static void CString$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) -{ +static void CString$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) { (void)info; const char *str = *(const char **)obj; int64_t len = (int64_t)strlen(str); @@ -59,30 +53,30 @@ static void CString$serialize(const void *obj, FILE *out, Table_t *pointers, con fwrite(str, sizeof(char), (size_t)len, out); } -static void CString$deserialize(FILE *in, void *out, List_t *pointers, const TypeInfo_t *info) -{ +static void CString$deserialize(FILE *in, void *out, List_t *pointers, const TypeInfo_t *info) { (void)info; int64_t len = -1; Int64$deserialize(in, &len, pointers, &Int64$info); - char *str = GC_MALLOC_ATOMIC((size_t)len+1); - if (fread(str, sizeof(char), (size_t)len, in) != (size_t)len) - fail("Not enough data in stream to deserialize"); - str[len+1] = '\0'; - *(const char**)out = str; + char *str = GC_MALLOC_ATOMIC((size_t)len + 1); + if (fread(str, sizeof(char), (size_t)len, in) != (size_t)len) fail("Not enough data in stream to deserialize"); + str[len + 1] = '\0'; + *(const char **)out = str; } -public const TypeInfo_t CString$info = { - .size=sizeof(const char*), - .align=__alignof__(const char*), - .metamethods={ - .hash=CString$hash, - .compare=CString$compare, - .equal=CString$equal, - .as_text=CString$as_text, - .is_none=CString$is_none, - .serialize=CString$serialize, - .deserialize=CString$deserialize, - }, +public +const TypeInfo_t CString$info = { + .size = sizeof(const char *), + .align = __alignof__(const char *), + .metamethods = + { + .hash = CString$hash, + .compare = CString$compare, + .equal = CString$equal, + .as_text = CString$as_text, + .is_none = CString$is_none, + .serialize = CString$serialize, + .deserialize = CString$deserialize, + }, }; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/datatypes.h b/src/stdlib/datatypes.h index e473956e..5d016246 100644 --- a/src/stdlib/datatypes.h +++ b/src/stdlib/datatypes.h @@ -12,9 +12,9 @@ #define LIST_REFCOUNT_BITS 3 #define LIST_STRIDE_BITS 12 -#define MAX_FOR_N_BITS(N) ((1L<<(N))-1L) -#define LIST_MAX_STRIDE MAX_FOR_N_BITS(LIST_STRIDE_BITS-1) -#define LIST_MIN_STRIDE (~MAX_FOR_N_BITS(LIST_STRIDE_BITS-1)) +#define MAX_FOR_N_BITS(N) ((1L << (N)) - 1L) +#define LIST_MAX_STRIDE MAX_FOR_N_BITS(LIST_STRIDE_BITS - 1) +#define LIST_MIN_STRIDE (~MAX_FOR_N_BITS(LIST_STRIDE_BITS - 1)) #define LIST_MAX_DATA_REFCOUNT MAX_FOR_N_BITS(LIST_REFCOUNT_BITS) #define LIST_MAX_FREE_ENTRIES MAX_FOR_N_BITS(LIST_FREE_BITS) @@ -41,15 +41,15 @@ typedef struct { // structs can be passed in two 64-bit registers. C will handle doing the // bit arithmetic to extract the necessary values, which is cheaper than // spilling onto the stack and needing to retrieve data from the stack. - int64_t length:LIST_LENGTH_BITS; - uint64_t free:LIST_FREE_BITS; - bool atomic:LIST_ATOMIC_BITS; - uint8_t data_refcount:LIST_REFCOUNT_BITS; - int16_t stride:LIST_STRIDE_BITS; + int64_t length : LIST_LENGTH_BITS; + uint64_t free : LIST_FREE_BITS; + bool atomic : LIST_ATOMIC_BITS; + uint8_t data_refcount : LIST_REFCOUNT_BITS; + int16_t stride : LIST_STRIDE_BITS; } List_t; typedef struct { - uint32_t occupied:1, index:31; + uint32_t occupied : 1, index : 31; uint32_t next_bucket; } bucket_t; @@ -57,8 +57,8 @@ typedef struct { #define TABLE_MAX_DATA_REFCOUNT 3 typedef struct { - uint32_t count:31, last_free:31; - uint8_t data_refcount:2; + uint32_t count : 31, last_free : 31; + uint8_t data_refcount : 2; bucket_t buckets[]; } bucket_info_t; @@ -76,9 +76,9 @@ typedef struct { enum text_type { TEXT_ASCII, TEXT_GRAPHEMES, TEXT_CONCAT, TEXT_BLOB }; typedef struct Text_s { - int64_t length:54; // Number of grapheme clusters - uint8_t tag:2; - uint8_t depth:8; + int64_t length : 54; // Number of grapheme clusters + uint8_t tag : 2; + uint8_t depth : 8; union { struct { const char *ascii; @@ -117,10 +117,9 @@ typedef struct { typedef struct { Byte_t value; - bool is_none:1; + bool is_none : 1; } OptionalByte_t; -#define NONE_BYTE ((OptionalByte_t){.is_none=true}) - +#define NONE_BYTE ((OptionalByte_t){.is_none = true}) // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/enums.c b/src/stdlib/enums.c index ae0c976d..7f7c4284 100644 --- a/src/stdlib/enums.c +++ b/src/stdlib/enums.c @@ -11,34 +11,30 @@ CONSTFUNC static ptrdiff_t value_offset(const TypeInfo_t *type) { ptrdiff_t offset = sizeof(int32_t); for (int i = 0; i < type->EnumInfo.num_tags; i++) { - if (type->EnumInfo.tags[i].type) - offset = MAX(offset, type->EnumInfo.tags[i].type->align); + if (type->EnumInfo.tags[i].type) offset = MAX(offset, type->EnumInfo.tags[i].type->align); } return offset; } -PUREFUNC public uint64_t Enum$hash(const void *obj, const TypeInfo_t *type) -{ - int32_t tag = *(int32_t*)obj; +PUREFUNC public uint64_t Enum$hash(const void *obj, const TypeInfo_t *type) { + int32_t tag = *(int32_t *)obj; uint32_t components[2] = {(uint32_t)tag, 0}; - const TypeInfo_t *value = type->EnumInfo.tags[tag-1].type; + const TypeInfo_t *value = type->EnumInfo.tags[tag - 1].type; if (value && value->size > 0) { components[1] = generic_hash(obj + value_offset(type), value); } - return siphash24((void*)components, sizeof(components)); + return siphash24((void *)components, sizeof(components)); } -PUREFUNC public int32_t Enum$compare(const void *x, const void *y, const TypeInfo_t *type) -{ +PUREFUNC public int32_t Enum$compare(const void *x, const void *y, const TypeInfo_t *type) { if (x == y) return 0; - int32_t x_tag = *(int32_t*)x; - int32_t y_tag = *(int32_t*)y; - if (x_tag != y_tag) - return x_tag > y_tag ? 1 : -1; + int32_t x_tag = *(int32_t *)x; + int32_t y_tag = *(int32_t *)y; + if (x_tag != y_tag) return x_tag > y_tag ? 1 : -1; - const TypeInfo_t *value = type->EnumInfo.tags[x_tag-1].type; + const TypeInfo_t *value = type->EnumInfo.tags[x_tag - 1].type; if (value && value->size > 0) { ptrdiff_t byte_offset = value_offset(type); return generic_compare(x + byte_offset, y + byte_offset, value); @@ -46,16 +42,14 @@ PUREFUNC public int32_t Enum$compare(const void *x, const void *y, const TypeInf return 0; } -PUREFUNC public bool Enum$equal(const void *x, const void *y, const TypeInfo_t *type) -{ +PUREFUNC public bool Enum$equal(const void *x, const void *y, const TypeInfo_t *type) { if (x == y) return true; - int32_t x_tag = *(int32_t*)x; - int32_t y_tag = *(int32_t*)y; - if (x_tag != y_tag) - return false; + int32_t x_tag = *(int32_t *)x; + int32_t y_tag = *(int32_t *)y; + if (x_tag != y_tag) return false; - const TypeInfo_t *value = type->EnumInfo.tags[x_tag-1].type; + const TypeInfo_t *value = type->EnumInfo.tags[x_tag - 1].type; if (value && value->size > 0) { ptrdiff_t byte_offset = value_offset(type); return generic_equal(x + byte_offset, y + byte_offset, value); @@ -63,12 +57,12 @@ PUREFUNC public bool Enum$equal(const void *x, const void *y, const TypeInfo_t * return true; } -public Text_t Enum$as_text(const void *obj, bool colorize, const TypeInfo_t *type) -{ +public +Text_t Enum$as_text(const void *obj, bool colorize, const TypeInfo_t *type) { if (!obj) return Text$from_str(type->EnumInfo.name); - int32_t tag = *(int32_t*)obj; - NamedType_t value = type->EnumInfo.tags[tag-1]; + int32_t tag = *(int32_t *)obj; + NamedType_t value = type->EnumInfo.tags[tag - 1]; if (!value.type || value.type->size == 0) { Text_t text = Text$from_str(value.name); return colorize ? Texts(Text("\x1b[1m"), text, Text("\x1b[m")) : text; @@ -77,30 +71,29 @@ public Text_t Enum$as_text(const void *obj, bool colorize, const TypeInfo_t *typ return generic_as_text(obj + value_offset(type), colorize, value.type); } -PUREFUNC public bool Enum$is_none(const void *x, const TypeInfo_t *info) -{ +PUREFUNC public bool Enum$is_none(const void *x, const TypeInfo_t *info) { (void)info; - return *(int32_t*)x == 0; + return *(int32_t *)x == 0; } -public void Enum$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) -{ - int32_t tag = *(int32_t*)obj; +public +void Enum$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) { + int32_t tag = *(int32_t *)obj; Int32$serialize(&tag, out, pointers, &Int32$info); - NamedType_t value = type->EnumInfo.tags[tag-1]; + NamedType_t value = type->EnumInfo.tags[tag - 1]; if (value.type && value.type->size > 0) { _serialize(obj + value_offset(type), out, pointers, value.type); } } -public void Enum$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type) -{ +public +void Enum$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type) { int32_t tag = 0; Int32$deserialize(in, &tag, pointers, &Int32$info); - *(int32_t*)outval = tag; + *(int32_t *)outval = tag; - NamedType_t value = type->EnumInfo.tags[tag-1]; + NamedType_t value = type->EnumInfo.tags[tag - 1]; if (value.type && value.type->size > 0) { _deserialize(in, outval + value_offset(type), pointers, value.type); } diff --git a/src/stdlib/enums.h b/src/stdlib/enums.h index 8345c527..99a0f615 100644 --- a/src/stdlib/enums.h +++ b/src/stdlib/enums.h @@ -15,24 +15,26 @@ PUREFUNC bool Enum$is_none(const void *obj, const TypeInfo_t *type); void Enum$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type); void Enum$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type); -#define Enum$metamethods { \ - .as_text=Enum$as_text, \ - .compare=Enum$compare, \ - .equal=Enum$equal, \ - .hash=Enum$hash, \ - .is_none=Enum$is_none, \ - .serialize=Enum$serialize, \ - .deserialize=Enum$deserialize, \ -} +#define Enum$metamethods \ + { \ + .as_text = Enum$as_text, \ + .compare = Enum$compare, \ + .equal = Enum$equal, \ + .hash = Enum$hash, \ + .is_none = Enum$is_none, \ + .serialize = Enum$serialize, \ + .deserialize = Enum$deserialize, \ + } -#define PackedDataEnum$metamethods { \ - .hash=PackedData$hash, \ - .compare=Enum$compare, \ - .equal=PackedData$equal, \ - .as_text=Enum$as_text, \ - .is_none=Enum$is_none, \ - .serialize=Enum$serialize, \ - .deserialize=Enum$deserialize, \ -} +#define PackedDataEnum$metamethods \ + { \ + .hash = PackedData$hash, \ + .compare = Enum$compare, \ + .equal = PackedData$equal, \ + .as_text = Enum$as_text, \ + .is_none = Enum$is_none, \ + .serialize = Enum$serialize, \ + .deserialize = Enum$deserialize, \ + } // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/files.c b/src/stdlib/files.c index 87b0205c..08023e0e 100644 --- a/src/stdlib/files.c +++ b/src/stdlib/files.c @@ -20,8 +20,8 @@ static const int tabstop = 4; -public char *resolve_path(const char *path, const char *relative_to, const char *system_path) -{ +public +char *resolve_path(const char *path, const char *relative_to, const char *system_path) { if (!relative_to || streq(relative_to, "/dev/stdin")) relative_to = "."; if (!path || strlen(path) == 0) return NULL; @@ -29,7 +29,7 @@ public char *resolve_path(const char *path, const char *relative_to, const char // it was found in: char buf[PATH_MAX] = {0}; if (streq(path, "~") || starts_with(path, "~/")) { - char *resolved = realpath(String(getenv("HOME"), path+1), buf); + char *resolved = realpath(String(getenv("HOME"), path + 1), buf); if (resolved) return GC_strdup(resolved); } else if (streq(path, ".") || starts_with(path, "./") || starts_with(path, "../")) { char *relative_dir = dirname(GC_strdup(relative_to)); @@ -49,12 +49,13 @@ public char *resolve_path(const char *path, const char *relative_to, const char char *resolved = realpath(String(dir, "/", path), buf); if (resolved) return GC_strdup(resolved); } else if (dir[0] == '~' && (dir[1] == '\0' || dir[1] == '/')) { - char *resolved = realpath(String(getenv("HOME"), dir+1, "/", path), buf); + char *resolved = realpath(String(getenv("HOME"), dir + 1, "/", path), buf); if (resolved) return GC_strdup(resolved); } else if (streq(dir, ".") || strncmp(dir, "./", 2) == 0) { char *resolved = realpath(String(relative_dir, "/", path), buf); if (resolved) return GC_strdup(resolved); - } else if (streq(dir, ".") || streq(dir, "..") || strncmp(dir, "./", 2) == 0 || strncmp(dir, "../", 3) == 0) { + } else if (streq(dir, ".") || streq(dir, "..") || strncmp(dir, "./", 2) == 0 + || strncmp(dir, "../", 3) == 0) { char *resolved = realpath(String(relative_dir, "/", dir, "/", path), buf); if (resolved) return GC_strdup(resolved); } else { @@ -66,24 +67,23 @@ public char *resolve_path(const char *path, const char *relative_to, const char return NULL; } -public char *file_base_name(const char *path) -{ +public +char *file_base_name(const char *path) { const char *slash = strrchr(path, '/'); if (slash) path = slash + 1; assert(!isdigit(*path)); const char *end = path + strcspn(path, "."); size_t len = (size_t)(end - path); - char *buf = GC_MALLOC_ATOMIC(len+1); + char *buf = GC_MALLOC_ATOMIC(len + 1); strncpy(buf, path, len); buf[len] = '\0'; return buf; } -static file_t *_load_file(const char* filename, FILE *file) -{ +static file_t *_load_file(const char *filename, FILE *file) { if (!file) return NULL; - file_t *ret = new(file_t, .filename=filename); + file_t *ret = new (file_t, .filename = filename); size_t file_size = 0, line_cap = 0; char *file_buf = NULL, *line_buf = NULL; @@ -98,7 +98,7 @@ static file_t *_load_file(const char* filename, FILE *file) } fclose(file); - char *copy = GC_MALLOC_ATOMIC(file_size+1); + char *copy = GC_MALLOC_ATOMIC(file_size + 1); memcpy(copy, file_buf, file_size); copy[file_size] = '\0'; ret->text = copy; @@ -114,7 +114,7 @@ static file_t *_load_file(const char* filename, FILE *file) char *cwd = getcwd(buf, sizeof(buf)); size_t cwd_len = strlen(cwd); if (strncmp(cwd, filename, cwd_len) == 0 && filename[cwd_len] == '/') - ret->relative_filename = &filename[cwd_len+1]; + ret->relative_filename = &filename[cwd_len + 1]; } return ret; } @@ -122,8 +122,8 @@ static file_t *_load_file(const char* filename, FILE *file) // // Read an entire file into memory. // -public file_t *load_file(const char* filename) -{ +public +file_t *load_file(const char *filename) { FILE *file = filename[0] ? fopen(filename, "r") : stdin; return _load_file(filename, file); } @@ -131,30 +131,27 @@ public file_t *load_file(const char* filename) // // Create a virtual file from a string. // -public file_t *spoof_file(const char* filename, const char *text) -{ - FILE *file = fmemopen((char*)text, strlen(text)+1, "r"); +public +file_t *spoof_file(const char *filename, const char *text) { + FILE *file = fmemopen((char *)text, strlen(text) + 1, "r"); return _load_file(filename, file); } // // Given a pointer, determine which line number it points to (1-indexed) // -public int64_t get_line_number(file_t *f, const char *p) -{ +public +int64_t get_line_number(file_t *f, const char *p) { // Binary search: - int64_t lo = 0, hi = (int64_t)f->num_lines-1; + int64_t lo = 0, hi = (int64_t)f->num_lines - 1; if (p < f->text) return 0; int64_t offset = (int64_t)(p - f->text); while (lo <= hi) { int64_t mid = (lo + hi) / 2; int64_t line_offset = f->line_offsets[mid]; - if (line_offset == offset) - return mid + 1; - else if (line_offset < offset) - lo = mid + 1; - else if (line_offset > offset) - hi = mid - 1; + if (line_offset == offset) return mid + 1; + else if (line_offset < offset) lo = mid + 1; + else if (line_offset > offset) hi = mid - 1; } return lo; // Return the line number whose line starts closest before p } @@ -162,33 +159,32 @@ public int64_t get_line_number(file_t *f, const char *p) // // Given a pointer, determine which line column it points to. // -public int64_t get_line_column(file_t *f, const char *p) -{ +public +int64_t get_line_column(file_t *f, const char *p) { int64_t line_no = get_line_number(f, p); - int64_t line_offset = f->line_offsets[line_no-1]; + int64_t line_offset = f->line_offsets[line_no - 1]; return 1 + (int64_t)(p - (f->text + line_offset)); } // // Return a pointer to the line with the specified line number (1-indexed) // -public const char *get_line(file_t *f, int64_t line_number) -{ +public +const char *get_line(file_t *f, int64_t line_number) { if (line_number == 0 || line_number > (int64_t)f->num_lines) return NULL; - int64_t line_offset = f->line_offsets[line_number-1]; + int64_t line_offset = f->line_offsets[line_number - 1]; return f->text + line_offset; } // // Return a value like /foo:line.col // -public const char *get_file_pos(file_t *f, const char *p) -{ +public +const char *get_file_pos(file_t *f, const char *p) { return String(f->filename, ":", get_line_number(f, p), ".", get_line_column(f, p)); } -static int fputc_column(FILE *out, char c, char print_char, int *column) -{ +static int fputc_column(FILE *out, char c, char print_char, int *column) { int printed = 0; if (print_char == '\t') print_char = ' '; if (c == '\t') { @@ -206,18 +202,16 @@ static int fputc_column(FILE *out, char c, char print_char, int *column) // // Print a span from a file // -public int highlight_error(file_t *file, const char *start, const char *end, const char *hl_color, int64_t context_lines, bool use_color) -{ +public +int highlight_error(file_t *file, const char *start, const char *end, const char *hl_color, int64_t context_lines, + bool use_color) { if (!file) return 0; // Handle spans that come from multiple files: - if (start < file->text || start > file->text + file->len) - start = end; - if (end < file->text || end > file->text + file->len) - end = start; + if (start < file->text || start > file->text + file->len) start = end; + if (end < file->text || end > file->text + file->len) end = start; // Just in case neither end of the span came from this file: - if (end < file->text || end > file->text + file->len) - start = end = file->text; + if (end < file->text || end > file->text + file->len) start = end = file->text; const char *lineno_prefix, *lineno_suffix, *normal_color, *empty_marker; bool print_carets = false; @@ -238,27 +232,25 @@ public int highlight_error(file_t *file, const char *start, const char *end, con printed += fprint(stderr, file->relative_filename); } - if (context_lines == 0) - return fprint(stderr, hl_color, string_slice(start, (size_t)(end - start)), normal_color); + if (context_lines == 0) return fprint(stderr, hl_color, string_slice(start, (size_t)(end - start)), normal_color); - int64_t start_line = get_line_number(file, start), - end_line = get_line_number(file, end); + int64_t start_line = get_line_number(file, start), end_line = get_line_number(file, end); - int64_t first_line = start_line - (context_lines - 1), - last_line = end_line + (context_lines - 1); + int64_t first_line = start_line - (context_lines - 1), last_line = end_line + (context_lines - 1); if (first_line < 1) first_line = 1; if (last_line > file->num_lines) last_line = file->num_lines; int digits = 1; - for (int64_t i = last_line; i > 0; i /= 10) ++digits; + for (int64_t i = last_line; i > 0; i /= 10) + ++digits; for (int64_t line_no = first_line; line_no <= last_line; ++line_no) { if (line_no > first_line + 5 && line_no < last_line - 5) { if (use_color) - printed += fprint(stderr, "\x1b[0;2;3;4m ... ", (last_line - first_line) - 11, " lines omitted ... \x1b[m"); - else - printed += fprint(stderr, " ... ", (last_line - first_line) - 11, " lines omitted ..."); + printed += fprint(stderr, "\x1b[0;2;3;4m ... ", (last_line - first_line) - 11, + " lines omitted ... \x1b[m"); + else printed += fprint(stderr, " ... ", (last_line - first_line) - 11, " lines omitted ..."); line_no = last_line - 6; continue; } @@ -305,14 +297,10 @@ public int highlight_error(file_t *file, const char *start, const char *end, con int col = 0; for (const char *sp = line; *sp && *sp != '\n'; ++sp) { char print_char; - if (sp < start) - print_char = ' '; - else if (sp == start && sp == end) - print_char = '^'; - else if (sp >= start && sp < end) - print_char = '-'; - else - print_char = ' '; + if (sp < start) print_char = ' '; + else if (sp == start && sp == end) print_char = '^'; + else if (sp >= start && sp < end) print_char = '-'; + else print_char = ' '; printed += fputc_column(stderr, *sp, print_char, &col); } printed += fputs("\n", stderr); diff --git a/src/stdlib/files.h b/src/stdlib/files.h index f650f78e..d3e5ebe8 100644 --- a/src/stdlib/files.h +++ b/src/stdlib/files.h @@ -16,20 +16,14 @@ typedef struct { } file_t; char *resolve_path(const char *path, const char *relative_to, const char *system_path); -__attribute__((pure, nonnull)) -char *file_base_name(const char *path); -__attribute__((nonnull)) -file_t *load_file(const char *filename); -__attribute__((nonnull, returns_nonnull)) -file_t *spoof_file(const char *filename, const char *text); -__attribute__((pure, nonnull)) -int64_t get_line_number(file_t *f, const char *p); -__attribute__((pure, nonnull)) -int64_t get_line_column(file_t *f, const char *p); -__attribute__((pure, nonnull)) -const char *get_line(file_t *f, int64_t line_number); -__attribute__((pure, nonnull)) -const char *get_file_pos(file_t *f, const char *p); -int highlight_error(file_t *file, const char *start, const char *end, const char *hl_color, int64_t context_lines, bool use_color); +__attribute__((pure, nonnull)) char *file_base_name(const char *path); +__attribute__((nonnull)) file_t *load_file(const char *filename); +__attribute__((nonnull, returns_nonnull)) file_t *spoof_file(const char *filename, const char *text); +__attribute__((pure, nonnull)) int64_t get_line_number(file_t *f, const char *p); +__attribute__((pure, nonnull)) int64_t get_line_column(file_t *f, const char *p); +__attribute__((pure, nonnull)) const char *get_line(file_t *f, int64_t line_number); +__attribute__((pure, nonnull)) const char *get_file_pos(file_t *f, const char *p); +int highlight_error(file_t *file, const char *start, const char *end, const char *hl_color, int64_t context_lines, + bool use_color); // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/fpconv.c b/src/stdlib/fpconv.c index 97699784..8b994cfa 100644 --- a/src/stdlib/fpconv.c +++ b/src/stdlib/fpconv.c @@ -6,37 +6,46 @@ #include "fpconv.h" #include "powers.h" -#define fracmask 0x000FFFFFFFFFFFFFU -#define expmask 0x7FF0000000000000U +#define fracmask 0x000FFFFFFFFFFFFFU +#define expmask 0x7FF0000000000000U #define hiddenbit 0x0010000000000000U -#define signmask 0x8000000000000000U -#define expbias (1023 + 52) +#define signmask 0x8000000000000000U +#define expbias (1023 + 52) #define absv(n) ((n) < 0 ? -(n) : (n)) #define minv(a, b) ((a) < (b) ? (a) : (b)) -static uint64_t tens[] = { - 10000000000000000000U, 1000000000000000000U, 100000000000000000U, - 10000000000000000U, 1000000000000000U, 100000000000000U, - 10000000000000U, 1000000000000U, 100000000000U, - 10000000000U, 1000000000U, 100000000U, - 10000000U, 1000000U, 100000U, - 10000U, 1000U, 100U, - 10U, 1U -}; - -static inline uint64_t get_dbits(double d) -{ +static uint64_t tens[] = {10000000000000000000U, + 1000000000000000000U, + 100000000000000000U, + 10000000000000000U, + 1000000000000000U, + 100000000000000U, + 10000000000000U, + 1000000000000U, + 100000000000U, + 10000000000U, + 1000000000U, + 100000000U, + 10000000U, + 1000000U, + 100000U, + 10000U, + 1000U, + 100U, + 10U, + 1U}; + +static inline uint64_t get_dbits(double d) { union { - double dbl; + double dbl; uint64_t i; - } dbl_bits = { d }; + } dbl_bits = {d}; return dbl_bits.i; } -static Fp build_fp(double d) -{ +static Fp build_fp(double d) { uint64_t bits = get_dbits(d); Fp fp; @@ -54,8 +63,7 @@ static Fp build_fp(double d) return fp; } -static void normalize(Fp* fp) -{ +static void normalize(Fp *fp) { while ((fp->frac & hiddenbit) == 0) { fp->frac <<= 1; fp->exp--; @@ -66,10 +74,9 @@ static void normalize(Fp* fp) fp->exp -= shift; } -static void get_normalized_boundaries(Fp* fp, Fp* lower, Fp* upper) -{ +static void get_normalized_boundaries(Fp *fp, Fp *lower, Fp *upper) { upper->frac = (fp->frac << 1) + 1; - upper->exp = fp->exp - 1; + upper->exp = fp->exp - 1; while ((upper->frac & (hiddenbit << 1)) == 0) { upper->frac <<= 1; @@ -81,64 +88,55 @@ static void get_normalized_boundaries(Fp* fp, Fp* lower, Fp* upper) upper->frac <<= u_shift; upper->exp = upper->exp - u_shift; - int l_shift = fp->frac == hiddenbit ? 2 : 1; lower->frac = (fp->frac << l_shift) - 1; lower->exp = fp->exp - l_shift; - lower->frac <<= lower->exp - upper->exp; lower->exp = upper->exp; } -static Fp multiply(Fp* a, Fp* b) -{ +static Fp multiply(Fp *a, Fp *b) { const uint64_t lomask = 0x00000000FFFFFFFF; - uint64_t ah_bl = (a->frac >> 32) * (b->frac & lomask); + uint64_t ah_bl = (a->frac >> 32) * (b->frac & lomask); uint64_t al_bh = (a->frac & lomask) * (b->frac >> 32); uint64_t al_bl = (a->frac & lomask) * (b->frac & lomask); - uint64_t ah_bh = (a->frac >> 32) * (b->frac >> 32); + uint64_t ah_bh = (a->frac >> 32) * (b->frac >> 32); uint64_t tmp = (ah_bl & lomask) + (al_bh & lomask) + (al_bl >> 32); /* round up */ tmp += 1U << 31; - Fp fp = { - ah_bh + (ah_bl >> 32) + (al_bh >> 32) + (tmp >> 32), - a->exp + b->exp + 64 - }; + Fp fp = {ah_bh + (ah_bl >> 32) + (al_bh >> 32) + (tmp >> 32), a->exp + b->exp + 64}; return fp; } -static void round_digit(char* digits, int ndigits, uint64_t delta, uint64_t rem, uint64_t kappa, uint64_t frac) -{ - while (rem < frac && delta - rem >= kappa && - (rem + kappa < frac || frac - rem > rem + kappa - frac)) { +static void round_digit(char *digits, int ndigits, uint64_t delta, uint64_t rem, uint64_t kappa, uint64_t frac) { + while (rem < frac && delta - rem >= kappa && (rem + kappa < frac || frac - rem > rem + kappa - frac)) { digits[ndigits - 1]--; rem += kappa; } } -static int generate_digits(Fp* fp, Fp* upper, Fp* lower, char* digits, int* K) -{ +static int generate_digits(Fp *fp, Fp *upper, Fp *lower, char *digits, int *K) { uint64_t wfrac = upper->frac - fp->frac; uint64_t delta = upper->frac - lower->frac; Fp one; one.frac = 1ULL << -upper->exp; - one.exp = upper->exp; + one.exp = upper->exp; uint64_t part1 = upper->frac >> -one.exp; uint64_t part2 = upper->frac & (one.frac - 1); int idx = 0, kappa = 10; - uint64_t* divp; + uint64_t *divp; /* 1000000000 */ - for(divp = tens + 10; kappa > 0; divp++) { + for (divp = tens + 10; kappa > 0; divp++) { uint64_t div = *divp; unsigned digit = part1 / div; @@ -150,7 +148,7 @@ static int generate_digits(Fp* fp, Fp* upper, Fp* lower, char* digits, int* K) part1 -= digit * div; kappa--; - uint64_t tmp = (part1 <<-one.exp) + part2; + uint64_t tmp = (part1 << -one.exp) + part2; if (tmp <= delta) { *K += kappa; round_digit(digits, idx, delta, tmp, div << -one.exp, wfrac); @@ -160,7 +158,7 @@ static int generate_digits(Fp* fp, Fp* upper, Fp* lower, char* digits, int* K) } /* 10 */ - uint64_t* unit = tens + 18; + uint64_t *unit = tens + 18; while (true) { part2 *= 10; @@ -184,8 +182,7 @@ static int generate_digits(Fp* fp, Fp* upper, Fp* lower, char* digits, int* K) } } -static int grisu2(double d, char* digits, int* K) -{ +static int grisu2(double d, char *digits, int *K) { Fp w = build_fp(d); Fp lower, upper; @@ -196,7 +193,7 @@ static int grisu2(double d, char* digits, int* K) int k; Fp cp = find_cachedpow10(upper.exp, &k); - w = multiply(&w, &cp); + w = multiply(&w, &cp); upper = multiply(&upper, &cp); lower = multiply(&lower, &cp); @@ -208,8 +205,7 @@ static int grisu2(double d, char* digits, int* K) return generate_digits(&w, &upper, &lower, digits, K); } -static int emit_digits(char* digits, int ndigits, char* dest, int K, bool neg) -{ +static int emit_digits(char *digits, int ndigits, char *dest, int K, bool neg) { int exp = absv(K + ndigits - 1); int max_trailing_zeros = 7; @@ -240,7 +236,7 @@ static int emit_digits(char* digits, int ndigits, char* dest, int K, bool neg) return ndigits + 2 + offset; - /* fp > 1.0 */ + /* fp > 1.0 */ } else { memcpy(dest, digits, (size_t)offset); dest[offset] = '.'; @@ -288,8 +284,7 @@ static int emit_digits(char* digits, int ndigits, char* dest, int K, bool neg) return idx; } -static int filter_special(double fp, char* dest) -{ +static int filter_special(double fp, char *dest) { if (fp == 0.0) { dest[0] = '0'; return 1; @@ -304,17 +299,20 @@ static int filter_special(double fp, char* dest) } if (bits & fracmask) { - dest[0] = 'n'; dest[1] = 'a'; dest[2] = 'n'; + dest[0] = 'n'; + dest[1] = 'a'; + dest[2] = 'n'; } else { - dest[0] = 'i'; dest[1] = 'n'; dest[2] = 'f'; + dest[0] = 'i'; + dest[1] = 'n'; + dest[2] = 'f'; } return 3; } -int fpconv_dtoa(double d, char dest[24]) -{ +int fpconv_dtoa(double d, char dest[24]) { char digits[18]; int str_len = 0; diff --git a/src/stdlib/functiontype.c b/src/stdlib/functiontype.c index 6d692c6d..a6e12798 100644 --- a/src/stdlib/functiontype.c +++ b/src/stdlib/functiontype.c @@ -9,18 +9,17 @@ #include "types.h" #include "util.h" -public Text_t Func$as_text(const void *fn, bool colorize, const TypeInfo_t *type) -{ +public +Text_t Func$as_text(const void *fn, bool colorize, const TypeInfo_t *type) { Text_t text = Text$from_str(type->FunctionInfo.type_str); - if (fn && colorize) - text = Text$concat(Text("\x1b[32;1m"), text, Text("\x1b[m")); + if (fn && colorize) text = Text$concat(Text("\x1b[32;1m"), text, Text("\x1b[m")); return text; } -public PUREFUNC bool Func$is_none(const void *obj, const TypeInfo_t *info) -{ +public +PUREFUNC bool Func$is_none(const void *obj, const TypeInfo_t *info) { (void)info; - return *(void**)obj == NULL; + return *(void **)obj == NULL; } // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/functiontype.h b/src/stdlib/functiontype.h index 095d630e..3169cfc9 100644 --- a/src/stdlib/functiontype.h +++ b/src/stdlib/functiontype.h @@ -13,20 +13,27 @@ OptionalText_t get_function_name(void *fn); OptionalText_t get_function_filename(void *fn); int64_t get_function_line_num(void *fn); Text_t Func$as_text(const void *fn, bool colorize, const TypeInfo_t *type); -PUREFUNC bool Func$is_none(const void *obj, const TypeInfo_t*); +PUREFUNC bool Func$is_none(const void *obj, const TypeInfo_t *); -#define Func$metamethods { \ - .as_text=Func$as_text, \ - .is_none=Func$is_none, \ - .serialize=cannot_serialize, \ - .deserialize=cannot_deserialize, \ -} +#define Func$metamethods \ + { \ + .as_text = Func$as_text, \ + .is_none = Func$is_none, \ + .serialize = cannot_serialize, \ + .deserialize = cannot_deserialize, \ + } -#define Function$info(typestr) &((TypeInfo_t){.size=sizeof(void*), .align=__alignof__(void*), \ - .tag=FunctionInfo, .FunctionInfo.type_str=typestr, \ - .metamethods=Func$metamethods}) -#define Closure$info(typestr) &((TypeInfo_t){.size=sizeof(void*[2]), .align=__alignof__(void*), \ - .tag=FunctionInfo, .FunctionInfo.type_str=typestr, \ - .metamethods=Func$metamethods}) +#define Function$info(typestr) \ + &((TypeInfo_t){.size = sizeof(void *), \ + .align = __alignof__(void *), \ + .tag = FunctionInfo, \ + .FunctionInfo.type_str = typestr, \ + .metamethods = Func$metamethods}) +#define Closure$info(typestr) \ + &((TypeInfo_t){.size = sizeof(void *[2]), \ + .align = __alignof__(void *), \ + .tag = FunctionInfo, \ + .FunctionInfo.type_str = typestr, \ + .metamethods = Func$metamethods}) // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/integers.c b/src/stdlib/integers.c index e24d984d..93095a70 100644 --- a/src/stdlib/integers.c +++ b/src/stdlib/integers.c @@ -17,20 +17,19 @@ #include "text.h" #include "types.h" -public int Int$print(FILE *f, Int_t i) { +public +int Int$print(FILE *f, Int_t i) { if (likely(i.small & 1L)) { - return _print_int(f, (int64_t)((i.small)>>2L)); + return _print_int(f, (int64_t)((i.small) >> 2L)); } else { return gmp_fprintf(f, "%Zd", *i.big); } } -static inline Text_t _int64_to_text(int64_t n) -{ - if (n == INT64_MIN) - return Text("-9223372036854775808"); +static inline Text_t _int64_to_text(int64_t n) { + if (n == INT64_MIN) return Text("-9223372036854775808"); - char buf[21] = {[20]=0}; // Big enough for INT64_MIN + '\0' + char buf[21] = {[20] = 0}; // Big enough for INT64_MIN + '\0' char *p = &buf[19]; bool negative = n < 0; if (negative) n = -n; // Safe to do because we checked for INT64_MIN earlier @@ -40,13 +39,13 @@ static inline Text_t _int64_to_text(int64_t n) n /= 10; } while (n > 0); - if (negative) - *(p--) = '-'; + if (negative) *(p--) = '-'; return Text$from_strn(p + 1, (size_t)(&buf[19] - p)); } -public Text_t Int$value_as_text(Int_t i) { +public +Text_t Int$value_as_text(Int_t i) { if (likely(i.small & 1L)) { return _int64_to_text(i.small >> 2L); } else { @@ -55,74 +54,76 @@ public Text_t Int$value_as_text(Int_t i) { } } -public Text_t Int$as_text(const void *i, bool colorize, const TypeInfo_t *info) { +public +Text_t Int$as_text(const void *i, bool colorize, const TypeInfo_t *info) { (void)info; if (!i) return Text("Int"); - Text_t text = Int$value_as_text(*(Int_t*)i); + Text_t text = Int$value_as_text(*(Int_t *)i); if (colorize) text = Text$concat(Text("\x1b[35m"), text, Text("\x1b[m")); return text; } -static bool Int$is_none(const void *i, const TypeInfo_t *info) -{ +static bool Int$is_none(const void *i, const TypeInfo_t *info) { (void)info; - return ((Int_t*)i)->small == 0L; + return ((Int_t *)i)->small == 0L; } -public PUREFUNC int32_t Int$compare_value(const Int_t x, const Int_t y) { - if (likely(x.small & y.small & 1L)) - return (x.small > y.small) - (x.small < y.small); - else if (x.small & 1) - return -mpz_cmp_si(*y.big, x.small); - else if (y.small & 1) - return mpz_cmp_si(*x.big, y.small); - else - return x.big == y.big ? 0 : mpz_cmp(*x.big, *y.big); +public +PUREFUNC int32_t Int$compare_value(const Int_t x, const Int_t y) { + if (likely(x.small & y.small & 1L)) return (x.small > y.small) - (x.small < y.small); + else if (x.small & 1) return -mpz_cmp_si(*y.big, x.small); + else if (y.small & 1) return mpz_cmp_si(*x.big, y.small); + else return x.big == y.big ? 0 : mpz_cmp(*x.big, *y.big); } -public PUREFUNC int32_t Int$compare(const void *x, const void *y, const TypeInfo_t *info) { +public +PUREFUNC int32_t Int$compare(const void *x, const void *y, const TypeInfo_t *info) { (void)info; - return Int$compare_value(*(Int_t*)x, *(Int_t*)y); + return Int$compare_value(*(Int_t *)x, *(Int_t *)y); } -public PUREFUNC bool Int$equal_value(const Int_t x, const Int_t y) { - if (likely((x.small | y.small) & 1L)) - return x.small == y.small; - else - return x.big == y.big ? 0 : (mpz_cmp(*x.big, *y.big) == 0); +public +PUREFUNC bool Int$equal_value(const Int_t x, const Int_t y) { + if (likely((x.small | y.small) & 1L)) return x.small == y.small; + else return x.big == y.big ? 0 : (mpz_cmp(*x.big, *y.big) == 0); } -public PUREFUNC bool Int$equal(const void *x, const void *y, const TypeInfo_t *info) { +public +PUREFUNC bool Int$equal(const void *x, const void *y, const TypeInfo_t *info) { (void)info; - return Int$equal_value(*(Int_t*)x, *(Int_t*)y); + return Int$equal_value(*(Int_t *)x, *(Int_t *)y); } -public CONSTFUNC Int_t Int$clamped(Int_t x, Int_t low, Int_t high) { +public +CONSTFUNC Int_t Int$clamped(Int_t x, Int_t low, Int_t high) { return (Int$compare(&x, &low, &Int$info) <= 0) ? low : (Int$compare(&x, &high, &Int$info) >= 0 ? high : x); } -public CONSTFUNC bool Int$is_between(const Int_t x, const Int_t low, const Int_t high) { +public +CONSTFUNC bool Int$is_between(const Int_t x, const Int_t low, const Int_t high) { return Int$compare_value(low, x) <= 0 && Int$compare_value(x, high) <= 0; } -public PUREFUNC uint64_t Int$hash(const void *vx, const TypeInfo_t *info) { +public +PUREFUNC uint64_t Int$hash(const void *vx, const TypeInfo_t *info) { (void)info; - Int_t *x = (Int_t*)vx; + Int_t *x = (Int_t *)vx; if (likely(x->small & 1L)) { - return siphash24((void*)x, sizeof(Int_t)); + return siphash24((void *)x, sizeof(Int_t)); } else { char *str = mpz_get_str(NULL, 16, *x->big); - return siphash24((void*)str, strlen(str)); + return siphash24((void *)str, strlen(str)); } } -public Text_t Int$hex(Int_t i, Int_t digits_int, bool uppercase, bool prefix) { - if (Int$is_negative(i)) - return Text$concat(Text("-"), Int$hex(Int$negative(i), digits_int, uppercase, prefix)); +public +Text_t Int$hex(Int_t i, Int_t digits_int, bool uppercase, bool prefix) { + if (Int$is_negative(i)) return Text$concat(Text("-"), Int$hex(Int$negative(i), digits_int, uppercase, prefix)); if (likely(i.small & 1L)) { uint64_t u64 = (uint64_t)(i.small >> 2); - return Text$from_str(String(hex(u64, .no_prefix=!prefix, .digits=Int32$from_int(digits_int, false), .uppercase=uppercase))); + return Text$from_str(String( + hex(u64, .no_prefix = !prefix, .digits = Int32$from_int(digits_int, false), .uppercase = uppercase))); } else { char *str = mpz_get_str(NULL, 16, *i.big); if (uppercase) { @@ -131,97 +132,88 @@ public Text_t Int$hex(Int_t i, Int_t digits_int, bool uppercase, bool prefix) { } int64_t digits = Int64$from_int(digits_int, false); int64_t needed_zeroes = digits - (int64_t)strlen(str); - if (needed_zeroes <= 0) - return prefix ? Text$concat(Text("0x"), Text$from_str(str)) : Text$from_str(str); + if (needed_zeroes <= 0) return prefix ? Text$concat(Text("0x"), Text$from_str(str)) : Text$from_str(str); char *zeroes = GC_MALLOC_ATOMIC((size_t)(needed_zeroes)); memset(zeroes, '0', (size_t)(needed_zeroes)); - if (prefix) - return Text$concat(Text("0x"), Text$from_str(zeroes), Text$from_str(str)); - else - return Text$concat(Text$from_str(zeroes), Text$from_str(str)); + if (prefix) return Text$concat(Text("0x"), Text$from_str(zeroes), Text$from_str(str)); + else return Text$concat(Text$from_str(zeroes), Text$from_str(str)); } } -public Text_t Int$octal(Int_t i, Int_t digits_int, bool prefix) { - if (Int$is_negative(i)) - return Text$concat(Text("-"), Int$octal(Int$negative(i), digits_int, prefix)); +public +Text_t Int$octal(Int_t i, Int_t digits_int, bool prefix) { + if (Int$is_negative(i)) return Text$concat(Text("-"), Int$octal(Int$negative(i), digits_int, prefix)); if (likely(i.small & 1L)) { uint64_t u64 = (uint64_t)(i.small >> 2); - return Text$from_str(String(oct(u64, .no_prefix=!prefix, .digits=Int32$from_int(digits_int, false)))); + return Text$from_str(String(oct(u64, .no_prefix = !prefix, .digits = Int32$from_int(digits_int, false)))); } else { int64_t digits = Int64$from_int(digits_int, false); char *str = mpz_get_str(NULL, 8, *i.big); int64_t needed_zeroes = digits - (int64_t)strlen(str); - if (needed_zeroes <= 0) - return prefix ? Text$concat(Text("0o"), Text$from_str(str)) : Text$from_str(str); + if (needed_zeroes <= 0) return prefix ? Text$concat(Text("0o"), Text$from_str(str)) : Text$from_str(str); char *zeroes = GC_MALLOC_ATOMIC((size_t)(needed_zeroes)); memset(zeroes, '0', (size_t)(needed_zeroes)); - if (prefix) - return Text$concat(Text("0o"), Text$from_str(zeroes), Text$from_str(str)); - else - return Text$concat(Text$from_str(zeroes), Text$from_str(str)); + if (prefix) return Text$concat(Text("0o"), Text$from_str(zeroes), Text$from_str(str)); + else return Text$concat(Text$from_str(zeroes), Text$from_str(str)); } } -public Int_t Int$slow_plus(Int_t x, Int_t y) { +public +Int_t Int$slow_plus(Int_t x, Int_t y) { mpz_t result; mpz_init_set_int(result, x); if (y.small & 1L) { - if (y.small < 0L) - mpz_sub_ui(result, result, (uint64_t)(-(y.small >> 2L))); - else - mpz_add_ui(result, result, (uint64_t)(y.small >> 2L)); + if (y.small < 0L) mpz_sub_ui(result, result, (uint64_t)(-(y.small >> 2L))); + else mpz_add_ui(result, result, (uint64_t)(y.small >> 2L)); } else { mpz_add(result, result, *y.big); } return Int$from_mpz(result); } -public Int_t Int$slow_minus(Int_t x, Int_t y) { +public +Int_t Int$slow_minus(Int_t x, Int_t y) { mpz_t result; mpz_init_set_int(result, x); if (y.small & 1L) { - if (y.small < 0L) - mpz_add_ui(result, result, (uint64_t)(-(y.small >> 2L))); - else - mpz_sub_ui(result, result, (uint64_t)(y.small >> 2L)); + if (y.small < 0L) mpz_add_ui(result, result, (uint64_t)(-(y.small >> 2L))); + else mpz_sub_ui(result, result, (uint64_t)(y.small >> 2L)); } else { mpz_sub(result, result, *y.big); } return Int$from_mpz(result); } -public Int_t Int$slow_times(Int_t x, Int_t y) { +public +Int_t Int$slow_times(Int_t x, Int_t y) { mpz_t result; mpz_init_set_int(result, x); - if (y.small & 1L) - mpz_mul_si(result, result, y.small >> 2L); - else - mpz_mul(result, result, *y.big); + if (y.small & 1L) mpz_mul_si(result, result, y.small >> 2L); + else mpz_mul(result, result, *y.big); return Int$from_mpz(result); } -public Int_t Int$slow_divided_by(Int_t dividend, Int_t divisor) { - // Euclidean division, see: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf +public +Int_t Int$slow_divided_by(Int_t dividend, Int_t divisor) { + // Euclidean division, see: + // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf mpz_t quotient, remainder; mpz_init_set_int(quotient, dividend); mpz_init_set_int(remainder, divisor); mpz_tdiv_qr(quotient, remainder, quotient, remainder); if (mpz_sgn(remainder) < 0) { bool d_positive = likely(divisor.small & 1L) ? divisor.small > 0x1L : mpz_sgn(*divisor.big) > 0; - if (d_positive) - mpz_sub_ui(quotient, quotient, 1); - else - mpz_add_ui(quotient, quotient, 1); + if (d_positive) mpz_sub_ui(quotient, quotient, 1); + else mpz_add_ui(quotient, quotient, 1); } return Int$from_mpz(quotient); } -public Int_t Int$slow_modulo(Int_t x, Int_t modulus) -{ +public +Int_t Int$slow_modulo(Int_t x, Int_t modulus) { mpz_t result; mpz_init_set_int(result, x); mpz_t divisor; @@ -230,8 +222,8 @@ public Int_t Int$slow_modulo(Int_t x, Int_t modulus) return Int$from_mpz(result); } -public Int_t Int$slow_modulo1(Int_t x, Int_t modulus) -{ +public +Int_t Int$slow_modulo1(Int_t x, Int_t modulus) { mpz_t result; mpz_init_set_int(result, x); mpz_sub_ui(result, result, 1); @@ -242,8 +234,8 @@ public Int_t Int$slow_modulo1(Int_t x, Int_t modulus) return Int$from_mpz(result); } -public Int_t Int$slow_left_shifted(Int_t x, Int_t y) -{ +public +Int_t Int$slow_left_shifted(Int_t x, Int_t y) { mp_bitcnt_t bits = (mp_bitcnt_t)Int64$from_int(y, false); mpz_t result; mpz_init_set_int(result, x); @@ -251,8 +243,8 @@ public Int_t Int$slow_left_shifted(Int_t x, Int_t y) return Int$from_mpz(result); } -public Int_t Int$slow_right_shifted(Int_t x, Int_t y) -{ +public +Int_t Int$slow_right_shifted(Int_t x, Int_t y) { mp_bitcnt_t bits = (mp_bitcnt_t)Int64$from_int(y, false); mpz_t result; mpz_init_set_int(result, x); @@ -260,8 +252,8 @@ public Int_t Int$slow_right_shifted(Int_t x, Int_t y) return Int$from_mpz(result); } -public Int_t Int$slow_bit_and(Int_t x, Int_t y) -{ +public +Int_t Int$slow_bit_and(Int_t x, Int_t y) { mpz_t result; mpz_init_set_int(result, x); mpz_t y_mpz; @@ -270,8 +262,8 @@ public Int_t Int$slow_bit_and(Int_t x, Int_t y) return Int$from_mpz(result); } -public Int_t Int$slow_bit_or(Int_t x, Int_t y) -{ +public +Int_t Int$slow_bit_or(Int_t x, Int_t y) { mpz_t result; mpz_init_set_int(result, x); mpz_t y_mpz; @@ -280,8 +272,8 @@ public Int_t Int$slow_bit_or(Int_t x, Int_t y) return Int$from_mpz(result); } -public Int_t Int$slow_bit_xor(Int_t x, Int_t y) -{ +public +Int_t Int$slow_bit_xor(Int_t x, Int_t y) { mpz_t result; mpz_init_set_int(result, x); mpz_t y_mpz; @@ -290,8 +282,8 @@ public Int_t Int$slow_bit_xor(Int_t x, Int_t y) return Int$from_mpz(result); } -public Int_t Int$slow_negated(Int_t x) -{ +public +Int_t Int$slow_negated(Int_t x) { mpz_t result; mpz_init_set_int(result, x); mpz_neg(result, result); @@ -299,10 +291,9 @@ public Int_t Int$slow_negated(Int_t x) return Int$from_mpz(result); } -public Int_t Int$slow_negative(Int_t x) -{ - if (likely(x.small & 1L)) - return (Int_t){.small=4L*-((x.small)>>2L) + 1L}; +public +Int_t Int$slow_negative(Int_t x) { + if (likely(x.small & 1L)) return (Int_t){.small = 4L * -((x.small) >> 2L) + 1L}; mpz_t result; mpz_init_set_int(result, x); @@ -310,10 +301,9 @@ public Int_t Int$slow_negative(Int_t x) return Int$from_mpz(result); } -public Int_t Int$abs(Int_t x) -{ - if (likely(x.small & 1L)) - return (Int_t){.small=4L*labs((x.small)>>2L) + 1L}; +public +Int_t Int$abs(Int_t x) { + if (likely(x.small & 1L)) return (Int_t){.small = 4L * labs((x.small) >> 2L) + 1L}; mpz_t result; mpz_init_set_int(result, x); @@ -321,53 +311,45 @@ public Int_t Int$abs(Int_t x) return Int$from_mpz(result); } -public Int_t Int$power(Int_t base, Int_t exponent) -{ +public +Int_t Int$power(Int_t base, Int_t exponent) { int64_t exp = Int64$from_int(exponent, false); - if (unlikely(exp < 0)) - fail("Cannot take a negative power of an integer!"); + if (unlikely(exp < 0)) fail("Cannot take a negative power of an integer!"); mpz_t result; mpz_init_set_int(result, base); mpz_pow_ui(result, result, (uint64_t)exp); return Int$from_mpz(result); } -public Int_t Int$gcd(Int_t x, Int_t y) -{ - if (likely(x.small & y.small & 0x1L)) - return I_small(Int32$gcd(x.small >> 2L, y.small >> 2L)); +public +Int_t Int$gcd(Int_t x, Int_t y) { + if (likely(x.small & y.small & 0x1L)) return I_small(Int32$gcd(x.small >> 2L, y.small >> 2L)); mpz_t result; mpz_init(result); - if (x.small & 0x1L) - mpz_gcd_ui(result, *y.big, (uint64_t)labs(x.small>>2L)); - else if (y.small & 0x1L) - mpz_gcd_ui(result, *x.big, (uint64_t)labs(y.small>>2L)); - else - mpz_gcd(result, *x.big, *y.big); + if (x.small & 0x1L) mpz_gcd_ui(result, *y.big, (uint64_t)labs(x.small >> 2L)); + else if (y.small & 0x1L) mpz_gcd_ui(result, *x.big, (uint64_t)labs(y.small >> 2L)); + else mpz_gcd(result, *x.big, *y.big); return Int$from_mpz(result); } -public OptionalInt_t Int$sqrt(Int_t i) -{ - if (Int$compare_value(i, I(0)) < 0) - return NONE_INT; +public +OptionalInt_t Int$sqrt(Int_t i) { + if (Int$compare_value(i, I(0)) < 0) return NONE_INT; mpz_t result; mpz_init_set_int(result, i); mpz_sqrt(result, result); return Int$from_mpz(result); } -public bool Int$get_bit(Int_t x, Int_t bit_index) -{ +public +bool Int$get_bit(Int_t x, Int_t bit_index) { mpz_t i; mpz_init_set_int(i, x); - if (Int$compare_value(bit_index, I(1)) < 0) - fail("Invalid bit index (expected 1 or higher): ", bit_index); - if (Int$compare_value(bit_index, Int$from_int64(INT64_MAX)) > 0) - fail("Bit index is too large! ", bit_index); + if (Int$compare_value(bit_index, I(1)) < 0) fail("Invalid bit index (expected 1 or higher): ", bit_index); + if (Int$compare_value(bit_index, Int$from_int64(INT64_MAX)) > 0) fail("Bit index is too large! ", bit_index); - int is_bit_set = mpz_tstbit(i, (mp_bitcnt_t)(Int64$from_int(bit_index, true)-1)); + int is_bit_set = mpz_tstbit(i, (mp_bitcnt_t)(Int64$from_int(bit_index, true) - 1)); return (bool)is_bit_set; } @@ -376,37 +358,41 @@ typedef struct { Int_t step; } IntRange_t; -static OptionalInt_t _next_int(IntRange_t *info) -{ +static OptionalInt_t _next_int(IntRange_t *info) { OptionalInt_t i = info->current; if (!Int$is_none(&i, &Int$info)) { Int_t next = Int$plus(i, info->step); - if (!Int$is_none(&info->last, &Int$info) && Int$compare_value(next, info->last) == Int$compare_value(info->step, I(0))) + if (!Int$is_none(&info->last, &Int$info) + && Int$compare_value(next, info->last) == Int$compare_value(info->step, I(0))) next = NONE_INT; info->current = next; } return i; } -public PUREFUNC Closure_t Int$to(Int_t first, Int_t last, OptionalInt_t step) { +public +PUREFUNC Closure_t Int$to(Int_t first, Int_t last, OptionalInt_t step) { IntRange_t *range = GC_MALLOC(sizeof(IntRange_t)); range->current = first; range->last = last; - range->step = Int$is_none(&step, &Int$info) ? - Int$compare_value(last, first) >= 0 ? (Int_t){.small=(1L<<2L)|1L} : (Int_t){.small=(-1L>>2L)|1L} - : step; - return (Closure_t){.fn=_next_int, .userdata=range}; + range->step = Int$is_none(&step, &Int$info) ? Int$compare_value(last, first) >= 0 + ? (Int_t){.small = (1L << 2L) | 1L} + : (Int_t){.small = (-1L >> 2L) | 1L} + : step; + return (Closure_t){.fn = _next_int, .userdata = range}; } -public PUREFUNC Closure_t Int$onward(Int_t first, Int_t step) { +public +PUREFUNC Closure_t Int$onward(Int_t first, Int_t step) { IntRange_t *range = GC_MALLOC(sizeof(IntRange_t)); range->current = first; range->last = NONE_INT; range->step = step; - return (Closure_t){.fn=_next_int, .userdata=range}; + return (Closure_t){.fn = _next_int, .userdata = range}; } -public Int_t Int$from_str(const char *str) { +public +Int_t Int$from_str(const char *str) { mpz_t i; int result; if (strncmp(str, "0x", 2) == 0) { @@ -418,12 +404,12 @@ public Int_t Int$from_str(const char *str) { } else { result = mpz_init_set_str(i, str, 10); } - if (result != 0) - return NONE_INT; + if (result != 0) return NONE_INT; return Int$from_mpz(i); } -public OptionalInt_t Int$parse(Text_t text, Text_t *remainder) { +public +OptionalInt_t Int$parse(Text_t text, Text_t *remainder) { const char *str = Text$as_c_string(text); mpz_t i; int result; @@ -455,8 +441,8 @@ public OptionalInt_t Int$parse(Text_t text, Text_t *remainder) { return Int$from_mpz(i); } -public bool Int$is_prime(Int_t x, Int_t reps) -{ +public +bool Int$is_prime(Int_t x, Int_t reps) { mpz_t p; mpz_init_set_int(p, x); if (unlikely(Int$compare_value(reps, I(9999)) > 0)) @@ -465,8 +451,8 @@ public bool Int$is_prime(Int_t x, Int_t reps) return (mpz_probab_prime_p(p, reps_int) != 0); } -public Int_t Int$next_prime(Int_t x) -{ +public +Int_t Int$next_prime(Int_t x) { mpz_t p; mpz_init_set_int(p, x); mpz_nextprime(p, p); @@ -475,28 +461,25 @@ public Int_t Int$next_prime(Int_t x) #if __GNU_MP_VERSION >= 6 #if __GNU_MP_VERSION_MINOR >= 3 -public OptionalInt_t Int$prev_prime(Int_t x) -{ +public +OptionalInt_t Int$prev_prime(Int_t x) { mpz_t p; mpz_init_set_int(p, x); - if (unlikely(mpz_prevprime(p, p) == 0)) - return NONE_INT; + if (unlikely(mpz_prevprime(p, p) == 0)) return NONE_INT; return Int$from_mpz(p); } #endif #endif -public Int_t Int$choose(Int_t n, Int_t k) -{ - if unlikely (Int$compare_value(n, I_small(0)) < 0) - fail("Negative inputs are not supported for choose()"); +public +Int_t Int$choose(Int_t n, Int_t k) { + if unlikely (Int$compare_value(n, I_small(0)) < 0) fail("Negative inputs are not supported for choose()"); mpz_t ret; mpz_init(ret); int64_t k_i64 = Int64$from_int(k, false); - if unlikely (k_i64 < 0) - fail("Negative inputs are not supported for choose()"); + if unlikely (k_i64 < 0) fail("Negative inputs are not supported for choose()"); if likely (n.small & 1L) { mpz_bin_uiui(ret, (unsigned long)(n.small >> 2L), (unsigned long)k_i64); @@ -508,21 +491,19 @@ public Int_t Int$choose(Int_t n, Int_t k) return Int$from_mpz(ret); } -public Int_t Int$factorial(Int_t n) -{ +public +Int_t Int$factorial(Int_t n) { mpz_t ret; mpz_init(ret); int64_t n_i64 = Int64$from_int(n, false); - if unlikely (n_i64 < 0) - fail("Factorials are not defined for negative numbers"); + if unlikely (n_i64 < 0) fail("Factorials are not defined for negative numbers"); mpz_fac_ui(ret, (unsigned long)n_i64); return Int$from_mpz(ret); } -static void Int$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) -{ +static void Int$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) { (void)info; - Int_t i = *(Int_t*)obj; + Int_t i = *(Int_t *)obj; if (likely(i.small & 1L)) { fputc(0, out); int64_t i64 = i.small >> 2L; @@ -530,44 +511,45 @@ static void Int$serialize(const void *obj, FILE *out, Table_t *pointers, const T } else { fputc(1, out); mpz_t n; - mpz_init_set_int(n, *(Int_t*)obj); + mpz_init_set_int(n, *(Int_t *)obj); mpz_out_raw(out, n); } } -static void Int$deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *info) -{ +static void Int$deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *info) { (void)info; if (fgetc(in) == 0) { int64_t i = 0; Int64$deserialize(in, &i, pointers, &Int64$info); - *(Int_t*)obj = (Int_t){.small=(i<<2L) | 1L}; + *(Int_t *)obj = (Int_t){.small = (i << 2L) | 1L}; } else { mpz_t n; mpz_init(n); mpz_inp_raw(n, in); - *(Int_t*)obj = Int$from_mpz(n); + *(Int_t *)obj = Int$from_mpz(n); } } -public const TypeInfo_t Int$info = { - .size=sizeof(Int_t), - .align=__alignof__(Int_t), - .metamethods={ - .compare=Int$compare, - .equal=Int$equal, - .hash=Int$hash, - .as_text=Int$as_text, - .is_none=Int$is_none, - .serialize=Int$serialize, - .deserialize=Int$deserialize, - }, +public +const TypeInfo_t Int$info = { + .size = sizeof(Int_t), + .align = __alignof__(Int_t), + .metamethods = + { + .compare = Int$compare, + .equal = Int$equal, + .hash = Int$hash, + .as_text = Int$as_text, + .is_none = Int$is_none, + .serialize = Int$serialize, + .deserialize = Int$deserialize, + }, }; -public void Int64$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) -{ +public +void Int64$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) { (void)info, (void)pointers; - int64_t i = *(int64_t*)obj; + int64_t i = *(int64_t *)obj; uint64_t z = (uint64_t)((i << 1L) ^ (i >> 63L)); // Zigzag encode while (z >= 0x80L) { fputc((uint8_t)(z | 0x80L), out); @@ -576,22 +558,22 @@ public void Int64$serialize(const void *obj, FILE *out, Table_t *pointers, const fputc((uint8_t)z, out); } -public void Int64$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *info) -{ +public +void Int64$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *info) { (void)info, (void)pointers; uint64_t z = 0; - for(size_t shift = 0; ; shift += 7) { + for (size_t shift = 0;; shift += 7) { uint8_t byte = (uint8_t)fgetc(in); z |= ((uint64_t)(byte & 0x7F)) << shift; if ((byte & 0x80) == 0) break; } - *(int64_t*)outval = (int64_t)((z >> 1L) ^ -(z & 1L)); // Zigzag decode + *(int64_t *)outval = (int64_t)((z >> 1L) ^ -(z & 1L)); // Zigzag decode } -public void Int32$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) -{ +public +void Int32$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) { (void)info, (void)pointers; - int32_t i = *(int32_t*)obj; + int32_t i = *(int32_t *)obj; uint32_t z = (uint32_t)((i << 1) ^ (i >> 31)); // Zigzag encode while (z >= 0x80) { fputc((uint8_t)(z | 0x80), out); @@ -600,16 +582,16 @@ public void Int32$serialize(const void *obj, FILE *out, Table_t *pointers, const fputc((uint8_t)z, out); } -public void Int32$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *info) -{ +public +void Int32$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *info) { (void)info, (void)pointers; uint32_t z = 0; - for(size_t shift = 0; ; shift += 7) { + for (size_t shift = 0;; shift += 7) { uint8_t byte = (uint8_t)fgetc(in); z |= ((uint32_t)(byte & 0x7F)) << shift; if ((byte & 0x80) == 0) break; } - *(int32_t*)outval = (int32_t)((z >> 1L) ^ -(z & 1L)); // Zigzag decode + *(int32_t *)outval = (int32_t)((z >> 1L) ^ -(z & 1L)); // Zigzag decode } // The space savings for smaller ints are not worth having: @@ -619,122 +601,146 @@ public void Int32$deserialize(FILE *in, void *outval, List_t *pointers, const Ty #define Int8$deserialize NULL #ifdef __TINYC__ -#define __builtin_add_overflow(x, y, result) ({ *(result) = (x) + (y); false; }) +#define __builtin_add_overflow(x, y, result) \ + ({ \ + *(result) = (x) + (y); \ + false; \ + }) #endif -#define DEFINE_INT_TYPE(c_type, KindOfInt, min_val, max_val, to_attr)\ - public Text_t KindOfInt ## $as_text(const void *i, bool colorize, const TypeInfo_t *info) { \ - (void)info; \ - if (!i) return Text(#KindOfInt); \ - Text_t text = _int64_to_text((int64_t)(*(c_type*)i)); \ - return colorize ? Texts(Text("\033[35m"), text, Text("\033[m")) : text; \ - } \ - public PUREFUNC int32_t KindOfInt ## $compare(const void *x, const void *y, const TypeInfo_t *info) { \ - (void)info; \ - return (*(c_type*)x > *(c_type*)y) - (*(c_type*)x < *(c_type*)y); \ - } \ - public PUREFUNC bool KindOfInt ## $equal(const void *x, const void *y, const TypeInfo_t *info) { \ - (void)info; \ - return *(c_type*)x == *(c_type*)y; \ - } \ - public CONSTFUNC bool KindOfInt ## $is_between(const c_type x, const c_type low, const c_type high) { \ - return low <= x && x <= high; \ - } \ - public CONSTFUNC c_type KindOfInt ## $clamped(c_type x, c_type min, c_type max) { \ - return x < min ? min : (x > max ? max : x); \ - } \ - public Text_t KindOfInt ## $hex(c_type i, Int_t digits_int, bool uppercase, bool prefix) { \ - Int_t as_int = Int$from_int64((int64_t)i); \ - return Int$hex(as_int, digits_int, uppercase, prefix); \ - } \ - public Text_t KindOfInt ## $octal(c_type i, Int_t digits_int, bool prefix) { \ - Int_t as_int = Int$from_int64((int64_t)i); \ - return Int$octal(as_int, digits_int, prefix); \ - } \ - public List_t KindOfInt ## $bits(c_type x) { \ - List_t bit_list = (List_t){.data=GC_MALLOC_ATOMIC(sizeof(bool[8*sizeof(c_type)])), .atomic=1, .stride=sizeof(bool), .length=8*sizeof(c_type)}; \ - bool *bits = bit_list.data + sizeof(c_type)*8; \ - for (size_t i = 0; i < 8*sizeof(c_type); i++) { \ - *(bits--) = x & 1; \ - x >>= 1; \ - } \ - return bit_list; \ - } \ - public bool KindOfInt ## $get_bit(c_type x, Int_t bit_index) { \ - if (Int$compare_value(bit_index, I(1)) < 0) \ - fail("Invalid bit index (expected 1 or higher): ", bit_index); \ - if (Int$compare_value(bit_index, Int$from_int64(sizeof(c_type)*8)) > 0) \ - fail("Bit index is too large! There are only ", (uint64_t)sizeof(c_type)*8, " bits, but index is: ", bit_index); \ - return ((x & (c_type)(1L << (Int64$from_int(bit_index, true)-1L))) != 0); \ - } \ - typedef struct { \ - Optional##KindOfInt##_t current, last; \ - KindOfInt##_t step; \ - } KindOfInt##Range_t; \ - static Optional##KindOfInt##_t _next_##KindOfInt(KindOfInt##Range_t *info) \ - { \ - Optional##KindOfInt##_t i = info->current; \ - if (!i.is_none) { \ - KindOfInt##_t next; bool overflow = __builtin_add_overflow(i.value, info->step, &next); \ - if (overflow || (!info->last.is_none && (info->step >= 0 ? next > info->last.value : next < info->last.value))) \ - info->current = (Optional##KindOfInt##_t){.is_none=true}; \ - else \ - info->current = (Optional##KindOfInt##_t){.value=next}; \ - } \ - return i; \ - } \ - public to_attr Closure_t KindOfInt ## $to(c_type first, c_type last, Optional ## KindOfInt ## _t step) { \ - KindOfInt##Range_t *range = GC_MALLOC(sizeof(KindOfInt##Range_t)); \ - range->current = (Optional##KindOfInt##_t){.value=first}; \ - range->last = (Optional##KindOfInt##_t){.value=last}; \ - range->step = step.is_none ? (last >= first ? 1 : -1) : step.value; \ - return (Closure_t){.fn=_next_##KindOfInt, .userdata=range}; \ - } \ - public to_attr Closure_t KindOfInt ## $onward(c_type first, c_type step) { \ - KindOfInt##Range_t *range = GC_MALLOC(sizeof(KindOfInt##Range_t)); \ - range->current = (Optional##KindOfInt##_t){.value=first}; \ - range->last = (Optional##KindOfInt##_t){.is_none=true}; \ - range->step = step; \ - return (Closure_t){.fn=_next_##KindOfInt, .userdata=range}; \ - } \ - public PUREFUNC Optional ## KindOfInt ## _t KindOfInt ## $parse(Text_t text, Text_t *remainder) { \ - OptionalInt_t full_int = Int$parse(text, remainder); \ - if (full_int.small == 0L) return (Optional ## KindOfInt ## _t){.is_none=true}; \ - if (Int$compare_value(full_int, I(min_val)) < 0) { \ - return (Optional ## KindOfInt ## _t){.is_none=true}; \ - } \ - if (Int$compare_value(full_int, I(max_val)) > 0) { \ - return (Optional ## KindOfInt ## _t){.is_none=true}; \ - } \ - return (Optional ## KindOfInt ## _t){.value=KindOfInt##$from_int(full_int, true)}; \ - } \ - public CONSTFUNC c_type KindOfInt ## $gcd(c_type x, c_type y) { \ - if (x == 0 || y == 0) return 0; \ - x = KindOfInt##$abs(x); \ - y = KindOfInt##$abs(y); \ - while (x != y) { \ - if (x > y) x -= y; \ - else y -= x; \ - } \ - return x; \ - } \ - public const c_type KindOfInt##$min = min_val; \ - public const c_type KindOfInt##$max = max_val; \ - public const TypeInfo_t KindOfInt##$info = { \ - .size=sizeof(c_type), \ - .align=__alignof__(c_type), \ - .metamethods={ \ - .compare=KindOfInt##$compare, \ - .as_text=KindOfInt##$as_text, \ - .serialize=KindOfInt##$serialize, \ - .deserialize=KindOfInt##$deserialize, \ - }, \ +#define DEFINE_INT_TYPE(c_type, KindOfInt, min_val, max_val, to_attr) \ + public \ + Text_t KindOfInt##$as_text(const void *i, bool colorize, const TypeInfo_t *info) { \ + (void)info; \ + if (!i) return Text(#KindOfInt); \ + Text_t text = _int64_to_text((int64_t)(*(c_type *)i)); \ + return colorize ? Texts(Text("\033[35m"), text, Text("\033[m")) : text; \ + } \ + public \ + PUREFUNC int32_t KindOfInt##$compare(const void *x, const void *y, const TypeInfo_t *info) { \ + (void)info; \ + return (*(c_type *)x > *(c_type *)y) - (*(c_type *)x < *(c_type *)y); \ + } \ + public \ + PUREFUNC bool KindOfInt##$equal(const void *x, const void *y, const TypeInfo_t *info) { \ + (void)info; \ + return *(c_type *)x == *(c_type *)y; \ + } \ + public \ + CONSTFUNC bool KindOfInt##$is_between(const c_type x, const c_type low, const c_type high) { \ + return low <= x && x <= high; \ + } \ + public \ + CONSTFUNC c_type KindOfInt##$clamped(c_type x, c_type min, c_type max) { \ + return x < min ? min : (x > max ? max : x); \ + } \ + public \ + Text_t KindOfInt##$hex(c_type i, Int_t digits_int, bool uppercase, bool prefix) { \ + Int_t as_int = Int$from_int64((int64_t)i); \ + return Int$hex(as_int, digits_int, uppercase, prefix); \ + } \ + public \ + Text_t KindOfInt##$octal(c_type i, Int_t digits_int, bool prefix) { \ + Int_t as_int = Int$from_int64((int64_t)i); \ + return Int$octal(as_int, digits_int, prefix); \ + } \ + public \ + List_t KindOfInt##$bits(c_type x) { \ + List_t bit_list = (List_t){.data = GC_MALLOC_ATOMIC(sizeof(bool[8 * sizeof(c_type)])), \ + .atomic = 1, \ + .stride = sizeof(bool), \ + .length = 8 * sizeof(c_type)}; \ + bool *bits = bit_list.data + sizeof(c_type) * 8; \ + for (size_t i = 0; i < 8 * sizeof(c_type); i++) { \ + *(bits--) = x & 1; \ + x >>= 1; \ + } \ + return bit_list; \ + } \ + public \ + bool KindOfInt##$get_bit(c_type x, Int_t bit_index) { \ + if (Int$compare_value(bit_index, I(1)) < 0) fail("Invalid bit index (expected 1 or higher): ", bit_index); \ + if (Int$compare_value(bit_index, Int$from_int64(sizeof(c_type) * 8)) > 0) \ + fail("Bit index is too large! There are only ", (uint64_t)sizeof(c_type) * 8, \ + " bits, but index is: ", bit_index); \ + return ((x & (c_type)(1L << (Int64$from_int(bit_index, true) - 1L))) != 0); \ + } \ + typedef struct { \ + Optional##KindOfInt##_t current, last; \ + KindOfInt##_t step; \ + } KindOfInt##Range_t; \ + static Optional##KindOfInt##_t _next_##KindOfInt(KindOfInt##Range_t *info) { \ + Optional##KindOfInt##_t i = info->current; \ + if (!i.is_none) { \ + KindOfInt##_t next; \ + bool overflow = __builtin_add_overflow(i.value, info->step, &next); \ + if (overflow \ + || (!info->last.is_none && (info->step >= 0 ? next > info->last.value : next < info->last.value))) \ + info->current = (Optional##KindOfInt##_t){.is_none = true}; \ + else info->current = (Optional##KindOfInt##_t){.value = next}; \ + } \ + return i; \ + } \ + public \ + to_attr Closure_t KindOfInt##$to(c_type first, c_type last, Optional##KindOfInt##_t step) { \ + KindOfInt##Range_t *range = GC_MALLOC(sizeof(KindOfInt##Range_t)); \ + range->current = (Optional##KindOfInt##_t){.value = first}; \ + range->last = (Optional##KindOfInt##_t){.value = last}; \ + range->step = step.is_none ? (last >= first ? 1 : -1) : step.value; \ + return (Closure_t){.fn = _next_##KindOfInt, .userdata = range}; \ + } \ + public \ + to_attr Closure_t KindOfInt##$onward(c_type first, c_type step) { \ + KindOfInt##Range_t *range = GC_MALLOC(sizeof(KindOfInt##Range_t)); \ + range->current = (Optional##KindOfInt##_t){.value = first}; \ + range->last = (Optional##KindOfInt##_t){.is_none = true}; \ + range->step = step; \ + return (Closure_t){.fn = _next_##KindOfInt, .userdata = range}; \ + } \ + public \ + PUREFUNC Optional##KindOfInt##_t KindOfInt##$parse(Text_t text, Text_t *remainder) { \ + OptionalInt_t full_int = Int$parse(text, remainder); \ + if (full_int.small == 0L) return (Optional##KindOfInt##_t){.is_none = true}; \ + if (Int$compare_value(full_int, I(min_val)) < 0) { \ + return (Optional##KindOfInt##_t){.is_none = true}; \ + } \ + if (Int$compare_value(full_int, I(max_val)) > 0) { \ + return (Optional##KindOfInt##_t){.is_none = true}; \ + } \ + return (Optional##KindOfInt##_t){.value = KindOfInt##$from_int(full_int, true)}; \ + } \ + public \ + CONSTFUNC c_type KindOfInt##$gcd(c_type x, c_type y) { \ + if (x == 0 || y == 0) return 0; \ + x = KindOfInt##$abs(x); \ + y = KindOfInt##$abs(y); \ + while (x != y) { \ + if (x > y) x -= y; \ + else y -= x; \ + } \ + return x; \ + } \ + public \ + const c_type KindOfInt##$min = min_val; \ + public \ + const c_type KindOfInt##$max = max_val; \ + public \ + const TypeInfo_t KindOfInt##$info = { \ + .size = sizeof(c_type), \ + .align = __alignof__(c_type), \ + .metamethods = \ + { \ + .compare = KindOfInt##$compare, \ + .as_text = KindOfInt##$as_text, \ + .serialize = KindOfInt##$serialize, \ + .deserialize = KindOfInt##$deserialize, \ + }, \ }; -DEFINE_INT_TYPE(int64_t, Int64, INT64_MIN, INT64_MAX, __attribute__(())) -DEFINE_INT_TYPE(int32_t, Int32, INT32_MIN, INT32_MAX, CONSTFUNC) -DEFINE_INT_TYPE(int16_t, Int16, INT16_MIN, INT16_MAX, CONSTFUNC) -DEFINE_INT_TYPE(int8_t, Int8, INT8_MIN, INT8_MAX, CONSTFUNC) +DEFINE_INT_TYPE(int64_t, Int64, INT64_MIN, INT64_MAX, __attribute__(())) +DEFINE_INT_TYPE(int32_t, Int32, INT32_MIN, INT32_MAX, CONSTFUNC) +DEFINE_INT_TYPE(int16_t, Int16, INT16_MIN, INT16_MAX, CONSTFUNC) +DEFINE_INT_TYPE(int8_t, Int8, INT8_MIN, INT8_MAX, CONSTFUNC) #undef DEFINE_INT_TYPE // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/integers.h b/src/stdlib/integers.h index 744992dd..a7249495 100644 --- a/src/stdlib/integers.h +++ b/src/stdlib/integers.h @@ -2,9 +2,9 @@ // Integer type infos and methods +#include <gmp.h> #include <stdbool.h> #include <stdint.h> -#include <gmp.h> #include "datatypes.h" #include "stdlib.h" @@ -16,74 +16,72 @@ #define I16(x) ((int16_t)x) #define I8(x) ((int8_t)x) -#define DEFINE_INT_TYPE(c_type, type_name) \ - typedef struct { \ - c_type value; \ - bool is_none:1; \ - } Optional ## type_name ## _t; \ - Text_t type_name ## $as_text(const void *i, bool colorize, const TypeInfo_t *type); \ - PUREFUNC int32_t type_name ## $compare(const void *x, const void *y, const TypeInfo_t *type); \ - PUREFUNC bool type_name ## $equal(const void *x, const void *y, const TypeInfo_t *type); \ - Text_t type_name ## $hex(c_type i, Int_t digits, bool uppercase, bool prefix); \ - Text_t type_name ## $octal(c_type i, Int_t digits, bool prefix); \ - List_t type_name ## $bits(c_type x); \ - bool type_name ## $get_bit(c_type x, Int_t bit_index); \ - Closure_t type_name ## $to(c_type first, c_type last, Optional ## type_name ## _t step); \ - Closure_t type_name ## $onward(c_type first, c_type step); \ - PUREFUNC Optional ## type_name ## _t type_name ## $parse(Text_t text, Text_t *remainder); \ - CONSTFUNC bool type_name ## $is_between(const c_type x, const c_type low, const c_type high); \ - CONSTFUNC c_type type_name ## $clamped(c_type x, c_type min, c_type max); \ - MACROLIKE CONSTFUNC c_type type_name ## $from_byte(Byte_t b) { return (c_type)b; } \ - MACROLIKE CONSTFUNC c_type type_name ## $from_bool(Bool_t b) { return (c_type)b; } \ - CONSTFUNC c_type type_name ## $gcd(c_type x, c_type y); \ - extern const c_type type_name ## $min, type_name##$max; \ - extern const TypeInfo_t type_name ## $info; \ - MACROLIKE c_type type_name ## $divided_by(c_type D, c_type d) { \ - c_type q = D/d, r = D%d; \ - q -= (r < 0) * (2*(d > 0) - 1); \ - return q; \ - } \ - MACROLIKE c_type type_name ## $modulo(c_type D, c_type d) { \ - c_type r = D%d; \ - r -= (r < 0) * (2*(d < 0) - 1) * d; \ - return r; \ - } \ - MACROLIKE c_type type_name ## $modulo1(c_type D, c_type d) { \ - return type_name ## $modulo(D-1, d) + 1; \ - } \ - MACROLIKE PUREFUNC c_type type_name ## $wrapping_plus(c_type x, c_type y) { \ - return (c_type)((u##c_type)x + (u##c_type)y); \ - } \ - MACROLIKE PUREFUNC c_type type_name ## $wrapping_minus(c_type x, c_type y) { \ - return (c_type)((u##c_type)x + (u##c_type)y); \ - } \ - MACROLIKE PUREFUNC c_type type_name ## $unsigned_left_shifted(c_type x, c_type y) { \ - return (c_type)((u##c_type)x << y); \ - } \ - MACROLIKE PUREFUNC c_type type_name ## $unsigned_right_shifted(c_type x, c_type y) { \ - return (c_type)((u##c_type)x >> y); \ +#define DEFINE_INT_TYPE(c_type, type_name) \ + typedef struct { \ + c_type value; \ + bool is_none : 1; \ + } Optional##type_name##_t; \ + Text_t type_name##$as_text(const void *i, bool colorize, const TypeInfo_t *type); \ + PUREFUNC int32_t type_name##$compare(const void *x, const void *y, const TypeInfo_t *type); \ + PUREFUNC bool type_name##$equal(const void *x, const void *y, const TypeInfo_t *type); \ + Text_t type_name##$hex(c_type i, Int_t digits, bool uppercase, bool prefix); \ + Text_t type_name##$octal(c_type i, Int_t digits, bool prefix); \ + List_t type_name##$bits(c_type x); \ + bool type_name##$get_bit(c_type x, Int_t bit_index); \ + Closure_t type_name##$to(c_type first, c_type last, Optional##type_name##_t step); \ + Closure_t type_name##$onward(c_type first, c_type step); \ + PUREFUNC Optional##type_name##_t type_name##$parse(Text_t text, Text_t *remainder); \ + CONSTFUNC bool type_name##$is_between(const c_type x, const c_type low, const c_type high); \ + CONSTFUNC c_type type_name##$clamped(c_type x, c_type min, c_type max); \ + MACROLIKE CONSTFUNC c_type type_name##$from_byte(Byte_t b) { return (c_type)b; } \ + MACROLIKE CONSTFUNC c_type type_name##$from_bool(Bool_t b) { return (c_type)b; } \ + CONSTFUNC c_type type_name##$gcd(c_type x, c_type y); \ + extern const c_type type_name##$min, type_name##$max; \ + extern const TypeInfo_t type_name##$info; \ + MACROLIKE c_type type_name##$divided_by(c_type D, c_type d) { \ + c_type q = D / d, r = D % d; \ + q -= (r < 0) * (2 * (d > 0) - 1); \ + return q; \ + } \ + MACROLIKE c_type type_name##$modulo(c_type D, c_type d) { \ + c_type r = D % d; \ + r -= (r < 0) * (2 * (d < 0) - 1) * d; \ + return r; \ + } \ + MACROLIKE c_type type_name##$modulo1(c_type D, c_type d) { return type_name##$modulo(D - 1, d) + 1; } \ + MACROLIKE PUREFUNC c_type type_name##$wrapping_plus(c_type x, c_type y) { \ + return (c_type)((u##c_type)x + (u##c_type)y); \ + } \ + MACROLIKE PUREFUNC c_type type_name##$wrapping_minus(c_type x, c_type y) { \ + return (c_type)((u##c_type)x + (u##c_type)y); \ + } \ + MACROLIKE PUREFUNC c_type type_name##$unsigned_left_shifted(c_type x, c_type y) { \ + return (c_type)((u##c_type)x << y); \ + } \ + MACROLIKE PUREFUNC c_type type_name##$unsigned_right_shifted(c_type x, c_type y) { \ + return (c_type)((u##c_type)x >> y); \ } DEFINE_INT_TYPE(int64_t, Int64) DEFINE_INT_TYPE(int32_t, Int32) DEFINE_INT_TYPE(int16_t, Int16) -DEFINE_INT_TYPE(int8_t, Int8) +DEFINE_INT_TYPE(int8_t, Int8) #undef DEFINE_INT_TYPE -#define NONE_INT64 ((OptionalInt64_t){.is_none=true}) -#define NONE_INT32 ((OptionalInt32_t){.is_none=true}) -#define NONE_INT16 ((OptionalInt16_t){.is_none=true}) -#define NONE_INT8 ((OptionalInt8_t){.is_none=true}) +#define NONE_INT64 ((OptionalInt64_t){.is_none = true}) +#define NONE_INT32 ((OptionalInt32_t){.is_none = true}) +#define NONE_INT16 ((OptionalInt16_t){.is_none = true}) +#define NONE_INT8 ((OptionalInt8_t){.is_none = true}) #define Int64$abs(...) I64(labs(__VA_ARGS__)) #define Int32$abs(...) I32(abs(__VA_ARGS__)) #define Int16$abs(...) I16(abs(__VA_ARGS__)) #define Int8$abs(...) I8(abs(__VA_ARGS__)) -void Int64$serialize(const void *obj, FILE *out, Table_t*, const TypeInfo_t*); -void Int64$deserialize(FILE *in, void *outval, List_t*, const TypeInfo_t*); -void Int32$serialize(const void *obj, FILE *out, Table_t*, const TypeInfo_t*); -void Int32$deserialize(FILE *in, void *outval, List_t*, const TypeInfo_t*); +void Int64$serialize(const void *obj, FILE *out, Table_t *, const TypeInfo_t *); +void Int64$deserialize(FILE *in, void *outval, List_t *, const TypeInfo_t *); +void Int32$serialize(const void *obj, FILE *out, Table_t *, const TypeInfo_t *); +void Int32$deserialize(FILE *in, void *outval, List_t *, const TypeInfo_t *); Text_t Int$as_text(const void *i, bool colorize, const TypeInfo_t *type); Text_t Int$value_as_text(Int_t i); @@ -109,19 +107,17 @@ bool Int$get_bit(Int_t x, Int_t bit_index); #define BIGGEST_SMALL_INT 0x3fffffff #define SMALLEST_SMALL_INT -0x40000000 -#define Int$from_mpz(mpz) (\ - mpz_cmpabs_ui(mpz, BIGGEST_SMALL_INT) <= 0 ? ( \ - (Int_t){.small=(mpz_get_si(mpz)<<2L)|1L} \ - ) : ( \ - (Int_t){.big=memcpy(new(mpz_t), &mpz, sizeof(mpz_t))} \ - )) +#define Int$from_mpz(mpz) \ + (mpz_cmpabs_ui(mpz, BIGGEST_SMALL_INT) <= 0 ? ((Int_t){.small = (mpz_get_si(mpz) << 2L) | 1L}) \ + : ((Int_t){.big = memcpy(new (mpz_t), &mpz, sizeof(mpz_t))})) -#define mpz_init_set_int(mpz, i) do { \ - if likely ((i).small & 1L) mpz_init_set_si(mpz, (i).small >> 2L); \ - else mpz_init_set(mpz, *(i).big); \ -} while (0) +#define mpz_init_set_int(mpz, i) \ + do { \ + if likely ((i).small & 1L) mpz_init_set_si(mpz, (i).small >> 2L); \ + else mpz_init_set(mpz, *(i).big); \ + } while (0) -#define I_small(i) ((Int_t){.small=(int64_t)((uint64_t)(i)<<2L)|1L}) +#define I_small(i) ((Int_t){.small = (int64_t)((uint64_t)(i) << 2L) | 1L}) #define I(i) _Generic(i, int8_t: I_small(i), int16_t: I_small(i), default: Int$from_int64(i)) #define I_is_zero(i) ((i).small == 1L) @@ -155,116 +151,107 @@ extern const TypeInfo_t Int$info; MACROLIKE Int_t Int$plus(Int_t x, Int_t y) { const int64_t z = (int64_t)((uint64_t)x.small + (uint64_t)y.small); - if likely ((z|2L) == (int32_t)z) - return (Int_t){.small=(z-1L)}; + if likely ((z | 2L) == (int32_t)z) return (Int_t){.small = (z - 1L)}; return Int$slow_plus(x, y); } MACROLIKE Int_t Int$minus(Int_t x, Int_t y) { const int64_t z = (int64_t)(((uint64_t)x.small ^ 3L) - (uint64_t)y.small); - if likely ((z & ~2L) == (int32_t)z) - return (Int_t){.small=z}; + if likely ((z & ~2L) == (int32_t)z) return (Int_t){.small = z}; return Int$slow_minus(x, y); } MACROLIKE Int_t Int$times(Int_t x, Int_t y) { if likely ((x.small & y.small) & 1L) { - const int64_t z = (x.small>>1L) * (y.small>>1L); - if likely (z == (int32_t)z) - return (Int_t){.small=z+1L}; + const int64_t z = (x.small >> 1L) * (y.small >> 1L); + if likely (z == (int32_t)z) return (Int_t){.small = z + 1L}; } return Int$slow_times(x, y); } MACROLIKE Int_t Int$divided_by(Int_t x, Int_t y) { if likely (x.small & y.small & 1L) { - // Euclidean division, see: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf - const int64_t D = (x.small>>2L); - const int64_t d = (y.small>>2L); - int64_t q = D/d, r = D%d; - q -= (r < 0L) * (2L*(d > 0L) - 1L); - if likely (q == (int32_t)q) - return (Int_t){.small=(q<<2L)|1L}; + // Euclidean division, see: + // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf + const int64_t D = (x.small >> 2L); + const int64_t d = (y.small >> 2L); + int64_t q = D / d, r = D % d; + q -= (r < 0L) * (2L * (d > 0L) - 1L); + if likely (q == (int32_t)q) return (Int_t){.small = (q << 2L) | 1L}; } return Int$slow_divided_by(x, y); } MACROLIKE Int_t Int$modulo(Int_t x, Int_t y) { if likely (x.small & y.small & 1L) { - // Euclidean modulus, see: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf - const int64_t D = (x.small>>2L); - const int64_t d = (y.small>>2L); - int64_t r = D%d; - r -= (r < 0L) * (2L*(d < 0L) - 1L) * d; - return (Int_t){.small=(r<<2L)|1L}; + // Euclidean modulus, see: + // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf + const int64_t D = (x.small >> 2L); + const int64_t d = (y.small >> 2L); + int64_t r = D % d; + r -= (r < 0L) * (2L * (d < 0L) - 1L) * d; + return (Int_t){.small = (r << 2L) | 1L}; } return Int$slow_modulo(x, y); } MACROLIKE Int_t Int$modulo1(Int_t x, Int_t y) { if likely (x.small & y.small & 1L) { - // Euclidean modulus, see: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf - const int64_t D = (x.small>>2L)-1L; - const int64_t d = (y.small>>2L); - int64_t r = D%d; - r -= (r < 0L) * (2L*(d < 0L) - 1L) * d; - return (Int_t){.small=((r+1L)<<2L)|1L}; + // Euclidean modulus, see: + // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf + const int64_t D = (x.small >> 2L) - 1L; + const int64_t d = (y.small >> 2L); + int64_t r = D % d; + r -= (r < 0L) * (2L * (d < 0L) - 1L) * d; + return (Int_t){.small = ((r + 1L) << 2L) | 1L}; } return Int$slow_modulo1(x, y); } MACROLIKE Int_t Int$left_shifted(Int_t x, Int_t y) { if likely (x.small & y.small & 1L) { - const int64_t z = ((x.small>>2L) << (y.small>>2L))<<2L; - if likely (z == (int32_t)z) - return (Int_t){.small=z+1L}; + const int64_t z = ((x.small >> 2L) << (y.small >> 2L)) << 2L; + if likely (z == (int32_t)z) return (Int_t){.small = z + 1L}; } return Int$slow_left_shifted(x, y); } MACROLIKE Int_t Int$right_shifted(Int_t x, Int_t y) { if likely (x.small & y.small & 1L) { - const int64_t z = ((x.small>>2L) >> (y.small>>2L))<<2L; - if likely (z == (int32_t)z) - return (Int_t){.small=z+1L}; + const int64_t z = ((x.small >> 2L) >> (y.small >> 2L)) << 2L; + if likely (z == (int32_t)z) return (Int_t){.small = z + 1L}; } return Int$slow_right_shifted(x, y); } MACROLIKE Int_t Int$bit_and(Int_t x, Int_t y) { const int64_t z = x.small & y.small; - if likely (z & 1L) - return (Int_t){.small=z}; + if likely (z & 1L) return (Int_t){.small = z}; return Int$slow_bit_and(x, y); } MACROLIKE Int_t Int$bit_or(Int_t x, Int_t y) { - if likely (x.small & y.small & 1L) - return (Int_t){.small=(x.small | y.small)}; + if likely (x.small & y.small & 1L) return (Int_t){.small = (x.small | y.small)}; return Int$slow_bit_or(x, y); } MACROLIKE Int_t Int$bit_xor(Int_t x, Int_t y) { - if likely (x.small & y.small & 1L) - return (Int_t){.small=(x.small ^ y.small) | 1L}; + if likely (x.small & y.small & 1L) return (Int_t){.small = (x.small ^ y.small) | 1L}; return Int$slow_bit_xor(x, y); } MACROLIKE Int_t Int$negated(Int_t x) { - if likely (x.small & 1L) - return (Int_t){.small=(~x.small) ^ 3L}; + if likely (x.small & 1L) return (Int_t){.small = (~x.small) ^ 3L}; return Int$slow_negated(x); } MACROLIKE Int_t Int$negative(Int_t x) { - if likely (x.small & 1L) - return (Int_t){.small=((-((x.small)>>2L))<<2L) | 1L}; + if likely (x.small & 1L) return (Int_t){.small = ((-((x.small) >> 2L)) << 2L) | 1L}; return Int$slow_negative(x); } MACROLIKE PUREFUNC bool Int$is_negative(Int_t x) { - if likely (x.small & 1L) - return x.small < 0L; + if likely (x.small & 1L) return x.small < 0L; return Int$compare_value(x, I_small(0)) < 0L; } @@ -278,42 +265,36 @@ MACROLIKE PUREFUNC bool Int$is_negative(Int_t x) { MACROLIKE PUREFUNC Int_t Int$from_num(double n, bool truncate) { mpz_t result; mpz_init_set_d(result, n); - if (!truncate && unlikely(mpz_get_d(result) != n)) - fail("Could not convert to an integer without truncation: ", n); + if (!truncate && unlikely(mpz_get_d(result) != n)) fail("Could not convert to an integer without truncation: ", n); return Int$from_mpz(result); } MACROLIKE PUREFUNC Int_t Int$from_num32(float n, bool truncate) { return Int$from_num((double)n, truncate); } MACROLIKE Int_t Int$from_int64(int64_t i) { - if likely (i >= SMALLEST_SMALL_INT && i <= BIGGEST_SMALL_INT) - return (Int_t){.small=(i<<2L)|1L}; + if likely (i >= SMALLEST_SMALL_INT && i <= BIGGEST_SMALL_INT) return (Int_t){.small = (i << 2L) | 1L}; mpz_t result; mpz_init_set_si(result, i); return Int$from_mpz(result); } MACROLIKE CONSTFUNC Int_t Int$from_int32(Int32_t i) { return Int$from_int64((Int32_t)i); } MACROLIKE CONSTFUNC Int_t Int$from_int16(Int16_t i) { return I_small(i); } -MACROLIKE CONSTFUNC Int_t Int$from_int8(Int8_t i) { return I_small(i); } +MACROLIKE CONSTFUNC Int_t Int$from_int8(Int8_t i) { return I_small(i); } MACROLIKE CONSTFUNC Int_t Int$from_byte(Byte_t b) { return I_small(b); } MACROLIKE CONSTFUNC Int_t Int$from_bool(Bool_t b) { return I_small(b); } // Int64 constructors MACROLIKE PUREFUNC Int64_t Int64$from_num(Num_t n, bool truncate) { int64_t i64 = (int64_t)n; - if (!truncate && unlikely((Num_t)i64 != n)) - fail("Could not convert Num to Int64 without truncation: ", n); + if (!truncate && unlikely((Num_t)i64 != n)) fail("Could not convert Num to Int64 without truncation: ", n); return i64; } MACROLIKE PUREFUNC Int64_t Int64$from_num32(Num32_t n, bool truncate) { int64_t i64 = (int64_t)n; - if (!truncate && unlikely((Num32_t)i64 != n)) - fail("Could not convert Num32 to Int64 without truncation: ", n); + if (!truncate && unlikely((Num32_t)i64 != n)) fail("Could not convert Num32 to Int64 without truncation: ", n); return i64; } MACROLIKE PUREFUNC Int64_t Int64$from_int(Int_t i, bool truncate) { - if likely (i.small & 1L) - return (int64_t)(i.small >> 2L); - if (!truncate && unlikely(!mpz_fits_slong_p(*i.big))) - fail("Integer is too big to fit in a 64-bit integer: ", i); + if likely (i.small & 1L) return (int64_t)(i.small >> 2L); + if (!truncate && unlikely(!mpz_fits_slong_p(*i.big))) fail("Integer is too big to fit in a 64-bit integer: ", i); return mpz_get_si(*i.big); } MACROLIKE CONSTFUNC Int64_t Int64$from_int32(Int32_t i) { return (Int64_t)i; } @@ -323,27 +304,23 @@ MACROLIKE CONSTFUNC Int64_t Int64$from_int8(Int8_t i) { return (Int64_t)i; } // Int32 constructors MACROLIKE PUREFUNC Int32_t Int32$from_num(Num_t n, bool truncate) { int32_t i32 = (int32_t)n; - if (!truncate && unlikely((Num_t)i32 != n)) - fail("Could not convert Num to Int32 without truncation: ", n); + if (!truncate && unlikely((Num_t)i32 != n)) fail("Could not convert Num to Int32 without truncation: ", n); return i32; } MACROLIKE PUREFUNC Int32_t Int32$from_num32(Num32_t n, bool truncate) { int32_t i32 = (int32_t)n; - if (!truncate && unlikely((Num32_t)i32 != n)) - fail("Could not convert Num32 to Int32 without truncation: ", n); + if (!truncate && unlikely((Num32_t)i32 != n)) fail("Could not convert Num32 to Int32 without truncation: ", n); return i32; } MACROLIKE PUREFUNC Int32_t Int32$from_int(Int_t i, bool truncate) { int64_t i64 = Int64$from_int(i, truncate); int32_t i32 = (int32_t)i64; - if (!truncate && unlikely((int64_t)i32 != i64)) - fail("Integer is too big to fit in a 32-bit integer: ", i); + if (!truncate && unlikely((int64_t)i32 != i64)) fail("Integer is too big to fit in a 32-bit integer: ", i); return i32; } MACROLIKE PUREFUNC Int32_t Int32$from_int64(Int64_t i64, bool truncate) { int32_t i32 = (int32_t)i64; - if (!truncate && unlikely((int64_t)i32 != i64)) - fail("Integer is too big to fit in a 32-bit integer: ", i64); + if (!truncate && unlikely((int64_t)i32 != i64)) fail("Integer is too big to fit in a 32-bit integer: ", i64); return i32; } MACROLIKE CONSTFUNC Int32_t Int32$from_int16(Int16_t i) { return (Int32_t)i; } @@ -352,8 +329,7 @@ MACROLIKE CONSTFUNC Int32_t Int32$from_int8(Int8_t i) { return (Int32_t)i; } // Int16 constructors MACROLIKE PUREFUNC Int16_t Int16$from_num(Num_t n, bool truncate) { int16_t i16 = (int16_t)n; - if (!truncate && unlikely((Num_t)i16 != n)) - fail("Could not convert Num to Int16 without truncation: ", n); + if (!truncate && unlikely((Num_t)i16 != n)) fail("Could not convert Num to Int16 without truncation: ", n); return i16; } MACROLIKE PUREFUNC Int16_t Int16$from_num32(Num32_t n, bool truncate) { @@ -365,20 +341,17 @@ MACROLIKE PUREFUNC Int16_t Int16$from_num32(Num32_t n, bool truncate) { MACROLIKE PUREFUNC Int16_t Int16$from_int(Int_t i, bool truncate) { int64_t i64 = Int64$from_int(i, truncate); int16_t i16 = (int16_t)i64; - if (!truncate && unlikely((int64_t)i16 != i64)) - fail("Integer is too big to fit in a 16-bit integer!"); + if (!truncate && unlikely((int64_t)i16 != i64)) fail("Integer is too big to fit in a 16-bit integer!"); return i16; } MACROLIKE PUREFUNC Int16_t Int16$from_int64(Int64_t i64, bool truncate) { int16_t i16 = (int16_t)i64; - if (!truncate && unlikely((int64_t)i16 != i64)) - fail("Integer is too big to fit in a 16-bit integer: ", i64); + if (!truncate && unlikely((int64_t)i16 != i64)) fail("Integer is too big to fit in a 16-bit integer: ", i64); return i16; } MACROLIKE PUREFUNC Int16_t Int16$from_int32(Int32_t i32, bool truncate) { int16_t i16 = (int16_t)i32; - if (!truncate && unlikely((int32_t)i16 != i32)) - fail("Integer is too big to fit in a 16-bit integer: ", i32); + if (!truncate && unlikely((int32_t)i16 != i32)) fail("Integer is too big to fit in a 16-bit integer: ", i32); return i16; } MACROLIKE CONSTFUNC Int16_t Int16$from_int8(Int8_t i) { return (Int16_t)i; } @@ -386,39 +359,33 @@ MACROLIKE CONSTFUNC Int16_t Int16$from_int8(Int8_t i) { return (Int16_t)i; } // Int8 constructors MACROLIKE PUREFUNC Int8_t Int8$from_num(Num_t n, bool truncate) { int8_t i8 = (int8_t)n; - if (!truncate && unlikely((Num_t)i8 != n)) - fail("Could not convert Num to Int8 without truncation: ", n); + if (!truncate && unlikely((Num_t)i8 != n)) fail("Could not convert Num to Int8 without truncation: ", n); return i8; } MACROLIKE PUREFUNC Int8_t Int8$from_num32(Num32_t n, bool truncate) { int8_t i8 = (int8_t)n; - if (!truncate && unlikely((Num32_t)i8 != n)) - fail("Could not convert Num32 to Int8 without truncation: ", n); + if (!truncate && unlikely((Num32_t)i8 != n)) fail("Could not convert Num32 to Int8 without truncation: ", n); return i8; } MACROLIKE PUREFUNC Int8_t Int8$from_int(Int_t i, bool truncate) { int64_t i64 = Int64$from_int(i, truncate); int8_t i8 = (int8_t)i64; - if (!truncate && unlikely((int64_t)i8 != i64)) - fail("Integer is too big to fit in an 8-bit integer!"); + if (!truncate && unlikely((int64_t)i8 != i64)) fail("Integer is too big to fit in an 8-bit integer!"); return i8; } MACROLIKE PUREFUNC Int8_t Int8$from_int64(Int64_t i64, bool truncate) { int8_t i8 = (int8_t)i64; - if (!truncate && unlikely((int64_t)i8 != i64)) - fail("Integer is too big to fit in a 8-bit integer: ", i64); + if (!truncate && unlikely((int64_t)i8 != i64)) fail("Integer is too big to fit in a 8-bit integer: ", i64); return i8; } MACROLIKE PUREFUNC Int8_t Int8$from_int32(Int32_t i32, bool truncate) { int8_t i8 = (int8_t)i32; - if (!truncate && unlikely((int32_t)i8 != i32)) - fail("Integer is too big to fit in a 8-bit integer: ", i32); + if (!truncate && unlikely((int32_t)i8 != i32)) fail("Integer is too big to fit in a 8-bit integer: ", i32); return i8; } MACROLIKE PUREFUNC Int8_t Int8$from_int16(Int16_t i16, bool truncate) { int8_t i8 = (int8_t)i16; - if (!truncate && unlikely((int16_t)i8 != i16)) - fail("Integer is too big to fit in a 8-bit integer: ", i16); + if (!truncate && unlikely((int16_t)i8 != i16)) fail("Integer is too big to fit in a 8-bit integer: ", i16); return i8; } #ifdef __GNUC__ diff --git a/src/stdlib/lists.c b/src/stdlib/lists.c index ce27f822..415e28a5 100644 --- a/src/stdlib/lists.c +++ b/src/stdlib/lists.c @@ -6,8 +6,8 @@ #include <stdint.h> #include <sys/param.h> -#include "lists.h" #include "integers.h" +#include "lists.h" #include "math.h" #include "metamethods.h" #include "optionals.h" @@ -18,39 +18,37 @@ // Use inline version of siphash code: #include "siphash-internals.h" -PUREFUNC static INLINE int64_t get_padded_item_size(const TypeInfo_t *info) -{ +PUREFUNC static INLINE int64_t get_padded_item_size(const TypeInfo_t *info) { int64_t size = info->ListInfo.item->size; - if (info->ListInfo.item->align > 1 && size % info->ListInfo.item->align) - errx(1, "Item size is not padded!"); + if (info->ListInfo.item->align > 1 && size % info->ListInfo.item->align) errx(1, "Item size is not padded!"); return size; } // Replace the list's .data pointer with a new pointer to a copy of the // data that is compacted and has a stride of exactly `padded_item_size` -public void List$compact(List_t *list, int64_t padded_item_size) -{ +public +void List$compact(List_t *list, int64_t padded_item_size) { void *copy = NULL; if (list->length > 0) { copy = list->atomic ? GC_MALLOC_ATOMIC((size_t)list->length * (size_t)padded_item_size) - : GC_MALLOC((size_t)list->length * (size_t)padded_item_size); + : GC_MALLOC((size_t)list->length * (size_t)padded_item_size); if ((int64_t)list->stride == padded_item_size) { memcpy(copy, list->data, (size_t)list->length * (size_t)padded_item_size); } else { for (int64_t i = 0; i < list->length; i++) - memcpy(copy + i*padded_item_size, list->data + list->stride*i, (size_t)padded_item_size); + memcpy(copy + i * padded_item_size, list->data + list->stride * i, (size_t)padded_item_size); } } *list = (List_t){ - .data=copy, - .length=list->length, - .stride=padded_item_size, - .atomic=list->atomic, + .data = copy, + .length = list->length, + .stride = padded_item_size, + .atomic = list->atomic, }; } -public void List$insert(List_t *list, const void *item, Int_t int_index, int64_t padded_item_size) -{ +public +void List$insert(List_t *list, const void *item, Int_t int_index, int64_t padded_item_size) { int64_t index = Int64$from_int(int_index, false); if (index <= 0) index = list->length + index + 1; @@ -61,42 +59,38 @@ public void List$insert(List_t *list, const void *item, Int_t int_index, int64_t if (!list->data) { list->free = 4; list->data = list->atomic ? GC_MALLOC_ATOMIC((size_t)list->free * (size_t)padded_item_size) - : GC_MALLOC((size_t)list->free * (size_t)padded_item_size); + : GC_MALLOC((size_t)list->free * (size_t)padded_item_size); list->stride = padded_item_size; } else if (list->free < 1 || list->data_refcount != 0 || (int64_t)list->stride != padded_item_size) { // Resize policy: +50% growth (clamped between 8 and LIST_MAX_FREE_ENTRIES) - list->free = MIN(LIST_MAX_FREE_ENTRIES, MAX(8, list->length)/2); + list->free = MIN(LIST_MAX_FREE_ENTRIES, MAX(8, list->length) / 2); void *copy = list->atomic ? GC_MALLOC_ATOMIC((size_t)(list->length + list->free) * (size_t)padded_item_size) - : GC_MALLOC((size_t)(list->length + list->free) * (size_t)padded_item_size); - for (int64_t i = 0; i < index-1; i++) - memcpy(copy + i*padded_item_size, list->data + list->stride*i, (size_t)padded_item_size); - for (int64_t i = index-1; i < (int64_t)list->length; i++) - memcpy(copy + (i+1)*padded_item_size, list->data + list->stride*i, (size_t)padded_item_size); + : GC_MALLOC((size_t)(list->length + list->free) * (size_t)padded_item_size); + for (int64_t i = 0; i < index - 1; i++) + memcpy(copy + i * padded_item_size, list->data + list->stride * i, (size_t)padded_item_size); + for (int64_t i = index - 1; i < (int64_t)list->length; i++) + memcpy(copy + (i + 1) * padded_item_size, list->data + list->stride * i, (size_t)padded_item_size); list->data = copy; list->data_refcount = 0; list->stride = padded_item_size; } else { - if (index != list->length+1) { + if (index != list->length + 1) { assert(list->length >= index); - size_t size = (size_t)((list->length - index + 1)*padded_item_size); + size_t size = (size_t)((list->length - index + 1) * padded_item_size); assert(size < SIZE_MAX); - memmove( - list->data + index*padded_item_size, - list->data + (index-1)*padded_item_size, - size); + memmove(list->data + index * padded_item_size, list->data + (index - 1) * padded_item_size, size); } } assert(list->free > 0); --list->free; ++list->length; - memcpy((void*)list->data + (index-1)*padded_item_size, item, (size_t)padded_item_size); + memcpy((void *)list->data + (index - 1) * padded_item_size, item, (size_t)padded_item_size); } -public void List$insert_all(List_t *list, List_t to_insert, Int_t int_index, int64_t padded_item_size) -{ +public +void List$insert_all(List_t *list, List_t to_insert, Int_t int_index, int64_t padded_item_size) { int64_t index = Int64$from_int(int_index, false); - if (to_insert.length == 0) - return; + if (to_insert.length == 0) return; if (!list->data) { *list = to_insert; @@ -111,34 +105,33 @@ public void List$insert_all(List_t *list, List_t to_insert, Int_t int_index, int fail("Invalid insertion index ", index, " for a list with length ", (int64_t)list->length); if ((int64_t)list->free >= (int64_t)to_insert.length // Adequate free space - && list->data_refcount == 0 // Not aliased memory - && (int64_t)list->stride == padded_item_size) { // Contiguous list + && list->data_refcount == 0 // Not aliased memory + && (int64_t)list->stride == padded_item_size) { // Contiguous list // If we can fit this within the list's preallocated free space, do that: list->free -= to_insert.length; list->length += to_insert.length; - if (index != list->length+1) - memmove((void*)list->data + index*padded_item_size, - list->data + (index-1)*padded_item_size, - (size_t)((list->length - index + to_insert.length-1)*padded_item_size)); + if (index != list->length + 1) + memmove((void *)list->data + index * padded_item_size, list->data + (index - 1) * padded_item_size, + (size_t)((list->length - index + to_insert.length - 1) * padded_item_size)); for (int64_t i = 0; i < to_insert.length; i++) - memcpy((void*)list->data + (index-1 + i)*padded_item_size, - to_insert.data + i*to_insert.stride, (size_t)padded_item_size); + memcpy((void *)list->data + (index - 1 + i) * padded_item_size, to_insert.data + i * to_insert.stride, + (size_t)padded_item_size); } else { // Otherwise, allocate a new chunk of memory for the list and populate it: int64_t new_len = list->length + to_insert.length; - list->free = MIN(LIST_MAX_FREE_ENTRIES, MAX(8, new_len/4)); + list->free = MIN(LIST_MAX_FREE_ENTRIES, MAX(8, new_len / 4)); void *data = list->atomic ? GC_MALLOC_ATOMIC((size_t)((new_len + list->free) * padded_item_size)) - : GC_MALLOC((size_t)((new_len + list->free) * padded_item_size)); + : GC_MALLOC((size_t)((new_len + list->free) * padded_item_size)); void *p = data; // Copy first chunk of `list` if needed: if (index > 1) { if (list->stride == padded_item_size) { - memcpy(p, list->data, (size_t)((index-1)*padded_item_size)); - p += (index-1)*padded_item_size; + memcpy(p, list->data, (size_t)((index - 1) * padded_item_size)); + p += (index - 1) * padded_item_size; } else { - for (int64_t i = 0; i < index-1; i++) { - memcpy(p, list->data + list->stride*i, (size_t)padded_item_size); + for (int64_t i = 0; i < index - 1; i++) { + memcpy(p, list->data + list->stride * i, (size_t)padded_item_size); p += padded_item_size; } } @@ -146,11 +139,11 @@ public void List$insert_all(List_t *list, List_t to_insert, Int_t int_index, int // Copy `to_insert` if (to_insert.stride == padded_item_size) { - memcpy(p, to_insert.data, (size_t)(to_insert.length*padded_item_size)); - p += to_insert.length*padded_item_size; + memcpy(p, to_insert.data, (size_t)(to_insert.length * padded_item_size)); + p += to_insert.length * padded_item_size; } else { - for (int64_t i = 0; i < index-1; i++) { - memcpy(p, to_insert.data + to_insert.stride*i, (size_t)padded_item_size); + for (int64_t i = 0; i < index - 1; i++) { + memcpy(p, to_insert.data + to_insert.stride * i, (size_t)padded_item_size); p += padded_item_size; } } @@ -158,11 +151,12 @@ public void List$insert_all(List_t *list, List_t to_insert, Int_t int_index, int // Copy last chunk of `list` if needed: if (index < list->length + 1) { if (list->stride == padded_item_size) { - memcpy(p, list->data + padded_item_size*(index-1), (size_t)((list->length - index + 1)*padded_item_size)); - p += (list->length - index + 1)*padded_item_size; + memcpy(p, list->data + padded_item_size * (index - 1), + (size_t)((list->length - index + 1) * padded_item_size)); + p += (list->length - index + 1) * padded_item_size; } else { - for (int64_t i = index-1; i < list->length-1; i++) { - memcpy(p, list->data + list->stride*i, (size_t)padded_item_size); + for (int64_t i = index - 1; i < list->length - 1; i++) { + memcpy(p, list->data + list->stride * i, (size_t)padded_item_size); p += padded_item_size; } } @@ -174,27 +168,27 @@ public void List$insert_all(List_t *list, List_t to_insert, Int_t int_index, int } } -public void List$remove_at(List_t *list, Int_t int_index, Int_t int_count, int64_t padded_item_size) -{ +public +void List$remove_at(List_t *list, Int_t int_index, Int_t int_count, int64_t padded_item_size) { int64_t index = Int64$from_int(int_index, false); if (index < 1) index = list->length + index + 1; int64_t count = Int64$from_int(int_count, false); if (index < 1 || index > (int64_t)list->length || count < 1) return; - if (count > list->length - index + 1) - count = (list->length - index) + 1; + if (count > list->length - index + 1) count = (list->length - index) + 1; if (index == 1) { list->data += list->stride * count; } else if (index + count > list->length) { list->free += count; } else if (list->data_refcount != 0 || (int64_t)list->stride != padded_item_size) { - void *copy = list->atomic ? GC_MALLOC_ATOMIC((size_t)((list->length-1) * padded_item_size)) - : GC_MALLOC((size_t)((list->length-1) * padded_item_size)); + void *copy = list->atomic ? GC_MALLOC_ATOMIC((size_t)((list->length - 1) * padded_item_size)) + : GC_MALLOC((size_t)((list->length - 1) * padded_item_size)); for (int64_t src = 1, dest = 1; src <= (int64_t)list->length; src++) { if (src < index || src >= index + count) { - memcpy(copy + (dest - 1)*padded_item_size, list->data + list->stride*(src - 1), (size_t)padded_item_size); + memcpy(copy + (dest - 1) * padded_item_size, list->data + list->stride * (src - 1), + (size_t)padded_item_size); ++dest; } } @@ -202,26 +196,27 @@ public void List$remove_at(List_t *list, Int_t int_index, Int_t int_count, int64 list->free = 0; list->data_refcount = 0; } else { - memmove((void*)list->data + (index-1)*padded_item_size, list->data + (index-1 + count)*padded_item_size, - (size_t)((list->length - index + count - 1)*padded_item_size)); + memmove((void *)list->data + (index - 1) * padded_item_size, + list->data + (index - 1 + count) * padded_item_size, + (size_t)((list->length - index + count - 1) * padded_item_size)); list->free += count; } list->length -= count; if (list->length == 0) list->data = NULL; } -public void List$remove_item(List_t *list, void *item, Int_t max_removals, const TypeInfo_t *type) -{ +public +void List$remove_item(List_t *list, void *item, Int_t max_removals, const TypeInfo_t *type) { int64_t padded_item_size = get_padded_item_size(type); - const Int_t ZERO = (Int_t){.small=(0<<2)|1}; - const Int_t ONE = (Int_t){.small=(1<<2)|1}; + const Int_t ZERO = (Int_t){.small = (0 << 2) | 1}; + const Int_t ONE = (Int_t){.small = (1 << 2) | 1}; const TypeInfo_t *item_type = type->ListInfo.item; - for (int64_t i = 0; i < list->length; ) { + for (int64_t i = 0; i < list->length;) { if (max_removals.small == ZERO.small) // zero break; - if (generic_equal(item, list->data + i*list->stride, item_type)) { - List$remove_at(list, I(i+1), ONE, padded_item_size); + if (generic_equal(item, list->data + i * list->stride, item_type)) { + List$remove_at(list, I(i + 1), ONE, padded_item_size); max_removals = Int$minus(max_removals, ONE); } else { i++; @@ -229,45 +224,41 @@ public void List$remove_item(List_t *list, void *item, Int_t max_removals, const } } -public OptionalInt_t List$find(List_t list, void *item, const TypeInfo_t *type) -{ +public +OptionalInt_t List$find(List_t list, void *item, const TypeInfo_t *type) { const TypeInfo_t *item_type = type->ListInfo.item; for (int64_t i = 0; i < list.length; i++) { - if (generic_equal(item, list.data + i*list.stride, item_type)) - return I(i+1); + if (generic_equal(item, list.data + i * list.stride, item_type)) return I(i + 1); } return NONE_INT; } -public OptionalInt_t List$first(List_t list, Closure_t predicate) -{ - bool (*is_good)(void*, void*) = (void*)predicate.fn; +public +OptionalInt_t List$first(List_t list, Closure_t predicate) { + bool (*is_good)(void *, void *) = (void *)predicate.fn; for (int64_t i = 0; i < list.length; i++) { - if (is_good(list.data + i*list.stride, predicate.userdata)) - return I(i+1); + if (is_good(list.data + i * list.stride, predicate.userdata)) return I(i + 1); } return NONE_INT; } -static Closure_t _sort_comparison = {.fn=NULL}; +static Closure_t _sort_comparison = {.fn = NULL}; -int _compare_closure(const void *a, const void *b) -{ - typedef int (*comparison_t)(const void*, const void*, void*); +int _compare_closure(const void *a, const void *b) { + typedef int (*comparison_t)(const void *, const void *, void *); return ((comparison_t)_sort_comparison.fn)(a, b, _sort_comparison.userdata); } -public void List$sort(List_t *list, Closure_t comparison, int64_t padded_item_size) -{ - if (list->data_refcount != 0 || (int64_t)list->stride != padded_item_size) - List$compact(list, padded_item_size); +public +void List$sort(List_t *list, Closure_t comparison, int64_t padded_item_size) { + if (list->data_refcount != 0 || (int64_t)list->stride != padded_item_size) List$compact(list, padded_item_size); _sort_comparison = comparison; qsort(list->data, (size_t)list->length, (size_t)padded_item_size, _compare_closure); } -public List_t List$sorted(List_t list, Closure_t comparison, int64_t padded_item_size) -{ +public +List_t List$sorted(List_t list, Closure_t comparison, int64_t padded_item_size) { List$compact(&list, padded_item_size); _sort_comparison = comparison; qsort(list.data, (size_t)list.length, (size_t)padded_item_size, _compare_closure); @@ -283,13 +274,12 @@ static ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) { } #elif defined(__linux__) // Use getrandom() -# include <sys/random.h> +#include <sys/random.h> #else - #error "Unsupported platform for secure random number generation" +#error "Unsupported platform for secure random number generation" #endif -static int64_t _default_random_int64(int64_t min, int64_t max, void *userdata) -{ +static int64_t _default_random_int64(int64_t min, int64_t max, void *userdata) { (void)userdata; if (min > max) fail("Random minimum value (", min, ") is larger than the maximum value (", max, ")"); if (min == max) return min; @@ -303,50 +293,49 @@ static int64_t _default_random_int64(int64_t min, int64_t max, void *userdata) return (int64_t)((uint64_t)min + (r % range)); } -public void List$shuffle(List_t *list, OptionalClosure_t random_int64, int64_t padded_item_size) -{ - if (list->data_refcount != 0 || (int64_t)list->stride != padded_item_size) - List$compact(list, padded_item_size); +public +void List$shuffle(List_t *list, OptionalClosure_t random_int64, int64_t padded_item_size) { + if (list->data_refcount != 0 || (int64_t)list->stride != padded_item_size) List$compact(list, padded_item_size); - typedef int64_t (*rng_fn_t)(int64_t, int64_t, void*); + typedef int64_t (*rng_fn_t)(int64_t, int64_t, void *); rng_fn_t rng_fn = random_int64.fn ? (rng_fn_t)random_int64.fn : _default_random_int64; char tmp[padded_item_size]; - for (int64_t i = list->length-1; i > 1; i--) { + for (int64_t i = list->length - 1; i > 1; i--) { int64_t j = rng_fn(0, i, random_int64.userdata); - if unlikely (j < 0 || j > list->length-1) + if unlikely (j < 0 || j > list->length - 1) fail("The provided random number function returned an invalid value: ", j, " (not between 0 and ", i, ")"); - memcpy(tmp, list->data + i*padded_item_size, (size_t)padded_item_size); - memcpy((void*)list->data + i*padded_item_size, list->data + j*padded_item_size, (size_t)padded_item_size); - memcpy((void*)list->data + j*padded_item_size, tmp, (size_t)padded_item_size); + memcpy(tmp, list->data + i * padded_item_size, (size_t)padded_item_size); + memcpy((void *)list->data + i * padded_item_size, list->data + j * padded_item_size, (size_t)padded_item_size); + memcpy((void *)list->data + j * padded_item_size, tmp, (size_t)padded_item_size); } } -public List_t List$shuffled(List_t list, Closure_t random_int64, int64_t padded_item_size) -{ +public +List_t List$shuffled(List_t list, Closure_t random_int64, int64_t padded_item_size) { List$compact(&list, padded_item_size); List$shuffle(&list, random_int64, padded_item_size); return list; } -public void *List$random(List_t list, OptionalClosure_t random_int64) -{ - if (list.length == 0) - return NULL; // fail("Cannot get a random item from an empty list!"); +public +void *List$random(List_t list, OptionalClosure_t random_int64) { + if (list.length == 0) return NULL; // fail("Cannot get a random item from an empty list!"); - typedef int64_t (*rng_fn_t)(int64_t, int64_t, void*); + typedef int64_t (*rng_fn_t)(int64_t, int64_t, void *); rng_fn_t rng_fn = random_int64.fn ? (rng_fn_t)random_int64.fn : _default_random_int64; - int64_t index = rng_fn(0, list.length-1, random_int64.userdata); - if unlikely (index < 0 || index > list.length-1) - fail("The provided random number function returned an invalid value: ", index, " (not between 0 and ", (int64_t)list.length, ")"); - return list.data + list.stride*index; + int64_t index = rng_fn(0, list.length - 1, random_int64.userdata); + if unlikely (index < 0 || index > list.length - 1) + fail("The provided random number function returned an invalid value: ", index, " (not between 0 and ", + (int64_t)list.length, ")"); + return list.data + list.stride * index; } -public Table_t List$counts(List_t list, const TypeInfo_t *type) -{ +public +Table_t List$counts(List_t list, const TypeInfo_t *type) { Table_t counts = {}; const TypeInfo_t count_type = *Table$info(type->ListInfo.item, &Int$info); for (int64_t i = 0; i < list.length; i++) { - void *key = list.data + i*list.stride; + void *key = list.data + i * list.stride; int64_t *count = Table$get(counts, key, &count_type); int64_t val = count ? *count + 1 : 1; Table$set(&counts, key, &val, &count_type); @@ -354,14 +343,13 @@ public Table_t List$counts(List_t list, const TypeInfo_t *type) return counts; } -static double _default_random_num(void *userdata) -{ +static double _default_random_num(void *userdata) { (void)userdata; union { Num_t num; uint64_t bits; - } r = {.bits=0}, one = {.num=1.0}; - assert(getrandom((uint8_t*)&r, sizeof(r), 0) == sizeof(r)); + } r = {.bits = 0}, one = {.num = 1.0}; + assert(getrandom((uint8_t *)&r, sizeof(r), 0) == sizeof(r)); // Set r.num to 1.<random-bits> r.bits &= ~(0xFFFULL << 52); @@ -369,39 +357,30 @@ static double _default_random_num(void *userdata) return r.num - 1.0; } -public List_t List$sample(List_t list, Int_t int_n, List_t weights, OptionalClosure_t random_num, int64_t padded_item_size) -{ +public +List_t List$sample(List_t list, Int_t int_n, List_t weights, OptionalClosure_t random_num, int64_t padded_item_size) { int64_t n = Int64$from_int(int_n, false); - if (n < 0) - fail("Cannot select a negative number of values"); + if (n < 0) fail("Cannot select a negative number of values"); - if (n == 0) - return (List_t){}; + if (n == 0) return (List_t){}; - if (list.length == 0) - fail("There are no elements in this list!"); + if (list.length == 0) fail("There are no elements in this list!"); if (weights.length != list.length) fail("List has ", (int64_t)list.length, " elements, but there are ", (int64_t)weights.length, " weights given"); double total = 0.0; for (int64_t i = 0; i < weights.length && i < list.length; i++) { - double weight = *(double*)(weights.data + weights.stride*i); - if (isinf(weight)) - fail("Infinite weight!"); - else if (isnan(weight)) - fail("NaN weight!"); - else if (weight < 0.0) - fail("Negative weight!"); - else - total += weight; + double weight = *(double *)(weights.data + weights.stride * i); + if (isinf(weight)) fail("Infinite weight!"); + else if (isnan(weight)) fail("NaN weight!"); + else if (weight < 0.0) fail("Negative weight!"); + else total += weight; } - if (isinf(total)) - fail("Sample weights have overflowed to infinity"); + if (isinf(total)) fail("Sample weights have overflowed to infinity"); - if (total == 0.0) - fail("None of the given weights are nonzero"); + if (total == 0.0) fail("None of the given weights are nonzero"); double inverse_average = (double)list.length / total; @@ -411,7 +390,7 @@ public List_t List$sample(List_t list, Int_t int_n, List_t weights, OptionalClos } aliases[list.length]; for (int64_t i = 0; i < list.length; i++) { - double weight = i >= weights.length ? 0.0 : *(double*)(weights.data + weights.stride*i); + double weight = i >= weights.length ? 0.0 : *(double *)(weights.data + weights.stride * i); aliases[i].odds = weight * inverse_average; aliases[i].alias = -1; } @@ -435,16 +414,16 @@ public List_t List$sample(List_t list, Int_t int_n, List_t weights, OptionalClos } for (int64_t i = small; i < list.length; i++) - if (aliases[i].alias == -1) - aliases[i].alias = i; + if (aliases[i].alias == -1) aliases[i].alias = i; - typedef double (*rng_fn_t)(void*); + typedef double (*rng_fn_t)(void *); rng_fn_t rng_fn = random_num.fn ? (rng_fn_t)random_num.fn : _default_random_num; - List_t selected = { - .data=list.atomic ? GC_MALLOC_ATOMIC((size_t)(n * padded_item_size)) : GC_MALLOC((size_t)(n * padded_item_size)), - .length=n, - .stride=padded_item_size, .atomic=list.atomic}; + List_t selected = {.data = list.atomic ? GC_MALLOC_ATOMIC((size_t)(n * padded_item_size)) + : GC_MALLOC((size_t)(n * padded_item_size)), + .length = n, + .stride = padded_item_size, + .atomic = list.atomic}; for (int64_t i = 0; i < n; i++) { double r = rng_fn(random_num.userdata); if unlikely (r < 0.0 || r >= 1.0) @@ -452,85 +431,77 @@ public List_t List$sample(List_t list, Int_t int_n, List_t weights, OptionalClos r *= (double)list.length; int64_t index = (int64_t)r; assert(index >= 0 && index < list.length); - if ((r - (double)index) > aliases[index].odds) - index = aliases[index].alias; - memcpy(selected.data + i*selected.stride, list.data + index*list.stride, (size_t)padded_item_size); + if ((r - (double)index) > aliases[index].odds) index = aliases[index].alias; + memcpy(selected.data + i * selected.stride, list.data + index * list.stride, (size_t)padded_item_size); } return selected; } -public List_t List$from(List_t list, Int_t first) -{ - return List$slice(list, first, I_small(-1)); -} +public +List_t List$from(List_t list, Int_t first) { return List$slice(list, first, I_small(-1)); } -public List_t List$to(List_t list, Int_t last) -{ - return List$slice(list, I_small(1), last); -} +public +List_t List$to(List_t list, Int_t last) { return List$slice(list, I_small(1), last); } -public List_t List$by(List_t list, Int_t int_stride, int64_t padded_item_size) -{ +public +List_t List$by(List_t list, Int_t int_stride, int64_t padded_item_size) { int64_t stride = Int64$from_int(int_stride, false); // In the unlikely event that the stride value would be too large to fit in // a 15-bit integer, fall back to creating a copy of the list: - if (unlikely(list.stride*stride < LIST_MIN_STRIDE || list.stride*stride > LIST_MAX_STRIDE)) { + if (unlikely(list.stride * stride < LIST_MIN_STRIDE || list.stride * stride > LIST_MAX_STRIDE)) { void *copy = NULL; int64_t len = (stride < 0 ? list.length / -stride : list.length / stride) + ((list.length % stride) != 0); if (len > 0) { - copy = list.atomic ? GC_MALLOC_ATOMIC((size_t)(len * padded_item_size)) : GC_MALLOC((size_t)(len * padded_item_size)); + copy = list.atomic ? GC_MALLOC_ATOMIC((size_t)(len * padded_item_size)) + : GC_MALLOC((size_t)(len * padded_item_size)); void *start = (stride < 0 ? list.data + (list.stride * (list.length - 1)) : list.data); for (int64_t i = 0; i < len; i++) - memcpy(copy + i*padded_item_size, start + list.stride*stride*i, (size_t)padded_item_size); + memcpy(copy + i * padded_item_size, start + list.stride * stride * i, (size_t)padded_item_size); } return (List_t){ - .data=copy, - .length=len, - .stride=padded_item_size, - .atomic=list.atomic, + .data = copy, + .length = len, + .stride = padded_item_size, + .atomic = list.atomic, }; } - if (stride == 0) - return (List_t){.atomic=list.atomic}; + if (stride == 0) return (List_t){.atomic = list.atomic}; return (List_t){ - .atomic=list.atomic, - .data=(stride < 0 ? list.data + (list.stride * (list.length - 1)) : list.data), - .length=(stride < 0 ? list.length / -stride : list.length / stride) + ((list.length % stride) != 0), - .stride=list.stride * stride, - .data_refcount=list.data_refcount, + .atomic = list.atomic, + .data = (stride < 0 ? list.data + (list.stride * (list.length - 1)) : list.data), + .length = (stride < 0 ? list.length / -stride : list.length / stride) + ((list.length % stride) != 0), + .stride = list.stride * stride, + .data_refcount = list.data_refcount, }; } -public List_t List$slice(List_t list, Int_t int_first, Int_t int_last) +public +List_t List$slice(List_t list, Int_t int_first, Int_t int_last) { int64_t first = Int64$from_int(int_first, false); - if (first < 0) - first = list.length + first + 1; + if (first < 0) first = list.length + first + 1; int64_t last = Int64$from_int(int_last, false); - if (last < 0) - last = list.length + last + 1; + if (last < 0) last = list.length + last + 1; - if (last > list.length) - last = list.length; + if (last > list.length) last = list.length; - if (first < 1 || first > list.length || last == 0) - return (List_t){.atomic=list.atomic}; + if (first < 1 || first > list.length || last == 0) return (List_t){.atomic = list.atomic}; return (List_t){ - .atomic=list.atomic, - .data=list.data + list.stride*(first-1), - .length=last - first + 1, - .stride=list.stride, - .data_refcount=list.data_refcount, + .atomic = list.atomic, + .data = list.data + list.stride * (first - 1), + .length = last - first + 1, + .stride = list.stride, + .data_refcount = list.data_refcount, }; } -public List_t List$reversed(List_t list, int64_t padded_item_size) -{ +public +List_t List$reversed(List_t list, int64_t padded_item_size) { // Just in case negating the stride gives a value that doesn't fit into a // 15-bit integer, fall back to List$by()'s more general method of copying // the list. This should only happen if list.stride is MIN_STRIDE to @@ -540,58 +511,54 @@ public List_t List$reversed(List_t list, int64_t padded_item_size) List_t reversed = list; reversed.stride = -list.stride; - reversed.data = list.data + (list.length-1)*list.stride; + reversed.data = list.data + (list.length - 1) * list.stride; return reversed; } -public List_t List$concat(List_t x, List_t y, int64_t padded_item_size) -{ - void *data = x.atomic ? GC_MALLOC_ATOMIC((size_t)(padded_item_size*(x.length + y.length))) - : GC_MALLOC((size_t)(padded_item_size*(x.length + y.length))); +public +List_t List$concat(List_t x, List_t y, int64_t padded_item_size) { + void *data = x.atomic ? GC_MALLOC_ATOMIC((size_t)(padded_item_size * (x.length + y.length))) + : GC_MALLOC((size_t)(padded_item_size * (x.length + y.length))); if (x.stride == padded_item_size) { - memcpy(data, x.data, (size_t)(padded_item_size*x.length)); + memcpy(data, x.data, (size_t)(padded_item_size * x.length)); } else { for (int64_t i = 0; i < x.length; i++) - memcpy(data + i*padded_item_size, x.data + i*padded_item_size, (size_t)padded_item_size); + memcpy(data + i * padded_item_size, x.data + i * padded_item_size, (size_t)padded_item_size); } - void *dest = data + padded_item_size*x.length; + void *dest = data + padded_item_size * x.length; if (y.stride == padded_item_size) { - memcpy(dest, y.data, (size_t)(padded_item_size*y.length)); + memcpy(dest, y.data, (size_t)(padded_item_size * y.length)); } else { for (int64_t i = 0; i < y.length; i++) - memcpy(dest + i*padded_item_size, y.data + i*y.stride, (size_t)padded_item_size); + memcpy(dest + i * padded_item_size, y.data + i * y.stride, (size_t)padded_item_size); } return (List_t){ - .data=data, - .length=x.length + y.length, - .stride=padded_item_size, - .atomic=x.atomic, + .data = data, + .length = x.length + y.length, + .stride = padded_item_size, + .atomic = x.atomic, }; } -public bool List$has(List_t list, void *item, const TypeInfo_t *type) -{ +public +bool List$has(List_t list, void *item, const TypeInfo_t *type) { const TypeInfo_t *item_type = type->ListInfo.item; for (int64_t i = 0; i < list.length; i++) { - if (generic_equal(list.data + i*list.stride, item, item_type)) - return true; + if (generic_equal(list.data + i * list.stride, item, item_type)) return true; } return false; } -public void List$clear(List_t *list) -{ - *list = (List_t){.data=0, .length=0}; -} +public +void List$clear(List_t *list) { *list = (List_t){.data = 0, .length = 0}; } -public int32_t List$compare(const void *vx, const void *vy, const TypeInfo_t *type) -{ - const List_t *x = (List_t*)vx, *y = (List_t*)vy; +public +int32_t List$compare(const void *vx, const void *vy, const TypeInfo_t *type) { + const List_t *x = (List_t *)vx, *y = (List_t *)vy; // Early out for lists with the same data, e.g. two copies of the same list: - if (x->data == y->data && x->stride == y->stride) - return (x->length > y->length) - (x->length < y->length); + if (x->data == y->data && x->stride == y->stride) return (x->length > y->length) - (x->length < y->length); const TypeInfo_t *item = type->ListInfo.item; if (item->tag == PointerInfo || !item->metamethods.compare) { // data comparison @@ -599,128 +566,120 @@ public int32_t List$compare(const void *vx, const void *vy, const TypeInfo_t *ty if (type->ListInfo.item->align > 1 && item_padded_size % type->ListInfo.item->align) errx(1, "Item size is not padded!"); - if ((int64_t)x->stride == item_padded_size && (int64_t)y->stride == item_padded_size && item->size == item_padded_size) { - int32_t cmp = (int32_t)memcmp(x->data, y->data, (size_t)(MIN(x->length, y->length)*item_padded_size)); + if ((int64_t)x->stride == item_padded_size && (int64_t)y->stride == item_padded_size + && item->size == item_padded_size) { + int32_t cmp = (int32_t)memcmp(x->data, y->data, (size_t)(MIN(x->length, y->length) * item_padded_size)); if (cmp != 0) return cmp; } else { for (int32_t i = 0, len = MIN(x->length, y->length); i < len; i++) { - int32_t cmp = (int32_t)memcmp(x->data+ x->stride*i, y->data + y->stride*i, (size_t)(item->size)); + int32_t cmp = (int32_t)memcmp(x->data + x->stride * i, y->data + y->stride * i, (size_t)(item->size)); if (cmp != 0) return cmp; } } } else { for (int32_t i = 0, len = MIN(x->length, y->length); i < len; i++) { - int32_t cmp = generic_compare(x->data + x->stride*i, y->data + y->stride*i, item); + int32_t cmp = generic_compare(x->data + x->stride * i, y->data + y->stride * i, item); if (cmp != 0) return cmp; } } return (x->length > y->length) - (x->length < y->length); } -public bool List$equal(const void *x, const void *y, const TypeInfo_t *type) -{ - return x == y || (((List_t*)x)->length == ((List_t*)y)->length && List$compare(x, y, type) == 0); +public +bool List$equal(const void *x, const void *y, const TypeInfo_t *type) { + return x == y || (((List_t *)x)->length == ((List_t *)y)->length && List$compare(x, y, type) == 0); } -public Text_t List$as_text(const void *obj, bool colorize, const TypeInfo_t *type) -{ - List_t *list = (List_t*)obj; - if (!list) - return Text$concat(Text("["), generic_as_text(NULL, false, type->ListInfo.item), Text("]")); +public +Text_t List$as_text(const void *obj, bool colorize, const TypeInfo_t *type) { + List_t *list = (List_t *)obj; + if (!list) return Text$concat(Text("["), generic_as_text(NULL, false, type->ListInfo.item), Text("]")); const TypeInfo_t *item_type = type->ListInfo.item; Text_t text = Text("["); for (int64_t i = 0; i < list->length; i++) { - if (i > 0) - text = Text$concat(text, Text(", ")); - Text_t item_text = generic_as_text(list->data + i*list->stride, colorize, item_type); + if (i > 0) text = Text$concat(text, Text(", ")); + Text_t item_text = generic_as_text(list->data + i * list->stride, colorize, item_type); text = Text$concat(text, item_text); } text = Text$concat(text, Text("]")); return text; } -public uint64_t List$hash(const void *obj, const TypeInfo_t *type) -{ - const List_t *list = (List_t*)obj; +public +uint64_t List$hash(const void *obj, const TypeInfo_t *type) { + const List_t *list = (List_t *)obj; const TypeInfo_t *item = type->ListInfo.item; siphash sh; siphashinit(&sh, sizeof(uint64_t[list->length])); - if (item->tag == PointerInfo || (!item->metamethods.hash && item->size == sizeof(void*))) { // Raw data hash + if (item->tag == PointerInfo || (!item->metamethods.hash && item->size == sizeof(void *))) { // Raw data hash for (int64_t i = 0; i < list->length; i++) - siphashadd64bits(&sh, (uint64_t)(list->data + i*list->stride)); + siphashadd64bits(&sh, (uint64_t)(list->data + i * list->stride)); } else { for (int64_t i = 0; i < list->length; i++) { - uint64_t item_hash = generic_hash(list->data + i*list->stride, item); + uint64_t item_hash = generic_hash(list->data + i * list->stride, item); siphashadd64bits(&sh, item_hash); } } return siphashfinish_last_part(&sh, 0); } -static void siftdown(List_t *heap, int64_t startpos, int64_t pos, Closure_t comparison, int64_t padded_item_size) -{ +static void siftdown(List_t *heap, int64_t startpos, int64_t pos, Closure_t comparison, int64_t padded_item_size) { assert(pos > 0 && pos < heap->length); char newitem[padded_item_size]; - memcpy(newitem, heap->data + heap->stride*pos, (size_t)(padded_item_size)); + memcpy(newitem, heap->data + heap->stride * pos, (size_t)(padded_item_size)); while (pos > startpos) { int64_t parentpos = (pos - 1) >> 1; - typedef int32_t (*cmp_fn_t)(void*, void*, void*); - int32_t cmp = ((cmp_fn_t)comparison.fn)(newitem, heap->data + heap->stride*parentpos, comparison.userdata); - if (cmp >= 0) - break; + typedef int32_t (*cmp_fn_t)(void *, void *, void *); + int32_t cmp = ((cmp_fn_t)comparison.fn)(newitem, heap->data + heap->stride * parentpos, comparison.userdata); + if (cmp >= 0) break; - memcpy(heap->data + heap->stride*pos, heap->data + heap->stride*parentpos, (size_t)(padded_item_size)); + memcpy(heap->data + heap->stride * pos, heap->data + heap->stride * parentpos, (size_t)(padded_item_size)); pos = parentpos; } - memcpy(heap->data + heap->stride*pos, newitem, (size_t)(padded_item_size)); + memcpy(heap->data + heap->stride * pos, newitem, (size_t)(padded_item_size)); } -static void siftup(List_t *heap, int64_t pos, Closure_t comparison, int64_t padded_item_size) -{ +static void siftup(List_t *heap, int64_t pos, Closure_t comparison, int64_t padded_item_size) { int64_t endpos = heap->length; int64_t startpos = pos; assert(pos < endpos); char old_top[padded_item_size]; - memcpy(old_top, heap->data + heap->stride*pos, (size_t)(padded_item_size)); + memcpy(old_top, heap->data + heap->stride * pos, (size_t)(padded_item_size)); // Bubble up the smallest leaf node int64_t limit = endpos >> 1; while (pos < limit) { - int64_t childpos = 2*pos + 1; // Smaller of the two child nodes + int64_t childpos = 2 * pos + 1; // Smaller of the two child nodes if (childpos + 1 < endpos) { - typedef int32_t (*cmp_fn_t)(void*, void*, void*); - int32_t cmp = ((cmp_fn_t)comparison.fn)( - heap->data + heap->stride*childpos, - heap->data + heap->stride*(childpos + 1), - comparison.userdata); + typedef int32_t (*cmp_fn_t)(void *, void *, void *); + int32_t cmp = ((cmp_fn_t)comparison.fn)(heap->data + heap->stride * childpos, + heap->data + heap->stride * (childpos + 1), comparison.userdata); childpos += (cmp >= 0); } // Move the child node up: - memcpy(heap->data + heap->stride*pos, heap->data + heap->stride*childpos, (size_t)(padded_item_size)); + memcpy(heap->data + heap->stride * pos, heap->data + heap->stride * childpos, (size_t)(padded_item_size)); pos = childpos; } - memcpy(heap->data + heap->stride*pos, old_top, (size_t)(padded_item_size)); + memcpy(heap->data + heap->stride * pos, old_top, (size_t)(padded_item_size)); // Shift the node's parents down: siftdown(heap, startpos, pos, comparison, padded_item_size); } -public void List$heap_push(List_t *heap, const void *item, Closure_t comparison, int64_t padded_item_size) -{ +public +void List$heap_push(List_t *heap, const void *item, Closure_t comparison, int64_t padded_item_size) { List$insert(heap, item, I(0), padded_item_size); if (heap->length > 1) { - if (heap->data_refcount != 0) - List$compact(heap, padded_item_size); - siftdown(heap, 0, heap->length-1, comparison, padded_item_size); + if (heap->data_refcount != 0) List$compact(heap, padded_item_size); + siftdown(heap, 0, heap->length - 1, comparison, padded_item_size); } } -public void List$heap_pop(List_t *heap, Closure_t comparison, int64_t padded_item_size) -{ - if (heap->length == 0) - fail("Attempt to pop from an empty list"); +public +void List$heap_pop(List_t *heap, Closure_t comparison, int64_t padded_item_size) { + if (heap->length == 0) fail("Attempt to pop from an empty list"); if (heap->length == 1) { *heap = (List_t){}; @@ -728,96 +687,90 @@ public void List$heap_pop(List_t *heap, Closure_t comparison, int64_t padded_ite heap->data += heap->stride; --heap->length; } else { - if (heap->data_refcount != 0) - List$compact(heap, padded_item_size); - memcpy(heap->data, heap->data + heap->stride*(heap->length-1), (size_t)(padded_item_size)); + if (heap->data_refcount != 0) List$compact(heap, padded_item_size); + memcpy(heap->data, heap->data + heap->stride * (heap->length - 1), (size_t)(padded_item_size)); --heap->length; siftup(heap, 0, comparison, padded_item_size); } } -public void List$heapify(List_t *heap, Closure_t comparison, int64_t padded_item_size) -{ - if (heap->data_refcount != 0) - List$compact(heap, padded_item_size); +public +void List$heapify(List_t *heap, Closure_t comparison, int64_t padded_item_size) { + if (heap->data_refcount != 0) List$compact(heap, padded_item_size); // It's necessary to bump the refcount because the user's comparison // function could do stuff that modifies the heap's data. LIST_INCREF(*heap); int64_t i, n = heap->length; - for (i = (n >> 1) - 1 ; i >= 0 ; i--) + for (i = (n >> 1) - 1; i >= 0; i--) siftup(heap, i, comparison, padded_item_size); LIST_DECREF(*heap); } -public Int_t List$binary_search(List_t list, void *target, Closure_t comparison) -{ - typedef int32_t (*cmp_fn_t)(void*, void*, void*); - int64_t lo = 0, hi = list.length-1; +public +Int_t List$binary_search(List_t list, void *target, Closure_t comparison) { + typedef int32_t (*cmp_fn_t)(void *, void *, void *); + int64_t lo = 0, hi = list.length - 1; while (lo <= hi) { int64_t mid = (lo + hi) / 2; - int32_t cmp = ((cmp_fn_t)comparison.fn)( - list.data + list.stride*mid, target, comparison.userdata); - if (cmp == 0) - return I(mid+1); - else if (cmp < 0) - lo = mid + 1; - else if (cmp > 0) - hi = mid - 1; + int32_t cmp = ((cmp_fn_t)comparison.fn)(list.data + list.stride * mid, target, comparison.userdata); + if (cmp == 0) return I(mid + 1); + else if (cmp < 0) lo = mid + 1; + else if (cmp > 0) hi = mid - 1; } - return I(lo+1); // Return the index where the target would be inserted + return I(lo + 1); // Return the index where the target would be inserted } -public PUREFUNC bool List$is_none(const void *obj, const TypeInfo_t *info) -{ +public +PUREFUNC bool List$is_none(const void *obj, const TypeInfo_t *info) { (void)info; - return ((List_t*)obj)->length < 0; + return ((List_t *)obj)->length < 0; } -public void List$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) -{ - List_t list = *(List_t*)obj; +public +void List$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) { + List_t list = *(List_t *)obj; int64_t len = list.length; Int64$serialize(&len, out, pointers, &Int64$info); serialize_fn_t item_serialize = type->ListInfo.item->metamethods.serialize; if (item_serialize) { for (int64_t i = 0; i < len; i++) - item_serialize(list.data + i*list.stride, out, pointers, type->ListInfo.item); + item_serialize(list.data + i * list.stride, out, pointers, type->ListInfo.item); } else if (list.stride == type->ListInfo.item->size) { fwrite(list.data, (size_t)type->ListInfo.item->size, (size_t)len, out); } else { for (int64_t i = 0; i < len; i++) - fwrite(list.data + i*list.stride, (size_t)type->ListInfo.item->size, 1, out); + fwrite(list.data + i * list.stride, (size_t)type->ListInfo.item->size, 1, out); } } -public void List$deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *type) -{ +public +void List$deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *type) { int64_t len = -1; Int64$deserialize(in, &len, pointers, &Int64$info); int64_t padded_size = type->ListInfo.item->size; if (type->ListInfo.item->align > 0 && padded_size % type->ListInfo.item->align > 0) padded_size += type->ListInfo.item->align - (padded_size % type->ListInfo.item->align); List_t list = { - .length=len, - .data=GC_MALLOC((size_t)(len*padded_size)), - .stride=padded_size, + .length = len, + .data = GC_MALLOC((size_t)(len * padded_size)), + .stride = padded_size, }; deserialize_fn_t item_deserialize = type->ListInfo.item->metamethods.deserialize; if (item_deserialize) { for (int64_t i = 0; i < len; i++) - item_deserialize(in, list.data + i*list.stride, pointers, type->ListInfo.item); + item_deserialize(in, list.data + i * list.stride, pointers, type->ListInfo.item); } else if (list.stride == type->ListInfo.item->size) { if (fread(list.data, (size_t)type->ListInfo.item->size, (size_t)len, in) != (size_t)len) fail("Not enough data in stream to deserialize"); } else { size_t item_size = (size_t)type->ListInfo.item->size; for (int64_t i = 0; i < len; i++) { - if (fread(list.data + i*list.stride, item_size, 1, in) != 1) + if (fread(list.data + i * list.stride, item_size, 1, in) != 1) fail("Not enough data in stream to deserialize"); } } - *(List_t*)obj = list; + *(List_t *)obj = list; } // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/lists.h b/src/stdlib/lists.h index a2853e48..afda3d74 100644 --- a/src/stdlib/lists.h +++ b/src/stdlib/lists.h @@ -10,88 +10,141 @@ #include "util.h" // Convert negative indices to back-indexed without branching: index0 = index + (index < 0)*(len+1)) - 1 -#define List_get(item_type, arr_expr, index_expr, start, end) *({ \ - const List_t list = arr_expr; int64_t index = index_expr; \ - int64_t off = index + (index < 0) * (list.length + 1) - 1; \ - if (unlikely(off < 0 || off >= list.length)) \ - fail_source(__SOURCE_FILE__, start, end, "Invalid list index: ", index, " (list has length ", (int64_t)list.length, ")\n"); \ - (item_type*)(list.data + list.stride * off);}) -#define List_get_unchecked(type, x, i) *({ const List_t list = x; int64_t index = i; \ - int64_t off = index + (index < 0) * (list.length + 1) - 1; \ - (type*)(list.data + list.stride * off);}) -#define List_lvalue(item_type, arr_expr, index_expr, start, end) *({ \ - List_t *list = arr_expr; int64_t index = index_expr; \ - int64_t off = index + (index < 0) * (list->length + 1) - 1; \ - if (unlikely(off < 0 || off >= list->length)) \ - fail_source(__SOURCE_FILE__, start, end, "Invalid list index: ", index, " (list has length ", (int64_t)list->length, ")\n"); \ - if (list->data_refcount > 0) \ - List$compact(list, sizeof(item_type)); \ - (item_type*)(list->data + list->stride * off); }) -#define List_lvalue_unchecked(item_type, arr_expr, index_expr) *({ \ - List_t *list = arr_expr; int64_t index = index_expr; \ - int64_t off = index + (index < 0) * (list->length + 1) - 1; \ - if (list->data_refcount > 0) \ - List$compact(list, sizeof(item_type)); \ - (item_type*)(list->data + list->stride * off); }) -#define List_set(item_type, list, index, value, start, end) \ - List_lvalue(item_type, arr_expr, index, start, end) = value -#define is_atomic(x) _Generic(x, bool: true, int8_t: true, int16_t: true, int32_t: true, int64_t: true, float: true, double: true, default: false) -#define TypedList(t, ...) ({ t items[] = {__VA_ARGS__}; \ - (List_t){.length=sizeof(items)/sizeof(items[0]), \ - .stride=(int64_t)&items[1] - (int64_t)&items[0], \ - .data=memcpy(GC_MALLOC(sizeof(items)), items, sizeof(items)), \ - .atomic=0, \ - .data_refcount=0}; }) -#define TypedListN(t, N, ...) ({ t items[N] = {__VA_ARGS__}; \ - (List_t){.length=N, \ - .stride=(int64_t)&items[1] - (int64_t)&items[0], \ - .data=memcpy(GC_MALLOC(sizeof(items)), items, sizeof(items)), \ - .atomic=0, \ - .data_refcount=0}; }) -#define List(x, ...) ({ __typeof(x) items[] = {x, __VA_ARGS__}; \ - (List_t){.length=sizeof(items)/sizeof(items[0]), \ - .stride=(int64_t)&items[1] - (int64_t)&items[0], \ - .data=memcpy(is_atomic(x) ? GC_MALLOC_ATOMIC(sizeof(items)) : GC_MALLOC(sizeof(items)), items, sizeof(items)), \ - .atomic=is_atomic(x), \ - .data_refcount=0}; }) +#define List_get(item_type, arr_expr, index_expr, start, end) \ + *({ \ + const List_t list = arr_expr; \ + int64_t index = index_expr; \ + int64_t off = index + (index < 0) * (list.length + 1) - 1; \ + if (unlikely(off < 0 || off >= list.length)) \ + fail_source(__SOURCE_FILE__, start, end, "Invalid list index: ", index, " (list has length ", \ + (int64_t)list.length, ")\n"); \ + (item_type *)(list.data + list.stride * off); \ + }) +#define List_get_unchecked(type, x, i) \ + *({ \ + const List_t list = x; \ + int64_t index = i; \ + int64_t off = index + (index < 0) * (list.length + 1) - 1; \ + (type *)(list.data + list.stride * off); \ + }) +#define List_lvalue(item_type, arr_expr, index_expr, start, end) \ + *({ \ + List_t *list = arr_expr; \ + int64_t index = index_expr; \ + int64_t off = index + (index < 0) * (list->length + 1) - 1; \ + if (unlikely(off < 0 || off >= list->length)) \ + fail_source(__SOURCE_FILE__, start, end, "Invalid list index: ", index, " (list has length ", \ + (int64_t)list->length, ")\n"); \ + if (list->data_refcount > 0) List$compact(list, sizeof(item_type)); \ + (item_type *)(list->data + list->stride * off); \ + }) +#define List_lvalue_unchecked(item_type, arr_expr, index_expr) \ + *({ \ + List_t *list = arr_expr; \ + int64_t index = index_expr; \ + int64_t off = index + (index < 0) * (list->length + 1) - 1; \ + if (list->data_refcount > 0) List$compact(list, sizeof(item_type)); \ + (item_type *)(list->data + list->stride * off); \ + }) +#define List_set(item_type, list, index, value, start, end) List_lvalue(item_type, arr_expr, index, start, end) = value +#define is_atomic(x) \ + _Generic(x, \ + bool: true, \ + int8_t: true, \ + int16_t: true, \ + int32_t: true, \ + int64_t: true, \ + float: true, \ + double: true, \ + default: false) +#define TypedList(t, ...) \ + ({ \ + t items[] = {__VA_ARGS__}; \ + (List_t){.length = sizeof(items) / sizeof(items[0]), \ + .stride = (int64_t)&items[1] - (int64_t)&items[0], \ + .data = memcpy(GC_MALLOC(sizeof(items)), items, sizeof(items)), \ + .atomic = 0, \ + .data_refcount = 0}; \ + }) +#define TypedListN(t, N, ...) \ + ({ \ + t items[N] = {__VA_ARGS__}; \ + (List_t){.length = N, \ + .stride = (int64_t)&items[1] - (int64_t)&items[0], \ + .data = memcpy(GC_MALLOC(sizeof(items)), items, sizeof(items)), \ + .atomic = 0, \ + .data_refcount = 0}; \ + }) +#define List(x, ...) \ + ({ \ + __typeof(x) items[] = {x, __VA_ARGS__}; \ + (List_t){.length = sizeof(items) / sizeof(items[0]), \ + .stride = (int64_t)&items[1] - (int64_t)&items[0], \ + .data = memcpy(is_atomic(x) ? GC_MALLOC_ATOMIC(sizeof(items)) : GC_MALLOC(sizeof(items)), items, \ + sizeof(items)), \ + .atomic = is_atomic(x), \ + .data_refcount = 0}; \ + }) // List refcounts use a saturating add, where once it's at the max value, it stays there. #define LIST_INCREF(list) (list).data_refcount += ((list).data_refcount < LIST_MAX_DATA_REFCOUNT) #define LIST_DECREF(list) (list).data_refcount -= ((list).data_refcount < LIST_MAX_DATA_REFCOUNT) -#define LIST_COPY(list) ({ LIST_INCREF(list); list; }) +#define LIST_COPY(list) \ + ({ \ + LIST_INCREF(list); \ + list; \ + }) -#define List$insert_value(list, item_expr, index, padded_item_size) List$insert(list, (__typeof(item_expr)[1]){item_expr}, index, padded_item_size) +#define List$insert_value(list, item_expr, index, padded_item_size) \ + List$insert(list, (__typeof(item_expr)[1]){item_expr}, index, padded_item_size) void List$insert(List_t *list, const void *item, Int_t index, int64_t padded_item_size); void List$insert_all(List_t *list, List_t to_insert, Int_t index, int64_t padded_item_size); void List$remove_at(List_t *list, Int_t index, Int_t count, int64_t padded_item_size); void List$remove_item(List_t *list, void *item, Int_t max_removals, const TypeInfo_t *type); -#define List$remove_item_value(list, item_expr, max, type) List$remove_item(list, (__typeof(item_expr)[1]){item_expr}, max, type) +#define List$remove_item_value(list, item_expr, max, type) \ + List$remove_item(list, (__typeof(item_expr)[1]){item_expr}, max, type) -#define List$pop(arr_expr, index_expr, item_type, nonnone_var, nonnone_expr, none_expr) ({ \ - List_t *list = arr_expr; \ - Int_t index = index_expr; \ - int64_t index64 = Int64$from_int(index, false); \ - int64_t off = index64 + (index64 < 0) * (list->length + 1) - 1; \ - (off >= 0 && off < list->length) ? ({ \ - item_type nonnone_var = *(item_type*)(list->data + off*list->stride); \ - List$remove_at(list, index, I_small(1), sizeof(item_type)); \ - nonnone_expr; \ - }) : none_expr; }) +#define List$pop(arr_expr, index_expr, item_type, nonnone_var, nonnone_expr, none_expr) \ + ({ \ + List_t *list = arr_expr; \ + Int_t index = index_expr; \ + int64_t index64 = Int64$from_int(index, false); \ + int64_t off = index64 + (index64 < 0) * (list->length + 1) - 1; \ + (off >= 0 && off < list->length) ? ({ \ + item_type nonnone_var = *(item_type *)(list->data + off * list->stride); \ + List$remove_at(list, index, I_small(1), sizeof(item_type)); \ + nonnone_expr; \ + }) \ + : none_expr; \ + }) OptionalInt_t List$find(List_t list, void *item, const TypeInfo_t *type); -#define List$find_value(list, item_expr, type) ({ __typeof(item_expr) item = item_expr; List$find(list, &item, type); }) +#define List$find_value(list, item_expr, type) \ + ({ \ + __typeof(item_expr) item = item_expr; \ + List$find(list, &item, type); \ + }) OptionalInt_t List$first(List_t list, Closure_t predicate); void List$sort(List_t *list, Closure_t comparison, int64_t padded_item_size); List_t List$sorted(List_t list, Closure_t comparison, int64_t padded_item_size); void List$shuffle(List_t *list, OptionalClosure_t random_int64, int64_t padded_item_size); List_t List$shuffled(List_t list, OptionalClosure_t random_int64, int64_t padded_item_size); void *List$random(List_t list, OptionalClosure_t random_int64); -#define List$random_value(list, random_int64, t) ({ List_t _arr = list; if (_arr.length == 0) fail("Cannot get a random value from an empty list!"); *(t*)List$random(_arr, random_int64); }) +#define List$random_value(list, random_int64, t) \ + ({ \ + List_t _arr = list; \ + if (_arr.length == 0) fail("Cannot get a random value from an empty list!"); \ + *(t *)List$random(_arr, random_int64); \ + }) List_t List$sample(List_t list, Int_t n, List_t weights, Closure_t random_num, int64_t padded_item_size); Table_t List$counts(List_t list, const TypeInfo_t *type); void List$clear(List_t *list); void List$compact(List_t *list, int64_t padded_item_size); PUREFUNC bool List$has(List_t list, void *item, const TypeInfo_t *type); -#define List$has_value(list, item_expr, type) ({ __typeof(item_expr) item = item_expr; List$has(list, &item, type); }) +#define List$has_value(list, item_expr, type) \ + ({ \ + __typeof(item_expr) item = item_expr; \ + List$has(list, &item, type); \ + }) PUREFUNC List_t List$from(List_t list, Int_t first); PUREFUNC List_t List$to(List_t list, Int_t last); PUREFUNC List_t List$by(List_t list, Int_t stride, int64_t padded_item_size); @@ -101,37 +154,51 @@ List_t List$concat(List_t x, List_t y, int64_t padded_item_size); PUREFUNC uint64_t List$hash(const void *list, const TypeInfo_t *type); PUREFUNC int32_t List$compare(const void *x, const void *y, const TypeInfo_t *type); PUREFUNC bool List$equal(const void *x, const void *y, const TypeInfo_t *type); -PUREFUNC bool List$is_none(const void *obj, const TypeInfo_t*); +PUREFUNC bool List$is_none(const void *obj, const TypeInfo_t *); Text_t List$as_text(const void *list, bool colorize, const TypeInfo_t *type); void List$heapify(List_t *heap, Closure_t comparison, int64_t padded_item_size); void List$heap_push(List_t *heap, const void *item, Closure_t comparison, int64_t padded_item_size); -#define List$heap_push_value(heap, _value, comparison, padded_item_size) ({ __typeof(_value) value = _value; List$heap_push(heap, &value, comparison, padded_item_size); }) +#define List$heap_push_value(heap, _value, comparison, padded_item_size) \ + ({ \ + __typeof(_value) value = _value; \ + List$heap_push(heap, &value, comparison, padded_item_size); \ + }) void List$heap_pop(List_t *heap, Closure_t comparison, int64_t padded_item_size); -#define List$heap_pop_value(heap, comparison, type, nonnone_var, nonnone_expr, none_expr) \ - ({ List_t *_heap = heap; \ - (_heap->length > 0) ? ({ \ - type nonnone_var = *(type*)_heap->data; \ - List$heap_pop(_heap, comparison, sizeof(type)); \ - nonnone_expr; \ - }) : none_expr; }) +#define List$heap_pop_value(heap, comparison, type, nonnone_var, nonnone_expr, none_expr) \ + ({ \ + List_t *_heap = heap; \ + (_heap->length > 0) ? ({ \ + type nonnone_var = *(type *)_heap->data; \ + List$heap_pop(_heap, comparison, sizeof(type)); \ + nonnone_expr; \ + }) \ + : none_expr; \ + }) Int_t List$binary_search(List_t list, void *target, Closure_t comparison); -#define List$binary_search_value(list, target, comparison) \ - ({ __typeof(target) _target = target; List$binary_search(list, &_target, comparison); }) +#define List$binary_search_value(list, target, comparison) \ + ({ \ + __typeof(target) _target = target; \ + List$binary_search(list, &_target, comparison); \ + }) void List$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type); void List$deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *type); -#define List$metamethods { \ - .as_text=List$as_text, \ - .compare=List$compare, \ - .equal=List$equal, \ - .hash=List$hash, \ - .is_none=List$is_none, \ - .serialize=List$serialize, \ - .deserialize=List$deserialize, \ -} +#define List$metamethods \ + { \ + .as_text = List$as_text, \ + .compare = List$compare, \ + .equal = List$equal, \ + .hash = List$hash, \ + .is_none = List$is_none, \ + .serialize = List$serialize, \ + .deserialize = List$deserialize, \ + } -#define List$info(item_info) &((TypeInfo_t){.size=sizeof(List_t), .align=__alignof__(List_t), \ - .tag=ListInfo, .ListInfo.item=item_info, \ - .metamethods=List$metamethods}) +#define List$info(item_info) \ + &((TypeInfo_t){.size = sizeof(List_t), \ + .align = __alignof__(List_t), \ + .tag = ListInfo, \ + .ListInfo.item = item_info, \ + .metamethods = List$metamethods}) // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/mapmacro.h b/src/stdlib/mapmacro.h index 5ed7a4b7..68834f8f 100644 --- a/src/stdlib/mapmacro.h +++ b/src/stdlib/mapmacro.h @@ -9,7 +9,7 @@ #define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) #define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) #define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) -#define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) +#define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) #define MAP_END(...) #define MAP_OUT @@ -21,7 +21,7 @@ #define MAP_NEXT0(test, next, ...) next MAP_OUT #define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0) -#define MAP_LIST_NEXT(test, next) MAP_LIST_NEXT1(MAP_GET_END test, next) +#define MAP_LIST_NEXT(test, next) MAP_LIST_NEXT1(MAP_GET_END test, next) #define MAP_LIST0(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, peek, __VA_ARGS__) #define MAP_LIST1(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, peek, __VA_ARGS__) diff --git a/src/stdlib/memory.c b/src/stdlib/memory.c index a28b0069..90e14261 100644 --- a/src/stdlib/memory.c +++ b/src/stdlib/memory.c @@ -1,9 +1,9 @@ // Type info and methods for "Memory" opaque type +#include <err.h> #include <gc.h> #include <stdbool.h> #include <stdint.h> #include <sys/param.h> -#include <err.h> #include "memory.h" #include "metamethods.h" @@ -12,21 +12,24 @@ #include "types.h" #include "util.h" -public Text_t Memory$as_text(const void *p, bool colorize, const TypeInfo_t *info) { +public +Text_t Memory$as_text(const void *p, bool colorize, const TypeInfo_t *info) { (void)info; if (!p) return Text("Memory"); - Text_t text = Text$from_str(String("Memory<", *(void**)p, ">")); + Text_t text = Text$from_str(String("Memory<", *(void **)p, ">")); return colorize ? Texts(Text("\x1b[0;34;1m"), text, Text("\x1b[m")) : text; } -public const TypeInfo_t Memory$info = { - .size=0, - .align=0, - .metamethods={ - .as_text=Memory$as_text, - .serialize=cannot_serialize, - .deserialize=cannot_deserialize, - }, +public +const TypeInfo_t Memory$info = { + .size = 0, + .align = 0, + .metamethods = + { + .as_text = Memory$as_text, + .serialize = cannot_serialize, + .deserialize = cannot_deserialize, + }, }; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/metamethods.c b/src/stdlib/metamethods.c index 8c755c59..ea06d20b 100644 --- a/src/stdlib/metamethods.c +++ b/src/stdlib/metamethods.c @@ -9,52 +9,44 @@ #include "types.h" #include "util.h" -PUREFUNC public uint64_t generic_hash(const void *obj, const TypeInfo_t *type) -{ - if (type->metamethods.hash) - return type->metamethods.hash(obj, type); +PUREFUNC public uint64_t generic_hash(const void *obj, const TypeInfo_t *type) { + if (type->metamethods.hash) return type->metamethods.hash(obj, type); - return siphash24((void*)obj, (size_t)(type->size)); + return siphash24((void *)obj, (size_t)(type->size)); } -PUREFUNC public int32_t generic_compare(const void *x, const void *y, const TypeInfo_t *type) -{ +PUREFUNC public int32_t generic_compare(const void *x, const void *y, const TypeInfo_t *type) { if (x == y) return 0; - if (type->metamethods.compare) - return type->metamethods.compare(x, y, type); + if (type->metamethods.compare) return type->metamethods.compare(x, y, type); - return (int32_t)memcmp((void*)x, (void*)y, (size_t)(type->size)); + return (int32_t)memcmp((void *)x, (void *)y, (size_t)(type->size)); } -PUREFUNC public bool generic_equal(const void *x, const void *y, const TypeInfo_t *type) -{ +PUREFUNC public bool generic_equal(const void *x, const void *y, const TypeInfo_t *type) { if (x == y) return true; - if (type->metamethods.equal) - return type->metamethods.equal(x, y, type); + if (type->metamethods.equal) return type->metamethods.equal(x, y, type); return (generic_compare(x, y, type) == 0); } -public Text_t generic_as_text(const void *obj, bool colorize, const TypeInfo_t *type) -{ - if (!type->metamethods.as_text) - fail("No text metamethod provided for type!"); +public +Text_t generic_as_text(const void *obj, bool colorize, const TypeInfo_t *type) { + if (!type->metamethods.as_text) fail("No text metamethod provided for type!"); return type->metamethods.as_text(obj, colorize, type); } -public void _serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) -{ - if (type->metamethods.serialize) - return type->metamethods.serialize(obj, out, pointers, type); +public +void _serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) { + if (type->metamethods.serialize) return type->metamethods.serialize(obj, out, pointers, type); fwrite(obj, (size_t)type->size, 1, out); } -public List_t generic_serialize(const void *x, const TypeInfo_t *type) -{ +public +List_t generic_serialize(const void *x, const TypeInfo_t *type) { char *buf = NULL; size_t size = 0; FILE *stream = open_memstream(&buf, &size); @@ -62,31 +54,29 @@ public List_t generic_serialize(const void *x, const TypeInfo_t *type) _serialize(x, stream, &pointers, type); fclose(stream); List_t bytes = { - .data=GC_MALLOC_ATOMIC(size), - .length=(int64_t)size, - .stride=1, - .atomic=1, + .data = GC_MALLOC_ATOMIC(size), + .length = (int64_t)size, + .stride = 1, + .atomic = 1, }; memcpy(bytes.data, buf, size); free(buf); return bytes; } -public void _deserialize(FILE *input, void *outval, List_t *pointers, const TypeInfo_t *type) -{ +public +void _deserialize(FILE *input, void *outval, List_t *pointers, const TypeInfo_t *type) { if (type->metamethods.deserialize) { type->metamethods.deserialize(input, outval, pointers, type); return; } - if (fread(outval, (size_t)type->size, 1, input) != 1) - fail("Not enough data in stream to deserialize"); + if (fread(outval, (size_t)type->size, 1, input) != 1) fail("Not enough data in stream to deserialize"); } -public void generic_deserialize(List_t bytes, void *outval, const TypeInfo_t *type) -{ - if (bytes.stride != 1) - List$compact(&bytes, 1); +public +void generic_deserialize(List_t bytes, void *outval, const TypeInfo_t *type) { + if (bytes.stride != 1) List$compact(&bytes, 1); FILE *input = fmemopen(bytes.data, (size_t)bytes.length, "r"); List_t pointers = {}; @@ -94,23 +84,21 @@ public void generic_deserialize(List_t bytes, void *outval, const TypeInfo_t *ty fclose(input); } -public int generic_print(const void *obj, bool colorize, const TypeInfo_t *type) -{ +public +int generic_print(const void *obj, bool colorize, const TypeInfo_t *type) { Text_t text = generic_as_text(obj, colorize, type); return Text$print(stdout, text) + fputc('\n', stdout); } -__attribute__((noreturn)) -public void cannot_serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) -{ +__attribute__((noreturn)) public +void cannot_serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) { (void)obj, (void)out, (void)pointers; Text_t typestr = generic_as_text(NULL, false, type); fail("Values of type ", typestr, " cannot be serialized or deserialized!"); } -__attribute__((noreturn)) -public void cannot_deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *type) -{ +__attribute__((noreturn)) public +void cannot_deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *type) { (void)obj, (void)in, (void)pointers; Text_t typestr = generic_as_text(NULL, false, type); fail("Values of type ", typestr, " cannot be serialized or deserialized!"); diff --git a/src/stdlib/metamethods.h b/src/stdlib/metamethods.h index ca0a1e7e..eda2230e 100644 --- a/src/stdlib/metamethods.h +++ b/src/stdlib/metamethods.h @@ -16,7 +16,7 @@ List_t generic_serialize(const void *x, const TypeInfo_t *type); void _deserialize(FILE *input, void *outval, List_t *pointers, const TypeInfo_t *type); void generic_deserialize(List_t bytes, void *outval, const TypeInfo_t *type); int generic_print(const void *obj, bool colorize, const TypeInfo_t *type); -void cannot_serialize(const void*, FILE*, Table_t*, const TypeInfo_t *type); -void cannot_deserialize(FILE*, void*, List_t*, const TypeInfo_t *type); +void cannot_serialize(const void *, FILE *, Table_t *, const TypeInfo_t *type); +void cannot_deserialize(FILE *, void *, List_t *, const TypeInfo_t *type); // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/nums.c b/src/stdlib/nums.c index 83166659..05ed14a8 100644 --- a/src/stdlib/nums.c +++ b/src/stdlib/nums.c @@ -13,20 +13,21 @@ #include "text.h" #include "types.h" -public PUREFUNC Text_t Num$as_text(const void *f, bool colorize, const TypeInfo_t *info) { +public +PUREFUNC Text_t Num$as_text(const void *f, bool colorize, const TypeInfo_t *info) { (void)info; if (!f) return Text("Num"); char *str = GC_MALLOC_ATOMIC(24); - int len = fpconv_dtoa(*(double*)f, str); + int len = fpconv_dtoa(*(double *)f, str); static const Text_t color_prefix = Text("\x1b[35m"), color_suffix = Text("\x1b[m"); Text_t text = Text$from_strn(str, (size_t)len); return colorize ? Texts(color_prefix, text, color_suffix) : text; -} +} -public PUREFUNC int32_t Num$compare(const void *x, const void *y, const TypeInfo_t *info) { +public +PUREFUNC int32_t Num$compare(const void *x, const void *y, const TypeInfo_t *info) { (void)info; - int64_t rx = *(int64_t*)x, - ry = *(int64_t*)y; + int64_t rx = *(int64_t *)x, ry = *(int64_t *)y; if (rx == ry) return 0; @@ -34,14 +35,16 @@ public PUREFUNC int32_t Num$compare(const void *x, const void *y, const TypeInfo if (ry < 0) ry ^= INT64_MAX; return (rx > ry) - (rx < ry); -} +} -public PUREFUNC bool Num$equal(const void *x, const void *y, const TypeInfo_t *info) { +public +PUREFUNC bool Num$equal(const void *x, const void *y, const TypeInfo_t *info) { (void)info; - return *(double*)x == *(double*)y; -} + return *(double *)x == *(double *)y; +} -public CONSTFUNC bool Num$near(double a, double b, double ratio, double absolute) { +public +CONSTFUNC bool Num$near(double a, double b, double ratio, double absolute) { if (ratio < 0) ratio = 0; else if (ratio > 1) ratio = 1; @@ -56,17 +59,19 @@ public CONSTFUNC bool Num$near(double a, double b, double ratio, double absolute return (diff < epsilon); } -public Text_t Num$percent(double f, double precision) { +public +Text_t Num$percent(double f, double precision) { double d = 100. * f; d = Num$with_precision(d, precision); return Texts(Num$as_text(&d, false, &Num$info), Text("%")); } -public CONSTFUNC double Num$with_precision(double num, double precision) { +public +CONSTFUNC double Num$with_precision(double num, double precision) { if (precision == 0.0) return num; // Precision will be, e.g. 0.01 or 100. if (precision < 1.) { - double inv = round(1./precision); // Necessary to make the math work + double inv = round(1. / precision); // Necessary to make the math work double k = num * inv; return round(k) / inv; } else { @@ -75,37 +80,34 @@ public CONSTFUNC double Num$with_precision(double num, double precision) { } } -public CONSTFUNC double Num$mod(double num, double modulus) { - // Euclidean division, see: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf +public +CONSTFUNC double Num$mod(double num, double modulus) { + // Euclidean division, see: + // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf double r = remainder(num, modulus); - r -= (r < 0) * (2*(modulus < 0) - 1) * modulus; + r -= (r < 0) * (2 * (modulus < 0) - 1) * modulus; return r; } -public CONSTFUNC double Num$mod1(double num, double modulus) { - return 1.0 + Num$mod(num-1, modulus); -} +public +CONSTFUNC double Num$mod1(double num, double modulus) { return 1.0 + Num$mod(num - 1, modulus); } -public CONSTFUNC double Num$mix(double amount, double x, double y) { - return (1.0-amount)*x + amount*y; -} +public +CONSTFUNC double Num$mix(double amount, double x, double y) { return (1.0 - amount) * x + amount * y; } -public CONSTFUNC bool Num$is_between(const double x, const double low, const double high) { - return low <= x && x <= high; -} -public CONSTFUNC double Num$clamped(double x, double low, double high) { - return (x <= low) ? low : (x >= high ? high : x); -} +public +CONSTFUNC bool Num$is_between(const double x, const double low, const double high) { return low <= x && x <= high; } +public +CONSTFUNC double Num$clamped(double x, double low, double high) { return (x <= low) ? low : (x >= high ? high : x); } -public OptionalNum_t Num$parse(Text_t text, Text_t *remainder) { +public +OptionalNum_t Num$parse(Text_t text, Text_t *remainder) { const char *str = Text$as_c_string(text); char *end = NULL; double d = strtod(str, &end); if (end > str) { - if (remainder) - *remainder = Text$from_str(end); - else if (*end != '\0') - return nan("none"); + if (remainder) *remainder = Text$from_str(end); + else if (*end != '\0') return nan("none"); return d; } else { if (remainder) *remainder = text; @@ -113,45 +115,54 @@ public OptionalNum_t Num$parse(Text_t text, Text_t *remainder) { } } -public CONSTFUNC bool Num$is_none(const void *n, const TypeInfo_t *info) -{ +public +CONSTFUNC bool Num$is_none(const void *n, const TypeInfo_t *info) { (void)info; - return isnan(*(Num_t*)n); -} - -public CONSTFUNC bool Num$isinf(double n) { return (fpclassify(n) == FP_INFINITE); } -public CONSTFUNC bool Num$finite(double n) { return (fpclassify(n) != FP_INFINITE); } -public CONSTFUNC bool Num$isnan(double n) { return (fpclassify(n) == FP_NAN); } - -public const TypeInfo_t Num$info = { - .size=sizeof(double), - .align=__alignof__(double), - .metamethods={ - .compare=Num$compare, - .equal=Num$equal, - .as_text=Num$as_text, - .is_none=Num$is_none, - }, + return isnan(*(Num_t *)n); +} + +public +CONSTFUNC bool Num$isinf(double n) { return (fpclassify(n) == FP_INFINITE); } +public +CONSTFUNC bool Num$finite(double n) { return (fpclassify(n) != FP_INFINITE); } +public +CONSTFUNC bool Num$isnan(double n) { return (fpclassify(n) == FP_NAN); } + +public +const TypeInfo_t Num$info = { + .size = sizeof(double), + .align = __alignof__(double), + .metamethods = + { + .compare = Num$compare, + .equal = Num$equal, + .as_text = Num$as_text, + .is_none = Num$is_none, + }, }; -public PUREFUNC Text_t Num32$as_text(const void *f, bool colorize, const TypeInfo_t *info) { +public +PUREFUNC Text_t Num32$as_text(const void *f, bool colorize, const TypeInfo_t *info) { (void)info; if (!f) return Text("Num32"); - double d = (double)(*(float*)f); + double d = (double)(*(float *)f); return Num$as_text(&d, colorize, &Num$info); } -public PUREFUNC int32_t Num32$compare(const void *x, const void *y, const TypeInfo_t *info) { +public +PUREFUNC int32_t Num32$compare(const void *x, const void *y, const TypeInfo_t *info) { (void)info; - return (*(float*)x > *(float*)y) - (*(float*)x < *(float*)y); -} + return (*(float *)x > *(float *)y) - (*(float *)x < *(float *)y); +} -public PUREFUNC bool Num32$equal(const void *x, const void *y, const TypeInfo_t *info) { +public +PUREFUNC bool Num32$equal(const void *x, const void *y, const TypeInfo_t *info) { (void)info; - return *(float*)x == *(float*)y; + return *(float *)x == *(float *)y; } -public CONSTFUNC bool Num32$near(float a, float b, float ratio, float absolute) { +public +CONSTFUNC bool Num32$near(float a, float b, float ratio, float absolute) { if (ratio < 0) ratio = 0; else if (ratio > 1) ratio = 1; @@ -166,17 +177,19 @@ public CONSTFUNC bool Num32$near(float a, float b, float ratio, float absolute) return (diff < epsilon); } -public Text_t Num32$percent(float f, float precision) { +public +Text_t Num32$percent(float f, float precision) { double d = 100. * (double)f; d = Num$with_precision(d, (double)precision); return Texts(Num$as_text(&d, false, &Num$info), Text("%")); } -public CONSTFUNC float Num32$with_precision(float num, float precision) { +public +CONSTFUNC float Num32$with_precision(float num, float precision) { if (precision == 0.0f) return num; // Precision will be, e.g. 0.01 or 100. if (precision < 1.f) { - float inv = roundf(1.f/precision); // Necessary to make the math work + float inv = roundf(1.f / precision); // Necessary to make the math work float k = num * inv; return roundf(k) / inv; } else { @@ -185,37 +198,35 @@ public CONSTFUNC float Num32$with_precision(float num, float precision) { } } -public CONSTFUNC float Num32$mod(float num, float modulus) { - // Euclidean division, see: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf +public +CONSTFUNC float Num32$mod(float num, float modulus) { + // Euclidean division, see: + // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf float r = remainderf(num, modulus); - r -= (r < 0) * (2*(modulus < 0) - 1) * modulus; + r -= (r < 0) * (2 * (modulus < 0) - 1) * modulus; return r; } -public CONSTFUNC float Num32$mod1(float num, float modulus) { - return 1.0f + Num32$mod(num-1, modulus); -} +public +CONSTFUNC float Num32$mod1(float num, float modulus) { return 1.0f + Num32$mod(num - 1, modulus); } -public CONSTFUNC float Num32$mix(float amount, float x, float y) { - return (1.0f-amount)*x + amount*y; -} +public +CONSTFUNC float Num32$mix(float amount, float x, float y) { return (1.0f - amount) * x + amount * y; } -public CONSTFUNC bool Num32$is_between(const float x, const float low, const float high) { - return low <= x && x <= high; -} +public +CONSTFUNC bool Num32$is_between(const float x, const float low, const float high) { return low <= x && x <= high; } -public CONSTFUNC float Num32$clamped(float x, float low, float high) { - return (x <= low) ? low : (x >= high ? high : x); -} +public +CONSTFUNC float Num32$clamped(float x, float low, float high) { return (x <= low) ? low : (x >= high ? high : x); } -public OptionalNum32_t Num32$parse(Text_t text, Text_t *remainder) { +public +OptionalNum32_t Num32$parse(Text_t text, Text_t *remainder) { const char *str = Text$as_c_string(text); char *end = NULL; double d = strtod(str, &end); if (end > str && end[0] == '\0') { if (remainder) *remainder = Text$from_str(end); - else if (*end != '\0') - return nan("none"); + else if (*end != '\0') return nan("none"); return d; } else { if (remainder) *remainder = text; @@ -223,25 +234,30 @@ public OptionalNum32_t Num32$parse(Text_t text, Text_t *remainder) { } } -public CONSTFUNC bool Num32$is_none(const void *n, const TypeInfo_t *info) -{ +public +CONSTFUNC bool Num32$is_none(const void *n, const TypeInfo_t *info) { (void)info; - return isnan(*(Num32_t*)n); -} - -public CONSTFUNC bool Num32$isinf(float n) { return (fpclassify(n) == FP_INFINITE); } -public CONSTFUNC bool Num32$finite(float n) { return (fpclassify(n) != FP_INFINITE); } -public CONSTFUNC bool Num32$isnan(float n) { return (fpclassify(n) == FP_NAN); } - -public const TypeInfo_t Num32$info = { - .size=sizeof(float), - .align=__alignof__(float), - .metamethods={ - .compare=Num32$compare, - .equal=Num32$equal, - .as_text=Num32$as_text, - .is_none=Num32$is_none, - }, + return isnan(*(Num32_t *)n); +} + +public +CONSTFUNC bool Num32$isinf(float n) { return (fpclassify(n) == FP_INFINITE); } +public +CONSTFUNC bool Num32$finite(float n) { return (fpclassify(n) != FP_INFINITE); } +public +CONSTFUNC bool Num32$isnan(float n) { return (fpclassify(n) == FP_NAN); } + +public +const TypeInfo_t Num32$info = { + .size = sizeof(float), + .align = __alignof__(float), + .metamethods = + { + .compare = Num32$compare, + .equal = Num32$equal, + .as_text = Num32$as_text, + .is_none = Num32$is_none, + }, }; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/optionals.c b/src/stdlib/optionals.c index 36683241..a6f943ae 100644 --- a/src/stdlib/optionals.c +++ b/src/stdlib/optionals.c @@ -9,22 +9,19 @@ #include "text.h" #include "util.h" -public PUREFUNC bool is_none(const void *obj, const TypeInfo_t *non_optional_type) -{ - if (non_optional_type->metamethods.is_none) - return non_optional_type->metamethods.is_none(obj, non_optional_type); +public +PUREFUNC bool is_none(const void *obj, const TypeInfo_t *non_optional_type) { + if (non_optional_type->metamethods.is_none) return non_optional_type->metamethods.is_none(obj, non_optional_type); const void *dest = (obj + non_optional_type->size); - return *(bool*)dest; + return *(bool *)dest; } -PUREFUNC public uint64_t Optional$hash(const void *obj, const TypeInfo_t *type) -{ +PUREFUNC public uint64_t Optional$hash(const void *obj, const TypeInfo_t *type) { return is_none(obj, type->OptionalInfo.type) ? 0 : generic_hash(obj, type->OptionalInfo.type); } -PUREFUNC public int32_t Optional$compare(const void *x, const void *y, const TypeInfo_t *type) -{ +PUREFUNC public int32_t Optional$compare(const void *x, const void *y, const TypeInfo_t *type) { if (x == y) return 0; bool x_is_null = is_none(x, type->OptionalInfo.type); bool y_is_null = is_none(y, type->OptionalInfo.type); @@ -33,8 +30,7 @@ PUREFUNC public int32_t Optional$compare(const void *x, const void *y, const Typ else return generic_compare(x, y, type->OptionalInfo.type); } -PUREFUNC public bool Optional$equal(const void *x, const void *y, const TypeInfo_t *type) -{ +PUREFUNC public bool Optional$equal(const void *x, const void *y, const TypeInfo_t *type) { if (x == y) return true; bool x_is_null = is_none(x, type->OptionalInfo.type); @@ -44,46 +40,37 @@ PUREFUNC public bool Optional$equal(const void *x, const void *y, const TypeInfo else return generic_equal(x, y, type->OptionalInfo.type); } -public Text_t Optional$as_text(const void *obj, bool colorize, const TypeInfo_t *type) -{ - if (!obj) - return Text$concat(generic_as_text(obj, colorize, type->OptionalInfo.type), Text("?")); +public +Text_t Optional$as_text(const void *obj, bool colorize, const TypeInfo_t *type) { + if (!obj) return Text$concat(generic_as_text(obj, colorize, type->OptionalInfo.type), Text("?")); - if (is_none(obj, type->OptionalInfo.type)) - return colorize ? Text("\x1b[31mnone\x1b[m") : Text("none"); + if (is_none(obj, type->OptionalInfo.type)) return colorize ? Text("\x1b[31mnone\x1b[m") : Text("none"); return generic_as_text(obj, colorize, type->OptionalInfo.type); } -public void Optional$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) -{ +public +void Optional$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) { bool has_value = !is_none(obj, type->OptionalInfo.type); assert(fputc((int)has_value, out) != EOF); - if (has_value) - _serialize(obj, out, pointers, type->OptionalInfo.type); + if (has_value) _serialize(obj, out, pointers, type->OptionalInfo.type); } -public void Optional$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type) -{ +public +void Optional$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type) { bool has_value = (bool)fgetc(in); const TypeInfo_t *nonnull = type->OptionalInfo.type; if (has_value) { memset(outval, 0, (size_t)type->size); _deserialize(in, outval, pointers, nonnull); } else { - if (nonnull->tag == TextInfo) - *(Text_t*)outval = NONE_TEXT; - else if (nonnull->tag == ListInfo) - *(List_t*)outval = (List_t){.length=-1}; - else if (nonnull->tag == TableInfo) - *(Table_t*)outval = (Table_t){.entries={.length=-1}}; - else if (nonnull == &Num$info) - *(double*)outval = (double)NAN; - else if (nonnull == &Num32$info) - *(float*)outval = (float)NAN; + if (nonnull->tag == TextInfo) *(Text_t *)outval = NONE_TEXT; + else if (nonnull->tag == ListInfo) *(List_t *)outval = (List_t){.length = -1}; + else if (nonnull->tag == TableInfo) *(Table_t *)outval = (Table_t){.entries = {.length = -1}}; + else if (nonnull == &Num$info) *(double *)outval = (double)NAN; + else if (nonnull == &Num32$info) *(float *)outval = (float)NAN; else if (nonnull->tag == StructInfo || (nonnull->tag == OpaqueInfo && type->size > nonnull->size)) memset(outval + type->size, -1, (size_t)(type->size - nonnull->size)); - else - memset(outval, 0, (size_t)type->size); + else memset(outval, 0, (size_t)type->size); } } diff --git a/src/stdlib/optionals.h b/src/stdlib/optionals.h index bd1f63b3..77ce6db4 100644 --- a/src/stdlib/optionals.h +++ b/src/stdlib/optionals.h @@ -9,13 +9,13 @@ #include "types.h" #include "util.h" -#define NONE_LIST ((List_t){.length=-1}) +#define NONE_LIST ((List_t){.length = -1}) #define NONE_BOOL ((OptionalBool_t)2) -#define NONE_INT ((OptionalInt_t){.small=0}) -#define NONE_TABLE ((OptionalTable_t){.entries.length=-1}) -#define NONE_CLOSURE ((OptionalClosure_t){.fn=NULL}) -#define NONE_TEXT ((OptionalText_t){.length=-1}) -#define NONE_PATH ((Path_t){.type=PATH_NONE}) +#define NONE_INT ((OptionalInt_t){.small = 0}) +#define NONE_TABLE ((OptionalTable_t){.entries.length = -1}) +#define NONE_CLOSURE ((OptionalClosure_t){.fn = NULL}) +#define NONE_TEXT ((OptionalText_t){.length = -1}) +#define NONE_PATH ((Path_t){.type = PATH_NONE}) PUREFUNC bool is_none(const void *obj, const TypeInfo_t *non_optional_type); PUREFUNC uint64_t Optional$hash(const void *obj, const TypeInfo_t *type); @@ -25,17 +25,21 @@ Text_t Optional$as_text(const void *obj, bool colorize, const TypeInfo_t *type); void Optional$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type); void Optional$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type); -#define Optional$metamethods { \ - .hash=Optional$hash, \ - .compare=Optional$compare, \ - .equal=Optional$equal, \ - .as_text=Optional$as_text, \ - .serialize=Optional$serialize, \ - .deserialize=Optional$deserialize, \ -} - -#define Optional$info(_size, _align, t) &((TypeInfo_t){.size=_size, .align=_align, \ - .tag=OptionalInfo, .OptionalInfo.type=t, \ - .metamethods=Optional$metamethods}) +#define Optional$metamethods \ + { \ + .hash = Optional$hash, \ + .compare = Optional$compare, \ + .equal = Optional$equal, \ + .as_text = Optional$as_text, \ + .serialize = Optional$serialize, \ + .deserialize = Optional$deserialize, \ + } + +#define Optional$info(_size, _align, t) \ + &((TypeInfo_t){.size = _size, \ + .align = _align, \ + .tag = OptionalInfo, \ + .OptionalInfo.type = t, \ + .metamethods = Optional$metamethods}) // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/paths.c b/src/stdlib/paths.c index a14b32c2..efe9f7ad 100644 --- a/src/stdlib/paths.c +++ b/src/stdlib/paths.c @@ -17,32 +17,30 @@ #include <unistd.h> #include <unistr.h> -#include "lists.h" #include "enums.h" #include "integers.h" +#include "lists.h" #include "optionals.h" #include "paths.h" +#include "print.h" #include "structs.h" #include "text.h" #include "types.h" #include "util.h" -#include "print.h" // Use inline version of the siphash code for performance: #include "siphash-internals.h" -static const Path_t HOME_PATH = {.type.$tag=PATH_HOME}, - ROOT_PATH = {.type.$tag=PATH_ABSOLUTE}, - CURDIR_PATH = {.type.$tag=PATH_RELATIVE}; +static const Path_t HOME_PATH = {.type.$tag = PATH_HOME}, ROOT_PATH = {.type.$tag = PATH_ABSOLUTE}, + CURDIR_PATH = {.type.$tag = PATH_RELATIVE}; -static void clean_components(List_t *components) -{ - for (int64_t i = 0; i < components->length; ) { - Text_t *component = (Text_t*)(components->data + i*components->stride); +static void clean_components(List_t *components) { + for (int64_t i = 0; i < components->length;) { + Text_t *component = (Text_t *)(components->data + i * components->stride); if (component->length == 0 || Text$equal_values(*component, Text("."))) { - List$remove_at(components, I(i+1), I(1), sizeof(Text_t)); + List$remove_at(components, I(i + 1), I(1), sizeof(Text_t)); } else if (i > 0 && Text$equal_values(*component, Text(".."))) { - Text_t *prev = (Text_t*)(components->data + (i-1)*components->stride); + Text_t *prev = (Text_t *)(components->data + (i - 1) * components->stride); if (!Text$equal_values(*prev, Text(".."))) { List$remove_at(components, I(i), I(2), sizeof(Text_t)); i -= 1; @@ -55,16 +53,15 @@ static void clean_components(List_t *components) } } -public Path_t Path$from_str(const char *str) -{ +public +Path_t Path$from_str(const char *str) { if (!str || str[0] == '\0' || streq(str, "/")) return ROOT_PATH; else if (streq(str, "~")) return HOME_PATH; else if (streq(str, ".")) return CURDIR_PATH; - if (strchr(str, ';') != NULL) - fail("Path has illegal character (semicolon): ", str); + if (strchr(str, ';') != NULL) fail("Path has illegal character (semicolon): ", str); - Path_t result = {.components={}}; + Path_t result = {.components = {}}; if (str[0] == '/') { result.type.$tag = PATH_ABSOLUTE; str += 1; @@ -83,12 +80,13 @@ public Path_t Path$from_str(const char *str) if (component_len > 0) { if (component_len == 1 && str[0] == '.') { // ignore /./ - } else if (component_len == 2 && strncmp(str, "..", 2) == 0 - && result.components.length > 1 - && !Text$equal_values(Text(".."), *(Text_t*)(result.components.data + result.components.stride*(result.components.length-1)))) { + } else if (component_len == 2 && strncmp(str, "..", 2) == 0 && result.components.length > 1 + && !Text$equal_values( + Text(".."), *(Text_t *)(result.components.data + + result.components.stride * (result.components.length - 1)))) { // Pop off /foo/baz/.. -> /foo List$remove_at(&result.components, I(result.components.length), I(1), sizeof(Text_t)); - } else { + } else { Text_t component = Text$from_strn(str, component_len); List$insert_value(&result.components, component, I(0), sizeof(Text_t)); } @@ -99,25 +97,23 @@ public Path_t Path$from_str(const char *str) return result; } -public Path_t Path$from_text(Text_t text) -{ - return Path$from_str(Text$as_c_string(text)); -} +public +Path_t Path$from_text(Text_t text) { return Path$from_str(Text$as_c_string(text)); } -public Path_t Path$expand_home(Path_t path) -{ +public +Path_t Path$expand_home(Path_t path) { if (path.type.$tag == PATH_HOME) { Path_t pwd = Path$from_str(getenv("HOME")); List_t components = List$concat(pwd.components, path.components, sizeof(Text_t)); assert(components.length == path.components.length + pwd.components.length); clean_components(&components); - path = (Path_t){.type.$tag=PATH_ABSOLUTE, .components=components}; + path = (Path_t){.type.$tag = PATH_ABSOLUTE, .components = components}; } return path; } -public Path_t Path$_concat(int n, Path_t items[n]) -{ +public +Path_t Path$_concat(int n, Path_t items[n]) { assert(n > 0); Path_t result = items[0]; LIST_INCREF(result.components); @@ -130,10 +126,11 @@ public Path_t Path$_concat(int n, Path_t items[n]) return result; } -public Path_t Path$resolved(Path_t path, Path_t relative_to) -{ - if (path.type.$tag == PATH_RELATIVE && !(relative_to.type.$tag == PATH_RELATIVE && relative_to.components.length == 0)) { - Path_t result = {.type.$tag=relative_to.type.$tag}; +public +Path_t Path$resolved(Path_t path, Path_t relative_to) { + if (path.type.$tag == PATH_RELATIVE + && !(relative_to.type.$tag == PATH_RELATIVE && relative_to.components.length == 0)) { + Path_t result = {.type.$tag = relative_to.type.$tag}; result.components = relative_to.components; LIST_INCREF(result.components); List$insert_all(&result.components, path.components, I(0), sizeof(Text_t)); @@ -143,87 +140,86 @@ public Path_t Path$resolved(Path_t path, Path_t relative_to) return path; } -public Path_t Path$relative_to(Path_t path, Path_t relative_to) -{ +public +Path_t Path$relative_to(Path_t path, Path_t relative_to) { if (path.type.$tag != relative_to.type.$tag) - fail("Cannot create a path relative to a different path with a mismatching type: (", path, ") relative to (", relative_to, ")"); + fail("Cannot create a path relative to a different path with a mismatching type: (", path, ") relative to (", + relative_to, ")"); - Path_t result = {.type.$tag=PATH_RELATIVE}; + Path_t result = {.type.$tag = PATH_RELATIVE}; int64_t shared = 0; for (; shared < path.components.length && shared < relative_to.components.length; shared++) { - Text_t *p = (Text_t*)(path.components.data + shared*path.components.stride); - Text_t *r = (Text_t*)(relative_to.components.data + shared*relative_to.components.stride); - if (!Text$equal_values(*p, *r)) - break; + Text_t *p = (Text_t *)(path.components.data + shared * path.components.stride); + Text_t *r = (Text_t *)(relative_to.components.data + shared * relative_to.components.stride); + if (!Text$equal_values(*p, *r)) break; } for (int64_t i = shared; i < relative_to.components.length; i++) List$insert_value(&result.components, Text(".."), I(1), sizeof(Text_t)); for (int64_t i = shared; i < path.components.length; i++) { - Text_t *p = (Text_t*)(path.components.data + i*path.components.stride); + Text_t *p = (Text_t *)(path.components.data + i * path.components.stride); List$insert(&result.components, p, I(0), sizeof(Text_t)); } - //clean_components(&result.components); + // clean_components(&result.components); return result; } -public bool Path$exists(Path_t path) -{ +public +bool Path$exists(Path_t path) { path = Path$expand_home(path); struct stat sb; return (stat(Path$as_c_string(path), &sb) == 0); } -static INLINE int path_stat(Path_t path, bool follow_symlinks, struct stat *sb) -{ +static INLINE int path_stat(Path_t path, bool follow_symlinks, struct stat *sb) { path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); return follow_symlinks ? stat(path_str, sb) : lstat(path_str, sb); } -public bool Path$is_file(Path_t path, bool follow_symlinks) -{ +public +bool Path$is_file(Path_t path, bool follow_symlinks) { struct stat sb; int status = path_stat(path, follow_symlinks, &sb); if (status != 0) return false; return (sb.st_mode & S_IFMT) == S_IFREG; } -public bool Path$is_directory(Path_t path, bool follow_symlinks) -{ +public +bool Path$is_directory(Path_t path, bool follow_symlinks) { struct stat sb; int status = path_stat(path, follow_symlinks, &sb); if (status != 0) return false; return (sb.st_mode & S_IFMT) == S_IFDIR; } -public bool Path$is_pipe(Path_t path, bool follow_symlinks) -{ +public +bool Path$is_pipe(Path_t path, bool follow_symlinks) { struct stat sb; int status = path_stat(path, follow_symlinks, &sb); if (status != 0) return false; return (sb.st_mode & S_IFMT) == S_IFIFO; } -public bool Path$is_socket(Path_t path, bool follow_symlinks) -{ +public +bool Path$is_socket(Path_t path, bool follow_symlinks) { struct stat sb; int status = path_stat(path, follow_symlinks, &sb); if (status != 0) return false; return (sb.st_mode & S_IFMT) == S_IFSOCK; } -public bool Path$is_symlink(Path_t path) -{ +public +bool Path$is_symlink(Path_t path) { struct stat sb; int status = path_stat(path, false, &sb); if (status != 0) return false; return (sb.st_mode & S_IFMT) == S_IFLNK; } -public bool Path$can_read(Path_t path) -{ +public +bool Path$can_read(Path_t path) { path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); #ifdef _GNU_SOURCE @@ -233,8 +229,8 @@ public bool Path$can_read(Path_t path) #endif } -public bool Path$can_write(Path_t path) -{ +public +bool Path$can_write(Path_t path) { path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); #ifdef _GNU_SOURCE @@ -244,8 +240,8 @@ public bool Path$can_write(Path_t path) #endif } -public bool Path$can_execute(Path_t path) -{ +public +bool Path$can_execute(Path_t path) { path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); #ifdef _GNU_SOURCE @@ -255,93 +251,86 @@ public bool Path$can_execute(Path_t path) #endif } -public OptionalInt64_t Path$modified(Path_t path, bool follow_symlinks) -{ +public +OptionalInt64_t Path$modified(Path_t path, bool follow_symlinks) { struct stat sb; int status = path_stat(path, follow_symlinks, &sb); if (status != 0) return NONE_INT64; - return (OptionalInt64_t){.value=(int64_t)sb.st_mtime}; + return (OptionalInt64_t){.value = (int64_t)sb.st_mtime}; } -public OptionalInt64_t Path$accessed(Path_t path, bool follow_symlinks) -{ +public +OptionalInt64_t Path$accessed(Path_t path, bool follow_symlinks) { struct stat sb; int status = path_stat(path, follow_symlinks, &sb); if (status != 0) return NONE_INT64; - return (OptionalInt64_t){.value=(int64_t)sb.st_atime}; + return (OptionalInt64_t){.value = (int64_t)sb.st_atime}; } -public OptionalInt64_t Path$changed(Path_t path, bool follow_symlinks) -{ +public +OptionalInt64_t Path$changed(Path_t path, bool follow_symlinks) { struct stat sb; int status = path_stat(path, follow_symlinks, &sb); if (status != 0) return NONE_INT64; - return (OptionalInt64_t){.value=(int64_t)sb.st_ctime}; + return (OptionalInt64_t){.value = (int64_t)sb.st_ctime}; } -static void _write(Path_t path, List_t bytes, int mode, int permissions) -{ +static void _write(Path_t path, List_t bytes, int mode, int permissions) { path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); int fd = open(path_str, mode, permissions); - if (fd == -1) - fail("Could not write to file: ", path_str, "\n", strerror(errno)); + if (fd == -1) fail("Could not write to file: ", path_str, "\n", strerror(errno)); - if (bytes.stride != 1) - List$compact(&bytes, 1); + if (bytes.stride != 1) List$compact(&bytes, 1); ssize_t written = write(fd, bytes.data, (size_t)bytes.length); - if (written != (ssize_t)bytes.length) - fail("Could not write to file: ", path_str, "\n", strerror(errno)); + if (written != (ssize_t)bytes.length) fail("Could not write to file: ", path_str, "\n", strerror(errno)); close(fd); } -public void Path$write(Path_t path, Text_t text, int permissions) -{ +public +void Path$write(Path_t path, Text_t text, int permissions) { List_t bytes = Text$utf8_bytes(text); _write(path, bytes, O_WRONLY | O_CREAT | O_TRUNC, permissions); } -public void Path$write_bytes(Path_t path, List_t bytes, int permissions) -{ +public +void Path$write_bytes(Path_t path, List_t bytes, int permissions) { _write(path, bytes, O_WRONLY | O_CREAT | O_TRUNC, permissions); } -public void Path$append(Path_t path, Text_t text, int permissions) -{ +public +void Path$append(Path_t path, Text_t text, int permissions) { List_t bytes = Text$utf8_bytes(text); _write(path, bytes, O_WRONLY | O_APPEND | O_CREAT, permissions); } -public void Path$append_bytes(Path_t path, List_t bytes, int permissions) -{ +public +void Path$append_bytes(Path_t path, List_t bytes, int permissions) { _write(path, bytes, O_WRONLY | O_APPEND | O_CREAT, permissions); } -public OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t count) -{ +public +OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t count) { path = Path$expand_home(path); int fd = open(Path$as_c_string(path), O_RDONLY); - if (fd == -1) - return NONE_LIST; + if (fd == -1) return NONE_LIST; struct stat sb; - if (fstat(fd, &sb) != 0) - return NONE_LIST; + if (fstat(fd, &sb) != 0) return NONE_LIST; int64_t const target_count = count.small ? Int64$from_int(count, false) : INT64_MAX; - if (target_count < 0) - fail("Cannot read a negative number of bytes!"); + if (target_count < 0) fail("Cannot read a negative number of bytes!"); if ((sb.st_mode & S_IFMT) == S_IFREG) { // Use memory mapping if it's a real file: const char *mem = mmap(NULL, (size_t)sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - char *content = GC_MALLOC_ATOMIC((size_t)sb.st_size+1); + char *content = GC_MALLOC_ATOMIC((size_t)sb.st_size + 1); memcpy(content, mem, (size_t)sb.st_size); content[sb.st_size] = '\0'; close(fd); if (count.small && (int64_t)sb.st_size < target_count) fail("Could not read ", target_count, " bytes from ", path, " (only got ", (uint64_t)sb.st_size, ")"); int64_t len = count.small ? target_count : (int64_t)sb.st_size; - return (List_t){.data=content, .atomic=1, .stride=1, .length=len}; + return (List_t){.data = content, .atomic = 1, .stride = 1, .length = len}; } else { size_t capacity = 256, len = 0; char *content = GC_MALLOC_ATOMIC(capacity); @@ -354,8 +343,7 @@ public OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t count) close(fd); return NONE_LIST; } else if (just_read == 0) { - if (errno == EAGAIN || errno == EINTR) - continue; + if (errno == EAGAIN || errno == EINTR) continue; break; } count_remaining -= (int64_t)just_read; @@ -370,19 +358,19 @@ public OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t count) close(fd); if (count.small != 0 && (int64_t)len < target_count) fail("Could not read ", target_count, " bytes from ", path, " (only got ", (uint64_t)len, ")"); - return (List_t){.data=content, .atomic=1, .stride=1, .length=(int64_t)len}; + return (List_t){.data = content, .atomic = 1, .stride = 1, .length = (int64_t)len}; } } -public OptionalText_t Path$read(Path_t path) -{ +public +OptionalText_t Path$read(Path_t path) { List_t bytes = Path$read_bytes(path, NONE_INT); if (bytes.length < 0) return NONE_TEXT; return Text$from_bytes(bytes); } -public OptionalText_t Path$owner(Path_t path, bool follow_symlinks) -{ +public +OptionalText_t Path$owner(Path_t path, bool follow_symlinks) { struct stat sb; int status = path_stat(path, follow_symlinks, &sb); if (status != 0) return NONE_TEXT; @@ -390,8 +378,8 @@ public OptionalText_t Path$owner(Path_t path, bool follow_symlinks) return pw ? Text$from_str(pw->pw_name) : NONE_TEXT; } -public OptionalText_t Path$group(Path_t path, bool follow_symlinks) -{ +public +OptionalText_t Path$group(Path_t path, bool follow_symlinks) { struct stat sb; int status = path_stat(path, follow_symlinks, &sb); if (status != 0) return NONE_TEXT; @@ -399,8 +387,8 @@ public OptionalText_t Path$group(Path_t path, bool follow_symlinks) return gr ? Text$from_str(gr->gr_name) : NONE_TEXT; } -public void Path$set_owner(Path_t path, OptionalText_t owner, OptionalText_t group, bool follow_symlinks) -{ +public +void Path$set_owner(Path_t path, OptionalText_t owner, OptionalText_t group, bool follow_symlinks) { uid_t owner_id = (uid_t)-1; if (owner.length >= 0) { struct passwd *pwd = getpwnam(Text$as_c_string(owner)); @@ -416,38 +404,34 @@ public void Path$set_owner(Path_t path, OptionalText_t owner, OptionalText_t gro } const char *path_str = Path$as_c_string(path); int result = follow_symlinks ? chown(path_str, owner_id, group_id) : lchown(path_str, owner_id, group_id); - if (result < 0) - fail("Could not set owner!"); + if (result < 0) fail("Could not set owner!"); } -static int _remove_files(const char *path, const struct stat *sbuf, int type, struct FTW *ftwb) -{ +static int _remove_files(const char *path, const struct stat *sbuf, int type, struct FTW *ftwb) { (void)sbuf, (void)ftwb; switch (type) { - case FTW_F: case FTW_SL: case FTW_SLN: + case FTW_F: + case FTW_SL: + case FTW_SLN: if (remove(path) < 0) { fail("Could not remove file: ", path, " (", strerror(errno), ")"); return -1; } return 0; case FTW_DP: - if (rmdir(path) != 0) - fail("Could not remove directory: ", path, " (", strerror(errno), ")"); + if (rmdir(path) != 0) fail("Could not remove directory: ", path, " (", strerror(errno), ")"); return 0; - default: - fail("Could not remove path: ", path, " (not a file or directory)"); - return -1; + default: fail("Could not remove path: ", path, " (not a file or directory)"); return -1; } } -public void Path$remove(Path_t path, bool ignore_missing) -{ +public +void Path$remove(Path_t path, bool ignore_missing) { path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); struct stat sb; if (lstat(path_str, &sb) != 0) { - if (!ignore_missing) - fail("Could not remove file: ", path_str, " (", strerror(errno), ")"); + if (!ignore_missing) fail("Could not remove file: ", path_str, " (", strerror(errno), ")"); return; } @@ -456,48 +440,40 @@ public void Path$remove(Path_t path, bool ignore_missing) fail("Could not remove file: ", path_str, " (", strerror(errno), ")"); } else if ((sb.st_mode & S_IFMT) == S_IFDIR) { const int num_open_fd = 10; - if (nftw(path_str, _remove_files, num_open_fd, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) < 0) + if (nftw(path_str, _remove_files, num_open_fd, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) < 0) fail("Could not remove directory: %s (%s)", path_str, strerror(errno)); } else { fail("Could not remove path: ", path_str, " (not a file or directory)"); } } -public void Path$create_directory(Path_t path, int permissions) -{ +public +void Path$create_directory(Path_t path, int permissions) { path = Path$expand_home(path); const char *c_path = Path$as_c_string(path); int status = mkdir(c_path, (mode_t)permissions); - if (status != 0 && errno != EEXIST) - fail("Could not create directory: ", c_path, " (", strerror(errno), ")"); + if (status != 0 && errno != EEXIST) fail("Could not create directory: ", c_path, " (", strerror(errno), ")"); } -static List_t _filtered_children(Path_t path, bool include_hidden, mode_t filter) -{ +static List_t _filtered_children(Path_t path, bool include_hidden, mode_t filter) { path = Path$expand_home(path); struct dirent *dir; List_t children = {}; const char *path_str = Path$as_c_string(path); size_t path_len = strlen(path_str); DIR *d = opendir(path_str); - if (!d) - fail("Could not open directory: ", path, " (", strerror(errno), ")"); + if (!d) fail("Could not open directory: ", path, " (", strerror(errno), ")"); - if (path_str[path_len-1] == '/') - --path_len; + if (path_str[path_len - 1] == '/') --path_len; while ((dir = readdir(d)) != NULL) { - if (!include_hidden && dir->d_name[0] == '.') - continue; - if (streq(dir->d_name, ".") || streq(dir->d_name, "..")) - continue; + if (!include_hidden && dir->d_name[0] == '.') continue; + if (streq(dir->d_name, ".") || streq(dir->d_name, "..")) continue; const char *child_str = String(string_slice(path_str, path_len), "/", dir->d_name); struct stat sb; - if (stat(child_str, &sb) != 0) - continue; - if (!((sb.st_mode & S_IFMT) & filter)) - continue; + if (stat(child_str, &sb) != 0) continue; + if (!((sb.st_mode & S_IFMT) & filter)) continue; Path_t child = Path$from_str(child_str); List$insert(&children, &child, I(0), sizeof(Path_t)); @@ -506,23 +482,19 @@ static List_t _filtered_children(Path_t path, bool include_hidden, mode_t filter return children; } -public List_t Path$children(Path_t path, bool include_hidden) -{ - return _filtered_children(path, include_hidden, (mode_t)-1); -} +public +List_t Path$children(Path_t path, bool include_hidden) { return _filtered_children(path, include_hidden, (mode_t)-1); } -public List_t Path$files(Path_t path, bool include_hidden) -{ - return _filtered_children(path, include_hidden, S_IFREG); -} +public +List_t Path$files(Path_t path, bool include_hidden) { return _filtered_children(path, include_hidden, S_IFREG); } -public List_t Path$subdirectories(Path_t path, bool include_hidden) -{ +public +List_t Path$subdirectories(Path_t path, bool include_hidden) { return _filtered_children(path, include_hidden, S_IFDIR); } -public Path_t Path$unique_directory(Path_t path) -{ +public +Path_t Path$unique_directory(Path_t path) { path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); size_t len = strlen(path_str); @@ -530,15 +502,14 @@ public Path_t Path$unique_directory(Path_t path) char buf[PATH_MAX] = {}; memcpy(buf, path_str, len); buf[len] = '\0'; - if (buf[len-1] == '/') - buf[--len] = '\0'; + if (buf[len - 1] == '/') buf[--len] = '\0'; char *created = mkdtemp(buf); if (!created) fail("Failed to create temporary directory: ", path_str, " (", strerror(errno), ")"); return Path$from_str(created); } -public Path_t Path$write_unique_bytes(Path_t path, List_t bytes) -{ +public +Path_t Path$write_unique_bytes(Path_t path, List_t bytes) { path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); size_t len = strlen(path_str); @@ -554,82 +525,73 @@ public Path_t Path$write_unique_bytes(Path_t path, List_t bytes) ++suffixlen; int fd = mkstemps(buf, suffixlen); - if (fd == -1) - fail("Could not write to unique file: ", buf, "\n", strerror(errno)); + if (fd == -1) fail("Could not write to unique file: ", buf, "\n", strerror(errno)); - if (bytes.stride != 1) - List$compact(&bytes, 1); + if (bytes.stride != 1) List$compact(&bytes, 1); ssize_t written = write(fd, bytes.data, (size_t)bytes.length); - if (written != (ssize_t)bytes.length) - fail("Could not write to file: ", buf, "\n", strerror(errno)); + if (written != (ssize_t)bytes.length) fail("Could not write to file: ", buf, "\n", strerror(errno)); close(fd); return Path$from_str(buf); } -public Path_t Path$write_unique(Path_t path, Text_t text) -{ - return Path$write_unique_bytes(path, Text$utf8_bytes(text)); -} +public +Path_t Path$write_unique(Path_t path, Text_t text) { return Path$write_unique_bytes(path, Text$utf8_bytes(text)); } -public Path_t Path$parent(Path_t path) -{ +public +Path_t Path$parent(Path_t path) { if (path.type.$tag == PATH_ABSOLUTE && path.components.length == 0) { return path; - } else if (path.components.length > 0 && !Text$equal_values(*(Text_t*)(path.components.data + path.components.stride*(path.components.length-1)), - Text(".."))) { - return (Path_t){.type.$tag=path.type.$tag, .components=List$slice(path.components, I(1), I(-2))}; + } else if (path.components.length > 0 + && !Text$equal_values( + *(Text_t *)(path.components.data + path.components.stride * (path.components.length - 1)), + Text(".."))) { + return (Path_t){.type.$tag = path.type.$tag, .components = List$slice(path.components, I(1), I(-2))}; } else { - Path_t result = {.type.$tag=path.type.$tag, .components=path.components}; + Path_t result = {.type.$tag = path.type.$tag, .components = path.components}; LIST_INCREF(result.components); List$insert_value(&result.components, Text(".."), I(0), sizeof(Text_t)); return result; } } -public PUREFUNC Text_t Path$base_name(Path_t path) -{ +public +PUREFUNC Text_t Path$base_name(Path_t path) { if (path.components.length >= 1) - return *(Text_t*)(path.components.data + path.components.stride*(path.components.length-1)); - else if (path.type.$tag == PATH_HOME) - return Text("~"); - else if (path.type.$tag == PATH_RELATIVE) - return Text("."); - else - return EMPTY_TEXT; + return *(Text_t *)(path.components.data + path.components.stride * (path.components.length - 1)); + else if (path.type.$tag == PATH_HOME) return Text("~"); + else if (path.type.$tag == PATH_RELATIVE) return Text("."); + else return EMPTY_TEXT; } -public Text_t Path$extension(Path_t path, bool full) -{ +public +Text_t Path$extension(Path_t path, bool full) { const char *base = Text$as_c_string(Path$base_name(path)); const char *dot = full ? strchr(base + 1, '.') : strrchr(base + 1, '.'); const char *extension = dot ? dot + 1 : ""; return Text$from_str(extension); } -public bool Path$has_extension(Path_t path, Text_t extension) -{ - if (path.components.length < 2) - return extension.length == 0; +public +bool Path$has_extension(Path_t path, Text_t extension) { + if (path.components.length < 2) return extension.length == 0; - Text_t last = *(Text_t*)(path.components.data + path.components.stride*(path.components.length-1)); + Text_t last = *(Text_t *)(path.components.data + path.components.stride * (path.components.length - 1)); if (extension.length == 0) return !Text$has(Text$from(last, I(2)), Text(".")) || Text$equal_values(last, Text("..")); - if (!Text$starts_with(extension, Text("."), NULL)) - extension = Texts(Text("."), extension); + if (!Text$starts_with(extension, Text("."), NULL)) extension = Texts(Text("."), extension); return Text$ends_with(Text$from(last, I(2)), extension, NULL); } -public Path_t Path$child(Path_t path, Text_t name) -{ - if (Text$has(name, Text("/")) || Text$has(name, Text(";"))) - fail("Path name has invalid characters: ", name); +public +Path_t Path$child(Path_t path, Text_t name) { + if (Text$has(name, Text("/")) || Text$has(name, Text(";"))) fail("Path name has invalid characters: ", name); Path_t result = { - .type.$tag=path.type.$tag, - .components=path.components, + .type.$tag = path.type.$tag, + .components = path.components, }; LIST_INCREF(result.components); List$insert(&result.components, &name, I(0), sizeof(Text_t)); @@ -637,31 +599,27 @@ public Path_t Path$child(Path_t path, Text_t name) return result; } -public Path_t Path$sibling(Path_t path, Text_t name) -{ - return Path$child(Path$parent(path), name); -} +public +Path_t Path$sibling(Path_t path, Text_t name) { return Path$child(Path$parent(path), name); } -public Path_t Path$with_extension(Path_t path, Text_t extension, bool replace) -{ - if (path.components.length == 0) - fail("A path with no components can't have an extension!"); +public +Path_t Path$with_extension(Path_t path, Text_t extension, bool replace) { + if (path.components.length == 0) fail("A path with no components can't have an extension!"); if (Text$has(extension, Text("/")) || Text$has(extension, Text(";"))) fail("Path extension has invalid characters: ", extension); Path_t result = { - .type.$tag=path.type.$tag, - .components=path.components, + .type.$tag = path.type.$tag, + .components = path.components, }; LIST_INCREF(result.components); - Text_t last = *(Text_t*)(path.components.data + path.components.stride*(path.components.length-1)); + Text_t last = *(Text_t *)(path.components.data + path.components.stride * (path.components.length - 1)); List$remove_at(&result.components, I(-1), I(1), sizeof(Text_t)); if (replace) { const char *base = Text$as_c_string(last); const char *dot = strchr(base + 1, '.'); - if (dot) - last = Text$from_strn(base, (size_t)(dot - base)); + if (dot) last = Text$from_strn(base, (size_t)(dot - base)); } last = Text$concat(last, extension); @@ -669,16 +627,14 @@ public Path_t Path$with_extension(Path_t path, Text_t extension, bool replace) return result; } -static void _line_reader_cleanup(FILE **f) -{ +static void _line_reader_cleanup(FILE **f) { if (f && *f) { fclose(*f); *f = NULL; } } -static Text_t _next_line(FILE **f) -{ +static Text_t _next_line(FILE **f) { if (!f || !*f) return NONE_TEXT; char *line = NULL; @@ -689,43 +645,41 @@ static Text_t _next_line(FILE **f) return NONE_TEXT; } - while (len > 0 && (line[len-1] == '\r' || line[len-1] == '\n')) + while (len > 0 && (line[len - 1] == '\r' || line[len - 1] == '\n')) --len; - if (u8_check((uint8_t*)line, (size_t)len) != NULL) - fail("Invalid UTF8!"); + if (u8_check((uint8_t *)line, (size_t)len) != NULL) fail("Invalid UTF8!"); Text_t line_text = Text$from_strn(line, (size_t)len); free(line); return line_text; } -public OptionalClosure_t Path$by_line(Path_t path) -{ +public +OptionalClosure_t Path$by_line(Path_t path) { path = Path$expand_home(path); FILE *f = fopen(Path$as_c_string(path), "r"); - if (f == NULL) - return NONE_CLOSURE; + if (f == NULL) return NONE_CLOSURE; - FILE **wrapper = GC_MALLOC(sizeof(FILE*)); + FILE **wrapper = GC_MALLOC(sizeof(FILE *)); *wrapper = f; - GC_register_finalizer(wrapper, (void*)_line_reader_cleanup, NULL, NULL, NULL); - return (Closure_t){.fn=(void*)_next_line, .userdata=wrapper}; + GC_register_finalizer(wrapper, (void *)_line_reader_cleanup, NULL, NULL, NULL); + return (Closure_t){.fn = (void *)_next_line, .userdata = wrapper}; } -public List_t Path$glob(Path_t path) -{ +public +List_t Path$glob(Path_t path) { glob_t glob_result; int status = glob(Path$as_c_string(path), GLOB_BRACE | GLOB_TILDE, NULL, &glob_result); - if (status != 0 && status != GLOB_NOMATCH) - fail("Failed to perform globbing"); + if (status != 0 && status != GLOB_NOMATCH) fail("Failed to perform globbing"); List_t glob_files = {}; for (size_t i = 0; i < glob_result.gl_pathc; i++) { size_t len = strlen(glob_result.gl_pathv[i]); - if ((len >= 2 && glob_result.gl_pathv[i][len-1] == '.' && glob_result.gl_pathv[i][len-2] == '/') - || (len >= 2 && glob_result.gl_pathv[i][len-1] == '.' && glob_result.gl_pathv[i][len-2] == '.' && glob_result.gl_pathv[i][len-3] == '/')) + if ((len >= 2 && glob_result.gl_pathv[i][len - 1] == '.' && glob_result.gl_pathv[i][len - 2] == '/') + || (len >= 2 && glob_result.gl_pathv[i][len - 1] == '.' && glob_result.gl_pathv[i][len - 2] == '.' + && glob_result.gl_pathv[i][len - 3] == '/')) continue; Path_t p = Path$from_str(glob_result.gl_pathv[i]); List$insert(&glob_files, &p, I(0), sizeof(Path_t)); @@ -733,52 +687,51 @@ public List_t Path$glob(Path_t path) return glob_files; } -public Path_t Path$current_dir(void) -{ +public +Path_t Path$current_dir(void) { char cwd[PATH_MAX]; - if (getcwd(cwd, sizeof(cwd)) == NULL) - fail("Could not get current working directory"); + if (getcwd(cwd, sizeof(cwd)) == NULL) fail("Could not get current working directory"); return Path$from_str(cwd); } -public PUREFUNC uint64_t Path$hash(const void *obj, const TypeInfo_t *type) -{ +public +PUREFUNC uint64_t Path$hash(const void *obj, const TypeInfo_t *type) { (void)type; - Path_t *path = (Path_t*)obj; + Path_t *path = (Path_t *)obj; siphash sh; siphashinit(&sh, (uint64_t)path->type.$tag); for (int64_t i = 0; i < path->components.length; i++) { - uint64_t item_hash = Text$hash(path->components.data + i*path->components.stride, &Text$info); + uint64_t item_hash = Text$hash(path->components.data + i * path->components.stride, &Text$info); siphashadd64bits(&sh, item_hash); } return siphashfinish_last_part(&sh, (uint64_t)path->components.length); } -public PUREFUNC int32_t Path$compare(const void *va, const void *vb, const TypeInfo_t *type) -{ +public +PUREFUNC int32_t Path$compare(const void *va, const void *vb, const TypeInfo_t *type) { (void)type; - Path_t *a = (Path_t*)va, *b = (Path_t*)vb; + Path_t *a = (Path_t *)va, *b = (Path_t *)vb; int diff = ((int)a->type.$tag - (int)b->type.$tag); if (diff != 0) return diff; return List$compare(&a->components, &b->components, List$info(&Text$info)); } -public PUREFUNC bool Path$equal(const void *va, const void *vb, const TypeInfo_t *type) -{ +public +PUREFUNC bool Path$equal(const void *va, const void *vb, const TypeInfo_t *type) { (void)type; - Path_t *a = (Path_t*)va, *b = (Path_t*)vb; + Path_t *a = (Path_t *)va, *b = (Path_t *)vb; if (a->type.$tag != b->type.$tag) return false; return List$equal(&a->components, &b->components, List$info(&Text$info)); } -public PUREFUNC bool Path$equal_values(Path_t a, Path_t b) -{ +public +PUREFUNC bool Path$equal_values(Path_t a, Path_t b) { if (a.type.$tag != b.type.$tag) return false; return List$equal(&a.components, &b.components, List$info(&Text$info)); } -public int Path$print(FILE *f, Path_t path) -{ +public +int Path$print(FILE *f, Path_t path) { if (path.components.length == 0) { if (path.type.$tag == PATH_ABSOLUTE) return fputs("/", f); else if (path.type.$tag == PATH_RELATIVE) return fputs(".", f); @@ -791,91 +744,86 @@ public int Path$print(FILE *f, Path_t path) } else if (path.type.$tag == PATH_HOME) { n += fputs("~/", f); } else if (path.type.$tag == PATH_RELATIVE) { - if (!Text$equal_values(*(Text_t*)path.components.data, Text(".."))) - n += fputs("./", f); + if (!Text$equal_values(*(Text_t *)path.components.data, Text(".."))) n += fputs("./", f); } for (int64_t i = 0; i < path.components.length; i++) { - Text_t *comp = (Text_t*)(path.components.data + i*path.components.stride); + Text_t *comp = (Text_t *)(path.components.data + i * path.components.stride); n += Text$print(f, *comp); - if (i + 1 < path.components.length) - n += fputc('/', f); + if (i + 1 < path.components.length) n += fputc('/', f); } return n; } -public const char *Path$as_c_string(Path_t path) -{ - return String(path); -} +public +const char *Path$as_c_string(Path_t path) { return String(path); } -public Text_t Path$as_text(const void *obj, bool color, const TypeInfo_t *type) -{ +public +Text_t Path$as_text(const void *obj, bool color, const TypeInfo_t *type) { (void)type; if (!obj) return Text("Path"); - Path_t *path = (Path_t*)obj; + Path_t *path = (Path_t *)obj; Text_t text = Text$join(Text("/"), path->components); - if (path->type.$tag == PATH_HOME) - text = Text$concat(path->components.length > 0 ? Text("~/") : Text("~"), text); - else if (path->type.$tag == PATH_ABSOLUTE) - text = Text$concat(Text("/"), text); - else if (path->type.$tag == PATH_RELATIVE && (path->components.length == 0 || !Text$equal_values(*(Text_t*)(path->components.data), Text("..")))) + if (path->type.$tag == PATH_HOME) text = Text$concat(path->components.length > 0 ? Text("~/") : Text("~"), text); + else if (path->type.$tag == PATH_ABSOLUTE) text = Text$concat(Text("/"), text); + else if (path->type.$tag == PATH_RELATIVE + && (path->components.length == 0 || !Text$equal_values(*(Text_t *)(path->components.data), Text("..")))) text = Text$concat(path->components.length > 0 ? Text("./") : Text("."), text); - if (color) - text = Texts(Text("\033[32;1m"), text, Text("\033[m")); + if (color) text = Texts(Text("\033[32;1m"), text, Text("\033[m")); return text; } -public CONSTFUNC bool Path$is_none(const void *obj, const TypeInfo_t *type) -{ +public +CONSTFUNC bool Path$is_none(const void *obj, const TypeInfo_t *type) { (void)type; - return ((Path_t*)obj)->type.$tag == PATH_NONE; + return ((Path_t *)obj)->type.$tag == PATH_NONE; } -public void Path$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) -{ +public +void Path$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) { (void)type; - Path_t *path = (Path_t*)obj; + Path_t *path = (Path_t *)obj; fputc((int)path->type.$tag, out); List$serialize(&path->components, out, pointers, List$info(&Text$info)); } -public void Path$deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *type) -{ +public +void Path$deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *type) { (void)type; Path_t path = {}; path.type.$tag = fgetc(in); List$deserialize(in, &path.components, pointers, List$info(&Text$info)); - *(Path_t*)obj = path; -} - -public const TypeInfo_t Path$info = { - .size=sizeof(Path_t), - .align=__alignof__(Path_t), - .tag=OpaqueInfo, - .metamethods={ - .as_text=Path$as_text, - .hash=Path$hash, - .compare=Path$compare, - .equal=Path$equal, - .is_none=Path$is_none, - .serialize=Path$serialize, - .deserialize=Path$deserialize, - } -}; - -public const TypeInfo_t PathType$info = { - .size=sizeof(PathType_t), - .align=__alignof__(PathType_t), - .metamethods=PackedDataEnum$metamethods, - .tag=EnumInfo, - .EnumInfo={ - .name="PathType", - .num_tags=3, - .tags=((NamedType_t[3]){{.name="Relative"}, {.name="Absolute"}, {.name="Home"}}), - }, + *(Path_t *)obj = path; +} + +public +const TypeInfo_t Path$info = {.size = sizeof(Path_t), + .align = __alignof__(Path_t), + .tag = OpaqueInfo, + .metamethods = { + .as_text = Path$as_text, + .hash = Path$hash, + .compare = Path$compare, + .equal = Path$equal, + .is_none = Path$is_none, + .serialize = Path$serialize, + .deserialize = Path$deserialize, + }}; + +public +const TypeInfo_t PathType$info = { + .size = sizeof(PathType_t), + .align = __alignof__(PathType_t), + .metamethods = PackedDataEnum$metamethods, + .tag = EnumInfo, + .EnumInfo = + { + .name = "PathType", + .num_tags = 3, + .tags = ((NamedType_t[3]){{.name = "Relative"}, {.name = "Absolute"}, {.name = "Home"}}), + }, }; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/paths.h b/src/stdlib/paths.h index 6284e55b..7f0d8830 100644 --- a/src/stdlib/paths.h +++ b/src/stdlib/paths.h @@ -15,7 +15,7 @@ Path_t Path$from_text(Text_t text); const char *Path$as_c_string(Path_t path); #define Path(str) Path$from_str(str) Path_t Path$_concat(int n, Path_t items[n]); -#define Path$concat(...) Path$_concat((int)sizeof((Path_t[]){__VA_ARGS__})/sizeof(Path_t), ((Path_t[]){__VA_ARGS__})) +#define Path$concat(...) Path$_concat((int)sizeof((Path_t[]){__VA_ARGS__}) / sizeof(Path_t), ((Path_t[]){__VA_ARGS__})) Path_t Path$resolved(Path_t path, Path_t relative_to); Path_t Path$relative_to(Path_t path, Path_t relative_to); Path_t Path$expand_home(Path_t path); @@ -59,7 +59,7 @@ Path_t Path$current_dir(void); Closure_t Path$by_line(Path_t path); List_t Path$glob(Path_t path); -uint64_t Path$hash(const void *obj, const TypeInfo_t*); +uint64_t Path$hash(const void *obj, const TypeInfo_t *); int32_t Path$compare(const void *a, const void *b, const TypeInfo_t *type); bool Path$equal(const void *a, const void *b, const TypeInfo_t *type); bool Path$equal_values(Path_t a, Path_t b); @@ -72,4 +72,3 @@ extern const TypeInfo_t Path$info; extern const TypeInfo_t PathType$info; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 - diff --git a/src/stdlib/pointers.c b/src/stdlib/pointers.c index 3708da62..e94ede6d 100644 --- a/src/stdlib/pointers.c +++ b/src/stdlib/pointers.c @@ -13,22 +13,19 @@ #include "types.h" #include "util.h" -public Text_t Pointer$as_text(const void *x, bool colorize, const TypeInfo_t *type) { +public +Text_t Pointer$as_text(const void *x, bool colorize, const TypeInfo_t *type) { __typeof(type->PointerInfo) ptr_info = type->PointerInfo; if (!x) { Text_t typename = generic_as_text(NULL, false, ptr_info.pointed); - if (colorize) - return Text$concat(Text("\x1b[34;1m"), Text$from_str(ptr_info.sigil), typename, Text("\x1b[m")); - else - return Text$concat(Text$from_str(ptr_info.sigil), typename); + if (colorize) return Text$concat(Text("\x1b[34;1m"), Text$from_str(ptr_info.sigil), typename, Text("\x1b[m")); + else return Text$concat(Text$from_str(ptr_info.sigil), typename); } - const void *ptr = *(const void**)x; + const void *ptr = *(const void **)x; if (!ptr) { Text_t typename = generic_as_text(NULL, false, ptr_info.pointed); - if (colorize) - return Text$concat(Text("\x1b[34;1m!"), typename, Text("\x1b[m")); - else - return Text$concat(Text("!"), typename); + if (colorize) return Text$concat(Text("\x1b[34;1m!"), typename, Text("\x1b[m")); + else return Text$concat(Text("!"), typename); } static const void *root = NULL; @@ -61,38 +58,38 @@ public Text_t Pointer$as_text(const void *x, bool colorize, const TypeInfo_t *ty } Text_t text; - if (colorize) - text = Text$concat(Text("\x1b[34;1m"), Text$from_str(ptr_info.sigil), Text("\x1b[m"), pointed); - else - text = Text$concat(Text$from_str(ptr_info.sigil), pointed); + if (colorize) text = Text$concat(Text("\x1b[34;1m"), Text$from_str(ptr_info.sigil), Text("\x1b[m"), pointed); + else text = Text$concat(Text$from_str(ptr_info.sigil), pointed); return text; } PUREFUNC public int32_t Pointer$compare(const void *x, const void *y, const TypeInfo_t *info) { (void)info; - const void *xp = *(const void**)x, *yp = *(const void**)y; + const void *xp = *(const void **)x, *yp = *(const void **)y; return (xp > yp) - (xp < yp); } PUREFUNC public bool Pointer$equal(const void *x, const void *y, const TypeInfo_t *info) { (void)info; - const void *xp = *(const void**)x, *yp = *(const void**)y; + const void *xp = *(const void **)x, *yp = *(const void **)y; return xp == yp; } -PUREFUNC public bool Pointer$is_none(const void *x, const TypeInfo_t *info) -{ +PUREFUNC public bool Pointer$is_none(const void *x, const TypeInfo_t *info) { (void)info; - return *(void**)x == NULL; + return *(void **)x == NULL; } -public void Pointer$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) -{ - void *ptr = *(void**)obj; +public +void Pointer$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) { + void *ptr = *(void **)obj; assert(ptr != NULL); - const TypeInfo_t ptr_to_int_table = {.size=sizeof(Table_t), .align=__alignof__(Table_t), - .tag=TableInfo, .TableInfo.key=type, .TableInfo.value=&Int64$info}; + const TypeInfo_t ptr_to_int_table = {.size = sizeof(Table_t), + .align = __alignof__(Table_t), + .tag = TableInfo, + .TableInfo.key = type, + .TableInfo.value = &Int64$info}; int64_t *id_ptr = Table$get(*pointers, &ptr, &ptr_to_int_table); int64_t id; @@ -105,23 +102,22 @@ public void Pointer$serialize(const void *obj, FILE *out, Table_t *pointers, con Int64$serialize(&id, out, pointers, &Int64$info); - if (!id_ptr) - _serialize(ptr, out, pointers, type->PointerInfo.pointed); + if (!id_ptr) _serialize(ptr, out, pointers, type->PointerInfo.pointed); } -public void Pointer$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type) -{ +public +void Pointer$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type) { int64_t id = 0; Int64$deserialize(in, &id, pointers, &Int64$info); assert(id != 0); if (id > pointers->length) { void *obj = GC_MALLOC((size_t)type->PointerInfo.pointed->size); - List$insert(pointers, &obj, I(0), sizeof(void*)); + List$insert(pointers, &obj, I(0), sizeof(void *)); _deserialize(in, obj, pointers, type->PointerInfo.pointed); - *(void**)outval = obj; + *(void **)outval = obj; } else { - *(void**)outval = *(void**)(pointers->data + (id-1)*pointers->stride); + *(void **)outval = *(void **)(pointers->data + (id - 1) * pointers->stride); } } diff --git a/src/stdlib/pointers.h b/src/stdlib/pointers.h index 001dc5ce..522a97be 100644 --- a/src/stdlib/pointers.h +++ b/src/stdlib/pointers.h @@ -12,25 +12,33 @@ Text_t Pointer$as_text(const void *x, bool colorize, const TypeInfo_t *type); PUREFUNC int32_t Pointer$compare(const void *x, const void *y, const TypeInfo_t *type); PUREFUNC bool Pointer$equal(const void *x, const void *y, const TypeInfo_t *type); -PUREFUNC bool Pointer$is_none(const void *x, const TypeInfo_t*); +PUREFUNC bool Pointer$is_none(const void *x, const TypeInfo_t *); void Pointer$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type); void Pointer$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type); -#define Null(t) (t*)NULL -#define POINTER_TYPE(_sigil, _pointed) (&(TypeInfo_t){\ - .size=sizeof(void*), .align=__alignof__(void*), .tag=PointerInfo, .PointerInfo.sigil=_sigil, .PointerInfo.pointed=_pointed}) - -#define Pointer$metamethods { \ - .as_text=Pointer$as_text, \ - .compare=Pointer$compare, \ - .equal=Pointer$equal, \ - .is_none=Pointer$is_none, \ - .serialize=Pointer$serialize, \ - .deserialize=Pointer$deserialize, \ -} - -#define Pointer$info(sigil_expr, pointed_info) &((TypeInfo_t){.size=sizeof(void*), .align=__alignof__(void*), \ - .tag=PointerInfo, .PointerInfo={.sigil=sigil_expr, .pointed=pointed_info}, \ - .metamethods=Pointer$metamethods}) +#define Null(t) (t *)NULL +#define POINTER_TYPE(_sigil, _pointed) \ + (&(TypeInfo_t){.size = sizeof(void *), \ + .align = __alignof__(void *), \ + .tag = PointerInfo, \ + .PointerInfo.sigil = _sigil, \ + .PointerInfo.pointed = _pointed}) + +#define Pointer$metamethods \ + { \ + .as_text = Pointer$as_text, \ + .compare = Pointer$compare, \ + .equal = Pointer$equal, \ + .is_none = Pointer$is_none, \ + .serialize = Pointer$serialize, \ + .deserialize = Pointer$deserialize, \ + } + +#define Pointer$info(sigil_expr, pointed_info) \ + &((TypeInfo_t){.size = sizeof(void *), \ + .align = __alignof__(void *), \ + .tag = PointerInfo, \ + .PointerInfo = {.sigil = sigil_expr, .pointed = pointed_info}, \ + .metamethods = Pointer$metamethods}) // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/powers.h b/src/stdlib/powers.h index d1829b60..e9b6a74e 100644 --- a/src/stdlib/powers.h +++ b/src/stdlib/powers.h @@ -4,13 +4,12 @@ #include <stdint.h> -#define npowers 87 -#define steppowers 8 +#define npowers 87 +#define steppowers 8 #define firstpower -348 /* 10 ^ -348 */ -#define expmax -32 -#define expmin -60 - +#define expmax -32 +#define expmin -60 typedef struct Fp { uint64_t frac; @@ -18,54 +17,37 @@ typedef struct Fp { } Fp; static const Fp powers_ten[] = { - { 18054884314459144840U, -1220 }, { 13451937075301367670U, -1193 }, - { 10022474136428063862U, -1166 }, { 14934650266808366570U, -1140 }, - { 11127181549972568877U, -1113 }, { 16580792590934885855U, -1087 }, - { 12353653155963782858U, -1060 }, { 18408377700990114895U, -1034 }, - { 13715310171984221708U, -1007 }, { 10218702384817765436U, -980 }, - { 15227053142812498563U, -954 }, { 11345038669416679861U, -927 }, - { 16905424996341287883U, -901 }, { 12595523146049147757U, -874 }, - { 9384396036005875287U, -847 }, { 13983839803942852151U, -821 }, - { 10418772551374772303U, -794 }, { 15525180923007089351U, -768 }, - { 11567161174868858868U, -741 }, { 17236413322193710309U, -715 }, - { 12842128665889583758U, -688 }, { 9568131466127621947U, -661 }, - { 14257626930069360058U, -635 }, { 10622759856335341974U, -608 }, - { 15829145694278690180U, -582 }, { 11793632577567316726U, -555 }, - { 17573882009934360870U, -529 }, { 13093562431584567480U, -502 }, - { 9755464219737475723U, -475 }, { 14536774485912137811U, -449 }, - { 10830740992659433045U, -422 }, { 16139061738043178685U, -396 }, - { 12024538023802026127U, -369 }, { 17917957937422433684U, -343 }, - { 13349918974505688015U, -316 }, { 9946464728195732843U, -289 }, - { 14821387422376473014U, -263 }, { 11042794154864902060U, -236 }, - { 16455045573212060422U, -210 }, { 12259964326927110867U, -183 }, - { 18268770466636286478U, -157 }, { 13611294676837538539U, -130 }, - { 10141204801825835212U, -103 }, { 15111572745182864684U, -77 }, - { 11258999068426240000U, -50 }, { 16777216000000000000U, -24 }, - { 12500000000000000000U, 3 }, { 9313225746154785156U, 30 }, - { 13877787807814456755U, 56 }, { 10339757656912845936U, 83 }, - { 15407439555097886824U, 109 }, { 11479437019748901445U, 136 }, - { 17105694144590052135U, 162 }, { 12744735289059618216U, 189 }, - { 9495567745759798747U, 216 }, { 14149498560666738074U, 242 }, - { 10542197943230523224U, 269 }, { 15709099088952724970U, 295 }, - { 11704190886730495818U, 322 }, { 17440603504673385349U, 348 }, - { 12994262207056124023U, 375 }, { 9681479787123295682U, 402 }, - { 14426529090290212157U, 428 }, { 10748601772107342003U, 455 }, - { 16016664761464807395U, 481 }, { 11933345169920330789U, 508 }, - { 17782069995880619868U, 534 }, { 13248674568444952270U, 561 }, - { 9871031767461413346U, 588 }, { 14708983551653345445U, 614 }, - { 10959046745042015199U, 641 }, { 16330252207878254650U, 667 }, - { 12166986024289022870U, 694 }, { 18130221999122236476U, 720 }, - { 13508068024458167312U, 747 }, { 10064294952495520794U, 774 }, - { 14996968138956309548U, 800 }, { 11173611982879273257U, 827 }, - { 16649979327439178909U, 853 }, { 12405201291620119593U, 880 }, - { 9242595204427927429U, 907 }, { 13772540099066387757U, 933 }, - { 10261342003245940623U, 960 }, { 15290591125556738113U, 986 }, - { 11392378155556871081U, 1013 }, { 16975966327722178521U, 1039 }, - { 12648080533535911531U, 1066 } -}; + {18054884314459144840U, -1220}, {13451937075301367670U, -1193}, {10022474136428063862U, -1166}, + {14934650266808366570U, -1140}, {11127181549972568877U, -1113}, {16580792590934885855U, -1087}, + {12353653155963782858U, -1060}, {18408377700990114895U, -1034}, {13715310171984221708U, -1007}, + {10218702384817765436U, -980}, {15227053142812498563U, -954}, {11345038669416679861U, -927}, + {16905424996341287883U, -901}, {12595523146049147757U, -874}, {9384396036005875287U, -847}, + {13983839803942852151U, -821}, {10418772551374772303U, -794}, {15525180923007089351U, -768}, + {11567161174868858868U, -741}, {17236413322193710309U, -715}, {12842128665889583758U, -688}, + {9568131466127621947U, -661}, {14257626930069360058U, -635}, {10622759856335341974U, -608}, + {15829145694278690180U, -582}, {11793632577567316726U, -555}, {17573882009934360870U, -529}, + {13093562431584567480U, -502}, {9755464219737475723U, -475}, {14536774485912137811U, -449}, + {10830740992659433045U, -422}, {16139061738043178685U, -396}, {12024538023802026127U, -369}, + {17917957937422433684U, -343}, {13349918974505688015U, -316}, {9946464728195732843U, -289}, + {14821387422376473014U, -263}, {11042794154864902060U, -236}, {16455045573212060422U, -210}, + {12259964326927110867U, -183}, {18268770466636286478U, -157}, {13611294676837538539U, -130}, + {10141204801825835212U, -103}, {15111572745182864684U, -77}, {11258999068426240000U, -50}, + {16777216000000000000U, -24}, {12500000000000000000U, 3}, {9313225746154785156U, 30}, + {13877787807814456755U, 56}, {10339757656912845936U, 83}, {15407439555097886824U, 109}, + {11479437019748901445U, 136}, {17105694144590052135U, 162}, {12744735289059618216U, 189}, + {9495567745759798747U, 216}, {14149498560666738074U, 242}, {10542197943230523224U, 269}, + {15709099088952724970U, 295}, {11704190886730495818U, 322}, {17440603504673385349U, 348}, + {12994262207056124023U, 375}, {9681479787123295682U, 402}, {14426529090290212157U, 428}, + {10748601772107342003U, 455}, {16016664761464807395U, 481}, {11933345169920330789U, 508}, + {17782069995880619868U, 534}, {13248674568444952270U, 561}, {9871031767461413346U, 588}, + {14708983551653345445U, 614}, {10959046745042015199U, 641}, {16330252207878254650U, 667}, + {12166986024289022870U, 694}, {18130221999122236476U, 720}, {13508068024458167312U, 747}, + {10064294952495520794U, 774}, {14996968138956309548U, 800}, {11173611982879273257U, 827}, + {16649979327439178909U, 853}, {12405201291620119593U, 880}, {9242595204427927429U, 907}, + {13772540099066387757U, 933}, {10261342003245940623U, 960}, {15290591125556738113U, 986}, + {11392378155556871081U, 1013}, {16975966327722178521U, 1039}, {12648080533535911531U, 1066}}; -static Fp find_cachedpow10(int exp, int* k) -{ +static Fp find_cachedpow10(int exp, int *k) { const double one_log_ten = 0.30102999566398114; int approx = -(exp + npowers) * one_log_ten; diff --git a/src/stdlib/print.c b/src/stdlib/print.c index 8e2dd862..d246c9c3 100644 --- a/src/stdlib/print.c +++ b/src/stdlib/print.c @@ -8,9 +8,9 @@ #include "print.h" #include "util.h" -public int _print_int(FILE *f, int64_t n) -{ - char buf[21] = {[20]=0}; // Big enough for INT64_MIN + '\0' +public +int _print_int(FILE *f, int64_t n) { + char buf[21] = {[20] = 0}; // Big enough for INT64_MIN + '\0' char *p = &buf[19]; bool negative = n < 0; @@ -19,15 +19,14 @@ public int _print_int(FILE *f, int64_t n) n /= 10; } while (n > 0); - if (negative) - *(p--) = '-'; + if (negative) *(p--) = '-'; return fwrite(p + 1, sizeof(char), (size_t)(&buf[19] - p), f); } -public int _print_uint(FILE *f, uint64_t n) -{ - char buf[21] = {[20]=0}; // Big enough for UINT64_MAX + '\0' +public +int _print_uint(FILE *f, uint64_t n) { + char buf[21] = {[20] = 0}; // Big enough for UINT64_MAX + '\0' char *p = &buf[19]; do { @@ -38,8 +37,8 @@ public int _print_uint(FILE *f, uint64_t n) return fwrite(p + 1, sizeof(char), (size_t)(&buf[19] - p), f); } -public int _print_hex(FILE *f, hex_format_t hex) -{ +public +int _print_hex(FILE *f, hex_format_t hex) { int printed = 0; if (!hex.no_prefix) printed += fputs("0x", f); if (hex.digits > 0) { @@ -50,24 +49,21 @@ public int _print_hex(FILE *f, hex_format_t hex) printed += fputc('0', f); } } - char buf[9] = {[8]='\0'}; // Enough space for FFFFFFFF + '\0' + char buf[9] = {[8] = '\0'}; // Enough space for FFFFFFFF + '\0' char *p = &buf[7]; do { uint8_t digit = hex.n % 16; - if (digit <= 9) - *(p--) = '0' + digit; - else if (hex.uppercase) - *(p--) = 'A' + digit - 10; - else - *(p--) = 'a' + digit - 10; + if (digit <= 9) *(p--) = '0' + digit; + else if (hex.uppercase) *(p--) = 'A' + digit - 10; + else *(p--) = 'a' + digit - 10; hex.n /= 16; } while (hex.n > 0); printed += (int)fwrite(p + 1, sizeof(char), (size_t)(&buf[7] - p), f); return printed; } -public int _print_oct(FILE *f, oct_format_t oct) -{ +public +int _print_oct(FILE *f, oct_format_t oct) { int printed = 0; if (!oct.no_prefix) printed += fputs("0o", f); if (oct.digits > 0) { @@ -76,7 +72,7 @@ public int _print_oct(FILE *f, oct_format_t oct) for (; oct.digits > 0; oct.digits -= 1) printed += fputc('0', f); } - char buf[12] = {[11]='\0'}; // Enough space for octal UINT64_MAX + '\0' + char buf[12] = {[11] = '\0'}; // Enough space for octal UINT64_MAX + '\0' char *p = &buf[10]; do { *(p--) = '0' + (oct.n % 8); @@ -86,25 +82,24 @@ public int _print_oct(FILE *f, oct_format_t oct) return printed; } -public int _print_double(FILE *f, double n) -{ +public +int _print_double(FILE *f, double n) { static char buf[24]; int len = fpconv_dtoa(n, buf); return (int)fwrite(buf, sizeof(char), (size_t)len, f); } -public int _print_hex_double(FILE *f, hex_double_t hex) -{ - if (hex.d != hex.d) - return fputs("NAN", f); - else if (hex.d == 1.0/0.0) - return fputs("INF", f); - else if (hex.d == -1.0/0.0) - return fputs("-INF", f); - else if (hex.d == 0.0) - return fputs("0.0", f); +public +int _print_hex_double(FILE *f, hex_double_t hex) { + if (hex.d != hex.d) return fputs("NAN", f); + else if (hex.d == 1.0 / 0.0) return fputs("INF", f); + else if (hex.d == -1.0 / 0.0) return fputs("-INF", f); + else if (hex.d == 0.0) return fputs("0.0", f); - union { double d; uint64_t u; } bits = { .d = hex.d }; + union { + double d; + uint64_t u; + } bits = {.d = hex.d}; int sign = (bits.u >> 63) & 1ull; int exp = (int)((bits.u >> 52) & 0x7FF) - 1023ull; @@ -161,30 +156,27 @@ public int _print_hex_double(FILE *f, hex_double_t hex) return fwrite(buf, sizeof(char), (size_t)(p - buf), f); } -public int _print_char(FILE *f, char c) -{ +public +int _print_char(FILE *f, char c) { #define ESC(e) "'\\" e "'" - const char *named[256] = {['\'']=ESC("'"), ['\\']=ESC("\\"), - ['\n']=ESC("n"), ['\t']=ESC("t"), ['\r']=ESC("r"), - ['\033']=ESC("e"), ['\v']=ESC("v"), ['\a']=ESC("a"), ['\b']=ESC("b")}; + const char *named[256] = { + ['\''] = ESC("'"), ['\\'] = ESC("\\"), ['\n'] = ESC("n"), ['\t'] = ESC("t"), ['\r'] = ESC("r"), + ['\033'] = ESC("e"), ['\v'] = ESC("v"), ['\a'] = ESC("a"), ['\b'] = ESC("b")}; const char *name = named[(uint8_t)c]; - if (name != NULL) - return fputs(name, f); - else if (isprint(c)) - - return fputc('\'', f) + fputc(c, f) + fputc('\'', f); + if (name != NULL) return fputs(name, f); + else if (isprint(c)) return fputc('\'', f) + fputc(c, f) + fputc('\'', f); else - return (fputs("'\\x", f) + _print_hex(f, hex((uint64_t)c, .digits=2, .no_prefix=true, .uppercase=true)) + return (fputs("'\\x", f) + _print_hex(f, hex((uint64_t)c, .digits = 2, .no_prefix = true, .uppercase = true)) + fputs("'", f)); #undef ESC } -public int _print_quoted(FILE *f, quoted_t quoted) -{ +public +int _print_quoted(FILE *f, quoted_t quoted) { #define ESC(e) "\\" e - const char *named[256] = {['"']=ESC("\""), ['\\']=ESC("\\"), - ['\n']=ESC("n"), ['\t']=ESC("t"), ['\r']=ESC("r"), - ['\033']=ESC("e"), ['\v']=ESC("v"), ['\a']=ESC("a"), ['\b']=ESC("b")}; + const char *named[256] = { + ['"'] = ESC("\""), ['\\'] = ESC("\\"), ['\n'] = ESC("n"), ['\t'] = ESC("t"), ['\r'] = ESC("r"), + ['\033'] = ESC("e"), ['\v'] = ESC("v"), ['\a'] = ESC("a"), ['\b'] = ESC("b")}; int printed = fputc('"', f); for (const char *p = quoted.str; *p; p++) { const char *name = named[(uint8_t)*p]; @@ -193,7 +185,8 @@ public int _print_quoted(FILE *f, quoted_t quoted) } else if (isprint(*p) || (uint8_t)*p > 0x7F) { printed += fputc(*p, f); } else { - printed += fputs("\\x", f) + _print_hex(f, hex((uint64_t)*p, .digits=2, .no_prefix=true, .uppercase=true)); + printed += + fputs("\\x", f) + _print_hex(f, hex((uint64_t)*p, .digits = 2, .no_prefix = true, .uppercase = true)); } } printed += fputc('"', f); @@ -202,52 +195,56 @@ public int _print_quoted(FILE *f, quoted_t quoted) } #if defined(__GLIBC__) && defined(_GNU_SOURCE) - // GLIBC has fopencookie() - static ssize_t _gc_stream_write(void *cookie, const char *buf, size_t size) { - gc_stream_t *stream = (gc_stream_t *)cookie; - if (stream->position + size + 1 > *stream->size) - *stream->buffer = GC_REALLOC(*stream->buffer, (*stream->size += MAX(MAX(16UL, *stream->size/2UL), size + 1UL))); - memcpy(&(*stream->buffer)[stream->position], buf, size); - stream->position += size; - (*stream->buffer)[stream->position] = '\0'; - return (ssize_t)size; - } +// GLIBC has fopencookie() +static ssize_t _gc_stream_write(void *cookie, const char *buf, size_t size) { + gc_stream_t *stream = (gc_stream_t *)cookie; + if (stream->position + size + 1 > *stream->size) + *stream->buffer = + GC_REALLOC(*stream->buffer, (*stream->size += MAX(MAX(16UL, *stream->size / 2UL), size + 1UL))); + memcpy(&(*stream->buffer)[stream->position], buf, size); + stream->position += size; + (*stream->buffer)[stream->position] = '\0'; + return (ssize_t)size; +} - public FILE *gc_memory_stream(char **buf, size_t *size) { - gc_stream_t *stream = GC_MALLOC(sizeof(gc_stream_t)); - stream->size = size; - stream->buffer = buf; - *stream->size = 16; - *stream->buffer = GC_MALLOC_ATOMIC(*stream->size); - (*stream->buffer)[0] = '\0'; - stream->position = 0; - cookie_io_functions_t functions = {.write = _gc_stream_write}; - return fopencookie(stream, "w", functions); - } +public +FILE *gc_memory_stream(char **buf, size_t *size) { + gc_stream_t *stream = GC_MALLOC(sizeof(gc_stream_t)); + stream->size = size; + stream->buffer = buf; + *stream->size = 16; + *stream->buffer = GC_MALLOC_ATOMIC(*stream->size); + (*stream->buffer)[0] = '\0'; + stream->position = 0; + cookie_io_functions_t functions = {.write = _gc_stream_write}; + return fopencookie(stream, "w", functions); +} #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - // BSDs have funopen() and fwopen() - static int _gc_stream_write(void *cookie, const char *buf, int size) { - gc_stream_t *stream = (gc_stream_t *)cookie; - if (stream->position + size + 1 > *stream->size) - *stream->buffer = GC_REALLOC(*stream->buffer, (*stream->size += MAX(MAX(16UL, *stream->size/2UL), size + 1UL))); - memcpy(&(*stream->buffer)[stream->position], buf, size); - stream->position += size; - (*stream->buffer)[stream->position] = '\0'; - return size; - } +// BSDs have funopen() and fwopen() +static int _gc_stream_write(void *cookie, const char *buf, int size) { + gc_stream_t *stream = (gc_stream_t *)cookie; + if (stream->position + size + 1 > *stream->size) + *stream->buffer = + GC_REALLOC(*stream->buffer, (*stream->size += MAX(MAX(16UL, *stream->size / 2UL), size + 1UL))); + memcpy(&(*stream->buffer)[stream->position], buf, size); + stream->position += size; + (*stream->buffer)[stream->position] = '\0'; + return size; +} - public FILE *gc_memory_stream(char **buf, size_t *size) { - gc_stream_t *stream = GC_MALLOC(sizeof(gc_stream_t)); - stream->size = size; - stream->buffer = buf; - *stream->size = 16; - *stream->buffer = GC_MALLOC_ATOMIC(*stream->size); - (*stream->buffer)[0] = '\0'; - stream->position = 0; - return fwopen(stream, _gc_stream_write); - } +public +FILE *gc_memory_stream(char **buf, size_t *size) { + gc_stream_t *stream = GC_MALLOC(sizeof(gc_stream_t)); + stream->size = size; + stream->buffer = buf; + *stream->size = 16; + *stream->buffer = GC_MALLOC_ATOMIC(*stream->size); + (*stream->buffer)[0] = '\0'; + stream->position = 0; + return fwopen(stream, _gc_stream_write); +} #else -# error "This platform doesn't support fopencookie() or funopen()!" +#error "This platform doesn't support fopencookie() or funopen()!" #endif // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/print.h b/src/stdlib/print.h index 56953866..c7e59f8b 100644 --- a/src/stdlib/print.h +++ b/src/stdlib/print.h @@ -38,19 +38,19 @@ typedef struct { bool uppercase; int digits; } hex_format_t; -#define hex(x, ...) ((hex_format_t){.n=x, __VA_ARGS__}) +#define hex(x, ...) ((hex_format_t){.n = x, __VA_ARGS__}) typedef struct { double d; } hex_double_t; -#define hex_double(x, ...) ((hex_double_t){.d=x, __VA_ARGS__}) +#define hex_double(x, ...) ((hex_double_t){.d = x, __VA_ARGS__}) typedef struct { uint64_t n; bool no_prefix; int digits; } oct_format_t; -#define oct(x, ...) ((oct_format_t){.n=x, __VA_ARGS__}) +#define oct(x, ...) ((oct_format_t){.n = x, __VA_ARGS__}) typedef struct { const char *str; @@ -67,7 +67,7 @@ typedef struct { char c; int length; } repeated_char_t; -#define repeated_char(ch, len) ((repeated_char_t){.c=ch, .length=len}) +#define repeated_char(ch, len) ((repeated_char_t){.c = ch, .length = len}) #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #define FMT64 "ll" @@ -87,7 +87,9 @@ PRINT_FN _print_bool(FILE *f, bool b) { return fputs(b ? "yes" : "no", f); } PRINT_FN _print_str(FILE *f, const char *s) { return fputs(s ? s : "(null)", f); } int _print_char(FILE *f, char c); int _print_quoted(FILE *f, quoted_t quoted); -PRINT_FN _print_string_slice(FILE *f, string_slice_t slice) { return slice.str ? fwrite(slice.str, 1, slice.length, f) : (size_t)fputs("(null)", f); } +PRINT_FN _print_string_slice(FILE *f, string_slice_t slice) { + return slice.str ? fwrite(slice.str, 1, slice.length, f) : (size_t)fputs("(null)", f); +} PRINT_FN _print_repeated_char(FILE *f, repeated_char_t repeated) { int len = 0; for (int n = 0; n < repeated.length; n++) @@ -99,31 +101,32 @@ extern int Text$print(FILE *stream, Text_t text); extern int Path$print(FILE *stream, Path_t path); extern int Int$print(FILE *f, Int_t i); #ifndef _fprint1 -#define _fprint1(f, x) _Generic((x), \ - char*: _print_str, \ - const char*: _print_str, \ - char: _print_char, \ - bool: _print_bool, \ - int64_t: _print_int, \ - int32_t: _print_int, \ - int16_t: _print_int, \ - int8_t: _print_int, \ - uint64_t: _print_uint, \ - uint32_t: _print_uint, \ - uint16_t: _print_uint, \ - uint8_t: _print_uint, \ - float: _print_float, \ - double: _print_double, \ - hex_format_t: _print_hex, \ - hex_double_t: _print_hex_double, \ - oct_format_t: _print_oct, \ - quoted_t: _print_quoted, \ - string_slice_t: _print_string_slice, \ - repeated_char_t: _print_repeated_char, \ - Text_t: Text$print, \ - Path_t: Path$print, \ - Int_t: Int$print, \ - void*: _print_pointer)(f, x) +#define _fprint1(f, x) \ + _Generic((x), \ + char *: _print_str, \ + const char *: _print_str, \ + char: _print_char, \ + bool: _print_bool, \ + int64_t: _print_int, \ + int32_t: _print_int, \ + int16_t: _print_int, \ + int8_t: _print_int, \ + uint64_t: _print_uint, \ + uint32_t: _print_uint, \ + uint16_t: _print_uint, \ + uint8_t: _print_uint, \ + float: _print_float, \ + double: _print_double, \ + hex_format_t: _print_hex, \ + hex_double_t: _print_hex_double, \ + oct_format_t: _print_oct, \ + quoted_t: _print_quoted, \ + string_slice_t: _print_string_slice, \ + repeated_char_t: _print_repeated_char, \ + Text_t: Text$print, \ + Path_t: Path$print, \ + Int_t: Int$print, \ + void *: _print_pointer)(f, x) #endif typedef struct { @@ -135,19 +138,31 @@ typedef struct { FILE *gc_memory_stream(char **buf, size_t *size); #define _print(x) _n += _fprint1(_printing, x) -#define _fprint(f, ...) ({ FILE *_printing = f; int _n = 0; MAP_LIST(_print, __VA_ARGS__); _n; }) +#define _fprint(f, ...) \ + ({ \ + FILE *_printing = f; \ + int _n = 0; \ + MAP_LIST(_print, __VA_ARGS__); \ + _n; \ + }) #define fprint(f, ...) _fprint(f, __VA_ARGS__, "\n") #define print(...) fprint(stdout, __VA_ARGS__) #define fprint_inline(f, ...) _fprint(f, __VA_ARGS__) #define print_inline(...) fprint_inline(stdout, __VA_ARGS__) -#define String(...) ({ \ - char *_buf = NULL; \ - size_t _size = 0; \ - FILE *_stream = gc_memory_stream(&_buf, &_size); \ - assert(_stream); \ - _fprint(_stream, __VA_ARGS__); \ - fflush(_stream); \ - _buf; }) -#define print_err(...) ({ fprint(stderr, "\033[31;1m", __VA_ARGS__, "\033[m"); exit(EXIT_FAILURE); }) +#define String(...) \ + ({ \ + char *_buf = NULL; \ + size_t _size = 0; \ + FILE *_stream = gc_memory_stream(&_buf, &_size); \ + assert(_stream); \ + _fprint(_stream, __VA_ARGS__); \ + fflush(_stream); \ + _buf; \ + }) +#define print_err(...) \ + ({ \ + fprint(stderr, "\033[31;1m", __VA_ARGS__, "\033[m"); \ + exit(EXIT_FAILURE); \ + }) // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/random.h b/src/stdlib/random.h index 8509dbd7..1a6e89a5 100644 --- a/src/stdlib/random.h +++ b/src/stdlib/random.h @@ -1,5 +1,5 @@ -#include <stdint.h> #include <assert.h> +#include <stdint.h> #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) #include <stdlib.h> @@ -10,9 +10,9 @@ static ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) { } #elif defined(__linux__) // Use getrandom() -# include <sys/random.h> +#include <sys/random.h> #else - #error "Unsupported platform for secure random number generation" +#error "Unsupported platform for secure random number generation" #endif static int64_t random_range(int64_t low, int64_t high) { diff --git a/src/stdlib/simpleparse.c b/src/stdlib/simpleparse.c index 1ee64036..3929c32b 100644 --- a/src/stdlib/simpleparse.c +++ b/src/stdlib/simpleparse.c @@ -26,41 +26,42 @@ static bool _match_word(const char **str, const char *target) { return false; } -public const char *simpleparse(const char *str, int n, parse_type_e types[n], void *destinations[n]) -{ +public +const char *simpleparse(const char *str, int n, parse_type_e types[n], void *destinations[n]) { for (int i = 0; i < n; i++) { switch (types[i]) { case PARSE_SOME_OF: { - if (destinations[i]) str += strspn(str, (char*)destinations[i]); + if (destinations[i]) str += strspn(str, (char *)destinations[i]); break; } case PARSE_LITERAL: { - const char *target = (const char*)destinations[i]; + const char *target = (const char *)destinations[i]; if (target) { - if (strncmp(str, target, strlen(target)) != 0) - return str; + if (strncmp(str, target, strlen(target)) != 0) return str; str += strlen(target); } break; } case PARSE_STRING: { size_t len; - static const char matching_pair[256] = {[(int)'(']=')', [(int)'{']='}', [(int)'[']=']', - [(int)'"']='"', [(int)'\'']='\'', [(int)'`']='`', [(int)'<']='>'}; - if (i > 0 && i + 1 < n && types[i-1] == PARSE_LITERAL && types[i+1] == PARSE_LITERAL - && destinations[i-1] && destinations[i+1] - && strlen((char*)destinations[i-1]) == 1 && strlen((char*)destinations[i+1]) == 1 - && *(char*)destinations[i+1] == matching_pair[(int)*(char*)destinations[i-1]]) { + static const char matching_pair[256] = {[(int)'('] = ')', [(int)'{'] = '}', [(int)'['] = ']', + [(int)'"'] = '"', [(int)'\''] = '\'', [(int)'`'] = '`', + [(int)'<'] = '>'}; + if (i > 0 && i + 1 < n && types[i - 1] == PARSE_LITERAL && types[i + 1] == PARSE_LITERAL + && destinations[i - 1] && destinations[i + 1] && strlen((char *)destinations[i - 1]) == 1 + && strlen((char *)destinations[i + 1]) == 1 + && *(char *)destinations[i + 1] == matching_pair[(int)*(char *)destinations[i - 1]]) { len = 0; - char special_characters[4] = {'\\', *(char*)destinations[i-1], *(char*)destinations[i+1], 0}; - for (int depth = 1; depth > 0; ) { + char special_characters[4] = {'\\', *(char *)destinations[i - 1], *(char *)destinations[i + 1], 0}; + for (int depth = 1; depth > 0;) { len += strcspn(str + len, special_characters); if (str[len] == '\0') { return str; } else if (str[len] == '\\' - && (special_characters[1] == '"' || special_characters[1] == '\'' || special_characters[1] == '`')) { - if (str[len+1] == '\0') return str; - len += 2; + && (special_characters[1] == '"' || special_characters[1] == '\'' + || special_characters[1] == '`')) { + if (str[len + 1] == '\0') return str; + len += 2; } else if (str[len] == special_characters[2]) { // Check for closing quotes before opening quotes depth -= 1; if (depth > 0) len += 1; @@ -70,8 +71,8 @@ public const char *simpleparse(const char *str, int n, parse_type_e types[n], vo len += 1; } } - } else if (i + 1 < n && types[i+1] == PARSE_LITERAL) { - const char *terminator = (const char*)destinations[i+1]; + } else if (i + 1 < n && types[i + 1] == PARSE_LITERAL) { + const char *terminator = (const char *)destinations[i + 1]; if (terminator) { const char *end = strstr(str, terminator); if (!end) return str; @@ -79,16 +80,17 @@ public const char *simpleparse(const char *str, int n, parse_type_e types[n], vo } else { len = strlen(str); } - } else if (i + 1 < n && types[i+1] == PARSE_SOME_OF) { - len = destinations[i+1] ? strcspn(str, (char*)destinations[i+1]) : strlen(str);; + } else if (i + 1 < n && types[i + 1] == PARSE_SOME_OF) { + len = destinations[i + 1] ? strcspn(str, (char *)destinations[i + 1]) : strlen(str); + ; } else { len = strlen(str); } if (destinations[i]) { - char *matched = GC_MALLOC_ATOMIC(len+1); + char *matched = GC_MALLOC_ATOMIC(len + 1); memcpy(matched, str, len); matched[len] = '\0'; - *(const char**)destinations[i] = matched; + *(const char **)destinations[i] = matched; } str += len; break; @@ -97,7 +99,7 @@ public const char *simpleparse(const char *str, int n, parse_type_e types[n], vo char *end = NULL; double val = strtod(str, &end); if (end == str) return str; - if (destinations[i]) *(double*)destinations[i] = val; + if (destinations[i]) *(double *)destinations[i] = val; str = end; break; } @@ -105,15 +107,17 @@ public const char *simpleparse(const char *str, int n, parse_type_e types[n], vo char *end = NULL; long val = strtol(str, &end, 10); if (end == str) return str; - if (destinations[i]) *(long*)destinations[i] = val; + if (destinations[i]) *(long *)destinations[i] = val; str = end; break; } case PARSE_BOOL: { - if (_match_word(&str, "true") || _match_word(&str, "yes") || _match_word(&str, "on") || _match_word(&str, "1")) { - if (destinations[i]) *(bool*)destinations[i] = true; - } else if (_match_word(&str, "false") || _match_word(&str, "no") || _match_word(&str, "off") || _match_word(&str, "0")) { - if (destinations[i]) *(bool*)destinations[i] = false; + if (_match_word(&str, "true") || _match_word(&str, "yes") || _match_word(&str, "on") + || _match_word(&str, "1")) { + if (destinations[i]) *(bool *)destinations[i] = true; + } else if (_match_word(&str, "false") || _match_word(&str, "no") || _match_word(&str, "off") + || _match_word(&str, "0")) { + if (destinations[i]) *(bool *)destinations[i] = false; } else { return str; } diff --git a/src/stdlib/simpleparse.h b/src/stdlib/simpleparse.h index da077e20..d35a8b3b 100644 --- a/src/stdlib/simpleparse.h +++ b/src/stdlib/simpleparse.h @@ -22,30 +22,42 @@ #include "mapmacro.h" -typedef struct { char c; } some_char_t; -#define PARSE_SOME_OF(chars) ((some_char_t*)chars) +typedef struct { + char c; +} some_char_t; +#define PARSE_SOME_OF(chars) ((some_char_t *)chars) #define PARSE_WHITESPACE PARSE_SOME_OF(" \t\r\n\v") -typedef enum {PARSE_LITERAL, PARSE_LONG, PARSE_DOUBLE, PARSE_BOOL, PARSE_STRING, PARSE_SOME_OF} parse_type_e; - -typedef struct { parse_type_e type; void *dest; } parse_element_t; - -#define _parse_type(dest) _Generic((dest), \ - some_char_t*: PARSE_SOME_OF, \ - const char*: PARSE_LITERAL, \ - char*: PARSE_LITERAL, \ - const char**: PARSE_STRING, \ - char**: PARSE_STRING, \ - double*: PARSE_DOUBLE, \ - long*: PARSE_LONG, \ - bool*: PARSE_BOOL) - -#define as_void_star(x) ((void*)x) -#define strparse(str, ...) simpleparse(str, sizeof((const void*[]){__VA_ARGS__})/sizeof(void*), (parse_type_e[]){MAP_LIST(_parse_type, __VA_ARGS__)}, (void*[]){MAP_LIST(as_void_star, __VA_ARGS__)}) -#define fparse(file, ...) ({ char *_file_contents = NULL; size_t _capacity; \ - ssize_t _just_read = getdelim(&_file_contents, &_capacity, '\0', file); \ - const char *_parse_err = _just_read > 0 ? strparse(_file_contents, __VA_ARGS__) : "No such file"; \ - if (_file_contents) free(_file_contents); \ - _parse_err; }) +typedef enum { PARSE_LITERAL, PARSE_LONG, PARSE_DOUBLE, PARSE_BOOL, PARSE_STRING, PARSE_SOME_OF } parse_type_e; + +typedef struct { + parse_type_e type; + void *dest; +} parse_element_t; + +#define _parse_type(dest) \ + _Generic((dest), \ + some_char_t *: PARSE_SOME_OF, \ + const char *: PARSE_LITERAL, \ + char *: PARSE_LITERAL, \ + const char **: PARSE_STRING, \ + char **: PARSE_STRING, \ + double *: PARSE_DOUBLE, \ + long *: PARSE_LONG, \ + bool *: PARSE_BOOL) + +#define as_void_star(x) ((void *)x) +#define strparse(str, ...) \ + simpleparse(str, sizeof((const void *[]){__VA_ARGS__}) / sizeof(void *), \ + (parse_type_e[]){MAP_LIST(_parse_type, __VA_ARGS__)}, (void *[]){MAP_LIST(as_void_star, __VA_ARGS__)}) +#define fparse(file, ...) \ + ({ \ + char *_file_contents = NULL; \ + size_t _capacity; \ + ssize_t _just_read = getdelim(&_file_contents, &_capacity, '\0', file); \ + const char *_parse_err = _just_read > 0 ? strparse(_file_contents, __VA_ARGS__) : "No such file"; \ + if (_file_contents) free(_file_contents); \ + _parse_err; \ + }) const char *simpleparse(const char *str, int n, parse_type_e types[n], void *destinations[n]); diff --git a/src/stdlib/siphash-internals.h b/src/stdlib/siphash-internals.h index b359cea7..c8ec1142 100644 --- a/src/stdlib/siphash-internals.h +++ b/src/stdlib/siphash-internals.h @@ -51,24 +51,25 @@ struct siphash { uint64_t v1; uint64_t v2; uint64_t v3; - uint64_t b; + uint64_t b; }; typedef struct siphash siphash; -#define ROTATE(x, b) (uint64_t)( ((x) << (b)) | ( (x) >> (64 - (b))) ) +#define ROTATE(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b)))) -#define HALF_ROUND(a,b,c,d,s,t) \ - a += b; c += d; \ - b = ROTATE(b, s) ^ a; \ - d = ROTATE(d, t) ^ c; \ +#define HALF_ROUND(a, b, c, d, s, t) \ + a += b; \ + c += d; \ + b = ROTATE(b, s) ^ a; \ + d = ROTATE(d, t) ^ c; \ a = ROTATE(a, 32); -#define DOUBLE_ROUND(v0,v1,v2,v3) \ - HALF_ROUND(v0,v1,v2,v3,13,16); \ - HALF_ROUND(v2,v1,v0,v3,17,21); \ - HALF_ROUND(v0,v1,v2,v3,13,16); \ - HALF_ROUND(v2,v1,v0,v3,17,21); +#define DOUBLE_ROUND(v0, v1, v2, v3) \ + HALF_ROUND(v0, v1, v2, v3, 13, 16); \ + HALF_ROUND(v2, v1, v0, v3, 17, 21); \ + HALF_ROUND(v0, v1, v2, v3, 13, 16); \ + HALF_ROUND(v2, v1, v0, v3, 17, 21); -MACROLIKE void siphashinit (siphash *sh, size_t src_sz) { +MACROLIKE void siphashinit(siphash *sh, size_t src_sz) { const uint64_t k0 = TOMO_HASH_KEY[0]; const uint64_t k1 = TOMO_HASH_KEY[1]; sh->b = (uint64_t)src_sz << 56; @@ -77,20 +78,20 @@ MACROLIKE void siphashinit (siphash *sh, size_t src_sz) { sh->v2 = k0 ^ 0x6c7967656e657261ULL; sh->v3 = k1 ^ 0x7465646279746573ULL; } -MACROLIKE void siphashadd64bits (siphash *sh, const uint64_t in) { +MACROLIKE void siphashadd64bits(siphash *sh, const uint64_t in) { const uint64_t mi = in; sh->v3 ^= mi; - DOUBLE_ROUND(sh->v0,sh->v1,sh->v2,sh->v3); + DOUBLE_ROUND(sh->v0, sh->v1, sh->v2, sh->v3); sh->v0 ^= mi; } -MACROLIKE uint64_t siphashfinish_last_part (siphash *sh, uint64_t t) { +MACROLIKE uint64_t siphashfinish_last_part(siphash *sh, uint64_t t) { sh->b |= t; sh->v3 ^= sh->b; - DOUBLE_ROUND(sh->v0,sh->v1,sh->v2,sh->v3); + DOUBLE_ROUND(sh->v0, sh->v1, sh->v2, sh->v3); sh->v0 ^= sh->b; sh->v2 ^= 0xff; - DOUBLE_ROUND(sh->v0,sh->v1,sh->v2,sh->v3); - DOUBLE_ROUND(sh->v0,sh->v1,sh->v2,sh->v3); + DOUBLE_ROUND(sh->v0, sh->v1, sh->v2, sh->v3); + DOUBLE_ROUND(sh->v0, sh->v1, sh->v2, sh->v3); return (sh->v0 ^ sh->v1) ^ (sh->v2 ^ sh->v3); } /* This union helps us avoid doing weird things with pointers that can cause old @@ -99,26 +100,26 @@ MACROLIKE uint64_t siphashfinish_last_part (siphash *sh, uint64_t t) { union SipHash64_union { uint64_t u64; uint32_t u32; - uint8_t u8[8]; + uint8_t u8[8]; }; -MACROLIKE uint64_t siphashfinish (siphash *sh, const uint8_t *src, size_t src_sz) { - union SipHash64_union t = { 0 }; +MACROLIKE uint64_t siphashfinish(siphash *sh, const uint8_t *src, size_t src_sz) { + union SipHash64_union t = {0}; switch (src_sz) { - /* Falls through */ - case 7: t.u8[6] = src[6]; - /* Falls through */ - case 6: t.u8[5] = src[5]; - /* Falls through */ - case 5: t.u8[4] = src[4]; - /* Falls through */ - case 4: t.u8[3] = src[3]; - /* Falls through */ - case 3: t.u8[2] = src[2]; - /* Falls through */ - case 2: t.u8[1] = src[1]; - /* Falls through */ - case 1: t.u8[0] = src[0]; - default: break; + /* Falls through */ + case 7: t.u8[6] = src[6]; + /* Falls through */ + case 6: t.u8[5] = src[5]; + /* Falls through */ + case 5: t.u8[4] = src[4]; + /* Falls through */ + case 4: t.u8[3] = src[3]; + /* Falls through */ + case 3: t.u8[2] = src[2]; + /* Falls through */ + case 2: t.u8[1] = src[1]; + /* Falls through */ + case 1: t.u8[0] = src[0]; + default: break; } return siphashfinish_last_part(sh, t.u64); } diff --git a/src/stdlib/siphash.c b/src/stdlib/siphash.c index 274b3195..9af845e7 100644 --- a/src/stdlib/siphash.c +++ b/src/stdlib/siphash.c @@ -5,7 +5,8 @@ #include "siphash.h" #include "util.h" -public uint64_t TOMO_HASH_KEY[2] = {23, 42}; // Randomized in tomo_init() +public +uint64_t TOMO_HASH_KEY[2] = {23, 42}; // Randomized in tomo_init() /* <MIT License> Copyright (c) 2013 Marek Majkowski <marek@popcount.org> @@ -53,10 +54,10 @@ PUREFUNC public uint64_t siphash24(const uint8_t *src, size_t src_sz) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" #endif - const uint64_t *in = (uint64_t*)src; + const uint64_t *in = (uint64_t *)src; /* Find largest src_sz evenly divisible by 8 bytes. */ const ptrdiff_t src_sz_nearest_8bits = ((ptrdiff_t)src_sz >> 3) << 3; - const uint64_t *goal = (uint64_t*)(src + src_sz_nearest_8bits); + const uint64_t *goal = (uint64_t *)(src + src_sz_nearest_8bits); #ifdef __GNUC__ #pragma GCC diagnostic pop #endif @@ -74,7 +75,8 @@ PUREFUNC public uint64_t siphash24(const uint8_t *src, size_t src_sz) { uint64_t in_64; memcpy(&in_64, in, sizeof(uint64_t)); siphashadd64bits(&sh, in_64); - in += 8; src_sz -= 8; + in += 8; + src_sz -= 8; } return siphashfinish(&sh, (uint8_t *)in, src_sz); } diff --git a/src/stdlib/siphash.h b/src/stdlib/siphash.h index 67bad582..3dbef0fd 100644 --- a/src/stdlib/siphash.h +++ b/src/stdlib/siphash.h @@ -2,8 +2,8 @@ // An implementation of the SipHash algorithm. -#include <stdint.h> #include <stddef.h> +#include <stdint.h> #include "util.h" diff --git a/src/stdlib/stacktrace.c b/src/stdlib/stacktrace.c index f408d753..9f01a4bf 100644 --- a/src/stdlib/stacktrace.c +++ b/src/stdlib/stacktrace.c @@ -1,6 +1,6 @@ #ifndef _GNU_SOURCE #define _GNU_SOURCE -#endif +#endif #include <dlfcn.h> #include <err.h> @@ -19,8 +19,7 @@ extern bool USE_COLOR; -static void fprint_context(FILE *out, const char *filename, int lineno, int context_before, int context_after) -{ +static void fprint_context(FILE *out, const char *filename, int lineno, int context_before, int context_after) { FILE *f = fopen(filename, "r"); if (!f) return; char *line = NULL; @@ -33,35 +32,31 @@ static void fprint_context(FILE *out, const char *filename, int lineno, int cont num_width += 1; while ((nread = getline(&line, &size, f)) != -1) { - if (line[strlen(line)-1] == '\n') - line[strlen(line)-1] = '\0'; + if (line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = '\0'; if (cur_line >= lineno - context_before) { int w = 1; - for (int n = cur_line; n >= 10; n /= 10) w += 1; + for (int n = cur_line; n >= 10; n /= 10) + w += 1; if (USE_COLOR) { fprint(out, cur_line == lineno ? "\033[31;1m>\033[m " : " ", "\033[2m", - repeated_char(' ', num_width-w), - cur_line, "\033(0\x78\033(B", cur_line == lineno ? "\033[0;31;1m" : "\033[0m", - line, "\033[m"); + repeated_char(' ', num_width - w), cur_line, "\033(0\x78\033(B", + cur_line == lineno ? "\033[0;31;1m" : "\033[0m", line, "\033[m"); } else { - fprint(out, cur_line == lineno ? "> " : " ", - repeated_char(' ', num_width-w), - cur_line, "| ", line); + fprint(out, cur_line == lineno ? "> " : " ", repeated_char(' ', num_width - w), cur_line, "| ", line); } } cur_line += 1; - if (cur_line > lineno + context_after) - break; + if (cur_line > lineno + context_after) break; } if (line) free(line); fclose(f); } -static void _print_stack_frame(FILE *out, const char *cwd, const char *install_dir, const char *function, const char *filename, int lineno) -{ +static void _print_stack_frame(FILE *out, const char *cwd, const char *install_dir, const char *function, + const char *filename, int lineno) { if (function == NULL) { fprint(out, USE_COLOR ? "\033[2m...unknown function...\033[m" : "...unknown function..."); return; @@ -71,7 +66,7 @@ static void _print_stack_frame(FILE *out, const char *cwd, const char *install_d if (function[0] == '\0') function = "???"; char *function_display = GC_MALLOC_ATOMIC(strlen(function)); - memcpy(function_display, function, strlen(function)+1); + memcpy(function_display, function, strlen(function) + 1); char *last_dollar = strrchr(function_display, '$'); if (last_dollar) *last_dollar = '\0'; for (char *p = function_display; *p; p++) { @@ -79,14 +74,12 @@ static void _print_stack_frame(FILE *out, const char *cwd, const char *install_d } if (filename) { - if (strncmp(filename, cwd, strlen(cwd)) == 0) - filename += strlen(cwd); + if (strncmp(filename, cwd, strlen(cwd)) == 0) filename += strlen(cwd); fprint_inline(out, USE_COLOR ? "\033[1mIn \033[33m" : "In ", function_display, USE_COLOR ? "()\033[37m" : "()"); if (install_dir[0] && strncmp(filename, install_dir, strlen(install_dir)) == 0) fprint_inline(out, USE_COLOR ? " in library \033[35m" : " in library ", filename, ":", lineno); - else - fprint(out, USE_COLOR ? " in \033[35m" : " in ", filename, ":", lineno); + else fprint(out, USE_COLOR ? " in \033[35m" : " in ", filename, ":", lineno); fprint(out, USE_COLOR ? "\033[m" : ""); fprint_context(out, filename, lineno, 3, 1); } else { @@ -94,48 +87,41 @@ static void _print_stack_frame(FILE *out, const char *cwd, const char *install_d } } -__attribute__ ((noinline)) -public void print_stacktrace(FILE *out, int offset) -{ +__attribute__((noinline)) public +void print_stacktrace(FILE *out, int offset) { char cwd[PATH_MAX]; - if (getcwd(cwd, sizeof(cwd)) == NULL) - errx(1, "Path too large!"); + if (getcwd(cwd, sizeof(cwd)) == NULL) errx(1, "Path too large!"); size_t cwd_len = strlen(cwd); - if (cwd_len + 2 > sizeof(cwd)) - errx(1, "Path too large!"); + if (cwd_len + 2 > sizeof(cwd)) errx(1, "Path too large!"); cwd[cwd_len++] = '/'; cwd[cwd_len] = '\0'; - const char *install_dir = TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed/"; + const char *install_dir = TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed/"; static void *stack[1024]; - int64_t size = (int64_t)backtrace(stack, sizeof(stack)/sizeof(stack[0])); + int64_t size = (int64_t)backtrace(stack, sizeof(stack) / sizeof(stack[0])); char **strings = backtrace_symbols(stack, size); bool main_func_onwards = false; - for (int64_t i = size-1; i > offset; i--) { + for (int64_t i = size - 1; i > offset; i--) { Dl_info info; - void *call_address = stack[i]-1; + void *call_address = stack[i] - 1; if (dladdr(call_address, &info) && info.dli_fname) { const char *file = info.dli_fname; uintptr_t frame_offset = (uintptr_t)call_address - (uintptr_t)info.dli_fbase; - FILE *fp = popen(String("addr2line -f -e '", file, "' ", (void*)frame_offset, " 2>/dev/null"), "r"); + FILE *fp = popen(String("addr2line -f -e '", file, "' ", (void *)frame_offset, " 2>/dev/null"), "r"); if (fp) { const char *function = NULL, *filename = NULL; long line_num = 0; if (fparse(fp, &function, "\n", &filename, ":", &line_num) == NULL) { - if (starts_with(function, "main$")) - main_func_onwards = true; - if (main_func_onwards) - _print_stack_frame(out, cwd, install_dir, function, filename, line_num); + if (starts_with(function, "main$")) main_func_onwards = true; + if (main_func_onwards) _print_stack_frame(out, cwd, install_dir, function, filename, line_num); } else { - if (main_func_onwards) - _print_stack_frame(out, cwd, install_dir, NULL, NULL, line_num); + if (main_func_onwards) _print_stack_frame(out, cwd, install_dir, NULL, NULL, line_num); } pclose(fp); } } else { - if (main_func_onwards) - _print_stack_frame(out, cwd, install_dir, NULL, NULL, 0); + if (main_func_onwards) _print_stack_frame(out, cwd, install_dir, NULL, NULL, 0); } if (main_func_onwards && i - 1 > offset) fputs("\n", out); } diff --git a/src/stdlib/stacktrace.h b/src/stdlib/stacktrace.h index 828bbe98..36e81bea 100644 --- a/src/stdlib/stacktrace.h +++ b/src/stdlib/stacktrace.h @@ -1,5 +1,4 @@ #pragma once #include <stdio.h> -__attribute__ ((noinline)) -void print_stacktrace(FILE *out, int offset); +__attribute__((noinline)) void print_stacktrace(FILE *out, int offset); diff --git a/src/stdlib/stdlib.c b/src/stdlib/stdlib.c index 159acdb3..411ce300 100644 --- a/src/stdlib/stdlib.c +++ b/src/stdlib/stdlib.c @@ -37,16 +37,17 @@ static ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) { } #elif defined(__linux__) // Use getrandom() -# include <sys/random.h> +#include <sys/random.h> #else - #error "Unsupported platform for secure random number generation" +#error "Unsupported platform for secure random number generation" #endif -public bool USE_COLOR; -public Text_t TOMO_VERSION_TEXT = Text(TOMO_VERSION); +public +bool USE_COLOR; +public +Text_t TOMO_VERSION_TEXT = Text(TOMO_VERSION); -static _Noreturn void signal_handler(int sig, siginfo_t *info, void *userdata) -{ +static _Noreturn void signal_handler(int sig, siginfo_t *info, void *userdata) { (void)info, (void)userdata; assert(sig == SIGILL); fflush(stdout); @@ -58,107 +59,92 @@ static _Noreturn void signal_handler(int sig, siginfo_t *info, void *userdata) _exit(1); } -public void tomo_init(void) -{ - GC_INIT(); - USE_COLOR = getenv("COLOR") ? strcmp(getenv("COLOR"), "1") == 0 : isatty(STDOUT_FILENO); - if (getenv("NO_COLOR") && getenv("NO_COLOR")[0] != '\0') - USE_COLOR = false; - - setlocale(LC_ALL, ""); - assert(getrandom(TOMO_HASH_KEY, sizeof(TOMO_HASH_KEY), 0) == sizeof(TOMO_HASH_KEY)); - - struct sigaction sigact; - sigact.sa_sigaction = signal_handler; - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - sigaction(SIGILL, &sigact, (struct sigaction *)NULL); +public +void tomo_init(void) { + GC_INIT(); + USE_COLOR = getenv("COLOR") ? strcmp(getenv("COLOR"), "1") == 0 : isatty(STDOUT_FILENO); + if (getenv("NO_COLOR") && getenv("NO_COLOR")[0] != '\0') USE_COLOR = false; + + setlocale(LC_ALL, ""); + assert(getrandom(TOMO_HASH_KEY, sizeof(TOMO_HASH_KEY), 0) == sizeof(TOMO_HASH_KEY)); + + struct sigaction sigact; + sigact.sa_sigaction = signal_handler; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigaction(SIGILL, &sigact, (struct sigaction *)NULL); } -static bool parse_single_arg(const TypeInfo_t *info, char *arg, void *dest) -{ +static bool parse_single_arg(const TypeInfo_t *info, char *arg, void *dest) { if (!arg) return false; - if (info->tag == OptionalInfo && streq(arg, "none")) - return true; + if (info->tag == OptionalInfo && streq(arg, "none")) return true; while (info->tag == OptionalInfo) info = info->OptionalInfo.type; if (info == &Int$info) { OptionalInt_t parsed = Int$from_str(arg); - if (parsed.small != 0) - *(OptionalInt_t*)dest = parsed; + if (parsed.small != 0) *(OptionalInt_t *)dest = parsed; return parsed.small != 0; } else if (info == &Int64$info) { OptionalInt64_t parsed = Int64$parse(Text$from_str(arg), NULL); - if (!parsed.is_none) - *(OptionalInt64_t*)dest = parsed; + if (!parsed.is_none) *(OptionalInt64_t *)dest = parsed; return !parsed.is_none; } else if (info == &Int32$info) { OptionalInt32_t parsed = Int32$parse(Text$from_str(arg), NULL); - if (!parsed.is_none) - *(OptionalInt32_t*)dest = parsed; + if (!parsed.is_none) *(OptionalInt32_t *)dest = parsed; return !parsed.is_none; } else if (info == &Int16$info) { OptionalInt16_t parsed = Int16$parse(Text$from_str(arg), NULL); - if (!parsed.is_none) - *(OptionalInt16_t*)dest = parsed; + if (!parsed.is_none) *(OptionalInt16_t *)dest = parsed; return !parsed.is_none; } else if (info == &Int8$info) { OptionalInt8_t parsed = Int8$parse(Text$from_str(arg), NULL); - if (!parsed.is_none) - *(OptionalInt8_t*)dest = parsed; + if (!parsed.is_none) *(OptionalInt8_t *)dest = parsed; return !parsed.is_none; } else if (info == &Bool$info) { OptionalBool_t parsed = Bool$parse(Text$from_str(arg), NULL); - if (parsed != NONE_BOOL) - *(OptionalBool_t*)dest = parsed; + if (parsed != NONE_BOOL) *(OptionalBool_t *)dest = parsed; return parsed != NONE_BOOL; } else if (info == &Num$info) { OptionalNum_t parsed = Num$parse(Text$from_str(arg), NULL); - if (!isnan(parsed)) - *(OptionalNum_t*)dest = parsed; + if (!isnan(parsed)) *(OptionalNum_t *)dest = parsed; return !isnan(parsed); } else if (info == &Num32$info) { OptionalNum32_t parsed = Num32$parse(Text$from_str(arg), NULL); - if (!isnan(parsed)) - *(OptionalNum32_t*)dest = parsed; + if (!isnan(parsed)) *(OptionalNum32_t *)dest = parsed; return !isnan(parsed); } else if (info == &Path$info) { - *(OptionalPath_t*)dest = Path$from_str(arg); + *(OptionalPath_t *)dest = Path$from_str(arg); return true; } else if (info->tag == TextInfo) { - *(OptionalText_t*)dest = Text$from_str(arg); + *(OptionalText_t *)dest = Text$from_str(arg); return true; } else if (info->tag == EnumInfo) { for (int t = 0; t < info->EnumInfo.num_tags; t++) { NamedType_t named = info->EnumInfo.tags[t]; size_t len = strlen(named.name); if (strncmp(arg, named.name, len) == 0 && (arg[len] == '\0' || arg[len] == ':')) { - *(int32_t*)dest = (t + 1); + *(int32_t *)dest = (t + 1); // Simple tag (no associated data): if (!named.type || (named.type->tag == StructInfo && named.type->StructInfo.num_fields == 0)) return true; // Single-argument tag: - if (arg[len] != ':') - print_err("Invalid value for ", t, ".", named.name, ": ", arg); + if (arg[len] != ':') print_err("Invalid value for ", t, ".", named.name, ": ", arg); size_t offset = sizeof(int32_t); if (named.type->align > 0 && offset % (size_t)named.type->align > 0) offset += (size_t)named.type->align - (offset % (size_t)named.type->align); - if (!parse_single_arg(named.type, arg + len + 1, dest + offset)) - return false; + if (!parse_single_arg(named.type, arg + len + 1, dest + offset)) return false; return true; } } print_err("Invalid value for ", info->EnumInfo.name, ": ", arg); } else if (info->tag == StructInfo) { - if (info->StructInfo.num_fields == 0) - return true; - else if (info->StructInfo.num_fields == 1) - return parse_single_arg(info->StructInfo.fields[0].type, arg, dest); + if (info->StructInfo.num_fields == 0) return true; + else if (info->StructInfo.num_fields == 1) return parse_single_arg(info->StructInfo.fields[0].type, arg, dest); Text_t t = generic_as_text(NULL, false, info); print_err("Unsupported multi-argument struct type for argument parsing: ", t); @@ -173,41 +159,36 @@ static bool parse_single_arg(const TypeInfo_t *info, char *arg, void *dest) return false; } -static List_t parse_list(const TypeInfo_t *item_info, int n, char *args[]) -{ +static List_t parse_list(const TypeInfo_t *item_info, int n, char *args[]) { int64_t padded_size = item_info->size; if ((padded_size % item_info->align) > 0) padded_size = padded_size + item_info->align - (padded_size % item_info->align); List_t items = { - .stride=padded_size, - .length=n, - .data=GC_MALLOC((size_t)(padded_size*n)), + .stride = padded_size, + .length = n, + .data = GC_MALLOC((size_t)(padded_size * n)), }; for (int i = 0; i < n; i++) { - bool success = parse_single_arg(item_info, args[i], items.data + items.stride*i); - if (!success) - print_err("Couldn't parse argument: ", args[i]); + bool success = parse_single_arg(item_info, args[i], items.data + items.stride * i); + if (!success) print_err("Couldn't parse argument: ", args[i]); } return items; } // Arguments take the form key=value, with a guarantee that there is an '=' -static Table_t parse_table(const TypeInfo_t *table, int n, char *args[]) -{ +static Table_t parse_table(const TypeInfo_t *table, int n, char *args[]) { const TypeInfo_t *key = table->TableInfo.key, *value = table->TableInfo.value; int64_t padded_size = key->size; - if ((padded_size % value->align) > 0) - padded_size = padded_size + value->align - (padded_size % value->align); + if ((padded_size % value->align) > 0) padded_size = padded_size + value->align - (padded_size % value->align); int64_t value_offset = padded_size; padded_size += value->size; - if ((padded_size % key->align) > 0) - padded_size = padded_size + key->align - (padded_size % key->align); + if ((padded_size % key->align) > 0) padded_size = padded_size + key->align - (padded_size % key->align); List_t entries = { - .stride=padded_size, - .length=n, - .data=GC_MALLOC((size_t)(padded_size*n)), + .stride = padded_size, + .length = n, + .data = GC_MALLOC((size_t)(padded_size * n)), }; for (int i = 0; i < n; i++) { char *key_arg = args[i]; @@ -216,13 +197,11 @@ static Table_t parse_table(const TypeInfo_t *table, int n, char *args[]) char *value_arg = equals + 1; *equals = '\0'; - bool success = parse_single_arg(key, key_arg, entries.data + entries.stride*i); - if (!success) - print_err("Couldn't parse table key: ", key_arg); + bool success = parse_single_arg(key, key_arg, entries.data + entries.stride * i); + if (!success) print_err("Couldn't parse table key: ", key_arg); - success = parse_single_arg(value, value_arg, entries.data + entries.stride*i + value_offset); - if (!success) - print_err("Couldn't parse table value: ", value_arg); + success = parse_single_arg(value, value_arg, entries.data + entries.stride * i + value_offset); + if (!success) print_err("Couldn't parse table value: ", value_arg); *equals = '='; } @@ -233,13 +212,14 @@ static Table_t parse_table(const TypeInfo_t *table, int n, char *args[]) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstack-protector" #endif -public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, const char *version, int spec_len, cli_arg_t spec[spec_len]) -{ +public +void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, const char *version, int spec_len, + cli_arg_t spec[spec_len]) { bool populated_args[spec_len]; bool used_args[argc]; memset(populated_args, 0, sizeof(populated_args)); memset(used_args, 0, sizeof(used_args)); - for (int i = 1; i < argc; ) { + for (int i = 1; i < argc;) { if (argv[i][0] == '-' && argv[i][1] == '-') { if (argv[i][2] == '\0') { // "--" signals the rest of the arguments are literal used_args[i] = true; @@ -252,58 +232,54 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, while (non_opt_type->tag == OptionalInfo) non_opt_type = non_opt_type->OptionalInfo.type; - if (non_opt_type == &Bool$info - && strncmp(argv[i], "--no-", strlen("--no-")) == 0 + if (non_opt_type == &Bool$info && strncmp(argv[i], "--no-", strlen("--no-")) == 0 && strcmp(argv[i] + strlen("--no-"), spec[s].name) == 0) { - *(OptionalBool_t*)spec[s].dest = false; + *(OptionalBool_t *)spec[s].dest = false; populated_args[s] = true; used_args[i] = true; goto next_arg; } - if (strncmp(spec[s].name, argv[i] + 2, strlen(spec[s].name)) != 0) - continue; + if (strncmp(spec[s].name, argv[i] + 2, strlen(spec[s].name)) != 0) continue; - char after_name = argv[i][2+strlen(spec[s].name)]; + char after_name = argv[i][2 + strlen(spec[s].name)]; if (after_name == '\0') { // --foo val used_args[i] = true; if (non_opt_type->tag == ListInfo) { int num_args = 0; while (i + 1 + num_args < argc) { - if (argv[i+1+num_args][0] == '-') - break; - used_args[i+1+num_args] = true; + if (argv[i + 1 + num_args][0] == '-') break; + used_args[i + 1 + num_args] = true; num_args += 1; } populated_args[s] = true; - *(OptionalList_t*)spec[s].dest = parse_list(non_opt_type->ListInfo.item, num_args, &argv[i+1]); + *(OptionalList_t *)spec[s].dest = + parse_list(non_opt_type->ListInfo.item, num_args, &argv[i + 1]); } else if (non_opt_type->tag == TableInfo) { int num_args = 0; while (i + 1 + num_args < argc) { - if (argv[i+1+num_args][0] == '-' || !strchr(argv[i+1+num_args], '=')) - break; - used_args[i+1+num_args] = true; + if (argv[i + 1 + num_args][0] == '-' || !strchr(argv[i + 1 + num_args], '=')) break; + used_args[i + 1 + num_args] = true; num_args += 1; } populated_args[s] = true; - *(OptionalTable_t*)spec[s].dest = parse_table(non_opt_type, num_args, &argv[i+1]); + *(OptionalTable_t *)spec[s].dest = parse_table(non_opt_type, num_args, &argv[i + 1]); } else if (non_opt_type == &Bool$info) { // --flag populated_args[s] = true; - *(OptionalBool_t*)spec[s].dest = true; + *(OptionalBool_t *)spec[s].dest = true; } else { - if (i + 1 >= argc) - print_err("Missing argument: ", argv[i], "\n", usage); - used_args[i+1] = true; - populated_args[s] = parse_single_arg(spec[s].type, argv[i+1], spec[s].dest); + if (i + 1 >= argc) print_err("Missing argument: ", argv[i], "\n", usage); + used_args[i + 1] = true; + populated_args[s] = parse_single_arg(spec[s].type, argv[i + 1], spec[s].dest); if (!populated_args[s]) - print_err("Couldn't parse argument: ", argv[i], " ", argv[i+1], "\n", usage); + print_err("Couldn't parse argument: ", argv[i], " ", argv[i + 1], "\n", usage); } goto next_arg; } else if (after_name == '=') { // --foo=val used_args[i] = true; - populated_args[s] = parse_single_arg(spec[s].type, 2 + argv[i] + strlen(spec[s].name) + 1, spec[s].dest); - if (!populated_args[s]) - print_err("Couldn't parse argument: ", argv[i], "\n", usage); + populated_args[s] = + parse_single_arg(spec[s].type, 2 + argv[i] + strlen(spec[s].name) + 1, spec[s].dest); + if (!populated_args[s]) print_err("Couldn't parse argument: ", argv[i], "\n", usage); goto next_arg; } else { continue; @@ -324,8 +300,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, for (char *f = argv[i] + 1; *f; f++) { char flag[] = {'-', *f, 0}; for (int s = 0; s < spec_len; s++) { - if (spec[s].name[0] != *f || strlen(spec[s].name) > 1) - continue; + if (spec[s].name[0] != *f || strlen(spec[s].name) > 1) continue; const TypeInfo_t *non_opt_type = spec[s].type; while (non_opt_type->tag == OptionalInfo) @@ -335,32 +310,31 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, if (f[1]) print_err("No value provided for ", flag, "\n", usage); int num_args = 0; while (i + 1 + num_args < argc) { - if (argv[i+1+num_args][0] == '-') - break; - used_args[i+1+num_args] = true; + if (argv[i + 1 + num_args][0] == '-') break; + used_args[i + 1 + num_args] = true; num_args += 1; } populated_args[s] = true; - *(OptionalList_t*)spec[s].dest = parse_list(non_opt_type->ListInfo.item, num_args, &argv[i+1]); + *(OptionalList_t *)spec[s].dest = + parse_list(non_opt_type->ListInfo.item, num_args, &argv[i + 1]); } else if (non_opt_type->tag == TableInfo) { int num_args = 0; while (i + 1 + num_args < argc) { - if (argv[i+1+num_args][0] == '-' || !strchr(argv[i+1+num_args], '=')) - break; - used_args[i+1+num_args] = true; + if (argv[i + 1 + num_args][0] == '-' || !strchr(argv[i + 1 + num_args], '=')) break; + used_args[i + 1 + num_args] = true; num_args += 1; } populated_args[s] = true; - *(OptionalTable_t*)spec[s].dest = parse_table(non_opt_type, num_args, &argv[i+1]); + *(OptionalTable_t *)spec[s].dest = parse_table(non_opt_type, num_args, &argv[i + 1]); } else if (non_opt_type == &Bool$info) { // -f populated_args[s] = true; - *(OptionalBool_t*)spec[s].dest = true; + *(OptionalBool_t *)spec[s].dest = true; } else { - if (f[1] || i+1 >= argc) print_err("No value provided for ", flag, "\n", usage); - used_args[i+1] = true; - populated_args[s] = parse_single_arg(spec[s].type, argv[i+1], spec[s].dest); + if (f[1] || i + 1 >= argc) print_err("No value provided for ", flag, "\n", usage); + used_args[i + 1] = true; + populated_args[s] = parse_single_arg(spec[s].type, argv[i + 1], spec[s].dest); if (!populated_args[s]) - print_err("Couldn't parse argument: ", argv[i], " ", argv[i+1], "\n", usage); + print_err("Couldn't parse argument: ", argv[i], " ", argv[i + 1], "\n", usage); } goto next_flag; } @@ -370,7 +344,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, exit(0); } print_err("Unrecognized flag: ", flag, "\n", usage); - next_flag:; + next_flag:; } } else { // Handle positional args later @@ -378,7 +352,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, continue; } - next_arg: + next_arg: while (used_args[i] && i < argc) i += 1; } @@ -393,10 +367,9 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, if (used_args[i]) continue; while (populated_args[s]) { - next_non_bool_flag: + next_non_bool_flag: ++s; - if (s >= spec_len) - print_err("Extra argument: ", argv[i], "\n", usage); + if (s >= spec_len) print_err("Extra argument: ", argv[i], "\n", usage); } const TypeInfo_t *non_opt_type = spec[s].type; @@ -404,45 +377,40 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, non_opt_type = non_opt_type->OptionalInfo.type; // You can't specify boolean flags positionally - if (non_opt_type == &Bool$info) - goto next_non_bool_flag; + if (non_opt_type == &Bool$info) goto next_non_bool_flag; if (non_opt_type->tag == ListInfo) { int num_args = 0; while (i + num_args < argc) { - if (!ignore_dashes && (argv[i+num_args][0] == '-' && !isdigit(argv[i+num_args][1]))) - break; - used_args[i+num_args] = true; + if (!ignore_dashes && (argv[i + num_args][0] == '-' && !isdigit(argv[i + num_args][1]))) break; + used_args[i + num_args] = true; num_args += 1; } populated_args[s] = true; - *(OptionalList_t*)spec[s].dest = parse_list(non_opt_type->ListInfo.item, num_args, &argv[i]); + *(OptionalList_t *)spec[s].dest = parse_list(non_opt_type->ListInfo.item, num_args, &argv[i]); } else if (non_opt_type->tag == TableInfo) { int num_args = 0; while (i + num_args < argc) { - if ((argv[i+num_args][0] == '-' && !isdigit(argv[i+num_args][1])) || !strchr(argv[i+num_args], '=')) + if ((argv[i + num_args][0] == '-' && !isdigit(argv[i + num_args][1])) + || !strchr(argv[i + num_args], '=')) break; - used_args[i+num_args] = true; + used_args[i + num_args] = true; num_args += 1; } populated_args[s] = true; - *(OptionalTable_t*)spec[s].dest = parse_table(non_opt_type, num_args, &argv[i]); + *(OptionalTable_t *)spec[s].dest = parse_table(non_opt_type, num_args, &argv[i]); } else { populated_args[s] = parse_single_arg(spec[s].type, argv[i], spec[s].dest); } - if (!populated_args[s]) - print_err("Invalid value for ", spec[s].name, ": ", argv[i], "\n", usage); + if (!populated_args[s]) print_err("Invalid value for ", spec[s].name, ": ", argv[i], "\n", usage); } for (int s = 0; s < spec_len; s++) { if (!populated_args[s] && spec[s].required) { - if (spec[s].type->tag == ListInfo) - *(OptionalList_t*)spec[s].dest = (List_t){}; - else if (spec[s].type->tag == TableInfo) - *(OptionalTable_t*)spec[s].dest = (Table_t){}; - else - print_err("The required argument '", spec[s].name, "' was not provided\n", usage); + if (spec[s].type->tag == ListInfo) *(OptionalList_t *)spec[s].dest = (List_t){}; + else if (spec[s].type->tag == TableInfo) *(OptionalTable_t *)spec[s].dest = (Table_t){}; + else print_err("The required argument '", spec[s].name, "' was not provided\n", usage); } } } @@ -450,24 +418,18 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, #pragma GCC diagnostic pop #endif -public _Noreturn void fail_text(Text_t message) -{ - fail(message); -} +public +_Noreturn void fail_text(Text_t message) { fail(message); } -public Text_t builtin_last_err() -{ - return Text$from_str(strerror(errno)); -} +public +Text_t builtin_last_err() { return Text$from_str(strerror(errno)); } static int _inspect_depth = 0; static file_t *file = NULL; -__attribute__((nonnull)) -public void start_inspect(const char *filename, int64_t start, int64_t end) -{ - if (file == NULL || strcmp(file->filename, filename) != 0) - file = load_file(filename); +__attribute__((nonnull)) public +void start_inspect(const char *filename, int64_t start, int64_t end) { + if (file == NULL || strcmp(file->filename, filename) != 0) file = load_file(filename); if (file) { size_t first_line_len = strcspn(file->text + start, "\r\n"); @@ -476,26 +438,25 @@ public void start_inspect(const char *filename, int64_t start, int64_t end) int64_t line_num = get_line_number(file, file->text + start); if (USE_COLOR) { - print(repeated_char(' ', 3*_inspect_depth), "\x1b[33;1m>> \x1b[m", - string_slice(file->text + start, first_line_len), - " ", repeated_char(' ', MAX(0, 35-(int64_t)first_line_len-3*_inspect_depth)), - "\x1b[32;2m[", file_base, ":", line_num, "]\x1b[m"); + print(repeated_char(' ', 3 * _inspect_depth), "\x1b[33;1m>> \x1b[m", + string_slice(file->text + start, first_line_len), " ", + repeated_char(' ', MAX(0, 35 - (int64_t)first_line_len - 3 * _inspect_depth)), "\x1b[32;2m[", + file_base, ":", line_num, "]\x1b[m"); } else { - print(repeated_char(' ', 3*_inspect_depth), ">> ", - string_slice(file->text + start, first_line_len), - " ", repeated_char(' ', MAX(0, 35-(int64_t)first_line_len-3*_inspect_depth)), - "[", file_base, ":", line_num, "]"); + print(repeated_char(' ', 3 * _inspect_depth), ">> ", string_slice(file->text + start, first_line_len), + " ", repeated_char(' ', MAX(0, 35 - (int64_t)first_line_len - 3 * _inspect_depth)), "[", file_base, + ":", line_num, "]"); } // For multi-line expressions, dedent each and print it on a new line with ".. " in front: if (end > start + (int64_t)first_line_len) { const char *line_start = get_line(file, line_num); int64_t indent_len = (int64_t)strspn(line_start, " \t"); - for (const char *line = file->text + start + first_line_len; line < file->text + end; line += strcspn(line, "\r\n")) { + for (const char *line = file->text + start + first_line_len; line < file->text + end; + line += strcspn(line, "\r\n")) { line += strspn(line, "\r\n"); - if ((int64_t)strspn(line, " \t") >= indent_len) - line += indent_len; - print(repeated_char(' ', 3*_inspect_depth), USE_COLOR ? "\x1b[33m.. " : ".. ", + if ((int64_t)strspn(line, " \t") >= indent_len) line += indent_len; + print(repeated_char(' ', 3 * _inspect_depth), USE_COLOR ? "\x1b[33m.. " : ".. ", string_slice(line, strcspn(line, "\r\n"))); } } @@ -503,23 +464,24 @@ public void start_inspect(const char *filename, int64_t start, int64_t end) _inspect_depth += 1; } -public void end_inspect(const void *expr, const TypeInfo_t *type) -{ +public +void end_inspect(const void *expr, const TypeInfo_t *type) { _inspect_depth -= 1; if (type && type->metamethods.as_text) { Text_t expr_text = generic_as_text(expr, USE_COLOR, type); Text_t type_name = generic_as_text(NULL, false, type); - for (int i = 0; i < 3*_inspect_depth; i++) fputc(' ', stdout); - fprint(stdout, USE_COLOR ? "\x1b[33;1m=\x1b[0m " : "= ", expr_text, USE_COLOR ? " \x1b[2m: \x1b[36m" : " : ", type_name, USE_COLOR ? "\033[m" : ""); + for (int i = 0; i < 3 * _inspect_depth; i++) + fputc(' ', stdout); + fprint(stdout, USE_COLOR ? "\x1b[33;1m=\x1b[0m " : "= ", expr_text, USE_COLOR ? " \x1b[2m: \x1b[36m" : " : ", + type_name, USE_COLOR ? "\033[m" : ""); } } -__attribute__((nonnull)) -public void test_value(const char *filename, int64_t start, int64_t end, const void *expr, const void *expected, const TypeInfo_t *type) -{ - if (generic_equal(expr, expected, type)) - return; +__attribute__((nonnull)) public +void test_value(const char *filename, int64_t start, int64_t end, const void *expr, const void *expected, + const TypeInfo_t *type) { + if (generic_equal(expr, expected, type)) return; print_stacktrace(stderr, 2); fprint(stderr, ""); @@ -532,38 +494,42 @@ public void test_value(const char *filename, int64_t start, int64_t end, const v Text_t expr_text = generic_as_text(expr, USE_COLOR, type); Text_t expected_text = generic_as_text(expected, USE_COLOR, type); if (USE_COLOR) { - fprint(stderr, - "\n\x1b[31;7m ==================== TEST FAILED ==================== \x1b[0;1m\n\n" - "You expected: \x1b[m", expected_text, "\x1b[0m\n" - "\x1b[1m But I got:\x1b[m ", expr_text, "\n"); + fprint(stderr, + "\n\x1b[31;7m ==================== TEST FAILED ==================== \x1b[0;1m\n\n" + "You expected: \x1b[m", + expected_text, + "\x1b[0m\n" + "\x1b[1m But I got:\x1b[m ", + expr_text, "\n"); } else { - fprint(stderr, - "\n==================== TEST FAILED ====================\n\n" - "You expected: ", expected_text, "\n" - " But I got: ", expr_text, "\n"); + fprint(stderr, + "\n==================== TEST FAILED ====================\n\n" + "You expected: ", + expected_text, + "\n" + " But I got: ", + expr_text, "\n"); } fflush(stderr); raise(SIGABRT); } -public void say(Text_t text, bool newline) -{ +public +void say(Text_t text, bool newline) { Text$print(stdout, text); - if (newline) - fputc('\n', stdout); + if (newline) fputc('\n', stdout); fflush(stdout); } -public _Noreturn void tomo_exit(Text_t text, int32_t status) -{ - if (text.length > 0) - print(text); +public +_Noreturn void tomo_exit(Text_t text, int32_t status) { + if (text.length > 0) print(text); _exit(status); } -public OptionalText_t ask(Text_t prompt, bool bold, bool force_tty) -{ +public +OptionalText_t ask(Text_t prompt, bool bold, bool force_tty) { OptionalText_t ret = NONE_TEXT; FILE *out = stdout; FILE *in = stdin; @@ -597,8 +563,8 @@ public OptionalText_t ask(Text_t prompt, bool bold, bool force_tty) goto cleanup; } - if (length > 0 && line[length-1] == '\n') { - line[length-1] = '\0'; + if (length > 0 && line[length - 1] == '\n') { + line[length - 1] = '\0'; --length; } @@ -607,14 +573,14 @@ public OptionalText_t ask(Text_t prompt, bool bold, bool force_tty) ret = Text$from_strn(gc_input, (size_t)(length)); - cleanup: +cleanup: if (out && out != stdout) fclose(out); if (in && in != stdin) fclose(in); return ret; } -public bool pop_flag(char **argv, int *i, const char *flag, Text_t *result) -{ +public +bool pop_flag(char **argv, int *i, const char *flag, Text_t *result) { if (argv[*i][0] != '-' || argv[*i][1] != '-') { return false; } else if (streq(argv[*i] + 2, flag)) { @@ -637,23 +603,21 @@ public bool pop_flag(char **argv, int *i, const char *flag, Text_t *result) } } -public void sleep_num(double seconds) -{ +public +void sleep_num(double seconds) { struct timespec ts; ts.tv_sec = (time_t)seconds; ts.tv_nsec = (long)((seconds - (double)ts.tv_sec) * 1e9); nanosleep(&ts, NULL); } -public OptionalText_t getenv_text(Text_t name) -{ +public +OptionalText_t getenv_text(Text_t name) { const char *val = getenv(Text$as_c_string(name)); return val ? Text$from_str(val) : NONE_TEXT; } -public void setenv_text(Text_t name, Text_t value) -{ - setenv(Text$as_c_string(name), Text$as_c_string(value), 1); -} +public +void setenv_text(Text_t name, Text_t value) { setenv(Text$as_c_string(name), Text$as_c_string(value), 1); } // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/stdlib.h b/src/stdlib/stdlib.h index 8ac35f72..c676beb1 100644 --- a/src/stdlib/stdlib.h +++ b/src/stdlib/stdlib.h @@ -23,66 +23,72 @@ typedef struct { } cli_arg_t; void tomo_init(void); -void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, const char *version, int spec_len, cli_arg_t spec[spec_len]); -#define tomo_parse_args(argc, argv, usage, help, version, ...) \ - _tomo_parse_args(argc, argv, usage, help, version, sizeof((cli_arg_t[]){__VA_ARGS__})/sizeof(cli_arg_t), (cli_arg_t[]){__VA_ARGS__}) +void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, const char *version, int spec_len, + cli_arg_t spec[spec_len]); +#define tomo_parse_args(argc, argv, usage, help, version, ...) \ + _tomo_parse_args(argc, argv, usage, help, version, sizeof((cli_arg_t[]){__VA_ARGS__}) / sizeof(cli_arg_t), \ + (cli_arg_t[]){__VA_ARGS__}) -#define fail(...) ({ \ - fflush(stdout); \ - if (USE_COLOR) fputs("\x1b[31;7m ==================== ERROR ==================== \033[m\n\n", stderr); \ - else fputs("==================== ERROR ====================\n\n", stderr); \ - print_stacktrace(stderr, 1); \ - if (USE_COLOR) fputs("\n\x1b[31;1m", stderr); \ - else fputs("\n", stderr); \ - fprint_inline(stderr, "Error: ", __VA_ARGS__); \ - if (USE_COLOR) fputs("\x1b[m\n", stderr); \ - else fputs("\n", stderr); \ - fflush(stderr); \ - raise(SIGABRT); \ - _exit(1); \ -}) +#define fail(...) \ + ({ \ + fflush(stdout); \ + if (USE_COLOR) fputs("\x1b[31;7m ==================== ERROR ==================== \033[m\n\n", stderr); \ + else fputs("==================== ERROR ====================\n\n", stderr); \ + print_stacktrace(stderr, 1); \ + if (USE_COLOR) fputs("\n\x1b[31;1m", stderr); \ + else fputs("\n", stderr); \ + fprint_inline(stderr, "Error: ", __VA_ARGS__); \ + if (USE_COLOR) fputs("\x1b[m\n", stderr); \ + else fputs("\n", stderr); \ + fflush(stderr); \ + raise(SIGABRT); \ + _exit(1); \ + }) -#define fail_source(filename, start, end, ...) ({ \ - fflush(stdout); \ - if (USE_COLOR) fputs("\x1b[31;7m ==================== ERROR ==================== \n\n\x1b[0;1m", stderr); \ - else fputs("==================== ERROR ====================\n\n", stderr); \ - print_stacktrace(stderr, 0); \ - fputs("\n", stderr); \ - if (USE_COLOR) fputs("\x1b[31;1m", stderr); \ - fprint_inline(stderr, __VA_ARGS__); \ - file_t *_file = (filename) ? load_file(filename) : NULL; \ - if ((filename) && _file) { \ - fputs("\n", stderr); \ - highlight_error(_file, _file->text+(start), _file->text+(end), "\x1b[31;1m", 1, USE_COLOR); \ - } \ - if (USE_COLOR) fputs("\x1b[m", stderr); \ - fflush(stderr); \ - raise(SIGABRT); \ - _exit(1); \ -}) +#define fail_source(filename, start, end, ...) \ + ({ \ + fflush(stdout); \ + if (USE_COLOR) fputs("\x1b[31;7m ==================== ERROR ==================== \n\n\x1b[0;1m", stderr); \ + else fputs("==================== ERROR ====================\n\n", stderr); \ + print_stacktrace(stderr, 0); \ + fputs("\n", stderr); \ + if (USE_COLOR) fputs("\x1b[31;1m", stderr); \ + fprint_inline(stderr, __VA_ARGS__); \ + file_t *_file = (filename) ? load_file(filename) : NULL; \ + if ((filename) && _file) { \ + fputs("\n", stderr); \ + highlight_error(_file, _file->text + (start), _file->text + (end), "\x1b[31;1m", 1, USE_COLOR); \ + } \ + if (USE_COLOR) fputs("\x1b[m", stderr); \ + fflush(stderr); \ + raise(SIGABRT); \ + _exit(1); \ + }) _Noreturn void fail_text(Text_t message); Text_t builtin_last_err(); -__attribute__((nonnull)) -void start_inspect(const char *filename, int64_t start, int64_t end); +__attribute__((nonnull)) void start_inspect(const char *filename, int64_t start, int64_t end); void end_inspect(const void *expr, const TypeInfo_t *type); -#define inspect(type, expr, typeinfo, start, end) {\ - start_inspect(__SOURCE_FILE__, start, end); \ - type _expr = expr; \ - end_inspect(&_expr, typeinfo); \ -} -#define inspect_void(expr, typeinfo, start, end) {\ - start_inspect(__SOURCE_FILE__, start, end); \ - expr; \ - end_inspect(NULL, typeinfo); \ -} -__attribute__((nonnull)) -void test_value(const char *filename, int64_t start, int64_t end, const void *expr, const void *expected, const TypeInfo_t *type); -#define test(type, expr, expected, typeinfo, start, end) {\ - type _expr = expr; \ - type _expected = expected; \ - test_value(__SOURCE_FILE__, start, end, &_expr, &_expected, typeinfo); \ -} +#define inspect(type, expr, typeinfo, start, end) \ + { \ + start_inspect(__SOURCE_FILE__, start, end); \ + type _expr = expr; \ + end_inspect(&_expr, typeinfo); \ + } +#define inspect_void(expr, typeinfo, start, end) \ + { \ + start_inspect(__SOURCE_FILE__, start, end); \ + expr; \ + end_inspect(NULL, typeinfo); \ + } +__attribute__((nonnull)) void test_value(const char *filename, int64_t start, int64_t end, const void *expr, + const void *expected, const TypeInfo_t *type); +#define test(type, expr, expected, typeinfo, start, end) \ + { \ + type _expr = expr; \ + type _expected = expected; \ + test_value(__SOURCE_FILE__, start, end, &_expr, &_expected, typeinfo); \ + } void say(Text_t text, bool newline); Text_t ask(Text_t prompt, bool bold, bool force_tty); diff --git a/src/stdlib/structs.c b/src/stdlib/structs.c index 5d6b2319..d2300b74 100644 --- a/src/stdlib/structs.c +++ b/src/stdlib/structs.c @@ -13,13 +13,10 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstack-protector" #endif -PUREFUNC public uint64_t Struct$hash(const void *obj, const TypeInfo_t *type) -{ - if (type->StructInfo.num_fields == 0) - return 0; +PUREFUNC public uint64_t Struct$hash(const void *obj, const TypeInfo_t *type) { + if (type->StructInfo.num_fields == 0) return 0; - if (type->StructInfo.num_fields == 1) - return generic_hash(obj, type->StructInfo.fields[0].type); + if (type->StructInfo.num_fields == 1) return generic_hash(obj, type->StructInfo.fields[0].type); uint64_t field_hashes[type->StructInfo.num_fields]; ptrdiff_t byte_offset = 0; @@ -27,7 +24,7 @@ PUREFUNC public uint64_t Struct$hash(const void *obj, const TypeInfo_t *type) for (int i = 0; i < type->StructInfo.num_fields; i++) { NamedType_t field = type->StructInfo.fields[i]; if (field.type == &Bool$info) { - bool b = ((*(char*)(obj + byte_offset)) >> bit_offset) & 0x1; + bool b = ((*(char *)(obj + byte_offset)) >> bit_offset) & 0x1; field_hashes[i] = (uint32_t)b; bit_offset += 1; if (bit_offset >= 8) { @@ -45,34 +42,29 @@ PUREFUNC public uint64_t Struct$hash(const void *obj, const TypeInfo_t *type) byte_offset += field.type->size; } } - return siphash24((void*)field_hashes, sizeof(field_hashes)); + return siphash24((void *)field_hashes, sizeof(field_hashes)); } #ifdef __GNUC__ #pragma GCC diagnostic pop #endif -PUREFUNC public uint64_t PackedData$hash(const void *obj, const TypeInfo_t *type) -{ - if (type->StructInfo.num_fields == 0) - return 0; +PUREFUNC public uint64_t PackedData$hash(const void *obj, const TypeInfo_t *type) { + if (type->StructInfo.num_fields == 0) return 0; return siphash24(obj, (size_t)type->size); } -PUREFUNC public int32_t Struct$compare(const void *x, const void *y, const TypeInfo_t *type) -{ - if (x == y) - return 0; +PUREFUNC public int32_t Struct$compare(const void *x, const void *y, const TypeInfo_t *type) { + if (x == y) return 0; ptrdiff_t byte_offset = 0; ptrdiff_t bit_offset = 0; for (int i = 0; i < type->StructInfo.num_fields; i++) { NamedType_t field = type->StructInfo.fields[i]; if (field.type == &Bool$info) { - bool bx = ((*(char*)(x + byte_offset)) >> bit_offset) & 0x1; - bool by = ((*(char*)(y + byte_offset)) >> bit_offset) & 0x1; - if (bx != by) - return (int32_t)bx - (int32_t)by; + bool bx = ((*(char *)(x + byte_offset)) >> bit_offset) & 0x1; + bool by = ((*(char *)(y + byte_offset)) >> bit_offset) & 0x1; + if (bx != by) return (int32_t)bx - (int32_t)by; bit_offset += 1; if (bit_offset >= 8) { byte_offset += 1; @@ -86,28 +78,24 @@ PUREFUNC public int32_t Struct$compare(const void *x, const void *y, const TypeI if (field.type->align && byte_offset % field.type->align > 0) byte_offset += field.type->align - (byte_offset % field.type->align); int32_t cmp = generic_compare(x + byte_offset, y + byte_offset, field.type); - if (cmp != 0) - return cmp; + if (cmp != 0) return cmp; byte_offset += field.type->size; } } return 0; } -PUREFUNC public bool Struct$equal(const void *x, const void *y, const TypeInfo_t *type) -{ - if (x == y) - return true; +PUREFUNC public bool Struct$equal(const void *x, const void *y, const TypeInfo_t *type) { + if (x == y) return true; ptrdiff_t byte_offset = 0; ptrdiff_t bit_offset = 0; for (int i = 0; i < type->StructInfo.num_fields; i++) { NamedType_t field = type->StructInfo.fields[i]; if (field.type == &Bool$info) { - bool bx = ((*(char*)(x + byte_offset)) >> bit_offset) & 0x1; - bool by = ((*(char*)(y + byte_offset)) >> bit_offset) & 0x1; - if (bx != by) - return false; + bool bx = ((*(char *)(x + byte_offset)) >> bit_offset) & 0x1; + bool by = ((*(char *)(y + byte_offset)) >> bit_offset) & 0x1; + if (bx != by) return false; bit_offset += 1; if (bit_offset >= 8) { byte_offset += 1; @@ -120,22 +108,19 @@ PUREFUNC public bool Struct$equal(const void *x, const void *y, const TypeInfo_t } if (field.type->align && byte_offset % field.type->align > 0) byte_offset += field.type->align - (byte_offset % field.type->align); - if (!generic_equal(x + byte_offset, y + byte_offset, field.type)) - return false; + if (!generic_equal(x + byte_offset, y + byte_offset, field.type)) return false; byte_offset += field.type->size; } } return true; } -PUREFUNC public bool PackedData$equal(const void *x, const void *y, const TypeInfo_t *type) -{ +PUREFUNC public bool PackedData$equal(const void *x, const void *y, const TypeInfo_t *type) { if (x == y) return true; return (memcmp(x, y, (size_t)type->size) == 0); } -PUREFUNC public Text_t Struct$as_text(const void *obj, bool colorize, const TypeInfo_t *type) -{ +PUREFUNC public Text_t Struct$as_text(const void *obj, bool colorize, const TypeInfo_t *type) { if (!obj) return Text$from_str(type->StructInfo.name); Text_t name = Text$from_str(type->StructInfo.name); @@ -148,15 +133,14 @@ PUREFUNC public Text_t Struct$as_text(const void *obj, bool colorize, const Type ptrdiff_t bit_offset = 0; for (int i = 0; i < type->StructInfo.num_fields; i++) { NamedType_t field = type->StructInfo.fields[i]; - if (i > 0) - text = Text$concat(text, Text(", ")); + if (i > 0) text = Text$concat(text, Text(", ")); - if (type->StructInfo.num_fields > 1) - text = Text$concat(text, Text$from_str(field.name), Text("=")); + if (type->StructInfo.num_fields > 1) text = Text$concat(text, Text$from_str(field.name), Text("=")); if (field.type == &Bool$info) { - bool b = ((*(char*)(obj + byte_offset)) >> bit_offset) & 0x1; - text = Text$concat(text, Text$from_str(colorize ? (b ? "\x1b[35myes\x1b[m" : "\x1b[35mno\x1b[m") : (b ? "yes" : "no"))); + bool b = ((*(char *)(obj + byte_offset)) >> bit_offset) & 0x1; + text = Text$concat( + text, Text$from_str(colorize ? (b ? "\x1b[35myes\x1b[m" : "\x1b[35mno\x1b[m") : (b ? "yes" : "no"))); bit_offset += 1; if (bit_offset >= 8) { byte_offset += 1; @@ -176,19 +160,16 @@ PUREFUNC public Text_t Struct$as_text(const void *obj, bool colorize, const Type return Text$concat(text, Text(")")); } -PUREFUNC public bool Struct$is_none(const void *obj, const TypeInfo_t *type) -{ - return *(bool*)(obj + type->size); -} +PUREFUNC public bool Struct$is_none(const void *obj, const TypeInfo_t *type) { return *(bool *)(obj + type->size); } -public void Struct$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) -{ +public +void Struct$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) { ptrdiff_t byte_offset = 0; ptrdiff_t bit_offset = 0; for (int i = 0; i < type->StructInfo.num_fields; i++) { NamedType_t field = type->StructInfo.fields[i]; if (field.type == &Bool$info) { - bool b = ((*(char*)(obj + byte_offset)) >> bit_offset) & 0x1; + bool b = ((*(char *)(obj + byte_offset)) >> bit_offset) & 0x1; fputc((int)b, out); bit_offset += 1; if (bit_offset >= 8) { @@ -208,15 +189,15 @@ public void Struct$serialize(const void *obj, FILE *out, Table_t *pointers, cons } } -public void Struct$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type) -{ +public +void Struct$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type) { ptrdiff_t byte_offset = 0; ptrdiff_t bit_offset = 0; for (int i = 0; i < type->StructInfo.num_fields; i++) { NamedType_t field = type->StructInfo.fields[i]; if (field.type == &Bool$info) { bool b = (bool)fgetc(in); - *(char*)(outval + byte_offset) |= (b << bit_offset); + *(char *)(outval + byte_offset) |= (b << bit_offset); bit_offset += 1; if (bit_offset >= 8) { byte_offset += 1; diff --git a/src/stdlib/structs.h b/src/stdlib/structs.h index c9c6c40a..03c051ec 100644 --- a/src/stdlib/structs.h +++ b/src/stdlib/structs.h @@ -17,24 +17,26 @@ PUREFUNC bool Struct$is_none(const void *obj, const TypeInfo_t *type); void Struct$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type); void Struct$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type); -#define Struct$metamethods { \ - .hash=Struct$hash, \ - .compare=Struct$compare, \ - .equal=Struct$equal, \ - .as_text=Struct$as_text, \ - .is_none=Struct$is_none, \ - .serialize=Struct$serialize, \ - .deserialize=Struct$deserialize, \ -} +#define Struct$metamethods \ + { \ + .hash = Struct$hash, \ + .compare = Struct$compare, \ + .equal = Struct$equal, \ + .as_text = Struct$as_text, \ + .is_none = Struct$is_none, \ + .serialize = Struct$serialize, \ + .deserialize = Struct$deserialize, \ + } -#define PackedData$metamethods { \ - .hash=PackedData$hash, \ - .compare=Struct$compare, \ - .equal=PackedData$equal, \ - .as_text=Struct$as_text, \ - .is_none=Struct$is_none, \ - .serialize=Struct$serialize, \ - .deserialize=Struct$deserialize, \ -} +#define PackedData$metamethods \ + { \ + .hash = PackedData$hash, \ + .compare = Struct$compare, \ + .equal = PackedData$equal, \ + .as_text = Struct$as_text, \ + .is_none = Struct$is_none, \ + .serialize = Struct$serialize, \ + .deserialize = Struct$deserialize, \ + } // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/tables.c b/src/stdlib/tables.c index 63f8eb09..781da415 100644 --- a/src/stdlib/tables.c +++ b/src/stdlib/tables.c @@ -41,29 +41,29 @@ #define EQUAL_KEYS(x, y) (generic_equal((x), (y), type->TableInfo.key)) #define END_OF_CHAIN UINT32_MAX -#define GET_ENTRY(t, i) ((t).entries.data + (t).entries.stride*(i)) +#define GET_ENTRY(t, i) ((t).entries.data + (t).entries.stride * (i)) static TypeInfo_t MemoryPointer = { - .size=sizeof(void*), - .align=__alignof__(void*), - .tag=PointerInfo, - .PointerInfo={ - .sigil="@", - .pointed=&Memory$info, - }, - .metamethods=Pointer$metamethods, + .size = sizeof(void *), + .align = __alignof__(void *), + .tag = PointerInfo, + .PointerInfo = + { + .sigil = "@", + .pointed = &Memory$info, + }, + .metamethods = Pointer$metamethods, }; const TypeInfo_t CStrToVoidStarTable = { - .size=sizeof(Table_t), - .align=__alignof__(Table_t), - .tag=TableInfo, - .TableInfo={.key=&CString$info, .value=&MemoryPointer}, - .metamethods=Table$metamethods, + .size = sizeof(Table_t), + .align = __alignof__(Table_t), + .tag = TableInfo, + .TableInfo = {.key = &CString$info, .value = &MemoryPointer}, + .metamethods = Table$metamethods, }; -PUREFUNC static INLINE size_t entry_size(const TypeInfo_t *info) -{ +PUREFUNC static INLINE size_t entry_size(const TypeInfo_t *info) { size_t size = (size_t)info->TableInfo.key->size; if (info->TableInfo.value->align > 1 && size % (size_t)info->TableInfo.value->align) size += (size_t)info->TableInfo.value->align - (size % (size_t)info->TableInfo.value->align); // padding @@ -73,31 +73,27 @@ PUREFUNC static INLINE size_t entry_size(const TypeInfo_t *info) return size; } -PUREFUNC static INLINE size_t value_offset(const TypeInfo_t *info) -{ +PUREFUNC static INLINE size_t value_offset(const TypeInfo_t *info) { size_t offset = (size_t)info->TableInfo.key->size; if ((size_t)info->TableInfo.value->align > 1 && offset % (size_t)info->TableInfo.value->align) offset += (size_t)info->TableInfo.value->align - (offset % (size_t)info->TableInfo.value->align); // padding return offset; } -static INLINE void hshow(const Table_t *t) -{ +static INLINE void hshow(const Table_t *t) { hdebug("{"); for (uint32_t i = 0; t->bucket_info && i < t->bucket_info->count; i++) { if (i > 0) hdebug(" "); if (t->bucket_info->buckets[i].occupied) - hdebug("[", i, "]=", (uint32_t)t->bucket_info->buckets[i].index, "(", t->bucket_info->buckets[i].next_bucket, ")"); - else - hdebug("[", i, "]=_"); + hdebug("[", i, "]=", (uint32_t)t->bucket_info->buckets[i].index, "(", + t->bucket_info->buckets[i].next_bucket, ")"); + else hdebug("[", i, "]=_"); } hdebug("}\n"); } -static void maybe_copy_on_write(Table_t *t, const TypeInfo_t *type) -{ - if (t->entries.data_refcount != 0) - List$compact(&t->entries, (int64_t)entry_size(type)); +static void maybe_copy_on_write(Table_t *t, const TypeInfo_t *type) { + if (t->entries.data_refcount != 0) List$compact(&t->entries, (int64_t)entry_size(type)); if (t->bucket_info && t->bucket_info->data_refcount != 0) { size_t size = sizeof(bucket_info_t) + sizeof(bucket_t[t->bucket_info->count]); @@ -107,8 +103,7 @@ static void maybe_copy_on_write(Table_t *t, const TypeInfo_t *type) } // Return address of value or NULL -PUREFUNC public void *Table$get_raw(Table_t t, const void *key, const TypeInfo_t *type) -{ +PUREFUNC public void *Table$get_raw(Table_t t, const void *key, const TypeInfo_t *type) { assert(type->tag == TableInfo); if (!key || !t.bucket_info) return NULL; @@ -123,14 +118,12 @@ PUREFUNC public void *Table$get_raw(Table_t t, const void *key, const TypeInfo_t hdebug("Found key!\n"); return entry + value_offset(type); } - if (buckets[i].next_bucket == END_OF_CHAIN) - break; + if (buckets[i].next_bucket == END_OF_CHAIN) break; } return NULL; } -PUREFUNC public void *Table$get(Table_t t, const void *key, const TypeInfo_t *type) -{ +PUREFUNC public void *Table$get(Table_t t, const void *key, const TypeInfo_t *type) { assert(type->tag == TableInfo); for (const Table_t *iter = &t; iter; iter = iter->fallback) { void *ret = Table$get_raw(*iter, key, type); @@ -139,8 +132,7 @@ PUREFUNC public void *Table$get(Table_t t, const void *key, const TypeInfo_t *ty return NULL; } -static void Table$set_bucket(Table_t *t, const void *entry, int32_t index, const TypeInfo_t *type) -{ +static void Table$set_bucket(Table_t *t, const void *entry, int32_t index, const TypeInfo_t *type) { assert(t->bucket_info); hshow(t); const void *key = entry; @@ -182,15 +174,14 @@ static void Table$set_bucket(Table_t *t, const void *entry, int32_t index, const bucket->next_bucket = END_OF_CHAIN; } else { // Collided with the start of a chain, put the new entry in chain position #2 hdebug("Hit start of a chain\n"); - buckets[t->bucket_info->last_free] = (bucket_t){ - .occupied = 1, .index=index, .next_bucket=bucket->next_bucket}; + buckets[t->bucket_info->last_free] = + (bucket_t){.occupied = 1, .index = index, .next_bucket = bucket->next_bucket}; bucket->next_bucket = t->bucket_info->last_free; } hshow(t); } -static void hashmap_resize_buckets(Table_t *t, uint32_t new_capacity, const TypeInfo_t *type) -{ +static void hashmap_resize_buckets(Table_t *t, uint32_t new_capacity, const TypeInfo_t *type) { if (unlikely(new_capacity > TABLE_MAX_BUCKETS)) fail("Table has exceeded the maximum table size (2^31) and cannot grow further!"); hdebug("About to resize from ", t->bucket_info ? (int32_t)t->bucket_info->count : 0, " to ", new_capacity, "\n"); @@ -199,7 +190,7 @@ static void hashmap_resize_buckets(Table_t *t, uint32_t new_capacity, const Type t->bucket_info = GC_MALLOC_ATOMIC(alloc_size); memset(t->bucket_info->buckets, 0, sizeof(bucket_t[new_capacity])); t->bucket_info->count = new_capacity; - t->bucket_info->last_free = new_capacity-1; + t->bucket_info->last_free = new_capacity - 1; // Rehash: for (int64_t i = 0; i < Table$length(*t); i++) { hdebug("Rehashing ", i, "\n"); @@ -215,16 +206,15 @@ static void hashmap_resize_buckets(Table_t *t, uint32_t new_capacity, const Type #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstack-protector" #endif -public void *Table$reserve(Table_t *t, const void *key, const void *value, const TypeInfo_t *type) -{ +public +void *Table$reserve(Table_t *t, const void *key, const void *value, const TypeInfo_t *type) { assert(type->tag == TableInfo); if (!t || !key) return NULL; hshow(t); t->hash = 0; - int64_t key_size = type->TableInfo.key->size, - value_size = type->TableInfo.value->size; + int64_t key_size = type->TableInfo.key->size, value_size = type->TableInfo.value->size; if (!t->bucket_info || t->bucket_info->count == 0) { hashmap_resize_buckets(t, 8, type); } else { @@ -236,8 +226,7 @@ public void *Table$reserve(Table_t *t, const void *key, const void *value, const maybe_copy_on_write(t, type); value_home = t->entries.data + offset; - if (value && value_size > 0) - memcpy(value_home, value, (size_t)value_size); + if (value && value_size > 0) memcpy(value_home, value, (size_t)value_size); return value_home; } @@ -247,9 +236,8 @@ public void *Table$reserve(Table_t *t, const void *key, const void *value, const // Resize buckets if necessary if (t->entries.length >= (int64_t)t->bucket_info->count) { // Current resize policy: +50% at a time: - uint32_t newsize = MAX(8, (uint32_t)(3*t->bucket_info->count)/2); - if (unlikely(newsize > TABLE_MAX_BUCKETS)) - newsize = TABLE_MAX_BUCKETS; + uint32_t newsize = MAX(8, (uint32_t)(3 * t->bucket_info->count) / 2); + if (unlikely(newsize > TABLE_MAX_BUCKETS)) newsize = TABLE_MAX_BUCKETS; hashmap_resize_buckets(t, newsize, type); } @@ -265,13 +253,11 @@ public void *Table$reserve(Table_t *t, const void *key, const void *value, const char buf[entry_size(type)]; memset(buf, 0, sizeof(buf)); memcpy(buf, key, (size_t)key_size); - if (value && value_size > 0) - memcpy(buf + value_offset(type), value, (size_t)value_size); - else if (value_size > 0) - memset(buf + value_offset(type), 0, (size_t)value_size); + if (value && value_size > 0) memcpy(buf + value_offset(type), value, (size_t)value_size); + else if (value_size > 0) memset(buf + value_offset(type), 0, (size_t)value_size); List$insert(&t->entries, buf, I(0), (int64_t)entry_size(type)); - int64_t entry_index = t->entries.length-1; + int64_t entry_index = t->entries.length - 1; void *entry = GET_ENTRY(*t, entry_index); Table$set_bucket(t, entry, entry_index, type); return entry + value_offset(type); @@ -280,14 +266,14 @@ public void *Table$reserve(Table_t *t, const void *key, const void *value, const #pragma GCC diagnostic pop #endif -public void Table$set(Table_t *t, const void *key, const void *value, const TypeInfo_t *type) -{ +public +void Table$set(Table_t *t, const void *key, const void *value, const TypeInfo_t *type) { assert(type->tag == TableInfo); (void)Table$reserve(t, key, value, type); } -public void Table$remove(Table_t *t, const void *key, const TypeInfo_t *type) -{ +public +void Table$remove(Table_t *t, const void *key, const TypeInfo_t *type) { assert(type->tag == TableInfo); if (!t || Table$length(*t) == 0) return; @@ -295,8 +281,7 @@ public void Table$remove(Table_t *t, const void *key, const TypeInfo_t *type) maybe_copy_on_write(t, type); // If unspecified, pop the last key: - if (!key) - key = GET_ENTRY(*t, t->entries.length-1); + if (!key) key = GET_ENTRY(*t, t->entries.length - 1); // Steps: look up the bucket for the removed key // If missing, then return immediately @@ -322,13 +307,12 @@ public void Table$remove(Table_t *t, const void *key, const TypeInfo_t *type) hdebug("Found key to delete in bucket ", i, "\n"); goto found_it; } - if (t->bucket_info->buckets[i].next_bucket == END_OF_CHAIN) - return; + if (t->bucket_info->buckets[i].next_bucket == END_OF_CHAIN) return; prev = &t->bucket_info->buckets[i]; } return; - found_it:; +found_it:; assert(bucket->occupied); t->hash = 0; @@ -337,7 +321,7 @@ public void Table$remove(Table_t *t, const void *key, const TypeInfo_t *type) // swap the other entry into the last position and then remove the last // entry. This disturbs the ordering of the table, but keeps removal O(1) // instead of O(N) - int64_t last_entry = t->entries.length-1; + int64_t last_entry = t->entries.length - 1; if (bucket->index != last_entry) { hdebug("Removing key/value from the middle of the entries list\n"); @@ -374,70 +358,58 @@ public void Table$remove(Table_t *t, const void *key, const TypeInfo_t *type) } t->bucket_info->buckets[bucket_to_clear] = (bucket_t){0}; - if (bucket_to_clear > t->bucket_info->last_free) - t->bucket_info->last_free = bucket_to_clear; + if (bucket_to_clear > t->bucket_info->last_free) t->bucket_info->last_free = bucket_to_clear; hshow(t); } -CONSTFUNC public void *Table$entry(Table_t t, int64_t n) -{ - if (n < 1 || n > Table$length(t)) - return NULL; - return GET_ENTRY(t, n-1); +CONSTFUNC public void *Table$entry(Table_t t, int64_t n) { + if (n < 1 || n > Table$length(t)) return NULL; + return GET_ENTRY(t, n - 1); } -public void Table$clear(Table_t *t) -{ - memset(t, 0, sizeof(Table_t)); -} +public +void Table$clear(Table_t *t) { memset(t, 0, sizeof(Table_t)); } -public Table_t Table$sorted(Table_t t, const TypeInfo_t *type) -{ - Closure_t cmp = (Closure_t){.fn=generic_compare, .userdata=(void*)type->TableInfo.key}; +public +Table_t Table$sorted(Table_t t, const TypeInfo_t *type) { + Closure_t cmp = (Closure_t){.fn = generic_compare, .userdata = (void *)type->TableInfo.key}; List_t entries = List$sorted(t.entries, cmp, (int64_t)entry_size(type)); return Table$from_entries(entries, type); } -PUREFUNC public bool Table$equal(const void *vx, const void *vy, const TypeInfo_t *type) -{ +PUREFUNC public bool Table$equal(const void *vx, const void *vy, const TypeInfo_t *type) { if (vx == vy) return true; - Table_t *x = (Table_t*)vx, *y = (Table_t*)vy; + Table_t *x = (Table_t *)vx, *y = (Table_t *)vy; - if (x->hash && y->hash && x->hash != y->hash) - return false; + if (x->hash && y->hash && x->hash != y->hash) return false; assert(type->tag == TableInfo); - if (x->entries.length != y->entries.length) - return false; - - if ((x->fallback != NULL) != (y->fallback != NULL)) - return false; + if (x->entries.length != y->entries.length) return false; + + if ((x->fallback != NULL) != (y->fallback != NULL)) return false; const TypeInfo_t *value_type = type->TableInfo.value; size_t offset = value_offset(type); for (int64_t i = 0; i < x->entries.length; i++) { - void *x_key = x->entries.data + i*x->entries.stride; + void *x_key = x->entries.data + i * x->entries.stride; void *y_value = Table$get_raw(*y, x_key, type); if (!y_value) return false; void *x_value = x_key + offset; - if (!generic_equal(y_value, x_value, value_type)) - return false; + if (!generic_equal(y_value, x_value, value_type)) return false; } return true; } -PUREFUNC public int32_t Table$compare(const void *vx, const void *vy, const TypeInfo_t *type) -{ +PUREFUNC public int32_t Table$compare(const void *vx, const void *vy, const TypeInfo_t *type) { if (vx == vy) return 0; - Table_t *x = (Table_t*)vx, *y = (Table_t*)vy; + Table_t *x = (Table_t *)vx, *y = (Table_t *)vy; assert(type->tag == TableInfo); __typeof(type->TableInfo) table = type->TableInfo; // Sort empty tables before non-empty tables: - if (x->entries.length == 0 || y->entries.length == 0) - return ((x->entries.length > 0) - (y->entries.length > 0)); + if (x->entries.length == 0 || y->entries.length == 0) return ((x->entries.length > 0) - (y->entries.length > 0)); // Table comparison rules: // - If two tables have different keys, then compare as if comparing a @@ -446,22 +418,20 @@ PUREFUNC public int32_t Table$compare(const void *vx, const void *vy, const Type // - Otherwise, compare as if comparing lists of values for the sorted key // lists: // `[x[k] for k in x.keys.sorted()] <> [y[k] for k in y.keys.sorted()]` - // + // // We can do this in _linear_ time if we find the smallest `k` such that // `x[k] != y[k]`, as well as the largest key in `x` and `y`. void *mismatched_key = NULL, *max_x_key = NULL; for (int64_t i = 0; i < x->entries.length; i++) { void *key = x->entries.data + x->entries.stride * i; - if (max_x_key == NULL || generic_compare(key, max_x_key, table.key) > 0) - max_x_key = key; + if (max_x_key == NULL || generic_compare(key, max_x_key, table.key) > 0) max_x_key = key; void *x_value = key + value_offset(type); void *y_value = Table$get_raw(*y, key, type); if (!y_value || (table.value->size > 0 && !generic_equal(x_value, y_value, table.value))) { - if (mismatched_key == NULL || generic_compare(key, mismatched_key, table.key) < 0) - mismatched_key = key; + if (mismatched_key == NULL || generic_compare(key, mismatched_key, table.key) < 0) mismatched_key = key; } } @@ -470,14 +440,12 @@ PUREFUNC public int32_t Table$compare(const void *vx, const void *vy, const Type void *max_y_key = NULL; for (int64_t i = 0; i < y->entries.length; i++) { void *key = y->entries.data + y->entries.stride * i; - if (max_y_key == NULL || generic_compare(key, max_y_key, table.key) > 0) - max_y_key = key; + if (max_y_key == NULL || generic_compare(key, max_y_key, table.key) > 0) max_y_key = key; void *y_value = key + value_offset(type); void *x_value = Table$get_raw(*x, key, type); if (!x_value || !generic_equal(x_value, y_value, table.value)) { - if (mismatched_key == NULL || generic_compare(key, mismatched_key, table.key) < 0) - mismatched_key = key; + if (mismatched_key == NULL || generic_compare(key, mismatched_key, table.key) < 0) mismatched_key = key; } } @@ -529,12 +497,10 @@ PUREFUNC public int32_t Table$compare(const void *vx, const void *vy, const Type return 0; } -PUREFUNC public uint64_t Table$hash(const void *obj, const TypeInfo_t *type) -{ +PUREFUNC public uint64_t Table$hash(const void *obj, const TypeInfo_t *type) { assert(type->tag == TableInfo); - Table_t *t = (Table_t*)obj; - if (t->hash != 0) - return t->hash; + Table_t *t = (Table_t *)obj; + if (t->hash != 0) return t->hash; // Table hashes are computed as: // hash(t.length, (xor: t.keys), (xor: t.values), t.fallback) @@ -544,12 +510,12 @@ PUREFUNC public uint64_t Table$hash(const void *obj, const TypeInfo_t *type) size_t offset = value_offset(type); if (table.value->size > 0) { for (int64_t i = 0; i < t->entries.length; i++) { - keys_hash ^= generic_hash(t->entries.data + i*t->entries.stride, table.key); - values_hash ^= generic_hash(t->entries.data + i*t->entries.stride + offset, table.value); + keys_hash ^= generic_hash(t->entries.data + i * t->entries.stride, table.key); + values_hash ^= generic_hash(t->entries.data + i * t->entries.stride + offset, table.value); } } else { for (int64_t i = 0; i < t->entries.length; i++) - keys_hash ^= generic_hash(t->entries.data + i*t->entries.stride, table.key); + keys_hash ^= generic_hash(t->entries.data + i * t->entries.stride, table.key); } volatile struct { @@ -561,41 +527,31 @@ PUREFUNC public uint64_t Table$hash(const void *obj, const TypeInfo_t *type) values_hash, t->fallback ? Table$hash(t->fallback, type) : 0, }; - t->hash = siphash24((void*)&components, sizeof(components)); - if unlikely (t->hash == 0) - t->hash = 1234567; + t->hash = siphash24((void *)&components, sizeof(components)); + if unlikely (t->hash == 0) t->hash = 1234567; return t->hash; } -public Text_t Table$as_text(const void *obj, bool colorize, const TypeInfo_t *type) -{ - Table_t *t = (Table_t*)obj; +public +Text_t Table$as_text(const void *obj, bool colorize, const TypeInfo_t *type) { + Table_t *t = (Table_t *)obj; assert(type->tag == TableInfo); __typeof(type->TableInfo) table = type->TableInfo; if (!t) { - if (table.value != &Void$info) - return Text$concat( - Text("{"), - generic_as_text(NULL, false, table.key), - Text("="), - generic_as_text(NULL, false, table.value), - Text("}")); - else - return Text$concat( - Text("|"), - generic_as_text(NULL, false, table.key), - Text("|")); + if (table.value != &Void$info) + return Text$concat(Text("{"), generic_as_text(NULL, false, table.key), Text("="), + generic_as_text(NULL, false, table.value), Text("}")); + else return Text$concat(Text("|"), generic_as_text(NULL, false, table.key), Text("|")); } int64_t val_off = (int64_t)value_offset(type); Text_t text = table.value == &Void$info ? Text("|") : Text("{"); for (int64_t i = 0, length = Table$length(*t); i < length; i++) { - if (i > 0) - text = Text$concat(text, Text(", ")); + if (i > 0) text = Text$concat(text, Text(", ")); void *entry = GET_ENTRY(*t, i); text = Text$concat(text, generic_as_text(entry, colorize, table.key)); - if (table.value != &Void$info) + if (table.value != &Void$info) text = Text$concat(text, Text("="), generic_as_text(entry + val_off, colorize, table.value)); } @@ -607,11 +563,10 @@ public Text_t Table$as_text(const void *obj, bool colorize, const TypeInfo_t *ty return text; } -public Table_t Table$from_entries(List_t entries, const TypeInfo_t *type) -{ +public +Table_t Table$from_entries(List_t entries, const TypeInfo_t *type) { assert(type->tag == TableInfo); - if (entries.length == 0) - return (Table_t){}; + if (entries.length == 0) return (Table_t){}; Table_t t = {}; int64_t length = entries.length + entries.length / 4; @@ -619,19 +574,19 @@ public Table_t Table$from_entries(List_t entries, const TypeInfo_t *type) t.bucket_info = GC_MALLOC_ATOMIC(alloc_size); memset(t.bucket_info->buckets, 0, sizeof(bucket_t[length])); t.bucket_info->count = length; - t.bucket_info->last_free = length-1; + t.bucket_info->last_free = length - 1; size_t offset = value_offset(type); for (int64_t i = 0; i < entries.length; i++) { - void *key = entries.data + i*entries.stride; + void *key = entries.data + i * entries.stride; Table$set(&t, key, key + offset, type); } return t; } // Overlap is "set intersection" in formal terms -public Table_t Table$overlap(Table_t a, Table_t b, const TypeInfo_t *type) -{ +public +Table_t Table$overlap(Table_t a, Table_t b, const TypeInfo_t *type) { // Return a table such that t[k]==a[k] for all k such that a.has(k), b.has(k), and a[k]==b[k] Table_t result = {}; const size_t offset = value_offset(type); @@ -648,9 +603,10 @@ public Table_t Table$overlap(Table_t a, Table_t b, const TypeInfo_t *type) } // With is "set union" in formal terms -public Table_t Table$with(Table_t a, Table_t b, const TypeInfo_t *type) -{ - // return a table such that t[k]==b[k] for all k such that b.has(k), and t[k]==a[k] for all k such that a.has(k) and not b.has(k) +public +Table_t Table$with(Table_t a, Table_t b, const TypeInfo_t *type) { + // return a table such that t[k]==b[k] for all k such that b.has(k), and t[k]==a[k] for all k such that a.has(k) and + // not b.has(k) Table_t result = {}; const size_t offset = value_offset(type); for (Table_t *t = &a; t; t = t->fallback) { @@ -669,31 +625,29 @@ public Table_t Table$with(Table_t a, Table_t b, const TypeInfo_t *type) } // Xor is "disjunctive union" or "symmetric difference" in formal terms -public Table_t Table$xor(Table_t a, Table_t b, const TypeInfo_t *type) -{ +public +Table_t Table$xor(Table_t a, Table_t b, const TypeInfo_t *type) { // return a table with elements in `a` or `b`, but not both Table_t result = {}; const size_t offset = value_offset(type); for (Table_t *t = &a; t; t = t->fallback) { for (int64_t i = 0; i < Table$length(*t); i++) { void *key = GET_ENTRY(*t, i); - if (Table$get(b, key, type) == NULL) - Table$set(&result, key, key + offset, type); + if (Table$get(b, key, type) == NULL) Table$set(&result, key, key + offset, type); } } for (Table_t *t = &b; t; t = t->fallback) { for (int64_t i = 0; i < Table$length(*t); i++) { void *key = GET_ENTRY(*t, i); - if (Table$get(a, key, type) == NULL) - Table$set(&result, key, key + offset, type); + if (Table$get(a, key, type) == NULL) Table$set(&result, key, key + offset, type); } } return result; } // Without is "set difference" in formal terms -public Table_t Table$without(Table_t a, Table_t b, const TypeInfo_t *type) -{ +public +Table_t Table$without(Table_t a, Table_t b, const TypeInfo_t *type) { // Return a table such that t[k]==a[k] for all k such that not b.has(k) or b[k] != a[k] Table_t result = {}; const size_t offset = value_offset(type); @@ -709,8 +663,8 @@ public Table_t Table$without(Table_t a, Table_t b, const TypeInfo_t *type) return result; } -public Table_t Table$with_fallback(Table_t t, OptionalTable_t fallback) -{ +public +Table_t Table$with_fallback(Table_t t, OptionalTable_t fallback) { if (fallback.entries.length <= 0) { t.fallback = NULL; return t; @@ -722,10 +676,8 @@ public Table_t Table$with_fallback(Table_t t, OptionalTable_t fallback) } } -PUREFUNC public bool Table$is_subset_of(Table_t a, Table_t b, bool strict, const TypeInfo_t *type) -{ - if (a.entries.length > b.entries.length || (strict && a.entries.length == b.entries.length)) - return false; +PUREFUNC public bool Table$is_subset_of(Table_t a, Table_t b, bool strict, const TypeInfo_t *type) { + if (a.entries.length > b.entries.length || (strict && a.entries.length == b.entries.length)) return false; for (int64_t i = 0; i < Table$length(a); i++) { void *found = Table$get_raw(b, GET_ENTRY(a, i), type); @@ -734,73 +686,61 @@ PUREFUNC public bool Table$is_subset_of(Table_t a, Table_t b, bool strict, const return true; } -PUREFUNC public bool Table$is_superset_of(Table_t a, Table_t b, bool strict, const TypeInfo_t *type) -{ +PUREFUNC public bool Table$is_superset_of(Table_t a, Table_t b, bool strict, const TypeInfo_t *type) { return Table$is_subset_of(b, a, strict, type); } -PUREFUNC public void *Table$str_get(Table_t t, const char *key) -{ +PUREFUNC public void *Table$str_get(Table_t t, const char *key) { void **ret = Table$get(t, &key, &CStrToVoidStarTable); return ret ? *ret : NULL; } -PUREFUNC public void *Table$str_get_raw(Table_t t, const char *key) -{ +PUREFUNC public void *Table$str_get_raw(Table_t t, const char *key) { void **ret = Table$get_raw(t, &key, &CStrToVoidStarTable); return ret ? *ret : NULL; } -public void *Table$str_reserve(Table_t *t, const char *key, const void *value) -{ +public +void *Table$str_reserve(Table_t *t, const char *key, const void *value) { return Table$reserve(t, &key, &value, &CStrToVoidStarTable); } -public void Table$str_set(Table_t *t, const char *key, const void *value) -{ - Table$set(t, &key, &value, &CStrToVoidStarTable); -} +public +void Table$str_set(Table_t *t, const char *key, const void *value) { Table$set(t, &key, &value, &CStrToVoidStarTable); } -public void Table$str_remove(Table_t *t, const char *key) -{ - return Table$remove(t, &key, &CStrToVoidStarTable); -} +public +void Table$str_remove(Table_t *t, const char *key) { return Table$remove(t, &key, &CStrToVoidStarTable); } -CONSTFUNC public void *Table$str_entry(Table_t t, int64_t n) -{ - return Table$entry(t, n); -} +CONSTFUNC public void *Table$str_entry(Table_t t, int64_t n) { return Table$entry(t, n); } -PUREFUNC public bool Table$is_none(const void *obj, const TypeInfo_t *info) -{ +PUREFUNC public bool Table$is_none(const void *obj, const TypeInfo_t *info) { (void)info; - return ((Table_t*)obj)->entries.length < 0; + return ((Table_t *)obj)->entries.length < 0; } -public void Table$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) -{ - Table_t *t = (Table_t*)obj; +public +void Table$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) { + Table_t *t = (Table_t *)obj; int64_t len = t->entries.length; Int64$serialize(&len, out, pointers, &Int64$info); size_t offset = value_offset(type); for (int64_t i = 0; i < len; i++) { - _serialize(t->entries.data + i*t->entries.stride, out, pointers, type->TableInfo.key); + _serialize(t->entries.data + i * t->entries.stride, out, pointers, type->TableInfo.key); if (type->TableInfo.value->size > 0) - _serialize(t->entries.data + i*t->entries.stride + offset, out, pointers, type->TableInfo.value); + _serialize(t->entries.data + i * t->entries.stride + offset, out, pointers, type->TableInfo.value); } assert(fputc(t->fallback != NULL ? 1 : 0, out) != EOF); - if (t->fallback) - Table$serialize(t->fallback, out, pointers, type); + if (t->fallback) Table$serialize(t->fallback, out, pointers, type); } #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstack-protector" #endif -public void Table$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type) -{ +public +void Table$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type) { int64_t len; Int64$deserialize(in, &len, pointers, &Int$info); @@ -809,8 +749,7 @@ public void Table$deserialize(FILE *in, void *outval, List_t *pointers, const Ty char key[type->TableInfo.key->size]; _deserialize(in, key, pointers, type->TableInfo.key); char value[type->TableInfo.value->size]; - if (type->TableInfo.value->size > 0) - _deserialize(in, value, pointers, type->TableInfo.value); + if (type->TableInfo.value->size > 0) _deserialize(in, value, pointers, type->TableInfo.value); Table$set(&t, key, value, type); } @@ -819,7 +758,7 @@ public void Table$deserialize(FILE *in, void *outval, List_t *pointers, const Ty Table$deserialize(in, t.fallback, pointers, type); } - *(Table_t*)outval = t; + *(Table_t *)outval = t; } #ifdef __GNUC__ #pragma GCC diagnostic pop diff --git a/src/stdlib/tables.h b/src/stdlib/tables.h index f80c16c3..65f35060 100644 --- a/src/stdlib/tables.h +++ b/src/stdlib/tables.h @@ -2,59 +2,94 @@ // Hash table datastructure with methods and type information -#include <stdint.h> #include <stdbool.h> +#include <stdint.h> #include <string.h> -#include "lists.h" #include "datatypes.h" +#include "lists.h" #include "types.h" #include "util.h" -#define Table(key_t, val_t, key_info, value_info, fb, N, ...) ({ \ - struct { key_t k; val_t v; } ents[N] = {__VA_ARGS__}; \ - Table_t table = Table$from_entries((List_t){ \ - .data=memcpy(GC_MALLOC(sizeof(ents)), ents, sizeof(ents)), \ - .length=sizeof(ents)/sizeof(ents[0]), \ - .stride=(void*)&ents[1] - (void*)&ents[0], \ - }, Table$info(key_info, value_info)); \ - table.fallback = fb; \ - table; }) -#define Set(item_t, item_info, N, ...) ({ \ - item_t ents[N] = {__VA_ARGS__}; \ - Table_t set = Table$from_entries((List_t){ \ - .data=memcpy(GC_MALLOC(sizeof(ents)), ents, sizeof(ents)), \ - .length=sizeof(ents)/sizeof(ents[0]), \ - .stride=(void*)&ents[1] - (void*)&ents[0], \ - }, Set$info(item_info)); \ - set; }) +#define Table(key_t, val_t, key_info, value_info, fb, N, ...) \ + ({ \ + struct { \ + key_t k; \ + val_t v; \ + } ents[N] = {__VA_ARGS__}; \ + Table_t table = Table$from_entries( \ + (List_t){ \ + .data = memcpy(GC_MALLOC(sizeof(ents)), ents, sizeof(ents)), \ + .length = sizeof(ents) / sizeof(ents[0]), \ + .stride = (void *)&ents[1] - (void *)&ents[0], \ + }, \ + Table$info(key_info, value_info)); \ + table.fallback = fb; \ + table; \ + }) +#define Set(item_t, item_info, N, ...) \ + ({ \ + item_t ents[N] = {__VA_ARGS__}; \ + Table_t set = Table$from_entries( \ + (List_t){ \ + .data = memcpy(GC_MALLOC(sizeof(ents)), ents, sizeof(ents)), \ + .length = sizeof(ents) / sizeof(ents[0]), \ + .stride = (void *)&ents[1] - (void *)&ents[0], \ + }, \ + Set$info(item_info)); \ + set; \ + }) Table_t Table$from_entries(List_t entries, const TypeInfo_t *type); void *Table$get(Table_t t, const void *key, const TypeInfo_t *type); -#define Table$get_optional(table_expr, key_t, val_t, key_expr, nonnull_var, nonnull_expr, null_expr, info_expr) ({ \ - const Table_t t = table_expr; const key_t k = key_expr; \ - val_t *nonnull_var = Table$get(t, &k, info_expr); \ - nonnull_var ? nonnull_expr : null_expr; }) -#define Table$get_or_setdefault(table_expr, key_t, val_t, key_expr, default_expr, info_expr) ({ \ - Table_t *t = table_expr; const key_t k = key_expr; \ - if (t->entries.data_refcount > 0) List$compact(&t->entries, sizeof(struct {key_t k; val_t v;})); \ - val_t *v = Table$get(*t, &k, info_expr); \ - v ? v : (val_t*)Table$reserve(t, &k, (val_t[1]){default_expr}, info_expr); }) -#define Table$get_or_default(table_expr, key_t, val_t, key_expr, default_expr, info_expr) ({ \ - const Table_t t = table_expr; const key_t k = key_expr; \ - val_t *v = Table$get(t, &k, info_expr); \ - v ? *v : default_expr; }) -#define Table$has_value(table_expr, key_expr, info_expr) ({ \ - const Table_t t = table_expr; __typeof(key_expr) k = key_expr; \ - (Table$get(t, &k, info_expr) != NULL); }) +#define Table$get_optional(table_expr, key_t, val_t, key_expr, nonnull_var, nonnull_expr, null_expr, info_expr) \ + ({ \ + const Table_t t = table_expr; \ + const key_t k = key_expr; \ + val_t *nonnull_var = Table$get(t, &k, info_expr); \ + nonnull_var ? nonnull_expr : null_expr; \ + }) +#define Table$get_or_setdefault(table_expr, key_t, val_t, key_expr, default_expr, info_expr) \ + ({ \ + Table_t *t = table_expr; \ + const key_t k = key_expr; \ + if (t->entries.data_refcount > 0) \ + List$compact(&t->entries, sizeof(struct { \ + key_t k; \ + val_t v; \ + })); \ + val_t *v = Table$get(*t, &k, info_expr); \ + v ? v : (val_t *)Table$reserve(t, &k, (val_t[1]){default_expr}, info_expr); \ + }) +#define Table$get_or_default(table_expr, key_t, val_t, key_expr, default_expr, info_expr) \ + ({ \ + const Table_t t = table_expr; \ + const key_t k = key_expr; \ + val_t *v = Table$get(t, &k, info_expr); \ + v ? *v : default_expr; \ + }) +#define Table$has_value(table_expr, key_expr, info_expr) \ + ({ \ + const Table_t t = table_expr; \ + __typeof(key_expr) k = key_expr; \ + (Table$get(t, &k, info_expr) != NULL); \ + }) PUREFUNC void *Table$get_raw(Table_t t, const void *key, const TypeInfo_t *type); CONSTFUNC void *Table$entry(Table_t t, int64_t n); void *Table$reserve(Table_t *t, const void *key, const void *value, const TypeInfo_t *type); void Table$set(Table_t *t, const void *key, const void *value, const TypeInfo_t *type); -#define Table$set_value(t, key_expr, value_expr, type) ({ __typeof(key_expr) k = key_expr; __typeof(value_expr) v = value_expr; \ - Table$set(t, &k, &v, type); }) +#define Table$set_value(t, key_expr, value_expr, type) \ + ({ \ + __typeof(key_expr) k = key_expr; \ + __typeof(value_expr) v = value_expr; \ + Table$set(t, &k, &v, type); \ + }) void Table$remove(Table_t *t, const void *key, const TypeInfo_t *type); -#define Table$remove_value(t, key_expr, type) ({ __typeof(key_expr) k = key_expr; Table$remove(t, &k, type); }) +#define Table$remove_value(t, key_expr, type) \ + ({ \ + __typeof(key_expr) k = key_expr; \ + Table$remove(t, &k, type); \ + }) Table_t Table$overlap(Table_t a, Table_t b, const TypeInfo_t *type); Table_t Table$with(Table_t a, Table_t b, const TypeInfo_t *type); @@ -67,13 +102,22 @@ PUREFUNC bool Table$is_superset_of(Table_t a, Table_t b, bool strict, const Type void Table$clear(Table_t *t); Table_t Table$sorted(Table_t t, const TypeInfo_t *type); void Table$mark_copy_on_write(Table_t *t); -#define TABLE_INCREF(t) ({ LIST_INCREF((t).entries); if ((t).bucket_info) (t).bucket_info->data_refcount += ((t).bucket_info->data_refcount < TABLE_MAX_DATA_REFCOUNT); }) -#define TABLE_COPY(t) ({ TABLE_INCREF(t); t; }) +#define TABLE_INCREF(t) \ + ({ \ + LIST_INCREF((t).entries); \ + if ((t).bucket_info) \ + (t).bucket_info->data_refcount += ((t).bucket_info->data_refcount < TABLE_MAX_DATA_REFCOUNT); \ + }) +#define TABLE_COPY(t) \ + ({ \ + TABLE_INCREF(t); \ + t; \ + }) PUREFUNC int32_t Table$compare(const void *x, const void *y, const TypeInfo_t *type); PUREFUNC bool Table$equal(const void *x, const void *y, const TypeInfo_t *type); PUREFUNC uint64_t Table$hash(const void *t, const TypeInfo_t *type); Text_t Table$as_text(const void *t, bool colorize, const TypeInfo_t *type); -PUREFUNC bool Table$is_none(const void *obj, const TypeInfo_t*); +PUREFUNC bool Table$is_none(const void *obj, const TypeInfo_t *); CONSTFUNC void *Table$str_entry(Table_t t, int64_t n); PUREFUNC void *Table$str_get(Table_t t, const char *key); @@ -88,19 +132,30 @@ void Table$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_ extern const TypeInfo_t CStrToVoidStarTable; -#define Table$metamethods { \ - .as_text=Table$as_text, \ - .compare=Table$compare, \ - .equal=Table$equal, \ - .hash=Table$hash, \ - .is_none=Table$is_none, \ - .serialize=Table$serialize, \ - .deserialize=Table$deserialize, \ -} +#define Table$metamethods \ + { \ + .as_text = Table$as_text, \ + .compare = Table$compare, \ + .equal = Table$equal, \ + .hash = Table$hash, \ + .is_none = Table$is_none, \ + .serialize = Table$serialize, \ + .deserialize = Table$deserialize, \ + } -#define Table$info(key_expr, value_expr) &((TypeInfo_t){.size=sizeof(Table_t), .align=__alignof__(Table_t), \ - .tag=TableInfo, .TableInfo.key=key_expr, .TableInfo.value=value_expr, .metamethods=Table$metamethods}) -#define Set$info(item_info) &((TypeInfo_t){.size=sizeof(Table_t), .align=__alignof__(Table_t), \ - .tag=TableInfo, .TableInfo.key=item_info, .TableInfo.value=&Void$info, .metamethods=Table$metamethods}) +#define Table$info(key_expr, value_expr) \ + &((TypeInfo_t){.size = sizeof(Table_t), \ + .align = __alignof__(Table_t), \ + .tag = TableInfo, \ + .TableInfo.key = key_expr, \ + .TableInfo.value = value_expr, \ + .metamethods = Table$metamethods}) +#define Set$info(item_info) \ + &((TypeInfo_t){.size = sizeof(Table_t), \ + .align = __alignof__(Table_t), \ + .tag = TableInfo, \ + .TableInfo.key = item_info, \ + .TableInfo.value = &Void$info, \ + .metamethods = Table$metamethods}) // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1 diff --git a/src/stdlib/text.c b/src/stdlib/text.c index d9793eb8..aad3fd76 100644 --- a/src/stdlib/text.c +++ b/src/stdlib/text.c @@ -116,8 +116,8 @@ #include "text.h" // Use inline version of the siphash code for performance: -#include "siphash.h" #include "siphash-internals.h" +#include "siphash.h" typedef struct { ucs4_t main_codepoint; @@ -133,9 +133,9 @@ static synthetic_grapheme_t *synthetic_graphemes = NULL; static int32_t synthetic_grapheme_capacity = 0; static int32_t num_synthetic_graphemes = 0; -#define NUM_GRAPHEME_CODEPOINTS(id) (synthetic_graphemes[-(id)-1].utf32_cluster[0]) -#define GRAPHEME_CODEPOINTS(id) (&synthetic_graphemes[-(id)-1].utf32_cluster[1]) -#define GRAPHEME_UTF8(id) (synthetic_graphemes[-(id)-1].utf8) +#define NUM_GRAPHEME_CODEPOINTS(id) (synthetic_graphemes[-(id) - 1].utf32_cluster[0]) +#define GRAPHEME_CODEPOINTS(id) (&synthetic_graphemes[-(id) - 1].utf32_cluster[1]) +#define GRAPHEME_UTF8(id) (synthetic_graphemes[-(id) - 1].utf8) // Somewhat arbitrarily chosen, if two short literal ASCII or grapheme chunks // are concatenated below this length threshold, we just merge them into a @@ -145,16 +145,17 @@ static int32_t num_synthetic_graphemes = 0; static Text_t simple_concatenation(Text_t a, Text_t b); -public Text_t EMPTY_TEXT = { - .length=0, - .tag=TEXT_ASCII, - .ascii=0, +public +Text_t EMPTY_TEXT = { + .length = 0, + .tag = TEXT_ASCII, + .ascii = 0, }; PUREFUNC static bool graphemes_equal(const void *va, const void *vb, const TypeInfo_t *info) { (void)info; - ucs4_t *a = *(ucs4_t**)va; - ucs4_t *b = *(ucs4_t**)vb; + ucs4_t *a = *(ucs4_t **)va; + ucs4_t *b = *(ucs4_t **)vb; if (a[0] != b[0]) return false; for (int i = 0; i < (int)a[0]; i++) if (a[i] != b[i]) return false; @@ -163,37 +164,37 @@ PUREFUNC static bool graphemes_equal(const void *va, const void *vb, const TypeI PUREFUNC static uint64_t grapheme_hash(const void *g, const TypeInfo_t *info) { (void)info; - ucs4_t *cluster = *(ucs4_t**)g; - return siphash24((void*)&cluster[1], sizeof(ucs4_t[cluster[0]])); + ucs4_t *cluster = *(ucs4_t **)g; + return siphash24((void *)&cluster[1], sizeof(ucs4_t[cluster[0]])); } static const TypeInfo_t GraphemeClusterInfo = { - .size=sizeof(ucs4_t*), - .align=__alignof__(ucs4_t*), - .metamethods={ - .equal=graphemes_equal, - .hash=grapheme_hash, - }, + .size = sizeof(ucs4_t *), + .align = __alignof__(ucs4_t *), + .metamethods = + { + .equal = graphemes_equal, + .hash = grapheme_hash, + }, }; #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstack-protector" #endif -public int32_t get_synthetic_grapheme(const ucs4_t *codepoints, int64_t utf32_len) -{ - if (utf32_len == 1) - return (int32_t)*codepoints; +public +int32_t get_synthetic_grapheme(const ucs4_t *codepoints, int64_t utf32_len) { + if (utf32_len == 1) return (int32_t)*codepoints; - ucs4_t length_prefixed[1+utf32_len]; + ucs4_t length_prefixed[1 + utf32_len]; length_prefixed[0] = (ucs4_t)utf32_len; for (int i = 0; i < utf32_len; i++) - length_prefixed[i+1] = codepoints[i]; + length_prefixed[i + 1] = codepoints[i]; ucs4_t *ptr = &length_prefixed[0]; // Optimization for common case of one frequently used synthetic grapheme: static int32_t last_grapheme = 0; - if (last_grapheme != 0 && graphemes_equal(&ptr, &synthetic_graphemes[-last_grapheme-1].utf32_cluster, NULL)) + if (last_grapheme != 0 && graphemes_equal(&ptr, &synthetic_graphemes[-last_grapheme - 1].utf32_cluster, NULL)) return last_grapheme; TypeInfo_t GraphemeIDLookupTableInfo = *Table$info(&GraphemeClusterInfo, &Int32$info); @@ -209,12 +210,12 @@ public int32_t get_synthetic_grapheme(const ucs4_t *codepoints, int64_t utf32_le synthetic_graphemes = new; } - int32_t grapheme_id = -(num_synthetic_graphemes+1); + int32_t grapheme_id = -(num_synthetic_graphemes + 1); num_synthetic_graphemes += 1; // Get UTF8 representation: uint8_t u8_buf[64]; - size_t u8_len = sizeof(u8_buf)/sizeof(u8_buf[0]); + size_t u8_len = sizeof(u8_buf) / sizeof(u8_buf[0]); uint8_t *u8 = u32_to_u8(codepoints, (size_t)utf32_len, u8_buf, &u8_len); if (u8 == NULL) fail("Invalid graphemes encountered!"); @@ -223,11 +224,10 @@ public int32_t get_synthetic_grapheme(const ucs4_t *codepoints, int64_t utf32_le // area with good cache locality: static void *arena = NULL, *arena_end = NULL; // Eat up any space needed to make arena 32-bit aligned: - if ((size_t)arena % __alignof__(ucs4_t) != 0) - arena += __alignof__(ucs4_t) - ((size_t)arena % __alignof__(ucs4_t)); + if ((size_t)arena % __alignof__(ucs4_t) != 0) arena += __alignof__(ucs4_t) - ((size_t)arena % __alignof__(ucs4_t)); // If we have filled up this arena, allocate a new one: - size_t needed_memory = sizeof(ucs4_t[1+utf32_len]) + sizeof(uint8_t[u8_len + 1]); + size_t needed_memory = sizeof(ucs4_t[1 + utf32_len]) + sizeof(uint8_t[u8_len + 1]); if (arena + needed_memory > arena_end) { // Do reasonably big chunks at a time, so most synthetic codepoints are // nearby each other in memory and cache locality is good. This is a @@ -239,28 +239,27 @@ public int32_t get_synthetic_grapheme(const ucs4_t *codepoints, int64_t utf32_le // Copy length-prefixed UTF32 codepoints into the arena and store where they live: ucs4_t *codepoint_copy = arena; - memcpy(codepoint_copy, length_prefixed, sizeof(ucs4_t[1+utf32_len])); - synthetic_graphemes[-grapheme_id-1].utf32_cluster = codepoint_copy; - arena += sizeof(ucs4_t[1+utf32_len]); + memcpy(codepoint_copy, length_prefixed, sizeof(ucs4_t[1 + utf32_len])); + synthetic_graphemes[-grapheme_id - 1].utf32_cluster = codepoint_copy; + arena += sizeof(ucs4_t[1 + utf32_len]); // Copy UTF8 bytes into the arena and store where they live: uint8_t *utf8_final = arena; memcpy(utf8_final, u8, sizeof(uint8_t[u8_len])); utf8_final[u8_len] = '\0'; // Add a terminating NUL byte - synthetic_graphemes[-grapheme_id-1].utf8 = utf8_final; + synthetic_graphemes[-grapheme_id - 1].utf8 = utf8_final; arena += sizeof(uint8_t[u8_len + 1]); // Sickos at the unicode consortium decreed that you can have grapheme clusters // that begin with *prefix* modifiers, so we gotta check for that case: - synthetic_graphemes[-grapheme_id-1].main_codepoint = length_prefixed[1]; + synthetic_graphemes[-grapheme_id - 1].main_codepoint = length_prefixed[1]; for (ucs4_t i = 0; i < utf32_len; i++) { #if _LIBUNISTRING_VERSION >= 0x010200 -// libuinstring version 1.2.0 introduced uc_is_property_prepended_concatenation_mark() -// It's not critical, but it's technically more correct to have this check: - if (unlikely(uc_is_property_prepended_concatenation_mark(length_prefixed[1+i]))) - continue; + // libuinstring version 1.2.0 introduced uc_is_property_prepended_concatenation_mark() + // It's not critical, but it's technically more correct to have this check: + if (unlikely(uc_is_property_prepended_concatenation_mark(length_prefixed[1 + i]))) continue; #endif - synthetic_graphemes[-grapheme_id-1].main_codepoint = length_prefixed[1+i]; + synthetic_graphemes[-grapheme_id - 1].main_codepoint = length_prefixed[1 + i]; break; } @@ -276,8 +275,8 @@ public int32_t get_synthetic_grapheme(const ucs4_t *codepoints, int64_t utf32_le #pragma GCC diagnostic pop #endif -public int Text$print(FILE *stream, Text_t t) -{ +public +int Text$print(FILE *stream, Text_t t) { if (t.length == 0) return 0; switch (t.tag) { @@ -290,14 +289,14 @@ public int Text$print(FILE *stream, Text_t t) if (grapheme >= 0) { uint8_t buf[8]; size_t len = sizeof(buf); - uint8_t *u8 = u32_to_u8((ucs4_t*)&grapheme, 1, buf, &len); + uint8_t *u8 = u32_to_u8((ucs4_t *)&grapheme, 1, buf, &len); if (u8 == NULL) fail("Invalid grapheme encountered: ", grapheme); written += (int)fwrite(u8, sizeof(char), len, stream); if (u8 != buf) free(u8); } else { const uint8_t *u8 = GRAPHEME_UTF8(grapheme); assert(u8); - written += (int)fwrite(u8, sizeof(uint8_t), strlen((char*)u8), stream); + written += (int)fwrite(u8, sizeof(uint8_t), strlen((char *)u8), stream); } } return written; @@ -309,14 +308,14 @@ public int Text$print(FILE *stream, Text_t t) if (grapheme >= 0) { uint8_t buf[8]; size_t len = sizeof(buf); - uint8_t *u8 = u32_to_u8((ucs4_t*)&grapheme, 1, buf, &len); + uint8_t *u8 = u32_to_u8((ucs4_t *)&grapheme, 1, buf, &len); if (u8 == NULL) fail("Invalid grapheme encountered: ", grapheme); written += (int)fwrite(u8, sizeof(char), len, stream); if (u8 != buf) free(u8); } else { const uint8_t *u8 = GRAPHEME_UTF8(grapheme); assert(u8); - written += (int)fwrite(u8, sizeof(uint8_t), strlen((char*)u8), stream); + written += (int)fwrite(u8, sizeof(uint8_t), strlen((char *)u8), stream); } } return written; @@ -332,16 +331,16 @@ public int Text$print(FILE *stream, Text_t t) static const int64_t min_len_for_depth[MAX_TEXT_DEPTH] = { // Fibonacci numbers (skipping first two) - 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, - 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, - 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, - 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, + 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, + 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, + 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, + 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, + 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, }; #define IS_BALANCED_TEXT(t) ((t).length >= min_len_for_depth[(t).depth]) -static void insert_balanced_recursive(Text_t balanced_texts[MAX_TEXT_DEPTH], Text_t text) -{ +static void insert_balanced_recursive(Text_t balanced_texts[MAX_TEXT_DEPTH], Text_t text) { if (text.tag == TEXT_CONCAT && (!IS_BALANCED_TEXT(text) || text.depth >= MAX_TEXT_DEPTH)) { insert_balanced_recursive(balanced_texts, *text.left); insert_balanced_recursive(balanced_texts, *text.right); @@ -370,8 +369,7 @@ static void insert_balanced_recursive(Text_t balanced_texts[MAX_TEXT_DEPTH], Tex balanced_texts[i] = accumulator; } -static Text_t rebalanced(Text_t a, Text_t b) -{ +static Text_t rebalanced(Text_t a, Text_t b) { Text_t balanced_texts[MAX_TEXT_DEPTH]; memset(balanced_texts, 0, sizeof(balanced_texts)); insert_balanced_recursive(balanced_texts, a); @@ -379,14 +377,12 @@ static Text_t rebalanced(Text_t a, Text_t b) Text_t ret = EMPTY_TEXT; for (int i = 0; ret.length < a.length + b.length; i++) { - if (balanced_texts[i].length) - ret = simple_concatenation(balanced_texts[i], ret); + if (balanced_texts[i].length) ret = simple_concatenation(balanced_texts[i], ret); } return ret; } -Text_t simple_concatenation(Text_t a, Text_t b) -{ +Text_t simple_concatenation(Text_t a, Text_t b) { if (a.length == 0) return b; if (b.length == 0) return a; @@ -395,53 +391,53 @@ Text_t simple_concatenation(Text_t a, Text_t b) // every concatenation to yield a balanced text, since many concatenations // are ephemeral (e.g. doing a loop repeatedly concatenating without using // the intermediary values). - if (new_depth >= MAX_TEXT_DEPTH) - return rebalanced(a, b); + if (new_depth >= MAX_TEXT_DEPTH) return rebalanced(a, b); Text_t *children = GC_MALLOC(sizeof(Text_t[2])); children[0] = a; children[1] = b; return (Text_t){ - .tag=TEXT_CONCAT, - .length=a.length + b.length, - .depth=new_depth, - .left=&children[0], - .right=&children[1], + .tag = TEXT_CONCAT, + .length = a.length + b.length, + .depth = new_depth, + .left = &children[0], + .right = &children[1], }; } -static Text_t concat2_assuming_safe(Text_t a, Text_t b) -{ +static Text_t concat2_assuming_safe(Text_t a, Text_t b) { if (a.length == 0) return b; if (b.length == 0) return a; if (a.tag == TEXT_ASCII && b.tag == TEXT_ASCII && (size_t)(a.length + b.length) <= SHORT_ASCII_LENGTH) { struct Text_s ret = { - .tag=TEXT_ASCII, - .length=a.length + b.length, + .tag = TEXT_ASCII, + .length = a.length + b.length, }; ret.ascii = GC_MALLOC_ATOMIC(sizeof(char[ret.length])); - memcpy((char*)ret.ascii, a.ascii, sizeof(char[a.length])); - memcpy((char*)&ret.ascii[a.length], b.ascii, sizeof(char[b.length])); + memcpy((char *)ret.ascii, a.ascii, sizeof(char[a.length])); + memcpy((char *)&ret.ascii[a.length], b.ascii, sizeof(char[b.length])); return ret; - } else if (a.tag == TEXT_GRAPHEMES && b.tag == TEXT_GRAPHEMES && (size_t)(a.length + b.length) <= SHORT_GRAPHEMES_LENGTH) { + } else if (a.tag == TEXT_GRAPHEMES && b.tag == TEXT_GRAPHEMES + && (size_t)(a.length + b.length) <= SHORT_GRAPHEMES_LENGTH) { struct Text_s ret = { - .tag=TEXT_GRAPHEMES, - .length=a.length + b.length, + .tag = TEXT_GRAPHEMES, + .length = a.length + b.length, }; ret.graphemes = GC_MALLOC_ATOMIC(sizeof(int32_t[ret.length])); - memcpy((int32_t*)ret.graphemes, a.graphemes, sizeof(int32_t[a.length])); - memcpy((int32_t*)&ret.graphemes[a.length], b.graphemes, sizeof(int32_t[b.length])); + memcpy((int32_t *)ret.graphemes, a.graphemes, sizeof(int32_t[a.length])); + memcpy((int32_t *)&ret.graphemes[a.length], b.graphemes, sizeof(int32_t[b.length])); return ret; - } else if (a.tag != TEXT_CONCAT && b.tag != TEXT_CONCAT && (size_t)(a.length + b.length) <= SHORT_GRAPHEMES_LENGTH) { + } else if (a.tag != TEXT_CONCAT && b.tag != TEXT_CONCAT + && (size_t)(a.length + b.length) <= SHORT_GRAPHEMES_LENGTH) { // Turn a small bit of ASCII into graphemes if it helps make things smaller // Text structs come with an extra 8 bytes, so allocate enough to hold the text struct Text_s ret = { - .tag=TEXT_GRAPHEMES, - .length=a.length + b.length, + .tag = TEXT_GRAPHEMES, + .length = a.length + b.length, }; ret.graphemes = GC_MALLOC_ATOMIC(sizeof(int32_t[ret.length])); - int32_t *dest = (int32_t*)ret.graphemes; + int32_t *dest = (int32_t *)ret.graphemes; if (a.tag == TEXT_GRAPHEMES) { memcpy(dest, a.graphemes, sizeof(int32_t[a.length])); dest += a.length; @@ -474,12 +470,11 @@ static Text_t concat2_assuming_safe(Text_t a, Text_t b) return simple_concatenation(a, b); } -static Text_t concat2(Text_t a, Text_t b) -{ +static Text_t concat2(Text_t a, Text_t b) { if (a.length == 0) return b; if (b.length == 0) return a; - int32_t last_a = Text$get_grapheme(a, a.length-1); + int32_t last_a = Text$get_grapheme(a, a.length - 1); int32_t first_b = Text$get_grapheme(b, 0); // Magic number, we know that no codepoints below here trigger instability: @@ -509,60 +504,51 @@ static Text_t concat2(Text_t a, Text_t b) // Do a normalization run for these two codepoints and see if it looks different. // Normalization should not exceed 3x in the input length (but if it does, it will be // handled gracefully) - ucs4_t norm_buf[3*len]; - size_t norm_length = sizeof(norm_buf)/sizeof(norm_buf[0]); + ucs4_t norm_buf[3 * len]; + size_t norm_length = sizeof(norm_buf) / sizeof(norm_buf[0]); ucs4_t *normalized = u32_normalize(UNINORM_NFC, codepoints, len, norm_buf, &norm_length); bool stable = (norm_length == len && memcmp(codepoints, normalized, sizeof(codepoints)) == 0); if (stable) { const void *second_grapheme = u32_grapheme_next(normalized, &normalized[norm_length]); - if (second_grapheme == &normalized[norm_length]) - stable = false; + if (second_grapheme == &normalized[norm_length]) stable = false; } if likely (stable) { - if (normalized != norm_buf) - free(normalized); + if (normalized != norm_buf) free(normalized); return concat2_assuming_safe(a, b); } - Text_t glue = Text$from_codepoints((List_t){.data=norm_buf, .length=(int64_t)norm_length, .stride=sizeof(int32_t)}); + Text_t glue = + Text$from_codepoints((List_t){.data = norm_buf, .length = (int64_t)norm_length, .stride = sizeof(int32_t)}); - if (normalized != norm_buf) - free(normalized); + if (normalized != norm_buf) free(normalized); - if (a.length == 1 && b.length == 1) - return glue; - else if (a.length == 1) - return concat2_assuming_safe(glue, Text$slice(b, I(2), I(b.length))); - else if (b.length == 1) - return concat2_assuming_safe(Text$slice(a, I(1), I(a.length-1)), glue); + if (a.length == 1 && b.length == 1) return glue; + else if (a.length == 1) return concat2_assuming_safe(glue, Text$slice(b, I(2), I(b.length))); + else if (b.length == 1) return concat2_assuming_safe(Text$slice(a, I(1), I(a.length - 1)), glue); else - return concat2_assuming_safe( - concat2_assuming_safe(Text$slice(a, I(1), I(a.length-1)), glue), - Text$slice(b, I(2), I(b.length))); + return concat2_assuming_safe(concat2_assuming_safe(Text$slice(a, I(1), I(a.length - 1)), glue), + Text$slice(b, I(2), I(b.length))); } -public Text_t Text$_concat(int n, Text_t items[n]) -{ +public +Text_t Text$_concat(int n, Text_t items[n]) { if (n == 0) return EMPTY_TEXT; Text_t ret = items[0]; for (int i = 1; i < n; i++) { - if (items[i].length > 0) - ret = concat2(ret, items[i]); + if (items[i].length > 0) ret = concat2(ret, items[i]); } return ret; } -public Text_t Text$repeat(Text_t text, Int_t count) -{ - if (text.length == 0 || Int$is_negative(count)) - return EMPTY_TEXT; +public +Text_t Text$repeat(Text_t text, Int_t count) { + if (text.length == 0 || Int$is_negative(count)) return EMPTY_TEXT; Int_t result_len = Int$times(count, I(text.length)); - if (Int$compare_value(result_len, I(1l<<40)) > 0) - fail("Text repeating would produce too big of an result!"); + if (Int$compare_value(result_len, I(1l << 40)) > 0) fail("Text repeating would produce too big of an result!"); int64_t count64 = Int64$from_int(count, false); Text_t ret = text; @@ -571,19 +557,17 @@ public Text_t Text$repeat(Text_t text, Int_t count) return ret; } -public Int_t Text$width(Text_t text, Text_t language) -{ - int width = u8_strwidth((const uint8_t*)Text$as_c_string(text), Text$as_c_string(language)); +public +Int_t Text$width(Text_t text, Text_t language) { + int width = u8_strwidth((const uint8_t *)Text$as_c_string(text), Text$as_c_string(language)); return Int$from_int32(width); } -static Text_t Text$repeat_to_width(Text_t to_repeat, int64_t target_width, Text_t language) -{ - if (target_width <= 0) - return EMPTY_TEXT; +static Text_t Text$repeat_to_width(Text_t to_repeat, int64_t target_width, Text_t language) { + if (target_width <= 0) return EMPTY_TEXT; const char *lang_str = Text$as_c_string(language); - int64_t width = (int64_t)u8_strwidth((const uint8_t*)Text$as_c_string(to_repeat), lang_str); + int64_t width = (int64_t)u8_strwidth((const uint8_t *)Text$as_c_string(to_repeat), lang_str); Text_t repeated = EMPTY_TEXT; int64_t repeated_width = 0; while (repeated_width + width <= target_width) { @@ -593,8 +577,8 @@ static Text_t Text$repeat_to_width(Text_t to_repeat, int64_t target_width, Text_ if (repeated_width < target_width) { for (int64_t i = 0; repeated_width < target_width && i < to_repeat.length; i++) { - Text_t c = Text$slice(to_repeat, I_small(i+1), I_small(i+1)); - int64_t w = (int64_t)u8_strwidth((const uint8_t*)Text$as_c_string(c), lang_str); + Text_t c = Text$slice(to_repeat, I_small(i + 1), I_small(i + 1)); + int64_t w = (int64_t)u8_strwidth((const uint8_t *)Text$as_c_string(c), lang_str); if (repeated_width + w > target_width) { repeated = concat2(repeated, Text$repeat(Text(" "), I(target_width - repeated_width))); repeated_width = target_width; @@ -608,35 +592,33 @@ static Text_t Text$repeat_to_width(Text_t to_repeat, int64_t target_width, Text_ return repeated; } -public Text_t Text$left_pad(Text_t text, Int_t width, Text_t padding, Text_t language) -{ - if (padding.length == 0) - fail("Cannot pad with an empty text!"); +public +Text_t Text$left_pad(Text_t text, Int_t width, Text_t padding, Text_t language) { + if (padding.length == 0) fail("Cannot pad with an empty text!"); int64_t needed = Int64$from_int(width, false) - Int64$from_int(Text$width(text, language), false); return concat2(Text$repeat_to_width(padding, needed, language), text); } -public Text_t Text$right_pad(Text_t text, Int_t width, Text_t padding, Text_t language) -{ - if (padding.length == 0) - fail("Cannot pad with an empty text!"); +public +Text_t Text$right_pad(Text_t text, Int_t width, Text_t padding, Text_t language) { + if (padding.length == 0) fail("Cannot pad with an empty text!"); int64_t needed = Int64$from_int(width, false) - Int64$from_int(Text$width(text, language), false); return concat2(text, Text$repeat_to_width(padding, needed, language)); } -public Text_t Text$middle_pad(Text_t text, Int_t width, Text_t padding, Text_t language) -{ - if (padding.length == 0) - fail("Cannot pad with an empty text!"); +public +Text_t Text$middle_pad(Text_t text, Int_t width, Text_t padding, Text_t language) { + if (padding.length == 0) fail("Cannot pad with an empty text!"); int64_t needed = Int64$from_int(width, false) - Int64$from_int(Text$width(text, language), false); - return Texts(Text$repeat_to_width(padding, needed/2, language), text, Text$repeat_to_width(padding, (needed+1)/2, language)); + return Texts(Text$repeat_to_width(padding, needed / 2, language), text, + Text$repeat_to_width(padding, (needed + 1) / 2, language)); } -public Text_t Text$slice(Text_t text, Int_t first_int, Int_t last_int) -{ +public +Text_t Text$slice(Text_t text, Int_t first_int, Int_t last_int) { int64_t first = Int64$from_int(first_int, false); int64_t last = Int64$from_int(last_int, false); if (first == 0) fail("Invalid index: 0"); @@ -647,11 +629,9 @@ public Text_t Text$slice(Text_t text, Int_t first_int, Int_t last_int) if (last > text.length) last = text.length; - if (first > text.length || last < first) - return EMPTY_TEXT; + if (first > text.length || last < first) return EMPTY_TEXT; - if (first == 1 && last == text.length) - return text; + if (first == 1 && last == text.length) return text; while (text.tag == TEXT_CONCAT) { if (last < text.left->length) { @@ -662,31 +642,31 @@ public Text_t Text$slice(Text_t text, Int_t first_int, Int_t last_int) text = *text.right; } else { return concat2_assuming_safe(Text$slice(*text.left, I(first), I(text.length)), - Text$slice(*text.right, I(1), I(last-text.left->length))); + Text$slice(*text.right, I(1), I(last - text.left->length))); } } switch (text.tag) { case TEXT_ASCII: { return (Text_t){ - .tag=TEXT_ASCII, - .length=last - first + 1, - .ascii=text.ascii + (first-1), + .tag = TEXT_ASCII, + .length = last - first + 1, + .ascii = text.ascii + (first - 1), }; } case TEXT_GRAPHEMES: { return (Text_t){ - .tag=TEXT_GRAPHEMES, - .length=last - first + 1, - .graphemes=text.graphemes + (first-1), + .tag = TEXT_GRAPHEMES, + .length = last - first + 1, + .graphemes = text.graphemes + (first - 1), }; } case TEXT_BLOB: { Text_t ret = (Text_t){ - .tag=TEXT_BLOB, - .length=last - first + 1, - .blob.map=text.blob.map, - .blob.bytes=text.blob.bytes + (first-1), + .tag = TEXT_BLOB, + .length = last - first + 1, + .blob.map = text.blob.map, + .blob.bytes = text.blob.bytes + (first - 1), }; return ret; } @@ -695,48 +675,44 @@ public Text_t Text$slice(Text_t text, Int_t first_int, Int_t last_int) return EMPTY_TEXT; } -public Text_t Text$from(Text_t text, Int_t first) -{ - return Text$slice(text, first, I_small(-1)); -} +public +Text_t Text$from(Text_t text, Int_t first) { return Text$slice(text, first, I_small(-1)); } -public Text_t Text$to(Text_t text, Int_t last) -{ - return Text$slice(text, I_small(1), last); -} +public +Text_t Text$to(Text_t text, Int_t last) { return Text$slice(text, I_small(1), last); } -public Text_t Text$reversed(Text_t text) -{ +public +Text_t Text$reversed(Text_t text) { switch (text.tag) { case TEXT_ASCII: { struct Text_s ret = { - .tag=TEXT_ASCII, - .length=text.length, + .tag = TEXT_ASCII, + .length = text.length, }; ret.ascii = GC_MALLOC_ATOMIC(sizeof(char[ret.length])); for (int64_t i = 0; i < text.length; i++) - ((char*)ret.ascii)[text.length-1-i] = text.ascii[i]; + ((char *)ret.ascii)[text.length - 1 - i] = text.ascii[i]; return ret; } case TEXT_GRAPHEMES: { struct Text_s ret = { - .tag=TEXT_GRAPHEMES, - .length=text.length, + .tag = TEXT_GRAPHEMES, + .length = text.length, }; ret.graphemes = GC_MALLOC_ATOMIC(sizeof(int32_t[ret.length])); for (int64_t i = 0; i < text.length; i++) - ((int32_t*)ret.graphemes)[text.length-1-i] = text.graphemes[i]; + ((int32_t *)ret.graphemes)[text.length - 1 - i] = text.graphemes[i]; return ret; } case TEXT_BLOB: { struct Text_s ret = { - .tag=TEXT_BLOB, - .length=text.length, - .blob.map=text.blob.map, + .tag = TEXT_BLOB, + .length = text.length, + .blob.map = text.blob.map, }; ret.blob.bytes = GC_MALLOC_ATOMIC(sizeof(uint8_t[ret.length])); for (int64_t i = 0; i < text.length; i++) - ((uint8_t*)ret.blob.bytes)[text.length-1-i] = text.graphemes[i]; + ((uint8_t *)ret.blob.bytes)[text.length - 1 - i] = text.graphemes[i]; return ret; } case TEXT_CONCAT: { @@ -747,32 +723,30 @@ public Text_t Text$reversed(Text_t text) return EMPTY_TEXT; } -public PUREFUNC Text_t Text$cluster(Text_t text, Int_t index) -{ - return Text$slice(text, index, index); -} +public +PUREFUNC Text_t Text$cluster(Text_t text, Int_t index) { return Text$slice(text, index, index); } -static Text_t Text$from_components(List_t graphemes, Table_t unique_clusters) -{ - size_t blob_size = ( - sizeof(int32_t[unique_clusters.entries.length]) - + sizeof(uint8_t[graphemes.length])); +static Text_t Text$from_components(List_t graphemes, Table_t unique_clusters) { + size_t blob_size = (sizeof(int32_t[unique_clusters.entries.length]) + sizeof(uint8_t[graphemes.length])); // If blob optimization will save at least 200 bytes: if (unique_clusters.entries.length <= 256 && blob_size + 200 < sizeof(int32_t[graphemes.length])) { Text_t ret = { - .tag=TEXT_BLOB, - .length=graphemes.length, - .depth=0, + .tag = TEXT_BLOB, + .length = graphemes.length, + .depth = 0, }; void *blob = GC_MALLOC_ATOMIC(blob_size); int32_t *map = blob; uint8_t *bytes = blob + sizeof(int32_t[unique_clusters.entries.length]); for (int64_t i = 0; i < unique_clusters.entries.length; i++) { - struct { int32_t g; uint8_t b; } *entry = unique_clusters.entries.data + i*unique_clusters.entries.stride; + struct { + int32_t g; + uint8_t b; + } *entry = unique_clusters.entries.data + i * unique_clusters.entries.stride; map[entry->b] = entry->g; } for (int64_t i = 0; i < graphemes.length; i++) { - int32_t g = *(int32_t*)(graphemes.data + i*graphemes.stride); + int32_t g = *(int32_t *)(graphemes.data + i * graphemes.stride); uint8_t *byte = Table$get(unique_clusters, &g, Table$info(&Int32$info, &Byte$info)); assert(byte); bytes[i] = *byte; @@ -782,15 +756,15 @@ static Text_t Text$from_components(List_t graphemes, Table_t unique_clusters) return ret; } else { return (Text_t){ - .tag=TEXT_GRAPHEMES, - .length=graphemes.length, - .graphemes=graphemes.data, + .tag = TEXT_GRAPHEMES, + .length = graphemes.length, + .graphemes = graphemes.data, }; } } -public OptionalText_t Text$from_strn(const char *str, size_t len) -{ +public +OptionalText_t Text$from_strn(const char *str, size_t len) { int64_t ascii_span = 0; for (size_t i = 0; i < len && isascii(str[i]); i++) ascii_span++; @@ -799,52 +773,48 @@ public OptionalText_t Text$from_strn(const char *str, size_t len) char *copy = GC_MALLOC_ATOMIC(len); memcpy(copy, str, len); return (Text_t){ - .tag=TEXT_ASCII, - .length=ascii_span, - .ascii=copy, + .tag = TEXT_ASCII, + .length = ascii_span, + .ascii = copy, }; } - if (u8_check((uint8_t*)str, len) != NULL) - return NONE_TEXT; + if (u8_check((uint8_t *)str, len) != NULL) return NONE_TEXT; List_t graphemes = {}; Table_t unique_clusters = {}; - const uint8_t *pos = (const uint8_t*)str; - const uint8_t *end = (const uint8_t*)&str[len]; + const uint8_t *pos = (const uint8_t *)str; + const uint8_t *end = (const uint8_t *)&str[len]; // Iterate over grapheme clusters - for (const uint8_t *next; (next=u8_grapheme_next(pos, end)); pos = next) { + for (const uint8_t *next; (next = u8_grapheme_next(pos, end)); pos = next) { uint32_t buf[256]; - size_t u32_len = sizeof(buf)/sizeof(buf[0]); - uint32_t *u32s = u8_to_u32(pos, (size_t)(next-pos), buf, &u32_len); + size_t u32_len = sizeof(buf) / sizeof(buf[0]); + uint32_t *u32s = u8_to_u32(pos, (size_t)(next - pos), buf, &u32_len); uint32_t buf2[256]; - size_t u32_normlen = sizeof(buf2)/sizeof(buf2[0]); + size_t u32_normlen = sizeof(buf2) / sizeof(buf2[0]); uint32_t *u32s_normalized = u32_normalize(UNINORM_NFC, u32s, u32_len, buf2, &u32_normlen); int32_t g = get_synthetic_grapheme(u32s_normalized, (int64_t)u32_normlen); List$insert(&graphemes, &g, I(0), sizeof(int32_t)); - Table$get_or_setdefault(&unique_clusters, int32_t, uint8_t, g, (uint8_t)unique_clusters.entries.length, Table$info(&Int32$info, &Byte$info)); + Table$get_or_setdefault(&unique_clusters, int32_t, uint8_t, g, (uint8_t)unique_clusters.entries.length, + Table$info(&Int32$info, &Byte$info)); if (u32s != buf) free(u32s); if (u32s_normalized != buf2) free(u32s_normalized); if (unique_clusters.entries.length >= 256) { - return concat2_assuming_safe( - Text$from_components(graphemes, unique_clusters), - Text$from_strn((const char*)next, (size_t)(end-next))); + return concat2_assuming_safe(Text$from_components(graphemes, unique_clusters), + Text$from_strn((const char *)next, (size_t)(end - next))); } } return Text$from_components(graphemes, unique_clusters); } -public OptionalText_t Text$from_str(const char *str) -{ - return str ? Text$from_strn(str, strlen(str)) : Text(""); -} +public +OptionalText_t Text$from_str(const char *str) { return str ? Text$from_strn(str, strlen(str)) : Text(""); } -static void u8_buf_append(Text_t text, char **buf, int64_t *capacity, int64_t *i) -{ +static void u8_buf_append(Text_t text, char **buf, int64_t *capacity, int64_t *i) { switch (text.tag) { case TEXT_ASCII: { if (*i + text.length > (int64_t)*capacity) { @@ -863,7 +833,7 @@ static void u8_buf_append(Text_t text, char **buf, int64_t *capacity, int64_t *i if (graphemes[g] >= 0) { uint8_t u8_buf[64]; size_t u8_len = sizeof(u8_buf); - uint8_t *u8 = u32_to_u8((ucs4_t*)&graphemes[g], 1, u8_buf, &u8_len); + uint8_t *u8 = u32_to_u8((ucs4_t *)&graphemes[g], 1, u8_buf, &u8_len); if (u8 == NULL) fail("Invalid grapheme encountered: ", graphemes[g]); if (*i + (int64_t)u8_len > (int64_t)*capacity) { @@ -894,7 +864,7 @@ static void u8_buf_append(Text_t text, char **buf, int64_t *capacity, int64_t *i if (grapheme >= 0) { uint8_t u8_buf[64]; size_t u8_len = sizeof(u8_buf); - uint8_t *u8 = u32_to_u8((ucs4_t*)&grapheme, 1, u8_buf, &u8_len); + uint8_t *u8 = u32_to_u8((ucs4_t *)&grapheme, 1, u8_buf, &u8_len); if (u8 == NULL) fail("Invalid grapheme encountered: ", grapheme); if (*i + (int64_t)u8_len > (int64_t)*capacity) { @@ -928,8 +898,8 @@ static void u8_buf_append(Text_t text, char **buf, int64_t *capacity, int64_t *i } } -public char *Text$as_c_string(Text_t text) -{ +public +char *Text$as_c_string(Text_t text) { int64_t capacity = text.length + 1; char *buf = GC_MALLOC_ATOMIC((size_t)capacity); int64_t i = 0; @@ -943,10 +913,9 @@ public char *Text$as_c_string(Text_t text) return buf; } -PUREFUNC public uint64_t Text$hash(const void *obj, const TypeInfo_t *info) -{ +PUREFUNC public uint64_t Text$hash(const void *obj, const TypeInfo_t *info) { (void)info; - Text_t text = *(Text_t*)obj; + Text_t text = *(Text_t *)obj; siphash sh; siphashinit(&sh, sizeof(int32_t[text.length])); @@ -959,40 +928,41 @@ PUREFUNC public uint64_t Text$hash(const void *obj, const TypeInfo_t *info) const char *bytes = text.ascii; for (int64_t i = 0; i + 1 < text.length; i += 2) { tmp.chunks[0] = (int32_t)bytes[i]; - tmp.chunks[1] = (int32_t)bytes[i+1]; + tmp.chunks[1] = (int32_t)bytes[i + 1]; siphashadd64bits(&sh, tmp.whole); } - int32_t last = text.length & 0x1 ? (int32_t)bytes[text.length-1] : 0; // Odd number of graphemes + int32_t last = text.length & 0x1 ? (int32_t)bytes[text.length - 1] : 0; // Odd number of graphemes return siphashfinish_last_part(&sh, (uint64_t)last); } case TEXT_GRAPHEMES: { const int32_t *graphemes = text.graphemes; for (int64_t i = 0; i + 1 < text.length; i += 2) { tmp.chunks[0] = graphemes[i]; - tmp.chunks[1] = graphemes[i+1]; + tmp.chunks[1] = graphemes[i + 1]; siphashadd64bits(&sh, tmp.whole); } - int32_t last = text.length & 0x1 ? graphemes[text.length-1] : 0; // Odd number of graphemes + int32_t last = text.length & 0x1 ? graphemes[text.length - 1] : 0; // Odd number of graphemes return siphashfinish_last_part(&sh, (uint64_t)last); } case TEXT_BLOB: { for (int64_t i = 0; i + 1 < text.length; i += 2) { tmp.chunks[0] = text.blob.map[text.blob.bytes[i]]; - tmp.chunks[1] = text.blob.map[text.blob.bytes[i+1]]; + tmp.chunks[1] = text.blob.map[text.blob.bytes[i + 1]]; siphashadd64bits(&sh, tmp.whole); } - int32_t last = text.length & 0x1 ? text.blob.map[text.blob.bytes[text.length-1]] : 0; // Odd number of graphemes + int32_t last = + text.length & 0x1 ? text.blob.map[text.blob.bytes[text.length - 1]] : 0; // Odd number of graphemes return siphashfinish_last_part(&sh, (uint64_t)last); } case TEXT_CONCAT: { TextIter_t state = NEW_TEXT_ITER_STATE(text); for (int64_t i = 0; i + 1 < text.length; i += 2) { tmp.chunks[0] = Text$get_grapheme_fast(&state, i); - tmp.chunks[1] = Text$get_grapheme_fast(&state, i+1); + tmp.chunks[1] = Text$get_grapheme_fast(&state, i + 1); siphashadd64bits(&sh, tmp.whole); } - int32_t last = (text.length & 0x1) ? Text$get_grapheme_fast(&state, text.length-1) : 0; + int32_t last = (text.length & 0x1) ? Text$get_grapheme_fast(&state, text.length - 1) : 0; return siphashfinish_last_part(&sh, (uint64_t)last); } default: errx(1, "Invalid text"); @@ -1000,8 +970,8 @@ PUREFUNC public uint64_t Text$hash(const void *obj, const TypeInfo_t *info) return 0; } -public int32_t Text$get_grapheme_fast(TextIter_t *state, int64_t index) -{ +public +int32_t Text$get_grapheme_fast(TextIter_t *state, int64_t index) { if (index < 0) return 0; if (index >= state->stack[0].text.length) return 0; @@ -1051,18 +1021,17 @@ public int32_t Text$get_grapheme_fast(TextIter_t *state, int64_t index) return 0; } -public uint32_t Text$get_main_grapheme_fast(TextIter_t *state, int64_t index) -{ +public +uint32_t Text$get_main_grapheme_fast(TextIter_t *state, int64_t index) { int32_t g = Text$get_grapheme_fast(state, index); return (g) >= 0 ? (ucs4_t)(g) : synthetic_graphemes[-(g)-1].main_codepoint; } -PUREFUNC public int32_t Text$compare(const void *va, const void *vb, const TypeInfo_t *info) -{ +PUREFUNC public int32_t Text$compare(const void *va, const void *vb, const TypeInfo_t *info) { (void)info; if (va == vb) return 0; - const Text_t a = *(const Text_t*)va; - const Text_t b = *(const Text_t*)vb; + const Text_t a = *(const Text_t *)va; + const Text_t b = *(const Text_t *)vb; // TODO: make this smarter and more efficient int64_t len = MAX(a.length, b.length); @@ -1073,31 +1042,21 @@ PUREFUNC public int32_t Text$compare(const void *va, const void *vb, const TypeI if (ai == bi) continue; int32_t cmp; if (ai > 0 && bi > 0) { - cmp = u32_cmp((ucs4_t*)&ai, (ucs4_t*)&bi, 1); + cmp = u32_cmp((ucs4_t *)&ai, (ucs4_t *)&bi, 1); } else if (ai > 0) { - cmp = u32_cmp2( - (ucs4_t*)&ai, 1, - GRAPHEME_CODEPOINTS(bi), - NUM_GRAPHEME_CODEPOINTS(bi)); + cmp = u32_cmp2((ucs4_t *)&ai, 1, GRAPHEME_CODEPOINTS(bi), NUM_GRAPHEME_CODEPOINTS(bi)); } else if (bi > 0) { - cmp = u32_cmp2( - GRAPHEME_CODEPOINTS(ai), - NUM_GRAPHEME_CODEPOINTS(ai), - (ucs4_t*)&bi, 1); + cmp = u32_cmp2(GRAPHEME_CODEPOINTS(ai), NUM_GRAPHEME_CODEPOINTS(ai), (ucs4_t *)&bi, 1); } else { - cmp = u32_cmp2( - GRAPHEME_CODEPOINTS(ai), - NUM_GRAPHEME_CODEPOINTS(ai), - GRAPHEME_CODEPOINTS(bi), - NUM_GRAPHEME_CODEPOINTS(bi)); + cmp = u32_cmp2(GRAPHEME_CODEPOINTS(ai), NUM_GRAPHEME_CODEPOINTS(ai), GRAPHEME_CODEPOINTS(bi), + NUM_GRAPHEME_CODEPOINTS(bi)); } if (cmp != 0) return cmp; } return 0; } -bool _matches(TextIter_t *text_state, TextIter_t *target_state, int64_t pos) -{ +bool _matches(TextIter_t *text_state, TextIter_t *target_state, int64_t pos) { for (int64_t i = 0; i < target_state->stack[0].text.length; i++) { int32_t text_i = Text$get_grapheme_fast(text_state, pos + i); int32_t prefix_i = Text$get_grapheme_fast(target_state, i); @@ -1106,10 +1065,8 @@ bool _matches(TextIter_t *text_state, TextIter_t *target_state, int64_t pos) return true; } -PUREFUNC public bool Text$starts_with(Text_t text, Text_t prefix, Text_t *remainder) -{ - if (text.length < prefix.length) - return false; +PUREFUNC public bool Text$starts_with(Text_t text, Text_t prefix, Text_t *remainder) { + if (text.length < prefix.length) return false; TextIter_t text_state = NEW_TEXT_ITER_STATE(text), prefix_state = NEW_TEXT_ITER_STATE(prefix); if (_matches(&text_state, &prefix_state, 0)) { if (remainder) *remainder = Text$from(text, Int$from_int64(prefix.length + 1)); @@ -1120,10 +1077,8 @@ PUREFUNC public bool Text$starts_with(Text_t text, Text_t prefix, Text_t *remain } } -PUREFUNC public bool Text$ends_with(Text_t text, Text_t suffix, Text_t *remainder) -{ - if (text.length < suffix.length) - return false; +PUREFUNC public bool Text$ends_with(Text_t text, Text_t suffix, Text_t *remainder) { + if (text.length < suffix.length) return false; TextIter_t text_state = NEW_TEXT_ITER_STATE(text), suffix_state = NEW_TEXT_ITER_STATE(suffix); if (_matches(&text_state, &suffix_state, text.length - suffix.length)) { if (remainder) *remainder = Text$to(text, Int$from_int64(text.length - suffix.length)); @@ -1134,18 +1089,17 @@ PUREFUNC public bool Text$ends_with(Text_t text, Text_t suffix, Text_t *remainde } } -public Text_t Text$without_prefix(Text_t text, Text_t prefix) -{ +public +Text_t Text$without_prefix(Text_t text, Text_t prefix) { return Text$starts_with(text, prefix, NULL) ? Text$slice(text, I(prefix.length + 1), I(text.length)) : text; } -public Text_t Text$without_suffix(Text_t text, Text_t suffix) -{ +public +Text_t Text$without_suffix(Text_t text, Text_t suffix) { return Text$ends_with(text, suffix, NULL) ? Text$slice(text, I(1), I(text.length - suffix.length)) : text; } -static bool _has_grapheme(TextIter_t *text, int32_t g) -{ +static bool _has_grapheme(TextIter_t *text, int32_t g) { for (int64_t t = 0; t < text->stack[0].text.length; t++) { if (g == Text$get_grapheme_fast(text, t)) { return true; @@ -1154,8 +1108,8 @@ static bool _has_grapheme(TextIter_t *text, int32_t g) return false; } -public Text_t Text$trim(Text_t text, Text_t to_trim, bool left, bool right) -{ +public +Text_t Text$trim(Text_t text, Text_t to_trim, bool left, bool right) { int64_t first = 0; TextIter_t text_state = NEW_TEXT_ITER_STATE(text), trim_state = NEW_TEXT_ITER_STATE(to_trim); if (left) { @@ -1163,28 +1117,29 @@ public Text_t Text$trim(Text_t text, Text_t to_trim, bool left, bool right) first += 1; } } - int64_t last = text.length-1; + int64_t last = text.length - 1; if (right) { while (last >= first && _has_grapheme(&trim_state, Text$get_grapheme_fast(&text_state, last))) { last -= 1; } } - return (first != 0 || last != text.length-1) ? Text$slice(text, I(first+1), I(last+1)) : text; + return (first != 0 || last != text.length - 1) ? Text$slice(text, I(first + 1), I(last + 1)) : text; } -public Text_t Text$translate(Text_t text, Table_t translations) -{ +public +Text_t Text$translate(Text_t text, Table_t translations) { TextIter_t text_state = NEW_TEXT_ITER_STATE(text); Text_t result = EMPTY_TEXT; int64_t span_start = 0; List_t replacement_list = translations.entries; - for (int64_t i = 0; i < text.length; ) { + for (int64_t i = 0; i < text.length;) { for (int64_t r = 0; r < replacement_list.length; r++) { - struct { Text_t target, replacement; } *entry = replacement_list.data + r*replacement_list.stride; + struct { + Text_t target, replacement; + } *entry = replacement_list.data + r * replacement_list.stride; TextIter_t target_state = NEW_TEXT_ITER_STATE(entry->target); if (_matches(&text_state, &target_state, i)) { - if (i > span_start) - result = concat2(result, Text$slice(text, I(span_start+1), I(i))); + if (i > span_start) result = concat2(result, Text$slice(text, I(span_start + 1), I(i))); result = concat2(result, entry->replacement); i += entry->target.length; @@ -1193,22 +1148,21 @@ public Text_t Text$translate(Text_t text, Table_t translations) } } i += 1; - found_match: continue; + found_match: + continue; } - if (span_start < text.length) - result = concat2(result, Text$slice(text, I(span_start+1), I(text.length))); + if (span_start < text.length) result = concat2(result, Text$slice(text, I(span_start + 1), I(text.length))); return result; } -public Text_t Text$replace(Text_t text, Text_t target, Text_t replacement) -{ +public +Text_t Text$replace(Text_t text, Text_t target, Text_t replacement) { TextIter_t text_state = NEW_TEXT_ITER_STATE(text), target_state = NEW_TEXT_ITER_STATE(target); Text_t result = EMPTY_TEXT; int64_t span_start = 0; - for (int64_t i = 0; i < text.length; ) { + for (int64_t i = 0; i < text.length;) { if (_matches(&text_state, &target_state, i)) { - if (i > span_start) - result = concat2(result, Text$slice(text, I(span_start+1), I(i))); + if (i > span_start) result = concat2(result, Text$slice(text, I(span_start + 1), I(i))); result = concat2(result, replacement); i += target.length; @@ -1217,34 +1171,31 @@ public Text_t Text$replace(Text_t text, Text_t target, Text_t replacement) i += 1; } } - if (span_start < text.length) - result = concat2(result, Text$slice(text, I(span_start+1), I(text.length))); + if (span_start < text.length) result = concat2(result, Text$slice(text, I(span_start + 1), I(text.length))); return result; } -public PUREFUNC bool Text$has(Text_t text, Text_t target) -{ +public +PUREFUNC bool Text$has(Text_t text, Text_t target) { TextIter_t text_state = NEW_TEXT_ITER_STATE(text), target_state = NEW_TEXT_ITER_STATE(target); for (int64_t i = 0; i < text.length; i++) { - if (_matches(&text_state, &target_state, i)) - return true; + if (_matches(&text_state, &target_state, i)) return true; } return false; } -public List_t Text$split(Text_t text, Text_t delimiters) -{ - if (delimiters.length == 0) - return Text$clusters(text); +public +List_t Text$split(Text_t text, Text_t delimiters) { + if (delimiters.length == 0) return Text$clusters(text); TextIter_t text_state = NEW_TEXT_ITER_STATE(text), delim_state = NEW_TEXT_ITER_STATE(delimiters); List_t splits = {}; - for (int64_t i = 0; i < text.length; ) { + for (int64_t i = 0; i < text.length;) { int64_t span_len = 0; while (i + span_len < text.length && !_matches(&text_state, &delim_state, i + span_len)) { span_len += 1; } - Text_t slice = Text$slice(text, I(i+1), I(i+span_len)); + Text_t slice = Text$slice(text, I(i + 1), I(i + span_len)); List$insert(&splits, &slice, I(0), sizeof(slice)); i += span_len + delimiters.length; if (i == text.length) { @@ -1255,20 +1206,20 @@ public List_t Text$split(Text_t text, Text_t delimiters) return splits; } -public List_t Text$split_any(Text_t text, Text_t delimiters) -{ - if (delimiters.length == 0) - return List(text); +public +List_t Text$split_any(Text_t text, Text_t delimiters) { + if (delimiters.length == 0) return List(text); TextIter_t text_state = NEW_TEXT_ITER_STATE(text), delim_state = NEW_TEXT_ITER_STATE(delimiters); List_t splits = {}; - for (int64_t i = 0; i < text.length; ) { + for (int64_t i = 0; i < text.length;) { int64_t span_len = 0; - while (i + span_len < text.length && !_has_grapheme(&delim_state, Text$get_grapheme_fast(&text_state, i + span_len))) { + while (i + span_len < text.length + && !_has_grapheme(&delim_state, Text$get_grapheme_fast(&text_state, i + span_len))) { span_len += 1; } bool trailing_delim = i + span_len < text.length; - Text_t slice = Text$slice(text, I(i+1), I(i+span_len)); + Text_t slice = Text$slice(text, I(i + 1), I(i + span_len)); List$insert(&splits, &slice, I(0), sizeof(slice)); i += span_len + 1; while (i < text.length && _has_grapheme(&delim_state, Text$get_grapheme_fast(&text_state, i))) { @@ -1288,8 +1239,7 @@ typedef struct { Text_t delimiter; } split_iter_state_t; -static OptionalText_t next_split(split_iter_state_t *state) -{ +static OptionalText_t next_split(split_iter_state_t *state) { Text_t text = state->state.stack[0].text; if (state->i >= text.length) { if (state->delimiter.length > 0 && state->i == text.length) { // special case @@ -1310,21 +1260,20 @@ static OptionalText_t next_split(split_iter_state_t *state) while (i + span_len < text.length && !_matches(&state->state, &delim_state, i + span_len)) { span_len += 1; } - Text_t slice = Text$slice(text, I(i+1), I(i+span_len)); + Text_t slice = Text$slice(text, I(i + 1), I(i + span_len)); state->i = i + span_len + state->delimiter.length; return slice; } -public Closure_t Text$by_split(Text_t text, Text_t delimiter) -{ +public +Closure_t Text$by_split(Text_t text, Text_t delimiter) { return (Closure_t){ - .fn=(void*)next_split, - .userdata=new(split_iter_state_t, .state=NEW_TEXT_ITER_STATE(text), .i=0, .delimiter=delimiter), + .fn = (void *)next_split, + .userdata = new (split_iter_state_t, .state = NEW_TEXT_ITER_STATE(text), .i = 0, .delimiter = delimiter), }; } -static OptionalText_t next_split_any(split_iter_state_t *state) -{ +static OptionalText_t next_split_any(split_iter_state_t *state) { Text_t text = state->state.stack[0].text; if (state->i >= text.length) { if (state->delimiter.length > 0 && state->i == text.length) { // special case @@ -1335,7 +1284,7 @@ static OptionalText_t next_split_any(split_iter_state_t *state) } if (state->delimiter.length == 0) { // special case - Text_t ret = Text$cluster(text, I(state->i+1)); + Text_t ret = Text$cluster(text, I(state->i + 1)); state->i += 1; return ret; } @@ -1343,10 +1292,11 @@ static OptionalText_t next_split_any(split_iter_state_t *state) TextIter_t delim_state = NEW_TEXT_ITER_STATE(state->delimiter); int64_t i = state->i; int64_t span_len = 0; - while (i + span_len < text.length && !_has_grapheme(&delim_state, Text$get_grapheme_fast(&state->state, i + span_len))) { + while (i + span_len < text.length + && !_has_grapheme(&delim_state, Text$get_grapheme_fast(&state->state, i + span_len))) { span_len += 1; } - Text_t slice = Text$slice(text, I(i+1), I(i+span_len)); + Text_t slice = Text$slice(text, I(i + 1), I(i + span_len)); i += span_len + 1; while (i < text.length && _has_grapheme(&delim_state, Text$get_grapheme_fast(&state->state, i))) { i += 1; @@ -1355,18 +1305,16 @@ static OptionalText_t next_split_any(split_iter_state_t *state) return slice; } -public Closure_t Text$by_split_any(Text_t text, Text_t delimiters) -{ +public +Closure_t Text$by_split_any(Text_t text, Text_t delimiters) { return (Closure_t){ - .fn=(void*)next_split_any, - .userdata=new(split_iter_state_t, .state=NEW_TEXT_ITER_STATE(text), .i=0, .delimiter=delimiters), + .fn = (void *)next_split_any, + .userdata = new (split_iter_state_t, .state = NEW_TEXT_ITER_STATE(text), .i = 0, .delimiter = delimiters), }; } -PUREFUNC public bool Text$equal_values(Text_t a, Text_t b) -{ - if (a.length != b.length) - return false; +PUREFUNC public bool Text$equal_values(Text_t a, Text_t b) { + if (a.length != b.length) return false; int64_t len = a.length; TextIter_t a_state = NEW_TEXT_ITER_STATE(a), b_state = NEW_TEXT_ITER_STATE(b); // TODO: make this smarter and more efficient @@ -1378,17 +1326,14 @@ PUREFUNC public bool Text$equal_values(Text_t a, Text_t b) return true; } -PUREFUNC public bool Text$equal(const void *a, const void *b, const TypeInfo_t *info) -{ +PUREFUNC public bool Text$equal(const void *a, const void *b, const TypeInfo_t *info) { (void)info; if (a == b) return true; - return Text$equal_values(*(Text_t*)a, *(Text_t*)b); + return Text$equal_values(*(Text_t *)a, *(Text_t *)b); } -PUREFUNC public bool Text$equal_ignoring_case(Text_t a, Text_t b, Text_t language) -{ - if (a.length != b.length) - return false; +PUREFUNC public bool Text$equal_ignoring_case(Text_t a, Text_t b, Text_t language) { + if (a.length != b.length) return false; int64_t len = a.length; TextIter_t a_state = NEW_TEXT_ITER_STATE(a), b_state = NEW_TEXT_ITER_STATE(b); const char *uc_language = Text$as_c_string(language); @@ -1396,76 +1341,79 @@ PUREFUNC public bool Text$equal_ignoring_case(Text_t a, Text_t b, Text_t languag int32_t ai = Text$get_grapheme_fast(&a_state, i); int32_t bi = Text$get_grapheme_fast(&b_state, i); if (ai != bi) { - const ucs4_t *a_codepoints = ai >= 0 ? (ucs4_t*)&ai : GRAPHEME_CODEPOINTS(ai); + const ucs4_t *a_codepoints = ai >= 0 ? (ucs4_t *)&ai : GRAPHEME_CODEPOINTS(ai); int64_t a_len = ai >= 0 ? 1 : NUM_GRAPHEME_CODEPOINTS(ai); - const ucs4_t *b_codepoints = bi >= 0 ? (ucs4_t*)&bi : GRAPHEME_CODEPOINTS(bi); + const ucs4_t *b_codepoints = bi >= 0 ? (ucs4_t *)&bi : GRAPHEME_CODEPOINTS(bi); int64_t b_len = bi >= 0 ? 1 : NUM_GRAPHEME_CODEPOINTS(bi); int cmp = 0; (void)u32_casecmp(a_codepoints, (size_t)a_len, b_codepoints, (size_t)b_len, uc_language, UNINORM_NFC, &cmp); - if (cmp != 0) - return false; + if (cmp != 0) return false; } } return true; } -public Text_t Text$upper(Text_t text, Text_t language) -{ +public +Text_t Text$upper(Text_t text, Text_t language) { if (text.length == 0) return text; List_t codepoints = Text$utf32_codepoints(text); const char *uc_language = Text$as_c_string(language); size_t out_len = 0; ucs4_t *upper = u32_toupper(codepoints.data, (size_t)codepoints.length, uc_language, UNINORM_NFC, NULL, &out_len); - Text_t ret = Text$from_codepoints((List_t){.data=upper, .length=(int64_t)out_len, .stride=sizeof(int32_t)}); + Text_t ret = Text$from_codepoints((List_t){.data = upper, .length = (int64_t)out_len, .stride = sizeof(int32_t)}); return ret; } -public Text_t Text$lower(Text_t text, Text_t language) -{ +public +Text_t Text$lower(Text_t text, Text_t language) { if (text.length == 0) return text; List_t codepoints = Text$utf32_codepoints(text); const char *uc_language = Text$as_c_string(language); size_t out_len = 0; ucs4_t *lower = u32_tolower(codepoints.data, (size_t)codepoints.length, uc_language, UNINORM_NFC, NULL, &out_len); - Text_t ret = Text$from_codepoints((List_t){.data=lower, .length=(int64_t)out_len, .stride=sizeof(int32_t)}); + Text_t ret = Text$from_codepoints((List_t){.data = lower, .length = (int64_t)out_len, .stride = sizeof(int32_t)}); return ret; } -public Text_t Text$title(Text_t text, Text_t language) -{ +public +Text_t Text$title(Text_t text, Text_t language) { if (text.length == 0) return text; List_t codepoints = Text$utf32_codepoints(text); const char *uc_language = Text$as_c_string(language); size_t out_len = 0; ucs4_t *title = u32_totitle(codepoints.data, (size_t)codepoints.length, uc_language, UNINORM_NFC, NULL, &out_len); - Text_t ret = Text$from_codepoints((List_t){.data=title, .length=(int64_t)out_len, .stride=sizeof(int32_t)}); + Text_t ret = Text$from_codepoints((List_t){.data = title, .length = (int64_t)out_len, .stride = sizeof(int32_t)}); return ret; } -public Text_t Text$quoted(Text_t text, bool colorize, Text_t quotation_mark) -{ - if (quotation_mark.length != 1) - fail("Invalid quote text: ", quotation_mark, " (must have length == 1)"); +public +Text_t Text$quoted(Text_t text, bool colorize, Text_t quotation_mark) { + if (quotation_mark.length != 1) fail("Invalid quote text: ", quotation_mark, " (must have length == 1)"); Text_t ret = colorize ? Text("\x1b[35m") : EMPTY_TEXT; - if (!Text$equal_values(quotation_mark, Text("\"")) && !Text$equal_values(quotation_mark, Text("'")) && !Text$equal_values(quotation_mark, Text("`"))) + if (!Text$equal_values(quotation_mark, Text("\"")) && !Text$equal_values(quotation_mark, Text("'")) + && !Text$equal_values(quotation_mark, Text("`"))) ret = concat2_assuming_safe(ret, Text("$")); ret = concat2_assuming_safe(ret, quotation_mark); int32_t quote_char = Text$get_grapheme(quotation_mark, 0); -#define flush_unquoted() ({ \ - if (unquoted_span > 0) { \ - ret = concat2_assuming_safe(ret, Text$slice(text, I(i-unquoted_span+1), I(i))); \ - unquoted_span = 0; \ - } }) -#define add_escaped(str) ({ \ - flush_unquoted(); \ - if (colorize) ret = concat2_assuming_safe(ret, Text("\x1b[34;1m")); \ - ret = concat2_assuming_safe(ret, Text("\\" str)); \ - if (colorize) ret = concat2_assuming_safe(ret, Text("\x1b[0;35m")); }) +#define flush_unquoted() \ + ({ \ + if (unquoted_span > 0) { \ + ret = concat2_assuming_safe(ret, Text$slice(text, I(i - unquoted_span + 1), I(i))); \ + unquoted_span = 0; \ + } \ + }) +#define add_escaped(str) \ + ({ \ + flush_unquoted(); \ + if (colorize) ret = concat2_assuming_safe(ret, Text("\x1b[34;1m")); \ + ret = concat2_assuming_safe(ret, Text("\\" str)); \ + if (colorize) ret = concat2_assuming_safe(ret, Text("\x1b[0;35m")); \ + }) TextIter_t state = NEW_TEXT_ITER_STATE(text); int64_t unquoted_span = 0; int64_t i = 0; @@ -1488,8 +1436,10 @@ public Text_t Text$quoted(Text_t text, bool colorize, Text_t quotation_mark) add_escaped("$"); break; } - case '\x00' ... '\x06': case '\x0E' ... '\x1A': - case '\x1C' ... '\x1F': case '\x7F' ... '\x7F': { + case '\x00' ... '\x06': + case '\x0E' ... '\x1A': + case '\x1C' ... '\x1F': + case '\x7F' ... '\x7F': { flush_unquoted(); if (colorize) ret = concat2_assuming_safe(ret, Text("\x1b[34;1m")); ret = concat2_assuming_safe(ret, Text("\\x")); @@ -1499,8 +1449,7 @@ public Text_t Text$quoted(Text_t text, bool colorize, Text_t quotation_mark) '\0', }; ret = concat2_assuming_safe(ret, Text$from_strn(tmp, 2)); - if (colorize) - ret = concat2_assuming_safe(ret, Text("\x1b[0;35m")); + if (colorize) ret = concat2_assuming_safe(ret, Text("\x1b[0;35m")); break; } default: { @@ -1522,21 +1471,19 @@ public Text_t Text$quoted(Text_t text, bool colorize, Text_t quotation_mark) #undef flush_unquoted ret = concat2_assuming_safe(ret, quotation_mark); - if (colorize) - ret = concat2_assuming_safe(ret, Text("\x1b[m")); + if (colorize) ret = concat2_assuming_safe(ret, Text("\x1b[m")); return ret; } -public Text_t Text$as_text(const void *vtext, bool colorize, const TypeInfo_t *info) -{ +public +Text_t Text$as_text(const void *vtext, bool colorize, const TypeInfo_t *info) { (void)info; if (!vtext) return info && info->TextInfo.lang ? Text$from_str(info->TextInfo.lang) : Text("Text"); - Text_t text = *(Text_t*)vtext; + Text_t text = *(Text_t *)vtext; // Figure out the best quotation mark to use: - bool has_double_quote = false, has_backtick = false, - has_single_quote = false, needs_escapes = false; + bool has_double_quote = false, has_backtick = false, has_single_quote = false, needs_escapes = false; TextIter_t state = NEW_TEXT_ITER_STATE(text); for (int64_t i = 0; i < text.length; i++) { int32_t g = Text$get_grapheme_fast(&state, i); @@ -1554,39 +1501,33 @@ public Text_t Text$as_text(const void *vtext, bool colorize, const TypeInfo_t *i // needing to escape them by using single quotes, but only if we don't have // single quotes or need to escape anything else (because single quotes // don't have interpolation): - if (has_double_quote && !has_single_quote) - quote = Text("'"); + if (has_double_quote && !has_single_quote) quote = Text("'"); // If there is a double quote, but no backtick, we can save a bit of // escaping by using backtick instead of double quote: - else if (has_double_quote && has_single_quote && !has_backtick && !needs_escapes) - quote = Text("`"); + else if (has_double_quote && has_single_quote && !has_backtick && !needs_escapes) quote = Text("`"); // Otherwise fall back to double quotes as the default quoting style: - else - quote = Text("\""); + else quote = Text("\""); Text_t as_text = Text$quoted(text, colorize, quote); if (info && info->TextInfo.lang && info != &Text$info) - as_text = Text$concat( - colorize ? Text("\x1b[1m$") : Text("$"), - Text$from_str(info->TextInfo.lang), - colorize ? Text("\x1b[0m") : Text(""), - as_text); + as_text = Text$concat(colorize ? Text("\x1b[1m$") : Text("$"), Text$from_str(info->TextInfo.lang), + colorize ? Text("\x1b[0m") : Text(""), as_text); return as_text; } -public Text_t Text$join(Text_t glue, List_t pieces) -{ +public +Text_t Text$join(Text_t glue, List_t pieces) { if (pieces.length == 0) return EMPTY_TEXT; - Text_t result = *(Text_t*)pieces.data; + Text_t result = *(Text_t *)pieces.data; for (int64_t i = 1; i < pieces.length; i++) { - result = Text$concat(result, glue, *(Text_t*)(pieces.data + i*pieces.stride)); + result = Text$concat(result, glue, *(Text_t *)(pieces.data + i * pieces.stride)); } return result; } -public List_t Text$clusters(Text_t text) -{ +public +List_t Text$clusters(Text_t text) { List_t clusters = {}; for (int64_t i = 1; i <= text.length; i++) { Text_t cluster = Text$slice(text, I(i), I(i)); @@ -1595,9 +1536,9 @@ public List_t Text$clusters(Text_t text) return clusters; } -public List_t Text$utf32_codepoints(Text_t text) -{ - List_t codepoints = {.atomic=1}; +public +List_t Text$utf32_codepoints(Text_t text) { + List_t codepoints = {.atomic = 1}; TextIter_t state = NEW_TEXT_ITER_STATE(text); for (int64_t i = 0; i < text.length; i++) { int32_t grapheme = Text$get_grapheme_fast(&state, i); @@ -1613,24 +1554,23 @@ public List_t Text$utf32_codepoints(Text_t text) return codepoints; } -public List_t Text$utf8_bytes(Text_t text) -{ +public +List_t Text$utf8_bytes(Text_t text) { const char *str = Text$as_c_string(text); - return (List_t){.length=(int64_t)strlen(str), .stride=1, .atomic=1, .data=(void*)str}; + return (List_t){.length = (int64_t)strlen(str), .stride = 1, .atomic = 1, .data = (void *)str}; } -static INLINE const char *codepoint_name(ucs4_t c) -{ +static INLINE const char *codepoint_name(ucs4_t c) { char *name = GC_MALLOC_ATOMIC(UNINAME_MAX); char *found_name = unicode_character_name(c, name); if (found_name) return found_name; const uc_block_t *block = uc_block(c); assert(block); - return String(block->name, "-", hex(c, .no_prefix=true, .uppercase=true)); + return String(block->name, "-", hex(c, .no_prefix = true, .uppercase = true)); } -public List_t Text$codepoint_names(Text_t text) -{ +public +List_t Text$codepoint_names(Text_t text) { List_t names = {}; TextIter_t state = NEW_TEXT_ITER_STATE(text); for (int64_t i = 0; i < text.length; i++) { @@ -1650,81 +1590,78 @@ public List_t Text$codepoint_names(Text_t text) return names; } -public Text_t Text$from_codepoints(List_t codepoints) -{ - if (codepoints.stride != sizeof(uint32_t)) - List$compact(&codepoints, sizeof(uint32_t)); +public +Text_t Text$from_codepoints(List_t codepoints) { + if (codepoints.stride != sizeof(uint32_t)) List$compact(&codepoints, sizeof(uint32_t)); List_t graphemes = {}; Table_t unique_clusters = {}; - const uint32_t *pos = (const uint32_t*)codepoints.data; - const uint32_t *end = (const uint32_t*)&pos[codepoints.length]; + const uint32_t *pos = (const uint32_t *)codepoints.data; + const uint32_t *end = (const uint32_t *)&pos[codepoints.length]; // Iterate over grapheme clusters - for (const uint32_t *next; (next=u32_grapheme_next(pos, end)); pos = next) { + for (const uint32_t *next; (next = u32_grapheme_next(pos, end)); pos = next) { // Buffer for normalized cluster: uint32_t buf[256]; - size_t u32_normlen = sizeof(buf)/sizeof(buf[0]); - uint32_t *u32s_normalized = u32_normalize(UNINORM_NFC, pos, (size_t)(next-pos), buf, &u32_normlen); + size_t u32_normlen = sizeof(buf) / sizeof(buf[0]); + uint32_t *u32s_normalized = u32_normalize(UNINORM_NFC, pos, (size_t)(next - pos), buf, &u32_normlen); int32_t g = get_synthetic_grapheme(u32s_normalized, (int64_t)u32_normlen); List$insert(&graphemes, &g, I(0), sizeof(int32_t)); - Table$get_or_setdefault( - &unique_clusters, int32_t, uint8_t, g, (uint8_t)unique_clusters.entries.length, - Table$info(&Int32$info, &Byte$info)); + Table$get_or_setdefault(&unique_clusters, int32_t, uint8_t, g, (uint8_t)unique_clusters.entries.length, + Table$info(&Int32$info, &Byte$info)); if (u32s_normalized != buf) free(u32s_normalized); if (unique_clusters.entries.length == 256) { List_t remaining_codepoints = { - .length=(int64_t)(end-next), - .data=(void*)next, - .stride=sizeof(int32_t), + .length = (int64_t)(end - next), + .data = (void *)next, + .stride = sizeof(int32_t), }; - return concat2_assuming_safe(Text$from_components(graphemes, unique_clusters), Text$from_codepoints(remaining_codepoints)); + return concat2_assuming_safe(Text$from_components(graphemes, unique_clusters), + Text$from_codepoints(remaining_codepoints)); } } return Text$from_components(graphemes, unique_clusters); } -public OptionalText_t Text$from_codepoint_names(List_t codepoint_names) -{ +public +OptionalText_t Text$from_codepoint_names(List_t codepoint_names) { List_t codepoints = {}; for (int64_t i = 0; i < codepoint_names.length; i++) { - Text_t *name = ((Text_t*)(codepoint_names.data + i*codepoint_names.stride)); + Text_t *name = ((Text_t *)(codepoint_names.data + i * codepoint_names.stride)); const char *name_str = Text$as_c_string(*name); ucs4_t codepoint = unicode_name_character(name_str); - if (codepoint == UNINAME_INVALID) - return NONE_TEXT; + if (codepoint == UNINAME_INVALID) return NONE_TEXT; List$insert(&codepoints, &codepoint, I_small(0), sizeof(ucs4_t)); } return Text$from_codepoints(codepoints); } -public OptionalText_t Text$from_bytes(List_t bytes) -{ - if (bytes.stride != sizeof(int8_t)) - List$compact(&bytes, sizeof(int8_t)); +public +OptionalText_t Text$from_bytes(List_t bytes) { + if (bytes.stride != sizeof(int8_t)) List$compact(&bytes, sizeof(int8_t)); return Text$from_strn(bytes.data, (size_t)bytes.length); } -public List_t Text$lines(Text_t text) -{ +public +List_t Text$lines(Text_t text) { List_t lines = {}; TextIter_t state = NEW_TEXT_ITER_STATE(text); for (int64_t i = 0, line_start = 0; i < text.length; i++) { int32_t grapheme = Text$get_grapheme_fast(&state, i); if (grapheme == '\r' && Text$get_grapheme_fast(&state, i + 1) == '\n') { // CRLF - Text_t line = Text$slice(text, I(line_start+1), I(i)); + Text_t line = Text$slice(text, I(line_start + 1), I(i)); List$insert(&lines, &line, I_small(0), sizeof(Text_t)); i += 1; // skip one extra for CR line_start = i + 1; } else if (grapheme == '\n') { // newline - Text_t line = Text$slice(text, I(line_start+1), I(i)); + Text_t line = Text$slice(text, I(line_start + 1), I(i)); List$insert(&lines, &line, I_small(0), sizeof(Text_t)); line_start = i + 1; - } else if (i == text.length-1 && line_start != i) { // last line - Text_t line = Text$slice(text, I(line_start+1), I(i+1)); + } else if (i == text.length - 1 && line_start != i) { // last line + Text_t line = Text$slice(text, I(line_start + 1), I(i + 1)); List$insert(&lines, &line, I_small(0), sizeof(Text_t)); } } @@ -1736,21 +1673,20 @@ typedef struct { int64_t i; } line_iter_state_t; -static OptionalText_t next_line(line_iter_state_t *state) -{ +static OptionalText_t next_line(line_iter_state_t *state) { Text_t text = state->state.stack[0].text; for (int64_t i = state->i; i < text.length; i++) { int32_t grapheme = Text$get_grapheme_fast(&state->state, i); if (grapheme == '\r' && Text$get_grapheme_fast(&state->state, i + 1) == '\n') { // CRLF - Text_t line = Text$slice(text, I(state->i+1), I(i)); + Text_t line = Text$slice(text, I(state->i + 1), I(i)); state->i = i + 2; // skip one extra for CR return line; } else if (grapheme == '\n') { // newline - Text_t line = Text$slice(text, I(state->i+1), I(i)); + Text_t line = Text$slice(text, I(state->i + 1), I(i)); state->i = i + 1; return line; - } else if (i == text.length-1 && state->i != i) { // last line - Text_t line = Text$slice(text, I(state->i+1), I(i+1)); + } else if (i == text.length - 1 && state->i != i) { // last line + Text_t line = Text$slice(text, I(state->i + 1), I(i + 1)); state->i = i + 1; return line; } @@ -1758,81 +1694,75 @@ static OptionalText_t next_line(line_iter_state_t *state) return NONE_TEXT; } -public Closure_t Text$by_line(Text_t text) -{ +public +Closure_t Text$by_line(Text_t text) { return (Closure_t){ - .fn=(void*)next_line, - .userdata=new(line_iter_state_t, .state=NEW_TEXT_ITER_STATE(text), .i=0), + .fn = (void *)next_line, + .userdata = new (line_iter_state_t, .state = NEW_TEXT_ITER_STATE(text), .i = 0), }; } -PUREFUNC public bool Text$is_none(const void *t, const TypeInfo_t *info) -{ +PUREFUNC public bool Text$is_none(const void *t, const TypeInfo_t *info) { (void)info; - return ((Text_t*)t)->length < 0; + return ((Text_t *)t)->length < 0; } -public Int_t Text$memory_size(Text_t text) -{ +public +Int_t Text$memory_size(Text_t text) { switch (text.tag) { - case TEXT_ASCII: - return Int$from_int64((int64_t)sizeof(Text_t) + (int64_t)sizeof(char[text.length])); - case TEXT_GRAPHEMES: - return Int$from_int64((int64_t)sizeof(Text_t) + (int64_t)sizeof(int32_t[text.length])); + case TEXT_ASCII: return Int$from_int64((int64_t)sizeof(Text_t) + (int64_t)sizeof(char[text.length])); + case TEXT_GRAPHEMES: return Int$from_int64((int64_t)sizeof(Text_t) + (int64_t)sizeof(int32_t[text.length])); case TEXT_BLOB: - return Int$from_int64((int64_t)sizeof(Text_t) + (int64_t)((void*)text.blob.bytes - (void*)text.blob.map) + (int64_t)sizeof(uint8_t[text.length])); + return Int$from_int64((int64_t)sizeof(Text_t) + (int64_t)((void *)text.blob.bytes - (void *)text.blob.map) + + (int64_t)sizeof(uint8_t[text.length])); case TEXT_CONCAT: - return Int$plus( - Int$from_int64((int64_t)sizeof(Text_t)), - Int$plus(Text$memory_size(*text.left), Text$memory_size(*text.right))); + return Int$plus(Int$from_int64((int64_t)sizeof(Text_t)), + Int$plus(Text$memory_size(*text.left), Text$memory_size(*text.right))); default: errx(1, "Invalid text tag: %d", text.tag); } } -public Text_t Text$layout(Text_t text) -{ +public +Text_t Text$layout(Text_t text) { switch (text.tag) { - case TEXT_ASCII: - return Texts(Text("ASCII("), Int64$as_text((int64_t[1]){text.length}, false, NULL), Text(")")); + case TEXT_ASCII: return Texts(Text("ASCII("), Int64$as_text((int64_t[1]){text.length}, false, NULL), Text(")")); case TEXT_GRAPHEMES: return Texts(Text("Graphemes("), Int64$as_text((int64_t[1]){text.length}, false, NULL), Text(")")); - case TEXT_BLOB: - return Texts(Text("Blob("), Int64$as_text((int64_t[1]){text.length}, false, NULL), Text(")")); + case TEXT_BLOB: return Texts(Text("Blob("), Int64$as_text((int64_t[1]){text.length}, false, NULL), Text(")")); case TEXT_CONCAT: return Texts(Text("Concat("), Text$layout(*text.left), Text(", "), Text$layout(*text.right), Text(")")); default: errx(1, "Invalid text tag: %d", text.tag); } } -public void Text$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) -{ +public +void Text$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) { (void)info; - const char *str = Text$as_c_string(*(Text_t*)obj); + const char *str = Text$as_c_string(*(Text_t *)obj); int64_t len = (int64_t)strlen(str); Int64$serialize(&len, out, pointers, &Int64$info); fwrite(str, sizeof(char), (size_t)len, out); } -public void Text$deserialize(FILE *in, void *out, List_t *pointers, const TypeInfo_t *info) -{ +public +void Text$deserialize(FILE *in, void *out, List_t *pointers, const TypeInfo_t *info) { (void)info; int64_t len = 0; Int64$deserialize(in, &len, pointers, &Int64$info); - if (len < 0) - fail("Cannot deserialize text with a negative length!"); - char *buf = GC_MALLOC_ATOMIC((size_t)len+1); - if (fread(buf, sizeof(char), (size_t)len, in) != (size_t)len) - fail("Not enough data in stream to deserialize"); - buf[len+1] = '\0'; - *(Text_t*)out = Text$from_strn(buf, (size_t)len); -} - -public const TypeInfo_t Text$info = { - .size=sizeof(Text_t), - .align=__alignof__(Text_t), - .tag=TextInfo, - .TextInfo={.lang="Text"}, - .metamethods=Text$metamethods, + if (len < 0) fail("Cannot deserialize text with a negative length!"); + char *buf = GC_MALLOC_ATOMIC((size_t)len + 1); + if (fread(buf, sizeof(char), (size_t)len, in) != (size_t)len) fail("Not enough data in stream to deserialize"); + buf[len + 1] = '\0'; + *(Text_t *)out = Text$from_strn(buf, (size_t)len); +} + +public +const TypeInfo_t Text$info = { + .size = sizeof(Text_t), + .align = __alignof__(Text_t), + .tag = TextInfo, + .TextInfo = {.lang = "Text"}, + .metamethods = Text$metamethods, }; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/text.h b/src/stdlib/text.h index cb3f1b90..6bedcb60 100644 --- a/src/stdlib/text.h +++ b/src/stdlib/text.h @@ -21,22 +21,20 @@ typedef struct { int64_t stack_index; } TextIter_t; -#define NEW_TEXT_ITER_STATE(t) (TextIter_t){.stack={{t, 0}}, .stack_index=0} +#define NEW_TEXT_ITER_STATE(t) (TextIter_t){.stack = {{t, 0}}, .stack_index = 0} -#define Text(str) ((Text_t){.length=sizeof(str)-1, .tag=TEXT_ASCII, .ascii="" str}) +#define Text(str) ((Text_t){.length = sizeof(str) - 1, .tag = TEXT_ASCII, .ascii = "" str}) static inline Text_t Text_from_str_literal(const char *str) { - return (Text_t){.length=strlen(str), .tag=TEXT_ASCII, .ascii=str}; + return (Text_t){.length = strlen(str), .tag = TEXT_ASCII, .ascii = str}; } -static inline Text_t Text_from_text(Text_t t) { - return t; -} +static inline Text_t Text_from_text(Text_t t) { return t; } -#define convert_to_text(x) _Generic(x, Text_t: Text_from_text, char*: Text$from_str, const char*: Text$from_str)(x) +#define convert_to_text(x) _Generic(x, Text_t: Text_from_text, char *: Text$from_str, const char *: Text$from_str)(x) Text_t Text$_concat(int n, Text_t items[n]); -#define Text$concat(...) Text$_concat(sizeof((Text_t[]){__VA_ARGS__})/sizeof(Text_t), (Text_t[]){__VA_ARGS__}) +#define Text$concat(...) Text$_concat(sizeof((Text_t[]){__VA_ARGS__}) / sizeof(Text_t), (Text_t[]){__VA_ARGS__}) #define Texts(...) Text$concat(MAP_LIST(convert_to_text, __VA_ARGS__)) // int Text$print(FILE *stream, Text_t t); Text_t Text$slice(Text_t text, Int_t first_int, Int_t last_int); @@ -46,12 +44,12 @@ Text_t Text$reversed(Text_t text); Text_t Text$cluster(Text_t text, Int_t index_int); OptionalText_t Text$from_str(const char *str); OptionalText_t Text$from_strn(const char *str, size_t len); -PUREFUNC uint64_t Text$hash(const void *text, const TypeInfo_t*); -PUREFUNC int32_t Text$compare(const void *va, const void *vb, const TypeInfo_t*); -PUREFUNC bool Text$equal(const void *a, const void *b, const TypeInfo_t*); +PUREFUNC uint64_t Text$hash(const void *text, const TypeInfo_t *); +PUREFUNC int32_t Text$compare(const void *va, const void *vb, const TypeInfo_t *); +PUREFUNC bool Text$equal(const void *a, const void *b, const TypeInfo_t *); PUREFUNC bool Text$equal_values(Text_t a, Text_t b); PUREFUNC bool Text$equal_ignoring_case(Text_t a, Text_t b, Text_t language); -PUREFUNC bool Text$is_none(const void *t, const TypeInfo_t*); +PUREFUNC bool Text$is_none(const void *t, const TypeInfo_t *); Text_t Text$upper(Text_t text, Text_t language); Text_t Text$lower(Text_t text, Text_t language); Text_t Text$title(Text_t text, Text_t language); @@ -92,8 +90,7 @@ Text_t Text$layout(Text_t text); void Text$serialize(const void *obj, FILE *out, Table_t *, const TypeInfo_t *); void Text$deserialize(FILE *in, void *out, List_t *, const TypeInfo_t *); -MACROLIKE int32_t Text$get_grapheme(Text_t text, int64_t index) -{ +MACROLIKE int32_t Text$get_grapheme(Text_t text, int64_t index) { TextIter_t state = NEW_TEXT_ITER_STATE(text); return Text$get_grapheme_fast(&state, index); } @@ -101,14 +98,15 @@ MACROLIKE int32_t Text$get_grapheme(Text_t text, int64_t index) extern const TypeInfo_t Text$info; extern Text_t EMPTY_TEXT; -#define Text$metamethods { \ - .as_text=Text$as_text, \ - .hash=Text$hash, \ - .compare=Text$compare, \ - .equal=Text$equal, \ - .is_none=Text$is_none, \ - .serialize=Text$serialize, \ - .deserialize=Text$deserialize, \ -} +#define Text$metamethods \ + { \ + .as_text = Text$as_text, \ + .hash = Text$hash, \ + .compare = Text$compare, \ + .equal = Text$equal, \ + .is_none = Text$is_none, \ + .serialize = Text$serialize, \ + .deserialize = Text$deserialize, \ + } // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/types.c b/src/stdlib/types.c index 0f17cb21..e64195fe 100644 --- a/src/stdlib/types.c +++ b/src/stdlib/types.c @@ -3,24 +3,21 @@ #include <gc.h> #include <sys/param.h> -#include "util.h" #include "text.h" #include "types.h" +#include "util.h" -public Text_t Type$as_text(const void *typeinfo, bool colorize, const TypeInfo_t *type) -{ +public +Text_t Type$as_text(const void *typeinfo, bool colorize, const TypeInfo_t *type) { if (!typeinfo) return Text("Type"); - if (colorize) - return Text$concat( - Text("\x1b[36;1m"), - Text$from_str(type->TypeInfoInfo.type_str), - Text("\x1b[m")); - else - return Text$from_str(type->TypeInfoInfo.type_str); + if (colorize) return Text$concat(Text("\x1b[36;1m"), Text$from_str(type->TypeInfoInfo.type_str), Text("\x1b[m")); + else return Text$from_str(type->TypeInfoInfo.type_str); } -public const TypeInfo_t Void$info = {.size=0, .align=0, .tag=StructInfo}; -public const TypeInfo_t Abort$info = {.size=0, .align=0, .tag=StructInfo}; +public +const TypeInfo_t Void$info = {.size = 0, .align = 0, .tag = StructInfo}; +public +const TypeInfo_t Abort$info = {.size = 0, .align = 0, .tag = StructInfo}; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/types.h b/src/stdlib/types.h index 60f1fcfd..a6509183 100644 --- a/src/stdlib/types.h +++ b/src/stdlib/types.h @@ -10,13 +10,13 @@ typedef struct TypeInfo_s TypeInfo_t; -typedef void (*serialize_fn_t)(const void*, FILE*, Table_t*, const TypeInfo_t*); -typedef void (*deserialize_fn_t)(FILE*, void*, List_t*, const TypeInfo_t*); -typedef bool (*is_none_fn_t)(const void*, const TypeInfo_t*); -typedef uint64_t (*hash_fn_t)(const void*, const TypeInfo_t*); -typedef int32_t (*compare_fn_t)(const void*, const void*, const TypeInfo_t*); -typedef bool (*equal_fn_t)(const void*, const void*, const TypeInfo_t*); -typedef Text_t (*as_text_fn_t)(const void*, bool, const TypeInfo_t*); +typedef void (*serialize_fn_t)(const void *, FILE *, Table_t *, const TypeInfo_t *); +typedef void (*deserialize_fn_t)(FILE *, void *, List_t *, const TypeInfo_t *); +typedef bool (*is_none_fn_t)(const void *, const TypeInfo_t *); +typedef uint64_t (*hash_fn_t)(const void *, const TypeInfo_t *); +typedef int32_t (*compare_fn_t)(const void *, const void *, const TypeInfo_t *); +typedef bool (*equal_fn_t)(const void *, const void *, const TypeInfo_t *); +typedef Text_t (*as_text_fn_t)(const void *, bool, const TypeInfo_t *); typedef struct { hash_fn_t hash; @@ -36,11 +36,22 @@ typedef struct { struct TypeInfo_s { int64_t size, align; metamethods_t metamethods; - struct { // Anonymous tagged union for convenience - enum { OpaqueInfo, StructInfo, EnumInfo, PointerInfo, TextInfo, ListInfo, TableInfo, FunctionInfo, - OptionalInfo, TypeInfoInfo } tag; + struct { // Anonymous tagged union for convenience + enum { + OpaqueInfo, + StructInfo, + EnumInfo, + PointerInfo, + TextInfo, + ListInfo, + TableInfo, + FunctionInfo, + OptionalInfo, + TypeInfoInfo + } tag; union { - struct {} OpaqueInfo; + struct { + } OpaqueInfo; struct { const char *sigil; const TypeInfo_t *pointed; @@ -72,7 +83,7 @@ struct TypeInfo_s { const char *name; NamedType_t *fields; int num_fields; - bool is_secret:1, is_opaque:1; + bool is_secret : 1, is_opaque : 1; } StructInfo; }; }; @@ -84,18 +95,22 @@ extern const TypeInfo_t Abort$info; Text_t Type$as_text(const void *typeinfo, bool colorize, const TypeInfo_t *type); -#define Type$info(typestr) &((TypeInfo_t){.size=sizeof(TypeInfo_t), .align=__alignof__(TypeInfo_t), \ - .tag=TypeInfoInfo, .TypeInfoInfo.type_str=typestr, \ - .metamethods={.serialize=cannot_serialize, .deserialize=cannot_deserialize, .as_text=Type$as_text}}) +#define Type$info(typestr) \ + &((TypeInfo_t){ \ + .size = sizeof(TypeInfo_t), \ + .align = __alignof__(TypeInfo_t), \ + .tag = TypeInfoInfo, \ + .TypeInfoInfo.type_str = typestr, \ + .metamethods = {.serialize = cannot_serialize, .deserialize = cannot_deserialize, .as_text = Type$as_text}}) -#define DEFINE_OPTIONAL_TYPE(t, unpadded_size, name) \ - typedef struct { \ - union { \ - t value; \ - struct { \ - char _padding[unpadded_size]; \ - Bool_t is_none; \ - }; \ - }; \ +#define DEFINE_OPTIONAL_TYPE(t, unpadded_size, name) \ + typedef struct { \ + union { \ + t value; \ + struct { \ + char _padding[unpadded_size]; \ + Bool_t is_none; \ + }; \ + }; \ } name // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/util.h b/src/stdlib/util.h index 3b00e6e9..819cecd9 100644 --- a/src/stdlib/util.h +++ b/src/stdlib/util.h @@ -3,38 +3,56 @@ // Built-in utility functions #include <assert.h> +#include <err.h> #include <gc.h> #include <stdbool.h> #include <string.h> -#include <err.h> #define streq(a, b) (((a) == NULL && (b) == NULL) || (((a) == NULL) == ((b) == NULL) && strcmp(a, b) == 0)) #define starts_with(line, prefix) (strncmp(line, prefix, strlen(prefix)) == 0) -#define ends_with(line, suffix) (strlen(line) >= strlen(suffix) && strcmp(line + strlen(line) - strlen(suffix), suffix) == 0) -#define new(t, ...) ((t*)memcpy(GC_MALLOC(sizeof(t)), &(t){__VA_ARGS__}, sizeof(t))) -#define heap(x) (__typeof(x)*)memcpy(GC_MALLOC(sizeof(x)), (__typeof(x)[1]){x}, sizeof(x)) -#define stack(x) (__typeof(x)*)((__typeof(x)[1]){x}) -#define check_initialized(var, init_var, name) *({ if (!init_var) fail("The variable " name " is being accessed before it has been initialized!"); \ - &var; }) +#define ends_with(line, suffix) \ + (strlen(line) >= strlen(suffix) && strcmp(line + strlen(line) - strlen(suffix), suffix) == 0) +#define new(t, ...) ((t *)memcpy(GC_MALLOC(sizeof(t)), &(t){__VA_ARGS__}, sizeof(t))) +#define heap(x) (__typeof(x) *)memcpy(GC_MALLOC(sizeof(x)), (__typeof(x)[1]){x}, sizeof(x)) +#define stack(x) (__typeof(x) *)((__typeof(x)[1]){x}) +#define check_initialized(var, init_var, name) \ + *({ \ + if (!init_var) fail("The variable " name " is being accessed before it has been initialized!"); \ + &var; \ + }) -#define IF_DECLARE(decl, expr, block) if (({ decl; expr ? ({ block; 1; }) : 0; })) {} +#define IF_DECLARE(decl, expr, block) \ + if (({ \ + decl; \ + expr ? ({ \ + block; \ + 1; \ + }) \ + : 0; \ + })) { \ + } -#define WHEN(type, subj, var, body) { type var = subj; switch (var.$tag) body } +#define WHEN(type, subj, var, body) \ + { \ + type var = subj; \ + switch (var.$tag) \ + body \ + } #ifndef public -#define public __attribute__ ((visibility ("default"))) +#define public __attribute__((visibility("default"))) #endif #ifndef PUREFUNC -#define PUREFUNC __attribute__ ((pure)) +#define PUREFUNC __attribute__((pure)) #endif #ifndef CONSTFUNC -#define CONSTFUNC __attribute__ ((const)) +#define CONSTFUNC __attribute__((const)) #endif #ifndef INLINE -#define INLINE inline __attribute__ ((always_inline)) +#define INLINE inline __attribute__((always_inline)) #endif #ifndef likely diff --git a/src/structs.c b/src/structs.c index 0bcb8a08..9be38d1f 100644 --- a/src/structs.c +++ b/src/structs.c @@ -5,31 +5,33 @@ #include "compile.h" #include "environment.h" #include "naming.h" -#include "stdlib/text.h" #include "stdlib/tables.h" +#include "stdlib/text.h" #include "typecheck.h" -Text_t compile_struct_typeinfo(env_t *env, type_t *t, const char *name, arg_ast_t *fields, bool is_secret, bool is_opaque) -{ +Text_t compile_struct_typeinfo(env_t *env, type_t *t, const char *name, arg_ast_t *fields, bool is_secret, + bool is_opaque) { Text_t typeinfo_name = namespace_name(env, env->namespace, Texts(name, "$$info")); - Text_t type_code = Match(t, StructType)->external ? - Text$from_str(name) : Texts("struct ", namespace_name(env, env->namespace, Texts(name, "$$struct"))); + Text_t type_code = Match(t, StructType)->external + ? Text$from_str(name) + : Texts("struct ", namespace_name(env, env->namespace, Texts(name, "$$struct"))); int num_fields = 0; for (arg_ast_t *f = fields; f; f = f->next) num_fields += 1; const char *short_name = name; - if (strchr(short_name, '$')) - short_name = strrchr(short_name, '$') + 1; + if (strchr(short_name, '$')) short_name = strrchr(short_name, '$') + 1; const char *metamethods = is_packed_data(t) ? "PackedData$metamethods" : "Struct$metamethods"; - Text_t typeinfo = Texts("public const TypeInfo_t ", typeinfo_name, - " = {.size=sizeof(", type_code, "), .align=__alignof__(", type_code, "), " - ".metamethods=", metamethods, ", " - ".tag=StructInfo, .StructInfo.name=\"", short_name, "\"", - is_secret ? Text(", .StructInfo.is_secret=true") : EMPTY_TEXT, - is_opaque ? Text(", .StructInfo.is_opaque=true") : EMPTY_TEXT, - ", .StructInfo.num_fields=", String(num_fields)); + Text_t typeinfo = Texts( + "public const TypeInfo_t ", typeinfo_name, " = {.size=sizeof(", type_code, "), .align=__alignof__(", type_code, + "), " + ".metamethods=", + metamethods, + ", " + ".tag=StructInfo, .StructInfo.name=\"", + short_name, "\"", is_secret ? Text(", .StructInfo.is_secret=true") : EMPTY_TEXT, + is_opaque ? Text(", .StructInfo.is_opaque=true") : EMPTY_TEXT, ", .StructInfo.num_fields=", String(num_fields)); if (fields) { typeinfo = Texts(typeinfo, ", .StructInfo.fields=(NamedType_t[", String(num_fields), "]){"); for (arg_ast_t *f = fields; f; f = f->next) { @@ -42,12 +44,12 @@ Text_t compile_struct_typeinfo(env_t *env, type_t *t, const char *name, arg_ast_ return Texts(typeinfo, "};\n"); } -Text_t compile_struct_header(env_t *env, ast_t *ast) -{ +Text_t compile_struct_header(env_t *env, ast_t *ast) { DeclareMatch(def, ast, StructDef); Text_t typeinfo_name = namespace_name(env, env->namespace, Texts(def->name, "$$info")); - Text_t type_code = def->external ? Text$from_str(def->name) - : Texts("struct ", namespace_name(env, env->namespace, Texts(def->name, "$$struct"))); + Text_t type_code = def->external + ? Text$from_str(def->name) + : Texts("struct ", namespace_name(env, env->namespace, Texts(def->name, "$$struct"))); Text_t fields = EMPTY_TEXT; for (arg_ast_t *field = def->fields; field; field = field->next) { @@ -65,12 +67,13 @@ Text_t compile_struct_header(env_t *env, ast_t *ast) Text_t struct_code = def->external ? EMPTY_TEXT : Texts(type_code, " {\n", fields, "};\n"); type_t *t = Table$str_get(*env->types, def->name); - Text_t unpadded_size = def->opaque ? Texts("sizeof(", type_code, ")") : Text$from_str(String((int64_t)unpadded_struct_size(t))); + Text_t unpadded_size = + def->opaque ? Texts("sizeof(", type_code, ")") : Text$from_str(String((int64_t)unpadded_struct_size(t))); Text_t typeinfo_code = Texts("extern const TypeInfo_t ", typeinfo_name, ";\n"); Text_t optional_code = EMPTY_TEXT; if (!def->opaque) { optional_code = Texts("DEFINE_OPTIONAL_TYPE(", compile_type(t), ", ", unpadded_size, ", ", - namespace_name(env, env->namespace, Texts("$Optional", def->name, "$$type")), ");\n"); + namespace_name(env, env->namespace, Texts("$Optional", def->name, "$$type")), ");\n"); } return Texts(struct_code, optional_code, typeinfo_code); } diff --git a/src/structs.h b/src/structs.h index d5337626..9a9d08e5 100644 --- a/src/structs.h +++ b/src/structs.h @@ -5,7 +5,8 @@ #include "ast.h" #include "environment.h" -Text_t compile_struct_typeinfo(env_t *env, type_t *t, const char *name, arg_ast_t *fields, bool is_secret, bool is_opaque); +Text_t compile_struct_typeinfo(env_t *env, type_t *t, const char *name, arg_ast_t *fields, bool is_secret, + bool is_opaque); Text_t compile_struct_header(env_t *env, ast_t *ast); // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 @@ -13,9 +13,9 @@ #endif #include "ast.h" -#include "config.h" #include "changes.md.h" #include "compile.h" +#include "config.h" #include "modules.h" #include "naming.h" #include "parse.h" @@ -33,8 +33,18 @@ #include "stdlib/text.h" #include "types.h" -#define run_cmd(...) ({ const char *_cmd = String(__VA_ARGS__); if (verbose) print("\033[34;1m", _cmd, "\033[m"); popen(_cmd, "w"); }) -#define xsystem(...) ({ int _status = system(String(__VA_ARGS__)); if (!WIFEXITED(_status) || WEXITSTATUS(_status) != 0) errx(1, "Failed to run command: %s", String(__VA_ARGS__)); }) +#define run_cmd(...) \ + ({ \ + const char *_cmd = String(__VA_ARGS__); \ + if (verbose) print("\033[34;1m", _cmd, "\033[m"); \ + popen(_cmd, "w"); \ + }) +#define xsystem(...) \ + ({ \ + int _status = system(String(__VA_ARGS__)); \ + if (!WIFEXITED(_status) || WEXITSTATUS(_status) != 0) \ + errx(1, "Failed to run command: %s", String(__VA_ARGS__)); \ + }) #define list_text(list) Text$join(Text(" "), list) #define whisper(...) print("\033[2m", __VA_ARGS__, "\033[m") @@ -48,7 +58,7 @@ static const char *paths_str(List_t paths) { Text_t result = EMPTY_TEXT; for (int64_t i = 0; i < paths.length; i++) { if (i > 0) result = Texts(result, Text(" ")); - result = Texts(result, Path$as_text((Path_t*)(paths.data + i*paths.stride), false, &Path$info)); + result = Texts(result, Path$as_text((Path_t *)(paths.data + i * paths.stride), false, &Path$info)); } return Text$as_c_string(result); } @@ -59,50 +69,39 @@ static const char *paths_str(List_t paths) { #define SHARED_SUFFIX ".so" #endif -static OptionalList_t files = NONE_LIST, - args = NONE_LIST, - uninstall = NONE_LIST, - libraries = NONE_LIST; -static OptionalBool_t verbose = false, - quiet = false, - show_version = false, - show_parse_tree = false, - show_prefix = false, - stop_at_transpile = false, - stop_at_obj_compilation = false, - compile_exe = false, - should_install = false, - clean_build = false, - source_mapping = true, +static OptionalList_t files = NONE_LIST, args = NONE_LIST, uninstall = NONE_LIST, libraries = NONE_LIST; +static OptionalBool_t verbose = false, quiet = false, show_version = false, show_parse_tree = false, + show_prefix = false, stop_at_transpile = false, stop_at_obj_compilation = false, + compile_exe = false, should_install = false, clean_build = false, source_mapping = true, show_changelog = false; -static OptionalText_t - show_codegen = NONE_TEXT, - cflags = Text("-Werror -fdollars-in-identifiers -std=c2x -Wno-trigraphs " - " -ffunction-sections -fdata-sections" - " -fno-signed-zeros -fno-finite-math-only " - " -D_XOPEN_SOURCE -D_DEFAULT_SOURCE -fPIC -ggdb" +static OptionalText_t show_codegen = NONE_TEXT, + cflags = Text("-Werror -fdollars-in-identifiers -std=c2x -Wno-trigraphs " + " -ffunction-sections -fdata-sections" + " -fno-signed-zeros -fno-finite-math-only " + " -D_XOPEN_SOURCE -D_DEFAULT_SOURCE -fPIC -ggdb" #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) - " -D_BSD_SOURCE" + " -D_BSD_SOURCE" #endif - " -DGC_THREADS" - " -I'" TOMO_PREFIX "/include' -I'" TOMO_PREFIX "/share/tomo_"TOMO_VERSION"/installed' -I/usr/local/include"), - ldlibs = Text("-lgc -lm -lgmp -lunistring -ltomo_"TOMO_VERSION), - ldflags = Text("-Wl,-rpath,'"TOMO_PREFIX"/lib',-rpath,/usr/local/lib" - " -L/usr/local/lib"), - optimization = Text("2"), - cc = Text(DEFAULT_C_COMPILER); + " -DGC_THREADS" + " -I'" TOMO_PREFIX "/include' -I'" TOMO_PREFIX "/share/tomo_" TOMO_VERSION + "/installed' -I/usr/local/include"), + ldlibs = Text("-lgc -lm -lgmp -lunistring -ltomo_" TOMO_VERSION), + ldflags = Text("-Wl,-rpath,'" TOMO_PREFIX "/lib',-rpath,/usr/local/lib" + " -L/usr/local/lib"), + optimization = Text("2"), cc = Text(DEFAULT_C_COMPILER); static Text_t config_summary, - // This will be either "" or "sudo -u <user>" or "doas -u <user>" - // to allow a command to put stuff into TOMO_PREFIX as the owner - // of that directory. - as_owner = Text(""); + // This will be either "" or "sudo -u <user>" or "doas -u <user>" + // to allow a command to put stuff into TOMO_PREFIX as the owner + // of that directory. + as_owner = Text(""); static void transpile_header(env_t *base_env, Path_t path); static void transpile_code(env_t *base_env, Path_t path); static void compile_object_file(Path_t path); -static Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t object_files, List_t extra_ldlibs); +static Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t object_files, + List_t extra_ldlibs); static void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_link); static void build_library(Path_t lib_dir); static void install_library(Path_t lib_dir); @@ -114,25 +113,22 @@ static void wait_for_child_success(pid_t child); static bool is_config_outdated(Path_t path); typedef struct { - bool h:1, c:1, o:1; + bool h : 1, c : 1, o : 1; } staleness_t; #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstack-protector" #endif -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { #ifdef __linux__ // Get the file modification time of the compiler, so we // can recompile files after changing the compiler: char compiler_path[PATH_MAX]; ssize_t count = readlink("/proc/self/exe", compiler_path, PATH_MAX); - if (count == -1) - err(1, "Could not find age of compiler"); + if (count == -1) err(1, "Could not find age of compiler"); compiler_path[count] = '\0'; - if (stat(compiler_path, &compiler_stat) != 0) - err(1, "Could not find age of compiler"); + if (stat(compiler_path, &compiler_stat) != 0) err(1, "Could not find age of compiler"); #endif #ifdef __OpenBSD__ @@ -140,31 +136,32 @@ int main(int argc, char *argv[]) #endif USE_COLOR = getenv("COLOR") ? strcmp(getenv("COLOR"), "1") == 0 : isatty(STDOUT_FILENO); - if (getenv("NO_COLOR") && getenv("NO_COLOR")[0] != '\0') - USE_COLOR = false; + if (getenv("NO_COLOR") && getenv("NO_COLOR")[0] != '\0') USE_COLOR = false; #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) arc4random_buf(TOMO_HASH_KEY, sizeof(TOMO_HASH_KEY)); #elif defined(__linux__) assert(getrandom(TOMO_HASH_KEY, sizeof(TOMO_HASH_KEY), 0) == sizeof(TOMO_HASH_KEY)); #else - #error "Unsupported platform for secure random number generation" +#error "Unsupported platform for secure random number generation" #endif // Set up environment variables: const char *PATH = getenv("PATH"); - setenv("PATH", PATH ? String(TOMO_PREFIX"/bin:", PATH) : TOMO_PREFIX"/bin", 1); + setenv("PATH", PATH ? String(TOMO_PREFIX "/bin:", PATH) : TOMO_PREFIX "/bin", 1); const char *LD_LIBRARY_PATH = getenv("LD_LIBRARY_PATH"); - setenv("LD_LIBRARY_PATH", LD_LIBRARY_PATH ? String(TOMO_PREFIX"/lib:", LD_LIBRARY_PATH) : TOMO_PREFIX"/lib", 1); + setenv("LD_LIBRARY_PATH", LD_LIBRARY_PATH ? String(TOMO_PREFIX "/lib:", LD_LIBRARY_PATH) : TOMO_PREFIX "/lib", 1); const char *LIBRARY_PATH = getenv("LIBRARY_PATH"); - setenv("LIBRARY_PATH", LIBRARY_PATH ? String(TOMO_PREFIX"/lib:", LIBRARY_PATH) : TOMO_PREFIX"/lib", 1); + setenv("LIBRARY_PATH", LIBRARY_PATH ? String(TOMO_PREFIX "/lib:", LIBRARY_PATH) : TOMO_PREFIX "/lib", 1); const char *C_INCLUDE_PATH = getenv("C_INCLUDE_PATH"); - setenv("C_INCLUDE_PATH", C_INCLUDE_PATH ? String(TOMO_PREFIX"/include:", C_INCLUDE_PATH) : TOMO_PREFIX"/include", 1); + setenv("C_INCLUDE_PATH", C_INCLUDE_PATH ? String(TOMO_PREFIX "/include:", C_INCLUDE_PATH) : TOMO_PREFIX "/include", + 1); // Run a tool: if ((streq(argv[1], "-r") || streq(argv[1], "--run")) && argc >= 3) { if (strcspn(argv[2], "/;$") == strlen(argv[2])) { - const char *program = String("'"TOMO_PREFIX"'/share/tomo_"TOMO_VERSION"/installed/", argv[2], "/", argv[2]); + const char *program = + String("'" TOMO_PREFIX "'/share/tomo_" TOMO_VERSION "/installed/", argv[2], "/", argv[2]); execv(program, &argv[2]); } print_err("This is not an installed tomo program: ", argv[2]); @@ -183,43 +180,25 @@ int main(int argc, char *argv[]) " --parse|-p: show parse tree\n" " --install|-I: install the executable or library\n" " --optimization|-O <level>: set optimization level\n" - " --run|-r: run a program from " TOMO_PREFIX "/share/tomo_"TOMO_VERSION"/installed\n" - ); + " --run|-r: run a program from " TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed\n"); Text_t help = Texts(Text("\x1b[1mtomo\x1b[m: a compiler for the Tomo programming language"), Text("\n\n"), usage); tomo_parse_args( - argc, argv, usage, help, TOMO_VERSION, - {"files", true, List$info(&Path$info), &files}, - {"args", true, List$info(&Text$info), &args}, - {"verbose", false, &Bool$info, &verbose}, - {"v", false, &Bool$info, &verbose}, - {"version", false, &Bool$info, &show_version}, - {"parse", false, &Bool$info, &show_parse_tree}, - {"p", false, &Bool$info, &show_parse_tree}, - {"prefix", false, &Bool$info, &show_prefix}, - {"quiet", false, &Bool$info, &quiet}, - {"q", false, &Bool$info, &quiet}, - {"transpile", false, &Bool$info, &stop_at_transpile}, - {"t", false, &Bool$info, &stop_at_transpile}, - {"compile-obj", false, &Bool$info, &stop_at_obj_compilation}, - {"c", false, &Bool$info, &stop_at_obj_compilation}, - {"compile-exe", false, &Bool$info, &compile_exe}, - {"e", false, &Bool$info, &compile_exe}, - {"uninstall", false, List$info(&Text$info), &uninstall}, - {"u", false, List$info(&Text$info), &uninstall}, - {"library", false, List$info(&Path$info), &libraries}, - {"L", false, List$info(&Path$info), &libraries}, - {"show-codegen", false, &Text$info, &show_codegen}, - {"C", false, &Text$info, &show_codegen}, - {"install", false, &Bool$info, &should_install}, - {"I", false, &Bool$info, &should_install}, - {"optimization", false, &Text$info, &optimization}, - {"O", false, &Text$info, &optimization}, - {"force-rebuild", false, &Bool$info, &clean_build}, - {"f", false, &Bool$info, &clean_build}, - {"source-mapping", false, &Bool$info, &source_mapping}, - {"m", false, &Bool$info, &source_mapping}, - {"changelog", false, &Bool$info, &show_changelog}, - ); + argc, argv, usage, help, TOMO_VERSION, {"files", true, List$info(&Path$info), &files}, + {"args", true, List$info(&Text$info), &args}, {"verbose", false, &Bool$info, &verbose}, + {"v", false, &Bool$info, &verbose}, {"version", false, &Bool$info, &show_version}, + {"parse", false, &Bool$info, &show_parse_tree}, {"p", false, &Bool$info, &show_parse_tree}, + {"prefix", false, &Bool$info, &show_prefix}, {"quiet", false, &Bool$info, &quiet}, + {"q", false, &Bool$info, &quiet}, {"transpile", false, &Bool$info, &stop_at_transpile}, + {"t", false, &Bool$info, &stop_at_transpile}, {"compile-obj", false, &Bool$info, &stop_at_obj_compilation}, + {"c", false, &Bool$info, &stop_at_obj_compilation}, {"compile-exe", false, &Bool$info, &compile_exe}, + {"e", false, &Bool$info, &compile_exe}, {"uninstall", false, List$info(&Text$info), &uninstall}, + {"u", false, List$info(&Text$info), &uninstall}, {"library", false, List$info(&Path$info), &libraries}, + {"L", false, List$info(&Path$info), &libraries}, {"show-codegen", false, &Text$info, &show_codegen}, + {"C", false, &Text$info, &show_codegen}, {"install", false, &Bool$info, &should_install}, + {"I", false, &Bool$info, &should_install}, {"optimization", false, &Text$info, &optimization}, + {"O", false, &Text$info, &optimization}, {"force-rebuild", false, &Bool$info, &clean_build}, + {"f", false, &Bool$info, &clean_build}, {"source-mapping", false, &Bool$info, &source_mapping}, + {"m", false, &Bool$info, &source_mapping}, {"changelog", false, &Bool$info, &show_changelog}, ); if (show_prefix) { print(TOMO_PREFIX); @@ -227,15 +206,13 @@ int main(int argc, char *argv[]) } if (show_changelog) { - print_inline(string_slice((const char*)CHANGES_md, CHANGES_md_len)); + print_inline(string_slice((const char *)CHANGES_md, CHANGES_md_len)); return 0; } if (show_version) { - if (verbose) - print(TOMO_VERSION, " ", GIT_VERSION); - else - print(TOMO_VERSION); + if (verbose) print(TOMO_VERSION, " ", GIT_VERSION); + else print(TOMO_VERSION); return 0; } @@ -256,25 +233,25 @@ int main(int argc, char *argv[]) #endif if (show_codegen.length > 0 && Text$equal_values(show_codegen, Text("pretty"))) - show_codegen = Text("{ sed '/^#line/d;/^$/d' | indent -o /dev/stdout | bat -l c -P; }"); + show_codegen = Text("{ sed '/^#line/d;/^$/d' | clang-format | bat -l c -P; }"); config_summary = Text$from_str(String(cc, " ", cflags, " -O", optimization)); Text_t owner = Path$owner(Path$from_str(TOMO_PREFIX), true); Text_t user = Text$from_str(getenv("USER")); if (!Text$equal_values(user, owner)) { - as_owner = Texts(Text(SUDO" -u "), owner, Text(" ")); + as_owner = Texts(Text(SUDO " -u "), owner, Text(" ")); } for (int64_t i = 0; i < uninstall.length; i++) { - Text_t *u = (Text_t*)(uninstall.data + i*uninstall.stride); - xsystem(as_owner, "rm -rvf '"TOMO_PREFIX"'/share/tomo_"TOMO_VERSION"/installed/", *u); + Text_t *u = (Text_t *)(uninstall.data + i * uninstall.stride); + xsystem(as_owner, "rm -rvf '" TOMO_PREFIX "'/share/tomo_" TOMO_VERSION "/installed/", *u); print("Uninstalled ", *u); } Path_t cwd = Path$current_dir(); for (int64_t i = 0; i < libraries.length; i++) { - Path_t *lib = (Path_t*)(libraries.data + i*libraries.stride); + Path_t *lib = (Path_t *)(libraries.data + i * libraries.stride); *lib = Path$resolved(*lib, cwd); // Fork a child process to build the library to prevent cross-contamination // of side effects when building one library from affecting another library. @@ -282,8 +259,7 @@ int main(int argc, char *argv[]) pid_t child = fork(); if (child == 0) { build_library(*lib); - if (should_install) - install_library(*lib); + if (should_install) install_library(*lib); _exit(0); } wait_for_child_success(child); @@ -301,22 +277,19 @@ int main(int argc, char *argv[]) // Convert `foo` to `foo/foo.tm` and resolve all paths to absolute paths: Path_t cur_dir = Path$current_dir(); for (int64_t i = 0; i < files.length; i++) { - Path_t *path = (Path_t*)(files.data + i*files.stride); - if (Path$is_directory(*path, true)) - *path = Path$child(*path, Texts(Path$base_name(*path), Text(".tm"))); + Path_t *path = (Path_t *)(files.data + i * files.stride); + if (Path$is_directory(*path, true)) *path = Path$child(*path, Texts(Path$base_name(*path), Text(".tm"))); *path = Path$resolved(*path, cur_dir); - if (!Path$exists(*path)) - fail("File not found: ", *path); + if (!Path$exists(*path)) fail("File not found: ", *path); } - if (files.length < 1) - print_err("No file specified!"); + if (files.length < 1) print_err("No file specified!"); quiet = !verbose; for (int64_t i = 0; i < files.length; i++) { - Path_t path = *(Path_t*)(files.data + i*files.stride); + Path_t path = *(Path_t *)(files.data + i * files.stride); if (show_parse_tree) { ast_t *ast = parse_file(Path$as_c_string(path), NULL); print(ast_to_sexp_str(ast)); @@ -324,23 +297,21 @@ int main(int argc, char *argv[]) } Path_t exe_path = compile_exe ? Path$with_extension(path, Text(""), true) - : build_file(Path$with_extension(path, Text(""), true), ""); + : build_file(Path$with_extension(path, Text(""), true), ""); pid_t child = fork(); if (child == 0) { env_t *env = global_env(source_mapping); - List_t object_files = {}, - extra_ldlibs = {}; + List_t object_files = {}, extra_ldlibs = {}; compile_files(env, files, &object_files, &extra_ldlibs); compile_executable(env, path, exe_path, object_files, extra_ldlibs); - if (compile_exe) - _exit(0); + if (compile_exe) _exit(0); char *prog_args[1 + args.length + 1]; - prog_args[0] = (char*)Path$as_c_string(exe_path); + prog_args[0] = (char *)Path$as_c_string(exe_path); for (int64_t j = 0; j < args.length; j++) - prog_args[j + 1] = Text$as_c_string(*(Text_t*)(args.data + j*args.stride)); + prog_args[j + 1] = Text$as_c_string(*(Text_t *)(args.data + j * args.stride)); prog_args[1 + args.length] = NULL; execv(prog_args[0], prog_args); print_err("Could not execute program: ", prog_args[0]); @@ -351,9 +322,9 @@ int main(int argc, char *argv[]) if (compile_exe && should_install) { for (int64_t i = 0; i < files.length; i++) { - Path_t path = *(Path_t*)(files.data + i*files.stride); + Path_t path = *(Path_t *)(files.data + i * files.stride); Path_t exe = Path$with_extension(path, Text(""), true); - xsystem(as_owner, "cp -v '", exe, "' '"TOMO_PREFIX"'/bin/"); + xsystem(as_owner, "cp -v '", exe, "' '" TOMO_PREFIX "'/bin/"); } } return 0; @@ -362,14 +333,11 @@ int main(int argc, char *argv[]) #pragma GCC diagnostic pop #endif -void wait_for_child_success(pid_t child) -{ +void wait_for_child_success(pid_t child) { int status; while (waitpid(child, &status, 0) < 0 && errno == EINTR) { - if (WIFEXITED(status) || WIFSIGNALED(status)) - break; - else if (WIFSTOPPED(status)) - kill(child, SIGCONT); + if (WIFEXITED(status) || WIFSIGNALED(status)) break; + else if (WIFSTOPPED(status)) kill(child, SIGCONT); } if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { @@ -377,18 +345,15 @@ void wait_for_child_success(pid_t child) } } -Path_t build_file(Path_t path, const char *extension) -{ +Path_t build_file(Path_t path, const char *extension) { Path_t build_dir = Path$sibling(path, Text(".build")); if (mkdir(Path$as_c_string(build_dir), 0755) != 0) { - if (!Path$is_directory(build_dir, true)) - err(1, "Could not make .build directory"); + if (!Path$is_directory(build_dir, true)) err(1, "Could not make .build directory"); } return Path$child(build_dir, Texts(Path$base_name(path), Text$from_str(extension))); } -static const char *get_version(Path_t lib_dir) -{ +static const char *get_version(Path_t lib_dir) { Path_t changes_file = Path$child(lib_dir, Text("CHANGES.md")); OptionalText_t changes = Path$read(changes_file); if (changes.length <= 0) { @@ -401,22 +366,16 @@ static const char *get_version(Path_t lib_dir) return String(string_slice(version_line + 4, strcspn(version_line + 4, "\r\n"))); } -static Text_t get_version_suffix(Path_t lib_dir) -{ - return Texts(Text("_"), Text$from_str(get_version(lib_dir))); -} +static Text_t get_version_suffix(Path_t lib_dir) { return Texts(Text("_"), Text$from_str(get_version(lib_dir))); } -void build_library(Path_t lib_dir) -{ +void build_library(Path_t lib_dir) { lib_dir = Path$resolved(lib_dir, Path$current_dir()); - if (!Path$is_directory(lib_dir, true)) - print_err("Not a valid directory: ", lib_dir); + if (!Path$is_directory(lib_dir, true)) print_err("Not a valid directory: ", lib_dir); Text_t lib_dir_name = Path$base_name(lib_dir); List_t tm_files = Path$glob(Path$child(lib_dir, Text("[!._0-9]*.tm"))); env_t *env = fresh_scope(global_env(source_mapping)); - List_t object_files = {}, - extra_ldlibs = {}; + List_t object_files = {}, extra_ldlibs = {}; compile_files(env, tm_files, &object_files, &extra_ldlibs); @@ -429,27 +388,27 @@ void build_library(Path_t lib_dir) FILE *prog = run_cmd(cc, " -O", optimization, " ", cflags, " ", ldflags, " ", ldlibs, " ", list_text(extra_ldlibs), #ifdef __APPLE__ - " -Wl,-install_name,@rpath/'lib", lib_dir_name, version_suffix, SHARED_SUFFIX, "'" + " -Wl,-install_name,@rpath/'lib", lib_dir_name, version_suffix, SHARED_SUFFIX, + "'" #else - " -Wl,-soname,'lib", lib_dir_name, version_suffix, SHARED_SUFFIX, "'" + " -Wl,-soname,'lib", lib_dir_name, version_suffix, SHARED_SUFFIX, + "'" #endif - " -shared ", paths_str(object_files), " -o '", shared_lib, "'"); + " -shared ", + paths_str(object_files), " -o '", shared_lib, "'"); - if (!prog) - print_err("Failed to run C compiler: ", cc); + if (!prog) print_err("Failed to run C compiler: ", cc); int status = pclose(prog); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - exit(EXIT_FAILURE); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) exit(EXIT_FAILURE); - if (!quiet) - print("Compiled library:\t", shared_lib); + if (!quiet) print("Compiled library:\t", shared_lib); } -void install_library(Path_t lib_dir) -{ +void install_library(Path_t lib_dir) { Text_t lib_dir_name = Path$base_name(lib_dir); Text_t version_suffix = get_version_suffix(lib_dir); - Path_t dest = Path$child(Path$from_str(TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed"), Texts(lib_dir_name, version_suffix)); + Path_t dest = Path$child(Path$from_str(TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed"), + Texts(lib_dir_name, version_suffix)); if (!Path$equal_values(lib_dir, dest)) { if (verbose) whisper("Clearing out any pre-existing version of ", lib_dir_name); xsystem(as_owner, "rm -rf '", dest, "'"); @@ -461,25 +420,26 @@ void install_library(Path_t lib_dir) // If we have `debugedit` on this system, use it to remap the debugging source information // to point to the installed version of the source file. Otherwise, fail silently. if (verbose) whisper("Updating debug symbols for ", dest, "/lib", lib_dir_name, SHARED_SUFFIX); - int result = system(String(as_owner, "debugedit -b ", lib_dir, - " -d '", dest, "'" - " '", dest, "/lib", lib_dir_name, version_suffix, SHARED_SUFFIX, "' " + int result = system(String(as_owner, "debugedit -b ", lib_dir, " -d '", dest, + "'" + " '", + dest, "/lib", lib_dir_name, version_suffix, SHARED_SUFFIX, + "' " ">/dev/null 2>/dev/null")); (void)result; - print("Installed \033[1m", lib_dir_name, "\033[m to "TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed/", lib_dir_name, version_suffix); + print("Installed \033[1m", lib_dir_name, "\033[m to " TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed/", + lib_dir_name, version_suffix); } -void compile_files(env_t *env, List_t to_compile, List_t *object_files, List_t *extra_ldlibs) -{ +void compile_files(env_t *env, List_t to_compile, List_t *object_files, List_t *extra_ldlibs) { Table_t to_link = {}; Table_t dependency_files = {}; for (int64_t i = 0; i < to_compile.length; i++) { - Path_t filename = *(Path_t*)(to_compile.data + i*to_compile.stride); + Path_t filename = *(Path_t *)(to_compile.data + i * to_compile.stride); Text_t extension = Path$extension(filename, true); if (!Text$equal_values(extension, Text("tm"))) print_err("Not a valid .tm file: \x1b[31;1m", filename, "\x1b[m"); - if (!Path$is_file(filename, true)) - print_err("Couldn't find file: ", filename); + if (!Path$is_file(filename, true)) print_err("Couldn't find file: ", filename); build_file_dependency_graph(filename, &dependency_files, &to_link); } @@ -488,14 +448,14 @@ void compile_files(env_t *env, List_t to_compile, List_t *object_files, List_t * struct { Path_t filename; staleness_t staleness; - } *entry = (dependency_files.entries.data + i*dependency_files.entries.stride); + } *entry = (dependency_files.entries.data + i * dependency_files.entries.stride); Path_t id_file = build_file(entry->filename, ".id"); if (!Path$exists(id_file)) { static const char id_chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - char id_str[8]; + char id_str[8]; for (int j = 0; j < (int)sizeof(id_str); j++) { - id_str[j] = id_chars[random_range(0, sizeof(id_chars)-1)]; + id_str[j] = id_chars[random_range(0, sizeof(id_chars) - 1)]; } Text_t filename_id = Text(""); Text_t base = Path$base_name(entry->filename); @@ -516,19 +476,18 @@ void compile_files(env_t *env, List_t to_compile, List_t *object_files, List_t * struct { Path_t filename; staleness_t staleness; - } *entry = (dependency_files.entries.data + i*dependency_files.entries.stride); + } *entry = (dependency_files.entries.data + i * dependency_files.entries.stride); if (entry->staleness.h || clean_build) { transpile_header(env, entry->filename); entry->staleness.o = true; } else { if (verbose) whisper("Unchanged: ", build_file(entry->filename, ".h")); - if (show_codegen.length > 0) - xsystem(show_codegen, " <", build_file(entry->filename, ".h")); + if (show_codegen.length > 0) xsystem(show_codegen, " <", build_file(entry->filename, ".h")); } } - env->imports = new(Table_t); + env->imports = new (Table_t); struct child_s { struct child_s *next; @@ -541,26 +500,23 @@ void compile_files(env_t *env, List_t to_compile, List_t *object_files, List_t * struct { Path_t filename; staleness_t staleness; - } *entry = (dependency_files.entries.data + i*dependency_files.entries.stride); + } *entry = (dependency_files.entries.data + i * dependency_files.entries.stride); if (!clean_build && !entry->staleness.c && !entry->staleness.h && !entry->staleness.o && !is_config_outdated(entry->filename)) { if (verbose) whisper("Unchanged: ", build_file(entry->filename, ".c")); - if (show_codegen.length > 0) - xsystem(show_codegen, " <", build_file(entry->filename, ".c")); + if (show_codegen.length > 0) xsystem(show_codegen, " <", build_file(entry->filename, ".c")); if (verbose) whisper("Unchanged: ", build_file(entry->filename, ".o")); continue; } pid_t pid = fork(); if (pid == 0) { - if (clean_build || entry->staleness.c) - transpile_code(env, entry->filename); + if (clean_build || entry->staleness.c) transpile_code(env, entry->filename); else if (verbose) whisper("Unchanged: ", build_file(entry->filename, ".c")); - if (!stop_at_transpile) - compile_object_file(entry->filename); + if (!stop_at_transpile) compile_object_file(entry->filename); _exit(EXIT_SUCCESS); } - child_processes = new(struct child_s, .next=child_processes, .pid=pid); + child_processes = new (struct child_s, .next = child_processes, .pid = pid); } for (; child_processes; child_processes = child_processes->next) @@ -571,7 +527,7 @@ void compile_files(env_t *env, List_t to_compile, List_t *object_files, List_t * struct { Path_t filename; staleness_t staleness; - } *entry = (dependency_files.entries.data + i*dependency_files.entries.stride); + } *entry = (dependency_files.entries.data + i * dependency_files.entries.stride); Path_t path = entry->filename; path = build_file(path, ".o"); List$insert(object_files, &path, I(0), sizeof(Path_t)); @@ -579,44 +535,39 @@ void compile_files(env_t *env, List_t to_compile, List_t *object_files, List_t * } if (extra_ldlibs) { for (int64_t i = 0; i < to_link.entries.length; i++) { - Text_t lib = *(Text_t*)(to_link.entries.data + i*to_link.entries.stride); + Text_t lib = *(Text_t *)(to_link.entries.data + i * to_link.entries.stride); List$insert(extra_ldlibs, &lib, I(0), sizeof(Text_t)); } } } -bool is_config_outdated(Path_t path) -{ +bool is_config_outdated(Path_t path) { OptionalText_t config = Path$read(build_file(path, ".config")); if (config.length < 0) return true; return !Text$equal_values(config, config_summary); } -void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_link) -{ - if (Table$has_value(*to_compile, path, Table$info(&Path$info, &Byte$info))) - return; +void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_link) { + if (Table$has_value(*to_compile, path, Table$info(&Path$info, &Byte$info))) return; staleness_t staleness = { - .h=is_stale(build_file(path, ".h"), Path$sibling(path, Text("modules.ini")), true) - || is_stale(build_file(path, ".h"), build_file(path, ":modules.ini"), true) - || is_stale(build_file(path, ".h"), path, false) - || is_stale(build_file(path, ".h"), build_file(path, ".id"), false), - .c=is_stale(build_file(path, ".c"), Path$sibling(path, Text("modules.ini")), true) - || is_stale(build_file(path, ".c"), build_file(path, ":modules.ini"), true) - || is_stale(build_file(path, ".c"), path, false) - || is_stale(build_file(path, ".c"), build_file(path, ".id"), false), + .h = is_stale(build_file(path, ".h"), Path$sibling(path, Text("modules.ini")), true) + || is_stale(build_file(path, ".h"), build_file(path, ":modules.ini"), true) + || is_stale(build_file(path, ".h"), path, false) + || is_stale(build_file(path, ".h"), build_file(path, ".id"), false), + .c = is_stale(build_file(path, ".c"), Path$sibling(path, Text("modules.ini")), true) + || is_stale(build_file(path, ".c"), build_file(path, ":modules.ini"), true) + || is_stale(build_file(path, ".c"), path, false) + || is_stale(build_file(path, ".c"), build_file(path, ".id"), false), }; - staleness.o = staleness.c || staleness.h - || is_stale(build_file(path, ".o"), build_file(path, ".c"), false) - || is_stale(build_file(path, ".o"), build_file(path, ".h"), false); + staleness.o = staleness.c || staleness.h || is_stale(build_file(path, ".o"), build_file(path, ".c"), false) + || is_stale(build_file(path, ".o"), build_file(path, ".h"), false); Table$set(to_compile, &path, &staleness, Table$info(&Path$info, &Byte$info)); assert(Text$equal_values(Path$extension(path, true), Text("tm"))); ast_t *ast = parse_file(Path$as_c_string(path), NULL); - if (!ast) - print_err("Could not parse file: ", path); + if (!ast) print_err("Could not parse file: ", path); for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) { ast_t *stmt_ast = stmt->ast; @@ -626,14 +577,10 @@ void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_l switch (use->what) { case USE_LOCAL: { Path_t dep_tm = Path$resolved(Path$from_str(use->path), Path$parent(path)); - if (!Path$is_file(dep_tm, true)) - code_err(stmt_ast, "Not a valid file: ", dep_tm); - if (is_stale(build_file(path, ".h"), dep_tm, false)) - staleness.h = true; - if (is_stale(build_file(path, ".c"), dep_tm, false)) - staleness.c = true; - if (staleness.c || staleness.h) - staleness.o = true; + if (!Path$is_file(dep_tm, true)) code_err(stmt_ast, "Not a valid file: ", dep_tm); + if (is_stale(build_file(path, ".h"), dep_tm, false)) staleness.h = true; + if (is_stale(build_file(path, ".c"), dep_tm, false)) staleness.c = true; + if (staleness.c || staleness.h) staleness.o = true; Table$set(to_compile, &path, &staleness, Table$info(&Path$info, &Byte$info)); build_file_dependency_graph(dep_tm, to_compile, to_link); break; @@ -641,16 +588,17 @@ void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_l case USE_MODULE: { module_info_t mod = get_module_info(stmt_ast); const char *full_name = mod.version ? String(mod.name, "_", mod.version) : mod.name; - Text_t lib = Texts(Text("-Wl,-rpath,'"), - Text(TOMO_PREFIX "/share/tomo_"TOMO_VERSION"/installed/"), Text$from_str(full_name), - Text("' '" TOMO_PREFIX "/share/tomo_"TOMO_VERSION"/installed/"), - Text$from_str(full_name), Text("/lib"), Text$from_str(full_name), Text(SHARED_SUFFIX "'")); + Text_t lib = + Texts(Text("-Wl,-rpath,'"), Text(TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed/"), + Text$from_str(full_name), Text("' '" TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed/"), + Text$from_str(full_name), Text("/lib"), Text$from_str(full_name), Text(SHARED_SUFFIX "'")); Table$set(to_link, &lib, NULL, Table$info(&Text$info, &Void$info)); - List_t children = Path$glob(Path$from_str(String(TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed/", full_name, "/[!._0-9]*.tm"))); + List_t children = Path$glob(Path$from_str( + String(TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed/", full_name, "/[!._0-9]*.tm"))); for (int64_t i = 0; i < children.length; i++) { - Path_t *child = (Path_t*)(children.data + i*children.stride); - Table_t discarded = {.fallback=to_compile}; + Path_t *child = (Path_t *)(children.data + i * children.stride); + Table_t discarded = {.fallback = to_compile}; build_file_dependency_graph(*child, &discarded, to_link); } break; @@ -671,9 +619,9 @@ void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_l } break; } - case USE_HEADER: case USE_C_CODE: { - if (use->path[0] == '<') - break; + case USE_HEADER: + case USE_C_CODE: { + if (use->path[0] == '<') break; Path_t dep_path = Path$resolved(Path$from_str(use->path), Path$parent(path)); if (is_stale(build_file(path, ".o"), dep_path, false)) { @@ -687,38 +635,36 @@ void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_l } } -time_t latest_included_modification_time(Path_t path) -{ +time_t latest_included_modification_time(Path_t path) { static Table_t c_modification_times = {}; - const TypeInfo_t time_info = {.size=sizeof(time_t), .align=__alignof__(time_t), .tag=OpaqueInfo}; + const TypeInfo_t time_info = {.size = sizeof(time_t), .align = __alignof__(time_t), .tag = OpaqueInfo}; time_t *cached_latest = Table$get(c_modification_times, &path, Table$info(&Path$info, &time_info)); if (cached_latest) return *cached_latest; struct stat s; time_t latest = 0; - if (stat(Path$as_c_string(path), &s) == 0) - latest = s.st_mtime; + if (stat(Path$as_c_string(path), &s) == 0) latest = s.st_mtime; Table$set(&c_modification_times, &path, &latest, Table$info(&Path$info, &time_info)); OptionalClosure_t by_line = Path$by_line(path); if (by_line.fn == NULL) return 0; - OptionalText_t (*next_line)(void*) = by_line.fn; + OptionalText_t (*next_line)(void *) = by_line.fn; Path_t parent = Path$parent(path); bool allow_dot_include = Path$has_extension(path, Text("s")) || Path$has_extension(path, Text("S")); - for (Text_t line; (line=next_line(by_line.userdata)).length >= 0; ) { + for (Text_t line; (line = next_line(by_line.userdata)).length >= 0;) { line = Text$trim(line, Text(" \t"), true, false); - if (!Text$starts_with(line, Text("#include"), NULL) && !(allow_dot_include && Text$starts_with(line, Text(".include"), NULL))) + if (!Text$starts_with(line, Text("#include"), NULL) + && !(allow_dot_include && Text$starts_with(line, Text(".include"), NULL))) continue; // Check for `"` after `#include` or `.include` and some spaces: - if (!Text$starts_with(Text$trim(Text$from(line, I(9)), Text(" \t"), true, false), Text("\""), NULL)) - continue; + if (!Text$starts_with(Text$trim(Text$from(line, I(9)), Text(" \t"), true, false), Text("\""), NULL)) continue; List_t chunks = Text$split(line, Text("\"")); if (chunks.length < 3) // Should be `#include "foo" ...` -> ["#include ", "foo", "..."] continue; - Text_t included = *(Text_t*)(chunks.data + 1*chunks.stride); + Text_t included = *(Text_t *)(chunks.data + 1 * chunks.stride); Path_t included_path = Path$resolved(Path$from_text(included), parent); time_t included_time = latest_included_modification_time(included_path); if (included_time > latest) { @@ -729,8 +675,7 @@ time_t latest_included_modification_time(Path_t path) return latest; } -bool is_stale(Path_t path, Path_t relative_to, bool ignore_missing) -{ +bool is_stale(Path_t path, Path_t relative_to, bool ignore_missing) { struct stat target_stat; if (stat(Path$as_c_string(path), &target_stat) != 0) { if (ignore_missing) return false; @@ -739,8 +684,7 @@ bool is_stale(Path_t path, Path_t relative_to, bool ignore_missing) #ifdef __linux__ // Any file older than the compiler is stale: - if (target_stat.st_mtime < compiler_stat.st_mtime) - return true; + if (target_stat.st_mtime < compiler_stat.st_mtime) return true; #endif if (Path$has_extension(relative_to, Text("c")) || Path$has_extension(relative_to, Text("h")) @@ -757,55 +701,44 @@ bool is_stale(Path_t path, Path_t relative_to, bool ignore_missing) return target_stat.st_mtime < relative_to_stat.st_mtime; } -bool is_stale_for_any(Path_t path, List_t relative_to, bool ignore_missing) -{ +bool is_stale_for_any(Path_t path, List_t relative_to, bool ignore_missing) { for (int64_t i = 0; i < relative_to.length; i++) { - Path_t r = *(Path_t*)(relative_to.data + i*relative_to.stride); - if (is_stale(path, r, ignore_missing)) - return true; + Path_t r = *(Path_t *)(relative_to.data + i * relative_to.stride); + if (is_stale(path, r, ignore_missing)) return true; } return false; } -void transpile_header(env_t *base_env, Path_t path) -{ +void transpile_header(env_t *base_env, Path_t path) { Path_t h_filename = build_file(path, ".h"); ast_t *ast = parse_file(Path$as_c_string(path), NULL); - if (!ast) - print_err("Could not parse file: ", path); + if (!ast) print_err("Could not parse file: ", path); env_t *module_env = load_module_env(base_env, ast); Text_t h_code = compile_file_header(module_env, Path$resolved(h_filename, Path$from_str(".")), ast); FILE *header = fopen(Path$as_c_string(h_filename), "w"); - if (!header) - print_err("Failed to open header file: ", h_filename); + if (!header) print_err("Failed to open header file: ", h_filename); Text$print(header, h_code); - if (fclose(header) == -1) - print_err("Failed to write header file: ", h_filename); + if (fclose(header) == -1) print_err("Failed to write header file: ", h_filename); - if (!quiet) - print("Transpiled header:\t", h_filename); + if (!quiet) print("Transpiled header:\t", h_filename); - if (show_codegen.length > 0) - xsystem(show_codegen, " <", h_filename); + if (show_codegen.length > 0) xsystem(show_codegen, " <", h_filename); } -void transpile_code(env_t *base_env, Path_t path) -{ +void transpile_code(env_t *base_env, Path_t path) { Path_t c_filename = build_file(path, ".c"); ast_t *ast = parse_file(Path$as_c_string(path), NULL); - if (!ast) - print_err("Could not parse file: ", path); + if (!ast) print_err("Could not parse file: ", path); env_t *module_env = load_module_env(base_env, ast); Text_t c_code = compile_file(module_env, ast); FILE *c_file = fopen(Path$as_c_string(c_filename), "w"); - if (!c_file) - print_err("Failed to write C file: ", c_filename); + if (!c_file) print_err("Failed to write C file: ", c_filename); Text$print(c_file, c_code); @@ -814,54 +747,43 @@ void transpile_code(env_t *base_env, Path_t path) if (main_binding && main_binding->type->tag == FunctionType) { type_t *ret = Match(main_binding->type, FunctionType)->ret; if (ret->tag != VoidType && ret->tag != AbortType) - compiler_err(ast->file, ast->start, ast->end, - "The main() function in this file has a return type of ", type_to_str(ret), - ", but it should not have any return value!"); + compiler_err(ast->file, ast->start, ast->end, "The main() function in this file has a return type of ", + type_to_str(ret), ", but it should not have any return value!"); - Text$print(c_file, Texts( - "int parse_and_run$$", main_binding->code, "(int argc, char *argv[]) {\n", - module_env->do_source_mapping ? Text("#line 1\n") : EMPTY_TEXT, - "tomo_init();\n", - namespace_name(module_env, module_env->namespace, Text("$initialize")), "();\n" - "\n", - compile_cli_arg_call(module_env, main_binding->code, main_binding->type, version), - "return 0;\n" - "}\n")); + Text$print(c_file, Texts("int parse_and_run$$", main_binding->code, "(int argc, char *argv[]) {\n", + module_env->do_source_mapping ? Text("#line 1\n") : EMPTY_TEXT, "tomo_init();\n", + namespace_name(module_env, module_env->namespace, Text("$initialize")), + "();\n" + "\n", + compile_cli_arg_call(module_env, main_binding->code, main_binding->type, version), + "return 0;\n" + "}\n")); } - if (fclose(c_file) == -1) - print_err("Failed to output C code to ", c_filename); + if (fclose(c_file) == -1) print_err("Failed to output C code to ", c_filename); - if (!quiet) - print("Transpiled code:\t", c_filename); + if (!quiet) print("Transpiled code:\t", c_filename); - if (show_codegen.length > 0) - xsystem(show_codegen, " <", c_filename); + if (show_codegen.length > 0) xsystem(show_codegen, " <", c_filename); } -void compile_object_file(Path_t path) -{ +void compile_object_file(Path_t path) { Path_t obj_file = build_file(path, ".o"); Path_t c_file = build_file(path, ".c"); FILE *prog = run_cmd(cc, " ", cflags, " -O", optimization, " -c ", c_file, " -o ", obj_file); - if (!prog) - print_err("Failed to run C compiler: ", cc); + if (!prog) print_err("Failed to run C compiler: ", cc); int status = pclose(prog); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - exit(EXIT_FAILURE); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) exit(EXIT_FAILURE); Path$write(build_file(path, ".config"), config_summary, 0644); - if (!quiet) - print("Compiled object:\t", obj_file); + if (!quiet) print("Compiled object:\t", obj_file); } -Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t object_files, List_t extra_ldlibs) -{ +Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t object_files, List_t extra_ldlibs) { ast_t *ast = parse_file(Path$as_c_string(path), NULL); - if (!ast) - print_err("Could not parse file ", path); + if (!ast) print_err("Could not parse file ", path); env_t *env = load_module_env(base_env, ast); binding_t *main_binding = get_binding(env, "main"); if (!main_binding || main_binding->type->tag != FunctionType) @@ -875,15 +797,16 @@ Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t return exe_path; } - FILE *runner = run_cmd(cc, " ", cflags, " -O", optimization, " ", ldflags, " ", ldlibs, " ", list_text(extra_ldlibs), " ", - paths_str(object_files), " -x c - -o ", exe_path); - Text_t program = Texts( - "extern int parse_and_run$$", main_binding->code, "(int argc, char *argv[]);\n" - "__attribute__ ((noinline))\n" - "int main(int argc, char *argv[]) {\n" - "\treturn parse_and_run$$", main_binding->code, "(argc, argv);\n" - "}\n" - ); + FILE *runner = run_cmd(cc, " ", cflags, " -O", optimization, " ", ldflags, " ", ldlibs, " ", + list_text(extra_ldlibs), " ", paths_str(object_files), " -x c - -o ", exe_path); + Text_t program = Texts("extern int parse_and_run$$", main_binding->code, + "(int argc, char *argv[]);\n" + "__attribute__ ((noinline))\n" + "int main(int argc, char *argv[]) {\n" + "\treturn parse_and_run$$", + main_binding->code, + "(argc, argv);\n" + "}\n"); if (show_codegen.length > 0) { FILE *out = run_cmd(show_codegen); @@ -893,11 +816,9 @@ Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t Text$print(runner, program); int status = pclose(runner); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) - exit(EXIT_FAILURE); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) exit(EXIT_FAILURE); - if (!quiet) - print("Compiled executable:\t", exe_path); + if (!quiet) print("Compiled executable:\t", exe_path); return exe_path; } diff --git a/src/typecheck.c b/src/typecheck.c index 19d6c09f..680d3049 100644 --- a/src/typecheck.c +++ b/src/typecheck.c @@ -20,8 +20,7 @@ #include "typecheck.h" #include "types.h" -type_t *parse_type_ast(env_t *env, type_ast_t *ast) -{ +type_t *parse_type_ast(env_t *env, type_ast_t *ast) { #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch-default" @@ -51,7 +50,7 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast) type_t *pointed_t = parse_type_ast(env, ptr->pointed); if (pointed_t->tag == VoidType) code_err(ast, "Void pointers are not supported. You probably meant 'Memory' instead of 'Void'"); - return Type(PointerType, .pointed=pointed_t, .is_stack=ptr->is_stack); + return Type(PointerType, .pointed = pointed_t, .is_stack = ptr->is_stack); } case ListTypeAST: { type_ast_t *item_type = Match(ast, ListTypeAST)->item; @@ -61,8 +60,9 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast) code_err(item_type, "Lists can't have stack references because the list may outlive the stack frame."); if (type_size(item_t) > LIST_MAX_STRIDE) code_err(ast, "This list holds items that take up ", (uint64_t)type_size(item_t), - " bytes, but the maximum supported size is ", (int64_t)LIST_MAX_STRIDE, " bytes. Consider using a list of pointers instead."); - return Type(ListType, .item_type=item_t); + " bytes, but the maximum supported size is ", (int64_t)LIST_MAX_STRIDE, + " bytes. Consider using a list of pointers instead."); + return Type(ListType, .item_type = item_t); } case SetTypeAST: { type_ast_t *item_type = Match(ast, SetTypeAST)->item; @@ -72,8 +72,9 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast) code_err(item_type, "Sets can't have stack references because the list may outlive the stack frame."); if (type_size(item_t) > LIST_MAX_STRIDE) code_err(ast, "This set holds items that take up ", (uint64_t)type_size(item_t), - " bytes, but the maximum supported size is ", (int64_t)LIST_MAX_STRIDE, " bytes. Consider using an set of pointers instead."); - return Type(SetType, .item_type=item_t); + " bytes, but the maximum supported size is ", (int64_t)LIST_MAX_STRIDE, + " bytes. Consider using an set of pointers instead."); + return Type(SetType, .item_type = item_t); } case TableTypeAST: { DeclareMatch(table_type, ast, TableTypeAST); @@ -86,39 +87,38 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast) type_t *val_type = parse_type_ast(env, table_type->value); if (!val_type) code_err(table_type->value, "I can't figure out what type this is."); if (has_stack_memory(val_type)) - code_err(table_type->value, "Tables can't have stack references because the list may outlive the stack frame."); + code_err(table_type->value, + "Tables can't have stack references because the list may outlive the stack frame."); else if (val_type->tag == OptionalType) code_err(ast, "Tables with optional-typed values are not currently supported"); - return Type(TableType, .key_type=key_type, .value_type=val_type, .env=env, .default_value=table_type->default_value); + return Type(TableType, .key_type = key_type, .value_type = val_type, .env = env, + .default_value = table_type->default_value); } case FunctionTypeAST: { DeclareMatch(fn, ast, FunctionTypeAST); type_t *ret_t = fn->ret ? parse_type_ast(env, fn->ret) : Type(VoidType); if (has_stack_memory(ret_t)) - code_err(fn->ret, "Functions are not allowed to return stack references, because the reference may no longer exist on the stack."); + code_err(fn->ret, "Functions are not allowed to return stack references, because the reference may no " + "longer exist on the stack."); arg_t *type_args = NULL; for (arg_ast_t *arg = fn->args; arg; arg = arg->next) { - type_args = new(arg_t, .name=arg->name, .next=type_args); - if (arg->type) - type_args->type = parse_type_ast(env, arg->type); - else if (arg->value) - type_args->type = get_type(env, arg->value); + type_args = new (arg_t, .name = arg->name, .next = type_args); + if (arg->type) type_args->type = parse_type_ast(env, arg->type); + else if (arg->value) type_args->type = get_type(env, arg->value); - if (arg->value) - type_args->default_val = arg->value; + if (arg->value) type_args->default_val = arg->value; } REVERSE_LIST(type_args); - return Type(ClosureType, Type(FunctionType, .args=type_args, .ret=ret_t)); + return Type(ClosureType, Type(FunctionType, .args = type_args, .ret = ret_t)); } case OptionalTypeAST: { DeclareMatch(opt, ast, OptionalTypeAST); type_t *t = parse_type_ast(env, opt->type); if (t->tag == VoidType || t->tag == AbortType || t->tag == ReturnType) code_err(ast, "Optional ", type_to_str(t), " types are not supported."); - else if (t->tag == OptionalType) - code_err(ast, "Nested optional types are not currently supported"); - return Type(OptionalType, .type=t); + else if (t->tag == OptionalType) code_err(ast, "Nested optional types are not currently supported"); + return Type(OptionalType, .type = t); } case UnknownTypeAST: code_err(ast, "I don't know how to get this type"); } @@ -151,19 +151,20 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast) // } // } -PUREFUNC type_t *get_math_type(env_t *env, ast_t *ast, type_t *lhs_t, type_t *rhs_t) -{ +PUREFUNC type_t *get_math_type(env_t *env, ast_t *ast, type_t *lhs_t, type_t *rhs_t) { (void)env; switch (compare_precision(lhs_t, rhs_t)) { - case NUM_PRECISION_EQUAL: case NUM_PRECISION_MORE: return lhs_t; + case NUM_PRECISION_EQUAL: + case NUM_PRECISION_MORE: return lhs_t; case NUM_PRECISION_LESS: return rhs_t; - default: code_err(ast, "Math operations between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), " are not supported"); + default: + code_err(ast, "Math operations between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), + " are not supported"); } return NULL; } -static env_t *load_module(env_t *env, ast_t *module_ast) -{ +static env_t *load_module(env_t *env, ast_t *module_ast) { DeclareMatch(use, module_ast, Use); switch (use->what) { case USE_LOCAL: { @@ -171,12 +172,10 @@ static env_t *load_module(env_t *env, ast_t *module_ast) Path_t source_dir = Path$parent(source_path); Path_t used_path = Path$resolved(Path$from_str(use->path), source_dir); - if (!Path$exists(used_path)) - code_err(module_ast, "No such file exists: ", quoted(use->path)); + if (!Path$exists(used_path)) code_err(module_ast, "No such file exists: ", quoted(use->path)); env_t *module_env = Table$str_get(*env->imports, String(used_path)); - if (module_env) - return module_env; + if (module_env) return module_env; ast_t *ast = parse_file(String(used_path), NULL); if (!ast) print_err("Could not compile file ", used_path); @@ -186,9 +185,10 @@ static env_t *load_module(env_t *env, ast_t *module_ast) module_info_t mod = get_module_info(module_ast); glob_t tm_files; const char *folder = mod.version ? String(mod.name, "_", mod.version) : mod.name; - if (glob(String(TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL, &tm_files) != 0) { - if (!try_install_module(mod)) - code_err(module_ast, "Could not find or install library"); + if (glob(String(TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, + NULL, &tm_files) + != 0) { + if (!try_install_module(mod)) code_err(module_ast, "Could not find or install library"); } env_t *module_env = fresh_scope(env); @@ -203,8 +203,9 @@ static env_t *load_module(env_t *env, ast_t *module_ast) env_t *subenv = load_module_env(module_file_env, ast); for (int64_t j = 0; j < subenv->locals->entries.length; j++) { struct { - const char *name; binding_t *binding; - } *entry = subenv->locals->entries.data + j*subenv->locals->entries.stride; + const char *name; + binding_t *binding; + } *entry = subenv->locals->entries.data + j * subenv->locals->entries.stride; Table$str_set(module_env->locals, entry->name, entry->binding); } } @@ -215,8 +216,7 @@ static env_t *load_module(env_t *env, ast_t *module_ast) } } -void prebind_statement(env_t *env, ast_t *statement) -{ +void prebind_statement(env_t *env, ast_t *statement) { switch (statement->tag) { case DocTest: { prebind_statement(env, Match(statement, DocTest)->expr); @@ -229,63 +229,73 @@ void prebind_statement(env_t *env, ast_t *statement) case StructDef: { DeclareMatch(def, statement, StructDef); if (get_binding(env, def->name)) - code_err(statement, "A ", type_to_str(get_binding(env, def->name)->type), " called ", quoted(def->name), " has already been defined"); + code_err(statement, "A ", type_to_str(get_binding(env, def->name)->type), " called ", quoted(def->name), + " has already been defined"); env_t *ns_env = namespace_env(env, def->name); - type_t *type = Type(StructType, .name=def->name, .opaque=true, .external=def->external, .env=ns_env); // placeholder + type_t *type = Type(StructType, .name = def->name, .opaque = true, .external = def->external, + .env = ns_env); // placeholder Table$str_set(env->types, def->name, type); - set_binding(env, def->name, Type(TypeInfoType, .name=def->name, .type=type, .env=ns_env), + set_binding(env, def->name, Type(TypeInfoType, .name = def->name, .type = type, .env = ns_env), namespace_name(env, env->namespace, Texts(def->name, "$$info"))); - for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; stmt = stmt->next) + for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; + stmt = stmt->next) prebind_statement(ns_env, stmt->ast); break; } case EnumDef: { DeclareMatch(def, statement, EnumDef); if (get_binding(env, def->name)) - code_err(statement, "A ", type_to_str(get_binding(env, def->name)->type), " called ", quoted(def->name), " has already been defined"); + code_err(statement, "A ", type_to_str(get_binding(env, def->name)->type), " called ", quoted(def->name), + " has already been defined"); env_t *ns_env = namespace_env(env, def->name); - type_t *type = Type(EnumType, .name=def->name, .opaque=true, .env=ns_env); // placeholder + type_t *type = Type(EnumType, .name = def->name, .opaque = true, .env = ns_env); // placeholder Table$str_set(env->types, def->name, type); - set_binding(env, def->name, Type(TypeInfoType, .name=def->name, .type=type, .env=ns_env), + set_binding(env, def->name, Type(TypeInfoType, .name = def->name, .type = type, .env = ns_env), namespace_name(env, env->namespace, Texts(def->name, "$$info"))); - for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; stmt = stmt->next) + for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; + stmt = stmt->next) prebind_statement(ns_env, stmt->ast); break; } case LangDef: { DeclareMatch(def, statement, LangDef); if (get_binding(env, def->name)) - code_err(statement, "A ", type_to_str(get_binding(env, def->name)->type), " called ", quoted(def->name), " has already been defined"); + code_err(statement, "A ", type_to_str(get_binding(env, def->name)->type), " called ", quoted(def->name), + " has already been defined"); env_t *ns_env = namespace_env(env, def->name); - type_t *type = Type(TextType, .lang=def->name, .env=ns_env); + type_t *type = Type(TextType, .lang = def->name, .env = ns_env); Table$str_set(env->types, def->name, type); - set_binding(env, def->name, Type(TypeInfoType, .name=def->name, .type=type, .env=ns_env), + set_binding(env, def->name, Type(TypeInfoType, .name = def->name, .type = type, .env = ns_env), namespace_name(env, env->namespace, Texts(def->name, "$$info"))); - for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; stmt = stmt->next) + for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; + stmt = stmt->next) prebind_statement(ns_env, stmt->ast); break; } case Extend: { DeclareMatch(extend, statement, Extend); env_t *ns_env = namespace_env(env, extend->name); - env_t *extended = new(env_t); + env_t *extended = new (env_t); *extended = *ns_env; - extended->locals = new(Table_t, .fallback=env->locals); - extended->namespace_bindings = new(Table_t, .fallback=env->namespace_bindings); + extended->locals = new (Table_t, .fallback = env->locals); + extended->namespace_bindings = new (Table_t, .fallback = env->namespace_bindings); extended->id_suffix = env->id_suffix; for (ast_list_t *stmt = extend->body ? Match(extend->body, Block)->statements : NULL; stmt; stmt = stmt->next) prebind_statement(extended, stmt->ast); List_t new_bindings = extended->locals->entries; for (int64_t i = 0; i < new_bindings.length; i++) { - struct { const char *name; binding_t *binding; } *entry = new_bindings.data + i*new_bindings.stride; + struct { + const char *name; + binding_t *binding; + } *entry = new_bindings.data + i * new_bindings.stride; binding_t *clobbered = Table$str_get(*ns_env->locals, entry->name); if (clobbered && !type_eq(clobbered->type, entry->binding->type)) code_err(statement, "This `extend` block overwrites the binding for ", quoted(entry->name), - " in the original namespace (with type ", type_to_str(clobbered->type), ") with a new binding with type ", - type_to_str(entry->binding->type)); + " in the original namespace (with type ", type_to_str(clobbered->type), + ") with a new binding with type ", type_to_str(entry->binding->type)); Table$str_set(ns_env->locals, entry->name, entry->binding); } break; @@ -294,8 +304,7 @@ void prebind_statement(env_t *env, ast_t *statement) } } -void bind_statement(env_t *env, ast_t *statement) -{ +void bind_statement(env_t *env, ast_t *statement) { switch (statement->tag) { case DocTest: { bind_statement(env, Match(statement, DocTest)->expr); @@ -311,19 +320,16 @@ void bind_statement(env_t *env, ast_t *statement) if (streq(name, "_")) // Explicit discard return; if (get_binding(env, name)) - code_err(decl->var, "A ", type_to_str(get_binding(env, name)->type), " called ", quoted(name), " has already been defined"); - if (decl->value) - bind_statement(env, decl->value); + code_err(decl->var, "A ", type_to_str(get_binding(env, name)->type), " called ", quoted(name), + " has already been defined"); + if (decl->value) bind_statement(env, decl->value); type_t *type = decl->type ? parse_type_ast(env, decl->type) : get_type(env, decl->value); - if (!type) - code_err(statement, "I couldn't figure out the type of this value"); - if (type->tag == FunctionType) - type = Type(ClosureType, type); + if (!type) code_err(statement, "I couldn't figure out the type of this value"); + if (type->tag == FunctionType) type = Type(ClosureType, type); Text_t code; if (name[0] != '_' && (env->namespace || decl->top_level)) code = namespace_name(env, env->namespace, Text$from_str(name)); - else - code = Texts("_$", name); + else code = Texts("_$", name); set_binding(env, name, type, code); break; } @@ -339,11 +345,12 @@ void bind_statement(env_t *env, ast_t *statement) type_t *ret_t = Match(type, FunctionType)->ret; const char *name = get_type_name(ret_t); if (!name) - code_err(statement, "Conversions are only supported for text, struct, and enum types, not ", type_to_str(ret_t)); + code_err(statement, "Conversions are only supported for text, struct, and enum types, not ", + type_to_str(ret_t)); Text_t code = namespace_name(env, env->namespace, - Texts(name, "$", String(get_line_number(statement->file, statement->start)))); - binding_t binding = {.type=type, .code=code}; + Texts(name, "$", String(get_line_number(statement->file, statement->start)))); + binding_t binding = {.type = type, .code = code}; env_t *type_ns = get_namespace_by_type(env, ret_t); List$insert(&type_ns->namespace->constructors, &binding, I(0), sizeof(binding)); break; @@ -371,23 +378,34 @@ void bind_statement(env_t *env, ast_t *statement) file = field_ast->value->file, start = field_ast->value->start, end = field_ast->value->end; } if (non_opt_field_t == type) - compiler_err(file, start, end, "This is a recursive struct that would be infinitely large. Maybe you meant to use an optional '@", type_to_str(type), "?' pointer instead?"); + compiler_err(file, start, end, + "This is a recursive struct that would be infinitely large. Maybe you meant to " + "use an optional '@", + type_to_str(type), "?' pointer instead?"); else if (non_opt_field_t->tag == StructType && Match(non_opt_field_t, StructType)->external) - compiler_err(file, start, end, "This is an opaque externally defined struct.\n" - "I can't use it as a member without knowing what its fields are.\n" - "Either specify its fields and remove the `opaque` qualifier, or use something like a @", type_to_str(non_opt_field_t), " pointer."); + compiler_err( + file, start, end, + "This is an opaque externally defined struct.\n" + "I can't use it as a member without knowing what its fields are.\n" + "Either specify its fields and remove the `opaque` qualifier, or use something like a @", + type_to_str(non_opt_field_t), " pointer."); else - compiler_err(file, start, end, "I'm still in the process of defining the fields of ", type_to_str(field_t), ", so I don't know how to use it as a member." - "\nTry using a @", type_to_str(field_t), " pointer for this field."); + compiler_err(file, start, end, "I'm still in the process of defining the fields of ", + type_to_str(field_t), + ", so I don't know how to use it as a member." + "\nTry using a @", + type_to_str(field_t), " pointer for this field."); } - fields = new(arg_t, .name=field_ast->name, .type=field_t, .default_val=field_ast->value, .next=fields); + fields = new (arg_t, .name = field_ast->name, .type = field_t, .default_val = field_ast->value, + .next = fields); } REVERSE_LIST(fields); type->__data.StructType.fields = fields; // populate placeholder type->__data.StructType.opaque = false; } - for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; stmt = stmt->next) + for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; + stmt = stmt->next) bind_statement(ns_env, stmt->ast); break; } @@ -416,22 +434,32 @@ void bind_statement(env_t *env, ast_t *statement) file = field_ast->value->file, start = field_ast->value->start, end = field_ast->value->end; } if (non_opt_field_t == type) - compiler_err(file, start, end, "This is a recursive enum that would be infinitely large. Maybe you meant to use an optional '@", type_to_str(type), "?' pointer instead?"); + compiler_err(file, start, end, + "This is a recursive enum that would be infinitely large. Maybe you meant to use " + "an optional '@", + type_to_str(type), "?' pointer instead?"); else if (non_opt_field_t->tag == StructType && Match(non_opt_field_t, StructType)->external) - compiler_err(file, start, end, "This is an opaque externally defined struct.\n" - "I can't use it as a member without knowing what its fields are.\n" - "Either specify its fields and remove the `opaque` qualifier, or use something like a @", type_to_str(non_opt_field_t), " pointer."); + compiler_err( + file, start, end, + "This is an opaque externally defined struct.\n" + "I can't use it as a member without knowing what its fields are.\n" + "Either specify its fields and remove the `opaque` qualifier, or use something like a @", + type_to_str(non_opt_field_t), " pointer."); else - compiler_err(file, start, end, "I'm still in the process of defining the fields of ", type_to_str(field_t), + compiler_err(file, start, end, "I'm still in the process of defining the fields of ", + type_to_str(field_t), ", so I don't know how to use it as a member." - "\nTry using a @", type_to_str(field_t), " pointer for this field."); + "\nTry using a @", + type_to_str(field_t), " pointer for this field."); } - fields = new(arg_t, .name=field_ast->name, .type=field_t, .default_val=field_ast->value, .next=fields); + fields = new (arg_t, .name = field_ast->name, .type = field_t, .default_val = field_ast->value, + .next = fields); } REVERSE_LIST(fields); env_t *member_ns = namespace_env(env, String(def->name, "$", tag_ast->name)); - type_t *tag_type = Type(StructType, .name=String(def->name, "$", tag_ast->name), .fields=fields, .env=member_ns); - tags = new(tag_t, .name=tag_ast->name, .tag_value=(next_tag++), .type=tag_type, .next=tags); + type_t *tag_type = + Type(StructType, .name = String(def->name, "$", tag_ast->name), .fields = fields, .env = member_ns); + tags = new (tag_t, .name = tag_ast->name, .tag_value = (next_tag++), .type = tag_type, .next = tags); } REVERSE_LIST(tags); type->__data.EnumType.tags = tags; @@ -439,14 +467,14 @@ void bind_statement(env_t *env, ast_t *statement) for (tag_t *tag = tags; tag; tag = tag->next) { if (Match(tag->type, StructType)->fields) { // Constructor: - type_t *constructor_t = Type(FunctionType, .args=Match(tag->type, StructType)->fields, .ret=type); + type_t *constructor_t = Type(FunctionType, .args = Match(tag->type, StructType)->fields, .ret = type); Text_t tagged_name = namespace_name(env, env->namespace, Texts(def->name, "$tagged$", tag->name)); set_binding(ns_env, tag->name, constructor_t, tagged_name); - binding_t binding = {.type=constructor_t, .code=tagged_name}; + binding_t binding = {.type = constructor_t, .code = tagged_name}; List$insert(&ns_env->namespace->constructors, &binding, I(1), sizeof(binding)); } else if (has_any_tags_with_fields) { // Empty singleton value: Text_t code = Texts("((", namespace_name(env, env->namespace, Texts(def->name, "$$type")), "){", - namespace_name(env, env->namespace, Texts(def->name, "$tag$", tag->name)), "})"); + namespace_name(env, env->namespace, Texts(def->name, "$tag$", tag->name)), "})"); set_binding(ns_env, tag->name, type, code); } else { Text_t code = namespace_name(env, env->namespace, Texts(def->name, "$tag$", tag->name)); @@ -454,8 +482,9 @@ void bind_statement(env_t *env, ast_t *statement) } Table$str_set(env->types, String(def->name, "$", tag->name), tag->type); } - - for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; stmt = stmt->next) { + + for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; + stmt = stmt->next) { bind_statement(ns_env, stmt->ast); } break; @@ -463,35 +492,39 @@ void bind_statement(env_t *env, ast_t *statement) case LangDef: { DeclareMatch(def, statement, LangDef); env_t *ns_env = namespace_env(env, def->name); - type_t *type = Type(TextType, .lang=def->name, .env=ns_env); + type_t *type = Type(TextType, .lang = def->name, .env = ns_env); ns_env->current_type = type; Table$str_set(env->types, def->name, type); - set_binding(ns_env, "from_text", NewFunctionType(type, {.name="text", .type=TEXT_TYPE}), + set_binding(ns_env, "from_text", NewFunctionType(type, {.name = "text", .type = TEXT_TYPE}), Texts("(", namespace_name(env, env->namespace, Texts(def->name, "$$type")), ")")); - for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; stmt = stmt->next) + for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; + stmt = stmt->next) bind_statement(ns_env, stmt->ast); break; } case Extend: { DeclareMatch(extend, statement, Extend); env_t *ns_env = namespace_env(env, extend->name); - env_t *extended = new(env_t); + env_t *extended = new (env_t); *extended = *ns_env; - extended->locals = new(Table_t, .fallback=env->locals); - extended->namespace_bindings = new(Table_t, .fallback=env->namespace_bindings); + extended->locals = new (Table_t, .fallback = env->locals); + extended->namespace_bindings = new (Table_t, .fallback = env->namespace_bindings); extended->id_suffix = env->id_suffix; for (ast_list_t *stmt = extend->body ? Match(extend->body, Block)->statements : NULL; stmt; stmt = stmt->next) bind_statement(extended, stmt->ast); List_t new_bindings = extended->locals->entries; for (int64_t i = 0; i < new_bindings.length; i++) { - struct { const char *name; binding_t *binding; } *entry = new_bindings.data + i*new_bindings.stride; + struct { + const char *name; + binding_t *binding; + } *entry = new_bindings.data + i * new_bindings.stride; binding_t *clobbered = Table$str_get(*ns_env->locals, entry->name); if (clobbered && !type_eq(clobbered->type, entry->binding->type)) code_err(statement, "This `extend` block overwrites the binding for ", quoted(entry->name), - " in the original namespace (with type ", type_to_str(clobbered->type), ") with a new binding with type ", - type_to_str(entry->binding->type)); + " in the original namespace (with type ", type_to_str(clobbered->type), + ") with a new binding with type ", type_to_str(entry->binding->type)); Table$str_set(ns_env->locals, entry->name, entry->binding); } break; @@ -502,22 +535,25 @@ void bind_statement(env_t *env, ast_t *statement) for (Table_t *bindings = module_env->locals; bindings != module_env->globals; bindings = bindings->fallback) { List_t entries = bindings->entries; for (int64_t i = 0; i < entries.length; i++) { - struct { const char *name; binding_t *binding; } *entry = entries.data + entries.stride*i; - if (entry->name[0] == '_' || streq(entry->name, "main")) - continue; + struct { + const char *name; + binding_t *binding; + } *entry = entries.data + entries.stride * i; + if (entry->name[0] == '_' || streq(entry->name, "main")) continue; binding_t *b = Table$str_get(*env->locals, entry->name); - if (!b) - Table$str_set(env->locals, entry->name, entry->binding); + if (!b) Table$str_set(env->locals, entry->name, entry->binding); else if (b != entry->binding) - code_err(statement, "This module imports a symbol called '", entry->name, "', which would clobber another variable"); + code_err(statement, "This module imports a symbol called '", entry->name, + "', which would clobber another variable"); } } for (int64_t i = 0; i < module_env->types->entries.length; i++) { - struct { const char *name; type_t *type; } *entry = module_env->types->entries.data + module_env->types->entries.stride*i; - if (entry->name[0] == '_') - continue; - if (Table$str_get(*env->types, entry->name)) - continue; + struct { + const char *name; + type_t *type; + } *entry = module_env->types->entries.data + module_env->types->entries.stride * i; + if (entry->name[0] == '_') continue; + if (Table$str_get(*env->types, entry->name)) continue; Table$str_set(env->types, entry->name, entry->type); } @@ -533,8 +569,7 @@ void bind_statement(env_t *env, ast_t *statement) case Extern: { DeclareMatch(ext, statement, Extern); type_t *t = parse_type_ast(env, ext->type); - if (t->tag == ClosureType) - t = Match(t, ClosureType)->fn; + if (t->tag == ClosureType) t = Match(t, ClosureType)->fn; set_binding(env, ext->name, t, Text$from_str(ext->name)); break; } @@ -542,15 +577,15 @@ void bind_statement(env_t *env, ast_t *statement) } } -type_t *get_function_def_type(env_t *env, ast_t *ast) -{ +type_t *get_function_def_type(env_t *env, ast_t *ast) { arg_ast_t *arg_asts = ast->tag == FunctionDef ? Match(ast, FunctionDef)->args : Match(ast, ConvertDef)->args; - type_ast_t *ret_type = ast->tag == FunctionDef ? Match(ast, FunctionDef)->ret_type : Match(ast, ConvertDef)->ret_type; + type_ast_t *ret_type = + ast->tag == FunctionDef ? Match(ast, FunctionDef)->ret_type : Match(ast, ConvertDef)->ret_type; arg_t *args = NULL; env_t *scope = fresh_scope(env); for (arg_ast_t *arg = arg_asts; arg; arg = arg->next) { type_t *t = arg->type ? parse_type_ast(env, arg->type) : get_type(env, arg->value); - args = new(arg_t, .name=arg->name, .type=t, .default_val=arg->value, .next=args); + args = new (arg_t, .name = arg->name, .type = t, .default_val = arg->value, .next = args); set_binding(scope, arg->name, t, EMPTY_TEXT); } REVERSE_LIST(args); @@ -558,29 +593,26 @@ type_t *get_function_def_type(env_t *env, ast_t *ast) type_t *ret = ret_type ? parse_type_ast(scope, ret_type) : Type(VoidType); if (has_stack_memory(ret)) code_err(ast, "Functions can't return stack references because the reference may outlive its stack frame."); - return Type(FunctionType, .args=args, .ret=ret); + return Type(FunctionType, .args = args, .ret = ret); } -type_t *get_method_type(env_t *env, ast_t *self, const char *name) -{ +type_t *get_method_type(env_t *env, ast_t *self, const char *name) { binding_t *b = get_namespace_binding(env, self, name); - if (!b || !b->type) - code_err(self, "No such method: ", type_to_str(get_type(env, self)), ".", name, "(...)"); + if (!b || !b->type) code_err(self, "No such method: ", type_to_str(get_type(env, self)), ".", name, "(...)"); return b->type; } -env_t *when_clause_scope(env_t *env, type_t *subject_t, when_clause_t *clause) -{ - if (clause->pattern->tag == Var || subject_t->tag != EnumType) - return env; +env_t *when_clause_scope(env_t *env, type_t *subject_t, when_clause_t *clause) { + if (clause->pattern->tag == Var || subject_t->tag != EnumType) return env; if (clause->pattern->tag != FunctionCall || Match(clause->pattern, FunctionCall)->fn->tag != Var) - code_err(clause->pattern, "I only support variables and constructors for pattern matching ", type_to_str(subject_t), " types in a 'when' block"); + code_err(clause->pattern, "I only support variables and constructors for pattern matching ", + type_to_str(subject_t), " types in a 'when' block"); DeclareMatch(fn, clause->pattern, FunctionCall); const char *tag_name = Match(fn->fn, Var)->name; type_t *tag_type = NULL; - tag_t * const tags = Match(subject_t, EnumType)->tags; + tag_t *const tags = Match(subject_t, EnumType)->tags; for (tag_t *tag = tags; tag; tag = tag->next) { if (streq(tag->name, tag_name)) { tag_type = tag->type; @@ -591,14 +623,12 @@ env_t *when_clause_scope(env_t *env, type_t *subject_t, when_clause_t *clause) if (!tag_type) code_err(clause->pattern, "There is no tag ", quoted(tag_name), " for the type ", type_to_str(subject_t)); - if (!fn->args) - return env; + if (!fn->args) return env; env_t *scope = fresh_scope(env); DeclareMatch(tag_struct, tag_type, StructType); if (fn->args && !fn->args->next && tag_struct->fields && tag_struct->fields->next) { - if (fn->args->value->tag != Var) - code_err(fn->args->value, "I expected a variable here"); + if (fn->args->value->tag != Var) code_err(fn->args->value, "I expected a variable here"); set_binding(scope, Match(fn->args->value, Var)->name, tag_type, EMPTY_TEXT); return scope; } @@ -606,9 +636,9 @@ env_t *when_clause_scope(env_t *env, type_t *subject_t, when_clause_t *clause) arg_t *field = tag_struct->fields; for (arg_ast_t *var = fn->args; var || field; var = var ? var->next : var) { if (!var) - code_err(clause->pattern, "The field ", type_to_str(subject_t), ".", tag_name, ".", field->name, " wasn't accounted for"); - if (!field) - code_err(var->value, "This is one more field than ", type_to_str(subject_t), " has"); + code_err(clause->pattern, "The field ", type_to_str(subject_t), ".", tag_name, ".", field->name, + " wasn't accounted for"); + if (!field) code_err(var->value, "This is one more field than ", type_to_str(subject_t), " has"); if (var->value->tag != Var) code_err(var->value, "I expected this to be a plain variable so I could bind it to a value"); if (!streq(Match(var->value, Var)->name, "_")) @@ -618,14 +648,12 @@ env_t *when_clause_scope(env_t *env, type_t *subject_t, when_clause_t *clause) return scope; } -type_t *get_clause_type(env_t *env, type_t *subject_t, when_clause_t *clause) -{ +type_t *get_clause_type(env_t *env, type_t *subject_t, when_clause_t *clause) { env_t *scope = when_clause_scope(env, subject_t, clause); return get_type(scope, clause->body); } -type_t *get_type(env_t *env, ast_t *ast) -{ +type_t *get_type(env_t *env, ast_t *ast) { if (!ast) return NULL; #ifdef __GNUC__ #pragma GCC diagnostic push @@ -633,7 +661,7 @@ type_t *get_type(env_t *env, ast_t *ast) #endif switch (ast->tag) { case None: { - return Type(OptionalType, .type=NULL); + return Type(OptionalType, .type = NULL); } case Bool: { return Type(BoolType); @@ -642,13 +670,14 @@ type_t *get_type(env_t *env, ast_t *ast) return Type(BigIntType); } case Num: { - return Type(NumType, .bits=TYPE_NBITS64); + return Type(NumType, .bits = TYPE_NBITS64); } case HeapAllocate: { type_t *pointed = get_type(env, Match(ast, HeapAllocate)->value); if (has_stack_memory(pointed)) - code_err(ast, "Stack references cannot be moved to the heap because they may outlive the stack frame they were created in."); - return Type(PointerType, .pointed=pointed); + code_err(ast, "Stack references cannot be moved to the heap because they may outlive the stack frame they " + "were created in."); + return Type(PointerType, .pointed = pointed); } case StackReference: { // Supported: @@ -677,24 +706,21 @@ type_t *get_type(env_t *env, ast_t *ast) code_err(base, "This value might be none, so it can't be safely dereferenced"); } else if (base_type->tag == PointerType) { DeclareMatch(ptr, base_type, PointerType); - return Type(PointerType, .pointed=ref_type, .is_stack=ptr->is_stack); + return Type(PointerType, .pointed = ref_type, .is_stack = ptr->is_stack); } else if (base->tag == Var) { - return Type(PointerType, .pointed=ref_type, .is_stack=true); + return Type(PointerType, .pointed = ref_type, .is_stack = true); } code_err(ast, "'&' stack references can only be used on the fields of pointers and local variables"); } - case Index: - code_err(ast, "'&' stack references are not supported for list or table indexing"); - default: - return Type(PointerType, .pointed=get_type(env, value), .is_stack=true); + case Index: code_err(ast, "'&' stack references are not supported for list or table indexing"); + default: return Type(PointerType, .pointed = get_type(env, value), .is_stack = true); } } case Optional: { ast_t *value = Match(ast, Optional)->value; type_t *t = get_type(env, value); - if (t->tag == OptionalType) - code_err(ast, "This value is already optional, it can't be converted to optional"); - return Type(OptionalType, .type=t); + if (t->tag == OptionalType) code_err(ast, "This value is already optional, it can't be converted to optional"); + return Type(OptionalType, .type = t); } case NonOptional: { ast_t *value = Match(ast, NonOptional)->value; @@ -730,21 +756,20 @@ type_t *get_type(env_t *env, ast_t *ast) env_t *scope = env; while (item_ast->tag == Comprehension) { DeclareMatch(comp, item_ast, Comprehension); - scope = for_scope( - scope, FakeAST(For, .iter=comp->iter, .vars=comp->vars)); + scope = for_scope(scope, FakeAST(For, .iter = comp->iter, .vars = comp->vars)); item_ast = comp->expr; } type_t *t2 = get_type(scope, item_ast); type_t *merged = item_type ? type_or_type(item_type, t2) : t2; - if (!merged) - return Type(ListType, .item_type=NULL); + if (!merged) return Type(ListType, .item_type = NULL); item_type = merged; } if (item_type && has_stack_memory(item_type)) - code_err(ast, "Lists cannot hold stack references, because the list may outlive the stack frame the reference was created in."); + code_err(ast, "Lists cannot hold stack references, because the list may outlive the stack frame the " + "reference was created in."); - return Type(ListType, .item_type=item_type); + return Type(ListType, .item_type = item_type); } case Set: { DeclareMatch(set, ast, Set); @@ -754,22 +779,20 @@ type_t *get_type(env_t *env, ast_t *ast) env_t *scope = env; while (item_ast->tag == Comprehension) { DeclareMatch(comp, item_ast, Comprehension); - scope = for_scope( - scope, FakeAST(For, .iter=comp->iter, .vars=comp->vars)); + scope = for_scope(scope, FakeAST(For, .iter = comp->iter, .vars = comp->vars)); item_ast = comp->expr; } type_t *this_item_type = get_type(scope, item_ast); type_t *item_merged = type_or_type(item_type, this_item_type); - if (!item_merged) - return Type(SetType, .item_type=NULL); + if (!item_merged) return Type(SetType, .item_type = NULL); item_type = item_merged; } if (item_type && has_stack_memory(item_type)) code_err(ast, "Sets cannot hold stack references because the set may outlive the reference's stack frame."); - return Type(SetType, .item_type=item_type); + return Type(SetType, .item_type = item_type); } case Table: { DeclareMatch(table, ast, Table); @@ -780,8 +803,7 @@ type_t *get_type(env_t *env, ast_t *ast) env_t *scope = env; while (entry_ast->tag == Comprehension) { DeclareMatch(comp, entry_ast, Comprehension); - scope = for_scope( - scope, FakeAST(For, .iter=comp->iter, .vars=comp->vars)); + scope = for_scope(scope, FakeAST(For, .iter = comp->iter, .vars = comp->vars)); entry_ast = comp->expr; } @@ -790,56 +812,52 @@ type_t *get_type(env_t *env, ast_t *ast) type_t *value_t = get_type(scope, e->value); type_t *key_merged = key_type ? type_or_type(key_type, key_t) : key_t; - if (!key_merged) - ambiguous_key_type = true; + if (!key_merged) ambiguous_key_type = true; key_type = key_merged; type_t *val_merged = value_type ? type_or_type(value_type, value_t) : value_t; - if (!val_merged) - ambiguous_value_type = true; + if (!val_merged) ambiguous_value_type = true; value_type = val_merged; } - if (ambiguous_key_type) - key_type = NULL; + if (ambiguous_key_type) key_type = NULL; - if (ambiguous_value_type) - value_type = NULL; + if (ambiguous_value_type) value_type = NULL; if ((key_type && has_stack_memory(key_type)) || (value_type && has_stack_memory(value_type))) - code_err(ast, "Tables cannot hold stack references because the table may outlive the reference's stack frame."); + code_err(ast, + "Tables cannot hold stack references because the table may outlive the reference's stack frame."); - return Type(TableType, .key_type=key_type, .value_type=value_type, .default_value=table->default_value, .env=env); + return Type(TableType, .key_type = key_type, .value_type = value_type, .default_value = table->default_value, + .env = env); } case TableEntry: { code_err(ast, "Table entries should not be typechecked directly"); } case Comprehension: { DeclareMatch(comp, ast, Comprehension); - env_t *scope = for_scope(env, FakeAST(For, .iter=comp->iter, .vars=comp->vars)); + env_t *scope = for_scope(env, FakeAST(For, .iter = comp->iter, .vars = comp->vars)); if (comp->expr->tag == Comprehension) { return get_type(scope, comp->expr); } else if (comp->expr->tag == TableEntry) { DeclareMatch(e, comp->expr, TableEntry); - return Type(TableType, .key_type=get_type(scope, e->key), .value_type=get_type(scope, e->value), .env=env); + return Type(TableType, .key_type = get_type(scope, e->key), .value_type = get_type(scope, e->value), + .env = env); } else { - return Type(ListType, .item_type=get_type(scope, comp->expr)); + return Type(ListType, .item_type = get_type(scope, comp->expr)); } } case FieldAccess: { DeclareMatch(access, ast, FieldAccess); type_t *fielded_t = get_type(env, access->fielded); if (access->field[0] == '_') { - if (!env->current_type || !type_eq(env->current_type, - fielded_t->tag == TypeInfoType - ? Match(fielded_t, TypeInfoType)->type - : value_type(fielded_t))) + if (!env->current_type + || !type_eq(env->current_type, fielded_t->tag == TypeInfoType ? Match(fielded_t, TypeInfoType)->type + : value_type(fielded_t))) code_err(ast, "Fields beginning with underscores like '", access->field, "' can't be accessed outside the scope where the type (", - type_to_text( - fielded_t->tag == TypeInfoType - ? Match(fielded_t, TypeInfoType)->type - : value_type(fielded_t)), + type_to_text(fielded_t->tag == TypeInfoType ? Match(fielded_t, TypeInfoType)->type + : value_type(fielded_t)), ") is defined."); } if (fielded_t->tag == ModuleType) { @@ -855,8 +873,7 @@ type_t *get_type(env_t *env, ast_t *ast) return b->type; } type_t *field_t = get_field_type(fielded_t, access->field); - if (!field_t) - code_err(ast, type_to_str(fielded_t), " objects don't have a field called '", access->field, "'"); + if (!field_t) code_err(ast, type_to_str(fielded_t), " objects don't have a field called '", access->field, "'"); return field_t; } case Index: { @@ -865,8 +882,7 @@ type_t *get_type(env_t *env, ast_t *ast) if (indexed_t->tag == OptionalType && !indexing->index) code_err(ast, "You're attempting to dereference a value whose type indicates it could be none"); - if (indexed_t->tag == PointerType && !indexing->index) - return Match(indexed_t, PointerType)->pointed; + if (indexed_t->tag == PointerType && !indexing->index) return Match(indexed_t, PointerType)->pointed; type_t *value_t = value_type(indexed_t); if (value_t->tag == ListType) { @@ -877,8 +893,7 @@ type_t *get_type(env_t *env, ast_t *ast) code_err(indexing->index, "I only know how to index lists using integers, not ", type_to_str(index_t)); } else if (value_t->tag == TableType) { DeclareMatch(table_type, value_t, TableType); - if (table_type->default_value) - return table_type->value_type; + if (table_type->default_value) return table_type->value_type; return Type(OptionalType, table_type->value_type); } else if (value_t->tag == TextType) { return value_t; @@ -889,26 +904,25 @@ type_t *get_type(env_t *env, ast_t *ast) case FunctionCall: { DeclareMatch(call, ast, FunctionCall); type_t *fn_type_t = get_type(env, call->fn); - if (!fn_type_t) - code_err(call->fn, "I couldn't find this function"); + if (!fn_type_t) code_err(call->fn, "I couldn't find this function"); if (fn_type_t->tag == TypeInfoType) { type_t *t = Match(fn_type_t, TypeInfoType)->type; - binding_t *constructor = get_constructor(env, t, call->args, env->current_type != NULL && type_eq(env->current_type, t)); - if (constructor) - return t; + binding_t *constructor = + get_constructor(env, t, call->args, env->current_type != NULL && type_eq(env->current_type, t)); + if (constructor) return t; else if (t->tag == StructType || t->tag == IntType || t->tag == BigIntType || t->tag == NumType - || t->tag == ByteType || t->tag == TextType || t->tag == CStringType) + || t->tag == ByteType || t->tag == TextType || t->tag == CStringType) return t; // Constructor arg_t *arg_types = NULL; for (arg_ast_t *arg = call->args; arg; arg = arg->next) - arg_types = new(arg_t, .type=get_type(env, arg->value), .name=arg->name, .next=arg_types); + arg_types = new (arg_t, .type = get_type(env, arg->value), .name = arg->name, .next = arg_types); REVERSE_LIST(arg_types); - code_err(call->fn, "I couldn't find a type constructor for ", type_to_text(Type(FunctionType, .args=arg_types, .ret=t))); + code_err(call->fn, "I couldn't find a type constructor for ", + type_to_text(Type(FunctionType, .args = arg_types, .ret = t))); } - if (fn_type_t->tag == ClosureType) - fn_type_t = Match(fn_type_t, ClosureType)->fn; + if (fn_type_t->tag == ClosureType) fn_type_t = Match(fn_type_t, ClosureType)->fn; if (fn_type_t->tag != FunctionType) code_err(call->fn, "This isn't a function, it's a ", type_to_str(fn_type_t)); DeclareMatch(fn_type, fn_type_t, FunctionType); @@ -925,8 +939,10 @@ type_t *get_type(env_t *env, ast_t *ast) self_value_t = value_type(self_value_t); if (self_value_t->tag == TypeInfoType || self_value_t->tag == ModuleType) { - return get_type(env, WrapAST(ast, FunctionCall, .fn=WrapAST(call->self, FieldAccess, .fielded=call->self, .field=call->name), - .args=call->args)); + return get_type(env, + WrapAST(ast, FunctionCall, + .fn = WrapAST(call->self, FieldAccess, .fielded = call->self, .field = call->name), + .args = call->args)); } switch (self_value_t->tag) { @@ -935,17 +951,17 @@ type_t *get_type(env_t *env, ast_t *ast) if (streq(call->name, "binary_search")) return INT_TYPE; else if (streq(call->name, "by")) return self_value_t; else if (streq(call->name, "clear")) return Type(VoidType); - else if (streq(call->name, "counts")) return Type(TableType, .key_type=item_type, .value_type=INT_TYPE); - else if (streq(call->name, "find")) return Type(OptionalType, .type=INT_TYPE); - else if (streq(call->name, "where")) return Type(OptionalType, .type=INT_TYPE); + else if (streq(call->name, "counts")) return Type(TableType, .key_type = item_type, .value_type = INT_TYPE); + else if (streq(call->name, "find")) return Type(OptionalType, .type = INT_TYPE); + else if (streq(call->name, "where")) return Type(OptionalType, .type = INT_TYPE); else if (streq(call->name, "from")) return self_value_t; else if (streq(call->name, "has")) return Type(BoolType); - else if (streq(call->name, "heap_pop")) return Type(OptionalType, .type=item_type); + else if (streq(call->name, "heap_pop")) return Type(OptionalType, .type = item_type); else if (streq(call->name, "heap_push")) return Type(VoidType); else if (streq(call->name, "heapify")) return Type(VoidType); else if (streq(call->name, "insert")) return Type(VoidType); else if (streq(call->name, "insert_all")) return Type(VoidType); - else if (streq(call->name, "pop")) return Type(OptionalType, .type=item_type); + else if (streq(call->name, "pop")) return Type(OptionalType, .type = item_type); else if (streq(call->name, "random")) return item_type; else if (streq(call->name, "remove_at")) return Type(VoidType); else if (streq(call->name, "remove_item")) return Type(VoidType); @@ -957,7 +973,7 @@ type_t *get_type(env_t *env, ast_t *ast) else if (streq(call->name, "sort")) return Type(VoidType); else if (streq(call->name, "sorted")) return self_value_t; else if (streq(call->name, "to")) return self_value_t; - else if (streq(call->name, "unique")) return Type(SetType, .item_type=item_type); + else if (streq(call->name, "unique")) return Type(SetType, .item_type = item_type); else code_err(ast, "There is no '", call->name, "' method for lists"); } case SetType: { @@ -977,7 +993,7 @@ type_t *get_type(env_t *env, ast_t *ast) case TableType: { DeclareMatch(table, self_value_t, TableType); if (streq(call->name, "clear")) return Type(VoidType); - else if (streq(call->name, "get")) return Type(OptionalType, .type=table->value_type); + else if (streq(call->name, "get")) return Type(OptionalType, .type = table->value_type); else if (streq(call->name, "get_or_set")) return table->value_type; else if (streq(call->name, "has")) return Type(BoolType); else if (streq(call->name, "remove")) return Type(VoidType); @@ -989,19 +1005,17 @@ type_t *get_type(env_t *env, ast_t *ast) default: { if (call->name[0] == '_') { if (env->current_type == NULL || !type_eq(env->current_type, self_value_t)) - code_err(ast, "You can't call private methods starting with underscore (like '", call->name, "') " - "outside of the place where the type (", type_to_text(self_value_t), ") is defined."); + code_err(ast, "You can't call private methods starting with underscore (like '", call->name, + "') " + "outside of the place where the type (", + type_to_text(self_value_t), ") is defined."); } type_t *field_type = get_field_type(self_value_t, call->name); - if (field_type && field_type->tag == ClosureType) - field_type = Match(field_type, ClosureType)->fn; - if (field_type && field_type->tag == FunctionType) - return Match(field_type, FunctionType)->ret; + if (field_type && field_type->tag == ClosureType) field_type = Match(field_type, ClosureType)->fn; + if (field_type && field_type->tag == FunctionType) return Match(field_type, FunctionType)->ret; type_t *fn_type_t = get_method_type(env, call->self, call->name); - if (!fn_type_t) - code_err(ast, "No such method!"); - if (fn_type_t->tag != FunctionType) - code_err(ast, "This isn't a method, it's a ", type_to_str(fn_type_t)); + if (!fn_type_t) code_err(ast, "No such method!"); + if (fn_type_t->tag != FunctionType) code_err(ast, "This isn't a method, it's a ", type_to_str(fn_type_t)); DeclareMatch(fn_type, fn_type_t, FunctionType); return fn_type->ret; } @@ -1010,15 +1024,21 @@ type_t *get_type(env_t *env, ast_t *ast) case Block: { DeclareMatch(block, ast, Block); ast_list_t *last = block->statements; - if (!last) - return Type(VoidType); + if (!last) return Type(VoidType); while (last->next) last = last->next; // Early out if the type is knowable without any context from the block: switch (last->ast->tag) { - case UPDATE_CASES: case Assign: case Declare: case FunctionDef: case ConvertDef: case StructDef: case EnumDef: case LangDef: case Extend: - return Type(VoidType); + case UPDATE_CASES: + case Assign: + case Declare: + case FunctionDef: + case ConvertDef: + case StructDef: + case EnumDef: + case LangDef: + case Extend: return Type(VoidType); default: break; } @@ -1030,10 +1050,13 @@ type_t *get_type(env_t *env, ast_t *ast) bind_statement(block_env, stmt->ast); if (stmt->next) { // Check for unreachable code: if (stmt->ast->tag == Return) - code_err(stmt->ast, "This statement will always return, so the rest of the code in this block is unreachable!"); + code_err( + stmt->ast, + "This statement will always return, so the rest of the code in this block is unreachable!"); type_t *statement_type = get_type(block_env, stmt->ast); if (statement_type && statement_type->tag == AbortType && stmt->next) - code_err(stmt->ast, "This statement will always abort, so the rest of the code in this block is unreachable!"); + code_err(stmt->ast, + "This statement will always abort, so the rest of the code in this block is unreachable!"); } } return get_type(block_env, last->ast); @@ -1041,7 +1064,11 @@ type_t *get_type(env_t *env, ast_t *ast) case Extern: { return parse_type_ast(env, Match(ast, Extern)->type); } - case Declare: case Assign: case UPDATE_CASES: case DocTest: case Assert: { + case Declare: + case Assign: + case UPDATE_CASES: + case DocTest: + case Assert: { return Type(VoidType); } case Use: { @@ -1052,50 +1079,46 @@ type_t *get_type(env_t *env, ast_t *ast) Path_t used_path = Path$resolved(Path$from_str(Match(ast, Use)->path), source_dir); return Type(ModuleType, Path$as_c_string(used_path)); } - default: - return Type(ModuleType, Match(ast, Use)->path); + default: return Type(ModuleType, Match(ast, Use)->path); } } case Return: { ast_t *val = Match(ast, Return)->value; - if (env->fn_ret) - env = with_enum_scope(env, env->fn_ret); - return Type(ReturnType, .ret=(val ? get_type(env, val) : Type(VoidType))); + if (env->fn_ret) env = with_enum_scope(env, env->fn_ret); + return Type(ReturnType, .ret = (val ? get_type(env, val) : Type(VoidType))); } - case Stop: case Skip: { + case Stop: + case Skip: { return Type(AbortType); } - case Pass: case Defer: return Type(VoidType); + case Pass: + case Defer: return Type(VoidType); case Negative: { ast_t *value = Match(ast, Negative)->value; type_t *t = get_type(env, value); - if (t->tag == IntType || t->tag == NumType) - return t; + if (t->tag == IntType || t->tag == NumType) return t; binding_t *b = get_namespace_binding(env, value, "negative"); if (b && b->type->tag == FunctionType) { DeclareMatch(fn, b->type, FunctionType); - if (fn->args && type_eq(t, get_arg_type(env, fn->args)) && type_eq(t, fn->ret)) - return t; + if (fn->args && type_eq(t, get_arg_type(env, fn->args)) && type_eq(t, fn->ret)) return t; } code_err(ast, "I don't know how to get the negative value of type ", type_to_str(t)); } case Not: { type_t *t = get_type(env, Match(ast, Not)->value); - if (t->tag == IntType || t->tag == NumType || t->tag == BoolType) - return t; - if (t->tag == OptionalType) - return Type(BoolType); + if (t->tag == IntType || t->tag == NumType || t->tag == BoolType) return t; + if (t->tag == OptionalType) return Type(BoolType); ast_t *value = Match(ast, Not)->value; binding_t *b = get_namespace_binding(env, value, "negated"); if (b && b->type->tag == FunctionType) { DeclareMatch(fn, b->type, FunctionType); - if (fn->args && type_eq(t, get_arg_type(env, fn->args)) && type_eq(t, fn->ret)) - return t; + if (fn->args && type_eq(t, get_arg_type(env, fn->args)) && type_eq(t, fn->ret)) return t; } - code_err(ast, "I only know how to get 'not' of boolean, numeric, and optional pointer types, not ", type_to_str(t)); + code_err(ast, "I only know how to get 'not' of boolean, numeric, and optional pointer types, not ", + type_to_str(t)); } case Or: { binary_operands_t binop = BINARY_OPERANDS(ast); @@ -1103,13 +1126,10 @@ type_t *get_type(env_t *env, ast_t *ast) type_t *rhs_t = get_type(env, binop.rhs); type_t *lhs_val = value_type(lhs_t), *rhs_val = value_type(rhs_t); - if (type_eq(lhs_val, rhs_val) && lhs_val->tag == SetType) - return lhs_val; + if (type_eq(lhs_val, rhs_val) && lhs_val->tag == SetType) return lhs_val; - if (binop.lhs->tag == Int && is_int_type(rhs_t)) - return rhs_t; - else if (binop.rhs->tag == Int && is_int_type(lhs_t)) - return lhs_t; + if (binop.lhs->tag == Int && is_int_type(rhs_t)) return rhs_t; + else if (binop.rhs->tag == Int && is_int_type(lhs_t)) return lhs_t; // `opt? or (x == y)` / `(x == y) or opt?` is a boolean conditional: if ((lhs_t->tag == OptionalType && rhs_t->tag == BoolType) @@ -1126,22 +1146,20 @@ type_t *get_type(env_t *env, ast_t *ast) if (rhs_t->tag == OptionalType) { type_t *result = most_complete_type(lhs_t, rhs_t); if (result == NULL) - code_err(ast, "I could not determine the type of ", type_to_str(lhs_t), " `or` ", type_to_str(rhs_t)); + code_err(ast, "I could not determine the type of ", type_to_str(lhs_t), " `or` ", + type_to_str(rhs_t)); return result; } else if (rhs_t->tag == AbortType || rhs_t->tag == ReturnType) { return Match(lhs_t, OptionalType)->type; } type_t *non_opt = Match(lhs_t, OptionalType)->type; non_opt = most_complete_type(non_opt, rhs_t); - if (non_opt != NULL) - return non_opt; + if (non_opt != NULL) return non_opt; } else if ((is_numeric_type(lhs_t) || lhs_t->tag == BoolType) - && (is_numeric_type(rhs_t) || rhs_t->tag == BoolType) - && lhs_t->tag != NumType && rhs_t->tag != NumType) { - if (can_compile_to_type(env, binop.rhs, lhs_t)) - return lhs_t; - else if (can_compile_to_type(env, binop.lhs, rhs_t)) - return rhs_t; + && (is_numeric_type(rhs_t) || rhs_t->tag == BoolType) && lhs_t->tag != NumType + && rhs_t->tag != NumType) { + if (can_compile_to_type(env, binop.rhs, lhs_t)) return lhs_t; + else if (can_compile_to_type(env, binop.lhs, rhs_t)) return rhs_t; } else if (lhs_t->tag == SetType && rhs_t->tag == SetType && type_eq(lhs_t, rhs_t)) { return lhs_t; } @@ -1153,13 +1171,10 @@ type_t *get_type(env_t *env, ast_t *ast) type_t *rhs_t = get_type(env, binop.rhs); type_t *lhs_val = value_type(lhs_t), *rhs_val = value_type(rhs_t); - if (type_eq(lhs_val, rhs_val) && lhs_val->tag == SetType) - return lhs_val; + if (type_eq(lhs_val, rhs_val) && lhs_val->tag == SetType) return lhs_val; - if (binop.lhs->tag == Int && is_int_type(rhs_t)) - return rhs_t; - else if (binop.rhs->tag == Int && is_int_type(lhs_t)) - return lhs_t; + if (binop.lhs->tag == Int && is_int_type(rhs_t)) return rhs_t; + else if (binop.rhs->tag == Int && is_int_type(lhs_t)) return lhs_t; // `and` between optionals/bools is a boolean expression like `if opt? and opt?:` or `if x > 0 and opt?:` if ((lhs_t->tag == OptionalType || lhs_t->tag == BoolType) @@ -1173,17 +1188,15 @@ type_t *get_type(env_t *env, ast_t *ast) } // Bitwise AND: - if ((is_numeric_type(lhs_t) || lhs_t->tag == BoolType) - && (is_numeric_type(rhs_t) || rhs_t->tag == BoolType) + if ((is_numeric_type(lhs_t) || lhs_t->tag == BoolType) && (is_numeric_type(rhs_t) || rhs_t->tag == BoolType) && lhs_t->tag != NumType && rhs_t->tag != NumType) { - if (can_compile_to_type(env, binop.rhs, lhs_t)) - return lhs_t; - else if (can_compile_to_type(env, binop.lhs, rhs_t)) - return rhs_t; + if (can_compile_to_type(env, binop.rhs, lhs_t)) return lhs_t; + else if (can_compile_to_type(env, binop.lhs, rhs_t)) return rhs_t; } else if (lhs_t->tag == SetType && rhs_t->tag == SetType && type_eq(lhs_t, rhs_t)) { return lhs_t; } - code_err(ast, "I couldn't figure out how to do `and` between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, "I couldn't figure out how to do `and` between ", type_to_str(lhs_t), " and ", + type_to_str(rhs_t)); } case Xor: { binary_operands_t binop = BINARY_OPERANDS(ast); @@ -1191,13 +1204,10 @@ type_t *get_type(env_t *env, ast_t *ast) type_t *rhs_t = get_type(env, binop.rhs); type_t *lhs_val = value_type(lhs_t), *rhs_val = value_type(rhs_t); - if (type_eq(lhs_val, rhs_val) && lhs_val->tag == SetType) - return lhs_val; + if (type_eq(lhs_val, rhs_val) && lhs_val->tag == SetType) return lhs_val; - if (binop.lhs->tag == Int && is_int_type(rhs_t)) - return rhs_t; - else if (binop.rhs->tag == Int && is_int_type(lhs_t)) - return lhs_t; + if (binop.lhs->tag == Int && is_int_type(rhs_t)) return rhs_t; + else if (binop.rhs->tag == Int && is_int_type(lhs_t)) return lhs_t; // `xor` between optionals/bools is a boolean expression like `if opt? xor opt?:` or `if x > 0 xor opt?:` if ((lhs_t->tag == OptionalType || lhs_t->tag == BoolType) @@ -1211,45 +1221,55 @@ type_t *get_type(env_t *env, ast_t *ast) } // Bitwise XOR: - if ((is_numeric_type(lhs_t) || lhs_t->tag == BoolType) - && (is_numeric_type(rhs_t) || rhs_t->tag == BoolType) + if ((is_numeric_type(lhs_t) || lhs_t->tag == BoolType) && (is_numeric_type(rhs_t) || rhs_t->tag == BoolType) && lhs_t->tag != NumType && rhs_t->tag != NumType) { - if (can_compile_to_type(env, binop.rhs, lhs_t)) - return lhs_t; - else if (can_compile_to_type(env, binop.lhs, rhs_t)) - return rhs_t; + if (can_compile_to_type(env, binop.rhs, lhs_t)) return lhs_t; + else if (can_compile_to_type(env, binop.lhs, rhs_t)) return rhs_t; } else if (lhs_t->tag == SetType && rhs_t->tag == SetType && type_eq(lhs_t, rhs_t)) { return lhs_t; } - code_err(ast, "I couldn't figure out how to do `xor` between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, "I couldn't figure out how to do `xor` between ", type_to_str(lhs_t), " and ", + type_to_str(rhs_t)); } case Compare: - case Equals: case NotEquals: case LessThan: case LessThanOrEquals: case GreaterThan: case GreaterThanOrEquals: { + case Equals: + case NotEquals: + case LessThan: + case LessThanOrEquals: + case GreaterThan: + case GreaterThanOrEquals: { binary_operands_t binop = BINARY_OPERANDS(ast); type_t *lhs_t = get_type(env, binop.lhs); type_t *rhs_t = get_type(env, binop.rhs); - if ((binop.lhs->tag == Int && is_numeric_type(rhs_t)) - || (binop.rhs->tag == Int && is_numeric_type(lhs_t)) - || can_compile_to_type(env, binop.rhs, lhs_t) - || can_compile_to_type(env, binop.lhs, rhs_t)) - return ast->tag == Compare ? Type(IntType, .bits=TYPE_IBITS32) : Type(BoolType); + if ((binop.lhs->tag == Int && is_numeric_type(rhs_t)) || (binop.rhs->tag == Int && is_numeric_type(lhs_t)) + || can_compile_to_type(env, binop.rhs, lhs_t) || can_compile_to_type(env, binop.lhs, rhs_t)) + return ast->tag == Compare ? Type(IntType, .bits = TYPE_IBITS32) : Type(BoolType); code_err(ast, "I don't know how to compare ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); } - case Power: case Multiply: case Divide: case Mod: case Mod1: case Plus: case Minus: case LeftShift: - case UnsignedLeftShift: case RightShift: case UnsignedRightShift: { + case Power: + case Multiply: + case Divide: + case Mod: + case Mod1: + case Plus: + case Minus: + case LeftShift: + case UnsignedLeftShift: + case RightShift: + case UnsignedRightShift: { binary_operands_t binop = BINARY_OPERANDS(ast); type_t *lhs_t = get_type(env, binop.lhs); type_t *rhs_t = get_type(env, binop.rhs); if (ast->tag == Minus) { type_t *lhs_val = value_type(lhs_t), *rhs_val = value_type(rhs_t); - if (type_eq(lhs_val, rhs_val) && lhs_val->tag == SetType) - return lhs_val; + if (type_eq(lhs_val, rhs_val) && lhs_val->tag == SetType) return lhs_val; } - if (ast->tag == LeftShift || ast->tag == UnsignedLeftShift || ast->tag == RightShift || ast->tag == UnsignedRightShift) { + if (ast->tag == LeftShift || ast->tag == UnsignedLeftShift || ast->tag == RightShift + || ast->tag == UnsignedRightShift) { if (!is_int_type(rhs_t)) code_err(binop.rhs, "I only know how to do bit shifting by integer amounts, not ", type_to_str(rhs_t)); } @@ -1279,9 +1299,8 @@ type_t *get_type(env_t *env, ast_t *ast) if (b && b->type->tag == FunctionType) { DeclareMatch(fn, b->type, FunctionType); if (type_eq(fn->ret, rhs_t)) { - arg_ast_t *args = new(arg_ast_t, .value=binop.rhs, .next=new(arg_ast_t, .value=binop.lhs)); - if (is_valid_call(env, fn->args, args, true)) - return rhs_t; + arg_ast_t *args = new (arg_ast_t, .value = binop.rhs, .next = new (arg_ast_t, .value = binop.lhs)); + if (is_valid_call(env, fn->args, args, true)) return rhs_t; } } } else if (ast->tag == Multiply && is_numeric_type(rhs_t)) { @@ -1289,9 +1308,8 @@ type_t *get_type(env_t *env, ast_t *ast) if (b && b->type->tag == FunctionType) { DeclareMatch(fn, b->type, FunctionType); if (type_eq(fn->ret, lhs_t)) { - arg_ast_t *args = new(arg_ast_t, .value=binop.lhs, .next=new(arg_ast_t, .value=binop.rhs)); - if (is_valid_call(env, fn->args, args, true)) - return lhs_t; + arg_ast_t *args = new (arg_ast_t, .value = binop.lhs, .next = new (arg_ast_t, .value = binop.rhs)); + if (is_valid_call(env, fn->args, args, true)) return lhs_t; } } } else if ((ast->tag == Divide || ast->tag == Mod || ast->tag == Mod1) && is_numeric_type(rhs_t)) { @@ -1299,39 +1317,43 @@ type_t *get_type(env_t *env, ast_t *ast) if (b && b->type->tag == FunctionType) { DeclareMatch(fn, b->type, FunctionType); if (type_eq(fn->ret, lhs_t)) { - arg_ast_t *args = new(arg_ast_t, .value=binop.lhs, .next=new(arg_ast_t, .value=binop.rhs)); - if (is_valid_call(env, fn->args, args, true)) - return lhs_t; + arg_ast_t *args = new (arg_ast_t, .value = binop.lhs, .next = new (arg_ast_t, .value = binop.rhs)); + if (is_valid_call(env, fn->args, args, true)) return lhs_t; } } } - type_t *overall_t = (can_compile_to_type(env, binop.rhs, lhs_t) ? lhs_t : (can_compile_to_type(env, binop.lhs, rhs_t) ? rhs_t : NULL)); + type_t *overall_t = + (can_compile_to_type(env, binop.rhs, lhs_t) ? lhs_t + : (can_compile_to_type(env, binop.lhs, rhs_t) ? rhs_t : NULL)); if (overall_t == NULL) - code_err(ast, "I don't know how to do math operations between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, "I don't know how to do math operations between ", type_to_str(lhs_t), " and ", + type_to_str(rhs_t)); binding_t *b = get_metamethod_binding(env, ast->tag, binop.lhs, binop.rhs, overall_t); if (b) return overall_t; - if (is_numeric_type(lhs_t) && is_numeric_type(rhs_t)) - return overall_t; + if (is_numeric_type(lhs_t) && is_numeric_type(rhs_t)) return overall_t; - code_err(ast, "I don't know how to do math operations between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, "I don't know how to do math operations between ", type_to_str(lhs_t), " and ", + type_to_str(rhs_t)); } case Concat: { binary_operands_t binop = BINARY_OPERANDS(ast); type_t *lhs_t = get_type(env, binop.lhs); type_t *rhs_t = get_type(env, binop.rhs); - type_t *overall_t = (can_compile_to_type(env, binop.rhs, lhs_t) ? lhs_t : (can_compile_to_type(env, binop.lhs, rhs_t) ? rhs_t : NULL)); + type_t *overall_t = + (can_compile_to_type(env, binop.rhs, lhs_t) ? lhs_t + : (can_compile_to_type(env, binop.lhs, rhs_t) ? rhs_t : NULL)); if (overall_t == NULL) - code_err(ast, "I don't know how to do operations between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, "I don't know how to do operations between ", type_to_str(lhs_t), " and ", + type_to_str(rhs_t)); binding_t *b = get_metamethod_binding(env, ast->tag, binop.lhs, binop.rhs, overall_t); if (b) return overall_t; - if (overall_t->tag == ListType || overall_t->tag == SetType || overall_t->tag == TextType) - return overall_t; + if (overall_t->tag == ListType || overall_t->tag == SetType || overall_t->tag == TextType) return overall_t; code_err(ast, "I don't know how to do concatenation between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); } @@ -1341,8 +1363,9 @@ type_t *get_type(env_t *env, ast_t *ast) type_t *iter_t = get_type(env, reduction->iter); if (reduction->op == Equals || reduction->op == NotEquals || reduction->op == LessThan - || reduction->op == LessThanOrEquals || reduction->op == GreaterThan || reduction->op == GreaterThanOrEquals) - return Type(OptionalType, .type=Type(BoolType)); + || reduction->op == LessThanOrEquals || reduction->op == GreaterThan + || reduction->op == GreaterThanOrEquals) + return Type(OptionalType, .type = Type(BoolType)); type_t *iterated = get_iterated_type(iter_t); if (!iterated) @@ -1352,10 +1375,11 @@ type_t *get_type(env_t *env, ast_t *ast) set_binding(item_scope, "$", iterated, EMPTY_TEXT); iterated = get_type(item_scope, reduction->key); } - return iterated->tag == OptionalType ? iterated : Type(OptionalType, .type=iterated); + return iterated->tag == OptionalType ? iterated : Type(OptionalType, .type = iterated); } - case Min: case Max: { + case Min: + case Max: { // Unsafe! These types *should* have the same fields and this saves a lot of duplicate code: ast_t *lhs = ast->__data.Min.lhs, *rhs = ast->__data.Min.rhs; // Okay safe again @@ -1363,7 +1387,8 @@ type_t *get_type(env_t *env, ast_t *ast) type_t *lhs_t = get_type(env, lhs), *rhs_t = get_type(env, rhs); type_t *t = type_or_type(lhs_t, rhs_t); if (!t) - code_err(ast, "The two sides of this operation are not compatible: ", type_to_str(lhs_t), " vs ", type_to_str(rhs_t)); + code_err(ast, "The two sides of this operation are not compatible: ", type_to_str(lhs_t), " vs ", + type_to_str(rhs_t)); return t; } @@ -1373,24 +1398,21 @@ type_t *get_type(env_t *env, ast_t *ast) env_t *scope = fresh_scope(env); // For now, just use closed variables in scope normally for (arg_ast_t *arg = lambda->args; arg; arg = arg->next) { type_t *t = get_arg_ast_type(env, arg); - args = new(arg_t, .name=arg->name, .type=t, .next=args); + args = new (arg_t, .name = arg->name, .type = t, .next = args); set_binding(scope, arg->name, t, EMPTY_TEXT); } REVERSE_LIST(args); type_t *ret = get_type(scope, lambda->body); - if (ret->tag == ReturnType) - ret = Match(ret, ReturnType)->ret; - if (ret->tag == AbortType) - ret = Type(VoidType); + if (ret->tag == ReturnType) ret = Match(ret, ReturnType)->ret; + if (ret->tag == AbortType) ret = Type(VoidType); if (ret->tag == OptionalType && !Match(ret, OptionalType)->type) code_err(lambda->body, "This function doesn't return a specific optional type"); if (lambda->ret_type) { type_t *declared = parse_type_ast(env, lambda->ret_type); - if (can_promote(ret, declared)) - ret = declared; + if (can_promote(ret, declared)) ret = declared; else code_err(ast, "This function was declared to return a value of type ", type_to_str(declared), ", but actually returns a value of type ", type_to_str(ret)); @@ -1398,17 +1420,21 @@ type_t *get_type(env_t *env, ast_t *ast) if (has_stack_memory(ret)) code_err(ast, "Functions can't return stack references because the reference may outlive its stack frame."); - return Type(ClosureType, Type(FunctionType, .args=args, .ret=ret)); + return Type(ClosureType, Type(FunctionType, .args = args, .ret = ret)); } - case FunctionDef: case ConvertDef: case StructDef: case EnumDef: case LangDef: case Extend: { + case FunctionDef: + case ConvertDef: + case StructDef: + case EnumDef: + case LangDef: + case Extend: { return Type(VoidType); } case If: { DeclareMatch(if_, ast, If); - if (!if_->else_body) - return Type(VoidType); + if (!if_->else_body) return Type(VoidType); env_t *truthy_scope = env; env_t *falsey_scope = env; @@ -1421,17 +1447,14 @@ type_t *get_type(env_t *env, ast_t *ast) truthy_scope = fresh_scope(env); if (condition_type->tag == OptionalType) - set_binding(truthy_scope, varname, - Match(condition_type, OptionalType)->type, EMPTY_TEXT); - else - set_binding(truthy_scope, varname, condition_type, EMPTY_TEXT); + set_binding(truthy_scope, varname, Match(condition_type, OptionalType)->type, EMPTY_TEXT); + else set_binding(truthy_scope, varname, condition_type, EMPTY_TEXT); } else if (if_->condition->tag == Var) { type_t *condition_type = get_type(env, if_->condition); if (condition_type->tag == OptionalType) { truthy_scope = fresh_scope(env); const char *varname = Match(if_->condition, Var)->name; - set_binding(truthy_scope, varname, - Match(condition_type, OptionalType)->type, EMPTY_TEXT); + set_binding(truthy_scope, varname, Match(condition_type, OptionalType)->type, EMPTY_TEXT); } } @@ -1439,8 +1462,7 @@ type_t *get_type(env_t *env, ast_t *ast) type_t *false_t = get_type(falsey_scope, if_->else_body); type_t *t_either = type_or_type(true_t, false_t); if (!t_either) - code_err(if_->else_body, - "I was expecting this block to have a ", type_to_str(true_t), + code_err(if_->else_body, "I was expecting this block to have a ", type_to_str(true_t), " value (based on earlier clauses), but it actually has a ", type_to_str(false_t), " value."); return t_either; } @@ -1453,15 +1475,13 @@ type_t *get_type(env_t *env, ast_t *ast) for (when_clause_t *clause = when->clauses; clause; clause = clause->next) { t = type_or_type(t, get_type(env, clause->body)); } - if (when->else_body) - t = type_or_type(t, get_type(env, when->else_body)); - else if (t && t->tag != OptionalType) - t = Type(OptionalType, .type=t); + if (when->else_body) t = type_or_type(t, get_type(env, when->else_body)); + else if (t && t->tag != OptionalType) t = Type(OptionalType, .type = t); return t; } type_t *overall_t = NULL; - tag_t * const tags = Match(subject_t, EnumType)->tags; + tag_t *const tags = Match(subject_t, EnumType)->tags; typedef struct match_s { tag_t *tag; @@ -1470,22 +1490,19 @@ type_t *get_type(env_t *env, ast_t *ast) } match_t; match_t *matches = NULL; for (tag_t *tag = tags; tag; tag = tag->next) - matches = new(match_t, .tag=tag, .handled=false, .next=matches); + matches = new (match_t, .tag = tag, .handled = false, .next = matches); for (when_clause_t *clause = when->clauses; clause; clause = clause->next) { const char *tag_name; - if (clause->pattern->tag == Var) - tag_name = Match(clause->pattern, Var)->name; + if (clause->pattern->tag == Var) tag_name = Match(clause->pattern, Var)->name; else if (clause->pattern->tag == FunctionCall && Match(clause->pattern, FunctionCall)->fn->tag == Var) tag_name = Match(Match(clause->pattern, FunctionCall)->fn, Var)->name; - else - code_err(clause->pattern, "This is not a valid pattern for a ", type_to_str(subject_t), " enum"); + else code_err(clause->pattern, "This is not a valid pattern for a ", type_to_str(subject_t), " enum"); Text_t valid_tags = EMPTY_TEXT; for (match_t *m = matches; m; m = m->next) { if (streq(m->tag->name, tag_name)) { - if (m->handled) - code_err(clause->pattern, "This tag was already handled earlier"); + if (m->handled) code_err(clause->pattern, "This tag was already handled earlier"); m->handled = true; goto found_matching_tag; } @@ -1493,9 +1510,9 @@ type_t *get_type(env_t *env, ast_t *ast) valid_tags = Texts(valid_tags, m->tag->name); } - code_err(clause->pattern, "There is no tag '", tag_name, - "' for the type ", type_to_str(subject_t), " (valid tags: ", valid_tags, ")"); - found_matching_tag:; + code_err(clause->pattern, "There is no tag '", tag_name, "' for the type ", type_to_str(subject_t), + " (valid tags: ", valid_tags, ")"); + found_matching_tag:; } for (when_clause_t *clause = when->clauses; clause; clause = clause->next) { @@ -1524,27 +1541,27 @@ type_t *get_type(env_t *env, ast_t *ast) type_t *else_t = get_type(env, when->else_body); type_t *merged = type_or_type(overall_t, else_t); if (!merged) - code_err(when->else_body, - "I was expecting this block to have a ", type_to_str(overall_t), + code_err(when->else_body, "I was expecting this block to have a ", type_to_str(overall_t), " value (based on earlier clauses), but it actually has a ", type_to_str(else_t), " value."); return merged; } else { Text_t unhandled = EMPTY_TEXT; for (match_t *m = matches; m; m = m->next) { if (!m->handled) - unhandled = unhandled.length > 0 ? Texts(unhandled, ", ", m->tag->name) : Text$from_str(m->tag->name); + unhandled = + unhandled.length > 0 ? Texts(unhandled, ", ", m->tag->name) : Text$from_str(m->tag->name); } - if (unhandled.length > 0) - code_err(ast, "This 'when' statement doesn't handle the tags: ", unhandled); + if (unhandled.length > 0) code_err(ast, "This 'when' statement doesn't handle the tags: ", unhandled); return overall_t; } } - case While: case Repeat: case For: return Type(VoidType); + case While: + case Repeat: + case For: return Type(VoidType); case InlineCCode: { DeclareMatch(inline_code, ast, InlineCCode); - if (inline_code->type) - return inline_code->type; + if (inline_code->type) return inline_code->type; type_ast_t *type_ast = inline_code->type_ast; return type_ast ? parse_type_ast(env, type_ast) : Type(VoidType); } @@ -1559,35 +1576,37 @@ type_t *get_type(env_t *env, ast_t *ast) return NULL; } -PUREFUNC bool is_discardable(env_t *env, ast_t *ast) -{ +PUREFUNC bool is_discardable(env_t *env, ast_t *ast) { switch (ast->tag) { - case UPDATE_CASES: case Assign: case Declare: case FunctionDef: case ConvertDef: case StructDef: case EnumDef: - case LangDef: case Use: case Extend: - return true; + case UPDATE_CASES: + case Assign: + case Declare: + case FunctionDef: + case ConvertDef: + case StructDef: + case EnumDef: + case LangDef: + case Use: + case Extend: return true; default: break; } type_t *t = get_type(env, ast); return (t->tag == VoidType || t->tag == AbortType || t->tag == ReturnType); } -type_t *get_arg_ast_type(env_t *env, arg_ast_t *arg) -{ +type_t *get_arg_ast_type(env_t *env, arg_ast_t *arg) { assert(arg->type || arg->value); - if (arg->type) - return parse_type_ast(env, arg->type); + if (arg->type) return parse_type_ast(env, arg->type); return get_type(env, arg->value); } -type_t *get_arg_type(env_t *env, arg_t *arg) -{ +type_t *get_arg_type(env_t *env, arg_t *arg) { assert(arg->type || arg->default_val); if (arg->type) return arg->type; return get_type(env, arg->default_val); } -static Table_t *get_arg_bindings_with_promotion(env_t *env, arg_t *spec_args, arg_ast_t *call_args) -{ +static Table_t *get_arg_bindings_with_promotion(env_t *env, arg_t *spec_args, arg_ast_t *call_args) { Table_t used_args = {}; // Populate keyword args: @@ -1597,13 +1616,12 @@ static Table_t *get_arg_bindings_with_promotion(env_t *env, arg_t *spec_args, ar for (arg_t *spec_arg = spec_args; spec_arg; spec_arg = spec_arg->next) { if (!streq(call_arg->name, spec_arg->name)) continue; type_t *spec_type = get_arg_type(env, spec_arg); - if (!can_compile_to_type(env, call_arg->value, spec_type)) - return NULL; + if (!can_compile_to_type(env, call_arg->value, spec_type)) return NULL; Table$str_set(&used_args, call_arg->name, call_arg); goto next_call_arg; } return NULL; - next_call_arg:; + next_call_arg:; } arg_ast_t *unused_args = call_args; @@ -1615,34 +1633,31 @@ static Table_t *get_arg_bindings_with_promotion(env_t *env, arg_t *spec_args, ar for (; unused_args; unused_args = unused_args->next) { if (unused_args->name) continue; // Already handled the keyword args if (!can_compile_to_type(env, unused_args->value, spec_type)) - return NULL; // Positional arg trying to fill in + return NULL; // Positional arg trying to fill in Table$str_set(&used_args, spec_arg->name, unused_args); unused_args = unused_args->next; goto found_it; } - if (spec_arg->default_val) - goto found_it; + if (spec_arg->default_val) goto found_it; return NULL; - found_it: continue; + found_it: + continue; } while (unused_args && unused_args->name) unused_args = unused_args->next; - if (unused_args != NULL) - return NULL; + if (unused_args != NULL) return NULL; - Table_t *ret = new(Table_t); + Table_t *ret = new (Table_t); *ret = used_args; return ret; } -Table_t *get_arg_bindings(env_t *env, arg_t *spec_args, arg_ast_t *call_args, bool promotion_allowed) -{ - if (promotion_allowed) - return get_arg_bindings_with_promotion(env, spec_args, call_args); +Table_t *get_arg_bindings(env_t *env, arg_t *spec_args, arg_ast_t *call_args, bool promotion_allowed) { + if (promotion_allowed) return get_arg_bindings_with_promotion(env, spec_args, call_args); Table_t used_args = {}; @@ -1654,15 +1669,15 @@ Table_t *get_arg_bindings(env_t *env, arg_t *spec_args, arg_ast_t *call_args, bo for (arg_t *spec_arg = spec_args; spec_arg; spec_arg = spec_arg->next) { if (!streq(call_arg->name, spec_arg->name)) continue; type_t *spec_type = get_arg_type(env, spec_arg); - type_t *complete_call_type = is_incomplete_type(call_type) ? most_complete_type(call_type, spec_type) : call_type; + type_t *complete_call_type = + is_incomplete_type(call_type) ? most_complete_type(call_type, spec_type) : call_type; if (!complete_call_type) return NULL; - if (!type_eq(complete_call_type, spec_type)) - return NULL; + if (!type_eq(complete_call_type, spec_type)) return NULL; Table$str_set(&used_args, call_arg->name, call_arg); goto next_call_arg; } return NULL; - next_call_arg:; + next_call_arg:; } arg_ast_t *unused_args = call_args; @@ -1674,41 +1689,38 @@ Table_t *get_arg_bindings(env_t *env, arg_t *spec_args, arg_ast_t *call_args, bo for (; unused_args; unused_args = unused_args->next) { if (unused_args->name) continue; // Already handled the keyword args type_t *call_type = get_arg_ast_type(env, unused_args); - type_t *complete_call_type = is_incomplete_type(call_type) ? most_complete_type(call_type, spec_type) : call_type; + type_t *complete_call_type = + is_incomplete_type(call_type) ? most_complete_type(call_type, spec_type) : call_type; if (!complete_call_type) return NULL; - if (!type_eq(complete_call_type, spec_type)) - return NULL; // Positional arg trying to fill in + if (!type_eq(complete_call_type, spec_type)) return NULL; // Positional arg trying to fill in Table$str_set(&used_args, spec_arg->name, unused_args); unused_args = unused_args->next; goto found_it; } - if (spec_arg->default_val) - goto found_it; + if (spec_arg->default_val) goto found_it; return NULL; - found_it: continue; + found_it: + continue; } while (unused_args && unused_args->name) unused_args = unused_args->next; - if (unused_args != NULL) - return NULL; + if (unused_args != NULL) return NULL; - Table_t *ret = new(Table_t); + Table_t *ret = new (Table_t); *ret = used_args; return ret; } -bool is_valid_call(env_t *env, arg_t *spec_args, arg_ast_t *call_args, bool promotion_allowed) -{ +bool is_valid_call(env_t *env, arg_t *spec_args, arg_ast_t *call_args, bool promotion_allowed) { Table_t *arg_bindings = get_arg_bindings(env, spec_args, call_args, promotion_allowed); return (arg_bindings != NULL); } -PUREFUNC bool can_be_mutated(env_t *env, ast_t *ast) -{ +PUREFUNC bool can_be_mutated(env_t *env, ast_t *ast) { switch (ast->tag) { case Var: return true; case InlineCCode: return true; @@ -1733,16 +1745,16 @@ PUREFUNC bool can_be_mutated(env_t *env, ast_t *ast) } } -type_t *parse_type_string(env_t *env, const char *str) -{ +type_t *parse_type_string(env_t *env, const char *str) { type_ast_t *ast = parse_type_str(str); return ast ? parse_type_ast(env, ast) : NULL; } -PUREFUNC bool is_constant(env_t *env, ast_t *ast) -{ +PUREFUNC bool is_constant(env_t *env, ast_t *ast) { switch (ast->tag) { - case Bool: case Num: case None: return true; + case Bool: + case Num: + case None: return true; case Int: { DeclareMatch(info, ast, Int); Int_t int_val = Int$parse(Text$from_str(info->str), NULL); @@ -1751,17 +1763,16 @@ PUREFUNC bool is_constant(env_t *env, ast_t *ast) } case TextJoin: { DeclareMatch(text, ast, TextJoin); - if (!text->children) return true; // Empty string, OK + if (!text->children) return true; // Empty string, OK if (text->children->next) return false; // Concatenation, not constant return is_constant(env, text->children->ast); } case TextLiteral: { - Text_t literal = Match(ast, TextLiteral)->text; + Text_t literal = Match(ast, TextLiteral)->text; TextIter_t state = NEW_TEXT_ITER_STATE(literal); for (int64_t i = 0; i < literal.length; i++) { int32_t g = Text$get_grapheme_fast(&state, i); - if (g < 0 || g > 127 || !isascii(g)) - return false; + if (g < 0 || g > 127 || !isascii(g)) return false; } return true; // Literal ASCII string, OK } @@ -1770,10 +1781,12 @@ PUREFUNC bool is_constant(env_t *env, ast_t *ast) case BINOP_CASES: { binary_operands_t binop = BINARY_OPERANDS(ast); switch (ast->tag) { - case Power: case Concat: case Min: case Max: case Compare: - return false; - default: - return is_constant(env, binop.lhs) && is_constant(env, binop.rhs); + case Power: + case Concat: + case Min: + case Max: + case Compare: return false; + default: return is_constant(env, binop.lhs) && is_constant(env, binop.rhs); } } case Use: return true; @@ -1783,39 +1796,32 @@ PUREFUNC bool is_constant(env_t *env, ast_t *ast) } } -PUREFUNC bool can_compile_to_type(env_t *env, ast_t *ast, type_t *needed) -{ - if (is_incomplete_type(needed)) - return false; +PUREFUNC bool can_compile_to_type(env_t *env, ast_t *ast, type_t *needed) { + if (is_incomplete_type(needed)) return false; - if (needed->tag == OptionalType && ast->tag == None) - return true; + if (needed->tag == OptionalType && ast->tag == None) return true; type_t *actual = get_type(env, ast); - if (actual->tag == OptionalType && needed->tag == OptionalType) - return can_promote(actual, needed); + if (actual->tag == OptionalType && needed->tag == OptionalType) return can_promote(actual, needed); needed = non_optional(needed); if (needed->tag == ListType && ast->tag == List) { type_t *item_type = Match(needed, ListType)->item_type; for (ast_list_t *item = Match(ast, List)->items; item; item = item->next) { - if (!can_compile_to_type(env, item->ast, item_type)) - return false; + if (!can_compile_to_type(env, item->ast, item_type)) return false; } return true; } else if (needed->tag == SetType && ast->tag == Set) { type_t *item_type = Match(needed, SetType)->item_type; for (ast_list_t *item = Match(ast, Set)->items; item; item = item->next) { - if (!can_compile_to_type(env, item->ast, item_type)) - return false; + if (!can_compile_to_type(env, item->ast, item_type)) return false; } return true; } else if (needed->tag == TableType && ast->tag == Table) { type_t *key_type = Match(needed, TableType)->key_type; type_t *value_type = Match(needed, TableType)->value_type; for (ast_list_t *entry = Match(ast, Table)->entries; entry; entry = entry->next) { - if (entry->ast->tag != TableEntry) - continue; // TODO: fix this + if (entry->ast->tag != TableEntry) continue; // TODO: fix this DeclareMatch(e, entry->ast, TableEntry); if (!can_compile_to_type(env, e->key, key_type) || !can_compile_to_type(env, e->value, value_type)) return false; @@ -1827,8 +1833,7 @@ PUREFUNC bool can_compile_to_type(env_t *env, ast_t *ast, type_t *needed) return !ptr->is_stack && can_compile_to_type(env, Match(ast, HeapAllocate)->value, ptr->pointed); else if (ast->tag == StackReference) return ptr->is_stack && can_compile_to_type(env, Match(ast, StackReference)->value, ptr->pointed); - else - return can_promote(actual, needed); + else return can_promote(actual, needed); } else if (actual->tag == OptionalType && needed->tag != OptionalType) { return false; } else { diff --git a/src/types.c b/src/types.c index 3e39c682..e60483aa 100644 --- a/src/types.c +++ b/src/types.c @@ -13,92 +13,84 @@ #include "types.h" Text_t type_to_text(type_t *t) { - if (!t) - return Text("(Unknown type)"); + if (!t) return Text("(Unknown type)"); switch (t->tag) { - case UnknownType: return Text("???"); - case AbortType: return Text("Abort"); - case ReturnType: { - type_t *ret = Match(t, ReturnType)->ret; - return Texts("Return(", ret ? type_to_text(ret) : Text("Void"), ")"); - } - case VoidType: return Text("Void"); - case MemoryType: return Text("Memory"); - case BoolType: return Text("Bool"); - case ByteType: return Text("Byte"); - case CStringType: return Text("CString"); - case TextType: return Match(t, TextType)->lang ? Text$from_str(Match(t, TextType)->lang) : Text("Text"); - case BigIntType: return Text("Int"); - case IntType: return Texts("Int", String(Match(t, IntType)->bits)); - case NumType: return Match(t, NumType)->bits == TYPE_NBITS32 ? Text("Num32") : Text("Num"); - case ListType: { - DeclareMatch(list, t, ListType); - return Texts("[", type_to_text(list->item_type), "]"); - } - case TableType: { - DeclareMatch(table, t, TableType); - return Texts("{", type_to_text(table->key_type), "=", type_to_text(table->value_type), "}"); - } - case SetType: { - DeclareMatch(set, t, SetType); - return Texts("{", type_to_text(set->item_type), "}"); - } - case ClosureType: { - return type_to_text(Match(t, ClosureType)->fn); - } - case FunctionType: { - Text_t c = Text("func("); - DeclareMatch(fn, t, FunctionType); - for (arg_t *arg = fn->args; arg; arg = arg->next) { - c = Texts(c, type_to_text(arg->type)); - if (arg->next) c = Texts(c, ","); - } - if (fn->ret && fn->ret->tag != VoidType) - c = Texts(c, fn->args ? " -> " : "-> ", type_to_text(fn->ret)); - c = Texts(c, ")"); - return c; - } - case StructType: { - DeclareMatch(struct_, t, StructType); - return Text$from_str(struct_->name); - } - case PointerType: { - DeclareMatch(ptr, t, PointerType); - Text_t sigil = ptr->is_stack ? Text("&") : Text("@"); - return Texts(sigil, type_to_text(ptr->pointed)); - } - case EnumType: { - DeclareMatch(tagged, t, EnumType); - return Text$from_str(tagged->name); - } - case OptionalType: { - type_t *opt = Match(t, OptionalType)->type; - if (opt) - return Texts(type_to_text(opt), "?"); - else - return Text("(Unknown optional type)"); - } - case TypeInfoType: { - return Texts("Type$info(", Match(t, TypeInfoType)->name, ")"); - } - case ModuleType: { - return Texts("Module(", Match(t, ModuleType)->name, ")"); - } - default: { - raise(SIGABRT); - return Texts("Unknown type: ", String(t->tag)); + case UnknownType: return Text("???"); + case AbortType: return Text("Abort"); + case ReturnType: { + type_t *ret = Match(t, ReturnType)->ret; + return Texts("Return(", ret ? type_to_text(ret) : Text("Void"), ")"); + } + case VoidType: return Text("Void"); + case MemoryType: return Text("Memory"); + case BoolType: return Text("Bool"); + case ByteType: return Text("Byte"); + case CStringType: return Text("CString"); + case TextType: return Match(t, TextType)->lang ? Text$from_str(Match(t, TextType)->lang) : Text("Text"); + case BigIntType: return Text("Int"); + case IntType: return Texts("Int", String(Match(t, IntType)->bits)); + case NumType: return Match(t, NumType)->bits == TYPE_NBITS32 ? Text("Num32") : Text("Num"); + case ListType: { + DeclareMatch(list, t, ListType); + return Texts("[", type_to_text(list->item_type), "]"); + } + case TableType: { + DeclareMatch(table, t, TableType); + return Texts("{", type_to_text(table->key_type), "=", type_to_text(table->value_type), "}"); + } + case SetType: { + DeclareMatch(set, t, SetType); + return Texts("{", type_to_text(set->item_type), "}"); + } + case ClosureType: { + return type_to_text(Match(t, ClosureType)->fn); + } + case FunctionType: { + Text_t c = Text("func("); + DeclareMatch(fn, t, FunctionType); + for (arg_t *arg = fn->args; arg; arg = arg->next) { + c = Texts(c, type_to_text(arg->type)); + if (arg->next) c = Texts(c, ","); } + if (fn->ret && fn->ret->tag != VoidType) c = Texts(c, fn->args ? " -> " : "-> ", type_to_text(fn->ret)); + c = Texts(c, ")"); + return c; + } + case StructType: { + DeclareMatch(struct_, t, StructType); + return Text$from_str(struct_->name); + } + case PointerType: { + DeclareMatch(ptr, t, PointerType); + Text_t sigil = ptr->is_stack ? Text("&") : Text("@"); + return Texts(sigil, type_to_text(ptr->pointed)); + } + case EnumType: { + DeclareMatch(tagged, t, EnumType); + return Text$from_str(tagged->name); + } + case OptionalType: { + type_t *opt = Match(t, OptionalType)->type; + if (opt) return Texts(type_to_text(opt), "?"); + else return Text("(Unknown optional type)"); + } + case TypeInfoType: { + return Texts("Type$info(", Match(t, TypeInfoType)->name, ")"); + } + case ModuleType: { + return Texts("Module(", Match(t, ModuleType)->name, ")"); + } + default: { + raise(SIGABRT); + return Texts("Unknown type: ", String(t->tag)); + } } } -const char *type_to_str(type_t *t) -{ - return Text$as_c_string(type_to_text(t)); -} +const char *type_to_str(type_t *t) { return Text$as_c_string(type_to_text(t)); } -PUREFUNC const char *get_type_name(type_t *t) -{ +PUREFUNC const char *get_type_name(type_t *t) { switch (t->tag) { case TextType: return Match(t, TextType)->lang; case StructType: return Match(t, StructType)->name; @@ -107,8 +99,7 @@ PUREFUNC const char *get_type_name(type_t *t) } } -bool type_eq(type_t *a, type_t *b) -{ +bool type_eq(type_t *a, type_t *b) { if (a == b) return true; if (!a && !b) return true; if (!a || !b) return false; @@ -116,11 +107,9 @@ bool type_eq(type_t *a, type_t *b) return Text$equal_values(type_to_text(a), type_to_text(b)); } -bool type_is_a(type_t *t, type_t *req) -{ +bool type_is_a(type_t *t, type_t *req) { if (type_eq(t, req)) return true; - if (req->tag == OptionalType && Match(req, OptionalType)->type) - return type_is_a(t, Match(req, OptionalType)->type); + if (req->tag == OptionalType && Match(req, OptionalType)->type) return type_is_a(t, Match(req, OptionalType)->type); if (t->tag == PointerType && req->tag == PointerType) { DeclareMatch(t_ptr, t, PointerType); DeclareMatch(req_ptr, req, PointerType); @@ -130,20 +119,15 @@ bool type_is_a(type_t *t, type_t *req) return false; } -type_t *non_optional(type_t *t) -{ - return t->tag == OptionalType ? Match(t, OptionalType)->type : t; -} +type_t *non_optional(type_t *t) { return t->tag == OptionalType ? Match(t, OptionalType)->type : t; } -PUREFUNC type_t *value_type(type_t *t) -{ +PUREFUNC type_t *value_type(type_t *t) { while (t->tag == PointerType) t = Match(t, PointerType)->pointed; return t; } -type_t *type_or_type(type_t *a, type_t *b) -{ +type_t *type_or_type(type_t *a, type_t *b) { if (!a) return b; if (!b) return a; if (a->tag == OptionalType && !Match(a, OptionalType)->type) @@ -151,12 +135,10 @@ type_t *type_or_type(type_t *a, type_t *b) if (b->tag == OptionalType && !Match(b, OptionalType)->type) return a->tag == OptionalType ? a : Type(OptionalType, a); if (a->tag == ReturnType && b->tag == ReturnType) - return Type(ReturnType, .ret=type_or_type(Match(a, ReturnType)->ret, Match(b, ReturnType)->ret)); + return Type(ReturnType, .ret = type_or_type(Match(a, ReturnType)->ret, Match(b, ReturnType)->ret)); - if (is_incomplete_type(a) && type_eq(b, most_complete_type(a, b))) - return b; - if (is_incomplete_type(b) && type_eq(a, most_complete_type(a, b))) - return a; + if (is_incomplete_type(a) && type_eq(b, most_complete_type(a, b))) return b; + if (is_incomplete_type(b) && type_eq(a, most_complete_type(a, b))) return a; if (type_is_a(b, a)) return a; if (type_is_a(a, b)) return b; @@ -164,7 +146,8 @@ type_t *type_or_type(type_t *a, type_t *b) if (b->tag == AbortType || b->tag == ReturnType) return non_optional(a); if ((a->tag == IntType || a->tag == NumType) && (b->tag == IntType || b->tag == NumType)) { switch (compare_precision(a, b)) { - case NUM_PRECISION_EQUAL: case NUM_PRECISION_MORE: return a; + case NUM_PRECISION_EQUAL: + case NUM_PRECISION_MORE: return a; case NUM_PRECISION_LESS: return b; default: return NULL; } @@ -173,12 +156,11 @@ type_t *type_or_type(type_t *a, type_t *b) return NULL; } -static PUREFUNC INLINE double type_min_magnitude(type_t *t) -{ +static PUREFUNC INLINE double type_min_magnitude(type_t *t) { switch (t->tag) { case BoolType: return (double)false; case ByteType: return 0; - case BigIntType: return -1./0.; + case BigIntType: return -1. / 0.; case IntType: { switch (Match(t, IntType)->bits) { case TYPE_IBITS8: return (double)INT8_MIN; @@ -188,17 +170,16 @@ static PUREFUNC INLINE double type_min_magnitude(type_t *t) default: errx(1, "Invalid integer bit size"); } } - case NumType: return -1./0.; + case NumType: return -1. / 0.; default: return (double)NAN; } } -static PUREFUNC INLINE double type_max_magnitude(type_t *t) -{ +static PUREFUNC INLINE double type_max_magnitude(type_t *t) { switch (t->tag) { case BoolType: return (double)true; case ByteType: return (double)UINT8_MAX; - case BigIntType: return 1./0.; + case BigIntType: return 1. / 0.; case IntType: { switch (Match(t, IntType)->bits) { case TYPE_IBITS8: return (double)INT8_MAX; @@ -208,36 +189,28 @@ static PUREFUNC INLINE double type_max_magnitude(type_t *t) default: errx(1, "Invalid integer bit size"); } } - case NumType: return 1./0.; + case NumType: return 1. / 0.; default: return (double)NAN; } } -PUREFUNC precision_cmp_e compare_precision(type_t *a, type_t *b) -{ - if (a == NULL || b == NULL) - return NUM_PRECISION_INCOMPARABLE; +PUREFUNC precision_cmp_e compare_precision(type_t *a, type_t *b) { + if (a == NULL || b == NULL) return NUM_PRECISION_INCOMPARABLE; - if (is_int_type(a) && b->tag == NumType) - return NUM_PRECISION_LESS; - else if (a->tag == NumType && is_int_type(b)) - return NUM_PRECISION_MORE; + if (is_int_type(a) && b->tag == NumType) return NUM_PRECISION_LESS; + else if (a->tag == NumType && is_int_type(b)) return NUM_PRECISION_MORE; - double a_min = type_min_magnitude(a), - b_min = type_min_magnitude(b), - a_max = type_max_magnitude(a), + double a_min = type_min_magnitude(a), b_min = type_min_magnitude(b), a_max = type_max_magnitude(a), b_max = type_max_magnitude(b); - if (isnan(a_min) || isnan(b_min) || isnan(a_max) || isnan(b_max)) - return NUM_PRECISION_INCOMPARABLE; + if (isnan(a_min) || isnan(b_min) || isnan(a_max) || isnan(b_max)) return NUM_PRECISION_INCOMPARABLE; else if (a_min == b_min && a_max == b_max) return NUM_PRECISION_EQUAL; else if (a_min <= b_min && b_max <= a_max) return NUM_PRECISION_MORE; else if (b_min <= a_min && a_max <= b_max) return NUM_PRECISION_LESS; else return NUM_PRECISION_INCOMPARABLE; } -PUREFUNC bool has_heap_memory(type_t *t) -{ +PUREFUNC bool has_heap_memory(type_t *t) { switch (t->tag) { case ListType: return true; case TableType: return true; @@ -247,15 +220,13 @@ PUREFUNC bool has_heap_memory(type_t *t) case BigIntType: return true; case StructType: { for (arg_t *field = Match(t, StructType)->fields; field; field = field->next) { - if (has_heap_memory(field->type)) - return true; + if (has_heap_memory(field->type)) return true; } return false; } case EnumType: { for (tag_t *tag = Match(t, EnumType)->tags; tag; tag = tag->next) { - if (tag->type && has_heap_memory(tag->type)) - return true; + if (tag->type && has_heap_memory(tag->type)) return true; } return false; } @@ -263,8 +234,7 @@ PUREFUNC bool has_heap_memory(type_t *t) } } -PUREFUNC bool has_stack_memory(type_t *t) -{ +PUREFUNC bool has_stack_memory(type_t *t) { if (!t) return false; switch (t->tag) { case PointerType: return Match(t, PointerType)->is_stack; @@ -273,14 +243,12 @@ PUREFUNC bool has_stack_memory(type_t *t) } } -PUREFUNC const char *enum_single_value_tag(type_t *enum_type, type_t *t) -{ +PUREFUNC const char *enum_single_value_tag(type_t *enum_type, type_t *t) { const char *found = NULL; for (tag_t *tag = Match(enum_type, EnumType)->tags; tag; tag = tag->next) { if (tag->type->tag != StructType) continue; DeclareMatch(s, tag->type, StructType); - if (!s->fields || s->fields->next || !s->fields->type) - continue; + if (!s->fields || s->fields->next || !s->fields->type) continue; if (can_promote(t, s->fields->type)) { if (found) // Ambiguous case, multiple matches @@ -292,51 +260,39 @@ PUREFUNC const char *enum_single_value_tag(type_t *enum_type, type_t *t) return found; } -PUREFUNC bool can_promote(type_t *actual, type_t *needed) -{ - if (!actual || !needed) - return false; +PUREFUNC bool can_promote(type_t *actual, type_t *needed) { + if (!actual || !needed) return false; // No promotion necessary: - if (type_eq(actual, needed)) - return true; + if (type_eq(actual, needed)) return true; - if (actual->tag == NumType && needed->tag == IntType) - return false; + if (actual->tag == NumType && needed->tag == IntType) return false; - if (actual->tag == IntType && (needed->tag == NumType || needed->tag == BigIntType)) - return true; + if (actual->tag == IntType && (needed->tag == NumType || needed->tag == BigIntType)) return true; - if (actual->tag == BigIntType && needed->tag == NumType) - return true; + if (actual->tag == BigIntType && needed->tag == NumType) return true; if (actual->tag == IntType && needed->tag == IntType) { precision_cmp_e cmp = compare_precision(actual, needed); return cmp == NUM_PRECISION_EQUAL || cmp == NUM_PRECISION_LESS; } - if (needed->tag == EnumType) - return (enum_single_value_tag(needed, actual) != NULL); + if (needed->tag == EnumType) return (enum_single_value_tag(needed, actual) != NULL); // Lang to Text: - if (actual->tag == TextType && needed->tag == TextType && streq(Match(needed, TextType)->lang, "Text")) - return true; + if (actual->tag == TextType && needed->tag == TextType && streq(Match(needed, TextType)->lang, "Text")) return true; // Text to C String - if (actual->tag == TextType && !Match(actual, TextType)->lang && needed->tag == CStringType) - return true; + if (actual->tag == TextType && !Match(actual, TextType)->lang && needed->tag == CStringType) return true; // Automatic dereferencing: - if (actual->tag == PointerType && can_promote(Match(actual, PointerType)->pointed, needed)) - return true; + if (actual->tag == PointerType && can_promote(Match(actual, PointerType)->pointed, needed)) return true; if (actual->tag == OptionalType) { - if (needed->tag == BoolType) - return true; + if (needed->tag == BoolType) return true; // Ambiguous `none` to concrete optional - if (Match(actual, OptionalType)->type == NULL) - return (needed->tag == OptionalType); + if (Match(actual, OptionalType)->type == NULL) return (needed->tag == OptionalType); // Optional num -> num if (needed->tag == NumType && actual->tag == OptionalType && Match(actual, OptionalType)->type->tag == NumType) @@ -344,7 +300,8 @@ PUREFUNC bool can_promote(type_t *actual, type_t *needed) } // Optional promotion: - if (needed->tag == OptionalType && Match(needed, OptionalType)->type != NULL && can_promote(actual, Match(needed, OptionalType)->type)) + if (needed->tag == OptionalType && Match(needed, OptionalType)->type != NULL + && can_promote(actual, Match(needed, OptionalType)->type)) return true; if (needed->tag == PointerType && actual->tag == PointerType) { @@ -361,8 +318,7 @@ PUREFUNC bool can_promote(type_t *actual, type_t *needed) // Can't use @Foo for a function that wants @Baz // But you *can* use @Foo for a function that wants @Memory return false; - else - return true; + else return true; } // Empty literals: @@ -370,7 +326,8 @@ PUREFUNC bool can_promote(type_t *actual, type_t *needed) return true; // [] -> [T] if (actual->tag == SetType && needed->tag == SetType && Match(actual, SetType)->item_type == NULL) return true; // || -> |T| - if (actual->tag == TableType && needed->tag == TableType && Match(actual, TableType)->key_type == NULL && Match(actual, TableType)->value_type == NULL) + if (actual->tag == TableType && needed->tag == TableType && Match(actual, TableType)->key_type == NULL + && Match(actual, TableType)->value_type == NULL) return true; // {} -> {K=V} // Cross-promotion between tables with default values and without @@ -403,10 +360,9 @@ PUREFUNC bool can_promote(type_t *actual, type_t *needed) type_t *needed_ret = Match(needed, FunctionType)->ret; if (!needed_ret) needed_ret = Type(VoidType); - return ( - (type_eq(actual_ret, needed_ret)) - || (actual_ret->tag == PointerType && needed_ret->tag == PointerType - && can_promote(actual_ret, needed_ret))); + return ((type_eq(actual_ret, needed_ret)) + || (actual_ret->tag == PointerType && needed_ret->tag == PointerType + && can_promote(actual_ret, needed_ret))); } // Set -> List promotion @@ -417,30 +373,24 @@ PUREFUNC bool can_promote(type_t *actual, type_t *needed) return false; } -PUREFUNC bool is_int_type(type_t *t) -{ - return t->tag == IntType || t->tag == BigIntType || t->tag == ByteType; -} +PUREFUNC bool is_int_type(type_t *t) { return t->tag == IntType || t->tag == BigIntType || t->tag == ByteType; } -PUREFUNC bool is_numeric_type(type_t *t) -{ +PUREFUNC bool is_numeric_type(type_t *t) { return t->tag == IntType || t->tag == BigIntType || t->tag == NumType || t->tag == ByteType; } -PUREFUNC bool is_packed_data(type_t *t) -{ - if (t->tag == IntType || t->tag == NumType || t->tag == ByteType || t->tag == PointerType || t->tag == BoolType || t->tag == FunctionType) { +PUREFUNC bool is_packed_data(type_t *t) { + if (t->tag == IntType || t->tag == NumType || t->tag == ByteType || t->tag == PointerType || t->tag == BoolType + || t->tag == FunctionType) { return true; } else if (t->tag == StructType) { for (arg_t *field = Match(t, StructType)->fields; field; field = field->next) { - if (!is_packed_data(field->type)) - return false; + if (!is_packed_data(field->type)) return false; } return true; } else if (t->tag == EnumType) { for (tag_t *tag = Match(t, EnumType)->tags; tag; tag = tag->next) { - if (!is_packed_data(tag->type)) - return false; + if (!is_packed_data(tag->type)) return false; } return true; } else { @@ -448,10 +398,10 @@ PUREFUNC bool is_packed_data(type_t *t) } } -PUREFUNC size_t unpadded_struct_size(type_t *t) -{ +PUREFUNC size_t unpadded_struct_size(type_t *t) { if (Match(t, StructType)->opaque) - compiler_err(NULL, NULL, NULL, "The struct type %s is opaque, so I can't get the size of it", Match(t, StructType)->name); + compiler_err(NULL, NULL, NULL, "The struct type %s is opaque, so I can't get the size of it", + Match(t, StructType)->name); arg_t *fields = Match(t, StructType)->fields; size_t size = 0; size_t bit_offset = 0; @@ -469,8 +419,7 @@ PUREFUNC size_t unpadded_struct_size(type_t *t) bit_offset = 0; } size_t align = type_align(field_type); - if (align > 1 && size % align > 0) - size += align - (size % align); // Padding + if (align > 1 && size % align > 0) size += align - (size % align); // Padding size += type_size(field_type); } } @@ -481,8 +430,7 @@ PUREFUNC size_t unpadded_struct_size(type_t *t) return size; } -PUREFUNC size_t type_size(type_t *t) -{ +PUREFUNC size_t type_size(type_t *t) { if (t == PATH_TYPE) return sizeof(Path_t); if (t == PATH_TYPE_TYPE) return sizeof(PathType_t); #ifdef __GNUC__ @@ -490,11 +438,14 @@ PUREFUNC size_t type_size(type_t *t) #pragma GCC diagnostic ignored "-Wswitch-default" #endif switch (t->tag) { - case UnknownType: case AbortType: case ReturnType: case VoidType: return 0; + case UnknownType: + case AbortType: + case ReturnType: + case VoidType: return 0; case MemoryType: errx(1, "Memory has undefined type size"); case BoolType: return sizeof(bool); case ByteType: return sizeof(uint8_t); - case CStringType: return sizeof(char*); + case CStringType: return sizeof(char *); case BigIntType: return sizeof(Int_t); case IntType: { switch (Match(t, IntType)->bits) { @@ -510,9 +461,9 @@ PUREFUNC size_t type_size(type_t *t) case ListType: return sizeof(List_t); case SetType: return sizeof(Table_t); case TableType: return sizeof(Table_t); - case FunctionType: return sizeof(void*); - case ClosureType: return sizeof(struct {void *fn, *userdata;}); - case PointerType: return sizeof(void*); + case FunctionType: return sizeof(void *); + case ClosureType: return sizeof(struct { void *fn, *userdata; }); + case PointerType: return sizeof(void *); case OptionalType: { type_t *nonnull = Match(t, OptionalType)->type; switch (nonnull->tag) { @@ -528,8 +479,7 @@ PUREFUNC size_t type_size(type_t *t) size_t size = unpadded_struct_size(nonnull); size += sizeof(bool); // is_null flag size_t align = type_align(nonnull); - if (align > 0 && (size % align) > 0) - size = (size + align) - (size % align); + if (align > 0 && (size % align) > 0) size = (size + align) - (size % align); return size; } default: return type_size(nonnull); @@ -537,11 +487,11 @@ PUREFUNC size_t type_size(type_t *t) } case StructType: { if (Match(t, StructType)->opaque) - compiler_err(NULL, NULL, NULL, "The struct type %s is opaque, so I can't get the size of it", Match(t, StructType)->name); + compiler_err(NULL, NULL, NULL, "The struct type %s is opaque, so I can't get the size of it", + Match(t, StructType)->name); size_t size = unpadded_struct_size(t); size_t align = type_align(t); - if (size > 0 && align > 0 && (size % align) > 0) - size = (size + align) - (size % align); + if (size > 0 && align > 0 && (size % align) > 0) size = (size + align) - (size % align); return size; } case EnumType: { @@ -553,7 +503,7 @@ PUREFUNC size_t type_size(type_t *t) size_t size = type_size(tag->type); if (size > max_size) max_size = size; } - size_t size = sizeof(UnknownType); // generic enum + size_t size = sizeof(UnknownType); // generic enum if (max_align > 1 && size % max_align > 0) // Padding before first union field size += max_align - (size % max_align); size += max_size; @@ -572,8 +522,7 @@ PUREFUNC size_t type_size(type_t *t) return 0; } -PUREFUNC size_t type_align(type_t *t) -{ +PUREFUNC size_t type_align(type_t *t) { if (t == PATH_TYPE) return __alignof__(Path_t); if (t == PATH_TYPE_TYPE) return __alignof__(PathType_t); #ifdef __GNUC__ @@ -581,11 +530,14 @@ PUREFUNC size_t type_align(type_t *t) #pragma GCC diagnostic ignored "-Wswitch-default" #endif switch (t->tag) { - case UnknownType: case AbortType: case ReturnType: case VoidType: return 0; + case UnknownType: + case AbortType: + case ReturnType: + case VoidType: return 0; case MemoryType: errx(1, "Memory has undefined type alignment"); case BoolType: return __alignof__(bool); case ByteType: return __alignof__(uint8_t); - case CStringType: return __alignof__(char*); + case CStringType: return __alignof__(char *); case BigIntType: return __alignof__(Int_t); case IntType: { switch (Match(t, IntType)->bits) { @@ -601,9 +553,9 @@ PUREFUNC size_t type_align(type_t *t) case SetType: return __alignof__(Table_t); case ListType: return __alignof__(List_t); case TableType: return __alignof__(Table_t); - case FunctionType: return __alignof__(void*); - case ClosureType: return __alignof__(struct {void *fn, *userdata;}); - case PointerType: return __alignof__(void*); + case FunctionType: return __alignof__(void *); + case ClosureType: return __alignof__(struct { void *fn, *userdata; }); + case PointerType: return __alignof__(void *); case OptionalType: { type_t *nonnull = Match(t, OptionalType)->type; switch (nonnull->tag) { @@ -624,7 +576,7 @@ PUREFUNC size_t type_align(type_t *t) compiler_err(NULL, NULL, NULL, "The struct type %s is opaque, so I can't get the alignment of it", Match(t, StructType)->name); arg_t *fields = Match(t, StructType)->fields; - size_t align = t->tag == StructType ? 0 : sizeof(void*); + size_t align = t->tag == StructType ? 0 : sizeof(void *); for (arg_t *field = fields; field; field = field->next) { size_t field_align = type_align(field->type); if (field_align > align) align = field_align; @@ -649,50 +601,39 @@ PUREFUNC size_t type_align(type_t *t) return 0; } -type_t *get_field_type(type_t *t, const char *field_name) -{ +type_t *get_field_type(type_t *t, const char *field_name) { t = value_type(t); switch (t->tag) { - case PointerType: - return get_field_type(Match(t, PointerType)->pointed, field_name); + case PointerType: return get_field_type(Match(t, PointerType)->pointed, field_name); case TextType: { - if (Match(t, TextType)->lang && streq(field_name, "text")) - return TEXT_TYPE; + if (Match(t, TextType)->lang && streq(field_name, "text")) return TEXT_TYPE; else if (streq(field_name, "length")) return INT_TYPE; return NULL; } case StructType: { DeclareMatch(struct_t, t, StructType); for (arg_t *field = struct_t->fields; field; field = field->next) { - if (streq(field->name, field_name)) - return field->type; + if (streq(field->name, field_name)) return field->type; } return NULL; } case EnumType: { DeclareMatch(e, t, EnumType); for (tag_t *tag = e->tags; tag; tag = tag->next) { - if (streq(field_name, tag->name)) - return Type(BoolType); + if (streq(field_name, tag->name)) return Type(BoolType); } return NULL; } case SetType: { - if (streq(field_name, "length")) - return INT_TYPE; - else if (streq(field_name, "items")) - return Type(ListType, .item_type=Match(t, SetType)->item_type); + if (streq(field_name, "length")) return INT_TYPE; + else if (streq(field_name, "items")) return Type(ListType, .item_type = Match(t, SetType)->item_type); return NULL; } case TableType: { - if (streq(field_name, "length")) - return INT_TYPE; - else if (streq(field_name, "keys")) - return Type(ListType, Match(t, TableType)->key_type); - else if (streq(field_name, "values")) - return Type(ListType, Match(t, TableType)->value_type); - else if (streq(field_name, "fallback")) - return Type(OptionalType, .type=t); + if (streq(field_name, "length")) return INT_TYPE; + else if (streq(field_name, "keys")) return Type(ListType, Match(t, TableType)->key_type); + else if (streq(field_name, "values")) return Type(ListType, Match(t, TableType)->value_type); + else if (streq(field_name, "fallback")) return Type(OptionalType, .type = t); return NULL; } case ListType: { @@ -703,28 +644,28 @@ type_t *get_field_type(type_t *t, const char *field_name) } } -PUREFUNC type_t *get_iterated_type(type_t *t) -{ +PUREFUNC type_t *get_iterated_type(type_t *t) { type_t *iter_value_t = value_type(t); switch (iter_value_t->tag) { - case BigIntType: case IntType: return iter_value_t; break; + case BigIntType: + case IntType: return iter_value_t; break; case ListType: return Match(iter_value_t, ListType)->item_type; break; case SetType: return Match(iter_value_t, SetType)->item_type; break; case TableType: return NULL; - case FunctionType: case ClosureType: { + case FunctionType: + case ClosureType: { // Iterator function - __typeof(iter_value_t->__data.FunctionType) *fn = iter_value_t->tag == ClosureType ? - Match(Match(iter_value_t, ClosureType)->fn, FunctionType) : Match(iter_value_t, FunctionType); - if (fn->args || fn->ret->tag != OptionalType) - return NULL; + __typeof(iter_value_t->__data.FunctionType) *fn = + iter_value_t->tag == ClosureType ? Match(Match(iter_value_t, ClosureType)->fn, FunctionType) + : Match(iter_value_t, FunctionType); + if (fn->args || fn->ret->tag != OptionalType) return NULL; return Match(fn->ret, OptionalType)->type; } default: return NULL; } } -CONSTFUNC bool is_incomplete_type(type_t *t) -{ +CONSTFUNC bool is_incomplete_type(type_t *t) { if (t == NULL) return true; switch (t->tag) { case ReturnType: return is_incomplete_type(Match(t, ReturnType)->ret); @@ -738,8 +679,7 @@ CONSTFUNC bool is_incomplete_type(type_t *t) case FunctionType: { DeclareMatch(fn, t, FunctionType); for (arg_t *arg = fn->args; arg; arg = arg->next) { - if (arg->type == NULL || is_incomplete_type(arg->type)) - return true; + if (arg->type == NULL || is_incomplete_type(arg->type)) return true; } return fn->ret ? is_incomplete_type(fn->ret) : false; } @@ -749,18 +689,14 @@ CONSTFUNC bool is_incomplete_type(type_t *t) } } -CONSTFUNC type_t *most_complete_type(type_t *t1, type_t *t2) -{ +CONSTFUNC type_t *most_complete_type(type_t *t1, type_t *t2) { if (!t1) return t2; if (!t2) return t1; - if (is_incomplete_type(t1) && is_incomplete_type(t2)) - return NULL; - else if (!is_incomplete_type(t1) && !is_incomplete_type(t2) && type_eq(t1, t2)) - return t1; + if (is_incomplete_type(t1) && is_incomplete_type(t2)) return NULL; + else if (!is_incomplete_type(t1) && !is_incomplete_type(t2) && type_eq(t1, t2)) return t1; - if (t1->tag != t2->tag) - return NULL; + if (t1->tag != t2->tag) return NULL; switch (t1->tag) { case ReturnType: { @@ -792,16 +728,15 @@ CONSTFUNC type_t *most_complete_type(type_t *t1, type_t *t2) DeclareMatch(fn2, t2, FunctionType); arg_t *args = NULL; for (arg_t *arg1 = fn1->args, *arg2 = fn2->args; arg1 || arg2; arg1 = arg1->next, arg2 = arg2->next) { - if (!arg1 || !arg2) - return NULL; + if (!arg1 || !arg2) return NULL; type_t *arg_type = most_complete_type(arg1->type, arg2->type); if (!arg_type) return NULL; - args = new(arg_t, .type=arg_type, .next=args); + args = new (arg_t, .type = arg_type, .next = args); } REVERSE_LIST(args); type_t *ret = most_complete_type(fn1->ret, fn2->ret); - return ret ? Type(FunctionType, .args=args, .ret=ret) : NULL; + return ret ? Type(FunctionType, .args = args, .ret = ret) : NULL; } case ClosureType: { type_t *fn = most_complete_type(Match(t1, ClosureType)->fn, Match(t1, ClosureType)->fn); @@ -810,35 +745,29 @@ CONSTFUNC type_t *most_complete_type(type_t *t1, type_t *t2) case PointerType: { DeclareMatch(ptr1, t1, PointerType); DeclareMatch(ptr2, t2, PointerType); - if (ptr1->is_stack != ptr2->is_stack) - return NULL; + if (ptr1->is_stack != ptr2->is_stack) return NULL; type_t *pointed = most_complete_type(ptr1->pointed, ptr2->pointed); - return pointed ? Type(PointerType, .is_stack=ptr1->is_stack, .pointed=pointed) : NULL; + return pointed ? Type(PointerType, .is_stack = ptr1->is_stack, .pointed = pointed) : NULL; } default: { - if (is_incomplete_type(t1) || is_incomplete_type(t2)) - return NULL; + if (is_incomplete_type(t1) || is_incomplete_type(t2)) return NULL; return type_eq(t1, t2) ? t1 : NULL; } } } -type_t *_make_function_type(type_t *ret, int n, arg_t args[n]) -{ +type_t *_make_function_type(type_t *ret, int n, arg_t args[n]) { arg_t *arg_pointers = GC_MALLOC(sizeof(arg_t[n])); for (int i = 0; i < n; i++) { arg_pointers[i] = args[i]; - if (i + 1 < n) - arg_pointers[i].next = &arg_pointers[i+1]; + if (i + 1 < n) arg_pointers[i].next = &arg_pointers[i + 1]; } - return Type(FunctionType, .ret=ret, .args=&arg_pointers[0]); + return Type(FunctionType, .ret = ret, .args = &arg_pointers[0]); } -PUREFUNC bool enum_has_fields(type_t *t) -{ +PUREFUNC bool enum_has_fields(type_t *t) { for (tag_t *e_tag = Match(t, EnumType)->tags; e_tag; e_tag = e_tag->next) { - if (e_tag->type != NULL && Match(e_tag->type, StructType)->fields) - return true; + if (e_tag->type != NULL && Match(e_tag->type, StructType)->fields) return true; } return false; } diff --git a/src/types.h b/src/types.h index be16cd5f..2fd1526c 100644 --- a/src/types.h +++ b/src/types.h @@ -15,11 +15,13 @@ typedef struct arg_s { struct arg_s *next; } arg_t; -#define ARG_LIST(...) ({\ - arg_t *args[] = {__VA_ARGS__}; \ - for (size_t i = 0; i < sizeof(args)/sizeof(args[0])-1; i++) \ - args[i]->next = args[i+1]; \ - args[0]; }) +#define ARG_LIST(...) \ + ({ \ + arg_t *args[] = {__VA_ARGS__}; \ + for (size_t i = 0; i < sizeof(args) / sizeof(args[0]) - 1; i++) \ + args[i]->next = args[i + 1]; \ + args[0]; \ + }) typedef struct tag_s { const char *name; @@ -66,15 +68,18 @@ struct type_s { struct { type_t *ret; } ReturnType; - struct {} BigIntType; struct { - enum { TYPE_IBITS8=8, TYPE_IBITS16=16, TYPE_IBITS32=32, TYPE_IBITS64=64 } bits; + } BigIntType; + struct { + enum { TYPE_IBITS8 = 8, TYPE_IBITS16 = 16, TYPE_IBITS32 = 32, TYPE_IBITS64 = 64 } bits; } IntType; - struct {} ByteType; struct { - enum { TYPE_NBITS32=32, TYPE_NBITS64=64 } bits; + } ByteType; + struct { + enum { TYPE_NBITS32 = 32, TYPE_NBITS64 = 64 } bits; } NumType; - struct {} CStringType; + struct { + } CStringType; struct { const char *lang; struct env_s *env; @@ -99,13 +104,13 @@ struct type_s { } ClosureType; struct { type_t *pointed; - bool is_stack:1; + bool is_stack : 1; } PointerType; struct { const char *name; arg_t *fields; struct env_s *env; - bool opaque:1, external:1; + bool opaque : 1, external : 1; } StructType; struct { const char *name; @@ -127,10 +132,11 @@ struct type_s { } __data; }; -#define Type(typetag, ...) new(type_t, .tag=typetag, .__data.typetag={__VA_ARGS__}) +#define Type(typetag, ...) new (type_t, .tag = typetag, .__data.typetag = {__VA_ARGS__}) #define INT_TYPE Type(BigIntType) -#define NUM_TYPE Type(NumType, .bits=TYPE_NBITS64) -#define NewFunctionType(ret, ...) _make_function_type(ret, sizeof((arg_t[]){__VA_ARGS__})/sizeof(arg_t), (arg_t[]){__VA_ARGS__}) +#define NUM_TYPE Type(NumType, .bits = TYPE_NBITS64) +#define NewFunctionType(ret, ...) \ + _make_function_type(ret, sizeof((arg_t[]){__VA_ARGS__}) / sizeof(arg_t), (arg_t[]){__VA_ARGS__}) Text_t type_to_text(type_t *t); const char *type_to_str(type_t *t); @@ -139,7 +145,12 @@ PUREFUNC bool type_eq(type_t *a, type_t *b); PUREFUNC bool type_is_a(type_t *t, type_t *req); type_t *type_or_type(type_t *a, type_t *b); type_t *value_type(type_t *a); -typedef enum {NUM_PRECISION_EQUAL, NUM_PRECISION_LESS, NUM_PRECISION_MORE, NUM_PRECISION_INCOMPARABLE} precision_cmp_e; +typedef enum { + NUM_PRECISION_EQUAL, + NUM_PRECISION_LESS, + NUM_PRECISION_MORE, + NUM_PRECISION_INCOMPARABLE +} precision_cmp_e; PUREFUNC precision_cmp_e compare_precision(type_t *a, type_t *b); PUREFUNC bool has_heap_memory(type_t *t); PUREFUNC bool has_stack_memory(type_t *t); |
