Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / ASM

Bird Programming Language: Part 3

Rate me:
Please Sign up or sign in to vote.
4.88/5 (5 votes)
1 Jan 2013GPL35 min read 29.5K   282   14  
A new general purpose language that aims to be fast, high level and simple to use.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Linq;

namespace Bird
{
	[Flags]
	public enum CommandFlags : byte
	{
		None = 0,
		Unreachable = 1,
		Breakable = 2,
		Continueable = 4,
		TryHasCatchVariable = 8,
		CatchesAllException = 16
	}

	public enum CommandType : byte
	{
		Unknown,
		Expression,
		Cycle,
		While,
		DoWhile,
		For,
		If,
		Try,

		Return,
		Break,
		Continue,
		Goto,
		Label,
		Throw,
		Rethrow,
	}

	public static class Commands
	{
		public static bool IsJumpCommand(CommandType Type)
		{
			return Type == CommandType.Return || Type == CommandType.Break ||
				Type == CommandType.Continue || Type == CommandType.Goto;
		}

		public static bool IsLoopCommand(CommandType Type)
		{
			return Type == CommandType.Cycle || Type == CommandType.While ||
				Type == CommandType.DoWhile || Type == CommandType.For;
		}

		public static bool IsBreakableCommand(CommandType Type)
		{
			return IsLoopCommand(Type);
		}

		public static bool CreateCatchVariable(Command TryComm,
			Identifier Type = null, CodeString Name = new CodeString())
		{
			var State = TryComm.State;
			if (!Name.IsValid)
				Name = State.AutoVarName;

			if (Type == null)
			{
				Type = Identifiers.GetByFullNameFast<ClassType>(State, "System.Exception");
				if (Type == null) throw new ApplicationException();
			}

			var Var = TryComm.CatchScope.CreateVariable(Name, Type);
			if (Var == null) return false;

			if (!TryComm.CatchScope.CanIdDeclared(Var)) return false;
			TryComm.CatchScope.IdentifierList.Insert(0, Var);
			TryComm.Flags |= CommandFlags.TryHasCatchVariable;

			var Loc = Var as LocalVariable;
			Loc.PreAssigned = true;
			return true;
		}

		public static Identifier GetOrCreateCatchVariable(Command TryComm)
		{
			if ((TryComm.Flags & CommandFlags.TryHasCatchVariable) == 0)
			{
				if (!CreateCatchVariable(TryComm))
					return null;
			}

			return TryComm.CatchScope.IdentifierList[0];
		}

		static void CreateRootCatchScope(Command TryComm)
		{
			TryComm.CatchScope = new CodeScopeNode(TryComm, TryComm.Code);
			TryComm.Children.Add(TryComm.CatchScope);
		}

		static bool CopyCatchVar(CodeScopeNode Scope, Identifier From, Identifier To, CodeString Code)
		{
			var Plugin = Scope.GetPlugin();
			if (!Plugin.Begin()) return false;

			var Ch = new ExpressionNode[]
			{
				Plugin.NewNode(new IdExpressionNode(To, Code)),
				Plugin.NewNode(new IdExpressionNode(From, Code)),
			};

			if (Ch[0] == null || Ch[1] == null)
				return false;

			if (!To.Children[0].IsEquivalent(From.Children[0]))
			{
				var CastCh1 = Plugin.NewNode(new IdExpressionNode(To.Children[0], Code));
				if (CastCh1 == null) return false;

				var CastCh = new ExpressionNode[] { Ch[1], CastCh1 };
				Ch[1] = Plugin.NewNode(new OpExpressionNode(Operator.Cast, CastCh, Code));
				if (Ch[1] == null) return false;
			}

			var Node = Plugin.NewNode(new OpExpressionNode(Operator.Assignment, Ch, Code));
			if (Node == null || Plugin.End(ref Node) == PluginResult.Failed)
				return false;

			var Comm = new Command(Scope, Code, CommandType.Expression);
			Comm.Expressions = new List<ExpressionNode>() { Node };
			Scope.Children.Add(Comm);
			return true;
		}

		static bool CopyCatchVar(Command TryComm, CodeScopeNode Scope, Identifier Type, CodeString Name, CodeString Code)
		{
			var CatchVar = GetOrCreateCatchVariable(TryComm);
			var NewVar = Scope.CreateAndDeclareVariable(Name, Type);
			if (NewVar == null || CatchVar == null) return false;

			return CopyCatchVar(Scope, CatchVar, NewVar, Code);
		}

		static ExpressionNode CreateCatchCondition(Command TryComm, Command Condition, Identifier Type, CodeString Code)
		{
			var CatchVar = GetOrCreateCatchVariable(TryComm);
			if (CatchVar == null) return null;

			var Plugin = Condition.GetPlugin();
			if (!Plugin.Begin()) return null;

			var Ch = new ExpressionNode[]
			{
				Plugin.NewNode(new IdExpressionNode(CatchVar, Code)),
				Plugin.NewNode(new IdExpressionNode(Type, Code)),
			};
			
			var Node = Plugin.NewNode(new OpExpressionNode(Operator.Is, Ch, Code));
			if (Node == null || Plugin.End(ref Node) == PluginResult.Failed)
				return null;

			return Node;
		}

		public static bool CreateDefaultCatchScope(Command TryComm)
		{
			if ((TryComm.Flags & CommandFlags.CatchesAllException) != 0)
				throw new ApplicationException();

			var Scope = CreateCatchScope(TryComm, TryComm.Code, TryComm.Code);
			var CatchVar = GetOrCreateCatchVariable(TryComm);
			if (Scope == null || CatchVar == null) return false;

			var Plugin = Scope.GetPlugin();
			if (!Plugin.Begin()) return false;

			var Node = Plugin.NewNode(new IdExpressionNode(CatchVar, TryComm.Code));
			if (Node == null || Plugin.End(ref Node) == PluginResult.Failed)
				return false;

			var Comm = new Command(Scope, TryComm.Code, CommandType.Rethrow);
			Comm.Expressions = new List<ExpressionNode>() { Node };
			Scope.Children.Add(Comm);
			return true;
		}

		public static CodeScopeNode CreateCatchScope(Command TryComm, CodeString CommandCode, CodeString Inner,
			Identifier Type = null, CodeString Name = new CodeString())
		{
			var State = TryComm.State;
			if ((TryComm.Flags & CommandFlags.CatchesAllException) != 0)
			{
				State.Messages.Add(MessageId.CatchesAllException, CommandCode);
				return null;
			}

			if (TryComm.CatchScope == null)
				CreateRootCatchScope(TryComm);

			var ExceptionClass = Identifiers.GetByFullNameFast<ClassType>(State, "System.Exception");
			if (ExceptionClass == null) throw new ApplicationException();

			if (Type == null || Type.IsEquivalent(ExceptionClass))
			{
				TryComm.Flags |= CommandFlags.CatchesAllException;
				if (TryComm.CatchScope.Children.Count == 0)
				{
					if ((TryComm.Flags & CommandFlags.TryHasCatchVariable) != 0)
						throw new ApplicationException();

					if (Type != null && !CreateCatchVariable(TryComm, Type, Name))
						return null;

					TryComm.CatchScope.Code = Inner;
					return TryComm.CatchScope;
				}
				else
				{
					var Cond = TryComm.CatchScope.Children[0] as Command;
					var ElseScope = new CodeScopeNode(Cond, Inner);
					Cond.Children.Add(ElseScope);

					if (Type != null && !CopyCatchVar(TryComm, ElseScope, Type, Name, CommandCode))
						return null;

					return ElseScope;
				}
			}
			else
			{
				if (!Identifiers.IsSubtypeOf(Type, ExceptionClass))
				{
					State.Messages.Add(MessageId.CannotBeThisType, Name);
					return null;
				}

				Command Cond;
				if (TryComm.CatchScope.Children.Count == 0)
				{
					Cond = new Command(TryComm.CatchScope, TryComm.Code, CommandType.If);
					Cond.Expressions = new List<ExpressionNode>();
					TryComm.CatchScope.Children.Add(Cond);
				}
				else
				{
					Cond = TryComm.CatchScope.Children[0] as Command;
				}

				var Condition = CreateCatchCondition(TryComm, Cond, Type, CommandCode);
				if (Condition == null) return null;

				var ThenScope = new CodeScopeNode(Cond, Inner);
				Cond.Expressions.Add(Condition);
				Cond.Children.Add(ThenScope);

				if (!CopyCatchVar(TryComm, ThenScope, Type, Name, CommandCode))
					return null;

				return ThenScope;
			}
		}
	}

	public interface ICommandExtension
	{
		void GetAssembly(CodeGenerator CG);
	}

	public class Command : IdContainer
	{
		public CommandType Type;
		public CommandFlags Flags;
		public CodeString Code;

		public List<ExpressionNode> Expressions;
		public ICommandExtension Extension;

		// Breakable
		public int BreakLabel = -1;
		public int ContinueLabel = -1;

		// Jump, Label Command
		public int Label = -1;

		// Try
		public CodeScopeNode CatchScope;
		public int CatchLabel;
		public CodeScopeNode FinallyScope;
		public int FinallyLabel;

		// Goto Command
		public Command JumpTo;
		public CodeString LabelName;

		public Command(IdContainer Parent, CodeString Code, CommandType Type)
			: base(Parent)
		{
			this.Type = Type;
			this.Code = Code;

			if (Commands.IsLoopCommand(Type))
			{
				BreakLabel = State.AutoLabel;
				ContinueLabel = State.AutoLabel;

				Flags |= CommandFlags.Breakable;
				Flags |= CommandFlags.Continueable;
			}
			else if (Type == CommandType.Try)
			{
				CatchLabel = State.AutoLabel;
				FinallyLabel = State.AutoLabel;
			}
		}

		public void ForEachJumpedOver<T>(Action<T> Func)
			where T : IdContainer
		{
			if (Type == CommandType.Return)
			{
				ForEachParent<T>(Func, FunctionScope.Parent);
			}
			else if (Type == CommandType.Break || Type == CommandType.Continue)
			{
				var NeededFlag = Type == CommandType.Break ?
					CommandFlags.Breakable : CommandFlags.Continueable;

				var Loop = GetParent<Command>(x => (x.Flags & NeededFlag) != 0);
				ForEachParent<T>(Func, Loop.Children[0]);
			}
			else if (Type == CommandType.Goto)
			{
				ForEachParent<T>(Func, JumpTo.Parent);
			}
			else
			{
				throw new InvalidOperationException();
			}
		}

		public T JumpsOver<T>(Predicate<T> Func)
			where T : IdContainer
		{
			if (Type == CommandType.Return)
			{
				return GetParent<T>(Func, FunctionScope.Parent);
			}
			else if (Type == CommandType.Break || Type == CommandType.Continue)
			{
				var NeededFlag = Type == CommandType.Break ?
					CommandFlags.Breakable : CommandFlags.Continueable;

				var Loop = GetParent<Command>(x => (x.Flags & NeededFlag) != 0);
				return GetParent<T>(Func, Loop.Children[0]);
			}
			else if (Type == CommandType.Goto)
			{
				return GetParent<T>(Func, JumpTo.Parent);
			}
			else
			{
				throw new InvalidOperationException();
			}
		}

		public JumpDestination GetJumpDestination()
		{
			if (Type == CommandType.Break || Type == CommandType.Continue)
			{
				var Breakable = GetParent<Command>(x => (x.Flags & CommandFlags.Breakable) != 0);
				return new JumpDestination(Breakable, JumpMode.Leave);
			}
			else
			{
				if (Type == CommandType.Goto)
					return new JumpDestination(JumpTo);
				else if (Type == CommandType.Return)
					return new JumpDestination(FunctionScope, JumpMode.Leave);
				else
					throw new InvalidOperationException();
			}
		}

		public bool ReplaceExpressions(Func<ExpressionNode, ExpressionNode> Func)
		{
			var RetValue = true;
			if (Expressions != null)
			{
				for (var i = 0; i < Expressions.Count; i++)
				{
					Expressions[i] = Func(Expressions[i]);
					if (Expressions[i] == null) RetValue = false;
				}
			}

			return RetValue;
		}

		public override Variable OnCreateVariable(CodeString Name, Identifier Type, List<Modifier> Mods = null)
		{
			var Ret = CreateVariableHelper(Name, Type, Mods);
			if (Ret == null) Ret = new LocalVariable(this, Name, Type);
			return Ret;
		}

		public override PluginRoot GetPlugin()
		{
			return new PluginForCodeScope(this);
		}

		public override void GetAssembly(CodeGenerator CG, GetAssemblyMode Mode = GetAssemblyMode.Code)
		{ 
			CG.Container = this;
			if (Extension != null)
			{
				Extension.GetAssembly(CG);
				return;
			}

			if (Type == CommandType.Expression)
			{
				CG.EmitExpression(Expressions[0]);
			}
			else if (Type == CommandType.Cycle)
			{
				CG.InsContainer.Label(ContinueLabel);
				Children[0].GetAssembly(CG);
				CG.InsContainer.Jump(ContinueLabel);
				CG.InsContainer.Label(BreakLabel);
			}
			else if (Type == CommandType.While || Type == CommandType.DoWhile)
			{
				GetWhileAssembly(CG, Expressions[0], Type == CommandType.DoWhile);
			}
			else if (Type == CommandType.For)
			{
				if (Expressions[0] != null)
					CG.EmitExpression(Expressions[0]);

				var DoWhile = WillRun == ConditionResult.True;
				GetWhileAssembly(CG, Expressions[1], DoWhile, Expressions[2]);
			}
			else if (Type == CommandType.If)
			{
				NextLabel = State.AutoLabel;
				CalcIfAssembly(CG, 0);
				CG.InsContainer.Label(NextLabel);
			}
			else if (Type == CommandType.Label)
			{
				CG.InsContainer.Label(Label);
			}
			else if (Type == CommandType.Try)
			{
				Children[0].GetAssembly(CG);
				if (CatchScope != null)
					CG.InsContainer.Jump(FinallyLabel);

				CG.InsContainer.Label(CatchLabel);
				if (CatchScope != null)
					CatchScope.GetAssembly(CG);

				CG.InsContainer.Label(FinallyLabel);
				if (FinallyScope != null)
					FinallyScope.GetAssembly(CG);
			}
			else if (Commands.IsJumpCommand(Type))
			{
				if (Type == CommandType.Return && Expressions != null && Expressions.Count > 0)
					CG.EmitExpression(Expressions[0]);

				CG.InsContainer.Jump(Label);
			}
			else if (Type != CommandType.Unknown)
			{
				throw new NotImplementedException();
			}
		}

		Command GetCommand(IdContainer Container)
		{
			var Scope = Container as CodeScopeNode;
			if (Scope == null) return null;

			if (Scope.Children.Count != 1) return null;
			else return Scope.Children[0] as Command;
		}

		int NextLabel;
		void CalcIfAssembly(CodeGenerator CG, int Pos)
		{
			for (var i = Pos; i < Children.Count; i++)
			{
				var Res = ConditionResult.True;
				if (Expressions.Count > i)
					Res = Expressions[i].ConditionResult;

				if (Res == ConditionResult.Unknown)
				{
					var Cond = Expressions[i];
					var Container = Children[i];

					var ThenCmd = GetCommand(Container);
					var HasCondLessElse = i + 1 < Children.Count && i + 1 >= Expressions.Count;
					var ElseCmd = HasCondLessElse ? GetCommand(Children[i + 1]) : null;

					var Branches = State.Arch.GetBranches(GlobalContainer, ThenCmd, ElseCmd, ref Cond);
					if (Branches[0] == null)
						Branches[0] = new CodeCondBranch(_CG => Container.GetAssembly(_CG));

					if (Branches[1] == null && i + 1 < Children.Count)
						Branches[1] = new CodeCondBranch(_CG => CalcIfAssembly(_CG, i + 1));

					CG.EmitCondition(Cond, Branches[0], Branches[1], NextLabel: NextLabel);
					break;
				}
				else if (Res == ConditionResult.True)
				{
					Children[i].GetAssembly(CG);
					break;
				}
			}
		}

		private void GetWhileAssembly(CodeGenerator CG, ExpressionNode Condition,
			bool DoWhile = false, ExpressionNode Loop = null)
		{
			var Then = State.AutoLabel;
			var CondLbl = State.AutoLabel;

			//--------------------------------------------------------
			if (State.Arch.IsSimpleCompareNode(Condition))
			{
				CG.SetJumpReplacing(CondLbl, () =>
				{
					CG.EmitCondition(Condition, Then, BreakLabel, true);
					CG.InsContainer.Jump(DoWhile ? Then : BreakLabel);
				});
			}

			//--------------------------------------------------------
			if (!DoWhile)
			{
				CG.InsContainer.Label(CondLbl);
				CG.EmitCondition(Condition, Then, BreakLabel, false);
			}

			CG.InsContainer.Label(Then);
			Children[0].GetAssembly(CG);

			CG.InsContainer.Label(ContinueLabel);
			if (Loop != null) CG.EmitExpression(Loop);

			if (DoWhile)
			{
				CG.InsContainer.Label(CondLbl);
				CG.EmitCondition(Condition, Then, BreakLabel, true);
			}
			else
			{
				CG.InsContainer.Jump(CondLbl);
			}

			CG.InsContainer.Label(BreakLabel);
		}

		ConditionResult _WillRun;
		bool _WillRun_Calced = false;
		public ConditionResult WillRun
		{
			get
			{
				if (!_WillRun_Calced)
				{
					if (Type == CommandType.Cycle)
					{
						_WillRun = ConditionResult.True;
					}
					else if (Type == CommandType.While || Type == CommandType.DoWhile)
					{
						_WillRun = Expressions[0].ConditionResult;
					}
					else if (Type == CommandType.For)
					{
						_WillRun = Expressions[1].ConditionResult;
						if (_WillRun == ConditionResult.Unknown)
						{
							ExpressionNode AssignTo = null;
							Variable AssignVar = null;

							if (Expressions[0] is OpExpressionNode)
							{
								var OpNode = Expressions[0] as OpExpressionNode;
								if (OpNode.Operator == Operator.Assignment)
								{
									var Ch = OpNode.Children;
									AssignTo = Ch[1];
									OpNode.GetAssignVar(ref AssignVar);
								}
							}

							if (AssignVar != null && !AssignTo.IdUsed())
							{
								var ConstAssignTo = AssignTo as ConstExpressionNode;
								if (ConstAssignTo == null) return ConditionResult.Unknown;

								var OpCondition = Expressions[1] as OpExpressionNode;
								if (OpCondition == null) return ConditionResult.Unknown;
								var Op = OpCondition.Operator;
								var Ch = OpCondition.Children;

								if (!Operators.IsRelEquality(Op)) return ConditionResult.Unknown;
								var IdCh0 = Ch[0] as IdExpressionNode;
								if (IdCh0 == null || IdCh0.Identifier != AssignVar) return ConditionResult.Unknown;
								var ConstCh1 = Ch[1] as ConstExpressionNode;
								if (ConstCh1 == null) return ConditionResult.Unknown;

								var Ret = ConstAssignTo.Value.DoOperation(ConstCh1.Value, Op);
								if (!(Ret is BooleanValue)) return ConditionResult.Unknown;

								var BoolRet = Ret as BooleanValue;
								_WillRun = BoolRet.Value ? ConditionResult.True : ConditionResult.False;
							}
						}
					}
					else
					{
						throw new NotImplementedException();
					}

					_WillRun_Calced = true;
				}

				return _WillRun;
			}
		}

		public bool HasExpressions
		{
			get { return Expressions != null && Expressions.Count > 0; }
		}

		public void AddExpression(ExpressionNode Node)
		{
			if (Expressions == null)
				Expressions = new List<ExpressionNode>();

			Expressions.Add(Node);
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Software Developer
Hungary Hungary
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions