""" The code in this file should not .throwError, record warnings, etc. All of that happens during .bindFoo phases. """ ## ## Compiler ## class Compiler is partial # executable filenames var _baseExeFileName as String = '' var _fullExeFileName as String = '' get clrBaseExeFileName from _baseExeFileName get clrFullExeFileName from _fullExeFileName # The next two properties are set in GenerateSharpCodePhase pro nativeCompiler from var = '' pro willWriteSharpToDisk from var as bool var _sharpSource = Dictionary() def createSharpFile(fileName as String, contents as String) if .willWriteSharpToDisk, File.writeAllText(fileName, contents) else, _sharpSource[fileName] = contents .addIntermediateFile(fileName) def writeSharp Node.setCompiler(this) try _moduleFileName_to_curlyToCobraLineNum = Dictionary>() for _curModule in _modules if not _curModule.fileName.endsWith('SystemInterfaces.cobra') sharpToCobraLineNum = _curModule.writeSharpDef _moduleFileName_to_curlyToCobraLineNum[_curModule.fileName] = sharpToCobraLineNum _moduleFileName_to_curlyToCobraLineNum[Path.getFullPath(_curModule.fileName)] = sharpToCobraLineNum finally # Node.setCompiler(nil) pass def writeSharpTestInvocation dt = DateTime.now fileName = 'test-[dt.year][dt.month][dt.day][dt.hour][dt.minute].cs' # to-do: format the numbers to have leading zeros w = CodePoundLineWriter() w.writeLine('using CobraCoreInternal = Cobra.Core[.embedRunTimeSuffix];') w.writeLine('') w.writeLine('class _GeneratedTestRunner {') w.writeLine('') w.writeLine(' public static void Main() {') w.writeLine(' CobraCoreInternal.CobraImp.ShowTestProgress = true;') if .hasExceptionReportOption w.writeLine(' try {') .writeSharpRunAllTests(w) if .hasExceptionReportOption w.writeLine(' } catch (System.Exception e) {') w.writeLine(' CobraCoreInternal.CobraCore.HandleUnhandledException(e);') w.writeLine(' }') w.writeLine(' }') w.writeLine('}') .createSharpFile(fileName, w.output) .mainMethodTypeName = '_GeneratedTestRunner' _modules.add(SharpModule(FileSpec(fileName, w.output), _verbosity)) def writeSharpRunAllTests(cw as CurlyWriter) runner = .options['test-runner'] to String # ex: Cobra.Core.CobraCore.runAllTests, ex: MyProgram.runTests if runner <> 'nil' if runner.endsWith('()'), runner = runner[:-2] if runner.startsWith('Cobra.Core.'), runner = 'CobraCoreInternal.' + runner['Cobra.Core.'.length:] parts = List(runner.split(c'.')) if parts.count > 1 parts[parts.count-1] = parts.last[0].toUpper.toString + parts.last[1:] stmt = parts.join('.') + '();' cw.writeLine(stmt) var _didWriteSharpInfoClass as bool var _platform as PlatformEnum? get platform as PlatformEnum if sharp'_platform == null' # CC: take out the sharp'' after the next snapshot # if _platform is nil _platform = if(CobraCore.isRunningOnMono, PlatformEnum.Novell, PlatformEnum.Microsoft) return _platform to ! def computeOutNameSharp as String ensure result == _fullExeFileName outName = '' if .options.boolValue('test') outName = _modules.last.sharpFileName else if .options.containsKey('out') outName = .options.getDefault('out', '') if outName == '', outName = .defaultOutName to ! if outName.endsWith('.cs') or outName.endsWith('.CS'), outName = outName[:-3] if outName.endsWith('.cobra') or outName.endsWith('.COBRA'), outName = outName[:-6] _baseExeFileName = outName target = .options.getDefault('target', '') to String if .options.boolValue('test') and target == 'lib', target = 'exe' if target.length # TODO: what is the output for a module? branch target on 'exe' or 'winexe', outName = Utils.forceExtension(outName, '.exe') on 'lib', outName = Utils.forceExtension(outName, '.dll') on 'module', outName = Utils.forceExtension(outName, '.netmodule') # http://msdn2.microsoft.com/en-us/library/58scf68s(VS.80).aspx else, throw FallThroughException(target) else outName = Utils.forceExtension(outName, '.exe') _fullExeFileName = outName return outName def compileSharp .compileSharp(List()) def compileSharp(extraCscOptions as List) require .modules.count body v, options = .verbosity, .options optChar = if(.platform==PlatformEnum.Microsoft, '/', '-') # option prefix character # exe names outName = .computeOutNameSharp # compute backEndOptions backEndOptions = List() target = options.getDefault('target', '') to String if target.length if options.boolValue('test') and target == 'lib' # C# doesn't allow a .main method designation on libraries, # so don't respect the -target in this situation pass else if target=='lib', target = 'library' backEndOptions.add('[optChar]target:[target]') delaySign = options.boolValue('delay-sign') if delaySign, backEndOptions.add('[optChar]delaysign+') keyContainer = options.getDefault('key-container', nil) if keyContainer, backEndOptions.add('[optChar]keycontainer:[keyContainer]') keyFile = options.getDefault('key-file', nil) if keyFile, backEndOptions.add('[optChar]keyfile:[keyFile]') optimize = options.boolValue('optimize') if optimize, backEndOptions.add('[optChar]optimize+') for refer in .loadedReferences # don't take the library references from options, because the references can grow if refer == 'Cobra.Lang.dll', continue # hack. remove at some point 2012-07-17 backEndOptions.add('[optChar]r:[refer]') debug = options.getDefault('debug', '') to String if debug.length # TODO: mono does not support full and pdbonly afaik if debug == '0', debug = '-' else if debug == '1', debug = '+' assert debug in ['-', '+', 'full', 'pdbonly'], debug backEndOptions.add('[optChar]debug' + if(debug.length==1, debug, ':[debug]')) platform = options.getDefault('clr-platform', '') to String if platform.length backEndOptions.add('[optChar]platform:[platform]') if v < 3, backEndOptions.add('[optChar]nologo') backEndOptions.add('[optChar]nowarn:0108,0162,0169,0183,0184,0219,0414,0429,1717,1718') # CS0108: Hides inherited member. Mono reports this for workspace/Tests/720-libraries/400-other/500-follow-dependencies/102-lib-b.cobra. MS does not. For multiple reasons, disabling is the best choice here. # CS0162: Unreachable code detected # CS0169: The private field `Cobra.Core.CannotReadPropertyException._ih_invariantGuard' is never used # CS0183: The given expression is always of the provided (`int') type # CS0184: The given expression is never of the provided (`decimal') type # CS0219: The variable `x' is assigned but its value is never used / Cobra handles these # CS0414: The private field `Point._ih_invariantGuard' is assigned but its value is never used # CS0429: Unreachable expression code detected / MS C# 2.0 gives these especially for the 'bool' test case in 'basics' # CS1717: Assignment made to same variable; did you mean to assign something else? # CS1718: A comparison made to same variable. if .mainMethodTypeName <> '', backEndOptions.add('[optChar]main:[.mainMethodTypeName]') backEndOptions.addRange(extraCscOptions) sharpArgs = .options.getDefault('native-compiler-arg', List()) to List if sharpArgs.count for sharpArg in sharpArgs if sharpArg.length > 2 and sharpArg[0]=="'" and sharpArg[sharpArg.length-1]=="'" # on Windows, you should really use double quotes instead of single, but # we try to compensate here. sharpArg = sharpArg[1:-1] if sharpArg.length, backEndOptions.add(sharpArg) for libPath in .options.getStringList('library-directory') backEndOptions.add('-lib:[libPath]') # Cobra.Core.dll is in the same directory as cobra.exe libPath = Path.getDirectoryName(CobraCore.exePath) to ! backEndOptions.add('-lib:[libPath]') # Passing -pkg: to the C# compiler seems unnecessary. # Compiler.refsForPackage already gets the .dll refs and including this was reported at # http://cobra-language.com/forums/viewtopic.php?f=4&t=263 2008-12 / 2009-01 # for pkgName in .options.getStringList('pkg') # backEndOptions.add('-pkg:[pkgName]') # .cs files sharpFileNameList = List() for module in _modules if module.sharpFileName.length sharpFileNameList.add(module.sharpFileName) assert sharpFileNameList.count # compilation command if v, print 'Compiling to produce [outName]' # the 'auto' choice for -native-compiler is handled in GenerateSharpCodePhase nativeCompiler = .nativeCompiler if nativeCompiler == 'provider' # otherwise use Code Provider nativeCompiler = _compileSharpWithCodeProvider(optChar, .joinOptions(backEndOptions), sharpFileNameList) ? '' if nativeCompiler <> '' _compileSharpWithProcessCSC(nativeCompiler, optChar, .joinOptions(backEndOptions), sharpFileNameList) else if nativeCompiler.endsWith('.dll') # assume the user is specifying some kind of Cobra.Sharp*.dll _compileSharpWithSharpCompilerLib(nativeCompiler, optChar, backEndOptions, sharpFileNameList) else if nativeCompiler <> '' # use Process class instead of CSharpCodeProvider because the user specified a path to a C# compiler _compileSharpWithProcessCSC(optChar, .joinOptions(backEndOptions), sharpFileNameList) else .recordError(SourceException('Unknown -native-compiler:[nativeCompiler]')) _deleteIntermediateFiles _copyCoreClr(outName) def _copyCoreClr(outName as String) if not .options.boolValue('copy-core'), return sourceDir = Path.getDirectoryName(Assembly.getEntryAssembly.location) ? '' outDir = Path.getDirectoryName(outName) ? '' _copyFile(sourceDir, outDir, 'Cobra.Core.dll') if .options.getDefault('debug', '') not in ['', '-', '0', 0, false] if CobraCore.isRunningOnMono and File.exists(Path.combine(sourceDir, 'Cobra.Core.dll.mdb')) _copyFile(sourceDir, outDir, 'Cobra.Core.dll.mdb') else if File.exists(Path.combine(sourceDir, 'Cobra.Core.pdb')) _copyFile(sourceDir, outDir, 'Cobra.Core.pdb') def _copyFile(sourceDir as String, outDir as String, fileName as String) source = Path.combine(sourceDir, fileName) destination = Path.combine(outDir, fileName) try File.copy(source, destination, true) catch exc as Exception .recordError(SourceException('Cannot copy [fileName] due to: [exc.typeOf.name]: [exc.message]')) def joinOptions(options as List) as String sb = StringBuilder() sep = '' for opt in options sb.append(sep) if '"' not in opt sb.append('"[opt]"') # helps with: '-foo:path with spaces' else sb.append(opt) sep = ' ' return sb.toString var _cobraSharpProxy as CobraSharpProxy? def _compileSharpWithSharpCompilerLib(dllName as String, optChar as String, backEndOptions as List, sharpFileNameList as List) v = .verbosity if _cobraSharpProxy is nil _cobraSharpProxy = CobraSharpProxy(dllName) if _cobraSharpProxy.error print 'Cannot use C# compiler library because:' print _cobraSharpProxy.error throw StopCompilation(this) _cobraSharpProxy.reset backEndOptions.add('[optChar]out:[_fullExeFileName]') args = List(backEndOptions) args.addRange(sharpFileNameList) filenameToSource = nil to IDictionary? if .willWriteSharpToDisk if v >= 3, print 'Intermediate code is on disk.' else if v >= 3, print 'Intermediate code is in memory.' filenameToSource = Dictionary() for fileName, writer in _sharpSource filenameToSource.add(fileName, writer.toString) if _verbosity >= 2, print '[dllName] [args]' output = StringWriter() saveOut = Console.out Console.setOut(output) try _cobraSharpProxy.invokeCompiler(args.toArray to !, output, filenameToSource) finally Console.setOut(saveOut) _parseSharpCompilerOutput(output.toString) def _compileSharpWithProcessCSC(optChar as String, backEndOptions as String, sharpFileNameList as List) # locate the C# compiler cscPath = .options.getDefault('native-compiler', '') to String assert cscPath <> '' if File.exists(cscPath) pass else if File.exists(cscPath+'.exe') cscPath += '.exe' _compileSharpWithProcessCSC(cscPath, optChar, backEndOptions, sharpFileNameList) def _compileSharpWithProcessCSC(cscPath as String, optChar as String, backEndOptions as String, sharpFileNameList as List) backEndOptions += ' "[optChar]out:[_fullExeFileName]"' p = System.Diagnostics.Process() p.startInfo.fileName = cscPath sharpFileNames = (for fileName in sharpFileNameList get '"' + fileName + '"').join(' ') p.startInfo.arguments = '[backEndOptions] [sharpFileNames]' if _verbosity >= 2 print '[p.startInfo.fileName] [p.startInfo.arguments]' try output = p.runAndCaptureAllOutput catch exc as System.ComponentModel.Win32Exception # ex: 'ApplicationName='gmcsaoeu', CommandLine='"-r:Cobra.Core.dll" "-nologo" ...' /# Win32Exception.NativeErrorCode http://msdn.microsoft.com/en-us/library/ms681382.aspx 0 = ERROR_SUCCESS 1 = ERROR_INVALID_FUNCTION 2 = ERROR_FILE_NOT_FOUND 3 = ERROR_PATH_NOT_FOUND 4 = ERROR_TOO_MANY_OPEN_FILES 5 = ERROR_ACCESS_DENIED ... #/ if exc.nativeErrorCode == 2 # Sometimes Mono will look for 'dmcs' in the PATH and find it, # and other times it will not. Don't know why. if not Path.directorySeparatorChar in cscPath find = Utils.findFileInSystemPath(cscPath) if find if _verbosity >= 1, print 'Found "[cscPath]" at "[find]".' _compileSharpWithProcessCSC(find, optChar, backEndOptions, sharpFileNameList) return .recordError(SourceException('Cannot find compiler specified by -native-compiler argument: [cscPath]')) if Utils.isDevMachine print exc print 'errorCode:', exc.errorCode print 'nativeErrorCode:', exc.nativeErrorCode return else throw # TODO: check p.exitCode, especially if output is empty _parseSharpCompilerOutput(output) def _compileSharpWithCodeProvider(optChar as String, backEndOptions as String, sharpFileNameList as List) as String? """ Returns a compiler to use if the CodeDom Compiler failed mysteriously. Otherwise, returns nil. """ provider = Microsoft.CSharp.CSharpCodeProvider() cp = System.CodeDom.Compiler.CompilerParameters() cp.compilerOptions = backEndOptions cp.referencedAssemblies.add('System.dll') cp.generateExecutable = .options.boolValue('test') or .options.getDefault('target', 'exe') in ['exe', 'winexe'] cp.outputAssembly = _fullExeFileName if _verbosity >= 2 print 'Code Provider =', provider print ' compilerOptions =', backEndOptions print ' referencedAssemblies =', cp.referencedAssemblies print ' generateExecutable =', cp.generateExecutable print ' outputAssembly =', _fullExeFileName print ' sharpFileNameList =', sharpFileNameList try cr = provider.compileAssemblyFromFile(cp, sharpFileNameList.toArray) catch exc as SystemException # On Mono targeting CLR 2, the codedom provider works fine. # However, on at least Mono 2.10.8.1 on Ubuntu 12.04 targeting CLR **4** # this exception can be thrown: # SystemException: Error running dmcs: Cannot find the specified file # ...despite the fact that dmcs is in the PATH, sits next to gmcs, # works from the command line, etc. if 'Error running dmcs: Cannot find the specified file' in exc.toString if _verbosity >= 2, print 'CodeDom compiler failed for dmcs. Switching to external compiler.' return 'dmcs' else throw if false trace cr.compiledAssembly trace cr.evidence trace cr.pathToAssembly trace cr.errors trace cr.output trace cr.nativeCompilerReturnValue trace cr.tempFiles # On MS .NET 2.0, the CompilerResult.errors never include "(Location of symbol related to previous error)", while the CompilerResult.output contains everything # On Novell Mono 1.9, the CompilerResult.output is empty, leaving CompilerResult.errors as the only means to get the C# compiler messages. # Do the Mono .errors have "(Location of symbol related to previous error)"? I haven't checked. if CobraCore.isRunningOnMono for bem in cr.errors # cr.errors also includes warnings. supposedly. backEndMessage = bem to ! if not SharpCompilationMessage.willSkipMessage(backEndMessage.errorText to !) _addMessage(SharpCompilationMessage(backEndMessage, this)) else #for x in cr.output to dynamic # print 'output>', x # [1:] below is to chop out the first line which is the C# compilation line # cr.output is empty if there was no error # otherwise, the first element is the C# compilation line if _verbosity >= 2 and cr.output.count > 0 print 'C# compilation line created by CSharpCodeProvider:' print cr.output[0] output = if(cr.output.count==0, '', cr.output[1:].join('\n').trim) # trace output _parseSharpCompilerOutput(output) return nil # nil means that an external compiler does not have to be run def _parseSharpCompilerOutput(output as String) for line in output.split(c'\n') line = line.trim if not line.length, continue if SharpCompilationMessage.willSkipMessage(line), continue if _verbosity >= 3, print 'C# message: [line]' _addMessage(SharpCompilationMessage(line, this)) ## ## Node ## class Node is partial def writeSharpDef(sw as CurlyWriter) require .didBindInt or .didBindImp pass class SyntaxNode is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) sw.node(this) interface INamedNode is partial get sharpName as String """ Return the name that should be used in C# source code. """ class NamedNode is partial get sharpName as String return _name get sharpRef as String return .sharpName ## ## Module ## class Module is partial var _sharpSource as String? var _sharpFileName = '' get sharpFileName from var get sharpSource as String if _sharpSource, return _sharpSource if _sharpFileName.length return File.readAllText(_sharpFileName) else throw FallThroughException('No sharp source or filename.') def writeSharpDef as Dictionary? is abstract def writeSharpTestInvocation(sw as CurlyWriter) pass class JavaModule inherits NativeModule is partial def writeSharpDef as Dictionary? is override assert false return nil class SharpModule inherits NativeModule is partial """ The purpose of a C# module is to allow the programmer to include .cs source for Cobra to incorporate in the final compilation. """ cue init(file as FileSpec, verbosity as int) base.init(file, verbosity, '') _sharpFileName = file.path to ! def writeSharpDef as Dictionary? is override return nil class AssemblyModule is partial get sharpSource as String is override return '(no C# source for [this])' def writeSharpDef as Dictionary? is override return nil class CobraModule is partial get sharpFileName as String is override if _sharpFileName == '' and not _fileName.endsWith('SystemInterfaces.cobra') _sharpFileName = _fileName + '.cs' return _sharpFileName def writeSharpDef as Dictionary? is override # Generate writer = CodePoundLineWriter() writer.start(Path.getFileName(.fileName) to !) # without the use of Path.getFileName above, the generated code can be: # #line 1 "subdir/Foo.cobra" # and then in debugging the "subdir" can be doubled up. # This happens on both Mono 2.10 on Linux and Mac, and .NET 4.0 on Windows writer.write('// [_sharpFileName]\n') writer.write('// Generated by Cobra\n') # TODO: put version number here writer.write('// on [DateTime.now]\n') # TODO: list op sys writer.write('\n') if .isMainWrapper .synthesizeSharpMainWrapper(writer) else writer.write('using CobraCoreInternal = Cobra.Core[.compiler.embedRunTimeSuffix];\n') .topNameSpace.writeSharpDef(writer) d = writer.curlyToCobraLineNum # Output #print 'Output: ', writer.output (.compiler to Compiler).createSharpFile(.sharpFileName, writer.output) return d def writeSharpTestInvocation(sw as CurlyWriter) is override .topNameSpace.writeSharpTestInvocation(sw) def synthesizeSharpMainWrapper(sw as CurlyWriter) """ Generate a wrapper class with a valid main method for C#/Clr The wrapper will instantiate and call the provided user main method. """ # Module top namespace is expected to have a first entry which is a dummy class holding # the method (+ class ref) for the mainwrapper to instantiate and invoke. assert .topNameSpace.declsInOrder.count assert .topNameSpace.declsInOrder[0] inherits Class placeHolderClass = .topNameSpace.declsInOrder[0] to Class assert placeHolderClass.declsInOrder.count assert placeHolderClass.declsInOrder[0] inherits AbstractMethod mainMethod = placeHolderClass.declsInOrder[0] to AbstractMethod # method data sw.write('// Cobra synthesized program entrypoint wrapping call to user supplied main method\n\n') sw.writeAndIndent('class MainWrapper : System.Object {\n') sw.bumpLineNum for attr in mainMethod.attributes attr.writeSharpDef(sw, '', false) if mainMethod.attributes.count, sw.writeLine sw.writeAndIndent('public static void Main() {\n') sw.bumpLineNum sw.writeLine('(new [mainMethod.parentBox.qualifiedName]()).Main();') sw.bumpLineNum sw.dedentAndWrite('} // end Main\n\n') sw.bumpLineNum sw.writeLine('public MainWrapper()') sw.indentAndWrite(': base() {\n') sw.dedentAndWrite('}') sw.write('\n') sw.writeLine('} // class MainWrapper') ## ## Container and friends ## interface IParentSpace is partial get sharpRef as String class Container is partial get sharpInit as String is abstract get sharpNameComponent as String """ Returns a string that can be used in a C# identifier. That means it cannot have special symbols such as period or left bracket. """ ensure result.length > 0 # TODO? Move to interface return .sharpRef var _sharpRef as String? get sharpRef as String is override if _sharpRef is nil and .didBindImp _sharpRef = _computeSharpRef return _sharpRef to ! else return _computeSharpRef get sharpParamRef as String return .sharpRef def _computeSharpRef as String if .parent s = .parent.sharpRef if s.length and not s.endsWith('::') # C# has weird "global::" name s += '.' s += .sharpName else s = .sharpName return s def writeSharpIsNames(sw as CurlyWriter) isNameCS = { # only have to specify the ones that are different 'shared': 'static', 'nonvirtual': '', } sep = '' for name in _isNames name = isNameCS.get(name, name) sw.write(sep) sw.write(name) sep = ' ' if sep.length, sw.write(' ') def writeSharpTestInvocation(sw as CurlyWriter) pass interface IMember is partial def writeSharpDef(sw as CurlyWriter) """ Write the C# code for this member declaration to the given CurlyWriter. """ def writeSharpTestInvocation(sw as CurlyWriter) """ Write the C# call to the test method for this member. """ ## ## Namespace ## class NameSpace is partial get sharpFullName as String value as Object? if not .addOnValues.tryGetValue('sharpFullName', out value) fullName = .fullName if fullName == 'Cobra.Core' name = 'Cobra.Core' + .compiler.embedRunTimeSuffix else if fullName in ['Cobra.Core.Test'] # must include all namespaces found in CobraWorkspace/Source/Cobra.Core # else if fullName.startsWith('Cobra.Core.') -- this breaks -namespace:Cobra.Core -ert:yes (used in comp-compiler-dll script) name = 'Cobra.Core[.compiler.embedRunTimeSuffix].' + fullName['Cobra.Core.'.length:] else name = fullName .addOnValues['sharpFullName'] = name return name else return value to String get sharpInit as String is override # TODO: remove when not a type assert false return '' get sharpRef as String is override if _superNameSpace is nil return 'global::' else return 'global::' + .sharpFullName get sharpQualifier as String ensure result.endsWith('.') or result == 'global::' if _superNameSpace is nil return 'global::' else return 'global::' + .sharpFullName + '.' def writeSharpDef(sw as CurlyWriter) is override assert not .isUnified base.writeSharpDef(sw) if not .isRoot # to-do: isn't the following incorrect for Cobra.Core.Test? suffix = if(.fullName == 'Cobra.Core', .compiler.embedRunTimeSuffix, '') sw.writeAndIndent('namespace [.name][suffix] {\n\n') for ud in _useDirectives ud.writeSharpDef(sw) for decl in _declsInOrder if decl inherits Box .compiler.boxStack.push(decl) decl.writeSharpDef(sw) if decl inherits Box .compiler.boxStack.pop if not .isRoot sw.dedentAndWrite('} // namespace [.name]\n') def writeSharpTestInvocation(sw as CurlyWriter) is override for decl in _declsInOrder decl.writeSharpTestInvocation(sw) class UseDirective is partial # Do not rely on C#'s using statement as it could lead to problems: # -- The semantics between Cobra `use` and C# `using` can be different. # -- Using Cobra.Core library type names without qualification in the generated code could lead to confusing errors if the user happens to have a type with the same name. # -- You can't used unqualified type names in IL code which we may migrate to in the future. Might as well deal with this now. # def writeSharpDef(sw as CurlyWriter) is override # sw.write('using [.boundNameSpace.sharpRef];\n') pass ## ## Types ## class FakeLibraryType is partial get sharpInit as String throw NoSourceGenerationException(this) get sharpName as String throw NoSourceGenerationException(this) get sharpNameComponent as String throw NoSourceGenerationException(this) get sharpParamRef as String throw NoSourceGenerationException(this) get sharpRef as String throw NoSourceGenerationException(this) def writeSharpDef(sw as CurlyWriter) throw NoSourceGenerationException(this) def writeSharpTestInvocation(sw as CurlyWriter) throw NoSourceGenerationException(this) interface IType is partial get sharpInit as String get sharpNameComponent as String """ Returns a string that refers to this type and is suitable for embedding in a larger identifier (meaning there will be no punction, spaces or C# comments). """ get sharpRef as String """ Returns a string that refers to this type. Examples: 'int' 'List' 'object' """ get sharpParamRef as String """ Returns a string that refers to this type including any necessary parameter declaration specification such as C# 'params' or 'out'. Invoked by Param.writeSharpDef. """ class CobraType is partial get sharpInit as String is abstract get sharpName as String return .name get sharpNameComponent as String name = .name i = name.indexOf('/*') # example: /*dynamic*/object if i <> -1 j = name.indexOf('*/', i) name = name[:i] + name[j+2:] name = name.replace('.', '_').replace('<', '_').replace('>', '_').replace(', ', '_') return name get sharpRef as String return .sharpName get sharpParamRef as String return .sharpRef def writeSharpTestInvocation(sw as CurlyWriter) pass class BoolType is partial get sharpInit as String is override return 'false' class CharType is partial get sharpInit as String is override return '(char)0' class DynamicType is partial get sharpInit as String is override return 'null' get sharpName as String is override return '/*dynamic*/object' class FloatType is partial get sharpName as String is override return _nativeType.fullName class IntType is partial get sharpName as String is override return _nativeType.fullName class NilableType is partial get sharpInit as String is override return 'null' get sharpRef as String is override return _wrappedType.sharpRef + if(not _wrappedType.isReference, '?', '') class NilType is partial get sharpInit as String is override return 'null' get sharpRef as String is override return '/*nil*/object' class AbstractNumberType is partial get sharpInit as String is override return '0' class PassThroughType is partial get sharpInit as String is override return 'null' get sharpRef as String is override return '/*passthrough*/object' class StreamType is partial get sharpInit as String is override return 'new CobraCoreInternal.EmptyStream<[.theWrappedType.sharpRef]>()' get sharpRef as String is override assert .didBindInh and .didBindInt return '/*[.name]*/[.box.sharpRef]' class VoidType is partial get sharpInit as String is override throw Exception('Cannot init a void type.') class WrappedType is partial get sharpInit as String is override return _wrappedType.sharpInit class ArrayType is partial get sharpRef as String is override return '[_wrappedType.sharpRef]' + r'[]' get sharpInit as String is override return 'null' class VariType is partial get sharpRef as String is override return '[_wrappedType.sharpRef]' + r'[]' get sharpParamRef as String is override return 'params ' + .sharpRef class UnspecifiedType is partial get sharpInit as String is override # TODO: throw Exception('Not expecting code gen for unspecified types.') return '!UnspecifiedType!' ## ## Enums ## class EnumDecl is partial get sharpInit as String is override return .sharpRef + '.' + _declsInOrder[0].name def writeSharpDef(sw as CurlyWriter) is override base.writeSharpDef(sw) if 'extern' in _isNames return for attr in .attributes attr.writeSharpDef(sw) .writeSharpIsNames(sw) sw.write('enum [_name]') storageType = _storageType if storageType and storageType is not .compiler.intType # Cannot say UInt64 because: # C# error: Type byte, sbyte, short, ushort, int, uint, long, or ulong expected st = storageType to IntType branch st.size on 8, t = 'byte' on 16, t = 'short' on 32, t = 'int' on 64, t = 'long' else, throw FallThroughException(st) if not st.isSigned, t = 'u' + t sw.write(' : [t]') sw.write(' {\n') sw.indent sep, i = '', 0 for em as EnumMember in .declsInOrder sw.write(sep) sw.write(em.name) if em.value, sw.write(' = [em.value]') i += 1 sep = ',\n' sw.write('\n') sw.dedent sw.write('}\n\n') class EnumMember is partial def writeSharpTestInvocation(sw as CurlyWriter) pass get sharpRef as String is override return _enumDecl.sharpRef + '.' + .sharpName ## ## Boxes ## class Box is partial get sharpNameComponent as String is override name = .sharpName i = name.indexOf('/*') # example: /*dynamic*/object if i <> -1 j = name.indexOf('*/', i) name = name[:i] + name[j+2:] # qualified types and generics need cleanup for ch in '.<>, ' name = name.replace(ch, c'_') return name get sharpThis as String """ Returns 'this' as you would expect. Overridden by Extension. """ return 'this' get sharpKeyWord as String is abstract get sharpName as String is override return _name.replace('') sharp = sb.toString else sharp = .sharpName if .parentNameSpace if .parentNameSpace.isRoot sharp = 'global::' + sharp else sharp = .parentNameSpace.sharpQualifier + sharp else if .parentBox sharp = .parentBox.sharpRef + '.' + sharp return sharp def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) if .isExtern, return .compiler.boxStack.push(this) try assert not .isConstructed .writeSharpAttribs(sw) .writeSharpIsNames(sw) sw.write('[.sharpKeyWord] ') .writeSharpDefName(sw) .writeSharpInheritance(sw) sep = '' for inter in _baseInterfaces sw.write(sep) sw.write(inter.sharpRef) sep = ', ' if _genericParams.count sw.indent for param in _genericParams if param inherits GenericParam param.writeSharpConstraint(sw) sw.dedent sw.writeAndIndent('{\n') .writeSharpInvariantMethod(sw) if not .isGenericDef for tm in _testMethods .compiler.boxMemberStack.push(tm) try, tm.writeSharpDef(sw) finally, .compiler.boxMemberStack.pop for decl in _declsInOrder if not .willWriteDeclSharpDef(decl), continue .compiler.boxMemberStack.push(decl) try decl.writeSharpDef(sw) finally .compiler.boxMemberStack.pop if .compiler.includeTests and not .isGenericDef, .writeSharpTest(sw) sw.dedentAndWrite('} // [.sharpKeyWord] [.name]\n') sw.write('\n') finally .compiler.boxStack.pop if .compiler.includeTests and .isGenericDef, .writeSharpTestsForGeneric(sw) def writeSharpTestsForGeneric(sw as CurlyWriter) sw.write('public class [.sharpNameComponent]_Tests_[.serialNum] {\n') sw.write('// tests for: [.name]\n') for tm in _testMethods .compiler.boxMemberStack.push(tm) try, tm.writeSharpDef(sw) finally, .compiler.boxMemberStack.pop .writeSharpTest(sw) for decl in _declsInOrder if not .willWriteDeclSharpDef(decl), continue .compiler.boxMemberStack.push(decl) try # decl.writeSharpDef(sw) (decl to BoxMember).writeSharpTest(sw) finally .compiler.boxMemberStack.pop sw.write('}\n\n') def willWriteDeclSharpDef(decl) as bool return true def writeSharpAttribs(sw as CurlyWriter) for attrib in _attribs attrib.writeSharpDef(sw) def writeSharpDefName(sw as CurlyWriter) sw.write('[.rootName]') if _genericParams.count sw.write('<') sep = '' for param in _genericParams sw.write(sep) param.writeSharpDef(sw) sep = ', ' sw.write('>') def writeSharpInheritance(sw as CurlyWriter) """ Class uses this to write its base class declaration. No box uses this to write interface implementation--that's handled in the Box class. """ pass get sharpInvariantVisibility as String is abstract def writeSharpInvariantMethod(sw as CurlyWriter) if .compiler.options['contracts'] <> 'none' sw.write('\nint _ih_invariantGuard;\n\n') if .compiler.options['contracts'] <> 'methods' return sw.write('\n[.sharpInvariantVisibility] void invariant_[.rootName]() {\n') sw.indent if _baseClass and (not _baseClass.isFromBinaryLibrary or _baseClass.declForName('invariant_[_baseClass.rootName]')) sw.write('invariant_[_baseClass.rootName]();\n') .writeSharpInvariantChecks(sw) sw.dedent sw.write('}\n\n') def writeAllSharpInvariantChecks(sw as CurlyWriter) """ Writes all C# invariant checks including inherited. In support of -contracts:inline """ _writeAllSharpInvariantChecks(sw, this) def _writeAllSharpInvariantChecks(sw as CurlyWriter, box as Box) if box.baseClass and not box.baseClass.isFromBinaryLibrary _writeAllSharpInvariantChecks(sw, box.baseClass to !) box.writeSharpInvariantChecks(sw) def writeSharpInvariantChecks(sw as CurlyWriter) """ Writes the C# invariant checks just for this class. """ for expr in _invariants sharpThis = 'typeof([.sharpRef])' sw.write('if (!') expr.writeSharpDef(sw) sw.write(')\n') sw.indent sw.write('throw new CobraCoreInternal.InvariantException([expr.sharpSourceSite(.name, "invariant", sharpThis)], ') expr.writeSharpBreakdown(sw) sw.write('[sharpThis], null);\n') sw.dedent def writeSharpTest(sw as CurlyWriter) pass def writeSharpTestInvocation(sw as CurlyWriter) is override pass get newForSharpTest as String return '' class ClassOrStruct is partial get sharpRef as String if .isGeneric and .isGenericDef return .qualifiedRootName + '<' + String(c',',.genericParams.count-1) + '>' else return base.sharpRef class Class is partial get sharpInit as String is override return 'null' get sharpInvariantVisibility as String is override return 'protected' get sharpKeyWord as String is override return 'class' def writeSharpInheritance(sw as CurlyWriter) is override sw.write(' : ') if _baseClass sw.write(_baseClass.sharpRef) didWrite = true if didWrite if _baseInterfaces.count sw.write(', ') else sw.write(' ') get newForSharpTest as String is override return if(not _baseClass.isExtern, ' new', '') class Interface is partial get sharpInit as String is override return 'null' get sharpInvariantVisibility as String is override throw ShouldNotCallException(.getType) get sharpKeyWord as String is override return 'interface' def writeSharpInheritance(sw as CurlyWriter) is override if _baseInterfaces.count sw.write(' : ') def writeSharpInvariantMethod(sw as CurlyWriter) is override # Even when invariants are inherited by the classes and structs that implement them, # the code gen for the interface invariant does not go "here", inside the C# interface definition. pass def writeSharpTest(sw as CurlyWriter) is override # Static methods can't be put in interfaces. However, at some point Cobra should support # tests for interfaces which could then be run against the classes that implement them. # Just requires more code gen smarts. pass def writeSharpTestInvocation(sw as CurlyWriter) is override pass class Mixin is partial get sharpName as String name = base.sharpName # to-do: if .canHaveStatements, name += 'Mixin' return name get sharpKeyWord as String is override return if(.canHaveStatements, 'class', 'interface') shared var _nonPublicVisibility = ['protected', 'internal', 'protected internal', 'private'] def willWriteDeclSharpDef(decl) as bool is override # this is for writing the interface if decl inherits BoxVar, return false for viz in _nonPublicVisibility for name in decl.isNames if name == viz, return false return true def writeSharpDef(sw as CurlyWriter) # write the interface _canHaveStatements = false base.writeSharpDef(sw) /# # write the class _sharpRef = nil _canHaveStatements = true base.writeSharpDef(sw) #/ class Struct is partial get sharpInit as String is override # all structs have a valid parameterless constructor return 'new [.sharpRef]()' get sharpInvariantVisibility as String is override return 'private' get sharpKeyWord as String is override return 'struct' def writeSharpInheritance(sw as CurlyWriter) is override if _baseInterfaces.count, sw.write(' : ') def willWriteDeclSharpDef(decl) as bool is override return not (decl inherits Initializer) or (decl to Initializer).hasParams class MethodSig is partial def writeSharpDef(sw as CurlyWriter) is override if .isExtern, return .writeSharpAttribs(sw) .writeSharpIsNames(sw) sw.write('delegate [.resultType.sharpRef] [.rootName]') if _genericParams.count sw.write('<') sep = '' for genericParam in _genericParams sw.write(sep) genericParam.writeSharpDef(sw) sep = ', ' sw.write('>') sw.write('(') sep = '' for param in .params sw.write(sep) param.writeSharpDef(sw) sep = ', ' sw.write(');\n') def writeSharpTest(sw as CurlyWriter) is override pass def writeSharpTestInvocation(sw as CurlyWriter) is override pass class GenericParam is partial get sharpInit as String is override return 'default([_name])' def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) sw.write(.name) def writeSharpConstraint(sw as CurlyWriter) if _constraints.count sw.write(' where [_name] : ') sep = '' for constraint in _constraints sw.write(sep) constraint.writeSharpDef(sw) sep = ', ' sw.write('\n') class GenericClassConstraint is partial def writeSharpDef(sw as CurlyWriter) is override sw.write('class') class GenericStructConstraint is partial def writeSharpDef(sw as CurlyWriter) is override sw.write('struct') class GenericCallableConstraint is partial def writeSharpDef(sw as CurlyWriter) is override sw.write('new()') class GenericTypeConstraint is partial def writeSharpDef(sw as CurlyWriter) is override sw.write(_representedType.sharpRef) class Extension is partial def _computeSharpRef as String is override sharp = .sharpName if .parentNameSpace and not .parentNameSpace.isRoot sharp = .parentNameSpace.sharpQualifier + sharp else if .parentBox sharp = .parentBox.sharpRef + '.' + sharp return sharp get sharpInit as String is override return _extendedBox.sharpInit get sharpInvariantVisibility as String is override return _extendedBox.sharpInvariantVisibility get sharpKeyWord as String is override return 'class' # because the extension is always housed in a C# class var _backEndName as String? get sharpName as String is override if not _backEndName _backEndName = if(.nativeType, .nativeType.name, 'Extend_[.extendedBox.sharpNameComponent]_[Utils.toIdentifier(Path.getFileNameWithoutExtension(.compiler.curModule.fileName) to !)]') return _backEndName to ! get sharpThis as String is override return '_lh_this' def extendedMethodGenericParams(genericParams as IList) as IList is override if .isGeneric # the generated static C# method will need the generic params of the extension on it # since C# itself doesn't have "extension types", only extension methods r = List(.genericParams) r.addRange(genericParams) return r else return base.extendedMethodGenericParams(genericParams) def writeSharpDefName(sw as CurlyWriter) is override # replace base behavior sw.write(.sharpName) def writeSharpDef(sw as CurlyWriter) is override for decl in .declsInOrder if decl inherits BoxMember decl.sharpExtraIsNames = {'static'} if decl inherits AbstractMethod decl.params.insert(0, Param('_lh_this', .extendedBox, isImplicit=true).bindAll to Param) base.writeSharpDef(sw) ## ## Vars ## interface IVar is partial get sharpAssignmentNames as List? """ Return the C# names to assign to above and beyond the sharpName. This in support of if-inherits. """ class AbstractLocalVar is partial get sharpAssignmentNames as List return [_backEndName] # return List(_useBackEndNameStack) get sharpName as String is override if _ifInheritsStack.count if _type.isReference return '(([_ifInheritsStack.peek.sharpRef])[_backEndName])' else # could it be a subclass? no. value types are structs and cannot be subclassed so this must be `if i` where `i` is nullable struct return '[.name].Value' # Nullable.Value else return _backEndName # return _useBackEndNameStack.peek to ! class Param is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) if .type.isReference and not .type inherits NilableType sw.write(r'[CobraCoreInternal.NotNull] ') for attr in .attributes, attr.writeSharpDef(sw, '', false) branch .direction on Direction.In, pass on Direction.InOut, sw.write('ref ') on Direction.Out, sw.write('out ') sw.write('[.type.sharpParamRef] [.sharpName]') if .optionalValue # optional param sw.write('=') .optionalValue.writeSharpDef(sw) def writeSharpDefSansReferenceLabels(sw as CurlyWriter) """ Writes the parameter declaration but without C# "out" or "ref"--these are not available for contract methods (require_foo and ensure_foo) because contracts cannot modify arguments. """ # skip the [NotNull] as well since it's not really needed sw.write('[.type.sharpRef] [.sharpName]') class LocalVar is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) sw.write('[.type.sharpRef] [.sharpName]') init = .type.sharpInit if init.length, sw.write(' = [init]') sw.write(';\n') class ResultVar is partial get sharpName as String is override return _backEndName ## ## Members ## class BoxMember is partial var _sharpExtraIsNames as Set? pro sharpExtraIsNames from var get sharpThis as String # sigh. I wish .NET had metaclasses instead of this instance vs. static crap return if(.isShared, 'typeof([.parentBox.sharpName])', .parentBox.sharpThis) def writeSharpNotNull(sw as CurlyWriter) if .resultType.isReference and not .resultType inherits NilableType sw.writeLine(r'[' + .sharpNotNullPrefix + 'CobraCoreInternal.NotNull]') get sharpNotNullPrefix as String # 2007-12-22 Writing the attribute like [return: NotNull] is problematic because MemberInfo.getCustomAttributes() # will not see it. At least not on Novell Mono 1.2.4. I don't know if that is a bug or a "feature". #return 'return: ' return '' def includeSharpTestDirectly as bool # cannot put static/shared test methods in a generic def since they cannot be invoked # (without computing some type args which could end up being non-trivial) return .compiler.includeTests and not .parentBox.isGenericDef def writeSharpTest(sw as CurlyWriter) if _testMethods and _testMethods.count for tm in _testMethods, tm.writeSharpDef(sw) def writeSharpTestInvocation(sw as CurlyWriter) if _testMethods and _testMethods.count for tm in _testMethods, sw.write('[tm.sharpName]();\n') def writeSharpParams(sw as CurlyWriter) .writeSharpParams(sw, '()') def writeSharpParams(sw as CurlyWriter, parens as String) require parens.length==2 or parens=='' if parens.length sw.write(parens[0]) sep = '' for param in .params sw.write(sep) param.writeSharpDef(sw) sep = ', ' if parens.length sw.write(parens[1].toString+' ') def writeSharpIsNames(sw as CurlyWriter) isNameCS = { # only have to specify the ones that are different 'shared': 'static', 'nonvirtual': '', 'readonly': 'readonly', 'synchronized': '', } if 'synchronized' in _isNames sw.write('\[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]\n') wroteNames = Set() sep = '' for name in _isNames name = isNameCS.get(name, name) sw.write(sep) sw.write(name) sep = ' ' wroteNames.add(name) if _sharpExtraIsNames for name in _sharpExtraIsNames if name not in wroteNames sw.write(sep) sw.write(name) sep = ' ' if sep.length, sw.write(' ') def writeSharpAttribs(sw as CurlyWriter) for attrib in _attribs attrib.writeSharpDef(sw) class BoxEvent is partial get sharpName as String is override return .name.capitalized def writeSharpDef(sw as CurlyWriter) is override base.writeSharpDef(sw) .writeSharpAttribs(sw) .writeSharpIsNames(sw) sw.write('event ') sw.write(.handlerType.sharpRef) sw.write(' [.sharpName]') sw.write(';\n') class BoxField is partial get _sharpBackEndName as String name = .name if not name.startsWith('_') name = name.capitalized return name class BoxConst is partial def writeSharpDef(sw as CurlyWriter) is override base.writeSharpDef(sw) .writeSharpAttribs(sw) .writeSharpIsNames(sw) sw.write('const ') sw.write(_type.sharpRef) sw.write(' [_sharpBackEndName] = ') _initExpr.writeSharpDef(sw) sw.write(';\n') class BoxVar is partial get sharpAssignmentNames as List return [_sharpBackEndName] # return List(_useBackEndNameStack) get sharpName as String is override if _ifInheritsStack.count if _type.isReference return '(([_ifInheritsStack.peek.sharpRef])[_sharpBackEndName])' else # could it be a subclass? no. value types are structs and cannot be subclassed so this must be `if i` where `i` is nullable struct return '[_sharpBackEndName].Value' # Nullable.Value else backEndName = .name if not backEndName.startsWith('_') backEndName = backEndName.capitalized return backEndName #return _backEndName # return _useBackEndNameStack.peek to ! def writeSharpDef(sw as CurlyWriter) is override base.writeSharpDef(sw) .writeSharpAttribs(sw) .writeSharpIsNames(sw) sw.write(_type.sharpRef) sw.write(' [_sharpBackEndName]') if _initExpr sw.write(' = ') _initExpr.writeSharpDef(sw) else if _canInitDefault sharpInit = .type.sharpInit if sharpInit.length sw.write(' = ') sw.write(sharpInit) sw.write(';\n') def _canInitDefault as bool # In C#, struct members can't be explicitly init'ed on declaration if .parentBox inherits Struct, return false return .type.isReference and not .type.nonNil inherits GenericParam class AbstractMethod is partial get sharpGenericParams as String """ Can return, for example, '' or ''. """ ensure ' ' not in result return '' def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) .compiler.codeMemberStack.push(this) try .innerWriteSharpDef(sw) finally .compiler.codeMemberStack.pop def innerWriteSharpDef(sw as CurlyWriter) pass def writeSharpImp(sw as CurlyWriter, skipFirst as bool) .compiler.codeMemberStack.push(this) try sw.writeAndIndent('{\n') .writeSharpImpHeader(sw) if .compiler.hasDetailedStackTraceOption, .writeDSTHead(sw) for param in _params, .writeSharpParamCheck(param, sw) .writeSharpLocals(sw, nil, false) if _requirePart and _requirePart.willGenerateCode, _requirePart.writeSharpDef(sw) willEnsure = _ensurePart and _ensurePart.willGenerateCode if willEnsure sw.write('bool _lh_canEnsure = false;\n') # have to set `out` parameters or C# will error on them being passed in the `finally` for the ensure, # even though the _lh_canEnsure guard logically prevents problems at run-time for param in _params if param.isOut sharpInit = param.type.sharpInit if not sharpInit.length # TODO/HACK: yes we need something like this (or a way to convice C# that the out param reference in `finally` really is okay), # but it should be something less hackish, like an attribute sharpInit = 'DateTime.Today' sw.write('[param.sharpName] = [sharpInit];') if _backEndResultVarName.length # set in _bindImp sw.write('[_returnType.sharpRef] [_backEndResultVarName]') if _returnType.sharpInit.length sw.write('= [_returnType.sharpInit]') sw.write(';\n') sw.writeAndIndent('try {\n') for stmt in _stmts if skipFirst skipFirst = false continue stmt.writeSharpStmt(sw) if willEnsure if not stmt inherits ReturnStmt and not stmt inherits ThrowStmt sw.write('_lh_canEnsure = true;\n') sw.dedentAndWrite('} finally { // ensure\n') sw.indentAndWrite('if (_lh_canEnsure) {\n') sw.indent _ensurePart.writeSharpDef(sw) sw.dedentAndWrite('}\n') sw.dedentAndWrite('}\n') if .compiler.hasDetailedStackTraceOption .writeDSTTail(sw) .writeSharpImpFooter(sw) sw.dedent sw.write('}\n') sw.write('\n') finally .compiler.codeMemberStack.pop def writeSharpImpHeader(sw as CurlyWriter) pass def writeSharpImpFooter(sw as CurlyWriter) pass def writeSharpPassArgs(sw as CurlyWriter) .writeSharpPassArgs(sw, '()', false) def writeSharpPassArgs(sw as CurlyWriter, parens as String, excludeOutArgs as bool) require parens.length==2 or parens=='' if parens.length, sw.write(parens[0]) sep = '' for param in _params if excludeOutArgs and param.isOut, continue sw.write(sep) sw.write(param.sharpName) sep = ', ' if parens.length, sw.write(parens[1]) def writeSharpLocals(sw as CurlyWriter, locals as List?, open as bool) if open, sw.writeAndIndent('{\n') # record the `old` expressions for `ensure` .writeSharpOldAssignments(sw) if locals is nil, locals = _locals contractsAreMethods = .compiler.options['contracts'] == 'methods' if locals.count sw.hideLineNumbers sw.write('// locals\n') try for local in locals if not local.isImplicit and not (local.isForContract and contractsAreMethods) local.writeSharpDef(sw) finally sw.unhideLineNumbers def writeSharpRequireParamDecls(sw as CurlyWriter) sep = '' for param in .params if param.isOut # a requirement cannot say anything useful about a parameter that is output only continue sw.write(sep) param.writeSharpDefSansReferenceLabels(sw) # no `inout/ref` label since a contract cannot modify reference arguments sep = ', ' def writeSharpOldAssignments(sw as CurlyWriter) if .isOverride and .matchingBaseMember (.matchingBaseMember to AbstractMethod).writeSharpOldAssignments(sw) for oldExpr in _oldExprs oldExpr.writeSharpAssignment(sw) def writeSharpEnsureArgs(sw as CurlyWriter) if .backEndResultVarName.length sw.write('[.backEndResultVarName]') sep = [','] else sep = [''] _writeSharpOldArgs(sw, sep) if .params.count sw.write(sep[0]) .writeSharpPassArgs(sw, '', false) # these have to come last, because they might include a C# "params" which has to be last def _writeSharpOldArgs(sw as CurlyWriter, sep as List) if .isOverride and .matchingBaseMember assert .matchingBaseMember is not this (.matchingBaseMember to AbstractMethod)._writeSharpOldArgs(sw, sep) for oldExpr in _oldExprs sw.write(sep[0]) sw.write(oldExpr.sharpVarName) sep[0] = ', ' def writeSharpEnsureParamDecls(sw as CurlyWriter) if .backEndResultVarName.length sw.write('[.resultType.sharpRef] [.backEndResultVarName]') sep = [','] else sep = [''] _writeSharpOldParamDecls(sw, sep) # these have to come last, because they might include a C# "params" which has to be last for param in .params sw.write(sep[0]) param.writeSharpDefSansReferenceLabels(sw) # no `out` or `inout/ref` labels since a contract cannot modify reference arguments sep[0] = ', ' def _writeSharpOldParamDecls(sw as CurlyWriter, sep as List) if .isOverride and .matchingBaseMember assert .matchingBaseMember is not this (.matchingBaseMember to AbstractMethod)._writeSharpOldParamDecls(sw, sep) for oldExpr in _oldExprs sw.write(sep[0]) sw.write('[oldExpr.type.sharpRef] [oldExpr.sharpVarName]') sep[0] = ', ' def writeSharpParamCheck(param as Param, sw as CurlyWriter) if .isCompilerGenerated, return if param.type.isReference and not param.type inherits NilableType and not param.isOut if .compiler.options.boolValue('include-nil-checks') sw.write('if (CobraCoreInternal.CobraCore._willCheckNil && [param.sharpName]==null) throw new System.ArgumentNullException("[param.name]");\n') def writeDSTHead(sw as CurlyWriter) if not .canHaveDetailedStackTrace, return # could be a neat option, -trace-methods, but maybe done with events: # sw.writeLine('Console.WriteLine(">> [.parentBox.name].[.name]");') sw.write('CobraCoreInternal.CobraImp.PushFrame("[.parentBox.name]", "[.name]", [Utils.sharpStringLiteralFor(.token.fullPathName)], [.token.lineNum]') if not .isShared sw.write(', "this", [.compiler.curBox.sharpThis]') for param in .params if not param.isImplicit and not param.isOut sw.write(', "[param.name]", [param.sharpName]') sw.write(');\n') sw.writeAndIndent('try {\n') def writeDSTTail(sw as CurlyWriter) if not .canHaveDetailedStackTrace, return # sw.writeLine('Console.WriteLine("<< [.parentBox.name].[.name]");') # this catch all clashes with yield statements. which is why .canHaveDetailedStackTrace returns false if a method has a yield statement. sw.dedentAndWrite('} catch {\n') sw.indent sw.write('CobraCoreInternal.CobraImp.CaughtUncaughtException();\n') sw.write('throw;\n') sw.dedentAndWrite('} finally {\n') sw.indent sw.write('CobraCoreInternal.CobraImp.PopFrame();\n') sw.dedentAndWrite('}\n') class Initializer is partial def innerWriteSharpDef(sw as CurlyWriter) base.innerWriteSharpDef(sw) .writeSharpAttribs(sw) first = if(_stmts.count, _stmts[0], nil) callInitializer as String? = nil if first inherits DotExpr if first.left inherits ThisOrBaseLit if (firstRight = first.right) inherits IDotRightExpr if firstRight.name == 'init' callInitializer = (first.left to ThisOrBaseLit).asSharp args = firstRight.args Stmt.inInitCall = true didSetInInitCall = true _fixIsNames .writeSharpIsNames(sw) sw.write(' [.parentBox.rootName]') .writeSharpParams(sw) if callInitializer sw.writeAndIndent('\n') sw.write(': [callInitializer](') sep = '' for arg in args sw.write(sep) arg.writeSharpDef(sw) sep = ', ' sw.write(') ') sw.dedent if didSetInInitCall assert Stmt.inInitCall # make sure it wasn't reset somewhere else Stmt.inInitCall = false .writeSharpImp(sw, if(callInitializer, true, false)) if _requirePart _requirePart.writeSharpMethod(sw) if _ensurePart _ensurePart.writeSharpMethod(sw) if .includeSharpTestDirectly, .writeSharpTest(sw) def _fixIsNames # suppress adding default access modifiers on static initializer # any access modifier in static ctor is error but that should have been caught in bind* methods # we'll just cater to the default 'public' if 'shared' in _isNames and _isNames.count > 1 _isNames.remove('public') def writeSharpImpHeader(sw as CurlyWriter) base.writeSharpImpHeader(sw) if .isStructMember and .compiler.options['contracts'] <> 'none' sw.write('\n_ih_invariantGuard = 0;\n\n') # otherwise C# complains: Field "STRUCTNAME._ih_invariantGuard" must be fully assigned before control leaves the constructor class Method is partial get sharpGenericParams as String is override params = .parentBox.extendedMethodGenericParams(_genericParams) if params.count sb = StringBuilder('<') sep = '' for param in params sb.append(sep) sb.append(param.sharpName) sep = ',' sb.append('>') return sb.toString else return '' def innerWriteSharpDef(sw as CurlyWriter) base.innerWriteSharpDef(sw) .writeSharpNotNull(sw) .writeSharpAttribs(sw) name = .sharpName returnType = _returnType ? .compiler.voidType if _implementsType name = _implementsType.sharpRef + '.' + name .writeSharpIsNames(sw) sw.write('[returnType.sharpRef] [name][.sharpGenericParams]') .writeSharpParams(sw) if .genericParams.count sw.indent for param in .genericParams if param inherits GenericParam param.writeSharpConstraint(sw) sw.dedent if not .parentBox.canHaveStatements sw.write(';\n') else if .bodyExclusion, sw.writeLine(';') else, .writeSharpImp(sw, false) if _requirePart, _requirePart.writeSharpMethod(sw) if _ensurePart, _ensurePart.writeSharpMethod(sw) if .includeSharpTestDirectly, .writeSharpTest(sw) def writeSharpImpHeader(sw as CurlyWriter) base.writeSharpImpHeader(sw) if .isMain if .compiler.hasExceptionReportOption sw.writeLine('try { // Exception Report') sw.indent else if .compiler.options.boolValue('debugging-tips') sw.writeLine('try { // -debugging-tips') sw.indent if .isMain and .compiler.options.boolValue('include-tests') (.compiler to Compiler).writeSharpRunAllTests(sw) def writeSharpImpFooter(sw as CurlyWriter) base.writeSharpImpFooter(sw) if .isMain if .compiler.hasExceptionReportOption sw.dedent sw.writeLine('} catch (System.Exception _lh_exceptionReportException) { CobraCoreInternal.CobraCore.HandleUnhandledException(_lh_exceptionReportException); }') else if .compiler.options.boolValue('debugging-tips') sw.dedent sw.writeLine('} catch { CobraCoreInternal.CobraCore.PrintDebuggingTips(); throw; }') get sharpName as String is override return .name.capitalized class ProperDexer is partial def writeSharpBody(sw as CurlyWriter) sw.write(' {\n') sw.indent if _getPart if .isAbstract or not .parentBox.canHaveStatements sw.write('\tget;\n') else _getPart.writeSharpDef(sw) if _setPart if .isAbstract or not .parentBox.canHaveStatements sw.write('\tset;\n') else _setPart.writeSharpDef(sw) sw.dedent sw.write('}\n') class ProperDexerXetter is partial def innerWriteSharpDef(sw as CurlyWriter) base.innerWriteSharpDef(sw) sw.write(.xetPartName) .writeSharpImp(sw, false) class Property is partial get sharpNotNullPrefix as String is override return '' def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) sw.writeLine('') .writeSharpNotNull(sw) .writeSharpAttribs(sw) .writeSharpIsNames(sw) sw.write(' [_returnType.sharpRef] [.name.capitalized]') .writeSharpBody(sw) getPart = .getPart if getPart .compiler.codeMemberStack.push(getPart) try if getPart.requirePart getPart.requirePart.writeSharpMethod(sw) if getPart.ensurePart getPart.ensurePart.writeSharpMethod(sw) finally .compiler.codeMemberStack.pop setPart = .setPart if setPart .compiler.codeMemberStack.push(setPart) try if setPart.requirePart setPart.requirePart.writeSharpMethod(sw) if setPart.ensurePart setPart.ensurePart.writeSharpMethod(sw) finally .compiler.codeMemberStack.pop if .includeSharpTestDirectly, .writeSharpTest(sw) class Indexer is partial get sharpNotNullPrefix as String is override return '' def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) assert _name==r'[]' sw.writeLine('') .writeSharpNotNull(sw) .writeSharpAttribs(sw) .writeSharpIsNames(sw) sw.write(' [_returnType.sharpRef] this') .writeSharpParams(sw, r'[]') .writeSharpBody(sw) getPart = .getPart if getPart .compiler.codeMemberStack.push(getPart) try if getPart.requirePart getPart.requirePart.writeSharpMethod(sw) if getPart.ensurePart getPart.ensurePart.writeSharpMethod(sw) finally .compiler.codeMemberStack.pop setPart = .setPart if setPart .compiler.codeMemberStack.push(setPart) try if setPart.requirePart setPart.requirePart.writeSharpMethod(sw) if setPart.ensurePart setPart.ensurePart.writeSharpMethod(sw) finally .compiler.codeMemberStack.pop if .includeSharpTestDirectly, .writeSharpTest(sw) class MemberOverload is partial pass class TestMethod is partial get sharpName as String is override if _forMember name = 'test_' + _forMember.name.capitalized.replace('Cue.', '_cue_') if .overloadId <> -1 name += '_ol_[.overloadId]' else name = 'test_[_forBox.sharpKeyWord]_'+_forBox.sharpNameComponent return '[name]_[.serialNum]' # embed serial number because there can be multiple test sections class ContractPart is partial var _sharpMethodNameCache as String? def writeSharpMethod(sw as CurlyWriter) is abstract def _sharpMethodName(prefix as String) as String if _sharpMethodNameCache is nil # TODO: having the serial number in the name avoids problem with overloads, but it's ugly and it can change from run-to-run. # would be nice if each member of an overload had an overload number. name = '[prefix]_[_codeMember.sharpName][_codeMember.sharpGenericParams]_[_codeMember.serialNum]_[_codeMember.parentBox.name]' name = name.replace(r'[]', 'Item') # indexer # TODO: maybe Indexer should return 'Item' in the first place # properindexers like "name.get" have a '.' # generics have < > and , for ch in [c'.', c'<', c'>', c','] name = name.replace(ch, c'_') _sharpMethodNameCache = name return _sharpMethodNameCache to ! get sharpThis as String return _codeMember.sharpThis class RequirePart is partial get sharpMethodName as String return _sharpMethodName('require') def writeSharpDef(sw as CurlyWriter) is override base.writeSharpDef(sw) assert .willGenerateCode willInline = .willInlineCode sw.write('if (CobraCoreInternal.CobraCore._willCheckRequire) {\n') sw.indent if _codeMember.isOverride # Note that in Cobra, when the top level class does not define `require` then that is the # same as saying `require true`--there is no requirement for calling the method. But when # an overriding method does not define `require` then it is inheriting the `require` of # its matching base method. curCodeMember = _codeMember to ? count = 0 n = 1 post while curCodeMember if not curCodeMember.parentBox.isExtern and curCodeMember.requirePart and (not curCodeMember.requirePart.isImplicit or curCodeMember.matchingBaseMember is nil) count += 1 sw.write('try {\n') sw.indent if willInline if curCodeMember.requirePart.exprs.count willEncapsulate = curCodeMember is not _codeMember if willEncapsulate # parameter names can be different sw.write('{\n') sw.indent for i = 0 .. _codeMember.params.count ourParam = _codeMember.params[i] ancestorParam = curCodeMember.params[i] if ourParam.name <> ancestorParam.name # this is the problem`; name mismatch sw.write('[ourParam.type.sharpRef] [ancestorParam.name] = [ourParam.name];\n') curCodeMember.requirePart.writeSharpChecks(sw) if willEncapsulate sw.dedent sw.write('}\n') else sw.write('[curCodeMember.requirePart.sharpMethodName]') _codeMember.writeSharpPassArgs(sw, '()', true) # always use *our* code member since the arg names can be different than base classes sw.write(';\n') sw.dedent sw.write('} catch (CobraCoreInternal.RequireException re[n]) {\n') sw.indent if n>1 sw.write('re[n-1].Next = re[n];\n') n += 1 curCodeMember = curCodeMember.matchingBaseMember to AbstractMethod? # at this point in C# code, all requirements have failed if n > 1 sw.write('throw re1;\n') for m = 1 .. n sw.dedent sw.write('}\n') count -= 1 else if willInline .writeSharpChecks(sw) else sw.write('[.sharpMethodName]') _codeMember.writeSharpPassArgs(sw, '()', true) sw.write(';\n') sw.dedent sw.write('}\n') def writeSharpMethod(sw as CurlyWriter) is override if .compiler.options['contracts'] <> 'methods' return if _codeMember.isInterfaceMember # contracts on interface members are not supported yet # and when they are, these methods will have to be written outside the interface return if not .willGenerateCode return static = if(_codeMember.isShared, 'static ', '') access = if(_codeMember.parentBox inherits Struct, 'private', 'protected') sw.write('[static][access] void [.sharpMethodName][_codeMember.sharpGenericParams](') _codeMember.writeSharpRequireParamDecls(sw) sw.write(') {\n') sw.indent for local in .codeMember.locals, if local.isForRequire, local.writeSharpDef(sw) .writeSharpChecks(sw) sw.dedent sw.write('}\n\n') def writeSharpChecks(sw as CurlyWriter) for expr in _exprs sw.write('if (!') expr.writeSharpDef(sw) sw.write(') ') sw.indent sw.write('throw new CobraCoreInternal.RequireException([expr.sharpSourceSite], ') expr.writeSharpBreakdown(sw) sw.write('[.sharpThis], null);\n') sw.dedent class EnsurePart is partial get sharpMethodName as String return _sharpMethodName('ensure') def writeSharpDef(sw as CurlyWriter) is override base.writeSharpDef(sw) assert .willGenerateCode willInline = .willInlineCode sw.write('if (CobraCoreInternal.CobraCore._willCheckEnsure) {\n') sw.indent if _codeMember.isOverride curCodeMember = _codeMember to ? post while curCodeMember if not curCodeMember.parentBox.isExtern and curCodeMember.ensurePart if willInline curCodeMember.ensurePart.writeSharpChecks(sw) else sw.write('[curCodeMember.ensurePart.sharpMethodName](') _codeMember.writeSharpEnsureArgs(sw) sw.write(');\n') curCodeMember = curCodeMember.matchingBaseMember to AbstractMethod? # TODO: should a cast really be needed here? # at this point in C# code, all ensure have passed else if willInline .writeSharpChecks(sw) else sw.write('[_codeMember.ensurePart.sharpMethodName](') _codeMember.writeSharpEnsureArgs(sw) sw.write(');\n') sw.dedent sw.write('}\n') def writeSharpMethod(sw as CurlyWriter) is override if .compiler.options['contracts'] <> 'methods' return if _codeMember.isInterfaceMember # contracts on interface members are not supported yet # and when they are, these methods will have to be written outside the interface return if not .willGenerateCode return static = if(_codeMember.isShared, 'static ', '') access = if(_codeMember.parentBox inherits Struct, 'private', 'protected') sw.write('[static][access] void [.sharpMethodName][_codeMember.sharpGenericParams](') _codeMember.writeSharpEnsureParamDecls(sw) sw.write(') {\n') sw.indent for local in .codeMember.locals, if local.isForEnsure, local.writeSharpDef(sw) .writeSharpChecks(sw) sw.dedent sw.write('}\n\n') def writeSharpChecks(sw as CurlyWriter) for expr in _exprs sw.write('if (!') expr.writeSharpDef(sw) sw.write(') ') sw.indent sw.write('throw new CobraCoreInternal.EnsureException([expr.sharpSourceSite], ') expr.writeSharpBreakdown(sw) sw.write('[.sharpThis], null);\n') sw.dedent if not _codeMember.isShared and not _codeMember.isExtensionMember sw.write('if (CobraCoreInternal.CobraCore._willCheckInvariant && _ih_invariantGuard == 0) {\n') sw.indent sw.write('_ih_invariantGuard += 1;\n') sw.write('try {\n') sw.indent if .willInlineCode .codeMember.parentBox.writeAllSharpInvariantChecks(sw) else sw.write('invariant_[_codeMember.parentBox.rootName]();') sw.dedent sw.write('} finally { _ih_invariantGuard -= 1; }\n') sw.dedent sw.write('} // invariant\n') ## ## Statements ## class Stmt is partial def sharpSourceSite as String """ Returns a C# "new SourceSite(...)" expression with the correct arguments for the current code generation. """ if .compiler.codeMemberStack.count codeMember = .compiler.curCodeMember return .sharpSourceSite(codeMember.parentBox.name, codeMember.name, .sharpThis) else # example: var _x = someExpr ... return .sharpSourceSite(.compiler.curBox.name, .compiler.curBoxMember.name, .sharpThis) def sharpSourceSite(boxName as String, memberName as String, sharpThis as String) as String """ Returns a C# "new SourceSite(...)" expression with the correct arguments for the current code generation. The arguments to this method allow for some explicit control in special circumstances, such as `invariant`. """ require boxName.length memberName.length sharpThis.length body return 'new CobraCoreInternal.SourceSite([.sharpSourceSiteArgs(boxName, memberName, sharpThis)])' def sharpSourceSiteArgs as String return .sharpSourceSiteArgs(.compiler.curBox.name, .compiler.curBoxMember.name, .sharpThis) def sharpSourceSiteArgs(boxName as String, memberName as String, sharpThis as String) as String require boxName.length memberName.length sharpThis.length body boxName = Utils.sharpStringLiteralFor(boxName) memberName = Utils.sharpStringLiteralFor(memberName) tok = .token fileName = tok.fileName if not Path.isPathRooted(fileName) fileName = Path.combine(Environment.currentDirectory, fileName) fileName = Utils.sharpStringLiteralFor(fileName) return '[fileName], [tok.lineNum], [boxName], [memberName], [sharpThis]' get sharpThis as String if _inInitCall # C# won't allow 'this' in a base constructor call return '"(uninitialized [.compiler.curBox.name] instance)"' else if .compiler.codeMemberStack.count return .compiler.curCodeMember.sharpThis else assert .compiler.boxMemberStack.count # so not a code member return '"(uninitialized [.compiler.curBox.name] instance)"' def writeSharpSetLine(sw as CurlyWriter) if _canSetLine and .token.lineNum > 0 sw.write('CobraCoreInternal.CobraImp._curFrame._lineNum = [.token.lineNum];\n') def writeSharpStmt(sw as CurlyWriter) require .didBindImp sw.node(this) .writeSharpSetLine(sw) .writeSharpDef(sw) class AssertStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) if not .compiler.options.boolValue('include-asserts') return sw.write('if (CobraCoreInternal.CobraCore._willCheckAssert && !') _expr.writeSharpDef(sw) sw.write(') \n') # \n for improved readability of the generated code sw.indent sw.write('throw new CobraCoreInternal.AssertException([.sharpSourceSite], ') _expr.writeSharpBreakdown(sw) sw.write('[.sharpThis], ') if _info _info.writeSharpDef(sw) else sw.write('null') sw.write(');\n') sw.dedent class BranchStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) if _expr.type.isDynamic # if the expression is dynamic, then use an if-else ladder with CobraImp.Equals( sw.writeLine('// branch [_expr.toCobraSource]') varName = '_lh_branch_[.serialNum]' sw.write('[_expr.type.sharpRef] [varName] = ') _expr.writeSharpDef(sw) sw.writeLine(';') elseWord = '' for onPart in _onParts sw.write('[elseWord]if (') sep = '' for e in onPart.exprs sw.write(sep) sw.write('CobraCoreInternal.CobraImp.Equals([varName], ') e.writeSharpDef(sw, false) sw.write(')') sep = ' || ' sw.write(')') onPart.block.writeSharpDef(sw) elseWord = 'else ' if _elsePart sw.write('else ') _elsePart.writeSharpDef(sw) else # for any other kind of expression, use a C# switch sw.write('switch(') _expr.writeSharpDef(sw) sw.write(') {\n') sw.indent for onPart in _onParts sep = '' for e in onPart.exprs sw.write(sep) sw.write('case ') e.writeSharpDef(sw) sw.write(': ') sep = '\n' onPart.block.writeSharpDef(sw) if not onPart.block.stmts.count or not onPart.block.stmts[onPart.block.stmts.count-1] inherits ReturnStmt # CC: use .last sw.write('break;\n') if _elsePart sw.write('default: ') _elsePart.writeSharpDef(sw) sw.write('break;\n') sw.dedent sw.write('}\n') class BlockStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) .writeSharpDef(sw, true, nil) def writeSharpDef(sw as CurlyWriter, close as bool) .writeSharpDef(sw, close, nil) def writeSharpDef(sw as CurlyWriter, top as String?) .writeSharpDef(sw, true, top) def writeSharpDef(sw as CurlyWriter, close as bool, top as String?) sw.node(this) sw.write(' {\n') sw.indent if top sw.write(top) if _ifInheritsVar #typeName = _ifInheritsType.sharpRef #newName = '_lh_' + _ifInheritsVar.name + '_' + _ifInheritsType.sharpNameComponent #sw.write('[typeName] [newName] = ([typeName])[_ifInheritsVar.sharpName];\n') #_ifInheritsVar.useBackEndNameStack.push(newName) ifInheritsCount = _ifInheritsVar.ifInheritsStack.count # AssignExpr could pop our _ifInheritsType. We detect that with the count. _ifInheritsVar.ifInheritsStack.push(_ifInheritsType) for stmt in _stmts stmt.writeSharpStmt(sw) if _ifInheritsVar and _ifInheritsVar.ifInheritsStack.count > ifInheritsCount #_ifInheritsVar.useBackEndNameStack.pop _ifInheritsVar.ifInheritsStack.pop if close sw.dedent sw.write('}\n') class BreakStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) sw.write('break;\n') class ContinueStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) sw.write('continue;\n') class ExpectStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) exceptionTypeRef = _exceptionType.sharpRef gotRightExceptionVarName = '_lh_expect_[_varNumber-1]' # lh => "local helper" sw.write('bool [gotRightExceptionVarName] = false;\n') sw.write('try ') _block.writeSharpDef(sw) sw.writeAndIndent('catch ([exceptionTypeRef]) {\n') sw.write('// exactly what is expected\n') sw.write('[gotRightExceptionVarName] = true;\n') sw.dedentAndWrite('}\n') if _exceptionType inherits Box and not (_exceptionType to Box).isClrSystemExceptionClass wrongExceptionVarName = '_lh_expect_[_varNumber]' assert gotRightExceptionVarName <> wrongExceptionVarName sw.writeAndIndent('catch (System.Exception [wrongExceptionVarName]) {\n') sw.write('throw new CobraCoreInternal.ExpectException(typeof([exceptionTypeRef]), [wrongExceptionVarName]);\n') sw.dedentAndWrite('}\n') sw.write('if (![gotRightExceptionVarName]) throw new CobraCoreInternal.ExpectException(typeof([exceptionTypeRef]), null);\n') class ForStmt is partial pass class OldForNumericStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) csVar = _var.sharpName trackLocals = .compiler.willTrackLocals if trackLocals sw.write('for (CobraCoreInternal.CobraImp.SetLocal("[.var.name]", [csVar]=') else sw.write('for ([csVar]=') _start.writeSharpDef(sw) if trackLocals sw.write(')') if _var.type.isDynamic sw.write('; CobraCoreInternal.CobraImp.DynamicCompare([csVar], ') _stop.writeSharpDef(sw, false) sw.write(')[if(_dir==1, "<", ">")]0') opName = if(_dir==1, 'op_Addition', 'op_Subtraction') sw.write('; [csVar]=CobraCoreInternal.CobraImp.DynamicOp("[opName]", [csVar], ') if _step _step.writeSharpDef(sw, false) else sw.write('1') sw.write(')') else sw.write('; [csVar]') sw.write(if(_dir==1, '<', '>')) _stop.writeSharpDef(sw) sw.write('; ') if _step if _dir==1 sw.write('[csVar]+=') else sw.write('[csVar]-=') _step.writeSharpDef(sw) else if _dir==1 sw.write('[csVar]++') else sw.write('[csVar]--') sw.write(')') _block.writeSharpDef(sw) class ForNumericStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) if _var.type.isDynamic _writeSharpDefForDynamic(sw) else _writeSharpDefForStatic(sw) def _writeSharpDefForDynamic(sw as CurlyWriter) # TODO: this is the old numeric for loop behavior. csVar = _var.sharpName trackLocals = .compiler.willTrackLocals if trackLocals sw.write('for (CobraCoreInternal.CobraImp.SetLocal("[.var.name]", [csVar]=') else sw.write('for ([csVar]=') _start.writeSharpDef(sw) if trackLocals sw.write(')') sw.write('; CobraCoreInternal.CobraImp.DynamicCompare([csVar], ') _stop.writeSharpDef(sw, false) sw.write(')[if(_dir==1, "<", ">")]0') opName = if(_dir==1, 'op_Addition', 'op_Subtraction') sw.write('; [csVar]=CobraCoreInternal.CobraImp.DynamicOp("[opName]", [csVar], ') if _step _step.writeSharpDef(sw, false) else sw.write('1') sw.write(')') sw.write(')') _block.writeSharpDef(sw) def _writeSharpDefForStatic(sw as CurlyWriter) """ Pseudo example: Cobra: for x in start : stop : step statements C#: { type _lh_start_1 = start; type _lh_stop_2 = stop; type _lh_step_3 = step; int _lh_dir_4 = (_lh_stop_2 - _lh_start_1) < 0 ? -1 : +1; for (x = start; (_lh_dir_4==-1) ? x > stop : x < stop; x += step) { statements } } Note that it's important that the C# code gen uses a for() loop so that the "continue" statement still increments the control variable. In the case that the 'stop' or 'step' are integer literals, no variables are needed for them. TODO: If 'stop' or 'step' are const or readonly, same thing. """ trackLocals = .compiler.willTrackLocals sw.write('{ // for [.var.name] in [.start.toCobraSource] : [.stop.toCobraSource]' + if(.step, ' : [.step.toCobraSource]', '') + '\n') sharpVar = _var.sharpName sharpStart = '_lh_start_[.serialNum]' sharpStop = '_lh_stop_[.serialNum]' sharpStep = '_lh_step_[.serialNum]' sharpDir = '_lh_dir_[.serialNum]' sharpType = _var.type.sharpRef sw.write('[sharpType] [sharpStart] = ') .start.writeSharpDef(sw) sw.write(';\n') if .stop inherits IntegerLit sharpStop = (.stop to IntegerLit).asSharp else sw.write('[sharpType] [sharpStop] = ') .stop.writeSharpDef(sw) sw.write(';\n') isSimpleStep = false step = .step if step is nil sharpStep = '1' isSimpleStep = true else if step inherits IntegerLit sharpStep = step.asSharp isSimpleStep = step.valueAsInt > 0 else sw.write('[sharpType] [sharpStep] = ') step.writeSharpDef(sw) sw.write(';\n') if not isSimpleStep sw.write('int [sharpDir] = [sharpStep] < 0 ? -1 : +1;\n') sw.write('for(') if trackLocals sw.write('CobraCoreInternal.CobraImp.SetLocal("[.var.name]", [sharpVar] = [sharpStart])') else sw.write('[sharpVar] = [sharpStart]') if isSimpleStep sw.write('; [sharpVar] < [sharpStop] ; ') else sw.write('; ([sharpDir]==1) ? [sharpVar] < [sharpStop] : [sharpVar] > [sharpStop]; ') if trackLocals sw.write('CobraCoreInternal.CobraImp.SetLocal("[.var.name]", [sharpVar] += [sharpStep])') else sw.write('[sharpVar] += [sharpStep]') sw.write(') ') _block.writeSharpDef(sw, false) sw.dedent sw.write('}\n') sw.write('} // for [.var.name]\n') class ForEnumerableStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) # in a C# foreach, (1) you must declare a new variable, (2) you cannot reassign it and (3) you cannot access it after the loop # these constraints don't exist in Cobra helperName = '_lh_for_[_var.name]_[_varNumber]' sw.write('foreach ([_var.type.sharpRef] [helperName] in ') # TODO: the sharpRef of a type is not qualified and there is no using System.Collections; # _what.writeSharpDefInContext(sw, false) if _what.type.isDynamic sw.write('CobraCoreInternal.CobraImp.GetEnumerable(') _what.writeSharpDef(sw, false) sw.write(')') else _what.writeSharpDef(sw, false) sw.write(')') stmt = '[.var.sharpName] = [helperName]' if .compiler.willTrackLocals stmt = 'CobraCoreInternal.CobraImp.SetLocal("[.var.name]", [stmt])' stmt += ';\n' _block.writeSharpDef(sw, stmt) class IfStmt is partial def writeSharpDef(sw as CurlyWriter) # if you're looking for if-inherits related code gen, see BlockStmt base.writeSharpDef(sw) sw.write('if (') _cond.writeSharpDef(sw, false) sw.write(')') _trueStmts.writeSharpDef(sw, false) sw.dedent if _falseStmts sw.write('} else') _falseStmts.writeSharpDef(sw, false) sw.dedent sw.write('}\n') class ListenOrIgnoreStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) _event.writeSharpDef(sw, false) sw.write(' [.sharpOperator] ') _target.writeSharpDef(sw, false) sw.writeLine(';') get sharpOperator as String is abstract class ListenStmt is partial get sharpOperator as String is override return '+=' class IgnoreStmt is partial get sharpOperator as String is override return '-=' class LockStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) sw.write('lock (') _expr.writeSharpDef(sw, false) sw.write(')') _block.writeSharpDef(sw) class PassStmt is partial pass class PrintStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) if _destination _destination.writeSharpDef(sw, true) methodName = if(_stop, 'Write', 'WriteLine') sw.write('.[methodName](') else methodName = if(_stop, 'PrintStop', 'PrintLine') sw.write('CobraCoreInternal.CobraImp.[methodName](') sep = '' for arg in _args sw.write('[sep]CobraCoreInternal.CobraImp._printStringMaker.MakeString(') arg.writeSharpDef(sw) sw.write(')') sep = '+" "+' sw.writeLine(');') class PrintRedirectStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) sw.write('CobraCoreInternal.CobraImp.PushPrintTo(') _destination.writeSharpDef(sw, false) sw.write(');\n') sw.write('try') _block.writeSharpDef(sw) sw.write('finally {\n') sw.indent sw.write('CobraCoreInternal.CobraImp.PopPrintTo();\n') sw.dedent sw.write('}\n') class RaiseStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) name = .name.capitalized localName = '_lh_event_[.serialNum]' sw.writeLine('// raise [.name] ...') this_ = if(_definition and _definition.isShared, '', 'this.') sw.write('{ [_eventType.sharpRef] [localName] = [this_][name]; if ([localName]!=null) [localName](') sep = '' for expr in _args sw.write(sep) expr.writeSharpDef(sw, false) sep = ', ' sw.write('); }\n') class ReturnStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) ensurePart = .compiler.curCodeMember.ensurePart willEnsure = ensurePart and ensurePart.willGenerateCode if willEnsure sw.write('_lh_canEnsure = true;\n') if _expr if willEnsure and _backEndResultVarName and _backEndResultVarName.length sw.write('return [_backEndResultVarName]=') _expr.writeSharpDefInContext(sw) sw.write(';\n') else sw.write('return ') _expr.writeSharpDefInContext(sw, false) sw.write(';\n') else sw.write('return;\n') class TraceStmt is partial pass class TraceLocationStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) if .includeTraces sw.write('CobraCoreInternal.CobraCore.Tracer.Trace([.sharpSourceSite]);\n') class TraceAllStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) if .includeTraces sw.write('CobraCoreInternal.CobraCore.Tracer.Trace([.sharpSourceSite], "this", [_codePart.sharpThis]') for param in _codePart.params sw.write(', "[param.name]", [param.sharpName]') for local in _codePart.locals sw.write(', "[local.name]", [local.sharpName]') sw.write(');\n') class TraceExprsStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) if .includeTraces sw.write('CobraCoreInternal.CobraCore.Tracer.Trace([.sharpSourceSite]') sep = ', ' for expr in _exprs sw.write('[sep][Utils.sharpStringLiteralFor(expr.toCobraSource)][sep]') expr.writeSharpDef(sw, false) sw.write(');\n') class TryStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) if _successBlock # C# has no "success" (or in Python, "else") block for the "try" statement # so it has to be simulated helperName = '_lh_success_[_varNumber]' sw.write('bool [helperName] = true;\n') if _finallyBlock sw.write('try {\n') sw.indent sw.write('try') _tryBlock.writeSharpDef(sw, false) sw.dedent sw.write('}\n') if _catchBlocks.count .writeCatchBlocks(sw, helperName) sw.write('finally { if ([helperName])') _successBlock.writeSharpDef(sw) sw.write('}\n') if _finallyBlock sw.dedent sw.write('}\n') sw.write('finally') _finallyBlock.writeSharpDef(sw) else sw.write('try') _tryBlock.writeSharpDef(sw) .writeCatchBlocks(sw, nil) if _finallyBlock sw.write('finally') _finallyBlock.writeSharpDef(sw) def writeCatchBlocks(sw as CurlyWriter, localSuccessVar as String?) for eb in _catchBlocks sw.write('catch') if eb.type sw.write(' ([eb.type.sharpRef]') if eb.varName helperName = eb.sharpHelperName sw.write(' [helperName]') sw.write(')') sb = StringBuilder() if localSuccessVar sb.append('[localSuccessVar] = false;\n') if .compiler.hasDetailedStackTraceOption sb.append('CobraCoreInternal.CobraImp.HandledException();\n') if eb.varName trackLocals = .compiler.willTrackLocals if trackLocals sb.append('CobraCoreInternal.CobraImp.SetLocal("[eb.varName]", ') sb.append('[eb.sharpVarName] = [helperName]') if trackLocals sb.append(')') sb.append(';\n') eb.block.writeSharpDef(sw, sb.toString) class CatchBlock is partial var _sharpHelperName as String? get sharpHelperName from var get sharpVarName as String return _var.sharpName class ThrowStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) sw.write('throw ') if _expr if _expr.type.isDynamic sw.write('((System.Exception)') _expr.writeSharpDef(sw, false) if _expr.type.isDynamic sw.write(')') sw.write(';\n') class UsingStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) name = _var.sharpName sw.write('// using\n') sw.write('[name] = ') _initExpr.writeSharpDef(sw) sw.write(';\ntry') _block.writeSharpDef(sw) sw.write('finally {\n') sw.indent if _var.type.isReference sw.write('if ([name]!=null) { ((System.IDisposable)[name]).Dispose(); [name] = null; }\n') else sw.write('((System.IDisposable)[name]).Dispose();\n') sw.dedentAndWrite('}\n') class WhileStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) sw.write('while (') _expr.writeSharpDef(sw, false) sw.write(')') _block.writeSharpDef(sw) class PostWhileStmt is partial def writeSharpDef(sw as CurlyWriter) is override # base.writeSharpDef(sw) - don't generate the other form of while loop sw.write('do') _block.writeSharpDef(sw, false) sw.dedent sw.write('} while (') _expr.writeSharpDef(sw, false) sw.write(');\n') class YieldStmt is partial pass class YieldBreakStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) sw.write('yield break;\n') class YieldReturnStmt is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) ensurePart = .compiler.curCodeMember.ensurePart willEnsure = ensurePart and ensurePart.willGenerateCode if willEnsure sw.write('_lh_canEnsure = true;\n') if _expr backEndResultVarName = .compiler.curCodeMember.backEndResultVarName if willEnsure and backEndResultVarName.length # TODO: resolve yield return with ensure # sw.write('yield return [backEndResultVarName]=') # so for now: sw.write('yield return ') _expr.writeSharpDefInContext(sw) sw.write(';\n') else sw.write('yield return ') _expr.writeSharpDefInContext(sw) sw.write(';\n') else sw.write('yield return;\n') class MultiTargetAssignStatement is partial def writeSharpDef(sw as CurlyWriter) base.writeSharpDef(sw) if _rightValues sw.write('{ // multi-assign \n') nums = List() for value in _rightValues # .bindImp is called on the .block and if transformations # happen, the assignment statements in the block will be # updated rather that in _rightValues, hence this check: if value.transformedTo, value = value.transformedTo to Expr num = .compiler.curBox.makeNextPrivateSerialNumber sw.write('[value.type.sharpRef] lh_rvalue_[num] = ') value.writeSharpDef(sw, false) sw.write(';\n') nums.add(num) for i in _rightValues.count assign = _block.stmts[i] to AssignExpr assign.writeSharpDefWithRight(sw, false) # false = do not write right side sw.write('lh_rvalue_[nums[i]];\n') sw.write('}\n') else _block.writeSharpDef(sw) ## ## Expressions ## class Expr is partial def needsContextCast as bool """ Returns true if this expression needs to be cast within its context. One example, is that a dynamically typed expression requires such casting. """ type = .type if type.isDynamic, return true if type inherits IntType if type.size < 32, return true return false def writeSharpDefInContext(sw as CurlyWriter) .writeSharpDefInContext(sw, true) def writeSharpDefInContext(sw as CurlyWriter, parens as bool) """ When an expression is used where a particular type is expected, such as: # assignment x = y # argument passing obj.foo(x, y) obj[x, y] it may need C# typecasting--particularly if it is dynamic typed expression. This method relies on having had its .contextType set beforehand. This method should be invoked instead of `writeSharpDef` in any situation where .contextType was set. """ cast = _contextType and .needsContextCast if cast if parens, sw.write('(') sw.write('([_contextType.sharpRef])(') .writeSharpDef(sw) if cast sw.write(')') if parens, sw.write(')') def writeSharpDef(sw as CurlyWriter) is override base.writeSharpDef(sw) branch .direction on Direction.In, pass on Direction.Out, sw.write('out ') on Direction.InOut, sw.write('ref ') .writeSharpDef(sw, true) def writeSharpDef(sw as CurlyWriter, parens as bool) pass def writeSharpStmt(sw as CurlyWriter) is override assert .didBindImp sw.node(this) .writeSharpSetLine(sw) .writeSharpDef(sw, false) sw.write(';\n') def writeSharpBreakdown(sw as CurlyWriter) sw.write(r'new object[] { 0') .writeSharpBreakdownItems(sw) sw.write('}, ') get willWriteSharpBreakdownItems as bool return _direction <> Direction.Out def writeSharpBreakdownItems(sw as CurlyWriter) if .willWriteSharpBreakdownItems src = Utils.sharpStringLiteralFor(.toCobraSource) sw.write(', [src], ') .writeSharpDefForBreakdown(sw) def writeSharpDefForBreakdown(sw as CurlyWriter) .writeSharpDef(sw) class NameExpr is partial get asSharp as String return _definition.sharpName class AsExpr is partial def writeSharpStmt(sw as CurlyWriter) is override # this happens for declarations like "i as int" sw.write('// [_name] as [_type.name]\n') def writeSharpDef(sw as CurlyWriter, parens as bool) is override sw.write(.asSharp) class AnonymousMethodExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override # note that C# delegates don't allow explicit return types # C# always infers the return type from the return statement sw.write('delegate') if .params.count sw.write('(') sep = '' for param in .params sw.write(sep) sw.write('[param.type.sharpRef] [param.sharpName]') sep = ', ' sw.write(')') _method.writeSharpImp(sw, false) # writes { ... } def writeSharpDefForBreakdown(sw as CurlyWriter) is override # .writeSharpDef(sw) sw.write('new CobraCoreInternal.CobraDirectString("(anonymous method)")') class LambdaExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override # note that C# delegates don't allow explicit return types # C# always infers the return type from the return statement sw.write('delegate') if .params.count sw.write('(') sep = '' for param in .params sw.write(sep) sw.write('[param.type.sharpRef] [param.sharpName]') sep = ', ' sw.write(')') sw.write('{ return ') .expr.writeSharpDef(sw, false) sw.write(';}') def writeSharpDefForBreakdown(sw as CurlyWriter) is override # .writeSharpDef(sw) sw.write('new CobraCoreInternal.CobraDirectString("(lambda)")') class CallExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override assert .superNode inherits DotExpr name = .name if _definition inherits BoxMember and (_definition to BoxMember).binaryName name = (_definition to BoxMember).binaryName to ! else name = name.capitalized sw.write('[name]') if _genericArgTypes and _genericArgTypes.count sw.write('<') sep = '' for genericArgType in _genericArgTypes sw.write(sep + genericArgType.sharpRef) sep = ', ' sw.write('>') sw.write('(') sep = '' for arg in _args sw.write(sep) if arg inherits AssignExpr # named parameter # can't do arg.left.writeSharpDef as binding invariants are incomplete # C# form is: name:value sw.write((arg.left to IdentifierExpr).name) sw.write(':') arg.right.writeSharpDef(sw) else arg.writeSharpDefInContext(sw) sep = ', ' sw.write(')') def writeSharpBreakdownItems(sw as CurlyWriter) is override # leaving out the base call is intentional: # base.writeSharpBreakdownItems(sw) sw.write(', +1') for expr in _args expr.writeSharpBreakdownItems(sw) sw.write(', -1') class EnumCallExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override if parens sw.write('(') if _args.count == 0 sw.write('default([_definition.sharpRef])') else sep = '' for member in _members sw.write(sep) sw.write(member.sharpRef) sep = '|' if parens sw.write(')') def writeSharpBreakdownItems(sw as CurlyWriter) base.writeSharpBreakdownItems(sw) sw.write(', +1') for member in _members sw.write(', ') sw.write(member.sharpRef) sw.write(', -1') class ForExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override # C#: CobraImp.For(stuff, delegate(x as int) { return x*x }) # C#: CobraImp.For(stuff, delegate(x as int) { if (x<0) return x*x; }) inType = .inferredType outType = _getExpr.type sw.write('CobraCoreInternal.CobraImp.For<[inType.sharpRef],[outType.sharpRef]>(') if _stop # C#: Numeric For Expr CobraImp.For(start, stop, step, delegate(x as int) { return x*x; }) _start.writeSharpDef(sw, false) sw.write(', ') _stop.writeSharpDef(sw, false) sw.write(', ') _step.writeSharpDef(sw, false) else _what.writeSharpDef(sw, false) sw.write(', ') helperName = '_lh_for_[_var.name]_[_varNumber]' if _whereExpr is nil sw.write('delegate([inType.sharpRef] [helperName]) {\n') sw.indent sw.write('[_var.sharpName] = [helperName];\n') sw.write('return ') _getExpr.writeSharpDef(sw, false) sw.write(';\n') sw.dedent sw.write('})') else outHelperName = helperName + '_out' sw.write('delegate([inType.sharpRef] [helperName], out [outType.sharpRef] [outHelperName]) {\n') sw.indent sw.write('[_var.sharpName] = [helperName];\n') sw.write('if (') _whereExpr.writeSharpDef(sw, false) sw.write(') {\n') sw.indent sw.write('[outHelperName] = ') _getExpr.writeSharpDef(sw, false) sw.write(';\n') sw.write('return true;\n') sw.dedent sw.write('} else {\n') sw.indent sw.write('[outHelperName] = [outType.sharpInit];\n') sw.write('return false;\n') sw.dedent sw.write('}\n') sw.dedent sw.write('})') class TryCatchExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override sw.write('CobraCoreInternal.CobraImp.RunTryCatchExpr<[_type.sharpRef]') if _excType sw.write(',') sw.write(_excType.sharpRef) sw.write('>(') # try-expr delegate sw.write('delegate() {\n') sw.indent sw.write('return (') .what.writeSharpDef(sw, false) sw.write(');\n') sw.dedent sw.write('}') sw.write(', ') # get-expr delegate sw.write('delegate() {\n') sw.indent sw.write('return (') _getExpr.writeSharpDef(sw, false) sw.write(');\n') sw.dedent sw.write('})') class IdentifierExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override # recall that this cannot be the right side of "foo.bar" since that is a MemberExpr sharp = .sharpQualification + .sharpName if _requiresTypeOf, sharp = 'typeof(' + sharp + ')' sw.write(sharp) get sharpQualification as String qual = '' if .definition inherits IVar # the only definition that has no parentNameSpace in practice is IVar pass else pn = .definition.parentNameSpace if pn, qual = pn.sharpQualifier return qual get sharpName as String assert .didBindImp assert .namedDefinition assert not .type inherits UnspecifiedType if .superNode inherits DotExpr assert this is not (.superNode to DotExpr).right # should be a CallExpr or MemberExpr instead defi = .namedDefinition return defi.sharpName def _requiresTypeOf as bool # C# often requires typeof(Foo) instead of just plain Foo if not .isTypeReference, return false superNode = .superNode if superNode inherits DotExpr, return false if superNode inherits InheritsExpr, return false if superNode inherits PostCallExpr if superNode.expr is this return false return true def writeSharpStmt(sw as CurlyWriter) is override assert .isCalling sw.write('[_name]();') get sharpAssignmentNames as List? require .didBindImp .namedDefinition body if _definition inherits IVar return _definition.sharpAssignmentNames else return nil def writeSharpDefForBreakdown(sw as CurlyWriter) is override name = .sharpQualification + .sharpName # _requiresTypeOf may return false, but in the sharp def breakdown, typeof() is always required if .isTypeReference, name = 'typeof(' + name + ')' sw.write(name) class IfExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override cond, tpart, fpart = .cond, .tpart, .fpart cast = tpart.type <> fpart.type # C# doesn't do the "greatest common denominator" thing like Cobra does, so some casting is in order sw.write('(') cond.writeSharpDef(sw) sw.write('?') if cast, sw.write('([.type.sharpRef])(') tpart.writeSharpDef(sw) if cast, sw.write(')') sw.write(':') if cast, sw.write('([.type.sharpRef])(') fpart.writeSharpDef(sw) if cast, sw.write(')') sw.write(')') def writeSharpBreakdownItems(sw as CurlyWriter) base.writeSharpBreakdownItems(sw) sw.write(', +1') # indent cond, tpart, fpart = .cond, .tpart, .fpart cond.writeSharpBreakdownItems(sw) # only one of the target expressions is actually evaluated # tpart: src = Utils.sharpStringLiteralFor(tpart.toCobraSource) sw.write(', [src], new CobraCoreInternal.CobraDirectString(') cond.writeSharpDefForBreakdown(sw) sw.write(' ? CobraCoreInternal.CobraCore.ToTechString(') tpart.writeSharpDefForBreakdown(sw) sw.write(') : "(not-evaluated)")') # fpart: src = Utils.sharpStringLiteralFor(fpart.toCobraSource) sw.write(', [src], new CobraCoreInternal.CobraDirectString(') cond.writeSharpDefForBreakdown(sw) sw.write(' ? "(not-evaluated)" : CobraCoreInternal.CobraCore.ToTechString(') fpart.writeSharpDefForBreakdown(sw) sw.write('))') sw.write(', -1') # dedent class IndexExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override if _target.type.isDynamic sw.write('CobraCoreInternal.CobraImp.GetIndexerValue(') _target.writeSharpDef(sw, false) for expr in _args sw.write(', ') expr.writeSharpDef(sw, false) sw.write(')') return if parens sw.write('(') if _target inherits IdentifierExpr if _target.isKindOf(.compiler.typeType) # here we're favoring "Foo[]" being an array type rather than a shared indexer sw.write(_target.name) handled = true if not handled _target.writeSharpDef(sw) sw.write(r'[') sep = '' for expr in _args sw.write(sep) expr.writeSharpDefInContext(sw) sep = ', ' sw.write(']') if parens sw.write(')') def writeSharpBreakdownItems(sw as CurlyWriter) base.writeSharpBreakdownItems(sw) sw.write(', +1') _target.writeSharpBreakdownItems(sw) for expr in _args expr.writeSharpBreakdownItems(sw) sw.write(', -1') class IsNilExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override if parens sw.write('(') sw.write('(') _expr.writeSharpDef(sw) sw.write(')') sw.write('==null') if parens sw.write(')') def writeSharpBreakdownItems(sw as CurlyWriter) base.writeSharpBreakdownItems(sw) _expr.writeSharpBreakdownItems(sw) class IsNotNilExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override if parens, sw.write('(') sw.write('(') _expr.writeSharpDef(sw) sw.write(')') sw.write('!=null') if parens, sw.write(')') def writeSharpBreakdownItems(sw as CurlyWriter) base.writeSharpBreakdownItems(sw) _expr.writeSharpBreakdownItems(sw) class MemberExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override assert .superNode inherits DotExpr if _definition inherits BoxMember name = _definition.binaryName else if _definition inherits EnumMember name = _definition.sharpName if name is nil, name = _name.capitalized sw.write(name) if _definition and (_definition.isMethod or _name=='toString') and not _isReference # TODO: axe the 'toString' check sw.write('()') def writeSharpBreakdownItems(sw as CurlyWriter) is override pass class OldExpr is partial var _sharpVarName as String? pro sharpVarName from var def writeSharpAssignment(sw as CurlyWriter) require .didBindImp .sharpVarName .type body sw.write('[.type.sharpRef] [_sharpVarName] = ') _expr.writeSharpDef(sw) sw.write(';\n') def writeSharpDef(sw as CurlyWriter, parens as bool) is override assert _sharpVarName # this gets called when generating the `ensure` code sw.write(_sharpVarName) class PostCallExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override if _helperMethod sw.write(_helperMethod.name + '(') sep = '' for arg in _args sw.write(sep) if arg inherits AssignExpr arg = arg.right arg.writeSharpDefInContext(sw, false) sep = ',' sw.write(')') else if parens, sw.write('(') expr = _expr isMethodSig = false isDynamic = false if expr inherits TypeExpr if expr.containedType inherits ArrayType # arrays _writeNewCall(sw, nil, (expr.containedType to ArrayType).theWrappedType.sharpRef, false) else _writeNewCall(sw, expr, '', true) else if expr inherits IdentifierExpr if expr.isTypeReference _writeNewCall(sw, expr, '', true) else if expr.receiverType inherits GenericParam # TODO: shouldn't expr.isTypeReference above have caught this? _writeNewCall(sw, nil, expr.receiverType.sharpRef, true) else if expr.type.nonNil.isDescendantOf(.compiler.delegateType) isMethodSig = true else if expr.type.isSystemTypeClass or _type.isDynamic isDynamic = true else assert false, expr else if expr inherits IndexExpr if expr.type.nonNil.isDescendantOf(.compiler.delegateType) isMethodSig = true else if expr.type.isSystemTypeClass or _type.isDynamic isDynamic = true else assert false, expr else assert false, expr if isMethodSig expr.writeSharpDef(sw, false) sw.write('(') .writeSharpArgs(sw) sw.write(')') else if isDynamic defi = expr.definition assert not defi inherits Box, expr # TODO: just curious what = if(defi inherits IType, 'typeof([defi.sharpName])', defi.sharpName to String) if defi inherits IVar if defi.type.isDynamic what = '(System.Type)' + what # TODO: could speed this up. see http://ayende.com/Blog/archive/2008/02/27/Creating-objects--Perf-implications.aspx sw.write('System.Activator.CreateInstance(') if what == r'[]' # indexer - something like myList[idx]() expr.writeSharpDef(sw) else sw.write(what) .writeSharpArgs(sw, ', ') sw.write(')') if parens, sw.write(')') def _writeNewCall(sw as CurlyWriter, expr as Expr?, exprStr as String, isInvocation as bool) require expr or exprStr.length >0 delimeters = if(isInvocation, '()', r'[]') # invocation or index deref sw.write('new ') if expr, expr.writeSharpDef(sw) else, sw.write(exprStr) sw.write(delimeters[0]) .writeSharpArgs(sw) sw.write(delimeters[1]) def writeSharpArgs(sw as CurlyWriter) .writeSharpArgs(sw, '') def writeSharpArgs(sw as CurlyWriter, sep as String) for arg in _args sw.write(sep) if arg inherits AssignExpr # named parameter # cant do arg.left.writeSharpDef as binding invariants are incomplete sw.write((arg.left to IdentifierExpr).name) sw.write(':') # C# form is name:value arg.right.writeSharpDef(sw) else arg.writeSharpDefInContext(sw, false) sep = ',' def writeSharpBreakdownItems(sw as CurlyWriter) base.writeSharpBreakdownItems(sw) sw.write(', +1') _expr.writeSharpBreakdownItems(sw) for expr in _args expr.writeSharpBreakdownItems(sw) sw.write(', -1') class RefExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override if parens sw.write('(') _expr.writeSharpDef(sw, false) if parens sw.write(')') def writeSharpBreakdownItems(sw as CurlyWriter) base.writeSharpBreakdownItems(sw) # TODO class SharpExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override if parens, sw.write('(') if _sharpSource sw.write(_sharpSource) else sw.write(_expr.token.value) if parens, sw.write(')') class SliceExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override sw.write('CobraCoreInternal.CobraImp.GetSlice(') _target.writeSharpDef(sw, false) sw.write(',') if _start is nil, sw.write('null') else, _start.writeSharpDefInContext(sw, false) sw.write(',') if _stop is nil, sw.write('null') else, _stop.writeSharpDefInContext(sw, false) sw.write(',') if _step is nil, sw.write('null') else, _step.writeSharpDefInContext(sw, false) sw.write(')') def writeSharpBreakdownItems(sw as CurlyWriter) base.writeSharpBreakdownItems(sw) sw.write(', +1') _target.writeSharpBreakdownItems(sw) if _start, _start.writeSharpBreakdownItems(sw) if _stop, _stop.writeSharpBreakdownItems(sw) if _step, _step.writeSharpBreakdownItems(sw) sw.write(', -1') class TruthExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override lparen = if(parens, '(', '') rparen = if(parens, ')', '') branch _treatment on Treatment.AsIs _expr.writeSharpDef(sw, parens) on Treatment.InvokeRuntime sw.write('CobraCoreInternal.CobraImp.IsTrue(') _expr.writeSharpDef(sw, false) sw.write(')') on Treatment.CompareToZero sw.write('[lparen]0!=') _expr.writeSharpDef(sw, true) sw.write(rparen) on Treatment.CompareToZeroChar sw.write("[lparen]'\\0'!=") _expr.writeSharpDef(sw, true) sw.write(rparen) on Treatment.CompareToNull sw.write('[lparen]null!=') _expr.writeSharpDef(sw, true) sw.write(rparen) def writeSharpBreakdownItems(sw as CurlyWriter) is override # leaving out the base class is intentional: # base.writeSharpBreakdownItems(sw, isFirstExpr) _expr.writeSharpBreakdownItems(sw) class TypeExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override sharpRef = _containedType.sharpRef if _requiresTypeOf() sw.write('typeof(') sw.write(sharpRef) sw.write(')') else sw.write(sharpRef) def _requiresTypeOf as bool # Cobra never requires that you wrap a type reference in typeof(Foo). # C# requires typeof() in a variety of circumstances and won't accept it in a variety of others. superNode = .superNode if superNode is nil return false else if superNode inherits DotExpr return false if superNode inherits PostCallExpr # could be Type(foo) or _bar(Type) # in C#, the second requires `typeof` and the first won't take it if superNode.expr is this return false if superNode inherits BinaryOpExpr if this is superNode.right if superNode inherits InheritsExpr or superNode inherits AbstractToExpr return false return true def writeSharpDefForBreakdown(sw as CurlyWriter) is override requiresTypeOf = _requiresTypeOf if not requiresTypeOf, sw.write('typeof(') .writeSharpDef(sw) if not requiresTypeOf, sw.write(')') class AllOrAnyExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override sw.write('CobraCoreInternal.CobraImp.[.opName.capitalized](') .expr.writeSharpDefInContext(sw) sw.write(')') def writeSharpBreakdownItems(sw as CurlyWriter) base.writeSharpBreakdownItems(sw) sw.write(', +1') .expr.writeSharpBreakdownItems(sw) sw.write(', -1') class UnaryOpExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override if _op == 'PLUS' if parens sw.write('(') _expr.writeSharpDef(sw, false) if parens sw.write(')') return if _expr.type.isDynamic specs = OperatorSpecs.unaryOpSpecsByCobraText assert specs.containsKey(.token.text) spec = specs[.token.text] opText = Utils.sharpStringLiteralFor(spec.opMethodName) sw.write('CobraCoreInternal.CobraImp.DynamicOp([opText], ') _expr.writeSharpDef(sw, false) sw.write(')') return if parens sw.write('(') branch _op on 'MINUS' sw.write('-') on 'TILDE' sw.write('~') on 'NOT' sw.write('!') else throw FallThroughException(_op) _expr.writeSharpDef(sw) if parens sw.write(')') def writeSharpBreakdownItems(sw as CurlyWriter) base.writeSharpBreakdownItems(sw) sw.write(', +1') _expr.writeSharpBreakdownItems(sw) sw.write(', -1') class Literal is partial get asSharp as String return '' def writeSharpDef(sw as CurlyWriter, parens as bool) is override sw.write(.asSharp) class AtomicLiteral is partial get willWriteSharpBreakdownItems as bool is override return false class BoolLit is partial get asSharp as String is override return if(_value, 'true', 'false') class CharLit is partial get asSharp as String is override if _value[0] to int == 39, return "'\\''" # single quote else, return "'" + _value.toString + "'" class DecimalLit is partial get asSharp as String is override return _value.toString(Utils.cultureInfoForNumbers) + 'm' class FractionalLit is partial get asSharp as String is override if _type == .compiler.floatType(32), suffix = 'f' else if _type == .compiler.floatType(64), suffix = '' else if _type == .compiler.decimalType, suffix = 'm' else, throw FallThroughException(_type) return _value.toString(Utils.cultureInfoForNumbers) + suffix class FloatLit is partial get asSharp as String is override # suppose you have the literal: 0.00001 # using 'F' format for 'fixed point' gives '0.00' which loses the original value # using 'R' format for 'round trip' gives '1E-05' s = _value.toString('R', Utils.cultureInfoForNumbers) if s.indexOf('.') == -1 and 'E' not in s, s += '.0' if (.type to FloatType).size == 32, s += 'f' return s class IntegerLit is partial get asSharp as String is override s = '' if (info = .token.info) inherits int branch info on - 8, s = 'System.SByte' on + 8, s = 'System.Byte' on -16, s = 'System.Int16' on +16, s = 'System.UInt16' on -32, s = '' on +32, s = 'U' on -64, s = 'L' on +64, s = 'UL' else, throw FallThroughException(info) if s.length <= 2 return _value.toString + s else return '([s])[_value.toString]' class NilLiteral is partial get asSharp as String is override return 'null' class StringLit is partial get asSharp as String is override return Utils.sharpStringLiteralFor(_string) class StringSubstLit is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override if _items.count>1 sw.write('CobraCoreInternal.CobraImp.MakeString(') sep = '' for item in _items sw.write(sep) if item inherits StringLit item.writeSharpDef(sw, true) # CC: axe the "true" when the bug about overload groups crossing inheritance is fixed else if item inherits FormattedExpr sw.write('CobraCoreInternal.CobraImp._printStringMaker.MakeString(') item.expr.writeSharpDef(sw) sw.write(',') sw.write(Utils.sharpStringLiteralFor(item.format)) sw.write(')') else sw.write('CobraCoreInternal.CobraImp._printStringMaker.MakeString(') item.writeSharpDef(sw, false) sw.write(')') sep = ',' if _items.count>1 sw.write(')') class BaseLit is partial get asSharp as String is override return 'base' class ThisLit is partial get asSharp as String is override return .compiler.curBox.sharpThis class VarLit is partial get asSharp as String is override return _name class SequenceLit is partial def writeSharpBreakdownItems(sw as CurlyWriter) is override # CC: axe is override base.writeSharpBreakdownItems(sw) sw.write(', +1') for expr in _exprs expr.writeSharpBreakdownItems(sw) sw.write(', -1') class ListLit is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override innerType = (_type to Box).genericParams[0] sw.write('CobraCoreInternal.CobraImp.MakeList<[innerType.sharpRef]>(typeof([_type.sharpRef])') if _exprs.count sw.write(', ') sep = '' for expr in _exprs sw.write(sep) expr.writeSharpDef(sw, false) sep = ', ' sw.write(')') class ArrayLit is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override sw.write('new [_type.sharpRef] { ') if _exprs.count sep = '' for expr in _exprs sw.write(sep) expr.writeSharpDef(sw, false) sep = ', ' sw.write(' }') class SetLit is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override innerType = (_type to Box).genericParams[0] sw.write('CobraCoreInternal.CobraImp.MakeSet<[innerType.sharpRef]>(typeof([_type.sharpRef])') if _exprs.count sw.write(', ') sep = '' for expr in _exprs sw.write(sep) expr.writeSharpDef(sw, false) sep = ', ' sw.write(')') class DictLit is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override assert (_type to Box).genericParams.count==2 keyType = (_type to Box).genericParams[0] valueType = (_type to Box).genericParams[1] sw.write('CobraCoreInternal.CobraImp.MakeDict<[keyType.sharpRef],[valueType.sharpRef]>(typeof([_type.sharpRef])') if _entries.count sw.write(', ') sep = '' for entry in _entries sw.write(sep) entry[0].writeSharpDef(sw) sw.write(',') entry[1].writeSharpDef(sw) sep = ', ' sw.write(')') def writeSharpBreakdownItems(sw as CurlyWriter) is override # CC: axe is override base.writeSharpBreakdownItems(sw) sw.write(', +1') for entry in _entries # CC: keyExpr, valueExpr = entry keyExpr = entry[0] valueExpr = entry[1] keyExpr.writeSharpBreakdownItems(sw) valueExpr.writeSharpBreakdownItems(sw) sw.write(', -1') class ToNilableOrNotExpr is partial def writeSharpBreakdownItems(sw as CurlyWriter) base.writeSharpBreakdownItems(sw) _expr.writeSharpBreakdownItems(sw) class ToNilableExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override if _expr.type.isReference # In C#/.NET, reference types are always "nilable" _expr.writeSharpDef(sw) else # ex: ((int?)x) # ex: ((Color?)Color.Black) sw.write('(([_expr.type.sharpRef]?)') _expr.writeSharpDef(sw, false) sw.write(')') class ToNonNilableExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override type = _expr.type if type inherits NilableType and not type.nonNil.isReference # ex: (x).Value # ex: (obj.foo).Value sw.write('(') _expr.writeSharpDef(sw, false) sw.write(').Value') else if .compiler.options.boolValue('include-nil-checks') sw.write('(CobraCoreInternal.CobraCore._willCheckNil?CobraCoreInternal.CobraImp.CheckNonNil<[type.sharpRef]>([.sharpThis], [Utils.sharpStringLiteralFor(_expr.toCobraSource)], ') _expr.writeSharpDef(sw, false) sw.write(', [.sharpSourceSiteArgs]):') _expr.writeSharpDef(sw, false) sw.write(')') else _expr.writeSharpDef(sw, parens) ## ## Binary Expressions ## class BinaryOpExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override if parens, sw.write('(') _writeSharpDef(sw) if parens, sw.write(')') def _writeSharpDef(sw as CurlyWriter) pass def writeSharpBreakdownItems(sw as CurlyWriter) base.writeSharpBreakdownItems(sw) sw.write(', +1') _writeSharpBreakdownItemsLeft(sw) _writeSharpBreakdownItemsRight(sw) sw.write(', -1') def _writeSharpBreakdownItemsLeft(sw as CurlyWriter) _left.writeSharpBreakdownItems(sw) def _writeSharpBreakdownItemsRight(sw as CurlyWriter) _right.writeSharpBreakdownItems(sw) class AbstractAssignExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override if _trackLocal sw.write('CobraCoreInternal.CobraImp.SetLocal("[_trackName]", ') else if parens sw.write('(') _writeSharpDef(sw) if _trackLocal or parens sw.write(')') class AssignExpr is partial def _writeSharpDef(sw as CurlyWriter) is override .writeSharpDefWithRight(sw, true) def writeSharpDefWithRight(sw as CurlyWriter, right as bool) # TODO: # if trackLocal: # out.write('CobraCoreInternal.CobraImp.SetLocal("%s", ' % .left.name) if _left inherits IndexExpr if _left.target.type.isDynamic # assigning to an indexer of a dynamically typed target requires special code gen sw.write('CobraCoreInternal.CobraImp.SetIndexerValue(') _left.target.writeSharpDef(sw, false) sw.write(', ') _right.writeSharpDef(sw, false) for expr in _left.args sw.write(', ') expr.writeSharpDef(sw, false) sw.write(')') return handled = false if _left inherits IdentifierExpr sharpNames = _left.sharpAssignmentNames if sharpNames and sharpNames.count sharpNames.reverse for i in sharpNames.count sw.write(if(i==0, '', '=')) assert sharpNames[i].length sw.write(sharpNames[i]) else assert _left.sharpName <> '' sw.write(_left.sharpName) handled = true if not handled # `obj.foo = bar` where `obj` is dynamic, requires special handling if _left inherits DotExpr if _left.left.type.isDynamic assert _left.right inherits MemberExpr sw.write('CobraCoreInternal.CobraImp.SetPropertyValue(') _left.left.writeSharpDef(sw, false) sw.write(', ') sw.write(Utils.sharpStringLiteralFor((_left.right to MemberExpr).name.capitalized)) sw.write(', ') _right.writeSharpDef(sw) sw.write(')') return if not handled # for something like "p.total = 0", cannot generate "(p.Total)=0" because then C# sees # the left hand side as an r-value instead an l-value/target. hence the false below. _left.writeSharpDef(sw, false) sw.write('=') if right, _right.writeSharpDefInContext(sw) # handle the case where a type got backed up because of assignment inside of an if-inherits if _backUpIfInheritsStack assert _left.definition inherits IVar _left.definition.ifInheritsStack = Stack(_backUpIfInheritsStack) class NumericPromoExpr is partial def _writeSharpDefDynamic(sw as CurlyWriter) specs = OperatorSpecs.binaryOpSpecsByCobraText assert specs.containsKey(.token.text) spec = specs[.token.text] opText = Utils.sharpStringLiteralFor(spec.opMethodName) sw.write('CobraCoreInternal.CobraImp.DynamicOp([opText], ') _left.writeSharpDef(sw) sw.write(', ') _right.writeSharpDef(sw) sw.write(')') class AugAssignMathExpr is partial def _writeSharpDef(sw as CurlyWriter) is override if .isConcated _writeSharpDefConcated(sw) else if _type.isDynamic _writeSharpDefDynamic(sw) else _writeSharpDefOperation(sw) def _writeSharpDefConcated(sw as CurlyWriter) _left.writeSharpDef(sw, false) sw.write(' = ') sw.write('(' + _left.type.sharpRef + ')') sw.write('CobraCoreInternal.CobraImp.Concated(') _left.writeSharpDef(sw, false) sw.write(', ') _right.writeSharpDef(sw, false) sw.write(')') def _writeSharpDefDynamic(sw as CurlyWriter) is override _left.writeSharpDef(sw) sw.write(' = ') specs = OperatorSpecs.binaryOpSpecsByCobraText assert specs.containsKey(.token.text) spec = specs[.token.text] opText = Utils.sharpStringLiteralFor(spec.opMethodName) sw.write('CobraCoreInternal.CobraImp.DynamicOp([opText], ') _left.writeSharpDef(sw) # TODO: add , false sw.write(', ') _right.writeSharpDef(sw) # TODO: add , false sw.write(')') def _writeSharpDefOperation(sw as CurlyWriter) op = '' sep = ',' # for the method call case branch _op on 'PLUS_EQUALS' op = '+=' on 'MINUS_EQUALS' op = '-=' on 'STAR_EQUALS' op = '*=' on 'STARSTAR_EQUALS' op = 'CobraCoreInternal.CobraImp.PowerTo(' on 'SLASH_EQUALS' op = '/=' on 'SLASHSLASH_EQUALS' if .left.type.nonNil.isDescendantOf(.compiler.anyIntType) op = '/=' else op = 'CobraCoreInternal.CobraImp.IntDiv(' on 'PERCENT_EQUALS' op = '%=' else throw FallThroughException(_op) assert op.length if op.length==2 # C# operator. ex: *= += _left.writeSharpDef(sw) sw.write(op) _right.writeSharpDef(sw) else # method call that returns a value _left.writeSharpDef(sw) sw.write('=') sw.write(op) # ex: 'CobraCoreInternal.CobraImp.Foo(' _left.writeSharpDef(sw) sw.write(sep) _right.writeSharpDef(sw) sw.write(')') class AugAssignBitwiseExpr is partial def _writeSharpDef(sw as CurlyWriter) is override if _left.type.isDynamic _left.writeSharpDef(sw) sw.write(' = ') specs = OperatorSpecs.binaryOpSpecsByCobraText assert specs.containsKey(.token.text) spec = specs[.token.text] opText = Utils.sharpStringLiteralFor(spec.opMethodName) sw.write('CobraCoreInternal.CobraImp.DynamicOp([opText], ') _left.writeSharpDef(sw) # TODO: add , false sw.write(', ') _right.writeSharpDef(sw) # TODO: add , false sw.write(')') return op = '' branch _op on 'AMPERSAND_EQUALS' op = '&=' on 'VERTICAL_BAR_EQUALS' op = '|=' on 'CARET_EQUALS' op = '^=' on 'DOUBLE_LT_EQUALS' op = '<<=' on 'DOUBLE_GT_EQUALS' op = '>>=' else throw FallThroughException(_op) assert op.length _left.writeSharpDef(sw) sw.write(op) _right.writeSharpDef(sw) class BinaryBoolExpr is partial def _writeSharpDef(sw as CurlyWriter) is override if _op=='IMPLIES' sw.write('!') _left.writeSharpDef(sw) branch _op on 'AND' sw.write('&&') on 'OR' or 'IMPLIES' sw.write('||') _right.writeSharpDef(sw) def _writeSharpBreakdownItemsRight(sw as CurlyWriter) is override # have to respect the short circuit otherwise something like "s and s.length" will give # NullReference exception for "s.length" # solution is: given L and R, # write: "R", (!(L) ? "short-circuted" : (R).ToString()) # that's for 'AND'. for, 'OR', remove the !, for 'IMPLIES' it's like AND # so rather than: # _right.writeSharpBreakdownItems(sw) # do this: src = Utils.sharpStringLiteralFor(_right.toCobraSource) sharpNot = if(_op=='OR', '', '!') sw.write(', [src], new CobraCoreInternal.CobraDirectString([sharpNot]') _left.writeSharpDefForBreakdown(sw) sw.write(' ? "(short-circuited)" : CobraCoreInternal.CobraCore.ToTechString(') _right.writeSharpDefForBreakdown(sw) sw.write('))') class BinaryBitwiseExpr is partial def _writeSharpDef(sw as CurlyWriter) is override if _type.isDynamic _writeSharpDefDynamic(sw) return op = '' branch _op on 'AMPERSAND' op = '&' on 'VERTICAL_BAR' op = '|' on 'CARET' op = '^' on 'DOUBLE_LT' op = '<<' on 'DOUBLE_GT' op = '>>' else throw FallThroughException(_op) assert op.length _left.writeSharpDef(sw) sw.write(op) _right.writeSharpDef(sw) class BinaryMathExpr is partial def _writeSharpDef(sw as CurlyWriter) is override if .isConcated _writeSharpDefConcated(sw) else if _type.isDynamic _writeSharpDefDynamic(sw) else _writeSharpDefOperation(sw) def _writeSharpDefConcated(sw as CurlyWriter) sw.write('(' + _left.type.sharpRef + ')') sw.write('CobraCoreInternal.CobraImp.Concated(') _left.writeSharpDef(sw, false) sw.write(', ') _right.writeSharpDef(sw, false) sw.write(')') def _writeSharpDefOperation(sw as CurlyWriter) intType = .compiler.anyIntType left = _left right = _right op = '' pre = '' sep = ',' # for the method call case branch _op on 'PLUS' op = '+' on 'MINUS' op = '-' on 'STAR' op = '*' on 'STARSTAR' op = 'CobraCoreInternal.CobraImp.PowerTo(' on 'SLASH' op = '/' if _left.isKindOf(intType) and _right.isKindOf(intType) pre = '(' + .compiler.numberType.sharpRef + ')' on 'SLASHSLASH' if left.isKindOf(intType) and right.isKindOf(intType) op = '/' else if left.isKindOf(.compiler.decimalType) or right.isKindOf(.compiler.decimalType) op = 'System.Decimal.Floor(' sep = '/' else if left.isKindOf(.compiler.anyFloatType) or right.isKindOf(.compiler.anyFloatType) op = 'System.Math.Floor(' sep = '/' else throw FallThroughException([left.type, right.type]) on 'PERCENT' op = '%' else throw FallThroughException(_op) assert op.length sw.write(pre) if op.length==1 _left.writeSharpDef(sw) sw.write(op) _right.writeSharpDef(sw) else sw.write(op) # ex: 'CobraCoreInternal.CobraImp.Foo(' _left.writeSharpDef(sw) sw.write(sep) _right.writeSharpDef(sw) sw.write(')') class CompareExpr is partial var _cobraToSharp = { 'EQ': '==', 'NE': '!=', 'GT': '>', 'LT': '<', 'GE': '>=', 'LE': '<=', 'IS': '==', 'ISNOT': '!=', } def _writeSharpDef(sw as CurlyWriter) is override left = _left right = _right op = _op # Compute the C# operation which will be an operator or method call if op=='EQ' or op=='NE' if not left.type.isReference and not right.type.isReference # struct/value types if not left.type inherits PrimitiveType and not right.type inherits PrimitiveType # user-defined structs op = '.Equals(' else # primitives like ints and decimals (even mixed like someDecimal == someInt) op = _cobraToSharp[_op] else done = false stringType = .compiler.stringType if left.isKindOf(stringType) and right.isKindOf(stringType) # TODO: check for static comparison operations instead op = _cobraToSharp[_op] done = true else if left.type inherits Box # TODO: try skipping on requiring that the type is a Box leftBox = left.type to Box compareTo = leftBox.memberForName('compareTo') if compareTo and compareTo.isMethod and compareTo.resultType.isDescendantOf(.compiler.anyIntType) and right.type.isDescendantOf(leftBox) # need last condition to prevent someString.CompareTo(someChar) op = '.CompareTo(' done = true equals = leftBox.methodForExactSignature('equals', [leftBox to IType], .compiler.boolType) if equals op = '.Equals(' done = true if not done assert op in ['EQ', 'NE'] assert op == .token.which op = if(op=='EQ', 'CobraCoreInternal.CobraImp.Equals(', 'CobraCoreInternal.CobraImp.NotEquals(') else if op=='IS' or op=='ISNOT' if left.type inherits PrimitiveType and right.type inherits PrimitiveType op = _cobraToSharp[_op] else if left.type.isReference and right.type.isReference # skip the CobraCoreInternal.CobraImp.Is call for faster performance op = if(op=='IS', 'System.Object.ReferenceEquals(', '!System.Object.ReferenceEquals(') else # non-trivial situation.. fall back to runtime support op = if(op=='IS', 'CobraCoreInternal.CobraImp.Is(', 'CobraCoreInternal.CobraImp.IsNot(') else if left.type.isDynamic or right.type.isDynamic sw.write('CobraCoreInternal.CobraImp.DynamicCompare(') left.writeSharpDef(sw, false) sw.write(', ') right.writeSharpDef(sw, false) sw.write(')[_cobraToSharp[_op]]0') return else done = false if left.type inherits Box # TODO: try skipping on requiring that the type is a Box leftBox = left.type to Box compareTo = leftBox.memberForName('compareTo') if compareTo and compareTo.isMethod and compareTo.resultType.isDescendantOf(.compiler.anyIntType) and left.type == right.type # need last condition to prevent someString.CompareTo(someChar) op = '.CompareTo(' done = true if not done op = _cobraToSharp[_op] # Write the C# code if op.length <= 2 left.writeSharpDef(sw) sw.write(op) right.writeSharpDef(sw) else if op == '.Equals(' if _op == 'NE', sw.write('!(') left.writeSharpDef(sw) sw.write(op) right.writeSharpDef(sw, false) sw.write(')') if _op == 'NE', sw.write(')') else if op == '.CompareTo(' left.writeSharpDef(sw) sw.write(op) right.writeSharpDef(sw, false) sw.write(') [_cobraToSharp[_op]] 0') else if op.endsWith('(') sw.write(op) left.writeSharpDef(sw) sw.write(',') right.writeSharpDef(sw) sw.write(')') else throw FallThroughException(op) class ChainedCompareExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override if parens, sw.write('(') .writeSharpDef(sw) if parens, sw.write(')') def writeSharpDef(sw as CurlyWriter) is override sw.write('CobraCoreInternal.CobraCore.ChainedComparison(') _items[0].writeSharpDef(sw) itemIndex = 1 for operation in _operations sw.write(', "[operation]", ') _items[itemIndex].writeSharpDef(sw) itemIndex += 1 sw.write(')') class DotExpr is partial def needsContextCast as bool r = base.needsContextCast if not r r = .left.type.isDynamic # Why isn't the base implementation sufficient? # Because for `d.getType` where `d` is dynamic, the type of `d.getType` is *not*. # It`s System.Type because `getType` is recognized as an Object method. # This would confuse the code generation into thinking that `d.getType` is a statically typed expression. return r def _writeSharpDef(sw as CurlyWriter) is override if _left.receiverType.isDynamic # handle dynamic typing if _dotRightExpr.definition sw.write('(' + _dotRightExpr.type.sharpRef + ')') if _right inherits MemberExpr sw.write('CobraCoreInternal.CobraImp.GetPropertyValue(') _left.writeSharpDef(sw, not _left inherits DotExpr) sw.write(', ') sw.write(Utils.sharpStringLiteralFor(_right.name.capitalized)) sw.write(')') else if _right inherits CallExpr sw.write('CobraCoreInternal.CobraImp.InvokeMethod(') _left.writeSharpDef(sw, not _left inherits DotExpr) sw.write(', ') sw.write(Utils.sharpStringLiteralFor(_right.name.capitalized)) for arg in _right.args sw.write(', ') arg.writeSharpDef(sw, false) sw.write(')') else throw FallThroughException(_right) else # Given Cobra "A.B.C()" where C is a class/type/struct, then the C# needs to be "new A.B.C()" # (But stuff like this makes me wonder if the ASTs should be doing some transformations, like collapsing qualified types.) if _dotRightExpr.memberDefinition inherits BoxMember backing = _dotRightExpr.definition.sharedMethodBacking to String? if backing # example: Cobra: c.isUpper C#: char.IsUpper(c) # also used for extension methods under .NET 2.0 sw.write(backing+'(') if not (_dotRightExpr.memberDefinition to BoxMember).sharedMethodBackingIsAlias # guard against Cobra "decimal.parse('5.0')" --> C# "Decimal.Parse(decimal, "5.0")" _left.writeSharpDef(sw, false) sep = ', ' else sep = '' if _right inherits CallExpr for arg in _right.args sw.write(sep) arg.writeSharpDefInContext(sw) sep = ', ' sw.write(')') return # handle static typing # don't write 'this' for shared members writeThis = true if _left inherits ThisLit writeThis = not _dotRightExpr.memberDefinition.isShared else if .curCodeMember inherits Initializer and _right inherits IDotRightExpr and (_right to IDotRightExpr).name == 'init' and _left inherits ThisOrBaseLit # Well, in practice this doesn't really happen because Constructer.innerWriteSharpDef usurps the code gen for base calls Stmt.inInitCall = true didSetInInitCall = true if writeThis if _left.needsContextCast _left.writeSharpDefInContext(sw) else _left.writeSharpDef(sw, not _left inherits DotExpr) sw.write('.') _right.writeSharpDef(sw, false) if didSetInInitCall Stmt.inInitCall = false def _writeSharpBreakdownItemsLeft(sw as CurlyWriter) is override _left.writeSharpBreakdownItems(sw) class InExpr is partial def writeSharpDef(sw as CurlyWriter, parens as bool) is override v = true branch _op on 'NOTIN', sw.write('!') on 'IN', pass else, throw FallThroughException(_op) if .needsNilCheck # Constraints: # (1) Don't evaluate the left expression twice # (2) Don't evaluate the right expression if the left is nil # In general, don't cause side effects from extra evaluation. if .left inherits IdentifierExpr and (.left to IdentifierExpr).definition inherits IVar # If the left is a var, there is no danger in double evaluation causing side effects. # sharp: (someVar==null ? false : ) sw.write('(') .left.writeSharpDef(sw, false) sw.write('==null ? false : ') # may have to cast int? to int, for example if .left.type inherits NilableType _ and not .left.type.nonNil.isReference _ and .containsExpr # well this last condition doesn't work so well due to generic args like List.Contains(T item) # and not (((.containsExpr to DotExpr).dotRight.memberDefinition to Method).params[0].type inherits NilableType) # then need cast assert .left.type inherits NilableType .right.writeSharpDef(sw, false) sw.write('.Contains(([.left.type.nonNil.sharpRef])') .left.writeSharpDef(sw) sw.write(')') if v, sw.write('/* A */') else .writeInCheckSharp(sw, false, v) if v, sw.write('/* B */') sw.write(')') else # cobra: left in right # sharp: CobraCoreInternal.CobraImp.InWithNullCheck(, delegate(__lh_value) { return .Contains(__lh_value); }) # use: static public bool InWithNullCheck(T a, Predicate predicate) # cobra: left in right # sharp: CobraCoreInternal.CobraImp.In(left, delegate() { return right; }) sw.write('CobraCoreInternal.CobraImp.InWithNullCheck(') .left.writeSharpDef(sw, false) sw.write(', delegate([.left.type.sharpRef] __lh_value_[.serialNum]) { return ') if .containsExpr # Do not write the .containsExpr here because it's argument to .contains in the # full left expression. Instead, we want to pass the argument passed to the delegate. .right.writeSharpDef(sw, false) sw.write('.Contains(([.left.type.nonNil.sharpRef]) __lh_value_[.serialNum]);') if v, sw.write('/* C */') else # No .containsExpr means we use CobraImp.In(a, b) for the delegate sw.write('CobraCoreInternal.CobraImp.In(__lh_value_[.serialNum], ') .right.writeSharpDef(sw, false) sw.write(');') if v, sw.write('/* D */') sw.write(' })') else .writeInCheckSharp(sw, parens, v) if v, sw.write('/* E */') def writeInCheckSharp(sw as CurlyWriter, parens as bool, v as bool) if .containsExpr .containsExpr.writeSharpDef(sw, parens) if v, sw.write('/* F */') else sw.write('CobraCoreInternal.CobraImp.In(') .left.writeSharpDef(sw, false) sw.write(',') .right.writeSharpDef(sw, false) sw.write(')') if v, sw.write('/* G */') class InheritsExpr is partial def _writeSharpDef(sw as CurlyWriter) is override _left.writeSharpDef(sw) sw.write(' is ') _right.writeSharpDef(sw, false) class ToExpr is partial def needsContextCast as bool is override if .type.isDynamic, return base.needsContextCast else, return false # the user is already casting, so go with their cast def _writeSharpDef(sw as CurlyWriter) is override rightType = _potentialTypeExpr.potentialType to ! # it's not nil or _bindImp would have thrown an error if rightType is .compiler.passThroughType _left.writeSharpDef(sw) return sw.write('(') _right.writeSharpDef(sw, false) # double parens would be pointless and also causes a C# error sw.write(')') if not rightType inherits NilableType and rightType.isReference and .compiler.options.boolValue('include-nil-checks') sw.write('CobraCoreInternal.CobraImp.CheckNonNil<[_left.type.sharpRef]>([.sharpThis], [Utils.sharpStringLiteralFor(_left.toCobraSource)], ') _left.writeSharpDef(sw, false) sw.write(', [.sharpSourceSiteArgs])') else _left.writeSharpDef(sw) class ToQExpr is partial def _writeSharpDef(sw as CurlyWriter) is override # x to? int --> ((x is int || x is int?) ? (int?)x : (int?)null) # x to? int? --> ((x is int || x is int?) ? (int?)x : (int?)null) # ^ but this would cause multiple evaluation of x, so... # x to? int? --> (int?)CobraCoreInternal.CobraImp.ToOrNil(x) # x to? string --> (x as String) # x to? string? --> (x as String) right = .right t = right.definition to IType t = t.nonNil typeSharpRef = t.sharpRef if t.isReference sw.write('(') .left.writeSharpDef(sw) sw.write(') as [typeSharpRef]') else sw.write('[typeSharpRef]?)CobraCoreInternal.CobraImp.ToOrNil<[typeSharpRef]>(') .left.writeSharpDef(sw) class CoalesceExpr is partial def _writeSharpDef(sw as CurlyWriter) is override _left.writeSharpDef(sw) sw.write(' ?? ') _right.writeSharpDef(sw) class InverseCoalesceExpr is partial def _writeSharpDef(sw as CurlyWriter) is override if _right inherits NilLiteral # `left ! nil` always returns nil -- it's like a no-op if _left inherits IdentifierExpr # no side effect possible sw.write('null') else # in case `left` has side effects, evaluate it sw.write('CobraCoreInternal.CobraImp.Return<[_type.sharpRef]>(null, ') _left.writeSharpDef(sw, false) sw.write(')') else _left.writeSharpDef(sw) sw.write('==null ? null : ') if not _right.type inherits NilableType and not _right.type inherits NilType sw.write('([_right.type.sharpRef]?)') _right.writeSharpDef(sw) class CoalesceAssignExpr is partial def _writeSharpDef(sw as CurlyWriter) is override # L = L * R _left.writeSharpDef(sw) sw.write(' = ') _left.writeSharpDef(sw) sw.write(' ?? ') _right.writeSharpDef(sw) class InverseCoalesceAssignExpr is partial def _writeSharpDef(sw as CurlyWriter) is override _left.writeSharpDef(sw) sw.write(' = ') if _right inherits NilLiteral # `left != nil` is the same as `left = nil` sw.write('null') else _left.writeSharpDef(sw) sw.write('==null ? null : ') if not _right.type inherits NilableType and not _right.type inherits NilType sw.write('([_right.type.sharpRef]?)') _right.writeSharpDef(sw) ## ## Attributes ## class AttributeDecl is partial def writeSharpDef(sw as CurlyWriter) is override .writeSharpDef(sw, '', true) def writeSharpDef(sw as CurlyWriter, prefix as String) .writeSharpDef(sw, prefix, true) def writeSharpDef(sw as CurlyWriter, prefix as String, willNewLine as bool) sw.write(r'[') if .isReturnTarget and prefix == '', prefix = 'return:' sw.write(prefix) expr = _expr to PostCallExpr sw.write('[expr.type.sharpRef]') if expr.args.count sw.write('(') sep = '' for arg in expr.args sw.write(sep) if arg inherits AssignExpr sw.write(((arg.left to dynamic).name to String).capitalized) # CC: wouldn't need this if extension methods were registered with dynamic binder sw.write('=') arg = arg.right assert not arg inherits AssignExpr arg.writeSharpDefInContext(sw) sep = ', ' sw.write(')') sw.write(r']') if willNewLine, sw.writeLine('') class AssemblyDecl is partial def writeSharpDef(sw as CurlyWriter) is override base.writeSharpDef(sw) for attrib in _attribs attrib.writeSharpDef(sw, 'assembly: ') def writeSharpTestInvocation(sw as CurlyWriter) pass ## ## Misc items ## class SharpBackEndUtils shared var _backEndKeyWordList = 'abstract as base bool break byte case catch char checked class const continue decimal default delegate do double else enum event explicit extern false finally fixed float for foreach goto if implicit in int interface internal is lock long namespace new null object operator out override params private protected public readonly ref return sbyte sealed short sizeof stackalloc static string struct switch this throw true try typeof uint ulong unchecked unsafe ushort using virtual void volatile while'.split var _backEndKeyWordSet = Set() def isBackEndKeyWord(word as String) as bool """ Returns true if the given word is a keyword in C# 2.0. """ require word.length test assert .isBackEndKeyWord('object') assert .isBackEndKeyWord('if') assert not .isBackEndKeyWord('total') body if _backEndKeyWordSet.count == 0 for word in _backEndKeyWordList _backEndKeyWordSet.add(word) return word in _backEndKeyWordSet def backEndNameForLocalVarName(name as String) as String if .isBackEndKeyWord(name) return '@' + name # C# supports @ as a prefix to escape identifiers else return name