/-
Copyright (c) 2025 Harmonic. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
-/
import Langlib.Grammars.Unrestricted.Definition
import Langlib.Classes.ContextFree.Definition
import Langlib.Classes.Regular.Definition

/-! # Linear Languages

This file defines the class of linear languages. A linear grammar is a context-free
grammar in which every production has **at most one** nonterminal on the right-hand
side. Equivalently, every rule has the form `A → w` or `A → u B v` where `u`, `v`
are terminal strings and `B` is a nonterminal.

Linear languages sit strictly between the regular and the context-free languages
in the Chomsky hierarchy:

    Regular ⊊ Linear ⊊ Context-Free

## Main declarations

- `linear_output`    — Predicate: a symbol string has at most one nonterminal.
- `grammar_linear`   — Predicate: an unrestricted grammar is linear.
- `is_Linear`        — A language is linear.
- `Linear`           — The class of linear languages.
-/

variable {T : Type}

/-- A symbol string has **linear output form** if it contains at most one nonterminal.
Concretely, it is either a purely terminal string or can be decomposed as
`map terminal u ++ [nonterminal B] ++ map terminal v`. -/
def linear_output {N : Type} (s : List (symbol T N)) : Prop :=
  (∀ x ∈ s, ∃ t, x = symbol.terminal t) ∨
  (∃ (u : List T) (B : N) (v : List T),
    s = List.map symbol.terminal u ++ [symbol.nonterminal B] ++ List.map symbol.terminal v)

/-- An unrestricted grammar is **linear** if every rule is context-free
(empty left/right context) and has linear output. -/
def grammar_linear (g : grammar T) : Prop :=
  ∀ r ∈ g.rules,
    r.input_L = [] ∧ r.input_R = [] ∧ linear_output r.output_string

/-- A language is **linear** if it is generated by some linear unrestricted grammar. -/
def is_Linear (L : Language T) : Prop :=
  ∃ g : grammar T, grammar_linear g ∧ grammar_language g = L

/-- The class of linear languages. -/
def Linear : Set (Language T) :=
  setOf is_Linear
