Augmenting to the Good For Nothing Compiler (Part 1 of 2)

A little while back, Joel posted the source to the Good For Nothing (GFN) Compiler and wondered what people can do with it. As a little side project, I added some features to the GFN Compiler which I will post in two parts.

Part 1: Making BCL calls from the GFN Compiler

Part 2: Making Function calls from the GFN Compiler

And without further ado.. here is part 1...

Part 1: Making BCL calls from the GFN Compiler

Here is the grammar modification I did to accomodate making BCL calls:

<stmt> := var <ident> = <expr>
| <ident> = <expr>
| <ident> = <func_stmt>
| for <ident> = <expr> to <expr> do <stmt> end
| read_int <ident>
| print <expr>
| <stmt> ; <stmt>
| <func_stmt>

<func_stmt> := <scope><func_call>
<scope> := (<namespace>)? <scope>*
<namespace> := <ident>.
<func_call> := <ident> (<args>)

Additons to scanner.cs for class Scanner:

First, I need to scan the new objects for a function call into the tokens.

// Constants to represent arithmitic tokens. This could
// be alternatively written as an enum.
// ....
public static readonly object Comma = new object();
public static readonly object LeftBracket = new object(); // "("
public static readonly object RightBracket = new object(); // ")"

// Switch statment for character
// ....
else switch (ch)
{
case ',':
   input.Read();
result.Add(Scanner.Comma);
break;
case '(':
   input.Read();
result.Add(Scanner.LeftBracket);
break;
case ')':
input.Read();
result.Add(Scanner.RightBracket);
break;
}

Additons to Ast.cs:

Then, I created a class to hold a function call.

 public class FunctionCall : Stmt 
{ 
    public List<string> DotedScopes; 
    public string FunctionName; 
    public List<Expr> Args; 

    public FunctionCall() 
    { 
        DotedScopes = new List<string>(); 
        Args = new List<Expr>(); 
    } 
}

Additons to Parser.cs for class Paser:

I need to make additions to the Parser so that it can parse the function call into the the class I created in Ast.cs.

     private FunctionCall ParseFunc() 
    { 
        //Function calls 
        FunctionCall func = new FunctionCall(); 

        // function scope 
        while (this.index + 1 < this.tokens.Count 
            && this.tokens[this.index + 1] == Scanner.Dot) 
        { 
            func.DotedScopes.Add((string)this.tokens[this.index++]); 
            // Skips the dot 
            this.index++; 
        } 
        // Function Name 
        if (this.index == this.tokens.Count || 
            !(this.tokens[this.index] is string)) 
        { 
            throw new System.Exception("expected function name after scope"); 
        } 
        func.FunctionName = (string)this.tokens[this.index++]; 

        // Arguments 
        if (this.index == this.tokens.Count || 
            this.tokens[this.index] != Scanner.LeftBracket) 
        { 
            throw new System.Exception("expect open bracket after function name"); 
        } 
        this.index++;   // Skip Left Bracket 

        while ((this.tokens[this.index] != Scanner.RightBracket) 
            && (this.index < this.tokens.Count)) 
        { 
            func.Args.Add(this.ParseExpr()); 
            if (this.tokens[this.index] == Scanner.Comma) 
                this.index++; // Skip comma 
            else if (this.tokens[this.index] == Scanner.RightBracket) 
                break; 
            else 
                throw new System.Exception("unexpected character in arg list"); 
        } 

        if (this.index == this.tokens.Count || 
            this.tokens[this.index] != Scanner.RightBracket) 
        { 
            throw new System.Exception("expect close bracket after open bracket/args"); 
        } 
        this.index++;   // Skip RightBracket 

        return func; 
    } 
} 

Additons to CodeGen.cs for class CodeGen

Now, the best part of writing a compiler... the actual code generation.

     private void CallFunction(FunctionCall func, out System.Type returnType) 
    { 
        System.Type[] typeArray = new System.Type[func.Args.Count]; 
        for (int i = 0; i < func.Args.Count; i++) 
        { 
            typeArray[i] = this.TypeOfExpr(func.Args[i]); 
            this.GenExpr(func.Args[i], typeof(object)); 
        } 
        Reflect.MethodInfo mi = null; 
        // BCL calls 
        if (func.DotedScopes.Count > 0) 
        { 
            Text.StringBuilder scope = new Text.StringBuilder(); 
            foreach (string str in func.DotedScopes) 
            { 
                scope.Append(str + "."); 
            } 
            // Remove the last dot 
            scope.Remove(scope.Length - 1, 1); 
            System.Type type = System.Type.GetType(scope.ToString()); 
            mi = type.GetMethod(func.FunctionName, typeArray); 
        } 
        else 
        // Local Function calls 
        { 
            if (!this.symbolTable.FunctionTable.ContainsKey(func.FunctionName)) 
            { 
                throw new System.Exception("function \"" + func.FunctionName + "\" not declared");     
            } 
            mi = this.symbolTable.FunctionTable[func.FunctionName]; 
        } 
        returnType = mi.ReturnType; 
        this.il.Emit(Emit.OpCodes.Call, mi); 
    }