Question

I wonder if Dart is capable of executing closures in runtime with such delegate support what Groovy is capable of? See this example for better understanding: A Groovy DSL from scratch in 2 hours.

I have a DSL written in Groovy for easily defining MARC library records. I would love to be able to process a similar Dart script natively by binding definition calls to a delegate class in my program.

record {
   leader "00000nam a2200000 u 4500"
   controlfield "001", "LIB001"
   controlfield "005", "20120311123453.0"
   datafield("100") {
     subfield "a", "Author of record"
   }
   datafield("245", "0") {
     subfield "a", "Title of record"
   }
}

You may wonder: why this can't be expressed in JSON? With such a DSL i can do a lot more than expressing data. Because it's embedded you can do anything inside the DSL which is valid in the host language (this case Groovy). You can do a for loop if you have to define the same thing multiple times only with different values, you can use GString expressions, call a database, access files, etc. With a DSLD defined the IDE is knowing your concept just like if it was always part of the language, it can offer you assisting tools. It's highly expressive and intuitive.

A similar thing for Dart is what i'm looking for.

Was it helpful?

Solution

Dart does not have any built-in support for DSLs. However, you can use method cascades and operator overloading to achieve basic DSLs in some cases.

For your example, only method cascades are needed. You can see some good examples of operator overloading in the parsers library and the Fuzzy Logic library.

Here is what a method cascades version of your code could look like:

new Record()
  ..leader = '00000nam a2200000 u 4500'
  ..controlfield('001', 'LIB001')
  ..controlfield('005', '20120311123453.0')
  ..datafield('100', '',
      new Subfield('a', 'Author of record'))
  ..datafield('245', '0',
      new Subfield('a', 'Title of record'));

Loops cannot be added within this, but you can define methods that accept data and a function to create fields based on that data:

List data = [
               ['a', 'Title of record'],
               ['a', 'Something of record']
              ];
// Same record code from above with the addition of this line:
  ..datafields('245', '', data, (e) => new Subfield(e[0], e[1]));

The examples use the following supporting classes:

class Record {
  String leader;
  List<ControlField> controlFields = [];
  List<DataField> datafieldList = [];

  void controlfield(String a, String b) {
    controlFields.add(new ControlField(a, b));
  }

  void datafield(String a, String b, Subfield subfield) {
    datafieldList.add(new DataField(a, b, subfield));
  }

  void datafields(String a, String b, Iterable data, Subfield f(E e)) {
    data.forEach( (e) {
      datafieldList.add(new DataField(a, b, f(e)));
    });
  }

}

class ControlField {

  String a;
  String b;

  ControlField(this.a, this.b);
}

class DataField {

  String a;
  String b;
  Subfield subfield;

  DataField(this.a, this.b, this.subfield);
}

class Subfield {

  String a;
  String b;

  Subfield(this.a, this.b);
}

As I am unfamiliar with MARC records, I've used the rather unhelpful field names a and b for all fields, feel free to change them to something appropriate. Also I am sure some of the assumptions I made on the structure based on your code snippet are wrong, but they should probably be changed easily.

OTHER TIPS

You should look into sweet.js which provide hygienic macros for JavaScript. You can write the macros to accept the DSL you want. When compiled you get JavaScript file which you have to include. Then you can use the DSL directly from JavaScript or Dart.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top