Skip to content

yurybotov/trac

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

75 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ВНИМАНИЕ! Реально работает только javascript версия и немного esp8266. Остальное в зачаточном состоянии.

Данный проект - попытка реализовать язык программирования Trac Кельвина Муэрса.

Кельвин Муэрс более известен в среде хранения и доступа к данным. Он сформулировал Закон Муэрса: «Степень использования информации прямо пропорционально легкости ее получения». «Система получения информации окажется невостребованной, если обладание информацией будет вызывать у клиента больше неудобств и беспокойства, чем ее отсутствие.»

Концепция языка родилась в 1959, первая реализация была осуществлена в 1964 году на PDP-11 в сотрудничестве с Питером Дойчем. В 1965 вышла статья Муэрса с описанием минимального языка и его интерпретатора (TRAC, A Procedure-Describing Language for the Reactive Typewriter) В интернете эта статья есть, однако в ней есть существенные ошибки и относиться к ней надо с аккуратностью - включив голову: http://web.archive.org/web/20050215092544/http://tracfoundation.org/trac64/procedure.htm мой вольный перевод этой статьи лежит в папке doc. Сайт сообщества Trac сейчас не доступен (ранее был), однако он сохранился в архивах:
http://web.archive.org/web/20050213111043/http://tracfoundation.org В дальнейшем язык активно развивался пока не столкнулся со стандартной для Америкацев проблемой права. Муэрсу пришлось судиться с сообществом R.E.S.I.S.T.O.R.S. которое продвигало похожий по концепции язык SAM-76. В результате оба языка почивали в бозе. Лично я познакомился с Trac в книге "Этюды для программистов" Чарльза Уэззерела.

Но это все лирика. Нафига я этим занялся? Дело в том, что Trac славится тем что у него САМЫЙ ПРОСТОЙ интерпретатор. После BraynFuck конечно. Но BF это уже извращение, а Trac - вполне себе макроязык. Описание интерпретатора есть в той самой статье в интернете. Кое что придется подчерпнуть и из соседних описаний (например формат записи в базе forms). То есть реализовано из чистого любопытства, безо всякой нужды. Вообще, прикольно видеть Json в языке 1964 года :) еще прикольнее видеть в нем Unicode c русскими именами команд и строк. Реализация вроде как "обратно совместимая", то есть все исходные программы по идее на ней должны работать. Под занавес: не пугайтесь синтаксиса. Примеры с моей точки зрения выбраны крайне не удачно, да еще и ошибки в них есть. На самом деле это обычный макроязык: идет-идет куча обычного текста, в нужном месте - вставка макроязыка - потом опять куча текста и так далее... Годится например для массированной впечатки адресов и обращений в письма (не даром: Reactive Typewriter - 1965 год), автоматизированного перевода. А если серьезно, то на нем можно даже web-сервер соорудить. Ну производительность то понятно... интерпретатор-же.

Краткие пояснения по языку:

Язык работает со строками. Других типов данных нет. Арифметика тоже работает именно со строковыми представлениями чисел. Язык это набор функций. Вид функций: #(имяфункции,0-или-больше-аргументов-через-запятую). Пробелы имеют значение. То есть #(ab) и #( ab) это две разные функции. То же и с аргументами. Текст вне функций напрямую передается на выходное устройство...но не без фокусов. То есть программа "Hello world#(ps,!)" напечатает именно то что мы и ожидаем, но не так как мы ожидаем: "!Hello world".
Напрягает? Нет проблем, напишите так: "Hello world `#(ps,!)`" или, что правильнее: "#(ps,Hello world)#(ps,!)" и все будет на местах. Мета-символ позволяет наводить порядок в записях. К слову. Кодировка изначально ASCII, можно использовать только первые 127 символов. Остальные просто игнорируются (за исключением неких спецсимволов). Также во входном потоке игнорируются CR, LF и TAB. Но! Поскольку изначально реализовано оно у меня на F#, а там сплошной Юникод, я не стал отказывать себе в удобствах и работаю в Юникоде. А потому - русский (да хоть хинди!) текст, русские имена функций и переменных - к вашим услугам. В качестве спецсимволов использую гендерные символы марса и венеры, какую-то арабскую скобку, и две вертикальные черты из псевдографики. Думаю вы не часто их используете в реальном тексте. Но есть символы с которыми в тексте приходится быть аккуратнее: "#(" - ясно это признак перехода на макроязык, "(...)" - тоже ясно, "," - внутри строк макроязыка будет восприниматься как переход к следующему параметру, "{" и "}" - нельзя использовать в запоминаемых в хранилище на диске данных и функциях - там JSON, он этого не простит. Я обычно просто перекодирую их во входных текстах и текстах программ куда нибудь в '\ufffx', а потом перекодирую полученную строку обратно. Если записать #(ab)#(cd) две функции вызовутся последовательно слева направо, а их результаты в том же порядке отпечатаны в выходном потоке. Если записать #(ab,#(cd)) то сначала вызовется cd, вернет строку, а эта строка передастся как аргумент к ab которая выполнится и ее выходная строка будет записана выходной поток. Если записать #(ab,(#(cd))) то немного сложнее. Экранирование. Те есть #(ps,#(ad,1,1)) это не тоже что #(ps,(#(ad,1,1))). Средние скобки как бы скрывают внутреннее выражение от выполнения поэтому первое выражение выдаст на печать "2", а второе выражение "#(ad,1,1)". Эта фишка применяется при определении именованных функций и обработке исключений. Так #(ps,##(np,(#(dv,4,2,#(ps,Ошибка деления)0)))) ##(np,) в нейтральном режиме, вернет "#(dv,4,2,#(ps,Ошибка деления)0)", а вот при #(np,)в активном режиме #(ps,#(np,(#(dv,4,2,#(ps,Ошибка деления)0)))) результат будет "2". Веселее другое, в нейтральном режиме: ##(ps, result=##(dv,10,1,(##(ps,error:)9999))) вернет... " result=10", все ясно, НО если при выполнении произошла ошибка, например деление на ноль: ##(ps, result=##(dv,10,0,(##(ps,error:)9999))) функция автоматически становится активной и выполняет код (##(ps,error:)9999) в результате "error: result=9999". То есть все функции принимающие параметр Z и вернувшие его из-за ошибки автоматически становятся активными. Я специально внедрил в код строки данных чтобы показать как это делается. Вообще, разница между активными (#) и нейтральными (##) функциями проявляется только при работе с кодом: активные функции вычислив значение отправляют его опять на выполнение а нейтральные - нет. По отношению к данным ( которые не являются сами кодом) они ведут себя одинаково не зависимо от количества '#'.

Свои макросы объявлять тоже можно просто сохраняя их в forms через #(ds,имя,(тело)) как данные, после этого их надо немного "подшлифовать напильником" с помощью #(ss,имя1парам,имя2парам...) а вызываются они через #(cl,имя,подставляемые,параметры) - cl должна быть в активном режиме! Будет работать и #(имя,подставляемые,параметры), но внутри определения рекурсивных функций надо использовать полную запись ;) Давайте подменим функцию печати #(ps,X):
#(ds,печать,(#(ps,X)))#(ss,печать,X))) после выполнения этого кода #(печать,русский-текст) напечатает "русский-текст". Макросы и переменные можно сохранять на диске а потом загружать обратно в хранилище форм.

Типовые операции:

присваивание константы var name = value : #(ds,name,value)`

использование переменной name : #(name)

инкремент переменной name++ : #(ds,name,#(ad,#(name),1)`

присваивание результата выражения name <- expression : #(ds,name,expression)`

определение функции func name(param1, param2) <- expression : #(ds,name,(expression))#(ss,param1,param2)`

вызов функции name(param1,param2) : #(name,param1,param2)`

условное ветвление if(condition,trueexpression,falseexpression) : #(eq,condition,0,falseexpression,trueexpression)`

цикл по условию while(condition, expression) : #(ds,tempname,(#(eq,condition,0,expression#(tempname),)))`#(tempname)`#(dd,tempname)`

цикл по счетчику for(startcount,endcount, expression) : #(ds,tempvar,(startcount))#(ds,tempname,(#(eq,tempvar,endcount,,expression#(ds,tempvar,#(ad,#(tempvar),1)))#(tempname))))`#(tempname)`#(dd,tempvar,tempname)`

Замечания по текущей реализации на JS:

  • не реализованы диагностические функции - не придумал еще как удобнее сделать трассировку
  • не работает и не будет работать сохранение на диск, позднее вместо него организуем хранение в localstorage
  • не работает работа с битами. Можно конечно потом сделать ее, однако реализаци битовых операций в языке не самая удачная и может быть будет правильнее реализовать их как то по другому.
  • добавлен преобразователь из языка простейшего функционального калькулятора в trac через RPN

About

trac interpreter

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published