Skip to content

Commit

Permalink
use littoral templates for language textualizer
Browse files Browse the repository at this point in the history
  • Loading branch information
dslmeinte committed Sep 17, 2024
1 parent 7ef1e8b commit 2e1a9bd
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 51 deletions.
3 changes: 2 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/test/src/m3/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/languageWithEnum.actual.txt
14 changes: 14 additions & 0 deletions packages/test/src/m3/languageWithEnum.expected.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
language WithEnum
version: 1
entities (↓name):

concept EnumHolder
features (↓name):
enumValue: MyEnum

enumeration MyEnum
literals:
literal1
literal2


Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,21 @@ const {equal} = assert

import {serializeNodes} from "@lionweb/core"
import {genericAsTreeText, languageAsText} from "@lionweb/utilities"
import {readFileSync, writeFileSync} from "fs"

import {languageWithEnum} from "../languages/with-enum.js"
import {libraryExtractionFacade, libraryModel} from "../instances/library.js"
import {libraryLanguage} from "../languages/library.js";
import {libraryLanguage} from "../languages/library.js"


describe("LionCore-specific textual syntax", () => {

it("textualize language with an enum as text", () => {
const actual = languageAsText(languageWithEnum)
writeFileSync("src/m3/languageWithEnum.actual.txt", actual)
equal(
languageAsText(languageWithEnum),
`language WithEnum
version: 1
entities (↓name):
concept EnumHolder
features (↓name):
enumValue: MyEnum
enumeration MyEnum
literals:
literal1
literal2
`
actual,
readFileSync("src/m3/languageWithEnum.expected.txt", { encoding: "utf8" })
)
})

Expand Down
3 changes: 2 additions & 1 deletion packages/utilities/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ It contains utilities on top of the `core` package, such as:

### 0.6.9 - not yet officially released

* Made `withoutAnnotations` _not_ modify the original serialization chunk.
* Make `withoutAnnotations` _not_ modify the original serialization chunk.
* (Use the `littoral-templates` package for textualization — of M2s, so far. This is a technical change, not a functional one, except for maybe some extra whitespace.)

### 0.6.8

Expand Down
79 changes: 46 additions & 33 deletions packages/utilities/src/m3/textualizer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
Annotation,
Classifier,
Concept,
Containment,
Enumeration,
Expand All @@ -16,66 +17,75 @@ import {
SingleRef,
unresolved
} from "@lionweb/core"
import {asString, indentWith, NestedString} from "littoral-templates"


// TODO use littoral-templates?
const indent = (str: string) =>
str.split("\n").map((line) => ` ${line}`).join("\n")


const descent = <T extends M3Node>(ts: T[], separator: string): string =>
nameSorted(ts).map((t) => indent(indent(asText(t)))).join(separator)
const indent1 = indentWith(" ")(1)

const refAsText = <T extends INamed>(ref: SingleRef<T>): string =>
ref === unresolved ? `???` : ref.name

const recurse = <T extends M3Node>(ts: T[], header: string, func: (t: T) => NestedString = asText): NestedString =>
ts.length === 0
? []
: indent1([
header,
indent1(ts.map(func))
])

const featuresOf = (classifier: Classifier): NestedString =>
recurse(nameSorted(classifier.features), `features (↓name):`)

const asText = (node: M3Node): string => {
const asText = (node: M3Node): NestedString => {

if (node instanceof Annotation) {
return `annotation ${node.name}${node.extends === undefined ? `` : ` extends ${refAsText(node.extends)}`}${node.implements.length === 0 ? `` : ` implements ${nameSorted(node.implements).map(nameOf).join(", ")}`}${node.features.length === 0 ? `` : `
features (↓name):
${descent(node.features, "\n")}`}`
return [
`annotation ${node.name}${node.extends === undefined ? `` : ` extends ${refAsText(node.extends)}`}${node.implements.length === 0 ? `` : ` implements ${nameSorted(node.implements).map(nameOf).join(", ")}`}`,
featuresOf(node)
]
}

if (node instanceof Concept) {
return `${node.partition ? `<<partition>> ` : ``}${node.abstract ? `abstract ` : ``}concept ${node.name}${node.extends === undefined ? `` : ` extends ${refAsText(node.extends)}`}${node.implements.length === 0 ? `` : ` implements ${nameSorted(node.implements).map(nameOf).join(", ")}`}${node.features.length === 0 ? `` : `
features (↓name):
${descent(node.features, "\n")}`}`
return [
`${node.partition ? `<<partition>> ` : ``}${node.abstract ? `abstract ` : ``}concept ${node.name}${node.extends === undefined ? `` : ` extends ${refAsText(node.extends)}`}${node.implements.length === 0 ? `` : ` implements ${nameSorted(node.implements).map(nameOf).join(", ")}`}`,
featuresOf(node)
]
}

if (node instanceof Interface) {
return `interface ${node.name}${node.extends.length === 0 ? `` : ` extends ${nameSorted(node.extends).map(nameOf).join(", ")}`}${node.features.length === 0 ? `` : `
features (↓name):
${descent(node.features, "\n")}`}`
return [
`interface ${node.name}${node.extends.length === 0 ? `` : ` extends ${nameSorted(node.extends).map(nameOf).join(", ")}`}`,
featuresOf(node)
]
}

if (node instanceof Link) {
return `${node.name}${node instanceof Containment ? `:` : ` ->`} ${refAsText(node.type)}${node.multiple ? `[${node.optional ? `0` : `1`}..*]` : ``}${node.optional && !node.multiple ? `?` : ``}`
}

if (node instanceof Enumeration) {
return `enumeration ${node.name}${node.literals.length === 0 ? `` : `
literals:
${descent(node.literals, "\n")}`}`
return [
`enumeration ${node.name}`,
recurse(node.literals, `literals:`)
]
}

if (node instanceof EnumerationLiteral) {
return `${node.name}`
}

if (node instanceof Language) {
return `language ${node.name}
version: ${node.version}${node.dependsOn.length > 0
? `\n dependsOn:
${node.dependsOn.map((language) => ` ${language.name} (${language.version})`).join("\n")}
`
: ``}
entities (↓name):
${descent(node.entities, "\n\n")}
`
return [
`language ${node.name}`,
indent1([
`version: ${node.version}`,
recurse(node.dependsOn, `dependsOn`, (language) => `${language.name} (${language.version}`),
`entities (↓name):`,
``,
indent1(nameSorted(node.entities).map((entity) => [asText(entity), ``]))
]),
``
]
}

if (node instanceof PrimitiveType) {
Expand All @@ -91,8 +101,11 @@ ${descent(node.entities, "\n\n")}
}


export const languageAsText = asText
export const languageAsText = (language: Language) =>
asString(asText(language))

export const languagesAsText = (languages: Language[]): string =>
nameSorted(languages).map(asText).join("\n\n")
asString(
nameSorted(languages).map((language) => [asText(language), ``])
)

0 comments on commit 2e1a9bd

Please sign in to comment.