From 4742bf52a4d0082530a04048c7bdd4a50fd6fbc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BE=AA?= Date: Sun, 19 Jan 2025 01:48:35 +1100 Subject: [PATCH] refact --- build.sbt | 35 +- eval/src/main/scala/chester/eval/Eval.scala | 15 - .../main/scala/chester/runtime/Value.scala | 15 - .../main/scala/chester/resolve/Desalt.scala | 590 ------------------ .../scala/chester/resolve/MacroExpander.scala | 3 - .../main/scala/chester/resolve/operator.scala | 322 ---------- .../src/main/scala/chester/syntax/Const.scala | 22 - .../scala/chester/syntax/TASTPackage.scala | 75 --- .../src/main/scala/chester/tyck/Builtin.scala | 19 - .../src/main/scala/chester/tyck/Context.scala | 98 --- .../src/main/scala/chester/tyck/Get.scala | 101 --- .../main/scala/chester/tyck/TyckResult.scala | 69 -- .../chester/tyck/api/SemanticCollector.scala | 98 --- .../src/main/scala/chester/tyck/common.scala | 7 - .../main/scala/chester/tyck/convertMeta.scala | 9 - .../utils/propagator/CommonPropagator.scala | 147 ----- .../utils/propagator/ProvideCellId.scala | 233 ------- .../utils/propagator/ProvideImmutable.scala | 132 ---- .../utils/propagator/ProvideImpl.scala | 5 - .../utils/propagator/ProvideMultithread.scala | 313 ---------- .../utils/propagator/ProvideMutable.scala | 238 ------- .../src/main/scala/chester/cps/CPSTrans.scala | 4 - .../main/scala/chester/erasure/Eraser.scala | 19 - .../main/scala/chester/tyck/Context2.scala | 67 -- .../main/scala/chester/tyck/Elaborater.scala | 363 ----------- .../scala/chester/tyck/ElaboraterBase.scala | 87 --- .../scala/chester/tyck/ElaboraterBlock.scala | 452 -------------- .../scala/chester/tyck/ElaboraterCommon.scala | 163 ----- .../chester/tyck/ElaboraterFunction.scala | 169 ----- .../chester/tyck/ElaboraterFunctionCall.scala | 258 -------- .../scala/chester/tyck/MethodExtractor.scala | 69 -- .../scala/chester/tyck/TyckPropagator.scala | 383 ------------ 32 files changed, 6 insertions(+), 4574 deletions(-) delete mode 100644 eval/src/main/scala/chester/eval/Eval.scala delete mode 100644 eval/src/main/scala/chester/runtime/Value.scala delete mode 100644 tyck-base/src/main/scala/chester/resolve/Desalt.scala delete mode 100644 tyck-base/src/main/scala/chester/resolve/MacroExpander.scala delete mode 100644 tyck-base/src/main/scala/chester/resolve/operator.scala delete mode 100644 tyck-base/src/main/scala/chester/syntax/Const.scala delete mode 100644 tyck-base/src/main/scala/chester/syntax/TASTPackage.scala delete mode 100644 tyck-base/src/main/scala/chester/tyck/Builtin.scala delete mode 100644 tyck-base/src/main/scala/chester/tyck/Context.scala delete mode 100644 tyck-base/src/main/scala/chester/tyck/Get.scala delete mode 100644 tyck-base/src/main/scala/chester/tyck/TyckResult.scala delete mode 100644 tyck-base/src/main/scala/chester/tyck/api/SemanticCollector.scala delete mode 100644 tyck-base/src/main/scala/chester/tyck/common.scala delete mode 100644 tyck-base/src/main/scala/chester/tyck/convertMeta.scala delete mode 100644 tyck-base/src/main/scala/chester/utils/propagator/CommonPropagator.scala delete mode 100644 tyck-base/src/main/scala/chester/utils/propagator/ProvideCellId.scala delete mode 100644 tyck-base/src/main/scala/chester/utils/propagator/ProvideImmutable.scala delete mode 100644 tyck-base/src/main/scala/chester/utils/propagator/ProvideImpl.scala delete mode 100644 tyck-base/src/main/scala/chester/utils/propagator/ProvideMultithread.scala delete mode 100644 tyck-base/src/main/scala/chester/utils/propagator/ProvideMutable.scala delete mode 100644 tyck/src/main/scala/chester/cps/CPSTrans.scala delete mode 100644 tyck/src/main/scala/chester/erasure/Eraser.scala delete mode 100644 tyck/src/main/scala/chester/tyck/Context2.scala delete mode 100644 tyck/src/main/scala/chester/tyck/Elaborater.scala delete mode 100644 tyck/src/main/scala/chester/tyck/ElaboraterBase.scala delete mode 100644 tyck/src/main/scala/chester/tyck/ElaboraterBlock.scala delete mode 100644 tyck/src/main/scala/chester/tyck/ElaboraterCommon.scala delete mode 100644 tyck/src/main/scala/chester/tyck/ElaboraterFunction.scala delete mode 100644 tyck/src/main/scala/chester/tyck/ElaboraterFunctionCall.scala delete mode 100644 tyck/src/main/scala/chester/tyck/MethodExtractor.scala delete mode 100644 tyck/src/main/scala/chester/tyck/TyckPropagator.scala diff --git a/build.sbt b/build.sbt index 7a29b7dc8..4a591213f 100644 --- a/build.sbt +++ b/build.sbt @@ -554,22 +554,11 @@ lazy val err = crossProject(JSPlatform, JVMPlatform, NativePlatform) ) .jvmSettings(commonJvmLibSettings) -lazy val tyckBase = crossProject(JSPlatform, JVMPlatform, NativePlatform) +lazy val semantic = crossProject(JSPlatform, JVMPlatform, NativePlatform) .withoutSuffixFor(JVMPlatform) - .crossType(CrossType.Pure) - .in(file("tyck-base")) + .crossType(CrossType.Full) + .in(file("semantic")) .dependsOn(utils, syntax, err) - .settings( - name := "tyck-base", - commonSettings - ) - .jvmSettings(commonJvmLibSettings) - -lazy val tyck = crossProject(JSPlatform, JVMPlatform, NativePlatform) - .withoutSuffixFor(JVMPlatform) - .crossType(CrossType.Pure) - .in(file("tyck")) - .dependsOn(utils, syntax, err, tyckBase, eval) .settings( commonSettings ) @@ -737,7 +726,7 @@ lazy val core = crossProject(JSPlatform, JVMPlatform, NativePlatform) .withoutSuffixFor(JVMPlatform) .crossType(CrossType.Pure) .in(file("core")) - .dependsOn(utils, reader, syntax, pretty, tyck) + .dependsOn(utils, reader, syntax, pretty, semantic) .settings( name := "core", assembly / assemblyOutputPath := file("target") / "chester-core.jar", @@ -1251,21 +1240,11 @@ lazy val buildTool = crossProject(JVMPlatform) ) ) -lazy val eval = crossProject(JSPlatform, JVMPlatform, NativePlatform) - .withoutSuffixFor(JVMPlatform) - .crossType(CrossType.Pure) - .in(file("eval")) - .dependsOn(tyckBase) - .settings( - commonSettings - ) - .jvmSettings(commonJvmLibSettings) - lazy val interpreter = crossProject(JSPlatform, JVMPlatform, NativePlatform) .withoutSuffixFor(JVMPlatform) .crossType(CrossType.Full) .in(file("interpreter")) - .dependsOn(eval, reader, tyck) + .dependsOn(reader, semantic) .settings(commonSettings) // https://github.com/b-studios/scala-graal-truffle-example/blob/c2747a6eece156f878c5b934116aaa00a2cd6311/build.sbt .settings( @@ -1351,8 +1330,7 @@ lazy val root = crossProject(JSPlatform, JVMPlatform, NativePlatform) syntax, err, pretty, - tyck, - tyckBase, + semantic, platform, jsForJvm, core, @@ -1361,7 +1339,6 @@ lazy val root = crossProject(JSPlatform, JVMPlatform, NativePlatform) lsp, buildProtocol, buildTool, - eval, interpreter, js, site, diff --git a/eval/src/main/scala/chester/eval/Eval.scala b/eval/src/main/scala/chester/eval/Eval.scala deleted file mode 100644 index 51b84faf3..000000000 --- a/eval/src/main/scala/chester/eval/Eval.scala +++ /dev/null @@ -1,15 +0,0 @@ -package chester.eval - -import chester.runtime.Value -import chester.syntax.core.* -import chester.syntax.core.spec.spec.{BooleanTermC, TermT} - -case class EvalContext() - -case class Eval[Term <: TermT[Term]]() { - type BooleanTerm = BooleanTermC[Term] - def evalNoEffect(code: Term, ctx: EvalContext = EvalContext()): Value = code match { - case b: BooleanTerm => Value(b.value) - case _ => ??? - } -} diff --git a/eval/src/main/scala/chester/runtime/Value.scala b/eval/src/main/scala/chester/runtime/Value.scala deleted file mode 100644 index 0a9a45137..000000000 --- a/eval/src/main/scala/chester/runtime/Value.scala +++ /dev/null @@ -1,15 +0,0 @@ -package chester.runtime - -import spire.math.Rational - -import scala.collection.immutable.HashMap -import scala.language.implicitConversions - -opaque type Value = Vector[Any] | Boolean | Int | BigInt | String | Symbol | HashMap[Any, Any] | Rational | Double | Function[Any, Any] -type OpenValue = Vector[Value] | Boolean | Int | BigInt | String | Symbol | HashMap[Value, Value] | Rational | Double | Function[Vector[Value], Value] - -inline implicit def viewValue(x: Value): OpenValue = x.asInstanceOf[OpenValue] - -inline implicit def packValue(x: OpenValue): Value = x.asInstanceOf[Value] - -inline def Value(x: OpenValue | Value): Value = x.asInstanceOf[Value] diff --git a/tyck-base/src/main/scala/chester/resolve/Desalt.scala b/tyck-base/src/main/scala/chester/resolve/Desalt.scala deleted file mode 100644 index b841a4c88..000000000 --- a/tyck-base/src/main/scala/chester/resolve/Desalt.scala +++ /dev/null @@ -1,590 +0,0 @@ -package chester.resolve - -import cats.data.* -import cats.implicits.* -import chester.error.* -import chester.syntax.{Const, ModuleRef} -import chester.syntax.concrete.* -import chester.tyck.* -import _root_.io.github.iltotore.iron.constraint.numeric.* -import chester.utils.* - -import scala.annotation.tailrec - -case class DesugarInfo() - -private object DesaltCaseClauseMatch { - def unapply( - x: Expr - )(using reporter: Reporter[TyckProblem]): Option[DesaltCaseClause] = x match { - case OpSeq( - Vector( - Identifier(Const.Case, _), - pattern, - Identifier(Const.Arrow2, _), - returning - ), - meta - ) => - Some(DesaltCaseClause(pattern, returning, meta)) - case OpSeq(Vector(Identifier(Const.Case, _), _*), _) => - val error = ExpectCase(x) - reporter(error) - None - case _ => None - } -} - -private object MatchDeclarationTelescope { - private def handleTerms(terms: Vector[Expr], x: Expr, implicitly: Boolean)(using reporter: Reporter[TyckProblem]): Option[DefTelescope] = { - // Parameters enclosed in parentheses - val argsResult = terms.map { - case id: Identifier => - Some(Arg(name = id, meta = id.meta)) - case OpSeq(Vector(id: Identifier, Identifier(Const.`:`, _), ty), _) => - Some(Arg(name = id, ty = Some(ty), meta = id.meta)) - case _ => - reporter(ExpectParameterList(x)) - None - } - - if (argsResult.contains(None)) { - None - } else { - Some(DefTelescope(argsResult.flatten.toVector, implicitly = implicitly, meta = x.meta)) - } - } - def unapply( - x: Expr - )(using reporter: Reporter[TyckProblem]): Option[DefTelescope] = x match { - case id: Identifier => - // Single parameter without type - Some(DefTelescope(Vector(Arg(name = id, meta = id.meta)), meta = x.meta)) - case opseq @ OpSeq(terms, meta) if terms.nonEmpty => unapply(Tuple(Vector(opseq), meta)) - case t @ Tuple(terms, _) => handleTerms(terms, t, false) - case t @ ListExpr(terms, _) => handleTerms(terms, t, true) - case _ => - reporter(ExpectParameterList(x)) - None - } -} - -def opSeq(xs: Seq[Expr])(using reporter: Reporter[TyckProblem]): Expr = - SimpleDesalt.desugar(OpSeq(xs.toVector, xs.head.meta)) - -private object DesaltSimpleFunction { - def predicate(x: Expr): Boolean = x match { - case Identifier(Const.Arrow2, _) => true - case _ => false - } - - def unapply(x: Expr)(using reporter: Reporter[TyckProblem]): Option[Expr] = - x match { - case OpSeq(xs, meta) if xs.exists(predicate) => - val index = xs.indexWhere(predicate) - assert(index >= 0) - val before = xs.take(index) - val after = xs.drop(index + 1) - - val paramsExpr = before match { - case Vector(Tuple(terms, meta)) => Vector(Tuple(terms, meta)) - case _ => before - } - - paramsExpr.traverse(MatchDeclarationTelescope.unapply) match { - case Some(telescopes) => - val body = opSeq(after) - Some( - FunctionExpr( - telescope = telescopes, - body = body, - meta = meta - ) - ) - case None => - val error = ExpectLambda(x) - reporter(error) - Some(DesaltFailed(x, error, meta)) - } - case _ => None - } -} - -private object ObjectDesalt { - def desugarQualifiedName(qname: QualifiedName): Vector[String] = qname match { - case Identifier(name, _) => Vector(name) - case DotCall(expr: QualifiedName, field: Identifier, _, _) => - desugarQualifiedName(expr) :+ field.name - case _ => - throw new IllegalArgumentException("Invalid QualifiedName structure") - } - - def insertNested( - fields: Vector[(Vector[String], Expr)], - base: ObjectExpr - ): ObjectExpr = { - fields.foldLeft(base) { - case (acc, (Vector(k), v)) => - acc.copy(clauses = acc.clauses :+ ObjectExprClause(Identifier(k, v.meta), v)) - case (acc, (k +: ks, v)) => - val nestedObj = acc.clauses - .collectFirst { - case ObjectExprClause(id: Identifier, obj: ObjectExpr) if id.name == k => - obj - } - .getOrElse(ObjectExpr(Vector.empty, v.meta)) - val updatedNested = insertNested(Vector((ks, v)), nestedObj) - val updatedClauses = acc.clauses.filterNot { - case ObjectExprClause(id: Identifier, _) => id.name == k - case _ => false - } :+ ObjectExprClause(Identifier(k, v.meta), updatedNested) - acc.copy(clauses = updatedClauses) - case (acc, _) => acc - } - } - - def desugarObjectExpr(expr: ObjectExpr): ObjectExpr = { - val (desugaredFields, otherClauses) = expr.clauses.foldLeft( - (Vector.empty[(Vector[String], Expr)], Vector.empty[ObjectClause]) - ) { - case ((fields, others), ObjectExprClause(qname, value)) => - (fields :+ (desugarQualifiedName(qname), value), others) - case ((fields, others), clause) => - (fields, others :+ clause) - } - - val nestedObject = insertNested(desugaredFields, ObjectExpr(Vector.empty, meta = expr.meta)) - - val updatedClauses = (nestedObject.clauses ++ otherClauses).map { - case ObjectExprClause(key: Identifier, value) => - ObjectExprClauseOnValue(SymbolLiteral(key.name, key.meta), value) - case other: ObjectExprClauseOnValue => other - case _ => unreachable() - } - - expr.copy(clauses = updatedClauses) - } -} - -case object PatternDesalt { - def desugar( - x: Expr - )(using reporter: Reporter[TyckProblem]): Option[DesaltPattern] = x match { - case id @ Identifier(_, meta) => Some(PatternBind(id, meta)) - case _ => None // TODO: more - } -} - -case object StmtDesalt { - def desugar(x: Expr)(using reporter: Reporter[TyckProblem]): Expr = x match { - case StmtDesalt(x) => x - case _ => x - } - - def defined( - xs: Vector[Expr] - )(using reporter: Reporter[TyckProblem]): Option[Defined] = { - if (xs.isEmpty) None - else if (xs.length == 1) xs.head match { - // TODO: support multiple telescopes - case FunctionCall(f: Identifier, MatchDeclarationTelescope(t), _) => - Some(DefinedFunction(f, Vector(t).assumeNonEmpty)) - case a => PatternDesalt.desugar(a).map(DefinedPattern.apply) - } - else - xs.head match { - case identifier: Identifier => - xs.tail.traverse(MatchDeclarationTelescope.unapply).map { telescopes => - DefinedFunction( - identifier, - NonEmptyVector.fromVectorUnsafe(telescopes) - ) - } - case _ => None - } - } - - def letdef( - decorations: Vector[Expr], - kw: Identifier, - xs: Vector[Expr], - cause: Expr - )(using reporter: Reporter[TyckProblem]): Stmt = { - val typeIdx = xs.indexWhere { - case Identifier(Const.`:`, _) => true; case _ => false - } - val valueIdx = xs.indexWhere { - case Identifier(Const.`=`, _) => true; case _ => false - } - val kind = kw.name match { - case Const.Let => LetDefType.Let - case Const.Def => LetDefType.Def - case name => unreachable(s"Unknown keyword ${name}") - } - - val (onExprs, typeExprs, valueExprs) = (typeIdx, valueIdx) match { - case (-1, -1) => (xs, Vector.empty[Expr], Vector.empty[Expr]) - case (tIdx, -1) => (xs.take(tIdx), xs.drop(tIdx + 1), Vector.empty[Expr]) - case (-1, vIdx) => (xs.take(vIdx), Vector.empty[Expr], xs.drop(vIdx + 1)) - case (tIdx, vIdx) if tIdx < vIdx => - (xs.take(tIdx), xs.slice(tIdx + 1, vIdx), xs.drop(vIdx + 1)) - case _ => - val error = ExpectLetDef(cause) - reporter(error) - return DesaltFailed(cause, error, cause.meta) - } - - val on = defined(onExprs).getOrElse { - val error = ExpectLetDef(cause) - reporter(error) - return DesaltFailed(cause, error, cause.meta) - } - - val ty = if (typeExprs.nonEmpty) Some(opSeq(typeExprs)) else None - val body = if (valueExprs.nonEmpty) Some(opSeq(valueExprs)) else None - unrollFunction( - LetDefStmt( - kind, - on, - ty = ty, - body = body, - decorations = decorations, - meta = cause.meta - ) - ) - } - - def unrollFunction(stmt: LetDefStmt): LetDefStmt = { - stmt.defined match { - case DefinedFunction(id, telescopes) => - require(stmt.decorations.isEmpty, "not supported yet") - require(stmt.body.nonEmpty, "not supported yet") - val expr = FunctionExpr( - telescope = telescopes.toVector, - resultTy = stmt.ty, - effect = stmt.effect, - body = stmt.body.get, - meta = stmt.meta - ) - stmt.copy( - defined = DefinedPattern(PatternBind(id, id.meta)), - body = Some(expr), - ty = None, - effect = None - ) - case _ => stmt - } - } - - def unapply(x: Expr)(using reporter: Reporter[TyckProblem]): Option[Stmt] = - x match { - case opseq @ OpSeq(seq, _) => - seq.indexWhere { - case Identifier(id, _) if Const.kw1.contains(id) => true - case _ => false - } match { - case -1 => None - case kwIdx => - val kwId = seq(kwIdx).asInstanceOf[Identifier] - val beforeKw = seq.take(kwIdx) - val afterKw = seq.drop(kwIdx + 1) - if (!beforeKw.forall(_.isInstanceOf[Identifier])) None - else - kwId.name match { - case Const.Let | Const.Def => - Some(letdef(beforeKw, kwId, afterKw, opseq)) - case other => unreachable(s"Unknown keyword ${other}") - } - } - case _ => None - } -} - -case object SimpleDesalt { - def desugar(expr: Expr)(using reporter: Reporter[TyckProblem]): Expr = - expr match { - case OpSeq(xs, _) if xs.length == 1 => xs.head - case _ @DesaltCaseClauseMatch(x) => x - case block @ Block(heads, tail, _) - if heads.exists(_.isInstanceOf[DesaltCaseClause]) || - tail.exists(_.isInstanceOf[DesaltCaseClause]) => - val clauses = (heads ++ tail.toVector).collect { case clause: DesaltCaseClause => - clause - } - if (clauses.length != heads.length + tail.size) { - val error = ExpectFullCaseBlock(block) - reporter(error) - DesaltFailed(block, error, block.meta) - } else { - DesaltMatching(clauses, block.meta) - } - case _ @Block(heads, tail, meta) => - Block(heads.map(StmtDesalt.desugar), tail.map(StmtDesalt.desugar), meta) - case DesaltSimpleFunction(func) => func - case obj: ObjectExpr => ObjectDesalt.desugarObjectExpr(obj) - case FunctionCall(function, telescopes, meta) => - val desugaredFunction = desugar(function) - val desugaredTelescopes = telescopes match { - case t: Tuple => - Vector( - DesaltCallingTelescope( - t.terms.map(term => CallingArg(expr = desugar(term), meta = term.meta)), - meta = t.meta - ) - ) - case other => - reporter(UnexpectedTelescope(other)) - Vector( - DesaltCallingTelescope( - Vector(CallingArg(expr = desugar(other), meta = other.meta)), - meta = other.meta - ) - ) - } - desugaredFunction match { - case DesaltFunctionCall(f, t, m) => - DesaltFunctionCall(f, t ++ desugaredTelescopes, m) - case _ => - DesaltFunctionCall( - desugaredFunction, - NonEmptyVector.fromVectorUnsafe(desugaredTelescopes), - meta - ) - } - case OpSeq(Vector(lhs, Identifier(Const.`:`, _), rhs), meta) => - val desugaredLhs = desugar(lhs) - val desugaredRhs = desugar(rhs) - TypeAnotationNoEffects(desugaredLhs, desugaredRhs, meta) - case opSeq @ OpSeq(Vector(Identifier(Const.Import, _), some), meta) => - Some(some) match { - case Some(qualifiedName) => - val modulePath = ModuleRef( - ObjectDesalt.desugarQualifiedName( - qualifiedName.asInstanceOf[QualifiedName] - ) - ) - ImportStmt(modulePath, meta) - } - case opseq @ OpSeq(Vector(Identifier(Const.Module, _), some), meta) => - Some(some) match { - case Some(qualifiedName) => - val modulePath = ModuleRef( - ObjectDesalt.desugarQualifiedName( - qualifiedName.asInstanceOf[QualifiedName] - ) - ) - ModuleStmt(modulePath, meta) - } - case expr @ OpSeq(Vector(Identifier(Const.Record, _), nameExpr, rest @ _*), meta) => - // Parse the record name and parameters if any - val (name, parameterExprs) = nameExpr match { - case id: Identifier => - (id, Vector.empty[Expr]) - case FunctionCall(id: Identifier, telescope, _) => - (id, Vector(telescope)) - case _ => - val error = ExpectRecordName(nameExpr) - reporter(error) - return DesaltFailed(expr, error, meta) - } - - // Process the rest of the tokens - var tokens = rest.toList - - // Use the common method to parse the optional ExtendsClause - val (extendsClause, remainingTokens) = parseExtendsClause(tokens, meta) - tokens = remainingTokens - - // Parse fields and body - val (fieldExprs0, bodyExprs) = tokens.span { - case Tuple(_, _) => true - case _ => false - } - val fieldExprs = parameterExprs ++ fieldExprs0 - - // Desugar fields into Field instances - val desugaredFields = fieldExprs - .flatMap { - case Tuple(terms, _) => - terms.map { - case OpSeq(Vector(id: Identifier, Identifier(Const.`:`, _), ty), _) => - Some(RecordField(name = id, ty = Some(ty), meta = id.meta)) - case id: Identifier => - Some(RecordField(name = id, meta = id.meta)) - case other => - reporter(ExpectFieldDeclaration(other)) - None - } - case other => - reporter(ExpectFieldDeclaration(other)) - None - } - .flatten - .toVector - - // Desugar body if present - val desugaredBody = if (bodyExprs.nonEmpty) { - val bodyExpr = opSeq(bodyExprs) - Some(desugar(bodyExpr) match { - case b: Block => b - case other => Block(Vector(other), None, meta = other.meta) - }) - } else None - - RecordStmt( - name = name, - extendsClause = extendsClause, - fields = desugaredFields, - body = desugaredBody, - meta = meta - ) - - // Handling 'trait' keyword - case expr @ OpSeq(Vector(Identifier(Const.Trait, _), nameExpr, rest @ _*), meta) => - // Parse the trait name and parameters if any - val (name, parameterExprs) = nameExpr match { - case id: Identifier => - (id, Vector.empty[Expr]) - case FunctionCall(id: Identifier, telescope, _) => - (id, Vector(telescope)) - case _ => - val error = ExpectTraitName(nameExpr) - reporter(error) - return DesaltFailed(expr, error, meta) - } - - // Process the rest of the tokens - var tokens = rest.toList - - // Use the common method to parse the optional ExtendsClause - val (extendsClause, remainingTokens) = parseExtendsClause(tokens, meta) - tokens = remainingTokens - - // Parse body if present - val bodyExpr = if (tokens.nonEmpty) { - val body = opSeq(tokens) - Some(desugar(body) match { - case b: Block => b - case other => Block(Vector(other), None, meta = other.meta) - }) - } else None - - TraitStmt( - name = name, - extendsClause = extendsClause, - body = bodyExpr, - meta = meta - ) - case expr @ OpSeq(Vector(Identifier(Const.Object, _), nameExpr, rest @ _*), meta) => { - // Parse the object name - val name = nameExpr match { - case id: Identifier => id - case _ => - val error = ExpectObjectName(nameExpr) - reporter(error) - return DesaltFailed(expr, error, meta) - } - - // Process the rest of the tokens - val tokens = rest.toList - - // Parse the optional ExtendsClause - val (extendsClause, remainingTokens) = parseExtendsClause(tokens, meta) - - // Parse body if present - val bodyExpr = if (remainingTokens.nonEmpty) { - val body = opSeq(remainingTokens) - Some(desugar(body) match { - case b: Block => b - case other => Block(Vector(other), None, meta = other.meta) - }) - } else None - - ObjectStmt( - name = name, - extendsClause = extendsClause, - body = bodyExpr, - meta = meta - ) - } - // Handling 'interface' keyword - case expr @ OpSeq(Vector(Identifier(Const.Interface, _), nameExpr, rest @ _*), meta) => - // Parse the interface name and parameters if any - val (name, parameterExprs) = nameExpr match { - case id: Identifier => - (id, Vector.empty[Expr]) - case FunctionCall(id: Identifier, telescope, _) => - (id, Vector(telescope)) - case _ => - val error = ExpectInterfaceName(nameExpr) - reporter(error) - return DesaltFailed(expr, error, meta) - } - - // Process the rest of the tokens - var tokens = rest.toList - - // Use the common method to parse the optional ExtendsClause - val (extendsClause, remainingTokens) = parseExtendsClause(tokens, meta) - tokens = remainingTokens - - // Parse body if present - val bodyExpr = if (tokens.nonEmpty) { - val body = opSeq(tokens) - Some(desugar(body) match { - case b: Block => b - case other => Block(Vector(other), None, meta = other.meta) - }) - } else None - - InterfaceStmt( - name = name, - extendsClause = extendsClause, - body = bodyExpr, - meta = meta - ) - - case default => default - } - - @tailrec - private def unwrap(e: Expr)(using reporter: Reporter[TyckProblem]): Expr = - e match { - case Block(Vector(), Some(tail), _) => unwrap(desugar(tail)) - case Tuple(Vector(term), _) => unwrap(desugar(term)) - case _ => e - } - - def desugarUnwrap(expr: Expr)(using reporter: Reporter[TyckProblem]): Expr = - unwrap(desugar(expr)) - - // Helper method to parse super types separated by 'with' - def parseSuperTypes(tokens: List[Expr]): (List[Expr], List[Expr]) = { - @tailrec - def loop(accum: List[Expr], remaining: List[Expr]): (List[Expr], List[Expr]) = { - remaining match { - case Identifier(Const.`with`, _) :: next :: tail => - loop(next :: accum, tail) - case _ => - (accum.reverse, remaining) - } - } - tokens match { - case firstType :: tail => loop(List(firstType), tail) - case Nil => (Nil, Nil) - } - } - - private def parseExtendsClause(tokens: List[Expr], meta: Option[ExprMeta]): (Option[ExtendsClause], List[Expr]) = { - tokens match { - case Identifier(Const.`<:`, _) :: rest => - val (superTypes, remainingTokens) = parseSuperTypes(rest) - (Some(ExtendsClause(superTypes.toVector, meta)), remainingTokens) - case _ => - (None, tokens) - } - } -} -case object OpSeqDesalt { - def desugar(expr: Expr): Expr = ??? -} diff --git a/tyck-base/src/main/scala/chester/resolve/MacroExpander.scala b/tyck-base/src/main/scala/chester/resolve/MacroExpander.scala deleted file mode 100644 index a40521543..000000000 --- a/tyck-base/src/main/scala/chester/resolve/MacroExpander.scala +++ /dev/null @@ -1,3 +0,0 @@ -package chester.resolve - -case class MacroExpander() {} diff --git a/tyck-base/src/main/scala/chester/resolve/operator.scala b/tyck-base/src/main/scala/chester/resolve/operator.scala deleted file mode 100644 index 5588468f8..000000000 --- a/tyck-base/src/main/scala/chester/resolve/operator.scala +++ /dev/null @@ -1,322 +0,0 @@ -package chester.resolve - -import chester.error.* -import chester.syntax.* -import chester.syntax.accociativity.* -import chester.syntax.concrete.* -import chester.tyck.Reporter -import scalax.collection.edges.DiEdge -import scalax.collection.immutable.Graph - -import scala.collection.mutable - -// Constructs the precedence graph from the given context -def constructPrecedenceGraph( - ctx: PrecedenceGroupCtx -): Graph[PrecedenceGroup, DiEdge[PrecedenceGroup]] = { - val groups: Seq[PrecedenceGroup] = ctx.groups - - // Create directed edges based on 'higherThan' and 'lowerThan' relationships - val edges: Seq[DiEdge[PrecedenceGroup]] = groups.flatMap { group => - val higherEdges = group.higherThan.map { higherGroup => - DiEdge(group, higherGroup) - } - - val lowerEdges = group.lowerThan.map { lowerGroup => - DiEdge(lowerGroup, group) - } - - higherEdges ++ lowerEdges - } - - // Construct the graph from the nodes and edges - val graph = Graph.from(nodes = groups, edges = edges) - - // Check for cycles in the graph - if (graph.isCyclic) { - throw new IllegalArgumentException("The precedence graph contains cycles") - } - - graph -} - -// Determines the precedence of an operator based on its group -def precedenceOf( - opInfo: OpInfo, - groupPrecedence: Map[QualifiedIDString, Int], - reporter: Reporter[TyckError] -): Int = { - opInfo match { - case op: OpWithGroup => - val groupName = op.group.name - groupPrecedence.getOrElse( - groupName, { - // Report unknown precedence group - reporter.apply(UnknownPrecedenceGroup(op.group)) - Int.MaxValue - } - ) - case _ => - // Assign default precedence for operators without a group - groupPrecedence.getOrElse(DefaultPrecedenceGroup.name, Int.MaxValue) - } -} - -// Determines the associativity of an operator -def associativityOf(opInfo: OpInfo): Associativity = { - opInfo match { - case op: OpWithGroup => op.group.associativity - case _ => Associativity.None - } -} - -// Determines the operator type (Infix, Prefix, Postfix, Operand) based on context -def determineOpType( - tokenInfo: TokenInfo, - prevToken: Option[TokenInfo], - nextToken: Option[TokenInfo] -): OpType = { - val isPrevOperand = - prevToken.exists(_.possibleOpTypes.contains(OpType.Operand)) - val isNextOperand = - nextToken.exists(_.possibleOpTypes.contains(OpType.Operand)) - - val possibleTypes = tokenInfo.possibleOpTypes - - if (possibleTypes.contains(OpType.Infix) && isPrevOperand && isNextOperand) { - OpType.Infix - } else if (possibleTypes.contains(OpType.Prefix) && !isPrevOperand && isNextOperand) { - OpType.Prefix - } else if (possibleTypes.contains(OpType.Postfix) && isPrevOperand && !isNextOperand) { - OpType.Postfix - } else if (possibleTypes.contains(OpType.Operand)) { - OpType.Operand - } else { - // Default to Operand if unable to determine - OpType.Operand - } -} - -// Parses tokens from expressions, associating them with operator information -def parseTokens( - seq: Vector[Expr], - opContext: OperatorsContext, - groupPrecedence: Map[QualifiedIDString, Int], - reporter: Reporter[TyckError] -): Vector[TokenInfo] = { - seq.map { - case id @ Identifier(name, _) => - val possibleOps = Seq( - opContext.resolveInfix(name).map(op => (op, OpType.Infix)), - opContext.resolvePrefix(name).map(op => (op, OpType.Prefix)), - opContext.resolvePostfix(name).map(op => (op, OpType.Postfix)) - ).flatten - - if (possibleOps.nonEmpty) { - val possibleOpTypes = possibleOps.map(_._2).toSet - val precedences = possibleOps.map { case (opInfo, _) => - precedenceOf(opInfo, groupPrecedence, reporter) - } - val associativities = possibleOps.map { case (opInfo, _) => - associativityOf(opInfo) - } - // For simplicity, take the lowest precedence and the first associativity - val precedence = precedences.min - val associativity = associativities.head - - TokenInfo(id, precedence, associativity, possibleOpTypes, possibleOps) - } else { - // If no operator is found, treat it as an operand - reporter.apply(UnknownOperator(id)) - TokenInfo(id, Int.MaxValue, Associativity.None, Set(OpType.Operand)) - } - case expr => - // Non-identifier expressions are treated as operands - TokenInfo(expr, Int.MaxValue, Associativity.None, Set(OpType.Operand)) - } -} - -// Builds the expression tree from the output stack using operator information -def buildExpr( - stack: mutable.Stack[TokenInfo], - opContext: OperatorsContext, - reporter: Reporter[TyckError] -): Expr = { - if (stack.isEmpty) { - reporter.apply(UnexpectedTokens(Nil)) - Identifier("error", meta = None) - } else { - val tokenInfo = stack.pop() - val expr = tokenInfo.expr - val opType = tokenInfo.possibleOpTypes.headOption.getOrElse(OpType.Operand) - - opType match { - case OpType.Infix => - // For infix operators, pop two operands - val right = buildExpr(stack, opContext, reporter) - val left = buildExpr(stack, opContext, reporter) - val associativity = tokenInfo.associativity - InfixExpr(left, expr.asInstanceOf[Identifier], right, associativity, expr.meta) - case OpType.Prefix => - // For prefix operators, pop one operand - val operand = buildExpr(stack, opContext, reporter) - PrefixExpr(expr.asInstanceOf[Identifier], operand, expr.meta) - case OpType.Postfix => - // For postfix operators, pop one operand - val operand = buildExpr(stack, opContext, reporter) - PostfixExpr(operand, expr.asInstanceOf[Identifier], expr.meta) - case OpType.Operand => - // Operands are returned as is - expr - } - } -} - -// Determines whether to pop operators from the stack based on precedence and associativity -def shouldPopOperator( - topOperator: TokenInfo, - currentPrec: Int, - currentAssoc: Associativity -): Boolean = { - val topPrec = topOperator.precedence - if (currentAssoc == Associativity.Left) { - topPrec <= currentPrec - } else { - topPrec < currentPrec - } -} - -// Parses the expression tokens into an expression tree using the Shunting Yard algorithm -def parseExpression( - tokens: Vector[TokenInfo], - opContext: OperatorsContext, - reporter: Reporter[TyckError] -): Expr = { - val output = mutable.Stack[TokenInfo]() - val operators = mutable.Stack[TokenInfo]() - - tokens.zipWithIndex.foreach { case (tokenInfo, index) => - val prevToken = if (index > 0) Some(tokens(index - 1)) else None - val nextToken = - if (index < tokens.length - 1) Some(tokens(index + 1)) else None - - // Determine the operator type based on context - val opType = determineOpType(tokenInfo, prevToken, nextToken) - val tokenInfoWithType = tokenInfo.copy(possibleOpTypes = Set(opType)) - - opType match { - case OpType.Operand => - // Operands are pushed onto the output stack - output.push(tokenInfoWithType) - case OpType.Prefix | OpType.Postfix | OpType.Infix => - // Operators are pushed onto the operator stack after popping higher precedence operators - while ( - operators.nonEmpty && shouldPopOperator( - operators.top, - tokenInfo.precedence, - tokenInfo.associativity - ) - ) { - output.push(operators.pop()) - } - operators.push(tokenInfoWithType) - case _ => - reporter.apply(UnknownOperator(tokenInfo.expr)) - } - } - - // Pop any remaining operators onto the output stack - while (operators.nonEmpty) { - output.push(operators.pop()) - } - - // Build the expression tree from the output stack - val expr = buildExpr(output, opContext, reporter) - if (output.nonEmpty) { - reporter.apply(UnexpectedTokens(output.map(_.expr).toList)) - } - expr -} - -// Definition of operator types -sealed trait OpType - -object OpType { - case object Infix extends OpType - - case object Prefix extends OpType - - case object Postfix extends OpType - - case object Operand extends OpType -} - -// Holds information about each token in the expression -case class TokenInfo( - expr: Expr, - precedence: Int, - associativity: Associativity, - possibleOpTypes: Set[OpType], - opInfos: Seq[(OpInfo, OpType)] = Seq.empty -) - -// Resolves an operation sequence into an expression -def resolveOpSeq( - reporter: Reporter[TyckError], - opContext: OperatorsContext, - opSeq: OpSeq -): Expr = { - - // Construct the precedence graph - val precedenceGraph = constructPrecedenceGraph(opContext.groups) - - // Perform a topological sort of the precedence graph - val topOrder = precedenceGraph.topologicalSort - - val groupPrecedence: Map[QualifiedIDString, Int] = topOrder match { - case Right(order) => - // Convert to LayeredTopologicalOrder to access layers - val layeredOrder = order.toLayered - // Map each group to its precedence level - layeredOrder.iterator.flatMap { case (index, nodes) => - nodes.map { node => - node.outer.name -> index - } - }.toMap - case Left(_) => - // If there's a cycle, report an error - reporter.apply( - PrecedenceCycleDetected(precedenceGraph.nodes.map(_.outer)) - ) - Map.empty - } - - // Parse tokens from the operation sequence - val tokens = parseTokens(opSeq.seq, opContext, groupPrecedence, reporter) - - // Collect operator groups from tokens - val operatorGroups = tokens.flatMap { tokenInfo => - tokenInfo.opInfos.collect { case (op: OpWithGroup, _) => - op.group - } - } - - // Verify that all operator groups are connected in the precedence graph - for { - group1 <- operatorGroups - group2 <- operatorGroups - if group1 != group2 - node1Option = precedenceGraph.find(group1) - node2Option = precedenceGraph.find(group2) - if node1Option.isDefined && node2Option.isDefined - node1 = node1Option.get - node2 = node2Option.get - // Check if no path exists between group1 and group2 - if node1.pathTo(node2).isEmpty && node2.pathTo(node1).isEmpty - } { - reporter.apply(UnconnectedPrecedenceGroups(group1, group2)) - } - - // Parse the expression from tokens - parseExpression(tokens, opContext, reporter) -} diff --git a/tyck-base/src/main/scala/chester/syntax/Const.scala b/tyck-base/src/main/scala/chester/syntax/Const.scala deleted file mode 100644 index 27e132386..000000000 --- a/tyck-base/src/main/scala/chester/syntax/Const.scala +++ /dev/null @@ -1,22 +0,0 @@ -package chester.syntax - -object Const { - val Record = "record" - val Case: String = "case" - val Data: String = "data" - val Trait: String = "trait" - val Implement: String = "implement" - val Import: String = "import" - val Module: String = "module" - val Arrow2: String = "=>" - val Arrow: String = "->" - val Let: String = "let" - val Def: String = "def" - val `:`: String = ":" - val `=`: String = "=" - val kw1: Set[String] = Set(Let, Def) - val `<:`: String = "<:" - val Interface = "interface" - val `with` = "with" - val Object = "object" -} diff --git a/tyck-base/src/main/scala/chester/syntax/TASTPackage.scala b/tyck-base/src/main/scala/chester/syntax/TASTPackage.scala deleted file mode 100644 index 29463cb97..000000000 --- a/tyck-base/src/main/scala/chester/syntax/TASTPackage.scala +++ /dev/null @@ -1,75 +0,0 @@ -package chester.syntax - -import chester.syntax.core.{BlockTerm, Effects, Term} -import chester.tyck.SeverityMap -import chester.uniqid.* -import chester.utils.* -import upickle.default.* -import upickle.default as upickle - -import scala.collection.immutable.HashMap - -object TASTPackage { - onNativeImageBuildTime { - // it will be lazy val in the JVM bytecode, if we are building a native image, it will be calculated at build time. - val _ = readwriter[TAST] - } - - // Typed Abstract Syntax Trees - // files - // TODO: handle SourcePos for performance and file size, especially avoid duplicated SourceOffset - case class TAST( - fileName: String, - module: ModuleRef, - ast: BlockTerm, - ty: Term, - effects: Effects, - problems: SeverityMap - ) extends ContainsUniqid derives ReadWriter { - override def collectU(collector: UCollector): Unit = { - ast.collectU(collector) - ty.collectU(collector) - effects.collectU(collector) - } - - override def replaceU(reranger: UReplacer): TAST = { - copy( - ast = ast.replaceU(reranger).asInstanceOf[BlockTerm], - ty = ty.replaceU(reranger), - effects = effects.replaceU(reranger).asInstanceOf[Effects] - ) - } - - def writeBinary: Array[Byte] = upickle.writeBinary[TAST](this) - - def readBinary(bytes: Array[Byte]): TAST = upickle.readBinary[TAST](bytes) - - def writeString: String = upickle.write[TAST](this) - - def readString(str: String): TAST = upickle.read[TAST](str) - } - - case class LoadedModules(map: HashMap[ModuleRef, Vector[TAST]] = HashMap()) extends AnyVal { - def add(tast: TAST): LoadedModules = { - if ( - map.contains(tast.module) && map - .apply(tast.module) - .exists(_.fileName == tast.fileName) - ) { - throw new IllegalArgumentException( - s"Module ${tast.module} already loaded from file ${tast.fileName}" - ) - } else { - val newTASTs = map.getOrElse(tast.module, Vector()) :+ tast - LoadedModules(map.updated(tast.module, newTASTs)) - } - } - } - - object LoadedModules { - val Empty: LoadedModules = LoadedModules() - } - -} - -export TASTPackage.* diff --git a/tyck-base/src/main/scala/chester/tyck/Builtin.scala b/tyck-base/src/main/scala/chester/tyck/Builtin.scala deleted file mode 100644 index 29fedf825..000000000 --- a/tyck-base/src/main/scala/chester/tyck/Builtin.scala +++ /dev/null @@ -1,19 +0,0 @@ -package chester.tyck - -import chester.syntax.Name -import chester.syntax.core.* - -object BuiltIn { - case class BuiltinItem(id: Name, value: Term, ty: Term) {} - - val builtinItems: Seq[BuiltinItem] = Vector( - BuiltinItem("Int", IntType(None), Type0), - BuiltinItem("Integer", IntegerType(None), Type0), - BuiltinItem("Float", FloatType(None), Type0), - BuiltinItem("Rational", RationalType(None), Type0), - BuiltinItem("String", StringType(None), Type0), - BuiltinItem("Symbol", SymbolType(None), Type0), - BuiltinItem("List", ListF(None), TyToty) - ) - -} diff --git a/tyck-base/src/main/scala/chester/tyck/Context.scala b/tyck-base/src/main/scala/chester/tyck/Context.scala deleted file mode 100644 index 009badbbb..000000000 --- a/tyck-base/src/main/scala/chester/tyck/Context.scala +++ /dev/null @@ -1,98 +0,0 @@ -package chester.tyck - -import chester.syntax.* -import chester.syntax.accociativity.OperatorsContext -import chester.syntax.core.* -import chester.tyck.api.SymbolCollector -import chester.uniqid.UniqidOf - -import scala.collection.immutable.HashMap - -case class TyAndVal( - ty: Term, - value: Term -) {} - -object TyAndVal {} - -/** for pure values only like let and def. record is not included */ -case class ContextItem( - name: Name, - uniqId: UniqidOf[ReferenceCall], - ref: ReferenceCall, - ty: Term, - reference: Option[SymbolCollector] = None -) -object ContextItem {} -case class Imports() - -object Imports { - val Empty: Imports = Imports() -} - -case class Context( - map: Map[Name, UniqidOf[ReferenceCall]] = HashMap.empty[Name, UniqidOf[ReferenceCall]], // empty[...] are needed because compiler bugs - contextItems: Map[UniqidOf[ReferenceCall], ContextItem] = - HashMap.empty[UniqidOf[ReferenceCall], ContextItem], // empty[...] are needed because compiler bugs - knownMap: Map[UniqidOf[ReferenceCall], TyAndVal] = - HashMap.empty[UniqidOf[ReferenceCall], TyAndVal], // empty[...] are needed because compiler bugs - typeDefinitionNames: Map[Name, UniqidOf[TypeDefinition]] = HashMap.empty, - typeDefinitions: Map[UniqidOf[TypeDefinition], TypeDefinition] = HashMap.empty, - imports: Imports = Imports.Empty, - loadedModules: LoadedModules = LoadedModules.Empty, - operators: OperatorsContext = OperatorsContext.Default, - currentModule: ModuleRef = DefaultModule -) { - def updateModule(module: ModuleRef): Context = copy(currentModule = module) - - def getKnown(x: ReferenceCall): Option[TyAndVal] = - knownMap.get(x.uniqId.asInstanceOf[UniqidOf[ReferenceCall]]) - - def get(id: Name): Option[ContextItem] = - map.get(id).flatMap(uniqId => contextItems.get(uniqId)) - - def knownAdd(id: UniqidOf[ReferenceCall], y: TyAndVal): Context = - knownAdd(Seq(id -> y)) - - def knownAdd( - seq: Seq[(UniqidOf[ReferenceCall], TyAndVal)] - ): Context = { - val newKnownMap = seq.foldLeft(knownMap) { (acc, item) => - assert(!acc.contains(item._1), s"Duplicate key ${item._1}") - acc + item - } - copy(knownMap = newKnownMap) - } - - def add(item: ContextItem): Context = add(Seq(item)) - - def add(seq: Seq[ContextItem]): Context = { - val newMap = seq.foldLeft(map) { (acc, item) => - acc + (item.name -> item.uniqId) - } - val newContextItems = seq.foldLeft(contextItems) { (acc, item) => - require(!acc.contains(item.uniqId), s"Duplicate key ${item.uniqId}") - acc + (item.uniqId -> item) - } - copy(map = newMap, contextItems = newContextItems) - } - def addTypeDefinition(typeDef: TypeDefinition): Context = { - copy( - typeDefinitionNames = typeDefinitionNames + (typeDef.name -> typeDef.uniqId), - typeDefinitions = typeDefinitions + (typeDef.uniqId -> typeDef) - ) - } - - def getTypeDefinition(name: Name): Option[TypeDefinition] = { - val uniqId = typeDefinitionNames.get(name) - if (uniqId.isEmpty) return None - val r = typeDefinitions.get(uniqId.get) - return r - } - - def getTypeDefinitionById(id: UniqidOf[TypeDefinition]): Option[TypeDefinition] = { - typeDefinitions.get(id) - } -} - -object Context {} diff --git a/tyck-base/src/main/scala/chester/tyck/Get.scala b/tyck-base/src/main/scala/chester/tyck/Get.scala deleted file mode 100644 index 1a68fcd77..000000000 --- a/tyck-base/src/main/scala/chester/tyck/Get.scala +++ /dev/null @@ -1,101 +0,0 @@ -package chester.tyck - -import chester.error.* -import chester.error.Problem.Severity -import chester.utils.MutBox -import upickle.default.* - -trait Reporter[-T] { - def apply(value: T): Unit -} - -object StdErrReporter extends Reporter[Problem] { - def apply(value: Problem): Unit = { - println(value) - } -} - -extension [T](reporter: Reporter[T]) { - def report(xs: Seq[T]): Unit = xs.foreach(reporter.apply) -} - -class VectorReporter[T] extends Reporter[T] { - private val buffer = scala.collection.mutable.ArrayBuffer[T]() - - def apply(value: T): Unit = buffer += value - - def getReports: Vector[T] = buffer.toVector -} - -case class SeverityMap( - error: Boolean, - goal: Boolean, - warn: Boolean, - info: Boolean -) derives ReadWriter - -object SeverityMap { - def Empty: SeverityMap = - SeverityMap(error = false, goal = false, warn = false, info = false) -} - -class ReporterTrackError[T <: Problem](x: Reporter[T]) extends Reporter[T] { - private var errorVar = false - private var warnVar = false - private var goalVar = false - private var infoVar = false - - def apply(value: T): Unit = { - x.apply(value) - if value.severity == Severity.Error then errorVar = true - if value.severity == Severity.Warning then warnVar = true - if value.severity == Severity.Goal then goalVar = true - if value.severity == Severity.Info then infoVar = true - } - - def hasError: Boolean = errorVar - def hasWarn: Boolean = warnVar - def hasGoal: Boolean = goalVar - def hasInfo: Boolean = infoVar - - def getSeverityMap: SeverityMap = SeverityMap( - error = errorVar, - goal = goalVar, - warn = warnVar, - info = infoVar - ) -} - -class Get[P, S](val reporter: Reporter[P], private val state: MutBox[S]) { - def getState: S = state.get - - implicit inline def toReporter: Reporter[P] = reporter - - def report(problem: P): Unit = reporter.apply(problem) - - def reportseq(problems: Seq[P]): Unit = problems.foreach(report) - - def updateState(f: S => S): Unit = { - state.update(f) - } - - def uncheckedSetState(newState: S): Unit = { - state.set(newState) - } - - def updateAndMap[T](f: S => (S, T)): T = { - state.updateAndMap(f) - } -} - -object Get { - def run[P <: WithServerity, S, A]( - program: Get[P, S] => A - )(state: S): TyckResult0[P, S, A] = { - val reporter = new VectorReporter[P] - val stateBox = MutBox(state) - val get = Get(reporter, stateBox) - val result = program(get) - TyckResult0(stateBox.get, result, reporter.getReports) - } -} diff --git a/tyck-base/src/main/scala/chester/tyck/TyckResult.scala b/tyck-base/src/main/scala/chester/tyck/TyckResult.scala deleted file mode 100644 index 6a9a0fda3..000000000 --- a/tyck-base/src/main/scala/chester/tyck/TyckResult.scala +++ /dev/null @@ -1,69 +0,0 @@ -package chester.tyck - -import chester.error.* - -// maybe add marks about what item is used when there is a multiple choice. Maybe report some warning when two or more candidates are equally good -case class TyckResult0[Problem <: WithServerity, +S, +T]( - state: S, - result: T, - problems: Vector[Problem] = Vector() -) { - private var noErrors: java.lang.Boolean = null - - def errorsEmpty: Boolean = { - if (noErrors != null) return noErrors - val result = !problems.exists(_.isError) - noErrors = result - result - } - - def >>[S, T](next: TyckResult0[Problem, S, T]): TyckResult0[Problem, S, T] = { - TyckResult0( - state = next.state, - result = next.result, - problems = problems ++ next.problems - ) - } -} - -type TyckResult[+S, +T] = TyckResult0[TyckProblem, S, T] - -object TyckResult { - def apply[S, T]( - state: S, - result: T, - warnings: Vector[TyckWarning] = Vector(), - errors: Vector[TyckError] = Vector() - ): TyckResult[S, T] = { - TyckResult0(state, result, warnings ++ errors) - } - - object Success { - def unapply[S, T]( - x: TyckResult[S, T] - ): Option[(T, S, Vector[TyckWarning])] = { - if (x.errorsEmpty) - Some((x.result, x.state, x.problems.asInstanceOf[Vector[TyckWarning]])) - else None - } - } - - object Failure { - def unapply[S, T]( - x: TyckResult[S, T] - ): Option[(Vector[TyckError], Vector[TyckWarning], S, T)] = { - if (!x.errorsEmpty) - Some( - ( - x.problems - .collect { case e: TyckError => e }, - x.problems - .collect { case w: TyckWarning => w }, - x.state, - x.result - ) - ) - else None - } - } -} diff --git a/tyck-base/src/main/scala/chester/tyck/api/SemanticCollector.scala b/tyck-base/src/main/scala/chester/tyck/api/SemanticCollector.scala deleted file mode 100644 index 2eed9f501..000000000 --- a/tyck-base/src/main/scala/chester/tyck/api/SemanticCollector.scala +++ /dev/null @@ -1,98 +0,0 @@ -package chester.tyck.api - -import chester.syntax.* -import chester.syntax.concrete.* -import chester.syntax.core.* -import chester.tyck.Context -import upickle.default.* -import chester.uniqid.* - -import scala.collection.mutable - -trait SymbolCollector { - def referencedOn(expr: Expr): Unit = () -} - -object NoopSymbolCollector extends SymbolCollector {} - -trait SemanticCollector { - // TODO: semantic highlighting? - def highlightLetDef(expr: Expr): Unit = () - def highlightLiteral(expr: Expr): Unit = () - - def newSymbol( - call: ReferenceCall, - id: UniqidOf[ReferenceCall], - definedOn: Expr, - localCtx: Context - ): SymbolCollector = NoopSymbolCollector - - def metaFinished(replace: MetaTerm => Term): Unit = () -} - -private implicit inline def rwUniqIDOfVar[T]: ReadWriter[UniqidOf[ReferenceCall]] = - rwUniqIDOf.asInstanceOf[ReadWriter[UniqidOf[ReferenceCall]]] - -// TODO: handle when call's ty is MetaTerm -case class CollectedSymbol( - call: ReferenceCall, - id: UniqidOf[ReferenceCall], - definedOn: Expr, - referencedOn: Vector[Expr] -) derives ReadWriter { - def name: Name = call.name - - def metaFinished(replace: MetaTerm => Term): CollectedSymbol = { - this.copy(call = call.replaceMeta(replace).asInstanceOf[ReferenceCall]) - } -} - -class VectorSemanticCollector extends SemanticCollector { - private var builder: mutable.ArrayDeque[CollectedSymbol] = - new mutable.ArrayDeque[CollectedSymbol]() - override def newSymbol( - call: ReferenceCall, - id: UniqidOf[ReferenceCall], - definedOn: Expr, - localCtx: Context - ): SymbolCollector = { - val index = builder.length - builder.append(CollectedSymbol(call, id, definedOn, Vector())) - assert(builder.length == index + 1) - new SymbolCollector { - override def referencedOn(expr: Expr): Unit = { - val symbol = builder(index) - builder(index) = symbol.copy(referencedOn = symbol.referencedOn :+ expr) - } - } - } - def get: Vector[CollectedSymbol] = builder.toVector - - override def metaFinished(replace: MetaTerm => Term): Unit = { - builder = builder.map(_.metaFinished(replace)) - } -} - -object NoopSemanticCollector extends SemanticCollector {} - -class UnusedVariableWarningWrapper(x: SemanticCollector) extends SemanticCollector { - private var unusedVariables: Vector[CollectedSymbol] = Vector() - override def newSymbol( - call: ReferenceCall, - id: UniqidOf[ReferenceCall], - definedOn: Expr, - localCtx: Context - ): SymbolCollector = { - val symbolCollector = x.newSymbol(call, id, definedOn, localCtx) - val c = CollectedSymbol(call, id, definedOn, Vector()) - unusedVariables = unusedVariables :+ c - new SymbolCollector { - override def referencedOn(expr: Expr): Unit = { - symbolCollector.referencedOn(expr) - unusedVariables = unusedVariables.filterNot(_ == c) - } - } - } - def foreachUnused(f: CollectedSymbol => Unit): Unit = - unusedVariables.foreach(f) -} diff --git a/tyck-base/src/main/scala/chester/tyck/common.scala b/tyck-base/src/main/scala/chester/tyck/common.scala deleted file mode 100644 index d2f5ae4b7..000000000 --- a/tyck-base/src/main/scala/chester/tyck/common.scala +++ /dev/null @@ -1,7 +0,0 @@ -package chester.tyck - -import chester.error.TyckProblem - -given ckToReport(using ck: Tyck): Reporter[TyckProblem] = ck.reporter - -type Tyck = Get[TyckProblem, Unit] diff --git a/tyck-base/src/main/scala/chester/tyck/convertMeta.scala b/tyck-base/src/main/scala/chester/tyck/convertMeta.scala deleted file mode 100644 index 136899891..000000000 --- a/tyck-base/src/main/scala/chester/tyck/convertMeta.scala +++ /dev/null @@ -1,9 +0,0 @@ -package chester.tyck - -import chester.syntax.concrete.ExprMeta -import chester.syntax.core.* - -def convertMeta(meta: Option[ExprMeta]): Option[TermMeta] = for { - m <- meta - s <- m.sourcePos -} yield TermMeta(s) diff --git a/tyck-base/src/main/scala/chester/utils/propagator/CommonPropagator.scala b/tyck-base/src/main/scala/chester/utils/propagator/CommonPropagator.scala deleted file mode 100644 index 347d2befb..000000000 --- a/tyck-base/src/main/scala/chester/utils/propagator/CommonPropagator.scala +++ /dev/null @@ -1,147 +0,0 @@ -package chester.utils.propagator -import cats.implicits.* - -trait CommonPropagator[Ck] extends ProvideCellId { - - case class MergeSimple[T](a: CellId[T], b: CellId[T]) extends Propagator[Ck] { - override val readingCells: Set[CIdOf[Cell[?]]] = Set(a, b) - override val writingCells: Set[CIdOf[Cell[?]]] = Set(a, b) - override val zonkingCells: Set[CIdOf[Cell[?]]] = Set(a, b) - - override def run(using state: StateAbility[Ck], more: Ck): Boolean = { - val aVal = state.readStable(a) - val bVal = state.readStable(b) - if (aVal.isDefined && bVal.isDefined) { - if (aVal.get == bVal.get) return true - throw new IllegalStateException( - "Merge propagator should not be used if the values are different" - ) - return true - } - if (aVal.isDefined) { - state.fill(b, aVal.get) - return true - } - if (bVal.isDefined) { - state.fill(a, bVal.get) - return true - } - false - } - - override def naiveZonk( - needed: Vector[CellIdAny] - )(using state: StateAbility[Ck], more: Ck): ZonkResult = { - val aVal = state.readStable(a) - val bVal = state.readStable(b) - if (aVal.isDefined && bVal.isDefined) { - if (aVal.get == bVal.get) return ZonkResult.Done - throw new IllegalStateException( - "Merge propagator should not be used if the values are different" - ) - return ZonkResult.Done - } - if (aVal.isDefined) { - state.fill(b, aVal.get) - return ZonkResult.Done - } - if (bVal.isDefined) { - state.fill(a, bVal.get) - return ZonkResult.Done - } - ZonkResult.NotYet - } - } - - case class FlatMaping[T, U]( - xs: Seq[CellId[T]], - f: Seq[T] => U, - result: CellId[U] - ) extends Propagator[Ck] { - override val readingCells = xs.toSet - override val writingCells: Set[CIdOf[Cell[?]]] = Set(result) - override val zonkingCells: Set[CIdOf[Cell[?]]] = Set(result) - - override def run(using state: StateAbility[Ck], more: Ck): Boolean = { - xs.traverse(state.readStable(_)).map(f) match { - case Some(result) => { - state.fill(this.result, result) - true - } - case None => false - } - } - - override def naiveZonk( - needed: Vector[CellIdAny] - )(using state: StateAbility[Ck], more: Ck): ZonkResult = { - val needed = xs.filter(state.noStableValue(_)) - if (needed.nonEmpty) return ZonkResult.Require(needed) - val done = run - require(done) - ZonkResult.Done - } - } - - def FlatMap[T, U]( - xs: Seq[CellId[T]] - )(f: Seq[T] => U)(using ck: Ck, state: StateAbility[Ck]): CellId[U] = { - val cell = state.addCell(OnceCell[U]()) - state.addPropagator(FlatMaping(xs, f, cell)) - cell - } - - def Map1[T, U]( - x: CellId[T] - )(f: T => U)(using ck: Ck, state: StateAbility[Ck]): CellId[U] = { - val cell = state.addCell(OnceCell[U]()) - state.addPropagator(FlatMaping(Vector(x), (xs: Seq[T]) => f(xs.head), cell)) - cell - } - - def Map2[A, B, C](x: CellId[A], y: CellId[B])( - f: (A, B) => C - )(using ck: Ck, state: StateAbility[Ck]): CellId[C] = { - val cell = state.addCell(OnceCell[C]()) - state.addPropagator( - FlatMaping( - Vector[CellId[Any]]( - x.asInstanceOf[CellId[Any]], - y.asInstanceOf[CellId[Any]] - ), - (xs: Seq[Any]) => f(xs(0).asInstanceOf[A], xs(1).asInstanceOf[B]), - cell - ) - ) - cell - } - - def Map3[A, B, C, D](x: CellId[A], y: CellId[B], z: CellId[C])( - f: (A, B, C) => D - )(using ck: Ck, state: StateAbility[Ck]): CellId[D] = { - val cell = state.addCell(OnceCell[D]()) - state.addPropagator( - FlatMaping( - Vector[CellId[Any]]( - x.asInstanceOf[CellId[Any]], - y.asInstanceOf[CellId[Any]], - z.asInstanceOf[CellId[Any]] - ), - (xs: Seq[Any]) => - f( - xs(0).asInstanceOf[A], - xs(1).asInstanceOf[B], - xs(2).asInstanceOf[C] - ), - cell - ) - ) - cell - } - - def Traverse[A]( - x: Seq[CellId[A]] - )(using ck: Ck, state: StateAbility[Ck]): CellId[Seq[A]] = - FlatMap(x)(identity) - -} diff --git a/tyck-base/src/main/scala/chester/utils/propagator/ProvideCellId.scala b/tyck-base/src/main/scala/chester/utils/propagator/ProvideCellId.scala deleted file mode 100644 index 6f83c9ec4..000000000 --- a/tyck-base/src/main/scala/chester/utils/propagator/ProvideCellId.scala +++ /dev/null @@ -1,233 +0,0 @@ -package chester.utils.propagator - -// TODO: maybe distinguish between read and fill to have more sound Scala types and functions. One is +T and one is -T -trait ProvideCellId { - type CIdOf[+T <: Cell[?]] - type PIdOf[+T <: Propagator[?]] - type CellId[T] = CIdOf[Cell[T]] - type CellIdAny = CIdOf[Cell[?]] - type SeqId[T] = CIdOf[SeqCell[T]] - type CellIdOr[T] = CellId[T] | T - - def isCId(x: Any): Boolean - - def assumeCId(x: Any): CellIdAny = x.asInstanceOf[CIdOf[Cell[?]]] - - trait Cell[T] { - def default: Option[T] = None - - /** stable means can only change once from None to a fixed Some value or always be a fixed value - */ - def readStable: Option[T] - - def readUnstable: Option[T] = readStable - - def hasStableValue: Boolean = readStable.isDefined - - def noStableValue: Boolean = !hasStableValue - - def hasSomeValue: Boolean = readUnstable.isDefined - - def noAnyValue: Boolean = !hasSomeValue - - /** fill an unstable cell */ - def fill(newValue: T): Cell[T] - } - - trait SeqCell[T] extends UnstableCell[Seq[T]] with NoFill[Seq[T]] { - def add(newValue: T): SeqCell[T] - } - - trait BaseMapCell[A, B] { - def add(key: A, value: B): BaseMapCell[A, B] - } - - trait UnstableCell[T] extends Cell[T] { - override def readStable: Option[T] = - throw new UnsupportedOperationException( - s"${getClass.getName} is not stable" - ) - - override def hasStableValue: Boolean = - throw new UnsupportedOperationException( - s"${getClass.getName} is not stable" - ) - - override def noStableValue: Boolean = - throw new UnsupportedOperationException( - s"${getClass.getName} is not stable" - ) - } - trait NoFill[T] extends Cell[T] { - override def fill(newValue: T): Cell[T] = - throw new UnsupportedOperationException( - s"${getClass.getName} cannot be filled" - ) - } - - trait MapCell[A, B] extends UnstableCell[Map[A, B]] with BaseMapCell[A, B] with NoFill[Map[A, B]] {} - - case class OnceCell[T]( - value: Option[T] = None, - override val default: Option[T] = None - ) extends Cell[T] { - override def readStable: Option[T] = value - - override def fill(newValue: T): OnceCell[T] = { - require(value.isEmpty) - copy(value = Some(newValue)) - } - } - - case class MutableCell[T](value: Option[T]) extends Cell[T] { - override def readStable: Option[T] = value - - override def fill(newValue: T): MutableCell[T] = { - copy(value = Some(newValue)) - } - } - - case class CollectionCell[T](value: Vector[T] = Vector.empty) extends SeqCell[T] { - override def readUnstable: Option[Vector[T]] = Some(value) - - override def add(newValue: T): CollectionCell[T] = - copy(value = value :+ newValue) - } - - case class MappingCell[A, B](value: Map[A, B] = Map.empty[A, B]) extends MapCell[A, B] { - override def readStable: Option[Map[A, B]] = Some(value) - - override def add(key: A, newValue: B): MappingCell[A, B] = - copy(value = value + (key -> newValue)) - } - - case class LiteralCell[T](value: T) extends Cell[T] { - override def readStable: Option[T] = Some(value) - - override def hasStableValue: Boolean = true - - override def fill(newValue: T): LiteralCell[T] = - throw new UnsupportedOperationException("LiteralCell cannot be filled") - } - - def literal[T](t: T)(using state: StateAbility[?]): CellId[T] = { - val cell = state.addCell(LiteralCell[T](t)) - cell - } - - val NormalScore: Int = 8 - val NoScore: Int = 0 - - trait Propagator[Ability] { - def score: Int = NormalScore - - def identify: Option[Any] = None - - def readingCells: Set[CIdOf[Cell[?]]] = Set.empty - - def writingCells: Set[CIdOf[Cell[?]]] = Set.empty - - def zonkingCells: Set[CIdOf[Cell[?]]] = Set.empty - - /** @return - * true if the propagator finished its work - */ - def run(using state: StateAbility[Ability], more: Ability): Boolean - - /** make a best guess for zonkingCells */ - def naiveZonk( - needed: Vector[CIdOf[Cell[?]]] - )(using state: StateAbility[Ability], more: Ability): ZonkResult - - def naiveFallbackZonk( - needed: Vector[CIdOf[Cell[?]]] - )(using state: StateAbility[Ability], more: Ability): ZonkResult = - naiveZonk(needed) - } - - trait StateAbility[Ability] { - def readCell[T <: Cell[?]](id: CIdOf[T]): Option[T] - - def readStable[U](id: CellId[U]): Option[U] = - readCell[Cell[U]](id).get.readStable - def readUnstable[U](id: CellId[U]): Option[U] = - readCell[Cell[U]](id).get.readUnstable - - protected def update[T <: Cell[?]](id: CIdOf[T], f: T => T)(using - Ability - ): Unit - - def fill[T <: Cell[U], U](id: CIdOf[T], f: U)(using Ability): Unit = { - update[T](id, _.fill(f).asInstanceOf[T]) - } - - def add[T <: SeqCell[U], U](id: CIdOf[T], f: U)(using Ability): Unit = { - update[T](id, _.add(f).asInstanceOf[T]) - } - - def add[T <: MapCell[A, B], A, B](id: CIdOf[T], key: A, value: B)(using - Ability - ): Unit = { - update[T](id, _.add(key, value).asInstanceOf[T]) - } - - def addCell[T <: Cell[?]](cell: T): CIdOf[T] - - def hasStableValue[T <: Cell[?]](id: CIdOf[T]): Boolean = - readCell(id).exists((x: T) => x.hasStableValue) - - def noStableValue[T <: Cell[?]](id: CIdOf[T]): Boolean = !hasStableValue(id) - - def hasSomeValue[T <: Cell[?]](id: CIdOf[T]): Boolean = - readCell(id).exists((x: T) => x.hasSomeValue) - - def noAnyValue[T <: Cell[?]](id: CIdOf[T]): Boolean = !hasSomeValue(id) - @deprecated("impure") - def requireRemovePropagatorZonking(identify: Any, cell: CellIdAny): Unit = - ??? - - def addPropagatorGetPid[T <: Propagator[Ability]](propagator: T)(using - more: Ability - ): PIdOf[T] - - final def addPropagator[T <: Propagator[Ability]](propagator: T)(using - more: Ability - ): Unit = { - val _ = addPropagatorGetPid(propagator) - } - - def tick(using more: Ability): Unit - - def stable: Boolean - - def tickAll(using more: Ability): Unit = { - while (!stable) { - tick(using more) - } - } - - @deprecated("impure") - def readingZonkings( - cells: Vector[CIdOf[Cell[?]]] - ): Vector[Propagator[Ability]] = ??? - - /** make a best guess for those cells */ - def naiveZonk(cells: Vector[CIdOf[Cell[?]]])(using more: Ability): Unit - - def toId[T](x: CellIdOr[T]): CIdOf[Cell[T]] = x match { - case x if isCId(x) => x.asInstanceOf[CIdOf[Cell[T]]] - case x => { - val t = x.asInstanceOf[T] - val cell = addCell(LiteralCell[T](t)) - cell.asInstanceOf[CIdOf[Cell[T]]] - } - } - } - - enum ZonkResult { - case Done extends ZonkResult - case Require(needed: Seq[CIdOf[Cell[?]]]) extends ZonkResult - case NotYet extends ZonkResult - } - -} diff --git a/tyck-base/src/main/scala/chester/utils/propagator/ProvideImmutable.scala b/tyck-base/src/main/scala/chester/utils/propagator/ProvideImmutable.scala deleted file mode 100644 index a755651b7..000000000 --- a/tyck-base/src/main/scala/chester/utils/propagator/ProvideImmutable.scala +++ /dev/null @@ -1,132 +0,0 @@ -package chester.utils.propagator - -import chester.uniqid.* - -trait ProvideImmutable extends ProvideImpl { - type CIdOf[+T <: Cell[?]] = UniqidOf[T] - type PIdOf[+T <: Propagator[?]] = UniqidOf[T] - override def isCId(x: Any): Boolean = Uniqid.is(x) - - type CellsState = Map[CIdOf[Cell[?]], Cell[?]] - private val CellsStateEmpty: CellsState = Map.empty - type PropagatorsState[Ability] = - Map[PIdOf[Propagator[Ability]], Propagator[Ability]] - - private inline def PropagatorsStateEmpty[Ability]: PropagatorsState[Ability] = - Map.empty - - case class State[Ability]( - cells: CellsState = CellsStateEmpty, - propagators: PropagatorsState[Ability] = PropagatorsStateEmpty[Ability], - didChanged: Vector[CIdOf[Cell[?]]] = Vector.empty - ) { - def stable: Boolean = didChanged.isEmpty - } - - override def stateAbilityImpl[Ability]: StateAbility[Ability] = - StateCells[Ability]() - - class StateCells[Ability](var state: State[Ability] = State[Ability]()) extends StateAbility[Ability] { - override def stable: Boolean = state.stable - - override def readCell[T <: Cell[?]](id: CIdOf[T]): Option[T] = - state.cells.get(id).asInstanceOf[Option[T]] - - override def update[T <: Cell[?]](id: CIdOf[T], f: T => T)(using - Ability - ): Unit = { - state.cells.get(id) match { - case Some(cell) => - val newCell = f(cell.asInstanceOf[T]) - if (cell != newCell) { - state = state.copy( - didChanged = state.didChanged :+ id, - cells = state.cells.updated(id, newCell) - ) - } - case None => - throw new IllegalArgumentException(s"Cell with id $id not found") - } - } - - override def addCell[T <: Cell[?]](cell: T): CIdOf[T] = { - val id = Uniqid.generate[T] - state = state.copy(cells = state.cells.updated(id, cell)) - id - } - - override def addPropagatorGetPid[T <: Propagator[Ability]]( - propagator: T - )(using more: Ability): PIdOf[T] = { - val uniqId = Uniqid.generate[T] - state = state.copy(propagators = state.propagators.updated(uniqId, propagator)) - if (propagator.run(using this, more)) { - state = state.copy(propagators = state.propagators.removed(uniqId)) - } - uniqId - } - - override def tick(using more: Ability): Unit = { - val didChanged = state.didChanged - state = state.copy(didChanged = Vector.empty) - state.propagators - .filter((_, propagator) => propagator.readingCells.exists(didChanged.contains)) - .foreach { case (pid, propagator) => - if (state.propagators.contains(pid)) { - val done = propagator.run(using this, more) - if (done) { - state = state.copy(propagators = state.propagators.removed(pid)) - } - } - } - } - - override def readingZonkings( - cells: Vector[CIdOf[Cell[?]]] - ): Vector[Propagator[Ability]] = { - state.propagators - .filter((_, propagator) => propagator.zonkingCells.exists(cells.contains)) - .values - .toVector - } - - override def naiveZonk( - cells: Vector[CIdOf[Cell[?]]] - )(using more: Ability): Unit = { - var cellsNeeded = Vector.empty[CIdOf[Cell[?]]] - while (true) { - tickAll - val cellsToZonk = if (cellsNeeded.nonEmpty) { - val a = cellsNeeded - cellsNeeded = Vector.empty - (a ++ cells).filter(id => !state.cells(id).hasStableValue) - } else { - cells.filter(id => !state.cells(id).hasStableValue) - } - val xs = state.propagators.filter((_, propagator) => propagator.zonkingCells.exists(cellsToZonk.contains)) - val uncorvedCells = cellsToZonk.filter(id => !xs.values.exists(_.zonkingCells.contains(id))) - if (uncorvedCells.nonEmpty) { - throw new IllegalStateException( - s"Cells $uncorvedCells are not covered by any propagator" - ) - } - xs.foreach { case (pid, propagator) => - tickAll - if (state.propagators.contains(pid)) { - val result = propagator.naiveZonk(cells)(using this, more) - result match { - case ZonkResult.Done => - state = state.copy(propagators = state.propagators.removed(pid)) - case ZonkResult.Require(needed) => - cellsNeeded = cellsNeeded ++ needed - case ZonkResult.NotYet => - } - } - } - tickAll - if (cellsToZonk.isEmpty) return - } - } - } - -} diff --git a/tyck-base/src/main/scala/chester/utils/propagator/ProvideImpl.scala b/tyck-base/src/main/scala/chester/utils/propagator/ProvideImpl.scala deleted file mode 100644 index ddbdd2b1b..000000000 --- a/tyck-base/src/main/scala/chester/utils/propagator/ProvideImpl.scala +++ /dev/null @@ -1,5 +0,0 @@ -package chester.utils.propagator - -trait ProvideImpl extends ProvideCellId { - def stateAbilityImpl[Ability]: StateAbility[Ability] -} diff --git a/tyck-base/src/main/scala/chester/utils/propagator/ProvideMultithread.scala b/tyck-base/src/main/scala/chester/utils/propagator/ProvideMultithread.scala deleted file mode 100644 index 651573d7e..000000000 --- a/tyck-base/src/main/scala/chester/utils/propagator/ProvideMultithread.scala +++ /dev/null @@ -1,313 +0,0 @@ -package chester.utils.propagator - -import chester.uniqid.{Uniqid, UniqidOf} - -import java.util.concurrent.* -import java.util.concurrent.atomic.{AtomicBoolean, AtomicReference} -import scala.jdk.CollectionConverters.* - -// currently broken too eager to use default values -trait ProvideMultithread extends ProvideImpl { - - class HoldCell[+T <: Cell[?]]( - val uniqId: UniqidOf[Impl[?]], - initialValue: T - ) { - private val storeRef = new AtomicReference[Cell[?]](initialValue) - val readingPropagators = new ConcurrentLinkedQueue[PIdOf[Propagator[?]]]() - val zonkingPropagators = new ConcurrentLinkedQueue[PIdOf[Propagator[?]]]() - - def store: T = storeRef.get().asInstanceOf[T] - - /** Atomically updates the store */ - def compareAndSetStore( - expectedValue: Cell[?], - newValue: Cell[?] - ): Boolean = { - storeRef.compareAndSet(expectedValue, newValue) - } - - def noAnyValue: Boolean = store.noAnyValue - def noStableValue: Boolean = store.noStableValue - } - - type CIdOf[+T <: Cell[?]] = HoldCell[T] - type PIdOf[+T <: Propagator[?]] = HoldPropagator[T] - type CellId[T] = CIdOf[Cell[T]] - type SeqId[T] = CIdOf[SeqCell[T]] - - def isCId(x: Any): Boolean = x.isInstanceOf[HoldCell[?]] - - override def assumeCId(x: Any): CIdOf[Cell[?]] = - x.asInstanceOf[CIdOf[Cell[?]]] - - class HoldPropagator[+T <: Propagator[?]]( - val uniqId: UniqidOf[Impl[?]], - initialValue: T - ) { - private val storeRef = new AtomicReference[Propagator[?]](initialValue) - private val aliveRef = new AtomicBoolean(true) - - def store: T = storeRef.get().asInstanceOf[T] - - /** Atomically updates the store */ - def compareAndSetStore( - expectedValue: Propagator[?], - newValue: Propagator[?] - ): Boolean = { - storeRef.compareAndSet(expectedValue, newValue) - } - - def alive: Boolean = aliveRef.get() - - def setAlive(value: Boolean): Unit = { - aliveRef.set(value) - } - } - - override def stateAbilityImpl[Ability]: StateAbility[Ability] = - Impl[Ability]() - - class Impl[Ability]( - val uniqId: UniqidOf[Impl[Ability]] = Uniqid.generate[Impl[Ability]] - ) extends StateAbility[Ability] { - - private val propagators = - new ConcurrentLinkedQueue[PIdOf[Propagator[Ability]]]() - private val forkJoinPool = new ForkJoinPool( - Runtime.getRuntime.availableProcessors() - ) - - override def readCell[T <: Cell[?]](id: CIdOf[T]): Option[T] = { - require(id.uniqId == uniqId) - Some(id.store) - } - - override def update[T <: Cell[?]](id: CIdOf[T], f: T => T)(using - Ability - ): Unit = { - require(id.uniqId == uniqId) - var updated = false - while (!updated) { - val oldValue = id.store - val newValue = f(oldValue) - updated = id.compareAndSetStore(oldValue, newValue) - if (updated) { - // Immediately process dependent propagators - processPropagators(id) - } - } - } - - override def tick(using Ability): Unit = { - // Do nothing - } - - override def fill[T <: Cell[U], U](id: CIdOf[T], value: U)(using - Ability - ): Unit = { - require(id.uniqId == uniqId) - var updated = false - while (!updated) { - val oldValue = id.store - val newValue = oldValue.fill(value).asInstanceOf[T] - updated = id.compareAndSetStore(oldValue, newValue) - if (updated) { - // Immediately process dependent propagators - processPropagators(id) - } - } - } - - override def addCell[T <: Cell[?]](cell: T): CIdOf[T] = { - new HoldCell[T](uniqId, cell) - } - - override def addPropagatorGetPid[T <: Propagator[Ability]]( - propagator: T - )(using Ability): PIdOf[T] = { - val id = new HoldPropagator[T](uniqId, propagator) - propagators.add(id.asInstanceOf[PIdOf[Propagator[Ability]]]) - // Submit task to process this propagator - forkJoinPool.execute( - new PropagatorTask( - Vector(id.asInstanceOf[HoldPropagator[Propagator[Ability]]]), - this - ) - ) - id - } - - override def stable: Boolean = propagators.isEmpty - - // Process propagators in batches to improve performance - private def processPropagators( - changedCell: CIdOf[?] - )(using Ability): Unit = { - val dependentPropagators = changedCell.readingPropagators - .iterator() - .asScala - .filter(_.alive) - .toVector - if (dependentPropagators.nonEmpty) { - forkJoinPool.execute( - new BatchPropagatorTask( - dependentPropagators - .asInstanceOf[Vector[HoldPropagator[Propagator[Ability]]]], - this - ) - ) - } - } - - override def naiveZonk( - cells: Vector[CIdOf[Cell[?]]] - )(using Ability): Unit = { - var cellsNeeded = cells - var tryFallback = 0 - var loop = true - - while (loop) { - // Process any remaining propagators - val tasks = cellsNeeded.flatMap { c => - if (c.noAnyValue) { - c.zonkingPropagators.asScala.filter(_.alive).map { p => - new ZonkTask( - c, - p.asInstanceOf[HoldPropagator[Propagator[Ability]]], - firstFallback = tryFallback == 0, - this - ) - } - } else { - Vector.empty - } - } - if (tasks.nonEmpty) { - val _ = ForkJoinTask.invokeAll(tasks.asJava) - } - - // Check if all cells have values - cellsNeeded = cellsNeeded.filter(_.noAnyValue) - if (cellsNeeded.isEmpty) { - loop = false - } else { - tryFallback += 1 - if (tryFallback > 2) { - // Last resort: try default values - val defaultTasks = - cellsNeeded.map(c => new DefaultValueTask(c, this)) - val _ = ForkJoinTask.invokeAll(defaultTasks.asJava) - - // Check again if all cells have values - cellsNeeded = cellsNeeded.filter(_.noAnyValue) - if (cellsNeeded.nonEmpty) { - throw new IllegalStateException( - s"Cells $cellsNeeded are not covered by any propagator" - ) - } else { - loop = false - } - } - } - } - } - - class BatchPropagatorTask( - propagators: Vector[PIdOf[Propagator[Ability]]], - state: Impl[Ability] - )(using more: Ability) - extends RecursiveAction { - override def compute(): Unit = { - try { - propagators.foreach { p => - require(p.uniqId == uniqId) - if (p.alive) { - val propagator = p.store - val done = propagator.run(using state, more) - if (done) { - p.setAlive(false) - val _ = state.propagators.remove(p) - } - } - } - } catch { - case ex: Exception => - // Handle or log the exception as needed - throw ex - } - } - } - - class PropagatorTask( - propagators: Vector[PIdOf[Propagator[Ability]]], - state: Impl[Ability] - )(using more: Ability) - extends RecursiveAction { - override def compute(): Unit = { - try { - propagators.foreach { p => - require(p.uniqId == uniqId) - if (p.alive) { - val propagator = p.store - val done = propagator.run(using state, more) - if (done) { - p.setAlive(false) - val _ = state.propagators.remove(p) - } - } - } - } catch { - case ex: Exception => - // Handle or log the exception as needed - throw ex - } - } - } - - class ZonkTask( - c: CIdOf[Cell[?]], - p: PIdOf[Propagator[Ability]], - firstFallback: Boolean, - state: Impl[Ability] - )(using more: Ability) - extends RecursiveAction { - override def compute(): Unit = { - require(c.uniqId == uniqId) - require(p.uniqId == uniqId) - if (c.noAnyValue && p.alive) { - val propagator = p.store - val zonkResult = if (firstFallback) { - propagator.naiveZonk(Vector(c))(using state, more) - } else { - propagator.naiveFallbackZonk(Vector(c))(using state, more) - } - zonkResult match { - case ZonkResult.Done => - p.setAlive(false) - val _ = propagators.remove(p) - case ZonkResult.Require(needed) => - needed.foreach { n => - if (n.noStableValue) { - // Process the newly needed cells - state.naiveZonk(Vector(n)) - } - } - case ZonkResult.NotYet => - // Do nothing - } - } - } - } - - class DefaultValueTask(c: CIdOf[Cell[?]], state: Impl[Ability])(using - ability: Ability - ) extends RecursiveAction { - override def compute(): Unit = { - if (c.noAnyValue && c.store.default.isDefined) { - state.fill(c.asInstanceOf[CIdOf[Cell[Any]]], c.store.default.get) - } - } - } - } -} diff --git a/tyck-base/src/main/scala/chester/utils/propagator/ProvideMutable.scala b/tyck-base/src/main/scala/chester/utils/propagator/ProvideMutable.scala deleted file mode 100644 index 310e830b2..000000000 --- a/tyck-base/src/main/scala/chester/utils/propagator/ProvideMutable.scala +++ /dev/null @@ -1,238 +0,0 @@ -package chester.utils.propagator - -import chester.uniqid.{Uniqid, UniqidOf} - -import scala.collection.mutable - -trait ProvideMutable extends ProvideImpl { - class HoldCell[+T <: Cell[?]](val uniqId: UniqidOf[Impl[?]], value: T) { - var store: Cell[?] = value - var didChange: Boolean = false - var readingPropagators: Vector[PIdOf[Propagator[?]]] = Vector.empty - var zonkingPropagators: Vector[PIdOf[Propagator[?]]] = Vector.empty - - inline def noAnyValue: Boolean = store.noAnyValue - } - - type CIdOf[+T <: Cell[?]] = HoldCell[T] - - class HoldPropagator[+T <: Propagator[?]]( - val uniqId: UniqidOf[Impl[?]], - value: T - ) { - var store: Propagator[?] = value - var alive: Boolean = true - } - - type PIdOf[+T <: Propagator[?]] = HoldPropagator[T] - - override def isCId(x: Any): Boolean = { - val result = x.isInstanceOf[HoldCell[?]] - result - } - - override def assumeCId(x: Any): CIdOf[Cell[?]] = { - require(isCId(x)) - x.asInstanceOf[CIdOf[Cell[?]]] - } - - override def stateAbilityImpl[Ability]: StateAbility[Ability] = - Impl[Ability]() - - class Impl[Ability]( - val uniqId: UniqidOf[Impl[Ability]] = Uniqid.generate[Impl[Ability]] - ) extends StateAbility[Ability] { - var didChanged: mutable.ArrayDeque[CIdOf[?]] = mutable.ArrayDeque.empty - - override def readCell[T <: Cell[?]](id: CIdOf[T]): Option[T] = { - require(id.uniqId == uniqId) - Some(id.store.asInstanceOf[T]) - } - - override def update[T <: Cell[?]](id: CIdOf[T], f: T => T)(using - Ability - ): Unit = { - didSomething = true - require(id.uniqId == uniqId) - id.store = f(id.store.asInstanceOf[T]) - id.didChange = true - didChanged.append(id) - } - - override def addCell[T <: Cell[?]](cell: T): CIdOf[T] = { - didSomething = true - val id = new HoldCell[T](uniqId, cell) - id - } - - override def addPropagatorGetPid[T <: Propagator[Ability]]( - propagator: T - )(using more: Ability): PIdOf[T] = { - didSomething = true - val id = new HoldPropagator[T](uniqId, propagator) - for (cell <- propagator.zonkingCells) { - cell.zonkingPropagators = cell.zonkingPropagators :+ id.asInstanceOf[PIdOf[Propagator[?]]] - } - for (cell <- propagator.readingCells) { - cell.readingPropagators = cell.readingPropagators :+ id.asInstanceOf[PIdOf[Propagator[?]]] - } - if (propagator.run(using this, more)) { - id.alive = false - } - id - } - - override def stable: Boolean = didChanged.isEmpty - - override def tick(using more: Ability): Unit = { - while (didChanged.nonEmpty) { - val id = didChanged.removeHead() - if (id.didChange) { - id.didChange = false - for (p <- id.readingPropagators) { - require(p.uniqId == uniqId) - if (p.alive) { - if (p.store.asInstanceOf[Propagator[Ability]].run(using this, more)) { - didSomething = true - p.alive = false - } - } - } - } - } - } - - @deprecated("impure") - override def readingZonkings( - cells: Vector[CIdOf[Cell[?]]] - ): Vector[Propagator[Ability]] = { - cells - .flatMap(_.zonkingPropagators) - .map(_.store.asInstanceOf[Propagator[Ability]]) - } - - @deprecated("impure") - override def requireRemovePropagatorZonking( - identify: Any, - cell: CellIdAny - ): Unit = { - val cell1 = cell.asInstanceOf[CIdOf[Cell[?]]] - for ( - p <- cell1.zonkingPropagators - .filter(x => x.store.identify == Some(identify)) - ) { - if (p.alive) { - didSomething = true - p.alive = false - } - } - } - - var didSomething = false - - override def naiveZonk( - cells: Vector[CIdOf[Cell[?]]] - )(using more: Ability): Unit = { - var cellsNeeded = cells - var tryFallback: Int = 0 - while (true) { - didSomething = false - tickAll - cellsNeeded = cellsNeeded - .filter(this.noAnyValue(_)) - .sortBy(x => -x.zonkingPropagators.map(_.store.score).sum) - if (cellsNeeded.isEmpty) { - return - } - for (c <- cellsNeeded) { - require(c.uniqId == uniqId) - if (c.noAnyValue) { - def processZonking = { - val aliveP = c.zonkingPropagators.filter(_.alive) - c.zonkingPropagators = aliveP - val zonking = aliveP.sortBy(x => -x.store.score) - zonking - } - for (p <- processZonking) { - require(p.uniqId == uniqId) - tickAll - if (c.noAnyValue && p.alive) { - val store = p.store.asInstanceOf[Propagator[Ability]] - if (store.run(using this, more)) { - p.alive = false - didSomething = true - } else { - val on = store.naiveZonk(cellsNeeded)(using this, more) - on match { - case ZonkResult.Done => - p.alive = false - didSomething = true - case ZonkResult.Require(needed) => - val needed1 = needed - .filter(this.noStableValue(_)) - .filterNot(cellsNeeded.contains) - if (needed1.nonEmpty) { - cellsNeeded = cellsNeeded ++ needed1 - didSomething = true - } - case ZonkResult.NotYet => - } - } - } - } - if (tryFallback > 0 && !didSomething) { - for (p <- processZonking) { - require(p.uniqId == uniqId) - tickAll - if (c.noAnyValue && p.alive) { - val store = p.store.asInstanceOf[Propagator[Ability]] - if (store.run(using this, more)) { - p.alive = false - didSomething = true - } else { - val on = - store.naiveFallbackZonk(cellsNeeded)(using this, more) - on match { - case ZonkResult.Done => - p.alive = false - didSomething = true - case ZonkResult.Require(needed) => - val needed1 = needed - .filter(this.noStableValue(_)) - .filterNot(cellsNeeded.contains) - if (needed1.nonEmpty) { - cellsNeeded = cellsNeeded ++ needed1 - didSomething = true - } - case ZonkResult.NotYet => - } - } - } - } - } - } - } - if (tryFallback > 1 && !didSomething) { - for (c <- cellsNeeded) { - if (c.noAnyValue && c.store.default.isDefined) { - fill(c.asInstanceOf[CellId[Any]], c.store.default.get) - didSomething = true - } - } - } - if (!didSomething) { - if (tryFallback > 1) { - throw new IllegalStateException( - s"Cells $cellsNeeded are not covered by any propagator" - ) - } else { - tryFallback = tryFallback + 1 - } - } else { - tryFallback = 0 - } - } - tickAll - } - } -} diff --git a/tyck/src/main/scala/chester/cps/CPSTrans.scala b/tyck/src/main/scala/chester/cps/CPSTrans.scala deleted file mode 100644 index 0e8177ee2..000000000 --- a/tyck/src/main/scala/chester/cps/CPSTrans.scala +++ /dev/null @@ -1,4 +0,0 @@ -package chester.cps - -// placeholder for CPS Transformations -object CPSTrans {} diff --git a/tyck/src/main/scala/chester/erasure/Eraser.scala b/tyck/src/main/scala/chester/erasure/Eraser.scala deleted file mode 100644 index 0769a4267..000000000 --- a/tyck/src/main/scala/chester/erasure/Eraser.scala +++ /dev/null @@ -1,19 +0,0 @@ -package chester.erasure - -import chester.error.* -import chester.syntax.core.* -import chester.tyck.* - -trait Eraser { - def checkAndErase(term: Term, ty: Term, effects: Effects)(using context: ErasureContext, reporter: Reporter[TyckProblem]): Term -} - -case class ErasureContext() - -object EraserImpl extends Eraser { - def checkAndErase(term: Term, ty: Term, effects: Effects)(using context: ErasureContext, reporter: Reporter[TyckProblem]): Term = { - term match { - case _ => ??? - } - } -} diff --git a/tyck/src/main/scala/chester/tyck/Context2.scala b/tyck/src/main/scala/chester/tyck/Context2.scala deleted file mode 100644 index ba08c5a31..000000000 --- a/tyck/src/main/scala/chester/tyck/Context2.scala +++ /dev/null @@ -1,67 +0,0 @@ -package chester.tyck - -import chester.syntax.* -import chester.syntax.core.* -import chester.tyck.BuiltIn.BuiltinItem -import chester.uniqid.* -import chester.utils.propagator.* - -trait ProvideCtx extends ProvideCellId with ElaboraterBase { - - implicit class TyAndValOpsss(ignored: TyAndVal.type) { - def create(ty: Term, value: Term)(using - state: StateAbility[Tyck] - ): TyAndVal = { - new TyAndVal(toTerm(literal(ty)), toTerm(literal(value))) - } - - def create()(using state: StateAbility[Tyck]): TyAndVal = { - new TyAndVal(toTerm(state.addCell(OnceCell[Term]())), toTerm(state.addCell(OnceCell[Term]()))) - } - } - - extension (context: ContextItem) { - def tyId(using state: StateAbility[Tyck]): CellId[Term] = toId(context.ty) - - def tyTerm(using state: StateAbility[Tyck]): Term = toTerm(context.ty) - } - - implicit class ContextItemObject(ignored: ContextItem.type) { - def builtin( - item: BuiltinItem - )(using state: StateAbility[Tyck]): (TyAndVal, ContextItem) = { - val varId = Uniqid.generate[ToplevelV] - val name = ToplevelV(AbsoluteRef(BuiltinModule, item.id), item.ty, varId, None) - val ty1 = state.toId(item.ty) - ( - new TyAndVal(toTerm(ty1), item.value), - new ContextItem(item.id, varId, name, toTerm(ty1)) - ) - } - } - - implicit class TyAndValOps(tyandval: TyAndVal) { - def tyId(using state: StateAbility[Tyck]): CellId[Term] = toId(tyandval.ty) - - def valueId(using state: StateAbility[Tyck]): CellId[Term] = toId(tyandval.value) - - def tyTerm(using state: StateAbility[Tyck]): Term = toTerm(tyandval.ty) - - def valueTerm(using state: StateAbility[Tyck]): Term = toTerm(tyandval.value) - - } - - implicit class LocalCtxOps(ignored: Context.type) { - def default(using state: StateAbility[Tyck]): Context = { - val items = BuiltIn.builtinItems.map(ContextItem.builtin) - val map = items.map(item => item._2.name -> item._2.uniqId).toMap - val contextItems = items.map(item => item._2.uniqId -> item._2).toMap - val knownMap: Map[UniqidOf[ReferenceCall], TyAndVal] = items - .map(item => item._2.uniqId -> item._1) - .toMap - .asInstanceOf[Map[UniqidOf[ReferenceCall], TyAndVal]] - new Context(map, contextItems, knownMap) - } - } - -} diff --git a/tyck/src/main/scala/chester/tyck/Elaborater.scala b/tyck/src/main/scala/chester/tyck/Elaborater.scala deleted file mode 100644 index 19f1b0ecf..000000000 --- a/tyck/src/main/scala/chester/tyck/Elaborater.scala +++ /dev/null @@ -1,363 +0,0 @@ -package chester.tyck - -import chester.error.* -import chester.syntax.concrete.* -import chester.syntax.core.{*, given} -import chester.tyck.* -import chester.utils.* -import chester.utils.propagator.* -import chester.syntax.* -import chester.tyck.api.{NoopSemanticCollector, SemanticCollector, UnusedVariableWarningWrapper} -import scala.language.implicitConversions -import scala.util.boundary -import scala.util.boundary.break - -trait Elaborater extends ProvideCtx with TyckPropagator { - - def checkType(expr: Expr)(using - localCtx: Context, - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): Term = { - // Create a new type cell representing the kind Typeω (the type of types) - val kindType = literal(Typeω: Term) - - elab(expr, kindType, toEffectsCell(Effects.Empty)) - } - - def checkTypeId(expr: Expr)(using - localCtx: Context, - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): CellId[Term] = { - toId(checkType(expr)) - } - - def elabTy(expr: Option[Expr])(using - localCtx: Context, - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): Term = - expr match { - case Some(expr) => checkType(expr) - case None => Meta(newType) - } - - def elab(expr: Expr, ty: CellIdOr[Term], effects: CIdOf[EffectsCell])(using - localCtx: Context, - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): Term - - def elabId(expr: Expr, ty: CellIdOr[Term], effects: CIdOf[EffectsCell])(using - localCtx: Context, - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): CellId[Term] = { - val term = elab(expr, ty, effects) - toId(term) - } -} - -trait ProvideElaborater extends ProvideCtx with Elaborater with ElaboraterFunction with ElaboraterFunctionCall with ElaboraterBlock { - - // TODO: add something for implicit conversion - - def newSubtype(ty: CellIdOr[Term], cause: Expr)(using - localCtx: Context, - ck: Tyck, - state: StateAbility[Tyck] - ): CellId[Term] = { - val cell = newType - state.addPropagator(Unify(toId(ty), cell, cause)) - cell - } - - /** ty is lhs */ - override def elab( - expr: Expr, - ty0: CellIdOr[Term], - effects: CIdOf[EffectsCell] - )(using - localCtx: Context, - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): Term = toTerm { - val ty = toId(readMetaVar(toTerm(ty0))) - resolve(expr) match { - case expr @ Identifier(name, _) => { - localCtx.get(name) match { - case Some(c: ContextItem) => { - if (c.reference.isDefined) { - c.reference.get.referencedOn(expr) - } - state.addPropagator(Unify(ty, c.tyId, expr)) - c.ref - } - case None => { - // Check if 'name' refers to an object definition - localCtx.getTypeDefinition(name) match { - case Some(objectDef: ObjectStmtTerm) => - val objectCallTerm = ObjectCallTerm(objectDef, convertMeta(expr.meta)) - unify(ty, ObjectTypeTerm(objectDef, convertMeta(expr.meta)), expr) - objectCallTerm - case Some(recordDef: RecordStmtTerm) => - val recordCallTerm = RecordCallTerm(recordDef, TelescopeTerm(Vector(), meta = None), convertMeta(expr.meta)) // TODO - unify(ty, Type0, expr) // TODO: Type - recordCallTerm - case Some(todo) => ??? - case None => - val problem = UnboundVariable(name, expr) - ck.reporter.apply(problem) - ErrorTerm(problem, convertMeta(expr.meta)) - } - } - } - } - case expr @ IntegerLiteral(value, meta) => { - state.addPropagator(LiteralType(expr, ty)) - AbstractIntTerm_.from(value, convertMeta(meta)) - } - case expr @ RationalLiteral(value, meta) => { - state.addPropagator(LiteralType(expr, ty)) - RationalTerm(value, convertMeta(meta)) - } - case expr @ StringLiteral(value, meta) => { - state.addPropagator(LiteralType(expr, ty)) - StringTerm(value, convertMeta(meta)) - } - case expr @ SymbolLiteral(value, meta) => { - state.addPropagator(LiteralType(expr, ty)) - SymbolTerm(value, convertMeta(meta)) - } - case expr @ UnitExpr(meta) => { - unify(ty, UnitType(convertMeta(meta)), expr) - UnitTerm_(convertMeta(meta)) - } - case expr @ ListExpr(terms, meta) => { - val t = newType - // Relate the list type 'ty' to 'ListType(t)' - state.addPropagator(ListOf(t, ty, expr)) - - // For each term, check it with its own type variable and collect the results - val termResults = terms.map { term => - val elemTy = newType - val wellTypedTerm = elab(term, elemTy, effects) - (wellTypedTerm, elemTy) - } - - // Collect the types of the elements - val elemTypes = termResults.map(_._2).toVector - - // Ensure that 't' is the union of the element types - if (elemTypes.nonEmpty) state.addPropagator(UnionOf(t, elemTypes, expr)) - - ListTerm(termResults.map(_._1), convertMeta(meta)) - } - case expr @ TypeAnotationNoEffects(innerExpr, tyExpr, _) => - // Check the type annotation expression to get its type - val declaredTyTerm = checkType(tyExpr) - - unify(ty, declaredTyTerm, expr) - - elab(innerExpr, declaredTyTerm, effects) - case expr: FunctionExpr => elabFunction(expr, ty, effects) - case expr: Block => elabBlock(expr, ty, effects) - case expr: DesaltFunctionCall => elabFunctionCall(expr, ty, effects) - case expr @ ObjectExpr(fields, _) => - elabObjectExpr(expr, fields, ty, effects) - case expr @ DotCall(recordExpr, fieldExpr, telescopes, meta) => - if (telescopes.nonEmpty) { - val problem = NotImplementedFeature("Field access with arguments is not yet supported", expr) - ck.reporter.apply(problem) - ErrorTerm(problem, convertMeta(expr.meta)) - } else { - fieldExpr match { - case Identifier(fieldName, _) => - val recordTy = newType - val recordTerm = elab(recordExpr, recordTy, effects) - val resultTerm = FieldAccessTerm(recordTerm, fieldName, toTerm(ty), convertMeta(meta)) - state.addPropagator(RecordFieldPropagator(recordTy, fieldName, ty, expr)) - resultTerm - case _ => - val problem = InvalidFieldName(fieldExpr) - ck.reporter.apply(problem) - ErrorTerm(problem, convertMeta(expr.meta)) - } - } - case expr: Expr => { - val problem = NotImplemented(expr) - ck.reporter.apply(problem) - ErrorTerm(problem, convertMeta(expr.meta)) - } - } - } - - // TODO: untested - def elabObjectExpr( - expr: ObjectExpr, - fields: Vector[ObjectClause], - ty: CellId[Term], - effects: CIdOf[EffectsCell] - )(using - localCtx: Context, - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): Term = { - // Create collections to store field keys and types - val fieldTypeVars = scala.collection.mutable.Map[Term, CellId[Term]]() - val elaboratedFields = fields.flatMap { - case ObjectExprClauseOnValue(keyExpr, valueExpr) => - // Elaborate the key and value expressions - val elaboratedKey = elab(keyExpr, newType, effects) - val fieldType = newType - val elaboratedValue = elab(valueExpr, fieldType, effects) - val _ = fieldTypeVars.put(elaboratedKey, fieldType) - Some(ObjectClauseValueTerm(elaboratedKey, elaboratedValue, convertMeta(expr.meta))) - // Handle other possible clauses - case _ => ??? - } - - // Construct the object term with elaborated fields - val objectTerm = ObjectTerm(elaboratedFields, convertMeta(expr.meta)) - - // Construct the expected object type - val expectedObjectType = ObjectType( - elaboratedFields.map { case ObjectClauseValueTerm(keyTerm, _, _) => - ObjectClauseValueTerm(keyTerm, Meta(fieldTypeVars(keyTerm)), convertMeta(expr.meta)) - }, - meta = convertMeta(expr.meta) - ) - - // Unify the expected type with the object's type - unify(ty, expectedObjectType, expr) - - objectTerm - } -} - -trait DefaultImpl - extends ProvideElaborater - with ProvideImpl - with ProvideElaboraterFunction - with ProvideElaboraterFunctionCall - with ProvideElaboraterBlock { - - def check( - expr: Expr, - ty: Option[Term] = None, - effects: Option[Effects] = None, - sementicCollector: SemanticCollector = NoopSemanticCollector - ): TyckResult[Unit, Judge] = { - implicit val collecter: UnusedVariableWarningWrapper = - new UnusedVariableWarningWrapper(sementicCollector) - val reporter = new VectorReporter[TyckProblem] - implicit val get: Tyck = new Get(reporter, new MutBox(())) - implicit val able: StateAbility[Tyck] = stateAbilityImpl - val ty1: CellId[Term] = ty match { - case Some(ty) => { - val cell = literal[Term](ty) - cell - } - case None => { - val cell = newType - cell - } - } - val effects1: CIdOf[EffectsCell] = effects match { - case Some(effects) => { - val cell = toEffectsCell(effects) - cell - } - case None => { - newEffects - } - } - implicit val ctx: Context = Context.default - val wellTyped = elabId(expr, ty1, effects1) - able.naiveZonk(Vector(ty1, effects1, wellTyped)) - val judge = Judge( - able.readStable(wellTyped).get, - able.readStable(ty1).get, - able.readUnstable(effects1).get - ) - val finalJudge = finalizeJudge(judge) - - TyckResult0((), finalJudge, reporter.getReports) - - } - - def finalizeJudge( - judge0: Judge - )(using - ck: Tyck, - able: StateAbility[Tyck], - recording: SemanticCollector, - reporter: Reporter[TyckProblem] - ): Judge = { - var judge = judge0 - boundary { - while (true) { - val metas = judge.collectMeta - if (metas.isEmpty) break() - able.naiveZonk(metas.map(x => x.unsafeRead[CellId[Term]])) - judge = judge.replaceMeta(x => able.readUnstable(x.unsafeRead[CellId[Term]]).get) - } - } - recording.metaFinished(x => able.readUnstable(x.unsafeRead[CellId[Term]]).get) - judge - } - - def checkTop( - fileName: String, - expr: Expr, - reporter0: Reporter[Problem], - sementicCollector: SemanticCollector = NoopSemanticCollector, - loadedModules: LoadedModules = LoadedModules.Empty - ): chester.syntax.TAST = { - implicit val collecter: UnusedVariableWarningWrapper = - new UnusedVariableWarningWrapper(sementicCollector) - implicit val reporter: ReporterTrackError[Problem] = new ReporterTrackError( - reporter0 - ) - implicit val get: Tyck = new Get(reporter, new MutBox(())) - implicit val able: StateAbility[Tyck] = stateAbilityImpl - implicit var ctx: Context = Context.default.copy(loadedModules = loadedModules) - val (module, block): (ModuleRef, Block) = resolve(expr) match { - case b @ Block(head +: heads, tail, _) => - resolve(head) match { - case ModuleStmt(module, meta) => (module, Block(heads, tail, meta)) - case _ => (DefaultModule, b) - } - case expr => (DefaultModule, Block(Vector(), Some(expr), expr.meta)) - } - ctx = ctx.updateModule(module) - val ty = newType - val effects = newEffects - val wellTyped = elabBlock(block, ty, effects) - able.naiveZonk(Vector(ty, effects)) - val judge = - Judge(wellTyped, able.readStable(ty).get, able.readUnstable(effects).get) - val finalJudge = finalizeJudge(judge) - - TAST( - fileName = fileName, - module = module, - ast = finalJudge.wellTyped.asInstanceOf[BlockTerm], - ty = finalJudge.ty, - effects = finalJudge.effects, - problems = reporter.getSeverityMap - ) - } -} - -object Tycker extends DefaultImpl with ProvideMutable {} - -export Tycker.{check, checkTop} diff --git a/tyck/src/main/scala/chester/tyck/ElaboraterBase.scala b/tyck/src/main/scala/chester/tyck/ElaboraterBase.scala deleted file mode 100644 index 11272c6ff..000000000 --- a/tyck/src/main/scala/chester/tyck/ElaboraterBase.scala +++ /dev/null @@ -1,87 +0,0 @@ -package chester.tyck - -import chester.syntax.Name -import chester.syntax.concrete.* -import chester.syntax.core.* -import chester.uniqid.* -import chester.utils.* -import chester.utils.propagator.CommonPropagator - -trait ElaboraterBase extends CommonPropagator[Tyck] { - - object Meta { - def rec(x: CellId[Term], default: Term)(using - state: StateAbility[Tyck] - ): Term = { - state.readStable(x) match { - case Some(x) => x - case None => default - } - } - - def apply[T <: Term](x: CellId[T])(using state: StateAbility[Tyck]): T | MetaTerm = { - state.readUnstable(x) match { - case Some(x @ Meta(id)) => rec(id, x).asInstanceOf[T | MetaTerm] - case Some(x) => x - case None => MetaTerm(HoldNotReadable(x), meta = None) - } - } - - def unapply( - x: Term - )(using state: StateAbility[Tyck]): Option[CellId[Term]] = x match { - case m: MetaTerm => { - var result: CellId[Term] = m.unsafeRead[CellId[Term]] - while (true) { - state.readUnstable(result) match { - case Some(m: MetaTerm) => result = m.unsafeRead[CellId[Term]] - case _ => return Some(result) - } - } - throw new IllegalStateException("Unreachable") - } - case _ => None - } - } - - def newLocalv( - name: Name, - ty: CellIdOr[Term], - id: UniqidOf[LocalV], - meta: Option[ExprMeta] - )(using ck: Tyck, state: StateAbility[Tyck]): LocalV = { - val m = convertMeta(meta) - LocalV(name, toTerm(ty), id, m) - } - - def toTerm[T <: Term](x: CellIdOr[T])(using state: StateAbility[Tyck]): T | MetaTerm = x match { - case x: Term => - x match { - case Meta(x) => Meta(x).asInstanceOf[T | MetaTerm] - case x => x.asInstanceOf[T | MetaTerm] - } - case x => Meta(x.asInstanceOf[CellId[Term]]).asInstanceOf[T | MetaTerm] - } - - def toId[T <: Term]( - x: CellIdOr[T] - )(using state: StateAbility[Tyck]): CellId[T] = x match { - case Meta(id) => id.asInstanceOf[CellId[T]] - case x => state.toId(x) - } - - def merge(a: CellIdOr[Term], b: CellIdOr[Term])(using - state: StateAbility[Tyck], - ab: Tyck - ): Unit = { - if (a == b) return - val t1 = toTerm(a) - val t2 = toTerm(b) - if (a == b) return - (t1, t2) match { - case (Meta(t1), t2) => state.fill(t1, t2) - case (t1, Meta(t2)) => state.fill(t2, t1) - case _ => ??? - } - } -} diff --git a/tyck/src/main/scala/chester/tyck/ElaboraterBlock.scala b/tyck/src/main/scala/chester/tyck/ElaboraterBlock.scala deleted file mode 100644 index 7333d7e1b..000000000 --- a/tyck/src/main/scala/chester/tyck/ElaboraterBlock.scala +++ /dev/null @@ -1,452 +0,0 @@ -package chester.tyck - -import cats.implicits.* -import chester.tyck.* -import chester.utils.* -import chester.syntax.* -import scala.language.implicitConversions -import chester.error.* -import chester.syntax.concrete.* -import chester.syntax.core.* -import chester.tyck.api.SemanticCollector -import chester.uniqid.* - -trait ElaboraterBlock extends Elaborater { - // Sealed trait for declaration information, for forwarding references - sealed trait DeclarationInfo { - def expr: Expr - def name: Name - } - - // Case class for 'def' declarations - case class DefDeclaration( - expr: LetDefStmt, - localv: LocalV, - tyAndVal: TyAndVal, - item: ContextItem - ) extends DeclarationInfo { - def name: Name = item.name - } - - // Case class for 'record' declarations - case class RecordDeclaration( - expr: RecordStmt, - uniqId: UniqidOf[RecordStmtTerm], - name: Name - ) extends DeclarationInfo - - // New declarations for trait and interface - case class TraitDeclaration( - expr: TraitStmt, - uniqId: UniqidOf[TraitStmtTerm], - name: Name - ) extends DeclarationInfo - - case class InterfaceDeclaration( - expr: InterfaceStmt, - uniqId: UniqidOf[InterfaceStmtTerm], - name: Name - ) extends DeclarationInfo - - // Add case class for 'object' declarations - case class ObjectDeclaration( - expr: ObjectStmt, - uniqId: UniqidOf[ObjectStmtTerm], - name: Name - ) extends DeclarationInfo - - def elabBlock(expr: Block, ty0: CellIdOr[Term], effects: CIdOf[EffectsCell])(using - localCtx: Context, - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): BlockTerm -} - -trait ProvideElaboraterBlock extends ElaboraterBlock { - def elabBlock(expr: Block, ty0: CellIdOr[Term], effects: CIdOf[EffectsCell])(using - localCtx: Context, - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): BlockTerm = { - val ty = toId(readMetaVar(toTerm(ty0))) - val Block(heads0, tail, meta) = expr - val heads = heads0.map(resolve) - - val (declarations, names, initialCtx) = collectDeclarations(heads, meta) - checkForDuplicateNames(names, expr) - - var ctx = initialCtx - val declarationsMap = declarations.map(info => (info.expr, info)).toMap - - val stmts: Vector[StmtTerm] = heads.flatMapOrdered { - case expr: LetDefStmt if expr.kind == LetDefType.Def => - val (stmtTerms, newCtx) = processDefLetDefStmt(expr, ctx, declarationsMap, effects) - ctx = newCtx - stmtTerms - - case expr: RecordStmt => - val (stmtTerms, newCtx) = processRecordStmt(expr, ctx, declarationsMap, effects) - ctx = newCtx - stmtTerms - - // Process trait statements - case expr: TraitStmt => - val (stmtTerms, newCtx) = processTraitStmt(expr, ctx, declarationsMap, effects) - ctx = newCtx - stmtTerms - - // Process interface statements - case expr: InterfaceStmt => - val (stmtTerms, newCtx) = processInterfaceStmt(expr, ctx, declarationsMap, effects) - ctx = newCtx - stmtTerms - - case expr: LetDefStmt if expr.kind == LetDefType.Let => - val (stmtTerms, newCtx) = processLetLetDefStmt(expr, ctx, effects, meta) - ctx = newCtx - stmtTerms - - case importStmt: ImportStmt => - ck.reporter.apply(NotImplemented(importStmt)) - Vector.empty - - // Process object statements - case expr: ObjectStmt => - val (stmtTerms, newCtx) = processObjectStmt(expr, ctx, declarationsMap, effects) - ctx = newCtx - stmtTerms - - case expr => - implicit val localCtx: Context = ctx - val ty = newType - Vector(ExprStmtTerm(elab(expr, ty, effects), Meta(ty), convertMeta(expr.meta))) - } - - // Block is needed for implicit locals, don't remove - { - implicit val localCtx: Context = ctx - val tailExpr = tail.getOrElse(UnitExpr(meta)) - val wellTyped = elab(tailExpr, ty, effects) - BlockTerm(stmts, wellTyped, meta = convertMeta(expr.meta)) - } - } - - def collectDeclarations( - heads: Seq[Expr], - meta: Option[ExprMeta] - )(using - localCtx: Context, - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): (Seq[DeclarationInfo], Seq[Name], Context) = { - - // Collect all declarations in a single pass - val declarations = heads.collect { - // Collect 'def' declarations - case expr: LetDefStmt if expr.kind == LetDefType.Def => - val name = expr.defined match { - // TODO: support other defined patterns - case DefinedPattern(PatternBind(name, _)) => name.name - case _ => ??? - } - val tyAndVal = TyAndVal.create() - val id = Uniqid.generate[LocalV] - val localv = newLocalv(name, tyAndVal.ty, id, meta) - val r = parameter.newSymbol(localv, id, expr, localCtx) - DefDeclaration( - expr, - localv, - tyAndVal, - ContextItem(name, id, localv, tyAndVal.ty, Some(r)) - ) - - // Collect 'record' declarations - case expr: RecordStmt => - val name = expr.name.name - val id = Uniqid.generate[RecordStmtTerm] - RecordDeclaration(expr, id, name) - - // Collect 'trait' declarations - case expr: TraitStmt => - val name = expr.name.name - val id = Uniqid.generate[TraitStmtTerm] - TraitDeclaration(expr, id, name) - - // Collect 'interface' declarations - case expr: InterfaceStmt => - val name = expr.name.name - val id = Uniqid.generate[InterfaceStmtTerm] - InterfaceDeclaration(expr, id, name) - - // Collect 'object' declarations - case expr: ObjectStmt => - val name = expr.name.name - val id = Uniqid.generate[ObjectStmtTerm] - ObjectDeclaration(expr, id, name) - } - - val names = declarations.map(_.name) - - // Collect context items from 'def' declarations - val defContextItems = declarations.collect { case defDecl: DefDeclaration => - defDecl.item - } - val initialCtx = localCtx.add(defContextItems) - - // Return all declarations, names, and the initial context - (declarations, names, initialCtx) - } - - def checkForDuplicateNames(names: Seq[Name], expr: Expr)(using ck: Tyck): Unit = { - if (names.hasDuplication) { - val problem = DuplicateDefinition(expr) - ck.reporter.apply(problem) - } - } - def processDefLetDefStmt( - expr: LetDefStmt, - ctx: Context, - declarationsMap: Map[Expr, DeclarationInfo], - effects: CIdOf[EffectsCell] - )(using - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): (Seq[StmtTerm], Context) = { - implicit val localCtx: Context = ctx - val defInfo = declarationsMap(expr).asInstanceOf[DefDeclaration] - val ty = expr.ty match { - case Some(tyExpr) => - val t = checkTypeId(tyExpr) - merge(t, defInfo.tyAndVal.tyId) - t - case None => defInfo.tyAndVal.ty - } - val wellTyped = elabId(expr.body.get, ty, effects) - merge(defInfo.tyAndVal.valueId, wellTyped) - val newCtx = ctx.knownAdd(defInfo.localv.uniqId, TyAndVal(toTerm(ty), toTerm(wellTyped))) - ( - Vector(DefStmtTerm(defInfo.localv, Meta(wellTyped), toTerm(ty), convertMeta(expr.meta))), - newCtx - ) - } - - def processRecordStmt( - expr: RecordStmt, - ctx: Context, - declarationsMap: Map[Expr, DeclarationInfo], - effects: CIdOf[EffectsCell] - )(using - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): (Seq[StmtTerm], Context) = { - implicit val localCtx: Context = ctx - val recordInfo = declarationsMap(expr).asInstanceOf[RecordDeclaration] - val name = recordInfo.name - - // Extract the fields from the record - val fields = expr.fields - - // Extract the symbol from the extendsClause, if any. TODO: this is a stub only - val _ = expr.extendsClause.map { case clause @ ExtendsClause(superType, _) => - superType.head match { - case Identifier(superName, _) => superName - case _ => - // For now, only handle simple identifiers; report a warning or error if needed - ck.reporter.apply(UnsupportedExtendsType(clause)) - // Return None since we cannot handle complex types yet - return (Seq.empty, ctx) - } - } - - // Elaborate the fields without combining them with any super class fields - val elaboratedFields = fields.map { field => - val fieldType = field.ty match { - case Some(tyExpr) => checkType(tyExpr) - case None => newTypeTerm - } - // Create a FieldTerm representing the field in the record - FieldTerm(field.name.name, fieldType, convertMeta(expr.meta)) - } - - // Elaborate the optional body (if any) - val elaboratedBody = expr.body.map { body => - elabBlock(body, newTypeTerm, effects)(using ctx, parameter, ck, state) - } - - // Construct the RecordStmtTerm that includes the fields and extendsClause - val recordStmtTerm = RecordStmtTerm( - name = name, - uniqId = recordInfo.uniqId, - fields = elaboratedFields, - body = elaboratedBody, - meta = convertMeta(expr.meta) - // We can store the extendsSymbol here if needed in the future - // For now, we add a TODO comment - // TODO: Handle the extendsClause during type checking - ) - - // Update the context with the new record definition - val newCtx = ctx - .addTypeDefinition(recordStmtTerm) - - // Return the statement term and the updated context - (Seq(recordStmtTerm), newCtx) - } - - def processLetLetDefStmt( - expr: LetDefStmt, - ctx: Context, - effects: CIdOf[EffectsCell], - meta: Option[ExprMeta] - )(using - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): (Seq[StmtTerm], Context) = { - implicit val localCtx: Context = ctx - val name = expr.defined match { - // TODO: support other defined patterns - case DefinedPattern(PatternBind(name, _)) => name.name - case _ => ??? - } - val id = Uniqid.generate[LocalV] - val ty = expr.ty match { - case Some(tyExpr) => checkType(tyExpr) - case None => newTypeTerm - } - val localv = newLocalv(name, ty, id, meta) - val r = parameter.newSymbol(localv, id, expr, localCtx) - val wellTyped = elab(expr.body.get, ty, effects) - val newCtx = ctx - .add(ContextItem(name, id, localv, ty, Some(r))) - .knownAdd(id, TyAndVal(ty, wellTyped)) - ( - Vector(LetStmtTerm(localv, wellTyped, ty, convertMeta(expr.meta))), - newCtx - ) - } - - def processTraitStmt( - expr: TraitStmt, - ctx: Context, - declarationsMap: Map[Expr, DeclarationInfo], - effects: CIdOf[EffectsCell] - )(using - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): (Seq[StmtTerm], Context) = { - implicit val localCtx: Context = ctx - val traitInfo = declarationsMap(expr).asInstanceOf[TraitDeclaration] - val name = traitInfo.name - - // TODO: Elaborate the extends clause properly - val elaboratedExtendsClause = expr.extendsClause.map { clause => - checkType(clause) - } - - // Elaborate the optional body (if any) - val elaboratedBody = expr.body.map { body => - elabBlock(body, newTypeTerm, effects)(using ctx, parameter, ck, state) - } - - // Create the TraitStmtTerm - val traitStmtTerm = TraitStmtTerm( - name = name, - uniqId = traitInfo.uniqId, - extendsClause = elaboratedExtendsClause, - body = elaboratedBody, - meta = convertMeta(expr.meta) - ) - - // Update the context with the new trait definition - val newCtx = ctx.addTypeDefinition(traitStmtTerm) - - // Return the statement term and the updated context - (Seq(traitStmtTerm), newCtx) - } - - def processInterfaceStmt( - expr: InterfaceStmt, - ctx: Context, - declarationsMap: Map[Expr, DeclarationInfo], - effects: CIdOf[EffectsCell] - )(using - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): (Seq[StmtTerm], Context) = { - implicit val localCtx: Context = ctx - val interfaceInfo = declarationsMap(expr).asInstanceOf[InterfaceDeclaration] - val name = interfaceInfo.name - - // TODO: Elaborate the extends clause properly - val elaboratedExtendsClause = expr.extendsClause.map { clause => - checkType(clause) - } - - // Elaborate the optional body (if any) - val elaboratedBody = expr.body.map { body => - elabBlock(body, newTypeTerm, effects)(using ctx, parameter, ck, state) - } - - // Create the InterfaceStmtTerm - val interfaceStmtTerm = InterfaceStmtTerm( - name = name, - uniqId = interfaceInfo.uniqId, - extendsClause = elaboratedExtendsClause, - body = elaboratedBody, - meta = convertMeta(expr.meta) - ) - - // Update the context with the new interface definition - val newCtx = ctx.addTypeDefinition(interfaceStmtTerm) - - // Return the statement term and the updated context - (Seq(interfaceStmtTerm), newCtx) - } - - def processObjectStmt( - expr: ObjectStmt, - ctx: Context, - declarationsMap: Map[Expr, DeclarationInfo], - effects: CIdOf[EffectsCell] - )(using - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): (Seq[StmtTerm], Context) = { - implicit val localCtx: Context = ctx - val objectInfo = declarationsMap(expr).asInstanceOf[ObjectDeclaration] - val name = objectInfo.name - - // Elaborate the extends clause if present - val elaboratedExtendsClause = expr.extendsClause.map { clause => - checkType(clause) - } - - // Elaborate the body if present - val elaboratedBody = expr.body.map { body => - elabBlock(body, newTypeTerm, effects)(using ctx, parameter, ck, state) - } - - // Create the ObjectStmtTerm - val objectStmtTerm = ObjectStmtTerm( - name = name, - uniqId = objectInfo.uniqId, - extendsClause = elaboratedExtendsClause, - body = elaboratedBody, - meta = convertMeta(expr.meta) - ) - - val newCtx = ctx.addTypeDefinition(objectStmtTerm) - - // Return the statement term and the updated context - (Seq(objectStmtTerm), newCtx) - } -} diff --git a/tyck/src/main/scala/chester/tyck/ElaboraterCommon.scala b/tyck/src/main/scala/chester/tyck/ElaboraterCommon.scala deleted file mode 100644 index 440557042..000000000 --- a/tyck/src/main/scala/chester/tyck/ElaboraterCommon.scala +++ /dev/null @@ -1,163 +0,0 @@ -package chester.tyck - -import chester.error.* -import chester.resolve.{SimpleDesalt, resolveOpSeq} -import chester.syntax.concrete.* -import chester.syntax.core.* -import chester.utils.* -import chester.utils.propagator.CommonPropagator - -trait ElaboraterCommon extends ProvideCtx with ElaboraterBase with CommonPropagator[Tyck] { - - trait EffectsCell extends Cell[Effects] { - def requireEffect( - effect: Term - )(using ck: Tyck, state: StateAbility[Tyck]): LocalV = { - ??? - } - } - - def toEffectsM( - x: CellIdOr[Effects] - )(using state: StateAbility[Tyck]): EffectsM = x match { - case x: Effects => x - case x: EffectsCell => Meta(x.asInstanceOf[CellId[Effects]]) - case _ => unreachable() - } - - def toEffectsCell( - x: EffectsM - )(using state: StateAbility[Tyck]): CIdOf[EffectsCell] = x match { - case x: Effects => state.addCell(FixedEffectsCell(x)) - case Meta(x) => x.asInstanceOf[CIdOf[EffectsCell]] - case _ => unreachable() - } - - case class DynamicEffectsCell(effects: Map[LocalV, Term] = Map()) - extends BaseMapCell[LocalV, Term] - with EffectsCell - with UnstableCell[Effects] - with NoFill[Effects] { - override def add(key: LocalV, value: Term): DynamicEffectsCell = { - require(!effects.contains(key)) - copy(effects = effects.updated(key, value)) - } - - override def readUnstable: Option[Effects] = Some(Effects(effects, None)) - } - - case class FixedEffectsCell(effects: Effects) extends EffectsCell with NoFill[Effects] { - override def readStable: Option[Effects] = Some(effects) - } - - def resolve( - expr: Expr - )(using localCtx: Context, reporter: Reporter[TyckProblem]): Expr = { - val result = SimpleDesalt.desugarUnwrap(expr) match { - case opseq: OpSeq => { - val result = resolveOpSeq(reporter, localCtx.operators, opseq) - result - } - case default => default - } - reuse(expr, result) - } - - def newMeta(using ck: Tyck, state: StateAbility[Tyck]): CellId[Term] = { - val cell = state.addCell(OnceCell[Term]()) - cell - } - - def newType(using ck: Tyck, state: StateAbility[Tyck]): CellId[Term] = { - val cell = state.addCell(OnceCell[Term](default = Some(AnyType0))) - cell - } - - def newTypeTerm(using ck: Tyck, state: StateAbility[Tyck]): Term = { - Meta(newType) - } - - def newEffects(using - ck: Tyck, - state: StateAbility[Tyck] - ): CIdOf[EffectsCell] = { - val cell = state.addCell(DynamicEffectsCell()) - cell - } - - def newEffectsTerm(using ck: Tyck, state: StateAbility[Tyck]): Effects | MetaTerm = { - Meta(newEffects) - } - - def readVar( - x: Term - )(using localCtx: Context, ck: Tyck, state: StateAbility[Tyck]): Term = { - var result = x - while (true) { - result match { - case varCall: ReferenceCall => - localCtx.getKnown(varCall) match { - case Some(tyAndVal) => - result = state.readStable(tyAndVal.valueId).getOrElse { - return result - } - case None => return result - } - case _ => return result - } - } - result - } - - def readMetaVar( - x: Term - )(using localCtx: Context, ck: Tyck, state: StateAbility[Tyck]): Term = { - var result = x - while (true) { - result match { - case varCall: ReferenceCall => - localCtx.getKnown(varCall) match { - case Some(tyAndVal) => - result = state.readStable(tyAndVal.valueId).getOrElse { - return result - } - case None => return result - } - case Meta(id) => - state.readStable(id) match { - case Some(x) => result = x - case None => return result - } - case _ => return result - } - } - result - } - - class MutableContext(var ctx: Context) { - def update(f: Context => Context): Unit = { - ctx = f(ctx) - } - } - - given mutL(using m: MutableContext): Context = m.ctx - implicit def mutLc(m: MutableContext): Context = m.ctx - - def generateImplicitArg( - paramTy: Term, - cause: Expr - )(using - ctx: Context, - state: StateAbility[Tyck], - ck: Tyck - ): Term = { - // TODO: Implement logic to generate an implicit argument based on the parameter type. - // This could involve looking up available implicit values in the context. - // For now, create a new meta-term as a placeholder. - val argTerm = newMeta - // Report a warning or note that an implicit argument is not provided explicitly. - ck.reporter(MissingImplicitArgumentWarning(paramTy, cause)) - toTerm(argTerm) - } - -} diff --git a/tyck/src/main/scala/chester/tyck/ElaboraterFunction.scala b/tyck/src/main/scala/chester/tyck/ElaboraterFunction.scala deleted file mode 100644 index 239d809a4..000000000 --- a/tyck/src/main/scala/chester/tyck/ElaboraterFunction.scala +++ /dev/null @@ -1,169 +0,0 @@ -package chester.tyck - -import chester.syntax.concrete.* -import chester.syntax.core.* -import chester.tyck.api.SemanticCollector -import chester.error.* -import chester.uniqid.* - -trait ElaboraterFunction extends ProvideCtx with Elaborater { - def elabFunction( - expr: FunctionExpr, - ty: CellId[Term], - outerEffects: CIdOf[EffectsCell] - )(using - ctx: Context, - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): Term -} - -trait ProvideElaboraterFunction extends ElaboraterFunction { - // Flag to enable or disable termination checking - val terminationCheckEnabled: Boolean = true // Set to false to disable termination checking - - def elabArg(arg: Arg, effects: CIdOf[EffectsCell])(using - localCtx: MutableContext, - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): ArgTerm = { - require(arg.decorations.isEmpty, "decorations are not supported yet") - val ty = elabTy(arg.ty) - val default = arg.exprOrDefault.map(elab(_, ty, effects)) - val id = Uniqid.generate[LocalV] - val bind = newLocalv(arg.name.name, ty, id, arg.meta) - val r = parameter.newSymbol(bind, id, arg, localCtx) - localCtx.update(_.add(ContextItem(arg.name.name, id, bind, ty, Some(r)))) - default match { - case Some(defaultValue) => - ArgTerm(bind, ty, Some(defaultValue), arg.vararg, meta = None) - case None => - ArgTerm(bind, ty, None, arg.vararg, meta = None) - } - } - - def elabTelescope(telescope: DefTelescope, effects: CIdOf[EffectsCell])(using - localCtx: MutableContext, - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): TelescopeTerm = { - // Process each argument in the telescope, updating the context - val argTerms = telescope.args.map { arg => - elabArg(arg, effects) - } - - TelescopeTerm(argTerms, telescope.implicitly, meta = None) - } - - def elabFunction( - expr: FunctionExpr, - ty: CellId[Term], - outerEffects: CIdOf[EffectsCell] - )(using - ctx: Context, - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): Term = { - // Start with a mutable local context based on the current context - val mutableCtx = new MutableContext(ctx) - - val effects = newEffects - - // Elaborate each telescope and collect TelescopeTerms - val telescopeTerms: Vector[TelescopeTerm] = expr.telescope.map { telescope => - elabTelescope(telescope, effects)(using - mutableCtx, - parameter, - ck, - state - ) - } - - // Process the return type, if provided - val returnType: Term = expr.resultTy match { - case Some(rtExpr) => - checkType(rtExpr)(using mutableCtx.ctx, parameter, ck, state) - case None => - newTypeTerm(using ck, state) - } - - // Process the body of the function using the updated context - val bodyTerm: Term = elab(expr.body, returnType, effects)(using - mutableCtx.ctx, - parameter, - ck, - state - ) - - // Build the function type by folding over the telescopes - val functionType = - FunctionType(telescopeTerms, returnType, effects = toTerm(effects), meta = None) - - // Unify the expected type with the constructed function type - unify(ty, functionType, expr) - - // Extract the function name from 'expr.name', if available - val functionNameOpt: Option[String] = None // TODO // placeholder - - // Termination check logic (can be easily removed or disabled) - if (terminationCheckEnabled) { - // placeholder - val isTerminating = analyzeTermination(bodyTerm, functionNameOpt) - if (!isTerminating) { - val problem = PotentialNonterminatingFunction(expr) - ck.reporter(problem) - } - } - - Function(functionType, bodyTerm, meta = None) - } - - // placeholder, broken code - // Simple termination analysis by traversing the Term using inspectRecursive - def analyzeTermination( - term: Term, - functionNameOpt: Option[String] - )(using - ck: Tyck, - state: StateAbility[Tyck] - ): Boolean = { - // Collect function calls within the term - val functionCalls = collectFunctionCalls(term) - - functionNameOpt match { - case Some(functionName) => - // If the function calls itself, consider it potentially non-terminating - !functionCalls.contains(functionName) - case None => - // Cannot determine the function name; assume it terminates - true - } - } - - // Helper function to collect function call names from a Term using inspectRecursive - def collectFunctionCalls(term: Term): Set[String] = { - val calls = scala.collection.mutable.Set[String]() - - term.inspectRecursive { t => - t match { - case FCallTerm(function, _, _) => - function match { - // TODO: calls += name - case _ => - // Continue traversing the function term - function.inspectRecursive { - // TODO: calls += name - case _ => - } - } - case _ => - } - } - - calls.toSet - } -} diff --git a/tyck/src/main/scala/chester/tyck/ElaboraterFunctionCall.scala b/tyck/src/main/scala/chester/tyck/ElaboraterFunctionCall.scala deleted file mode 100644 index 6946cd476..000000000 --- a/tyck/src/main/scala/chester/tyck/ElaboraterFunctionCall.scala +++ /dev/null @@ -1,258 +0,0 @@ -package chester.tyck - -import chester.error.* -import chester.syntax.concrete.* -import chester.syntax.core.* -import chester.tyck.api.SemanticCollector - -trait ElaboraterFunctionCall extends ProvideCtx with Elaborater { - def elabFunctionCall( - expr: DesaltFunctionCall, - ty: CellId[Term], - effects: CIdOf[EffectsCell] - )(using - ctx: Context, - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): Term -} - -trait ProvideElaboraterFunctionCall extends ElaboraterFunctionCall { - override def elabFunctionCall( - expr: DesaltFunctionCall, - ty: CellId[Term], - effects: CIdOf[EffectsCell] - )(using - ctx: Context, - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): Term = { - - // Check if the function refers to a record definition - val functionExpr = expr.function - - val resultTerm = functionExpr match { - case Identifier(name, _) => - ctx.getTypeDefinition(name) match { - case Some(recordDef: RecordStmtTerm) => - // Elaborate the arguments - val argTerms = expr.telescopes.flatMap(_.args.map { arg => - elab(arg.expr, newTypeTerm, effects) - }) - val recordCallTerm = RecordConstructorCallTerm(recordDef.name, argTerms, meta = None) - // TODO: Unify the type with the expected type - // unify(ty, recordDefType, expr) - recordCallTerm - case _ => - // Proceed with default elaboration - defaultElabFunctionCall(expr, ty, effects) - } - case _ => - // Proceed with default elaboration for other expressions - defaultElabFunctionCall(expr, ty, effects) - } - - resultTerm - } - - def defaultElabFunctionCall( - expr: DesaltFunctionCall, - ty: CellId[Term], - effects: CIdOf[EffectsCell] - )(using - ctx: Context, - parameter: SemanticCollector, - ck: Tyck, - state: StateAbility[Tyck] - ): Term = { - - // Elaborate the function expression to get its term and type - val functionTy = newType - val functionTerm = elab(expr.function, functionTy, effects) - - // **No need to infer implicit arguments here** - // We will handle implicit arguments during unification - - // Elaborate the arguments in the telescopes - val callings = expr.telescopes.map { telescope => - val callingArgs = telescope.args.map { arg => - val argTy = newTypeTerm - val argTerm = elab(arg.expr, argTy, effects) - CallingArgTerm(argTerm, argTy, arg.name.map(_.name), arg.vararg, meta = None) - } - Calling(callingArgs, telescope.implicitly, meta = None) - } - - // Create a placeholder for the function call term - val functionCallTerm = newMeta - - // Create a new type variable for the function's result type - val resultTy = newType - - // Add a propagator to unify the function type with the arguments and construct the function call term - state.addPropagator( - UnifyFunctionCall( - functionTy, - callings.toVector, - resultTy, - expr, - functionTerm, - functionCallTerm - ) - ) - - // Unify the result type with the expected type - unify(ty, resultTy, expr) - - toTerm(functionCallTerm) - } - - case class UnifyFunctionCall( - functionTy: CellId[Term], - callings: Vector[Calling], - resultTy: CellId[Term], - cause: Expr, - functionTerm: Term, - functionCallTerm: CellId[Term] - )(using localCtx: Context) - extends Propagator[Tyck] { - - override val readingCells: Set[CellIdAny] = Set(functionTy) - override val writingCells: Set[CellIdAny] = Set(resultTy, functionCallTerm) - override val zonkingCells: Set[CellIdAny] = Set(resultTy, functionCallTerm) - - override def run(using state: StateAbility[Tyck], ck: Tyck): Boolean = { - val readFunctionTy = state.readStable(functionTy) - readFunctionTy match { - case Some(FunctionType(telescopes, retTy, _, _)) => - // Unify the telescopes, handling implicit parameters - val adjustedCallings = unifyTelescopes(telescopes, callings, cause) - // Unify the result type - unify(resultTy, retTy, cause) - // Construct the function call term with adjusted callings - val fCallTerm = FCallTerm(functionTerm, adjustedCallings, meta = None) - state.fill(functionCallTerm, fCallTerm) - true - case Some(Meta(id)) => - // If the function type is a meta variable, delay until it is known - state.addPropagator( - UnifyFunctionCall( - id, - callings, - resultTy, - cause, - functionTerm, - functionCallTerm - ) - ) - true - case Some(other) => - // Report a function call unification error - val argTypes = callings.flatMap(_.args.map(_.value)) - ck.reporter(FunctionCallUnificationError(other, argTypes, cause)) - true - case None => - // Function type is not yet known; cannot proceed - false - } - } - - // Unify expected and actual telescopes, handling implicit parameters - def unifyTelescopes( - expected: Vector[TelescopeTerm], - actual: Vector[Calling], - cause: Expr - )(using - state: StateAbility[Tyck], - ck: Tyck - ): Vector[Calling] = { - var actualIndex = 0 - var adjustedCallings: Vector[Calling] = Vector.empty - - expected.foreach { expectedTele => - val hasActual = actualIndex < actual.length - val actualTeleOpt = if (hasActual) Some(actual(actualIndex)) else None - - val matchesProvided = actualTeleOpt match { - case Some(actualTele) => actualTele.implicitly == expectedTele.implicitly - case None => false - } - - if (matchesProvided) { - // Telescopes match; proceed to unify their arguments - val actualTele = actualTeleOpt.get - unifyArgs(expectedTele.args, actualTele.args, cause) - adjustedCallings = adjustedCallings :+ actualTele - actualIndex += 1 - } else { - if (expectedTele.implicitly) { - // Expected implicit telescope not provided; infer arguments - val callingArgs = expectedTele.args.map { argTerm => - // For now, throw an exception - throw new NotImplementedError(s"Implicit parameter with identifier '$argTerm' is not implemented yet.") - } - val calling = Calling(callingArgs, implicitly = true, meta = None) - adjustedCallings = adjustedCallings :+ calling - // Unify the inferred arguments - unifyArgs(expectedTele.args, calling.args, cause) - } else { - // Expected explicit telescope not matched; report error - ck.reporter(FunctionCallArityMismatchError(expected.length, actual.length, cause)) - return adjustedCallings - } - } - } - adjustedCallings - } - - // Unify the arguments of expected and actual telescopes - def unifyArgs( - expectedArgs: Vector[ArgTerm], - actualArgs: Seq[CallingArgTerm], - cause: Expr - )(using - state: StateAbility[Tyck], - ck: Tyck - ): Unit = { - // Check that the number of arguments matches - if (expectedArgs.length != actualArgs.length) { - ck.reporter( - FunctionCallArgumentMismatchError( - expectedArgs.length, - actualArgs.length, - cause - ) - ) - return - } - - // Unify each pair of expected and actual argument types - expectedArgs.zip(actualArgs).foreach { case (expectedArg, actualArg) => - unify(expectedArg.ty, actualArg.ty, cause) - } - } - - override def naiveZonk( - needed: Vector[CellIdAny] - )(using state: StateAbility[Tyck], ck: Tyck): ZonkResult = { - ZonkResult.Require(Vector(functionTy)) - } - } - - // Placeholder for future implicit argument inference implementation - def inferImplicitArguments( - functionTy: CellId[Term], - expr: DesaltFunctionCall - )(using - ctx: Context, - state: StateAbility[Tyck], - ck: Tyck - ): List[Calling] = { - - // TODO: Implement logic to infer implicit arguments based on the function type - // For now, return an empty list as a stub - List.empty - } -} diff --git a/tyck/src/main/scala/chester/tyck/MethodExtractor.scala b/tyck/src/main/scala/chester/tyck/MethodExtractor.scala deleted file mode 100644 index 2bbe79fd6..000000000 --- a/tyck/src/main/scala/chester/tyck/MethodExtractor.scala +++ /dev/null @@ -1,69 +0,0 @@ -package chester.tyck - -import chester.syntax.concrete._ -import chester.syntax.core._ - -trait MethodExtractor { - - // Extract methods from a concrete syntax (Expr) - def extractMethodsFromExpr(expr: Expr): Seq[LetDefStmt] = { - expr match { - case record: RecordStmt => collectMethodsFromBody(record.body) - case traitStmt: TraitStmt => collectMethodsFromBody(traitStmt.body) - case interfaceStmt: InterfaceStmt => collectMethodsFromBody(interfaceStmt.body) - case objectStmt: ObjectStmt => collectMethodsFromBody(objectStmt.body) - case _ => Seq.empty - } - } - - // Extract methods from a core syntax (Term) - def extractMethodsFromTerm(term: Term): Seq[DefStmtTerm] = { - term match { - case record: RecordStmtTerm => collectMethodsFromBodyTerm(record.body) - case traitStmt: TraitStmtTerm => collectMethodsFromBodyTerm(traitStmt.body) - case interfaceStmt: InterfaceStmtTerm => collectMethodsFromBodyTerm(interfaceStmt.body) - case objectStmt: ObjectStmtTerm => collectMethodsFromBodyTerm(objectStmt.body) - case _ => Seq.empty - } - } - - // Helper method to collect methods from Option[Block] - private def collectMethodsFromBody(body: Option[Block]): Seq[LetDefStmt] = { - body.map(collectMethodsFromBlock).getOrElse(Seq.empty) - } - - // Helper method to collect methods from Block - private def collectMethodsFromBlock(block: Block): Seq[LetDefStmt] = { - block.statements.collect { - case letDefStmt: LetDefStmt if letDefStmt.kind == LetDefType.Def => letDefStmt - } ++ block.result.toSeq.flatMap(collectMethodsFromExpr) - } - - // Overloaded method for Term - private def collectMethodsFromBodyTerm(body: Option[BlockTerm]): Seq[DefStmtTerm] = { - body.map(collectMethodsFromBlockTerm).getOrElse(Seq.empty) - } - - private def collectMethodsFromBlockTerm(block: BlockTerm): Seq[DefStmtTerm] = { - block.statements.collect { case defStmtTerm: DefStmtTerm => - defStmtTerm - } ++ collectMethodsFromTerm(block.result) - } - - // Recursive method to collect methods from an Expr - private def collectMethodsFromExpr(expr: Expr): Seq[LetDefStmt] = { - expr match { - case block: Block => collectMethodsFromBlock(block) - case _ => Seq.empty - } - } - - // Recursive method to collect methods from a Term - private def collectMethodsFromTerm(term: Term): Seq[DefStmtTerm] = { - term match { - case block: BlockTerm => collectMethodsFromBlockTerm(block) - case _ => Seq.empty - } - } - -} diff --git a/tyck/src/main/scala/chester/tyck/TyckPropagator.scala b/tyck/src/main/scala/chester/tyck/TyckPropagator.scala deleted file mode 100644 index 0ac245382..000000000 --- a/tyck/src/main/scala/chester/tyck/TyckPropagator.scala +++ /dev/null @@ -1,383 +0,0 @@ -package chester.tyck - -import chester.error.* -import chester.syntax.Name -import chester.syntax.concrete.* -import chester.syntax.core.* -import chester.utils.* - -trait TyckPropagator extends ElaboraterCommon { - - def unify(lhs: Term, rhs: Term, cause: Expr)(using - localCtx: Context, - ck: Tyck, - state: StateAbility[Tyck] - ): Unit = { - if (lhs == rhs) return - val lhsResolved = readVar(lhs) - val rhsResolved = readVar(rhs) - if (lhsResolved == rhsResolved) return - (lhsResolved, rhsResolved) match { - case (Meta(lhs), rhs) => unify(lhs, rhs, cause) - case (lhs, Meta(rhs)) => unify(lhs, rhs, cause) - - // Structural unification for ListType - case (ListType(elem1, _), ListType(elem2, _)) => - unify(elem1, elem2, cause) - - case (Type(LevelUnrestricted(_), _), Type(LevelFinite(_, _), _)) => () - - case (x, Intersection(xs, _)) => - if (xs.exists(tryUnify(x, _))) return - ck.reporter.apply(TypeMismatch(lhs, rhs, cause)) // TODO - - // Structural unification for TupleType - case (TupleType(types1, _), TupleType(types2, _)) if types1.length == types2.length => - types1.zip(types2).foreach { case (t1, t2) => - unify(t1, t2, cause) - } - - // Type levels: unify levels - case (Type(level1, _), Type(level2, _)) => - unify(level1, level2, cause) - - case (LevelFinite(_, _), LevelUnrestricted(_)) => () - - case (Union(_, _), Union(_, _)) => ??? - - // Base case: types do not match - case _ => - ck.reporter.apply(TypeMismatch(lhs, rhs, cause)) - - } - } - - def unify(t1: Term, t2: CellId[Term], cause: Expr)(using - localCtx: Context, - ck: Tyck, - state: StateAbility[Tyck] - ): Unit = { - state.addPropagator(Unify(literal(t1), t2, cause)) - } - - def unify(t1: CellId[Term], t2: Term, cause: Expr)(using - localCtx: Context, - ck: Tyck, - state: StateAbility[Tyck] - ): Unit = { - state.addPropagator(Unify(t1, literal(t2), cause)) - } - - def unify(t1: CellId[Term], t2: CellId[Term], cause: Expr)(using - localCtx: Context, - ck: Tyck, - state: StateAbility[Tyck] - ): Unit = { - state.addPropagator(Unify(t1, t2, cause)) - } - - type Literals = Expr & (IntegerLiteral | RationalLiteral | StringLiteral | SymbolLiteral) - - case class Unify(lhs: CellId[Term], rhs: CellId[Term], cause: Expr)(using - localCtx: Context - ) extends Propagator[Tyck] { - override val readingCells: Set[CIdOf[Cell[?]]] = Set(lhs, rhs) - override val writingCells: Set[CIdOf[Cell[?]]] = Set(lhs, rhs) - override val zonkingCells: Set[CIdOf[Cell[?]]] = Set(lhs, rhs) - - override def run(using state: StateAbility[Tyck], more: Tyck): Boolean = { - val lhs = state.readStable(this.lhs) - val rhs = state.readStable(this.rhs) - if (lhs.isDefined && rhs.isDefined) { - unify(lhs.get, rhs.get, cause) - return true - } - (lhs, rhs) match { - case (Some(Meta(lhs)), _) => { - state.addPropagator(Unify(lhs, this.rhs, cause)) - return true - } - case (_, Some(Meta(rhs))) => { - state.addPropagator(Unify(this.lhs, rhs, cause)) - return true - } - case _ => () - } - return false - } - - override def naiveZonk( - needed: Vector[CellIdAny] - )(using state: StateAbility[Tyck], more: Tyck): ZonkResult = { - val lhs = state.readStable(this.lhs) - val rhs = state.readStable(this.rhs) - (lhs, rhs) match { - case (Some(lhs), Some(rhs)) if lhs == rhs => return ZonkResult.Done - case (Some(lhs), None) => { - state.fill(this.rhs, lhs) - return ZonkResult.Done - } - case (None, Some(rhs)) => { - state.fill(this.lhs, rhs) - return ZonkResult.Done - } - case _ => return ZonkResult.Require(Vector(this.lhs, this.rhs)) - } - } - } - - case class UnionOf( - lhs: CellId[Term], - rhs: Vector[CellId[Term]], - cause: Expr - )(using localCtx: Context) - extends Propagator[Tyck] { - override val readingCells: Set[CIdOf[Cell[?]]] = Set(lhs) ++ rhs.toSet - override val writingCells: Set[CIdOf[Cell[?]]] = Set(lhs) - override val zonkingCells: Set[CIdOf[Cell[?]]] = Set(lhs) ++ rhs.toSet - - override def run(using state: StateAbility[Tyck], more: Tyck): Boolean = { - val lhsValueOpt = state.readStable(lhs) - val rhsValuesOpt = rhs.map(state.readStable) - - if (lhsValueOpt.isDefined && rhsValuesOpt.forall(_.isDefined)) { - val lhsValue = lhsValueOpt.get - val rhsValues = rhsValuesOpt.map(_.get) - - // Check that each rhsValue is assignable to lhsValue - val assignable = rhsValues.forall { rhsValue => - unify(lhsValue, rhsValue, cause) - true // Assuming unify reports errors internally - } - assignable - } else { - // Not all values are available yet - false - } - } - - override def naiveZonk( - needed: Vector[CellIdAny] - )(using state: StateAbility[Tyck], more: Tyck): ZonkResult = { - val lhsValueOpt = state.readStable(lhs) - val rhsValuesOpt = rhs.map(state.readStable) - - val unknownRhs = rhs.zip(rhsValuesOpt).collect { case (id, None) => id } - if (unknownRhs.nonEmpty) { - // Wait for all rhs values to be known - ZonkResult.Require(unknownRhs.toVector) - } else { - val rhsValues = rhsValuesOpt.map(_.get) - - lhsValueOpt match { - case Some(lhsValue) => - // LHS is known, unify each RHS with LHS - rhsValues.foreach { rhsValue => - unify(lhsValue, rhsValue, cause) - } - ZonkResult.Done - case None => - // LHS is unknown, create UnionType from RHS values and set LHS - val unionType = Union_.from(rhsValues.assumeNonEmpty) - state.fill(lhs, unionType) - ZonkResult.Done - } - } - } - } - - def tryUnify(lhs: Term, rhs: Term)(using - state: StateAbility[Tyck], - localCtx: Context - ): Boolean = { - if (lhs == rhs) return true - val lhsResolved = lhs match { - case varCall: ReferenceCall => - localCtx.getKnown(varCall) match { - case Some(tyAndVal) => - state.readStable(tyAndVal.valueId).getOrElse(lhs) - case None => lhs - } - case _ => lhs - } - val rhsResolved = rhs match { - case varCall: ReferenceCall => - localCtx.getKnown(varCall) match { - case Some(tyAndVal) => - state.readStable(tyAndVal.valueId).getOrElse(rhs) - case None => rhs - } - case _ => rhs - } - if (lhsResolved == rhsResolved) return true - - (lhsResolved, rhsResolved) match { - case (Type(level1, _), Type(level2, _)) => - level1 == level2 - - case (ListType(elem1, _), ListType(elem2, _)) => - tryUnify(elem1, elem2) - - case (Union(_, _), Union(_, _)) => ??? - - case (Intersection(_, _), Intersection(_, _)) => ??? - - case _ => false - } - } - - case class LiteralType(x: Literals, tyLhs: CellId[Term])(using - localCtx: Context - ) extends Propagator[Tyck] { - override val readingCells: Set[CIdOf[Cell[?]]] = Set(tyLhs) - override val writingCells: Set[CIdOf[Cell[?]]] = Set(tyLhs) - override val zonkingCells: Set[CIdOf[Cell[?]]] = Set(tyLhs) - - override def run(using state: StateAbility[Tyck], more: Tyck): Boolean = { - if (state.noStableValue(tyLhs)) return false - val ty_ = state.readStable(this.tyLhs).get - ty_ match { - case Meta(ty) => { - state.addPropagator(LiteralType(x, ty)) - return true - } - case _ => () - } - x match { - case IntegerLiteral(value, _) => { - if (value.isValidInt && tryUnify(ty_, IntType(None))) return true - if (value > 0 && tryUnify(ty_, NaturalType(None))) return true - val i = Vector(IntegerType(None)) ++ - Vector(NaturalType(None)).filter(x => value > 0) ++ - Vector(IntType(None)).filter(x => value.isValidInt) ++ - Vector(UIntType(None)).filter(x => value > 0 && value.isValidInt) - unify(ty_, Intersection(i.assumeNonEmpty, None), x) - return true - } - case RationalLiteral(_, _) => { - unify(ty_, RationalType(None), x) - return true - } - case StringLiteral(_, _) => { - unify(ty_, StringType(None), x) - - return true - } - case SymbolLiteral(_, _) => { - unify(ty_, SymbolType(None), x) - return true - } - } - } - - override def naiveZonk( - needed: Vector[CellIdAny] - )(using state: StateAbility[Tyck], more: Tyck): ZonkResult = - state.fill( - tyLhs, - x match { - case IntegerLiteral(_, _) => IntegerType(None) - case RationalLiteral(_, _) => RationalType(None) - case StringLiteral(_, _) => StringType(None) - case SymbolLiteral(_, _) => SymbolType(None) - } - ) - ZonkResult.Done - } - - /** t is rhs, listT is lhs */ - case class ListOf(tRhs: CellId[Term], listTLhs: CellId[Term], cause: Expr)(using - ck: Tyck, - localCtx: Context - ) extends Propagator[Tyck] { - override val readingCells: Set[CIdOf[Cell[?]]] = Set(tRhs, listTLhs) - override val writingCells: Set[CIdOf[Cell[?]]] = Set(tRhs, listTLhs) - override val zonkingCells: Set[CIdOf[Cell[?]]] = Set(listTLhs) - - override def run(using state: StateAbility[Tyck], more: Tyck): Boolean = { - val t1 = state.readStable(this.tRhs) - val listT1 = state.readStable(this.listTLhs) - (t1, listT1) match { - case (_, Some(Meta(listTLhs))) => { - state.addPropagator(ListOf(tRhs, listTLhs, cause)) - true - } - case (_, Some(l)) if !l.isInstanceOf[ListType] => { - ck.reporter.apply(TypeMismatch(ListType(AnyType0, meta = None), l, cause)) - true - } - case (Some(t1), Some(ListType(t2, _))) => { - unify(t2, t1, cause) - true - } - case (_, Some(ListType(t2, _))) => { - unify(t2, tRhs, cause) - true - } - case (Some(t1), None) => { - unify(this.listTLhs, ListType(t1, meta = None): Term, cause) - true - } - case (None, None) => { - unify(this.listTLhs, ListType(Meta(tRhs), meta = None): Term, cause) - true - } - } - } - - override def naiveZonk( - needed: Vector[CellIdAny] - )(using state: StateAbility[Tyck], more: Tyck): ZonkResult = { - val t1 = state.readStable(this.tRhs) - val listT1 = state.readStable(this.listTLhs) - if (!t1.isDefined) return ZonkResult.Require(Vector(this.tRhs)) - val ty = t1.get - assert(listT1.isEmpty) - state.fill(this.listTLhs, ListType(ty, meta = None)) - ZonkResult.Done - } - } - - case class RecordFieldPropagator( - recordTy: CellId[Term], - fieldName: Name, - expectedTy: CellId[Term], - cause: Expr - )(using localCtx: Context) - extends Propagator[Tyck] { - override val readingCells: Set[CIdOf[Cell[?]]] = Set(recordTy) - override val writingCells: Set[CIdOf[Cell[?]]] = Set(expectedTy) - override val zonkingCells: Set[CIdOf[Cell[?]]] = Set(recordTy, expectedTy) - - override def run(using state: StateAbility[Tyck], more: Tyck): Boolean = { - state.readStable(recordTy) match { - case Some(Meta(id)) => - state.addPropagator(RecordFieldPropagator(id, fieldName, expectedTy, cause)) - true - case Some(RecordCallTerm(recordDef, _, _)) => - recordDef.fields.find(_.name == fieldName) match { - case Some(fieldTerm) => - unify(expectedTy, fieldTerm.ty, cause) - true - case None => - val problem = FieldNotFound(fieldName, recordDef.name, cause) - more.reporter.apply(problem) - true - } - case Some(other) => - val problem = NotARecordType(other, cause) - more.reporter.apply(problem) - true - case None => false - } - } - - override def naiveZonk(needed: Vector[CellIdAny])(using state: StateAbility[Tyck], more: Tyck): ZonkResult = { - state.readStable(recordTy) match { - case None => ZonkResult.Require(Vector(recordTy)) - case _ => ZonkResult.Done - } - } - } - -}