/// <summary> /// Creates a loop parse node. /// </summary> public LoopParseNode() : base(ParseID.LOOP) { StepExpression = new NumberParseNode(1); }
/// <summary> /// Returns a ParametersParseNode object that represents the parameters defined /// for this external function using the values specified in the given control /// list dictionary. For parameters where no value is given, a default of 0 or /// NULL is substituted. /// </summary> /// <param name="cilist">A dictionary of control list values</param> /// <returns>A ParametersParseNode object.</returns> public ParametersParseNode ParametersNode(ControlList cilist) { if (cilist == null) { throw new ArgumentNullException("cilist"); } ParametersParseNode paramList = new ParametersParseNode(); foreach (FunctionDefinition def in _definitions) { if (def.Include) { ParseNode exprNode; if (!cilist.Has(def.Name)) { if (Symbol.IsNumberType(def.Symbol.Type)) { exprNode = new NumberParseNode(0); } else if (Symbol.IsLogicalType(def.Symbol.Type)) { exprNode = new NumberParseNode(new Variant(false)); } else { exprNode = new NullParseNode(); exprNode.Type = def.Symbol.Type; } } else { exprNode = cilist[def.Name]; } paramList.Add(exprNode, def.Symbol); } } return paramList; }
/// READ keyword /// Reads data into variables. Basically the run-time handles the I/O and the /// format list, and we provide the parameters. ParseNode KRead() { ReadParseNode node = new ReadParseNode(); InitFunctionNodes(); ControlList cilist = ParseCIList(_readFunctions.ParameterList); if (cilist == null) { SkipToEndOfLine(); return null; } node.ArgList = ParseVarargReferenceList(); node.EndLabel = (SymbolParseNode)cilist["END"]; node.ErrLabel = (SymbolParseNode)cilist["ERR"]; node.ReadParamsNode = _ioCoreFunctions.ParametersNode(cilist); // If this is internal storage, create an expression that // uses the given character string as the input source. ParseNode unit = cilist["UNIT"]; if (unit != null && unit.ID == ParseID.IDENT && Symbol.IsCharType(unit.Type)) { if (cilist.Has("REC")) { _messages.Error(MessageCode.CILISTNOTALLOWED, "Parameter REC not allowed here"); SkipToEndOfLine(); return null; } node.ReadManagerParamsNode = _readFunctionString.ParametersNode(cilist); } else { if (unit == null) { cilist["UNIT"] = new NumberParseNode(new Variant(IOConstant.Stdin)); } node.ReadManagerParamsNode = _readFunctions.ParametersNode(cilist); } return node; }
/// WRITE keyword ParseNode KWrite() { WriteParseNode node = new WriteParseNode(); InitFunctionNodes(); ControlList cilist = ParseCIList(_writeFunctions.ParameterList); if (cilist == null) { SkipToEndOfLine(); return null; } node.ArgList = ParseVarargList(); node.ErrLabel = (SymbolParseNode)cilist["ERR"]; // First column is special for F77 only node.FirstColumnSpecial = (_opts.F77); // If this is internal storage, create an expression that // assigns the result to the character string ParseNode unit = cilist["UNIT"]; if (unit != null && unit.ID == ParseID.IDENT && Symbol.IsCharType(unit.Type)) { node.WriteParamsNode = _ioCoreFunctions.ParametersNode(cilist); node.WriteManagerParamsNode = _writeFunctionString.ParametersNode(cilist); AssignmentParseNode assignNode = new AssignmentParseNode(); assignNode.Identifier = (IdentifierParseNode)unit; assignNode.ValueExpression = node; return assignNode; } if (unit == null) { cilist["UNIT"] = new NumberParseNode(new Variant(IOConstant.Stdout)); } node.WriteParamsNode = _ioCoreFunctions.ParametersNode(cilist); node.WriteManagerParamsNode = _writeFunctions.ParametersNode(cilist); return node; }
// Optimise a subtraction expression where both nodes are literal // values. Substitute the node with the result of the subtraction. ParseNode OptimiseSubtraction(ParseNode node) { BinaryOpParseNode tokenNode = (BinaryOpParseNode)node; tokenNode.Left = OptimiseExpressionTree(tokenNode.Left); tokenNode.Right = OptimiseExpressionTree(tokenNode.Right); if (tokenNode.IsNumber) { NumberParseNode op1 = (NumberParseNode)tokenNode.Left; NumberParseNode op2 = (NumberParseNode)tokenNode.Right; node = new NumberParseNode(op1.Value - op2.Value); } // Check for zero simplification if (tokenNode.Right.IsNumber) { if (tokenNode.Right.Value.IsZero) { return tokenNode.Left; } } return node; }
/// PRINT keyword ParseNode KPrint() { WriteParseNode node = new WriteParseNode(); InitFunctionNodes(); ControlList cilist = new ControlList(); cilist["FMT"] = ParseFormatSpecifier(); cilist["UNIT"] = new NumberParseNode(new Variant(IOConstant.Stdout)); // First column is special for F77 only node.FirstColumnSpecial = (_opts.F77); if (!IsAtEndOfLine()) { SimpleToken token; ExpectToken(TokenID.COMMA); VarArgParseNode varargs = new VarArgParseNode(); do { varargs.Add(ParseExpressionWithImpliedDo()); token = _ls.GetToken(); } while (token.ID == TokenID.COMMA); _ls.BackToken(); node.ArgList = varargs; } node.WriteParamsNode = _ioCoreFunctions.ParametersNode(cilist); node.WriteManagerParamsNode = _writeFunctions.ParametersNode(cilist); return node; }
// Optimise a negation expression where both nodes are literal // values. Substitute the node with the result of the negation. ParseNode OptimiseMinus(ParseNode node) { UnaryOpParseNode tokenNode = (UnaryOpParseNode)node; tokenNode.Operand = OptimiseExpressionTree(tokenNode.Operand); if (tokenNode.Operand.IsNumber) { NumberParseNode op1 = (NumberParseNode)tokenNode.Operand; node = new NumberParseNode(-op1.Value); } return node; }
// Optimise an exponentation expression where both nodes are literal // values. Substitute the node with the result of the exponentation. ParseNode OptimiseExponentation(ParseNode node) { BinaryOpParseNode tokenNode = (BinaryOpParseNode)node; tokenNode.Left = OptimiseExpressionTree(tokenNode.Left); tokenNode.Right = OptimiseExpressionTree(tokenNode.Right); if (tokenNode.IsNumber) { NumberParseNode op1 = (NumberParseNode)tokenNode.Left; NumberParseNode op2 = (NumberParseNode)tokenNode.Right; node = new NumberParseNode(op1.Value.Pow(op2.Value)); } // x raised to the powers of -1, 0 and 1 all yield constant expressions // so we can simplify that right now. if (tokenNode.Right.IsNumber) { Variant rightValue = tokenNode.Right.Value; if (rightValue.Compare(-1)) { BinaryOpParseNode divNode = new BinaryOpParseNode(ParseID.DIVIDE); divNode.Left = new NumberParseNode(new Variant(1)); divNode.Right = tokenNode.Left; divNode.Type = tokenNode.Left.Type; return divNode; } if (rightValue.IsZero) { return new NumberParseNode(1); } if (rightValue.Compare(1)) { return tokenNode.Left; } } return node; }
// Optimise a division expression where both nodes are literal // values. Substitute the node with the result of the division. ParseNode OptimiseDivision(ParseNode node) { BinaryOpParseNode tokenNode = (BinaryOpParseNode)node; tokenNode.Left = OptimiseExpressionTree(tokenNode.Left); tokenNode.Right = OptimiseExpressionTree(tokenNode.Right); if (tokenNode.IsNumber) { NumberParseNode op1 = (NumberParseNode)tokenNode.Left; NumberParseNode op2 = (NumberParseNode)tokenNode.Right; try { node = new NumberParseNode(op1.Value / op2.Value); } catch (DivideByZeroException) { _messages.Error(MessageCode.DIVISIONBYZERO, "Constant division by zero"); } } return node; }
// Parse an identifier parse node from the specified token. IdentifierParseNode ParseIdentifierParseNode() { IdentifierParseNode node = new IdentifierParseNode(null); Collection<ParseNode> indices = null; SimpleToken token = _ls.GetToken(); bool isSubstring = false; while (token.ID == TokenID.LPAREN) { if (indices == null) { indices = new Collection<ParseNode>(); } if (_ls.PeekToken().ID != TokenID.RPAREN) { do { ParseNode item = null; if (_ls.PeekToken().ID == TokenID.RPAREN) { SkipToken(TokenID.RPAREN); break; } if (_ls.PeekToken().ID != TokenID.COLON) { item = Expression(); } token = _ls.GetToken(); if (token.ID == TokenID.COLON) { isSubstring = true; if (item == null) { item = new NumberParseNode(1); } node.SubstringStart = item; token = new SimpleToken(TokenID.COMMA); continue; } if (isSubstring) { node.SubstringEnd = item; break; } indices.Add(item); } while (token.ID == TokenID.COMMA); _ls.BackToken(); } ExpectToken(TokenID.RPAREN); token = _ls.GetToken(); } node.Indexes = indices; _ls.BackToken(); return node; }
// Parse set of array dimensions if one is found. Collection<SymDimension> ParseArrayDimensions() { Collection<SymDimension> dimensions = new Collection<SymDimension>(); SimpleToken token = _ls.PeekToken(); bool hasAssumedBound = false; if (token.ID == TokenID.LPAREN) { ExpectToken(TokenID.LPAREN); do { do { ParseNode intVal; if (_ls.PeekToken().ID == TokenID.STAR) { SkipToken(TokenID.STAR); intVal = new NumberParseNode(0); hasAssumedBound = true; } else { intVal = IntegerExpression(); if (intVal == null) { SkipToEndOfLine(); return null; } // Assumed size declaration, which must be the last in // the list. We use a value of 0 for this since the array // calculations disregard it. However we need to make sure // this IS the last dimension. if (hasAssumedBound) { _messages.Error(MessageCode.ARRAYENDEXPECTED, "Dimensions not permitted after assumed bound"); SkipToEndOfLine(); return null; } } SymDimension dim = new SymDimension(); // Fortran arrays lower bounds start from 1 but one can // specify a custom bound range with the lower:upper syntax. ParseNode in1 = new NumberParseNode(1); ParseNode in2 = intVal; token = _ls.GetToken(); if (token.ID == TokenID.COLON) { if (_ls.PeekToken().ID == TokenID.STAR) { SkipToken(TokenID.STAR); intVal = new NumberParseNode(0); hasAssumedBound = true; } else { intVal = IntegerExpression(); } if (intVal != null) { in1 = in2; in2 = intVal; } } else { _ls.BackToken(); } if (in2.IsConstant && in1.IsConstant) { if (in2.Value.IntValue > 0 && in2.Value.IntValue < in1.Value.IntValue) { _messages.Error(MessageCode.ARRAYILLEGALBOUNDS, "Illegal bounds in array"); } } dim.LowerBound = in1; dim.UpperBound = in2; if (dimensions.Count == 7) { _messages.Error(MessageCode.TOOMANYDIMENSIONS, "Too many dimensions in array"); } else { dimensions.Add(dim); } token = _ls.GetToken(); } while (token.ID == TokenID.COMMA); _ls.BackToken(); ExpectToken(TokenID.RPAREN); token = _ls.GetToken(); } while (token.ID == TokenID.LPAREN); _ls.BackToken(); } return dimensions; }