Extract Macro Parameters which Contain Spaces

In Refnotes, I have to extract all macro parameters from tiddler text! This is done using find macro! and it is fast and efficient! However an issue is reported recently!

  • Example: abbr macro
    The abbr is a macro with two parameters: <<abbr term:"" dict:"">>

Users are allowed to call macro in any way they like, for example:

# has space + single quote <<abbr 'single qoute'>>
# has space + double quote <<abbr "double quote">>
# has space + double bracket <<abbr [[double quote w brackets]]>>
# with term: <<abbr term:"myterm1">>
# with term+dict+single quote: <<abbr term:'myterm-s' dict:'Glossary1'>>
# with term+dict+double quote: <<abbr term:"myterm-d" dict:"Glossary2">>

# with term+dict no quote: <<abbr term:myterm_noq dict:Glossary>>
# with term+dict reverse: <<abbr dict:"Glossary" term:"myterm_reverse">>

Refnotes handles all of them! However consider below cases where Refnotes fails

# pass names w space in term: <<abbr term:'myterm s' dict:'Glossary_1'>>
# pass names w space in dict: <<abbr term:'myterm_d' dict:'Glossary 2'>>

Both cases above have space in one the parameter! So, the question is

How extract macro parameters contains spaces?

See my suggestion below and let me know your solution!

Solution one: Extract macro parameters by split on space but not in quotes

Consider a tiddler as below

<<abbr "hello world 3" tiddlywiki_5>>

<<abbr "ac1 with space" "gal1 with space">>

<<abbr 'ac2 with space' 'gal2 with space'>>

<<abbr term:'ac3 with space' dict:'gal3 with space'>>

The below script extract the parameters! This is a prototype, no worry for remaing <<abbr and >>

\define pattern()  ('.*?'|".*?"|\S+)
\define out() [[$(currentTiddler)$]]
<$list filter="""[{split-on-space/data}split[:]trim[term]trim[dict]splitregexp<pattern>trim[]!is[blank]]""">
<$text text=<<currentTiddler>>/><br>
</$list>

You can give a try

I think the solution works, but I expect to have a better solution!

What do you propose?

I need some hint. (Maybe I didn’t understand something)
What is the fail with the parameter? What is the expected result?
How do you need the parameter? whole or splitted?

You only need the accessing to the parameter, see the doc https://tiddlywiki.com/#Macro%20Definitions%20in%20WikiText
Depends on your need you can choice text sustitution or parameter as variable.

Here a basic example for output of whole (x) or splitted (y) parameter.

\define test(x, y)
`x`<br>
<$list filter="[<__x__>]">
<<currentTiddler>><br>
</$list>
<br>
`y`<br>
<$list filter="[enlist<__y__>]">
<<currentTiddler>><br>
</$list>
\end

''__double quote, single quote__'' - `<<test x:"abc xyz" 'xyz abc'>>`<br>
<<test x:"abc xyz" 'xyz abc'>>

<hr><br>
''__single quote, double quote__'' - `<<test x:'ABC XYZ' "XYZ ABC">>`<br>
<<test x:'ABC XYZ' "XYZ ABC">>

Hi @Alvaro
Many thanks for your reply! Actually Refnotes scans all tiddlers and extracts the macro calls like
<<abbr term dict>>, it then processes these cases to see how users call one of its macros.
The next step is to extract parameters from those macrocall. The third step is to create summary and tables from those parameters. For example creates a table of acronyms.

In the original post see how flexible users may call a macro (see Extract Macro Parameters which Contain Spaces)
In the second post see how I extract the params. The question is when params have space, specially when you have to input parameters, what is the best method to distinguish which parameter is passed and what is its value?

1 Like

Here’s what I got. I only tested this with the sample content you sent:

\define splitter() \s(?!.*?("|'))|["']\s["']|["']
\define filter() [{content}splitregexp[\n]search-replace:g:regexp[<<abbr|>>|term:|dict:],[]!is[blank]splitregexp<splitter>trim[]!is[blank]format:titlelist[]]

<$list filter=<<filter>>><p><$text text=<<currentTiddler>> /></p></$list>
1 Like

Hi @sull-vitsy - Thank you!
Does this extract params for specific macro namely here abbr?

I got it, it is here: <<abbr. Let’s me do some experiments! thank you

The splitter variable does:

  1. Split on whitespace that is not followed by quotes (for cases like <<abbr "hello world 3" tiddlywiki_5>>)
  2. Split on any quote-space-quote
  3. split on quotes

Hello again @sull-vitsy
Yes, it works! I think your solution is semantic! Thank you!

There is one point, see

<<abbr dict:"gal4 with space" term:'ac4 with space' >>

<<abbr dict:"gal5 with space" 'ac5 with space' >>

versus

<<abbr 'ac2 with space' 'gal2 with space'>>

<<abbr term:'ac3 with space' dict:'gal3 with space'>>

Is it possible the parser, always returns the term param value in the first place and and dict param value (if available) in the second place?

This way the script knows to do the next step processing on parameters value!

That is the Tiddlywiki which allows that amount of flexibility and parsing gets difficult :wink:

Note: the term parameter is always passed in the first place! Only when named parameters are used (e.g. (dict:"some value") the term may appear in the second place! So, term has the second place in macro call if and only if there has been a dict:"..." in macro call!

I think it’s too long, but I hope it is of some help to you or to anyone else who would like to join the discussion:

<$vars 
	split-space="""\s(?!.*?("|'))|["']\s["']|["']"""
	split-dict="""["']\s(?=dict:|term:)"""
	remove-termdict="""term:["']|dict:["']|["']"""
	remove-abbr="""<<abbr|>>"""
>
	
<$list filter="""[{data}splitregexp[\n]search-replace:g:regexp<remove-abbr>,[]trim[]!is[blank]]""">
	<$list filter="""[<currentTiddler>search::regexp[dict:("|')]splitregexp<split-dict>trim[]!is[blank]!sort[]search-replace:g:regexp<remove-termdict>,[]] ~[<currentTiddler>splitregexp<split-space>trim[]!is[blank]]"""><p><$text text=<<currentTiddler>> > /></p></$list>
</$list>
</$vars>

explanation:

<$list filter="""[{extract-macro-params/data}splitregexp[\n]
search-replace:g:regexp<remove-abbr>,[]trim[]!is[blank]]""">

split on linebreak and remove <<abbr >>.

<$list filter="""[<currentTiddler>search::regexp[dict:("|')]
splitregexp<split-dict>trim[]!is[blank]!sort[]
search-replace:g:regexp<remove-termdict>,[]]
"""
  1. First, search the line if it has dict:" This means the line also has “term” and may not be in the right order
  2. If yes:
    a. split on the space between a quote and either term: / dict:
    term:"a term"_!split!_dict:"the meaning"
    dict:"the meaning"_!split!_term:"a term"
    b. reverse alphabetical sort. term starts with t, and dict starts with d, so reverse alphabetical sort would put term before dict.
    c. remove “term” and “dict”
  3. If no:
    a. Split on whitespace that is not followed by quotes (for cases like <<abbr “hello world 3” tiddlywiki_5>>). If not applicable, then:
    b. Split on any quote-space-quote. If not applicable, then:
    c. split on quotes

Thank you @sull-vitsy works great!
It only cant not handle this case <<abbr dict:"gal5 with space" 'ac5 with space' >> which is not a big deal! Normally in other language when you use named parameters, from the point you pass name, all following params have to use name, but in Tiddlywiki it seems it allows to use unnamed param after a named param!

Thank you again and best wishes!