Извлечение структур C в отдельные файлы с помощью регулярных выражений

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

Я хочу извлечь все структуры и поместить их в отдельные файлы с помощью PowerShell Regex.

Я написал следующий скрипт:

$file = Get-Content -Path "./types.h"
Write-Output $file
$structs = Select-String -Pattern "struct\s*([a-zA-Z0-9_]+)\s*{(?:\s*.*\s*)*?}\s*;" -InputObject $file
Write-Output $structs.Matches.Count
foreach($struct in $structs.Matches) {
    New-Item "./$($struct.groups[1]).h" -Force -Value $struct.Value
}

У меня есть следующий тестовый файл types.h:

struct foo {

    int a;
};

struct bar{

    short i[200];
};

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

struct foo {    int a; };  struct bar{      short i[200]; };

Я ожидал бы два файла foo.h и bar.h с соответствующими структурами в них.

Мое регулярное выражение должно работать (regex101):

Разве так не должно быть foreach($struct in $structs)? Однако я тестировал со строковой переменной, а не с файлом, т. е. $structs = [regex]::Matches($types, "struct\s*([a-zA-Z0-9_]+)\s*{(?:.*\s*)*?}\s*;")

Wiktor Stribiżew 21.06.2024 09:56

В PowerShell для этого есть парсер (PowerShell) AST, я предполагаю, что C имеет что-то подобное...

iRon 21.06.2024 10:29

Я предлагаю вам иметь еще пару образцов. Например, struct внутри struct, а также union внутри struct. Мне любопытно, что произойдет, если у вас есть struct внутри union.

Richard Chambers 22.06.2024 14:12
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
3
85
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

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

param (
    [string]$SourceFile,
    [string]$OutputPath
)
$structs = Get-Content -Path $SourceFile -Raw | Select-String -List -Pattern "(?:struct|enum)\s*([a-zA-Z0-9_]+)\s*{(?:\s*.*?\s*)*?}\s*;" -AllMatches
foreach($struct in $structs.Matches) {
    Write-Output $struct.Value
    New-Item "$($OutputPath)/$($struct.groups[1].Value.ToLower().Replace('_','')).h" -Force -Value @"
#ifndef $($struct.groups[1].Value.ToUpper())_INCLUDED
#define $($struct.groups[1].Value.ToUpper())_INCLUDED
$($struct.Value)
#endif
"@
}

Это работает для простого примера, который вы представили, но он сломается, как только вы учтете такие вещи, как {/} в комментариях, вложенные блоки объединения и т. д.

Mathias R. Jessen 21.06.2024 10:48

ты прав. но это подходит для моего варианта использования, и, честно говоря, я никогда не видел скобок {} в комментариях (если только структура/класс не была закомментирована)

Raildex 21.06.2024 12:58

Вам не просто не хватало переключателя -AllMatches, вам также нужно было сопоставить все строки, что требовало от вас передачи всего содержимого файла в виде одной строки в Select-String через Get-Content -Raw .... Обратите внимание, что -List можно опустить, поскольку он имеет смысл только при передаче нескольких входные строки.

mklement0 21.06.2024 19:49

Определение структуры C не определяется регулярной грамматикой, поэтому его нельзя проанализировать с помощью регулярного выражения.

Вы используете .* в своем регулярном выражении, что позволяет выполнять синтаксический анализ так, как вы его определяете (вы разрешаете, чтобы } сопровождался { снова внутри него). Здесь у вас есть две альтернативы, обе из которых могут потерпеть неудачу. Вы можете использовать [^}]* вместо .*, что приведет к сбою (поскольку он запрещает анализ вложенных struct, заканчиваясь на первом найденном }, вырезая остальные, если это произойдет, у вас есть определение вложенной структуры), но будет правильно анализировать невложенные структуры, Или вы можете создать регулярное выражение (гораздо более сложное), которое допускает максимальное количество определений вложенных структур, но этот контекст не позволяет мне привести пример такого подхода.

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

Для более общего решения, использующего парсер CFG, используйте Antlr4 , грамматику C , Trash и pwsh или bash. Недостатком является то, что для установки требуется немного.

Шаг 1. Клонируйте репозиторий грамматики.

$ git clone https://github.com/antlr/grammars-v4.git
Cloning into 'grammars-v4'...
remote: Enumerating objects: 50583, done.
remote: Counting objects: 100% (1872/1872), done.
remote: Compressing objects: 100% (1265/1265), done.
remote: Total 50583 (delta 669), reused 1599 (delta 495), pack-reused 48711
Receiving objects: 100% (50583/50583), 47.49 MiB | 23.16 MiB/s, done.
Resolving deltas: 100% (27090/27090), done.
Updating files: 100% (9413/9413), done.

Шаг 2. Создайте синтаксический анализатор для C.

$ cd grammars-v4/c
$ trgen -t CSharp
CSharp  C.g4 success 0.0555443
Rendering template file from CSharp/Other.csproj to ./Generated-CSharp/Other.csproj
Rendering template file from CSharp/st.build.ps1 to ./Generated-CSharp/st.build.ps1
Rendering template file from CSharp/st.build.sh to ./Generated-CSharp/st.build.sh
Rendering template file from CSharp/st.clean.ps1 to ./Generated-CSharp/st.clean.ps1
Rendering template file from CSharp/st.clean.sh to ./Generated-CSharp/st.clean.sh
Rendering template file from CSharp/st.Encodings.cs to ./Generated-CSharp/st.Encodings.cs
Rendering template file from CSharp/st.ErrorListener.cs to ./Generated-CSharp/st.ErrorListener.cs
Rendering template file from CSharp/st.makefile to ./Generated-CSharp/st.makefile
Rendering template file from CSharp/st.perf.sh to ./Generated-CSharp/st.perf.sh
Rendering template file from CSharp/st.ProfilingCommonTokenStream.cs to ./Generated-CSharp/st.ProfilingCommonTokenStream.cs
Rendering template file from CSharp/st.run.ps1 to ./Generated-CSharp/st.run.ps1
Rendering template file from CSharp/st.run.sh to ./Generated-CSharp/st.run.sh
Rendering template file from CSharp/st.test-cover.sh to ./Generated-CSharp/st.test-cover.sh
Rendering template file from CSharp/st.Test.cs to ./Generated-CSharp/st.Test.cs
Rendering template file from CSharp/st.test.ps1 to ./Generated-CSharp/st.test.ps1
Rendering template file from CSharp/st.test.sh to ./Generated-CSharp/st.test.sh
Rendering template file from CSharp/Test.csproj.st to ./Generated-CSharp/Test.csproj.st
Copying source file from C:/msys64/home/Kenne/temp/grammars-v4/c/desc.xml to ./Generated-CSharp/desc.xml
Copying source file from C:/msys64/home/Kenne/temp/grammars-v4/c/C.g4 to ./Generated-CSharp/C.g4
$ cd Generated-CSharp/
$ make
bash build.sh
  Determining projects to restore...
  Restored C:\msys64\home\Kenne\temp\grammars-v4\c\Generated-CSharp\Test.csproj (in 526 ms).
  Determining projects to restore...
  All projects are up-to-date for restore.
  Test -> C:\msys64\home\Kenne\temp\grammars-v4\c\Generated-CSharp\bin\Debug\net8.0\Test.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:04.56

Шаг 3. Запустите сценарий для извлечения имен типов, затем извлеките типы в файлы с соответствующими именами.

$ cat extract.sh
#!/bin/bash

g=`trparse types.h | trxgrep ' //structOrUnionSpecifier/Identifier/text()' | tr '\n' ' ' | tr -d '\r'`
for f in $g
do
        trparse types.h | trxgrep " //structOrUnionSpecifier[Identifier = '$f']" | trtext > $f.h
done

$ cat types.h


// This is foo.
struct foo {

        int a;
};

// This is bar.
struct bar{

        short i[200];
};
$ bash extract.sh
CSharp 0 types.h success 0.0539358
CSharp 0 types.h success 0.0550352
CSharp 0 types.h success 0.0597384
$ cat foo.h


// This is foo.
struct foo {

        int a;
}
$ cat bar.h


// This is bar.
struct bar{

        short i[200];
}
$

Обратите внимание, что сценарий, эквивалентный Powershell:

$g = trparse types.h | trxgrep ' //structOrUnionSpecifier/Identifier/text()' | Out-String
$g = $g -replace "\n", " "
$g = $g -replace "\r", ""

$g.Trim().Split(" ") | ForEach-Object {
    $f = $_
    trparse types.h | trxgrep " //structOrUnionSpecifier[Identifier = '$f']" | trtext | Out-File "$f.h"
}

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