Browse Source

Pass all encoder tests:

* Count newlines in whitespace.
* Always return parse errors.
* Fix lang tag length bug.
* Remove last NULL parser call.
Stefano Cossu 2 years ago
parent
commit
f1e7c45e36
12 changed files with 129 additions and 95 deletions
  1. 1 1
      include/codec.h
  2. 1 1
      src/codec.c
  3. 1 1
      src/codec/codec_nt.c
  4. 1 1
      src/codec/codec_ttl.c
  5. 21 28
      src/codec/grammar_ttl.y
  6. 3 2
      src/codec/lexer_nt.re
  7. 37 31
      src/codec/lexer_ttl.re
  8. 0 1
      src/graph.c
  9. 1 1
      src/term.c
  10. 11 8
      test/test.h
  11. 26 8
      test/test_codec_nt.c
  12. 26 12
      test/test_codec_ttl.c

+ 1 - 1
include/codec.h

@@ -359,7 +359,7 @@ LSUP_pred_obj_list_add (LSUP_PredObjList *pol, LSUP_Term *p, LSUP_Term **o);
  *
  * @param[in] po Predicate-object list.
  *
- * @return Number of triples added on success; <0 (LSUP_*_ERR) on error.
+ * @return Number of triples parsed on success, or <0 (LSUP_*_ERR) on error.
  */
 size_t
 LSUP_spo_list_add_triples (

+ 1 - 1
src/codec.c

@@ -166,7 +166,7 @@ LSUP_spo_list_add_triples (
             spo->o = po->o[i][j];
 
             LSUP_rc rc = LSUP_graph_add_iter (it, spo);
-            if (rc == LSUP_OK) ct++;
+            if (rc >= 0) ct++;
             PRCCK (rc);
         }
     }

+ 1 - 1
src/codec/codec_nt.c

@@ -108,7 +108,7 @@ term_to_nt (const LSUP_Term *term, const LSUP_NSMap *nsm, char **out_p)
 
         default:
             out = NULL;
-            rc = LSUP_VALUE_ERR;
+            rc = LSUP_PARSE_ERR;
     }
 
     *out_p = out;

+ 1 - 1
src/codec/codec_ttl.c

@@ -107,7 +107,7 @@ term_to_ttl (const LSUP_Term *term, const LSUP_NSMap *nsm, char **out_p)
 
         default:
             out = NULL;
-            rc = LSUP_VALUE_ERR;
+            rc = LSUP_PARSE_ERR;
     }
 
     *out_p = out;

+ 21 - 28
src/codec/grammar_ttl.y

@@ -5,20 +5,11 @@
  * The `lemon' parser generator executable must be in your PATH:
  * https://sqlite.org/src/doc/trunk/doc/lemon.html
  *
- * To generate the parser, run: `make parsers'
- *
  * TTL EBNF: https://www.w3.org/TeamSubmission/turtle/#sec-grammar-grammar
  */
 
 #include "codec.h"
 
-/*
- * Disable all error recovery processing in the parser push-down
- * automaton. From SQLite grammar file parse.y
- */
-// FIXME This throws a compile error.
-//#define YYNOERRORRECOVERY 1
-
 }
 
 %name TTLParse
@@ -34,17 +25,11 @@
 }
 
 %syntax_error {
-    log_warn ("Syntax error. Attempting recovery.");
+    // Fail immediately on first error.
+    yy_parse_failed (yypParser);
 }
 
-%stack_size CHUNK_SIZE
-
-/*
-%syntax_error {
-    if (TOKEN[0]) log_error ("near \"%T\": syntax error", &TOKEN);
-    else log_error ("incomplete input");
-}
-*/
+//%stack_size 1024
 
 %token_prefix "T_"
 %token_type { char * }
@@ -83,7 +68,7 @@ statement   ::= base .
 statement   ::= triples .
 statement   ::= WS .
 
-prefixID    ::= PREFIX(P) WS IRIREF(N) ows PERIOD . {
+prefixID    ::= PREFIX(P) WS IRIREF(N) PERIOD . {
                 LSUP_nsmap_add (state->nsm, P, N);
             }
 
@@ -91,7 +76,7 @@ base        ::= BASE WS IRIREF(D) PERIOD . {
                 state->base = LSUP_iriref_new (D, NULL);
             }
 
-triples 	::= subject(S) WS predObjList(L) PERIOD . {
+triples 	::= subject(S) ows predObjList(L) PERIOD . {
                 size_t ct = LSUP_spo_list_add_triples (state->it, S, L);
                 state->ct += ct;
                 state->rc = LSUP_OK;
@@ -99,7 +84,7 @@ triples 	::= subject(S) WS predObjList(L) PERIOD . {
                 LSUP_term_free (S);
                 LSUP_pred_obj_list_free (L);
             }
-triples 	::= subject(S) WS predObjList(L) SEMICOLON PERIOD . [PERIOD] {
+triples 	::= subject(S) ows predObjList(L) SEMICOLON PERIOD . [PERIOD] {
                 size_t ct = LSUP_spo_list_add_triples (state->it, S, L);
                 state->ct += ct;
                 state->rc = LSUP_OK;
@@ -107,17 +92,14 @@ triples 	::= subject(S) WS predObjList(L) SEMICOLON PERIOD . [PERIOD] {
                 LSUP_term_free (S);
                 LSUP_pred_obj_list_free (L);
             }
-triples     ::= subject WS error EOF . {
-                log_warn ("Error symbol popped.");
-            }
 
 %type predObjList       { LSUP_PredObjList * }
 %destructor predObjList { LSUP_pred_obj_list_free ($$); }
-predObjList(A) ::= predicate(P) WS objectList(O) . [SEMICOLON] {
+predObjList(A) ::= predicate(P) ows objectList(O) . [SEMICOLON] {
                 A = LSUP_pred_obj_list_new();
                 LSUP_pred_obj_list_add (A, P, O);
             }
-predObjList(A) ::= predObjList(L) SEMICOLON predicate(P) WS objectList(O) . {
+predObjList(A) ::= predObjList(L) SEMICOLON predicate(P) ows objectList(O) . {
                 LSUP_pred_obj_list_add (L, P, O);
                 A = L;
             }
@@ -148,12 +130,15 @@ object 	    ::= literal .
 %type literal { LSUP_Term * }
 literal(A)  ::= STRING(D) . {
                 A = LSUP_term_new (LSUP_TERM_LITERAL, D, NULL);
+                log_trace ("Created plain literal: \"%s\"", A->data);
             }
 literal(A)  ::= STRING(D) LANGTAG(L) . {
                 A = LSUP_term_new (LSUP_TERM_LT_LITERAL, D, L);
+                log_trace ("Created LT-literal: \"%s\"@%s", A->data, A->lang);
             }
 literal(A)  ::= STRING(D) DTYPE_MARKER resource(M) . {
                 A = LSUP_term_new (LSUP_TERM_LITERAL, D, M);
+                log_trace ("Created DT-literal: \"%s\"^^%s", A->data, A->datatype);
             }
 literal(A)  ::= INTEGER(D) . {
                 A = LSUP_term_new (
@@ -179,19 +164,23 @@ literal(A)  ::= BOOLEAN(D) . {
 %type blank { LSUP_Term * }
 blank(A)    ::= BNODE_ID(D) . {
                 A = LSUP_term_new (LSUP_TERM_BNODE, D, NULL);
+                log_trace ("Created blank node: _:%s", A->data);
             }
 blank(A)    ::= LBRACKET RBRACKET . [BNODE_ID] {
                 A = LSUP_term_new (LSUP_TERM_BNODE, NULL, NULL);
+                log_trace ("Created empty list BN: _:%s", A->data);
             }
 blank(A)    ::= LBRACKET predObjList(L) RBRACKET . [BNODE_ID] {
                 A = LSUP_term_new (LSUP_TERM_BNODE, NULL, NULL);
                 state->ct += LSUP_spo_list_add_triples (state->it, A, L);
+                log_trace ("Created list BN: _:%s", A->data);
 
                 LSUP_pred_obj_list_free (L);
             }
 blank       ::= collection . [BNODE_ID]
 blank(A)    ::= LPAREN RPAREN . [BNODE_ID] {
                 A = LSUP_iriref_new ("rdf:nil", state->nsm);
+                log_trace ("Created list terminator: %s", A->data);
             }
 
 // "collection" is the subject of the first collection item.
@@ -204,7 +193,7 @@ collection(A) ::= LPAREN itemList(L) RPAREN . {
 %type itemList { LSUP_Term ** }
 // Freed when the item list in the collection gets added to the graph.
 %destructor itemList {}
-itemList(A) ::= itemList(L) WS object(O) . { A = LSUP_obj_list_add (L, O); }
+itemList(A) ::= itemList(L) ows object(O) . { A = LSUP_obj_list_add (L, O); }
 itemList(A) ::= object(O) . {
                 A = calloc (sizeof (*A), 2);
                 A[0] = O;
@@ -220,8 +209,12 @@ resource(A) ::= IRIREF(D) . {
                 } else {
                     A = rel_iri;
                 }
+                log_trace ("Created IRI: <%s>", A->data);
+            }
+resource(A) ::= QNAME(D) . {
+                A = LSUP_iriref_new (D, state->nsm);
+                log_trace ("Created IRI: %s", A->data);
             }
-resource(A) ::= QNAME(D) . { A = LSUP_iriref_new (D, state->nsm); }
 
 ows         ::= WS .
 ows         ::= .

+ 3 - 2
src/codec/lexer_nt.re

@@ -244,7 +244,7 @@ LSUP_nt_parse_term (const char *rep, const LSUP_NSMap *map, LSUP_Term **term)
         case T_BNODE:
             return LSUP_OK;
         default:
-            return LSUP_VALUE_ERR;
+            return LSUP_PARSE_ERR;
     }
 }
 
@@ -295,8 +295,9 @@ LSUP_nt_parse_doc (FILE *fh, LSUP_Graph **gr_p, size_t *ct, char **err_p)
             char *err_str = malloc (err_size);
             sprintf (err_str, "%s%s%s", err_start, token, err_info);
 
-            rc = LSUP_VALUE_ERR;
+            rc = LSUP_PARSE_ERR;
             *err_p = err_str;
+            log_error (err_str);
 
             goto finally;
         }

+ 37 - 31
src/codec/lexer_ttl.re

@@ -75,6 +75,13 @@ static void parse_init (ParseIterator *it, FILE *fh)
 }
 
 
+static void newline (ParseIterator *it) {
+    it->line ++;
+    it->bol = YYCURSOR;
+    log_trace ("New line: #%u.", it->line);
+}
+
+
 // Parser interface. Required here to silence linters.
 void *TTLParseAlloc();
 void TTLParse();
@@ -99,7 +106,6 @@ static int lex (ParseIterator *it, YYCTYPE **token_p)
 
     // Character classes.
     EOL             = [\n\r];
-    NCWS            = [\t\x20] | EOL;
     HEX             = [\x30-\x39\x41-\x46];
     CHAR_BASE       = "\\u" HEX{4} | "\\U" HEX{8} | '\\'
                     | [\U0000005D-\U0010FFFF];
@@ -119,12 +125,12 @@ static int lex (ParseIterator *it, YYCTYPE **token_p)
     LCHAR           = ECHAR | ([\\] ["]) | [\t\n\r];
 
     // Constructs.
-    COMMENT         = '#' ( [^\n\r] )*;
-    WS              = (NCWS+ | COMMENT)+;
+    COMMENT         = '#' [^\n\r]*;
+    WS              = ([\t\x20] | EOL | COMMENT)+;
     INTEGER         = ('-' | '+')? [0-9]+;
     EXPONENT        = [eE] INTEGER;
     DOUBLE          = ('-' | '+') ? ([0-9]+ '.' [0-9]* EXPONENT
-                    | '.' ([0-9])+ EXPONENT | ([0-9])+ EXPONENT);
+                    | '.'? ([0-9])+ EXPONENT);
     DECIMAL         = ('-' | '+')?
                     ( [0-9]+ '.' [0-9]* | '.' ([0-9])+ | ([0-9])+ );
 
@@ -151,9 +157,7 @@ loop: // Start new token.
     }
 
     EOL {
-        it->line ++;
-        it->bol = YYCURSOR;
-        log_trace ("New line: #%u.", it->line);
+        newline (it);
         goto loop;
     }
 
@@ -202,17 +206,27 @@ loop: // Start new token.
         return T_BNODE_ID;
     }
 
+    COMMENT {
+        log_trace ("Comment: `%s`", it->tok);
+        goto loop;
+    }
+
     WS {
         uint8_t *ws = uint8_ndup (it->tok, YYCURSOR - it->tok);
         log_trace ("Whitespace: '%s'", ws);
+        // Count newlines in mixed whitespace.
+        // That's not great because it scans through the whole whitespace again
+        // but it's the simplest and safest.
+        for (size_t i = 0; i < strlen ((char *)ws); i++)
+            if (ws[i] == '\n' || ws[i] == '\r') newline (it);
         free (ws);
 
         return T_WS;
     }
 
-    '@' [a-z]+ ('-' [a-z0-9]+)* {
-        *token_p = uint8_ndup (it->tok + 1, YYCURSOR - it->tok);
-        log_trace ("Lang tag: %s", *token_p);
+    '@' [a-z]+ ('-' [a-zA-Z0-9]+)* {
+        *token_p = uint8_ndup (it->tok + 1, YYCURSOR - it->tok - 1);
+        log_trace ("Lang tag: '%s'", *token_p);
 
         return T_LANGTAG;
     }
@@ -278,11 +292,6 @@ loop: // Start new token.
 
     '^^' { return T_DTYPE_MARKER; }
 
-    COMMENT {
-        log_trace ("Comment: `%s`", it->tok);
-        goto loop;
-    }
-
     "a" {
         log_trace ("RDF type shorthand 'a'.");
         return T_RDF_TYPE;
@@ -295,7 +304,7 @@ schar:
 
     * {
         log_warn (
-            "Invalid token @ %lu: %s (\\x%x)",
+            "Invalid token in string @ %lu: %s (\\x%x)",
             YYCURSOR - it->buf - 1, it->tok, *it->tok);
 
         return -1;
@@ -338,7 +347,7 @@ lchar:
 
     * {
         log_warn (
-            "Invalid token @ %lu: %s (\\x%x)",
+            "Invalid token in long string @ %lu: %s (\\x%x)",
             YYCURSOR - it->buf - 1, it->tok, *it->tok);
 
         return -1;
@@ -354,22 +363,17 @@ LSUP_ttl_parse_doc (FILE *fh, LSUP_Graph **gr_p, size_t *ct, char **err_p)
     *err_p = NULL;
     *gr_p = NULL;
 
-    ParseIterator parse_it;
-    parse_init (&parse_it, fh);
-
-    void *parser = TTLParseAlloc (malloc);
-
-    LSUP_rc rc;
-
     LSUP_TTLParserState *state = malloc (sizeof (*state));
-    if (UNLIKELY (!state)) {
-        rc = LSUP_MEM_ERR;
-        goto finally;
-    }
+    if (UNLIKELY (!state)) return LSUP_MEM_ERR;
     state->base = NULL;
     state->ct = 0;
     state->rc = LSUP_NORESULT;
 
+    ParseIterator parse_it;
+    parse_init (&parse_it, fh);
+
+    void *parser = TTLParseAlloc (malloc);
+
     state->nsm = LSUP_nsmap_new();
     // TODO add basic NS, critically xsd: and rdf:
     LSUP_Graph *gr = LSUP_graph_new (
@@ -405,8 +409,9 @@ LSUP_ttl_parse_doc (FILE *fh, LSUP_Graph **gr_p, size_t *ct, char **err_p)
             size_t err_size = strlen (err_start) + 16 + strlen(err_info);
             char *err_str = malloc (err_size);
             sprintf (err_str, "%s%s%s", err_start, err_token, err_info);
+            log_error (err_str);
 
-            rc = LSUP_PARSE_ERR;
+            state->rc = LSUP_PARSE_ERR;
             *err_p = err_str;
 
             goto finally;
@@ -425,9 +430,10 @@ LSUP_ttl_parse_doc (FILE *fh, LSUP_Graph **gr_p, size_t *ct, char **err_p)
     *gr_p = gr;
 
 finally:
-    TTLParse (parser, 0, NULL, state);
+    LSUP_rc rc = state->rc;
+    log_trace ("rc is %d", rc);
+
     TTLParseFree (parser, free);
-    rc = state->rc;
 
     LSUP_graph_add_done (state->it);
     free (state);

+ 0 - 1
src/graph.c

@@ -249,7 +249,6 @@ LSUP_graph_add_init (LSUP_Graph *gr)
 LSUP_rc
 LSUP_graph_add_iter (LSUP_GraphIterator *it, const LSUP_Triple *spo)
 {
-
     LSUP_BufferTriple *sspo = LSUP_triple_serialize (spo);
     if (UNLIKELY (!sspo)) return LSUP_MEM_ERR;
     const LSUP_StoreInt *sif = it->graph->store->sif;

+ 1 - 1
src/term.c

@@ -562,7 +562,7 @@ term_init (
             term->type = LSUP_TERM_LITERAL;
         } else {
             char *lang_str = (char *) metadata;
-            log_trace("Lang string: %s", lang_str);
+            log_trace("Lang string: '%s'", lang_str);
             // Lang tags longer than 7 characters will be truncated.
             strncpy(term->lang, lang_str, sizeof (term->lang) - 1);
             if (strlen (term->lang) < 1) {

+ 11 - 8
test/test.h

@@ -17,11 +17,14 @@ char *start_nt_doc, *bad_nt_doc;
 char *end_nt_doc[7];
 LSUP_Codec codec;
 
+// These are used by other codecs than just NT.
 LSUP_Term **init_terms (void);
 void init_triples(LSUP_Term **terms);
 void free_terms (LSUP_Term **terms);
 int test_encode_nt_term();
 int test_encode_nt_graph();
+int test_decode_nt_graph();
+int test_decode_nt_bad_graph();
 
 
 
@@ -29,8 +32,8 @@ int test_encode_nt_graph();
  */
 #define ASSERT(test, msg) do { \
     if (!(test)) {\
-        fprintf(\
-                stderr, "!!! Assertion failed at %s:%d. Message: %s\n", \
+        log_error (\
+                "!!! Assertion failed at %s:%d. Message: %s\n", \
                 __FILE__, __LINE__, msg); \
         return -1; \
     }\
@@ -40,8 +43,8 @@ int test_encode_nt_graph();
  */
 #define EXPECT_INT_EQ(got, exp) do { \
     if ((exp) != (got)) {\
-        fprintf(\
-                stderr, "!!! Test failed at %s:%d. Expected: %lu; got: %lu\n",\
+        log_error (\
+                "!!! Test failed at %s:%d. Expected: %lu; got: %lu\n",\
                 __FILE__, __LINE__, (size_t)(exp), (size_t)(got)); \
         return -1; \
     }\
@@ -53,8 +56,8 @@ int test_encode_nt_graph();
     const char *_str1 = (exp); \
     const char *_str2 = (got); \
     if (((_str1 == NULL) ^ (_str2 == NULL)) || strcmp(_str1, _str2) != 0) {\
-        fprintf(\
-                stderr, "!!! Test failed at %s:%d. Expected: %s; got: %s\n", \
+        log_error (\
+                "!!! Test failed at %s:%d. Expected: %s; got: %s\n", \
                 __FILE__, __LINE__, (exp), (got)); \
         return -1; \
     }\
@@ -63,8 +66,8 @@ int test_encode_nt_graph();
 #define EXPECT_PASS(exp) do { \
     int _rc = (exp); \
     if (_rc != LSUP_OK) {\
-        fprintf(\
-                stderr, "!!! Test failed at %s:%d. Error: %s (%d)\n",\
+        log_error (\
+                "!!! Test failed at %s:%d. Error: %s (%d)\n",\
                 __FILE__, __LINE__, LSUP_strerror(_rc), _rc); \
         return -1; \
     }\

+ 26 - 8
test/test_codec_nt.c

@@ -60,7 +60,7 @@ char *start_nt_doc = (
     "<urn:local:s1> <http://example.org/p1> \"hello\" . #  Comment here.\n"
     "<urn:local:s1> <http://example.org/p1> "
         "\"hello\"^^<http://www.w3.org/2001/XMLSchema#string> .\n"
-    "<urn:local:s1> <http://example.org/p1>\"hello\"@en-US .\n"
+    "<urn:local:s1> <http://example.org/p1>\"hello\"@en-US.\n"
     "<urn:local:s1> <http://example.org/p1>\"hello\"@es-ES .\n"
     "# Some comments\n# To make it a bit \n   #less boring.\n"
     "<urn:local:s1> <http://example.org/p1> _:bn1 .\n"
@@ -77,7 +77,7 @@ char *start_nt_doc = (
 char *bad_nt_doc = (
     "<urn:local:s1> <http://example.org/p1> \"hello\" . #  Comment here.\n"
     "<urn:local:s1> <http://example.org/p1>\"hello\"@es-ES .\n"
-    "<urn:local:s1> dc:title \"Bad Data.\" ."
+    "<urn:local:s1> @ \"Bad Data.\" ."
 );
 
 
@@ -145,10 +145,10 @@ test_encode_nt_term()
         EXPECT_STR_EQ (out, end_nt[i]);
     }
 
-    EXPECT_INT_EQ (codec.encode_term (terms[8], NULL, &out), LSUP_VALUE_ERR);
+    EXPECT_INT_EQ (codec.encode_term (terms[8], NULL, &out), LSUP_PARSE_ERR);
     ASSERT (out == NULL, "Encoding of undefined term should be NULL!");
 
-    EXPECT_INT_EQ (codec.encode_term (terms[9], NULL, &out), LSUP_VALUE_ERR);
+    EXPECT_INT_EQ (codec.encode_term (terms[9], NULL, &out), LSUP_PARSE_ERR);
     ASSERT (out == NULL, "Encoding of undefined term should be NULL!");
 
     free (out);
@@ -210,7 +210,7 @@ test_decode_nt_term()
 }
 
 
-static int
+int
 test_decode_nt_graph()
 {
     FILE *input = fmemopen ((void *)start_nt_doc, strlen (start_nt_doc), "r");
@@ -223,6 +223,22 @@ test_decode_nt_graph()
     fclose (input);
 
     ASSERT (err == NULL, "Error string is not NULL!");
+
+#if 1
+    // For debugging: dump the decoded graph into NT.
+    char *out = calloc (1, 1);
+    char *tmp = NULL;
+    LSUP_CodecIterator *it = codec.encode_graph_init (gr);
+    while (codec.encode_graph_iter (it, (unsigned char **)&tmp) != LSUP_END) {
+        out = realloc (out, strlen(out) + strlen (tmp) + 1);
+        out = strcat (out, tmp);
+    }
+    codec.encode_graph_done (it);
+    free (tmp);
+    log_trace ("Serialized graph: \n%s", out);
+    free (out);
+
+#endif
     EXPECT_INT_EQ (ct, 8);
     EXPECT_INT_EQ (LSUP_graph_size (gr), 7);
 
@@ -237,18 +253,20 @@ test_decode_nt_graph()
 }
 
 
-static int
+int
 test_decode_nt_bad_graph()
 {
+    log_info ("testing illegal NT document.");
     FILE *input = fmemopen ((void *)bad_nt_doc, strlen (start_nt_doc), "r");
 
     LSUP_Graph *gr;
     size_t ct;
     char *err;
-    EXPECT_INT_EQ (codec.decode_graph (input, &gr, &ct, &err), LSUP_VALUE_ERR);
+    LSUP_rc rc = codec.decode_graph (input, &gr, &ct, &err);
+    EXPECT_INT_EQ (rc, LSUP_PARSE_ERR);
 
     log_info ("Error: %s", err);
-    ASSERT (strstr (err, "`dc:title") != NULL, "Wrong error string report!");
+    ASSERT (strstr (err, "`@ \"Bad Data.\"") != NULL, "Wrong error string report!");
     ASSERT (strstr (err, "line 3") != NULL, "Wrong error line report!");
     ASSERT (strstr (err, "character 16") != NULL, "Wrong error char report!");
 

+ 26 - 12
test/test_codec_ttl.c

@@ -38,6 +38,20 @@ test_w3c_pos()
         EXPECT_PASS (codec.decode_graph (test_stream, &gr, &ct, &err));
         EXPECT_INT_EQ (LSUP_graph_size (gr), nt_ct); // Just count NT lines.
     }
+
+    return 0;
+}
+
+
+/// Negative test suite from W3C.
+int
+test_w3c_neg()
+{
+    char test_fname[36];
+    LSUP_Graph *gr;
+    size_t ct;
+    char *err;
+
     for (int i = 0; i <= W3C_NEG_TEST_CT; i++) {
         sprintf (test_fname, "test/assets/ttl/bad-%02d.ttl", i);
         FILE *test_stream = fopen (test_fname, "r");
@@ -47,15 +61,15 @@ test_w3c_pos()
         log_info ("rc: %d", rc);
         ASSERT (rc == LSUP_PARSE_ERR, "Bad test did not raise a parse error!");
     }
+    for (int i = 0; i <= W3C_NEG_TEST_CT; i++) {
+        sprintf (test_fname, "test/assets/ttl/bad-%02d.ttl", i);
+        FILE *test_stream = fopen (test_fname, "r");
+        log_info ("Testing %s", test_fname);
 
-    return 0;
-}
-
-
-/// Negative test suite from W3C.
-int
-test_w3c_neg()
-{
+        LSUP_rc rc = codec.decode_graph (test_stream, &gr, &ct, &err);
+        log_info ("rc: %d", rc);
+        ASSERT (rc == LSUP_PARSE_ERR, "Bad test did not raise a parse error!");
+    }
 
     return 0;
 }
@@ -68,11 +82,11 @@ int codec_ttl_tests()
 
     codec = ttl_codec;
 
-    //RUN (test_encode_ttl_graph);
-    //RUN (test_decode_ttl_graph);
-    //RUN (test_decode_ttl_bad_graph);
     RUN (test_w3c_pos);
-    //RUN (test_w3c_neg);
+    RUN (test_w3c_neg);
+    //RUN (test_encode_nt_graph);
+    RUN (test_decode_nt_graph);
+    RUN (test_decode_nt_bad_graph);
 
     free_terms(terms);