Console calculator Part 1 : The Scanner

ddanbe 1 Tallied Votes 2K Views Share

This is a series of 3 code snippets which together form a completly working "calculator".
I used a console application in VS C# 2008, but 2005 should cause no problems.
To start with : here you have the code for a scanner class.
A scanner reads the inputline and tries to get some meaningfull words(sometimes called tokens) from it. It then passes that information to a parser.

sknake commented: nice :) I missed this snippet of yours somehow +6
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleCalculator
{
    class Scanner
    {
        /// <summary>
        /// Scanner class, main routine here is get_token.
        /// get_token returns the next token_value to the parser,
        /// and sets some variables. It is up to the parser to decide
        /// what to do next.
        /// </summary>
       
        public enum token_value
        {
            NAME, NUMBER, END, LIST, HELP, SQRT,
            PLUS = '+', MINUS = '-',
            MUL = '*', DIV = '/',
            PRINT = ';', ASSIGN = '=',
            LPAREN = '(', RPAREN = ')',
        };

        public token_value curr_tok;    //current token value found
        public double number_value;     //if the token was a number the value is held here
        public string name_string;      //if the token was a variable name, the name can be found here

        private char[] StringToParse;   //the "inputstream"
        private int charpos = 0;        //position of the next character to read from the inputstream
        private StringBuilder numberSB = new StringBuilder();
        private StringBuilder nameSB = new StringBuilder();

        
        /// <summary>
        /// Two generic Dictionary's that use hashing to store string keys and values for
        /// variables, commands and reserved words.
        /// SymbolTable is public, the parser likes this table too
        /// </summary>
        public Dictionary<string, double> SymbolTable = new Dictionary<string, double>();
        private Dictionary<string, token_value> ReservedWords = new Dictionary<string, token_value>();

        /// <summary>
        /// Scanner default custom constructor, defines a small set of reseved words,
        /// used to give special commands to the calculator.
        /// </summary>
        public Scanner()
        {
            ReservedWords.Add("END", token_value.END);  //Quit the application.
            ReservedWords.Add("LIST", token_value.LIST);//Show a list of all variables in the calculator.
            ReservedWords.Add("?", token_value.HELP);   //Display help info
            ReservedWords.Add("SQRT", token_value.SQRT); //Function to take the square root
        }

        //property to pass the inputline to the scanner
        //---------------------------------------------
        public string InputLine
        {
            set 
            {
                string line = value + "\n";
                StringToParse = line.ToCharArray();
            }
        }

        //for functions we must first find a left parentesis
        //--------------------------------------------------
        public bool FindLeftParentesis()
        {
            char ch;
            try
            {
                do
                {
                    ch = GetChar();
                }
                while (!ch.Equals('('));
                return true;
            }
            catch
            {
                Error("( expected"); return false;
            }
        }

        //find a character in the "inputstream"
        //-------------------------------------
        private char GetChar()
        {
            char ch = StringToParse[charpos];
            charpos++;
            return ch;
        }

        public void PutBackChar()
        {
            charpos--;
        }

        public void InitScan()
        {
            charpos = 0;
            number_value = 0.0;
            name_string = "";
        }

        public void Error(string message)
        {
            //minor error handling, but sufficient for the moment
            Console.ForegroundColor = ConsoleColor.Black;
            Console.BackgroundColor = ConsoleColor.Cyan;
            Console.WriteLine(message);
            Console.ResetColor();
        }

        /// <summary>
        /// Main function of the scanner returns the current token and
        /// sets the variables name_string and number_value when appropriate.
        /// </summary>
        /// <returns></returns>
        public token_value get_token()
        {
            char ch;

            do          //skip whitespace except '\n'
            {
                ch = GetChar();
            }
            while (ch != '\n' && char.IsWhiteSpace(ch));
            switch (ch)
            {
                case ';': case '\n':
                    curr_tok = token_value.PRINT;
                    return curr_tok;
                case '*':
                case '/':
                case '+':
                case '-':
                case '(':
                case ')':
                case '=':
                    curr_tok = (token_value)(ch);
                    return curr_tok;
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                case '.':
                case ',':
                    if (ch == '.') ch = ',';
                    numberSB.Append(ch);
                    ch = GetChar();
                    if (ch == '.') ch = ',';
                    while (char.IsDigit(ch) || ch == ',')
                    {
                        numberSB.Append(ch);
                        ch = GetChar();
                    }
                    PutBackChar();
                    try
                    {
                        number_value = Convert.ToDouble(numberSB.ToString());
                        numberSB.Remove(0, numberSB.Length);
                    }
                    catch
                    {
                        Error("Fout in formaat van getal.");
                    }
                    return token_value.NUMBER;
                default:
                    if (char.IsLetter(ch) | ch.Equals('?'))
                    {
                        nameSB.Append(ch);
                        ch = GetChar();
                        while (char.IsLetterOrDigit(ch))
                        {
                            nameSB.Append(ch);
                            ch = GetChar();
                        }
                        PutBackChar();
                        name_string = nameSB.ToString();
                        nameSB.Remove(0, nameSB.Length);
                        if (ReservedWords.TryGetValue(name_string.ToUpper(), out curr_tok)) return curr_tok;
                        return token_value.NAME;
                    }
                    
                    Error("Bad token.");
                    return token_value.PRINT;
            }
        }   //end get_token    

        //print a list of all the variables defined in the symboltable
        //------------------------------------------------------------
        public void ListVars()
        {
            Console.WriteLine("+-------------------------------------------+");
            Console.WriteLine("| List of variables :                       |");
            Console.WriteLine("+-------------------------------------------+");
            Console.WriteLine();
            foreach (KeyValuePair<string, double> kvp in SymbolTable)
            {
                Console.WriteLine("Var = {0} \t = {1}", kvp.Key, kvp.Value);
            } //yeah, generics is something to get used to
            Console.WriteLine("+-------------------------------------------+");
        }
    }
}
ddanbe 2,724 Professional Procrastinator Featured Poster

application in VS C# 2008, but 2005 should cause no problems.
The application makes use of generics in C# I don't know if version 2005 supports generics...

pradeep9601 0 Newbie Poster

code work nicely but can you help me that how to give high priority to * (multiply) then / (division) and other operators in the input equation.

ddanbe 2,724 Professional Procrastinator Featured Poster

Yes.
This scanner just does what it says, it scans the inputstring for "tokens".
These tokens are fed to a parser It is the job of the parser to find out about operator precedence.
Look also for the third snippet which will put al this together.
Succes!

pradeep9601 0 Newbie Poster

now i wanna add one more operator % (modulos) in the calculator so where to add codes for modulus property in this three part codes.
thanks

ddanbe 2,724 Professional Procrastinator Featured Poster

This should be rather straitforward. Just like adding more funcions, like SIN or EXP etc.
Give it a try! Succes!

pradeep9601 0 Newbie Poster

hi can anybody help me to run this calculator in cmd prompt by taking argument rather than readline. if it is possible then explain with code how to do it?

ddanbe 2,724 Professional Procrastinator Featured Poster

You could probably use one of the Main args strings and feed it to Parse.Input.
http://www.daniweb.com/code/snippet217187.html

pradeep9601 0 Newbie Poster

ya but how can you explain me in this code

ddanbe 2,724 Professional Procrastinator Featured Poster

You could test the arguments you are passing with this code:

class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < args.Length; i++)
            {
                Console.WriteLine("Argument {0} = {1}", i, args[i]);
            }
            Console.ReadKey();
        }
    }

It is up to you which argument string to pass to Parse.InputLine(see line 59 of the above snippet in the previous post)
But I am wondering why you want to do this, because this calculator is a rather interactive console application.

hafsa ayaz 0 Newbie Poster

y can'nt the above lexical code in c# show output for a moment???..:( when the program run suddenly the output screen hide

ddanbe 2,724 Professional Procrastinator Featured Poster

Remember this is not the whole program, that's why it is called Part 1
You also must add Part 2 and Part 3.
You will find these here:
http://www.daniweb.com/software-development/csharp/code/217186
http://www.daniweb.com/software-development/csharp/code/217187

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.