MSL-CE Новое начало
mikelsv
MSL умер, да здравствует msl. MSL-FL (SP) на фоне очевидных ошибок с тормозами и отсуствием преобразования в быстроисполняемый код прекращает свое существование и больше не будет развиваться. Первая буква из SP расшифровывалась как Slow, и оказалась неким предсказанием.
Новая версия MSL называется Compile Edition и планируется, что она не только будет конвертироваться в байт код, но и компилироваться в исполняемый. Версия разрабатывается с нуля, но использует наработки в разборе кода.
Теперь, всесто одного шага: разбор и выполнение кода, будут 3 шага: разбор, создание байт кода, выполнение. Байт код будет напоминать ассемблерные команды, будет стек для хранения переменных и промежуточных значений. Скорость исполнения кода возрастет, а при компиляции вообще взлетит.
Тема компиляции тема отдельная. Код будет преобразован в код на c++, ибо там уже есть все готовое включая оптимизации и нет смысла заморачиваться со своим компиляторов. Полагаю логичнее было бы преобразовывать код, а не байт код, В первом случае будет получен код на c++ в более вменяемом виде, что даст возможность использовать его где-то еще.

Разбор кода взят из MSL-FL, сейчас разрабатывается преобпразование в байт код. Код, над исполнением которого я работаю: 1+2;

MSL. Пара слов о состоянии проекта.
mikelsv
MSL не остался валяться без дела, а используется в одном проекте, позволяя вытащить часть кода из программы и модифицировать его по своему желаню. И это очень круто, явно круче неповоротливой программы с некоторым количеством настроек.
Отладка программ под MSL вышло довольно интересным, когда еще не компилятор указывает тебе на ошибки, а ты ему. Пишешь правильный код, запускаешь, а он не работает, и начинаешь разбираться почему. Из самых главных ошибок: деление умножало, сложение не складывало, != не работало, ну и куча остальных косяков.
Так же MSL обрастал функционалом под задачи. Например потребовалось семейство reset(), current(), prev(), next() и end() для прохождения по массиву. Так же добавился strlen(), sizeof() и unset(). В какой-то момент была реализована функция array(), скорее, чтобы было. => еще нет. Так же есть print_r(), позволяющая вывести весь массив и понять, что в нем. Добавлена include(), чтобы не писать кучу кода в каждом файле. Так же реализованы: substr(), clock(), time(), date(). Возможность добавлять свои функции позволяет передавать данные из программы в msl и обратно.
Тип данных в msl это динамический массив, каждая ячейка содержит указатель на такой же массив. Ключ и значение являются строками. Все абсолютно просто. Никаких других типов данных.
Тормоза, они более чем очевидны. Без псевдокода никуда. В будущем будет обязательно запилен. Так же тормозит копирование, например в коде $data; уже есть копирование данных из $data. В коде $id=current($data); while($id){ ... $id=next($data); ... }  куча бесполезного копирования. Когда в $data порядка тысячи элементов тормоза выходят за пределы разумного. Сейчас отсутствие псевдокода и полное копирование массива создают главные проблемы в скорости выполнения. Хотя копирование все же больше.

А в остальном язык вполне себе стабилен и неплох. Внезапный баг с утечкой памяти был исправлен. Функциионал потихоньку растет. Можно пользоваться. Например в небольших программках, куда php не потащишь, а msl спокойно прицепляется.

Это четвертая версия языка. Самая стабильная, но пока еще не самая шустрая. Но все впереди.

MSL FL (SP) - Компилятор своими руками. Шаг 9. Выход в Web
mikelsv
Это будет не слишком крутое обновление, но важное по значимости. Добавлены скобки, а то представьте не было. И добавлено определение строк в случаях ошибок. Плюс поправлены мелкие баги. Главное обновление - возможность подключаться к веб серверу как CGI.

Например веб сервер Apache подключается так в httpd.conf:
AddType application/x-httpd-msl .msl
AddHandler application/x-httpd-msl .msl

ScriptAlias /msl/ "/usr/local/apache/cgi-bin/"

<Directory "/usr/local/apache/cgi-bin/">
  AllowOverride None
  Options +ExecCGI
  order deny,allow
  allow from all
</Directory>

Action application/x-httpd-msl "/msl/msl-fl"

И главное работает. Теперь на msl можно писать сайты.

MSL FL (SP) - Компилятор своими руками. Шаг 8. Функции? Дайте две!
mikelsv
В каждом языке рпограммирования обязаны быть функции, у нас еще нет? Значит делаем! Погнали.

Задача: function test($k, $v){ print('Key: '.$k.' and val: '.$v); } test('box', 'gold');

Очевидно идем в DoCodeFunction() занимающийся обработкой всех функций. И прямо после проверки имена на "for" добавляем проверку на "function":
if(name=="function"){
     DoCodeFunctionFunction(line, to); return ;
}

И вся дальнейшая обработка происходит в этой функции. Кстати, все посты пишутся параллельно с разработкой, и я каждый раз сам не знаю чем закончится пост и какой будет реализация.
Относительно функций, так как код исполняется линейно, функции обязаны быть описаны раньше, чем будут вызваны.

Разбор функции довольно прост. Определяем имя, параметры ни что иное как $val, хм, параметры по умолчанию это в принципе только текст. Дальше ставим флаг активности в ноль и DoCode() ищет от { до }.

Добавлю функцию _skipspace(), она будет пропускать неиспользуемые символы, просто перенес этот код в ункцию:
while(line<to && (*line==' ' || *line=='\t' || *line=='\r' || *line=='\n')) line++;

Скоро понадобится определять положение ошибок, и подсчитывать переходы строк. Функция пригодится.
Добавляю новый класс для хранения фунций: msl_function; и добавляю в msl: msl_function functions;

Функции выполняются в DoCodeFunctionExec(). Ищем функцию подходящую по названию и количеству параметров, переопределяем переменные окружения, устанавливаем входящие параметры, запускаем в DoCode();
Для решения задачи потребовалось создать в msl: msl_value *local_value; указывающую на текущую переменную хранящую значения. При вызове функции мы создаем новую msl_value и указываем в local_value на нее.

Код готов, отлажен и работает.  В DoCodeOneValue() нашлась тонна косяков, поправлены. Довольно интересное занятие писать примеры, а потом искать, почему они не работают.
В msl нашелся один неприятный момент. обьявление функции нужно закрывать ';', иначе оно будет записано как значение и будет конфликтовать со следующим кодом. Хотя, добавим переменную в DoCodeFunction(..., int &df), которая будет 1 если это обьявление. Проблема решена.

Все проблемы решены. Больше нас не смогут обвинить в отсутствии функций. Но могут в отсутствиии доступа к глобальным переменным. И в принципе, возможно стоит сделать его так же как в php, чтобы не упускать такую возможность, как переносимость кода.

На этом все. В ближайшем будущем подыщу задачу для реального использования, дабы уменьшить количество багов на единицу кода.
В следующей серии: global? class? внешние функции? отладчик? котики!

"Проект качать отсюда: anon:anon@svn://svn.loglist.org/usr/svn/opensource/msl-fl/ и anon:anon@svn://svn.loglist.org/usr/svn/opensource/msvlib . Исходный код будет тут: http://pastebin.com/Z9tF98TJ. Версия: #define USE_MSLFL_VER 8 "

MSL FL (SP) - Компилятор своими руками. Шаг 7. Жизненные приоритеты
mikelsv
Я продолжаю свою увлекательную разработку языка программирования. И мы уже на финишной прямой к первой контрольной точке, после которой языком можно вполне полноценно пользоваться. Не хватает только операций. Другое дело, что без функций и классов не особо то развернешься, но ограничений на использование уже не останется.

Немного истории. MSL начал свое существование в 2005 году. Первая его версия была ужасна, много указателей, нестабильности и падений. Код разбирался по частям в динамические массивы и потом выполнялся. Массивы копировались и выполнялись путем объединения элементов, типа [ $i, '+', '1' ]. Писалась эта версия кажется пол года. Ничего так, не больно то много для языка. Вторая версия появилась тоже где-то в 2005 году, уже стабильнее, но все равно количество проблем и падений зашкаливало. Стоило заметить, что язык писался не просто для развлечения, а для выполнения конкретных задач. Такие языки подходят для написания локальных скриптов, которым очевидно нечего делать в самой программе. Так msl использовался в программе сканирования документов, выполняя некоторые операции, такие как выбор названия для файла и определение, куда его положить. Так же на msl писалась своя википедия и она до сих пор где-то работает. То есть плюсы языка очевидны: вытащить из программы лишний код и дать возможность пользователям с ним работать и настраивать по собственному желанию.
Третья версия msl начала свое существование 22.05.2009. Стабильная, работающая, но с использованием не задалось и проект оставили в покое.
Данную версию msl можно считать четвертой. На ее разработку до тех же возможностей потрачено 5 дней. Думаю, стоило разрабатывать по пол года глючные версии, для того, чтобы в один прекрасный момент быстро и просто написать хорошую и стабильную.

"Проект качать отсюда: anon:anon@svn://svn.loglist.org/usr/svn/opensource/msl-fl/ и anon:anon@svn://svn.loglist.org/usr/svn/opensource/msvlib . Исходный код будет тут: http://pastebin.com/yJ1mzqfL . Версия: #define USE_MSLFL_VER 7 "

Операции: +-/*.!<> , все они должны обрабатываться и желательно в правильном порядке. Хотя есть мысль, что нам это не грозит. Но давайте попробуем.
Не буду писать примеры для реализации, сразу иду в DoCode() и добавляю в обработчик все знаки операций. Реакцию на знаки можно поделить на 5 частей: '=' - присвоение, выполняем код с права, записываем в переменную. Знак '!' - можно применять сразу, но то, к чему применяется отрицание нужно еще получить. Сделаем переменную для этого значения. Дальше операция '++' , тут либо применять сразу, либо как с '!'. И последние, даже можно объединить: '+' и '==' - операции между двумя переменными. Причем с приоритетами, что беда.
Пришло время сделать решительный шаг и пожертвовать обещанной линейностью. Оставить можно, но код будет выполнять операции не по приоритетам, а в порядке следования. Что кстати неплохо, быстрее будет работать. Но этот момент создаст столько ошибок, что нас запинают абсолютно все.
Делаем так, а заодно и проверок добавим. Каждое получаемое значение в DoCode() записываем в список, в конце выполняем операции. Останется только одно значение.

Пишем код... Смотрю приоритеты операций, '()' забыл.

Есть. Не совсем удачный код, на мой взгляд, придется вылизывать в будущем. Но вполне работающий. Обновилось секция // operators в DoCode() и добавилась функция DoCodeOneValue(), выполняющая операции, пока не останется одно значение. Ну и мелкие доработки. Все изменения можно наблюдать в репозитарии. Тестирую на msl.Do("<? $i=13; $j=15; $i++; --$j; print($i, ' . ', $j); ?>"); По минимуму. Эта задача переносится на следующий шаг.

С начала разработки прошло 5 дней. За которые получен язык, который уже можно взять и использовать.
Здесь все.

MSL FL (SP) - Компилятор своими руками. Шаг 6. Покончить с общей логикой
mikelsv
"Проект качать отсюда: anon:anon@svn://svn.loglist.org/usr/svn/opensource/msl-fl/ и anon:anon@svn://svn.loglist.org/usr/svn/opensource/msvlib . Исходный код будет тут: http://pastebin.com/QG3Kn9gr . Версия: #define USE_MSLFL_VER 6 "


У нас похоже сдох ЖЖ и я уже не помню, что обещал сделать в следующей серии.  For,  если не ошибаюсь. С него и продолжим. Реализуем выполнение вот этого нехитрого кода <? print('Hello World!!!'); for($i=55; $i; $i--){ print('1'); } ?>

Не стану переписывать if() и while(), они и так хорошо работают. Лучше вынесу обработку параметров в отдельную функцию DoCodeFunctionArgs();  И перед вызовом этой функции добавляем  обработчик for if(name=="for"){ DoCodeFunctionFor(line, to); }

Пишем обработчик аргументов функции, проверяем, что их 3. Не забыв отключить внесение изменений  do_opt_active=0; Теперь анализируем код цикла.
Дальше выполняем код  первого параметра. И в цикле крутим выполнение второго параметра с проверкой на false, выполняем код цикла, выполняем третий параметр.
Не кажется ли вам, что у нас слишком много анализаторов кода, в if(), в while(), третий раз писать для for() совсем не улыбается. Но все-таки похоже они разные, оставим пока так.
Код готов:
       int DoCodeFunctionFor(unsigned char *&line, unsigned char *to){
          // save values
          int old_active=do_opt_active; do_opt_active=0;
          // for args
          VString a[4]; int as=0;

          while(!do_opt_stopit && as<4){
                 msl_value val;
                 a[as].data=line;
                 DoCode(line, to, val, ';', ')');
                 a[as].sz=line-a[as].data; as++;

                 if(line>=to){ SetError("not found ')'. EOF"); return 0; }
                 if(*line==')') break;
                    line++;
          }
          line++;

             if(as!=3){
                 SetError("for(args!=3)."); return 0;
             }

          // Do {}
             msl_value val; unsigned char *lline, *elline, *tline, *eline; unsigned char endcode;

             lline=line;
             if(*(line)=='{'){ endcode='}'; line++; } else endcode=';';
          DoCodeMulti(line, to, val, endcode); line++;
          elline=line;

          // load old value
          do_opt_active=old_active;

          // Go or Nooo...
          if(old_active){
                 // do for(1)
                 tline=a[0].data; eline=a[0].endu()+1;
                 DoCode(tline, eline, val, ';');

                 while(1){
                        // do for(2), test
                        tline=a[1].data; eline=a[1].endu()+1;
                        DoCode(tline, eline, val, ';');
                        if(!val.val || val.val=="0") break;
                
                        // do {}
                        tline=lline; eline=elline;
                        if(*(tline)=='{'){ endcode='}'; tline++; } else endcode=';';                 
                        DoCodeMulti(tline, eline, val, endcode);

                        // cbr
                        if(do_opt_cbr){
                               if(do_opt_cbr==MSLCBR_CONT){ do_opt_cbr=MSLCBR_NULL; }
                               if(do_opt_cbr==MSLCBR_BREAK){ do_opt_cbr=MSLCBR_NULL; break; }
                               if(do_opt_cbr==MSLCBR_RET){ break; }
                        }

                        // set value
                        do_opt_active=old_active;

                        // do for(3)
                        tline=a[2].data; eline=a[2].endu()+1;
                        DoCode(tline, eline, val, ')');                      
                 }
          }

          // load old value
          do_opt_active=old_active; do_opt_ifw=0;
             return 1;
    }


Поехали отлаживаться.

Вывод:  Hello World!!!1111111111111111111111111111111111111111111111111111111

Ок. Done.

И чтобы два раза не вставать напишем continue, break и return ; Для этого нам понадобится одна переменная в msl,  int do_opt_cbr; по первым буквам этих слов.
Занчаения:
#define MSLCBR_NULL 0
#define MSLCBR_CONT 1
#define MSLCBR_BREAK 2
#define MSLCBR_RET  3

Еще, помнится в прошлых версиях msl была IBREAK, писалась как ibreak; и выходила только из if.

В месте, где мы ругаемся на отсутствие открывающей скобки у функции:
                                        if(*line==';'){
                                            if(name=="continue"){ do_opt_cbr=MSLCBR_CONT; do_opt_active=0; return ; }
                                            if(name=="break"){ do_opt_cbr=MSLCBR_BREAK; do_opt_active=0;  return ; }
                                            if(name=="return"){ do_opt_cbr=MSLCBR_RET; do_opt_active=0;  return ; }
                                        }
Ставим флаги.  Очевидно, упускаем вариант, где return; может содержать значение.

Дописываем в while() и for() реакции на эти значения.
При выходе из функции Do()  проверяем, не осталось ли за нами долгов:
             if(do_opt_cbr){
                 if(do_opt_cbr==MSLCBR_CONT) SetError("for/while for continue; not found");
                 if(do_opt_cbr==MSLCBR_BREAK) SetError("for/while for break; not found");
                 if(do_opt_cbr==MSLCBR_RET) do_opt_cbr=MSLCBR_NULL;
             }

Все.  Тестировать увы не на чем. Операций == и других пока нет. Они и станут нашей следующей темой.
С общей логикой в принципе покончено. Добавить операции и можно пользоваться как вполне полноценным языком. Без функций и классов, но уже язык.

ЗЫ. Спасибо ЖЖ за падение и чудесное открытие, что копируясь из word'а код не теряет своего цвета.

MSL FL (SP) - Компилятор своими руками. Шаг 5. while из ничего
mikelsv
Три с половиной ночи, спать не хочется. Давайте покодим. Что делать понятно, как делать - тоже. Ну так вперед. Пол часа и все осилим.

Банальность:
Проект качать отсюда: anon:anon@svn://svn.loglist.org/usr/svn/opensource/msl-fl/ и anon:anon@svn://svn.loglist.org/usr/svn/opensource/msvlib . Исходный код будет тут: http://pastebin.com/QY5Xbmd1 . Версия: #define USE_MSLFL_VER 5

Мы реализуем <? print('Hello World!!!'); $i=55; while($i){ $i--; print('1'); } ?>
Новое тут только while() и $i--; И никаких $i=0; while($i<5){ $i++; } ибо лень, на целый оператор.

Пишем код. Очевидно идем в DoCodeFunction(). Вся обработка функций здесь. Нам нужно сохранить указатель на код внутри скобок, на случай, если это while(), ибо этот код отребуется выполнять на каждой итерации while(). Выполнялка функций DoCodeFunctionExec() должна установить флаг, обозначающий while(), дальше как с if, пишем функцию обрабатывающую while().
Поехали реализовывать:
В DoCodeFunction() добавляем в самое начало unsigned char *code, *ecode; указатели на код в скобках. Как нашли '(' записываем в code адрес+1. Как обработали до ')' записываем ее адрес ecode=line;
Добавляем в наш класс переменную int do_opt_while;
В DoCodeFunctionExec() добавляем обработку аналогичкую "if":
if(name=="whie" && args.Sz()==1){
   do_opt_while=args[0].val.val && args[0].val.val!="0"; do_opt_while++;
   return 1;
}

И после DoCodeFunctionExec() повторяем фоккус с "if":
// while function
if(do_opt_while){
  DoCodeFunctionWhile(line, to);
}

Остается написать DoCodeFunctionWhile();
Не хочу плодить переменные, сделаем одну для всех. int do_opt_ifw; по первым буквам if, for, while; А проверять будем по этой переменной и названию функции.
Код DoCodeFunctionWhile():

 void DoCodeFunctionWhile(unsigned char *&line, unsigned char *to, unsigned char *code, unsigned char *ecode){
        msl_value val; unsigned char *lline, *elline, *tline; unsigned char endcode;
      
        // save values
        int old_ifw=do_opt_ifw, old_active=do_opt_active; do_opt_ifw=0;
        // skip space
        while(line<to && (*line==' ' || *line=='\t' || *line=='\r' || *line=='\n')) line++;
        if(line>=to){ SetError("if(...) EOF"); return ; }

        // set active
        do_opt_active=old_active && old_ifw==2; lline=line;
        // do while(){ code }
        if(*(line)=='{'){ endcode='}'; line++; } else endcode=';';
        DoCodeMulti(line, to, val, endcode); line++;
        elline=line;

        // while
        while(old_active && old_ifw==2){
            msl_value val;
            // do while( it code )
            tline=code;
            DoCode(tline, ecode+1, val, ')');
            // result
            if(!val.val || val.val=="0") break;

            // do while(1){ it code }
            tline=lline;
            if(*(tline)=='{'){ endcode='}'; tline++; } else endcode=';';
            DoCodeMulti(tline, elline, val, endcode);
        }

        // load old value
        do_opt_active=old_active; do_opt_ifw=0;
        return ;
    }

Практически повторяет код для if(), разница лишь в том, что мы сохраняем указатели на код внутри while(1){} Первый раз код проходит обработку в любом случае, нам нужно знать, где он кончается. Если while(0), то do_opt_active=0 и выполнение не вносит изменений.
Дальше у нас идет цикл с проверкой условия в while() и пока оно будет истинно код будет выполняться.
Вот и все.

Теперь реализуем $i--; Правим DoCode(). Сделаем просто и тупо:
            // operator--
            if(*line=='-' && line+1<to && *(line+1)=='-'){
                if(!value){ SetError("need $value--"); return ; }
                // $val--
                value->val=itos(value->val.toi()-1);
                // set outval
                outval.Copy(value);
                line+=2;
                continue;
            }

Реализация всех этих финтов не сейчас. Под операции потребуется написать свой обработчик и чуть-чуть сойти с ума с приоритетом операций.
После запуска оказалось, что обработка {} глючит, и в if тоже. :) Эту прелесть еще тестировать и тестировать. К счастью пока все баги простые и легко исправляются.
Я снова вношу правки в код, их много и они мелкие, задолбаешься описывать. Добавил функцию DoCodeMulti(), передающую код в DoCode() деля их по ';'. А то код есть, а функции не оказалось.

Вот и все: "Hello World!!!1111111111111111111111111111111111111111111111111111111".
Дальше вероятно будет for(); Снова нестандартная ситуация, надо думать. Подозреваю проверку названия функции придется вынести перед DoCodeFunctionExec() и описывать их обработку совсем отдельно. Так же у нас break; и continue; на подходе. Тут тоже сделать переменную и обрабатывать соответственно, после выполнения кода while();

Пять утра. 1,5 часа унеслось на апгрейд.

MSL FL (SP) - Компилятор своими руками. Шаг 4. Требования к разработчику.
mikelsv
Ужасно хочется спать и ничего полезного делать не удается. Однако продолжение разработки языка кажется вполне нормальной идеей. Что как бы говорит: 640 IQ хватит всем на разработку интерпретатора. Или стоит переформулировать: интерпритатор нужно писать так, чтобы для понимания его работы хватало этих самых 60 ICQ.

Щас и проверим эту теорию. Так на чем мы остановились? Цыклы или операции? Да цыклы естественно! С операциями придется реально долго думать, к тому же их много. А цыклы, это ж практически функции. Поехали.

Проект качать отсюда: anon:anon@svn://svn.loglist.org/usr/svn/opensource/msl-fl/ и anon:anon@svn://svn.loglist.org/usr/svn/opensource/msvlib . Исходный код будет тут: http://pastebin.com/ym6Z0t7N . Версия: #define USE_MSLFL_VER 4


Первым пойдет if, та же функция, ничего нового, однако с продолжением кода. И да! Совсем упущены из виду фигурные скобки, а зря, будем исправлять. Теперь давайте по шагам. А выполнять мы будем: if(1) print('true'); else print('false');
Добавляем переменную в класс, обозначающий значение if, пусть будет int do_opt_if; Будем записывать в нее результат if(), чтобы вызывать код в if{} или в else{}.
Так же добавляем переменную, назовем ее, int do_opt_active. Так как код будет выполняться линейно и программа будет обрабатывать команды внутри if(0) необходимо как-то указывать, что этот код не нужно выполнять. За это и будет отвечать данная опция.
Добавляем в DoCodeFunctionExec() обработку функции по имени if:
if(name=="if" && args.Sz()==1){
     do_opt_if=args[0].val.val && args[0].val.val!="0"; do_opt_if++;
     return 1;
}
Немного хитрости с увеличением на один, чтобы сразу определить, что у нас был if.

Пишем код. Это пишем после вызова DoCodeFunctionExec():
if(do_opt_if){
    DoCodeFunctionIf(line, to);
}
Проверка, а был ли if?

    void DoCodeFunctionIf(unsigned char*&line, unsigned char *to){
        msl_value val;
        // save values
        int old_if=do_opt_if, old_active=do_opt_active;  do_opt_if=0;

        // skip space
        while(line<to && (*line==' ' || *line=='\t' || *line=='\r' || *line=='\n')) line++;
        // single or {multi}
        if(line>=to){ SetError("if(...) EOF"); return ; }
      
        // set active
        do_opt_active=old_active && old_if==2;
        // do if(){ code }
        DoCode(line, to, val, *(line)=='}' ? '}' : ';'); line++;

        // skip space
        while(line<to && (*line==' ' || *line=='\t' || *line=='\r' || *line=='\n')) line++;
        // test on 'else'
        if(line+4<to && *line=='e' && *(line+1)=='l' && *(line+2)=='s' && *(line+3)=='e'){
            // skip 'else'
            line+=4;
            // skip space
            while(line<to && (*line==' ' || *line=='\t' || *line=='\r' || *line=='\n')) line++;
            // set active
            do_opt_active=old_active && old_if==1;
            // do else{ code }
            DoCode(line, to, val, *(line)=='}' ? '}' : ';'); line++;
        }

        // load old value
        do_opt_active=old_active; do_opt_if=0;
        return ;
    }

А это сама обработка. В '// skip space' пропускаем неиспользуемые символы. Устанавливаем do_opt_active - выполнять дальнейший код или нет и сохраняем его старое значение, не забывая восстановить в конце. Вызываем DoCode() , с символом остановки '}' или ';', ориентируясь на первый символ после if().
Вот и все, копмилируется с первой попытки. Работает со второй.
Дальше пихаем проверку do_opt_active в правильные месте, по настроению. Думаю достаточно добавить проверку перед выполнением функций и перед присвоением значений.
Так же пришлось дописать недостающий обработчик цифр в DoCode(), а то ж "1" он и не заметил.

Тестируем, заменяя в нашем примере if(1) на if(0);
Или вот пример использующий предыдущий шаг: <? $hello='Hello code!'; if($hello) print($hello); else print('Sorry, code hate you.'); ?>

На этом пока все. Как видите, такие вещи можно писать в том состоянии, когда на другие нет сил и желания. Здесь нет дикой и безумной логики. Все просто, красиво и рекурсивно. Я стараюсь это поддерживать.

Дальше будет, хм, а while без операций не сделать, считать то нечем. Сделаем <? print('Hello World!!!'); $i=55; while($i){ $i--; print('1'); } ?>  Этот пример интересен тем, что вот тут то мы и начнем терять производительность, или не начнем? Сделать то просто, возвращать указатель на начало и делать DoCode() и каждый раз обрабатывать заново, вместо генерации псевдокода и быстро использовать его. Хотя, генерация тоже занимает время. Да и кто в общем-то мешает подключить ее? Но это совсем далекая тема. Оптимизационные навороты нам сейчас совершенно не к чему, они лишь усложнять код для понимания.

Вот так.

MSL FL (SP) - Компилятор своими руками. Шаг 3. Что вы знаете о переменных?
mikelsv
Итак, сразу ссылки на исходники anon:anon@svn://svn.loglist.org/usr/svn/opensource/msl-fl/ и anon:anon@svn://svn.loglist.org/usr/svn/opensource/msvlib . Исходный код будет еще и тут: http://pastebin.com/G8PKZLq5 . Версия: #define USE_MSLFL_VER 3

Следующая задача: <? $hello="Hello World!"; print($hello); ?> .
Немного подумать и можно писать код. Но для начала: а что мы вобще такое пишем? Интерпретатор с синтаксисом c++ и php, последний нам ближе. Так что же, у нас, такое переменные? Это динамический массив в каждый элемент которого можно запихнуть такой же массив. То есть структура проста: ключ, значение, указаель на следующий элемент и указатель на массив, хранящийся в этом массиве.
Класс:
class msl_value{
    // prev, next, up first, up end
    msl_value *_p, *_n, *_a, *_e;
    MString key, val;
};

Теперь я беру свой шаблон OMatrixT, который разрулит всеми указателями и пишу функции New() - выделяющую память под переменную, Find() - ищет переменную по имени, Get() - возвращает значение переменной, Set() - соответственно устанавливает. В итоге:
class msl_value : public OMatrixT<msl_value>{
public:
    // prev, next, up first, up end
    msl_value *_p, *_n;
    MString key, val;

    msl_value(){ _p=0; _n=0; }

    msl_value* New(){
        msl_value *p=new msl_value;
        if(!p) return 0;

        OMAdd(p);
        return p;
    }

    msl_value* Find(VString key){
        if(!this) return 0;
      
        for(msl_value*p=_a; p; p=p->_n){
            if(p->key==key) return p;
        }

        return 0;
    }

    void Set(VString key, VString val){
        msl_value *p=Find(key);
        if(p){
            p->val=val;
        } else{
            p=New();
            if(p){
                p->key=key; p->val=val;
            }
        }
        return ;
    }

    VString Get(VString key){
        msl_value *p=Find(key);
        if(p)
            return p->val;
        else
            return VString();
    }

    void Del(VString key){
        msl_value *p=Find(key);
        if(p){
            OMDel(p); delete p;
        }
        return ;
    }

    ~msl_value(){ Clear(); }
    void Clear(){ OMClear(); }

};

Добавляем это в msl как msl_value global; и никак иначе. Глобальные переменные есть. Где-то в процессе нужно будет подумать о работе с ветками переменных в случае передачи их в функцию или обратно. А сейчас думаем над передачей самой переменной. Например в случае $hello='Hello World!'; мы должны записать имя первой переменной. Даже не так, мы ее установим, раз в нее устанавливаются данные. Это нужно для того, чтобы на нее можно было ссылаться. Дальше выполняем 'Hello World!', который дожен вернуться как msl_val, где key пустой, а значение 'Hello World!'. Ок. Так же перепишем обработку параметров функций, чтобы передавать не строку, а msl_val.
Вот кстати можно реализовать function()='value', чтобы значение подставлялось как последний пераметр.

Так, вот итоговая мысль, видим переменную - создаем, это нужно для всяких $a[$b]['c'][0xd][e()]; если в нее не устанавливаются данные удалим, ибо у нас линейность, а значит обрабатываем по порядку и не знаем, просто она тут висит или в нее устанавливаются данные.

Теперь по шагам.
Пусть все крутится в DoCode(), он будет раскрывать все скобки, находить переменные и функции. Для того, чтобы он понимал, когда выйти добавляем параметр unsigned char ecode=1, сначала неактивный, ибо ну кто добавит char(1) в код? В принципе подойдет любое другое неиспольземое значение. Надеюсь не будет предложений ecode=0 и if(ecode), что абсолютно лишнее. Добавляем обработчик if(*line==ecode){ return ; }. Ок, всякие (), [] готовы к обработке. Так как в функции может быть и скобка и запятая, добавляем еще один такой же параметр.
Так же надо добавить возвращение результата, добавим параметр msl_value &outval. Порядок.
Переписываю DoCode() под обработку всех возможных вариантов. DoCodeFunctionArgs() больше не нужна, DoCode() прекрасно выполнит ее функцию.
Новая схема работы: Do() -> DoCode(line, to, outval, ';'); Код делится на блоки по ;, пока не дойдет до конца.
DoCodeValues() обрабатывает переменные,
Добавляем SGet() в msl_value, эта функция вернет адрес на переменную, если ее нет, создаст. Это не радует, и обязательно будет удаление таких переменных, если им не присваивается значение. А лучше сделать так сразу. Скажем через тот же SGet(), установив указатель val.sz=0xffffffff; Указатель остается нулевым, и никаких последствий быть не должно, а переменная помечена.

... Много кода спустя.
Есть, <? $hello='Hello World!'; print($hello); ?> работает. Пришлось поработать над DoCode() и DoCodeFunction(), остальное практически без изменений. Код стал сложнее, добавилась работа с переменными, которые у нас одного типа в виде динамических массивов.
Жить стало лучше, жить стало веселее, однако и на данном этапе разработки код особо не поиспользуешь. Вот и остается думать, что делать дальше.
В планах: подключаемые функции, новый класс для хранения переменных, мысли о многопоточности. Но это все не главное. Нам нужны if, while и for, нам нужны операции с переменными, весь набор: !, ||, &&, ., +, /, *, -, .=, ... Одна из этих важных тем и станет нашей следующей целью. Они обе достаточно сложные и вот тут то придется пораскинуть мозгами. Все, что написано до этого сущая ерунда, всего-то 480 строк.

Наслаждайтесь, экспериментируйте, ищите ошибки и баги. Следующий шаг совсем скоро.

MSL FL (SP) - Компилятор своими руками. Шаг 2. Легкие пути.
mikelsv
Я пожалуй повторюсь, что это интерпретатор, но компилятор звучит и лень переделывать заголовок. А у меня продолжается рад постов про разработку, в которых в частности рассказывается, что курил разработчик - один из частых вопросов при попытке писать на чужом языке, а в некоторых случаях и на своем.

Итак, наша задача выполнить <? print('Hello World!'); ?> . Поэтому вместо того, чтобы заняться переменными мы пойдем простым путем, сразу займемся парсером. Ну а че. Запарсить нужно всего ничего. Теги <??>, $переменные и очевидно funкции(), ну и 'текст всякий'.

Пока одна рука пишет код, второй расскажу о трех классах, используемых в коде, это:
VString() - класс хранящий в себе указатель на память. Это практически тоже самое как unsigned char*, только с размером. Это решает кучу проблем. Никаких проверок конуца строки по нулевому символу.  Иначе это бред, куча всевозможных дырок и падений. Очень часто нужен именно этот класс при работе с данными. Офигенная штука. Не выделяет память. На те же операции с использованием класса выделяющего память уйдет намного больше времени.
MString() - как и предыдущий, но с выделением памяти.
HLString() - класс с динамическим набором буферов памяти. Отличная штука для сложения строк. Выделяет большие блоки памяти, что позволяет уменьшить обращение к функциям выделения памяти и ускорить работу.

Так как версий будет много добавил в код дефайн USE_MSLFL_VER в котором можно указывать версию кода. Для этой статьи установить USE_MSLFL_VER равный 2.
Скачать готовый пример можно отсюда: anon:anon@svn://svn.loglist.org/usr/svn/opensource/msl-fl/ и anon:anon@svn://svn.loglist.org/usr/svn/opensource/msvlib .
Так же исходный код выложу сюда: http://pastebin.com/g7u3ry6P

Пишем код:
Меняем GetResult() на GetOutput(), SetResult() так же. Звучит лучше.
Пишем функции обработки предупреждений и ошибок SetWarning(), SetError() и SetEpic(); Разница в разрушительном эффекте от ошибок. От незначительных до ошибок при выделении памяти. Первые две для ошибок кодера, последняя для ошибок компиялтора и системы. Хотя для системы тоже надо придумать отдельную функцию.

В функции Do(code); ищется '<?' обозначающая начало программы, дальше управление передается в DoCode(). DoCode() по первому символуц определяет, что это $ - переменная, aA-zZ или _ - функция или выдает ошибку.
Обработчик $переменных пока не пишем, наша задача обработать функцию.
DoCodeFunction() определяет название функции и передает управление в функцию определения аргументов функции.

Остановимся на этом. Что такое аргумент функции? Это может быть переменная, функция, строка. Останавливаемся на строке, все строго по задаче. Пишем структуру для аргумента функции:
class msl_fl_farg{
public:
    VString val;
};

Строка, без внутренней обработки. Пока только так, строго по задаче, хотя в будущем обязательно придется переделывать для всех вариантов.
Дальше класс для списка аргументов: class msl_fl_fargs; Тут у нас динамическое выделение памяти, asz - выделено памяти, usz - испольовано под параметры. UpSize() выделяет память под еще 16 параметров, Add(val) - добавляет параметр, Sz() - возвращает количество параметров, operator[id] - возвращает параметр с указанным номером.

Переходим к функци DoCodeFunctionArgs(), которая занимается той самой обработкой параметров нашей функции. Сейчас она умеет определять строки и переменные, а так же разбирает запятые и закрывающую скобку.
После обработки параметров нас снова возвращает в DoCodeFunction(), откуда вызывается DoCodeFunctionExec().
Ее код:
    int DoCodeFunctionExec(VString name, msl_fl_fargs &args){
        if(name=="print" || name=="echo"){
            for(int i=0; i<args.Sz(); i++){
                print(args[i].val);
            }
            return 1;
        }

        SetError(HLString()+"Function: '"+name"' not found");
        return 0;
    }

Вот и все. Немного отладки... И "Hello World!" выводится в консоле. Попробуте добавить свою функцию в DoCodeFunctionExec(); Можно добавить проверку количества параметров в args.Sz() и выдавать ошибку. Развлекайтесь.

Что дальше? Полагаю: <? $hello="Hello World!"; print($hello); ?> Переменные, куда ж без них.

?

Log in

No account? Create an account