packages feed

zeolite-lang-0.24.1.0: example/highlighter/main.0rx

/* -----------------------------------------------------------------------------
Copyright 2023-2024 Kevin P. Barry

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
----------------------------------------------------------------------------- */

// Author: Kevin P. Barry [ta0kira@gmail.com]

concrete ZeoliteHighlight {
  @type run () -> ()
}

define ZeoliteHighlight {
  $ReadOnlyExcept[]$

  @category ZeoliteParseContext zeoliteSourceContext <- ZeoliteParseContext.new()
      .include<ZeoliteWhitespace>()
      .include<ZeoliteLineComment>()
      .include<ZeoliteBlockComment>()
      .include<ZeoliteUpperSymbol>()
      // Must be before ZeoliteLowerSymbol.
      .include<ZeoliteTestcase>()
      // Must be before ZeoliteOperator. (Due to : in labels.)
      .include<ZeoliteLowerSymbol>()
      .include<ZeoliteScopeQualifier>()
      // Must be before ZeoliteExtras. (Due to \ in escapes.)
      .include<ZeoliteNumber>()
      .include<ZeoliteOperator>()
      .include<ZeoliteParamName>()
      .include<ZeoliteStringLiteral>()
      .include<ZeoliteCharLiteral>()
      .include<ZeoliteBraceSection>()
      .include<ZeoliteParenSection>()
      .include<ZeoliteSquareSection>()
      .include<ZeolitePragma>()
      .include<ZeoliteExtras>()

  run () {
    if (Argv.global().size() < 3) {
      \ BasicOutput.stderr()
          .append(Argv.global().readAt(0))
          .append(" [page title] [.0r? input files]\n")
      exit(1)
    }
    \ writeHtmlStart(title: Argv.global().readAt(1))
    traverse (Counter.builder().start(2).limit(Argv.global().size()).done() -> Int i) {
      String filename <- Argv.global().readAt(i)
      $ReadOnly[filename]$
      scoped {
        optional String content <- loadFile(filename: filename)
      } in if (`present` content) {
        \ processFile(filename: filename, content: `require` content)
      }
    }
    \ writeHtmlEnd()
  }

  @type writeHtmlStart (String title:) -> ()
  writeHtmlStart (title) {
    Formatted escapedTitle <- HtmlContent.escape(content: title)
    // TODO: Use HtmlContent to format this stuff?
    \ BasicOutput.stdout()
        .append("<!DOCTYPE html>")
        .append("<html>")
        .append("<head>")
        .append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />")
        .append("<meta name=\"Generator\" content=\"ZeoliteHighlighter\" />")
        .append("<title>").append(escapedTitle).append("</title>")
        .append("</head>")
        .append("<body>")
        .append("<h1>").append(escapedTitle).append("</h1>\n")
    \ writeCommand()
    \ BasicOutput.stdout().append("<hr>")
  }

  @type writeCommand () -> ()
  writeCommand () {
    \ BasicOutput.stdout()
        .append("<p>This file was generated by <a href=\"https://github.com/ta0kira/zeolite/tree/master/example/highlighter\" style=\"font-family:monospace;\">ZeoliteHighlight</a>.</p>")
  }

  @type writeHtmlEnd () -> ()
  writeHtmlEnd () {
    \ BasicOutput.stdout()
        .append("</body>")
        .append("</html>")
  }

  @type processFile (String filename:, String content:) -> ()
  processFile (filename, content) {
    TextStream input <- TextStream.new(content)
    $Hidden[content]$
    DefaultOrder<ZeoliteParsed> output <- defer
    \ StreamTokenizer:new(context: zeoliteSourceContext, tokenizer: zeoliteSourceContext.defaultTokenizer())
        .tokenizeAll(input, (output <- Vector<ZeoliteParsed>.new()))

    if (input.atEnd()) {
      \ BasicOutput.stderr()
          .append("Successfully parsed file ")
          .append(filename)
          .append(":\n")
    } else {
      \ BasicOutput.stderr()
          .append("Incomplete parse of file ")
          .append(filename)
          .append(" at line ")
          .append(input.currentLine())
          .append(" char ")
          .append(input.currentChar())
          .append(":\n")
    }

    ZeoliteHtmlFormatter formatter <- ZeoliteHtmlFormatter.new()
    \ BasicOutput.stdout().append("<h2>").append(HtmlContent.escape(content: filename)).append("</h2>\n")
    \ BasicOutput.stdout().append("<pre style=\"background:#fafffa;\">\n")
    traverse (output.defaultOrder() -> ZeoliteParsed parsed) {
      \ BasicOutput.stdout().append(parsed.formatWith(formatter))
    }
    \ BasicOutput.stdout().append("</pre>\n")
  }

  @type loadFile (String filename:) -> (optional String)
  loadFile (filename) {
    \ BasicOutput.stderr()
        .append("Reading file ")
        .append(filename)
        .append("...\n")
    scoped {
      RawFileReader input <- RawFileReader.open(filename)
    } cleanup {
      \ input.freeResource()
    } in if (`present` input.getFileError()) {
      \ BasicOutput.stderr()
          .append("Error reading file ")
          .append(filename)
          .append(": ")
          .append(`require` input.getFileError())
          .append("\n")
      return empty
    } else {
      return TextReader.readAll(input)
    }
  }
}