GEEK BOY'S BLOG http://geekboy.posterous.com Peter Clark on life, coding, parenthood, Java, Scala, Ruby, grad school, and demographic data posterous.com Mon, 23 Apr 2012 16:17:00 -0700 Minecraft launcher http://geekboy.posterous.com/minecraft-launcher http://geekboy.posterous.com/minecraft-launcher

My older daughter is becoming a minecraft junkie. Recently, she asked if she could run her own server for her and a friend to use. I was kind of excited that she wanted to try that, but didn't want to give her access to Terminal.app in Parental Controls to run it on the mac. However, the server itself is pretty straightforward - it's a java app that opens a socket and also takes input from the console. It seemed like it should be pretty easy to wrap that into an app that she could run by double-clicking, and that I could have some control over. So, that's what I did. And here it is, in case you want to play with it.

You'll need to get the minecraft_server.jar file yourself, since I don't think I can redistribute that. However, it's easy to find.

Enjoy, and fork!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark
Sun, 22 Apr 2012 18:54:00 -0700 Home networking woes http://geekboy.posterous.com/home-networking-woes http://geekboy.posterous.com/home-networking-woes

I've been a QWest CenturyLink DSL customer for ages. I started out with a Cisco 675 modem, then moved to a Cisco 678 when the DSL signaling technology for the line switched from CAP to DMT. The little Cisco box was a sweet DSL modem. However, earlier this year, I couldn't get more than 32kpbs downstream on the DSL line, despite having a steady connection at 7mpbs the night before. A tech came out and was able to get a solid 7 mpbs with his line diagnostic tool, and we tried a newer modem and also got 7 mpbs. So, my cisco got traded out for an ActionTec GT701, and I now get 7mpbs again.

Except when the DHCP server in the actiontec stalls. Then, I get bupkis. I've got a fairly complicated home network - a mix of windows, mac, ios and other stuff. All of it expects DHCP. If something already has a DHCP-assigned address, it works great, but newly-powered-on devices don't get an address and don't get to the net.

As it happens, the apple time capsule will also do DHCP. With a bit of configuration shuffling, I turned off the DHCP server on the ActionTec and turned on DHCP on the time capsule. The ActionTec still does NAT and routing, but isn't being asked to hand out addresses anymore. This seems to work well.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark
Wed, 29 Feb 2012 20:55:00 -0800 Vendor Kudos http://geekboy.posterous.com/vendor-kudos http://geekboy.posterous.com/vendor-kudos

I have to give a big shout-out to VMWare's booth staff for cloud solutions at the O'Reilly Strata Conference. 

They told an interesting story about their Cloud Foundry offering, so I went to sign up. After getting approved, I had the option to download a vmware image of a cloud foundry instance to run on my laptop to test stuff out. Great! But it's a gigabyte, and I'm on hotel wifi or conference wifi - one is expensive and slow, the other is slow and rude to other participants.

So, I went back to the VMWare booth to ask if they happened to have it on a flash drive or something. One of the booth staff (and I'm sorry I've forgotten your name) not only had it on her macbook and let me copy it over to mine, she also helped me configure it so that I could play with it. The configuration was straightforward and I could have figured it out on my own, but getting tech support on developer tools from a vendor's booth staff (at least, for a non-startup business) is unexpected and way cool.

Thanks, VMWare folks!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark
Sun, 26 Feb 2012 20:24:00 -0800 Troubles with sharing from Win7 to Mac OS/X http://geekboy.posterous.com/troubles-with-sharing-from-win7-to-mac-osx http://geekboy.posterous.com/troubles-with-sharing-from-win7-to-mac-osx

I wanted to back up a bunch of photos from my mac to my win7 machine, some of which were already there, so I shared a drive on the win7 machine, mounted it on the mac, and got ready to fire up rsync(1). After copying over a good chunk of the pictures, the rsync job started throwing io errors.

The mac console indicated the following error message:

kernel: smb_ntstatus_error_to_errno: Couldn't map ntstatus (0xc0000205) to errno returning EIO

At the same time, the Event Viewer on the Windows side showed a bunch of messages stating:

The server was unable to allocate from the system nonpaged pool because the server reached the configured limit for nonpaged pool allocations.

A little bit of googling turned up a posting that offered an answer. In short, there are some registry settings to be edited:

Set the following registry key to ’1′:

HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\MemoryManagement\LargeSystemCache

and set the following registry key to ’3′:

HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters\Size

So far, no problems!

These are actually old settings, that go back to the NT Workstation/NT Server days. For more info, this is interesting reading.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark
Thu, 16 Feb 2012 07:56:18 -0800 Mac App Store updates http://geekboy.posterous.com/mac-app-store-updates http://geekboy.posterous.com/mac-app-store-updates

The Mac App Store says I've got -4 updates. If I hit 'update', it goes to -5. Kinda neat!

Negative_updates

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark
Fri, 03 Feb 2012 19:13:00 -0800 Why is my win7 box slow? http://geekboy.posterous.com/why-is-my-win7-box-slow http://geekboy.posterous.com/why-is-my-win7-box-slow

On my win7 x64 ultimate box, there's what looks like a runaway process named WmiPrvSE.exe. It's eating up all of one core. Digging into the Event Viewer isn't very enlightening - you can turn on logging by checking View->Show Analytic and Debug Logs in the menu bar, and then going to Applications and Services Logs\Microsoft\Windows\WMI-Activity\Trace. Microsoft provides more detailed information about turning on this logging.

There's a lot of activity in there, well over 150 operations in the 5 seconds of the first page of the log. None of it appears to be particularly relevant.

However, a lot of the traffic to the log appears to originate from a specific and relatively low process id - which turns out to be svchost.exe. No joy there - svchost.exe could be doing anything, and this particular instance of svchost doesn't appear to be busy; its CPU utilization is 0%. But in the task manager, we can right-click on that instance of svchost.exe and say 'go to services' to see what services are running inside that task. Maybe that'll be helpful.

It's running all kinds of relatively low-level junk: Themes, Windows Management Instrumentation, Shell Hardware Detection, User Profile Service, Group Policy Client, Computer Browser, Task Scheduler, Server, IP Helper, Application Experience - many of which are important and can't be turned off.

After some trial and error in the Services app looking for stuff that I don't need, I turn off Windows Live Family Safety Service. After about 15 seconds, WmiPrvSE.exe settles down and my machine CPU usage goes to 0%. I turn in back on, and WmiPrvSE spikes again.

I don't know what Family Safety is doing, and I'm tempted to turn it off and try a different solution to keep my kids off the nastier parts of teh interwebz. Anyone have any advice?

 

 

 

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark
Thu, 05 Jan 2012 20:02:05 -0800 Playing with PostGIS http://geekboy.posterous.com/playing-with-postgis http://geekboy.posterous.com/playing-with-postgis

I’m working on some stuff for work that involves integrating spatial data (both vector and raster), census microdata, and census tables. Neat stuff. PostgresSQL and PostGIS look like they’ll be extremely useful but I’ve just gotten started learning. I also had the opportunity to contribute a minor bugfix to PostGIS 2.0. Hopefully it won’t be the last one.

For someone coming from a traditional SQL background, the geometry functions take some getting used to. I’ve spent more time in MySQL than anything else, but I tend to think in the Transact-SQL dialect used by Sybase and MS SQL Server, particularly for join syntax. It’s pleasant that PostgreSQL supports a similar join syntax!

I wrote up some simple queries, to play with the spatial functions and to see if they behave the way I expect. I offer them here with no guarantee of usefulness, just a hope that they might prove to be a good starting point for the next adventurer.

This is using PostGIS 2.0-SVN as checked out from the svn repository on Jan 5th, 2012, but none of these functions should be PostGIS 2.0 specific. Once I start playing with the raster stuff, that may no longer be true.

# PostGIS notes:
create table my_geom (id serial NOT NULL PRIMARY KEY, name varchar(20));

select AddGeometryColumn('public', 'my_geom', 'my_polygons', -1, 'POLYGON', 2);

insert into my_geom(name, my_polygons) values ('square1',
    ST_GeomFromText('POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))'));

insert into my_geom(name, my_polygons) values ('square2',
        ST_GeomFromText('POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))'));

# approximate an approximation of a circle of radius 1, centered at 1,1
insert into my_geom(name, my_polygons) values ('circle1', ST_Buffer(ST_GeomFromText('POINT(1 1)'), 1));

# what did we just insert?
select id, name, ST_AsText(my_polygons) from my_geom g1;

# and are they valid?
select name, ST_IsValid(my_polygons) from my_geom;

# how big are they?
select name, ST_Area(my_polygons), ST_Perimeter(my_polygons) from my_geom;

# do square1 and square2 intersect?
select ST_Intersects(g1.my_polygons, g2.my_polygons)
from my_geom g1, my_geom g2
where g1.name = 'square1' and g2.name = 'square2';

# What's the intersection between the two squares? (should be the same as square1)
select ST_AsText(ST_Intersection(g1.my_polygons, g2.my_polygons))
from my_geom g1, my_geom g2
where g1.name = 'square1' and g2.name = 'square2';

# Is it really the same as square1?
select ST_Equals(g1.my_polygons, ST_Intersection(g1.my_polygons, g2.my_polygons))
from my_geom g1, my_geom g2
where g1.name = 'square1' and g2.name = 'square2';

# What's the symmetric difference?
select ST_AsText(ST_SymDifference(g1.my_polygons, g2.my_polygons))
from my_geom g1, my_geom g2
where g1.name = 'square1' and g2.name = 'square2';

# What is the area of the difference (should be 3)
select ST_Area(ST_SymDifference(g1.my_polygons, g2.my_polygons))
from my_geom g1, my_geom g2
where g1.name = 'square1' and g2.name = 'square2';

# What is the area of the difference between square1 and the other two shapes?
select g2.name, ST_Area(ST_SymDifference(g1.my_polygons, g2.my_polygons))
from my_geom g1, my_geom g2
where g1.name = 'square1' and g2.name != 'square1';


drop table my_geom;

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark
Thu, 05 Jan 2012 19:22:41 -0800 Looks like I've officially graduated! http://geekboy.posterous.com/looks-like-ive-officially-graduated http://geekboy.posterous.com/looks-like-ive-officially-graduated

Got a happy email from the U of Mn Graduate School on Jan 4th. They said:

Dear Peter,
Congratulations! You have completed all requirements for your M S degree in Computer Science. Your degree award was awarded 12/30/2011.

Your official transcript will be updated, to show the award of the degree, 2-3 weeks following the award date. Official transcripts may be obtained from the One Stop office.  Your diploma will be mailed to you 4-6 weeks following the award date by the One Stop office.  If you need to change the address to which the diploma will be mailed, you may contact One Stop at 612-624-1111.

Note: Posting of your degree is contingent upon payment of all assessed University fees, fines and bills.  

Congratulations on your accomplishments at the University of Minnesota.  We wish you all the best with your new endeavors.

Thankfully, my student account shows a balance of $0.00. Yay!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark
Tue, 29 Nov 2011 12:40:00 -0800 I passed my final oral exam for my master's degree! http://geekboy.posterous.com/i-passed-my-final-oral-exam-for-my-masters-de http://geekboy.posterous.com/i-passed-my-final-oral-exam-for-my-masters-de

Hey - grad school? We need to talk. It's been a good run, this relationship, but I think we need to start seeing other people. Or at least, I do. It's not you, really, it's me. I'm through. I'm done. We had some good times, you and me, but now it's time to move on. Thanks. Really - I mean that. Let's stay in touch - you know where to find me.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark
Sun, 13 Nov 2011 07:43:00 -0800 Overheard at a party last night http://geekboy.posterous.com/overheard-at-a-party-last-night http://geekboy.posterous.com/overheard-at-a-party-last-night

Girl: Yeah, I got dumped a couple weeks ago...

Guy: So, what does your dad do?

Girl: He's a cop.

Guy: Well, that's the end of that conversation!

 

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark
Wed, 05 Oct 2011 18:25:00 -0700 Thanks, Steve. http://geekboy.posterous.com/thanks-steve http://geekboy.posterous.com/thanks-steve

In 1992, I was working for a small and ultimately unsuccessful NeXTStep ISV named Appsoft. NeXT had recently cancelled their beautiful black hardware and announced that the OS was being ported to the Intel 486 architecture. They set up a porting lab at NeXT HQ in Redwood City, and I spent a couple days at the porting lab getting the spreadsheet running on Intel.

On one of those days in the porting lab, Steve Jobs walked by, and I waved to him. He came in and asked what I was working on, and what I thought of the Intel stuff. I gave him a demo of the spreadsheet as best I could (the UI layer worked fine on Intel but the calc engine was horribly broken). He had some good ideas (including suggesting that we should explore junking the existing spreadsheet and seeing if Borland would license the Quattro Pro engine to us). Steve had some minor suggestions about the UI, and got me in touch with some of the compiler guys to help troubleshoot the calc engine issues. He was helpful and polite and put me at ease - lord knows I was intimidated at doing an impromptu demo for the guy. It wasn't a long meeting in any sense, and I never had the privilege of meeting him again for any length of time, but it certainly made an impression on me.

That memory is nearly twenty years old, but I remember it vividly. I'm grateful to have had the opportunity to meet one of my heroes.

Steve's been an inspiration to multiple generations of technologists, and the field is far better for it. His passion for getting the design right and intolerance for close-enough solutions is well-documented and is a source of personal inspiration. As I type this, I sit in my home office surrounded by products that he'd had a hand in developing - an iPhone and iPad, a Macbook Air, and a NeXTCube (Yes, I have one. Yes, it still boots. No, I'm never getting rid of it.) I've got other stuff in the office as well, but the gear that runs Mac OS/X and iOS - the stuff that blossomed from the NeXTStep environment - is what gets used. That ethos of close-enough isn't good enough is the reason why.

My career would be very different if he hadn't been around - frankly, I'd probably be a chemist rather than a software guy. I'll miss him. We'll not soon see his like again.

 

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark
Wed, 14 Sep 2011 18:19:00 -0700 Crazy Politics http://geekboy.posterous.com/crazy-politics http://geekboy.posterous.com/crazy-politics

Our own Michelle Bachmann recently said:

“I don't know how much God has to do to get the attention of the politicians. We've had an earthquake; we’ve had a hurricane. He said, 'Are you going to start listening to me here? Listen to the American people because the American people are roaring right now. They know government is on a morbid obesity diet and we've got to rein in the spending.' ”

Hmmm...

I note that, as I write this, both Texas - the home state of Rick Perry - and Minnesota - the home state of Michelle Bachmann - are on fire. Perry and Bachmann are the two presidential candidates most strongly associated with the extrovert, outrageous side of the Tea Party.

Both states are ON FIRE. What's the message from God in that, Michelle?

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark
Thu, 18 Aug 2011 11:14:00 -0700 IOS iPad iTunes error E800002D http://geekboy.posterous.com/ios-ipad-itunes-error-e800002d http://geekboy.posterous.com/ios-ipad-itunes-error-e800002d

id Software put their IOS video game, Rage HD, out for free today. I was happy to download it, but was unhappy when it wouldn't install on my first-gen iPad. iTunes responded with an error, with the unhelpful error code of 0xE800002D. Googling this turned up surprisingly little.

It turned out that the solution was to turn off Parental Controls on the iPad. I had things set to only allow apps rated 12 or younger to run for when my kids are playing with it, and that appeared to be preventing Rage from installing. Turning off parental controls allowed it to install and run.

YMMV.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark
Thu, 11 Aug 2011 14:12:00 -0700 Apple Boot Camp 4.0 and an old MacBook pro "Boot Camp x64 is unsupported on this computer model" http://geekboy.posterous.com/apple-boot-camp-40-and-an-old-macbook-pro-boo http://geekboy.posterous.com/apple-boot-camp-40-and-an-old-macbook-pro-boo

I'm working on getting Win7 set up on a middle-aged MacBook Pro, and when running the boot camp installer from within Win7, I got a surprising error stating "Boot Camp x64 is unsupported on this computer model". Which kind of sucks.

There are a variety of web pages and messages on apple forums stating how to get around this, most of which say go to the Drivers/Apple folder and then run the BootCampx64.msi file in compatibility mode. This evidently worked in BootCamp 3, but it doesn't in Bootcamp 4 which ships with Lion. The installer responds with an error saying that it needs to run with elevated privileges.

What worked for me was to run a command prompt window as administrator, and then run msiexec /i bootcampx64.msi from the command line.

Hope this works for you as well.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark
Sat, 11 Jun 2011 20:58:00 -0700 First sail of the season #Duluth http://geekboy.posterous.com/first-sail-of-the-season-duluth http://geekboy.posterous.com/first-sail-of-the-season-duluth

223a0f1f-88ae-4430-a23b-c1da524b177d

First sail of the season #Duluth

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark
Thu, 02 Jun 2011 15:49:56 -0700 Goodbye Florida? http://geekboy.posterous.com/goodbye-florida http://geekboy.posterous.com/goodbye-florida

Looks like Google Maps had a hiccup... or maybe the post-rapture apocalypse got an early start.

 

Goodbye_florida

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark
Mon, 16 May 2011 07:03:37 -0700 Walking down the middle of Washington ave. No traffic. Feels apocalyptic, in a good way. http://geekboy.posterous.com/walking-down-the-middle-of-washington-ave-no http://geekboy.posterous.com/walking-down-the-middle-of-washington-ave-no

C585da4e-205b-4920-baf4-f5e65af235b1

Walking down the middle of Washington ave. No traffic. Feels apocalyptic, in a good way.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark
Thu, 12 May 2011 20:26:00 -0700 Building an External DSL in Scala http://geekboy.posterous.com/building-an-external-dsl-in-scala http://geekboy.posterous.com/building-an-external-dsl-in-scala

I’ve been working on a project to synthesize microdata for the 1890 census, using microdata from IPUMS USA. The original data was lost in a fire, but the aggregate statistics still survive. For purposes of testing, however, I’m working against the 1900 aggregate data. I’ll no doubt write more about the project itself later, but for now I want to talk about a DSL I built in Scala (just noticed that scala 2.9.0 is out, I’ll need to upgrade), and it was quite easy. I’m not a compilers guy, although I do know my way around BNF.

I wanted to be able to specify the characteristics by which a given person record fit a given aggregate statistic – this can be considered to be being “in universe” for the aggregate statistic. For instance, if the statistic in question is ‘total population of persons born in France’, a person would be “in universe” if they had the value 421 for the BPL variable. My first attempt at this had a function bound to each aggregate statistic that would take in a person record and return true or false – it’s a functional language, right, so a function would seem the proper hammer for this particular nail, and it would look something like this:

Universe check for BPL

(personRec:String) => {
    val bplVar = variables("BPL") // variables is a Map[String, Variable]
    val bpl = bplVar.asNumber(personRec) // returns the numeric code for the BPL variable for this person
    (421 == bpl)
}

This works, but it’s crufty and a lot to write. It could be more concise, but I want to introduce the concepts I’m working on. But wait, it gets worse. Consider a cell from table 56 – total illiterate white population age 10 or over, native-born with foreign-born parents. For the purposes of ‘foreign-born’, having either the mother or father be foreign-born counts. This has a number of different variables in play — RACE, LIT, AGE, and NATIVITY, and we need to consider all of them to determine if a given person record is “in universe” for this variable. My code for this looks like:

Universe check for total illiterate white population age 10 or over, native-born with foreign-born parents

(personRec:String) => {
    val raceVar = variables("RACE")
    val literacyVar = variables("LIT")
    val ageVar = variables("AGE")
    val nativityVar = variables("NATIVITY")
    val nativity = nativityVar.asNumber(personRec)
    // is this person part of "illiterate white population age 10 or over, native-born with foreign-born parents"
    ((literacyVar.asNumber(personRec) < 4) &&
        (raceVar.asNumber(personRec) == 1) &&
        (ageVar.asNumber(personRec) >= 10) &&
        ((nativity >= 2) && (nativity <=4)))
}

Ugh.

For the first bit of code, the only salient part is “return true when BPL for this person equals 421”, so what I want to write BPL = 421.

For the second, what I want to write is LIT < 4 and RACE = 1 and AGE >= 10 and (nativity >= 2 and nativity <= 4). Alternatively, for those with a SQL-ish bent, you might instead want to write LIT < 4 and RACE = 1 and AGE >= 10 and nativity in [2, 3, 4]. Either of those collapse 9 lines of code down into one, which is a big improvement in readability.

So – how much Scala does it take to implement that expression language? Not a lot, as it turns out. Scala has parser combinators built in to the standard library, and they’re fun to play with. But it can be hard to find good code examples.

Here’s the grammar for my little universe language:

Universe Check BNF Grammar

expr ::= term {"and" term | "or" term}
term ::= varname operator value | varname "in" intlist | "(" expr ")"
operator ::= "<" | "<=" | "=" | ">=" | ">"| "!="
value ::= wholeNumber
intlist ::= "[" wholeNumber {"," wholeNumber} "]"

And here’s the class definition of a parser for it:

Universe.scala

import scala.util.parsing.combinator._
/*
  Class to implement a DSL for parsing universe statements. A universe statement should look like
  sex = 1 and age > 10
  or
  (relate in [10, 20, 30]) and age > 30
*/

class Universe extends JavaTokenParsers {
    def expr: Parser[Expression] = term~rep("and"~term | "or"~term) ^^ {
      case t ~ l => l.foldLeft(t) {
        case (x, "and" ~ term) => new Conjunction(x, term)
        case (x, "or" ~ term) => new Disjunction(x, term)
      }
    }

   def term: Parser[Expression] = cond | incond | "(" ~> expr <~ ")"
   def incond: Parser[InCondition] = varname ~ "in" ~ intlist ^^ { case name~op~values => new InCondition(name, values) }
   def cond: Parser[Condition] = varname~operator~value ^^ { case name~op~num => new Condition(name, op, num) } 
   def varname: Parser[String] = ident ^^ { case ident => ident }
   def operator: Parser[String] = "<" | "<=" | "=" | ">=" | ">" | "!="
   def intlist: Parser[List[Long]] = "[" ~> repsep(value, ",") <~ "]" ^^ (List() ++ _)
   def value: Parser[Long] = wholeNumber ^^ { _.toLong }
}

Each def in the class constructs a parser that returns an object of the type given in the type parameter – so Parser[String] returns a String. A Parser[Condition] returns a Condition instance.

Here’s the class definitions for the data-bearing objects that are returned by my parser:

Expression.scala

abstract class Expression
case class Condition(val variableName:String, val operator:String, val value:Long) extends Expression
case class InCondition(val variableName:String, val values:List[Long]) extends Expression
case class Conjunction(val left:Expression, val right:Expression) extends Expression
case class Disjunction(val left:Expression, val right:Expression) extends Expression

Yes, that’s all there is. Lots less needless verbosity than Java. Of course, all this stuff still just gives us a parse tree. We still need to do something with the parse tree – we need compute whether or not a data line is in universe. Turns out that’s less than a page of code as well:

UniverseChecker.scala

object UniverseChecker {
  def conditionEvaluate(vars:Map[String, Variable], c:Condition, line:String) : Boolean = {
    val varToEvaluate = vars(c.variableName)
    val valueForVar = varToEvaluate.asNumber(line)
    val valueForCondition = c.value

    c match {
      case Condition(_, "=", _) => (valueForVar == valueForCondition)
      case Condition(_, ">", _) => (valueForVar > valueForCondition)
      case Condition(_, "<", _) => (valueForVar < valueForCondition)
      case Condition(_, ">=", _) => (valueForVar >= valueForCondition)
      case Condition(_, "<=", _) => (valueForVar <= valueForCondition)
      case Condition(_, "!=", _) => (valueForVar != valueForCondition)
      case _ => false
    }
  }

  def inConditionEvaluate(vars:Map[String, Variable], i:InCondition, line:String) : Boolean = {
    val varToEvaluate = vars(i.variableName)
    val valueForVar = varToEvaluate.asNumber(line)
    val valuesForCondition = i.values
   (valuesForCondition contains valueForVar)
  }

  def conjunctionEvaluate(vars:Map[String, Variable], c:Conjunction, line:String) : Boolean = {
    val leftSide = evaluate(vars, c.left, line)

    // do short-circuit evalation. If the left-hand-side of the conjunction is false, then the expression is false.
    // if it's not, then the value of the expression is the value of the right-hand-side.
    if (leftSide) {
      evaluate(vars, c.right, line)
    } else {
      false
    }
  }

  def disjunctionEvaluate(vars:Map[String, Variable], d:Disjunction, line:String) : Boolean = {
    val leftSide = evaluate(vars, d.left, line)

    // do short-circuit evalation. If the left-hand-side of the conjunction is true, then the expression is true.
    // if it's not, then the value of the expression is the value of the right-hand-side.
    if (!leftSide) {
      evaluate(vars, d.right, line)
    } else {
      true
    }
  }

  def evaluate(vars:Map[String, Variable], e:Expression, line:String) : Boolean = {
    e match {
      case c:Condition => conditionEvaluate(vars, c, line)
      case i:InCondition => inConditionEvaluate(vars, i, line)
      case c:Conjunction => conjunctionEvaluate(vars, c, line)
      case d:Disjunction => disjunctionEvaluate(vars, d, line)
      case _ => false
    }
  }
}

Well, at least it’s less than a page of code on my monitor. Can’t vouch for that on yours. Also, of course, we can’t know that the code works without some test cases. So here are the tests:

UniverseGrammarSpec.scala

import scala.io.Source
import org.scalatest.FlatSpec
import org.scalatest.matchers.ShouldMatchers

class UniverseGrammarSpec extends FlatSpec with ShouldMatchers {
  "My UniverseGrammar" should "parse simple universe statements" in {
    val sexStatement = "sex = 1"
    val ageStatement = "age > 10"
    val parser = new Universe()
    parser.parseAll(parser.expr, sexStatement) match {
      case parser.Success(r,_) => r.toString should be === "Condition(sex,=,1)"
      case x => fail(x.toString)
    }

    parser.parseAll(parser.expr, ageStatement) match {
      case parser.Success(r,_) => r.toString should be === "Condition(age,>,10)"
      case x => fail(x.toString)
    }
  }

  it should "parse 'in' statements" in {
    val inStmt = "relate in [1, 2, 3, 4]"
    val parser = new Universe()
    val inAST = parser.parseAll(parser.expr, inStmt)
    inAST match {
        case parser.Success(r,_) => r.toString should be === "InCondition(relate,List(1, 2, 3, 4))"
        case x => fail(x.toString)
    }
  }

  it should "fail to parse bad operators" in {
     val inStmt = "relate => 4"
     val parser = new Universe()
     val inAST = parser.parseAll(parser.expr, inStmt)
     inAST match {
         case parser.Success(r,_) => fail("Unexpected Success with" + r.toString)
         case x => true
     }
   }

  it should "parse expressions separated by 'and' or 'or'" in {
    val conjunctiveStmt = "age = 1 and sex > 10 and somethingElse in [1, 2, 3]"
    val parser = new Universe()
    val ast = parser.parseAll(parser.expr, conjunctiveStmt)
    ast match {
      case parser.Success(r,_) => r.toString should be === "Conjunction(Conjunction(Condition(age,=,1),Condition(sex,>,10)),InCondition(somethingElse,List(1, 2, 3)))"
      case x => fail(x.toString)
    }
  }

  it should "parse expressions nested with parens" in {
    val conjunctiveStmt = "age = 1 and (sex > 10)"
    val parser = new Universe()
    val ast = parser.parseAll(parser.expr, conjunctiveStmt)
    ast match {
      case parser.Success(r,_) => r.toString should be === "Conjunction(Condition(age,=,1),Condition(sex,>,10))"
      case x => fail(x.toString)
    }
  }

  it should "parse expressions with parens and disjunctions" in {
    val bigStmt = "age = 1 or (sex >= 10 and relate in [10, 20])"
    val parser = new Universe()
    val ast = parser.parseAll(parser.expr, bigStmt)
    ast match {
      case parser.Success(r,_) => r.toString should be === "Disjunction(Condition(age,=,1),Conjunction(Condition(sex,>=,10),InCondition(relate,List(10, 20))))"
      case x => fail(x.toString)
    }
  }
}

and

UniverseCheckerSpec.scala

class UniverseCheckerSpec extends FlatSpec with ShouldMatchers {
  val varname = "COL1"
  val variables = Map(varname -> new Variable(varname, 0, 1))
  val dataline = "12345"
  val anotherDataline = "92345"
  val aThirdDataline = "72345"

  "A UniverseChecker" should "return true for equals when it really is" in {
    val expr = new Condition(varname, "=", 1)
    UniverseChecker.evaluate(variables, expr, dataline) should be (true)
  }

  it should "return false for equals when it isn't" in {
    val expr = new Condition(varname, "=", 5)
    UniverseChecker.evaluate(variables, expr, dataline) should be (false)
  }

  it should "return correct values for other operations, assuming the above two tests work." in {
    UniverseChecker.evaluate(variables, Condition(varname, ">", 5), dataline) should be (false)
    UniverseChecker.evaluate(variables, Condition(varname, "<=", 5), dataline) should be (true)
    UniverseChecker.evaluate(variables, Condition(varname, "!=", 9), dataline) should be (true)
    UniverseChecker.evaluate(variables, Condition(varname, ">=", 0), dataline) should be (true)
    UniverseChecker.evaluate(variables, Condition(varname, ">=", 2), dataline) should be (false)
  }

  it should "return true for inexprs when appropriate" in {
    val expr = new InCondition(varname, List(1, 2, 3))
    UniverseChecker.evaluate(variables, expr, dataline) should be (true)
  }

  it should "return false for inexprs when appropriate" in {
    val expr = new InCondition(varname, List(100, 200, 300))
    UniverseChecker.evaluate(variables, expr, dataline) should be (false)
  }

  it should "do the right thing for conjunctions and disjunctions" in {
    val expr = new Conjunction(new Condition(varname, "<=", 5), new Condition(varname, "=", 1))
    UniverseChecker.evaluate(variables, expr, dataline) should be (true)
    UniverseChecker.evaluate(variables, expr, anotherDataline) should be (false)

    val dexpr = new Disjunction(new Condition(varname, ">=", 9), new Condition(varname, "=", 1))
    UniverseChecker.evaluate(variables, dexpr, dataline) should be (true)
    UniverseChecker.evaluate(variables, dexpr, anotherDataline) should be (true)
    UniverseChecker.evaluate(variables, dexpr, aThirdDataline) should be (false)
  }
}

Lastly, you can’t do anything with that without knowing what I’m calling a variable. Here’s a slightly simplified version of what I’m using in my project:

Variable.scala

/*
  Simple definition of a variable - it has a name/mnemonic, a description, a start location in the line, and a width.
 */

class Variable(val mnemonic: String, val desc: String, val start: Int, val width: Int) extends Ordered[Variable] {
  def this(n:String, s:Int, w:Int) = this(n, "", s, w)

  def compare(that:Variable) = {
    mnemonic.compare(that.mnemonic)
  }

  // given a data line, get the value for this variable.
  def apply(line: String) : String = {
    line.substring(start, start + width).trim
  }

  // return the value of this variable for the given line as a Long.
  def asNumber(data:String):Long = {
    val substr = this(data)
    if (substr.length > 0) {
      substr.toLong
    } else {
      0
    }
  }
}

And that is the simple-but-nontrivial example of parsing in scala that I’d wished I could find, back when I started working on this last night. The closest I came was Ted Neward’s tutorial on IBM developerWorks, building a calculator tutorial. I recommend reading all three parts of the Building a calculator tutorial if you’re interested in writing a DSL in scala; Neward goes into far more detail about what’s happening that I’ve done.

O'Reilly have also made their excellent Programming Scala book available, and there’s a chapter specifically on Domain-Specific Languages in Scala that is also well worth reading. In general, I’ve got much value from the book Programming in Scala, and the first edition (based on Scala 2.7) is available on-line for free. However, that book didn’t take me as far as I needed to go with DSLs, and the on-line class docs weren’t enough either. Neward’s tutorial and the O'Reilly book got me over the hump.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark
Sun, 17 Apr 2011 11:41:18 -0700 Yay - boba smile! http://geekboy.posterous.com/yay-boba-smile http://geekboy.posterous.com/yay-boba-smile

025285ed-c7cd-458e-8636-b4737f0088aa

Yay - boba smile!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark
Sun, 17 Apr 2011 11:40:38 -0700 Checking out the new bubble tea shop on grand avenue http://geekboy.posterous.com/checking-out-the-new-bubble-tea-shop-on-grand http://geekboy.posterous.com/checking-out-the-new-bubble-tea-shop-on-grand

De96185c-b916-4fde-ac8e-0e685889d482

Checking out the new bubble tea shop on grand avenue

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/512677/Photo_5.jpg http://posterous.com/users/5ebMAStQS0vL Peter Clark geekboy Peter Clark