using System;
using System.Collections.Generic;
using NTERA.EmuEra.Game.EraEmu.GameData.Variable;
using NTERA.EmuEra.Game.EraEmu.Sub;
namespace NTERA.EmuEra.Game.EraEmu.GameData.Expression
{
internal enum ArgsEndWith
{
None,
EoL,
RightParenthesis,//)終端
RightBracket//]終端
}
internal enum TermEndWith
{
None = 0x0000,
EoL = 0x0001,
Comma = 0x0002,//','終端
RightParenthesis = 0x0004,//')'終端
RightBracket = 0x0008,//')'終端
Assignment = 0x0010,//')'終端
RightParenthesis_Comma = RightParenthesis | Comma,//',' or ')'終端
RightBracket_Comma = RightBracket | Comma,//',' or ']'終端
Comma_Assignment = Comma | Assignment,//',' or '='終端
RightParenthesis_Comma_Assignment = RightParenthesis | Comma | Assignment,//',' or ')' or '='終端
RightBracket_Comma_Assignment = RightBracket | Comma | Assignment//',' or ']' or '='終端
}
internal static class ExpressionParser
{
#region public Reduce
///
/// カンマで区切られた引数を一括して取得。
/// return時にはendWithの次の文字がCurrentになっているはず。終端の適切さの検証はExpressionParserがが行う。
/// 呼び出し元はCodeEEを適切に処理すること
///
///
public static IOperandTerm[] ReduceArguments(WordCollection wc, ArgsEndWith endWith, bool isDefine)
{
if(wc == null)
throw new ExeEE("空のストリームを渡された");
List terms = new List();
TermEndWith termEndWith = TermEndWith.EoL;
switch (endWith)
{
case ArgsEndWith.EoL:
termEndWith = TermEndWith.Comma;
break;
//case ArgsEndWith.RightBracket:
// termEndWith = TermEndWith.RightBracket_Comma;
// break;
case ArgsEndWith.RightParenthesis:
termEndWith = TermEndWith.RightParenthesis_Comma;
break;
}
TermEndWith termEndWith_Assignment = termEndWith | TermEndWith.Assignment;
while (true)
{
Word word = wc.Current;
switch (word.Type)
{
case '\0':
if (endWith == ArgsEndWith.RightBracket)
throw new CodeEE("'['に対応する']'が見つかりません");
if (endWith == ArgsEndWith.RightParenthesis)
throw new CodeEE("'('に対応する')'が見つかりません");
goto end;
case ')':
if (endWith == ArgsEndWith.RightParenthesis)
{
wc.ShiftNext();
goto end;
}
throw new CodeEE("構文解析中に予期しない')'を発見しました");
case ']':
if (endWith == ArgsEndWith.RightBracket)
{
wc.ShiftNext();
goto end;
}
throw new CodeEE("構文解析中に予期しない']'を発見しました");
}
if(!isDefine)
terms.Add(ReduceExpressionTerm(wc, termEndWith));
else
{
terms.Add(ReduceExpressionTerm(wc, termEndWith_Assignment));
if (terms[terms.Count - 1] == null)
throw new CodeEE("関数定義の引数は省略できません");
if (wc.Current is OperatorWord)
{//=がある
wc.ShiftNext();
IOperandTerm term = reduceTerm(wc, false, termEndWith, VariableCode.__NULL__);
if (term == null)
throw new CodeEE("'='の後に式がありません");
if (term.GetOperandType() != terms[terms.Count - 1].GetOperandType())
throw new CodeEE("'='の前後で型が一致しません");
terms.Add(term);
}
else
{
if (terms[terms.Count - 1].GetOperandType() == typeof(Int64))
terms.Add(new NullTerm(0));
else
terms.Add(new NullTerm(""));
}
}
if (wc.Current.Type == ',')
wc.ShiftNext();
}
end:
IOperandTerm[] ret = new IOperandTerm[terms.Count];
terms.CopyTo(ret);
return ret;
}
///
/// 数式または文字列式。CALLの引数などを扱う。nullを返すことがある。
/// return時にはendWithの文字がCurrentになっているはず。終端の適切さの検証は呼び出し元が行う。
///
///
///
public static IOperandTerm ReduceExpressionTerm(WordCollection wc, TermEndWith endWith)
{
IOperandTerm term = reduceTerm(wc, false, endWith, VariableCode.__NULL__);
return term;
}
/////
///// 単純文字列、書式付文字列、文字列式のうち、文字列式を取り扱う。
///// 終端記号が正しいかどうかは呼び出し元で調べること
/////
/////
/////
//public static IOperandTerm ReduceStringTerm(WordCollection wc, TermEndWith endWith)
//{
// IOperandTerm term = reduceTerm(wc, false, endWith, VariableCode.__NULL__);
// if (term.GetOperandType() != typeof(string))
// throw new CodeEE("式の結果が文字列ではありません");
// return term;
//}
public static IOperandTerm ReduceIntegerTerm(WordCollection wc, TermEndWith endwith)
{
IOperandTerm term = reduceTerm(wc, false, endwith, VariableCode.__NULL__);
if (term == null)
throw new CodeEE("構文を式として解釈できません");
if (term.GetOperandType() != typeof(Int64))
throw new CodeEE("式の結果が数値ではありません");
return term;
}
///
/// 結果次第ではSingleTermを返すことがある。
///
///
public static IOperandTerm ToStrFormTerm(StrFormWord sfw)
{
StrForm strf = StrForm.FromWordToken(sfw);
if(strf.IsConst)
return new SingleTerm(strf.GetString(null));
return new StrFormTerm(strf);
}
///
/// カンマで区切られたCASEの引数を一括して取得。行端で終わる。
///
///
///
public static CaseExpression[] ReduceCaseExpressions(WordCollection wc)
{
List terms = new List();
while (!wc.EOL)
{
terms.Add(reduceCaseExpression(wc));
wc.ShiftNext();
}
CaseExpression[] ret = new CaseExpression[terms.Count];
terms.CopyTo(ret);
return ret;
}
public static IOperandTerm ReduceVariableArgument(WordCollection wc, VariableCode varCode)
{
IOperandTerm ret = reduceTerm(wc, false, TermEndWith.EoL, varCode);
if(ret == null)
throw new CodeEE("変数の:の後に引数がありません");
return ret;
}
public static VariableToken ReduceVariableIdentifier(WordCollection wc, string idStr)
{
string subId = null;
if (wc.Current.Type == '@')
{
wc.ShiftNext();
IdentifierWord subidWT = wc.Current as IdentifierWord;
if (subidWT == null)
throw new CodeEE("@の使い方が不正です");
wc.ShiftNext();
subId = subidWT.Code;
}
return GlobalStatic.IdentifierDictionary.GetVariableToken(idStr, subId, true);
}
///
/// 識別子一つを解決
///
///
/// 識別子文字列
/// 変数の引数の場合はその変数のCode。連想配列的につかう
///
private static IOperandTerm reduceIdentifier(WordCollection wc, string idStr, VariableCode varCode)
{
wc.ShiftNext();
SymbolWord symbol = wc.Current as SymbolWord;
if (symbol != null && symbol.Type == '.')
{//名前空間
throw new NotImplCodeEE();
}
if (symbol != null && (symbol.Type == '(' || symbol.Type == '['))
{//関数
wc.ShiftNext();
if (symbol.Type == '[')//1810 多分永久に実装されない
throw new CodeEE("[]を使った機能はまだ実装されていません");
//引数を処理
IOperandTerm[] args = ReduceArguments(wc, ArgsEndWith.RightParenthesis, false);
IOperandTerm mToken = GlobalStatic.IdentifierDictionary.GetFunctionMethod(GlobalStatic.LabelDictionary, idStr, args, false);
if (mToken == null)
{
if (!Program.AnalysisMode)
GlobalStatic.IdentifierDictionary.ThrowException(idStr, true);
else
{
if (GlobalStatic.tempDic.ContainsKey(idStr))
GlobalStatic.tempDic[idStr]++;
else
GlobalStatic.tempDic.Add(idStr, 1);
return new NullTerm(0);
}
}
return mToken;
}
//変数 or キーワード
idStr = idStr.Replace("PARAM","PALAM"); // JVN: cheatsy doodles activate! PARAM becomes PALAM internally, things now work
VariableToken id = ReduceVariableIdentifier(wc, idStr);
if (id != null)//idStrが変数名の場合、
{
if (varCode != VariableCode.__NULL__)//変数の引数が引数を持つことはない
return VariableParser.ReduceVariable(id, null, null, null);
return VariableParser.ReduceVariable(id, wc);
}
//idStrが変数名でない場合、
IOperandTerm refToken = GlobalStatic.IdentifierDictionary.GetFunctionMethod(GlobalStatic.LabelDictionary, idStr, null, false);
if (refToken != null)//関数参照と名前が一致したらそれを返す。実際に使うとエラー
return refToken;
if (varCode != VariableCode.__NULL__ && GlobalStatic.ConstantData.isDefined(varCode, idStr))//連想配列的な可能性アリ
return new SingleTerm(idStr);
GlobalStatic.IdentifierDictionary.ThrowException(idStr, false);
throw new ExeEE("エラー投げ損ねた");//ここまででthrowかreturnのどちらかをするはず。
}
#endregion
#region private reduce
private static CaseExpression reduceCaseExpression(WordCollection wc)
{
CaseExpression ret = new CaseExpression();
IdentifierWord id = wc.Current as IdentifierWord;
if ((id != null) && (id.Code.Equals("IS", Config.Config.SCVariable)))
{
wc.ShiftNext();
ret.CaseType = CaseExpressionType.Is;
OperatorWord opWT = wc.Current as OperatorWord;
if (opWT == null)
throw new CodeEE("ISキーワードの後に演算子がありません");
OperatorCode op = opWT.Code;
if (!OperatorManager.IsBinary(op))
throw new CodeEE("ISキーワードの後の演算子が2項演算子ではありません");
wc.ShiftNext();
ret.Operator = op;
ret.LeftTerm = reduceTerm(wc, false, TermEndWith.Comma, VariableCode.__NULL__);
if (ret.LeftTerm == null)
throw new CodeEE("ISキーワードの後に式がありません");
Type type = ret.LeftTerm.GetOperandType();
return ret;
}
ret.LeftTerm = reduceTerm(wc, true, TermEndWith.Comma, VariableCode.__NULL__);
if (ret.LeftTerm == null)
throw new CodeEE("CASEの引数は省略できません");
id = wc.Current as IdentifierWord;
if ((id != null) && (id.Code.Equals("TO", Config.Config.SCVariable)))
{
ret.CaseType = CaseExpressionType.To;
wc.ShiftNext();
ret.RightTerm = reduceTerm(wc, true, TermEndWith.Comma, VariableCode.__NULL__);
if (ret.RightTerm == null)
throw new CodeEE("TOキーワードの後に式がありません");
id = wc.Current as IdentifierWord;
if ((id != null) && (id.Code.Equals("TO", Config.Config.SCVariable)))
throw new CodeEE("TOキーワードが2度使われています");
if (ret.LeftTerm.GetOperandType() != ret.RightTerm.GetOperandType())
throw new CodeEE("TOキーワードの前後の型が一致していません");
return ret;
}
ret.CaseType = CaseExpressionType.Normal;
return ret;
}
///
/// 解析器の本体
///
///
/// TOキーワードが見つかっても良いか
/// 終端記号
///
private static IOperandTerm reduceTerm(WordCollection wc, bool allowKeywordTo, TermEndWith endWith, VariableCode varCode)
{
TermStack stack = new TermStack();
//int termCount = 0;
int ternaryCount = 0;
OperatorCode formerOp = OperatorCode.NULL;
bool varArg = varCode != VariableCode.__NULL__;
do
{
Word token = wc.Current;
switch (token.Type)
{
case '\0':
goto end;
case '"'://LiteralStringWT
stack.Add(((LiteralStringWord)token).Str);
break;
case '0'://LiteralIntegerWT
stack.Add(((LiteralIntegerWord)token).Int);
break;
case 'F'://FormattedStringWT
stack.Add(ToStrFormTerm((StrFormWord)token));
break;
case 'A'://IdentifierWT
{
string idStr = (((IdentifierWord)token).Code);
if (idStr.Equals("TO", Config.Config.SCVariable))
{
if (allowKeywordTo)
goto end;
throw new CodeEE("TOキーワードはここでは使用できません");
}
if (idStr.Equals("IS", Config.Config.SCVariable))
throw new CodeEE("ISキーワードはここでは使用できません");
stack.Add(reduceIdentifier(wc, idStr, varCode));
continue;
}
case '='://OperatorWT
{
if (varArg)
throw new CodeEE("変数の引数の読み取り中に予期しない演算子を発見しました");
OperatorCode op = ((OperatorWord)token).Code;
if (op == OperatorCode.Assignment)
{
if ((endWith & TermEndWith.Assignment) == TermEndWith.Assignment)
goto end;
throw new CodeEE("式中で代入演算子'='が使われています(等価比較には'=='を使用してください)");
}
if (formerOp == OperatorCode.Equal || formerOp == OperatorCode.Greater || formerOp == OperatorCode.Less
|| formerOp == OperatorCode.GreaterEqual || formerOp == OperatorCode.LessEqual || formerOp == OperatorCode.NotEqual)
{
if (op == OperatorCode.Equal || op == OperatorCode.Greater || op == OperatorCode.Less
|| op == OperatorCode.GreaterEqual || op == OperatorCode.LessEqual || op == OperatorCode.NotEqual)
{
ParserMediator.Warn("(構文上の注意)比較演算子が連続しています。", GlobalStatic.Process.GetScaningLine(), 0, false, false);
}
}
stack.Add(op);
formerOp = op;
if (op == OperatorCode.Ternary_a)
ternaryCount++;
else if (op == OperatorCode.Ternary_b)
{
if (ternaryCount > 0)
ternaryCount--;
else
throw new CodeEE("対応する'?'のない'#'です");
}
break;
}
case '(':
wc.ShiftNext();
IOperandTerm inTerm = reduceTerm(wc, false, TermEndWith.RightParenthesis, VariableCode.__NULL__);
if (inTerm == null)
throw new CodeEE("かっこ\"(\"~\")\"の中に式が含まれていません");
stack.Add(inTerm);
if (wc.Current.Type != ')')
throw new CodeEE("対応する')'のない'('です");
//termCount++;
wc.ShiftNext();
continue;
case ')':
if ((endWith & TermEndWith.RightParenthesis) == TermEndWith.RightParenthesis)
goto end;
throw new CodeEE("構文解釈中に予期しない記号'" + token.Type + "'を発見しました");
case ']':
if ((endWith & TermEndWith.RightBracket) == TermEndWith.RightBracket)
goto end;
throw new CodeEE("構文解釈中に予期しない記号'" + token.Type + "'を発見しました");
case ',':
if ((endWith & TermEndWith.Comma) == TermEndWith.Comma)
goto end;
throw new CodeEE("構文解釈中に予期しない記号'" + token.Type + "'を発見しました");
case 'M':
throw new ExeEE("マクロ解決失敗");
default:
throw new CodeEE("構文解釈中に予期しない記号'" + token.Type + "'を発見しました");
}
//termCount++;
wc.ShiftNext();
} while (!varArg);
end:
if (ternaryCount > 0)
throw new CodeEE("'?'と'#'の数が正しく対応していません");
return stack.ReduceAll();
}
#endregion
///
/// 式解決用クラス
///
private class TermStack
{
///
/// 次に来るべきものの種類。
/// (前置)単項演算子か値待ちなら0、二項・三項演算子待ちなら1、値待ちなら2、++、--、!に対応する値待ちの場合は3。
///
int state;
bool hasBefore;
bool hasAfter;
bool waitAfter;
Stack