Существует ли C-подобный мини-синтаксис / язык, который может быть переведен как на родной C/C++, так и на Java?

Я бы хотел, чтобы мое приложение было "скриптовым". Язык сценария должен быть набран и подобен Си. Он должен иметь обычные операторы управления, примитивные типы, массивы и операторы, которые можно найти как в C, так и в Java. Сценарии должны иметь возможность определять функции и вызывать предварительно определенные функции API, такие как sin() ..., которые я предпочитаю предлагать. Если сценарий пройдет проверку синтаксиса, он будет затем переведен приложением в Java, а затем скомпилирован на лету, но также должна быть возможность перевести его на C/C++, и он будет скомпилирован нативно. Проверка синтаксиса и перевод на Java/C должны выполняться в JVM. Мне не нужен переводчик, так как он всегда будет переведен на Java/C. Только проверка синтаксиса и переводчик.

Есть ли такой язык там? Если нет, то какой самый простой способ сделать это в JVM, учитывая, что я не разбираюсь в программировании компилятора / интерпретатора? (Если бы я был, мне не нужно было бы задавать этот вопрос...)

Если есть решение "Scala", это также будет хорошо, так как я на самом деле перевожу свой Java-код в Scala.

[РЕДАКТИРОВАТЬ] Единственная причина, почему я хочу перевод C / C++ - это производительность. Я ожидаю много "битовых манипуляций" над массивами, для которых Java не очень подходит, в частности из-за проверки диапазона при каждой операции индекса массива. Я также ожидаю много объектов, которые косвенно стоят в циклах GC.

[РЕДАКТИРОВАТЬ #2] Хорошо, я вижу, что я должен получить бетон. Я рассматриваю программирование клона Minecraft в качестве упражнения, чтобы отвлечься от "Business Computing". Я говорю о движке, а не об игровом процессе. И меня больше интересует серверная часть, чем 3D, потому что я "серверный парень". Мы говорим о том, чтобы использовать шаблон веса полета для представления миллионов объектов (блоков / вокселей) и получать к ним доступ много раз в секунду. Это не то, для чего была создана JVM. Посмотрите в этой статье пример того, что я имею в виду: почему мы выбрали CPP вместо Java

Я не хочу программировать все на C/C++, но подозреваю, что это единственный способ получить хорошую производительность. Другой пример того, что я имею в виду, это VoltDB, который, возможно, является самой быстрой базой данных SQL. Они написали это на Java и C/C++, используя Java для ввода-вывода и сети, а C - для тяжелых манипуляций с памятью и суеты. Пользовательские хранимые процедуры написаны на Java, но я не думаю, что так должно быть. Я думаю, что должна быть возможность компилировать в Java на клиенте и в тестовых сборках и компилировать в C на сервере, где можно настроить полную среду разработки.

9 ответов

Решение

Два слова: преждевременная оптимизация.

Вы беспокоитесь о производительности. Но, учитывая, что вы хотите сделать клон Майнкрафта, это означает, что игровой мир очень хорошо может быть представлен трехмерным массивом. Доступ к ним достаточно быстрый на всех упомянутых языках программирования; игровая логика должна выполняться гораздо больше времени, чем доступ к миллионам записей массива. Так зачем оптимизировать часть, которая не займет большую часть времени вычислений - даже до того, как вы написали минимально работающую версию?

Возможно, вы захотите создать интерфейс Java или черту Scala, которая представляет игровой мир. Он предлагает методы для получения и хранения содержимого блоков игрового мира. Позже вы также можете добавить массовые методы для дальнейшей оптимизации производительности; например тот, который проверит, все ли блоки в данном кубе пусты, или посчитает количество деревянных блоков, что-то в этом направлении. Но в начале лучше не использовать эти методы или создавать тривиальные реализации, которые основаны на повторном вызове абстрактных методов. Вы можете оптимизировать их позже.

Затем вы можете предоставить очень простую реализацию этого интерфейса на Java/Scala, которая на самом деле использует трехмерный массив. Альтернативой может быть карта, ключи которой являются координатами, а значения - состояниями блоков. Преимущество состоит в том, что не будет никакого реального ограничения на размер игрового мира, и пустые блоки не будут занимать какую-либо память (для координат с пустыми блоками нет записи на карте). Недостатком, очевидно, может быть производительность.

На этом этапе вы можете рассмотреть возможность более плотной упаковки данных, если они занимают слишком много памяти. Вы можете использовать наборы битов. Когда вы достигаете этой стадии, на самом деле имеет смысл использовать JNI для внедрения некоторого кода, написанного на C или C++, в JVM. Таким образом, вы сохраняете игровую логику в Java/Scala и выполняете упаковку и поиск памяти в C.

Нет никакого смысла в создании общего "скриптового" источника, который может создавать Java/Scala и C/C++ версию нативной части кода; нативные функции C/C++ сильно зависят от оптимизаций, которые нельзя напрямую перевести на Java. Если вы хотите запустить сервер в "чистом режиме Java/Scala", то есть без функций JNI, просто используйте другие классы, которые вы создали в предыдущем шаге. Они могут быть немного медленнее, но они являются чистым байтовым кодом JVM. И поскольку вы сохранили их простыми, нет никакой опасности, что вам придется расширять их или вносить в них новые ошибки. По крайней мере, накладные расходы на создание или адаптацию генератора кода на разных языках программирования намного, намного больше, чем поддержание двух отдельных баз кода, особенно когда реализация Java/Scala действительно проста.

Конечно, битовая упаковка только улетит. Возможно, вы захотите заметить, что некоторые части игрового мира почти полностью пусты (особенно над поверхностью), а другие содержат огромные области, заполненные блоками одного типа (например, подземные области, состоящие почти исключительно из камня). Поддержание огромной структуры памяти с такой избыточностью на самом деле является пустой тратой памяти. Таким образом, вы, вероятно, рассмотрите возможность упаковки игрового мира в дерево, где каждый узел обозначает большую кубическую область игрового мира, и дети делят его дальше, вплоть до листьев, которые описывают содержимое только одной конкретной координаты игрового мира. Когда у одного узла есть только дочерние элементы одного и того же типа контента, вам не нужно хранить дочерние элементы. Просто срежьте дерево в этой точке и попросите узел сказать: "Вам не нужно смотреть дальше. Я полон воды, поэтому каждая координата, которая находится внутри меня, указывает на плитку с водой". - Это значительно уменьшит использование памяти. Только части игрового мира, которые на самом деле являются сложными, будут занимать много памяти, и это правильно. Более скучные части мира занимают лишь несколько узлов в дереве. Это хорошо. А поскольку это дерево, то прохождение его от вершины к листу занимает в среднем логарифмическое время. Это очень хорошо! - Конечно, вы должны сохранить дерево изменчивым. Если скучная часть мира, представленная только одним узлом, изменится, вам нужно взломать этот узел и разделить его на двух или более дочерних узлов. Если потом снова станет просто, вы можете снова присоединиться к детям и срубить дерево.

В этот момент вы можете заметить одну вещь: упаковка памяти и оптимизация доступа больше не являются проблемой здесь. Такое дерево не может быть разумно оптимизировано с помощью встроенных функций для методов хранения и поиска. Если вы сможете получить больше, чем, скажем, 10% от такой оптимизации, это было бы невероятно и невероятно впечатляюще. (Скорее всего, это может означать, что аналоги Java/Scala были плохо оптимизированы.) Такое минимальное увеличение скорости не оправдывает огромных дополнительных усилий, которые необходимо приложить к нему. Скорее вставьте лучший процессор в машину и наслаждайтесь временем, которое вы сэкономили, поедая мороженое, наблюдая за доктором Хаусом или продолжая улучшать игру и делать ее более интересной и привлекательной для игроков. Создавая что-то ценное, что действительно улучшит продукт.

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

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

Однако можно оптимизировать еще кое-что: алгоритм генерации процедурного мира. Это один из ключевых компонентов, который вы можете написать на C или C++. Он должен быть относительно небольшим и не иметь большого количества кода, но он требует много математики и будет вызываться очень часто. Так что оптимизируйте его хорошо и сделайте из него небольшую библиотеку JNI. Это даст вам огромный прирост производительности, который стоит усилий. (Конечно, вы, возможно, захотите сначала выполнить реализацию Java/Scala. Если это уже достаточно быстро, тогда нет необходимости попадать в проблему JNI.)

Если ваша процедура генерации мира все еще должна быть слишком медленной, вы можете реализовать кеш для нее. Кэш может даже превентивно генерировать часть окружения игрока, когда JVM имеет некоторое время ожидания.

Я изложил этот процесс разработки для вас как итерацию нескольких идей, одна из которых лучше предыдущей. Изображение, которое вы бы начали писать библиотеки оптимизированного кода C/C++ уже на первом этапе. Это было бы пустой тратой времени; Вы могли бы выбросить все это на более поздних этапах. Эффективное хранилище массивов, использующее упаковку битов, написанное на C, - это хорошо, но оно абсолютно бесполезно, когда вы реорганизуете свой мир в дерево с разделенными двоичными пространствами.

Так что не переусердствуйте. Если вы не можете создать минимально работающую (но медленную и неоптимизированную) версию только в Java/Scala, то вы не можете создать оптимизированную версию в C/C++ или в каком-либо кросс-компиляционном языке сценариев. Сначала делайте простые версии, затем тестируйте производительность и оптимизируйте только тогда, когда это действительно необходимо. Не начинайте свой проект с разработки концепций для оптимизации в первую очередь. Подобные оптимизации должны быть последними вещами, над которыми вы будете работать.

Может быть, haXe подойдет вашим потребностям. Это промежуточный язык высокого уровня, который может быть скомпилирован в исходный код C++. Цели Java находятся в разработке.

Синтаксис JavaScript похож на C/C++ и Java. Существуют различные движки JavaScript, один из которых написан на Java - Rhino.

Существует также LLVM, который является механизмом компиляции, который компилирует код в свой собственный байт-код и оттуда в машинный код. Он также имеет интегрированный JIT и существует несколько языковых интерфейсов. Я не слишком много знаю об этом проекте, но он выглядит интересно.

Есть ли такой язык там? ... Какой самый простой способ сделать это в JVM,

Я хотел бы использовать простую Java и позволить JVM компилировать код в машинный код. Вы можете сделать этот сборник более агрессивным, если вам это нужно.

Возможно, вы могли бы уточнить, что вы надеетесь получить, что JVM еще не дает вам.


Если вы серьезно хотите развить свой собственный мини-язык, вы хотите иметь реалистичное представление о том, что для этого нужно. Если вы не возьмете на себя обязательство, вы, вероятно, придумаете что-то, что не так хорошо.

http://www.ohloh.net/p/openjdk/estimated_cost

OpenJDK: Project Cost Calculator

Include     
Codebase    4,782,885 lines
Effort (est.)   1451 person-years
Estimated Cost  $ 79,805,125

Поскольку Scala, кажется, вариант, просто придерживайтесь его.

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

Что касается производительности, то JVM отвечает за дальнейшую компиляцию байт-кода до нативного кода, это тоже неплохо для этой работы. Я сомневаюсь, что вы заметите существенное повышение производительности с C/C++ (время компиляции, в частности, будет намного хуже)

Это полный C, но, возможно, полезно для вашей цели. Цибил.

Прочитав все ответы, я решил это сделать. Чтобы начать работу над своим проектом, я не буду использовать сценарии в начале. Вместо этого я буду программировать на Java, но без использования проблемных конструкций.

Сначала я создам базовый класс API, полный любых вспомогательных методов, которые могут мне понадобиться. 95% этого кода просто делегируют какой-то библиотеке, которая выполняет эту работу за меня. Этот класс API будет иметь фабричные методы для классов примитивных коллекций. Также будет несколько служебных классов, таких как Point, Event, ...

Затем я создам свой игровой код, расширив объект API. Я не буду использовать модификаторы доступа, поэтому все общедоступно. Я не буду использовать "final", "static", "this", "super", "abstract", "interface", массивы, универсальные шаблоны, приведение классов, "instanceof", "new" или обработку исключений. Я не буду импортировать ничего или обращаться к этому стандартному Java API, кроме класса String. Все, что мне нужно от стандартного Java API, должно быть включено в мой базовый класс API. Есть также некоторые ограничения операторов, в частности побитовых.

Чтобы преодолеть ограничение, вызванное неиспользованием обобщений, приведением классов или "instanceof", я буду использовать инверсию управления, внедрение зависимостей и немного размышлений для решения проблемы.

Это должно быть относительно легко и быстро для программирования, но не будет работать. Позже, когда мой движок стабилизировался и был отлажен, я могу вернуться к своей первоначальной идее и создать парсер, который будет либо переводиться в оптимизированную Java, либо в C++. Удаление всех этих специальных конструкций Java значительно упростит создание синтаксического анализатора, и, поскольку я буду использовать стандартный синтаксис Java, я могу просто использовать предопределенный синтаксический анализатор и настроить его в соответствии со своими потребностями.

Я обнаружил, что Groovy довольно хорошо работает как C-подобный язык сценариев в JVM.

Не уверен, насколько хорошо вы могли бы перевести его на C/C++, но если у вас есть код C/C++, который вам нужно вызвать, вы можете легко связать его с JNI.

Я не думаю, что есть смысл в переводе кода на основе JVM в C/C++. JVM JIT является действительно хорошим компилятором уже для очевидного пути компиляции (язык JVM -> байт-код JVM -> нативный) и почти наверняка побьет производительность всего, что пытается сделать намного более сложный (язык JVM -> C/C++ -> родной).

А как насчет JavaScript? Я не отвечаю всем вашим требованиям (они только слабо типизированы), но кажется, что они удовлетворяют довольно многим из них: C-подобные обычные операторы управления и операторы, массивы, функции, вы можете предложить свой собственный API... Он даже может быть скомпилирован в Java, но современные интерпретаторы в любом случае неплохо справляются с его компиляцией. Если вы пишете на Java, я настоятельно рекомендую вам взглянуть на Rhino.

Другие вопросы по тегам