using System;
using System.Collections.Generic;
namespace Anonymus
{
public class MacroArgPlugIn : ExprPlugIn
{
public List<string> Parameters;
public MacroArgPlugIn(IdContainer Container, List<string> Parameters)
: base(Container)
{
this.Parameters = Parameters;
}
public override PlugInRes NewNode(ref ExpressionNode Node)
{
if (Node is StrExpressionNode)
{
var StrNode = Node as StrExpressionNode;
if (Parameters != null)
{
var Index = Parameters.IndexOf(Node.Code.String);
if (Index != -1)
{
Node = Root.NewNode(new MacroArgNode(Index, Node.Code));
return PlugInRes.Ready;
}
}
}
return PlugInRes.Succeeded;
}
}
public class DefinePlugIn : MultiPlugIn
{
public DefinePlugIn(IdContainer Container, List<string> Parameters)
: base(Container)
{
AddPlugIn(new MacroArgPlugIn(Container, Parameters));
AddPlugIn(new PreProcPlugIn(Container, false));
AddPlugIn(new CalcPlugIn(Container, false));
}
}
public class PreProcPlugIn : ExprPlugIn
{
public bool IfDef;
public PreProcPlugIn(IdContainer Container, bool IfDef)
: base(Container)
{
this.IfDef = IfDef;
}
public static IEnumerable<ExpressionNode> GetParams(OpExpressionNode Node)
{
for (int i = 1; i < Node.Children.Count; i++)
yield return Node.Children[i];
}
public bool ChkMacroNodes(ExpressionNode Node)
{
var RetValue = true;
var ONode = Node as OpExpressionNode;
IEnumerable<ExpressionNode> Children = null;
if (ONode != null && ONode.Operator == Operator.Call) Children = GetParams(ONode);
else Children = Node.EnumChildren;
foreach (var e in Children)
if (e is MacroExpressionNode)
{
var m = e as MacroExpressionNode;
if (m.Macro.Parameters.Count != 0)
{
State.Messages.Add(MessageId.ParamCount, m.Code);
RetValue = false;
}
}
return RetValue;
}
public override PlugInRes NewNode(ref ExpressionNode Node)
{
if (Node is StrExpressionNode)
{
var IdNode = Node as StrExpressionNode;
var Macro = PreState.GetMacro(IdNode.Code.String);
if (IfDef)
{
Node = Root.NewNode(Expressions.GetBoolValue(Container, Macro != null, Node.Code));
if (Node == null) return PlugInRes.Failed;
return PlugInRes.Ready;
}
else if (Macro != null)
{
if (Macro.Value == null)
{
State.Messages.Add(MessageId.MacroWithoutValue, Node.Code);
return PlugInRes.Failed;
}
Macro.Used = true;
var Params = Macro.Parameters;
if (Params == null || Params.Count == 0)
Node = Macro.Value.Copy(State, PlugIn: Root, Begin: false, End: false);
else Node = Root.NewNode(new MacroExpressionNode(Macro, Node.Code));
if (Node == null) return PlugInRes.Failed;
return PlugInRes.Ready;
}
}
if (PreState == null) return PlugInRes.Succeeded;
if (Node is OpExpressionNode)
{
var OpNode = Node as OpExpressionNode;
var Ch = OpNode.Children;
var Op = OpNode.Operator;
if (Op == Operator.Call && Ch[0] is MacroExpressionNode)
{
var MFunc = Ch[0] as MacroExpressionNode;
var Macro = MFunc.Macro;
Macro.Used = true;
if (Ch.Count != Macro.Parameters.Count + 1)
{
State.Messages.Add(MessageId.ParamCount, Node.Code);
return PlugInRes.Failed;
}
var Nodes = new List<ExpressionNode>();
var LnkNodes = (List<LinkedExprNode>)null;
for (var i = 1; i < Ch.Count; i++)
{
if (Ch[i] is IdExpressionNode || Ch[i] is ConstExpressionNode)
{
Nodes.Add(Ch[i]);
}
else
{
if (LnkNodes == null)
LnkNodes = new List<LinkedExprNode>();
var N = new LinkedExprNode(Ch[i]);
LnkNodes.Add(N);
Nodes.Add(new LinkingNode(N, Node.Code));
}
}
ReplaceNodeFunc RFunc = (ChNode) =>
{
if (ChNode is MacroArgNode)
{
var ArgIndex = (ChNode as MacroArgNode).Index;
return Nodes[ArgIndex].Copy(State);
}
return ChNode;
};
Node = Macro.Value.Copy(State, Node.Code, null, RFunc, Begin: false, End: false);
if (Node == null) return PlugInRes.Failed;
Node.LinkedNodes = LnkNodes;
Node = Node.CallNewNode(Root, false, false, false);
return PlugInRes.Ready;
}
}
if (!ChkMacroNodes(Node)) return PlugInRes.Failed;
else return PlugInRes.Succeeded;
}
public override PlugInRes End(ref ExpressionNode Node)
{
if (Node != null && Node is MacroExpressionNode)
{
var MNode = Node as MacroExpressionNode;
State.Messages.Add(MessageId.ParamCount, MNode.Code);
return PlugInRes.Failed;
}
return PlugInRes.Succeeded;
}
}
public class Macro
{
public PString Name;
public ExpressionNode Value;
public List<string> Parameters;
public bool Used = false;
public Macro(PString Name, ExpressionNode Value, List<string> Parameters)
{
this.Name = Name;
this.Value = Value;
this.Parameters = Parameters;
}
}
public class PreprocessorState
{
public MessageCollector Messages;
public List<Macro> Macroes = new List<Macro>();
public List<bool> Conditions = new List<bool>();
public PreprocessorState(MessageCollector Messages)
{
this.Messages = Messages;
}
public void ChkUnusedMacros()
{
foreach (var e in Macroes)
if (!e.Used) Messages.Add(MessageId.UnusedId, e.Name);
}
public void Redefine(Macro m)
{
RemoveMacro(m.Name.String);
Macroes.Add(m);
}
public bool IsDefined(string name)
{
foreach (var e in Macroes)
if (e.Name.String == name) return true;
return false;
}
public Macro GetMacro(string name)
{
foreach (var e in Macroes)
if (e.Name.String == name) return e;
return null;
}
public bool RemoveMacro(string name)
{
int Pos = -1;
int Count = Macroes.Count;
for (int i = 0; i < Count; i++)
{
if (Macroes[i].Name.String == name)
{
Pos = i;
break;
}
}
if (Pos != -1)
{
Macroes.RemoveAt(Pos);
return true;
}
return false;
}
public bool IsInDefBlock()
{
foreach (var e in Conditions)
if (!e) return false;
return true;
}
public void AddCondition(bool Value)
{
Conditions.Add(Value);
}
public bool DoElse()
{
int l = Conditions.Count - 1;
if (l < 0) return false;
Conditions[l] = !Conditions[l];
return true;
}
public bool DoEndif()
{
int l = Conditions.Count - 1;
if (l < 0) return false;
Conditions.RemoveAt(l);
return true;
}
public PreprocessorState NewState()
{
var Ret = new PreprocessorState(Messages);
foreach (var e in Macroes)
Ret.Macroes.Add(e);
return Ret;
}
public bool CheckConditions()
{
if (Conditions.Count > 0)
{
Messages.Add(MessageId.NoEndif);
return false;
}
return true;
}
}
public enum PreprocessorRes
{
Succeed,
Failed,
NotPreprocLine,
}
public class Preprocessor
{
public PreprocessorState PreState;
public CompilerState State;
public IdContainer Container;
public Preprocessor(ScopeNode Container)
{
this.PreState = Container.PreState;
this.State = Container.State;
this.Container = Container;
}
Macro ProcMacro(PString MLine)
{
var MacroName = MLine.Word();
if (!Helper.IsValidIdentifierName(MacroName.String))
{
PreState.Messages.Add(MessageId.NotValidName, MacroName);
return null;
}
PString Params = null;
var zp = BracketHelper.ZPos(MLine.String);
if (zp > 0)
{
Params = MLine.Substring(1, zp - 1);
MLine = MLine.Substring(zp + 1).Trim();
}
List<string> ParamList = null;
if (Params != null)
{
var PStrList = new List<PString>();
var Recognizer = State.Language.ArgRecognizer;
if (!Recognizer.SplitArgs(State, Params, PStrList))
return null;
ParamList = new List<string>();
foreach (var e in PStrList)
{
if (!Helper.IsValidIdentifierName(e.String))
{
PreState.Messages.Add(MessageId.WrongParamList, e);
return null;
}
if (ParamList.Contains(e.String))
{
PreState.Messages.Add(MessageId.IdAlreadyDefined, e);
return null;
}
ParamList.Add(e.String);
}
}
MLine.TrimThis();
if (MLine.StrLen > 0)
{
var PlugIn = new DefinePlugIn(Container, ParamList);
var Node = Expressions.NewExpressionTree(MLine, PlugIn, true);
if (Node != null)
return new Macro(MacroName, Node, ParamList);
else return null;
}
return new Macro(MacroName, null, ParamList);
}
private PreprocessorRes ProcCommands(string Order, PString Line, PString MLine)
{
PString Name;
var ReDef = false;
switch (Order)
{
case "error":
PreState.Messages.Add(MessageId.PreprocError, MLine);
return PreprocessorRes.Failed;
case "warning":
PreState.Messages.Add(MessageId.PreprocWarning, MLine);
return PreprocessorRes.Succeed;
case "info":
PreState.Messages.Add(MessageId.PreprocInfo, MLine);
return PreprocessorRes.Succeed;
case "redef":
ReDef = true;
goto case "define";
case "define":
var Macro = ProcMacro(MLine);
if (Macro != null)
{
Name = Macro.Name;
if (!ReDef && PreState.IsDefined(Name.String))
{
PreState.Messages.Add(MessageId.MacroAlreadyDefined, Name);
return PreprocessorRes.Failed;
}
PreState.Redefine(Macro);
return PreprocessorRes.Succeed;
}
else
{
return PreprocessorRes.Failed;
}
case "undef":
Name = MLine.Word();
if (!Helper.IsValidIdentifierName(Name.String))
{
PreState.Messages.Add(MessageId.NotValidName, Name);
return PreprocessorRes.Failed;
}
if (!PreState.RemoveMacro(Name.String))
{
PreState.Messages.Add(MessageId.NoMacro, Name);
return PreprocessorRes.Failed;
}
if (MLine.String != "") PreState.Messages.Add(MessageId.ParamCount, MLine);
return PreprocessorRes.Succeed;
default:
PreState.Messages.Add(MessageId.UnknownCommand, Line);
return PreprocessorRes.Failed;
}
}
public PreprocessorRes PreprocessLine(PString Line)
{
ExpressionNode Node;
var InDefBlock = PreState.IsInDefBlock();
Line = Line.Trim();
if (Line.String[0] == '#')
{
var MLine = Line.TrimmedSubstring(1);
var Order = MLine.Word();
bool IfDef = false;
switch (Order.String)
{
case "ifdef":
IfDef = true;
goto case "if";
case "if":
if (InDefBlock)
{
var PlugIn = new GlobalPlugIn(Container);
PlugIn.GetPlugIn<TypeMgrPlugIn>().RetType = Container.GlobalScope.BoolType;
PlugIn.GetPlugIn<PreProcPlugIn>().IfDef = IfDef;
PlugIn.GetPlugIn<CalcPlugIn>().Const = true;
Node = Expressions.NewExpressionTree(MLine, PlugIn, true);
if (Node != null)
{
var CNode = Node as ConstExpressionNode;
PreState.AddCondition(CNode.Bool);
return PreprocessorRes.Succeed;
}
else
{
PreState.AddCondition(true);
return PreprocessorRes.Failed;
}
}
PreState.AddCondition(true);
break;
case "elif":
if (!PreState.DoElse())
{
PreState.Messages.Add(MessageId.NoMatchingIf, Line);
return PreprocessorRes.Failed;
}
goto case "if";
case "else":
if (!PreState.DoElse())
{
PreState.Messages.Add(MessageId.NoMatchingIf, Line);
return PreprocessorRes.Failed;
}
break;
case "endif":
if (!PreState.DoEndif())
{
PreState.Messages.Add(MessageId.NoMatchingIf, Line);
return PreprocessorRes.Failed;
}
break;
default:
if (InDefBlock) return ProcCommands(Order.String, Line, MLine);
State.Messages.Add(MessageId.UnknownCommand, Line);
return PreprocessorRes.Failed;
}
return PreprocessorRes.Succeed;
}
return InDefBlock ? PreprocessorRes.NotPreprocLine : PreprocessorRes.Succeed;
}
}
}