protected string defineVariable(PreparedData prepared, string evaluated) { evaluated = vSignOperation(prepared, evaluated); Log.Debug("Evaluate: ready to define variable - '{0}':'{1}'", prepared.variable.name, prepared.variable.project); uvariable.set(prepared.variable.name, prepared.variable.project, evaluated); uvariable.evaluate(prepared.variable.name, prepared.variable.project, new EvaluatorBlank(), true); tSignOperation(prepared, ref evaluated); return(String.Empty); }
/// <summary> /// The final value from prepared information /// </summary> /// <param name="prepared"></param> protected string evaluate(PreparedData prepared) { string evaluated = String.Empty; string custom = customRule(prepared.property.unevaluated, prepared.property.project); if (custom != null) { Log.Trace("Evaluate: custom '{0}'", custom); evaluated = evaluate(custom, null); } else if (prepared.variable.type == PreparedData.ValueType.StringFromDouble || prepared.variable.type == PreparedData.ValueType.StringFromSingle || !String.IsNullOrEmpty(prepared.variable.name)) { //Note: * content from double quotes should be already evaluated in prev. steps. // * content from single quotes shouldn't be evaluated by rules with protector above. Log.Trace("Evaluate: use content from string or use escaped property"); evaluated = prepared.property.unevaluated; } else if (!prepared.property.complex) { Log.Trace("Evaluate: use getProperty"); evaluated = getProperty(prepared.property.unevaluated, prepared.property.project); try { evaluated = unlooping(evaluated, prepared, false); } catch (PossibleLoopException) { // last chance with direct evaluation evaluated = evaluate(prepared.property.unevaluated, prepared.property.project); evaluated = unlooping(evaluated, prepared, true); } } else { Log.Trace("Evaluate: use evaluation with msbuild engine"); evaluated = evaluate(prepared.property.unevaluated, prepared.property.project); evaluated = unlooping(evaluated, prepared, true); } Log.Debug("Evaluated: '{0}'", evaluated); // updating of variables if (!String.IsNullOrEmpty(prepared.variable.name)) { evaluated = defineVariable(prepared, evaluated); } return(evaluated); }
protected void tSignOperation(PreparedData prepared, ref string evaluated) { #if DEBUG Log.Trace($"tSignOperation: '{prepared.variable.tSign}'"); #endif if (prepared.variable.tSign == PreparedData.TSignType.DefProperty) { defProperty(prepared.variable, evaluated); } else if (prepared.variable.tSign == PreparedData.TSignType.UndefProperty) { undefProperty(prepared.variable); } }
protected string vSignOperation(PreparedData prepared, string val) { if (prepared.variable.vSign == PreparedData.VSignType.Default) { return(val); } var left = uvariable.get(prepared.variable.name, prepared.variable.project) ?? "0"; bool isNumber = RPattern.IsNumber.IsMatch(left); Log.Trace($"vSignOperation: '{prepared.variable.vSign}'; `{left}` (isNumber: {isNumber})"); if (prepared.variable.vSign == PreparedData.VSignType.Increment) { if (!isNumber) { // equiv.: $(var = $([System.String]::Concat($(var), "str")) ) return(left + val); } // $(var = $([MSBuild]::Add($(var), 1)) ) // TODO: additional check for errors from right string $(i += 'test') ... this correct: $(i += $(i)) return(evaluate($"$([MSBuild]::Add('{left}', '{val}'))", prepared.variable.project)); } if (prepared.variable.vSign == PreparedData.VSignType.Decrement) { if (!isNumber) { throw new InvalidArgumentException($"Argument `{val}` is not valid for operation '-=' or it is not supported yet."); } // $(var = $([MSBuild]::Subtract($(var), 1)) ) return(evaluate($"$([MSBuild]::Subtract('{left}', '{val}'))", prepared.variable.project)); } return(val); }
/// <summary> /// Prepare data for next evaluation step. /// </summary> /// <param name="raw">a single container e.g.: '(..data..)'</param> /// <exception cref="IncorrectSyntaxException"></exception> protected PreparedData prepare(string raw) { Match m = Regex.Match(raw.Trim(), String.Format(@"^\( (?: \s* ([A-Za-z_0-9]+) # 1 -> variable name (optional) \s*=\s* (?: {0} # 2 -> string data inside double quotes | {1} # 3 -> string data inside single quotes )? )? (?: (.+) # 4 -> unevaluated data (?<!:): ([^:)]+) # 5 -> specific project for variable if 1 is present or for unevaluated data | # or: (.+) # 6 -> unevaluated data )? \)$", RPattern.DoubleQuotesContent, RPattern.SingleQuotesContent ), RegexOptions.IgnorePatternWhitespace); if(!m.Success) { throw new IncorrectSyntaxException("prepare: failed - '{0}'", raw); } Group rVariable = m.Groups[1]; Group rStringDataD = m.Groups[2]; Group rStringDataS = m.Groups[3]; Group rDataWithProject = m.Groups[4]; Group rProject = m.Groups[5]; Group rData = m.Groups[6]; PreparedData ret = new PreparedData() { property = new PreparedData.Property() { raw = raw } }; //Log.Trace("prepare: raw '{0}'", raw); /* Variable */ if(rVariable.Success) { ret.variable.name = rVariable.Value; // all $() in right operand cannot be evaluated because it's escaped and already unwrapped property. // i.e. this already should be evaluated with prev. steps - because we are moving upward from deepest container ! Log.Trace("prepare: variable name = '{0}'", ret.variable.name); } /* Data */ if(rStringDataD.Success) { ret.property.unevaluated = rStringDataD.Value.Replace("\\\"", "\""); //TODO: ret.variable.type = PreparedData.ValueType.StringFromDouble; } else if(rStringDataS.Success) { ret.property.unevaluated = rStringDataS.Value.Replace("\\'", "'"); //TODO: ret.variable.type = PreparedData.ValueType.StringFromSingle; } else { ret.property.unevaluated = ((rDataWithProject.Success)? rDataWithProject : rData).Value.Trim(); ret.variable.type = (rVariable.Success)? PreparedData.ValueType.Unknown : PreparedData.ValueType.Property; } ret.property.complex = !isPropertySimple(ref ret.property.unevaluated); Log.Trace("prepare: value = '{0}'({1})", ret.property.unevaluated, ret.variable.type); Log.Trace("prepare: complex {0}", ret.property.complex); /* Project */ if(rDataWithProject.Success) { string project = rProject.Value.Trim(); if(rVariable.Success) { //TODO: non standard! but it also can be as a $(varname = $($(..data..):project)) ret.variable.project = project; } else { ret.property.project = project; } } Log.Trace("prepare: project [v:'{0}'; p:'{1}']", ret.variable.project, ret.property.project); return ret; }
/// <summary> /// The final value from prepared information /// </summary> /// <param name="prepared"></param> protected string evaluate(PreparedData prepared) { string evaluated = String.Empty; string custom = customRule(prepared.property.unevaluated, prepared.property.project); if(custom != null) { Log.Trace("Evaluate: custom '{0}'", custom); evaluated = evaluate(custom, null); } else if(prepared.variable.type == PreparedData.ValueType.StringFromDouble || prepared.variable.type == PreparedData.ValueType.StringFromSingle || !String.IsNullOrEmpty(prepared.variable.name)) { //Note: * content from double quotes should be already evaluated in prev. steps. // * content from single quotes shouldn't be evaluated because uses protector for current type above. Log.Trace("Evaluate: use content from string or use escaped property"); evaluated = prepared.property.unevaluated; } else if(!prepared.property.complex) { Log.Trace("Evaluate: use getProperty"); evaluated = getProperty(prepared.property.unevaluated, prepared.property.project); } else { Log.Trace("Evaluate: use evaluation with msbuild engine"); evaluated = evaluate(prepared.property.unevaluated, prepared.property.project); } Log.Debug("Evaluated: '{0}'", evaluated); // alternative to SBE-Scripts if(!String.IsNullOrEmpty(prepared.variable.name)) { Log.Debug("Evaluate: ready to define variable - '{0}':'{1}'", prepared.variable.name, prepared.variable.project); uvariable.set(prepared.variable.name, prepared.variable.project, evaluated); uvariable.evaluate(prepared.variable.name, prepared.variable.project, new EvaluatorBlank(), true); evaluated = String.Empty; } return evaluated; }
/// <summary> /// Operations for recursive properties and to avoiding some looping. /// /// The recursive property means that value contains same definition, even after evaluation of this, for example: /// `$(var)` -> `value from $(var)` -> `value from value from $(var)` ... /// /// TODO: I need a COW... fix mooo ~@@`` /// </summary> /// <param name="data">Data with possible recursive property or some looping.</param> /// <param name="prepared">Describes property.</param> /// <param name="eqEscape">To escape equality if true, otherwise throw PossibleLoopException.</param> /// <returns>Evaluated data.</returns> protected string unlooping(string data, PreparedData prepared, bool eqEscape) { string pname = prepared.property.name; if (pname == null || data.IndexOf("$(") == -1) { return(data); // all clear } HashSet <string> pchecked = new HashSet <string>(); Action <string> unlink = null; unlink = delegate(string _name) { if (!pchecked.Add(_name)) { #if DEBUG Log.Debug("unlooping-unlink: has been protected with `{0}`", _name); #endif return; //throw new MismatchException(""); } if (!references.ContainsKey(_name)) { return; } if (references[_name].Contains(pname)) { throw new LimitException("Found looping for used properties: `{0}` <-> `{1}`", _name, pname); } foreach (string refvar in references[_name]) { unlink(refvar); } }; data = RPattern.ContainerInNamedCompiled.Replace(data, delegate(Match m) { string found = m.Groups["name"].Value; if (pname == found) { if (eqEscape) { return("$" + m.Groups[0].Value); } throw new PossibleLoopException(); } if (!references.ContainsKey(pname)) { references[pname] = new HashSet <string>(); } references[pname].Add(found); // checking unlink(found); return(m.Groups[0].Value); }); return(data); }
/// <summary> /// Prepare data for next evaluation step. /// </summary> /// <param name="raw">a single container e.g.: '(..data..)'</param> /// <exception cref="IncorrectSyntaxException"></exception> protected PreparedData prepare(string raw) { Match m = RPattern.PItem.Match(raw.Trim()); if (!m.Success) { throw new IncorrectSyntaxException("prepare: failed - '{0}'", raw); } Group rTSign = m.Groups["tsign"]; Group rVSign = m.Groups["vsign"]; Group rVariable = m.Groups[1]; Group rStringDataD = m.Groups[2]; Group rStringDataS = m.Groups[3]; Group rDataWithProject = m.Groups[4]; Group rProject = m.Groups[5]; Group rData = m.Groups[6]; PreparedData ret = new PreparedData() { property = new PreparedData.Property() { raw = raw } }; //Log.Trace("prepare: raw '{0}'", raw); /* Variable */ if (rVariable.Success) { ret.variable.name = rVariable.Value; switch (rTSign.Value) { case "+": { ret.variable.tSign = PreparedData.TSignType.DefProperty; break; } case "-": { ret.variable.tSign = PreparedData.TSignType.UndefProperty; break; } default: { ret.variable.tSign = PreparedData.TSignType.Default; break; } } switch (rVSign.Value) { case "+": { ret.variable.vSign = PreparedData.VSignType.Increment; break; } case "-": { ret.variable.vSign = PreparedData.VSignType.Decrement; break; } default: { ret.variable.vSign = PreparedData.VSignType.Default; break; } } // all $() in right operand cannot be evaluated because it's escaped and already unwrapped property. // i.e. this already should be evaluated with prev. steps - because we are moving upward from deepest container ! Log.Trace($"prepare: variable name = '{ret.variable.name}'; tSign({ret.variable.tSign}), vSign({ret.variable.vSign})"); } /* Data */ if ((rStringDataD.Success || rStringDataS.Success) && (rData.Success && !String.IsNullOrWhiteSpace(rData.Value))) { throw new IncorrectSyntaxException("Incorrect composition of data: string with raw data."); } if (rStringDataD.Success) { ret.property.unevaluated = parse(Tokens.unescapeQuotes('"', rStringDataD.Value)); ret.variable.type = PreparedData.ValueType.StringFromDouble; } else if (rStringDataS.Success) { ret.property.unevaluated = Tokens.unescapeQuotes('\'', rStringDataS.Value); ret.variable.type = PreparedData.ValueType.StringFromSingle; } else { ret.property.unevaluated = hquotes((rDataWithProject.Success ? rDataWithProject : rData).Value.Trim()); ret.variable.type = (rVariable.Success)? PreparedData.ValueType.Unknown : PreparedData.ValueType.Property; int lp = ret.property.unevaluated.IndexOf('.'); ret.property.name = ((lp == -1)? ret.property.unevaluated : ret.property.unevaluated.Substring(0, lp)).Trim(); //TODO: if (ret.property.name.IndexOf('[') != -1) { ret.property.name = null; // avoid static methods } } ret.property.complex = !isPropertySimple(ref ret.property.unevaluated); Log.Trace($"prepare: value = '{ret.property.unevaluated}'({ret.variable.type}); complex: {ret.property.complex}"); /* Project */ if (rDataWithProject.Success) { string project = rProject.Value.Trim(); if (rVariable.Success) { //TODO: non standard! but it also can be as a $(varname = $($(..data..):project)) ret.variable.project = project; } else { ret.property.project = project; } } Log.Trace("prepare: project [v:'{0}'; p:'{1}']", ret.variable.project, ret.property.project); return(ret); }
public new string evaluate(PreparedData prepared) { return base.evaluate(prepared); }