implemented object expressions (anonymous classes)
This commit is contained in:
1 parent
8611543623
commit
eec732d11a
7 files changed
+225
-19
No files matched your search
@@ -95,6 +95,11 @@ class Compiler(
|
||||
}
|
||||
}
|
||||
|
||||
private var anonCounter = 0
|
||||
private fun generateAnonName(pos: Pos): String {
|
||||
return "${"$"}${"Anon"}_${pos.line+1}_${pos.column}_${++anonCounter}"
|
||||
}
|
||||
|
||||
private fun pushPendingDocToken(t: Token) {
|
||||
val s = stripCommentLexeme(t.value)
|
||||
if (pendingDocStart == null) pendingDocStart = t.pos
|
||||
@@ -248,7 +253,7 @@ class Compiler(
|
||||
when (t.type) {
|
||||
Token.Type.RBRACE, Token.Type.EOF, Token.Type.SEMICOLON -> {}
|
||||
else ->
|
||||
throw ScriptError(t.pos, "unexpeced `${t.value}` here")
|
||||
throw ScriptError(t.pos, "unexpected `${t.value}` here")
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -1248,6 +1253,8 @@ class Compiler(
|
||||
}
|
||||
}
|
||||
|
||||
Token.Type.OBJECT -> StatementRef(parseObjectDeclaration())
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
@@ -1860,8 +1867,12 @@ class Compiler(
|
||||
}
|
||||
|
||||
private suspend fun parseObjectDeclaration(): Statement {
|
||||
val nameToken = cc.requireToken(Token.Type.ID)
|
||||
val startPos = pendingDeclStart ?: nameToken.pos
|
||||
val next = cc.peekNextNonWhitespace()
|
||||
val nameToken = if (next.type == Token.Type.ID) cc.requireToken(Token.Type.ID) else null
|
||||
|
||||
val startPos = pendingDeclStart ?: nameToken?.pos ?: cc.current().pos
|
||||
val className = nameToken?.value ?: generateAnonName(startPos)
|
||||
|
||||
val doc = pendingDeclDoc ?: consumePendingDoc()
|
||||
pendingDeclDoc = null
|
||||
pendingDeclStart = null
|
||||
@@ -1887,11 +1898,11 @@ class Compiler(
|
||||
|
||||
// Robust body detection
|
||||
var classBodyRange: MiniRange? = null
|
||||
val bodyInit: Statement? = run {
|
||||
val bodyInit: Statement? = inCodeContext(CodeContext.ClassBody(className)) {
|
||||
val saved = cc.savePos()
|
||||
val next = cc.nextNonWhitespace()
|
||||
if (next.type == Token.Type.LBRACE) {
|
||||
val bodyStart = next.pos
|
||||
val nextBody = cc.nextNonWhitespace()
|
||||
if (nextBody.type == Token.Type.LBRACE) {
|
||||
val bodyStart = nextBody.pos
|
||||
val st = withLocalNames(emptySet()) {
|
||||
parseScript()
|
||||
}
|
||||
@@ -1906,15 +1917,15 @@ class Compiler(
|
||||
}
|
||||
|
||||
val initScope = popInitScope()
|
||||
val className = nameToken.value
|
||||
|
||||
return statement(startPos) { context ->
|
||||
val parentClasses = baseSpecs.map { baseSpec ->
|
||||
val rec = context[baseSpec.name] ?: throw ScriptError(nameToken.pos, "unknown base class: ${baseSpec.name}")
|
||||
(rec.value as? ObjClass) ?: throw ScriptError(nameToken.pos, "${baseSpec.name} is not a class")
|
||||
val rec = context[baseSpec.name] ?: throw ScriptError(startPos, "unknown base class: ${baseSpec.name}")
|
||||
(rec.value as? ObjClass) ?: throw ScriptError(startPos, "${baseSpec.name} is not a class")
|
||||
}
|
||||
|
||||
val newClass = ObjInstanceClass(className, *parentClasses.toTypedArray())
|
||||
newClass.isAnonymous = nameToken == null
|
||||
for (i in parentClasses.indices) {
|
||||
val argsList = baseSpecs[i].args
|
||||
// In object, we evaluate parent args once at creation time
|
||||
@@ -1924,12 +1935,14 @@ class Compiler(
|
||||
val classScope = context.createChildScope(newThisObj = newClass)
|
||||
classScope.currentClassCtx = newClass
|
||||
newClass.classScope = classScope
|
||||
classScope.addConst("object", newClass)
|
||||
|
||||
bodyInit?.execute(classScope)
|
||||
|
||||
// Create instance (singleton)
|
||||
val instance = newClass.callOn(context.createChildScope(Arguments.EMPTY))
|
||||
context.addItem(className, false, instance)
|
||||
if (nameToken != null)
|
||||
context.addItem(className, false, instance)
|
||||
instance
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ private object ClassIdGen { var c: Long = 1L; fun nextId(): Long = c++ }
|
||||
|
||||
val ObjClassType by lazy {
|
||||
ObjClass("Class").apply {
|
||||
addProperty("className", getter = { thisAs<ObjClass>().classNameObj })
|
||||
addFnDoc(
|
||||
name = "name",
|
||||
doc = "Simple name of this class (without package).",
|
||||
@@ -91,6 +92,8 @@ open class ObjClass(
|
||||
vararg parents: ObjClass,
|
||||
) : Obj() {
|
||||
|
||||
var isAnonymous: Boolean = false
|
||||
|
||||
var isAbstract: Boolean = false
|
||||
|
||||
// Stable identity and simple structural version for PICs
|
||||
|
||||
@@ -321,16 +321,23 @@ class CastRef(
|
||||
/** Qualified `this@Type`: resolves to a view of current `this` starting dispatch from the ancestor Type. */
|
||||
class QualifiedThisRef(private val typeName: String, private val atPos: Pos) : ObjRef {
|
||||
override suspend fun get(scope: Scope): ObjRecord {
|
||||
val thisObj = scope.thisObj
|
||||
val t = scope[typeName]?.value as? ObjClass
|
||||
?: scope.raiseError("unknown type $typeName")
|
||||
val inst = (thisObj as? ObjInstance)
|
||||
?: scope.raiseClassCastError("this is not an instance")
|
||||
if (!inst.objClass.allParentsSet.contains(t) && inst.objClass !== t)
|
||||
scope.raiseClassCastError(
|
||||
"Qualifier ${'$'}{t.className} is not an ancestor of ${'$'}{inst.objClass.className} (order: ${'$'}{inst.objClass.renderLinearization(true)})"
|
||||
)
|
||||
return ObjQualifiedView(inst, t).asReadonly
|
||||
|
||||
var s: Scope? = scope
|
||||
while (s != null) {
|
||||
val inst = s.thisObj as? ObjInstance
|
||||
if (inst != null) {
|
||||
if (inst.objClass === t || inst.objClass.allParentsSet.contains(t)) {
|
||||
return ObjQualifiedView(inst, t).asReadonly
|
||||
}
|
||||
}
|
||||
s = s.parent
|
||||
}
|
||||
|
||||
scope.raiseClassCastError(
|
||||
"No instance of type ${t.className} found in the scope chain"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@ open class LynonEncoder(val bout: BitOutput, val settings: LynonSettings = Lynon
|
||||
* Caching is used automatically.
|
||||
*/
|
||||
suspend fun encodeAny(scope: Scope, obj: Obj) {
|
||||
if (obj.objClass.isAnonymous) scope.raiseError("Cannot serialize anonymous object")
|
||||
encodeCached(obj) {
|
||||
val type = putTypeRecord(scope, obj, obj.lynonType())
|
||||
obj.serialize(scope, this, type)
|
||||
|
||||
Reference in new issue
Block a user