In this article, I would like to find out whether it is possible to construct a programming language with Mathos Parser. In general, the question is if a simple expression parser has the necessary things to easily convert it into a language.

The answer is yes, but the language will be very limited if additional features are not added. The reason for this is because what most languages have in common is some sort of  user/machine interaction, while an expression parser only allows you to parse mathematical things and is not really designed to interpret how to write to a file or allocate memory.

For example, imagine you want to create something similar to if statements  and you decide to treat an if as a function. You would use following code:

parser.LocalFunctions.Add("if", x =>
{
    if (x[0] == 1)
    {
        return x[1];
    }
    else
    {
        if (x.Length == 3)
        {
            return x[2];
        }
        else
        {
            return 0;
        }
    }
});

So, if whatever we pass into the first parameter is true, the value in the second parameter will be returned. This is quite cool, but it does not allow us to return a series of statements. Instead, we could design a new kind of if statement that would, depending on the result from this if function, execute the right method. Here is how I solved it:

let a = get()               # ask for user input

if(a>3,1, 0)                # it only returns a number
1-> write It is true        # these execute something depending on result
0-> write It is false

As you can see, the if statement is still there, but there is now a feature that will interpret the result and make something happen. You can also use \n to separate between different statements and also name a collection of statements, as shown below:

# Using statements as small methods
# The first result will not use statement,
# the second one will.

notTrue : title Not true \n write Your number is smaller than five (or equal to) \n pause

write Type a number between 1-10

let b be get()

if(b>5, 1, 0)
1-> title A New title! \n write Your number is greater than five! \n pause
0-> notTrue

Note that both examples use a function get(). It can also be implemented as a function:

parser.LocalFunctions.Add("get", x =>
{
    return System.Convert.ToDecimal(Console.ReadLine());
}
);

You might have noticed the use of statements (see notTrue statement). Their role is to simulate a method. Everything you enter into a statement will not be executed until you call it, so you can insert undefined variables into a statement when you define it and later assign them with a value. I have also added commands like write, title, pause, clear and end. These are extensions to the language and are interpreted in the method below:

static void command(Mathos.Parser.MathParser parser, string input, Dictionary<string, string> statement, ref decimal lastResult)
{
    try
    {
        input = Regex.Replace(input, "#\\{.*?\\}#", ""); // Delete Comments #{Comment}#
        input = Regex.Replace(input, "#.*$", ""); // Delete Comments #Comment

        input = Regex.Replace(input, @"[0-9]+\!", new MatchEvaluator(FactorialString));

        input = Regex.Replace(input, @"^\s+", "");

        if (input != "")
        {
            if (input.StartsWith("write"))
            {
                Console.WriteLine(input.Substring(6));
            }
            else if (input.StartsWith("title"))
            {
                Console.Title = input.Substring(6);
            }
            else if (input.StartsWith ("clear"))
            {
                Console.Clear();
            }
            else
            {
                if (input.Contains("->"))
                {
                    int index = input.IndexOf("->");

                    if (lastResult == System.Convert.ToDecimal(input.Substring(0, index)))
                    {
                        mCommand(parser, input.Substring(index + 2), statement, ref lastResult);
                    }
                }
                else
                {
                    if (input.Contains(":") && input.Contains(":=") == false)
                    {
                        int index = input.IndexOf(":");

                        statement.Add(input.Substring(0, index).Replace(" ", ""), input.Substring(index + 1, input.Length - index - 1));

                    }
                    else if (input.StartsWith("pause"))
                    {
                        Console.ReadKey();

                    }
                    else if (input.StartsWith("end"))
                    {
                        Environment.Exit(0);
                    }

                    else
                    {
                        foreach (var item in statement)
                        {
                            if (input.Contains(item.Key))
                            {
                                input = "";
                                mCommand(parser, item.Value, statement, ref lastResult);
                            }
                        }
                        lastResult = parser.ProgrammaticallyParse(input, identifyComments: false);
                        Console.WriteLine(lastResult);
                    }
                }
            }
        }
    }
    catch (Exception e)
    {
        Console.WriteLine("An error occured. Message: " + e.Message);
    }
}

static void mCommand(Mathos.Parser.MathParser parser, string input, Dictionary<string, string> statement, ref decimal lastResult)
{
    input = input.Replace(@"\n", System.Environment.NewLine);

    System.IO.StringReader sr = new System.IO.StringReader(input);

    string _input = sr.ReadLine();

    while (_input != null)
    {
        command(parser, _input, statement, ref lastResult);
        _input = sr.ReadLine();
    }
    sr.Close();
}

Some programmers will certainly argue that the code above is not that optimized and is quite primitive. Well, it serves its purpose and makes Mathos Parser look more like a language.

In the next article I want to implement a while feature into this awesome programming language.

Conclusion: Just because something can parse expressions does not mean it can parse a series of statement. In order to fix this we can either choose to add some features externally, or, go into the parser itself and make it more adjusted to this particular task. This language does not alter Mathos Parser in any way.

Downloads: