protected ScriptVariable(string name, ScriptVariableScope scope) { Name = name; Scope = scope; unchecked { _hashCode = (Name.GetHashCode() * 397) ^ (int)Scope; } }
private void PushVariableScope(ScriptVariableScope scope) { Debug.Assert(scope != ScriptVariableScope.Global); var store = _availableStores.Count > 0 ? _availableStores.Pop() : new ScriptObject(); if (scope == ScriptVariableScope.Local) { PushLocalContext(store); } else { _currentLocalContext.Loops.Push(store); } }
public static ScriptVariable Create(string name, ScriptVariableScope scope) { switch (scope) { case ScriptVariableScope.Global: return(new ScriptVariableGlobal(name)); case ScriptVariableScope.Local: return(new ScriptVariableLocal(name)); default: throw new InvalidOperationException($"Scope `{scope}` is not supported"); } }
/// <summary> /// Push a new <see cref="ScriptVariableScope"/> for variables /// </summary> /// <param name="scope"></param> internal void PushVariableScope(ScriptVariableScope scope) { var store = _availableStores.Count > 0 ? _availableStores.Pop() : new ScriptObject(); var tags = _availableTags.Count > 0 ? _availableTags.Pop() : new Dictionary <object, object>(); if (scope == ScriptVariableScope.Local) { _localStores.Push(store); _localTagsStack.Push(tags); } else { _loopStores.Push(store); _loopTagsStack.Push(tags); } }
/// <summary> /// Pops a previous <see cref="ScriptVariableScope"/>. /// </summary> /// <param name="scope"></param> internal void PopVariableScope(ScriptVariableScope scope) { var stores = (scope == ScriptVariableScope.Local ? _localStores : _loopStores); if (stores.Count == 0) { // Should not happen at runtime throw new InvalidOperationException("Invalid number of matching push/pop VariableScope."); } var store = stores.Pop(); // The store is cleanup once it is pushed back store.Clear(); _availableStores.Push(store); }
/// <summary> /// Creates a <see cref="ScriptVariable"/> according to the specified name and <see cref="ScriptVariableScope"/> /// </summary> /// <param name="name">Name of the variable</param> /// <param name="scope">Scope of the variable</param> /// <returns>The script variable</returns> public static ScriptVariable Create(string name, ScriptVariableScope scope) { switch (scope) { case ScriptVariableScope.Global: return(new ScriptVariableGlobal(name)); case ScriptVariableScope.Local: return(new ScriptVariableLocal(name)); case ScriptVariableScope.Loop: return(new ScriptVariableLoop(name)); default: throw new InvalidOperationException(string.Format(RS.UnsupportedScope, scope)); } }
/// <summary> /// Pops a previous <see cref="ScriptVariableScope"/>. /// </summary> /// <param name="scope"></param> internal void PopVariableScope(ScriptVariableScope scope) { Dictionary <object, object> tags; if (scope == ScriptVariableScope.Local) { PopVariableScope(ref _localStores); tags = _localTagsStack.Pop(); } else { PopVariableScope(ref _loopStores); tags = _loopTagsStack.Pop(); } // Make sure that tags are clear tags.Clear(); _availableTags.Push(tags); }
protected ScriptVariable(string name, ScriptVariableScope scope) { BaseName = name; Scope = scope; switch (scope) { case ScriptVariableScope.Global: Name = name; break; case ScriptVariableScope.Local: Name = $"${name}"; break; } unchecked { _hashCode = (BaseName.GetHashCode() * 397) ^ (int)Scope; } }
/// <summary> /// Returns the list of <see cref="ScriptObject"/> depending on the scope of the variable. /// </summary> /// <param name="variable"></param> /// <exception cref="NotImplementedException"></exception> /// <returns>The list of script objects valid for the specified variable scope</returns> private IEnumerable <IScriptObject> GetStoreForSet(ScriptVariable variable) { ScriptVariableScope scope = variable.Scope; switch (scope) { case ScriptVariableScope.Global: for (int i = _globalStores.Count - 1; i >= 0; i--) { yield return(_globalStores.Items[i]); } break; case ScriptVariableScope.Local: if (_localStores.Count > 0) { yield return(_localStores.Peek()); } else { throw new ScriptRuntimeException(variable.Span, string.Format(RS.InvalidLocalVariableInContext, variable)); } break; case ScriptVariableScope.Loop: if (_loopStores.Count > 0) { yield return(_loopStores.Peek()); } else { // unit test: 215-for-special-var-error1.txt throw new ScriptRuntimeException(variable.Span, string.Format(RS.InvalidLoopVariableOutsideLoop, variable)); } break; default: throw new NotImplementedException(string.Format(RS.InvalidVariableScope, scope)); } }
private void PopVariableScope(ScriptVariableScope scope) { Debug.Assert(scope != ScriptVariableScope.Global); if (scope == ScriptVariableScope.Local) { var local = PopLocalContext(); local.Clear(); _availableStores.Push(local); } else { if (_currentLocalContext.Loops.Count == 0) { // Should not happen at runtime throw new InvalidOperationException("Invalid number of matching push/pop VariableScope."); } var store = _currentLocalContext.Loops.Pop(); // The store is cleanup once it is pushed back store.Clear(); _availableStores.Push(store); } }
private static void ParseHeaderNodeDefNode(ScriptProgram program, ScriptNode root, TokenStream stream) { string variablename = ""; string defaultValue = ""; // To be converted later Guid variableId = Guid.Empty; ScriptVariableType variableType = ScriptVariableType.Unknown; ScriptVariableScope variableScope = ScriptVariableScope.Unknown; var parameter = stream.Pop(); while (parameter.TokenType == SemanticTokenType.NodeParameter) { //var argumentValue = stream.Pop(); if (parameter.TokenValue == "name:") { var value = stream.Pop(); variablename = value.TokenValue; } else if (parameter.TokenValue == "type:") { var value = stream.Pop(); if (value.TokenValue == "flag") { variableType = ScriptVariableType.Flag; } else if (value.TokenValue == "int") { variableType = ScriptVariableType.Integer; } else { throw new Exception("Unsupported variable type:" + value.TokenValue); } } else if (parameter.TokenValue == "scope:") { var value = stream.Pop(); if (value.TokenValue == "global") { variableScope = ScriptVariableScope.Global; } else if (value.TokenValue == "local") { variableScope = ScriptVariableScope.Local; } else { throw new Exception("Unsupported scope:" + value); } } else if (parameter.TokenValue == "default:") { var value = stream.Pop(); defaultValue = value.TokenValue; } else { throw new Exception("Unknown parameter:" + parameter.TokenValue); } var nexttype = stream.GetCurrent().TokenType; if (nexttype != SemanticTokenType.NodeParameter) { break; } parameter = stream.Pop(); } // TODO Look up guid properly Console.WriteLine("TODO Look up guid for variable name"); if (variableId == Guid.Empty) { variableId = Guid.NewGuid(); } switch (variableType) { case ScriptVariableType.Integer: { var value = int.Parse(defaultValue); if (variableScope == ScriptVariableScope.Global) { program.GlobalVariables.AddVariable(variableId, variableType, value, variablename); } else if (variableScope == ScriptVariableScope.Local) { root.LocalVariables.AddVariable(variableId, variableType, value, variablename); } break; } case ScriptVariableType.Flag: { var value = bool.Parse(defaultValue); if (variableScope == ScriptVariableScope.Global) { program.GlobalVariables.AddVariable(variableId, variableType, value, variablename); } else if (variableScope == ScriptVariableScope.Local) { root.LocalVariables.AddVariable(variableId, variableType, value, variablename); } else { throw new Exception("Unsupported Variable"); } break; } default: throw new Exception("Unsupported VariableType:" + variableType); } }
/// <summary> /// Push a new <see cref="ScriptVariableScope"/> for variables /// </summary> /// <param name="scope"></param> internal void PushVariableScope(ScriptVariableScope scope) { var store = _availableStores.Count > 0 ? _availableStores.Pop() : new ScriptObject(); (scope == ScriptVariableScope.Local ? _localStores : _loopStores).Push(store); }
private ScriptExpression ParseVariable() { Token currentToken = Current; SourceSpan currentSpan = CurrentSpan; SourceSpan endSpan = currentSpan; string text = GetAsText(currentToken); // Return ScriptLiteral for null, true, false // Return ScriptAnonymousFunction switch (text) { case "null": ScriptLiteral nullValue = Open <ScriptLiteral>(); NextToken(); return(Close(nullValue)); case "true": ScriptLiteral trueValue = Open <ScriptLiteral>(); trueValue.Value = true; NextToken(); return(Close(trueValue)); case "false": ScriptLiteral falseValue = Open <ScriptLiteral>(); falseValue.Value = false; NextToken(); return(Close(falseValue)); case "do": ScriptAnonymousFunction functionExp = Open <ScriptAnonymousFunction>(); functionExp.Function = ParseFunctionStatement(true); ScriptAnonymousFunction func = Close(functionExp); return(func); case "this": if (!_isLiquid) { ScriptThisExpression thisExp = Open <ScriptThisExpression>(); NextToken(); return(Close(thisExp)); } break; } // Keeps trivia before this token List <ScriptTrivia> triviasBefore = null; if (_isKeepTrivia && _trivias.Count > 0) { triviasBefore = new List <ScriptTrivia>(); triviasBefore.AddRange(_trivias); _trivias.Clear(); } NextToken(); ScriptVariableScope scope = ScriptVariableScope.Global; if (text.StartsWith("$")) { scope = ScriptVariableScope.Local; text = text.Substring(1); // Convert $0, $1... $n variable into $[0] $[1]...$[n] variables int index; if (int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out index)) { ScriptIndexerExpression indexerExpression = new ScriptIndexerExpression { Span = currentSpan, Target = new ScriptVariableLocal(ScriptVariable.Arguments.Name) { Span = currentSpan }, Index = new ScriptLiteral() { Span = currentSpan, Value = index } }; if (_isKeepTrivia) { if (triviasBefore != null) { indexerExpression.Target.AddTrivias(triviasBefore, true); } FlushTrivias(indexerExpression.Index, false); } return(indexerExpression); } } else if (text == "for" || text == "while" || text == "tablerow" || (_isLiquid && (text == "forloop" || text == "tablerowloop"))) { if (Current.Type == TokenType.Dot) { NextToken(); if (Current.Type == TokenType.Identifier) { endSpan = CurrentSpan; string loopVariableText = GetAsText(Current); NextToken(); scope = ScriptVariableScope.Loop; if (_isLiquid) { switch (loopVariableText) { case "first": text = ScriptVariable.LoopFirst.Name; break; case "last": text = ScriptVariable.LoopLast.Name; break; case "index0": text = ScriptVariable.LoopIndex.Name; break; case "rindex0": text = ScriptVariable.LoopRIndex.Name; break; case "rindex": case "index": // Because forloop.index is 1 based index, we need to create a binary expression // to support it here bool isrindex = loopVariableText == "rindex"; ScriptNestedExpression nested = new ScriptNestedExpression() { Expression = new ScriptBinaryExpression() { Operator = ScriptBinaryOperator.Add, Left = new ScriptVariableLoop(isrindex ? ScriptVariable.LoopRIndex.Name : ScriptVariable.LoopIndex.Name) { Span = currentSpan }, Right = new ScriptLiteral(1) { Span = currentSpan }, Span = currentSpan }, Span = currentSpan }; if (_isKeepTrivia) { if (triviasBefore != null) { nested.AddTrivias(triviasBefore, true); } FlushTrivias(nested, false); } return(nested); case "length": text = ScriptVariable.LoopLength.Name; break; case "col": if (text != "tablerowloop") { // unit test: 108-variable-loop-error2.txt LogError(currentToken, string.Format(RS.InvalidLoopVariable, text + ".col")); } text = ScriptVariable.TableRowCol.Name; break; default: text = text + "." + loopVariableText; LogError(currentToken, string.Format(RS.InvalidLiquidLoopVariable, text)); break; } } else { switch (loopVariableText) { case "first": text = ScriptVariable.LoopFirst.Name; break; case "last": if (text == "while") { // unit test: 108-variable-loop-error2.txt LogError(currentToken, string.Format(RS.InvalidLoopVariable, "while.last")); } text = ScriptVariable.LoopLast.Name; break; case "changed": if (text == "while") { // unit test: 108-variable-loop-error2.txt LogError(currentToken, string.Format(RS.InvalidLoopVariable, "while.changed")); } text = ScriptVariable.LoopChanged.Name; break; case "length": if (text == "while") { // unit test: 108-variable-loop-error2.txt LogError(currentToken, string.Format(RS.InvalidLoopVariable, "while.length")); } text = ScriptVariable.LoopLength.Name; break; case "even": text = ScriptVariable.LoopEven.Name; break; case "odd": text = ScriptVariable.LoopOdd.Name; break; case "index": text = ScriptVariable.LoopIndex.Name; break; case "rindex": if (text == "while") { // unit test: 108-variable-loop-error2.txt LogError(currentToken, string.Format(RS.InvalidLoopVariable, "while.rindex")); } text = ScriptVariable.LoopRIndex.Name; break; case "col": if (text != "tablerow") { // unit test: 108-variable-loop-error2.txt LogError(currentToken, string.Format(RS.InvalidLoopVariable, text + ".col")); } text = ScriptVariable.TableRowCol.Name; break; default: text = text + "." + loopVariableText; // unit test: 108-variable-loop-error1.txt LogError(currentToken, string.Format(RS.InvalidLoopVariable, text)); break; } } // We no longer checks at parse time usage of loop variables, as they can be used in a wrap context //if (!IsInLoop()) //{ // LogError(currentToken, string.Format(RS.InvalidLoopVariableOutsideLoop, text)); //} } else { LogError(currentToken, string.Format(RS.LoopVariableDotRequireIdentifier, Current.Type, text)); } } } else if (_isLiquid && text == "continue") { scope = ScriptVariableScope.Local; } ScriptVariable result = ScriptVariable.Create(text, scope); result.Span = new SourceSpan { FileName = currentSpan.FileName, Start = currentSpan.Start, End = endSpan.End }; // A liquid variable can have `-` in its identifier // If this is the case, we need to translate it to `this["this"]` instead if (_isLiquid && text.IndexOf('-') >= 0) { ScriptIndexerExpression newExp = new ScriptIndexerExpression { Target = new ScriptThisExpression() { Span = result.Span }, Index = new ScriptLiteral(text) { Span = result.Span }, Span = result.Span }; // Flush any trivias after if (_isKeepTrivia) { if (triviasBefore != null) { newExp.Target.AddTrivias(triviasBefore, true); } FlushTrivias(newExp, false); } // Return the expression return(newExp); } if (_isKeepTrivia) { // Flush any trivias after if (triviasBefore != null) { result.AddTrivias(triviasBefore, true); } FlushTrivias(result, false); } return(result); }
internal void PopVariableScope(ScriptVariableScope scope) { var stores = (scope == ScriptVariableScope.Local ? localStores : loopStores); if (stores.Count == 0) { // Should not happen at runtime throw new InvalidOperationException("Invalid number of matching push/pop VariableScope."); } var store = stores.Pop(); // The store is cleanup once it is pushed back store.Clear(); availableStores.Push(store); }
internal void PushVariableScope(ScriptVariableScope scope) { var store = availableStores.Count > 0 ? availableStores.Pop() : new ScriptObject(); (scope == ScriptVariableScope.Local ? localStores : loopStores).Push(store); }