756 lines
26 KiB
C#
756 lines
26 KiB
C#
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
|
|
namespace TLang
|
|
{
|
|
public class Parser
|
|
{
|
|
private static bool _checkForArrayType(ref int i,List<LexToken> tokens)
|
|
{
|
|
|
|
if(i+2 < tokens.Count && tokens[i+1].Type == LexSymbol.Symbol && tokens[i+2].Type == LexSymbol.Symbol && tokens[i+1].TokenText == "[" && tokens[i+2].TokenText == "]")
|
|
{
|
|
//we are getting somewhere
|
|
//type normal parse
|
|
//type [ ] normal parse
|
|
i+=2;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
public static ProgramNode Parse(List<LexToken> tokens)
|
|
{
|
|
ProgramNode node=new ProgramNode();
|
|
//function pointers are
|
|
//funcptr func identifer (arg_type arg_name, ...);
|
|
//funcptr
|
|
|
|
// a function can be
|
|
// func identifier (arg_type arg_name,...)
|
|
// return_type identifier (arg_type arg_name, ...)
|
|
// func identifier (arg_type arg_name)
|
|
// return_type identifier (arg_type arg_name)
|
|
// func identifier ()
|
|
// return_type identifier ()
|
|
|
|
//a const variable can be (also in func)
|
|
//variable_type identifier;
|
|
//variable_type identifier = expression;
|
|
|
|
int i = 0;
|
|
for(;i<tokens.Count;i++)
|
|
{
|
|
//we need a type or null
|
|
LexToken type_token = tokens[i];
|
|
if(type_token.Type == LexSymbol.Keyword || type_token.Type == LexSymbol.Identifier)
|
|
{
|
|
string type;
|
|
//we only allow keywords for variable types for now eg no data types other than system ones
|
|
if(_checkForArrayType(ref i,tokens))
|
|
{
|
|
type=$"{type_token.TokenText}[]";
|
|
//it is an array
|
|
}else{
|
|
type=type_token.TokenText;
|
|
//not a bloddy array
|
|
}
|
|
i++;
|
|
if(i<tokens.Count)
|
|
{
|
|
LexToken name_token = tokens[i];
|
|
if(name_token.Type == LexSymbol.Identifier)
|
|
{
|
|
//this must be an identifier
|
|
|
|
//now we will check if it is a function or a variable
|
|
//is next char ( or = or ;
|
|
|
|
i++;
|
|
if(i<tokens.Count)
|
|
{
|
|
LexToken del=tokens[i];
|
|
if(del.Type == LexSymbol.Symbol)
|
|
{
|
|
if(del.TokenText == "(")
|
|
{
|
|
FunctionNode fnode=ParseArguments(ref i,tokens);
|
|
ParseFunction(ref i,tokens,fnode);
|
|
fnode.Column = type_token.ColumnNumber;
|
|
fnode.FilePosition = type_token.OffsetInFile;
|
|
fnode.Line = type_token.LineNumber;
|
|
fnode.ReturnType = type_token.TokenText;
|
|
fnode.Name = name_token.TokenText;
|
|
node.Functions.Add(fnode);
|
|
|
|
|
|
//must be function
|
|
}else if(del.TokenText == "=")
|
|
{
|
|
i++;
|
|
if(i<tokens.Count){
|
|
//must be creation of global variable
|
|
var res=ParseExpression(ref i,tokens);
|
|
VariableDeclarationNode decl=new VariableDeclarationNode();
|
|
decl.Column = type_token.ColumnNumber;
|
|
decl.FilePosition = type_token.OffsetInFile;
|
|
decl.Line = type_token.LineNumber;
|
|
decl.VariableType = type_token.TokenText;
|
|
decl.Name = name_token.TokenText;
|
|
decl.InitialValue = res;
|
|
node.Variables.Add(decl);
|
|
}
|
|
|
|
}else if(del.TokenText == ";")
|
|
{
|
|
//null global variable
|
|
VariableDeclarationNode decl=new VariableDeclarationNode();
|
|
decl.Column = type_token.ColumnNumber;
|
|
decl.FilePosition = type_token.OffsetInFile;
|
|
decl.Line = type_token.LineNumber;
|
|
decl.VariableType = type_token.TokenText;
|
|
decl.Name = name_token.TokenText;
|
|
decl.InitialValue=new UninitNode();
|
|
node.Variables.Add(decl);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
private static FunctionNode ParseArguments(ref int i, List<LexToken> tokens)
|
|
{
|
|
i++;
|
|
List<ArgumentNode> argumentNodes = new List<ArgumentNode>();
|
|
//type arg,type arg
|
|
for(;i<tokens.Count;i++)
|
|
{
|
|
LexToken type = tokens[i];
|
|
if(type.Type ==LexSymbol.Symbol && type.TokenText == ")")
|
|
{
|
|
break;
|
|
}
|
|
if(type.Type == LexSymbol.Keyword || type.Type == LexSymbol.Identifier)
|
|
{
|
|
//we got a type
|
|
ArgumentNode node = new ArgumentNode();
|
|
string typestr;
|
|
//we only allow keywords for variable types for now eg no data types other than system ones
|
|
if(_checkForArrayType(ref i,tokens))
|
|
{
|
|
typestr=$"{type.TokenText}[]";
|
|
//it is an array
|
|
}else{
|
|
typestr=type.TokenText;
|
|
//not a bloddy array
|
|
}
|
|
node.ArgType=typestr;
|
|
|
|
i++;
|
|
if(i<tokens.Count)
|
|
{
|
|
LexToken name = tokens[i];
|
|
if(name.Type == LexSymbol.Identifier)
|
|
{
|
|
node.Name = name.TokenText;
|
|
argumentNodes.Add(node);
|
|
i++;
|
|
if(i<tokens.Count)
|
|
{
|
|
LexToken sep= tokens[i];
|
|
if(sep.Type == LexSymbol.Symbol)
|
|
{
|
|
i++;
|
|
|
|
if(sep.TokenText == ")")
|
|
{
|
|
break;
|
|
}else if(sep.TokenText == ",")
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}else{
|
|
throw new Exception("argument name expected");
|
|
}
|
|
}
|
|
}
|
|
return new FunctionNode(){Arguments=argumentNodes};
|
|
}
|
|
private static Node ParseStatement(ref int i,List<LexToken> tokens)
|
|
{
|
|
//if(EXPRESSION) {} else {}
|
|
//if(EXPRESSION) {}
|
|
//while {}
|
|
//break;
|
|
//return EXPRESSION;
|
|
//TYPE VARNAME = EXPRESSION;
|
|
//VARNAME = EXPRESSION;
|
|
//FUNCNAME(EXPRESSION,...)
|
|
LexToken type_token_keyword_token = tokens[i];
|
|
if(type_token_keyword_token.Type == LexSymbol.Symbol && type_token_keyword_token.TokenText == "{")
|
|
{
|
|
ScopeNode node=new ScopeNode();
|
|
i++;
|
|
for(;i<tokens.Count;i++)
|
|
{
|
|
if(tokens[i].TokenText == "}")
|
|
{
|
|
//i++;
|
|
break;
|
|
}
|
|
node.Nodes.Add(ParseStatement(ref i,tokens));
|
|
|
|
}
|
|
return node;
|
|
}else
|
|
if(type_token_keyword_token.Type == LexSymbol.Keyword)
|
|
{
|
|
//its if, while, break or return
|
|
if(type_token_keyword_token.TokenText == "break")
|
|
{
|
|
BreakNode node=new BreakNode();
|
|
|
|
i++;
|
|
return node;
|
|
//break
|
|
}
|
|
else if(type_token_keyword_token.TokenText=="while")
|
|
{
|
|
i++;
|
|
WhileNode node=new WhileNode();
|
|
node.Body = ParseStatement(ref i,tokens);
|
|
|
|
return node;
|
|
//while
|
|
}
|
|
else if(type_token_keyword_token.TokenText == "if")
|
|
{
|
|
//if
|
|
if(i+2<tokens.Count)
|
|
{
|
|
LexToken lparn = tokens[i+1];
|
|
i+=2;
|
|
if(lparn.TokenText == "(" && lparn.Type == LexSymbol.Symbol)
|
|
{
|
|
Expression ifCondition=ParseExpression(ref i,tokens);
|
|
|
|
|
|
LexToken rparn = tokens[i];
|
|
if(rparn.TokenText == ")" && rparn.Type == LexSymbol.Symbol)
|
|
{
|
|
i++;
|
|
var truth=ParseStatement(ref i,tokens);
|
|
|
|
|
|
//we need to parse body
|
|
}else{
|
|
throw new Exception("missing )");
|
|
}
|
|
}else{
|
|
throw new Exception("missing (");
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|
|
else if(type_token_keyword_token.TokenText == "return")
|
|
{
|
|
//return
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
//variable
|
|
|
|
var type_token=type_token_keyword_token;
|
|
string type;
|
|
if(_checkForArrayType(ref i,tokens))
|
|
{
|
|
type=$"{type_token_keyword_token.TokenText}[]";
|
|
//it is an array
|
|
}else{
|
|
type=type_token_keyword_token.TokenText;
|
|
//not a bloddy array
|
|
}
|
|
i++;
|
|
if(i<tokens.Count)
|
|
{
|
|
LexToken name_token = tokens[i];
|
|
if(name_token.Type == LexSymbol.Identifier)
|
|
{
|
|
//this must be an identifier
|
|
|
|
//now we will check if it is a function or a variable
|
|
//is next char ( or = or ;
|
|
|
|
i++;
|
|
if(i<tokens.Count)
|
|
{
|
|
LexToken del=tokens[i];
|
|
if(del.Type == LexSymbol.Symbol)
|
|
{
|
|
if(del.TokenText == "=")
|
|
{
|
|
i++;
|
|
if(i<tokens.Count){
|
|
//must be creation of global variable
|
|
var res=ParseExpression(ref i,tokens);
|
|
VariableDeclarationNode decl=new VariableDeclarationNode();
|
|
decl.Column = type_token.ColumnNumber;
|
|
decl.FilePosition = type_token.OffsetInFile;
|
|
decl.Line = type_token.LineNumber;
|
|
decl.VariableType = type_token.TokenText;
|
|
decl.Name = name_token.TokenText;
|
|
decl.InitialValue = res;
|
|
return decl;
|
|
}
|
|
|
|
}else if(del.TokenText == ";")
|
|
{
|
|
//null global variable
|
|
VariableDeclarationNode decl=new VariableDeclarationNode();
|
|
decl.Column = type_token.ColumnNumber;
|
|
decl.FilePosition = type_token.OffsetInFile;
|
|
decl.Line = type_token.LineNumber;
|
|
decl.VariableType = type_token.TokenText;
|
|
decl.Name = name_token.TokenText;
|
|
decl.InitialValue=new UninitNode();
|
|
return decl;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
}
|
|
throw new Exception("Thrown");
|
|
}
|
|
|
|
private static void ParseFunction(ref int i, List<LexToken> tokens,FunctionNode node)
|
|
{
|
|
node.Body=ParseStatement(ref i,tokens);
|
|
}
|
|
|
|
private static Expression ParseExpression(ref int i, List<LexToken> tokens)
|
|
{
|
|
//Console.WriteLine(tokens[i].TokenText);
|
|
//an expression can be
|
|
|
|
//number
|
|
//char
|
|
//string
|
|
//function_call
|
|
//bool
|
|
//variable
|
|
|
|
//function call
|
|
//method_name(expression ...)
|
|
|
|
LexToken identifer = tokens[i];
|
|
|
|
if(identifer.Type == LexSymbol.Integer)
|
|
{
|
|
i++;
|
|
return new ConstInt(identifer.TokenText);
|
|
}
|
|
if(identifer.Type == LexSymbol.Float)
|
|
{
|
|
i++;
|
|
return new ConstFloat(identifer.TokenText);
|
|
}
|
|
if(identifer.Type == LexSymbol.Char)
|
|
{
|
|
i++;
|
|
return new ConstChar(identifer.TokenText);
|
|
}
|
|
if(identifer.Type == LexSymbol.String)
|
|
{
|
|
i++;
|
|
return new ConstString(identifer.TokenText);
|
|
}
|
|
|
|
//either function call or identifer
|
|
|
|
if(identifer.Type == LexSymbol.Identifier)
|
|
{
|
|
if(i+1<tokens.Count)
|
|
{
|
|
var parenToken = tokens[i+1];
|
|
|
|
if(parenToken.Type == LexSymbol.Symbol)
|
|
{
|
|
if(parenToken.TokenText == "(")
|
|
{
|
|
|
|
//function_call
|
|
List<Expression> args=new List<Expression>();
|
|
Console.WriteLine(tokens[i].TokenText);
|
|
|
|
i+=2;
|
|
for(;i<tokens.Count;i++)
|
|
{
|
|
var exp=ParseExpression(ref i,tokens);
|
|
Console.WriteLine(exp);
|
|
args.Add(exp);
|
|
if(i<tokens.Count)
|
|
{
|
|
var t = tokens[i];
|
|
if(t.Type == LexSymbol.Symbol)
|
|
{
|
|
if(t.TokenText == ",")
|
|
{
|
|
continue;
|
|
}
|
|
if(t.TokenText == ")") {break;}
|
|
}
|
|
}else{
|
|
//error
|
|
}
|
|
}
|
|
i++;
|
|
return new FunctionCallNode(identifer.TokenText,args);
|
|
}
|
|
if(parenToken.TokenText == "[")
|
|
{
|
|
Expression e=ParseExpression(ref i,tokens);
|
|
if(parenToken.Type == LexSymbol.Symbol && parenToken.TokenText == "]")
|
|
{
|
|
//we got array index
|
|
i++;
|
|
return new GetArrayValueNode(identifer.TokenText,e);
|
|
}
|
|
throw new Exception();
|
|
//array index;
|
|
}
|
|
else{
|
|
i++;
|
|
|
|
return new GetVariableValueNode(identifer.TokenText);
|
|
}
|
|
}else
|
|
{
|
|
//it is actually a variable
|
|
i++;
|
|
|
|
return new GetVariableValueNode(identifer.TokenText);
|
|
}
|
|
|
|
}else{
|
|
//eof
|
|
throw new Exception();
|
|
}
|
|
|
|
}
|
|
throw new Exception("Error");
|
|
}
|
|
|
|
}
|
|
|
|
public class WhileNode : Node
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return $"While Loop:\n{TabOver(Body.ToString())}";
|
|
}
|
|
public override NodeType Type {get {return NodeType.While;}}
|
|
public Node Body { get; set; }
|
|
}
|
|
|
|
public class BreakNode : Node
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return "break loop";
|
|
}
|
|
public override NodeType Type {get{return NodeType.Break;}}
|
|
}
|
|
|
|
|
|
internal class GetVariableValueNode : Expression
|
|
{
|
|
public string Name {get;set;}
|
|
public override NodeType Type {get{return NodeType.GetVariableValue;}}
|
|
public override string ToString()
|
|
{
|
|
return $"Get value from {Name}";
|
|
}
|
|
public GetVariableValueNode(string tokenText)
|
|
{
|
|
Name= tokenText;
|
|
}
|
|
}
|
|
|
|
public class GetArrayValueNode : Expression
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return $"GetArrayValue:\nName: {Name}\nIndex:\n{TabOver(Index.ToString())}";
|
|
}
|
|
public GetArrayValueNode(string name,Expression expression)
|
|
{
|
|
Name=name;
|
|
Index=expression;
|
|
}
|
|
public Expression Index {get;set;}
|
|
public string Name {get;set;}
|
|
public override NodeType Type {get{return NodeType.GetArrayValue;}}
|
|
}
|
|
public class ConstString : Expression
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return $"Const String: {Text}";
|
|
}
|
|
|
|
public ConstString(string tokenText)
|
|
{
|
|
Text = tokenText;
|
|
}
|
|
public string Text {get;set;}
|
|
public override NodeType Type {get{return NodeType.ConstString;}}
|
|
}
|
|
|
|
public class ConstChar : Expression
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return $"Const Char: {Text}";
|
|
}
|
|
public string Text {get;set;}
|
|
|
|
public override NodeType Type {get{return NodeType.ConstChar;}}
|
|
|
|
public ConstChar(string tokenText)
|
|
{
|
|
Text = tokenText;
|
|
}
|
|
}
|
|
public class FunctionCallNode : Expression
|
|
{
|
|
public override string ToString()
|
|
{
|
|
StringBuilder b=new StringBuilder();
|
|
int i=0;
|
|
foreach(var args in Arguments)
|
|
{
|
|
b.Append($"args[{i}]: {args}\n");
|
|
i++;
|
|
}
|
|
return $"Function Call: {Name}\n{TabOver(b.ToString())}";
|
|
}
|
|
public string Name {get;set;}
|
|
public List<Expression> Arguments {get;set;}
|
|
public override NodeType Type {get{return NodeType.CallMethod;}}
|
|
public FunctionCallNode(string tokenText, List<Expression> args)
|
|
{
|
|
Name = tokenText;
|
|
Arguments = args;
|
|
}
|
|
}
|
|
|
|
public class ConstFloat : Expression
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return $"Const Float: {Text}";
|
|
}
|
|
public override NodeType Type {get{return NodeType.ConstFloat;}}
|
|
public string Text {get;set;}
|
|
|
|
public ConstFloat(string tokenText)
|
|
{
|
|
Text = tokenText;
|
|
}
|
|
}
|
|
|
|
public class ConstInt : Expression
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return $"Const Int: {Text}";
|
|
}
|
|
public string Text {get;set;}
|
|
|
|
public ConstInt(string tokenText)
|
|
{
|
|
Text = tokenText;
|
|
}
|
|
public override NodeType Type {get{return NodeType.ConstInt;}}
|
|
}
|
|
|
|
public class ProgramNode : Node
|
|
{
|
|
public override NodeType Type {get {return NodeType.Program;}}
|
|
public ProgramNode()
|
|
{
|
|
Functions=new List<FunctionNode>();
|
|
Variables=new List<VariableDeclarationNode>();
|
|
}
|
|
public List<FunctionNode> Functions {get;set;}
|
|
|
|
public List<VariableDeclarationNode> Variables {get;set;}
|
|
|
|
public override string ToString()
|
|
{
|
|
StringBuilder funcs=new StringBuilder();
|
|
foreach(var args in Functions)
|
|
{
|
|
funcs.Append($"{args}\n");
|
|
|
|
}
|
|
StringBuilder vars = new StringBuilder();
|
|
foreach(var args in Variables)
|
|
{
|
|
vars.Append($"{args}\n");
|
|
|
|
}
|
|
return $"Program:\nFunctions:\n{TabOver(funcs.ToString())}\nGlobal Variables:\n{TabOver(vars.ToString())}";
|
|
}
|
|
}
|
|
public class VariableDeclarationNode : Node
|
|
{
|
|
public string Name {get;set;}="";
|
|
public string VariableType {get;set;}="";
|
|
public Expression InitialValue {get;set;}=new UninitNode();
|
|
public override NodeType Type {get{return NodeType.VariableCreate;}}
|
|
public override string ToString()
|
|
{
|
|
return $"Variable declared with name: {Name}\nType: {VariableType}\nInitial Value:\n{TabOver(InitialValue.ToString())}";
|
|
}
|
|
}
|
|
public class ArgumentNode : Node
|
|
{
|
|
public override NodeType Type {get {return NodeType.Arguments;}}
|
|
public string ArgType {get;set;}="";
|
|
public string Name {get;set;}="";
|
|
|
|
public override string ToString()
|
|
{
|
|
return $"ArgType: {ArgType}\nName: {Name}";
|
|
}
|
|
}
|
|
public class ScopeNode : Node
|
|
{
|
|
public List<Node> Nodes {get;set;}=new List<Node>();
|
|
public override NodeType Type {get{return NodeType.Scope;}}
|
|
|
|
public override string ToString()
|
|
{
|
|
StringBuilder b=new StringBuilder();
|
|
foreach(var node in Nodes)
|
|
{
|
|
b.Append(node.ToString()+"\n");
|
|
}
|
|
return $"Scope:\n{TabOver(b.ToString())}";
|
|
}
|
|
}
|
|
public class FunctionNode : Node
|
|
{
|
|
public override string ToString()
|
|
{
|
|
StringBuilder b=new StringBuilder();
|
|
|
|
int i=0;
|
|
foreach(var args in Arguments)
|
|
{
|
|
b.Append($"args[{i}]: {args}\n");
|
|
i++;
|
|
}
|
|
|
|
|
|
return $"Function: {Name}\nReturn Type: {ReturnType}\nArguments:\n{TabOver(b.ToString())}\nBody:{TabOver(Body.ToString())}";
|
|
}
|
|
public string ReturnType {get;set;}="";
|
|
public override NodeType Type {get {return NodeType.Function;}}
|
|
public string Name {get;set;}="";
|
|
|
|
public Node Body {get;set;}
|
|
|
|
public List<ArgumentNode> Arguments {get;set;}=new List<ArgumentNode>();
|
|
}
|
|
public enum NodeType
|
|
{
|
|
|
|
ConstInt=0,
|
|
ConstFloat=1,
|
|
ConstString=2,
|
|
ConstChar=3,
|
|
|
|
VariableCreate=4,
|
|
GetVariableValue=5,
|
|
SetVariableValue=6,
|
|
|
|
CallMethod=7,
|
|
|
|
Return=8,
|
|
|
|
Function=9,
|
|
Program=10,
|
|
|
|
Arguments=11,
|
|
Uninit=12,
|
|
|
|
GetArrayValue=13,
|
|
Scope=14,
|
|
If=15,
|
|
Else=16,
|
|
While=17,
|
|
Break=18,
|
|
|
|
}
|
|
public abstract class Expression : Node
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return $"Expression {base.ToString()}";
|
|
}
|
|
}
|
|
public class UninitNode : Expression
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return $"Uninitilized {base.ToString()}";
|
|
}
|
|
public override NodeType Type {get {return NodeType.Uninit;}}
|
|
}
|
|
public abstract class Node
|
|
{
|
|
public override string ToString()
|
|
{
|
|
return $"Node:\nFilePosition: {FilePosition}\nColumn: {Column}\nLine: {Line}";
|
|
}
|
|
protected string TabOver(string text)
|
|
{
|
|
StringBuilder b=new StringBuilder();
|
|
foreach(var txt in text.Split('\n'))
|
|
{
|
|
b.Append($"\t{txt}\n");
|
|
}
|
|
return b.ToString();
|
|
}
|
|
public abstract NodeType Type {get;}
|
|
public int FilePosition {get;set;}
|
|
public int Column {get;set;}
|
|
|
|
public int Line {get;set;}
|
|
}
|
|
|
|
|
|
} |