-
Notifications
You must be signed in to change notification settings - Fork 7
Domain Modeling Language
A simple DSL (domain specific language) based on the Ion text format
is used to define a type universe. Within a type universe, the developer defines one or more type domains, each of which
consists of a number of product
, record
and sum
type definitions. Together, these type definitions describe the
complete structure of a tree and comprise all the data used to generate the code listed in Code Generation.
A Note About Terminology
Although PIG currently only generates code for Kotlin, In the future PIG will generate source code for other languages. Thus, we felt we should avoid terms like "enum", "class", "struct", since these all have different meanings unique to each language.
Each type definition consists of a name and zero or more definitions for its elements. The element definition consists of a name and data type.
PIG's product
types are named after the same from
type theory. Conceptually, product
s are tuples,
represented as a class
in Kotlin.
Here is a simple example of a tree definition that uses only a single product
type.
(define sample_type_domain
(domain
(product person
first::symbol
last::symbol
children::(* person 0))
)
)
This type domain defines a single product
type named person
with three elements: first
, last
and a list of at
least 0 children
.
Element definitions take the following form:
<property_name>::<data_type>
Where:
-
property_name
is the name of the property in the corresponding Kotlin class. Forproduct
types, this is required. -
data_type
is the name of the data type of the element. This can be one of the supported primitive types:int
,symbol
,bool
,ion
, or any other data type defined in the same domain (excluding sum variants which will be discussed below).
TODO: describe the way it is today, provide a link to: https://github.com/partiql/partiql-ir-generator/issues/98 (possibly this is better moved to after the s-expression representation is described)
Here is a simple example of a tree definition that uses only a single record
type.
(define sample_type_domain
(domain
(record person
first_name::(first symbol)
last_name::(last symbol)
(children (* person 0))
)
)
This record
stores the same information as the product
person
shown above. Aside from the obviously different
syntax, the primary difference between product
and record
types are in their s-expressions representations. More
details will be included in this later--for now the reader should know that the names of the elements are included the
s-expressions of a record
and this is not true for product
types.
Field definitions take the following form:
<property_name>::(<field_name> symbol)
Where:
-
property_name
is the name of the property in the generated Kotlin class. Unlikeproduct
types, this is optional. When not specified, theproperty_name
defaults to thefield_name
. -
field_name
is the name of the field in the s-expression representation.
The Kotlin class of this record type isn't shown because it has the same API as the product
type.
Also note that Builder
interface also includes definitions needed to construct record
types as well.
Sum types are used to hold a value that could take on several different, but fixed, types. Only one of the types can
be in use at any one time, and the name of the type, known as a tag, explicitly indicates which one is in use. Sum
types are also known as algebraic We use the term "variant" to
refer to one of the possible types for a given sum
.
Sum types are a natural fit for modeling expressions in a programming language. Let's demonstrate this with an AST for a very simple toy calculator language that supports integer literals and simple binary expressions:
(define calculator_ast
(domain
(sum operator (plus) (minus) (times) (divide) (modulo))
(sum expr
(lit value::int)
(binary op::operator left::expr right::expr)
)
)
)
This involves two sum
types: operator
and expr
.
The operator
sum
declares five different arithmetic operations: plus
, minus
, times
, divide
, and modulo
.
Due to the syntax used here (more on that later), each of these is a product
type, and each gets a Kotlin class
similar to the example shown above (shown below). However, none of these have any elements.
The expr
sum
defines two possible types of expressions that exist our toy calculator: lit
and binary
.
The Kotlin equivalent of a sum
type is a sealed class
.
PIG uses the syntax used to define a variant's elements to determine if it is a product
or record
.
(define toy_ast
(domain
(sum expr
// This syntax defines a `record` variant. Note the similarity to a non-variant `record`.
(let
(name symbol)
(value expr)
optional_name::(body expr))
// This syntax defines a `product` variant. Note the similarity to a non-variant `product`.
(binary op::operator left::expr right::expr)
)
// ...
)
)
As with non-variant record
elements, specifying the property name is optional and defaults to the field name.