public RantAction Read() { var output = new List <RantAction>(); // NOTE: since parselets are more like containers for multiple parsers, maybe figure out something to stuff a stack full of rather than parselets //var parseletStack = new Stack<Parselet>(); var enumeratorStack = new Stack <IEnumerator <Parselet> >(); Token <R> token = null; while (!reader.End) { token = reader.ReadToken(); if (token.ID == R.EOF) { goto done; } var parselet = Parselet.GetParselet(token, output.Add); //parseletStack.Push(parselet); enumeratorStack.Push(parselet.Parse()); top: while (enumeratorStack.Any()) { var enumerator = enumeratorStack.Peek(); while (enumerator.MoveNext()) { if (enumerator.Current == null) { break; } //parseletStack.Push(enumerator.Current); enumeratorStack.Push(enumerator.Current.Parse()); goto top; } //parseletStack.Pop(); enumeratorStack.Pop(); } } done: return(new RASequence(output, token)); }
public RantAction Read(ExpressionReadType type = ExpressionReadType.Expression) { List <RichActionBase> actions = new List <RichActionBase>(); Token <R> token = null; if (type == ExpressionReadType.Expression) { _hitEndOfExpr = false; } while (!_reader.End) { if (_hitEndOfExpr) { goto done; } token = _reader.ReadToken(); switch (token.ID) { case R.EOF: throw new RantCompilerException(_sourceName, token, "Unexpected end of file."); case R.Whitespace: if (type == ExpressionReadType.InvertValue) { goto done; } break; case R.LeftSquare: { if (!actions.Any()) { // empty list initializer if (!_reader.End && _reader.PeekLooseToken().ID == R.RightSquare) { _reader.ReadLooseToken(); actions.Add(new RichList(token, new List <RichActionBase>(), false)); break; } actions.Add((Read(ExpressionReadType.List) as RAExpression).Group); break; } var val = Read(ExpressionReadType.BracketValue) as RAExpression; if (val.Group.Actions.Count == 0) { SyntaxError(val.Range, "Expected value in bracket indexer."); } var obj = actions.Last(); actions.RemoveAt(actions.Count - 1); actions.Add(new RichObjectProperty(token, val.Group, obj)); } break; case R.RightSquare: if (type == ExpressionReadType.List) { goto done; } if ( type == ExpressionReadType.FunctionBody || type == ExpressionReadType.VariableValue) { _hitEndOfExpr = true; } if ( type != ExpressionReadType.Expression && type != ExpressionReadType.VariableValue && type != ExpressionReadType.BracketValue && type != ExpressionReadType.FunctionBody) { SyntaxError(token, "Unexpected end of expression."); } goto done; case R.LeftParen: { var group = (Read(ExpressionReadType.ExpressionGroup) as RAExpression).Group; // could be a function call if (actions.Any() && (actions.Last() is RichPatternString || actions.Last() is RichVariable || actions.Last() is RichFunctionCall || actions.Last() is RichGroup || actions.Last() is RichObjectProperty)) { var last = actions.Last(); actions.Remove(last); actions.Add(new RichFunctionCall(token, last, group as RichGroup, _sourceName)); break; } // could be a function definition (lambda) if (_reader.PeekLooseToken().ID == R.Equal) { _reader.ReadLooseToken(); _reader.ReadLoose(R.RightAngle, "fat arrow"); var body = Read(ExpressionReadType.FunctionBody); actions.Add(new RichFunction(token, (body as RAExpression).Group, group as RichGroup)); if (type == ExpressionReadType.VariableValue) { goto done; } if (_reader.PrevLooseToken.ID == R.RightParen && type == ExpressionReadType.ExpressionGroup) { goto done; } break; } if (!group.Actions.Any()) { Unexpected(token); } if (group.Actions.Any(x => x is RichArgumentSeperator)) { actions.Add(CreateListFromActions(type, group.Actions)); } else { actions.Add(group); } } break; case R.RightParen: if ( type == ExpressionReadType.ExpressionGroup || type == ExpressionReadType.VariableValue || type == ExpressionReadType.ExpressionGroupNoList || type == ExpressionReadType.FunctionBody) { goto done; } break; case R.LeftCurly: // is it an object? let's find out if (_reader.PeekLooseToken().ID == R.RightCurly) { // empty object _reader.Read(R.RightCurly); actions.Add(new RichObject(token, new RichObjectKeyValue[0])); if (type == ExpressionReadType.VariableValue) { if (!_reader.End && _reader.PeekLooseToken().ID != R.Semicolon) { Unexpected(_reader.PeekLooseToken()); } goto done; } break; } // maybe it's an object with key/values var tempPosition = _reader.Position; if (_reader.PeekLooseToken().ID == R.ConstantLiteral || _reader.PeekLooseToken().ID == R.Text) { var firstValue = _reader.ReadLooseToken(); // ok, it's an object if (_reader.PeekLooseToken().ID == R.Colon) { _reader.Position = tempPosition; var obj = Read(ExpressionReadType.KeyValueObject); actions.Add(obj as RichActionBase); break; } else { _reader.Position = tempPosition; } } if (type == ExpressionReadType.FunctionBody || type == ExpressionReadType.Expression) { var body = Read(ExpressionReadType.ExpressionBlock); actions.Add(body as RichActionBase); if (type == ExpressionReadType.FunctionBody) { goto done; } break; } Unexpected(token); break; case R.RightCurly: if (type == ExpressionReadType.ExpressionBlock) { var group = new RichGroup(actions, token, _sourceName); return(new RichBlock(token, new List <RichActionBase>() { group })); } if (type == ExpressionReadType.KeyValueObjectValue) { return(new RichGroup(actions, token, _sourceName)); } Unexpected(token); break; case R.Semicolon: if (type != ExpressionReadType.Expression && type != ExpressionReadType.ExpressionBlock) { goto done; } if (actions.Count == 0) { break; } var action = new RichGroup(actions.ToList(), token, _sourceName); actions.Clear(); actions.Add(action); break; case R.ConstantLiteral: if (type == ExpressionReadType.KeyValueObject) { var name = token; _reader.ReadLoose(R.Colon, "key value seperator"); var value = Read(ExpressionReadType.KeyValueObjectValue) as RichGroup; if (value.Actions.Count == 0) { SyntaxError(value.Range, "Expected value for key."); } actions.Add(new RichObjectKeyValue(name, value)); if (_reader.PrevLooseToken.ID == R.RightCurly) { List <RichObjectKeyValue> keyValues = new List <RichObjectKeyValue>(); foreach (RichActionBase kv in actions) { if (!(kv is RichObjectKeyValue)) { throw new RantCompilerException(_sourceName, token, "Unexpected token in key value object."); } keyValues.Add(kv as RichObjectKeyValue); } return(new RichObject(name, keyValues.ToArray())); } break; } actions.Add(new RichString(Util.UnescapeConstantLiteral(token.Value), token)); break; case R.Dollar: if (_reader.PeekType() == R.ConstantLiteral) { var value = _reader.Read(R.ConstantLiteral, "string"); actions.Add(new RichPatternString(Util.UnescapeConstantLiteral(value.Value), token)); break; } Unexpected(token); break; case R.Text: // numbers if (char.IsDigit(token.Value[0])) { if (actions.Any() && actions.Last() is RichNumber) { Unexpected(token); } double number; if (!ReadNumber(token, out number)) { SyntaxError(token, "Invalid number: " + token.Value); } actions.Add(new RichNumber(number, token)); break; } // keywords if (_keywords.IndexOf(token.Value) >= 0) { switch (token.Value) { case "var": var name = _reader.ReadLoose(R.Text, "variable name"); if (_keywords.IndexOf(name.Value) > -1) { SyntaxError(name, "Cannot use reserved variable name."); } if (_reader.PeekType() == R.Equal) { _reader.ReadToken(); var val = Read(ExpressionReadType.VariableValue) as RAExpression; actions.Add(new RichObjectPropertyAssignment(name, null, val.Group)); } else if (_reader.PeekType() == R.Semicolon || _reader.End) { actions.Add(new RichObjectPropertyAssignment(name, null, null)); } break; case "function": { _reader.ReadLoose(R.LeftParen, "function definition open paren"); var argGroup = Read(ExpressionReadType.ExpressionGroup) as RAExpression; _reader.ReadLoose(R.LeftCurly, "function body open bracket"); var body = Read(ExpressionReadType.ExpressionBlock); actions.Add(new RichFunction(token, body as RichActionBase, argGroup.Group)); if (type == ExpressionReadType.VariableValue) { goto done; } } break; case "true": actions.Add(new RichBoolean(token, true)); break; case "false": actions.Add(new RichBoolean(token, false)); break; case "no": actions.Add(new RichNull(token)); break; case "maybe": actions.Add(new RichMaybe(token)); break; case "list": { var num = _reader.ReadLoose(R.Text, "list length"); double listLength = -1; ReadNumber(num, out listLength); if (listLength < 0) { SyntaxError(num, "Invalid list length."); } List <RichActionBase> fakeList = new List <RichActionBase>(); for (var i = 0; i < listLength; i++) { fakeList.Add(null); } actions.Add(new RichList(token, fakeList, false)); } break; case "if": { if (type != ExpressionReadType.Expression && type != ExpressionReadType.ExpressionBlock && type != ExpressionReadType.FunctionBody) { Unexpected(token); } _reader.ReadLoose(R.LeftParen, "if statement param"); var val = Read(ExpressionReadType.ExpressionGroupNoList) as RAExpression; var body = Read(ExpressionReadType.FunctionBody) as RAExpression; RichActionBase elseBody = null; if (!_reader.End && _reader.PeekLooseToken().Value == "else") { _reader.ReadLooseToken(); elseBody = (Read(ExpressionReadType.FunctionBody) as RAExpression).Group; } actions.Add(new RichIfStatement(token, val.Group, body.Group, elseBody)); } break; case "return": { if (type != ExpressionReadType.ExpressionBlock && type != ExpressionReadType.FunctionBody && type != ExpressionReadType.Expression) { Unexpected(token); } RichActionBase expr = null; if (_reader.PeekLooseToken().ID != R.Semicolon) { expr = (Read(ExpressionReadType.VariableValue) as RAExpression).Group; } actions.Add(new RichReturn(token, expr)); if (_reader.PrevLooseToken.ID == R.Semicolon && type == ExpressionReadType.FunctionBody) { return(new RAExpression(actions, token, _sourceName)); } } break; case "while": { if (type != ExpressionReadType.Expression && type != ExpressionReadType.ExpressionBlock) { Unexpected(token); } _reader.ReadLoose(R.LeftParen, "while loop test"); var test = (Read(ExpressionReadType.ExpressionGroupNoList) as RAExpression).Group; var body = (Read(ExpressionReadType.FunctionBody) as RAExpression).Group; actions.Add(new RichWhile(token, test, body)); } break; case "break": { if (type != ExpressionReadType.FunctionBody && type != ExpressionReadType.ExpressionBlock) { Unexpected(token); } actions.Add(new RichBreak(token)); } break; case "for": { if (type != ExpressionReadType.Expression && type != ExpressionReadType.ExpressionBlock) { Unexpected(token); } _reader.ReadLoose(R.LeftParen, "for loop open paren"); var indexName = _reader.ReadLoose(R.Text, "for loop index name"); if (indexName.Value == "var") { indexName = _reader.ReadLoose(R.Text, "for loop index name"); } if (indexName.Value == "in") { SyntaxError(indexName, "Expected index name in for loop."); } var inStmt = _reader.ReadLoose(R.Text, "for loop in statement"); if (inStmt.Value != "in") { SyntaxError(inStmt, "Expected in statement in for loop."); } var expr = (Read(ExpressionReadType.ExpressionGroupNoList) as RAExpression).Group; var body = (Read(ExpressionReadType.FunctionBody) as RAExpression).Group; actions.Add(new RichFor(token, indexName.Value, body, expr)); } break; } break; } // object key if (type == ExpressionReadType.KeyValueObject) { var name = token; _reader.ReadLoose(R.Colon, "key value seperator"); var value = Read(ExpressionReadType.KeyValueObjectValue) as RichGroup; if (value.Actions.Count == 0) { SyntaxError(value.Range, "Expected value for key."); } actions.Add(new RichObjectKeyValue(name, value)); if (_reader.PrevLooseToken.ID == R.RightCurly) { List <RichObjectKeyValue> keyValues = new List <RichObjectKeyValue>(); foreach (RichActionBase kv in actions) { if (!(kv is RichObjectKeyValue)) { throw new RantCompilerException(_sourceName, token, "Unexpected token in key value object."); } keyValues.Add(kv as RichObjectKeyValue); } return(new RichObject(name, keyValues.ToArray())); } break; } if (actions.Any() && actions.Last() is RichVariable) { Unexpected(token); } // just a variable actions.Add(new RichVariable(token)); if (type == ExpressionReadType.InvertValue) { return(new RAExpression(actions, token, _sourceName)); } break; // binary number operators case R.Plus: // increment operator if (!_reader.End && _reader.PeekType() == R.Plus) { _reader.ReadToken(); // postfix if (actions.Any() && actions.Last() is RichVariable) { var variable = actions.Last(); actions.RemoveAt(actions.Count - 1); actions.Add(new RichPostfixIncDec(token) { LeftSide = variable }); break; } if (!_reader.End && _reader.PeekLooseToken().ID == R.Text) { var varName = _reader.ReadLooseToken(); double dummyNumber = -1; if (ReadNumber(varName, out dummyNumber)) { SyntaxError(token, "Cannot increment constant."); } actions.Add(new RichPrefixIncDec(token) { RightSide = new RichVariable(varName) }); break; } Unexpected(token); } actions.Add(new RichAdditionOperator(token)); break; case R.Hyphen: // decrement operator if (!_reader.End && _reader.PeekType() == R.Hyphen) { _reader.ReadToken(); // postfix if (actions.Any() && actions.Last() is RichVariable) { var variable = actions.Last(); actions.RemoveAt(actions.Count - 1); actions.Add(new RichPostfixIncDec(token) { LeftSide = variable, Increment = false }); break; } if (!_reader.End && _reader.PeekLooseToken().ID == R.Text) { var varName = _reader.ReadLooseToken(); actions.Add(new RichPrefixIncDec(token) { Increment = false, RightSide = new RichVariable(varName) }); break; } Unexpected(token); } // could be negative sign on number - is there a digit directly following this? if (!actions.Any() && char.IsDigit(_reader.PeekToken().Value[0]) && _reader.PrevToken.ID != R.Whitespace) { double number; if (!ReadNumber(token, out number)) { SyntaxError(token, "Invalid number: " + token.Value); } actions.Add(new RichNumber(number, token)); break; } actions.Add(new RichSubtractionOperator(token)); break; case R.Comma: if (type == ExpressionReadType.KeyValueObjectValue) { return(new RichGroup(actions, token, _sourceName)); } if (type == ExpressionReadType.ExpressionGroup || type == ExpressionReadType.VariableValue || type == ExpressionReadType.List || type == ExpressionReadType.Expression) { actions.Add(new RichArgumentSeperator(token)); break; } Unexpected(token); break; case R.ForwardSlash: actions.Add(new RichDivisionOperator(token)); break; case R.Asterisk: actions.Add(new RichMultiplicationOperator(token)); break; case R.Tilde: actions.Add(new RichConcatOperator(token)); break; // accessing properties case R.Subtype: { var name = _reader.Read(R.Text, "object property access"); var obj = actions.Last(); actions.Remove(obj); actions.Add(new RichObjectProperty(name, obj)); } break; case R.LeftAngle: if (_reader.PeekType() == R.Equal) { _reader.ReadToken(); actions.Add(new RichLessThanOperator(token, true)); } else { actions.Add(new RichLessThanOperator(token, false)); } break; case R.RightAngle: if (_reader.PeekType() == R.Equal) { _reader.ReadToken(); actions.Add(new RichGreaterThanOperator(token, true)); } else { actions.Add(new RichGreaterThanOperator(token, false)); } break; case R.Exclamation: if (!_reader.End && _reader.PeekToken().ID == R.Equal) { _reader.ReadToken(); actions.Add(new RichInequalityOperator(token)); break; } // invert operator maybe if (!_reader.End && (_reader.PeekLooseToken().ID == R.LeftParen || _reader.PeekLooseToken().ID == R.Text)) { RichActionBase rightSide = null; if (_reader.PeekLooseToken().ID == R.LeftParen) { _reader.ReadLooseToken(); rightSide = (Read(ExpressionReadType.ExpressionGroup) as RAExpression).Group; actions.Add(new RichPrefixInvert(token) { RightSide = rightSide }); break; } rightSide = (Read(ExpressionReadType.InvertValue) as RAExpression).Group; actions.Add(new RichPrefixInvert(token) { RightSide = rightSide }); break; } Unexpected(token); break; case R.Equal: if (_reader.PeekType() == R.Equal) { _reader.ReadToken(); actions.Add(new RichEqualityOperator(token)); break; } RichInfixOperator op = null; if (!actions.Any()) { SyntaxError(token, "Cannot assign value to nothing."); } if (actions.Last() is RichInfixOperator) { if (_reader.PrevToken.ID == R.Whitespace) { Unexpected(token); } op = actions.Last() as RichInfixOperator; if (op.Type == RichActionBase.ActionValueType.Boolean) { Unexpected(token); } actions.RemoveAt(actions.Count - 1); if (!actions.Any()) { Unexpected(token); } } // variable assignment if (actions.Last() is RichVariable) { var lastAction = actions.Last(); var val = (Read(ExpressionReadType.VariableValue) as RAExpression).Group; actions.RemoveAt(actions.Count - 1); if (_keywords.IndexOf(lastAction.Range.Value) > -1) { SyntaxError(lastAction.Range, "Cannot use reserved variable name."); } var assignment = new RichObjectPropertyAssignment(lastAction.Range, null, val); assignment.Operator = op; actions.Add(assignment); if (type == ExpressionReadType.FunctionBody) { goto done; } break; } // object property assignment if (actions.Last() is RichObjectProperty) { var lastAction = actions.Last() as RichObjectProperty; var value = Read(ExpressionReadType.VariableValue) as RAExpression; RichObjectPropertyAssignment assignment = null; if (lastAction.Name is RichActionBase) { assignment = new RichObjectPropertyAssignment(lastAction.Range, lastAction.Name as RichActionBase, lastAction.Object, value.Group); } else { assignment = new RichObjectPropertyAssignment(lastAction.Range, lastAction.Object, value.Group); } assignment.Operator = op; actions.Add(assignment); break; } Unexpected(token); break; case R.Undefined: if (!_reader.End && _reader.PeekLooseToken().ID != R.Semicolon && _reader.PeekLooseToken().ID != R.RightSquare) { Unexpected(_reader.PeekLooseToken()); } actions.Add(new RichUndefined(token)); break; case R.Percent: actions.Add(new RichModuloOperator(token)); break; case R.Ampersand: // boolean AND if (!_reader.End && _reader.PeekType() == R.Ampersand) { _reader.ReadToken(); actions.Add(new RichBooleanAndOperator(token)); break; } Unexpected(token); break; case R.Pipe: // boolean OR if (!_reader.End && _reader.PeekType() == R.Pipe) { _reader.ReadToken(); actions.Add(new RichBooleanOrOperator(token)); break; } Unexpected(token); break; default: Unexpected(token); break; } } done: if ((actions.Any(x => x is RichArgumentSeperator) && (type == ExpressionReadType.Expression || type == ExpressionReadType.VariableValue) || type == ExpressionReadType.List)) { // lists var list = CreateListFromActions(type, actions); actions.Clear(); actions.Add(list); } else if (actions.Where(x => x is RichArgumentSeperator).Any() && type != ExpressionReadType.ExpressionGroup) { Unexpected(actions.Where(x => x is RichArgumentSeperator).First().Range); } switch (type) { case ExpressionReadType.ExpressionGroup: case ExpressionReadType.ExpressionGroupNoList: case ExpressionReadType.VariableValue: case ExpressionReadType.Expression: case ExpressionReadType.BracketValue: case ExpressionReadType.FunctionBody: case ExpressionReadType.List: case ExpressionReadType.InvertValue: return(new RAExpression(actions, token, _sourceName)); default: throw new RantCompilerException(_sourceName, token, "Unexpected end of expression."); } }