246 lines
9.5 KiB
C#
246 lines
9.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using static GASi.Lexer;
|
|
|
|
namespace GASi {
|
|
class Parser {
|
|
public enum kControl {
|
|
FUNC, IF, FOR, WHILE, SWITCH, CASE
|
|
}
|
|
|
|
private ScopeTable Globals = new ScopeTable();
|
|
private MethodTable Methods = new MethodTable();
|
|
private Stack<kControl> ControlStack = new Stack<kControl>();
|
|
|
|
private MethodTable.Method CurrentMethod = null;
|
|
private List<Token> Tokens;
|
|
private int TokenIterator;
|
|
|
|
private static Dictionary<string, List<Pattern>> Patterns = new Dictionary<string, List<Pattern>>() {
|
|
{ "pre_inc", new List<Pattern> {
|
|
new Pattern(Token.kType.PREPROC, "#include"),
|
|
new Pattern(Token.kType.STRING)
|
|
} },
|
|
|
|
{ "fndecl", new List<Pattern> {
|
|
new Pattern(Token.kType.TYPE),
|
|
new Pattern(Token.kType.IDENTIFIER),
|
|
new Pattern(Token.kType.LPAREN)
|
|
} },
|
|
|
|
{ "fnparams", new List<Pattern> {
|
|
new Pattern(Token.kType.TYPE),
|
|
new Pattern(Token.kType.IDENTIFIER),
|
|
new Pattern(Token.kType.COMMA),
|
|
} },
|
|
|
|
{ "ctrlstart", new List<Pattern> {
|
|
new Pattern(Token.kType.RPAREN),
|
|
new Pattern(Token.kType.LBRACE)
|
|
} }
|
|
};
|
|
|
|
public Parser(List<Token> Tokens) {
|
|
this.Tokens = Tokens;
|
|
}
|
|
|
|
public List<Expression> Parse() {
|
|
var ret = new List<Expression>();
|
|
|
|
TokenIterator = 0;
|
|
while(TokenIterator < Tokens.Count) {
|
|
var expr = new Expression();
|
|
var token = Tokens[TokenIterator];
|
|
|
|
if(CurrentMethod == null) { // if we're in the global scope
|
|
switch(token.Type) {
|
|
case Token.kType.PREPROC: // preprocessor definition
|
|
// TODO add more preproc defs
|
|
if(!CheckPattern(Patterns["pre_inc"]))
|
|
Transpiler.Exception("Invalid preprocessor directive '" + token.Value.Substring(1) + "'", 0, 0);
|
|
|
|
expr.Tokens = Iterate(2);
|
|
expr.Type = Expression.kType.PREPROC;
|
|
break;
|
|
case Token.kType.TYPE: // function declaration
|
|
expr = ParseFunctionDeclaration();
|
|
break;
|
|
default:
|
|
Transpiler.Exception("Unexpected token '" + token.Value + "' of type '" + token.Type.ToString() + "' in global scope", 0, 0);
|
|
break;
|
|
}
|
|
} else { // if we're in a local scope
|
|
|
|
}
|
|
|
|
ret.Add(expr);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public Expression ParseFunctionDeclaration() {
|
|
var expr = new Expression();
|
|
|
|
if(CheckPattern(Patterns["fndecl"])) {
|
|
expr.Tokens = Iterate(Patterns["fndecl"].Count);
|
|
var functionName = expr.Tokens[1].Value;
|
|
|
|
expr.ControlName = functionName;
|
|
expr.ControlType = kControl.FUNC;
|
|
expr.Type = Expression.kType.CTRLSTART;
|
|
|
|
var method = new MethodTable.Method() {
|
|
Name = functionName,
|
|
ReturnType = Conversion.StringToType(expr.Tokens[0].Value)
|
|
};
|
|
|
|
int argTokenCount;
|
|
if((argTokenCount = CheckPatternLoop(Patterns["fnparams"], Token.kType.RPAREN)) == -1)
|
|
Transpiler.Exception("Invalid token found in argument list of function '"+ functionName +"'", 0, 0);
|
|
|
|
// Now that we know that it follows the pattern, we must check for early truncation.
|
|
// Note the standard form for an argument list:
|
|
// TOKENS: [TYPE IDENTIFIER COMMA]*(n-1) + [TYPE IDENTIFIER]
|
|
// Where n is the function's number of arguments.
|
|
// The last argument should not be followed by a comma, nor should be incomplete
|
|
// (that is to say, only a type identifier by itself), so we can check the
|
|
// length of the argument list passed and treat it as such:
|
|
// TOKEN COUNT = 3*(n-1) + 2
|
|
// This follows from the token formula state above.
|
|
// If you add one to this token count, you end up with this:
|
|
// TOKEN COUNT = 3*(n-1) + 3 = 3n - 3 + 3 = 3n
|
|
// Which can then be confirmed using the remainder of dividing the argument list
|
|
// by three and determining if it is zero. This will only be true for a well-
|
|
// formed argument list.
|
|
if((argTokenCount + 1) % 3 != 0)
|
|
Transpiler.Exception("Malformed final argument in argument list of function '"+ functionName +"'", 0, 0);
|
|
|
|
var args = Iterate(argTokenCount);
|
|
var arg = new MethodTable.Method.Argument();
|
|
for(int i = 0; i < args.Count; ++i) {
|
|
var token = args[i];
|
|
|
|
switch(i%3) {
|
|
case 0:
|
|
arg = new MethodTable.Method.Argument {
|
|
Type = Conversion.StringToType(token.Value)
|
|
};
|
|
|
|
if(arg.Type == kType.VOID)
|
|
Transpiler.Exception("Function '" + functionName + "' cannot have arguments with a void type", 0, 0);
|
|
break;
|
|
case 1:
|
|
arg.Name = token.Value;
|
|
method.Arguments.Add(arg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
expr.Tokens.AddRange(args);
|
|
if(!CheckPattern(Patterns["ctrlstart"]))
|
|
Transpiler.Exception("Malformed tokens after argument list in function declaration '"+ functionName +"'", 0, 0);
|
|
|
|
expr.Tokens.AddRange(Iterate(Patterns["ctrlstart"].Count));
|
|
|
|
Methods.AddMethod(method);
|
|
} else
|
|
Transpiler.Exception("Invalid token found during function declaration", 0, 0);
|
|
|
|
return expr;
|
|
}
|
|
|
|
private bool CheckPattern(List<Pattern> pattern) {
|
|
for(var i = TokenIterator; i < TokenIterator + pattern.Count; ++i) {
|
|
if(i >= Tokens.Count)
|
|
Transpiler.Exception("Unexpected EOF when checking for token pattern", 0, 0);
|
|
|
|
var j = i - TokenIterator;
|
|
if(Tokens[i].Type != pattern[j].Type || (pattern[j].Value != null && Tokens[i].Value != pattern[j].Value))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private int CheckPatternLoop(List<Pattern> pattern, Token.kType terminator) {
|
|
var i = TokenIterator;
|
|
for(; ; ++i) {
|
|
if(i >= Tokens.Count)
|
|
Transpiler.Exception("Unexpected EOF when checking for repeated token pattern", 0, 0);
|
|
|
|
var j = (i - TokenIterator) % pattern.Count;
|
|
if(Tokens[i].Type == terminator)
|
|
break;
|
|
if(Tokens[i].Type != pattern[j].Type || (pattern[j].Value != null && Tokens[i].Value != pattern[j].Value))
|
|
return -1;
|
|
}
|
|
|
|
return i - TokenIterator;
|
|
}
|
|
|
|
private List<Token> Extract(int length) {
|
|
if(TokenIterator + length > Tokens.Count)
|
|
Transpiler.Exception("Attempted to read expected tokens past EOF", 0, 0);
|
|
|
|
return Tokens.GetRange(TokenIterator, length);
|
|
}
|
|
|
|
private void Skip(int count) {
|
|
TokenIterator += count;
|
|
}
|
|
|
|
private List<Token> Iterate(int count) {
|
|
var tmp = Extract(count);
|
|
Skip(count);
|
|
return tmp;
|
|
}
|
|
|
|
private int CountUntilNext(Token.kType type, Token.kType? nester = null, bool allowNesting = true) {
|
|
int depth = 1, count = 0;
|
|
for(; depth > 0; ++count) {
|
|
if(TokenIterator + count >= Tokens.Count)
|
|
Transpiler.Exception("Closing token '"+ type.ToString() +"' for appropriate scope not found", 0, 0);
|
|
|
|
var token = Tokens[TokenIterator + count];
|
|
|
|
if(token.Type == type)
|
|
--depth;
|
|
else if(token.Type == nester) {
|
|
if(allowNesting)
|
|
++depth;
|
|
else
|
|
Transpiler.Exception("Invalid token of type '" + token.Type + "' found when nesting is not allowed", 0, 0);
|
|
}
|
|
|
|
}
|
|
return count + 1;
|
|
}
|
|
|
|
public class Pattern {
|
|
public Token.kType Type { get; set; }
|
|
public string Value { get; set; }
|
|
|
|
public Pattern(Token.kType type, string value = null) {
|
|
Type = type;
|
|
Value = value;
|
|
}
|
|
}
|
|
|
|
public class Expression {
|
|
public enum kType {
|
|
PREPROC,
|
|
CTRLSTART, BREAK, CTRLEND,
|
|
LVAL, ASSIGN, RVAL
|
|
}
|
|
|
|
public kType Type { get; set; }
|
|
public kControl ControlType { get; set; }
|
|
public string ControlName { get; set; }
|
|
public List<Token> Tokens { get; set; }
|
|
}
|
|
}
|
|
}
|