/// <summary>Transform a node into something we can use as a body</summary> internal static Value UseNodeAsBody(DelimiterNode bodyNode) { if (bodyNode.List.Delimiter.DelimiterType == DelimiterType.AsArray) { List<DelimiterList> body = new List<DelimiterList>(); List<DelimiterNode> originalList = bodyNode.List.Nodes; foreach (DelimiterNode node in originalList) { IScope scope = node.List.Scope; DelimiterList dList = new DelimiterList(ValueDelimiter.Line, node.List.Nodes, 0, "", node.ToString(), scope); body.Add(dList); } return new ValueLine(body, bodyNode.List.Scope); } else { IScope scope = bodyNode.List.Scope; DelimiterList dList = new DelimiterList(ValueDelimiter.Line, bodyNode.List.Nodes, 0, "", bodyNode.ToString(), scope); List<DelimiterList> body = new List<DelimiterList>(); body.Add(dList); return new ValueLine(body, scope); } }
internal override Value Eval(Value arg, IScope scope) { Map map = arg.AsMap; Value collection = map["collection"]; // todo: consolidate these bodies, one from an explicit param & one from the following lines Value valueBody = map.ContainsKey("body") ? map["body"] : map[ValueFunction.keyBody]; IScope bodyScope = scope; if (valueBody is ValueRaw) { IScope tempScope = (valueBody as ValueRaw).Scope; if (tempScope != null) bodyScope = tempScope; } // todo: abstract iteration to avoid these ifs Value result = ValueNil.Nil; if (collection is ValueString) { string s = collection.AsString; foreach (char c in s) { IScope local = new ScopeChain(bodyScope); PatternAssign assign = new PatternAssign(map, local, true/*bCreate*/); assign.Assign(new ValueString(c.ToString())); result = EvalBody.Do(valueBody, local); local.Exit(); } } else if (collection is ValueArray) { List<Value> list = collection.AsArray; foreach (Value v in list) { IScope local = new ScopeChain(bodyScope); PatternAssign assign = new PatternAssign(map, local, true/*bCreate*/); assign.Assign(v); result = EvalBody.Do(valueBody, local); local.Exit(); } } else if (collection is ValueMap) { Dictionary<string, Value> dict = collection.AsMap.Raw; foreach (string key in dict.Keys) { List<Value> list = new List<Value>(); list.Add(new ValueString(key)); list.Add(dict[key]); IScope local = new ScopeChain(bodyScope); PatternAssign assign = new PatternAssign(map, local, true/*bCreate*/); assign.Assign(new ValueArray(list)); result = EvalBody.Do(valueBody, local); local.Exit(); } } else if (collection is ValueLine) { List<DelimiterList> list = collection.AsLine; // if delimiter is specified, wrap each line w/ it ValueDelimiter delim = scope.GetValue(new Token(map["delim"].AsString)) as ValueDelimiter; if (delim != null) { List<DelimiterList> delimList = new List<DelimiterList>(); int indent = (list.Count > 0 ? list[0].Indent : 0); foreach (DelimiterList line in list) { DelimiterList newLine = line; // wrap lines & nested lines with proper delimiter, except for nested values that get evaled if (line.Indent == indent || (delim.DelimiterType != DelimiterType.AsValue && line.Indent >= indent)) { List<DelimiterNode> nodes = new List<DelimiterNode>(); string original = line.Original; if (delim.DelimiterType == DelimiterType.AsString && line.Indent > indent) { // put the indentation back if portions of the body were indented original = ""; for (int i = 0; i < line.Indent - indent; i++) original += " "; original += line.Original; } nodes.Add(new DelimiterNodeList(new DelimiterList(delim, line.Nodes, line.Indent, "", original, line.Scope))); newLine = new DelimiterList(delim, nodes, indent, "", original, line.Scope); } delimList.Add(newLine); } list = delimList; } // for each line, eval it then eval the body ILineRequestor lines = new LineConsumer(list); while (lines.HasCurrent()) { IScope local = new ScopeChain(bodyScope); Value value = EvalLines.DoOne(lines, local); PatternAssign assign = new PatternAssign(map, local, true/*bCreate*/); assign.Assign(value); result = EvalBody.Do(valueBody, local); local.Exit(); } } return result; }
internal DelimiterNodeList(DelimiterList tree) { m_list = tree; }
// simply search for end and stuff everything in the middle into a single token private static bool ParseString(int indent, string[] strs, int iStart, ValueDelimiter thisDelim, List<DelimiterNode> nodes, out int iEnd, out DelimiterList result) { iEnd = -1; bool bExtra = false; for (int i = iStart; i < strs.Length; i++) { string s = strs[i]; if (s == thisDelim.End) { iEnd = i; break; } else if (s.Substring(s.Length - 1, 1) == thisDelim.End) { iEnd = i + 1; bExtra = true; break; } } // no specified end delim means take the remainder of the line if (thisDelim.End == "") iEnd = strs.Length; // if we found end, wrap entire string in a single node if (iEnd != -1) { string subStr = GetSubStr(iStart, iEnd, strs); if (bExtra) { subStr = subStr.Substring(0, subStr.Length - 1); --iEnd; } Token token = new Token(subStr); DelimiterNode node = new DelimiterNodeToken(token); nodes.Add(node); result = new DelimiterList(thisDelim, nodes, indent, strs[iStart - 1], subStr, null); return true; } result = null; return false; }
// handle as individual tokens and nested lists private static bool ParseMisc(int indent, string original, string[] strs, int iStart, ValueDelimiter thisDelim, IParseLineDelimiters delims, ILineRequestor requestor, List<DelimiterNode> nodes, out int iEnd, out DelimiterList result) { result = null; iEnd = strs.Length; for (int i = iStart; i < strs.Length; i++) { string s = strs[i]; // is this the end of current set of delimited tokens? if (s == thisDelim.End) { // end delimiter iEnd = i; string subStr = GetSubStr(iStart, iEnd, strs); result = new DelimiterList(thisDelim, nodes, indent, strs[iStart - 1], subStr, null); return true; } // TODO: rework this so I don't need to check for : (e.g. for :}, when creating a delim) if (s.Substring(s.Length - 1, 1) == thisDelim.End && (s[0] != ':' || s.Length > 2)) { // end delimiter is part of final token iEnd = i + 1; string without = s.Substring(0, s.Length - 1); Token token = new Token(without); DelimiterNode node = new DelimiterNodeToken(token); nodes.Add(node); strs[i] = without; string subStr = GetSubStr(iStart, iEnd, strs); result = new DelimiterList(thisDelim, nodes, indent, strs[iStart - 1], subStr, null); strs[i] = s; --iEnd; return true; } // is it a stand alone starting delimiter? bool bAnyToken = false; ValueDelimiter subDelim = (delims == null ? null : delims.GetDelim(s, out bAnyToken)); string[] strsToUse = strs; bool bExtra = true; if (subDelim == null && !bAnyToken) { // whole thing wasn't a delimiter, function, etc., how about the 1st char? string s1 = s.Substring(0, 1); subDelim = (delims == null ? null : delims.GetDelim(s1, out bAnyToken)); if (subDelim != null) { // copy across array, but break iStart into delim & remainder bExtra = false; strsToUse = new string[strs.Length + 1]; for (int j = 0; j < i; j++) strsToUse[j] = strs[j]; strsToUse[i] = s1; strsToUse[i + 1] = s.Substring(1); for (int j = i + 1; j < strs.Length; j++) strsToUse[j + 1] = strs[j]; } } if (subDelim != null) { // start delimiter int end; DelimiterList sublist = Do(0, original, strsToUse, i + 1, subDelim, delims, requestor, out end); if (sublist != null) { DelimiterNodeList node = new DelimiterNodeList(sublist); nodes.Add(node); } // skip past the sublist i = (bExtra ? end : end - 1); } else { // stand alone token Token token = new Token(s); DelimiterNode node = new DelimiterNodeToken(token); nodes.Add(node); } } return false; }
// ignore everything up to the end delimiter private static bool ParseComment(string[] strs, int iStart, ValueDelimiter thisDelim, out int iEnd, out DelimiterList result) { result = null; for (int i = iStart; i < strs.Length; i++) { if (strs[i] == thisDelim.End) { iEnd = i; return true; } } iEnd = strs.Length; return false; }