This blog thing is finally picking up! I would consider this to be one of my more polished projects. There are still some warts on it that I don't particularly like, but I've done my best to make it as clean and modular as possible. This project was inspired by another similar project that you can find here so if you prefer common lisp to scheme go check it out. The idea is pretty much the same, but (I think) my version is cleaner and more modular. My version is also written in guile rather than common lisp.
The idea here is simple. You write whatever sort of text file as you
normally would, except scheme expressions following a
be evaluated and printed. If you want to write a '
~', just write
~~". It's that simple. Lets jump straight into an example.
One of the most useful uses for a macro system is to add features to a language to fit it to your uses better. As an example, lets fix one of the things that annoys me most about Java, import statements. They always end up being verbose and repetitive.
import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.awt.Color;
Look at that. Yes, the repetition is kind of mesmerizing; it does look fairly tidy once
you're finished typing it all out. I write enough as it is though. I don't want to have
to type out
java.util.* half a dozen times every time I make a new file. Let's
clean this up a bit. I think it should look more like this:
~(java-import '((java (util HashMap HashSet Map Random Set UUID (concurrent ExecutorService Executors Callable Future)) (awt Color))))
Much better! My eye's were starting to get sore looking at all those jagged semi-colons! So, How do we make this happen? It's as simple as writing a function that takes a tree of symbols and prints appropriate import statements:
(define (java-import specification) (letrec ((recurse ;;future Russell thinks he should have used a named loop here (lambda (spec-string spec-list) (for-each (lambda (spec) (if (list? spec) (recurse (string-append spec-string (symbol->string (car spec)) ".") (cdr spec)) (begin (display (string-append "import " spec-string (symbol->string spec) ";")) (newline)))) spec-list)))) (recurse "" specification)))
How easy was that!!? You can use this in a few ways. You can make a macro file
and include it in your project loading it as you would any scheme code. You can
put it in your
lipsrc.scm file in
you can do what I did and make a module and put it in
lips will look here by default when using a standard configuration.
git clone https://github.com/rbryan/guile-lips cd guile-lips ./install.sh #If you don't read this first you're a fool. lips < test.txt
Copyright 2016 Russell Bryan