Scala External DSL and Combinator Parsing experimentation

Yesterday I was looking around on the inter-webs for a Scala project that was “iBatis” like, but more Scala oriented.  I found some cool stuff (ScalaQL, and scala-sql-dsl for example) but nothing quite what I was looking for.

I kind of chewed on what exactly it was that I was looking for and this is my 10k/foot feature list:

  • Ability to define the underlying data store in a DSL that can be understood by Scala
  • Ability to map Scala classes to JDBC results by binding to artifacts of the data store

This is more or less what iBatis accomplishes with xml (now annotations) and a bunch of reflection.  I’d like to see it done without XML and minimal reflection.

That sent me down the rabbit hole on how exactly one goes about defining a DSL in scala.  I re-read chapter 31 in Programming in Scala.  I also read chapter 11 in Programming Scala.  Then I took a stab at writing a parser combinator for a very, very small subset of the “CREATE TABLE” DDL syntax for SQL.

import scala.util.parsing.combinator._

class TableDdlParser extends JavaTokenParsers {

def tables: Parser[Map[String, Any]] = rep(table) ^^ { Map() ++ _ }

def table: Parser[(String,Any)] =
("TABLE" ~ tableName ~ columns
^^ { case "TABLE" ~ tableName ~ tableContents => (tableName,tableContents) })

def tableName: Parser[String] = ident ^^ { case ident => ident }

def columns: Parser[Map[String, Any]] = "("~> repsep(column, ",") <~")" ^^ { Map() ++ _ }

def column: Parser[(String,Any)] =
columnName ~ dataType ^^ { case columnName ~ dataType => (columnName,dataType) }

def columnName: Parser[String] = ident ^^ { case ident => ident }

def dataType: Parser[Any] = "VARCHAR" | "INTEGER"

}

object TableDdlParserRunner extends TableDdlParser {

def main(args: Array[String]) {
val input =
"""TABLE person (first_name VARCHAR, last_name VARCHAR, age INTEGER)
TABLE place (city VARCHAR, state VARCHAR)"""
println(parseAll(tables,input))
}

}


Here is the output



[2.51] parsed: 
Map(person -> Map(first_name -> VARCHAR,
last_name -> VARCHAR,
age -> INTEGER),
place -> Map(city -> VARCHAR,
state -> VARCHAR))


This experiment only supports two data types (VARCHAR and INTEGER), without any of the other metadata that is required for those types.  I just wanted a feel for how hard it would be to write and didn’t want to get bogged down in the details.  It turned out to be quite a bit easier than I thought it would be.



Next up is to figure out the best way to map this DDL information to a scala class (If I don’t get distracted by something else first).

Save TortoiseHg Remote Repository Credentials

A quick tip so you don’t have to re-enter your user name and password every time you pull or push from a remote Mercurial repository when using TortoiseHg.

Right click in the folder explorer and select TortoiseHg –> Repository Settings.

repository_settings

Select the “Sync” tab in the “TortoiseHg Configure Repository” window.  Either select the desired Remote repository path and click “Edit” or create a new Remote repository path with the “Add” button.

sync

That will bring up the “Edit remote repository path” dialog.  Enter your User and Password, then click OK.

 

edit

Finally click the “Apply” button on the “TortoiseHg Configure Repository” window.