packages feed

symantic-parser-0.1.0.20210201: test/Parser/Nandlang.hs

{-# LANGUAGE DeriveLift #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UnboxedTuples #-}
module Parser.Nandlang where

import Data.Bool
import Data.Char (isSpace, isAlpha, isAlphaNum)
import Data.Char (Char)
import Data.Eq (Eq(..))
import Data.Ord (Ord(..))
import Data.String (String)
import qualified Data.Set as Set
import qualified Data.Text as Text

import Symantic.Univariant.Trans
import qualified Symantic.Parser as P
import qualified Symantic.Parser.Haskell as H

type Parser a = P.Parser Text.Text a

nandIdentStart :: Char -> Bool
nandIdentStart c = isAlpha c || c == '_'

nandIdentLetter :: Char -> Bool
nandIdentLetter c = isAlphaNum c || c == '_'

nandUnreservedName :: String -> Bool
nandUnreservedName = \s -> not (Set.member s keys)
  where
  keys = Set.fromList ["if", "else", "while", "function", "var"]

nandStringLetter :: Char -> Bool
nandStringLetter c = (c /= '"') && (c /= '\\') && (c > '\026')

nandlang :: forall repr.
  P.Grammar Char repr =>
  repr ()
nandlang = whitespace P.*> P.skipMany funcdef P.<* P.eof
  where
  index :: repr ()
  index = brackets nat
  identifier :: repr ()
  identifier = P.try (identStart P.*> P.skipMany identLetter) P.*> whitespace
  variable :: repr ()
  variable = identifier P.*> P.optional index

  literal :: repr ()
  literal = bit P.<|> charLit

  keyword :: String -> repr ()
  keyword s = P.try (P.string s P.*> notIdentLetter) P.*> whitespace

  identStart = P.satisfy
    [P.ErrorItemLabel "identStart"]
    (trans (H.ValueCode nandIdentStart [||nandIdentStart||]))
  identLetter = P.satisfy
    [P.ErrorItemLabel "identLetter"]
    (trans (H.ValueCode nandIdentLetter [||nandIdentLetter||]))
  notIdentLetter = P.negLook identLetter

  bit :: repr ()
  bit = (P.char '0' P.<|> P.char '1') P.*> whitespace

  nat :: repr ()
  nat = decimal

  charLit :: repr ()
  charLit = P.between (P.char '\'') (symbol '\'') charChar

  charChar :: repr ()
  charChar = P.void (P.satisfy
    [P.ErrorItemLabel "Char"]
    (trans (H.ValueCode nandStringLetter [||nandStringLetter||]))) P.<|> esc

  esc :: repr ()
  esc = P.char '\\' P.*> P.void (P.oneOf "0tnvfr")

  expr :: repr ()
  expr = nandexpr P.*> P.skipMany (symbol '!' P.*> nandexpr)

  nandexpr :: repr ()
  nandexpr = literal P.<|> funccallOrVar

  funccallOrVar :: repr ()
  funccallOrVar = identifier P.*> P.optional (parens exprlist P.<|> index)

  exprlist = commaSep expr
  exprlist1 = commaSep1 expr
  varlist = commaSep variable
  varlist1 = commaSep1 variable

  funcparam = varlist P.*> P.optional (symbol ':' P.*> varlist)
  varstmt = P.optional (keyword "var") P.*> varlist1 P.*> symbol '=' P.*> exprlist1 P.<* semi
  ifstmt = keyword "if" P.*> expr P.*> block P.*> P.optional (keyword "else" P.*> block)
  whilestmt = keyword "while" P.*> expr P.*> block
  statement = ifstmt P.<|> whilestmt P.<|> P.try varstmt P.<|> expr P.<* semi
  block = braces (P.skipMany statement)
  funcdef = keyword "function" P.*> identifier P.*> parens funcparam P.*> block

  decimal :: repr ()
  decimal = number (P.oneOf ['0'..'9'])
  -- hexadecimal = P.oneOf "xX" P.*> number (P.oneOf (['a'..'f'] <> ['A'..'F'] <> ['0'..'9']))
  -- octal = P.oneOf "oO" P.*> number (P.oneOf ['0'..'7'])
  number :: repr a -> repr ()
  number digit = P.skipSome digit

  symbol :: Char -> repr Char
  symbol c = P.char c P.<* whitespace
  parens :: repr a -> repr a
  parens = P.between (symbol '(') (symbol ')')
  brackets :: repr a -> repr a
  brackets = P.between (symbol '[') (symbol ']')
  braces :: repr a -> repr a
  braces = P.between (symbol '{') (symbol '}')
  semi :: repr Char
  semi = symbol ';'
  comma :: repr Char
  comma = symbol ','
  commaSep :: repr a -> repr ()
  commaSep p = P.optional (commaSep1 p)
  commaSep1 :: repr a -> repr ()
  commaSep1 p = p P.*> P.skipMany (comma P.*> p)

  space :: repr ()
  space = P.void (P.satisfy
    [P.ErrorItemLabel "space"]
    (trans (H.ValueCode isSpace [||isSpace||])))
  whitespace :: repr ()
  whitespace = P.skipMany (spaces P.<|> oneLineComment)
  spaces :: repr ()
  spaces = P.skipSome space
  oneLineComment :: repr ()
  oneLineComment = P.void (P.string "//" P.*> P.skipMany (P.satisfy
    [P.ErrorItemLabel "oneLineComment"]
    (trans (H.ValueCode (/= '\n') [||(/= '\n')||]))))