-
Notifications
You must be signed in to change notification settings - Fork 3
Introducing Termpose! (To Scala)
Happy to announce that Termpose is at a stage where I can recommend it to people as a fairly joyous thing to use, so long as you're a Scala dev. I have ported the parser to like 5 other languages, but those APIs only provide you with String => Term functionality. In this article, I'll be explaining the Iterator[Char]|File|String|Term => T functionality, that takes a term(or a term source) checks that it conforms to the specified representation of a certain type, then gives you a result of that type. ( If you're interested in helping to flesh these APIs out onto any of those other platforms, let's chat https://gitter.im/makoConstruct/termpose )
###What actually is Termpose Termpose is an extremely flexible markup language. Termpose takes the flexibility and regular semantics of S-Expressions and drops most of the parentheses, which also makes it an extremely pretty, minimal markup language. If you ever felt like S-Expressions had too many parens, to the point that they were noise, it turns out you were right to. Where proper indentation is present, parens are frequently redundant.
###Begin Tutorial A simple example:
Termpose.parse("""
production_spire_A
drone
name mary shmidt
projected_expiry 2032
caste accounting
drone
name harry jordan
projected_expiry 2067
caste sales
drone name(clarice wilkins) projected_expiry:2080 caste:advertising
(drone (name "jarae" "hillfolk") (projected_expiry "2075") (caste "web development"))
""").toString
>Success((production_spire_A (drone (name mary shmidt) (projected_expiry 2032) (caste accounting)) (drone (name harry jordan) (projected_expiry 2067) (caste sales)) (drone (name clarice wilkins) (projected_expiry 2080) (caste marketing)) (drone (name jarae hillfolk) (projected_expiry 2075) (caste "web development"))))
The Termpose.parse method returns a Try[Term]. The term data structure is quite manageable. Each Term is either a Seqs or Stri, Seqs contains a nested sequence of terms, and Stri contain just a string. However, a lot of the time you're going to wish you could just tell Termpose to type check a structure of Terms and give you a strongly typed data structure of the stated type, so that you don't have to bother leafing through primitive S-Expression deserializations yourself and check for super banal requirements by hand.
Well ya can. Let's try an example where we type termpose into a Map[Int, Seq[Boolean]]
for some reason.
file.terms:
11 (true)
2 (false)
3 (true false)
5
4 ()
hammersmith (eighty eighty)
import Termpose.{TypingSuccess, TypingFailure}
{
import Termpose.dsl._
map(int, seq(bool))
}.checkFile("file.terms") match {
case TypingSuccess(misbs:Map[Int,Seq[Boolean]])=> println(misbs(11)(0)) //prints `true` :DD
case tf:TypingFailure=> //or at least, it would have printed `true`, if it wern't for lines 4 and 6, which are of course type errors according to the typer we composed
println(tf.toString) //prints`
//line:4 column:0. this needs to be a (int list(bool)) pair, but it's a leaf term
//line:6 column:0. this needs to be a int
//line:6 column:12. this needs to be a bool
//line:6 column:18. this needs to be a bool
}
As you can see, the typer tries to report every error it can. Rather than just hurling an inscrutable exception on arriving at the first one, which is assuredly the way a programmer would implement their type checking, in a shameful fit of laziness and despair, if they didn't have Typer combinators.
There are also combinators that ignore errors, or transform trees with errors into options, for instance
import Termpose.dsl._
seqAgreeable(int).check("1 2 dogs 3 4").get == Seq(1,2,3,4)
import Termpose.dsl._
seq(optional(property("a", string))).check("(a:hogs a:hags b:hors)").get ==
Seq(Some("hogs"), Some("hags"), None)
The dsl I have here is already surprising me with its flexibility, however, there's a ways to go yet. Future plans include:
- Macros for generating Typers for user defined classes
- Typers for combining results into tuples or passing them through a given lambda. Also seems like it'll require macros to do properly(effectively requires variadic type parameters).
- Making Typers bidirectional. It seems that in most cases a Typer should be able to translate T => Term just as easily as it reads from Term => TyperResult[T]
Till then, Sincerely, Mako