Package: src/packages/grammars.fdoc

key file
grammars.flx share/lib/std/strings/grammars.flx

Grammars

Grammar

//[grammars.flx]

class Grammars {

typedef generic_gramentry_t[T] = string * T;
typedef generic_gramlib_t[T] = list[generic_gramentry_t[T]];
typedef generic_grammar_t[T] = string * generic_gramlib_t[T];

fun generic_cls[T]
  (generic_add: list[string] -> T -> list[string])
  (lib:generic_gramlib_t[T])
  (unprocessed: list[string])
  (processed:list[string])
: list[string]
=>
  match unprocessed with
  | Empty => processed
  | Cons (h,tail) =>
    if h in processed then generic_cls generic_add lib tail processed else
    match find lib h with
    | Some p =>
      let unprocessed = generic_add tail p in
      generic_cls generic_add lib unprocessed (Cons (h,processed))
    | None =>
      fun_fail[list[string]] ("MISSING NONTERMINAL " + h)
    endmatch
  endmatch
;

fun generic_closure[T]
  (generic_add: list[string] -> T -> list[string])
  (g:generic_grammar_t[T])
: list[string] =>
  match g with
  | start, lib => generic_cls generic_add lib ([start]) Empty[string]
;

// NOTE: this depends on Recognisers, but Recognisers
// depends on Grammars. BAD BAD.

typedef open_prod_t[T] =
(
  | `Terminal of string * Recognisers::recog_t
  | `Nonterminal of string
  | `Epsilon
  | `Seq of list[T]
  | `Alt of list[T]
)
;

typedef prod_t = open_prod_t[prod_t];

instance[T with Str[T]] Str[open_prod_t[T]]
{
  fun str: open_prod_t[T] -> string =
  | `Terminal (s,r) => '"' + s + '"'
  | `Nonterminal name => name
  | `Epsilon => "Eps"
  | `Seq ss => "(" + catmap " " (str of T) ss + ")"
  | `Alt ss => "[" + catmap " | " (str of T) ss + "]"
  ;
}

typedef open_gramentry_t[T] = string * open_prod_t[T];
typedef open_gramlib_t[T] = list[open_gramentry_t[T]];
typedef open_grammar_t[T] = string * open_gramlib_t[T];


typedef gramentry_t = open_gramentry_t[prod_t];
typedef gramlib_t = open_gramlib_t[prod_t];
typedef grammar_t = open_grammar_t[prod_t];

Grammar Operations

Closure

//[grammars.flx]

fun add_unique (acc:list[string]) (elt:string) : list[string] =>
  if elt in acc then acc else Cons (elt,acc)
;

fun open_add_prod[T]
  (aux: list[string] -> T -> list[string])
  (acc:list[string]) (p: open_prod_t[T])
: list[string] =>
  match p with
  | `Terminal _ => acc
  | `Nonterminal name => Cons (name, acc)
  | `Epsilon => acc
  | `Seq ps => fold_left aux acc ps
  | `Alt ps => fold_left aux acc ps
  endmatch
;

fun add_prod(acc:list[string]) (p:prod_t) : list[string] =>
  fix open_add_prod[prod_t] acc p
;

fun closure (g:grammar_t): list[string] =>
  generic_closure[prod_t] add_prod g
;

fun nullable_prod (lib:gramlib_t) (e:prod_t) (trail:list[string]) =>
  match e with
  | `Terminal _ => false
  | `Seq es => fold_left (fun (acc:bool) (sym:prod_t) =>
      acc and (nullable_prod lib sym trail)) true es

  | `Alt es => fold_left (fun (acc:bool) (sym:prod_t) =>
      acc or (nullable_prod lib sym trail)) false es

  | `Nonterminal nt => nullable_nt lib nt trail
  | `Epsilon => true
;

fun nullable_nt (lib: gramlib_t) (nt:string) (trail:list[string]) : bool =>
  if nt in trail then false else
  match find lib nt with
  | None => false
  | Some e => nullable_prod lib e (nt ! trail)
;

fun is_nullable_prod (lib:gramlib_t) (e:prod_t) =>
  nullable_prod lib e Empty[string]
;

fun is_nullable_nt (lib:gramlib_t) (nt:string) =>
  nullable_nt lib nt Empty[string]
;

fun recursive_prod (lib:gramlib_t) (e:prod_t) (orig:string) (trail:list[string]) =>
  match e with
  | `Terminal _ => false
  | `Seq es => fold_left (fun (acc:bool) (sym:prod_t) =>
      acc or (recursive_prod lib sym orig trail)) false es

  | `Alt es => fold_left (fun (acc:bool) (sym:prod_t) =>
      acc or (recursive_prod lib sym orig trail)) false es

  | `Nonterminal nt => if nt == orig then true else recursive_nt lib nt orig trail
  | `Epsilon => false
;

fun recursive_nt (lib: gramlib_t) (nt:string) (orig:string) (trail:list[string]) : bool =>
  if nt in trail then false else
  match find lib nt with
  | None => false
  | Some e => recursive_prod lib e orig (nt ! trail)
;


fun is_recursive_nt (lib:gramlib_t) (nt:string) =>
  recursive_nt lib nt nt Empty[string]
;

fun left_recursive_prod (lib:gramlib_t) (e:prod_t) (orig:string) (trail:list[string]) =>
  match e with
  | `Terminal _ => false

  | `Seq es =>
    let fun aux (es:list[prod_t]) =>
      match es with
      | Empty => false
      | Cons (head, tail) =>
        if left_recursive_prod lib head orig trail then true
        elif is_nullable_prod lib head then aux tail
        else false
      endmatch
    in
    aux es

  | `Alt es => fold_left (fun (acc:bool) (sym:prod_t) =>
      acc or (left_recursive_prod lib sym orig trail)) false es

  | `Nonterminal nt =>
    if nt == orig then true
    else left_recursive_nt lib nt orig trail

  | `Epsilon => false
;

fun left_recursive_nt (lib: gramlib_t) (nt:string) (orig:string) (trail:list[string]) : bool =>
  if nt in trail then false else
  match find lib nt with
  | None => false
  | Some e => left_recursive_prod lib e orig (nt ! trail)
;


fun is_left_recursive_nt (lib:gramlib_t) (nt:string) =>
  left_recursive_nt lib nt nt Empty[string]
;


fun unpack (fresh:1->string) (head:string, p:prod_t) : gramlib_t =
{
 var out = Empty[gramentry_t];
 match p with
 | `Epsilon => out = ([head,p]);
 | `Terminal _ => out = ([head,(`Seq ([p]) :>> prod_t)]);
 | `Nonterminal s => out= ([head,(`Seq ([p]) :>> prod_t)]);

 | `Seq ps =>
   var newseq = Empty[prod_t];
   for term in ps do
     match term with
     | `Epsilon => ;
     | `Nonterminal _ => newseq = term ! newseq;
     | `Terminal _ => newseq = term ! newseq;
     | _ =>
       var newhead = fresh();
       newseq = (`Nonterminal newhead  :>> prod_t) ! newseq;
       out = unpack fresh (newhead,term);
     endmatch;
   done

   match newseq with
   | Empty => out = (head,(#`Epsilon :>> prod_t)) ! out;
   | _ => out = (head,(`Seq (rev newseq) :>> prod_t)) ! out;
   endmatch;

 | `Alt ps =>
   iter (proc (p:prod_t) { out = unpack fresh (head,p) + out; }) ps;
 endmatch;
 return out;
}

fun normalise_lib (fresh:1->string) (lib:gramlib_t) = {
  var normalised = Empty[gramentry_t];
  for p in lib perform
    normalised = unpack fresh p + normalised;
  return normalised;
}

fun sort_merge (g:gramlib_t) : gramlib_t =>
 let fun enlt (a:gramentry_t, b:gramentry_t) : bool => a.0 < b.0 in
 merge (sort enlt g)
;

fun merge (var p:gramlib_t): gramlib_t =
{
 if p.len == 0uz return p;

 var out: gramlib_t;

 var key: string;
 var alts = Empty[prod_t];
 var cur: gramentry_t;

 proc fetch() {
   match p with
   | Cons (head,tail) => cur = head; p = tail;
   | Empty => assert false;
   endmatch;
 }

 proc dohead() { key = cur.0; alts = Empty[prod_t]; }
 proc dofoot() { out = (key,(`Alt alts :>> prod_t)) ! out;  }
 proc dobreak() { dofoot; dohead; }
 proc check() { if key != cur.0 call dobreak; }

 fetch;
 dohead;
 while p.len > 0uz do
   check;
   alts = cur.1 ! alts;
   fetch;
 done
 check;
 alts = cur.1 ! alts;
 dofoot;
 return out;
}

} // class Grammar