diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/parser/Parsers.scala | 117 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/parser/Scanners.scala | 50 |
2 files changed, 85 insertions, 82 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index b2570d64e3..1d5f35b7d6 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -40,8 +40,8 @@ trait ParsersCommon extends ScannersCommon { self => */ abstract class ParserCommon { val in: ScannerCommon - def deprecationWarning(off: Int, msg: String): Unit - def accept(token: Int): Int + def deprecationWarning(off: Offset, msg: String): Unit + def accept(token: Token): Int /** Methods inParensOrError and similar take a second argument which, should * the next token not be the expected opener (e.g. LPAREN) will be returned @@ -163,10 +163,10 @@ self => def unit = global.currentUnit // suppress warnings; silent abort on errors - def warning(offset: Int, msg: String) {} - def deprecationWarning(offset: Int, msg: String) {} + def warning(offset: Offset, msg: String) {} + def deprecationWarning(offset: Offset, msg: String) {} - def syntaxError(offset: Int, msg: String): Unit = throw new MalformedInput(offset, msg) + def syntaxError(offset: Offset, msg: String): Unit = throw new MalformedInput(offset, msg) def incompleteInputError(msg: String): Unit = throw new MalformedInput(source.content.length - 1, msg) object symbXMLBuilder extends SymbolicXMLBuilder(this, preserveWS = true) { // DEBUG choices @@ -213,11 +213,11 @@ self => override def newScanner() = new UnitScanner(unit, patches) - override def warning(offset: Int, msg: String) { + override def warning(offset: Offset, msg: String) { unit.warning(o2p(offset), msg) } - override def deprecationWarning(offset: Int, msg: String) { + override def deprecationWarning(offset: Offset, msg: String) { unit.deprecationWarning(o2p(offset), msg) } @@ -235,7 +235,7 @@ self => for ((offset, msg) <- syntaxErrors) unit.error(o2p(offset), msg) - override def syntaxError(offset: Int, msg: String) { + override def syntaxError(offset: Offset, msg: String) { if (smartParsing) syntaxErrors += ((offset, msg)) else unit.error(o2p(offset), msg) } @@ -259,9 +259,10 @@ self => } } - final val Local = 0 - final val InBlock = 1 - final val InTemplate = 2 + type Location = Int + final val Local: Location = 0 + final val InBlock: Location = 1 + final val InTemplate: Location = 2 // These symbols may not yet be loaded (e.g. in the ide) so don't go // through definitions to obtain the names. @@ -293,10 +294,10 @@ self => implicit def fresh: FreshNameCreator = unit.fresh - def o2p(offset: Int): Position = Position.offset(source, offset) - def r2p(start: Int, mid: Int, end: Int): Position = rangePos(source, start, mid, end) - def r2p(start: Int, mid: Int): Position = r2p(start, mid, in.lastOffset max start) - def r2p(offset: Int): Position = r2p(offset, offset) + def o2p(offset: Offset): Position = Position.offset(source, offset) + def r2p(start: Offset, mid: Offset, end: Offset): Position = rangePos(source, start, mid, end) + def r2p(start: Offset, mid: Offset): Position = r2p(start, mid, in.lastOffset max start) + def r2p(offset: Offset): Position = r2p(offset, offset) /** whether a non-continuable syntax error has been seen */ private var lastErrorOffset : Int = -1 @@ -496,7 +497,7 @@ self => finally inFunReturnType = saved } - protected def skip(targetToken: Int) { + protected def skip(targetToken: Token) { var nparens = 0 var nbraces = 0 while (true) { @@ -524,17 +525,17 @@ self => in.nextToken() } } - def warning(offset: Int, msg: String): Unit + def warning(offset: Offset, msg: String): Unit def incompleteInputError(msg: String): Unit private def syntaxError(pos: Position, msg: String, skipIt: Boolean) { syntaxError(pos pointOrElse in.offset, msg, skipIt) } - def syntaxError(offset: Int, msg: String): Unit + def syntaxError(offset: Offset, msg: String): Unit def syntaxError(msg: String, skipIt: Boolean) { syntaxError(in.offset, msg, skipIt) } - def syntaxError(offset: Int, msg: String, skipIt: Boolean) { + def syntaxError(offset: Offset, msg: String, skipIt: Boolean) { if (offset > lastErrorOffset) { syntaxError(offset, msg) // no more errors on this token. @@ -558,10 +559,10 @@ self => } def expectedMsgTemplate(exp: String, fnd: String) = s"$exp expected but $fnd found." - def expectedMsg(token: Int): String = expectedMsgTemplate(token2string(token), token2string(in.token)) + def expectedMsg(token: Token): String = expectedMsgTemplate(token2string(token), token2string(in.token)) /** Consume one token of the specified type, or signal an error if it is not there. */ - def accept(token: Int): Int = { + def accept(token: Token): Offset = { val offset = in.offset if (in.token != token) { syntaxErrorOrIncomplete(expectedMsg(token), skipIt = false) @@ -647,14 +648,14 @@ self => def isIdent = in.token == IDENTIFIER || in.token == BACKQUOTED_IDENT - def isLiteralToken(token: Int) = token match { + def isLiteralToken(token: Token) = token match { case CHARLIT | INTLIT | LONGLIT | FLOATLIT | DOUBLELIT | STRINGLIT | INTERPOLATIONID | SYMBOLLIT | TRUE | FALSE | NULL => true case _ => false } def isLiteral = isLiteralToken(in.token) - def isExprIntroToken(token: Int): Boolean = isLiteralToken(token) || (token match { + def isExprIntroToken(token: Token): Boolean = isLiteralToken(token) || (token match { case IDENTIFIER | BACKQUOTED_IDENT | THIS | SUPER | IF | FOR | NEW | USCORE | TRY | WHILE | DO | RETURN | THROW | LPAREN | LBRACE | XMLSTART => true @@ -663,7 +664,7 @@ self => def isExprIntro: Boolean = isExprIntroToken(in.token) - def isTypeIntroToken(token: Int): Boolean = token match { + def isTypeIntroToken(token: Token): Boolean = token match { case IDENTIFIER | BACKQUOTED_IDENT | THIS | SUPER | USCORE | LPAREN | AT => true case _ => false @@ -673,7 +674,7 @@ self => def isCaseDefEnd = in.token == RBRACE || in.token == CASE || in.token == EOF - def isStatSep(token: Int): Boolean = + def isStatSep(token: Token): Boolean = token == NEWLINE || token == NEWLINES || token == SEMI def isStatSep: Boolean = isStatSep(in.token) @@ -688,10 +689,10 @@ self => /* ---------- TREE CONSTRUCTION ------------------------------------------- */ - def atPos[T <: Tree](offset: Int)(t: T): T = atPos(r2p(offset))(t) - def atPos[T <: Tree](start: Int, point: Int)(t: T): T = atPos(r2p(start, point))(t) - def atPos[T <: Tree](start: Int, point: Int, end: Int)(t: T): T = atPos(r2p(start, point, end))(t) - def atPos[T <: Tree](pos: Position)(t: T): T = global.atPos(pos)(t) + def atPos[T <: Tree](offset: Offset)(t: T): T = atPos(r2p(offset))(t) + def atPos[T <: Tree](start: Offset, point: Offset)(t: T): T = atPos(r2p(start, point))(t) + def atPos[T <: Tree](start: Offset, point: Offset, end: Offset)(t: T): T = atPos(r2p(start, point, end))(t) + def atPos[T <: Tree](pos: Position)(t: T): T = global.atPos(pos)(t) def atInPos[T <: Tree](t: T): T = atPos(o2p(in.offset))(t) def setInPos[T <: Tree](t: T): T = t setPos o2p(in.offset) @@ -729,7 +730,7 @@ self => } /** {{{ part { `sep` part } }}},or if sepFirst is true, {{{ { `sep` part } }}}. */ - final def tokenSeparated[T](separator: Int, sepFirst: Boolean, part: => T): List[T] = { + final def tokenSeparated[T](separator: Token, sepFirst: Boolean, part: => T): List[T] = { val ts = new ListBuffer[T] if (!sepFirst) ts += part @@ -772,7 +773,7 @@ self => } } - def checkAssoc(offset: Int, op: Name, leftAssoc: Boolean) = + def checkAssoc(offset: Offset, op: Name, leftAssoc: Boolean) = if (treeInfo.isLeftAssoc(op) != leftAssoc) syntaxError( offset, "left- and right-associative operators with same precedence may not be mixed", skipIt = false) @@ -812,7 +813,7 @@ self => def argType(): Tree def functionArgType(): Tree - private def tupleInfixType(start: Int) = { + private def tupleInfixType(start: Offset) = { in.nextToken() if (in.token == RPAREN) { in.nextToken() @@ -1049,7 +1050,7 @@ self => t } - def selectors(t: Tree, typeOK: Boolean, dotOffset: Int): Tree = + def selectors(t: Tree, typeOK: Boolean, dotOffset: Offset): Tree = if (typeOK && in.token == TYPE) { in.nextToken() atPos(t.pos.start, dotOffset) { SingletonTypeTree(t) } @@ -1107,7 +1108,7 @@ self => * | null * }}} */ - def literal(isNegated: Boolean = false, inPattern: Boolean = false, start: Int = in.offset): Tree = atPos(start) { + def literal(isNegated: Boolean = false, inPattern: Boolean = false, start: Offset = in.offset): Tree = atPos(start) { def finish(value: Any): Tree = try newLiteral(value) finally in.nextToken() if (in.token == SYMBOLLIT) Apply(scalaDot(nme.Symbol), List(finish(in.strVal))) @@ -1204,12 +1205,12 @@ self => in.nextToken() } - def newLineOptWhenFollowedBy(token: Int) { + def newLineOptWhenFollowedBy(token: Offset) { // note: next is defined here because current == NEWLINE if (in.token == NEWLINE && in.next.token == token) newLineOpt() } - def newLineOptWhenFollowing(p: Int => Boolean) { + def newLineOptWhenFollowing(p: Token => Boolean) { // note: next is defined here because current == NEWLINE if (in.token == NEWLINE && p(in.next.token)) newLineOpt() } @@ -1224,7 +1225,7 @@ self => if (in.token == COLON) { in.nextToken(); typ() } else TypeTree() - def typeOrInfixType(location: Int): Tree = + def typeOrInfixType(location: Location): Tree = if (location == Local) typ() else startInfixType() @@ -1235,7 +1236,7 @@ self => * WildcardType ::= `_' TypeBounds * }}} */ - def wildcardType(start: Int) = { + def wildcardType(start: Offset) = { val pname = freshTypeName("_$") val t = atPos(start)(Ident(pname)) val bounds = typeBounds() @@ -1261,7 +1262,7 @@ self => /* hook for IDE, unlike expression can be stubbed * don't use for any tree that can be inspected in the parser! */ - def statement(location: Int): Tree = expr(location) // !!! still needed? + def statement(location: Location): Tree = expr(location) // !!! still needed? /** {{{ * Expr ::= (Bindings | [`implicit'] Id | `_') `=>' Expr @@ -1288,9 +1289,9 @@ self => */ def expr(): Tree = expr(Local) - def expr(location: Int): Tree = withPlaceholders(expr0(location), isAny = false) + def expr(location: Location): Tree = withPlaceholders(expr0(location), isAny = false) - def expr0(location: Int): Tree = (in.token: @scala.annotation.switch) match { + def expr0(location: Location): Tree = (in.token: @scala.annotation.switch) match { case IF => def parseIf = atPos(in.skipToken()) { val cond = condExpr() @@ -1448,7 +1449,7 @@ self => * }}} */ - def implicitClosure(start: Int, location: Int): Tree = { + def implicitClosure(start: Offset, location: Location): Tree = { val param0 = convertToParam { atPos(in.offset) { Ident(ident()) match { @@ -1712,7 +1713,7 @@ self => while (in.token == IF) enums += makeFilter(in.offset, guard()) } - def makeFilter(start: Int, tree: Tree) = Filter(r2p(start, tree.pos.point, tree.pos.end), tree) + def makeFilter(start: Offset, tree: Tree) = Filter(r2p(start, tree.pos.point, tree.pos.end), tree) /* -------- PATTERNS ------------------------------------------- */ @@ -2276,7 +2277,7 @@ self => t setPos o2p(in.offset) } - def bound(tok: Int): Tree = if (in.token == tok) { in.nextToken(); typ() } else EmptyTree + def bound(tok: Token): Tree = if (in.token == tok) { in.nextToken(); typ() } else EmptyTree /* -------- DEFS ------------------------------------------- */ @@ -2395,7 +2396,7 @@ self => * | type [nl] TypeDcl * }}} */ - def defOrDcl(pos: Int, mods: Modifiers): List[Tree] = { + def defOrDcl(pos: Offset, mods: Modifiers): List[Tree] = { if (mods.isLazy && in.token != VAL) syntaxError("lazy not allowed here. Only vals can be lazy", skipIt = false) in.token match { @@ -2524,7 +2525,7 @@ self => } } - def funDefRest(start: Int, nameOffset: Int, mods: Modifiers, name: Name): Tree = { + def funDefRest(start: Offset, nameOffset: Offset, mods: Modifiers, name: Name): Tree = { val result = atPos(start, if (name.toTermName == nme.ERROR) start else nameOffset) { var newmods = mods // contextBoundBuf is for context bounded type parameters of the form @@ -2607,7 +2608,7 @@ self => * TypeDcl ::= type Id [TypeParamClause] TypeBounds * }}} */ - def typeDefOrDcl(start: Int, mods: Modifiers): Tree = { + def typeDefOrDcl(start: Offset, mods: Modifiers): Tree = { in.nextToken() newLinesOpt() atPos(start, in.offset) { @@ -2640,7 +2641,7 @@ self => * | [override] trait TraitDef * }}} */ - def tmplDef(pos: Int, mods: Modifiers): Tree = { + def tmplDef(pos: Offset, mods: Modifiers): Tree = { if (mods.isLazy) syntaxError("classes cannot be lazy", skipIt = false) in.token match { case TRAIT => @@ -2664,7 +2665,7 @@ self => * TraitDef ::= Id [TypeParamClause] RequiresTypeOpt TraitTemplateOpt * }}} */ - def classDef(start: Int, mods: Modifiers): ClassDef = { + def classDef(start: Offset, mods: Modifiers): ClassDef = { in.nextToken() val nameOffset = in.offset val name = identForType() @@ -2704,7 +2705,7 @@ self => * ObjectDef ::= Id ClassTemplateOpt * }}} */ - def objectDef(start: Int, mods: Modifiers): ModuleDef = { + def objectDef(start: Offset, mods: Modifiers): ModuleDef = { in.nextToken() val nameOffset = in.offset val name = ident() @@ -2743,7 +2744,7 @@ self => ) // TODO - eliminate this and use "def packageObjectDef" (see call site of this // method for small elaboration.) - def makePackageObject(start: Int, objDef: ModuleDef): PackageDef = objDef match { + def makePackageObject(start: Offset, objDef: ModuleDef): PackageDef = objDef match { case ModuleDef(mods, name, impl) => makePackaging( start, atPos(o2p(objDef.pos.start)){ Ident(name) }, List(ModuleDef(mods, nme.PACKAGEkw, impl))) @@ -2818,7 +2819,7 @@ self => * TraitExtends ::= `extends' | `<:' * }}} */ - def templateOpt(mods: Modifiers, name: Name, constrMods: Modifiers, vparamss: List[List[ValDef]], tstart: Int): Template = { + def templateOpt(mods: Modifiers, name: Name, constrMods: Modifiers, vparamss: List[List[ValDef]], tstart: Offset): Template = { val (parents, self, body) = ( if (in.token == EXTENDS || in.token == SUBTYPE && mods.isTrait) { in.nextToken() @@ -2881,17 +2882,17 @@ self => /* -------- STATSEQS ------------------------------------------- */ /** Create a tree representing a packaging. */ - def makePackaging(start: Int, pkg: Tree, stats: List[Tree]): PackageDef = pkg match { + def makePackaging(start: Offset, pkg: Tree, stats: List[Tree]): PackageDef = pkg match { case x: RefTree => atPos(start, pkg.pos.point)(PackageDef(x, stats)) } - def makeEmptyPackage(start: Int, stats: List[Tree]): PackageDef = ( + def makeEmptyPackage(start: Offset, stats: List[Tree]): PackageDef = ( makePackaging(start, atPos(start, start, start)(Ident(nme.EMPTY_PACKAGE_NAME)), stats) ) - def statSeq(stat: PartialFunction[Int, List[Tree]], errorMsg: String = "illegal start of definition"): List[Tree] = { + def statSeq(stat: PartialFunction[Token, List[Tree]], errorMsg: String = "illegal start of definition"): List[Tree] = { val stats = new ListBuffer[Tree] - def default(tok: Int) = + def default(tok: Token) = if (isStatSep) Nil else syntaxErrorOrIncompleteAnd(errorMsg, skipIt = true)(Nil) while (!isStatSeqEnd) { @@ -2911,7 +2912,7 @@ self => * }}} */ def topStatSeq(): List[Tree] = statSeq(topStat, errorMsg = "expected class or object definition") - def topStat: PartialFunction[Int, List[Tree]] = { + def topStat: PartialFunction[Token, List[Tree]] = { case PACKAGE => packageOrPackageObject(in.skipToken()) :: Nil case IMPORT => @@ -2963,7 +2964,7 @@ self => * }}} */ def templateStats(): List[Tree] = statSeq(templateStat) - def templateStat: PartialFunction[Int, List[Tree]] = { + def templateStat: PartialFunction[Token, List[Tree]] = { case IMPORT => in.flushDoc importClause() diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 6957f85689..b12be1a056 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -21,19 +21,24 @@ trait ScannersCommon { val global : Global import global._ + /** Offset into source character array */ + type Offset = Int + + type Token = Int + trait CommonTokenData { - def token: Int + def token: Token def name: TermName } trait ScannerCommon extends CommonTokenData { // things to fill in, in addition to buf, decodeUni which come from CharArrayReader - def error (off: Int, msg: String): Unit - def incompleteInputError(off: Int, msg: String): Unit - def deprecationWarning(off: Int, msg: String): Unit + def error(off: Offset, msg: String): Unit + def incompleteInputError(off: Offset, msg: String): Unit + def deprecationWarning(off: Offset, msg: String): Unit } - def createKeywordArray(keywords: Seq[(Name, Int)], defaultToken: Int): (Int, Array[Int]) = { + def createKeywordArray(keywords: Seq[(Name, Token)], defaultToken: Token): (Token, Array[Token]) = { val names = keywords sortBy (_._1.start) map { case (k, v) => (k.start, v) } val low = names.head._1 val high = names.last._1 @@ -48,13 +53,10 @@ trait Scanners extends ScannersCommon { val global : Global import global._ - /** Offset into source character array */ - type Offset = Int - trait TokenData extends CommonTokenData { /** the next token */ - var token: Int = EMPTY + var token: Token = EMPTY /** the offset of the first character of the current token */ var offset: Offset = 0 @@ -169,7 +171,7 @@ trait Scanners extends ScannersCommon { def isAtEnd = charOffset >= buf.length - def resume(lastCode: Int) = { + def resume(lastCode: Token) = { token = lastCode if (next.token != EMPTY && !reporter.hasErrors) syntaxError("unexpected end of input: possible missing '}' in XML block") @@ -194,7 +196,7 @@ trait Scanners extends ScannersCommon { protected def emitIdentifierDeprecationWarnings = true /** Clear buffer and set name and token */ - private def finishNamed(idtoken: Int = IDENTIFIER) { + private def finishNamed(idtoken: Token = IDENTIFIER) { name = newTermName(cbuf.toString) cbuf.clear() token = idtoken @@ -225,7 +227,7 @@ trait Scanners extends ScannersCommon { * (the STRINGLIT appears twice in succession on the stack iff the * expression is a multiline string literal). */ - var sepRegions: List[Int] = List() + var sepRegions: List[Token] = List() // Get next token ------------------------------------------------------------ @@ -583,7 +585,7 @@ trait Scanners extends ScannersCommon { } /** Can token start a statement? */ - def inFirstOfStat(token: Int) = token match { + def inFirstOfStat(token: Token) = token match { case EOF | CATCH | ELSE | EXTENDS | FINALLY | FORSOME | MATCH | WITH | YIELD | COMMA | SEMI | NEWLINE | NEWLINES | DOT | COLON | EQUALS | ARROW | LARROW | SUBTYPE | VIEWBOUND | SUPERTYPE | HASH | RPAREN | RBRACKET | RBRACE | LBRACKET => @@ -593,7 +595,7 @@ trait Scanners extends ScannersCommon { } /** Can token end a statement? */ - def inLastOfStat(token: Int) = token match { + def inLastOfStat(token: Token) = token match { case CHARLIT | INTLIT | LONGLIT | FLOATLIT | DOUBLELIT | STRINGLIT | SYMBOLLIT | IDENTIFIER | BACKQUOTED_IDENT | THIS | NULL | TRUE | FALSE | RETURN | USCORE | TYPE | XMLSTART | RPAREN | RBRACKET | RBRACE => @@ -1122,7 +1124,7 @@ trait Scanners extends ScannersCommon { def applyBracePatch(): Boolean = false /** overridden in UnitScanners */ - def parenBalance(token: Int) = 0 + def parenBalance(token: Token) = 0 /** overridden in UnitScanners */ def healBraces(): List[BracePatch] = List() @@ -1137,7 +1139,7 @@ trait Scanners extends ScannersCommon { // ------------- keyword configuration ----------------------------------- - private val allKeywords = List[(Name, Int)]( + private val allKeywords = List[(Name, Token)]( nme.ABSTRACTkw -> ABSTRACT, nme.CASEkw -> CASE, nme.CATCHkw -> CATCH, @@ -1191,8 +1193,8 @@ trait Scanners extends ScannersCommon { nme.MACROkw -> IDENTIFIER, nme.THENkw -> IDENTIFIER) - private var kwOffset: Int = -1 - private val kwArray: Array[Int] = { + private var kwOffset: Offset = -1 + private val kwArray: Array[Token] = { val (offset, arr) = createKeywordArray(allKeywords, IDENTIFIER) kwOffset = offset arr @@ -1203,7 +1205,7 @@ trait Scanners extends ScannersCommon { // Token representation ---------------------------------------------------- /** Returns the string representation of given token. */ - def token2string(token: Int): String = (token: @switch) match { + def token2string(token: Token): String = (token: @switch) match { case IDENTIFIER | BACKQUOTED_IDENT => "identifier" case CHARLIT => "character literal" case INTLIT => "integer literal" @@ -1234,7 +1236,7 @@ trait Scanners extends ScannersCommon { } } - class MalformedInput(val offset: Int, val msg: String) extends Exception + class MalformedInput(val offset: Offset, val msg: String) extends Exception /** A scanner for a given source file not necessarily attached to a compilation unit. * Useful for looking inside source files that aren not currently compiled to see what's there @@ -1262,7 +1264,7 @@ trait Scanners extends ScannersCommon { lazy val parensAnalyzer = new ParensAnalyzer(unit, List()) - override def parenBalance(token: Int) = parensAnalyzer.balance(token) + override def parenBalance(token: Token) = parensAnalyzer.balance(token) override def healBraces(): List[BracePatch] = { var patches: List[BracePatch] = List() @@ -1412,7 +1414,7 @@ trait Scanners extends ScannersCommon { var tabSeen = false - def line(offset: Int): Int = { + def line(offset: Offset): Int = { def findLine(lo: Int, hi: Int): Int = { val mid = (lo + hi) / 2 if (offset < lineStart(mid)) findLine(lo, mid - 1) @@ -1423,7 +1425,7 @@ trait Scanners extends ScannersCommon { else findLine(0, lineStart.length - 1) } - def column(offset: Int): Int = { + def column(offset: Offset): Int = { var col = 0 var i = offset - 1 while (i >= 0 && buf(i) != CR && buf(i) != LF) { @@ -1485,6 +1487,6 @@ trait Scanners extends ScannersCommon { // when skimming through the source file trying to heal braces override def emitIdentifierDeprecationWarnings = false - override def error(offset: Int, msg: String) {} + override def error(offset: Offset, msg: String) {} } } |