From b90b94feb8e27ded4b5f28a86f12df8ac2b773ec Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Fri, 24 Oct 2025 14:19:58 -0500 Subject: [PATCH] merge in kiss-tools --- .github/workflows/kiss-tools-test.yml | 17 ++++ .gitignore | 1 + kiss-tools-build.hxml | 4 + kiss-tools-test.sh | 4 + src/kiss/Kiss.hx | 2 + src/kiss/KissFrontend.hx | 7 ++ src/kiss/Macros.hx | 3 + src/kiss_tools/FuzzyJson.kiss | 33 ++++++++ src/kiss_tools/JsonArray.hx | 10 +++ src/kiss_tools/JsonArray.kiss | 15 ++++ src/kiss_tools/JsonBool.hx | 7 ++ src/kiss_tools/JsonBool.kiss | 4 + src/kiss_tools/JsonFloat.hx | 7 ++ src/kiss_tools/JsonFloat.kiss | 4 + src/kiss_tools/JsonInt.hx | 7 ++ src/kiss_tools/JsonInt.kiss | 4 + src/kiss_tools/JsonMap.hx | 23 +++++ src/kiss_tools/JsonMap.kiss | 48 +++++++++++ src/kiss_tools/JsonString.hx | 8 ++ src/kiss_tools/JsonString.kiss | 7 ++ src/kiss_tools/KeyShortcutHandler.hx | 21 +++++ src/kiss_tools/KeyShortcutHandler.kiss | 111 +++++++++++++++++++++++++ src/kiss_tools/OBSTools.hx | 6 ++ src/kiss_tools/OBSTools.kiss | 58 +++++++++++++ src/kiss_tools/RefactorUtil.kiss | 22 +++++ src/kiss_tools/TimerWithPause.hx | 15 ++++ src/kiss_tools/TimerWithPause.kiss | 57 +++++++++++++ src/test/KissToolsMain.hx | 8 ++ src/test/KissToolsMain_.kiss | 43 ++++++++++ src/test/TestJsonable.hx | 7 ++ src/test/TestJsonable.kiss | 5 ++ test.sh | 2 +- test/fuzzy.json | 5 ++ test/fuzzy2.json | 3 + test/map-end.json | 5 ++ test/map-start.json | 4 + 36 files changed, 586 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/kiss-tools-test.yml create mode 100644 kiss-tools-build.hxml create mode 100755 kiss-tools-test.sh create mode 100644 src/kiss_tools/FuzzyJson.kiss create mode 100644 src/kiss_tools/JsonArray.hx create mode 100644 src/kiss_tools/JsonArray.kiss create mode 100644 src/kiss_tools/JsonBool.hx create mode 100644 src/kiss_tools/JsonBool.kiss create mode 100644 src/kiss_tools/JsonFloat.hx create mode 100644 src/kiss_tools/JsonFloat.kiss create mode 100644 src/kiss_tools/JsonInt.hx create mode 100644 src/kiss_tools/JsonInt.kiss create mode 100644 src/kiss_tools/JsonMap.hx create mode 100644 src/kiss_tools/JsonMap.kiss create mode 100644 src/kiss_tools/JsonString.hx create mode 100644 src/kiss_tools/JsonString.kiss create mode 100644 src/kiss_tools/KeyShortcutHandler.hx create mode 100644 src/kiss_tools/KeyShortcutHandler.kiss create mode 100644 src/kiss_tools/OBSTools.hx create mode 100644 src/kiss_tools/OBSTools.kiss create mode 100644 src/kiss_tools/RefactorUtil.kiss create mode 100644 src/kiss_tools/TimerWithPause.hx create mode 100644 src/kiss_tools/TimerWithPause.kiss create mode 100644 src/test/KissToolsMain.hx create mode 100644 src/test/KissToolsMain_.kiss create mode 100644 src/test/TestJsonable.hx create mode 100644 src/test/TestJsonable.kiss create mode 100644 test/fuzzy.json create mode 100644 test/fuzzy2.json create mode 100644 test/map-end.json create mode 100644 test/map-start.json diff --git a/.github/workflows/kiss-tools-test.yml b/.github/workflows/kiss-tools-test.yml new file mode 100644 index 0000000..45cea08 --- /dev/null +++ b/.github/workflows/kiss-tools-test.yml @@ -0,0 +1,17 @@ +name: CI + +on: [push, pull_request] + +jobs: + test: + runs-on: [ubuntu-latest] + steps: + - uses: actions/checkout@v3 + + # lix + - uses: https://k7izh9.gitea.cloud/kiss-lang/setup-lix@1.0.2 + with: + lix-version: 15.12.0 + + - run: lix download + - run: ./kiss-tools-test.sh \ No newline at end of file diff --git a/.gitignore b/.gitignore index adc4f6b..e50c629 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ bin/ .DS_Store *.bak .kissCache.json +haxe_libraries/kiss.hxml diff --git a/kiss-tools-build.hxml b/kiss-tools-build.hxml new file mode 100644 index 0000000..3067a9b --- /dev/null +++ b/kiss-tools-build.hxml @@ -0,0 +1,4 @@ +-lib kiss +-cp src +--main test.KissToolsMain +--interp \ No newline at end of file diff --git a/kiss-tools-test.sh b/kiss-tools-test.sh new file mode 100755 index 0000000..d3cc4db --- /dev/null +++ b/kiss-tools-test.sh @@ -0,0 +1,4 @@ +#! /bin/bash + +lix dev kiss . +haxe kiss-tools-build.hxml diff --git a/src/kiss/Kiss.hx b/src/kiss/Kiss.hx index 013ce90..82b725c 100644 --- a/src/kiss/Kiss.hx +++ b/src/kiss/Kiss.hx @@ -77,6 +77,8 @@ typedef KissState = { #end class Kiss { + public static var loadFromAliases = ["kiss-tools" => "kiss"]; + public static var defaultCallAliases:Map = [ // TODO some of these probably won't conflict, and could be passed as functions for a number of reasons "print" => Symbol("Prelude.print"), diff --git a/src/kiss/KissFrontend.hx b/src/kiss/KissFrontend.hx index fba59a4..01fee3d 100644 --- a/src/kiss/KissFrontend.hx +++ b/src/kiss/KissFrontend.hx @@ -6,6 +6,7 @@ import haxe.macro.Expr; import haxe.macro.Context; import haxe.macro.Expr.ImportMode; using StringTools; +import haxe.io.Path; class KissFrontend implements FrontendPlugin { @@ -19,8 +20,14 @@ class KissFrontend implements FrontendPlugin { public function extensions() { return [extension].iterator(); } + static var filesParsed:Map = []; public function parse(file:String, context:FrontendContext):Void { + if(!Path.isAbsolute(file)){ + file = Path.join([Sys.getCwd(), file]); + } + if(filesParsed.exists(file)) return; + filesParsed[file] = true; var files = [file]; if (dslFile.length > 0) { diff --git a/src/kiss/Macros.hx b/src/kiss/Macros.hx index 58536d8..79c46a6 100644 --- a/src/kiss/Macros.hx +++ b/src/kiss/Macros.hx @@ -54,6 +54,9 @@ class Macros { k.doc("loadFrom", 2, 2, '(loadFrom "" "")'); macros["loadFrom"] = (wholeExp:ReaderExp, args:Array, k:KissState) -> { var libName = compileTimeResolveToString("The first argument to (loadFrom...)", "a haxe library's name", args[0], k); + if (Kiss.loadFromAliases.exists(libName)) { + libName = Kiss.loadFromAliases[libName]; + } var libPath = Helpers.libPath(libName); var otherKissFile = compileTimeResolveToString("The second argument to (loadFrom...)", "a .kiss file path", args[1], k); Kiss.load(otherKissFile, k, libPath, false, wholeExp); diff --git a/src/kiss_tools/FuzzyJson.kiss b/src/kiss_tools/FuzzyJson.kiss new file mode 100644 index 0000000..6672092 --- /dev/null +++ b/src/kiss_tools/FuzzyJson.kiss @@ -0,0 +1,33 @@ +(defMacroVar fuzzyJsons (new StringMap)) + +(defMacroFunction _loadFuzzyJson [key file] + (unless (fuzzyJsons.exists key) + (dictSet fuzzyJsons key (new StringMap))) + (let [map (dictGet fuzzyJsons key) + json (Json.parse (File.getContent file))] + (doFor key (Reflect.fields json) + (if (map.exists key) + // mark the key to throw an error if accessed, + // because it is a duplicate + (dictSet map key null) + (dictSet map key (Reflect.field json key)))))) + +(defMacro loadFuzzyJson [key file] + (_loadFuzzyJson (eval key) (eval file)) + `{}) + +(defMacroFunction _getFuzzyJson [whichJson key take] + (let [json (dictGet fuzzyJsons whichJson) + bm (FuzzyMapTools.bestMatch json key) + val (dictGet json bm)] + (when take (json.remove bm)) + val)) + +(defMacro getFuzzyJson [whichJson key &opt take &builder b] + (let [value (_getFuzzyJson (eval whichJson) (eval key) take)] + (if !(= null value) + `(haxe.Json.parse ,(Json.stringify value)) + (throw (+ (eval key) " has a duplicate definition between multiple files of fuzzy json " (eval whichJson)))))) + +(defMacro takeFuzzyJson [whichJson key] + `(getFuzzyJson ,whichJson ,key true)) \ No newline at end of file diff --git a/src/kiss_tools/JsonArray.hx b/src/kiss_tools/JsonArray.hx new file mode 100644 index 0000000..f3fd117 --- /dev/null +++ b/src/kiss_tools/JsonArray.hx @@ -0,0 +1,10 @@ +package kiss_tools; + +import kiss.Prelude; +import kiss.List; +import kiss_tools.JsonMap; +import haxe.Json; +import haxe.DynamicAccess; + +@:build(kiss.Kiss.build()) +class JsonArray> {} diff --git a/src/kiss_tools/JsonArray.kiss b/src/kiss_tools/JsonArray.kiss new file mode 100644 index 0000000..fd85ebf --- /dev/null +++ b/src/kiss_tools/JsonArray.kiss @@ -0,0 +1,15 @@ +(defNew [&prop :Array elements + &prop :T defaultVal]) + +(method parse [:String representation] + (let [:Array arr (Json.parse representation)] + (new JsonArray + (for elem arr + (defaultVal.parse elem)) + defaultVal))) + +(method stringify [] + (Json.stringify (for elem elements (elem.stringify)))) + +(method iterator [] + (elements.iterator)) \ No newline at end of file diff --git a/src/kiss_tools/JsonBool.hx b/src/kiss_tools/JsonBool.hx new file mode 100644 index 0000000..4965dc2 --- /dev/null +++ b/src/kiss_tools/JsonBool.hx @@ -0,0 +1,7 @@ +package kiss_tools; + +import kiss.Prelude; +import kiss.List; + +@:build(kiss.Kiss.build()) +class JsonBool {} diff --git a/src/kiss_tools/JsonBool.kiss b/src/kiss_tools/JsonBool.kiss new file mode 100644 index 0000000..fea03b9 --- /dev/null +++ b/src/kiss_tools/JsonBool.kiss @@ -0,0 +1,4 @@ +(defNew [&prop :Bool value]) + +(method stringify [] (if value "true" "false")) +(method parse [:String data] (new JsonBool ?(= data "true"))) \ No newline at end of file diff --git a/src/kiss_tools/JsonFloat.hx b/src/kiss_tools/JsonFloat.hx new file mode 100644 index 0000000..6d9482e --- /dev/null +++ b/src/kiss_tools/JsonFloat.hx @@ -0,0 +1,7 @@ +package kiss_tools; + +import kiss.Prelude; +import kiss.List; + +@:build(kiss.Kiss.build()) +class JsonFloat {} diff --git a/src/kiss_tools/JsonFloat.kiss b/src/kiss_tools/JsonFloat.kiss new file mode 100644 index 0000000..5c3c00d --- /dev/null +++ b/src/kiss_tools/JsonFloat.kiss @@ -0,0 +1,4 @@ +(defNew [&prop :Float value]) + +(method stringify [] "$value") +(method parse [:String data] (new JsonFloat (Std.parseFloat data))) \ No newline at end of file diff --git a/src/kiss_tools/JsonInt.hx b/src/kiss_tools/JsonInt.hx new file mode 100644 index 0000000..760b651 --- /dev/null +++ b/src/kiss_tools/JsonInt.hx @@ -0,0 +1,7 @@ +package kiss_tools; + +import kiss.Prelude; +import kiss.List; + +@:build(kiss.Kiss.build()) +class JsonInt {} diff --git a/src/kiss_tools/JsonInt.kiss b/src/kiss_tools/JsonInt.kiss new file mode 100644 index 0000000..15aafcd --- /dev/null +++ b/src/kiss_tools/JsonInt.kiss @@ -0,0 +1,4 @@ +(defNew [&prop :Int value]) + +(method stringify [] "$value") +(method parse [:String data] (new JsonInt (Std.parseInt data))) \ No newline at end of file diff --git a/src/kiss_tools/JsonMap.hx b/src/kiss_tools/JsonMap.hx new file mode 100644 index 0000000..8f8a1de --- /dev/null +++ b/src/kiss_tools/JsonMap.hx @@ -0,0 +1,23 @@ +// TODO bug in kiss-vscode new class: it takes the first line of the currently open file, +// instead of intelligently picking a package declaration +package kiss_tools; + +import kiss.Prelude; +import kiss.List; + +import haxe.ds.Map; +import haxe.Json; +import haxe.DynamicAccess; +import sys.io.File; +import sys.FileSystem; +using StringTools; + +typedef Jsonable = { + function stringify():String; + function parse(s:String):T; +} + +typedef JsonStringMap = JsonMap; + +@:build(kiss.Kiss.build()) +class JsonMap> {} diff --git a/src/kiss_tools/JsonMap.kiss b/src/kiss_tools/JsonMap.kiss new file mode 100644 index 0000000..96c2309 --- /dev/null +++ b/src/kiss_tools/JsonMap.kiss @@ -0,0 +1,48 @@ +(method _parseFrom [:String representation] + (let [:DynamicAccess json (Json.parse representation)] + (doFor =>key representation json + (dictSet m key (defaultVal.parse representation))))) + +(method stringify [] + (let [json + (Json.stringify + (for =>k v m =>k (v.stringify)) + null + "\t") + lines + (json.split "\n")] + (lines.shift) // { + (lines.pop) // } + (+ "{\n" (.join (sort (for line lines (if (line.endsWith ",") (substr line 0 -1) line))) ",\n") "\n}"))) + +(defNew [&prop :String jsonPath &prop :T defaultVal] + [:Map m (new Map)] + + (unless (FileSystem.exists jsonPath) + (File.saveContent jsonPath "{}")) + + (try + (_parseFrom (File.getContent jsonPath)) + (catch [:String message] + (throw "JsonParser error loading ${jsonPath}: ${message}")))) + +(method put [:String key :T value] + (dictSet m key value) + (File.saveContent jsonPath (stringify))) + +(method get [:String key] + (unless (m.exists key) + (put key defaultVal)) + (dictGet m key)) + +(method exists [:String key] + (m.exists key)) + +(method iterator [] + (m.iterator)) + +(method keys [] + (m.keys)) + +(method keyValueIterator [] + (m.keyValueIterator)) \ No newline at end of file diff --git a/src/kiss_tools/JsonString.hx b/src/kiss_tools/JsonString.hx new file mode 100644 index 0000000..320b952 --- /dev/null +++ b/src/kiss_tools/JsonString.hx @@ -0,0 +1,8 @@ +package kiss_tools; + +import kiss.Prelude; +import kiss.List; +import haxe.Json; + +@:build(kiss.Kiss.build()) +class JsonString {} diff --git a/src/kiss_tools/JsonString.kiss b/src/kiss_tools/JsonString.kiss new file mode 100644 index 0000000..9d996f1 --- /dev/null +++ b/src/kiss_tools/JsonString.kiss @@ -0,0 +1,7 @@ +(defNew [&prop :String value]) + +(method parse [:String representation] + (new JsonString (Json.parse representation))) + +(method stringify [] + (Json.stringify value)) \ No newline at end of file diff --git a/src/kiss_tools/KeyShortcutHandler.hx b/src/kiss_tools/KeyShortcutHandler.hx new file mode 100644 index 0000000..3dfb4d5 --- /dev/null +++ b/src/kiss_tools/KeyShortcutHandler.hx @@ -0,0 +1,21 @@ +package kiss_tools; + +import kiss.Prelude; +import kiss.Stream; +import kiss.List; + +typedef PrefixMap = Map>; +typedef PrefixMapHandler = (Map>, Map) -> Void; +typedef ItemHandler = (T) -> Void; +typedef FinishHandler = () -> Void; +typedef BadKeyHandler = (String, PrefixMap) -> Void; +typedef BadShortcutHandler = (String, ShortcutKey) -> Void; + +enum ShortcutKey { + Cancel(key:String); + Final(item:T); + Prefix(keys:PrefixMap); +} + +@:build(kiss.Kiss.build()) +class KeyShortcutHandler {} diff --git a/src/kiss_tools/KeyShortcutHandler.kiss b/src/kiss_tools/KeyShortcutHandler.kiss new file mode 100644 index 0000000..56abb3c --- /dev/null +++ b/src/kiss_tools/KeyShortcutHandler.kiss @@ -0,0 +1,111 @@ +(prop :PrefixMap rootMap (new Map)) +(prop &mut :PrefixMap currentMap null) + +(prop &mut :String cancelKey "escape") +(prop &mut :PrefixMapHandler onSelectPrefixMap null) +(prop &mut :ItemHandler onSelectItem null) +(prop &mut :FinishHandler onFinishOrCancel null) +(prop &mut :BadKeyHandler onBadKey null) +(prop &mut :BadShortcutHandler onBadShortcut null) + +(defNew []) + +(defMacro tryCall [handler &rest args] + `(whenLet [handler ,handler] + (handler ,@args))) + +(defMacro tryCallOrThrow [handler message returnNull &rest args] + `(ifLet [handler ,handler] + {(handler ,@args),(if (eval returnNull) `(return null) `(return))} + (throw ,message))) + +(method _selectMap [m] + (set currentMap m) + (tryCall onSelectPrefixMap m (prefixMapToStrings m))) + +(method start [] + (_selectMap rootMap)) + +(method cancel [] + (set currentMap null) + (tryCall onFinishOrCancel)) + +(method clear [] + (rootMap.clear)) + +(method handleKey [:String key] + (unless currentMap (tryCallOrThrow onBadKey "Tried to handle key $key without calling start() first" true key null)) + (if (= cancelKey key) + (cancel) + (case (dictGet currentMap key) + ((Final item) + (cancel) + (tryCall onSelectItem item)) + ((Prefix nextMap) + (_selectMap nextMap)) + (otherwise + (tryCallOrThrow onBadKey "Key $key is not defined in $currentMap and no onBadKey event was given" true key currentMap))))) + +// Extract [k]eyboard [s]hortcuts from a string: +(method extractKeyboardShortcuts [str &opt :Stream stream :Array shortcuts] + (unless stream (set stream (Stream.fromString str))) + (unless shortcuts (set shortcuts [])) + (case (stream.takeUntilOneOf ["[" "{"] false) + ((Some _) + (case (stream.takeChars 1) + ((Some "[") + (case (stream.takeUntilAndDrop "]") + ((Some newShortcuts) + (extractKeyboardShortcuts str stream (shortcuts.concat (newShortcuts.split "")))) + (otherwise + (tryCallOrThrow onBadShortcut "unclosed [ in $str" true str null) + []))) + ((Some "{") + (case (stream.takeUntilAndDrop "}") + ((Some newShortcut) + (extractKeyboardShortcuts str stream (shortcuts.concat [newShortcut]))) + (otherwise + (tryCallOrThrow onBadShortcut "unclosed { in $str" true str null) + []))) + (otherwise (throw "takeUntilOneOf lied")))) + (otherwise + shortcuts))) + +(method :Void registerShortcut [keys description item &opt :Bool force :PrefixMap prefixMap] + (unless prefixMap (set prefixMap rootMap)) + (unless keys (return)) + (let [firstKey (keys.shift)] + (cond + ((and !force (or (prefixMap.exists firstKey) (= firstKey cancelKey))) + (let [existingKey (if (= firstKey cancelKey) (Cancel cancelKey) (dictGet prefixMap firstKey)) + conflictMessage "Keyboard shortcut for $description conflicts with $existingKey"] + (if keys + (case existingKey + // No harm in registering the same item twice: + ((when (= otherItem item) (or (Final otherItem) (Cancel otherItem))) + (return)) + // Conflicting with a different one, on the other hand: + ((or (Final _) (Cancel _)) + (tryCallOrThrow onBadShortcut conflictMessage false description existingKey)) + ((Prefix innerPrefixMap) + (registerShortcut keys description item innerPrefixMap)) + (otherwise)) + (tryCallOrThrow onBadShortcut conflictMessage false description existingKey)))) + (true + (if keys + (let [innerPrefixMap (new Map)] + (dictSet prefixMap firstKey (Prefix innerPrefixMap)) + (registerShortcut keys description item force innerPrefixMap)) + (dictSet prefixMap firstKey (Final item))))))) + +(method :Void registerItem [description :T item &opt :Bool force] + (whenLet [keyboardShortcut (extractKeyboardShortcuts description)] + (registerShortcut keyboardShortcut description item force))) + +(method :Map prefixMapToStrings [:PrefixMap map] + (for =>key mapping map + =>key (case mapping + ((Cancel _) "cancel") + ((Final thing) (Std.string thing)) + ((Prefix map) (.join (collect (map.keys)) ", ")) + (never null)))) \ No newline at end of file diff --git a/src/kiss_tools/OBSTools.hx b/src/kiss_tools/OBSTools.hx new file mode 100644 index 0000000..bd859ec --- /dev/null +++ b/src/kiss_tools/OBSTools.hx @@ -0,0 +1,6 @@ +package kiss_tools; + +import kiss.Prelude; + +@:build(kiss.Kiss.build()) +class OBSTools {} \ No newline at end of file diff --git a/src/kiss_tools/OBSTools.kiss b/src/kiss_tools/OBSTools.kiss new file mode 100644 index 0000000..584dece --- /dev/null +++ b/src/kiss_tools/OBSTools.kiss @@ -0,0 +1,58 @@ +(var &mut :String projectObsFolder null) +(var &mut :Bool obsIsRecording false) +(var &mut :sys.io.Process obsProcess null) + +(function obsExe [] + (case (Sys.systemName) + ("Linux" "obs") + ("Windows" #"C:\Program Files\obs-studio\bin\64bit\obs64.exe"#) + (otherwise (throw "OBS executable not defined for $(Sys.systemName)")))) + +(function :Void startObs [] + (when obsIsRecording + (print "Warning! OBS is already recording!") + (return)) + (if projectObsFolder + (let [os (Sys.systemName) + exe (obsExe) + dirOfExe (haxe.io.Path.directory exe) + profileDir "${projectObsFolder}/${os}_Profile" + sceneCollectionFile "${projectObsFolder}/${os}_SceneCollection.json"] + (if (and (sys.FileSystem.exists profileDir) (sys.FileSystem.exists sceneCollectionFile)) + (when (Prelude.tryProcess exe ["--version"] + ->error { + (print "error trying to run OBS: $error") + } + [] + true + (when dirOfExe dirOfExe)) + + // This is required to find the local.ini file: + (localVar lastCwd (Sys.getCwd)) + (when dirOfExe (Sys.setCwd dirOfExe)) + (set obsProcess + (new sys.io.Process + exe + [ + "--profile" + profileDir + "--collection" + sceneCollectionFile + "--scene" + "Scene" + "--startrecording" + "--minimize-to-tray" + "--multi" + ])) + (when dirOfExe (Sys.setCwd lastCwd)) + (set obsIsRecording true)) + (print "OBS profile and scene collection not found for ${os} in ${projectObsFolder}. Will not be recording"))) + (print "OBSTools.projectObsFolder is not set. Will not be recording"))) + +(function :Void stopObs [] + (#when debug + (when obsIsRecording + // kill the obs process + (obsProcess.kill) + (set obsIsRecording false)))) + diff --git a/src/kiss_tools/RefactorUtil.kiss b/src/kiss_tools/RefactorUtil.kiss new file mode 100644 index 0000000..c2ea118 --- /dev/null +++ b/src/kiss_tools/RefactorUtil.kiss @@ -0,0 +1,22 @@ +// Quick and dirty way to make a block reusable without restructuring code +// TODO won't work for function/method if locals are used in the body +(defMacro defAndCall [type name &builder b &body body] + (assert (exprCase type + ((exprOr function method localFunction) true) + (_ false)) "the first argument to defAndCall must be a symbol: function, method, or localFunction") + `{(,type ,name [] ,@body) (,(b.symbol (symbolNameValue name true true)))}) + +// Quick and dirty way to define constants without restructuring code +(defMacro defAndReturn [type name value &builder b] + (assert (exprCase type + ((exprOr var prop localVar) true) + (_ false)) "the first argument to defAndReturn must be a symbol: var, prop, or localVar") + `{(,type ,name ,value) ,(b.symbol (symbolNameValue name true true))}) + +(defMacro quickToggle [toggleFlag &body body] + `{ + (savedVar :Bool ,toggleFlag true) + (set ,toggleFlag ,toggleFlag) + (when ,toggleFlag + ,@body) + }) \ No newline at end of file diff --git a/src/kiss_tools/TimerWithPause.hx b/src/kiss_tools/TimerWithPause.hx new file mode 100644 index 0000000..0baf6c5 --- /dev/null +++ b/src/kiss_tools/TimerWithPause.hx @@ -0,0 +1,15 @@ +package kiss_tools; + +import kiss.Prelude; +import kiss.List; +import haxe.Timer; + +typedef WrappedTimer = { + startTime:Float, + duration:Float, + t:Timer, + f:Void->Void +} + +@:build(kiss.Kiss.build()) +class TimerWithPause {} diff --git a/src/kiss_tools/TimerWithPause.kiss b/src/kiss_tools/TimerWithPause.kiss new file mode 100644 index 0000000..6e1046c --- /dev/null +++ b/src/kiss_tools/TimerWithPause.kiss @@ -0,0 +1,57 @@ +(var &mut :Array timers []) + +(function :WrappedTimer delay [:Void->Void f :Float sec] + (let [wrapped + (object + startTime (Timer.stamp) + duration sec + t null + f f) + wrappedF + ->{(timers.remove wrapped)(f)}] + (set wrapped.t (Timer.delay wrappedF (Std.int (* sec 1000)))) + (timers.push wrapped) + wrapped)) + +(function :WrappedTimer interval [:Void->Void f :Float sec &opt :Int numberOfTimes] + (let [&mut :WrappedTimer wrapped null + wrappedF + ->:Void { + (when (and numberOfTimes (<= numberOfTimes 0)) + (stop wrapped) + (return)) + (f) + (when numberOfTimes + (-= numberOfTimes 1)) + // Maintain the original wrapper object as a reference to the current iteration of the interval: + (let [newWrapped (interval f sec numberOfTimes)] + (set wrapped.t newWrapped.t) + (set wrapped.f newWrapped.f) + (set wrapped.startTime newWrapped.startTime) + (set wrapped.duration newWrapped.duration)) + }] + (set wrapped (delay wrappedF sec)) + wrapped)) + +(function stop [:WrappedTimer timer] + (timer.t.stop) + (timers.remove timer)) + +(function pause [] + (let [now (Timer.stamp)] + (doFor timer timers + (let [elapsed (- now timer.startTime)] + (-= timer.duration elapsed) + (timer.t.stop))))) + +(function resume [] + (let [oldTimers timers] + (set timers []) + (doFor timer oldTimers + // This delays with f instead of wrappedF because it will be re-wrapped by delay + (delay timer.f timer.duration)))) + +(function stopAll [] + (doFor timer timers + (timer.t.stop)) + (set timers [])) \ No newline at end of file diff --git a/src/test/KissToolsMain.hx b/src/test/KissToolsMain.hx new file mode 100644 index 0000000..e017c81 --- /dev/null +++ b/src/test/KissToolsMain.hx @@ -0,0 +1,8 @@ +package test; + +class KissToolsMain { + static function main() { + KissToolsMain_.main(); + } +} + diff --git a/src/test/KissToolsMain_.kiss b/src/test/KissToolsMain_.kiss new file mode 100644 index 0000000..1cc683c --- /dev/null +++ b/src/test/KissToolsMain_.kiss @@ -0,0 +1,43 @@ +(import sys.io.File) +(import sys.FileSystem) +(import kiss_tools.JsonMap) +(import kiss_tools.JsonArray) + +// Test FuzzyJson +(loadFrom "kiss-tools" "src/kiss_tools/FuzzyJson.kiss") + +(loadFuzzyJson "dogs" "test/fuzzy.json") +(loadFuzzyJson "dogs" "test/fuzzy2.json") + +(assert (= "is a very good dog" (getFuzzyJson "dogs" "Albort"))) +// takeFuzzyJson removes the match to make following fuzzyJson retrievals save time: +(assert (= "is a very good dog" (takeFuzzyJson "dogs" "Albort"))) +// No good match will cause crash at compile time: +(assertThrowsAtCompileTime (getFuzzyJson "dogs" "Albort")) +// duplicate definitions throw an error at compile time: +(assertThrowsAtCompileTime (getFuzzyJson "dogs" "Rangie")) + + +// Test JsonMap +(var TEST_JSON_FILE "testJsonFile.json") + +(File.saveContent TEST_JSON_FILE (File.getContent "test/map-start.json")) +(let [jsonMap (new JsonMap> TEST_JSON_FILE (new JsonArray> [] (new TestJsonable ""))) + a (jsonMap.get "a") + aString (a.elements.toString) + b (jsonMap.get "b") + bString (b.elements.toString)] + (assert (contains aString "abc")) + (assert (contains aString "def")) + (assert (contains bString "ghi")) + (assert (contains bString "jkl")) + + (a.elements.push (new TestJsonable "mno")) + (jsonMap.put "a" a) + (let [c (jsonMap.get "c")] + (assertEquals 0 c.elements.length))) + +// Uncomment this line when expanding the test: +// (File.saveContent "test/map-end.json" (File.getContent TEST_JSON_FILE)) +(assertEquals (File.getContent "test/map-end.json") (File.getContent TEST_JSON_FILE)) +(FileSystem.deleteFile TEST_JSON_FILE) \ No newline at end of file diff --git a/src/test/TestJsonable.hx b/src/test/TestJsonable.hx new file mode 100644 index 0000000..bdf51b3 --- /dev/null +++ b/src/test/TestJsonable.hx @@ -0,0 +1,7 @@ +package test; + +import kiss.Prelude; +import kiss.List; + +@:build(kiss.Kiss.build()) +class TestJsonable {} diff --git a/src/test/TestJsonable.kiss b/src/test/TestJsonable.kiss new file mode 100644 index 0000000..a921707 --- /dev/null +++ b/src/test/TestJsonable.kiss @@ -0,0 +1,5 @@ +(defNew [&prop :String s]) + +(method parse [:String s] (new TestJsonable s)) +(method stringify [] s) +(method toString [] s) \ No newline at end of file diff --git a/test.sh b/test.sh index 8928a7e..4d4de87 100755 --- a/test.sh +++ b/test.sh @@ -24,4 +24,4 @@ if [ ! -z "$2" ]; then haxe $FLAGS -D cases=$2 build-scripts/common-args.hxml build-scripts/common-test-args.hxml build-scripts/$KISS_TARGET/test.hxml else haxe $FLAGS build-scripts/common-args.hxml build-scripts/common-test-args.hxml build-scripts/$KISS_TARGET/test.hxml -fi \ No newline at end of file +fi diff --git a/test/fuzzy.json b/test/fuzzy.json new file mode 100644 index 0000000..51b1af2 --- /dev/null +++ b/test/fuzzy.json @@ -0,0 +1,5 @@ +{ + "albert": "is a very good dog", + "ruuuuuby": "is the bestest doggy", + "rangie": "is also my favorite" +} \ No newline at end of file diff --git a/test/fuzzy2.json b/test/fuzzy2.json new file mode 100644 index 0000000..36dc81d --- /dev/null +++ b/test/fuzzy2.json @@ -0,0 +1,3 @@ +{ + "rangie": "is so cute he gets a duplicate entry" +} \ No newline at end of file diff --git a/test/map-end.json b/test/map-end.json new file mode 100644 index 0000000..1fcd17b --- /dev/null +++ b/test/map-end.json @@ -0,0 +1,5 @@ +{ + "a": "[\"abc\",\"def\",\"mno\"]", + "b": "[\"ghi\",\"jkl\"]", + "c": "[]" +} \ No newline at end of file diff --git a/test/map-start.json b/test/map-start.json new file mode 100644 index 0000000..c16cd2b --- /dev/null +++ b/test/map-start.json @@ -0,0 +1,4 @@ +{ + "a": "[\"abc\", \"def\"]", + "b": "[\"ghi\", \"jkl\"]" +} \ No newline at end of file