Terrace C Documentation

Documentation is available for the following languages:

Getting Started

The terrace parser is distributed as a set of C header files.
To use it, download and include the following files in your project tree:

  • parser.h - Core terrace parser
  • document.h - (optional) Convenience functions for parsing of documents

A simple example program using to read each line from stdin and output parser information, looking for a "title" key:

// Provides getline() for reading from stdin
#include <stdio.h>
// Provides free() for deallocating the lines from getline()
#include <stdlib.h>
// Provides document API for interacting with Terrace files
#include "document.h"

// Custom userData struct. Stores information needed
// by read_line below.
typedef struct read_line_container_s {
  size_t bufsize;
} read_line_container_t;

// A user-supplied function to read lines from stdin (or whichever data source you choose)
int read_line_from_stdin(char** line, void* userData) {
  read_line_container_t* lineContainer = (read_line_container_t*) userData;
  // Uses getline from the C stdlib to read the next line from stdin.
  int num_chars_read = getline(line, &lineContainer->bufsize, stdin);
  // Change trailing newline to null char. Terrace doesn't use trailing newlines
  if (num_chars_read > 0) (*line)[num_chars_read - 1] = '\0';
  // Return the number of charaters read to the document parser.
  return num_chars_read;
}

int main(int argc, char *argv[]) {
  read_line_container_t read_line_information = { .bufsize = 64 };
  // Initialize the terrace document with the line reader function created above.
  terrace_document_t doc = terrace_create_document(' ', &read_line_from_stdin, &read_line_information);
  
  // Loop over every line in the document.
  while(terrace_next(&doc, -1)) {
    // > Replace with your custom line handling code.
    
    // Print the line and level to demonstrate the terrace_level and terrace_line functions.
    printf("| level %u | line %s |", terrace_level(&doc), terrace_line(&doc, -1));
    // If one of the lines starts with "title", output it.
    if (terrace_match(&doc, "title")) {
      printf("Title: %s |", terrace_tail(&doc));
    }
  };
  
  // Free allocated line memory
  free(line);
  
  return 0;
}

Core API

Note: The Core API is designed for maximum portability and is not intended to be directly consumed.

For most projects you'll want to use the Document API instead. It provides an ergonomic wrapper around the Core API and lets you focus on parsing your documents.

terrace_linedata_t

This struct holds information about each line as it is parsed. Mutated each time terrace_parse_line() is called. Not intended to be used directly. Use the relevant terrace_ functions from the Document API instead.

// Holds the parsed information from each line.
typedef struct terrace_linedata_s {
  // Which character is being used for indentation. Avoids having to specify it on each terrace_parse_line call.
  char indent;
  // How many indent characters are present in the current line before the first non-indent character.
  unsigned int level;
  // The number of characters before the start of the line's "head" section.
  // (Normally the same as `level`)
  unsigned int offsetHead;
  // The number of characters before the start of the line's "tail" section.
  unsigned int offsetTail;
} terrace_linedata_t;

terrace_create_linedata()

Parameter Type Description
indent const char The character used for indentation in the document. Only a single character is permitted.
@returns terrace_linedata_t A terrace_linedata_t struct with the specified indent character and all other values initialized to 0.

Initialize a terrace_linedata struct with default values to pass to terrace_parse_line().

// Call Signature
terrace_linedata_t terrace_create_linedata(const char indent)

terrace_parse_line()

Parameter Type Description
line char* A pointer to the line to parse as a C-style string. Shouldn't end with a newline.
lineData terrace_linedata_t* A pointer to the terrace_linedata_t struct to store information about the current line in.

Core Terrace parser function, sets level, offsetHead, and offsetTail in a terrace_linedata struct based on the current line.

// Call Signature
void terrace_parse_line(const char* line, terrace_linedata_t* lineData)

Document API

terrace_document_t

Tracks state of a document while being parsed. Obtained from terrace_create_document() below

// Type Definition
typedef struct terrace_document_s {
  // == Internal State == //
  unsigned int _repeatCurrentLine;
  // Current line being read
  char* _currentLine;
  
  // == External Information == //
  // Embedded line data struct. Holds information about the current parsed line
  terrace_linedata_t lineData;
  // Custom data passed to the readline function
  void* userData;
  /**
  * Line reader function, provided by the user
  * Needed to get the next line inside of `terrace_next(doc)`
  * @param {char**} line First argument is a pointer to `_currentLine`, above
  * @param {void*} userData Second argument is `userData`, above
  * @returns {int} The number of characters read, or -1 if no characters were read.
  */
  int (*reader)(char** line, void* userData);
} terrace_document_t;

terrace_create_document()

Parameter Type Description
indent const char The indent character to use. Generally a single space character.
reader int (*reader)(char** line, void* userData) A function pointer to a function that reads lines sequentially from a user-provided source. Receives a pointer to lineData->_currLine, and userData, supplied in the next argument.
userData void * A user-supplied pointer to any state information needed by their reader function. Passed to readereach time it is called.
@returns terrace_document_t A state struct needed by the convenience functions below.

Initializes the state needed for the convenience functions below. Takes a user-supplied reader function to read each line from a user-determined source.

// Call Signature
terrace_document_t terrace_create_document(const char indent, int (*reader)(char** line, void* userData), void* userData)

terrace_next()

Parameter Type Description
doc terrace_document_t* A pointer to the current document state struct.
levelScope int If set above -1, next() will return 0 when it encounters a line with a level at or below levelScope
@returns char Returns 1 after parsing a line, or 0 if the document has ended or a line at or below levelScope has been encountered.

Advances the current position in the terrace document and populates lineData with the parsed information from that line.

Returns 1 after parsing the next line, or 0 upon reaching the end of the document. If the levelScope parameter is not -1, terrace_next() will also return 0 when it encounters a line with a level at or below levelScope. This allows you to iterate through subsections of a document.

If a lower-level line was encountered, the following call to terrace_next() will repeat this line again. This allows a child loop to look forward, determine that the next line will be outside its purview, and return control to the calling loop transparently without additional logic.

Intended to be used inside a while loop to parse a section of a Terrace document.

// Call Signature
char terrace_next(terrace_document_t* doc, int levelScope)

// Usage
while(terrace_next(doc, -1)) {
  // Do something with each line.
}

terrace_level()

Parameter Type Description
doc terrace_document_t* A pointer to the current document state struct.
@returns unsigned int The indent level of the current line

Returns the number of indent characters of the current line.

Given the following document, terrace_level(doc) would return 0, 1, 2, and 5 respectively for each line.

block
  block
  block
      block
// Call Signature
unsigned int terrace_level(terrace_document_t* doc)

// Usage
while(terrace_next(doc, -1)) {
  printf("Indent Level: %u", terrace_level(doc));
}

terrace_line()

Parameter Type Description
doc terrace_document_t* A pointer to the current document state struct.
startOffset int How many indent characters to skip before outputting the line contents. If set to -1, uses the current indent level.
@returns char* The line contents starting from startOffset

Get a string with the current line contents. If startOffset is -1, skips all indent characters by default. Otherwise only skips the amount specified.

Given the following document

root
    sub-line
  • Calling terrace_line(doc, -1) on the second line returns "sub-line", trimming off the leading indent characters.
  • Calling terrace_line(doc, 0) however, returns "    sub-line", with all four leading spaces.

startOffsets other than -1 are primarily used for parsing blocks that have literal indented multi-line text

// Call Signature
char* terrace_line(terrace_document_t* doc, int startOffset)

// Usage
while(terrace_next(doc, -1)) {
  printf("Line with indent characters: %s", terrace_line(doc, 0));
  printf("Line without indent characters: %s", terrace_line(doc, -1));
}

terrace_head_length()

title An Important Document
// Call Signature
unsigned int terrace_head_length(terrace_document_t* doc)

// Usage
while(terrace_next(doc, -1)) {
  printf("Head length: %u", terrace_head_length(doc));
}

terrace_tail()

Parameter Type Description
doc terrace_document_t* A pointer to the current document state struct.
@returns char* The remainder of the line following the head portion, with no leading space.

Get a char pointer to everything following the first "word" of a line, starting from the first character after the space at the end of head.

Terrace DSLs do not need to use head-tail line structure, but support for them is built into the parser

Given the following line, terrace_tail(doc) returns "An Important Document"

title An Important Document
// Call Signature
char* terrace_tail(terrace_document_t* doc)

// Usage
while(terrace_next(doc, -1)) {
  printf("Line tail: %s", terrace_tail(doc));
}

terrace_match()

Parameter Type Description
doc terrace_document_t* A pointer to the current document state struct.
matchValue const char* A string to check against the line head for equality.
@returns char A byte set to 0 if the head does not match, or 1if it does match.

Quickly check if the current line head matches a specified value. Useful in many document-parsing situations.

Given the following line:

title An Important Document
  • terrace_match(doc, "title") returns 1
  • terrace_match(doc, "somethingElse") returns 0
// Call Signature
char terrace_match(terrace_document_t* doc, const char* matchHead)

// Usage
while(terrace_next(doc, -1)) {
  printf("Does the line start with 'title': %d", terrace_match(doc, "title"));
}

Recipes

Parse a single line

Parses a single line into line_data, the prints the information from line_data.

#include "parser.h"

int main(int argc, char *argv[]) {
  char* line = "example line";
  // Create the line_data struct
  terrace_linedata_t line_data;
  // Set the indent character to a space
  line_data.indent = ' ';
  // Populates line_data level, offsetHead, and offsetTail from line
  terrace_parse_line(line, &line_data);
  
  printf(
    "level %u | indent %c | offsetHead %u | offsetTail %u\n",
    line_data.level,
    line_data.indent,
    line_data.offsetHead,
    line_data.offsetTail
  );
  
  return 0;
}

Parse all lines from stdin

Reads lines from stdin one-by-one and prints each line's line_data.

#include "parser.h"
// Depends on several cstdlib functions
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

int main(int argc, char *argv[]) {
  // Pointer to start of line
  char *line = NULL;
  // Initial size of the buffer to read into
  // getline() will resize as needed
  size_t bufsize = 128;
  // How many characters have been read
  ssize_t chars_read = 0;
  
  // Create the line_data struct
  terrace_linedata_t line_data;
  // Set the indent character to a space
  line_data.indent = ' ';
  
  while (chars_read = getline(&line, &bufsize, stdin)) {
    // If chars_read is -1, we've reached end of file.
    if (chars_read == -1) break;
    // getline returns lines with a trailing newline
    // terrace_parse_line expects no trailing newline
    // strip it off using strtok()
    // (An odd solution, probably leaks memory)
    char *terrace_line = strtok(line, "\n");
    terrace_parse_line(terrace_line, &line_data);
    
    printf(
      "level %u | indent %c | offsetHead %u | offsetTail %u\n",
      line_data.level,
      line_data.indent,
      line_data.offsetHead,
      line_data.offsetTail
    );
  };
  
  // Free the buffer allocated by getline().
  free(line);
}

Contributing

Maintained by the Terrace Team. Find an issue? Let us know!

Site contents licensed under the CC BY 3.0 license
All code examples licensed under the MIT license