refal-5-lambda
refal-5-lambda copied to clipboard
Поддержка Юникода в Рефале-5λ
Текущая реализация Рефала-5λ не поддерживает Юникод, интерпретирует текст как последовательность байт. Поэтому с текстами в кодировке UTF-8 он частично совместим, ровно на столько, на сколько UTF 8 может заменить однобайтную кодировку.
Требуется реализовать поддержку Юникода и кодировок символов на уровне лексики (в частности, национальные символы в идентификаторах), библиотеки времени выполнения и библиотеки стандартных функций.
Библиотеку для Юникода можно реализовывать тремя путями:
- по принципу чучхэ¹ — самим реализовать всё на C++,
- используя API операционной системы, при этом должны поддерживаться как минимум три ОС: Windows, Linux и OS X (есть подозрение, что у Linux и OS X API будет разным);
- используя библиотеками
<locale>
и<wctype.h>
, но насколько точно и переносимо они поддерживают Юникод в разных версиях компиляторов — точно не знаю.
У каждого варианта свои достоинства и недостатки, нужно будет выбрать какой-то один и его реализовать.
¹ Чучхэ (кор.) — идеология опоры на собственные силы
https://github.com/nemtrif/utfcpp
Роберт Тебиев (@tebrobert), работавший над этой задачей, вроде как ушёл из вуза. Поэтому задача переходит на следующий осенний семестр.
Забираю задачу себе, поскольку в качестве курсового её никто не взял.
Сначала, вроде как Роберт (@tebrobert) восстановился, приходил на консультацию, а сегодня я узнал, что он снова ушёл. Задачу исключаю из вехи.
Задача актуальна для курсового проекта.
Никто не взял как тему курсового.
Задача снова вывешивается на курсовую работу.
Кстати, на момент написания комментария — это самая старая задача из открытых.
Задача не была выбрана в качестве курсовой.
Не буду предлагать на курсовую, т.к. задача подразумевает изменение рантайма, объём которых мне сейчас не очевиден.
Мне кажется, задачу нужно разбить на несколько подзадач, чтобы с ней можно было как-то работать. Примерно так:
- Выбор внутреннего представления.
- Например, в Haskell, Python, Java, Qt используется UTF-32 - очевидные плюсы - в большинстве случаях символ имеет фиксированный размер - соответственно скорость обработки практически не отличима от обычных char. Очевидные минусы - большой перерасход памяти в стандартных случаях. Кроме того в патологических случаях символ не вмещается в 32бита. Правда для идентификаторов можно специально ограничить набор символов - т. е. на скорость рантайм это не должно повлиять.
- UTF-16 - используется в Win32. Промежуточный по памяти вариант. Но если вспомнить про Китайцев - то от многопозиционных символов не уйти.
- UTF-8 - скорость будет всегда в пролёте. Памяти - минимум.
- Что-то нужно решать с байтовыми данными. Как-то разграничить работу и применение просто буферов байтов, C-сторк и символов. В приведённых выше языках это решается на уровне системы типов.
- Далее, из первых 2х пунктов выбирать реализацию собственно работы с unicode.
- Добавить выбранную библиотеку в рантайм.
- Добавить в компилятор и научить его работать с литералами и идентификаторами в unicode.
- Что-то решить с линкером и вставками на C/C++.
Добрый день, @tonal!
-
Выбор внутреннего представления — UTF-32. Данные в памяти представляются как двусвязные списки звеньев с тегом и полем информации: https://github.com/bmstu-iu9/refal-5-lambda/blob/21d12cc4071e73f4297f0ba958c31b7e3a54aff6/src/lib/refalrts.h#L114-L134 Так что, если заменить
char
наint
, никакого перерасхода памяти не будет.Для представления имён функций и символов-слов будет использоваться UTF-8 ради компактности и удобства работы.
-
Байтовые данные на данный момент просто обрабатываются как строки из чисел 0…255. Скорее всего, потребуется написать функции, преобразующие строки char’ов в строки чисел 0…255 в той или иной кодировке. Это вопрос исключительно библиотеки, а не рантайма.
-
—
-
—
-
Тут нужно не только сам компилятор (лексер) научить читать литералы, идентификаторы и т.д., но и научиться читать файлы в различных кодировках (UTF-8, UTF-16 оба вида, UTF-32 оба вида, однобайтовые вроде 1251…).
-
Нативные вставки я планирую удалить (https://github.com/bmstu-iu9/refal-5-lambda/issues/318#native). Сложности с C/C++ я пока не вижу. Для имён функций в Unicode нужно будет расширить декорирование имён, но это технический вопрос.
Хорошего окончания года @Mazdaywik!
-
Тут есть чуток ремарок: а) Некоторые символы могут иметь представление как 2 и более кода. Самые очевидные Й и Ё - они могут быть представлены 1 собственно символом, или 2мя - основным и надстрочным совмещённым. б) Хуже того. Сортировка может зависеть от несколькобуквенных последовательностей - возникает неоднозначность при сопоставлениях с образцом... в) В том же Haskell-е именно такое представление и было изначально. Но оказалось, что для реальной работы оно изрядно тормозное. Поэтому сейчас используются пакеты ByteString при любой сколько-нибудь массовой работы со строками (в компиляторе так же).
-
См. П 5 и П 1.б.
-
(п5) В том же Python-е стандартизировали комментарий в начале файла, в котором можно указать кодировку. Если не указано - считается utf-8. В С++ добавили флаг компиляции, позволяющий указать кодировку исходника. В Haskell - зафиксировали utf-8. Но, в любом случае компилятор должен быть осведомлён о кодировке, и использовать какие-то библиотеки для конвертации. Предоставлять ли их как доступные в рантайм - вопрос открытый. Но у компилятора они должны быть.
@tonal, спасибо за интересные дополнения, в частности, за опыт с другими языками программирования. С Хаскелем я не работал, поэтому этих тонкостей не знал. С Python работал, в глубины особенностей реализации Юникода и кодировок не углублялся. Знаю только про комментарий-кодировку (правда, не знаю, как Python работает с файлами в кодировках UTF-16 и UTF-32 без BOM).
Флаг компилятора в C++ с кодировкой исходника — это, по-видимому, особенность той реализации C++, которой Вы пользуетесь. Странно было бы, если бы эту функцию затребовали в Стандарте. Вообще C++11 и последующие уже поддерживают строковые литералы в UTF-16 и UTF-32, поэтому как-то работать с этим надо.
По поводу того, как хранить составные символы (ё, й, ñ, ударе́ние…) в памяти — как они загрузились из файла, так и хранить. Но при этом иметь библиотечные функции для приведения текста к той или иной нормальной форме (там их 4). Потому что учитывать эти составные символы в процессе сопоставления с образцом — жутко сложно и накладно.
Кстати, на macOS почему-то «ё» и «й» по умолчанию представляются парой кодовых точек. Иногда мне присылают картинки, созданные на macOS, в именах которых находятся упомянутые буквы. IrfanView открыть их не может.
Кстати, знаки-модификаторы сейчас используются не только для точек над «ё». Эмодзям можно задать цвет кожи и пол, которые тоже задаются модификаторами. Без модификатора цвета кожи смайлик показывается жёлтым, с модификатором — от розового до чёрного. Без модификатора пола — какой-то пол по умолчанию, у разных смайликов разный. Сейчас я пишу с компьютера с Windows 7, на нём геморно смайлики вводить. Сяду за компьютер с Windows 10 — накидаю примеров в комментарии, если не забуду.
А вообще, хороший вопрос, как правильно компилировать файлы с разными кодировками.
В самом языке имена функций и переменных сейчас могут содержать только латинские буквы ASCII с учётом регистра символа, поэтому кодировка на них не влияет. Влияет кодировка на константы — литеры (characters) и символы-слова в кавычках (что-то вроде цитированных имён в Лиспе). Сейчас они просто рассматриваются как байты. Если исходник в кодировке UTF-8, то русская буква в памяти будет храниться как два отдельных character’а, какой-нибудь иероглиф — три или четыре.
Но если поддерживать Юникод, то нужно предложить разумное поведение для случая, когда кодировка исходника не определена. Например, можно так: если определена правильно (есть псевдокомментарий, или начинается на BOM и при считывании не было ошибок), то компилируется тихо-спокойно. Если не определена, то на символы с кодом больше 127 (кроме комментариев) можно выдавать предупреждение или даже ошибку, что это какая-то фигня.
Хороший вопрос с нормализацией и символами-словами с составными знаками (вроде тех же «ё»). Исходники могут совместно правиться под разными системами, и один и тот же по начертанию и написанию символ будет представлен в файле по-разному. Но он при этом во время выполнения должен быть одним и тем же. Возможное решение — для символов-слов принудительно применять нормализацию. Для символов-литер это менее актуально, но тоже можно применять некую нормальную форму (какую из четырёх?).
Сейчас из поддержки Юникода компилятор умеет только игнорировать UTF-8 BOM в начале — не выдаёт на него ошибку.