Язык предназначен для встраивания его как языка сценариев в программы на языке c#. Поставляется в форме отдельного компилятора plpc или в форме набора подключаемых библиотек.
Основной идеей языка является простота синтаксиса и выразительность синтаксиса в сочетании со скоростью и безопасностью.
Программирование на языке выполняется в процедурной парадигме. Это значит, что код разбит на набор процедур, которые вызывают друг друга. Язык средствами синтаксиса явно отделяет поведение от данных.
- Сборка
- Запуск
- Базовые типы данных
- Арифметика
- Переменные
- Массивы
- Управляющие конструкции
- Процедуры
- Пользовательские типы
- Что дальше
Для сборки требуется .net8 на устройстве, которое выполняет сборку проекта. Другие внешние зависимости отсутствуют.
Сборка происходит через вызов одной команды (пример для bash)
cd plamp.Cli && dotnet publish -c Release -r linux-x64 --self-contained true -p:PublishReadyToRun=false -p:Pu
blishSingleFile=trueКомпилятор запускается как консольная утилита
plamp.Cli <имя кодового файла>
Создание бинарных файлов пока не реализовано. Компилятор выплёвывает код в динамическую сборку внутри себя.
Все данные в языке выражаются комбинацией следующих базовых типов данных.
int- 4 байтовое целое число со знаком-2.1B - ~2.1Bпостфикс после числаiuint- 4 байтовое целое число без знака0 - ~4.3Bпостфикс после числаuilong- 8 байтовое целое число со знаком-9.2e18 - ~9.2e18постфикс после числаlulong- 8 байтовое целое число без знака0 - ~1.8e19постфикс после числаulshort- 2 байтовое целое число со знаком-32,768 - 32,767постфикс после числаsushort- 2 байтовое целое число без знака0 - 65,535постфикс после числаusbyte- 1 байтовое целое число без знака0 - 255постфикс после числаbsbyte- 1 байтовое целое число со знаком-128 - 127постфикс после числаsb
float- 4 байтовое дробное число со знаком±1.5e-45 - ±3.4e38точность 6-9 цифр после запятой постфикс после числаfdouble- 8 байтовое дробное число со знаком±5.0e-324 - ±1.7e308точность 15-17 цифр после запятой постфикс после числаd
Постфиксы после чисел означают указание типов этих чисел. Например, 14f запишет число типа float. Число без точки и постфикса воспринимается как int, а число с точкой и без постфикса - double
bool- логический тип данныхtrueилиfalse
-
string- неизменяемый строковый тип данных. Записывается в двойных кавычках."Привет"- валидная строка. Внутри строк есть спец. символы, они начинаются с обратного слэша. -
\"- Кавычка -
\\- Обратный слэш -
\n- Перенос строки -
\t- Символ tab
char- тип данных, хранящий единственный символ. Объявляется как единственный символ в одиночных кавычках (пока не реализовано)
any- что угодно(любой тип)array- какой угодно массив
В языке поддерживаются все базовые арифметические операции.
+- сложение-- вычитание*- умножение/- деление%- остаток от деления нацело--- декремент(префиксный и постфиксный)++- инкремент(префиксный и постфиксный)
Также поддерживаются базовые логические операции
!- Логическое НЕ||- Логическое ИЛИ&&- Логическое и
Также есть арифметико - логические операции
>- Больше<- Меньше=- Равно!=- Не равно>=- Больше или равно<=- Меньше или равно
В приоритете сначала арифметические операции, потом арифметико-логические, потом логические. Также приоритет можно изменять с помощью заключения части выражения в круглые скобки
(2 + 2) * 2 = 8 - выдаст true
Попробуй разделить на 0
Объявить переменную в языке можно 2мя способами.
Первый - записать переменную, а за ней через двоеточие тип.
a: int;
Второй - записать переменную и присвоить ей сразу значение. Через оператор :=
a := 31;
Если переменная была объявлена выше в коде, то при повторном присваивании новая переменная объявлена не будет.
Переменная не может менять тип. Это значит, что если объявлена переменная одного типа, то в неё нельзя положить значение другого.
Этот код не сработает.
a: string;
a := 31;
Значение переменных можно передавать друг в друга.
a := 1;
b := a;
b будет равен 1
Также есть компактные формы записи для объявления и присваивания множества переменных.
a, b, c: float; - создаст 3 переменные типа float
d, e, f, g := "Привет", "дорогой", "друг", true; - создаст и запишет 3 строки и одну логическую переменную.
Также есть интересная хитрость.
a, b := b, a; - поменяет значения местами
Для группировки нескольких однотипных значений существуют массивы.
Записать массив можно как
a: []int; - Пустой массив целых чисел
a := [10]int; - Массив целых числе длинной 10
Индексация массива происходит с нуля.
Для записи элемента массива к нему можно обратиться через квадратные скобки
a[9] := 4; - запишет в 10 элемент массива.
Синтаксис для получения похожий
b := a[3]; - получит 4 элемент из массива.
Если выйти за границы массива, то программа завершится с ошибкой.
Массив может содержать в себе массивы, тогда это записывается как
[][]int - массив массивов интов(кстати это пока сломается)
Есть 2 способа управлять потоком выполнения кода.
Синтаксис записи условий следующий
if(<условие>) {действия если true} [опционально else { действия если false }]
Пример простейшего условия
if(a > 5) {
b := a;
}
Пример условия с блоком else
if(a = 3){
b := -1;
} else {
b := 22;
}
Приятная полезность. Условия можно комбинировать.
if(a = 2){
Делай первое
} else if(a < 2){
Делай второе
} else {
Иначе делай 3-e
}
В язке реализован 1 тип цикла - while (это не конечный вариант всё может поменяться)
Синтаксис цикла
while(<условие>) { действия цикла }
Пример цикла
i := 0;
while(i < 10){
i++;
}
Совершит 10 повторений
Для циклов есть специальные операторы, которые могут быть использованы только внутри них. break и continue
Первый прерывает выполение и выходит из цикла.
Второй пропускает итерацию и идёт на следующее повторение цикла.
Пример.
i := 0;
while(i++ < 100){
if(i < 50){
continue;
}
print("exit");
break;
}
Данный цикл прокрутит 50 итераций, потом напечатает exit и завершится, хотя в условии написана цифра 100.
Процедура объявляется следующим образом:
fn <Имя процедуры>([Список аргументов]) <Возвращаемый тип> {
тело процедуры
}
Возврат из процедуры происходит по средством вызова return с возвращаемым значением. Каждое выражение в теле процедуры отделяется символом ;
Пример корректной процедуры:
fn Echo(a: string) string {
return a;
}
Процедура может не возрващать ничего, тогда тип возвращаемого значения писать не надо.
fn Say(a: string) {
println(a);
}
Если процедура не возвращает ничего, то можно написать return без значения и когда код выполнится до этой инструкции произойдёт выход из процедуры
fn Interrupt(){
println("Это я напишу");
return;
println("A это нет");
}
Чтобы начать выполнение кода в файле должна быть обязательно процедура main и указание имени модуля.
Пример процедуры main:
module hello;
fn main() { }
Процедура может вызывать другую процедуру Делается это с помощью указания имени процедуры и списка значений её аргументов в скобках следом за ней.
Пример программы, которая здоровается.
module hello;
fn say_hello(name: string) {
print("Hello, ");
println(name);
}
fn main() {
say_hello("Vasya");
say_hello("Petya");
}
Аргументы одного типа можно группировать.
fn Add(first, second: int) int {
return first + second;
}
А можно не группировать
fn Add(first: int, second: int) int {
return first + second;
}
Поддерживается рекурсия
fn fib(n: int) int {
if(n <= 1) return 0;
if(n = 2 || n = 3) return 1;
else return fib(n - 1) + fib(n - 2);
}
Данный код выполнит вычисление n-го числа Фибоначчи
Для удобной передачи типов из функции и в функцию добавлены пользовательские типы. Тип группирует в себе несколько полей.
Тип в программе объявляется следующим образом.
type <Имя типа>{
поля типа
}
Пример объявления типа.
type Point {
X: int;
Y: int;
}
Также поля можно группировать
type Point{
X, Y: int;
}
Чтобы создать тип в коде следует записать его имя и фигурные скобки следом.
a := Point{};
Данный пример запишет в переменную пустой тип Point(пустой значит, что у всех полей будут значения по умолчанию).
Для обращения к полю типа следует указать его через точку после переменной этого типа.
a := Point{};
x := a.X;
a.Y := 3;
На примере чтение и запись в поля типа.
Все типы в языке ведут себя как структуры, это значит, что тип не может иметь в себе поле того же типа, что и он сам. А также как временное искусственное ограничение - тип не может иметь в себе поле с типом массива элементом которого является он сам.
Для всех типов в языке не существует понятия null. Строки и массивы имеют значение "" и ARRAY[0] по умолчанию.
Текущий набор функций крайне мал и служит лишь для целей отладки. Набор будет только расти.
pirnt(any)- выводит значение на экранprintln(any)- выводит значение на экран и переносит строкуlength(array) int- возвращает длину массиваlength(string) int- возвращает длину строкиread() char- читает один символreadln() string- читает одну строку до переносаconcat(string, string) string- соединяет строки воединоconcat([]string) string- соединяет массив строк воедино
Для ветки builtins был реализован демонстративный набор функций и типов. Функции и типы, которые будут перенесены в будущих версиях в отдельные модули
Типы
interval- Временной промежутокdate- ДатаAnyList- Список нетипизированных элементов
Функции для списков
length(AnyList) int- Длина спискаappend(AnyList, any)- Добавляет элемент в списокget(AnyList, int) any- Возвращает элемент списка по индексуremoveAt(AnyList, int)- Удаляет элемент списка по индексуreverse(AnyList)- Разворачивает списокconcat(AnyList, AnyList) AnyList- соединение двух списков
Функции для конверсии типов
toInt(any) int- пытается перевести что угодно в числоtoInt(string) int- парсит строку в числоtoUint(any) uint- пытается перевести что угодно в числоtoUint(string) uint- парсит строку в числоtoLong(any) long- пытается перевести что угодно в числоtoLong(string) long- парсит строку в числоtoUlong(any) ulong- пытается перевести что угодно в числоtoUlong(string) ulong- парсит строку в числоtoChar(any) char- пытается перевести что угодно в числоtoChar(string) char- парсит строку в числоtoShort(any) short- пытается перевести что угодно в числоtoShort(string) short- парсит строку в числоtoUshort(any) ushort- пытается перевести что угодно в числоtoUshort(string) ushort- парсит строку в числоtoFloat(any) float- пытается перевести что угодно в числоtoFloat(string) float- парсит строку в числоtoByte(any) byte- пытается перевести что угодно в числоtoByte(string) byte- парсит строку в числоtoSbyte(any) sbyte- пытается перевести что угодно в числоtoSbyte(string) sbyte- парсит строку в числоtoDouble(any) double- пытается перевести что угодно в числоtoDouble(string) double- парсит строку в число
Функции для работы с датами
fromMilliseconds(double) interval- Создаёт интервал из миллисекундfromSeconds(double) interval- Создаёт интервал из секундfromMinutes(double) interval- Создаёт интервал из минутfromHours(double) interval- Создаёт интервал из часовfromDays(double) interval- Создаёт интервал из днейtotalDays(interval) double- Сколько всего дней в интервалеtotalMilliseconds(interval) double- Сколько всего миллисекунд в интервалеtotalHours(interval) double- Сколько всего часов в интервалеtotalSeconds(interval) double- Сколько всего секунд в интервалеtotalMinutes(interval) double- Сколько всего минут в интервалеdays(interval) int- Число только дней в интервалеhours(interval) int- Число только часов в интервалеminutes(interval) int- Число только минут в интервалеseconds(interval) int- Число только секунд в интервалеmilliseconds(interval) int- Число только миллисекунд в интервалеadd(interval, interval) interval- Суммирует 2 временных промежуткаadd(date, interval) date- Добавляет интервал к датеsub(interval, interval) interval- Вычитает временной промежуток один из другогоsub(date, interval) date- Вычитает временной промежуток из датыsub(date, date) interval- Находит разницу между датамиtoDate(string) date- Парсит строку в датуsecond(date) int- Получает секунды из датыminute(date) int- Получает минуты из датыhour(date) int- Получает часы из датыday(date) int- Получает дни из датыmonth(date) int- Получает месяца из датыyear(date) int- Получает года из датыdayOfYear(date) int- Получает порядковый номер дня из годаdayOfWeek(date) int- Получает порядковый номер дня в неделе. Дни нумеруются с нуля и нумерация начинается с воскресенияtimeOfDay(date) interval- Получает времяnow() int- Получает текущую дату со временемutcNow() int- Получает текущую дату со временем по поясу GMT+0date(date) date- Получает дату без времени из даты со временем
Функции для конверсии в строку
toString(date, string) string- Превращает дату в строку с определённым форматированиемtoString(date) string- Превращает дату в строкуtoString(interval, string) string- Превращает интервал в строку с определённым форматированиемtoString(interval) string- Превращает интервал в строкуtoString(any) string- Превращает что угодно в строку
Функции для работы со строками
substring(string, int, int) string- Получает подстроку начиная с определённого индекса определённой длиныsubstring(string, int) string- Получает подстроку начиная с определённого до концаget(string, int) char- Получает символ строки по определённому индексуstrcmp(string, string) bool- Посимвольное сравнение двух строк.
Текущий набор возможностей кажется мне как разработчику недостаточным, поэтому уже сформирован бэклог дальнейших нововведений.
- Возврат нескольких значений из функции за раз
- Generic типы
- Union типы
- Делегаты и closure
- Механизмы паники(panic) и восстановления(recover)
- typeclasses (см. haskell)
- Система подключаемых библиотек
- Стандартная библиотека.