# Small (big?) feature request for the KScript Editor



## Dynamitec (Jan 24, 2009)

Hi Nils,

it's been a while since my last request (i know you have very little time these days).
But i could really need a small change in the editor, that could save a lot of time.
I sometimes use the fact, that you use your editor to pass complete expressions to functions. But there is a limit, which would be nice to removed somehow. For example:

on init
Expression(declare const, 1)
end on

function Expression(Expr1, Expr2)
Expr1 := Expr2
end function

gets compiled to:

on init
declare const := 1
end on

which of course makes no sense in this case  But it's just an example to make clear what i would like.

Something like this would be useful: 
The term: ?"Expression" is replaced to Expression in the first step of compiling.

on init
UI.Add(?"ui_knob Test(1,1,100)", ?"TEST", ?"1")
end on

function UI.Add(Element, Constant, ConstantValue)
declare global Element
declare global const Constant := ConstantValue 
end function

would lead to:

on init
declare ui_knob Test(1,1,100)
declare const TEST := 1
end on

With that one could use "function"-like initialization for UI elements and "auto
declaration" of constants which could later be used for example to refer to a PGS value which will contain the value of the knob later.

Also macros still would be very useful, so one could change some of the cryptic NI KSP functions to easier to write terms.

For example (using "rename" as command):

rename _pgs_get_key_val to PGS.Read

or:

rename _get_engine_par_disp to EP.Disp

Best,
Benjamin

PS1:
The inline replace could also be useful in such cases:

with #knob = (1,2,3,4,5,6,7,9,10)
declare ui_knob Knob#knob(1, 1, 100)
set_text(Knob#knob, "Test")
make_persistent(Knob#knob)
end with

PS2:
I really would be glad to sponsor this update o-[][]-o


----------



## germancomponist (Jan 24, 2009)

*Re: Small feature request for the KScript Editor*



Dynamitec @ Sat Jan 24 said:


> Hi Nils,
> 
> it's been a while since my last request (i know you have very little time these days).
> But i could really need a small change in the editor, that could save a lot of time.
> ...



dsfakfadfjdfj, adfjjafgi, fgkkgkkgk..... .

Oops,

I would be sooo happy if I would understand only 1% here... . :mrgreen:


----------



## Dynamitec (Jan 24, 2009)

Hehe  Those requests are a act of desperation to minimize code duplications in the daily live of a KSP programmer. I'm pretty sure Nils understand those request but I'm pretty sure he is also swamped with work, too!


----------



## Dynamitec (Jan 24, 2009)

As an alternative I would be happy to see some kind of plugin ability for your editor. Where you could enter a program which is called before your editor starts the compilation process (with a fixed input / output scheme).

For example: "plugin.exe BeforeCompilation.txt AfterPlugin.txt" where your editor creates the first file and uses the second one for compilation. And plugin.exe needs to be in the program directory of your editor.

This way i would be happy to im write some additions to your editor and make them available for those who also could need them. I have some spare time at night i need to fill


----------



## germancomponist (Jan 24, 2009)

It could be true that I need your help very soon!


----------



## kotori (Jan 24, 2009)

*Re: Small feature request for the KScript Editor*

Hi Benjamin,

Thanks for the suggestions. The tricky thing about preprocessing is that any extra preprocessing step would need to keep some meta-data about each line: line number, file name, and namespaces (this is used for compilation error reports), but if I cleared up the code a bit it would be possible to provide the possibility of writing preprocessor plugins. Furthermore, I'm not sure which line to report in case there is a compilation error due to a macro - the macro invocation line or the line inside the macro definition. If the macros are kept short it might make most sense to report the line where the macro is used.

Btw. is there any difference between inline replacement and multi-line replacement?
To me it seems that the use of a macro would mostly be to facilitate variable declarations since declarations using function parameters is something that is currently not supported. But if you do the declaration explicitly you can still use a function to do all the initialization, right? So I wonder if it's really that useful. Couldn't one save a lot of lines by using initialization functions? Could you try to convince me.

The rename and inline replacement seems to be essentially the same thing (to me "define" seems like a better word than "with" btw).

Another question is whether macros should be allowed to be nested and whether the same namespace rules should apply to them as to functions (eg. should an imported macro be used like imported_file.mymacro or mymacro). 

Will think a bit more about this.

/Nils


----------



## Dynamitec (Jan 24, 2009)

*Re: Small feature request for the KScript Editor*

Hi Nils,

maybe i should explain a little bit more how i intend to save time with the suggestions i made. First of all: Kontakt 3.5 is about to release and hopefully there will be a new Player version released soon, too. The PGS callback thing has proven to be very useful to keep scripts small and much easier to handle by using one slot of the UI and the other slots for the control of the instrument / notes / releases etc. 

To visualize it (for other guests here, following our discussion):

[Slot 0: UI] [Slot 1: Script Engine 1][Slot 2: Script Engine 2] etc.

For an example let's say I have a X-Fade knob for the legato crossfade in slot 0. 
If you have the engine in the same script lot you were able to use the knob just like a variable in the script (still speaking for all the other users here, too, I'm aware that you know all this). It also was possible to send the value of the knob via CC to the other scripts even in Kontakt 2, but all this needed a lot of extra work. The PGS callback makes it's much easier now, as the PGS variables work as global arrays.

However, there is one big drawback: you need to write the value of the knob to the PGS array every time it is changed and you need to read the PGS array whenever you want to access this knob-value in the other script slots. 

In my next follow up post I'll post a example of my the basic program structure 
i use in the UI slot...


----------



## Dynamitec (Jan 24, 2009)

*on init*
```UI.OnInit
*end on*

*function* UI.OnInit``
```_{ Declare the UI elements }_
```*family* UI
`````_{ UI Elements }_
``````*declare* global ui_knob LegatoXTime(1, 1000, 1)
``````*declare* global ui_knob LegatoBTime(1, 1000, 1)
``````*declare* global ui_button LegatoActive``````
```*end* *family*``
```
```_{ These constants are needed to later
read the PGS values from the UI key 
in the other script slots }_
```*declare* global *const* PGS_UI_LEGATO_XTIME := 0
```*declare* global *const* PGS_UI_LEGATO_BTIME := 1
```*declare* global *const* PGS_UI_LEGATO_ACTIVE := 2```
```_{ Create the PGS key for the UI }_
```_pgs_create_key(PGS_UI, 128)
```
```_{ Make all UI elements persistent }_
```make_persistent(UI.LegatoXTime)```
```_read_persistent_var(UI.LegatoXTime)
```make_persistent(UI.LegatoBTime)```
```_read_persistent_var(UI.LegatoBTime)
```make_persistent(UI.LegatoActive)```
```_read_persistent_var(UI.LegatoActive)
```
```_{ Initalize the texts }_
```set_text(UI.LegatoXTime, "X-Time")
```set_text(UI.LegatoBTime, "B-Time")
```set_text(UI.LegatoActive, "Legato")

```_{ Initialize all knobs with their later actions on init }_
```UI.LegatoXTime.OnChange
```UI.LegatoBTime.OnChange
```UI.LegatoActive.OnChange
*end function*

*on ui_control*(UI.LegatoXTime)
```UI.LegatoXTime.OnChange
*end on*

_{ Handler for UI.LegatoXTime }_
*function* UI.LegatoXTime.OnChange
```_pgs_set_key_val(PGS_UI, PGS_UI_LEGATO_XTIME, UI.LegatoXTime)
*end function*

*on ui_control*(UI.LegatoBTime)
```UI.LegatoBTime.OnChange
*end on*

_{ Handler for UI.LegatoBTime }_
*function* UI.LegatoBTime.OnChange
```_pgs_set_key_val(PGS_UI, PGS_UI_LEGATO_BTIME, UI.LegatoBTime)
*end function*

*on ui_control*(UI.LegatoActive)
```UI.LegatoActive.OnChange
*end on*

_{ Handler for UI.LegatoActive }_
*function* UI.LegatoActive.OnChange
```_pgs_set_key_val(PGS_UI, PGS_UI_LEGATO_ACTIVE, UI.LegatoActive)
*end function*


----------



## Dynamitec (Jan 24, 2009)

As I already mentioned the post above shows the basic structure i always use. In this small example it looks more complicated than necessary, however in big projects it's helpfull to "emulate" a little bit of OOP writing conventions (to bring in more structure). But as anyone can imagine it's a lot of work to add a new UI element later. All those callbacks, the functions etc. should be in the same order etc. and it's getting more and more complex with each new element.

Btw. for all here not familiar with the PGS callback, you can access the UI elements from the above example with _pgs_get_key_val(PGS_UI, PGS_UI_LEGATO_XTIME) in the other slots (of course the constant definitions need to be in all slots and should be used as a import on top of each script). So it's almost as easy as having the knob UI.LegatoXTime in each slot.

Of course one could use numbers instead of the constants to refer to the PGS values, but this would make the code totally unreadable after some time. 
So the constants are very important here and that brings us back to my initial requests in my follow up post (I post follow ups, since the scripting examples are using a lot of characters, and there aren't too much allowed per post here


----------



## kotori (Jan 24, 2009)

*Re: Small feature request for the KScript Editor*

Wouldn't it get shorter if you used:

*function* InitUIElement(var, text)
``make_persistent(var)
``_read_persistent_var(var)
``set_text(var, text)
``var.OnChange
*end function*

*function* UI.OnInit```
```*family* UI
``````*declare* global ui_knob LegatoXTime(1, 1000, 1)
``````*declare* global ui_knob LegatoBTime(1, 1000, 1)
``````*declare* global ui_button LegatoActive``````
```*end* *family*``

```*declare* global *const* PGS_UI_LEGATO_XTIME := 0
```*declare* global *const* PGS_UI_LEGATO_BTIME := 1
```*declare* global *const* PGS_UI_LEGATO_ACTIVE := 2```
```
```InitUIElement(UI.LegatoXTime)
```InitUIElement(UI.LegatoBTime)
```InitUIElement(UI.LegatoActive)```
*end function*

Are the pgs callbacks included in the macro scheme that you envision or are they left out of the picture?
Btw. maybe I should make OnInit a special name like on_init so that the global keyword is implicit. What do you think?


----------



## Dynamitec (Jan 24, 2009)

The PGS are included! I posting my full example shortly. Btw. I'm often using your scheme above, but it doesn't save to much time, since each callback need to be defined, each variable etc. If you have a script dealing with 50 UI Elements it's really hard to keep an overview


----------



## Dynamitec (Jan 24, 2009)

*Re: Small feature request for the KScript Editor*

Here is the example above using macro, etc.

*on init*
```UI.OnInit
*end on*

*function* UI.OnInit
```_{ Create the PGS key for the UI }_
```_pgs_create_key(PGS_UI, 128)
```
```UI.Element.AddKnob(LegatoXTime, 1, "X-Time", 1, 1000, 1, 25, 1, 1)
```UI.Element.AddKnob(LegatoBTime, 2, "B-Time", 1, 1000, 1, 25, 2, 1)
```UI.Element.AddButton(LegatoActive, 3, "Active", 3, 1)```
```
```LegatoXTime.OnChange
```LegatoBTime.OnChange
```LegatoActive.OnChange
*end function*

##UI.Handler(LegatoXTime)
##UI.Handler(LegatoBTime)
##UI.Handler(LegatoActive)

*function* UI.Element.AddKnob(#Element#, #Id#, Text, Min, Max, Steps, DefVal, PosX, PosY)
```*declare* global ui_knob #Element#(Min, Max, Steps)
```*declare* global PGS_#Element# := #Id#
``````
```make_persistent(#Element#)
```_read_persistent_var(#Element#)```
```set_knob_defval(#Element#, DefVal)
```set_text(#Element#, Text)```
```move_control(#Element#, PosX, PosY)
*end function*

*function* UI.Element.AddButton(#Element#, #Id#, Text, PosX, PosY)
```*declare* global ui_button #Element#
```*declare* global PGS_#Element# := #Id#
```
```make_persistent(#Element#)
```_read_persistent_var(#Element#)```
```set_text(#Element#, Text)```
```move_control(#Element#, PosX, PosY)
*end function*

macro ##UI.Handler(#Element#)
```*on ui_control*(#Element#)
``````#Element#.OnChange
```*end on*
``
```*function* #Element#.OnChange
``````_pgs_set_key_val(PGS_UI, PGS_#Element#, #Element#)
```*end function*
*end* macro


----------



## Dynamitec (Jan 24, 2009)

The most important timesaver (for me) would really be a inline replace. For example when used like this #Replacer#. In a first step all those get replaced with either their value as a function parameter or when used like #Replacer# := Expression.

Something like this:

function xyz(#Expression#)
#Expression#
end function


----------



## Dynamitec (Jan 24, 2009)

> Btw. maybe I should make OnInit a special name like on_init so that the global keyword is implicit. What do you think?



Sounds good, but how do you handle the order when uses in imports? First all imports in the order they were important than the OnInit from the main script?


----------



## Dynamitec (Jan 24, 2009)

I should note that all my ideas above are by no way the best way to handle it, they are meant as a rough draft of what i have in mind. So I'm very open for discussion 

I attached a example of one of my projects... i'm sure you now understand why i don't want to type everything that often  All those variables are stored in PGS callbacks, so it's basically always the same code for the basic handling.
Btw. this is only about half of the actual callbacks / OnChange functions!


----------



## kotori (Jan 24, 2009)

Dynamitec @ Sat Jan 24 said:


> > Btw. maybe I should make OnInit a special name like on_init so that the global keyword is implicit. What do you think?
> 
> 
> 
> Sounds good, but how do you handle the order when uses in imports? First all imports in the order they were important than the OnInit from the main script?


It wouldn't change the order if you call OnInit explicitly.

Interesting and enlightening examples you provided.
What do you think about error reporting and how macros could complicate it?


----------



## Dynamitec (Jan 24, 2009)

Here is what I would do: don't add the inline stuff to your compiler. Do it a step before the actual compiling takes places. Add all line numbers which were added by a macro to an array. If an error occours in your compiler just look if that line number is in the "macro array" and report that macro.

Edit: I don't think you need to worry to much about error reporting. I don't think beginners will use macros very often. What i would do is doing a short example with a new macro before using it in the main script to make sure everything works. 
Remember: working with imports isn't as easy as working with only one script, however it has proven to be very, very useful! In the import case one need to make sure the import works, too, before using it in the main script.

Edit:
define PGS.Get as _pgs_get_key_val still would be very cool, too.

I would rather use:
PGS.Get, PGS.Set, PGS.Create

instead of:
_pgs_get_key_val, _pgs_set_key_val, _pgs_create_key

Btw. do anyone know why NI sometimes uses _ before some functions and sometimes not? For example:
make_persistent() and _read_persistent_var()


----------



## Big Bob (Jan 24, 2009)

Hi Nils,

As long as we have re-opened the Macro wishlist, I thought I would repeat a very simple request that I made a long time ago. This is probably little more than a minor subset of what you and Benjamin are discussing, but just in case, I'm going to copy and paste it here below the line.

Thanks a million for considering any of this stuff. I don't know what we'd do without your KScript Editor in our toolboxes :roll: 

Have a lovely day my friend, 

Bob
------------------------------------------------------------------------------------------------


> If it's not too difficult to implement, I think that some kind of 'macro' expression expander could be very useful for making KS source code even more readable. As an example:




```
macro Old 
  1 - np.New 
end macro
```
This would then allow one to write 

```
if np.parent[Old] = 1 
    { Do Something } 
  end if
```

Instead of: 


```
if np.parent[1 - np.New] = 1 
    { Do Something } 
  end if
```

Needless to say the foregoing is only a trivial example but, I think it illustrates the idea of what I'm hoping for.


----------



## kotori (Jan 27, 2009)

I don't think anyone answered my question about whether being able to invoke a macro from within a macro is important or not. Any thoughts?


----------



## Dynamitec (Jan 27, 2009)

Hi Nils, 
hm, it might be useful but i think it might be more complicated to implement. What do you think? It also might bring other difficulties:

macro ##ShowText
message(##Text)
##ThirdMacro
end macro

macro ##Text
"Hello..." 
##ShowText("World" & ##Text)
end macro

macro ##ThirdMacro
##ShowText(##Text)
end macro

You know what i mean? Cross reference etc. 
I wouldn't allow macros inside of macros to keep it simple. It's a powerful enough feature if you add simple macros in my opinion.


----------



## kotori (Jan 27, 2009)

Thanks for the response Benjamin. Do you see any need for being able to import macros from other scripts?


----------



## Dynamitec (Jan 27, 2009)

That would be very useful! But it really depends on the extra work you'll might have with it. Anyway, if it's not too hard to do, this would be great!


----------



## kotori (Jan 29, 2009)

Do you think it would be necessary to be able to have macro parameters inline inside strings/comments?


----------



## Dynamitec (Jan 29, 2009)

I can say the same again: it depends on how difficult it is for you to add this. I think it's not that important.


----------



## kotori (Jan 31, 2009)

Any further suggestions for the syntax?

One idea might be to permit a special character (like eg. # in the examples above) to be used when one needs partial replacement of variable names (eg. having PGS_#var# substituted by PGS_myknob). Another solution could be to not allow partial name substitution since one can pass the variants as parameters to the macro.

Any thoughts? Other alternatives to using the # character?
I also wonder if it might be good to mark the difference between a multi-line macro and a macro which can act inside expressions on a single line (I haven't implemented any support for the latter yet)...


----------



## Dynamitec (Jan 31, 2009)

Hi Nils,

I would vote for: having PGS_#var# substituted by PGS_myknob
While it's a little bit more to write, it makes clear that a substitution will take place in the compiling process. I actually came up with #xyz# because of the Autohotkey syntax which uses %Variable% to do substitions while interpreting the code.
But since % is used for arrays and # isn't used, I would vorte for #.

I also think that it makes sense to mark the difference between a multi-line macro and a single line macro.


----------

