Конкретно занимаемся строками

Операционная система, написанная на языке Оберон и проект её перевода на русский язык. Только технические вопросы.
Ответить
БудДен
Сообщения: 854
Зарегистрирован: 07.10.18 14:01

Конкретно занимаемся строками

Сообщение БудДен » 19.04.20 22:00

Часть проблем с кириллицей уже решена, но до конца ещё далеко.

Текущая цель - чтобы при наличии в исходнике кириллицы не сбивалась позиция. Пока что, если есть кириллица (даже в комментариях), место ошибки компиляции показывается криво.

При том, в комментариях и строковых литералах кириллица допустима и работает, при формате файла ЮТФ-8.

Вторая цель - перевести парсер на широкие буквы, либо ЮТФ-16, либо ЮТФ-32. В принципе, UCS-16 (2 байта, игнорируя суррогатные пары) для кириллицы должно хватить. Потери будут таковы:

Код: Выделить всё

A: Most supplementary characters (expressed with surrogate pairs in UTF-16) are not too common. However, that does not mean that supplementary characters should be neglected. Among them are a number of individual characters that are very popular, as well as many sets important to East Asian procurement specifications. Among the notable supplementary characters are:

many popular emoji and emoticons

symbols used for interoperating with Wingdings and Webdings

numerous small sets of CJK characters important for procurement, including personal and place names

variation selectors used for all ideographic variation sequences

numerous minority scripts important for some user communities

some highly salient historic scripts, such as Egyptian hieroglyphics
(источник: http://www.unicode.org/faq/utf_bom.html#utf16-9)

Где куски кода для работы с кириллицей?

ПРоблемой было то, что понимался только правильный юникод, при неправильном строчка просто резалась. Это я вроде поправил. На данную секунду правильный (я надеюсь) код чтения УТФ8 из потока находится в Proba.Mod, а код чтения из буферизованного потока - в TextUtils.Mod (процедура что-то.Add). Причём код чтения из буферизованного потока я переписал, но не тестировал. Остаётся только молиться, конечно же.
Последний раз редактировалось БудДен 20.04.20 22:18, всего редактировалось 1 раз.

БудДен
Сообщения: 854
Зарегистрирован: 07.10.18 14:01

Re: Конкретно занимаемся строками

Сообщение БудДен » 19.04.20 22:11

Займёмся первой целью. Существует объект Streams.Reader, к-рый является базовым классом для читателя в т.ч. из файла, к-рым пользуется компилятор.
Поскольку мы пока не пытаемся читать символы юникода, а нас устраивают code байты (каковые и есть тип CHAR), нас устраивает интерфейс. Единственное, что должно поменяться - это функция Pos. Поэтому мы сначала посмотрим, используется ли эта ф-я внутри и можно ли её безболезненно поменять (с двух точек зрения - возможно ли это и что при этом сломается).

Хотя по идее, нафиг нам менять Pos, мы добавим свой новый UTF8LCPos (UTF8 и LC - Line count). Но мы должны всё же поменять функции чтения буквы (Char, Get, Peek). И вот тут я не настолько знаю устройство ООП в АО, чтобы быть уверенным в успехе. Попробуем... Как действовать, не совсем ясно.

Похоже, нам во-первых нужно будет сделать обёртку (т.е. исходный поток будет полем в нашем потоке) и плюс наследование от обычного Stream с перешибанием Char, Get, Peek и добавлением UTF8LCPos.

Для создания нового потока в принципе достаточно перекрыть несколько функций заполнения буфера. Например, вот Files.Reader:

Код: Выделить всё

	(** Reader for buffered reading of a file via Streams.Read* procedures.  See OpenReader. *)
	Reader* = OBJECT (Streams.Reader)	(** not sharable between multiple processes *)
	VAR
		file : File;
		r: Rider;

		PROCEDURE Receive(VAR buf: ARRAY OF CHAR; ofs, size, min: SIZE; VAR len: SIZE; VAR res: INTEGER);
		BEGIN
			file.ReadBytes(r, buf, ofs, size);
			len := size - r.res;
			IF len >= min THEN res := Streams.Ok ELSE res := Streams.EOF (* end of file *) END
		END Receive;

		PROCEDURE CanSetPos*() : BOOLEAN;
		BEGIN
			RETURN TRUE;
		END CanSetPos;

		PROCEDURE SetPos*(pos : Streams.Position);
		BEGIN
			file.Set(r, pos);
			Reset;
			received := pos; (* this effects that Streams.Reader.Pos() returns the correct location in the file *)
		END SetPos;

		PROCEDURE &InitFileReader*(file : File; pos: Position);
		BEGIN
			ASSERT(file # NIL);
			SELF.file := file;
			file.Set(r, pos);
			received := pos; (* this effects that Streams.Reader.Pos() returns the correct location in the file *)
			InitReader(SELF.Receive, DefaultReaderSize);
		END InitFileReader;

	END Reader;
Усё. Тогда запихиваем поток вместо поля file и читаем из него. И плюс переопределяем нужные нам функции. Для начала, чтобы не возиться с UTF, сделаем, чтобы наш поток считал только литеры цифр, но не что либо другое. Т.е. литеры '0'..'9' двигали бы UTF8LCPos, а остальные - нет.

БудДен
Сообщения: 854
Зарегистрирован: 07.10.18 14:01

Re: Конкретно занимаемся строками

Сообщение БудДен » 19.04.20 23:31

Дуплетом идут. Экспериментальный код сейчас вот такой:

Код: Выделить всё

MODULE Prob; 

IMPORT Streams, KernelLog, Commands, UTF8Strings;


CONST 	
	DefaultReaderSize = 4096;
	CR = 0DX; (* LF = 0AX; *)

TYPE
	Reader* = OBJECT (Streams.Reader)	(** not sharable between multiple processes *)
	VAR
		donor : Streams.Reader;
		myPos : SIZE;
		(** 0 - prochitali bukvu ASCII ili sbros chtenija po oshibke, N > 0 - ostalosq N bukv do konca code point'a; -1 = EOF *)
		DecodeCharFAState : INTEGER; 

		(** MoveDecodeCharFA skopirovano s UTF8Strings.Mod/DecodeChar *)	
		PROCEDURE MoveDecodeCharFA(ch : CHAR);
		VAR ord : UNSIGNED16; newLen : CHAR;
		BEGIN
		ord := ORD(ch);
		IF ord = 0 THEN 
			DecodeCharFAState := -1
		ELSIF DecodeCharFAState > 0 THEN
			INC(DecodeCharFAState, -1)
		ELSE
			newLen := UTF8Strings.CodeLength[ord];
			IF newLen = 7X THEN (* Ne startujushijj - krivojj UTF-8 *)
				DecodeCharFAState := 0
			ELSE
				DecodeCharFAState := ORD(newLen) - 1 END END; 
		IF (DecodeCharFAState = 0)&(ch # CR) THEN
			INC(myPos) END END MoveDecodeCharFA;
			
		PROCEDURE Receive(VAR buf: ARRAY OF CHAR; ofs, size, min: SIZE; VAR len: SIZE; VAR res: INTEGER);
		BEGIN
 			donor.Bytes(buf, ofs, size, len);
 			KernelLog.String("Receive: "); KernelLog.String(buf); 
 			KernelLog.String(" "); KernelLog.Size(size);
 			KernelLog.String(" "); KernelLog.Size(min);
 			KernelLog.String(" "); KernelLog.Size(len); KernelLog.Ln;
			IF len >= min THEN res := Streams.Ok ELSE res := Streams.EOF (* end of file *) END END Receive;

		(** My nadeemsja, chto vsjo chtenie teksta idjot cherez Char i Get. Smelye my! *)
		PROCEDURE Char*( VAR x: CHAR );
		BEGIN
			SELF.Char^(x);
			MoveDecodeCharFA(x);
		END Char;

	(** Like Read, but return result. Return 0X if no success (e.g. file ended) *)
		PROCEDURE Get*( ): CHAR;
		VAR x : CHAR;
		BEGIN
			x := SELF.Get^();
			KernelLog.String("Donor.Get returned char code "); KernelLog.Int(ORD(x),0); 
			KernelLog.Ln;
			MoveDecodeCharFA(x);
			KernelLog.String("Donor.Get returned char code is now"); KernelLog.Int(ORD(x),0); 
			RETURN x END Get;

		PROCEDURE &InitMyReader*(iDonor : Streams.Reader);
		BEGIN
			ASSERT(iDonor # NIL);
			SELF.donor := iDonor;
			InitReader(SELF.Receive, DefaultReaderSize) END InitMyReader;

		PROCEDURE UTF8LCPos() : SIZE; BEGIN RETURN myPos END UTF8LCPos;

	END Reader;
	
	
	PROCEDURE W*(context : Commands.Context); 
	VAR donor : Streams.StringReader; hero : Reader; ch : CHAR;
	VAR m : ARRAY 20 OF CHAR;
	BEGIN
		NEW(donor, 8);
		COPY("bsБудk",m);
		KernelLog.String(m);
		donor.Set(m);
		NEW(hero, donor);
		LOOP
			ch := hero.Get();
			KernelLog.String("hero.Get returned char code "); 
			KernelLog.Int(ORD(ch),0);
			KernelLog.Ln;
			IF ORD(ch) = 0 THEN EXIT END;
			(* KernelLog.Char(ch); *) KernelLog.String(" "); KernelLog.Int(hero.UTF8LCPos(),0); KernelLog.Ln END;
	END W;

END Prob.W
Но обнаружилось, что при выводе KernelLog.Char(ch) при ch вне ASCII диапазона глотается не только этот ch, но и всё, что за ним, в течение достаточно длительного времени (непонятно, как оно выздоравливает).

БудДен
Сообщения: 854
Зарегистрирован: 07.10.18 14:01

Re: Конкретно занимаемся строками

Сообщение БудДен » 23.04.20 12:34

Работает CHR(числоБольше256) и берёт то ли по модулю, то ли как-то ещё. В итоге CHR(ORD(CHR('Т')))='"'

Отсутствие контроля такого поведения неправильно, если целью является внедрение юникода, поскольку оно молча работает неправильно. Но если прямо сразу ставить красный экранчик, то всё сломается. Если сделать ошибку компиляции, то ничего не скомпилируется. Если сделать предупреждение времени выполнения, то такими предупреждениями заполонится весь экран. Думаю, нужно на первом этапе сделать предупреждение для любых типов, отличных от SIGNED8 и сделать опасную версию, к-рая выдаёт предупреждение во время выполнения, которую ставить в сложных местах. А ошибку компиляции или красный экранчик можно будет сделать только после очистки кода от всех предупреждений.

Ответить