Search code examples
c#compiler-constructionantlrantlr4interpreter

If statement inside loop ID string name instead of value in Antlr4 based Interpreter


Good day can really appreciate if some one would help. I am working on interpreter based on Antlr4 now i am having challenge after implementation of Visitor class. The implementation is working perfectly i.e the if statement unless inside loop it will return the ID name instead of value. Below is the implementation

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EasyBite
{
    public class Interpreter : EasyBiteBaseVisitor<object>
    {
        private Dictionary<string, object> variables = new Dictionary<string, object>();

        // Helper function to convert an object to a bool
        private bool ToBool(object obj)
        {
            if (obj is bool)
                return (bool)obj;
            if (obj is int)
                return (int)obj != 0;
            if (obj is double)
                return (double)obj != 0.0;
            if (obj is string)
                return !string.IsNullOrEmpty((string)obj);
            return obj != null;
        }

        // Helper function to check if two objects are equal
        private bool Equals(object a, object b)
        {
            if (a is double && b is double)
                return Math.Abs((double)a - (double)b) < double.Epsilon;
            return object.Equals(a, b);
        }

        // Declare the VisitProgram method
        public override object VisitProgram(EasyBiteParser.ProgramContext context)
        {
            // Visit all the child statements
            foreach (var statementContext in context.statement_list())
            {
                Visit(statementContext);
            }
            return null;
        }
        // Declare the VisitDeclareStatement method
        public override object VisitDeclareStatement(EasyBiteParser.DeclareStatementContext context)
        {
            // Add the variable to the dictionary
            if (!variables.ContainsKey(context.ID().GetText()))
            {
                variables.Add(context.ID().GetText(), null);
            }
            else
            {
                throw new Exception(String.Format("Variable {0} already declared", context.ID().GetText()));
            }
            return null;
        }

        // Declare the VisitSetStatement method
        public override object VisitSetStatement(EasyBiteParser.SetStatementContext context)
        {
            // Update the value of the variable
            var id = context.ID().GetText();

            var value = Visit(context.expression());
            variables[id] = value;
            return null;
        }

        // Declare the VisitAssignStatement method
        public override object VisitAssignStatement(EasyBiteParser.AssignStatementContext context)
        {
            // Update the value of the variable
            var id = context.ID().GetText();
            if (!variables.ContainsKey(id))
            {
                throw new Exception(String.Format("Variable {0} is not declared", context.ID().GetText()));
            }
            else
            {
                var value = Visit(context.expression());
                variables[id] = value;
            }
            return null;
        }
        

        // Declare the VisitShowStatement method
        public override object VisitShowStatement(EasyBiteParser.ShowStatementContext context)
        {
            // Print the value of the expression
            var value = Visit(context.expression());
            Console.WriteLine(value);
            return null;
        }

        public override object VisitShowlnStatement(EasyBiteParser.ShowlnStatementContext context)
        {
            Console.WriteLine();
            return null;
        }
        // Declare the VisitInputStatement method
        public override object VisitInputStatement(EasyBiteParser.InputStatementContext context)
        {
            string variableName;
            string inputPrompt;
            if (context.ID() != null)
            {
                variableName = context.ID().GetText();

            }
            else
            {
                variableName = context.SET().GetText();
                
            }

            inputPrompt = (string)Visit(context.expression());

            Console.Write(inputPrompt);
            string userInput = Console.ReadLine();

            // Convert the user input to the appropriate data type based on the variable type
            variables[variableName] = userInput;

            return null;
        }
       

        public override object VisitExpression(EasyBiteParser.ExpressionContext context)
        {
            if (context.LPAREN() != null && context.RPAREN() != null)
            {
                return Visit(context.expression(0));
            }
            else if (context.STRING() != null)
            {
                return context.GetText().Substring(1, context.GetText().Length - 2)
               .Replace("\\\\", "\\").Replace("\\\"", "\"").Replace("\\'", "'");
            }
            else if (context.NUMBER() != null)
            {
                return double.Parse(context.NUMBER().GetText());
            }
            else if (context.ID() != null)
            {
                string id = context.ID().GetText();
                if (variables.ContainsKey(id))
                {
                    if (variables[id] is int)
                    {
                        return (int)variables[id];
                    }
                    else if (variables[id] is double || variables[id] is float)
                    {
                        return (double)variables[id];
                    }
                    else if (variables[id] is string)
                    {
                        return (string)variables[id];
                    }
                    else if (variables[id] is bool)
                    {
                        return (bool)variables[id];
                    }
                }
                else
                {
                    Console.WriteLine("No variable named {0}", id);
                }
            
            }
            else if (context.NOT() != null)
            {
                bool value = (bool)VisitExpression(context.expression(0));
                return !value;
            }
            else if (context.AND() != null)
            {
                bool left = (bool)VisitExpression(context.expression(0));
                bool right = (bool)VisitExpression(context.expression(1));
                return left && right;
            }
            else if (context.OR() != null)
            {
                bool left = (bool)VisitExpression(context.expression(0));
                bool right = (bool)VisitExpression(context.expression(1));
                return left || right;
            }
            else if (context.LESS_THAN() != null)
            {
                double left = (double)VisitExpression(context.expression(0));
                double right = (double)VisitExpression(context.expression(1));
                return left < right;
            }
            else if (context.LESS_THAN_EQUAL() != null)
            {
                double left = (double)VisitExpression(context.expression(0));
                double right = (double)VisitExpression(context.expression(1));
                return left <= right;
            }
            else if (context.GREATER_THAN() != null)
            {
                double left = (double)VisitExpression(context.expression(0));
                double right = (double)VisitExpression(context.expression(1));
                return left > right;
            }
            else if (context.GREATER_THAN_EQUAL() != null)
            {
                double left = (double)VisitExpression(context.expression(0));
                double right = (double)VisitExpression(context.expression(1));
                return left >= right;
            }
            else if (context.EQUAL() != null)
            {
                double left = (double)VisitExpression(context.expression(0));
                double right = (double)VisitExpression(context.expression(1));
                return left == right;
            }
            else if (context.NOT_EQUAL() != null)
            {
                double left = (double)VisitExpression(context.expression(0));
                double right = (double)VisitExpression(context.expression(1));
                return left != right;
            }
            else if (context.PLUS() != null)
            {
                double left = (double)VisitExpression(context.expression(0));
                double right = (double)VisitExpression(context.expression(1));
                return left + right;
            }
            else if (context.MINUS() != null)
            {
                double left = (double)VisitExpression(context.expression(0));
                double right = (double)VisitExpression(context.expression(1));
                return left - right;
            }
            else if (context.MUL() != null)
            {
                double left = (double)VisitExpression(context.expression(0));
                double right = (double)VisitExpression(context.expression(1));
                return left * right;
            }
            else if (context.DIV() != null)
            {
                double left = (double)VisitExpression(context.expression(0));
                double right = (double)VisitExpression(context.expression(1));
                return left/ right;
            }
            else if (context.MODULO() != null)
            {
                int left = (int)VisitExpression(context.expression(0));
                int right = (int)VisitExpression(context.expression(1));
                return left % right;
            }
            else if (context.POW() != null)
            {
                double left = (double)VisitExpression(context.expression(0));
                double right = (double)VisitExpression(context.expression(1));
                return Math.Pow(left, right);
            }
            return "Invalid expression";
        }
        public override object VisitForStatement(EasyBiteParser.ForStatementContext context)
        {
            string variableName = context.ID().GetText();
            int start = (int)Visit(context.expression(0));
            int end = (int)Visit(context.expression(1));
            int step = context.expression(2) != null ? (int)Visit(context.expression(2)) : 1;
            for (int i = start; i <= end; i += step)
            {
                variables[variableName] = i;
                var statementListIndex = (i - start) / step; // compute the index of the statement list
                var statementList = context.statement_list(statementListIndex);
                
                    foreach (var statement in context.statement_list())
                    {
                        Visit(statement);
                    }
                
            }
            return null;
        }
        public override object VisitGenerateStatement(EasyBiteParser.GenerateStatementContext context)
        {
            string variableName = context.ID().GetText();
            int start = Convert.ToInt32(Visit(context.expression(0)));
            int end = Convert.ToInt32(Visit(context.expression(1)));
            int step = context.expression(2) != null ? Convert.ToInt32(Visit(context.expression(2))) : 1;
            for (int i = start; i <= end; i += step)
            {
                variables[variableName] = i;
                var statementListIndex = (i - start) / step; // compute the index of the statement list
                var statementList = context.statement_list(statementListIndex);

                foreach (var statement in context.statement_list())
                {
                    Visit(statement);
                }

            }
            return null;
        }
        public override object VisitIfStatement(EasyBiteParser.IfStatementContext context)
        {
            bool condition = (bool)Visit(context.expression());

            if (condition == true)
            {
                foreach (var statementListContext in context.statement_list())
                {
                    Visit(statementListContext);
                }

            }
            else
            {
                if (context.elseifStatement() != null)
                {
                    foreach (var elseIfStatementContext in context.elseifStatement())
                    {
                        bool elseIfCondition = (bool)Visit(elseIfStatementContext.expression());



                        if (elseIfCondition)
                        {
                            foreach (var statementListContext in elseIfStatementContext.statement_list())
                            {
                                Visit(statementListContext);
                            }

                            // Stop processing further else-if and else statements
                            return null;
                        }
                    }
                }
                var elseStatement = context.elseStatement();

                if (elseStatement != null)
                {
                    foreach (var statementListContext in elseStatement.statement_list())
                    {
                        Visit(statementListContext);
                    }
                    return null;
                }
            }

            return null;
        }

        public object Divide(object left, object right)
        {
            if (left is int && right is int)
            {
                return (int)left / (int)right;
            }
            else if (left is double && right is double)
            {
                return (double)left / (double)right;
            }
            else if ((left is double && right is int) || (left is int && right is double))
            {
                return (double)left / (double)right;
            }
            else
            {
                return String.Format("Cant divide {0} by {1}", left, right);
            }
        }
    }


}

And the source code i tried with

generate i from 1 to 50 
if i / 2 == 2 then 
show "Make sense"
else 
show "is not working"
end if     
stop

repeat while(2 > 3) 
show "test" 
showline()
show("")
end repeat

Now this i / 2 == 2 is not working which crashes the interpreter with message of invalid convertion after using debugger i realized it actually calling i name instead of i value.

Thank you.


Solution

  • Apparently your i is an int, and when you do:

    else if (context.DIV() != null)
    {
        double left = (double)VisitExpression(context.expression(0));
        ...
    }
    

    the cast of an int to a double breaks things.

    If you'd try:

    else if (context.DIV() != null)
    {
        int left = (int)VisitExpression(context.expression(0));
        ...
    }
    

    maybe it'd work. Of course, that will break when i is not an int, but at least you know where to problem lies.

    EDIT

    I assumed your i is an int because of this:

    public override object VisitGenerateStatement(EasyBiteParser.GenerateStatementContext context)
    {
        string variableName = context.ID().GetText();
        ...
        for (int i = start; i <= end; i += step)
        {
            variables[variableName] = i; // <-- stored as an `int`
            ...
        }
    
        return null;
    }
    

    and then you retrieve it like this:

    else if (context.ID() != null)
    {
        string id = context.ID().GetText();
    
        if (variables.ContainsKey(id))
        {
            if (variables[id] is int)
            {
                return (int)variables[id]; // <-- this must be the case
            }
            ...
        }
        else
        {
            Console.WriteLine("No variable named {0}", id);
        }
    }
    

    If that is not the case, you'll need to update your question with a full working example. That means posting a complete grammar that works with the visitor from your question.