Skip to content
Philipp Schmitt edited this page Nov 6, 2021 · 2 revisions

ZQL draft

Intro

It's a shame that even NixOS, a system with declarative configuration at its core, implements declaring of Bash and Zsh configuration by blocks of text assigned to some configuration fields. This isn't "declaring", it's a regular shell configuration maintenance.

The project and the language behind it – the ZQL – is to solve this problem. Basing on the parser that's built into Zsh and exposed via the (z) flag it will allow to truly declare the contents of shell scripts, being the Bash (Zsh correctly parses a very large subset of Bash syntax) or Zsh startup files, or regular shell scripts.

Below is a draft version of the language. If you'll see any mistakes or inconstences then feel free to report them to r/zplugin subreddit.

Basic Matching

  • quoting-free symbols
    • #, "#", '#', $'#' all match any string, i.e. string, "string", 'string', $'string' but not var
    • $, "$", '$', $'$' all match any variable, in any quoting, i.e. $var, "$var", '$var', $'$var'
    • *, "*", '*', $'*' all match anything, i.e. in practice any combination of string and var, in any quoting
  • quoting-locked symbols
    • ##, "##", '##', $'##' all match only the corresponding string, i.e. ## matches string, "##" matches "string", etc.
    • $$, "$$", '$$', $'$$' all match only the corresponding quoting, i.e. $$ matches $var, "$$" matches "$var", etc.
    • **, "**", '**', $'**' all match anything, i.e. in practice any combination of string and var, but only with concrete quoting that's being the same as the quoting of the symbol

1. BEGIN & INSERT

  • BEGIN – match the beginning of the script
  • INSERT – insert the 2 commands
declzsh 'BEGIN INSERT { typeset -F4 SECONDS=0; bindkey ^T widget }'

2. BEGIN, IFNE & INSERT

  • BEGIN – match the beginning of the script

  • IFNE – if not exists – if there's no bindkey with arbitrary arguments (the %) at the beginning of the script, then

  • INSERT – insert the bindkey command

declzsh 'BEGIN IFNE { bindkey % }
           INSERT { bindkey ^T widget }'

3. BEGIN, IFE & ALTER

  • BEGIN[2] – match the second line of the script – IFE – if-exists – if it's a bindkey with 2 arbitrary strings (but not vars), then
  • ALTER – set the second argument to widget leaving the first one unchanged
declzsh 'BEGIN[2] IFE { bindkey # # }
           ALTER { bindkey * widget }'

4. BEGIN, IFE, BEGIN & ALTER

  • BEGIN[3] – match the third line of the script
  • IFE – if-exists – if it's a bindkey with 2 arbitrary strings (but not vars), then
  • BEGIN[4] – match the fourth line of the script
  • ALTER – set the second argument to widget leaving the first one unchanged
declzsh 'BEGIN[3] IFE { bindkey # # }
           BEGIN[4] ALTER { bindkey * widget }'

5. BODY, IFE, PRE, INSERT

Base concept: if zplugin is sourced somewhere in the .zshrc, then add a new plugin zdharma/null after last loaded plugin.

  • BODY – matches whole .zshrc
  • IFE – if-exists – checks whether the command source ... exists in the .zshrc (i.e. in the matched portion of .zshrc, by the preceding BODY directive)
  • PRE[-1] – moves the insert pointer after the command that matches (i.e. the zplugin load %); [-1] – take the last such matching command
  • INSERT – appends given command after the command matched by PRE
declzsh 'BODY IFE { source '%/zplugin.zsh' }
           PRE[-1] { zplugin load % }
           INSERT { zplugin load zdharma/null }'

6. END & INSERT

  • END – match the end of the script
  • INSERT – append the echo ... command
declzsh 'END INSERT { echo "[zshrc] Load time:" $SECONDS sec }'

7. END, IFNE & INSERT

  • END – match the end of the script
  • IFNE – if there's no echo with substring "[zshrc] " in the first argument, then
  • INSERT – append the echo ... command
declzsh 'END IFNE { echo "[zshrc] %" % }
           INSERT { echo "[zshrc] Load time:" $SECONDS sec }'

8. END, IFE & ALTER

  • END[-2] – match the second-line from the end of the script
  • IFE – if there's echo with arbitrary string as 1st arg, an arbitrarily quoted var as the second arg, and then an arbitrary string as the third arg, then argument, then
  • ALTER – set the echo to have only single argument the string inside single quoting
declzsh 'END[-2] IFE { echo # $ # }
           ALTER { echo "[zshrc] Load time: $SECONDS sec" }'

9. END, IFE & DELETE

  • END[-3] – match the third-line from the end of the script
  • IFE – if it's SECONDS=0, then
  • DELETE – delete it
declzsh 'END[-3] IFE { SECONDS=0 }
           DELETE'

10. END, IFE & DELETE (2)

  • END[-3] – match the third-line from the end of the script
  • IFE – if it's SECONDS=0, then
  • DELETE – delete it and a following command and also any following blank line (because of the +)
declzsh 'END[-3] IFE { SECONDS=0 }
           END[-2] DELETE %,+'

11. PRE, POST & ALTER

  • PRE – Grep through file searching for command setopt with any string (i.e. str, 'str', "str", $'str') but not var (for string AND var there's *, for var-only there's $) followed by any number of whitespace and then by command print that takes exactly "any-combination-of-string-and-var"

  • ALTER – for the command following the one matched with PRE, which should be echo set the second argument to "added" leaving the first one unchanged; then for any following command (e.g. make) set the second argument to DEBUG=1; then, set the argument of the following command, which should be ls, to *

  • POST – grep for an assignment VALUE={any-string-or-var-with-any-quoting}

The PRE and POST are to match a specific part of the script, i.e. to provide a surrounding context for the inner matching that does the editing

declzsh 'PRE { setopt #; print "**"; }
        ALTER { echo * "added"; * DEBUG=1; ls \* }
        POST { VALUE=* }'

12. PRE, POST & INSERT

  • PRE – setopt with exactly "string" as an argument and a blank following line (because of the +), then a print with exactly "string$var" or "$var$var" as the 1st arg and with exactly string or "anything" as the second arg

  • INSERT – appends 2 commands ls * (no quoting of * needed, as this is an insert of a text, not e.g. an ALTER command) and the echo ... and a blank line after the echo (because of the +)

  • POST – match for assignment VALUE="anything-exactly-this-way-quoted"

declzsh 'PRE { setopt "##"+; print "##$|$+$" ##|"**" }
        INSERT { ls *; echo "Hello World"+ }
        POST { VALUE="**" }'

13. PRE & DELETE

  • PRE – match a blank line then a setopt with "anything" as the arg and then a blank line, then a print with $var$var or "$var$var" etc. (quoting is arbitrary because the pattern isn't "$$+$$" or at least "$$+$") as the first arg then the line-continuation backslash then an arbitrarily quoted arbitrary argument and then ANYTHING – the % is like an "pass-through all", or "accept-all" or "match-anything"

  • DELETE – delete any following command, then a blank line, then again any following command and then the echo with arbitrary arguments

declzsh 'PRE { +setopt "**"+; print "$+$" \\+"*" % }
           DELETE { %;+;%;echo % }'