-- 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]/