module Unison.Syntax.Precedence where

import Data.Map qualified as Map
import Unison.Prelude

-- Precedence rules for infix operators.
-- Lower number means higher precedence (tighter binding).
-- Operators not in this list have no precedence and will simply be parsed
-- left-to-right.
infixRules :: Map Text Precedence
infixRules :: Map Text Precedence
infixRules =
  [(Text, Precedence)] -> Map Text Precedence
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList do
    ([Text]
ops, Precedence
prec) <- [[Text]] -> [Precedence] -> [([Text], Precedence)]
forall a b. [a] -> [b] -> [(a, b)]
zip [[Text]]
infixLevels ((Int -> Precedence) -> [Int] -> [Precedence]
forall a b. (a -> b) -> [a] -> [b]
map (InfixPrecedence -> Precedence
InfixOp (InfixPrecedence -> Precedence)
-> (Int -> InfixPrecedence) -> Int -> Precedence
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> InfixPrecedence
Level) [Int
0 ..])
    (Text -> (Text, Precedence)) -> [Text] -> [(Text, Precedence)]
forall a b. (a -> b) -> [a] -> [b]
map (,Precedence
prec) [Text]
ops

-- | Indicates this is the RHS of a top-level definition.
isTopLevelPrecedence :: Precedence -> Bool
isTopLevelPrecedence :: Precedence -> Bool
isTopLevelPrecedence Precedence
i = Precedence
i Precedence -> Precedence -> Bool
forall a. Eq a => a -> a -> Bool
== Precedence
Basement

increment :: Precedence -> Precedence
increment :: Precedence -> Precedence
increment = \case
  Precedence
Basement -> Precedence
Bottom
  Precedence
Bottom -> Precedence
Annotation
  Precedence
Annotation -> Precedence
Statement
  Precedence
Statement -> Precedence
Control
  Precedence
Control -> InfixPrecedence -> Precedence
InfixOp InfixPrecedence
Lowest
  InfixOp InfixPrecedence
Lowest -> InfixPrecedence -> Precedence
InfixOp (Int -> InfixPrecedence
Level Int
0)
  InfixOp (Level Int
n) -> InfixPrecedence -> Precedence
InfixOp (Int -> InfixPrecedence
Level (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1))
  InfixOp InfixPrecedence
Highest -> Precedence
Application
  Precedence
Application -> Precedence
Prefix
  Precedence
Prefix -> Precedence
Top
  Precedence
Top -> Precedence
Top

data Precedence
  = -- | The lowest precedence, used for top-level bindings
    Basement
  | -- | Used for terms that never need parentheses
    Bottom
  | -- | Type annotations
    Annotation
  | -- | A statement in a block
    Statement
  | -- | Control flow constructs like `if`, `match`, `case`
    Control
  | -- | Infix operators
    InfixOp InfixPrecedence
  | -- | Function application
    Application
  | -- | Prefix operators like `'`, `!`
    Prefix
  | -- | The highest precedence, used for let bindings and blocks
    Top
  deriving (Precedence -> Precedence -> Bool
(Precedence -> Precedence -> Bool)
-> (Precedence -> Precedence -> Bool) -> Eq Precedence
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Precedence -> Precedence -> Bool
== :: Precedence -> Precedence -> Bool
$c/= :: Precedence -> Precedence -> Bool
/= :: Precedence -> Precedence -> Bool
Eq, Eq Precedence
Eq Precedence =>
(Precedence -> Precedence -> Ordering)
-> (Precedence -> Precedence -> Bool)
-> (Precedence -> Precedence -> Bool)
-> (Precedence -> Precedence -> Bool)
-> (Precedence -> Precedence -> Bool)
-> (Precedence -> Precedence -> Precedence)
-> (Precedence -> Precedence -> Precedence)
-> Ord Precedence
Precedence -> Precedence -> Bool
Precedence -> Precedence -> Ordering
Precedence -> Precedence -> Precedence
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: Precedence -> Precedence -> Ordering
compare :: Precedence -> Precedence -> Ordering
$c< :: Precedence -> Precedence -> Bool
< :: Precedence -> Precedence -> Bool
$c<= :: Precedence -> Precedence -> Bool
<= :: Precedence -> Precedence -> Bool
$c> :: Precedence -> Precedence -> Bool
> :: Precedence -> Precedence -> Bool
$c>= :: Precedence -> Precedence -> Bool
>= :: Precedence -> Precedence -> Bool
$cmax :: Precedence -> Precedence -> Precedence
max :: Precedence -> Precedence -> Precedence
$cmin :: Precedence -> Precedence -> Precedence
min :: Precedence -> Precedence -> Precedence
Ord, Int -> Precedence -> ShowS
[Precedence] -> ShowS
Precedence -> String
(Int -> Precedence -> ShowS)
-> (Precedence -> String)
-> ([Precedence] -> ShowS)
-> Show Precedence
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Precedence -> ShowS
showsPrec :: Int -> Precedence -> ShowS
$cshow :: Precedence -> String
show :: Precedence -> String
$cshowList :: [Precedence] -> ShowS
showList :: [Precedence] -> ShowS
Show)

data InfixPrecedence = Lowest | Level Int | Highest
  deriving (InfixPrecedence -> InfixPrecedence -> Bool
(InfixPrecedence -> InfixPrecedence -> Bool)
-> (InfixPrecedence -> InfixPrecedence -> Bool)
-> Eq InfixPrecedence
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: InfixPrecedence -> InfixPrecedence -> Bool
== :: InfixPrecedence -> InfixPrecedence -> Bool
$c/= :: InfixPrecedence -> InfixPrecedence -> Bool
/= :: InfixPrecedence -> InfixPrecedence -> Bool
Eq, Eq InfixPrecedence
Eq InfixPrecedence =>
(InfixPrecedence -> InfixPrecedence -> Ordering)
-> (InfixPrecedence -> InfixPrecedence -> Bool)
-> (InfixPrecedence -> InfixPrecedence -> Bool)
-> (InfixPrecedence -> InfixPrecedence -> Bool)
-> (InfixPrecedence -> InfixPrecedence -> Bool)
-> (InfixPrecedence -> InfixPrecedence -> InfixPrecedence)
-> (InfixPrecedence -> InfixPrecedence -> InfixPrecedence)
-> Ord InfixPrecedence
InfixPrecedence -> InfixPrecedence -> Bool
InfixPrecedence -> InfixPrecedence -> Ordering
InfixPrecedence -> InfixPrecedence -> InfixPrecedence
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: InfixPrecedence -> InfixPrecedence -> Ordering
compare :: InfixPrecedence -> InfixPrecedence -> Ordering
$c< :: InfixPrecedence -> InfixPrecedence -> Bool
< :: InfixPrecedence -> InfixPrecedence -> Bool
$c<= :: InfixPrecedence -> InfixPrecedence -> Bool
<= :: InfixPrecedence -> InfixPrecedence -> Bool
$c> :: InfixPrecedence -> InfixPrecedence -> Bool
> :: InfixPrecedence -> InfixPrecedence -> Bool
$c>= :: InfixPrecedence -> InfixPrecedence -> Bool
>= :: InfixPrecedence -> InfixPrecedence -> Bool
$cmax :: InfixPrecedence -> InfixPrecedence -> InfixPrecedence
max :: InfixPrecedence -> InfixPrecedence -> InfixPrecedence
$cmin :: InfixPrecedence -> InfixPrecedence -> InfixPrecedence
min :: InfixPrecedence -> InfixPrecedence -> InfixPrecedence
Ord, Int -> InfixPrecedence -> ShowS
[InfixPrecedence] -> ShowS
InfixPrecedence -> String
(Int -> InfixPrecedence -> ShowS)
-> (InfixPrecedence -> String)
-> ([InfixPrecedence] -> ShowS)
-> Show InfixPrecedence
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> InfixPrecedence -> ShowS
showsPrec :: Int -> InfixPrecedence -> ShowS
$cshow :: InfixPrecedence -> String
show :: InfixPrecedence -> String
$cshowList :: [InfixPrecedence] -> ShowS
showList :: [InfixPrecedence] -> ShowS
Show)

infixLevels :: [[Text]]
infixLevels :: [[Text]]
infixLevels =
  [ [Text
"||", Text
"|"],
    [Text
"&&", Text
"&"],
    [Text
"==", Text
"!==", Text
"!=", Text
"==="],
    [Text
"<", Text
">", Text
">=", Text
"<="],
    [Text
"+", Text
"-"],
    [Text
"*", Text
"/", Text
"%"],
    [Text
"^", Text
"^^", Text
"**"]
  ]

-- | Returns the precedence of an infix operator, if it has one.
operatorPrecedence :: Text -> Maybe Precedence
operatorPrecedence :: Text -> Maybe Precedence
operatorPrecedence Text
op = Text -> Map Text Precedence -> Maybe Precedence
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Text
op Map Text Precedence
infixRules