--- title: "Assembling Pages" series: "Mostly Oberon" number: 13 author: "rsdoiel@gmail.com (R. S. Doiel)" date: "2020-10-19" keywords: [ "Oberon-07", "portable", "markdown", "pandoc", "frontmatter" ] copyright: "copyright (c) 2020, R. S. Doiel" license: "https://creativecommons.org/licenses/by-sa/4.0/" --- Assembling pages ================ This is the thirteenth post in the [Mostly Oberon](https://rsdoiel.github.io/blog/2020/04/11/Mostly-Oberon.html) series. Mostly Oberon documents my exploration of the Oberon Language, Oberon System and the various rabbit holes I will inevitably fall into. Pandoc and JSON --------------- I use [Pandoc](https://pandoc.org) to process Markdown documents. I like to keep my front matter in JSON rather than Pandoc's YAML. Fortunately Pandoc does support working with JSON as a metadata file include. Normally I would manually split the JSON front matter and the rest of the markup into two separate files, then process with Pandoc and other tooling like [LunrJS](https://lunrjs.com). [AssemblePage](AssemblePage.Mod) automates this process. Example shell usage: ~~~ AssemblePage MyText.txt \ metadata=document.json \ document=document.md pandoc --from markdown --to html \ --metadata-file document.json \ --standalone \ document.md >MyText.html ~~~ Source code for **AssemblePage.Mod** ------------------------------------ ~~~ MODULE AssemblePage; IMPORT Out, Strings, Files, Args := extArgs; VAR srcName, metaName, docName : ARRAY 1024 OF CHAR; (* FrontMatter takes a "read" Rider, r, and a "write" Rider "w". If the first character read by r is an opening curly bracket (the start of the front matter) it writes it out with w, until it finds a matching closing curly bracket or the file ends. *) PROCEDURE FrontMatter*(VAR r : Files.Rider; VAR w : Files.Rider); VAR c : BYTE; cCnt : INTEGER; BEGIN (* Scan for opening JSON front matter *) cCnt := 0; REPEAT Files.Read(r, c); IF r.eof = FALSE THEN IF c = ORD("{") THEN cCnt := cCnt + 1; ELSIF c = ORD("}") THEN cCnt := cCnt - 1; END; Files.Write(w, c); END; UNTIL (r.eof = TRUE) OR (cCnt = 0); IF cCnt # 0 THEN Out.String("ERROR: mis matched '{' and '}' in front matter"); ASSERT(FALSE); END; END FrontMatter; (* CopyIO copies the characters from a "read" Rider to a "write" Rider *) PROCEDURE CopyIO*(VAR r : Files.Rider; VAR w: Files.Rider); VAR c : BYTE; BEGIN REPEAT Files.Read(r, c); IF r.eof = FALSE THEN Files.Write(w, c); END; UNTIL r.eof = TRUE; END CopyIO; PROCEDURE ProcessParameters(VAR sName, mName, dName : ARRAY OF CHAR); VAR arg : ARRAY 1024 OF CHAR; i, res : INTEGER; BEGIN mName := "document.json"; dName := "document.txt"; arg := ""; FOR i := 0 TO (Args.count - 1) DO Args.Get(i, arg, res); IF Strings.Pos("metadata=", arg, 0) = 0 THEN Strings.Extract(arg, 9, Strings.Length(arg), mName); ELSIF Strings.Pos("document=", arg, 0) = 0 THEN Strings.Extract(arg, 9, Strings.Length(arg), dName); ELSE Strings.Extract(arg, 0, Strings.Length(arg), sName); END; END; END ProcessParameters; PROCEDURE AssemblePage(srcName, metaName, docName : ARRAY OF CHAR); VAR src, meta, doc : Files.File; reader, writer : Files.Rider; BEGIN src := Files.Old(srcName); IF src # NIL THEN Files.Set(reader, src, 0); IF metaName # "" THEN meta := Files.New(metaName); Files.Register(meta); Files.Set(writer, meta, 0); FrontMatter(reader, writer); Files.Close(meta); END; IF docName # "" THEN doc := Files.New(docName); Files.Register(doc); Files.Set(writer, doc, 0); CopyIO(reader, writer); Files.Close(doc); END; ELSE Out.String("ERROR: Could not read ");Out.String(srcName);Out.Ln(); ASSERT(FALSE); END; Files.Close(src); END AssemblePage; BEGIN ProcessParameters(srcName, metaName, docName); AssemblePage(srcName, metaName, docName); END AssemblePage. ~~~ ### Next, Previous + Next [Dates & Clock](../../11/27/Dates-and-Clock.html) + Previous [Oberon To Markdown](../../10/03/Oberon-to-markdown.html)