Я экспериментирую с созданием полунатурального скриптового языка, в основном для собственных целей обучения и для развлечения.Подвох в том, что он должен быть в нативном C #, без синтаксического анализа или лексического анализа с моей стороны, поэтому все, что я делаю, должно быть в состоянии сделать с помощью обычного синтаксического сахара.предложение будет таким, чтобы его было легко читать и изучать, особенно для тех, кто не особенно хорошо разбирается в программировании, но я также хочу, чтобы полный функционал нативного кода был доступен пользователю.
Например,в идеальном мире это выглядело бы как естественный язык (в данном случае английский):
When an enemy is within 10 units of player, the enemy attacks the player
В C #, позволяя такому предложению фактически выполнять то, что намеревается сценарий, почти наверняка потребует, чтобы это былострока, которая проходит через анализатор и лексический анализатор.Моя цель не в том, чтобы у меня было что-то такое естественное, и я не хочу, чтобы сценарист использовал строки для сценария.Я хочу, чтобы скрипт имел полный доступ к C # и имел такие вещи, как подсветка синтаксиса, intellisense, отладка в IDE и т. Д. Итак, я пытаюсь получить что-то, что легко читается, но есть в нативном C #.Пара основных препятствий, которые я не вижу, как преодолеть это избавление от периодов .
, запятых ,
и скобок для пустых методов ()
.Например, что-то вроде этого возможно, но не очень читается:
// C#
When(Enemy.Condition(Conditions.isWithinDistance(Enemy, Player, 10))), Event(Attack(Enemy, Player))
Используя такой язык, как Scala, вы действительно можете стать намного ближе, потому что точки и скобки могут быть заменены одним пробелом во многихслучаев.Например, вы можете взять приведенное выше утверждение и сделать его похожим на Scala:
// Scala
When(Enemy is WithinDistance(Player, 10)) => Then(Attack From(Enemy, Player))
Этот код на самом деле будет компилироваться, если вы настроите свой движок для его обработки, фактически вы можетеуговорить дальнейшие скобки и запятые из этого.Без синтаксического сахара в приведенном выше примере это было бы больше похоже на это в Scala:
// Scala (without syntactical sugar)
When(Enemy.is(WithinDistance(Player, 10)) => Then(Attack().From(Enemy, Player))
Суть в том, что я хочу максимально приблизиться к чему-то похожему на первый пример scala с использованием нативного C #.Может быть, на самом деле я ничего не могу сделать, но я готов попробовать все возможные трюки, чтобы сделать его более естественным, и вывести отсюда точки, скобки и запятые (кроме случаев, когда они имеют смыслдаже на естественном языке).
Я не настолько опытен в C #, как другие языки, поэтому я могу не знать о некоторых синтаксических приемах, таких как макросы в C ++.Не то чтобы макросы на самом деле были бы хорошим решением, они, вероятно, вызывали бы больше проблем, чем решали бы, и были бы кошмаром отладки, но вы понимаете, куда я иду с этим, по крайней мере в C ++ это было бы осуществимо.Это то, что я хочу, возможно даже в C #?
Вот пример, используя выражения LINQ и Lambda, вы можете иногда выполнять тот же объем работы с меньшим количеством строк, меньшим количеством символов и кодировать чтение ближе к английскому,Например, вот пример трех коллизий, которые происходят между парами объектов с идентификаторами, мы хотим собрать все коллизии с объектом с идентификатором 5, затем отсортировать эти коллизии по «первому» идентификатору в паре и затем вывестипар.Вот как бы вы сделали это без выражений LINQ и / или Lambra:
struct CollisionPair : IComparable, IComparer
{
public int first;
public int second;
// Since we're sorting we'll need to write our own Comparer
int IComparer.Compare( object one, object two )
{
CollisionPair pairOne = (CollisionPair)one;
CollisionPair pairTwo = (CollisionPair)two;
if (pairOne.first < pairTwo.first)
return -1;
else if (pairTwo.first < pairOne.first)
return 1;
else
return 0;
}
// ...and our own compable
int IComparable.CompareTo( object two )
{
CollisionPair pairTwo = (CollisionPair)two;
if (this.first < pairTwo.first)
return -1;
else if (pairTwo.first < this.first)
return 1;
else
return 0;
}
}
static void Main( string[] args )
{
List<CollisionPair> collisions = new List<CollisionPair>
{
new CollisionPair { first = 1, second = 5 },
new CollisionPair { first = 2, second = 3 },
new CollisionPair { first = 5, second = 4 }
};
// In a script this would be all the code you needed, everything above
// would be part of the game engine
List<CollisionPair> sortedCollisionsWithFive = new List<CollisionPair>();
foreach (CollisionPair c in collisions)
{
if (c.first == 5 || c.second == 5)
{
sortedCollisionsWithFive.Add(c);
}
}
sortedCollisionsWithFive.Sort();
foreach (CollisionPair c in sortedCollisionsWithFive)
{
Console.WriteLine("Collision between " + c.first +
" and " + c.second);
}
}
А теперь тот же пример с LINQ и Lambda.Обратите внимание, что в этом примере нам не нужно одновременно делать CollisionPair
и IComparable
и IComparer
, и не нужно реализовывать методы Compare
и CompareTo
:
struct CollisionPair
{
public int first;
public int second;
}
static void Main( string[] args )
{
List<CollisionPair> collisions = new List<CollisionPair>
{
new CollisionPair { first = 1, second = 5 },
new CollisionPair { first = 2, second = 3 },
new CollisionPair { first = 5, second = 4 }
};
// In a script this would be all the code you needed, everything above
// would be part of the game engine
(from c in collisions
where ( c.first == 5 || c.second == 5 )
orderby c.first select c).ForEach(c =>
Console.WriteLine("Collision between " + c.first +
" and " + c.second));
}
В итоге у нас осталось выражение LINQ и Lambda, которое читается ближе к естественному языку и намного меньше кода как для игрового движка, так и для сценария.Подобные изменения действительно то, что я ищу, но, очевидно, LINQ и Lambda ограничены конкретным синтаксисом, а не чем-то общим, как мне хотелось бы в конце.