Hello Everyone,
I am attaching a rough draft of a patch to add ${1} curly brace support to m4. I was reading the archives and someone gave me an idea for an extended syntax. It is my understanding that POSIX lets us extent the ${} syntax. NOTE: in this email I use `TEXT' to denote m4 syntax examples. So with that being said heres what this patch does:
1) add an expand_parameter function that
A) supports the old `$0', `$1', ..., `$9', `$#', `$*', and `$@' syntax
B) `$10' is now parsed as `$1'`0'
C) if `$' is not as above or followed by `{' just print `$' so that `$$$ Save Money Now! $$$' is unmodified by parameter expansion
D) remove empty `${}' and allow it to break apart parameter expansions: `X${}X' somewhat like `X`'X'
E) add 1 level escape sequence `${ ' so that `${ HOME}' prints `${HOME}'. The idea is that this is a fast escape for shell variables. Most shells do not allow a space to start ${}, so it should be uncommon anyways.
F) add escape syntax: `${$}' => `$', `${(}' => `{', `${,}' => `,', and `${)}' => `}' these use the m4_has_syntax function and should work if the syntax is changed around via `changesyntax()'
H) Otherwise call collect_parameter_arguments, and m4_parameter_call
2) add collect_parameter_arguments which is analogous to collect_arguments:
A) `{' does not nest within `${}' like `(' does in `macro(())'
B) the start and end quotes have no special meaning in ${}, so the above escapes must be used
C) parameters are separated by unescaped `,' as in `${1,3}' and end at the first unescaped `}'
D) whitespace after `,' is ignored like macro arguments: `${1, 2}' => `${1,2}'
E) parameter expansion does occur while gathering arguments so that `${${#}}' expands as the last macro argument's value.
3) add m4_parameter_call to handle the extended parameters
A) parameter expansion has no access to macro values, and no macro expansion occurs while parameters evaluate
B) parameter expansion is only evaluated once and is non-recursive unlike macro expansion. See `${$, TEXT}' below.
C) address@hidden' is equivalent to `$@' but allows for an extended syntax:
i) address@hidden' => `${@, 1, -1, 1, ${,}}'
ii) the second argument is used as the start of a slice
iii) the third argument is the end of the slice, and defaults to the start value if a start is given, -1 otherwise
iv) the fourth argument is the slice step and defaults to 1.
v) the fifth argument is used as the value separator like "join" in other languages. Default: `,'
vi) if either the slice start or end is negative it is relative to the and of the macro argument list. (e.g. -1 is the last macro argument)
vii) a negative slice start or stop will never evaluate as 0
viii) if either slice start or stop are `0' then the parameter expands as the macro name just as in `$0'
ix) if the slice step is given then only every step'th argument after start is included, possibly including stop
x) `${@, 2, 7, 3}' => `${@, 2}${@,5}' ; `${@, 2, 8, 3}' => `${@,2}${@,5}${@,8}'
xi) if the slice step is negative then the macro arguments are reversed before the slice start/stop is applied
xii) `${@, 2, 7, -3}' => `${@,7}${@,4}'
xiii) all macro arguments expanded by address@hidden' are quoted just as in `$@'
D) `${*}' => `${*, 1, -1, -, ${,}}' => `${1, -1}'
i) `${*}' is basically like address@hidden' only the expanded macro arguments are not quoted just as in `$*'
ii) currently `${*}' is exactly like `${1, -1}', as such either could be changed to provide a different functionality.
E) `${0}' is the macro name
i) if the start or stop of a slice is 0 then the macro name is returned.
ii) `${0}' => `${0, 0}' => `${@, 2, 0}' => `${*, 0, 4}' => `$0'
iii) I could see the case for `${0, 2}' => `$0(${2, -1})' as a commonly used _expression_ but I think it may be too ugly
iv) we should then maybe also allow `${3,0}' => `$0(${1,3})' for symmetry? this is even more ugly.
F) `${1}' => `$1'; `${10}' => m4 1.X `$10'
i) currently `${1, 2, 1, ${,}}' => `${*, 1, 2}' . Maybe `${START, STOP, STEP, ...}' could accept other parameters
G) `${#}' => `$#'
i) `${#}' returns the number of macro arguments
ii) if more arguments are passed to `${#}' it behaves as a fast, efficient shortcut for `ifelse' with `$#'
iii) `${#, TEXT}' is used as a blind macro shorthand. If no args are passed to the macro then this evals to `$0' otherwise `TEXT'
iv) `$(#, BLIND MACRO BODY)' => `${#, 0, 0, ${0}, BLIND MACRO BODY}'
v) `${#, N, TEXT}' evaluate to `TEXT' iff $# == N
vi) `${#, -N, TEXT}' evaluate to `TEXT' if $# >= N, somewhat ugly, but a very common use case
vii) `${#, START, STOP, TEXT,...}' evaluate to `TEXT', if $# >= START && $# <= STOP, either START or STOP may be 0
viii) if STOP is negative then it is set to $# so that `${#, 2, -1, TEXT}' works as expected. setting -STOP to anything other than $# would always fail.
ix) if START is negative then START = -START, but continue to evaluate other clauses even with this one is true, like a C "case:" without a "break;"
x) `${#, -2, 4, TWO-FOUR, 3, -1, _THREE-OR-MORE, DEFAULT}' => `DEFAULT' if $# == 0 || $# == 1
xi) `${#, -2, 4, TWO-FOUR, 3, -1, _THREE-OR-MORE, DEFAULT}' => `TWO-FOUR' if $# == 2
xii) `${#, -2, 4, TWO-FOUR, 3, -1, _THREE-OR-MORE, DEFAULT}' => `TWO-FOUR_THREE-OR-MORE' if $# == 3 || $# == 4
xiii) `${#, -2, 4, TWO-FOUR, 3, -1, _THREE-OR-MORE, DEFAULT}' => `_THREE-OR-MORE' if $# >= 5
xiv) this -START syntax is ugly but leads to shorter macros. just like "case:" without "break" leads to shorted code, but is equally ugly. I'm open to ideas on what to do with -START and/or -STOP
xv) when ${#} has 3 or more arguments, they are parsed in groups of 3, with the final 3 parsed as ${#, ..., START, STOP, IF-TRUE}, final 2 as ${#, ..., N, TEXT} as above, or final 1 argument as ${#,...,DEFAULT}
H) `${$, TEXT}' recursively reevaluate TEXT until no more ${} references exist.
i) this allows macros such as `define(`X', `${#, ${$, ${1}}}')' to be defined which would allow arbitrary parameter expansions for things like advanced quoting
ii) there is currently a bug where this will expand forever so long as a `$' remains in the text.
I) Possible `${/, regex, replacement}' to do string manipulation
i) this is not implemented yet, I want to get feedback first
ii) remember that no macro expansion occurs during parameter expansion, so macros can't be used
iii) theoretically this could handle most string manipulation (limited by CS theory, regex engin, etc.)
iv) other string functions should have their own function
v) again: parameter expansion happens at a special time and this could lead to macros that are many time more efficient.
J)
Possible `${?, STRING, pattern-1, if-1, pattern-2, if-2, ..., default}'
i) this is not implemented yet, I want to get feedback first
ii) this would be like the shell's "case" statement
iii) this would be even faster than `ifelse'
K) Possible `${=, 2 + 2}'
i) this is not implemented yet, I want to get feedback first
ii) this would be like `eval' but available as parameter expansion time.
L) Possible `${%, list comprehension, pattern, stuff, format, ...}'
i) this is not implemented yet, I want to get feedback first
ii) this would allow for the generation of text for things like loops, arrays, etc
iii) this would work something like the shell's "{1..4}" brace expansion mechanism.
I'm not sure if this is too far removed from the m4 ethos, or if parts should be changed. The code should probably be reviewed to make sure that I am using the internal structures properly. Any constructive feedback is welcome. The attached patch is based off of the current git head. The code does need work, but its at a point where its ideas come through. I'd like to get this in m4 2.0 if everyone things this is worth doing.
Thank you,
Lester Scofield