Skip to content

Commit

Permalink
Merge pull request #47 from KacperFKorban/missing-primitives
Browse files Browse the repository at this point in the history
Missing primitives
  • Loading branch information
KacperFKorban authored Apr 14, 2024
2 parents 90336c7 + 5904cd5 commit 936f9e6
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 48 deletions.
63 changes: 39 additions & 24 deletions guinep/src/main/scala/macros.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,20 @@ private[guinep] object macros {
extension (t: Term)
private def select(s: Term): Term = Select(t, s.symbol)
private def select(s: String): Term =
t.select(
t.tpe
.typeSymbol
.methodMember(s)
.headOption.
getOrElse(report.errorAndAbort(s"PANIC: No member $s in term ${t.show} with type ${t.tpe.show}"))
)
val sym = t.tpe
.typeSymbol
.methodMember(s)
.headOption
.getOrElse(report.errorAndAbort(s"PANIC: No member $s in term ${t.show} with type ${t.tpe.show}"))
Select(t, sym)
private def select(s: String, argnum: Int): Term =
val sym = t.tpe
.typeSymbol
.methodMember(s)
.filter(_.paramSymss.flatten.size == argnum)
.headOption
.getOrElse(report.errorAndAbort(s"PANIC: No member $s in term ${t.show} with type ${t.tpe.show}"))
Select(t, sym)

extension (s: Symbol)
private def prettyName: String =
Expand Down Expand Up @@ -133,12 +140,18 @@ private[guinep] object macros {
FormElement.NumberInput(paramName, Types.IntType.Short)
case ntpe: NamedType if ntpe =:= TypeRepr.of[Byte] =>
FormElement.NumberInput(paramName, Types.IntType.Byte)
case ntpe: NamedType if ntpe =:= TypeRepr.of[BigInt] =>
FormElement.NumberInput(paramName, Types.IntType.BigInt)
case ntpe: NamedType if ntpe =:= TypeRepr.of[Boolean] =>
FormElement.CheckboxInput(paramName)
case ntpe: NamedType if ntpe =:= TypeRepr.of[Float] =>
FormElement.FloatingNumberInput(paramName, Types.FloatingType.Float)
case ntpe: NamedType if ntpe =:= TypeRepr.of[Double] =>
FormElement.FloatingNumberInput(paramName, Types.FloatingType.Double)
case ntpe: NamedType if ntpe =:= TypeRepr.of[BigDecimal] =>
FormElement.FloatingNumberInput(paramName, Types.FloatingType.BigDecimal)
case ntpe: NamedType if ntpe =:= TypeRepr.of[Unit] =>
FormElement.HiddenInput(paramName, "Unit")
case AppliedType(ntpe: NamedType, List(tpeArg)) if listLikeSymbolsTypes.contains(ntpe.typeSymbol) =>
FormElement.ListInput(paramName, functionFormElementFromTreeWithCaching("elem", tpeArg), listLikeSymbolsTypes(ntpe.typeSymbol))
case OrType(ltpe, rtpe) if ltpe =:= TypeRepr.of[Null] || rtpe =:= TypeRepr.of[Null] =>
Expand Down Expand Up @@ -249,20 +262,22 @@ private[guinep] object macros {
private def constructArg(paramTpe: TypeRepr, param: Term)(using ConstrContext): Term = {
paramTpe match {
case ntpe: NamedType if ntpe =:= TypeRepr.of[String] =>
param.select("asInstanceOf").appliedToType(ntpe)
param.select("asInstanceOf", 1).appliedToType(ntpe)
case ntpe: NamedType if ntpe =:= TypeRepr.of[Char] =>
param.select("asInstanceOf").appliedToType(ntpe)
param.select("asInstanceOf", 1).appliedToType(ntpe)
case ntpe: NamedType
if ntpe =:= TypeRepr.of[Int] || ntpe =:= TypeRepr.of[Long] || ntpe =:= TypeRepr.of[Short] || ntpe =:= TypeRepr.of[Byte] =>
param.select(s"asInstanceOf").appliedToType(ntpe)
if ntpe =:= TypeRepr.of[Int] || ntpe =:= TypeRepr.of[Long] || ntpe =:= TypeRepr.of[Short] || ntpe =:= TypeRepr.of[Byte] || ntpe =:= TypeRepr.of[BigInt] =>
param.select(s"asInstanceOf", 1).appliedToType(ntpe)
case ntpe: NamedType if ntpe =:= TypeRepr.of[Boolean] =>
param.select("asInstanceOf").appliedToType(ntpe)
case ntpe: NamedType if ntpe =:= TypeRepr.of[Double] || ntpe =:= TypeRepr.of[Float] =>
param.select(s"asInstanceOf").appliedToType(ntpe)
param.select("asInstanceOf", 1).appliedToType(ntpe)
case ntpe: NamedType if ntpe =:= TypeRepr.of[Double] || ntpe =:= TypeRepr.of[Float] || ntpe =:= TypeRepr.of[BigDecimal] =>
param.select(s"asInstanceOf", 1).appliedToType(ntpe)
case ntpe: NamedType if ntpe =:= TypeRepr.of[Unit] =>
param.select("asInstanceOf", 1).appliedToType(ntpe)
case AppliedType(ntpe: NamedType, List(tpeArg)) if listLikeSymbolsTypes.contains(ntpe.typeSymbol) =>
param.select("asInstanceOf").appliedToType(paramTpe)
param.select("asInstanceOf", 1).appliedToType(paramTpe)
case OrType(ltpe, rtpe) if ltpe =:= TypeRepr.of[Null] || rtpe =:= TypeRepr.of[Null] =>
val castedParam = param.select("asInstanceOf").appliedToType(paramTpe)
val castedParam = param.select("asInstanceOf", 1).appliedToType(paramTpe)
'{ if ${param.asExpr} == null then null else ${castedParam.asExpr} }.asTerm
case ntpe if isCaseObjectTpe(ntpe) && ntpe.typeSymbol.flags.is(Flags.Module) =>
Ref(ntpe.typeSymbol.companionModule)
Expand All @@ -275,7 +290,7 @@ private[guinep] object macros {
val paramValue = '{ ${param.asExpr}.asInstanceOf[Map[String, Any]] }.asTerm
val args = fields.collect { case field: ValDef =>
val fieldName = field.name
val fieldValue = paramValue.select("apply").appliedTo(Literal(StringConstant(fieldName)))
val fieldValue = paramValue.select("apply", 1).appliedTo(Literal(StringConstant(fieldName)))
constructArgWithCaching(
field.tpt.tpe.substituteTypes(typeDefParams, ntpe.typeArgs),
fieldValue
Expand All @@ -288,14 +303,14 @@ private[guinep] object macros {
val children = classSymbol.children
val childrenAppliedTpes = children.map(child => appliedChild(child, classSymbol, ntpe.typeArgs)).map(_.stripAnnots)
val paramMap = '{ ${param.asExpr}.asInstanceOf[Map[String, Any]] }.asTerm
val paramName = paramMap.select("apply").appliedTo(Literal(StringConstant("name")))
val paramValue = paramMap.select("apply").appliedTo(Literal(StringConstant("value")))
val paramName = paramMap.select("apply", 1).appliedTo(Literal(StringConstant("name")))
val paramValue = paramMap.select("apply", 1).appliedTo(Literal(StringConstant("value")))
children.zip(childrenAppliedTpes).foldRight[Term]{
'{ throw new RuntimeException(s"Class ${${paramName.asExpr}} is not a child of ${${Expr(className)}}") }.asTerm
} { case ((child, childAppliedTpe), acc) =>
val childName = Literal(StringConstant(child.prettyName))
If(
paramName.select("equals").appliedTo(childName),
paramName.select("equals", 1).appliedTo(childName),
constructArgWithCaching(childAppliedTpe, paramValue),
acc
)
Expand All @@ -316,7 +331,7 @@ private[guinep] object macros {
{ case (sym, List(params: Term)) =>
val args = functionParams(f).zipWithIndex.map { case (valdef, i) =>
val paramTpe = valdef.tpt.tpe
val param = params.select("apply").appliedTo(Literal(IntConstant(i)))
val param = params.select("apply", 1).appliedTo(Literal(IntConstant(i)))
constructArgWithCaching(paramTpe, param)
}.toList
val aply = l.select("apply")
Expand All @@ -325,7 +340,7 @@ private[guinep] object macros {
aply.appliedToNone
else
aply.appliedToArgs(args)
res.select("toString").appliedToNone
res.select("toString", 0).appliedToNone
}
)
Block(
Expand All @@ -337,15 +352,15 @@ private[guinep] object macros {
Symbol.spliceOwner,
MethodType(List("inputs"))(_ => List(TypeRepr.of[List[Any]]), _ => TypeRepr.of[String]),
{ case (sym, List(params: Term)) =>
t.select("toString").appliedToNone
t.select("toString", 0).appliedToNone
}
).asExprOf[List[Any] => String]
case a@Apply(Ident(_), Nil) =>
Lambda(
Symbol.spliceOwner,
MethodType(List("inputs"))(_ => List(TypeRepr.of[List[Any]]), _ => TypeRepr.of[String]),
{ case (sym, List(params: Term)) =>
a.select("toString").appliedToNone
a.select("toString", 0).appliedToNone
}
).asExprOf[List[Any] => String]
case _ =>
Expand Down
10 changes: 10 additions & 0 deletions guinep/src/main/scala/model.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ private[guinep] object model {
enum FloatingType:
case Double
case Float
case BigDecimal

object FloatingType:
given ToExpr[FloatingType] with
Expand All @@ -24,12 +25,15 @@ private[guinep] object model {
'{ FloatingType.Double }
case FloatingType.Float =>
'{ FloatingType.Float }
case FloatingType.BigDecimal =>
'{ FloatingType.BigDecimal }

enum IntType:
case Int
case Long
case Byte
case Short
case BigInt

object IntType:
given ToExpr[IntType] with
Expand All @@ -42,6 +46,8 @@ private[guinep] object model {
'{ IntType.Byte }
case IntType.Short =>
'{ IntType.Short }
case IntType.BigInt =>
'{ IntType.BigInt }

enum ListType:
case List
Expand All @@ -65,6 +71,7 @@ private[guinep] object model {
case NumberInput(override val name: String, underlying: Types.IntType) extends FormElement(name)
case FloatingNumberInput(override val name: String, underlying: Types.FloatingType) extends FormElement(name)
case CheckboxInput(override val name: String) extends FormElement(name)
case HiddenInput(override val name: String, underlying: String) extends FormElement(name)
case Dropdown(override val name: String, options: List[(String, FormElement)]) extends FormElement(name)
case ListInput(override val name: String, element: FormElement, underlying: Types.ListType) extends FormElement(name)
case TextArea(override val name: String, rows: Option[Int] = None, cols: Option[Int] = None) extends FormElement(name)
Expand All @@ -80,6 +87,7 @@ private[guinep] object model {
case CharInput(_) => 0
case NumberInput(_, _) => 1
case FloatingNumberInput(_, _) => 1
case HiddenInput(_, _) => 1
case CheckboxInput(_) => 2
case Dropdown(_, _) => 3
case ListInput(_, _, _) => 3
Expand All @@ -106,6 +114,8 @@ private[guinep] object model {
'{ FormElement.FloatingNumberInput(${Expr(name)}, ${Expr(underlying)}) }
case FormElement.CheckboxInput(name) =>
'{ FormElement.CheckboxInput(${Expr(name)}) }
case FormElement.HiddenInput(name, underlying) =>
'{ FormElement.HiddenInput(${Expr(name)}, ${Expr(underlying)}) }
case FormElement.Dropdown(name, options) =>
'{ FormElement.Dropdown(${Expr(name)}, ${Expr(options)}) }
case FormElement.ListInput(name, element, underlying) =>
Expand Down
33 changes: 33 additions & 0 deletions guinep/src/test/scala/formgentests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,39 @@ class FormGenTests extends munit.FunSuite {
)
)

checkGeneratedFormEquals(
"factorialBigInt",
factorialBigInt,
Form(
Seq(
FormElement.NumberInput("n", Types.IntType.BigInt)
),
Map.empty
)
)

checkGeneratedFormEquals(
"inverseBigDecimal",
inverseBigDecimal,
Form(
Seq(
FormElement.FloatingNumberInput("bd", Types.FloatingType.BigDecimal)
),
Map.empty
)
)

checkGeneratedFormEquals(
"sayBye",
sayBye,
Form(
Seq(
FormElement.HiddenInput("unit", "Unit")
),
Map.empty
)
)

checkGeneratedFormEquals(
"isInTree",
isInTree,
Expand Down
21 changes: 21 additions & 0 deletions guinep/src/test/scala/rungentests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,27 @@ class RunGenTests extends munit.FunSuite {
"1"
)

checkGeneratedRunResultEquals(
"factorialBigInt",
factorialBigInt,
List(BigInt(5)),
"120"
)

checkGeneratedRunResultEquals(
"inverseBigDecimal",
inverseBigDecimal,
List(BigDecimal(2)),
"0.5"
)

checkGeneratedRunResultEquals(
"sayBye",
sayBye,
List("Unit"),
"Bye!"
)

checkGeneratedRunResultEquals(
"isInTree",
isInTree,
Expand Down
9 changes: 9 additions & 0 deletions guinep/src/test/scala/testsdata.scala
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ object TestsData {
def showNullableInt(i: Int | Null): String =
if i == null then "null" else i.toString

def factorialBigInt(n: BigInt): BigInt =
if n == 0 then 1 else n * factorialBigInt(n - 1)

def inverseBigDecimal(bd: BigDecimal): BigDecimal =
BigDecimal(1) / bd

def sayBye(unit: Unit): String =
"Bye!"

enum IntTree:
case Leaf
case Node(left: IntTree, value: Int, right: IntTree)
Expand Down
21 changes: 19 additions & 2 deletions testcases/src/main/scala/main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,23 @@ def seqProduct(seq: Seq[Float]): Float =
def showNullableInt(i: Int | Null): String =
if i == null then "null" else i.toString

def factorialBigInt(n: BigInt): BigInt =
if n == 0 then 1 else n * factorialBigInt(n - 1)

def inverseBigDecimal(bd: BigDecimal): BigDecimal =
BigDecimal(1) / bd

def sayBye(unit: Unit): String =
"Bye!"

case class TakesUnit(unit: Unit)

def runTakesUnit(takesUnit: TakesUnit): Unit =
()

@main
def run: Unit =
guinep.web
.withModifyConfig(_.copy(requireNonNullableInputs = true))
.apply(
upperCaseText,
add,
Expand All @@ -129,7 +142,11 @@ def run: Unit =
listProduct,
sumVector,
seqProduct,
showNullableInt
showNullableInt,
factorialBigInt,
inverseBigDecimal,
sayBye,
runTakesUnit,
// isInTreeExt
// addManyParamLists
// printsWeirdGADT
Expand Down
19 changes: 11 additions & 8 deletions web/src/main/scala/htmlgen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ private[guinep] trait HtmlGen {
style(
"""
body { font-family: Arial, sans-serif; }
.sidebar { position: fixed; left: 0; top: 0; width: 200px; height: 100vh; background-color: #f0f0f0; padding: 20px; }
.sidebar { position: fixed; left: 0; top: 0; width: 200px; height: 100vh; background-color: #f0f0f0; padding: 20px; overflow-y: auto; }
.sidebar a { display: block; padding: 10px; margin-bottom: 10px; background-color: #007bff; color: white; text-decoration: none; text-align: center; border-radius: 5px; }
.sidebar a:hover { background-color: #0056b3; }
.main-content { margin-left: 232px; padding: 40px; display: flex; justify-content: center; padding-top: 20px; }
Expand All @@ -29,8 +29,8 @@ private[guinep] trait HtmlGen {
input[type=submit]:hover { background-color: #45a049; }
select { display: inline-block; margin-bottom: 10px; padding: 8px; box-sizing: border-box; }
.result { margin-top: 20px; font-weight: bold; }
.button { text-decoration: none; background-color: #AAAAAA; color: white; padding: 5px 10px; border: none; border-radius: 4px; cursor: pointer; }
.button:hover { background-color: #BBBBBB; }
.add-button { text-decoration: none; background-color: #AAAAAA; color: white; padding: 5px 10px; border: none; border-radius: 4px; cursor: pointer; }
.add-button:hover { background-color: #BBBBBB; }
"""
),
script(Dom.raw(jsToChangeFormBasedOnPath)),
Expand Down Expand Up @@ -138,7 +138,7 @@ private[guinep] trait HtmlGen {
const button = document.createElement('a');
button.innerText = '+';
button.href = '#';
button.classList.add('button');
button.classList.add('add-button');
button.onclick = () => {
addFormElement(fieldset, formElem.element, namedLookup, button);
};
Expand Down Expand Up @@ -187,13 +187,16 @@ private[guinep] trait HtmlGen {
form.insertBefore(input, before);
form.insertBefore(br.cloneNode(), before);
} else {
const label = document.createElement('label');
label.innerText = formElem.name + ': ';
label.for = formElem.name;
form.insertBefore(label, before);
if (formElem.type !== 'hidden') {
const label = document.createElement('label');
label.innerText = formElem.name + ': ';
label.for = formElem.name;
form.insertBefore(label, before);
}
const input = document.createElement('input');
input.type = formElem.type;
input.name = formElem.name;
input.value = formElem.value;
input.id = formElem.name;
input.placeholder = formElem.name;
if (formElem.nullable) {
Expand Down
Loading

0 comments on commit 936f9e6

Please sign in to comment.