| 1 | 
 | 
| 2 | #include "cpp/embedded_file.h"
 | 
| 3 | 
 | 
| 4 | namespace embedded_file {
 | 
| 5 | GLOBAL_STR(gStr0, R"zZXx(
 | 
| 6 |   Errors
 | 
| 7 |   <a class="group-link" href="chap-errors.html">errors</a>
 | 
| 8 | 
 | 
| 9 | 
 | 
| 10 |   [UTF8]      err-utf8-encode       err-utf8-decode
 | 
| 11 |   [J8 String] err-j8-str-encode     err-j8-str-decode
 | 
| 12 |   [J8 Lines]  err-j8-lines-encode   err-j8-lines-decode
 | 
| 13 |   [JSON]      err-json-encode       err-json-decode
 | 
| 14 |   [JSON8]     err-json8-encode      err-json8-decode
 | 
| 15 | )zZXx");
 | 
| 16 | 
 | 
| 17 | GLOBAL_STR(gStr1, R"zZXx(
 | 
| 18 |   
 | 
| 19 |   Front End <a class="group-link" href="chap-front-end.html">front-end</a>
 | 
| 20 | 
 | 
| 21 | 
 | 
| 22 |   [Lexing]        ascii-whitespace [ \t\r\n]
 | 
| 23 |                   ascii-control-chars
 | 
| 24 | )zZXx");
 | 
| 25 | 
 | 
| 26 | GLOBAL_STR(gStr2, R"zZXx(
 | 
| 27 |   J8 Notation
 | 
| 28 |   <a class="group-link" href="chap-j8.html">j8</a>
 | 
| 29 | 
 | 
| 30 | 
 | 
| 31 |   [J8 Strings]   json-string        "hi"
 | 
| 32 |                  json-escape        \"  \\  \u1234
 | 
| 33 |                  surrogate-pair     \ud83e\udd26
 | 
| 34 |                  j8-escape          \'  \u{1f926}  \yff
 | 
| 35 |                  u-prefix           u'hi'
 | 
| 36 |                  b-prefix           b'hi'
 | 
| 37 |                  no-prefix          'hi'
 | 
| 38 |   [J8 Lines]     unquoted-line
 | 
| 39 |   [JSON8]        json8-num          json8-str
 | 
| 40 |                X json8-list       X json8-dict
 | 
| 41 |                  json8-comment
 | 
| 42 |   [TSV8]         column-attrs       column-types
 | 
| 43 | )zZXx");
 | 
| 44 | 
 | 
| 45 | GLOBAL_STR(gStr3, R"zZXx(Usage: help TOPIC?
 | 
| 46 | 
 | 
| 47 | Examples:
 | 
| 48 | 
 | 
| 49 |     help               # this help
 | 
| 50 |     help echo          # help on the 'echo' builtin
 | 
| 51 |     help command-sub   # help on command sub $(date)
 | 
| 52 | 
 | 
| 53 |     help oils-usage    # identical to oils-for-unix --help
 | 
| 54 |     help osh-usage     #              osh --help
 | 
| 55 |     help ysh-usage     #              ysh --help
 | 
| 56 | )zZXx");
 | 
| 57 | 
 | 
| 58 | GLOBAL_STR(gStr4, R"zZXx(bin/oils-for-unix is an executable that contains OSH, YSH, and more.
 | 
| 59 | 
 | 
| 60 | Usage: oils-for-unix MAIN_NAME ARG*
 | 
| 61 |        MAIN_NAME ARG*
 | 
| 62 | 
 | 
| 63 | It behaves like busybox.  The command name can be passed as the first argument:
 | 
| 64 | 
 | 
| 65 |     oils-for-unix ysh -c 'echo hi'
 | 
| 66 | 
 | 
| 67 | More commonly, it's invoked through a symlink like 'ysh', which causes it to
 | 
| 68 | behave like that command:
 | 
| 69 | 
 | 
| 70 |     ysh -c 'echo hi'
 | 
| 71 | )zZXx");
 | 
| 72 | 
 | 
| 73 | GLOBAL_STR(gStr5, R"zZXx(
 | 
| 74 |   Builtin Commands <a class="group-link" href="chap-builtin-cmd.html">builtin-cmd</a>
 | 
| 75 | 
 | 
| 76 | 
 | 
| 77 |   [I/O]           read        echo      printf
 | 
| 78 |                   readarray   mapfile
 | 
| 79 |   [Run Code]      source .    eval      trap
 | 
| 80 |   [Set Options]   set         shopt
 | 
| 81 |   [Working Dir]   cd          pwd       pushd     popd         dirs
 | 
| 82 |   [Completion]    complete    compgen   compopt   compadjust   compexport
 | 
| 83 |   [Shell Process] exec      X logout 
 | 
| 84 |                   umask       ulimit    times
 | 
| 85 |   [Child Process] jobs        wait
 | 
| 86 |                   fg        X bg      X kill        X disown 
 | 
| 87 |   [External]      test [      getopts
 | 
| 88 |   [Introspection] help        hash      cmd/type    X caller
 | 
| 89 |   [Word Lookup]   command     builtin
 | 
| 90 |   [Interactive]   alias       unalias   history     X fc     X bind
 | 
| 91 | X [Unsupported]   enable
 | 
| 92 | )zZXx");
 | 
| 93 | 
 | 
| 94 | GLOBAL_STR(gStr6, R"zZXx(The reference is divided in to "chapters", each of which has its own table of
 | 
| 95 | contents.  Type:
 | 
| 96 | 
 | 
| 97 |     help osh-$CHAPTER
 | 
| 98 | 
 | 
| 99 | Where $CHAPTER is one of:
 | 
| 100 | 
 | 
| 101 |     front-end
 | 
| 102 |     command-lang
 | 
| 103 |     osh-assign
 | 
| 104 |     word-lang
 | 
| 105 |     mini-lang
 | 
| 106 |     builtin-cmd
 | 
| 107 |     option
 | 
| 108 |     special-var
 | 
| 109 |     plugin
 | 
| 110 | 
 | 
| 111 | Example:
 | 
| 112 | 
 | 
| 113 |     help osh-word-lang
 | 
| 114 | )zZXx");
 | 
| 115 | 
 | 
| 116 | GLOBAL_STR(gStr7, R"zZXx(
 | 
| 117 |   Command Language <a class="group-link" href="chap-cmd-lang.html">cmd-lang</a>
 | 
| 118 | 
 | 
| 119 | 
 | 
| 120 |   [Commands]      simple-command            semicolon ;
 | 
| 121 |   [Conditional]   case        if            dbracket [[
 | 
| 122 |                   true        false         colon :
 | 
| 123 |                   bang !      and &&        or ||
 | 
| 124 |   [Iteration]     while       until         for            for-expr-sh ((
 | 
| 125 |   [Control Flow]  break       continue      return         exit
 | 
| 126 |   [Grouping]      sh-func     sh-block {    subshell (
 | 
| 127 |   [Concurrency]   pipe |    X pipe-amp |&   ampersand &
 | 
| 128 |   [Redirects]     redir-file  >  >>  >|  <  <>   not impl: &>
 | 
| 129 |                   redir-desc  >&  <&
 | 
| 130 |                   here-doc    <<  <<-  <<<
 | 
| 131 |   [Other Command] dparen ((   time        X coproc       X select
 | 
| 132 | )zZXx");
 | 
| 133 | 
 | 
| 134 | GLOBAL_STR(gStr8, R"zZXx(
 | 
| 135 |   Front End <a class="group-link" href="chap-front-end.html">front-end</a>
 | 
| 136 | 
 | 
| 137 | 
 | 
| 138 |   [Usage]         oils-usage   osh-usage             config
 | 
| 139 |                   startup      line-editing          exit-codes
 | 
| 140 |   [Lexing]        comment #    line-continuation \   ascii-whitespace [ \t\r\n]
 | 
| 141 | )zZXx");
 | 
| 142 | 
 | 
| 143 | GLOBAL_STR(gStr9, R"zZXx(
 | 
| 144 |   Other Mini Languages <a class="group-link" href="chap-mini-lang.html">mini-lang</a>
 | 
| 145 | 
 | 
| 146 | 
 | 
| 147 |   [Arithmetic]    arith-context Where legacy arithmetic is allowed
 | 
| 148 |                   sh-numbers    0xFF  0755  etc.
 | 
| 149 |                   sh-arith      1 + 2*3   a *= 2
 | 
| 150 |                   sh-logical    !a && b
 | 
| 151 |                   sh-bitwise    ~a ^ b
 | 
| 152 |   [Boolean]       bool-expr     [[ ! $x && $y || $z ]]
 | 
| 153 |                                 test ! $x -a $y -o $z
 | 
| 154 |                   bool-infix    $a -nt $b    $x == $y
 | 
| 155 |                   bool-path     -d /etc
 | 
| 156 |                   bool-str      -n foo   -z '' 
 | 
| 157 |                   bool-other    -o errexit   -v name[index]
 | 
| 158 |   [Patterns]      glob-pat      *.py
 | 
| 159 |                   extglob       ,(*.py|*.sh)
 | 
| 160 |                   regex         [[ foo =~ [a-z]+ ]]
 | 
| 161 |   [Other Sublang] braces        {alice,bob}@example.com
 | 
| 162 |                   histsub       !$  !!  !n
 | 
| 163 |                   char-escapes  \t  \c  \x00  \u03bc
 | 
| 164 | )zZXx");
 | 
| 165 | 
 | 
| 166 | GLOBAL_STR(gStr10, R"zZXx(
 | 
| 167 |   Global Shell Options <a class="group-link" href="chap-option.html">option</a>
 | 
| 168 | 
 | 
| 169 | 
 | 
| 170 |   [Errors]         nounset -u      errexit -e   inherit_errexit   pipefail
 | 
| 171 |   [Globbing]       noglob -f       nullglob     failglob        X dotglob
 | 
| 172 |                    dashglob (true)
 | 
| 173 |   [Debugging]      xtrace        X verbose    X extdebug
 | 
| 174 |   [Interactive]    emacs           vi
 | 
| 175 |   [Other POSIX]  X noclobber
 | 
| 176 |   [Compat]         eval_unsafe_arith            ignore_flags_not_impl
 | 
| 177 | )zZXx");
 | 
| 178 | 
 | 
| 179 | GLOBAL_STR(gStr11, R"zZXx(
 | 
| 180 |   Assignments and Expressions <a class="group-link" href="chap-osh-assign.html">osh-assign</a>
 | 
| 181 | 
 | 
| 182 | 
 | 
| 183 |   [Literals]      sh-array      array=(a b c)   array[1]=B   "${a[@]}"
 | 
| 184 |                   sh-assoc      assoc=(['a']=1 ['b']=2)   assoc['x']=b
 | 
| 185 |   [Operators]     sh-assign     str='xyz'
 | 
| 186 |                   sh-append     str+='abc'
 | 
| 187 |   [Builtins]      local     readonly    export   unset   shift
 | 
| 188 |                   declare   typeset   X let
 | 
| 189 | )zZXx");
 | 
| 190 | 
 | 
| 191 | GLOBAL_STR(gStr12, R"zZXx(
 | 
| 192 |   Plugins and Hooks <a class="group-link" href="chap-plugin.html">plugin</a>
 | 
| 193 | 
 | 
| 194 | 
 | 
| 195 |   [Signals]       SIGTERM     SIGINT     SIGQUIT
 | 
| 196 |                   SIGTTIN     SIGTTOU    SIGWINCH
 | 
| 197 |   [Traps]         DEBUG       ERR        EXIT    X RETURN
 | 
| 198 |   [Words]         PS1       X PS2      X PS3       PS4
 | 
| 199 |   [Completion]    complete
 | 
| 200 |   [Other Plugin]  PROMPT_COMMAND       X command_not_found    
 | 
| 201 | )zZXx");
 | 
| 202 | 
 | 
| 203 | GLOBAL_STR(gStr13, R"zZXx(
 | 
| 204 |   Special Variables <a class="group-link" href="chap-special-var.html">special-var</a>
 | 
| 205 | 
 | 
| 206 | 
 | 
| 207 |   [POSIX Special] $@  $*  $#     $?  $-     $$  $!   $0  $9
 | 
| 208 |   [Shell Vars]    IFS             X LANG       X GLOBIGNORE
 | 
| 209 |   [Shell Options] SHELLOPTS       X BASHOPTS
 | 
| 210 |   [Other Env]     HOME              PATH
 | 
| 211 |   [Other Special] BASH_REMATCH     @PIPESTATUS
 | 
| 212 |   [Platform]      HOSTNAME          OSTYPE
 | 
| 213 |   [Call Stack]    @BASH_SOURCE     @FUNCNAME    @BASH_LINENO   
 | 
| 214 |                 X @BASH_ARGV     X @BASH_ARGC
 | 
| 215 |   [Tracing]       LINENO
 | 
| 216 |   [Process State] UID               EUID         PPID       X BASHPID
 | 
| 217 | X [Process Stack] BASH_SUBSHELL     SHLVL
 | 
| 218 | X [Shell State]   BASH_CMDS        @DIRSTACK
 | 
| 219 |   [Completion]   @COMP_WORDS        COMP_CWORD    COMP_LINE   COMP_POINT
 | 
| 220 |                   COMP_WORDBREAKS  @COMPREPLY   X COMP_KEY
 | 
| 221 |                 X COMP_TYPE         COMP_ARGV
 | 
| 222 |   [History]       HISTFILE
 | 
| 223 |   [cd]            PWD               OLDPWD      X CDPATH
 | 
| 224 |   [getopts]       OPTIND            OPTARG      X OPTERR
 | 
| 225 |   [read]          REPLY
 | 
| 226 |   [Functions]   X RANDOM            SECONDS
 | 
| 227 |   [Oils VM]       OILS_VERSION      LIB_OSH
 | 
| 228 | )zZXx");
 | 
| 229 | 
 | 
| 230 | GLOBAL_STR(gStr14, R"zZXx(
 | 
| 231 |   Standard Library <a class="group-link" href="chap-stdlib.html">stdlib</a>
 | 
| 232 | 
 | 
| 233 | 
 | 
| 234 |   [two]           log             die
 | 
| 235 |   [no-quotes]     nq-assert       nq-run          
 | 
| 236 |                   nq-capture      nq-capture-2
 | 
| 237 |                   nq-redir        nq-redir-2
 | 
| 238 |   [bash-strict]   
 | 
| 239 |   [task-five]     
 | 
| 240 | )zZXx");
 | 
| 241 | 
 | 
| 242 | GLOBAL_STR(gStr15, R"zZXx(
 | 
| 243 |   OSH Types <a class="group-link" href="chap-type-method.html">type-method</a>
 | 
| 244 | 
 | 
| 245 | 
 | 
| 246 |   [OSH]           BashArray   BashAssoc
 | 
| 247 | )zZXx");
 | 
| 248 | 
 | 
| 249 | GLOBAL_STR(gStr16, R"zZXx(bin/osh is compatible with POSIX shell, bash, and other shells.
 | 
| 250 | 
 | 
| 251 | Usage: osh FLAG* SCRIPT ARG*
 | 
| 252 |        osh FLAG* -c COMMAND ARG*
 | 
| 253 |        osh FLAG*
 | 
| 254 | 
 | 
| 255 | The command line accepted by `bin/osh` is compatible with /bin/sh and bash.
 | 
| 256 | 
 | 
| 257 |     osh -c 'echo hi'
 | 
| 258 |     osh myscript.sh
 | 
| 259 |     echo 'echo hi' | osh
 | 
| 260 | 
 | 
| 261 | It also has a few enhancements:
 | 
| 262 | 
 | 
| 263 |     osh -n -c 'hello'                    # pretty-print the AST
 | 
| 264 |     osh --ast-format text -n -c 'hello'  # print it full
 | 
| 265 | 
 | 
| 266 | osh accepts POSIX sh flags, with these additions:
 | 
| 267 | 
 | 
| 268 |     -n             parse the program but don't execute it.  Print the AST.
 | 
| 269 |     --ast-format   what format the AST should be in
 | 
| 270 | )zZXx");
 | 
| 271 | 
 | 
| 272 | GLOBAL_STR(gStr17, R"zZXx(
 | 
| 273 |   Word Language <a class="group-link" href="chap-word-lang.html">word-lang</a>
 | 
| 274 | 
 | 
| 275 | 
 | 
| 276 |   [Quotes]        osh-string    'abc'  $'line\n'  "$var"
 | 
| 277 |   [Substitutions] command-sub   $(command)   `command`
 | 
| 278 |                   var-sub       ${var}   $0   $9   
 | 
| 279 |                   arith-sub     $((1 + 2))
 | 
| 280 |                   tilde-sub     ~/src
 | 
| 281 |                   proc-sub      diff <(sort L.txt) <(sort R.txt)
 | 
| 282 |   [Var Ops]       op-test       ${x:-default}  
 | 
| 283 |                   op-strip      ${x%%suffix}  etc.
 | 
| 284 |                   op-replace    ${x//y/z}
 | 
| 285 |                   op-index      ${a[i+1}
 | 
| 286 |                   op-slice      ${a[@]:0:1}
 | 
| 287 |                   op-format     ${x@P}
 | 
| 288 | )zZXx");
 | 
| 289 | 
 | 
| 290 | GLOBAL_STR(gStr18, R"zZXx(
 | 
| 291 |   Builtin Commands <a class="group-link" href="chap-builtin-cmd.html">builtin-cmd</a>
 | 
| 292 | 
 | 
| 293 | 
 | 
| 294 |   [Memory]        cmd/append             Add elements to end of array
 | 
| 295 |                   pp                     asdl   cell   X gc-stats   line   proc
 | 
| 296 |   [Handle Errors] error                  error 'failed' (status=2)
 | 
| 297 |                   try                    Run with errexit, set _error
 | 
| 298 |                   failed                 Test if _error.code !== 0
 | 
| 299 |                   boolstatus             Enforce 0 or 1 exit status
 | 
| 300 |   [Shell State]   ysh-cd       ysh-shopt compatible, and takes a block
 | 
| 301 |                   shvar                  Temporary modify global settings
 | 
| 302 |                   ctx                    Share and update a temporary "context"
 | 
| 303 |                   push-registers         Save registers like $?, PIPESTATUS
 | 
| 304 |   [Modules]       runproc                Run a proc; use as main entry point
 | 
| 305 |                   module                 guard against duplicate 'source'
 | 
| 306 |                   is-main                false when sourcing a file
 | 
| 307 |                   use                    change first word lookup
 | 
| 308 |   [I/O]           ysh-read               flags --all, -0
 | 
| 309 |                   ysh-echo               no -e -n with simple_echo
 | 
| 310 |                   write                  Like echo, with --, --sep, --end
 | 
| 311 |                   fork         forkwait  Replace & and (), and takes a block
 | 
| 312 |                   fopen                  Open multiple streams, takes a block
 | 
| 313 |                 X dbg                    Only thing that can be used in funcs
 | 
| 314 |   [Hay Config]    hay          haynode   For DSLs and config files
 | 
| 315 |   [Completion]    compadjust   compexport
 | 
| 316 |   [Data Formats]  json                   read write
 | 
| 317 |                   json8                  read write
 | 
| 318 | X [Testing]       assert                 takes an expression
 | 
| 319 | )zZXx");
 | 
| 320 | 
 | 
| 321 | GLOBAL_STR(gStr19, R"zZXx(
 | 
| 322 |   Builtin Functions <a class="group-link" href="chap-builtin-func.html">builtin-func</a>
 | 
| 323 | 
 | 
| 324 | 
 | 
| 325 |   [Values]        len()             func/type()
 | 
| 326 |   [Conversions]   bool()            int()           float()
 | 
| 327 |                   str()             list()          dict()
 | 
| 328 |                 X runes()         X encodeRunes()
 | 
| 329 |                 X bytes()         X encodeBytes()
 | 
| 330 |   [Str]         X strcmp()        X split()         shSplit()
 | 
| 331 |   [List]          join()       
 | 
| 332 |   [Float]         floatsEqual()   X isinf()       X isnan()
 | 
| 333 |   [Collections] X copy()          X deepCopy()
 | 
| 334 |   [Word]          glob()            maybe()
 | 
| 335 |   [Serialize]     toJson()          fromJson()
 | 
| 336 |                   toJson8()         fromJson8()
 | 
| 337 | X [J8 Decode]     J8.Bool()         J8.Int()        ...
 | 
| 338 |   [Pattern]       _group()          _start()        _end()
 | 
| 339 |   [Introspection] shvarGet()        getVar()        evalExpr()
 | 
| 340 |   [Hay Config]    parseHay()        evalHay()
 | 
| 341 | X [Hashing]       sha1dc()          sha256()
 | 
| 342 | )zZXx");
 | 
| 343 | 
 | 
| 344 | GLOBAL_STR(gStr20, R"zZXx(The reference is divided in to "chapters", each of which has its own table of
 | 
| 345 | contents.  Type:
 | 
| 346 | 
 | 
| 347 |     help ysh-$CHAPTER
 | 
| 348 | 
 | 
| 349 | Where $CHAPTER is one of:
 | 
| 350 | 
 | 
| 351 |     front-end
 | 
| 352 |     command-lang
 | 
| 353 |     expr-lang
 | 
| 354 |     word-lang
 | 
| 355 |     builtin-cmd
 | 
| 356 |     option
 | 
| 357 |     special-var
 | 
| 358 |     type-method
 | 
| 359 |     builtin-func
 | 
| 360 | 
 | 
| 361 | Example:
 | 
| 362 | 
 | 
| 363 |     help ysh-expr-lang
 | 
| 364 | )zZXx");
 | 
| 365 | 
 | 
| 366 | GLOBAL_STR(gStr21, R"zZXx(
 | 
| 367 |   Command Language <a class="group-link" href="chap-cmd-lang.html">cmd-lang</a>
 | 
| 368 | 
 | 
| 369 | 
 | 
| 370 |   [YSH Simple]    typed-arg     json write (x)
 | 
| 371 |                   lazy-expr-arg assert [42 === x]
 | 
| 372 |                   block-arg     cd /tmp { echo $PWD }; cd /tmp (; ; blockexpr)
 | 
| 373 |   [YSH Cond]      ysh-case      case (x) { *.py { echo 'python' } }
 | 
| 374 |                   ysh-if        if (x > 0) { echo }
 | 
| 375 |   [YSH Iter]      ysh-while     while (x > 0) { echo }
 | 
| 376 |                   ysh-for       for i, item in (mylist) { echo }
 | 
| 377 | )zZXx");
 | 
| 378 | 
 | 
| 379 | GLOBAL_STR(gStr22, R"zZXx(
 | 
| 380 |   Expression Language and Assignments <a class="group-link" href="chap-expr-lang.html">expr-lang</a>
 | 
| 381 | 
 | 
| 382 | 
 | 
| 383 |   [Assignment]    assign        =
 | 
| 384 |                   aug-assign    +=   -=   *=   /=   **=   //=   %=
 | 
| 385 |                                 &=   |=   ^=   <<=   >>=
 | 
| 386 |   [Literals]      atom-literal  true   false   null
 | 
| 387 |                   int-literal   42  65_536  0xFF  0o755  0b10
 | 
| 388 |                   float-lit     3.14  1.5e-10
 | 
| 389 |                   char-literal  \\ \t \"   \y00   \u{3bc}
 | 
| 390 |                 X num-suffix    42 K Ki M Mi G Gi T Ti / ms us
 | 
| 391 |                   ysh-string    "x is $x"  $"x is $x"   r'[a-z]\n'
 | 
| 392 |                                 u'line\n'  b'byte \yff'
 | 
| 393 |                   triple-quoted """  $"""  r'''  u'''  b'''
 | 
| 394 |                   str-template  ^"$a and $b" for Str::replace()
 | 
| 395 |                   list-literal  ['one', 'two', 3]  :| unquoted words |
 | 
| 396 |                   dict-literal  {name: 'bob'}  {a, b}
 | 
| 397 |                   range         1 .. n+1
 | 
| 398 |                   block-expr    ^(echo $PWD)
 | 
| 399 |                   expr-literal  ^[1 + 2*3]
 | 
| 400 |                 X expr-sub      $[myobj]
 | 
| 401 |                 X expr-splice   @[myobj]
 | 
| 402 |   [Operators]     op-precedence Like Python
 | 
| 403 |                   concat        s1 ++ s2,  L1 ++ L2
 | 
| 404 |                   ysh-equals    ===   !==   ~==   is, is not
 | 
| 405 |                   ysh-in        in, not in
 | 
| 406 |                   ysh-compare   <  <=  >  >=  (numbers only)
 | 
| 407 |                   ysh-logical   not  and  or
 | 
| 408 |                   ysh-arith     +  -  *  /  //  %   ** 
 | 
| 409 |                   ysh-bitwise   ~  &  |  ^  <<  >>
 | 
| 410 |                   ysh-ternary   '+' if x >= 0 else '-'
 | 
| 411 |                   ysh-index     s[0]  mylist[3]  mydict['key']
 | 
| 412 |                   ysh-attr      mydict.key
 | 
| 413 |                   ysh-slice     a[1:-1]  s[1:-1]
 | 
| 414 |                   func-call     f(x, y; ...named)
 | 
| 415 |                   thin-arrow    mylist->pop()
 | 
| 416 |                   fat-arrow     mystr => startsWith('prefix')
 | 
| 417 |                   match-ops     ~   !~   ~~   !~~
 | 
| 418 |   [Eggex]         re-literal    / d+ ; re-flags ; ERE /
 | 
| 419 |                   re-primitive  %zero    'sq'
 | 
| 420 |                   class-literal [c a-z 'abc' @str_var \\ \xFF \u{3bc}]
 | 
| 421 |                   named-class    dot   digit   space   word   d  s  w
 | 
| 422 |                   re-repeat     d?   d*   d+   d{3}   d{2,4}
 | 
| 423 |                   re-compound    seq1 seq2   alt1|alt2   (expr1 expr2)
 | 
| 424 |                   re-capture    <capture d+ as name: int>
 | 
| 425 |                   re-splice     Subpattern   @subpattern
 | 
| 426 |                   re-flags      reg_icase   reg_newline
 | 
| 427 |                 X re-multiline  ///
 | 
| 428 | )zZXx");
 | 
| 429 | 
 | 
| 430 | GLOBAL_STR(gStr23, R"zZXx(
 | 
| 431 |   Front End <a class="group-link" href="chap-front-end.html">front-end</a>
 | 
| 432 | 
 | 
| 433 | 
 | 
| 434 |   [Usage]         oils-usage                   ysh-usage
 | 
| 435 |   [Lexing]        ascii-whitespace [ \t\r\n]
 | 
| 436 |                   doc-comment ###              multiline-command ...
 | 
| 437 |   [Tools]         cat-em
 | 
| 438 | )zZXx");
 | 
| 439 | 
 | 
| 440 | GLOBAL_STR(gStr24, R"zZXx(
 | 
| 441 |   Other Mini Languages <a class="group-link" href="chap-mini-lang.html">mini-lang</a>
 | 
| 442 | 
 | 
| 443 | 
 | 
| 444 |   [Patterns]      glob-pat      *.py
 | 
| 445 |   [Other Sublang] braces        {alice,bob}@example.com
 | 
| 446 | )zZXx");
 | 
| 447 | 
 | 
| 448 | GLOBAL_STR(gStr25, R"zZXx(
 | 
| 449 |   Global Shell Options <a class="group-link" href="chap-option.html">option</a>
 | 
| 450 | 
 | 
| 451 | 
 | 
| 452 |   [Groups]       strict:all      ysh:upgrade     ysh:all
 | 
| 453 |   [YSH Details]  opts-redefine   opts-internal
 | 
| 454 | )zZXx");
 | 
| 455 | 
 | 
| 456 | GLOBAL_STR(gStr26, R"zZXx(
 | 
| 457 |   Plugins and Hooks <a class="group-link" href="chap-plugin.html">plugin</a>
 | 
| 458 | 
 | 
| 459 | 
 | 
| 460 |   [YSH]   renderPrompt()
 | 
| 461 | )zZXx");
 | 
| 462 | 
 | 
| 463 | GLOBAL_STR(gStr27, R"zZXx(
 | 
| 464 |   Special Variables <a class="group-link" href="chap-special-var.html">special-var</a>
 | 
| 465 | 
 | 
| 466 | 
 | 
| 467 |   [YSH Vars]      ARGV              X ENV                 X _ESCAPE
 | 
| 468 |                   _this_dir
 | 
| 469 |   [YSH Status]    _error
 | 
| 470 |                   _pipeline_status    _process_sub_status
 | 
| 471 |   [YSH Tracing]   SHX_indent          SHX_punct             SHX_pid_str
 | 
| 472 |   [YSH read]      _reply
 | 
| 473 |   [History]       YSH_HISTFILE
 | 
| 474 |   [Oils VM]       OILS_VERSION
 | 
| 475 |                   OILS_GC_THRESHOLD   OILS_GC_ON_EXIT
 | 
| 476 |                   OILS_GC_STATS       OILS_GC_STATS_FD
 | 
| 477 |                   LIB_YSH
 | 
| 478 |   [Float]         NAN                 INFINITY
 | 
| 479 | )zZXx");
 | 
| 480 | 
 | 
| 481 | GLOBAL_STR(gStr28, R"zZXx(
 | 
| 482 |   Standard Library<a class="group-link" href="chap-stdlib.html">stdlib</a>
 | 
| 483 | 
 | 
| 484 | 
 | 
| 485 |   [math]          abs()     
 | 
| 486 |                   max()     min()
 | 
| 487 |                 X round()
 | 
| 488 |                   sum()     
 | 
| 489 |   [list]          all()     any()     
 | 
| 490 |                   repeat()
 | 
| 491 |   [args]          parser                 Parse command line arguments
 | 
| 492 |                   flag
 | 
| 493 |                   arg
 | 
| 494 |                   rest
 | 
| 495 |                   parseArgs()
 | 
| 496 |   [yblocks]       yb-capture
 | 
| 497 |                   yb-capture-2
 | 
| 498 | X [Lines]         slurp-by               combine adjacent lines into cells
 | 
| 499 | X [Awk]           each-line              --j8 --max-jobs (Str, Template, Block) - xargs
 | 
| 500 |                   each-row               --max-jobs (Str, Template, Block) - xargs
 | 
| 501 |                   each-word              xargs-like splitting, similar to IFS too
 | 
| 502 |                   split-by               (str=\n, ifs=':', pattern=/s+/)
 | 
| 503 |                   if-split-by  
 | 
| 504 |                   chop                   alias for split-by (pattern=/s+/)
 | 
| 505 |                   must-match             (/ <capture d+> </capture w+> /)
 | 
| 506 |                   if-match               
 | 
| 507 | X [Table Create]  table                  --by-row --by-col (&place); construct/parse a table
 | 
| 508 |                   table/cols             cols name age - cols name:Str age:Int
 | 
| 509 |                   types                  type       Str Int
 | 
| 510 |                   attr                   attr units -   secs
 | 
| 511 |                   row                    emit row
 | 
| 512 |                   table cat              concatenate TSV8
 | 
| 513 |                   table align            to ssv8
 | 
| 514 |                   table tabify           to tsv8
 | 
| 515 |                   table header           (cols = :|name age|, types = :|Str Int|, units = :|- secs|)
 | 
| 516 |                   table slice            e.g. slice (1, -1)   slice (5, 7)
 | 
| 517 |                   table to-tsv           lose type info, and error on \t in cells
 | 
| 518 | X [Table Ops]     where                  subset of rows; dplyr filter()
 | 
| 519 |                   pick                   subset of columns ('select' taken by shell)
 | 
| 520 |                   mutate    transmute    [average = count / sum] - drop the ones that are used?
 | 
| 521 |                   rename                 (bytes='bytes', path='filename')
 | 
| 522 |                   group-by               add a column with a group ID [ext]
 | 
| 523 |                   sort-by                sort by columns; dplyr arrange() [ext]
 | 
| 524 |                   summary                count, sum, histogram, any, all, reduce(), etc. [ext]
 | 
| 525 | )zZXx");
 | 
| 526 | 
 | 
| 527 | GLOBAL_STR(gStr29, R"zZXx(
 | 
| 528 |   Types and Methods <a class="group-link" href="chap-type-method.html">type-method</a>
 | 
| 529 | 
 | 
| 530 | 
 | 
| 531 |   [Atom Types]     Null           Bool
 | 
| 532 |   [Number Types]   Int            Float
 | 
| 533 |   [Str]          X find()         replace()
 | 
| 534 |                    trim()         trimStart()   trimEnd()
 | 
| 535 |                    startsWith()   endsWith()
 | 
| 536 |                    upper()        lower()
 | 
| 537 |                    search()       leftMatch()
 | 
| 538 |   [List]           List/append()  pop()         extend()    indexOf()
 | 
| 539 |                  X insert()     X remove()      reverse()
 | 
| 540 |   [Dict]           keys()         values()    X get()     X erase()
 | 
| 541 |                  X inc()        X accum()
 | 
| 542 |   [Range] 
 | 
| 543 |   [Eggex] 
 | 
| 544 |   [Match]          group()        start()       end()
 | 
| 545 |                  X groups()     X groupDict()
 | 
| 546 |   [Place]          setValue()
 | 
| 547 |   [Code Types]     Expr           Command
 | 
| 548 |                    BuiltinFunc    BoundFunc
 | 
| 549 | X [Func]           name()         location()    toJson()
 | 
| 550 | X [Proc]           name()         location()    toJson()
 | 
| 551 | X [Module]         name()         filename()
 | 
| 552 |   [IO]           X eval()       X captureStdout()
 | 
| 553 |                    promptVal()
 | 
| 554 |                  X time()       X strftime()
 | 
| 555 |                  X glob()
 | 
| 556 | X [Guts]           heapId()
 | 
| 557 | )zZXx");
 | 
| 558 | 
 | 
| 559 | GLOBAL_STR(gStr30, R"zZXx(bin/ysh is the shell with data tYpes, influenced by pYthon, JavaScript, ...
 | 
| 560 | 
 | 
| 561 | Usage: ysh FLAG* SCRIPT ARG*
 | 
| 562 |        ysh FLAG* -c COMMAND ARG*
 | 
| 563 |        ysh FLAG*
 | 
| 564 | 
 | 
| 565 | `bin/ysh` is the same as `bin/osh` with a the `ysh:all` option group set.  So
 | 
| 566 | `bin/ysh` also accepts shell flags.
 | 
| 567 | 
 | 
| 568 |     ysh -c 'echo hi'
 | 
| 569 |     ysh myscript.ysh
 | 
| 570 |     echo 'echo hi' | ysh
 | 
| 571 | )zZXx");
 | 
| 572 | 
 | 
| 573 | GLOBAL_STR(gStr31, R"zZXx(
 | 
| 574 |   Word Language <a class="group-link" href="chap-word-lang.html">word-lang</a>
 | 
| 575 | 
 | 
| 576 | 
 | 
| 577 |   [Quotes]        ysh-string    "x is $x"  $"x is $x"  r'[a-z]\n'
 | 
| 578 |                                 u'line\n'  b'byte \yff'
 | 
| 579 |                   triple-quoted """  $"""  r'''  u'''  b'''
 | 
| 580 |                 X tagged-str    "<span id=$x>"html
 | 
| 581 |   [Substitutions] expr-sub      echo $[42 + a[i]]
 | 
| 582 |                   expr-splice   echo @[split(x)]
 | 
| 583 |                   var-splice    @myarray @ARGV
 | 
| 584 |                   command-sub   @(split command)
 | 
| 585 |   [Formatting]  X ysh-printf    ${x %.3f}
 | 
| 586 |                 X ysh-format    ${x|html}
 | 
| 587 | )zZXx");
 | 
| 588 | 
 | 
| 589 | GLOBAL_STR(gStr32, R"zZXx(
 | 
| 590 |   YSH Command Language Keywords <a class="group-link" href="chap-ysh-cmd.html">ysh-cmd</a>
 | 
| 591 | 
 | 
| 592 | 
 | 
| 593 |   [Assignment]    const   var   Declare variables
 | 
| 594 |                   setvar        setvar a[i] = 42
 | 
| 595 |                   setglobal     setglobal d.key = 'foo'
 | 
| 596 |   [Expression]    equal =       = 1 + 2*3
 | 
| 597 |                   call          call mylist->append(42)
 | 
| 598 |   [Definitions]   proc          proc p (s, ...rest) {
 | 
| 599 |                                 typed proc p (; typed, ...rest; n=0; b) {
 | 
| 600 |                   func          func f(x; opt1, opt2) { return (x + 1) }
 | 
| 601 |                   ysh-return    return (myexpr)
 | 
| 602 | )zZXx");
 | 
| 603 | 
 | 
| 604 | GLOBAL_STR(gStr33, R"zZXx(func identity(x) {
 | 
| 605 |   ## The identity function. Returns its argument.
 | 
| 606 | 
 | 
| 607 |   return (x)
 | 
| 608 | }
 | 
| 609 | )zZXx");
 | 
| 610 | 
 | 
| 611 | GLOBAL_STR(gStr34, R"zZXx(# Can we define methods in pure YSH?
 | 
| 612 | #
 | 
| 613 | # (mylist->find(42) !== -1)
 | 
| 614 | #
 | 
| 615 | #   instead of 
 | 
| 616 | #
 | 
| 617 | # ('42' in mylist)
 | 
| 618 | #
 | 
| 619 | # Because 'in' is for Dict
 | 
| 620 | 
 | 
| 621 | func find (haystack List, needle) {
 | 
| 622 |   for i, x in (haystack) {
 | 
| 623 |     if (x === needle) {
 | 
| 624 |       return (i)
 | 
| 625 |     }
 | 
| 626 |   }
 | 
| 627 |   return (-1)
 | 
| 628 | }
 | 
| 629 | )zZXx");
 | 
| 630 | 
 | 
| 631 | GLOBAL_STR(gStr35, R"zZXx(# Bash strict mode, updated for 2024
 | 
| 632 | 
 | 
| 633 | set -o nounset
 | 
| 634 | set -o pipefail
 | 
| 635 | set -o errexit
 | 
| 636 | shopt -s inherit_errexit
 | 
| 637 | shopt -s strict:all 2>/dev/null || true  # dogfood for OSH
 | 
| 638 | 
 | 
| 639 | )zZXx");
 | 
| 640 | 
 | 
| 641 | GLOBAL_STR(gStr36, R"zZXx(# Library to turn a shell file into a "BYO test server"
 | 
| 642 | #
 | 
| 643 | # Usage:
 | 
| 644 | #
 | 
| 645 | #   # from both bash and OSH
 | 
| 646 | #   if test -z "$LIB_OSH"; then LIB_OSH=stdlib/osh; fi
 | 
| 647 | #   source $LIB_OSH/byo-server-lib.sh
 | 
| 648 | #
 | 
| 649 | # The client creates a clean process state and directory state for each tests.
 | 
| 650 | #
 | 
| 651 | # (This file requires compgen -A, and maybe declare -f, so it's not POSIX
 | 
| 652 | # shell.)
 | 
| 653 | 
 | 
| 654 | : ${LIB_OSH:-stdlib/osh}
 | 
| 655 | source $LIB_OSH/two.sh
 | 
| 656 | 
 | 
| 657 | byo-maybe-run() {
 | 
| 658 |   local command=${BYO_COMMAND:-}
 | 
| 659 | 
 | 
| 660 |   case $command in
 | 
| 661 |     '')
 | 
| 662 |       # Do nothing if it's not specified
 | 
| 663 |       return 
 | 
| 664 |       ;;
 | 
| 665 | 
 | 
| 666 |     detect)
 | 
| 667 |       # all the commands supported, except 'detect'
 | 
| 668 |       echo list-tests
 | 
| 669 |       echo run-test
 | 
| 670 | 
 | 
| 671 |       exit 66  # ASCII code for 'B' - what the protocol specifies
 | 
| 672 |       ;;
 | 
| 673 | 
 | 
| 674 |     list-tests)
 | 
| 675 |       # bash extension that OSH also implements
 | 
| 676 |       compgen -A function | grep '^test-'
 | 
| 677 |       exit 0
 | 
| 678 |       ;;
 | 
| 679 | 
 | 
| 680 |     run-test)
 | 
| 681 |       local test_name=${BYO_ARG:-}
 | 
| 682 |       if test -z "$test_name"; then
 | 
| 683 |         die "BYO run-test: Expected BYO_ARG"
 | 
| 684 |       fi
 | 
| 685 | 
 | 
| 686 |       # Shell convention: we name functions test-*
 | 
| 687 |       $test_name
 | 
| 688 | 
 | 
| 689 |       # Only run if not set -e.  Either way it's equivalent
 | 
| 690 |       exit $?
 | 
| 691 |       ;;
 | 
| 692 | 
 | 
| 693 |     *)
 | 
| 694 |       die "Invalid BYO command '$command'"
 | 
| 695 |       ;;
 | 
| 696 |   esac
 | 
| 697 | 
 | 
| 698 |   # Do nothing if BYO_COMMAND is not set.
 | 
| 699 |   # The program continues to its "main".
 | 
| 700 | }
 | 
| 701 | 
 | 
| 702 | byo-must-run() {
 | 
| 703 |   local command=${BYO_COMMAND:-}
 | 
| 704 |   if test -z "$command"; then
 | 
| 705 |     die "Expected BYO_COMMAND= in environment"
 | 
| 706 |   fi
 | 
| 707 | 
 | 
| 708 |   byo-maybe-run
 | 
| 709 | }
 | 
| 710 | )zZXx");
 | 
| 711 | 
 | 
| 712 | GLOBAL_STR(gStr37, R"zZXx(#!/usr/bin/env bash
 | 
| 713 | #
 | 
| 714 | # Testing library for bash and OSH.
 | 
| 715 | #
 | 
| 716 | # Capture status/stdout/stderr, and nq-assert those values.
 | 
| 717 | 
 | 
| 718 | : ${LIB_OSH=stdlib/osh}
 | 
| 719 | source $LIB_OSH/two.sh
 | 
| 720 | 
 | 
| 721 | nq-assert() {
 | 
| 722 |   ### Assertion with same syntax as shell 'test'
 | 
| 723 | 
 | 
| 724 |   if ! test "$@"; then
 | 
| 725 |     die "line ${BASH_LINENO[0]}: nq-assert $(printf '%q ' "$@") failed"
 | 
| 726 |   fi
 | 
| 727 | }
 | 
| 728 | 
 | 
| 729 | # Problem: we want to capture status and stdout at the same time
 | 
| 730 | #
 | 
| 731 | # We use:
 | 
| 732 | #
 | 
| 733 | #  __stdout=$(set -o errexit; "$@")
 | 
| 734 | #  __status=$?
 | 
| 735 | #
 | 
| 736 | # However, we lose the trailing \n, since that's how command subs work.
 | 
| 737 | 
 | 
| 738 | # Here is another possibility:
 | 
| 739 | #
 | 
| 740 | # shopt -s lastpipe  # need this too
 | 
| 741 | # ( set -o errexit; "$@" ) | read -r -d __stdout
 | 
| 742 | # __status=${PIPESTATUS[0]}
 | 
| 743 | # shopt -u lastpipe
 | 
| 744 | #
 | 
| 745 | # But this feels complex for just the \n issue, which can be easily worked
 | 
| 746 | # around.
 | 
| 747 | 
 | 
| 748 | nq-run() {
 | 
| 749 |   ### capture status only
 | 
| 750 | 
 | 
| 751 |   local -n out_status=$1
 | 
| 752 |   shift
 | 
| 753 | 
 | 
| 754 |   local __status
 | 
| 755 | 
 | 
| 756 |   # Tricky: turn errexit off so we can capture it, but turn it on against
 | 
| 757 |   set +o errexit
 | 
| 758 |   ( set -o errexit; "$@" )
 | 
| 759 |   __status=$?
 | 
| 760 |   set -o errexit
 | 
| 761 | 
 | 
| 762 |   out_status=$__status
 | 
| 763 | }
 | 
| 764 | 
 | 
| 765 | nq-capture() {
 | 
| 766 |   ### capture status and stdout
 | 
| 767 | 
 | 
| 768 |   local -n out_status=$1
 | 
| 769 |   local -n out_stdout=$2
 | 
| 770 |   shift 2
 | 
| 771 | 
 | 
| 772 |   local __status
 | 
| 773 |   local __stdout
 | 
| 774 | 
 | 
| 775 |   # Tricky: turn errexit off so we can capture it, but turn it on against
 | 
| 776 |   set +o errexit
 | 
| 777 |   __stdout=$(set -o errexit; "$@")
 | 
| 778 |   __status=$?
 | 
| 779 |   set -o errexit
 | 
| 780 | 
 | 
| 781 |   out_status=$__status
 | 
| 782 |   out_stdout=$__stdout
 | 
| 783 | }
 | 
| 784 | 
 | 
| 785 | nq-capture-2() {
 | 
| 786 |   ### capture status and stderr 
 | 
| 787 |   
 | 
| 788 |   # This is almost identical to the above
 | 
| 789 | 
 | 
| 790 |   local -n out_status=$1
 | 
| 791 |   local -n out_stderr=$2
 | 
| 792 |   shift 2
 | 
| 793 | 
 | 
| 794 |   local __status
 | 
| 795 |   local __stderr
 | 
| 796 | 
 | 
| 797 |   # Tricky: turn errexit off so we can capture it, but turn it on against
 | 
| 798 |   set +o errexit
 | 
| 799 |   __stderr=$(set -o errexit; "$@" 2>&1)
 | 
| 800 |   __status=$?
 | 
| 801 |   set -o errexit
 | 
| 802 | 
 | 
| 803 |   out_status=$__status
 | 
| 804 |   out_stderr=$__stderr
 | 
| 805 | }
 | 
| 806 | 
 | 
| 807 | # 'byo test' can set this?
 | 
| 808 | : ${NQ_TEST_TEMP=/tmp}
 | 
| 809 | 
 | 
| 810 | nq-redir() {
 | 
| 811 |   ### capture status and stdout
 | 
| 812 | 
 | 
| 813 |   local -n out_status=$1
 | 
| 814 |   local -n out_stdout_file=$2
 | 
| 815 |   shift 2
 | 
| 816 | 
 | 
| 817 |   local __status
 | 
| 818 |   local __stdout_file=$NQ_TEST_TEMP/nq-redir-$$.txt
 | 
| 819 | 
 | 
| 820 |   # Tricky: turn errexit off so we can capture it, but turn it on against
 | 
| 821 |   set +o errexit
 | 
| 822 |   ( set -o errexit; "$@" ) > $__stdout_file
 | 
| 823 |   __status=$?
 | 
| 824 |   set -o errexit
 | 
| 825 | 
 | 
| 826 |   out_status=$__status
 | 
| 827 |   out_stdout_file=$__stdout_file
 | 
| 828 | }
 | 
| 829 | 
 | 
| 830 | nq-redir-2() {
 | 
| 831 |   ### capture status and stdout
 | 
| 832 | 
 | 
| 833 |   local -n out_status=$1
 | 
| 834 |   local -n out_stderr_file=$2
 | 
| 835 |   shift 2
 | 
| 836 | 
 | 
| 837 |   local __status
 | 
| 838 |   local __stderr_file=$NQ_TEST_TEMP/nq-redir-$$.txt
 | 
| 839 | 
 | 
| 840 |   # Tricky: turn errexit off so we can capture it, but turn it on against
 | 
| 841 |   set +o errexit
 | 
| 842 |   ( set -o errexit; "$@" ) 2> $__stderr_file
 | 
| 843 |   __status=$?
 | 
| 844 |   set -o errexit
 | 
| 845 | 
 | 
| 846 |   out_status=$__status
 | 
| 847 |   out_stderr_file=$__stderr_file
 | 
| 848 | }
 | 
| 849 | )zZXx");
 | 
| 850 | 
 | 
| 851 | GLOBAL_STR(gStr38, R"zZXx(#!/usr/bin/env bash
 | 
| 852 | #
 | 
| 853 | # Common shell functions for task scripts.
 | 
| 854 | #
 | 
| 855 | # Usage:
 | 
| 856 | #   source $LIB_OSH/task-five.sh
 | 
| 857 | #
 | 
| 858 | #   test-foo() {  # define task functions
 | 
| 859 | #     echo foo
 | 
| 860 | #   }
 | 
| 861 | #   task-five "$@"
 | 
| 862 | 
 | 
| 863 | # Definition of a "task"
 | 
| 864 | #
 | 
| 865 | # - File invokes task-five "$@"
 | 
| 866 | #   - or maybe you can look at its source
 | 
| 867 | # - It's a shell function
 | 
| 868 | #   - Has ### docstring
 | 
| 869 | #   - Doesn't start with _
 | 
| 870 | 
 | 
| 871 | : ${LIB_OSH=stdlib/osh}
 | 
| 872 | source $LIB_OSH/byo-server.sh
 | 
| 873 | 
 | 
| 874 | 
 | 
| 875 | # List all functions defined in this file (and not in sourced files).
 | 
| 876 | _bash-print-funcs() {
 | 
| 877 |   ### Print shell functions in this file that don't start with _ (bash reflection)
 | 
| 878 | 
 | 
| 879 |   local funcs
 | 
| 880 |   funcs=($(compgen -A function))
 | 
| 881 |   # extdebug makes `declare -F` print the file path, but, annoyingly, only
 | 
| 882 |   # if you pass the function names as arguments.
 | 
| 883 |   shopt -s extdebug
 | 
| 884 |   declare -F "${funcs[@]}" | grep --fixed-strings " $0" | awk '{print $1}'
 | 
| 885 |   shopt -u extdebug
 | 
| 886 | }
 | 
| 887 | 
 | 
| 888 | _gawk-print-funcs() {
 | 
| 889 |   ### Print shell functions in this file that don't start with _ (awk parsing)
 | 
| 890 | 
 | 
| 891 |   # Using gawk because it has match()
 | 
| 892 |   # - doesn't start with _
 | 
| 893 | 
 | 
| 894 |   # space     = / ' '* /
 | 
| 895 |   # shfunc    = / %begin
 | 
| 896 |   #               <capture !['_' ' '] ![' ']*>
 | 
| 897 |   #               '()' space '{' space
 | 
| 898 |   #               %end /
 | 
| 899 |   # docstring = / %begin
 | 
| 900 |   #               space '###' ' '+
 | 
| 901 |   #               <capture dot*>
 | 
| 902 |   #               %end /
 | 
| 903 |   gawk '
 | 
| 904 |   match($0, /^([^_ ][^ ]*)\(\)[ ]*{[ ]*$/, m) {
 | 
| 905 |     #print NR " shfunc " m[1]
 | 
| 906 |     print m[1]
 | 
| 907 |     #print m[0]
 | 
| 908 |   }
 | 
| 909 | 
 | 
| 910 |   match($0, /^[ ]*###[ ]+(.*)$/, m) {
 | 
| 911 |     print NR " docstring " m[1]
 | 
| 912 |   }
 | 
| 913 | ' $0
 | 
| 914 | }
 | 
| 915 | 
 | 
| 916 | _print-funcs() {
 | 
| 917 |   if command -v gawk > /dev/null; then
 | 
| 918 |     _gawk-print-funcs
 | 
| 919 |   else
 | 
| 920 |     _bash-print-funcs
 | 
| 921 |   fi
 | 
| 922 | }
 | 
| 923 | 
 | 
| 924 | _show-help() {
 | 
| 925 |   # TODO:
 | 
| 926 |   # - Use awk to find comments at the top of the file?
 | 
| 927 |   # - Use OSH to extract docstrings
 | 
| 928 |   # - BYO_COMMAND=list-tasks will reuse that logic?  It only applies to the
 | 
| 929 |   #   current file, not anything in a different file?
 | 
| 930 | 
 | 
| 931 |   echo "Usage: $0 TASK_NAME ARGS..."
 | 
| 932 |   echo
 | 
| 933 |   echo "To complete tasks, run:"
 | 
| 934 |   echo "   source devtools/completion.bash"
 | 
| 935 |   echo
 | 
| 936 |   echo "Tasks:"
 | 
| 937 | 
 | 
| 938 |   if command -v column >/dev/null; then
 | 
| 939 |     _print-funcs | column
 | 
| 940 |   else
 | 
| 941 |     _print-funcs
 | 
| 942 |   fi
 | 
| 943 | }
 | 
| 944 | 
 | 
| 945 | task-five() {
 | 
| 946 |   # Respond to BYO_COMMAND=list-tasks, etc.  All task files need this.
 | 
| 947 |   byo-maybe-run
 | 
| 948 | 
 | 
| 949 |   case ${1:-} in
 | 
| 950 |     ''|--help|-h)
 | 
| 951 |       _show-help
 | 
| 952 |       exit 0
 | 
| 953 |       ;;
 | 
| 954 |   esac
 | 
| 955 | 
 | 
| 956 |   if ! declare -f "$1" >/dev/null; then
 | 
| 957 |     echo "$0: '$1' isn't an action in this task file.  Try '$0 --help'"
 | 
| 958 |     exit 1
 | 
| 959 |   fi
 | 
| 960 | 
 | 
| 961 |   "$@"
 | 
| 962 | }
 | 
| 963 | )zZXx");
 | 
| 964 | 
 | 
| 965 | GLOBAL_STR(gStr39, R"zZXx(# Two functions I actually use, all the time.
 | 
| 966 | #
 | 
| 967 | # To keep depenedencies small, this library will NEVER grow other functions
 | 
| 968 | # (and is named to imply that.)
 | 
| 969 | #
 | 
| 970 | # Usage:
 | 
| 971 | #   source --builtin two.sh
 | 
| 972 | #
 | 
| 973 | # Examples:
 | 
| 974 | #    log 'hi'
 | 
| 975 | #    die 'expected a number'
 | 
| 976 | 
 | 
| 977 | if command -v source-guard >/dev/null; then  # include guard for YSH
 | 
| 978 |   source-guard two || return 0
 | 
| 979 | fi
 | 
| 980 | 
 | 
| 981 | log() {
 | 
| 982 |   ### Write a message to stderr.
 | 
| 983 |   echo "$@" >&2
 | 
| 984 | }
 | 
| 985 | 
 | 
| 986 | die() {
 | 
| 987 |   ### Write an error message with the script name, and exit with status 1.
 | 
| 988 |   log "$0: fatal: $@"
 | 
| 989 |   exit 1
 | 
| 990 | }
 | 
| 991 | 
 | 
| 992 | )zZXx");
 | 
| 993 | 
 | 
| 994 | GLOBAL_STR(gStr40, R"zZXx(# These were helpful while implementing args.ysh
 | 
| 995 | # Maybe we will want to export them in a prelude so that others can use them too?
 | 
| 996 | #
 | 
| 997 | # Prior art: Rust has `todo!()` which is quite nice. Other languages allow
 | 
| 998 | # users to `raise NotImplmentedError()`.
 | 
| 999 | 
 | 
| 1000 | # Andy comments:
 | 
| 1001 | # - 'pass' can be : or true in shell.  It's a little obscure / confusing, but
 | 
| 1002 | #   there is an argument for minimalism.  Although I prefer words like 'true',
 | 
| 1003 | #   and that already means something.
 | 
| 1004 | #   - UPDATE: we once took 'pass' as a keyword, but users complained because
 | 
| 1005 | #     there is a command 'pass'.  So we probably can't have this by default.
 | 
| 1006 | #     Need to discuss source --builtin.
 | 
| 1007 | 
 | 
| 1008 | # - todo could be more static?  Rust presumably does it at compile time
 | 
| 1009 | 
 | 
| 1010 | proc todo () {
 | 
| 1011 |   ## Raises a not implemented error when run.
 | 
| 1012 |   error ("TODO: not implemented")  # TODO: is error code 1 ok?
 | 
| 1013 | }
 | 
| 1014 | 
 | 
| 1015 | proc pass () {
 | 
| 1016 |   ## Use when you want to temporarily leave a block empty.
 | 
| 1017 |   _ null
 | 
| 1018 | }
 | 
| 1019 | )zZXx");
 | 
| 1020 | 
 | 
| 1021 | GLOBAL_STR(gStr41, R"zZXx(# testing.ysh
 | 
| 1022 | #
 | 
| 1023 | # Usage:
 | 
| 1024 | #   source --builtin testing.sh
 | 
| 1025 | #
 | 
| 1026 | # func f(x) { return (x + 1) }
 | 
| 1027 | #
 | 
| 1028 | # describe foo {
 | 
| 1029 | #   assert (43 === f(42))
 | 
| 1030 | # }
 | 
| 1031 | #
 | 
| 1032 | # if is-main {
 | 
| 1033 | #   run-tests @ARGV   # --filter
 | 
| 1034 | # }
 | 
| 1035 | 
 | 
| 1036 | module stdlib/testing || return 0
 | 
| 1037 | 
 | 
| 1038 | source --builtin args.ysh
 | 
| 1039 | 
 | 
| 1040 | proc assert ( ; cond ; fail_message='default fail message') {
 | 
| 1041 |   echo 'hi from assert'
 | 
| 1042 | 
 | 
| 1043 |   = cond
 | 
| 1044 | 
 | 
| 1045 |   # I think this might be ready now?
 | 
| 1046 | 
 | 
| 1047 |   var val = evalExpr(cond) 
 | 
| 1048 | 
 | 
| 1049 |   echo
 | 
| 1050 |   echo 'value'
 | 
| 1051 |   = val
 | 
| 1052 |   pp line (val)
 | 
| 1053 | 
 | 
| 1054 |   = fail_message
 | 
| 1055 | 
 | 
| 1056 |   if (val) {
 | 
| 1057 |     echo 'OK'
 | 
| 1058 |   } else {
 | 
| 1059 |     var m = evalExpr(fail_message) 
 | 
| 1060 |     echo "FAIL - this is where we extract the string - $m"
 | 
| 1061 |   }
 | 
| 1062 | }
 | 
| 1063 | 
 | 
| 1064 | proc test-assert {
 | 
| 1065 |   var x = 42
 | 
| 1066 |   assert [42 === x]
 | 
| 1067 | }
 | 
| 1068 | 
 | 
| 1069 | proc test-expr ( ; expr ) {
 | 
| 1070 |   echo 'expr'
 | 
| 1071 |   pp line (expr)
 | 
| 1072 | }
 | 
| 1073 | 
 | 
| 1074 | proc test-named ( ; ; n=^[99] ) {
 | 
| 1075 |   echo 'n'
 | 
| 1076 |   pp line (n)
 | 
| 1077 | }
 | 
| 1078 | 
 | 
| 1079 | # What happens when there are duplicate test IDs?
 | 
| 1080 | #
 | 
| 1081 | # Also I think filter by "$test_id/$case_id"
 | 
| 1082 | 
 | 
| 1083 | proc __it (case_id ; ; ; block) {
 | 
| 1084 |   # This uses a clean directory
 | 
| 1085 |   echo TODO
 | 
| 1086 | }
 | 
| 1087 | 
 | 
| 1088 | # is this accessible to users?
 | 
| 1089 | # It can contain a global list of things to run
 | 
| 1090 | 
 | 
| 1091 | # Naming convention: a proc named 'describe' mutates a global named _describe?
 | 
| 1092 | # Or maybe _describe_list ?
 | 
| 1093 | 
 | 
| 1094 | var _describe_list = []
 | 
| 1095 | 
 | 
| 1096 | proc describe (test_id ; ; ; block) {
 | 
| 1097 |   echo describe
 | 
| 1098 |   #= desc
 | 
| 1099 | 
 | 
| 1100 |   # TODO:
 | 
| 1101 |   # - need append
 | 
| 1102 |   # - need ::
 | 
| 1103 |   # _ _describe->append(cmd)
 | 
| 1104 |   #
 | 
| 1105 |   # Need to clean this up
 | 
| 1106 |   # append (_describe, cmd)  # does NOT work!
 | 
| 1107 | 
 | 
| 1108 |   call _describe_list->append(block)
 | 
| 1109 | }
 | 
| 1110 | 
 | 
| 1111 | proc Args {
 | 
| 1112 |   echo TODO
 | 
| 1113 | }
 | 
| 1114 | 
 | 
| 1115 | # Problem: this creates a global variable?
 | 
| 1116 | Args (&spec) {
 | 
| 1117 |   flag --filter 'Regex of test descriptions'
 | 
| 1118 | }
 | 
| 1119 | 
 | 
| 1120 | proc run-tests {
 | 
| 1121 |   var opt, i = parseArgs(spec, ARGV)
 | 
| 1122 | 
 | 
| 1123 |   # TODO:
 | 
| 1124 |   # - parse --filter foo, which you can use eggex for!
 | 
| 1125 | 
 | 
| 1126 |   for cmd in (_describe) {
 | 
| 1127 |     # TODO: print filename and 'describe' name?
 | 
| 1128 |     try {
 | 
| 1129 |       eval (cmd)
 | 
| 1130 |     }
 | 
| 1131 |     if (_status !== 0) {
 | 
| 1132 |       echo 'failed'
 | 
| 1133 |     }
 | 
| 1134 |   }
 | 
| 1135 | }
 | 
| 1136 | )zZXx");
 | 
| 1137 | 
 | 
| 1138 | GLOBAL_STR(gStr42, R"zZXx(# args.ysh
 | 
| 1139 | #
 | 
| 1140 | # Usage:
 | 
| 1141 | #   source --builtin args.sh
 | 
| 1142 | #
 | 
| 1143 | # parser (&spec) {
 | 
| 1144 | #   flag -v --verbose (help="Verbosely")  # default is Bool, false
 | 
| 1145 | #
 | 
| 1146 | #   flag -P --max-procs ('int', default=-1, doc='''
 | 
| 1147 | #     Run at most P processes at a time
 | 
| 1148 | #     ''')
 | 
| 1149 | #
 | 
| 1150 | #   flag -i --invert ('bool', default=true, doc='''
 | 
| 1151 | #     Long multiline
 | 
| 1152 | #     Description
 | 
| 1153 | #     ''')
 | 
| 1154 | #
 | 
| 1155 | #   arg src (help='Source')
 | 
| 1156 | #   arg dest (help='Dest')
 | 
| 1157 | #   arg times (help='Foo')
 | 
| 1158 | #
 | 
| 1159 | #   rest files
 | 
| 1160 | # }
 | 
| 1161 | #
 | 
| 1162 | # var args = parseArgs(spec, ARGV)
 | 
| 1163 | #
 | 
| 1164 | # echo "Verbose $[args.verbose]"
 | 
| 1165 | 
 | 
| 1166 | # TODO: See list
 | 
| 1167 | # - It would be nice to keep `flag` and `arg` private, injecting them into the
 | 
| 1168 | #   proc namespace only within `Args`
 | 
| 1169 | # - We need "type object" to replace the strings 'int', 'bool', etc.
 | 
| 1170 | # - flag builtin:
 | 
| 1171 | #   - handle only long flag or only short flag
 | 
| 1172 | #   - flag aliases
 | 
| 1173 | 
 | 
| 1174 | proc parser (; place ; ; block_def) {
 | 
| 1175 |   ## Create an args spec which can be passed to parseArgs.
 | 
| 1176 |   ##
 | 
| 1177 |   ## Example:
 | 
| 1178 |   ##
 | 
| 1179 |   ##   # NOTE: &spec will create a variable named spec
 | 
| 1180 |   ##   parser (&spec) {
 | 
| 1181 |   ##     flag -v --verbose ('bool')
 | 
| 1182 |   ##   }
 | 
| 1183 |   ##
 | 
| 1184 |   ##   var args = parseArgs(spec, ARGV)
 | 
| 1185 | 
 | 
| 1186 |   var p = {flags: [], args: []}
 | 
| 1187 |   ctx push (p; ; block_def)
 | 
| 1188 | 
 | 
| 1189 |   # Validate that p.rest = [name] or null and reduce p.rest into name or null.
 | 
| 1190 |   if ('rest' in p) {
 | 
| 1191 |     if (len(p.rest) > 1) {
 | 
| 1192 |       error '`rest` was called more than once' (code=3)
 | 
| 1193 |     } else {
 | 
| 1194 |       setvar p.rest = p.rest[0]
 | 
| 1195 |     }
 | 
| 1196 |   } else {
 | 
| 1197 |     setvar p.rest = null
 | 
| 1198 |   }
 | 
| 1199 | 
 | 
| 1200 |   var names = {}
 | 
| 1201 |   for items in ([p.flags, p.args]) {
 | 
| 1202 |     for x in (items) {
 | 
| 1203 |       if (x.name in names) {
 | 
| 1204 |         error "Duplicate flag/arg name $[x.name] in spec" (code=3)
 | 
| 1205 |       }
 | 
| 1206 | 
 | 
| 1207 |       setvar names[x.name] = null
 | 
| 1208 |     }
 | 
| 1209 |   }
 | 
| 1210 | 
 | 
| 1211 |   # TODO: what about `flag --name` and then `arg name`?
 | 
| 1212 | 
 | 
| 1213 |   call place->setValue(p)
 | 
| 1214 | }
 | 
| 1215 | 
 | 
| 1216 | proc flag (short, long ; type='bool' ; default=null, help=null) {
 | 
| 1217 |   ## Declare a flag within an `arg-parse`.
 | 
| 1218 |   ##
 | 
| 1219 |   ## Examples:
 | 
| 1220 |   ##
 | 
| 1221 |   ##   arg-parse (&spec) {
 | 
| 1222 |   ##     flag -v --verbose
 | 
| 1223 |   ##     flag -n --count ('int', default=1)
 | 
| 1224 |   ##     flag -f --file ('str', help="File to process")
 | 
| 1225 |   ##   }
 | 
| 1226 | 
 | 
| 1227 |   # bool has a default of false, not null
 | 
| 1228 |   if (type === 'bool' and default === null) {
 | 
| 1229 |     setvar default = false
 | 
| 1230 |   }
 | 
| 1231 | 
 | 
| 1232 |   # TODO: validate `type`
 | 
| 1233 | 
 | 
| 1234 |   # TODO: Should use "trimPrefix"
 | 
| 1235 |   var name = long[2:]
 | 
| 1236 | 
 | 
| 1237 |   ctx emit flags ({short, long, name, type, default, help})
 | 
| 1238 | }
 | 
| 1239 | 
 | 
| 1240 | proc arg (name ; ; help=null) {
 | 
| 1241 |   ## Declare a positional argument within an `arg-parse`.
 | 
| 1242 |   ##
 | 
| 1243 |   ## Examples:
 | 
| 1244 |   ##
 | 
| 1245 |   ##   arg-parse (&spec) {
 | 
| 1246 |   ##     arg name
 | 
| 1247 |   ##     arg config (help="config file path")
 | 
| 1248 |   ##   }
 | 
| 1249 | 
 | 
| 1250 |   ctx emit args ({name, help})
 | 
| 1251 | }
 | 
| 1252 | 
 | 
| 1253 | proc rest (name) {
 | 
| 1254 |   ## Take the remaining positional arguments within an `arg-parse`.
 | 
| 1255 |   ##
 | 
| 1256 |   ## Examples:
 | 
| 1257 |   ##
 | 
| 1258 |   ##   arg-parse (&grepSpec) {
 | 
| 1259 |   ##     arg query
 | 
| 1260 |   ##     rest files
 | 
| 1261 |   ##   }
 | 
| 1262 | 
 | 
| 1263 |   # We emit instead of set to detect multiple invocations of "rest"
 | 
| 1264 |   ctx emit rest (name)
 | 
| 1265 | }
 | 
| 1266 | 
 | 
| 1267 | func parseArgs(spec, argv) {
 | 
| 1268 |   ## Given a spec created by `parser`. Parse an array of strings `argv` per
 | 
| 1269 |   ## that spec.
 | 
| 1270 |   ##
 | 
| 1271 |   ## See `parser` for examples of use.
 | 
| 1272 | 
 | 
| 1273 |   var i = 0
 | 
| 1274 |   var positionalPos = 0
 | 
| 1275 |   var argc = len(argv)
 | 
| 1276 |   var args = {}
 | 
| 1277 |   var rest = []
 | 
| 1278 | 
 | 
| 1279 |   var value
 | 
| 1280 |   var found
 | 
| 1281 |   while (i < argc) {
 | 
| 1282 |     var arg = argv[i]
 | 
| 1283 |     if (arg->startsWith('-')) {
 | 
| 1284 |       setvar found = false
 | 
| 1285 | 
 | 
| 1286 |       for flag in (spec.flags) {
 | 
| 1287 |         if ( (flag.short and flag.short === arg) or
 | 
| 1288 |              (flag.long and flag.long === arg) ) {
 | 
| 1289 |           case (flag.type) {
 | 
| 1290 |             ('bool') | (null) { setvar value = true }
 | 
| 1291 |             int {
 | 
| 1292 |               setvar i += 1
 | 
| 1293 |               if (i >= len(argv)) {
 | 
| 1294 |                 error "Expected integer after '$arg'" (code=2)
 | 
| 1295 |               }
 | 
| 1296 | 
 | 
| 1297 |               try { setvar value = int(argv[i]) }
 | 
| 1298 |               if (_status !== 0) {
 | 
| 1299 |                 error "Expected integer after '$arg', got '$[argv[i]]'" (code=2)
 | 
| 1300 |               }
 | 
| 1301 |             }
 | 
| 1302 |           }
 | 
| 1303 | 
 | 
| 1304 |           setvar args[flag.name] = value
 | 
| 1305 |           setvar found = true
 | 
| 1306 |           break
 | 
| 1307 |         }
 | 
| 1308 |       }
 | 
| 1309 | 
 | 
| 1310 |       if (not found) {
 | 
| 1311 |         error "Unknown flag '$arg'" (code=2)
 | 
| 1312 |       }
 | 
| 1313 |     } elif (positionalPos >= len(spec.args)) {
 | 
| 1314 |       if (not spec.rest) {
 | 
| 1315 |         error "Too many arguments, unexpected '$arg'" (code=2)
 | 
| 1316 |       }
 | 
| 1317 | 
 | 
| 1318 |       call rest->append(arg)
 | 
| 1319 |     } else {
 | 
| 1320 |       var pos = spec.args[positionalPos]
 | 
| 1321 |       setvar positionalPos += 1
 | 
| 1322 |       setvar value = arg
 | 
| 1323 |       setvar args[pos.name] = value
 | 
| 1324 |     }
 | 
| 1325 | 
 | 
| 1326 |     setvar i += 1
 | 
| 1327 |   }
 | 
| 1328 | 
 | 
| 1329 |   if (spec.rest) {
 | 
| 1330 |     setvar args[spec.rest] = rest
 | 
| 1331 |   }
 | 
| 1332 | 
 | 
| 1333 |   # Set defaults for flags
 | 
| 1334 |   for flag in (spec.flags) {
 | 
| 1335 |     if (flag.name not in args) {
 | 
| 1336 |       setvar args[flag.name] = flag.default
 | 
| 1337 |     }
 | 
| 1338 |   }
 | 
| 1339 | 
 | 
| 1340 |   # Raise error on missing args
 | 
| 1341 |   for arg in (spec.args) {
 | 
| 1342 |     if (arg.name not in args) {
 | 
| 1343 |       error "Usage Error: Missing required argument $[arg.name]" (code=2)
 | 
| 1344 |     }
 | 
| 1345 |   }
 | 
| 1346 | 
 | 
| 1347 |   return (args)
 | 
| 1348 | }
 | 
| 1349 | )zZXx");
 | 
| 1350 | 
 | 
| 1351 | GLOBAL_STR(gStr43, R"zZXx(func any(list) {
 | 
| 1352 |   ### Returns true if any value in the list is truthy.
 | 
| 1353 |   # Empty list: returns false
 | 
| 1354 | 
 | 
| 1355 |   for item in (list) {
 | 
| 1356 |     if (item) {
 | 
| 1357 |       return (true)
 | 
| 1358 |     }
 | 
| 1359 |   }
 | 
| 1360 |   return (false)
 | 
| 1361 | }
 | 
| 1362 | 
 | 
| 1363 | func all(list) {
 | 
| 1364 |   ## Returns true if all values in the list are truthy.
 | 
| 1365 |   # Empty list: returns true
 | 
| 1366 | 
 | 
| 1367 |   for item in (list) {
 | 
| 1368 |     if (not item) {
 | 
| 1369 |       return (false)
 | 
| 1370 |     }
 | 
| 1371 |   }
 | 
| 1372 |   return (true)
 | 
| 1373 | }
 | 
| 1374 | 
 | 
| 1375 | func sum(list; start=0) {
 | 
| 1376 |   ### Returns the sum of all elements in the list.
 | 
| 1377 |   # Empty list: returns 0
 | 
| 1378 | 
 | 
| 1379 |   var sum = start
 | 
| 1380 |   for item in (list) {
 | 
| 1381 |     setvar sum += item
 | 
| 1382 |   }
 | 
| 1383 |   return (sum)
 | 
| 1384 | }
 | 
| 1385 | 
 | 
| 1386 | func repeat(x, n) {
 | 
| 1387 |   ### Returns a list with the given string or list repeated
 | 
| 1388 | 
 | 
| 1389 |   # Like Python's 'foo'*3 or ['foo', 'bar']*3
 | 
| 1390 |   # negative numbers are like 0 in Python
 | 
| 1391 | 
 | 
| 1392 |   var t = type(x)
 | 
| 1393 |   case (t) {
 | 
| 1394 |     Str {
 | 
| 1395 |       var parts = []
 | 
| 1396 |       for i in (0 .. n) {
 | 
| 1397 |         call parts->append(x)
 | 
| 1398 |       }
 | 
| 1399 |       return (join(parts))
 | 
| 1400 |     }
 | 
| 1401 |     List {
 | 
| 1402 |       var result = []
 | 
| 1403 |       for i in (0 .. n) {
 | 
| 1404 |         call result->extend(x)
 | 
| 1405 |       }
 | 
| 1406 |       return (result)
 | 
| 1407 |     }
 | 
| 1408 |     (else) {
 | 
| 1409 |       error "Expected Str or List, got $t"
 | 
| 1410 |     }
 | 
| 1411 |   }
 | 
| 1412 | }
 | 
| 1413 | )zZXx");
 | 
| 1414 | 
 | 
| 1415 | GLOBAL_STR(gStr44, R"zZXx(func __math_select(list, cmp) {
 | 
| 1416 |   ## Internal helper for `max` and `min`.
 | 
| 1417 |   ##
 | 
| 1418 |   ## NOTE: If `list` is empty, then an error is thrown.
 | 
| 1419 | 
 | 
| 1420 |   if (len(list) === 0) {
 | 
| 1421 |     error "Unexpected empty list" (code=3)
 | 
| 1422 |   }
 | 
| 1423 | 
 | 
| 1424 |   if (len(list) === 1) {
 | 
| 1425 |     return (list[0])
 | 
| 1426 |   }
 | 
| 1427 | 
 | 
| 1428 |   var match = list[0]
 | 
| 1429 |   for i in (1 .. len(list)) {
 | 
| 1430 |     setvar match = cmp(list[i], match)
 | 
| 1431 |   }
 | 
| 1432 |   return (match)
 | 
| 1433 | }
 | 
| 1434 | 
 | 
| 1435 | func max(...args) {
 | 
| 1436 |   ## Compute the maximum of 2 or more values.
 | 
| 1437 |   ##
 | 
| 1438 |   ## `max` takes two different signatures:
 | 
| 1439 |   ##  - `max(a, b)` to return the maximum of `a`, `b`
 | 
| 1440 |   ##  - `max(list)` to return the greatest item in the `list`
 | 
| 1441 |   ##
 | 
| 1442 |   ## So, for example:
 | 
| 1443 |   ##
 | 
| 1444 |   ##   max(1, 2)  # => 2
 | 
| 1445 |   ##   max([1, 2, 3])  # => 3
 | 
| 1446 | 
 | 
| 1447 |   case (len(args)) {
 | 
| 1448 |     (1) { return (__math_select(args[0], max)) }
 | 
| 1449 |     (2) {
 | 
| 1450 |       if (args[0] > args[1]) {
 | 
| 1451 |         return (args[0])
 | 
| 1452 |       } else {
 | 
| 1453 |         return (args[1])
 | 
| 1454 |       }
 | 
| 1455 |     }
 | 
| 1456 |     (else) { error "max expects 1 or 2 args" (code=3) }
 | 
| 1457 |   }
 | 
| 1458 | }
 | 
| 1459 | 
 | 
| 1460 | func min(...args) {
 | 
| 1461 |   ## Compute the minimum of 2 or more values.
 | 
| 1462 |   ##
 | 
| 1463 |   ## `min` takes two different signatures:
 | 
| 1464 |   ##  - `min(a, b)` to return the minimum of `a`, `b`
 | 
| 1465 |   ##  - `min(list)` to return the least item in the `list`
 | 
| 1466 |   ##
 | 
| 1467 |   ## So, for example:
 | 
| 1468 |   ##
 | 
| 1469 |   ##   min(2, 3)  # => 2
 | 
| 1470 |   ##   max([1, 2, 3])  # => 1
 | 
| 1471 | 
 | 
| 1472 |   case (len(args)) {
 | 
| 1473 |     (1) { return (__math_select(args[0], min)) }
 | 
| 1474 |     (2) {
 | 
| 1475 |       if (args[0] < args[1]) {
 | 
| 1476 |         return (args[0])
 | 
| 1477 |       } else {
 | 
| 1478 |         return (args[1])
 | 
| 1479 |       }
 | 
| 1480 |     }
 | 
| 1481 |     (else) { error "min expects 1 or 2 args" (code=3) }
 | 
| 1482 |   }
 | 
| 1483 | }
 | 
| 1484 | 
 | 
| 1485 | func abs(x) {
 | 
| 1486 |   ## Compute the absolute (positive) value of a number (float or int).
 | 
| 1487 | 
 | 
| 1488 |   if (x < 0) {
 | 
| 1489 |     return (-x)
 | 
| 1490 |   } else {
 | 
| 1491 |     return (x)
 | 
| 1492 |   }
 | 
| 1493 | }
 | 
| 1494 | )zZXx");
 | 
| 1495 | 
 | 
| 1496 | GLOBAL_STR(gStr45, R"zZXx(# stream.ysh
 | 
| 1497 | #
 | 
| 1498 | # Usage:
 | 
| 1499 | #   source --builtin stream.ysh
 | 
| 1500 | #
 | 
| 1501 | # For reading lines, decoding, extracting, splitting
 | 
| 1502 | 
 | 
| 1503 | # make this file a test server
 | 
| 1504 | source $LIB_OSH/byo-server.sh
 | 
| 1505 | 
 | 
| 1506 | source $LIB_YSH/args.ysh
 | 
| 1507 | 
 | 
| 1508 | proc slurp-by (; num_lines) {
 | 
| 1509 |   # TODO: (stdin)
 | 
| 1510 |   for line in (stdin) {
 | 
| 1511 |     echo TODO
 | 
| 1512 |   }
 | 
| 1513 | }
 | 
| 1514 | 
 | 
| 1515 | # Note:
 | 
| 1516 | # - these are all the same algorithm
 | 
| 1517 | # - also word, block, etc. are all optional
 | 
| 1518 | 
 | 
| 1519 | proc each-line (...words; template=null; ; block=null) {
 | 
| 1520 |   # TODO: 
 | 
| 1521 |   # parse --j8 --max-jobs flag
 | 
| 1522 | 
 | 
| 1523 |   # parse template_str as string
 | 
| 1524 |   # TODO: this is dangerous though ... because you can execute code
 | 
| 1525 |   # I think you need a SAFE version
 | 
| 1526 | 
 | 
| 1527 |   # evaluate template string expression - I guess that allows $(echo hi) and so
 | 
| 1528 |   # forth
 | 
| 1529 | 
 | 
| 1530 |   # evaluate block with _line binding
 | 
| 1531 |   # block: execute in parallel with --max-jobs
 | 
| 1532 | 
 | 
| 1533 |   for line in (stdin) {
 | 
| 1534 |     echo TODO
 | 
| 1535 |   }
 | 
| 1536 | }
 | 
| 1537 | 
 | 
| 1538 | proc test-each-line {
 | 
| 1539 |   echo 'TODO: need basic test runner'
 | 
| 1540 | 
 | 
| 1541 |   # ysh-tool test stream.ysh
 | 
| 1542 |   # 
 | 
| 1543 |   # Col
 | 
| 1544 | 
 | 
| 1545 | 
 | 
| 1546 | }
 | 
| 1547 | 
 | 
| 1548 | proc each-row (; ; block) {
 | 
| 1549 |   echo TODO
 | 
| 1550 | }
 | 
| 1551 | 
 | 
| 1552 | proc split-by (; ifs=null; block) {
 | 
| 1553 |   echo TODO
 | 
| 1554 | }
 | 
| 1555 | 
 | 
| 1556 | proc if-split-by (; ifs=null; block) {
 | 
| 1557 |   echo TODO
 | 
| 1558 | }
 | 
| 1559 | 
 | 
| 1560 | proc chop () {
 | 
| 1561 |   ### alias for if-split-by
 | 
| 1562 |   echo TODO
 | 
| 1563 | }
 | 
| 1564 | 
 | 
| 1565 | proc must-match (; pattern; block) {
 | 
| 1566 |   echo TODO
 | 
| 1567 | }
 | 
| 1568 | 
 | 
| 1569 | proc if-match (; pattern; block) {
 | 
| 1570 |   echo TODO
 | 
| 1571 | }
 | 
| 1572 | 
 | 
| 1573 | # Protocol:
 | 
| 1574 | #
 | 
| 1575 | # - The file lists its tests the "actions"
 | 
| 1576 | # - Then the test harness runs them
 | 
| 1577 | # - But should it be ENV vars
 | 
| 1578 | #
 | 
| 1579 | # - BYO_LIST_TESTS=1
 | 
| 1580 | # - BYO_RUN_TEST=foo
 | 
| 1581 | # - $PWD is a CLEAN temp dir, the process doesn't have to do anything
 | 
| 1582 | 
 | 
| 1583 | #   - silent on success, but prints file on output
 | 
| 1584 | #   - OK this makes sense
 | 
| 1585 | #
 | 
| 1586 | # The trivial test in Python:
 | 
| 1587 | #   
 | 
| 1588 | # from test import byo
 | 
| 1589 | # byo.maybe_main()
 | 
| 1590 | #
 | 
| 1591 | # bash library:
 | 
| 1592 | #  source --builtin byo-server.sh
 | 
| 1593 | #
 | 
| 1594 | # byo-maybe-main   # reads env variables, and then exits
 | 
| 1595 | #
 | 
| 1596 | #  source --builtin assertions.ysh
 | 
| 1597 | #
 | 
| 1598 | # assert-ok 'echo hi'
 | 
| 1599 | # assert-stdout 'hi' 'echo -n hi'
 | 
| 1600 | #
 | 
| 1601 | # "$@"
 | 
| 1602 | #
 | 
| 1603 | # Run all tests
 | 
| 1604 | # util/byo-client.sh run-tests $YSH stdlib/table.ysh
 | 
| 1605 | # util/byo-client.sh run-tests -f x $YSH stdlib/table.ysh
 | 
| 1606 | 
 | 
| 1607 | # Clean process
 | 
| 1608 | # Clean working dir
 | 
| 1609 | 
 | 
| 1610 | #
 | 
| 1611 | # Stream Protocol:
 | 
| 1612 | #     #.byo - is this she-dot, that's for a file
 | 
| 1613 | # Do we need metadata?
 | 
| 1614 | #
 | 
| 1615 | 
 | 
| 1616 | # The harness
 | 
| 1617 | #
 | 
| 1618 | # It's process based testing.
 | 
| 1619 | #
 | 
| 1620 | # Test runner process: bash or OSH (unlike sharness!)
 | 
| 1621 | # Tested process: any language - bash, 
 | 
| 1622 | #
 | 
| 1623 | # Key point: you don't have to quote shell code?
 | 
| 1624 | 
 | 
| 1625 | list-byo-tests() {
 | 
| 1626 |   echo TODO
 | 
| 1627 | }
 | 
| 1628 | 
 | 
| 1629 | run-byo-tests() {
 | 
| 1630 |   # source it
 | 
| 1631 |   echo TODO
 | 
| 1632 | }
 | 
| 1633 | 
 | 
| 1634 | byo-maybe-run
 | 
| 1635 | )zZXx");
 | 
| 1636 | 
 | 
| 1637 | GLOBAL_STR(gStr46, R"zZXx(# table.ysh - Library for tables.
 | 
| 1638 | #
 | 
| 1639 | # Usage:
 | 
| 1640 | #   source --builtin table.ysh
 | 
| 1641 | 
 | 
| 1642 | # make this file a test server
 | 
| 1643 | source --builtin osh/byo-server.sh
 | 
| 1644 | 
 | 
| 1645 | proc table (...words; place; ; block) {
 | 
| 1646 |   var n = len(words)
 | 
| 1647 | 
 | 
| 1648 |   # TODO: parse flags
 | 
| 1649 |   #
 | 
| 1650 |   # --by-row
 | 
| 1651 |   # --by-col
 | 
| 1652 |   #
 | 
| 1653 |   # Place is optional
 | 
| 1654 | 
 | 
| 1655 |   if (n === 0) {
 | 
| 1656 |     echo TODO
 | 
| 1657 |     return
 | 
| 1658 |   }
 | 
| 1659 | 
 | 
| 1660 |   var action = words[0]
 | 
| 1661 | 
 | 
| 1662 |   # textual operations
 | 
| 1663 |   case (action) {
 | 
| 1664 |     cat {
 | 
| 1665 |       echo todo
 | 
| 1666 |     }
 | 
| 1667 |     align {
 | 
| 1668 |       echo todo
 | 
| 1669 |     }
 | 
| 1670 |     tabify {
 | 
| 1671 |       echo todo
 | 
| 1672 |     }
 | 
| 1673 |     tabify {
 | 
| 1674 |       echo todo
 | 
| 1675 |     }
 | 
| 1676 |     header {
 | 
| 1677 |       echo todo
 | 
| 1678 |     }
 | 
| 1679 |     slice {
 | 
| 1680 |       # this has typed args
 | 
| 1681 |       # do we do some sort of splat?
 | 
| 1682 |       echo todo
 | 
| 1683 |     }
 | 
| 1684 |     to-tsv {
 | 
| 1685 |       echo todo
 | 
| 1686 |     }
 | 
| 1687 |   }
 | 
| 1688 | 
 | 
| 1689 |   echo TODO
 | 
| 1690 | }
 | 
| 1691 | 
 | 
| 1692 | proc test-table {
 | 
| 1693 |   return
 | 
| 1694 | 
 | 
| 1695 |   table (&files1) {
 | 
| 1696 |     cols  num_bytes path
 | 
| 1697 |     type  Int       Str
 | 
| 1698 | 
 | 
| 1699 |     row   10        README.md
 | 
| 1700 |     row   12        main.py
 | 
| 1701 | 
 | 
| 1702 |     row   (12,      'lib.py')
 | 
| 1703 |     row   (num_bytes=12, path='util.py')
 | 
| 1704 |   }
 | 
| 1705 | 
 | 
| 1706 |   # 2 columns - The default is by column?
 | 
| 1707 |   assert ['Dict' === type(files1)]
 | 
| 1708 |   assert [2 === len(files1)]
 | 
| 1709 | 
 | 
| 1710 |   # Same table
 | 
| 1711 |   table --by-row (&files2) {
 | 
| 1712 |     cols  num_bytes path
 | 
| 1713 |     type  Int       Str
 | 
| 1714 | 
 | 
| 1715 |     row   10        README.md
 | 
| 1716 |     row   12        main.py
 | 
| 1717 | 
 | 
| 1718 |     row   (12,      'lib.py')
 | 
| 1719 |     row   (num_bytes=12, path='util.py')
 | 
| 1720 |   }
 | 
| 1721 | 
 | 
| 1722 |   # 4 rows
 | 
| 1723 |   assert ['List' === type(files2)]
 | 
| 1724 |   assert [4 === len(files2)]
 | 
| 1725 | }
 | 
| 1726 | 
 | 
| 1727 | # "subcommands" of the dialect
 | 
| 1728 | 
 | 
| 1729 | proc cols (...names) {
 | 
| 1730 |   # cols name age
 | 
| 1731 |   echo TODO
 | 
| 1732 | }
 | 
| 1733 | 
 | 
| 1734 | proc types (...types) {
 | 
| 1735 |   # types - Int? Str?
 | 
| 1736 |   echo TODO
 | 
| 1737 | }
 | 
| 1738 | 
 | 
| 1739 | proc attr (name; ...values) {
 | 
| 1740 |   # attr units ('-', 'secs')
 | 
| 1741 |   echo TODO
 | 
| 1742 | }
 | 
| 1743 | 
 | 
| 1744 | # is this allowed outside table {} blocks too?
 | 
| 1745 | proc row {
 | 
| 1746 |   echo TODO
 | 
| 1747 | }
 | 
| 1748 | 
 | 
| 1749 | #
 | 
| 1750 | # dplyr names
 | 
| 1751 | #
 | 
| 1752 | 
 | 
| 1753 | # TODO: can we parse select?
 | 
| 1754 | 
 | 
| 1755 | proc where {
 | 
| 1756 |   echo
 | 
| 1757 | }
 | 
| 1758 | 
 | 
| 1759 | # TODO: should be able to test argv[0] or something
 | 
| 1760 | # Or pass to _mutate-transmute
 | 
| 1761 | 
 | 
| 1762 | proc mutate {
 | 
| 1763 |   echo TODO
 | 
| 1764 | }
 | 
| 1765 | 
 | 
| 1766 | proc transmute {
 | 
| 1767 |   echo TODO
 | 
| 1768 | }
 | 
| 1769 | 
 | 
| 1770 | proc rename {
 | 
| 1771 |   echo TODO
 | 
| 1772 | }
 | 
| 1773 | 
 | 
| 1774 | proc group-by {
 | 
| 1775 |   echo TODO
 | 
| 1776 | }
 | 
| 1777 | 
 | 
| 1778 | proc sort-by {
 | 
| 1779 |   echo TODO
 | 
| 1780 | }
 | 
| 1781 | 
 | 
| 1782 | proc summary {
 | 
| 1783 |   echo TODO
 | 
| 1784 | }
 | 
| 1785 | 
 | 
| 1786 | byo-maybe-run
 | 
| 1787 | )zZXx");
 | 
| 1788 | 
 | 
| 1789 | GLOBAL_STR(gStr47, R"zZXx(#!/usr/bin/env bash
 | 
| 1790 | #
 | 
| 1791 | # Testing library for bash and OSH.
 | 
| 1792 | #
 | 
| 1793 | # Capture status/stdout/stderr, and nq-assert those values.
 | 
| 1794 | 
 | 
| 1795 | #module yblocks || return 0
 | 
| 1796 | 
 | 
| 1797 | : ${LIB_OSH=stdlib/osh}
 | 
| 1798 | source $LIB_OSH/two.sh
 | 
| 1799 | 
 | 
| 1800 | # There is no yb-run, because you can just use try { } and inspect _error.code
 | 
| 1801 | # There is no yb-redir, because you can just use try >$tmp { } and inspect _error.code
 | 
| 1802 | 
 | 
| 1803 | proc yb-capture(; out; ; block) {
 | 
| 1804 |   ### capture status and stderr 
 | 
| 1805 | 
 | 
| 1806 |   var stdout = ''
 | 
| 1807 |   try {
 | 
| 1808 |     eval (block) | read --all (&stdout)
 | 
| 1809 |   }
 | 
| 1810 |   # TODO: if 'block' contains a pipeline, we lose this magic var
 | 
| 1811 |   var result = {status: _pipeline_status[0], stdout}
 | 
| 1812 | 
 | 
| 1813 |   #echo 'result-1'
 | 
| 1814 |   #pp line (result)
 | 
| 1815 | 
 | 
| 1816 |   call out->setValue(result)
 | 
| 1817 | }
 | 
| 1818 | 
 | 
| 1819 | proc yb-capture-2(; out; ; block) {
 | 
| 1820 |   ### capture status and stderr 
 | 
| 1821 | 
 | 
| 1822 |   var stderr = ''
 | 
| 1823 |   try {
 | 
| 1824 |     eval (block) 2>&1 | read --all (&stderr)
 | 
| 1825 |   }
 | 
| 1826 |   #pp line (_pipeline_status)
 | 
| 1827 | 
 | 
| 1828 |   var result = {status: _pipeline_status[0], stderr}
 | 
| 1829 |   #echo 'result-2'
 | 
| 1830 |   #pp line (result)
 | 
| 1831 | 
 | 
| 1832 |   call out->setValue(result)
 | 
| 1833 | }
 | 
| 1834 | )zZXx");
 | 
| 1835 | 
 | 
| 1836 | 
 | 
| 1837 | 
 | 
| 1838 | TextFile array[] = {
 | 
| 1839 |     {.rel_path = "_devbuild/help/data-errors", .contents = gStr0},
 | 
| 1840 |     {.rel_path = "_devbuild/help/data-front-end", .contents = gStr1},
 | 
| 1841 |     {.rel_path = "_devbuild/help/data-j8-notation", .contents = gStr2},
 | 
| 1842 |     {.rel_path = "_devbuild/help/help", .contents = gStr3},
 | 
| 1843 |     {.rel_path = "_devbuild/help/oils-usage", .contents = gStr4},
 | 
| 1844 |     {.rel_path = "_devbuild/help/osh-builtin-cmd", .contents = gStr5},
 | 
| 1845 |     {.rel_path = "_devbuild/help/osh-chapters", .contents = gStr6},
 | 
| 1846 |     {.rel_path = "_devbuild/help/osh-cmd-lang", .contents = gStr7},
 | 
| 1847 |     {.rel_path = "_devbuild/help/osh-front-end", .contents = gStr8},
 | 
| 1848 |     {.rel_path = "_devbuild/help/osh-mini-lang", .contents = gStr9},
 | 
| 1849 |     {.rel_path = "_devbuild/help/osh-option", .contents = gStr10},
 | 
| 1850 |     {.rel_path = "_devbuild/help/osh-osh-assign", .contents = gStr11},
 | 
| 1851 |     {.rel_path = "_devbuild/help/osh-plugin", .contents = gStr12},
 | 
| 1852 |     {.rel_path = "_devbuild/help/osh-special-var", .contents = gStr13},
 | 
| 1853 |     {.rel_path = "_devbuild/help/osh-stdlib", .contents = gStr14},
 | 
| 1854 |     {.rel_path = "_devbuild/help/osh-type-method", .contents = gStr15},
 | 
| 1855 |     {.rel_path = "_devbuild/help/osh-usage", .contents = gStr16},
 | 
| 1856 |     {.rel_path = "_devbuild/help/osh-word-lang", .contents = gStr17},
 | 
| 1857 |     {.rel_path = "_devbuild/help/ysh-builtin-cmd", .contents = gStr18},
 | 
| 1858 |     {.rel_path = "_devbuild/help/ysh-builtin-func", .contents = gStr19},
 | 
| 1859 |     {.rel_path = "_devbuild/help/ysh-chapters", .contents = gStr20},
 | 
| 1860 |     {.rel_path = "_devbuild/help/ysh-cmd-lang", .contents = gStr21},
 | 
| 1861 |     {.rel_path = "_devbuild/help/ysh-expr-lang", .contents = gStr22},
 | 
| 1862 |     {.rel_path = "_devbuild/help/ysh-front-end", .contents = gStr23},
 | 
| 1863 |     {.rel_path = "_devbuild/help/ysh-mini-lang", .contents = gStr24},
 | 
| 1864 |     {.rel_path = "_devbuild/help/ysh-option", .contents = gStr25},
 | 
| 1865 |     {.rel_path = "_devbuild/help/ysh-plugin", .contents = gStr26},
 | 
| 1866 |     {.rel_path = "_devbuild/help/ysh-special-var", .contents = gStr27},
 | 
| 1867 |     {.rel_path = "_devbuild/help/ysh-stdlib", .contents = gStr28},
 | 
| 1868 |     {.rel_path = "_devbuild/help/ysh-type-method", .contents = gStr29},
 | 
| 1869 |     {.rel_path = "_devbuild/help/ysh-usage", .contents = gStr30},
 | 
| 1870 |     {.rel_path = "_devbuild/help/ysh-word-lang", .contents = gStr31},
 | 
| 1871 |     {.rel_path = "_devbuild/help/ysh-ysh-cmd", .contents = gStr32},
 | 
| 1872 |     {.rel_path = "stdlib/funcs.ysh", .contents = gStr33},
 | 
| 1873 |     {.rel_path = "stdlib/methods.ysh", .contents = gStr34},
 | 
| 1874 |     {.rel_path = "stdlib/osh/bash-strict.sh", .contents = gStr35},
 | 
| 1875 |     {.rel_path = "stdlib/osh/byo-server.sh", .contents = gStr36},
 | 
| 1876 |     {.rel_path = "stdlib/osh/no-quotes.sh", .contents = gStr37},
 | 
| 1877 |     {.rel_path = "stdlib/osh/task-five.sh", .contents = gStr38},
 | 
| 1878 |     {.rel_path = "stdlib/osh/two.sh", .contents = gStr39},
 | 
| 1879 |     {.rel_path = "stdlib/prelude.ysh", .contents = gStr40},
 | 
| 1880 |     {.rel_path = "stdlib/testing.ysh", .contents = gStr41},
 | 
| 1881 |     {.rel_path = "stdlib/ysh/args.ysh", .contents = gStr42},
 | 
| 1882 |     {.rel_path = "stdlib/ysh/list.ysh", .contents = gStr43},
 | 
| 1883 |     {.rel_path = "stdlib/ysh/math.ysh", .contents = gStr44},
 | 
| 1884 |     {.rel_path = "stdlib/ysh/stream.ysh", .contents = gStr45},
 | 
| 1885 |     {.rel_path = "stdlib/ysh/table.ysh", .contents = gStr46},
 | 
| 1886 |     {.rel_path = "stdlib/ysh/yblocks.ysh", .contents = gStr47},
 | 
| 1887 | 
 | 
| 1888 |     {.rel_path = nullptr, .contents = nullptr},
 | 
| 1889 | };
 | 
| 1890 | 
 | 
| 1891 | }  // namespace embedded_file
 | 
| 1892 | 
 | 
| 1893 | TextFile* gEmbeddedFiles = embedded_file::array;  // turn array into pointer
 |