Описание языка

Стандартная библиотека

Ссылки

Скриптовый язык программирования Gentee

Введение

Gentee является строго-типизированным процедурным языком. В первую очередь, он предназначен для написания скриптов с целью автоматизации повторяющихся действий и процессов на компьютере. Язык имеет простой синтаксис, лёгок в изучении и сопровождении.

GitHub репозитарий исходников и запускаемых файлов: https://github.com/gentee/gentee
GitHub репозитарий документации: https://github.com/gentee/gentee.github.io
Язык разработки: Go

Лексические элементы

Исходный код должен быть в кодировке UTF-8. Синтаксис описан с использованием расширенной формы Бэкуса-Наура.

newline        = 0x0A
unicode_char = /* Unicode code point */
unicode_linechar  = /* Unicode code point except newline */ 
unicode_letter = /* a Unicode code point classified as "Letter" */
letter        = unicode_letter | "_"
decimal_digit = "0" … "9" 
octal_digit   = "0" … "7" 
hex_digit     = "0" … "9" | "A" … "F" | "a" … "f" 
decimals  = decimal_digit { decimal_digit }
exponent  = ( "e" | "E" ) [ "+" | "-" ] decimals

Комментарии и замена символов

Имеются следующие типы комментариев и автоматически заменяемых символов

// Однострочный комментарий
Однострочный комментарий начинается с двойного слэша // и заканчивается символом перевода строки newline.

/* Общий комментарий */
Общий комментарий начинается с комбинации /* и заканчивается */. Такие комментарии могут вставляться где угодно.

# Заголовок
В начале скрипта можно указать данные для использования в других программах. Такие комментарии должны идти подряд в каждой строке от начала скрипта. Можно не указывать ‘#’ в начале каждой строки, а вставить ### перед и после текста.

#!/usr/local/bin/gentee
# первая строка может использоваться для запуска скрипта в Linux.
###
  desc = Description of the script
  result = ok
  var = value
###

;
Символ перевода строки служит разделителем между выражениями и управляющими конструкциями. Точка с запятой заменяется на перевод строки. Таким образом, вы можете использовать точку с запятой, если вы хотите разместить несколько выражений на одной строке.

:
Двоеточие заменяется на открывающую фигурную скобку и вставляется закрывающая фигурная скобка в конце текущей строки.

// эти примеры эквивалентны
if a == 10 : a = b + c; c = d + e 

if a == 10 
{
   a = b + c
   c = d + e
}

Идентификаторы

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

identifier = letter { letter | unicode_digit }
IdentifierList = identifier { identifier }

Имеется несколько предопределённых идентификаторов и ключевых слов. Следующие слова зарезервированы и не могут быть использованы в качестве идентификаторов.

Ключевые слова

const elif else false for func if in return run struct true while

Предопределенные типы стандартной библиотеки

arr bool buf char error finfo float int map range str time trace

Литералы

Целочисленный литерал - это последовательность цифр представляющая целочисленное число (константу).

decimal = ( "1" … "9" ) { decimal_digit } 
octal = "0" { octal_digit } .
hex = "0" ( "x" | "X" ) hex_digit { hex_digit } 
integer = decimal | octal | hex
float = decimals "." [ decimals ] [ exponent ] | decimals exponent
0x34Fab
0722
19023862
0.123e+3
234.e-2
9.7732E-1
0.0177E+2
5e-2

Символьный char литерал служит для идентификации Unicode символа. Вы можете указать один конкретный символ или последовательность символов начинающуюся с обратного слэша заключенные в одинарные кавычки. Последовательность символов с обратным слэшем может иметь несколько форматов:

'\r',  '\n',  '\t', '\"', '\'', '\\' 
\xa5 \x2B  (\x + two hex_digit)
\u03B1  (\u + four hex_digit)
\0371  (\0 + three octal_digit)
byteVal  = octalStr | hexStr .
octalStr = `\` "0" octal_digit octal_digit octal_digit .
hexStr   = `\` "x" hex_digit hex_digit .
uShort   = `\` "u" hex_digit hex_digit hex_digit hex_digit .
uLong    = `\` "U" hex_digit hex_digit hex_digit hex_digit hex_digit hex_digit hex_digit hex_digit .
escapedChar     = `\` ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | `\` | `"` ) 
charLit         = "'" ( unicode_char | uShort | uLong | escapedChar | byteVal | `\'`) "'" .

Имеется два типа строковых литералов.

  1. Строка в обратных кавычках может содержать любые символы. Если нужно указать обратную кавычку, то нужно удвоить её.
  2. Строка в двойных кавычках также может содержать любые символы (в том числе перенос строки), но у неё имеется управляющий символ в виде обратной косой черты. Вы можете указывать после обратной косой черты следующие символы
\a   U+0007 alert or bell  
\b   U+0008 backspace  
\f   U+000C form feed  
\n   U+000A newline  
\r   U+000D carriage return  
\t   U+0009 horizontal tab  
\v   U+000b vertical tab  
\\   U+005c backslash  
\"   U+0022 double quote  
stringLit         = stringBackQuote | stringDoubleQuote
stringBackQuote   = "`" { unicode_char | "%{" Expression "}" | "${" identifier "}" } "`"
stringDoubleQuote = `"` { unicode_char | uShort | uLong | escapedChar | byteVal | "\{" Expression "}" } `"`

В любой тип строки можно вставлять выражения. При этом тип выражения может быть любым, если имеется соответствующая функция приведения этого типа к строке. Выражения должны быть заключены в фигурные скобки со предшествующим знаком % (для обратных кавычек) или обратной косой чертой (в случае двойных кавычек).

`10+20 equals %{10 + 20}. User name is "%{USERNAME}"`
"This is the first line.\r\nThis is \{ `the` + `second`} line."

Типы

Тип описывает множество значений, которые имеют одинаковые операции и функции специально для этих значений. Тип определяется именем типа. Ассоциативный массив map - это группа элементов одного типа к которой можно обращаться по строковому индексу. Каждый элемент имеет соответствующий уникальный строковый ключ. По умолчанию, массивы arr и map состоят из строк, но вы можете указать любую вложенность типов, разделив их точкой. Следует заметить, что переменные типов arr, map, buf и типов определенных с помощью struct, в отличии от прочих типов, передаются по ссылке, а не по значению. Это значит, что если внутри функции вы изменили значение такого параметра, то у вас изменится оригинальная переменная.

TypeName  = identifier  { "." identifier }

Язык Gentee содержит следующие предопределенные типы.

Имя Описание Значения Начальное значение
int 64-bit целочисленный тип -9223372036854775808 .. 9223372036854775807 0
float 64-bit тип с плавающей точкой 2.22 E–308 .. 1.79 E+308 0.0
bool логический тип true or false false
str строка последовательность байт пустая строка
char Unicode символ Unicode символ int32 пробел
arr массив массив элементов пустой массив строк
map ассоциативный массив ассоциативный массив элементов пустой ассоциативный массив строк
buf массив байт последовательность uint8 пустой массив
arr.map.int a
map.arr.str b   // the same as map.arr b
map.bool c
arr.int  d

Приведение типов

В языке Gentee отсутствует автоматическое приведение типов. Для основных типов имеются функции конвертирования из одного типа в другой, их имена совпадают с именем результирующего типа.

  int bool str char float
int   int(false) int(“-23”) int(‘A’) int(3.24)
bool bool(1)   bool(“0”)   bool(1.1)
str str(20) str(false)   str(‘z’) str(5.662)
char          
float float(10)   float(“-2E-34”)    
int(false) // = 0           
int(true) // = 1    
bool(0) // = false  
bool(0.) // = false
bool(integer except zero) // = true    
bool("")  bool("0") bool("false") //=false
bool("not empty, zero or false string")   //=true

Определение типа

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

structDecl = "struct" identifier "{" FieldDecl { newline  FieldDecl } "}"
FieldDecl = TypeName identifier
FieldExpr = PrimaryExpr "." identifier
struct my : int ID; str name
struct myStruct {
      int ID
      my myval
      arr st_arr
      map.int st_map
}
run int {
    myStruct ms
    ms.ID = 20
    return ms.ID * 2
}

Описания

Описание констант

Имя константы не должно содержать букв в нижнем регистре. Константам можно присваивать любые выражения. Значение константы вычисляется при первом обращении к данной константе, но тип константы автоматически определяется на этапе компиляции по типу присваиваемого выражения. Поэтому, несмотря на то, что тип при определении константы не указывается, действует проверка типов при её использовании.

ConstDecl      = "const" ( ConstIota | ConstExp )
ConstIota = Expression "{" { IdentifierList newline } "}"
ConstExp = "{" { identifier "=" Expression newline } "}"

Константы можно определить двумя способами.

  1. Указывая начальное значение или выражение для каждой константы.
    const {
     MY_ID = 1
     MY_VAL = myFunc( MY_ID + 23)
     CHECK= MY_VAL < 32
    }
    
  2. Используя общее выражение с IOTA. Иногда возникает необходимость определить список констант со значениями, которые вычисляются по определенным правилам. В этом случае, после ключевого слова const необходимо указать одно общее выражение, которая будет вычисляться для каждой константы в данном определении. В этом выражении можно использовать специальную переменную IOTA, которая равна порядковому индексу константы в списке с нуля. Сами константы могут перечисляться через пробел или с новой строки.
    const 0x1 << IOTA {
       FIRST SECOND   // 0x1    0x2
       THIRD                   // 0x4
    }
    const (IOTA * 2) + 1 {
       MY1    // 1
       MY2   // 3
       MY3   // 5
    }
    

Описание функции

Определение функции состоит из двух частей - описание параметров с возвращаемым типом и тела функции. При определении функции вы должны указать ключевое слово “func”, имя функции, передаваемые параметры и тип возвращаемого значения. Только имя функции является обязательным элементом.

FunctionDecl   = "func" FunctionName [Parameters] [ Result ] Block
FunctionName   = identifier 
Result         = TypeName 
Parameters     = "(" [ ParameterList ] ["..."] ")"
ParameterList  = VarList { "," VarList }
func VariadicExample(int i, int s...) int {
    int sum = i*2
    for v in s {
       sum += v
    }
    return sum
}
func MyFunc(int par1 par2) int { 
    int par3 = VariadicExample(3, par1, par2, 4, 5, par1+par2)
    return (par1+par2 +par3)/3 
}

Заключительный параметр в описании функции может иметь суффикс ‘…‘. Функция с таким параметром называется вариативной и может принимать ноль и более аргументов для этого параметра. Вы получаете этот параметр как массив переданных аргументов. Например, int pars… означает, что pars в действительности является arr.int и вы можете получить i-й аргумент с помощью pars[i].

Описание функции запуска

Скрипт на языке Gentee должен содержать специальную функцию без параметров, которая определяется с помощью ключевого слова “run”. Выполнение скрипта начинается с вызова этой функции. Скрипт должен иметь только одно определение “run”.

RunDecl = "run" [FunctionName] [ Result ] Block
run int {
    int i ret
    while i < 10 {
       ret += myFunc(i++)
    }
    return ret
}

Конструкции языка

Блок это последовательность определений и конструкций внутри фигурных скобок. Блоки могут вкладываться друг в друга.

Block = "{" StatementList "}" .
StatementList = { Statement newline } .
Statement = ReturnStmt | IfStmt | Expression | WhileStmt | VarDeclaration | ForStmt | 
            BreakStmt | ContinueStmt | SwitchStmt

Определение переменной

Любая переменная функции должна быть описана перед её использованием. Определение переменной создает одну или несколько переменных и присваивает каждой начальные значения. Переменная может быть определена в любом блоке. Область видимости переменной распространяется на блок, в котором она определена и на все вложенные блоки. Нельзя создавать переменные с именем существующих функций и видимых переменных. Также, имя переменной должно содержать как минимум одну букву в нижнем регистре, так как имена в верхнем регистре используются для констант. Определение переменой начинается с указания её типа. Существует два типа инициализации - можно определить одну переменную с присваиванием ей значения или несколько переменных одного типа с инициализацией по умолчанию.

VarDeclaration = VarAssign | VarList
VarList = TypeName IdentifierList
VarAssign = TypeName identifier "=" | "&=" VarInit
int x y myVal
int z = myFunc(x) + y + 10
arr a &= b

Конструкция if

Конструкции if начинаются с “if”, они могут иметь один или несколько блоков “elif” и заканчиваться “else”. Команда последовательно вычисляет условие для каждой ветки и, если условие возвращает истину, то тогда происходит выполнение соответствующего блока. В этом случае, остальные ветки пропускаются и управление передается следующей команде. Если условия во всех ветках ложны, то выполняется блок “else”, если он существует.

IfStmt = "if" Expression Block [{ "elif" Expression Block }][ "else" Block ]
if a == 11 {
    b = 20
} else {
    c = a+b
}
if x > y && isOK { 
     x = 1 
} elif a > 1 {
   x++
} elif b < 10 {
    b = a
} else {x = 0}

Конструкция while

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

WhileStmt = "while" Expression Block 
a = 0
while a < 5 {
   с += a
   a++
}

Конструкция for

Конструкция for служит для перебора всех элементов указанного объекта. Объект должен иметь тип, который поддерживает обращение по индексу, например, arr, map, str, buf, range of integers. Для каждого из его элементов выполняется код, который определен внутри конструкции. Вы должны указать имя переменной, которой будут присваиваться элементы и, опционально, имя переменной, которая будет равна текущему индексу.
Если вы хотите перебрать целочисленные значения в указанном диапазоне, то используйте в качестве объекта запись from..to, где from и to значения типа int. Такой цикл будет перебирать все числа от from до to, включая крайние значения. Начальное значение может быть больше конечного значения, в этом случае, значение на каждом цикле будет уменьшаться.

ForStmt = "for" identifier [, identifier] "in" Expression Block
str dest
for ch, i in `strΔ` {
   dest += "\{i}\{ch}"
}
int sum
for i in 0..100 : sum += i

Конструкция switch

Конструкция “switch” позволяет производить разные действия в зависимости от значения выражения. После ключевого слова switch необходимо указать начальное выражение, значение которого будет сравниваться с различными вариантами в блоках case. Значение может иметь тип int, float, char, str. Затем вам нужно перечислить блоки case со всеми возможными значениями и соответствующим кодом в фигурных скобках, который необходимо будет выполнить в случае соответствия. Для одного case может быть указано несколько значений, разделённых запятыми. После выполнения case блока с найденными соответствием, программа заканчивает работу switch конструкции. Оставшиеся case блоки не проверяются.

Если вы хотите выполнить какие-то действия в случае, если подходящий вариант не найден, то укажите в конце блок default. Конструкция default может быть только одна и идти после всех case конструкций.

SwitchStmt = "switch" Expression newline CaseStmt { CaseStmt } [ "default" Block ]
CaseStmt = "case" Expression {, Expression } Block
int i = 67
int j
switch i+3 
case 20,10,5 {
  i +=10
}
case j,20+50,80 {
  i -=10
}
default: i *= 2

Конструкция return

Конструкция “return” прекращает выполнение текущей функции и может возвращать результирующее значение. Если у функции не указан результирующий тип, то конструкция “return” не должна возвращать значение. Вы можете использовать return в любом вложенном блоке.

ReturnStmt = "return" [ Expression ]
func mul2(int i) int { return i*2}

Конструкция break

Конструкция break используется для выхода из конструкции switch/case и циклов (for и while). break может быть внутри вложенных блоков. Если есть несколько вложенных циклов, то произойдёт выход из текущего цикла.

BreakStmt = "break"
while b > c {
   if !myfunc( b ) {
      break   
   }
   b++
}

Конструкция continue

Конструкция “continue” действует внутри циклов (for и while) и позволяет перейти к выражению изменения счетчика для циклов for или к выражению условию для while не выполняя до конца тело цикла. В случае вложенных циклов инструкция действует на текущий цикл .

ContinueStmt = "continue"
for i in 0..100 {
   if i > 10 && i < 20 {
      continue 
   }
   a += i // The given expression is not evaluated if i>10 and i<20
}

Выражения

Выражение возвращает значение путем применения операторов и функций к операндам. Операндом может быть литерал, идентификатор определяющий константу, переменную, функцию или выражение в скобках. Для вызова функции необходимо указать её имя и в круглых скобках перечислить параметры через запятую. Параметры тоже могут быть выражениями.

Operand     = Literal | OperandName | "(" Expression ")"
Literal     = BasicLiteral
constLit    = "true" | "false"
BasicLiteral    = float | integer | stringLit | constLit | charLit
OperandName = identifier | EnvVariable
PrimaryExpr = Operand |	FuncName Arguments | IfOp | IndexExp | FieldExpr
Arguments      = "(" [  ExpressionList [ "," ExpressionList ]  ] ")" 
ExpressionList = Expression { "," Expression } 
Expression = UnaryExpr | Expression binaryOp Expression | OperandName assignOp Expression
UnaryExpr  = PrimaryExpr | unaryOp UnaryExpr | incOp OperandName | OperandName incOp 
binaryOp  = "||" | "&&" | relOp | mathOp | assignOp | rangeOp
relOp     = "==" | "!=" | "<" | "<=" | ">" | ">=" 
mathOp     = "+" | "-" | "|" | "^" | "*" | "/" | "%" | "<<" | ">>" | "&" | 
unaryOp   = "-" | "!" | "^" | "*"
incOp = "++" | "--" 
rangeOp = ".."
assignOp = "=" | "+=" | "-=" | "|=" | "^=" | "*=" | "/=" | "%=" | "<<=" | ">>=" | "&=" 
IfOp = "?" "(" Expression "," Expression "," Expression ")"

При вычислении логических операторов “&&” (И) и “||” (ИЛИ), правый операнд вычисляется опционально. Например, в случаях false && myFunc() и true || myFunc() функция myFunc не будет вызываться.

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

int i j k
i = j = 5+(k=60/5)*2
return (k+j)*2 + i   // 111

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

Как правило все операторы выполняются слева направо, но имеется такое понятие как приоритет операторов. Если следующий оператор имеет более высокий приоритет, то в начале выполнится оператор с более высоким приоритетом. Например, умножение имеет более высокий приоритет и 4 + 5 * 2 равно 14, но если мы поставим круглые скобки то ( 4 + 5 ) * 2 равно 18.

Оператор Тип Ассоциативность
Высший приоритет    
(   )  [   ]   Слева направо
!   -   ^   *   ++   -- Унарный префикс Справа налево
++   -- Унарный постфикс Слева направо
/   %   * Бинарный Слева направо
+   - Бинарный Слева направо
<<   >> Бинарный Слева направо
& Бинарный Слева направо
^ Бинарный Слева направо
| Бинарный Слева направо
==   !=   <   <=   >   >= Бинарный Слева направо
|| Бинарный Слева направо
&& Бинарный Слева направо
=   +=   -=   *=   /=   %=   <<=   >>=   &=   ^=   |= Бинарный Справа налево
.. Бинарный Слева направо
Низший приоритет    

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

Условный оператор “?”

Условный оператор “?” аналогичен по своей работе конструкции “if”, но может использоваться внутри выражения. Он содержит три операнда-выражения. Операнды заключены в скобки и разделены запятыми, в начале вычисляется значение первого логического (целочисленного) выражения. Если значение истинно, то вычисляется второе выражение и полученное значение становиться результатом работы условного оператора. В противном случае вычисляется третий операнд и возвращается его значение.

if a >= ?( x, 0xFFF, ?( y < 5 && y > 2, y, 2*b )) + 2345
{
     r = ?( a == 10, a, a + b ) 
}

Инициализация массивов и структур

При определении переменных с типом arr, buf или map можно сразу присвоить элементы массива. Также можно указывать значения полей переменных структурных типов. Значения перечисляются через запятую или перенос строки. В качестве значения можно указывать выражения. При инициализации ассоциативного массива map необходимо указать ключ в виде строки и через двоеточие значение. Если элементами массива являются другие массивы, то они тоже инициализируются с помощью фигурных скобок. При инициализации полей структур необходимо указать ключ в виде идентификатора и через двоеточие значение. Переменная типа buf может инициализироваться комбинацией значений типов int, str, char, buf.

DelimInit = "," | newline
ArrInit = "{" VarInit {DelimInit VarInit} "}"
BufInit = "{" Expression {DelimInit Expression} "}"
MapInit = "{" Expression ":" VarInit { DelimInit Expression ":" VarInit  }  "}"
StructInit = "{" identifier ":" VarInit { DelimInit identifier ":" VarInit  }  "}"
VarInit = ArrInit | BufInit| MapInit | StructInit | Expression
mystruct my = {ID: 20, name: "some text"}
buf a = {250+5, '1', 'A', "test", 0}
map.arr.int ret = {"key1": {0, 1 }, `key2`:{ 2, 3 } }
arr.map ret = { {"test": "value 1"}
                {`next`:"value 2"} }
arr.bool mb = : true, false, true
map  my = {"key1": GetVal(1), "key2": GetVal(2)}

Индексное выражение

Индексы позволяют вам получать или устанавливать определенный элемент переменной по его индексу. Индекс - это позиция определенного элемента внутри указанной переменной. Индексы в Gentee начинаются с нуля (для map индексы имеют строковый тип), первый элемент имеет индекс 0, второй индекс один и т.д. Следующие типы поддерживают индексы:

  • str. Индекс должен иметь тип int и быть меньше длины строки. Если индекс выходит за этот диапазон, то возникает ошибка выполнения. Результат имеет тип char.
  • buf. Индекс должен иметь тип int и быть меньше длины массива. Если индекс выходит за этот диапазон, то возникает ошибка выполнения. Результат имеет тип int, но 0 <= значение <= 255.
  • arr. Индекс должен иметь тип int и быть меньше длины массива. Если индекс выходит за этот диапазон, то возникает ошибка выполнения. Результат имеет такой же тип, как тип элементов массива.
  • map. Индекс должен иметь тип str. В случае получения значения, элемент с таким индексом должен существовать в ассоциативном массиве. Если такой ключ отсутствует, то возникает ошибка выполнения. Результат имеет такой же тип, как тип элементов ассоциативного массива.

Если массив array или map состоит из массивов, то вы можете последовательно применить индексные выражения.

IndexExp = PrimaryExpr "[" Expression "]" { "[" Expression "]" } 
str temp = `0123`
temp[1] = temp[3]  // result `0323`
arr ain
ain += `test`
temp = ain[0]
map mymap
mymap["mykey"] = "myvalue"
arr.map amap
amap += mymap
amap[0]["mykey"] = "new value"

Выражения присваивания

Для всех типов в языке Gentee существует оператор присваивания =. При использовании присваивания для типов buf, arr, map и всех структурных типов вы будете получать копии данных. Рассмотрим пример

arr a1 = {`A`, `B`, `C`}
arr a2 = a1
a2 += `D`
a1[0] = `Z`
//  a1 = `Z`, `B`, `C`
//  a2 = `A`, `B`, `C`, `D`

При присваивании a2 = a1 мы получили копию массива a1. Действия над массивами никак не будут влиять друг на друга. Иногда создание копий больших объектов может замедлять выполнение программы. Например, если функция возвращает какую-то структуру, с которой нужно работать дальше, то нет смысла создавать её копию. Для разрешения таких ситуаций, для типов buf, arr, map и всех структурных типов имеется еще один оператор присваивания &=. Этот оператор не копирует данные в переменную, а создает клон этих данных. В этом случае, данные остаются в единственном экземпляре.

arr a1 = {`A`, `B`, `C`}
arr a2 &= a1
a2 += `D`
a1[0] = `Z`
//  a1 = `Z`, `B`, `C`, `D`
//  a2 = `Z`, `B`, `C`, `D`
time t
t &= Now()  // it is better than t = Now()

Запуск программ

В языке существует специальная команда $ для запуска приложений и команд операционной системы с указанными параметрами. Данная команда запускает весь следующий за ней текст до конца строки. Между символом $ и командной строкой должен присутствовать пробел. Если данная команда используется в выражении, то она перехватывает стандартный вывод и возвращает его в виде строки. В противном случае, стандартный вывод будет виден в консоли. Можно использовать подстановку выражений с помощью %{Expression} как в строке с обратными кавычками. Если какой-то параметр содержит пробел, то его нужно заключить в любые кавычки - “a b”, ‘c d’, `e f`. Если запускаемое приложение или команда завершилось с кодом ошибки, отличным от нуля, то скрипт также прекратит работу и возвратит ошибку.

Command = "$ " { unicode_linechar | "%{" Expression "}" | "${" identifier "}" }
run str {
   $ dir
   str name = $ echo "John Smith"
   return $ echo My name is %{name}
}

Переменные окружения

Язык Gentee позволяет вам легко получать и присваивать значения переменных окружения. Для этого укажите знак $ перед именем переменной. Кроме этого, вы можете подставлять переменные окружения с помощью конструкции ${ENV_NAME} в командах запуска $ и строках с обратными кавычками. Эта запись короче, чем %{ $ENV_NAME }. Переменные окружения всегда имеют строковый тип, но вы можете присваивать им значения типа str, int и bool.

EnvVariable = "$" identifier
run str {
    $MYVAR = `Go path: ${GOPATH}` + $GOROOT
    return $ echo ${MYVAR}
}

Include и import

Включение файлов

Описание include импортирует все типы, функции и константы из указанных файлов и их дочерних файлов включенных с помощью include.

Описание import импортирует только публичные типы, функции и константы из указанных файлов и их дочерних файлов включенных с помощью include. Публичные объекты определяются с помощью ключевого слова pub.

stringConst         = "`" { unicode_char } "`" | stringDoubleConst
stringDoubleConst = `"` { unicode_char | uShort | uLong | escapedChar | byteVal } `"`
importDecl = "import" "{" {stringConst newline} "}"
includeDecl = "include" "{" {stringConst newline} "}"

Рассмотрим видимость объектов в виде таблицы. Пусть имеется два файла.

// a.g can include or import b.g
func afunc(i int) : return i*2
pub func apubfunc(i int) : return i*3

// b.g 
func bfunc(i int) : return i*4
pub func bpubfunc(i int) : return i*5

Пусть файл c.g может импортировать или включать файл a.g. Вы можете видеть какие функции будут видимы в файле c.g в зависимости от различных ситуаций.

  include a
a includes b
include a
a imports b
import a
a includes b
import a
a imports b
afunc visible visible    
apubfunc visible visible visible visible
bfunc visible      
bpubfunc visible   visible  

Описание pub

Команда pub определяет следующую функцию, тип или константы как публичные. Вы можете импортировать их с помощью команды import.

pubDecl = "pub"

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

pub const IOTA {  // public constants. Visible when include or import
    MY1
    MY2
}

const IOTA*2 {  // private constants. Visible only when include
    MY3
    MY4
}