Сообщение Re: стоит создать ЯП? от 11.10.2023 1:47
Изменено 11.10.2023 2:03 Sm0ke
Re: стоит создать ЯП?
Здравствуйте, Shmj, Вы писали:
S>Вот если посмотреть популярные ЯП — то это дофига работы команд. Годы работы, годы доработок. Т.е. идея может быть вдохновлена одним человеком (на самом деле он эту идею где-то подсмотрел, как правило), а вот разработка языка — это уже только команда.
S>Получается хороший новый язык не может создать один человек и это всегда годы работы. Т.е. неожиданно новый ЯП возникнуть не может, в отличие от JS-фреймворка типа Vue.
S>Но бывают и исключения. Вот же наши как-то смогли создать тот же Nemerle вообще без особых затрат, силами энтузиастов. Как так?
В одиночку разработать свой ЯП я считаю возможным. Но трудности возникают когда приходится принять то или иное решение.
Это может быть и касательно синтаксиса, и касательно реализации, или установки неких ограничений.
Совместными усилиями, как мне кажется, результата можно добиться быстрее.
PHP изначально сделал Расмус. Классов там не было, их добавили позднее (в 5-ой версии), когда появилась команда разработчиков. Енумы там появились вообще относительно недавно (версия 8.1). Кстати имена переменных в PHP начинаются с символа $
Квадратные скобки там служат не только для обращения к элементу по индексу, но и для создания массива. Все массивы в PHP ассоциативны (как ключ допустимо и целое число и строка текста).
Я думаю многие задумывались поменять что-то в уже существующих ЯП, с которыми они работали, или что-то туда добавить. А не лучше ли вообще сделать своё? — Примерно такая идея у меня возникла ещё когда я учился в Колледже. Вам же это знакомо?
Я действительно хочу учавствовать в разработке нового ЯП!
Си++ хорош, но слишком переусложнён. Его стандартники делают с ним, то что хотят они — и это не всегда чего хотим мы.
Когда методы определяются внутри самого класса, то класс постепенно разрастается и превращается в монстра великана.
Мне кажется более наглядным, когда в программе данные определяются отдельно от действий над ними.
Я предлагаю создать свой язык вместе. У меня есть идеи, чтобы их обсудить.
Фичи языка:
*- Ячейка — это может быть: локальная переменная, параметр функции, свойство модуля, и т.д.
*- Возможность работать с типом, как со значением. Тип такой ячейки будет $type
*- Имена системных типов начинаются к примеру с символа $ (но это можно указать в конфиге, как и другие начальные символы).
Таким образом добавление системных типов не будет конфликтовать с уже написанным пользовательским кодом.
Тип может быть ключом ассоциативного массива, ведь его можно сравнивать с другим типом как просто значение.
*- Императивный кусок кода помещается в фигурные скобки и имеет тип $seq (sequence).
Значение типа $seq можно передавать в функцию, присваивать в ячейку, и даже использовать как ключ в ассоциативном массиве.
Это позволит определять языковые конструкции как обычные функции.
Сами по себе фигурные скобки — действий внутри не выполняют без определённой команды. Это называется вычисление по требованию.
* По сути sequence — это как лямбда без параметров.
Приблизительный пример:
Квадратные скобки — массив/мапа. Двоеточие — разделитель в паре ключ-значение.
hint: думаю нужно отделить типы $map от $array
Символ восклицательный знак после ячейки с функцией — применить её к последующему выражению (просто чтобы не писать круглые скобки if_first([])).
if_first — выполняет sequence каждого условия до тех пор, пока не встретит первое правдивое, и тогда выполнит sequence действие из значения пары.
if_each — будет проверять все условия, даже после того как встретит правдивое.
Такая штука ведь нагляднее, чем городить огород из if else if else ?
Функцию вывода на консоль или в файл можно сделать чтобы она принимала sequence. Так она итератором будет вычислять элементы $seq — выражения, и выводить их результат, пока не они не закончатся, или не возникнет ощибка вывода / исключение, при которой оставшиеся выражения не будут вычислены.
Собачка — начало имени модуля. Файл программы начинается с указания модуля. Далее следуют ячейки модуля — его свойства.
Это декларативная часть. Императивная часть находится внутри фигурных скобок.
При запуске программы выполнится sequence из ячеки do модуля @main
Запятая — разделитель опциональный.
Вертикальная палка перед ячейкой с функцией — применить функцию как метод к результату предшествующего выражения.
Так можно вызывать функции по цепочке слева-направо. Эта идея подсмотрена в PHP библиотеке шаблонов Smarty
Оператор "присвоить вправо" => в общем то под вопросом.
Определение структур как типов: команда #struct
Команда #fn определяет функцию
Ячейка результата вызова функции #ret
Если добавить в язык Возможность её-же принять как параметр функции, то:
Строчка: pos_2& |scale(0.5) передать яцейку pos_2 как link на ячейку (функция сможет её изменить)
Строчка: pos_1 |scale(2) запись в ячейку #ret в этом случае не изменит ячейку pos_1
scale можно переписать по другому:
Тут будут изменены свойства (x y) у координат в pos_1
Ведь структуры передаются по указателю, как и прочие значения системной категории _compound
Все категории типов (значение $cat) произрастают из базовой _any, включённой во все типы по-умолчанию.
Категория — это просто категория, она не хранит в себе данных. Каждый тип может принадлежать своему набору категорий.
В программе можно проверить их принадлежность. Это может быть полезно при динамической типизации.
При строгой типизации $cat можно использовать как ограничение (например ограничить тип параметра функции).
Категории можно использовать как обычные значения в программе, их сравнивать, использовать как ключ в $map.
Ведь тип $cat пренадлежит категории _map_key
Естественно пользователь сможет определять и свои категории типов.
( _null / _undefined ) — пример для пустых значений при динамической типизации, возможно это системные категории.
Атрибуты
Например у всех типов есть атрибут initial — это начальное значение ячейки, которое будет использовано при неуказании инициализации.
Доступ к атрибутам происходит через оператор стрелка ->
Что это? Да это же шаблон структуры. Параметр шаблона под именем value_type — тип.
А могло быть и число
#struct [$type: Type, $int: N] ()
А мог быть и тип с ограничением $type_refers[_number]
А могла быть и категория с ограничением $cat_includes[_plain]
Собственно нужны ли категории и шаблоны в языке — под вопросом.
Про типизацию и название языка
Мне нравится название ksi-lang (и строгая и динамическая типизация)
ksi-script (только динамическая типизация)
ksi-pl (только строгая типизация)
Я долгое время пилю интерпретатор для ksi-script
Дело в том, что я по разным причинам откладывал разработку и возвращался к ней снова.
В одиночку непросто это.
Назначение языка — для любых целей. Хотя кси скрипт скорее всего для web, и как скриптовый движок в game-dev
p/s
Вопрос: синглтон или набор статик свойств
Ответ: вид значений #nest (это команда определиния типа)
Попытка создания значения типа, который nest only, приводит к получению этого же типа.
К струткуре можно добавить статик свойства командой #add_nest
Перечисления — они для чёткого набора состояний ячейки.
У элемента перечисления я думаю можно добавить атрибут data, чтобы там что-то хранить.
Атрибут data для каждого элемента перечисления привязан к этому элементу. Сами элементы передаются по значению-номеру (их data не копируется при этом).
Можно сделать атрибуты ->name ->index ->next_wrap (обсуждаемо)
А сомому енуму атрибут align_h->elements
Вот такую примерно можно придумать интроспекцию.
В нюансах все прелести и сложности. Как делать захват локальных ячеек в sequence, который внутри sequence ? Всё ли захватывать, или указывать конкретные, или автоматом по необходимости (как в javascript).
Нужна ли языку константность (read_only ячейки)
Т.е при первом присвоении в имени ячейки окончание — спецсимвол из конфига.
В использовании имени в дальнейшем ридонли символ уже не указывается (чтобы не пришлось переписывать все места вручную, привет ide).
private public protected / friend — отдельная тема. Но я бы не тянул геттеры и сеттеры из си шарп
Наследование — сомнительно, но можно сделать команду #insert_struct
Это позволит при интроспекции обходить свойства структуры именно в заданной последовательности. (В отличии от наследования, где сперва идёт база)
Доступ к под-объекту — как вариант в индексе [] тип
Ещё есть идея
std::optional аналог
Оно хранит только заданные опции. Чтение из незаданных — вопрос. (или _null / _absent; либо ошибка/exception)
В отличии от $map — имена допустимых ключей явно заданы в определении.
Способ хранения данных — implementation defined
(наверно для $cat_exact можно по битам опции разложить, или придумать свой #bit_set / #flags)
Для системных функций я бы ещё сделал префикс &
&if_first &each &loop &write &switch &catch &throw
Так мы освобождаем имя модуля @std для прочих штук.
--
Для исключений (если да) — атрибуты:
e->source.line
e->source.column
e->source.file
Получится собрать команду разработчиков нового языка программирования в этом году?
Какие идеи вам приглянулись?
pp/s Хотел сперва вкратце написать про ksi, чтож
Даже идею про #actions (аналог интерфейсам не показал)
Ещё можно придумать команду #allow_actions_only, исключающую применение повторябельной #allow_actions
S>Вот если посмотреть популярные ЯП — то это дофига работы команд. Годы работы, годы доработок. Т.е. идея может быть вдохновлена одним человеком (на самом деле он эту идею где-то подсмотрел, как правило), а вот разработка языка — это уже только команда.
S>Получается хороший новый язык не может создать один человек и это всегда годы работы. Т.е. неожиданно новый ЯП возникнуть не может, в отличие от JS-фреймворка типа Vue.
S>Но бывают и исключения. Вот же наши как-то смогли создать тот же Nemerle вообще без особых затрат, силами энтузиастов. Как так?
В одиночку разработать свой ЯП я считаю возможным. Но трудности возникают когда приходится принять то или иное решение.
Это может быть и касательно синтаксиса, и касательно реализации, или установки неких ограничений.
Совместными усилиями, как мне кажется, результата можно добиться быстрее.
PHP изначально сделал Расмус. Классов там не было, их добавили позднее (в 5-ой версии), когда появилась команда разработчиков. Енумы там появились вообще относительно недавно (версия 8.1). Кстати имена переменных в PHP начинаются с символа $
Квадратные скобки там служат не только для обращения к элементу по индексу, но и для создания массива. Все массивы в PHP ассоциативны (как ключ допустимо и целое число и строка текста).
Я думаю многие задумывались поменять что-то в уже существующих ЯП, с которыми они работали, или что-то туда добавить. А не лучше ли вообще сделать своё? — Примерно такая идея у меня возникла ещё когда я учился в Колледже. Вам же это знакомо?
Я действительно хочу учавствовать в разработке нового ЯП!
Си++ хорош, но слишком переусложнён. Его стандартники делают с ним, то что хотят они — и это не всегда чего хотим мы.
Когда методы определяются внутри самого класса, то класс постепенно разрастается и превращается в монстра великана.
Мне кажется более наглядным, когда в программе данные определяются отдельно от действий над ними.
Я предлагаю создать свой язык вместе. У меня есть идеи, чтобы их обсудить.
Фичи языка:
*- Ячейка — это может быть: локальная переменная, параметр функции, свойство модуля, и т.д.
*- Возможность работать с типом, как со значением. Тип такой ячейки будет $type
*- Имена системных типов начинаются к примеру с символа $ (но это можно указать в конфиге, как и другие начальные символы).
Таким образом добавление системных типов не будет конфликтовать с уже написанным пользовательским кодом.
Тип может быть ключом ассоциативного массива, ведь его можно сравнивать с другим типом как просто значение.
t1 = $int
t2 = $float
t1 <> t2
*- Императивный кусок кода помещается в фигурные скобки и имеет тип $seq (sequence).
do = {
/* expression-list here */
}
Значение типа $seq можно передавать в функцию, присваивать в ячейку, и даже использовать как ключ в ассоциативном массиве.
Это позволит определять языковые конструкции как обычные функции.
Сами по себе фигурные скобки — действий внутри не выполняют без определённой команды. Это называется вычисление по требованию.
* По сути sequence — это как лямбда без параметров.
do = {
local_var = { call_some_fn() }
local_var()
call_other_fn(local_var)
}
Приблизительный пример:
if_each!
[
{ a == b } : { /* действие 1 */ }
{ /* условие 2 */ } : { /* действие 2 */ }
]
if_first!
[
{ a == b } : { /* действие 1 */ }
{ /* условие 2 */ } : { /* действие 2 */ }
$bool.true : { /* действие иначе */ }
]
Квадратные скобки — массив/мапа. Двоеточие — разделитель в паре ключ-значение.
hint: думаю нужно отделить типы $map от $array
Символ восклицательный знак после ячейки с функцией — применить её к последующему выражению (просто чтобы не писать круглые скобки if_first([])).
if_first — выполняет sequence каждого условия до тех пор, пока не встретит первое правдивое, и тогда выполнит sequence действие из значения пары.
if_each — будет проверять все условия, даже после того как встретит правдивое.
Такая штука ведь нагляднее, чем городить огород из if else if else ?
Функцию вывода на консоль или в файл можно сделать чтобы она принимала sequence. Так она итератором будет вычислять элементы $seq — выражения, и выводить их результат, пока не они не закончатся, или не возникнет ощибка вывода / исключение, при которой оставшиеся выражения не будут вычислены.
@main
do = {
/* c-style function call */
@std.write( {sinus(a), " ", cosine(b)} )
/* Smarty style function call */
{sin(a), " ", cos(b)} |write@std
}
Собачка — начало имени модуля. Файл программы начинается с указания модуля. Далее следуют ячейки модуля — его свойства.
Это декларативная часть. Императивная часть находится внутри фигурных скобок.
При запуске программы выполнится sequence из ячеки do модуля @main
Запятая — разделитель опциональный.
Вертикальная палка перед ячейкой с функцией — применить функцию как метод к результату предшествующего выражения.
Так можно вызывать функции по цепочке слева-направо. Эта идея подсмотрена в PHP библиотеке шаблонов Smarty
a |sinus |write@std(to: file)
/* аналог: */
write@std(sinus(a), to: file)
-- line-comment
a |sinus => x |write_line@std(to: file)
write_line@std( x = sinus(a), to: file )
Оператор "присвоить вправо" => в общем то под вопросом.
Определение структур как типов: команда #struct
Команда #fn определяет функцию
@lib
coords = #struct ( x y )
scale = #fn (pos, factor) {
#ret = coords(pos.x * factor, pos.y * factor)
}
do = {
pos = coords(10 20) |scale(5.5)
}
Ячейка результата вызова функции #ret
Если добавить в язык Возможность её-же принять как параметр функции, то:
scale = #fn (#ret, factor) {
#ret = coords(pos.x * factor, pos.y * factor)
}
do = {
pos_1 = coords(10, 20)
pos_2 = pos_1 |scale(2)
pos_2& |scale(0.5)
}
Строчка: pos_2& |scale(0.5) передать яцейку pos_2 как link на ячейку (функция сможет её изменить)
Строчка: pos_1 |scale(2) запись в ячейку #ret в этом случае не изменит ячейку pos_1
scale можно переписать по другому:
scale = #fn (#ret, factor) {
(#ret.x, #ret.y) *= factor
}
do = {
pos_1 = coords(10, 20)
pos_1 |scale(2)
}
Тут будут изменены свойства (x y) у координат в pos_1
Ведь структуры передаются по указателю, как и прочие значения системной категории _compound
Все категории типов (значение $cat) произрастают из базовой _any, включённой во все типы по-умолчанию.
Категория — это просто категория, она не хранит в себе данных. Каждый тип может принадлежать своему набору категорий.
В программе можно проверить их принадлежность. Это может быть полезно при динамической типизации.
При строгой типизации $cat можно использовать как ограничение (например ограничить тип параметра функции).
@sample
squared = #fn(_plain_number: n) { #ret = n*n }
do = {
i = 2 |squared
f = 1.5 |squared
}
@global
/* exposition only */
#cat (
_any /* base category */
_compound /* values of _compound types are passed by pointer */
_plain /* values of _plain types are passed by-value */
_number
_plain_number #includes( _plain _number )
)
_plain_number #refers_to ($int $float)
@lib
complex = #struct( re im )
complex #refers( _number )
Категории можно использовать как обычные значения в программе, их сравнивать, использовать как ключ в $map.
Ведь тип $cat пренадлежит категории _map_key
Естественно пользователь сможет определять и свои категории типов.
( _null / _undefined ) — пример для пустых значений при динамической типизации, возможно это системные категории.
Атрибуты
Например у всех типов есть атрибут initial — это начальное значение ячейки, которое будет использовано при неуказании инициализации.
Доступ к атрибутам происходит через оператор стрелка ->
range = #struct [$type: value_type] ( value_type: from, to )
range->initial(from: 0, to: value_type->max)
/* или */
range->initial.from = value_type->zero
range->initial.to = value_type->max
Что это? Да это же шаблон структуры. Параметр шаблона под именем value_type — тип.
А могло быть и число
#struct [$type: Type, $int: N] ()
А мог быть и тип с ограничением $type_refers[_number]
А могла быть и категория с ограничением $cat_includes[_plain]
Собственно нужны ли категории и шаблоны в языке — под вопросом.
Про типизацию и название языка
Мне нравится название ksi-lang (и строгая и динамическая типизация)
ksi-script (только динамическая типизация)
ksi-pl (только строгая типизация)
Я долгое время пилю интерпретатор для ksi-script
Дело в том, что я по разным причинам откладывал разработку и возвращался к ней снова.
В одиночку непросто это.
Назначение языка — для любых целей. Хотя кси скрипт скорее всего для web, и как скриптовый движок в game-dev
p/s
Вопрос: синглтон или набор статик свойств
Ответ: вид значений #nest (это команда определиния типа)
colors = #nest (
red = 0h_ff0000
black = 0
)
/* colors.red |write */
Попытка создания значения типа, который nest only, приводит к получению этого же типа.
К струткуре можно добавить статик свойства командой #add_nest
@sample
color = #struct ( r g b a )
color->initial.a = 255
color #add_nest (
from_code = #fn($int: code) { /* вычисления */ }
red_color = color.from_code(colors.red)
)
Перечисления — они для чёткого набора состояний ячейки.
У элемента перечисления я думаю можно добавить атрибут data, чтобы там что-то хранить.
Атрибут data для каждого элемента перечисления привязан к этому элементу. Сами элементы передаются по значению-номеру (их data не копируется при этом).
align_h = #enum (left center right)
align_h.left->data = /* да тут что угодно, хоть даже структура */
do = { align_h.center->name |write_line@std }
Можно сделать атрибуты ->name ->index ->next_wrap (обсуждаемо)
А сомому енуму атрибут align_h->elements
align_h->elements |each: #fn(it) { it->data |write }
Вот такую примерно можно придумать интроспекцию.
В нюансах все прелести и сложности. Как делать захват локальных ячеек в sequence, который внутри sequence ? Всё ли захватывать, или указывать конкретные, или автоматом по необходимости (как в javascript).
Нужна ли языку константность (read_only ячейки)
$int: (c# = 5, x = 4, y# = c + x)
c += 1 /* ошибка */
Т.е при первом присвоении в имени ячейки окончание — спецсимвол из конфига.
В использовании имени в дальнейшем ридонли символ уже не указывается (чтобы не пришлось переписывать все места вручную, привет ide).
private public protected / friend — отдельная тема. Но я бы не тянул геттеры и сеттеры из си шарп
Наследование — сомнительно, но можно сделать команду #insert_struct
coords = #struct( x y )
game_object = #struct (
name
#insert_struct( coords )
type
)
Это позволит при интроспекции обходить свойства структуры именно в заданной последовательности. (В отличии от наследования, где сперва идёт база)
Доступ к под-объекту — как вариант в индексе [] тип
rabbit = game_object( coords:(1 1), name: "Vitalik" )
pos = rabbit[coords]
Ещё есть идея
std::optional аналог
#cat ( _yes )
range = #options (
_any:
from
to
$cat_exact[_yes]:
exact_from
exact_to
)
r = range(to: 5, exact_to: _yes)
Оно хранит только заданные опции. Чтение из незаданных — вопрос. (или _null / _absent; либо ошибка/exception)
В отличии от $map — имена допустимых ключей явно заданы в определении.
Способ хранения данных — implementation defined
(наверно для $cat_exact можно по битам опции разложить, или придумать свой #bit_set / #flags)
Для системных функций я бы ещё сделал префикс &
&if_first &each &loop &write &switch &catch &throw
Так мы освобождаем имя модуля @std для прочих штук.
--
Для исключений (если да) — атрибуты:
e->source.line
e->source.column
e->source.file
Получится собрать команду разработчиков нового языка программирования в этом году?
Какие идеи вам приглянулись?
pp/s Хотел сперва вкратце написать про ksi, чтож
Даже идею про #actions (аналог интерфейсам не показал)
do = {
c = 0h_e3d7f5 !as[color]
}
color = #struct ($integer_unsigned[8]: r g b)
color #allow_actions( conversion_from[_integer] )
conversion_from = #actions [$hint: from_type] (
as = #fn_params [$type: to_type] (from_type: this) : to_type
)
conversion_from[$int].as[color] = #fn (_integer: this) : color {
[#ret.r #ret.g #ret.b] = (this !make_byte_array: [3 2 1])
}
Ещё можно придумать команду #allow_actions_only, исключающую применение повторябельной #allow_actions
Re: стоит создать ЯП?
Здравствуйте, Shmj, Вы писали:
S>Вот если посмотреть популярные ЯП — то это дофига работы команд. Годы работы, годы доработок. Т.е. идея может быть вдохновлена одним человеком (на самом деле он эту идею где-то подсмотрел, как правило), а вот разработка языка — это уже только команда.
S>Получается хороший новый язык не может создать один человек и это всегда годы работы. Т.е. неожиданно новый ЯП возникнуть не может, в отличие от JS-фреймворка типа Vue.
S>Но бывают и исключения. Вот же наши как-то смогли создать тот же Nemerle вообще без особых затрат, силами энтузиастов. Как так?
В одиночку разработать свой ЯП я считаю возможным. Но трудности возникают когда приходится принять то или иное решение.
Это может быть и касательно синтаксиса, и касательно реализации, или установки неких ограничений.
Совместными усилиями, как мне кажется, результата можно добиться быстрее.
PHP изначально сделал Расмус. Классов там не было, их добавили позднее (в 5-ой версии), когда появилась команда разработчиков. Енумы там появились вообще относительно недавно (версия 8.1). Кстати имена переменных в PHP начинаются с символа $
Квадратные скобки там служат не только для обращения к элементу по индексу, но и для создания массива. Все массивы в PHP ассоциативны (как ключ допустимо и целое число и строка текста).
Я думаю многие задумывались поменять что-то в уже существующих ЯП, с которыми они работали, или что-то туда добавить. А не лучше ли вообще сделать своё? — Примерно такая идея у меня возникла ещё когда я учился в Колледже. Вам же это знакомо?
Я действительно хочу учавствовать в разработке нового ЯП!
Си++ хорош, но слишком переусложнён. Его стандартники делают с ним, то что хотят они — и это не всегда чего хотим мы.
Когда методы определяются внутри самого класса, то класс постепенно разрастается и превращается в монстра великана.
Мне кажется более наглядным, когда в программе данные определяются отдельно от действий над ними.
Я предлагаю создать свой язык вместе. У меня есть идеи, чтобы их обсудить.
Фичи языка:
*- Ячейка — это может быть: локальная переменная, параметр функции, свойство модуля, и т.д.
*- Возможность работать с типом, как со значением. Тип такой ячейки будет $type
*- Имена системных типов начинаются к примеру с символа $ (но это можно указать в конфиге, как и другие начальные символы).
Таким образом добавление системных типов не будет конфликтовать с уже написанным пользовательским кодом.
Тип может быть ключом ассоциативного массива, ведь его можно сравнивать с другим типом как просто значение.
*- Императивный кусок кода помещается в фигурные скобки и имеет тип $seq (sequence).
Значение типа $seq можно передавать в функцию, присваивать в ячейку, и даже использовать как ключ в ассоциативном массиве.
Это позволит определять языковые конструкции как обычные функции.
Сами по себе фигурные скобки — действий внутри не выполняют без определённой команды. Это называется вычисление по требованию.
* По сути sequence — это как лямбда без параметров.
Приблизительный пример:
Квадратные скобки — массив/мапа. Двоеточие — разделитель в паре ключ-значение.
hint: думаю нужно отделить типы $map от $array
Символ восклицательный знак после ячейки с функцией — применить её к последующему выражению (просто чтобы не писать круглые скобки if_first([])).
if_first — выполняет sequence каждого условия до тех пор, пока не встретит первое правдивое, и тогда выполнит sequence действие из значения пары.
if_each — будет проверять все условия, даже после того как встретит правдивое.
Такая штука ведь нагляднее, чем городить огород из if else if else ?
Функцию вывода на консоль или в файл можно сделать чтобы она принимала sequence. Так она итератором будет вычислять элементы $seq — выражения, и выводить их результат, пока не они не закончатся, или не возникнет ощибка вывода / исключение, при которой оставшиеся выражения не будут вычислены.
Собачка — начало имени модуля. Файл программы начинается с указания модуля. Далее следуют ячейки модуля — его свойства.
Это декларативная часть. Императивная часть находится внутри фигурных скобок.
При запуске программы выполнится sequence из ячеки do модуля @main
Запятая — разделитель опциональный.
Вертикальная палка перед ячейкой с функцией — применить функцию как метод к результату предшествующего выражения.
Так можно вызывать функции по цепочке слева-направо. Эта идея подсмотрена в PHP библиотеке шаблонов Smarty
Оператор "присвоить вправо" => в общем то под вопросом.
Определение структур как типов: команда #struct
Команда #fn определяет функцию
Ячейка результата вызова функции #ret
Если добавить в язык Возможность её-же принять как параметр функции, то:
Строчка: pos_2& |scale(0.5) передать яцейку pos_2 как link на ячейку (функция сможет её изменить)
Строчка: pos_1 |scale(2) запись в ячейку #ret в этом случае не изменит ячейку pos_1
scale можно переписать по другому:
Тут будут изменены свойства (x y) у координат в pos_1
Ведь структуры передаются по указателю, как и прочие значения системной категории _compound
Все категории типов (значение $cat) произрастают из базовой _any, включённой во все типы по-умолчанию.
Категория — это просто категория, она не хранит в себе данных. Каждый тип может принадлежать своему набору категорий.
В программе можно проверить их принадлежность. Это может быть полезно при динамической типизации.
При строгой типизации $cat можно использовать как ограничение (например ограничить тип параметра функции).
Категории можно использовать как обычные значения в программе, их сравнивать, использовать как ключ в $map.
Ведь тип $cat пренадлежит категории _map_key
Естественно пользователь сможет определять и свои категории типов.
( _null / _undefined ) — пример для пустых значений при динамической типизации, возможно это системные категории.
Атрибуты
Например у всех типов есть атрибут initial — это начальное значение ячейки, которое будет использовано при неуказании инициализации.
Доступ к атрибутам происходит через оператор стрелка ->
Что это? Да это же шаблон структуры. Параметр шаблона под именем value_type — тип.
А могло быть и число
#struct [$type: Type, $int: N] ()
А мог быть и тип с ограничением $type_refers[_number]
А могла быть и категория с ограничением $cat_includes[_plain]
Собственно нужны ли категории и шаблоны в языке — под вопросом.
Про типизацию и название языка
Мне нравится название ksi-lang (и строгая и динамическая типизация)
ksi-script (только динамическая типизация)
ksi-pl (только строгая типизация)
Я долгое время пилю интерпретатор для ksi-script
Дело в том, что я по разным причинам откладывал разработку и возвращался к ней снова.
В одиночку непросто это.
Назначение языка — для любых целей. Хотя кси скрипт скорее всего для web, и как скриптовый движок в game-dev
p/s
Вопрос: синглтон или набор статик свойств
Ответ: вид значений #nest (это команда определиния типа)
Попытка создания значения типа, который nest only, приводит к получению этого же типа.
К струткуре можно добавить статик свойства командой #add_nest
Перечисления — они для чёткого набора состояний ячейки.
У элемента перечисления я думаю можно добавить атрибут data, чтобы там что-то хранить.
Атрибут data для каждого элемента перечисления привязан к этому элементу. Сами элементы передаются по значению-номеру (их data не копируется при этом).
Можно сделать атрибуты ->name ->index ->next_wrap (обсуждаемо)
А сомому енуму атрибут align_h->elements
Вот такую примерно можно придумать интроспекцию.
В нюансах все прелести и сложности. Как делать захват локальных ячеек в sequence, который внутри sequence ? Всё ли захватывать, или указывать конкретные, или автоматом по необходимости (как в javascript).
Нужна ли языку константность (read_only ячейки)
Т.е при первом присвоении в имени ячейки окончание — спецсимвол из конфига.
В использовании имени в дальнейшем ридонли символ уже не указывается (чтобы не пришлось переписывать все места вручную, привет ide).
private public protected / friend — отдельная тема. Но я бы не тянул геттеры и сеттеры из си шарп
Наследование — сомнительно, но можно сделать команду #insert_struct
Это позволит при интроспекции обходить свойства структуры именно в заданной последовательности. (В отличии от наследования, где сперва идёт база)
Доступ к под-объекту — как вариант в индексе [] тип
Ещё есть идея
std::optional аналог
Оно хранит только заданные опции. Чтение из незаданных — вопрос. (или _null / _absent; либо ошибка/exception)
В отличии от $map — имена допустимых ключей явно заданы в определении.
Способ хранения данных — implementation defined
(наверно для $cat_exact можно по битам опции разложить, или придумать свой #bit_set / #flags)
Для системных функций я бы ещё сделал префикс &
&if_first &each &loop &write &switch &catch &throw
Так мы освобождаем имя модуля @std для прочих штук.
--
Для исключений (если да) — атрибуты:
e->source.line
e->source.column
e->source.file
Получится собрать команду разработчиков нового языка программирования в этом году?
Какие идеи вам приглянулись?
pp/s Хотел сперва вкратце написать про ksi, чтож
Даже идею про #actions (аналог интерфейсам не показал)
Ещё можно придумать команду #allow_actions_only, исключающую применение повторябельной #allow_actions
S>Вот если посмотреть популярные ЯП — то это дофига работы команд. Годы работы, годы доработок. Т.е. идея может быть вдохновлена одним человеком (на самом деле он эту идею где-то подсмотрел, как правило), а вот разработка языка — это уже только команда.
S>Получается хороший новый язык не может создать один человек и это всегда годы работы. Т.е. неожиданно новый ЯП возникнуть не может, в отличие от JS-фреймворка типа Vue.
S>Но бывают и исключения. Вот же наши как-то смогли создать тот же Nemerle вообще без особых затрат, силами энтузиастов. Как так?
В одиночку разработать свой ЯП я считаю возможным. Но трудности возникают когда приходится принять то или иное решение.
Это может быть и касательно синтаксиса, и касательно реализации, или установки неких ограничений.
Совместными усилиями, как мне кажется, результата можно добиться быстрее.
PHP изначально сделал Расмус. Классов там не было, их добавили позднее (в 5-ой версии), когда появилась команда разработчиков. Енумы там появились вообще относительно недавно (версия 8.1). Кстати имена переменных в PHP начинаются с символа $
Квадратные скобки там служат не только для обращения к элементу по индексу, но и для создания массива. Все массивы в PHP ассоциативны (как ключ допустимо и целое число и строка текста).
Я думаю многие задумывались поменять что-то в уже существующих ЯП, с которыми они работали, или что-то туда добавить. А не лучше ли вообще сделать своё? — Примерно такая идея у меня возникла ещё когда я учился в Колледже. Вам же это знакомо?
Я действительно хочу учавствовать в разработке нового ЯП!
Си++ хорош, но слишком переусложнён. Его стандартники делают с ним, то что хотят они — и это не всегда чего хотим мы.
Когда методы определяются внутри самого класса, то класс постепенно разрастается и превращается в монстра великана.
Мне кажется более наглядным, когда в программе данные определяются отдельно от действий над ними.
Я предлагаю создать свой язык вместе. У меня есть идеи, чтобы их обсудить.
Фичи языка:
*- Ячейка — это может быть: локальная переменная, параметр функции, свойство модуля, и т.д.
*- Возможность работать с типом, как со значением. Тип такой ячейки будет $type
*- Имена системных типов начинаются к примеру с символа $ (но это можно указать в конфиге, как и другие начальные символы).
Таким образом добавление системных типов не будет конфликтовать с уже написанным пользовательским кодом.
Тип может быть ключом ассоциативного массива, ведь его можно сравнивать с другим типом как просто значение.
t1 = $int
t2 = $float
t1 <> t2
*- Императивный кусок кода помещается в фигурные скобки и имеет тип $seq (sequence).
do = {
/* expression-list here */
}
Значение типа $seq можно передавать в функцию, присваивать в ячейку, и даже использовать как ключ в ассоциативном массиве.
Это позволит определять языковые конструкции как обычные функции.
Сами по себе фигурные скобки — действий внутри не выполняют без определённой команды. Это называется вычисление по требованию.
* По сути sequence — это как лямбда без параметров.
do = {
local_var = { call_some_fn() }
local_var()
call_other_fn(local_var)
}
Приблизительный пример:
if_each!
[
{ a == b } : { /* действие 1 */ }
{ /* условие 2 */ } : { /* действие 2 */ }
]
if_first!
[
{ a == b } : { /* действие 1 */ }
{ /* условие 2 */ } : { /* действие 2 */ }
$bool.true : { /* действие иначе */ }
]
Квадратные скобки — массив/мапа. Двоеточие — разделитель в паре ключ-значение.
hint: думаю нужно отделить типы $map от $array
Символ восклицательный знак после ячейки с функцией — применить её к последующему выражению (просто чтобы не писать круглые скобки if_first([])).
if_first — выполняет sequence каждого условия до тех пор, пока не встретит первое правдивое, и тогда выполнит sequence действие из значения пары.
if_each — будет проверять все условия, даже после того как встретит правдивое.
Такая штука ведь нагляднее, чем городить огород из if else if else ?
Функцию вывода на консоль или в файл можно сделать чтобы она принимала sequence. Так она итератором будет вычислять элементы $seq — выражения, и выводить их результат, пока не они не закончатся, или не возникнет ощибка вывода / исключение, при которой оставшиеся выражения не будут вычислены.
@main
do = {
/* c-style function call */
@std.write( {sinus(a), " ", cosine(b)} )
/* Smarty style function call */
{sin(a), " ", cos(b)} |write@std
}
Собачка — начало имени модуля. Файл программы начинается с указания модуля. Далее следуют ячейки модуля — его свойства.
Это декларативная часть. Императивная часть находится внутри фигурных скобок.
При запуске программы выполнится sequence из ячеки do модуля @main
Запятая — разделитель опциональный.
Вертикальная палка перед ячейкой с функцией — применить функцию как метод к результату предшествующего выражения.
Так можно вызывать функции по цепочке слева-направо. Эта идея подсмотрена в PHP библиотеке шаблонов Smarty
a |sinus |write@std(to: file)
/* аналог: */
write@std(sinus(a), to: file)
-- line-comment
a |sinus => x |write_line@std(to: file)
write_line@std( x = sinus(a), to: file )
Оператор "присвоить вправо" => в общем то под вопросом.
Определение структур как типов: команда #struct
Команда #fn определяет функцию
@lib
coords = #struct ( x y )
scale = #fn (pos, factor) {
#ret = coords(pos.x * factor, pos.y * factor)
}
do = {
pos = coords(10 20) |scale(5.5)
}
Ячейка результата вызова функции #ret
Если добавить в язык Возможность её-же принять как параметр функции, то:
scale = #fn (#ret, factor) {
#ret = coords(pos.x * factor, pos.y * factor)
}
do = {
pos_1 = coords(10, 20)
pos_2 = pos_1 |scale(2)
pos_2& |scale(0.5)
}
Строчка: pos_2& |scale(0.5) передать яцейку pos_2 как link на ячейку (функция сможет её изменить)
Строчка: pos_1 |scale(2) запись в ячейку #ret в этом случае не изменит ячейку pos_1
scale можно переписать по другому:
scale = #fn (#ret, factor) {
(#ret.x, #ret.y) *= factor
}
do = {
pos_1 = coords(10, 20)
pos_1 |scale(2)
}
Тут будут изменены свойства (x y) у координат в pos_1
Ведь структуры передаются по указателю, как и прочие значения системной категории _compound
Все категории типов (значение $cat) произрастают из базовой _any, включённой во все типы по-умолчанию.
Категория — это просто категория, она не хранит в себе данных. Каждый тип может принадлежать своему набору категорий.
В программе можно проверить их принадлежность. Это может быть полезно при динамической типизации.
При строгой типизации $cat можно использовать как ограничение (например ограничить тип параметра функции).
@sample
squared = #fn(_plain_number: n) { #ret = n*n }
do = {
i = 2 |squared
f = 1.5 |squared
}
@global
/* exposition only */
#cat (
_any /* base category */
_compound /* values of _compound types are passed by pointer */
_plain /* values of _plain types are passed by-value */
_number
_plain_number #includes( _plain _number )
)
_plain_number #refers_to ($int $float)
@lib
complex = #struct( re im )
complex #refers( _number )
Категории можно использовать как обычные значения в программе, их сравнивать, использовать как ключ в $map.
Ведь тип $cat пренадлежит категории _map_key
Естественно пользователь сможет определять и свои категории типов.
( _null / _undefined ) — пример для пустых значений при динамической типизации, возможно это системные категории.
Атрибуты
Например у всех типов есть атрибут initial — это начальное значение ячейки, которое будет использовано при неуказании инициализации.
Доступ к атрибутам происходит через оператор стрелка ->
range = #struct [$type: value_type] ( value_type: from, to )
range->initial(from: 0, to: value_type->max)
/* или */
range->initial.from = value_type->zero
range->initial.to = value_type->max
Что это? Да это же шаблон структуры. Параметр шаблона под именем value_type — тип.
А могло быть и число
#struct [$type: Type, $int: N] ()
А мог быть и тип с ограничением $type_refers[_number]
А могла быть и категория с ограничением $cat_includes[_plain]
Собственно нужны ли категории и шаблоны в языке — под вопросом.
Про типизацию и название языка
Мне нравится название ksi-lang (и строгая и динамическая типизация)
ksi-script (только динамическая типизация)
ksi-pl (только строгая типизация)
Я долгое время пилю интерпретатор для ksi-script
Дело в том, что я по разным причинам откладывал разработку и возвращался к ней снова.
В одиночку непросто это.
Назначение языка — для любых целей. Хотя кси скрипт скорее всего для web, и как скриптовый движок в game-dev
p/s
Вопрос: синглтон или набор статик свойств
Ответ: вид значений #nest (это команда определиния типа)
colors = #nest (
red = 0h_ff0000
black = 0
)
/* colors.red |write */
Попытка создания значения типа, который nest only, приводит к получению этого же типа.
К струткуре можно добавить статик свойства командой #add_nest
@sample
color = #struct ( r g b a )
color->initial.a = 255
color #add_nest (
from_code = #fn($int: code) { /* вычисления */ }
red_color = color.from_code(colors.red)
)
Перечисления — они для чёткого набора состояний ячейки.
У элемента перечисления я думаю можно добавить атрибут data, чтобы там что-то хранить.
Атрибут data для каждого элемента перечисления привязан к этому элементу. Сами элементы передаются по значению-номеру (их data не копируется при этом).
align_h = #enum (left center right)
align_h.left->data = /* да тут что угодно, хоть даже структура */
do = {
a = align_h.center
a->data = /* what? can we change it? */
a->name |write_line@std
/* align_h.center->name */
}
Можно сделать атрибуты ->name ->index ->next_wrap (обсуждаемо)
А сомому енуму атрибут align_h->elements
align_h->elements |each: #fn(it) { it->data |write }
Вот такую примерно можно придумать интроспекцию.
В нюансах все прелести и сложности. Как делать захват локальных ячеек в sequence, который внутри sequence ? Всё ли захватывать, или указывать конкретные, или автоматом по необходимости (как в javascript).
Нужна ли языку константность (read_only ячейки)
$int: (c# = 5, x = 4, y# = c + x)
c += 1 /* ошибка */
Т.е при первом присвоении в имени ячейки окончание — спецсимвол из конфига.
В использовании имени в дальнейшем ридонли символ уже не указывается (чтобы не пришлось переписывать все места вручную, привет ide).
private public protected / friend — отдельная тема. Но я бы не тянул геттеры и сеттеры из си шарп
Наследование — сомнительно, но можно сделать команду #insert_struct
coords = #struct( x y )
game_object = #struct (
name
#insert_struct( coords )
type
)
Это позволит при интроспекции обходить свойства структуры именно в заданной последовательности. (В отличии от наследования, где сперва идёт база)
Доступ к под-объекту — как вариант в индексе [] тип
rabbit = game_object( coords:(1 1), name: "Vitalik" )
pos = rabbit[coords]
Ещё есть идея
std::optional аналог
#cat ( _yes )
range = #options (
_any:
from
to
$cat_exact[_yes]:
exact_from
exact_to
)
r = range(to: 5, exact_to: _yes)
Оно хранит только заданные опции. Чтение из незаданных — вопрос. (или _null / _absent; либо ошибка/exception)
В отличии от $map — имена допустимых ключей явно заданы в определении.
Способ хранения данных — implementation defined
(наверно для $cat_exact можно по битам опции разложить, или придумать свой #bit_set / #flags)
Для системных функций я бы ещё сделал префикс &
&if_first &each &loop &write &switch &catch &throw
Так мы освобождаем имя модуля @std для прочих штук.
--
Для исключений (если да) — атрибуты:
e->source.line
e->source.column
e->source.file
Получится собрать команду разработчиков нового языка программирования в этом году?
Какие идеи вам приглянулись?
pp/s Хотел сперва вкратце написать про ksi, чтож
Даже идею про #actions (аналог интерфейсам не показал)
do = {
c = 0h_e3d7f5 !as[color]
}
color = #struct ($integer_unsigned[8]: r g b)
color #allow_actions( conversion_from[_integer] )
conversion_from = #actions [$hint: from_type] (
as = #fn_params [$type: to_type] (from_type: this) : to_type
)
conversion_from[$int].as[color] = #fn (_integer: this) : color {
[#ret.r #ret.g #ret.b] = (this !make_byte_array: [3 2 1])
}
Ещё можно придумать команду #allow_actions_only, исключающую применение повторябельной #allow_actions