Parsers

Four implementations. Identical JSON output. Pick your language.

Python

Uses Lark parser with Earley algorithm. Type-safe transformation with dataclasses.

Installation

cd python/
pip install -r requirements.txt

Dependencies: lark>=1.1.0

Command Line

python parser.py input.3tl              # Compact JSON
python parser.py input.3tl --pretty     # Pretty-printed
python validator.py input.3tl           # Validate only

Library Usage

from parser import parse_file, parse_string, to_json

# Parse file
doc = parse_file('data.3tl')

# Parse string
doc = parse_string('''
#! User
#@ id:uint, name:str
1, Alice
''')

# Access data
for table in doc.tables:
    print(f"Table: {table.name}")
    for col in table.columns:
        print(f"  {col.name}: {col.type.base_type}")
    for row in table.rows:
        print(f"  Row: {row}")

# Convert to JSON
json_str = to_json(doc, pretty=True)

Data Structures

@dataclass
class TypeInfo:
    base_type: str
    is_array: bool = False
    is_nullable: bool = False
    params: Optional[dict[str, Any]] = None

@dataclass
class Column:
    name: str
    type: TypeInfo

@dataclass
class Table:
    name: str
    columns: list[Column]
    rows: list[list[Any]]

@dataclass
class Document:
    tables: list[Table]

Testing

python -m pytest test_parser.py

12 tests. All passing.


JavaScript

Uses Peggy PEG parser. ES modules. Node 16+.

Installation

cd javascript/
npm install

Dependencies: peggy ^4.0.3

Command Line

node src/cli.js input.3tl              # Compact JSON
node src/cli.js input.3tl --pretty     # Pretty-printed

Library Usage

import { parseFile, parseString, toJSON } from './src/parser.js';

// Parse file
const doc = parseFile('../examples/products.3tl');

// Parse string
const doc = parseString(`
#! User
#@ id:uint, name:str
1, Alice
`);

// Access data
for (const table of doc.tables) {
  console.log(`Table: ${table.name}`);
  for (const col of table.columns) {
    console.log(`  ${col.name}: ${col.type}`);
  }
  for (const row of table.rows) {
    console.log(`  Row: ${row}`);
  }
}

// Convert to JSON
const json = toJSON(doc, true);
console.log(json);

Data Structures

{
  tables: [
    {
      name: 'User',
      columns: [
        { name: 'id', type: 'uint' },
        { name: 'name', type: 'str' }
      ],
      rows: [
        [1, 'Alice'],
        [2, 'Bob']
      ]
    }
  ]
}

Testing

npm test

12 tests. All passing.


Clojure

Uses Instaparse with EBNF grammar. Functional transformation. Immutable data.

Installation

cd clojure/
# Dependencies auto-downloaded via deps.edn

Dependencies: instaparse 1.4.12, org.clojure/data.json 2.4.0

Command Line

clojure -M -m three-tl.parser input.3tl        # Compact JSON
clojure -M -m three-tl.parser input.3tl true   # Pretty-printed

Library Usage

(require '[three-tl.parser :as parser])

;; Parse file
(def doc (parser/parse-file "../examples/products.3tl"))

;; Parse string
(def doc (parser/parse-string 
  "#! User
   #@ id:uint, name:str
   1, Alice"))

;; Access data
(doseq [table (:tables doc)]
  (println "Table:" (:name table))
  (doseq [col (:columns table)]
    (println "  " (:name col) ":" (:type col)))
  (doseq [row (:rows table)]
    (println "  Row:" row)))

;; Convert to JSON
(parser/to-json doc true)

Data Structures

{:tables
 [{:name "User"
   :columns [{:name "id" :type {:base-type "uint"}}
             {:name "name" :type {:base-type "str"}}]
   :rows [[1 "Alice"]
          [2 "Bob"]]}]}

Testing

clojure -X:test

12 tests. All passing.


Go

Uses Participle parser. Grammar defined via struct tags. Go 1.21+.

Installation

cd go/
go mod download

Dependencies: github.com/alecthomas/participle/v2 v2.1.1

Command Line

go run ./cmd/3tl-parser input.3tl              # Compact JSON
go run ./cmd/3tl-parser input.3tl --pretty     # Pretty-printed

Library Usage

package main

import (
    "fmt"
    "log"
    "github.com/jiriknesl/3tl/pkg/parser"
)

func main() {
    // Parse file
    doc, err := parser.ParseFile("../examples/products.3tl")
    if err != nil {
        log.Fatal(err)
    }

    // Parse string
    doc, err = parser.ParseString(`
#! User
#@ id:uint, name:str
1, Alice
`)
    if err != nil {
        log.Fatal(err)
    }

    // Access data
    for _, table := range doc.Tables {
        fmt.Printf("Table: %s\n", table.Name)
        for _, col := range table.Columns {
            fmt.Printf("  %s: %s\n", col.Name, col.Type)
        }
        for _, row := range table.Rows {
            fmt.Printf("  Row: %v\n", row)
        }
    }

    // Convert to JSON
    json, err := parser.ToJSON(doc, true)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(json)
}

Data Structures

type Document struct {
    Tables []Table `json:"tables"`
}

type Table struct {
    Name    string     `json:"name"`
    Columns []Column   `json:"columns"`
    Rows    [][]any    `json:"rows"`
}

type Column struct {
    Name string `json:"name"`
    Type string `json:"type"`
}

Testing

go test ./pkg/parser -v

12 tests. All passing.


JSON Output

All parsers produce identical JSON:

{
  "tables": [
    {
      "name": "User",
      "columns": [
        {
          "name": "id",
          "type": "uint"
        },
        {
          "name": "name",
          "type": "str"
        }
      ],
      "rows": [
        [1, "Alice"],
        [2, "Bob"]
      ]
    }
  ]
}

Type information preserved. Foreign keys explicit. Arrays and nullability marked.

Performance

All parsers validated with identical test suites:

12 tests per implementation. 48 tests total. All passing.

Choosing a Parser

LanguageBest ForNotes
PythonData science, scriptingMost common for LLM work
JavaScriptWeb apps, Node servicesES modules, modern syntax
ClojureFunctional programmingImmutable data structures
GoSystems, performanceCompiled, fast, type-safe

Pick based on your environment. All parsers are complete and tested.