Skip to content

Commit

Permalink
%shift pragma for lowest priority rules
Browse files Browse the repository at this point in the history
  • Loading branch information
int-index committed Oct 3, 2019
1 parent 2a37b3a commit e7a13c6
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 15 deletions.
19 changes: 19 additions & 0 deletions doc/happy.xml
Expand Up @@ -876,6 +876,25 @@ Exp : let var '=' Exp in Exp { Let $2 $4 $6 }
would normally be the precedence of '-') with the precedence
of <literal>NEG</literal>.</para>
</sect2>

<sect2 id="shift pragma">
<title>The %shift directive for lowest precedence rules</title>
<para>
Rules annotated with the <literal>%shift</literal> directive
have the lowest possible precedence and are non-associative.
A shift/reduce conflict that involves such a rule is resolved as a shift.

One can think of <literal>%shift</literal> as
<literal>%prec SHIFT</literal> such that <literal>SHIFT</literal>
has lower precedence than any other token.
</para>
<para>
This is useful in conjunction with
<literal>%expect 0</literal> to explicitly point out all rules in the grammar that
result in conflicts, and thereby resolve such conflicts.
</para>
</sect2>

</sect1>

<sect1 id="sec-type-signatures">
Expand Down
8 changes: 6 additions & 2 deletions src/AbsSyn.lhs
Expand Up @@ -12,7 +12,7 @@ Here is the abstract syntax of the language we parse.
> getImportedIdentity, getMonad, getError,
> getPrios, getPrioNames, getExpect, getErrorHandlerType,
> getAttributes, getAttributetype,
> Rule(..), Prod(..), Term(..)
> Rule(..), Prod(..), Term(..), Prec(..)
> ) where

> data AbsSyn
Expand All @@ -34,13 +34,17 @@ Here is the abstract syntax of the language we parse.
> [Term] -- terms that make up the rule
> String -- code body that runs when the rule reduces
> Int -- line number
> (Maybe String) -- inline precedence annotation for the rule
> Prec -- inline precedence annotation for the rule

> data Term
> = App
> String -- name of the term
> [Term] -- parameter arguments (usually this is empty)

> data Prec
> = PrecNone -- no user-specified precedence
> | PrecShift -- %shift
> | PrecId String -- %prec ID


#ifdef DEBUG
Expand Down
15 changes: 9 additions & 6 deletions src/Grammar.lhs
Expand Up @@ -106,7 +106,7 @@ Here is our mid-section datatype

#endif

> data Priority = No | Prio Assoc Int
> data Priority = No | Prio Assoc Int | PrioLowest

#ifdef DEBUG

Expand Down Expand Up @@ -283,17 +283,20 @@ Translate the rules from string to name-based.
> return (Production nt lhs' code' No)
> Right p -> return (Production nt lhs' code' p)
>
> mkPrec :: [Name] -> Maybe String -> Either String Priority
> mkPrec lhs prio =
> case prio of
> Nothing -> case filter (flip elem terminal_names) lhs of
> mkPrec :: [Name] -> Prec -> Either String Priority
> mkPrec lhs PrecNone =
> case filter (flip elem terminal_names) lhs of
> [] -> Right No
> xs -> case lookup (last xs) prios of
> Nothing -> Right No
> Just p -> Right p
> Just s -> case lookup s prioByString of
> mkPrec _ (PrecId s) =
> case lookup s prioByString of
> Nothing -> Left s
> Just p -> Right p
>
> mkPrec _ PrecShift = Right PrioLowest
>
> -- in

> rules1 <- mapM convNT rules
Expand Down
7 changes: 7 additions & 0 deletions src/LALR.lhs
Expand Up @@ -561,6 +561,9 @@ NOTE: on (LR'Multiple as a) handling
> res a@(LR'Shift {}) b@(LR'Reduce {}) = res b a
> res a@(LR'Reduce _ p) b@(LR'Shift _ p')
> = case (p,p') of
> (PrioLowest,PrioLowest) -> LR'MustFail
> (_,PrioLowest) -> a
> (PrioLowest,_) -> b
> (No,_) -> LR'Multiple [a] b -- shift wins
> (_,No) -> LR'Multiple [a] b -- shift wins
> (Prio c i, Prio _ j)
Expand All @@ -573,6 +576,10 @@ NOTE: on (LR'Multiple as a) handling
> None -> LR'MustFail
> res a@(LR'Reduce r p) b@(LR'Reduce r' p')
> = case (p,p') of
> (PrioLowest,PrioLowest) ->
> LR'Multiple [a] b -- give to earlier rule?
> (_,PrioLowest) -> a
> (PrioLowest,_) -> b
> (No,_) -> LR'Multiple [a] b -- give to earlier rule?
> (_,No) -> LR'Multiple [a] b
> (Prio _ i, Prio _ j)
Expand Down
3 changes: 3 additions & 0 deletions src/Lexer.lhs
Expand Up @@ -45,6 +45,7 @@ The lexer.
> | TokSpecId_Left -- %left
> | TokSpecId_Right -- %right
> | TokSpecId_Prec -- %prec
> | TokSpecId_Shift -- %shift
> | TokSpecId_Expect -- %expect
> | TokSpecId_Error -- %error
> | TokSpecId_Attributetype -- %attributetype
Expand Down Expand Up @@ -132,6 +133,8 @@ followed by a special identifier.
> returnToken cont (TokenKW TokSpecId_Right) rest
> 'p':'r':'e':'c':rest ->
> returnToken cont (TokenKW TokSpecId_Prec) rest
> 's':'h':'i':'f':'t':rest ->
> returnToken cont (TokenKW TokSpecId_Shift) rest
> 'e':'x':'p':'e':'c':'t':rest ->
> returnToken cont (TokenKW TokSpecId_Expect) rest
> 'e':'r':'r':'o':'r':'h':'a':'n':'d':'l':'e':'r':'t':'y':'p':'e':rest ->
Expand Down
2 changes: 1 addition & 1 deletion src/ParamRules.hs
Expand Up @@ -27,7 +27,7 @@ newtype Funs = Funs (M.Map RuleName Rule)
data Rule1 = Rule1 RuleName [Prod1] (Maybe (String, Subst))

-- | Similar to 'Prod', but `Term`'s have been flattened into `RuleName`'s
data Prod1 = Prod1 [RuleName] String Int (Maybe String)
data Prod1 = Prod1 [RuleName] String Int Prec

inst_name :: Inst -> RuleName
inst_name (Inst f []) = f
Expand Down
8 changes: 5 additions & 3 deletions src/Parser.ly
Expand Up @@ -29,6 +29,7 @@ The parser.
> spec_left { TokenKW TokSpecId_Left }
> spec_right { TokenKW TokSpecId_Right }
> spec_prec { TokenKW TokSpecId_Prec }
> spec_shift { TokenKW TokSpecId_Shift }
> spec_expect { TokenKW TokSpecId_Expect }
> spec_error { TokenKW TokSpecId_Error }
> spec_errorhandlertype { TokenKW TokSpecId_ErrorHandlerType }
Expand Down Expand Up @@ -95,9 +96,10 @@ The parser.
> : term { [$1] }
> | comma_terms "," term { $3 : $1 }

> prec :: { Maybe String }
> : spec_prec id { Just $2 }
> | { Nothing }
> prec :: { Prec }
> : spec_prec id { PrecId $2 }
> | spec_shift { PrecShift }
> | { PrecNone }

> tokInfos :: { [Directive String] }
> : tokInfos tokInfo { $2 : $1 }
Expand Down
8 changes: 6 additions & 2 deletions src/PrettyGrammar.hs
Expand Up @@ -29,10 +29,14 @@ ppRule (Rule name _ prods _) = text name
starts = text " :" : repeat (text " |")

ppProd :: Prod -> Doc
ppProd (Prod ts _ _ p) = psDoc <+> precDoc
ppProd (Prod ts _ _ p) = psDoc <+> ppPrec p
where
psDoc = if null ts then text "{- empty -}" else hsep (map ppTerm ts)
precDoc = maybe empty (\x -> text "%prec" <+> text x) p

ppPrec :: Prec -> Doc
ppPrec PrecNone = empty
ppPrec PrecShift = text "%shift"
ppPrec (PrecId x) = text "%prec" <+> text x

ppTerm :: Term -> Doc
ppTerm (App x ts) = text x <> ppTuple (map ppTerm ts)
Expand Down
2 changes: 1 addition & 1 deletion tests/Makefile
Expand Up @@ -29,7 +29,7 @@ TESTS = Test.ly TestMulti.ly TestPrecedence.ly bug001.ly \
AttrGrammar001.y AttrGrammar002.y \
test_rules.y monaderror.y monaderror-explist.y \
typeclass_monad001.y typeclass_monad002.ly typeclass_monad_lexer.y \
rank2.y
rank2.y shift01.y

ERROR_TESTS = error001.y

Expand Down
30 changes: 30 additions & 0 deletions tests/shift01.y
@@ -0,0 +1,30 @@
-- Testing the %shift directive

{
module Main where

import System.IO
import Data.Char
}

%expect 0 -- We must resolve the conflicts with %shift
%name group_a
%tokentype { Token }

%token 'A' { A }

%%
exp : exp 'A' %shift { $1 ++ ",A" }
| exp 'A' 'A' { $1 ++ ",2A" }
| { "S" }

{
main =
if group_a [A, A, A] == "S,2A,A"
then return ()
else error "bad parse"

data Token = A

happyError _ = error "parse error"
}

0 comments on commit e7a13c6

Please sign in to comment.