Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
1

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

20.06.2009, 20:03. Показов 238176. Ответов 464
Метки нет (Все метки)

Благодаря форуму и Evg в частности интерпретатор развивается, потихоньку превращаясь в простенький интерпретатор QBASIC.
Некоторые из самых старых версий сохранились в теме и ссылки на них будут добавлены в это сообщение,а также ссылки на другие темы,связанные с этой.

Репозиторий с проектом находится тут, там же есть возможность в браузере посмотреть историю ревизий (английский в логах весьма примитивен,комментарии и рекомендации можете писать в личку),а также скачать самый последний архив репозитория в формате .tar.gz
Если кто-то пользуется Subversion,скачать исходники можно так:
Код
svn co https://basin.svn.sourceforge.net/svnroot/basin basin
Эти темы возникли в результате моих вопросов по ходу написания:
Технический приём для формирования согласованных данных
https://www.cyberforum.ru/c-linux/thread46096.html
Вопрос по svn (Subversion)
Создание системы тестирования ПО.
Вопрос про разные реализации бэйсиков
Можно ли выразить порядковый номер элемента массива через индексы?
[C++] Какие флаги указать линкеру для компиляции программы?
Как можно определить переменную в файле configure.in,чтобы её можно было использовать в Makefile?
Странный SIGSEGV, или что зависит от порядка написания интерфейса класса
https://www.cyberforum.ru/c-linux/thread61324.html
Альтернативная версия интерпретатора от 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;
31
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
20.06.2009, 20:03
Ответы с готовыми решениями:

Пишем свой интерпретатор языка BASIC
Добрый день. Я смотрю, тут на форуме была тема коллективного написания интерпретатора BASIC на...

Пишем свой strlen
Всем привет, вырвал часть задание из общего задание по написанию своего string. На данном этапе...

Пишем свой чекер
Я хочу написать свой чекер, но не знаю с чего начать? Кто знает основные принцип работы чекеров...

пишем свой троян с нуля
Всем привет)))соглашусь, что изобретаю велосипед, но хочется сделать все своими ручками не прибегая...

464
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
26.10.2009, 22:34  [ТС] 221
Да,вот интересно,под виндой работает намного быстрее,причём только в полноэкранном режиме,хотя и не дотягивает до натурального QBASIC-а.
Возможно,тут уже ничего не сделаешь,это уже библиотека так работает видимо,но всё же со временем надо будет попытаться ускорить хоть чуть-чуть.
Небольшое напутствие,если делать линию через цикл от края до края,то почему-то нельзя указывать размер границы,видимо происходит выход запределы экрана и программа падает(скорее всего это в самой SDL)
0
Evg
Эксперт CАвтор FAQ
21245 / 8261 / 636
Регистрация: 30.03.2009
Сообщений: 22,606
Записей в блоге: 30
26.10.2009, 23:29 222
Со скоростью сделать можно, надо только разобраться. На SDL'е написан FreeCraft - по сути переделка 2-го warcraft'а под линух. Всё там шустро бегало. Надо только разобраться, как эта бодяга работает
1
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
28.10.2009, 06:55  [ТС] 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
0
Evg
Эксперт CАвтор FAQ
21245 / 8261 / 636
Регистрация: 30.03.2009
Сообщений: 22,606
Записей в блоге: 30
28.10.2009, 09:56 224
Ты заметил, что когда рисуешь по точкам через PRESET, то тормозит, а когда через LINE - то практически мгновенно. Т.е. проблему я вижу в концепции SDL. Насколько я понимаю, подобные библиотеки работают через некий виртуальный монитор. Т.е. все точки ты сначала наносишь в этот монитор (который попросту лежит в обычной памяти), и эти операции являются быстрыми. Затем ты этот монитор отображаешь в реальную картинку. Делается это путём ПОЛНОГО обновления виртуального монитора на реальную картинку. Этот процесс уже "медленный" (по сравнению с помещением точки в виртуальный монитор)

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

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

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

Добавлено через 5 минут
Ещё надо какую-нить операцию для паузы (опять-таки можно врЕменную), чтобы картинка не удалялась. А то если вставить бесконечный цикл, то программу только по kill -9 смог убить. INPUT так и не понял, как правильно писать
1
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
28.10.2009, 18:40  [ТС] 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бит,но если в силу каких-либо причин этот режим не очень подходит,библиотека сама подберёт лучший режим.
0
Evg
Эксперт CАвтор FAQ
21245 / 8261 / 636
Регистрация: 30.03.2009
Сообщений: 22,606
Записей в блоге: 30
30.10.2009, 18:03 226
Ещё надо разбираться с тем, что если программа зациклилась на интерпретации в то время, как работает графический режим, то кроме как по kill -9 она не убивается
1
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
30.10.2009, 23:30  [ТС] 227
Что-то пока никак не соображу,как же работают таймеры.Вот есть программа,с каким-то списком инструкций.А где-то есть функция,запускающая таймер и следящая за временными отрезками.Но ведь программа не состоит из одной этой функции,как тогда обновляется таймер вовремя? Или сам таймер должен работать как отдельный процесс,или я не знаю.Какая там концепция?
0
Evg
Эксперт CАвтор FAQ
21245 / 8261 / 636
Регистрация: 30.03.2009
Сообщений: 22,606
Записей в блоге: 30
30.10.2009, 23:45 228
Вообще концепция по идее такая, что процесс вызывает ядро и говорит "разбуди через 10 миллисекунд", через 10 миллисекунд выдаётся сигнал, программа прерывается и вызывается пользовательский обработчик сигнала (см. sigaction). Но я не знаю, насколько такое быстро работает, к тому же это линух-зависимое

Что касается встроенной возможности в SDL, то я как-то не понял (правда особо и не разбирался). Но там они пишут, что это замедляет работу, но в SMP этого замедления не будет. SMP, насколько я понимаю, это многопроцессорная машина, а потому вполне возможно, что запускают ещё один процесс
1
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
31.10.2009, 08:02  [ТС] 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?
0
Evg
Эксперт CАвтор FAQ
21245 / 8261 / 636
Регистрация: 30.03.2009
Сообщений: 22,606
Записей в блоге: 30
31.10.2009, 11:51 230
Цитата Сообщение от #pragma Посмотреть сообщение
Так вот эти опции B и BF никак не вяжутся с проверкой на инициализацию,я пока их сделал в ковычках.А вообще наверное придётся генерировать вручную эти опции,подменив TOKEN_IDENT на строковую константу.
Что-то не понял, в чём проблема. В тот момент, когда читаешь токен, проверяешь что его тип равен IDENT, а строковое значение - "B" или "BF"

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

Добавлено через 3 минуты
Цитата Сообщение от #pragma Посмотреть сообщение
Пока нет проверки на рисование за границы экрана,поэтому сразу говорю,неправильные данные рушат SDL.Пока не решил,где и как именно сделать проверку,но это мелочи.
В putpixel
1
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
02.11.2009, 12:23  [ТС] 231
Помнишь,ты писал,что отдельный графический интерфейс делается в виде библиотеки .so или .dll? А что должно быть внутри библиотеки? Как именно она должна взаимодействовать с консольным приложением? Есть ли там функция main,всмысле это тоже отдельная программа? Я поискал,некоторые советуют делать общение GUI с программой через сокеты.Но в тоже время есть класс в Qt,называется QProcess,и он предназначен для запуска внешних программ.Значит,если в таком варианте,нужно разработать ещё одно приложение с окнами,которое будет обрабатывать события и запускать что нужно с необходимыми опциями командной строки.Но тогда это уже нельзя сделать в виде .so?
0
Evg
Эксперт CАвтор FAQ
21245 / 8261 / 636
Регистрация: 30.03.2009
Сообщений: 22,606
Записей в блоге: 30
02.11.2009, 15:38 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 Кб, 86 просмотров)
1
Evg
Эксперт CАвтор FAQ
21245 / 8261 / 636
Регистрация: 30.03.2009
Сообщений: 22,606
Записей в блоге: 30
13.11.2009, 14:14 233
Оказывается, я тебе наврал про реализацию двухаргументных арифметических операций. Я почему-то всю жизнь считал, что в Си второй аргумент приводится к типу первого. Оказывается это не так. Из двух аргументов выбирается "более широкий тип" и оба аргумента приводятся к этому типу (а точнее, приводится только один из аргументов, ибо второй имеет этит тип) и результат имеет этот самый более широкий тип. Считается, что плавающий тип шире целого

Т.е. я утверждал, что INT + FLOAT эквивалентно (int) (INT + (int)FLOAT), на самом деле это (float) ((float)INT + FLOAT). Т.е. поведение в этом месте бэйсика и Си совпадают. Собственно из-за неправильной реализации у меня коряво работала картинка с синусами и косинусами - мне приходилось заводить плавающие константы
1
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
14.11.2009, 12:27  [ТС] 234
Сделал первую рабочую версию графического интерфейса в виде .so.Особо не ковырялся,просто взял примеры из документации,поменял немного.Интерфейс очень простой,там в этих классах потонуть можно,столько всего.
Чтобы запустить программу с графическим интерфейсом,нужно использовать опцию --gui
Вот так выглядит это дело у меня:

Пока имеет смысл только кнопочка Run (Третья слева).Но ещё всё пока не доделано.
К сожалению пока даже правки,сделанные в редакторе,не учтутся при интерпретации.Надо разобраться с освобождением памяти при открытии нового файла и т.д.Есть небольшая проблема: функция setupEditor() запускается в конструкторе окна,и в этой функции путь к файлу уже должен быть известен,но я не могу послать его в конструктор,что-то не даёт.Типа параметры конструктора менять нельзя.Наверное,самый нормальный выход-просто открывать "пустой файл".
Есть проблема с передачей управления окну с графикой-я не знаю,как это делать,это ведь не какое-нибудь окно производимое от Qt ,а создаваемое другой библиотекой.Получается,что после прорисовки графики окно с графикой закрыть нельзя,так как события от клавы не попадают в деструктор для SDL(именно в деструкторе graphics у меня сделано что-то наподобие перехватчика нажатий клавиш,и при нажатии на любую клавишу SDL производит выход).
С добавлением кода библиотеки получилось,что проект достиг почти 20000 строк.
Поскольку иконки и примеры распространяются по GPL ,то gui тоже распространяется по GPL,всё остальное-BSD
Все мои старания,связанные с интерфейсом,дают понять,что разработка хорошего интерфейса-дело непростое,и требует больших знаний и кропотливой работы.
0
Evg
Эксперт CАвтор FAQ
21245 / 8261 / 636
Регистрация: 30.03.2009
Сообщений: 22,606
Записей в блоге: 30
14.11.2009, 12:43 235
Цитата Сообщение от #pragma Посмотреть сообщение
Все мои старания,связанные с интерфейсом,дают понять,что разработка хорошего интерфейса-дело непростое,и требует больших знаний и кропотливой работы.
Это точно. Я сел осваивать Borland Builder. Получается так, что приличное время уходит на создание хорошего гуя. После установки семёрки я пока не реанимировал VMware, так что на работе заценю
1
Evg
Эксперт CАвтор FAQ
21245 / 8261 / 636
Регистрация: 30.03.2009
Сообщений: 22,606
Записей в блоге: 30
16.11.2009, 11:34 236
А ты Qt по какому мануалу осваивал? А то я вот всё думаю, что надо на Qt переходить, но вот как-то разбираться постоянно ломает
1
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
16.11.2009, 11:45  [ТС] 237
А я не осваивал.Там в документации есть примеры (с исходниками),я просто взял за основу исходники окошка с подсветкой кода,немного поменял его (там понятный код,правда шаблоны знаков ("регекспы") пришлось методом тыка делать).Ещё там рядом есть пример,но с ToolBar-ом с кнопочками-иконками,я оттуда перенёс этот тулбар.
Сначала-то я вообще не знал,с чего начинать,то ли .ui файл делать с дизайнером,то ли ещё как-то.Тыкался,тыкался,пока не содрал из документации.В-общем,хотел как лучше,а получилось как всегда
0
Evg
Эксперт CАвтор FAQ
21245 / 8261 / 636
Регистрация: 30.03.2009
Сообщений: 22,606
Записей в блоге: 30
16.11.2009, 12:05 238
Понятно. Тогда придётся всё-таки осваивать. Ибо для полного счастья надо ещё и отладчик встроенный сделать. Ну а потом остаётся тупое развитие и добавление новых инструкций языка

Добавлено через 55 секунд
Кстати, у тебя как сечас сделано? И gui, и интерпретатор всё в одном флаконе? По идее нормально должно быть так, что интерпретатор отдельно, а гуй отдельно и из гуя вызывается (запускается) интерпретатор
1
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
16.11.2009, 23:31  [ТС] 239
Ой,а у меня наоборот )). Я просто подумал,что консольный вариант главный,gui только как опция.Ну ещё от незнания,что-то я сразу не представил вариант,когда gui вызывает консольное приложение.Просто тогда получается,что нужно все исходники интерпретатора вкладывать в проект GUI (это мне так видится).Или как иначе? В Qt же по нажатию на кнопку нужно вызывать функцию из интерпретатора,а как её туда "приделать"?
0
Evg
Эксперт CАвтор FAQ
21245 / 8261 / 636
Регистрация: 30.03.2009
Сообщений: 22,606
Записей в блоге: 30
16.11.2009, 23:57 240
Нормально должно быть так. Отдельно пишется интерпретатор (т.е. то, что у тебя было до гуя). Отдельно пишется графическая оболочка с редактором. Далее в этой графической оболочке при нажатии на клавишу "Run" у тебя записывается редактируемый файл с исходником (чтобы все правки подцепились), после чего запускается "basin source.bas". По завершении исполнения программы анализируем код возврата: если у нас отработало нормально, значит передаём управление обратно на редактор. Если завершилось с ошибкой, то анализируем выдачу от интерпретатора в stdout: парсим то, что он напечатал (вырезаем имя файла и номер строки), позиционируем курсор на той строке исходника и где-то отображаём полную выдачу ошибки от интерпретатора

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

Цитата Сообщение от #pragma Посмотреть сообщение
В Qt же по нажатию на кнопку нужно вызывать функцию из интерпретатора,а как её туда "приделать"?
Какую функцию и что значит "вызвать"
1
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
16.11.2009, 23:57
Помогаю со студенческими работами здесь

Пишем свой класс, спецификатор доступа protected
Всем привет! Из книги Р. Лафоре относительно спецификатора доступа protected: Далее пишется...

Интерпретатор небольшого языка программирования на С++
Здравствуйте, уважаемые форумчане! Я тут где-то год назад прочитал тему Evg и #pragma о создании...

Не удается откомпилировать интерпретатор М-языка
Задача: взять интерпретатор М-языка на сайте...

Интерпретатор музыки стандарта BASIC PLAY на С++
У кого нибудь есть функция или класс, который сможет воспроизводить в С++ напрямую музыкальные...

Написать интерпретатор программного языка -помощь
Здраствуйте! Ребят, кто хорошо разбирается в C++ помогите пожалуйста с реализацией данного задания...

Интерпретатор/компилятор ассемблер-подобного языка
Привет! Чую, что изобрёл велисипед, даже скорее велопарк, но всё же, поделюсь: Некоторое время...


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

Или воспользуйтесь поиском по форуму:
240
Закрытая тема Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2022, CyberForum.ru