This commit is contained in:
Joshua Bemenderfer
2025-09-08 16:24:38 -04:00
parent 70200a4091
commit 9d9757e868
79 changed files with 11705 additions and 3554 deletions

View File

@@ -0,0 +1,74 @@
Heading 2 Core API
class mt-12
Markdown
**Note:** The Core API uses C-style conventions to optimize memory management
and improve portability to other environments and languages.
It is unwieldy and does not follow Go best practices.
For most projects you'll want to use the [Document API](#document-api) instead.
It provides an ergonomic wrapper around the Core API and lets you focus on parsing
your documents.
Heading 3 LineData
class mb-4 mt-12
CodeBlock go
// Type Definition
// Holds the parsed information from each line.
type LineData struct {
// Which character is being used for indentation.
Indent rune
// How many indent characters are present in the current line.
Level int
// The number of characters before the start of the line's "head" section.
OffsetHead int
// The number of characters before the start of the line's "tail" section.
OffsetTail int
}
Heading 3 NewLineData()
class mb-4 mt-12
Markdown
| Parameter | Type | Description
| -------------- | --------------------- | -----------------------------------------------------------------------
| indent | rune | The character used for indentation in the document. Only a single character is permitted.
| **@returns** | *LineData | A LineData instance with the specified indent character and all other values initialized to 0.
Initialize a LineData instance with default values to pass to [ParseLine()](#parse-line).
CodeBlock go
// Function Definition
func NewLineData(indent rune) *LineData
// Import Path
import "terrace.go"
// Usage
lineData := terrace.NewLineData(' ')
fmt.Printf("%+v\n", lineData)
// &{Indent:32 Level:0 OffsetHead:0 OffsetTail:0}
// Use the same lineData object for all calls to ParseLine in the same document.
Heading 3 ParseLine()
class mb-4 mt-12
Markdown
| Parameter | Type | Description
| -------------- | --------------------- | -----------------------------------------------------------------------
| line | string | A string containing a line to parse. Shouldn't end with a newline.
| lineData | *LineData | A LineData object to store information about the current line, from [NewLineData()](#new-line-data).<br/>**Mutated in-place!**
| **@returns** | error | Returns an error if the input parameters are invalid, nil otherwise.
Core Terrace parser function, sets `Level`, `OffsetHead`, and `OffsetTail` in a [LineData](#line-data) object based on the passed line.
Note that this is a C-style function, `lineData` is treated as a reference and mutated in-place.
CodeBlock go
// Function Definition
func ParseLine(line string, lineData *LineData) error
// Import Path
import "terrace.go"
// Usage
lineData := terrace.NewLineData(' ')
terrace.ParseLine("title Example Title", lineData)
fmt.Printf("%+v\n", lineData)
// &{Indent:32 Level:0 OffsetHead:0 OffsetTail:5}

View File

@@ -0,0 +1,151 @@
Heading 2 Document API
class mt-12
Heading 3 NewTerraceDocument()
class mb-4 mt-12
Markdown
| Parameter | Type | Description
| -------------- | --------------------- | -----------------------------------------------------------------------
| reader | [Reader](#reader) | An interface that reads lines from a document.
| indent | rune | The character used for indentation in the document. Only a single character is permitted.
| **@returns** | *TerraceDocument | A pointer to a TerraceDocument, which is an iterator for parsing a document line by line.
Provides a simple set of convenience functions around ParseLine for more ergonomic parsing of Terrace documents.
CodeBlock go
// Function Definition
func NewTerraceDocument(reader Reader, indent rune) *TerraceDocument
// Import Path
import "terrace.go"
Heading 3 TerraceDocument
class mb-4 mt-12
Markdown
Container for a handful of convenience functions for parsing documents.
Obtained from [NewTerraceDocument()](#newterracedocument) above
CodeBlock go
// Type Definition
type TerraceDocument struct {
// ... (private fields)
}
Heading 3 TerraceDocument.Next()
class mb-4 mt-12
Markdown
| Parameter | Type | Description
| -------------- | --------------------- | -----------------------------------------------------------------------
| **@returns** | (*TerraceNode, error) | Returns a pointer to the next TerraceNode and an error. The error is `io.EOF` at the end of the document.
Advances the current position in the terrace document and returns the next node.
Returns `io.EOF` upon reaching the end of the document.
Intended to be used inside a for loop to parse a section of a Terrace document.
CodeBlock go
// Method Definition
func (d *TerraceDocument) Next() (*TerraceNode, error)
// Import Path
import "terrace.go"
// Usage
doc := terrace.NewTerraceDocument(...)
for {
node, err := doc.Next()
if err == io.EOF {
break
}
if err != nil {
// Handle error
}
// Do something with each node.
}
Heading 3 TerraceNode
class mb-4 mt-12
Markdown
Represents a single node/line in a Terrace document.
CodeBlock go
// Type Definition
type TerraceNode struct {
// ... (private fields)
}
Heading 3 TerraceNode.Level()
class mb-4 mt-12
Markdown
| Parameter | Type | Description
| -------------- | --------------------- | -----------------------------------------------------------------------
| **@returns** | int | The indent level of the current node
Returns the number of indent characters of the current node.
Given the following document, `Level()` would return 0, 1, 2, and 5 respectively for each line.
CodeBlock terrace
block
block
block
block
CodeBlock go
// Method Definition
func (n *TerraceNode) Level() int
Heading 3 TerraceNode.Content()
class mb-4 mt-12
Markdown
| Parameter | Type | Description
| -------------- | --------------------- | -----------------------------------------------------------------------
| **@returns** | string | The line contents starting from the first non-indent character.
Get a string with the current line contents. Skips all indent characters.
Given the following document
CodeBlock terrace
root
sub-line
Markdown
- Calling `Content()` on the second line returns "sub-line", trimming off the leading indent characters.
CodeBlock go
// Method Definition
func (n *TerraceNode) Content() string
Heading 3 TerraceNode.Head()
class mb-4 mt-12
Markdown
| Parameter | Type | Description
| -------------- | --------------------- | -----------------------------------------------------------------------
| **@returns** | string | The `head` portion (first word) of a line
Get the first "word" of a line, starting from the first non-indent character to the first space or end of the line.
Often used for deciding how to parse a block.
Terrace DSLs do not *need* to use head-tail line structure, but support for them is built into the parser
Given the following line, `Head()` returns "title"
CodeBlock terrace
title An Important Document
CodeBlock go
// Method Definition
func (n *TerraceNode) Head() string
Heading 3 TerraceNode.Tail()
class mb-4 mt-12
Markdown
| Parameter | Type | Description
| -------------- | --------------------- | -----------------------------------------------------------------------
| **@returns** | string | The remainder of the line following the `Head()` portion, with no leading space
Get all text 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, `Tail()` returns "An Important Document"
CodeBlock terrace
title An Important Document
CodeBlock go
// Method Definition
func (n *TerraceNode) Tail() string

View File

@@ -0,0 +1,42 @@
layout layout.njk
title Go Documentation - Terrace
description
Go language documentation for the Terrace programming language
Section light
class flex flex-col md:flex-row gap-16
Block
class w-full lg:w-1/3
TableOfContents
Block
Heading 1 Terrace Go Documentation
class -ml-2
Markdown
Documentation is available for the following languages:
- [C](/docs/c/) - 100% Complete
- [JavaScript](/docs/javascript/) - 75% Complete
- [Go](/docs/go/) - 50% Complete
- [Python](/docs/python/) - 100% Complete
- [Rust](/docs/rust/) - 100% Complete
Heading 2 Getting Started
class mt-12 mb-6
Markdown
Install Terrace using `go get`:
CodeBlock bash
$ go get terrace.go
Include ./core-api.inc.tce
Include ./document-api.inc.tce
Include ./reader-api.inc.tce
Heading 2 Contributing
class mt-12
Section dark
Footer
class w-full

View File

@@ -0,0 +1,101 @@
Heading 2 Reader API
class mt-12
Markdown
The [Document API](#document-api) requires a `Reader` interface to iterate through lines
in a document. A `Reader` has a `Read()` method that returns a string and an error. Each time it is called, it returns the next line from whichever source it is pulling them.
Terrace for Go does not provide built-in readers, but you can easily create your own.
Heading 3 Reader
class mb-4 mt-12
Markdown
An interface with a `Read()` method that returns the next line in a document and an error. The error should be `io.EOF` when the end of the document has been reached.
CodeBlock go
// Interface Definition
type Reader interface {
Read() (string, error)
}
Heading 3 StringReader
class mb-4 mt-12
Markdown
You can implement a `Reader` that reads from a string.
CodeBlock go
import (
"io"
"strings"
)
type StringReader struct {
reader *strings.Reader
}
func (r *StringReader) Read() (string, error) {
line, err := r.reader.ReadString('\n')
if err != nil {
return "", err
}
return strings.TrimRight(line, "\n"), nil
}
Markdown
**Usage**
CodeBlock go
import (
"fmt"
"io"
"strings"
"terrace.go"
)
func main() {
data := `
title Example Title
line 2
`
reader := &StringReader{reader: strings.NewReader(data)}
doc := terrace.NewTerraceDocument(reader, ' ')
for {
node, err := doc.Next()
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
fmt.Printf("%d %s\n", node.Level(), node.Content())
}
}
Heading 3 FileReader
class mb-4 mt-12
Markdown
You can use the `bufio` package to create a `Reader` for a file.
CodeBlock go
import (
"bufio"
"os"
)
type FileReader struct {
scanner *bufio.Scanner
}
func NewFileReader(file *os.File) *FileReader {
return &FileReader{scanner: bufio.NewScanner(file)}
}
func (r *FileReader) Read() (string, error) {
if r.scanner.Scan() {
return r.scanner.Text(), nil
}
if err := r.scanner.Err(); err != nil {
return "", err
}
return "", io.EOF
}

View File

@@ -0,0 +1,204 @@
Heading 2 Recipes
class mt-12
Heading 3 Parse object properties
class mb-2
Markdown
Read known properties from a Terrace block and write them to a struct.
CodeBlock go
package main
import (
"fmt"
"io"
"strconv"
"strings"
"terrace.go"
)
type Config struct {
StringProperty string
NumericProperty int
}
func main() {
input := `object
string_property An example string
numeric_property 42`
reader := &StringReader{reader: strings.NewReader(input)}
doc := terrace.NewTerraceDocument(reader, ' ')
config := Config{}
for {
node, err := doc.Next()
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
if node.Head() == "object" {
objectLevel := node.Level()
for {
node, err := doc.Next()
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
if node.Level() <= objectLevel {
// We've exited the object block
break
}
switch node.Head() {
case "string_property":
config.StringProperty = node.Tail()
case "numeric_property":
if val, err := strconv.Atoi(node.Tail()); err == nil {
config.NumericProperty = val
}
}
}
}
}
fmt.Printf("%+v\n", config)
// {StringProperty:An example string NumericProperty:42}
}
Markdown
Read *all* properties as strings from a Terrace block and write them to a map.
CodeBlock go
package main
import (
"fmt"
"io"
"strings"
"terrace.go"
)
func main() {
input := `object
property1 Value 1
property2 Value 2
random_property igazi3ii4quaC5OdoB5quohnah1beeNg`
reader := &StringReader{reader: strings.NewReader(input)}
doc := terrace.NewTerraceDocument(reader, ' ')
output := make(map[string]string)
for {
node, err := doc.Next()
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
if node.Head() == "object" {
objectLevel := node.Level()
for {
node, err := doc.Next()
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
if node.Level() <= objectLevel {
// We've exited the object block
break
}
// Skip empty lines
if node.Content() == "" {
continue
}
// Add any properties to the map as strings using the
// line Head() as the key and Tail() as the value
output[node.Head()] = node.Tail()
}
}
}
fmt.Printf("%+v\n", output)
// map[property1:Value 1 property2:Value 2 random_property:igazi3ii4quaC5OdoB5quohnah1beeNg]
}
Heading 3 Process nested blocks
class mb-2
Markdown
Handle hierarchically nested blocks with recursive processing.
CodeBlock go
package main
import (
"fmt"
"io"
"strings"
"terrace.go"
)
type Block struct {
Name string
Content string
Children []Block
}
func parseBlock(doc *terrace.TerraceDocument, parentLevel int) []Block {
var blocks []Block
for {
node, err := doc.Next()
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
// If we've returned to the parent level or higher, we're done
if node.Level() <= parentLevel {
break
}
block := Block{
Name: node.Head(),
Content: node.Tail(),
}
// Parse any nested children
block.Children = parseBlock(doc, node.Level())
blocks = append(blocks, block)
}
return blocks
}
func main() {
input := `root
section1 Section 1 Content
subsection1 Subsection 1 Content
subsection2 Subsection 2 Content
section2 Section 2 Content
nested
deeply Nested Content`
reader := &StringReader{reader: strings.NewReader(input)}
doc := terrace.NewTerraceDocument(reader, ' ')
blocks := parseBlock(doc, -1)
fmt.Printf("%+v\n", blocks)
}