558 lines
23 KiB
C#
558 lines
23 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace CompilerVVM
|
|
{
|
|
public class Parser
|
|
{
|
|
int pointer;
|
|
IList<object> Tokens;
|
|
int idvar;
|
|
Dictionary<string, string> Constants;
|
|
Dictionary<string, MethodAtom> Methods;
|
|
int jumpid;
|
|
|
|
public Parser(IList<object> tokens, Dictionary<string, string> TextConst, Dictionary<string, MethodAtom> Methods)
|
|
{
|
|
Tokens = tokens;
|
|
pointer = 0;
|
|
jumpid = 0;
|
|
Constants = TextConst;
|
|
this.Methods = Methods;
|
|
ParseMethod();
|
|
}
|
|
|
|
private void ParseMethod()
|
|
{
|
|
//<program> := <method>+
|
|
while (pointer != Tokens.Count)
|
|
{
|
|
//<method> := method <type_method> <ident>(<param>*) <block>
|
|
//<type> := int | double | string
|
|
//<type_method> := <type> | void
|
|
MethodAtom method = null;
|
|
if (!Tokens[pointer++].Equals("method"))
|
|
throw new ParserException("No method detected");
|
|
else
|
|
{
|
|
method = Methods[Tokens[++pointer].ToString()];
|
|
idvar = method.NumOfParams;
|
|
method.Code.Add(String.Format(".proc {0}", method.Name, method.NumOfParams));
|
|
|
|
pointer += 3 + (2 * method.NumOfParams);
|
|
if (Tokens[pointer].Equals(OP.OpenBlock))
|
|
{
|
|
pointer++;
|
|
ParseBlock(method);
|
|
}
|
|
else throw new ParserException("No code block in method: " + method.Name);
|
|
|
|
if (method.Name == "main")
|
|
method.Code.Add("STOP");
|
|
else if (method.Type == "void")
|
|
method.Code.Add("RETURN");
|
|
else
|
|
{
|
|
if (method.Code[method.Code.Count - 1] != "RETURN")
|
|
throw new ParserException("No return sequence in method:" + method.Name);
|
|
}
|
|
method.Code.Add(".endp");
|
|
|
|
method.NumOfLocals = method.Variables.Count;
|
|
method.Code[0] = String.Format(".proc {0} {1} {2}", method.Name, method.NumOfLocals, method.NumOfParams);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private void ParseBlock(MethodAtom method)
|
|
{
|
|
while (!Tokens[pointer].Equals(OP.CloseBlock))
|
|
{
|
|
if (Tokens[pointer].Equals(OP.Semicolon))
|
|
{
|
|
pointer++;
|
|
}
|
|
else if (code_types.Contains(Tokens[pointer].ToString()))
|
|
DeclareVar(method);
|
|
else if (Tokens[pointer + 1].Equals(OP.Assigment))
|
|
{
|
|
List<Variable> NeededVars = method.Variables.FindAll(x => x.Name == Tokens[pointer].ToString());
|
|
if (NeededVars.Count == 0)
|
|
throw new ParserException(string.Format("Variable {0} is not defined", Tokens[pointer].ToString()));
|
|
else
|
|
{
|
|
pointer += 2;
|
|
ParseExpression(method, NeededVars[NeededVars.Count - 1].Type);
|
|
switch (NeededVars[NeededVars.Count - 1].Type)
|
|
{
|
|
case "int":
|
|
method.Code.Add(string.Format("STOREIVAR {0}", NeededVars[NeededVars.Count - 1].ID));
|
|
break;
|
|
case "double":
|
|
method.Code.Add(string.Format("STOREDVAR {0}", NeededVars[NeededVars.Count - 1].ID));
|
|
break;
|
|
case "string":
|
|
method.Code.Add(string.Format("STORESVAR {0}", NeededVars[NeededVars.Count - 1].ID));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (Tokens[pointer].ToString() == "return")
|
|
{
|
|
ParseSingleExpr(method, pointer + 1, method.Type);
|
|
method.Code.Add("RETURN");
|
|
pointer += 3;
|
|
}
|
|
else if (Tokens[pointer].ToString() == "if")
|
|
{
|
|
if (!Tokens[pointer + 1].Equals(OP.OpenParam) || !Tokens[pointer + 5].Equals(OP.CloseParam))
|
|
throw new ParserException("Wrong if statement");
|
|
string var1 = GetTypeOfVar(method, pointer + 2);
|
|
string var2 = GetTypeOfVar(method, pointer + 4);
|
|
if (var1 != var2)
|
|
throw new ParserException("Incompatible variable types in if statement");
|
|
int jump = jumpid++;
|
|
ParseSingleExpr(method, pointer + 4, var2);
|
|
ParseSingleExpr(method, pointer + 2, var1);
|
|
|
|
switch ((OP)Tokens[pointer + 3])
|
|
{
|
|
case OP.Equal:
|
|
method.Code.Add(string.Format("IFICMPNE jump_{0}", jump));
|
|
break;
|
|
case OP.NotEqual:
|
|
method.Code.Add(string.Format("IFICMPE jump_{0}", jump));
|
|
break;
|
|
case OP.Greater:
|
|
method.Code.Add(string.Format("IFICMPLE jump_{0}", jump));
|
|
break;
|
|
case OP.GreaterEqual:
|
|
method.Code.Add(string.Format("IFICMPL jump_{0}", jump));
|
|
break;
|
|
case OP.Less:
|
|
method.Code.Add(string.Format("IFICMPGE jump_{0}", jump));
|
|
break;
|
|
case OP.LessEqual:
|
|
method.Code.Add(string.Format("IFICMPG jump_{0}", jump));
|
|
break;
|
|
}
|
|
method.Code.Add("POP");
|
|
method.Code.Add("POP");
|
|
if (!Tokens[pointer + 6].Equals(OP.OpenBlock))
|
|
throw new ParserException("No { code } in if statement");
|
|
pointer += 7;
|
|
ParseBlock(method);
|
|
if (Tokens[pointer].ToString() != "else")
|
|
{
|
|
method.Code.Add(string.Format("jump_{0}:", jump));
|
|
method.Code.Add("POP");
|
|
method.Code.Add("POP");
|
|
}
|
|
else
|
|
{
|
|
int jump2 = jumpid++;
|
|
method.Code.Add(string.Format("JA jump_{0}", jump2));
|
|
if (!Tokens[pointer + 1].Equals(OP.OpenBlock))
|
|
throw new ParserException("No { code } in else statement");
|
|
method.Code.Add(string.Format("jump_{0}:", jump));
|
|
method.Code.Add("POP");
|
|
method.Code.Add("POP");
|
|
pointer += 2;
|
|
ParseBlock(method);
|
|
method.Code.Add(string.Format("jump_{0}:", jump2));
|
|
}
|
|
}
|
|
else if (Tokens[pointer].ToString() == "while")
|
|
{
|
|
if (!Tokens[pointer + 1].Equals(OP.OpenParam) || !Tokens[pointer + 5].Equals(OP.CloseParam))
|
|
throw new ParserException("Wrong while statement");
|
|
string var1 = GetTypeOfVar(method, pointer + 2);
|
|
string var2 = GetTypeOfVar(method, pointer + 4);
|
|
if (var1 != var2)
|
|
throw new ParserException("Incompatible variable types in while statement");
|
|
int jump = jumpid++;
|
|
int jump2 = jumpid++;
|
|
|
|
method.Code.Add(string.Format("jump_{0}:", jump));
|
|
ParseSingleExpr(method, pointer + 4, var2);
|
|
ParseSingleExpr(method, pointer + 2, var1);
|
|
|
|
switch ((OP)Tokens[pointer + 3])
|
|
{
|
|
case OP.Equal:
|
|
method.Code.Add(string.Format("IFICMPNE jump_{0}", jump2));
|
|
break;
|
|
case OP.NotEqual:
|
|
method.Code.Add(string.Format("IFICMPE jump_{0}", jump2));
|
|
break;
|
|
case OP.Greater:
|
|
method.Code.Add(string.Format("IFICMPLE jump_{0}", jump2));
|
|
break;
|
|
case OP.GreaterEqual:
|
|
method.Code.Add(string.Format("IFICMPL jump_{0}", jump2));
|
|
break;
|
|
case OP.Less:
|
|
method.Code.Add(string.Format("IFICMPGE jump_{0}", jump2));
|
|
break;
|
|
case OP.LessEqual:
|
|
method.Code.Add(string.Format("IFICMPG jump_{0}", jump2));
|
|
break;
|
|
}
|
|
method.Code.Add("POP");
|
|
method.Code.Add("POP");
|
|
if (!Tokens[pointer + 6].Equals(OP.OpenBlock))
|
|
throw new ParserException("No { code } in while statement");
|
|
pointer += 7;
|
|
ParseBlock(method);
|
|
method.Code.Add(string.Format("JA jump_{0}", jump));
|
|
method.Code.Add(string.Format("jump_{0}:", jump2));
|
|
}
|
|
else if (Tokens[pointer].ToString() == "do")
|
|
{
|
|
int jump = jumpid++;
|
|
int jump2 = jumpid++;
|
|
method.Code.Add(string.Format("JA jump_{0}", jump2));
|
|
method.Code.Add(string.Format("jump_{0}:", jump));
|
|
method.Code.Add("POP");
|
|
method.Code.Add("POP");
|
|
method.Code.Add(string.Format("jump_{0}:", jump2));
|
|
pointer += 2;
|
|
ParseBlock(method);
|
|
|
|
if (!Tokens[pointer + 1].Equals(OP.OpenParam) || !Tokens[pointer + 5].Equals(OP.CloseParam) ||
|
|
Tokens[pointer].ToString() != "until")
|
|
throw new ParserException("Wrong until statement");
|
|
string var1 = GetTypeOfVar(method, pointer + 2);
|
|
string var2 = GetTypeOfVar(method, pointer + 4);
|
|
if (var1 != var2)
|
|
throw new Exception("Incompatible variable types in until statement");
|
|
|
|
ParseSingleExpr(method, pointer + 4, var2);
|
|
ParseSingleExpr(method, pointer + 2, var1);
|
|
|
|
switch ((OP)Tokens[pointer + 3])
|
|
{
|
|
case OP.Equal:
|
|
method.Code.Add(string.Format("IFICMPNE jump_{0}", jump));
|
|
break;
|
|
case OP.NotEqual:
|
|
method.Code.Add(string.Format("IFICMPE jump_{0}", jump));
|
|
break;
|
|
case OP.Greater:
|
|
method.Code.Add(string.Format("IFICMPLE jump_{0}", jump));
|
|
break;
|
|
case OP.GreaterEqual:
|
|
method.Code.Add(string.Format("IFICMPL jump_{0}", jump));
|
|
break;
|
|
case OP.Less:
|
|
method.Code.Add(string.Format("IFICMPGE jump_{0}", jump));
|
|
break;
|
|
case OP.LessEqual:
|
|
method.Code.Add(string.Format("IFICMPG jump_{0}", jump));
|
|
break;
|
|
}
|
|
method.Code.Add("POP");
|
|
method.Code.Add("POP");
|
|
pointer += 7;
|
|
}
|
|
else if (Tokens[pointer].ToString() == "print")
|
|
{
|
|
pointer += 2;
|
|
string type = GetTypeOfVar(method, pointer);
|
|
ParseSingleExpr(method, pointer, type);
|
|
pointer += 3;
|
|
switch (type)
|
|
{
|
|
case "int":
|
|
method.Code.Add("IPRINT");
|
|
break;
|
|
case "double":
|
|
method.Code.Add("DPRINT");
|
|
break;
|
|
case "string":
|
|
method.Code.Add("SPRINT");
|
|
break;
|
|
}
|
|
}
|
|
else if (Tokens[pointer+1].Equals(OP.OpenParam))
|
|
{
|
|
ParseCall(method);
|
|
}
|
|
}
|
|
pointer++;
|
|
}
|
|
private void ParseExpression(MethodAtom method, string type)
|
|
{
|
|
if (Tokens[pointer + 1].Equals(OP.Semicolon))
|
|
{
|
|
ParseSingleExpr(method, pointer, type);
|
|
pointer += 2;
|
|
}
|
|
else if (Tokens[pointer + 1].Equals(OP.OpenParam))
|
|
{
|
|
ParseCall(method, type);
|
|
}
|
|
else if (Tokens[pointer].Equals(OP.Sub))
|
|
{
|
|
ParseSingleExpr(method, pointer + 1, type);
|
|
switch (type)
|
|
{
|
|
case "int":
|
|
method.Code.Add("INEG");
|
|
break;
|
|
case "double":
|
|
method.Code.Add("DNEG");
|
|
break;
|
|
default:
|
|
throw new ParserException("Incompatible types");
|
|
}
|
|
}
|
|
else if (Tokens[pointer + 1].Equals(OP.Add))
|
|
{
|
|
int prevpointer = pointer;
|
|
pointer += 2;
|
|
|
|
ParseExpression(method, type);
|
|
ParseSingleExpr(method, prevpointer, type);
|
|
|
|
switch (type)
|
|
{
|
|
case "int":
|
|
method.Code.Add("IADD");
|
|
break;
|
|
case "double":
|
|
method.Code.Add("DADD");
|
|
break;
|
|
default:
|
|
throw new ParserException("Incompatible types");
|
|
}
|
|
}
|
|
else if (Tokens[pointer + 1].Equals(OP.Sub))
|
|
{
|
|
int prevpointer = pointer;
|
|
pointer += 2;
|
|
|
|
ParseExpression(method, type);
|
|
ParseSingleExpr(method, prevpointer, type);
|
|
|
|
switch (type)
|
|
{
|
|
case "int":
|
|
method.Code.Add("ISUB");
|
|
break;
|
|
case "double":
|
|
method.Code.Add("DSUB");
|
|
break;
|
|
default:
|
|
throw new ParserException("Incompatible types");
|
|
}
|
|
}
|
|
else if (Tokens[pointer + 1].Equals(OP.Mul))
|
|
{
|
|
int prevpointer = pointer;
|
|
pointer += 2;
|
|
|
|
ParseExpression(method, type);
|
|
ParseSingleExpr(method, prevpointer, type);
|
|
|
|
switch (type)
|
|
{
|
|
case "int":
|
|
method.Code.Add("IMUL");
|
|
break;
|
|
case "double":
|
|
method.Code.Add("DMUL");
|
|
break;
|
|
default:
|
|
throw new ParserException("Incompatible types");
|
|
}
|
|
}
|
|
else if (Tokens[pointer + 1].Equals(OP.Div))
|
|
{
|
|
int prevpointer = pointer;
|
|
pointer += 2;
|
|
|
|
ParseExpression(method, type);
|
|
ParseSingleExpr(method, prevpointer, type);
|
|
|
|
switch (type)
|
|
{
|
|
case "int":
|
|
method.Code.Add("IDIV");
|
|
break;
|
|
case "double":
|
|
method.Code.Add("DDIV");
|
|
break;
|
|
default:
|
|
throw new ParserException("Incompatible types");
|
|
}
|
|
}
|
|
|
|
else if (Tokens[pointer + 1].Equals(OP.Mod))
|
|
{
|
|
int prevpointer = pointer;
|
|
pointer += 2;
|
|
|
|
ParseExpression(method, type);
|
|
ParseSingleExpr(method, prevpointer, type);
|
|
|
|
switch (type)
|
|
{
|
|
case "int":
|
|
method.Code.Add("IMOD");
|
|
break;
|
|
default:
|
|
throw new ParserException("Incompatible types");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ParseCall(MethodAtom method, string type = null)
|
|
{
|
|
if (Methods.ContainsKey(Tokens[pointer].ToString()))
|
|
{
|
|
MethodAtom CallMethod = Methods[Tokens[pointer].ToString()];
|
|
if (type != null && type != CallMethod.Type)
|
|
throw new ParserException("Incompatible types when call method" + CallMethod.Name);
|
|
|
|
pointer += 2;
|
|
List<object> param = new List<object>();
|
|
while (!Tokens[pointer++].Equals(OP.CloseParam))
|
|
{
|
|
param.Add(Tokens[pointer]);
|
|
}
|
|
|
|
if (param.Count != CallMethod.NumOfParams)
|
|
throw new ParserException("Wrong params when call method" + CallMethod.Name);
|
|
|
|
for (int i = 0; i < param.Count; i++)
|
|
{
|
|
ParseSingleExpr(method, pointer - 2 - i, CallMethod.Variables[i].Type);
|
|
}
|
|
|
|
method.Code.Add(string.Format("CALL {0}", CallMethod.Name));
|
|
|
|
}
|
|
else throw new ParserException("Undefined method to call");
|
|
}
|
|
|
|
private void ParseSingleExpr(MethodAtom method, int pointer, string type)
|
|
{
|
|
if (Tokens[pointer] is StringBuilder)
|
|
{
|
|
if (type == "string")
|
|
method.Code.Add(string.Format("SLOAD {0}", Constants[Tokens[pointer++].ToString()]));
|
|
else
|
|
throw new ParserException("Incompatible type");
|
|
}
|
|
else if (Tokens[pointer] is Number)
|
|
{
|
|
switch (type)
|
|
{
|
|
case "int":
|
|
if (!Tokens[pointer].ToString().Contains("."))
|
|
{
|
|
method.Code.Add(string.Format("ILOAD {0}", Tokens[pointer++].ToString()));
|
|
break;
|
|
}
|
|
else throw new ParserException("Incompatible type");
|
|
|
|
case "double":
|
|
method.Code.Add(string.Format("DLOAD {0}", Tokens[pointer++].ToString()));
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
List<Variable> NeededVars = method.Variables.FindAll(x => x.Name == Tokens[pointer].ToString());
|
|
if (NeededVars.Count != 0)
|
|
{
|
|
if (NeededVars[NeededVars.Count - 1].Type != type)
|
|
throw new ParserException("Incompatible type");
|
|
else
|
|
{
|
|
switch (type)
|
|
{
|
|
case "int":
|
|
method.Code.Add(string.Format("LOADIVAR {0}", NeededVars[NeededVars.Count - 1].ID));
|
|
break;
|
|
case "double":
|
|
method.Code.Add(string.Format("LOADDVAR {0}", NeededVars[NeededVars.Count - 1].ID));
|
|
break;
|
|
case "string":
|
|
method.Code.Add(string.Format("LOADSVAR {0}", NeededVars[NeededVars.Count - 1].ID));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else throw new ParserException("Can't parse sequence");
|
|
}
|
|
}
|
|
|
|
private void DeclareVar(MethodAtom method)
|
|
{
|
|
method.Variables.Add(new Variable(idvar++, Tokens[pointer].ToString(), Tokens[pointer + 1].ToString()));
|
|
if (Tokens[pointer + 2].Equals(OP.Semicolon))
|
|
{
|
|
pointer += 3; return;
|
|
}
|
|
else if (Tokens[pointer + 2].Equals(OP.Assigment))
|
|
{
|
|
pointer += 3;
|
|
ParseExpression(method, method.Variables[idvar - 1].Type);
|
|
switch (method.Variables[idvar - 1].Type)
|
|
{
|
|
case "int":
|
|
method.Code.Add(string.Format("STOREIVAR {0}", idvar - 1));
|
|
break;
|
|
|
|
case "double":
|
|
method.Code.Add(string.Format("STOREDVAR {0}", idvar - 1));
|
|
break;
|
|
|
|
case "string":
|
|
method.Code.Add(string.Format("STORESVAR {0}", idvar - 1));
|
|
break;
|
|
}
|
|
|
|
if (Tokens[pointer].Equals(OP.Semicolon))
|
|
{
|
|
pointer++; return;
|
|
}
|
|
}
|
|
else
|
|
throw new ParserException("Wrong variable defenition");
|
|
}
|
|
|
|
private string GetTypeOfVar(MethodAtom method, int pointer)
|
|
{
|
|
string result = null;
|
|
if (Tokens[pointer] is StringBuilder)
|
|
result = "string";
|
|
else if (Tokens[pointer] is Number)
|
|
{
|
|
if (Tokens[pointer].ToString().Contains("."))
|
|
result = "double";
|
|
else result = "int";
|
|
}
|
|
else if (Methods.ContainsKey(Tokens[pointer].ToString()))
|
|
{
|
|
result = Methods[Tokens[pointer].ToString()].Type;
|
|
}
|
|
else
|
|
{
|
|
List<Variable> isVariable = method.Variables.FindAll(x => x.Name == Tokens[pointer].ToString());
|
|
if (isVariable.Count == 0)
|
|
throw new ParserException("No defined variable with name "+Tokens[pointer].ToString());
|
|
else result = isVariable[isVariable.Count - 1].Type;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
List<string> code_types = new List<string>() { "int", "double", "string" };
|
|
}
|
|
}
|