Исходный код Parsing - уникальные идентификаторы для разных языков?

Я создаю приложение, которое получает исходный код для ввода и анализирует несколько аспектов кода. Он может принимать код из многих общих языков, например. C/С++, С#, Java, Python, PHP, Pascal, SQL и т.д. (Однако многие языки не поддерживаются, например, Ada, Cobol, Fortran). Как только язык известен, мое приложение знает, что делать (у меня разные обработчики для разных языков).

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

Мне кажется, что должен быть способ выяснить (в большинстве случаев), что такое язык, из самого входного текста. Несколько примечаний:

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

Примеры:

  • Если текст содержит "x- > y()", я могу точно знать, что это С++ (?)
  • Если текст содержит "public static void main", я могу точно знать, что это Java (?)
  • Если текст содержит "для x: = y to z do begin", я могу точно знать, что это Pascal (?)

Мой вопрос:

  • Вы знакомы с какой-либо стандартной библиотекой/методом для автоматического определения того, что является языком исходного кода?
  • Каковы уникальные коды "токенов", с которыми я мог бы отличить один язык от другого?

Я пишу свой код в Python, но я считаю, что этот вопрос является агностиком.

Спасибо

Ответ 1

Vim имеет свойство файла автоопределения. Если вы загрузите исходный код vim, вы найдете файл /vim/runtime/filetype.vim.

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

Ответ 2

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

Ответ 3

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

Этот метод имеет следующие преимущества:

  • У вас уже есть большая часть кода, необходимого для этого.
  • Анализ может выполняться параллельно на многоядерных машинах.
  • Большинство языков могут быть устранены очень быстро.
  • Этот метод очень надежный. Языки, которые могут показаться очень похожими при использовании нечеткого анализа (например, байсиан), вероятно, будут иметь много ошибок при запуске фактического анализатора.
  • Если программа правильно проанализирована на двух разных языках, тогда никогда не было никакой надежды выделить их в первую очередь.

Ответ 4

Одна программа, которую я знаю, которая даже может различать несколько разных языков в одном файле, ohcount. У вас могут быть некоторые идеи, хотя я действительно не знаю, как они это делают.

В целом вы можете искать отличительные шаблоны:

  • Операторы могут быть индикатором, например := для Pascal/Modula/Oberon, => или всего LINQ в С#
  • Ключевые слова были бы еще одним, так как, возможно, не два языка имеют одинаковый набор ключевых слов
  • Правила обхода для идентификаторов, предполагая, что часть кода написана в соответствии с передовыми методами. Вероятно, очень слабое правило
  • Стандартные функции или типы библиотек. Специально для языков, которые обычно сильно зависят от них, например PHP, вы можете просто использовать длинный список стандартных библиотечных функций.

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

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

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

Ответ 5

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

  • использование таких функций, как предварительный процессор C, может эффективно маскировать недостижимый язык вообще
  • поиск ключевых слов недостаточен, поскольку ключевые слова могут использоваться на других языках в качестве идентификаторов
  • Поиск реальных языковых конструкций требует от вас анализа кода, но для этого вам нужно знать язык
  • Что вы делаете с неправильным кодом?

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

Ответ 6

Некоторые мысли:

$x- > y() будет действителен в PHP, поэтому убедитесь, что нет символа $, если вы думаете, что С++ (хотя я думаю, что вы можете хранить указатели на объекты в структуре C, поэтому это также может быть C).

public static void main - это Java, если он правильно сложен - напишите Main и С#. Это усложняется, если вы учитываете языки, не учитывающие регистр, как многие языки сценариев или Pascal. Синтаксис атрибута [] в С#, с другой стороны, кажется довольно уникальным.

Вы также можете попробовать использовать ключевые слова языка - например, Option Strict или End Sub являются типичными для VB и т.п., тогда как yield, скорее всего, С# и initialization/implementation являются объектами Паскаль/Дельфы.

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

Ответ 7

Мой подход:

Создайте список строк или регулярных выражений (с учетом и без чувствительности к регистру), где каждому элементу присвоен список языков, для которых этот элемент является индикатором для:

  • class= > С++, С#, Java
  • interface = > С#, Java
  • реализует = > Java
  • [attribute] = > С#
  • procedure = > Паскаль, Модула
  • создать таблицу/вставить/... = > SQL

и т.д.. Затем проанализируйте файл по строкам, сопоставьте каждый элемент списка и посчитайте его.

Язык с наибольшим количеством выигрышей выигрывает;)

Ответ 8

Как насчет анализа частоты слов (с завихрением)? Разбирайте исходный код и классифицируйте его так же, как фильтр спама. Таким образом, когда в ваше приложение вводится фрагмент кода, который не может быть идентифицирован на 100%, вы можете показать ему самые близкие совпадения, которые пользователь может выбрать - это можно затем загрузить в вашу базу данных.

Ответ 9

Вот идея для вас. Для каждого из ваших N языков найдите некоторые файлы на этом языке, чего-то вроде 10-20 на язык будет достаточно, каждый из них не слишком короткий. Объедините все файлы на одном языке вместе. Назовите этот lang1.txt. GZip это к lang1.txt.gz. У вас будет набор файлов N langX.txt и langX.txt.gz.

Теперь возьмите файл и добавьте в каждый из файлов langX.txt файл langXapp.txt и соответствующий gzipped langXapp.txt.gz. Для каждого X найдите разницу между размером langXapp.gz и langX.gz. Наименьшая разница будет соответствовать языку вашего файла.

Отказ от ответственности: это будет работать достаточно хорошо только для более длинных файлов. Кроме того, он не очень эффективен. Но с плюсом вы не должны ничего знать о языке, он полностью автоматизирован. И он может обнаруживать естественные языки и говорить между французским или китайским языком. На всякий случай вам это нужно:) Но главная причина, я просто думаю, что интересно попробовать:)

Ответ 10

Очень интересный вопрос, я не знаю, можно ли отличить языки от фрагментов кода, но вот несколько идей:

  • Один простой способ - следить за отдельными кавычками: на некоторых языках он используется как оболочка символов, тогда как в остальных он может содержать целую строку
  • Унарная звездочка или унарный оператор амперсанда является определенным признаком того, что это либо C/С++/С#.
  • Паскаль - единственный язык (из приведенных) для использования двух символов для назначений :=. Паскаль также имеет много уникальных ключевых слов (начало, юг, конец,...)
  • инициализация класса с помощью функции может быть хорошим намеком для Java.
  • Функции, которые не принадлежат классу, исключают java (например, max())
  • Именование основных типов (bool vs boolean)
  • Это напоминает мне: С++ может выглядеть по-разному в рамках проектов (#define boolean int). Таким образом, вы никогда не сможете гарантировать, что вы нашли правильный язык.
  • Если вы запускаете исходный код с помощью алгоритма хеширования, и он выглядит одинаково, вы, скорее всего, анализируете Perl
  • Отступы - хороший совет для Python
  • Вы можете использовать функции, предоставляемые самими языками - например, token_get_all() для PHP - или сторонние инструменты - например pychecker для python - для проверки синтаксиса

Подводя итог: этот проект сделал бы интересный исследовательский документ (ИМХО), и если вы хотите, чтобы он работал хорошо, будьте готовы приложить к нему много усилий.

Ответ 11

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

Эвристика работает до определенной точки, и чем больше языков вы поддержите, тем меньше поможет вам получить от них. Но для первых нескольких версий это хороший старт, главным образом потому, что он быстро реализуется и работает достаточно хорошо в большинстве случаев. Вы можете проверить определенные ключевые слова, имена функций/классов в API, которые часто используются, некоторые языковые конструкции и т.д. Лучший способ - проверить, сколько из этих конкретных файлов имеет файл для каждого возможного языка, это поможет с некоторыми синтаксическими ошибками, пользовательские функции с именами типа this() на языках, на которых нет таких ключевых слов, материалов, написанных в комментариях и строковых литералах.

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

Ответ 12

Я думаю, вы никогда не должны полагаться на одну единственную функцию, поскольку отсутствие в фрагменте (например, кто-то, систематически использующий WHILE, а не for) может смутить вас.

Также старайтесь держаться подальше от глобальных идентификаторов, таких как "IMPORT" или "MODULE" или "UNIT" или INITIALIZATION/FINALIZATION, поскольку они могут не всегда существовать, быть необязательными в полных источниках и полностью отсутствовать в фрагментах.

Диалекты и подобные языки (например, Modula2 и Pascal) также опасны.

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

Обратите внимание, что это также удобный способ разрешить пользователям подключать "известные" ключевые слова для увеличения коэффициента обнаружения, например, предоставление идентификаторов подпрограмм или типов библиотеки времени выполнения.

Ответ 13

Нет никакого способа сделать это надежным, но я бы лично начал с операторов, так как они в большинстве случаев "установлены в камне" (я не могу сказать, что это относится ко всем языкам, поскольку я знаю только ограниченный набор). Это значительно сократило бы его, но не было бы достаточно. Например, "- > " используется на многих языках (по крайней мере, C, С++ и Perl).

Я бы пошел на что-то вроде этого:

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

Например: Некоторые языки имеют строки, начинающиеся с символа "#", включая C, С++ и Perl. Другие, чем первые два, используют #include и #define в своем словаре? Если вы обнаружите этот символ в начале строки, возможно, это один из них. Если символ находится в середине строки, язык, скорее всего, Perl.

Кроме того, если вы найдете шаблон: = это сократит его до некоторых возможных языков.

Etc.

У меня была бы двумерная таблица с найденными языками и шаблонами, и после анализа я бы просто подсчитал, какой язык имеет большинство "хитов". Если бы я хотел, чтобы это было действительно умно, я бы дал каждой функции вес, который бы означал, насколько вероятно или маловероятно, что эта функция включена в фрагмент этого языка. Например, если вы можете найти фрагмент, который начинается с /* и заканчивается на */, более вероятно, что это либо C, либо С++.

Проблема с ключевыми словами заключается в том, что кто-то может использовать ее как обычную переменную или даже внутри комментариев. Они могут использоваться в качестве решающего фактора (например, слово "класс" гораздо более вероятно в С++, чем C, если все остальное равно), но вы не можете полагаться на них.

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

Ответ 14

В ответ на 2: если есть "#!" и имя переводчика в самом начале, то вы определенно знаете, на каком языке он есть. (Не могу поверить, что это никому не упоминалось.)