Skip to content

Commit 0c37ee8

Browse files
committed
Merge pull request #2 from anatoliykmetyuk/develop
Modifications to local variables declarations
2 parents 287224f + 0ae2933 commit 0c37ee8

File tree

2 files changed

+199
-37
lines changed

2 files changed

+199
-37
lines changed

src/compiler/scala/tools/nsc/ast/parser/Parsers.scala

Lines changed: 198 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1665,8 +1665,13 @@ self =>
16651665
// context sensitive transformations should be moved to a later compiler phase
16661666
var scriptFormalOutputParameters = new scala.collection.mutable.HashMap[String,Tree]
16671667
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)]
16701675

16711676
def scriptDefsOrDcls(start : Int, mods: Modifiers): List[Tree] = {
16721677
in.isInSubScript_script = true
@@ -1799,13 +1804,31 @@ self =>
17991804

18001805
// add for each variable and value: val _c = subscript.DSL._declare[Char]('c)
18011806
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 = {
18031812
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 = {
18041821
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+
}
18091832
rhs_withVariablesAndValuesDeclarations += valDef
18101833
}
18111834

@@ -2051,41 +2074,87 @@ self =>
20512074
if (tok == BACKQUOTED_IDENT) Ident(name) updateAttachment BackquotedIdentifierAttachment
20522075
else Ident(name)
20532076
}
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}
20662077

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+
}
20692118

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
20732138

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)
20772152
}
2078-
2079-
val initializerCode = blockToFunction_here (rhs, typer, rhs.pos)
2080-
if (mods.isMutable) scriptLocalVariables += name->tp
2081-
else scriptLocalValues += name->tp
20822153

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+
20872155
}
20882156

2157+
20892158
def unaryPostfixScriptTerm (): Tree = {
20902159
var result = unaryPrefixScriptTerm()
20912160
while (isSubScriptUnaryPostfixOp(in)) {
@@ -2322,7 +2391,7 @@ self =>
23222391
}
23232392
inSubscriptArgumentParens(if (in.token == RPAREN) Nil else args())
23242393
}
2325-
2394+
23262395
/* ----------- EXPRESSIONS ------------------------------------------------ */
23272396

23282397
def condExpr(): Tree = {
@@ -4135,5 +4204,98 @@ self =>
41354204
makeEmptyPackage(start, stats)
41364205
}
41374206
}
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+
41384300
}
41394301
}

src/subscript/subscript/swing/GraphicalDebugger.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ class GraphicalDebuggerApp extends SimpleSubscriptApplication with ScriptDebugge
384384
var resultW = 0d // drawn width of this subtree
385385
var childHCs = new ListBuffer[Double]
386386

387-
val isCurrentNode = currentMessage != null && currentMessage.node.asInstanceOf[CallGraphNode].index == n.index
387+
val isCurrentNode = currentMessage != null && currentMessage.node.asInstanceOf[CallGraphNodeTrait].index == n.index
388388
n match {
389389
case p:CallGraphParentNodeTrait =>
390390
val pcl=p.children.length

0 commit comments

Comments
 (0)