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);
}

Comments (3)

  1. barrkel says:

    Is there any way you could change DotedScope to DottedScope?

    (Yes, I know I’m a pedant. The pain, the pain.)

  2. Burak SARICA says:

    What about boolean expression? Can you show a way to implement boolean? So this may be the first step for If or while statements?

  3. Over the past few weeks I have been developing a version of Joel Pobar&#39;s GFN language that utilizes