private static string ParseExpression(string code, string id, ParseMode mode, bool whenCondition, DekiScriptEnv env, DekiScriptRuntime runtime, Dictionary<string, string> channels, ref int i) { StringBuilder result = new StringBuilder(); int nesting = 0; for(; i < code.Length; ++i) { int start; switch(code[i]) { case '"': case '\'': // process strings start = i; ScanString(code, code[i], ref i); result.Append(code, start, i - start); --i; break; case '/': // check if / denotes the beginning of a comment, if so process it start = i; if(TryScanComment(code, ref i)) { // NOTE: remove comments in when-condition if(!whenCondition) { result.Append(code, start, i - start); result.Append("\n"); } --i; } else { result.Append(code[i]); } break; case '\\': // backslash (\) always appends the next character result.Append(code[i++]); if(i < code.Length) { result.Append(code[i]); } break; case '(': // increase nesting level result.Append(code[i]); ++nesting; break; case '{': // check if this is the beginning of a dekiscript block {{ }} if(((i + 1) < code.Length) && (code[i + 1] == '{')) { ++i; string value; start = i; if(TryParseDekiScriptExpression(code, env, runtime, ref i, out value)) { result.Append(value); } else { ++nesting; result.Append('{'); result.Append(code, start, i - start); --i; } } else { ++nesting; result.Append(code[i]); } break; case ')': case '}': // decrease nesting level and check if this is the end of the sougth expression result.Append(code[i]); --nesting; // NOTE: only exit if // 1) we don't have to read all of the code // 2) there are no open parentheses or cruly braces // 3) we don't on a complete statement or the current characteris a closing curly brace if((mode != ParseMode.ALL) && (nesting <= 0) && ((mode != ParseMode.STATEMENT) || (code[i] == '}'))) { // found the end of the expression ++i; return result.ToString(); } break; case ';': // check if the statement is the end of the sougth expression result.Append(code[i]); // NOTE: only exit if // 1) we don't have to read all of the code // 2) there are no open parentheses or cruly braces // 3) we stop on a complete statement if((nesting <= 0) && (mode == ParseMode.STATEMENT)) { // found the end of the expression ++i; return result.ToString(); } break; case '@': // channel name if(channels != null) { ++i; start = i; string channel; string name; if((i < code.Length) && ((code[i] == '"') || (code[i] == '\''))) { // process: @"channel_name" or @'channel_name' ScanString(code, code[i], ref i); channel = code.Substring(start, i - start); name = channel.Substring(1, channel.Length - 2).UnescapeString(); } else { // process: @channel_magic_id ScanId(code, ref i); name = code.Substring(start, i - start); if(!channels.TryGetValue(name, out channel)) { channel = env.GetMagicId(name).ToString(); } } start = i; ScanWhitespace(code, ref i); if((i < code.Length) && (code[i] == '(')) { // process: @channel ( ... ) string message = ParseExpression(code, id, ParseMode.EXPRESSION, false, env, runtime, channels, ref i); message = message.Substring(1, message.Length - 2).Trim(); if(message.Length == 0) { result.AppendFormat("Deki.publish({0})", channel); } else { result.AppendFormat("Deki.publish({0}, {1})", channel, message); } } else { // channel is used for reading; add it to the channel set to read on activation channels[name] = channel; // convert channel name and add whitespace result.AppendFormat("$channels[{0}]", name.QuoteString()); result.Append(code, start, i - start); } --i; } else { result.Append(code[i]); } break; case '#': // NOTE: don't process #id in the when-condition // element name if(!whenCondition && (channels != null)) { ++i; start = i; // process: #id ScanId(code, ref i); string name = code.Substring(start, i - start); result.Append("$(\"#" + name + "\")"); --i; } else { result.Append(code[i]); } break; default: // NOTE: don't process when() in the when-condition // check if this is the beginning of an identifier if(!whenCondition && IsAlpha(code[i])) { start = i; ScanId(code, ref i); int j = i; ScanWhitespace(code, ref j); // check if scanned identifier is the keyword 'when' if(((i - start) == WHEN.Length) && (string.Compare(code, start, WHEN, 0, WHEN.Length, StringComparison.Ordinal) == 0) && (j < code.Length) && (code[j] == '(')) { i = j; Dictionary<string, string> subChannels = new Dictionary<string, string>(); // parse the condition of the 'when()' statement string condition = ParseExpression(code, id, ParseMode.EXPRESSION, true, env, runtime, subChannels, ref i); // parse the body of the 'when()' expression string body = ParseExpression(code, id, ParseMode.STATEMENT, false, env, runtime, subChannels, ref i); BuildWhenStatement(condition.Trim(), id, body.Trim(), result, env, subChannels); } else { result.Append(code, start, i - start); } --i; } else { result.Append(code[i]); } break; } } return result.ToString(); }