Skip to content

Commit

Permalink
Improve handling of symbolic names
Browse files Browse the repository at this point in the history
  • Loading branch information
LPTK committed Jan 11, 2025
1 parent 6d029bb commit 3a31c6e
Show file tree
Hide file tree
Showing 35 changed files with 270 additions and 150 deletions.
2 changes: 2 additions & 0 deletions hkmc2/shared/src/main/scala/hkmc2/bbml/bbML.scala
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,8 @@ class BBTyper(using elState: Elaborator.State, tl: TL, scope: Scope):
goStats(stats)
case Import(sym, pth) :: stats =>
goStats(stats) // TODO:
case stat :: _ =>
TODO(stat)
goStats(stats)
val (ty, eff) = typeCheck(res)
(ty, effBuff.foldLeft(eff)((res, e) => res | e))
Expand Down
6 changes: 3 additions & 3 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/BlockImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package semantics

import mlscript.utils.*, shorthands.*
import syntax.Tree.*
import hkmc2.syntax.{Annotations, TypeOrTermDef}
import hkmc2.syntax.{PossiblyAnnotated, TypeOrTermDef}


trait BlockImpl(using Elaborator.State):
Expand All @@ -14,13 +14,13 @@ trait BlockImpl(using Elaborator.State):
val definedSymbols: Array[Str -> BlockMemberSymbol] =
desugStmts
.flatMap:
case Annotations(_, td: syntax.TypeOrTermDef) =>
case PossiblyAnnotated(_, td: syntax.TypeOrTermDef) =>
td.name match
case L(_) => Nil
case R(id) =>
id.name -> R(td) :: (
td.symbName match
case S(sid: Ident) => id.name -> L(sid.name) :: Nil
case S(R(sid)) => id.name -> L(sid.name) :: Nil
case _ => Nil
)
case _ => Nil
Expand Down
12 changes: 6 additions & 6 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/Desugarer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ class Desugarer(val elaborator: Elaborator)
case blk: Block => blk.desugStmts.foldRight(default): (branch, elabFallback) =>
// Terminology: _fallback_ refers to subsequent branches, _backup_ refers
// to the backup plan passed from the parent split.
branch match
branch.deparenthesized match
case LetLike(`let`, ident @ Ident(_), termTree, N) => backup => ctx =>
termTree match
case S(termTree) =>
Expand Down Expand Up @@ -409,7 +409,7 @@ class Desugarer(val elaborator: Elaborator)
*/
def expandMatch(scrutSymbol: BlockLocalSymbol, pattern: Tree, sequel: Sequel): Split => Sequel =
def ref = scrutSymbol.ref(/* FIXME ident? */)
pattern match
pattern.deparenthesized match
// A single wildcard pattern.
case Under() => _ => ctx => sequel(ctx)
// Alias pattern
Expand Down Expand Up @@ -510,17 +510,17 @@ class Desugarer(val elaborator: Elaborator)
):
Branch(ref, Pattern.Lit(literal), sequel(ctx)) ~: fallback
// A single pattern in conjunction with more conditions
case pattern and consequent => fallback => ctx =>
case pattern and consequent => fallback => ctx =>
val innerSplit = termSplit(consequent, identity)(Split.End)
expandMatch(scrutSymbol, pattern, innerSplit)(fallback)(ctx)
case Jux(Ident(".."), Ident(_)) => fallback => _ =>
raise(ErrorReport(msg"Illgeal rest pattern." -> pattern.toLoc :: Nil))
raise(ErrorReport(msg"Illegal rest pattern." -> pattern.toLoc :: Nil))
fallback
case _ => fallback => _ =>
// Raise an error and discard `sequel`. Use `fallback` instead.
raise(ErrorReport(msg"Unrecognized pattern." -> pattern.toLoc :: Nil))
raise(ErrorReport(msg"Unrecognized pattern (${pattern.describe})" -> pattern.toLoc :: Nil))
fallback

/** Desugar a list of sub-patterns (with their corresponding scrutinees).
* This is called when handling nested patterns. The caller is responsible
* for providing the symbols of scrutinees.
Expand Down
16 changes: 14 additions & 2 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ extends Importer:
def term(tree: Tree, inAppPrefix: Bool = false): Ctxl[Term] =
trace[Term](s"Elab term ${tree.showDbg}", r => s"~> $r"):
tree.desugared match
case Bra(k, e) =>
k match
case BracketKind.Round =>
case _ =>
raise(ErrorReport(msg"Unsupported ${k.name} in this position" -> tree.toLoc :: Nil))
term(e)
case Block(s :: Nil) =>
term(s)
case Block(sts) =>
Expand Down Expand Up @@ -713,6 +719,9 @@ extends Importer:
go(sts, Nil, Term.Error :: acc)
case (td @ TermDef(k, nme, rhs)) :: sts =>
log(s"Processing term definition $nme")
td.symbName match
case S(L(d)) => raise(d)
case _ => ()
td.name match
case R(id) =>
val sym = members.getOrElse(id.name, die)
Expand Down Expand Up @@ -759,11 +768,11 @@ extends Importer:
s match
case N if em => raise:
ErrorReport:
msg"Function returning module values must have explicit return types." ->
msg"Functions returning module values must have explicit return types." ->
td.head.toLoc :: Nil
case S(t) if em && ModuleChecker.isTypeParam(t) => raise:
ErrorReport:
msg"Function returning module values must have concrete return types." ->
msg"Functions returning module values must have concrete return types." ->
td.head.toLoc :: Nil
case S(_) if em && !mm => raise:
ErrorReport:
Expand All @@ -783,6 +792,9 @@ extends Importer:
go(sts, Nil, acc)
case (td @ TypeDef(k, head, extension, body)) :: sts =>
assert((k is Als) || (k is Cls) || (k is Mod) || (k is Obj) || (k is Pat), k)
td.symbName match
case S(L(d)) => raise(d)
case _ => ()
val nme = td.name match
case R(id) => id
case L(d) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class Translator(val elaborator: Elaborator)
pre = s"full <<< $pat",
post = (split: Split) => s"full >>> $split"
):
pat match
pat.deparenthesized match
case lhs or rhs => full(scrut, lhs, inner) ~~: full(scrut, rhs, inner)
case (lo: StrLit) to (incl, hi: StrLit) => if isInvalidStringBounds(lo, hi) then failure else
makeRange(scrut, lo, hi, incl, inner)
Expand All @@ -113,15 +113,15 @@ class Translator(val elaborator: Elaborator)
error(msg"Cannot use this ${ctor.describe} as an extractor" -> ctor.toLoc)
errorSplit
case _ =>
error(msg"Unrecognized pattern." -> pat.toLoc)
error(msg"Unrecognized pattern (${pat.describe})" -> pat.toLoc)
errorSplit

/** Generate a split that consumes the prefix of the scrutinee. */
private def stringPrefix(scrut: Scrut, pat: Tree, inner: PrefixInner)(using Raise): Split = trace(
pre = s"stringPrefix <<< $pat",
post = (split: Split) => s"stringPrefix >>> $split"
):
pat match
pat.deparenthesized match
case lhs or rhs => stringPrefix(scrut, lhs, inner) ~~: stringPrefix(scrut, rhs, inner)
case (lo: StrLit) to (incl, hi: StrLit) => if isInvalidStringBounds(lo, hi) then failure else
val emptyTest = app(eq.ref(), tup(fld(scrut()), fld(str(""))), "test empty")
Expand Down Expand Up @@ -160,7 +160,7 @@ class Translator(val elaborator: Elaborator)
error(msg"Cannot use this ${ctor.describe} as an extractor" -> ctor.toLoc)
errorSplit
case _ =>
error(msg"Unrecognized pattern." -> pat.toLoc)
error(msg"Unrecognized pattern (${pat.describe})" -> pat.toLoc)
errorSplit

/** Create a function that compiles the resulting term of each case. It checks
Expand Down
10 changes: 5 additions & 5 deletions hkmc2/shared/src/main/scala/hkmc2/syntax/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -527,8 +527,8 @@ abstract class Parser(
case Square => Tup(sts).withLoc(S(loc))
case Round => sts match
case Nil => UnitLit(true).withLoc(S(loc))
case e :: Nil => e
case es => Block(es).withLoc(S(loc))
case e :: Nil => Bra(Round, e).withLoc(S(loc))
case es => Bra(Round, Block(es).withLoc(S(loc)))
exprCont(res, prec, allowNewlines = true)
case (QUOTE, loc) :: _ =>
consume
Expand Down Expand Up @@ -767,7 +767,7 @@ abstract class Parser(
consume
// rec(toks, S(br.innerLoc), br.describe).concludeWith(f(_, true))
val rhs = rec(toks, S(l0), "operator split").concludeWith(_.split)
App(v, PlainTup(acc, Block(rhs)))
App(v, PlainTup(acc, Block(rhs).withLoc(S(l0))))
case _ =>
// val rhs = simpleExpr(opPrec(opStr)._2)
val rhs = expr(opPrec(opStr)._2)
Expand Down Expand Up @@ -988,15 +988,15 @@ abstract class Parser(
if prec < AppPrec && !Keyword.all.contains(id) =>
val res = exprCont(Jux(acc, expr(AppPrec)), prec, allowNewlines)
exprJux(res, prec, allowNewlines)
case (br @ BRACKETS(Curly | Indent, toks), _) :: _
case (br @ BRACKETS(Curly | Indent, toks), l0) :: _
if prec < AppPrec && (toks.headOption match
case S((IDENT(nme, sym), _)) => !sym && !Keyword.all.contains(nme)
case _ => true
) =>
consume
val res = rec(toks, S(br.innerLoc), br.describe).concludeWith:
_.block(allowNewlines = true)
exprCont(Jux(acc, Block(res)), prec, allowNewlines = true)
exprCont(Jux(acc, Block(res).withLoc(S(l0))), prec, allowNewlines = true)

case (tok, _) :: _ =>
printDbg(s"stops at ${tok.toString}")
Expand Down
88 changes: 52 additions & 36 deletions hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ enum Tree extends AutoLocated:
case StrLit(value: Str) extends Tree with Literal
case UnitLit(undefinedOrNull: Bool) extends Tree with Literal
case BoolLit(value: Bool) extends Tree with Literal
case Bra(k: BracketKind, inner: Tree)
case Block(stmts: Ls[Tree])(using State) extends Tree with semantics.BlockImpl
case OpBlock(items: Ls[Tree -> Tree])
case LetLike(kw: Keyword.letLike, lhs: Tree, rhs: Opt[Tree], body: Opt[Tree])
Expand Down Expand Up @@ -81,6 +82,7 @@ enum Tree extends AutoLocated:

def children: Ls[Tree] = this match
case _: Empty | _: Error | _: Ident | _: Literal | _: Under => Nil
case Bra(_, e) => e :: Nil
case Block(stmts) => stmts
case OpBlock(items) => items.flatMap:
case (op, body) => op :: body :: Nil
Expand Down Expand Up @@ -115,14 +117,15 @@ enum Tree extends AutoLocated:

def describe: Str = this match
case Empty() => "empty"
case Error() => "error"
case Error() => "<erroneous syntax>"
case Under() => "underscore"
case Ident(name) => "identifier"
case IntLit(value) => "integer literal"
case DecLit(value) => "decimal literal"
case StrLit(value) => "string literal"
case UnitLit(value) => if value then "null" else "undefined"
case BoolLit(value) => s"$value literal"
case Bra(k, _) => k.name + " section"
case Block(stmts) => "block"
case OpBlock(_) => "operator block"
case LetLike(kw, lhs, rhs, body) => kw.name
Expand Down Expand Up @@ -150,6 +153,10 @@ enum Tree extends AutoLocated:
case Spread(_, _, _) => "spread"
case Annotated(_, _) => "annotated"
case Open(_) => "open"

def deparenthesized: Tree = this match
case Bra(BracketKind.Round, inner) => inner.deparenthesized
case _ => this

def showDbg: Str = toString // TODO

Expand Down Expand Up @@ -208,11 +215,16 @@ object Apps:
case App(Apps(base, args), arg: Tup) => S(base, args :+ arg)
case t => S(t, Nil)

object Annotations:
object PossiblyAnnotated:
def unapply(t: Tree): Opt[(Ls[Tree], Tree)] = t match
case Annotated(q, Annotations(qs, target)) => S(q :: qs, target)
case Annotated(q, PossiblyAnnotated(qs, target)) => S(q :: qs, target)
case other => S((Nil, other))

object PossiblyParenthesized:
def unapply(t: Tree): S[Tree] = t match
case Bra(BracketKind.Round, inner) => S(inner)
case _ => S(t)

/** Matches applications with underscores in some argument and/or prefix positions. */
object PartialApp:
def unapply(t: App): Opt[(Tree \/ Under, Ls[Tree \/ Under])] = t match
Expand Down Expand Up @@ -259,60 +271,64 @@ trait TermDefImpl extends TypeOrTermDef:
trait TypeOrTermDef:
this: TypeDef | TermDef =>

def k: DeclKind
def head: Tree

type MaybeIdent = Diagnostic \/ Ident

lazy val (symbName, name, paramLists, typeParams, annotatedResultType)
: (Opt[Tree], Diagnostic \/ Ident, Ls[Tup], Opt[TyTup], Opt[Tree]) =
def rec(t: Tree, symbName: Opt[Tree]):
(Opt[Tree], Diagnostic \/ Ident, Ls[Tup], Opt[TyTup], Opt[Tree]) =
: (Opt[MaybeIdent], MaybeIdent, Ls[Tup], Opt[TyTup], Opt[Tree]) =
def rec(t: Tree, symbName: Opt[MaybeIdent], annot: Opt[Tree]):
(Opt[MaybeIdent], MaybeIdent, Ls[Tup], Opt[TyTup], Opt[Tree]) =
t match

// fun f: Int
// fun f(n1: Int): Int
// fun f(n1: Int)(nn: Int): Int
case InfixApp(Apps(id: Ident, paramLists), Keyword.`:`, sign) =>
(symbName, R(id), paramLists, N, S(sign))

// fun f[T]: Int
// fun f[T](n1: Int): Int
// fun f[T](n1: Int)(nn: Int): Int
case InfixApp(Apps(App(id: Ident, typeParams: TyTup), paramLists), Keyword.`:`, ret) =>
(symbName, R(id), paramLists, S(typeParams), S(ret))

case InfixApp(Jux(lhs, rhs), Keyword.`:`, ret) =>
rec(InfixApp(rhs, Keyword.`:`, ret), S(lhs))
case InfixApp(tree, Keyword.`:`, ann) =>
rec(tree, symbName, S(ann))

case InfixApp(derived, Keyword.`extends`, base) =>
// TODO handle `extends`!
rec(derived, symbName)
rec(derived, symbName, annot)

// fun f
// fun f(n1: Int)
// fun f(n1: Int)(nn: Int)
case Apps(id: Ident, paramLists) =>
(symbName, R(id), paramLists, N, N)
case Apps(PossiblyParenthesized(id: Ident), paramLists) =>
(symbName, R(id), paramLists, N, annot)

// fun f[T]
// fun f[T](n1: Int)
// fun f[T](n1: Int)(nn: Int)
case Apps(App(id: Ident, typeParams: TyTup), paramLists) =>
(symbName, R(id), paramLists, S(typeParams), N)

case Jux(lhs, rhs) => // happens in `fun (op) nme` form
require(symbName.isEmpty) // TOOD
rec(rhs, S(lhs))
case Apps(App(PossiblyParenthesized(id: Ident), typeParams: TyTup), paramLists) =>
(symbName, R(id), paramLists, S(typeParams), annot)

case Jux(id: Ident, rhs) =>
val err = L:
ErrorReport:
msg"Invalid ${k.desc} definition head: unexpected ${rhs.describe} in this position" -> rhs.toLoc :: Nil
(S(err), R(id), Nil, N, annot)

case Jux(lhs, rhs) => // happens in `fun (op) nme` form
val sn = lhs match
case Bra(BracketKind.Round, id: Ident) =>
require(symbName.isEmpty) // TODO
R(id)
case Bra(BracketKind.Round, lhs) =>
L:
ErrorReport:
msg"This ${lhs.describe} is not a valid symbolic name" -> lhs.toLoc :: Nil
case tree =>
L:
ErrorReport:
msg"Invalid ${k.desc} definition head: unexpected ${lhs.describe} in this position" -> lhs.toLoc :: Nil
rec(rhs, S(sn), annot)

case _ =>
(N, L(ErrorReport(
msg"Expected a valid definition head, found ${t.describe} instead" -> t.toLoc :: Nil)),
Nil, N, N)
msg"Expected a valid ${k.desc} definition head; found ${t.describe} instead" -> t.toLoc :: Nil)),
Nil, N, annot)

rec(head, N)
rec(head, N, N)

lazy val symbolicName: Opt[Ident] = symbName match
case S(id: Ident) => S(id)
case _ => N

end TypeOrTermDef


Expand Down
12 changes: 6 additions & 6 deletions hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls
Original file line number Diff line number Diff line change
Expand Up @@ -50,24 +50,24 @@ fun (++) t() = 1
//│ ╔══[ERROR] Multiple definitions of symbol '**'
//│ ╟── defined here
//│ ║ l.48: fun (**) t() = 0
//│ ║ ^^^^^^^^^^^
//│ ║ ^^^^^^^^^^^^
//│ ╟── defined here
//│ ║ l.49: fun (++) t() = 1
//│ ╙── ^^^^^^^^^^^
//│ ╙── ^^^^^^^^^^^^
//│ ╔══[ERROR] Multiple definitions of symbol '++'
//│ ╟── defined here
//│ ║ l.48: fun (**) t() = 0
//│ ║ ^^^^^^^^^^^
//│ ║ ^^^^^^^^^^^^
//│ ╟── defined here
//│ ║ l.49: fun (++) t() = 1
//│ ╙── ^^^^^^^^^^^
//│ ╙── ^^^^^^^^^^^^
//│ ╔══[ERROR] Multiple definitions of symbol 't'
//│ ╟── defined here
//│ ║ l.48: fun (**) t() = 0
//│ ║ ^^^^^^^^^^^
//│ ║ ^^^^^^^^^^^^
//│ ╟── defined here
//│ ║ l.49: fun (++) t() = 1
//│ ╙── ^^^^^^^^^^^
//│ ╙── ^^^^^^^^^^^^

// ——— ——— ———

Expand Down
Loading

0 comments on commit 3a31c6e

Please sign in to comment.