Skip to content

Commit

Permalink
[Scala 3 Support metarank#48] Add partial implementation using code f…
Browse files Browse the repository at this point in the history
…rom spire cfor
  • Loading branch information
jpsacha committed Nov 25, 2022
1 parent ee6f803 commit 450c7de
Showing 1 changed file with 72 additions and 5 deletions.
77 changes: 72 additions & 5 deletions macros/src/main/scala-3/io/github/metarank/cfor/package.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package io.github.metarank.cfor

inline def cfor[A](inline init: A)(inline test: A => Boolean, inline next: A => A)(inline body: A => Any): Unit =
var a = init
while test(a) do
body(a)
a = next(a)
import scala.PartialFunction.cond

//inline def cfor[A](inline init: A)(inline test: A => Boolean, inline next: A => A)(inline body: A => Any): Unit =
// var a = init
// while test(a) do
// body(a)
// a = next(a)

inline def cfor[A](inline init: A)(inline test: A => Boolean, inline next: A => A)(inline body: A => Unit): Unit =
${ cforImpl('init, 'test, 'next, 'body) }

inline def cfor[A](inline array: Array[A])(inline body: A => Unit): Unit =
var i = 0
Expand All @@ -26,3 +31,65 @@ inline def cfor(inline r: Range)(inline body: Int => Unit): Unit =
while i > end do
body(i)
i += r.step


//--------------------------------------------------------------------------
//
// Code from below are fragments of file
// `core/src/main/scala-3/spire/syntax/macros/cforMacros.scala`
// Original copyright and license noted
// OUt out 133 lines 37 are uised here.
//

/*
* **********************************************************************\
* * Project **
* * ______ ______ __ ______ ____ **
* * / ____/ / __ / / / / __ / / __/ (c) 2011-2021 **
* * / /__ / /_/ / / / / /_/ / / /_ **
* * /___ / / ____/ / / / __ / / __/ Erik Osheim, Tom Switzer **
* * ____/ / / / / / / / | | / /__ **
* * /_____/ /_/ /_/ /_/ |_| /____/ All rights reserved. **
* * **
* * Redistribution and use permitted under the MIT license. **
* * **
* \***********************************************************************
*/
import scala.quoted.*
def cforImpl[R: Type](init: Expr[R], test: Expr[R => Boolean], next: Expr[R => R], body: Expr[R => Unit])(using
Quotes
): Expr[Unit] =
import quotes.reflect.*

def code(testRef: Expr[R => Boolean], nextRef: Expr[R => R], bodyRef: Expr[R => Unit]): Expr[Unit] = '{
var index = $init
while $testRef(index) do
$bodyRef(index)
index = $nextRef(index)
}

letFunc("test", test)(t => letFunc("next", next)(n => letFunc("body", body)(b => code(t, n, b))))
end cforImpl

/**
* Equivalent to `'{ val name: A => B = $rhs; ${in('name)} }`, except when `rhs` is a function literal, then equivalent
* to `in(rhs)`.
*
* This allows inlined function arguments to perform side-effects only once before their first evaluation, while still
* avoiding the creation of closures for function literal arguments.
*/
private def letFunc[A, B, C](using Quotes)(name: String, rhs: Expr[A => B])(in: Expr[A => B] => Expr[C]): Expr[C] =
import quotes.reflect.*

extension (t: Term) def unsafeAsExpr[A] = t.asExpr.asInstanceOf[Expr[A]] // cast without `quoted.Type[A]`

def isFunctionLiteral[A, B](f: Expr[A => B]): Boolean = cond(f.asTerm.underlyingArgument) { case Lambda(_, _) =>
true
}

def let[A, B](name: String, rhs: Expr[A])(in: Expr[A] => Expr[B])(using Quotes): Expr[B] =
// Equivalent to `'{ val name = $rhs; ${in('name)} }`
ValDef.let(Symbol.spliceOwner, name, rhs.asTerm)(ref => in(ref.unsafeAsExpr[A]).asTerm).unsafeAsExpr[B]

if isFunctionLiteral(rhs) then in(Expr.betaReduce(rhs))
else let(name, rhs)(in)

0 comments on commit 450c7de

Please sign in to comment.