public Translator(CompilerContext context) { MethodGenerator = new MethodGenerator(context); _context = context; _lexer = context.Lexer; _symbolTable = context.SymbolTable; _lastParsedPosition = _lexer.TokenStartPosition; _lexeme = string.Empty; }
private IrisType ProcessArrayAccess(FilePosition fp, Symbol symbol, SymbolLoadMode mode) { IrisType symbolType = symbol.Type; IrisType resultType = IrisType.Invalid; if (symbolType != IrisType.Invalid) { if (!symbolType.IsArray) { AddError(fp, string.Format("Symbol '{0}' is not an array, but is being used as an array.", _lexeme)); } else { EmitLoadSymbol(symbol, SymbolLoadMode.Dereference); resultType = symbol.Type.GetElementType(); } } FilePosition indexerPosition = _lexer.TokenStartPosition; IrisType indexerType = ParseExpression(); if (indexerType != IrisType.Integer) AddError(indexerPosition, "Expecting integer value as array index."); Expect(Token.ChrCloseBracket); if (resultType != IrisType.Invalid) { if (mode == SymbolLoadMode.ElementAddress) { MethodGenerator.LoadElementAddress(resultType); resultType = resultType.MakeByRefType(); } else if (mode == SymbolLoadMode.Element) { MethodGenerator.LoadElement(resultType); } } return resultType; }
protected void ParseMethod(bool isFunction) { FilePosition namePosition = _lexer.TokenStartPosition; if (!Accept(Token.Identifier)) { AddError(namePosition, "Expecting procedure or function name."); SkipToNextEnd(); return; } string methodName = _lexeme; if (!ValidateName(namePosition, methodName, global: true)) { methodName = (_nextUniqueName++).ToString(); } List <Tuple <Variable, FilePosition> > parameterList = new List <Tuple <Variable, FilePosition> >(); if (Accept(Token.ChrOpenParen) && !Accept(Token.ChrCloseParen)) { ParseVariableList(parameterList, isArgumentList: true); Expect(Token.ChrCloseParen); } IrisType returnType = IrisType.Void; if (Accept(Token.ChrColon)) { returnType = ParseType(); if (!isFunction) { AddErrorAtTokenStart("Procedure cannot have return value."); } } else if (isFunction) { AddErrorAtTokenStart("Expecting return type for function."); returnType = IrisType.Invalid; } Expect(Token.ChrSemicolon); // We've now parsed the method header. We can now create the method symbol and parse // the body of the method. IrisType method; Variable[] parameters = parameterList.Select(p => p.Item1).ToArray(); method = isFunction ? (IrisType)Function.Create(returnType, parameters) : Procedure.Create(parameterList.Select(p => p.Item1).ToArray()); Symbol methodSymbol = _symbolTable.OpenMethod(methodName, method); // Add argument symbols for all of the parameters foreach (Tuple <Variable, FilePosition> param in parameterList) { if (ValidateName(param.Item2, param.Item1.Name, global: false)) { _symbolTable.Add(param.Item1.Name, param.Item1.Type, StorageClass.Argument); } } List <Variable> locals = new List <Variable>(); if (isFunction) { // Create a local variable for the return value _symbolTable.Add(methodName, returnType, StorageClass.Local); locals.Add(new Variable(returnType, methodName)); } if (Accept(Token.KwVar)) { List <Tuple <Variable, FilePosition> > localsAndPositions = new List <Tuple <Variable, FilePosition> >(); ParseVariableList(localsAndPositions, isArgumentList: false); Accept(Token.ChrSemicolon); foreach (Tuple <Variable, FilePosition> localDecl in localsAndPositions) { Variable local = localDecl.Item1; string localName = local.Name; if (ValidateName(localDecl.Item2, localName, global: false)) { _symbolTable.Add(localName, local.Type, StorageClass.Local); locals.Add(local); } } } FilePosition begin = _lexer.TokenStartPosition; MethodGenerator.BeginMethod(methodSymbol.Name, returnType, parameters, locals.ToArray(), false, _context.FilePath); MethodGenerator.EmitNonCodeLineInfo(begin.Expand(5 /* Length of "begin" */)); // Initialize locals if needed foreach (Variable local in locals) { InitializeVariableIfNeeded(begin, local); } // Parse the body of the method Expect(Token.KwBegin); ParseStatements(Token.KwEnd); if (isFunction) { MethodGenerator.PushLocal(0); } MethodGenerator.EndMethod(); _symbolTable.CloseMethod(); }
internal void Add(FilePosition fp, string error) { _errors.Add(new Error(fp, error)); }
public Error(FilePosition fp, string error) { Position = fp; Text = error; }
internal void Add(int line, int column, string error) { FilePosition fp = new FilePosition(line, column); Add(fp, error); }
protected void ImportGlobalField( FilePosition fp, string symbolName, ImportedModule module, string declaringTypeName, string fieldName) { ImportedType declaringType = module.TryGetTypeByName(declaringTypeName); if (declaringType == null) { CompileErrors.Add(fp, String.Format("Cannot find imported type '{0}'.", declaringTypeName)); return; } ImportedField field = declaringType.TryGetPublicStaticField(fieldName); if (field == null) { CompileErrors.Add(fp, String.Format("Cannot find imported field '{0}.{1}'.", declaringTypeName, fieldName)); return; } IrisType fieldType = field.FieldType; if (fieldType == IrisType.Invalid) { CompileErrors.Add(fp, String.Format("Type of field '{0}.{1}' is not supported by the language.", declaringTypeName, fieldName)); return; } SymbolTable.Add(symbolName, field.FieldType, StorageClass.Global, field); }
public void BeginSourceLine(FilePosition fp) { if (_outputEnabled && _emitDebugInfo) { _lineStart = fp; _isSourceLine = true; } }
private IrisType ApplyTypeRules(FilePosition fp, IrisType lhs, IrisType rhs, bool boolResult = false) { // If there was a previous semantic error, don't apply any further rules. if (lhs == IrisType.Invalid || rhs == IrisType.Invalid) return IrisType.Invalid; string semanticError = null; if (lhs == IrisType.Void || rhs == IrisType.Void) semanticError = "Cannot apply operator to procedure call."; else if (lhs.IsByRef || rhs.IsByRef) semanticError = "Cannot take address of expression."; else if (lhs != rhs) semanticError = "Type mismatch error."; else if (!lhs.IsPrimitive) semanticError = "Operator requires a primitive type (boolean, integer, or string)."; if (semanticError != null) { AddError(fp, semanticError); return IrisType.Invalid; } else { return boolResult ? IrisType.Boolean : lhs; } }
private Symbol LookupSymbol(FilePosition symbolPosition, string name) { Symbol sym = _symbolTable.Lookup(name); if (sym == null) { // Undefined symbol. Emit an error and "fake" the symbol so we can continue. AddError(symbolPosition, string.Format("Symbol '{0}' is undefined.", name)); sym = _symbolTable.CreateUndefinedSymbol(name); } return sym; }
protected Operator AcceptOperator(Operator[] map) { Operator result = map[(int)_lexer.CurrentToken]; if (result != Operator.None) { _lastParsedPosition = _lexer.TokenEndPosition; _lexer.MoveNext(); } return result; }
private IrisType ProcessCall(FilePosition fp, Symbol symbol, bool skipArgList) { ParsedCallSyntax = true; IrisType symbolType = symbol.Type; if (!symbolType.IsMethod) { // Variables can have the same name as functions. If the symbol is not a method, // try looking up the same name in the global scope. If the global symbol is a // method, use it instead. Symbol globalSym = _symbolTable.LookupGlobal(symbol.Name); if (globalSym != null && globalSym.Type.IsMethod) { symbol = globalSym; symbolType = symbol.Type; } } IrisType resultType = IrisType.Invalid; bool semanticError = symbolType == IrisType.Invalid; if (!symbolType.IsMethod && !semanticError) { semanticError = true; AddError(fp, string.Format("Symbol '{0}' is not a procedure or function.", _lexeme)); } string symbolTypeName = symbolType.IsFunction ? "function" : "procedure"; Method method = symbolType as Method; Variable[] methodParams = method?.GetParameters(); int count = 0; if (!skipArgList && !Accept(Token.ChrCloseParen)) { do { FilePosition argPosition = _lexer.TokenStartPosition; if (methodParams != null && count < methodParams.Length) { Variable param = methodParams[count]; IrisType paramType = param.Type; IrisType argType = ParseExpression(paramType.IsByRef ? SymbolLoadMode.Address : SymbolLoadMode.Dereference); if (paramType != IrisType.Invalid && argType != IrisType.Invalid && paramType != argType) { if (paramType.IsByRef && !argType.IsByRef) { AddError(argPosition, "Cannot take address of constant, call, or expression."); } else { AddError(argPosition, string.Format( "Argument type doesn't match parameter '{0}' of {1} '{2}'", param.Name, symbolTypeName, symbol.Name)); } } } else { // Undefined method or too many arguments. Parse the argument without validation. ParseExpression(); } count++; }while (Accept(Token.ChrComma)); Expect(Token.ChrCloseParen); } // Verify argument count if (methodParams != null && methodParams.Length != count) { AddError(fp, string.Format( "Wrong number of arguments for {0} '{1}'. {2} expected. {3} provided.", symbolTypeName, symbol.Name, methodParams.Length, count)); } if (!semanticError) { MethodGenerator.Call(symbol); resultType = symbolType.IsFunction ? ((Function)symbolType).ReturnType : IrisType.Void; } return(resultType); }
protected IrisType ParseBaseExpression(SymbolLoadMode mode) { FilePosition fp = _lastParsedPosition; if (Accept(Token.Identifier)) { Symbol symbol = LookupSymbol(fp, _lexeme); if (Accept(Token.ChrOpenBracket)) { return(ProcessArrayAccess(fp, symbol, mode == SymbolLoadMode.Address ? SymbolLoadMode.ElementAddress : SymbolLoadMode.Element)); } if (Accept(Token.ChrOpenParen)) { return(ProcessCall(fp, symbol, skipArgList: false)); } IrisType type = symbol.Type; if (type.IsMethod) { return(ProcessCall(fp, symbol, skipArgList: true)); } if (type != IrisType.Invalid) { EmitLoadSymbol(symbol, mode); } if (mode == SymbolLoadMode.Address && !type.IsByRef) { return(type.MakeByRefType()); } else if (mode == SymbolLoadMode.Dereference) { return(DerefType(type)); } else { return(type); } } else if (Accept(Token.KwTrue)) { MethodGenerator.PushIntConst(1); return(IrisType.Boolean); } else if (Accept(Token.KwFalse)) { MethodGenerator.PushIntConst(0); return(IrisType.Boolean); } else if (Accept(Token.Number)) { MethodGenerator.PushIntConst(_lastIntegerLexeme); return(IrisType.Integer); } else if (Accept(Token.String)) { MethodGenerator.PushString(_lexeme); return(IrisType.String); } else if (Accept(Token.ChrOpenParen)) { IrisType type = ParseExpression(mode); Expect(Token.ChrCloseParen); return(type); } AddError(fp, "Expecting expression."); return(IrisType.Invalid); }
protected void ParseProgram() { string programName = "program"; if (Accept(Token.KwProgram)) { if (Accept(Token.Identifier)) { programName = _lexeme; } else { AddErrorAtTokenStart("Expecting program name."); } Expect(Token.ChrSemicolon); } _context.Emitter.BeginProgram(programName, _context.Importer.ImportedAssemblies); List <Tuple <Variable, FilePosition> > globals = new List <Tuple <Variable, FilePosition> >(); if (Accept(Token.KwVar)) { ParseVariableList(globals, isArgumentList: false); foreach (Tuple <Variable, FilePosition> globalDecl in globals) { Variable global = globalDecl.Item1; if (ValidateName(globalDecl.Item2, global.Name, global: true)) { Symbol globalSymbol = _symbolTable.Add(global.Name, global.Type, StorageClass.Global); _context.Emitter.DeclareGlobal(globalSymbol); } } } FilePosition blockBegin = _lexer.TokenStartPosition; while (!Accept(Token.KwBegin)) { if (Accept(Token.Eof)) { AddErrorAtLastParsedPosition("Unexpected end of file looking for main block."); return; } else if (Accept(Token.KwFunction)) { ParseMethod(isFunction: true); } else if (Accept(Token.KwProcedure)) { ParseMethod(isFunction: false); } else if (Accept(Token.KwVar)) { AddErrorAtTokenStart("Global variables must be declared before the first function or procedure."); List <Tuple <Variable, FilePosition> > unused = new List <Tuple <Variable, FilePosition> >(); ParseVariableList(unused, isArgumentList: false); } else if (!Accept(Token.ChrSemicolon)) { AddErrorAtTokenStart("Expecting 'function', 'procedure', or 'begin'."); SkipToNextEnd(); } blockBegin = _lexer.TokenStartPosition; } // We are now at the main block IrisType mainMethod = Procedure.Create(new Variable[0]); Symbol mainSymbol = _symbolTable.OpenMethod("$.main", mainMethod); MethodGenerator.BeginMethod(mainSymbol.Name, IrisType.Void, new Variable[0], new Variable[0], true, _context.FilePath); MethodGenerator.EmitNonCodeLineInfo(blockBegin.Expand(5 /* Length of "begin" */)); // Initialize global variables if needed foreach (Tuple <Variable, FilePosition> globalDecl in globals) { InitializeVariableIfNeeded(blockBegin, globalDecl.Item1); } ParseStatements(Token.KwEnd); MethodGenerator.EndMethod(); Accept(Token.ChrPeriod); Expect(Token.Eof); _context.Emitter.EndProgram(); }
protected void ParseStatement(bool allowEmpty = false) { FilePosition statementStart = _lexer.TokenStartPosition; MethodGenerator.BeginSourceLine(statementStart); if (Accept(Token.KwFor)) { Symbol iterator; FilePosition fp = _lexer.TokenStartPosition; if (!Accept(Token.Identifier)) { AddError(fp, "Expecting integer identifier."); SkipStatement(); return; } // Initial assignment iterator = LookupSymbol(fp, _lexeme); VerifyExpressionType(fp, DerefType(iterator.Type), IrisType.Integer); bool byRef = iterator.Type.IsByRef; if (byRef) { EmitLoadSymbol(iterator, SymbolLoadMode.Raw); } Expect(Token.ChrAssign); fp = _lexer.TokenStartPosition; IrisType rhs = ParseExpression(); VerifyExpressionType(fp, rhs, IrisType.Integer); if (byRef) { MethodGenerator.Store(rhs); } else { EmitStoreSymbol(iterator); } Expect(Token.KwTo); // Loop start and condition int loopLabel = GetNextLabel(); MethodGenerator.Label(loopLabel); EmitLoadSymbol(iterator, SymbolLoadMode.Dereference); fp = _lexer.TokenStartPosition; rhs = ParseExpression(); VerifyExpressionType(fp, rhs, IrisType.Integer); int exitLabel = GetNextLabel(); MethodGenerator.BranchCondition(Operator.GreaterThan, exitLabel); // Loop body Expect(Token.KwDo); FilePosition forEndPosition = _lastParsedPosition; MethodGenerator.EndSourceLine(forEndPosition); ParseStatement(); // Loop end MethodGenerator.BeginSourceLine(statementStart); // Source position is the same as the loop start. Increment(iterator); MethodGenerator.Goto(loopLabel); MethodGenerator.Label(exitLabel); MethodGenerator.EndSourceLine(forEndPosition); } else if (Accept(Token.KwWhile)) { int loopLabel = GetNextLabel(); MethodGenerator.Label(loopLabel); FilePosition fp = _lexer.TokenStartPosition; IrisType type = ParseExpression(); VerifyExpressionType(fp, type, IrisType.Boolean); int exitLabel = GetNextLabel(); MethodGenerator.BranchFalse(exitLabel); Expect(Token.KwDo); MethodGenerator.EndSourceLine(_lastParsedPosition); ParseStatement(); MethodGenerator.Goto(loopLabel); MethodGenerator.Label(exitLabel); } else if (Accept(Token.KwRepeat)) { int loopLabel = GetNextLabel(); MethodGenerator.Label(loopLabel); MethodGenerator.EmitNonCodeLineInfo(new SourceRange(statementStart, _lastParsedPosition)); ParseStatements(Token.KwUntil); FilePosition fp = _lexer.TokenStartPosition; IrisType type = ParseExpression(); VerifyExpressionType(fp, type, IrisType.Boolean); MethodGenerator.BranchFalse(loopLabel); MethodGenerator.EndSourceLine(_lastParsedPosition); } else if (Accept(Token.KwIf)) { ParseIf(); } else if (Accept(Token.KwBegin)) { MethodGenerator.EmitNonCodeLineInfo(new SourceRange(statementStart, _lastParsedPosition)); ParseStatements(Token.KwEnd); } else if (Accept(Token.Identifier)) { FilePosition fp = _lexer.TokenStartPosition; string symbolName = _lexeme; Symbol symbol = LookupSymbol(fp, symbolName); IrisType lhs = symbol.Type; bool assign = false; bool isArray = false; if (Accept(Token.ChrOpenBracket)) { // Assignment to an array element. isArray = true; lhs = ProcessArrayAccess(fp, symbol, SymbolLoadMode.Raw); } if (Accept(Token.ChrAssign)) { assign = true; bool indirectAssign = false; if (lhs.IsByRef) { lhs = lhs.GetElementType(); EmitLoadSymbol(symbol, SymbolLoadMode.Raw); indirectAssign = true; } FilePosition exprPosition = _lexer.TokenStartPosition; IrisType rhs = ParseExpression(); if (lhs.IsMethod) { AddError(fp, "Cannot assign to result of function or procedure call."); } else if (lhs != IrisType.Invalid) { if (rhs == IrisType.Void) { AddError(fp, "Cannot use procedure in assignment statement."); } else if (rhs != IrisType.Invalid && rhs != lhs) { AddError(exprPosition, string.Format("Cannot assign to '{0}' (type mismatch error).", symbolName)); } if (isArray) { MethodGenerator.StoreElement(lhs); } else if (indirectAssign) { MethodGenerator.Store(lhs); } else { EmitStoreSymbol(symbol); } } } else if (isArray) { // This is an array subscript. Assignment is the only kind of statement that // starts with an array subscript. AddErrorAtTokenStart("Expecting ':='."); SkipStatement(); } if (!assign && !isArray) { bool skipArgList = !Accept(Token.ChrOpenParen); ProcessCall(fp, symbol, skipArgList); if (symbol.Type.IsFunction) { MethodGenerator.Pop(); } } MethodGenerator.EndSourceLine(_lastParsedPosition); } else if (Accept(Token.KwElse)) { AddErrorAtTokenStart("Cannot start statement with 'else' or unexpected ';' after if statement."); SkipStatement(); } else if (!allowEmpty && !Accept(Token.ChrSemicolon)) { AddErrorAtLastParsedPosition("Expecting statement."); SkipStatement(); } }
private IrisType ProcessCall(FilePosition fp, Symbol symbol, bool skipArgList) { ParsedCallSyntax = true; IrisType symbolType = symbol.Type; if (!symbolType.IsMethod) { // Variables can have the same name as functions. If the symbol is not a method, // try looking up the same name in the global scope. If the global symbol is a // method, use it instead. Symbol globalSym = _symbolTable.LookupGlobal(symbol.Name); if (globalSym != null && globalSym.Type.IsMethod) { symbol = globalSym; symbolType = symbol.Type; } } IrisType resultType = IrisType.Invalid; bool semanticError = symbolType == IrisType.Invalid; if (!symbolType.IsMethod && !semanticError) { semanticError = true; AddError(fp, string.Format("Symbol '{0}' is not a procedure or function.", _lexeme)); } string symbolTypeName = symbolType.IsFunction ? "function" : "procedure"; Method method = symbolType as Method; Variable[] methodParams = method?.GetParameters(); int count = 0; if (!skipArgList && !Accept(Token.ChrCloseParen)) { do { FilePosition argPosition = _lexer.TokenStartPosition; if (methodParams != null && count < methodParams.Length) { Variable param = methodParams[count]; IrisType paramType = param.Type; IrisType argType = ParseExpression(paramType.IsByRef ? SymbolLoadMode.Address : SymbolLoadMode.Dereference); if (paramType != IrisType.Invalid && argType != IrisType.Invalid && paramType != argType) { if (paramType.IsByRef && !argType.IsByRef) { AddError(argPosition, "Cannot take address of constant, call, or expression."); } else { AddError(argPosition, string.Format( "Argument type doesn't match parameter '{0}' of {1} '{2}'", param.Name, symbolTypeName, symbol.Name)); } } } else { // Undefined method or too many arguments. Parse the argument without validation. ParseExpression(); } count++; } while (Accept(Token.ChrComma)); Expect(Token.ChrCloseParen); } // Verify argument count if (methodParams != null && methodParams.Length != count) { AddError(fp, string.Format( "Wrong number of arguments for {0} '{1}'. {2} expected. {3} provided.", symbolTypeName, symbol.Name, methodParams.Length, count)); } if (!semanticError) { MethodGenerator.Call(symbol); resultType = symbolType.IsFunction ? ((Function)symbolType).ReturnType : IrisType.Void; } return resultType; }
protected bool Accept(Token token) { if (_lexer.CurrentToken == token) { if (token != Token.Eof) _lastParsedPosition = _lexer.TokenEndPosition; if (token == Token.String || token == Token.Identifier) _lexeme = _lexer.GetLexeme(); else if (token == Token.Number) _lastIntegerLexeme = _lexer.ParseInteger(); _lexer.MoveNext(); return true; } return false; }
private bool ValidateName(FilePosition fp, string name, bool global) { if (name[0] == '$') AddError(fp, "Identifiers starting with '$' are reserved."); Symbol existing = global ? _symbolTable.LookupGlobal(name) : _symbolTable.LookupLocal(name); if (existing != null) { AddError(fp, string.Format("Cannot redefine symbol '{0}'.", name)); return false; } return true; }
public void EndSourceLine(FilePosition fp) { if (_outputEnabled && _isSourceLine) { _emitter.EmitLineInfo(new SourceRange(_lineStart, fp), _methodFileName ?? string.Empty); _isSourceLine = false; _methodFileName = null; // We only need to emit this for the first line } EmitDeferredInstructions(); }
private void VerifyExpressionType(FilePosition fp, IrisType actual, IrisType expected) { if (actual != expected) AddError(fp, string.Format("Expecting {0} expression.", expected)); }
protected void ImportMethod( FilePosition fp, string symbolName, ImportedModule module, string declaringTypeName, string methodName, bool instance, IrisType returnType, IrisType[] paramTypes) { ImportedType declaringType = module.TryGetTypeByName(declaringTypeName); if (declaringType == null) { CompileErrors.Add(fp, String.Format("Cannot find imported type '{0}'.", declaringTypeName)); return; } ImportedMethod importedMethod = declaringType.TryFindMethod(methodName, instance, returnType, paramTypes); if (importedMethod == null) { CompileErrors.Add(fp, String.Format("Cannot find imported function or procedure '{0}.{1}'.", declaringTypeName, methodName)); return; } Method method = importedMethod.ConvertToIrisMethod(); bool containsInvalidType = method.ReturnType == IrisType.Invalid; foreach (Variable param in method.GetParameters()) { if (containsInvalidType) break; if (param.Type == IrisType.Invalid) containsInvalidType = true; } if (containsInvalidType) CompileErrors.Add(fp, String.Format("The function or procedure '{0}.{1}' contains types that are not supported by the language.", declaringTypeName, methodName)); else SymbolTable.Add(symbolName, method, StorageClass.Global, importedMethod); }
protected void AddError(FilePosition fp, string error) { MethodGenerator.SetOutputEnabled(false); _context.CompileErrors.Add(fp, error); }
private void InitializeVariableIfNeeded(FilePosition fp, Variable variable) { IrisType varType = variable.Type; if (varType.IsArray || varType == IrisType.String) { // Variable needs to be initialized. Symbol varSymbol = _symbolTable.Lookup(variable.Name); if (varType.IsArray) { MethodGenerator.InitArray(varSymbol, variable.SubRange); if (varType.GetElementType() == IrisType.String) { // String arary - initialize all elements EmitLoadSymbol(varSymbol, SymbolLoadMode.Raw); Symbol initProc = LookupSymbol(fp, "$.initstrarray"); MethodGenerator.Call(initProc); } } else { // String Symbol emptyStr = LookupSymbol(fp, "$.emptystr"); MethodGenerator.PushGlobal(emptyStr); EmitStoreSymbol(varSymbol); } MethodGenerator.EmitDeferredInstructions(); } }
private IrisType ProcessArithemticOperator(FilePosition fp, IrisType lhs, IrisType rhs, Operator opr) { IrisType resultType = ApplyTypeRules(fp, lhs, rhs); if (resultType == IrisType.String) { if (opr != Operator.Add) AddError(fp, "Only the '+' or comparison operators can be used on string values."); Symbol concat = LookupSymbol(fp, "concat"); MethodGenerator.Call(concat); } else if (resultType == IrisType.Boolean) { AddError(fp, "Arithmetic operators cannot be applied to boolean values."); return IrisType.Invalid; } else { MethodGenerator.Operator(opr); } return resultType; }