In this article I will show you how to use text-based shell build into libgreattao(interface generation library). Libgreattao can works in one of three modes: normal mode(UI), shell mode(CLI), network mode(using tao protocol). In each mode way libgreattao works is different. In this article I will focus on shell mode.
Libgreattao allows applications written in libgreattao to creating windows, adding abstracts(for this article purpose I will call it event path) to windows, setting abstract attributes__(poprawić: chodzi o skojarzenia)__. Examples of abstract attribute are: description, name, icon. In shell window class don’t do anything. It was used only to doing operations on windows. Access to window are possible by window path. Window path looks like this:
In network mode it’s also no matter about window class, but in UI(normal) mode window class are very important. In normal mode window classes are associated with class file. Class file contains GUI elements definitions and process elements, which translates abstracts and abstract’s attributes. I attached example below:
void *window = tao_new_window("/desktop/dialogs/question_dialog"); tao_add_handler(window, "/message", NULL, NULL); tao_add_handler(window, "/actions/exit", &exit_callback, value_for_callback); tao_add_handler(window, "/actions/retry", &retry_callback, value_for_callbask2; tao_set_hint(window, "/message", HINT_NAME, "Critical error"); tao_set_hint(window, "/message", HINT_DESCRIPTION "We cannot open file required by application"); tao_set_hint(window, "/action/exit", HINT_DESCRIPTION "Shutdown application"); tao_set_hint(window, "/action/retry", HINT_DESCRIPTION "Will retry");
In above example we’ve used three abstracts and one window. First abstract is related to message. Next two abstracts are related to actions user can perform. In case of /message abstract third and last parameter doesn’t have meaning. Why we attached only description to buttons(ehmmm… .actions)? The answer is name are not needed here. In shell mode we have using abstract by window path and abstract path/name(second argument) and window class file perhaps contains rules for automatically attaching labels for buttons using part of abstract path/name. There’s no icons in shell mode.
Running application in shell mode
Libgreattao supports two switches to run libgreattao in shell mode. This it tao-shell-execute-file and tao-na-shell-interactive. In second parameter we have used string -na-, because we don’t supply any value for parameter. It is added, because maybe in future I would like add possibility to specify a value to an parameter.
Both of those switches makes libgreattao to run in shell mode. First of it will start script given as an argument. Second will start interactive mode. You can connect both of it to execute script first and run interactive mode once script ends of it execution.
You can exit from interactive mode by type command exit exit_code. If application doesn’t define exit function, application will ends with given exit code. In other case application will decide what do with exit code.
When creating this language I wish to follow lower price, but also by introducing many exciting features. By following this goal I decided to:
- Integers are only strings – you cannot create integer variable
- Other result of this decision are syntax of language – it looks like assember
Second point means there’s no operation marks – we have instructions, like =add, =text, etc. I attached example below:
=add variable_name value1 value2
In opposite with making my libgreattao language as simple as possible, I added exciting features to it, like closures, dictionaries or variables expanding. If you want to have arrays support, you can include file called types in shell-utils directory of libgreattao sources directory. Arrays are kind of dictionaries, so I have used dictionaries to implement arrays.
Language build in libgreattao don’t have name still, but I created two names: “goto languiage” and “scope switcher”. You can suggest your’s name too. Command interpreter have about 6500 lines of code with function declarations, macrodefinitions and others.
- There’s no possibility to use varnames as functions/code blocks names.
- Module names cannot start with ‘@’ or ‘.’ characters
- Name of variables cannot start with dots – only names of build in variables can start with dot
- Labels cannot start with dot – only special labels can start with dot
- Maximum command length can have 1024 characters
Typing commands and about commands
In interactive mode, commands should been typing with accepting each by enter. There’s exceptions from this rule. In this cases, command interpreter will wait for rest of command. This happens, when:
- Before newline character we put backslash
- You don’t close quatation marks
- Count of close parenthesis characters are not equal of count of open parenthesis characters
Attention: forwarding an character with backshalsh character makes forwarded character no special character. By putting enter in case shell awaits something different you can force shell to put newline character in place of pressing enter.
Before you place parameter in code, excepting first, you should place command default separator. Inverted commas and apostrophe are normal characters, when you place before or after it different character than command default separator. For example:
For other example:
For use variable from current context, you should use dollar sign. By placing variable name with forwarding dollar sign into inverted commas, apostrophe or parenthesis, or by forwarding dollar sign by backslash disallow to put variable value in place of name.
You can use many dollar signs to get value of variable visible on some level(it depends how many dollar signs you have use). For example one dollar sign means variable visible on current level. Two dollar signs means variable visible at one level bellow. You must be aware, while using multiple dollar sign, because mechanism for accessing variable in different level can be used only with special commands. One command which allow to use multiple dollar signs is scopelevel. There’s an example:
function a =text a 1 function b =text a 3 scopelevel -1 =add a $$a $a endblock b b echo $a endblock a a
This code will display 4. Scope switching mechanism allow me to avoid unnecessary problems with implementing return statement and references, etc. Functions can return any number of results into any levels.
Typing blocks of code
After met keyword function or block and ascending name, shell will wait for instructions. Functions/blocks of code must be closed with keyword endblock – programmer can pass block/function name after enblock keyword to make code cleaner and to allow interpreter to detects errors.
Parenthesis are normal strings, but starting with ( and ending with ). It have special purpose only, when using as command. In this case content of parenthesis are parset as normal command(without start and end characters). Parenthesis, which contains command should have default separator character after ( character. Example of usage of parenthesis are displayed below:
(\n echo This is a string)
Code placed above will display “This is a string”. Another example to display “This is a string” below:
echo "This is a string"
(; echo;This is a string)
To use empty parameter, you can do one of below:
- Place empty inverted commas or apostrophe
- Place two default separators for current command near, but only if it’s not space
(\n echo )
(In above example are placed two newline characters)
(; echo;;) <h2>Selected commands</h2> Libgreattao's shell are created to allow to control of application in shell way, so base command is command to run action associated with some abstract. It's run. As first parameter it required window and as second event path(abstract name). This is example for główny.o program, which will ran editor.o and exits from główny.o in next step. Sources of both programs are included in source of libgreattao. run /desktop/dialogs/question_dialog /actions/Button3 run /desktop/dialogs/question_dialog /actions/exit
You can use variable to remember window in it:
=window main_window /desktop/dialogs/question_dialog run $main_window /actions/Button3 run $main_window /actions/exit
Using window path as first argument will make shell try to found window. This operation can fail. Errors are generated, when you give unexpected value, like below:
=add i $a "We cause error"
=window variable_name window_path[window_number_with_this_path]
It will cause search for variable with given name in current block of code. If variable doesn't exist in current block of code, it will be created. In situation variable with given name exist with current block of code, but it has other type, it will cause error.
Commands starting with equal character are exposed to change variable value or creating new variables. This commands will write to variable with name given as second argument(first argument is command name). Commands starting with exclamation mark causes iteration over some values. For example, command below will show all abstracts for given window:
!event /desktop/dialogs/question_dialog a "echo $a"
Libgreattao's shell supports those kind of variables:
Two additional types are only other strings, but for commands requested some type, shell will check value can be converted into requested type. These two types are:
- Natural number(unsigned)
For removing variable from current block of code, you should use unset.
=text text "This is sample text" unset text echo $text
This will cause error.
Imagine that you have string contains two parameters, like window path and abstract name.We will now run the run command with those two parameters. You can achieve this by expanding this variable. Variable is expanded, when you pass .:~ after last dollar sign before first character of variable name. This is example:
=text parameters "/desktop/dialogs/question_dialog /actions/Button3" run $.:~parameters
Assigning and removing variables
When you change value of variable it looks like it was removed. I presented below list of action performed by shell, when you delete/assign variable:
- Deleting string - value are removed from memory; Creating string - value is copied
- Deleting window - decreasing reference count for some window - window will be removed once reference counter drops to 0 and program marks window to be removed; Creating window - increase reference count for window
- Deleting dictionary - value are removed from memory; Creating dictionary - all elements of dictionary are copied
This article introduces how to convert string into window reference. I must also tell how to convert string into dictionary. String, which representing dictionary, should looks like:
[key1 = value1 [key2 = value2 ... [keyN = valueN]]]
window = /desktop/dialogs/question_dialog action = /actions/exit
Spaces around equal sign are necessary. Also spaces between each element are necessary. You cannot use variables to create dictionary. To add value of variable to dictionary, you should create empty dictionary and use appendtodict. Example are below:
=dictionary name "" appendtodict name key value
To obtain value from a dictionary you should use =fromdict, for example:
=fromdict a $dictionary key_name
Dollar sign before dictionary name is put specially, because this command take dictionary instead of dictionary name . In place of $dictionary you can put string that represents an dictionary and get value from it.
To remove element from dictionary, you should use removefromdict:
removefromdict dict_name key_name
You can expand dictionary in the same way as string. Order of putting values of elements into command line depends on order of modify/create elements in dictionary.
Blocks of code and functions
Libgreattao supports two kind of code blocks(but there's way to use additional code block using parenthesis and execute-all command), but these two blocks of code can realize the same tasks as blocks of code in structural paradigm. Each code block supports commands such like continue, break, return. I will describe these commands, when I will describing conditional statements. You can achieve break label or continue label by using scopelevel or scopebyname.
Which kind of code blocks are supported? Normal block of code and function. How each blocks looks like I show you before. What's a difference? Normal block of code is executed, when after an currently executed instruction interpreter meets block of code and we didn't use call, callonce, goto, throwerror, return, break or conditional instruction. Function are executed in invoke time and there can be recursion of function. Another think you need to know is that before function is executed, there's created dictionary $.unnamed_params and positional arguments are added to this dictionary. Additional difference is that only inside function you can use commands functionuse and usemodule.
Each function and code block must contains name, but name don't must be unique.
Labels and goto
Labels and goto instructions can be used to change program execution. You can use labels only inside functions or code blocks. It is dedicated need to reduce memory consumption and simplify interpreter code.
I put two examples of infinite loop below:
block infinity_loop echo Started =text message "Iteration " =text i 1 : iteration =cat how_many $message $i echo $how_many goto iteration endblock infinity_loop
block infinite_loop function next scopelevel -1 goto iteration endblock next echo Started =text message "Iteration " =text i 1 : iteration =cat how_many $message $i echo $how_many next endblock infinite_loop
block iteration =cat how_many $messages $i echo $how_many continue endblock iteration endblock infinity_loop
Return and break
Return and break are not the same. The meaning are very low intuitive. Break not necessary cause return from current block. There's way to call label in current block - you can use call and callonce. Difference is in break return from current invocation, but return will return from block. This is used, when interpreter calling .initialize label.
In tao's shell function have two kind of parameters. First is named parameters and second is positioned parameters. Positioned parameters should be placed after function name, separated by space. This is an example:
Function_name [parameter1 [parameter2 ... [parameterN]]]
You can pass named parameters using example below:
appendtodict .named_params name_of_first_parameter value1 appendtodict .named_params name_of_second_parameter value2 Function_name
It will works only, when variable .named_params already exist. If not, you may create it:
=dictionary .named_params ""
Variables with this name is exception for rule disallowing to put dot at beginning of variable name, when changing value or creating. Named parameters could been used to simulate this from object programming, because dictionary of named parameters would been used between many calls.
Programmer can deliver both named and positioned parameters.
You could use both named and positioned parameters.
How to use parameters inside function? First way is variable $.@. This variable contains what you write in command line after function name. This way isn't ideal. Substitution variable $.@ as function parameter will causes variable $.@ inside invoked function will have $.@ substring. To solve this problem, you should use function expandvariablesandcallfunction from shell-utils/functions file from libgreattao source. This is an example:
expandvariablesandcallfunction function_name $.@
You can use =shellparsestring and =shellgettokencount. This example will show how to display each element passes in command line:
function echo_all =shellgettokencount i $.@ =text j 0 block process ifintless $j $i =add j $j 1 =shellparsestring wynik $j $.@ echo $wynik continue : .false endblock process endblock echo_all
I will write about conditional instructions later.
How to access named and unnamed parameters directly? Function have access to two dictionaries. First is for named params($.named_params) and next is for unnamed params($.unnamed_params). Named params isn't in current context, but unnamed params is in current context. $.unnamed_params contains elements counted starting from 1. Function have access also to two variables: $.unnamed_param_count and $.named_param_count. You can enjoy with these variables, but I've created simpler method - paramrequires function. This function is defined in shell-utils/function in directory with libgreattao sources. In next example I will show definition of two run function. Second of it gets two parameters: abstract name and window. In next step it calls build in run function. First function simply invoke run.
function run paramrequires positioned_window window positioned_string path buildin run $window $path endblock function run errordisplaymode void paramrequires any_string path any_window window errordisplaymode all buildin run $window $path break : .error errordisplaymode all next endblock
We had use error support and next instruction here. About errors I will write later. Instruction next call next function with the same name in current context. Thanks to these two functions, we can invoke run or run . Why? Because once we call run ..., second function is called and when we call run , paramrequires will throw error and next instruction is invoked.
I will now explain paramrequires function. From now parameters meaning all parameters without function name. For paramrequires you may pass natural number divisible by 2. Each unpaired parameter contains source, underline and type. Each paired parameter contain name of newly created variable. When these rules are not adjusted, paramrequires will throw error. Supported sources are: any(named and positioned), positioned, named. Supported types are: string, window, unsigned, signed. Paramrequires will first search in named dictionary and next in positioned dictionary for each parameters with any source. Search process in unnamed dictionary start for each parameter for next natural number, starting from 1.
You can call paramrequires from code of block inside function, like this:
scopetype readvariable scopebyname function_name paramrequires source1_type1 name1 ...
function error =text error_code "" block if_two_parameters ifinteq $.unnamed_param_count 2 varscopelevel -1 error_code varscopelevel -1 message scopetype readvariable scopevbyname error paramrequires positioned_unsigned error_code positioned_string message break : .false varscopelevel -1 message scopetype readvariable scopevbyname błąd paramrequires positioned_string message endblock if_two_parameters =cat error_code $error_code " " =cat message $error_code $message echo $message endblock
varscopelevel works similar to scopelevel, but makes all access to specified variable are related to specified level.
Switch block of code looks very similar to other programming language. It will works as libgreattao block, but also like switch block:
block our_switch ghostmode goto $i : 1 block 1 echo "have selected 1" endblock break : 2 block 2 echo "have selected 2" endblock break : 3 block 3 echo "have selected 3" endblock break : .error echo Default endblock
In case of missing label, interpreter was jump to .error label. Ghostmode instruction makes our_switch block are invisible, so on error condition in subblock shell won't display Default.
Conditions and loop
In shell of libgreattao, conditions are normal instructions, but started with if prefix. They must behaves in special way - on false they should jump one level higher to .false label. Thats all! You can write your own conditional instructions. You can use label .true and .intialize. Instruction endconditionlist will call .initialize label(if exist) at first execution and next true label. You won't perhaps use .true label, because conditions are normal instructions - if condition are true, next instruction will be executed. This makes:
- Conditions are connected with something like and operator
- Conditions are executed until any condition are not true(this behavior are similar to Python and modern C)
I put an example of negation below:
function not execute $.@ scopelevel -1 goto .false : .false endblock not
Negation are implemented in file shell-utils/logic in directory with libgreattao source. There's also implementation of OR and AND in the same file.
W pliku shell-utils/logic of directory with libgreattao source are implementation of assertions. Assertion does nothing, but return if condition are true and jump to .error one level higher on false. I must also tell if label is missing, then shell jumps to .error label. There's also assert_verbose, which also on false print command line.
block while condition1 condition2 ... conditionN instructions continue : .false endblock
block do_while instructions condition1 condition2 ... conditionN continue : .false endblock
block repeat_until : .false instructions end_condition endblock
block for inicjalization goto first_iteration : iteration ex.increase_counter : first_iteration condition1 condition2 ... conditionN instructions goto iteration endblock
Lovely condition formatting
Condition list in libgreattao looks very good. There's gap with OR and AND instructions. You could use it in this way:
OR "ifstreq a b" "ifstreq a c"
But this doesn't looks good. The solution is below:
(\n OR ifstreq a b ifstreq a c)
You can insert new line character before OR or AND as newline character is default separator. You can do the same with close parenthesis mark. You should remember that putting newline characters near means the same as putting empty parameter.
Closures can be used to return function from other function. Returned function can use selected variables from context, which return function. Returned function can used only remembered parameters.
For example I will show function, which returns function with assembled number to be powered with parameter.
function create_power =fromdict power $.unnamed_params 1 scopelevel -1 function power functionuse power =fromdict number $.unnamed_params 1 scopelevel -1 =text power 1 block wykonaj ifintless 1 $power =sub power $power 1 scopelevel -2 =mul power $$power $number continue : .false endblock endblock power endblock create_power
Most interesting thing is functionuse. It remembers value of variable with given name, when parsing(when function is returned) and restore value to context of called function, when executed.
Code below throws error:
function a echo $a =text a s functionuse a endblock a
Error will be caused on echo instruction, because variable a isn't set.
In libgreattao shell you can include files in this way:
You can also load files as modules:
loadmodule ścieżka_do_pliku wewętrzna_nazwa_modułu
Each file, which includes libgreattao instructions libgreattao can be included by first or second method. Second method makes each symbol, which doesn't be exported used scopebylevel or similar, private. To understood this you can download source of tao-file-manager in second version from libgreattao home page and look at libgreattao module of this program. Possibility to create custom module are not introduced only to allow user create custom scripts, but it's also for programmer to allow user do not write long and many commands. For example libgreattao module of tao-file-manager supports functions, like ls, copy, rm, cd, pwd.
Usemodule instruction are introduced especially to use inside blocks of code. This instruction makes module symbols(context) are inserted one level below. When block of code didn't use loadmodule before, module are inserted, but in other case module are replaced. This helps to create public function and private variables. To create public function. you need to use scopelevel functionuse to remember module name and usemodule.
This instruction can be used to check that structure representing current file description has set specified flag. While starting interactive, debug mode or loading file, libgreattao will create special structure contains some information. This help with detects file is loaded by script command or as module.
Libgreattao supports four behavior on error condition: throw, continue, exit, debug. First do jump to label .error in current block, but only when current context doesn't have set error flag(this flag is reset by default). In addition, throw set this flag. If this flag is set, throw jumps to first .error label below and set error flag for each context between current context and context with first .error label below. There's one exception to this rule. When checking block does not have throw behavior, there is no propagation of error, but error behavior for this block are invoked. Continue does nothing in error condition. Exit stops execution of script. Debug run interactive debug mode.
Current way to support debug mode can be selected by code below:
There's no way to set errorbehavior to debug, when program wasn't started with --tao-na-shell-enable-debug. In case program wasn't invoked with this switch and script tries to change debug behavior into debug, old behavior take precedence. Default behavior of support errors is throw.
Error behavior for current block is shipped in variable $.error_handling_behavior. Before block of code is invoked, there's $.error_handling_behavior variable append to new context. Value from $.error_handling_behavior_for_new_context are assigned to newly created $.error_handling_behavior, but there's one exception, when $.error_handling_behavior_for_new-context is inherit. In case the value is inherit, value from current $.error_handling_behaviour is assigned.
Error handling describes also flag about displaying errors with backtrace. To activate this flag, you should use this code:
To deactivate this flag, you should use this code: