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
        // Internal generation logic for a subroutine or function call.
        SymType InternalGenerate(CodeGenerator cg, SymClass callType, string callName)
        {
            Symbol sym = ProcName.Symbol;
            SymType thisType = SymType.NONE;

            if (!sym.Defined) {
                cg.Error(sym.RefLine, String.Format("Undefined {0} {1}", callName, sym.Name));
            }
            if (sym.Class != callType) {
                cg.Error(sym.RefLine, String.Format("{0} is not a {1}", sym.Name, callName));
            }

            Type[] paramTypes = Parameters.Generate(cg, sym);

            if (sym.IsParameter) {
                ProcName.Generate(cg);
                cg.Emitter.CallIndirect(sym.Type, paramTypes);
                thisType = sym.Type;
            } else {
                if (sym.Info != null) {
                    Debug.Assert(sym.Info is MethodInfo);
                    cg.Emitter.Call((MethodInfo)sym.Info);
                    thisType = sym.Type;
                }
            }

            // Sanity check, make sure alternate return labels are only specified
            // for subroutines.
            if (AlternateReturnLabels.Count > 0 && sym.Class != SymClass.SUBROUTINE) {
                throw new CodeGeneratorException("Codegen error: Alternate return labels only permitted for subroutines");
            }

            // Post-alternate return branching
            if (AlternateReturnLabels.Count > 0) {
                Label [] jumpTable = new Label[AlternateReturnLabels.Count];
                for (int c = 0; c < AlternateReturnLabels.Count; ++c) {
                    Symbol symLabel = AlternateReturnLabels[c];
                    jumpTable[c] = (Label)symLabel.Info;
                }
                cg.Emitter.LoadInteger(1);
                cg.Emitter.Sub(SymType.INTEGER);
                cg.Emitter.Switch(jumpTable);
            }

            Parameters.FreeLocalDescriptors();
            return thisType;
        }