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
Anonymous
October 19, 2005
Is there any way you could change DotedScope to DottedScope?
(Yes, I know I'm a pedant. The pain, the pain.)Anonymous
October 19, 2006
What about boolean expression? Can you show a way to implement boolean? So this may be the first step for If or while statements?Anonymous
January 08, 2009
Over the past few weeks I have been developing a version of Joel Pobar's GFN language that utilizes