
%% This file (rule_interpreter) contains predicates  
%% that serve the application of two level rules to 
%% surface/lexical segments in (non_incremental)
%% analysis and generation. 

%% The predicates specifically geared to _incremental_
%% analsyis are given in a different file. 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% APPLY_A_RULE:
%
% This is the most basic predicate applying rules to 
% surface/lexical segment sequences. Its role is to 
% advance the current state of processing by a single 
% rule application. Its input and output data structures
% are each a `CONFIG', basically just a list of all the bits of 
% information that go to make up a `configuration' at any
% stage in processing. What the elements of a config are, 
% glossed for the "In" config at the beginning of the 
% apply_a_rule/2 predicate, just following. 


apply_a_rule(In,Out):- 
   In  = [Count,       % Records number of surface segments so far chomped through in
                       % left to right processing. Only needed for incremental analysis. 
          Ptr,         % Current `state' in traversing the lexical FSM. 
          Sdone,       % Surface segments so far addressed in left to right processing.      
          Srest,       % Surface segments still to be processed. 
          Ldone,       % Lexical segments so far addressed in left to right processing.   
          Lrest,       % Lexical segments still to be processed.
          Partition],  % Partial partition, recording rule applications so far. 
	  
   Out = [New_Count,
          New_Ptr,
          New_Sdone,
          New_Srest,
          New_Ldone,
          New_Lrest,
          New_Partition], 
    
    twolevelrule(Name,_Status,              % try a two level rule
                 Sdone,Surf,New_Srest,             % left contexts matched immediately by 
		 Ldone,Lex, New_Lrest,             % simple unification (rule contexts have 
		 Conditions),                      % variable tails. 
		 
    append(Surf,New_Srest,Srest),                  % surface + right surf context match
    append(Lex, New_Lrest,Lrest),                  % Lex and right lex context match
    traverse_lex_fsm(Lex,Ptr,New_Ptr),             % Can move through Lexical FSM
    call(Conditions),                              % rule conditions satisfied 
    reverse(Surf,Rsurf),                           
    append(Rsurf,Sdone,New_Sdone),                 % Rest of predicate, constructing 
    reverse(Lex,Rlex),                             % elements of the new config. 
    append(Rlex, Ldone,New_Ldone), 
    length(Surf,N), 
    New_Count is Count + N,
    append(Partition,[[Name,Surf,Lex]],New_Partition). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% apply_rules/2:
% Iteratively applies the one-step predicate apply_rule/2
% to an input config until a completed configuration is 
% reached, i.e. where the surface and lexical sequences 
% are completely consumed, and the `pointer' to the lexicon
% is the "initial" state (which will be the case if the last
% lexical segment consumed is the boundary marker). Also 
% applies the check that the `obligatory' requirement of 
% any <=> rules has not be violated. 
%
% This rule only used for _non_incremental_ analysis and
% generation. A different version (incremental_apply_rules/3)
% is given elsewhere for incremental analysis. 

apply_rules(Out,Out):-               % finished, if: 
   Out = [_N,initial,_,[],_,[],_],   % Ptr = initial, Srest and LRest sequences empty. 
   obligatory_check_config(Out).     % Check for violation of obligatory 
                                     % requirementof <=> rules. 

apply_rules(In,Out):-         
   apply_a_rule(In,New),        % apply a single rule to config. 
   apply_rules(New,Out).        

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% The following flag set specifies what is the 
% start_state for traversing the lexical FSM. (May want to 
% vary this, e.g. if want to require that lexical sequences 
% must all start with a boundary marker rather than an
% ordinary segment.) 

:- set_flag(start_state,initial). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% PARTITION/3:
%
% Construct a two level mapping between surface and lexical 
% segment sequences.

partition(Surf,Lex,Partition):- 
   flag(start_state,START),           % Look up the start state. 
   IN = [0,START,[],Surf,[],Lex,[]],  % Construct initial config.  
   apply_rules(IN,OUT),               % Construct a two level mapping. 
   OUT = [_,_,_,_,_,_,Partition].     % Get partition from final config. 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% ANALYSIS/1: <segmentlist>
%
% Find all possible answers for analysing the input 
% (by failure driven loop on apply_rules/2). 

analysis(Surf):-  
   zero_num(answernum),              % Zero counter for number of answers found. 
   abolish(last_analysis_answer/2),  % Erase record of previous answers. 
   macro_eval_list(Surf,Surf1),      % Macro expand input. 
   write('Surface sequence: '), write(Surf), nl,  
   (\+(Surf = Surf1)                                  % Print result of macro expansion
         -> (nl, write('Macro evaluation gives: '),
	     print_segment_list(Surf1))
	  ; true), 
   fail_drive((partition(Surf1,Lex,Partition),      % Construct a two level mapping.
               nl, write('Lexical sequence: '), 
	       print_segment_list(Lex),             % Print answer. 
	       print_partition(Partition), 
               new_num(answernum,Num),              % Increment counter
	       assert(last_analysis_answer(Num,Partition))  % record answer. 
	       )), 
   (last_analysis_answer(1,_)           % Print warning if no answers found. 
         -> true 
	  ; (nl, write('No answers'), nl)). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% GENERATION/1: <segmentlist>
%
% Find all possible answers for generating from the input 
% (by failure driven loop on apply_rules/2). 

generation(Lex):- 
   zero_num(answernum),                 % Zero counter for number of answers found. 
   abolish(last_generation_answer/2),   % Erase record of previous answers. 
   macro_eval_list(Lex,Lex1),           % Macro expand input. 
   write('Lexical sequence: '), write(Lex), nl,  
   (\+(Lex = Lex1)                                  % Print result of macro expansion
           -> (nl, write('Macro evaluation gives: '), 
               print_segment_list(Lex1))
	    ; true), 
   fail_drive((partition(Surf,Lex1,Partition),      % Construct a two level mapping.
               nl, write('Surface sequence: '), 
	       print_segment_list(Surf),            % Print answer. 
	       print_partition(Partition), 
               new_num(answernum,Num),              % Increment counter
	       assert(last_generation_answer(Num,Partition))  % record answer. 
	       )), 
   (last_generation_answer(1,_)        % Print warning if no answers found. 
         -> true  
	  ; (nl, write('No answers'), nl)).  

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% OBLIGATORY_CHECK: <partition>
%
% Check that a partition does not violate the 
% obligatory requirement of any <=> rules. 

% obligatory_check/1:

obligatory_check(Partition):- 
   departition(Partition,Surf,Lex),   % extract surf and lex sequences
                                      % from the partition. 
   obligatory_check([],Surf,[],Lex,Partition).   % call on obligatory_check/5. 

% obligatory_check/5: 

obligatory_check(_,_,_,_,[]).            % finished. 

obligatory_check(SurfDone,
                 SurfRest,
                 LexDone, 
		 LexRest,
	         [[Name1,PartSurf,Lex]|Partition]):- 
  append(PartSurf,NewSurfRest,SurfRest),      % Extract surface right context
  append(Lex,NewLexRest,LexRest),             % Extract lexical right context

  \+((twolevelrule(Name2,oblig,    % Ensure that there is no rule such that:                  
                   SurfDone,         %  surf left context matches, 
		   RuleSurf,
		   NewSurfRest,      %  surf right context matches, 
                   LexDone,          %  lexical left context matches, 
		   Lex,              %  lexical part of sequence pair matches, 
		   NewLexRest,       %  lexical right context matches, 
                   Conditions), 
      \+( Name1 = Name2),            %  rule is not the one that licensed sequence pair
                                     %  (note 1 user rule may give >1 compiled rules),
      call(Conditions),              %  postconditions are satisfied, 
      \+( PartSurf = RuleSurf ))),   %  BUT the surface sequences DON'T MATCH. 
      
  reverse(PartSurf,RevPartSurf),               % Build arguments for recursion
  append(RevPartSurf,SurfDone,NewSurfDone),
  reverse(Lex,RevLex),
  append(RevLex,LexDone,NewLexDone),
  obligatory_check(NewSurfDone,NewSurfRest,    % Recurse. 
                   NewLexDone,NewLexRest,
                   Partition).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% OBLIGATORY_CHECK_CONFIG: <config>
%
% Check that the partition within a config does not 
% violate the obligatory requirement of any <=> rules. 

obligatory_check_config(Config):- 
   reverse(Config,[Partition|_]),    % Get partition from config.
   obligatory_check(Partition).      % Do the check. 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Extract the Surface and Lexical sequences from a partition. 

departition([],[],[]). 
departition([[_,S,L]|Partition],Surf,Lex):- 
   departition(Partition,Surf2,Lex2), 
   append(S,Surf2,Surf), 
   append(L,Lex2,Lex). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
