How to pass an Array of Expr (expressions) to Haxe Macro? - arrays

I'm basically trying to create a macro that automatically generates an if/else if chain by supplying one common outcome statement for all conditions.
This is what I've tried so far (modified the code just to show as an example):
import haxe.macro.Expr;
class LazyUtils {
public macro static function tryUntilFalse( xBool:Expr, xConds:Array<Expr> ) {
var con1, con2, con3, con4, con5;
/*
* Here's a switch to handle specific # of conditions, because for-loops
* don't seem to be allowed here (at least in the ways I've tried so far).
*
* If you know how to use for-loop for this, PLEASE do tell!
*/
switch(xConds.length) {
case 1: {
con1 = conds[0];
return macro {
if (!$con1) $xBool;
}
}
case 2: {
con1 = conds[0];
con2 = conds[1];
return macro {
if (!$con1) $xBool;
else if (!$con2) $xBool;
}
}
case 3: {
con1 = conds[0];
con2 = conds[1];
con3 = conds[2];
return macro {
if (!$con1) $xBool;
else if (!$con2) $xBool;
else if (!$con3) $xBool;
}
}
// ... so on and so forth
}
return macro { trace("Unhandled length of conditions :("); };
}
}
Then, in theory it could be used like this:
class Main {
static function main() {
var isOK = true;
LazyUtils.tryUntilFalse( isOK = false, [
doSomething(),
doSomethingElse(), //Returns false, so should stop here.
doFinalThing()
]);
}
static function doSomething():Bool {
// ???
return true;
}
static function doSomethingElse():Bool {
// ???
return false;
}
static function doFinalThing():Bool {
return true;
}
}
Which should generate this condition tree:
if (!doSomething()) isOK = false;
else if (!doSomethingElse()) isOK = false;
else if (!doFinalThing()) isOK = false;
Alternatively, I suppose it could output this instead:
if(!doSomething() || !doSomethingElse() || !doFinalThing()) isOK = false;
Looking back at this now, true - it may not make much sense to write a whole macro to generate code that would be easier to type out in it's raw format.
But for the sake of learning about macros, does anyone know if multiple expressions can be passed in an Array<Expr> like I tried in the above code sample?

You probably couldn't get the xConds argument to behave like you expected because the final argument of an expression macro with the type Array<Expr> is implicitly a rest argument. That means you ended up with an array that contained a single EArrayDecl expression. This can be fixed by simply omitting the [].
Regarding generating the if-else-chain - let's take a look at EIf:
/**
An `if(econd) eif` or `if(econd) eif else eelse` expression.
**/
EIf( econd : Expr, eif : Expr, eelse : Null<Expr> );
The chain can be thought of as a singly linked list - the eelse if the first EIf should reference the next EIf and so forth, until we stop with eelse = null for the last EIf. So we want to generate this for your example (pseudo-code):
EIf(doSomething(), isOk = false, /* else */
EIf(doSomethingElse, isOk = false, /* else */
EIf(doFinalThing(), isOk = false, null)
)
)
Recursion works well for this.
Typically it's more convenient to work with reification than raw expressions like I do here, but I'm not sure the former is really possible when dynamically generating expressions like this.
import haxe.macro.Context;
import haxe.macro.Expr;
class LazyUtils {
public macro static function tryUntilFalse(setBool:Expr, conditions:Array<Expr>):Expr {
return generateIfChain(setBool, conditions);
}
private static function generateIfChain(eif:Expr, conditions:Array<Expr>):Expr {
// get the next condition
var condition = conditions.shift();
if (condition == null) {
return null; // no more conditions
}
// recurse deeper to generate the next if
var nextIf = generateIfChain(eif, conditions);
return {
expr: EIf(condition, eif, nextIf),
pos: Context.currentPos()
};
}
}
And Main.hx (mostly unchanged):
class Main {
static function main() {
var isOK = true;
LazyUtils.tryUntilFalse(isOK = false,
!doSomething(),
!doSomethingElse(), //Returns false, so should stop here.
!doFinalThing()
);
}
static function doSomething():Bool {
trace("doSomething");
return true;
}
static function doSomethingElse():Bool {
trace("doSomethingElse");
return false;
}
static function doFinalThing():Bool {
trace("doFinalThing");
return true;
}
}
To keep things simple I inverted the function call arguments with ! at the call site instead of handling that in the macro.
You can use -D dump=pretty to generate AST dumps and check what code is being generated. Here's the result:
if ((! Main.doSomething()))isOK = false else if ((! Main.doSomethingElse()))isOK = false else if ((! Main.doFinalThing()))isOK = false;

Related

Swift Async/Await for C Callbacks

I am trying to convert some code from objective-c to swift and along with that use async/await as much as possible.
This code leverages a C library that uses callbacks with a void* context. There are parts of our code where we need to "wait" until the callback is completed. This can often be 2 different callbacks for success or failure.
Below is an example of the code
class Test
{
var sessionStarted: Bool = false
let sessionSemaphore = DispatchSemaphore(value: 0)
// We need the session to be available once this function returns
func startSession() throws
{
guard !sessionStarted else {
print("Session already started")
return
}
let callback: KMSessionEventCallback = { inSession, inEvent, inContext in
guard let session = inSession, let context = inContext else { return }
let testContext: Test = Unmanaged<Test>.fromOpaque(context).takeUnretainedValue()
testContext.handleSessionUpdate(session: session, event: inEvent)
}
let result = KMSessionAttach("SessionTest", &callback, Unmanaged.passUnretained(self).toOpaque())
if result != KM_SUCCESS {
print("Failed to attach session")
}
// Currently uses a semaphore to wait. =(
let dispatchResult = self.sessionSemaphore.wait(timeout: .now() + DispatchTimeInterval.seconds(30))
if dispatchResult == .timedOut {
print("Waited 30 seconds and never attached session")
}
}
private func handleSessionUpdate(session: KMSession, event: KMSessionEvent)
{
switch event
{
case KM_SESSION_ATTACHED:
self.sessionStarted = true
case KM_SESSION_TERMINATED:
fallthrough
case KM_SESSION_FAILED:
fallthrough
case KM_SESSION_DETACHED:
self.sessionStarted = false
default:
print("Unknown State")
}
self.sessionSemaphore.signal()
}
}
I am struggling to figure out how to use continuations here because I cannot capture any context within these C callbacks.

using lodash _find to return true or false

I'm trying to use lodash function to find if it satisfies the if statement. When the code has run the code which 'return false', it stills continue execute the _find function and did not return the result (boolean value) in the scope.onToggle.
$scope.onToggle = function(disposition) {
if (requiredFieldsEntered()===false){
// if return value from the function is false, alert user
}
}
function requiredFieldsEntered(){
var res = _.find($rootScope.CustomFields,
function(field){
if(field.enabled && field.required){
if(field.table_name === 'session'){
if(!$scope.session.external_id){
console.log(field.column_name);
return false;
}
else if (field.table_name === "session_extension"){
if(!$scope.session.extension|| ($scope.session.extension && !$scope.session.extension[field.column_name])){
console.log(field.column_name);
return false;
}
}
}
}});
if (res) return false;
else return true;
}
First let's stop and consider what _.find actually does. It loops over an array and returns the first element of the array that matches the condition.
_.find([1, 2, 3, 4], function(num) { return num % 2 === 0 });
// returns 2
In the case of your _.find function, then, you'll either get back one of the fields from $rootScope.CustomFields or undefined if none of the fields returns a truthy value from function(field)….
Here is where you have your problem. The anonymous function function(field)… only ever returns false. You need to return true if you find an error, or else return false otherwise.
Since your requiredFieldsEntered() function is ultimately trying to return either true or false, you can decide which of those to return based on whether or not your _.find function returns some object or undefined.
Something like this:
$scope.onToggle = function(disposition) {
if (requiredFieldsEntered()===false){
// if return value from the function is false, alert user
}
}
function requiredFieldsEntered(){
const fieldWithErrors = _.find($rootScope.CustomFields,
function(field){
let result = true;
if(field.enabled && field.required){
if(field.table_name === 'session'){
if(!$scope.session.external_id){
console.log(field.column_name);
result = false;
} else if (field.table_name === "session_extension"){
if(!$scope.session.extension|| ($scope.session.extension && !$scope.session.extension[field.column_name])){
console.log(field.column_name);
result = false;
}
}
}
}
return result;
});
if (fieldWithErrors) {
return false;
}
return true;
}
Quick note that a shorter but somewhat less-readable version at the end may replace this bit:
…
if (fieldWithErrors) {
return false;
}
return true;
}
With:
…
return !!!fieldWithErrors;
The first two exclamation points would be typecasting the result of your find function to either true if it comes up with something or false if it returns undefined, and then the third exclamation point would invert that boolean to match your current scheme of returning false if there are errors.
(Or you could change your function name to errorsArePresent and return true – !!fieldWithErrors).

How to check if Value of an array is true in a cell(Google Script)

I am having issues checking if the string value in my array is true in a cell.
function myFunction() {
var People = ['Amanda', 'John'];
for (var n in People )
{
if( People[0] == true);
Logger.log("BOOKED");
}
else{
Logger.log("FREE");
}
}
Plenty of issues with the code!
Does this do it ?
function myFunction() {
var People = ['Amanda', 'John'];
for (var n=0;n<People.length;n++) {
if( People[n] ) {
Logger.log("BOOKED");
}
else {
Logger.log("FREE");
}
}
}
I'm not sure if you just want to test for members of the array being present, or if you want to test actual names in the array

Issue using filter AngularJS

I'm using $filter to iterate through an array and fetch a specific value
Below is my code:
var selected = $filter('filter')($scope.folders, {url: el.selected[0] });
This code is working, but I got a problem when the url contain an accent and space like so :
/Users/Me/project/products/Poste à souder
In that case the string comparaison isn't working anymore.
What is the cleaner way to solve this situation ?
That true. As a francophone, I've often encounter encoding/decoding issues with angularjs.
The source code of the default filter is as follow
function filterFilter()
{
return function(array, expression, comparator)
{
if (!isArrayLike(array))
{
if (array == null)
{
return array;
}
else
{
throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
}
}
var expressionType = getTypeForFilter(expression);
var predicateFn;
var matchAgainstAnyProp;
switch (expressionType)
{
case 'function':
predicateFn = expression;
break;
case 'boolean':
case 'null':
case 'number':
case 'string':
matchAgainstAnyProp = true;
//jshint -W086
case 'object':
//jshint +W086
predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
break;
default:
return array;
}
return Array.prototype.filter.call(array, predicateFn);
};
}
and the predicate generator stand as follow: it generate the default comparator if the provided one is not a function
function createPredicateFn(expression, comparator, matchAgainstAnyProp)
{
var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
var predicateFn;
if (comparator === true)
{
comparator = equals;
}
else if (!isFunction(comparator))
{
comparator = function(actual, expected)
{
if (isUndefined(actual))
{
// No substring matching against `undefined`
return false;
}
if ((actual === null) || (expected === null))
{
// No substring matching against `null`; only match against `null`
return actual === expected;
}
if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual)))
{
// Should not compare primitives against objects, unless they have custom `toString` method
return false;
}
actual = lowercase('' + actual);
expected = lowercase('' + expected);
return actual.indexOf(expected) !== -1;
};
}
predicateFn = function(item)
{
if (shouldMatchPrimitives && !isObject(item))
{
return deepCompare(item, expression.$, comparator, false);
}
return deepCompare(item, expression, comparator, matchAgainstAnyProp);
};
return predicateFn;
}
Too much speech. You have the choice:
Provide a comparator to your filter see the doc
but remember that you can't define inline function in angular template
you can define a function in that scope, but it will only be available in that scope
You can write your own filter
.filter('myCustomFilter', function()
{
return function(input, criteria)
{
... // your logic here
return ...// the filtered values
};
})
Maybe it's best to write your own filter:
app.filter("customFilter", function () {
//the filter will accept an input array, the key you want to look for and the value that the key should have
return function (array, key, value) {
return array.filter(function(x){
return (x.hasOwnProperty(key) && (x[key] === value));
});
};
});
And use it in your controller like:
$scope.filtered = $filter("customFilter")($scope.folders, "url", "/Users/Me/project/products/Poste à souder");
Check out a working demo here.

Using variable A to control variable B

I have a function called FarmClick, and I want it to check the current MovieClip's array. For example:
var farmSpot1:("id",int,int);
var farmSpot2...
I need the clickEvent to capture the MovieClips name and check it's array for the 0th element to see whether it's empty, or not...
My code so far:
public function FarmClick(event: MouseEvent): void {
var CurrentSlot = event.currentTarget.name
if ([event.currentTarget.name[0]] = "empty") {
stage.addChild(menu);
menu.x = 400;
menu.y = 90;
this.menu.buyCornBtn2.addEventListener(MouseEvent.CLICK,buyCorn2);
} else if (farmEmpty == true && itemSelected != "none") {
selected();
} else if (farmEmpty == false) {
farmHarvest();
}
}
It took me a minute to figure out just exactly what it was that you're asking.
So basically, your movie clip's name would be farmSpot[someNumber] and you have an array with the same name?
in that case, to reference the array variable, you need to use the this keyword.
you can add any array subscripts after you've referenced it like so:
private var farmSpot1:Array = ["id", int, int];
public function FarmClick(event:MouseEvent): void
{
if (this[event.currentTarget.name][0] == "") // wasnt sure if you meant the string "empty" or just an empty string, so i just put an empty string
{
// do whatevs
}
}

Resources