public static IArgument String(CharSource source, ref char chr) { var str = new StringBuilder(); while (source.Advance(out chr, true)) { if (chr == '\\') { str.Append(chr); if (!source.Advance(out chr, true)) { throw new FinalCharacterException("Expected escaped character after '\\'"); } str.Append(chr); } else if (chr == '"') { // If this fails (i.e. the quote is the last character), future calls to skip space will fail source.Advance(out chr); return(new StringLiteral() { Value = Regex.Unescape(str.ToString()) }); } else { str.Append(chr); } } throw new FinalCharacterException("Expected closing '\"'"); }
public static IArgument TypeName(CharSource source, ref char chr) { // Arrive on seeing '/', get collections of names separated by '.' if (!source.Advance(out chr)) { throw new FinalCharacterException("Expected type name"); } var str = new StringBuilder(); while (true) { str.Append(source.ReadName(ref chr)); if (chr != '.') { return(new TypeNameLiteral() { Value = str.ToString() }); } str.Append('.'); // We now expect more if (!source.Advance(out chr)) { throw new FinalCharacterException("Expected further type name"); } if (!CharSource.IsNameStart(chr)) { throw new InvalidCharacterException("Expected name start character: found " + chr); } } }
private static string[] AssignTargets(CharSource source, ref char chr, Dictionary <string, IFunction> funcs) { // Seen '=' if (!source.Advance(out chr)) { throw new FinalCharacterException("Expected assignment token, \"=>\""); } else if (chr != '>') { throw new InvalidCharacterException($"Expected assignment token, \"=>\": found \"={chr}\""); } if (!source.AdvanceWhiteSpace(out chr)) { throw new FinalCharacterException("Expected assignment"); } var right = Arguments.Multiple(source, ref chr, funcs).Evaluate(); if (!right.All(t => t is NameArgument)) { throw new InvalidArgumentException($"Assigned types must be names"); } var names = right.Select(t => (t as NameArgument).Value).ToArray(); return(names); }
public static IArgument Function(CharSource source, ref char chr, Dictionary <string, IFunction> funcs) { // Arrive here having seen '(' if (!source.AdvanceWhiteSpace(out chr)) { throw new FinalCharacterException("Expected a function name"); } var name = Arguments.Single(source, ref chr, funcs); if (!(name is NameArgument nameArg)) { throw new InvalidArgumentException($"Function name type error: Type = {name.GetType().Name}, Value = {name.ToString()}"); } if (!funcs.TryGetValue(nameArg.Value, out var func)) { throw new InvalidArgumentException($"Function does not exist: \"{nameArg.Value}\""); } // Get next character: if (!source.SkipWhiteSpace(ref chr)) { throw new FinalCharacterException("Expected closing ')'"); } var funcArg = new FunctionArgument() { Call = new FunctionCall(0) // Line number doesn't really matter, as this isn't a whole statement { Function = func } }; if (chr == ':') { // Arguments to go with it if (!source.AdvanceWhiteSpace(out chr)) { throw new FinalCharacterException("Expected argument list"); } funcArg.Call.Arguments = Arguments.Multiple(source, ref chr, funcs); // Get closing bracket if (!source.SkipWhiteSpace(ref chr)) { throw new FinalCharacterException("Expected closing ')'"); } else if (chr != ')') { throw new InvalidCharacterException($"Expected closing ')': found '{chr}'"); } } else if (chr != ')') { throw new InvalidCharacterException($"Expected closing ')' or arguments (':'): found '{chr}'"); } source.Advance(out chr); return(funcArg); }
public static IArgument Number(CharSource source, ref char chr) { var integral = source.ReadInt(ref chr); if (chr == '.') { if (!source.Advance(out chr)) { throw new FinalCharacterException("Expected fractional value after '.'"); } else if (!char.IsDigit(chr)) { throw new InvalidCharacterException($"Expected fractional value after '.': found '{chr}'"); } var fractional = source.ReadFractional(ref chr); var mantissa = integral >= 0 ? integral + fractional : integral - fractional; if (char.ToLower(chr) == 'e') { if (!source.AdvanceWhiteSpace(out chr)) { throw new FinalCharacterException("Expected exponent after 'e'"); } else if (!CharSource.IsNumberStart(chr)) { throw new InvalidCharacterException($"Expected exponent after 'e': found '{chr}'"); } var exponent = source.ReadInt(ref chr); mantissa *= Math.Pow(10, exponent); } return(new DoubleLiteral() { Value = mantissa }); } return(new IntegerLiteral() { Value = integral }); }
public static IArgument Executable(CharSource source, ref char chr, Dictionary <string, IFunction> funcs) { // Having seen '<' if (!source.AdvanceWhiteSpace(out chr)) { throw new FinalCharacterException("Expected statements, or closing '>'"); } var exec = new ExecutableArgument(); // Statement reading takes us to the first character of the next potential while (true) { if (chr == '>') { source.Advance(out chr); return(exec); } var statement = Statement(source, ref chr, funcs); exec.Value.Add(statement); } }
public static ArgumentList Multiple(CharSource source, ref char chr, Dictionary <string, IFunction> funcs) { var args = new ArgumentList(); // Typically get here having seen nothing or the first character already // Running out of text here is not an error, unless specified as more to come while (source.SkipWhiteSpace(ref chr)) { var arg = Single(source, ref chr, funcs); args.Add(arg); if (!source.SkipWhiteSpace(ref chr)) { return(args); } else if (chr != ',') { return(args); } source.Advance(out chr); } // No need to throw here, as if more expected single argument parsing will catch return(args); }