/// <summary> /// Try to parse arguments for the expected function. Returns null if not a match. Throws if it's /// a match, but invalid. /// </summary> /// <param name="module"></param> /// <param name="parsedParams"></param> /// <param name="identifier"></param> /// <param name="numArguments">Required number of arguments.</param> /// <param name="exactNumberOfArguments">If true, numArguments must be exact value. If false, numArguments is a minimum.</param> /// <returns></returns> private static double[] TryParseArguments( PartModule module, ParsedParameters parsedParams, int numArguments, bool exactNumberOfArguments, params string[] identifiers) { if (identifiers.IndexOf(parsedParams.Identifier) < 0) { // not a match! return(null); } if (exactNumberOfArguments) { if (parsedParams.Count != numArguments) { throw new ArgumentException("Wrong number of arguments for '" + parsedParams.Identifier + "' (was " + parsedParams.Count + ", must be " + numArguments + ")"); } } else { if (parsedParams.Count < numArguments) { throw new ArgumentException("Wrong number of arguments for '" + parsedParams.Identifier + "' (was " + parsedParams.Count + ", must be at least " + numArguments + ")"); } } double[] args = new double[parsedParams.Count]; for (int i = 0; i < parsedParams.Count; ++i) { args[i] = Parse(module, parsedParams[i]); } return(args); }
public static IScalar TryParse(PartModule module, ParsedParameters parsedParams) { if (parsedParams == null) { return(null); } switch (parsedParams.Identifier) { case RANGE: { parsedParams.RequireCount(module, 3); IScalar input = Scalars.Require(module, parsedParams[0]); double minimum = Statics.Parse(module, parsedParams[1]); double maximum = Statics.Parse(module, parsedParams[2]); return(Between(input, minimum, maximum)); } case GREATER_THAN: { parsedParams.RequireCount(module, 2); IScalar input = Scalars.Require(module, parsedParams[0]); double minimum = Statics.Parse(module, parsedParams[1]); return(AtLeast(input, minimum)); } case LESS_THAN: { parsedParams.RequireCount(module, 2); IScalar input = Scalars.Require(module, parsedParams[0]); double maximum = Statics.Parse(module, parsedParams[1]); return(AtMost(input, maximum)); } default: return(null); } }
/// <summary> /// Try to get a "vessel control level" matcher from a ParsedParameters. The expected format is: /// /// controlLevel(level1, level2, ...) /// /// Must have at least one level. The allowed control level values are the enum constants /// in Vessel.ControlLevel. /// </summary> /// <param name="module"></param> /// <param name="parsedParams"></param> /// <returns></returns> public static IToggle TryParse(PartModule module, ParsedParameters parsedParams) { if (parsedParams == null) { return(null); } if (!TYPE_NAME.Equals(parsedParams.Identifier)) { return(null); } parsedParams.RequireCount(module, 1, -1); HashSet <Vessel.ControlLevel> requiredLevels = new HashSet <Vessel.ControlLevel>(); for (int i = 0; i < parsedParams.Count; ++i) { Vessel.ControlLevel additionalControlLevel = ParseLevel(module, parsedParams[i]); if (!requiredLevels.Add(additionalControlLevel)) { throw new ArgumentException("Duplicate specification of '" + parsedParams[i] + "' for " + TYPE_NAME + "() on " + module.ClassName + " of " + module.part.GetTitle()); } } return(new VesselControlLevelMatch(module, requiredLevels)); }
/// <summary> /// Parse an IToggle from the specified text. Throws ArgumentException if there's a problem. /// </summary> /// <param name="module"></param> /// <param name="text"></param> /// <returns></returns> public static IToggle Require(PartModule module, string text) { if (string.IsNullOrEmpty(text)) { throw new ArgumentException("must supply a value"); } text = text.Trim(); // Check for constants. if (text == "true") { return(Constant.TRUE); } if (text == "false") { return(Constant.FALSE); } // Perhaps it's an inverted toggle? if (text.StartsWith(NOT_OPERATOR)) { return(Inverter.of(Require(module, text.Substring(NOT_OPERATOR.Length)))); } // Maybe it's an identifier for a toggle. IToggle found = Identifiers.FindFirst <IToggle>(module.part, text); if (found != null) { return(found); } // Could it be a named-field reference? Identifiers.IFieldEvaluator field = Identifiers.FindKSPField(module.part, text); if (field != null) { if (!typeof(bool).IsAssignableFrom(field.FieldType)) { throw new ArgumentException("Can't use " + text + " as a boolean field (it's of type " + field.FieldType.Name + ")"); } return(new NamedField(field)); } // Perhaps it's a parameterized expression? ParsedParameters parsedParams = ParsedParameters.TryParse(text); if (parsedParams != null) { for (int i = 0; i < PARSEABLE_TOGGLES.Length; ++i) { IToggle parsed = PARSEABLE_TOGGLES[i](module, parsedParams); if (parsed != null) { return(parsed); } } } // Nope, not parseable. throw new ArgumentException("Invalid toggle syntax \"" + text + "\""); }
/// <summary> /// Parse a static expression thus: /// /// sqrt(arg) /// </summary> /// <param name="module"></param> /// <param name="parameters"></param> /// <returns></returns> private static double TryParseSquareRoot(PartModule module, ParsedParameters parameters) { double[] args = TryParseArguments(module, parameters, 1, true, "sqrt"); return((args == null) ? double.NaN : Math.Sqrt(args[0])); }
/// <summary> /// Try to get a random color source from a ParsedParameters. The expected format is: /// /// random(onSource, offSource, periodMillis, bias, seed) /// random(onSource, offSource, periodMillis, bias) /// random(onSource, offSource, periodMillis) /// </summary> public static IColorSource TryParse(PartModule module, ParsedParameters parsedParams) { if (parsedParams == null) { return(null); } if (!TYPE_NAME.Equals(parsedParams.Identifier)) { return(null); } parsedParams.RequireCount(module, 3, 5); IColorSource onSource; try { onSource = FindPrivate(module, parsedParams[0]); } catch (ColorSourceException e) { throw new ColorSourceException(module, TYPE_NAME + "() source has invalid 'on' parameter", e); } IColorSource offSource; try { offSource = FindPrivate(module, parsedParams[1]); } catch (ColorSourceException e) { throw new ColorSourceException(module, TYPE_NAME + "() source has invalid 'off' parameter", e); } long periodMillis; try { periodMillis = (long)Statics.Parse(module, parsedParams[2]); } catch (ArgumentException e) { throw new ColorSourceException(module, TYPE_NAME + "(): Invalid 'period' milliseconds value '" + parsedParams[2] + "': " + e.Message, e); } if (periodMillis < 1) { throw new ColorSourceException(module, TYPE_NAME + "(): 'period' milliseconds must be positive"); } double bias = 0; if (parsedParams.Count > 3) { try { bias = Statics.Parse(module, parsedParams[3]); } catch (ArgumentException e) { throw new ColorSourceException(module, TYPE_NAME + "(): Invalid bias value '" + parsedParams[3] + "': " + e.Message, e); } if ((bias < -1) || (bias > 1)) { throw new ColorSourceException(module, TYPE_NAME + "(): Invalid bias value '" + parsedParams[3] + "' (must be between -1 and 1)"); } } int seed = 0; if (parsedParams.Count > 4) { try { seed = (int)Statics.Parse(module, parsedParams[4]); } catch (ArgumentException e) { throw new ColorSourceException(module, TYPE_NAME + "(): Invalid seed value '" + parsedParams[4] + "': " + e.Message, e); } } if (module.vessel != null) { seed ^= module.vessel.id.GetHashCode(); } if (module.part != null) { seed ^= module.part.flightID.GetHashCode(); seed ^= IndexOf(module).GetHashCode(); } return(new RandomColorSource(onSource, offSource, periodMillis, bias, seed)); }
/// <summary> /// Try to get a pulsate color source from a ParsedParameters. The expected format is: /// /// pulsate(origin, cycleMillis, multiplier) /// pulsate(origin, cycleMillis, multiplier1, multiplier2) /// pulsate(origin, cycleMillis, multiplier1, multiplier2, phase) /// /// ...where multiplier is a float in the range 0 - 1. /// </summary> public static IColorSource TryParse(PartModule module, ParsedParameters parsedParams) { if (parsedParams == null) { return(null); } if (!TYPE_NAME.Equals(parsedParams.Identifier)) { return(null); } parsedParams.RequireCount(module, 3, 5); IColorSource origin; try { origin = FindPrivate(module, parsedParams[0]); } catch (ColorSourceException e) { throw new ColorSourceException(module, TYPE_NAME + "() source has invalid origin", e); } long cycleMillis; try { cycleMillis = (long)Statics.Parse(module, parsedParams[1]); } catch (ArgumentException e) { throw new ColorSourceException(module, TYPE_NAME + "(): Invalid cycle milliseconds value '" + parsedParams[1] + "': " + e.Message, e); } if (cycleMillis < 1) { throw new ColorSourceException(module, TYPE_NAME + "(): cycle milliseconds must be positive"); } float multiplier1; try { multiplier1 = (float)Statics.Parse(module, parsedParams[2]); } catch (ArgumentException e) { throw new ColorSourceException(module, TYPE_NAME + "(): Invalid multiplier value '" + parsedParams[2] + "': " + e.Message, e); } if ((multiplier1 < 0) || (multiplier1 > 1)) { throw new ColorSourceException(module, TYPE_NAME + "(): Invalid multiplier value '" + parsedParams[2] + "' (must be in range 0 - 1)"); } float multiplier2 = 1f; if (parsedParams.Count > 3) { try { multiplier2 = (float)Statics.Parse(module, parsedParams[3]); } catch (ArgumentException e) { throw new ColorSourceException(module, TYPE_NAME + "(): Invalid multiplier value '" + parsedParams[3] + "': " + e.Message, e); } if ((multiplier2 < 0) || (multiplier2 > 1)) { throw new ColorSourceException(module, TYPE_NAME + "(): Invalid multiplier value '" + parsedParams[3] + "' (must be in range 0 - 1)"); } } float phase = 0; if (parsedParams.Count > 4) { try { phase = (float)Statics.Parse(module, parsedParams[4]); } catch (ArgumentException e) { throw new ColorSourceException(module, TYPE_NAME + "(): Invalid phase value '" + parsedParams[4] + "': " + e.Message, e); } } if ((multiplier1 >= 1) && (multiplier2 >= 1)) { return(origin); } if ((multiplier1 <= 0) && (multiplier2 <= 0)) { return(BLACK); } return(new PulsateColorSource(origin, cycleMillis, multiplier1, multiplier2, phase)); }
/// <summary> /// Try to get a blink color source from a ParsedParameters. The expected format is: /// /// blink(onSource, onMillis, offSource, offMillis) /// blink(onSource, onMillis, offSource, offMillis, phase) /// </summary> public static IColorSource TryParse(PartModule module, ParsedParameters parsedParams) { if (parsedParams == null) { return(null); } if (!TYPE_NAME.Equals(parsedParams.Identifier)) { return(null); } parsedParams.RequireCount(module, 4, 5); IColorSource onSource; try { onSource = FindPrivate(module, parsedParams[0]); } catch (ColorSourceException e) { throw new ColorSourceException(module, TYPE_NAME + "() source has invalid 'on' parameter", e); } long onMillis; try { onMillis = (long)Statics.Parse(module, parsedParams[1]); } catch (ArgumentException e) { throw new ColorSourceException(module, TYPE_NAME + "(): Invalid 'on' milliseconds value '" + parsedParams[1] + "': " + e.Message, e); } if (onMillis < 1) { throw new ColorSourceException(module, TYPE_NAME + "(): 'on' milliseconds must be positive"); } IColorSource offSource; try { offSource = FindPrivate(module, parsedParams[2]); } catch (ColorSourceException e) { throw new ColorSourceException(module, TYPE_NAME + "() source has invalid 'off' parameter", e); } long offMillis; try { offMillis = (long)Statics.Parse(module, parsedParams[3]); } catch (ArgumentException e) { throw new ColorSourceException(module, TYPE_NAME + "(): Invalid 'off' milliseconds value '" + parsedParams[3] + "': " + e.Message, e); } if (offMillis < 1) { throw new ColorSourceException(module, TYPE_NAME + "(): 'off' milliseconds must be positive"); } float phase = 0F; if (parsedParams.Count > 4) { try { phase = (float)Statics.Parse(module, parsedParams[4]); } catch (ArgumentException e) { throw new ColorSourceException(module, TYPE_NAME + "(): Invalid phase value '" + parsedParams[4] + "': " + e.Message, e); } } return(new BlinkColorSource(onSource, onMillis, offSource, offMillis, phase)); }
/// <summary> /// Given a color source ID (which might be a literal color, the ID of a controller, /// or a parameterized source), try to find the appropriate source. If it can't /// be parsed or found, throws a ColorSourceException. private static IColorSource FindPrivate(PartModule module, string sourceID) { if (string.IsNullOrEmpty(sourceID)) { throw new ColorSourceException(module, "Null or empty color source"); } // Maybe it's a color string. if (Colors.IsColorString(sourceID)) { return(Constant(Colors.Parse(sourceID, Color.black))); } // Is it a parameterized source? ParsedParameters parsedParams = ParsedParameters.TryParse(sourceID); if (parsedParams != null) { // It has the right syntax for a parameterizeed source. Do we recognize it? for (int i = 0; i < PARSEABLE_SOURCES.Length; ++i) { IColorSource candidate = PARSEABLE_SOURCES[i](module, parsedParams); if (candidate != null) { return(candidate); } } throw new ColorSourceException(module, "Unknown function type '" + parsedParams.Identifier + "'"); } // Maybe it's another field on the module? for (int i = 0; i < module.Fields.Count; ++i) { BaseField field = module.Fields[i]; if (!sourceID.Equals(field.name)) { continue; } if (!ColorSourceIDField.Is(field)) { throw new ColorSourceException( module, sourceID + " field on " + module.ClassName + " of " + module.part.GetTitle() + " is not a color source ID field"); } return(Find(module, field.GetValue <string>(module))); } // Maybe it's a module on the part? for (int i = 0; i < module.part.Modules.Count; ++i) { IColorSource candidate = module.part.Modules[i] as IColorSource; if (candidate == null) { continue; } if (sourceID.Equals(candidate.ColorSourceID)) { return(candidate); } } // not found throw new ColorSourceException(module, "Can't find a color source named '" + sourceID + "'"); }
/// <summary> /// Parse an IScalar from the specified text. Throws ArgumentException if there's a problem. /// </summary> /// <param name="module"></param> /// <param name="text"></param> /// <returns></returns> public static IScalar Require(PartModule module, string text) { if (string.IsNullOrEmpty(text)) { throw new ArgumentException("must supply a value"); } text = text.Trim(); // Perhaps it's an inverted scalar? if (text.StartsWith(INVERT_OPERATOR)) { return(LinearTransform.Invert(Require(module, text.Substring(INVERT_OPERATOR.Length)))); } // Maybe it's an identifier for a scalar. IScalar found = Identifiers.FindFirst <IScalar>(module.part, text); if (found != null) { return(found); } // Could it be a named-field reference? Identifiers.IFieldEvaluator field = Identifiers.FindKSPField(module.part, text); if (field != null) { if (NamedSingleField.Is(field)) { return(new NamedSingleField(field)); } if (NamedDoubleField.Is(field)) { return(new NamedDoubleField(field)); } if (NamedIntegerField.Is(field)) { return(new NamedIntegerField(field)); } if (NamedShortField.Is(field)) { return(new NamedShortField(field)); } if (NamedLongField.Is(field)) { return(new NamedLongField(field)); } throw new ArgumentException("Can't use " + text + " as a scalar field (it's of type " + field.FieldType.Name + ")"); } // Perhaps it's a parameterized expression? ParsedParameters parsedParams = ParsedParameters.TryParse(text); if (parsedParams != null) { for (int i = 0; i < PARSEABLE_SCALARS.Length; ++i) { IScalar parsed = PARSEABLE_SCALARS[i](module, parsedParams); if (parsed != null) { return(parsed); } } } // Last chance: it could be a static value. return(Constant.Of(Statics.Parse(module, text))); }