Complete the parsing of the steps file (but no code generation yet)

This commit is contained in:
Emmanuel Briot 2014-02-14 15:20:36 +01:00
parent de799205d9
commit 29668421fc
4 changed files with 336 additions and 22 deletions

276
src/bdd-codegen.adb Normal file
View File

@ -0,0 +1,276 @@
-----------------------------------------------------------------------------
-- G N A T C O L L --
-- --
-- Copyright (C) 2014, AdaCore --
-- --
-- This library is free software; you can redistribute it and/or modify it --
-- under terms of the GNU General Public License as published by the Free --
-- Software Foundation; either version 3, or (at your option) any later --
-- version. This library is distributed in the hope that it will be useful, --
-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. --
-- --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception, --
-- version 3.1, as published by the Free Software Foundation. --
-- --
-- You should have received a copy of the GNU General Public License and --
-- a copy of the GCC Runtime Library Exception along with this program; --
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
-- <http://www.gnu.org/licenses/>. --
-- --
------------------------------------------------------------------------------
with Ada.Text_IO; use Ada.Text_IO;
with GNAT.Regpat; use GNAT.Regpat;
with GNATCOLL.Utils; use GNATCOLL.Utils;
package body BDD.Codegen is
Cst_Procedure : constant String := "procedure ";
Cst_Procedure_Re : constant Pattern_Matcher := Compile
("procedure\s+"
& "([\w+]+)" -- group 1: name of the procedure
& "\s*"
& "(\(" -- group 2: paramter list, including surrounding parens
& "(.*?)" -- group 3: list of parameters
& "\))?;", -- end of group 2
Case_Insensitive or Single_Line);
Cst_Package_Re : constant Pattern_Matcher :=
Compile ("^package ([_\.\w]+)", Case_Insensitive);
Cst_Comment_Re : constant Pattern_Matcher :=
Compile ("--\s*@(given|then|when)\s+");
function Trim (S : String) return String;
-- Trim all whitespaces (including ASCII.LF and ASCII.CR, as opposed to
-- what Ada.Strings.Fixed does) at both ends of S
procedure Check_Steps
(Self : in out Steps_Finder'Class;
File : Virtual_File);
-- Check whether File contains any step definition, and register those.
-- The pattern looks for "Step_Regexp"
procedure Parse_Subprogram_Def
(Contents : String;
Package_Name : String;
Regexp : String_Access;
Pos : in out Integer);
-- Parse a single procedure definition (the one starting just after Pos)
-- Pos is left after the declaration (if the latter could
-- be parsed).
type Param_Description is record
Name : GNAT.Strings.String_Access;
Of_Type : GNAT.Strings.String_Access;
end record;
type Param_List is array (Natural range <>) of Param_Description;
type Param_List_Access is access all Param_List;
----------
-- Free --
----------
procedure Free (Self : in out Steps_Finder) is
begin
Unchecked_Free (Self.Files);
end Free;
----------
-- Trim --
----------
function Trim (S : String) return String is
First : Integer := S'First;
Last : Integer := S'Last;
begin
while First <= S'Last and then Is_Whitespace (S (First)) loop
First := First + 1;
end loop;
while Last >= S'First and then Is_Whitespace (S (Last)) loop
Last := Last - 1;
end loop;
return S (First .. Last);
end Trim;
--------------------------
-- Parse_Subprogram_Def --
--------------------------
procedure Parse_Subprogram_Def
(Contents : String;
Package_Name : String;
Regexp : String_Access;
Pos : in out Integer)
is
Matches : Match_Array (0 .. 3);
Subprogram : String_Access;
Colon : Integer;
List : Param_List_Access;
Expected_Params : Natural;
begin
Put_Line ("MANU Found " & Regexp.all);
-- Check this is a valid regular expression
begin
declare
Re : constant Pattern_Matcher := Compile (Regexp.all);
begin
Expected_Params := Paren_Count (Re);
end;
exception
when Expression_Error =>
Put_Line
(Standard_Error,
"Error: invalid regular expression for step '"
& Regexp.all & "'");
return;
end;
Skip_Blanks (Contents, Pos);
if Pos > Contents'Last
or else not Starts_With
(Contents (Pos .. Contents'Last), Cst_Procedure)
then
Put_Line
(Standard_Error,
"Error: The step definition for '" & Regexp.all & "' must be"
& " following immediately by its subprogram");
return;
end if;
Match (Cst_Procedure_Re, Contents, Matches, Data_First => Pos);
if Matches (1) = No_Match then
Put_Line
(Standard_Error,
"Could not find name of subprogram for '" & Regexp.all & "'");
return;
end if;
Pos := Matches (0).Last;
Subprogram := new String'
(Package_Name & '.'
& Contents (Matches (1).First .. Matches (1).Last));
Put_Line ("MANU in subprogram " & Subprogram.all);
-- Parse the list of parameters
if Matches (3) /= No_Match then
declare
Params : String_List_Access := Split
(Contents (Matches (3).First .. Matches (3).Last),
On => ';');
begin
List := new Param_List (Params'Range);
for P in Params'Range loop
declare
D : String renames Params (P).all;
begin
Colon := Find_Char (D, ':');
if Colon > D'Last then
Put_Line
(Standard_Error,
"Error while parsing subprogram declaration for step '"
& Regexp.all & "'");
return;
end if;
List (P) :=
(Name => new String'(Trim (D (D'First .. Colon - 1))),
Of_Type => new String'(Trim (D (Colon + 1 .. D'Last))));
Put_Line ("MANU Param Name=" & List (P).Name.all
& " Type=" & List (P).Of_Type.all);
end;
end loop;
Free (Params);
end;
else
List := new Param_List (1 .. 0);
end if;
if List'Length /= Expected_Params then
Put_Line
(Standard_Error,
"Mismatch between the number of parenthesis in the regexp and the"
& " subprogram parameters for step '" & Regexp.all & "'");
return;
end if;
end Parse_Subprogram_Def;
-----------------
-- Check_Steps --
-----------------
procedure Check_Steps
(Self : in out Steps_Finder'Class;
File : Virtual_File)
is
pragma Unreferenced (Self);
Contents : String_Access := File.Read_File;
Pos : Integer;
Matches : Match_Array (0 .. 1);
Last : Integer;
Pack : String_Access;
Regexp : String_Access;
begin
if Contents /= null then
Pos := Contents'First;
-- Find the start of the package
Match (Cst_Package_Re, Contents.all, Matches, Data_First => Pos);
if Matches (1) = No_Match then
return;
end if;
Pack := new String'
(Contents (Matches (1).First .. Matches (1).Last));
while Pos <= Contents'Last loop
Match (Cst_Comment_Re, Contents.all, Matches, Data_First => Pos);
exit when Matches (0) = No_Match;
Pos := Matches (0).Last;
Skip_Blanks (Contents.all, Pos);
Last := EOL (Contents (Pos .. Contents'Last));
Regexp := new String'(Contents (Pos .. Last - 1));
Pos := Last + 1; -- After ASCII.LF
Parse_Subprogram_Def (Contents.all, Pack.all, Regexp, Pos);
end loop;
Free (Contents);
end if;
end Check_Steps;
--------------------
-- Discover_Steps --
--------------------
procedure Discover_Steps
(Self : in out Steps_Finder;
Extension : Filesystem_String := ".ads";
Directory : GNATCOLL.VFS.Virtual_File)
is
Files : File_Array_Access := Directory.Read_Dir_Recursive
(Extension => Extension, Filter => Files_Only);
begin
if Files /= null then
for F in Files'Range loop
Check_Steps (Self, File => Files (F));
end loop;
Unchecked_Free (Files);
end if;
end Discover_Steps;
end BDD.Codegen;

43
src/bdd-codegen.ads Normal file
View File

@ -0,0 +1,43 @@
-----------------------------------------------------------------------------
-- G N A T C O L L --
-- --
-- Copyright (C) 2014, AdaCore --
-- --
-- This library is free software; you can redistribute it and/or modify it --
-- under terms of the GNU General Public License as published by the Free --
-- Software Foundation; either version 3, or (at your option) any later --
-- version. This library is distributed in the hope that it will be useful, --
-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. --
-- --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception, --
-- version 3.1, as published by the Free Software Foundation. --
-- --
-- You should have received a copy of the GNU General Public License and --
-- a copy of the GCC Runtime Library Exception along with this program; --
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
-- <http://www.gnu.org/licenses/>. --
-- --
------------------------------------------------------------------------------
-- Support for code generation (detecting user-defined steps,...)
package BDD.Codegen is
type Steps_Finder is tagged private;
procedure Free (Self : in out Steps_Finder);
-- Free memory used by Self
procedure Discover_Steps
(Self : in out Steps_Finder;
Extension : Filesystem_String := ".ads";
Directory : GNATCOLL.VFS.Virtual_File);
-- Find all Ada files that contain step definitions
private
type Steps_Finder is tagged record
Files : GNATCOLL.VFS.File_Array_Access;
end record;
end BDD.Codegen;

View File

@ -36,24 +36,12 @@ package body BDD.Runner is
Extension : Filesystem_String := ".feature";
Directory : GNATCOLL.VFS.Virtual_File := Create_From_Base ("features"))
is
Files : File_Array_Access;
Files : File_Array_Access := Directory.Read_Dir_Recursive
(Extension => Extension, Filter => Files_Only);
begin
if Directory.Is_Directory then
Files := Directory.Read_Dir (Files_Only);
if Files /= null then
for F in Files'Range loop
if Files (F).File_Extension = Extension then
Self.Register (Files (F));
end if;
end loop;
Unchecked_Free (Files);
Files := Directory.Read_Dir (Dirs_Only);
for F in Files'Range loop
if Files (F).Base_Name /= "."
and then Files (F).Base_Name /= ".."
then
Self.Discover (Extension, Files (F));
end if;
Self.Register (Files (F));
end loop;
Unchecked_Free (Files);
end if;

View File

@ -1,9 +1,16 @@
-- This subprogram generates the test driver by including all the
-- step definitions provided by the user, as well as the predefined
-- steps, regular epressions and mockups.
with BDD.Codegen; use BDD.Codegen;
with GNATCOLL.VFS; use GNATCOLL.VFS;
procedure Main is
Finder : Steps_Finder;
begin
-- This subprogram generates the test driver by including all the
-- step definitions provided by the user, as well as the predefined
-- steps, regular epressions and mockups.
null;
Discover_Steps
(Finder,
Extension => ".ads",
Directory => Create_From_Base ("features/step_definitions"));
end Main;