1

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.

  • You might want to lookup _pattern matching_ in `if` statements. It would make your code a little easier to read. Consider `if (obj is bool boolObj) { return boolObj; }` – Flydog57 Mar 09 '23 at 17:38
  • Actually didn't understand, Sorry Can you give me clear example? The ToBool function i didn't used it yet. – Muhammad Baba Goni Mar 09 '23 at 18:16
  • Take a look at this article, particularly the _"Type Tests"_ section: https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching#type-tests. It's only a minor change to your code, and it doesn't change the behavior, but it removes the explicit casts and (to my eyes), makes it more readable. My comment actually include an example – Flydog57 Mar 09 '23 at 21:02
  • I got the problem actually but i just don't know where it lies, the issue actually is not about convertion rather about treating a variable as string instead of getting it value. As you can see i am storing variables and there values in dictionary. When i call i in if state it given me the i instead of value e.g 2. – Muhammad Baba Goni Mar 09 '23 at 22:21

1 Answers1

0

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.

Bart Kiers
  • 166,582
  • 36
  • 299
  • 288
  • I got the problem actually but i just don't know where it lies, the issue actually is not about convertion rather about treating a variable as string instead of getting it value. As you can see i am storing variables and there values in dictionary. When i call i in if state it given me the i instead of value e.g 2. – Muhammad Baba Goni Mar 09 '23 at 22:21
  • Without the if statement is working normal. Like if i want to print and etc – Muhammad Baba Goni Mar 09 '23 at 22:23
  • Check my edit. But it seems to me you need to carefully step through your code with a debugger. – Bart Kiers Mar 09 '23 at 22:45