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