Использование FsLex/Yacc в Vs2013

Я пытаюсь воскресить старый проект F# parser, с которым я работал в vs 2008 для работы с vs 2013. Он использует FsLexYacc.

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

fslex --unicode "$(ProjectDir)XpathLexer.fsl"
fsyacc --module XpathParser "$(ProjectDir)XpathParser.fsy"

Но это не идеально, так как всегда выполняется независимо от того, изменились ли входы.

Затем я попытался просто использовать старые действия MsBuild:

<FsYacc Include="XpathParser.fsy">
<FsLex Include="XpathLexer.fsl">

но они, похоже, полностью игнорировались в процессе сборки. Это правильно? Были ли эти задачи сборки удалены?

Затем я обнаружил некоторые вещи, документированные под vs C++, которые, как я думал, могли бы работать:

<CustomBuild Include="XpathParser.fsy">
  <Message>Calling FsYacc</Message>
  <Command>fsyacc --module XpathParser "$(ProjectDir)XpathParser.fsy"</Command>
  <Outputs>$(ProjectDir)XpathParser.fs</Outputs>
</CustomBuild>

а также

<PropertyGroup>
    <CustomBuildBeforeTargets>CoreCompile</CustomBuildBeforeTargets>
</PropertyGroup>

(Я проверил файл Microsoft.Fsharp.Targets для определения цели "CoreCompile".)

Увы, до сих пор нет сигар.

Кто-нибудь может пролить свет на то, действительно ли возможно правильно интегрировать fslex/yacc в решение vs 2013, и если да, то как?

3 ответа

Я не думаю, что эти инструменты включены по умолчанию в компилятор F#, который устанавливается вместе с Visual Studio, и поэтому задачи не существуют. Я сделал следующее с проектом Visual Studio 2012, но я ожидаю, что он будет похож на VS 2013. Вот шаги, которые я должен был выполнить:

  1. Установите FSharp.Powerpack от nuget. Здесь есть инструменты fslex и fsyacc, а также задачи и цели сборки.
  2. Выгрузите проект и отредактируйте файл.fsproj.
  3. Добавьте оператор импорта для файла FSharp.Powerpack.target. Это добавит CallFsLex а также CallFsYacc строить цели. Я добавил это после импорта для Microsoft.FSharp.targets:
    <Import Project="$(ProjectDir)\..\packages\FSPowerPack.Community.3.0.0.0\Tools\FSharp.PowerPack.targets" />

  4. Добавьте эти три свойства в основную группу PropertyGroup в верхней части файла:<FsYaccToolPath>..\packages\FSPowerPack.Community.3.0.0.0\Tools</FsYaccToolPath> <FsLexToolPath>..\packages\FSPowerPack.Community.3.0.0.0\Tools</FsLexToolPath> <FsLexUnicode>true</FsLexUnicode> Это сообщает задачам сборки, где найти необходимые инструменты, и устанавливает опцию Unicode для fslex.

  5. Чтобы использовать цели, которые мы импортировали, вам нужно определить группы элементов FsLex и FsYacc с используемыми входными файлами. Вам также необходимо добавить элементы Compile для выходных файлов.fs. В итоге вы получите что-то вроде этого в разделе ItemGroup:
    <Compile Include="Sql.fs" />
    <FsYacc Include="SqlParser.fsp">
    <Module>SqlParser</Module>
    </FsYacc>
    <Compile Include="SqlParser.fsi" />
    <Compile Include="SqlParser.fs" />
    <FsLex Include="SqlLexer.fsl" />
    <Compile Include="SqlLexer.fs" />

Вы можете использовать задачи сборки FsLex и FsYacc напрямую, ссылаясь на файл FSharp.Powerpack.Build.Tasks.dll, но для меня это было проще.

Вот что у меня работает (Windows 7 x64, Visual Studio 2013 Ultimate RTM):

  1. Загрузите и установите "PowerPack для FSharp 3.0 + .NET 4.x + VS2012" из CodePlex ( https://fsharppowerpack.codeplex.com/downloads/get/625449).

  2. Создайте следующий ключ реестра: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\AssemblyFolders\FSharp.PowerPack-1.9.9.9 (для 64-разрядных версий Windows опустите Wow6432Node для 32-битных версий) и установите его (Default) значение для установочного каталога F# PowerPack (например, "C:\Program Files (x86)\FSharpPowerPack-4.0.0.0\bin"). [Это связано с давней / регрессионной ошибкой в src/FSharp.PowerPack/CompilerLocationUtils.fs что в основном нарушает обнаружение инструмента.]

  3. Импортируйте цели PowerPack (ПОСЛЕ импорта целей F#) в ваш файл *.fsproj: <Import Project="$(MSBuildExtensionsPath32)\FSharp\1.0\FSharp.PowerPack.targets" />

  4. Обновите свой ItemGroup Узел что-то вроде этого (используйте FsYacc соответственно):

    <ItemGroup>
      <None Include="App.config" />
      <FsLex Include="Lexer.fsl" />
      <Compile Include="Lexer.fs">
        <Visible>False</Visible>
      </Compile>
      <Compile Include="Program.fs" />
    </ItemGroup>
    
  5. Включить ссылку на FSharp.PowerPack.dll и построить.

Вы должны получить файл *.fsproj, подобный следующему:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>8c565f99-d6bc-43a9-ace9-eadfe429c0f7</ProjectGuid>
    <OutputType>Exe</OutputType>
    <RootNamespace>FsYaccTest</RootNamespace>
    <AssemblyName>FsYaccTest</AssemblyName>
    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <TargetFSharpCoreVersion>4.3.1.0</TargetFSharpCoreVersion>
    <Name>FsYaccTest</Name>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
  <!-- Snip -->
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="FSharp.PowerPack">
      <HintPath>C:\Program Files (x86)\FSharpPowerPack-4.0.0.0\bin\FSharp.PowerPack.dll</HintPath>
    </Reference>
    <Reference Include="mscorlib" />
    <Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
      <Private>True</Private>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Numerics" />
  </ItemGroup>
  <PropertyGroup>
    <MinimumVisualStudioVersion Condition="'$(MinimumVisualStudioVersion)' == ''">11</MinimumVisualStudioVersion>
  </PropertyGroup>
  <Choose>
    <When Condition="'$(VisualStudioVersion)' == '11.0'">
      <PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets')">
        <FSharpTargetsPath>$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets</FSharpTargetsPath>
      </PropertyGroup>
    </When>
    <Otherwise>
      <PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets')">
        <FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets</FSharpTargetsPath>
      </PropertyGroup>
    </Otherwise>
  </Choose>
  <Import Project="$(FSharpTargetsPath)" />  
  <Import Project="$(MSBuildExtensionsPath32)\FSharp\1.0\FSharp.PowerPack.targets" />  
  <PropertyGroup>
    <FsLexUnicode>true</FsLexUnicode>
  </PropertyGroup>
  <ItemGroup>
    <None Include="App.config" />
    <FsLex Include="Lexer.fsl" />
    <Compile Include="Lexer.fs">
      <Visible>False</Visible>
    </Compile>
    <Compile Include="Program.fs" />
  </ItemGroup>
</Project>

Примечание: вы, вероятно, можете опустить создание ключа реестра, если вы предоставите FsYaccToolPath как описано в ответе Майка З.

Похоже, что это работает - по крайней мере, по моему опыту, если вы используете отдельный пакет nuget FsLexYacc, как описано здесь, а затем добавляете следующее в файл fsproj (извлеченный из примера github):

Рядом со всем другим импортом:

<Import Project="..\packages\FsLexYacc.6.0.4\bin\FsLexYacc.targets" />

и т. д.

а затем для исходных файлов:

<FsYacc Include="Parser.fsp">
  <OtherFlags>--module SqlParser</OtherFlags>
</FsYacc>
<FsLex Include="Lexer.fsl">
  <OtherFlags>--unicode</OtherFlags>
</FsLex>

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

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