public Repl Register <T>(string name, T obj) { _global[name] = new NonCallable(obj); return(this); }
public object Execute(string command) { var stack = new List <Token>(); var levels = new List <Level>(); // Kind a parser int GetPriority(string newToken) { switch (newToken) { case "=": return(1); case "*": case "/": return(4); case "+": case "-": return(5); default: return(15); } } void Reduce <T>(int offset, Func <Token, T> init, Dictionary <string, Func <T, Token, T> > reducers) { var result = init(stack[offset]); stack.RemoveAt(offset); while (offset < stack.Count) { if (reducers.TryGetValue(stack[offset].AsString, out var reducer)) { stack.RemoveAt(offset); result = reducer(result, stack[offset]); stack.RemoveAt(offset); } else { break; } } stack.Insert(offset, new Token(TokenType.Scalar, result)); } void GoDeep(TokenType type, int take, int priority) { while (levels.Count > 0 && levels[levels.Count - 1].Type != TokenType.Bracket && levels[levels.Count - 1].Priority < priority) { GoUp(); } if (levels.Count == 0 || levels[levels.Count - 1].Priority != priority) { levels.Add(new Level(type, stack.Count - 1 - take, priority)); } } void GoUp() { var last = levels[levels.Count - 1]; switch (last.Type) { case TokenType.Bracket: if (last.Offset > 0 && stack[last.Offset - 1].Type == TokenType.Identifier) { List <Token> args = stack[last.Offset + 1].Type != TokenType.Bracket ? stack[last.Offset + 1].Value as List <Token> ?? new List <Token> { stack[last.Offset + 1] } : new List <Token>(); var result = Call(stack[last.Offset - 1].AsString, args); stack.RemoveRange(last.Offset - 1, stack.Count - last.Offset + 1); stack.Insert(last.Offset - 1, new Token(TokenType.Scalar, result)); } else { var value = GetValue(stack[last.Offset + 1]); stack.RemoveRange(last.Offset, 3); stack.Insert(last.Offset, new Token(TokenType.Scalar, value)); } break; case TokenType.Delimiter: Reduce(last.Offset, x => new List <Token> { stack[last.Offset] }, new Dictionary <string, Func <List <Token>, Token, List <Token> > > { [","] = (s, x) => { s.Add(stack[last.Offset]); return(s); } }); break; case TokenType.Operation: if (stack[last.Offset + 1].AsString == "=") { // TODO implement right-to-left a = b = c var nonCallable = (NonCallable)Get(stack[last.Offset].AsString); if (nonCallable != null) { nonCallable.Value = GetValue(stack[last.Offset + 2]); } else { _global[stack[last.Offset].AsString] = nonCallable = new NonCallable(GetValue(stack[last.Offset + 2])); } stack.RemoveRange(last.Offset, 3); stack.Insert(last.Offset, new Token(TokenType.Scalar, nonCallable)); } else if (stack[last.Offset + 1].AsString == "+" || stack[last.Offset + 1].AsString == "-") { Reduce(last.Offset, x => Convert.ToDouble(GetValue(stack[last.Offset])), new Dictionary <string, Func <double, Token, double> > { ["+"] = (s, x) => s + Convert.ToDouble(GetValue(x)), ["-"] = (s, x) => s - Convert.ToDouble(GetValue(x)), }); } else if (stack[last.Offset + 1].AsString == "*" || stack[last.Offset + 1].AsString == "/") { Reduce(last.Offset, x => (double)GetValue(stack[last.Offset]), new Dictionary <string, Func <double, Token, double> > { ["*"] = (s, x) => s * Convert.ToDouble(GetValue(x)), ["/"] = (s, x) => s / Convert.ToDouble(GetValue(x)), }); } break; } levels.RemoveAt(levels.Count - 1); } // Kind a lexer var position = 0; var escaped = false; char Take(int offset) => position + offset < command.Length ? command[position + offset] : '\0'; bool IsScalarStart(char x) => x >= '0' && x <= '9' || x == '.' || x == '-'; bool IsScalarCont(char x) => x >= '0' && x <= '9' || x == '.'; bool IsIdentifierStart(char x) => x >= 'a' && x <= 'z' || x >= 'A' && x <= 'Z'; bool IsIdentifierCont(char x) => IsIdentifierStart(x) || IsScalarCont(x); bool IsStringStart(char x) => x == '\"' || x == '\''; bool IsStringCont(char start, char x) { if (x != start || escaped) { escaped = !escaped && x == '\\'; return(true); } return(false); } while (position < command.Length) { if (IsIdentifierStart(Take(0))) { var length = 1; while (IsIdentifierCont(Take(length))) { length += 1; } stack.Add(new Token(TokenType.Identifier, command.Substring(position, length))); position += length; } else if (IsScalarStart(Take(0))) { var length = 1; while (IsScalarCont(Take(length))) { length += 1; } stack.Add(new Token(TokenType.Scalar, double.Parse(command.Substring(position, length)))); position += length; } else if (IsStringStart(Take(0))) { var length = 1; while (IsStringCont(Take(0), Take(length))) { length += 1; } length += 1; stack.Add(new Token(TokenType.Scalar, command.Substring(position + 1, length - 2))); position += length; } else if (Take(0) == '=' || Take(0) == '+' || Take(0) == '-' || Take(0) == '*' || Take(0) == '/') { stack.Add(new Token(TokenType.Operation, command.Substring(position, 1))); GoDeep(TokenType.Operation, 1, GetPriority(Take(0).ToString())); position += 1; } else if (Take(0) == '(' || Take(0) == ')') { stack.Add(new Token(TokenType.Bracket, command.Substring(position, 1))); if (Take(0) == '(') { GoDeep(TokenType.Bracket, 0, 0); } else { while (levels[levels.Count - 1].Type != TokenType.Bracket) { GoUp(); } GoUp(); } position += 1; } else if (Take(0) == ',') { stack.Add(new Token(TokenType.Delimiter, command.Substring(position, 1))); GoDeep(TokenType.Delimiter, 1, 16); position += 1; } else if (char.IsWhiteSpace(Take(0))) { position += 1; } else { throw new ArgumentException($"Unexpected symbol at position {position}: {command.Substring(0, position + 1)}"); } } while (levels.Count > 0) { GoUp(); } if (stack.Count == 0) { return(null); } return(GetValue(stack[0])); }