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.
tdewebdev/kommander/widget/parser.cpp

1244 lines
30 KiB

/***************************************************************************
parser.cpp - Internal parser
-------------------
copyright : (C) 2004-2006 Michal Rudolf <mrudolf@kdewebdwev.org>
***************************************************************************/
/***************************************************************************
* *
* 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 option) any later version. *
* *
***************************************************************************/
#include <klocale.h>
#include "parser.h"
#include "parserdata.h"
#include "kommanderwidget.h"
using namespace Parse;
QString unescape(QString s)
{
return s.replace("\\\"", "\"").replace("\\t", "\t").replace("\\n", "\n").replace("\\\\", "\\");
}
Parser::Parser(ParserData* pData) : m_data(pData), m_start(0), m_error(QString::null), m_errorPosition(0),
m_widget(0)
{
}
Parser::Parser(ParserData* pData, const QString& expr) : m_data(pData), m_start(0),
m_error(QString::null), m_errorPosition(0), m_widget(0)
{
setString(expr);
}
bool Parser::setString(const QString& s)
{
reset();
m_parts.clear();
uint lines = 0;
uint start = 0;
uint i = 0;
while (start < s.length())
{
if (s[start].isSpace() && s[start] != '\n')
start++;
else if (s[start] == '\\' && start < s.length() - 1 && s[start+1] == '\n')
start += 2;
else if (s[start] == ';')
{
insertNode(Semicolon, lines);
start++;
}
else if (s[start] == '\n')
{
if (m_parts.count() && !m_parts.last().isKeyword(Semicolon))
insertNode(Semicolon, lines);
lines++;
start++;
}
else if (s[start] == '\"') // quoted string: "abc"
{
bool escaped = false;
for (i = start + 1; i < s.length() && (s[i] != '\"' || s[i-1] == '\\'); i++)
if (!escaped)
escaped = s[i] == '\\';
if (escaped)
insertNode(unescape(s.mid(start + 1, i - start - 1)), lines);
else
insertNode(s.mid(start + 1, i - start - 1), lines);
start = i+1;
}
else if (s[start].isDigit()) // number: 1000 or 2.45
{
bool decimal = false;
for (i = start+1; s[i].isDigit() || (!decimal && s[i] == QChar('.')); i++)
if (s[i] == '.')
decimal = true;
if (decimal)
insertNode(s.mid(start, i - start).toDouble(), lines);
else
insertNode(s.mid(start, i - start).toInt(), lines);
start = i;
}
else if (s[start].isLetter() || s[start] == '_') // keyword
{
for (i = start+1; s[i].isLetterOrNumber() || s[i] == '_'; i++)
;
QString name = s.mid(start, i - start);
insertNode(ParseNode(m_data->stringToKeyword(name), name), lines);
start = i;
} // comment
else if (s[start] == '#' || (s[start] == '/' && start < s.length() +1 && s[start+1] == '/'))
{
while (start < s.length() && s[start] != '\n')
start++;
} // enable /* */ block comments
else if (s[start] == '/' && start < s.length() +1 && s[start+1] == '*')
{
start += 2;
while (start < s.length() +1 && !(s[start] == '*' && s[start+1] == '/'))
{
start++;
}
start += 2;
} // special keyword: <>
else if (m_data->stringToKeyword(s.mid(start, 2)) <= LastRealKeyword)
{
insertNode(m_data->stringToKeyword(s.mid(start, 2)), lines);
start += 2;
} // special keyword: <
else if (m_data->stringToKeyword(s.mid(start, 1)) <= LastRealKeyword)
{
insertNode(m_data->stringToKeyword(s.mid(start, 1)), lines);
start++;
}
else // Bad character
{
insertNode(s.mid(start, 1), lines);
setError(i18n("Invalid character: '%1'").arg(s[start]), m_parts.count()-1);
return false;
}
}
return true;
}
void Parser::setWidget(KommanderWidget* w)
{
m_widget = w;
}
void Parser::insertNode(ParseNode p, int line)
{
p.setContext(line);
m_parts.append(p);
}
QString Parser::errorMessage() const
{
return m_error;
}
QString Parser::function(ParserData* data, const QString& name, const QStringList& params)
{
ParameterList par;
for (QStringList::ConstIterator Iter = params.begin(); Iter != params.end(); ++Iter)
par.append(*Iter);
Function f = data->function(name);
return f.execute(0, par).toString();
}
QString Parser::expression(Mode mode)
{
reset();
ParseNode p = parseExpression(mode);
if (!isError())
return p.toString();
else
return QString();
}
bool Parser::isError() const
{
return !m_error.isNull();
}
bool Parser::command(Mode mode)
{
reset();
parseCommand(mode);
return !isError();
}
bool Parser::parse(Mode mode)
{
reset();
parseBlock(mode);
return !isError();
}
int Parser::errorLine() const
{
if (isError())
return m_parts[m_errorPosition].context();
else
return -1;
}
ParseNode Parser::parseConstant(Parse::Mode)
{
ParseNode p = next();
m_start++;
if (!p.isValue())
{
setError(i18n("Constant value expected"));
return ParseNode();
}
return p;
}
//attempting to allow assign or copy of array, so far with no joy
ParseNode Parser::parseValue(Mode mode)
{
ParseNode p = next();
//QString p2 = QString(p.toString());
//qDebug("parseValue p2 = "+p2);
if (isFunction())
return parseFunction(mode);
else if (isWidget())
return parseWidget(mode);
else if (tryVariable(CheckOnly))
{
if (tryKeyword(LeftBracket, CheckOnly))
{
QString index = parseValue(mode).toString();
if (tryKeyword(DoubleBracket, CheckOnly))
{//2D array "matrix"
QString index2 = parseValue(mode).toString();
tryKeyword(RightBracket);
QString arr = p.variableName();
return matrixValue(arr, index, index2);
}
tryKeyword(RightBracket);
QString arr = p.variableName();
return arrayValue(arr, index);
}
else if (tryKeyword(Dot, CheckOnly))
{
QString value = variable(p.variableName()).toString();
if (m_widget && m_widget->isWidget(value))
{
m_start--;
return parseWidget(mode, value);
}else if (mode == Execute)
{
setError(i18n("'%1' (%2) is not a widget").arg(p.variableName()).arg(variable(p.variableName()).toString()));
return ParseNode();
} else
{
//this means it looks like a widget, but it is unknown. As we only check
//the syntax, we should ignore the error an parse as a widget.
m_start = m_start - 2;
return parseWidget(mode);
}
}
else if (tryKeyword(LeftParenthesis, CheckOnly))
{
setError(i18n("'%1' is not a function").arg(p.variableName()));
return ParseNode();
}
else
p = variable(p.variableName());
}
else if (tryKeyword(False, CheckOnly))
return ParseNode(0);
else if (tryKeyword(True, CheckOnly))
return ParseNode(1);
/* else if (isArray(p2))
{
qDebug("returning array fpr p2");
return p2;
}*/
else if (p.isKeyword())
setError(i18n("Expected value"));
else // single value
m_start++;
return p;
}
ParseNode Parser::parseMultiply(Mode mode)
{
ParseNode p = parseParenthesis(mode);
while (m_data->keywordGroup(next().keyword()) == GroupMultiply)
{
Keyword k = next().keyword();
m_start++;
ParseNode p2 = parseParenthesis(mode);
ValueType type = p.commonType(p2);
if (mode == Execute)
{
if (k == Multiply)
if (type == ValueInt)
p = p.toInt() * p2.toInt();
else
p = p.toDouble() * p2.toDouble();
else if (k == Divide)
{
if (p2.toDouble() == 0.0)
setError(i18n("Divide by zero"));
else
if (type == ValueInt)
p = p.toInt() / p2.toInt();
else
p = p.toDouble() / p2.toDouble();
}
else /* k == Mod */
{
if (p2.toInt() == 0)
setError(i18n("Divide by zero"));
else
p = p.toInt() - p.toInt() / p2.toInt() * p2.toInt();
}
}
}
return p;
}
ParseNode Parser::parseAdd(Mode mode)
{
ParseNode p = parseMultiply(mode);
while (m_data->keywordGroup(next().keyword()) == GroupAdd)
{
Keyword k = next().keyword();
m_start++;
ParseNode p2 = parseMultiply(mode);
ValueType type = p.commonType(p2);
if (mode == Execute)
{
if (k == Plus)
if (type == ValueString)
p = QString(p.toString() + p2.toString());
else if (type == ValueDouble)
p = p.toDouble() + p2.toDouble();
else
p = p.toInt() + p2.toInt();
else /* k == Minus */
if (type == ValueDouble)
p = p.toDouble() - p2.toDouble();
else
p = p.toInt() - p2.toInt();
}
}
return p;
}
ParseNode Parser::parseSignedNumber(Mode mode)
{
if (tryKeyword(Minus, CheckOnly))
{
ParseNode p = parseValue(mode);
if (p.type() == ValueDouble)
return ParseNode(-p.toDouble());
else
return ParseNode(-p.toInt());
}
else
return parseValue(mode);
}
ParseNode Parser::parseComparison(Mode mode)
{
ParseNode p1 = parseAdd(mode);
if (m_data->keywordGroup(next().keyword()) == GroupComparison)
{
Keyword k = next().keyword();
m_start++;
ParseNode p2 = parseAdd(mode);
switch (k) {
case Less: return ParseNode(p1 < p2);
case LessEqual: return ParseNode(p1 <= p2);
case Equal: return ParseNode(p1 == p2);
case NotEqual: return ParseNode(p1 != p2);
case GreaterEqual: return ParseNode(p1 >= p2);
case Greater: return ParseNode(p1 > p2);
default: ;
}
}
return p1;
}
ParseNode Parser::parseParenthesis(Mode mode)
{
if (tryKeyword(LeftParenthesis, CheckOnly))
{
ParseNode p = parseExpression(mode);
tryKeyword(RightParenthesis);
return p;
}
else
return parseSignedNumber(mode);
}
ParseNode Parser::parseNot(Mode mode)
{
if (tryKeyword(Not, CheckOnly))
return !parseComparison(mode).toBool();
else
return parseComparison(mode);
}
ParseNode Parser::parseAnd(Mode mode)
{
ParseNode p = parseNot(mode);
while (tryKeyword(And, CheckOnly))
{
if (p == false)
parseNot(CheckOnly);
else
p = parseNot(mode);
}
return p;
}
ParseNode Parser::parseOr(Mode mode)
{
ParseNode p = parseAnd(mode);
while (tryKeyword(Or, CheckOnly))
{
if (p == true)
parseAnd(CheckOnly);
else
p = parseAnd(mode);
}
return p;
}
ParseNode Parser::parseCondition(Mode mode)
{
return parseOr(mode);
}
ParseNode Parser::parseExpression(Mode mode)
{
return parseOr(mode);
}
ParseNode Parser::parseFunction(Mode mode)
{
int pos = m_start;
QString name = next().variableName();
//qDebug("Parsing function: "+name);
Function f = m_data->function(name);
m_start++;
ParameterList params;
if (tryKeyword(LeftParenthesis, CheckOnly) && !tryKeyword(RightParenthesis, CheckOnly))
{
do {
params.append(parseExpression(mode));
} while (tryKeyword(Comma, CheckOnly));
tryKeyword(RightParenthesis);
}
if (f.minArgs() > params.count())
setError(i18n("in function '%1': %2").arg(name).arg(i18n("too few parameters")), pos);
else if (f.maxArgs() < params.count())
setError(i18n("in function '%1': %2").arg(name).arg(i18n("too many parameters")), pos);
else if (mode == Execute)
{
ParseNode p = f.execute(this, params);
if (!p.isValid())
{
setError(i18n("in function '%1': %2").arg(name).arg(p.errorMessage()), pos);
return ParseNode();
}
else
return p;
}
return ParseNode();
}
ParseNode Parser::parseWidget(Mode mode, const QString &widgetName)
{
int pos = m_start;
QString widget;
if (widgetName.isNull())
widget = nextVariable(mode);
else
widget = widgetName;
Function f = m_data->function("internalDcop");
if (!tryKeyword(Dot))
return ParseNode();
QString var = nextVariable();
if (var.isNull())
return ParseNode();
ParameterList params;
params.append(var);
params.append(widget);
if (tryKeyword(LeftParenthesis, CheckOnly) && !tryKeyword(RightParenthesis, CheckOnly))
{
do {
params.append(parseExpression(mode));
} while (tryKeyword(Comma, CheckOnly));
tryKeyword(RightParenthesis);
}
if (mode == Execute)
{
ParseNode p = f.execute(this, params);
if (!p.isValid())
{
setError(i18n("in widget function '%1.%2': %3").arg(widget).arg(var).arg(p.errorMessage()), pos);
return ParseNode();
}
else
return p;
}
return ParseNode();
}
ParseNode Parser::parseAssignment(Mode mode)
{
QString var = nextVariable();
//qDebug("var = "+var+" Pos:"+QString::number(m_start));
if (tryKeyword(LeftBracket, CheckOnly))
{
QString index = parseValue(mode).toString();
if (tryKeyword(DoubleBracket, CheckOnly))
{//2D array "matrix"
ParseNode p1 = next(); //move along...
QString index2 = parseValue(mode).toString();
tryKeyword(RightBracket);
p1 = next();
ParseNode p2 = matrixValue(var, index, index2);
if (p1.isKeyword(PlusEqual))
{
tryKeyword(PlusEqual);
ParseNode p = parseExpression(mode);
if (mode == Execute)
{
if (p2.type() == ValueString)
p = QString(p2.toString() + p.toString());
else if (p2.type() == ValueDouble)
p = p2.toDouble() + p.toDouble();
else
p = p2.toInt() + p.toInt();
setMatrix(var, index, index2, p);
}
}
else if (p1.isKeyword(MinusEqual))
{
tryKeyword(MinusEqual);
ParseNode p = parseExpression(mode);
if (mode == Execute)
{
if (p2.type() == ValueDouble)
p = p2.toDouble() - p.toDouble();
else
p = p2.toInt() - p.toInt();
setMatrix(var, index, index2, p);
}
}
else if (p1.isKeyword(Increment))
{
tryKeyword(Increment);
if (mode == Execute)
{
p2 = p2.toInt() + 1;
setMatrix(var, index, index2, p2);
}
}
else if (p1.isKeyword(Decrement))
{
tryKeyword(Decrement);
if (mode == Execute)
{
p2 = p2.toInt() - 1;
setMatrix(var, index, index2, p2);
}
}
else
{
tryKeyword(Assign);
ParseNode p = parseExpression(mode);
if (mode == Execute)
setMatrix(var, index, index2, p);
}
}
else
{
tryKeyword(RightBracket);
ParseNode p1 = next();
// seems awkward and pedantic but array values are now handled like variables
// for special assign with oparator
ParseNode p2 = arrayValue(var, index);
if (p1.isKeyword(PlusEqual))
{
tryKeyword(PlusEqual);
ParseNode p = parseExpression(mode);
if (mode == Execute)
{
if (p2.type() == ValueString)
p = QString(p2.toString() + p.toString());
else if (p2.type() == ValueDouble)
p = p2.toDouble() + p.toDouble();
else
p = p2.toInt() + p.toInt();
setArray(var, index, p);
}
}
else if (p1.isKeyword(MinusEqual))
{
tryKeyword(MinusEqual);
ParseNode p = parseExpression(mode);
if (mode == Execute)
{
if (p2.type() == ValueDouble)
p = p2.toDouble() - p.toDouble();
else
p = p2.toInt() - p.toInt();
setArray(var, index, p);
}
}
else if (p1.isKeyword(Increment))
{
tryKeyword(Increment);
if (mode == Execute)
{
p2 = p2.toInt() + 1;
setArray(var, index, p2);
}
}
else if (p1.isKeyword(Decrement))
{
tryKeyword(Decrement);
if (mode == Execute)
{
p2 = p2.toInt() - 1;
setArray(var, index, p2);
}
}
else
{
tryKeyword(Assign);
ParseNode p = parseExpression(mode);
if (mode == Execute)
setArray(var, index, p);
}
}
}
else if (tryKeyword(Assign, CheckOnly))
{
ParseNode p = parseExpression(mode);
if (mode == Execute)
{
setVariable(var, p);
}
}
else if (tryKeyword(PlusEqual, CheckOnly))
{
ParseNode p = parseExpression(mode);
if (mode == Execute)
{
ParseNode p2 = variable(var);
if (p2.type() == ValueString)
p = QString(p2.toString() + p.toString());
else if (p2.type() == ValueDouble)
p = p2.toDouble() + p.toDouble();
else
p = p2.toInt() + p.toInt();
setVariable(var, p);
}
}
else if (tryKeyword(MinusEqual, CheckOnly))
{
ParseNode p = parseExpression(mode);
if (mode == Execute)
{
ParseNode p2 = variable(var);
if (p2.type() == ValueDouble)
p = p2.toDouble() - p.toDouble();
else
p = p2.toInt() - p.toInt();
setVariable(var, p);
}
}
else if (tryKeyword(Increment, CheckOnly))
{
//ParseNode p = parseExpression(mode);
if (mode == Execute)
{
ParseNode p = variable(var);
p = p.toInt() + 1;
setVariable(var, p);
}
}
else if (tryKeyword(Decrement, CheckOnly))
{
//ParseNode p = parseExpression(mode);
if (mode == Execute)
{
ParseNode p = variable(var);
p = p.toInt() - 1;
setVariable(var, p);
}
}
else if (tryKeyword(Dot, CheckOnly))
{
QString value = variable(var).toString();
if (m_widget && m_widget->isWidget(value))
{
m_start--;
return parseWidget(mode, value);
} else
if (mode == CheckOnly)
{
//this means it looks like a widget, but it is unknown. As we only check
//the syntax, we should ignore the error an parse as a widget.
m_start = m_start - 2;
return parseWidget(mode);
} else
setError(i18n("'%1' is not a widget").arg(var));
}
else if (tryKeyword(LeftParenthesis, CheckOnly))
setError(i18n("'%1' is not a function").arg(var));
else
setError(i18n("Unexpected symbol after variable '%1'").arg(var));
return ParseNode();
}
Flow Parser::parseIf(Mode mode)
{
ParseNode p = next();
Flow flow = FlowStandard;
bool matched = false;
bool thenFound = false;
do {
m_start++;
Mode m = matched ? CheckOnly : mode;
p = parseCondition(m);
thenFound = tryKeyword(Then, CheckOnly);
if (!thenFound)
tryKeyword(LeftCurlyBrace);
bool condition = !matched && p.toBool();
if (condition)
{
flow = parseBlock(mode);
if (flow == FlowExit)
return flow;
}
else
parseBlock(CheckOnly);
matched = matched || p.toBool();
if (!thenFound)
tryKeyword(RightCurlyBrace);
} while (nextElseIf() == true);
bool braceFound = false;
if (tryKeyword(Else, CheckOnly))
{
braceFound = tryKeyword(LeftCurlyBrace, CheckOnly);
if (!matched)
flow = parseBlock(mode);
else
parseBlock(CheckOnly);
}
if (braceFound)
tryKeyword(RightCurlyBrace);
if (thenFound)
tryKeyword(Endif);
return flow;
}
bool Parser::nextElseIf()
{
ParseNode p1 = next();
if (p1.isKeyword(Elseif))
return true;
else
{
ParseNode p2 = next();
if (p1.isKeyword(Else) && p2.isKeyword(If) )
return true;
}
return false;
}
Parse::Flow Parser::parseWhile(Mode mode)
{
m_start++;
int start = m_start;
bool running = true;
Parse::Flow flow = FlowStandard;
bool doFound = false;
while (running)
{
m_start = start;
ParseNode p = parseCondition(mode);
doFound = tryKeyword(Do, CheckOnly);
if (!doFound && !tryKeyword(LeftCurlyBrace))
break;
running = p.toBool();
flow = parseBlock(running ? mode : CheckOnly);
if ( flow == FlowBreak || flow == FlowExit)
break;
}
if (flow != FlowExit)
{
if (doFound)
tryKeyword(End);
else
tryKeyword(RightCurlyBrace);
return FlowStandard;
}
else
return FlowExit;
}
Parse::Flow Parser::parseFor(Mode mode)
{
m_start++;
QString var = nextVariable();
tryKeyword(Assign);
int start = parseExpression(mode).toInt();
tryKeyword(To);
int end = parseExpression(mode).toInt();
int step = 1;
if (tryKeyword(Step, CheckOnly))
step = parseExpression(mode).toInt();
bool doFound = tryKeyword(Do, CheckOnly);
if (!doFound)
tryKeyword(LeftCurlyBrace);
int block = m_start;
Parse::Flow flow = FlowStandard;
if (end >= start && step > 0)
{
for (int i = start; i <= end; i+=step)
{
m_start = block;
setVariable(var, ParseNode(i));
flow = parseBlock(mode);
if (flow == FlowBreak || flow == FlowExit)
break;
}
} else if (end <= start && step < 0)
{
for (int i = start; i >= end; i+=step)
{
m_start = block;
setVariable(var, ParseNode(i));
flow = parseBlock(mode);
if (flow == FlowBreak || flow == FlowExit)
break;
}
} else
parseBlock(Parse::CheckOnly);
if (flow != FlowExit)
{
if (doFound)
tryKeyword(End);
else
tryKeyword(RightCurlyBrace);
return FlowStandard;
}
else
return FlowExit;
}
Parse::Flow Parser::parseForeach(Mode mode)
{
m_start++;
QString var = nextVariable();
QString var2 = "";
bool matrixfound = tryKeyword(ArrKeyVal, CheckOnly);
if (matrixfound == true)
{
m_start--;
tryKeyword(ArrKeyVal);
var2 = nextVariable();
}
tryKeyword(In);
QString arr = nextVariable();
bool doFound = tryKeyword(Do, CheckOnly);
if (!doFound)
tryKeyword(LeftCurlyBrace);
int start = m_start;
Parse::Flow flow = FlowStandard;
if (isArray(arr) && array(arr).count() && !matrixfound)
{
const QMap<QString, ParseNode> A = array(arr);
for (QMapConstIterator<QString, ParseNode> It = A.begin(); It != A.end(); ++It)
{
m_start = start;
setVariable(var, It.key());
flow = parseBlock(mode);
if (flow == FlowBreak || flow == FlowExit)
break;
}
}
else if (isMatrix(arr) && matrix(arr).count() )
{
const QMap<QString, QMap<QString, ParseNode> > A = matrix(arr);
for (QMapConstIterator<QString, QMap<QString, ParseNode> > It = A.begin(); It != A.end(); ++It)
{
m_start = start;
setVariable(var, It.key());
if (matrixfound == true)
{
const QMap<QString, ParseNode> B = It.data();
for (QMapConstIterator<QString, ParseNode> It2 = B.begin(); It2 != B.end(); ++It2 )
{
m_start = start;
setVariable(var2, It2.key());
flow = parseBlock(mode);
if (flow == FlowBreak || flow == FlowExit)
break;
}
}
else
{
flow = parseBlock(mode);
if (flow == FlowBreak || flow == FlowExit)
break;
}
}
}
else
parseBlock(CheckOnly);
if (flow != FlowExit)
{
if (doFound)
tryKeyword(End);
else
tryKeyword(RightCurlyBrace);
return FlowStandard;
}
else
return FlowExit;
}
void Parser::parseSwitch(Mode mode)
{
m_start++;
QString var = nextVariable();
ParseNode caseValue = variable(var);
bool executed = false;
bool braceFound = false;
braceFound = tryKeyword(LeftCurlyBrace, CheckOnly);
tryKeyword(Semicolon, CheckOnly);
while (tryKeyword(Case, CheckOnly))
{
ParseNode p = parseConstant();
bool matched = mode == Execute && p == caseValue;
parseBlock(matched ? Execute : CheckOnly);
if (matched)
executed = true;
}
if (tryKeyword(Else, CheckOnly))
parseBlock(executed ? CheckOnly : mode);
if (!braceFound)
tryKeyword(End);
else
tryKeyword(RightCurlyBrace);
}
Flow Parser::parseCommand(Mode mode)
{
ParseNode p = next();
QString p2 = p.toString();
//qDebug("Parsing command: "+p2);
if (next().isKeyword(If))
return parseIf(mode);
else if (next().isKeyword(While))
return parseWhile(mode);
else if (next().isKeyword(For))
return parseFor(mode);
else if (next().isKeyword(Foreach))
return parseForeach(mode);
else if (next().isKeyword(Switch))
parseSwitch(mode);
else if (tryKeyword(Continue, CheckOnly))
return FlowContinue;
else if (tryKeyword(Break, CheckOnly))
return FlowBreak;
else if (isFunction())
{
QString name = next().variableName();
parseFunction(mode);
if (name == "return" && mode == Execute)
return FlowExit;
}
else if (isWidget())
parseWidget(mode);
else if (next().isVariable())
parseAssignment(mode);
else if (tryKeyword(Exit, CheckOnly))
{
if (mode == Execute)
return FlowExit;
}
return FlowStandard;
}
Flow Parser::parseBlock(Mode mode)
{
Flow flow = parseCommand(mode);
while (tryKeyword(Semicolon, CheckOnly) && flow != FlowExit)
{
if (flow == FlowStandard)
flow = parseCommand(mode);
else
parseCommand(CheckOnly);
}
return flow;
}
ParseNode Parser::next() const
{
if (isError() || m_start >= m_parts.count())
return ParseNode();
return m_parts[m_start];
}
bool Parser::tryKeyword(Keyword k, Mode mode)
{
if (next().isKeyword(k))
{
m_start++;
return true;
}
if (mode == Execute)
{
if (k == Dot)
setError(i18n("Expected '%1'<br><br>Possible cause of the error is having a variable with the same name as a widget").arg(m_data->keywordToString(k)));
else
setError(i18n("Expected '%1' got '%2'.").arg(m_data->keywordToString(k)).arg(next().toString()));
}
return false;
}
bool Parser::tryVariable(Mode mode)
{
if (next().isVariable())
{
QString name = next().variableName();
m_start++;
return true;
}
if (mode == Execute)
setError(i18n("Expected variable"));
return false;
}
QString Parser::nextVariable(Mode mode)
{
if (next().isVariable())
{
QString name = next().variableName();
m_start++;
return name;
}
else if (mode == Execute)
setError(i18n("Expected variable"));
return QString();
}
bool Parser::isFunction() const
{
return next().isVariable() && m_data->isFunction(next().variableName());
}
bool Parser::isWidget() const
{
return m_widget && next().isVariable() && m_widget->isWidget(next().variableName());
}
void Parser::reset()
{
m_start = 0;
m_error = QString::null;
m_errorPosition = 0;
}
void Parser::setError(const QString& msg)
{
setError(msg, m_start);
}
void Parser::setError(const QString& msg, int pos)
{
if (m_error.isNull())
{
m_errorPosition = pos;
m_error = msg;
}
}
void Parser::setVariable(const QString& name, ParseNode value)
{
if (isGlobal(name))
m_globalVariables[name] = value;
else
m_variables[name] = value;
}
ParseNode Parser::variable(const QString& name) const
{
if (isGlobal(name))
return m_globalVariables.contains(name) ? m_globalVariables[name] : ParseNode();
else
return m_variables.contains(name) ? m_variables[name] : ParseNode();
}
bool Parser::isGlobal(const QString& name) const
{
return !name.isEmpty() && name[0] == '_';
}
bool Parser::isVariable(const QString& name) const
{
return m_variables.contains(name) || m_globalVariables.contains(name);
}
void Parser::unsetVariable(const QString& key)
{
if (isGlobal(key))
m_globalVariables.remove(key);
else
m_variables.remove(key);
}
const QMap<QString, ParseNode>& Parser::array(const QString& name) const
{
if (isGlobal(name))
return m_globalArrays[name];
else
return m_arrays[name];
}
bool Parser::isArray(const QString& name) const
{
return m_arrays.contains(name) || m_globalArrays.contains(name);
}
void Parser::setArray(const QString& name, const QString& key, ParseNode value)
{
if (isGlobal(name))
m_globalArrays[name][key] = value;
else
m_arrays[name][key] = value;
}
void Parser::unsetArray(const QString& name, const QString& key)
{
if (isGlobal(name))
{
if (key.isNull())
m_globalArrays.remove(name);
else if (isArray(name))
m_globalArrays[name].remove(key);
}
else
{
if (key.isNull())
m_arrays.remove(name);
else if (isArray(name))
m_arrays[name].remove(key);
}
}
ParseNode Parser::arrayValue(const QString& name, const QString& key) const
{
if (!isArray(name))
return ParseNode();
if (isGlobal(name))
return m_globalArrays[name].contains(key) ? m_globalArrays[name][key] : ParseNode();
else
return m_arrays[name].contains(key) ? m_arrays[name][key] : ParseNode();
}
// 2D arrays "Matrix"
const QMap<QString, QMap<QString, ParseNode> >& Parser::matrix(const QString& name) const
{
if (isGlobal(name))
return m_globalMatrices[name];
else
return m_matrices[name];
}
bool Parser::isMatrix(const QString& name) const
{
return m_matrices.contains(name) || m_globalMatrices.contains(name);
}
void Parser::setMatrix(const QString& name, const QString& keyr, const QString& keyc, ParseNode value)
{
if (isGlobal(name))
m_globalMatrices[name][keyr][keyc] = value;
else
m_matrices[name][keyr][keyc] = value;
}
void Parser::unsetMatrix(const QString& name, const QString& keyr, const QString& keyc)
{
if (isGlobal(name))
{
if (keyr.isNull())
m_globalMatrices.remove(name);
else if (isMatrix(name))
{
if (keyc.isNull())
m_globalMatrices[name].remove(keyr);
else
m_globalMatrices[name][keyr].remove(keyc);
}
}
else
{
if (keyr.isNull())
m_matrices.remove(name);
else if (isMatrix(name))
{
if (keyc.isNull())
m_matrices[name].remove(keyr);
else
m_matrices[name][keyr].remove(keyc);
}
}
}
ParseNode Parser::matrixValue(const QString& name, const QString& keyr, const QString& keyc) const
{
if (!isMatrix(name))
return ParseNode();
if (isGlobal(name))
return m_globalMatrices[name].contains(keyr) && m_globalMatrices[name][keyr].contains(keyc) ? m_globalMatrices[name][keyr][keyc] : ParseNode();
else
return m_matrices[name].contains(keyr) && m_matrices[name][keyr].contains(keyc) ? m_matrices[name][keyr][keyc] : ParseNode();
}
KommanderWidget* Parser::currentWidget() const
{
return m_widget;
}
QMap<QString, ParseNode> Parser::m_globalVariables;
QMap<QString, QMap<QString, ParseNode> > Parser::m_globalArrays;
QMap<QString, QMap<QString, QMap<QString, ParseNode> > > Parser::m_globalMatrices;