OberonCore https://forum.oberoncore.ru/ |
|
A2 в качестве командного интерпретатора https://forum.oberoncore.ru/viewtopic.php?f=22&t=6402 |
Страница 1 из 1 |
Автор: | Ярослав Романченко [ Понедельник, 27 Май, 2019 08:30 ] |
Заголовок сообщения: | A2 в качестве командного интерпретатора |
Как-то в очередной раз у меня возникла задача поменять пару строчек в нескольких файлах и я не стал использовать готовое решение , а задумал целую концепцию Думаю, если накинуть сюда ещё парочку интересных задачек и решений, может получиться материал для неплохой статьи на Хабре для популяризации А2 Что-бы решить задачу замены строк в нескольких файлах надо либо качать какую-то утилиту, либо воспользоваться средствами, предоставляемыми командным интерпретатором системы. Решение для командного интерпретатора системы мне показалось вообще страшным (что в нём вообще происходит, как понять ): Код: @echo off REM -- Prepare the Command Processor -- SETLOCAL ENABLEEXTENSIONS SETLOCAL DISABLEDELAYEDEXPANSION ::BatchSubstitude - parses a File line by line and replaces a substring" ::syntax: BatchSubstitude.bat OldStr NewStr File :: OldStr [in] - string to be replaced :: NewStr [in] - string to replace with :: File [in] - file to be parsed :$changed 20100115 :$source https://www.dostips.com if "%~1"=="" findstr "^::" "%~f0"&GOTO:EOF for /f "tokens=1,* delims=]" %%A in ('"type %3|find /n /v """') do ( set "line=%%B" if defined line ( call set "line=echo.%%line:%~1=%~2%%" for /f "delims=" %%X in ('"echo."%%line%%""') do %%~X ) ELSE echo. ) А теперь Active Oberon way. Можно читателя на Хабре познакомить со всеми нюансами кода, например, обратить внимание на перегрузку операторов для удобного вывода сообщений в лог и на разбор параметров командной строки и надёжное закрытие файлов: Код: MODULE OberonConsoleUtils; И вот, как это можно использовать. Например, если-бы вы разрабатывали что-то в среде CodeTyphon (как я в своё время разработал CGI движок своего сайта), вы бы могли столкнуться с ситуацией, когда автор CodeTyphon взял и поменял расширения для некоторых типов файлов в CodeTyphon... и, естественно, какие-то файлы на какие-то ссылаются. В общем, расширения поменять не проблема, но ссылки поправить уже придётся руками. Автор CodeTyphon не предусмотрел ни какой конверсии между версиями проектов.(** AUTHOR "Yaroslav Romanchenko (SAGE) http://sage.com.ua/ rapturize@gmail.com"; PURPOSE "Different utilities usefull for console"; *) IMPORT Commands, Options, Files, Strings, Streams; CONST CRLF = ''0DX''0AX''; TYPE String = Strings.String; Chars = ARRAY OF CHAR; OPERATOR "+"(CONST a1, a2: Chars): String; VAR str: String; BEGIN NEW(str, Strings.Length(a1) + Strings.Length(a2) + 1); Strings.Concat(a1, a2, str^); RETURN str END "+"; OPERATOR "+"(i: HUGEINT; CONST a2: Chars): String; VAR a1: ARRAY 32 OF CHAR; str: String; BEGIN Strings.IntToStr(i, a1); NEW(str, Strings.Length(a1) + Strings.Length(a2) + 1); Strings.Concat(a1, a2, str^); RETURN str END "+"; VAR bSilent: BOOLEAN; PROCEDURE Message(log: Streams.Writer; strMessage: String); BEGIN IF ~bSilent THEN log.String(strMessage^) END END Message; PROCEDURE ReadFile(log: Streams.Writer; CONST aFileName: ARRAY OF CHAR; VAR iLength: LONGINT): String; VAR file: Files.File; reader: Files.Reader; str: String; BEGIN str := NIL; file := Files.Old(aFileName); IF file = NIL THEN Message(log, 'Bad file name: "' + aFileName + '"' + CRLF) ELSE NEW(str, file.Length() + 1); Files.OpenReader(reader, file, 0); reader.Bytes(str^, 0, LEN(str^), iLength) END; RETURN str FINALLY IF file # NIL THEN file.Close() END END ReadFile; PROCEDURE WriteFile(log: Streams.Writer; CONST aFileName, aContent: ARRAY OF CHAR; iPos, iLength: LONGINT); VAR file: Files.File; writer: Files.Writer; BEGIN Message(log, 'Writing file "' + aFileName + '" ...' + CRLF); file := Files.New(aFileName); IF file = NIL THEN Message(log, 'Unable to create file: "' + aFileName + '"' + CRLF) ELSE Files.OpenWriter(writer, file, 0); writer.Bytes(aContent, iPos, iLength) END FINALLY IF file # NIL THEN writer.Update(); Files.Register(file); file.Close() END END WriteFile; PROCEDURE StringReplace(CONST aString, aOldPat, aNewPat: ARRAY OF CHAR; bIgnoreCase: BOOLEAN; VAR nReplaced: LONGINT): Strings.Buffer; VAR length: LONGINT; from, pos, posLast: SIZE; buffer: Strings.Buffer; writer: Streams.Writer; BEGIN length := LEN(aString); NEW(buffer, length + length DIV 16); writer := buffer.GetWriter(); from := 0; posLast := -1; nReplaced := 0; REPEAT pos := Strings.GenericPos(aOldPat, from, aString, bIgnoreCase, FALSE); IF pos >= 0 THEN posLast := pos; INC(nReplaced); IF pos > 0 THEN writer.Bytes(aString, LONGINT(from), LONGINT(pos - from)) END; writer.Bytes(aNewPat, 0, Strings.Length(aNewPat)); from := pos + Strings.Length(aOldPat) ELSIF posLast >= 0 THEN (* Write tail *) writer.Bytes(aString, LONGINT(from), LONGINT(length - from)) END UNTIL pos < 0; IF nReplaced = 0 THEN (* Nothing replaced, just copy *) writer.Bytes(aString, 0, LONGINT(length)) END; RETURN buffer END StringReplace; (** Usage: ConsoleUtils.ReplaceStringInFile --old="old string" --new="new string" <Input file> [<Ouput file>] *) PROCEDURE ReplaceStringInFile*(context: Commands.Context); VAR bOptionsOk, bIgnoreCase: BOOLEAN; options: Options.Options; aOldPat, aNewPat: ARRAY 256 OF CHAR; aFileNameIn, aFileNameOut: Files.FileName; strContent: String; buffer: Strings.Buffer; nRead, nReplaced: LONGINT; BEGIN NEW(options); options.Add("o", "old", Options.String); options.Add("n", "new", Options.String); options.Add("s", "silent", Options.Flag); options.Add("c", "case", Options.Flag); (* case sensitive *) bOptionsOk := options.Parse(context.arg, context.out) & options.GetString("old", aOldPat) & options.GetString("new", aNewPat); IF bOptionsOk THEN context.arg.SkipWhitespace(); context.arg.String(aFileNameIn); bOptionsOk := aFileNameIn # "" END; bSilent := options.GetFlag("silent"); bIgnoreCase := ~options.GetFlag("case"); IF ~bOptionsOk THEN Message(context.out, CRLF + "Required parameters not provided. Usage:" + CRLF + 'ConsoleUtils.ReplaceStringInFile --old="old string" --new="new string" <Input file> [<Ouput file>]' + CRLF) ELSE context.arg.SkipWhitespace(); context.arg.String(aFileNameOut); strContent := ReadFile(context.out, aFileNameIn, nRead); IF strContent # NIL THEN Message(context.out, 'File "' + aFileNameIn + '" opened, ' + nRead + ' bytes read.' + CRLF); buffer := StringReplace(strContent^, aOldPat, aNewPat, bIgnoreCase, nReplaced); Message(context.out, nReplaced + ' occurences of pattern "' + aOldPat + '" found.' + CRLF); IF aFileNameOut # "" THEN WriteFile(context.out, aFileNameOut, buffer.GetString()^, 0, buffer.GetLength() - 1) ELSE WriteFile(context.out, aFileNameIn, buffer.GetString()^, 0, buffer.GetLength() - 1) END END END END ReplaceStringInFile; BEGIN END OberonConsoleUtils. System.Free OberonConsoleUtils ~ И вот, теперь уже с применением А2 и моей небольшой утилиты можно так эту проблему решить: Код: rem Convert project from CT v. 5.XX to CT v. 6.XX rename *.lpi *.ctpr rename *.lpr *.ppr rename *.lfm *.frm del oberon.txt for %%G in (*.pas) do echo/OberonConsoleUtils.ReplaceStringInFile -o=*.lfm -n=*.frm %%G ~>>oberon.txt for %%G in (*.ctpr) do echo/OberonConsoleUtils.ReplaceStringInFile -o=.lpr -n=.ppr %%G ~>>oberon.txt echo/System.PowerDown ~>>oberon.txt oberon.exe run oberon.txt Код: rem Convert project from CT v. 6.XX to CT v. 5.XX В итоге, для запуска такого батничка понадобится лишь oberon.exe и скомпилированный OberonConsoleUtils. rename *.ctpr *.lpi rename *.ppr *.lpr rename *.frm *.lfm del oberon.txt for %%G in (*.pas) do echo/OberonConsoleUtils.ReplaceStringInFile -o=*.frm -n=*.lfm %%G ~>>oberon.txt for %%G in (*.lpi) do echo/OberonConsoleUtils.ReplaceStringInFile -o=.ppr -n=.lpr %%G ~>>oberon.txt echo/System.PowerDown ~>>oberon.txt oberon.exe run oberon.txt В случае 32-х разрядной А2 oberon.exe будет занимать 952 Кб, а OberonConsoleUtils.GofW - 8,44 Кб Неплохой такой контраст в мире гигабайтных сред разработки Ведь можно утилиты и посерьёзнее написать, чем замена строк в файлах... Что скажете, какие ещё идеи предложите? PS. А в качестве среды разработки таких утилит можно предложить WinA2Mini, собранную из последних исходников, которая в 7z архиве всего 9,12 Мб весит. |
Автор: | budden [ Понедельник, 27 Май, 2019 11:37 ] |
Заголовок сообщения: | Re: A2 в качестве командного интерпретатора |
Идея использовать мини-А32 хороша. А вот спроектировать хороший командный интерпретатор - это задача очень сложная. Во всяком случае, у меня пока есть только определённые намётки. Проблема в том, что есть традиции. Они зачастую плохие (вообще путь юникс - это кучка протекающих абстракций). Но беда в том, что они удобны и наступание на грабли происходит недостаточно часто, чтобы отказаться от них. И, главное, они стандартизированы. Кроме того, идеологически командные интерпретаторы астрономически далеки от привычных парадигм программирования, таких, как ООП или асинхронное программирование. |
Автор: | Ярослав Романченко [ Понедельник, 27 Май, 2019 11:44 ] |
Заголовок сообщения: | Re: A2 в качестве командного интерпретатора |
budden писал(а): Кроме того, идеологически командные интерпретаторы астрономически далеки от привычных парадигм программирования, таких, как ООП или асинхронное программирование. Как вариант, такие скрипты с минимальной традиционной обвязкой, как я привёл, зато с возможностью подключить "тяжёлую артиллерию" в виде A2 с задействованием мощи процессора на полную катушку с параллельными потоками и т.д. и скоростью компилированного кода.
|
Автор: | Comdiv [ Понедельник, 27 Май, 2019 13:45 ] |
Заголовок сообщения: | Re: A2 в качестве командного интерпретатора |
На хабре Вам сразу расскажут про sed, awk, python и powershell. |
Автор: | Ярослав Романченко [ Понедельник, 27 Май, 2019 13:48 ] |
Заголовок сообщения: | Re: A2 в качестве командного интерпретатора |
Comdiv писал(а): На хабре Вам сразу расскажут про sed, awk, python и powershell для Windows. так они ж большие и тяжёлые
|
Автор: | Comdiv [ Понедельник, 27 Май, 2019 13:58 ] |
Заголовок сообщения: | Re: A2 в качестве командного интерпретатора |
sed ~ 70Kb, awk ~ 700 Kb, а python и powershell всё равно идут в комплекте в соответствующих ОС. Я, кстати, только что установил powershell на Ubuntu 16.04. Лёгкость установки компенсирует размер пакета, а лишний десяток мегабайт сейчас никого не испугает, там не гигабайты. |
Автор: | Ярослав Романченко [ Понедельник, 27 Май, 2019 14:40 ] |
Заголовок сообщения: | Re: A2 в качестве командного интерпретатора |
Comdiv писал(а): sed ~ 70Kb, awk ~ 700 Kb, а python и powershell всё равно идут в комплекте в соответствующих ОС. Я, кстати, только что установил powershell на Ubuntu 16.04. Лёгкость установки компенсирует размер пакета, а лишний десяток мегабайт сейчас никого не испугает, там не гигабайты. Ну, вот, хорошо, что на родной оберонкоре сначала запостил. Оберонщики сильно бить не будут
|
Автор: | budden [ Вторник, 28 Май, 2019 18:25 ] |
Заголовок сообщения: | Re: A2 в качестве командного интерпретатора |
Насколько я понял показанный код - это всё же колхоз. При этом - да, идея использовать нормальный язык в качестве командного интерпретатора - это очень хорошая идея. Есть, к примеру, wish, который умеет показывать окошки, модальные или немодальные, но в нём нет & и > из bash. А чтобы был и с окошками, и с перенаправлением, и компилируемые - вот это была бы конфетка. |
Автор: | Ярослав Романченко [ Вторник, 28 Май, 2019 19:41 ] |
Заголовок сообщения: | Re: A2 в качестве командного интерпретатора |
budden писал(а): колхоз Колхоз ни колхоз, а скорее, некий прототип.Я пробовал сделать такой же батник, без создания временного файла что-бы передавать параметры в oberon.exe, например, через pipe, но пока что-то не срабатывает. Хотя, oberon.exe умеет общаться через pipe. Как-то так: Цитата: (for %%G in (*.pas) do echo/ OberonConsoleUtils.ReplaceStringInFile -o=*.lfm -n=*.frm %%G Ведь в данном случае хорошо сформировать все команды к oberon.exe в один большой пакет, а не дёргать его каждый раз. Не правда-ли? for %%G in (*.ctpr) do echo/ OberonConsoleUtils.ReplaceStringInFile -o=.lpr -n=.ppr %%G echo/ System.PowerDown )|oberon.exe budden писал(а): модальные или немодальные просто диалоговые окошки для ввода параметров? В чём сложности? Микро A2 можно под это приспособить
|
Автор: | Ярослав Романченко [ Вторник, 28 Май, 2019 19:54 ] |
Заголовок сообщения: | Re: A2 в качестве командного интерпретатора |
Можно сформировать один исполнимый файл и с минимальной обвязкой он будет исполнять всякие команды, показывать диалоги... и т.д. и даже при этом запустившись всего один единственный раз за всю сессию выполнения скрипта. |
Автор: | budden [ Вторник, 28 Май, 2019 23:10 ] |
Заголовок сообщения: | Re: A2 в качестве командного интерпретатора |
См. как устроен wish. Либо ActiveTcl, либо в Linux просто команда wish. Не нужно никаких передач. A2 сам должен всё это делать - запускать программы, создавать трубы и прочая. Хотя, честно сказать, это задача тяжёлая и стрёмная. Например, Powershell вроде работает под Linux, но говорят, что слишком большая доля его полезности связана с наличием интерфейсов к виндоус-специфичным возможностям, соответственно в Linux получается мимо кассы. |
Автор: | Ярослав Романченко [ Четверг, 12 Сентябрь, 2019 13:22 ] | ||
Заголовок сообщения: | Re: A2 в качестве командного интерпретатора | ||
Вот, ощутив в очередной раз потребность в простой утилите для решения своей конкретной задачи, ещё немного расширил библиотечку командных утилит функцией преобразования кодировок. Потребность была в преобразовании кучи паскаль исходников из кодировки 1250 в UTF-8 с BOM. Хоть 1250 и не кириллическая, быстро подшаманил её в свой модуль CyrillicUtilities, благо он в плане добавления кодировок очень легко расширяем. Попутно нашёл в каком месте декодеры превращали все окончания строк из Windows в Unix и для 1250 это исправил. Это конечно можно считать дефектом, но лучше, вероятно, расширить интерфейс кодеков возможностью конвертации Windows <-> Unix. И ещё попутно втемяшил добавление BOM. Тоже можно сделать опцией. Но, поскольку, теперешний интерфейс кодеков не предполагает никаких опций пришлось ввести на скорую руку. Может сообщество изволит опять закидать камнями. Можно бесконечно всё, конечно, обсуждать... Но, для продвижения Оберонов надо именно что-то делать. Вот, такой вот вырисовывается "чемоданчик" утилит. Уже с двумя функциями
|
Автор: | Ярослав Романченко [ Воскресенье, 22 Сентябрь, 2019 02:09 ] | ||
Заголовок сообщения: | Re: A2 в качестве командного интерпретатора | ||
1. Убрал зависимость модуля Codecs от Unzip (благодаря этому для работы утилиты теперь не нужны модули Unzip и Inflate) 2. Изменения в CyrillicUtilities интегрировал в репозиторий 3. Исправил интересный дефект из-за которого всё работало как-то через раз... Оказывается, если запускать батники, например, из TotalCommander, буква дисковода в путях будет в верхнем регистре, если запускать из терминального окна, буква дисковода будет в нижнем регистре. Это, оказывается, хорошо известная проблема в Windows. Поскольку для A2 регистр символов важен, это вызывало проблемы. Вот, можете позапускать такой простой батник из разных мест и посмотреть, какая будет буква диска: Код: echo %~dp0
pause
|
Страница 1 из 1 | Часовой пояс: UTC + 3 часа |
Powered by phpBB® Forum Software © phpBB Group https://www.phpbb.com/ |