Example #1
0
        /// Starts a subroutine block. For this, we create a separate symbol table
        /// to supplement the main one.
        ParseNode KSubFunc(SymClass klass, string methodName, SymFullType returnType)
        {
            ProcedureParseNode node = new ProcedureParseNode();
            EnsureNoLabel();

            // Add the name to the global scope, ensuring it hasn't already been
            // defined.
            if (methodName == null) {
                IdentifierToken identToken = ExpectIdentifierToken();
                if (identToken == null) {
                    SkipToEndOfLine();
                    return null;
                }
                methodName = identToken.Name;
            }

            Symbol method = _globalSymbols.Get(methodName);
            if (method != null && method.Defined && !method.IsExternal) {
                _messages.Error(MessageCode.SUBFUNCDEFINED, String.Format("{0} already defined", methodName));
                SkipToEndOfLine();
                return null;
            }

            // Reset the COMMON indexes for this program unit
            foreach (Symbol sym in _globalSymbols) {
                if (sym.Type == SymType.COMMON) {
                    sym.CommonIndex = 0;
                }
            }

            // New local symbol table for this block
            _localSymbols = new SymbolCollection("Local");
            _hasReturn = false;

            // Parameter list allowed for subroutines and functions, but
            // not the main program.
            Collection<Symbol> parameters = null;
            int alternateReturnCount = 0;

            switch (klass) {
                case SymClass.PROGRAM:
                    parameters = new Collection<Symbol>();
                    klass = SymClass.SUBROUTINE;
                    methodName = _entryPointName;
                    break;

                case SymClass.FUNCTION:
                    parameters = ParseParameterDecl(_localSymbols, SymScope.PARAMETER, out alternateReturnCount);
                    break;

                case SymClass.SUBROUTINE:
                    parameters = ParseParameterDecl(_localSymbols, SymScope.PARAMETER, out alternateReturnCount);
                    if (alternateReturnCount > 0) {
                        returnType = new SymFullType(SymType.INTEGER);
                    }
                    break;
            }

            // Don't allow alternate returns for anything except subroutines
            if (alternateReturnCount > 0 && klass != SymClass.SUBROUTINE) {
                _messages.Error(MessageCode.ALTRETURNNOTALLOWED, "Alternate return only permitted for subroutines");
            }

            // Add this method to the global symbol table now.
            if (method == null) {
                method = _globalSymbols.Add(methodName, new SymFullType(), klass, null, _ls.LineNumber);
            }

            method.Parameters = parameters;
            method.Defined = true;
            method.Class = klass;

            if (returnType.Type != SymType.NONE) {
                method.FullType = returnType;
            }

            if (methodName == _entryPointName) {
                method.Modifier |= SymModifier.ENTRYPOINT;
                _hasProgram = true;
            }

            // Special case for functions. Create a local symbol with the same
            // name to be used for the return value.
            if (klass == SymClass.FUNCTION || alternateReturnCount > 0) {
                method.RetVal = _localSymbols.Add(methodName, returnType, SymClass.VAR, null, _ls.LineNumber);
                method.RetVal.Modifier = SymModifier.RETVAL;
            }

            node.ProcedureSymbol = method;
            node.LocalSymbols = _localSymbols;
            node.LabelList = new Collection<ParseNode>();

            _currentProcedure = node;
            _currentProcedure.AlternateReturnCount = alternateReturnCount;

            _initList = new CollectionParseNode();
            node.InitList = _initList;

            // Compile the body of the procedure
            SimpleToken token = _ls.GetKeyword();
            while (token.ID != TokenID.ENDOFFILE) {
                if (token.ID != TokenID.EOL) {
                    ParseNode labelNode = CheckLabel();
                    if (labelNode != null) {
                        node.Add(labelNode);
                    }
                    if (token.ID == TokenID.KEND) {
                        break;
                    }
                    ParseNode lineNode = Statement(token);
                    if (lineNode != null) {
                        node.Add(MarkLine());
                        node.Add(lineNode);
                    }
                    ExpectEndOfLine();
                }
                token = _ls.GetKeyword();
            }

            // If we hit the end of the file first then we're missing
            // a mandatory END statement.
            if (token.ID != TokenID.KEND) {
                _messages.Error(MessageCode.MISSINGENDSTATEMENT, "Missing END statement");
            }

            // Make sure we have a RETURN statement.
            if (!_hasReturn) {
                node.Add(new ReturnParseNode());
            }

            // Validate the block.
            foreach (Symbol sym in _localSymbols) {
                if (sym.IsLabel && !sym.Defined) {
                    _messages.Error(MessageCode.UNDEFINEDLABEL, sym.RefLine, String.Format("Undefined label {0}", sym.Name));
                }
                if (_saveAll && sym.IsLocal) {
                    sym.Modifier |= SymModifier.STATIC;
                }

                // For non-array characters, if there's no value, set the empty string
                if (sym.Type == SymType.FIXEDCHAR && !sym.IsArray && !sym.Value.HasValue) {
                    sym.Value = new Variant(string.Empty);
                }

                if (!sym.IsReferenced && !(sym.Modifier.HasFlag(SymModifier.RETVAL))) {
                    string scopeName = (sym.IsParameter) ? "parameter" : (sym.IsLabel) ? "label" : "variable";
                    _messages.Warning(MessageCode.UNUSEDVARIABLE,
                                      3,
                                      sym.RefLine,
                                      String.Format("Unused {0} {1} in function", scopeName, sym.Name));
                }
            }
            ValidateBlock(0, node);
            _state = BlockState.SPECIFICATION;
            return node;
        }
Example #2
0
        /// COMMON keyword
        /// Syntax: COMMON ident [,ident]
        ParseNode KCommon()
        {
            SymFullType fullType = new SymFullType(SymType.NONE);
            List<Symbol> commonSymbols = null;
            bool isNewBlock = true;
            Symbol symCommon = null;

            do {
                SimpleToken token = _ls.GetToken();
                string name = "_COMMON";

                if (token.ID == TokenID.DIVIDE) {
                    IdentifierToken identToken = ExpectIdentifierToken();
                    if (identToken == null) {
                        SkipToEndOfLine();
                        return null;
                    }
                    name = identToken.Name;
                    isNewBlock = true;
                    ExpectToken(TokenID.DIVIDE);
                } else if (token.ID == TokenID.CONCAT) {
                    name = "_COMMON";
                    isNewBlock = true;
                } else {
                    _ls.BackToken();
                }
                if (isNewBlock) {
                    symCommon = _globalSymbols.Get(name);
                    if (symCommon == null) {
                        symCommon = _globalSymbols.Add(name, new SymFullType(SymType.COMMON), SymClass.COMMON, null, _ls.LineNumber);
                        commonSymbols = new List<Symbol>();
                        symCommon.Info = commonSymbols;
                        symCommon.CommonIndex = 0;
                    }
                    commonSymbols = (List<Symbol>)symCommon.Info;
                    isNewBlock = false;
                }
                Symbol sym = ParseIdentifierDeclaration(fullType);
                if (sym != null) {
                    if (sym.IsInCommon) {
                        _messages.Error(MessageCode.ALREADYINCOMMON, string.Format("{0} is already in a COMMON block", sym.Name));
                    } else {
                        sym.CommonIndex = symCommon.CommonIndex;
                        sym.Common = symCommon;
                        sym.IsReferenced = true;

                        // Symbols in the primary COMMON block need to be static so
                        // they can be referenced from sub-programs
                        if (sym.CommonIndex < commonSymbols.Count) {
                            Symbol symInCommon = commonSymbols[sym.CommonIndex];
                            sym.FullType = symInCommon.FullType;
                        } else {
                            sym.Modifier |= SymModifier.STATIC;
                            commonSymbols.Add(sym);
                        }
                        symCommon.CommonIndex += 1;
                    }
                    SkipToken(TokenID.COMMA);
                }
            } while (!IsAtEndOfLine());
            return null;
        }
Example #3
0
        /// IMPLICIT keyword
        /// Syntax: IMPLICIT type (c1-c2) - all identifiers beginning with the letter or letter range c1 to c2
        ///             are implicitly typed with the given type.
        ParseNode KImplicit()
        {
            SimpleToken token;
            EnsureNoLabel();
            do {
                token = _ls.GetKeyword();
                switch (token.ID) {
                    case TokenID.KINTEGER:
                    case TokenID.KDPRECISION:
                    case TokenID.KCHARACTER:
                    case TokenID.KLOGICAL:
                    case TokenID.KCOMPLEX:
                    case TokenID.KREAL: {
                        SymFullType fullType = new SymFullType(TokenToType(token.ID));

                        fullType.Width = ParseTypeWidth(1);
                        ExpectToken(TokenID.LPAREN);
                        do {
                            IdentifierToken identToken = ExpectIdentifierToken();
                            if (identToken != null) {
                                char ch1 = identToken.Name[0];
                                char ch2 = ch1;

                                if (ch1 < 'A' || ch1 > 'Z') {
                                    _messages.Error(MessageCode.IMPLICITSINGLECHAR, "IMPLICIT must have a single character");
                                }

                                token = _ls.GetToken();
                                if (token.ID == TokenID.MINUS) {
                                    identToken = ExpectIdentifierToken();
                                    if (identToken != null) {
                                        ch2 = identToken.Name[0];
                                        if (ch2 < 'A' || ch2 > 'Z') {
                                            _messages.Error(MessageCode.IMPLICITSINGLECHAR, "IMPLICIT must have a single character");
                                        }
                                        if (ch2 < ch1) {
                                            _messages.Error(MessageCode.IMPLICITRANGEERROR, "IMPLICIT character range out of sequence");
                                        }
                                        token = _ls.GetToken();
                                    }
                                }

                                // Check for duplicate definitions
                                for (char chTmp = ch1; chTmp <= ch2; ++chTmp) {
                                    if (_localSymbols.IsImplicitSet(chTmp)) {
                                        _messages.Error(MessageCode.IMPLICITCHAREXISTS, String.Format("Character {0} already specified in an IMPLICIT", chTmp));
                                    }
                                    _localSymbols.SetImplicit(chTmp);
                                }

                                _localSymbols.Implicit(ch1, ch2, fullType);
                            }
                        } while (token.ID == TokenID.COMMA);
                        _ls.BackToken();
                        ExpectToken(TokenID.RPAREN);
                        _state = BlockState.IMPLICIT;
                        break;
                    }

                    default:
                        _messages.Error(MessageCode.IMPLICITSYNTAXERROR, "Syntax error in IMPLICIT statement");
                        break;
                }
                token = _ls.GetToken();
            } while (token.ID == TokenID.COMMA);
            _ls.BackToken();
            return null;
        }
Example #4
0
        /// EXTERNAL keyword
        /// Specifies the given names to be an external function or subroutine.
        /// as arguments.
        ParseNode KExternal()
        {
            SimpleToken token;
            EnsureNoLabel();
            do {
                IdentifierToken identToken = ExpectIdentifierToken();
                if (identToken != null) {
                    Symbol sym = _localSymbols.Get(identToken.Name);

                    // External scope being given to a dummy argument
                    if (sym != null && sym.IsParameter) {
                        sym.Class = SymClass.FUNCTION;
                    } else {
                        SymFullType fullType = new SymFullType();
                        if (sym != null) {
                            fullType = sym.FullType;
                            _localSymbols.Remove(sym);
                        }
                        sym = _globalSymbols.Add(identToken.Name, fullType, SymClass.FUNCTION, null, _ls.LineNumber);
                        sym.Modifier |= SymModifier.EXTERNAL;
                        sym.Defined = true;
                    }
                    sym.Linkage = SymLinkage.BYVAL;
                }
                token = _ls.GetToken();
            } while (token.ID == TokenID.COMMA);
            _ls.BackToken();
            return null;
        }
Example #5
0
        /// DIMENSION keyword
        /// Used to apply a dimension to a prior declared variable. Can be used to
        /// declare a variable assuming implicit type is permitted for the name.
        ParseNode KDimension()
        {
            SymFullType fullType = new SymFullType(SymType.NONE);
            SimpleToken token;

            do {
                Symbol sym = ParseIdentifierDeclaration(fullType);
                if (sym != null) {
                    if (sym.Dimensions == null || sym.Dimensions.Count == 0) {
                        _messages.Error(MessageCode.MISSINGARRAYDIMENSIONS, "Array dimensions expected");
                    }
                    sym.Defined = false;
                }
                token = _ls.GetToken();
            } while (token.ID == TokenID.COMMA);
            _ls.BackToken();
            return null;
        }
Example #6
0
        /// Handle a declaration statement of the specified type.
        ParseNode KDeclaration(SymType type)
        {
            SymFullType fullType = new SymFullType(type);
            SimpleToken token;

            if (Symbol.IsCharType(type)) {
                fullType.Width = ParseTypeWidth(0);
            }

            // Could be a function declaration preceded by type?
            if (_ls.PeekKeyword() == TokenID.KFUNCTION) {
                _ls.GetToken();
                return KSubFunc(SymClass.FUNCTION, null, fullType);
            }

            do {
                Symbol sym = ParseIdentifierDeclaration(fullType);
                if (sym != null) {
                    if (sym.Defined) {
                        _messages.Error(MessageCode.IDENTIFIERREDEFINITION, String.Format("Identifier {0} already declared", sym.Name));
                    }
                    if (sym.IsParameter) {
                        if (!sym.IsArray && !sym.IsMethod && sym.IsValueType) {
                            sym.Linkage = SymLinkage.BYREF;
                        } else {
                            sym.Linkage = SymLinkage.BYVAL;
                        }
                    }
                    sym.Defined = true;
                }
                token = _ls.GetToken();
            } while (token.ID == TokenID.COMMA);
            _ls.BackToken();
            return null;
        }
Example #7
0
        // Parse an identifier declaration
        Symbol ParseIdentifierDeclaration(SymFullType fullType)
        {
            IdentifierToken identToken = ExpectIdentifierToken();
            Symbol sym = null;

            if (identToken != null) {

                // Ban any conflict with PROGRAM name or the current function
                Symbol globalSym = _globalSymbols.Get(identToken.Name);
                if (globalSym != null && globalSym.Type == SymType.PROGRAM) {
                    _messages.Error(MessageCode.IDENTIFIERISGLOBAL, String.Format("Identifier {0} already has global declaration", identToken.Name));
                    SkipToEndOfLine();
                    return null;
                }

                // Now check the local program unit
                sym = _localSymbols.Get(identToken.Name);

                // Handle array syntax and build a list of dimensions
                Collection<SymDimension> dimensions = ParseArrayDimensions();
                if (dimensions == null) {
                    return null;
                }

                // If this is the main program, all dimensions must be constant
                if (_currentProcedure != null && _currentProcedure.IsMainProgram) {
                    foreach (SymDimension dim in dimensions) {
                        if (dim.Size < 0) {
                            _messages.Error(MessageCode.ARRAYILLEGALBOUNDS, "Array dimensions must be constant");
                            break;
                        }
                    }
                }

                // Check for width specifier. This always follows array bounds and if one is
                // specified, it only applies to this identifier so the original width, if any,
                // must be preserved.
                SymFullType thisFullType = new SymFullType();
                int width = fullType.Width;

                if (width == 0) {
                    SymFullType impliedFullType = _localSymbols.ImplicitTypeForCharacter(identToken.Name[0]);
                    width = impliedFullType.Width;
                }
                thisFullType.Type = fullType.Type;
                thisFullType.Width = ParseTypeWidth(width);

                // Indicate this symbol is explicitly declared
                if (sym == null) {
                    if (identToken.Name.Length > 6 && _opts.F77) {
                        _messages.Error(MessageCode.IDENTIFIERTOOLONG, String.Format("Identifier {0} length too long", identToken.Name));
                    }
                    sym = _localSymbols.Add(identToken.Name, thisFullType, SymClass.VAR, dimensions, _ls.LineNumber);
                } else {
                    if (fullType.Type != SymType.NONE) {
                        sym.FullType = thisFullType;
                    }
                    if (dimensions.Count > 0) {
                        sym.Dimensions = dimensions;
                    }
                }

                // If this is applying type to a function, update the function too
                if (globalSym != null && sym == globalSym.RetVal) {
                    globalSym.FullType = thisFullType;
                }

                // BUGBUG: This should be done in the Symbols class when the
                // identifier becomes an array.
                if (sym.IsArray) {
                    sym.Linkage = SymLinkage.BYVAL;
                    sym.Modifier |= SymModifier.FLATARRAY;  // FORTRAN always uses flat arrays
                }
            }
            return sym;
        }