Skip to content
forked from haxscramper/hmisc

Collection of miscellaneous helper algorithms and types. Helper functions for strings, exceptions etc.

License

Notifications You must be signed in to change notification settings

disruptek/hmisc

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

readme

I don’t want to call this library officially “deprecated”, but I haven’t been paying as much attention to the code quality and/or in recent times. I think everything up until version 0.14.4 can be considered “mostly reliable” - so if you are looking to use this library, I would advise you to pin dependency to this version.

Maybe in the future I will return to working on this library - for now I don’t think I have the energy required to constantly fight nim documentation generator, write unit testing framework and test runner and so on.


Collection of miscellaneous helper algorithms and types. Over time this library accumulated a lot of functionality, not all of this is necessary and almost everything was made on case-by-case basis instead of being designed at once.

Most useful parts of the library are probably

  1. hmisc/other/oswrap - wrapper for the std/os but with support for proper disctinct types
  2. hmisc/other/hshell - shell command execution builder
  3. hmisc/core/gold or hmisc/core/all - these contain my import-everywhere templates, procedures and macros.

In addition to the general helpers, the library also contains forks of some of the fusion modules - specifically hmisc/types/hmap is a fork of fusion/btreetables with saner type naming (no more type clashes all over the place) and couple helper routines. I also copied fusion/matching back, since fusion can be considered effective dead and deprecated - not tagged (at the time of writing requires fusion still pulls 9-month old version, that contains none of the fixes that was made to pattern matching after that. For more details see forum thread - not especially interesting, but if you want more context …). Right now it is a simple copy, in the future I will clean up the implementation since I’ve got several ideas on how it can be improved. DSL will be cleaned up as well, right now it is really overloaded.

Installation:

nimble install hmisc

Links

Macros

hmisc/macros/iflet

Rust-like iflet. Get value from Option[T] only if it contains something, otherwise execute else.

import options
import hmisc/macros/iflet

iflet (val = some(12)):
  echo typeof val

iflet (val = none(int)):
  echo "???"
else:
  echo "no value"
int
no value

Algorithms

hmisc/algo/halgorithm documentation

  • predicates
    • allOfIt
    • anyOfIt
    • noneOfIt
    • == for Option-Val comparison
  • working with sequences
    • maxIt
    • disjointIter
    • last
  • working with strings
    • joinw, joinq, joinl, joinkv - join on whitespaces, whitespace + quote each string, newlines or key-value pairs. Mostly useful in strformat.& - can write {somevar.join()} instead of {somevar.join(\"\\n\")}
    • wrap - wrap strings in delimiters. Has convinience overload for .wrap("()") that automatically determines starting/ending wrapper strings.
    • Multiple overloads for join and startsWith
    • enclosedIn - check if string is wrapped in delimiters
    • dedent - decease indentation for multiline string
    • camelSplit - split string as camel case identifier
    • abbrevCamel - camelCase abbreviation search.
    • Several variations of dropPrefix, addPrefix, startsWith, addSuffix for less common use cases
    • Filtering sequence of strings by prefix
    • Dropping subsequence in strings
    • Finding common prefix in sequence of strings
  • other
    • ifSomeIt - same as opt.isSome() and (let it = opt.get(); predicate)
    • testEq - compare two objects. If they are different print first mismatching line in their string representation.
    • assertEq - compare objects using testEq, raise on failed comparison.
import hmisc/algo/halgorithm, std/strformat
let v = @["w234", "333"]

echo ": ", &"{v.joinq()}"

block:
  echo "-- withIt --"
  let immutable = (a: 12, b: 12)
  echo immutable.withIt do:
    it.a = 909

block:
  echo "-- withResIt --"
  let immutable = (a: 12, b: "eee")
  echo immutable.withResIt do:
    it.a += 999
    $it.a & it.b

block:
  echo "-- join* --"
  echo {1 : "22", 3: "333"}.joinkv().join()

block:
  echo "-- abbrevCamel --"
  echo abbrevCamel("AA", @["ABA", "AZZ", "A)"])
: "w234" "333"
-- withIt --
(a: 909, b: 12)
-- withResIt --
1011eee
-- join* --
0 = (1, "22") 1 = (3, "333")
-- abbrevCamel --
@["ABA"]

hmisc/algo/hseqdistance documentation

Fuzzy string matching and generic longest common subsequece implementation

  • longestCommonSubsequence - generic implementation of LCS algorithm for seq[T]
  • fuzzyMatch - weighted sequence fuzzy match. Compare each element in the sequence to pattern and assign similarity score. Should behave similarly to fzf or sublime text. Reimplementation of ‘Reverse engineering subtime text’s fuzzy match’. I haven’t used it in any interactive applications as of yet, but there are some unit tests. It has generic implementation and somewhat annoying to use, but provides very flexible interface, allowing to completely customize how fuzzy matching is performed.
import hmisc/doc_examples

echo "# ~~~~ leading / ~~~~ #\n|"
matchTest "//hell.txt", "/nice/we/hell.txt":
  if other[matches[0]] == '/':
    1000 # high cost if have exact match with starting /
  else:
    matches.sum()

echo "|\n# ~~~~ no leading / ~~~~ #\n|"
matchTest "nicehell.txt", "/nice/we/hell.txt":
  if other[matches[0]] == '/':
    1000
  else:
    matches.sum()
# ~~~~ leading / ~~~~ #
|
input: /nice/we/hell.txt //hell.txt :1000
match: /    /   hell.txt
|
# ~~~~ no leading / ~~~~ #
|
input: /nice/we/hell.txt nicehell.txt :113
match:  nic   e hell.txt

hmisc/algo/hseq_mapping

  • deduplicateIt
mapPairs
mapIt for types that implement pairs iterator, or items that return tuple, or sequence of tuples. Inject index of the item, lhs (first element) and rhs (second element). Should correctly handle {.requiresinit.} fields.

hmisc/algo/htree_mapping

mapItBFStoSeq
iterate over tree in BFS order, store mapping result in sequence.
iterateItBFS
iterate over tree in BFS order
iterateItDFS
iterate over tree in DFS order. Uses iterative DFS instead of recursive call.
mapItDFS
mapIt for converting trees in DFS order

Types

hmisc/types/colorstring

Easier manipulation of colored strings in terminal. Support splitting regular strin in same-color chunks, finding ‘visible’ length of the string (as printed in terminal). Helper functions like toYellow() or toRed() to make creation of the colored strings simpler. All attributes from terminal module are supported (fg/bg colors and modifiers).

Provides two types for colored text - ColoredString (string + styling) and ColoredRune (unicode rune + styling).

Other

hshell and oswrap modules provide more strictly typed wrappers for tasks that are usually performed using simple string concatenations. You get better static safety guarantees (not possible to pass relative path to function expecting absolute one) and less headaches related to correct quoting/CLI command syntax at the expense of little more verbose code.

oswrap is a 1:1 mapping of std/os and is expected to have all functions reimplemented (wrapped).

hshell also treats non-zero return codes as exceptions, so you can just execute shell commands without endless checks for code != 0: echo "oh no!". This can be turned off, but works by default, so when writing let (output, err, _) = runShell("someCommand") you will be sure that failures won’t be silently ignored.

hmisc/other/oswrap

Wrapper on top of os and nimscsrip that allows to use the same code on c and nimscript targets. Some helper templates/functions are introduced. Provide distinct string for files/directories - e.g. RelDIr = distinct string as well as overloads for almost all functions in os module.

NOTE it is expected to be imported instead of os module - functions without arguments were update to use distinct types too, so if two modules are imported togetether frequent type clashes are expected.

  • mkDir, getEnv, delEnv, toExe, listDirs, rmFile, mkDir, mvFile, cpFile, cpDir, cd, cwd - default file/directory manipulation functions
  • ~ (prefix tilda) prefix operator to get path relative to home directory. Same as getHomeDir() / path
  • && join shell command strings with correct spacing
  • withDir - temporarily set directory for body
  • withEnv - temporarily set environment variables for body

hmisc/other/hshell documentation

Helper functions for running shell commands - reduce need for string concatenation for shell - Cmd object supports adding commands/flags/options/subcommands/arguments while deferring conversion to string as long as possible and taking care of correct syntax (correct dashes for X11 CLI tools (always single prefix dash), key-value separators (nim tooling uses :, GNU is most likely to expect = or spaces)).

Possible use case: Imagine you need to write a script that launches new docker container, mounts some folders, copy files over, and perform some nontrivial commands inside container (and command is not predetermined - it is also has to be built in advance).

Regular approach would be to cobble together one giant string that will then be executed via startProcess. You need to then check for return code, and hope that you haven’t messed up quoting, argument syntax for particular command and so on (nim tooling uses :, GNU - = and so on).

hshell hopefully provides solution to most of the usability of command line programs - you no longer need to worry about correct spacing, quoting and other stuff like that. Instead, you just build AST for command to be executed, using set of convenient operators and functions.

let cmd = shCmd("nimble", "install")
# Nice side effect - you can now comment on different flags and use
# checks/loops without worrying about correct
# spacing/concatnation/prefixes etc.
let doCleanup = true
let dockerCmd = shCmd("docker").withIt do:
  it.cmd "run" # Add subcommand
  it - "i"
  it - "t"
  if doCleanup:
    it - "rm" # Remove container after test execution
  it - ("v", "/tmp/tmp-mount:/project") # Key-value pair
  it.arg "nim-base"
  it.arg "sh"
  it - "c"
  it.expr:
    shAnd:
      shCmd(cd, "/project/main")
      cmd # Can easily build complicated commands from variables

NOTE: no special DSL syntax is introduced, just couple of overlads for common use cases (- proc for flags/options)

  • runShell Raise exception when command has exited with non-zero code (because you will be checking return code anyway), get stderr and stdout separately. Uses fallback exec and gorgeEx on nimscript targets and tries to emulate compiled behaviour as close as possible (respect execution flags).
  • iterstdout - iterate each line for executed program’s stdout
  • execShell - execute shell command, redirect output into parent streams.

NOTE: You might consider this module a ‘shell program wrapper’. It was created to make using external processes from your code easier and safer. No need to check return codes all the time, think about quoting, correct arguments and so on. Thus said - it is quite difficult to wrap all complixity of the command line interfaces, even with quite sophisticated logic. Several escape hatches are present, to still pass almost arbitrary strings for shell execution. First: ShellExpr - thin wrapper, distinct string. Second is raw() function for setting command line arguments.

Contribution & development

Most of the features in this library were implemented on do-it-when-I-need-it basis. Some of them are tested quite extensively (sequence and tree mappings, colored strings), but more unit test are always welcome.

References

About

Collection of miscellaneous helper algorithms and types. Helper functions for strings, exceptions etc.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Nim 99.7%
  • HTML 0.3%