//=============================================================================
//
// File : kvi_kvs_parser_lside.cpp
// Creation date : Thu 03 Nov 2003 13.11 CEST by Szymon Stefanek
//
// This file is part of the KVirc irc client distribution
// Copyright (C) 2003 Szymon Stefanek (pragma at kvirc dot net)
//
// This program is FREE software. You can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your opinion) any later version.
//
// This program is distributed in the HOPE that it will be USEFUL,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, write to the Free Software Foundation,
// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
//=============================================================================
# define __KVIRC__
# include "kvi_kvs_parser.h"
# include "kvi_kvs_treenode.h"
# include "kvi_kvs_report.h"
# include "kvi_kvs_kernel.h"
# include "kvi_kvs_parser_macros.h"
# include "kvi_locale.h"
/*
@ doc : operators
@ title :
Operators
@ keyterms :
operator , operators , assignment , assign
@ type :
language
@ short :
Variable operators , assignments & co .
@ body :
Operator constructs are commands just like the other ones .
All the operators work on local or global variables and dictionaries . [ br ]
The generic operator syntax is : [ br ]
[ br ]
[ b ] < left_operand > < operator > [ right_operand ] [ / b ] [ br ]
[ br ]
where < left_operand > and [ right_operand ] depend on the < operator > . [ br ]
Some operators have no [ right_operand ] and these are called [ b ] unary operators [ / b ] :
they operate directly on < left_operand > . [ br ]
[ br ]
[ big ] = ( Assignment ) [ / big ] [ br ]
[ br ]
The assignment is the " plainest " of the binary operators : it works just like in any other programming language . [ br ]
[ br ]
If the < left_operand > is a variable , then the < right_operand > is assigned to it ;
if the variable doesn ' t exists yet , it is created . [ br ]
If < right_operand > evaluates to an empty value then the variable is unset . [ br ]
[ example ]
[ comment ] # Assigning a constant to the global variable % Tmp [ / comment ]
% Tmp = 1
[ comment ] # Assigning a string constant to the global variable % Tmp [ / comment ]
% Tmp = some string
[ comment ] # Assigning a string constant to the local variable % tmp [ / comment ]
% tmp = " some string with whitespace preserved "
[ comment ] # Assigning a variable to another variable copies its contents [ / comment ]
% tmp = % somevariable
[ comment ] # Assigning a variable string to the global variable % Z [ / comment ]
% Z = my eyes are % color
[ comment ] # Assigning a variable string ( with a function call inside ) to the local variable % x [ / comment ]
% x = the system os is [ fnc ] $ system . osname [ / fnc ]
[ comment ] # Assigning an empty string to the local variable % y unsets % y [ / comment ]
% y =
[ comment ] # This is equivalent to the above [ / comment ]
% y = " "
[ comment ] # This is equivalent too , if $ function evalutates to an empty string [ / comment ]
% y = $ function ( )
[ / example ]
[ br ]
If the < left_operand > is a dictionary / array entry , then the < right_operand > is assigned to it ;
if the dictionary / array entry doesn ' t exist ( or the whole dictionary / array doesn ' t exists ) it is created . [ br ]
If < right_operand > evaluates to an empty value then the dictionary / array entry ( and eventually the whole
dictionary / array , if there are no other entries ) is unset . [ br ]
[ example ]
[ comment ] # Assigning a variable string to a global dictionary entry [ / comment ]
% Dict [ key ] = [ fnc ] $ system . osname [ / fnc ] \ ian
[ comment ] # Unsetting a local dictionary entry [ / comment ]
% mydict [ 23 ] = " "
[ / example ]
[ br ]
If the < left_operand > is an array reference then the semantics depend on the < right_operand > type .
If < right_operand > is an array reference then its contents are copied to the < left_operand > . [ br ]
If < right_operand > is a dictionary ( keys ) reference then its values are copied to to < left_operand > . [ br ]
If < right_operand > is a scalar then the value is assigned to every entry of < left_operand > . [ br ]
This is an easy way of unsetting a whole array : just assign an empty string . [ br ]
If the < left_operand > is a dictionary reference then the semantics depend on the < right_operand > type .
If < right_operand > is a dictionary reference then its contents are copied to the < left_operand > . [ br ]
If < right_operand > is an array reference then its contents are copied to to < left_operand > using
the array indexes as keys . [ br ]
If < right_operand > is a dictionary key reference then the keys are copied to the < left_operand >
using numeric indexes starting from 0 as keys . [ br ]
If < right_operand > is a scalar then the value is assigned to every key of < left_operand > . [ br ]
This is an easy way of unsetting a whole dictionary : just assign an empty string to all its keys . [ br ]
( If you play with huge dictionaries / arrays it might be a good idea to unset them when no longer needed )
[ example ]
[ comment ] # Assigning a dictionary to another : % mydict [ ] becomes a copy of % anotherdict [ ] [ / comment ]
% mydict [ ] = % anotherdict [ ]
[ comment ] # % mydict [ ] gets the values of the dict returned by $ features [ / comment ]
% mydict [ ] = [ fnc ] $ features [ / fnc ]
[ comment ] # Assigning a string to ALL the keys of % mydict [ / comment ]
% mydict [ ] = " some default value "
[ comment ] # Unsetting a whole dictionary [ / comment ]
% mydict [ ] =
% AnotherGlobalDict [ ] = " "
[ / example ]
[ br ]
[ big ] = ~ ( Binding operator ) [ / big ] [ br ]
[ br ]
This operator is a really ugly , poor and clueless attempt to reach at least 1 % of the
power of the perl = ~ operator : D [ br ]
It allows some complex string operations to be performed efficently by operating directly
on the left operand ( in fact this is a lot faster in KVIrc since at least one step of parsing is skipped ) . [ br ]
Its basic syntax is : [ br ]
[ b ] < left_operand > = ~ < operation > [ parameters ] [ / b ] [ br ]
Where < operation > may be one of ' t ' , ' s ' and parameters depend on it . [ br ]
< left_operand > is the target of the < operation > . [ br ]
If < left_operand > is an array or dictionary , the < operation > is executed on each item they contain . [ br ]
Operation ' t ' is the transliteration . [ br ]
The complete syntax with parameters is : [ br ]
[ b ] < left_operand > = ~ t / < search characters > / < replacement characters > / [ / b ] [ br ]
where < search characters > is a string of characters that are replaced with the corresponding
characters in < replacement characters > . [ br ]
This operation can be also named ' y ' or ' tr ' ( to preserve some compatibility with other languages ) . [ br ]
[ example ]
% A = This is a test string
echo % A
% A = ~ tr / abcdefghi / ABCDEFGHI /
echo % A
[ / example ]
Operation ' s ' is the substitution . [ br ]
The complete syntax with parameters is : [ br ]
[ b ] < left_operand > = ~ s / < search pattern > / < replacement pattern > / [ flags ] [ / b ] [ br ]
where < search pattern > is an extended regular expression to be matched in the < left_operand >
and < replacement string > is a special pattern that will replace any occurence found . [ br ]
< search pattern > may contain parentheses to capture parts of the matched text .
< replacement string > can contain the escape sequences \ \ N where N is a number between 1 and 9
to be replaced by the captured text . [ br ]
( We use \ \ N because KVIrc will first unquote the string when parsing . . . ) [ br ]
\ \ 0 is a special escape that will be replaced by the entire match ( is always valid ! ) . [ br ]
WARNING : the " capture-text " feature is not available if KVIrc has been compiled
with qt older than 3.0 .0 . You can find out if the feature is available by
looking for the string " TQt3 " in the array returned by [ fnc ] $ features [ / fnc ] . [ br ]
[ flags ] may be a combination of the letters ' g ' , ' i ' and ' w ' . [ br ]
' g ' causes the search to be global and not stop after the first occurence of < search pattern > . [ br ]
' i ' causes the search to be case insensitive . [ br ]
' w ' causes the search pattern to be interpreted as a simple wildcard regular expression . [ br ]
' m ' causes the matching to be " minimal " instead of " greedy " ( default ) . Greedy matches
find the longest possible match in the string while minimal ( non - greedy ) matches the shortest possible . [ br ]
[ example ]
% A = This is a test string
echo % A
% A = ~ s / ( [ a - z ] ) i ( [ a - z ] ) / \ \ 1 I \ \ 2 /
echo % A
% A = ~ s / ( [ a - z ] ) i ( [ a - z ] ) / \ \ 1 @ \ \ 2 / gi
echo % A
[ / example ]
[ br ]
[ big ] X = ( Arithmetic Self - operators ) [ / big ] [ br ]
[ br ]
The general syntax is : [ br ]
[ b ] < left_operand > < operation > < right_operand > [ / b ] [ br ]
Where < left_operand > and < right_operand > must evaluate to numbers . [ br ]
All these operators perform the operation on < left_operand > and < right_operand > and then
store the result in < left_operand > ( which therefore must be a variable , an array entry or a dictionary entry ) . [ br ]
< operation > may be one of : [ br ]
+ = : sums the < right_operand > to < left_operand > [ br ]
- = : subtracts < right_operand > from < left_operand > [ br ]
* = : multiplies < left_operand > by < right_operand > [ br ]
% = : calculates < left_operand > modulus < right_operand > [ br ]
| = : calculates < left_operand > bitwise - or < right_operand > [ br ]
& = : calculates < left_operand > bitwise - and < right_operand > [ br ]
/ = : divides < left_operand > by < right_operand > [ br ]
[ br ]
[ big ] + + and - - ( Increment and Decrement ) [ / big ] [ br ]
[ br ]
These two operators work only on numeric operands . [ br ]
The general syntax is : [ br ]
[ b ] < left_operand > < operator > [ / b ] [ br ]
There is no < right_operand > . [ br ]
+ + increments < left_operand > by one , - - decrements < left_operand > by one . [ br ]
These are equivalent to + = 1 and - = 1. [ br ]
[ br ]
[ big ] . = , < < , < + , < , ( String concatenation operators ) [ / big ] [ br ]
[ br ]
All these operators work also on whole arrays and dictionaries . [ br ]
Operator [ b ] . = [ / b ] : APPENDS the < right_operand > to the < left_operand > [ br ]
Operator [ b ] < + [ / b ] is a synonim for . = ( backward compatibility ) [ br ]
Operator [ b ] < < [ / b ] : appends < right_operand > to < left_operand >
separating the two strings by a single space if and only if < left_operand > and < right_operand >
are non - empty . [ br ]
Operator [ b ] < , [ / b ] : is similar to ' < < ' ; appends , separating with a single ' , ' with the same condition . [ br ]
@ examples :
First set the variable % var
[ example ]
% var = Ciao ciao
[ / example ]
Then append a nickname . . .
[ example ]
% var < < Pragma
[ / example ]
% var now tqcontains " Ciao ciao Pragma " [ br ]
Append a ' ! ' character
[ example ]
% var < + !
[ / example ]
% var now tqcontains " Ciao ciao Pragma! "
Now reset it .
[ example ]
% var =
[ / example ]
Now % var is unset . [ br ]
Reset it with a comma separated list of items
[ example ]
% var = Pragma , Diabl0 , Arter | o
% var < , MalboroLi
[ / example ]
% var now tqcontains " Pragma,Diabl0,Arter|o,MalboroLi " [ br ]
[ br ]
Now a longer example .
[ example ]
% var = l
[ cmd ] echo [ / cmd ] It ' s name starts with the letter % var !
% var & lt ; + inux
[ cmd ] echo [ / cmd ] Yes , it is % var !
% var & lt ; & lt ; OS
[ cmd ] echo [ / cmd ] Use % var !
% var & lt ; , Mac OS
[ cmd ] echo [ / cmd ] There are two items in this list : % var
% var = [ fnc ] $ strlen [ / fnc ] ( % var )
[ cmd ] echo [ / cmd ] And it is % var characters long ( including the comma )
% var - -
[ cmd ] echo [ / cmd ] Excluding the comma : % var
% var + = % var
[ cmd ] echo [ / cmd ] Now it is doubled : % var
% var =
[ cmd ] echo [ / cmd ] Now the var is unset ( empty ) : ( % var ) !
[ / example ]
*/
KviKvsTreeNodeData * KviKvsParser : : parseOperationRightSide ( bool bPreferNumeric )
{
KviPointerList < KviKvsTreeNodeData > * l = new KviPointerList < KviKvsTreeNodeData > ( ) ;
l - > setAutoDelete ( true ) ;
const TQChar * pBegin = KVSP_curCharPointer ;
for ( ; ; )
{
switch ( KVSP_curCharUnicode )
{
case 0 :
goto end_of_the_param ;
break ;
case ' \n ' :
case ' \r ' :
case ' ; ' :
KVSP_skipChar ;
goto end_of_the_param ;
break ;
case ' ' :
case ' \t ' :
skipSpaces ( ) ;
if ( KVSP_curCharIsEndOfCommand )
{
goto end_of_the_param ;
} else {
// separate by single spaces
bPreferNumeric = false ; // this can't be a number
l - > append ( new KviKvsTreeNodeConstantData ( KVSP_curCharPointer , new KviKvsVariant ( TQString ( " " ) ) ) ) ;
}
break ;
default :
// anything else is a parameter
KviKvsTreeNodeData * p = parseCommandParameter ( bPreferNumeric ) ;
if ( ! p )
{
// this is an error
delete l ;
return 0 ;
}
l - > append ( p ) ;
break ;
}
}
end_of_the_param :
if ( l - > count ( ) > 1 )
{
// complex parameter needed
return new KviKvsTreeNodeCompositeData ( pBegin , l ) ;
} else {
if ( l - > count ( ) > 0 )
{
// a single parameter in the list
l - > setAutoDelete ( false ) ;
KviKvsTreeNodeData * p = l - > first ( ) ;
delete l ;
return p ;
} else {
// empty (this should NEVER happen anyway)
delete l ;
return new KviKvsTreeNodeConstantData ( pBegin , new KviKvsVariant ( TQString ( " " ) ) ) ;
}
}
// never reached
return 0 ;
}
/*
@ doc : assignment
@ title :
Assignment operation
@ keyterms :
assignment
@ type :
language
@ short :
Assignment operation
@ body :
The assignment is the " plainest " of the operators : it works just like in any other programming language . [ br ]
The syntax is : [ br ]
[ br ]
[ b ] < target > = < source > [ / b ]
[ br ]
< target > must be a variable , < source > can be any parameter . [ br ]
If the < target > variable doesn ' t exist , it is created .
If it already exists , it is eventually converted to the type of < souce > ( scalar , hash or array ) . [ br ]
If < source > evaluates to an empty value then the < target > variable is unset . [ br ]
[ example ]
[ comment ] # Assigning a constant to the variable % Tmp [ / comment ]
% Tmp = 1
[ cmd ] echo [ / cmd ] % Tmp
[ comment ] # Assigning a string constant to the variable % Tmp [ / comment ]
% Tmp = some string
[ cmd ] echo [ / cmd ] % Tmp
[ comment ] # Assigning a string constant to the variable % Tmp [ / comment ]
% Tmp = " some string with whitespace preserved "
[ cmd ] echo [ / cmd ] % Tmp
[ comment ] # Assigning a variable to another variable copies its contents [ / comment ]
% Someothervariable = " Contents "
% Tmp = % Someothervariable
[ cmd ] echo [ / cmd ] % Tmp
[ comment ] # Assigning a variable string to the variable % z [ / comment ]
% color = blue
% z = my eyes are % color
[ cmd ] echo [ / cmd ] % z
[ comment ] # Assigning a variable string ( with a function call inside ) to the variable % x [ / comment ]
% x = the system os is [ fnc ] $ system . osname [ / fnc ]
[ cmd ] echo [ / cmd ] % x
[ comment ] # Assigning an empty string to the local variable % y unsets % y [ / comment ]
% x =
[ cmd ] echo [ / cmd ] % y
[ comment ] # This is equivalent to the above [ / comment ]
% y = " "
[ comment ] # This is equivalent too , if $ function evalutates to an empty string [ / comment ]
% y = $ function ( )
[ comment ] # Assigning a variable string to a hash entry [ / comment ]
% Dict { key } = [ fnc ] $ system . osname [ / fnc ] \ ian
[ comment ] # Unsetting an array entry [ / comment ]
% mydict [ 23 ] = " "
[ comment ] # Assigning a hash to another : % mydict [ ] becomes a copy of % anotherdict [ ] [ / comment ]
% anotherdict { " The key " } = " Some dummy value "
% mydict = % anotherdict
[ cmd ] echo [ / cmd ] % mydict { " The key " }
[ comment ] # This will convert % mydict to be a scalar variable ( deleting all the % mydict contents ! ) [ / comment ]
% mydict = " some default value "
[ comment ] # Unsetting a whole hash [ / comment ]
% anotherdict =
[ / example ]
[ / p ]
*/
/*
@ doc : incrementdecrement
@ title :
Increment and decrement operations
@ keyterms :
increment , decrement
@ type :
language
@ short :
Increment and decrement operations
@ body :
These two operators work only on numeric operands . [ br ]
The syntax is : [ br ]
[ br ]
[ b ] < target > + + [ / b ] [ br ]
[ b ] < target > - - [ / b ] [ br ]
[ br ]
+ + increments < target > by one , - - decrements < target > by one . [ br ]
These are equivalent to + = 1 and - = 1. [ br ]
< target > must be an existing variable and contain an integer value . [ br ]
If < target > contains a real value then the real is truncated to the nearest
integer and then incremented or decremented . [ br ]
@ examples :
[ example ]
% a = 10
[ cmd ] echo [ / cmd ] " Incrementing "
[ cmd ] while [ / cmd ] ( % a < 20 )
{
[ cmd ] echo [ / cmd ] % a
[ b ] % a + + [ / b ]
}
[ cmd ] echo [ / cmd ] " Decrementing "
[ cmd ] while [ / cmd ] ( % a > 10 )
{
[ cmd ] echo [ / cmd ] % a
[ b ] % a - - [ / b ]
}
[ cmd ] echo [ / cmd ] " Testing for loop "
[ cmd ] for [ / cmd ] ( % a = 0 ; % a < 10 ; [ b ] % a + + [ / b ] )
{
[ cmd ] echo [ / cmd ] % a
}
[ example ]
@ seealso :
[ doc : operators ] Operators [ / doc ]
*/
/*
@ doc : selfarithmetic
@ title :
Arithmetic self - operators
@ type :
language
@ short :
Arithmetic self - operators
@ body :
These operators work only on numeric operands . [ br ]
The syntax is : [ br ]
[ br ]
[ b ] < target > + = < right_operand > [ / b ] [ br ]
[ b ] < target > - = < right_operand > [ / b ] [ br ]
[ b ] < target > * = < right_operand > [ / b ] [ br ]
[ b ] < target > / = < right_operand > [ / b ] [ br ]
[ b ] < target > % = < right_operand > [ / b ] [ br ]
[ br ]
< target > must be an existing variable and contain a numeric value .
< right_operand > must evaluate to a numeric value .
Note that if you want < right_operand > to be a result of an expression , you must
enclose it in the $ ( * ) expression evaluation call . [ br ]
Operator + = sums the < right_operand > value to the < target > value and stores the result in < target > . [ br ]
Operator - = subtracts < right_operand > from < target > and stores the result in < target > . [ br ]
Operator * = multiplies < target > by < right_operand > and stores the result in < target > . [ br ]
Operator / = divides < target > by < right_operand > and stores the result in < target > . [ br ]
Operator % = computes < target > modulus < right_operand > and stores the result in < target > . [ br ]
The division and modulus operators fail with an error if < right_operand > is 0. [ br ]
If both < target > and < right_operand > are integer values then the results of the division
and modulus are integers ( truncated for the division ) . [ br ]
If < target > or < right_operand > or both are floating point values then the result is a floating point value . [ br ]
@ examples :
[ example ]
% a = 10
[ cmd ] echo [ / cmd ] % a
% a + = 20
[ cmd ] echo [ / cmd ] % a
% a - = $ ( % a - 1 )
[ cmd ] echo [ / cmd ] % a
% a * = 10
[ cmd ] echo [ / cmd ] % a
% a / = 21
[ cmd ] echo [ / cmd ] % a
% a * = 20
[ cmd ] echo [ / cmd ] % a
% a / = 21.0
[ cmd ] echo [ / cmd ] % a
% b = 10.0
% a % = % b
[ cmd ] echo [ / cmd ] % a
% a = 10
% b = 3
[ comment ] # nice trick [ / comment ]
% a / = % b .0
[ cmd ] echo [ / cmd ] % a
[ example ]
@ seealso :
[ doc : operators ] Operators [ / doc ]
*/
/*
@ doc : selfbitwise
@ title :
Bitwise self - operators
@ type :
language
@ short :
Bitwise self - operators
@ body :
These operators work only on integer operands . [ br ]
The syntax is : [ br ]
[ br ]
[ b ] < target > | = < right_operand > [ / b ] [ br ]
[ b ] < target > & = < right_operand > [ / b ] [ br ]
[ b ] < target > ^ = < right_operand > [ / b ] [ br ]
[ b ] < target > > > = < right_operand > [ / b ] [ br ]
[ b ] < target > < < = < right_operand > [ / b ] [ br ]
[ br ]
< target > must be an existing variable and contain a numeric value .
< right_operand > must evaluate to a numeric value .
If < target > or < right_operand > are floating point values then they are truncated
and converted to integers . [ br ]
Note that if you want < right_operand > to be a result of an expression , you must
enclose it in the $ ( * ) expression evaluation call . [ br ]
Operator | = computes < target > bitwise - or < right_operand > and stores the result in < target > . [ br ]
Operator & = computes < target > bitwise - and < right_operand > and stores the result in < target > . [ br ]
Operator ^ = computes < target > bitwise - xor < right_operand > and stores the result in < target > . [ br ]
Operator > > = shifts < target > < right_operand > bits to the right and stores the result int < target > . [ br ]
Operator < < = shifts < target > < right_operand > bits to the left and stores the result int < target > . [ br ]
Note that " != " is not available . You must use % a = $ ( ! % b ) to implement it . [ br ]
For operators > > = and < < = < right_operand > must be a positive integer . [ br ]
@ examples :
[ example ]
% a = 1
[ cmd ] echo [ / cmd ] % a
% a | = 2
[ cmd ] echo [ / cmd ] % a
% a & = 2
[ cmd ] echo [ / cmd ] % a
% a ^ = 1
[ cmd ] echo [ / cmd ] % a
% a > > = 2
[ cmd ] echo [ / cmd ] % a
% a < < = 1
[ cmd ] echo [ / cmd ] % a
[ example ]
@ seealso :
[ doc : operators ] Operators [ / doc ]
*/
/*
@ doc : stringconcatenation
@ title :
String concatenation operators
@ type :
language
@ short :
String concatenation operators
@ body :
These operators concatenate strings .
The syntax is : [ br ]
[ br ]
[ b ] < target > . = < right_operand > [ / b ] [ br ]
[ b ] < target > < < < right_operand > [ / b ] [ br ]
[ b ] < target > < , < right_operand > [ / b ] [ br ]
[ br ]
Operator . = appends < right_operand > to < target > .
Operator < < appends a space followed by < right_operand > to < target > if < target > is non empty ,
otherwise sets < target > to < right_operand > .
Operator < , is similar to < < but uses a comma to separate the two variable contents .
The last two operators are useful in creating space - separated or comma - separated lists .
@ examples :
[ example ]
% a = " "
% a < < free
[ cmd ] echo [ / cmd ] % a
% a . = bsd
[ cmd ] echo [ / cmd ] % a
% a < < rox
[ cmd ] echo [ / cmd ] % a
% a < , but linux is better !
[ cmd ] echo [ / cmd ] % a
[ example ]
@ seealso :
[ doc : operators ] Operators [ / doc ]
*/
/*
@ doc : arrayconcatenation
@ title :
Array concatenation operator
@ type :
language
@ short :
Array concatenation operator
@ body :
This operator concatenates arrays
The syntax is : [ br ]
[ br ]
[ b ] < target > < + < right_operand > [ / b ] [ br ]
[ br ]
If < target > is not an array , it is converted to one first .
After that , if < right_operand > is a scalar then it is appended
to the end of the < target > array . If < right_operand > is an array
then all of its items are appended to the end of the < target > array .
If < right_operand > is a hash then all of its value items
are appended to the end of the < target > array .
@ seealso :
[ doc : operators ] Operators [ / doc ]
*/
/*
@ doc : binding
@ title :
Binding operator
@ type :
language
@ short :
Binding operator
@ body :
This operator is a really ugly , poor and clueless attempt to reach at least 1 % of the
power of the perl = ~ operator : D [ br ]
It allows some complex string operations to be performed efficently by operating directly
on the left operand ( in fact this is a lot faster in KVIrc since at least one step of parsing is skipped ) . [ br ]
Its basic syntax is : [ br ]
[ b ] < left_operand > = ~ < operation > [ parameters ] [ / b ] [ br ]
Where < operation > may be one of ' t ' , ' s ' and parameters depend on it . [ br ]
< left_operand > is the target of the < operation > . [ br ]
If < left_operand > is an array or dictionary , the < operation > is executed on each item they contain . [ br ]
Operation ' t ' is the transliteration . [ br ]
The complete syntax with parameters is : [ br ]
[ b ] < left_operand > = ~ t / < search characters > / < replacement characters > / [ / b ] [ br ]
where < search characters > is a string of characters that are replaced with the corresponding
characters in < replacement characters > . [ br ]
This operation can be also named ' y ' or ' tr ' ( to preserve some compatibility with other languages ) . [ br ]
[ example ]
% A = This is a test string
echo % A
% A = ~ tr / abcdefghi / ABCDEFGHI /
echo % A
[ / example ]
Operation ' s ' is the substitution . [ br ]
The complete syntax with parameters is : [ br ]
[ b ] < left_operand > = ~ s / < search pattern > / < replacement pattern > / [ flags ] [ / b ] [ br ]
where < search pattern > is an extended regular expression to be matched in the < left_operand >
and < replacement string > is a special pattern that will replace any occurence found . [ br ]
< search pattern > may contain parentheses to capture parts of the matched text .
< replacement string > can contain the escape sequences \ \ N where N is a number between 1 and 9
to be replaced by the captured text . [ br ]
( We use \ \ N because KVIrc will first unquote the string when parsing . . . ) [ br ]
\ \ 0 is a special escape that will be replaced by the entire match ( is always valid ! ) . [ br ]
[ flags ] may be a combination of the letters ' g ' , ' i ' and ' w ' . [ br ]
' g ' causes the search to be global and not stop after the first occurence of < search pattern > . [ br ]
' i ' causes the search to be case insensitive . [ br ]
' w ' causes the search pattern to be interpreted as a simple wildcard regular expression . [ br ]
[ example ]
% A = This is a test string
echo % A
% A = ~ s / ( [ a - z ] ) i ( [ a - z ] ) / \ \ 1 I \ \ 2 /
echo % A
% A = ~ s / ( [ a - z ] ) i ( [ a - z ] ) / \ \ 1 @ \ \ 2 / gi
echo % A
[ / example ]
@ examples :
[ example ]
% a = " "
% a < < free
[ cmd ] echo [ / cmd ] % a
% a . = bsd
[ cmd ] echo [ / cmd ] % a
% a < < rox
[ cmd ] echo [ / cmd ] % a
% a < , but linux is better !
[ cmd ] echo [ / cmd ] % a
[ example ]
@ seealso :
[ doc : operators ] Operators [ / doc ]
*/
/*
@ doc : operators
@ title :
Operators
@ keyterms :
operator , operators , assignment
@ type :
language
@ short :
Variable operators , assignments & co .
@ body :
[ p ]
Operator constructs are commands just like the other ones .
All the operators work on local or global variables . [ br ]
The generic operator syntax is : [ br ]
[ br ]
& nbsp ; & nbsp ; & nbsp ; & nbsp ; [ b ] < left_operand > < operator > [ right_operand ] [ / b ] [ br ]
[ br ]
where < left_operand > is a variable and [ right_operand ] is a variable , a constant or a complex expression . [ br ]
Some operators do not use [ right_operand ] and do their job directly on < left_operand > [ br ]
[ / p ]
[ table ]
[ tr ] [ td ] Operator [ / td ] [ td ] document [ / td ] [ / td ]
[ tr ] [ td ] = [ / td ] [ td ] [ doc : assignment ] assignment operator [ / doc ] [ / td ] [ / tr ]
[ tr ] [ td ] + + [ / td ] [ td ] [ doc : incrementdecrement ] Increment and decrement operators [ / doc ] [ / td ] [ / tr ]
[ tr ] [ td ] - - [ / td ] [ td ] [ doc : incrementdecrement ] Increment and decrement operators [ / doc ] [ / td ] [ / tr ]
[ tr ] [ td ] + = [ / td ] [ td ] [ doc : selfarithmetic ] Arithmetic self - operators [ / doc ] [ / td ] [ / tr ]
[ tr ] [ td ] - = [ / td ] [ td ] [ doc : selfarithmetic ] Arithmetic self - operators [ / doc ] [ / td ] [ / tr ]
[ tr ] [ td ] * = [ / td ] [ td ] [ doc : selfarithmetic ] Arithmetic self - operators [ / doc ] [ / td ] [ / tr ]
[ tr ] [ td ] / = [ / td ] [ td ] [ doc : selfarithmetic ] Arithmetic self - operators [ / doc ] [ / td ] [ / tr ]
[ tr ] [ td ] % = [ / td ] [ td ] [ doc : selfarithmetic ] Arithmetic self - operators [ / doc ] [ / td ] [ / tr ]
[ tr ] [ td ] | = [ / td ] [ td ] [ doc : selfbitwise ] Bitwise self - operators [ / doc ] [ / td ] [ / tr ]
[ tr ] [ td ] & = [ / td ] [ td ] [ doc : selfbitwise ] Bitwise self - operators [ / doc ] [ / td ] [ / tr ]
[ tr ] [ td ] ^ = [ / td ] [ td ] [ doc : selfbitwise ] Bitwise self - operators [ / doc ] [ / td ] [ / tr ]
[ tr ] [ td ] < < = [ / td ] [ td ] [ doc : selfbitwise ] Bitwise self - operators [ / doc ] [ / td ] [ / tr ]
[ tr ] [ td ] > > = [ / td ] [ td ] [ doc : selfbitwise ] Bitwise self - operators [ / doc ] [ / td ] [ / tr ]
[ tr ] [ td ] . = [ / td ] [ td ] [ doc : stringconcatenation ] String concatenation operators [ / doc ] [ / td ] [ / tr ]
[ tr ] [ td ] < < [ / td ] [ td ] [ doc : stringconcatenation ] String concatenation operators [ / doc ] [ / td ] [ / tr ]
[ tr ] [ td ] < , [ / td ] [ td ] [ doc : stringconcatenation ] String concatenation operators [ / doc ] [ / td ] [ / tr ]
[ tr ] [ td ] < + [ / td ] [ td ] [ doc : arrayconcatenation ] Array concatenation [ / doc ] [ / td ] [ / tr ]
[ tr ] [ td ] = ~ [ / td ] [ td ] [ doc : binding ] Binding operator [ / doc ] [ / td ] [ / tr ]
[ / table ]
*/
KviKvsTreeNodeData * KviKvsParser : : parseBindingOperationParameter ( )
{
KviPointerList < KviKvsTreeNodeData > * l = new KviPointerList < KviKvsTreeNodeData > ;
l - > setAutoDelete ( true ) ;
const TQChar * pBegin = KVSP_curCharPointer ;
for ( ; ; )
{
switch ( KVSP_curCharUnicode )
{
case 0 :
case ' / ' :
case ' \n ' :
case ' \r ' :
// not a part of a parameter
goto end_of_function_parameter ;
break ;
case ' $ ' :
case ' % ' :
{
// this may be a data reference
KviKvsTreeNodeData * p = parseParameterPercentOrDollar ( ) ;
if ( ! p )
{
// this is an error
delete l ;
return 0 ;
}
l - > append ( p ) ;
}
break ;
case ' " ' :
{
// this is a string
KviKvsTreeNodeData * p = parseStringParameter ( ) ;
if ( ! p )
{
// this is an error
delete l ;
return 0 ;
}
l - > append ( p ) ;
}
break ;
default :
{
// anything else is a literal
l - > append ( parseBindingOperationLiteralParameter ( ) ) ;
}
break ;
}
}
end_of_function_parameter :
if ( l - > count ( ) > 1 )
{
// complex parameter needed
return new KviKvsTreeNodeCompositeData ( pBegin , l ) ;
} else {
// a single parameter in the list or empty list at all
l - > setAutoDelete ( false ) ;
KviKvsTreeNodeData * p = l - > first ( ) ;
delete l ;
if ( ! p ) p = new KviKvsTreeNodeConstantData ( KVSP_curCharPointer , new KviKvsVariant ( TQString ( " " ) ) ) ;
return p ;
}
// never reached
return 0 ;
}
KviKvsTreeNodeOperation * KviKvsParser : : parseBindingOperation ( )
{
// t or tr or y
// s
const TQChar * pBegin = KVSP_curCharPointer ;
while ( KVSP_curCharIsLetter ) KVSP_skipChar ;
TQString szOp = TQString ( pBegin , KVSP_curCharPointer - pBegin ) . lower ( ) ;
skipSpaces ( ) ;
if ( KVSP_curCharUnicode ! = ' / ' )
{
error ( KVSP_curCharPointer , __tr2qs ( " Found character '%q' (tqunicode %x) where a slash '/' was expected " ) , KVSP_curCharPointer , KVSP_curCharUnicode ) ;
return 0 ;
}
KVSP_skipChar ;
KviKvsTreeNodeData * pFirst = parseBindingOperationParameter ( ) ;
if ( ! pFirst ) return 0 ;
if ( KVSP_curCharIsEndOfCommand )
{
error ( KVSP_curCharPointer , __tr2qs ( " Unexpected end of command in binding operation, at least two slashes are missing " ) ) ;
delete pFirst ;
return 0 ;
}
if ( KVSP_curCharUnicode ! = ' / ' )
{
error ( KVSP_curCharPointer , __tr2qs ( " Found character '%q' (tqunicode %x) where a slash '/' was expected " ) , KVSP_curCharPointer , KVSP_curCharUnicode ) ;
delete pFirst ;
return 0 ;
}
KVSP_skipChar ;
KviKvsTreeNodeData * pSecond = parseBindingOperationParameter ( ) ;
if ( ! pSecond )
{
delete pFirst ;
return 0 ;
}
if ( KVSP_curCharIsEndOfCommand )
{
error ( KVSP_curCharPointer , __tr2qs ( " Unexpected end of command in binding operation, at least one slash is missing " ) ) ;
delete pFirst ;
return 0 ;
}
if ( KVSP_curCharUnicode ! = ' / ' )
{
error ( KVSP_curCharPointer , __tr2qs ( " Found character '%q' (tqunicode %x) where a slash '/' was expected " ) , KVSP_curCharPointer , KVSP_curCharUnicode ) ;
delete pFirst ;
return 0 ;
}
KVSP_skipChar ;
KviKvsTreeNodeData * pThird = parseCommandParameter ( ) ;
if ( ! pThird )
{
if ( error ( ) )
{
delete pFirst ;
delete pSecond ;
return 0 ;
}
pThird = new KviKvsTreeNodeConstantData ( KVSP_curCharPointer , new KviKvsVariant ( TQString ( " " ) ) ) ;
}
while ( ! KVSP_curCharIsEndOfCommand ) KVSP_skipChar ;
if ( ! KVSP_curCharIsEndOfBuffer ) KVSP_skipChar ;
if ( ( szOp = = " t " ) | | ( szOp = = " tr " ) | | ( szOp = = " y " ) )
{
// transliteration tr/szFirst/szSecond/szFlags
return new KviKvsTreeNodeOperationStringTransliteration ( pBegin , pFirst , pSecond , pThird ) ;
} else if ( szOp = = " s " )
{
// regexp substitution s/szFirst/szSecond/szFlags
return new KviKvsTreeNodeOperationStringSubstitution ( pBegin , pFirst , pSecond , pThird ) ;
}
error ( KVSP_curCharPointer , __tr2qs ( " Unknown binding operation '%Q' " ) , & szOp ) ;
return 0 ;
}
KviKvsTreeNodeOperation * KviKvsParser : : parseOperation ( )
{
// find the operator
const TQChar * pBegin = KVSP_curCharPointer ;
switch ( KVSP_curCharUnicode )
{
case ' = ' :
{
KVSP_skipChar ;
if ( KVSP_curCharUnicode = = ' ~ ' )
{
KVSP_skipChar ;
skipSpaces ( ) ;
if ( KVSP_curCharIsEndOfCommand )
{
error ( KVSP_curCharPointer , __tr2qs ( " Missing right side operand for the binding operator '=~' " ) ) ;
return 0 ;
}
return parseBindingOperation ( ) ;
} else {
skipSpaces ( ) ;
KviKvsTreeNodeData * d = parseOperationRightSide ( true ) ;
if ( ! d ) return 0 ; // error
return new KviKvsTreeNodeOperationAssignment ( pBegin , d ) ;
}
}
break ;
case ' + ' :
KVSP_skipChar ;
switch ( KVSP_curCharUnicode )
{
case ' + ' :
// operator ++
KVSP_skipChar ;
skipSpaces ( ) ;
if ( ! KVSP_curCharIsEndOfCommand )
{
warning ( KVSP_curCharPointer , __tr2qs ( " Trailing garbage ignored after operator '++' " ) ) ;
}
while ( ! KVSP_curCharIsEndOfCommand ) KVSP_skipChar ;
if ( ! KVSP_curCharIsEndOfBuffer ) KVSP_skipChar ;
return new KviKvsTreeNodeOperationIncrement ( pBegin ) ;
break ;
case ' = ' :
// operator +=
KVSP_skipChar ;
skipSpaces ( ) ;
if ( KVSP_curCharIsEndOfCommand )
{
error ( KVSP_curCharPointer , __tr2qs ( " Missing right operand for operator '+=' " ) ) ;
return 0 ;
}
KviKvsTreeNodeData * d = parseOperationRightSide ( true ) ;
if ( ! d ) return 0 ; // error
return new KviKvsTreeNodeOperationSelfSum ( pBegin , d ) ;
break ;
}
break ;
case ' - ' :
KVSP_skipChar ;
switch ( KVSP_curCharUnicode )
{
case ' - ' :
KVSP_skipChar ;
// operator --
skipSpaces ( ) ;
if ( ! KVSP_curCharIsEndOfCommand )
{
warning ( KVSP_curCharPointer , __tr2qs ( " Trailing garbage ignored after operator '--' " ) ) ;
}
while ( ! KVSP_curCharIsEndOfCommand ) KVSP_skipChar ;
if ( ! KVSP_curCharIsEndOfBuffer ) KVSP_skipChar ;
return new KviKvsTreeNodeOperationDecrement ( pBegin ) ;
break ;
case ' > ' :
warning ( KVSP_curCharPointer , __tr2qs ( " This looks a lot like an object handle dereferencing operator '->' but in fact it isn't. Maybe you forgot a '$' just after ? " ) ) ;
break ;
case ' = ' :
// operator -=
KVSP_skipChar ;
skipSpaces ( ) ;
if ( KVSP_curCharIsEndOfCommand )
{
error ( KVSP_curCharPointer , __tr2qs ( " Missing right operand for operator '-=' " ) ) ;
return 0 ;
}
KviKvsTreeNodeData * d = parseOperationRightSide ( true ) ;
if ( ! d ) return 0 ; // error
return new KviKvsTreeNodeOperationSelfSubtraction ( pBegin , d ) ;
break ;
}
break ;
case ' < ' :
KVSP_skipChar ;
switch ( KVSP_curCharUnicode )
{
case ' < ' :
KVSP_skipChar ;
if ( KVSP_curCharUnicode = = ' = ' )
{
KVSP_skipChar ;
skipSpaces ( ) ;
if ( KVSP_curCharIsEndOfCommand )
{
error ( KVSP_curCharPointer , __tr2qs ( " Missing right operand for operator '<<=' " ) ) ;
return 0 ;
}
KviKvsTreeNodeData * d = parseOperationRightSide ( true ) ;
if ( ! d ) return 0 ; // error
return new KviKvsTreeNodeOperationSelfShl ( pBegin , d ) ;
} else {
skipSpaces ( ) ;
if ( KVSP_curCharIsEndOfCommand )
{
error ( KVSP_curCharPointer , __tr2qs ( " Missing right operand for operator '<<' " ) ) ;
return 0 ;
}
KviKvsTreeNodeData * d = parseOperationRightSide ( ) ;
if ( ! d ) return 0 ; // error
return new KviKvsTreeNodeOperationStringAppendWithSpace ( pBegin , d ) ;
}
break ;
case ' , ' :
{
KVSP_skipChar ;
skipSpaces ( ) ;
if ( KVSP_curCharIsEndOfCommand )
{
error ( KVSP_curCharPointer , __tr2qs ( " Missing right operand for operator '<,' " ) ) ;
return 0 ;
}
KviKvsTreeNodeData * d = parseOperationRightSide ( ) ;
if ( ! d ) return 0 ; // error
return new KviKvsTreeNodeOperationStringAppendWithComma ( pBegin , d ) ;
}
break ;
case ' + ' :
{
KVSP_skipChar ;
skipSpaces ( ) ;
if ( KVSP_curCharIsEndOfCommand )
{
error ( KVSP_curCharPointer , __tr2qs ( " Missing right operand for operator '<+' " ) ) ;
return 0 ;
}
KviKvsTreeNodeData * d = parseOperationRightSide ( ) ;
if ( ! d ) return 0 ; // error
return new KviKvsTreeNodeOperationArrayAppend ( pBegin , d ) ;
}
break ;
}
break ;
case ' > ' :
KVSP_skipChar ;
switch ( KVSP_curCharUnicode )
{
case ' > ' :
KVSP_skipChar ;
if ( KVSP_curCharUnicode = = ' = ' )
{
KVSP_skipChar ;
skipSpaces ( ) ;
if ( KVSP_curCharIsEndOfCommand )
{
error ( KVSP_curCharPointer , __tr2qs ( " Missing right operand for operator '>>=' " ) ) ;
return 0 ;
}
KviKvsTreeNodeData * d = parseOperationRightSide ( true ) ;
if ( ! d ) return 0 ; // error
return new KviKvsTreeNodeOperationSelfShr ( pBegin , d ) ;
}
break ;
}
break ;
case ' . ' :
KVSP_skipChar ;
switch ( KVSP_curCharUnicode )
{
case ' = ' :
KVSP_skipChar ;
skipSpaces ( ) ;
if ( KVSP_curCharIsEndOfCommand )
{
error ( KVSP_curCharPointer , __tr2qs ( " Missing right operand for operator '.=' " ) ) ;
return 0 ;
}
KviKvsTreeNodeData * d = parseOperationRightSide ( ) ;
if ( ! d ) return 0 ; // error
return new KviKvsTreeNodeOperationStringAppend ( pBegin , d ) ;
break ;
}
break ;
# define SELF_OPERATOR(__opchar,__opstr,__class) \
case __opchar : \
KVSP_skipChar ; \
switch ( KVSP_curCharUnicode ) \
{ \
case ' = ' : \
KVSP_skipChar ; \
skipSpaces ( ) ; \
if ( KVSP_curCharIsEndOfCommand ) \
{ \
error ( KVSP_curCharPointer , __tr2qs ( " Missing right operand for operator ' " __opstr " =' " ) ) ; \
return 0 ; \
} \
KviKvsTreeNodeData * d = parseOperationRightSide ( true ) ; \
if ( ! d ) return 0 ; \
return new __class ( pBegin , d ) ; \
break ; \
} \
break ;
SELF_OPERATOR ( ' * ' , " * " , KviKvsTreeNodeOperationSelfMultiplication )
SELF_OPERATOR ( ' / ' , " / " , KviKvsTreeNodeOperationSelfDivision )
SELF_OPERATOR ( ' % ' , " % " , KviKvsTreeNodeOperationSelfModulus )
SELF_OPERATOR ( ' | ' , " | " , KviKvsTreeNodeOperationSelfOr )
SELF_OPERATOR ( ' & ' , " & " , KviKvsTreeNodeOperationSelfAnd )
SELF_OPERATOR ( ' ^ ' , " ^ " , KviKvsTreeNodeOperationSelfXor )
}
error ( pBegin , __tr2qs ( " Unknown operator " ) ) ;
return 0 ;
}
KviKvsTreeNodeInstruction * KviKvsParser : : parseVoidFunctionCallOrOperation ( )
{
KVSP_ASSERT ( ( KVSP_curCharUnicode = = ' $ ' ) | | ( KVSP_curCharUnicode = = ' % ' ) | | ( KVSP_curCharUnicode = = ' @ ' ) ) ;
const TQChar * pBegin = KVSP_curCharPointer ;
KviKvsTreeNodeData * r = parsePercentOrDollar ( ) ;
if ( ! r )
{
// must be an error
return 0 ;
}
skipSpaces ( ) ;
if ( KVSP_curCharIsEndOfCommand )
{
// the end of the command
if ( ! r - > isFunctionCall ( ) )
{
if ( r - > isReadOnly ( ) )
{
warning ( pBegin , __tr2qs ( " Unexpected (and senseless) read-only data evaluation " ) ) ;
error ( KVSP_curCharPointer , __tr2qs ( " Syntax error: confused by earlier errors: bailing out " ) ) ;
} else {
error ( KVSP_curCharPointer , __tr2qs ( " Unexpected end of script after a variable reference: expected operator " ) ) ;
}
delete r ;
return 0 ;
} else {
if ( ! KVSP_curCharIsEndOfBuffer ) KVSP_skipChar ;
return new KviKvsTreeNodeVoidFunctionCall ( r - > location ( ) , ( KviKvsTreeNodeFunctionCall * ) r ) ;
}
}
// not the end of a command : an operation
if ( r - > isReadOnly ( ) )
{
// must be followed by the end of a command
if ( r - > isFunctionCall ( ) )
{
error ( KVSP_curCharPointer , __tr2qs ( " Unexpected character '%q' (tqunicode %x) after a void function call: end of instruction expected " ) , KVSP_curCharPointer , KVSP_curCharUnicode ) ;
} else {
warning ( pBegin , __tr2qs ( " Unexpected (and senseless) read-only data evaluation " ) ) ;
warning ( pBegin , __tr2qs ( " Unexpected character '%q' (tqunicode %x) " ) , KVSP_curCharPointer , KVSP_curCharUnicode ) ;
error ( KVSP_curCharPointer , __tr2qs ( " Syntax error: confused by earlier errors: bailing out " ) ) ;
}
delete r ;
return 0 ;
}
// ok.. parse the operation
KviKvsTreeNodeOperation * op = parseOperation ( ) ;
if ( ! op )
{
delete r ;
return 0 ;
}
op - > setTargetVariableReference ( r ) ;
return op ;
}