packages feed

distributors-0.4.0.0: test/Examples/SExpr.hs

module Examples.SExpr
  ( SExpr (..)
  , sexprGrammar
  , sexprExamples
  ) where

import Control.Lens hiding (List)
import Control.Lens.Grammar hiding (List)

-- | Abstract syntax tree for S-expressions
data SExpr
  = Atom String       -- ^ Atomic symbol
  | List [SExpr]      -- ^ List of S-expressions
  deriving stock (Eq, Ord, Show, Read)

-- Generate prisms
makePrisms ''SExpr

-- | Grammar for S-expressions
sexprGrammar :: Grammar Char SExpr
sexprGrammar = ruleRec "sexpr" $ \sexpr -> choice
  [ _Atom >? atomG
  , _List >? listG sexpr
  ]
  where
    -- Atom: one or more alphanumeric or symbol characters
    atomG = rule "atom" $ someP (tokenClass atomChars)

    -- List: parenthesized sequence of S-expressions
    -- Elements are separated by whitespace
    listG sexpr = rule "list" $
      terminal "(" >* several (sepWith " ") sexpr *< terminal ")"

    -- Characters allowed in atoms: letters, digits, and symbols
    atomChars =
      oneOf (['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ "_+-*/<>=!?")

-- | Example S-expressions for testing
sexprExamples :: [(SExpr, String)]
sexprExamples =
  [ (Atom "foo", "foo")
  , (Atom "x", "x")
  , (Atom "+", "+")
  , (Atom "define", "define")
  , (List [], "()")
  , (List [Atom "foo"], "(foo)")
  , (List [Atom "foo", Atom "bar"], "(foo bar)")
  , (List [Atom "foo", List [Atom "bar", Atom "baz"]],
     "(foo (bar baz))")
  , (List [Atom "define", List [Atom "square", Atom "x"],
           List [Atom "*", Atom "x", Atom "x"]],
     "(define (square x) (* x x))")
  , (List [Atom "+", Atom "1", Atom "2"], "(+ 1 2)")
  , (List [Atom "if", Atom "test",
           List [Atom "then-branch"],
           List [Atom "else-branch"]],
     "(if test (then-branch) (else-branch))")
  ]