Я разрабатываю библиотеку и хочу продемонстрировать код на небольшом веб-сайте. Фрагменты кода должны быть получены непосредственно из одного и того же проекта, т. е. код должен ссылаться на самого себя.
Я хочу написать макрос, который, учитывая файл или имя класса, возвращает строку с содержимым файла. Как я могу этого добиться?
Почему вы хотите, чтобы это был макрос? Почему это не может быть обычным методом?
Файлы не существуют во время выполнения. Они являются источником запущенного приложения.
Я бы также согласился указать имя класса или тип в качестве аргумента и вернуть источник класса/объекта/признака или что-то еще, что реализовало этот тип.
«Файлы не существуют во время выполнения». Почему? Вы удаляете исходники после компиляции?
Почему бы не запустить компилятор scala во время выполнения и не скомпилировать все необходимые фрагменты кода?
Файлы существуют во время выполнения, но приложение теряет их из виду. Запуск компилятора во время выполнения вызовет столько проблем, что я даже не знаю, с чего начать. Ваш ответ ниже, кажется, то, что мне нужно - я вернусь, как только у меня будет возможность проверить его: D Спасибо!
Я бы также согласился указать имя класса или тип в качестве аргумента
Вы ищете такой макрос?
import scala.quoted.*
inline def getContent[A]: String = ${getContentImpl[A]}
def getContentImpl[A: Type](using Quotes): Expr[String] =
import quotes.reflect.*
// for the source specifically of A
// val str = TypeRepr.of[A].typeSymbol.tree.pos.sourceCode.getOrElse(
// report.errorAndAbort("no source code")
// )
// for the whole source file containing the definition of A
val str = TypeRepr.of[A].typeSymbol.pos.getOrElse(
report.errorAndAbort("no symbol position")
).sourceFile.content.getOrElse(
report.errorAndAbort("no source-file content")
)
Expr(str)
// in a different file
getContent[A]
//import Macros.getContent
//
//object Main extends App {
// class A:
// def foo = 1
//
// println(
// getContent[A]
// )
//}
Как преобразовать блок кода scala в строку? (Скала 2)
Это действительно хорошо, спасибо!
У меня проблема - иногда не получается и позиция печатается как "?", есть идеи?
@ Феликс Нет. Странно. Если исходный код недоступен, он не должен компилироваться и возвращать ?. Попробуйте создать репродукцию
Очистка и перекомпиляция вроде бы исправляют, но потом снова быстро ломаются.
У меня есть репозиторий для воспроизведения проблемы здесь: github.com/hejfelix/macro-repro
@Felix Чистая компиляция является хорошей практикой при работе со сгенерированным кодом. Поведение в Scala 2 такое же gist.github.com/DmytroMitin/344f2d1410b4f285bc95b8299f9096aa Подозреваю, что это не баг, а то, как sbt + цинк управляют инкрементной компиляцией. При чистой компиляции sbt пишет compiling 3 Scala sources, при перекомпиляции после модификации SourceMacro пишет compiling 1 Scala source, compiling 1 Scala source. Думаю, это влияет на позицию.
@Felix Почему чистая компиляция является проблемой для вашего варианта использования?
Я полагаю, вы правы. Это просто раздражает при отладке/разработке. Последний артефакт исходит от CI, который в настоящее время выполняет чистую компиляцию, поэтому все должно быть в порядке. Спасибо за изучение этого, я повторно принял 👌
Предположим, ваш макрос inline def getContent(filePath: String): String = ${getContentImpl('className)}. Является ли filePath строкой времени компиляции (как в getContent("path/to/file")) или строкой времени выполнения (как в val str = "path/to/file"; getContent(str))?