Table of Contents

Language definition

A Hop.js language is a DSL or a complete language that can be used to implement an whole module content or a function body. There are two possibility for using a module MOD implemented in a language LANG.

  1. When MOD is required, LANG is passed as a second argument to require. See chapter HopScript modules. for details on module importations.
  2. The MOD header contains a "use LANG" declaration. This second form is possible only when the LANG is an extension of JavaScript, that is to be parsed by the Hop compiler parser.

Language implementation

A language is any JavaScript object that implements a compilation function associated with the Symbol.compiler property. This function accepts two parameters, a resolved name of file to be compiled, and an optional options object. If the options is undefined or if the options.target property is undefined, it is to the compiler to decide where and how to communicate its result to the Hop runtime system. A compilation result must be an object with two properties:

For instance, if the language compiler wants to communicate that its result is store into a file, it should return a JavaScript object like:

{ type: "filename", value: "/tmp/foo.tmp/js" }

When the compiler is to decide the file name where to store its result, it might make sense for it to use the standard Hop cache directory, whose name might be obtained with:

require( hop.config ).cacheDir

Example

This example shows how to define a new language and how to require modules implemented in that language. This verfy simple example defines CSV (comma separated value) a new language for literals.

For maximmal efficiency, the CSV loader uses the native Hop parser implemented in Scheme. This example also illustrates how to bind this CSV parser into JavaScript.

Let's start with a sample csv file.

lang/sample.csv

Year,Make,Model,Description,Price
1997,Ford,E350,"ac, abs, moon",3000.00
1999,Chevy,"Venture ""Extended Edition""","",4900.00
1999,Chevy,"Venture ""Extended Edition, Very Large""",,5000.00
1996,Jeep,Grand Cherokee,"MUST SELL!
air, moon roof, loaded",4799.00

The client file requires the sample.csv file as if it was a regular Hop.js module, but it specifies that this module is implemented in the csvg.js language.

lang/lang.js

"use hopscript";
const csv = require( "./sample.csv", "./csv.js" );

console.log( "csv=", csv );

service lang( o ) {
   return <html>
     <table>
       <tr>${csv[ 0 ].map( h => <th>${h}</th> )}</tr>
       ${csv.slice( 1 ).map( r => <tr>${r.map( e => <td>${e}</td> )}</tr> )}
     </table>
   </html>
}

The csv.js language is implemented as follows:

lang/csv.js

"use hopscript";

const csvloader = require( "./csv.hop" );
const fs = require( "fs" );

exports[ Symbol.compiler ] = (file, options) => {
   const val = csvloader.load( file, options );
   
   if( options && options.target ) {
      var fd = fs.openSync( options.target, "w" );
      try {
         var buf = JSON.stringify( val );
         fs.write( fd, buf, 0, buf.length );
         
         return {
            type: "filename",
            value: target,
         }
      } finally {
         fs.closeSync( options.target );
      }
   } else {
      return {
         type: "value",
         value: val,
      }
   }
}

The JavaScript language is a mere wrapper of the native Hop csv parser that is implemented as:

lang/csv.hop

(module csv
   (library hopscript hop hopwidget nodejs web csv)
   (export (hopscript %this this %scope %module)))

;*---------------------------------------------------------------------*/
;*    hopscript ...                                                    */
;*    -------------------------------------------------------------    */
;*    This is the function called by JavaScript when the Hop module    */
;*    is required. It binds the exports field of the newly             */
;*    allocated module                                                 */
;*---------------------------------------------------------------------*/
(define (hopscript %this this %scope %module)
   
   (define (literal->js v)
      (cond
         ((string? v) (js-string->jsstring v))
         ((number? v) (js-number->jsnumber v))
         (else v)))
   
   (with-access::JsGlobalObject %this (js-object)
      (let ((exports (js-new0 %this js-object)))
         (js-put! %module (& "exports") exports #f %this)
         ;; bind the load function is the pseudo javascript module
         (js-put! exports (& "load")
            (js-make-function %this
               ;; wrap the Hop read-csv records function into a JS function
               (lambda (this url options)
                  (call-with-input-file url
                     (lambda (p)
                        (js-vector->jsarray
                           (apply vector
                              (map (lambda (row)
                                      (js-vector->jsarray
                                         (apply vector (map literal->js row))
                                         %this))
                                 (read-csv-records p +csv-lexer+)))
                           %this))))
               2 "load")
            #f %this))))

Hopc parser plugins

The hopc parser can be extended with plugins that can be used to extend the syntax it analyses. This feature is still experimental and will be described when stabiliez. In the meantime, an example can be found in the implementation of the HipHop language.

Require extension

Hop extends the require form as follows:

<require> 
  require( <ModuleExpr> )
  | require( <ModuleExpr>, <LangExpr> )
  | require( <ModuleExpr>, <LangExpr>, <CompLangExpr> )

The first form is the original require form. The second, loads <ModuleExpr> defined in language <LangExpr>. The third, loads <ModuleExpr> defined in language <LangExpr>, passing <CompLangexpr> to the <LangExpr> compiler.