VCP Scripter

VCP Scripter. Часть 2. Синтактсис

Друзья, добрый день! Я рад видеть вас на страницах своего блога. Сегодня, я продолжаю серию заметок, посвященных VCP Scripter, и, специально для тех, кто уже читал предыдущую заметку про API и осознал те удобства, которые в нем предлагаются, я хочу рассказать про синтаксис VCP Scripter и его особенности.

VCP Scripter использует синтаксис Pascal, поэтому если вы хорошо знакомы с Pascal и уже активно применяете VCP Scripter, то можете пропустить эту заметку и не читать дальше. Эта заметка в бОльшей степени ориентирована на тех пользователей, кто не знаком с Pascal, не знает особенностей реализации VCP Scripter, но имеет общее представление об основах программирования и хочет начать писать свои скрипты.

Важно помнить и понимать, что VCP Scripter это частичная реализация, поэтому некоторые примеры, которые доступны по ссылке http://www.tutorialspoint.com/pascal, рекомендуемой в качестве руководства в официальном API, не пройдут проверку синтаксиса в редакторе VCP System и не будут выполнены. Забегая вперед, скажу, что весь необходимый функционал, тем не менее, присутствует и «выпилено», действительно, только то, что не очень-то и нужно или вообще не нужно =)

Русскоязычным пользователям в качестве справочника по Pascal могу порекомендовать материалы, представленные на https://pas1.ru/. Часть последних я активно использовал при написании это заметки.

Общее замечание: в VCP Scripter, как и в Pascal, синтаксис регистронезависимый, поэтому, например, разницы между переменными bcm и BcM нет.

Переменные и их типы

В VCP Scripter доступны для использования следующие типы переменных:

  • ansistring;
  • boolean;
  • byte;
  • char;
  • integer;
  • ShortInt;
  • string;
  • variant.

Если с первыми типами все более менее понятно, то последний тип variant требует небольшого комментария. Этот тип уникален тем, что тип определяется в процессе выполнения скрипта. Переменной такого типа можно присваивать значения других типов, например, integer или string, и менять их процессе выполнения скрипта.

Декларация переменных осуществляется указанием ключевого слова var. Инициализировать значение сразу при декларации переменной в VCP Scripter НЕЛЬЗЯ, зато можно декларировать типы сразу для нескольких переменных.

var
address1, address2: integer;
input: string;

Можно декларировать свои типы, для этого достаточно использовать ключевое слово type. При декларации типов есть ограничение: нельзя декларировать несколько типов за раз. Декларация для каждого типа должна быть на новой строчке, даже если они одинаковые.

type 
ecuAddress = integer;

var 
engine:ecuAddress;

Константы

В VCP Scripter можно объявлять константы. Для этого используется ключевое слово const:

const
bcmAddress = $09;

Массивы

Массивы в VCP Scripter, как и в Pascal, это структура, занимающая в памяти единую область и состоящая из фиксированного числа компонентов одного типа.

Декларация массива состоящего из трех элементов типа string может быть выполнена, например, следующим образом:

type
   addressList = array [ 0..2] of string;
var
   ecus: addressList;

Операторы

Оператор присваивания

В качестве оператора присваивания в VCP Scripter, как и в Pascal, используется конструкция вида :=.

v := a;

Арифметические операторы

Из арифметических операторов в VCP Scripter реализованы все, кроме оператора взятия по модулю % .

+сложение двух операндов
вычитание правого операнда из левого
*умножение операндов
/деление левого операнда на правый
Список математических операторов VCP Scripter

Операторы отношения

Операторы отношения в VCP Scripter доступны все.

=равно
<>НЕ равно
>больше
<меньше
>=больше или равно
<=меньше или равно
Список операторов отношения

Логические операторы

Логические операторы так же представлены в полном объеме.

andлогическое И
orлогическое ИЛИ
notлогическое НЕ
xorлогическое исключающее ИЛИ
Список логических операторов

Битовые операторы

Из битовых операторов VCP Scripter не поддерживаются операторы сдвига вправо >> и влево << , но придумать зачем они могут понадобиться в скриптах сложно.

andпобитовое И
orпобитовое ИЛИ
notпобитовое НЕ
Список битовых операторов

Условные операторы

Оператор if-else

Когда выполнение скрипта доходит до условного оператора if-else, то в зависимости от результата логического выражения в его заголовке выполняются разные блоки кода. В качестве условия может стоять любое выражение, результатом вычисления которого является одно из булевых значений true или false. Если выражение вернуло true, то выполняется блок, начинающийся со слова then, если false – то блок, начинающийся со слова else. После выполнения одного из вложенных блоков кода, выполнение скрипта возвращается в основную ветку. Другой вложенный блок не выполняется.

if YOUR_CONDITION then
    OPERATOR_IN_CONDITION_OF_TRUE
else
    OPERATOR_IN_CONDITION_OF_TRUE;

В качестве примера, можете использовать вот этот скрипт

var 
inp: string;

const
inpMsg = 'Please, input your choice';

begin

   inp := AskUserForInput(inpMsg);

   if (inp = '1') then
      writeln('You choice is 1. Run block A' )
   else
      writeln('You choice is NOT 1. Run block B' );

end.

Непосредственно после then может стоять только один оператор. При необходимости выполнения нескольких операторов они должны быть заключены в операторные скобки begin-end.

if YOUR_CONDITION then
    begin
        YOUR_CODE_IN_CONDITION_OF_TRUE
    end
else
    begin
        YOUR_CODE_IN_CONDITION_OF_FALSE
    end

Ниже пример скрипта, где используется несколько операторов в блоке.

var 
inp: string;

const
inpMsg1 = 'Please, input your first choice';
inpMsg2 = 'Please, input your second choice';

begin

    inp := AskUserForInput(inpMsg);

    if (inp = '1') then
        begin
            writeln('You choice is 1. Operation 1.');
            writeln('... next operation of block');            
        end
    else
        writeln('You choice is NOT 1.')

end.

Допустимо вложение одного оператора if или if-else в другой. При этом, действует следующее правило: каждому then соответствует ближайшее else, не задействованное при установлении соответствия с другим then. Глубина вложенности операторов if может быть сколь угодно большой.

var 
inp1: string;
inp2: string;

const
inpMsg1 = 'Please, input your first choice';
inpMsg2 = 'Please, input your second choice';

begin

    inp1 := AskUserForInput(inpMsg1);
    inp2 := AskUserForInput(inpMsg2);

    if (inp1 = '1') then
        if (inp2 = '3') then
            writeln('You first choice is 1, second choice is 3')  
        else
            writeln('You first choice is 1, second choice is NOT 3') 
    else
        if (inp2 = '2') then
            writeln('You first choice is NOT 1, second choice is 2')  
        else
            writeln('You first choice is NOT 1, second choice is NOT 2') 

end.

Оператор case

Кроме оператора if в VCP Scripter реализован оператор case. Его можно трактовать как некий вопрос, имеющий большое число ответов. В заголовке оператора case вместо логического выражения фигурирует переменная, которую называют селектором. До этого в скрипте ей присваивается какое-либо значение. По ходу выполнения оператора case, значение селектора сравнивается с различными, описанными в нем, альтернативами (метками). Как только совпадение будет найдено, то выполняется блок скрипта для данной метке и происходит выход в основную ветку программы. Метки являются константами, которые может принимать селектор. Тип меток и тип селектора должны быть совместимы по присваиванию.

case YOUR_SELECTOR of
    YOUR_LABEL_A: YOUR_OPERATOR_FOR_LABEL_A ;
    YOUR_LABEL_B: YOUR_OPERATOR_FOR_LABEL_B ;
    YOUR_LABEL_C: YOUR_OPERATOR_FOR_LABEL_C ;
    else YOUR_DEFAULT_OPERATOR 
end;

Пример скрипта с использованием оператора case.

var 
inp: string;

const
inpMsg = 'Please, input your choice';

begin

    inp := AskUserForInput(inpMsg);

    case inp of
        '1': writeln ('Your choice is 1');
        '2': writeln ('Your choice is 2');
        '3': writeln ('Your choice is 3');
        '4': writeln ('Your choice is 4');
        else writeln ('Error')
    end;

end.

На использование оператора case накладываются следующие ограничения:

  • селектор должен иметь какой-либо порядковый тип;
  • каждая метка не может быть переменной или выражением.

Циклы

Цикл for-do

Цикл позволяет выполнить операции, указанные в его теле заданное число раз. В заголовке указываются два значения. Первое значение присваивается переменной-счетчику и от этого значения начинается отсчет количества итераций (повторений). Отсчет идет всегда с шагом равным единице. Второе значение указывает при каком значении счетчика цикл должен остановиться. Количество итераций цикла определяется разностью между вторым и первым значением плюс единица.

for COUNTER:=INITIAL_VALUE to FINAL_VALUE do begin
     YOUR_CODE_HERE;
end;
for COUNTER:=INITIAL_VALUE downto FINAL_VALUE do begin
     YOUR_CODE_HERE;
end;

В Pascal доступно два варианта конструкций с использование служебного слова to (с увеличением счетчика) и с использованием служебного слова downto (с уменьшением счетчика).

VCP Scripter поддерживает оба варианта. Пример цикла for-to с увеличением счетчика ниже.

var
i:byte;

begin

    for i:=1 to 3 do begin
        writeln('iteration');
    end;
 
end.

Цикл while

Цикл while является циклом с предусловием. В заголовке цикла находится логическое выражение. Если оно возвращает true, то тело цикла выполняется, если false – то нет.

while YOUR_CONDITION do begin
    YOUR_CODE_IN_CONDITION_OF_TRUE
end;

Применение цикла while в примере ниже. Работу этого скрипта можно прекратить не только вводом цифры 3, но и нажатием кнопки Cancel или закрытием окна ввода.

var 
inp: string;

const
inpMsg = 'Please, input 3';

begin

    inp := '';
    
    while (inp <> '3') do begin
        writeln('You input ' + inp);
        inp := AskUserForInput(inpMsg);
    end;

end.

Тело цикла выполнится столько раз, сколько раз логическое выражение вернет true.

Цикл repeat

Тело цикла while не выполнится ни разу, если логическое выражение в заголовке сразу вернет false. На практике же бывает, что нужно предусмотреть выполнение тела цикла хотя бы один раз, вне зависимости от того, что вернуло логическое выражение в заголовке при первом обращении. Для таких случаев используется цикл с постусловием.

repeat
    YOUR_CODE;
until YOUR_CONDITION;

Обратите внимание, в отличии от предыдущего примера построенного с использованием цикла while, здесь скрипт будет показывать окно ввода до тех пор, пока не будет введена цифра 3, нажата кнопка Cancel или закрыто окно.

var 
inp: string;

const
inpMsg = 'Please, input 3б press Cancel or close box';

begin

    inp := '';
    
    repeat
        inp := AskUserForInput(inpMsg);
    until (inp <> '3') or (inp <>'');

end.

Операторы break и continue

Оба оператора используются для прерывания хода выполнения цикла.

Оператор break выполняет полный выход из цикла, т.е. все возможные итерации цикла прерываются.

Оператор continue прерывает только текущую итерацию. 

Пример скрипта с использованием оператора break.

var
i:byte;

begin

    for i:=1 to 10 do begin
        writeln('iteration');
        if i = 2 then
            break;
    end;
 
end.

Оператор безусловного перехода goto

Оператор goto осуществляет переход к оператору, помеченному специальной меткой, которая отделяется от самого оператора двоеточием. В качестве метки может быть использовано любое целое число без знака, содержащее более четырех цифр, или любое имя. Чтобы можно было использовать метку, она должна быть объявлена в разделе меток вашего скрипта. Этот раздел начинается служебным словом label. Чтобы перейти к помеченному оператору, используется оператор перехода goto .

label YOUR_LABEL_NAME;

begin
    YOUR_LABEL_NAME:
    YOUR_OPERATOR;

    YOUR_CODE

    goto YOUR_LABEL_NAME;

end.

В примере ниже реализован переход к началу скрипта с помощью метки goStart , в случае если пользователь введет цифру 2, в противном случае выполнение скрипта будет завершено.

label goStart;

var 
inp: string;

const
inpMsg = 'Please, input 2 to run script from begining';

begin
    goStart:
    writeln('Begining');

    inp := AskUserForInput(inpMsg);

    if inp = '2' then
        goto goStart;
                
    writeln('End');

end.

Процедуры и функции

VCP Scripter поддерживает процедуры и функции. В общем виде, процедура — это подпрограмма, которая ничего не возвращает, а функция — это подпрограмма, которая возвращает одиночное значение.

Функции

Конструкция определения функции в VCP Scripter в целом повторяет синтаксис, принятый в Pascal. Единственный момент, это присвоение результата выполнения функции. В VCP Scripter для этого существует специальная переменная result, поэтому результат работы функции нужно присваивать этой переменной.

function FUNCTION_NAME(FIRST_ARG: arg1; SECOND_ARG: type2; ...): FUNCTION_RESULT_TYPE;
local FUNCTION_LOCAL_VAR;
begin
   ...
   YOUR_CODE
   ...
   result := RESULT_OF_CODE;
end;

Очевидно, что в функциях можно использовать только те типы переменных, которые доступны в VCP Scripter.

В следующем примере определена функция ReadLcBit, которая возвращает значение указанного бита, в указанном байте, указанного блока с использование протокола UDS. В теле скрипта функция вызывается фиксированными параметрами: 17 блок, 1 байт, 0 бит.

function ReadLcBit(ecuAddress : string;  byteNo, bitNo : integer ) : integer;
var lc:string;
begin
    if ConnectToEcuUDS(ecuAddress) then
        begin
            lc := ReadLongCoding;
            if TestBitInHexString(lc, byteNo, bitNo) then
                result := 1
            else
                result := 0;
        end
    else
        result := -1;
end;

begin

    case ReadLcBit('17', 1, 0) of
        1: writeln('activated');
        0: writeln('deactivated');
        else writeln('could not connect to ECU');
    end;
 
end.

Процедуры

Конструкция определения процедуры в VCP Scripter в целом повторяет конструкцию определения функцию, с тем отличием, что процедура не возвращает результат.

procedure PROCEDURE_NAME(FIRST_ARG: arg1; SECOND_ARG: type2; ...);
local PROCEDURE_LOCAL_VAR;
begin
   ...
   YOUR_CODE
   ...
end;

Пример простой процедуры, осуществляющей логирование в файл с именем, начинающимся с VIN вы найдете ниже.

var
vin:string;

procedure LogToFile(msg : string);
var fPath: string;
begin
    fPath :=  GetCurrentScriptPath + 'logs\' + vin + '_';
    SaveStringToFile(msg, fPath);
end;

begin

    vin:=ReadVin;
    LogToFile('Your log message');
 
end.

Комментарии

Думаю, что не сильно ошибусь, если скажу, что это один из самых востребованных и полезных элементов синтаксиса в любом языке программирования. VCP Scripter поддерживаются все варианты, доступные в Pascal: как однострочные, так и многострочные. Причем многострочные варианты можно оформлять двумя способами.

// single line comment
(* multiline
comments *)
{ alternative multiline
comments }

Заключение

Пожалуй, это все что я хотел рассказать про синтаксис VCP Scripter. Надеюсь, что вы нашли в этом материале что-то полезное. Если у вас есть что добавить к заметке или обсудить, вы можете сделать на нашем форуме в этой теме.

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