«Программы должны писаться для того, чтобы их читали люди, и лишь во вторую очередь для выполнения машиной.» — Х. Абельсон, Дж. Сассман («Структура и интерпретация компьютерных программ»)
Скриптовый язык 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) (^)
// Определяем 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; // все флаги/параметры отключены до старта
Чтобы узнать битовое состояние, используется побитовое И:
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
}
- до 80% стоимости ПО приходится на поддержку
- обычно поддержкой занимается не автор
- соглашения улучшают читабельность кода, позволяя разработчикам и другим инженерам быстрее вникать в (чужой) код
Используйте 4 пробела на один уровень отступа.
Ограничьте максимальную длину строки 79 символами.
Перенос строчек (общие принципы):
- перенос после запятой
- перед операторами
- лучше использовать переносы верхнего уровня
- необходимо выравнивать новые строки по выражению на предыдущей строке
- если правила ведут к плохой читабельности, используется 8 пробелов
- Каждая строка должна содержать только одно выражение
function(longExpression1, longExpression2, longExpression3,
longExpression4, longExpression5);
var = function(longExpression1,
func2(longExpression2,
longExpression3));
alpha = (aLongBooleanExpression)
? beta
: gamma;
argv++; // Correct
argc--; // Correct
argv++; argc--; // AVOID!
- Перед комментарием ДОЛЖНА быть добавлена пустая строка
- Перед объявлением функции ДОЛЖНА быть добавлена пустая строка
- Группы функций отделять ДВУМЯ пустыми строками
- Группы переменных и констант отделять пустой строкой
Кодировка должна быть UTF-8
- «Внутренние» выражения сдвигаются на 1 уровень вправо
- Открывающая "{" ставится на новой строке кода
- Закрывающая "}" ставится на отдельной строке, выравнивается по выражению, открывающему блок
- Скобки используются ВСЕГДА вокруг выражений, являющихся частью управляющих выражений (даже однострочных)
- Всегда используйте скобки в длинных выражениях, это позволяет избежать проблем с приоритетом операций. Если приоритет понятен Вам, совершенно не обязательно, что он так же воспринимается другими.
if (condifion)
{
Diagnostics(“condition is true!”);
}
if ( (a == b) && (c == d) )
{
statements;
}
else if (condition)
{
statements;
}
else
{
statements;
}
- Ключевые слова с последующей скобкой должны быть разделены пробелом
- Пробел НЕ используется между названием функции и скобкой (параметры). Это помогает различать методы и управляющие конструкции.
- Пробел обязателен после запятой в списке параметров
- Все бинарные операторы (за исключением точки) должны отделяться пробелом от обоих операндов
- Выражения, задающие цикл for, отделяются пробелами
- Имена должны быть короткими, но «смысловыми», должны прояснять смысл их использования.
- Имена переменных, определенных как константы записываются в верхнем регистре со словами, разделенными подчеркиванием (“_”)
const ASCII_ZERO = 0x30;
const ASCII_ONE = 0x31;
const ASCII_TWO = 0x32;
const ASCII_THREE = 0x33;
const ASCII_FOUR = 0x34;
const ASCII_FIVE = 0x35;
const ASCII_SIX = 0x36;
const ASCII_SEVEN = 0x37;
const ASCII_EIGHT = 0x38;
const ASCII_NINE = 0x39;
const ASCII_DOT = 0x2E;
- Числовые константы не должны использоваться в коде напрямую, за исключением значений -1, 0, и 1, которые часто появляются в циклах
- Односимвольные имена должны избегаться за исключением временных переменных. Стандартные имена для них:
- i, j, k, m and n for integers
- c, d and e for characters