Форум программистов, компьютерный форум CyberForum.ru

Пишем свой интерпретатор языка BASIC - C++

Восстановить пароль Регистрация
 
 
Рейтинг: Рейтинг темы: голосов - 1509, средняя оценка - 4.80
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
20.06.2009, 20:03     Пишем свой интерпретатор языка BASIC #1
*****************
Благодаря форуму и Evg в частности интерпретатор развивается, потихоньку превращаясь в простенький интерпретатор QBASIC.
Некоторые из самых старых версий сохранились в теме и ссылки на них будут добавлены в это сообщение,а также ссылки на другие темы,связанные с этой.

Репозиторий с проектом находится тут, там же есть возможность в браузере посмотреть историю ревизий (английский в логах весьма примитивен,комментарии и рекомендации можете писать в личку),а также скачать самый последний архив репозитория в формате .tar.gz
Если кто-то пользуется Subversion,скачать исходники можно так:
Код
svn co https://basin.svn.sourceforge.net/svnroot/basin basin
Эти темы возникли в результате моих вопросов по ходу написания:
Технический приём для формирования согласованных данных
Makefile: как с использованием gcc строить автоматические зависимости от .h файлов?
Вопрос по svn (Subversion)
Создание системы тестирования ПО.
Вопрос про разные реализации бэйсиков
[C/C++] Можно ли выразить порядковый номер элемента массива через индексы?
[C++] Какие флаги указать линкеру для компиляции программы?
Как можно определить переменную в файле configure.in,чтобы её можно было использовать в Makefile?
Странный SIGSEGV, или что зависит от порядка написания интерфейса класса
[C++]Можно ли как-то указать в Makefile,чтобы часть файлов компилировал компилятор C?
Альтернативная версия интерпретатора от Evg на C
Это простая реализация разбора выражений, написанная Evg на C:
Представление выражения в двоичном дереве
*****************
Первое сообщение:
*****************
Задание(Страуструп,из книги,по готовому коду): Введите программу калькулятора и заставьте её работать.Например,при вводе
C++
1
2
r = 2.5
area = pi*r*r
Программа калькулятора выведет:
C++
1
2
2.5
19.635
Получили такой код:
LexicalAnalyzer.h
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// LexicalAnalyzer.h
#ifndef LEXICALANALYZER_H_INCLUDED
#define LEXICALANALYZER_H_INCLUDED
 
#include <cctype>
#include <string>
#include <map>
#include <iostream>
 
enum Token_value {
    NAME,       NUMBER,      END,
    PLUS = '+', MINUS = '-', MUL = '*', DIV = '/',
    PRINT = ';',ASSIGN = '=',LP = '(',  RP = ')'
};
extern Token_value curr_tok;
extern std::map<std::string,double>table;
extern int no_of_errors;
 
Token_value get_token();
 
double expr(bool);
double term (bool);
double prim (bool);
int error(const std::string&);
 
#endif // LEXICALANALYZER_H_INCLUDED

LexicalAnalyzer.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// LexicalAnalyzer.cpp
#include "LexicalAnalyzer.h"
 
 
std::map<std::string,double>table;
Token_value curr_tok=PRINT;
 
double expr (bool get)
{
    double left = term(get);
 
    for (;;)
        switch (curr_tok) {
            case PLUS:
                 left += term(true);
            break;
            case MINUS:
                 left-= term(true);
            break;
            default:
                 return left;
        }
}
 
double term (bool get)
{
    double left = prim (get);
 
    for (;;)
        switch (curr_tok) {
            case MUL:
                 left*=prim(true);
            break;
            case DIV:
                 if (double d = prim (true)) {
                     left /= prim (true);
                     break;
                 }
                 return error("Деление на ноль");
            default:
                 return left;
        }
}
 
double number_value;
std::string string_value;
 
double prim (bool get)
{
    if (get) get_token();
    switch (curr_tok){
        case NUMBER:{
            double& v = number_value;
            get_token();
            return v;
        }
        case NAME:{
            double& v = table[string_value];
            if (get_token()==ASSIGN) v = expr(true);
            return v;
        }
        case MINUS:
            return -prim(true);
        case LP:{
            double e = expr(true);
            if (curr_tok!=RP) return error("Ожидалась )");
            get_token();
            return e;
        }
        default:
            return error("Ожидалось первичное выражение");
    }
}
 
Token_value get_token()
{
    char ch = 0;
 
    do {
        if (!std::cin.get(ch)) return curr_tok = END;
    } while (ch!='\n'&&isspace(ch));
 
    switch (ch) {
        case 0:
             return curr_tok = END;
        case ';':case '\n':
             return curr_tok = PRINT;
        case '*':case'/':case '+':case '-':case '(':case ')':case '=':
             return Token_value(ch);
        case '0':case '1':case '2':case '3':case '4' :
        case '5':case '6':case '7':case '8':case '9':case '.':
             std::cin.putback(ch);
             std::cin>>number_value;
             return curr_tok=NUMBER;
        default:
             if (isalpha(ch)) {
                 string_value = ch;
                 while (std::cin.get(ch)&&isalnum(ch)) string_value.push_back(ch);
                 std::cin.putback(ch);
                 return curr_tok = NAME;
             }
             error ("Неправильная лексема");
             return curr_tok = PRINT;
    }
}
int no_of_errors=0;
int error (const std::string& s)
{
    no_of_errors++;
    std::cerr<<"Ошибка: "<<s<<'\n';
    return no_of_errors;
}

main.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// main.cpp
#include "LexicalAnalyzer.h"
 
 
int main()
{
    table["pi"]=3.1415926535897932385;
    table["e"]=2.7182818284590452354;
    while (std::cin) {
        get_token();
        if (curr_tok==END) break;
        if (curr_tok==PRINT) continue;
        std::cout<<expr(false)<<'\n';
    }
    return no_of_errors;
}

Анализатор-то работает,но конечное значение не вычисляется.Более того,если вводим
C++
1
a = 3 + 6
,то получаем "a", равное первому элементу в выражении,то есть 3.В чём логическая ошибка данной программы?С этими каскадными вызовами она слегка запутана.Уверен,что кто-то уже делал это задание.

Добавлено через 2 часа 5 минут 30 секунд
Пришлось решать влоб с дебаггером.У Страуструпа опечатка (или намеренная ошибка,что более вероятно ) Вот в этом куске кода в функции get_token():
C++
1
2
        case '*':case'/':case '+':case '-':case '(':case ')':case '=':
             return Token_value(ch);
Нехватает смены значения curr_tok,что и приводит к ошибочной работе.
C++
1
2
        case '*':case'/':case '+':case '-':case '(':case ')':case '=':
             return curr_tok=Token_value(ch);
Теперь всё пашет,всем спасибо,вопрос можно считать закрытым,но есть вопрос поважнее: В функциях prim и term возвращается int при ошибке,но ведь они имеют тип double,как вообще это работает?Происходит неявное преобразование типа,так?Мне интересно,почему Страуструп прибег к такому способу,это распространённая практика?

Добавлено через 16 минут 19 секунд
И ещё опечатка была
C++
1
2
3
                 if (double d = prim (true)) {
                     left /= d;// было left /= prim (true)
                     break;
Лучшие ответы (1)
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
26.10.2009, 22:34  [ТС]     Пишем свой интерпретатор языка BASIC #221
Да,вот интересно,под виндой работает намного быстрее,причём только в полноэкранном режиме,хотя и не дотягивает до натурального QBASIC-а.
Возможно,тут уже ничего не сделаешь,это уже библиотека так работает видимо,но всё же со временем надо будет попытаться ускорить хоть чуть-чуть.
Небольшое напутствие,если делать линию через цикл от края до края,то почему-то нельзя указывать размер границы,видимо происходит выход запределы экрана и программа падает(скорее всего это в самой SDL)
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16843 / 5264 / 323
Регистрация: 30.03.2009
Сообщений: 14,159
Записей в блоге: 26
26.10.2009, 23:29     Пишем свой интерпретатор языка BASIC #222
Со скоростью сделать можно, надо только разобраться. На SDL'е написан FreeCraft - по сути переделка 2-го warcraft'а под линух. Всё там шустро бегало. Надо только разобраться, как эта бодяга работает
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
28.10.2009, 06:55  [ТС]     Пишем свой интерпретатор языка BASIC #223
Попробовал улучшить немного со скоростью,так,по мелочи поменял,но вроде эффект есть.Вот можешь проверить таким вот тестом.Алгоритм аппроксимации(так вроде?) линии содрал прямо с вики,там используется битовая арифметика,я пока не очень понимаю,зачем там сдвиги на 12 позиций и прочее,надеюсь потом пойму.
PureBasic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   SCREEN 12
   FOR i=5 TO 639 STEP 20
      FOR j=1 TO 479
        PRESET (i,j),3
       NEXT j
   NEXT i 
   FOR i=5 TO 479 STEP 20
      FOR j=1 TO 639
        PRESET (j,i),3
     NEXT j
   NEXT i
   LET k=1
   FOR i=20 TO 420 STEP 10
      FOR j=20 TO 420 STEP 10
         LINE (i,j)-(j,i+k),14 
         LET k=k+1
      NEXT j
      LET k=0
   NEXT i
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16843 / 5264 / 323
Регистрация: 30.03.2009
Сообщений: 14,159
Записей в блоге: 26
28.10.2009, 09:56     Пишем свой интерпретатор языка BASIC #224
Ты заметил, что когда рисуешь по точкам через PRESET, то тормозит, а когда через LINE - то практически мгновенно. Т.е. проблему я вижу в концепции SDL. Насколько я понимаю, подобные библиотеки работают через некий виртуальный монитор. Т.е. все точки ты сначала наносишь в этот монитор (который попросту лежит в обычной памяти), и эти операции являются быстрыми. Затем ты этот монитор отображаешь в реальную картинку. Делается это путём ПОЛНОГО обновления виртуального монитора на реальную картинку. Этот процесс уже "медленный" (по сравнению с помещением точки в виртуальный монитор)

В случае, когда ты рисуешь по точкам, получается, что для происовки линии из 100 точек тебе нужно 100 раз обновить экран. Когда ты рисуешь линию, то сначала все 100 точек линии отображаются в виртуальный монитор, и затем один раз обновляются. Т.е. в теории нарисовать линию из 100 точек работает в 100 раз быстрее, чем рисовать 100 точек

Библиотека видно написана всё-таки для игрушек. Поскольку игрушки как правило отображают экран по тикам. Т.е. (к примеру) 60 раз в секунду рассчитывается картинка, при этом она сначала рисуется по точкам в виртуальный монитор (т.к. эти операции быстрые), а затем картинка отрисовывается на экран (медленная операция, но 60 раз в секунду успевает).

Для интерпретатора такое поведение неудобно, потому что у тебя нет точек привязки, по которым можно отображать картинку из виртуального монитора на реальный. В качестве эксперимента можешь ввести дополнительный оператор в бэйсике, который перенесёт виртуальный монитор на реальную картинку, а все точки ставить строго в виртуальном мониторе. Этот эксперимент нужен лишь для того, чтобы проверить догадку. Если она оправдается, то надо придумать механизм, как это работало бы нормально. Т.е. видимо всегда всё будет рисоваться в виртуальный монитор, при этом асинхронно по таймеру виртуальный монитор будет отображаться в картинку

Добавлено через 5 минут
Ещё надо какую-нить операцию для паузы (опять-таки можно врЕменную), чтобы картинка не удалялась. А то если вставить бесконечный цикл, то программу только по kill -9 смог убить. INPUT так и не понял, как правильно писать
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
28.10.2009, 18:40  [ТС]     Пишем свой интерпретатор языка BASIC #225
Насчёт обновления картинки ты прав на все 100.Прямо в точку.Подозрение пало как раз на функцию SDL_UpdateRect(x,y,w,h) и я прочитал в интернете как многие жаловались на медленную работу графики с этой функцией.

В качестве средства предлагалось добавить флаг SDL_DOUBLEBUF при выставлении режима видео,а также создавать поверхность не в системной памяти,а в видеопамяти.После чего просто использовать функцию SDL_Flip,которая делает замену между первым и вторым буфером,происходит это довольно быстро.Но всё же она ждёт вертикальной синхронизации,и в некоторых случаях SDL_UpdateRect может работать быстрее. Но самое правильное-это таймер.

В общем пока для пикселей используется SDL_UpdateRect и обновляет только один пиксель,который рисуется,а для линий тоже SDL_UpdateRect ,только обновляется только четырёхугольник,диагональю которого является рисуемая линия.

Но ты всё правильно сказал-нужен таймер,чтобы обновлял всю картинку с помощью SDL_Flip с постоянной частотой,например 30-35 кадров в секунду,и сделать это следует безусловно,иначе графика попиксельно(да и вообще)будет относительно тормознутой.Благо таймер в SDL существует как отдельная подсистема,и его можно просто подключить.
Насчёт паузы после рисования-в самом QBASIC-е после рисования картинки появляется надпись "Нажмите любую клавишу для продолжения",поэтому надо будет как-то писать текст внизу поверх картинки или что-то такое.

Добавлено через 19 минут
А ещё забыл сказать:дело ещё не только в этом - как оказалось,у меня почему-то режим с глубиной цвета 16бит(слабо представляю,что это вообще) жутко тормозит,а когда делаешь 32бит,то всё намного быстрее.Так вот я добавил флаг SDL_ANYFORMAT,так что теперь есть предпочтение на 16бит,но если в силу каких-либо причин этот режим не очень подходит,библиотека сама подберёт лучший режим.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16843 / 5264 / 323
Регистрация: 30.03.2009
Сообщений: 14,159
Записей в блоге: 26
30.10.2009, 18:03     Пишем свой интерпретатор языка BASIC #226
Ещё надо разбираться с тем, что если программа зациклилась на интерпретации в то время, как работает графический режим, то кроме как по kill -9 она не убивается
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
30.10.2009, 23:30  [ТС]     Пишем свой интерпретатор языка BASIC #227
Что-то пока никак не соображу,как же работают таймеры.Вот есть программа,с каким-то списком инструкций.А где-то есть функция,запускающая таймер и следящая за временными отрезками.Но ведь программа не состоит из одной этой функции,как тогда обновляется таймер вовремя? Или сам таймер должен работать как отдельный процесс,или я не знаю.Какая там концепция?
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16843 / 5264 / 323
Регистрация: 30.03.2009
Сообщений: 14,159
Записей в блоге: 26
30.10.2009, 23:45     Пишем свой интерпретатор языка BASIC #228
Вообще концепция по идее такая, что процесс вызывает ядро и говорит "разбуди через 10 миллисекунд", через 10 миллисекунд выдаётся сигнал, программа прерывается и вызывается пользовательский обработчик сигнала (см. sigaction). Но я не знаю, насколько такое быстро работает, к тому же это линух-зависимое

Что касается встроенной возможности в SDL, то я как-то не понял (правда особо и не разбирался). Но там они пишут, что это замедляет работу, но в SMP этого замедления не будет. SMP, насколько я понимаю, это многопроцессорная машина, а потому вполне возможно, что запускают ещё один процесс
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
31.10.2009, 08:02  [ТС]     Пишем свой интерпретатор языка BASIC #229
Как раберусь,сделаю таймер.
Пока делал опции для LINE столкнулся с проблемкой:
Синтаксис LINE описывается так (Источник,которым пользуюсь):
PureBasic
1
LINE (x1, y1)-(x2, y2) [, [color], [B|BF], [style]]
Так вот эти опции B и BF никак не вяжутся с проверкой на инициализацию,я пока их сделал в ковычках.А вообще наверное придётся генерировать вручную эти опции,подменив TOKEN_IDENT на строковую константу.
Поэтому временно синтаксис LINE:
PureBasic
1
LINE (x1, y1)-(x2, y2) [, [color], ["B" | "BF"]
Синтаксис CIRCLE:
PureBasic
1
CIRCLE (x, y), radius [, [color]]
(Цвет получает значения от 0 до 15).
Пока нет проверки на рисование за границы экрана,поэтому сразу говорю,неправильные данные рушат SDL.Пока не решил,где и как именно сделать проверку,но это мелочи.
Небольшой тест
PureBasic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
   SCREEN 12  
   ' Attention! Currently there is no check for out of range pixel drawing
   ' Wrong coordinates will crash SDL engine.
   FOR i=17 TO -1 STEP -1
     FOR m=70 TO 350 STEP 40
       FOR j=70 TO 350 STEP 40
         CIRCLE (m+i,j+i),60,i
       NEXT j
     NEXT m          
   NEXT i
 
   LET k=0
   FOR i=0 TO 15
      FOR j=20 TO 420 STEP 40
       LINE (425+k,j)-(445+k,j+20),i,"BF"
      NEXT j
       LET k=k+13       
   NEXT i
 
  LINE (25,445)-(45,465),12,"B"  '(box)
  LINE (65,445)-(85,465),10,"B"  '(box)


P.S.
>Ещё надо разбираться с тем, что если программа зациклилась на интерпретации в то время, как работает графический режим, то кроме как по kill -9 она не убивается
А что вообще можно с этим сделать? Какие есть выходы из ситуации? Делать интерпретатор работающий с threads?
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16843 / 5264 / 323
Регистрация: 30.03.2009
Сообщений: 14,159
Записей в блоге: 26
31.10.2009, 11:51     Пишем свой интерпретатор языка BASIC #230
Цитата Сообщение от #pragma Посмотреть сообщение
Так вот эти опции B и BF никак не вяжутся с проверкой на инициализацию,я пока их сделал в ковычках.А вообще наверное придётся генерировать вручную эти опции,подменив TOKEN_IDENT на строковую константу.
Что-то не понял, в чём проблема. В тот момент, когда читаешь токен, проверяешь что его тип равен IDENT, а строковое значение - "B" или "BF"

Цитата Сообщение от #pragma Посмотреть сообщение
А что вообще можно с этим сделать? Какие есть выходы из ситуации? Делать интерпретатор работающий с threads?
Да хрен его знает... Я так подозреваю, что SDL ставит свои обработчики сигналов, в том числе и на SIGTERM (когда нажимаешь Ctrl-C). Надо в документации к SDL копаться. Можно наверное ещё раз перехватить, но хз, что там будет после этого

Добавлено через 3 минуты
Цитата Сообщение от #pragma Посмотреть сообщение
Пока нет проверки на рисование за границы экрана,поэтому сразу говорю,неправильные данные рушат SDL.Пока не решил,где и как именно сделать проверку,но это мелочи.
В putpixel
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
02.11.2009, 12:23  [ТС]     Пишем свой интерпретатор языка BASIC #231
Помнишь,ты писал,что отдельный графический интерфейс делается в виде библиотеки .so или .dll? А что должно быть внутри библиотеки? Как именно она должна взаимодействовать с консольным приложением? Есть ли там функция main,всмысле это тоже отдельная программа? Я поискал,некоторые советуют делать общение GUI с программой через сокеты.Но в тоже время есть класс в Qt,называется QProcess,и он предназначен для запуска внешних программ.Значит,если в таком варианте,нужно разработать ещё одно приложение с окнами,которое будет обрабатывать события и запускать что нужно с необходимыми опциями командной строки.Но тогда это уже нельзя сделать в виде .so?
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16843 / 5264 / 323
Регистрация: 30.03.2009
Сообщений: 14,159
Записей в блоге: 26
02.11.2009, 15:38     Пишем свой интерпретатор языка BASIC #232
Динамическая библиоткеа - это по большому счёту часть кода, вынесенная отдельно. Это экономит дисковое пространство, т.к. динамическая библиотека есть в единственном экземпляре и нет необходимости статически линковать к каждому бинарнику

Второй бонус динамической библиотеки - это возможность её подключения прямо во время работы программы

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

В какое-то время, глядя на твои заслуги по части графики у меня зачесалась ж..а и я тоже что-то там сделал, но в итоге опять забил. Т.е. сейчас всё находится в некотором устаканившемся состоянии, можешь попробовать посмотреть.

1. Сейчас FOR сделан несколько через ж...у (там вроде бы только целочисленный положительный STEP поддерживается)
2. В SCREEN можно подать любое число (пока оно игнорируется, реально должно отрабатываться в __rt_graph_SCREEN. Выхода обратно в текстовый режим пока нет
3. Обрати внимание на то, как реализованы PRINT, синусы-косинусы, графика с точки зрения реализации в виде процедур - для движка промежуточного представления всё это выглядит в виде одной и той же конструкции. При этом в операции вызова я список аргументов печатаю в квадратных скобках (чтобы не мельтешило в глазах с круглыми скобками от выражений) - запускай "./interp -s"
4. Обрати внимание, как у меня на нижнем уровне (runtime) реализована графика: разбор непосредственно бэйсиковской реализации отдельно, работа с графической библиотекой отдельо, алгоритмы прорисовки отдельно
5. Всё то, что находится в graph_sdl.c по большому счёту и есть та динамическая библиотека, о которой я писал выше. Если заменить префикс sdl_ на что-нибудь другое, то мы как раз и получим тот абстрактный интерфейс нижнего уровня. Всё, что находится за пределами модуля sdl_graph "не знает" о том, что мы работаем с библиотекой SDL
6. Пока по прежнему на вход читаем файл a.txt. Опции, которые на данный момент могут тебе показаться интересными
Пользовательские:
-w трассировка записей
-e трассировка исполнения
-u по умолчанию все переменные прописываются undef'ами (для отладки неинициализированных значений)
Внутренние:
-s печать промежуточного представления
Остальные опции увидишь в main.c но они тебе особенно не интересны
Вложения
Тип файла: rar basic.tar.gz.rar (37.7 Кб, 71 просмотров)
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16843 / 5264 / 323
Регистрация: 30.03.2009
Сообщений: 14,159
Записей в блоге: 26
13.11.2009, 14:14     Пишем свой интерпретатор языка BASIC #233
Оказывается, я тебе наврал про реализацию двухаргументных арифметических операций. Я почему-то всю жизнь считал, что в Си второй аргумент приводится к типу первого. Оказывается это не так. Из двух аргументов выбирается "более широкий тип" и оба аргумента приводятся к этому типу (а точнее, приводится только один из аргументов, ибо второй имеет этит тип) и результат имеет этот самый более широкий тип. Считается, что плавающий тип шире целого

Т.е. я утверждал, что INT + FLOAT эквивалентно (int) (INT + (int)FLOAT), на самом деле это (float) ((float)INT + FLOAT). Т.е. поведение в этом месте бэйсика и Си совпадают. Собственно из-за неправильной реализации у меня коряво работала картинка с синусами и косинусами - мне приходилось заводить плавающие константы
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
14.11.2009, 12:27  [ТС]     Пишем свой интерпретатор языка BASIC #234
Сделал первую рабочую версию графического интерфейса в виде .so.Особо не ковырялся,просто взял примеры из документации,поменял немного.Интерфейс очень простой,там в этих классах потонуть можно,столько всего.
Чтобы запустить программу с графическим интерфейсом,нужно использовать опцию --gui
Вот так выглядит это дело у меня:
http://itmages.ru/src/preview/12684/0a8751.png
Пока имеет смысл только кнопочка Run (Третья слева).Но ещё всё пока не доделано.
К сожалению пока даже правки,сделанные в редакторе,не учтутся при интерпретации.Надо разобраться с освобождением памяти при открытии нового файла и т.д.Есть небольшая проблема: функция setupEditor() запускается в конструкторе окна,и в этой функции путь к файлу уже должен быть известен,но я не могу послать его в конструктор,что-то не даёт.Типа параметры конструктора менять нельзя.Наверное,самый нормальный выход-просто открывать "пустой файл".
Есть проблема с передачей управления окну с графикой-я не знаю,как это делать,это ведь не какое-нибудь окно производимое от Qt ,а создаваемое другой библиотекой.Получается,что после прорисовки графики окно с графикой закрыть нельзя,так как события от клавы не попадают в деструктор для SDL(именно в деструкторе graphics у меня сделано что-то наподобие перехватчика нажатий клавиш,и при нажатии на любую клавишу SDL производит выход).
С добавлением кода библиотеки получилось,что проект достиг почти 20000 строк.
Поскольку иконки и примеры распространяются по GPL ,то gui тоже распространяется по GPL,всё остальное-BSD
Все мои старания,связанные с интерфейсом,дают понять,что разработка хорошего интерфейса-дело непростое,и требует больших знаний и кропотливой работы.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16843 / 5264 / 323
Регистрация: 30.03.2009
Сообщений: 14,159
Записей в блоге: 26
14.11.2009, 12:43     Пишем свой интерпретатор языка BASIC #235
Цитата Сообщение от #pragma Посмотреть сообщение
Все мои старания,связанные с интерфейсом,дают понять,что разработка хорошего интерфейса-дело непростое,и требует больших знаний и кропотливой работы.
Это точно. Я сел осваивать Borland Builder. Получается так, что приличное время уходит на создание хорошего гуя. После установки семёрки я пока не реанимировал VMware, так что на работе заценю
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16843 / 5264 / 323
Регистрация: 30.03.2009
Сообщений: 14,159
Записей в блоге: 26
16.11.2009, 11:34     Пишем свой интерпретатор языка BASIC #236
А ты Qt по какому мануалу осваивал? А то я вот всё думаю, что надо на Qt переходить, но вот как-то разбираться постоянно ломает
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
16.11.2009, 11:45  [ТС]     Пишем свой интерпретатор языка BASIC #237
А я не осваивал.Там в документации есть примеры (с исходниками),я просто взял за основу исходники окошка с подсветкой кода,немного поменял его (там понятный код,правда шаблоны знаков ("регекспы") пришлось методом тыка делать).Ещё там рядом есть пример,но с ToolBar-ом с кнопочками-иконками,я оттуда перенёс этот тулбар.
Сначала-то я вообще не знал,с чего начинать,то ли .ui файл делать с дизайнером,то ли ещё как-то.Тыкался,тыкался,пока не содрал из документации.В-общем,хотел как лучше,а получилось как всегда
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16843 / 5264 / 323
Регистрация: 30.03.2009
Сообщений: 14,159
Записей в блоге: 26
16.11.2009, 12:05     Пишем свой интерпретатор языка BASIC #238
Понятно. Тогда придётся всё-таки осваивать. Ибо для полного счастья надо ещё и отладчик встроенный сделать. Ну а потом остаётся тупое развитие и добавление новых инструкций языка

Добавлено через 55 секунд
Кстати, у тебя как сечас сделано? И gui, и интерпретатор всё в одном флаконе? По идее нормально должно быть так, что интерпретатор отдельно, а гуй отдельно и из гуя вызывается (запускается) интерпретатор
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
16.11.2009, 23:31  [ТС]     Пишем свой интерпретатор языка BASIC #239
Ой,а у меня наоборот )). Я просто подумал,что консольный вариант главный,gui только как опция.Ну ещё от незнания,что-то я сразу не представил вариант,когда gui вызывает консольное приложение.Просто тогда получается,что нужно все исходники интерпретатора вкладывать в проект GUI (это мне так видится).Или как иначе? В Qt же по нажатию на кнопку нужно вызывать функцию из интерпретатора,а как её туда "приделать"?
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
16.11.2009, 23:57     Пишем свой интерпретатор языка BASIC
Еще ссылки по теме:

Пишем свой чекер C++
Не удается откомпилировать интерпретатор М-языка C++
Пишем свой класс, спецификатор доступа protected C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16843 / 5264 / 323
Регистрация: 30.03.2009
Сообщений: 14,159
Записей в блоге: 26
16.11.2009, 23:57     Пишем свой интерпретатор языка BASIC #240
Нормально должно быть так. Отдельно пишется интерпретатор (т.е. то, что у тебя было до гуя). Отдельно пишется графическая оболочка с редактором. Далее в этой графической оболочке при нажатии на клавишу "Run" у тебя записывается редактируемый файл с исходником (чтобы все правки подцепились), после чего запускается "basin source.bas". По завершении исполнения программы анализируем код возврата: если у нас отработало нормально, значит передаём управление обратно на редактор. Если завершилось с ошибкой, то анализируем выдачу от интерпретатора в stdout: парсим то, что он напечатал (вырезаем имя файла и номер строки), позиционируем курсор на той строке исходника и где-то отображаём полную выдачу ошибки от интерпретатора

В нормальном случае делается так, что отдельной поставкой выдаётся интерпретатор (как логически законченная программа), а дополнительной поставкой ставится гуй, в качестве настройки которому надо указать, где лежит интерпретатор. И как вариант делается некоторая полная поставка, включающая в себя оба компонента. Технически удобнее держать исходники в разных каталогах: интерпретатор отдельно, гуй отдельно. Делать это в одном репозитории или в двух разных - хз как удобнее. Для начала делай так, как удобнее, а там будет видно, как надо правильно

Цитата Сообщение от #pragma Посмотреть сообщение
В Qt же по нажатию на кнопку нужно вызывать функцию из интерпретатора,а как её туда "приделать"?
Какую функцию и что значит "вызвать"
Yandex
Объявления
16.11.2009, 23:57     Пишем свой интерпретатор языка BASIC
Закрытая тема Создать тему
Опции темы

Текущее время: 06:21. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru