Community discussions

MikroTik App
 
User avatar
Sertik
Member
Member
Topic Author
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

execute & parse

Sun Mar 24, 2024 7:56 pm

The topic has been discussed many times, but everything is forgotten when there is little practice...
Is it possible to access a variable whose name is not known in advance to the script function by passing it in a parameter? Something like this...
:return [[:parse ":global \$1; return [\$1 hello]"]]
or
:return [:execute ":global $1; :return [$1 hello]"]
 
User avatar
Sertik
Member
Member
Topic Author
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: execute & parse

Mon Mar 25, 2024 9:49 am

:put [:return [[:parse ":global $1; return [$1 hello]"]]]
expected command name
In other words, Question: is how to call a function whose name is set by a parameter of another function? Or is this not possible because at the time of execution of :parse, the function declared in :global has not yet been defined? Is there any way around this problem?
 
User avatar
patrikg
Member
Member
Posts: 346
Joined: Thu Feb 07, 2013 6:38 pm
Location: Stockholm, Sweden

Re: execute & parse

Mon Mar 25, 2024 9:58 am

Like eval in another script languages ?
 
User avatar
Sertik
Member
Member
Topic Author
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: execute & parse

Mon Mar 25, 2024 10:06 am

I can't judge other scripting languages, I'm not a programmer...
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 12438
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: execute & parse

Mon Mar 25, 2024 10:54 am

RouterOS is not a programming language.

There are certainly simpler ways of doing things.

Concrete and well-explained examples may have specific suggestions.
 
User avatar
Sertik
Member
Member
Topic Author
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: execute & parse

Mon Mar 25, 2024 12:48 pm

Hello Rex. I don't know what can be explained well here. I am writing a global function that should receive data from other global functions, the names of which would be passed to the first one as parameter $1, a $2 to be the parameter passed to the second function. Having received data from other functions, the main one can do whatever it wants with it...

All my desire is expressed in the lines above, the meaning of which is absolutely clear to you, like a pro. Their syntax is of course not correct, I don’t know how to write it...

:global myFunc do={
     :local Ans [[:parse ":global $1; return [$1 $2]"]]
     :set Ans ($Ans+5)
     :return $Ans
}

[$myFunc Func1 Parametr1] 
Above is an example ...
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 12438
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: execute & parse

Mon Mar 25, 2024 2:27 pm

Given that it seems like xxxxx to me, unnecessary complications,
it's done like this:

example code

:global addten do={
    :local input [:tonum $1]
    :return ($input + 10)
}

:global removeten do={
    :local input [:tonum $1]
    :return ($input - 10)
}

:global dosomething do={
     :local func  [:tostr $1]
     :local input [:tostr $2]
     :local output [[:parse ":global $func ; :return [\$$func $input]"]]
     :return $output
}

:put [$dosomething addten 6]

:put [$dosomething removeten 15]
Last edited by chechito on Mon Mar 25, 2024 7:23 pm, edited 1 time in total.
Reason: harsh language
 
User avatar
Amm0
Forum Guru
Forum Guru
Posts: 4100
Joined: Sun May 01, 2016 7:12 pm
Location: California
Contact:

Re: execute & parse

Mon Mar 25, 2024 4:02 pm

Like eval in another script languages ?
Yes. And same generally approach: string interpolation to dynamically create a command.

Here the use more complex. @rextended and @Sertik are trying to get around the restriction that a function must declare any global variables to be able to use them. So the :parse (aka eval()) is used to create a new scope to run a global function provided by a string name (not variable)

@patrikg asked likely because it common wisdom that "eval() is bad". @rextended expresses that as "seems like bullshit to me". ;)

IMO using an RouterOS array to store functions is a better approach (akin to a C++ vtable) than use weird :parse tricks to dynamically call functions from other function. Especially since in V7 there are lot more restrictions of sharing global variables (e.g. netwatch etc script) and could be more in future.
 
User avatar
Sertik
Member
Member
Topic Author
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: execute & parse

Mon Mar 25, 2024 4:06 pm

Thank you so much, Rex! This is not nonsense, but a very necessary thing for me! That's the line I was waiting for from you and you helped me a lot in this.:
 :return [[:parse ":global $1 ; :return [\$$1 $2]"]]
I found the solution myself, but I was wrong about one thing - I had to screen it here twice : \$$1

As always, you're on top! Thank you, Teacher!
 
User avatar
Sertik
Member
Member
Topic Author
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: execute & parse

Mon Mar 25, 2024 4:10 pm

In this way, you can call functions whose names are not known in advance, that is, they will be determined during the script !

Let me try to explain: imagine that you have a certain process (function) that, in order to solve its tasks, must and can, depending on the initial conditions, use several different functions-solutions to this problem. That's the way out. The main function will, based on the conditions, call different handler functions, passing them the same parameters.

I used the Rex hint and now everything works for me the way I wanted it to! I'm happy ! :D
 
User avatar
Larsa
Forum Guru
Forum Guru
Posts: 1531
Joined: Sat Aug 29, 2015 7:40 pm
Location: The North Pole, Santa's Workshop

Re: execute & parse

Mon Mar 25, 2024 6:48 pm

 :return [[:parse ":global $1 ; :return [\$$1 $2]"]]

Yeah, that's a good one-liner. Here's another neat trick if you want to call system scripts with arguments. This also works with "[/file get /dirname/scriptname contents]" if you prefer to store your scripts in a different location. Use "" as shown above if you need to evaluate at runtime.
[[:parse [/system script get scriptname source]] $arg1 $arg2 varname=$varname ]
 
User avatar
Amm0
Forum Guru
Forum Guru
Posts: 4100
Joined: Sun May 01, 2016 7:12 pm
Location: California
Contact:

Re: execute & parse

Mon Mar 25, 2024 7:25 pm

It just be nice if you could have some "stored functions" since they could just be saved in config like a script. e.g. "/system/scripts/function add addVLAN source={}" so NO :global be need. Without resorting the @Larsa's nifty but ugly approach.

As a "config language", functions be more generally useful if folks didn't need to result to weird tricks to effectively use them (e.g. scoping, :globals & :parse). Rather if functions were part of the config, it be easy to "share" function that do config things.

I actually like the nifty tricks folks use. And get why @Sertik is trying here. Just my problem is these nifty tricks get tricky if this a production router... Anyway Functions are useful, but annoying they are hidden beyond all the cruft to use them effectively.
 
User avatar
Larsa
Forum Guru
Forum Guru
Posts: 1531
Joined: Sat Aug 29, 2015 7:40 pm
Location: The North Pole, Santa's Workshop

Re: execute & parse

Mon Mar 25, 2024 7:35 pm

Couldn't agree more. There is clearly something flawed when all sorts of workarounds pop up in the flow..
 
User avatar
Sertik
Member
Member
Topic Author
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: execute & parse

Tue Mar 26, 2024 12:41 pm

# example Magical :parse

# declaring a variable with substituting a value from Layer7 for it
[[:parse ":global $vname [:to$vtype $[/ip firewall layer7 get [find where name=$vname] regexp]]"]]

# Hack with array sorting (by Rextended)
:set hackSort ($hackSort, [[:parse "[:toarray {\"$ymd\"=\"$filename\"}]"]]);

# passing parameters to the script and running it
[[:parse "[:parse [/system script get $msgTxt source]] $Parametr1 $Parametr2"]]

# or the same thing, but with multithreading with parameter passing
:set result "[[:parse [system script get $calledFunctionName source]] ID=$queryID ChatID=$queryChatID]"
:execute script=$result

# or from the file
:execute script="[[:parse [/file get $FileName content]] ID=$queryID ChatID=$queryChatID]"
and there are many similar examples from forum scripts ...

Many authors have invested time and labor in creating such designs. There is a very good article in Russian by our esteemed Chupakabra (the author of the famous JSON parser for the Router OS), here is a link for those who are interested. The article examines the work in detail :parse, I highly recommend it to everyone:

https://habr.com/ru/articles/650795/

Yours truly also had a little hand in learning the functions in Microtik scripts:

https://habr.com/ru/articles/646663/

But we are far from real programmers like Amm0 and our great Rex!
 
User avatar
Sertik
Member
Member
Topic Author
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: execute & parse

Tue Mar 26, 2024 12:52 pm

As for the difficulties, I agree with Larsa and Amm0... I don't know how from the point of view of implementation, but the scripting language of the OS Router does not allow you to directly write :global $Name or :local $Name, so that $Name itself is a variable, this generates many difficulties, of which we dodged in particular here...
 
User avatar
Sertik
Member
Member
Topic Author
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: execute & parse

Tue Mar 26, 2024 1:19 pm

Regarding the introduction of something new in functions, the type proposed by Amm0:
/system/scripts/function
developers should be careful. By introducing a new one, you should not violate the old features, as this will lead to the need to rewrite thousands of working scripts...
 
User avatar
Sertik
Member
Member
Topic Author
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: execute & parse

Tue Oct 22, 2024 9:25 pm

I also want to ask Rex. For example, there is a function in which I want to make a recursive call. We do it as usual like this:

:global MT do={:global MT; [$MT]; :put $0}
How can I call her without explicitly mentioning her name? That is, I want to make it so that if the function itself is renamed, the code does not have to be edited. I'm trying to do it like this:
:global MT do={
[[:parse ":global [:pick \$$0 1 [:len \$$0]];  [[:pick \$$0 1 [:len \$$0]]"]]
:put $0
}
Of course I get a “syntax error” because I don’t like ROS :global [...

How can this be solved?
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 12438
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: execute & parse

Wed Oct 23, 2024 10:26 am

As mentioned, RouterOS scripts are not a programming language.

Beyond a certain point things are useless and excessive.

Some things are excessive even if it were a programming language.

When will you ask for a script without any variable or function having a name,
and assigns the names of the variables and functions by reading them from a separate file?
 
User avatar
Sertik
Member
Member
Topic Author
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: execute & parse

Wed Oct 23, 2024 11:08 am

In this case, I just wanted to make a recursive call to the function so that its name could be taken from $0, and not specified explicitly. This is necessary so that if someone other than me changes the name of this function, its operation will not be disrupted. Is this possible?
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 12438
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: execute & parse

Wed Oct 23, 2024 12:09 pm

How do you guarantee that if someone else messes with the script everything will continue to work the same?

I don't waste time with useless things.

Anyway, a parse within a parse is enough...
 
User avatar
Sertik
Member
Member
Topic Author
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: execute & parse

Wed Oct 23, 2024 12:11 pm

won't spoil it, but just rename the function...
Okay, I'll dig around myself, maybe it will work...
 
User avatar
rextended
Forum Guru
Forum Guru
Posts: 12438
Joined: Tue Feb 25, 2014 12:49 pm
Location: Italy
Contact:

Re: execute & parse

Wed Oct 23, 2024 12:41 pm

I already gave you the "solution" in the previous post... :D
 
User avatar
Sertik
Member
Member
Topic Author
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: execute & parse

Wed Oct 23, 2024 1:25 pm

I didn’t miss this... By :parse inside :parse you mean something like this construction:

[[:parse "[:parse [/system script get $msgTxt source]] $Parametr1 $Parametr2"]]
 
User avatar
Sertik
Member
Member
Topic Author
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: execute & parse

Wed Oct 23, 2024 4:08 pm

I thought about it, but it turned out, as always, only after Rex’s blessing:
:global  AnyName do={
:if ($1="test") do={
[[:parse [:parse ":global [:pick \$$0 1 [:len \$$0]]"]]];
[[:parse [:parse "[:pick \$$0 1 [:len \$$0]]"]]]
:put "Recursive call without mentioning the function name works"}
:put OK
}
 
User avatar
Sertik
Member
Member
Topic Author
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: execute & parse

Thu Oct 24, 2024 8:34 am

It turns out that I was happy early. The syntax of the constructs passes, but the execution does not. We still need to dig deeper.
 
User avatar
Sertik
Member
Member
Topic Author
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: execute & parse

Thu Oct 24, 2024 8:58 am

It turned out to be much simpler. To call a function recursively without specifying its name, you don’t need to declare it internally at all. Simple enough:
[$0]
:global  AnyName do={
:if ($1="test") do={
[$0]
:put "Recursive call without mentioning the function name works"}
:put OK
}
Now it works, but only without passing an argument.
 
User avatar
Sertik
Member
Member
Topic Author
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: execute & parse

Thu Oct 24, 2024 10:12 am

you can pass arguments during a recursive call like this:
[[:parse "global $[:pick $0 1 [:len $0]]; [$0 mama]"]]
 
User avatar
Sertik
Member
Member
Topic Author
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: execute & parse

Thu Oct 24, 2024 10:18 am

This example illustrates the correct solution:
:global CountDown do={
:if ($1>0) do={:put $1; :set $1 ($1-1);[[:parse "global $[:pick $0 1 [:len $0]]; [$0 $1]"]];}
:return "end"
}

:put [$CountDown 5]
5
4
3
2
1
end
 
User avatar
Sertik
Member
Member
Topic Author
Posts: 489
Joined: Fri Sep 25, 2020 3:30 pm
Location: Russia, Moscow

Re: execute & parse

Thu Oct 24, 2024 10:47 am

If you need to pass more arguments or/and named arguments, all of them must be specified in :parse
In general, the final phrase for declaring and immediately calling a recursive function call without specifying its name is as follows:
[[:parse "global $[:pick $0 1 [:len $0]]; [$0 $1 $2 $Par1 $Par2]"]]

Who is online

Users browsing this forum: patrikg and 12 guests