You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kvirc/src/kvirc/kvs/kvi_kvs_parser_lside.cpp

1197 lines
38 KiB

//=============================================================================
//
// 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 &nbsp; &nbsp; &nbsp; &nbsp; 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])/\\1I\\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 contains "Ciao ciao Pragma"[br]
Append a '!' character
[example]
%var <+ !
[/example]
%var now contains "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 contains "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 &nbsp; &nbsp; &nbsp; &nbsp; 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])/\\1I\\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' (unicode %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' (unicode %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' (unicode %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' (unicode %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' (unicode %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;
}