packages feed

zeolite-lang 0.19.0.0 → 0.20.0.0

raw patch · 219 files changed

+11326/−7960 lines, 219 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Compilation.CompilerState: ccInheritReturns :: CompilerContext c m s a => a -> [a] -> m a
- Compilation.CompilerState: csInheritReturns :: CompilerContext c m s a => [a] -> CompilerState a m ()
- CompilerCxx.Naming: collectionName :: CategoryName -> String
- Parser.Common: kwTypes :: TextParser ()
- Types.TypeCategory: PatternMatch :: Variance -> a -> a -> PatternMatch a
- Types.TypeCategory: [pmData] :: PatternMatch a -> a
- Types.TypeCategory: [pmPattern] :: PatternMatch a -> a
- Types.TypeCategory: [pmVariance] :: PatternMatch a -> Variance
- Types.TypeCategory: instance GHC.Show.Show a => GHC.Show.Show (Types.TypeCategory.PatternMatch a)
+ Base.CompilerError: mergeObjectsM :: CollectErrorsM m => (a -> a -> m b) -> [a] -> m [a]
+ Base.CompilerError: silenceErrorsM :: CollectErrorsM m => m a -> m a
+ Base.CompilerError: tryCompilerM :: CollectErrorsM m => m a -> m (Maybe a)
+ Cli.Compiler: [msExtra] :: ModuleSpec -> [FilePath]
+ Cli.RunCompiler: TraceEntry :: Integer -> Integer -> String -> String -> TraceEntry
+ Cli.RunCompiler: [teContext] :: TraceEntry -> String
+ Cli.RunCompiler: [teFunction] :: TraceEntry -> String
+ Cli.RunCompiler: [teMicroseconds] :: TraceEntry -> Integer
+ Cli.RunCompiler: [teProcess] :: TraceEntry -> Integer
+ Cli.RunCompiler: data TraceEntry
+ Cli.RunCompiler: parseTracesFile :: (FilePath, String) -> TrackedErrorsIO [TraceEntry]
+ Compilation.CompilerState: DeferVariable :: Map VariableName (PassedValue c) -> DeferVariable c
+ Compilation.CompilerState: [cdTraces] :: CompiledData s -> Set String
+ Compilation.CompilerState: [csDeferred] :: CleanupBlock c s -> DeferVariable c
+ Compilation.CompilerState: [dvDeferred] :: DeferVariable c -> Map VariableName (PassedValue c)
+ Compilation.CompilerState: addDeferred :: VariableName -> PassedValue c -> DeferVariable c -> DeferVariable c
+ Compilation.CompilerState: branchDeferred :: [DeferVariable c] -> DeferVariable c
+ Compilation.CompilerState: ccAddTrace :: CompilerContext c m s a => a -> String -> m a
+ Compilation.CompilerState: ccGetTraces :: CompilerContext c m s a => a -> m [String]
+ Compilation.CompilerState: ccInheritDeferred :: CompilerContext c m s a => a -> DeferVariable c -> m a
+ Compilation.CompilerState: ccInheritStatic :: CompilerContext c m s a => a -> [a] -> m a
+ Compilation.CompilerState: ccSetDeferred :: CompilerContext c m s a => a -> UsedVariable c -> m a
+ Compilation.CompilerState: checkDeferred :: VariableName -> DeferVariable c -> Bool
+ Compilation.CompilerState: csAddTrace :: CompilerContext c m s a => String -> CompilerState a m ()
+ Compilation.CompilerState: csInheritDeferred :: CompilerContext c m s a => DeferVariable c -> CompilerState a m ()
+ Compilation.CompilerState: csInheritStatic :: CompilerContext c m s a => [a] -> CompilerState a m ()
+ Compilation.CompilerState: csSetDeferred :: CompilerContext c m s a => UsedVariable c -> CompilerState a m ()
+ Compilation.CompilerState: emptyDeferred :: DeferVariable c
+ Compilation.CompilerState: followDeferred :: DeferVariable c -> DeferVariable c -> DeferVariable c
+ Compilation.CompilerState: instance GHC.Show.Show c => GHC.Show.Show (Compilation.CompilerState.DeferVariable c)
+ Compilation.CompilerState: newtype DeferVariable c
+ Compilation.CompilerState: removeDeferred :: VariableName -> DeferVariable c -> DeferVariable c
+ Compilation.ProcedureContext: [_pcDeferred] :: ProcedureContext c -> DeferVariable c
+ Compilation.ProcedureContext: [_pcTraces] :: ProcedureContext c -> [String]
+ CompilerCxx.Code: onlyDeps :: Set CategoryName -> CompiledData [String]
+ CompilerCxx.CxxFiles: [coPossibleTraces] :: CxxOutput -> Set String
+ CompilerCxx.Naming: categoryIdName :: CategoryName -> String
+ CompilerCxx.Naming: typeRemover :: CategoryName -> String
+ Module.CompileMetadata: [cmExtra] :: CompileMetadata -> [FilePath]
+ Module.CompileMetadata: [mcExtra] :: ModuleConfig -> [FilePath]
+ Module.ProcessMetadata: readPossibleTraces :: FilePath -> TrackedErrorsIO (Set String)
+ Module.ProcessMetadata: writePossibleTraces :: FilePath -> Set String -> TrackedErrorsIO ()
+ Parser.Common: kwDefer :: TextParser ()
+ Parser.Common: kwImmutable :: TextParser ()
+ Types.Procedure: DeferredVariables :: [c] -> [Assignable c] -> Statement c
+ Types.Procedure: ShowVariable :: [c] -> ValueType -> VariableName -> Statement c
+ Types.Procedure: [tpDisableCoverage] :: TestProcedure c -> Bool
+ Types.TypeCategory: CategoryImmutable :: [c] -> PragmaCategory c
+ Types.TypeCategory: DefinesPattern :: TypeInstance -> DefinesInstance -> PatternMatch
+ Types.TypeCategory: TypePattern :: Variance -> ValueType -> ValueType -> PatternMatch
+ Types.TypeCategory: [ciContext] :: PragmaCategory c -> [c]
+ Types.TypeCategory: [dpData] :: PatternMatch -> TypeInstance
+ Types.TypeCategory: [dpPattern] :: PatternMatch -> DefinesInstance
+ Types.TypeCategory: [iiPragmas] :: AnyCategory c -> [PragmaCategory c]
+ Types.TypeCategory: [tpData] :: PatternMatch -> ValueType
+ Types.TypeCategory: [tpPattern] :: PatternMatch -> ValueType
+ Types.TypeCategory: [tpVariance] :: PatternMatch -> Variance
+ Types.TypeCategory: [vcPragmas] :: AnyCategory c -> [PragmaCategory c]
+ Types.TypeCategory: [viPragmas] :: AnyCategory c -> [PragmaCategory c]
+ Types.TypeCategory: data PragmaCategory c
+ Types.TypeCategory: getCategoryPragmas :: AnyCategory c -> [PragmaCategory c]
+ Types.TypeCategory: guessesFromFilters :: CollectErrorsM m => ParamFilters -> ValueType -> ValueType -> m [PatternMatch]
+ Types.TypeCategory: instance GHC.Show.Show Types.TypeCategory.PatternMatch
+ Types.TypeCategory: instance GHC.Show.Show c => GHC.Show.Show (Types.TypeCategory.PragmaCategory c)
+ Types.TypeCategory: isCategoryImmutable :: PragmaCategory c -> Bool
+ Types.TypeCategory: prependCategoryPragmaContext :: [c] -> PragmaCategory c -> PragmaCategory c
+ Types.TypeInstance: ImmutableFilter :: TypeFilter
+ Types.TypeInstance: checkValueTypeImmutable :: (CollectErrorsM m, TypeResolver r) => r -> ParamFilters -> ValueType -> m ()
+ Types.TypeInstance: dedupGeneralInstance :: (CollectErrorsM m, TypeResolver r) => r -> ParamFilters -> GeneralInstance -> m GeneralInstance
+ Types.TypeInstance: trImmutable :: (TypeResolver r, CollectErrorsM m) => r -> CategoryName -> m Bool
- Cli.Compiler: ModuleSpec :: FilePath -> FilePath -> ExprMap SourceContext -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> [ExtraSource] -> [FilePath] -> CompileMode -> ForceMode -> ModuleSpec
+ Cli.Compiler: ModuleSpec :: FilePath -> FilePath -> [FilePath] -> ExprMap SourceContext -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> [ExtraSource] -> [FilePath] -> CompileMode -> ForceMode -> ModuleSpec
- Cli.Compiler: createModuleTemplates :: PathIOHandler r => r -> FilePath -> FilePath -> [CompileMetadata] -> [CompileMetadata] -> TrackedErrorsIO ()
+ Cli.Compiler: createModuleTemplates :: PathIOHandler r => r -> FilePath -> FilePath -> [FilePath] -> [CompileMetadata] -> [CompileMetadata] -> TrackedErrorsIO ()
- Compilation.CompilerState: CleanupBlock :: s -> [UsedVariable c] -> JumpType -> Set CategoryName -> CleanupBlock c s
+ Compilation.CompilerState: CleanupBlock :: s -> DeferVariable c -> [UsedVariable c] -> JumpType -> Set CategoryName -> CleanupBlock c s
- Compilation.CompilerState: CompiledData :: Set CategoryName -> s -> CompiledData s
+ Compilation.CompilerState: CompiledData :: Set CategoryName -> Set String -> s -> CompiledData s
- Compilation.ProcedureContext: ProcedureContext :: SymbolScope -> CategoryName -> Positional (ValueParam c) -> [DefinedMember c] -> CategoryMap c -> ParamFilters -> [ParamFilter c] -> Map ParamName SymbolScope -> Map FunctionName (ScopedFunction c) -> Map VariableName (VariableValue c) -> ReturnValidation c -> JumpType -> Bool -> [ReturnVariable] -> Set CategoryName -> [String] -> Bool -> LoopSetup [String] -> [Maybe (CleanupBlock c [String])] -> Bool -> [UsedVariable c] -> ExprMap c -> [(MacroName, [c])] -> Bool -> ProcedureContext c
+ Compilation.ProcedureContext: ProcedureContext :: SymbolScope -> CategoryName -> Positional (ValueParam c) -> [DefinedMember c] -> CategoryMap c -> ParamFilters -> [ParamFilter c] -> Map ParamName SymbolScope -> Map FunctionName (ScopedFunction c) -> Map VariableName (VariableValue c) -> ReturnValidation c -> DeferVariable c -> JumpType -> Bool -> [ReturnVariable] -> Set CategoryName -> [String] -> Bool -> LoopSetup [String] -> [Maybe (CleanupBlock c [String])] -> Bool -> [UsedVariable c] -> ExprMap c -> [(MacroName, [c])] -> Bool -> [String] -> ProcedureContext c
- Compilation.ProcedureContext: ValidateNames :: Positional VariableName -> Positional (PassedValue c) -> Map VariableName (PassedValue c) -> ReturnValidation c
+ Compilation.ProcedureContext: ValidateNames :: Positional VariableName -> Positional (PassedValue c) -> DeferVariable c -> ReturnValidation c
- Compilation.ProcedureContext: [vnRemaining] :: ReturnValidation c -> Map VariableName (PassedValue c)
+ Compilation.ProcedureContext: [vnRemaining] :: ReturnValidation c -> DeferVariable c
- Compilation.ScopeContext: getProcedureScopes :: (Show c, CollectErrorsM m) => CategoryMap c -> ExprMap c -> DefinedCategory c -> m [ProcedureScope c]
+ Compilation.ScopeContext: getProcedureScopes :: (Ord c, Show c, CollectErrorsM m) => CategoryMap c -> ExprMap c -> DefinedCategory c -> m [ProcedureScope c]
- CompilerCxx.CxxFiles: CxxOutput :: Maybe CategoryName -> String -> Namespace -> Set Namespace -> Set CategoryName -> [String] -> CxxOutput
+ CompilerCxx.CxxFiles: CxxOutput :: Maybe CategoryName -> String -> Namespace -> Set Namespace -> Set CategoryName -> Set String -> [String] -> CxxOutput
- CompilerCxx.Procedure: compileExecutableProcedure :: (Ord c, Show c, CollectErrorsM m) => CxxFunctionType -> ScopeContext c -> ScopedFunction c -> ExecutableProcedure c -> m (CompiledData [String])
+ CompilerCxx.Procedure: compileExecutableProcedure :: (Ord c, Show c, CollectErrorsM m) => Bool -> CxxFunctionType -> ScopeContext c -> ScopedFunction c -> ExecutableProcedure c -> m (CompiledData [String])
- CompilerCxx.Procedure: procedureDeclaration :: Monad m => Bool -> ScopedFunction c -> m (CompiledData [String])
+ CompilerCxx.Procedure: procedureDeclaration :: Monad m => Bool -> Bool -> ScopedFunction c -> m (CompiledData [String])
- Module.CompileMetadata: CompileMetadata :: VersionHash -> FilePath -> Namespace -> Namespace -> [FilePath] -> [FilePath] -> [CategoryName] -> [CategoryName] -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> [ObjectFile] -> CompileMetadata
+ Module.CompileMetadata: CompileMetadata :: VersionHash -> FilePath -> [FilePath] -> Namespace -> Namespace -> [FilePath] -> [FilePath] -> [CategoryName] -> [CategoryName] -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> [ObjectFile] -> CompileMetadata
- Module.CompileMetadata: ModuleConfig :: FilePath -> FilePath -> [(MacroName, Expression SourceContext)] -> [FilePath] -> [FilePath] -> [ExtraSource] -> [FilePath] -> CompileMode -> ModuleConfig
+ Module.CompileMetadata: ModuleConfig :: FilePath -> FilePath -> [FilePath] -> [(MacroName, Expression SourceContext)] -> [FilePath] -> [FilePath] -> [ExtraSource] -> [FilePath] -> CompileMode -> ModuleConfig
- Module.ProcessMetadata: findSourceFiles :: FilePath -> FilePath -> TrackedErrorsIO ([FilePath], [FilePath], [FilePath])
+ Module.ProcessMetadata: findSourceFiles :: FilePath -> [FilePath] -> TrackedErrorsIO ([FilePath], [FilePath], [FilePath])
- Types.Procedure: TestProcedure :: [c] -> FunctionName -> Procedure c -> TestProcedure c
+ Types.Procedure: TestProcedure :: [c] -> FunctionName -> Bool -> Procedure c -> TestProcedure c
- Types.TypeCategory: InstanceInterface :: [c] -> Namespace -> CategoryName -> [ValueParam c] -> [ScopedFunction c] -> AnyCategory c
+ Types.TypeCategory: InstanceInterface :: [c] -> Namespace -> CategoryName -> [PragmaCategory c] -> [ValueParam c] -> [ScopedFunction c] -> AnyCategory c
- Types.TypeCategory: ValueConcrete :: [c] -> Namespace -> CategoryName -> [ValueParam c] -> [ValueRefine c] -> [ValueDefine c] -> [ParamFilter c] -> [ScopedFunction c] -> AnyCategory c
+ Types.TypeCategory: ValueConcrete :: [c] -> Namespace -> CategoryName -> [PragmaCategory c] -> [ValueParam c] -> [ValueRefine c] -> [ValueDefine c] -> [ParamFilter c] -> [ScopedFunction c] -> AnyCategory c
- Types.TypeCategory: ValueInterface :: [c] -> Namespace -> CategoryName -> [ValueParam c] -> [ValueRefine c] -> [ScopedFunction c] -> AnyCategory c
+ Types.TypeCategory: ValueInterface :: [c] -> Namespace -> CategoryName -> [PragmaCategory c] -> [ValueParam c] -> [ValueRefine c] -> [ScopedFunction c] -> AnyCategory c
- Types.TypeCategory: data PatternMatch a
+ Types.TypeCategory: data PatternMatch
- Types.TypeCategory: inferParamTypes :: (CollectErrorsM m, TypeResolver r) => r -> ParamFilters -> ParamValues -> [PatternMatch ValueType] -> m (MergeTree InferredTypeGuess)
+ Types.TypeCategory: inferParamTypes :: (CollectErrorsM m, TypeResolver r) => r -> ParamFilters -> ParamValues -> [PatternMatch] -> m (MergeTree InferredTypeGuess)
- Types.TypeInstance: checkDefinesMatch :: (CollectErrorsM m, TypeResolver r) => r -> ParamFilters -> DefinesInstance -> DefinesInstance -> m ()
+ Types.TypeInstance: checkDefinesMatch :: (CollectErrorsM m, TypeResolver r) => r -> ParamFilters -> DefinesInstance -> DefinesInstance -> m (MergeTree InferredTypeGuess)

Files

ChangeLog.md view
@@ -1,5 +1,167 @@ # Revision history for zeolite-lang +## 0.20.0.0  -- 2021-11-13++### Language++* **[breaking]** Adds the `immutable` category property. This makes all `@value`+  members read-only, and requires their types to also be `immutable`. (This is+  breaking because `immutable` is now reserved.)++* **[breaking]** Allows temporarily postponing variable initialization using the+  `defer` keyword. (This is breaking because `defer` is now reserved.)++* **[breaking]** Adds compilation error if multiple functions match the name of+  a function used in a call. This can happen when multiple `requires` or+  `defines` types have a function with the same name.++* **[fix]** Fixes merging of multiple functions with the same name inherited+  *internally* multiple times.++  ```text+  @value interface Base0 {+    call (Int) -> ()+  }++  @value interface Base1 {+    call (String) -> ()+  }++  concrete Type {}++  define Type {+    refines Base0+    refines Base1++    // This was previously missed when checking for an explicit merge of call.+    @value call (Formatted) -> ()+    call (_) {}+  }+  ```++* **[fix]** Removes the `types` reserved word, which was previously used for+  internal types.++* **[fix]** Fixes `update` calls in `traverse`. Previously, the `update` was not+  called for `continue`.++* **[new]** Adds `$DisableCoverage$` `unittest` pragma, to disable trace+  collection in `--log-traces` when the `unittest` is executed.++* **[new]** Updates `String.builder()` to accept `Formatted` values, rather than+  just accepting `String`.++* **[new]** Updates param inference to infer a param that is only used in+  filters for another param, if that other param occurs as a top-level argument.++  ```text+  // The caller can infer both #x and #y, even though there is no arg with #y.+  @type call<#x,#y>+    #x requires Foo<#y>+  (#x) -> ()+  ```++* **[behavior]** Reduces memory size of `Bool`, `Char`, `Float`, `Int`, and+  references to all other value objects.++* **[behavior]** Efficiency improvements for passing values in function calls.++* **[behavior]** Skips calling `get` in `traverse` if the value is discarded.++  ```text+  // The _ suppresses the call to get() on the returned Order<Int> value.+  traverse (Counter.zeroIndexed(100) -> _) {+    // ...+  }+  ```++* **[behavior]** Makes dispatching of function calls quite a bit more efficient.++### Compiler CLI++* **[breaking]** Updates caching of `TypeInstance` (i.e., `@type`)+  implementations in C++.++  - The argument type for `CreateType_Foo` for extension `Foo` must now be a+    `const` `&`. For types with params, the `InstanceCache` should be made+    global and `static`.+  - A new function `void RemoveType_Foo` taking the same `params` argument as+    the above must be added, which will be called when the `TypeInstance` is+    destructed. For types with params, call `Remove` on the `InstanceCache`.+    Otherwise, it can be a no-op `{}`.++  (You can also just rerun in `--templates` mode and copy the updated `Create`+  and `Remove` functions.)++  Example for `concrete Foo<#x>`:++  ```c+++  static auto& Foo_instance_cache = *new InstanceCache<1, Type_Foo>([](const Params<1>::Type& params) {+      return S_get(new ExtType_Foo(CreateCategory_Foo(), params));+    });++  S<const Type_Foo> CreateType_Foo(const Params<1>::Type& params) {+    return Foo_instance_cache.GetOrCreate(params);+  }++  void RemoveType_Foo(const Params<1>::Type& params) {+    Foo_instance_cache.Remove(params);+  }+  ```++* **[breaking]** Removes all passing of `Param_self` in C++ extensions. Use+  `PARAM_SELF` where `Param_self` was previously used, and delete the+  `Param_self` argument from function signatures. This breaks all existing+  extensions that have `@type` functions.++* **[breaking]** Makes all references to `TypeInstance` `const` in C++, since+  Zeolite prohibits `@type` members in `concrete` categories.++  All extensions with `@type` functions need to be updated:++  - Make all `Call_foo` overrides `const` for `@type` functions.+  - Update `@value` constructors to take `S<const Type_Foo>` instead of+    `S<Type_Foo>`.+  - Update local params (e.g., `Param_foo`) to have type+    `S<const TypeInstance>`.+  - Update `CreateType_Foo` to return `S<const Type_Foo>` instead of+    `S<Type_Foo>`.++* **[breaking]** Removes `AnonymousOrder` C++ class, previously used for+  creating iterators for C++ extensions.++* **[new]** Adds `--show-traces` mode to `zeolite` to output a list of possible+  trace contexts when `--log-traces` is used in `-t` mode.++* **[new]** Adds `--missed-lines` mode to `zeolite` to output all code lines+  for a module that are not in a provided file that was created by+  `--log-traces` in `-t` mode.++* **[new]** `@value` function implementations for `immutable` categories are+  marked as `const` in C++ extensions.++* **[new]** Allows modules to have additional subdirectories, specified in+  `.zeolite-module` with the `extra_paths:` list section. This allows modules to+  separate `.0rx` and `.0rt` files from public `.0rp` files.++### Libraries++* **[breaking]** Removes iterators from `lib/util` (`iterator.0rp`) because they+  were awkward, inefficient, and had no current intended use-cases.++* **[breaking]** Requires the key param in `SearchTree` and `AutoBinaryTree` in+  `lib/container` to be `immutable`.++* **[breaking]** Requires the value param in `TreeSet` in `lib/container` to be+  `immutable`.++* **[breaking]** Requires the value param in `CategoricalTree` in `lib/math` to+  be `immutable`.++* **[new]** Adds the `LinkedNode` and `ForwardNode` linked-list types to+  `lib/container`, and adds the `sortList`, `sortListWith`, and `reverseList`+  functions to `Sorting`.+ ## 0.19.0.0  -- 2021-10-25  ### Compiler CLI
base/.zeolite-module view
@@ -33,6 +33,7 @@   "base/logging.hpp"   "base/thread-capture.h"   "base/thread-crosser.h"+  "base/pooled.hpp"   "base/types.hpp"   "base/src/boxed.cpp"   "base/src/category-source.cpp"
base/boxed.hpp view
@@ -30,7 +30,7 @@ namespace zeolite_internal {  struct UnionValue {-  enum class Type {+  enum class Type : char {     EMPTY,     BOOL,     CHAR,@@ -53,12 +53,12 @@   union {     unsigned char* as_bytes_;     Pointer*  as_pointer_;-    bool      as_bool_;+    PrimBool  as_bool_;     PrimChar  as_char_;     PrimInt   as_int_;     PrimFloat as_float_;-  } value_;-};+  } __attribute__((packed)) value_;+} __attribute__((packed));   class BoxedValue {@@ -66,16 +66,60 @@   constexpr BoxedValue()     : union_{ .type_ = UnionValue::Type::EMPTY, .value_ = { .as_pointer_ = nullptr } } {} -  BoxedValue(const BoxedValue&);-  BoxedValue& operator = (const BoxedValue&);-  BoxedValue(BoxedValue&&);-  BoxedValue& operator = (BoxedValue&&);+  inline BoxedValue(const BoxedValue& other)+    : union_(other.union_) {+    switch (union_.type_) {+      case UnionValue::Type::BOXED:+        ++union_.value_.as_pointer_->strong_;+        break;+      default:+        break;+    }+  } -  BoxedValue(bool value);-  BoxedValue(PrimChar value);-  BoxedValue(PrimInt value);-  BoxedValue(PrimFloat value);+  inline BoxedValue& operator = (const BoxedValue& other) {+    if (&other != this) {+      Cleanup();+      union_ = other.union_;+      switch (union_.type_) {+        case UnionValue::Type::BOXED:+          ++union_.value_.as_pointer_->strong_;+          break;+        default:+          break;+      }+    }+    return *this;+  } +  inline BoxedValue(BoxedValue&& other)+    : union_(other.union_) {+    other.union_.type_  = UnionValue::Type::EMPTY;+    other.union_.value_.as_pointer_ = nullptr;+  }++  inline BoxedValue& operator = (BoxedValue&& other) {+    if (&other != this) {+      Cleanup();+      union_ = other.union_;+      other.union_.type_  = UnionValue::Type::EMPTY;+      other.union_.value_.as_pointer_ = nullptr;+    }+    return *this;+  }++  inline BoxedValue(PrimBool value)+    : union_{ .type_ = UnionValue::Type::BOOL, .value_ = { .as_bool_ = value } } {}++  inline BoxedValue(PrimChar value)+    : union_{ .type_ = UnionValue::Type::CHAR, .value_ = { .as_char_ = value } } {}++  inline BoxedValue(PrimInt value)+    : union_{ .type_ = UnionValue::Type::INT, .value_ = { .as_int_ = value } } {}++  inline BoxedValue(PrimFloat value)+    : union_{ .type_ = UnionValue::Type::FLOAT, .value_ = { .as_float_ = value } } {}+   template<class T, class... As>   static inline BoxedValue New(const As&... args) {     using Pointer = UnionValue::Pointer;@@ -88,22 +132,78 @@     return new_value;   } -  ~BoxedValue();+  inline ~BoxedValue() {+    Cleanup();+  } -  void Validate(const std::string& name) const;+  inline PrimBool AsBool() const {+    switch (union_.type_) {+      case UnionValue::Type::BOOL:+        return union_.value_.as_bool_;+      default:+        FAIL() << union_.CategoryName() << " is not a Bool value";+        __builtin_unreachable();+        break;+    }+  } -  bool AsBool() const;-  PrimChar AsChar() const;-  PrimInt AsInt() const;-  PrimFloat AsFloat() const;+  inline PrimChar AsChar() const {+    switch (union_.type_) {+      case UnionValue::Type::CHAR:+        return union_.value_.as_char_;+      default:+        FAIL() << union_.CategoryName() << " is not a Char value";+        __builtin_unreachable();+        break;+    }+  } +  inline PrimInt AsInt() const {+    switch (union_.type_) {+      case UnionValue::Type::INT:+        return union_.value_.as_int_;+      default:+        FAIL() << union_.CategoryName() << " is not an Int value";+        __builtin_unreachable();+        break;+    }+  }++  inline PrimFloat AsFloat() const {+    switch (union_.type_) {+      case UnionValue::Type::FLOAT:+        return union_.value_.as_float_;+      default:+        FAIL() << union_.CategoryName() << " is not a Float value";+        __builtin_unreachable();+        break;+    }+  }++  inline static bool Present(const BoxedValue& target) {+    return target.union_.type_ != UnionValue::Type::EMPTY;+  }++  inline static BoxedValue Require(const BoxedValue& target) {+    switch (target.union_.type_) {+      case UnionValue::Type::EMPTY:+        FAIL() << "Cannot require empty value";+        __builtin_unreachable();+        break;+      default:+        return target;+    }+  }++  inline static BoxedValue Strong(const WeakValue& target) {+    return BoxedValue(target);+  }++  void Validate(const std::string& name) const;+   const PrimString& AsString() const;   PrimCharBuffer& AsCharBuffer() const; -  static bool Present(const BoxedValue& target);-  static BoxedValue Require(const BoxedValue& target);-  static BoxedValue Strong(const WeakValue& target);-  private:   friend class ::TypeValue;   friend class WeakValue;@@ -116,16 +216,14 @@   explicit BoxedValue(const WeakValue& other);    template<class T>-  static inline BoxedValue FromPointer(T* pointer) {+  static inline BoxedValue FromPointer(const T* pointer) {     BoxedValue value;-    if (pointer) {-      value.union_.type_ = UnionValue::Type::BOXED;-      value.union_.value_.as_bytes_ =-        reinterpret_cast<unsigned char*>(pointer)-sizeof(UnionValue::Pointer);-      if (value.union_.value_.as_pointer_->object_ != pointer) {-        FAIL() << "Bad VAR_SELF pointer";-      }-      ++value.union_.value_.as_pointer_->strong_;+    value.union_.type_ = UnionValue::Type::BOXED;+    value.union_.value_.as_bytes_ =+      reinterpret_cast<unsigned char*>(const_cast<T*>(pointer))-sizeof(UnionValue::Pointer);+    if (value.union_.value_.as_pointer_->object_ != pointer ||+        ++value.union_.value_.as_pointer_->strong_ == 1) {+      FAIL() << "Bad VAR_SELF pointer " << pointer << " in " << pointer->CategoryName();     }     return value;   }@@ -136,7 +234,7 @@   void Cleanup();    UnionValue union_;-};+} __attribute__((packed));   class WeakValue {@@ -161,7 +259,7 @@   void Cleanup();    UnionValue union_;-};+} __attribute__((packed));  }  // namespace zeolite_internal 
base/builtin.0rp view
@@ -24,6 +24,8 @@ // - You do not need () to make a function call on a literal; something like //   true.asInt() is totally fine. concrete Bool {+  immutable+   refines AsBool   refines AsInt   refines AsFloat@@ -47,6 +49,8 @@ // - When converting to Int with asInt(), the value will always be unsigned, //   i.e., beween 0-255. ASCII characters will be in the 0-127 range. concrete Char {+  immutable+   refines AsBool   refines AsChar   refines AsInt@@ -68,6 +72,8 @@ // Notes: // - You must use () to make a function call on a literal, e.g, (1.1).asInt(). concrete Float {+  immutable+   refines AsBool   refines AsInt   refines AsFloat@@ -96,6 +102,8 @@ // - When converting to Char with asChar(), the value will always be treated as //   unsigned, i.e., beween 0-255 mod 256. concrete Int {+  immutable+   refines AsBool   refines AsChar   refines AsInt@@ -116,6 +124,8 @@ // - You do not need () to make a function call on a literal; something like //   "abc".defaultOrder() is totally fine. concrete String {+  immutable+   refines AsBool   refines DefaultOrder<Char>   refines Formatted@@ -139,7 +149,7 @@   // Example:   //   //   String.builder().append("foo").append("bar").build()-  @type builder () -> ([Append<String>&Build<String>])+  @type builder () -> ([Append<Formatted>&Build<String>]) }  // Built-in fixed-size buffer of byte data.@@ -216,8 +226,9 @@ // // Example: //-//   traverse (someIntOrder -> Int i) {-//     // code that uses index i+//   // defaultOrder() returns optional Order<Char> for traverse to use.+//   traverse ("letters".defaultOrder() -> Char c) {+//     // code that uses Char c //   } @value interface Order<|#x> {   // Returns an updated instance, or empty if the end was reached.@@ -229,6 +240,11 @@   next () -> (optional #self)    // Returns the value at the current position.+  //+  // Notes:+  // - If the value is discarded in traverse with -> _ then the call to get will+  //   be skipped; therefore, get should not have side-effects.+  // - This should not generally be used directly; use the traverse built-in.   get () -> (#x) } 
base/category-source.hpp view
@@ -41,17 +41,21 @@   __builtin_unreachable(); \   } -BoxedValue Box_Bool(bool value);+#define VAR_SELF TypeValue::Var_self(this)++#define PARAM_SELF shared_from_this()++BoxedValue Box_Bool(PrimBool value); BoxedValue Box_String(const PrimString& value); BoxedValue Box_Char(PrimChar value); BoxedValue Box_Int(PrimInt value); BoxedValue Box_Float(PrimFloat value); -S<TypeInstance> Merge_Intersect(L<S<const TypeInstance>> params);-S<TypeInstance> Merge_Union(L<S<const TypeInstance>> params);+S<const TypeInstance> Merge_Intersect(const L<S<const TypeInstance>>& params);+S<const TypeInstance> Merge_Union(const L<S<const TypeInstance>>& params); -const S<TypeInstance>& GetMerged_Any();-const S<TypeInstance>& GetMerged_All();+const S<const TypeInstance>& GetMerged_Any();+const S<const TypeInstance>& GetMerged_All();  extern const BoxedValue Var_empty; @@ -77,13 +81,13 @@  class TypeInstance {  public:-  inline static ReturnTuple Call(const S<TypeInstance>& target,+  inline static ReturnTuple Call(const S<const TypeInstance>& target,                                  const TypeFunction& label,-                                 ParamTuple params, const ValueTuple& args) {+                                 const ParamTuple& params, const ValueTuple& args) {     if (target == nullptr) {       FAIL() << "Function called on null value";     }-    return target->Dispatch(target, label, params, args);+    return target->Dispatch(label, params, args);   }    virtual std::string CategoryName() const = 0;@@ -104,7 +108,7 @@   }    virtual bool TypeArgsForParent(-    const TypeCategory& category, std::vector<S<const TypeInstance>>& args) const+    const CategoryId& category, std::vector<S<const TypeInstance>>& args) const   { return false; }    ALWAYS_PERMANENT(TypeInstance)@@ -113,8 +117,8 @@  protected:   TypeInstance() = default; -  virtual ReturnTuple Dispatch(const S<TypeInstance>& self, const TypeFunction& label,-                               const ParamTuple& params, const ValueTuple& args);+  virtual ReturnTuple Dispatch(const TypeFunction& label,+                               const ParamTuple& params, const ValueTuple& args) const;    virtual bool CanConvertFrom(const S<const TypeInstance>& from) const   { return false; }@@ -148,14 +152,12 @@   virtual MergeType InstanceMergeType() const   { return MergeType::SINGLE; } -  virtual std::vector<S<const TypeInstance>> MergedTypes() const {+  virtual const L<S<const TypeInstance>>& MergedTypes() const {     FAIL() << "Category " << CategoryName() << " is not a merged type";     __builtin_unreachable();   } }; -#define VAR_SELF TypeValue::Var_self(this)- class TypeValue {  public:   inline static ReturnTuple Call(const BoxedValue& target, const ValueFunction& label,@@ -176,7 +178,10 @@   TypeValue() = default;    template<class T>-  inline static BoxedValue Var_self(T* must_be_this) {+  inline BoxedValue Var_self(T* must_be_this) const {+    if (must_be_this != this) {+      FAIL() << "Var_self called without passing this";+    }     return BoxedValue::FromPointer(must_be_this);   } @@ -193,43 +198,17 @@   } }; -class AnonymousOrder : public TypeValue {- protected:-  // Passing in the function labels allows linking without depending on Order-  // when this class isn't used anywhere.-  AnonymousOrder(const BoxedValue cont,-                 const ValueFunction& func_next,-                 const ValueFunction& func_get);--  std::string CategoryName() const final;-  ReturnTuple Dispatch(const ValueFunction& label,-                       const ParamTuple& params,-                       const ValueTuple& args) final;--  virtual ~AnonymousOrder() = default;-- private:-  // This *must* return VAR_SELF in the most-derived class!-  virtual BoxedValue Var_self() = 0;-  virtual BoxedValue Call_next(const BoxedValue& self) = 0;-  virtual BoxedValue Call_get(const BoxedValue& self) = 0;--  const BoxedValue container;-  const ValueFunction& function_next;-  const ValueFunction& function_get;-};- template <int P, class T> class InstanceCache {  public:-  using Creator = std::function<S<T>(typename Params<P>::Type)>;+  using Creator = std::function<S<T>(const typename Params<P>::Type&)>;    InstanceCache(const Creator& create) : create_(create) {} -  S<T> GetOrCreate(typename Params<P>::Type params) {+  S<T> GetOrCreate(const typename Params<P>::Type& params) {     while (lock_.test_and_set(std::memory_order_acquire));     auto& cached = cache_[GetKeyFromParams<P>(params)];-    S<T> type = cached;+    S<T> type = cached.lock();     if (!type) {       cached = type = create_(params);     }@@ -237,10 +216,22 @@     return type;   } +  void Remove(const typename Params<P>::Type& params) {+    while (lock_.test_and_set(std::memory_order_acquire));+    auto pos = cache_.find(GetKeyFromParams<P>(params));+    // Skip erasing if it's a valid pointer, since that could mean that another+    // thread created a new instance while the one we're trying to remove was in+    // the process of being destructed.+    if (pos != cache_.end() && !pos->second.lock()) {+      cache_.erase(pos);+    }+    lock_.clear(std::memory_order_release);+  }+  private:   const Creator create_;   std::atomic_flag lock_ = ATOMIC_FLAG_INIT;-  std::map<typename ParamsKey<P>::Type, S<T>> cache_;+  std::map<typename ParamsKey<P>::Type, W<T>> cache_; };  #endif  // CATEGORY_SOURCE_HPP_
base/function.hpp view
@@ -30,7 +30,7 @@   const int return_count;   const char* const category;   const char* const function;-  const CollectionType collection;+  const CategoryId collection;   const int function_num; }; @@ -40,7 +40,7 @@   const int return_count;   const char* const category;   const char* const function;-  const CollectionType collection;+  const CategoryId collection;   const int function_num; }; @@ -50,7 +50,7 @@   const int return_count;   const char* const category;   const char* const function;-  const CollectionType collection;+  const CategoryId collection;   const int function_num; }; 
− base/internal.hpp
@@ -1,126 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--#ifndef INTERNAL_HPP_-#define INTERNAL_HPP_--#include <algorithm>-#include <atomic>--#include "category-source.hpp"---#define COLLECTION_ID(vp) (int) 1000000009 * (long) (vp)--template<class F>-struct DispatchTable {-  constexpr DispatchTable() : key(), table(nullptr), size(0) {}--  template<int S>-  DispatchTable(CollectionType k, const F(&t)[S]) : key(k), table(t), size(S) {}--  inline bool operator < (const DispatchTable<F>& other) const { return key < other.key; }--  CollectionType key;-  const F* table;-  int size;-};--template<class F>-struct DispatchSingle {-  constexpr DispatchSingle() : key(nullptr), value() {}--  DispatchSingle(const void* k, const F v) : key(k), value(v) {}--  inline bool operator < (const DispatchSingle<F>& other) const { return key < other.key; }--  const void* key;-  F value;-};--struct StaticSort {-  template<class T, int S>-  StaticSort(T(&table)[S]) {-    std::sort(table, table+S);-  }-};--template<class T>-struct LoopLimit {};--template<class F>-struct LoopLimit<DispatchTable<F>> {-  // See function-calls.0rt for tests.-  static constexpr int max = 15;-};--template<class F>-struct LoopLimit<DispatchSingle<F>> {-  // See function-calls.0rt for tests.-  static constexpr int max = 15;-};--template<bool L, class T, int S>-struct DispatchChoice {};--template<class T>-struct DispatchChoice<true, T, 0> {-  template<class K>-  static const T* Select(K key, T* table) {-    return nullptr;-  }-};-template<class T, int S>-struct DispatchChoice<true, T, S> {-  template<class K>-  static const T* Select(K key, T* table) {-    if (table->key == key) {-      return table;-    }-    return DispatchChoice<true, T, S-1>::Select(key, table+1);-  }-};--template<class T, int S>-struct DispatchChoice<false, T, S> {-  template<class K>-  static const T* Select(K key, T* table) {-    int i = 0, j = S, k;-    while ((k = (i+j)/2) > i) {-      if (table[k].key < key) {-        i = k;-      } else if (table[k].key > key) {-        j = k;-      } else {-        return &table[k];-      }-    }-    if (table[k].key == key) {-      return &table[k];-    } else {-      return nullptr;-    }-  }-};--template<class K, class T, int S>-const T* DispatchSelect(K key, T(&table)[S]) {-  return DispatchChoice<(S <= LoopLimit<T>::max), T, S>::Select(key, table);-}--#endif  // INTERNAL_HPP_
base/logging.hpp view
@@ -97,7 +97,7 @@    #define PRED_CONTEXT_POINT(point) -  #define CAPTURE_CREATION+  #define CAPTURE_CREATION(name)    #define TRACE_CREATION @@ -118,7 +118,12 @@  protected:   virtual ~TraceContext() = default; +  static inline const char* GetName() {+    return GetCurrent() ? GetCurrent()->Name() : nullptr;+  }+  private:+  virtual const char* Name() const { return nullptr; }   virtual void SetLocal(const char*) = 0;   virtual void AppendTrace(TraceList& trace) const = 0;   virtual const TraceContext* GetNext() const = 0;@@ -131,6 +136,7 @@     : at_(nullptr), name_(name), capture_to_(this) {}   private:+  const char* Name() const final { return name_; }   void SetLocal(const char*) final;   void AppendTrace(TraceList& trace) const final;   const TraceContext* GetNext() const final;@@ -231,6 +237,8 @@       GetCurrent()->LogCall(name, at);     }   }++  static void DisableCallLogging();   protected:   virtual ~LogCalls() = default;
base/pooled.hpp view
@@ -60,39 +60,40 @@   friend class ::ReturnTuple;   friend class ::ParamTuple; -  constexpr PoolArray() : array_(nullptr) {}+  constexpr PoolArray() : size_(0), array_(nullptr) {} -  PoolArray(int size) : array_(PoolManager<T>::Take(size)) {}+  PoolArray(int size) : size_(size), array_(PoolManager<T>::Take(size_)) {} -  PoolArray(PoolArray&& other) : array_(other.array_) {+  PoolArray(PoolArray&& other) : size_(other.size_), array_(other.array_) {+    other.size_  = 0;     other.array_ = nullptr;   }    PoolArray& operator = (PoolArray&& other) {-    array_ = other.array_;-    other.array_ = nullptr;+    if (&other != this) {+      PoolManager<T>::Return(array_, size_);+      size_  = other.size_;+      array_ = other.array_;+      other.size_  = 0;+      other.array_ = nullptr;+    }     return *this;   } +  inline int Size() const {+    return size_;+  }+   const T& operator [] (int i) const {-    if (!array_ || i < 0 || i >= array_->size) {-      FAIL() << "Bad array index " << i;-    }     return array_->data()[i];   }    T& operator [] (int i) {-    if (!array_ || i < 0 || i >= array_->size) {-      FAIL() << "Bad array index " << i;-    }     return array_->data()[i];   }    template<class...Ts>   inline void Init(Ts... data) {-    if (sizeof...(Ts) > (array_? array_->size : 0)) {-      FAIL() << "Too many init values " << sizeof...(Ts);-    }     InitRec(0, std::move(data)...);   } @@ -105,12 +106,13 @@   inline void InitRec(int i) {}    ~PoolArray() {-    PoolManager<T>::Return(array_);+    PoolManager<T>::Return(array_, size_);   }    PoolArray(const PoolArray&) = delete;   PoolArray& operator = (const PoolArray&) = delete; +  int size_;   PoolStorage<T>* array_; }; 
base/src/Extension_Bool.cpp view
@@ -39,55 +39,53 @@ struct ExtType_Bool : public Type_Bool {   inline ExtType_Bool(Category_Bool& p, Params<0>::Type params) : Type_Bool(p, params) {} -  ReturnTuple Call_default(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_default(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Bool.default")     return ReturnTuple(Box_Bool(false));   } -  ReturnTuple Call_equals(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_equals(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Bool.equals")-    const bool Var_arg1 = (args.At(0)).AsBool();-    const bool Var_arg2 = (args.At(1)).AsBool();+    const PrimBool Var_arg1 = (args.At(0)).AsBool();+    const PrimBool Var_arg2 = (args.At(1)).AsBool();     return ReturnTuple(Box_Bool(Var_arg1==Var_arg2));   } }; -ReturnTuple DispatchBool(bool value, const ValueFunction& label,+ReturnTuple DispatchBool(PrimBool value, const ValueFunction& label,                          const ParamTuple& params, const ValueTuple& args) {-  if (&label == &Function_AsBool_asBool) {-    TRACE_FUNCTION("Bool.asBool")-    return ReturnTuple(Box_Bool(value));-  }-  if (&label == &Function_AsFloat_asFloat) {-    TRACE_FUNCTION("Bool.asFloat")-    return ReturnTuple(Box_Float(value ? 1.0 : 0.0));-  }-  if (&label == &Function_AsInt_asInt) {-    TRACE_FUNCTION("Bool.asInt")-    return ReturnTuple(Box_Int(value? 1 : 0));-  }-  if (&label == &Function_Formatted_formatted) {-    TRACE_FUNCTION("Bool.formatted")-    return ReturnTuple(Box_String(value? "true" : "false"));+  switch (label.collection) {+    case CategoryId_AsBool:+      return ReturnTuple(Box_Bool(value));+    case CategoryId_AsFloat:+      return ReturnTuple(Box_Float(value ? 1.0 : 0.0));+    case CategoryId_AsInt:+      return ReturnTuple(Box_Int(value? 1 : 0));+    case CategoryId_Formatted:+      return ReturnTuple(Box_String(value? "true" : "false"));+    default:+      FAIL() << "Bool does not implement " << label;+      __builtin_unreachable();   }-  FAIL() << "Bool does not implement " << label;-  __builtin_unreachable(); }  Category_Bool& CreateCategory_Bool() {   static auto& category = *new ExtCategory_Bool();   return category; }-S<Type_Bool> CreateType_Bool(Params<0>::Type params) {++S<const Type_Bool> CreateType_Bool(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_Bool(CreateCategory_Bool(), Params<0>::Type()));   return cached; } +void RemoveType_Bool(const Params<0>::Type& params) {}+ #ifdef ZEOLITE_PUBLIC_NAMESPACE }  // namespace ZEOLITE_PUBLIC_NAMESPACE using namespace ZEOLITE_PUBLIC_NAMESPACE; #endif  // ZEOLITE_PUBLIC_NAMESPACE -BoxedValue Box_Bool(bool value) {+BoxedValue Box_Bool(PrimBool value) {   return BoxedValue(value); }
base/src/Extension_Char.cpp view
@@ -42,29 +42,29 @@ struct ExtType_Char : public Type_Char {   inline ExtType_Char(Category_Char& p, Params<0>::Type params) : Type_Char(p, params) {} -  ReturnTuple Call_maxBound(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_maxBound(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Char.maxBound")     return ReturnTuple(Box_Char('\xff'));   } -  ReturnTuple Call_minBound(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_minBound(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Char.minBound")     return ReturnTuple(Box_Char('\0'));   } -  ReturnTuple Call_default(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_default(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Char.default")     return ReturnTuple(Box_Char('\0'));   } -  ReturnTuple Call_equals(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_equals(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Char.equals")     const PrimChar Var_arg1 = (args.At(0)).AsChar();     const PrimChar Var_arg2 = (args.At(1)).AsChar();     return ReturnTuple(Box_Bool(Var_arg1==Var_arg2));   } -  ReturnTuple Call_lessThan(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_lessThan(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Char.lessThan")     const PrimChar Var_arg1 = (args.At(0)).AsChar();     const PrimChar Var_arg2 = (args.At(1)).AsChar();@@ -74,38 +74,34 @@  ReturnTuple DispatchChar(PrimChar value, const ValueFunction& label,                          const ParamTuple& params, const ValueTuple& args) {-  if (&label == &Function_AsBool_asBool) {-    TRACE_FUNCTION("Char.asBool")-    return ReturnTuple(Box_Bool(value != '\0'));-  }-  if (&label == &Function_AsChar_asChar) {-    TRACE_FUNCTION("Char.asChar")-    return ReturnTuple(Box_Char(value));-  }-  if (&label == &Function_AsFloat_asFloat) {-    TRACE_FUNCTION("Char.asFloat")-    return ReturnTuple(Box_Float(((int) value + 256) % 256));-  }-  if (&label == &Function_AsInt_asInt) {-    TRACE_FUNCTION("Char.asInt")-    return ReturnTuple(Box_Int(((int) value + 256) % 256));-  }-  if (&label == &Function_Formatted_formatted) {-    TRACE_FUNCTION("Char.formatted")-    return ReturnTuple(Box_String(PrimString(1,value)));+  switch (label.collection) {+    case CategoryId_AsBool:+      return ReturnTuple(Box_Bool(value != '\0'));+    case CategoryId_AsChar:+      return ReturnTuple(Box_Char(value));+    case CategoryId_AsFloat:+      return ReturnTuple(Box_Float(((int) value + 256) % 256));+    case CategoryId_AsInt:+      return ReturnTuple(Box_Int(((int) value + 256) % 256));+    case CategoryId_Formatted:+      return ReturnTuple(Box_String(PrimString(1, value)));+    default:+      FAIL() << "Char does not implement " << label;+      __builtin_unreachable();   }-  FAIL() << "Char does not implement " << label;-  __builtin_unreachable(); }  Category_Char& CreateCategory_Char() {   static auto& category = *new ExtCategory_Char();   return category; }-S<Type_Char> CreateType_Char(Params<0>::Type params) {++S<const Type_Char> CreateType_Char(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_Char(CreateCategory_Char(), Params<0>::Type()));   return cached; }++void RemoveType_Char(const Params<0>::Type& params) {}  #ifdef ZEOLITE_PUBLIC_NAMESPACE }  // namespace ZEOLITE_PUBLIC_NAMESPACE
base/src/Extension_CharBuffer.cpp view
@@ -29,7 +29,7 @@ namespace ZEOLITE_PUBLIC_NAMESPACE { #endif  // ZEOLITE_PUBLIC_NAMESPACE -BoxedValue CreateValue_CharBuffer(S<Type_CharBuffer> parent, PrimCharBuffer buffer);+BoxedValue CreateValue_CharBuffer(S<const Type_CharBuffer> parent, PrimCharBuffer buffer);  struct ExtCategory_CharBuffer : public Category_CharBuffer { };@@ -37,7 +37,7 @@ struct ExtType_CharBuffer : public Type_CharBuffer {   inline ExtType_CharBuffer(Category_CharBuffer& p, Params<0>::Type params) : Type_CharBuffer(p, params) {} -  ReturnTuple Call_new(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_new(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("CharBuffer.new")     const PrimInt Var_arg1 = (args.At(0)).AsInt();     if (Var_arg1 < 0) {@@ -48,7 +48,8 @@ };  struct ExtValue_CharBuffer : public Value_CharBuffer {-  inline ExtValue_CharBuffer(S<Type_CharBuffer> p, PrimCharBuffer value) : Value_CharBuffer(p), value_(std::move(value)) {}+  inline ExtValue_CharBuffer(S<const Type_CharBuffer> p, PrimCharBuffer value)+    : Value_CharBuffer(std::move(p)), value_(std::move(value)) {}    ReturnTuple Call_readAt(const ParamTuple& params, const ValueTuple& args) final {     TRACE_FUNCTION("CharBuffer.readAt")@@ -96,12 +97,16 @@   static auto& category = *new ExtCategory_CharBuffer();   return category; }-S<Type_CharBuffer> CreateType_CharBuffer(Params<0>::Type params) {++S<const Type_CharBuffer> CreateType_CharBuffer(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_CharBuffer(CreateCategory_CharBuffer(), Params<0>::Type()));   return cached; }-BoxedValue CreateValue_CharBuffer(S<Type_CharBuffer> parent, PrimCharBuffer value) {-  return BoxedValue::New<ExtValue_CharBuffer>(parent, std::move(value));++void RemoveType_CharBuffer(const Params<0>::Type& params) {}++BoxedValue CreateValue_CharBuffer(S<const Type_CharBuffer> parent, PrimCharBuffer value) {+  return BoxedValue::New<ExtValue_CharBuffer>(std::move(parent), std::move(value)); }  #ifdef ZEOLITE_PUBLIC_NAMESPACE
base/src/Extension_Float.cpp view
@@ -42,19 +42,19 @@ struct ExtType_Float : public Type_Float {   inline ExtType_Float(Category_Float& p, Params<0>::Type params) : Type_Float(p, params) {} -  ReturnTuple Call_default(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_default(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Float.default")     return ReturnTuple(Box_Float(0.0));   } -  ReturnTuple Call_equals(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_equals(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Float.equals")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     const PrimFloat Var_arg2 = (args.At(1)).AsFloat();     return ReturnTuple(Box_Bool(Var_arg1==Var_arg2));   } -  ReturnTuple Call_lessThan(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_lessThan(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Float.lessThan")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     const PrimFloat Var_arg2 = (args.At(1)).AsFloat();@@ -64,36 +64,36 @@  ReturnTuple DispatchFloat(PrimFloat value, const ValueFunction& label,                           const ParamTuple& params, const ValueTuple& args) {-  if (&label == &Function_AsBool_asBool) {-    TRACE_FUNCTION("Float.asBool")-    return ReturnTuple(Box_Bool(value != 0.0));-  }-  if (&label == &Function_AsFloat_asFloat) {-    TRACE_FUNCTION("Float.asFloat")-    return ReturnTuple(Box_Float(value));-  }-  if (&label == &Function_AsInt_asInt) {-    TRACE_FUNCTION("Float.asInt")-    return ReturnTuple(Box_Int(value));-  }-  if (&label == &Function_Formatted_formatted) {-    TRACE_FUNCTION("Float.formatted")-    std::ostringstream output;-    output << value;-    return ReturnTuple(Box_String(output.str()));+  switch (label.collection) {+    case CategoryId_AsBool:+      return ReturnTuple(Box_Bool(value != 0.0));+    case CategoryId_AsFloat:+      return ReturnTuple(Box_Float(value));+    case CategoryId_AsInt:+      return ReturnTuple(Box_Int(value));+    case CategoryId_Formatted: {+      // NOTE: std::to_string does weird things with significant digits.+      std::ostringstream output;+      output << value;+      return ReturnTuple(Box_String(output.str()));+    }+    default:+      FAIL() << "Float does not implement " << label;+      __builtin_unreachable();   }-  FAIL() << "Float does not implement " << label;-  __builtin_unreachable(); }  Category_Float& CreateCategory_Float() {   static auto& category = *new ExtCategory_Float();   return category; }-S<Type_Float> CreateType_Float(Params<0>::Type params) {++S<const Type_Float> CreateType_Float(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_Float(CreateCategory_Float(), Params<0>::Type()));   return cached; }++void RemoveType_Float(const Params<0>::Type& params) {}  #ifdef ZEOLITE_PUBLIC_NAMESPACE }  // namespace ZEOLITE_PUBLIC_NAMESPACE
base/src/Extension_Int.cpp view
@@ -17,7 +17,7 @@ // Author: Kevin P. Barry [ta0kira@gmail.com]  #include <climits>-#include <sstream>+#include <string>  #include "category-source.hpp" #include "Streamlined_Int.hpp"@@ -45,29 +45,29 @@ struct ExtType_Int : public Type_Int {   inline ExtType_Int(Category_Int& p, Params<0>::Type params) : Type_Int(p, params) {} -  ReturnTuple Call_maxBound(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_maxBound(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Int.maxBound")     return ReturnTuple(Box_Int(9223372036854775807LL));   } -  ReturnTuple Call_minBound(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_minBound(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Int.minBound")     return ReturnTuple(Box_Int(-9223372036854775807LL - 1LL));   } -  ReturnTuple Call_default(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_default(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Int.default")     return ReturnTuple(Box_Int(0));   } -  ReturnTuple Call_equals(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_equals(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Int.equals")     const PrimInt Var_arg1 = (args.At(0)).AsInt();     const PrimInt Var_arg2 = (args.At(1)).AsInt();     return ReturnTuple(Box_Bool(Var_arg1==Var_arg2));   } -  ReturnTuple Call_lessThan(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_lessThan(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Int.lessThan")     const PrimInt Var_arg1 = (args.At(0)).AsInt();     const PrimInt Var_arg2 = (args.At(1)).AsInt();@@ -77,40 +77,34 @@  ReturnTuple DispatchInt(PrimInt value, const ValueFunction& label,                         const ParamTuple& params, const ValueTuple& args) {-  if (&label == &Function_AsBool_asBool) {-    TRACE_FUNCTION("Int.asBool")-    return ReturnTuple(Box_Bool(value != 0));-  }-  if (&label == &Function_AsChar_asChar) {-    TRACE_FUNCTION("Int.asChar")-    return ReturnTuple(Box_Char(PrimChar(((value % 256) + 256) % 256)));-  }-  if (&label == &Function_AsFloat_asFloat) {-    TRACE_FUNCTION("Int.asFloat")-    return ReturnTuple(Box_Float(value));-  }-  if (&label == &Function_AsInt_asInt) {-    TRACE_FUNCTION("Int.asInt")-    return ReturnTuple(Box_Int(value));-  }-  if (&label == &Function_Formatted_formatted) {-    TRACE_FUNCTION("Int.formatted")-    std::ostringstream output;-    output << value;-    return ReturnTuple(Box_String(output.str()));+  switch (label.collection) {+    case CategoryId_AsBool:+      return ReturnTuple(Box_Bool(value != 0));+    case CategoryId_AsChar:+      return ReturnTuple(Box_Char(PrimChar(((value % 256) + 256) % 256)));+    case CategoryId_AsFloat:+      return ReturnTuple(Box_Float(value));+    case CategoryId_AsInt:+      return ReturnTuple(Box_Int(value));+    case CategoryId_Formatted:+      return ReturnTuple(Box_String(std::to_string(value)));+    default:+      FAIL() << "Int does not implement " << label;+      __builtin_unreachable();   }-  FAIL() << "Int does not implement " << label;-  __builtin_unreachable(); }  Category_Int& CreateCategory_Int() {   static auto& category = *new ExtCategory_Int();   return category; }-S<Type_Int> CreateType_Int(Params<0>::Type params) {++S<const Type_Int> CreateType_Int(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_Int(CreateCategory_Int(), Params<0>::Type()));   return cached; }++void RemoveType_Int(const Params<0>::Type& params) {}  #ifdef ZEOLITE_PUBLIC_NAMESPACE }  // namespace ZEOLITE_PUBLIC_NAMESPACE
base/src/Extension_String.cpp view
@@ -58,7 +58,7 @@     if (&label == &Function_Append_append) {       TRACE_FUNCTION("StringBuilder.append")       std::lock_guard<std::mutex> lock(mutex);-      output_ << args.At(0).AsString();+      output_ << TypeValue::Call(args.At(0), Function_Formatted_formatted, ParamTuple(), ArgTuple()).Only().AsString();       return ReturnTuple(VAR_SELF);     }     if (&label == &Function_Build_build) {@@ -77,30 +77,30 @@ struct ExtType_String : public Type_String {   inline ExtType_String(Category_String& p, Params<0>::Type params) : Type_String(p, params) {} -  ReturnTuple Call_builder(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_builder(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("String.builder")     return ReturnTuple(BoxedValue::New<Value_StringBuilder>());   } -  ReturnTuple Call_default(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_default(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("String.default")     return ReturnTuple(Box_String(""));   } -  ReturnTuple Call_equals(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_equals(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("String.equals")     const BoxedValue& Var_arg1 = (args.At(0));     const BoxedValue& Var_arg2 = (args.At(1));     return ReturnTuple(Box_Bool(Var_arg1.AsString()==Var_arg2.AsString()));   } -  ReturnTuple Call_fromCharBuffer(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_fromCharBuffer(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("String.fromCharBuffer")     const BoxedValue& Var_arg1 = (args.At(0));     return ReturnTuple(Box_String(PrimString(Var_arg1.AsCharBuffer())));   } -  ReturnTuple Call_lessThan(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_lessThan(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("String.lessThan")     const BoxedValue& Var_arg1 = (args.At(0));     const BoxedValue& Var_arg2 = (args.At(1));@@ -108,41 +108,51 @@   } }; -struct StringOrder final : public AnonymousOrder {+class StringOrder : public TypeValue {+ public:   StringOrder(BoxedValue container, const std::string& s)-    : AnonymousOrder(container, Function_Order_next, Function_Order_get), value(s) {}+    : container_(container), value_(s) {} -  BoxedValue Var_self() final {-    return VAR_SELF;-  }+  std::string CategoryName() const final { return "StringOrder"; } -  BoxedValue Call_next(const BoxedValue& self) final {-    if (index+1 >= value.size()) {-      return Var_empty;-    } else {-      ++index;-      return self;+  ReturnTuple Dispatch(const ValueFunction& label,+                       const ParamTuple& params,+                       const ValueTuple& args) final {+    if (&label == &Function_Order_next) {+      TRACE_FUNCTION("StringOrder.next")+      if (index_+1 >= value_.size()) {+        return ReturnTuple(Var_empty);+      } else {+        ++index_;+        return ReturnTuple(VAR_SELF);+      }     }-  }--  BoxedValue Call_get(const BoxedValue& self) final {-    if (index >= value.size()) FAIL() << "Iterated past end of String";-    return Box_Char(value[index]);+    if (&label == &Function_Order_get) {+      TRACE_FUNCTION("StringOrder.get")+      if (index_ >= value_.size()) {+        FAIL() << "Iterated past end of String";+      }+      return ReturnTuple(Box_Char(value_[index_]));+    }+    return TypeValue::Dispatch(label, params, args);   } -  const std::string& value;-  int index = 0;+ private:+  const BoxedValue container_;+  const std::string& value_;+  int index_ = 0; };  struct ExtValue_String : public Value_String {-  inline ExtValue_String(S<Type_String> p, const PrimString& value) : Value_String(p), value_(value) {}+  inline ExtValue_String(S<const Type_String> p, const PrimString& value)+    : Value_String(std::move(p)), value_(value) {} -  ReturnTuple Call_asBool(const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_asBool(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("String.asBool")     return ReturnTuple(Box_Bool(value_.size() != 0));   } -  ReturnTuple Call_defaultOrder(const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_defaultOrder(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("String.defaultOrder")     if (value_.empty()) {       return ReturnTuple(Var_empty);@@ -151,12 +161,12 @@     }   } -  ReturnTuple Call_formatted(const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_formatted(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("String.formatted")     return ReturnTuple(VAR_SELF);   } -  ReturnTuple Call_readAt(const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_readAt(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("String.readAt")     const PrimInt Var_arg1 = (args.At(0)).AsInt();     if (Var_arg1 < 0 || Var_arg1 >= value_.size()) {@@ -165,12 +175,12 @@     return ReturnTuple(Box_Char(value_[Var_arg1]));   } -  ReturnTuple Call_size(const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_size(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("String.size")     return ReturnTuple(Box_Int(value_.size()));   } -  ReturnTuple Call_subSequence(const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_subSequence(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("String.subSequence")     const PrimInt Var_arg1 = (args.At(0)).AsInt();     const PrimInt Var_arg2 = (args.At(1)).AsInt();@@ -192,10 +202,13 @@   static auto& category = *new ExtCategory_String();   return category; }-S<Type_String> CreateType_String(Params<0>::Type params) {++S<const Type_String> CreateType_String(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_String(CreateCategory_String(), Params<0>::Type()));   return cached; }++void RemoveType_String(const Params<0>::Type& params) {}  #ifdef ZEOLITE_PUBLIC_NAMESPACE }  // namespace ZEOLITE_PUBLIC_NAMESPACE
base/src/boxed.cpp view
@@ -27,7 +27,7 @@ namespace ZEOLITE_PUBLIC_NAMESPACE { #endif  // ZEOLITE_PUBLIC_NAMESPACE -ReturnTuple DispatchBool(bool value, const ValueFunction& label,+ReturnTuple DispatchBool(PrimBool value, const ValueFunction& label,                          const ParamTuple& params, const ValueTuple& args);  ReturnTuple DispatchChar(PrimChar value, const ValueFunction& label,@@ -54,18 +54,25 @@     const auto strong = the_union.value_.as_pointer_->strong_.load();     const auto weak   = the_union.value_.as_pointer_->weak_.load();     const TypeValue* const object = the_union.value_.as_pointer_->object_;+    const std::string type = object ? the_union.CategoryName() : "deleted value"; -    if ((strong == 0 && object) || (weak == 0 && strong > 0)) {-      FAIL() << "Leaked " << the_union.CategoryName() << " " << name << " at "+    if (strong == 0 && object) {+      FAIL() << "Leaked " << type << " " << name << " at "              << ((void*) the_union.value_.as_pointer_) << " (S: "              << std::hex << strong << " W: " << weak << ")";     } -    if ((strong != 0 && !object)) {-      FAIL() << "Invalid counts for freed value " << name << " at "+    if (strong != 0 && !object) {+      FAIL() << "Prematurely deleted value " << name << " at "              << ((void*) the_union.value_.as_pointer_) << " (S: "              << std::hex << strong << " W: " << weak << ")";     }++    if (strong < 0 || weak < 0 || (weak == 0 && strong > 0)) {+      FAIL() << "Invalid counts for " << type << " " << name << " at "+             << ((void*) the_union.value_.as_pointer_) << " (S: "+             << std::hex << strong << " W: " << weak << ")";+    }   } } @@ -86,48 +93,6 @@   } } -BoxedValue::BoxedValue(const BoxedValue& other)-  : union_(other.union_) {-  switch (union_.type_) {-    case UnionValue::Type::BOXED:-      ++union_.value_.as_pointer_->strong_;-      break;-    default:-      break;-  }-}--BoxedValue& BoxedValue::operator = (const BoxedValue& other) {-  if (&other != this) {-    Cleanup();-    union_ = other.union_;-    switch (union_.type_) {-      case UnionValue::Type::BOXED:-        ++union_.value_.as_pointer_->strong_;-        break;-      default:-        break;-    }-  }-  return *this;-}--BoxedValue::BoxedValue(BoxedValue&& other)-  : union_(other.union_) {-  other.union_.type_  = UnionValue::Type::EMPTY;-  other.union_.value_.as_pointer_ = nullptr;-}--BoxedValue& BoxedValue::operator = (BoxedValue&& other) {-  if (&other != this) {-    Cleanup();-    union_ = other.union_;-    other.union_.type_  = UnionValue::Type::EMPTY;-    other.union_.value_.as_pointer_ = nullptr;-  }-  return *this;-}- BoxedValue::BoxedValue(const WeakValue& other)   : union_(other.union_) {   switch (union_.type_) {@@ -147,70 +112,10 @@   } } -BoxedValue::BoxedValue(bool value)-  : union_{ .type_ = UnionValue::Type::BOOL, .value_ = { .as_bool_ = value } } {}--BoxedValue::BoxedValue(PrimChar value)-  : union_{ .type_ = UnionValue::Type::CHAR, .value_ = { .as_char_ = value } } {}--BoxedValue::BoxedValue(PrimInt value)-  : union_{ .type_ = UnionValue::Type::INT, .value_ = { .as_int_ = value } } {}--BoxedValue::BoxedValue(PrimFloat value)-  : union_{ .type_ = UnionValue::Type::FLOAT, .value_ = { .as_float_ = value } } {}--BoxedValue::~BoxedValue() {-  Cleanup();-}- void BoxedValue::Validate(const std::string& name) const {   zeolite_internal::Validate(name, union_); } -bool BoxedValue::AsBool() const {-  switch (union_.type_) {-    case UnionValue::Type::BOOL:-      return union_.value_.as_bool_;-    default:-      FAIL() << union_.CategoryName() << " is not a Bool value";-      __builtin_unreachable();-      break;-  }-}--PrimChar BoxedValue::AsChar() const {-  switch (union_.type_) {-    case UnionValue::Type::CHAR:-      return union_.value_.as_char_;-    default:-      FAIL() << union_.CategoryName() << " is not a Char value";-      __builtin_unreachable();-      break;-  }-}--PrimInt BoxedValue::AsInt() const {-  switch (union_.type_) {-    case UnionValue::Type::INT:-      return union_.value_.as_int_;-    default:-      FAIL() << union_.CategoryName() << " is not an Int value";-      __builtin_unreachable();-      break;-  }-}--PrimFloat BoxedValue::AsFloat() const {-  switch (union_.type_) {-    case UnionValue::Type::FLOAT:-      return union_.value_.as_float_;-    default:-      FAIL() << union_.CategoryName() << " is not a Float value";-      __builtin_unreachable();-      break;-  }-}- const PrimString& BoxedValue::AsString() const {   switch (union_.type_) {     case UnionValue::Type::BOXED:@@ -237,28 +142,6 @@       __builtin_unreachable();       break;   }-}--// static-bool BoxedValue::Present(const BoxedValue& target) {-  return target.union_.type_ != UnionValue::Type::EMPTY;-}--// static-BoxedValue BoxedValue::Require(const BoxedValue& target) {-  switch (target.union_.type_) {-    case UnionValue::Type::EMPTY:-      FAIL() << "Cannot require empty value";-      __builtin_unreachable();-      break;-    default:-      return target;-  }-}--// static-BoxedValue BoxedValue::Strong(const WeakValue& target) {-  return BoxedValue(target); }  ReturnTuple BoxedValue::Dispatch(
base/src/category-source.cpp view
@@ -19,27 +19,39 @@ #include "category-source.hpp"  #include <algorithm>+#include <sstream>  #include "logging.hpp"   namespace { +void Remove_Intersect(const L<S<const TypeInstance>>& params);+void Remove_Union(const L<S<const TypeInstance>>& params);+ struct Type_Intersect : public TypeInstance {-  Type_Intersect(L<S<const TypeInstance>> params) : params_(params.begin(), params.end()) {}+  inline Type_Intersect(L<S<const TypeInstance>> params) : params_(std::move(params)) {} +  inline ~Type_Intersect() { Remove_Intersect(params_); }+   std::string CategoryName() const final { return "(intersection)"; }    void BuildTypeName(std::ostream& output) const final {     if (params_.empty()) {       output << "any";     } else {+      std::set<std::string> types;+      for (const S<const TypeInstance>& type : params_) {+        std::ostringstream text;+        type->BuildTypeName(text);+        types.insert(text.str());+      }       output << "[";       bool first = true;-      for (const auto param : params_) {+      for (const std::string& type : types) {         if (!first) output << "&";         first = false;-        param->BuildTypeName(output);+        output << type;       }       output << "]";     }@@ -48,27 +60,35 @@   MergeType InstanceMergeType() const final   { return MergeType::INTERSECT; } -  std::vector<S<const TypeInstance>> MergedTypes() const final+  const L<S<const TypeInstance>>& MergedTypes() const final   { return params_; }    const L<S<const TypeInstance>> params_; };  struct Type_Union : public TypeInstance {-  Type_Union(L<S<const TypeInstance>> params) : params_(params.begin(), params.end()) {}+  inline Type_Union(L<S<const TypeInstance>> params) : params_(std::move(params)) {} +  inline ~Type_Union() { Remove_Union(params_); }+   std::string CategoryName() const final { return "(union)"; }    void BuildTypeName(std::ostream& output) const final {     if (params_.empty()) {       output << "all";     } else {+      std::set<std::string> types;+      for (const S<const TypeInstance>& type : params_) {+        std::ostringstream text;+        type->BuildTypeName(text);+        types.insert(text.str());+      }       output << "[";       bool first = true;-      for (const auto param : params_) {+      for (const std::string& type : types) {         if (!first) output << "|";         first = false;-        param->BuildTypeName(output);+        output << type;       }       output << "]";     }@@ -77,7 +97,7 @@   MergeType InstanceMergeType() const final   { return MergeType::UNION; } -  std::vector<S<const TypeInstance>> MergedTypes() const final+  const L<S<const TypeInstance>>& MergedTypes() const final   { return params_; }    const L<S<const TypeInstance>> params_;@@ -86,37 +106,75 @@ L<const TypeInstance*> ParamsToKey(const L<S<const TypeInstance>>& params) {   L<const TypeInstance*> key;   for (const auto& param : params) {-    key.push_back(param.get());+    key.insert(param.get());   }-  std::sort(key.begin(), key.end());   return key; } +template <class T>+class MetaCache {+ public:+  S<const TypeInstance> GetOrCreate(const L<S<const TypeInstance>>& params) {+    if (params.size() == 1) {+      return *params.begin();+    }+    auto key = ParamsToKey(params);+    while (lock_.test_and_set(std::memory_order_acquire));+    auto& cached = cache_[std::move(key)];+    S<T> type = cached.lock();+    if (!type) {+      cached = type = S_get(new T(params));+    }+    lock_.clear(std::memory_order_release);+    return type;+  }++  void Remove(const L<S<const TypeInstance>>& params) {+    auto key = ParamsToKey(params);+    while (lock_.test_and_set(std::memory_order_acquire));+    auto pos = cache_.find(key);+    // Skip erasing if it's a valid pointer, since that could mean that another+    // thread created a new instance while the one we're trying to remove was in+    // the process of being destructed.+    if (pos != cache_.end() && !pos->second.lock()) {+      cache_.erase(pos);+    }+    lock_.clear(std::memory_order_release);+  }++ private:+  std::atomic_flag lock_ = ATOMIC_FLAG_INIT;+  std::map<L<const TypeInstance*>, W<T>> cache_;+};++static auto& intersect_cache = *new MetaCache<Type_Intersect>;+static auto& union_cache     = *new MetaCache<Type_Union>;++void Remove_Intersect(const L<S<const TypeInstance>>& params) {+  intersect_cache.Remove(params);+}++void Remove_Union(const L<S<const TypeInstance>>& params) {+  union_cache.Remove(params);+}+ }  // namespace  -S<TypeInstance> Merge_Intersect(L<S<const TypeInstance>> params) {-  static auto& cache = *new std::map<L<const TypeInstance*>,S<Type_Intersect>>();-  auto& cached = cache[ParamsToKey(params)];-  S<Type_Intersect> type = cached;-  if (!type) { cached = type = S_get(new Type_Intersect(params)); }-  return type;+S<const TypeInstance> Merge_Intersect(const L<S<const TypeInstance>>& params) {+  return intersect_cache.GetOrCreate(params); } -S<TypeInstance> Merge_Union(L<S<const TypeInstance>> params) {-  static auto& cache = *new std::map<L<const TypeInstance*>,S<Type_Union>>();-  auto& cached = cache[ParamsToKey(params)];-  S<Type_Union> type = cached;-  if (!type) { cached = type = S_get(new Type_Union(params)); }-  return type;+S<const TypeInstance> Merge_Union(const L<S<const TypeInstance>>& params) {+  return union_cache.GetOrCreate(params); } -const S<TypeInstance>& GetMerged_Any() {+const S<const TypeInstance>& GetMerged_Any() {   static const auto instance = Merge_Intersect(L_get<S<const TypeInstance>>());   return instance; } -const S<TypeInstance>& GetMerged_All() {+const S<const TypeInstance>& GetMerged_All() {   static const auto instance = Merge_Union(L_get<S<const TypeInstance>>());   return instance; }@@ -130,8 +188,8 @@   __builtin_unreachable(); } -ReturnTuple TypeInstance::Dispatch(const S<TypeInstance>& self, const TypeFunction& label,-                                   const ParamTuple& params, const ValueTuple& args) {+ReturnTuple TypeInstance::Dispatch(const TypeFunction& label,+                                   const ParamTuple& params, const ValueTuple& args) const {   FAIL() << CategoryName() << " does not implement " << label;   __builtin_unreachable(); }@@ -142,6 +200,7 @@   __builtin_unreachable(); } +// static bool TypeInstance::CanConvert(const S<const TypeInstance>& x,                               const S<const TypeInstance>& y) {   // See pairMergeTree for the ordering here.@@ -213,24 +272,4 @@ PrimCharBuffer& TypeValue::AsCharBuffer() {   FAIL() << CategoryName() << " is not a CharBuffer value";   __builtin_unreachable();-}--AnonymousOrder::AnonymousOrder(const BoxedValue cont,-                               const ValueFunction& func_next,-                               const ValueFunction& func_get)-  : container(cont), function_next(func_next), function_get(func_get) {}--std::string AnonymousOrder::CategoryName() const { return "AnonymousOrder"; }--ReturnTuple AnonymousOrder::Dispatch(-  const ValueFunction& label, const ParamTuple& params, const ValueTuple& args) {-  if (&label == &function_next) {-    TRACE_FUNCTION("AnonymousOrder.next")-    return ReturnTuple(Call_next(Var_self()));-  }-  if (&label == &function_get) {-    TRACE_FUNCTION("AnonymousOrder.get")-    return ReturnTuple(Call_get(Var_self()));-  }-  return TypeValue::Dispatch(label, params, args); }
base/src/logging.cpp view
@@ -150,6 +150,7 @@  void CleanupContext::SetLocal(const char* at) {   at_ = at;+  LogCalls::MaybeLogCall(GetName(), at_); }  void CleanupContext::AppendTrace(TraceList& trace) const {@@ -190,29 +191,18 @@   return argv_; } -std::string FixCsvString(const char* string) {-  std::string fixed;-  while (*string) {-    switch (*string) {-      case '\\':-        break;-      case '"':-        fixed.push_back('\'');-        break;-      default:-        fixed.push_back(*string);-        break;-    }-    ++string;-  }-  return fixed;-}- unsigned int UniqueId() {   const auto time = std::chrono::steady_clock::now().time_since_epoch();   return (1000000009 * std::chrono::duration_cast<std::chrono::microseconds>(time).count()); } +static bool enable_coverage = true;++// static+void LogCalls::DisableCallLogging() {+  enable_coverage = false;+}+ LogCallsToFile::LogCallsToFile(std::string filename)   : unique_id_(UniqueId()),     filename_(std::move(filename)),@@ -231,12 +221,12 @@ }  void LogCallsToFile::LogCall(const char* name, const char* at) {-  if (log_file_) {+  if (log_file_ && enable_coverage) {     std::lock_guard<std::mutex> lock(mutex_);     const auto time = std::chrono::steady_clock::now().time_since_epoch();     *log_file_ << std::chrono::duration_cast<std::chrono::microseconds>(time).count() << ","                << unique_id_ << ","-               << "\"" << FixCsvString(name) << "\"" << ","-               << "\"" << FixCsvString(at) << "\"" << std::endl;+               << "\"" << (name ? name : "") << "\"" << ","+               << "\"" << at << "\"" << std::endl;   } }
base/src/types.cpp view
@@ -25,24 +25,24 @@   int ArgTuple::Size() const {-  return size_;+  return data_.Size(); }  const BoxedValue& ArgTuple::At(int pos) const {-  if (pos < 0 || pos >= size_) {+  if (pos < 0 || pos >= data_.Size()) {     FAIL() << "Bad ArgTuple index";   }   return *data_[pos]; }  const BoxedValue& ArgTuple::Only() const {-  if (size_ != 1) {+  if (data_.Size() != 1) {     FAIL() << "Bad ArgTuple index";   }   return *data_[0]; } -ReturnTuple& ReturnTuple::operator = (ReturnTuple&& other) {+void ReturnTuple::TransposeFrom(ReturnTuple&& other) {   if (Size() != other.Size()) {     FAIL() << "ReturnTuple size mismatch in assignment: " << Size()            << " (expected) " << other.Size() << " (actual)";@@ -50,40 +50,39 @@   for (int i = 0; i < Size(); ++i) {     At(i) = std::move(other.At(i));   }-  return *this; }  int ReturnTuple::Size() const {-  return size_;+  return data_.Size(); }  BoxedValue& ReturnTuple::At(int pos) {-  if (pos < 0 || pos >= size_) {+  if (pos < 0 || pos >= data_.Size()) {     FAIL() << "Bad ReturnTuple index";   }   return data_[pos]; }  const BoxedValue& ReturnTuple::At(int pos) const {-  if (pos < 0 || pos >= size_) {+  if (pos < 0 || pos >= data_.Size()) {     FAIL() << "Bad ReturnTuple index";   }   return data_[pos]; }  const BoxedValue& ReturnTuple::Only() const {-  if (size_ != 1) {+  if (data_.Size() != 1) {     FAIL() << "Bad ReturnTuple index";   }   return data_[0]; }  int ParamTuple::Size() const {-  return size_;+  return data_.Size(); } -const S<TypeInstance>& ParamTuple::At(int pos) const {-  if (pos < 0 || pos >= size_) {+const S<const TypeInstance>& ParamTuple::At(int pos) const {+  if (pos < 0 || pos >= data_.Size()) {     FAIL() << "Bad ParamTuple index";   }   return data_[pos];@@ -105,7 +104,6 @@     pool = storage->next;     flag.clear(std::memory_order_release);     storage->next = nullptr;-    new (storage->data()) typename P::Managed[storage->size];     return storage;   } }@@ -138,24 +136,25 @@ std::atomic_flag PoolManager<BoxedValue>::pool4_flag_ = ATOMIC_FLAG_INIT;  // static-typename PoolManager<BoxedValue>::PoolEntry* PoolManager<BoxedValue>::Take(int size) {+typename PoolManager<BoxedValue>::PoolEntry* PoolManager<BoxedValue>::Take(int orig_size) {+  int size = orig_size;   if (size == 0) return nullptr;   if (size < 4) {     size = 4;   }   PoolEntry* storage = nullptr;   if (size == 4 && (storage = PoolTakeCommon(pool4_flag_, pool4_, pool4_size_))) {-    return storage;+  } else {+    storage = new (new unsigned char[sizeof(PoolEntry) + size*sizeof(Managed)]) PoolEntry(size, nullptr);   }-  storage = new (new unsigned char[sizeof(PoolEntry) + size*sizeof(Managed)]) PoolEntry(size, nullptr);-  new (storage->data()) Managed[size];+  new (storage->data()) Managed[orig_size];   return storage; }  // static-void PoolManager<BoxedValue>::Return(PoolEntry* storage) {+void PoolManager<BoxedValue>::Return(PoolEntry* storage, int orig_size) {   if (!storage) return;-  for (int i = 0; i < storage->size; ++i) {+  for (int i = 0; i < orig_size; ++i) {     storage->data()[i].~Managed();   }   if (storage->size == 4 && PoolReturnCommon(storage, pool4_flag_, pool4_, pool4_size_, pool_limit_)) {@@ -171,26 +170,25 @@ std::atomic_flag PoolManager<const BoxedValue*>::pool4_flag_ = ATOMIC_FLAG_INIT;  // static-typename PoolManager<const BoxedValue*>::PoolEntry* PoolManager<const BoxedValue*>::Take(int size) {+typename PoolManager<const BoxedValue*>::PoolEntry* PoolManager<const BoxedValue*>::Take(int orig_size) {+  int size = orig_size;   if (size == 0) return nullptr;   if (size < 4) {     size = 4;   }   PoolEntry* storage = nullptr;   if (size == 4 && (storage = PoolTakeCommon(pool4_flag_, pool4_, pool4_size_))) {-    return storage;+  } else {+    storage = new (new unsigned char[sizeof(PoolEntry) + size*sizeof(Managed)]) PoolEntry(size, nullptr);   }-  storage = new (new unsigned char[sizeof(PoolEntry) + size*sizeof(Managed)]) PoolEntry(size, nullptr);-  new (storage->data()) Managed[size];+  // NOTE: Skipping constructor calls since pointers are trivial.   return storage; }  // static-void PoolManager<const BoxedValue*>::Return(PoolEntry* storage) {+void PoolManager<const BoxedValue*>::Return(PoolEntry* storage, int orig_size) {   if (!storage) return;-  for (int i = 0; i < storage->size; ++i) {-    storage->data()[i].~Managed();-  }+  // NOTE: Skipping destructor calls since pointers are trivial.   if (storage->size == 4 && PoolReturnCommon(storage, pool4_flag_, pool4_, pool4_size_, pool_limit_)) {     return;   }@@ -199,29 +197,30 @@ }  -unsigned int PoolManager<S<TypeInstance>>::pool4_size_ = 0;-typename PoolManager<S<TypeInstance>>::PoolEntry* PoolManager<S<TypeInstance>>::pool4_{nullptr};-std::atomic_flag PoolManager<S<TypeInstance>>::pool4_flag_ = ATOMIC_FLAG_INIT;+unsigned int PoolManager<S<const TypeInstance>>::pool4_size_ = 0;+typename PoolManager<S<const TypeInstance>>::PoolEntry* PoolManager<S<const TypeInstance>>::pool4_{nullptr};+std::atomic_flag PoolManager<S<const TypeInstance>>::pool4_flag_ = ATOMIC_FLAG_INIT;  // static-typename PoolManager<S<TypeInstance>>::PoolEntry* PoolManager<S<TypeInstance>>::Take(int size) {+typename PoolManager<S<const TypeInstance>>::PoolEntry* PoolManager<S<const TypeInstance>>::Take(int orig_size) {+  int size = orig_size;   if (size == 0) return nullptr;   if (size < 4) {     size = 4;   }   PoolEntry* storage = nullptr;   if (size == 4 && (storage = PoolTakeCommon(pool4_flag_, pool4_, pool4_size_))) {-    return storage;+  } else {+    storage = new (new unsigned char[sizeof(PoolEntry) + size*sizeof(Managed)]) PoolEntry(size, nullptr);   }-  storage = new (new unsigned char[sizeof(PoolEntry) + size*sizeof(Managed)]) PoolEntry(size, nullptr);-  new (storage->data()) Managed[size];+  new (storage->data()) Managed[orig_size];   return storage; }  // static-void PoolManager<S<TypeInstance>>::Return(PoolEntry* storage) {+void PoolManager<S<const TypeInstance>>::Return(PoolEntry* storage, int orig_size) {   if (!storage) return;-  for (int i = 0; i < storage->size; ++i) {+  for (int i = 0; i < orig_size; ++i) {     storage->data()[i].~Managed();   }   if (storage->size == 4 && PoolReturnCommon(storage, pool4_flag_, pool4_, pool4_size_, pool_limit_)) {
base/types.hpp view
@@ -23,8 +23,8 @@ #include <cstdint> #include <functional> #include <memory>+#include <set> #include <tuple>-#include <vector>  #include "logging.hpp" #include "pooled.hpp"@@ -36,8 +36,9 @@   type& operator =(const type&) = delete; \   type& operator =(type&&) = delete; -using CollectionType = int;+using CategoryId = std::int64_t; +using PrimBool = bool; using PrimInt = std::int64_t; using PrimString = std::string; using PrimChar = char;@@ -61,6 +62,9 @@ template<class T> inline S<T> S_get(T* val) { return S<T>(val); } +template<class T>+using W = std::weak_ptr<T>;+ template<class...Ts> using T = std::tuple<Ts...>; @@ -68,7 +72,7 @@ T<Ts...> T_get(Ts... ts) { return std::make_tuple(ts...); }  template<class T>-using L = std::vector<T>;+using L = std::set<T>;  template<class T, class...Ts> inline L<T> L_get(Ts... ts) { return L<T>{ts...}; }@@ -128,7 +132,7 @@  template<int N, class...Ts> struct Params {-  using Type = typename Params<N-1, const S<TypeInstance>&, Ts...>::Type;+  using Type = typename Params<N-1, const S<const TypeInstance>&, Ts...>::Type; };  template<class...Ts>@@ -190,7 +194,7 @@   template<class> friend class PoolArray;    static PoolEntry* Take(int size);-  static void Return(PoolEntry* storage);+  static void Return(PoolEntry* storage, int size);    static constexpr unsigned int pool_limit_ = 256;   static unsigned int pool4_size_;@@ -206,7 +210,7 @@   template<class> friend class PoolArray;    static PoolEntry* Take(int size);-  static void Return(PoolEntry* storage);+  static void Return(PoolEntry* storage, int size);    static constexpr unsigned int pool_limit_ = 256;   static unsigned int pool4_size_;@@ -215,14 +219,14 @@ };  template<>-class PoolManager<S<TypeInstance>> {-  using PoolEntry = PoolStorage<S<TypeInstance>>;+class PoolManager<S<const TypeInstance>> {+  using PoolEntry = PoolStorage<S<const TypeInstance>>;   using Managed = PoolEntry::Managed;    template<class> friend class PoolArray;    static PoolEntry* Take(int size);-  static void Return(PoolEntry* storage);+  static void Return(PoolEntry* storage, int size);    static constexpr unsigned int pool_limit_ = 256;   static unsigned int pool4_size_;@@ -235,18 +239,19 @@  class ReturnTuple : public ValueTuple {  public:-  constexpr ReturnTuple() : size_(0), data_() {}+  constexpr ReturnTuple() : data_() {} -  ReturnTuple(int size) : size_(size), data_(size_) {}+  ReturnTuple(int size) : data_(size) {}    template<class...Ts>-  explicit ReturnTuple(Ts... returns) : size_(sizeof...(Ts)), data_(size_) {+  explicit ReturnTuple(Ts... returns) : data_(sizeof...(Ts)) {     data_.Init(std::move(returns)...);   }    ReturnTuple(ReturnTuple&&) = default;-  ReturnTuple& operator = (ReturnTuple&&); +  void TransposeFrom(ReturnTuple&& other);+   int Size() const final;   BoxedValue& At(int pos);   const BoxedValue& At(int pos) const final;@@ -254,19 +259,19 @@   private:   ReturnTuple(const ReturnTuple&) = delete;+  ReturnTuple& operator =(ReturnTuple&&) = delete;   ReturnTuple& operator =(const ReturnTuple&) = delete;   void* operator new(std::size_t size) = delete; -  int size_;   zeolite_internal::PoolArray<BoxedValue> data_; };  class ArgTuple : public ValueTuple {  public:-  constexpr ArgTuple() : size_(0), data_() {}+  constexpr ArgTuple() : data_() {}    template<class...Ts>-  explicit ArgTuple(const Ts&... args) : size_(sizeof...(Ts)), data_(size_) {+  explicit ArgTuple(const Ts&... args) : data_(sizeof...(Ts)) {     data_.Init(&args...);   } @@ -281,23 +286,22 @@   ArgTuple& operator = (ArgTuple&&) =  delete;   void* operator new(std::size_t size) = delete; -  int size_;   zeolite_internal::PoolArray<const BoxedValue*> data_; };  class ParamTuple {  public:-  constexpr ParamTuple() : size_(0), data_() {}+  constexpr ParamTuple() : data_() {}    template<class...Ts>-  explicit ParamTuple(const Ts&... params) : size_(sizeof...(Ts)), data_(size_) {+  explicit ParamTuple(const Ts&... params) : data_(sizeof...(Ts)) {     data_.Init(std::move(params)...);   }    ParamTuple(ParamTuple&& other) = default;    int Size() const;-  const S<TypeInstance>& At(int pos) const;+  const S<const TypeInstance>& At(int pos) const;   private:   ParamTuple(const ParamTuple&) = delete;@@ -305,8 +309,7 @@   ParamTuple& operator = (ParamTuple&&) = delete;   void* operator new(std::size_t size) = delete; -  int size_;-  zeolite_internal::PoolArray<S<TypeInstance>> data_;+  zeolite_internal::PoolArray<S<const TypeInstance>> data_; };  #endif  // TYPES_HPP_
bin/zeolite.hs view
@@ -24,6 +24,7 @@ import System.Exit import System.IO import qualified Data.Map as Map+import qualified Data.Set as Set  import Base.CompilerError import Base.TrackedErrors@@ -84,6 +85,40 @@   if null os      then exitSuccess      else exitFailure+tryFastModes ("--show-traces":ps) = do+  tryZeoliteIO $ do+    (_,backend) <- loadConfig+    let h = getCompilerHash backend+    mapM_ (showTraces h) ps+  exitSuccess where+    showTraces h p = do+      p' <- errorFromIO $ canonicalizePath p+      -- Not needed to show traces, but causes the error to be about the module+      -- rather than the filename if the module hasn't been compiled.+      _ <- loadModuleMetadata h ForceAll Map.empty p'+      ts <- readPossibleTraces p+      mapM_ (errorFromIO . hPutStrLn stdout) $ Set.toList ts+tryFastModes ("--missed-lines":l:ps) = do+  tryZeoliteIO $ do+    (_,backend) <- loadConfig+    let h = getCompilerHash backend+    expected <- fmap Set.unions $ mapM (getTraces h) ps+    actual <- loadTraces+    mapM_ (errorFromIO . hPutStrLn stdout) $ Set.toList $ expected `Set.difference` actual+  exitSuccess where+    loadTraces = do+      errorFromIO $ hPutStrLn stderr $ "Loading trace data from \"" ++ l ++ "\"."+      lc <- errorFromIO $ readFile l+      fmap (Set.fromList . map teContext) $ parseTracesFile (l,lc)+    getTraces h p = do+      p' <- errorFromIO $ canonicalizePath p+      -- Not needed to show traces, but causes the error to be about the module+      -- rather than the filename if the module hasn't been compiled.+      _ <- loadModuleMetadata h ForceAll Map.empty p'+      readPossibleTraces p+tryFastModes ("--missed-lines":_) = do+  hPutStrLn stderr $ "Pass a .csv generated by -t --log-traces."+  exitFailure tryFastModes ("--show-deps":ps) = do   tryZeoliteIO $ do     (_,backend) <- loadConfig
example/parser/parse-text.0rp view
@@ -15,7 +15,10 @@  // Parser combinators. concrete Parse {-  @type const<#x> (#x)                     -> (Parser<#x>)+  @type const<#x>+    #x immutable+  (#x) -> (Parser<#x>)+   @type error     (Formatted)              -> (Parser<all>)   @type try<#x>   (Parser<#x>)             -> (Parser<#x>)   @type or<#x>    (Parser<#x>,Parser<#x>)  -> (Parser<#x>)
example/parser/parse-text.0rx view
@@ -44,9 +44,9 @@                       min.formatted() + "," + max.formatted() + "} at " +                       contextOld.getPosition()     ParseContext<any> context <- contextOld-    [Append<String>&Build<String>] builder <- String.builder()+    [Append<Formatted>&Build<String>] builder <- String.builder() -    optional Order<Int> counter <- empty+    optional Order<Int> counter <- defer     if (max == 0) {       counter <- Counter.unlimited()     } else {@@ -63,7 +63,7 @@       traverse (Counter.zeroIndexed(matches.size()) -> Int index) {         $ReadOnly[context,index]$         if (context.current() == matches.readAt(index)) {-          \ builder.append(matches.readAt(index).formatted())+          \ builder.append(matches.readAt(index))           found <- true           break         }@@ -111,6 +111,8 @@ }  concrete ConstParser<#x> {+  #x immutable+   @type create (#x) -> (Parser<#x>) } @@ -135,14 +137,14 @@ define ErrorParser {   refines Parser<all> -  @value Formatted message+  @value String message    run (contextOld) {     return contextOld.setValue<?>(ErrorOr:error(message))   }    create (message) {-    return ErrorParser{ message }+    return ErrorParser{ message.formatted() }   } } @@ -200,6 +202,7 @@ }  concrete LeftParser<#x> {+   @type create (Parser<#x>,Parser<any>) -> (Parser<#x>) } @@ -259,12 +262,12 @@ }  define Parse {-  const (value) {-    return ConstParser<#x>.create(value)-  }-   error (message) {     return ErrorParser.create(message)+  }++  const (value) {+    return ConstParser<#x>.create(value)   }    try (parser) {
example/parser/parser.0rp view
@@ -14,6 +14,8 @@ // Since #x is covariant, any Parser can convert to Parser<any>, and Parser<all> // can convert to all other Parser. @value interface Parser<|#x> {+  immutable+   run (ParseContext<any>) -> (ParseState<#x>) } 
example/parser/parser.0rx view
@@ -59,9 +59,9 @@   getPosition () {     return String.builder()         .append("[line: ")-        .append(line.formatted())+        .append(line)         .append(", char: ")-        .append(char.formatted())+        .append(char)         .append("]")         .build()   }
lib/container/.zeolite-module view
@@ -1,5 +1,9 @@ root: "../.." path: "lib/container"+extra_paths: [+  "lib/container/src"+  "lib/container/test"+] public_deps: [   "lib/util" ]
lib/container/auto-tree.0rp view
@@ -36,6 +36,7 @@ concrete AutoBinaryTree<|#n,#k,#v|#r> {   refines Container   refines Duplicate+  #k immutable   #n defines  KVFactory<#k,#v>   #n requires BalancedTreeNode<#n,#k,#v>   #n requires Duplicate
− lib/container/auto-tree.0rx
@@ -1,358 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--define AutoBinaryTree {-  @value Int treeSize-  @value optional #n root--  new () {-    return #self{ 0, empty }-  }--  size () {-    return treeSize-  }--  duplicate () {-    if (present(root)) {-      return #self{ treeSize, require(root).duplicate() }-    } else {-      return #self{ 0, empty }-    }-  }--  getRoot () {-    return root-  }--  get (k) {-    return find(root,k)-  }--  set (k,v) {-    root, Bool added <- insert(root,k,v)-    if (added) {-      treeSize <- treeSize+1-    }-  }--  remove (k) {-    root, Bool removed <- delete(root,k)-    if (removed) {-      treeSize <- treeSize-1-    }-  }---  @type insert (optional #n,#k,#v) -> (optional #n,Bool)-  insert (node,k,v) (newRoot,added) {-    if (!present(node)) {-      return #n.newNode(k,v), true-    }-    #n node2 <- require(node)-    if (k `#k.lessThan` node2.getKey()) {-      newRoot, added <- insert(node2.getLower(),k,v)-      \ node2.setLower(newRoot)-      newRoot <- rebalance(node2)-    } elif (node2.getKey() `#k.lessThan` k) {-      newRoot, added <- insert(node2.getHigher(),k,v)-      \ node2.setHigher(newRoot)-      newRoot <- rebalance(node2)-    } else {-      \ node2.setValue(v)-      \ node2.updateNode()-      return node2, false-    }-  }--  @type delete (optional #n,#k) -> (optional #n,Bool)-  delete (node,k) (newRoot,removed) {-    if (!present(node)) {-      return empty, false-    }-    #n node2 <- require(node)-    if (k `#k.lessThan` node2.getKey()) {-      newRoot, removed <- delete(node2.getLower(),k)-      \ node2.setLower(newRoot)-      newRoot <- rebalance(node2)-    } elif (node2.getKey() `#k.lessThan` k) {-      newRoot, removed <- delete(node2.getHigher(),k)-      \ node2.setHigher(newRoot)-      newRoot <- rebalance(node2)-    } else {-      return rebalance(removeNode(node2)), true-    }-  }--  @type find (optional #n,#k) -> (optional #v)-  find (node,k) {-    if (present(node)) {-      scoped {-        #n node2 <- require(node)-      } in if (k `#k.lessThan` node2.getKey()) {-        return find(node2.getLower(),k)-      } elif (node2.getKey() `#k.lessThan` k) {-        return find(node2.getHigher(),k)-      } else {-        return node2.getValue()-      }-    } else {-      return empty-    }-  }--  @type getBalance (optional #n) -> (Int)-  getBalance (node) (balance) {-    balance <- 0-    if (present(node)) {-      #n node2 <- require(node)-      if (present(node2.getLower())) {-        balance <- balance-require(node2.getLower()).getHeight()-      }-      if (present(node2.getHigher())) {-        balance <- balance+require(node2.getHigher()).getHeight()-      }-    }-  }--  @type rebalance (optional #n) -> (optional #n)-  rebalance (node) {-    if (!present(node)) {-      return empty-    }-    #n node2 <- require(node)-    \ node2.updateNode()-    scoped {-      Int balance <- getBalance(node2)-    } in if (balance > 1) {-      return pivotLower(node2)-    } elif (balance < -1) {-      return pivotHigher(node2)-    } else {-      return node2-    }-  }--  @type pivotHigher (#n) -> (#n)-  pivotHigher (node) (newNode) {-    if (getBalance(node.getLower()) > 0) {-      \ node.setLower(pivotLower(require(node.getLower())))-    }-    newNode <- require(node.getLower())-    \ node.setLower(newNode.getHigher())-    \ node.updateNode()-    \ newNode.setHigher(node)-    \ newNode.updateNode()-  }--  @type pivotLower (#n) -> (#n)-  pivotLower (node) (newNode) {-    if (getBalance(node.getHigher()) < 0) {-      \ node.setHigher(pivotHigher(require(node.getHigher())))-    }-    newNode <- require(node.getHigher())-    \ node.setHigher(newNode.getLower())-    \ node.updateNode()-    \ newNode.setLower(node)-    \ newNode.updateNode()-  }--  @type removeNode (#n) -> (optional #n)-  removeNode (node) (newNode) {-    if (getBalance(node) < 0) {-      optional #n temp, newNode <- removeHighest(node.getLower())-      \ node.setLower(temp)-    } else {-      optional #n temp, newNode <- removeLowest(node.getHigher())-      \ node.setHigher(temp)-    }-    if (present(newNode)) {-      \ swapChildren(node,require(newNode))-      \ require(newNode).updateNode()-    }-  }--  @type removeHighest (optional #n) -> (optional #n,optional #n)-  removeHighest (node) (newNode,removed) {-    if (!present(node)) {-      return empty, empty-    }-    #n node2 <- require(node)-    if (present(node2.getHigher())) {-      optional #n temp, removed <- removeHighest(node2.getHigher())-      \ node2.setHigher(temp)-      newNode <- rebalance(node2)-    } else {-      newNode <- node2.getLower()-      \ node2.setLower(empty)-      removed <- node-    }-  }--  @type removeLowest (optional #n) -> (optional #n,optional #n)-  removeLowest (node) (newNode,removed) {-    if (!present(node)) {-      return empty, empty-    }-    #n node2 <- require(node)-    if (present(node2.getLower())) {-      optional #n temp, removed <- removeLowest(node2.getLower())-      \ node2.setLower(temp)-      newNode <- rebalance(node2)-    } else {-      newNode <- node2.getHigher()-      \ node2.setHigher(empty)-      removed <- node-    }-  }--  @type swapChildren (#n,#n) -> ()-  swapChildren (l,r) {-    scoped {-      optional #n temp <- l.getLower()-      \ l.setLower(r.getLower())-    } in \ r.setLower(temp)-    scoped {-      optional #n temp <- l.getHigher()-      \ l.setHigher(r.getHigher())-    } in \ r.setHigher(temp)-  }-}--concrete TreeKeyValue<|#k,#v> {-  refines KeyValue<#k,#v>--  @category create<#k,#v> (#k,#v) -> (TreeKeyValue<#k,#v>)-}--define TreeKeyValue {-  $ReadOnly[key,value]$--  @value #k key-  @value #v value--  create (key,value) {-    return TreeKeyValue<#k,#v>{ key, value }-  }--  getKey () {-    return key-  }--  getValue () {-    return value-  }-}--define ForwardTreeOrder {-  $ReadOnly[node,prev]$--  @value BinaryTreeNode<#k,#v> node-  @value optional ForwardTreeOrder<#k,#v> prev--  create (node) (current) {-    optional BinaryTreeNode<#k,#v> node2 <- node-    current <- empty-    while (present(node2)) {-      current <- ForwardTreeOrder<#k,#v>{ require(node2), current }-      node2 <- require(node2).getLower()-    }-  }--  seek (key,node) (current) {-    optional BinaryTreeNode<#k,#v> node2 <- node-    current <- empty-    while (present(node2)) {-      if (require(node2).getKey() `#k.lessThan` key) {-        // Skip node2 in the traversal, since it's before key.-        node2 <- require(node2).getHigher()-      } elif (key `#k.lessThan` require(node2).getKey()) {-        current <- ForwardTreeOrder<#k,#v>{ require(node2), current }-        node2 <- require(node2).getLower()-      } else {-        current <- ForwardTreeOrder<#k,#v>{ require(node2), current }-        break-      }-    }-  }--  next () (current) {-    // Algorithm:-    // 1. Pop self from the stack.-    // 2. Traverse lower to the bottom starting from the higher child of self.-    optional BinaryTreeNode<#k,#v> node2 <- node.getHigher()-    current <- prev-    while (present(node2)) {-      current <- ForwardTreeOrder<#k,#v>{ require(node2), current }-      node2 <- require(node2).getLower()-    }-  }--  get () {-    return TreeKeyValue:create<?,?>(node.getKey(),node.getValue())-  }-}--define ReverseTreeOrder {-  $ReadOnly[node,prev]$--  @value BinaryTreeNode<#k,#v> node-  @value optional ReverseTreeOrder<#k,#v> prev--  create (node) (current) {-    optional BinaryTreeNode<#k,#v> node2 <- node-    current <- empty-    while (present(node2)) {-      current <- ReverseTreeOrder<#k,#v>{ require(node2), current }-      node2 <- require(node2).getHigher()-    }-  }--  seek (key,node) (current) {-    optional BinaryTreeNode<#k,#v> node2 <- node-    current <- empty-    while (present(node2)) {-      if (require(node2).getKey() `#k.lessThan` key) {-        current <- ReverseTreeOrder<#k,#v>{ require(node2), current }-        node2 <- require(node2).getHigher()-      } elif (key `#k.lessThan` require(node2).getKey()) {-        // Skip node2 in the traversal, since it's after key.-        node2 <- require(node2).getLower()-      } else {-        current <- ReverseTreeOrder<#k,#v>{ require(node2), current }-        break-      }-    }-  }--  next () (current) {-    // Algorithm:-    // 1. Pop self from the stack.-    // 2. Traverse higher to the bottom starting from the lower child of self.-    optional BinaryTreeNode<#k,#v> node2 <- node.getLower()-    current <- prev-    while (present(node2)) {-      current <- ReverseTreeOrder<#k,#v>{ require(node2), current }-      node2 <- require(node2).getHigher()-    }-  }--  get () {-    return TreeKeyValue:create<?,?>(node.getKey(),node.getValue())-  }-}
− lib/container/helpers.0rt
@@ -1,107 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "comparator helper tests" {-  success-}--unittest keyValueHLessThan {-  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:lessThan<?,?>` KV.new(1,"a"),false)-  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:lessThan<?,?>` KV.new(1,"b"),true)-  \ Testing.checkEquals<?>(KV.new(2,"a") `KeyValueH:lessThan<?,?>` KV.new(1,"b"),false)-  \ Testing.checkEquals<?>(KV.new(1,"b") `KeyValueH:lessThan<?,?>` KV.new(2,"a"),true)-}--unittest keyValueHLessThanWith {-  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:lessThanWith<?,?,Int,AlwaysEqual>` KV.new(1,"a"),false)-  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:lessThanWith<?,?,Int,AlwaysEqual>` KV.new(1,"b"),false)-  \ Testing.checkEquals<?>(KV.new(2,"a") `KeyValueH:lessThanWith<?,?,Int,AlwaysEqual>` KV.new(1,"b"),false)-  \ Testing.checkEquals<?>(KV.new(1,"b") `KeyValueH:lessThanWith<?,?,Int,AlwaysEqual>` KV.new(2,"a"),true)--  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:lessThanWith<?,?,AlwaysEqual,String>` KV.new(1,"a"),false)-  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:lessThanWith<?,?,AlwaysEqual,String>` KV.new(1,"b"),true)-  \ Testing.checkEquals<?>(KV.new(2,"a") `KeyValueH:lessThanWith<?,?,AlwaysEqual,String>` KV.new(1,"b"),true)-  \ Testing.checkEquals<?>(KV.new(1,"b") `KeyValueH:lessThanWith<?,?,AlwaysEqual,String>` KV.new(2,"a"),false)-}--unittest keyValueHEquals {-  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:equals<?,?>` KV.new(1,"a"),true)-  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:equals<?,?>` KV.new(1,"b"),false)-  \ Testing.checkEquals<?>(KV.new(2,"a") `KeyValueH:equals<?,?>` KV.new(1,"b"),false)-  \ Testing.checkEquals<?>(KV.new(1,"b") `KeyValueH:equals<?,?>` KV.new(2,"a"),false)-}--unittest keyValueHEqualsWith {-  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:equalsWith<?,?,Int,AlwaysEqual>` KV.new(1,"a"),true)-  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:equalsWith<?,?,Int,AlwaysEqual>` KV.new(1,"b"),true)-  \ Testing.checkEquals<?>(KV.new(2,"a") `KeyValueH:equalsWith<?,?,Int,AlwaysEqual>` KV.new(1,"b"),false)-  \ Testing.checkEquals<?>(KV.new(1,"b") `KeyValueH:equalsWith<?,?,Int,AlwaysEqual>` KV.new(2,"a"),false)--  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:equalsWith<?,?,AlwaysEqual,String>` KV.new(1,"a"),true)-  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:equalsWith<?,?,AlwaysEqual,String>` KV.new(1,"b"),false)-  \ Testing.checkEquals<?>(KV.new(2,"a") `KeyValueH:equalsWith<?,?,AlwaysEqual,String>` KV.new(1,"b"),false)-  \ Testing.checkEquals<?>(KV.new(1,"b") `KeyValueH:equalsWith<?,?,AlwaysEqual,String>` KV.new(2,"a"),false)-}--unittest integrationTest {-  SearchTree<Int,String> tree1 <- SearchTree<Int,String>.new()-      .set(1,"d").set(2,"c").set(3,"b").set(4,"a")-  SearchTree<Int,String> tree2 <- SearchTree<Int,String>.new()-      .set(1,"a").set(2,"b").set(3,"c").set(4,"d")--  \ Testing.checkEquals<?>(tree1.defaultOrder() `OrderH:equalsWith<?,KVEquals<Int,AlwaysEqual>>`    tree2.defaultOrder(),true)-  \ Testing.checkEquals<?>(tree1.defaultOrder() `OrderH:equalsWith<?,KVEquals<Int,String>>`         tree2.defaultOrder(),false)-  \ Testing.checkEquals<?>(tree1.defaultOrder() `OrderH:equalsWith<?,KVEquals<AlwaysEqual,String>>` tree2.defaultOrder(),false)-}--concrete KVEquals<#k,#v> {-  defines Equals<KeyValue<Int,String>>-  #k defines Equals<Int>-  #v defines Equals<String>-}--define KVEquals {-  equals (x,y) {-    return x `KeyValueH:equalsWith<?,?,#k,#v>` y-  }-}--concrete KV {-  refines KeyValue<Int,String>--  @type new (Int,String) -> (KV)-}--define KV {-  $ReadOnly[key,value]$--  @value Int key-  @value String value--  new (k,v) {-    return KV{ k, v }-  }--  getKey () {-    return key-  }--  getValue () {-    return value-  }-}
− lib/container/helpers.0rx
@@ -1,45 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--define KeyValueH {-  lessThan (x,y) {-    return lessThanWith<#k,#v,#k,#v>(x,y)-  }--  lessThanWith (x,y) {-    if (x.getKey() `#kk.lessThan` y.getKey()) {-      return true-    } elif (y.getKey() `#kk.lessThan` x.getKey()) {-      return false-    } else {-      return x.getValue() `#vv.lessThan` y.getValue()-    }-  }--  equals (x,y) {-    return equalsWith<#k,#v,#k,#v>(x,y)-  }--  equalsWith (x,y) {-    if (!(x.getKey() `#kk.equals` y.getKey())) {-      return false-    } else {-      return x.getValue() `#vv.equals` y.getValue()-    }-  }-}
+ lib/container/list.0rp view
@@ -0,0 +1,109 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++// Factory for creating new nodes.+@type interface NewNode<#x|> {+  newNode (#x) -> (#self)+}++// List element whose next element can be replaced.+@value interface ListNode<|#n|#x> {+  refines Order<#x>++  // Replace next with the provided element.+  //+  // Returns:+  // - optional #n: The element being replaced as next.+  //+  // Notes:+  // - This is allowed to modify the argument, e.g., set prev if applicable.+  // - This is *not* required to prevent cycles.+  setNext (optional #n) -> (optional #n)+}++// List element that supports reverse iteration.+@value interface DoubleNode<|#n|#x> {+  refines ListNode<#n,#x>++  // Return the previous element.+  prev () -> (optional #self)++  // Replace prev with the provided element.+  //+  // Returns:+  // - optional #n: The element being replaced as prev.+  //+  // Notes:+  // - This is allowed to modify the argument, e.g., set next if applicable.+  // - This is *not* required to prevent cycles.+  setPrev (optional #n) -> (optional #n)+}++// Node in a doubly-linked list.+concrete LinkedNode<#x> {+  defines NewNode<#x>+  // Duplication happens only in the forward direction.+  refines Duplicate+  refines DoubleNode<LinkedNode<#x>,#x>++  // Create a new builder.+  @type builder () -> (ListBuilder<#x,#self>)++  // Set the value held by the node.+  @value set (#x) -> (#self)+}++// Node in a singly-linked list.+//+// Notes:+// - This is more efficient than LinkedNode, but lacks housekeeping when+//   modifying the list's structure.+concrete ForwardNode<#x> {+  defines NewNode<#x>+  refines Duplicate+  refines ListNode<ForwardNode<#x>,#x>++  // Create a new builder.+  @type builder () -> (ListBuilder<#x,#self>)++  // Set the value held by the node.+  @value set (#x) -> (#self)+}++// Builder for ListNode.+concrete ListBuilder<#x|#n> {+  refines Append<#x>+  #n defines NewNode<#x>+  #n requires ListNode<#n,#x>++  // Create a new builder.+  @type new () -> (#self)++  // Build the list.+  //+  // Returns:+  // - optional #n: The head of the built list.+  // - optional #n: The tail of the built list.+  //+  // Notes:+  // - Appending new values after calling build will not invalidate the previous+  //   head/tail, but that tail will no longer be the end.+  // - Calling mutating functions (e.g., setNext, setPrev) on any of the+  //   elements between the returned head/tail will invalidate the builder.+  @value build () -> (optional #n,optional #n)+}
lib/container/search-tree.0rp view
@@ -27,6 +27,7 @@   refines Duplicate   refines KVWriter<#k,#v>   refines KVReader<#k,#v>+  #k immutable   #k defines LessThan<#k>    // Create an empty tree.
− lib/container/search-tree.0rt
@@ -1,202 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "SearchTree tests" {-  success-}--unittest integrationTest {-  ValidatedTree<Int,Int> tree <- ValidatedTree<Int,Int>.new()-  Int count <- 30-  $ReadOnly[count]$--  // Insert values.-  traverse (Counter.zeroIndexed(count) -> Int i) {-    Int new <- ((i + 13) * 3547) % count-    \ tree.set(new,i)-    \ Testing.checkEquals<?>(tree.size(),i+1)-  }--  // Check and remove values.-  traverse (Counter.zeroIndexed(count) -> Int i) {-    Int new <- ((i + 13) * 3547) % count-    scoped {-      optional Int value <- tree.get(new)-    } in if (!present(value)) {-      \ LazyStream<Formatted>.new()-          .append("Not found ")-          .append(new)-          .append(" but should have been ")-          .append(i)-          .writeTo(SimpleOutput.error())-    } elif (require(value) != i) {-      \ LazyStream<Formatted>.new()-          .append("Element ")-          .append(new)-          .append(" should have been ")-          .append(i)-          .append(" but was ")-          .append(require(value))-          .writeTo(SimpleOutput.error())-    }-    \ tree.remove(new)-    \ Testing.checkEquals<?>(tree.size(),count-i-1)-  }-}--unittest defaultOrder {-  SearchTree<Int,Int> tree <- SearchTree<Int,Int>.new()-  Int hash <- 13-  Int max  <- 20-  $ReadOnly[hash,max]$--  // Populate the tree in a pseudo-random order.-  traverse (Counter.zeroIndexed(max) -> Int i) {-    \ ((i*hash)%max) `tree.set` i-  }--  // Validate the traversal order.-  Int index <- 0-  traverse (tree.defaultOrder() -> KeyValue<Int,Int> entry) {-    \ Testing.checkEquals<?>(entry.getKey(),index)-    \ Testing.checkEquals<?>((entry.getValue()*hash)%max,entry.getKey())-    index <- index+1-  }--  \ Testing.checkEquals<?>(index,max)-}--unittest reverseOrder {-  SearchTree<Int,Int> tree <- SearchTree<Int,Int>.new()-  Int hash <- 13-  Int max  <- 20-  $ReadOnly[hash,max]$--  // Populate the tree in a pseudo-random order.-  traverse (Counter.zeroIndexed(max) -> Int i) {-    \ ((i*hash)%max) `tree.set` i-  }--  // Validate the traversal order.-  Int index <- max-  traverse (tree.reverseOrder() -> KeyValue<Int,Int> entry) {-    index <- index-1-    \ Testing.checkEquals<?>(entry.getKey(),index)-    \ Testing.checkEquals<?>((entry.getValue()*hash)%max,entry.getKey())-  }--  \ Testing.checkEquals<?>(index,0)-}--unittest getForward {-  SearchTree<Int,Int> tree <- SearchTree<Int,Int>.new()-  Int hash <- 13-  Int max  <- 20-  $ReadOnly[hash,max]$--  // Populate the tree in a pseudo-random order.-  traverse (Counter.zeroIndexed(max) -> Int i) {-    \ ((i*hash)%max) `tree.set` i-  }--  // Validate the traversal order from every starting point.-  traverse (Counter.zeroIndexed(max) -> Int i) {-    Int index <- i-    traverse (tree.getForward(index) -> KeyValue<Int,Int> entry) {-      \ Testing.checkEquals<?>(entry.getKey(),index)-      \ Testing.checkEquals<?>((entry.getValue()*hash)%max,entry.getKey())-      index <- index+1-    }-    \ Testing.checkEquals<?>(index,max)-  }-}--unittest getForwardNotFound {-  SearchTree<Int,Int> tree <- SearchTree<Int,Int>.new()-  \ tree.set(1,1).set(3,3).set(5,5)--  scoped {-    optional Order<KeyValue<Int,Int>> start <- tree.getForward(4)-  } in if (!present(start)) {-    fail("Failed")-  } else {-    \ Testing.checkEquals<?>(require(start).get().getKey(),5)-  }--  scoped {-    optional Order<KeyValue<Int,Int>> start <- tree.getForward(6)-  } in if (present(start)) {-    fail("Failed")-  }-}--unittest getReverse {-  SearchTree<Int,Int> tree <- SearchTree<Int,Int>.new()-  Int hash <- 13-  Int max  <- 20-  $ReadOnly[hash,max]$--  // Populate the tree in a pseudo-random order.-  traverse (Counter.zeroIndexed(max) -> Int i) {-    \ ((i*hash)%max) `tree.set` i-  }--  // Validate the traversal order from every starting point.-  traverse (Counter.zeroIndexed(max) -> Int i) {-    Int index <- i-    traverse (tree.getReverse(index) -> KeyValue<Int,Int> entry) {-      \ Testing.checkEquals<?>(entry.getKey(),index)-      \ Testing.checkEquals<?>((entry.getValue()*hash)%max,entry.getKey())-      index <- index-1-    }-    \ Testing.checkEquals<?>(index,-1)-  }-}--unittest getReverseNotFound {-  SearchTree<Int,Int> tree <- SearchTree<Int,Int>.new()-  \ tree.set(1,1).set(3,3).set(5,5)--  scoped {-    optional Order<KeyValue<Int,Int>> start <- tree.getReverse(2)-  } in if (!present(start)) {-    fail("Failed")-  } else {-    \ Testing.checkEquals<?>(require(start).get().getKey(),1)-  }--  scoped {-    optional Order<KeyValue<Int,Int>> start <- tree.getReverse(0)-  } in if (present(start)) {-    fail("Failed")-  }-}--unittest duplicate {-  SearchTree<Int,Int> tree <- SearchTree<Int,Int>.new().set(1,1).set(2,2)--  SearchTree<Int,Int> copy <- tree.duplicate()-  \ Testing.checkEquals<?>(copy.size(),2)-  \ Testing.checkOptional<?>(copy.get(1),1)-  \ Testing.checkOptional<?>(copy.get(2),2)--  \ copy.remove(2)-  \ Testing.checkEquals<?>(copy.size(),1)-  \ Testing.checkOptional<?>(copy.get(1),1)-  \ Testing.checkOptional<?>(tree.get(2),2)-}
− lib/container/search-tree.0rx
@@ -1,195 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--define SearchTree {-  @value AutoBinaryTree<SearchTreeNode<#k,#v>,#k,#v,BinaryTreeNode<#k,#v>> tree--  new () {-    return #self{ AutoBinaryTree<SearchTreeNode<#k,#v>,#k,#v,BinaryTreeNode<#k,#v>>.new() }-  }--  size () {-    return tree.size()-  }--  duplicate () {-    return #self{ tree.duplicate() }-  }--  set (k,v) {-    \ tree.set(k,v)-    return self-  }--  remove (k) {-    \ tree.remove(k)-    return self-  }--  get (k) {-    return tree.get(k)-  }--  defaultOrder () {-    return ForwardTreeOrder:create<?,?>(tree.getRoot())-  }--  reverseOrder () {-    return ReverseTreeOrder:create<?,?>(tree.getRoot())-  }--  getForward (k) {-    return ForwardTreeOrder:seek<?,?>(k,tree.getRoot())-  }--  getReverse (k) {-    return ReverseTreeOrder:seek<?,?>(k,tree.getRoot())-  }-}--define ValidatedTree {-  @value AutoBinaryTree<SearchTreeNode<#k,#v>,#k,#v,BinaryTreeNode<#k,#v>> tree--  new () {-    return #self{ AutoBinaryTree<SearchTreeNode<#k,#v>,#k,#v,BinaryTreeNode<#k,#v>>.new() }-  }--  size () {-    return tree.size()-  }--  set (k,v) {-    \ tree.set(k,v)-    \ validate(tree.getRoot())-    return self-  }--  remove (k) {-    \ tree.remove(k)-    \ validate(tree.getRoot())-    return self-  }--  get (k) {-    return tree.get(k)-  }--  @type validate (optional BinaryTreeNode<#k,#v>) -> ()-  validate (node) {-    if (present(node)) {-      \ validateOrder(require(node))-      \ validateBalance(require(node))-    }-  }--  @type validateOrder (BinaryTreeNode<#k,#v>) -> ()-  validateOrder (node) {-    if (present(node.getLower())) {-      if (!(require(node.getLower()).getKey() `#k.lessThan` node.getKey())) {-        fail("bad lower order")-      }-      \ validateOrder(require(node.getLower()))-    }-    if (present(node.getHigher())) {-      if (!(node.getKey() `#k.lessThan` require(node.getHigher()).getKey())) {-        fail("bad higher order")-      }-      \ validateOrder(require(node.getHigher()))-    }-  }--  @type validateBalance (BinaryTreeNode<#k,#v>) -> ()-  validateBalance (node) {-    scoped {-      Int balance <- 0-      if (present(node.getLower())) {-        balance <- balance-require(node.getLower()).getHeight()-      }-      if (present(node.getHigher())) {-        balance <- balance+require(node.getHigher()).getHeight()-      }-      $ReadOnly[balance]$-    } in if (balance > 1 || balance < -1) {-      fail("out of balance: " + balance.formatted())-    }-    if (present(node.getLower())) {-      \ validateBalance(require(node.getLower()))-    }-    if (present(node.getHigher())) {-      \ validateBalance(require(node.getHigher()))-    }-  }-}--concrete SearchTreeNode<#k,#v> {-  defines KVFactory<#k,#v>-  refines BalancedTreeNode<SearchTreeNode<#k,#v>,#k,#v>-  refines Duplicate-}--define SearchTreeNode {-  $ReadOnly[key]$--  @value Int height-  @value #k key-  @value #v value-  @value optional #self lower-  @value optional #self higher--  newNode (k,v) {-    return #self{ 1, k, v, empty, empty }-  }--  duplicate () {-    optional #self lower2 <- empty-    if (present(lower)) {-      lower2 <- require(lower).duplicate()-    }-    optional #self higher2 <- empty-    if (present(higher)) {-      higher2 <- require(higher).duplicate()-    }-    return #self{ height, key, value, lower2, higher2 }-  }--  getLower ()   { return lower }-  setLower (l)  { lower <- l }-  getHigher ()  { return higher }-  setHigher (h) { higher <- h }-  getKey ()     { return key }-  getValue ()   { return value }-  setValue (v)  { value <- v }-  getHeight ()  { return height }--  updateNode () {-    scoped {-      Int l <- 0-      Int h <- 0-      if (present(lower)) {-        l <- require(lower).getHeight()-      }-      if (present(higher)) {-        h <- require(higher).getHeight()-      }-    } in if (l > h) {-      height <- l + 1-    } else {-      height <- h + 1-    }-  }-}
lib/container/sorting.0rp view
@@ -50,4 +50,60 @@    // In-place order reversal of a random-access container.   @category reverse<#x> ([ReadAt<#x>&WriteAt<#x>]) -> ()++  // In-place stable sorting of an iterable list.+  //+  // Params:+  // - #n: Node type representing the list.+  // - #x: Element type managed by the list.+  //+  // Returns:+  // - optional #n: The new head of the list after sorting.+  //+  // Notes:+  // - Worst-case performance is O(n log n).+  // - Worst-case storage is O(1).+  // - If the node is not the actual head, the part of the list being sorted+  //   might become detached from the earlier part of the list.+  @category sortList<#n,#x>+    #x defines LessThan<#x>+    #n requires ListNode<#n,#x>+  (optional #n) -> (optional #n)++  // In-place stable sorting of an iterable list.+  //+  // Params:+  // - #n: Node type representing the list.+  // - #x: Element type managed by the list.+  // - #xx: Comparator type providing the LessThan<#x> comparison.+  //+  // Returns:+  // - optional #n: The new head of the list after sorting.+  //+  // Notes:+  // - Worst-case performance is O(n log n).+  // - Worst-case storage is O(1).+  // - If the node is not the actual head, the part of the list being sorted+  //   might become detached from the earlier part of the list.+  //+  // Example:+  //+  //   // Sort myIntContainer in reverse order. (Reversed is from lib/util.)+  //   optional ListNode<#n,#x> newHead <- Sorting:sortListWith<?,?,Reversed<Int>>(oldHead)+  @category sortListWith<#n,#x,#xx>+    #xx defines LessThan<#x>+    #n requires ListNode<#n,#x>+  (optional #n) -> (optional #n)++  // Reverses the list in place and returns the new head.+  //+  // Returns:+  // - optional #n: The new head of the list after reversing.+  //+  // Notes:+  // - If the node is not the actual head, the part of the list being reversed+  //   might become detached from the earlier part of the list.+  @category reverseList<#n,#x>+    #n requires ListNode<#n,#x>+  (optional #n) -> (optional #n) }
− lib/container/sorting.0rt
@@ -1,154 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "Sorting tests" {-  success-}--unittest sort {-  TestSequence original <- TestSequence.create()-  TestSequence expected <- TestSequence.create()-  Int hash <- 269-  Int size <- 137-  $ReadOnly[hash,size]$--  traverse (Counter.zeroIndexed(size) -> Int i) {-    \ original.append((i*hash)%size/3)-    \ expected.append(i/3)-  }--  \ Sorting:sort<?>(original)-  \ Testing.checkEquals<?>(original,expected)-}--unittest sortWith {-  TestSequence original <- TestSequence.create()-  TestSequence expected <- TestSequence.create()-  Int hash <- 313-  Int size <- 197-  $ReadOnly[hash,size]$--  traverse (Counter.revZeroIndexed(size) -> Int i) {-    \ original.append((i*hash)%size/3)-    \ expected.append(i/3)-  }--  \ Sorting:sortWith<?,Reversed<Int>>(original)-  \ Testing.checkEquals<?>(original,expected)-}--unittest sortEmpty {-  TestSequence original <- TestSequence.create()-  TestSequence expected <- TestSequence.create()--  \ Sorting:sort<?>(original)-  \ Testing.checkEquals<?>(original,expected)-}--unittest reverseEvenSize {-  TestSequence original <- TestSequence.create()-  TestSequence expected <- TestSequence.create()-  Int size <- 100-  $ReadOnly[size]$--  traverse (Counter.zeroIndexed(size) -> Int i) {-    \ expected.append(i)-  }--  traverse (Counter.revZeroIndexed(size) -> Int i) {-    \ original.append(i)-  }--  \ Sorting:reverse<?>(original)-  \ Testing.checkEquals<?>(original,expected)-}--unittest reverseOddSize {-  TestSequence original <- TestSequence.create()-  TestSequence expected <- TestSequence.create()-  Int size <- 101-  $ReadOnly[size]$--  traverse (Counter.zeroIndexed(size) -> Int i) {-    \ expected.append(i)-  }--  traverse (Counter.revZeroIndexed(size) -> Int i) {-    \ original.append(i)-  }--  \ Sorting:reverse<?>(original)-  \ Testing.checkEquals<?>(original,expected)-}--unittest reverseEmpty {-  TestSequence original <- TestSequence.create()-  TestSequence expected <- TestSequence.create()--  \ Sorting:reverse<?>(original)-  \ Testing.checkEquals<?>(original,expected)-}--concrete TestSequence {-  refines Formatted-  refines ReadAt<Int>-  refines WriteAt<Int>-  refines Append<Int>-  defines Equals<TestSequence>--  @type create () -> (TestSequence)-}--define TestSequence {-  @value Vector<Int> seq--  create () {-    return TestSequence{ Vector:create<Int>() }-  }--  formatted () {-    [Append<String>&Build<String>] builder <- String.builder()-    \ builder.append("{")-    traverse (seq.defaultOrder() -> Formatted x) {-      \ builder.append(" ").append(x.formatted())-    }-    return builder.append(" }").build()-  }--  size () {-    return seq.size()-  }--  readAt (i) {-    return seq.readAt(i)-  }--  writeAt (i,x) {-    \ seq.writeAt(i,x)-    return self-  }--  append (x) {-    \ seq.append(x)-    return self-  }--  equals (x,y) {-    return x `ReadAtH:equals<?>` y-  }-}
− lib/container/sorting.0rx
@@ -1,104 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--define Sorting {-  sort (seq) {-    \ sortWith<#x,#x>(seq)-  }--  sortWith (seq) {-    \ HeapSort<#x,#xx>.inPlace(seq)-  }--  reverse (seq) {-    traverse (Counter.zeroIndexed(seq.size()/2) -> Int i) {-      Int j <- seq.size()-i-1-      #x temp <- seq.readAt(i)-      \ seq.writeAt(i,seq.readAt(j))-      \ seq.writeAt(j,temp)-    }-  }-}--// Putting the params at the top level allows helpers to be called without-// needing to pass the params every time.-concrete HeapSort<#x,#xx> {-  #xx defines LessThan<#x>--  @type inPlace ([ReadAt<#x>&WriteAt<#x>]) -> ()-}--define HeapSort {-  $ReadOnly[seq]$--  @value [ReadAt<#x>&WriteAt<#x>] seq--  inPlace (seq) {-    \ #self{ seq }.execute()-  }--  @value execute () -> ()-  execute () { $NoTrace$-    // Convert the container to a heap.-    traverse (Counter.revZeroIndexed(seq.size()/2) -> Int i) {-      \ sift(i,seq.size())-    }--    // Traverse the heap and populate the container in place.-    traverse (Counter.revZeroIndexed(seq.size()) -> Int i) {-      $ReadOnly[i]$-      \ swap(0,i)-      \ sift(0,i)-    }-  }--  @value sift (Int,Int) -> ()-  sift (start,size) { $NoTrace$-    scoped {-      Int last         <- start-      Int indexLargest <- last-      $Hidden[start]$-    } in while (2*last+1 < size) {-      $ReadOnly[last]$-      Int left  <- 2*last+1-      Int right <- 2*last+2-      $ReadOnly[left,right]$-      if (seq.readAt(indexLargest) `#xx.lessThan` seq.readAt(left)) {-        indexLargest <- left-      }-      if (right < size && seq.readAt(indexLargest) `#xx.lessThan` seq.readAt(right)) {-        indexLargest <- right-      }-      if (indexLargest == last) {-        break-      }-    } update {-      \ swap(last,indexLargest)-      last <- indexLargest-    }-  }--  @value swap (Int,Int) -> ()-  swap (i,j) { $NoTrace$-    if (i != j) {-      #x temp <- seq.readAt(i)-      \ seq.writeAt(i,seq.readAt(j))-      \ seq.writeAt(j,temp)-    }-  }-}
lib/container/src/Extension_Vector.cpp view
@@ -38,36 +38,36 @@  using VectorType = std::vector<BoxedValue>; -BoxedValue CreateValue_Vector(S<Type_Vector> parent, VectorType values);+BoxedValue CreateValue_Vector(S<const Type_Vector> parent, VectorType values);  struct ExtCategory_Vector : public Category_Vector {   ReturnTuple Call_copyFrom(const ParamTuple& params, const ValueTuple& args) final {     TRACE_FUNCTION("Vector:copyFrom")-    const S<TypeInstance> Param_y = params.At(0);+    const S<const TypeInstance> Param_y = params.At(0);     const BoxedValue& Var_arg1 = (args.At(0));     VectorType values;     const PrimInt size = TypeValue::Call(Var_arg1, Function_Container_size, ParamTuple(), ArgTuple()).Only().AsInt();     for (int i = 0; i < size; ++i) {       values.push_back(TypeValue::Call(Var_arg1, Function_ReadAt_readAt, ParamTuple(), ArgTuple(Box_Int(i))).Only());     }-    return ReturnTuple(CreateValue_Vector(CreateType_Vector(Params<1>::Type(Param_y)), values));+    return ReturnTuple(CreateValue_Vector(CreateType_Vector(Params<1>::Type(Param_y)), std::move(values)));   }    ReturnTuple Call_create(const ParamTuple& params, const ValueTuple& args) final {     TRACE_FUNCTION("Vector:create")-    const S<TypeInstance> Param_y = params.At(0);+    const S<const TypeInstance> Param_y = params.At(0);     return ReturnTuple(CreateValue_Vector(CreateType_Vector(Params<1>::Type(Param_y)), VectorType()));   }    ReturnTuple Call_createSize(const ParamTuple& params, const ValueTuple& args) final {     TRACE_FUNCTION("Vector:createSize")-    const S<TypeInstance> Param_y = params.At(0);+    const S<const TypeInstance> Param_y = params.At(0);     const PrimInt Var_arg1 = (args.At(0)).AsInt();     VectorType values;     for (int i = 0; i < Var_arg1; ++i) {       values.push_back(TypeInstance::Call(Param_y, Function_Default_default, ParamTuple(), ArgTuple()).Only());     }-    return ReturnTuple(CreateValue_Vector(CreateType_Vector(Params<1>::Type(Param_y)), values));+    return ReturnTuple(CreateValue_Vector(CreateType_Vector(Params<1>::Type(Param_y)), std::move(values)));   } }; @@ -75,35 +75,44 @@   inline ExtType_Vector(Category_Vector& p, Params<1>::Type params) : Type_Vector(p, params) {} }; -struct VectorOrder final : public AnonymousOrder {+class VectorOrder : public TypeValue {+ public:   VectorOrder(BoxedValue container, const VectorType& v)-    : AnonymousOrder(container, Function_Order_next, Function_Order_get), values(v) {}+    : container_(container), values_(v) {} -  BoxedValue Var_self() final {-    return VAR_SELF;-  }+  std::string CategoryName() const final { return "VectorOrder"; } -  BoxedValue Call_next(const BoxedValue& self) final {-    if (index+1 >= values.size()) {-      return Var_empty;-    } else {-      ++index;-      return self;+  ReturnTuple Dispatch(const ValueFunction& label,+                       const ParamTuple& params,+                       const ValueTuple& args) final {+    if (&label == &Function_Order_next) {+      TRACE_FUNCTION("VectorOrder.next")+      if (index_+1 >= values_.size()) {+        return ReturnTuple(Var_empty);+      } else {+        ++index_;+        return ReturnTuple(VAR_SELF);+      }     }-  }--  BoxedValue Call_get(const BoxedValue& self) final {-    if (index >= values.size()) FAIL() << "iterated past end of Vector";-    return values[index];+    if (&label == &Function_Order_get) {+      TRACE_FUNCTION("VectorOrder.get")+      if (index_ >= values_.size()) {+        FAIL() << "Iterated past end of Vector";+      }+      return ReturnTuple(values_[index_]);+    }+    return TypeValue::Dispatch(label, params, args);   } -  const VectorType& values;-  int index = 0;+ private:+  const BoxedValue container_;+  const VectorType& values_;+  int index_ = 0; };  struct ExtValue_Vector : public Value_Vector {-  inline ExtValue_Vector(S<Type_Vector> p, VectorType v)-    : Value_Vector(p), values(std::move(v)) {}+  inline ExtValue_Vector(S<const Type_Vector> p, VectorType v)+    : Value_Vector(std::move(p)), values(std::move(v)) {}    ReturnTuple Call_append(const ParamTuple& params, const ValueTuple& args) final {     TRACE_FUNCTION("Vector.append")@@ -177,15 +186,20 @@   return category; } -S<Type_Vector> CreateType_Vector(Params<1>::Type params) {-  static auto& cache = *new InstanceCache<1, Type_Vector>([](Params<1>::Type params) {-      return S_get(new ExtType_Vector(CreateCategory_Vector(), params));-    });-  return cache.GetOrCreate(params);+static auto& Vector_instance_cache = *new InstanceCache<1, Type_Vector>([](const Params<1>::Type& params) {+    return S_get(new ExtType_Vector(CreateCategory_Vector(), params));+  });++S<const Type_Vector> CreateType_Vector(const Params<1>::Type& params) {+  return Vector_instance_cache.GetOrCreate(params); } -BoxedValue CreateValue_Vector(S<Type_Vector> parent, VectorType values) {-  return BoxedValue::New<ExtValue_Vector>(parent, values);+void RemoveType_Vector(const Params<1>::Type& params) {+  Vector_instance_cache.Remove(params);+}++BoxedValue CreateValue_Vector(S<const Type_Vector> parent, VectorType values) {+  return BoxedValue::New<ExtValue_Vector>(std::move(parent), std::move(values)); }  #ifdef ZEOLITE_PUBLIC_NAMESPACE
+ lib/container/src/auto-tree.0rx view
@@ -0,0 +1,358 @@+/* -----------------------------------------------------------------------------+Copyright 2019-2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define AutoBinaryTree {+  @value Int treeSize+  @value optional #n root++  new () {+    return #self{ 0, empty }+  }++  size () {+    return treeSize+  }++  duplicate () {+    if (present(root)) {+      return #self{ treeSize, require(root).duplicate() }+    } else {+      return #self{ 0, empty }+    }+  }++  getRoot () {+    return root+  }++  get (k) {+    return find(root,k)+  }++  set (k,v) {+    root, Bool added <- insert(root,k,v)+    if (added) {+      treeSize <- treeSize+1+    }+  }++  remove (k) {+    root, Bool removed <- delete(root,k)+    if (removed) {+      treeSize <- treeSize-1+    }+  }+++  @type insert (optional #n,#k,#v) -> (optional #n,Bool)+  insert (node,k,v) (newRoot,added) {+    if (!present(node)) {+      return #n.newNode(k,v), true+    }+    #n node2 <- require(node)+    if (k `#k.lessThan` node2.getKey()) {+      newRoot, added <- insert(node2.getLower(),k,v)+      \ node2.setLower(newRoot)+      newRoot <- rebalance(node2)+    } elif (node2.getKey() `#k.lessThan` k) {+      newRoot, added <- insert(node2.getHigher(),k,v)+      \ node2.setHigher(newRoot)+      newRoot <- rebalance(node2)+    } else {+      \ node2.setValue(v)+      \ node2.updateNode()+      return node2, false+    }+  }++  @type delete (optional #n,#k) -> (optional #n,Bool)+  delete (node,k) (newRoot,removed) {+    if (!present(node)) {+      return empty, false+    }+    #n node2 <- require(node)+    if (k `#k.lessThan` node2.getKey()) {+      newRoot, removed <- delete(node2.getLower(),k)+      \ node2.setLower(newRoot)+      newRoot <- rebalance(node2)+    } elif (node2.getKey() `#k.lessThan` k) {+      newRoot, removed <- delete(node2.getHigher(),k)+      \ node2.setHigher(newRoot)+      newRoot <- rebalance(node2)+    } else {+      return rebalance(removeNode(node2)), true+    }+  }++  @type find (optional #n,#k) -> (optional #v)+  find (node,k) {+    if (present(node)) {+      scoped {+        #n node2 <- require(node)+      } in if (k `#k.lessThan` node2.getKey()) {+        return find(node2.getLower(),k)+      } elif (node2.getKey() `#k.lessThan` k) {+        return find(node2.getHigher(),k)+      } else {+        return node2.getValue()+      }+    } else {+      return empty+    }+  }++  @type getBalance (optional #n) -> (Int)+  getBalance (node) (balance) {+    balance <- 0+    if (present(node)) {+      #n node2 <- require(node)+      if (present(node2.getLower())) {+        balance <- balance-require(node2.getLower()).getHeight()+      }+      if (present(node2.getHigher())) {+        balance <- balance+require(node2.getHigher()).getHeight()+      }+    }+  }++  @type rebalance (optional #n) -> (optional #n)+  rebalance (node) {+    if (!present(node)) {+      return empty+    }+    #n node2 <- require(node)+    \ node2.updateNode()+    scoped {+      Int balance <- getBalance(node2)+    } in if (balance > 1) {+      return pivotLower(node2)+    } elif (balance < -1) {+      return pivotHigher(node2)+    } else {+      return node2+    }+  }++  @type pivotHigher (#n) -> (#n)+  pivotHigher (node) (newNode) {+    if (getBalance(node.getLower()) > 0) {+      \ node.setLower(pivotLower(require(node.getLower())))+    }+    newNode <- require(node.getLower())+    \ node.setLower(newNode.getHigher())+    \ node.updateNode()+    \ newNode.setHigher(node)+    \ newNode.updateNode()+  }++  @type pivotLower (#n) -> (#n)+  pivotLower (node) (newNode) {+    if (getBalance(node.getHigher()) < 0) {+      \ node.setHigher(pivotHigher(require(node.getHigher())))+    }+    newNode <- require(node.getHigher())+    \ node.setHigher(newNode.getLower())+    \ node.updateNode()+    \ newNode.setLower(node)+    \ newNode.updateNode()+  }++  @type removeNode (#n) -> (optional #n)+  removeNode (node) (newNode) {+    if (getBalance(node) < 0) {+      optional #n temp, newNode <- removeHighest(node.getLower())+      \ node.setLower(temp)+    } else {+      optional #n temp, newNode <- removeLowest(node.getHigher())+      \ node.setHigher(temp)+    }+    if (present(newNode)) {+      \ swapChildren(node,require(newNode))+      \ require(newNode).updateNode()+    }+  }++  @type removeHighest (optional #n) -> (optional #n,optional #n)+  removeHighest (node) (newNode,removed) {+    if (!present(node)) {+      return empty, empty+    }+    #n node2 <- require(node)+    if (present(node2.getHigher())) {+      optional #n temp, removed <- removeHighest(node2.getHigher())+      \ node2.setHigher(temp)+      newNode <- rebalance(node2)+    } else {+      newNode <- node2.getLower()+      \ node2.setLower(empty)+      removed <- node+    }+  }++  @type removeLowest (optional #n) -> (optional #n,optional #n)+  removeLowest (node) (newNode,removed) {+    if (!present(node)) {+      return empty, empty+    }+    #n node2 <- require(node)+    if (present(node2.getLower())) {+      optional #n temp, removed <- removeLowest(node2.getLower())+      \ node2.setLower(temp)+      newNode <- rebalance(node2)+    } else {+      newNode <- node2.getHigher()+      \ node2.setHigher(empty)+      removed <- node+    }+  }++  @type swapChildren (#n,#n) -> ()+  swapChildren (l,r) {+    scoped {+      optional #n temp <- l.getLower()+      \ l.setLower(r.getLower())+    } in \ r.setLower(temp)+    scoped {+      optional #n temp <- l.getHigher()+      \ l.setHigher(r.getHigher())+    } in \ r.setHigher(temp)+  }+}++concrete TreeKeyValue<|#k,#v> {+  refines KeyValue<#k,#v>++  @category create<#k,#v> (#k,#v) -> (TreeKeyValue<#k,#v>)+}++define TreeKeyValue {+  $ReadOnly[key,value]$++  @value #k key+  @value #v value++  create (key,value) {+    return TreeKeyValue<#k,#v>{ key, value }+  }++  getKey () {+    return key+  }++  getValue () {+    return value+  }+}++define ForwardTreeOrder {+  $ReadOnly[node,prev]$++  @value BinaryTreeNode<#k,#v> node+  @value optional ForwardTreeOrder<#k,#v> prev++  create (node) (current) {+    optional BinaryTreeNode<#k,#v> node2 <- node+    current <- empty+    while (present(node2)) {+      current <- ForwardTreeOrder<#k,#v>{ require(node2), current }+      node2 <- require(node2).getLower()+    }+  }++  seek (key,node) (current) {+    optional BinaryTreeNode<#k,#v> node2 <- node+    current <- empty+    while (present(node2)) {+      if (require(node2).getKey() `#k.lessThan` key) {+        // Skip node2 in the traversal, since it's before key.+        node2 <- require(node2).getHigher()+      } elif (key `#k.lessThan` require(node2).getKey()) {+        current <- ForwardTreeOrder<#k,#v>{ require(node2), current }+        node2 <- require(node2).getLower()+      } else {+        current <- ForwardTreeOrder<#k,#v>{ require(node2), current }+        break+      }+    }+  }++  next () (current) {+    // Algorithm:+    // 1. Pop self from the stack.+    // 2. Traverse lower to the bottom starting from the higher child of self.+    optional BinaryTreeNode<#k,#v> node2 <- node.getHigher()+    current <- prev+    while (present(node2)) {+      current <- ForwardTreeOrder<#k,#v>{ require(node2), current }+      node2 <- require(node2).getLower()+    }+  }++  get () {+    return TreeKeyValue:create<?,?>(node.getKey(),node.getValue())+  }+}++define ReverseTreeOrder {+  $ReadOnly[node,prev]$++  @value BinaryTreeNode<#k,#v> node+  @value optional ReverseTreeOrder<#k,#v> prev++  create (node) (current) {+    optional BinaryTreeNode<#k,#v> node2 <- node+    current <- empty+    while (present(node2)) {+      current <- ReverseTreeOrder<#k,#v>{ require(node2), current }+      node2 <- require(node2).getHigher()+    }+  }++  seek (key,node) (current) {+    optional BinaryTreeNode<#k,#v> node2 <- node+    current <- empty+    while (present(node2)) {+      if (require(node2).getKey() `#k.lessThan` key) {+        current <- ReverseTreeOrder<#k,#v>{ require(node2), current }+        node2 <- require(node2).getHigher()+      } elif (key `#k.lessThan` require(node2).getKey()) {+        // Skip node2 in the traversal, since it's after key.+        node2 <- require(node2).getLower()+      } else {+        current <- ReverseTreeOrder<#k,#v>{ require(node2), current }+        break+      }+    }+  }++  next () (current) {+    // Algorithm:+    // 1. Pop self from the stack.+    // 2. Traverse higher to the bottom starting from the lower child of self.+    optional BinaryTreeNode<#k,#v> node2 <- node.getLower()+    current <- prev+    while (present(node2)) {+      current <- ReverseTreeOrder<#k,#v>{ require(node2), current }+      node2 <- require(node2).getHigher()+    }+  }++  get () {+    return TreeKeyValue:create<?,?>(node.getKey(),node.getValue())+  }+}
+ lib/container/src/helpers.0rx view
@@ -0,0 +1,45 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define KeyValueH {+  lessThan (x,y) {+    return lessThanWith<#k,#v,#k,#v>(x,y)+  }++  lessThanWith (x,y) {+    if (x.getKey() `#kk.lessThan` y.getKey()) {+      return true+    } elif (y.getKey() `#kk.lessThan` x.getKey()) {+      return false+    } else {+      return x.getValue() `#vv.lessThan` y.getValue()+    }+  }++  equals (x,y) {+    return equalsWith<#k,#v,#k,#v>(x,y)+  }++  equalsWith (x,y) {+    if (!(x.getKey() `#kk.equals` y.getKey())) {+      return false+    } else {+      return x.getValue() `#vv.equals` y.getValue()+    }+  }+}
+ lib/container/src/list.0rx view
@@ -0,0 +1,161 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define LinkedNode {+  @value #x value+  @value optional #self prev+  @value optional #self next++  newNode (value) {+    return #self{ value, empty, empty }+  }++  get () {+    return value+  }++  set (value2) {+    value <- value2+    return self+  }++  builder () {+    return ListBuilder<#x,#self>.new()+  }++  next () {+    return next+  }++  prev () {+    return prev+  }++  duplicate () {+    $Hidden[prev,next,value]$+    ListBuilder<#x,#self> builder <- #self.builder()+    traverse (self -> #x value2) {+      \ builder.append(value2)+    }+    scoped {+      optional #self head, _ <- builder.build()+    } in return require(head)+  }++  setNext (next2) (old) {+    old <- next+    $Hidden[old]$+    if (present(next)) {+      \ require(next).setPrevBase(empty)+    }+    next <- next2+    if (present(next)) {+      \ require(next).setPrev(empty)+      \ require(next).setPrevBase(self)+    }+  }++  setPrev (prev2) (old) {+    old <- prev+    $Hidden[old]$+    if (present(prev)) {+      \ require(prev).setNextBase(empty)+    }+    prev <- prev2+    if (present(prev)) {+      \ require(prev).setNext(empty)+      \ require(prev).setNextBase(self)+    }+  }++  @value setNextBase (optional LinkedNode<#x>) -> ()+  setNextBase (next2) {+    next <- next2+  }++  @value setPrevBase (optional LinkedNode<#x>) -> ()+  setPrevBase (prev2) {+    prev <- prev2+  }+}++define ForwardNode {+  @value #x value+  @value optional #self next++  newNode (value) {+    return #self{ value, empty }+  }++  get () {+    return value+  }++  set (value2) {+    value <- value2+    return self+  }++  builder () {+    return ListBuilder<#x,#self>.new()+  }++  next () {+    return next+  }++  duplicate () {+    $Hidden[next,value]$+    ListBuilder<#x,#self> builder <- #self.builder()+    traverse (self -> #x value2) {+      \ builder.append(value2)+    }+    scoped {+      optional #self head, _ <- builder.build()+    } in return require(head)+  }++  setNext (next2) (old) {+    old <- next+    next <- next2+  }+}++define ListBuilder {+  @value optional #n head+  @value optional #n tail++  new () {+    return #self{ empty, empty }+  }++  append (value) {+    #n node <- #n.newNode(value)+    if (!present(head)) {+      head <- node+    } else {+      \ require(tail).setNext(node)+    }+    tail <- node+    return self+  }++  build () {+    return head, tail+  }+}
+ lib/container/src/search-tree.0rx view
@@ -0,0 +1,195 @@+/* -----------------------------------------------------------------------------+Copyright 2019-2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define SearchTree {+  @value AutoBinaryTree<SearchTreeNode<#k,#v>,#k,#v,BinaryTreeNode<#k,#v>> tree++  new () {+    return #self{ AutoBinaryTree<SearchTreeNode<#k,#v>,#k,#v,BinaryTreeNode<#k,#v>>.new() }+  }++  size () {+    return tree.size()+  }++  duplicate () {+    return #self{ tree.duplicate() }+  }++  set (k,v) {+    \ tree.set(k,v)+    return self+  }++  remove (k) {+    \ tree.remove(k)+    return self+  }++  get (k) {+    return tree.get(k)+  }++  defaultOrder () {+    return ForwardTreeOrder:create<?,?>(tree.getRoot())+  }++  reverseOrder () {+    return ReverseTreeOrder:create<?,?>(tree.getRoot())+  }++  getForward (k) {+    return ForwardTreeOrder:seek<?,?>(k,tree.getRoot())+  }++  getReverse (k) {+    return ReverseTreeOrder:seek<?,?>(k,tree.getRoot())+  }+}++define ValidatedTree {+  @value AutoBinaryTree<SearchTreeNode<#k,#v>,#k,#v,BinaryTreeNode<#k,#v>> tree++  new () { $NoTrace$+    return #self{ AutoBinaryTree<SearchTreeNode<#k,#v>,#k,#v,BinaryTreeNode<#k,#v>>.new() }+  }++  size () { $NoTrace$+    return tree.size()+  }++  set (k,v) { $NoTrace$+    \ tree.set(k,v)+    \ validate(tree.getRoot())+    return self+  }++  remove (k) { $NoTrace$+    \ tree.remove(k)+    \ validate(tree.getRoot())+    return self+  }++  get (k) { $NoTrace$+    return tree.get(k)+  }++  @type validate (optional BinaryTreeNode<#k,#v>) -> ()+  validate (node) { $NoTrace$+    if (present(node)) {+      \ validateOrder(require(node))+      \ validateBalance(require(node))+    }+  }++  @type validateOrder (BinaryTreeNode<#k,#v>) -> ()+  validateOrder (node) { $NoTrace$+    if (present(node.getLower())) {+      if (!(require(node.getLower()).getKey() `#k.lessThan` node.getKey())) {+        fail("bad lower order")+      }+      \ validateOrder(require(node.getLower()))+    }+    if (present(node.getHigher())) {+      if (!(node.getKey() `#k.lessThan` require(node.getHigher()).getKey())) {+        fail("bad higher order")+      }+      \ validateOrder(require(node.getHigher()))+    }+  }++  @type validateBalance (BinaryTreeNode<#k,#v>) -> ()+  validateBalance (node) { $NoTrace$+    scoped {+      Int balance <- 0+      if (present(node.getLower())) {+        balance <- balance-require(node.getLower()).getHeight()+      }+      if (present(node.getHigher())) {+        balance <- balance+require(node.getHigher()).getHeight()+      }+      $ReadOnly[balance]$+    } in if (balance > 1 || balance < -1) {+      fail("out of balance: " + balance.formatted())+    }+    if (present(node.getLower())) {+      \ validateBalance(require(node.getLower()))+    }+    if (present(node.getHigher())) {+      \ validateBalance(require(node.getHigher()))+    }+  }+}++concrete SearchTreeNode<#k,#v> {+  defines KVFactory<#k,#v>+  refines BalancedTreeNode<SearchTreeNode<#k,#v>,#k,#v>+  refines Duplicate+}++define SearchTreeNode {+  $ReadOnly[key]$++  @value Int height+  @value #k key+  @value #v value+  @value optional #self lower+  @value optional #self higher++  newNode (k,v) {+    return #self{ 1, k, v, empty, empty }+  }++  duplicate () {+    optional #self lower2 <- empty+    if (present(lower)) {+      lower2 <- require(lower).duplicate()+    }+    optional #self higher2 <- empty+    if (present(higher)) {+      higher2 <- require(higher).duplicate()+    }+    return #self{ height, key, value, lower2, higher2 }+  }++  getLower ()   { return lower }+  setLower (l)  { lower <- l }+  getHigher ()  { return higher }+  setHigher (h) { higher <- h }+  getKey ()     { return key }+  getValue ()   { return value }+  setValue (v)  { value <- v }+  getHeight ()  { return height }++  updateNode () {+    scoped {+      Int l <- 0+      Int h <- 0+      if (present(lower)) {+        l <- require(lower).getHeight()+      }+      if (present(higher)) {+        h <- require(higher).getHeight()+      }+    } in if (l > h) {+      height <- l + 1+    } else {+      height <- h + 1+    }+  }+}
+ lib/container/src/sorting.0rx view
@@ -0,0 +1,262 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define Sorting {+  sort (seq) {+    \ sortWith<#x,#x>(seq)+  }++  sortWith (seq) {+    \ HeapSort<#x,#xx>.inPlace(seq)+  }++  reverse (seq) {+    scoped {+      Int i <- seq.size()/2-1+    } in while (i >= 0) {+      $ReadOnly[i]$+      Int j <- seq.size()-i-1+      #x temp <- seq.readAt(i)+      \ seq.writeAt(i,seq.readAt(j))+      \ seq.writeAt(j,temp)+    } update {+      i <- i-1+    }+  }++  sortList (head) {+    return sortListWith<#n,#x,#x>(head)+  }++  sortListWith (head) {+    return MergeSort<#n,#x,#xx>.sort(head)+  }++  reverseList (head) (head2) {+    if (!present(head)) {+      return empty+    }+    head2 <- head+    $Hidden[head]$++    scoped {+      optional #n prev <- empty+    } in while (true) {+      optional #n next <- require(head2).next()+      \ require(head2).setNext(prev)+      prev <- head2+      if (present(next)) {+        head2 <- next+      } else {+        break+      }+    }+  }+}++// Putting the params at the top level allows helpers to be called without+// needing to pass the params every time.+concrete HeapSort<#x,#xx> {+  #xx defines LessThan<#x>++  @type inPlace ([ReadAt<#x>&WriteAt<#x>]) -> ()+}++define HeapSort {+  $ReadOnly[seq]$++  @value [ReadAt<#x>&WriteAt<#x>] seq++  inPlace (seq) {+    \ #self{ seq }.execute()+  }++  @value execute () -> ()+  execute () { $NoTrace$+    // Convert the container to a heap.+    scoped {+      Int i <- seq.size()/2-1+    } in while (i >= 0) {+      $ReadOnly[i]$+      \ sift(i,seq.size())+    } update {+      i <- i-1+    }++    // Traverse the heap and populate the container in place.+    scoped {+      Int i <- seq.size()-1+    } in while (i >= 0) {+      $ReadOnly[i]$+      \ swap(0,i)+      \ sift(0,i)+    } update {+      i <- i-1+    }+  }++  @value sift (Int,Int) -> ()+  sift (start,size) { $NoTrace$+    scoped {+      Int last         <- start+      Int indexLargest <- last+      $Hidden[start]$+    } in while (2*last+1 < size) {+      $ReadOnly[last]$+      Int left  <- 2*last+1+      Int right <- 2*last+2+      $ReadOnly[left,right]$+      if (seq.readAt(indexLargest) `#xx.lessThan` seq.readAt(left)) {+        indexLargest <- left+      }+      if (right < size && seq.readAt(indexLargest) `#xx.lessThan` seq.readAt(right)) {+        indexLargest <- right+      }+      if (indexLargest == last) {+        break+      }+    } update {+      \ swap(last,indexLargest)+      last <- indexLargest+    }+  }++  @value swap (Int,Int) -> ()+  swap (i,j) { $NoTrace$+    if (i != j) {+      #x temp <- seq.readAt(i)+      \ seq.writeAt(i,seq.readAt(j))+      \ seq.writeAt(j,temp)+    }+  }+}++// Putting the params at the top level allows helpers to be called without+// needing to pass the params every time.+concrete MergeSort<#n,#x,#xx> {+  #xx defines LessThan<#x>+  #n requires ListNode<#n,#x>++  @type sort (optional #n) -> (optional #n)+}++define MergeSort {+  sort (head) (head2) {+    head2 <- head+    $Hidden[head]$++    scoped {+      Int chunk <- 1+      Bool dirty <- true+    } in while (dirty) {+      $ReadOnly[chunk]$+      scoped {+        optional #n tail <- empty+        optional #n next <- head2+        head2 <- empty+        dirty <- false+      } in while (present(next)) {+        optional #n left  <- next+        optional #n right <- left  `splitAt` chunk+        next              <- right `splitAt` chunk+        $Hidden[next]$+        $ReadOnly[left,right]$+        dirty <- dirty || present(right)+        optional #n newHead, optional #n newTail <- left `merge` right+        if (!present(head2)) {+          head2 <- newHead+        } else {+          \ require(tail).setNext(newHead)+        }+        tail <- newTail+      }+    } update {+      chunk <- 2*chunk+    }+  }++  @type splitAt (optional #n,Int) -> (optional #n)+  splitAt (head,n) {+    optional #n head2 <- head+    $Hidden[head]$++    scoped {+      Int i <- n+    } in while (present(head2)) {+      if (i == 1) {+        return require(head2).setNext(empty)+      } else {+        head2 <- require(head2).next()+      }+    } update {+      i <- i-1+    }++    return empty+  }++  @type merge (optional #n,optional #n) -> (optional #n,optional #n)+  merge (left,right) (head,tail) {+    head <- empty+    tail <- empty++    optional #n left2  <- left+    optional #n right2 <- right+    $Hidden[left,right]$++    while (present(left2) && present(right2)) {+      #n append <- defer+      if (require(left2).get() `#xx.lessThan` require(right2).get()) {+        left2 <- (append <- require(left2)).next()+      } else {+        right2 <- (append <- require(right2)).next()+      }+      if (!present(head)) {+        head <- append+      } else {+        \ require(tail).setNext(append)+      }+      tail <- append+    }++    if (present(left2)) {+      if (!present(head)) {+        head <- require(left2)+      } else {+        \ require(tail).setNext(require(left2))+      }+    }++    if (present(right2)) {+      if (!present(head)) {+        head <- require(right2)+      } else {+        \ require(tail).setNext(require(right2))+      }+    }++    while (present(tail)) {+      optional #n next <- require(tail).next()+      if (present(next)) {+        tail <- next+      } else {+        break+      }+    }+  }+}
+ lib/container/src/tree-set.0rx view
@@ -0,0 +1,96 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define TreeSet {+  @value SearchTree<#k,any> tree++  new () {+    return #self{ SearchTree<#k,any>.new() }+  }++  size () {+    return tree.size()+  }++  duplicate () {+    return #self{ tree.duplicate() }+  }++  add (k) {+    \ tree.set(k,Void.void())+    return self+  }++  remove (k) {+    \ tree.remove(k)+    return self+  }++  member (k) {+    return present(tree.get(k))+  }++  defaultOrder () {+    return TreeSetOrder<#k>.wrap(tree.defaultOrder())+  }++  reverseOrder () {+    return TreeSetOrder<#k>.wrap(tree.reverseOrder())+  }++  getForward (k) {+    return TreeSetOrder<#k>.wrap(tree.getForward(k))+  }++  getReverse (k) {+    return TreeSetOrder<#k>.wrap(tree.getReverse(k))+  }+}++concrete TreeSetOrder<#k> {+  refines Order<#k>++  @type wrap (optional Order<KeyValue<#k,any>>) -> (optional TreeSetOrder<#k>)+}++define TreeSetOrder {+  @value Order<KeyValue<#k,any>> order++  wrap (order) {+    if (!present(order)) {+      return empty+    } else {+      return #self{ require(order) }+    }+  }++  next () {+    scoped {+      optional Order<KeyValue<#k,any>> order2 <- order.next()+    } in if (present(order2)) {+      order <- require(order2)+      return self+    } else {+      return empty+    }+  }++  get () {+    return order.get().getKey()+  }+}
+ lib/container/src/type-map.0rx view
@@ -0,0 +1,106 @@+/* -----------------------------------------------------------------------------+Copyright 2019-2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define TypeMap {+  @value SearchTree<TypeKey<any>,GenericValue<any>> tree++  new () {+    return TypeMap{ SearchTree<TypeKey<any>,GenericValue<any>>.new() }+  }++  duplicate () {+    return #self{ tree.duplicate() }+  }++  set (k,v) {+    \ tree.set(k,GenericValue:create<?>(v))+    return self+  }++  remove (k) {+    \ tree.remove(k)+    return self+  }++  get (k) {+    scoped {+      optional GenericValue<any> value <- tree.get(k)+    } in if (present(value)) {+      return require(value).check<#x>()+    } else {+      return empty+    }+  }+}++define TypeKey {+  $ReadOnly[counterMutex,index]$++  @category Mutex counterMutex <- SpinlockMutex.new()+  @category Int   counter      <- 0+  @value Int index++  new () {+    scoped {+      \ counterMutex.lock()+    } cleanup {+      \ counterMutex.unlock()+    } in return TypeKey<#x>{ (counter <- counter+1) }+  }++  formatted () {+    return String.builder()+        .append(typename<#self>())+        .append("{")+        .append(index)+        .append("}")+        .build()+  }++  lessThan (l,r) {+    return l.get() < r.get()+  }++  equals (l,r) {+    return l.get() == r.get()+  }++  @value get () -> (Int)+  get () {+    return index+  }+}++concrete GenericValue<|#x> {+  @category create<#x> (#x) -> (GenericValue<#x>)+  @value check<#y> () -> (optional #y)+}++define GenericValue {+  $ReadOnly[value]$++  @value #x value++  create (v) {+    return GenericValue<#x>{ v }+  }++  check () {+    return reduce<#x,#y>(value)+  }+}
+ lib/container/src/validated-tree.0rp view
@@ -0,0 +1,35 @@+/* -----------------------------------------------------------------------------+Copyright 2019-2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++$ModuleOnly$++// A validated binary search tree for key-value storage.+//+// Notes:+// - This is the same as SearchTree, but it validates the tree structure every+//   time there is a modification, which is extremely inefficient. This should+//   therefore only be used in tests.+concrete ValidatedTree<#k|#v|> {+  refines Container+  refines KVWriter<#k,#v>+  refines KVReader<#k,#v>+  #k immutable+  #k defines LessThan<#k>++  @type new () -> (#self)+}
+ lib/container/test/helpers.0rt view
@@ -0,0 +1,107 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "comparator helper tests" {+  success+}++unittest keyValueHLessThan {+  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:lessThan<?,?>` KV.new(1,"a"),false)+  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:lessThan<?,?>` KV.new(1,"b"),true)+  \ Testing.checkEquals<?>(KV.new(2,"a") `KeyValueH:lessThan<?,?>` KV.new(1,"b"),false)+  \ Testing.checkEquals<?>(KV.new(1,"b") `KeyValueH:lessThan<?,?>` KV.new(2,"a"),true)+}++unittest keyValueHLessThanWith {+  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:lessThanWith<?,?,Int,AlwaysEqual>` KV.new(1,"a"),false)+  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:lessThanWith<?,?,Int,AlwaysEqual>` KV.new(1,"b"),false)+  \ Testing.checkEquals<?>(KV.new(2,"a") `KeyValueH:lessThanWith<?,?,Int,AlwaysEqual>` KV.new(1,"b"),false)+  \ Testing.checkEquals<?>(KV.new(1,"b") `KeyValueH:lessThanWith<?,?,Int,AlwaysEqual>` KV.new(2,"a"),true)++  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:lessThanWith<?,?,AlwaysEqual,String>` KV.new(1,"a"),false)+  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:lessThanWith<?,?,AlwaysEqual,String>` KV.new(1,"b"),true)+  \ Testing.checkEquals<?>(KV.new(2,"a") `KeyValueH:lessThanWith<?,?,AlwaysEqual,String>` KV.new(1,"b"),true)+  \ Testing.checkEquals<?>(KV.new(1,"b") `KeyValueH:lessThanWith<?,?,AlwaysEqual,String>` KV.new(2,"a"),false)+}++unittest keyValueHEquals {+  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:equals<?,?>` KV.new(1,"a"),true)+  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:equals<?,?>` KV.new(1,"b"),false)+  \ Testing.checkEquals<?>(KV.new(2,"a") `KeyValueH:equals<?,?>` KV.new(1,"b"),false)+  \ Testing.checkEquals<?>(KV.new(1,"b") `KeyValueH:equals<?,?>` KV.new(2,"a"),false)+}++unittest keyValueHEqualsWith {+  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:equalsWith<?,?,Int,AlwaysEqual>` KV.new(1,"a"),true)+  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:equalsWith<?,?,Int,AlwaysEqual>` KV.new(1,"b"),true)+  \ Testing.checkEquals<?>(KV.new(2,"a") `KeyValueH:equalsWith<?,?,Int,AlwaysEqual>` KV.new(1,"b"),false)+  \ Testing.checkEquals<?>(KV.new(1,"b") `KeyValueH:equalsWith<?,?,Int,AlwaysEqual>` KV.new(2,"a"),false)++  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:equalsWith<?,?,AlwaysEqual,String>` KV.new(1,"a"),true)+  \ Testing.checkEquals<?>(KV.new(1,"a") `KeyValueH:equalsWith<?,?,AlwaysEqual,String>` KV.new(1,"b"),false)+  \ Testing.checkEquals<?>(KV.new(2,"a") `KeyValueH:equalsWith<?,?,AlwaysEqual,String>` KV.new(1,"b"),false)+  \ Testing.checkEquals<?>(KV.new(1,"b") `KeyValueH:equalsWith<?,?,AlwaysEqual,String>` KV.new(2,"a"),false)+}++unittest integrationTest {+  SearchTree<Int,String> tree1 <- SearchTree<Int,String>.new()+      .set(1,"d").set(2,"c").set(3,"b").set(4,"a")+  SearchTree<Int,String> tree2 <- SearchTree<Int,String>.new()+      .set(1,"a").set(2,"b").set(3,"c").set(4,"d")++  \ Testing.checkEquals<?>(tree1.defaultOrder() `OrderH:equalsWith<?,KVEquals<Int,AlwaysEqual>>`    tree2.defaultOrder(),true)+  \ Testing.checkEquals<?>(tree1.defaultOrder() `OrderH:equalsWith<?,KVEquals<Int,String>>`         tree2.defaultOrder(),false)+  \ Testing.checkEquals<?>(tree1.defaultOrder() `OrderH:equalsWith<?,KVEquals<AlwaysEqual,String>>` tree2.defaultOrder(),false)+}++concrete KVEquals<#k,#v> {+  defines Equals<KeyValue<Int,String>>+  #k defines Equals<Int>+  #v defines Equals<String>+}++define KVEquals {+  equals (x,y) {+    return x `KeyValueH:equalsWith<?,?,#k,#v>` y+  }+}++concrete KV {+  refines KeyValue<Int,String>++  @type new (Int,String) -> (KV)+}++define KV {+  $ReadOnly[key,value]$++  @value Int key+  @value String value++  new (k,v) {+    return KV{ k, v }+  }++  getKey () {+    return key+  }++  getValue () {+    return value+  }+}
+ lib/container/test/list.0rt view
@@ -0,0 +1,319 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "basic list tests" {+  success+  timeout 5+}++unittest forwardTraverse {+  optional LinkedNode<Int> head, _ <- LinkedNode<Int>.builder()+      .append(3)+      .append(2)+      .append(5)+      .append(1)+      .append(6)+      .append(4)+      .build()++  DefaultOrder<Int> expected <- Vector:create<Int>()+      .append(3)+      .append(2)+      .append(5)+      .append(1)+      .append(6)+      .append(4)++  traverse (expected.defaultOrder() -> Int value) {+    \ Testing.checkEquals<?>(require(head).get(),value)+  } update {+    head <- require(head).next()+  }+  \ Testing.checkEquals<?>(present(head),false)+}++unittest reverseTraverse {+  _, optional LinkedNode<Int> tail <- LinkedNode<Int>.builder()+      .append(3)+      .append(2)+      .append(5)+      .append(1)+      .append(6)+      .append(4)+      .build()++  DefaultOrder<Int> expected <- Vector:create<Int>()+      .append(4)+      .append(6)+      .append(1)+      .append(5)+      .append(2)+      .append(3)++  traverse (expected.defaultOrder() -> Int value) {+    \ Testing.checkEquals<?>(require(tail).get(),value)+  } update {+    tail <- require(tail).prev()+  }+  \ Testing.checkEquals<?>(present(tail),false)+}++unittest setNext {+  optional LinkedNode<Int> head1, _ <- LinkedNode<Int>.builder()+      .append(3)+      .append(2)+      .append(5)+      .append(1)+      .append(6)+      .append(4)+      .build()++  optional LinkedNode<Int> after1  <- Helper.jumpBy<?>(3,head1)+  optional LinkedNode<Int> before1 <- require(after1).prev()++  optional LinkedNode<Int> head2, _ <- LinkedNode<Int>.builder()+      .append(13)+      .append(12)+      .append(15)+      .append(11)+      .append(16)+      .append(14)+      .build()++  optional LinkedNode<Int> after2  <- Helper.jumpBy<?>(3,head2)+  optional LinkedNode<Int> before2 <- require(after2).prev()++  \ Testing.checkEquals<?>(require(require(before1).setNext(after2)).get(),1)++  \ Testing.checkEquals<?>(present(require(after1).prev()),false)+  \ Testing.checkEquals<?>(require(require(before1).next()).get(),11)+  \ Testing.checkEquals<?>(require(require(after2).prev()).get(),5)+  \ Testing.checkEquals<?>(present(require(before2).next()),false)+}++unittest setPrev {+  optional LinkedNode<Int> head1, _ <- LinkedNode<Int>.builder()+      .append(3)+      .append(2)+      .append(5)+      .append(1)+      .append(6)+      .append(4)+      .build()++  optional LinkedNode<Int> after1  <- Helper.jumpBy<?>(3,head1)+  optional LinkedNode<Int> before1 <- require(after1).prev()++  optional LinkedNode<Int> head2, _ <- LinkedNode<Int>.builder()+      .append(13)+      .append(12)+      .append(15)+      .append(11)+      .append(16)+      .append(14)+      .build()++  optional LinkedNode<Int> after2  <- Helper.jumpBy<?>(3,head2)+  optional LinkedNode<Int> before2 <- require(after2).prev()++  \ Testing.checkEquals<?>(require(require(after1).setPrev(before2)).get(),5)++  \ Testing.checkEquals<?>(require(require(after1).prev()).get(),15)+  \ Testing.checkEquals<?>(present(require(before1).next()),false)+  \ Testing.checkEquals<?>(present(require(after2).prev()),false)+  \ Testing.checkEquals<?>(require(require(before2).next()).get(),1)+}++unittest setNextSame {+  optional LinkedNode<Int> head, _ <- LinkedNode<Int>.builder()+      .append(3)+      .append(2)+      .append(5)+      .append(1)+      .append(6)+      .append(4)+      .build()++  optional LinkedNode<Int> after  <- Helper.jumpBy<?>(3,head)+  optional LinkedNode<Int> before <- require(after).prev()++  \ require(before).setNext(after)++  \ Testing.checkEquals<?>(require(require(after).prev()).get(),5)+  \ Testing.checkEquals<?>(require(require(before).next()).get(),1)+}++unittest setPrevSame {+  optional LinkedNode<Int> head, _ <- LinkedNode<Int>.builder()+      .append(3)+      .append(2)+      .append(5)+      .append(1)+      .append(6)+      .append(4)+      .build()++  optional LinkedNode<Int> after  <- Helper.jumpBy<?>(3,head)+  optional LinkedNode<Int> before <- require(after).prev()++  \ require(after).setPrev(before)++  \ Testing.checkEquals<?>(require(require(after).prev()).get(),5)+  \ Testing.checkEquals<?>(require(require(before).next()).get(),1)+}++unittest setNextSelf {+  optional LinkedNode<Int> head, _ <- LinkedNode<Int>.builder()+      .append(3)+      .append(2)+      .append(5)+      .append(1)+      .append(6)+      .append(4)+      .build()++  optional LinkedNode<Int> after  <- Helper.jumpBy<?>(3,head)+  optional LinkedNode<Int> before <- require(after).prev()++  \ require(before).setNext(before)++  \ Testing.checkEquals<?>(present(require(after).prev()),false)+  \ Testing.checkEquals<?>(require(require(before).prev()).get(),5)+  \ Testing.checkEquals<?>(require(require(before).next()).get(),5)+}++unittest setPrevSelf {+  optional LinkedNode<Int> head, _ <- LinkedNode<Int>.builder()+      .append(3)+      .append(2)+      .append(5)+      .append(1)+      .append(6)+      .append(4)+      .build()++  optional LinkedNode<Int> after  <- Helper.jumpBy<?>(3,head)+  optional LinkedNode<Int> before <- require(after).prev()++  \ require(after).setPrev(after)++  \ Testing.checkEquals<?>(present(require(before).next()),false)+  \ Testing.checkEquals<?>(require(require(after).prev()).get(),1)+  \ Testing.checkEquals<?>(require(require(after).next()).get(),1)+}++unittest duplicate {+  optional LinkedNode<Int> head, _ <- LinkedNode<Int>.builder()+      .append(3)+      .append(2)+      .append(5)+      .append(1)+      .append(6)+      .append(4)+      .build()++  DefaultOrder<Int> expected <- Vector:create<Int>()+      .append(3)+      .append(2)+      .append(5)+      .append(1)+      .append(6)+      .append(4)++  optional LinkedNode<Int> head2 <- require(head).duplicate()+  \ require(Helper.jumpBy<?>(3,head)).set(7).setNext(empty)+  $Hidden[head]$++  traverse (expected.defaultOrder() -> Int value) {+    \ Testing.checkEquals<?>(require(head2).get(),value)+  } update {+    head2 <- require(head2).next()+  }+  \ Testing.checkEquals<?>(present(head2),false)+}++unittest forwardTraverseSingle {+  optional ForwardNode<Int> head, _ <- ForwardNode<Int>.builder()+      .append(3)+      .append(2)+      .append(5)+      .append(1)+      .append(6)+      .append(4)+      .build()++  DefaultOrder<Int> expected <- Vector:create<Int>()+      .append(3)+      .append(2)+      .append(5)+      .append(1)+      .append(6)+      .append(4)++  traverse (expected.defaultOrder() -> Int value) {+    \ Testing.checkEquals<?>(require(head).get(),value)+  } update {+    head <- require(head).next()+  }+  \ Testing.checkEquals<?>(present(head),false)+}++unittest duplicateSingle {+  optional ForwardNode<Int> head, _ <- ForwardNode<Int>.builder()+      .append(3)+      .append(2)+      .append(5)+      .append(1)+      .append(6)+      .append(4)+      .build()++  DefaultOrder<Int> expected <- Vector:create<Int>()+      .append(3)+      .append(2)+      .append(5)+      .append(1)+      .append(6)+      .append(4)++  optional ForwardNode<Int> head2 <- require(head).duplicate()+  \ require(Helper.jumpBy<?>(3,head)).set(7).setNext(empty)+  $Hidden[head]$++  traverse (expected.defaultOrder() -> Int value) {+    \ Testing.checkEquals<?>(require(head2).get(),value)+  } update {+    head2 <- require(head2).next()+  }+  \ Testing.checkEquals<?>(present(head2),false)+}++concrete Helper {+  @type jumpBy<#x>+    #x requires Order<any>+  (Int,optional #x) -> (optional #x)+}++define Helper {+  jumpBy (n,x) (y) {+    y <- x+    traverse (Counter.zeroIndexed(n) -> _) {+      y <- require(y).next()+    }+  }+}
+ lib/container/test/search-tree.0rt view
@@ -0,0 +1,206 @@+/* -----------------------------------------------------------------------------+Copyright 2019-2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "SearchTree tests" {+  success+}++unittest integrationTest { $DisableCoverage$+  ValidatedTree<Int,Int> tree <- ValidatedTree<Int,Int>.new()+  Int count <- 30+  $ReadOnly[count]$++  // Insert values.+  traverse (Counter.zeroIndexed(count) -> Int i) {+    Int new <- ((i + 13) * 3547) % count+    \ tree.set(new,i)+    \ Testing.checkEquals<?>(tree.size(),i+1)+  }++  // Check and remove values.+  traverse (Counter.zeroIndexed(count) -> Int i) {+    Int new <- ((i + 13) * 3547) % count+    scoped {+      optional Int value <- tree.get(new)+    } in if (!present(value)) {+      \ LazyStream<Formatted>.new()+          .append("Not found ")+          .append(new)+          .append(" but should have been ")+          .append(i)+          .writeTo(SimpleOutput.error())+    } elif (require(value) != i) {+      \ LazyStream<Formatted>.new()+          .append("Element ")+          .append(new)+          .append(" should have been ")+          .append(i)+          .append(" but was ")+          .append(require(value))+          .writeTo(SimpleOutput.error())+    }+    \ tree.remove(new)+    \ Testing.checkEquals<?>(tree.size(),count-i-1)+  }+}++unittest defaultOrder {+  SearchTree<Int,Int> tree <- SearchTree<Int,Int>.new()+  Int hash <- 13+  Int max  <- 20+  $ReadOnly[hash,max]$++  // Populate the tree in a pseudo-random order.+  traverse (Counter.zeroIndexed(max) -> Int i) {+    \ ((i*hash)%max) `tree.set` i+  }++  // Validate the traversal order.+  Int index <- 0+  traverse (tree.defaultOrder() -> KeyValue<Int,Int> entry) {+    \ Testing.checkEquals<?>(entry.getKey(),index)+    \ Testing.checkEquals<?>((entry.getValue()*hash)%max,entry.getKey())+    index <- index+1+  }++  \ Testing.checkEquals<?>(index,max)+}++unittest reverseOrder {+  SearchTree<Int,Int> tree <- SearchTree<Int,Int>.new()+  Int hash <- 13+  Int max  <- 20+  $ReadOnly[hash,max]$++  // Populate the tree in a pseudo-random order.+  traverse (Counter.zeroIndexed(max) -> Int i) {+    \ ((i*hash)%max) `tree.set` i+  }++  // Validate the traversal order.+  Int index <- max+  traverse (tree.reverseOrder() -> KeyValue<Int,Int> entry) {+    index <- index-1+    \ Testing.checkEquals<?>(entry.getKey(),index)+    \ Testing.checkEquals<?>((entry.getValue()*hash)%max,entry.getKey())+  }++  \ Testing.checkEquals<?>(index,0)+}++unittest getForward {+  SearchTree<Int,Int> tree <- SearchTree<Int,Int>.new()+  Int hash <- 13+  Int max  <- 20+  $ReadOnly[hash,max]$++  // Populate the tree in a pseudo-random order.+  traverse (Counter.zeroIndexed(max) -> Int i) {+    \ ((i*hash)%max) `tree.set` i+  }++  // Validate the traversal order from every starting point.+  traverse (Counter.zeroIndexed(max) -> Int i) {+    Int index <- i+    traverse (tree.getForward(index) -> KeyValue<Int,Int> entry) {+      \ Testing.checkEquals<?>(entry.getKey(),index)+      \ Testing.checkEquals<?>((entry.getValue()*hash)%max,entry.getKey())+      index <- index+1+    }+    \ Testing.checkEquals<?>(index,max)+  }+}++unittest getForwardNotFound {+  SearchTree<Int,Int> tree <- SearchTree<Int,Int>.new()+  \ tree.set(1,1).set(3,3).set(5,5)++  scoped {+    optional Order<KeyValue<Int,Int>> start <- tree.getForward(4)+  } in if (!present(start)) {+    fail("Failed")+  } else {+    \ Testing.checkEquals<?>(require(start).get().getKey(),5)+  }++  scoped {+    optional Order<KeyValue<Int,Int>> start <- tree.getForward(6)+  } in if (present(start)) {+    fail("Failed")+  }+}++unittest getReverse {+  SearchTree<Int,Int> tree <- SearchTree<Int,Int>.new()+  Int hash <- 13+  Int max  <- 20+  $ReadOnly[hash,max]$++  // Populate the tree in a pseudo-random order.+  traverse (Counter.zeroIndexed(max) -> Int i) {+    \ ((i*hash)%max) `tree.set` i+  }++  // Validate the traversal order from every starting point.+  traverse (Counter.zeroIndexed(max) -> Int i) {+    Int index <- i+    traverse (tree.getReverse(index) -> KeyValue<Int,Int> entry) {+      \ Testing.checkEquals<?>(entry.getKey(),index)+      \ Testing.checkEquals<?>((entry.getValue()*hash)%max,entry.getKey())+      index <- index-1+    }+    \ Testing.checkEquals<?>(index,-1)+  }+}++unittest getReverseNotFound {+  SearchTree<Int,Int> tree <- SearchTree<Int,Int>.new()+  \ tree.set(1,1).set(3,3).set(5,5)++  scoped {+    optional Order<KeyValue<Int,Int>> start <- tree.getReverse(2)+  } in if (!present(start)) {+    fail("Failed")+  } else {+    \ Testing.checkEquals<?>(require(start).get().getKey(),1)+  }++  scoped {+    optional Order<KeyValue<Int,Int>> start <- tree.getReverse(0)+  } in if (present(start)) {+    fail("Failed")+  }+}++unittest duplicate {+  SearchTree<Int,Int> tree <- SearchTree<Int,Int>.new().set(1,1).set(2,2).set(3,3)++  SearchTree<Int,Int> copy <- tree.duplicate()+  \ Testing.checkEquals<?>(copy.size(),3)+  \ Testing.checkOptional<?>(copy.get(1),1)+  \ Testing.checkOptional<?>(copy.get(2),2)+  \ Testing.checkOptional<?>(copy.get(3),3)++  \ copy.remove(2)+  $Hidden[copy]$++  \ Testing.checkEquals<?>(tree.size(),3)+  \ Testing.checkOptional<?>(tree.get(1),1)+  \ Testing.checkOptional<?>(tree.get(2),2)+  \ Testing.checkOptional<?>(tree.get(3),3)+}
+ lib/container/test/sorting.0rt view
@@ -0,0 +1,311 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "Sorting tests" {+  success+  timeout 5+}++unittest sort {+  TestSequence original <- TestSequence.create()+  TestSequence expected <- TestSequence.create()+  Int hash <- 269+  Int size <- 137+  $ReadOnly[hash,size]$++  traverse (Counter.zeroIndexed(size) -> Int i) {+    \ original.append((i*hash)%size/3)+    \ expected.append(i/3)+  }++  \ Sorting:sort<?>(original)+  \ Testing.checkEquals<?>(original,expected)+}++unittest sortWith { $DisableCoverage$+  TestSequence original <- TestSequence.create()+  TestSequence expected <- TestSequence.create()+  Int hash <- 313+  Int size <- 197+  $ReadOnly[hash,size]$++  traverse (Counter.revZeroIndexed(size) -> Int i) {+    \ original.append((i*hash)%size/3)+    \ expected.append(i/3)+  }++  \ Sorting:sortWith<?,Reversed<Int>>(original)+  \ Testing.checkEquals<?>(original,expected)+}++unittest sortEmpty {+  TestSequence original <- TestSequence.create()+  TestSequence expected <- TestSequence.create()++  \ Sorting:sort<?>(original)+  \ Testing.checkEquals<?>(original,expected)+}++unittest reverseEvenSize {+  TestSequence original <- TestSequence.create()+  TestSequence expected <- TestSequence.create()+  Int size <- 100+  $ReadOnly[size]$++  traverse (Counter.zeroIndexed(size) -> Int i) {+    \ expected.append(i)+  }++  traverse (Counter.revZeroIndexed(size) -> Int i) {+    \ original.append(i)+  }++  \ Sorting:reverse<?>(original)+  \ Testing.checkEquals<?>(original,expected)+}++unittest reverseOddSize { $DisableCoverage$+  TestSequence original <- TestSequence.create()+  TestSequence expected <- TestSequence.create()+  Int size <- 101+  $ReadOnly[size]$++  traverse (Counter.zeroIndexed(size) -> Int i) {+    \ expected.append(i)+  }++  traverse (Counter.revZeroIndexed(size) -> Int i) {+    \ original.append(i)+  }++  \ Sorting:reverse<?>(original)+  \ Testing.checkEquals<?>(original,expected)+}++unittest reverseEmpty {+  TestSequence original <- TestSequence.create()+  TestSequence expected <- TestSequence.create()++  \ Sorting:reverse<?>(original)+  \ Testing.checkEquals<?>(original,expected)+}++unittest sortList {+  ListBuilder<Int,LinkedNode<Int>> builder <- LinkedNode<Int>.builder()+  Vector<Int> expected <- Vector:create<Int>()+  Int hash <- 269+  Int size <- 137+  $ReadOnly[hash,size]$++  traverse (Counter.zeroIndexed(size) -> Int i) {+    \ builder.append((i*hash)%size/3)+    \ expected.append(i/3)+  }++  scoped {+    optional LinkedNode<Int> head, _ <- builder.build()+  } in optional LinkedNode<Int> actual <- Sorting:sortList<?,?>(head)++  traverse (expected.defaultOrder() -> Int n) {+    \ Testing.checkEquals<?>(require(actual).get(),n)+  } update {+    actual <- require(actual).next()+  }+  \ Testing.checkEquals<?>(present(actual),false)+}++unittest sortListSingle { $DisableCoverage$+  ListBuilder<Int,ForwardNode<Int>> builder <- ForwardNode<Int>.builder()+  Vector<Int> expected <- Vector:create<Int>()+  Int hash <- 269+  Int size <- 137+  $ReadOnly[hash,size]$++  traverse (Counter.zeroIndexed(size) -> Int i) {+    \ builder.append((i*hash)%size/3)+    \ expected.append(i/3)+  }++  scoped {+    optional ForwardNode<Int> head, _ <- builder.build()+  } in optional ForwardNode<Int> actual <- Sorting:sortList<?,?>(head)++  traverse (expected.defaultOrder() -> Int n) {+    \ Testing.checkEquals<?>(require(actual).get(),n)+  } update {+    actual <- require(actual).next()+  }+  \ Testing.checkEquals<?>(present(actual),false)+}++unittest sortListEmpty {+  \ Sorting:sortList<LinkedNode<Int>,Int>(empty)+}++unittest sortListPow2 { $DisableCoverage$+  ListBuilder<Int,LinkedNode<Int>> builder <- LinkedNode<Int>.builder()+  Vector<Int> expected <- Vector:create<Int>()+  Int hash <- 269+  Int size <- 256+  $ReadOnly[hash,size]$++  traverse (Counter.zeroIndexed(size) -> Int i) {+    \ builder.append((i*hash)%size/3)+    \ expected.append(i/3)+  }++  scoped {+    optional LinkedNode<Int> head, _ <- builder.build()+  } in optional LinkedNode<Int> actual <- Sorting:sortList<?,?>(head)++  traverse (expected.defaultOrder() -> Int n) {+    \ Testing.checkEquals<?>(require(actual).get(),n)+  } update {+    actual <- require(actual).next()+  }+  \ Testing.checkEquals<?>(present(actual),false)+}++unittest sortListWith { $DisableCoverage$+  ListBuilder<Int,LinkedNode<Int>> builder <- LinkedNode<Int>.builder()+  Vector<Int> expected <- Vector:create<Int>()+  Int hash <- 269+  Int size <- 137+  $ReadOnly[hash,size]$++  traverse (Counter.revZeroIndexed(size) -> Int i) {+    \ builder.append((i*hash)%size/3)+    \ expected.append(i/3)+  }++  scoped {+    optional LinkedNode<Int> head, _ <- builder.build()+  } in optional LinkedNode<Int> actual <- Sorting:sortListWith<?,?,Reversed<Int>>(head)++  traverse (expected.defaultOrder() -> Int n) {+    \ Testing.checkEquals<?>(require(actual).get(),n)+  } update {+    actual <- require(actual).next()+  }+  \ Testing.checkEquals<?>(present(actual),false)+}++unittest reverseList {+  ListBuilder<Int,LinkedNode<Int>> builder <- LinkedNode<Int>.builder()+  Vector<Int> expected <- Vector:create<Int>()+  Int size <- 101+  $ReadOnly[size]$++  traverse (Counter.zeroIndexed(size) -> Int i) {+    \ expected.append(i)+  }++  traverse (Counter.revZeroIndexed(size) -> Int i) {+    \ builder.append(i)+  }++  scoped {+    optional LinkedNode<Int> head, _ <- builder.build()+  } in optional LinkedNode<Int> reversed <- Sorting:reverseList<?,?>(head)++  traverse (expected.defaultOrder() -> Int value) {+    \ Testing.checkEquals<?>(require(reversed).get(),value)+  } update {+    reversed <- require(reversed).next()+  }+  \ Testing.checkEquals<?>(present(reversed),false)+}++unittest reverseListSingle { $DisableCoverage$+  ListBuilder<Int,ForwardNode<Int>> builder <- ForwardNode<Int>.builder()+  Vector<Int> expected <- Vector:create<Int>()+  Int size <- 101+  $ReadOnly[size]$++  traverse (Counter.zeroIndexed(size) -> Int i) {+    \ expected.append(i)+  }++  traverse (Counter.revZeroIndexed(size) -> Int i) {+    \ builder.append(i)+  }++  scoped {+    optional ForwardNode<Int> head, _ <- builder.build()+  } in optional ForwardNode<Int> reversed <- Sorting:reverseList<?,?>(head)++  traverse (expected.defaultOrder() -> Int value) {+    \ Testing.checkEquals<?>(require(reversed).get(),value)+  } update {+    reversed <- require(reversed).next()+  }+  \ Testing.checkEquals<?>(present(reversed),false)+}++unittest reverseListEmpty {+  \ Sorting:reverseList<LinkedNode<Int>,Int>(empty)+}++concrete TestSequence {+  refines Formatted+  refines ReadAt<Int>+  refines WriteAt<Int>+  refines Append<Int>+  defines Equals<TestSequence>++  @type create () -> (TestSequence)+}++define TestSequence {+  @value Vector<Int> seq++  create () {+    return TestSequence{ Vector:create<Int>() }+  }++  formatted () {+    [Append<Formatted>&Build<String>] builder <- String.builder()+    \ builder.append("{")+    traverse (seq.defaultOrder() -> Formatted x) {+      \ builder.append(" ").append(x)+    }+    return builder.append(" }").build()+  }++  size () {+    return seq.size()+  }++  readAt (i) {+    return seq.readAt(i)+  }++  writeAt (i,x) {+    \ seq.writeAt(i,x)+    return self+  }++  append (x) {+    \ seq.append(x)+    return self+  }++  equals (x,y) {+    return x `ReadAtH:equals<?>` y+  }+}
+ lib/container/test/tree-set.0rt view
@@ -0,0 +1,186 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "TreeSet tests" {+  success+}++unittest addAndRemove {+  TreeSet<Int> set <- TreeSet<Int>.new()+  Int hash <- 13+  Int max  <- 20+  $ReadOnly[hash,max]$++  // Populate the set in a pseudo-random order.+  traverse (Counter.zeroIndexed(max) -> Int i) {+    \ set.add((i*hash)%max)+    \ Testing.checkEquals<?>(set.size(),i+1)+  }++  // Remove only the odd elements.+  traverse (Counter.zeroIndexed(max) -> Int i) {+    if (i%2 == 1) {+      // Remove it twice to ensure idempotence.+      \ set.remove(i).remove(i)+    }+    \ Testing.checkEquals<?>(set.size(),max-(i+1)/2)+  }++  // Validate set membership.+  traverse (Counter.zeroIndexed(max) -> Int i) {+    \ Testing.checkEquals<?>(set.member(i),i%2 == 0)+  }+}++unittest defaultOrder {+  TreeSet<Int> set <- TreeSet<Int>.new()+  Int hash <- 13+  Int max  <- 20+  $ReadOnly[hash,max]$++  // Populate the set in a pseudo-random order.+  traverse (Counter.zeroIndexed(max) -> Int i) {+    \ set.add((i*hash)%max)+  }++  // Validate the traversal order.+  Int index <- 0+  traverse (set.defaultOrder() -> Int entry) {+    \ Testing.checkEquals<?>(entry,index)+    index <- index+1+  }++  \ Testing.checkEquals<?>(index,max)+}++unittest reverseOrder {+  TreeSet<Int> set <- TreeSet<Int>.new()+  Int hash <- 13+  Int max  <- 20+  $ReadOnly[hash,max]$++  // Populate the set in a pseudo-random order.+  traverse (Counter.zeroIndexed(max) -> Int i) {+    \ set.add((i*hash)%max)+  }++  // Validate the traversal order.+  Int index <- max+  traverse (set.reverseOrder() -> Int entry) {+    index <- index-1+    \ Testing.checkEquals<?>(entry,index)+  }++  \ Testing.checkEquals<?>(index,0)+}++unittest getForward {+  TreeSet<Int> set <- TreeSet<Int>.new()+  Int hash <- 13+  Int max  <- 20+  $ReadOnly[hash,max]$++  // Populate the set in a pseudo-random order.+  traverse (Counter.zeroIndexed(max) -> Int i) {+    \ set.add((i*hash)%max)+  }++  // Validate the traversal order from every starting point.+  traverse (Counter.zeroIndexed(max) -> Int i) {+    Int index <- i+    traverse (set.getForward(index) -> Int entry) {+      \ Testing.checkEquals<?>(entry,index)+      index <- index+1+    }+    \ Testing.checkEquals<?>(index,max)+  }+}++unittest getForwardNotFound {+  TreeSet<Int> set <- TreeSet<Int>.new()+  \ set.add(1).add(3).add(5)++  scoped {+    optional Order<Int> start <- set.getForward(4)+  } in if (!present(start)) {+    fail("Failed")+  } else {+    \ Testing.checkEquals<?>(require(start).get(),5)+  }++  scoped {+    optional Order<Int> start <- set.getForward(6)+  } in if (present(start)) {+    fail("Failed")+  }+}++unittest getReverse {+  TreeSet<Int> set <- TreeSet<Int>.new()+  Int hash <- 13+  Int max  <- 20+  $ReadOnly[hash,max]$++  // Populate the set in a pseudo-random order.+  traverse (Counter.zeroIndexed(max) -> Int i) {+    \ set.add((i*hash)%max)+  }++  // Validate the traversal order from every starting point.+  traverse (Counter.zeroIndexed(max) -> Int i) {+    Int index <- i+    traverse (set.getReverse(index) -> Int entry) {+      \ Testing.checkEquals<?>(entry,index)+      index <- index-1+    }+    \ Testing.checkEquals<?>(index,-1)+  }+}++unittest getReverseNotFound {+  TreeSet<Int> set <- TreeSet<Int>.new()+  \ set.add(1).add(3).add(5)++  scoped {+    optional Order<Int> start <- set.getReverse(2)+  } in if (!present(start)) {+    fail("Failed")+  } else {+    \ Testing.checkEquals<?>(require(start).get(),1)+  }++  scoped {+    optional Order<Int> start <- set.getReverse(0)+  } in if (present(start)) {+    fail("Failed")+  }+}++unittest duplicate {+  TreeSet<Int> set <- TreeSet<Int>.new().add(1).add(2)++  TreeSet<Int> copy <- set.duplicate()+  \ Testing.checkEquals<?>(copy.size(),2)+  \ Testing.checkEquals<?>(copy.member(1),true)+  \ Testing.checkEquals<?>(copy.member(1),true)++  \ copy.remove(2)+  \ Testing.checkEquals<?>(copy.size(),1)+  \ Testing.checkEquals<?>(copy.member(1),true)+  \ Testing.checkEquals<?>(set.member(2),true)+}
+ lib/container/test/type-map.0rt view
@@ -0,0 +1,95 @@+/* -----------------------------------------------------------------------------+Copyright 2019-2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "TypeMap tests" {+  success+}++unittest basicOperations {+  TypeMap tree <- TypeMap.new()++  TypeKey<Int>    keyInt    <- TypeKey<Int>.new()+  TypeKey<String> keyString <- TypeKey<String>.new()+  TypeKey<Value>  keyValue  <- TypeKey<Value>.new()++  \ Testing.checkOptional<?>(tree.get<?>(keyInt),   empty)+  \ Testing.checkOptional<?>(tree.get<?>(keyString),empty)+  \ Testing.checkOptional<?>(tree.get<?>(keyValue), empty)++  \ tree.set<?>(keyInt,1)+  \ tree.set<?>(keyString,"a")+  \ tree.set<?>(keyValue,Value.new())++  \ Testing.checkOptional<?>(tree.get<?>(keyInt),   1)+  \ Testing.checkOptional<?>(tree.get<?>(keyString),"a")+  \ Testing.checkOptional<?>(tree.get<?>(keyValue), Value.new())++  \ tree.remove(keyString)++  \ Testing.checkOptional<?>(tree.get<?>(keyString),empty)+}++unittest wrongReturnType {+  TypeMap tree <- TypeMap.new()++  TypeKey<String> keyString <- TypeKey<String>.new()++  // Added as Formatted, which means it cannot be retrieved as String later.+  \ tree.set<Formatted>(keyString,"a")+  \ Testing.checkOptional<?>(tree.get<String>(keyString),empty)++  \ tree.set<String>(keyString,"a")+  \ Testing.checkOptional<?>(tree.get<String>(keyString),"a")+}++unittest duplicate {+  TypeKey<Int>    keyInt    <- TypeKey<Int>.new()+  TypeKey<String> keyString <- TypeKey<String>.new()++  TypeMap tree <- TypeMap.new().set<?>(keyInt,1).set<?>(keyString,"a")++  TypeMap copy <- tree.duplicate()+  \ Testing.checkOptional<?>(copy.get<?>(keyInt),1)+  \ Testing.checkOptional<?>(copy.get<?>(keyString),"a")++  \ copy.remove(keyString)+  \ Testing.checkOptional<?>(copy.get<?>(keyInt),1)+  \ Testing.checkOptional<?>(copy.get<?>(keyString),empty)+  \ Testing.checkOptional<?>(tree.get<?>(keyString),"a")+}++concrete Value {+  refines Formatted+  defines Equals<Value>++  @type new () -> (Value)+}++define Value {+  new () {+    return Value{ }+  }++  formatted () {+    return "Value"+  }++  equals (_,_) {+    return true+  }+}
+ lib/container/test/vector.0rt view
@@ -0,0 +1,226 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "Vector tests" {+  success+}++unittest createSize {+  Vector<Int> values <- Vector:createSize<Int>(10)+  \ Testing.checkEquals<?>(values.size(),10)++  scoped {+    Int i <- 0+  } in while (i < values.size()) {+    \ Testing.checkEquals<?>(values.readAt(i),Int.default())+  } update {+    i <- i+1+  }+}++unittest copyFrom {+  String original <- "abcde"+  Vector<Char> values <- Vector:copyFrom<?>(original)+  \ Testing.checkEquals<?>(values.size(),original.size())++  scoped {+    Int i <- 0+  } in while (i < original.size()) {+    \ Testing.checkEquals<?>(values.readAt(i),original.readAt(i))+  } update {+    i <- i+1+  }+}++unittest append {+  Vector<Int> values <- Vector:create<Int>()++  scoped {+    Int i <- 0+  } in while (i < 10) {+    \ values.append(i)+    \ Testing.checkEquals<?>(values.size(),i+1)+  } update {+    i <- i+1+  }+  \ Testing.checkEquals<?>(values.size(),10)++  scoped {+    Int i <- 0+  } in while (i < values.size()) {+    \ Testing.checkEquals<?>(values.readAt(i),i)+  } update {+    i <- i+1+  }+}++unittest pushAndPop {+  Vector<Int> values <- Vector:create<Int>()++  scoped {+    Int i <- 0+  } in while (i < 10) {+    \ values.push(i)+    \ Testing.checkEquals<?>(values.size(),i+1)+  } update {+    i <- i+1+  }+  \ Testing.checkEquals<?>(values.size(),10)++  scoped {+    Int i <- 0+  } in while (i < 10) {+    \ Testing.checkEquals<?>(values.pop(),10-i-1)+    \ Testing.checkEquals<?>(values.size(),10-i-1)+  } update {+    i <- i+1+  }+  \ Testing.checkEquals<?>(values.size(),0)+}++unittest writeAt {+  Vector<Int> values <- Vector:createSize<Int>(10)++  scoped {+    Int i <- 0+  } in while (i < values.size()) {+    \ values.writeAt(i,2*i)+  } update {+    i <- i+1+  }++  scoped {+    Int i <- 0+  } in while (i < values.size()) {+    \ Testing.checkEquals<?>(values.readAt(i),2*i)+  } update {+    i <- i+1+  }+}++unittest traverseVector {+  Int index <- 0+  Vector<Int> vector <- Vector:create<Int>()+  \ vector.push(2)+  \ vector.push(6)+  \ vector.push(3)+  \ vector.push(5)+  \ vector.push(4)+  \ vector.push(1)++  traverse (vector.defaultOrder() -> Int i) {+    \ Testing.checkEquals<?>(i,vector.readAt(index))+    index <- index+1+  }++  \ Testing.checkEquals<?>(index,6)+}++unittest duplicate {+  Vector<Int> vector <- Vector:create<Int>().push(1).push(2)++  Vector<Int> copy <- vector.duplicate()+  \ Testing.checkEquals<?>(copy.size(),2)+  \ Testing.checkEquals<?>(copy.readAt(0),1)+  \ Testing.checkEquals<?>(copy.readAt(1),2)++  \ copy.pop()+  \ Testing.checkEquals<?>(copy.size(),1)+  \ Testing.checkEquals<?>(copy.readAt(0),1)+  \ Testing.checkEquals<?>(vector.readAt(1),2)+}+++testcase "distinct default values in pre-sized Vector" {+  success+}++unittest test {+  Vector<Type> values <- Vector:createSize<Type>(10)+  \ values.readAt(3).set(7)++  \ Testing.checkEquals<?>(values.readAt(3),Type.create(7))++  scoped {+    Int i <- 0+  } in while (i < values.size()) {+    if (i != 3) {+      \ Testing.checkEquals<?>(values.readAt(i),Type.create(0))+    }+  } update {+    i <- i+1+  }+}++concrete Type {+  refines Formatted+  defines Default+  defines Equals<Type>++  @type create (Int) -> (Type)+  @value set (Int) -> ()+}++define Type {+  @value Int value++  default () {+    return create(0)+  }++  create (v) {+    return Type{ v }+  }++  formatted () {+    return value.formatted()+  }++  equals (x,y) {+    return x.get() == y.get()+  }++  set (v) {+    value <- v+  }++  @value get () -> (Int)+  get () {+    return value+  }+}+++testcase "negative Vector index" {+  crash+}++unittest test {+  Vector<Int> values <- Vector:create<Int>()+  \ values.readAt(-1)+}+++testcase "Vector index out of bounds" {+  crash+}++unittest test {+  Vector<Int> values <- Vector:create<Int>()+  \ values.readAt(1)+}
lib/container/tree-set.0rp view
@@ -22,6 +22,7 @@   refines Duplicate   refines SetReader<#k>   refines SetWriter<#k>+  #k immutable   #k defines LessThan<#k>    // Create a new set.
− lib/container/tree-set.0rt
@@ -1,186 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "TreeSet tests" {-  success-}--unittest addAndRemove {-  TreeSet<Int> set <- TreeSet<Int>.new()-  Int hash <- 13-  Int max  <- 20-  $ReadOnly[hash,max]$--  // Populate the set in a pseudo-random order.-  traverse (Counter.zeroIndexed(max) -> Int i) {-    \ set.add((i*hash)%max)-    \ Testing.checkEquals<?>(set.size(),i+1)-  }--  // Remove only the odd elements.-  traverse (Counter.zeroIndexed(max) -> Int i) {-    if (i%2 == 1) {-      // Remove it twice to ensure idempotence.-      \ set.remove(i).remove(i)-    }-    \ Testing.checkEquals<?>(set.size(),max-(i+1)/2)-  }--  // Validate set membership.-  traverse (Counter.zeroIndexed(max) -> Int i) {-    \ Testing.checkEquals<?>(set.member(i),i%2 == 0)-  }-}--unittest defaultOrder {-  TreeSet<Int> set <- TreeSet<Int>.new()-  Int hash <- 13-  Int max  <- 20-  $ReadOnly[hash,max]$--  // Populate the set in a pseudo-random order.-  traverse (Counter.zeroIndexed(max) -> Int i) {-    \ set.add((i*hash)%max)-  }--  // Validate the traversal order.-  Int index <- 0-  traverse (set.defaultOrder() -> Int entry) {-    \ Testing.checkEquals<?>(entry,index)-    index <- index+1-  }--  \ Testing.checkEquals<?>(index,max)-}--unittest reverseOrder {-  TreeSet<Int> set <- TreeSet<Int>.new()-  Int hash <- 13-  Int max  <- 20-  $ReadOnly[hash,max]$--  // Populate the set in a pseudo-random order.-  traverse (Counter.zeroIndexed(max) -> Int i) {-    \ set.add((i*hash)%max)-  }--  // Validate the traversal order.-  Int index <- max-  traverse (set.reverseOrder() -> Int entry) {-    index <- index-1-    \ Testing.checkEquals<?>(entry,index)-  }--  \ Testing.checkEquals<?>(index,0)-}--unittest getForward {-  TreeSet<Int> set <- TreeSet<Int>.new()-  Int hash <- 13-  Int max  <- 20-  $ReadOnly[hash,max]$--  // Populate the set in a pseudo-random order.-  traverse (Counter.zeroIndexed(max) -> Int i) {-    \ set.add((i*hash)%max)-  }--  // Validate the traversal order from every starting point.-  traverse (Counter.zeroIndexed(max) -> Int i) {-    Int index <- i-    traverse (set.getForward(index) -> Int entry) {-      \ Testing.checkEquals<?>(entry,index)-      index <- index+1-    }-    \ Testing.checkEquals<?>(index,max)-  }-}--unittest getForwardNotFound {-  TreeSet<Int> set <- TreeSet<Int>.new()-  \ set.add(1).add(3).add(5)--  scoped {-    optional Order<Int> start <- set.getForward(4)-  } in if (!present(start)) {-    fail("Failed")-  } else {-    \ Testing.checkEquals<?>(require(start).get(),5)-  }--  scoped {-    optional Order<Int> start <- set.getForward(6)-  } in if (present(start)) {-    fail("Failed")-  }-}--unittest getReverse {-  TreeSet<Int> set <- TreeSet<Int>.new()-  Int hash <- 13-  Int max  <- 20-  $ReadOnly[hash,max]$--  // Populate the set in a pseudo-random order.-  traverse (Counter.zeroIndexed(max) -> Int i) {-    \ set.add((i*hash)%max)-  }--  // Validate the traversal order from every starting point.-  traverse (Counter.zeroIndexed(max) -> Int i) {-    Int index <- i-    traverse (set.getReverse(index) -> Int entry) {-      \ Testing.checkEquals<?>(entry,index)-      index <- index-1-    }-    \ Testing.checkEquals<?>(index,-1)-  }-}--unittest getReverseNotFound {-  TreeSet<Int> set <- TreeSet<Int>.new()-  \ set.add(1).add(3).add(5)--  scoped {-    optional Order<Int> start <- set.getReverse(2)-  } in if (!present(start)) {-    fail("Failed")-  } else {-    \ Testing.checkEquals<?>(require(start).get(),1)-  }--  scoped {-    optional Order<Int> start <- set.getReverse(0)-  } in if (present(start)) {-    fail("Failed")-  }-}--unittest duplicate {-  TreeSet<Int> set <- TreeSet<Int>.new().add(1).add(2)--  TreeSet<Int> copy <- set.duplicate()-  \ Testing.checkEquals<?>(copy.size(),2)-  \ Testing.checkEquals<?>(copy.member(1),true)-  \ Testing.checkEquals<?>(copy.member(1),true)--  \ copy.remove(2)-  \ Testing.checkEquals<?>(copy.size(),1)-  \ Testing.checkEquals<?>(copy.member(1),true)-  \ Testing.checkEquals<?>(set.member(2),true)-}
− lib/container/tree-set.0rx
@@ -1,96 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--define TreeSet {-  @value SearchTree<#k,any> tree--  new () {-    return #self{ SearchTree<#k,any>.new() }-  }--  size () {-    return tree.size()-  }--  duplicate () {-    return #self{ tree.duplicate() }-  }--  add (k) {-    \ tree.set(k,Void.void())-    return self-  }--  remove (k) {-    \ tree.remove(k)-    return self-  }--  member (k) {-    return present(tree.get(k))-  }--  defaultOrder () {-    return TreeSetOrder<#k>.wrap(tree.defaultOrder())-  }--  reverseOrder () {-    return TreeSetOrder<#k>.wrap(tree.reverseOrder())-  }--  getForward (k) {-    return TreeSetOrder<#k>.wrap(tree.getForward(k))-  }--  getReverse (k) {-    return TreeSetOrder<#k>.wrap(tree.getReverse(k))-  }-}--concrete TreeSetOrder<#k> {-  refines Order<#k>--  @type wrap (optional Order<KeyValue<#k,any>>) -> (optional TreeSetOrder<#k>)-}--define TreeSetOrder {-  @value Order<KeyValue<#k,any>> order--  wrap (order) {-    if (!present(order)) {-      return empty-    } else {-      return #self{ require(order) }-    }-  }--  next () {-    scoped {-      optional Order<KeyValue<#k,any>> order2 <- order.next()-    } in if (present(order2)) {-      order <- require(order2)-      return self-    } else {-      return empty-    }-  }--  get () {-    return order.get().getKey()-  }-}
lib/container/type-map.0rp view
@@ -48,6 +48,8 @@  // A typed key for use with TypeMap. concrete TypeKey<|#x> {+  immutable+   refines Formatted   defines LessThan<TypeKey<any>>   defines Equals<TypeKey<any>>
− lib/container/type-map.0rt
@@ -1,95 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "TypeMap tests" {-  success-}--unittest basicOperations {-  TypeMap tree <- TypeMap.new()--  TypeKey<Int>    keyInt    <- TypeKey<Int>.new()-  TypeKey<String> keyString <- TypeKey<String>.new()-  TypeKey<Value>  keyValue  <- TypeKey<Value>.new()--  \ Testing.checkOptional<?>(tree.get<?>(keyInt),   empty)-  \ Testing.checkOptional<?>(tree.get<?>(keyString),empty)-  \ Testing.checkOptional<?>(tree.get<?>(keyValue), empty)--  \ tree.set<?>(keyInt,1)-  \ tree.set<?>(keyString,"a")-  \ tree.set<?>(keyValue,Value.new())--  \ Testing.checkOptional<?>(tree.get<?>(keyInt),   1)-  \ Testing.checkOptional<?>(tree.get<?>(keyString),"a")-  \ Testing.checkOptional<?>(tree.get<?>(keyValue), Value.new())--  \ tree.remove(keyString)--  \ Testing.checkOptional<?>(tree.get<?>(keyString),empty)-}--unittest wrongReturnType {-  TypeMap tree <- TypeMap.new()--  TypeKey<String> keyString <- TypeKey<String>.new()--  // Added as Formatted, which means it cannot be retrieved as String later.-  \ tree.set<Formatted>(keyString,"a")-  \ Testing.checkOptional<?>(tree.get<String>(keyString),empty)--  \ tree.set<String>(keyString,"a")-  \ Testing.checkOptional<?>(tree.get<String>(keyString),"a")-}--unittest duplicate {-  TypeKey<Int>    keyInt    <- TypeKey<Int>.new()-  TypeKey<String> keyString <- TypeKey<String>.new()--  TypeMap tree <- TypeMap.new().set<?>(keyInt,1).set<?>(keyString,"a")--  TypeMap copy <- tree.duplicate()-  \ Testing.checkOptional<?>(copy.get<?>(keyInt),1)-  \ Testing.checkOptional<?>(copy.get<?>(keyString),"a")--  \ copy.remove(keyString)-  \ Testing.checkOptional<?>(copy.get<?>(keyInt),1)-  \ Testing.checkOptional<?>(copy.get<?>(keyString),empty)-  \ Testing.checkOptional<?>(tree.get<?>(keyString),"a")-}--concrete Value {-  refines Formatted-  defines Equals<Value>--  @type new () -> (Value)-}--define Value {-  new () {-    return Value{ }-  }--  formatted () {-    return "Value"-  }--  equals (_,_) {-    return true-  }-}
− lib/container/type-map.0rx
@@ -1,106 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--define TypeMap {-  @value SearchTree<TypeKey<any>,GenericValue<any>> tree--  new () {-    return TypeMap{ SearchTree<TypeKey<any>,GenericValue<any>>.new() }-  }--  duplicate () {-    return #self{ tree.duplicate() }-  }--  set (k,v) {-    \ tree.set(k,GenericValue:create<?>(v))-    return self-  }--  remove (k) {-    \ tree.remove(k)-    return self-  }--  get (k) {-    scoped {-      optional GenericValue<any> value <- tree.get(k)-    } in if (present(value)) {-      return require(value).check<#x>()-    } else {-      return empty-    }-  }-}--define TypeKey {-  $ReadOnly[counterMutex,index]$--  @category Mutex counterMutex <- SpinlockMutex.new()-  @category Int   counter      <- 0-  @value Int index--  new () {-    scoped {-      \ counterMutex.lock()-    } cleanup {-      \ counterMutex.unlock()-    } in return TypeKey<#x>{ (counter <- counter+1) }-  }--  formatted () {-    return String.builder()-        .append(typename<#self>().formatted())-        .append("{")-        .append(index.formatted())-        .append("}")-        .build()-  }--  lessThan (l,r) {-    return l.get() < r.get()-  }--  equals (l,r) {-    return l.get() == r.get()-  }--  @value get () -> (Int)-  get () {-    return index-  }-}--concrete GenericValue<|#x> {-  @category create<#x> (#x) -> (GenericValue<#x>)-  @value check<#y> () -> (optional #y)-}--define GenericValue {-  $ReadOnly[value]$--  @value #x value--  create (v) {-    return GenericValue<#x>{ v }-  }--  check () {-    return reduce<#x,#y>(value)-  }-}
− lib/container/validated-tree.0rp
@@ -1,34 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--$ModuleOnly$--// A validated binary search tree for key-value storage.-//-// Notes:-// - This is the same as SearchTree, but it validates the tree structure every-//   time there is a modification, which is extremely inefficient. This should-//   therefore only be used in tests.-concrete ValidatedTree<#k|#v|> {-  refines Container-  refines KVWriter<#k,#v>-  refines KVReader<#k,#v>-  #k defines LessThan<#k>--  @type new () -> (#self)-}
− lib/container/vector.0rt
@@ -1,226 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "Vector tests" {-  success-}--unittest createSize {-  Vector<Int> values <- Vector:createSize<Int>(10)-  \ Testing.checkEquals<?>(values.size(),10)--  scoped {-    Int i <- 0-  } in while (i < values.size()) {-    \ Testing.checkEquals<?>(values.readAt(i),Int.default())-  } update {-    i <- i+1-  }-}--unittest copyFrom {-  String original <- "abcde"-  Vector<Char> values <- Vector:copyFrom<?>(original)-  \ Testing.checkEquals<?>(values.size(),original.size())--  scoped {-    Int i <- 0-  } in while (i < original.size()) {-    \ Testing.checkEquals<?>(values.readAt(i),original.readAt(i))-  } update {-    i <- i+1-  }-}--unittest append {-  Vector<Int> values <- Vector:create<Int>()--  scoped {-    Int i <- 0-  } in while (i < 10) {-    \ values.append(i)-    \ Testing.checkEquals<?>(values.size(),i+1)-  } update {-    i <- i+1-  }-  \ Testing.checkEquals<?>(values.size(),10)--  scoped {-    Int i <- 0-  } in while (i < values.size()) {-    \ Testing.checkEquals<?>(values.readAt(i),i)-  } update {-    i <- i+1-  }-}--unittest pushAndPop {-  Vector<Int> values <- Vector:create<Int>()--  scoped {-    Int i <- 0-  } in while (i < 10) {-    \ values.push(i)-    \ Testing.checkEquals<?>(values.size(),i+1)-  } update {-    i <- i+1-  }-  \ Testing.checkEquals<?>(values.size(),10)--  scoped {-    Int i <- 0-  } in while (i < 10) {-    \ Testing.checkEquals<?>(values.pop(),10-i-1)-    \ Testing.checkEquals<?>(values.size(),10-i-1)-  } update {-    i <- i+1-  }-  \ Testing.checkEquals<?>(values.size(),0)-}--unittest writeAt {-  Vector<Int> values <- Vector:createSize<Int>(10)--  scoped {-    Int i <- 0-  } in while (i < values.size()) {-    \ values.writeAt(i,2*i)-  } update {-    i <- i+1-  }--  scoped {-    Int i <- 0-  } in while (i < values.size()) {-    \ Testing.checkEquals<?>(values.readAt(i),2*i)-  } update {-    i <- i+1-  }-}--unittest traverseVector {-  Int index <- 0-  Vector<Int> vector <- Vector:create<Int>()-  \ vector.push(2)-  \ vector.push(6)-  \ vector.push(3)-  \ vector.push(5)-  \ vector.push(4)-  \ vector.push(1)--  traverse (vector.defaultOrder() -> Int i) {-    \ Testing.checkEquals<?>(i,vector.readAt(index))-    index <- index+1-  }--  \ Testing.checkEquals<?>(index,6)-}--unittest duplicate {-  Vector<Int> vector <- Vector:create<Int>().push(1).push(2)--  Vector<Int> copy <- vector.duplicate()-  \ Testing.checkEquals<?>(copy.size(),2)-  \ Testing.checkEquals<?>(copy.readAt(0),1)-  \ Testing.checkEquals<?>(copy.readAt(1),2)--  \ copy.pop()-  \ Testing.checkEquals<?>(copy.size(),1)-  \ Testing.checkEquals<?>(copy.readAt(0),1)-  \ Testing.checkEquals<?>(vector.readAt(1),2)-}---testcase "distinct default values in pre-sized Vector" {-  success-}--unittest test {-  Vector<Type> values <- Vector:createSize<Type>(10)-  \ values.readAt(3).set(7)--  \ Testing.checkEquals<?>(values.readAt(3),Type.create(7))--  scoped {-    Int i <- 0-  } in while (i < values.size()) {-    if (i != 3) {-      \ Testing.checkEquals<?>(values.readAt(i),Type.create(0))-    }-  } update {-    i <- i+1-  }-}--concrete Type {-  refines Formatted-  defines Default-  defines Equals<Type>--  @type create (Int) -> (Type)-  @value set (Int) -> ()-}--define Type {-  @value Int value--  default () {-    return create(0)-  }--  create (v) {-    return Type{ v }-  }--  formatted () {-    return value.formatted()-  }--  equals (x,y) {-    return x.get() == y.get()-  }--  set (v) {-    value <- v-  }--  @value get () -> (Int)-  get () {-    return value-  }-}---testcase "negative Vector index" {-  crash-}--unittest test {-  Vector<Int> values <- Vector:create<Int>()-  \ values.readAt(-1)-}---testcase "Vector index out of bounds" {-  crash-}--unittest test {-  Vector<Int> values <- Vector:create<Int>()-  \ values.readAt(1)-}
lib/file/.zeolite-module view
@@ -1,5 +1,9 @@ root: "../.." path: "lib/file"+extra_paths: [+  "lib/file/src"+  "lib/file/test"+] public_deps: [   "lib/util" ]
− lib/file/helpers.0rx
@@ -1,29 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2020 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--$TestsOnly$--define FileTesting {-  forceReadFile (file) {-    scoped {-      RawFileReader reader <- RawFileReader.open(file)-    } cleanup {-      \ reader.freeResource()-    } in return TextReader.readAll(reader)-  }-}
lib/file/src/Extension_RawFileReader.cpp view
@@ -25,7 +25,7 @@ namespace ZEOLITE_PUBLIC_NAMESPACE { #endif  // ZEOLITE_PUBLIC_NAMESPACE -BoxedValue CreateValue_RawFileReader(S<Type_RawFileReader> parent, const ValueTuple& args);+BoxedValue CreateValue_RawFileReader(S<const Type_RawFileReader> parent, const ValueTuple& args);  struct ExtCategory_RawFileReader : public Category_RawFileReader { };@@ -33,15 +33,15 @@ struct ExtType_RawFileReader : public Type_RawFileReader {   inline ExtType_RawFileReader(Category_RawFileReader& p, Params<0>::Type params) : Type_RawFileReader(p, params) {} -  ReturnTuple Call_open(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_open(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("RawFileReader.open")-    return ReturnTuple(CreateValue_RawFileReader(shared_from_this(), args));+    return ReturnTuple(CreateValue_RawFileReader(PARAM_SELF, args));   } };  struct ExtValue_RawFileReader : public Value_RawFileReader {-  inline ExtValue_RawFileReader(S<Type_RawFileReader> p, const ValueTuple& args)-    : Value_RawFileReader(p),+  inline ExtValue_RawFileReader(S<const Type_RawFileReader> p, const ValueTuple& args)+    : Value_RawFileReader(std::move(p)),       filename(args.At(0).AsString()),       file(new std::ifstream(filename, std::ios::in | std::ios::binary)) {} @@ -115,13 +115,15 @@   return category; } -S<Type_RawFileReader> CreateType_RawFileReader(Params<0>::Type params) {+S<const Type_RawFileReader> CreateType_RawFileReader(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_RawFileReader(CreateCategory_RawFileReader(), Params<0>::Type()));   return cached; } -BoxedValue CreateValue_RawFileReader(S<Type_RawFileReader> parent, const ValueTuple& args) {-  return BoxedValue::New<ExtValue_RawFileReader>(parent, args);+void RemoveType_RawFileReader(const Params<0>::Type& params) {}++BoxedValue CreateValue_RawFileReader(S<const Type_RawFileReader> parent, const ValueTuple& args) {+  return BoxedValue::New<ExtValue_RawFileReader>(std::move(parent), args); }  #ifdef ZEOLITE_PUBLIC_NAMESPACE
lib/file/src/Extension_RawFileWriter.cpp view
@@ -25,7 +25,7 @@ namespace ZEOLITE_PUBLIC_NAMESPACE { #endif  // ZEOLITE_PUBLIC_NAMESPACE -BoxedValue CreateValue_RawFileWriter(S<Type_RawFileWriter> parent, const ValueTuple& args);+BoxedValue CreateValue_RawFileWriter(S<const Type_RawFileWriter> parent, const ValueTuple& args);  struct ExtCategory_RawFileWriter : public Category_RawFileWriter { };@@ -33,15 +33,15 @@ struct ExtType_RawFileWriter : public Type_RawFileWriter {   inline ExtType_RawFileWriter(Category_RawFileWriter& p, Params<0>::Type params) : Type_RawFileWriter(p, params) {} -  ReturnTuple Call_open(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_open(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("RawFileWriter.open")-    return ReturnTuple(CreateValue_RawFileWriter(shared_from_this(), args));+    return ReturnTuple(CreateValue_RawFileWriter(PARAM_SELF, args));   } };  struct ExtValue_RawFileWriter : public Value_RawFileWriter {-  inline ExtValue_RawFileWriter(S<Type_RawFileWriter> p, const ValueTuple& args)-    : Value_RawFileWriter(p),+  inline ExtValue_RawFileWriter(S<const Type_RawFileWriter> p, const ValueTuple& args)+    : Value_RawFileWriter(std::move(p)),       filename(args.At(0).AsString()),       file(new std::ofstream(filename, std::ios::out | std::ios::binary | std::ios::trunc | std::ios::ate)) {} @@ -96,12 +96,16 @@   static auto& category = *new ExtCategory_RawFileWriter();   return category; }-S<Type_RawFileWriter> CreateType_RawFileWriter(Params<0>::Type params) {++S<const Type_RawFileWriter> CreateType_RawFileWriter(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_RawFileWriter(CreateCategory_RawFileWriter(), Params<0>::Type()));   return cached; }-BoxedValue CreateValue_RawFileWriter(S<Type_RawFileWriter> parent, const ValueTuple& args) {-  return BoxedValue::New<ExtValue_RawFileWriter>(parent, args);++void RemoveType_RawFileWriter(const Params<0>::Type& params) {}++BoxedValue CreateValue_RawFileWriter(S<const Type_RawFileWriter> parent, const ValueTuple& args) {+  return BoxedValue::New<ExtValue_RawFileWriter>(std::move(parent), args); }  #ifdef ZEOLITE_PUBLIC_NAMESPACE
+ lib/file/src/helpers.0rx view
@@ -0,0 +1,29 @@+/* -----------------------------------------------------------------------------+Copyright 2020 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++$TestsOnly$++define FileTesting {+  forceReadFile (file) {+    scoped {+      RawFileReader reader <- RawFileReader.open(file)+    } cleanup {+      \ reader.freeResource()+    } in return TextReader.readAll(reader)+  }+}
+ lib/file/test/tests.0rt view
@@ -0,0 +1,124 @@+/* -----------------------------------------------------------------------------+Copyright 2020 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "read and write" {+  success+}++unittest readWrite {+  String data <- "this is some\x00data"++  scoped {+    RawFileWriter writer <- RawFileWriter.open("testfile")+  } cleanup {+    \ writer.freeResource()+  } in {+    \ Testing.checkEmpty(writer.getFileError())+    Int writeSize <- writer.writeBlock(data)+    if (writeSize != data.size()) {+      fail(writeSize)+    }+    \ Testing.checkEmpty(writer.getFileError())+  }++  scoped {+    RawFileReader reader <- RawFileReader.open("testfile")+  } cleanup {+    \ reader.freeResource()+  } in {+    \ Testing.checkEmpty(reader.getFileError())++    scoped {+      String data2 <- reader.readBlock(8)+    } in if (data2 != "this is ") {+      fail("\"" + data2 + "\"")+    }+    scoped {+      String data2 <- reader.readBlock(20)+    } in if (data2 != "some\x00data") {+      fail("\"" + data2 + "\"")+    }+    \ Testing.checkEmpty(reader.getFileError())++    if (!reader.pastEnd()) {+      fail("more data in file")+    }+  }+}++unittest forceRead {+  String data <- "test data"++  scoped {+    RawFileWriter writer <- RawFileWriter.open("testfile")+  } cleanup {+    \ writer.freeResource()+  } in \ writer.writeBlock(data)++  \ Testing.checkEquals<?>(FileTesting.forceReadFile("testfile"),data)+}++unittest readEmpty {+  \ RawFileWriter.open("testfile").freeResource()+  RawFileReader reader <- RawFileReader.open("testfile")++  String data <- reader.readBlock(10)+  if (data != "") {+    fail("\"" + data + "\"")+  }+  \ Testing.checkEmpty(reader.getFileError())+  if (!reader.pastEnd()) {+    fail("more data in file")+  }+}++unittest readMissing {+  RawFileReader reader <- RawFileReader.open("do-not-create-this-file")+  \ Testing.checkPresent(reader.getFileError())+}++unittest unwritable {+  RawFileWriter writer <- RawFileWriter.open("testfile")+  \ writer.freeResource()+  \ Testing.checkPresent(writer.getFileError())+}+++testcase "read missing file crash" {+  crash+  require "do-not-create-this-file"+  require "RawFileReader .*creation"+}++unittest test {+  RawFileReader reader <- RawFileReader.open("do-not-create-this-file")+  \ reader.readBlock(0)+}+++testcase "unwritable file crash" {+  crash+  require "testfile"+  require "RawFileWriter .*creation"+}++unittest test {+  RawFileWriter writer <- RawFileWriter.open("testfile")+  \ writer.freeResource()+  \ writer.writeBlock("")+}
− lib/file/tests.0rt
@@ -1,124 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2020 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "read and write" {-  success-}--unittest readWrite {-  String data <- "this is some\x00data"--  scoped {-    RawFileWriter writer <- RawFileWriter.open("testfile")-  } cleanup {-    \ writer.freeResource()-  } in {-    \ Testing.checkEmpty(writer.getFileError())-    Int writeSize <- writer.writeBlock(data)-    if (writeSize != data.size()) {-      fail(writeSize)-    }-    \ Testing.checkEmpty(writer.getFileError())-  }--  scoped {-    RawFileReader reader <- RawFileReader.open("testfile")-  } cleanup {-    \ reader.freeResource()-  } in {-    \ Testing.checkEmpty(reader.getFileError())--    scoped {-      String data2 <- reader.readBlock(8)-    } in if (data2 != "this is ") {-      fail("\"" + data2 + "\"")-    }-    scoped {-      String data2 <- reader.readBlock(20)-    } in if (data2 != "some\x00data") {-      fail("\"" + data2 + "\"")-    }-    \ Testing.checkEmpty(reader.getFileError())--    if (!reader.pastEnd()) {-      fail("more data in file")-    }-  }-}--unittest forceRead {-  String data <- "test data"--  scoped {-    RawFileWriter writer <- RawFileWriter.open("testfile")-  } cleanup {-    \ writer.freeResource()-  } in \ writer.writeBlock(data)--  \ Testing.checkEquals<?>(FileTesting.forceReadFile("testfile"),data)-}--unittest readEmpty {-  \ RawFileWriter.open("testfile").freeResource()-  RawFileReader reader <- RawFileReader.open("testfile")--  String data <- reader.readBlock(10)-  if (data != "") {-    fail("\"" + data + "\"")-  }-  \ Testing.checkEmpty(reader.getFileError())-  if (!reader.pastEnd()) {-    fail("more data in file")-  }-}--unittest readMissing {-  RawFileReader reader <- RawFileReader.open("do-not-create-this-file")-  \ Testing.checkPresent(reader.getFileError())-}--unittest unwritable {-  RawFileWriter writer <- RawFileWriter.open("testfile")-  \ writer.freeResource()-  \ Testing.checkPresent(writer.getFileError())-}---testcase "read missing file crash" {-  crash-  require "do-not-create-this-file"-  require "RawFileReader .*creation"-}--unittest test {-  RawFileReader reader <- RawFileReader.open("do-not-create-this-file")-  \ reader.readBlock(0)-}---testcase "unwritable file crash" {-  crash-  require "testfile"-  require "RawFileWriter .*creation"-}--unittest test {-  RawFileWriter writer <- RawFileWriter.open("testfile")-  \ writer.freeResource()-  \ writer.writeBlock("")-}
lib/math/.zeolite-module view
@@ -1,5 +1,9 @@ root: "../.." path: "lib/math"+extra_paths: [+  "lib/math/src"+  "lib/math/test"+] public_deps: [   "lib/util" ]
lib/math/categorical-tree.0rp view
@@ -33,6 +33,7 @@ concrete CategoricalTree<#c> {   refines ReadAt<#c>   refines Duplicate+  #c immutable   #c defines LessThan<#c>    // Create a new distribution.
− lib/math/categorical-tree.0rt
@@ -1,199 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "CategoricalTree tests" {-  success-}--unittest simpleInsertion {-  CategoricalTree<Int> tree <- CategoricalTree<Int>.new()-      .setWeight(1,3)-      .setWeight(6,2)-      .setWeight(3,1)-      .setWeight(5,4)-      .setWeight(4,3)-      .setWeight(2,7)--  Vector<Int> expected <- Vector:create<Int>()-      .push(1).push(1).push(1)-      .push(2).push(2).push(2).push(2).push(2).push(2).push(2)-      .push(3)-      .push(4).push(4).push(4)-      .push(5).push(5).push(5).push(5)-      .push(6).push(6)--  \ Testing.checkEquals<?>(tree.getTotal(),expected.size())-  \ Testing.checkEquals<?>(tree.getWeight(1),3)-  \ Testing.checkEquals<?>(tree.getWeight(2),7)-  \ Testing.checkEquals<?>(tree.getWeight(3),1)-  \ Testing.checkEquals<?>(tree.getWeight(4),3)-  \ Testing.checkEquals<?>(tree.getWeight(5),4)-  \ Testing.checkEquals<?>(tree.getWeight(6),2)--  traverse (Counter.zeroIndexed(tree.getTotal()) -> Int pos) {-    \ Testing.checkEquals<?>(tree.locate(pos),expected.readAt(pos))-  }-}--unittest modifySizes {-  CategoricalTree<Int> tree <- CategoricalTree<Int>.new()-      .setWeight(1,3)-      .setWeight(6,2)-      .setWeight(3,1)-      .setWeight(5,4)-      .setWeight(4,3)-      .setWeight(2,7)-      .setWeight(7,3)-      // modifications start here-      .setWeight(5,1)-      .setWeight(3,0)--  Vector<Int> expected <- Vector:create<Int>()-      .push(1).push(1).push(1)-      .push(2).push(2).push(2).push(2).push(2).push(2).push(2)-      .push(4).push(4).push(4)-      .push(5)-      .push(6).push(6)-      .push(7).push(7).push(7)--  \ Testing.checkEquals<?>(tree.getTotal(),expected.size())-  \ Testing.checkEquals<?>(tree.getWeight(1),3)-  \ Testing.checkEquals<?>(tree.getWeight(2),7)-  \ Testing.checkEquals<?>(tree.getWeight(3),0)-  \ Testing.checkEquals<?>(tree.getWeight(4),3)-  \ Testing.checkEquals<?>(tree.getWeight(5),1)-  \ Testing.checkEquals<?>(tree.getWeight(6),2)-  \ Testing.checkEquals<?>(tree.getWeight(7),3)--  traverse (Counter.zeroIndexed(tree.getTotal()) -> Int pos) {-    \ Testing.checkEquals<?>(tree.locate(pos),expected.readAt(pos))-  }--  // Use ReadAt aliases for the same functionality.-  traverse (Counter.zeroIndexed(tree.size()) -> Int pos) {-    \ Testing.checkEquals<?>(tree.readAt(pos),expected.readAt(pos))-  }-}--unittest increment {-  CategoricalTree<Int> tree <- CategoricalTree<Int>.new()-    .incrWeight(1)-    .incrWeight(1)-    .incrWeight(1)--  \ Testing.checkEquals<?>(tree.getTotal(),3)-  \ Testing.checkEquals<?>(tree.getWeight(1),3)-}--unittest decrement {-  CategoricalTree<Int> tree <- CategoricalTree<Int>.new()-    .setWeight(1,10)-    .decrWeight(1)-    .decrWeight(1)-    .decrWeight(1)--  \ Testing.checkEquals<?>(tree.getTotal(),7)-  \ Testing.checkEquals<?>(tree.getWeight(1),7)-}--unittest duplicate {-  CategoricalTree<Int> tree <- CategoricalTree<Int>.new().setWeight(1,1).setWeight(2,2)--  CategoricalTree<Int> copy <- tree.duplicate()-  \ Testing.checkEquals<?>(copy.size(),3)-  \ Testing.checkEquals<?>(copy.getWeight(1),1)-  \ Testing.checkEquals<?>(copy.getWeight(2),2)--  \ copy.setWeight(2,0)-  \ Testing.checkEquals<?>(copy.size(),1)-  \ Testing.checkEquals<?>(copy.getWeight(1),1)-  \ Testing.checkEquals<?>(tree.getWeight(2),2)-}--unittest integrationTest {-  ValidatedTree<Int> tree      <- ValidatedTree<Int>.new()-  SearchTree<Int,Int> expected <- SearchTree<Int,Int>.new()--  Int count   <- 137-  Int maxSize <- 15-  Int hash1   <- 379-  Int hash2   <- 457-  $ReadOnly[count,maxSize,hash1,hash2]$--  // Populate the tree with arbitrary data.-  traverse (Counter.zeroIndexed(count) -> Int i) {-    \ tree.setWeight((i*hash1)%count,(i*hash2)%maxSize)-  }--  // Overwrite the tree with real data.-  traverse (Counter.zeroIndexed(count) -> Int i) {-    Int key <- (i*hash2)%count-    Int size <- (i*hash1)%maxSize-    \ tree.setWeight(key,size)-    \ expected.set(key,size)-  }--  optional Order<KeyValue<Int,Int>> node <- expected.defaultOrder()-  Int key       <- 0-  Int remaining <- 0-  traverse (Counter.zeroIndexed(tree.getTotal()) -> Int pos) {-    while (remaining < 1) {-      key       <- require(node).get().getKey()-      remaining <- require(node).get().getValue()-      node <- require(node).next()-    }-    $ReadOnly[key]$-    $Hidden[node]$-    \ Testing.checkEquals<?>(tree.locate(pos),key)-    remaining <- remaining-1-  }--  // NOTE: Order does not refine Formatted => can't use checkEmpty here.-  \ Testing.checkEquals<?>(present(node),false)-  \ Testing.checkEquals<?>(remaining,0)-}---testcase "negative size crashes" {-  crash-  require "size.+negative"-}--unittest test {-  \ CategoricalTree<Int>.new().setWeight(0,-10)-}---testcase "negative position crashes" {-  crash-  require "position.+negative"-}--unittest test {-  \ CategoricalTree<Int>.new().locate(-10)-}---testcase "position past end crashes" {-  crash-  require "position.+total"-}--unittest test {-  \ CategoricalTree<Int>.new().setWeight(0,1).locate(1)-}
− lib/math/categorical-tree.0rx
@@ -1,258 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--define CategoricalTree {-  @value AutoBinaryTree<CategoricalTreeNode<#c>,#c,Int,CategoricalSearch<#c>> tree--  new () {-    return #self{ AutoBinaryTree<CategoricalTreeNode<#c>,#c,Int,CategoricalSearch<#c>>.new() }-  }--  duplicate () {-    return #self{ tree.duplicate() }-  }--  getTotal () {-    return CategoricalTreeNode<#c>.tryTotal(tree.getRoot())-  }--  setWeight (cat,size) {-    if (size < 0) {-      fail("size must not be negative")-    } elif (size > 0) {-      \ tree.set(cat,size)-    } else {-      \ tree.remove(cat)-    }-    return self-  }--  getWeight (cat) {-    scoped {-      optional Int size <- tree.get(cat)-    } in if (present(size)) {-      return require(size)-    } else {-      return 0-    }-  }--  incrWeight (cat) {-    \ setWeight(cat,getWeight(cat)+1)-    return self-  }--  decrWeight (cat) {-    \ setWeight(cat,getWeight(cat)-1)-    return self-  }--  locate (pos) {-    if (pos < 0) {-      fail("position must not be negative")-    } elif (pos >= getTotal()) {-      fail("position must be strictly less than the total")-    } else {-      return require(CategoricalTreeNode<#c>.findPosition(pos,tree.getRoot()))-    }-  }--  readAt (pos) {-    return locate(pos)-  }--  size () {-    return getTotal()-  }-}--define ValidatedTree {-  @value AutoBinaryTree<CategoricalTreeNode<#c>,#c,Int,CategoricalSearch<#c>> tree--  new () {-    return #self{ AutoBinaryTree<CategoricalTreeNode<#c>,#c,Int,CategoricalSearch<#c>>.new() }-  }--  getTotal () {-    return CategoricalTreeNode<#c>.tryTotal(tree.getRoot())-  }--  setWeight (cat,size) {-    if (size < 0) {-      fail("size must not be negative")-    } elif (size > 0) {-      \ tree.set(cat,size)-    } else {-      \ tree.remove(cat)-    }-    \ validateTotal(tree.getRoot())-    return self-  }--  getWeight (cat) {-    scoped {-      optional Int size <- tree.get(cat)-    } in if (present(size)) {-      return require(size)-    } else {-      return 0-    }-  }--  locate (pos) {-    if (pos < 0) {-      fail("position must not be negative")-    } elif (pos >= getTotal()) {-      fail("position must be strictly less than the total")-    } else {-      return require(CategoricalTreeNode<#c>.findPosition(pos,tree.getRoot()))-    }-  }--  @type validateTotal (optional CategoricalSearch<#c>) -> ()-  validateTotal (node) {-    if (present(node)) {-      CategoricalSearch<#c> node2 <- require(node)-      \ validateTotal(node2.getLower())-      \ validateTotal(node2.getHigher())-      Int size   <- node2.getValue()-      Int lower  <- CategoricalTreeNode<#c>.tryTotal(node2.getLower())-      Int higher <- CategoricalTreeNode<#c>.tryTotal(node2.getHigher())-      if (node2.getTotal() != size+lower+higher) {-        fail(String.builder()-            .append("bad total: ")-            .append(size.formatted())-            .append("+")-            .append(lower.formatted())-            .append("+")-            .append(higher.formatted())-            .append(" != ")-            .append(node2.getTotal().formatted())-            .build())-      }-    }-  }-}--@value interface CategoricalSearch<|#c> {-  refines BinaryTreeNode<#c,Int>--  getTotal () -> (Int)-}--concrete CategoricalTreeNode<#c> {-  defines KVFactory<#c,Int>-  refines CategoricalSearch<#c>-  refines BalancedTreeNode<CategoricalTreeNode<#c>,#c,Int>-  refines Duplicate--  @type findPosition (Int,optional CategoricalSearch<#c>) -> (optional #c)-  @type tryTotal (optional CategoricalSearch<#c>) -> (Int)--  @value getTotal () -> (Int)-}--define CategoricalTreeNode {-  $ReadOnly[key]$--  @value Int height-  @value #c key-  @value Int size-  @value Int total-  @value optional #self lower-  @value optional #self higher--  duplicate () {-    optional #self lower2 <- empty-    if (present(lower)) {-      lower2 <- require(lower).duplicate()-    }-    optional #self higher2 <- empty-    if (present(higher)) {-      higher2 <- require(higher).duplicate()-    }-    return #self{ height, key, size, total, lower2, higher2 }-  }--  findPosition (pos,node) (cat) {-    cat <- empty-    if (present(node)) {-      scoped {-        Int pos2 <- pos-        $Hidden[pos]$-        CategoricalSearch<#c> node2 <- require(node)-        Int lower <- tryTotal(node2.getLower())-        Int size <- node2.getValue()-        $ReadOnly[node2,lower,size]$-      } in if (pos2 < node2.getTotal()) {-        if (pos2 < lower) {-          // pos2 is in the 1st (lower) of the 3 sections.-          return findPosition(pos2,node2.getLower())-        }-        pos2 <- pos2-lower-        if (pos2 < size) {-          // pos2 is in the 2nd (middle) of the 3 sections.-          return node2.getKey()-        }-        pos2 <- pos2-size-          // pos2 is in the 3rd (higher) of the 3 sections.-        return findPosition(pos2,node2.getHigher())-      }-    }-  }--  tryTotal (node) {-    if (present(node)) {-      return require(node).getTotal()-    } else {-      return 0-    }-  }--  newNode (k,v) {-    return #self{ 1, k, v, v, empty, empty }-  }--  getLower ()   { return lower }-  setLower (l)  { lower <- l }-  getHigher ()  { return higher }-  setHigher (h) { higher <- h }-  getKey ()     { return key }-  getValue ()   { return size }-  setValue (v)  { size <- v }-  getHeight ()  { return height }-  getTotal ()   { return total }--  updateNode () {-    scoped {-      Int l <- 0-      Int h <- 0-      if (present(lower)) {-        l <- require(lower).getHeight()-      }-      if (present(higher)) {-        h <- require(higher).getHeight()-      }-    } in if (l > h) {-      height <- l + 1-    } else {-      height <- h + 1-    }-    total <- size+tryTotal(lower)+tryTotal(higher)-  }-}
− lib/math/math.0rt
@@ -1,135 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2020 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "sanity check all functions" {-  success-}--unittest cos {-  \ Testing.checkBetween<?>(Math.cos(2.0),-0.42,-0.41)-}--unittest sin {-  \ Testing.checkBetween<?>(Math.sin(2.0),0.90,0.91)-}--unittest tan {-  \ Testing.checkBetween<?>(Math.tan(2.0),-2.19,-2.18)-}--unittest acos {-  \ Testing.checkBetween<?>(Math.acos(0.5),1.04,1.05)-}--unittest asin {-  \ Testing.checkBetween<?>(Math.asin(0.5),0.52,0.53)-}--unittest atan {-  \ Testing.checkBetween<?>(Math.atan(0.5),0.46,0.47)-}--unittest cosh {-  \ Testing.checkBetween<?>(Math.cosh(2.0),3.76,3.77)-}--unittest sinh {-  \ Testing.checkBetween<?>(Math.sinh(2.0),3.62,3.63)-}--unittest tanh {-  \ Testing.checkBetween<?>(Math.tanh(2.0),0.96,0.97)-}--unittest acosh {-  \ Testing.checkBetween<?>(Math.acosh(2.0),1.31,1.32)-}--unittest asinh {-  \ Testing.checkBetween<?>(Math.asinh(2.0),1.44,1.45)-}--unittest atanh {-  \ Testing.checkBetween<?>(Math.atanh(0.5),0.54,0.55)-}--unittest exp {-  \ Testing.checkBetween<?>(Math.exp(1.0),2.71,2.72)-}--unittest log {-  \ Testing.checkBetween<?>(Math.log(9.0),2.19,2.20)-}--unittest log10 {-  \ Testing.checkBetween<?>(Math.log10(100.0),1.99,2.01)-}--unittest log2 {-  \ Testing.checkBetween<?>(Math.log2(8.0),2.99,3.01)-}--unittest pow {-  \ Testing.checkBetween<?>(Math.pow(2.0,3.0),7.99,8.01)-}--unittest sqrt {-  \ Testing.checkBetween<?>(Math.sqrt(4.0),1.99,2.01)-}--unittest ceil {-  \ Testing.checkBetween<?>(Math.ceil(2.2),2.99,3.01)-}--unittest floor {-  \ Testing.checkBetween<?>(Math.floor(2.2),1.99,2.01)-}--unittest fmod {-  \ Testing.checkBetween<?>(Math.fmod(7.0,4.0),2.99,3.01)-}--unittest trunc {-  \ Testing.checkBetween<?>(Math.trunc(2.2),1.99,2.01)-}--unittest round {-  \ Testing.checkBetween<?>(Math.round(2.7),2.99,3.01)-}--unittest fabs {-  \ Testing.checkBetween<?>(Math.fabs(-10.0),9.99,10.01)-}--unittest isinf {-  if (!Math.isinf(Math.log(0.0))) {-    fail("Failed")-  }-}--unittest isnan {-  if (!Math.isnan(Math.sqrt(-1.0))) {-    fail("Failed")-  }-}--unittest abs {-  \ Testing.checkEquals<?>(Math.abs(-10),10)-  \ Testing.checkEquals<?>(Math.abs(10),10)-  \ Testing.checkEquals<?>(Math.abs(0),0)-}
− lib/math/random.0rt
@@ -1,225 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "valid instantiations" {-  success-}--unittest exponential {-  Generator<Float> random <- RandomExponential.new(1.0)-  Float value <- random.generate()-}--unittest gaussian {-  Generator<Float> random <- RandomGaussian.new(0.0,1.0)-  Float value <- random.generate()-}--unittest uniform {-  Generator<Float> random <- RandomUniform.new(0.0,1.0)-  Float value <- random.generate()-}---testcase "negative lambda in exponential" {-  crash-  require "lambda"-  require "-1"-}--unittest test {-  Generator<Float> random <- RandomExponential.new(-1.0)-}---testcase "negative standard deviation in gaussian" {-  crash-  require "standard deviation"-  require "-1"-}--unittest test {-  Generator<Float> random <- RandomGaussian.new(0.0,-1.0)-}---testcase "empty range in uniform" {-  crash-  require "range"-  require "0,-1"-}--unittest test {-  Generator<Float> random <- RandomUniform.new(0.0,-1.0)-}---testcase "distribution sanity checks" {-  success-}--unittest exponential {-  Int count <- 10000-  Float lambda <- 10.0-  $ReadOnly[count,lambda]$--  Generator<Float> random <- RandomExponential.new(lambda)--  Float sum <- 0.0-  traverse (Counter.zeroIndexed(count) -> _) {-    Float value <- random.generate()-    \ Testing.checkGreaterThan<?>(value,0.0)-    sum <- sum+value-  }--  \ Testing.checkBetween<?>(sum/count.asFloat(),0.9/lambda,1.1/lambda)-}--unittest gaussian {-  Int count <- 10000-  Float mean <- 100.0-  Float sd <- 1.0-  $ReadOnly[count,mean,sd]$--  Generator<Float> random <- RandomGaussian.new(mean,sd)--  Float sum <- 0.0-  traverse (Counter.zeroIndexed(count) -> _) {-    sum <- sum+random.generate()-  }--  \ Testing.checkBetween<?>(sum/count.asFloat(),mean-0.1*sd,mean+0.1*sd)-}--unittest uniform {-  Int count <- 10000-  Float min <- -11.0-  Float max <- 5.0-  $ReadOnly[count,min,max]$--  Generator<Float> random <- RandomUniform.new(min,max)--  Float sum <- 0.0-  traverse (Counter.zeroIndexed(count) -> _) {-    Float value <- random.generate()-    \ Testing.checkBetween<?>(value,min,max)-    sum <- sum+value-  }--  \ Testing.checkBetween<?>(sum/count.asFloat(),min-0.1*(max-min),max-0.1*(max-min))-}---testcase "distribution seed checks" {-  success-}--unittest exponential {-  Int count <- 10000-  Float lambda <- 10.0-  $ReadOnly[count,lambda]$--  RandomExponential random1 <- RandomExponential.new(lambda)-  RandomExponential random2 <- RandomExponential.new(lambda)--  Float v1 <- random1.generate()-  Float v2 <- random2.generate()-  random1 <- random1.setSeed(123)-  Float v3 <- random1.generate()-  random2 <- random2.setSeed(123)-  Float v4 <- random2.generate()--  \ Testing.checkEquals<?>(v1 == v2,false)-  \ Testing.checkEquals<?>(v1 == v3,false)-  \ Testing.checkEquals<?>(v3,v4)-}--unittest gaussian {-  Int count <- 10000-  Float mean <- 100.0-  Float sd <- 1.0-  $ReadOnly[count,mean,sd]$--  RandomGaussian random1 <- RandomGaussian.new(mean,sd)-  RandomGaussian random2 <- RandomGaussian.new(mean,sd)--  Float v1 <- random1.generate()-  Float v2 <- random2.generate()-  random1 <- random1.setSeed(123)-  Float v3 <- random1.generate()-  random2 <- random2.setSeed(123)-  Float v4 <- random2.generate()--  \ Testing.checkEquals<?>(v1 == v2,false)-  \ Testing.checkEquals<?>(v1 == v3,false)-  \ Testing.checkEquals<?>(v3,v4)-}--unittest uniform {-  Int count <- 10000-  Float min <- -11.0-  Float max <- 5.0-  $ReadOnly[count,min,max]$--  RandomUniform random1 <- RandomUniform.new(min,max)-  RandomUniform random2 <- RandomUniform.new(min,max)--  Float v1 <- random1.generate()-  Float v2 <- random2.generate()-  random1 <- random1.setSeed(123)-  Float v3 <- random1.generate()-  random2 <- random2.setSeed(123)-  Float v4 <- random2.generate()--  \ Testing.checkEquals<?>(v1 == v2,false)-  \ Testing.checkEquals<?>(v1 == v3,false)-  \ Testing.checkEquals<?>(v3,v4)-}---testcase "Randomize tests" {-  success-}--unittest permuteFrom {-  CategoricalTree<String> tree <- CategoricalTree<String>.new()-      .setWeight("a",2)-      .setWeight("b",3)-      .setWeight("c",1)--  Vector<String> expected <- Vector:create<String>()-      .append("a")-      .append("a")-      .append("b")-      .append("b")-      .append("b")-      .append("c")--  Vector<String> output <- Vector:create<String>()-  \ Randomize:permuteFrom<?>(tree,RandomUniform.new(0.0,1.0),output)-  \ Sorting:sort<?>(output)--  \ Testing.checkEquals<?>(output.size(),expected.size())-  traverse (Counter.zeroIndexed(output.size()) -> Int pos) {-    \ Testing.checkEquals<?>(output.readAt(pos),expected.readAt(pos))-  }--  \ Testing.checkEquals<?>(tree.getWeight("a"),2)-  \ Testing.checkEquals<?>(tree.getWeight("b"),3)-  \ Testing.checkEquals<?>(tree.getWeight("c"),1)-}
− lib/math/random.0rx
@@ -1,29 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--define Randomize {-  permuteFrom (tree,random,output) {-    CategoricalTree<#c> copy <- tree.duplicate()-    $Hidden[tree]$-    while (copy.getTotal() > 0) {-      #c cat <- copy.locate((random.generate()*copy.getTotal().asFloat()).asInt())-      \ output.append(cat)-      \ copy.decrWeight(cat)-    }-  }-}
lib/math/src/Extension_Math.cpp view
@@ -33,165 +33,165 @@ struct ExtType_Math : public Type_Math {   inline ExtType_Math(Category_Math& p, Params<0>::Type params) : Type_Math(p, params) {} -  ReturnTuple Call_acos(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_acos(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.acos")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::acos(Var_arg1)));   } -  ReturnTuple Call_acosh(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_acosh(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.acosh")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::acosh(Var_arg1)));   } -  ReturnTuple Call_asin(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_asin(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.asin")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::asin(Var_arg1)));   } -  ReturnTuple Call_asinh(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_asinh(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.asinh")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::asinh(Var_arg1)));   } -  ReturnTuple Call_atan(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_atan(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.atan")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::atan(Var_arg1)));   } -  ReturnTuple Call_atanh(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_atanh(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.atanh")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::atanh(Var_arg1)));   } -  ReturnTuple Call_ceil(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_ceil(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.ceil")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::ceil(Var_arg1)));   } -  ReturnTuple Call_cos(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_cos(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.cos")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::cos(Var_arg1)));   } -  ReturnTuple Call_cosh(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_cosh(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.cosh")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::cosh(Var_arg1)));   } -  ReturnTuple Call_exp(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_exp(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.exp")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::exp(Var_arg1)));   } -  ReturnTuple Call_fabs(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_fabs(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.fabs")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::fabs(Var_arg1)));   } -  ReturnTuple Call_floor(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_floor(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.floor")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::floor(Var_arg1)));   } -  ReturnTuple Call_fmod(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_fmod(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.fmod")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     const PrimFloat Var_arg2 = (args.At(1)).AsFloat();     return ReturnTuple(Box_Float(std::fmod(Var_arg1,Var_arg2)));   } -  ReturnTuple Call_isinf(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_isinf(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.isinf")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Bool(std::isinf(Var_arg1)));   } -  ReturnTuple Call_isnan(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_isnan(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.isnan")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Bool(std::isnan(Var_arg1)));   } -  ReturnTuple Call_log(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_log(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.log")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::log(Var_arg1)));   } -  ReturnTuple Call_log10(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_log10(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.log10")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::log10(Var_arg1)));   } -  ReturnTuple Call_log2(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_log2(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.log2")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::log2(Var_arg1)));   } -  ReturnTuple Call_pow(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_pow(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.pow")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     const PrimFloat Var_arg2 = (args.At(1)).AsFloat();     return ReturnTuple(Box_Float(std::pow(Var_arg1,Var_arg2)));   } -  ReturnTuple Call_round(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_round(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.round")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::round(Var_arg1)));   } -  ReturnTuple Call_sin(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_sin(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.sin")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::sin(Var_arg1)));   } -  ReturnTuple Call_sinh(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_sinh(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.sinh")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::sinh(Var_arg1)));   } -  ReturnTuple Call_sqrt(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_sqrt(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.sqrt")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::sqrt(Var_arg1)));   } -  ReturnTuple Call_tan(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_tan(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.tan")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::tan(Var_arg1)));   } -  ReturnTuple Call_tanh(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_tanh(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.tanh")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::tanh(Var_arg1)));   } -  ReturnTuple Call_trunc(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_trunc(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.trunc")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     return ReturnTuple(Box_Float(std::trunc(Var_arg1)));   } -  ReturnTuple Call_abs(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_abs(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Math.abs")     const PrimInt Var_arg1 = (args.At(0)).AsInt();     return ReturnTuple(Box_Int(std::abs(Var_arg1)));@@ -203,10 +203,12 @@   return category; } -S<Type_Math> CreateType_Math(Params<0>::Type params) {+S<const Type_Math> CreateType_Math(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_Math(CreateCategory_Math(), Params<0>::Type()));   return cached; }++void RemoveType_Math(const Params<0>::Type& params) {}  #ifdef ZEOLITE_PUBLIC_NAMESPACE }  // namespace ZEOLITE_PUBLIC_NAMESPACE
lib/math/src/Extension_RandomExponential.cpp view
@@ -29,7 +29,7 @@ namespace ZEOLITE_PUBLIC_NAMESPACE { #endif  // ZEOLITE_PUBLIC_NAMESPACE -BoxedValue CreateValue_RandomExponential(S<Type_RandomExponential> parent, const ValueTuple& args);+BoxedValue CreateValue_RandomExponential(S<const Type_RandomExponential> parent, const ValueTuple& args);  struct ExtCategory_RandomExponential : public Category_RandomExponential { };@@ -37,19 +37,19 @@ struct ExtType_RandomExponential : public Type_RandomExponential {   inline ExtType_RandomExponential(Category_RandomExponential& p, Params<0>::Type params) : Type_RandomExponential(p, params) {} -  ReturnTuple Call_new(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_new(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("RandomExponential.new")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     if (Var_arg1 <= 0) {       FAIL() << "Invalid lambda " << Var_arg1;     }-    return ReturnTuple(CreateValue_RandomExponential(shared_from_this(), args));+    return ReturnTuple(CreateValue_RandomExponential(PARAM_SELF, args));   } };  struct ExtValue_RandomExponential : public Value_RandomExponential {-  inline ExtValue_RandomExponential(S<Type_RandomExponential> p, const ValueTuple& args)-    : Value_RandomExponential(p),+  inline ExtValue_RandomExponential(S<const Type_RandomExponential> p, const ValueTuple& args)+    : Value_RandomExponential(std::move(p)),       generator_((int) (long) this),       distribution_(args.At(0).AsFloat()) {} @@ -74,13 +74,15 @@   return category; } -S<Type_RandomExponential> CreateType_RandomExponential(Params<0>::Type params) {+S<const Type_RandomExponential> CreateType_RandomExponential(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_RandomExponential(CreateCategory_RandomExponential(), Params<0>::Type()));   return cached; } -BoxedValue CreateValue_RandomExponential(S<Type_RandomExponential> parent, const ValueTuple& args) {-  return BoxedValue::New<ExtValue_RandomExponential>(parent, args);+void RemoveType_RandomExponential(const Params<0>::Type& params) {}++BoxedValue CreateValue_RandomExponential(S<const Type_RandomExponential> parent, const ValueTuple& args) {+  return BoxedValue::New<ExtValue_RandomExponential>(std::move(parent), args); }  #ifdef ZEOLITE_PUBLIC_NAMESPACE
lib/math/src/Extension_RandomGaussian.cpp view
@@ -29,7 +29,7 @@ namespace ZEOLITE_PUBLIC_NAMESPACE { #endif  // ZEOLITE_PUBLIC_NAMESPACE -BoxedValue CreateValue_RandomGaussian(S<Type_RandomGaussian> parent, const ValueTuple& args);+BoxedValue CreateValue_RandomGaussian(S<const Type_RandomGaussian> parent, const ValueTuple& args);  struct ExtCategory_RandomGaussian : public Category_RandomGaussian { };@@ -37,20 +37,20 @@ struct ExtType_RandomGaussian : public Type_RandomGaussian {   inline ExtType_RandomGaussian(Category_RandomGaussian& p, Params<0>::Type params) : Type_RandomGaussian(p, params) {} -  ReturnTuple Call_new(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_new(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("RandomGaussian.new")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     const PrimFloat Var_arg2 = (args.At(1)).AsFloat();     if (Var_arg2 <= 0) {       FAIL() << "Invalid standard deviation " << Var_arg2;     }-    return ReturnTuple(CreateValue_RandomGaussian(shared_from_this(), args));+    return ReturnTuple(CreateValue_RandomGaussian(PARAM_SELF, args));   } };  struct ExtValue_RandomGaussian : public Value_RandomGaussian {-  inline ExtValue_RandomGaussian(S<Type_RandomGaussian> p, const ValueTuple& args)-    : Value_RandomGaussian(p),+  inline ExtValue_RandomGaussian(S<const Type_RandomGaussian> p, const ValueTuple& args)+    : Value_RandomGaussian(std::move(p)),       generator_((int) (long) this),       distribution_(args.At(0).AsFloat(), args.At(1).AsFloat()) {} @@ -75,13 +75,15 @@   return category; } -S<Type_RandomGaussian> CreateType_RandomGaussian(Params<0>::Type params) {+S<const Type_RandomGaussian> CreateType_RandomGaussian(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_RandomGaussian(CreateCategory_RandomGaussian(), Params<0>::Type()));   return cached; } -BoxedValue CreateValue_RandomGaussian(S<Type_RandomGaussian> parent, const ValueTuple& args) {-  return BoxedValue::New<ExtValue_RandomGaussian>(parent, args);+void RemoveType_RandomGaussian(const Params<0>::Type& params) {}++BoxedValue CreateValue_RandomGaussian(S<const Type_RandomGaussian> parent, const ValueTuple& args) {+  return BoxedValue::New<ExtValue_RandomGaussian>(std::move(parent), args); }  #ifdef ZEOLITE_PUBLIC_NAMESPACE
lib/math/src/Extension_RandomUniform.cpp view
@@ -28,7 +28,7 @@ namespace ZEOLITE_PUBLIC_NAMESPACE { #endif  // ZEOLITE_PUBLIC_NAMESPACE -BoxedValue CreateValue_RandomUniform(S<Type_RandomUniform> parent, const ValueTuple& args);+BoxedValue CreateValue_RandomUniform(S<const Type_RandomUniform> parent, const ValueTuple& args);  struct ExtCategory_RandomUniform : public Category_RandomUniform { };@@ -36,20 +36,20 @@ struct ExtType_RandomUniform : public Type_RandomUniform {   inline ExtType_RandomUniform(Category_RandomUniform& p, Params<0>::Type params) : Type_RandomUniform(p, params) {} -  ReturnTuple Call_new(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_new(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("RandomUniform.new")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     const PrimFloat Var_arg2 = (args.At(1)).AsFloat();     if (Var_arg2 <= Var_arg1) {       FAIL() << "Invalid range (" << Var_arg1 << "," << Var_arg2 << ")";     }-    return ReturnTuple(CreateValue_RandomUniform(shared_from_this(), args));+    return ReturnTuple(CreateValue_RandomUniform(PARAM_SELF, args));   } };  struct ExtValue_RandomUniform : public Value_RandomUniform {-  inline ExtValue_RandomUniform(S<Type_RandomUniform> p, const ValueTuple& args)-    : Value_RandomUniform(p),+  inline ExtValue_RandomUniform(S<const Type_RandomUniform> p, const ValueTuple& args)+    : Value_RandomUniform(std::move(p)),       generator_((int) (long) this),       distribution_(args.At(0).AsFloat(), args.At(1).AsFloat()) {} @@ -74,13 +74,15 @@   return category; } -S<Type_RandomUniform> CreateType_RandomUniform(Params<0>::Type params) {+S<const Type_RandomUniform> CreateType_RandomUniform(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_RandomUniform(CreateCategory_RandomUniform(), Params<0>::Type()));   return cached; } -BoxedValue CreateValue_RandomUniform(S<Type_RandomUniform> parent, const ValueTuple& args) {-  return BoxedValue::New<ExtValue_RandomUniform>(parent, args);+void RemoveType_RandomUniform(const Params<0>::Type& params) {}++BoxedValue CreateValue_RandomUniform(S<const Type_RandomUniform> parent, const ValueTuple& args) {+  return BoxedValue::New<ExtValue_RandomUniform>(std::move(parent), args); }  #ifdef ZEOLITE_PUBLIC_NAMESPACE
+ lib/math/src/categorical-tree.0rx view
@@ -0,0 +1,258 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define CategoricalTree {+  @value AutoBinaryTree<CategoricalTreeNode<#c>,#c,Int,CategoricalSearch<#c>> tree++  new () {+    return #self{ AutoBinaryTree<CategoricalTreeNode<#c>,#c,Int,CategoricalSearch<#c>>.new() }+  }++  duplicate () {+    return #self{ tree.duplicate() }+  }++  getTotal () {+    return CategoricalTreeNode<#c>.tryTotal(tree.getRoot())+  }++  setWeight (cat,size) {+    if (size < 0) {+      fail("size must not be negative")+    } elif (size > 0) {+      \ tree.set(cat,size)+    } else {+      \ tree.remove(cat)+    }+    return self+  }++  getWeight (cat) {+    scoped {+      optional Int size <- tree.get(cat)+    } in if (present(size)) {+      return require(size)+    } else {+      return 0+    }+  }++  incrWeight (cat) {+    \ setWeight(cat,getWeight(cat)+1)+    return self+  }++  decrWeight (cat) {+    \ setWeight(cat,getWeight(cat)-1)+    return self+  }++  locate (pos) {+    if (pos < 0) {+      fail("position must not be negative")+    } elif (pos >= getTotal()) {+      fail("position must be strictly less than the total")+    } else {+      return require(CategoricalTreeNode<#c>.findPosition(pos,tree.getRoot()))+    }+  }++  readAt (pos) {+    return locate(pos)+  }++  size () {+    return getTotal()+  }+}++define ValidatedTree {+  @value AutoBinaryTree<CategoricalTreeNode<#c>,#c,Int,CategoricalSearch<#c>> tree++  new () { $NoTrace$+    return #self{ AutoBinaryTree<CategoricalTreeNode<#c>,#c,Int,CategoricalSearch<#c>>.new() }+  }++  getTotal () { $NoTrace$+    return CategoricalTreeNode<#c>.tryTotal(tree.getRoot())+  }++  setWeight (cat,size) { $NoTrace$+    if (size < 0) {+      fail("size must not be negative")+    } elif (size > 0) {+      \ tree.set(cat,size)+    } else {+      \ tree.remove(cat)+    }+    \ validateTotal(tree.getRoot())+    return self+  }++  getWeight (cat) { $NoTrace$+    scoped {+      optional Int size <- tree.get(cat)+    } in if (present(size)) {+      return require(size)+    } else {+      return 0+    }+  }++  locate (pos) { $NoTrace$+    if (pos < 0) {+      fail("position must not be negative")+    } elif (pos >= getTotal()) {+      fail("position must be strictly less than the total")+    } else {+      return require(CategoricalTreeNode<#c>.findPosition(pos,tree.getRoot()))+    }+  }++  @type validateTotal (optional CategoricalSearch<#c>) -> ()+  validateTotal (node) { $NoTrace$+    if (present(node)) {+      CategoricalSearch<#c> node2 <- require(node)+      \ validateTotal(node2.getLower())+      \ validateTotal(node2.getHigher())+      Int size   <- node2.getValue()+      Int lower  <- CategoricalTreeNode<#c>.tryTotal(node2.getLower())+      Int higher <- CategoricalTreeNode<#c>.tryTotal(node2.getHigher())+      if (node2.getTotal() != size+lower+higher) {+        fail(String.builder()+            .append("bad total: ")+            .append(size)+            .append("+")+            .append(lower)+            .append("+")+            .append(higher)+            .append(" != ")+            .append(node2.getTotal())+            .build())+      }+    }+  }+}++@value interface CategoricalSearch<|#c> {+  refines BinaryTreeNode<#c,Int>++  getTotal () -> (Int)+}++concrete CategoricalTreeNode<#c> {+  defines KVFactory<#c,Int>+  refines CategoricalSearch<#c>+  refines BalancedTreeNode<CategoricalTreeNode<#c>,#c,Int>+  refines Duplicate++  @type findPosition (Int,optional CategoricalSearch<#c>) -> (optional #c)+  @type tryTotal (optional CategoricalSearch<#c>) -> (Int)++  @value getTotal () -> (Int)+}++define CategoricalTreeNode {+  $ReadOnly[key]$++  @value Int height+  @value #c key+  @value Int size+  @value Int total+  @value optional #self lower+  @value optional #self higher++  duplicate () {+    optional #self lower2 <- empty+    if (present(lower)) {+      lower2 <- require(lower).duplicate()+    }+    optional #self higher2 <- empty+    if (present(higher)) {+      higher2 <- require(higher).duplicate()+    }+    return #self{ height, key, size, total, lower2, higher2 }+  }++  findPosition (pos,node) (cat) {+    cat <- empty+    if (present(node)) {+      scoped {+        Int pos2 <- pos+        $Hidden[pos]$+        CategoricalSearch<#c> node2 <- require(node)+        Int lower <- tryTotal(node2.getLower())+        Int size <- node2.getValue()+        $ReadOnly[node2,lower,size]$+      } in if (pos2 < node2.getTotal()) {+        if (pos2 < lower) {+          // pos2 is in the 1st (lower) of the 3 sections.+          return findPosition(pos2,node2.getLower())+        }+        pos2 <- pos2-lower+        if (pos2 < size) {+          // pos2 is in the 2nd (middle) of the 3 sections.+          return node2.getKey()+        }+        pos2 <- pos2-size+          // pos2 is in the 3rd (higher) of the 3 sections.+        return findPosition(pos2,node2.getHigher())+      }+    }+  }++  tryTotal (node) {+    if (present(node)) {+      return require(node).getTotal()+    } else {+      return 0+    }+  }++  newNode (k,v) {+    return #self{ 1, k, v, v, empty, empty }+  }++  getLower ()   { return lower }+  setLower (l)  { lower <- l }+  getHigher ()  { return higher }+  setHigher (h) { higher <- h }+  getKey ()     { return key }+  getValue ()   { return size }+  setValue (v)  { size <- v }+  getHeight ()  { return height }+  getTotal ()   { return total }++  updateNode () {+    scoped {+      Int l <- 0+      Int h <- 0+      if (present(lower)) {+        l <- require(lower).getHeight()+      }+      if (present(higher)) {+        h <- require(higher).getHeight()+      }+    } in if (l > h) {+      height <- l + 1+    } else {+      height <- h + 1+    }+    total <- size+tryTotal(lower)+tryTotal(higher)+  }+}
+ lib/math/src/random.0rx view
@@ -0,0 +1,29 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define Randomize {+  permuteFrom (tree,random,output) {+    CategoricalTree<#c> copy <- tree.duplicate()+    $Hidden[tree]$+    while (copy.getTotal() > 0) {+      #c cat <- copy.locate((random.generate()*copy.getTotal().asFloat()).asInt())+      \ output.append(cat)+      \ copy.decrWeight(cat)+    }+  }+}
+ lib/math/src/validated-tree.0rp view
@@ -0,0 +1,37 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++$ModuleOnly$++// A validated categorical tree.+//+// Notes:+// - This is the same as CategoricalTree, but it validates the tree structure+//   every time there is a modification, which is extremely inefficient. This+//   should therefore only be used in tests.+concrete ValidatedTree<#c> {+  #c immutable+  #c defines LessThan<#c>++  @type new () -> (#self)++  @value getTotal  ()       -> (Int)+  @value setWeight (#c,Int) -> (#self)+  @value getWeight (#c)     -> (Int)+  @value locate    (Int)    -> (#c)+}
+ lib/math/test/categorical-tree.0rt view
@@ -0,0 +1,199 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "CategoricalTree tests" {+  success+}++unittest simpleInsertion {+  CategoricalTree<Int> tree <- CategoricalTree<Int>.new()+      .setWeight(1,3)+      .setWeight(6,2)+      .setWeight(3,1)+      .setWeight(5,4)+      .setWeight(4,3)+      .setWeight(2,7)++  Vector<Int> expected <- Vector:create<Int>()+      .push(1).push(1).push(1)+      .push(2).push(2).push(2).push(2).push(2).push(2).push(2)+      .push(3)+      .push(4).push(4).push(4)+      .push(5).push(5).push(5).push(5)+      .push(6).push(6)++  \ Testing.checkEquals<?>(tree.getTotal(),expected.size())+  \ Testing.checkEquals<?>(tree.getWeight(1),3)+  \ Testing.checkEquals<?>(tree.getWeight(2),7)+  \ Testing.checkEquals<?>(tree.getWeight(3),1)+  \ Testing.checkEquals<?>(tree.getWeight(4),3)+  \ Testing.checkEquals<?>(tree.getWeight(5),4)+  \ Testing.checkEquals<?>(tree.getWeight(6),2)++  traverse (Counter.zeroIndexed(tree.getTotal()) -> Int pos) {+    \ Testing.checkEquals<?>(tree.locate(pos),expected.readAt(pos))+  }+}++unittest modifySizes {+  CategoricalTree<Int> tree <- CategoricalTree<Int>.new()+      .setWeight(1,3)+      .setWeight(6,2)+      .setWeight(3,1)+      .setWeight(5,4)+      .setWeight(4,3)+      .setWeight(2,7)+      .setWeight(7,3)+      // modifications start here+      .setWeight(5,1)+      .setWeight(3,0)++  Vector<Int> expected <- Vector:create<Int>()+      .push(1).push(1).push(1)+      .push(2).push(2).push(2).push(2).push(2).push(2).push(2)+      .push(4).push(4).push(4)+      .push(5)+      .push(6).push(6)+      .push(7).push(7).push(7)++  \ Testing.checkEquals<?>(tree.getTotal(),expected.size())+  \ Testing.checkEquals<?>(tree.getWeight(1),3)+  \ Testing.checkEquals<?>(tree.getWeight(2),7)+  \ Testing.checkEquals<?>(tree.getWeight(3),0)+  \ Testing.checkEquals<?>(tree.getWeight(4),3)+  \ Testing.checkEquals<?>(tree.getWeight(5),1)+  \ Testing.checkEquals<?>(tree.getWeight(6),2)+  \ Testing.checkEquals<?>(tree.getWeight(7),3)++  traverse (Counter.zeroIndexed(tree.getTotal()) -> Int pos) {+    \ Testing.checkEquals<?>(tree.locate(pos),expected.readAt(pos))+  }++  // Use ReadAt aliases for the same functionality.+  traverse (Counter.zeroIndexed(tree.size()) -> Int pos) {+    \ Testing.checkEquals<?>(tree.readAt(pos),expected.readAt(pos))+  }+}++unittest increment {+  CategoricalTree<Int> tree <- CategoricalTree<Int>.new()+    .incrWeight(1)+    .incrWeight(1)+    .incrWeight(1)++  \ Testing.checkEquals<?>(tree.getTotal(),3)+  \ Testing.checkEquals<?>(tree.getWeight(1),3)+}++unittest decrement {+  CategoricalTree<Int> tree <- CategoricalTree<Int>.new()+    .setWeight(1,10)+    .decrWeight(1)+    .decrWeight(1)+    .decrWeight(1)++  \ Testing.checkEquals<?>(tree.getTotal(),7)+  \ Testing.checkEquals<?>(tree.getWeight(1),7)+}++unittest duplicate {+  CategoricalTree<Int> tree <- CategoricalTree<Int>.new().setWeight(1,1).setWeight(2,2)++  CategoricalTree<Int> copy <- tree.duplicate()+  \ Testing.checkEquals<?>(copy.size(),3)+  \ Testing.checkEquals<?>(copy.getWeight(1),1)+  \ Testing.checkEquals<?>(copy.getWeight(2),2)++  \ copy.setWeight(2,0)+  \ Testing.checkEquals<?>(copy.size(),1)+  \ Testing.checkEquals<?>(copy.getWeight(1),1)+  \ Testing.checkEquals<?>(tree.getWeight(2),2)+}++unittest integrationTest { $DisableCoverage$+  ValidatedTree<Int> tree      <- ValidatedTree<Int>.new()+  SearchTree<Int,Int> expected <- SearchTree<Int,Int>.new()++  Int count   <- 137+  Int maxSize <- 15+  Int hash1   <- 379+  Int hash2   <- 457+  $ReadOnly[count,maxSize,hash1,hash2]$++  // Populate the tree with arbitrary data.+  traverse (Counter.zeroIndexed(count) -> Int i) {+    \ tree.setWeight((i*hash1)%count,(i*hash2)%maxSize)+  }++  // Overwrite the tree with real data.+  traverse (Counter.zeroIndexed(count) -> Int i) {+    Int key <- (i*hash2)%count+    Int size <- (i*hash1)%maxSize+    \ tree.setWeight(key,size)+    \ expected.set(key,size)+  }++  optional Order<KeyValue<Int,Int>> node <- expected.defaultOrder()+  Int key       <- 0+  Int remaining <- 0+  traverse (Counter.zeroIndexed(tree.getTotal()) -> Int pos) {+    while (remaining < 1) {+      key       <- require(node).get().getKey()+      remaining <- require(node).get().getValue()+      node <- require(node).next()+    }+    $ReadOnly[key]$+    $Hidden[node]$+    \ Testing.checkEquals<?>(tree.locate(pos),key)+    remaining <- remaining-1+  }++  // NOTE: Order does not refine Formatted => can't use checkEmpty here.+  \ Testing.checkEquals<?>(present(node),false)+  \ Testing.checkEquals<?>(remaining,0)+}+++testcase "negative size crashes" {+  crash+  require "size.+negative"+}++unittest test {+  \ CategoricalTree<Int>.new().setWeight(0,-10)+}+++testcase "negative position crashes" {+  crash+  require "position.+negative"+}++unittest test {+  \ CategoricalTree<Int>.new().locate(-10)+}+++testcase "position past end crashes" {+  crash+  require "position.+total"+}++unittest test {+  \ CategoricalTree<Int>.new().setWeight(0,1).locate(1)+}
+ lib/math/test/math.0rt view
@@ -0,0 +1,135 @@+/* -----------------------------------------------------------------------------+Copyright 2020 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "sanity check all functions" {+  success+}++unittest cos {+  \ Testing.checkBetween<?>(Math.cos(2.0),-0.42,-0.41)+}++unittest sin {+  \ Testing.checkBetween<?>(Math.sin(2.0),0.90,0.91)+}++unittest tan {+  \ Testing.checkBetween<?>(Math.tan(2.0),-2.19,-2.18)+}++unittest acos {+  \ Testing.checkBetween<?>(Math.acos(0.5),1.04,1.05)+}++unittest asin {+  \ Testing.checkBetween<?>(Math.asin(0.5),0.52,0.53)+}++unittest atan {+  \ Testing.checkBetween<?>(Math.atan(0.5),0.46,0.47)+}++unittest cosh {+  \ Testing.checkBetween<?>(Math.cosh(2.0),3.76,3.77)+}++unittest sinh {+  \ Testing.checkBetween<?>(Math.sinh(2.0),3.62,3.63)+}++unittest tanh {+  \ Testing.checkBetween<?>(Math.tanh(2.0),0.96,0.97)+}++unittest acosh {+  \ Testing.checkBetween<?>(Math.acosh(2.0),1.31,1.32)+}++unittest asinh {+  \ Testing.checkBetween<?>(Math.asinh(2.0),1.44,1.45)+}++unittest atanh {+  \ Testing.checkBetween<?>(Math.atanh(0.5),0.54,0.55)+}++unittest exp {+  \ Testing.checkBetween<?>(Math.exp(1.0),2.71,2.72)+}++unittest log {+  \ Testing.checkBetween<?>(Math.log(9.0),2.19,2.20)+}++unittest log10 {+  \ Testing.checkBetween<?>(Math.log10(100.0),1.99,2.01)+}++unittest log2 {+  \ Testing.checkBetween<?>(Math.log2(8.0),2.99,3.01)+}++unittest pow {+  \ Testing.checkBetween<?>(Math.pow(2.0,3.0),7.99,8.01)+}++unittest sqrt {+  \ Testing.checkBetween<?>(Math.sqrt(4.0),1.99,2.01)+}++unittest ceil {+  \ Testing.checkBetween<?>(Math.ceil(2.2),2.99,3.01)+}++unittest floor {+  \ Testing.checkBetween<?>(Math.floor(2.2),1.99,2.01)+}++unittest fmod {+  \ Testing.checkBetween<?>(Math.fmod(7.0,4.0),2.99,3.01)+}++unittest trunc {+  \ Testing.checkBetween<?>(Math.trunc(2.2),1.99,2.01)+}++unittest round {+  \ Testing.checkBetween<?>(Math.round(2.7),2.99,3.01)+}++unittest fabs {+  \ Testing.checkBetween<?>(Math.fabs(-10.0),9.99,10.01)+}++unittest isinf {+  if (!Math.isinf(Math.log(0.0))) {+    fail("Failed")+  }+}++unittest isnan {+  if (!Math.isnan(Math.sqrt(-1.0))) {+    fail("Failed")+  }+}++unittest abs {+  \ Testing.checkEquals<?>(Math.abs(-10),10)+  \ Testing.checkEquals<?>(Math.abs(10),10)+  \ Testing.checkEquals<?>(Math.abs(0),0)+}
+ lib/math/test/random.0rt view
@@ -0,0 +1,222 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "valid instantiations" {+  success+}++unittest exponential {+  Generator<Float> random <- RandomExponential.new(1.0)+  Float value <- random.generate()+}++unittest gaussian {+  Generator<Float> random <- RandomGaussian.new(0.0,1.0)+  Float value <- random.generate()+}++unittest uniform {+  Generator<Float> random <- RandomUniform.new(0.0,1.0)+  Float value <- random.generate()+}+++testcase "negative lambda in exponential" {+  crash+  require "lambda"+  require "-1"+}++unittest test {+  Generator<Float> random <- RandomExponential.new(-1.0)+}+++testcase "negative standard deviation in gaussian" {+  crash+  require "standard deviation"+  require "-1"+}++unittest test {+  Generator<Float> random <- RandomGaussian.new(0.0,-1.0)+}+++testcase "empty range in uniform" {+  crash+  require "range"+  require "0,-1"+}++unittest test {+  Generator<Float> random <- RandomUniform.new(0.0,-1.0)+}+++testcase "distribution sanity checks" {+  success+}++unittest exponential { $DisableCoverage$+  Int count <- 10000+  Float lambda <- 10.0+  $ReadOnly[count,lambda]$++  Generator<Float> random <- RandomExponential.new(lambda)++  Float sum <- 0.0+  traverse (Counter.zeroIndexed(count) -> _) {+    Float value <- random.generate()+    \ Testing.checkGreaterThan<?>(value,0.0)+    sum <- sum+value+  }++  \ Testing.checkBetween<?>(sum/count.asFloat(),0.9/lambda,1.1/lambda)+}++unittest gaussian { $DisableCoverage$+  Int count <- 10000+  Float mean <- 100.0+  Float sd <- 1.0+  $ReadOnly[count,mean,sd]$++  Generator<Float> random <- RandomGaussian.new(mean,sd)++  Float sum <- 0.0+  traverse (Counter.zeroIndexed(count) -> _) {+    sum <- sum+random.generate()+  }++  \ Testing.checkBetween<?>(sum/count.asFloat(),mean-0.1*sd,mean+0.1*sd)+}++unittest uniform { $DisableCoverage$+  Int count <- 10000+  Float min <- -11.0+  Float max <- 5.0+  $ReadOnly[count,min,max]$++  Generator<Float> random <- RandomUniform.new(min,max)++  Float sum <- 0.0+  traverse (Counter.zeroIndexed(count) -> _) {+    Float value <- random.generate()+    \ Testing.checkBetween<?>(value,min,max)+    sum <- sum+value+  }++  \ Testing.checkBetween<?>(sum/count.asFloat(),min-0.1*(max-min),max-0.1*(max-min))+}+++testcase "distribution seed checks" {+  success+}++unittest exponential {+  Float lambda <- 10.0+  $ReadOnly[lambda]$++  RandomExponential random1 <- RandomExponential.new(lambda)+  RandomExponential random2 <- RandomExponential.new(lambda)++  Float v1 <- random1.generate()+  Float v2 <- random2.generate()+  random1 <- random1.setSeed(123)+  Float v3 <- random1.generate()+  random2 <- random2.setSeed(123)+  Float v4 <- random2.generate()++  \ Testing.checkEquals<?>(v1 == v2,false)+  \ Testing.checkEquals<?>(v1 == v3,false)+  \ Testing.checkEquals<?>(v3,v4)+}++unittest gaussian {+  Float mean <- 100.0+  Float sd <- 1.0+  $ReadOnly[mean,sd]$++  RandomGaussian random1 <- RandomGaussian.new(mean,sd)+  RandomGaussian random2 <- RandomGaussian.new(mean,sd)++  Float v1 <- random1.generate()+  Float v2 <- random2.generate()+  random1 <- random1.setSeed(123)+  Float v3 <- random1.generate()+  random2 <- random2.setSeed(123)+  Float v4 <- random2.generate()++  \ Testing.checkEquals<?>(v1 == v2,false)+  \ Testing.checkEquals<?>(v1 == v3,false)+  \ Testing.checkEquals<?>(v3,v4)+}++unittest uniform {+  Float min <- -11.0+  Float max <- 5.0+  $ReadOnly[min,max]$++  RandomUniform random1 <- RandomUniform.new(min,max)+  RandomUniform random2 <- RandomUniform.new(min,max)++  Float v1 <- random1.generate()+  Float v2 <- random2.generate()+  random1 <- random1.setSeed(123)+  Float v3 <- random1.generate()+  random2 <- random2.setSeed(123)+  Float v4 <- random2.generate()++  \ Testing.checkEquals<?>(v1 == v2,false)+  \ Testing.checkEquals<?>(v1 == v3,false)+  \ Testing.checkEquals<?>(v3,v4)+}+++testcase "Randomize tests" {+  success+}++unittest permuteFrom {+  CategoricalTree<String> tree <- CategoricalTree<String>.new()+      .setWeight("a",2)+      .setWeight("b",3)+      .setWeight("c",1)++  Vector<String> expected <- Vector:create<String>()+      .append("a")+      .append("a")+      .append("b")+      .append("b")+      .append("b")+      .append("c")++  Vector<String> output <- Vector:create<String>()+  \ Randomize:permuteFrom<?>(tree,RandomUniform.new(0.0,1.0),output)+  \ Sorting:sort<?>(output)++  \ Testing.checkEquals<?>(output.size(),expected.size())+  traverse (Counter.zeroIndexed(output.size()) -> Int pos) {+    \ Testing.checkEquals<?>(output.readAt(pos),expected.readAt(pos))+  }++  \ Testing.checkEquals<?>(tree.getWeight("a"),2)+  \ Testing.checkEquals<?>(tree.getWeight("b"),3)+  \ Testing.checkEquals<?>(tree.getWeight("c"),1)+}
− lib/math/validated-tree.0rp
@@ -1,36 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--$ModuleOnly$--// A validated categorical tree.-//-// Notes:-// - This is the same as CategoricalTree, but it validates the tree structure-//   every time there is a modification, which is extremely inefficient. This-//   should therefore only be used in tests.-concrete ValidatedTree<#c> {-  #c defines LessThan<#c>--  @type new () -> (#self)--  @value getTotal  ()       -> (Int)-  @value setWeight (#c,Int) -> (#self)-  @value getWeight (#c)     -> (Int)-  @value locate    (Int)    -> (#c)-}
lib/testing/.zeolite-module view
@@ -1,3 +1,7 @@ root: "../.." path: "lib/testing"+extra_paths: [+  "lib/testing/src"+  "lib/testing/test"+] mode: incremental {}
− lib/testing/helpers.0rx
@@ -1,107 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2020 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--$TestsOnly$--define Testing {-  checkEquals (x,y) {-    if (!#x.equals(x,y)) {-      fail(String.builder()-          .append(x.formatted())-          .append(" is not equal to ")-          .append(y.formatted())-          .build())-    }-  }--  checkEmpty (x) {-    if (present(x)) {-      fail(String.builder()-          .append(require(x).formatted())-          .append(" is not empty")-          .build())-    }-  }--  checkPresent (x) {-    if (!present(x)) {-      fail(String.builder()-          .append("expected non-empty value")-          .build())-    }-  }--  checkOptional (x,y) {-    if (present(x) && present(y)) {-      \ checkEquals<?>(require(x),require(y))-    } elif (!present(x) && present(y)) {-      fail("empty is not equal to " + require(y).formatted())-    } elif (present(x) && !present(y)) {-      fail(require(x).formatted() + " is not equal to empty")-    }-  }--  checkBetween (x,l,h) {-    if (!lessEquals<?>(l,h)) {-      fail(String.builder()-          .append("Upper limit ")-          .append(l.formatted())-          .append(" is not at or above lower limit ")-          .append(h.formatted())-          .build())-    }-    if (!lessEquals<?>(l,x) || !lessEquals<?>(x,h)) {-      fail(String.builder()-          .append(x.formatted())-          .append(" is not between ")-          .append(l.formatted())-          .append(" and ")-          .append(h.formatted())-          .build())-    }-  }--  checkGreaterThan (x,l) {-    if (lessEquals<?>(x,l)) {-      fail(String.builder()-          .append(x.formatted())-          .append(" is not greater than ")-          .append(l.formatted())-          .build())-    }-  }--  checkLessThan (x,h) {-    if (lessEquals<?>(h,x)) {-      fail(String.builder()-          .append(x.formatted())-          .append(" is not less than ")-          .append(h.formatted())-          .build())-    }-  }--  @type lessEquals<#x>-    #x defines Equals<#x>-    #x defines LessThan<#x>-  (#x,#x) -> (Bool)-  lessEquals (x,y) {-    // Using !#x.lessThan(y,x) wouldn't account for NaNs.-    return #x.lessThan(x,y) || #x.equals(x,y)-  }-}
+ lib/testing/src/helpers.0rx view
@@ -0,0 +1,107 @@+/* -----------------------------------------------------------------------------+Copyright 2020 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++$TestsOnly$++define Testing {+  checkEquals (x,y) {+    if (!#x.equals(x,y)) {+      fail(String.builder()+          .append(x)+          .append(" is not equal to ")+          .append(y)+          .build())+    }+  }++  checkEmpty (x) {+    if (present(x)) {+      fail(String.builder()+          .append(require(x))+          .append(" is not empty")+          .build())+    }+  }++  checkPresent (x) {+    if (!present(x)) {+      fail(String.builder()+          .append("expected non-empty value")+          .build())+    }+  }++  checkOptional (x,y) {+    if (present(x) && present(y)) {+      \ checkEquals<?>(require(x),require(y))+    } elif (!present(x) && present(y)) {+      fail("empty is not equal to " + require(y).formatted())+    } elif (present(x) && !present(y)) {+      fail(require(x).formatted() + " is not equal to empty")+    }+  }++  checkBetween (x,l,h) {+    if (!lessEquals<?>(l,h)) {+      fail(String.builder()+          .append("Upper limit ")+          .append(l)+          .append(" is not at or above lower limit ")+          .append(h)+          .build())+    }+    if (!lessEquals<?>(l,x) || !lessEquals<?>(x,h)) {+      fail(String.builder()+          .append(x)+          .append(" is not between ")+          .append(l)+          .append(" and ")+          .append(h)+          .build())+    }+  }++  checkGreaterThan (x,l) {+    if (lessEquals<?>(x,l)) {+      fail(String.builder()+          .append(x)+          .append(" is not greater than ")+          .append(l)+          .build())+    }+  }++  checkLessThan (x,h) {+    if (lessEquals<?>(h,x)) {+      fail(String.builder()+          .append(x)+          .append(" is not less than ")+          .append(h)+          .build())+    }+  }++  @type lessEquals<#x>+    #x defines Equals<#x>+    #x defines LessThan<#x>+  (#x,#x) -> (Bool)+  lessEquals (x,y) {+    // Using !#x.lessThan(y,x) wouldn't account for NaNs.+    return #x.lessThan(x,y) || #x.equals(x,y)+  }+}
+ lib/testing/test/tests.0rt view
@@ -0,0 +1,180 @@+/* -----------------------------------------------------------------------------+Copyright 2020 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "checkEquals success" {+  success+}++unittest test {+  \ Testing.checkEquals<?>(13,13)+}+++testcase "checkEquals fail" {+  crash+  require stderr "13"+  require stderr "15"+}++unittest test {+  \ Testing.checkEquals<?>(13,15)+}+++testcase "checkEmpty success" {+  success+}++unittest test {+  \ Testing.checkEmpty(empty)+}+++testcase "checkEmpty fail" {+  crash+  require stderr "13"+  require stderr "empty"+}++unittest test {+  \ Testing.checkEmpty(13)+}+++testcase "checkPresent success" {+  success+}++unittest test {+  \ Testing.checkPresent(13)+}+++testcase "checkPresent fail" {+  crash+  require stderr "empty"+}++unittest test {+  \ Testing.checkPresent(empty)+}+++testcase "checkOptional success" {+  success+}++unittest test {+  \ Testing.checkOptional<Int>(empty,empty)+  \ Testing.checkOptional<?>(13,13)+}+++testcase "checkOptional fail empty" {+  crash+  require stderr "13"+  require stderr "empty"+}++unittest test {+  \ Testing.checkOptional<?>(13,empty)+}+++testcase "checkOptional fail present" {+  crash+  require stderr "13"+  require stderr "empty"+}++unittest test {+  \ Testing.checkOptional<?>(empty,13)+}+++testcase "checkBetween success" {+  success+}++unittest test {+  \ Testing.checkBetween<?>(13,13,15)+}+++testcase "checkBetween fail" {+  crash+  require stderr "13"+  require stderr "14"+  require stderr "15"+}++unittest test {+  \ Testing.checkBetween<?>(13,14,15)+}+++testcase "checkBetween bad range" {+  crash+  exclude stderr "14"+  require stderr "13"+  require stderr "15"+}++unittest test {+  \ Testing.checkBetween<?>(14,15,13)+}+++testcase "checkGreaterThan success" {+  success+}++unittest test {+  \ Testing.checkGreaterThan<?>(13,11)+}+++testcase "checkGreaterThan fail" {+  crash+  require stderr "13"+  require stderr "14"+}++unittest test {+  \ Testing.checkGreaterThan<?>(13,14)+}+++testcase "checkLessThan success" {+  success+}++unittest test {+  \ Testing.checkLessThan<?>(13,14)+}+++testcase "checkLessThan fail" {+  crash+  require stderr "13"+  require stderr "11"+}++unittest test {+  \ Testing.checkLessThan<?>(13,11)+}
− lib/testing/tests.0rt
@@ -1,180 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2020 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "checkEquals success" {-  success-}--unittest test {-  \ Testing.checkEquals<?>(13,13)-}---testcase "checkEquals fail" {-  crash-  require stderr "13"-  require stderr "15"-}--unittest test {-  \ Testing.checkEquals<?>(13,15)-}---testcase "checkEmpty success" {-  success-}--unittest test {-  \ Testing.checkEmpty(empty)-}---testcase "checkEmpty fail" {-  crash-  require stderr "13"-  require stderr "empty"-}--unittest test {-  \ Testing.checkEmpty(13)-}---testcase "checkPresent success" {-  success-}--unittest test {-  \ Testing.checkPresent(13)-}---testcase "checkPresent fail" {-  crash-  require stderr "empty"-}--unittest test {-  \ Testing.checkPresent(empty)-}---testcase "checkOptional success" {-  success-}--unittest test {-  \ Testing.checkOptional<Int>(empty,empty)-  \ Testing.checkOptional<?>(13,13)-}---testcase "checkOptional fail empty" {-  crash-  require stderr "13"-  require stderr "empty"-}--unittest test {-  \ Testing.checkOptional<?>(13,empty)-}---testcase "checkOptional fail present" {-  crash-  require stderr "13"-  require stderr "empty"-}--unittest test {-  \ Testing.checkOptional<?>(empty,13)-}---testcase "checkBetween success" {-  success-}--unittest test {-  \ Testing.checkBetween<?>(13,13,15)-}---testcase "checkBetween fail" {-  crash-  require stderr "13"-  require stderr "14"-  require stderr "15"-}--unittest test {-  \ Testing.checkBetween<?>(13,14,15)-}---testcase "checkBetween bad range" {-  crash-  exclude stderr "14"-  require stderr "13"-  require stderr "15"-}--unittest test {-  \ Testing.checkBetween<?>(14,15,13)-}---testcase "checkGreaterThan success" {-  success-}--unittest test {-  \ Testing.checkGreaterThan<?>(13,11)-}---testcase "checkGreaterThan fail" {-  crash-  require stderr "13"-  require stderr "14"-}--unittest test {-  \ Testing.checkGreaterThan<?>(13,14)-}---testcase "checkLessThan success" {-  success-}--unittest test {-  \ Testing.checkLessThan<?>(13,14)-}---testcase "checkLessThan fail" {-  crash-  require stderr "13"-  require stderr "11"-}--unittest test {-  \ Testing.checkLessThan<?>(13,11)-}
lib/thread/.zeolite-module view
@@ -1,5 +1,9 @@ root: "../.." path: "lib/thread"+extra_paths: [+  "lib/thread/src"+  "lib/thread/test"+] public_deps: [   "lib/util" ]
− lib/thread/barrier-ext.0rp
@@ -1,23 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--$ModuleOnly$--concrete EnumeratedWait {-  refines BarrierWait-}
− lib/thread/barrier.0rt
@@ -1,178 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "EnumeratedBarrier tests" {-  success-}--unittest correctCount {-  ReadAt<BarrierWait> barriers <- EnumeratedBarrier.new(13)-  \ Testing.checkEquals<?>(barriers.size(),13)-}--unittest waitForOneThread {-  ReadAt<BarrierWait> barriers <- EnumeratedBarrier.new(1)-  \ barriers.readAt(0).wait()-}--unittest zeroAllowed {-  ReadAt<BarrierWait> barriers <- EnumeratedBarrier.new(0)-  \ Testing.checkEquals<?>(barriers.size(),0)-}--unittest waitForMultipleThreads {-  Value value <- Value.create()-  Mutex mutex <- SimpleMutex.new()-  ReadAt<BarrierWait> barriers <- EnumeratedBarrier.new(3)--  Thread thread1 <- ProcessThread.from(WaitAndIncrement.create(2,value,mutex,barriers.readAt(1))).start()-  Thread thread2 <- ProcessThread.from(WaitAndIncrement.create(2,value,mutex,barriers.readAt(2))).start()--  \ barriers.readAt(0).wait()-  \ Testing.checkEquals<?>(value.get(),2)--  \ barriers.readAt(0).wait()-  \ Testing.checkEquals<?>(value.get(),4)--  \ thread1.join()-  \ thread2.join()-}--concrete WaitAndIncrement {-  refines Routine--  @type create (Int,Value,Mutex,BarrierWait) -> (WaitAndIncrement)-}--define WaitAndIncrement {-  @value Int count-  @value Value value-  @value Mutex mutex-  @value BarrierWait barrier--  create (count,value,mutex,barrier) {-    return WaitAndIncrement{ count, value, mutex, barrier }-  }--  run () {-    scoped {-      Int i <- 0-    } in while (i < count) {-      $Hidden[i,count]$-      scoped {-        MutexLock lock <- MutexLock.lock(mutex)-      } cleanup {-        \ lock.freeResource()-      } in {-        \ value.increment()-      }-      \ barrier.wait()-      // Gives the main thread time to read the value.-      \ Realtime.sleepSeconds(0.01)-    } update {-      i <- i+1-    }-  }-}---testcase "EnumeratedBarrier crashes with negative count" {-  crash-  require "-4"-}--unittest test {-  ReadAt<BarrierWait> barriers <- EnumeratedBarrier.new(-4)-}---testcase "wait() crashes when references are lost" {-  crash-  require "BarrierWait.*destroyed"-}--unittest test {-  scoped {-    ReadAt<BarrierWait> barriers <- EnumeratedBarrier.new(2)-  } in BarrierWait barrier <- barriers.readAt(0)-  \ barrier.wait()-}---testcase "references lost during wait() causes crash" {-  crash-  require "BarrierWait.*waiting"-}--unittest test {-  scoped {-    ReadAt<BarrierWait> barriers <- EnumeratedBarrier.new(2)-  } in {-    Thread thread <- ProcessThread.from(WaitAndExit.create(barriers.readAt(0))).start()-    // Give the thread time to start.-    \ Realtime.sleepSeconds(0.1)-  }-}--concrete WaitAndExit {-  refines Routine--  @type create (BarrierWait) -> (WaitAndExit)-}--define WaitAndExit {-  @value BarrierWait barrier--  create (barrier) {-    return WaitAndExit{ barrier }-  }--  run () {-    \ barrier.wait()-  }-}---testcase "wait() crashes when two threads use same BarrierWait" {-  crash-  require "BarrierWait.*in use"-}--unittest test {-  ReadAt<BarrierWait> barriers <- EnumeratedBarrier.new(2)-  Thread thread <- ProcessThread.from(WaitAndExit.create(barriers.readAt(0))).start()-  \ barriers.readAt(0).wait()-}--concrete WaitAndExit {-  refines Routine--  @type create (BarrierWait) -> (WaitAndExit)-}--define WaitAndExit {-  @value BarrierWait barrier--  create (barrier) {-    return WaitAndExit{ barrier }-  }--  run () {-    \ barrier.wait()-  }-}
− lib/thread/condition.0rt
@@ -1,179 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "ThreadCondition tests" {-  success-}--unittest resumeAll {-  Value value <- Value.create()-  ThreadCondition cond <- ThreadCondition.new()--  Thread thread1 <- ProcessThread.from(WaitAndUpdate.create(value,cond)).start()-  Thread thread2 <- ProcessThread.from(WaitAndUpdate.create(value,cond)).start()--  // Wait for both threads to increment Value once, to indicate readiness.-  while (true) {-    scoped {-      MutexLock lock <- MutexLock.lock(cond)-    } cleanup {-      \ lock.freeResource()-    } in if (value.get() >= 2) {-      break-    }-  }--  \ cond.resumeAll()-  \ thread1.join()-  \ thread2.join()--  \ Testing.checkEquals<?>(value.get(),4)-}--unittest resumeOne {-  Value value <- Value.create()-  ThreadCondition cond <- ThreadCondition.new()--  Thread thread1 <- ProcessThread.from(WaitAndUpdate.create(value,cond)).start()-  Thread thread2 <- ProcessThread.from(WaitAndUpdate.create(value,cond)).start()--  \ Realtime.sleepSeconds(0.01)--  // Wait for both threads to increment Value once, to indicate readiness.-  while (true) {-    scoped {-      MutexLock lock <- MutexLock.lock(cond)-    } cleanup {-      \ lock.freeResource()-    } in if (value.get() >= 2) {-      break-    }-  }--  \ cond.resumeOne()-  \ thread1.join()-  \ thread2.join()--  \ Testing.checkEquals<?>(value.get(),3)-}--unittest forceWaitTimeout {-  Value value <- Value.create()-  ThreadCondition cond <- ThreadCondition.new()--  Thread thread <- ProcessThread.from(WaitAndUpdate.create(value,cond)).start()--  \ Realtime.sleepSeconds(0.2)-  \ cond.resumeOne()-  \ thread.join()--  \ Testing.checkEquals<?>(value.get(),1)-}--unittest waitNoTimeout {-  ThreadCondition cond <- ThreadCondition.new()--  Thread thread <- ProcessThread.from(JustWait.create(cond)).start()--  \ Realtime.sleepSeconds(0.1)-  \ cond.resumeOne()-  \ thread.join()-  \ cond.lock().unlock()-}--concrete JustWait {-  refines Routine--  @type create (ConditionWait) -> (JustWait)-}--define JustWait {-  @value ConditionWait cond--  create (cond) {-    return JustWait{ cond }-  }--  run () {-    scoped {-      MutexLock lock <- MutexLock.lock(cond)-    } cleanup {-      \ lock.freeResource()-    } in \ cond.wait()-  }-}--concrete WaitAndUpdate {-  refines Routine--  @type create (Value,ConditionWait) -> (WaitAndUpdate)-}--define WaitAndUpdate {-  @value Value value-  @value ConditionWait cond--  create (value,cond) {-    return WaitAndUpdate{ value, cond }-  }--  run () {-    scoped {-      MutexLock lock <- MutexLock.lock(cond)-      // Increment once, to indicate readiness.-      \ value.increment()-    } cleanup {-      \ lock.freeResource()-    } in {-      // Mutex isn't unlocked until blocked here.-      if (cond.timedWait(0.1)) {-        \ value.increment()-      }-    }-  }-}---testcase "wait() crashes if not locked first" {-  crash-  require "waiting for condition"-}--unittest test {-  \ ThreadCondition.new().wait()-}---testcase "timedWait() crashes if not locked first" {-  crash-  require "waiting for condition"-}--unittest test {-  \ ThreadCondition.new().timedWait(0.1)-}---testcase "timedWait() crashes with negative wait" {-  crash-  require "-0\.1"-}--unittest test {-  \ ThreadCondition.new().lock().timedWait(-0.1)-}
lib/thread/src/Extension_Enumerated.cpp view
@@ -102,7 +102,7 @@ #endif  // ZEOLITE_PRIVATE_NAMESPACE  BoxedValue CreateValue_EnumeratedWait(-  S<Type_EnumeratedWait> parent, S<Barrier> b, int i);+  S<const Type_EnumeratedWait> parent, S<Barrier> b, int i);  struct ExtCategory_EnumeratedWait : public Category_EnumeratedWait { };@@ -112,8 +112,8 @@ };  struct ExtValue_EnumeratedWait : public Value_EnumeratedWait {-  inline ExtValue_EnumeratedWait(S<Type_EnumeratedWait> p, S<Barrier> b, int i)-    : Value_EnumeratedWait(p), barrier(b), index(i) {}+  inline ExtValue_EnumeratedWait(S<const Type_EnumeratedWait> p, S<Barrier> b, int i)+    : Value_EnumeratedWait(std::move(p)), barrier(b), index(i) {}    ReturnTuple Call_wait(const ParamTuple& params, const ValueTuple& args) final {     TRACE_FUNCTION("EnumeratedWait.wait")@@ -133,13 +133,17 @@   static auto& category = *new ExtCategory_EnumeratedWait();   return category; }-S<Type_EnumeratedWait> CreateType_EnumeratedWait(Params<0>::Type params) {++S<const Type_EnumeratedWait> CreateType_EnumeratedWait(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_EnumeratedWait(CreateCategory_EnumeratedWait(), Params<0>::Type()));   return cached; }++void RemoveType_EnumeratedWait(const Params<0>::Type& params) {}+ BoxedValue CreateValue_EnumeratedWait(-  S<Type_EnumeratedWait> parent, S<Barrier> b, int i) {-  return BoxedValue::New<ExtValue_EnumeratedWait>(parent, b, i);+  S<const Type_EnumeratedWait> parent, S<Barrier> b, int i) {+  return BoxedValue::New<ExtValue_EnumeratedWait>(std::move(parent), std::move(b), i); }  #ifdef ZEOLITE_PRIVATE_NAMESPACE@@ -153,7 +157,7 @@ #endif  // ZEOLITE_PUBLIC_NAMESPACE  BoxedValue CreateValue_EnumeratedBarrier(-  S<Type_EnumeratedBarrier> parent, std::vector<BoxedValue> w);+  S<const Type_EnumeratedBarrier> parent, std::vector<BoxedValue> w);  struct ExtCategory_EnumeratedBarrier : public Category_EnumeratedBarrier { };@@ -161,7 +165,7 @@ struct ExtType_EnumeratedBarrier : public Type_EnumeratedBarrier {   inline ExtType_EnumeratedBarrier(Category_EnumeratedBarrier& p, Params<0>::Type params) : Type_EnumeratedBarrier(p, params) {} -  ReturnTuple Call_new(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_new(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("EnumeratedBarrier.new")     const PrimInt Var_arg1 = (args.At(0)).AsInt();     if (Var_arg1 < 0) {@@ -174,13 +178,13 @@         CreateType_EnumeratedWait(Params<0>::Type()), barrier, i);       waits.push_back(wait);     }-    return ReturnTuple(CreateValue_EnumeratedBarrier(shared_from_this(), std::move(waits)));+    return ReturnTuple(CreateValue_EnumeratedBarrier(PARAM_SELF, std::move(waits)));   } };  struct ExtValue_EnumeratedBarrier : public Value_EnumeratedBarrier {-  inline ExtValue_EnumeratedBarrier(S<Type_EnumeratedBarrier> p, std::vector<BoxedValue> w)-    : Value_EnumeratedBarrier(p), waits(std::move(w)) {}+  inline ExtValue_EnumeratedBarrier(S<const Type_EnumeratedBarrier> p, std::vector<BoxedValue> w)+    : Value_EnumeratedBarrier(std::move(p)), waits(std::move(w)) {}    ReturnTuple Call_readAt(const ParamTuple& params, const ValueTuple& args) final {     TRACE_FUNCTION("EnumeratedBarrier.readAt")@@ -205,14 +209,16 @@   return category; } -S<Type_EnumeratedBarrier> CreateType_EnumeratedBarrier(Params<0>::Type params) {+S<const Type_EnumeratedBarrier> CreateType_EnumeratedBarrier(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_EnumeratedBarrier(CreateCategory_EnumeratedBarrier(), Params<0>::Type()));   return cached; } +void RemoveType_EnumeratedBarrier(const Params<0>::Type& params) {}+ BoxedValue CreateValue_EnumeratedBarrier(-  S<Type_EnumeratedBarrier> parent, std::vector<BoxedValue> w) {-  return BoxedValue::New<ExtValue_EnumeratedBarrier>(parent, std::move(w));+  S<const Type_EnumeratedBarrier> parent, std::vector<BoxedValue> w) {+  return BoxedValue::New<ExtValue_EnumeratedBarrier>(std::move(parent), std::move(w)); }  #ifdef ZEOLITE_PUBLIC_NAMESPACE
lib/thread/src/Extension_ProcessThread.cpp view
@@ -31,7 +31,7 @@ namespace ZEOLITE_PUBLIC_NAMESPACE { #endif  // ZEOLITE_PUBLIC_NAMESPACE -BoxedValue CreateValue_ProcessThread(S<Type_ProcessThread> parent, const ValueTuple& args);+BoxedValue CreateValue_ProcessThread(S<const Type_ProcessThread> parent, const ValueTuple& args);  struct ExtCategory_ProcessThread : public Category_ProcessThread { };@@ -39,15 +39,15 @@ struct ExtType_ProcessThread : public Type_ProcessThread {   inline ExtType_ProcessThread(Category_ProcessThread& p, Params<0>::Type params) : Type_ProcessThread(p, params) {} -  ReturnTuple Call_from(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_from(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("ProcessThread.from")-    return ReturnTuple(CreateValue_ProcessThread(shared_from_this(), args));+    return ReturnTuple(CreateValue_ProcessThread(PARAM_SELF, args));   } };  struct ExtValue_ProcessThread : public Value_ProcessThread {-  inline ExtValue_ProcessThread(S<Type_ProcessThread> p, const ValueTuple& args)-    : Value_ProcessThread(p), routine(args.Only()) {}+  inline ExtValue_ProcessThread(S<const Type_ProcessThread> p, const ValueTuple& args)+    : Value_ProcessThread(std::move(p)), routine(args.Only()) {}    ReturnTuple Call_detach(const ParamTuple& params, const ValueTuple& args) final {     TRACE_FUNCTION("ProcessThread.detach")@@ -119,13 +119,15 @@   return category; } -S<Type_ProcessThread> CreateType_ProcessThread(Params<0>::Type params) {+S<const Type_ProcessThread> CreateType_ProcessThread(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_ProcessThread(CreateCategory_ProcessThread(), Params<0>::Type()));   return cached; } -BoxedValue CreateValue_ProcessThread(S<Type_ProcessThread> parent, const ValueTuple& args) {-  return BoxedValue::New<ExtValue_ProcessThread>(parent, args);+void RemoveType_ProcessThread(const Params<0>::Type& params) {}++BoxedValue CreateValue_ProcessThread(S<const Type_ProcessThread> parent, const ValueTuple& args) {+  return BoxedValue::New<ExtValue_ProcessThread>(std::move(parent), args); }  #ifdef ZEOLITE_PUBLIC_NAMESPACE
lib/thread/src/Extension_Realtime.cpp view
@@ -44,7 +44,7 @@ struct ExtType_Realtime : public Type_Realtime {   inline ExtType_Realtime(Category_Realtime& p, Params<0>::Type params) : Type_Realtime(p, params) {} -  ReturnTuple Call_sleepSeconds(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_sleepSeconds(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Realtime.sleepSeconds")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();     if (Var_arg1 > 0) {@@ -53,7 +53,7 @@     return ReturnTuple();   } -  ReturnTuple Call_sleepSecondsPrecise(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_sleepSecondsPrecise(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Realtime.sleepSecondsPrecise")     const PrimFloat Var_arg1 = (args.At(0)).AsFloat(); @@ -78,7 +78,7 @@     return ReturnTuple();   } -  ReturnTuple Call_monoSeconds(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_monoSeconds(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Realtime.monoSeconds")     const auto time = std::chrono::steady_clock::now().time_since_epoch();     const PrimFloat seconds =@@ -93,10 +93,12 @@   return category; } -S<Type_Realtime> CreateType_Realtime(Params<0>::Type params) {+S<const Type_Realtime> CreateType_Realtime(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_Realtime(CreateCategory_Realtime(), Params<0>::Type()));   return cached; }++void RemoveType_Realtime(const Params<0>::Type& params) {}  #ifdef ZEOLITE_PUBLIC_NAMESPACE }  // namespace ZEOLITE_PUBLIC_NAMESPACE
lib/thread/src/Extension_ThreadCondition.cpp view
@@ -32,7 +32,7 @@ namespace ZEOLITE_PUBLIC_NAMESPACE { #endif  // ZEOLITE_PUBLIC_NAMESPACE -BoxedValue CreateValue_ThreadCondition(S<Type_ThreadCondition> parent);+BoxedValue CreateValue_ThreadCondition(S<const Type_ThreadCondition> parent);  struct ExtCategory_ThreadCondition : public Category_ThreadCondition { };@@ -40,14 +40,15 @@ struct ExtType_ThreadCondition : public Type_ThreadCondition {   inline ExtType_ThreadCondition(Category_ThreadCondition& p, Params<0>::Type params) : Type_ThreadCondition(p, params) {} -  ReturnTuple Call_new(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_new(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("ThreadCondition.new")-    return ReturnTuple(CreateValue_ThreadCondition(shared_from_this()));+    return ReturnTuple(CreateValue_ThreadCondition(PARAM_SELF));   } };  struct ExtValue_ThreadCondition : public Value_ThreadCondition {-  inline ExtValue_ThreadCondition(S<Type_ThreadCondition> p) : Value_ThreadCondition(p) {+  inline ExtValue_ThreadCondition(S<const Type_ThreadCondition> p)+    : Value_ThreadCondition(std::move(p)) {     pthread_mutexattr_t mutex_attr;     pthread_mutexattr_init(&mutex_attr);     // NOTE: Error checking is required to catch attempted waits without first@@ -151,13 +152,15 @@   return category; } -S<Type_ThreadCondition> CreateType_ThreadCondition(Params<0>::Type params) {+S<const Type_ThreadCondition> CreateType_ThreadCondition(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_ThreadCondition(CreateCategory_ThreadCondition(), Params<0>::Type()));   return cached; } -BoxedValue CreateValue_ThreadCondition(S<Type_ThreadCondition> parent) {-  return BoxedValue::New<ExtValue_ThreadCondition>(parent);+void RemoveType_ThreadCondition(const Params<0>::Type& params) {}++BoxedValue CreateValue_ThreadCondition(S<const Type_ThreadCondition> parent) {+  return BoxedValue::New<ExtValue_ThreadCondition>(std::move(parent)); }  #ifdef ZEOLITE_PUBLIC_NAMESPACE
+ lib/thread/src/barrier-ext.0rp view
@@ -0,0 +1,23 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++$ModuleOnly$++concrete EnumeratedWait {+  refines BarrierWait+}
+ lib/thread/src/testing.0rp view
@@ -0,0 +1,42 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++$ModuleOnly$+$TestsOnly$++concrete NoOpRoutine {+  refines Routine++  @type create () -> (NoOpRoutine)+}++concrete InfiniteRoutine {+  refines Routine++  @type create () -> (InfiniteRoutine)+}++concrete Value {+  @type create () -> (Value)++  @value get () -> (Int)+  @value increment () -> ()++  @value setInUse (Bool) -> ()+  @value getInUse () -> (Bool)+}
+ lib/thread/src/testing.0rx view
@@ -0,0 +1,64 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++$TestsOnly$++define NoOpRoutine {+  create () {+    return NoOpRoutine{ }+  }++  run () {}+}++define InfiniteRoutine {+  create () {+    return InfiniteRoutine{ }+  }++  run () { $NoTrace$+    while (true) {+      \ Realtime.sleepSeconds(60.0)+    }+  }+}++define Value {+  @value Int value+  @value Bool inUse++  create () {+    return Value{ 0, false }+  }++  get () {+    return value+  }++  increment () {+    value <- value+1+  }++  setInUse (i) {+    inUse <- i+  }++  getInUse () {+    return inUse+  }+}
+ lib/thread/test/barrier.0rt view
@@ -0,0 +1,178 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "EnumeratedBarrier tests" {+  success+}++unittest correctCount {+  ReadAt<BarrierWait> barriers <- EnumeratedBarrier.new(13)+  \ Testing.checkEquals<?>(barriers.size(),13)+}++unittest waitForOneThread {+  ReadAt<BarrierWait> barriers <- EnumeratedBarrier.new(1)+  \ barriers.readAt(0).wait()+}++unittest zeroAllowed {+  ReadAt<BarrierWait> barriers <- EnumeratedBarrier.new(0)+  \ Testing.checkEquals<?>(barriers.size(),0)+}++unittest waitForMultipleThreads {+  Value value <- Value.create()+  Mutex mutex <- SimpleMutex.new()+  ReadAt<BarrierWait> barriers <- EnumeratedBarrier.new(3)++  Thread thread1 <- ProcessThread.from(WaitAndIncrement.create(2,value,mutex,barriers.readAt(1))).start()+  Thread thread2 <- ProcessThread.from(WaitAndIncrement.create(2,value,mutex,barriers.readAt(2))).start()++  \ barriers.readAt(0).wait()+  \ Testing.checkEquals<?>(value.get(),2)++  \ barriers.readAt(0).wait()+  \ Testing.checkEquals<?>(value.get(),4)++  \ thread1.join()+  \ thread2.join()+}++concrete WaitAndIncrement {+  refines Routine++  @type create (Int,Value,Mutex,BarrierWait) -> (WaitAndIncrement)+}++define WaitAndIncrement {+  @value Int count+  @value Value value+  @value Mutex mutex+  @value BarrierWait barrier++  create (count,value,mutex,barrier) {+    return WaitAndIncrement{ count, value, mutex, barrier }+  }++  run () {+    scoped {+      Int i <- 0+    } in while (i < count) {+      $Hidden[i,count]$+      scoped {+        MutexLock lock <- MutexLock.lock(mutex)+      } cleanup {+        \ lock.freeResource()+      } in {+        \ value.increment()+      }+      \ barrier.wait()+      // Gives the main thread time to read the value.+      \ Realtime.sleepSeconds(0.01)+    } update {+      i <- i+1+    }+  }+}+++testcase "EnumeratedBarrier crashes with negative count" {+  crash+  require "-4"+}++unittest test {+  ReadAt<BarrierWait> barriers <- EnumeratedBarrier.new(-4)+}+++testcase "wait() crashes when references are lost" {+  crash+  require "BarrierWait.*destroyed"+}++unittest test {+  scoped {+    ReadAt<BarrierWait> barriers <- EnumeratedBarrier.new(2)+  } in BarrierWait barrier <- barriers.readAt(0)+  \ barrier.wait()+}+++testcase "references lost during wait() causes crash" {+  crash+  require "BarrierWait.*waiting"+}++unittest test {+  scoped {+    ReadAt<BarrierWait> barriers <- EnumeratedBarrier.new(2)+  } in {+    Thread thread <- ProcessThread.from(WaitAndExit.create(barriers.readAt(0))).start()+    // Give the thread time to start.+    \ Realtime.sleepSeconds(0.1)+  }+}++concrete WaitAndExit {+  refines Routine++  @type create (BarrierWait) -> (WaitAndExit)+}++define WaitAndExit {+  @value BarrierWait barrier++  create (barrier) {+    return WaitAndExit{ barrier }+  }++  run () {+    \ barrier.wait()+  }+}+++testcase "wait() crashes when two threads use same BarrierWait" {+  crash+  require "BarrierWait.*in use"+}++unittest test {+  ReadAt<BarrierWait> barriers <- EnumeratedBarrier.new(2)+  Thread thread <- ProcessThread.from(WaitAndExit.create(barriers.readAt(0))).start()+  \ barriers.readAt(0).wait()+}++concrete WaitAndExit {+  refines Routine++  @type create (BarrierWait) -> (WaitAndExit)+}++define WaitAndExit {+  @value BarrierWait barrier++  create (barrier) {+    return WaitAndExit{ barrier }+  }++  run () {+    \ barrier.wait()+  }+}
+ lib/thread/test/condition.0rt view
@@ -0,0 +1,179 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "ThreadCondition tests" {+  success+}++unittest resumeAll {+  Value value <- Value.create()+  ThreadCondition cond <- ThreadCondition.new()++  Thread thread1 <- ProcessThread.from(WaitAndUpdate.create(value,cond)).start()+  Thread thread2 <- ProcessThread.from(WaitAndUpdate.create(value,cond)).start()++  // Wait for both threads to increment Value once, to indicate readiness.+  while (true) {+    scoped {+      MutexLock lock <- MutexLock.lock(cond)+    } cleanup {+      \ lock.freeResource()+    } in if (value.get() >= 2) {+      break+    }+  }++  \ cond.resumeAll()+  \ thread1.join()+  \ thread2.join()++  \ Testing.checkEquals<?>(value.get(),4)+}++unittest resumeOne {+  Value value <- Value.create()+  ThreadCondition cond <- ThreadCondition.new()++  Thread thread1 <- ProcessThread.from(WaitAndUpdate.create(value,cond)).start()+  Thread thread2 <- ProcessThread.from(WaitAndUpdate.create(value,cond)).start()++  \ Realtime.sleepSeconds(0.01)++  // Wait for both threads to increment Value once, to indicate readiness.+  while (true) {+    scoped {+      MutexLock lock <- MutexLock.lock(cond)+    } cleanup {+      \ lock.freeResource()+    } in if (value.get() >= 2) {+      break+    }+  }++  \ cond.resumeOne()+  \ thread1.join()+  \ thread2.join()++  \ Testing.checkEquals<?>(value.get(),3)+}++unittest forceWaitTimeout {+  Value value <- Value.create()+  ThreadCondition cond <- ThreadCondition.new()++  Thread thread <- ProcessThread.from(WaitAndUpdate.create(value,cond)).start()++  \ Realtime.sleepSeconds(0.2)+  \ cond.resumeOne()+  \ thread.join()++  \ Testing.checkEquals<?>(value.get(),1)+}++unittest waitNoTimeout {+  ThreadCondition cond <- ThreadCondition.new()++  Thread thread <- ProcessThread.from(JustWait.create(cond)).start()++  \ Realtime.sleepSeconds(0.1)+  \ cond.resumeOne()+  \ thread.join()+  \ cond.lock().unlock()+}++concrete JustWait {+  refines Routine++  @type create (ConditionWait) -> (JustWait)+}++define JustWait {+  @value ConditionWait cond++  create (cond) {+    return JustWait{ cond }+  }++  run () {+    scoped {+      MutexLock lock <- MutexLock.lock(cond)+    } cleanup {+      \ lock.freeResource()+    } in \ cond.wait()+  }+}++concrete WaitAndUpdate {+  refines Routine++  @type create (Value,ConditionWait) -> (WaitAndUpdate)+}++define WaitAndUpdate {+  @value Value value+  @value ConditionWait cond++  create (value,cond) {+    return WaitAndUpdate{ value, cond }+  }++  run () {+    scoped {+      MutexLock lock <- MutexLock.lock(cond)+      // Increment once, to indicate readiness.+      \ value.increment()+    } cleanup {+      \ lock.freeResource()+    } in {+      // Mutex isn't unlocked until blocked here.+      if (cond.timedWait(0.1)) {+        \ value.increment()+      }+    }+  }+}+++testcase "wait() crashes if not locked first" {+  crash+  require "waiting for condition"+}++unittest test {+  \ ThreadCondition.new().wait()+}+++testcase "timedWait() crashes if not locked first" {+  crash+  require "waiting for condition"+}++unittest test {+  \ ThreadCondition.new().timedWait(0.1)+}+++testcase "timedWait() crashes with negative wait" {+  crash+  require "-0\.1"+}++unittest test {+  \ ThreadCondition.new().lock().timedWait(-0.1)+}
+ lib/thread/test/thread.0rt view
@@ -0,0 +1,245 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "simple Thread test" {+  success+}++unittest test {+  CountLoop counter <- CountLoop.create(10000)+  \ Testing.checkEquals<?>(counter.get(),0)+  \ counter.start()+  \ Testing.checkEquals<?>(counter.get(),10000)+  \ counter.start()+  \ Testing.checkEquals<?>(counter.get(),20000)+}++concrete CountLoop {+  refines Process++  @type create (Int) -> (CountLoop)+  @value get () -> (Int)+}++define CountLoop {+  refines Routine++  @value Int current+  @value Int count+  @value weak Thread thread++  create (c) {+    return CountLoop{ 0, c, empty }+  }++  start ()  {+    // Starting it right away causes the thread function to take a reference to+    // thread. This means that once it exits, thread will become empty.+    thread <- ProcessThread.from(self).start()+    return self+  }++  run () {+    scoped {+      Int i <- 0+    } in while (i < count) {+      current <- current+1+    } update {+      i <- i+1+    }+  }++  get () {+    optional Thread thread2 <- strong(thread)+    if (present(thread2)) {+      if (!require(thread2).isRunning()) {+        fail("thread has not been started yet")+      } else {+        \ require(thread2).join()+      }+    }+    return current+  }+}+++testcase "ProcessThread mutex integration test" {+  success+}++unittest test {+  Mutex mutex <- SimpleMutex.new()+  Value value <- Value.create()+  Thread evenThread <- ProcessThread.from(EvenOrOdd.create(mutex,true, 10,value)).start()+  Thread oddThread  <- ProcessThread.from(EvenOrOdd.create(mutex,false,10,value)).start()+  \ evenThread.join()+  \ oddThread.join()+  \ Testing.checkEquals<?>(value.get(),20)+}++concrete EvenOrOdd {+  refines Routine++  // Args:+  // - Mutex: Locks the Value.+  // - Bool:  Increment only on even if true, or on odd if false.+  // - Int:   Number of increment operations.+  // - Value: Value to increment.+  @type create (Mutex,Bool,Int,Value) -> (Routine)+}++define EvenOrOdd {+  @value Mutex mutex+  @value Bool even+  @value Int count+  @value Value value++  create (m,e,c,v) {+    return EvenOrOdd{ m, e, c, v }+  }++  run () {+    scoped {+      Int i <- 0+    } in while (i < count) {+      $Hidden[count]$+      scoped {+        MutexLock lock <- MutexLock.lock(mutex)+        $Hidden[mutex]$+        if (value.getInUse()) {+          fail("value in use")+        } else {+          \ value.setInUse(true)+        }+      } cleanup {+        \ value.setInUse(false)+        \ lock.freeResource()+      } in {+        $Hidden[lock]$+        if ((value.get()%2 == 0) ^ !even) {+          i <- i+1+          \ value.increment()+        }+      }+    }+  }+}++++testcase "Argv available in Thread" {+  success+  args "arg1"+}++unittest test {+  \ ProcessThread.from(CheckArgv.create()).start().join()+}++concrete CheckArgv {+  refines Routine++  @type create () -> (CheckArgv)+}++define CheckArgv {+  create () {+    return CheckArgv{ }+  }++  run () {+    \ Testing.checkEquals<?>(Argv.global().readAt(1),"arg1")+  }+}+++testcase "join() crashes if Thread not started yet" {+  crash+  require "thread.*started"+}++unittest test {+  \ ProcessThread.from(NoOpRoutine.create()).join()+}+++testcase "join() once" {+  success+}++unittest test {+  \ ProcessThread.from(NoOpRoutine.create()).start().join()+}+++testcase "join() twice crashes" {+  crash+  require "thread.*started"+}++unittest test {+  \ ProcessThread.from(NoOpRoutine.create()).start().join().join()+}+++testcase "detach() crashes if Thread not started yet" {+  crash+  require "thread.*started"+}++unittest test {+  \ ProcessThread.from(InfiniteRoutine.create()).detach()+}+++testcase "detach() once" {+  success+}++unittest test {+  \ ProcessThread.from(InfiniteRoutine.create()).start().detach()+}+++testcase "detach() twice crashes" {+  crash+  require "thread.*started"+}++unittest test {+  \ ProcessThread.from(InfiniteRoutine.create()).start().detach().detach()+}+++testcase "weak Thread frees on exit" {+  success+}++unittest startAndJoin {+  weak Thread thread <- ProcessThread.from(NoOpRoutine.create()).start().join()+  if (present(strong(thread))) {+    fail("thread is still present")+  }+}++unittest notStarted {+  weak Thread thread <- ProcessThread.from(NoOpRoutine.create())+  if (present(strong(thread))) {+    fail("thread is still present")+  }+}
+ lib/thread/test/time.0rt view
@@ -0,0 +1,79 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "zero sleep is allowed" {+  success+}++unittest zeroSleep {+  \ Realtime.sleepSeconds(0.0)+}++unittest negativeSleep {+  \ Realtime.sleepSeconds(-0.1)+}++unittest zeroPrecise {+  \ Realtime.sleepSecondsPrecise(0.0)+}++unittest negativePrecise {+  \ Realtime.sleepSecondsPrecise(-0.1)+}+++testcase "sleepSeconds() does not interfere with test timeout" {+  crash+  require "signal 14"+  timeout 1+}++unittest test {+  \ Realtime.sleepSeconds(5.0)+}+++testcase "sleepSecondsPrecise() does not interfere with test timeout" {+  crash+  require "signal 14"+  timeout 1+}++unittest test {+  \ Realtime.sleepSecondsPrecise(5.0)+}+++testcase "monoSeconds() diff is somewhat accurate" {+  success+  timeout 2+}++unittest testSleep {+  Float start <- Realtime.monoSeconds()+  \ Realtime.sleepSeconds(0.5)+  Float stop <- Realtime.monoSeconds()+  \ Testing.checkBetween<?>(stop-start,0.5,0.6)+}++unittest testPrecise {+  Float start <- Realtime.monoSeconds()+  \ Realtime.sleepSecondsPrecise(0.5)+  Float stop <- Realtime.monoSeconds()+  \ Testing.checkBetween<?>(stop-start,0.5,0.51)+}
− lib/thread/testing.0rp
@@ -1,42 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--$ModuleOnly$-$TestsOnly$--concrete NoOpRoutine {-  refines Routine--  @type create () -> (NoOpRoutine)-}--concrete InfiniteRoutine {-  refines Routine--  @type create () -> (InfiniteRoutine)-}--concrete Value {-  @type create () -> (Value)--  @value get () -> (Int)-  @value increment () -> ()--  @value setInUse (Bool) -> ()-  @value getInUse () -> (Bool)-}
− lib/thread/testing.0rx
@@ -1,64 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--$TestsOnly$--define NoOpRoutine {-  create () {-    return NoOpRoutine{ }-  }--  run () {}-}--define InfiniteRoutine {-  create () {-    return InfiniteRoutine{ }-  }--  run () { $NoTrace$-    while (true) {-      \ Realtime.sleepSeconds(60.0)-    }-  }-}--define Value {-  @value Int value-  @value Bool inUse--  create () {-    return Value{ 0, false }-  }--  get () {-    return value-  }--  increment () {-    value <- value+1-  }--  setInUse (i) {-    inUse <- i-  }--  getInUse () {-    return inUse-  }-}
− lib/thread/thread.0rt
@@ -1,245 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "simple Thread test" {-  success-}--unittest test {-  CountLoop counter <- CountLoop.create(10000)-  \ Testing.checkEquals<?>(counter.get(),0)-  \ counter.start()-  \ Testing.checkEquals<?>(counter.get(),10000)-  \ counter.start()-  \ Testing.checkEquals<?>(counter.get(),20000)-}--concrete CountLoop {-  refines Process--  @type create (Int) -> (CountLoop)-  @value get () -> (Int)-}--define CountLoop {-  refines Routine--  @value Int current-  @value Int count-  @value weak Thread thread--  create (c) {-    return CountLoop{ 0, c, empty }-  }--  start ()  {-    // Starting it right away causes the thread function to take a reference to-    // thread. This means that once it exits, thread will become empty.-    thread <- ProcessThread.from(self).start()-    return self-  }--  run () {-    scoped {-      Int i <- 0-    } in while (i < count) {-      current <- current+1-    } update {-      i <- i+1-    }-  }--  get () {-    optional Thread thread2 <- strong(thread)-    if (present(thread2)) {-      if (!require(thread2).isRunning()) {-        fail("thread has not been started yet")-      } else {-        \ require(thread2).join()-      }-    }-    return current-  }-}---testcase "ProcessThread mutex integration test" {-  success-}--unittest test {-  Mutex mutex <- SimpleMutex.new()-  Value value <- Value.create()-  Thread evenThread <- ProcessThread.from(EvenOrOdd.create(mutex,true, 10,value)).start()-  Thread oddThread  <- ProcessThread.from(EvenOrOdd.create(mutex,false,10,value)).start()-  \ evenThread.join()-  \ oddThread.join()-  \ Testing.checkEquals<?>(value.get(),20)-}--concrete EvenOrOdd {-  refines Routine--  // Args:-  // - Mutex: Locks the Value.-  // - Bool:  Increment only on even if true, or on odd if false.-  // - Int:   Number of increment operations.-  // - Value: Value to increment.-  @type create (Mutex,Bool,Int,Value) -> (Routine)-}--define EvenOrOdd {-  @value Mutex mutex-  @value Bool even-  @value Int count-  @value Value value--  create (m,e,c,v) {-    return EvenOrOdd{ m, e, c, v }-  }--  run () {-    scoped {-      Int i <- 0-    } in while (i < count) {-      $Hidden[count]$-      scoped {-        MutexLock lock <- MutexLock.lock(mutex)-        $Hidden[mutex]$-        if (value.getInUse()) {-          fail("value in use")-        } else {-          \ value.setInUse(true)-        }-      } cleanup {-        \ value.setInUse(false)-        \ lock.freeResource()-      } in {-        $Hidden[lock]$-        if ((value.get()%2 == 0) ^ !even) {-          i <- i+1-          \ value.increment()-        }-      }-    }-  }-}----testcase "Argv available in Thread" {-  success-  args "arg1"-}--unittest test {-  \ ProcessThread.from(CheckArgv.create()).start().join()-}--concrete CheckArgv {-  refines Routine--  @type create () -> (CheckArgv)-}--define CheckArgv {-  create () {-    return CheckArgv{ }-  }--  run () {-    \ Testing.checkEquals<?>(Argv.global().readAt(1),"arg1")-  }-}---testcase "join() crashes if Thread not started yet" {-  crash-  require "thread.*started"-}--unittest test {-  \ ProcessThread.from(NoOpRoutine.create()).join()-}---testcase "join() once" {-  success-}--unittest test {-  \ ProcessThread.from(NoOpRoutine.create()).start().join()-}---testcase "join() twice crashes" {-  crash-  require "thread.*started"-}--unittest test {-  \ ProcessThread.from(NoOpRoutine.create()).start().join().join()-}---testcase "detach() crashes if Thread not started yet" {-  crash-  require "thread.*started"-}--unittest test {-  \ ProcessThread.from(InfiniteRoutine.create()).detach()-}---testcase "detach() once" {-  success-}--unittest test {-  \ ProcessThread.from(InfiniteRoutine.create()).start().detach()-}---testcase "detach() twice crashes" {-  crash-  require "thread.*started"-}--unittest test {-  \ ProcessThread.from(InfiniteRoutine.create()).start().detach().detach()-}---testcase "weak Thread frees on exit" {-  success-}--unittest startAndJoin {-  weak Thread thread <- ProcessThread.from(NoOpRoutine.create()).start().join()-  if (present(strong(thread))) {-    fail("thread is still present")-  }-}--unittest notStarted {-  weak Thread thread <- ProcessThread.from(NoOpRoutine.create())-  if (present(strong(thread))) {-    fail("thread is still present")-  }-}
− lib/thread/time.0rt
@@ -1,79 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "zero sleep is allowed" {-  success-}--unittest zeroSleep {-  \ Realtime.sleepSeconds(0.0)-}--unittest negativeSleep {-  \ Realtime.sleepSeconds(-0.1)-}--unittest zeroPrecise {-  \ Realtime.sleepSecondsPrecise(0.0)-}--unittest negativePrecise {-  \ Realtime.sleepSecondsPrecise(-0.1)-}---testcase "sleepSeconds() does not interfere with test timeout" {-  crash-  require "signal 14"-  timeout 1-}--unittest test {-  \ Realtime.sleepSeconds(5.0)-}---testcase "sleepSecondsPrecise() does not interfere with test timeout" {-  crash-  require "signal 14"-  timeout 1-}--unittest test {-  \ Realtime.sleepSecondsPrecise(5.0)-}---testcase "monoSeconds() diff is somewhat accurate" {-  success-  timeout 2-}--unittest testSleep {-  Float start <- Realtime.monoSeconds()-  \ Realtime.sleepSeconds(0.5)-  Float stop <- Realtime.monoSeconds()-  \ Testing.checkBetween<?>(stop-start,0.5,0.6)-}--unittest testPrecise {-  Float start <- Realtime.monoSeconds()-  \ Realtime.sleepSecondsPrecise(0.5)-  Float stop <- Realtime.monoSeconds()-  \ Testing.checkBetween<?>(stop-start,0.5,0.51)-}
lib/util/.zeolite-module view
@@ -1,5 +1,9 @@ root: "../.." path: "lib/util"+extra_paths: [+  "lib/util/src"+  "lib/util/test"+] expression_map: [   expression_macro {     name: BLOCK_READ_SIZE
− lib/util/counters.0rt
@@ -1,116 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "counter tests" {-  success-}--unittest zeroIndexed {-  Int index <- 0--  traverse (Counter.zeroIndexed(10) -> Int i) {-    \ Testing.checkEquals<?>(i,index)-    index <- index+1-  }--  \ Testing.checkEquals<?>(index,10)-}--unittest revZeroIndexed {-  Int index <- 10--  traverse (Counter.revZeroIndexed(10) -> Int i) {-    index <- index-1-    \ Testing.checkEquals<?>(i,index)-  }--  \ Testing.checkEquals<?>(index,0)-}--unittest unlimitedCount {-  Int index <- 0--  traverse (Counter.unlimited() -> Int i) {-    \ Testing.checkEquals<?>(i,index)-    if ((index <- index+1) >= 10) {-      break-    }-  }--  \ Testing.checkEquals<?>(index,10)-}--unittest times {-  Int index <- 0--  traverse ("message" `Repeat:times<?>` 10 -> String m) {-    \ Testing.checkEquals<?>(m,"message")-    index <- index+1-  }--  \ Testing.checkEquals<?>(index,10)-}--unittest unlimitedValue {-  Int index <- 0--  traverse (Repeat:unlimited<?>("message") -> String m) {-    \ Testing.checkEquals<?>(m,"message")-    if ((index <- index+1) >= 10) {-      break-    }-  }--  \ Testing.checkEquals<?>(index,10)-}--unittest minMax {-  scoped {-    Int x, Int y <- Ranges:minMax<?>(10,3)-  } in {-    \ Testing.checkEquals<?>(x,3)-    \ Testing.checkEquals<?>(y,10)-  }--  scoped {-    Int x, Int y <- Ranges:minMax<?>(3,10)-  } in {-    \ Testing.checkEquals<?>(x,3)-    \ Testing.checkEquals<?>(y,10)-  }-}--unittest min {-  scoped {-    Int x <- Ranges:min<?>(10,3)-  } in \ Testing.checkEquals<?>(x,3)--  scoped {-    Int x <- Ranges:min<?>(3,10)-  } in \ Testing.checkEquals<?>(x,3)-}--unittest max {-  scoped {-    Int x <- Ranges:max<?>(10,3)-  } in \ Testing.checkEquals<?>(x,10)--  scoped {-    Int x <- Ranges:max<?>(3,10)-  } in \ Testing.checkEquals<?>(x,10)-}
− lib/util/counters.0rx
@@ -1,116 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--define Counter {-  $ReadOnly[limit,incr]$--  refines Order<Int>--  @value Int current-  @value optional Int limit-  @value Int incr--  zeroIndexed (max) {-    if (max <= 0) {-      return empty-    } else {-      return Counter{ 0, max, 1 }-    }-  }--  revZeroIndexed (max) {-    if (max <= 0) {-      return empty-    } else {-      return Counter{ max-1, -1, -1 }-    }-  }--  unlimited () {-    return Counter{ 0, empty, 1 }-  }--  next () {-    if (!present(limit) ||-        (incr < 0 && current+incr > require(limit)) ||-        (incr > 0 && current+incr < require(limit))) {-      current <- current+incr-      return self-    } else {-      return empty-    }-  }--  get () {-    return current-  }-}--define Repeat {-  $ReadOnly[limit,value]$--  refines Order<#x>--  @value #x value-  @value Int current-  @value optional Int limit--  times (value,max) {-    if (max <= 0) {-      return empty-    } else {-      return Repeat<#y>{ value, 0, max }-    }-  }--  unlimited (value) {-    return Repeat<#y>{ value, 0, empty }-  }--  next () {-    if (!present(limit) || current+1 < require(limit)) {-      current <- current+1-      return self-    } else {-      return empty-    }-  }--  get () {-    return value-  }-}--define Ranges {-  min (x,y) (z) {-    z, _ <- minMax<?>(x,y)-  }--  max (x,y) (z) {-    _, z <- minMax<?>(x,y)-  }--  minMax (x,y) {-    // NOTE: Using y < x makes the default x, y when they are equal.-    if (y `#x.lessThan` x) {-      return y, x-    } else {-      return x, y-    }-  }-}
lib/util/extra.0rp view
@@ -22,6 +22,8 @@ // - The built-in type with no values is the meta-type all. Void should be used //   when an actual value is required but no other type makes sense. concrete Void {+  immutable+   refines Formatted    defines Equals<Void>@@ -54,6 +56,8 @@  // Command-line program arguments. concrete Argv {+  immutable+   refines ReadAt<String>   refines SubSequence 
− lib/util/extra.0rt
@@ -1,118 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "Argv checks" {-  success-  args "arg1" "arg2" "arg3" "arg4"-}--unittest global {-  Int count <- Argv.global().size()-  \ Testing.checkEquals<?>(count,5)-  \ Testing.checkEquals<?>(Argv.global().readAt(0),"testcase")-  \ Testing.checkEquals<?>(Argv.global().readAt(1),"arg1")-  \ Testing.checkEquals<?>(Argv.global().readAt(2),"arg2")-  \ Testing.checkEquals<?>(Argv.global().readAt(3),"arg3")-  \ Testing.checkEquals<?>(Argv.global().readAt(4),"arg4")-}--unittest subSequence {-  ReadAt<String> argv <- Argv.global().subSequence(2,2)-  Int count <- argv.size()-  \ Testing.checkEquals<?>(count,2)-  \ Testing.checkEquals<?>(argv.readAt(0),"arg2")-  \ Testing.checkEquals<?>(argv.readAt(1),"arg3")-}---testcase "Argv readAt out of bounds" {-  crash-  require "index 5"-}--unittest test {-  \ Argv.global().readAt(5)-}---testcase "Argv subSequence out of bounds" {-  crash-  require "index 5"-}--unittest test {-  \ Argv.global().subSequence(5,1)-}---testcase "ErrorOr checks" {-  success-}--unittest withValue {-  ErrorOr<Int> value <- ErrorOr:value<Int>(10)-  \ Testing.checkEquals<?>(value.isError(),false)-  \ Testing.checkEquals<?>(value.getValue(),10)-}--unittest withError {-  ErrorOr<String> value <- ErrorOr:error("error message")-  \ Testing.checkEquals<?>(value.isError(),true)-  \ Testing.checkEquals<?>(value.getError().formatted(),"error message")-}--unittest convertError {-  scoped {-    ErrorOr<String> error <- ErrorOr:error("error message")-  } in ErrorOr<Int> value <- error.convertError()-  \ Testing.checkEquals<?>(value.getError().formatted(),"error message")-}---testcase "ErrorOr getError() crashes with value" {-  crash-  require "empty"-}--unittest test {-  ErrorOr<Int> value <- ErrorOr:value<Int>(10)-  \ value.getError()-}---testcase "ErrorOr getValue() crashes with error" {-  crash-  require "error message"-}--unittest test {-  ErrorOr<String> value <- ErrorOr:error("error message")-  \ value.getValue()-}---testcase "ErrorOr convertError() crashes with no error" {-  crash-  require "no error"-}--unittest test {-  ErrorOr<Int> value <- ErrorOr:value<Int>(10)-  \ value.convertError()-}-
− lib/util/extra.0rx
@@ -1,74 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--define Void {-  @category Void singleton <- Void{ }--  formatted () {-    return "void"-  }--  equals (_,_) {-    return true-  }--  lessThan (_,_) {-    return true-  }--  void () {-    return singleton-  }-}--define ErrorOr {-  @value optional #x maybeValue-  @value optional Formatted maybeError--  value (x) {-    return ErrorOr<#x>{ x, empty }-  }--  error (message) {-    return ErrorOr<all>{ empty, message }-  }--  isError () {-    return !present(maybeValue) && present(maybeError)-  }--  getValue () {-    if (present(maybeValue)) {-      return require(maybeValue)-    } else {-      fail(require(maybeError))-    }-  }--  getError () {-    return require(maybeError)-  }--  convertError () {-    if (!present(maybeError)) {-      fail("no error present to convert")-    } else {-      return ErrorOr<all>{ empty, maybeError }-    }-  }-}
− lib/util/helpers.0rt
@@ -1,139 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "comparator helper tests" {-  success-}--unittest orderHLessThan {-  \ Testing.checkEquals<?>("".defaultOrder()    `OrderH:lessThan<?>` "".defaultOrder(),   false)-  \ Testing.checkEquals<?>("a".defaultOrder()   `OrderH:lessThan<?>` "abc".defaultOrder(),true)-  \ Testing.checkEquals<?>("b".defaultOrder()   `OrderH:lessThan<?>` "abc".defaultOrder(),false)-  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:lessThan<?>` "abc".defaultOrder(),false)-  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:lessThan<?>` "a".defaultOrder(),  false)-  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:lessThan<?>` "b".defaultOrder(),  true)-}--unittest orderHLessThanWith {-  \ Testing.checkEquals<?>("".defaultOrder()    `OrderH:lessThanWith<?,Reversed<Char>>` "".defaultOrder(),   false)-  \ Testing.checkEquals<?>("a".defaultOrder()   `OrderH:lessThanWith<?,Reversed<Char>>` "abc".defaultOrder(),true)-  \ Testing.checkEquals<?>("b".defaultOrder()   `OrderH:lessThanWith<?,Reversed<Char>>` "abc".defaultOrder(),true)-  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:lessThanWith<?,Reversed<Char>>` "abc".defaultOrder(),false)-  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:lessThanWith<?,Reversed<Char>>` "a".defaultOrder(),  false)-  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:lessThanWith<?,Reversed<Char>>` "b".defaultOrder(),  false)-}--unittest orderHEquals {-  \ Testing.checkEquals<?>("".defaultOrder()    `OrderH:equals<?>` "".defaultOrder(),   true)-  \ Testing.checkEquals<?>("a".defaultOrder()   `OrderH:equals<?>` "abc".defaultOrder(),false)-  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:equals<?>` "abc".defaultOrder(),true)-  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:equals<?>` "a".defaultOrder(),  false)-}--unittest orderHEqualsWith {-  \ Testing.checkEquals<?>("".defaultOrder()    `OrderH:equalsWith<?,IgnoreCase>` "".defaultOrder(),   true)-  \ Testing.checkEquals<?>("a".defaultOrder()   `OrderH:equalsWith<?,IgnoreCase>` "ABC".defaultOrder(),false)-  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:equalsWith<?,IgnoreCase>` "ABC".defaultOrder(),true)-  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:equalsWith<?,IgnoreCase>` "DEF".defaultOrder(),false)-  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:equalsWith<?,IgnoreCase>` "A".defaultOrder(),  false)-}--unittest readAtHLessThan {-  \ Testing.checkEquals<?>(""    `ReadAtH:lessThan<?>` "",   false)-  \ Testing.checkEquals<?>("a"   `ReadAtH:lessThan<?>` "abc",true)-  \ Testing.checkEquals<?>("b"   `ReadAtH:lessThan<?>` "abc",false)-  \ Testing.checkEquals<?>("abc" `ReadAtH:lessThan<?>` "abc",false)-  \ Testing.checkEquals<?>("abc" `ReadAtH:lessThan<?>` "a",  false)-  \ Testing.checkEquals<?>("abc" `ReadAtH:lessThan<?>` "b",  true)-}--unittest readAtHLessThanWith {-  \ Testing.checkEquals<?>(""    `ReadAtH:lessThanWith<?,Reversed<Char>>` "",   false)-  \ Testing.checkEquals<?>("a"   `ReadAtH:lessThanWith<?,Reversed<Char>>` "abc",true)-  \ Testing.checkEquals<?>("b"   `ReadAtH:lessThanWith<?,Reversed<Char>>` "abc",true)-  \ Testing.checkEquals<?>("abc" `ReadAtH:lessThanWith<?,Reversed<Char>>` "abc",false)-  \ Testing.checkEquals<?>("abc" `ReadAtH:lessThanWith<?,Reversed<Char>>` "a",  false)-  \ Testing.checkEquals<?>("abc" `ReadAtH:lessThanWith<?,Reversed<Char>>` "b",  false)-}--unittest readAtHEquals {-  \ Testing.checkEquals<?>(""    `ReadAtH:equals<?>` "",   true)-  \ Testing.checkEquals<?>("a"   `ReadAtH:equals<?>` "abc",false)-  \ Testing.checkEquals<?>("abc" `ReadAtH:equals<?>` "abc",true)-  \ Testing.checkEquals<?>("abc" `ReadAtH:equals<?>` "a",  false)-}--unittest readAtHEqualsWith {-  \ Testing.checkEquals<?>(""    `ReadAtH:equalsWith<?,IgnoreCase>` "",   true)-  \ Testing.checkEquals<?>("a"   `ReadAtH:equalsWith<?,IgnoreCase>` "ABC",false)-  \ Testing.checkEquals<?>("abc" `ReadAtH:equalsWith<?,IgnoreCase>` "ABC",true)-  \ Testing.checkEquals<?>("abc" `ReadAtH:equalsWith<?,IgnoreCase>` "DEF",false)-  \ Testing.checkEquals<?>("abc" `ReadAtH:equalsWith<?,IgnoreCase>` "a",  false)-}--concrete IgnoreCase {-  defines Equals<Char>-}--define IgnoreCase {-  equals (x,y) {-    return toLower(x) == toLower(y)-  }--  @type toLower (Char) -> (Char)-  toLower (c) {-    if (c >= 'A' && c <= 'Z') {-      return (c.asInt() - 'A'.asInt() + 'a'.asInt()).asChar()-    } else {-      return c-    }-  }-}--unittest reversed {-  \ Testing.checkEquals<?>(0 `Reversed<Int>.lessThan` 0,false)-  \ Testing.checkEquals<?>(0 `Reversed<Int>.lessThan` 1,false)-  \ Testing.checkEquals<?>(1 `Reversed<Int>.lessThan` 0,true)-}--unittest bySizeLessThan {-  \ Testing.checkEquals<?>(""   `BySize.lessThan` "",  false)-  \ Testing.checkEquals<?>("b"  `BySize.lessThan` "ac",true)-  \ Testing.checkEquals<?>("ac" `BySize.lessThan` "b", false)-  \ Testing.checkEquals<?>("a"  `BySize.lessThan` "b", false)-}--unittest bySizeEquals {-  \ Testing.checkEquals<?>(""   `BySize.equals` "",  true)-  \ Testing.checkEquals<?>("b"  `BySize.equals` "ac",false)-  \ Testing.checkEquals<?>("ac" `BySize.equals` "b", false)-  \ Testing.checkEquals<?>("a"  `BySize.equals` "b", true)-}--unittest alwaysEqualLessThan {-  \ Testing.checkEquals<?>(""   `AlwaysEqual.lessThan` "",  false)-  \ Testing.checkEquals<?>("b"  `AlwaysEqual.lessThan` "ac",false)-  \ Testing.checkEquals<?>("ac" `AlwaysEqual.lessThan` "b", false)-}--unittest alwaysEqualEquals {-  \ Testing.checkEquals<?>(""   `AlwaysEqual.equals` "",  true)-  \ Testing.checkEquals<?>("b"  `AlwaysEqual.equals` "ac",true)-  \ Testing.checkEquals<?>("ac" `AlwaysEqual.equals` "b", true)-  \ Testing.checkEquals<?>("a"  `AlwaysEqual.equals` "b", true)-}
− lib/util/helpers.0rx
@@ -1,122 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--define OrderH {-  lessThan (x,y) {-    return lessThanWith<#x,#x>(x,y)-  }--  lessThanWith (x,y) {-    optional Order<#x> xx <- x-    optional Order<#x> yy <- y-    $Hidden[x,y]$-    while (present(xx) && present(yy)) {-      $ReadOnly[xx,yy]$-      if (require(xx).get() `#xx.lessThan` require(yy).get()) {-        return true-      } elif (require(yy).get() `#xx.lessThan` require(xx).get()) {-        return false-      }-    } update {-      xx <- require(xx).next()-      yy <- require(yy).next()-    }-    return present(yy)-  }--  equals (x,y) {-    return equalsWith<#x,#x>(x,y)-  }--  equalsWith (x,y) {-    optional Order<#x> xx <- x-    optional Order<#x> yy <- y-    $Hidden[x,y]$-    while (present(xx) && present(yy)) {-      $ReadOnly[xx,yy]$-      if (!(require(xx).get() `#xx.equals` require(yy).get())) {-        return false-      }-    } update {-      xx <- require(xx).next()-      yy <- require(yy).next()-    }-    return !present(xx) && !present(yy)-  }-}--define ReadAtH {-  lessThan (x,y) {-    return lessThanWith<#x,#x>(x,y)-  }--  lessThanWith (x,y) {-    traverse (Counter.zeroIndexed(Ranges:min<?>(x.size(),y.size())) -> Int i) {-      $ReadOnly[i]$-      if (x.readAt(i) `#xx.lessThan` y.readAt(i)) {-        return true-      } elif (y.readAt(i) `#xx.lessThan` x.readAt(i)) {-        return false-      }-    }-    return x.size() < y.size()-  }--  equals (x,y) {-    return equalsWith<#x,#x>(x,y)-  }--  equalsWith (x,y) {-    if (x.size() != y.size()) {-      return false-    }-    traverse (Counter.zeroIndexed(x.size()) -> Int i) {-      $ReadOnly[i]$-      if (!(x.readAt(i) `#xx.equals` y.readAt(i))) {-        return false-      }-    }-    return true-  }-}--define Reversed {-  lessThan (x,y) {-    return y `#x.lessThan` x-  }-}--define BySize {-  equals (x,y) {-    return x.size() == y.size()-  }--  lessThan (x,y) {-    return x.size() < y.size()-  }-}--define AlwaysEqual {-  equals (_,_) {-    return true-  }--  lessThan (_,_) {-    return false-  }-}
− lib/util/input.0rt
@@ -1,99 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "TextReader checks" {-  success-}--concrete Helper {-  @type checkNext (TextReader,String) -> ()-}--define Helper {-  checkNext (reader,line) {-    if (reader.pastEnd()) {-      fail("past end")-    }-    String value <- reader.readNextLine()-    \ Testing.checkEquals<?>(value,line)-  }-}--concrete FakeFile {-  refines BlockReader<String>--  @type create () -> (FakeFile)-}--define FakeFile {-  @value Int counter--  create () {-    return FakeFile{ 0 }-  }--  readBlock (_) {-    cleanup {-      counter <- counter+1-    } in if (counter == 0) {-      return "this is a "-    } elif (counter == 1) {-      return "partial line\nwith "-    } elif (counter == 2) {-      return "some more data\nand\nmore lines"-    } elif (counter == 3) {-      return "\nunfinished"-    } else {-      fail("too many reads")-    }-  }--  pastEnd () {-    return counter > 3-  }-}--unittest correctSplit {-    TextReader reader <- TextReader.fromBlockReader(FakeFile.create())-    \ Helper.checkNext(reader,"this is a partial line")-    \ Helper.checkNext(reader,"with some more data")-    \ Helper.checkNext(reader,"and")-    \ Helper.checkNext(reader,"more lines")-    \ Helper.checkNext(reader,"unfinished")-    if (!reader.pastEnd()) {-      fail("not past end")-    }-    if (reader.readNextLine() != "") {-      fail("reused data")-    }-}--unittest readAll {-  String allContents <- TextReader.readAll(FakeFile.create())-  \ Testing.checkEquals<?>(allContents,"this is a partial line\nwith some more data\nand\nmore lines\nunfinished")-}---testcase "stdin is empty in tests" {-  success-  timeout 1-}--unittest test {-  \ Testing.checkEquals<?>(TextReader.readAll(SimpleInput.stdin()),"")-}
− lib/util/input.0rx
@@ -1,76 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--define TextReader {-  @value BlockReader<String> reader-  @value String buffer--  fromBlockReader (reader) {-    return TextReader{ reader, "" }-  }--  readAll (reader) {-    [Append<String>&Build<String>] builder <- String.builder()-    while (!reader.pastEnd()) {-      \ builder.append(reader.readBlock($ExprLookup[BLOCK_READ_SIZE]$))-    }-    return builder.build()-  }--  readNextLine () {-    while (true) {-      Int newline <- findNewline()-      if (newline < 0) {-        if (reader.pastEnd()) {-          cleanup {-            buffer <- ""-          } in return buffer-        } else {-          buffer <- buffer+reader.readBlock($ExprLookup[BLOCK_READ_SIZE]$)-        }-      } else {-        return takeLine(newline)-      }-    }-    return ""-  }--  pastEnd () {-    return buffer.size() == 0 && reader.pastEnd()-  }--  @value findNewline () -> (Int)-  findNewline () {-    traverse (Counter.zeroIndexed(buffer.size()) -> Int position) {-      $ReadOnly[position]$-      Char c <- buffer.readAt(position)-      // TODO: Maybe the line separator should be a factory argument.-      if (c == '\n' || c == '\r') {-        return position-      }-    }-    return -1-  }--  @value takeLine (Int) -> (String)-  takeLine (position) {-    String data <- buffer.subSequence(0,position)-    buffer <- buffer.subSequence(position+1,buffer.size()-position-1)-    return data-  }-}
− lib/util/iterator.0rp
@@ -1,116 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--// Read at the current position.-//-// Params:-// - #x: The value type to be read.-@value interface ReadCurrent<|#x> {-  // Return the value at the current position.-  readCurrent () -> (#x)-}--// Write at the current position.-//-// Params:-// - #x: The value type to be written.-@value interface WriteCurrent<#x|> {-  // Write the value at the current position and return self.-  writeCurrent (#x) -> (#self)-}--// Forward iteration.-@value interface IterateForward {-  // Return a copy that has been moved forward once. (Do not mutate self.)-  //-  // Notes:-  // - This should crash if pastForwardEnd() is true.-  forward () -> (#self)-  // Returns true iff no more forward movement is possible.-  pastForwardEnd () -> (Bool)-}--// Reverse iteration.-@value interface IterateReverse {-  // Return a copy that has been moved in reverse once. (Do not mutate self.)-  //-  // Notes:-  // - This should crash if pastReverseEnd() is true.-  reverse () -> (#self)-  // Returns true iff no more reverse movement is possible.-  pastReverseEnd () -> (Bool)-}--// Iterator for forward/reverse reading.-@value interface ReadIterator<|#x> {-  refines ReadCurrent<#x>-  refines IterateForward-  refines IterateReverse-}--// Iterator for forward/reverse writing.-@value interface WriteIterator<#x|> {-  refines WriteCurrent<#x>-  refines IterateForward-  refines IterateReverse-}--// Iterator for forward/reverse reading and writing.-@value interface Iterator<#x> {-  refines ReadIterator<#x>-  refines WriteIterator<#x>-}--// Automatic read iteration from ReadAt.-//-// Params:-// - #x: The value type to be read.-concrete AutoReadIterator<#x> {-  refines ReadIterator<#x>--  // Create an iterator from a ReadAt.-  @category from<#y> (ReadAt<#y>) -> (AutoReadIterator<#y>)-  // Create an iterator from a ReadAt starting at an offset.-  @category fromOffset<#y> (ReadAt<#y>,Int) -> (AutoReadIterator<#y>)-}--// Automatic write iteration from WriteAt.-//-// Params:-// - #x: The value type to be written.-concrete AutoWriteIterator<#x> {-  refines WriteIterator<#x>--  // Create an iterator from a WriteAt.-  @category from<#y> (WriteAt<#y>) -> (AutoWriteIterator<#y>)-  // Create an iterator from a WriteAt starting at an offset.-  @category fromOffset<#y> (WriteAt<#y>,Int) -> (AutoWriteIterator<#y>)-}--// Automatic read/write iteration from ReadAt&WriteAt.-//-// Params:-// - #x: The value type to be read/written.-concrete AutoIterator<#x> {-  refines Iterator<#x>--  // Create an iterator from a ReadAt&WriteAt.-  @category from<#y> ([ReadAt<#y>&WriteAt<#y>]) -> (AutoIterator<#y>)-  // Create an iterator from a ReadAt&WriteAt starting at an offset.-  @category fromOffset<#y> ([ReadAt<#y>&WriteAt<#y>],Int) -> (AutoIterator<#y>)-}
− lib/util/iterator.0rt
@@ -1,178 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "iterate ReadIterator" {-  success-}--unittest forwardRead {-  String s <- "abcde"-  Int count <- 0-  scoped {-    ReadIterator<Char> iter <- AutoReadIterator:from<?>(s)-  } in while (!iter.pastForwardEnd()) {-    $ReadOnly[count,iter]$-    \ Testing.checkEquals<?>(iter.readCurrent(),s.readAt(count))-  } update {-    count <- count+1-    iter <- iter.forward()-  }-  \ Testing.checkEquals<?>(count,s.size())-}--unittest reverseRead {-  String s <- "abcde"-  Int count <- s.size()-1-  scoped {-    ReadIterator<Char> iter <- AutoReadIterator:fromOffset<?>(s,count)-  } in while (!iter.pastReverseEnd()) {-    $ReadOnly[count,iter]$-    \ Testing.checkEquals<?>(iter.readCurrent(),s.readAt(count))-  } update {-    count <- count-1-    iter <- iter.reverse()-  }-  \ Testing.checkEquals<?>(count,-1)-}---testcase "iterate WriteIterator" {-  success-}--unittest forwardWrite {-  String s <- "abcde"-  Int count <- 0-  scoped {-    WriteChecker checker <- WriteChecker.create(s)-    WriteIterator<Char> iter <- AutoWriteIterator:from<?>(checker)-  } in while (!iter.pastForwardEnd()) {-    $ReadOnly[count,iter]$-    \ iter.writeCurrent(s.readAt(count))-  } update {-    count <- count+1-    iter <- iter.forward()-  }-  \ Testing.checkEquals<?>(count,s.size())-}--unittest reverseWrite {-  String s <- "abcde"-  Int count <- s.size()-1-  scoped {-    WriteChecker checker <- WriteChecker.create(s)-    WriteIterator<Char> iter <- AutoWriteIterator:fromOffset<?>(checker,count)-  } in while (!iter.pastReverseEnd()) {-    $ReadOnly[count,iter]$-    \ iter.writeCurrent(s.readAt(count))-  } update {-    count <- count-1-    iter <- iter.reverse()-  }-  \ Testing.checkEquals<?>(count,-1)-}--concrete WriteChecker {-  refines WriteAt<Char>--  @type create (String) -> (WriteChecker)-}--define WriteChecker {-  @value String content--  create (string) {-    return WriteChecker{ string }-  }--  writeAt (index,c) {-    \ Testing.checkEquals<?>(c,content.readAt(index))-    return self-  }--  size () {-    return content.size()-  }-}---testcase "iterate Iterator" {-  success-}--unittest forwardReadWrite {-  String s <- "abcde"-  Int count <- 0-  scoped {-    ReadWriteChecker checker <- ReadWriteChecker.create(s)-    Iterator<Char> iter <- AutoIterator:from<?>(checker)-  } in while (!iter.pastForwardEnd()) {-    $ReadOnly[count,iter]$-    \ Testing.checkEquals<?>(iter.readCurrent(),s.readAt(count))-    \ iter.writeCurrent(iter.readCurrent())-  } update {-    count <- count+1-    iter <- iter.forward()-  }-  \ Testing.checkEquals<?>(count,s.size())-}--unittest reverseReadWrite {-  String s <- "abcde"-  Int count <- s.size()-1-  scoped {-    ReadWriteChecker checker <- ReadWriteChecker.create(s)-    Iterator<Char> iter <- AutoIterator:fromOffset<?>(checker,count)-  } in while (!iter.pastReverseEnd()) {-    $ReadOnly[count,iter]$-    \ Testing.checkEquals<?>(iter.readCurrent(),s.readAt(count))-    \ iter.writeCurrent(iter.readCurrent())-  } update {-    count <- count-1-    iter <- iter.reverse()-  }-  \ Testing.checkEquals<?>(count,-1)-}--concrete ReadWriteChecker {-  refines ReadAt<Char>-  refines WriteAt<Char>--  @type create (String) -> (ReadWriteChecker)-}--define ReadWriteChecker {-  @value String content--  create (string) {-    return ReadWriteChecker{ string }-  }--  readAt (index) {-    return content.readAt(index)-  }--  writeAt (index,c) {-    \ Testing.checkEquals<?>(c,content.readAt(index))-    return self-  }--  size () {-    return content.size()-  }-}
− lib/util/iterator.0rx
@@ -1,171 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--define AutoReadIterator {-  @value ReadAt<#x> container-  @value Int position--  from (container) {-    return fromOffset<#y>(container,0)-  }--  fromOffset (container,position) {-    Int position2 <- position-    if (position < 0) {-      position2 <- -1-    }-    if (position >= container.size() || container.size() == 0) {-      position2 <- container.size()-    }-    return AutoReadIterator<#y>{ container, position }-  }--  readCurrent () {-    if (pastForwardEnd() || pastReverseEnd()) {-      \ LazyStream<Formatted>.new()-          .append("Position ")-          .append(position)-          .append(" is out of bounds")-          .writeTo(SimpleOutput.error())-    }-    return container.readAt(position)-  }--  forward () {-    return fromOffset<#x>(container,position+1)-  }--  pastForwardEnd () {-    return position >= container.size()-  }--  reverse () {-    return fromOffset<#x>(container,position-1)-  }--  pastReverseEnd () {-    return position < 0 || container.size() == 0-  }-}--define AutoWriteIterator {-  @value WriteAt<#x> container-  @value Int position--  from (container) {-    return fromOffset<#y>(container,0)-  }--  fromOffset (container,position) {-    Int position2 <- position-    if (position < 0) {-      position2 <- -1-    }-    if (position >= container.size() || container.size() == 0) {-      position2 <- container.size()-    }-    return AutoWriteIterator<#y>{ container, position }-  }--  writeCurrent (x) {-    if (pastForwardEnd() || pastReverseEnd()) {-      \ LazyStream<Formatted>.new()-          .append("Position ")-          .append(position)-          .append(" is out of bounds")-          .writeTo(SimpleOutput.error())-    }-    \ container.writeAt(position,x)-    return self-  }--  forward () {-    return fromOffset<#x>(container,position+1)-  }--  pastForwardEnd () {-    return position >= container.size()-  }--  reverse () {-    return fromOffset<#x>(container,position-1)-  }--  pastReverseEnd () {-    return position < 0 || container.size() == 0-  }-}--define AutoIterator {-  @value [ReadAt<#x>&WriteAt<#x>] container-  @value Int position--  from (container) {-    return fromOffset<#y>(container,0)-  }--  fromOffset (container,position) {-    Int position2 <- position-    if (position < 0) {-      position2 <- -1-    }-    if (position >= container.size() || container.size() == 0) {-      position2 <- container.size()-    }-    return AutoIterator<#y>{ container, position }-  }--  readCurrent () {-    if (pastForwardEnd() || pastReverseEnd()) {-      \ LazyStream<Formatted>.new()-          .append("Position ")-          .append(position)-          .append(" is out of bounds")-          .writeTo(SimpleOutput.error())-    }-    return container.readAt(position)-  }--  writeCurrent (x) {-    if (pastForwardEnd() || pastReverseEnd()) {-      \ LazyStream<Formatted>.new()-          .append("Position ")-          .append(position)-          .append(" is out of bounds")-          .writeTo(SimpleOutput.error())-    }-    \ container.writeAt(position,x)-    return self-  }--  forward () {-    return fromOffset<#x>(container,position+1)-  }--  pastForwardEnd () {-    return position >= container.size()-  }--  reverse () {-    return fromOffset<#x>(container,position-1)-  }--  pastReverseEnd () {-    return position < 0 || container.size() == 0-  }-}
− lib/util/mutex.0rt
@@ -1,55 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "Mutex tests" {-  success-}--unittest simpleLockUnlock {-  Mutex mutex <- SimpleMutex.new()-  \ mutex.lock().unlock().lock().unlock()-}--unittest spinlockLockUnlock {-  Mutex mutex <- SpinlockMutex.new()-  \ mutex.lock().unlock().lock().unlock()-}---testcase "MutexLock crashes if not freed" {-  crash-  require "not freed"-}--unittest test {-  Mutex mutex <- SimpleMutex.new()-  \ MutexLock.lock(mutex)-}---testcase "MutexLock crashes if freed twice" {-  crash-  require "freed multiple times"-}--unittest test {-  Mutex mutex <- SimpleMutex.new()-  MutexLock lock <- MutexLock.lock(mutex)-  \ lock.freeResource()-  \ lock.freeResource()-}
− lib/util/output.0rt
@@ -1,48 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "stdout writer" {-  success-  require stdout "message"-  exclude stderr "message"-}--unittest test {-  \ LazyStream<Formatted>.new().append("message").writeTo(SimpleOutput.stdout())-}---testcase "stderr writer" {-  success-  require stderr "message"-  exclude stdout "message"-}--unittest test {-  \ LazyStream<Formatted>.new().append("message").writeTo(SimpleOutput.stderr())-}---testcase "error writer" {-  crash-  require "message"-}--unittest test {-  \ LazyStream<Formatted>.new().append("message").writeTo(SimpleOutput.error())-}
− lib/util/output.0rx
@@ -1,45 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2019-2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--define LazyStream {-  @value optional #x value-  @value optional LazyStream<#x> next--  new () {-    return LazyStream<#x>{ empty, empty }-  }--  append (value2) {-    return LazyStream<#x>{ value2, self }-  }--  @value recursive (Writer<#x>) -> ()-  recursive (writer) {-    if (present(next)) {-      \ require(next).recursive(writer)-    }-    if (present(value)) {-      \ writer.write(require(value))-    }-  }--  writeTo (writer) {-    \ recursive(writer)-    \ writer.flush()-  }-}
− lib/util/parse.0rt
@@ -1,221 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "ParseChars tests" {-  success-}--unittest floatNoDecimal {-  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("123"),122.9,123.1)-}--unittest floatDecimalLeft {-  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float(".123"),0.1229,0.1231)-}--unittest floatDecimalRight {-  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("123."),122.9,123.1)-}--unittest floatDecimalMiddle {-  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("123.456"),123.4559,123.4561)-}--unittest floatLeadingTrailingZeros {-  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("000123.456000"),123.4559,123.4561)-}--unittest floatNegative {-  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("-123.456"),-123.4561,-123.4559)-}--unittest floatPositive {-  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("+123.456"),123.4559,123.4561)-}--unittest floatNegativeDecimalLeft {-  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("-.456"),-0.4561,-0.4559)-}--unittest floatExponentNoDecimal {-  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("123E5"),122.9E5,123.1E5)-}--unittest floatNegativeExponent {-  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("123E-5"),122.9E-5,123.1E-5)-}--unittest floatPositiveExponent {-  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("123E+5"),122.9E5,123.1E5)-}--unittest floatExponentDecimalRight {-  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("123.E5"),122.9E5,123.1E5)-}--unittest floatExponentDecimalLeft {-  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float(".123E5"),122.9E2,123.1E2)-}--unittest floatExponentDecimalMiddle {-  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("123.456E5"),123.4559E5,123.4561E5)-}--unittest floatEmpty {-  \ UtilTesting.checkError(ParseChars.float(""))-}--unittest floatNoDigits {-  \ UtilTesting.checkError(ParseChars.float("."))-}--unittest floatTwoDecimals {-  \ UtilTesting.checkError(ParseChars.float("1.1."))-}--unittest floatExponentNoDigitsNoDecimal {-  \ UtilTesting.checkError(ParseChars.float("E5"))-}--unittest floatExponentNoDigitsDecimal {-  \ UtilTesting.checkError(ParseChars.float(".E5"))-}--unittest floatExponentNoDigitsSign {-  \ UtilTesting.checkError(ParseChars.float("-E5"))-}--unittest floatDecimalAfterExponent {-  \ UtilTesting.checkError(ParseChars.float("1E.5"))-}--unittest floatExponentExcessData {-  \ UtilTesting.checkError(ParseChars.float("123E5Q"))-}--unittest int {-  \ UtilTesting.checkSuccess<?>(ParseChars.int("1234"),1234)-}--unittest intNegative {-  \ UtilTesting.checkSuccess<?>(ParseChars.int("-1234"),-1234)-}--unittest intPositive {-  \ UtilTesting.checkSuccess<?>(ParseChars.int("+1234"),1234)-}--unittest intLeadingZeros {-  \ UtilTesting.checkSuccess<?>(ParseChars.int("001234"),1234)-}--unittest intEmpty {-  \ UtilTesting.checkError(ParseChars.int(""))-}--unittest intJustSign {-  \ UtilTesting.checkError(ParseChars.int("-"))-}--unittest intBadDigit {-  \ UtilTesting.checkError(ParseChars.int("12A"))-}--unittest hexInt {-  \ UtilTesting.checkSuccess<?>(ParseChars.hexInt("12AbCdEf"),\x12ABCDEF)-}--unittest hexIntNegative {-  \ UtilTesting.checkSuccess<?>(ParseChars.hexInt("-12AbCdEf"),-\x12ABCDEF)-}--unittest hexIntPositive {-  \ UtilTesting.checkSuccess<?>(ParseChars.hexInt("+12AbCdEf"),\x12ABCDEF)-}--unittest hexIntLeadingZeros {-  \ UtilTesting.checkSuccess<?>(ParseChars.hexInt("0012AbCdEf"),\x12ABCDEF)-}--unittest hexIntEmpty {-  \ UtilTesting.checkError(ParseChars.hexInt(""))-}--unittest hexIntJustSign {-  \ UtilTesting.checkError(ParseChars.hexInt("-"))-}--unittest hexIntBadDigit {-  \ UtilTesting.checkError(ParseChars.hexInt("12Q"))-}--unittest octInt {-  \ UtilTesting.checkSuccess<?>(ParseChars.octInt("127"),\o127)-}--unittest octIntNegative {-  \ UtilTesting.checkSuccess<?>(ParseChars.octInt("-127"),-\o127)-}--unittest octIntPositive {-  \ UtilTesting.checkSuccess<?>(ParseChars.octInt("+127"),\o127)-}--unittest octIntLeadingZeros {-  \ UtilTesting.checkSuccess<?>(ParseChars.octInt("00127"),\o127)-}--unittest octIntEmpty {-  \ UtilTesting.checkError(ParseChars.octInt(""))-}--unittest octIntJustSign {-  \ UtilTesting.checkError(ParseChars.octInt("-"))-}--unittest octIntBadDigit {-  \ UtilTesting.checkError(ParseChars.octInt("19"))-}--unittest binInt {-  \ UtilTesting.checkSuccess<?>(ParseChars.binInt("100101"),\b100101)-}--unittest binIntNegative {-  \ UtilTesting.checkSuccess<?>(ParseChars.binInt("-100101"),-\b100101)-}--unittest binIntPositive {-  \ UtilTesting.checkSuccess<?>(ParseChars.binInt("+100101"),\b100101)-}--unittest binIntLeadingZeros {-  \ UtilTesting.checkSuccess<?>(ParseChars.binInt("00100101"),\b100101)-}--unittest binIntEmpty {-  \ UtilTesting.checkError(ParseChars.binInt(""))-}--unittest binIntJustSign {-  \ UtilTesting.checkError(ParseChars.binInt("-"))-}--unittest binIntBadDigit {-  \ UtilTesting.checkError(ParseChars.binInt("12"))-}
− lib/util/parse.0rx
@@ -1,299 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--define ParseChars {-  float (data) {-    Float total <- 0.0-    Float sign <- 0.0-    Bool hasDigit <- false-    Bool needExpDigits <- false--    optional Order<Char> curr <- data.defaultOrder()--    // Parse to the left of '.'.-    while (present(curr)) {-      Char c <- require(curr).get()-      curr <- require(curr).next()-      $ReadOnly[c]$-      $Hidden[curr]$-      if (sign == 0.0) {-        if (c == '-') {-          sign <- -1.0-          continue-        } elif (c == '+') {-          sign <- 1.0-          continue-        } else {-          sign <- 1.0-        }-      }-      if (c >= '0' && c <= '9') {-        hasDigit <- true-        total <- 10.0*total+(c.asInt() - '0'.asInt()).asFloat()-        continue-      }-      if (c == '.') {-        break-      }-      if (c == 'E' || c == 'e') {-        if (!hasDigit) {-          return autoFail<Float>(c)-        } else {-          needExpDigits <- true-          break-        }-      }-      return autoFail<Float>(c)-    }--    if (!needExpDigits) {--      Float decimal <- 0.1--      // Parse to the right of '.'.-      while (present(curr)) {-        Char c <- require(curr).get()-        curr <- require(curr).next()-        $ReadOnly[c]$-        $Hidden[curr]$-        if (c >= '0' && c <= '9') {-          hasDigit <- true-          total <- total+decimal*(c.asInt() - '0'.asInt()).asFloat()-          decimal <- decimal/10.0-          continue-        }-        if (c == 'E' || c == 'e') {-          if (!hasDigit) {-            return autoFail<Float>(c)-          } else {-            needExpDigits <- true-            break-          }-        }-        return autoFail<Float>(c)-      }--    }--    if (!hasDigit) {-      return ErrorOr:error("missing digits in Float value")-    }--    total <- sign*total-    $Hidden[sign,hasDigit]$--    Int expSign <- 0-    Int exp <- 0--    if (needExpDigits) {--      // Parse exponent.-      while (present(curr)) {-        Char c <- require(curr).get()-        curr <- require(curr).next()-        $ReadOnly[c]$-        $Hidden[curr,total]$-        if (expSign == 0) {-          if (c == '-') {-            expSign <- -1-            continue-          } elif (c == '+') {-            expSign <- 1-            continue-          } else {-            expSign <- 1-          }-        }-        if (c >= '0' && c <= '9') {-          needExpDigits <- false-          exp <- 10*exp+(c.asInt() - '0'.asInt())-          continue-        }-        return autoFail<Float>(c)-      }--    }--    if (present(curr)) {-      return autoFail<Float>(require(curr).get())-    }--    if (needExpDigits) {-      return ErrorOr:error("missing digits in Float exponent")-    }--    traverse (Counter.zeroIndexed(exp) -> _) {-      if (expSign > 0) {-        total <- 10.0*total-      } else {-        total <- total/10.0-      }-    }--    return ErrorOr:value<?>(total)-  }--  int (data) {-    Int total <- 0-    Int sign <- 0-    Bool hasDigit <- false--    traverse (data.defaultOrder() -> Char c) {-      if (sign == 0) {-        if (c == '-') {-          sign <- -1-          continue-        } elif (c == '+') {-          sign <- 1-          continue-        } else {-          sign <- 1-        }-      }-      if (c >= '0' && c <= '9') {-        hasDigit <- true-        total <- 10*total+(c.asInt() - '0'.asInt())-        continue-      }-      return autoFail<Int>(c)-    }--    if (!hasDigit) {-      return ErrorOr:error("missing digits in Int value")-    }--    return ErrorOr:value<?>(sign*total)-  }--  hexInt (data) {-    Int total <- 0-    Int sign <- 0-    Bool hasDigit <- false--    traverse (data.defaultOrder() -> Char c) {-      if (sign == 0) {-        if (c == '-') {-          sign <- -1-          continue-        } elif (c == '+') {-          sign <- 1-          continue-        } else {-          sign <- 1-        }-      }-      if (c >= '0' && c <= '9') {-        hasDigit <- true-        total <- 16*total+(c.asInt() - '0'.asInt())-        continue-      }-      if (c >= 'a' && c <= 'f') {-        hasDigit <- true-        total <- 16*total+(c.asInt() - 'a'.asInt() + 10)-        continue-      }-      if (c >= 'A' && c <= 'F') {-        hasDigit <- true-        total <- 16*total+(c.asInt() - 'A'.asInt() + 10)-        continue-      }-      return autoFail<Int>(c)-    }--    if (!hasDigit) {-      return ErrorOr:error("missing digits in Int value")-    }--    return ErrorOr:value<?>(sign*total)-  }--  octInt (data) {-    Int total <- 0-    Int sign <- 0-    Bool hasDigit <- false--    traverse (data.defaultOrder() -> Char c) {-      if (sign == 0) {-        if (c == '-') {-          sign <- -1-          continue-        } elif (c == '+') {-          sign <- 1-          continue-        } else {-          sign <- 1-        }-      }-      if (c >= '0' && c <= '7') {-        hasDigit <- true-        total <- 8*total+(c.asInt() - '0'.asInt())-        continue-      }-      return autoFail<Int>(c)-    }--    if (!hasDigit) {-      return ErrorOr:error("missing digits in Int value")-    }--    return ErrorOr:value<?>(sign*total)-  }--  binInt (data) {-    Int total <- 0-    Int sign <- 0-    Bool hasDigit <- false--    traverse (data.defaultOrder() -> Char c) {-      if (sign == 0) {-        if (c == '-') {-          sign <- -1-          continue-        } elif (c == '+') {-          sign <- 1-          continue-        } else {-          sign <- 1-        }-      }-      if (c >= '0' && c <= '1') {-        hasDigit <- true-        total <- 2*total+(c.asInt() - '0'.asInt())-        continue-      }-      return autoFail<Int>(c)-    }--    if (!hasDigit) {-      return ErrorOr:error("missing digits in Int value")-    }--    return ErrorOr:value<?>(sign*total)-  }--  @type autoFail<#x> (Char) -> (ErrorOr<all>)-  autoFail (c) {-    return ErrorOr:error(String.builder()-        .append("failed to parse ")-        .append(typename<#x>().formatted())-        .append(" at '")-        .append(c.formatted())-        .append("'")-        .build())-  }-}
lib/util/src/Extension_Argv.cpp view
@@ -26,7 +26,7 @@ #endif  // ZEOLITE_PUBLIC_NAMESPACE  namespace {-extern const BoxedValue& Var_global;+extern const BoxedValue Var_global; }  // namespace  struct ExtCategory_Argv : public Category_Argv {@@ -35,28 +35,28 @@ struct ExtType_Argv : public Type_Argv {   inline ExtType_Argv(Category_Argv& p, Params<0>::Type params) : Type_Argv(p, params) {} -  ReturnTuple Call_global(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_global(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Argv.global")     return ReturnTuple(Var_global);   } };  struct ExtValue_Argv : public Value_Argv {-  inline ExtValue_Argv(S<Type_Argv> p, int st, int sz)-    : Value_Argv(p), start(st), size(sz) {}+  inline ExtValue_Argv(S<const Type_Argv> p, int st, int sz)+    : Value_Argv(std::move(p)), start(st), size(sz) {} -  ReturnTuple Call_readAt(const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_readAt(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Argv.readAt")     const PrimInt Var_arg1 = (args.At(0)).AsInt();     return ReturnTuple(Box_String(Argv::GetArgAt(start + Var_arg1)));   } -  ReturnTuple Call_size(const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_size(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Argv.size")     return ReturnTuple(Box_Int(GetSize()));   } -  ReturnTuple Call_subSequence(const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_subSequence(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("Argv.subSequence")     const PrimInt Var_arg1 = (args.At(0)).AsInt();     const PrimInt Var_arg2 = (args.At(1)).AsInt();@@ -76,7 +76,7 @@ };  namespace {-const BoxedValue& Var_global = *new BoxedValue(BoxedValue::New<ExtValue_Argv>(CreateType_Argv(Params<0>::Type()), 0, -1));+const BoxedValue Var_global = BoxedValue::New<ExtValue_Argv>(CreateType_Argv(Params<0>::Type()), 0, -1); }  // namespace  Category_Argv& CreateCategory_Argv() {@@ -84,10 +84,12 @@   return category; } -S<Type_Argv> CreateType_Argv(Params<0>::Type params) {+S<const Type_Argv> CreateType_Argv(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_Argv(CreateCategory_Argv(), Params<0>::Type()));   return cached; }++void RemoveType_Argv(const Params<0>::Type& params) {}  #ifdef ZEOLITE_PUBLIC_NAMESPACE }  // namespace ZEOLITE_PUBLIC_NAMESPACE
lib/util/src/Extension_MutexLock.cpp view
@@ -28,7 +28,7 @@ namespace ZEOLITE_PUBLIC_NAMESPACE { #endif  // ZEOLITE_PUBLIC_NAMESPACE -BoxedValue CreateValue_MutexLock(S<Type_MutexLock> parent, const ValueTuple& args);+BoxedValue CreateValue_MutexLock(S<const Type_MutexLock> parent, const ValueTuple& args);  struct ExtCategory_MutexLock : public Category_MutexLock { };@@ -36,15 +36,15 @@ struct ExtType_MutexLock : public Type_MutexLock {   inline ExtType_MutexLock(Category_MutexLock& p, Params<0>::Type params) : Type_MutexLock(p, params) {} -  ReturnTuple Call_lock(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_lock(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("MutexLock.lock")     return ReturnTuple(CreateValue_MutexLock(CreateType_MutexLock(Params<0>::Type()), args));   } };  struct ExtValue_MutexLock : public Value_MutexLock {-  inline ExtValue_MutexLock(S<Type_MutexLock> p, const ValueTuple& args)-    : Value_MutexLock(p), mutex(args.Only()) {+  inline ExtValue_MutexLock(S<const Type_MutexLock> p, const ValueTuple& args)+    : Value_MutexLock(std::move(p)), mutex(args.Only()) {     TypeValue::Call(BoxedValue::Require(mutex), Function_Mutex_lock, ParamTuple(), ArgTuple());   } @@ -79,13 +79,15 @@   return category; } -S<Type_MutexLock> CreateType_MutexLock(Params<0>::Type params) {+S<const Type_MutexLock> CreateType_MutexLock(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_MutexLock(CreateCategory_MutexLock(), Params<0>::Type()));   return cached; } -BoxedValue CreateValue_MutexLock(S<Type_MutexLock> parent, const ValueTuple& args) {-  return BoxedValue::New<ExtValue_MutexLock>(parent, args);+void RemoveType_MutexLock(const Params<0>::Type& params) {}++BoxedValue CreateValue_MutexLock(S<const Type_MutexLock> parent, const ValueTuple& args) {+  return BoxedValue::New<ExtValue_MutexLock>(std::move(parent), args); }  #ifdef ZEOLITE_PUBLIC_NAMESPACE
lib/util/src/Extension_SimpleInput.cpp view
@@ -29,7 +29,7 @@ #endif  // ZEOLITE_PUBLIC_NAMESPACE  namespace {-extern const BoxedValue& Var_stdin;+extern const BoxedValue Var_stdin; }  // namespace  struct ExtCategory_SimpleInput : public Category_SimpleInput {@@ -38,14 +38,15 @@ struct ExtType_SimpleInput : public Type_SimpleInput {   inline ExtType_SimpleInput(Category_SimpleInput& p, Params<0>::Type params) : Type_SimpleInput(p, params) {} -  ReturnTuple Call_stdin(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_stdin(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("SimpleInput.stdin")     return ReturnTuple(Var_stdin);   } };  struct ExtValue_SimpleInput : public Value_SimpleInput {-  inline ExtValue_SimpleInput(S<Type_SimpleInput> p, const ValueTuple& args) : Value_SimpleInput(p) {}+  inline ExtValue_SimpleInput(S<const Type_SimpleInput> p, const ValueTuple& args)+    : Value_SimpleInput(std::move(p)) {}    ReturnTuple Call_pastEnd(const ParamTuple& params, const ValueTuple& args) final {     TRACE_FUNCTION("SimpleInput.pastEnd")@@ -75,7 +76,7 @@ };  namespace {-const BoxedValue& Var_stdin = *new BoxedValue(BoxedValue::New<ExtValue_SimpleInput>(CreateType_SimpleInput(Params<0>::Type()), ArgTuple()));+const BoxedValue Var_stdin = BoxedValue::New<ExtValue_SimpleInput>(CreateType_SimpleInput(Params<0>::Type()), ArgTuple()); }  // namespace  Category_SimpleInput& CreateCategory_SimpleInput() {@@ -83,10 +84,12 @@   return category; } -S<Type_SimpleInput> CreateType_SimpleInput(Params<0>::Type params) {+S<const Type_SimpleInput> CreateType_SimpleInput(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_SimpleInput(CreateCategory_SimpleInput(), Params<0>::Type()));   return cached; }++void RemoveType_SimpleInput(const Params<0>::Type& params) {}  #ifdef ZEOLITE_PUBLIC_NAMESPACE }  // namespace ZEOLITE_PUBLIC_NAMESPACE
lib/util/src/Extension_SimpleMutex.cpp view
@@ -27,7 +27,7 @@ namespace ZEOLITE_PUBLIC_NAMESPACE { #endif  // ZEOLITE_PUBLIC_NAMESPACE -BoxedValue CreateValue_SimpleMutex(S<Type_SimpleMutex> parent);+BoxedValue CreateValue_SimpleMutex(S<const Type_SimpleMutex> parent);  struct ExtCategory_SimpleMutex : public Category_SimpleMutex { };@@ -35,14 +35,15 @@ struct ExtType_SimpleMutex : public Type_SimpleMutex {   inline ExtType_SimpleMutex(Category_SimpleMutex& p, Params<0>::Type params) : Type_SimpleMutex(p, params) {} -  ReturnTuple Call_new(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_new(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("SimpleMutex.new")     return ReturnTuple(CreateValue_SimpleMutex(CreateType_SimpleMutex(Params<0>::Type())));   } };  struct ExtValue_SimpleMutex : public Value_SimpleMutex {-  inline ExtValue_SimpleMutex(S<Type_SimpleMutex> p) : Value_SimpleMutex(p) {}+  inline ExtValue_SimpleMutex(S<const Type_SimpleMutex> p)+    : Value_SimpleMutex(std::move(p)) {}    ReturnTuple Call_lock(const ParamTuple& params, const ValueTuple& args) final {     TRACE_FUNCTION("SimpleMutex.lock")@@ -64,13 +65,15 @@   return category; } -S<Type_SimpleMutex> CreateType_SimpleMutex(Params<0>::Type params) {+S<const Type_SimpleMutex> CreateType_SimpleMutex(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_SimpleMutex(CreateCategory_SimpleMutex(), Params<0>::Type()));   return cached; } -BoxedValue CreateValue_SimpleMutex(S<Type_SimpleMutex> parent) {-  return BoxedValue::New<ExtValue_SimpleMutex>(parent);+void RemoveType_SimpleMutex(const Params<0>::Type& params) {}++BoxedValue CreateValue_SimpleMutex(S<const Type_SimpleMutex> parent) {+  return BoxedValue::New<ExtValue_SimpleMutex>(std::move(parent)); }  #ifdef ZEOLITE_PUBLIC_NAMESPACE
lib/util/src/Extension_SimpleOutput.cpp view
@@ -30,9 +30,9 @@ #endif  // ZEOLITE_PUBLIC_NAMESPACE  namespace {-extern const BoxedValue& Var_stdout;-extern const BoxedValue& Var_stderr;-extern const BoxedValue& Var_error;+extern const BoxedValue Var_stdout;+extern const BoxedValue Var_stderr;+extern const BoxedValue Var_error; }  // namespace  struct ExtCategory_SimpleOutput : public Category_SimpleOutput {@@ -41,17 +41,17 @@ struct ExtType_SimpleOutput : public Type_SimpleOutput {   inline ExtType_SimpleOutput(Category_SimpleOutput& p, Params<0>::Type params) : Type_SimpleOutput(p, params) {} -  ReturnTuple Call_error(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_error(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("SimpleOutput.error")     return ReturnTuple(Var_error);   } -  ReturnTuple Call_stderr(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_stderr(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("SimpleOutput.stderr")     return ReturnTuple(Var_stderr);   } -  ReturnTuple Call_stdout(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_stdout(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("SimpleOutput.stdout")     return ReturnTuple(Var_stdout);   }@@ -66,8 +66,8 @@ }  // namespace  struct ExtValue_SimpleOutput : public Value_SimpleOutput {-  inline ExtValue_SimpleOutput(S<Type_SimpleOutput> p, S<Writer> w)-    : Value_SimpleOutput(p), writer(w) {}+  inline ExtValue_SimpleOutput(S<const Type_SimpleOutput> p, S<Writer> w)+    : Value_SimpleOutput(std::move(p)), writer(w) {}    ReturnTuple Call_flush(const ParamTuple& params, const ValueTuple& args) final {     TRACE_FUNCTION("SimpleOutput.flush")@@ -118,9 +118,9 @@   std::ostringstream output; }; -const BoxedValue& Var_stdout = *new BoxedValue(BoxedValue::New<ExtValue_SimpleOutput>(CreateType_SimpleOutput(Params<0>::Type()), S_get(new StreamWriter(std::cout))));-const BoxedValue& Var_stderr = *new BoxedValue(BoxedValue::New<ExtValue_SimpleOutput>(CreateType_SimpleOutput(Params<0>::Type()), S_get(new StreamWriter(std::cerr))));-const BoxedValue& Var_error  = *new BoxedValue(BoxedValue::New<ExtValue_SimpleOutput>(CreateType_SimpleOutput(Params<0>::Type()), S_get(new ErrorWriter())));+const BoxedValue Var_stdout = BoxedValue::New<ExtValue_SimpleOutput>(CreateType_SimpleOutput(Params<0>::Type()), S_get(new StreamWriter(std::cout)));+const BoxedValue Var_stderr = BoxedValue::New<ExtValue_SimpleOutput>(CreateType_SimpleOutput(Params<0>::Type()), S_get(new StreamWriter(std::cerr)));+const BoxedValue Var_error  = BoxedValue::New<ExtValue_SimpleOutput>(CreateType_SimpleOutput(Params<0>::Type()), S_get(new ErrorWriter()));  }  // namespace @@ -129,10 +129,12 @@   return category; } -S<Type_SimpleOutput> CreateType_SimpleOutput(Params<0>::Type params) {+S<const Type_SimpleOutput> CreateType_SimpleOutput(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_SimpleOutput(CreateCategory_SimpleOutput(), Params<0>::Type()));   return cached; }++void RemoveType_SimpleOutput(const Params<0>::Type& params) {}  #ifdef ZEOLITE_PUBLIC_NAMESPACE }  // namespace ZEOLITE_PUBLIC_NAMESPACE
lib/util/src/Extension_SpinlockMutex.cpp view
@@ -27,7 +27,7 @@ namespace ZEOLITE_PUBLIC_NAMESPACE { #endif  // ZEOLITE_PUBLIC_NAMESPACE -BoxedValue CreateValue_SpinlockMutex(S<Type_SpinlockMutex> parent);+BoxedValue CreateValue_SpinlockMutex(S<const Type_SpinlockMutex> parent);  struct ExtCategory_SpinlockMutex : public Category_SpinlockMutex { };@@ -35,14 +35,15 @@ struct ExtType_SpinlockMutex : public Type_SpinlockMutex {   inline ExtType_SpinlockMutex(Category_SpinlockMutex& p, Params<0>::Type params) : Type_SpinlockMutex(p, params) {} -  ReturnTuple Call_new(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {+  ReturnTuple Call_new(const ParamTuple& params, const ValueTuple& args) const final {     TRACE_FUNCTION("SpinlockMutex.new")     return ReturnTuple(CreateValue_SpinlockMutex(CreateType_SpinlockMutex(Params<0>::Type())));   } };  struct ExtValue_SpinlockMutex : public Value_SpinlockMutex {-  inline ExtValue_SpinlockMutex(S<Type_SpinlockMutex> p) : Value_SpinlockMutex(p) {}+  inline ExtValue_SpinlockMutex(S<const Type_SpinlockMutex> p)+    : Value_SpinlockMutex(std::move(p)) {}    ReturnTuple Call_lock(const ParamTuple& params, const ValueTuple& args) final {     TRACE_FUNCTION("SpinlockMutex.lock")@@ -64,13 +65,15 @@   return category; } -S<Type_SpinlockMutex> CreateType_SpinlockMutex(Params<0>::Type params) {+S<const Type_SpinlockMutex> CreateType_SpinlockMutex(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_SpinlockMutex(CreateCategory_SpinlockMutex(), Params<0>::Type()));   return cached; } -BoxedValue CreateValue_SpinlockMutex(S<Type_SpinlockMutex> parent) {-  return BoxedValue::New<ExtValue_SpinlockMutex>(parent);+void RemoveType_SpinlockMutex(const Params<0>::Type& params) {}++BoxedValue CreateValue_SpinlockMutex(S<const Type_SpinlockMutex> parent) {+  return BoxedValue::New<ExtValue_SpinlockMutex>(std::move(parent)); }  #ifdef ZEOLITE_PUBLIC_NAMESPACE
+ lib/util/src/counters.0rx view
@@ -0,0 +1,173 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define Counter {+  zeroIndexed (max) {+    return ForwardCounter.new(max)+  }++  revZeroIndexed (max) {+    return ReverseCounter.new(max)+  }++  unlimited () {+    return UnlimitedCounter.new()+  }+}++concrete ForwardCounter {+  @type new (Int) -> (optional Order<Int>)+}++define ForwardCounter {+  $ReadOnly[limit]$++  refines Order<Int>++  @value Int current+  @value Int limit++  new (max) {+    if (max > 0) {+      return #self{ 0, max }+    } else {+      return empty+    }+  }++  next () {+    if (current < limit-1) {+      current <- current+1+      return self+    } else {+      return empty+    }+  }++  get () {+    return current+  }+}++concrete ReverseCounter {+  @type new (Int) -> (optional Order<Int>)+}++define ReverseCounter {+  refines Order<Int>++  @value Int current++  new (max) {+    if (max > 0) {+      return #self{ max-1 }+    } else {+      return empty+    }+  }++  next () {+    if (current > 0) {+      current <- current-1+      return self+    } else {+      return empty+    }+  }++  get () {+    return current+  }+}++concrete UnlimitedCounter {+  @type new () -> (optional Order<Int>)+}++define UnlimitedCounter {+  refines Order<Int>++  @value Int current++  new () {+    return #self{ 0 }+  }++  next () {+    current <- current+1+    return self+  }++  get () {+    return current+  }+}++define Repeat {+  $ReadOnly[limit,value]$++  refines Order<#x>++  @value #x value+  @value Int current+  @value optional Int limit++  times (value,max) {+    if (max <= 0) {+      return empty+    } else {+      return Repeat<#y>{ value, 0, max }+    }+  }++  unlimited (value) {+    return Repeat<#y>{ value, 0, empty }+  }++  next () {+    if (!present(limit) || current+1 < require(limit)) {+      current <- current+1+      return self+    } else {+      return empty+    }+  }++  get () {+    return value+  }+}++define Ranges {+  min (x,y) (z) {+    z, _ <- minMax<?>(x,y)+  }++  max (x,y) (z) {+    _, z <- minMax<?>(x,y)+  }++  minMax (x,y) {+    // NOTE: Using y < x makes the default x, y when they are equal.+    if (y `#x.lessThan` x) {+      return y, x+    } else {+      return x, y+    }+  }+}
+ lib/util/src/extra.0rx view
@@ -0,0 +1,74 @@+/* -----------------------------------------------------------------------------+Copyright 2019-2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define Void {+  @category Void singleton <- Void{ }++  formatted () {+    return "void"+  }++  equals (_,_) {+    return true+  }++  lessThan (_,_) {+    return true+  }++  void () {+    return singleton+  }+}++define ErrorOr {+  @value optional #x maybeValue+  @value optional Formatted maybeError++  value (x) {+    return ErrorOr<#x>{ x, empty }+  }++  error (message) {+    return ErrorOr<all>{ empty, message }+  }++  isError () {+    return !present(maybeValue) && present(maybeError)+  }++  getValue () {+    if (present(maybeValue)) {+      return require(maybeValue)+    } else {+      fail(require(maybeError))+    }+  }++  getError () {+    return require(maybeError)+  }++  convertError () {+    if (!present(maybeError)) {+      fail("no error present to convert")+    } else {+      return ErrorOr<all>{ empty, maybeError }+    }+  }+}
+ lib/util/src/helpers.0rx view
@@ -0,0 +1,122 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define OrderH {+  lessThan (x,y) {+    return lessThanWith<#x,#x>(x,y)+  }++  lessThanWith (x,y) {+    optional Order<#x> xx <- x+    optional Order<#x> yy <- y+    $Hidden[x,y]$+    while (present(xx) && present(yy)) {+      $ReadOnly[xx,yy]$+      if (require(xx).get() `#xx.lessThan` require(yy).get()) {+        return true+      } elif (require(yy).get() `#xx.lessThan` require(xx).get()) {+        return false+      }+    } update {+      xx <- require(xx).next()+      yy <- require(yy).next()+    }+    return present(yy)+  }++  equals (x,y) {+    return equalsWith<#x,#x>(x,y)+  }++  equalsWith (x,y) {+    optional Order<#x> xx <- x+    optional Order<#x> yy <- y+    $Hidden[x,y]$+    while (present(xx) && present(yy)) {+      $ReadOnly[xx,yy]$+      if (!(require(xx).get() `#xx.equals` require(yy).get())) {+        return false+      }+    } update {+      xx <- require(xx).next()+      yy <- require(yy).next()+    }+    return !present(xx) && !present(yy)+  }+}++define ReadAtH {+  lessThan (x,y) {+    return lessThanWith<#x,#x>(x,y)+  }++  lessThanWith (x,y) {+    traverse (Counter.zeroIndexed(Ranges:min<?>(x.size(),y.size())) -> Int i) {+      $ReadOnly[i]$+      if (x.readAt(i) `#xx.lessThan` y.readAt(i)) {+        return true+      } elif (y.readAt(i) `#xx.lessThan` x.readAt(i)) {+        return false+      }+    }+    return x.size() < y.size()+  }++  equals (x,y) {+    return equalsWith<#x,#x>(x,y)+  }++  equalsWith (x,y) {+    if (x.size() != y.size()) {+      return false+    }+    traverse (Counter.zeroIndexed(x.size()) -> Int i) {+      $ReadOnly[i]$+      if (!(x.readAt(i) `#xx.equals` y.readAt(i))) {+        return false+      }+    }+    return true+  }+}++define Reversed {+  lessThan (x,y) {+    return y `#x.lessThan` x+  }+}++define BySize {+  equals (x,y) {+    return x.size() == y.size()+  }++  lessThan (x,y) {+    return x.size() < y.size()+  }+}++define AlwaysEqual {+  equals (_,_) {+    return true+  }++  lessThan (_,_) {+    return false+  }+}
+ lib/util/src/input.0rx view
@@ -0,0 +1,76 @@+/* -----------------------------------------------------------------------------+Copyright 2019-2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define TextReader {+  @value BlockReader<String> reader+  @value String buffer++  fromBlockReader (reader) {+    return TextReader{ reader, "" }+  }++  readAll (reader) {+    [Append<Formatted>&Build<String>] builder <- String.builder()+    while (!reader.pastEnd()) {+      \ builder.append(reader.readBlock($ExprLookup[BLOCK_READ_SIZE]$))+    }+    return builder.build()+  }++  readNextLine () {+    while (true) {+      Int newline <- findNewline()+      if (newline < 0) {+        if (reader.pastEnd()) {+          cleanup {+            buffer <- ""+          } in return buffer+        } else {+          buffer <- buffer+reader.readBlock($ExprLookup[BLOCK_READ_SIZE]$)+        }+      } else {+        return takeLine(newline)+      }+    }+    return ""+  }++  pastEnd () {+    return buffer.size() == 0 && reader.pastEnd()+  }++  @value findNewline () -> (Int)+  findNewline () {+    traverse (Counter.zeroIndexed(buffer.size()) -> Int position) {+      $ReadOnly[position]$+      Char c <- buffer.readAt(position)+      // TODO: Maybe the line separator should be a factory argument.+      if (c == '\n' || c == '\r') {+        return position+      }+    }+    return -1+  }++  @value takeLine (Int) -> (String)+  takeLine (position) {+    String data <- buffer.subSequence(0,position)+    buffer <- buffer.subSequence(position+1,buffer.size()-position-1)+    return data+  }+}
+ lib/util/src/output.0rx view
@@ -0,0 +1,45 @@+/* -----------------------------------------------------------------------------+Copyright 2019-2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define LazyStream {+  @value optional #x value+  @value optional LazyStream<#x> next++  new () {+    return LazyStream<#x>{ empty, empty }+  }++  append (value2) {+    return LazyStream<#x>{ value2, self }+  }++  @value recursive (Writer<#x>) -> ()+  recursive (writer) {+    if (present(next)) {+      \ require(next).recursive(writer)+    }+    if (present(value)) {+      \ writer.write(require(value))+    }+  }++  writeTo (writer) {+    \ recursive(writer)+    \ writer.flush()+  }+}
+ lib/util/src/parse.0rx view
@@ -0,0 +1,299 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define ParseChars {+  float (data) {+    Float total <- 0.0+    Float sign <- 0.0+    Bool hasDigit <- false+    Bool needExpDigits <- false++    optional Order<Char> curr <- data.defaultOrder()++    // Parse to the left of '.'.+    while (present(curr)) {+      Char c <- require(curr).get()+      curr <- require(curr).next()+      $ReadOnly[c]$+      $Hidden[curr]$+      if (sign == 0.0) {+        if (c == '-') {+          sign <- -1.0+          continue+        } elif (c == '+') {+          sign <- 1.0+          continue+        } else {+          sign <- 1.0+        }+      }+      if (c >= '0' && c <= '9') {+        hasDigit <- true+        total <- 10.0*total+(c.asInt() - '0'.asInt()).asFloat()+        continue+      }+      if (c == '.') {+        break+      }+      if (c == 'E' || c == 'e') {+        if (!hasDigit) {+          return autoFail<Float>(c)+        } else {+          needExpDigits <- true+          break+        }+      }+      return autoFail<Float>(c)+    }++    if (!needExpDigits) {++      Float decimal <- 0.1++      // Parse to the right of '.'.+      while (present(curr)) {+        Char c <- require(curr).get()+        curr <- require(curr).next()+        $ReadOnly[c]$+        $Hidden[curr]$+        if (c >= '0' && c <= '9') {+          hasDigit <- true+          total <- total+decimal*(c.asInt() - '0'.asInt()).asFloat()+          decimal <- decimal/10.0+          continue+        }+        if (c == 'E' || c == 'e') {+          if (!hasDigit) {+            return autoFail<Float>(c)+          } else {+            needExpDigits <- true+            break+          }+        }+        return autoFail<Float>(c)+      }++    }++    if (!hasDigit) {+      return ErrorOr:error("missing digits in Float value")+    }++    total <- sign*total+    $Hidden[sign,hasDigit]$++    Int expSign <- 0+    Int exp <- 0++    if (needExpDigits) {++      // Parse exponent.+      while (present(curr)) {+        Char c <- require(curr).get()+        curr <- require(curr).next()+        $ReadOnly[c]$+        $Hidden[curr,total]$+        if (expSign == 0) {+          if (c == '-') {+            expSign <- -1+            continue+          } elif (c == '+') {+            expSign <- 1+            continue+          } else {+            expSign <- 1+          }+        }+        if (c >= '0' && c <= '9') {+          needExpDigits <- false+          exp <- 10*exp+(c.asInt() - '0'.asInt())+          continue+        }+        return autoFail<Float>(c)+      }++    }++    if (present(curr)) {+      return autoFail<Float>(require(curr).get())+    }++    if (needExpDigits) {+      return ErrorOr:error("missing digits in Float exponent")+    }++    traverse (Counter.zeroIndexed(exp) -> _) {+      if (expSign > 0) {+        total <- 10.0*total+      } else {+        total <- total/10.0+      }+    }++    return ErrorOr:value<?>(total)+  }++  int (data) {+    Int total <- 0+    Int sign <- 0+    Bool hasDigit <- false++    traverse (data.defaultOrder() -> Char c) {+      if (sign == 0) {+        if (c == '-') {+          sign <- -1+          continue+        } elif (c == '+') {+          sign <- 1+          continue+        } else {+          sign <- 1+        }+      }+      if (c >= '0' && c <= '9') {+        hasDigit <- true+        total <- 10*total+(c.asInt() - '0'.asInt())+        continue+      }+      return autoFail<Int>(c)+    }++    if (!hasDigit) {+      return ErrorOr:error("missing digits in Int value")+    }++    return ErrorOr:value<?>(sign*total)+  }++  hexInt (data) {+    Int total <- 0+    Int sign <- 0+    Bool hasDigit <- false++    traverse (data.defaultOrder() -> Char c) {+      if (sign == 0) {+        if (c == '-') {+          sign <- -1+          continue+        } elif (c == '+') {+          sign <- 1+          continue+        } else {+          sign <- 1+        }+      }+      if (c >= '0' && c <= '9') {+        hasDigit <- true+        total <- 16*total+(c.asInt() - '0'.asInt())+        continue+      }+      if (c >= 'a' && c <= 'f') {+        hasDigit <- true+        total <- 16*total+(c.asInt() - 'a'.asInt() + 10)+        continue+      }+      if (c >= 'A' && c <= 'F') {+        hasDigit <- true+        total <- 16*total+(c.asInt() - 'A'.asInt() + 10)+        continue+      }+      return autoFail<Int>(c)+    }++    if (!hasDigit) {+      return ErrorOr:error("missing digits in Int value")+    }++    return ErrorOr:value<?>(sign*total)+  }++  octInt (data) {+    Int total <- 0+    Int sign <- 0+    Bool hasDigit <- false++    traverse (data.defaultOrder() -> Char c) {+      if (sign == 0) {+        if (c == '-') {+          sign <- -1+          continue+        } elif (c == '+') {+          sign <- 1+          continue+        } else {+          sign <- 1+        }+      }+      if (c >= '0' && c <= '7') {+        hasDigit <- true+        total <- 8*total+(c.asInt() - '0'.asInt())+        continue+      }+      return autoFail<Int>(c)+    }++    if (!hasDigit) {+      return ErrorOr:error("missing digits in Int value")+    }++    return ErrorOr:value<?>(sign*total)+  }++  binInt (data) {+    Int total <- 0+    Int sign <- 0+    Bool hasDigit <- false++    traverse (data.defaultOrder() -> Char c) {+      if (sign == 0) {+        if (c == '-') {+          sign <- -1+          continue+        } elif (c == '+') {+          sign <- 1+          continue+        } else {+          sign <- 1+        }+      }+      if (c >= '0' && c <= '1') {+        hasDigit <- true+        total <- 2*total+(c.asInt() - '0'.asInt())+        continue+      }+      return autoFail<Int>(c)+    }++    if (!hasDigit) {+      return ErrorOr:error("missing digits in Int value")+    }++    return ErrorOr:value<?>(sign*total)+  }++  @type autoFail<#x> (Char) -> (ErrorOr<all>)+  autoFail (c) {+    return ErrorOr:error(String.builder()+        .append("failed to parse ")+        .append(typename<#x>())+        .append(" at '")+        .append(c)+        .append("'")+        .build())+  }+}
+ lib/util/src/testing.0rx view
@@ -0,0 +1,58 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++$TestsOnly$++define UtilTesting {+  checkError (x) {+    if (!x.isError()) {+      fail(String.builder()+          .append("expected an error but got value ")+          .append(x.getValue())+          .build())+    }+  }++  checkSuccess (x,y) {+    if (x.isError()) {+      fail(String.builder()+          .append("expected ")+          .append(y)+          .append(" but got error ")+          .append(x.getError())+          .build())+    } else {+      \ Testing.checkEquals<?>(x.getValue(),y)+    }+  }++  checkSuccessBetween (x,l,h) {+    if (x.isError()) {+      fail(String.builder()+          .append("expected value between ")+          .append(l)+          .append(" and ")+          .append(h)+          .append(" but got error ")+          .append(x.getError())+          .build())+    } else {+      \ Testing.checkBetween<?>(x.getValue(),l,h)+    }+  }+}
+ lib/util/test/counters.0rt view
@@ -0,0 +1,116 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "counter tests" {+  success+}++unittest zeroIndexed {+  Int index <- 0++  traverse (Counter.zeroIndexed(10) -> Int i) {+    \ Testing.checkEquals<?>(i,index)+    index <- index+1+  }++  \ Testing.checkEquals<?>(index,10)+}++unittest revZeroIndexed {+  Int index <- 10++  traverse (Counter.revZeroIndexed(10) -> Int i) {+    index <- index-1+    \ Testing.checkEquals<?>(i,index)+  }++  \ Testing.checkEquals<?>(index,0)+}++unittest unlimitedCount {+  Int index <- 0++  traverse (Counter.unlimited() -> Int i) {+    \ Testing.checkEquals<?>(i,index)+    if ((index <- index+1) >= 10) {+      break+    }+  }++  \ Testing.checkEquals<?>(index,10)+}++unittest times {+  Int index <- 0++  traverse ("message" `Repeat:times<?>` 10 -> String m) {+    \ Testing.checkEquals<?>(m,"message")+    index <- index+1+  }++  \ Testing.checkEquals<?>(index,10)+}++unittest unlimitedValue {+  Int index <- 0++  traverse (Repeat:unlimited<?>("message") -> String m) {+    \ Testing.checkEquals<?>(m,"message")+    if ((index <- index+1) >= 10) {+      break+    }+  }++  \ Testing.checkEquals<?>(index,10)+}++unittest minMax {+  scoped {+    Int x, Int y <- Ranges:minMax<?>(10,3)+  } in {+    \ Testing.checkEquals<?>(x,3)+    \ Testing.checkEquals<?>(y,10)+  }++  scoped {+    Int x, Int y <- Ranges:minMax<?>(3,10)+  } in {+    \ Testing.checkEquals<?>(x,3)+    \ Testing.checkEquals<?>(y,10)+  }+}++unittest min {+  scoped {+    Int x <- Ranges:min<?>(10,3)+  } in \ Testing.checkEquals<?>(x,3)++  scoped {+    Int x <- Ranges:min<?>(3,10)+  } in \ Testing.checkEquals<?>(x,3)+}++unittest max {+  scoped {+    Int x <- Ranges:max<?>(10,3)+  } in \ Testing.checkEquals<?>(x,10)++  scoped {+    Int x <- Ranges:max<?>(3,10)+  } in \ Testing.checkEquals<?>(x,10)+}
+ lib/util/test/extra.0rt view
@@ -0,0 +1,118 @@+/* -----------------------------------------------------------------------------+Copyright 2019-2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "Argv checks" {+  success+  args "arg1" "arg2" "arg3" "arg4"+}++unittest global {+  Int count <- Argv.global().size()+  \ Testing.checkEquals<?>(count,5)+  \ Testing.checkEquals<?>(Argv.global().readAt(0),"testcase")+  \ Testing.checkEquals<?>(Argv.global().readAt(1),"arg1")+  \ Testing.checkEquals<?>(Argv.global().readAt(2),"arg2")+  \ Testing.checkEquals<?>(Argv.global().readAt(3),"arg3")+  \ Testing.checkEquals<?>(Argv.global().readAt(4),"arg4")+}++unittest subSequence {+  ReadAt<String> argv <- Argv.global().subSequence(2,2)+  Int count <- argv.size()+  \ Testing.checkEquals<?>(count,2)+  \ Testing.checkEquals<?>(argv.readAt(0),"arg2")+  \ Testing.checkEquals<?>(argv.readAt(1),"arg3")+}+++testcase "Argv readAt out of bounds" {+  crash+  require "index 5"+}++unittest test {+  \ Argv.global().readAt(5)+}+++testcase "Argv subSequence out of bounds" {+  crash+  require "index 5"+}++unittest test {+  \ Argv.global().subSequence(5,1)+}+++testcase "ErrorOr checks" {+  success+}++unittest withValue {+  ErrorOr<Int> value <- ErrorOr:value<Int>(10)+  \ Testing.checkEquals<?>(value.isError(),false)+  \ Testing.checkEquals<?>(value.getValue(),10)+}++unittest withError {+  ErrorOr<String> value <- ErrorOr:error("error message")+  \ Testing.checkEquals<?>(value.isError(),true)+  \ Testing.checkEquals<?>(value.getError().formatted(),"error message")+}++unittest convertError {+  scoped {+    ErrorOr<String> error <- ErrorOr:error("error message")+  } in ErrorOr<Int> value <- error.convertError()+  \ Testing.checkEquals<?>(value.getError().formatted(),"error message")+}+++testcase "ErrorOr getError() crashes with value" {+  crash+  require "empty"+}++unittest test {+  ErrorOr<Int> value <- ErrorOr:value<Int>(10)+  \ value.getError()+}+++testcase "ErrorOr getValue() crashes with error" {+  crash+  require "error message"+}++unittest test {+  ErrorOr<String> value <- ErrorOr:error("error message")+  \ value.getValue()+}+++testcase "ErrorOr convertError() crashes with no error" {+  crash+  require "no error"+}++unittest test {+  ErrorOr<Int> value <- ErrorOr:value<Int>(10)+  \ value.convertError()+}+
+ lib/util/test/helpers.0rt view
@@ -0,0 +1,139 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "comparator helper tests" {+  success+}++unittest orderHLessThan {+  \ Testing.checkEquals<?>("".defaultOrder()    `OrderH:lessThan<?>` "".defaultOrder(),   false)+  \ Testing.checkEquals<?>("a".defaultOrder()   `OrderH:lessThan<?>` "abc".defaultOrder(),true)+  \ Testing.checkEquals<?>("b".defaultOrder()   `OrderH:lessThan<?>` "abc".defaultOrder(),false)+  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:lessThan<?>` "abc".defaultOrder(),false)+  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:lessThan<?>` "a".defaultOrder(),  false)+  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:lessThan<?>` "b".defaultOrder(),  true)+}++unittest orderHLessThanWith {+  \ Testing.checkEquals<?>("".defaultOrder()    `OrderH:lessThanWith<?,Reversed<Char>>` "".defaultOrder(),   false)+  \ Testing.checkEquals<?>("a".defaultOrder()   `OrderH:lessThanWith<?,Reversed<Char>>` "abc".defaultOrder(),true)+  \ Testing.checkEquals<?>("b".defaultOrder()   `OrderH:lessThanWith<?,Reversed<Char>>` "abc".defaultOrder(),true)+  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:lessThanWith<?,Reversed<Char>>` "abc".defaultOrder(),false)+  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:lessThanWith<?,Reversed<Char>>` "a".defaultOrder(),  false)+  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:lessThanWith<?,Reversed<Char>>` "b".defaultOrder(),  false)+}++unittest orderHEquals {+  \ Testing.checkEquals<?>("".defaultOrder()    `OrderH:equals<?>` "".defaultOrder(),   true)+  \ Testing.checkEquals<?>("a".defaultOrder()   `OrderH:equals<?>` "abc".defaultOrder(),false)+  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:equals<?>` "abc".defaultOrder(),true)+  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:equals<?>` "a".defaultOrder(),  false)+}++unittest orderHEqualsWith {+  \ Testing.checkEquals<?>("".defaultOrder()    `OrderH:equalsWith<?,IgnoreCase>` "".defaultOrder(),   true)+  \ Testing.checkEquals<?>("a".defaultOrder()   `OrderH:equalsWith<?,IgnoreCase>` "ABC".defaultOrder(),false)+  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:equalsWith<?,IgnoreCase>` "ABC".defaultOrder(),true)+  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:equalsWith<?,IgnoreCase>` "DEF".defaultOrder(),false)+  \ Testing.checkEquals<?>("abc".defaultOrder() `OrderH:equalsWith<?,IgnoreCase>` "A".defaultOrder(),  false)+}++unittest readAtHLessThan {+  \ Testing.checkEquals<?>(""    `ReadAtH:lessThan<?>` "",   false)+  \ Testing.checkEquals<?>("a"   `ReadAtH:lessThan<?>` "abc",true)+  \ Testing.checkEquals<?>("b"   `ReadAtH:lessThan<?>` "abc",false)+  \ Testing.checkEquals<?>("abc" `ReadAtH:lessThan<?>` "abc",false)+  \ Testing.checkEquals<?>("abc" `ReadAtH:lessThan<?>` "a",  false)+  \ Testing.checkEquals<?>("abc" `ReadAtH:lessThan<?>` "b",  true)+}++unittest readAtHLessThanWith {+  \ Testing.checkEquals<?>(""    `ReadAtH:lessThanWith<?,Reversed<Char>>` "",   false)+  \ Testing.checkEquals<?>("a"   `ReadAtH:lessThanWith<?,Reversed<Char>>` "abc",true)+  \ Testing.checkEquals<?>("b"   `ReadAtH:lessThanWith<?,Reversed<Char>>` "abc",true)+  \ Testing.checkEquals<?>("abc" `ReadAtH:lessThanWith<?,Reversed<Char>>` "abc",false)+  \ Testing.checkEquals<?>("abc" `ReadAtH:lessThanWith<?,Reversed<Char>>` "a",  false)+  \ Testing.checkEquals<?>("abc" `ReadAtH:lessThanWith<?,Reversed<Char>>` "b",  false)+}++unittest readAtHEquals {+  \ Testing.checkEquals<?>(""    `ReadAtH:equals<?>` "",   true)+  \ Testing.checkEquals<?>("a"   `ReadAtH:equals<?>` "abc",false)+  \ Testing.checkEquals<?>("abc" `ReadAtH:equals<?>` "abc",true)+  \ Testing.checkEquals<?>("abc" `ReadAtH:equals<?>` "a",  false)+}++unittest readAtHEqualsWith {+  \ Testing.checkEquals<?>(""    `ReadAtH:equalsWith<?,IgnoreCase>` "",   true)+  \ Testing.checkEquals<?>("a"   `ReadAtH:equalsWith<?,IgnoreCase>` "ABC",false)+  \ Testing.checkEquals<?>("abc" `ReadAtH:equalsWith<?,IgnoreCase>` "ABC",true)+  \ Testing.checkEquals<?>("abc" `ReadAtH:equalsWith<?,IgnoreCase>` "DEF",false)+  \ Testing.checkEquals<?>("abc" `ReadAtH:equalsWith<?,IgnoreCase>` "a",  false)+}++concrete IgnoreCase {+  defines Equals<Char>+}++define IgnoreCase {+  equals (x,y) {+    return toLower(x) == toLower(y)+  }++  @type toLower (Char) -> (Char)+  toLower (c) {+    if (c >= 'A' && c <= 'Z') {+      return (c.asInt() - 'A'.asInt() + 'a'.asInt()).asChar()+    } else {+      return c+    }+  }+}++unittest reversed {+  \ Testing.checkEquals<?>(0 `Reversed<Int>.lessThan` 0,false)+  \ Testing.checkEquals<?>(0 `Reversed<Int>.lessThan` 1,false)+  \ Testing.checkEquals<?>(1 `Reversed<Int>.lessThan` 0,true)+}++unittest bySizeLessThan {+  \ Testing.checkEquals<?>(""   `BySize.lessThan` "",  false)+  \ Testing.checkEquals<?>("b"  `BySize.lessThan` "ac",true)+  \ Testing.checkEquals<?>("ac" `BySize.lessThan` "b", false)+  \ Testing.checkEquals<?>("a"  `BySize.lessThan` "b", false)+}++unittest bySizeEquals {+  \ Testing.checkEquals<?>(""   `BySize.equals` "",  true)+  \ Testing.checkEquals<?>("b"  `BySize.equals` "ac",false)+  \ Testing.checkEquals<?>("ac" `BySize.equals` "b", false)+  \ Testing.checkEquals<?>("a"  `BySize.equals` "b", true)+}++unittest alwaysEqualLessThan {+  \ Testing.checkEquals<?>(""   `AlwaysEqual.lessThan` "",  false)+  \ Testing.checkEquals<?>("b"  `AlwaysEqual.lessThan` "ac",false)+  \ Testing.checkEquals<?>("ac" `AlwaysEqual.lessThan` "b", false)+}++unittest alwaysEqualEquals {+  \ Testing.checkEquals<?>(""   `AlwaysEqual.equals` "",  true)+  \ Testing.checkEquals<?>("b"  `AlwaysEqual.equals` "ac",true)+  \ Testing.checkEquals<?>("ac" `AlwaysEqual.equals` "b", true)+  \ Testing.checkEquals<?>("a"  `AlwaysEqual.equals` "b", true)+}
+ lib/util/test/input.0rt view
@@ -0,0 +1,99 @@+/* -----------------------------------------------------------------------------+Copyright 2019-2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "TextReader checks" {+  success+}++concrete Helper {+  @type checkNext (TextReader,String) -> ()+}++define Helper {+  checkNext (reader,line) {+    if (reader.pastEnd()) {+      fail("past end")+    }+    String value <- reader.readNextLine()+    \ Testing.checkEquals<?>(value,line)+  }+}++concrete FakeFile {+  refines BlockReader<String>++  @type create () -> (FakeFile)+}++define FakeFile {+  @value Int counter++  create () {+    return FakeFile{ 0 }+  }++  readBlock (_) {+    cleanup {+      counter <- counter+1+    } in if (counter == 0) {+      return "this is a "+    } elif (counter == 1) {+      return "partial line\nwith "+    } elif (counter == 2) {+      return "some more data\nand\nmore lines"+    } elif (counter == 3) {+      return "\nunfinished"+    } else {+      fail("too many reads")+    }+  }++  pastEnd () {+    return counter > 3+  }+}++unittest correctSplit {+    TextReader reader <- TextReader.fromBlockReader(FakeFile.create())+    \ Helper.checkNext(reader,"this is a partial line")+    \ Helper.checkNext(reader,"with some more data")+    \ Helper.checkNext(reader,"and")+    \ Helper.checkNext(reader,"more lines")+    \ Helper.checkNext(reader,"unfinished")+    if (!reader.pastEnd()) {+      fail("not past end")+    }+    if (reader.readNextLine() != "") {+      fail("reused data")+    }+}++unittest readAll {+  String allContents <- TextReader.readAll(FakeFile.create())+  \ Testing.checkEquals<?>(allContents,"this is a partial line\nwith some more data\nand\nmore lines\nunfinished")+}+++testcase "stdin is empty in tests" {+  success+  timeout 1+}++unittest test {+  \ Testing.checkEquals<?>(TextReader.readAll(SimpleInput.stdin()),"")+}
+ lib/util/test/mutex.0rt view
@@ -0,0 +1,55 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "Mutex tests" {+  success+}++unittest simpleLockUnlock {+  Mutex mutex <- SimpleMutex.new()+  \ mutex.lock().unlock().lock().unlock()+}++unittest spinlockLockUnlock {+  Mutex mutex <- SpinlockMutex.new()+  \ mutex.lock().unlock().lock().unlock()+}+++testcase "MutexLock crashes if not freed" {+  crash+  require "not freed"+}++unittest test {+  Mutex mutex <- SimpleMutex.new()+  \ MutexLock.lock(mutex)+}+++testcase "MutexLock crashes if freed twice" {+  crash+  require "freed multiple times"+}++unittest test {+  Mutex mutex <- SimpleMutex.new()+  MutexLock lock <- MutexLock.lock(mutex)+  \ lock.freeResource()+  \ lock.freeResource()+}
+ lib/util/test/output.0rt view
@@ -0,0 +1,48 @@+/* -----------------------------------------------------------------------------+Copyright 2019-2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "stdout writer" {+  success+  require stdout "message"+  exclude stderr "message"+}++unittest test {+  \ LazyStream<Formatted>.new().append("message").writeTo(SimpleOutput.stdout())+}+++testcase "stderr writer" {+  success+  require stderr "message"+  exclude stdout "message"+}++unittest test {+  \ LazyStream<Formatted>.new().append("message").writeTo(SimpleOutput.stderr())+}+++testcase "error writer" {+  crash+  require "message"+}++unittest test {+  \ LazyStream<Formatted>.new().append("message").writeTo(SimpleOutput.error())+}
+ lib/util/test/parse.0rt view
@@ -0,0 +1,221 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "ParseChars tests" {+  success+}++unittest floatNoDecimal {+  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("123"),122.9,123.1)+}++unittest floatDecimalLeft {+  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float(".123"),0.1229,0.1231)+}++unittest floatDecimalRight {+  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("123."),122.9,123.1)+}++unittest floatDecimalMiddle {+  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("123.456"),123.4559,123.4561)+}++unittest floatLeadingTrailingZeros {+  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("000123.456000"),123.4559,123.4561)+}++unittest floatNegative {+  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("-123.456"),-123.4561,-123.4559)+}++unittest floatPositive {+  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("+123.456"),123.4559,123.4561)+}++unittest floatNegativeDecimalLeft {+  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("-.456"),-0.4561,-0.4559)+}++unittest floatExponentNoDecimal {+  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("123E5"),122.9E5,123.1E5)+}++unittest floatNegativeExponent {+  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("123E-5"),122.9E-5,123.1E-5)+}++unittest floatPositiveExponent {+  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("123E+5"),122.9E5,123.1E5)+}++unittest floatExponentDecimalRight {+  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("123.E5"),122.9E5,123.1E5)+}++unittest floatExponentDecimalLeft {+  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float(".123E5"),122.9E2,123.1E2)+}++unittest floatExponentDecimalMiddle {+  \ UtilTesting.checkSuccessBetween<?>(ParseChars.float("123.456E5"),123.4559E5,123.4561E5)+}++unittest floatEmpty {+  \ UtilTesting.checkError(ParseChars.float(""))+}++unittest floatNoDigits {+  \ UtilTesting.checkError(ParseChars.float("."))+}++unittest floatTwoDecimals {+  \ UtilTesting.checkError(ParseChars.float("1.1."))+}++unittest floatExponentNoDigitsNoDecimal {+  \ UtilTesting.checkError(ParseChars.float("E5"))+}++unittest floatExponentNoDigitsDecimal {+  \ UtilTesting.checkError(ParseChars.float(".E5"))+}++unittest floatExponentNoDigitsSign {+  \ UtilTesting.checkError(ParseChars.float("-E5"))+}++unittest floatDecimalAfterExponent {+  \ UtilTesting.checkError(ParseChars.float("1E.5"))+}++unittest floatExponentExcessData {+  \ UtilTesting.checkError(ParseChars.float("123E5Q"))+}++unittest int {+  \ UtilTesting.checkSuccess<?>(ParseChars.int("1234"),1234)+}++unittest intNegative {+  \ UtilTesting.checkSuccess<?>(ParseChars.int("-1234"),-1234)+}++unittest intPositive {+  \ UtilTesting.checkSuccess<?>(ParseChars.int("+1234"),1234)+}++unittest intLeadingZeros {+  \ UtilTesting.checkSuccess<?>(ParseChars.int("001234"),1234)+}++unittest intEmpty {+  \ UtilTesting.checkError(ParseChars.int(""))+}++unittest intJustSign {+  \ UtilTesting.checkError(ParseChars.int("-"))+}++unittest intBadDigit {+  \ UtilTesting.checkError(ParseChars.int("12A"))+}++unittest hexInt {+  \ UtilTesting.checkSuccess<?>(ParseChars.hexInt("12AbCdEf"),\x12ABCDEF)+}++unittest hexIntNegative {+  \ UtilTesting.checkSuccess<?>(ParseChars.hexInt("-12AbCdEf"),-\x12ABCDEF)+}++unittest hexIntPositive {+  \ UtilTesting.checkSuccess<?>(ParseChars.hexInt("+12AbCdEf"),\x12ABCDEF)+}++unittest hexIntLeadingZeros {+  \ UtilTesting.checkSuccess<?>(ParseChars.hexInt("0012AbCdEf"),\x12ABCDEF)+}++unittest hexIntEmpty {+  \ UtilTesting.checkError(ParseChars.hexInt(""))+}++unittest hexIntJustSign {+  \ UtilTesting.checkError(ParseChars.hexInt("-"))+}++unittest hexIntBadDigit {+  \ UtilTesting.checkError(ParseChars.hexInt("12Q"))+}++unittest octInt {+  \ UtilTesting.checkSuccess<?>(ParseChars.octInt("127"),\o127)+}++unittest octIntNegative {+  \ UtilTesting.checkSuccess<?>(ParseChars.octInt("-127"),-\o127)+}++unittest octIntPositive {+  \ UtilTesting.checkSuccess<?>(ParseChars.octInt("+127"),\o127)+}++unittest octIntLeadingZeros {+  \ UtilTesting.checkSuccess<?>(ParseChars.octInt("00127"),\o127)+}++unittest octIntEmpty {+  \ UtilTesting.checkError(ParseChars.octInt(""))+}++unittest octIntJustSign {+  \ UtilTesting.checkError(ParseChars.octInt("-"))+}++unittest octIntBadDigit {+  \ UtilTesting.checkError(ParseChars.octInt("19"))+}++unittest binInt {+  \ UtilTesting.checkSuccess<?>(ParseChars.binInt("100101"),\b100101)+}++unittest binIntNegative {+  \ UtilTesting.checkSuccess<?>(ParseChars.binInt("-100101"),-\b100101)+}++unittest binIntPositive {+  \ UtilTesting.checkSuccess<?>(ParseChars.binInt("+100101"),\b100101)+}++unittest binIntLeadingZeros {+  \ UtilTesting.checkSuccess<?>(ParseChars.binInt("00100101"),\b100101)+}++unittest binIntEmpty {+  \ UtilTesting.checkError(ParseChars.binInt(""))+}++unittest binIntJustSign {+  \ UtilTesting.checkError(ParseChars.binInt("-"))+}++unittest binIntBadDigit {+  \ UtilTesting.checkError(ParseChars.binInt("12"))+}
+ lib/util/test/testing.0rt view
@@ -0,0 +1,96 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "UtilTesting.checkError with error" {+  success+}++unittest test {+  \ UtilTesting.checkError(ErrorOr:error("failure"))+}+++testcase "UtilTesting.checkError with value" {+  crash+  require "message"+}++unittest test {+  \ UtilTesting.checkError(ErrorOr:value<?>("message"))+}+++testcase "UtilTesting.checkSuccess success" {+  success+}++unittest test {+  \ UtilTesting.checkSuccess<?>(ErrorOr:value<?>("message"),"message")+}+++testcase "UtilTesting.checkSuccess bad value" {+  crash+  require "message"+  require "foo"+}++unittest test {+  \ UtilTesting.checkSuccess<?>(ErrorOr:value<?>("foo"),"message")+}+++testcase "UtilTesting.checkSuccess with error" {+  crash+  require "message"+  require "failure"+}++unittest test {+  \ UtilTesting.checkSuccess<?>(ErrorOr:error("failure"),"message")+}+++testcase "UtilTesting.checkSuccessBetween success" {+  success+}++unittest test {+  \ UtilTesting.checkSuccessBetween<?>(ErrorOr:value<?>(1),0,2)+}+++testcase "UtilTesting.checkSuccess bad value" {+  crash+  require "7.+0.*2"+}++unittest test {+  \ UtilTesting.checkSuccessBetween<?>(ErrorOr:value<?>(7),0,2)+}+++testcase "UtilTesting.checkSuccess with error" {+  crash+  require "0.*2"+  require "failure"+}++unittest test {+  \ UtilTesting.checkSuccessBetween<?>(ErrorOr:error("failure"),0,2)+}
− lib/util/testing.0rt
@@ -1,96 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--testcase "UtilTesting.checkError with error" {-  success-}--unittest test {-  \ UtilTesting.checkError(ErrorOr:error("failure"))-}---testcase "UtilTesting.checkError with value" {-  crash-  require "message"-}--unittest test {-  \ UtilTesting.checkError(ErrorOr:value<?>("message"))-}---testcase "UtilTesting.checkSuccess success" {-  success-}--unittest test {-  \ UtilTesting.checkSuccess<?>(ErrorOr:value<?>("message"),"message")-}---testcase "UtilTesting.checkSuccess bad value" {-  crash-  require "message"-  require "foo"-}--unittest test {-  \ UtilTesting.checkSuccess<?>(ErrorOr:value<?>("foo"),"message")-}---testcase "UtilTesting.checkSuccess with error" {-  crash-  require "message"-  require "failure"-}--unittest test {-  \ UtilTesting.checkSuccess<?>(ErrorOr:error("failure"),"message")-}---testcase "UtilTesting.checkSuccessBetween success" {-  success-}--unittest test {-  \ UtilTesting.checkSuccessBetween<?>(ErrorOr:value<?>(1),0,2)-}---testcase "UtilTesting.checkSuccess bad value" {-  crash-  require "7.+0.*2"-}--unittest test {-  \ UtilTesting.checkSuccessBetween<?>(ErrorOr:value<?>(7),0,2)-}---testcase "UtilTesting.checkSuccess with error" {-  crash-  require "0.*2"-  require "failure"-}--unittest test {-  \ UtilTesting.checkSuccessBetween<?>(ErrorOr:error("failure"),0,2)-}
− lib/util/testing.0rx
@@ -1,58 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--$TestsOnly$--define UtilTesting {-  checkError (x) {-    if (!x.isError()) {-      fail(String.builder()-          .append("expected an error but got value ")-          .append(x.getValue().formatted())-          .build())-    }-  }--  checkSuccess (x,y) {-    if (x.isError()) {-      fail(String.builder()-          .append("expected ")-          .append(y.formatted())-          .append(" but got error ")-          .append(x.getError().formatted())-          .build())-    } else {-      \ Testing.checkEquals<?>(x.getValue(),y)-    }-  }--  checkSuccessBetween (x,l,h) {-    if (x.isError()) {-      fail(String.builder()-          .append("expected value between ")-          .append(l.formatted())-          .append(" and ")-          .append(h.formatted())-          .append(" but got error ")-          .append(x.getError().formatted())-          .build())-    } else {-      \ Testing.checkBetween<?>(x.getValue(),l,h)-    }-  }-}
src/Base/CompilerError.hs view
@@ -39,6 +39,9 @@   mapCompilerM,   mapCompilerM_,   mapErrorsM,+  mergeObjectsM,+  silenceErrorsM,+  tryCompilerM, ) where  import Control.Monad.IO.Class@@ -111,6 +114,9 @@ mapCompilerM_ :: CollectErrorsM m => (a -> m b) -> [a] -> m () mapCompilerM_ f = collectAllM_ . map f +tryCompilerM :: CollectErrorsM m => m a -> m (Maybe a)+tryCompilerM x = collectFirstM [x >>= return . Just,return Nothing]+ isCompilerError :: (ErrorContextT t, ErrorContextM (t Identity)) => t Identity a -> Bool isCompilerError = runIdentity . isCompilerErrorT @@ -129,12 +135,28 @@ emptyErrorM :: CollectErrorsM m => m a emptyErrorM = compilerErrorM "" +silenceErrorsM :: CollectErrorsM m => m a -> m a+silenceErrorsM x = do+  x' <- collectAnyM [x]+  case x' of+       [y] -> return y+       _   -> emptyErrorM+ errorFromIO :: (MonadIO m, ErrorContextM m) => IO a -> m a errorFromIO x = do   x' <- liftIO $ fmap Right x `catchIOError` (return . Left . show)   case x' of        (Right x2) -> return x2        (Left e)   -> compilerErrorM e++-- For fixed x, if f y x succeeds for some y then x is removed.+mergeObjectsM :: CollectErrorsM m => (a -> a -> m b) -> [a] -> m [a]+mergeObjectsM f = merge [] where+  merge cs [] = return cs+  merge cs (x:xs) = do+    ys <- collectFirstM $ map check (cs ++ xs) ++ [return [x]]+    merge (cs ++ ys) xs where+      check x2 = x2 `f` x >> return []  instance ErrorContextM m => ErrorContextM (StateT a m) where   compilerErrorM      = lift . compilerErrorM
src/Cli/Compiler.hs view
@@ -58,6 +58,7 @@   ModuleSpec {     msRoot :: FilePath,     msPath :: FilePath,+    msExtra :: [FilePath],     msExprMap :: ExprMap SourceContext,     msPublicDeps :: [FilePath],     msPrivateDeps :: [FilePath],@@ -83,7 +84,7 @@   deriving (Show)  compileModule :: (PathIOHandler r, CompilerBackend b) => r -> b -> ModuleSpec -> TrackedErrorsIO ()-compileModule resolver backend (ModuleSpec p d em is is2 ps xs ts es ep m f) = do+compileModule resolver backend (ModuleSpec p d ee em is is2 ps xs ts es ep m f) = do   as  <- fmap fixPaths $ mapCompilerM (resolveModule resolver (p </> d)) is   as2 <- fmap fixPaths $ mapCompilerM (resolveModule resolver (p </> d)) is2   let ca0 = Map.empty@@ -102,6 +103,7 @@                  return $ bpDeps ++ deps1   time <- errorFromIO getZonedTime   path <- errorFromIO $ canonicalizePath $ p </> d+  extra <- errorFromIO $ sequence $ map (canonicalizePath . (p</>)) ee   -- NOTE: Making the public namespace deterministic allows freshness checks to   -- skip checking all inputs/outputs for each dependency.   let ns0 = StaticNamespace $ publicNamespace  $ show compilerHash ++ path@@ -117,9 +119,9 @@   fs <- compileLanguageModule cm xa   mf <- maybeCreateMain cm xa m   eraseCachedData (p </> d)-  let ps2 = map takeFileName ps-  let xs2 = map takeFileName xs-  let ts2 = map takeFileName ts+  ps2 <- mapCompilerM (errorFromIO. canonicalizePath . (p </>)) ps+  xs2 <- mapCompilerM (errorFromIO. canonicalizePath . (p </>)) xs+  ts2 <- mapCompilerM (errorFromIO. canonicalizePath . (p </>)) ts   let paths = map (\ns -> getCachedPath (p </> d) ns "") $ nub $ filter (not . null) $ map show $ map coNamespace fs   paths' <- mapM (errorFromIO . canonicalizePath) paths   s0 <- errorFromIO $ canonicalizePath $ getCachedPath (p </> d) (show ns0) ""@@ -144,6 +146,7 @@   let cm2 = CompileMetadata {       cmVersionHash = compilerHash,       cmPath = path,+      cmExtra = extra,       cmPublicNamespace = ns0,       cmPrivateNamespace = ns1,       cmPublicDeps = as,@@ -166,6 +169,7 @@   let cm2' = CompileMetadata {       cmVersionHash = cmVersionHash cm2,       cmPath = cmPath cm2,+      cmExtra = cmExtra cm2,       cmPublicNamespace = cmPublicNamespace cm2,       cmPrivateNamespace = cmPrivateNamespace cm2,       cmPublicDeps = cmPublicDeps cm2,@@ -184,10 +188,12 @@       cmLinkFlags = cmLinkFlags cm2,       cmObjectFiles = cmObjectFiles cm2     }-  writeMetadata (p </> d) cm2' where+  writeMetadata (p </> d) cm2'+  let traces = Set.unions $ map coPossibleTraces $ hxx ++ other+  writePossibleTraces (p </> d) traces where     compilerHash = getCompilerHash backend     ep' = fixPaths $ map (p </>) ep-    writeOutputFile paths ca@(CxxOutput _ f2 ns _ _ content) = do+    writeOutputFile paths ca@(CxxOutput _ f2 ns _ _ _ content) = do       errorFromIO $ hPutStrLn stderr $ "Writing file " ++ f2       writeCachedFile (p </> d) (show ns) f2 $ concat $ map (++ "\n") content       if isSuffixOf ".cpp" f2 || isSuffixOf ".cc" f2@@ -216,6 +222,7 @@                                Nothing  -> NoNamespace,             coUsesNamespace = Set.fromList [ns0,ns1],             coUsesCategory = c `Set.delete` allDeps,+            coPossibleTraces = Set.empty,             coOutput = []           }     compileExtraSource (ns0,ns1) _ paths (OtherSource f2) = do@@ -232,11 +239,11 @@           let f2' = p </> f2           createCachePath (p </> d)           let ms = [(publicNamespaceMacro,Just $ show ns0),(privateNamespaceMacro,Just $ show ns1)]-          let command = CompileToObject f2' (getCachedPath (p </> d) "" "") ms paths e+          let command = CompileToObject f2' (getCachedPath (p </> d) (show ns0) "") ms paths e           fmap Just $ runCxxCommand backend command       | isSuffixOf ".a" f2 || isSuffixOf ".o" f2 = return (Just f2)       | otherwise = return Nothing-    createBinary paths deps (CompileBinary n _ lm o lf) [CxxOutput _ _ _ ns2 req content] = do+    createBinary paths deps (CompileBinary n _ lm o lf) [CxxOutput _ _ _ ns2 req _ content] = do       f0 <- if null o                 then errorFromIO $ canonicalizePath $ p </> d </> show n                 else errorFromIO $ canonicalizePath $ p </> d </> o@@ -277,26 +284,26 @@       fmap (:[]) $ compileModuleMain cm2 xs2 n f2     maybeCreateMain _ _ _ = return [] -createModuleTemplates :: PathIOHandler r => r -> FilePath -> FilePath ->+createModuleTemplates :: PathIOHandler r => r -> FilePath -> FilePath -> [FilePath] ->   [CompileMetadata] -> [CompileMetadata] -> TrackedErrorsIO ()-createModuleTemplates resolver p d deps1 deps2 = do-  (ps,xs,_) <- findSourceFiles p d+createModuleTemplates resolver p d ds deps1 deps2 = do+  (ps,xs,_) <- findSourceFiles p (d:ds)   (LanguageModule _ _ _ cs0 ps0 ts0 cs1 ps1 ts1 _ _) <-     fmap (createLanguageModule [] Map.empty) $ loadModuleGlobals resolver p (PublicNamespace,PrivateNamespace) ps Nothing deps1 deps2   xs' <- zipWithContents resolver p xs-  ds <- mapCompilerM parseInternalSource xs'-  let ds2 = concat $ map (\(_,_,d2) -> d2) ds+  ds2 <- mapCompilerM parseInternalSource xs'+  let ds3 = concat $ map (\(_,_,d2) -> d2) ds2   tm <- foldM includeNewTypes defaultCategories [cs0,cs1,ps0,ps1,ts0,ts1]   let cs = filter isValueConcrete $ cs1++ps1++ts1   let ca = Set.fromList $ map getCategoryName $ filter isValueConcrete cs-  let ca' = foldr Set.delete ca $ map dcName ds2+  let ca' = foldr Set.delete ca $ map dcName ds3   let testingCats = Set.fromList $ map getCategoryName ts1   ts <- fmap concat $ mapCompilerM (\n -> generate (n `Set.member` testingCats) tm n) $ Set.toList ca'   mapCompilerM_ writeTemplate ts where     generate testing tm n = do       (_,t) <- getConcreteCategory tm ([],n)       generateStreamlinedTemplate testing tm t-    writeTemplate (CxxOutput _ n _ _ _ content) = do+    writeTemplate (CxxOutput _ n _ _ _ _ content) = do       let n' = p </> d </> n       exists <- errorFromIO $ doesFileExist n'       if exists@@ -316,7 +323,9 @@   cm <- fmap (createLanguageModule [] em) $ loadModuleGlobals resolver path (NoNamespace,NoNamespace) [] (Just m) deps1 []   mapCompilerM (runSingleTest backend cl cm paths (m:deps2)) ts' where     allowTests = Set.fromList tp-    isTestAllowed t = if null allowTests then True else t `Set.member` allowTests+    isTestAllowed t+      | null allowTests = True+      | otherwise = any (`isSuffixOf` t) allowTests     showSkipped f = compilerWarningM $ "Skipping tests in " ++ f ++ " due to explicit test filter."  loadPrivateSource :: PathIOHandler r => r -> VersionHash -> FilePath -> FilePath -> TrackedErrorsIO (PrivateSource SourceContext)
src/Cli/ParseCompileOptions.hs view
@@ -45,7 +45,9 @@     "",     "zeolite [options...] --templates [modules...]",     "zeolite --get-path",+    "zeolite --missed-lines [filename] [modules...]",     "zeolite --show-deps [modules...]",+    "zeolite --show-traces [modules...]",     "zeolite --version",     "",     "Compilation Modes:",@@ -61,7 +63,9 @@     "Special Modes:",     "  --templates: Only create C++ templates for undefined categories in .0rp sources.",     "  --get-path: Show the data path and immediately exit.",+    "  --missed-lines: List all lines missed in a log file taken from --log-traces.",     "  --show-deps: Show category dependencies for the modules.",+    "  --show-traces: Show the possible code traces for the modules.",     "  --version: Show the compiler version and immediately exit.",     "",     "Options:",@@ -93,6 +97,10 @@     "",     "  # Execute the tests from .0rt files in mylibrary.",     "  zeolite -t mylibrary",+    "",+    "  # Display the contexts of all code lines missed by tests for mylibrary.",+    "  zeolite -t --log-traces traces.csv mylibrary",+    "  zeolite --missed-lines traces.csv mylibrary",     ""   ] 
src/Cli/RunCompiler.hs view
@@ -17,11 +17,13 @@ -- Author: Kevin P. Barry [ta0kira@gmail.com]  module Cli.RunCompiler (+  TraceEntry(..),+  parseTracesFile,   runCompiler, ) where  import Control.Monad (foldM,when)-import Data.List (intercalate,nub)+import Data.List (intercalate,isSuffixOf,nub) import System.Directory import System.FilePath import System.IO@@ -37,6 +39,8 @@ import Module.CompileMetadata import Module.Paths import Module.ProcessMetadata+import Parser.Common+import Parser.TextParser   runCompiler :: (PathIOHandler r, CompilerBackend b) => r -> b -> CompileOptions -> TrackedErrorsIO ()@@ -69,8 +73,9 @@       em <- getExprMap (p </> d) rm       return (ca5,ms ++ [LoadedTests p d m em (deps1) deps2])     checkTestFilters ts = do-      let possibleTests = Set.fromList $ concat $ map (cmTestFiles . ltMetadata) ts-      case Set.toList $ (Set.fromList tp) `Set.difference` possibleTests of+      let possibleTests = concat $ map (cmTestFiles . ltMetadata) ts+      let remaining = filter (not . flip any (map (flip isSuffixOf) possibleTests). flip ($)) tp+      case remaining of           [] -> return ()           fs -> compilerErrorM $ "Some test files do not occur in the selected modules: " ++                                 intercalate ", " (map show fs) ++ "\n"@@ -88,6 +93,7 @@   let rm = ModuleConfig {     mcRoot = "",     mcPath = ".",+    mcExtra = [],     mcExprMap = [],     mcPublicDeps = [],     mcPrivateDeps = [],@@ -99,6 +105,7 @@   let spec = ModuleSpec {     msRoot = absolute,     msPath = dir,+    msExtra = [],     msExprMap = em,     msPublicDeps = is,     msPrivateDeps = is2,@@ -123,7 +130,7 @@   compilerHash = getCompilerHash backend   compileSingle d = do     d' <- errorFromIO $ canonicalizePath (p </> d)-    (is',is2') <- maybeUseConfig d'+    (ep,is',is2') <- maybeUseConfig d'     as  <- fmap fixPaths $ mapCompilerM (resolveModule resolver d') is'     as2 <- fmap fixPaths $ mapCompilerM (resolveModule resolver d') is2'     isBase <- isBaseModule resolver d'@@ -134,15 +141,15 @@                   loadPublicDeps compilerHash f Map.empty (base:as)     deps2 <- loadPublicDeps compilerHash f (mapMetadata deps1) as2     path <- errorFromIO $ canonicalizePath p-    createModuleTemplates resolver path d deps1 deps2 <?? "In module \"" ++ d' ++ "\""+    createModuleTemplates resolver path d ep deps1 deps2 <?? "In module \"" ++ d' ++ "\""   maybeUseConfig d2 = do     let rm = loadRecompile d2     isError <- isCompilerErrorM rm     if isError-       then return (is,is2)+       then return ([],is,is2)        else do-         (ModuleConfig _ _ _ is3 is4 _ _ _) <- rm-         return (nub $ is ++ is3,nub $ is2 ++ is4)+         (ModuleConfig p2 _ ep _ is3 is4 _ _ _) <- rm+         return (map (p2 </>) ep,nub $ is ++ is3,nub $ is2 ++ is4)  runCompiler resolver _ (CompileOptions _ is is2 ds es ep p m f) = mapM_ compileSingle ds where   compileSingle d = do@@ -156,6 +163,7 @@     let rm = ModuleConfig {       mcRoot = absolute,       mcPath = d,+      mcExtra = [],       mcExprMap = [],       mcPublicDeps = as,       mcPrivateDeps = as2,@@ -172,6 +180,39 @@        then return i        else resolveModule resolver p2 i +data TraceEntry =+  TraceEntry {+    teMicroseconds :: Integer,+    teProcess :: Integer,+    teFunction :: String,+    teContext :: String+  }++parseTracesFile :: (FilePath,String) -> TrackedErrorsIO [TraceEntry]+parseTracesFile (f,s) = runTextParser (between nullParse endOfDoc tracesFile) f s where+  tracesFile =  do+    parseHeader+    many parseSingle+  parseHeader = do+    _ <- quotedString+    string_ ","+    _ <- quotedString+    string_ ","+    _ <- quotedString+    string_ ","+    _ <- quotedString+    many (char '\n' <|> char '\n') >> return ()+  parseSingle = do+    ms <- parseDec+    string_ ","+    pid <- parseDec+    string_ ","+    func <- quotedString+    string_ ","+    c <- quotedString+    many (char '\n' <|> char '\n') >> return ()+    return $ TraceEntry ms pid func c+ runRecompileCommon :: (PathIOHandler r, CompilerBackend b) => r -> b ->   ForceMode -> Bool -> FilePath -> [FilePath] -> TrackedErrorsIO () runRecompileCommon resolver backend f rec p ds = do@@ -197,13 +238,14 @@       if f < ForceAll && upToDate          then compilerWarningM $ "Path " ++ d ++ " is up to date"          else do-           rm@(ModuleConfig p2 d2 _ is is2 es ep m) <- loadRecompile d+           rm@(ModuleConfig p2 d2 ee _ is is2 es ep m) <- loadRecompile d            let fixed = fixPath (d </> p2)-           (ps,xs,ts) <- findSourceFiles fixed d2+           (ps,xs,ts) <- findSourceFiles fixed (d2:ee)            em <- getExprMap d rm            let spec = ModuleSpec {              msRoot = fixed,              msPath = d2,+             msExtra = ee,              msExprMap = em,              msPublicDeps = is,              msPrivateDeps = is2,
src/Cli/TestRunner.hs view
@@ -205,7 +205,7 @@       let found = any (=~ r) ms       when (found && not expected) $ compilerErrorM $ "Pattern \"" ++ r ++ "\" present in " ++ n       when (not found && expected) $ compilerErrorM $ "Pattern \"" ++ r ++ "\" missing from " ++ n-    createBinary (CxxOutput _ f2 _ _ _ content) timeout xx = do+    createBinary (CxxOutput _ f2 _ _ _ _ content) timeout xx = do       dir <- errorFromIO $ mkdtemp "/tmp/ztest_"       errorFromIO $ hPutStrLn stderr $ "Writing temporary files to " ++ dir       sources <- mapCompilerM (writeSingleFile dir) xx@@ -224,7 +224,7 @@     timeoutMacro (Just t)       | t < 0 || t > 65535 = compilerErrorM $ "Invalid testcase timeout " ++ show t ++ " => use timeout 0 for unlimited time"       | otherwise = return [(testTimeoutMacro,Just (show t))]  -- Custom timeout.-    writeSingleFile d ca@(CxxOutput _ f2 _ _ _ content) = do+    writeSingleFile d ca@(CxxOutput _ f2 _ _ _ _ content) = do       errorFromIO $ writeFile (d </> f2) $ concat $ map (++ "\n") content       if isSuffixOf ".cpp" f2          then return ([d </> f2],ca)
src/Compilation/CompilerState.hs view
@@ -27,14 +27,19 @@   CompilerContext(..),   CompiledData(..),   CompilerState,+  DeferVariable(..),   LoopSetup(..),   JumpType(..),   MemberValue(..),   ReturnVariable(..),   UsedVariable(..),+  addDeferred,   autoSelfType,+  branchDeferred,+  checkDeferred,   concatM,   csAddRequired,+  csAddTrace,   csAddUsed,   csAddVariable,   csAllFilters,@@ -51,7 +56,8 @@   csGetParamScope,   csGetTypeFunction,   csGetVariable,-  csInheritReturns,+  csInheritDeferred,+  csInheritStatic,   csInheritUsed,   csIsNamedReturns,   csIsUnreachable,@@ -63,6 +69,7 @@   csResolver,   csSameType,   csSelfType,+  csSetDeferred,   csSetHidden,   csSetJumpType,   csSetNoTrace,@@ -72,7 +79,10 @@   csUpdateAssigned,   csWrite,   emptyCleanupBlock,+  emptyDeferred,+  followDeferred,   getCleanContext,+  removeDeferred,   runDataCompiler, ) where @@ -80,6 +90,7 @@ import Control.Monad.Trans.State (StateT(..),execStateT,get,put) import Data.Functor import Prelude hiding (foldr)+import qualified Data.Map as Map import qualified Data.Set as Set  #if MIN_VERSION_base(4,11,0)@@ -111,6 +122,7 @@   ccCheckValueInit :: a -> [c] -> TypeInstance -> ExpressionType -> m ()   ccGetVariable :: a -> UsedVariable c -> m (VariableValue c)   ccAddVariable :: a -> UsedVariable c -> VariableValue c -> m a+  ccSetDeferred :: a -> UsedVariable c -> m a   ccSetReadOnly :: a -> UsedVariable c -> m a   ccSetHidden :: a -> UsedVariable c -> m a   ccCheckVariableInit :: a -> [UsedVariable c] -> m ()@@ -120,7 +132,8 @@   ccUpdateAssigned :: a -> VariableName -> m a   ccAddUsed :: a -> UsedVariable c -> m a   ccInheritUsed :: a -> a -> m a-  ccInheritReturns :: a -> [a] -> m a+  ccInheritStatic :: a -> [a] -> m a+  ccInheritDeferred :: a -> DeferVariable c -> m a   ccRegisterReturn :: a -> [c] -> Maybe ExpressionType -> m a   ccPrimNamedReturns :: a -> m [ReturnVariable]   ccIsUnreachable :: a -> m Bool@@ -136,6 +149,8 @@   ccReleaseExprMacro :: a -> [c] -> MacroName -> m a   ccSetNoTrace :: a -> Bool -> m a   ccGetNoTrace :: a -> m Bool+  ccAddTrace :: a -> String -> m a+  ccGetTraces :: a -> m [String]  data MemberValue c =   MemberValue {@@ -169,6 +184,7 @@ data CleanupBlock c s =   CleanupBlock {     csCleanup :: s,+    csDeferred :: DeferVariable c,     csUsesVars :: [UsedVariable c],     csJumpType :: JumpType,     csRequires :: Set.Set CategoryName@@ -176,7 +192,7 @@   deriving (Show)  emptyCleanupBlock :: Monoid s => CleanupBlock c s-emptyCleanupBlock = CleanupBlock mempty [] NextStatement Set.empty+emptyCleanupBlock = CleanupBlock mempty emptyDeferred [] NextStatement Set.empty  data JumpType =   NextStatement |@@ -187,6 +203,30 @@   JumpMax  -- Max value for use as initial state in folds.   deriving (Eq,Ord,Show) +newtype DeferVariable c =+  DeferVariable {+    dvDeferred :: Map.Map VariableName (PassedValue c)+  }+  deriving (Show)++branchDeferred :: [DeferVariable c] -> DeferVariable c+branchDeferred = DeferVariable . Map.unions . map dvDeferred++followDeferred :: DeferVariable c -> DeferVariable c -> DeferVariable c+followDeferred (DeferVariable xa) (DeferVariable ya) = DeferVariable $ Map.intersection xa ya++emptyDeferred :: DeferVariable c+emptyDeferred = DeferVariable Map.empty++checkDeferred :: VariableName -> DeferVariable c -> Bool+checkDeferred n (DeferVariable va) = n `Map.member` va++removeDeferred :: VariableName -> DeferVariable c -> DeferVariable c+removeDeferred n (DeferVariable va) = DeferVariable (n `Map.delete` va)++addDeferred :: VariableName -> PassedValue c -> DeferVariable c -> DeferVariable c+addDeferred n v (DeferVariable va) = DeferVariable (Map.insert n v va)+ csCurrentScope :: CompilerContext c m s a => CompilerState a m SymbolScope csCurrentScope = fmap ccCurrentScope get >>= lift @@ -231,6 +271,10 @@   UsedVariable c -> VariableValue c -> CompilerState a m () csAddVariable v t = fmap (\x -> ccAddVariable x v t) get >>= lift >>= put +csSetDeferred :: CompilerContext c m s a =>+  UsedVariable c -> CompilerState a m ()+csSetDeferred v = fmap (\x -> ccSetDeferred x v) get >>= lift >>= put+ csSetReadOnly :: CompilerContext c m s a =>   UsedVariable c -> CompilerState a m () csSetReadOnly v = fmap (\x -> ccSetReadOnly x v) get >>= lift >>= put@@ -261,9 +305,12 @@ csInheritUsed :: CompilerContext c m s a => a -> CompilerState a m () csInheritUsed c = fmap (\x -> ccInheritUsed x c) get >>= lift >>= put -csInheritReturns :: CompilerContext c m s a => [a] -> CompilerState a m ()-csInheritReturns xs = fmap (\x -> ccInheritReturns x xs) get >>= lift >>= put+csInheritStatic :: CompilerContext c m s a => [a] -> CompilerState a m ()+csInheritStatic xs = fmap (\x -> ccInheritStatic x xs) get >>= lift >>= put +csInheritDeferred :: CompilerContext c m s a => DeferVariable c -> CompilerState a m ()+csInheritDeferred xs = fmap (\x -> ccInheritDeferred x xs) get >>= lift >>= put+ csRegisterReturn :: CompilerContext c m s a =>   [c] -> Maybe ExpressionType -> CompilerState a m () csRegisterReturn c rs = fmap (\x -> ccRegisterReturn x c rs) get >>= lift >>= put@@ -310,18 +357,22 @@ csGetNoTrace :: CompilerContext c m s a => CompilerState a m Bool csGetNoTrace = fmap ccGetNoTrace get >>= lift +csAddTrace :: CompilerContext c m s a => String -> CompilerState a m ()+csAddTrace t = fmap (\x -> ccAddTrace x t) get >>= lift >>= put+ data CompiledData s =   CompiledData {     cdRequired :: Set.Set CategoryName,+    cdTraces :: Set.Set String,     cdOutput :: s   }  instance Semigroup s => Semigroup (CompiledData s) where-  (CompiledData r1 s1) <> (CompiledData r2 s2) =-    CompiledData (r1 `Set.union` r2) (s1 <> s2)+  (CompiledData r1 t1 s1) <> (CompiledData r2 t2 s2) =+    CompiledData (r1 `Set.union` r2) (t1 `Set.union` t2) (s1 <> s2)  instance (Semigroup s, Monoid s) => Monoid (CompiledData s) where-  mempty = CompiledData Set.empty mempty+  mempty = CompiledData Set.empty Set.empty mempty   mappend = (<>)  runDataCompiler :: CompilerContext c m s a =>@@ -330,8 +381,10 @@   ctx' <- execStateT x ctx   required <- ccGetRequired ctx'   output <- ccGetOutput ctx'-  return $ CompiledData {+  traces <- ccGetTraces ctx'+  return CompiledData {       cdRequired = required,+      cdTraces = Set.fromList traces,       cdOutput = output     } 
src/Compilation/ProcedureContext.hs view
@@ -30,9 +30,10 @@ ) where  import Control.Monad (foldM,when)+import Data.List (nub)+import Data.Maybe (fromJust,isJust) import Lens.Micro hiding (mapped) import Lens.Micro.TH-import Data.Maybe (fromJust,isJust) import qualified Data.Map as Map import qualified Data.Set as Set @@ -42,6 +43,7 @@ import Base.Positional import Compilation.CompilerState import Types.DefinedCategory+import Types.Function import Types.Procedure import Types.TypeCategory import Types.TypeInstance@@ -56,7 +58,7 @@   ValidateNames {     vnNames :: Positional VariableName,     vnTypes :: Positional (PassedValue c),-    vnRemaining :: Map.Map VariableName (PassedValue c)+    vnRemaining :: DeferVariable c   }   deriving (Show) @@ -73,6 +75,7 @@     _pcFunctions :: Map.Map FunctionName (ScopedFunction c),     _pcVariables :: Map.Map VariableName (VariableValue c),     _pcReturns :: ReturnValidation c,+    _pcDeferred :: DeferVariable c,     _pcJumpType :: JumpType,     _pcIsNamed :: Bool,     _pcPrimNamed :: [ReturnVariable],@@ -85,7 +88,8 @@     _pcUsedVars :: [UsedVariable c],     _pcExprMap :: ExprMap c,     _pcReservedMacros :: [(MacroName,[c])],-    _pcNoTrace :: Bool+    _pcNoTrace :: Bool,+    _pcTraces :: [String]   }  $(makeLenses ''ProcedureContext)@@ -141,25 +145,42 @@                  case t of                       Just t0 -> replaceSelfInstance self t0                       Nothing -> return self-    getFunction t' t' where+    getFunction t' t' >>= onlyFunc t' where+      onlyFunc _ [f] = return f+      onlyFunc t2 fs = collectFirstM $ map (tryMergeFrom fs) fs ++ [multipleMatchError t2 fs]+      multipleMatchError t2 fs = do+        "Multiple matches for function " ++ show n ++ " called on " ++ show t2 ++ formatFullContextBrace c !!>+          mapErrorsM (map show fs)+      tryMergeFrom fs f = do+        mapCompilerM_ (tryMergeFunc f) fs+        return f+      tryMergeFunc f1 f2 = do+        f1' <- parsedToFunctionType f1+        f2' <- parsedToFunctionType f2+        r <- ccResolver ctx+        allFilters <- ccAllFilters ctx+        silenceErrorsM $ checkFunctionConvert r allFilters Map.empty f2' f1'       getFunction t0 t2 = reduceMergeTree getFromAny getFromAll (getFromSingle t0) t2       getFromAny _ =         compilerErrorM $ "Use explicit type conversion to call " ++ show n ++ " from " ++ show t-      getFromAll ts = do+      getFromAll fs = do         t' <- case t of                    Just t2 -> return t2                    Nothing -> fmap (singleType . JustTypeInstance) $ ccSelfType ctx-        collectFirstM ts <!!+        collectFirstM_ fs <!!           "Function " ++ show n ++ " not available for type " ++ show t' ++ formatFullContextBrace c+        fmap concat $ collectAnyM fs       getFromSingle t0 (JustParamName _ p) = do         fa <- ccAllFilters ctx-        fs <- case p `Map.lookup` fa of+        ff <- case p `Map.lookup` fa of                    (Just fs) -> return fs                    _ -> compilerErrorM $ "Param " ++ show p ++ " not found"-        let ts = map tfType $ filter isRequiresFilter fs-        let ds = map dfType $ filter isDefinesFilter  fs-        collectFirstM (map (getFunction t0) ts ++ map (checkDefine t0) ds) <!!+        let ts = map tfType $ filter isRequiresFilter ff+        let ds = map dfType $ filter isDefinesFilter  ff+        let fs = map (getFunction t0) ts ++ map (checkDefine t0) ds+        collectFirstM_ fs <!!           "Function " ++ show n ++ " not available for param " ++ show p ++ formatFullContextBrace c+        fmap concat $ collectAnyM fs       getFromSingle t0 (JustTypeInstance t2)         -- Same category as the procedure itself.         | tiName t2 == ctx ^. pcType =@@ -186,7 +207,7 @@         paired <- processPairs alwaysPair ps1 ps2 <??           "In external function call at " ++ formatFullContext c         let assigned = Map.fromList paired-        uncheckedSubFunction assigned f >>= replaceSelfFunction (fixTypeParams t0)+        uncheckedSubFunction assigned f >>= replaceSelfFunction (fixTypeParams t0) >>= return . (:[])       subAndCheckFunction _ t2 _ _ _ =         compilerErrorM $ "Category " ++ show t2 ++                          " does not have a type or value function named " ++ show n ++@@ -232,42 +253,63 @@   ccSetReadOnly ctx v@(UsedVariable c n) = do     (VariableValue c2 s t _) <- ccGetVariable ctx v     return $ ctx & pcVariables %~ Map.insert n (VariableValue c2 s t (VariableReadOnly c))+  ccSetDeferred ctx v@(UsedVariable c n) = do+    (VariableValue c2 s t r) <- ccGetVariable ctx v+    when (s /= LocalScope) $+      compilerErrorM $ "Variable " ++ show n ++ formatFullContextBrace c2 +++        " cannot be marked as deferred because it is not locally scoped" ++ formatFullContextBrace c+    case r of+         VariableReadOnly c3 -> compilerErrorM $ "Variable " ++ show n ++ formatFullContextBrace c2 +++                                  " cannot be marked as deferred " ++ formatFullContextBrace c +++                                  " because it is read-only" ++ formatFullContextBrace c3+         VariableHidden c3 -> compilerErrorM $ "Variable " ++ show n ++ formatFullContextBrace c2 +++                                  " cannot be marked as deferred " ++ formatFullContextBrace c +++                                  " because it is hidden" ++ formatFullContextBrace c3+         _ -> return ()+    return $ ctx & pcDeferred %~ addDeferred n (PassedValue c t)   ccSetHidden ctx v@(UsedVariable c n) = do     (VariableValue c2 s t _) <- ccGetVariable ctx v     return $ ctx & pcVariables %~ Map.insert n (VariableValue c2 s t (VariableHidden c))   ccCheckVariableInit ctx vs-    -- Returns are checked during cleanup inlining.+    -- Deferred variables are checked during cleanup inlining.     | ctx ^. pcInCleanup = return ()-    | otherwise =+    | otherwise = do         case ctx ^. pcReturns of-             ValidateNames _ _ na -> mapCompilerM_ (checkSingle na) vs-             _ -> return ()+              ValidateNames _ _ na -> mapCompilerM_ (checkSingle na) vs+              _ -> return ()+        mapCompilerM_ (checkSingle $ ctx ^. pcDeferred) vs         where           checkSingle na (UsedVariable c n) =-             when (n `Map.member` na) $-               compilerErrorM $ "Named return " ++ show n ++-                                " might not be initialized" ++ formatFullContextBrace c+            when (n `checkDeferred` na) $+              compilerErrorM $ "Deferred variable " ++ show n +++                               " might not be initialized" ++ formatFullContextBrace c   ccWrite ctx ss = return $ ctx & pcOutput <>~ ss   ccGetOutput = return . (^. pcOutput)   ccClearOutput ctx = return $ ctx & pcOutput .~ mempty-  ccUpdateAssigned ctx n = return $ ctx & pcReturns %~ update where-    update (ValidateNames ns ts ra) = ValidateNames ns ts $ Map.delete n ra-    update rs                       = rs+  ccUpdateAssigned ctx n = return $ ctx & pcReturns %~ updateReturns & pcDeferred %~ updateDeferred where+    updateReturns (ValidateNames ns ts ra) = ValidateNames ns ts $ n `removeDeferred` ra+    updateReturns rs                       = rs+    updateDeferred = (n `removeDeferred`)   ccAddUsed ctx v     | ctx ^. pcInCleanup = return $ ctx & pcUsedVars <>~ [v]     | otherwise          = return ctx-  ccInheritReturns ctx cs = return $ ctx & pcReturns .~ returns & pcJumpType .~ jump & pcUsedVars .~ used where+  ccInheritStatic ctx [] = return ctx+  ccInheritStatic ctx cs = return $ ctx & pcReturns .~ returns & pcJumpType .~ jump & pcUsedVars .~ used & pcDeferred .~ deferred & pcTraces .~ traces where+    traces = nub $ ctx ^. pcTraces ++ (concat $ map (^. pcTraces) cs)     used = ctx ^. pcUsedVars ++ (concat $ map (^. pcUsedVars) cs)-    (returns,jump) = combineSeries (ctx ^. pcReturns,ctx ^. pcJumpType) inherited+    deferred = (ctx ^. pcDeferred) `followDeferred` deferred2+    (returns,jump) = combineSeries (ctx ^. pcReturns,ctx ^. pcJumpType) (returns2,jump2)     combineSeries (r@(ValidatePositions _),j1) (_,j2) = (r,max j1 j2)     combineSeries (_,j1) (r@(ValidatePositions _),j2) = (r,max j1 j2)-    combineSeries (ValidateNames ns ts ra1,j1) (ValidateNames _ _ ra2,j2) = (ValidateNames ns ts $ Map.intersection ra1 ra2,max j1 j2)-    inherited = foldr combineParallel (ValidateNames (Positional []) (Positional []) Map.empty,JumpMax) $ zip (map (^. pcReturns) cs) (map (^. pcJumpType) cs)+    combineSeries (ValidateNames ns ts ra1,j1) (ValidateNames _ _ ra2,j2) = (ValidateNames ns ts $ ra1 `followDeferred` ra2,max j1 j2)+    (deferred2,returns2,jump2) = foldr combineParallel (emptyDeferred,ValidateNames (Positional []) (Positional []) emptyDeferred,JumpMax) $+      zip3 (map (^. pcDeferred) cs) (map (^. pcReturns) cs) (map (^. pcJumpType) cs)     -- Ignore a branch if it jumps to a higher scope.-    combineParallel (_,j1) (r,j2) | j1 > NextStatement = (r,min j1 j2)-    combineParallel (ValidateNames ns ts ra1,j1) (ValidateNames _ _ ra2,j2) = (ValidateNames ns ts $ Map.union ra1 ra2,min j1 j2)-    combineParallel (r@(ValidatePositions _),j1) (_,j2) = (r,min j1 j2)-    combineParallel (_,j1) (r@(ValidatePositions _),j2) = (r,min j1 j2)+    combineParallel (_,_,j1) (da2,r,j2) | j1 > NextStatement = (da2,r,min j1 j2)+    combineParallel (da1,ValidateNames ns ts ra1,j1) (da2,ValidateNames _ _ ra2,j2) = (branchDeferred [da1,da2],ValidateNames ns ts $ branchDeferred [ra1,ra2],min j1 j2)+    combineParallel (da1,r@(ValidatePositions _),j1) (da2,_,j2) = (branchDeferred [da1,da2],r,min j1 j2)+    combineParallel (da1,_,j1) (da2,r@(ValidatePositions _),j2) = (branchDeferred [da1,da2],r,min j1 j2)+  ccInheritDeferred ctx ds = return $ ctx & pcDeferred .~ ds   ccInheritUsed ctx ctx2 = return $ ctx & pcUsedVars <>~ (ctx2 ^. pcUsedVars)   ccRegisterReturn ctx c vs = do     returns <- check (ctx ^. pcReturns)@@ -293,8 +335,8 @@       check (ValidateNames ns ts ra) = do         case vs of              Just _  -> check (ValidatePositions ts) >> return ()-             Nothing -> mapCompilerM_ alwaysError $ Map.toList ra-        return (ValidateNames ns ts Map.empty)+             Nothing -> mapCompilerM_ alwaysError $ Map.toList $ dvDeferred ra+        return (ValidateNames ns ts emptyDeferred)       alwaysError (n,_) = compilerErrorM $ "Named return " ++ show n ++                                            " might not be initialized" ++                                            formatFullContextBrace c@@ -322,8 +364,9 @@       case n `Map.lookup` vs of            Just (VariableValue c2 s@LocalScope t _) -> Map.insert n (VariableValue c2 s t (VariableReadOnly c)) vs            _ -> vs-  ccPushCleanup ctx ctx2 = return $ ctx & pcCleanupBlocks %~ (Just cleanup:) where-      cleanup = CleanupBlock (ctx2 ^. pcOutput) (ctx2 ^. pcUsedVars) (ctx2 ^. pcJumpType) (ctx2 ^. pcRequiredTypes)+  ccPushCleanup ctx ctx2 = return $ ctx & pcCleanupBlocks %~ (Just cleanup:) & pcTraces .~ traces where+    traces = nub $ ctx ^. pcTraces ++ ctx2 ^. pcTraces+    cleanup = CleanupBlock (ctx2 ^. pcOutput) (ctx2 ^. pcDeferred) (ctx2 ^. pcUsedVars) (ctx2 ^. pcJumpType) (ctx2 ^. pcRequiredTypes)   ccGetCleanup ctx j = return combined where     combined       | j == NextStatement =@@ -334,6 +377,7 @@       | j == JumpBreak || j == JumpContinue = combine $ map fromJust $ takeWhile isJust $ ctx ^. pcCleanupBlocks       | otherwise = emptyCleanupBlock     combine cs = CleanupBlock (concat $ map csCleanup cs)+                              (foldr followDeferred (ctx ^. pcDeferred) $ map csDeferred cs)                               (concat $ map csUsesVars cs)                               (foldr max NextStatement (map csJumpType cs))                               (Set.unions $ map csRequires cs)@@ -354,6 +398,9 @@   ccReleaseExprMacro ctx _ n = return $ ctx & pcReservedMacros %~ (filter ((/= n) . fst))   ccSetNoTrace ctx t = return $ ctx & pcNoTrace .~ t   ccGetNoTrace = return . (^. pcNoTrace)+  ccAddTrace ctx "" = return ctx+  ccAddTrace ctx t = return $ ctx & pcTraces <>~ [t]+  ccGetTraces = return . (^. pcTraces)  updateReturnVariables :: (Show c, CollectErrorsM m) =>   (Map.Map VariableName (VariableValue c)) ->
src/Compilation/ScopeContext.hs view
@@ -26,6 +26,7 @@   getProcedureScopes, ) where +import Control.Monad (when) import qualified Data.Map as Map import qualified Data.Set as Set @@ -61,13 +62,14 @@   (ScopeContext c -> ScopedFunction c -> ExecutableProcedure c -> a) -> ProcedureScope c -> [a] applyProcedureScope f (ProcedureScope ctx fs) = map (uncurry (f ctx)) fs -getProcedureScopes :: (Show c, CollectErrorsM m) =>+getProcedureScopes :: (Ord c, Show c, CollectErrorsM m) =>   CategoryMap c -> ExprMap c -> DefinedCategory c -> m [ProcedureScope c] getProcedureScopes ta em (DefinedCategory c n pragmas _ _ ms ps fs) = message ??> do   (_,t) <- getConcreteCategory ta (c,n)   let params = Positional $ getCategoryParams t   let typeInstance = TypeInstance n $ fmap (singleType . JustParamName False . vpParam) params-  let filters = getCategoryFilters t+  let rawFilters = getCategoryFilters t+  filters <- getCategoryFilterMap t   let r = CategoryResolver ta   fa <- setInternalFunctions r t fs   pa <- pairProceduresToFunctions fa ps@@ -80,16 +82,21 @@   let cm0 = builtins typeInstance CategoryScope   let tm0 = builtins typeInstance TypeScope   let vm0 = builtins typeInstance ValueScope-  cm2 <- mapMembers readOnly hidden cm-  tm2 <- mapMembers readOnly hidden $ cm ++ tm'-  vm2 <- mapMembers readOnly hidden $ cm ++ tm' ++ vm'+  let immutable = immutableContext t+  when (not $ null immutable) $ mapCompilerM_ (checkImmutableMember r filters immutable) vm'+  let readOnly2 = if null immutable+                     then readOnly+                     else Map.fromListWith (++) $ Map.toList readOnly ++ zip valueMembers (repeat immutable)+  cm2 <- mapMembers readOnly2 hidden cm+  tm2 <- mapMembers readOnly2 hidden $ cm ++ tm'+  vm2 <- mapMembers readOnly2 hidden $ cm ++ tm' ++ vm'   mapCompilerM_ checkPragma pragmas   let cv = Map.union cm0 cm2   let tv = Map.union tm0 tm2   let vv = Map.union vm0 vm2-  let ctxC = ScopeContext ta n params vm' filters fa cv em-  let ctxT = ScopeContext ta n params vm' filters fa tv em-  let ctxV = ScopeContext ta n params vm' filters fa vv em+  let ctxC = ScopeContext ta n params vm' rawFilters fa cv em+  let ctxT = ScopeContext ta n params vm' rawFilters fa tv em+  let ctxV = ScopeContext ta n params vm' rawFilters fa vv em   return [ProcedureScope ctxC cp,ProcedureScope ctxT tp',ProcedureScope ctxV vp']   where     message = "In compilation of definition for " ++ show n ++ formatFullContextBrace c@@ -104,12 +111,18 @@                                             " does not exist (marked Hidden at " ++                                             formatFullContext c2 ++ ")") missing     allMembers = Set.fromList $ map dmName ms+    valueMembers = map dmName $ filter ((== ValueScope) . dmScope) ms+    immutableContext t = head $ (map ciContext $ filter isCategoryImmutable (getCategoryPragmas t)) ++ [[]]     readOnly = Map.fromListWith (++) $ concat $ map (\m -> zip (mroMembers m) (repeat $ mroContext m)) $ filter isMembersReadOnly pragmas     hidden   = Map.fromListWith (++) $ concat $ map (\m -> zip (mhMembers m)  (repeat $ mhContext m))  $ filter isMembersHidden   pragmas     firstM f (x,y) = do       x' <- f x       return (x',y)     builtins t s0 = Map.filter ((<= s0) . vvScope) $ builtinVariables t+    checkImmutableMember r fs2 c2 m = checkValueTypeImmutable r fs2 (dmType m) <!!+      "@value member " ++ show (dmName m) +++      " at " ++ formatFullContext (dmContext m) +++      " must have an immutable type" ++ formatFullContextBrace c2  -- TODO: This is probably in the wrong module. builtinVariables :: TypeInstance -> Map.Map VariableName (VariableValue c)
src/CompilerCxx/CategoryContext.hs view
@@ -71,6 +71,7 @@       _pcFunctions = fa,       _pcVariables = Map.union builtin members,       _pcReturns = ValidatePositions (Positional []),+      _pcDeferred = emptyDeferred,       _pcJumpType = NextStatement,       _pcIsNamed = False,       _pcPrimNamed = [],@@ -83,7 +84,8 @@       _pcUsedVars = [],       _pcExprMap = em,       _pcReservedMacros = [],-      _pcNoTrace = False+      _pcNoTrace = False,+      _pcTraces = []     }  getProcedureContext :: (Show c, CollectErrorsM m) =>@@ -93,7 +95,7 @@                     (ExecutableProcedure _ _ _ _ as2 rs2 _) = do   rs' <- if isUnnamedReturns rs2             then return $ ValidatePositions rs1-            else fmap (ValidateNames (fmap ovName $ nrNames rs2) rs1 . Map.fromList) $ processPairs pairOutput rs1 (nrNames rs2)+            else fmap (ValidateNames (fmap ovName $ nrNames rs2) rs1 . foldr (uncurry addDeferred) emptyDeferred) $ processPairs pairOutput rs1 (nrNames rs2)   va' <- updateArgVariables va as1 as2   va'' <- updateReturnVariables va' rs1 rs2   let pa' = if s == CategoryScope@@ -129,6 +131,7 @@       _pcFunctions = fa,       _pcVariables = va'',       _pcReturns = rs',+      _pcDeferred = emptyDeferred,       _pcJumpType = NextStatement,       _pcIsNamed = not (isUnnamedReturns rs2),       _pcPrimNamed = ns,@@ -141,7 +144,8 @@       _pcUsedVars = [],       _pcExprMap = em,       _pcReservedMacros = [],-      _pcNoTrace = False+      _pcNoTrace = False,+      _pcTraces = []     }   where     pairOutput (PassedValue c1 t2) (OutputValue c2 n2) = return $ (n2,PassedValue (c2++c1) t2)@@ -159,6 +163,7 @@     _pcFunctions = Map.empty,     _pcVariables = Map.empty,     _pcReturns = ValidatePositions (Positional []),+    _pcDeferred = emptyDeferred,     _pcJumpType = NextStatement,     _pcIsNamed = False,     _pcPrimNamed = [],@@ -171,5 +176,6 @@     _pcUsedVars = [],     _pcExprMap = em,     _pcReservedMacros = [],-    _pcNoTrace = False+    _pcNoTrace = False,+    _pcTraces = []   }
src/CompilerCxx/Code.hs view
@@ -35,6 +35,7 @@   noTestsOnlySourceGuard,   onlyCode,   onlyCodes,+  onlyDeps,   paramType,   predTraceContext,   readStoredVariable,@@ -82,13 +83,16 @@ onlyCode = onlyCodes . (:[])  onlyCodes :: [String] -> CompiledData [String]-onlyCodes = CompiledData Set.empty+onlyCodes = CompiledData Set.empty Set.empty +onlyDeps :: Set.Set CategoryName -> CompiledData [String]+onlyDeps d = CompiledData d Set.empty []+ indentCompiled :: CompiledData [String] -> CompiledData [String]-indentCompiled (CompiledData r o) = CompiledData r $ map ("  " ++) o+indentCompiled (CompiledData r t o) = CompiledData r t $ map ("  " ++) o  clearCompiled :: CompiledData [String] -> CompiledData [String]-clearCompiled (CompiledData r _) = CompiledData r []+clearCompiled (CompiledData r t _) = CompiledData r t []  startFunctionTracing :: CategoryName -> ScopedFunction c -> String startFunctionTracing t f = "TRACE_FUNCTION(" ++ show (functionDebugName t f) ++ ")"@@ -259,7 +263,7 @@  variableStoredType :: ValueType -> String variableStoredType t-  | t == boolRequiredValue   = "bool"+  | t == boolRequiredValue   = "PrimBool"   | t == intRequiredValue    = "PrimInt"   | t == floatRequiredValue  = "PrimFloat"   | t == charRequiredValue   = "PrimChar"@@ -271,7 +275,7 @@  variableProxyType :: ValueType -> String variableProxyType t-  | t == boolRequiredValue   = "bool"+  | t == boolRequiredValue   = "PrimBool"   | t == intRequiredValue    = "PrimInt"   | t == floatRequiredValue  = "PrimFloat"   | t == charRequiredValue   = "PrimChar"@@ -318,7 +322,7 @@   returnCount = show $ length $ pValues $ sfReturns f   category    = show $ show $ sfType f   function    = show $ show $ sfName f-  collection  = collectionName $ sfType f+  collection  = categoryIdName $ sfType f   functionNum = show i   getType CategoryScope = "CategoryFunction"   getType TypeScope     = "TypeFunction"@@ -335,7 +339,7 @@ valueBase = "TypeValue"  paramType :: String-paramType = "const S<" ++ typeBase ++ ">"+paramType = "const S<const " ++ typeBase ++ ">"  unescapedChars :: Set.Set Char unescapedChars = Set.fromList $ ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ [' ','.']
src/CompilerCxx/CxxFiles.hs view
@@ -32,6 +32,7 @@ ) where  import Data.List (intercalate,sortBy)+import Data.Hashable (hash) import Prelude hiding (pi) import qualified Data.Map as Map import qualified Data.Set as Set@@ -67,6 +68,7 @@     coNamespace :: Namespace,     coUsesNamespace :: Set.Set Namespace,     coUsesCategory :: Set.Set CategoryName,+    coPossibleTraces :: Set.Set String,     coOutput :: [String]   }   deriving (Show)@@ -118,9 +120,10 @@                      (getCategoryNamespace t)                      (getCategoryNamespace t `Set.insert` ns)                      (cdRequired file)+                     (cdTraces file)                      (cdOutput file) where     file = mconcat $ [-        CompiledData depends [],+        onlyDeps depends,         onlyCodes guardTop,         onlyCodes $ (if testing then testsOnlyCategoryGuard (getCategoryName t) else []),         onlyCodes baseHeaderIncludes,@@ -128,7 +131,7 @@         onlyCodes guardBottom       ]     depends = getCategoryDeps t-    content = mconcat [collection,labels,getCategory2,getType]+    content = mconcat [categoryId,labels,getCategory2,getType]     name = getCategoryName t     guardTop = ["#ifndef " ++ guardName,"#define " ++ guardName]     guardBottom = ["#endif  // " ++ guardName]@@ -143,9 +146,10 @@     valueFunctions = filter ((== ValueScope) . sfScope) functions     labels = onlyCodes $ map label $ categoryFunctions ++ typeFunctions ++ valueFunctions     label f = "extern " ++ functionLabelType f ++ " " ++ functionName f ++ ";"-    collection-      | isValueConcrete t = emptyCode-      | otherwise         = onlyCodes ["extern const CollectionType " ++ collectionName name ++ ";"]+    categoryId = onlyCodes [+        "static constexpr CategoryId " ++ categoryIdName name ++ " = " ++ show actualId ++ ";"+      ]+    actualId = abs $ hash $ show (getCategoryContext t) ++ show (getCategoryNamespace t) ++ show (getCategoryName t)     getCategory2       | isInstanceInterface t = emptyCode       | otherwise             = declareGetCategory t@@ -181,7 +185,7 @@     singleSource = do       let filename = sourceFilename (getCategoryName t)       let (cf,tf,vf) = partitionByScope sfScope $ getCategoryFunctions t-      (CompiledData req out) <- fmap (addNamespace t) $ concatM [+      (CompiledData req traces out) <- fmap (addNamespace t) $ concatM [           defineFunctions t cf tf vf,           declareInternalGetters t,           defineInterfaceCategory t,@@ -197,6 +201,7 @@                          (getCategoryNamespace t)                          (Set.fromList [getCategoryNamespace t])                          req'+                         traces                          (allowTestsOnly $ addSourceIncludes $ addCategoryHeader t $ addIncludes req' out)   common (StreamlinedExtension t ns) = sequence [streamlinedHeader,streamlinedSource] where     streamlinedHeader = do@@ -204,9 +209,10 @@       let maybeValue = if hasPrimitiveValue (getCategoryName t)                           then []                           else [defineAbstractValue t]-      (CompiledData req out) <- fmap (addNamespace t) $ concatM $ [+      (CompiledData req traces out) <- fmap (addNamespace t) $ concatM $ [           defineAbstractCategory t,-          defineAbstractType     t+          return $ declareInternalType t (length $ getCategoryParams t),+          defineAbstractType t         ] ++ maybeValue ++ [           declareAbstractGetters t         ]@@ -215,6 +221,7 @@                          (getCategoryNamespace t)                          (getCategoryNamespace t `Set.insert` ns)                          req+                         traces                          (headerGuard (getCategoryName t) $ allowTestsOnly $ addTemplateIncludes $ addCategoryHeader t $ addIncludes req out)     streamlinedSource = do       let filename = sourceStreamlined (getCategoryName t)@@ -222,7 +229,7 @@       let maybeValue = if hasPrimitiveValue (getCategoryName t)                           then []                           else [defineValueOverrides t (getCategoryFunctions t)]-      (CompiledData req out) <- fmap (addNamespace t) $ concatM $ [+      (CompiledData req traces out) <- fmap (addNamespace t) $ concatM $ [           defineFunctions t cf tf vf,           defineCategoryOverrides t (getCategoryFunctions t),           defineTypeOverrides     t (getCategoryFunctions t)@@ -235,6 +242,7 @@                          (getCategoryNamespace t)                          (getCategoryNamespace t `Set.insert` ns)                          req'+                         traces                          (addSourceIncludes $ addStreamlinedHeader t $ addIncludes req' out)   common (StreamlinedTemplate t tm) = fmap (:[]) streamlinedTemplate where     streamlinedTemplate = do@@ -248,7 +256,7 @@       let maybeValue = if hasPrimitiveValue (getCategoryName t)                           then []                           else [defineCustomValue t vp]-      (CompiledData req out) <- fmap (addNamespace t) $ concatM $+      (CompiledData req traces out) <- fmap (addNamespace t) $ concatM $         maybeGetter ++ [           defineCustomCategory t cp,           defineCustomType     t tp@@ -261,6 +269,7 @@                          (getCategoryNamespace t)                          (Set.fromList [getCategoryNamespace t])                          req'+                         traces                          (addTemplateIncludes $ addStreamlinedHeader t $ addIncludes req' out)     filename = templateStreamlined (getCategoryName t)     defined = DefinedCategory {@@ -301,7 +310,7 @@       let cf = map fst $ psProcedures cp       let tf = map fst $ psProcedures tp       let vf = map fst $ psProcedures vp-      (CompiledData req out) <- fmap (addNamespace t) $ concatM [+      (CompiledData req traces out) <- fmap (addNamespace t) $ concatM [           defineFunctions t cf tf vf,           declareInternalGetters t,           defineConcreteCategory r cf ta' em t d,@@ -322,18 +331,12 @@                          (getCategoryNamespace t)                          (getCategoryNamespace t `Set.insert` ns)                          req'+                         traces                          (allowTestsOnly $ addSourceIncludes $ addCategoryHeader t $ addIncludes req' out) -  defineFunctions t cf tf vf = concatM [createCollection,createAllLabels] where+  defineFunctions t cf tf vf = createAllLabels where     name = getCategoryName t-    createCollection = return $ onlyCodes [-        "namespace {",-        "const int " ++ collectionValName ++ " = 0;",-        "}  // namespace",-        "const CollectionType " ++ collectionName name ++ " = COLLECTION_ID(&" ++ collectionValName ++ ");"-      ]     createAllLabels = return $ onlyCodes $ concat $ map createLabels [cf,tf,vf]-    collectionValName = "collection_" ++ show name     createLabels = map (uncurry createLabelForFunction) . zip [0..] . sortBy compareName . filter ((== name) . sfType)     compareName x y = sfName x `compare` sfName y @@ -390,8 +393,8 @@   defineInterfaceType t = concatM [       return $ onlyCode $ "struct " ++ typeName (getCategoryName t) ++ " : public " ++ typeBase ++ " {",       fmap indentCompiled $ inlineTypeConstructor t,+      fmap indentCompiled $ inlineTypeDestructor False t,       return declareTypeOverrides,-      return $ declareTypeArgGetters t,       return $ indentCompiled $ createParams $ getCategoryParams t,       return $ onlyCode $ "  " ++ categoryName (getCategoryName t) ++ "& parent;",       return $ onlyCode "};"@@ -401,7 +404,7 @@       return $ onlyCode $ "struct " ++ categoryName (getCategoryName t) ++ " : public " ++ categoryBase ++ " {",       fmap indentCompiled $ inlineCategoryConstructor t d tm em,       return declareCategoryOverrides,-      fmap indentCompiled $ concatM $ map (procedureDeclaration False) fs,+      fmap indentCompiled $ concatM $ map (declareProcedure t False) fs,       fmap indentCompiled $ concatM $ map (createMemberLazy r) members,       return $ onlyCode "};"     ] where@@ -409,9 +412,9 @@   defineConcreteType fs t = concatM [       return $ onlyCode $ "struct " ++ className ++ " : public " ++ typeBase ++ ", std::enable_shared_from_this<" ++ className ++ "> {",       fmap indentCompiled $ inlineTypeConstructor t,+      fmap indentCompiled $ inlineTypeDestructor False t,       return declareTypeOverrides,-      return $ declareTypeArgGetters t,-      fmap indentCompiled $ concatM $ map (procedureDeclaration False) fs,+      fmap indentCompiled $ concatM $ map (declareProcedure t False) fs,       return $ indentCompiled $ createParams $ getCategoryParams t,       return $ onlyCode $ "  " ++ categoryName (getCategoryName t) ++ "& parent;",       return $ onlyCode "};"@@ -422,9 +425,9 @@       return $ onlyCode $ "struct " ++ valueName (getCategoryName t) ++ " : public " ++ valueBase ++ " {",       fmap indentCompiled $ inlineValueConstructor t d,       return declareValueOverrides,-      fmap indentCompiled $ concatM $ map (procedureDeclaration False) fs,+      fmap indentCompiled $ concatM $ map (declareProcedure t False) fs,       fmap indentCompiled $ concatM $ map (createMember r params t) members,-      return $ onlyCode $ "  const S<" ++ typeName (getCategoryName t) ++ "> parent;",+      return $ onlyCode $ "  const S<const " ++ typeName (getCategoryName t) ++ "> parent;",       return $ onlyCodes traceCreation,       return $ onlyCode "};"     ] where@@ -437,17 +440,16 @@   defineAbstractCategory t = concatM [       return $ onlyCode $ "struct " ++ categoryName (getCategoryName t) ++ " : public " ++ categoryBase ++ " {",       return declareCategoryOverrides,-      fmap indentCompiled $ concatM $ map (procedureDeclaration True) $ filter ((== CategoryScope). sfScope) $ getCategoryFunctions t,+      fmap indentCompiled $ concatM $ map (declareProcedure t True) $ filter ((== CategoryScope). sfScope) $ getCategoryFunctions t,       return $ onlyCode $ "  virtual inline ~" ++ categoryName (getCategoryName t) ++ "() {}",       return $ onlyCode "};"     ]   defineAbstractType t = concatM [       return $ onlyCode $ "struct " ++ className ++ " : public " ++ typeBase ++ ", std::enable_shared_from_this<" ++ className ++ "> {",       fmap indentCompiled $ inlineTypeConstructor t,+      fmap indentCompiled $ inlineTypeDestructor True t,       return declareTypeOverrides,-      return $ declareTypeArgGetters t,-      fmap indentCompiled $ concatM $ map (procedureDeclaration True) $ filter ((== TypeScope). sfScope) $ getCategoryFunctions t,-      return $ onlyCode $ "  virtual inline ~" ++ typeName (getCategoryName t) ++ "() {}",+      fmap indentCompiled $ concatM $ map (declareProcedure t True) $ filter ((== TypeScope). sfScope) $ getCategoryFunctions t,       return $ indentCompiled $ createParams $ getCategoryParams t,       return $ onlyCode $ "  " ++ categoryName (getCategoryName t) ++ "& parent;",       return $ onlyCode "};"@@ -457,39 +459,39 @@       return $ onlyCode $ "struct " ++ valueName (getCategoryName t) ++ " : public " ++ valueBase ++ " {",       fmap indentCompiled $ abstractValueConstructor t,       return declareValueOverrides,-      fmap indentCompiled $ concatM $ map (procedureDeclaration True) $ filter ((== ValueScope). sfScope) $ getCategoryFunctions t,+      fmap indentCompiled $ concatM $ map (declareProcedure t True) $ filter ((== ValueScope). sfScope) $ getCategoryFunctions t,       return $ onlyCode $ "  virtual inline ~" ++ valueName (getCategoryName t) ++ "() {}",-      return $ onlyCode $ "  const S<" ++ typeName (getCategoryName t) ++ "> parent;",+      return $ onlyCode $ "  const S<const " ++ typeName (getCategoryName t) ++ "> parent;",       return $ onlyCode "};"     ]    defineCustomCategory :: (Ord c, Show c, CollectErrorsM m) => AnyCategory c -> ProcedureScope c -> m (CompiledData [String])   defineCustomCategory t ps = concatM [       return $ onlyCode $ "struct " ++ categoryCustom (getCategoryName t) ++ " : public " ++ categoryName (getCategoryName t) ++ " {",-      fmap indentCompiled $ concatM $ applyProcedureScope (compileExecutableProcedure FinalInlineFunction) ps,+      fmap indentCompiled $ concatM $ applyProcedureScope (defineProcedure t FinalInlineFunction) ps,       return $ onlyCode "};"     ]   defineCustomType :: (Ord c, Show c, CollectErrorsM m) => AnyCategory c -> ProcedureScope c -> m (CompiledData [String])   defineCustomType t ps = concatM [       return $ onlyCode $ "struct " ++ typeCustom (getCategoryName t) ++ " : public " ++ typeName (getCategoryName t) ++ " {",       fmap indentCompiled $ customTypeConstructor t,-      fmap indentCompiled $ concatM $ applyProcedureScope (compileExecutableProcedure FinalInlineFunction) ps,+      fmap indentCompiled $ concatM $ applyProcedureScope (defineProcedure t FinalInlineFunction) ps,       return $ onlyCode "};"     ]   defineCustomValue :: (Ord c, Show c, CollectErrorsM m) => AnyCategory c -> ProcedureScope c -> m (CompiledData [String])   defineCustomValue t ps = concatM [       return $ onlyCode $ "struct " ++ valueCustom (getCategoryName t) ++ " : public " ++ valueName (getCategoryName t) ++ " {",       fmap indentCompiled $ customValueConstructor t,-      fmap indentCompiled $ concatM $ applyProcedureScope (compileExecutableProcedure FinalInlineFunction) ps,+      fmap indentCompiled $ concatM $ applyProcedureScope (defineProcedure t FinalInlineFunction) ps,       return $ onlyCode "};"     ]    defineCategoryFunctions :: (Ord c, Show c, CollectErrorsM m) => AnyCategory c -> ProcedureScope c -> m (CompiledData [String])-  defineCategoryFunctions t = concatM . applyProcedureScope (compileExecutableProcedure $ OutOfLineFunction $ categoryName $ getCategoryName t)+  defineCategoryFunctions t = concatM . applyProcedureScope (defineProcedure t $ OutOfLineFunction $ categoryName $ getCategoryName t)   defineTypeFunctions :: (Ord c, Show c, CollectErrorsM m) => AnyCategory c -> ProcedureScope c -> m (CompiledData [String])-  defineTypeFunctions t = concatM . applyProcedureScope (compileExecutableProcedure $ OutOfLineFunction $ typeName $ getCategoryName t)+  defineTypeFunctions t = concatM . applyProcedureScope (defineProcedure t $ OutOfLineFunction $ typeName $ getCategoryName t)   defineValueFunctions :: (Ord c, Show c, CollectErrorsM m) => AnyCategory c -> ProcedureScope c -> m (CompiledData [String])-  defineValueFunctions t = concatM . applyProcedureScope (compileExecutableProcedure $ OutOfLineFunction $ valueName $ getCategoryName t)+  defineValueFunctions t = concatM . applyProcedureScope (defineProcedure t $ OutOfLineFunction $ valueName $ getCategoryName t)    declareCategoryOverrides = onlyCodes [       "  std::string CategoryName() const final;",@@ -498,8 +500,8 @@   declareTypeOverrides = onlyCodes [       "  std::string CategoryName() const final;",       "  void BuildTypeName(std::ostream& output) const final;",-      "  bool TypeArgsForParent(const TypeCategory& category, std::vector<S<const TypeInstance>>& args) const final;",-      "  ReturnTuple Dispatch(const S<TypeInstance>& self, const TypeFunction& label, const ParamTuple& params, const ValueTuple& args) final;",+      "  bool TypeArgsForParent(const CategoryId& category, std::vector<S<const TypeInstance>>& args) const final;",+      "  ReturnTuple Dispatch(const TypeFunction& label, const ParamTuple& params, const ValueTuple& args) const final;",       "  bool CanConvertFrom(const S<const TypeInstance>& from) const final;"     ]   declareValueOverrides = onlyCodes [@@ -510,7 +512,7 @@   defineCategoryOverrides t fs = return $ mconcat [       onlyCode $ "std::string " ++ className ++ "::CategoryName() const { return \"" ++ show (getCategoryName t) ++ "\"; }",       onlyCode $ "ReturnTuple " ++ className ++ "::Dispatch(const CategoryFunction& label, const ParamTuple& params, const ValueTuple& args) {",-      createFunctionDispatch (getCategoryName t) CategoryScope fs,+      createFunctionDispatch t CategoryScope fs,       onlyCode "}"     ] where       className = categoryName (getCategoryName t)@@ -519,12 +521,11 @@       onlyCode $ "void " ++ className ++ "::BuildTypeName(std::ostream& output) const {",       defineTypeName params,       onlyCode "}",-      onlyCode $ "bool " ++ className ++ "::TypeArgsForParent(const TypeCategory& category, std::vector<S<const TypeInstance>>& args) const {",+      onlyCode $ "bool " ++ className ++ "::TypeArgsForParent(const CategoryId& category, std::vector<S<const TypeInstance>>& args) const {",       createTypeArgsForParent t,       onlyCode $ "}",-      defineTypeArgGetters t,-      onlyCode $ "ReturnTuple " ++ className ++ "::Dispatch(const S<TypeInstance>& self, const TypeFunction& label, const ParamTuple& params, const ValueTuple& args) {",-      createFunctionDispatch (getCategoryName t) TypeScope fs,+      onlyCode $ "ReturnTuple " ++ className ++ "::Dispatch(const TypeFunction& label, const ParamTuple& params, const ValueTuple& args) const {",+      createFunctionDispatch t TypeScope fs,       onlyCode $ "}",       onlyCode $ "bool " ++ className ++ "::CanConvertFrom(const S<const TypeInstance>& from) const {",       createCanConvertFrom t,@@ -535,19 +536,17 @@   defineValueOverrides t fs = return $ mconcat [       onlyCode $ "std::string " ++ className ++ "::CategoryName() const { return parent->CategoryName(); }",       onlyCode $ "ReturnTuple " ++ className ++ "::Dispatch(const ValueFunction& label, const ParamTuple& params, const ValueTuple& args) {",-      createFunctionDispatch (getCategoryName t) ValueScope fs,+      createFunctionDispatch t ValueScope fs,       onlyCode $ "}"     ] where       className = valueName (getCategoryName t) -  createMember r params t m = do+  createMember r params t m = "In creation of " ++ show (dmName m) ++ " at " ++ formatFullContext (dmContext m) ??> do     m' <- replaceSelfMember (instanceFromCategory t) m-    validateGeneralInstance r params (vtType $ dmType m') <??-      "In creation of " ++ show (dmName m') ++ " at " ++ formatFullContext (dmContext m')+    validateGeneralInstance r params (vtType $ dmType m')     return $ onlyCode $ variableStoredType (dmType m') ++ " " ++ variableName (dmName m') ++ ";"-  createMemberLazy r m = do-    validateGeneralInstance r Set.empty (vtType $ dmType m) <??-      "In creation of " ++ show (dmName m) ++ " at " ++ formatFullContext (dmContext m)+  createMemberLazy r m = "In creation of " ++ show (dmName m) ++ " at " ++ formatFullContext (dmContext m) ??> do+    validateGeneralInstance r Set.empty (vtType $ dmType m)     return $ onlyCode $ variableLazyType (dmType m) ++ " " ++ variableName (dmName m) ++ ";"    createParams = mconcat . map createParam where@@ -580,8 +579,16 @@         indentCompiled $ onlyCode $ startInitTracing (getCategoryName t) TypeScope,         onlyCode "}"       ]+  inlineTypeDestructor abstract t = do+    let ps2 = map (paramName . vpParam) $ getCategoryParams t+    let params = "Params<" ++ show (length ps2) ++ ">::Type(" ++ intercalate "," ps2 ++ ")"+    let prefix = if abstract then "virtual " else "inline "+    return $ onlyCodes [+        prefix ++ "~" ++ typeName (getCategoryName t) ++ "() { " ++ typeRemover (getCategoryName t) ++ "(" ++ params ++ "); }"+      ]+   inlineValueConstructor t d = do-    let argParent = "S<" ++ typeName (getCategoryName t) ++ "> p"+    let argParent = "S<const " ++ typeName (getCategoryName t) ++ "> p"     let argsPassed = "const ValueTuple& args"     let allArgs = intercalate ", " [argParent,argsPassed]     let initParent = "parent(p)"@@ -592,7 +599,7 @@       members = filter ((== ValueScope). dmScope) $ dcMembers d    abstractValueConstructor t = do-    let argParent = "S<" ++ typeName (getCategoryName t) ++ "> p"+    let argParent = "S<const " ++ typeName (getCategoryName t) ++ "> p"     let allArgs = intercalate ", " [argParent]     let initParent = "parent(p)"     let allInit = initParent@@ -606,10 +613,10 @@     let allInit = typeName (getCategoryName t) ++ "(p, params)"     return $ onlyCode $ "inline " ++ typeCustom (getCategoryName t) ++ "(" ++ allArgs ++ ") : " ++ allInit ++ " {}"   customValueConstructor t = do-    let argParent = "S<" ++ typeName (getCategoryName t) ++ "> p"+    let argParent = "S<const " ++ typeName (getCategoryName t) ++ "> p"     let argsPassed = "const ValueTuple& args"     let allArgs = intercalate ", " [argParent,argsPassed]-    let allInit = valueName (getCategoryName t) ++ "(p)"+    let allInit = valueName (getCategoryName t) ++ "(std::move(p))"     return $ onlyCode $ "inline " ++ valueCustom (getCategoryName t) ++ "(" ++ allArgs ++ ") : " ++ allInit ++ " {}"    allowTestsOnly@@ -635,6 +642,12 @@       "CycleCheck<" ++ n2 ++ "> marker(*this);"     ] +  defineProcedure t = compileExecutableProcedure (isImmutable t)+  declareProcedure t = procedureDeclaration (isImmutable t)++isImmutable :: AnyCategory c -> Bool+isImmutable = any isCategoryImmutable . getCategoryPragmas+ formatFunctionTypes :: Show c => ScopedFunction c -> [String] formatFunctionTypes (ScopedFunction c _ _ s as rs ps fa _) = [location,args,returns,params] ++ filters where   location = show s ++ " function declared at " ++ formatFullContext c@@ -645,7 +658,7 @@   singleFilter (ParamFilter _ n2 f) = "  " ++ show n2 ++ " " ++ show f  createMainCommon :: String -> CompiledData [String] -> CompiledData [String] -> [String]-createMainCommon n (CompiledData req0 out0) (CompiledData req1 out1) =+createMainCommon n (CompiledData req0 _ out0) (CompiledData req1 _ out1) =   baseSourceIncludes ++ mainSourceIncludes ++ depIncludes (req0 `Set.union` req1) ++ out0 ++ [       "int main(int argc, const char** argv) {",       "  SetSignalHandler();",@@ -671,11 +684,11 @@ generateTestFile tm em args ts = "In the creation of the test binary procedure" ??> do   ts' <- fmap mconcat $ mapCompilerM (compileTestProcedure tm em) ts   (include,sel) <- selectTestFromArgv1 $ map tpName ts-  let (CompiledData req _) = ts' <> sel+  let (CompiledData req traces _) = ts' <> sel   let contentTop = mconcat [timeoutInclude,onlyCodes include,ts']   let contentMain = mconcat [setTimeout,argv,callLog,sel]   let file = testsOnlySourceGuard ++ createMainCommon "testcase" contentTop contentMain-  return $ CompiledData req file where+  return $ CompiledData req traces file where     args' = map escapeChars args     argv = onlyCodes [         "const char* argv2[] = { \"testcase\" " ++ concat (map (", " ++) args') ++ " };",@@ -727,56 +740,61 @@ createLabelForFunction i f = functionLabelType f ++ " " ++ functionName f ++                               " = " ++ newFunctionLabel i f ++ ";" -createFunctionDispatch :: CategoryName -> SymbolScope -> [ScopedFunction c] -> CompiledData [String]-createFunctionDispatch n s fs = function where+createFunctionDispatch :: AnyCategory c -> SymbolScope -> [ScopedFunction c] -> CompiledData [String]+createFunctionDispatch t s fs = function where+  name = getCategoryName t   function     | null filtered = onlyCode fallback-    | otherwise = onlyCodes $ [typedef] ++ concat (map table $ byCategory) ++ metaTable ++ select+    | otherwise = onlyCodes $ [typedef] ++ concat (map table $ byCategory) ++ select   filtered = filter ((== s) . sfScope) fs   flatten f = f:(concat $ map flatten $ sfMerges f)   flattened = concat $ map flatten filtered   byCategory = Map.toList $ Map.fromListWith (++) $ map (\f -> (sfType f,[f])) flattened   typedef-    | s == CategoryScope = "  using CallType = ReturnTuple(" ++ categoryName n ++-                           "::*)(const ParamTuple&, const ValueTuple&);"-    | s == TypeScope     = "  using CallType = ReturnTuple(" ++ typeName n ++-                           "::*)(const S<TypeInstance>&, const ParamTuple&, const ValueTuple&);"-    | s == ValueScope    = "  using CallType = ReturnTuple(" ++ valueName n +++    | s == CategoryScope = "  using CallType = ReturnTuple(" ++ categoryName name ++                            "::*)(const ParamTuple&, const ValueTuple&);"+    | s == TypeScope     = "  using CallType = ReturnTuple(" ++ typeName name +++                           "::*)(const ParamTuple&, const ValueTuple&) const;"+    | s == ValueScope    = "  using CallType = ReturnTuple(" ++ valueName name +++                           "::*)(const ParamTuple&, const ValueTuple&)" ++ suffix ++ ";"     | otherwise = undefined-  name f-    | s == CategoryScope = categoryName n ++ "::" ++ callName f-    | s == TypeScope     = typeName n     ++ "::" ++ callName f-    | s == ValueScope    = valueName n    ++ "::" ++ callName f+  suffix+    | isImmutable t = " const"+    | otherwise     = ""+  funcName f+    | s == CategoryScope = categoryName name ++ "::" ++ callName f+    | s == TypeScope     = typeName name     ++ "::" ++ callName f+    | s == ValueScope    = valueName name    ++ "::" ++ callName f     | otherwise = undefined+  table (_,[_]) = []   table (n2,fs2) =     ["  static const CallType " ++ tableName n2 ++ "[] = {"] ++-    map (\f -> "    &" ++ name f ++ ",") (Set.toList $ Set.fromList $ map sfName fs2) +++    map (\f -> "    &" ++ funcName f ++ ",") (Set.toList $ Set.fromList $ map sfName fs2) ++     ["  };"]-  metaTable = ["  static DispatchTable<CallType> all_tables[] = {"] ++-              map dispatchKeyValue byCategory ++-              ["  };"]-  dispatchKeyValue (n2,_) = "    DispatchTable<CallType>(" ++ collectionName n2 ++ ", " ++ tableName n2 ++ "),"   select = [-      "  static const StaticSort force_sort = all_tables;",-      "  const DispatchTable<CallType>* const table = DispatchSelect(label.collection, all_tables);",-      "  if (table) {",-      "    if (label.function_num < 0 || label.function_num >= table->size) {",-      "      FAIL() << \"Bad function call \" << label;",-      "    } else {",-      "      return (this->*table->table[label.function_num])(" ++ args ++ ");",-      "    }",-      "  }",-      fallback+      "  switch (label.collection) {"+    ] ++ categoryCases ++ [+      "    default:",+      "    " ++ fallback,+      "  }"     ]+  categoryCases = concat $ map singleCase byCategory+  singleCase (n2,[f]) = [+      "    case " ++ categoryIdName n2 ++ ":",+      "      return " ++ callName (sfName f) ++ "(" ++ args ++ ");"+    ]+  singleCase (n2,_) = [+      "    case " ++ categoryIdName n2 ++ ":",+      "      return (this->*" ++ tableName n2 ++ "[label.function_num])(" ++ args ++ ");"+    ]   args     | s == CategoryScope = "params, args"-    | s == TypeScope     = "self, params, args"+    | s == TypeScope     = "params, args"     | s == ValueScope    = "params, args"     | otherwise = undefined   fallback     | s == CategoryScope = "  return TypeCategory::Dispatch(label, params, args);"-    | s == TypeScope     = "  return TypeInstance::Dispatch(self, label, params, args);"+    | s == TypeScope     = "  return TypeInstance::Dispatch(label, params, args);"     | s == ValueScope    = "  return TypeValue::Dispatch(label, params, args);"     | otherwise = undefined @@ -785,7 +803,7 @@   | isInstanceInterface t = onlyCode $ "  return " ++ typeBase ++ "::CanConvertFrom(from);"   | otherwise = onlyCodes $ [       "  std::vector<S<const TypeInstance>> args;",-      "  if (!from->TypeArgsForParent(parent, args)) return false;",+      "  if (!from->TypeArgsForParent(" ++ categoryIdName (getCategoryName t) ++ ", args)) return false;",       "  if(args.size() != " ++ show (length params) ++ ") {",       "    FAIL() << \"Wrong number of args (\" << args.size() << \")  for \" << CategoryName();",       "  }"@@ -798,42 +816,23 @@       checkCov i p = "  if (!TypeInstance::CanConvert(args[" ++ show i ++ "], " ++ paramName p ++ ")) return false;"       checkCon i p = "  if (!TypeInstance::CanConvert(" ++ paramName p ++ ", args[" ++ show i ++ "])) return false;" -declareTypeArgGetters :: AnyCategory c -> CompiledData [String]-declareTypeArgGetters t = onlyCodes $ map paramGetter (getCategoryName t:refines) where-  refines = map (tiName  . vrType) $ getCategoryRefines t-  paramGetter r = "  void Params_" ++ show r ++ "(std::vector<S<const TypeInstance>>& args) const;"--defineTypeArgGetters :: AnyCategory c -> CompiledData [String]-defineTypeArgGetters t = onlyCodes $ concat $ map paramGetter (myType:refines) where-  params = map (\p -> (vpParam p,vpVariance p)) $ getCategoryParams t-  myType = (getCategoryName t,map (singleType . JustParamName False . fst) params)-  refines = map (\r -> (tiName r,pValues $ tiParams r)) $ map vrType $ getCategoryRefines t-  paramGetter (r,ps) = [-      "void " ++ typeName (getCategoryName t) ++ "::Params_" ++ show r ++ "(std::vector<S<const TypeInstance>>& args) const {",-      "  args = std::vector<S<const TypeInstance>>{" ++ intercalate ", " (map expandLocalType ps) ++ "};",-      "}"-    ]- createTypeArgsForParent :: AnyCategory c -> CompiledData [String]-createTypeArgsForParent t-  | isInstanceInterface t = onlyCode $ "  return false;"-  | otherwise = onlyCodes $ [-      "  using CallType = void(" ++ className ++ "::*)(std::vector<S<const TypeInstance>>&)const;",-      "  static DispatchSingle<CallType> all_calls[] = {"-    ] ++ map dispatchKeyValue ((getCategoryName t):refines) ++ [-      "  };",-      "  static const StaticSort force_sort = all_calls;",-      "  const DispatchSingle<CallType>* const call = DispatchSelect(&category, all_calls);",-      "  if (call) {",-      "    (this->*call->value)(args);",-      "    return true;",-      "  }",-      "  return false;"-    ] where-      className = typeName $ getCategoryName t-      refines = map (tiName . vrType) $ getCategoryRefines t-      dispatchKeyValue n = "    DispatchSingle<CallType>(&" ++ categoryGetter n ++-                           "(), &" ++ className ++ "::Params_" ++ show n ++ "),"+createTypeArgsForParent t = onlyCodes $ [+    "  switch (category) {"+  ] ++ categoryCases ++ [+    "    default:",+    "      return false;",+    "  }"+  ] where+    categoryCases = concat $ map singleCase (myType:refines)+    params = map (\p -> (vpParam p,vpVariance p)) $ getCategoryParams t+    myType = (getCategoryName t,map (singleType . JustParamName False . fst) params)+    refines = map (\r -> (tiName r,pValues $ tiParams r)) $ map vrType $ getCategoryRefines t+    singleCase (n2,ps) = [+        "    case " ++ categoryIdName n2 ++ ":",+        "      args = std::vector<S<const TypeInstance>>{" ++ intercalate ", " (map expandLocalType ps) ++ "};",+        "      return true;"+      ]  -- Similar to Procedure.expandGeneralInstance but doesn't account for scope. expandLocalType :: GeneralInstance -> String@@ -869,13 +868,13 @@  declareGetType :: AnyCategory c -> CompiledData [String] declareGetType t = onlyCodes [-    "S<" ++ typeBase ++ "> " ++ typeGetter (getCategoryName t) ++ "(Params<" +++    "S<const " ++ typeBase ++ "> " ++ typeGetter (getCategoryName t) ++ "(Params<" ++             show (length $getCategoryParams t) ++ ">::Type params);"   ]  defineGetType :: AnyCategory c -> CompiledData [String] defineGetType t = onlyCodes [-    "S<" ++ typeBase ++ "> " ++ typeGetter (getCategoryName t) ++ "(Params<" +++    "S<const " ++ typeBase ++ "> " ++ typeGetter (getCategoryName t) ++ "(Params<" ++             show (length $ getCategoryParams t) ++ ">::Type params) {",     "  return " ++ typeCreator (getCategoryName t) ++ "(params);",     "}"@@ -899,8 +898,11 @@  declareInternalType :: AnyCategory c -> Int -> CompiledData [String] declareInternalType t n = onlyCodes [-    "S<" ++ typeName (getCategoryName t) ++ "> " ++ typeCreator (getCategoryName t) ++-            "(Params<" ++ show n ++ ">::Type params);"+    "struct " ++ typeName (getCategoryName t) ++ ";",+    "S<const " ++ typeName (getCategoryName t) ++ "> " ++ typeCreator (getCategoryName t) +++      "(const Params<" ++ show n ++ ">::Type& params);",+    "void " ++ typeRemover (getCategoryName t) +++      "(const Params<" ++ show n ++ ">::Type& params);"   ]  defineInternalType :: AnyCategory c -> Int -> CompiledData [String]@@ -910,26 +912,31 @@ defineInternalType2 className t n   | n < 1 =       onlyCodes [-        "S<" ++ typeName (getCategoryName t) ++ "> " ++ typeCreator (getCategoryName t) ++ "(Params<" ++ show n ++ ">::Type params) {",+        "S<const " ++ typeName (getCategoryName t) ++ "> " ++ typeCreator (getCategoryName t) ++ "(const Params<" ++ show n ++ ">::Type& params) {",         "  static const auto cached = S_get(new " ++ className ++ "(" ++ categoryCreator (getCategoryName t) ++ "(), Params<" ++ show n ++ ">::Type()));",         "  return cached;",-        "}"+        "}",+        "void " ++ typeRemover (getCategoryName t) ++ "(const Params<" ++ show n ++ ">::Type& params) {}"       ]   | otherwise =       onlyCodes [-        "S<" ++ typeName (getCategoryName t) ++ "> " ++ typeCreator (getCategoryName t) ++ "(Params<" ++ show n ++ ">::Type params) {",-        "  static auto& cache = *new InstanceCache<" ++ show n ++ ", " ++ typeName (getCategoryName t) ++ ">([](Params<" ++ show n ++ ">::Type params) {",-        "      return S_get(new " ++ className ++ "(" ++ categoryCreator (getCategoryName t) ++ "(), params));",-        "    });",-        "  return cache.GetOrCreate(params);",+        "static auto& " ++ cacheName ++ " = *new InstanceCache<" ++ show n ++ ", " ++ typeName (getCategoryName t) ++ ">([](const Params<" ++ show n ++ ">::Type& params) {",+        "    return S_get(new " ++ className ++ "(" ++ categoryCreator (getCategoryName t) ++ "(), params));",+        "  });",+        "S<const " ++ typeName (getCategoryName t) ++ "> " ++ typeCreator (getCategoryName t) ++ "(const Params<" ++ show n ++ ">::Type& params) {",+        "  return " ++ cacheName ++ ".GetOrCreate(params);",+        "}",+        "void " ++ typeRemover (getCategoryName t) ++ "(const Params<" ++ show n ++ ">::Type& params) {",+        "  " ++ cacheName ++ ".Remove(params);",         "}"-      ]+      ] where+        cacheName = show (getCategoryName t) ++ "_instance_cache"  declareInternalValue :: AnyCategory c -> CompiledData [String] declareInternalValue t =   onlyCodes [       "BoxedValue " ++ valueCreator (getCategoryName t) ++-      "(S<" ++ typeName (getCategoryName t) ++ "> parent, " +++      "(S<const " ++ typeName (getCategoryName t) ++ "> parent, " ++       "const ValueTuple& args);"     ] @@ -939,9 +946,10 @@ defineInternalValue2 :: String -> AnyCategory c -> CompiledData [String] defineInternalValue2 className t =   onlyCodes [-      "BoxedValue " ++ valueCreator (getCategoryName t) ++ "(S<" ++ typeName (getCategoryName t) ++ "> parent, " +++      "BoxedValue " ++ valueCreator (getCategoryName t) +++      "(S<const " ++ typeName (getCategoryName t) ++ "> parent, " ++       "const ValueTuple& args) {",-      "  return BoxedValue::New<" ++ className ++ ">(parent, args);",+      "  return BoxedValue::New<" ++ className ++ ">(std::move(parent), args);",       "}"     ] @@ -959,4 +967,5 @@   fromFilters fs = concat $ map (fromFilter . pfFilter) fs   fromFilter (TypeFilter _ t2)  = Set.toList $ categoriesFromTypes t2   fromFilter (DefinesFilter t2) = fromDefine t2+  fromFilter ImmutableFilter = []   fromGenerals = Set.toList . Set.unions . map categoriesFromTypes
src/CompilerCxx/LanguageModule.hs view
@@ -111,9 +111,8 @@       xxConcrete   <- fmap concat $ mapCompilerM (generateConcrete cs ctx) ds       return $ xxInterfaces ++ xxConcrete     generateConcrete cs (FileContext testing tm ns em2) d = do-      tm' <- mergeInternalInheritance tm d       t <- getCategoryDecl cs tm d-      let ctx = FileContext testing tm' ns em2+      let ctx = FileContext testing tm ns em2       generateNativeConcrete ctx (t,d)     getCategoryDecl cs tm d = do       checkLocal cs (dcContext d) (dcName d)@@ -184,8 +183,8 @@   m (CxxOutput,[(FunctionName,[c])]) compileTestMain (LanguageModule ns0 ns1 ns2 cs0 ps0 ts0 cs1 ps1 ts1 _ em) args ts2 tests = do   tm' <- tm-  (CompiledData req main) <- generateTestFile tm' em args tests-  let output = CxxOutput Nothing testFilename NoNamespace (psNamespace ts2 `Set.insert` Set.unions [ns0,ns1,ns2]) req main+  (CompiledData req traces main) <- generateTestFile tm' em args tests+  let output = CxxOutput Nothing testFilename NoNamespace (psNamespace ts2 `Set.insert` Set.unions [ns0,ns1,ns2]) req traces main   let tests' = map (\t -> (tpName t,tpContext t)) tests   return (output,tests') where   tm = foldM includeNewTypes defaultCategories [cs0,cs1,ps0,ps1,ts0,ts1,psCategory ts2]@@ -199,7 +198,7 @@   let cs = filter (\c -> getCategoryName c == n) $ concat $ map psCategory xa   tm'' <- includeNewTypes tm' cs   (ns,main) <- generateMainFile tm'' em n f-  return $ CxxOutput Nothing mainFilename NoNamespace (ns `Set.insert` Set.unions [ns0,ns1,ns2]) (Set.fromList [n]) main where+  return $ CxxOutput Nothing mainFilename NoNamespace (ns `Set.insert` Set.unions [ns0,ns1,ns2]) (Set.fromList [n]) Set.empty main where     tm = foldM includeNewTypes defaultCategories [cs0,cs1,ps0,ps1]     reconcile [_] = return ()     reconcile []  = compilerErrorM $ "No matches for main category " ++ show n ++ " ($TestsOnly$ sources excluded)"
src/CompilerCxx/Naming.hs view
@@ -28,7 +28,7 @@   categoryCustom,   categoryGetter,   categoryName,-  collectionName,+  categoryIdName,   functionDebugName,   functionName,   headerFilename,@@ -56,6 +56,7 @@   typeCustom,   typeGetter,   typeName,+  typeRemover,   unionGetter,   valueCreator,   valueCustom,@@ -96,7 +97,7 @@ baseHeaderIncludes = ["#include \"category-header.hpp\""]  baseSourceIncludes :: [String]-baseSourceIncludes = ["#include \"category-source.hpp\"","#include \"internal.hpp\""]+baseSourceIncludes = ["#include \"category-source.hpp\""]  templateIncludes :: [String] templateIncludes = ["#include \"category-source.hpp\""]@@ -105,7 +106,8 @@ mainSourceIncludes = ["#include \"logging.hpp\""]  paramName :: ParamName -> String-paramName p = "Param_" ++ tail (show p) -- Remove leading '#'.+paramName ParamSelf = "PARAM_SELF"+paramName p         = "Param_" ++ tail (show p) -- Remove leading '#'.  variableName :: VariableName -> String variableName VariableSelf = "VAR_SELF"@@ -164,8 +166,8 @@ functionName :: ScopedFunction c -> String functionName f = "Function_" ++ show (sfType f) ++ "_" ++ show (sfName f) -collectionName :: CategoryName -> String-collectionName n = "Functions_" ++ show n+categoryIdName :: CategoryName -> String+categoryIdName n = "CategoryId_" ++ show n  testFunctionName :: FunctionName -> String testFunctionName f = "Test_" ++ show f@@ -178,6 +180,9 @@  typeCreator :: CategoryName -> String typeCreator n = "CreateType_" ++ show n++typeRemover :: CategoryName -> String+typeRemover n = "RemoveType_" ++ show n  valueCreator :: CategoryName -> String valueCreator n = "CreateValue_" ++ show n
src/CompilerCxx/Procedure.hs view
@@ -62,21 +62,22 @@ import Types.Variance  -procedureDeclaration :: Monad m => Bool -> ScopedFunction c -> m (CompiledData [String])-procedureDeclaration abstract f = return $ onlyCode func where+procedureDeclaration :: Monad m => Bool -> Bool -> ScopedFunction c -> m (CompiledData [String])+procedureDeclaration immutable abstract f = return $ onlyCode func where   func     | abstract = "virtual " ++ proto ++ " = 0;"     | otherwise = proto ++ ";"   name = callName (sfName f)+  suffix+    | immutable = " const"+    | otherwise = ""   proto     | sfScope f == CategoryScope =       "ReturnTuple " ++ name ++ "(const ParamTuple& params, const ValueTuple& args)"     | sfScope f == TypeScope =-      "ReturnTuple " ++ name ++-      "(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args)"+      "ReturnTuple " ++ name ++ "(const ParamTuple& params, const ValueTuple& args) const"     | sfScope f == ValueScope =-      "ReturnTuple " ++ name ++-      "(const ParamTuple& params, const ValueTuple& args)"+      "ReturnTuple " ++ name ++ "(const ParamTuple& params, const ValueTuple& args)" ++ suffix     | otherwise = undefined  data CxxFunctionType =@@ -86,9 +87,9 @@   deriving Show  compileExecutableProcedure :: (Ord c, Show c, CollectErrorsM m) =>-  CxxFunctionType -> ScopeContext c -> ScopedFunction c ->+  Bool -> CxxFunctionType -> ScopeContext c -> ScopedFunction c ->   ExecutableProcedure c -> m (CompiledData [String])-compileExecutableProcedure cxxType ctx+compileExecutableProcedure immutable cxxType ctx   ff@(ScopedFunction _ _ _ s as1 rs1 ps1 _ _)   pp@(ExecutableProcedure c pragmas c2 n as2 rs2 p) = do   ctx' <- getProcedureContext ctx ff pp@@ -104,7 +105,7 @@       when (not unreachable) $         doImplicitReturn c2 <??           "In implicit return from " ++ show n ++ formatFullContextBrace c-    funcMergeDeps f = mconcat $ (CompiledData (Set.fromList [sfType f]) []):(map funcMergeDeps $ sfMerges f)+    funcMergeDeps f = mconcat $ (onlyDeps (Set.fromList [sfType f])):(map funcMergeDeps $ sfMerges f)     wrapProcedure output pt ct =       mconcat [           funcMergeDeps ff,@@ -124,19 +125,19 @@                   OutOfLineFunction cn -> cn ++ "::"                   _ -> ""     final = case cxxType of-                FinalInlineFunction -> " final"-                _ -> ""+                 FinalInlineFunction -> " final"+                 _ -> ""+    suffix+      | immutable = " const"+      | otherwise = ""     proto       | s == CategoryScope =-        returnType ++ " " ++ prefix ++ name ++ "(const ParamTuple& params, const ValueTuple& args)" ++ final ++ " {"+        "ReturnTuple " ++ prefix ++ name ++ "(const ParamTuple& params, const ValueTuple& args)" ++ final ++ " {"       | s == TypeScope =-        returnType ++ " " ++ prefix ++ name ++-        "(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args)" ++ final ++ " {"+        "ReturnTuple " ++ prefix ++ name ++ "(const ParamTuple& params, const ValueTuple& args) const" ++ final ++ " {"       | s == ValueScope =-        returnType ++ " " ++ prefix ++ name ++-        "(const ParamTuple& params, const ValueTuple& args)" ++ final ++ " {"+        "ReturnTuple " ++ prefix ++ name ++ "(const ParamTuple& params, const ValueTuple& args)" ++ suffix ++ final ++ " {"       | otherwise = undefined-    returnType = "ReturnTuple"     setProcedureTrace       | any isNoTrace pragmas = return []       | otherwise             = return [startFunctionTracing (scName ctx) ff]@@ -148,7 +149,7 @@       | otherwise = return [showCreationTrace]     defineReturns       | isUnnamedReturns rs2 = []-      | otherwise            = [returnType ++ " returns(" ++ show (length $ pValues rs1) ++ ");"]+      | otherwise            = ["ReturnTuple returns(" ++ show (length $ pValues rs1) ++ ");"]     nameParams = flip map (zip ([0..] :: [Int]) $ pValues ps1) $       (\(i,p2) -> paramType ++ " " ++ paramName (vpParam p2) ++ " = params.At(" ++ show i ++ ");")     nameArgs = flip map (zip ([0..] :: [Int]) $ filter (not . isDiscardedInput . snd) $ zip (pValues as1) (pValues $ avNames as2)) $@@ -171,7 +172,10 @@   noTrace <- csGetNoTrace   if noTrace      then return (e',ctx')-     else return (predTraceContext c ++ "(" ++ e' ++ ")",ctx')+     else do+       let c2 = getExpressionContext e+       csAddTrace $ formatFullContext c2+       return (predTraceContext c2 ++ "(" ++ e' ++ ")",ctx')   where     compile = "In condition at " ++ formatFullContext c ??> do       (ts,e') <- compileExpression e@@ -205,7 +209,9 @@   [c] -> CompilerState a m () maybeSetTrace c = do   noTrace <- csGetNoTrace-  when (not noTrace) $ csWrite $ setTraceContext c+  when (not noTrace) $ do+    csWrite $ setTraceContext c+    csAddTrace $ formatFullContext c  compileStatement :: (Ord c, Show c, CollectErrorsM m,                      CompilerContext c m [String] a) =>@@ -271,6 +277,21 @@   (_,e') <- compileExpression e   maybeSetTrace c   csWrite ["(void) (" ++ useAsWhatever e' ++ ");"]+compileStatement (DeferredVariables c as) = message ??> mapM_ createVariable as+  where+    message = "Deferred initialization at " ++ formatFullContext c+    createVariable (CreateVariable c2 t1 n) =+      "In creation of " ++ show n ++ " at " ++ formatFullContext c2 ??> do+        self <- autoSelfType+        t1' <- lift $ replaceSelfValueType self t1+        csAddVariable (UsedVariable c2 n) (VariableValue c2 LocalScope t1' VariableDefault)+        csWrite [variableStoredType t1' ++ " " ++ variableName n ++ ";"]+        csSetDeferred (UsedVariable c2 n)+    createVariable (ExistingVariable (InputValue c2 n)) =+      "In deferring of " ++ show n ++ " at " ++ formatFullContext c2 ??>+      csSetDeferred (UsedVariable c2 n)+    createVariable (ExistingVariable (DiscardInput c2)) =+      compilerErrorM $ "Cannot defer discarded value" ++ formatFullContextBrace c2 compileStatement (Assignment c as e) = message ??> do   (ts,e') <- compileExpression e   r <- csResolver@@ -316,7 +337,7 @@       scoped <- autoScope s       csWrite [scoped ++ variableName n ++ " = " ++ writeStoredVariable t e2 ++ ";"]     assignSingle (_,_,ExistingVariable (DiscardInput _)) e2 = do-      csWrite ["(void)" ++ useAsWhatever e2 ++ ";"]+      csWrite ["(void) (" ++ useAsWhatever e2 ++ ");"]     assignMulti (i,t,CreateVariable _ _ n) =       csWrite [variableName n ++ " = " ++                writeStoredVariable t (UnwrappedSingle $ "r.At(" ++ show i ++ ")") ++ ";"]@@ -335,6 +356,7 @@     let e = readStoredVariable False t (variableName n)     maybeSetTrace c     csWrite [useAsUnwrapped e ++ ".Validate(\"" ++ show n ++ "\");"]+compileStatement (ShowVariable c t n) = csAddVariable (UsedVariable c n) (VariableValue c LocalScope t VariableDefault) compileStatement (RawCodeLine s) = csWrite [s]  compileRegularInit :: (Ord c, Show c, CollectErrorsM m,@@ -392,7 +414,7 @@ compileIfElifElse (IfStatement c e p es) = do   ctx0 <- getCleanContext   cs <- commonIf ctx0 "if" c e p es-  csInheritReturns cs+  csInheritStatic cs   where     unwind ctx0 (IfStatement c2 e2 p2 es2) = commonIf ctx0 "else if" c2 e2 p2 es2     unwind ctx0 (ElseStatement _ p2) = do@@ -419,7 +441,7 @@ compileIteratedLoop (WhileLoop c e p u) = do   ctx0 <- getCleanContext   (e',ctx1) <- compileCondition ctx0 c e-  csInheritReturns [ctx1]+  csInheritStatic [ctx1]   ctx0' <- case u of                 Just p2 -> do                   ctx2 <- lift $ ccStartLoop ctx1 (LoopSetup [])@@ -455,13 +477,17 @@   let callGet  = Expression c2 currExpr [ValueCall c2 $ FunctionCall c2 (FunctionName "get")  (Positional []) (Positional [])]   (Positional [typeGet],exprNext) <- compileExpression callNext   when (typeGet /= currType) $ compilerErrorM $ "Unexpected return type from next(): " ++ show typeGet ++ " (expected) " ++ show currType ++ " (actual)"-  let assnGet = Assignment c2 (Positional [a]) callGet+  let assnGet = if isAssignableDiscard a then [] else [Assignment c2 (Positional [a]) callGet]+  let showVar = case a of+                     CreateVariable c4 t3 n -> [ShowVariable c4 t3 n]+                     _ -> []+  let next = [RawCodeLine $ currVar ++ " = " ++ writeStoredVariable currType exprNext ++ ";"]   csAddRequired $ categoriesFromTypes $ vtType currType   compileStatement $ NoValueExpression [] $ WithScope $ ScopedBlock []     (Procedure [] [RawCodeLine $ variableStoredType currType ++ " " ++ currVar ++ " = " ++ writeStoredVariable currType e' ++ ";"]) Nothing []     (NoValueExpression [] $ Loop $ WhileLoop [] (Expression [] currPresent [])-      (Procedure c3 (assnGet:(ss ++ update)))-      (Just $ Procedure [] [RawCodeLine $ currVar ++ " = " ++ writeStoredVariable currType exprNext ++ ";"]))+      (Procedure c3 (assnGet ++ ss))+      (Just $ Procedure [] (next ++ showVar ++ update)))     where       update = case u of                     Just (Procedure _ ss2) -> ss2@@ -476,6 +502,10 @@   ScopedBlock c -> CompilerState a m () compileScopedBlock s@(ScopedBlock _ _ _ c2 _) = do   let (vs,p,cl,st) = rewriteScoped s+  case st of+       DeferredVariables c3 _ ->+         compilerErrorM $ "Cannot defer variable initialization at the top level of scoped/cleanup in statements" ++ formatFullContextBrace c3+       _ -> return ()   self <- autoSelfType   vs' <- lift $ mapCompilerM (replaceSelfVariable self) vs   -- Capture context so we can discard scoped variable names.@@ -501,6 +531,10 @@          -- Insert an empty cleanup so that it can be used below.          Nothing -> lift $ ccPushCleanup ctxP ctxCl0   ctxS <- compileProcedure ctxP' (Procedure [] [st])+  case st of+       -- Make sure that top-level assignments removed deferred status.+       Assignment _ (Positional existing) _ -> mapM_ setAssigned existing+       _ -> return ()   csWrite ["{"]   autoInlineOutput ctxS   -- NOTE: Keep this after inlining the in block in case the in block contains a@@ -510,6 +544,8 @@   csWrite ["}"]   sequence_ $ map showVariable vs'   where+    setAssigned (ExistingVariable (InputValue _ n)) = csUpdateAssigned n+    setAssigned _ = return ()     replaceSelfVariable self (c,t,n) = do       t' <- replaceSelfValueType self t       return (c,t',n)@@ -537,6 +573,11 @@         (created,existing) = foldr update ([],[]) (pValues vs)         update (CreateVariable c t n) (cs,es) = ((c,t,n):cs,(ExistingVariable $ InputValue c n):es)         update e2 (cs,es) = (cs,e2:es)+    rewriteScoped (ScopedBlock _ p cl _ (DeferredVariables c3 vs)) =+      (created,p,cl,DeferredVariables c3 existing) where+        (created,existing) = foldr update ([],[]) vs+        update (CreateVariable c t n) (cs,es) = ((c,t,n):cs,(ExistingVariable $ InputValue c n):es)+        update e2 (cs,es) = (cs,e2:es)     -- Merge the statement into the scoped block.     rewriteScoped (ScopedBlock _ p cl _ s2) =       ([],p,cl,s2)@@ -862,7 +903,7 @@           UnwrappedSingle $ valueCreator (tiName t') ++ "(" ++ typeInstance ++ ", " ++ es'' ++ ")")   where     getType _  True ValueScope _      = "parent"-    getType _  True TypeScope  _      = "shared_from_this()"+    getType _  True TypeScope  _      = "PARAM_SELF"     getType t2 _    _          params = typeCreator (tiName t2) ++ "(" ++ params ++ ")"     -- Single expression, but possibly multi-return.     getValues [(Positional ts,e)] = return (ts,useAsArgs e)@@ -958,9 +999,9 @@     assemble Nothing _ ValueScope ValueScope ps2 es2 =       return $ callName (sfName f) ++ "(" ++ ps2 ++ ", " ++ es2 ++ ")"     assemble Nothing _ TypeScope TypeScope ps2 es2 =-      return $ callName (sfName f) ++ "(Param_self, " ++ ps2 ++ ", " ++ es2 ++ ")"+      return $ callName (sfName f) ++ "(" ++ ps2 ++ ", " ++ es2 ++ ")"     assemble Nothing scoped ValueScope TypeScope ps2 es2 =-      return $ scoped ++ callName (sfName f) ++ "(parent, "++ ps2 ++ ", " ++ es2 ++ ")"+      return $ scoped ++ callName (sfName f) ++ "(" ++ ps2 ++ ", " ++ es2 ++ ")"     assemble Nothing scoped _ _ ps2 es2 =       return $ scoped ++ callName (sfName f) ++ "(" ++ ps2 ++ ", " ++ es2 ++ ")"     assemble (Just e2) _ _ ValueScope ps2 es2 =@@ -988,9 +1029,10 @@   Positional ValueType -> m (Positional GeneralInstance) guessParamsFromArgs r fa f ps ts = do   fm <- getFunctionFilterMap f-  args <- processPairs (\t1 t2 -> return $ PatternMatch Covariant t1 t2) ts (fmap pvType $ sfArgs f)+  args <- processPairs (\t1 t2 -> return $ TypePattern Covariant t1 t2) ts (fmap pvType $ sfArgs f)+  filts <- fmap concat $ processPairs (guessesFromFilters fm) ts (fmap pvType $ sfArgs f)   pa <- fmap Map.fromList $ processPairs toInstance (fmap vpParam $ sfParams f) ps-  gs <- inferParamTypes r fa pa args+  gs <- inferParamTypes r fa pa (args ++ filts)   gs' <- mergeInferredTypes r fa fm pa gs   let pa3 = gs' `Map.union` pa   fmap Positional $ mapCompilerM (subPosition pa3) (pValues $ sfParams f) where@@ -1006,7 +1048,7 @@   r -> ParamFilters -> Positional ValueType -> Positional ParamName ->   Positional (InstanceOrInferred c) -> Positional ValueType -> m (Positional GeneralInstance) guessParams r fa args params ps ts = do-  args' <- processPairs (\t1 t2 -> return $ PatternMatch Covariant t1 t2) ts args+  args' <- processPairs (\t1 t2 -> return $ TypePattern Covariant t1 t2) ts args   pa <- fmap Map.fromList $ processPairs toInstance params ps   gs <- inferParamTypes r fa pa args'   gs' <- mergeInferredTypes r fa (Map.fromList $ zip (pValues params) (repeat [])) pa gs@@ -1031,12 +1073,13 @@  compileTestProcedure :: (Ord c, Show c, CollectErrorsM m) =>   CategoryMap c -> ExprMap c -> TestProcedure c -> m (CompiledData [String])-compileTestProcedure tm em (TestProcedure c n p) = do+compileTestProcedure tm em (TestProcedure c n cov p) = do   ctx <- getMainContext tm em   p' <- runDataCompiler compiler ctx <??     "In unittest " ++ show n ++ formatFullContextBrace c   return $ mconcat [       onlyCode $ "ReturnTuple " ++ testFunctionName n ++ "() {",+      indentCompiled handleCoverage,       indentCompiled $ onlyCode $ startTestTracing n,       indentCompiled p',       indentCompiled $ onlyCode $ "return ReturnTuple();",@@ -1045,6 +1088,9 @@     compiler = do       ctx0 <- getCleanContext       compileProcedure ctx0 p >>= put+    handleCoverage+      | cov       = onlyCode "LogCalls::DisableCallLogging();"+      | otherwise = emptyCode  selectTestFromArgv1 :: CollectErrorsM m => [FunctionName] -> m ([String],CompiledData [String]) selectTestFromArgv1 fs = return (includes,allCode) where@@ -1118,56 +1164,57 @@ expandGeneralInstance t   | t == minBound = return $ allGetter ++ "()"   | t == maxBound = return $ anyGetter ++ "()"-expandGeneralInstance t = reduceMergeTree getAny getAll getSingle t where-  getAny ts = combine ts >>= return . (unionGetter ++)-  getAll ts = combine ts >>= return . (intersectGetter ++)-  getSingle (JustTypeInstance (TypeInstance t2 ps)) = do-    ps' <- sequence $ map expandGeneralInstance $ pValues ps-    return $ typeGetter t2 ++ "(T_get(" ++ intercalate "," ps' ++ "))"-  getSingle (JustParamName _ p)  = do-    s <- csGetParamScope p-    scoped <- autoScope s-    return $ scoped ++ paramName p-  getSingle (JustInferredType p) = getSingle (JustParamName False p)-  combine ps = do-    ps' <- sequence ps-    return $ "(L_get<S<const " ++ typeBase ++ ">>(" ++ intercalate "," ps' ++ "))"+expandGeneralInstance t = do+  r <- csResolver+  f <- csAllFilters+  t' <- lift $ dedupGeneralInstance r f t+  reduceMergeTree getAny getAll getSingle t' where+    getAny ts = combine ts >>= return . (unionGetter ++)+    getAll ts = combine ts >>= return . (intersectGetter ++)+    getSingle (JustTypeInstance (TypeInstance t2 ps)) = do+      ps' <- sequence $ map expandGeneralInstance $ pValues ps+      return $ typeGetter t2 ++ "(T_get(" ++ intercalate "," ps' ++ "))"+    getSingle (JustParamName _ p)  = do+      s <- csGetParamScope p+      scoped <- autoScope s+      return $ scoped ++ paramName p+    getSingle (JustInferredType p) = getSingle (JustParamName False p)+    combine ps = do+      ps' <- sequence ps+      return $ "(L_get<S<const " ++ typeBase ++ ">>(" ++ intercalate "," ps' ++ "))"  doImplicitReturn :: (CollectErrorsM m, Ord c, Show c, CompilerContext c m [String] a) =>   [c] -> CompilerState a m () doImplicitReturn c = do   named <- csIsNamedReturns   csRegisterReturn c Nothing-  (CleanupBlock ss _ _ req) <- csGetCleanup JumpReturn-  csAddRequired req-  csSetJumpType c JumpReturn-  csWrite ss+  get >>= autoInsertCleanup c JumpReturn   if not named-     then csWrite ["return ReturnTuple(0);"]+     then csWrite ["return ReturnTuple();"]      else do        getPrimNamedReturns        csWrite ["return returns;"]   where -autoPositionalCleanup :: (CollectErrorsM m, CompilerContext c m [String] a) =>+autoPositionalCleanup :: (Ord c,Eq c,Show c,CollectErrorsM m, CompilerContext c m [String] a) =>   [c] -> ExpressionValue -> CompilerState a m () autoPositionalCleanup c e = do   named <- csIsNamedReturns-  (CleanupBlock ss _ _ req) <- csGetCleanup JumpReturn-  csAddRequired req-  csSetJumpType c JumpReturn+  (CleanupBlock ss _ _ _ _) <- csGetCleanup JumpReturn   if null ss-     then csWrite ["return " ++ useAsReturns e ++ ";"]+     then do+       csSetJumpType c JumpReturn+       csWrite ["return " ++ useAsReturns e ++ ";"]      else do        if named           then do-            csWrite ["returns = " ++ useAsReturns e ++ ";"]+            csWrite ["returns.TransposeFrom(" ++ useAsReturns e ++ ");"]             setPrimNamedReturns-            csWrite ss+            get >>= autoInsertCleanup c JumpReturn             csWrite ["return returns;"]           else do             csWrite ["{","ReturnTuple returns = " ++ useAsReturns e ++ ";"]-            csWrite ss+            get >>= autoInsertCleanup c JumpReturn             csWrite ["return returns;","}"]  setPrimNamedReturns ::  (CollectErrorsM m, CompilerContext c m [String] a) =>@@ -1190,27 +1237,30 @@ autoInsertCleanup :: (Ord c, Show c, CollectErrorsM m, CompilerContext c m [String] a) =>   [c] -> JumpType -> a -> CompilerState a m () autoInsertCleanup c j ctx = do-  (CleanupBlock ss vs jump req) <- lift $ ccGetCleanup ctx j-  lift (ccCheckVariableInit ctx $ nub vs) <??+  (CleanupBlock ss ds vs jump req) <- lift $ ccGetCleanup ctx j+  csCheckVariableInit (nub vs) <??     "In inlining of cleanup block after statement at " ++ formatFullContext c   let vs2 = map (\(UsedVariable c0 v) -> UsedVariable (c ++ c0) v) vs+  csInheritDeferred ds   -- This is needed in case a cleanup is inlined within another cleanup, e.g.,   -- e.g., if the latter has a break statement.-  sequence_ $ map csAddUsed $ vs2+  mapM_ csAddUsed vs2   csWrite ss   csAddRequired req   csSetJumpType c jump  inheritRequired :: (CollectErrorsM m, CompilerContext c m [String] a) =>   a -> CompilerState a m ()-inheritRequired ctx = lift (ccGetRequired ctx) >>= csAddRequired+inheritRequired ctx = do+  lift (ccGetRequired ctx) >>= csAddRequired+  lift (ccGetTraces ctx) >>= sequence . map csAddTrace >> return ()  autoInlineOutput :: (Ord c, Show c, CollectErrorsM m, CompilerContext c m [String] a) =>   a -> CompilerState a m () autoInlineOutput ctx = do   inheritRequired ctx   getAndIndentOutput ctx >>= csWrite-  csInheritReturns [ctx]+  csInheritStatic [ctx]  getAndIndentOutput :: (Ord c, Show c, CollectErrorsM m, CompilerContext c m [String] a) =>   a -> CompilerState a m [String]
src/Module/CompileMetadata.hs view
@@ -40,6 +40,7 @@   CompileMetadata {     cmVersionHash :: VersionHash,     cmPath :: FilePath,+    cmExtra :: [FilePath],     cmPublicNamespace :: Namespace,     cmPrivateNamespace :: Namespace,     cmPublicDeps :: [FilePath],@@ -103,6 +104,7 @@   ModuleConfig {     mcRoot :: FilePath,     mcPath :: FilePath,+    mcExtra :: [FilePath],     mcExprMap :: [(MacroName,Expression SourceContext)],     mcPublicDeps :: [FilePath],     mcPrivateDeps :: [FilePath],@@ -113,9 +115,10 @@   deriving (Show)  instance Eq ModuleConfig where-  (ModuleConfig pA dA _ isA is2A esA epA mA) == (ModuleConfig pB dB _ isB is2B esB epB mB) =+  (ModuleConfig pA dA eeA _ isA is2A esA epA mA) == (ModuleConfig pB dB eeB _ isB is2B esB epB mB) =     all id [         pA == pB,+        eeA == eeB,         dA == dB,         isA == isB,         is2A == is2B,
src/Module/ParseMetadata.hs view
@@ -125,6 +125,7 @@   readConfig = runPermutation $ CompileMetadata     <$> parseRequired "version_hash:"       parseHash     <*> parseRequired "path:"               parseQuoted+    <*> parseOptional "extra_paths:"        [] (parseList parseQuoted)     <*> parseOptional "public_namespace:"   NoNamespace parseNamespace     <*> parseOptional "private_namespace:"  NoNamespace parseNamespace     <*> parseRequired "public_deps:"        (parseList parseQuoted)@@ -142,7 +143,7 @@     <*> parseRequired "libraries:"          (parseList parseQuoted)     <*> parseRequired "link_flags:"         (parseList parseQuoted)     <*> parseRequired "object_files:"       (parseList readConfig)-  writeConfig (CompileMetadata h p ns1 ns2 is is2 cs1 cs2 ds1 ds2 ps xs ts hxx cxx bs ls lf os) = do+  writeConfig (CompileMetadata h p ee ns1 ns2 is is2 cs1 cs2 ds1 ds2 ps xs ts hxx cxx bs ls lf os) = do     validateHash h     ns1' <- maybeShowNamespace "public_namespace:"  ns1     ns2' <- maybeShowNamespace "private_namespace:" ns2@@ -153,6 +154,9 @@         "version_hash: " ++ show h,         "path: " ++ show p       ] ++ ns1' ++ ns2' ++ [+        "extra_paths: ["+      ] ++ indents (map show ee) ++ [+        "]",         "public_deps: ["       ] ++ indents (map show is) ++ [         "]",@@ -275,19 +279,23 @@   readConfig = runPermutation $ ModuleConfig     <$> parseOptional "root:"           "" parseQuoted     <*> parseRequired "path:"              parseQuoted+    <*> parseOptional "extra_paths:"    [] (parseList parseQuoted)     <*> parseOptional "expression_map:" [] (parseList parseExprMacro)     <*> parseOptional "public_deps:"    [] (parseList parseQuoted)     <*> parseOptional "private_deps:"   [] (parseList parseQuoted)     <*> parseOptional "extra_files:"    [] (parseList readConfig)     <*> parseOptional "include_paths:"  [] (parseList parseQuoted)     <*> parseRequired "mode:"              readConfig-  writeConfig (ModuleConfig p d em is is2 es ep m) = do+  writeConfig (ModuleConfig p d ee em is is2 es ep m) = do     es' <- fmap concat $ mapCompilerM writeConfig es     m' <- writeConfig m     when (not $ null em) $ compilerErrorM "Only empty expression maps are allowed when writing"     return $ [         "root: " ++ show p,         "path: " ++ show d,+        "extra_paths: ["+      ] ++ indents (map show ee) ++ [+        "]",         "expression_map: [",         -- NOTE: expression_map isn't output because that would require making         -- all Expression serializable.
src/Module/ProcessMetadata.hs view
@@ -42,17 +42,19 @@   loadRecompile,   loadTestingDeps,   mapMetadata,+  readPossibleTraces,   resolveCategoryDeps,   resolveObjectDeps,   sortCompiledFiles,   writeCachedFile,   writeMetadata,+  writePossibleTraces,   writeRecompile, ) where  import Control.Applicative ((<|>)) import Control.Monad (when)-import Data.List (isSuffixOf)+import Data.List (isSuffixOf,nub) import Data.Maybe (isJust) import System.Directory import System.FilePath@@ -85,6 +87,9 @@ metadataFilename :: FilePath metadataFilename = "compile-metadata" +tracesFilename :: FilePath+tracesFilename = "traced-lines"+ type MetadataMap = Map.Map FilePath CompileMetadata  mapMetadata :: [CompileMetadata] -> MetadataMap@@ -137,6 +142,17 @@   m' <- autoWriteConfig m <?? "In data for " ++ p   errorFromIO $ writeFile f m' +writePossibleTraces :: FilePath -> Set.Set String -> TrackedErrorsIO ()+writePossibleTraces p ts = do+  p' <- errorFromIO $ canonicalizePath p+  writeCachedFile p' "" tracesFilename $ concat $ map (++"\n") $ Set.toList ts++readPossibleTraces :: FilePath -> TrackedErrorsIO (Set.Set String)+readPossibleTraces p = do+  p' <- errorFromIO $ canonicalizePath p+  c <- errorFromIO $ readFile (getCachedPath p' "" tracesFilename)+  return $ Set.fromList $ lines c+ getRecompilePath :: FilePath -> TrackedErrorsIO FilePath getRecompilePath p = do   p' <- errorFromIO $ canonicalizePath p@@ -166,18 +182,19 @@ getCacheRelativePath :: FilePath -> FilePath getCacheRelativePath f = ".." </> f -findSourceFiles :: FilePath -> FilePath -> TrackedErrorsIO ([FilePath],[FilePath],[FilePath])-findSourceFiles p0 p = do-  let absolute = p0 </> p-  isFile <- errorFromIO $ doesFileExist absolute-  when isFile $ compilerErrorM $ "Path \"" ++ absolute ++ "\" is not a directory"-  isDir <- errorFromIO $ doesDirectoryExist absolute-  when (not isDir) $ compilerErrorM $ "Path \"" ++ absolute ++ "\" does not exist"-  ds <- errorFromIO $ getDirectoryContents absolute >>= return . map (p </>)-  let ps = filter (isSuffixOf ".0rp") ds-  let xs = filter (isSuffixOf ".0rx") ds-  let ts = filter (isSuffixOf ".0rt") ds-  return (ps,xs,ts)+findSourceFiles :: FilePath -> [FilePath] -> TrackedErrorsIO ([FilePath],[FilePath],[FilePath])+findSourceFiles p0 = fmap (select . concat) . mapCompilerM find where+  find p = do+    let absolute = p0 </> p+    isFile <- errorFromIO $ doesFileExist absolute+    when isFile $ compilerErrorM $ "Path \"" ++ absolute ++ "\" is not a directory"+    isDir <- errorFromIO $ doesDirectoryExist absolute+    when (not isDir) $ compilerErrorM $ "Path \"" ++ absolute ++ "\" does not exist"+    errorFromIO $ getDirectoryContents absolute >>= return . map (p </>)+  select ds = (ps,xs,ts) where+    ps = nub $ filter (isSuffixOf ".0rp") ds+    xs = nub $ filter (isSuffixOf ".0rx") ds+    ts = nub $ filter (isSuffixOf ".0rt") ds  getExprMap :: FilePath -> ModuleConfig -> TrackedErrorsIO (ExprMap SourceContext) getExprMap p m = do@@ -292,19 +309,19 @@ checkModuleVersionHash h m = cmVersionHash m == h  checkModuleFreshness :: VersionHash -> MetadataMap -> FilePath -> CompileMetadata -> TrackedErrorsIO ()-checkModuleFreshness h ca p m@(CompileMetadata _ p2 _ _ is is2 _ _ _ _ ps xs ts hxx cxx bs ls _ os) = do+checkModuleFreshness h ca p m@(CompileMetadata _ p2 ep _ _ is is2 _ _ _ _ ps xs ts hxx cxx bs ls _ os) = do   time <- errorFromIO $ getModificationTime $ getCachedPath p "" metadataFilename-  (ps2,xs2,ts2) <- findSourceFiles p ""+  (ps2,xs2,ts2) <- findSourceFiles "" (p2:ep)   let rs = Set.toList $ Set.fromList $ concat $ map getRequires os   collectAllM_ $ [       checkHash,       checkInput time (p </> moduleFilename),-      checkMissing ps ps2,-      checkMissing xs xs2,-      checkMissing ts ts2+      mapCompilerM (errorFromIO . canonicalizePath) ps2 >>= checkMissing ps,+      mapCompilerM (errorFromIO . canonicalizePath) xs2 >>= checkMissing xs,+      mapCompilerM (errorFromIO . canonicalizePath) ts2 >>= checkMissing ts     ] ++     (map (checkDep time) $ is ++ is2) ++-    (map (checkInput time . (p2 </>)) $ ps ++ xs) +++    (map (checkInput time) $ ps ++ xs) ++     (map (checkInput time . getCachedPath p2 "") $ hxx ++ cxx) ++     (map checkOutput bs) ++     (map checkOutput ls) ++@@ -337,7 +354,7 @@       compilerErrorM $ "Required category " ++ show c ++ " is unresolved"     checkMissing s0 s1 = do       let missing = Set.toList $ Set.fromList s1 `Set.difference` Set.fromList s0-      mapCompilerM_ (\f -> compilerErrorM $ "Required path \"" ++ f ++ "\" has not been compiled") missing+      mapCompilerM_ (\f -> compilerErrorM $ "Required path \"" ++ f ++ "\" is not present in cached data") missing     doesFileOrDirExist f2 = do       existF <- errorFromIO $ doesFileExist f2       if existF@@ -384,9 +401,9 @@                 -- TODO: The path comparison here is sloppy.                 (if cmPath dep == p then zip (cmPrivateCategories dep) (repeat $ cmPrivateNamespace dep) else [])   categoriesToIds dep = map (uncurry $ CategoryIdentifier $ cmPath dep) $ getCats dep-  cxxToId (CxxOutput (Just c) _ ns _ _ _) = CategoryIdentifier d c ns-  cxxToId _                               = undefined-  resolveCategory (fs,ca@(CxxOutput _ _ _ ns2 ds _)) =+  cxxToId (CxxOutput (Just c) _ ns _ _ _ _) = CategoryIdentifier d c ns+  cxxToId _                                 = undefined+  resolveCategory (fs,ca@(CxxOutput _ _ _ ns2 ds _ _)) =     (cxxToId ca,CategoryObjectFile (cxxToId ca) (filter (/= cxxToId ca) rs) fs) where       rs = concat $ map (resolveDep categoryMap (Set.toList ns2 ++ publicNamespaces)) $ Set.toList ds 
src/Parser/Common.hs view
@@ -40,6 +40,7 @@   kwCleanup,   kwConcrete,   kwContinue,+  kwDefer,   kwDefine,   kwDefines,   kwElif,@@ -50,6 +51,7 @@   kwIf,   kwIgnore,   kwIn,+  kwImmutable,   kwInterface,   kwOptional,   kwPresent,@@ -66,7 +68,6 @@   kwTrue,   kwType,   kwTypename,-  kwTypes,   kwUnittest,   kwUpdate,   kwValue,@@ -198,6 +199,9 @@ kwContinue :: TextParser () kwContinue = keyword "continue" +kwDefer :: TextParser ()+kwDefer = keyword "defer"+ kwDefine :: TextParser () kwDefine = keyword "define" @@ -228,6 +232,9 @@ kwIgnore :: TextParser () kwIgnore = keyword "_" +kwImmutable :: TextParser ()+kwImmutable = keyword "immutable"+ kwInterface :: TextParser () kwInterface = keyword "interface" @@ -276,9 +283,6 @@ kwTypename :: TextParser () kwTypename = keyword "typename" -kwTypes :: TextParser ()-kwTypes = keyword "types"- kwUnittest :: TextParser () kwUnittest = keyword "unittest" @@ -319,6 +323,7 @@     kwCleanup,     kwConcrete,     kwContinue,+    kwDefer,     kwDefine,     kwDefines,     kwElif,@@ -329,6 +334,7 @@     kwIf,     kwIn,     kwIgnore,+    kwImmutable,     kwInterface,     kwOptional,     kwPresent,@@ -345,7 +351,6 @@     kwTrue,     kwType,     kwTypename,-    kwTypes,     kwUnittest,     kwUpdate,     kwValue,
src/Parser/Procedure.hs view
@@ -64,9 +64,11 @@     kwUnittest     n <- try sourceParser     sepAfter (string_ "{")+    cov <- coveragePragma <|> return False     pp <- sourceParser     sepAfter (string_ "}")-    return $ TestProcedure [c] n pp+    return $ TestProcedure [c] n cov pp where+      coveragePragma = autoPragma "DisableCoverage" $ Left (const True)  instance ParseFromSource (ArgValues SourceContext) where   sourceParser = labeled "procedure arguments" $ do@@ -133,13 +135,19 @@                  parseAssign <|>                  parsePragma <|>                  parseIgnore where-    parseAssign = labeled "statement" $ do+    parseAssign = labeled "assignment" $ do       c <- getSourceContext       as <- sepBy sourceParser (sepAfter $ string_ ",")       assignOperator+      assignExpr c as <|> assignDefer c as+    assignExpr c as = do       e <- sourceParser       statementEnd       return $ Assignment [c] (Positional as) e+    assignDefer c as = do+      kwDefer+      statementEnd+      return $ DeferredVariables [c] as     parseBreak = labeled "break" $ do       c <- getSourceContext       kwBreak
src/Parser/TextParser.hs view
@@ -60,7 +60,7 @@  instance Show SourceContext where   show (SourceContext (SourcePos f l c)) =-    "line " ++ show (unPos l) ++ " column " ++ show (unPos c) ++ " of " ++ "\"" ++ f ++ "\""+    "line " ++ show (unPos l) ++ " column " ++ show (unPos c) ++ " of " ++ f  getSourceContext :: TextParser SourceContext getSourceContext = fmap SourceContext getSourcePos
src/Parser/TypeCategory.hs view
@@ -46,29 +46,37 @@       n <- sourceParser       ps <- parseCategoryParams       open+      pg <- pragmas       rs <- parseCategoryRefines       fs <- flip sepBy optionalSpace $ parseScopedFunction (return ValueScope) (return n)       close-      return $ ValueInterface [c] NoNamespace n ps rs fs+      return $ ValueInterface [c] NoNamespace n pg ps rs fs     parseInstance = labeled "type interface" $ do       c <- getSourceContext       try $ kwType >> kwInterface       n <- sourceParser       ps <- parseCategoryParams       open+      pg <- pragmas       fs <- flip sepBy optionalSpace $ parseScopedFunction (return TypeScope) (return n)       close-      return $ InstanceInterface [c] NoNamespace n ps fs+      return $ InstanceInterface [c] NoNamespace n pg ps fs     parseConcrete = labeled "concrete type" $ do       c <- getSourceContext       kwConcrete       n <- sourceParser       ps <- parseCategoryParams       open+      pg <- pragmas       (rs,ds,vs) <- parseRefinesDefinesFilters       fs <- flip sepBy optionalSpace $ parseScopedFunction parseScope (return n)       close-      return $ ValueConcrete [c] NoNamespace n ps rs ds vs fs+      return $ ValueConcrete [c] NoNamespace n pg ps rs ds vs fs+    pragmas = fmap (:[]) immutable <|> return []+    immutable = do+      c <- getSourceContext+      kwImmutable+      return $ CategoryImmutable [c]  parseCategoryParams :: TextParser [ValueParam SourceContext] parseCategoryParams = do
src/Parser/TypeInstance.hs view
@@ -122,7 +122,7 @@     inst = labeled "type instance" $ fmap JustTypeInstance sourceParser  instance ParseFromSource TypeFilter where-  sourceParser = requires <|> allows <|> defines where+  sourceParser = requires <|> allows <|> defines <|> immutable where     requires = labeled "requires filter" $ do       kwRequires       t <- sourceParser@@ -135,3 +135,6 @@       kwDefines       t <- sourceParser       return $ DefinesFilter t+    immutable = labeled "immutable filter" $ do+      kwImmutable+      return ImmutableFilter
src/Test/ParseMetadata.hs view
@@ -39,6 +39,10 @@ hugeCompileMetadata = CompileMetadata {     cmVersionHash = VersionHash "0123456789ABCDEFabcdef",     cmPath = "/home/project/special",+    cmExtra = [+      "extra1",+      "extra2"+    ],     cmPublicNamespace = StaticNamespace "public_ABCDEF",     cmPrivateNamespace = StaticNamespace "private_ABCDEF",     cmPublicDeps = [@@ -126,6 +130,10 @@ hugeModuleConfig = ModuleConfig {     mcRoot = "/home/projects",     mcPath = "special",+    mcExtra = [+      "extra1",+      "extra2"+    ],     mcExprMap = [],     mcPublicDeps = [       "/home/project/public-dep1",@@ -170,6 +178,7 @@     checkWriteFail "bad hash" $ CompileMetadata {       cmVersionHash = VersionHash "bad hash",       cmPath = "/home/project/special",+      cmExtra = [],       cmPublicNamespace = NoNamespace,       cmPrivateNamespace = NoNamespace,       cmPublicDeps = [],@@ -192,6 +201,7 @@     checkWriteFail "bad namespace" $ CompileMetadata {       cmVersionHash = VersionHash "0123456789ABCDEFabcdef",       cmPath = "/home/project/special",+      cmExtra = [],       cmPublicNamespace = StaticNamespace "bad namespace",       cmPrivateNamespace = NoNamespace,       cmPublicDeps = [],@@ -214,6 +224,7 @@     checkWriteFail "bad namespace" $ CompileMetadata {       cmVersionHash = VersionHash "0123456789ABCDEFabcdef",       cmPath = "/home/project/special",+      cmExtra = [],       cmPublicNamespace = NoNamespace,       cmPrivateNamespace = StaticNamespace "bad namespace",       cmPublicDeps = [],@@ -236,6 +247,7 @@     checkWriteFail "bad category" $ CompileMetadata {       cmVersionHash = VersionHash "0123456789ABCDEFabcdef",       cmPath = "/home/project/special",+      cmExtra = [],       cmPublicNamespace = NoNamespace,       cmPrivateNamespace = NoNamespace,       cmPublicDeps = [],@@ -260,6 +272,7 @@     checkWriteFail "bad category" $ CompileMetadata {       cmVersionHash = VersionHash "0123456789ABCDEFabcdef",       cmPath = "/home/project/special",+      cmExtra = [],       cmPublicNamespace = NoNamespace,       cmPrivateNamespace = NoNamespace,       cmPublicDeps = [],@@ -286,6 +299,7 @@     checkWriteFail "empty.+map" $ ModuleConfig {       mcRoot = "/home/projects",       mcPath = "special",+      mcExtra = [],       mcExprMap = [(MacroName "MACRO",Literal (StringLiteral [] "something"))],       mcPublicDeps = [],       mcPrivateDeps = [],
src/Test/TypeCategory.hs view
@@ -98,28 +98,28 @@     checkOperationSuccess       ("testfiles" </> "concrete_refines_value.0rx")       (checkConnectedTypes $ Map.fromList [-          (CategoryName "Parent2",InstanceInterface [] NoNamespace (CategoryName "Parent2") [] [])+          (CategoryName "Parent2",InstanceInterface [] NoNamespace (CategoryName "Parent2") [] [] [])         ]),     checkOperationFail       ("testfiles" </> "concrete_refines_value.0rx")       (checkConnectedTypes $ Map.fromList [-          (CategoryName "Parent",InstanceInterface [] NoNamespace (CategoryName "Parent") [] [])+          (CategoryName "Parent",InstanceInterface [] NoNamespace (CategoryName "Parent") [] [] [])         ]),      checkOperationSuccess       ("testfiles" </> "partial.0rx")       (checkConnectedTypes $ Map.fromList [-          (CategoryName "Parent",ValueInterface [] NoNamespace (CategoryName "Parent") [] [] [])+          (CategoryName "Parent",ValueInterface [] NoNamespace (CategoryName "Parent") [] [] [] [])         ]),     checkOperationFail       ("testfiles" </> "partial.0rx")       (checkConnectedTypes $ Map.fromList [-          (CategoryName "Parent",InstanceInterface [] NoNamespace (CategoryName "Parent") [] [])+          (CategoryName "Parent",InstanceInterface [] NoNamespace (CategoryName "Parent") [] [] [])         ]),     checkOperationFail       ("testfiles" </> "partial.0rx")       (checkConnectedTypes $ Map.fromList [-          (CategoryName "Parent",ValueConcrete [] NoNamespace (CategoryName "Parent") [] [] [] [] [])+          (CategoryName "Parent",ValueConcrete [] NoNamespace (CategoryName "Parent") [] [] [] [] [] [])         ]),      checkOperationSuccess ("testfiles" </> "value_refines_value.0rx") (checkConnectionCycles Map.empty),@@ -165,7 +165,7 @@       ("testfiles" </> "flatten.0rx")       (\ts -> do         existing  <- return $ Map.fromList [-            (CategoryName "Parent2",InstanceInterface [] NoNamespace (CategoryName "Parent2") [] [])+            (CategoryName "Parent2",InstanceInterface [] NoNamespace (CategoryName "Parent2") [] [] [])           ]         ts2 <- topoSortCategories existing ts         flattenAllConnections existing ts2),@@ -173,7 +173,7 @@       ("testfiles" </> "flatten.0rx")       (\ts -> do         existing  <- return $ Map.fromList [-            (CategoryName "Parent",InstanceInterface [] NoNamespace (CategoryName "Parent") [] [])+            (CategoryName "Parent",InstanceInterface [] NoNamespace (CategoryName "Parent") [] [] [])           ]         topoSortCategories existing ts), @@ -182,13 +182,13 @@       (\ts -> do         existing  <- return $ Map.fromList [             (CategoryName "Parent",-            ValueInterface [] NoNamespace (CategoryName "Parent") []+            ValueInterface [] NoNamespace (CategoryName "Parent") [] []                            [ValueRefine [] $ TypeInstance (CategoryName "Object1") (Positional []),-                           ValueRefine [] $ TypeInstance (CategoryName "Object2") (Positional [])] []),+                            ValueRefine [] $ TypeInstance (CategoryName "Object2") (Positional [])] []),             -- NOTE: Object1 deliberately excluded here so that we catch             -- unnecessary recursion in existing categories.             (CategoryName "Object2",-            ValueInterface [] NoNamespace (CategoryName "Object2") [] [] [])+            ValueInterface [] NoNamespace (CategoryName "Object2") [] [] [] [])           ]         ts2 <- topoSortCategories existing ts         ts3 <- flattenAllConnections existing ts2@@ -214,19 +214,19 @@     checkOperationSuccess       ("testfiles" </> "concrete_refines_value.0rx")       (checkParamVariances $ Map.fromList [-          (CategoryName "Parent2",InstanceInterface [] NoNamespace (CategoryName "Parent2") [] [])+          (CategoryName "Parent2",InstanceInterface [] NoNamespace (CategoryName "Parent2") [] [] [])         ]),     checkOperationFail       ("testfiles" </> "concrete_refines_value.0rx")       (checkParamVariances $ Map.fromList [-          (CategoryName "Parent",InstanceInterface [] NoNamespace (CategoryName "Parent") [] [])+          (CategoryName "Parent",InstanceInterface [] NoNamespace (CategoryName "Parent") [] [] [])         ]),      checkOperationSuccess       ("testfiles" </> "partial_params.0rx")       (checkParamVariances $ Map.fromList [           (CategoryName "Parent",-           ValueInterface [] NoNamespace (CategoryName "Parent")+           ValueInterface [] NoNamespace (CategoryName "Parent") []                           [ValueParam [] (ParamName "#w") Contravariant,                            ValueParam [] (ParamName "#z") Covariant] [] [])       ]),@@ -234,7 +234,7 @@       ("testfiles" </> "partial_params.0rx")       (checkParamVariances $ Map.fromList [           (CategoryName "Parent",-           ValueInterface [] NoNamespace (CategoryName "Parent")+           ValueInterface [] NoNamespace (CategoryName "Parent") []                           [ValueParam [] (ParamName "#w") Invariant,                            ValueParam [] (ParamName "#z") Covariant] [] [])       ]),@@ -242,7 +242,7 @@       ("testfiles" </> "partial_params.0rx")       (checkParamVariances $ Map.fromList [           (CategoryName "Parent",-           ValueInterface [] NoNamespace (CategoryName "Parent")+           ValueInterface [] NoNamespace (CategoryName "Parent") []                           [ValueParam [] (ParamName "#w") Contravariant,                            ValueParam [] (ParamName "#z") Invariant] [] [])       ]),@@ -535,7 +535,7 @@       ("testfiles" </> "flatten.0rx")       (\ts -> do         let tm0 = Map.fromList [-                    (CategoryName "Parent2",InstanceInterface [] NoNamespace (CategoryName "Parent2") [] [])+                    (CategoryName "Parent2",InstanceInterface [] NoNamespace (CategoryName "Parent2") [] [] [])                   ]         tm <- includeNewTypes tm0 ts         rs <- getRefines tm "Child"@@ -1099,13 +1099,13 @@  scrapeAllRefines :: [AnyCategory c] -> [(String, String)] scrapeAllRefines = map (show *** show) . concat . map scrapeSingle where-  scrapeSingle (ValueInterface _ _ n _ rs _)    = map ((,) n . vrType) rs-  scrapeSingle (ValueConcrete _ _ n _ rs _ _ _) = map ((,) n . vrType) rs+  scrapeSingle (ValueInterface _ _ n _ _ rs _)    = map ((,) n . vrType) rs+  scrapeSingle (ValueConcrete _ _ n _ _ rs _ _ _) = map ((,) n . vrType) rs   scrapeSingle _ = []  scrapeAllDefines :: [AnyCategory c] -> [(String, String)] scrapeAllDefines = map (show *** show) . concat . map scrapeSingle where-  scrapeSingle (ValueConcrete _ _ n _ _ ds _ _) = map ((,) n . vdType) ds+  scrapeSingle (ValueConcrete _ _ n _ _ _ ds _ _) = map ((,) n . vdType) ds   scrapeSingle _ = []  checkPaired :: Show a => (a -> a -> TrackedErrors ()) -> [a] -> [a] -> TrackedErrors ()@@ -1223,7 +1223,7 @@   parsePair im v (t1,t2) = do     t1' <- readSingle "(string)" t1     t2' <- readSingle "(string)" t2 >>= uncheckedSubValueType (weakLookup im)-    return (PatternMatch v t1' t2')+    return (TypePattern v t1' t2')   weakLookup tm2 n =     case n `Map.lookup` tm2 of          Just t  -> return t
src/Test/TypeInstance.hs view
@@ -927,6 +927,7 @@   trDefinesFilters _ = getDefinesFilters   -- Type5 is concrete, somewhat arbitrarily.   trConcrete _ = \t -> return (t == type5)+  trImmutable _ _ = return False  getParams :: CollectErrorsM m =>   Map.Map CategoryName (Map.Map CategoryName (InstanceParams -> InstanceParams))
src/Test/testfiles/module-cache.txt view
@@ -66,6 +66,10 @@     ]   } ]+extra_paths: [+  "extra1"+  "extra2"+] binaries: [   "/home/project/special/binary1"   "/home/project/special/binary2"
src/Test/testfiles/module-config.txt view
@@ -15,6 +15,10 @@   "/home/project/private-dep1"   "/home/project/private-dep2" ]+extra_paths: [+  "extra1"+  "extra2"+] include_paths: [   "extra1"   "extra2"
src/Types/DefinedCategory.hs view
@@ -210,8 +210,8 @@ mergeInternalInheritance tm d = do   let rs2 = dcRefines d   let ds2 = dcDefines d-  (_,t@(ValueConcrete c ns n ps rs ds vs fs)) <- getConcreteCategory tm (dcContext d,dcName d)-  let c2 = ValueConcrete c ns n ps (rs++rs2) (ds++ds2) vs fs+  (_,t@(ValueConcrete c ns n pg ps rs ds vs fs)) <- getConcreteCategory tm (dcContext d,dcName d)+  let c2 = ValueConcrete c ns n pg ps (rs++rs2) (ds++ds2) vs fs   let tm' = Map.insert (dcName d) c2 tm   let r = CategoryResolver tm'   fm <- getCategoryFilterMap t@@ -220,11 +220,37 @@   noDuplicateRefines [] n rs'   ds' <- mergeDefines r fm (ds++ds2)   noDuplicateDefines [] n ds'-  fs' <- mergeFunctions r tm' pm fm rs' ds' fs-  let c2' = ValueConcrete c ns n ps rs' ds' vs fs'+  pg2 <- fmap concat $ mapCompilerM getRefinesPragmas rs2+  pg3 <- fmap concat $ mapCompilerM getDefinesPragmas ds2+  let fs2 = mergeInternalFunctions fs (dcFunctions d)+  fs' <- mergeFunctions r tm' pm fm rs' ds' fs2+  let c2' = ValueConcrete c ns n (pg++pg2++pg3) ps rs' ds' vs fs'   let tm0 = (dcName d) `Map.delete` tm   checkCategoryInstances tm0 [c2']   return $ Map.insert (dcName d) c2' tm+  where+    getRefinesPragmas rf = do+      (_,t) <- getCategory tm (vrContext rf,tiName $ vrType rf)+      return $ map (prependCategoryPragmaContext $ vrContext rf) $ getCategoryPragmas t+    getDefinesPragmas df = do+      (_,t) <- getCategory tm (vdContext df,diName $ vdType df)+      return $ map (prependCategoryPragmaContext $ vdContext df) $ getCategoryPragmas t+    mergeInternalFunctions fs1 = Map.elems . foldr single (funcMap fs1)+    funcMap = Map.fromList . map (\f -> (sfName f,f))+    single f fm =+      case sfName f `Map.lookup` fm of+           Nothing -> Map.insert (sfName f) f fm+           Just f2 -> Map.insert (sfName f) (ScopedFunction {+               sfContext = sfContext f,+               sfName = sfName f,+               sfType = sfType f,+               sfScope = sfScope f,+               sfArgs = sfArgs f,+               sfReturns = sfReturns f,+               sfParams = sfParams f,+               sfFilters = sfFilters f,+               sfMerges = sfMerges f ++ [f2]+             }) fm  replaceSelfMember :: (Show c, CollectErrorsM m) =>   GeneralInstance -> DefinedMember c -> m (DefinedMember c)
src/Types/Function.hs view
@@ -85,6 +85,7 @@     checkFilterVariance (n,f@(DefinesFilter t)) =       validateDefinesVariance r allVariances Contravariant t <??         ("In filter " ++ show n ++ " " ++ show f)+    checkFilterVariance (_,ImmutableFilter) = return ()     checkArg fa2 ta@(ValueType _ t) = ("In argument " ++ show ta) ??> do       when (isWeakValue ta) $ compilerErrorM "Weak values not allowed as argument types"       validateGeneralInstance r fa2 t
src/Types/Procedure.hs view
@@ -156,6 +156,7 @@   TestProcedure {     tpContext :: [c],     tpName :: FunctionName,+    tpDisableCoverage :: Bool,     tpProcedure :: Procedure c   }   deriving (Show)@@ -173,10 +174,12 @@   RawFailCall String |   IgnoreValues [c] (Expression c) |   Assignment [c] (Positional (Assignable c)) (Expression c) |+  DeferredVariables [c] [Assignable c] |   NoValueExpression [c] (VoidExpression c) |   MarkReadOnly [c] [VariableName] |   MarkHidden [c] [VariableName] |   ValidateRefs [c] [VariableName] |+  ShowVariable [c] ValueType VariableName |   RawCodeLine String   deriving (Show) @@ -193,10 +196,12 @@ getStatementContext (RawFailCall _)         = [] getStatementContext (IgnoreValues c _)      = c getStatementContext (Assignment c _ _)      = c+getStatementContext (DeferredVariables c _) = c getStatementContext (NoValueExpression c _) = c getStatementContext (MarkReadOnly c _)      = c getStatementContext (MarkHidden c _)        = c getStatementContext (ValidateRefs c _)      = c+getStatementContext (ShowVariable _ _ _)    = [] getStatementContext (RawCodeLine _)         = []  data Assignable c =
src/Types/TypeCategory.hs view
@@ -28,6 +28,7 @@   ParamFilter(..),   PassedValue(..),   PatternMatch(..),+  PragmaCategory(..),   ScopedFunction(..),   SymbolScope(..),   ValueDefine(..),@@ -53,15 +54,18 @@   getCategoryParamMap,   getCategoryParamSet,   getCategoryParams,+  getCategoryPragmas,   getCategoryRefines,   getConcreteCategory,   getFilterMap,   getFunctionFilterMap,   getInstanceCategory,   getValueCategory,+  guessesFromFilters,   includeNewTypes,   inferParamTypes,   instanceFromCategory,+  isCategoryImmutable,   isInstanceInterface,   isNoNamespace,   isPrivateNamespace,@@ -77,6 +81,7 @@   noDuplicateRefines,   parsedToFunctionType,   partitionByScope,+  prependCategoryPragmaContext,   replaceSelfFunction,   setCategoryNamespace,   topoSortCategories,@@ -105,6 +110,7 @@     viContext :: [c],     viNamespace :: Namespace,     viName :: CategoryName,+    viPragmas :: [PragmaCategory c],     viParams :: [ValueParam c],     viRefines :: [ValueRefine c],     viFunctions :: [ScopedFunction c]@@ -113,6 +119,7 @@     iiContext :: [c],     iiNamespace :: Namespace,     iiName :: CategoryName,+    iiPragmas :: [PragmaCategory c],     iiParams :: [ValueParam c],     iiFunctions :: [ScopedFunction c]   } |@@ -120,6 +127,7 @@     vcContext :: [c],     vcNamespace :: Namespace,     vcName :: CategoryName,+    vcPragmas :: [PragmaCategory c],     vcParams :: [ValueParam c],     vcRefines :: [ValueRefine c],     vcDefines :: [ValueDefine c],@@ -127,6 +135,20 @@     vcFunctions :: [ScopedFunction c]   } +data PragmaCategory c =+  CategoryImmutable {+    ciContext :: [c]+  }++instance Show c => Show (PragmaCategory c) where+  show (CategoryImmutable c) = "immutable /*" ++ formatFullContext c ++ "*/"++isCategoryImmutable :: PragmaCategory c -> Bool+isCategoryImmutable (CategoryImmutable _) = True++prependCategoryPragmaContext :: [c] -> PragmaCategory c -> PragmaCategory c+prependCategoryPragmaContext c (CategoryImmutable c2) = CategoryImmutable (c++c2)+ formatFullContext :: Show a => [a] -> String formatFullContext cs = intercalate " -> " (map show cs) @@ -136,20 +158,23 @@  instance Show c => Show (AnyCategory c) where   show = format where-    format (ValueInterface cs ns n ps rs fs) =+    format (ValueInterface cs ns n pg ps rs fs) =       "@value interface " ++ show n ++ formatParams ps ++ namespace ns ++ " { " ++ formatContext cs ++ "\n" ++       (intercalate "\n\n" $+         map (\p -> "  " ++ show p) pg ++          map (\r -> "  " ++ formatRefine r) rs ++          map (\f -> formatInterfaceFunc f) fs) ++       "\n}\n"-    format (InstanceInterface cs ns n ps fs) =+    format (InstanceInterface cs ns n pg ps fs) =       "@type interface " ++ show n ++ formatParams ps ++ namespace ns ++ " { " ++ formatContext cs ++       (intercalate "\n\n" $+         map (\p -> "  " ++ show p) pg ++          map (\f -> formatInterfaceFunc f) fs) ++       "\n}\n"-    format (ValueConcrete cs ns n ps rs ds vs fs) =+    format (ValueConcrete cs ns n pg ps rs ds vs fs) =       "concrete " ++ show n ++ formatParams ps ++ namespace ns ++ " { " ++ formatContext cs ++ "\n" ++       (intercalate "\n\n" $+         map (\p -> "  " ++ show p) pg ++          map (\r -> "  " ++ formatRefine r) rs ++          map (\d -> "  " ++ formatDefine d) ds ++          map (\v -> "  " ++ formatValue v) vs ++@@ -174,49 +199,54 @@     formatConcreteFunc f = showFunctionInContext (show (sfScope f) ++ " ") "  " f  getCategoryName :: AnyCategory c -> CategoryName-getCategoryName (ValueInterface _ _ n _ _ _)    = n-getCategoryName (InstanceInterface _ _ n _ _)   = n-getCategoryName (ValueConcrete _ _ n _ _ _ _ _) = n+getCategoryName (ValueInterface _ _ n _ _ _ _)    = n+getCategoryName (InstanceInterface _ _ n _ _ _)   = n+getCategoryName (ValueConcrete _ _ n _ _ _ _ _ _) = n  getCategoryContext :: AnyCategory c -> [c]-getCategoryContext (ValueInterface c _ _ _ _ _)    = c-getCategoryContext (InstanceInterface c _ _ _ _)   = c-getCategoryContext (ValueConcrete c _ _ _ _ _ _ _) = c+getCategoryContext (ValueInterface c _ _ _ _ _ _)    = c+getCategoryContext (InstanceInterface c _ _ _ _ _)   = c+getCategoryContext (ValueConcrete c _ _ _ _ _ _ _ _) = c  getCategoryNamespace :: AnyCategory c -> Namespace-getCategoryNamespace (ValueInterface _ ns _ _ _ _)    = ns-getCategoryNamespace (InstanceInterface _ ns _ _ _)   = ns-getCategoryNamespace (ValueConcrete _ ns _ _ _ _ _ _) = ns+getCategoryNamespace (ValueInterface _ ns _ _ _ _ _)    = ns+getCategoryNamespace (InstanceInterface _ ns _ _ _ _)   = ns+getCategoryNamespace (ValueConcrete _ ns _ _ _ _ _ _ _) = ns +getCategoryPragmas :: AnyCategory c -> [PragmaCategory c]+getCategoryPragmas (ValueInterface _ _ _ pg _ _ _)    = pg+getCategoryPragmas (InstanceInterface _ _ _ pg _ _)   = pg+getCategoryPragmas (ValueConcrete _ _ _ pg _ _ _ _ _) = pg+ setCategoryNamespace :: Namespace -> AnyCategory c -> AnyCategory c-setCategoryNamespace ns (ValueInterface c _ n ps rs fs)      = (ValueInterface c ns n ps rs fs)-setCategoryNamespace ns (InstanceInterface c _ n ps fs)      = (InstanceInterface c ns n ps fs)-setCategoryNamespace ns (ValueConcrete c _ n ps rs ds vs fs) = (ValueConcrete c ns n ps rs ds vs fs)+setCategoryNamespace ns (ValueInterface c _ n pg ps rs fs)      = (ValueInterface c ns n pg ps rs fs)+setCategoryNamespace ns (InstanceInterface c _ n pg ps fs)      = (InstanceInterface c ns n pg ps fs)+setCategoryNamespace ns (ValueConcrete c _ n pg ps rs ds vs fs) = (ValueConcrete c ns n pg ps rs ds vs fs)  getCategoryParams :: AnyCategory c -> [ValueParam c]-getCategoryParams (ValueInterface _ _ _ ps _ _)    = ps-getCategoryParams (InstanceInterface _ _ _ ps _)   = ps-getCategoryParams (ValueConcrete _ _ _ ps _ _ _ _) = ps+getCategoryParams (ValueInterface _ _ _ _ ps _ _)    = ps+getCategoryParams (InstanceInterface _ _ _ _ ps _)   = ps+getCategoryParams (ValueConcrete _ _ _ _ ps _ _ _ _) = ps  getCategoryRefines :: AnyCategory c -> [ValueRefine c]-getCategoryRefines (ValueInterface _ _ _ _ rs _)    = rs-getCategoryRefines (InstanceInterface _ _ _ _ _)    = []-getCategoryRefines (ValueConcrete _ _ _ _ rs _ _ _) = rs+getCategoryRefines (ValueInterface _ _ _ _ _ rs _)    = rs+getCategoryRefines (InstanceInterface _ _ _ _ _ _)    = []+getCategoryRefines (ValueConcrete _ _ _ _ _ rs _ _ _) = rs  getCategoryDefines :: AnyCategory c -> [ValueDefine c]-getCategoryDefines (ValueInterface _ _ _ _ _ _)     = []-getCategoryDefines (InstanceInterface _ _ _ _ _)    = []-getCategoryDefines (ValueConcrete _ _ _ _ _ ds _ _) = ds+getCategoryDefines (ValueInterface _ _ _ _ _ _ _)     = []+getCategoryDefines (InstanceInterface _ _ _ _ _ _)    = []+getCategoryDefines (ValueConcrete _ _ _ _ _ _ ds _ _) = ds  getCategoryFilters :: AnyCategory c -> [ParamFilter c]-getCategoryFilters (ValueInterface _ _ _ _ _ _)     = []-getCategoryFilters (InstanceInterface _ _ _ _ _)    = []-getCategoryFilters (ValueConcrete _ _ _ _ _ _ vs _) = vs+getCategoryFilters (ValueInterface _ _ _ _ _ _ _)     = []+getCategoryFilters (InstanceInterface _ _ _ _ _ _)    = []+getCategoryFilters (ValueConcrete _ _ _ _ _ _ _ vs _) = vs  getCategoryFunctions :: AnyCategory c -> [ScopedFunction c]-getCategoryFunctions (ValueInterface _ _ _ _ _ fs)    = fs-getCategoryFunctions (InstanceInterface _ _ _ _ fs)   = fs-getCategoryFunctions (ValueConcrete _ _ _ _ _ _ _ fs) = fs+getCategoryFunctions (ValueInterface _ _ _ _ _ _ fs)    = fs+getCategoryFunctions (InstanceInterface _ _ _ _ _ fs)   = fs+getCategoryFunctions (ValueConcrete _ _ _ _ _ _ _ _ fs) = fs  instanceFromCategory :: AnyCategory c -> GeneralInstance instanceFromCategory t = singleType $ JustTypeInstance $ TypeInstance n (Positional ps) where@@ -235,6 +265,7 @@   fromDefine (DefinesInstance n ps) = n:(concat $ map fromInstance $ pValues ps)   fromFilter (TypeFilter _ t2)  = fromInstance t2   fromFilter (DefinesFilter t2) = fromDefine t2+  fromFilter ImmutableFilter = []   fromType (ValueType _ t2) = fromInstance t2   fromFunction f = args ++ returns ++ filters2 where     args = concat $ map (fromType . pvType) $ pValues $ sfArgs f@@ -242,15 +273,15 @@     filters2 = concat $ map (fromFilter . pfFilter) $ sfFilters f  isValueInterface :: AnyCategory c -> Bool-isValueInterface (ValueInterface _ _ _ _ _ _) = True+isValueInterface (ValueInterface _ _ _ _ _ _ _) = True isValueInterface _ = False  isInstanceInterface :: AnyCategory c -> Bool-isInstanceInterface (InstanceInterface _ _ _ _ _) = True+isInstanceInterface (InstanceInterface _ _ _ _ _ _) = True isInstanceInterface _ = False  isValueConcrete :: AnyCategory c -> Bool-isValueConcrete (ValueConcrete _ _ _ _ _ _ _ _) = True+isValueConcrete (ValueConcrete _ _ _ _ _ _ _ _ _) = True isValueConcrete _ = False  data Namespace =@@ -361,6 +392,9 @@     trConcrete (CategoryResolver tm) n = do       (_,t) <- getCategory tm ([],n)       return (isValueConcrete t)+    trImmutable (CategoryResolver tm) n = do+      (_,t) <- getCategory tm ([],n)+      return $ any isCategoryImmutable (getCategoryPragmas t)  data SymbolScope =   LocalScope |@@ -399,6 +433,7 @@     subSingleFilter pa (n,(DefinesFilter (DefinesInstance n2 ps2))) = do       ps3 <- mapCompilerM (uncheckedSubInstance $ getValueForParam pa) (pValues ps2)       return (n,(DefinesFilter (DefinesInstance n2 (Positional ps3))))+    subSingleFilter _ f@(_,ImmutableFilter) = return f     assignFilter fa n =       case n `Map.lookup` fa of             (Just x) -> return x@@ -493,7 +528,7 @@   return $ getFilters mirrored $ zip (Set.toList pa) (repeat []) where     pa = Set.fromList $ map vpParam ps     maybeMirror fa@(ParamFilter c p1 (TypeFilter d p2)) = do-      p <- collectFirstM [fmap Just $ matchOnlyLeaf p2,return Nothing]+      p <- tryCompilerM $ matchOnlyLeaf p2       case p of           Just (JustParamName _ p') ->             if p' `Set.member` pa@@ -540,12 +575,12 @@   tm <- declareAllTypes tm0 ts   collectAllM_ (map (checkSingle tm) ts)   where-    checkSingle tm (ValueInterface c _ n _ rs _) = do+    checkSingle tm (ValueInterface c _ n _ _ rs _) = do       let ts2 = map (\r -> (vrContext r,tiName $ vrType r)) rs       is <- mapCompilerM (getCategory tm) ts2       collectAllM_ (map (valueRefinesInstanceError c n) is)       collectAllM_ (map (valueRefinesConcreteError c n) is)-    checkSingle tm (ValueConcrete c _ n _ rs ds _ _) = do+    checkSingle tm (ValueConcrete c _ n _ _ rs ds _ _) = do       let ts2 = map (\r -> (vrContext r,tiName $ vrType r)) rs       let ts3 = map (\d -> (vdContext d,diName $ vdType d)) ds       is1 <- mapCompilerM (getCategory tm) ts2@@ -598,12 +633,12 @@   CategoryMap c -> [AnyCategory c] -> m () checkConnectionCycles tm0 ts = collectAllM_ (map (checker []) ts) where   tm = Map.union tm0 $ Map.fromList $ zip (map getCategoryName ts) ts-  checker us (ValueInterface c _ n _ rs _) = do+  checker us (ValueInterface c _ n _ _ rs _) = do     failIfCycle n c us     let ts2 = map (\r -> (vrContext r,tiName $ vrType r)) rs     is <- mapCompilerM (getValueCategory tm) ts2     collectAllM_ (map (checker (us ++ [n]) . snd) is)-  checker us (ValueConcrete c _ n _ rs _ _ _) = do+  checker us (ValueConcrete c _ n _ _ rs _ _ _) = do     failIfCycle n c us     let ts2 = map (\r -> (vrContext r,tiName $ vrType r)) rs     is <- mapCompilerM (getValueCategory tm) ts2@@ -626,16 +661,16 @@     categoryContext t =       "In " ++ show (getCategoryName t) ++ formatFullContextBrace (getCategoryContext t)     checkBounds t = categoryContext t ??> (getCategoryFilterMap t >>= disallowBoundedParams)-    checkCategory r t@(ValueInterface c _ n ps rs _) = categoryContext t ??> do+    checkCategory r t@(ValueInterface c _ n _ ps rs _) = categoryContext t ??> do       noDuplicates c n ps       let vm = Map.fromList $ map (\p -> (vpParam p,vpVariance p)) ps       collectAllM_ (map (checkRefine r vm) rs)-    checkCategory r t@(ValueConcrete c _ n ps rs ds _ _) = categoryContext t ??> do+    checkCategory r t@(ValueConcrete c _ n _ ps rs ds _ _) = categoryContext t ??> do       noDuplicates c n ps       let vm = Map.fromList $ map (\p -> (vpParam p,vpVariance p)) ps       collectAllM_ (map (checkRefine r vm) rs)       collectAllM_ (map (checkDefine r vm) ds)-    checkCategory _ t@(InstanceInterface c _ n ps _) = categoryContext t ??> do+    checkCategory _ t@(InstanceInterface c _ n _ ps _) = categoryContext t ??> do       noDuplicates c n ps     noDuplicates c n ps = collectAllM_ (map checkCount $ group $ sort $ map vpParam ps) where       checkCount xa@(x:_:_) =@@ -711,18 +746,9 @@            return (ts3 ++ [t] ++ ts4,ta3)     update _ ta _ = return ([],ta) --- For fixed x, if f y x succeeds for some y then x is removed.-mergeObjects :: CollectErrorsM m => (a -> a -> m b) -> [a] -> m [a]-mergeObjects f = merge [] where-  merge cs [] = return cs-  merge cs (x:xs) = do-    ys <- collectFirstM $ map check (cs ++ xs) ++ [return [x]]-    merge (cs ++ ys) xs where-      check x2 = x2 `f` x >> return []- mergeRefines :: (CollectErrorsM m, TypeResolver r) =>   r -> ParamFilters -> [ValueRefine c] -> m [ValueRefine c]-mergeRefines r f = mergeObjects check where+mergeRefines r f = mergeObjectsM check where   check (ValueRefine _ t1@(TypeInstance n1 _)) (ValueRefine _ t2@(TypeInstance n2 _))     | n1 /= n2 = compilerErrorM $ show t1 ++ " and " ++ show t2 ++ " are incompatible"     | otherwise =@@ -732,12 +758,10 @@  mergeDefines :: (CollectErrorsM m, TypeResolver r) =>   r -> ParamFilters -> [ValueDefine c] -> m [ValueDefine c]-mergeDefines r f = mergeObjects check where+mergeDefines r f = mergeObjectsM check where   check (ValueDefine _ t1@(DefinesInstance n1 _)) (ValueDefine _ t2@(DefinesInstance n2 _))     | n1 /= n2 = compilerErrorM $ show t1 ++ " and " ++ show t2 ++ " are incompatible"-    | otherwise = do-      checkDefinesMatch r f t1 t2-      return ()+    | otherwise = checkDefinesMatch r f t2 t1 >> return ()  noDuplicateRefines :: (Show c, CollectErrorsM m) =>   [c] -> CategoryName -> [ValueRefine c] -> m ()@@ -775,12 +799,12 @@       tm <- u       t' <- preMergeSingle tm t       return $ Map.insert (getCategoryName t') t' tm-    preMergeSingle tm (ValueInterface c ns n ps rs fs) = do+    preMergeSingle tm (ValueInterface c ns n pg ps rs fs) = do       rs' <- fmap concat $ mapCompilerM (getRefines tm) rs-      return $ ValueInterface c ns n ps rs' fs-    preMergeSingle tm (ValueConcrete c ns n ps rs ds vs fs) = do+      return $ ValueInterface c ns n pg ps rs' fs+    preMergeSingle tm (ValueConcrete c ns n pg ps rs ds vs fs) = do       rs' <- fmap concat $ mapCompilerM (getRefines tm) rs-      return $ ValueConcrete c ns n ps rs' ds vs fs+      return $ ValueConcrete c ns n pg ps rs' ds vs fs     preMergeSingle _ t = return t     update r t u = do       (ts2,tm) <- u@@ -788,18 +812,19 @@               "In category " ++ show (getCategoryName t) ++                 formatFullContextBrace (getCategoryContext t)       return (ts2 ++ [t'],Map.insert (getCategoryName t') t' tm)-    updateSingle r tm t@(ValueInterface c ns n ps rs fs) = do+    updateSingle r tm t@(ValueInterface c ns n pg ps rs fs) = do       fm <- getCategoryFilterMap t       let pm = getCategoryParamMap t       rs' <- fmap concat $ mapCompilerM (getRefines tm) rs       rs'' <- mergeRefines r fm rs'       noDuplicateRefines c n rs''       checkMerged r fm rs rs''+      pg2 <- fmap concat $ mapCompilerM (getRefinesPragmas tm) rs       -- Only merge from direct parents.       fs' <- mergeFunctions r tm pm fm rs [] fs-      return $ ValueInterface c ns n ps rs'' fs'+      return $ ValueInterface c ns n (pg++pg2) ps rs'' fs'     -- TODO: Remove duplication below and/or have separate tests.-    updateSingle r tm t@(ValueConcrete c ns n ps rs ds vs fs) = do+    updateSingle r tm t@(ValueConcrete c ns n pg ps rs ds vs fs) = do       fm <- getCategoryFilterMap t       let pm = getCategoryParamMap t       rs' <- fmap concat $ mapCompilerM (getRefines tm) rs@@ -808,9 +833,11 @@       checkMerged r fm rs rs''       ds' <- mergeDefines r fm ds       noDuplicateDefines c n ds'+      pg2 <- fmap concat $ mapCompilerM (getRefinesPragmas tm) rs+      pg3 <- fmap concat $ mapCompilerM (getDefinesPragmas tm) ds       -- Only merge from direct parents.       fs' <- mergeFunctions r tm pm fm rs ds fs-      return $ ValueConcrete c ns n ps rs'' ds' vs fs'+      return $ ValueConcrete c ns n (pg++pg2++pg3) ps rs'' ds' vs fs'     updateSingle _ _ t = return t     getRefines tm ra@(ValueRefine c t@(TypeInstance n _)) = do       (_,v) <- getValueCategory tm (c,n)@@ -835,6 +862,12 @@                           "Cannot refine " ++ show ta1 ++ " from inherited " ++ show ta2       return ()     checkConvert _ _ _ _ = return ()+    getRefinesPragmas tm rf = do+      (_,t) <- getCategory tm (vrContext rf,tiName $ vrType rf)+      return $ map (prependCategoryPragmaContext $ vrContext rf) $ getCategoryPragmas t+    getDefinesPragmas tm df = do+      (_,t) <- getCategory tm (vdContext df,diName $ vdType df)+      return $ map (prependCategoryPragmaContext $ vdContext df) $ getCategoryPragmas t  mergeFunctions :: (Show c, CollectErrorsM m, TypeResolver r) =>   r -> CategoryMap c -> ParamValues -> ParamFilters -> [ValueRefine c] ->@@ -1020,28 +1053,34 @@         f' <- replaceSelfFilter self f         return $ ParamFilter c2 n2 f' -data PatternMatch a =-  PatternMatch {-    pmVariance :: Variance,-    pmData :: a,-    pmPattern :: a+data PatternMatch =+  TypePattern {+    tpVariance :: Variance,+    tpData :: ValueType,+    tpPattern :: ValueType+  } |+  DefinesPattern {+    dpData :: TypeInstance,+    dpPattern :: DefinesInstance   } -instance Show a => Show (PatternMatch a) where-  show (PatternMatch Covariant     l r) = show l ++ " -> "  ++ show r-  show (PatternMatch Contravariant l r) = show l ++ " <- "  ++ show r-  show (PatternMatch Invariant     l r) = show l ++ " <-> " ++ show r+instance Show PatternMatch where+  show (TypePattern Covariant     l r) = show l ++ " -> "  ++ show r+  show (TypePattern Contravariant l r) = show l ++ " <- "  ++ show r+  show (TypePattern Invariant     l r) = show l ++ " <-> " ++ show r+  show (DefinesPattern l r) = show l ++ " -> " ++ show r  inferParamTypes :: (CollectErrorsM m, TypeResolver r) =>-  r -> ParamFilters -> ParamValues -> [PatternMatch ValueType] ->+  r -> ParamFilters -> ParamValues -> [PatternMatch] ->   m (MergeTree InferredTypeGuess)-inferParamTypes r f ps ts = do-  ts2 <- mapCompilerM subAll ts-  fmap mergeAll $ mapCompilerM matchPattern ts2 where-    subAll (PatternMatch v t1 t2) = do-      t2' <- uncheckedSubValueType (getValueForParam ps) t2-      return (PatternMatch v t1 t2')-    matchPattern (PatternMatch v t1 t2) = checkValueTypeMatch r f v t1 t2+inferParamTypes r f ps = fmap mergeAll . mapCompilerM single where+  single (TypePattern v t1 t2) = do+    t2' <- uncheckedSubValueType (getValueForParam ps) t2+    checkValueTypeMatch r f v t1 t2'+  single (DefinesPattern t1 (DefinesInstance n ps2)) = do+    ps3 <- trDefines r t1 n+    ps2' <- fmap Positional $ mapCompilerM (uncheckedSubInstance (getValueForParam ps)) $ pValues ps2+    checkDefinesMatch r f (DefinesInstance n ps3) (DefinesInstance n ps2')  data GuessRange a =   GuessRange {@@ -1061,6 +1100,26 @@     guGuesses :: [GuessRange GeneralInstance]   } +guessesFromFilters :: CollectErrorsM m =>+  ParamFilters -> ValueType -> ValueType -> m [PatternMatch]+guessesFromFilters fm (ValueType _ t1) (ValueType _ t2) = tryParam >>= fromFilters where+  tryParam = tryCompilerM $ matchOnlyLeaf t2+  fromFilters (Just (JustParamName _ n)) =+    case n `Map.lookup` fm of+         Just fs -> fmap concat $ mapCompilerM toGuess fs+         Nothing -> return []+  fromFilters _ = return []+  toGuess (TypeFilter FilterRequires t3) =+    return [TypePattern Covariant (ValueType RequiredValue t1) (ValueType RequiredValue t3)]+  toGuess (TypeFilter FilterAllows t3) =+    return [TypePattern Contravariant (ValueType RequiredValue t1) (ValueType RequiredValue t3)]+  toGuess (DefinesFilter t3) = do+    maybeInstance <- tryCompilerM $ matchOnlyLeaf t1+    case maybeInstance of+         Just (JustTypeInstance t) -> return [DefinesPattern t t3]+         _ -> return []+  toGuess _ = return []+ mergeInferredTypes :: (CollectErrorsM m, TypeResolver r) =>   r -> ParamFilters -> ParamFilters -> ParamValues -> MergeTree InferredTypeGuess -> m ParamValues mergeInferredTypes r f ff ps gs0 = do@@ -1132,7 +1191,8 @@            _                 -> tryRangeUnion (ms ++ [g2]) g1 gs     tryRangeUnion _ _ _ = return Nothing     takeBest [gs] = return $ Map.fromList gs-    takeBest gs = "No feasible param guesses found" !!> do+    takeBest [] = compilerErrorM "No feasible param guesses found"+    takeBest gs = "Unable to merge alternative param guesses" !!> do       mapCompilerM_ showAmbiguous (zip ([1..] :: [Int]) gs)       emptyErrorM     showAmbiguous (n,gs) = "Param guess set " ++ show n !!>
src/Types/TypeInstance.hs view
@@ -43,7 +43,9 @@   checkDefinesMatch,   checkGeneralMatch,   checkValueAssignment,+  checkValueTypeImmutable,   checkValueTypeMatch,+  dedupGeneralInstance,   filterLookup,   fixTypeParams,   flipFilter,@@ -78,7 +80,7 @@   validateTypeFilter, ) where -import Control.Monad (when)+import Control.Monad (when,(>=>)) import Data.List (intercalate) import qualified Data.Map as Map import qualified Data.Set as Set@@ -245,13 +247,15 @@   } |   DefinesFilter {     dfType :: DefinesInstance-  }+  } |+  ImmutableFilter   deriving (Eq,Ord)  instance Show TypeFilter where   show (TypeFilter FilterRequires t) = "requires " ++ show t   show (TypeFilter FilterAllows t)   = "allows "   ++ show t   show (DefinesFilter t)             = "defines "  ++ show t+  show ImmutableFilter               = "immutable"  isTypeFilter :: TypeFilter -> Bool isTypeFilter (TypeFilter _ _) = True@@ -265,6 +269,10 @@ isDefinesFilter (DefinesFilter _) = True isDefinesFilter _                 = False +isImmutableFilter :: TypeFilter -> Bool+isImmutableFilter ImmutableFilter = True+isImmutableFilter _               = False+ viewTypeFilter :: ParamName -> TypeFilter -> String viewTypeFilter n f = show n ++ " " ++ show f @@ -300,6 +308,9 @@   -- Returns True if the type is concrete.   trConcrete :: CollectErrorsM m =>     r -> CategoryName -> m Bool+  -- Returns True if the type is immutable.+  trImmutable ::  CollectErrorsM m =>+    r -> CategoryName -> m Bool  data AnyTypeResolver = forall r. TypeResolver r => AnyTypeResolver r @@ -310,6 +321,7 @@   trTypeFilters (AnyTypeResolver r) = trTypeFilters r   trDefinesFilters (AnyTypeResolver r) = trDefinesFilters r   trConcrete (AnyTypeResolver r) = trConcrete r+  trImmutable (AnyTypeResolver r) = trImmutable r  filterLookup :: ErrorContextM m =>   ParamFilters -> ParamName -> m [TypeFilter]@@ -354,6 +366,30 @@   r -> ParamFilters -> ValueType -> ValueType -> m () checkValueAssignment r f t1 t2 = noInferredTypes $ checkValueTypeMatch r f Covariant t1 t2 +checkValueTypeImmutable :: (CollectErrorsM m, TypeResolver r) => r -> ParamFilters -> ValueType -> m ()+checkValueTypeImmutable r f (ValueType _ t) = reduceMergeTree collectAllM_ collectFirstM leafOp t where+  leafOp (JustTypeInstance (TypeInstance n _)) = do+    immutable <- trImmutable r n+    when (not immutable) $ compilerErrorM $ "Category " ++ show n ++ " is not immutable"+  leafOp (JustParamName _ n) = do+    fs <- f `filterLookup` n+    collectFirstM (map (checkFilter n) fs) <!! "No filters imply that " ++ show n ++ " is immutable"+  leafOp (JustInferredType n) = compilerErrorM $ "Inferred type " ++ show n ++ " is not allowed here"+  checkFilter _ ImmutableFilter = return ()+  checkFilter _ (DefinesFilter (DefinesInstance n2 _)) = do+    immutable <- trImmutable r n2+    when (not immutable) $ compilerErrorM $ "Category " ++ show n2 ++ " is not immutable"+  checkFilter _ (TypeFilter FilterRequires t2) = checkValueTypeImmutable r f (ValueType RequiredValue t2)+  checkFilter n ff =+    compilerErrorM $ "Filter " ++ show n ++ " " ++ show ff ++ " does not imply that " ++ show n ++ " is immutable"++dedupGeneralInstance :: (CollectErrorsM m, TypeResolver r) =>+  r -> ParamFilters -> GeneralInstance -> m GeneralInstance+dedupGeneralInstance r f = reduceMergeTree dedupAny dedupAll (return . singleType) where+  dedupAny = fmap mergeAny . (collectAllM >=> mergeObjectsM (checkSingle Contravariant)) -- [A|B], A<-B => remove B+  dedupAll = fmap mergeAll . (collectAllM >=> mergeObjectsM (checkSingle Covariant))     -- [A&B], A->B => remove B+  checkSingle v = checkGeneralMatch r f v+ checkValueTypeMatch :: (CollectErrorsM m, TypeResolver r) =>   r -> ParamFilters -> Variance -> ValueType -> ValueType -> m (MergeTree InferredTypeGuess) checkValueTypeMatch r f v (ValueType r1 t1) (ValueType r2 t2) = result where@@ -370,7 +406,7 @@   GeneralInstance -> GeneralInstance -> m (MergeTree InferredTypeGuess) checkGeneralMatch r f v t1 t2 = message !!> result where   result = do-    ss <- collectFirstM [fmap Just bothSingle,return Nothing]+    ss <- tryCompilerM bothSingle     collectFirstM [matchInferredRight,getMatcher ss]   message     | v == Covariant     = "Cannot convert " ++ show t1 ++ " -> "  ++ show t2@@ -574,23 +610,24 @@   checkFilter t1 (DefinesFilter t2) = do     t1' <- matchOnlyLeaf t1 <!! "Merged type " ++ show t1 ++ " cannot satisfy defines constraint " ++ show t2     checkDefinesFilter t2 t1'+  checkFilter t1 ImmutableFilter = checkValueTypeImmutable r f (ValueType RequiredValue t1)   checkDefinesFilter f2@(DefinesInstance n2 _) (JustTypeInstance t1) = do     ps1' <- trDefines r t1 n2-    checkDefinesMatch r f f2 (DefinesInstance n2 ps1')+    checkDefinesMatch r f (DefinesInstance n2 ps1') f2 >> return ()   checkDefinesFilter f2 (JustParamName _ n1) = do       fs1 <- fmap (map dfType . filter isDefinesFilter) $ f `filterLookup` n1-      (collectFirstM_ $ map (checkDefinesMatch r f f2) fs1) <!!+      (collectFirstM_ $ map (flip (checkDefinesMatch r f) f2) fs1) <!!         "No filters imply " ++ show n1 ++ " defines " ++ show f2   checkDefinesFilter _ (JustInferredType n) =     compilerErrorM $ "Inferred param " ++ show n ++ " is not allowed here"  checkDefinesMatch :: (CollectErrorsM m, TypeResolver r) =>-  r -> ParamFilters -> DefinesInstance -> DefinesInstance -> m ()-checkDefinesMatch r f f2@(DefinesInstance n2 ps2) f1@(DefinesInstance n1 ps1)+  r -> ParamFilters -> DefinesInstance -> DefinesInstance -> m (MergeTree InferredTypeGuess)+checkDefinesMatch r f f1@(DefinesInstance n1 ps1) f2@(DefinesInstance n2 ps2)   | n1 == n2 = do     paired <- processPairs alwaysPair ps1 ps2     variance <- trVariance r n2-    processPairs_ (\v2 (p1,p2) -> checkGeneralMatch r f v2 p1 p2) variance (Positional paired)+    fmap mergeAll $ processPairs (\v2 (p1,p2) -> checkGeneralMatch r f v2 p1 p2) variance (Positional paired)   | otherwise = compilerErrorM $ "Constraint " ++ show f1 ++ " does not imply " ++ show f2  validateGeneralInstance :: (CollectErrorsM m, TypeResolver r) =>@@ -617,6 +654,7 @@   r -> Set.Set ParamName -> TypeFilter -> m () validateTypeFilter r params (TypeFilter _ t)  = validateGeneralInstance r params t validateTypeFilter r params (DefinesFilter t) = validateDefinesInstance r params t+validateTypeFilter _ _      ImmutableFilter   = return ()  validateInstanceVariance :: (CollectErrorsM m, TypeResolver r) =>   r -> ParamVariances -> Variance -> GeneralInstance -> m ()@@ -674,6 +712,7 @@ uncheckedSubFilter replace (DefinesFilter (DefinesInstance n ts)) = do   ts' <- mapCompilerM (uncheckedSubInstance replace) (pValues ts)   return (DefinesFilter (DefinesInstance n (Positional ts')))+uncheckedSubFilter _ ImmutableFilter = return ImmutableFilter  uncheckedSubFilters :: CollectErrorsM m =>   (ParamName -> m GeneralInstance) -> ParamFilters -> m ParamFilters@@ -715,3 +754,4 @@ replaceSelfFilter self (DefinesFilter (DefinesInstance n ts)) = do   ts' <- mapCompilerM (replaceSelfInstance self) (pValues ts)   return (DefinesFilter (DefinesInstance n (Positional ts')))+replaceSelfFilter _ ImmutableFilter = return ImmutableFilter
tests/builtin-types.0rt view
@@ -340,10 +340,10 @@ }  unittest stringBuilder {-  [Append<String>&Build<String>] builder <- String.builder()+  [Append<Formatted>&Build<String>] builder <- String.builder()   \ Testing.checkEquals<?>(builder.build(),"")   \ Testing.checkEquals<?>(builder.append("xyz").build(),"xyz")-  \ Testing.checkEquals<?>(builder.append("123").build(),"xyz123")+  \ Testing.checkEquals<?>(builder.append(123).build(),"xyz123") }  unittest charLessThan {@@ -585,7 +585,7 @@   if (!present(strong(value2))) {     fail("Failed")   }-  [Append<String>&Build<String>] builder <- String.builder().append(require(value1))+  [Append<Formatted>&Build<String>] builder <- String.builder().append(require(value1))   value1 <- empty   if (present(strong(value2))) {     fail("Failed")
tests/cli-tests.sh view
@@ -243,6 +243,12 @@ }  +test_self_offset() {+  do_zeolite -p "$ZEOLITE_PATH" -r tests/self-offset -f+  do_zeolite -p "$ZEOLITE_PATH" -t tests/self-offset+}++ test_templates() {   execute rm -f $ZEOLITE_PATH/tests/templates/Extension_*.cpp   do_zeolite -p "$ZEOLITE_PATH" --templates tests/templates@@ -370,6 +376,20 @@ }  +test_traces() {+  do_zeolite -p "$ZEOLITE_PATH" -r tests/traces -f+  local source_files=("$ZEOLITE_PATH/tests/traces/traces.0rx")+  local expected=$(fgrep -n '// TRACED' "${source_files[@]}" | egrep -o '^[0-9]+' | sort -u)+  local actual=$(do_zeolite --show-traces "$ZEOLITE_PATH/tests/traces" | grep 0rx | sed -r 's/^line ([0-9]+).*/\1/' | sort -u)+  if [[ "$actual" != "$expected" ]]; then+    show_message "Mismatch between expected and actual traced lines:"+    echo "Expected:" $expected 1>&2+    echo "Actual:  " $actual   1>&2+    return 1+  fi+}++ run_all() {   ZEOLITE_PATH=$(do_zeolite --get-path | grep '^/')   echo 1>&2@@ -404,27 +424,29 @@  ALL_TESTS=(   test_bad_path+  test_bad_system_include   test_check_defs+  test_example_hello+  test_example_parser+  test_example_primes+  test_example_random+  test_fast_static+  test_global_include   test_leak_check+  test_module_only+  test_module_only2+  test_module_only3+  test_module_only4+  test_self_offset+  test_show_deps   test_simulate_refs+  test_templates   test_tests_only   test_tests_only2   test_tests_only3   test_tests_only4-  test_module_only-  test_module_only2-  test_module_only3-  test_module_only4+  test_traces   test_warn_public-  test_templates-  test_show_deps-  test_fast_static-  test_bad_system_include-  test_global_include-  test_example_hello-  test_example_parser-  test_example_primes-  test_example_random )  run_all "${ALL_TESTS[@]}" 1>&2
+ tests/defer.0rt view
@@ -0,0 +1,341 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "unused deferred" {+  compiles+}++unittest test {+  Int value <- defer+}+++testcase "assigned deferred" {+  compiles+}++unittest test {+  Int value <- defer+  value <- 1+  \ value+}+++testcase "defer new" {+  error+  require "value"+  require "initialized"+}++unittest test {+  Int value <- defer+  \ value+}+++testcase "defer existing" {+  error+  require "value"+  require "initialized"+}++unittest test {+  Int value <- 1+  value <- defer+  \ value+}+++testcase "multiple deferred" {+  error+  require "value1"+  require "initialized"+  exclude "value0"+}++unittest test {+  Int value1 <- 1+  Int value2, value1 <- defer+  \ value1+}+++testcase "cannot defer _" {+  error+  require "defer"+  require "discard"+}++unittest test {+  _ <- defer+}+++testcase "cannot defer @value member" {+  error+  require "defer"+  require "value"+  require "local"+}++concrete Type {}++define Type {+  @value Int value++  @value call () -> ()+  call () {+    value <- defer+  }+}+++testcase "cannot defer @category member" {+  error+  require "defer"+  require "value"+  require "local"+}++concrete Type {}++define Type {+  @category Int value <- 1++  @value call () -> ()+  call () {+    value <- defer+  }+}+++testcase "cannot defer function argument" {+  error+  require "defer"+  require "value"+  require "read-only"+}++concrete Type {}++define Type {+  @value call (Int) -> ()+  call (value) {+    value <- defer+  }+}+++testcase "cannot defer read-only value" {+  error+  require "defer"+  require "value"+  require "read-only"+}++unittest test {+  Int value <- 1+  $ReadOnly[value]$+  value <- defer+}+++testcase "cannot defer hidden value" {+  error+  require "defer"+  require "value"+  require "hidden"+}++unittest test {+  Int value <- 1+  $Hidden[value]$+  value <- defer+}+++testcase "deferred initialized in if/elif/else" {+  success+}++unittest test {+  Int value <- defer+  if (1 == 2) {+    value <- 1+  } elif (1 == 3) {+    value <- 2+  } else {+    value <- 3+  }+  \ value+}+++testcase "deferred missed in if" {+  error+  require "value"+  require "initialized"+}++unittest test {+  Int value <- defer+  if (1 == 2) {+  } elif (1 == 3) {+    value <- 2+  } else {+    value <- 3+  }+  \ value+}+++testcase "deferred missed in elif" {+  error+  require "value"+  require "initialized"+}++unittest test {+  Int value <- defer+  if (1 == 2) {+    value <- 1+  } elif (1 == 3) {+  } else {+    value <- 3+  }+  \ value+}+++testcase "deferred missed in else" {+  error+  require "value"+  require "initialized"+}++unittest test {+  Int value <- defer+  if (1 == 2) {+    value <- 1+  } elif (1 == 3) {+    value <- 2+  } else {+  }+  \ value+}+++testcase "deferred initialized before cleanup" {+  success+}++unittest test {+  Int value <- defer+  cleanup {+    \ value+  } in value <- 1+}+++testcase "deferred initialized in cleanup" {+  success+}++unittest test {+  Int value <- defer+  cleanup {+    value <- 1+  } in \ empty+  \ value+}+++testcase "deferred initialized in scoped" {+  success+}++unittest test {+  Int value <- defer+  scoped {+    value <- 1+  } in \ value+}+++testcase "deferred not allowed in top-level scoped" {+  error+  require "defer"+  require "scoped"+}++unittest test {+  scoped {+  } in Int value <- defer+}+++testcase "deferred missed before cleanup" {+  error+  require "cleanup"+  require "value"+  require "initialized"+}++unittest test {+  Int value <- defer+  cleanup {+    \ value+  } in {+    if (true) {+      return _+    } else {+      value <- 1+    }+  }+}+++testcase "deferred not checked in condition that jumps" {+  crash+  require "success"+}++unittest test {+  Int value <- defer+  if (true) {+    fail("success")+  } else {+    value <- 1+  }+  \ value+}+++testcase "deferred in nested block does not propagate" {+  success+}++unittest test {+  Int value <- 1+  if (true) {+    value <- defer+  } else {+    value <- defer+  }+  \ value+}
tests/function-calls.0rt view
@@ -562,3 +562,153 @@ @value interface Base16 { get16 () -> (Int) } @value interface Base17 { get17 () -> (Int) } @value interface Base18 { get18 () -> (Int) }+++testcase "multiple mergeable @value functions match from filters" {+  compiles+}++@value interface Type1 {+  execute (Int) -> ()+}++@value interface Type2 {+  execute (Formatted) -> ()+}++concrete Test {}++define Test {+  @type test<#x>+    #x requires Type1+    #x requires Type2+  (#x) -> ()+  test (x) {+    \ x.execute("message")+  }+}+++testcase "multiple mergeable @value functions match from intersection" {+  compiles+}++@value interface Type1 {+  execute (Int) -> ()+}++@value interface Type2 {+  execute (Formatted) -> ()+}++concrete Test {}++define Test {+  @type test ([Type1&Type2]) -> ()+  test (x) {+    \ x.execute("message")+  }+}+++testcase "multiple mergeable @type functions match from filters" {+  compiles+}++@type interface Type1 {+  execute (Int) -> ()+}++@type interface Type2 {+  execute (Formatted) -> ()+}++concrete Test {}++define Test {+  @type test<#x>+    #x defines Type1+    #x defines Type2+  () -> ()+  test () {+    \ #x.execute("message")+  }+}+++testcase "multiple unmergeable @value functions match from filters" {+  error+  require "[Mm]ultiple"+  require "execute"+}++@value interface Type1 {+  execute () -> (Int)+}++@value interface Type2 {+  execute () -> (String)+}++concrete Test {}++define Test {+  @type test<#x>+    #x requires Type1+    #x requires Type2+  (#x) -> ()+  test (x) {+    \ x.execute()+  }+}+++testcase "multiple unmergeable @value functions match from intersection" {+  error+  require "[Mm]ultiple"+  require "execute"+}++@value interface Type1 {+  execute () -> (Int)+}++@value interface Type2 {+  execute () -> (String)+}++concrete Test {}++define Test {+  @type test ([Type1&Type2]) -> ()+  test (x) {+    \ x.execute()+  }+}+++testcase "multiple unmergeable @type functions match from filters" {+  error+  require "[Mm]ultiple"+  require "execute"+}++@type interface Type1 {+  execute () -> (Int)+}++@type interface Type2 {+  execute () -> (String)+}++concrete Test {}++define Test {+  @type test<#x>+    #x defines Type1+    #x defines Type2+  () -> ()+  test () {+    \ #x.execute()+  }+}
tests/function-merging.0rt view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2020 Kevin P. Barry+Copyright 2020-2021 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -129,4 +129,232 @@   call () {     return self   }+}+++testcase "merging of internal inherited functions works" {+  compiles+}++@value interface Base0 {+  call (Int) -> ()+}++@value interface Base1 {+  call (String) -> ()+}++concrete Type {}++define Type {+  refines Base0+  refines Base1++  @value call (Formatted) -> ()+  call (_) {}+}+++testcase "merging of internal/public inherited functions works" {+  compiles+}++@value interface Base0 {+  call (Int) -> ()+}++@value interface Base1 {+  call (String) -> ()+}++concrete Type {+  refines Base0+}++define Type {+  refines Base1++  @value call (Formatted) -> ()+  call (_) {}+}+++testcase "public merge does not interfere with internal merge" {+  compiles+}++@value interface Base0 {+  call (Int) -> ()+}++@value interface Base1 {+  call (String) -> ()+}++concrete Type {+  refines Base0+  refines Base1++  @value call (Formatted) -> ()+}++define Type {+  @value call (any) -> ()+  call (_) {}+}+++testcase "public function used for internal merge" {+  compiles+}++@value interface Base0 {+  call (Int) -> ()+}++@value interface Base1 {+  call (String) -> ()+}++concrete Type {+  @value call (Formatted) -> ()+}++define Type {+  refines Base0+  refines Base1++  call (_) {}+}+++testcase "public merge conflicts with internal merge" {+  error+  require "any"+  require "Formatted"+  require "call"+}++@value interface Base0 {+  call (Int) -> ()+}++@value interface Base1 {+  call (String) -> ()+}++concrete Type {+  refines Base0+  refines Base1++  @value call (any) -> ()+}++define Type {+  @value call (Formatted) -> ()+  call (_) {}+}+++testcase "public merge invalid for internal merge" {+  error+  require "Int"+  require "String"+  require "Float"+  require "call"+}++@value interface Base0 {+  call (Int) -> ()+}++@value interface Base1 {+  call (String) -> ()+}++concrete Type {+  @value call (Float) -> ()+}++define Type {+  refines Base0+  refines Base1++  call (_) {}+}+++testcase "public and internal merges both required" {+  compiles+}++@value interface Base0 {+  call (Int) -> ()+}++@value interface Base1 {+  call (String) -> ()+}++@value interface Base2 {+  call (Base2) -> ()+}++@value interface Base3 {+  call (Base3) -> ()+}++concrete Type {+  refines Base0+  refines Base1++  @value call (Formatted) -> ()+}++define Type {+  refines Base2+  refines Base3++  @value call (any) -> ()+  call (_) {}+}+++testcase "public and internal merges both required but internal missing" {+  error+  require "Base2"+  require "Base3"+  require "Formatted"+  exclude "Int"+  exclude "String"+}++@value interface Base0 {+  call (Int) -> ()+}++@value interface Base1 {+  call (String) -> ()+}++@value interface Base2 {+  call (Base2) -> ()+}++@value interface Base3 {+  call (Base3) -> ()+}++concrete Type {+  refines Base0+  refines Base1++  @value call (Formatted) -> ()+}++define Type {+  refines Base2+  refines Base3++  call (_) {} }
+ tests/immutable.0rt view
@@ -0,0 +1,509 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "immutable concrete protects @value members" {+  error+  require "value"+  require "read-only"+}++concrete Test {+  immutable+}++define Test {+  @value Int value++  @value call () -> ()+  call () {+    value <- 2+  }+}+++testcase "immutable concrete skips @category members" {+  compiles+}++concrete Test {+  immutable+}++define Test {+  @category Int value <- 0++  @value call () -> ()+  call () {+    value <- 2+  }+}+++testcase "immutable is transitively inherited from @value interface" {+  error+  require "value"+  require "read-only"+}++@value interface Base0 {+  immutable+}++@value interface Base1 {+  refines Base0+}++concrete Test {+  refines Base1+}++define Test {+  @value Int value++  @value call () -> ()+  call () {+    value <- 2+  }+}+++testcase "immutable is transitively inherited from internal @value interface" {+  error+  require "value"+  require "read-only"+}++@value interface Base0 {+  immutable+}++@value interface Base1 {+  refines Base0+}++concrete Test {+}++define Test {+  refines Base1++  @value Int value++  @value call () -> ()+  call () {+    value <- 2+  }+}+++testcase "immutable is inherited from @type interface" {+  error+  require "value"+  require "read-only"+}++@type interface Base {+  immutable+}++concrete Test {+  defines Base+}++define Test {+  @value Int value++  @value call () -> ()+  call () {+    value <- 2+  }+}+++testcase "immutable is inherited from internal @type interface" {+  error+  require "value"+  require "read-only"+}++@type interface Base {+  immutable+}++concrete Test {+}++define Test {+  defines Base++  @value Int value++  @value call () -> ()+  call () {+    value <- 2+  }+}+++testcase "@value members must have immutable types" {+  error+  require "immutable"+  require "value0"+  require "value1"+  require "value2"+}++@value interface Foo {}++concrete Test {+  immutable+}++define Test {+  @value          Foo value0+  @value optional Foo value1+  @value weak     Foo value2+}+++testcase "literal built-in types are immutable" {+  compiles+}++concrete Test {+  immutable+}++define Test {+  @value Bool   bool+  @value Char   char+  @value Float  float+  @value Int    int+  @value String string+}+++testcase "intersection requires at least one immutable type" {+  error+  require "immutable"+  require "value"+}++@value interface Foo {}++@value interface Bar {}++concrete Test {+  immutable+}++define Test {+  @value [Foo&Bar] value+}+++testcase "intersection only requires one immutable type" {+  compiles+}++@value interface Foo {}++@value interface Bar {+  immutable+}++concrete Test {+  immutable+}++define Test {+  @value [Foo&Bar] value+}+++testcase "union requires all immutable types" {+  error+  require "immutable"+  require "value"+}++@value interface Foo {}++@value interface Bar {+  immutable+}++concrete Test {+  immutable+}++define Test {+  @value [Foo|Bar] value+}+++testcase "union works with all immutable types" {+  compiles+}++@value interface Foo {+  immutable+}++@value interface Bar {+  immutable+}++concrete Test {+  immutable+}++define Test {+  @value [Foo|Bar] value+}+++testcase "param members must be immutable" {+  error+  require "immutable"+  require "value"+}++concrete Test<#x> {+  immutable+}++define Test {+  @value #x value+}+++testcase "explicit immutable filter is sufficient for param members" {+  compiles+}++concrete Test<#x> {+  immutable++  #x immutable+}++define Test {+  @value #x value+}+++testcase "indirect immutable via requires filter is sufficient for param members" {+  compiles+}++@value interface Foo {+  immutable+}++concrete Test<#x> {+  immutable++  #x requires Foo+}++define Test {+  @value #x value+}+++testcase "indirect immutable via defines filter is sufficient for param members" {+  compiles+}++@type interface Foo {+  immutable+}++concrete Test<#x> {+  immutable++  #x defines Foo+}++define Test {+  @value #x value+}+++testcase "indirect immutable via allows filter is not sufficient for param members" {+  error+  require "immutable"+  require "value"+}++@value interface Foo {+  immutable+}++concrete Test<#x> {+  immutable++  #x allows Foo+}++define Test {+  @value #x value+}+++testcase "param substitution checked for @type calls" {+  error+  require "immutable"+  require "Foo"+}++@value interface Foo {}++concrete Test<#x> {+  #x immutable++  @type call () -> ()+}++define Test {+  call () { }++  @category run () -> ()+  run () {+    \ Test<Foo>.call()+  }+}+++testcase "immutable type allowed for @type calls" {+  compiles+}++@value interface Foo {+  immutable+}++concrete Test<#x> {+  #x immutable++  @type call () -> ()+}++define Test {+  call () { }++  @category run () -> ()+  run () {+    \ Test<Foo>.call()+  }+}+++testcase "param substitution checked for function params" {+  error+  require "immutable"+  require "Foo"+}++@value interface Foo {}++concrete Test {+  @type call<#x>+    #x immutable+  () -> ()+}++define Test {+  call () { }++  @category run () -> ()+  run () {+    \ Test.call<Foo>()+  }+}+++testcase "immutable type allowed for function params" {+  compiles+}++@value interface Foo {+  immutable+}++concrete Test {+  @type call<#x>+    #x immutable+  () -> ()+}++define Test {+  call () { }++  @category run () -> ()+  run () {+    \ Test.call<Foo>()+  }+}+++testcase "immutable type checked for internal refines inheritance" {+  error+  require "immutable"+  require "value"+  exclude "value2"+}++@value interface Base {+  immutable+}++@value interface Foo {}++concrete Test {}++define Test {+  refines Base++  @value Foo value+  @value Test value2+}+++testcase "immutable type checked for internal defines inheritance" {+  error+  require "immutable"+  require "value"+  exclude "value2"+}++@type interface Base {+  immutable+}++@value interface Foo {}++concrete Test {}++define Test {+  defines Base++  @value Foo value+  @value Test value2+}
tests/inference.0rt view
@@ -195,13 +195,21 @@ }  unittest test {-  \ Test.get<?>("value")+  \ Test.get<?>(Type<String>.new()) } +concrete Type<#x> {+  @type new () -> (#self)+}++define Type {+  new () { return #self{ } }+}+ concrete Test {   @type get<#x>     #x allows Formatted-  (#x) -> (#x)+  (Type<#x>) -> (Type<#x>) }  define Test {@@ -219,13 +227,21 @@ }  unittest test {-  \ Test.get<Formatted,?>("value")+  \ Test.get<Formatted,?>(Type<String>.new()) } +concrete Type<#x> {+  @type new () -> (#self)+}++define Type {+  new () { return #self{ } }+}+ concrete Test {   @type get<#y,#x>     #x allows #y-  (#x) -> (#x)+  (Type<#x>) -> (Type<#x>) }  define Test {@@ -244,23 +260,23 @@ }  unittest test {-  \ Test.get<?>(Type<String>.create())+  \ Test.get<?>(Type<String>.new()) }  concrete Type<#x> {-  @type create () -> (Type<#x>)+  @type new () -> (#self) }  define Type {-  create () {-    return Type<#x>{ }+  new () {+    return #self{ }   } }  concrete Test {   @type get<#x>     #x allows Type<#x>-  (#x) -> (#x)+  (Type<#x>) -> (Type<#x>) }  define Test {@@ -388,5 +404,348 @@ define Convert {   create () {     return #self{ }+  }+}+++testcase "dependent implicit param inferred from requires" {+  crash+  require "m e s s a g e"+}++unittest test {+  \ Type.run()+}++concrete Type {+  @type run () -> ()+}++define Type {+  run () {+    \ call<?,?>("message")+    \ call<String,?>("message")+  }++  @type call<#x,#c>+    #c requires Formatted+    #x requires DefaultOrder<#c>+  (#x) -> ()+  call (value) {+    [Append<Formatted>&Build<String>] builder <- String.builder()+    traverse (value.defaultOrder() -> #c char) {+      \ builder.append(char).append(" ")+    }+    fail(builder.build())+  }+}+++testcase "dependent implicit param inferred from defines" {+  crash+  require "String"+}++unittest test {+  \ Type.run()+}++concrete Type {+  @type run () -> ()+}++define Type {+  run () {+    \ call<?,?>("message")+    \ call<String,?>("message")+  }++  @type call<#x,#c>+    #x defines LessThan<#c>+  (#x) -> ()+  call (value) {+    fail(typename<#c>())+  }+}+++testcase "dependent implicit param inferred from allows" {+  crash+  require "String"+}++unittest test {+  \ Type.run()+}++concrete Type {+  @type run () -> ()+}++define Type {+  run () {+    \ call<?,?>("message")+    \ call<String,?>("message")+  }++  @type call<#x,#c>+    #x allows #c+  (#x) -> ()+  call (value) {+    fail(typename<#c>())+  }+}+++testcase "dependent implicit param inferred from reverse allows" {+  crash+  require "String"+}++unittest test {+  \ Type.run()+}++concrete Type {+  @type run () -> ()+}++define Type {+  run () {+    \ call<?,?>("message")+    \ call<String,?>("message")+  }++  @type call<#x,#c>+    #c allows #x+  (#x) -> ()+  call (value) {+    fail(typename<#c>())+  }+}+++testcase "dependent implicit param inferred from reverse requires" {+  crash+  require "String"+}++unittest test {+  \ Type.run()+}++concrete Type {+  @type run () -> ()+}++define Type {+  run () {+    \ call<?,?>("message")+    \ call<String,?>("message")+  }++  @type call<#x,#c>+    #c requires #x+  (#x) -> ()+  call (value) {+    fail(typename<#c>())+  }+}+++testcase "immutable filter excludes guess" {+  error+  require "#x"+  require "guesses"+  require "Formatted"+  require "immutable"+}++unittest test {+    Formatted value <- "message"+    \ Type.call<?>(value)+}++concrete Type {+  @type call<#x>+    #x immutable+    #x requires Formatted+  (#x) -> ()+}++define Type {+  call (value) { }+}+++testcase "filter guess merged with arg guess" {+  crash+  require "String"+  exclude "Formatted"+}++unittest test {+    \ Type.call<?>("message")+}++concrete Type {+  @type call<#x>+    #x requires Formatted+  (#x) -> ()+}++define Type {+  call (value) {+    fail(typename<#x>())+  }+}+++testcase "multiple alternative guesses" {+  error+  require "#x"+  require "guesses"+  require "Int"+  require "String"+}++unittest test {+  \ Test.call<?>(Type.create())+}++@value interface Base1<|#x> {}++@value interface Base2<|#x> {}++concrete Type {+  refines Base1<Int>+  refines Base2<String>++  @type create () -> (Type)+}++define Type {+  create () {+    return #self{ }+  }+}++concrete Test {+  @type call<#x> ([Base1<#x>|Base2<#x>]) -> ()+}++define Test {+  call (_) { }+}+++testcase "unable to merge guesses" {+  error+  require "#x"+  require "guesses"+}++unittest test {+  \ Test.call<?>(Type.create())+}++@value interface Base1<#x> {}++@value interface Base2<#x> {}++concrete Type {+  refines Base1<Int>+  refines Base2<String>++  @type create () -> (Type)+}++define Type {+  create () {+    return #self{ }+  }+}++concrete Test {+  @type call<#x> ([Base1<#x>&Base2<#x>]) -> ()+}++define Test {+  call (_) { }+}+++testcase "variance allows guesses to be merged using meta type" {+  crash+  require "\[Int\|String\]"+}++unittest test {+  \ Test.call<?>(Type.create())+}++@value interface Base1<|#x> {}++@value interface Base2<|#x> {}++concrete Type {+  refines Base1<Int>+  refines Base2<String>++  @type create () -> (Type)+}++define Type {+  create () {+    return #self{ }+  }+}++concrete Test {+  @type call<#x> ([Base1<#x>&Base2<#x>]) -> ()+}++define Test {+  call (_) {+    fail(typename<#x>())+  }+}+++testcase "alternative guess eliminated by filter" {+  crash+  require "Int"+}++unittest test {+  \ Test.call<?>(Type.create())+}++@value interface Base1<|#x> {}++@value interface Base2<|#x> {}++concrete Type {+  refines Base1<Int>+  refines Base2<String>++  @type create () -> (Type)+}++define Type {+  create () {+    return #self{ }+  }+}++concrete Test {+  @type call<#x>+    #x requires AsChar+  ([Base1<#x>|Base2<#x>]) -> ()+}++define Test {+  call (_) {+    fail(typename<#x>())   } }
tests/module-only4/common-source.cpp view
@@ -29,7 +29,7 @@ namespace ZEOLITE_PRIVATE_NAMESPACE { #endif  // ZEOLITE_PRIVATE_NAMESPACE -BoxedValue CreateValue_Type1(S<Type_Type1> parent, const ValueTuple& args);+BoxedValue CreateValue_Type1(S<const Type_Type1> parent, const ValueTuple& args);  struct ExtCategory_Type1 : public Category_Type1 { };@@ -37,16 +37,16 @@ struct ExtType_Type1 : public Type_Type1 {   inline ExtType_Type1(Category_Type1& p, Params<0>::Type params) : Type_Type1(p, params) {} -  ReturnTuple Call_create(const S<TypeInstance>& self, const ParamTuple& params, const ValueTuple& args) {+  ReturnTuple Call_create(const ParamTuple& params, const ValueTuple& args) const {     TRACE_FUNCTION("Type1.create")-    return ReturnTuple(CreateValue_Type1(CreateType_Type1(Params<0>::Type()),+    return ReturnTuple(CreateValue_Type1(PARAM_SELF,       TypeInstance::Call(GetType_Type2(Params<0>::Type()), Function_Type2_create, ParamTuple(), ArgTuple())));   } };  struct ExtValue_Type1 : public Value_Type1 {-  inline ExtValue_Type1(S<Type_Type1> p, const ValueTuple& args)-    : Value_Type1(p), value(args.At(0)) {}+  inline ExtValue_Type1(S<const Type_Type1> p, const ValueTuple& args)+    : Value_Type1(std::move(p)), value(args.At(0)) {}    ReturnTuple Call_get(const ParamTuple& params, const ValueTuple& args) {     TRACE_FUNCTION("Type1.get")@@ -61,13 +61,15 @@   return category; } -S<Type_Type1> CreateType_Type1(Params<0>::Type params) {+S<const Type_Type1> CreateType_Type1(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_Type1(CreateCategory_Type1(), Params<0>::Type()));   return cached; } -BoxedValue CreateValue_Type1(S<Type_Type1> parent, const ValueTuple& args) {-  return BoxedValue::New<ExtValue_Type1>(parent, args);+void RemoveType_Type1(const Params<0>::Type& params) {}++BoxedValue CreateValue_Type1(S<const Type_Type1> parent, const ValueTuple& args) {+  return BoxedValue::New<ExtValue_Type1>(std::move(parent), args); }  #ifdef ZEOLITE_PRIVATE_NAMESPACE@@ -80,7 +82,7 @@ namespace ZEOLITE_PUBLIC_NAMESPACE { #endif  // ZEOLITE_PUBLIC_NAMESPACE -BoxedValue CreateValue_Type3(S<Type_Type3> parent, const ValueTuple& args);+BoxedValue CreateValue_Type3(S<const Type_Type3> parent, const ValueTuple& args);  struct ExtCategory_Type3 : public Category_Type3 { };@@ -88,16 +90,16 @@ struct ExtType_Type3 : public Type_Type3 {   inline ExtType_Type3(Category_Type3& p, Params<0>::Type params) : Type_Type3(p, params) {} -  ReturnTuple Call_create(const S<TypeInstance>& self, const ParamTuple& params, const ValueTuple& args) {+  ReturnTuple Call_create(const ParamTuple& params, const ValueTuple& args) const {     TRACE_FUNCTION("Type3.create")-    return ReturnTuple(CreateValue_Type3(CreateType_Type3(Params<0>::Type()),+    return ReturnTuple(CreateValue_Type3(PARAM_SELF,       TypeInstance::Call(GetType_Type2(Params<0>::Type()), Function_Type2_create, ParamTuple(), ArgTuple())));   } };  struct ExtValue_Type3 : public Value_Type3 {-  inline ExtValue_Type3(S<Type_Type3> p, const ValueTuple& args)-    : Value_Type3(p), value(args.At(0)) {}+  inline ExtValue_Type3(S<const Type_Type3> p, const ValueTuple& args)+    : Value_Type3(std::move(p)), value(args.At(0)) {}    ReturnTuple Call_get(const ParamTuple& params, const ValueTuple& args) {     TRACE_FUNCTION("Type3.get")@@ -112,13 +114,15 @@   return category; } -S<Type_Type3> CreateType_Type3(Params<0>::Type params) {+S<const Type_Type3> CreateType_Type3(const Params<0>::Type& params) {   static const auto cached = S_get(new ExtType_Type3(CreateCategory_Type3(), Params<0>::Type()));   return cached; } -BoxedValue CreateValue_Type3(S<Type_Type3> parent, const ValueTuple& args) {-  return BoxedValue::New<ExtValue_Type3>(parent, args);+void RemoveType_Type3(const Params<0>::Type& params) {}++BoxedValue CreateValue_Type3(S<const Type_Type3> parent, const ValueTuple& args) {+  return BoxedValue::New<ExtValue_Type3>(std::move(parent), args); }  #ifdef ZEOLITE_PUBLIC_NAMESPACE
tests/named-returns.0rt view
@@ -1,5 +1,5 @@ /* ------------------------------------------------------------------------------Copyright 2020 Kevin P. Barry+Copyright 2020-2021 Kevin P. Barry  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.@@ -278,5 +278,59 @@     cleanup {       fail(value)     } in return "message"+  }+}+++testcase "top-level scoped/cleanup sets named return" {+  success+}++unittest withScoped {+  \ Testing.checkEquals<?>(Test.withScoped(),"message")+}++unittest withCleanup {+  \ Testing.checkEquals<?>(Test.withCleanup(),"message")+}++concrete Test {+  @type withScoped () -> (String)+  @type withCleanup () -> (String)+}++define Test {+  withScoped () (value) {+    scoped {+    } in value <- "message"+  }++  withCleanup () (value) {+    cleanup {+    } in value <- "message"+  }+}+++testcase "named return not checked in condition that jumps" {+  crash+  require "success"+}++unittest test {+  \ Test.get()+}++concrete Test {+  @type get () -> (String)+}++define Test {+  get () (message) {+    if (true) {+      fail("success")+    } else {+      message <- "message"+    }   } }
tests/scoped.0rt view
@@ -768,9 +768,8 @@ }  -testcase "default return not allowed in cleanup" {-  error-  require compiler "return.+cleanup"+testcase "default return allowed in cleanup" {+  compiles }  unittest test {
+ tests/self-offset/.zeolite-module view
@@ -0,0 +1,16 @@+root: "../.."+path: "tests/self-offset"+private_deps: [+  "lib/testing"+]+extra_files: [+  category_source {+    source: "tests/self-offset/Extension_Destructor.cpp"+    categories: [Destructor]+  }+  category_source {+    source: "tests/self-offset/Extension_Offset.cpp"+    categories: [Offset]+  }+]+mode: incremental {}
+ tests/self-offset/Extension_Destructor.cpp view
@@ -0,0 +1,68 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++#include "category-source.hpp"+#include "Streamlined_Destructor.hpp"+#include "Category_Destructor.hpp"++#ifdef ZEOLITE_PUBLIC_NAMESPACE+namespace ZEOLITE_PUBLIC_NAMESPACE {+#endif  // ZEOLITE_PUBLIC_NAMESPACE++BoxedValue CreateValue_Destructor(S<const Type_Destructor> parent, const ValueTuple& args);++struct ExtCategory_Destructor : public Category_Destructor {+};++struct ExtType_Destructor : public Type_Destructor {+  inline ExtType_Destructor(Category_Destructor& p, Params<0>::Type params) : Type_Destructor(p, params) {}+  ReturnTuple Call_new(const ParamTuple& params, const ValueTuple& args) const final {+    TRACE_FUNCTION("Destructor.new")+    return ReturnTuple(CreateValue_Destructor(PARAM_SELF, ArgTuple()));+  }+};++struct ExtValue_Destructor : public Value_Destructor {+  inline ExtValue_Destructor(S<const Type_Destructor> p, const ValueTuple& args)+    : Value_Destructor(std::move(p)) {}++  ~ExtValue_Destructor() {+    (void) VAR_SELF;+  }+};++Category_Destructor& CreateCategory_Destructor() {+  static auto& category = *new ExtCategory_Destructor();+  return category;+}++S<const Type_Destructor> CreateType_Destructor(const Params<0>::Type& params) {+  static const auto cached = S_get(new ExtType_Destructor(CreateCategory_Destructor(), Params<0>::Type()));+  return cached;+}++void RemoveType_Destructor(const Params<0>::Type& params) {}++BoxedValue CreateValue_Destructor(S<const Type_Destructor> parent, const ValueTuple& args) {+  return BoxedValue::New<ExtValue_Destructor>(std::move(parent), args);+}++#ifdef ZEOLITE_PUBLIC_NAMESPACE+}  // namespace ZEOLITE_PUBLIC_NAMESPACE+using namespace ZEOLITE_PUBLIC_NAMESPACE;+#endif  // ZEOLITE_PUBLIC_NAMESPACE
+ tests/self-offset/Extension_Offset.cpp view
@@ -0,0 +1,89 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++#include "category-source.hpp"+#include "Streamlined_Offset.hpp"+#include "Category_Offset.hpp"++#ifdef ZEOLITE_PUBLIC_NAMESPACE+namespace ZEOLITE_PUBLIC_NAMESPACE {+#endif  // ZEOLITE_PUBLIC_NAMESPACE++namespace {++struct Base {+  std::string message;+};++}  // namespace++BoxedValue CreateValue_Offset(S<const Type_Offset> parent, const ValueTuple& args);++struct ExtCategory_Offset : public Category_Offset {+};++struct ExtType_Offset : public Type_Offset {+  inline ExtType_Offset(Category_Offset& p, Params<0>::Type params) : Type_Offset(p, params) {}++  ReturnTuple Call_new(const ParamTuple& params, const ValueTuple& args) const final {+    TRACE_FUNCTION("Offset.new")+    return ReturnTuple(CreateValue_Offset(PARAM_SELF, ArgTuple()));+  }+};++// Using virtual will (ideally) change the offset.+struct ExtValue_Offset : public Base, virtual public Value_Offset {+  inline ExtValue_Offset(S<const Type_Offset> p, const ValueTuple& args)+    : Value_Offset(std::move(p)) {}++  ReturnTuple Call_call(const ParamTuple& params, const ValueTuple& args) final {+    TRACE_FUNCTION("Offset.call")+    if ((void*) this == (void*) static_cast<TypeValue*>(this)) {+      message = "same";+    } else {+      message = "different";+    }+    return ReturnTuple(VAR_SELF);+  }++  ReturnTuple Call_get(const ParamTuple& params, const ValueTuple& args) final {+    TRACE_FUNCTION("Offset.get")+    return ReturnTuple(Box_String(message));+  }+};++Category_Offset& CreateCategory_Offset() {+  static auto& category = *new ExtCategory_Offset();+  return category;+}++S<const Type_Offset> CreateType_Offset(const Params<0>::Type& params) {+  static const auto cached = S_get(new ExtType_Offset(CreateCategory_Offset(), Params<0>::Type()));+  return cached;+}++void RemoveType_Offset(const Params<0>::Type& params) {}++BoxedValue CreateValue_Offset(S<const Type_Offset> parent, const ValueTuple& args) {+  return BoxedValue::New<ExtValue_Offset>(std::move(parent), args);+}++#ifdef ZEOLITE_PUBLIC_NAMESPACE+}  // namespace ZEOLITE_PUBLIC_NAMESPACE+using namespace ZEOLITE_PUBLIC_NAMESPACE;+#endif  // ZEOLITE_PUBLIC_NAMESPACE
+ tests/self-offset/README.md view
@@ -0,0 +1,12 @@+# Testing Pointer Offset of `self`++This module tests that the generated C++ correctly accounts for pointer offsets+when C++ extensions use `VAR_SELF`.++To compile and run the test:++```shell+ZEOLITE_PATH=$(zeolite --get-path)+zeolite -p $ZEOLITE_PATH -r tests/self-offset+zeolite -p $ZEOLITE_PATH -t tests/self-offset+```
+ tests/self-offset/offset.0rp view
@@ -0,0 +1,29 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++// Defined in Extension_Destructor.cpp.+concrete Destructor {+  @type new () -> (Destructor)+}++// Defined in Extension_Offset.cpp.+concrete Offset {+  @type new () -> (Offset)+  @value call () -> (#self)+  @value get () -> (String)+}
+ tests/self-offset/offset.0rt view
@@ -0,0 +1,36 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++testcase "check VAR_SELF offset" {+  success+}++unittest test {+  \ Testing.checkEquals<?>(Offset.new().call().get(),"different")+}+++testcase "VAR_SELF not allowed in destructor" {+  crash+  require "VAR_SELF"+  require "Destructor"+}++unittest test {+  \ Destructor.new()+}
tests/simulate-refs/.zeolite-module view
@@ -1,5 +1,11 @@ root: "../.." path: "tests/simulate-refs"+expression_map: [+  expression_macro {+    name: LOCK_VAL+    expression: 10000+  }+] private_deps: [   "lib/container"   "lib/math"
tests/simulate-refs/README.md view
@@ -6,12 +6,15 @@ using state machines that are stochastically interleaved, in an attempt to determine which sequences of events caused the memory leak. -This simulation *has not* been updated to reflect the fixed behavior of `weak`-references, and therefore will show "error" output describing the situation that-previously led to a memory leak.+This simulation has 2 modes: +1. Run `SimulateRefs` with no arguments to use the current behavior. You should+   see no errors, and it should continue to run forever.+2. Run with `broken` as the only argument to use the previous behavior that had+   the memory leak. You should see quite a few errors fairly quickly.+ Note that this simulation code isn't specific to Zeolite; it could have been-written in any other language. The errors output by the simulation are+written in any other language. Any errors output by the simulation are predictions made by the model, rather than actual runtime errors in the program.  To compile and run the simulation:
tests/simulate-refs/main.0rx view
@@ -25,6 +25,12 @@   @category RandomUniform random <- RandomUniform.new(0.0,1.0).setSeed(Realtime.monoSeconds().asInt())    run () {+    Bool useBroken <- Argv.global().size() > 1 && Argv.global().readAt(1) == "broken"++    if (Argv.global().size() > 1 && !useBroken) {+      fail("Pass no args or \"broken\"")+    }+     traverse (Counter.unlimited() -> Int i) {       \ LazyStream<Formatted>.new()           .append("Iteration ")@@ -32,7 +38,7 @@           .append("\n")           .writeTo(SimpleOutput.stderr()) -      if (!runOnce() && (errorLimit <- errorLimit-1) == 0) {+      if (!runOnce(useBroken) && (errorLimit <- errorLimit-1) == 0) {         break       }     }@@ -41,20 +47,37 @@   @type getRoutines (ReferenceState) -> (DefaultOrder<StateMachine>)   getRoutines (state) {     return Vector:create<StateMachine>()-        .append(SharedRoutine.new("shared1",state))-        .append(WeakRoutine.new("weak1",state))-        .append(WeakRoutine.new("weak2",state))+        .append(Routines.newShared("shared1",state))+        .append(Routines.newWeak("weak1",state))+        .append(Routines.newWeak("weak2",state))   } -  @type runOnce () -> (Bool)-  runOnce () {+  @type getRoutinesBroken (ReferenceState) -> (DefaultOrder<StateMachine>)+  getRoutinesBroken (state) {+    return Vector:create<StateMachine>()+        .append(Routines.newSharedBroken("shared1",state))+        .append(Routines.newWeakBroken("weak1",state))+        .append(Routines.newWeakBroken("weak2",state))+  }++  @type runOnce (Bool) -> (Bool)+  runOnce (useBroken) {     ReferenceState state <- ReferenceState.new()-    \ StateExecutor:multiplexStates(getRoutines(state),random)+    DefaultOrder<StateMachine> routines <- defer+    if (useBroken) {+      routines <- getRoutinesBroken(state)+    } else {+      routines <- getRoutines(state)+    }+    \ StateExecutor:multiplexStates(routines,random)      scoped {       optional String error <- state.getError()       if (!present(error) && (state.hasStrong() || state.hasWeak())) {         error <- "expected zero final references"+      }+      if (!present(error) && state.hasLock()) {+        error <- "expected unlocked final reference"       }     } in if (present(error)) {       \ LazyStream<Formatted>.new()
tests/simulate-refs/ref-state.0rp view
@@ -18,6 +18,16 @@  $ModuleOnly$ +@value interface ReferenceMachine {+  immutable++  runWith (String,ReferenceState) -> (optional ReferenceMachine)+}++concrete Run {+  @type then (ReferenceMachine,ReferenceMachine) -> (ReferenceMachine)+}+ concrete ReferenceState {   refines Formatted @@ -28,6 +38,8 @@    @value addStrong () -> (Int)   @value remStrong () -> (Int)+  @value plusStrong (Int) -> (Int)+  @value minusStrong (Int) -> (Int)   @value hasStrong () -> (Bool)    @value addWeak () -> (Int)@@ -35,14 +47,13 @@   @value getWeak () -> (Int)   @value hasWeak () -> (Bool) -  @value addLock () -> (Int)-  @value remLock () -> (Int)-  @value convertLock () -> (Int)-  @type lockMod () -> (Int)+  @value tryLock () -> (Bool)+  @value remLock () -> ()+  @value hasLock () -> (Bool)    @value kill () -> ()   @value isAlive () -> (Bool)-  @value cleanupWeak () -> ()+  @value freeData () -> ()    @value getError () -> (optional String) }
tests/simulate-refs/ref-state.0rx view
@@ -17,27 +17,24 @@ // Author: Kevin P. Barry [ta0kira@gmail.com]  define ReferenceState {-  $ReadOnly[lockMod]$--  @category Int lockMod <- \x1000-   @value Int strongCount   @value Int weakCount+  @value Bool locked   @value Int aliveCleanup-  @value Int weakCleanup-  @value Vector<Formatted> operations+  @value Int dataCleanup+  @value [Append<Formatted>&DefaultOrder<Formatted>] operations    new () {-    return ReferenceState{ 0, 0, 0, 0, Vector:create<Formatted>() }+    return ReferenceState{ 0, 0, false, 0, 0, Vector:create<Formatted>() }   }    addOperation (name) {     \ operations.append(String.builder()         .append(name)         .append("[")-        .append(typename<#x>().formatted())+        .append(typename<#x>())         .append("]: ")-        .append(formatted())+        .append(self)         .build())   } @@ -53,6 +50,14 @@     return (strongCount <- strongCount-1)   } +  plusStrong (n) {+    return (strongCount <- strongCount+n)+  }++  minusStrong (n) {+    return (strongCount <- strongCount-n)+  }+   hasStrong () {     return strongCount > 0   }@@ -73,20 +78,21 @@     return weakCount > 0   } -  addLock () {-    return (strongCount <- strongCount+lockMod)+  tryLock () {+    if (locked) {+      return false+    } else {+      locked <- true+      return true+    }   }    remLock () {-    return (strongCount <- strongCount-lockMod)-  }--  convertLock () {-    return (strongCount <- strongCount-(lockMod-1))+    locked <- false   } -  lockMod () {-    return lockMod+  hasLock () {+    return locked   }    kill () {@@ -97,15 +103,15 @@     return aliveCleanup == 0   } -  cleanupWeak () {-    weakCleanup <- weakCleanup+1+  freeData () {+    dataCleanup <- dataCleanup+1   }    getError () {     if (aliveCleanup > 1) {       return "object freed multiple times"     }-    if (weakCleanup > 1) {+    if (dataCleanup > 1) {       return "counters freed multiple times"     }     if (aliveCleanup > 0 && strongCount > 0) {@@ -119,14 +125,47 @@    formatted () {     return String.builder()-        .append("S: ")-        .append(strongCount.formatted())+        .append("L: ")+        .append(locked)+        .append(" S: ")+        .append(strongCount)         .append(" W: ")-        .append(weakCount.formatted())+        .append(weakCount)         .append(" AC: ")-        .append(aliveCleanup.formatted())-        .append(" WC: ")-        .append(weakCleanup.formatted())+        .append(aliveCleanup)+        .append(" DC: ")+        .append(dataCleanup)         .build()+  }+}++define Run {+  then (first,second) {+    return RunThen.new(first,second)+  }+}++concrete RunThen {+  @type new (ReferenceMachine,ReferenceMachine) -> (ReferenceMachine)+}++define RunThen {+  refines ReferenceMachine++  @value ReferenceMachine first+  @value ReferenceMachine second++  new (first,second) {+    return RunThen{ first, second }+  }++  runWith (name,state) {+    scoped {+      optional ReferenceMachine next <- first.runWith(name,state)+    } in if (present(next)) {+      return RunThen{ require(next), second }+    } else {+      return second+    }   } }
+ tests/simulate-refs/routines.0rp view
@@ -0,0 +1,27 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++$ModuleOnly$++concrete Routines {+  @type newShared (String,ReferenceState) -> (StateMachine)+  @type newWeak   (String,ReferenceState) -> (StateMachine)++  @type newSharedBroken (String,ReferenceState) -> (StateMachine)+  @type newWeakBroken   (String,ReferenceState) -> (StateMachine)+}
+ tests/simulate-refs/routines.0rx view
@@ -0,0 +1,102 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define Routines {+  $ReadOnly[dropShared,dropSharedBroken,dropWeak,sharedThenDrop,sharedThenDropBroken,name,state]$++  refines StateMachine++  @category ReferenceMachine dropWeak <-+      IfElseDecrWeakIsZero.new(+      /*if*/   FreeRef.new(),+      /*else*/ empty)++  @category ReferenceMachine sharedThenDrop <-+      LockRef.new() `Run.then`+      IfElseIncrRefIsOne.new(+      /*if*/   DecrRef.new() `Run.then`+               UnlockRef.new() `Run.then`+               dropWeak,+      /*else*/ UnlockRef.new() `Run.then`+               dropWeak `Run.then`+               dropShared)++  @category ReferenceMachine dropShared <-+      LockRef.new() `Run.then`+      IfElseDecrRefIsZero.new(+      /*if*/   FreeObject.new() `Run.then`+               UnlockRef.new() `Run.then`+               IfElseDecrWeakIsZero.new(+               /*if*/   FreeRef.new(),+               /*else*/ empty),+      /*else*/ UnlockRef.new())++  @category ReferenceMachine sharedThenDropBroken <-+      IfElsePlusLockModLockZero.new(+      /*if*/   MinusLock.new() `Run.then`+               dropWeak,+      /*else*/ MinusLockPlusOne.new() `Run.then`+               dropWeak `Run.then`+               dropSharedBroken)++  @category ReferenceMachine dropSharedBroken <-+      IfElseDecrRefIsZero.new(+      /*if*/   FreeObject.new() `Run.then`+               IfElseDecrWeakIsZero.new(+               /*if*/   FreeRef.new(),+               /*else*/ empty),+      /*else*/ empty)++  @value String name+  @value ReferenceState state+  @value optional ReferenceMachine machine++  newShared (name,state) {+    if (state.addStrong() == 1) {+      \ state.addWeak()+    }+    return #self{ name, state, dropShared }+  }++  newWeak (name,state) {+    \ state.addWeak()+    return #self{ name, state, sharedThenDrop }+  }++  newSharedBroken (name,state) {+    if (state.addStrong() == 1) {+      \ state.addWeak()+    }+    return #self{ name, state, dropSharedBroken }+  }++  newWeakBroken (name,state) {+    \ state.addWeak()+    return #self{ name, state, sharedThenDropBroken }+  }++  transition () {+    if (!present(machine)) {+      return empty+    } elif (present((machine <- require(machine).runWith(name,state)))) {+      return self+    } else {+      return empty+    }+  }+}
− tests/simulate-refs/shared.0rp
@@ -1,24 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--$ModuleOnly$--concrete SharedRoutine {-  @type new (String,ReferenceState) -> (StateMachine)-  @type alreadyStrong (String,ReferenceState) -> (StateMachine)-}
− tests/simulate-refs/shared.0rx
@@ -1,132 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--define SharedRoutine {-  new (name,state) {-    if (state.addStrong() == 1) {-      \ state.addWeak()-    }-    return DropSharedEnter.new(name,state)-  }--  alreadyStrong (name,state) {-    return DropSharedEnter.new(name,state)-  }-}---concrete DropSharedEnter {-  @type new (String,ReferenceState) -> (StateMachine)-}--define DropSharedEnter {-  $ReadOnly[name,state]$--  refines StateMachine--  @value String name-  @value ReferenceState state--  new (name,state) {-    return DropSharedEnter{ name, state }-  }--  transition () {-    \ state.addOperation<#self>(name)-    if (state.remStrong() == 0) {-      return DropSharedFreeObject.new(name,state)-    } else {-      return empty-    }-  }-}---concrete DropSharedFreeObject {-  @type new (String,ReferenceState) -> (StateMachine)-}--define DropSharedFreeObject {-  $ReadOnly[name,state]$--  refines StateMachine--  @value String name-  @value ReferenceState state--  new (name,state) {-    return DropSharedFreeObject{ name, state }-  }--  transition () {-    \ state.addOperation<#self>(name)-    \ state.kill()-    return DropSharedCheckWeak.new(name,state)-  }-}---concrete DropSharedCheckWeak {-  @type new (String,ReferenceState) -> (StateMachine)-}--define DropSharedCheckWeak {-  $ReadOnly[name,state]$--  refines StateMachine--  @value String name-  @value ReferenceState state--  new (name,state) {-    return DropSharedCheckWeak{ name, state }-  }--  transition () {-    \ state.addOperation<#self>(name)-    if (state.remWeak() == 0) {-      return DropSharedFreeWeak.new(name,state)-    } else {-      return empty-    }-  }-}---concrete DropSharedFreeWeak {-  @type new (String,ReferenceState) -> (StateMachine)-}--define DropSharedFreeWeak {-  $ReadOnly[name,state]$--  refines StateMachine--  @value String name-  @value ReferenceState state--  new (name,state) {-    return DropSharedFreeWeak{ name, state }-  }--  transition () {-    \ state.addOperation<#self>(name)-    \ state.cleanupWeak()-    return empty-  }-}
+ tests/simulate-refs/transitions.0rp view
@@ -0,0 +1,67 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++$ModuleOnly$++concrete LockRef {+  @type new () -> (ReferenceMachine)+}++concrete IfElseDecrRefIsZero {+  @type new (optional ReferenceMachine,optional ReferenceMachine) -> (ReferenceMachine)+}++concrete UnlockRef {+  @type new () -> (ReferenceMachine)+}++concrete FreeObject {+  @type new () -> (ReferenceMachine)+}++concrete IfElseDecrWeakIsZero {+  @type new (optional ReferenceMachine,optional ReferenceMachine) -> (ReferenceMachine)+}++concrete FreeRef {+  @type new () -> (ReferenceMachine)+}++concrete IfElseIncrRefIsOne {+  @type new (optional ReferenceMachine,optional ReferenceMachine) -> (ReferenceMachine)+}++concrete DecrRef {+  @type new () -> (ReferenceMachine)+}++concrete DecrWeak {+  @type new () -> (ReferenceMachine)+}++concrete IfElsePlusLockModLockZero {+  @type new (optional ReferenceMachine,optional ReferenceMachine) -> (ReferenceMachine)+}++concrete MinusLockPlusOne {+  @type new () -> (ReferenceMachine)+}++concrete MinusLock {+  @type new () -> (ReferenceMachine)+}
+ tests/simulate-refs/transitions.0rx view
@@ -0,0 +1,188 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++define LockRef {+  refines ReferenceMachine++  new () { return #self{ } }++  runWith (name,state) {+    \ state.addOperation<#self>(name)+    if (state.tryLock()) {+      return empty+    } else {+      return self+    }+  }+}++define IfElseDecrRefIsZero {+  refines ReferenceMachine++  @value optional ReferenceMachine doIf+  @value optional ReferenceMachine doElse++  new (doIf,doElse) { return #self{ doIf, doElse } }++  runWith (name,state) {+    \ state.addOperation<#self>(name)+    if (state.remStrong() == 0) {+      return doIf+    } else {+      return doElse+    }+  }+}++define UnlockRef {+  refines ReferenceMachine++  new () { return #self{ } }++  runWith (name,state) {+    \ state.addOperation<#self>(name)+    \ state.remLock()+    return empty+  }+}++define FreeObject {+  refines ReferenceMachine++  new () { return #self{ } }++  runWith (name,state) {+    \ state.addOperation<#self>(name)+    \ state.kill()+    return empty+  }+}++define IfElseDecrWeakIsZero {+  refines ReferenceMachine++  @value optional ReferenceMachine doIf+  @value optional ReferenceMachine doElse++  new (doIf,doElse) { return #self{ doIf, doElse } }++  runWith (name,state) {+    \ state.addOperation<#self>(name)+    if (state.remWeak() == 0) {+      return doIf+    } else {+      return doElse+    }+  }+}++define FreeRef {+  refines ReferenceMachine++  new () { return #self{ } }++  runWith (name,state) {+    \ state.addOperation<#self>(name)+    \ state.freeData()+    return empty+  }+}++define IfElseIncrRefIsOne {+  refines ReferenceMachine++  @value optional ReferenceMachine doIf+  @value optional ReferenceMachine doElse++  new (doIf,doElse) { return #self{ doIf, doElse } }++  runWith (name,state) {+    \ state.addOperation<#self>(name)+    if (state.addStrong() == 1) {+      return doIf+    } else {+      return doElse+    }+  }+}++define DecrRef {+  refines ReferenceMachine++  new () { return #self{ } }++  runWith (name,state) {+    \ state.addOperation<#self>(name)+    \ state.remStrong()+    return empty+  }+}++define DecrWeak {+  refines ReferenceMachine++  new () { return #self{ } }++  runWith (name,state) {+    \ state.addOperation<#self>(name)+    \ state.remWeak()+    return empty+  }+}++define IfElsePlusLockModLockZero {+  refines ReferenceMachine++  @value optional ReferenceMachine doIf+  @value optional ReferenceMachine doElse++  new (doIf,doElse) { return #self{ doIf, doElse } }++  runWith (name,state) {+    \ state.addOperation<#self>(name)+    if (state.plusStrong($ExprLookup[LOCK_VAL]$) % $ExprLookup[LOCK_VAL]$ == 0) {+      return doIf+    } else {+      return doElse+    }+  }+}++define MinusLockPlusOne {+  refines ReferenceMachine++  new () { return #self{ } }++  runWith (name,state) {+    \ state.addOperation<#self>(name)+    \ state.minusStrong($ExprLookup[LOCK_VAL]$-1)+    return empty+  }+}++define MinusLock {+  refines ReferenceMachine++  new () { return #self{ } }++  runWith (name,state) {+    \ state.addOperation<#self>(name)+    \ state.minusStrong($ExprLookup[LOCK_VAL]$)+    return empty+  }+}
− tests/simulate-refs/weak.0rp
@@ -1,23 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--$ModuleOnly$--concrete WeakRoutine {-  @type new (String,ReferenceState) -> (StateMachine)-}
− tests/simulate-refs/weak.0rx
@@ -1,174 +0,0 @@-/* ------------------------------------------------------------------------------Copyright 2021 Kevin P. Barry--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at--    http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.------------------------------------------------------------------------------ */--// Author: Kevin P. Barry [ta0kira@gmail.com]--define WeakRoutine {-  new (name,state) {-    \ state.addWeak()-    return ToStrongEnter.new(name,state)-  }-}---concrete ToStrongEnter {-  @type new (String,ReferenceState) -> (StateMachine)-}--define ToStrongEnter {-  $ReadOnly[name,state]$--  refines StateMachine--  @value String name-  @value ReferenceState state--  new (name,state) {-    return ToStrongEnter{ name, state }-  }--  transition () {-    \ state.addOperation<#self>(name)-    if (state.addLock() % ReferenceState.lockMod() == 0) {-      return ToStrongDead.new(name,state)-    } else {-      return ToStrongAlive.new(name,state)-    }-  }-}---concrete ToStrongAlive {-  @type new (String,ReferenceState) -> (StateMachine)-}--define ToStrongAlive {-  $ReadOnly[name,state]$--  refines StateMachine--  @value String name-  @value ReferenceState state--  new (name,state) {-    return ToStrongAlive{ name, state }-  }--  transition () {-    \ state.addOperation<#self>(name)-    \ state.convertLock()-    return ToStrongDiscardWeak.new(name,state)-  }-}---concrete ToStrongDiscardWeak {-  @type new (String,ReferenceState) -> (StateMachine)-}--define ToStrongDiscardWeak {-  $ReadOnly[name,state]$--  refines StateMachine--  @value String name-  @value ReferenceState state--  new (name,state) {-    return ToStrongDiscardWeak{ name, state }-  }--  transition () {-    \ state.addOperation<#self>(name)-    \ state.remWeak()-    return SharedRoutine.alreadyStrong(name,state)-  }-}---concrete ToStrongDead {-  @type new (String,ReferenceState) -> (StateMachine)-}--define ToStrongDead {-  $ReadOnly[name,state]$--  refines StateMachine--  @value String name-  @value ReferenceState state--  new (name,state) {-    return ToStrongDead{ name, state }-  }--  transition () {-    \ state.addOperation<#self>(name)-    \ state.remLock()-    return ToStrongCheckWeak.new(name,state)-  }-}---concrete ToStrongCheckWeak {-  @type new (String,ReferenceState) -> (StateMachine)-}--define ToStrongCheckWeak {-  $ReadOnly[name,state]$--  refines StateMachine--  @value String name-  @value ReferenceState state--  new (name,state) {-    return ToStrongCheckWeak{ name, state }-  }--  transition () {-    \ state.addOperation<#self>(name)-    if (state.remWeak() == 0) {-      return ToStrongFreeWeak.new(name,state)-    } else {-      return empty-    }-  }-}---concrete ToStrongFreeWeak {-  @type new (String,ReferenceState) -> (StateMachine)-}--define ToStrongFreeWeak {-  $ReadOnly[name,state]$--  refines StateMachine--  @value String name-  @value ReferenceState state--  new (name,state) {-    return ToStrongFreeWeak{ name, state }-  }--  transition () {-    \ state.addOperation<#self>(name)-    \ state.cleanupWeak()-    return empty-  }-}
tests/templates/private.0rp view
@@ -19,5 +19,9 @@ $ModuleOnly$  concrete Templated2 {+  immutable+   @type create () -> (Templated2)++  @value get () -> (Int) }
+ tests/traces/.zeolite-module view
@@ -0,0 +1,3 @@+root: "../.."+path: "tests/traces"+mode: incremental {}
+ tests/traces/README.md view
@@ -0,0 +1,17 @@+# Testing of Possible Traces++This module does not have an executable code. Instead, it is just meant for+sanity-checking of `zeolite`'s collection of possible trace contexts.++To compile:++```shell+ZEOLITE_PATH=$(zeolite --get-path)+zeolite -p $ZEOLITE_PATH -r tests/traces+```++Note that there *will* be warnings about unreachable code.++You can then examine `zeolite --show-traces $ZEOLITE_PATH/tests/traces` to see+what trace contexts `zeolite` picked up during compilation. Every line that is+commented with `TRACED` should have an entry in `--show-traces`.
+ tests/traces/traces.0rx view
@@ -0,0 +1,47 @@+/* -----------------------------------------------------------------------------+Copyright 2021 Kevin P. Barry++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++    http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.+----------------------------------------------------------------------------- */++// Author: Kevin P. Barry [ta0kira@gmail.com]++concrete Test {}++define Test {+  @type doNotExecute () -> ()+  doNotExecute () {+    String message <- "message"  // TRACED++    if (message.asBool()) {  // TRACED+      fail(message)  // TRACED+      String unreachable <- "unreachable lines should not have a trace entry"+    } elif (false) {  // TRACED+      String alternative <- "elif"  // TRACED+    } else {+      String alternative <- "else"  // TRACED+    }++    scoped {+      Int i <- 0  // TRACED+    } cleanup {+      fail("cleanup")  // TRACED+    } in while (i < 10) {  // TRACED+      String repeat <- "while"  // TRACED+    } update {+      i <- i+1  // TRACED+    }++    String unreachable <- "unreachable lines should not have a trace entry"+  }+}
tests/traverse.0rt view
@@ -77,7 +77,26 @@   } update {     total <- total+i   }-  \ Testing.checkEquals<?>(total,20)+  \ Testing.checkEquals<?>(total,45)+}++unittest overwriteVisibleValue {+  Int i <- 10+  traverse (Counter.zeroIndexed(5) -> i) {+    // i shouldn't be overwritten after the last iteration.+    i <- 0+  }+  \ Testing.checkEquals<?>(i,0)+}++unittest overwriteLocalValue {+  traverse (Counter.zeroIndexed(5) -> Int i) {+    // i should be visible within this scope.+    i <- 0+  } update {+    // i should also be visible here.+    \ Testing.checkEquals<?>(i,0)+  } }  concrete Counter {
tests/typename.0rt view
@@ -21,92 +21,94 @@ }  unittest string {-  Formatted name <- typename<String>()-  if (name.formatted() != "String") {-    fail(name)-  }+  \ Testing.checkEquals<?>(typename<String>().formatted(),"String") }  unittest int {-  Formatted name <- typename<Int>()-  if (name.formatted() != "Int") {-    fail(name)-  }+  \ Testing.checkEquals<?>(typename<Int>().formatted(),"Int") }  unittest char {-  Formatted name <- typename<Char>()-  if (name.formatted() != "Char") {-    fail(name)-  }+  \ Testing.checkEquals<?>(typename<Char>().formatted(),"Char") } -unittest float {-  Formatted name <- typename<Float>()-  if (name.formatted() != "Float") {-    fail(name)-  }+unittest charBuffer {+  \ Testing.checkEquals<?>(typename<CharBuffer>().formatted(),"CharBuffer") } -unittest bool {-  Formatted name <- typename<Bool>()-  if (name.formatted() != "Bool") {-    fail(name)-  }+unittest float {+  \ Testing.checkEquals<?>(typename<Float>().formatted(),"Float") } -unittest formatted {-  Formatted name <- typename<Formatted>()-  if (name.formatted() != "Formatted") {-    fail(name)-  }+unittest bool {+  \ Testing.checkEquals<?>(typename<Bool>().formatted(),"Bool") }  unittest anyType {-  Formatted name <- typename<any>()-  if (name.formatted() != "any") {-    fail(name)-  }+  \ Testing.checkEquals<?>(typename<any>().formatted(),"any") }  unittest allType {-  Formatted name <- typename<all>()-  if (name.formatted() != "all") {-    fail(name)-  }+  \ Testing.checkEquals<?>(typename<all>().formatted(),"all") } -unittest intersect {-  Formatted name <- typename<[String&Int]>()-  if (name.formatted() != "[Int&String]") {-    fail(name)+unittest intersectDedup {+  \ Testing.checkEquals<?>(typename<[AsBool&Char&Formatted&Int]>().formatted(),+                           typename<[Char&Int]>().formatted())+}++unittest unionDedup {+  \ Testing.checkEquals<?>(typename<[AsBool|Char|Formatted|Int]>().formatted(),+                           typename<[AsBool|Formatted]>().formatted())+}++unittest param {+  \ Testing.checkEquals<?>(Wrapped:getTypename<String,Value<Int>>().formatted(),+                           "Type<String,Value<Int>>")+}++unittest intersectParam {+  String text <- Wrapped:getTypenameIntersect<Formatted,Int>().formatted()+  if (text != "[Formatted&Int]") {+    fail(text)   } } -unittest union {-  Formatted name <- typename<[String|Int]>()-  if (name.formatted() != "[Int|String]") {-    fail(name)+unittest unionParam {+  String text <- Wrapped:getTypenameUnion<Formatted,Int>().formatted()+  if (text != "[Formatted|Int]") {+    fail(text)   } } +unittest intersectDedupParam {+  \ Testing.checkEquals<?>(Wrapped:getTypenameIntersect<Int,Int>().formatted(),"Int")+}++unittest unionDedupParam {+  \ Testing.checkEquals<?>(Wrapped:getTypenameUnion<Int,Int>().formatted(),"Int")+}+ @value interface Value<#x> {}  @value interface Type<#x,#y> {} +concrete Wrapped {+  @category getTypename<#x,#y> () -> (Formatted)+  @category getTypenameIntersect<#x,#y> () -> (Formatted)+  @category getTypenameUnion<#x,#y> () -> (Formatted)+}+ define Wrapped {   getTypename () {     return typename<Type<#x,#y>>()   }-} -concrete Wrapped {-  @category getTypename<#x,#y> () -> (Formatted)-}+  getTypenameIntersect () {+    return typename<[#x&#y]>()+  } -unittest param {-  Formatted name <- Wrapped:getTypename<String,Value<Int>>()-  if (name.formatted() != "Type<String,Value<Int>>") {-    fail(name)+  getTypenameUnion () {+    return typename<[#x|#y]>()   } }
zeolite-lang.cabal view
@@ -1,7 +1,7 @@ cabal-version:       2.2  name:                zeolite-lang-version:             0.19.0.0+version:             0.20.0.0 synopsis:            Zeolite is a statically-typed, general-purpose programming language.  description:@@ -97,38 +97,41 @@                      lib/container/README.md,                      lib/container/.zeolite-module,                      lib/container/*.0rp,-                     lib/container/*.0rt,-                     lib/container/*.0rx,+                     lib/container/src/*.0rp,+                     lib/container/src/*.0rx,+                     lib/container/test/*.0rt,                      lib/container/src/*.cpp,                      lib/file/README.md,                      lib/file/.zeolite-module,                      lib/file/*.0rp,-                     lib/file/*.0rt,-                     lib/file/*.0rx,+                     lib/file/src/*.0rx,                      lib/file/src/*.cpp,+                     lib/file/test/*.0rt,                      lib/math/README.md,                      lib/math/.zeolite-module,                      lib/math/*.0rp,-                     lib/math/*.0rx,-                     lib/math/*.0rt,+                     lib/math/src/*.0rp,+                     lib/math/src/*.0rx,                      lib/math/src/*.cpp,+                     lib/math/test/*.0rt,                      lib/testing/README.md,                      lib/testing/.zeolite-module,                      lib/testing/*.0rp,-                     lib/testing/*.0rt,-                     lib/testing/*.0rx,+                     lib/testing/src/*.0rx,+                     lib/testing/test/*.0rt,                      lib/thread/README.md,                      lib/thread/.zeolite-module,                      lib/thread/*.0rp,-                     lib/thread/*.0rt,-                     lib/thread/*.0rx,+                     lib/thread/src/*.0rp,+                     lib/thread/src/*.0rx,                      lib/thread/src/*.cpp,+                     lib/thread/test/*.0rt,                      lib/util/README.md,                      lib/util/.zeolite-module,                      lib/util/*.0rp,-                     lib/util/*.0rt,-                     lib/util/*.0rx,+                     lib/util/src/*.0rx,                      lib/util/src/*.cpp,+                     lib/util/test/*.0rt,                      tests/.zeolite-module,                      tests/*.0rp,                      tests/*.0rt,@@ -164,6 +167,11 @@                      tests/module-only4/*.0rp,                      tests/module-only4/*.0rt,                      tests/module-only4/*.0rx,+                     tests/self-offset/README.md,+                     tests/self-offset/.zeolite-module,+                     tests/self-offset/*.cpp,+                     tests/self-offset/*.0rp,+                     tests/self-offset/*.0rt,                      tests/simulate-refs/.zeolite-module,                      tests/simulate-refs/*.0rp,                      tests/simulate-refs/*.0rx,@@ -188,6 +196,9 @@                      tests/tests-only4/.zeolite-module,                      tests/tests-only4/*.cpp,                      tests/tests-only4/*.0rp,+                     tests/traces/README.md,+                     tests/traces/.zeolite-module,+                     tests/traces/*.0rx,                      tests/visibility/.zeolite-module,                      tests/visibility/*.0rp,                      tests/visibility/*.0rx,