Text-Treesitter

 view release on metacpan or  search on metacpan

lib/Text/Treesitter/Language.pm  view on Meta::CPAN


C<Text::Treesitter::Language> - represents a F<tree-sitter> language grammar

=head1 SYNOPSIS

Usually accessed indirectly, via C<Text::Treesitter>. Can also be used
directly.

   use Text::Treesitter::Language;

   my $language_lib = "path/to/the/tree-sitter-perl.so";

   my $lang = Text::Treesitter::Language::load( $language_lib, "perl" );

   printf "This language defines %d symbols\n", $lang->symbol_count;

=head1 DESCRIPTION

Instances of this class represent an entire language grammar specification.
Typically an application will load just one of these for the lifetime of its
operation; or at least, just one per type of language being parsed.

=cut

=head1 UTILITY FUNCTIONS

These utility functions are not exported, and must be called fully-qualified.

=cut

=head2 build

   Text::Treesitter::Language::build( $output, @dirs );

Requests that a language grammar repository directory (or several) be compiled
into an object file that can later be loaded.

=cut

# We -could- use ExtUtils::CBuilder but that's intended for building
# specifically to link against perl, and it won't cope with the C++ version
# of the final link step

use Config;
use constant CC => $Config::Config{cc};

{
   my $guess;

   my @CXX_compile;
   sub CXX_compile
   {
      return @CXX_compile if @CXX_compile;

      require ExtUtils::CppGuess;
      $guess //= ExtUtils::CppGuess->new;
      my %opts = $guess->module_build_options;

      return @CXX_compile = ( $opts{config}{cc},
         # $opts{extra_compiler_flags} might begin with a space
         split m/ +/, $opts{extra_compiler_flags} =~ s/^ +//r,
      );
   }

   my @CXX_link;
   sub CXX_link
   {
      return @CXX_link if @CXX_link;

      require ExtUtils::CppGuess;
      $guess //= ExtUtils::CppGuess->new;
      my %opts = $guess->module_build_options;

      return @CXX_link = ( $opts{config}{cc},
         # $opts{extra_linker_flags} might begin with a space
         split m/ +/, $opts{extra_linker_flags} =~ s/^ +//r,
      );
   }
}

sub _compile
{
   my ( $source ) = @_;
   my $is_cpp = $source =~ m/\.cc$/;

   my $output = $source =~ s/\.cc?$/.o/r;

   my @args = ( $is_cpp ? CXX_compile : CC,
      "-o", $output,
      "-fPIC",
      "-c", $source,
   );

   push @args, "-ggdb";

   print join( " ", @args ), "\n";
   system( @args ) == 0 or
      die "Unable to $args[0] - $?\n";

   return $output;
}

sub _link
{
   my ( $output, $is_cpp, @objects ) = @_;

   my @args = ( $is_cpp ? CXX_link : CC,
      "-o", $output,
      "-shared",
      @objects,
   );

   print join( " ", @args ), "\n";
   system( @args ) == 0 or
      die "Unable to $args[0] - $?\n";

   return $output;
}

sub build
{
   my ( $output, @dirs ) = @_;

   my $is_cpp = 0;
   my @objects;

   foreach my $dir ( @dirs ) {
      my $srcdir = "$dir/src";

      unless( -f "$srcdir/parser.c" ) {
         die "Expected a parser.c within $srcdir\n";
      }

      push @objects, _compile( "$srcdir/parser.c" );

      if( -f "$srcdir/scanner.c" ) {



( run in 0.445 second using v1.01-cache-2.11-cpan-5511b514fd6 )