// ExpressionParser.cs:
//
// Copyright (C) 2007 by Alex Nek
// alexnek@russinger.com
// simpleparser.russinger.com
// This file is part of ExpressionParser classes
//
// ExpressionParser classes 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 3 of the License, or
// 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, see <http://www.gnu.org/licenses/>
//////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using SimpleParser;
namespace ExpressionsParser
{
/// <summary>
/// Base class for one lexical item
/// </summary>
public abstract class CParseObject
{
/// <summary>
/// Consructor
/// </summary>
public CParseObject ()
{
}
/// <summary>
/// Parse lexical item
/// </summary>
/// <param name="Executor">Data Storage. We store parsed data here</param>
/// <param name="Parser">Data Parser. We parse input stream here</param>
/// <param name="Token">Next Token. We always have the next Token after parsing the current item</param>
abstract public void Parse (CExecutor Executor, CExpressionParser Parser, ref CToken Token);
}
/// <summary>
/// Extension for standard token
/// </summary>
public class CSimpexToken : CToken
{
/// <summary>
/// Define of token type.
/// We can use token type only as integer because it is not possible to inherit enum
/// </summary>
public enum ETokenType
{
Empty = CToken.ETokenType.NullValue,
FloatValue = CToken.ETokenType.FloatValue,
EndOfFile = CToken.ETokenType.EndOfFile,
EndOfLine = CToken.ETokenType.EndOfLine,
StartOfTokeTypeValue = CToken.ETokenType.EndOfTokenTypeValue,
Equals = StartOfTokeTypeValue,
QuestionMark,
Colon,
Comma,
Plus,
Minus,
Multiplay,
Divide,
OpenBracket,
CloseBracket,
NotOperator,
IsLessAs,
IsBiggerAs,
IsLessOrEqualAs,
IsBiggerOrEqualAs,
IsEqual,
IsNotEqual,
FunctionPi,
FunctionSqrt,
FunctionPow,
EndOfTokenTypeValue
};
/// <summary>
/// Consructor
/// </summary>
public CSimpexToken ()
{
}
}
/// <summary>
/// Extention for base parser
/// </summary>
public class CExpressionParser : CBaseParser
{
#region Symbols combinations
/// <summary>
/// Possible symbols combination in our expressions.
/// It could be one or more.
/// </summary>
private KeyValuePair<string, int>[] COMBINATIONS = {
new KeyValuePair<string, int>("+",(int)CSimpexToken.ETokenType.Plus), // +
new KeyValuePair<string, int>("-",(int)CSimpexToken.ETokenType.Minus), // -
new KeyValuePair<string, int>("*",(int)CSimpexToken.ETokenType.Multiplay), // *
new KeyValuePair<string, int>("/",(int)CSimpexToken.ETokenType.Divide), // /
new KeyValuePair<string, int>("=",(int)CSimpexToken.ETokenType.Equals), // =
new KeyValuePair<string, int>("(",(int)CSimpexToken.ETokenType.OpenBracket), // (
new KeyValuePair<string, int>(")",(int)CSimpexToken.ETokenType.CloseBracket), // )
new KeyValuePair<string, int>("?",(int)CSimpexToken.ETokenType.QuestionMark), // ?
new KeyValuePair<string, int>(":",(int)CSimpexToken.ETokenType.Colon), // :
new KeyValuePair<string, int>(",",(int)CSimpexToken.ETokenType.Comma), // ,
new KeyValuePair<string, int>("!",(int)CSimpexToken.ETokenType.NotOperator), // !
new KeyValuePair<string, int>("<",(int)CSimpexToken.ETokenType.IsLessAs), // <
new KeyValuePair<string, int>(">",(int)CSimpexToken.ETokenType.IsBiggerAs), // >
new KeyValuePair<string, int>("<=",(int)CSimpexToken.ETokenType.IsLessOrEqualAs), // <=
new KeyValuePair<string, int>(">=",(int)CSimpexToken.ETokenType.IsBiggerOrEqualAs), // >=
new KeyValuePair<string, int>("==",(int)CSimpexToken.ETokenType.IsEqual), // ==
new KeyValuePair<string, int>("!=",(int)CSimpexToken.ETokenType.IsNotEqual) // !=
};
#endregion
#region Data Members
/// <summary>
/// Reference to input text box.
/// It is our input data
/// </summary>
private System.Windows.Forms.TextBox m_Input = null;
/// <summary>
/// Reference for output text box.
/// Result and error written here
/// </summary>
private System.Windows.Forms.TextBox m_Output = null;
#endregion
/// <summary>
/// Consructor
/// </summary>
/// <param name="Input">Reference for input text box</param>
/// <param name="Output">Reference for output text box</param>
public CExpressionParser (TextBox Input, TextBox Output)
{
m_Input = Input;
m_Output = Output;
ArraySegment<KeyValuePair<string, int>> KeywordsArray = CFunctionNode.Functions;
AddKeywords(KeywordsArray);
ArraySegment<KeyValuePair<string, int>> CombinationsArray = new ArraySegment<KeyValuePair<string, int>>(COMBINATIONS);
AddCombinations(CombinationsArray);
SetState(ESTATE.ReturnEolToken,true);
}
/// <summary>
/// Check if start of comment found
/// For us it is " or //
/// </summary>
/// <param name="bMoveReadPosition">Move read position after start of comment if set</param>
/// <returns>true if start of comment found</returns>
protected override bool IsCommentStartFound (bool bMoveReadPosition)
{
char ch = GetCurrentChar();
bool bRet = false;
if (ch == CToken.SYMBOL_APOSTROPHE)
{
bRet = true;
if (bMoveReadPosition)
{
m_nReadPos++;
}
}
else if (ch == CToken.SYMBOL_SLASH && LookAtNextChar() == CToken.SYMBOL_SLASH)
{
bRet = true;
if (bMoveReadPosition)
{
m_nReadPos+=2;
}
}
return bRet;
}
/// <summary>
/// If end of comment found.
/// For us comment is the full line
/// </summary>
/// <param name="bMoveReadPosition">Move read position to the next line if set</param>
/// <returns>ture if comment found</returns>
protected override bool IsCommentEndFound (bool bMoveReadPosition)
{
bool bRet = IsEndOfCurrentLine();
if (bMoveReadPosition)
{
m_nReadPos = m_sCurrLine.Length - 1;
}
return bRet;
}
/// <summary>
/// Read next line from buffer if needed
/// </summary>
/// <returns>true if next line was read</returns>
protected override bool ReadNextLine ()
{
bool bRet = false;
if (LineNumber < m_Input.Lines.Length)
{
m_sCurrLine = m_Input.Lines[LineNumber];
m_nReadPos = 0;
m_nLineNumber++;
bRet = true;
}
return bRet;
}
/// <summary>
/// Get next token and skip all comments
/// </summary>
/// <param name="Token">Current token. Normally it ia the next one</param>
/// <returns>True if exists</returns>
public override bool GetNextToken (ref CToken Token)
{
bool bRet = false;
bRet = base.GetNextToken(ref Token);
// Skip comments
while (bRet && Token.TokenClass == CToken.ETokenClass.Comment)
{
bRet = base.GetNextToken(ref Token);
// Skip new lines symbols after comments
while (bRet && Token.TokenClass == CToken.ETokenClass.EOL)
{
bRet = base.GetNextToken(ref Token);
}
}
return bRet;
}
/// <summary>
/// Show error
/// </summary>
/// <param name="Text">Error text</param>
public override void Error (string Text)
{
base.Error(Text);
StringBuilder Line = new StringBuilder(CurrentLine);
StringBuilder SuccessParsed = new StringBuilder(CurrentLine);
//sLine = sLine.TrimEnd('\0');
int nNullPos = CurrentLine.IndexOf('\0');
if (nNullPos >= 0)
{
Line.Remove(nNullPos, Line.Length - nNullPos);
}
SuccessParsed.Remove(ReadPos, SuccessParsed.Length - ReadPos);
string sMessage = "At line " + LineNumber.ToString() + " before position " + ReadPos.ToString()
+ "\r\n'" + Line + "'\r\n"
+ "error was found: " + Text + "\r\n"
+ "Succesfully parsed up to\r\n"
+ "'" + SuccessParsed + "'" + "\r\n"
;
m_Output.AppendText(sMessage);
}
/// <summary>
/// Show error and skip line with it.
/// </summary>
/// <param name="Text">Error text</param>
public void ErrorAndSkipLine (string Text)
{
Error(Text);
//Skip current line
ReadNextLine();
}
}
}