Getting MyC to execute with Irony

Nov 4, 2009 at 10:41 PM

Using this as an example: http://www.codeproject.com/KB/recipes/VSLanguageService.aspx?msg=2995031, I was trying to get MyC up and going on Irony.

I can get a basic code snippet to compile, but am not clear on the syntax to get it to actually run.

For example, the following will compile:

int foo(int a, int b)
{
   return a+b;
}

I couldn't figure out how to declare a main, or some equivalent. All I am able to do is compile functions, I can't call them.

 

If anyone know how to then call the methods I'd like to know.

The grammar I am using is listed below. Any help is greatly appreciated.

    public class MyC : Irony.Parsing.Grammar
    {
        public MyC()
        {
            #region Declare Terminals Here
            CommentTerminal blockComment = new CommentTerminal("block-comment", "/*", "*/");
            CommentTerminal lineComment = new CommentTerminal("line-comment", "//", "\r", "\n", "\u2085", "\u2028", "\u2029");
            NonGrammarTerminals.Add(blockComment);
            NonGrammarTerminals.Add(lineComment);

            NumberLiteral number = new NumberLiteral("number");
            number.AddPrefix("0x", NumberFlags.Hex);
            number.AddSuffixCodes("f", TypeCode.Single);

            IdentifierTerminal identifier = new IdentifierTerminal("identifier");
            #endregion

            #region Declare NonTerminals Here
            NonTerminal program = new NonTerminal("program");
            NonTerminal declarations = new NonTerminal("declaration");
            NonTerminal declaration = new NonTerminal("declaration");
            NonTerminal simpleDeclarations = new NonTerminal("simple-declarations");
            NonTerminal simpleDeclaration = new NonTerminal("simple-declaration");
            NonTerminal semiDeclaration = new NonTerminal("semi-declaration");
            NonTerminal parenParameters = new NonTerminal("paren-parameters");
            NonTerminal parameters = new NonTerminal("parameters");
            NonTerminal classOption = new NonTerminal("class-option");
            NonTerminal variableType = new NonTerminal("variable-type");
            NonTerminal block = new NonTerminal("block");
            NonTerminal blockContent = new NonTerminal("block-content");
            NonTerminal statements = new NonTerminal("statements");
            NonTerminal statement = new NonTerminal("statement");
            NonTerminal parenExpressionAlways = new NonTerminal("paren-expression-always");
            NonTerminal parenExpression = new NonTerminal("paren-expression");
            NonTerminal forHeader = new NonTerminal("for-header");
            NonTerminal forBlock = new NonTerminal("for-block");
            NonTerminal semiStatement = new NonTerminal("semi-statement");
            NonTerminal arguments = new NonTerminal("arguments");
            NonTerminal parenArguments = new NonTerminal("paren-arguments");
            NonTerminal assignExpression = new NonTerminal("assign-expression");
            NonTerminal expression = new NonTerminal("expression");
            NonTerminal booleanOperator = new NonTerminal("boolean-operator");
            NonTerminal relationalExpression = new NonTerminal("relational-expression");
            NonTerminal relationalOperator = new NonTerminal("relational-operator");
            NonTerminal bitExpression = new NonTerminal("bit-expression");
            NonTerminal bitOperator = new NonTerminal("bit-operator");
            NonTerminal addExpression = new NonTerminal("add-expression");
            NonTerminal addOperator = new NonTerminal("add-operator");
            NonTerminal multiplyExpression = new NonTerminal("muliply-expression");
            NonTerminal multiplyOperator = new NonTerminal("multiply-operator");
            NonTerminal prefixExpression = new NonTerminal("prefix-expression");
            NonTerminal prefixOperator = new NonTerminal("prefix-operator");
            NonTerminal factor = new NonTerminal("factor");
            NonTerminal identifierExpression = new NonTerminal("identifier-expression");
            #endregion

            #region Place Rules Here
            

            declarations.Rule = MakeStarRule(declarations, declaration);

            //Must follow declarations so that assignment is made after rule is initialized.
            program.Rule = declarations.Rule;

            declaration.Rule
                = classOption + variableType + identifier + parameters + block
                | classOption + identifier + parenParameters + block
                | variableType + identifier + parenParameters + block
                | identifier + parenParameters + block
                | simpleDeclaration;

            simpleDeclarations.Rule = MakePlusRule(simpleDeclarations, simpleDeclaration);

            simpleDeclaration.Rule = semiDeclaration + ";";

            semiDeclaration.Rule
                = semiDeclaration + "," + identifier
                | classOption + variableType + identifier
                | variableType + identifier;

            parameters.Rule
                = parameters + "," + variableType + identifier
                | variableType + identifier;

            parenParameters.Rule
                = Symbol("(") + ")"
                | "(" + parameters + ")";

            classOption.Rule
                = Symbol("static")
                | "auto"
                | "extern";

            variableType.Rule
                = Symbol("int")
                | "void";

            block.Rule
                = Symbol("{") + "}"
                | "{" + blockContent + "}";

            blockContent.Rule
                = simpleDeclarations + statements
                | simpleDeclarations
                | statements;

            statements.Rule = MakePlusRule(statements, statement);

            statement.Rule
                = semiStatement + ";"
                | "while" + parenExpression + statement
                | "for" + forHeader + statement
                | "if" + parenExpression + statement
                | "if" + parenExpression + statement + PreferShiftHere() + "else" + statement;

            parenExpression.Rule = Symbol("(") + expression + ")";

            forHeader.Rule = "(" + forBlock + ")";

            forBlock.Rule = assignExpression + ";" + expression + ";" + assignExpression;

            semiStatement.Rule
                = assignExpression
                | "return" + expression
                | "break"
                | "continue";

            arguments.Rule
                = expression + "," + arguments
                | expression;

            parenArguments.Rule
                = Symbol("(") + ")"
                | "(" + arguments + ")";

            assignExpression.Rule
                = identifier + "=" + expression
                | expression;

            expression.Rule
                = relationalExpression + booleanOperator + expression
                | relationalExpression;

            booleanOperator.Rule
                = Symbol("&&")
                | "||";

            relationalExpression.Rule
                = bitExpression + relationalOperator + bitExpression
                | bitExpression;

            relationalOperator.Rule
                = Symbol(">")
                | ">="
                | "<"
                | "<="
                | "=="
                | "!=";

            bitExpression.Rule
                = addExpression + bitOperator + bitExpression
                | addExpression;

            bitOperator.Rule
                = Symbol("|")
                | "&"
                | "^";

            addExpression.Rule
                = multiplyExpression + addOperator + addExpression
                | prefixExpression;

            addOperator.Rule
                = Symbol("+") | "-";

            multiplyExpression.Rule
                = prefixExpression + multiplyOperator + multiplyExpression
                | prefixExpression;

            multiplyOperator.Rule
                = Symbol("*")
                | "/";

            prefixExpression.Rule
                = prefixOperator + factor
                | factor;

            prefixOperator.Rule = Symbol("!");

            factor.Rule
                = identifierExpression + parenArguments
                | identifierExpression
                | number
                | parenExpression;

            identifierExpression.Rule
                = identifier
                | identifierExpression + "." + identifier;
            #endregion

            #region Define Keywords and Register Symbols
            //this.AddKeywords("break", "continue", "else", "extern", "for",
            //    "if", "int", "return", "static", "void", "while");

            this.RegisterBracePair("{", "}");
            this.RegisterBracePair("(", ")");

            this.RegisterOperators(1, "+", "-");
            this.RegisterOperators(2, "*", "/");
            #endregion


            


            this.Root = expression;
            this.LanguageFlags = LanguageFlags.CreateAst | LanguageFlags.CanRunSample;
        }
    }

Coordinator
Nov 4, 2009 at 10:48 PM

I'm afraid you're a bit confused. Having Irony grammar for a language allows you to parse it. To execute it, some more should be done: AST node types defined for non-terminals. If you look at expression evaluator grammar, you'll see that non-terminals declarations have this second parameter "typeof(xxxNode)" - this is for construction of executable AST tree. For GWBasic grammar, and MyC as well, these nodes are not defined, so all Irony can do is just parse it into a parse tree.

And I'm afraid you can't modify these grammars easily to make them run the scripts - the Irony's AST node set is very limited now, it is enough only for very primitive languages, like ExprEvaluator or mini-Python.

Roman

Nov 5, 2009 at 3:57 AM

Do not be afraid of me being confused. I'm very happy with the delusional world I have constructed for myself.

In all seroiusness, I guess I was/am confused. I see the working example of mini-python, where you can actually define functions AND run them, and examples where you can assign variables and use numeric operators. I guess I am not clear on what Irony is and isn't capable of.

1. Would you be able to use Irony to create the full grammar for a Basic like language? I assume yes.

2. Would you be able to overload methods with different number and types of parameters?

3. Once you had the full AST would you just have to implement the correct classes to get this to run?

4. If you had to provide a bullet proof language for users to write scripts would you use this version of Irony, or wait a few versions? By bullet proof I don't mean it is an awesome language, I just mean that there are no defects in it.

 

Irony looks well thought out, but I obviously need to get a better grasp on what it is and isn't designed for.

 

thanks a ton, this tool looks great so far.

Coordinator
Nov 5, 2009 at 5:12 PM

The answer to questions 1-3 is yes. As for #4, if you need to provide a solid scripting engine inside your app, and you need it now, then you should go for IronPython.

Irony is a tool for creating scripting solutions, but not a scripting solution by itself. People will build real things using Irony, but Irony itself is not the thing you're looking for. As for IronPython, you should consider the following factor:

1. Language - it's better to have some standardized and popular language, so customers wouldn't have trouble finding help and advice about it.

2. Implementation - it should be .NET based, with full .NET interoperability, allowed to be hosted inside other .NET app, and allow extension/hooks to the hosting app (to manipulate the host app's objects)

IronPython fits well into these requirements. I myself uses it as a script engine in one of my features in Dynamics AX - see my presentation in Lang.NET 2009

Hope this answers your questions

Roman

Nov 5, 2009 at 7:57 PM

My customers have 1000s of scripts written in a basic-like language, and any language I provide must also run all of their scripts. 

This is why I'm looking at implementing a language. I am fully aware of all the reasons an already existing language should be used, but compatibility with the old scripts is the number one priority for my customers at this point.

As a side note, I have already provided an IronPython solution, and it is working great for new features and scripts.

 

Thanks for the info. It is very helpful.

Coordinator
Nov 6, 2009 at 8:29 AM

Well. if you're stuck with GwBasic for existing scripts, and wish to upgrade the engine, then it is a doable project with Irony, even in incomplete state as it is today. You have a parser already, you'd need to complete the AST set for various statement types. I don't think its a very big undertaking, you have already some initial sketch of Interpreter infrastructure, it should be matter of weeks at most. The only trouble I see is that your solution may mismatch my design decisions when I finally get to completing the AST's. Or you can just wait, I may get to it in 2-3 months period, and then I hope building Basic interpreter would be an easy and fast work.

Roman