Ruby: What percentage of the code do I own?

mine = 0
total = 0
require 'find'
Find.find(".") do |p|
  Find.prune if File.basename(p).downcase == ".git"
  next unless p =~ /\.java$/i
  system "git annotate #{p} > c:\\temp\\foo.txt"
 
  lines = File.open("C:\\temp\\foo.txt","r") { |f| f.read.split(/\n/) }
  mine += lines.grep(/jfelice/).size
  total += lines.size
  print "#{p}: #{mine}/#{total}\n" # Just to show progress
end
 
print "#{mine}\n"
Posted on August 6, 2010 at 1:44 pm by Jason Felice · Permalink · Comments Closed
In: Uncategorized

Ruby: Find ini files containing an entry

Ruby’s always good with the “find”:

require 'find'
 
Find.find(".") do |path|
  Find.prune if path =~ /\.svn$/
  Find.prune if path =~ /XPay$/i
  next unless path =~ /\.ini$/i
  File.open(path,'r') do |f|
    f.each_line do |l|
      next unless l =~ /^\s*dasserver\.exe\s*=\s*(?:[0-9]+\.){3}([0-9]+)\s*$/
      print "#{path} (2.0.2.#{$1})\n" if $1.to_i >= 1885
    end
  end
end
Posted on December 1, 2009 at 9:31 am by Jason Felice · Permalink · Comments Closed
In: Uncategorized

Scala: Ugly hairy rewrite C++ code jig

I think this might be the first time I’ve gotten to use a real algorithm at work in several years. This is also my first attempt at Scala (pretty nice!), and one ugly jig. It had to be adjusted every fourth or fifth component I used it on because of strangeness in the way that component was written, or in the way Java handles regular expressions, or both.

 
import java.io.{File,FileReader,FileWriter}
import java.util.regex.{Pattern,Matcher}
import java.lang.Math.min
 
class Fixer(path : String) {
 
  def files = new File(path).listFiles
 
  def isCppFile (f : File) = f.getName().toLowerCase() endsWith ".cpp"
  def isHFile (f: File) = {
    val name = f.getName().toLowerCase()
    name.endsWith(".h") && name != "resource.h" && !name.startsWith("test_") && name != "vxnapi3.h"
  }
 
  def mostSimilarFile(fs : => Array[File]) = {
    var result : File = null
    var bestDistance = 9999
    for (f <- fs) {
      val dist = editDistance(
          f.getName().replaceAll("\\.[^\\.]*$", "").toLowerCase,
          new File(path).getName().toLowerCase
          )
      if (dist < bestDistance) {
        bestDistance = dist
        result = f
      }
    }
    result
  }
 
  def cppFile = {
    val result = mostSimilarFile(files filter isCppFile)
    println(result)
    result
  }
  def hFile = {
    val result = mostSimilarFile(files filter isHFile)
    println("hFile:"+result)
    result
  }
 
  def readFile (f: File) = {
    val r = new FileReader(f)
    val b = new Array[Char](1024)
    val sb = new StringBuffer
    var l = 0
    do {
      l = r.read(b)
      if (l > 0) sb.append(b, 0, l)
    } while (l > 0)
    r.close()
    sb.toString()
  }
 
  def writeFile (f: File, s : String) = {
    val w = new FileWriter(f) 
    w.write(s)
    w.close
  }
 
  var cppText_ : String = null
  def cppText = {
    if (cppText_ == null) cppText_ = readFile(cppFile)
    cppText_
  }
 
  var hText_ : String = null
  def hText = {
    if (hText_ == null) hText_ = readFile(hFile)
    hText_
  }
 
  class RegInfo(
      guid_ : String,
      compId_ : String,
      compType_ : String,
      compSubType_ : String,
      friendlyName_ : String,
      progId_ : String,
      className_ : String,
      baseClassName_ : String
    )
  {
    def guid = guid_
    def compId = compId_
    def compType = compType_
    def compSubType = compSubType_
    def friendlyName = friendlyName_
    def progId = progId_
    def className = className_
    def baseClassName = baseClassName_
 
    def traitClassName = className + "Traits"
    def declareTraits =
      "struct " + traitClassName + " {\r\n" +
      "    static const int COMPID;\r\n" +
      "    static const int TYPE;\r\n" +
      "    static const int SUBTYPE;\r\n" +
      "    static const GUID IID;\r\n" +
      "    static const char* PROG_ID;\r\n" +
      "    static const char* FRIENDLY_NAME;\r\n" +
      "};\r\n"
 
    def rightPad(s : String, l : Int) : String =
      if (s.length >= l) s else rightPad(s+" ", l)
 
    def implementItem(typ : String, name : String, value : String) =
      "const " + rightPad(typ, 5) + " " + traitClassName + "::" + rightPad(name, 14) +
      "= " + value + ";\r\n"
 
    def implementTraits =
      "//////////////////////////////////////////////////////////////////////////////\r\n" +
      "// " + traitClassName + "\r\n" +
      "\r\n" +
      implementItem("int", "COMPID", compId) +
      implementItem("int", "TYPE", compType) +
      implementItem("int", "SUBTYPE", compSubType) +
      implementItem("GUID", "IID", guid) +
      implementItem("char*", "PROG_ID", progId) +
      implementItem("char*", "FRIENDLY_NAME", friendlyName) +
      "\r\n"
  }
 
  var regInfo_ : RegInfo = null
  def regInfo = {
    if (regInfo_ == null) regInfo_ = makeRegInfo
    regInfo_
  }
 
  def extract(re : String, g : Int) = {
    val matcher = Pattern.compile(re, Pattern.MULTILINE).matcher(hText + cppText)
    if (!matcher.find) null
    else {
      val result = matcher.group(g)
      if (matcher.find) throw new Exception("Found more than one!! " + re)
      result
    }
  }
 
  val fdataTerm = "\\s*([^,]*),\\s*(?://[^\n]*)?"
  val fdataPattern = Pattern.compile(
    "CFactoryData\\s+g_FactoryDataArray[^=]*=\\s*" +
    "\\{\\s*" +
    "\\{" +
      fdataTerm +
      fdataTerm +
      fdataTerm +
      fdataTerm +
      fdataTerm +
      fdataTerm +
    "\\s*\\}\\s*" +
    "\\}\\s*;"
    )
 
  /*
  ** Yet again, a regexp broken down into code because the Java regexp
  ** NFA runner takes exponential time for things that should have no
  ** backtracking whatsoever.  Argh!!!
  */
  def scanFactoryData = {
    var begin = 0
    var end = 0
    def nextLine = {
      begin = end
      if (begin >= cppText.length) null
      else {
        end = begin
        while (end < cppText.length && cppText(end) != '\n') end += 1
        if (end < cppText.length) end += 1
        cppText.substring(begin, end)
      }
    }
 
    var state = -1
    val startPattern = Pattern.compile("^\\s*CFactoryData\\s+g_FactoryDataArray.*")
    val valuePattern = Pattern.compile("^\\s*([^,]*),\\s*(?://[^\n]*)?\n?")
    var line = nextLine
    var friendlyName : String = null
    var progId : String = null
    while (line != null) {
      state match {
      case -1 =>
        if (startPattern.matcher(line).find)
          state = 0
      case _ =>
        val m = valuePattern.matcher(line)
        if (m.find) {
          state match {
          case 2 =>
            friendlyName = m.group(1)
          case 3 =>
            progId = m.group(1)
          case _ =>
            ()
          }
          state += 1
        }
      }
      line = nextLine
    }
    ( friendlyName, progId )
  }
 
  def editDistance(cname : String, fname : String) = {
    val dp = new Array[Array[Int]](100, 100)
    dp(0)(0) = 0;
    for (i <- 1 to cname.length)
      dp(i)(0) = i
    for (j <- 1 to fname.length)
      dp(0)(j) = j
    for (i <- 1 to cname.length)
      for (j <- 1 to fname.length)
        dp(i)(j) = min(
                     min(1+dp(i-1)(j), 1+dp(i)(j-1)),
                     dp(i-1)(j-1) + (if (cname(i-1) == fname(j-1)) 0 else 1)
                     )
 
    dp(cname.length)(fname.length)
  }
 
  def findClassName = {
    val constructorMatcher = Pattern.compile(
      "^([A-Z][A-Z0-9a-z_]*)\\s*::\\s*([A-Z][A-Za-z0-9_]*)\\s*\\(",
      Pattern.MULTILINE
      ).matcher(cppText)
 
    // There can be many constructors - find the one with the closest Levenshtein
    // edit distance to the filename (sans extension).
 
    var bestDistance = 9999
    var className : String = null
    while (constructorMatcher.find) {
      if (constructorMatcher.group(1) == constructorMatcher.group(2)) {
        val cname = constructorMatcher.group(1)
        val fname = cppFile.getName().replaceAll("\\.[^\\.]*$", "")
        val dist = editDistance(cname, fname)
        if (dist < bestDistance) {
          bestDistance = dist
          className = cname
        }
      }
    }
 
    className
  }
 
  def findDIID = {
    val matcher = Pattern.compile("DIID_[A-Z0-9a-z_]*").matcher(cppText)
    val fname = cppFile.getName().replaceAll("\\.[^\\.]*$", "").toUpperCase
 
    var bestDistance = 9999
    var diid : String = null
    while (matcher.find) {
      val dist = editDistance(
        matcher.group(0).replaceAll("^DIID_", ""),
        fname
      )
      println("diid = " + matcher.group(0) + "; dist = " + dist)
      if (dist < bestDistance) {
        println("accept")
        bestDistance = dist
        diid = matcher.group(0)
      }
    }
    diid
  }
 
  def makeRegInfo = {
    val ( friendlyName, progId ) = scanFactoryData
 
    var baseClassName : String = null  
    val className = findClassName
    val bcMatcher = Pattern.compile(
      "class\\s+" + className + "\\s*:\\s*public\\s+([A-Za-z0-9_]+(?:<[^>]*>)?)"
      ).matcher(hText)
    if (bcMatcher.find)
      baseClassName = bcMatcher.group(1)
 
    new RegInfo(
      findDIID,
      extract("m_iCompId\\s*=\\s*([^;]*?)\\s*;", 1),
      extract("m_iType\\s*=\\s*([^;]*?)\\s*;", 1),
      extract("m_iSubType\\s*=\\s*([^;]*?)\\s*;", 1),
      friendlyName,
      progId,
      className,
      baseClassName
    )
  }
 
  val precedingComments = "\\s*(?://[^\n]*\n\\s*)*"
 
  def roughPrecedingComment (text : String, bp : Int) = {
    // Why, oh why, does Java use the NFA matcher for regular expressions?
    // There isn't enough stack space to properly match optional leading
    // C-style comments, so here is a rough approximation by backward scanning
    var b = bp - 1
    while (b > 0 && Character.isSpace(text(b-1))) b -= 1
    if (b > 4 && text(b-1) == '/' && text(b-2) == '*') {
      b -= 4
      while (b > 0 && (text(b) != '/' || text(b+1) != '*'))
        b -= 1
      while (b > 0 && Character.isSpace(text(b-1)))
        b -= 1
    }
    b
  }
 
  def removeNDQI = {
    val pm = Pattern.compile(
      "^HRESULT\\s+[a-zA-Z0-9_\\s]*::NondelegatingQueryInterface\\s*\\(" +
        "[^)]*\\)\\s*\\{\\s*" +
        ".*?" +
        "^\\}[\t ]*\\n?",
        Pattern.DOTALL | Pattern.MULTILINE
      ).matcher(cppText)
 
    if (pm.find) {
      val b = roughPrecedingComment(cppText, pm.start)
      val e = pm.end
 
      cppText_ =
        cppText_.substring(0, b) +
        "\r\n" +
        cppText_.substring(e)
    }
  }
 
  def removeGUIDDecl = {
    val p = Pattern.compile("\\s+^EXTERN_C.*GUID.*=.*;[^\n]*\n", Pattern.MULTILINE)
    cppText_ = p.matcher(cppText).replaceAll("")
  }
 
  def replace(re : String, s : String) = {
    val p = Pattern.compile(re, Pattern.MULTILINE)
    cppText_ = p.matcher(cppText).replaceAll(s)
    hText_ = p.matcher(hText).replaceAll(s)
  }
 
  def removeFactoryData = {
    replace(
      precedingComments +
      "CFactoryData\\s+g_FactoryDataArray[^=]*=\\s*\\{\\s*\\{[^}]*\\},?\\s*\\}\\s*;[^\\n]*\\n?",
      "\r\n"
      )
    replace(
      precedingComments +
      "int\\s+g_cFactoryDataEntries\\s*=[^;]*;[^\n]*\n",
      "\r\n"
      )
  }
 
  def removeGetRegName = {
    replace(
      precedingComments +
      "STDAPI\\s+GetRegName\\([^\\)]*\\)\\s*\\{[^\\}]*\\}[^\n]*\n",
      "\r\n"
      )
  }
 
  def removeSillyInstance = {
    replace(
      precedingComments +
      regInfo.className + " [A-Za-z_0-9]+\\s*\\(\\s*NULL\\s*\\)\\s*;[^\n]*\n?",
      ""
      )
  }
 
  def fixConstructor = {
    replace(
      "^\\s*(m_iCompId|m_iType|m_iSubType)\\s*=\\s*[^;]*;[^\n]*\n",
      ""
      )
    replace(
      "(?:" + regInfo.baseClassName + "|CInterleaveCommTempl_)\\s*(\\([^)]*\\))",
      "ComponentImpl_$1"
      )
  }
 
  def addRegisterMacro = {
    if (!cppText.endsWith("\r\n")) cppText_ += "\r\n"
    cppText_ += "\r\nREGISTER(" + regInfo.className + ");\r\n"
  }
 
  def addTraitImpl = {
    val matcher = Pattern.compile(
      "(?:^\\s*#\\s*include[^\n]*\n|^\\s*using\\s+namespace\\s[^\n]*\n)",
      Pattern.MULTILINE
      ).matcher(cppText)
    var last = 0
    while (matcher.find) last = matcher.end
 
    cppText_ =
      cppText_.substring(0, last) +
      "\r\n" +
      regInfo.implementTraits +
      "//////////////////////////////////////////////////////////////////////////////\r\n" +
      "// " + regInfo.className + "\r\n" +
      cppText_.substring(last)
  }
 
  def addTraitDecl = {
    val cmatcher = Pattern.compile(
      "^\\s*class\\s+" + regInfo.className + "[^a-zA-Z0-9_]",
      Pattern.MULTILINE
      ).matcher(hText)
 
    assert(cmatcher.find)
    val pos = cmatcher.start
 
    hText_ =
      hText_.substring(0, pos) +
      "\r\n" +
      regInfo.declareTraits +
      hText_.substring(pos)
  }
 
  def removeNDQIDecl = {
    var pm = Pattern.compile(
      "(?m)^\\s*(?:virtual\\s+)?HRESULT\\s+_?_stdcall\\s+NondelegatingQueryInterface\\s*\\([^)]*\\)\\s*;[^\n]*\n"
      ).matcher(hText)
    if (pm.find) {
      val b = roughPrecedingComment(hText, pm.start)
      val e = pm.end
 
      hText_ =
        hText_.substring(0, b) +
        "\r\n" +
        hText_.substring(e)
    }
  }
 
  def fixDerivation = {
    replace(
      "\\s*:\\s*public\\s+" + regInfo.baseClassName + "\\s*\\{\\s*",
      "\r\n" +
      "    : public ComponentImpl<\r\n" +
      "          " + regInfo.className + "\r\n" +
      "        , " + regInfo.baseClassName + "\r\n" +
      "        , " + regInfo.traitClassName + "\r\n" +
      "    >\r\n" +
      "{\r\n"
      )
  }
 
  def fixCppFile = {
    removeNDQI
    removeGUIDDecl
    removeFactoryData
    removeGetRegName
    removeSillyInstance
    fixConstructor
    addRegisterMacro
    addTraitImpl
  }
 
  def fixHFile = {
    addTraitDecl
    removeNDQIDecl
    fixDerivation
  }
 
  def fix {
    fixHFile
    fixCppFile
 
    writeFile(cppFile, cppText)
    writeFile(hFile, hText)
  }
}
 
object fixreg {
  def main(args : Array[String]) = new Fixer(args(0)).fix
}
 
// vi:set ft=scala sts=2 sw=2 ai et:
Posted on October 5, 2009 at 3:23 pm by Jason Felice · Permalink · Comments Closed
In: Uncategorized

Ruby: Remove instances of a method

Here’s an “old school” style jig. I first attempted this in Haskell, but I needed Perl-compatible regular expressions (Posix can’t handle the negative lookahead needed for correctly identifying C-style comments), and installing the PCRE library for Haskell on Windows is painful, though I’ll get around to it one of these days.

 
require 'find'
 
Find.find "Components\\CommComponents" do |p|
  Find.prune if p =~ /\.svn$/i
  next unless p =~ /\.(cpp|cxx|h)$/i
 
  data = File.open(p,"rb") {|f| f.read}
  orig_data = data.clone
  if p =~ /\.h$/
    data.gsub!(/
      \s*
      (?:^[\ \t]*\/\/[^\n]*\n)*
      ^[\ \t]*(?:virtual\s+)?HRESULT\s+(?:[A-Za-z0-9_]*::)?GetAttributes\s\([^\)]*\)\s*;[\ \t]*
      /mx, "")
  elsif p =~ /\.(cpp|cxx)$/
    data.gsub!(/
      \s*
      (?:\/\*(?:[^\*]|\*(?!\/))*\*\/)?\s*
      HRESULT\s+[A-Za-z_][A-Za-z0-9_]*::GetAttributes\s*\(\s*
        ATTRIBUTES\s*\*[^,)]*,\s*int\s*\*[^,)]*\)\s*
        \{[^\}]*\}
      /mx, "")
  end
  if data != orig_data
    File.open(p,"wb") {|f| f.write(data) }
  end
end
Posted on September 3, 2009 at 9:24 am by Jason Felice · Permalink · Comments Closed
In: Jigs · Tagged with: , ,

Haskell: Filter C++ Classes

I’ve needed a jig to separate a C++ class’s declaration and implementation for quite some time. Compile with “ghc -O2 –make classfilt.hs” and invoke with “classfilt –impl” or “classfilt –decl”, piping in the class source with inline methods. It certainly doesn’t handle all the corners of the C++ language, but it does most of the job.

import Char (isAlpha, isDigit, isSpace)
import Data.Maybe
import System.Environment (getArgs)
 
isCSymF c = isAlpha c || c == '_'
isCSym c  = isCSymF c || isDigit c
 
spanEscapedQuote q (c:cs) | c == q = ([c], cs)
spanEscapedQuote q ('\\':c:cs)     = case spanEscapedQuote q cs of
                                       (t,r) -> ('\\':c:t, r)
spanEscapedQuote q (c:cs)          = case spanEscapedQuote q cs of
                                       (t,r) -> (c:t, r)
 
isNonNLSpace c = isSpace c && c /= '\n'
 
maybeLBrace []       = Nothing
maybeLBrace ('{':cs) = Just ("{", cs)
maybeLBrace (c:cs)
    | isSpace c         = case maybeLBrace cs of
                            Just (t,r) -> Just (c:t,r)
                            Nothing    -> Nothing
    | True              = Nothing
 
hasLBrace s = isJust $ maybeLBrace s
 
-- Primitive C++ lexical scanner.  It knows enough not to get confused by
-- things in comments and quotation marks, and things on preprocessor directive
-- lines.  It returns a list of pairs, and one invariant is that collecting and
-- concatenating all of the 'snd' elements of the pair will return the original
-- file.
 
data Token = Comment
             | Identifier
             | LBrace
             | RBrace
             | LParen
             | Semi
             | OtherStuff
             deriving (Show, Eq)
 
tokens _ []           = []
tokens _ ('/':'*':cs) = case finishCStyleComment cs of
                          (t,r) -> (Comment, "/*" ++ t) : tokens False r
                        where
                          finishCStyleComment ('*':'/':cs) = ("*/", cs)
                          finishCStyleComment (c:cs) = case finishCStyleComment cs of
                                                         (s,r) -> (c:s,r)
tokens _ ('/':'/':cs) = case span (/= '\n') cs of
                          (t,'\n':r) -> (Comment, "//" ++ t ++ "\n") : tokens True r
                          (t,r)      -> (Comment, "//" ++ t) : tokens False r
tokens _ ('"':cs)     = case spanEscapedQuote '"' cs of
                          (t,r)      -> (OtherStuff, '"' : t) : tokens False r -- "
tokens _ ('\'':cs)    = case spanEscapedQuote '\'' cs of
                          (t,r)      -> (OtherStuff, '\'' : t) : tokens False r
tokens _ ('{':cs)     = (LBrace, "{") : tokens False cs
tokens _ s
    | hasLBrace s     = case maybeLBrace s of
                          Just (t,r) -> (LBrace,t) : tokens False r
 
tokens _ ('}':cs)     = (RBrace, "}") : tokens False cs
tokens _ (';':cs)     = (Semi, ";") : tokens False cs
tokens _ ('(':cs)     = (LParen, "(") : tokens False cs
 
tokens _ ('\n':cs)    = (OtherStuff, "\n") : tokens True cs
tokens True ('#':cs)  = case span (/= '\n') cs of
                          (t,'\n':r) -> (OtherStuff, "#" ++ t ++ "\n") : tokens True r
                          (t,r)      -> (OtherStuff, "#" ++ t) : tokens False r
tokens _ (c:cs)
    | isCSymF c       = case span isCSym cs of
                          (t,r)      -> (Identifier, c:t) : tokens False r
    | isNonNLSpace c  = case span isNonNLSpace cs of
                          (t,r)      -> (OtherStuff, c:t) : tokens False r
tokens s (c:cs)       = (OtherStuff, [c]) : tokens s cs
 
 
-- filter method bodies from class declarations and replace with ';'
rewriteAsClassDeclaration s =
        rTopLevel $ tokens True s
    where
        rTopLevel []                         = ""
        rTopLevel ((Identifier,"class"):ts)  = "class" ++ rClassDecl ts
        rTopLevel ((_,s):ts)                 = s ++ rTopLevel ts
 
        rClassDecl ((Semi,s):ts)             = s ++ rTopLevel ts
        rClassDecl ((LBrace,s):ts)           = s ++ rClassBody ts
        rClassDecl ((_,s):ts)                = s ++ rClassDecl ts
 
        rClassBody ((RBrace,s):ts)           = s ++ rTopLevel ts
        rClassBody ((LBrace,_):ts)           = ";" ++ rMethodImpl 0 ts
        rClassBody ((_,s):ts)                = s ++ rClassBody ts
 
        rMethodImpl 0 ((RBrace,s):ts)        = rClassBody ts
        rMethodImpl n ((RBrace,_):ts)        = rMethodImpl (n+1) ts
        rMethodImpl n ((LBrace,_):ts)        = rMethodImpl (n-1) ts
        rMethodImpl n ((_,_):ts)             = rMethodImpl n ts
 
-- filter everything except method implementations and add class
-- name prefix.
 
minimumIndentation s =
        minimum $ filter (/= 0) $ map findIndent $ lines s
    where
        findIndent (' ':cs)  = 1 + findIndent cs
        findIndent ('\t':cs) = 4 + findIndent cs
        findIndent _         = 0
 
unindent n s =
        init $ unlines $ map (unindentLine n) $ lines s
    where
        unindentLine n s | n <= 0 = s
        unindentLine n (' ':cs)   = unindentLine (n-1) cs
        unindentLine n ('\t':cs)  = unindentLine (n-4) cs
        unindentLine n s          = s
 
reformatMethod name s = unindent (minimumIndentation s) s
 
qualifyMethodName cname s =
        leader ++ cname ++ "::" ++ methodName
    where
        methodName = reverse $ takeWhile isCSym $ dropWhile isSpace $ reverse s
        leader = reverse $ dropWhile isCSym $ dropWhile isSpace $ reverse s
 
rewriteAsClassImplementation s =
        rTopLevel $ tokens True s
    where
        rTopLevel []                                  = ""
        rTopLevel ((Identifier,"class"):ts)           = rClassDecl "class" Nothing ts
        rTopLevel ((_,s):ts)                          = s ++ rTopLevel ts
 
        rClassDecl s _ []                             = s
        rClassDecl s _ ((Semi,rs):ts)                 = s ++ rs ++ rTopLevel ts
        rClassDecl s (Nothing) ((Identifier,name):ts) = rClassDecl (s ++ name) (Just name) ts
        rClassDecl s (Just name) ((LBrace,_):ts)      = rClassBodyStmt name False "" ts
        rClassDecl s maybeName ((_,rs):ts)            = rClassDecl (s ++ rs) maybeName ts
 
        rClassBodyStmt cname _ _ ((Semi,_):ts)        = rClassBodyStmt cname False "" ts
        rClassBodyStmt cname _ _ ((RBrace,_):ts)      = rClassBodySemi ts
        rClassBodyStmt cname _ s ((LBrace,rs):ts)     = rMethodBody cname (s ++ rs) 0 ts
        rClassBodyStmt cname False s ((LParen,rs):ts) = rClassBodyStmt cname True (qualifyMethodName cname s ++ rs) ts
        rClassBodyStmt cname m s ((Identifier,"virtual"):ts) = rClassBodyStmt cname m s ts
        rClassBodyStmt cname m s ((_,rs):ts)          = rClassBodyStmt cname m (s ++ rs) ts
 
        rMethodBody name s 0 ((RBrace,rs):ts)         = (reformatMethod name $ s ++ rs) ++ rClassBodyStmt name False "" ts
        rMethodBody name s n ((RBrace,rs):ts)         = rMethodBody name (s ++ rs) (n-1) ts
        rMethodBody name s n ((LBrace,rs):ts)         = rMethodBody name (s ++ rs) (n+1) ts
        rMethodBody name s n ((_,rs):ts)              = rMethodBody name (s ++ rs) n ts
 
        rClassBodySemi ((Semi,_):ts)                  = rTopLevel ts
        rClassBodySemi ((_,_):ts)                     = rClassBodySemi ts
 
classfilt ["--decl"] s = rewriteAsClassDeclaration s
classfilt ["--impl"] s = rewriteAsClassImplementation s
 
main = do args <- getArgs
          interact (classfilt args)
 
-- vi:set sts=4 sw=4 ai et:
Posted on August 18, 2009 at 9:59 pm by Jason Felice · Permalink · Comments Closed
In: Jigs · Tagged with: ,

Erlang: Remove #include directives from all cpp files

I’m certain this can be done better and with fewer lines of code, but it’s my first Erlang jig.

-module(cppdev).
-compile(export_all).
 
quote_re([]) ->
    [];
quote_re([$.|Rest]) ->
    "\\." ++ quote_re(Rest);
quote_re([C|Rest]) ->
    [C|quote_re(Rest)].
 
filter_include(Data, Include) ->
    {ok, Pattern} = re:compile("^\\s*#\\s*include\\s*[<\"](?:(?i)" ++ quote_re(Include) ++ ")[>\"]\\s*$"),
    grep_v(Data, Pattern).
 
extract_line(Text) ->
    case lists:splitwith(fun(C) -> C =/= $\n end, Text) of
        {Line, [C|Rest]} -> {Line ++ [C],Rest};
        {Line, Rest}     -> {Line, Rest}
    end.
 
grep_v(Data, Pattern) ->
    grep_v([], Data, Pattern).
 
grep_v(Accum, [], _) ->
    lists:flatten(lists:reverse(Accum));
grep_v(Accum, Data, Pattern) ->
    {Line,Rest} = extract_line(Data),
    case re:run(Line, Pattern) of
        {match,_} -> grep_v(Accum, Rest, Pattern);
        _         -> grep_v([Line|Accum], Rest, Pattern)
    end.
 
transform_file(Fun, Filename) ->
	{ok, Data} = file:read_file(Filename),
	NewData = list_to_binary(Fun(binary_to_list(Data))),
	if
	  Data =:= NewData ->
        nochange;
	  true ->
        ok = file:write_file(Filename ++ "-", Data),
        ok = file:write_file(Filename, NewData)
	end.
 
find_files(Fun, Root) ->
    find_files([], Fun, [Root]).
 
find_files(Accum, _, []) ->
    Accum;
find_files(Accum, Fun, [Root|RestOfRoots]) ->
    {ok,Files} = file:list_dir(Root),
    Files2 = lists:filter(fun(FN) -> string:to_lower(FN) =/= ".svn" end, Files),
    PathedFiles = lists:map(fun(FN) -> Root ++ "\\" ++ FN end, Files2),
    Dirs = lists:filter(fun(P) -> filelib:is_dir(P) end, PathedFiles),
    FilteredFiles = lists:filter(Fun, PathedFiles),
    find_files(Accum ++ FilteredFiles, Fun, RestOfRoots ++ Dirs).
 
 
fix() ->
    StripHeaderF = fun(D) -> filter_include(D, "ISOMsgEx.h") end,
    TransformF = fun(FN) -> transform_file(StripHeaderF, FN) end,
    FilenameTestF = fun(FN) -> string:to_lower(string:right(FN, 4)) =:= ".cpp" end,
    lists:map(TransformF, find_files(FilenameTestF, "Components")).
Posted on August 3, 2009 at 3:54 pm by Jason Felice · Permalink · Comments Closed
In: Jigs · Tagged with: ,

Python: Generate Test Case Stubs For All Classes In A Module

I had a module full of classes. I wanted to generate a unittest.TestCase stub for every class, such as this:

# TODO: Finish this
class Test_className(unittest.TestCase):
    def setUp(self):
        pass

So, I wrote the following Python script:

if __name__=="__main__":
    file = "c:/dev/sourceFile.py"
    f = open(file, "r")
    line = f.readline()
    count = 0
    thing = [ ]
 
    while line:
        if line[:5] == "class":
            count += 1
            parenpos = line.find("(")
            classname = line[6:parenpos]
            thing.append('# TODO: Finish this')
            thing.append('class Test_%s(unittest.TestCase):' % classname)
            thing.append('    def setUp(self):')
            thing.append('        pass')
            thing.append('')
 
 
        line = f.readline()
 
    f.close()
 
    for item in thing:
        print item

Just a quick way to save myself some mundane typing.

Posted on July 9, 2009 at 7:39 pm by David Andrzejewski · Permalink · One Comment
In: Jigs · Tagged with: , ,

Scheme: Translate static data from C++ to XML

The C++ was extracted and munged with vim to make nice Scheme syntax elements before running this script.

(with-input-from-file "das.cpp"
  (lambda ()
    (do ((field-type (read) (read))
	  (field-length (read) (read))
	  (field-description (read) (read))
	  (bit-number (read) (read)))
        ((eof-object? field-type) #f)
      (printf "<isofield id=\"~a\" length=\"~a\" name=\"~a\" pad=\"true\" class=\"org.jpos.iso.~a\"/>~n"
	     bit-number
	     field-length
	     field-description
	     field-type))))
Posted on June 23, 2009 at 9:13 am by Jason Felice · Permalink · Comments Closed
In: Jigs · Tagged with: , , ,

Java: JFrame tester

Our first submission:

So here is my silly Java JFrame that I use to test all sorts of things; Swing components, logging frameworks or just to see pretty colors on my screen.

Really though, just follow the comments and play with what a JFrame can do. In addition there is a convenient comment that instructs you where to place your code to make this handy JFrame do something you need it to do.

It only relies on standard Java stuff like Swing and AWT libraries so it should work for most anyone.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowEvent;
 
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.WindowConstants;
 
/**
 * It is a Java Swing Window or JFrame to be specific.
 *
 * @author MasterJigger
 *
 */
public class JavaTestFrame extends JFrame implements KeyListener {
    private static final long serialVersionUID = 1L;
 
    public JavaTestFrame() {
        super("Jigcode Java Test Frame");
 
        setSize(new Dimension(700, 500));       //This is a usable window size
 
        //Center me on the screen
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        Dimension screenSize = toolkit.getScreenSize();
        int x = (screenSize.width - getWidth()) / 2;
        int y = (screenSize.height - getHeight()) / 2;
        setLocation(x, y);
 
        //close and exit when the user request it!
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);                  
        this.addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(WindowEvent winEvt) {
                System.exit(0);
            }
        });
 
        addKeyListener(this);              //Lets show keystrokes in the consol for something to do!
 
        /**
         * This is where the usefulness comes in, place what you need on the JFrame here.
         * Try Swing components out, test logging frameworks, see pretty colors.
         */
        //*********************************************************************************************
        getContentPane().setLayout(new BorderLayout());               //Setup to put something useful on the JFrame
        add(new JLabel("Hello fellow Jigcoders"), BorderLayout.CENTER);     //Time for something useful
        //*********************************************************************************************
 
        setUndecorated(false);  //false will display frame decorations ha ha! that's what Java says, these are the min/max/close etc on the title bar
        setResizable(true);           //pretty self-explanatory                                             
        setFocusable(true);         //a must have for any good frame
        setVisible(true);              //unless you want to keep this for yourself it should be visible.
        requestFocus();              //selfish but necessary
    }
 
    /**
     * @param args
     */
    public static void main(String[] args) {
        new JavaTestFrame();                           
    }
 
    public void keyPressed(KeyEvent e) {
        System.out.println(e.getKeyChar());
    }
 
    public void keyReleased(KeyEvent e) {
 
    }
 
    public void keyTyped(KeyEvent e) {
 
    }
}
Posted on June 18, 2009 at 8:34 pm by Jason Felice · Permalink · Comments Closed
In: Uncategorized

Quick and Dirty WordPress Backup Script (*NIX Shell)

Here’s a quick and dirty script to back up a wordpress blog.  In fact, it’s the backup script we use to back up Jigcode.  Of course, all of the hostnames, usernames and passwords have been removed.

This runs from a crontab which gets e-mailed to me, so I do an ‘ls’ at the end so I can see the fruits of my labor.

A prerequisite is that you set up SSH keys to connect to the host, so you don’t need to store a plaintext password.

This script runs on FreeBSD.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/sh
cd /tank/backup/jigcode
 
rm -f wordpress_blog.sql.gz.003
mv wordpress_blog.sql.gz.002 wordpress_blog.sql.gz.003
mv wordpress_blog.sql.gz.001 wordpress_blog.sql.gz.002
mv wordpress_blog.sql.gz wordpress_blog.sql.gz.001
 
/usr/local/bin/mysqldump -h HOSTNAME -uUSERNAME -pPASSWORD DATABASENAME --complete-insert --create-options > wordpress_blog.sql
if [ $? -ne 0 ]; then
    echo "Error returned from MYSQLDUMP!"
fi
 
/usr/local/bin/rsync --del -r --verbose USERNAME@HOSTNAME:jigcode /tank/backup/jigcode/
if [ $? -ne 0 ]; then
    echo "Error returned from rsync!"
fi
 
gzip -9 wordpress_blog.sql
if [ $? -ne 0 ]; then
    echo "Error returned from gzip!"
fi
ls -alth /tank/backup/jigcode
Posted on June 11, 2009 at 10:50 pm by David Andrzejewski · Permalink · Comments Closed
In: Jigs · Tagged with: , , ,