This repository has been archived on 2021-09-20. You can view files and clone it, but cannot push or open issues or pull requests.
reemo-gasi/GASi/Parser.cs

247 lines
9.5 KiB
C#
Raw Permalink Normal View History

2021-09-20 01:05:36 +00:00
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; }
}
}
}