""" Miscellaneous utility methods for use throughout the Cobra compiler. """ use System.Reflection extend IList def toList2 as List """ Turn an IEnumerable of T into a list of T. """ return List(this) class ObjectLister shared def list(obj) print print 'Object:', obj print ' getType = [obj.getType.name]' props = List(obj.getType.getProperties) props.sort(do(a as PropertyInfo, b as PropertyInfo)=a.name.toLower.compareTo(b.name.toLower)) for pi in props if pi.canRead try value = pi.getValue(obj, nil) catch TargetParameterCountException # happens for .Item[i] "indexer" continue catch exc as Exception try if exc inherits TargetInvocationException and exc.innerException exc = exc.innerException to ! value = '(exception: [exc.getType.name]: [exc.message])' catch value = '(exception)' success value = _toTechString(value) name = pi.name name = Utils.cobraNameForNativeMemberName(name) print ' [name] = [value]' if obj inherits System.Collections.ICollection i = 0 for item in obj print ' \[[i]] = [_toTechString(item)]' i += 1 def _toTechString(value) as String try return CobraCore.toTechString(value) catch exc as Exception try return '(exception: [exc.getType.name]: [exc.message])' catch return '(exception from .toTechString)' extend String def before(sep as String) as String test s = 'abc|de' assert s.before('|') == 'abc' assert s.before('=') == 'abc|de' assert '|abcde'.before('|') == '' assert 'abcde|'.before('|') == 'abcde' body index = .indexOf(sep) if index < 0, return this return .substring(0, index) def after(sep as String) as String test s = 'abc|de' assert s.after('|') == 'de' assert s.after('=') == '' assert '|abcde'.after('|') == 'abcde' assert 'abcde|'.after('|') == '' body index = .indexOf(sep) if index < 0, return '' return .substring(index+sep.length) ## Language specific def canBeUndottedMemberName as bool """ Returns true if this string is the kind of name that can reference a box member without using the dot operator. Returns true if the name starts with an underscore or capital letter. The underscored names are typically protected data fields or methods while the uppercase names would be enums or (in the future) nested boxes. """ return .startsWith('_') or .isCapitalized def startsWithLowerLetter as bool require .length test assert 'a'.startsWithLowerLetter assert 'z'.startsWithLowerLetter assert not 'A'.startsWithLowerLetter assert not '1'.startsWithLowerLetter assert not '_'.startsWithLowerLetter body return this[0].isLower def startsWithNonLowerLetter as bool require .length test assert not 'a'.startsWithNonLowerLetter assert not 'z'.startsWithNonLowerLetter assert not '1'.startsWithNonLowerLetter assert 'A'.startsWithNonLowerLetter body return this[0] <> this[0].toLower class Utils is partial shared def combinePaths(a as String, b as String) as String """ Same as Path.combine() but leaves no '\.\' or '/./' in the result. """ p = Path.combine(a, b) good = Path.directorySeparatorChar.toString bad = '[good].[good]' p = p.replace(bad, good) return p def normalizePath(path as String) as String while path.startsWith('.\\'), path = path[2:] while path.startsWith('./'), path = path[2:] return path def plural(items) as String ensure result == '' or result == 's' test assert Utils.plural([]) == 's' assert Utils.plural([2]) == '' assert Utils.plural([2, 2]) == 's' body return if(items.count==1, '', 's') def pluralize(name as String) as String test assert Utils.pluralize('bar') == 'bars' assert Utils.pluralize('class') == 'classes' body return name + if(name.endsWith('s'), 'es', 's') def toIdentifier(s as String) as String test cases = [ ['', ''], ['aoeu', 'aoeu'], [' aoeu', '_aoeu'], ['aoeu/aoeu', 'aoeu_aoeu'], ] for case in cases assert Utils.toIdentifier(case[0]) == case[1] body sb = StringBuilder() for c in s if c.isLetterOrDigit or c == c'_' sb.append(c) else sb.append(c'_') return sb.toString ## BackEnd C# def cobraNameForNativeMemberName(name as String) as String require name.length ensure not result[0].isUpper test cases = [ 'Foo foo', 'FooBar fooBar', 'PI pi', 'OSVersion osVersion', 'XMLParser xmlParser', 'foo foo', 'fooBar fooBar', '_foo _foo', '_fooBar _fooBar', '_Foo _Foo', '_FooBar _FooBar', '_ _', 'UTF8 utf8', ] for case in cases parts = case.split assert Utils.cobraNameForNativeMemberName(parts[0]) == parts[1] body if name.length == 1 return name.toLower else upperCount = 0 for ch in name if ch.isUpper upperCount += 1 else break if upperCount == 0 return name if upperCount == name.length # all upper like 'PI' return name.toLower if upperCount == 1 return name[0].toLower.toString + name[1:] else # multi upper count like OSVersion or UTF8 if name[upperCount].isLetter return name[:upperCount-1].toLower + name[upperCount-1:] # osVersion else return name[:upperCount].toLower + name[upperCount:] # utf8 def sharpStringLiteralFor(s as String) as String # Reference: "Escape Sequences" - http://msdn.microsoft.com/en-us/library/h21280bw.aspx sb = StringBuilder() sb.append('"') for c in s branch c on c'\a', sb.append('\\a') on c'\b', sb.append('\\b') on c'\f', sb.append('\\f') on c'\n', sb.append('\\n') on c'\r', sb.append('\\r') on c'\t', sb.append('\\t') on c'\v', sb.append('\\v') on c'"', sb.append('\\"') on c'\\', sb.append('\\\\') on c'\0', sb.append('\\0') else, sb.append(c) sb.append('"') return sb.toString def printSource(src as String) lineNum = 1 for line in src.split(c'\n') line = line.replace('\t', ' ') print '[lineNum]|[line]' # CC: right align and pad 0 the lineNum lineNum += 1 def forceExtension(fileName as String, extension as String) as String require fileName.length extension.length ensure result.endsWith(extension) test assert Utils.forceExtension('foo.csv', '.exe') == 'foo.exe' assert Utils.forceExtension('foo.csv', 'exe') == 'foo.exe' assert Utils.forceExtension('foo', '.exe') == 'foo.exe' assert Utils.forceExtension('foo', 'exe') == 'foo.exe' assert Utils.forceExtension('foo.bar.csv', '.exe') == 'foo.bar.exe' body if not extension.startsWith('.') extension = '.' + extension return Path.changeExtension(fileName, extension) to ! get isDevMachine as bool return Environment.getEnvironmentVariable('COBRA_IS_DEV_MACHINE') == '1' get isRunningOnUnix as bool """ Returns true if the current process is running on Unix/Posix/Linux/BSD/etc. TODO: move to CobraCore """ platform = Environment.osVersion.platform to int return platform in [4, 6, 128] # http://www.mono-project.com/FAQ:_Technical def readKeyValues(path as String) as Dictionary require path.length return .readKeyValues(path, File.openText(path), Console.out) def readKeyValues(path as String, tr as TextReader, warnings as TextWriter) as Dictionary test nl = Environment.newLine warnings = Console.out d = Utils.readKeyValues('test', StringReader('# blah[nl][nl]foo = bar[nl]'), warnings) assert d.count == 1 assert d['foo'] == 'bar' body d = Dictionary() while true line = tr.readLine if line is nil, break line = line.trim if line == '' or line.startsWith('#'), continue parts = line.split(@[c'='], 2) if parts.length == 1 warnings.writeLine('.readKeyValue: [path]: ignoring line: [line]') # TODO: report line number continue key, value = parts[0].trim, parts[1].trim d[key] = value return d var _cultureInfoForNumbers = System.Globalization.CultureInfo('en-US', false) get cultureInfoForNumbers from var """ To control decimal point and thousands separators in the language, it is necessary to pass a CultureInfo to .parse and .toString methods of decimal and float. Without this, Cobra would expect numeric literals to be formatted to the culture of the current user. This makes source code non-portable. This problem was reported from a non-English culture and the report requested that Cobra consistently use '.' for decimal. """ def loadWithPartialName(name as String) as Assembly? # TODO: Holy crap! loadWithPartialName is marked obsolete, # but I know of no other way to support a library reference like "compiler /r:System.Data.dll myprog.ext" as C#, Cobra, etc. do. # Suggestions/solutions are welcome. # avoid warning: "System.Reflection.Assembly.LoadWithPartialName(string)" is obsolete # return Assembly.loadWithPartialName(name) # with dynamic binding: assemblyType = Assembly to dynamic try ass = assemblyType.loadWithPartialName(name) catch System.Reflection.TargetInvocationException # or any Exception ass = nil return ass class ShouldNotCallException inherits Exception # TODO: perhaps there should also be an attribute so the compiler can give compile-time warnings var _type as Type? cue init base.init cue init(t as Type) base.init('Type is [t]') _type = t get containingType from _type class NotSupportedException inherits Exception cue init base.init class BasicTests test # verify sanity assert 2 + 2 == 4 # verify that .toList produces a new list when used on lists t = [1, 2, 3] assert t.toList is not t