Add nested declarations and lifted enums
This commit is contained in:
parent
c35efdc2ae
commit
3b290116b8
10
README.md
10
README.md
@ -25,6 +25,16 @@ Point(x:, y:).dist() //< 5
|
|||||||
fun swapEnds(first, args..., last, f) {
|
fun swapEnds(first, args..., last, f) {
|
||||||
f( last, ...args, first)
|
f( last, ...args, first)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class A {
|
||||||
|
class B(x?)
|
||||||
|
object Inner { val foo = "bar" }
|
||||||
|
enum E* { One, Two }
|
||||||
|
}
|
||||||
|
val ab = A.B()
|
||||||
|
assertEquals(ab.x, null)
|
||||||
|
assertEquals(A.Inner.foo, "bar")
|
||||||
|
assertEquals(A.One, A.E.One)
|
||||||
```
|
```
|
||||||
|
|
||||||
- extremely simple Kotlin integration on any platform (JVM, JS, WasmJS, Lunux, MacOS, iOS, Windows)
|
- extremely simple Kotlin integration on any platform (JVM, JS, WasmJS, Lunux, MacOS, iOS, Windows)
|
||||||
|
|||||||
42
docs/OOP.md
42
docs/OOP.md
@ -113,6 +113,48 @@ val handler = object {
|
|||||||
- **Serialization**: Anonymous objects are **not serializable**. Attempting to encode an anonymous object via `Lynon` will throw a `SerializationException`. This is because their class definition is transient and cannot be safely restored in a different session or process.
|
- **Serialization**: Anonymous objects are **not serializable**. Attempting to encode an anonymous object via `Lynon` will throw a `SerializationException`. This is because their class definition is transient and cannot be safely restored in a different session or process.
|
||||||
- **Type Identity**: Every object expression creates a unique anonymous class. Two identical object expressions will result in two different classes with distinct type identities.
|
- **Type Identity**: Every object expression creates a unique anonymous class. Two identical object expressions will result in two different classes with distinct type identities.
|
||||||
|
|
||||||
|
## Nested Declarations
|
||||||
|
|
||||||
|
Lyng allows classes, objects, enums, and type aliases to be declared inside another class. These declarations live in the **class namespace** (not the instance), so they do not capture an outer instance and are accessed with a qualifier.
|
||||||
|
|
||||||
|
```lyng
|
||||||
|
class A {
|
||||||
|
class B(x?)
|
||||||
|
object Inner { val foo = "bar" }
|
||||||
|
type Alias = B
|
||||||
|
enum E { One, Two }
|
||||||
|
}
|
||||||
|
|
||||||
|
val ab = A.B()
|
||||||
|
assertEquals(ab.x, null)
|
||||||
|
assertEquals(A.Inner.foo, "bar")
|
||||||
|
```
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- **Qualified access**: use `Outer.Inner` for nested classes/objects/enums/aliases. Inside `Outer` you can refer to them by unqualified name unless shadowed.
|
||||||
|
- **No inner semantics**: nested declarations do not capture an instance of the outer class. They are resolved at compile time.
|
||||||
|
- **Visibility**: `private` restricts a nested declaration to the declaring class body (not visible from outside or subclasses).
|
||||||
|
- **Reflection name**: a nested class reports `Outer.Inner` (e.g., `A.B::class.name` is `"A.B"`).
|
||||||
|
- **Type aliases**: behave as aliases of the qualified nested type and are expanded by the type system.
|
||||||
|
|
||||||
|
### Lifted Enum Entries
|
||||||
|
|
||||||
|
Enums can optionally lift their entries into the surrounding class namespace using `*`:
|
||||||
|
|
||||||
|
```lyng
|
||||||
|
class A {
|
||||||
|
enum E* { One, Two }
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(A.One, A.E.One)
|
||||||
|
assertEquals(A.Two, A.E.Two)
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- `E*` exposes entries in `A` as if they were direct members (`A.One`).
|
||||||
|
- If a name would conflict with an existing class member, compilation fails (no implicit fallback).
|
||||||
|
- Without `*`, use the normal `A.E.One` form.
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
Properties allow you to define member accessors that look like fields but execute code when read or written. Unlike regular fields, properties in Lyng do **not** have automatic backing fields; they are pure accessors.
|
Properties allow you to define member accessors that look like fields but execute code when read or written. Unlike regular fields, properties in Lyng do **not** have automatic backing fields; they are pure accessors.
|
||||||
|
|||||||
@ -107,6 +107,23 @@ Singleton objects are declared using the `object` keyword. They define a class a
|
|||||||
|
|
||||||
Logger.log("Hello singleton!")
|
Logger.log("Hello singleton!")
|
||||||
|
|
||||||
|
## Nested Declarations (short)
|
||||||
|
|
||||||
|
Classes, objects, and enums can be declared inside another class. They live in the class namespace (no outer instance capture), so you access them with a qualifier:
|
||||||
|
|
||||||
|
class A {
|
||||||
|
class B(x?)
|
||||||
|
object Inner { val foo = "bar" }
|
||||||
|
enum E* { One, Two }
|
||||||
|
}
|
||||||
|
|
||||||
|
val ab = A.B()
|
||||||
|
assertEquals(ab.x, null)
|
||||||
|
assertEquals(A.Inner.foo, "bar")
|
||||||
|
assertEquals(A.One, A.E.One)
|
||||||
|
|
||||||
|
See [OOP notes](OOP.md#nested-declarations) for rules, visibility, and enum lifting details.
|
||||||
|
|
||||||
## Delegation (briefly)
|
## Delegation (briefly)
|
||||||
|
|
||||||
You can delegate properties and functions to other objects using the `by` keyword. This is perfect for patterns like `lazy` initialization.
|
You can delegate properties and functions to other objects using the `by` keyword. This is perfect for patterns like `lazy` initialization.
|
||||||
|
|||||||
@ -108,6 +108,24 @@ object Config {
|
|||||||
Config.show()
|
Config.show()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Nested Declarations and Lifted Enums
|
||||||
|
You can now declare classes, objects, enums, and type aliases inside another class. These nested declarations live in the class namespace (no outer instance capture) and are accessed with a qualifier.
|
||||||
|
|
||||||
|
```lyng
|
||||||
|
class A {
|
||||||
|
class B(x?)
|
||||||
|
object Inner { val foo = "bar" }
|
||||||
|
enum E* { One, Two }
|
||||||
|
}
|
||||||
|
|
||||||
|
val ab = A.B()
|
||||||
|
assertEquals(ab.x, null)
|
||||||
|
assertEquals(A.Inner.foo, "bar")
|
||||||
|
assertEquals(A.One, A.E.One)
|
||||||
|
```
|
||||||
|
|
||||||
|
The `*` on `enum E*` lifts entries into the enclosing class namespace (compile-time error on ambiguity).
|
||||||
|
|
||||||
### Object Expressions
|
### Object Expressions
|
||||||
You can now create anonymous objects that inherit from classes or interfaces using the `object : Base { ... }` syntax. These expressions capture their lexical scope and support multiple inheritance.
|
You can now create anonymous objects that inherit from classes or interfaces using the `object : Base { ... }` syntax. These expressions capture their lexical scope and support multiple inheritance.
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,7 @@ package net.sergeych.lyng
|
|||||||
import net.sergeych.lyng.miniast.MiniTypeRef
|
import net.sergeych.lyng.miniast.MiniTypeRef
|
||||||
import net.sergeych.lyng.obj.Obj
|
import net.sergeych.lyng.obj.Obj
|
||||||
import net.sergeych.lyng.obj.ObjList
|
import net.sergeych.lyng.obj.ObjList
|
||||||
|
import net.sergeych.lyng.obj.ObjNull
|
||||||
import net.sergeych.lyng.obj.ObjRecord
|
import net.sergeych.lyng.obj.ObjRecord
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,12 +62,20 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasComplex) {
|
if (!hasComplex) {
|
||||||
if (arguments.list.size != params.size)
|
if (arguments.list.size > params.size)
|
||||||
scope.raiseIllegalArgument("expected ${params.size} arguments, got ${arguments.list.size}")
|
scope.raiseIllegalArgument("expected ${params.size} arguments, got ${arguments.list.size}")
|
||||||
|
if (arguments.list.size < params.size) {
|
||||||
|
for (i in arguments.list.size until params.size) {
|
||||||
|
val a = params[i]
|
||||||
|
if (!a.type.isNullable) {
|
||||||
|
scope.raiseIllegalArgument("expected ${params.size} arguments, got ${arguments.list.size}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (i in params.indices) {
|
for (i in params.indices) {
|
||||||
val a = params[i]
|
val a = params[i]
|
||||||
val value = arguments.list[i]
|
val value = if (i < arguments.list.size) arguments.list[i] else ObjNull
|
||||||
val recordType = if (declaringClass != null && a.accessType != null) {
|
val recordType = if (declaringClass != null && a.accessType != null) {
|
||||||
ObjRecord.Type.ConstructorField
|
ObjRecord.Type.ConstructorField
|
||||||
} else {
|
} else {
|
||||||
@ -103,6 +112,11 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun missingValue(a: Item, error: String): Obj {
|
||||||
|
return a.defaultValue?.execute(scope)
|
||||||
|
?: if (a.type.isNullable) ObjNull else scope.raiseIllegalArgument(error)
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare positional args and parameter count, handle tail-block binding
|
// Prepare positional args and parameter count, handle tail-block binding
|
||||||
val callArgs: List<Obj>
|
val callArgs: List<Obj>
|
||||||
val paramsSize: Int
|
val paramsSize: Int
|
||||||
@ -181,8 +195,7 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
|||||||
assign(a, namedValues[i]!!)
|
assign(a, namedValues[i]!!)
|
||||||
} else {
|
} else {
|
||||||
val value = if (hp < callArgs.size) callArgs[hp++]
|
val value = if (hp < callArgs.size) callArgs[hp++]
|
||||||
else a.defaultValue?.execute(scope)
|
else missingValue(a, "too few arguments for the call (missing ${a.name})")
|
||||||
?: scope.raiseIllegalArgument("too few arguments for the call (missing ${a.name})")
|
|
||||||
assign(a, value)
|
assign(a, value)
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
@ -202,8 +215,7 @@ data class ArgsDeclaration(val params: List<Item>, val endTokenType: Token.Type)
|
|||||||
assign(a, namedValues[i]!!)
|
assign(a, namedValues[i]!!)
|
||||||
} else {
|
} else {
|
||||||
val value = if (tp >= headPosBound) callArgs[tp--]
|
val value = if (tp >= headPosBound) callArgs[tp--]
|
||||||
else a.defaultValue?.execute(scope)
|
else missingValue(a, "too few arguments for the call")
|
||||||
?: scope.raiseIllegalArgument("too few arguments for the call")
|
|
||||||
assign(a, value)
|
assign(a, value)
|
||||||
}
|
}
|
||||||
i--
|
i--
|
||||||
|
|||||||
@ -31,6 +31,7 @@ sealed class CodeContext {
|
|||||||
var typeParamDecls: List<TypeDecl.TypeParam> = emptyList()
|
var typeParamDecls: List<TypeDecl.TypeParam> = emptyList()
|
||||||
val pendingInitializations = mutableMapOf<String, Pos>()
|
val pendingInitializations = mutableMapOf<String, Pos>()
|
||||||
val declaredMembers = mutableSetOf<String>()
|
val declaredMembers = mutableSetOf<String>()
|
||||||
|
val classScopeMembers = mutableSetOf<String>()
|
||||||
val memberOverrides = mutableMapOf<String, Boolean>()
|
val memberOverrides = mutableMapOf<String, Boolean>()
|
||||||
val memberFieldIds = mutableMapOf<String, Int>()
|
val memberFieldIds = mutableMapOf<String, Int>()
|
||||||
val memberMethodIds = mutableMapOf<String, Int>()
|
val memberMethodIds = mutableMapOf<String, Int>()
|
||||||
|
|||||||
@ -141,6 +141,8 @@ class Compiler(
|
|||||||
private val callableReturnTypeByName: MutableMap<String, ObjClass> = mutableMapOf()
|
private val callableReturnTypeByName: MutableMap<String, ObjClass> = mutableMapOf()
|
||||||
private val lambdaReturnTypeByRef: MutableMap<ObjRef, ObjClass> = mutableMapOf()
|
private val lambdaReturnTypeByRef: MutableMap<ObjRef, ObjClass> = mutableMapOf()
|
||||||
private val classFieldTypesByName: MutableMap<String, MutableMap<String, ObjClass>> = mutableMapOf()
|
private val classFieldTypesByName: MutableMap<String, MutableMap<String, ObjClass>> = mutableMapOf()
|
||||||
|
private val classScopeMembersByClassName: MutableMap<String, MutableSet<String>> = mutableMapOf()
|
||||||
|
private val classScopeCallableMembersByClassName: MutableMap<String, MutableSet<String>> = mutableMapOf()
|
||||||
private val encodedPayloadTypeByScopeId: MutableMap<Int, MutableMap<Int, ObjClass>> = mutableMapOf()
|
private val encodedPayloadTypeByScopeId: MutableMap<Int, MutableMap<Int, ObjClass>> = mutableMapOf()
|
||||||
private val encodedPayloadTypeByName: MutableMap<String, ObjClass> = mutableMapOf()
|
private val encodedPayloadTypeByName: MutableMap<String, ObjClass> = mutableMapOf()
|
||||||
|
|
||||||
@ -330,10 +332,61 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"class", "object" -> {
|
}
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
cc.restorePos(saved)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun predeclareClassScopeMembers(
|
||||||
|
className: String,
|
||||||
|
target: MutableSet<String>,
|
||||||
|
callableTarget: MutableSet<String>
|
||||||
|
) {
|
||||||
|
val saved = cc.savePos()
|
||||||
|
var depth = 0
|
||||||
|
val modifiers = setOf(
|
||||||
|
"public", "private", "protected", "internal",
|
||||||
|
"override", "abstract", "extern", "static", "transient", "open", "closed"
|
||||||
|
)
|
||||||
|
fun nextNonWs(): Token {
|
||||||
|
var t = cc.next()
|
||||||
|
while (t.type == Token.Type.NEWLINE || t.type == Token.Type.SINGLE_LINE_COMMENT || t.type == Token.Type.MULTILINE_COMMENT) {
|
||||||
|
t = cc.next()
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
while (cc.hasNext()) {
|
||||||
|
var t = cc.next()
|
||||||
|
when (t.type) {
|
||||||
|
Token.Type.LBRACE -> depth++
|
||||||
|
Token.Type.RBRACE -> if (depth == 0) break else depth--
|
||||||
|
Token.Type.ID -> if (depth == 0) {
|
||||||
|
var sawStatic = false
|
||||||
|
while (t.type == Token.Type.ID && t.value in modifiers) {
|
||||||
|
if (t.value == "static") sawStatic = true
|
||||||
|
t = nextNonWs()
|
||||||
|
}
|
||||||
|
when (t.value) {
|
||||||
|
"class" -> {
|
||||||
val nameToken = nextNonWs()
|
val nameToken = nextNonWs()
|
||||||
if (nameToken.type == Token.Type.ID) {
|
if (nameToken.type == Token.Type.ID) {
|
||||||
target.add(nameToken.value)
|
target.add(nameToken.value)
|
||||||
|
callableTarget.add(nameToken.value)
|
||||||
|
registerClassScopeMember(className, nameToken.value)
|
||||||
|
registerClassScopeCallableMember(className, nameToken.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"object" -> {
|
||||||
|
val nameToken = nextNonWs()
|
||||||
|
if (nameToken.type == Token.Type.ID) {
|
||||||
|
target.add(nameToken.value)
|
||||||
|
registerClassScopeMember(className, nameToken.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"enum" -> {
|
"enum" -> {
|
||||||
@ -341,6 +394,25 @@ class Compiler(
|
|||||||
val nameToken = if (next.type == Token.Type.ID && next.value == "class") nextNonWs() else next
|
val nameToken = if (next.type == Token.Type.ID && next.value == "class") nextNonWs() else next
|
||||||
if (nameToken.type == Token.Type.ID) {
|
if (nameToken.type == Token.Type.ID) {
|
||||||
target.add(nameToken.value)
|
target.add(nameToken.value)
|
||||||
|
callableTarget.add(nameToken.value)
|
||||||
|
registerClassScopeMember(className, nameToken.value)
|
||||||
|
registerClassScopeCallableMember(className, nameToken.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"type" -> {
|
||||||
|
val nameToken = nextNonWs()
|
||||||
|
if (nameToken.type == Token.Type.ID) {
|
||||||
|
target.add(nameToken.value)
|
||||||
|
registerClassScopeMember(className, nameToken.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"fun", "fn", "val", "var" -> {
|
||||||
|
if (sawStatic) {
|
||||||
|
val nameToken = nextNonWs()
|
||||||
|
if (nameToken.type == Token.Type.ID) {
|
||||||
|
target.add(nameToken.value)
|
||||||
|
registerClassScopeMember(className, nameToken.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -353,6 +425,24 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun registerClassScopeMember(className: String, name: String) {
|
||||||
|
classScopeMembersByClassName.getOrPut(className) { mutableSetOf() }.add(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerClassScopeCallableMember(className: String, name: String) {
|
||||||
|
classScopeCallableMembersByClassName.getOrPut(className) { mutableSetOf() }.add(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isClassScopeCallableMember(className: String, name: String): Boolean {
|
||||||
|
return classScopeCallableMembersByClassName[className]?.contains(name) == true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerClassScopeFieldType(ownerClassName: String?, memberName: String, memberClassName: String) {
|
||||||
|
if (ownerClassName == null) return
|
||||||
|
val memberClass = resolveClassByName(memberClassName) ?: return
|
||||||
|
classFieldTypesByName.getOrPut(ownerClassName) { mutableMapOf() }[memberName] = memberClass
|
||||||
|
}
|
||||||
|
|
||||||
private fun resolveCompileClassInfo(name: String): CompileClassInfo? {
|
private fun resolveCompileClassInfo(name: String): CompileClassInfo? {
|
||||||
compileClassInfos[name]?.let { return it }
|
compileClassInfos[name]?.let { return it }
|
||||||
val scopeRec = seedScope?.get(name) ?: importManager.rootScope.get(name)
|
val scopeRec = seedScope?.get(name) ?: importManager.rootScope.get(name)
|
||||||
@ -509,6 +599,11 @@ class Compiler(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun currentEnclosingClassName(): String? {
|
||||||
|
val ctx = codeContexts.asReversed().firstOrNull { it is CodeContext.ClassBody } as? CodeContext.ClassBody
|
||||||
|
return ctx?.name
|
||||||
|
}
|
||||||
|
|
||||||
private fun currentTypeParams(): Set<String> {
|
private fun currentTypeParams(): Set<String> {
|
||||||
val result = mutableSetOf<String>()
|
val result = mutableSetOf<String>()
|
||||||
pendingTypeParamStack.lastOrNull()?.let { result.addAll(it) }
|
pendingTypeParamStack.lastOrNull()?.let { result.addAll(it) }
|
||||||
@ -597,21 +692,20 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun parseTypeAliasDeclaration(): Statement {
|
private suspend fun parseTypeAliasDeclaration(): Statement {
|
||||||
if (codeContexts.lastOrNull() is CodeContext.ClassBody) {
|
|
||||||
throw ScriptError(cc.currentPos(), "type alias is not allowed in class body")
|
|
||||||
}
|
|
||||||
val nameToken = cc.requireToken(Token.Type.ID, "type alias name expected")
|
val nameToken = cc.requireToken(Token.Type.ID, "type alias name expected")
|
||||||
val name = nameToken.value
|
val declaredName = nameToken.value
|
||||||
if (typeAliases.containsKey(name)) {
|
val outerClassName = currentEnclosingClassName()
|
||||||
throw ScriptError(nameToken.pos, "type alias $name already declared")
|
val qualifiedName = if (outerClassName != null) "$outerClassName.$declaredName" else declaredName
|
||||||
|
if (typeAliases.containsKey(qualifiedName)) {
|
||||||
|
throw ScriptError(nameToken.pos, "type alias $qualifiedName already declared")
|
||||||
}
|
}
|
||||||
if (resolveTypeDeclObjClass(TypeDecl.Simple(name, false)) != null) {
|
if (resolveTypeDeclObjClass(TypeDecl.Simple(qualifiedName, false)) != null) {
|
||||||
throw ScriptError(nameToken.pos, "type alias $name conflicts with existing class")
|
throw ScriptError(nameToken.pos, "type alias $qualifiedName conflicts with existing class")
|
||||||
}
|
}
|
||||||
val typeParams = parseTypeParamList()
|
val typeParams = parseTypeParamList()
|
||||||
val uniqueParams = typeParams.map { it.name }.toSet()
|
val uniqueParams = typeParams.map { it.name }.toSet()
|
||||||
if (uniqueParams.size != typeParams.size) {
|
if (uniqueParams.size != typeParams.size) {
|
||||||
throw ScriptError(nameToken.pos, "type alias $name has duplicate type parameters")
|
throw ScriptError(nameToken.pos, "type alias $qualifiedName has duplicate type parameters")
|
||||||
}
|
}
|
||||||
val typeParamNames = uniqueParams
|
val typeParamNames = uniqueParams
|
||||||
if (typeParamNames.isNotEmpty()) pendingTypeParamStack.add(typeParamNames)
|
if (typeParamNames.isNotEmpty()) pendingTypeParamStack.add(typeParamNames)
|
||||||
@ -619,25 +713,30 @@ class Compiler(
|
|||||||
cc.skipWsTokens()
|
cc.skipWsTokens()
|
||||||
val eq = cc.nextNonWhitespace()
|
val eq = cc.nextNonWhitespace()
|
||||||
if (eq.type != Token.Type.ASSIGN) {
|
if (eq.type != Token.Type.ASSIGN) {
|
||||||
throw ScriptError(eq.pos, "type alias $name expects '='")
|
throw ScriptError(eq.pos, "type alias $qualifiedName expects '='")
|
||||||
}
|
}
|
||||||
parseTypeExpressionWithMini().first
|
parseTypeExpressionWithMini().first
|
||||||
} finally {
|
} finally {
|
||||||
if (typeParamNames.isNotEmpty()) pendingTypeParamStack.removeLast()
|
if (typeParamNames.isNotEmpty()) pendingTypeParamStack.removeLast()
|
||||||
}
|
}
|
||||||
val alias = TypeAliasDecl(name, typeParams, body, nameToken.pos)
|
val alias = TypeAliasDecl(qualifiedName, typeParams, body, nameToken.pos)
|
||||||
typeAliases[name] = alias
|
typeAliases[qualifiedName] = alias
|
||||||
declareLocalName(name, isMutable = false)
|
declareLocalName(declaredName, isMutable = false)
|
||||||
resolutionSink?.declareSymbol(name, SymbolKind.LOCAL, isMutable = false, pos = nameToken.pos)
|
resolutionSink?.declareSymbol(declaredName, SymbolKind.LOCAL, isMutable = false, pos = nameToken.pos)
|
||||||
|
if (outerClassName != null) {
|
||||||
|
val outerCtx = codeContexts.asReversed().firstOrNull { it is CodeContext.ClassBody } as? CodeContext.ClassBody
|
||||||
|
outerCtx?.classScopeMembers?.add(declaredName)
|
||||||
|
registerClassScopeMember(outerClassName, declaredName)
|
||||||
|
}
|
||||||
pendingDeclDoc = null
|
pendingDeclDoc = null
|
||||||
|
|
||||||
val aliasExpr = net.sergeych.lyng.obj.TypeDeclRef(body, nameToken.pos)
|
val aliasExpr = net.sergeych.lyng.obj.TypeDeclRef(body, nameToken.pos)
|
||||||
val initStmt = ExpressionStatement(aliasExpr, nameToken.pos)
|
val initStmt = ExpressionStatement(aliasExpr, nameToken.pos)
|
||||||
val slotPlan = slotPlanStack.lastOrNull()
|
val slotPlan = slotPlanStack.lastOrNull()
|
||||||
val slotIndex = slotPlan?.slots?.get(name)?.index
|
val slotIndex = slotPlan?.slots?.get(declaredName)?.index
|
||||||
val scopeId = slotPlan?.id
|
val scopeId = slotPlan?.id
|
||||||
return VarDeclStatement(
|
return VarDeclStatement(
|
||||||
name = name,
|
name = declaredName,
|
||||||
isMutable = false,
|
isMutable = false,
|
||||||
visibility = Visibility.Public,
|
visibility = Visibility.Public,
|
||||||
initializer = initStmt,
|
initializer = initStmt,
|
||||||
@ -749,6 +848,10 @@ class Compiler(
|
|||||||
val ids = resolveImplicitThisMemberIds(name, pos, implicitType)
|
val ids = resolveImplicitThisMemberIds(name, pos, implicitType)
|
||||||
return ImplicitThisMemberRef(name, pos, ids.fieldId, ids.methodId, implicitType)
|
return ImplicitThisMemberRef(name, pos, ids.fieldId, ids.methodId, implicitType)
|
||||||
}
|
}
|
||||||
|
if (classCtx != null && classCtx.classScopeMembers.contains(name)) {
|
||||||
|
resolutionSink?.referenceMember(name, pos, classCtx.name)
|
||||||
|
return ClassScopeMemberRef(name, pos, classCtx.name)
|
||||||
|
}
|
||||||
val modulePlan = moduleSlotPlan()
|
val modulePlan = moduleSlotPlan()
|
||||||
val moduleEntry = modulePlan?.slots?.get(name)
|
val moduleEntry = modulePlan?.slots?.get(name)
|
||||||
if (moduleEntry != null) {
|
if (moduleEntry != null) {
|
||||||
@ -1634,6 +1737,7 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
is QualifiedThisMethodSlotCallRef -> true
|
is QualifiedThisMethodSlotCallRef -> true
|
||||||
is QualifiedThisFieldSlotRef -> true
|
is QualifiedThisFieldSlotRef -> true
|
||||||
|
is ClassScopeMemberRef -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2015,56 +2119,65 @@ class Compiler(
|
|||||||
when (nt.type) {
|
when (nt.type) {
|
||||||
Token.Type.LPAREN -> {
|
Token.Type.LPAREN -> {
|
||||||
cc.next()
|
cc.next()
|
||||||
// instance method call
|
if (shouldTreatAsClassScopeCall(left, next.value)) {
|
||||||
val receiverType = if (next.value == "apply" || next.value == "run") {
|
val parsed = parseArgs(null)
|
||||||
inferReceiverTypeFromRef(left)
|
val args = parsed.first
|
||||||
} else null
|
val tailBlock = parsed.second
|
||||||
val parsed = parseArgs(receiverType)
|
isCall = true
|
||||||
val args = parsed.first
|
val field = FieldRef(left, next.value, isOptional)
|
||||||
val tailBlock = parsed.second
|
operand = CallRef(field, args, tailBlock, isOptional)
|
||||||
if (left is LocalVarRef && left.name == "scope") {
|
} else {
|
||||||
val first = args.firstOrNull()?.value
|
// instance method call
|
||||||
val const = (first as? ExpressionStatement)?.ref as? ConstRef
|
val receiverType = if (next.value == "apply" || next.value == "run") {
|
||||||
val name = const?.constValue as? ObjString
|
inferReceiverTypeFromRef(left)
|
||||||
if (name != null) {
|
} else null
|
||||||
resolutionSink?.referenceReflection(name.value, next.pos)
|
val parsed = parseArgs(receiverType)
|
||||||
|
val args = parsed.first
|
||||||
|
val tailBlock = parsed.second
|
||||||
|
if (left is LocalVarRef && left.name == "scope") {
|
||||||
|
val first = args.firstOrNull()?.value
|
||||||
|
val const = (first as? ExpressionStatement)?.ref as? ConstRef
|
||||||
|
val name = const?.constValue as? ObjString
|
||||||
|
if (name != null) {
|
||||||
|
resolutionSink?.referenceReflection(name.value, next.pos)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
isCall = true
|
||||||
isCall = true
|
operand = when (left) {
|
||||||
operand = when (left) {
|
is LocalVarRef -> if (left.name == "this") {
|
||||||
is LocalVarRef -> if (left.name == "this") {
|
resolutionSink?.referenceMember(next.value, next.pos)
|
||||||
resolutionSink?.referenceMember(next.value, next.pos)
|
val implicitType = currentImplicitThisTypeName()
|
||||||
val implicitType = currentImplicitThisTypeName()
|
val ids = resolveMemberIds(next.value, next.pos, implicitType)
|
||||||
val ids = resolveMemberIds(next.value, next.pos, implicitType)
|
ThisMethodSlotCallRef(next.value, ids.methodId, args, tailBlock, isOptional)
|
||||||
ThisMethodSlotCallRef(next.value, ids.methodId, args, tailBlock, isOptional)
|
} else if (left.name == "scope") {
|
||||||
} else if (left.name == "scope") {
|
if (next.value == "get" || next.value == "set") {
|
||||||
if (next.value == "get" || next.value == "set") {
|
val first = args.firstOrNull()?.value
|
||||||
val first = args.firstOrNull()?.value
|
val const = (first as? ExpressionStatement)?.ref as? ConstRef
|
||||||
val const = (first as? ExpressionStatement)?.ref as? ConstRef
|
val name = const?.constValue as? ObjString
|
||||||
val name = const?.constValue as? ObjString
|
if (name != null) {
|
||||||
if (name != null) {
|
resolutionSink?.referenceReflection(name.value, next.pos)
|
||||||
resolutionSink?.referenceReflection(name.value, next.pos)
|
}
|
||||||
}
|
}
|
||||||
|
MethodCallRef(left, next.value, args, tailBlock, isOptional)
|
||||||
|
} else {
|
||||||
|
enforceReceiverTypeForMember(left, next.value, next.pos)
|
||||||
|
MethodCallRef(left, next.value, args, tailBlock, isOptional)
|
||||||
}
|
}
|
||||||
MethodCallRef(left, next.value, args, tailBlock, isOptional)
|
is QualifiedThisRef ->
|
||||||
} else {
|
QualifiedThisMethodSlotCallRef(
|
||||||
enforceReceiverTypeForMember(left, next.value, next.pos)
|
left.typeName,
|
||||||
MethodCallRef(left, next.value, args, tailBlock, isOptional)
|
next.value,
|
||||||
}
|
resolveMemberIds(next.value, next.pos, left.typeName).methodId,
|
||||||
is QualifiedThisRef ->
|
args,
|
||||||
QualifiedThisMethodSlotCallRef(
|
tailBlock,
|
||||||
left.typeName,
|
isOptional
|
||||||
next.value,
|
).also {
|
||||||
resolveMemberIds(next.value, next.pos, left.typeName).methodId,
|
resolutionSink?.referenceMember(next.value, next.pos, left.typeName)
|
||||||
args,
|
}
|
||||||
tailBlock,
|
else -> {
|
||||||
isOptional
|
enforceReceiverTypeForMember(left, next.value, next.pos)
|
||||||
).also {
|
MethodCallRef(left, next.value, args, tailBlock, isOptional)
|
||||||
resolutionSink?.referenceMember(next.value, next.pos, left.typeName)
|
|
||||||
}
|
}
|
||||||
else -> {
|
|
||||||
enforceReceiverTypeForMember(left, next.value, next.pos)
|
|
||||||
MethodCallRef(left, next.value, args, tailBlock, isOptional)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2872,7 +2985,15 @@ class Compiler(
|
|||||||
pos: Pos,
|
pos: Pos,
|
||||||
seen: MutableSet<String>
|
seen: MutableSet<String>
|
||||||
): TypeDecl? {
|
): TypeDecl? {
|
||||||
val alias = typeAliases[name] ?: typeAliases[name.substringAfterLast('.')] ?: return null
|
val alias = run {
|
||||||
|
if (!name.contains('.')) {
|
||||||
|
val classCtx = currentEnclosingClassName()
|
||||||
|
if (classCtx != null) {
|
||||||
|
typeAliases["$classCtx.$name"]?.let { return@run it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
typeAliases[name] ?: typeAliases[name.substringAfterLast('.')]
|
||||||
|
} ?: return null
|
||||||
if (!seen.add(alias.name)) throw ScriptError(pos, "circular type alias: ${alias.name}")
|
if (!seen.add(alias.name)) throw ScriptError(pos, "circular type alias: ${alias.name}")
|
||||||
val bindings = buildTypeAliasBindings(alias, args, pos)
|
val bindings = buildTypeAliasBindings(alias, args, pos)
|
||||||
val substituted = substituteTypeAliasTypeVars(alias.body, bindings)
|
val substituted = substituteTypeAliasTypeVars(alias.body, bindings)
|
||||||
@ -3534,6 +3655,10 @@ class Compiler(
|
|||||||
?: nameObjClass[ref.name]
|
?: nameObjClass[ref.name]
|
||||||
?: resolveClassByName(ref.name)
|
?: resolveClassByName(ref.name)
|
||||||
}
|
}
|
||||||
|
is ClassScopeMemberRef -> {
|
||||||
|
val targetClass = resolveClassByName(ref.ownerClassName()) ?: return null
|
||||||
|
inferFieldReturnClass(targetClass, ref.name)
|
||||||
|
}
|
||||||
is ListLiteralRef -> ObjList.type
|
is ListLiteralRef -> ObjList.type
|
||||||
is MapLiteralRef -> ObjMap.type
|
is MapLiteralRef -> ObjMap.type
|
||||||
is RangeRef -> ObjRange.type
|
is RangeRef -> ObjRange.type
|
||||||
@ -3568,6 +3693,10 @@ class Compiler(
|
|||||||
is LocalVarRef -> nameObjClass[ref.name]
|
is LocalVarRef -> nameObjClass[ref.name]
|
||||||
?: nameTypeDecl[ref.name]?.let { resolveTypeDeclObjClass(it) }
|
?: nameTypeDecl[ref.name]?.let { resolveTypeDeclObjClass(it) }
|
||||||
?: resolveClassByName(ref.name)
|
?: resolveClassByName(ref.name)
|
||||||
|
is ClassScopeMemberRef -> {
|
||||||
|
val targetClass = resolveClassByName(ref.ownerClassName())
|
||||||
|
inferFieldReturnClass(targetClass, ref.name)
|
||||||
|
}
|
||||||
is ImplicitThisMemberRef -> {
|
is ImplicitThisMemberRef -> {
|
||||||
val typeName = ref.preferredThisTypeName() ?: currentImplicitThisTypeName()
|
val typeName = ref.preferredThisTypeName() ?: currentImplicitThisTypeName()
|
||||||
val targetClass = typeName?.let { resolveClassByName(it) }
|
val targetClass = typeName?.let { resolveClassByName(it) }
|
||||||
@ -3632,6 +3761,11 @@ class Compiler(
|
|||||||
is ObjString -> ObjString.type
|
is ObjString -> ObjString.type
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
is FieldRef -> {
|
||||||
|
val receiverClass = resolveReceiverClassForMember(target.target) ?: return null
|
||||||
|
if (!isClassScopeCallableMember(receiverClass.className, target.name)) return null
|
||||||
|
resolveClassByName("${receiverClass.className}.${target.name}")
|
||||||
|
}
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3828,6 +3962,11 @@ class Compiler(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun shouldTreatAsClassScopeCall(left: ObjRef, memberName: String): Boolean {
|
||||||
|
val receiverClass = resolveReceiverClassForMember(left) ?: return false
|
||||||
|
return isClassScopeCallableMember(receiverClass.className, memberName)
|
||||||
|
}
|
||||||
|
|
||||||
private fun enforceReceiverTypeForMember(left: ObjRef, memberName: String, pos: Pos) {
|
private fun enforceReceiverTypeForMember(left: ObjRef, memberName: String, pos: Pos) {
|
||||||
if (left is LocalVarRef && left.name == "scope") return
|
if (left is LocalVarRef && left.name == "scope") return
|
||||||
if (left is LocalSlotRef && left.name == "scope") return
|
if (left is LocalSlotRef && left.name == "scope") return
|
||||||
@ -5098,7 +5237,22 @@ class Compiler(
|
|||||||
val doc = pendingDeclDoc ?: consumePendingDoc()
|
val doc = pendingDeclDoc ?: consumePendingDoc()
|
||||||
pendingDeclDoc = null
|
pendingDeclDoc = null
|
||||||
pendingDeclStart = null
|
pendingDeclStart = null
|
||||||
resolutionSink?.declareSymbol(nameToken.value, SymbolKind.ENUM, isMutable = false, pos = nameToken.pos)
|
val declaredName = nameToken.value
|
||||||
|
val outerClassName = currentEnclosingClassName()
|
||||||
|
val qualifiedName = if (outerClassName != null) "$outerClassName.$declaredName" else declaredName
|
||||||
|
resolutionSink?.declareSymbol(declaredName, SymbolKind.ENUM, isMutable = false, pos = nameToken.pos)
|
||||||
|
if (outerClassName != null) {
|
||||||
|
val outerCtx = codeContexts.asReversed().firstOrNull { it is CodeContext.ClassBody } as? CodeContext.ClassBody
|
||||||
|
outerCtx?.classScopeMembers?.add(declaredName)
|
||||||
|
registerClassScopeMember(outerClassName, declaredName)
|
||||||
|
registerClassScopeCallableMember(outerClassName, declaredName)
|
||||||
|
}
|
||||||
|
val lifted = if (cc.peekNextNonWhitespace().type == Token.Type.STAR) {
|
||||||
|
cc.nextNonWhitespace()
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
// so far only simplest enums:
|
// so far only simplest enums:
|
||||||
val names = mutableListOf<String>()
|
val names = mutableListOf<String>()
|
||||||
val positions = mutableListOf<Pos>()
|
val positions = mutableListOf<Pos>()
|
||||||
@ -5134,7 +5288,7 @@ class Compiler(
|
|||||||
miniSink?.onEnumDecl(
|
miniSink?.onEnumDecl(
|
||||||
MiniEnumDecl(
|
MiniEnumDecl(
|
||||||
range = MiniRange(startPos, cc.currentPos()),
|
range = MiniRange(startPos, cc.currentPos()),
|
||||||
name = nameToken.value,
|
name = declaredName,
|
||||||
entries = names,
|
entries = names,
|
||||||
doc = doc,
|
doc = doc,
|
||||||
nameStart = nameToken.pos,
|
nameStart = nameToken.pos,
|
||||||
@ -5142,28 +5296,69 @@ class Compiler(
|
|||||||
entryPositions = positions
|
entryPositions = positions
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
if (lifted) {
|
||||||
|
val classCtx = codeContexts.asReversed().firstOrNull { it is CodeContext.ClassBody } as? CodeContext.ClassBody
|
||||||
|
val conflicts = when {
|
||||||
|
classCtx != null -> names.filter { entry ->
|
||||||
|
classCtx.classScopeMembers.contains(entry) || classCtx.declaredMembers.contains(entry)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val modulePlan = moduleSlotPlan()
|
||||||
|
names.filter { entry -> modulePlan?.slots?.containsKey(entry) == true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (conflicts.isNotEmpty()) {
|
||||||
|
val entry = conflicts.first()
|
||||||
|
val disambiguation = "${qualifiedName}.$entry"
|
||||||
|
throw ScriptError(
|
||||||
|
nameToken.pos,
|
||||||
|
"lifted enum entry '$entry' conflicts with existing member; use '$disambiguation'"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
val fieldIds = LinkedHashMap<String, Int>(names.size + 1)
|
val fieldIds = LinkedHashMap<String, Int>(names.size + 1)
|
||||||
fieldIds["entries"] = 0
|
fieldIds["entries"] = 0
|
||||||
for ((index, entry) in names.withIndex()) {
|
for ((index, entry) in names.withIndex()) {
|
||||||
fieldIds[entry] = index + 1
|
fieldIds[entry] = index + 1
|
||||||
}
|
}
|
||||||
val methodIds = mapOf("valueOf" to 0)
|
val methodIds = mapOf("valueOf" to 0)
|
||||||
compileClassInfos[nameToken.value] = CompileClassInfo(
|
compileClassInfos[qualifiedName] = CompileClassInfo(
|
||||||
name = nameToken.value,
|
name = qualifiedName,
|
||||||
fieldIds = fieldIds,
|
fieldIds = fieldIds,
|
||||||
methodIds = methodIds,
|
methodIds = methodIds,
|
||||||
nextFieldId = fieldIds.size,
|
nextFieldId = fieldIds.size,
|
||||||
nextMethodId = methodIds.size,
|
nextMethodId = methodIds.size,
|
||||||
baseNames = listOf("Object")
|
baseNames = listOf("Object")
|
||||||
)
|
)
|
||||||
enumEntriesByName[nameToken.value] = names.toList()
|
enumEntriesByName[qualifiedName] = names.toList()
|
||||||
|
registerClassScopeFieldType(outerClassName, declaredName, qualifiedName)
|
||||||
|
if (lifted && outerClassName != null) {
|
||||||
|
val outerCtx = codeContexts.asReversed().firstOrNull { it is CodeContext.ClassBody } as? CodeContext.ClassBody
|
||||||
|
for (entry in names) {
|
||||||
|
outerCtx?.classScopeMembers?.add(entry)
|
||||||
|
registerClassScopeMember(outerClassName, entry)
|
||||||
|
registerClassScopeFieldType(outerClassName, entry, qualifiedName)
|
||||||
|
}
|
||||||
|
} else if (lifted) {
|
||||||
|
for (entry in names) {
|
||||||
|
declareLocalName(entry, isMutable = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val stmtPos = startPos
|
val stmtPos = startPos
|
||||||
val enumDeclStatement = object : Statement() {
|
val enumDeclStatement = object : Statement() {
|
||||||
override val pos: Pos = stmtPos
|
override val pos: Pos = stmtPos
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
val enumClass = ObjEnumClass.createSimpleEnum(nameToken.value, names)
|
val enumClass = ObjEnumClass.createSimpleEnum(qualifiedName, names)
|
||||||
scope.addItem(nameToken.value, false, enumClass, recordType = ObjRecord.Type.Enum)
|
scope.addItem(declaredName, false, enumClass, recordType = ObjRecord.Type.Enum)
|
||||||
|
if (lifted) {
|
||||||
|
for (entry in names) {
|
||||||
|
val rec = enumClass.getInstanceMemberOrNull(entry, includeAbstract = false, includeStatic = true)
|
||||||
|
if (rec != null) {
|
||||||
|
scope.addItem(entry, false, rec.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return enumClass
|
return enumClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5175,10 +5370,18 @@ class Compiler(
|
|||||||
val nameToken = if (next.type == Token.Type.ID) cc.requireToken(Token.Type.ID) else null
|
val nameToken = if (next.type == Token.Type.ID) cc.requireToken(Token.Type.ID) else null
|
||||||
|
|
||||||
val startPos = pendingDeclStart ?: nameToken?.pos ?: cc.current().pos
|
val startPos = pendingDeclStart ?: nameToken?.pos ?: cc.current().pos
|
||||||
val className = nameToken?.value ?: generateAnonName(startPos)
|
val declaredName = nameToken?.value
|
||||||
if (nameToken != null) {
|
val outerClassName = currentEnclosingClassName()
|
||||||
resolutionSink?.declareSymbol(nameToken.value, SymbolKind.CLASS, isMutable = false, pos = nameToken.pos)
|
val baseName = declaredName ?: generateAnonName(startPos)
|
||||||
declareLocalName(nameToken.value, isMutable = false)
|
val className = if (declaredName != null && outerClassName != null) "$outerClassName.$declaredName" else baseName
|
||||||
|
if (declaredName != null) {
|
||||||
|
resolutionSink?.declareSymbol(declaredName, SymbolKind.CLASS, isMutable = false, pos = nameToken!!.pos)
|
||||||
|
declareLocalName(declaredName, isMutable = false)
|
||||||
|
if (outerClassName != null) {
|
||||||
|
val outerCtx = codeContexts.asReversed().firstOrNull { it is CodeContext.ClassBody } as? CodeContext.ClassBody
|
||||||
|
outerCtx?.classScopeMembers?.add(declaredName)
|
||||||
|
registerClassScopeMember(outerClassName, declaredName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val doc = pendingDeclDoc ?: consumePendingDoc()
|
val doc = pendingDeclDoc ?: consumePendingDoc()
|
||||||
@ -5215,7 +5418,7 @@ class Compiler(
|
|||||||
run {
|
run {
|
||||||
val node = MiniClassDecl(
|
val node = MiniClassDecl(
|
||||||
range = MiniRange(startPos, cc.currentPos()),
|
range = MiniRange(startPos, cc.currentPos()),
|
||||||
name = className,
|
name = declaredName ?: className,
|
||||||
bases = baseSpecs.map { it.name },
|
bases = baseSpecs.map { it.name },
|
||||||
bodyRange = null,
|
bodyRange = null,
|
||||||
doc = doc,
|
doc = doc,
|
||||||
@ -5232,6 +5435,8 @@ class Compiler(
|
|||||||
resolutionSink?.enterScope(ScopeKind.CLASS, startPos, className, baseSpecs.map { it.name })
|
resolutionSink?.enterScope(ScopeKind.CLASS, startPos, className, baseSpecs.map { it.name })
|
||||||
val classCtx = codeContexts.lastOrNull() as? CodeContext.ClassBody
|
val classCtx = codeContexts.lastOrNull() as? CodeContext.ClassBody
|
||||||
classCtx?.let { ctx ->
|
classCtx?.let { ctx ->
|
||||||
|
val callableMembers = classScopeCallableMembersByClassName.getOrPut(className) { mutableSetOf() }
|
||||||
|
predeclareClassScopeMembers(className, ctx.classScopeMembers, callableMembers)
|
||||||
val baseIds = collectBaseMemberIds(baseSpecs.map { it.name })
|
val baseIds = collectBaseMemberIds(baseSpecs.map { it.name })
|
||||||
ctx.memberFieldIds.putAll(baseIds.fieldIds)
|
ctx.memberFieldIds.putAll(baseIds.fieldIds)
|
||||||
ctx.memberMethodIds.putAll(baseIds.methodIds)
|
ctx.memberMethodIds.putAll(baseIds.methodIds)
|
||||||
@ -5254,6 +5459,9 @@ class Compiler(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (declaredName != null) {
|
||||||
|
registerClassScopeFieldType(outerClassName, declaredName, className)
|
||||||
|
}
|
||||||
parsed
|
parsed
|
||||||
} finally {
|
} finally {
|
||||||
slotPlanStack.removeLast()
|
slotPlanStack.removeLast()
|
||||||
@ -5269,7 +5477,7 @@ class Compiler(
|
|||||||
run {
|
run {
|
||||||
val node = MiniClassDecl(
|
val node = MiniClassDecl(
|
||||||
range = MiniRange(startPos, cc.currentPos()),
|
range = MiniRange(startPos, cc.currentPos()),
|
||||||
name = className,
|
name = declaredName ?: className,
|
||||||
bases = baseSpecs.map { it.name },
|
bases = baseSpecs.map { it.name },
|
||||||
bodyRange = null,
|
bodyRange = null,
|
||||||
doc = doc,
|
doc = doc,
|
||||||
@ -5291,6 +5499,9 @@ class Compiler(
|
|||||||
baseNames = baseSpecs.map { it.name }
|
baseNames = baseSpecs.map { it.name }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (declaredName != null) {
|
||||||
|
registerClassScopeFieldType(outerClassName, declaredName, className)
|
||||||
|
}
|
||||||
cc.restorePos(saved)
|
cc.restorePos(saved)
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@ -5324,8 +5535,8 @@ class Compiler(
|
|||||||
|
|
||||||
// Create instance (singleton)
|
// Create instance (singleton)
|
||||||
val instance = newClass.callOn(scope.createChildScope(Arguments.EMPTY))
|
val instance = newClass.callOn(scope.createChildScope(Arguments.EMPTY))
|
||||||
if (nameToken != null)
|
if (declaredName != null)
|
||||||
scope.addItem(className, false, instance)
|
scope.addItem(declaredName, false, instance)
|
||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5338,8 +5549,17 @@ class Compiler(
|
|||||||
val doc = pendingDeclDoc ?: consumePendingDoc()
|
val doc = pendingDeclDoc ?: consumePendingDoc()
|
||||||
pendingDeclDoc = null
|
pendingDeclDoc = null
|
||||||
pendingDeclStart = null
|
pendingDeclStart = null
|
||||||
resolutionSink?.declareSymbol(nameToken.value, SymbolKind.CLASS, isMutable = false, pos = nameToken.pos)
|
val declaredName = nameToken.value
|
||||||
return inCodeContext(CodeContext.ClassBody(nameToken.value, isExtern = isExtern)) {
|
val outerClassName = currentEnclosingClassName()
|
||||||
|
val qualifiedName = if (outerClassName != null) "$outerClassName.$declaredName" else declaredName
|
||||||
|
resolutionSink?.declareSymbol(declaredName, SymbolKind.CLASS, isMutable = false, pos = nameToken.pos)
|
||||||
|
if (outerClassName != null) {
|
||||||
|
val outerCtx = codeContexts.asReversed().firstOrNull { it is CodeContext.ClassBody } as? CodeContext.ClassBody
|
||||||
|
outerCtx?.classScopeMembers?.add(declaredName)
|
||||||
|
registerClassScopeMember(outerClassName, declaredName)
|
||||||
|
registerClassScopeCallableMember(outerClassName, declaredName)
|
||||||
|
}
|
||||||
|
return inCodeContext(CodeContext.ClassBody(qualifiedName, isExtern = isExtern)) {
|
||||||
val classCtx = codeContexts.lastOrNull() as? CodeContext.ClassBody
|
val classCtx = codeContexts.lastOrNull() as? CodeContext.ClassBody
|
||||||
val typeParamDecls = parseTypeParamList()
|
val typeParamDecls = parseTypeParamList()
|
||||||
classCtx?.typeParamDecls = typeParamDecls
|
classCtx?.typeParamDecls = typeParamDecls
|
||||||
@ -5366,7 +5586,7 @@ class Compiler(
|
|||||||
if (param.accessType != null) {
|
if (param.accessType != null) {
|
||||||
val declClass = resolveTypeDeclObjClass(param.type)
|
val declClass = resolveTypeDeclObjClass(param.type)
|
||||||
if (declClass != null) {
|
if (declClass != null) {
|
||||||
classFieldTypesByName.getOrPut(nameToken.value) { mutableMapOf() }[param.name] = declClass
|
classFieldTypesByName.getOrPut(qualifiedName) { mutableMapOf() }[param.name] = declClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5440,7 +5660,7 @@ class Compiler(
|
|||||||
run {
|
run {
|
||||||
val node = MiniClassDecl(
|
val node = MiniClassDecl(
|
||||||
range = MiniRange(startPos, cc.currentPos()),
|
range = MiniRange(startPos, cc.currentPos()),
|
||||||
name = nameToken.value,
|
name = declaredName,
|
||||||
bases = baseSpecs.map { it.name },
|
bases = baseSpecs.map { it.name },
|
||||||
bodyRange = null,
|
bodyRange = null,
|
||||||
ctorFields = ctorFields,
|
ctorFields = ctorFields,
|
||||||
@ -5453,8 +5673,8 @@ class Compiler(
|
|||||||
// parse body
|
// parse body
|
||||||
val bodyStart = next.pos
|
val bodyStart = next.pos
|
||||||
slotPlanStack.add(classSlotPlan)
|
slotPlanStack.add(classSlotPlan)
|
||||||
resolutionSink?.declareClass(nameToken.value, baseSpecs.map { it.name }, startPos)
|
resolutionSink?.declareClass(qualifiedName, baseSpecs.map { it.name }, startPos)
|
||||||
resolutionSink?.enterScope(ScopeKind.CLASS, startPos, nameToken.value, baseSpecs.map { it.name })
|
resolutionSink?.enterScope(ScopeKind.CLASS, startPos, qualifiedName, baseSpecs.map { it.name })
|
||||||
constructorArgsDeclaration?.params?.forEach { param ->
|
constructorArgsDeclaration?.params?.forEach { param ->
|
||||||
val accessType = param.accessType
|
val accessType = param.accessType
|
||||||
val kind = if (accessType != null) SymbolKind.MEMBER else SymbolKind.PARAM
|
val kind = if (accessType != null) SymbolKind.MEMBER else SymbolKind.PARAM
|
||||||
@ -5463,8 +5683,10 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
val st = try {
|
val st = try {
|
||||||
classCtx?.let { ctx ->
|
classCtx?.let { ctx ->
|
||||||
|
val callableMembers = classScopeCallableMembersByClassName.getOrPut(qualifiedName) { mutableSetOf() }
|
||||||
|
predeclareClassScopeMembers(qualifiedName, ctx.classScopeMembers, callableMembers)
|
||||||
predeclareClassMembers(ctx.declaredMembers, ctx.memberOverrides)
|
predeclareClassMembers(ctx.declaredMembers, ctx.memberOverrides)
|
||||||
val existingExternInfo = if (isExtern) resolveCompileClassInfo(nameToken.value) else null
|
val existingExternInfo = if (isExtern) resolveCompileClassInfo(qualifiedName) else null
|
||||||
if (existingExternInfo != null) {
|
if (existingExternInfo != null) {
|
||||||
ctx.memberFieldIds.putAll(existingExternInfo.fieldIds)
|
ctx.memberFieldIds.putAll(existingExternInfo.fieldIds)
|
||||||
ctx.memberMethodIds.putAll(existingExternInfo.methodIds)
|
ctx.memberMethodIds.putAll(existingExternInfo.methodIds)
|
||||||
@ -5474,18 +5696,18 @@ class Compiler(
|
|||||||
val hasField = member in existingExternInfo.fieldIds
|
val hasField = member in existingExternInfo.fieldIds
|
||||||
val hasMethod = member in existingExternInfo.methodIds
|
val hasMethod = member in existingExternInfo.methodIds
|
||||||
if (!hasField && !hasMethod) {
|
if (!hasField && !hasMethod) {
|
||||||
throw ScriptError(nameToken.pos, "extern member $member is not found in runtime class ${nameToken.value}")
|
throw ScriptError(nameToken.pos, "extern member $member is not found in runtime class $qualifiedName")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
constructorArgsDeclaration?.params?.forEach { param ->
|
constructorArgsDeclaration?.params?.forEach { param ->
|
||||||
if (param.accessType == null) return@forEach
|
if (param.accessType == null) return@forEach
|
||||||
if (!ctx.memberFieldIds.containsKey(param.name)) {
|
if (!ctx.memberFieldIds.containsKey(param.name)) {
|
||||||
val fieldId = existingExternInfo.fieldIds[param.name]
|
val fieldId = existingExternInfo.fieldIds[param.name]
|
||||||
?: throw ScriptError(nameToken.pos, "extern field ${param.name} is not found in runtime class ${nameToken.value}")
|
?: throw ScriptError(nameToken.pos, "extern field ${param.name} is not found in runtime class $qualifiedName")
|
||||||
ctx.memberFieldIds[param.name] = fieldId
|
ctx.memberFieldIds[param.name] = fieldId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileClassInfos[nameToken.value] = existingExternInfo
|
compileClassInfos[qualifiedName] = existingExternInfo
|
||||||
} else {
|
} else {
|
||||||
val baseIds = collectBaseMemberIds(baseSpecs.map { it.name })
|
val baseIds = collectBaseMemberIds(baseSpecs.map { it.name })
|
||||||
ctx.memberFieldIds.putAll(baseIds.fieldIds)
|
ctx.memberFieldIds.putAll(baseIds.fieldIds)
|
||||||
@ -5512,8 +5734,8 @@ class Compiler(
|
|||||||
ctx.memberFieldIds[param.name] = ctx.nextFieldId++
|
ctx.memberFieldIds[param.name] = ctx.nextFieldId++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileClassInfos[nameToken.value] = CompileClassInfo(
|
compileClassInfos[qualifiedName] = CompileClassInfo(
|
||||||
name = nameToken.value,
|
name = qualifiedName,
|
||||||
fieldIds = ctx.memberFieldIds.toMap(),
|
fieldIds = ctx.memberFieldIds.toMap(),
|
||||||
methodIds = ctx.memberMethodIds.toMap(),
|
methodIds = ctx.memberMethodIds.toMap(),
|
||||||
nextFieldId = ctx.nextFieldId,
|
nextFieldId = ctx.nextFieldId,
|
||||||
@ -5527,8 +5749,8 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
if (!isExtern) {
|
if (!isExtern) {
|
||||||
classCtx?.let { ctx ->
|
classCtx?.let { ctx ->
|
||||||
compileClassInfos[nameToken.value] = CompileClassInfo(
|
compileClassInfos[qualifiedName] = CompileClassInfo(
|
||||||
name = nameToken.value,
|
name = qualifiedName,
|
||||||
fieldIds = ctx.memberFieldIds.toMap(),
|
fieldIds = ctx.memberFieldIds.toMap(),
|
||||||
methodIds = ctx.memberMethodIds.toMap(),
|
methodIds = ctx.memberMethodIds.toMap(),
|
||||||
nextFieldId = ctx.nextFieldId,
|
nextFieldId = ctx.nextFieldId,
|
||||||
@ -5537,6 +5759,7 @@ class Compiler(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
registerClassScopeFieldType(outerClassName, declaredName, qualifiedName)
|
||||||
parsed
|
parsed
|
||||||
} finally {
|
} finally {
|
||||||
slotPlanStack.removeLast()
|
slotPlanStack.removeLast()
|
||||||
@ -5552,7 +5775,7 @@ class Compiler(
|
|||||||
run {
|
run {
|
||||||
val node = MiniClassDecl(
|
val node = MiniClassDecl(
|
||||||
range = MiniRange(startPos, cc.currentPos()),
|
range = MiniRange(startPos, cc.currentPos()),
|
||||||
name = nameToken.value,
|
name = declaredName,
|
||||||
bases = baseSpecs.map { it.name },
|
bases = baseSpecs.map { it.name },
|
||||||
bodyRange = null,
|
bodyRange = null,
|
||||||
ctorFields = ctorFields,
|
ctorFields = ctorFields,
|
||||||
@ -5562,9 +5785,9 @@ class Compiler(
|
|||||||
)
|
)
|
||||||
miniSink?.onClassDecl(node)
|
miniSink?.onClassDecl(node)
|
||||||
}
|
}
|
||||||
resolutionSink?.declareClass(nameToken.value, baseSpecs.map { it.name }, startPos)
|
resolutionSink?.declareClass(qualifiedName, baseSpecs.map { it.name }, startPos)
|
||||||
classCtx?.let { ctx ->
|
classCtx?.let { ctx ->
|
||||||
val existingExternInfo = if (isExtern) resolveCompileClassInfo(nameToken.value) else null
|
val existingExternInfo = if (isExtern) resolveCompileClassInfo(qualifiedName) else null
|
||||||
if (existingExternInfo != null) {
|
if (existingExternInfo != null) {
|
||||||
ctx.memberFieldIds.putAll(existingExternInfo.fieldIds)
|
ctx.memberFieldIds.putAll(existingExternInfo.fieldIds)
|
||||||
ctx.memberMethodIds.putAll(existingExternInfo.methodIds)
|
ctx.memberMethodIds.putAll(existingExternInfo.methodIds)
|
||||||
@ -5574,18 +5797,18 @@ class Compiler(
|
|||||||
val hasField = member in existingExternInfo.fieldIds
|
val hasField = member in existingExternInfo.fieldIds
|
||||||
val hasMethod = member in existingExternInfo.methodIds
|
val hasMethod = member in existingExternInfo.methodIds
|
||||||
if (!hasField && !hasMethod) {
|
if (!hasField && !hasMethod) {
|
||||||
throw ScriptError(nameToken.pos, "extern member $member is not found in runtime class ${nameToken.value}")
|
throw ScriptError(nameToken.pos, "extern member $member is not found in runtime class $qualifiedName")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
constructorArgsDeclaration?.params?.forEach { param ->
|
constructorArgsDeclaration?.params?.forEach { param ->
|
||||||
if (param.accessType == null) return@forEach
|
if (param.accessType == null) return@forEach
|
||||||
if (!ctx.memberFieldIds.containsKey(param.name)) {
|
if (!ctx.memberFieldIds.containsKey(param.name)) {
|
||||||
val fieldId = existingExternInfo.fieldIds[param.name]
|
val fieldId = existingExternInfo.fieldIds[param.name]
|
||||||
?: throw ScriptError(nameToken.pos, "extern field ${param.name} is not found in runtime class ${nameToken.value}")
|
?: throw ScriptError(nameToken.pos, "extern field ${param.name} is not found in runtime class $qualifiedName")
|
||||||
ctx.memberFieldIds[param.name] = fieldId
|
ctx.memberFieldIds[param.name] = fieldId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileClassInfos[nameToken.value] = existingExternInfo
|
compileClassInfos[qualifiedName] = existingExternInfo
|
||||||
} else {
|
} else {
|
||||||
val baseIds = collectBaseMemberIds(baseSpecs.map { it.name })
|
val baseIds = collectBaseMemberIds(baseSpecs.map { it.name })
|
||||||
ctx.memberFieldIds.putAll(baseIds.fieldIds)
|
ctx.memberFieldIds.putAll(baseIds.fieldIds)
|
||||||
@ -5612,8 +5835,8 @@ class Compiler(
|
|||||||
ctx.memberFieldIds[param.name] = ctx.nextFieldId++
|
ctx.memberFieldIds[param.name] = ctx.nextFieldId++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileClassInfos[nameToken.value] = CompileClassInfo(
|
compileClassInfos[qualifiedName] = CompileClassInfo(
|
||||||
name = nameToken.value,
|
name = qualifiedName,
|
||||||
fieldIds = ctx.memberFieldIds.toMap(),
|
fieldIds = ctx.memberFieldIds.toMap(),
|
||||||
methodIds = ctx.memberMethodIds.toMap(),
|
methodIds = ctx.memberMethodIds.toMap(),
|
||||||
nextFieldId = ctx.nextFieldId,
|
nextFieldId = ctx.nextFieldId,
|
||||||
@ -5622,6 +5845,7 @@ class Compiler(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
registerClassScopeFieldType(outerClassName, declaredName, qualifiedName)
|
||||||
// restore if no body starts here
|
// restore if no body starts here
|
||||||
cc.restorePos(saved)
|
cc.restorePos(saved)
|
||||||
null
|
null
|
||||||
@ -5631,7 +5855,7 @@ class Compiler(
|
|||||||
val initScope = popInitScope()
|
val initScope = popInitScope()
|
||||||
|
|
||||||
// create class
|
// create class
|
||||||
val className = nameToken.value
|
val className = qualifiedName
|
||||||
|
|
||||||
// @Suppress("UNUSED_VARIABLE") val defaultAccess = if (isStruct) AccessType.Variable else AccessType.Initialization
|
// @Suppress("UNUSED_VARIABLE") val defaultAccess = if (isStruct) AccessType.Variable else AccessType.Initialization
|
||||||
// @Suppress("UNUSED_VARIABLE") val defaultVisibility = Visibility.Public
|
// @Suppress("UNUSED_VARIABLE") val defaultVisibility = Visibility.Public
|
||||||
@ -5698,7 +5922,7 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.addItem(className, false, newClass)
|
scope.addItem(declaredName, false, newClass)
|
||||||
// Prepare class scope for class-scope members (static) and future registrations
|
// Prepare class scope for class-scope members (static) and future registrations
|
||||||
val classScope = scope.createChildScope(newThisObj = newClass)
|
val classScope = scope.createChildScope(newThisObj = newClass)
|
||||||
// Set lexical class context for visibility tagging inside class body
|
// Set lexical class context for visibility tagging inside class body
|
||||||
@ -5715,7 +5939,7 @@ class Compiler(
|
|||||||
return newClass
|
return newClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClassDeclStatement(classDeclStatement, startPos, nameToken.value)
|
ClassDeclStatement(classDeclStatement, startPos, qualifiedName)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -6721,6 +6945,11 @@ class Compiler(
|
|||||||
target is ImplicitThisMemberRef && target.name == "iterator" -> ObjIterator
|
target is ImplicitThisMemberRef && target.name == "iterator" -> ObjIterator
|
||||||
target is ThisFieldSlotRef && target.name == "iterator" -> ObjIterator
|
target is ThisFieldSlotRef && target.name == "iterator" -> ObjIterator
|
||||||
target is FieldRef && target.name == "iterator" -> ObjIterator
|
target is FieldRef && target.name == "iterator" -> ObjIterator
|
||||||
|
target is FieldRef -> {
|
||||||
|
val receiverClass = resolveReceiverClassForMember(target.target) ?: return null
|
||||||
|
if (!isClassScopeCallableMember(receiverClass.className, target.name)) return null
|
||||||
|
resolveClassByName("${receiverClass.className}.${target.name}")
|
||||||
|
}
|
||||||
target is ConstRef -> when (val value = target.constValue) {
|
target is ConstRef -> when (val value = target.constValue) {
|
||||||
is ObjClass -> value
|
is ObjClass -> value
|
||||||
is ObjString -> ObjString.type
|
is ObjString -> ObjString.type
|
||||||
@ -6774,6 +7003,12 @@ class Compiler(
|
|||||||
else -> return null
|
else -> return null
|
||||||
}
|
}
|
||||||
val name = rawName.substringAfterLast('.')
|
val name = rawName.substringAfterLast('.')
|
||||||
|
if (!rawName.contains('.')) {
|
||||||
|
val classCtx = currentEnclosingClassName()
|
||||||
|
if (classCtx != null) {
|
||||||
|
resolveClassByName("$classCtx.$rawName")?.let { return it }
|
||||||
|
}
|
||||||
|
}
|
||||||
return when (name) {
|
return when (name) {
|
||||||
"Object", "Obj" -> Obj.rootObjectType
|
"Object", "Obj" -> Obj.rootObjectType
|
||||||
"String" -> ObjString.type
|
"String" -> ObjString.type
|
||||||
|
|||||||
@ -242,6 +242,7 @@ private fun applyEnumConstantHeuristics(
|
|||||||
var j = i + 1
|
var j = i + 1
|
||||||
// skip optional whitespace/newlines tokens are separate types, so we just check IDs and braces
|
// skip optional whitespace/newlines tokens are separate types, so we just check IDs and braces
|
||||||
if (j < tokens.size && tokens[j].type == Type.ID) j++ else { i++; continue }
|
if (j < tokens.size && tokens[j].type == Type.ID) j++ else { i++; continue }
|
||||||
|
if (j < tokens.size && tokens[j].type == Type.STAR) j++
|
||||||
if (j < tokens.size && tokens[j].type == Type.LBRACE) {
|
if (j < tokens.size && tokens[j].type == Type.LBRACE) {
|
||||||
j++
|
j++
|
||||||
while (j < tokens.size) {
|
while (j < tokens.size) {
|
||||||
|
|||||||
@ -2133,6 +2133,50 @@ class ImplicitThisMemberRef(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to a class-scope member in the nearest enclosing class context.
|
||||||
|
*/
|
||||||
|
class ClassScopeMemberRef(
|
||||||
|
val name: String,
|
||||||
|
private val atPos: Pos,
|
||||||
|
private val ownerClassName: String
|
||||||
|
) : ObjRef {
|
||||||
|
internal fun ownerClassName(): String = ownerClassName
|
||||||
|
override fun forEachVariable(block: (String) -> Unit) {
|
||||||
|
block(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun forEachVariableWithPos(block: (String, Pos) -> Unit) {
|
||||||
|
block(name, atPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveClass(scope: Scope): ObjClass {
|
||||||
|
scope.thisVariants.firstOrNull { it is ObjClass && it.className == ownerClassName }?.let {
|
||||||
|
return it as ObjClass
|
||||||
|
}
|
||||||
|
val cls = scope[ownerClassName]?.value as? ObjClass
|
||||||
|
if (cls != null) return cls
|
||||||
|
scope.raiseSymbolNotFound(ownerClassName)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun get(scope: Scope): ObjRecord {
|
||||||
|
scope.pos = atPos
|
||||||
|
val cls = resolveClass(scope)
|
||||||
|
return cls.readField(scope, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun evalValue(scope: Scope): Obj {
|
||||||
|
val rec = get(scope)
|
||||||
|
return scope.resolve(rec, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun setAt(pos: Pos, scope: Scope, newValue: Obj) {
|
||||||
|
scope.pos = atPos
|
||||||
|
val cls = resolveClass(scope)
|
||||||
|
cls.writeField(scope, name, newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fast path for implicit member calls in class bodies: `foo(...)` resolves locals first,
|
* Fast path for implicit member calls in class bodies: `foo(...)` resolves locals first,
|
||||||
* then falls back to member lookup on `this`.
|
* then falls back to member lookup on `this`.
|
||||||
|
|||||||
@ -3545,6 +3545,45 @@ class ScriptTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun nestedTypesAndObjects() = runTest {
|
||||||
|
eval(
|
||||||
|
"""
|
||||||
|
class A {
|
||||||
|
class B(x?)
|
||||||
|
object Inner { val foo = "bar" }
|
||||||
|
}
|
||||||
|
|
||||||
|
val ab = A.B()
|
||||||
|
assertEquals(ab.x, null)
|
||||||
|
assertEquals(A.Inner.foo, "bar")
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun liftedEnumEntries() = runTest {
|
||||||
|
eval(
|
||||||
|
"""
|
||||||
|
class A {
|
||||||
|
enum E* { One, Two }
|
||||||
|
}
|
||||||
|
assertEquals(A.One, A.E.One)
|
||||||
|
assertEquals(A.Two, A.E.Two)
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
assertFailsWith<ScriptError> {
|
||||||
|
eval(
|
||||||
|
"""
|
||||||
|
class A {
|
||||||
|
val One = 1
|
||||||
|
enum E* { One, Two }
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun enumSerializationTest() = runTest {
|
fun enumSerializationTest() = runTest {
|
||||||
eval(
|
eval(
|
||||||
|
|||||||
@ -75,6 +75,18 @@ class B { fun foo() = 2 }
|
|||||||
class C : A, B { } // error: requires override
|
class C : A, B { } // error: requires override
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Class Namespace (Nested Declarations)
|
||||||
|
Nested classes, objects, enums, and type aliases belong to the **class namespace** of their enclosing class. They are not instance members and do not capture an outer instance.
|
||||||
|
|
||||||
|
Resolution rules:
|
||||||
|
- Qualified access (`Outer.Inner`) resolves to a class-namespace member at compile time.
|
||||||
|
- Unqualified access inside `Outer` can resolve to nested declarations if not shadowed by locals/params.
|
||||||
|
- Class-namespace members are never resolved via runtime name lookup; failures are compile-time errors.
|
||||||
|
|
||||||
|
Enum lifting:
|
||||||
|
- `enum E* { ... }` lifts entries into the enclosing class namespace (e.g., `Outer.Entry`).
|
||||||
|
- Any ambiguity with existing class members is a compile-time error.
|
||||||
|
|
||||||
## Shadowing Rules
|
## Shadowing Rules
|
||||||
Shadowing policy is configurable:
|
Shadowing policy is configurable:
|
||||||
- Locals may shadow parameters (allowed by default).
|
- Locals may shadow parameters (allowed by default).
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user