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.
900 lines
21 KiB
900 lines
21 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;
|
|
|
|
TQString unescape(TQString s)
|
|
{
|
|
return s.replace("\\\"", "\"").replace("\\t", "\t").replace("\\n", "\n").replace("\\\\", "\\");
|
|
}
|
|
|
|
Parser::Parser(ParserData* pData) : m_data(pData), m_start(0), m_error(TQString()), m_errorPosition(0),
|
|
m_widget(0)
|
|
{
|
|
}
|
|
|
|
Parser::Parser(ParserData* pData, const TQString& expr) : m_data(pData), m_start(0),
|
|
m_error(TQString()), m_errorPosition(0), m_widget(0)
|
|
{
|
|
setString(expr);
|
|
}
|
|
|
|
bool Parser::setString(const TQString& 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] == TQChar('.')); 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++)
|
|
;
|
|
TQString 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++;
|
|
} // 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);
|
|
}
|
|
|
|
TQString Parser::errorMessage() const
|
|
{
|
|
return m_error;
|
|
}
|
|
|
|
TQString Parser::function(ParserData* data, const TQString& name, const TQStringList& params)
|
|
{
|
|
ParameterList par;
|
|
for (TQStringList::ConstIterator Iter = params.begin(); Iter != params.end(); ++Iter)
|
|
par.append(*Iter);
|
|
Function f = data->function(name);
|
|
return f.execute(0, par).toString();
|
|
}
|
|
|
|
TQString Parser::expression(Mode mode)
|
|
{
|
|
reset();
|
|
ParseNode p = parseExpression(mode);
|
|
if (!isError())
|
|
return p.toString();
|
|
else
|
|
return TQString();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
ParseNode Parser::parseValue(Mode mode)
|
|
{
|
|
ParseNode p = next();
|
|
if (isFunction())
|
|
return parseFunction(mode);
|
|
else if (isWidget())
|
|
return parseWidget(mode);
|
|
else if (tryVariable(CheckOnly))
|
|
{
|
|
if (tryKeyword(LeftBracket, CheckOnly))
|
|
{
|
|
TQString index = parseValue(mode).toString();
|
|
tryKeyword(RightBracket);
|
|
TQString arr = p.variableName();
|
|
return arrayValue(arr, index);
|
|
}
|
|
else if (tryKeyword(Dot, CheckOnly))
|
|
{
|
|
TQString 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 (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 = TQString(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;
|
|
TQString name = next().variableName();
|
|
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 TQString &widgetName)
|
|
{
|
|
int pos = m_start;
|
|
TQString widget;
|
|
if (widgetName.isNull())
|
|
widget = nextVariable(mode);
|
|
else
|
|
widget = widgetName;
|
|
Function f = m_data->function("internalDcop");
|
|
|
|
if (!tryKeyword(Dot))
|
|
return ParseNode();
|
|
TQString 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)
|
|
{
|
|
TQString var = nextVariable();
|
|
if (tryKeyword(LeftBracket, CheckOnly))
|
|
{
|
|
TQString index = parseValue(mode).toString();
|
|
tryKeyword(RightBracket);
|
|
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(Dot, CheckOnly))
|
|
{
|
|
TQString 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;
|
|
do {
|
|
m_start++;
|
|
Mode m = matched ? CheckOnly : mode;
|
|
p = parseCondition(m);
|
|
tryKeyword(Then);
|
|
bool condition = !matched && p.toBool();
|
|
if (condition)
|
|
{
|
|
flow = parseBlock(mode);
|
|
if (flow == FlowExit)
|
|
return flow;
|
|
}
|
|
else
|
|
parseBlock(CheckOnly);
|
|
matched = matched || p.toBool();
|
|
} while (next().isKeyword(Elseif));
|
|
if (tryKeyword(Else, CheckOnly))
|
|
{
|
|
if (!matched)
|
|
flow = parseBlock(mode);
|
|
else
|
|
parseBlock(CheckOnly);
|
|
}
|
|
tryKeyword(Endif);
|
|
return flow;
|
|
}
|
|
|
|
Parse::Flow Parser::parseWhile(Mode mode)
|
|
{
|
|
m_start++;
|
|
int start = m_start;
|
|
bool running = true;
|
|
Parse::Flow flow = FlowStandard;
|
|
while (running)
|
|
{
|
|
m_start = start;
|
|
ParseNode p = parseCondition(mode);
|
|
if (!tryKeyword(Do))
|
|
break;
|
|
running = p.toBool();
|
|
flow = parseBlock(running ? mode : CheckOnly);
|
|
if ( flow == FlowBreak || flow == FlowExit)
|
|
break;
|
|
}
|
|
if (flow != FlowExit)
|
|
{
|
|
tryKeyword(End);
|
|
return FlowStandard;
|
|
}
|
|
else
|
|
return FlowExit;
|
|
}
|
|
|
|
Parse::Flow Parser::parseFor(Mode mode)
|
|
{
|
|
m_start++;
|
|
TQString 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();
|
|
tryKeyword(Do);
|
|
int block = m_start;
|
|
Parse::Flow flow = FlowStandard;
|
|
if (end >= start)
|
|
{
|
|
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)
|
|
{
|
|
tryKeyword(End);
|
|
return FlowStandard;
|
|
}
|
|
else
|
|
return FlowExit;
|
|
}
|
|
|
|
Parse::Flow Parser::parseForeach(Mode mode)
|
|
{
|
|
m_start++;
|
|
TQString var = nextVariable();
|
|
tryKeyword(In);
|
|
TQString arr = nextVariable();
|
|
tryKeyword(Do);
|
|
int start = m_start;
|
|
Parse::Flow flow = FlowStandard;
|
|
if (isArray(arr) && array(arr).count())
|
|
{
|
|
const TQMap<TQString, ParseNode> A = array(arr);
|
|
for (TQMapConstIterator<TQString, 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
|
|
parseBlock(CheckOnly);
|
|
if (flow != FlowExit)
|
|
{
|
|
tryKeyword(End);
|
|
return FlowStandard;
|
|
}
|
|
else
|
|
return FlowExit;
|
|
}
|
|
|
|
void Parser::parseSwitch(Mode mode)
|
|
{
|
|
m_start++;
|
|
TQString var = nextVariable();
|
|
ParseNode caseValue = variable(var);
|
|
bool executed = false;
|
|
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);
|
|
tryKeyword(End);
|
|
}
|
|
|
|
Flow Parser::parseCommand(Mode mode)
|
|
{
|
|
ParseNode p = next();
|
|
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())
|
|
{
|
|
TQString 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'").arg(m_data->keywordToString(k)));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Parser::tryVariable(Mode mode)
|
|
{
|
|
if (next().isVariable())
|
|
{
|
|
TQString name = next().variableName();
|
|
m_start++;
|
|
return true;
|
|
}
|
|
if (mode == Execute)
|
|
setError(i18n("Expected variable"));
|
|
return false;
|
|
}
|
|
|
|
TQString Parser::nextVariable(Mode mode)
|
|
{
|
|
if (next().isVariable())
|
|
{
|
|
TQString name = next().variableName();
|
|
m_start++;
|
|
return name;
|
|
}
|
|
else if (mode == Execute)
|
|
setError(i18n("Expected variable"));
|
|
return TQString();
|
|
}
|
|
|
|
|
|
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 = TQString();
|
|
m_errorPosition = 0;
|
|
}
|
|
|
|
void Parser::setError(const TQString& msg)
|
|
{
|
|
setError(msg, m_start);
|
|
}
|
|
|
|
void Parser::setError(const TQString& msg, int pos)
|
|
{
|
|
if (m_error.isNull())
|
|
{
|
|
m_errorPosition = pos;
|
|
m_error = msg;
|
|
}
|
|
}
|
|
|
|
void Parser::setVariable(const TQString& name, ParseNode value)
|
|
{
|
|
if (isGlobal(name))
|
|
m_globalVariables[name] = value;
|
|
else
|
|
m_variables[name] = value;
|
|
}
|
|
|
|
ParseNode Parser::variable(const TQString& 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 TQString& name) const
|
|
{
|
|
return !name.isEmpty() && name[0] == '_';
|
|
}
|
|
|
|
bool Parser::isVariable(const TQString& name) const
|
|
{
|
|
return m_variables.contains(name) || m_globalVariables.contains(name);
|
|
}
|
|
|
|
void Parser::unsetVariable(const TQString& key)
|
|
{
|
|
if (isGlobal(key))
|
|
m_globalVariables.remove(key);
|
|
else
|
|
m_variables.remove(key);
|
|
}
|
|
|
|
const TQMap<TQString, ParseNode>& Parser::array(const TQString& name) const
|
|
{
|
|
if (isGlobal(name))
|
|
return m_globalArrays[name];
|
|
else
|
|
return m_arrays[name];
|
|
}
|
|
|
|
bool Parser::isArray(const TQString& name) const
|
|
{
|
|
return m_arrays.contains(name) || m_globalArrays.contains(name);
|
|
}
|
|
|
|
void Parser::setArray(const TQString& name, const TQString& key, ParseNode value)
|
|
{
|
|
if (isGlobal(name))
|
|
m_globalArrays[name][key] = value;
|
|
else
|
|
m_arrays[name][key] = value;
|
|
}
|
|
|
|
void Parser::unsetArray(const TQString& name, const TQString& 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 TQString& name, const TQString& 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();
|
|
}
|
|
|
|
|
|
|
|
KommanderWidget* Parser::currentWidget() const
|
|
{
|
|
return m_widget;
|
|
}
|
|
|
|
TQMap<TQString, ParseNode> Parser::m_globalVariables;
|
|
TQMap<TQString, TQMap<TQString, ParseNode> > Parser::m_globalArrays;
|
|
|
|
|