Написать синтаксический анализатор-калькулятор - C#
Формулировка задачи:
Задача заключается в том что надо написать программу "калькулятор" на языке С Sharp. Калькулятор должен уметь синтаксически анализировать выражения: вычитание, сложение, умножение, тангенс, синус и действительные числа. Подскажите пожалуйста советами или примерами, где можно найти информацию на эту задачу.
Решение задачи: «Написать синтаксический анализатор-калькулятор»
textual
Листинг программы
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CalcTrans
{
class Program
{
static void Main(string[] args)
{
string expr;
Parser p = new Parser();
Console.WriteLine("Для выхода из программы введите пустое выражение. ");
for (; ; )
{
Console.Write("Введите выражение: ");
expr = Console.ReadLine();
if (expr == "") break;
Console.WriteLine("Результат: " + p.Evaluate(expr));
}
}
//класс исключающий для ошибок для анализатора
class ParserException : ApplicationException
{
public ParserException(string str) : base(str) { }
public override string ToString()
{ return Message; }
}
class Parser
{
//перчисляем типы лексем.
enum Types { NONE, DELIMITER, VARIABLE, NUMBER };
// Перечисляем типы ошибок.
enum Errors { SYNTAX, UNBALPARENS, NOEXP, DIVBYZERO };
string exp; // Ссылка на строку выражения,
int expIdx; // Текущий индекс в выражении,
string token; // Текущая лексема.
Types tokType; // Тип лексемы.
// Массив для переменных,
double[] vars = new double[26];
public Parser()
{
// Инициализируем переменные нулевыми значениями.
for (int i = 0; i < vars.Length; i++)
vars[i] = 0.0;
}
// Входная точка анализатора.
public double Evaluate(string expstr)
{
double result;
exp = expstr;
expIdx = 0;
try
{
GetToken();
if (token == "")
{
SyntaxErr(Errors.NOEXP); // Выражение отсутствует,
return 0.0;
}
EvalExp1(out result); // В этом варианте анализатора
// сначала вызывается
// метод EvalExpl().
if (token != "") // Последняя лексема должна
// быть нулевой.
SyntaxErr(Errors.SYNTAX);
return result;
}
catch (ParserException exc)
{
// При желании добавляем здесь обработку ошибок.
Console.WriteLine(exc);
return 0.0;
}
}
// Обрабатываем присвоение,
void EvalExp1(out double result)
{
int varIdx;
Types ttokType;
string temptoken;
if (tokType == Types.VARIABLE)
{
// Сохраняем старую лексему,
temptoken = String.Copy(token);
ttokType = tokType;
// Вычисляем индекс переменной,
varIdx = Char.ToUpper(token[0]) - 'A';
GetToken();
if (token != "=")
{
PutBack();// Возвращаем текущую лексему в поток
//и восстанавливаем старую,
// поскольку отсутствует присвоение.
token = String.Copy(temptoken);
tokType = ttokType;
}
else
{
GetToken();// Получаем следующую часть
// выражения ехр.
EvalExp2(out result);
vars[varIdx] = result;
return;
}
}
EvalExp2(out result);
}
// Складываем или вычитаем два члена выражения.
void EvalExp2(out double result)
{
string op;
double partialResult;
EvalExp3(out result);
while ((op = token) == "+" || op == "-")
{
GetToken();
EvalExp3(out partialResult);
switch (op)
{
case "-":
result = result - partialResult;
break;
case "+":
result = result + partialResult;
break;
}
}
}
// Выполняем умножение или деление двух множителей.
void EvalExp3(out double result)
{
string op;
double partialResult = 0.0;
EvalExp4(out result);
while ((op = token) == "*" || op == "/" || op == "%")
{
GetToken();
EvalExp4(out partialResult);
switch (op)
{
case "*":
result = result * partialResult;
break;
case "/":
if (partialResult == 0.0)
SyntaxErr(Errors.DIVBYZERO);
result = result / partialResult;
break;
case "%":
if (partialResult == 0.0)
SyntaxErr(Errors.DIVBYZERO);
result = (int)result % (int)partialResult;
break;
}
}
}
// выполняем возведение в степень
void EvalExp4(out double result)
{
double partialResult, ex;
int t;
EvalExp5(out result);
if (token == "^")
{
GetToken();
EvalExp4(out partialResult);
ex = result;
if (partialResult == 0.0)
{
result = 1.0;
return;
}
for (t = (int)partialResult - 1; t > 0; t--)
result = result * (double)ex;
}
}
// Выполненяем операцию унарного + или -.
void EvalExp5(out double result)
{
string op;
op = "";
if ((tokType == Types.DELIMITER) && token == "+" || token == "-")
{
op = token;
GetToken();
}
EvalExp6(out result);
if (op == "-") result = -result;
}
// обрабатываем выражение в круглых скобках
void EvalExp6(out double result)
{
if ((token == "("))
{
GetToken();
EvalExp2(out result);
if (token != ")")
SyntaxErr(Errors.UNBALPARENS);
GetToken();
}
else Atom(out result);
}
// Получаем значение числа или переменной.
void Atom(out double result)
{
switch (tokType)
{
case Types.NUMBER:
try
{
result = Double.Parse(token);
}
catch (FormatException)
{
result = 0.0;
SyntaxErr(Errors.SYNTAX);
}
GetToken();
return;
case Types.VARIABLE:
result = FindVar(token);
GetToken();
return;
default:
result = 0.0;
SyntaxErr(Errors.SYNTAX);
break;
}
}
// Возвращаем значение переменной.
double FindVar(string vname)
{
if (!Char.IsLetter(vname[0]))
{
SyntaxErr(Errors.SYNTAX);
return 0.0;
}
return vars[Char.ToUpper(vname[0]) - 'A'];
}
// Возвращаем лексему во входной поток.
void PutBack()
{
for (int i = 0; i < token.Length; i++) expIdx--;
}
// Обрабатываем синтаксическую ошибку
void SyntaxErr(Errors error)
{
string[] err ={
"Синтаксическая оошибка",
"Дисбаланс скобок",
"Выражение отсутствет",
"Деление на нуль"};
throw new ParserException(err[(int)error]);
}
// получем следующую лексему.
void GetToken()
{
tokType = Types.NONE;
token = "";
if (expIdx == exp.Length) return; // Конец выражения.
// Опускаем пробел.
while (expIdx < exp.Length && Char.IsWhiteSpace(exp[expIdx])) ++expIdx;
// Хвостовой пробел завершает выражение.
if (expIdx == exp.Length) return;
if (IsDelim(exp[expIdx]))
{
token += exp[expIdx];
expIdx++;
tokType = Types.DELIMITER;
}
else if (Char.IsLetter(exp[expIdx]))
{
// Это переменная?
while (!IsDelim(exp[expIdx]))
{
token += exp[expIdx];
expIdx++;
if (expIdx >= exp.Length) break;
}
tokType = Types.VARIABLE;
}
else if (Char.IsDigit(exp[expIdx]))
{
// Это число?
while (!IsDelim(exp[expIdx]))
{
token += exp[expIdx];
expIdx++;
if (expIdx >= exp.Length) break;
}
tokType = Types.NUMBER;
}
}
// Метод возвращает значение true,
// если с -- разделитель.
bool IsDelim(char c)
{
if (("+-/*%^=()".IndexOf(c) != -1))
return true;
return false;
}
}
}
}