Skip to content

Janet Production

Authors
Joe Starr, Ph.D. image

Joe Starr, Ph.D.

I specialize in computational knot theory. I’m also a professional embedded software engineer.

What Is a Janet Production?

Most production types of the PDGL are written in C and compiled into machine code. Meaning that for most productions the scope of functionality is fixed at compile time. It's easy to see that fixing the functional scope at compile time limits the flexibility of the PDGL. A Janet production helps mitigate this issue by allowing the execution of arbitrary functionality during the resolution and termination of a production. Janet is a small Lisp style scripting language with an interpreter embeddable into other programs. The embedded Janet interpreter is fully featured and is what allows the incision of arbitrary functionality into a PDGL language specification.

Using a Janet Production

The Janet production takes in a script as the production output generator and then runs that script in a Janet interpreter. The standard Janet print function is used to pass replacement data back to the PDGL. This means calling:

(print "value")

will place the string value into the replacement string buffer.

Important

The Janet scripts do not maintain state between executions within the PDGL. That means if you want to maintain/pass state in a Janet script you must do it yourself.

One easy way to do this on a PC is by creating files. Here's an example from the linked site:

# opens a file named filename for writing, and writes Hello World!
(def f (file/open "filename" :w))
(file/write f "Hello World!")
(file/flush f)
(file/close f)

For the browser while I haven't looked into it, I suspect something similar can be done with the Emscripten file I/O system.

Warning!

In line with the PDGL portability and memory goals the output buffer for a Janet production is a fixed size of length DEFS_PDGL_MAX_STRING_SIZE.

Configuring

A Janet production contains two configurable scripts, the replacement script and the terminal script. The two scripts use the same execution flow but do not run in the same interpreter instance.

Examples

File I/O

[[production]]
name = "entry"
type = "pure"
replacements = ["%{janet prod}"]
terminals = [""]

[[production]]
name = "janet prod"
type = "janet"
replacement = '''#replacement script
  (def f (file/open "filename" :w))
  (file/write f "Hello World!")
  (file/flush f)
  (file/close f)
  (print "terminal")
'''
terminal = '''#terminal script
  (print "terminal")
'''

Use Library

[[production]]
name = "entry"
type = "pure"
replacements = ["%{janet prod}"]
terminals = [""]

[[production]]
name = "janet prod"
type = "janet"
replacement = '''#replacement script
  (def number (math/random))
  (print (string 
    number
    )
  )
'''
terminal = '''#terminal script
  (print "terminal")
'''

Include Library

[[production]]
name = "entry"
type = "pure"
replacements = ["%{janet prod}"]
terminals = [""]

[[production]]
name = "janet prod"
type = "janet"
replacement = '''#replacement script
(import ./docs/manual/productions/janet/media/3/script :as scr)
(scr/dothething)
'''
terminal = '''#terminal script
  (print "terminal")
'''

Supported by ./docs/manual/productions/janet/media/3/script.janet

(defn dothething [] 
    (print "the thing")
    nil
)

Janet Entry

[[production]]
name = "entry"
type = "pure"
replacements = ["%{janet prod}"]
terminals = [""]

[[production]]
name = "janet prod"
type = "janet"
replacement = '''#replacement script
  (def number (math/random))
  (print (string 
    number
    )
  )
'''
terminal = '''#terminal script
  (print "terminal")
'''

Production Schema

{
    "$id": "janet-production_schema",
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "properties": {
        "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
        },
        "type": {
            "type": "string",
            "const": "janet"
        },
        "replacement": {
            "type": "string",
            "minLength": 0,
            "maxLength": 3000
        },
        "terminal": {
            "type": "string",
            "minLength": 0,
            "maxLength": 3000
        }
    },
    "additionalProperties": false,
    "minProperties": 4
}