/// 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; }
// 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; }