Перейти к содержанию
    

Движок для исполнения пользовательского скрипта.

10 минут назад, xvr сказал:

Вы внутрь этого смотрели? Это только для сильных духом :crazy: 

А кто вам сказал, что ТС будет с этим разбираться? Он впендюрит этот код к себе в проект как есть, а потом, если что-то не заработает как надо, то разбираться будем в этом здесь, всем форумом.  :biggrin:

как будто в первой раз....

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 minute ago, xvr said:

Вы внутрь этого смотрели? Это только для сильных духом :crazy: (И размер в 500 строк ужаса содержимого не компенсируют)

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

слабые духом могут в те три функции что непосредственно парсят С, даже не заглядывать, а просто добавить доступ из скрипта к своим "внешним переменным" вместо маллоков и принтфов, ничего особо не меняя.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вопрос к jenya7 - будете смотреть с4, или мне продолжать писать тут движок?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, xvr said:

Вопрос к jenya7 - будете смотреть с4, или мне продолжать писать тут движок?

 

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

Изменено пользователем jenya7

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ок, надеюсь что у меня получится понятнее :)

 

Продолжим - лексер.

Spoiler

int Parser::yylex(void *out_)
{
    static std::map<std::string, std::pair<int, int>> ids {
#define VAR(n) {#n, {vID, IDV_##n}},
#include "var_defs.inc"
#undef VAR
    {"if", {tIF, 0}},
    {"then", {tTHEN, 0}}
    };
    
    YYSTYPE& out = *(YYSTYPE*)out_;

    while(isspace(*input)) ++input;

    if (isdigit(*input))
    {
        out.i_const = strtol(input, (char**)&input, 0);
        return vNUM;
    }

    if (isalnum(*input) || *input=='_')
    {
        char* start = input;
        while(isalnum(*input) || *input=='_') ++input;
        std::string tok(start, input);
        if (ids.count(tok))
        {
            auto& value = ids[tok];
            out.i_const = value.second;
            return value.first;
        }
        yyerror("Unexpected ID `"+tok+"`");
        return -1;
    }

#define V2(c1, c2) (uint8_t(c1) + 256*uint8_t(c2))
    input += 2;
    switch(V2(input[-2], input[-1]))
    {
        case V2('>', '='): return sGE;
        case V2('<', '='): return sLE;
        case V2('=', '='): return sEQ;
        case V2('!', '='): return sNE;
        case V2('+', '='): return sASSIGN_ADD;
        case V2('-', '='): return sASSIGN_SUB;
        case V2('&', '&'): return sAND;
        case V2('|', '|'): return sOR;
    }
    return (--input)[-1];
#undef V2
}

 

В файле var_defs.inc лежит список ваших переменных в таком виде -

Spoiler

VAR(CMP)
VAR(PWM0)
VAR(PWM1)

 

Этот файл так же общий для РС и интерпретатора на МК

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 hours ago, xvr said:

 

  Reveal hidden contents


VAR(CMP)
VAR(PWM0)
VAR(PWM1)

 

Этот файл так же общий для РС и интерпретатора на МК

 

как общий? я на МК пишу на С не С++.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, xvr said:

Там чистый С

 

Выглядит как С++   int Parser::yylex(void *out_)

Изменено пользователем jenya7

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

22 hours ago, jenya7 said:

Выглядит как С++   int Parser::yylex(void *out_)

Не этот файл общий, а var_defs.inc

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Продолжаем. Буффер кода и процедуры генерации байткода

Spoiler

void CodeGenerator::push(int opc, int length, int32_t data, const std::string& asm_menmonic)
{
    size_t start_shift = code_image.size();
    code_image.push_back(opc);
    uint8_t* p = (uint8_t*)&data;
    while(length--) code_image.push_back(*p++);

    if (lst_file)
    {
        std::string acc
        for(size_t i=start_shift; i<code_image.size(); ++i) acc += prn("%02X ", code_image[i]);
        add_lst_line(prn("%04X: %-15s %s", start_shift, acc.c_str(), asm_menmonic.c_str()));
    }
}
///////////////////////////////////////////////////////////////////////////////////////

void NodeConst::generate_code(CodeGenerator &cg)
{
    auto asm = prn("Const %d", value);
    if (int16_t(value) != value) cg.push4(OP_Const4, value, asm); else
    if (value >= -64 && value <= 63) cg.push0(OP_Const0 | (value & 0x7F), asm);
    else cg.push2(OP_Const2, value, asm);
}

void NodeVar::generate_code(CodeGenerator &cg)
{
    static const char* mnems[] = {"ReadVar", "SetVar", "AddVar"};
    static const char* ids[] = {
#define VAR(n) #n,
#include "var_defs.inc"
#undef VAR
        NULL
    };
    if (assign_value) assign_value->generate_code(cg);
    auto asm = prn("%s #%d (%s)", mnems[(opcode - OP_ReadVar) >> 5], index, ids[index]);
    if (index<31) cg.push0(opcode | (index&0x31), asm);
    else cg.push1(opcode | 31, index-31, asm);
    if (next) next->generate_code(cg);
}

void NodeOp::generate_code(CodeGenerator &cg)
{
    if (opcode == OP_Stop) cg.push0(OP_Stop, "STOP"); else
    {
        left->generate_code(cg);
        if (right) right->generate_code(cg);
        static const char* mnems[]= {"EQ", "NE", "GT", "GE", "And", "Or", "Add", "Sub", "Mul", "Div", "Mod", "Neg"};
        cg.push0(opcode, mnems[opcode-OP_EQ]);
    }
}

 

Гененратор корневой конструкции (if ... then ...)

Spoiler

void Parser::add_if(Node* if_expression, NodeVar* then_expression)
{
    if_expression->generate_code(code);
    size_t body_size;
    {
        CodeGenerator tmp_code;
        then_expression->generate_code(tmp_code);
        body_size = tmp_code.get_image().size();
    }
    size_t shift = code.get_image().size() + body_size + 2;
    if (body_size <= 255) code.push1(OP_JmpF1, body_size, prn("JumpF +%d (=> %04X)", body_size, shift));
    else code.push2(OP_JmpF2, body_size, prn("JumpF +%d (=> %04X)", body_size, shift + 1));
    then_expression->generate_code(code);
    delete if_expression;
    delete then_expression;
}

 

И оптимизаторы для унарного минуса и бинарных операций

Spoiler

Node* Parser::new_unary(Opcode opcode, Node* left)
{
    if (opcode == OP_Neg) // For now this is only possible
    {
        switch(left->opcode) // Find for possible optimisation
        {
            case OP_Neg: {auto result = left->left; left->left=NULL; delete left; return result;} // Remove - - sequence
            case OP_Add: left->opcode = OP_Sub; return left; // Change += for -=
            case OP_Sub: left->opcode = OP_Add; return left; // Change -= for +=
            case OP_Const0: ((NodeConst*)left)->value *= -1; return left; // Flip sign of constant value
        }
    }
    return new NodeOp(opcode, left);
}

Node* Parser::new_binary(Opcode opcode, Node* left, Node* right)
{
    if (left->opcode == OP_Const0 && right->opcode == OP_Const0) // Constant evaluation
    {
        int32_t l = ((NodeConst*)left)->value;
        int32_t r = ((NodeConst*)right)->value;
        delete left; delete right;
        switch(opcode)
        {
            case OP_EQ:   r = l == r; break;
            case OP_NE:   r = l != r; break;
            case OP_GT:   r = l > r; break;
            case OP_GE:   r = l >= r; break;
            case OP_And:  r = l && r; break;
            case OP_Or:   r = l || r; break;
            case OP_Add:  r = l + r; break;
            case OP_Sub:  r = l - r; break;
            case OP_Mul:  r = l * r; break;
            case OP_Div:  r = l / r; break;
            case OP_Mod:  r = l % r; break;
            default: r=0; // this is should never happened!
        }
        return new NodeConst(r);
    }
    if (left->opcode == OP_Const0 || right->opcode == OP_Const0) // Shortcut with some constants
    {
#define L(opc, value) ((opc<<3) | ((value+1)<<1))       // <const> <opcode> <some> version
#define R(opc, value) (L(opc, value) | 1)               // <some> <opcode> <const> version
        Node *const_ptr = left;
        Node *other_ptr = right;
        int32_t value;
        int sw;
        if (left->opcode == OP_Const0)
        {
            const_ptr = left;
            other_ptr = right;
            value = ((NodeConst*)const_ptr)->value;
            sw = L(opcode, value);
        }
        else
        {
            const_ptr = right;
            other_ptr = left;
            value = ((NodeConst*)const_ptr)->value;
            sw = R(opcode, value);
        }
        if (value && other_ptr->opcode == OP_Or) {delete const_ptr; delete other_ptr; return new NodeConst(1);}
        if (value >= -1 && value <= 1)
            switch(sw) // Try to optimize
            {
#define LR(opc, num) L(opc, num): case R(opc, num)
                // <some> + 0, 0 + <some>, <some> * 1, 1 * <some>, <some> / 1  =>  <some>
                case LR(OP_Add,0):  case LR(OP_Mul,1): case R(OP_Sub,0): case R(OP_Div,1): delete const_ptr; return other_ptr;
                // <some> * -1, -1 * <some>, 0 - <some> => - <some>
                case LR(OP_Mul,-1): case R(OP_Div,-1): case L(OP_Sub,0): delete const_ptr; return new_unary(OP_Neg, other_ptr);
                // <some> * 0, 0 * <some>, <some> % 1  => 0
                case LR(OP_Mul,0):  case LR(OP_And,0): case R(OP_Mod,1): delete const_ptr; delete other_ptr; return new NodeConst(0);
            }
#undef L
#undef R
#undef LR
    }
    return new NodeOp(opcode, left, right);
}

 

В принципе оптимизаторы можно выкинуть (за исключением последовательности - <const>), если предполагать, что пользователь не будет писать бессмыссленных выражений (и что он сам умеет считать и сможет вычислить константы)

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

56 минут назад, xvr сказал:

В принципе оптимизаторы можно выкинуть (за исключением последовательности - <const>), если предполагать, что пользователь не будет писать бессмыссленных выражений (и что он сам умеет считать и сможет вычислить константы)

Лучше предполагать, что он так будет делать. Так как это - хороший стиль. Код должен быть самокомментируемым. По возможности. А не состоять из малоосмысленных числовых выражений x=1234+7654. И числовые константы - вещь очень нужная.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 hours ago, xvr said:

Продолжаем. Буффер кода и процедуры генерации байткода

  Reveal hidden contents


void CodeGenerator::push(int opc, int length, int32_t data, const std::string& asm_menmonic)
{
    size_t start_shift = code_image.size();
    code_image.push_back(opc);
    uint8_t* p = (uint8_t*)&data;
    while(length--) code_image.push_back(*p++);

    if (lst_file)
    {
        std::string acc
        for(size_t i=start_shift; i<code_image.size(); ++i) acc += prn("%02X ", code_image[i]);
        add_lst_line(prn("%04X: %-15s %s", start_shift, acc.c_str(), asm_menmonic.c_str()));
    }
}
///////////////////////////////////////////////////////////////////////////////////////

void NodeConst::generate_code(CodeGenerator &cg)
{
    auto asm = prn("Const %d", value);
    if (int16_t(value) != value) cg.push4(OP_Const4, value, asm); else
    if (value >= -64 && value <= 63) cg.push0(OP_Const0 | (value & 0x7F), asm);
    else cg.push2(OP_Const2, value, asm);
}

void NodeVar::generate_code(CodeGenerator &cg)
{
    static const char* mnems[] = {"ReadVar", "SetVar", "AddVar"};
    static const char* ids[] = {
#define VAR(n) #n,
#include "var_defs.inc"
#undef VAR
        NULL
    };
    if (assign_value) assign_value->generate_code(cg);
    auto asm = prn("%s #%d (%s)", mnems[(opcode - OP_ReadVar) >> 5], index, ids[index]);
    if (index<31) cg.push0(opcode | (index&0x31), asm);
    else cg.push1(opcode | 31, index-31, asm);
    if (next) next->generate_code(cg);
}

void NodeOp::generate_code(CodeGenerator &cg)
{
    if (opcode == OP_Stop) cg.push0(OP_Stop, "STOP"); else
    {
        left->generate_code(cg);
        if (right) right->generate_code(cg);
        static const char* mnems[]= {"EQ", "NE", "GT", "GE", "And", "Or", "Add", "Sub", "Mul", "Div", "Mod", "Neg"};
        cg.push0(opcode, mnems[opcode-OP_EQ]);
    }
}

 

Гененратор корневой конструкции (if ... then ...)

  Reveal hidden contents


void Parser::add_if(Node* if_expression, NodeVar* then_expression)
{
    if_expression->generate_code(code);
    size_t body_size;
    {
        CodeGenerator tmp_code;
        then_expression->generate_code(tmp_code);
        body_size = tmp_code.get_image().size();
    }
    size_t shift = code.get_image().size() + body_size + 2;
    if (body_size <= 255) code.push1(OP_JmpF1, body_size, prn("JumpF +%d (=> %04X)", body_size, shift));
    else code.push2(OP_JmpF2, body_size, prn("JumpF +%d (=> %04X)", body_size, shift + 1));
    then_expression->generate_code(code);
    delete if_expression;
    delete then_expression;
}

 

И оптимизаторы для унарного минуса и бинарных операций

  Reveal hidden contents


Node* Parser::new_unary(Opcode opcode, Node* left)
{
    if (opcode == OP_Neg) // For now this is only possible
    {
        switch(left->opcode) // Find for possible optimisation
        {
            case OP_Neg: {auto result = left->left; left->left=NULL; delete left; return result;} // Remove - - sequence
            case OP_Add: left->opcode = OP_Sub; return left; // Change += for -=
            case OP_Sub: left->opcode = OP_Add; return left; // Change -= for +=
            case OP_Const0: ((NodeConst*)left)->value *= -1; return left; // Flip sign of constant value
        }
    }
    return new NodeOp(opcode, left);
}

Node* Parser::new_binary(Opcode opcode, Node* left, Node* right)
{
    if (left->opcode == OP_Const0 && right->opcode == OP_Const0) // Constant evaluation
    {
        int32_t l = ((NodeConst*)left)->value;
        int32_t r = ((NodeConst*)right)->value;
        delete left; delete right;
        switch(opcode)
        {
            case OP_EQ:   r = l == r; break;
            case OP_NE:   r = l != r; break;
            case OP_GT:   r = l > r; break;
            case OP_GE:   r = l >= r; break;
            case OP_And:  r = l && r; break;
            case OP_Or:   r = l || r; break;
            case OP_Add:  r = l + r; break;
            case OP_Sub:  r = l - r; break;
            case OP_Mul:  r = l * r; break;
            case OP_Div:  r = l / r; break;
            case OP_Mod:  r = l % r; break;
            default: r=0; // this is should never happened!
        }
        return new NodeConst(r);
    }
    if (left->opcode == OP_Const0 || right->opcode == OP_Const0) // Shortcut with some constants
    {
#define L(opc, value) ((opc<<3) | ((value+1)<<1))       // <const> <opcode> <some> version
#define R(opc, value) (L(opc, value) | 1)               // <some> <opcode> <const> version
        Node *const_ptr = left;
        Node *other_ptr = right;
        int32_t value;
        int sw;
        if (left->opcode == OP_Const0)
        {
            const_ptr = left;
            other_ptr = right;
            value = ((NodeConst*)const_ptr)->value;
            sw = L(opcode, value);
        }
        else
        {
            const_ptr = right;
            other_ptr = left;
            value = ((NodeConst*)const_ptr)->value;
            sw = R(opcode, value);
        }
        if (value && other_ptr->opcode == OP_Or) {delete const_ptr; delete other_ptr; return new NodeConst(1);}
        if (value >= -1 && value <= 1)
            switch(sw) // Try to optimize
            {
#define LR(opc, num) L(opc, num): case R(opc, num)
                // <some> + 0, 0 + <some>, <some> * 1, 1 * <some>, <some> / 1  =>  <some>
                case LR(OP_Add,0):  case LR(OP_Mul,1): case R(OP_Sub,0): case R(OP_Div,1): delete const_ptr; return other_ptr;
                // <some> * -1, -1 * <some>, 0 - <some> => - <some>
                case LR(OP_Mul,-1): case R(OP_Div,-1): case L(OP_Sub,0): delete const_ptr; return new_unary(OP_Neg, other_ptr);
                // <some> * 0, 0 * <some>, <some> % 1  => 0
                case LR(OP_Mul,0):  case LR(OP_And,0): case R(OP_Mod,1): delete const_ptr; delete other_ptr; return new NodeConst(0);
            }
#undef L
#undef R
#undef LR
    }
    return new NodeOp(opcode, left, right);
}

 

В принципе оптимизаторы можно выкинуть (за исключением последовательности - <const>), если предполагать, что пользователь не будет писать бессмыссленных выражений (и что он сам умеет считать и сможет вычислить константы)

 

я с нуля никогда не создавал проект С++. В какой среде лучше работать?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Сделал проект в VS2017. Компилятор получился на 500 строк (в сумме по всем файлам)

Пример скрипта:

if CMP == 0 && PWM0 > PWM1 then PWM0 += 1; PWM1 = (CMP-(3-4))

Выхлоп (листинг):

# if CMP == 0 && PWM0 > PWM1 then PWM0 += 1; PWM1 = (CMP-(3-4))
0000: 80              ReadVar #0 (CMP)
0001: 00              Const 0
0002: E4              EQ
0003: 81              ReadVar #1 (PWM0)
0004: 80              ReadVar #2 (PWM1)
0005: E6              GT
0006: E8              And
0007: E2 06           JumpF +6 (=> 000F)
0009: 01              Const 1
000A: C1              AddVar #1 (PWM0)
000B: 80              ReadVar #0 (CMP)
000C: 7F              Const -1
000D: EB              Sub
000E: A0              SetVar #2 (PWM1)

000F: FF              Stop

 

script.zip

20 minutes ago, jcxz said:

И числовые константы - вещь очень нужная.

угу, только в этом недоязыке их нет :( Но можно пропустить через CPP препроцессор - появятся :)

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 hours ago, xvr said:

Сделал проект в VS2017. Компилятор получился на 500 строк (в сумме по всем файлам)

 

А какой тип проекта? там несколько.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

18 hours ago, jenya7 said:

А какой тип проекта? там несколько.

В атаче в моём предыдущем посте готовый проект (вместе с всеми необходимыми для VS файлами)

 

Проект - С++ Win32 Console

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...