### ----------------------------------------------------
### -- Marpa
### -- Licenses: Artistic-2.0
### -- Authors: 
### -- File: lib/Marpa.rakumod
### ----------------------------------------------------
### -- Chunk 1 of 4
use NativeCall;

=begin pod

=head1 NAME

Marpa - Raku interface to the libmarpa C library.

=head1 SYNOPSIS

=begin code :lang<raku>

use Marpa;

=end code

=head1 DESCRIPTION

Marpa is a Raku interface to the libmarpa C library.

Marpa requires libmarpa to be present. I'd recommend installing from
packages, or just look on the libmarpa website for install instructions.

=head1 AUTHOR

Jeffrey Goff, aka DrForr

Source can be located at: https://github.com/raku-community-modules/Marpa .
Comments and Pull Requests are welcome.

=head1 COPYRIGHT AND LICENSE

Copyright 2015 - 2018 Jeffrey Goff

Copyright 2019 - 2022 Raku Community

This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.

=end pod

class Marpa:ver<1.0.1>:auth<zef:raku-community-modules> {
  my constant LIB = 'libmarpa.so.8';

  my enum Marpa-Error-Code <
    MARPA-ERR-NONE
    MARPA-ERR-AHFA-IX-NEGATIVE
    MARPA-ERR-AHFA-IX-OOB
    MARPA-ERR-ANDID-NEGATIVE
    MARPA-ERR-ANDID-NOT-IN-OR
    MARPA-ERR-ANDIX-NEGATIVE
    MARPA-ERR-BAD-SEPARATOR
    MARPA-ERR-BOCAGE-ITERATION-EXHAUSTED
    MARPA-ERR-COUNTED-NULLABLE
    MARPA-ERR-DEVELOPMENT
    MARPA-ERR-DUPLICATE-AND-NODE
    MARPA-ERR-DUPLICATE-RULE
    MARPA-ERR-DUPLICATE-TOKEN
    MARPA-ERR-YIM-COUNT
    MARPA-ERR-YIM-ID-INVALID
    MARPA-ERR-EVENT-IX-NEGATIVE
    MARPA-ERR-EVENT-IX-OOB
    MARPA-ERR-GRAMMAR-HAS-CYCLE
    MARPA-ERR-INACCESSIBLE-TOKEN
    MARPA-ERR-INTERNAL
    MARPA-ERR-INVALID-AHFA-ID
    MARPA-ERR-INVALID-AIMID
    MARPA-ERR-INVALID-BOOLEAN
    MARPA-ERR-INVALID-IRLID
    MARPA-ERR-INVALID-NSYID
    MARPA-ERR-INVALID-LOCATION
    MARPA-ERR-INVALID-RULE-ID
    MARPA-ERR-INVALID-START-SYMBOL
    MARPA-ERR-INVALID-SYMBOL-ID
    MARPA-ERR-I-AM-NOT-OK
    MARPA-ERR-MAJOR-VERSION-MISMATCH
    MARPA-ERR-MICRO-VERSION-MISMATCH
    MARPA-ERR-MINOR-VERSION-MISMATCH
    MARPA-ERR-NOOKID-NEGATIVE
    MARPA-ERR-NOT-PRECOMPUTED
    MARPA-ERR-NOT-TRACING-COMPLETION-LINKS
    MARPA-ERR-NOT-TRACING-LEO-LINKS
    MARPA-ERR-NOT-TRACING-TOKEN-LINKS
    MARPA-ERR-NO-AND-NODES
    MARPA-ERR-NO-EARLEY-SET-AT-LOCATION
    MARPA-ERR-NO-OR-NODES
    MARPA-ERR-NO-PARSE
    MARPA-ERR-NO-RULES
    MARPA-ERR-NO-START-SYMBOL
    MARPA-ERR-NO-TOKEN-EXPECTED-HERE
    MARPA-ERR-NO-TRACE-YIM
    MARPA-ERR-NO-TRACE-YS
    MARPA-ERR-NO-TRACE-PIM
    MARPA-ERR-NO-TRACE-SRCL
    MARPA-ERR-NULLING-TERMINAL
    MARPA-ERR-ORDER-FROZEN
    MARPA-ERR-ORID-NEGATIVE
    MARPA-ERR-OR-ALREADY-ORDERED
    MARPA-ERR-PARSE-EXHAUSTED
    MARPA-ERR-PARSE-TOO-LONG
    MARPA-ERR-PIM-IS-NOT-LIM
    MARPA-ERR-POINTER-ARG-NULL
    MARPA-ERR-PRECOMPUTED
    MARPA-ERR-PROGRESS-REPORT-EXHAUSTED
    MARPA-ERR-PROGRESS-REPORT-NOT-STARTED
    MARPA-ERR-RECCE-NOT-ACCEPTING-INPUT
    MARPA-ERR-RECCE-NOT-STARTED
    MARPA-ERR-RECCE-STARTED
    MARPA-ERR-RHS-IX-NEGATIVE
    MARPA-ERR-RHS-IX-OOB
    MARPA-ERR-RHS-TOO-LONG
    MARPA-ERR-SEQUENCE-LHS-NOT-UNIQUE
    MARPA-ERR-SOURCE-TYPE-IS-AMBIGUOUS
    MARPA-ERR-SOURCE-TYPE-IS-COMPLETION
    MARPA-ERR-SOURCE-TYPE-IS-LEO
    MARPA-ERR-SOURCE-TYPE-IS-NONE
    MARPA-ERR-SOURCE-TYPE-IS-TOKEN
    MARPA-ERR-SOURCE-TYPE-IS-UNKNOWN
    MARPA-ERR-START-NOT-LHS
    MARPA-ERR-SYMBOL-VALUED-CONFLICT
    MARPA-ERR-TERMINAL-IS-LOCKED
    MARPA-ERR-TOKEN-IS-NOT-TERMINAL
    MARPA-ERR-TOKEN-LENGTH-LE-ZERO
    MARPA-ERR-TOKEN-TOO-LONG
    MARPA-ERR-TREE-EXHAUSTED
    MARPA-ERR-TREE-PAUSED
    MARPA-ERR-UNEXPECTED-TOKEN-ID
    MARPA-ERR-UNPRODUCTIVE-START
    MARPA-ERR-VALUATOR-INACTIVE
    MARPA-ERR-VALUED-IS-LOCKED
    MARPA-ERR-RANK-TOO-LOW
    MARPA-ERR-RANK-TOO-HIGH
    MARPA-ERR-SYMBOL-IS-NULLING
    MARPA-ERR-SYMBOL-IS-UNUSED
    MARPA-ERR-NO-SUCH-RULE-ID
    MARPA-ERR-NO-SUCH-SYMBOL-ID
    MARPA-ERR-BEFORE-FIRST-TREE
    MARPA-ERR-SYMBOL-IS-NOT-COMPLETION-EVENT
    MARPA-ERR-SYMBOL-IS-NOT-NULLED-EVENT
    MARPA-ERR-SYMBOL-IS-NOT-PREDICTION-EVENT
    MARPA-ERR-RECCE-IS-INCONSISTENT
    MARPA-ERR-INVALID-ASSERTION-ID
    MARPA-ERR-NO-SUCH-ASSERTION-ID
    MARPA-ERR-HEADERS-DO-NOT-MATCH
    MARPA-ERR-NOT-A-SEQUENCE
  >;

  my constant MARPA-MAJOR-VERSION = 8;
  my constant MARPA-MINOR-VERSION = 3;
  my constant MARPA-MICRO-VERSION = 0;

  my enum Marpa-Event-Code <
    MARPA-EVENT-NONE
    MARPA-EVENT-COUNTED-NULLABLE
    MARPA-EVENT-EARLEY-ITEM-THRESHOLD
    MARPA-EVENT-EXHAUSTED
    MARPA-EVENT-LOOP-RULES
    MARPA-EVENT-NULLING-TERMINAL
    MARPA-EVENT-SYMBOL-COMPLETED
    MARPA-EVENT-SYMBOL-EXPECTED
    MARPA-EVENT-SYMBOL-NULLED
    MARPA-EVENT-SYMBOL-PREDICTED
  >;

  my enum Marpa-Step-Code <
    MARPA-STEP-INTERNAL1
    MARPA-STEP-RULE
    MARPA-STEP-TOKEN
    MARPA-STEP-NULLING-SYMBOL
    MARPA-STEP-TRACE
    MARPA-STEP-INACTIVE
    MARPA-STEP-INTERNAL2
    MARPA-STEP-INITIAL
  >;

  my class g                   is repr('CPointer') { }

  my class Marpa-Assertion-ID  is repr('CPointer') { }
  my class Marpa-Bocage        is repr('CPointer') { }
  my class Marpa-Config        is repr('CPointer') { }
  my class Marpa-Earleme       is repr('CPointer') { }
  my class Marpa-Earley-Set-ID is repr('CPointer') { }
  #my class Marpa-Error-Code    is repr('CPointer') { }
  my class Marpa-Event         is repr('CPointer') { }
  my class Marpa-Grammar       is repr('CPointer') { }
  my class Marpa-Order         is repr('CPointer') { }
  my class Marpa-Rank          is repr('CPointer') { }
  my class Marpa-Recognizer    is repr('CPointer') { }
  my class Marpa-Rule-ID       is repr('CPointer') { }
  my class Marpa-Symbol-ID     is repr('CPointer') { }
  my class Marpa-Step-Type     is repr('CPointer') { }
  my class Marpa-Tree          is repr('CPointer') { }
  my class Marpa-Value         is repr('CPointer') { }

  #|{ Checks that the Marpa library in use is compatible with the given version. Generally you will pass in the constants MARPA-MAJOR-VERSION, MARPA-MINOR-VERSION, MARPA-MICRO-VERSION as the three arguments to this function; that produces a check that the library in use is compatible with the version of Libmarpa the application or module was compiled against. Compatibility is defined by two things: first the version of the running library is newer than the version required_major.required_minor.required_micro. Second the running library must be binary compatible with the version required_major.required_minor.required_micro (same major version.)

      Success value: MARPA-ERR-NONE if the Marpa library is compatible with the requested version.
      Failure value: If the library is not compatible, one of the following is returned, indicating the nature of the mismatch:

        MARPA-ERR-MAJOR-VERSION-MISMATCH,
        MARPA-ERR-MINOR-VERSION-MISMATCH
        MARPA-ERR-MICRO-VERSION-MISMATCH 
    }
  my sub marpa_check_version
         ( Int $required_major, Int $required_minor, Int $required_micro )
         returns Marpa-Error-Code
         is native( LIB ) { * }
  method check-version
         ( Int $required-major, Int $required-minor, Int $required-micro )
         returns Marpa-Error-Code {
           marpa_check_version( $required-major, $required-minor, $required-micro ) }

  #|{ Returns the version number in version, which must have room for three int's.

      Success value: A non-negative number.
      Failure value: −2.
    }
  my sub marpa_version
         ( Pointer[Int] $version )
         returns Marpa-Error-Code
         is native( LIB ) { * }
  method version
         ( Pointer[Int] $version )
         returns Marpa-Error-Code {
           marpa_version( $version ) }

  #|{ Initialize the config information to “safe” default values. Unspecified behavior will result if an initialized configuration is used to create a grammar.

      Success value: A non-negative value.
      Failure value: Always succeeds.
    }
  my sub marpa_c_init
         ( Pointer[Marpa-Config] $config )
         returns Int
         is native( LIB ) { * }
  method c-init
         ( Pointer[Marpa-Config] $config )
         returns Int {
           marpa_c_init( $config ) }

  #|{ Error codes are usually kept in the base grammar, which leaves marpa_g_new() no place to put its error code on failure. Objects of the Marpa-Config class provide such a place. p_error_string is reserved for use by the internals. Applications should set it to NULL.

      Success value: The error code in config.
      Failure value: Always succeeds.
    }
  my sub marpa_c_error
         ( Pointer[Marpa-Config] $config, Pointer[Str] $p_error_string )
         returns Marpa-Error-Code
         is native( LIB ) { * }
  method c-error
         ( Pointer[Marpa-Config] $config, Pointer[Str] $p-error-string )
         returns Marpa-Error-Code {
           marpa_c_error( $config, $p-error-string ) }

  #|{ Creates a new grammar time object. The returned grammar object is not yet precomputed, and will have no symbols and rules. Its reference count will be 1.

      Unless the application calls marpa_c_error, Libmarpa will not reference the location pointed to by the configuration argument after marpa_g_new returns. The configuration argument may be NULL, but if it is, there will be no way to determine the error code on failure.

      Success value: the grammar object.
      Failure value: NULL, and the error code is set in configuration. 
    }
  my sub marpa_g_new
         ( Pointer[Marpa-Config] $configuration )
         returns g
         is native( LIB ) { * }
  method g-new
         ( Pointer[Marpa-Config] $configuration )
         returns g {
           marpa_g_new( $configuration ) }

  #|{ It is recommended that this call be made immediately after the grammar constructor. It turns off a deprecated feature.

      The marpa_g_force_valued forces all the symbols in a grammar to be “valued”. The opposite of a valued symbol is one about whose value you do not care. This distinction has been made in the past in hope of gaining efficiencies at evaluation time. Current thinking is that the gains do not repay the extra complexity.

      Success value: a non-negative integer.
      Failure value: a negative integer. 
    }
  my sub marpa_g_force_valued
         ( g $g )
         returns Int
         is native( LIB ) { * }
  method g-force-valued
         ( g $g )
         returns Int {
           marpa_g_force_valued( $g ) }

  #|{ Increases the reference count by 1. Not needed by most applications.

      Success value: the grammar object it was called with.
      Failure value: NULL.
    }
  my sub marpa_g_ref
         ( Marpa-Grammar $g )
         returns g
         is native( LIB ) { * }
  method g-ref
         ( Marpa-Grammar $g )
         returns g {
           marpa_g_ref( $g ) }

  #|{ Decreases the reference count by 1, destroying g once the reference count reaches zero. 
    }
  my sub marpa_g_unref
         ( g $g )
         is native( LIB ) { * }
  method g-unref
         ( g $g ) {
           marpa_g_unref( $g ) }
        
  #|{ Returns current value of the start symbol of grammar g. The value is that specified in the marpa_g_start_symbol_set() call, if there has been one.

      Success value: The new start symbol.
      Failure value: -1 if there is no start symbol, otherwise -2.
    }
  my sub marpa_g_start_symbol
         ( g $g )
         returns Marpa-Symbol-ID
         is native( LIB ) { * }
  method g-start-symbol
         ( g $g )
         returns Marpa-Symbol-ID {
           marpa_g_start_symbol( $g ) }

  #|{ Sets the start symbol of grammar g to symbol id.

      Success value: The ID of the new start symbol.
      Failure value: -1 if sym_id is well-formed but there is no such symbol, otherwise -2.
    }
  my sub marpa_g_start_symbol_set
         ( g $g, Marpa-Symbol-ID $sym_id )
         returns Marpa-Symbol-ID
         is native( LIB ) { * }
  method g-start-symbol-set
         ( g $g, Marpa-Symbol-ID $sym-id )
         returns Marpa-Symbol-ID {
           marpa_g_start_symbol_set( $g, $sym-id ) }

  #|{ Success value: the numerically largest symbol ID of $g.
      Failure value: -2.
    }
  my sub marpa_g_highest_symbol_id
         ( g $g )
         returns Int
         is native( LIB ) { * }
  method g-highest-symbol-id
         ( g $g )
         returns Int {
           marpa_g_highest_symbol_id( $g ) }

  #|{ A symbol is accessible if it can be reached from the start symbol.

      Success value: 1 if symbol sym_id is accessible, 0 if not. If sym_id is well-formed, but there is no such symbol, −1. If the grammar is not precomputed, or on other failure, −2. 
      Failure value: -2.
    }
  my sub marpa_g_symbol_is_accessible
         ( g $g, Marpa-Symbol-ID $sym_id )
         returns Int
         is native( LIB ) { * }
  method g-symbol-is-accessible
         ( g $g, Marpa-Symbol-ID $sym-id )
         returns Int {
           marpa_g_symbol_is_accessible( $g, $sym-id ) }

  #|{ A symbol is nullable if it sometimes produces the empty string. A nulling symbol is always a nullable symbol, but not all nullable symbols are nulling symbols.

      Success value: 1 if symbol sym_id is nullable, 0 if not. If sym_id is well-formed, but there is no such symbol, −1. If the grammar is not precomputed, or on other failure, −2. 
      Failure value: -2.
    }
  my sub marpa_g_symbol_is_nullable
         ( g $g, Marpa-Symbol-ID $sym_id )
         returns Int
         is native( LIB ) { * }
  method g-symbol-is-nullable
         ( g $g, Marpa-Symbol-ID $sym-id )
         returns Int {
           marpa_g_symbol_is_nullable( $g, $sym-id ) }

  #|{ A symbol is nulling if it always produces the empty string.

      Success value: 1 if symbol sym_id is nulling, 0 if not. If sym_id is well-formed, but there is no such symbol, −1. If the grammar is not precomputed, or on other failure, −2. 
      Failure value: -2.
    }
  my sub marpa_g_symbol_is_nulling
         ( g $g, Marpa-Symbol-ID $sym_id )
         returns Int
         is native( LIB ) { * }
  method g-symbol-is-nulling
         ( g $g, Marpa-Symbol-ID $sym-id )
         returns Int {
           marpa_g_symbol_is_nulling( $g, $sym-id ) }

  #|{ A symbol is productive if it can produce a string of terminals. All nullable symbols are considered productive.

      Success value: 1 if symbol sym_id is productive, 0 if not. If sym_id is well-formed, but there is no such symbol, −1. If the grammar is not precomputed, or on other failure, −2. 
      Failure value: -2.
    }
  my sub marpa_g_symbol_is_productive
         ( g $g, Marpa-Symbol-ID $sym_id )
         returns Int
         is native( LIB ) { * }
  method g-symbol-is-productive
         ( g $g, Marpa-Symbol-ID $sym-id )
         returns Int {
           marpa_g_symbol_is_productive( $g, $sym-id ) }

  #|{ This return value of this call indicates whether sym_id is the start symbol.

      Success value: 1 if sym_id is the start symbol.
                     0 if sym_id is not the start symbol, either because the start symbol is different from sym_id, or because the start symbol has not been set yet.
      Failure value: -1 if sym_id is well-formed, but there is no such symbol.
                     -2 on other failure.
    }
  my sub marpa_g_symbol_is_start
         ( g $g, Marpa-Symbol-ID $sym_id )
         returns Int
         is native( LIB ) { * }
  method g-symbol-is-start
         ( g $g, Marpa-Symbol-ID $sym-id )
         returns Int {
           marpa_g_symbol_is_start( $g, $sym-id ) }

  #|{ These methods, respectively, set and query the “terminal status” of a symbol. To be used as an input symbol in the marpa_r_alternative() method, a symbol must be a terminal. This function flags symbol sym_id as a terminal if value is 1, or flags it as a non-terminal if value is 0.

      Once set to a value with the marpa_g_symbol_is_terminal_set() method, the terminal status of a symbol is “locked” at that value. A subsequent call to marpa_g_symbol_is_terminal_set() that attempts to change the terminal status of sym_id to a value different from its current one will fail. The error code will be MARPA-ERR-TERMINAL-IS-LOCKED.

      By default, a symbol is a terminal if and only if it does not appear on the LHS of any rule. An attempt to flag a nulling symbol as a terminal will cause a failure, but this is not necessarily detected before precomputation.

      Success returns: When successful, these methods return 1 if symbol sym_id is a terminal symbol after the call, 0 otherwise.

      Failure returns: If sym_id is well-formed, but there is no such symbol, −1. On all other failures, −2. Other failures include when value is not 0 or 1; when the terminal status is locked and value is different from its current value; and, in the case of marpa_g_symbol_is_terminal_set(), when the grammar g is precomputed. 
    }
  my sub marpa_g_symbol_is_terminal_set
         ( g $g, Marpa-Symbol-ID $sym_id, Int $value )
         returns Int
         is native( LIB ) { * }
  method g-symbol-is-terminal-set
         ( g $g, Marpa-Symbol-ID $sym-id, Int $value )
         returns Int {
           marpa_g_symbol_is_terminal_set( $g, $sym-id, $value ) }
  my sub marpa_g_symbol_is_terminal
         ( g $g, Marpa-Symbol-ID $sym_id )
         returns Int
         is native( LIB ) { * }
  method g-symbol-is-terminal
         ( g $g, Marpa-Symbol-ID $sym-id )
         returns Int {
           marpa_g_symbol_is_terminal( $g, $sym-id ) }

  #|{ Creates a new symbol. The symbol ID will be a non-negative integer.

      Success value: The ID of a new symbol.
      Failure value: −2. 
    }
  my sub marpa_g_symbol_new
         ( g $g )
         returns Marpa-Symbol-ID
         is native( LIB ) { * }
  method g-symbol-new
         ( g $g )
         returns Marpa-Symbol-ID {
           marpa_g_symbol_new( $g ) }

  #|{ Success value: The ID of the numerically largest rule ID of $g.
      Failure value: −2. 
    }
  my sub marpa_g_highest_rule_id
         ( g $g )
         returns Int
         is native( LIB ) { * }
  method g-highest-rule-id
         ( g $g )
         returns Int {
           marpa_g_highest_rule_id( $g ) }

  #|{ A rule is accessible if it can be reached from the start symbol. A rule is accessible if and only if its LHS symbol is accessible. The start rule is always an accessible rule.

      Success value: 1 if rule rule_id is accessible, 0 if not. If rule_id is well-formed, but there is no such rule, −1. If the grammar is not precomputed, or on other failure, −2. 
      Failure value: −2. 
    }
  my sub marpa_g_rule_is_accessible
         ( g $g, Marpa-Rule-ID $rule_id )
         returns Int
         is native( LIB ) { * }
  method g-rule-is-accessible
         ( g $g, Marpa-Rule-ID $rule-id )
         returns Int {
           marpa_g_rule_is_accessible( $g, $rule-id ) }

  #|{ A rule is nullable if it sometimes produces the empty string. A nulling rule is always a nullable rule, but not all nullable rules are nulling rules.

      Success value: 1 if rule ruleid is nullable, 0 if not. If rule_id is well-formed, but there is no such rule, −1. If the grammar is not precomputed, or on other failure, −2.
      Failure value: −2. 
    }
  my sub marpa_g_rule_is_nullable
         ( g $g, Marpa-Rule-ID $ruleid )
         returns Int
         is native( LIB ) { * }
  method g-rule-is-nullable
         ( g $g, Marpa-Rule-ID $ruleid )
         returns Int {
           marpa_g_rule_is_nullable( $g, $ruleid ) }

  #|{ A rule is nulling if it always produces the empty string.

      Success value: 1 if rule ruleid is nulling, 0 if not. If rule_id is well-formed, but there is no such rule, −1. If the grammar is not precomputed, or on other failure, −2. 
      Failure value: −2. 
    }
  my sub marpa_g_rule_is_nulling
         ( g $g, Marpa-Rule-ID $ruleid )
         returns Int
         is native( LIB ) { * }
  method g-rule-is-nulling
         ( g $g, Marpa-Rule-ID $ruleid )
         returns Int {
           marpa_g_rule_is_nulling( $g, $ruleid ) }

  #|{ A rule is a loop rule if it non-trivially produces the string of length one which consists only of its LHS symbol. Such a derivation takes the parse back to where it started, hence the term “loop”. “Non-trivially” means the zero-step derivation does not count — the derivation must have at least one step.

      The presence of a loop rule makes a grammar infinitely ambiguous, and applications will typically want to treat them as fatal errors. But nothing forces an application to do this, and Marpa will successfully parse and evaluate grammars with loop rules.

      Success value: 1 if rule rule_id is a loop rule, 0 if not. If rule_id is well-formed, but there is no such rule, −1. If the grammar is not precomputed, or on other failure, −2. 
      Failure value: −2. 
    }
  my sub marpa_g_rule_is_loop
         ( g $g, Marpa-Rule-ID $rule_id )
         returns Int
         is native( LIB ) { * }
  method g-rule-is-loop
         ( g $g, Marpa-Rule-ID $rule-id )
         returns Int {
           marpa_g_rule_is_loop( $g, $rule-id ) }

  #|{ A rule is productive if it can produce a string of terminals. An rule is productive if and only if all the symbols on its RHS are productive. The empty string counts as a string of terminals, so that a nullable rule is always a productive rule. For that same reason, an empty rule is considered productive.

      Success value: 1 if rule rule_id is productive, 0 if not. If rule_id is well-formed, but there is no such rule, −1. If the grammar is not precomputed, or on other failure, −2. 
      Failure value: −2. 
    }
  my sub marpa_g_rule_is_productive
         ( g $g, Marpa-Rule-ID $rule_id )
         returns Int
         is native( LIB ) { * }
  method g-rule-is-productive
         ( g $g, Marpa-Rule-ID $rule-id )
         returns Int {
           marpa_g_rule_is_productive( $g, $rule-id ) }

  #|{ The length of a rule is the number of symbols on its RHS.

      Success value: The length of rule $rule_id.
      Failure value: −2. 
    }
  my sub marpa_g_rule_length
         ( g $g, Marpa-Rule-ID $rule_id )
         returns Int
         is native( LIB ) { * }
  method g-rule-length
         ( g $g, Marpa-Rule-ID $rule-id )
         returns Int {
            marpa_g_rule_length( $g, $rule-id ) }

  #|{ Success value: The LHS symbol of rule rule_id. If rule_id is well-formed, but there is no such rule, −1.
      Failure value: −2. 
    }
  my sub marpa_g_rule_lhs
         ( g $g, Marpa-Rule-ID $rule_id )
         returns Marpa-Symbol-ID
         is native( LIB ) { * }
  method g-rule-lhs
         ( g $g, Marpa-Rule-ID $rule-id )
         returns Marpa-Symbol-ID {
           marpa_g_rule_lhs( $g, $rule-id ) }

  #|{ Creates a new external BNF rule in grammar g. The ID of the new rule will be a non-negative integer, which will be unique to that rule. In addition to BNF rules, Marpa also allows sequence rules, which are created by another method: marpa_g_sequence_new(). Sequence rules and BNF rules are numbered in the same series, so that a BNF rule will never have the same rule ID as a sequence rule, and vice versa.

      The LHS symbol is lhs_id, and there are length symbols on the RHS. The RHS symbols are in an array pointed to by rhs_ids.

      Possible failures, with their error codes, include:

          MARPA-ERR-SEQUENCE-LHS-NOT-UNIQUE: The LHS symbol is the same as that of a sequence rule.
          MARPA-ERR-DUPLICATE-RULE: The new rule would duplicate another BNF rule. Another BNF rule is considered the duplicate of the new one, if its LHS symbol is the same as symbol lhs_id, if its length is the same as length, and if its RHS symbols match one for one those in the array of symbols rhs_ids. 

      Success value: the ID of new external rule.
      Failure value: −2. 
    }
  my sub marpa_g_rule_new
         ( g $g, Marpa-Symbol-ID $lhs_id, Pointer[Marpa-Symbol-ID] $rhs_ids, Int $length )
         returns Marpa-Rule-ID
         is native( LIB ) { * }
  method g-rule-new
         ( g $g, Marpa-Symbol-ID $lhs-id, Pointer[Marpa-Symbol-ID] $rhs-ids, Int $length )
         returns Marpa-Rule-ID {
           marpa_g_rule_new( $g, $lhs-id, $rhs-ids, $length ) }

  #|{ Returns the ID of the symbol in position ix in the RHS of rule rule_id. The RHS position, ix, is zero-based.

      Success value: The ID of the symbol in position ix on the rules RHS. If rule_id is well-formed, but there is no such rule, −1. If ix is greater than or equal to the length of the rule, or on other failure, −2. 
      Failure value: −2. 
    }
  my sub marpa_g_rule_rhs
         ( g $g, Marpa-Rule-ID $rule_id, int $ix )
         returns Marpa-Symbol-ID
         is native( LIB ) { * }
  method g-rule-rhs
         ( g $g, Marpa-Rule-ID $rule-id, int $ix )
         returns Marpa-Symbol-ID {
           marpa_g_rule_rhs( $g, $rule-id, $ix ) }

  #|{ Success returns: marpa_g_rule_is_proper_separation() succeeds if and only if rule_id is valid. If rule rule_id is a sequence rule where the proper separation flag is set, returns 1. On other success, including the case where rule rule_id is not a sequence rule, returns 0.

      Failure returns: If rule_id is well-formed, but there is no such rule, returns −1. On other failure, −2. 
    }
  my sub marpa_g_rule_is_proper_separation
         ( g $g, Marpa-Rule-ID $rule_id )
         returns Int
         is native( LIB ) { * }
  method g-rule-is-proper-separation
         ( g $g, Marpa-Rule-ID $rule-id )
         returns Int {
           marpa_g_rule_is_proper_separation( $g, $rule-id ) }

  #|{ Returns the mininum length of a sequence rule. This accessor can be also be used to test whether or not a rule is a sequence rule. −1 is returned if and only if the rule is valid but not a sequence rule.

      Success value: If rule rule_id is a sequence rule, its minimum length. If rule rule_id is valid, but is not the rule ID of sequence rule, −1. If rule_id is well-formed, but there is no such rule, or on other failure, −2. 
      Failure value: −2. 
    }
  my sub marpa_g_sequence_min
         ( g $g, Marpa-Rule-ID $rule_id )
         returns Int
         is native( LIB ) { * }
  method g-sequence-min
         ( g $g, Marpa-Rule-ID $rule-id )
         returns Int {
           marpa_g_sequence_min( $g, $rule-id ) }

  #|{ Adds a new sequence rule to grammar g. The ID of the new sequence rule will be a non-negative integer, which is unique to that rule. Sequence rules and BNF rules are numbered in the same series, so that a BNF rule will never have the same rule ID as a sequence rule, and vice versa.

      The sequence is lhs_id, and the item to be repeated in the sequence is rhs_id. The sequence must be repeated at least min times, where min is 0 or 1. If separator_id is non-negative, it is a separator symbol.

      If flags & MARPA-PROPER-SEPARATION is non-zero, separation is “proper”, that is, a trailing separator is not allowed. The term proper is based on the idea that properly-speaking, separators should actually separate items.

      Some higher-level Marpa interfaces offer the ability to discard separators in the semantics, and in fact will do this by default. At the Libmarpa level, sequences always “keep separators”. It is up to the programmer to arrange to discard separators, if that is what is desired.

      The sequence RHS, or item, is restricted to a single symbol, and that symbol cannot be nullable. If separator_id is a symbol, it also cannot be a nullable symbol. Nullables on the RHS of sequences are restricted because they lead to highly ambiguous grammars. Grammars of this kind are allowed by Libmarpa, but they must be expressed using BNF rules, not sequence rules. This is for two reasons: First, sequence optimizations would not work in the presence of nullables. Second, since it is not completely clear what an application intends when it asks for a sequence of identical items, some of which are nullable, the user's intent can be more clearly expressed directly in BNF.

      The LHS symbol cannot be the LHS of any other rule, whether a BNF rule or a sequence rule. On an attempt to create an sequence rule with a duplicate LHS, marpa_g_sequence_new() fails, setting the error code to MARPA-ERR-SEQUENCE-LHS-NOT-UNIQUE.

      Sequence rules do not add to the classes of grammar parsed by Libmarpa — a sequence can always be written as BNF rules. When a rule is created with the marpa_g_sequence_new() method, Libmarpa will understand that it is a sequence, and will optimize accordingly. The speedup is often considerable.

      Success value: The ID of the external rule.
      Failure value: −2. 
    }
  my sub marpa_g_sequence_new
         ( g $g, Marpa-Symbol-ID $lhs_id, Marpa-Symbol-ID $rhs_id, Marpa-Symbol-ID $separator_id, Int $min, Int $flags )
         returns Marpa-Rule-ID
         is native( LIB ) { * }
  method g-sequence-new
         ( g $g, Marpa-Symbol-ID $lhs-id, Marpa-Symbol-ID $rhs-id, Marpa-Symbol-ID $separator-id, Int $min, Int $flags )
         returns Marpa-Rule-ID {
           marpa_g_sequence_new( $g, $lhs-id, $rhs-id, $separator-id, $min, $flags ) }

  #|{ Success value: If rule rule_id is a sequence rule, its separator. If rule rule_id is a sequence rule, but there is no