diff --git a/examples/dns.c b/examples/dns.c
index 027d675c5c5e80f2437882fa2b9adefbce70648d..9cf56b9fd8a0d1237c114079543a9b5704c862d3 100644
--- a/examples/dns.c
+++ b/examples/dns.c
@@ -1,16 +1,48 @@
 #include "../hammer.h"
 
 bool is_zero(parse_result_t *p) {
+  if (TT_UINT != p->ast->token_type)
+    return 0;
   return (0 == p->ast->uint);
 }
 
+/**
+ * A label can't be more than 63 characters.
+ */
+bool validate_label(parse_result_t *p) {
+  if (TT_SEQ != p->ast->token_type)
+    return 0;
+  return (64 > p->ast->seq->used);
+}
+
+/**
+ * Every DNS message should have QDCOUNT entries in the question
+ * section, and ANCOUNT+NSCOUNT+ARCOUNT resource records.
+ *
+ */
 bool validate_dns(parse_result_t *p) {
-  
+  if (TT_SEQ != p->ast->token_type)
+    return 0;
+  // The header holds the counts as its last 4 elements.
+  parsed_token_t *header = p->ast->seq->elements[0];
+  size_t qd = header->seq->elements[8];
+  size_t an = header->seq->elements[9];
+  size_t ns = header->seq->elements[10];
+  size_t ar = header->seq->elements[11];
+  parsed_token_t *questions = p->ast->seq->elements[1];
+  if (questions->seq->used != qd)
+    return false;
+  parsed_token_t *rrs = p->ast->seq->elements[2];
+  if (an+ns+ar != rrs->seq->used)
+    return false;
 }
 
-int main(int argc, char **argv) {
+parser_t init_parser() {
+  static parser_t *dns_message = NULL;
+  if (dns_message)
+    return dns_message;
 
-  const parser_t dns_header = sequence(bits(16, false), // ID
+  const parser_t *dns_header = sequence(bits(16, false), // ID
 				       bits(1, false),  // QR
 				       bits(4, false),  // opcode
 				       bits(1, false),  // AA
@@ -42,11 +74,12 @@ int main(int argc, char **argv) {
 					 ch('-'),
 					 NULL));
 
-  const parser_t *label = sequence(letter,
-				   optional(sequence(optional(ldh_str),
-						     let_dig,
-						     NULL)),
-				   NULL);
+  const parser_t *label = attr_bool(sequence(letter,
+					     optional(sequence(optional(ldh_str),
+							       let_dig,
+							       NULL)),
+					     NULL),
+				    validate_label);
 
   /**
    * You could write it like this ...
@@ -76,10 +109,12 @@ int main(int argc, char **argv) {
 				    NULL);
 
 
-  const parser_t *dns_message = attr_bool(sequence(dns_header,
-						   dns_question,
-						   many(dns_rr),
-						   end_p(),
-						   NULL),
-					  validate_dns);
+  dns_message = attr_bool(sequence(dns_header,
+				   many(dns_question),
+				   many(dns_rr),
+				   end_p(),
+				   NULL),
+			  validate_dns);
+
+  return dns_message;
 }
diff --git a/src/hammer.c b/src/hammer.c
index 474298537709224edc8ffa65dd7e9a8c462de1fa..e0b4c2dd67884565d2b910b622b856a53e2be4d7 100644
--- a/src/hammer.c
+++ b/src/hammer.c
@@ -311,11 +311,6 @@ const parser_t* ch(const uint8_t c) {
   return (const parser_t*)ret;
 }
 
-typedef struct {
-  uint8_t lower;
-  uint8_t upper;
-} range_t;
-
 static parse_result_t* parse_whitespace(void* env, parse_state_t *state) {
   char c;
   input_stream_t bak;
@@ -374,7 +369,7 @@ static parse_result_t* parse_charset(void *env, parse_state_t *state) {
     return NULL;
 }
 
-const parser_t* range(const uint8_t lower, const uint8_t upper) {
+const parser_t* ch_range(const uint8_t lower, const uint8_t upper) {
   parser_t *ret = g_new(parser_t, 1);
   charset cs = new_charset();
   for (int i = 0; i < 256; i++)
@@ -383,6 +378,27 @@ const parser_t* range(const uint8_t lower, const uint8_t upper) {
   return (const parser_t*)ret;
 }
 
+typedef struct {
+  int64_t lower;
+  int64_t upper;
+} range_t;
+
+const parser_t* int_range(const int64_t lower, const int64_t upper) {
+  struct bits_env env = p->env;
+  // p must be an integer parser, which means it's using parse_bits;
+  // if it's a uint parser, it can't be uint64
+  assert_message(p->fn == parse_bits, "int_range requires an integer parser"); 
+  assert_message(!(env->signed) ? (env->length < 64) : true, "int_range can't use a uint64 parser");
+
+  range_t *env = g_new(range_t, 1);
+  env->lower = lower;
+  env->upper = upper;
+  parser_t *ret = g_new(parser_t, 1);
+  ret->fn = parse_int_range;
+  ret->env = (void*)env;
+  return ret;
+}
+
 const parser_t* not_in(const uint8_t *options, int count) {
   parser_t *ret = g_new(parser_t, 1);
   charset cs = new_charset();
@@ -829,7 +845,7 @@ typedef struct {
 static parse_result_t* parse_attr_bool(void *env, parse_state_t *state) {
   attr_bool_t *a = (attr_bool_t*)env;
   parse_result_t *res = do_parse(a->p, state);
-  if (res) {
+  if (res && res->ast) {
     if (a->pred(res))
       return res;
     else
diff --git a/src/hammer.h b/src/hammer.h
index a0b93e569bb85a394e248fba9bcf3a939afe9607..c59cdbc684279195ed319c3c879cdd13473c72d2 100644
--- a/src/hammer.h
+++ b/src/hammer.h
@@ -129,7 +129,14 @@ const parser_t* ch(const uint8_t c);
  * 
  * Result token type: TT_UINT
  */
-const parser_t* range(const uint8_t lower, const uint8_t upper);
+const parser_t* ch_range(const uint8_t lower, const uint8_t upper);
+
+/**
+ * Given an integer parser, p, and two integer bounds, lower and upper,
+ * returns a parser that parses an integral value within the range 
+ * [lower, upper] (inclusive).
+ */
+const parser_t* int_range(const parser_t *p, const int64_t lower, const int64_t upper);
 
 /**
  * Returns a parser that parses the specified number of bits. sign == 
@@ -359,8 +366,13 @@ const parser_t* length_value(const parser_t* length, const parser_t* value);
  * This parser attaches a predicate function, which returns true or 
  * false, to a parser. The function is evaluated over the parser's 
  * result. 
+ *
  * The parse only succeeds if the attribute function returns true. 
  *
+ * attr_bool will check whether p's result exists and whether p's 
+ * result AST exists; you do not need to check for this in your 
+ * predicate function.
+ * 
  * Result token type: p's result type if pred succeeded, NULL otherwise.
  */
 const parser_t* attr_bool(const parser_t* p, predicate_t pred);