Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add private and protected in New Parser and Export Them in ts2mls #156

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion shared/src/main/scala/mlscript/NewLexer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,10 @@ object NewLexer {
"interface",
"new",
"namespace",
"type"
"type",
"public",
"private",
"protected"
)

def printToken(tl: TokLoc): Str = tl match {
Expand Down
23 changes: 18 additions & 5 deletions shared/src/main/scala/mlscript/NewParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -291,13 +291,26 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D
R(NuTypeDef(kind, tn, tparams, params, ps, body))

// TODO make `fun` by-name and `let` by-value
case (KEYWORD(kwStr @ ("fun" | "let")), l0) :: c => // TODO support rec?
case (KEYWORD(kwStr @ ("public" | "private" | "protected" | "fun" | "let")), l0) :: c => // TODO support rec?
consume
// TODO: apply access flags in OOP.
val flag = if (kwStr === "private") PrivateFlag
else if (kwStr === "protected") ProtectedFlag
else PublicFlag // if there is no access flag keyword, we consider it as public

val (decKw, wrongKw) = if (kwStr === "fun" || kwStr === "let") (kwStr, false) else yeetSpaces match {
case (KEYWORD(decStr @ ("fun" | "let")), _) :: _ => consume; (decStr, false)
case _ =>
val (tkstr, loc) = c.headOption.fold(("end of input", lastLoc))(_.mapFirst(_.describe).mapSecond(some))
err(msg"Expected keyword let or fun" -> loc :: Nil)
("<error>", true)
}

val isLetRec = yeetSpaces match {
case (KEYWORD("rec"), l1) :: _ if kwStr === "let" =>
case (KEYWORD("rec"), l1) :: _ if decKw === "let" =>
consume
S(true)
case c => if (kwStr === "fun") N else S(false)
case c => if (decKw === "fun") N else S(false)
}
val (v, success) = yeetSpaces match {
case (IDENT(idStr, false), l1) :: _ =>
Expand All @@ -312,8 +325,8 @@ abstract class NewParser(origin: Origin, tokens: Ls[Stroken -> Loc], raiseFun: D
// R(errExpr)
(Var("<error>").withLoc(curLoc.map(_.left)), false)
}
foundErr || !success pipe { implicit fe =>
val tparams = if (kwStr === "let") Ls[TypeName]() else yeetSpaces match {
foundErr || wrongKw || !success pipe { implicit fe =>
val tparams = if (decKw === "let") Ls[TypeName]() else yeetSpaces match {
case (br @ BRACKETS(Angle, toks), loc) :: _ =>
consume
val ts = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.argsMaybeIndented()).map {
Expand Down
5 changes: 5 additions & 0 deletions shared/src/main/scala/mlscript/syntax.scala
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ final case class TypeVar(val identifier: Int \/ Str, nameHint: Opt[Str]) extends

final case class PolyType(targs: Ls[TypeName], body: Type) extends PolyTypeImpl

// Access flags in OOP.
sealed trait AccessFlag
final case object PublicFlag extends AccessFlag
final case object PrivateFlag extends AccessFlag
final case object ProtectedFlag extends AccessFlag

// New Definitions AST

Expand Down
17 changes: 17 additions & 0 deletions shared/src/test/diff/parser/Classes.mls
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,21 @@ class Foo(x, y): Bar(y, x), Baz(x + y)
//│ |#class| |Foo|(|x|,| |y|)|#:| |Bar|(|y|,| |x|)|,| |Baz|(|x| |+| |y|)|
//│ Parsed: {class Foo(x, y,): Bar (y, x,), Baz (+ (x,) (y,),) {}}

class Foo() {
public let a = 42
private fun b = 42
protected let c = 42
}
//│ |#class| |Foo|(||)| |{|→|#public| |#let| |a| |#=| |42|↵|#private| |#fun| |b| |#=| |42|↵|#protected| |#let| |c| |#=| |42|←|↵|}|
//│ Parsed: {class Foo() {let a = 42; fun b = 42; let c = 42}}


class Bar() {
private a = 0
}
//│ |#class| |Bar|(||)| |{|→|#private| |a| |#=| |0|←|↵|}|
//│ ╔══[PARSE ERROR] Expected keyword let or fun
//│ ║ l.132: private a = 0
//│ ╙── ^
//│ Parsed: {class Bar() {let a = 0}}

23 changes: 10 additions & 13 deletions ts2mls/js/src/main/scala/ts2mls/types/Converter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ object Converter {
"false" -> "false"
)

def generateFunDeclaration(tsType: TSType, name: String)(implicit indent: String = ""): String = tsType match {
def generateFunDeclaration(tsType: TSType, name: String, modifier: String = "")(implicit indent: String = ""): String = tsType match {
case TSFunctionType(params, res, typeVars) => {
val pList = if (params.isEmpty) "" else params.map(p => s"${convert(p)("")}").reduceLeft((r, p) => s"$r, $p")
val tpList = if (typeVars.isEmpty) "" else s"<${typeVars.map(p => convert(p)("")).reduceLeft((r, p) => s"$r, $p")}>"
s"${indent}fun $name$tpList($pList): ${convert(res)("")}"
s"${indent}${modifier}fun $name$tpList($pList): ${convert(res)("")}"
}
case overload @ TSIgnoredOverload(base, _) => s"${generateFunDeclaration(base, name)} ${overload.warning}"
case inter: TSIntersectionType => s"${indent}fun ${name}: ${Converter.convert(inter)}"
case inter: TSIntersectionType => s"${indent}${modifier}fun ${name}: ${Converter.convert(inter)}"
case _ => throw new AssertionError("non-function type is not allowed.")
}

Expand Down Expand Up @@ -58,16 +58,13 @@ object Converter {

private def convertRecord(typeName: String, members: Map[String, TSMemberType], typeVars: List[TSTypeParameter],
parents: List[TSType], statics: Map[String, TSMemberType], constructorList: List[TSType])(implicit indent: String) = {
val allRecs = members.toList.map((m) => m._2.modifier match {
case Public =>
if (typeName === "trait ") s"${m._1}: ${convert(m._2)},"
else m._2.base match {
case _: TSFunctionType => s"${generateFunDeclaration(m._2.base, m._1)(indent + " ")}\n"
case _: TSIgnoredOverload => s"${generateFunDeclaration(m._2.base, m._1)(indent + " ")}\n"
case _ => s"${indent} let ${m._1}: ${convert(m._2)}\n"
}
case _ => "" // TODO: deal with private/protected members
}) :::
val allRecs = members.toList.map((m) =>
if (typeName === "trait ") s"${m._1}: ${convert(m._2)},"
else m._2.base match {
case _: TSFunctionType => s"${generateFunDeclaration(m._2.base, m._1, m._2.modifier.toString())(indent + " ")}\n"
case _: TSIgnoredOverload => s"${generateFunDeclaration(m._2.base, m._1, m._2.modifier.toString())(indent + " ")}\n"
case _ => s"${indent} ${m._2.modifier}let ${m._1}: ${convert(m._2)}\n"
}) :::
statics.toList.map((s) => s._2.modifier match {
case Public => s._2.base match {
case _: TSClassType => convert(s._2)(indent + " ") + "\n"
Expand Down
12 changes: 9 additions & 3 deletions ts2mls/js/src/main/scala/ts2mls/types/TSType.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package ts2mls.types

sealed abstract class TSAccessModifier
case object Public extends TSAccessModifier
case object Private extends TSAccessModifier
case object Protected extends TSAccessModifier
case object Public extends TSAccessModifier {
override def toString() = ""
}
case object Private extends TSAccessModifier {
override def toString() = "private " // add a space after the keyword so we can generate `private let...`
}
case object Protected extends TSAccessModifier {
override def toString() = "protected "
}

sealed abstract class TSType
case class TSParameterType(name: String, val tp: TSType) extends TSType // record both parameter's name and parameter's type
Expand Down