@@ -1665,8 +1665,13 @@ self =>
1665
1665
// context sensitive transformations should be moved to a later compiler phase
1666
1666
var scriptFormalOutputParameters = new scala.collection.mutable.HashMap [String ,Tree ]
1667
1667
var scriptFormalConstrainedParameters = new scala.collection.mutable.HashMap [String ,Tree ]
1668
- var scriptLocalVariables = new scala.collection.mutable.HashMap [Name ,Tree ] // should be in a context stack; now the scope becomes too big
1669
- var scriptLocalValues = new scala.collection.mutable.HashMap [Name ,Tree ]
1668
+
1669
+ // These are maps from Name to pairs of Tree and Boolean. Here's what Boolean means:
1670
+ // 1) true - Tree IS a type definition that can be used out-of-the-box
1671
+ // 2) flase - Tree is NOT a type definition. It is a value, for which this local variable is declared,
1672
+ // but who's type is still unknown.
1673
+ var scriptLocalVariables = new scala.collection.mutable.HashMap [Name ,(Tree , Boolean )] // should be in a context stack; now the scope becomes too big
1674
+ var scriptLocalValues = new scala.collection.mutable.HashMap [Name ,(Tree , Boolean )]
1670
1675
1671
1676
def scriptDefsOrDcls (start : Int , mods : Modifiers ): List [Tree ] = {
1672
1677
in.isInSubScript_script = true
@@ -1799,13 +1804,31 @@ self =>
1799
1804
1800
1805
// add for each variable and value: val _c = subscript.DSL._declare[Char]('c)
1801
1806
val rhs_withVariablesAndValuesDeclarations = new ListBuffer [Tree ]
1802
- for ((vn,vt) <- scriptLocalVariables ++ scriptLocalValues) {
1807
+
1808
+ // This pattern captures the algorithm of the local variable's body generation
1809
+ // First argument is the name of the variable, second - it's type, that makes sence
1810
+ // in current scope
1811
+ def localValBody (vn : Name , vt : Tree ): Tree = {
1803
1812
val vSym = Apply (scalaDot(nme.Symbol ), List (Literal (Constant (vn.toString))))
1813
+ val declare_typed = TypeApply (dslFunFor(DEF ), List (vt))
1814
+ val rhs = Apply (declare_typed, List (vSym))
1815
+ rhs
1816
+ }
1817
+
1818
+ // This pattern captures the local variable declaration.
1819
+ // First algorithm is the name, second algorithm is the body
1820
+ def localValDef (vn : Name , rhs : Tree ): ValDef = {
1804
1821
val underscored_v_name = newTermName(underscore_prefix(vn.toString))
1805
- // val tp = AppliedTypeTree(vmNodeFor(VAL), List(vt))
1806
- val declare_typed = TypeApply (dslFunFor(DEF ), List (vt))
1807
- val rhs = Apply (declare_typed, List (vSym))
1808
- val valDef = ValDef (NoMods , underscored_v_name, TypeTree (), rhs)
1822
+ val valDef = ValDef (NoMods , underscored_v_name, TypeTree (), rhs)
1823
+ valDef
1824
+ }
1825
+
1826
+ import TypeOperations ._
1827
+ for ((vn,(vt, isType)) <- scriptLocalVariables ++ scriptLocalValues) {
1828
+ val valDef = {
1829
+ val rhs = if (isType) localValBody(vn, vt) else withTypeOf(vt)(localValBody(vn, _))
1830
+ localValDef(vn, rhs)
1831
+ }
1809
1832
rhs_withVariablesAndValuesDeclarations += valDef
1810
1833
}
1811
1834
@@ -2051,41 +2074,87 @@ self =>
2051
2074
if (tok == BACKQUOTED_IDENT ) Ident (name) updateAttachment BackquotedIdentifierAttachment
2052
2075
else Ident (name)
2053
2076
}
2054
- accept(COLON ) // optionality for the typer is not supported yet...would require some work since the type is needed (?) at the start of the method generated for the script
2055
- val tp = exprSimpleType()
2056
- val rhs =
2057
- if (tp.isEmpty || in.token == EQUALS || ! newmods.isMutable || true /* FTTB enforce initialisation*/ ) {
2058
- accept(EQUALS )
2059
- if (! tp.isEmpty && newmods.isMutable &&
2060
- lhs.isInstanceOf [Ident ] && in.token == USCORE ) {
2061
- in.nextToken()
2062
- newmods = newmods | Flags .DEFAULTINIT ; EmptyTree }
2063
- else {simpleNativeValueExpr()}
2064
- }
2065
- else {newmods = newmods | Flags .DEFERRED ; EmptyTree }
2066
2077
2067
- // TBD: val result = ScriptValDef(newmods, name.toTermName, tp, rhs) FTTB a quick solution:
2068
- // val c = initializer ===> subscript.DSL._val(_c, here: subscript.DSL.N_localvar[Char] => initializer) likewise for var
2078
+ // A pattern function that captures the tree that will be constructed
2079
+ // Also, it captures the fact of passing rhs or tp out of the scope
2080
+ // in order to generate definitions at the beginning of the script
2081
+ //
2082
+ // isTypeTaransmittable variable indicates whether `tp` type can be
2083
+ // transfered out of current scope (true) or not (false)
2084
+ def operationPattern (rhs : Tree , tp : Tree , isTypeTransmittable : Boolean ) = {
2085
+ import TypeOperations ._
2086
+
2087
+ val vIdent = Ident (newTermName(underscore_prefix(name.toString)))
2088
+ val sFunValOrVar = dslFunFor(if (mods.isMutable) VAR else VAL )
2089
+ val sNodeValOrVar = vmNodeFor(if (mods.isMutable) VAR else VAL )
2090
+
2091
+ val typer = AppliedTypeTree (sNodeValOrVar, List (tp))
2092
+
2093
+ // If the type is transmittable (already known), enforce it
2094
+ // If it is just an unknown identifier, infer it
2095
+ val initializerCode =
2096
+ if (isTypeTransmittable) blockToFunction_here (enforcingType(tp)(rhs), typer, rhs.pos)
2097
+ else withTypeOf(rhs, tp.asInstanceOf [Ident ]){_ =>
2098
+ blockToFunction_here(rhs, typer, rhs.pos) // Typer is already influenced by `tp` type parameter
2099
+ }
2100
+
2101
+ // If tp is just a local identifier, it will be useless out of this scope
2102
+ // Hence, we'll need to pass the actual value `rhs` instead of `tp`
2103
+ // in order to infer the type from it on later stages of compilation
2104
+ // (see TypeOperations.withTypeOf transformation)
2105
+ //
2106
+ // Also, we'll need to set a proper Boolean flag in order to distinguish
2107
+ // between values and types trees
2108
+
2109
+ val treeToPass = if (isTypeTransmittable) tp else rhs
2110
+ if (mods.isMutable) scriptLocalVariables += name-> ((treeToPass, isTypeTransmittable))
2111
+ else scriptLocalValues += name-> ((treeToPass, isTypeTransmittable))
2112
+
2113
+ atPos(pos) {
2114
+ if (rhs.isEmpty) {dslFunFor(LPAREN_PLUS_MINUS_RPAREN )} // neutral; there is no value to provide
2115
+ else Apply (sFunValOrVar, List (vIdent, initializerCode))
2116
+ }
2117
+ }
2069
2118
2070
- val vIdent = Ident (newTermName(underscore_prefix(name.toString)))
2071
- val sFunValOrVar = dslFunFor(if (mods.isMutable) VAR else VAL )
2072
- val sNodeValOrVar = vmNodeFor(if (mods.isMutable) VAR else VAL )
2119
+ in.token match {
2120
+ // Type is present
2121
+ case COLON =>
2122
+ accept(COLON )
2123
+ val tp = exprSimpleType()
2124
+ val rhs =
2125
+ if (tp.isEmpty || in.token == EQUALS || ! newmods.isMutable || true /* FTTB enforce initialisation*/ ) {
2126
+ accept(EQUALS )
2127
+ if (! tp.isEmpty && newmods.isMutable &&
2128
+ lhs.isInstanceOf [Ident ] && in.token == USCORE ) {
2129
+ in.nextToken()
2130
+ newmods = newmods | Flags .DEFAULTINIT ; EmptyTree }
2131
+ else {simpleNativeValueExpr()}
2132
+ }
2133
+ else {newmods = newmods | Flags .DEFERRED ; EmptyTree }
2134
+
2135
+ operationPattern(rhs, tp, true )
2136
+ // TBD: val result = ScriptValDef(newmods, name.toTermName, tp, rhs) FTTB a quick solution:
2137
+ // val c = initializer ===> subscript.DSL._val(_c, here: subscript.DSL.N_localvar[Char] => initializer) likewise for var
2073
2138
2074
- // val typer = AppliedTypeTree(sNodeValOrVar, List(tp)) this does NOT work; need a wildcard type
2075
- val typer = placeholderTypeBoundary{
2076
- AppliedTypeTree (sNodeValOrVar, List (wildcardType(pos))) // note: wildcardType fills placeholderTypes, used by placeholderTypes
2139
+ // Type is absent
2140
+ // Copy pasting is not good - further abstractin will be required
2141
+ // for `rhs` computation
2142
+ case _ =>
2143
+ val rhs = {
2144
+ accept(EQUALS )
2145
+ if (newmods.isMutable &&
2146
+ lhs.isInstanceOf [Ident ] && in.token == USCORE ) {
2147
+ in.nextToken()
2148
+ newmods = newmods | Flags .DEFAULTINIT ; EmptyTree }
2149
+ else {simpleNativeValueExpr()}
2150
+ }
2151
+ operationPattern(rhs, Ident (newTypeName(" T" )), false )
2077
2152
}
2078
-
2079
- val initializerCode = blockToFunction_here (rhs, typer, rhs.pos)
2080
- if (mods.isMutable) scriptLocalVariables += name-> tp
2081
- else scriptLocalValues += name-> tp
2082
2153
2083
- atPos(pos) {
2084
- if (rhs.isEmpty) {dslFunFor(LPAREN_PLUS_MINUS_RPAREN )} // neutral; there is no value to provide
2085
- else Apply (sFunValOrVar, List (vIdent, initializerCode))
2086
- }
2154
+
2087
2155
}
2088
2156
2157
+
2089
2158
def unaryPostfixScriptTerm (): Tree = {
2090
2159
var result = unaryPrefixScriptTerm()
2091
2160
while (isSubScriptUnaryPostfixOp(in)) {
@@ -2322,7 +2391,7 @@ self =>
2322
2391
}
2323
2392
inSubscriptArgumentParens(if (in.token == RPAREN ) Nil else args())
2324
2393
}
2325
-
2394
+
2326
2395
/* ----------- EXPRESSIONS ------------------------------------------------ */
2327
2396
2328
2397
def condExpr (): Tree = {
@@ -4135,5 +4204,98 @@ self =>
4135
4204
makeEmptyPackage(start, stats)
4136
4205
}
4137
4206
}
4207
+
4208
+ /**
4209
+ * This object contains various transformations of the trees. The resulting trees,
4210
+ * as a rule, have some interesting properties from typing point of view, so that
4211
+ * you can generate trees that will be typed and treated properly.
4212
+ */
4213
+ object TypeOperations {
4214
+
4215
+ /**
4216
+ * This is roughly type casting in pre-typer phase.
4217
+ * Wraps the `tree` in a block of following contents:
4218
+ * {
4219
+ * val typedReturn: `ttype` = `tree`
4220
+ * typedReturn
4221
+ * }
4222
+ *
4223
+ * This gives a guarantee, that a) this tree can be assigned to
4224
+ * a variable of a given type and b) you actually get a tree of the
4225
+ * desired type.
4226
+ *
4227
+ * @param ttype - desired type
4228
+ * @param tree - tree to be 'casted'
4229
+ */
4230
+ def enforcingType (ttype : Tree )(tree : Tree ): Tree = {
4231
+ val typeSafetyDefinition = ValDef (Modifiers (0 ), newTermName(" typedReturn" ), ttype, tree)
4232
+ Block (typeSafetyDefinition, Ident (" typedReturn" ))
4233
+ }
4234
+
4235
+ def withTypeOf (target : Tree )(identToTree : Ident => Tree ): Tree =
4236
+ withTypeOf(target, Ident (newTypeName(" T" )))(identToTree) // TBD: come up with a way to generate unique names for Idents
4237
+
4238
+ /**
4239
+ * This transformation allows you to capture the type of a `target` value, encapsulate it
4240
+ * into the `typePlaceholder` identifier and generate a tree with this type using the Ident => Tree
4241
+ * function. The argument that will be passed to this function is the captured type.
4242
+ *
4243
+ * Assume this block `block` is given:
4244
+ * {
4245
+ * f[T]("Hello, World")
4246
+ * }
4247
+ * where f is some function and T is unknown type.
4248
+ *
4249
+ * Assume some value tree `target` is given, and there's a need to substitute all
4250
+ * unknown parameters T in the block with the type of `target`, that is not known either (and
4251
+ * will not be known till infered by the compiler on later stages).
4252
+ *
4253
+ * Then, withTypeOf(target, Ident(newTypeName("T")))(block) will generate following AST block:
4254
+ * {
4255
+ * def capturingFunction[T](x: T) = {
4256
+ * f[T]("Hello, World")
4257
+ * }
4258
+ * capturingFunction(target)
4259
+ * }
4260
+ */
4261
+ def withTypeOf (target : Tree , typePlaceholder : Ident )(identToTree : Ident => Tree ): Tree = {
4262
+ import scala .reflect .internal .ModifierFlags ._
4263
+
4264
+ val tree = identToTree(typePlaceholder)
4265
+
4266
+ // Generating a type-capturing function (DefDef)
4267
+ val mods = NoMods
4268
+ val name = newTermName(" capturingFunction" )
4269
+
4270
+ val typeParam =
4271
+ TypeDef (
4272
+ Modifiers (DEFERRED | PARAM ),
4273
+ typePlaceholder.name.asInstanceOf [TypeName ],
4274
+ List (),
4275
+ TypeBoundsTree (EmptyTree , EmptyTree )
4276
+ )
4277
+
4278
+ val valueParam =
4279
+ ValDef (
4280
+ Modifiers (BYNAMEPARAM | PARAM ), // TBD: BYNAMEPARAM doesn't seem to actually make it 'by name'
4281
+ newTermName(" x" ),
4282
+ typePlaceholder,
4283
+ EmptyTree
4284
+ )
4285
+ val returnType = TypeTree ()
4286
+
4287
+ val capturingFunction = DefDef (mods, name, List (typeParam), List (List (valueParam)), returnType, tree)
4288
+
4289
+
4290
+ // Constructing function application to the target tree
4291
+ val application = Apply (Ident (name), List (target))
4292
+
4293
+
4294
+ // Returning a block with the capturing function and it's application
4295
+ Block (capturingFunction, application)
4296
+ }
4297
+
4298
+ }
4299
+
4138
4300
}
4139
4301
}
0 commit comments