UBC-Stat-ML / blangDSL

Blang core (parsing, generation, eclipse plug-in)
https://www.stat.ubc.ca/~bouchard/blang/
BSD 2-Clause "Simplified" License
2 stars 0 forks source link

Improve Supplier generation #6

Open cubranic opened 8 years ago

cubranic commented 8 years ago

http://stackoverflow.com/q/34434562

cubranic commented 8 years ago

A minimal example capturing some of the ideas from the Blang DSL: repo

Example DSL source file:

model {
    task {
        val x = 2*5;
        Math.pow(2, x)
    }
}

This should compile to:

import java.util.Collection;

@SuppressWarnings("all")
public class MyFile {
  public Collection<Runnable> tasks() {
    ArrayList<Runnable> tasks = new ArrayList<>();
    tasks.add(getTask0());
    return tasks;
  }

  public static Runnable getTask0() {
    Runnable _runnable = new Runnable() {
      public void run() {
        final int x = (2 * 5);
        Math.pow(2, x);
      }
    }
    return _runnable;
  }
}
alexandrebouchard commented 8 years ago

Should the contents of the task be valid Xbase? Then I think int x = 2*5; should be var int x = 2*5; or just var x = 2*5

cubranic commented 8 years ago

Sorry, that was a typo on my part.

cubranic commented 8 years ago

But I can't just say:

model {
  task {
    1+1
  }
}

because it is compiled to:

... new Runnable() {
  public void run() {
    /* ( 1+1 ) */
  }
}

In this case, because 1+1 is a NumberLiteral, which toJavaStatement compiles to its commented-out translation. But doing it as with toJavaExpression would work (in this case). šŸ˜•

cubranic commented 8 years ago

StackOverflow question

cubranic commented 8 years ago

The answer from SO is to stay in the ā€œmodel inferrerā€ world if possible and just use multiple methods:

public static Runnable getTask0() {
    Runnable _runnable = new Runnable() {
        public void run() {
             doRun();
        }
    }
    return _runnable;
}

public static void doRun() {
    final int x = (2 * 5);
    Math.pow(2, x);
}
cubranic commented 8 years ago

Related discussion on Eclipse forums They give the same suggestion:

from your source eobject derive 2 methods 1.

body = thing.bodyexpression

2.

body = '''
try {
<<namefrom1>>(....);
} ....
'''

This also spawned a feature request on Eclipseā€™s Bugzilla bug 471992

alexandrebouchard commented 8 years ago

Interesting! Ok, I have voted for that bug.

I think the workaround will have some trickiness in our case, because of the arguments to pass in. If we pass everything, this will defy the dependency inference trick I was telling you about on your board the other day. I can see at least a couple of directions from here:

cubranic commented 8 years ago

Exactly. I added a comment on the suggestion to say why I really want to go the anonymous subclass route.

Maybe in the short term we should just create the static inner class using the inferrer with the dumb way of including all dependencies and take the performance hit. Then if we make get more help on using the approach with the custom compiler & type computer, we can switch to that.

If we don't get any further help on this topic, we can always add smarts to the anonymous inner class generation by only creating fields for dependencies that appear as free variables in the block. There are some existing "validators" that check for duplicate names and/or only referring to already-existing names, so maybe they could be repurposed because they also work by walking the expression tree.

alexandrebouchard commented 8 years ago

Sounds gold!

cubranic commented 8 years ago

We're getting somewhere:

import blang.core.Model;
import blang.core.ModelComponent;
import blang.prototype3.Real;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Supplier;

@SuppressWarnings("all")
public class MyFile implements Model {
  public final Real mu;

  public final Real y;

  public MyFile(final Real mu, final Real y) {
    this.mu = mu;
    this.y = y;
  }

  public Collection<ModelComponent> components() {
    ArrayList<ModelComponent> components = new ArrayList();

    components.add(new blang.prototype3.Normal(
        y,
        new $Generated_SupplierSubModel0Param0(mu),
        new $Generated_SupplierSubModel0Param1(mu))
    );

    return components;
  }

  public static class $Generated_SupplierSubModel0Param0 implements Supplier<Real> {
    private Real mean;

    public $Generated_SupplierSubModel0Param0(final Real mean) {
      this.mean = mean;
    }

    @Override
    public Real get() {
      return mean;
    }
  }

  public static class $Generated_SupplierSubModel0Param1 implements Supplier<Real> {
    private Real mean;

    public $Generated_SupplierSubModel0Param1(final Real mean) {
      this.mean = mean;
    }

    @Override
    public Real get() {
      final Real _function = new Real() {
        public double doubleValue() {
          double _doubleValue = $Generated_SupplierSubModel0Param1.this.mean.doubleValue();
          return Math.pow(_doubleValue, 2);
        }
      };
      return _function;
    }
  }
}

A little wordy, and the closure is compiled kind of funny, but I think it's the right thing.

alexandrebouchard commented 8 years ago

Adding the key work enhancement, as the static inner class method may create un-needed dependencies. This has no effect on correctness, but may have large performance penalties in certain cases.

alexandrebouchard commented 8 years ago

The main enhancement left here is to reduce the scope of the __generated..(..) methods to pass in only what is actually used inside the XExpression.