-- Morg description for Morgana
-- This should also work as a declarative description of the metalanguage.
-- ▼ This node is optional, and sets the name of the Morg.
morg Morgana
-- │ This node is optional, and sets the starting symbol for
-- │ the purposes of syntax analysis. If not present, the first
-- ▼ node declared will be considered the starting nonterminal.
start Morg
-- │ This symbol starts the definition of a *ProdDefinition*, that is,
-- │ a product type which can be mapped to `struct`s in Rust,
-- │ `record`s in Agda and Haskell and so on. In our lingo, we call this
-- ▼ kind of node a _prod_ node.
node Morg := (
-- The following lines describe the fields of the product type.
-- │ The field is enclosed between square brackets, which identify
-- │ the optionality of the field. In type theory, a field of type `[ A ]`
-- │ for any `A` will be mapped to the type MaybeA := A + ().
-- │ In languages like Rust it will be mapped to `Optional`;
-- │ in Haskell to `Maybe A`.
-- │
-- │ │ We can reference rules that will come up later in the file.
-- │ │ In this case, we refer to the constructor `Morg` of the `Keyword`
-- │ │ node, which is simply the literal "Morg". In Morgana, fields
-- │ │ declaration are comprised of both the syntax of the node
-- │ │ and the semantic information that is relevant. In this case,
-- │ │ the relevant part is the `Identifier`. We will see how
-- ▼ ▼ Morgana can track the difference between the two later on.
name: [ Keyword::Morg Identifier::Simple ], -- in this file: `Some("Morgana")`
start_symbol: [ StartStatement ], -- in this file: `Some("Morg")`
-- │ This field is enclosed between braces, which identify the unbounded
-- │ repetition of the field. In type theory, a field of type `{ A }` for
-- │ any `A` will be mapped to the inductive type `L_A: 1 + (A × L_A -> L_A)`.
-- │ In languages like Rust, it will be mapped to `Vec`; in Haskell to
-- │ `[ A ]`.
-- ▼
statements: { Statement }
) -- end node Morg
-- This node definition does not have the structure of the *ProdDefinition*,
-- instead, it simply states that the `StartStatement` node is the juxtaposition of
-- two symbols. This is mapped to "simple" (unnamed) product types; in
-- Haskell, it would be mapped to something like `data StartStatement = StartStatement Identifier`
-- and `type StartStatement = Identifier` in Rust; notice that `Keyword::Start` will be
-- dropped from the abstract type in both languages.
-- In our lingo, we call this kind of node an _alias_ node.
node StartStatement := Keyword::Start Identifier::Simple
-- The third and last kind of node definition is that of sum types.
-- These kind of nodes can be mapped to `enum`s in Rust and usual type
-- definitions in languages like Haskell and Agda.
node Statement :=
-- │ This is the name of the constructor
-- │
-- ▼ ▼ This is the type of the constructor
| Node: Node -- It is not necessary that the type is a single non-terminal,
-- it can obviously be any expression that respects the rules.
| Skip: Skip
| Special: Special
node Skip := Keyword::Skip Builtin
node Node := Keyword::Node NodeDefinition
node NodeDefinition := (
name: Identifier::Simple Operator::Definition,
constructor: NodeConstructor
)
node NodeConstructor :=
| Sum: SumDefinition
| Prod: ProdDefinition
| Alias: Expression
node SumConstructor := (
name: Identifier::Simple Operator::TypeDefinition,
constructor: Expression
)
node SumDefinition := Delimiter::Pipe << Delimiter::Pipe , SumConstructor >>
node ProdConstructor := (
field: Identifier::Simple Operator::TypeDefinition,
type: Expression
)
node ProdDefinition := Delimiter::LParen << Delimiter::Comma , ProdConstructor >> Delimiter::RParen
node Special := (
key: Keyword::Special SpecialKeyword Operator::Definition,
constructor: SpecialConstructor
)
node SpecialConstructor :=
| Sum: SumDefinition
| Prod: ProdDefinition
| Alias: Expression
node SpecialKeyword :=
| Comment: "Comment"
| Whitespace: "Whitespace"
| Keyword: "Keyword"
| Operator: "Operator"
| Delimiter: "Delimiter"
| Builtin: "Builtin"
| Identifier: "Identifier"
node Expression := (
factors: < Factor >
)
node Factor :=
| NonTerminal: NonTerminal
| ExternalRule: ExternalRule
| Builtin: Builtin
| Optional: Optional
| Repetition: Repetition
| RepetitionSep: RepetitionSep
| Once: Once
| OnceSep: OnceSep
| Join: Join
node NonTerminal := (
rule_name: Identifier::Simple,
specs: {{ Operator::Concat, Identifier::Simple }}
)
node ExternalRule := (
lib_name: Identifier::LibName,
specs: [Operator::Concat << Operator::Concat , Identifier::Simple >>]
)
node Optional := Delimiter::LBracket Expression Delimiter::RBracket
node Repetition := Delimiter::LBrace Expression Delimiter::RBrace
node RepetitionSep := (
separator: Delimiter::LDBrace Expression Delimiter::Comma,
expression: Expression Delimiter::RDBrace
)
node Once := Delimiter::LAngle Expression Delimiter::RAngle
node OnceSep := (
separator: Delimiter::LDAngle Expression Delimiter::Comma,
expression: Expression Delimiter::RDAngle
)
node Join := Delimiter::Hash Expression Delimiter::Hash
special Builtin :=
| Float: @builtin::Float
| Integer: @builtin::Integer
| Char: @builtin::Char
| String: @builtin::String
| Regex: @builtin::Regex
special Comment := "--" /[^\n]*/ "\n"
special Keyword :=
| Node: "node"
| Special: "special"
| Skip: "skip"
| Morg: "morg"
| Start: "start"
special Operator :=
| Definition: ":="
| TypeDefinition: ":"
| Concat: "::"
special Delimiter :=
| Comma: ","
| Pipe: "|"
| LParen: "("
| RParen: ")"
| LBracket: "["
| RBracket: "]"
| LBrace: "{"
| RBrace: "}"
| LDBrace: "{{"
| RDBrace: "}}"
| LAngle: "<"
| RAngle: ">"
| LDAngle: "<<"
| RDAngle: ">>"
| Hash: "#"
special Identifier :=
| Simple: SimpleIdentifier
| LibName: LibName
node SimpleIdentifier := /[a-zA-Z_][a-zA-Z0-9_]*/
node LibName := /@[a-zA-Z_][a-zA-Z0-9_]*/
skip /[\s]/