/SimpleBrainFuck.Core/Core/SimpleTranslator.cs
C# | 365 lines | 322 code | 43 blank | 0 comment | 104 complexity | b4d758626fed703b4364e27c45463eea MD5 | raw file
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using ZackFlame.SimpleBrainFuck.Core.Statments;
- using ZackFlame.SimpleBrainFuck.TextResources;
-
- namespace ZackFlame.SimpleBrainFuck.Core
- {
- public sealed class SimpleTranslator
- {
- readonly List<BrainDef> SystemDefs = new List<BrainDef>();
- readonly BrainMacros[] SystemMacroses;
-
- public SimpleTranslator()
- {
- SystemDef("error", ms => SystemFunctions.Error(ms[0]), "text");
- SystemDef("shift", ms => SystemFunctions.Shift(int.Parse(ms[0])), "k");
- SystemDef("zero", ms => SystemFunctions.Zero());
- SystemDef("add", ms => SystemFunctions.Add(int.Parse(ms[0])), "k");
- SystemDef("sub", ms => SystemFunctions.Sub(int.Parse(ms[0])), "k");
- SystemDef("move", ms => SystemFunctions.Move(int.Parse(ms[0])), "k");
- SystemDef("copy", ms => SystemFunctions.Copy(int.Parse(ms[0]), int.Parse(ms[1])), "k", "t");
- SystemDef("repeat", ms => SystemFunctions.Repeat(ms[0], int.Parse(ms[1])), "text", "k");
- SystemDef("value", ms => SystemFunctions.Value(int.Parse(ms[0]), int.Parse(ms[1])), "k", "t");
- SystemDef("text", ms => SystemFunctions.Text(ms[0]), "text");
- SystemDef("type", ms => SystemFunctions.Type(ms[0], int.Parse(ms[1])), "text", "t");
- SystemDef("print", ms => SystemFunctions.Print(int.Parse(ms[0])), "k");
-
- SystemMacroses = new BrainMacros[]
- {
- new BrainMacros("__version__")
- {
- Value = typeof(SimpleParser).Assembly.GetName().Version.Major.ToString()
- },
- new BrainMacros("__platform__")
- {
- Value = Environment.OSVersion.Platform.ToString()
- },
- };
- }
-
- private void SystemDef(string name,
- Func<string[], string> function, params string[] macrosesNames)
- {
- SystemDefs.Add(new BrainDef(function)
- {
- Name = name,
- MacrosArgs = macrosesNames
- .Select(macrosName => new BrainMacros() { Name = macrosName })
- .ToList()
- });
- }
-
- public string RenderBrainFuck(IEnumerable<Statment> semanticTree)
- {
- Stack<BrainDef> callStack = new Stack<BrainDef>();
- return RenderTree(semanticTree, callStack, SystemMacroses, SystemDefs);
- }
-
- private string RenderTree(
- IEnumerable<Statment> semanticTree, Stack<BrainDef> callStack,
- IEnumerable<BrainMacros> macroses, IEnumerable<BrainDef> defs)
- {
- var currentDefs = new List<BrainDef>();
- var currentMacroses = new List<BrainMacros>();
- StringBuilder brainString = new StringBuilder();
-
- foreach (Statment statment in semanticTree)
- {
- if (statment is BrainDef)
- {
- BrainDef defStat = (BrainDef)statment;
- ProcessDef(defStat, currentDefs);
- }
- else if (statment is BrainIf)
- {
- BrainIf ifStat = (BrainIf)statment;
- var allMacroses = macroses.Concat(currentMacroses);
- var allDefs = defs.Concat(currentDefs);
- brainString.Append(RenderIf(ifStat, callStack, allMacroses, allDefs));
- }
- else if (statment is BrainCall)
- {
- BrainCall callStat = (BrainCall)statment;
-
- var allMacroses = macroses.Concat(currentMacroses);
- var allDefs = defs.Concat(currentDefs);
-
- BrainDef def = allDefs.LastOrDefault(d => d.Name == callStat.DefName);
- if (def == null)
- throw new CodeErrorException(callStat.Line, CodeErrors.Call_DefNotFound);
- if (callStack.Contains(def))
- throw new CodeErrorException(callStat.Line, CodeErrors.Call_CycleCall);
-
- if (callStat.CallArgs.Count != def.MacrosArgs.Count)
- throw new CodeErrorException(callStat.Line, CodeErrors.Call_InvalidCallArgsCount);
-
- brainString.Append(RenderCall(callStat, def, callStack, allMacroses, allDefs));
- }
- else if (statment is BrainMacrosAssignment)
- {
- var assignment = (BrainMacrosAssignment)statment;
- ProcessAssignment(assignment, macroses, currentMacroses);
- }
- else if (statment is BrainPureCode)
- {
- brainString.Append(((BrainPureCode)statment).Value);
- }
-
- brainString.AppendLineLF();
- }
-
- return brainString.ToString();
- }
-
- private void ProcessDef(BrainDef defStat, List<BrainDef> currentDefs)
- {
- if (!BrainDef.NameRegex.Match(defStat.Name).Success)
- throw new CodeErrorException(defStat.Line, CodeErrors.Def_InvalidName);
-
- if (currentDefs.Exists(def => def.Name == defStat.Name))
- throw new CodeErrorException(defStat.Line, CodeErrors.Def_DefWithNameAlreadyExists);
-
- var matches = from macro in defStat.MacrosArgs
- select BrainMacros.NameRegex.Match(macro.Name);
-
- if (!matches.All(m => m.Success))
- throw new CodeErrorException(defStat.Line, CodeErrors.Def_InvalidParameterName);
- if (defStat.MacrosArgs.Any(m => m.Negative))
- throw new CodeErrorException(defStat.Line, CodeErrors.Def_ParameterCantBeNegative);
-
- for (int i = 0; i < defStat.MacrosArgs.Count; i++)
- {
- var parameter = defStat.MacrosArgs[i];
- for (int j = i + 1; j < defStat.MacrosArgs.Count; j++)
- {
- if (parameter.Name == defStat.MacrosArgs[j].Name)
- throw new CodeErrorException(defStat.Line, CodeErrors.Def_ParameterAlreadyDefined);
- }
- }
-
- currentDefs.Add(defStat);
- }
-
- private string RenderIf(BrainIf ifStatment, Stack<BrainDef> callStack,
- IEnumerable<BrainMacros> macroses, IEnumerable<BrainDef> defs)
- {
- string parameter = CalculateArgument(ifStatment.Parameter, macroses);
- if (parameter == null)
- throw new CodeErrorException(ifStatment.Line, CodeErrors.Call_MacrosNotDefined);
-
- int tParam;
- if (!int.TryParse(parameter, out tParam))
- throw new CodeErrorException(ifStatment.Line, CodeErrors.Call_InvalidArgumentsFormat);
-
- StringBuilder result = new StringBuilder()
- .AppendLineLF(SystemFunctions.If(tParam))
- .Append(RenderTree(ifStatment.IfStatments, callStack, macroses, defs))
- .AppendLineLF(SystemFunctions.Else(tParam))
- .Append(RenderTree(ifStatment.ElseStatments, callStack, macroses, defs))
- .AppendLineLF(SystemFunctions.EndIf(tParam));
-
- return result.ToString();
- }
-
- private string RenderCall(BrainCall callStatment,
- BrainDef callTarget, Stack<BrainDef> callStack,
- IEnumerable<BrainMacros> macroses, IEnumerable<BrainDef> defs)
- {
- var callParameters = callStatment.CallArgs.Select(arg =>
- {
- string value = CalculateArgument(arg, macroses);
- if (value == null)
- throw new CodeErrorException(callStatment.Line, CodeErrors.Call_MacrosNotDefined);
- return value;
- });
-
- if (callTarget.CanRenderItself)
- {
- try
- {
- return callTarget.Render(callParameters.ToArray());
- }
- catch (FormatException)
- {
- throw new CodeErrorException(callStatment.Line, CodeErrors.Call_InvalidArgumentsFormat);
- }
- catch (ArgumentException ex)
- {
- throw new CodeErrorException(callStatment.Line, ex.Message);
- }
- catch (CodeErrorException ex)
- {
- ex.Line = callStatment.Line;
- throw ex;
- }
- }
- else
- {
- var definedMacroses = callTarget.MacrosArgs.Zip(callParameters,
- (m, p) => new BrainMacros() { Name = m.Name, Value = p });
- var allMacroses = macroses.Concat(definedMacroses);
-
- callStack.Push(callTarget);
- string rendered = RenderTree(callTarget.Statments, callStack, allMacroses, defs);
- callStack.Pop();
- return rendered;
- }
- }
-
- private void ProcessAssignment(BrainMacrosAssignment assignment,
- IEnumerable<BrainMacros> macroses, List<BrainMacros> currentMacroses)
- {
- if (!BrainMacros.NameRegex.Match(assignment.Name).Success)
- throw new CodeErrorException(assignment.Line, CodeErrors.Macro_InvalidName);
-
- var allMacroses = macroses.Concat(currentMacroses);
-
- string leftValue = CalculateArgument(assignment.Expression.Left, allMacroses);
- if (leftValue == null)
- throw new CodeErrorException(assignment.Line, CodeErrors.Call_MacrosNotDefined);
-
- string rightValue = CalculateArgument(assignment.Expression.Right, allMacroses);
- string result = null;
-
- string oper = assignment.Expression.Operator;
- if (oper == null)
- {
- result = leftValue;
- }
- else if (oper == "-" && assignment.Expression.Right == null)
- {
- int left;
- if (int.TryParse(leftValue, out left))
- {
- result = (-left).ToString();
- }
- else
- {
- throw new CodeErrorException(assignment.Line,
- CodeErrors.Assignment_InvalidArgumentsFormat);
- }
- }
- else if (oper == "+" || oper == "-" || oper == "*" || oper == "/"
- && assignment.Expression.Right != null)
- {
- if (rightValue == null)
- throw new CodeErrorException(assignment.Line, CodeErrors.Call_MacrosNotDefined);
-
- int left;
- int right;
-
- if (int.TryParse(leftValue, out left) &&
- int.TryParse(rightValue, out right))
- {
- int resultValue = 0;
- if (oper == "+")
- resultValue = left + right;
- else if (oper == "-")
- resultValue = left - right;
- else if (oper == "*")
- resultValue = left * right;
- else if (oper == "/")
- resultValue = left / right;
-
- result = resultValue.ToString();
- }
- else
- {
- throw new CodeErrorException(assignment.Line,
- CodeErrors.Assignment_InvalidArgumentsFormat);
- }
- }
- else if (oper == "." && assignment.Expression.Right != null)
- {
- if (rightValue == null)
- throw new CodeErrorException(assignment.Line, CodeErrors.Call_MacrosNotDefined);
-
- result = string.Concat(leftValue, rightValue);
- }
- else if (oper == "length" && assignment.Expression.Right == null)
- {
- result = leftValue.Length.ToString();
- }
- else if (oper == "take" || oper == "skip" && assignment.Expression.Right != null)
- {
- if (rightValue == null)
- throw new CodeErrorException(assignment.Line, CodeErrors.Call_MacrosNotDefined);
-
- int right;
- if (int.TryParse(rightValue, out right))
- {
- if (right < 0 || right > leftValue.Length)
- {
- throw new CodeErrorException(assignment.Line,
- CodeErrors.Operator_InvalidTakeOrSkipCount);
- }
-
- if (oper == "take")
- result = leftValue.Substring(0, right);
- else
- result = leftValue.Substring(right);
- }
- }
- else
- {
- throw new CodeErrorException(assignment.Line, CodeErrors.Assignment_InvalidOperator);
- }
-
- BrainMacros current = currentMacroses.FirstOrDefault(
- m => m.Name == assignment.Name);
-
- if (current != null)
- {
- current.Value = result;
- }
- else
- {
- currentMacroses.Add(new BrainMacros()
- {
- Name = assignment.Name,
- Value = result,
- });
- }
- }
-
- private string CalculateArgument(BrainArg argument, IEnumerable<BrainMacros> macroses)
- {
- if (argument == null)
- return null;
-
- BrainMacros macros = argument as BrainMacros;
- if (macros != null)
- {
- BrainMacros definition = macroses
- .LastOrDefault(m => m.Name == macros.Name);
-
- if (definition == null)
- {
- return null;
- }
- else
- {
- if (macros.Negative)
- {
- int value;
- if (!int.TryParse(definition.Value, out value))
- throw new CodeErrorException(argument.Line, CodeErrors.Macro_NotANumber);
-
- return (-value).ToString();
- }
- else
- {
- return definition.Value;
- }
- }
- }
- else
- {
- return argument.Value;
- }
- }
- }
- }