Краткое описание языка EasyLogic
Скриптовый язык EasyLogic -- это специализированный простой Си подобный язык со статической типизацией применяемый для расширения возможностей контроллеров Galileosky.
Основные возможности:
функции для работы с портами (RS232, RS485, CAN port) и входами / выходами.
доступ к тегам (чтение / запись)
доступ к переменным графического алгоритма (чтение / запись), из которого запущен скрипт
доступ к глобальным переменным (только чтение)
работа с файлами на SD-карте
Он поддерживает написание кода в функциональном и императивном стиле.
Используемые типы данных:
bool
int32 - знаковые целые числа с диапазоном от -2 147 483 648 до 2 147 483 647
одномерные и многомерные массивы int32[]
строки символов в формате ASCII (которые хранятся как одномерные массивы символов)
Синтаксис языка программирования
Отличительные особенности от языка C:
не требуется добавление заголовочных файлов
символы ";" опциональны, за исключением случаев, когда несколько выражений записаны в одной строке
если тело функции состоит из одной инструкции, то опоясывающие скобки {} не обязательны
Комментарии
// однострочный комментарий
/* многострочный
комментарий
*/
Переменные и константы
#define CONST_5 5
const CONST_0 = 0
new var1
new var2 = 0
var1 = CONST_5
var2 = CONST_0
var1 = var2
Массивы
Индексация начинается с 0 элемента. Массив представляет собой последовательность 32-битных слов, доступ к которым осуществляется с помощью квадратных скобок []. Каждое слово состоит из 4 байтов, поэтому для сокращения использования памяти, можно использовать побайтовый доступ с помощью фигурных скобок {}, что позволяет использовать массив как последовательность байт. Так же, как и обычные переменные массив может быть объявлен как константа и/или быть инициализированным.
const PORT_BUF_SIZE = 245
new oBuf{PORT_BUF_SIZE} //Объявление массива, размером 245 байт, занимающий в памяти 245/4 => 62 элемента (32-битных слова)
new cBuf[10] //Объявление массива размером 10 элементов (32-битных слов), т.е 10*4 => 40 байт
new const fMap_fAttr[2] = [0x20000, 0x50000] //Объявление постоянного массива с его инициализацией
Примеры доступа к элементам массива:
oBuf{0} = 2 //Присвоение 0-му байту массива значения 2
oBuf[1] = 0x5028100 //Присвоение 1-му элементу того же массива значения 0x5028100, что будет эквивалентно следующим действиям:
//oBuf{4} = 0x05
//oBuf{5} = 0x02
//oBuf{6} = 0x81
//oBuf{7} = 0x00
cBuf{1} = 0x5 //Эквивалентно следующей записи(внимание, записываются сразу 4 байта): cBuf[0] = 0x00050000
Многомерные массивы - это массивы, содержащие в себе ссылки на подмассивы. Каждый подмассив может иметь разную длину. Ниже приведены примеры объявления двумерных массивов:
new a[4][3] //4 строки по 3 столбца в каждой
/* Двумерный массив с подмассивами различной длины.
e[1][5] содержит букву "l", но
e[0][5] - недопустимый элемент, т.к
длина подмассива 0 равна 3 ("O", "K", "\0")
*/
new e[2][] = [ "OK", "Cancel" ]
Для определения размера массива используется оператор sizeof. Этот оператор возвращает количество элементов (32-битных слов), а не байт! Для многомерных массивов вызов данного оператора с именем массива без скобок вернёт главную размерность, со скобками - размерность подмассива:
new matrix[3][2] = { { 1, 2 }, { 3, 4 }, { 5, 6 } }
Diagnostics("%d %d", sizeof matrix, sizeof matrix[]); // В диагностике будет строка "3 2"
Операторы
Обычные скобки () - управление порядком вычисления выражения. Квадратные скобки [] применяются для индексации массива. Оператор new используется для создания переменных и массивов.
Математические операторы
умножение (a * b),
деление (a / b) - дробная часть отбрасывается,
сложение (a + b),
вычитание (a - b),
остаток от деления a на b (a % b).
Операторы сравнения
«меньше» (<)
«меньше или равно» (<=)
«больше» (>)
«больше или равно» (>=)
«равно» (==)
«не равно» (!=)
Логические операторы
«И» (&&)
«ИЛИ» (||)
«НЕ» (!) - инвертирует значение отличное от нуля в 0, а 0 в 1, true в false и на наоборот.
Битовые операторы
«И» (&)
«ИЛИ» (|)
«НЕ» (~)
исключающее ИЛИ (XOR) (^) ```cpp
// Определяем 8 отдельных битовых флагов (они могут представлять всё, что вы захотите). const option1 = 0x01; // шестнадцатеричный литерал для 0000 0001 const option2 = 0x02; // шестнадцатеричный литерал для 0000 0010 const option3 = 0x04; // шестнадцатеричный литерал для 0000 0100 const option4 = 0x08; // шестнадцатеричный литерал для 0000 1000 const option5 = 0x10; // шестнадцатеричный литерал для 0001 0000 const option6 = 0x20; // шестнадцатеричный литерал для 0010 0000 const option7 = 0x40; // шестнадцатеричный литерал для 0100 0000 const option8 = 0x80; // шестнадцатеричный литерал для 1000 0000
// Байтовое значения для хранения комбинаций из 8 возможных вариантов new myflags = 0; // все флаги/параметры отключены до старта
Чтобы узнать битовое состояние, используется побитовое И:
```cpp
if (myflags & option4) ... // если option4 установлено - что-нибудь делаем
Чтобы включить биты, используется побитовое ИЛИ:
myflags |= option4; // включаем option4
myflags |= (option4 | option5); // включаем option4 и option5
Чтобы выключить биты, используется побитовое И с инвертированным литералом:
myflags &= ~option4; // выключаем option4
myflags &= ~(option4 | option5); // выключаем option4 и option5
Для переключения между состояниями бит, используется побитовое исключающее ИЛИ (XOR):
myflags ^= option4; // включаем или выключаем option4
myflags ^= (option4 | option5); // изменяем состояния option4 и option5
Операторы выбора
if (a > b)
{
Diagnostics("true")
}
if (a > b)
{
Diagnostics("true")
}
else
{
Diagnostics("false")
}
if (a > b)
{
Diagnostics("true")
}
else if (a < b)
{
Diagnostics("false")
}
else
{
Diagnostics("equals")
}
switch (a) // выполнится только один блок кода по порядку при совпадении искомого значения
{
case 0, 1: // при *а* == 0 или 1. Остальные блоки не выполнятся.
{
Diagnostics("true")
}
case 2: // при *а* == 2
{
Diagnostics("false")
}
default: // при любом другом значении. Остальные блоки не выполнятся.
{
Diagnostics("null")
}
}
new var = (a > b) ? a : b // если (a > b) == true, то вернёт *а*, инече *b*
Операторы цикла
for (new i = 0; i < 10; i++)
{
Diagnostics("%d", i)
}
new i = 0
for (; i < 10; i++)
{
Diagnostics("%d", i)
}
new j = 0
while (j < 10)
{
Diagnostics("%d", j++)
}
while(true) // вечный цикл
{
// code
}
while (1) // вечный цикл
{
// code
}
Функции
Переменные передаются в функции по значению, а массивы по ссылке. Можно передать переменную по ссылке через символ "&". Порядок объявления функции не важен. Функция может как возвращать значение (для этого используется оператор return <значение>), так и не возвращать.
//Пользовательская функция
myFunc(&a, b) //Аргумент *a* будет передан по ссылке, аргумент *b* будет передан по значению
{
a += 2
return a + b //Возвращаемое значение
}
myArrayFunc(buf{}, size) // Передача массивов в функцию всегда происходит по ссылке.
{
for(new i = 0; i < size; i++)
{
Diagnostics("Buf[%d] = %d", i, buf{i})
}
}
Структура
// объявление констант
#define CONST_5 5
const CONST_0 = 0
const CONST_1 = 1
// объявление голбальных переменных
new var1 = 42
new var2 = 0
/* Любая программа должна содержать "входную" функцию main,
которая будет вызвана при запуске скрипта из алгоритмов.
После выхода из функции main скрипт завершается и все переменные уничтожаются,
поэтому при необходимости сохранения данных между итерациями вызова скрипта
нужно использовать глобальные переменные графических алгоритмов.
*/
main()
{
// code
}
// объявление пользовательских функций
myFunc0()
{
// code
}
myFunc1(a)
{
// code
}