1
1
mirror of https://github.com/anoma/juvix.git synced 2024-12-01 00:04:58 +03:00

Disallow iterators with zero ranges (#2267)

* Closes #2265
This commit is contained in:
Łukasz Czajka 2023-08-03 11:43:59 +02:00 committed by GitHub
parent f38123c550
commit 22de9d0233
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 100 additions and 207 deletions

View File

@ -7,8 +7,6 @@ module Juvix.Compiler.Concrete.Extra
groupStatements,
flattenStatement,
migrateFunctionSyntax,
fromAmbiguousIterator,
ambiguousIteratorToAtoms,
)
where
@ -193,46 +191,3 @@ migrateFunctionSyntax m = over moduleBody (mapMaybe goStatement) m
}
getClauses :: S.Symbol -> [FunctionClause 'Scoped]
getClauses name = [c | StatementFunctionClause c <- ss', name == c ^. clauseOwnerFunction]
fromAmbiguousIterator :: AmbiguousIterator -> Iterator 'Parsed
fromAmbiguousIterator AmbiguousIterator {..} =
Iterator
{ _iteratorName = _ambiguousIteratorName,
_iteratorRanges = [],
_iteratorBody = _ambiguousIteratorBody,
_iteratorBodyBraces = _ambiguousIteratorBodyBraces,
_iteratorInitializers = map mkInitializer (toList _ambiguousIteratorInitializers),
_iteratorParens = _ambiguousIteratorParens
}
where
mkInitializer :: NamedArgument 'Parsed -> Initializer 'Parsed
mkInitializer a@NamedArgument {..} =
Initializer
{ _initializerAssignKw = _namedArgAssignKw,
_initializerPattern = PatternAtoms (pure (PatternAtomIden (NameUnqualified _namedArgName))) (Irrelevant (getLoc a)),
_initializerExpression = _namedArgValue
}
ambiguousIteratorToAtoms :: AmbiguousIterator -> ExpressionAtoms 'Parsed
ambiguousIteratorToAtoms AmbiguousIterator {..} =
ExpressionAtoms
{ _expressionAtomsLoc = Irrelevant (getLoc napp),
_expressionAtoms = pure (AtomNamedApplication napp) <> body
}
where
body :: NonEmpty (ExpressionAtom 'Parsed)
body
| _ambiguousIteratorBodyBraces = pure (AtomBraces (WithLoc (getLoc _ambiguousIteratorBody) _ambiguousIteratorBody))
| otherwise = _ambiguousIteratorBody ^. expressionAtoms
napp =
NamedApplication
{ _namedAppName = _ambiguousIteratorName,
_namedAppSignature = Irrelevant (),
_namedAppArgs =
pure
ArgumentBlock
{ _argBlockDelims = Irrelevant Nothing,
_argBlockImplicit = Explicit,
_argBlockArgs = _ambiguousIteratorInitializers
}
}

View File

@ -101,11 +101,6 @@ type family NameSignatureType s = res | res -> s where
NameSignatureType 'Parsed = ()
NameSignatureType 'Scoped = NameSignature
type AmbiguousIteratorType :: Stage -> GHC.Type
type family AmbiguousIteratorType s = res | res -> s where
AmbiguousIteratorType 'Parsed = AmbiguousIterator
AmbiguousIteratorType 'Scoped = Void
type ModulePathType :: Stage -> ModuleIsTop -> GHC.Type
type family ModulePathType s t = res | res -> t s where
ModulePathType 'Parsed 'ModuleTop = TopModulePath
@ -1184,20 +1179,6 @@ deriving stock instance Ord (Iterator 'Parsed)
deriving stock instance Ord (Iterator 'Scoped)
-- | Either an Iterator or a NamedApplication. Has the form:
-- f (sym := expr; .. ; symn := expr) -- not followed by range
data AmbiguousIterator = AmbiguousIterator
{ _ambiguousIteratorName :: Name,
_ambiguousIteratorInitializers :: NonEmpty (NamedArgument 'Parsed),
_ambiguousIteratorBody :: ExpressionType 'Parsed,
-- | Was the body enclosed in braces?
_ambiguousIteratorBodyBraces :: Bool,
-- | Due to limitations of the pretty printing algorithm, we store whether
-- the iterator was surrounded by parentheses in the code.
_ambiguousIteratorParens :: Bool
}
deriving stock (Show, Eq, Ord)
data List (s :: Stage) = List
{ _listBracketL :: Irrelevant KeywordRef,
_listBracketR :: Irrelevant KeywordRef,
@ -1285,7 +1266,6 @@ data ExpressionAtom (s :: Stage)
| AtomLiteral LiteralLoc
| AtomParens (ExpressionType s)
| AtomIterator (Iterator s)
| AtomAmbiguousIterator (AmbiguousIteratorType s)
| AtomNamedApplication (NamedApplication s)
deriving stock instance Show (ExpressionAtom 'Parsed)
@ -1493,7 +1473,6 @@ makeLenses ''PatternBinding
makeLenses ''PatternAtoms
makeLenses ''ExpressionAtoms
makeLenses ''Iterator
makeLenses ''AmbiguousIterator
makeLenses ''Initializer
makeLenses ''Range
makeLenses ''ModuleIndex

View File

@ -13,7 +13,6 @@ import Juvix.Compiler.Concrete.Data.InfoTable
import Juvix.Compiler.Concrete.Data.NameSignature.Base
import Juvix.Compiler.Concrete.Data.Scope.Base
import Juvix.Compiler.Concrete.Data.ScopedName qualified as S
import Juvix.Compiler.Concrete.Extra (fromAmbiguousIterator)
import Juvix.Compiler.Concrete.Extra qualified as Concrete
import Juvix.Compiler.Concrete.Keywords qualified as Kw
import Juvix.Compiler.Concrete.Language
@ -122,11 +121,6 @@ ppExpressionType = case sing :: SStage s of
SParsed -> ppCode
SScoped -> ppCode
ppAmbiguousIteratorType :: forall s. SingI s => PrettyPrinting (AmbiguousIteratorType s)
ppAmbiguousIteratorType = case sing :: SStage s of
SParsed -> ppCode
SScoped -> ppCode
ppPatternAtomType :: forall s. SingI s => PrettyPrinting (PatternAtomType s)
ppPatternAtomType = case sing :: SStage s of
SParsed -> ppCodeAtom
@ -247,9 +241,6 @@ instance SingI s => PrettyPrint (List s) where
e = hsepSemicolon (map ppExpressionType _listItems)
l <> e <> r
instance PrettyPrint AmbiguousIterator where
ppCode = ppCode . fromAmbiguousIterator
instance SingI s => PrettyPrint (NamedArgument s) where
ppCode NamedArgument {..} = do
let s = ppCode _namedArgName
@ -286,7 +277,6 @@ instance SingI s => PrettyPrint (ExpressionAtom s) where
AtomBraces e -> braces (ppExpressionType (e ^. withLocParam))
AtomHole w -> ppHoleType w
AtomIterator i -> ppCode i
AtomAmbiguousIterator i -> ppAmbiguousIteratorType i
AtomNamedApplication i -> ppCode i
instance PrettyPrint PatternScopedIden where

View File

@ -15,7 +15,6 @@ import Juvix.Compiler.Concrete.Data.Name qualified as N
import Juvix.Compiler.Concrete.Data.NameSignature
import Juvix.Compiler.Concrete.Data.Scope
import Juvix.Compiler.Concrete.Data.ScopedName qualified as S
import Juvix.Compiler.Concrete.Extra (fromAmbiguousIterator)
import Juvix.Compiler.Concrete.Extra qualified as P
import Juvix.Compiler.Concrete.Gen qualified as G
import Juvix.Compiler.Concrete.Language
@ -1792,18 +1791,6 @@ checkExpressionAtom e = case e of
AtomList l -> pure . AtomList <$> checkList l
AtomIterator i -> pure . AtomIterator <$> checkIterator i
AtomNamedApplication i -> pure . AtomNamedApplication <$> checkNamedApplication i
AtomAmbiguousIterator i -> checkAmbiguousIterator i
checkAmbiguousIterator ::
forall r.
Members '[Reader ScopeParameters, Error ScoperError, State Scope, State ScoperState, InfoTableBuilder, NameIdGen] r =>
AmbiguousIterator ->
Sem r (NonEmpty (ExpressionAtom 'Scoped))
checkAmbiguousIterator a = do
nm <- checkName (a ^. ambiguousIteratorName)
if
| isJust (identifierName nm ^. S.nameIterator) -> pure . AtomIterator <$> checkIterator (fromAmbiguousIterator a)
| otherwise -> (^. expressionAtoms) <$> checkExpressionAtoms (P.ambiguousIteratorToAtoms a)
checkNamedApplication ::
forall r.

View File

@ -599,7 +599,7 @@ expressionAtom :: (Members '[InfoTableBuilder, PragmasStash, JudocStash, NameIdG
expressionAtom =
P.label "<expression>" $
AtomLiteral <$> P.try literal
<|> either AtomIterator AtomAmbiguousIterator <$> iterator
<|> AtomIterator <$> iterator
<|> AtomNamedApplication <$> namedApplication
<|> AtomList <$> parseList
<|> AtomIdentifier <$> name
@ -624,100 +624,76 @@ parseExpressionAtoms = do
-- Iterators
--------------------------------------------------------------------------------
-- | Checks whether an iterator is ambiguous
ambiguousIterator :: Iterator 'Parsed -> Either (Iterator 'Parsed) AmbiguousIterator
ambiguousIterator i@Iterator {..} = maybe (Left i) Right . run . runFail $ mkAmbiguousIterator
where
mkAmbiguousIterator :: forall r. Members '[Fail] r => Sem r AmbiguousIterator
mkAmbiguousIterator = do
failUnless (null _iteratorRanges)
_ambiguousIteratorInitializers <- nonEmpty' <$> mapM mkNamedArgument _iteratorInitializers
let _ambiguousIteratorBodyBraces = _iteratorBodyBraces
_ambiguousIteratorParens = _iteratorParens
_ambiguousIteratorName = _iteratorName
_ambiguousIteratorBody = _iteratorBody
return AmbiguousIterator {..}
where
mkNamedArgument :: Initializer 'Parsed -> Sem r (NamedArgument 'Parsed)
mkNamedArgument Initializer {..} = do
let _namedArgAssignKw = _initializerAssignKw
_namedArgValue = _initializerExpression
_namedArgName <- case _initializerPattern ^. patternAtoms of
PatternAtomIden (NameUnqualified n) :| [] -> return n
_ -> fail
return NamedArgument {..}
iterator ::
forall r.
Members '[InfoTableBuilder, PragmasStash, JudocStash, NameIdGen] r =>
ParsecS r (Either (Iterator 'Parsed) AmbiguousIterator)
iterator = fmap ambiguousIterator iteratorHelper
ParsecS r (Iterator 'Parsed)
iterator = P.try $ do
off <- P.getOffset
(firstIsInit, keywordRef, _iteratorName, pat) <- P.try $ do
n <- name
lparen
pat <- parsePatternAtoms
(isInit, kwr) <-
(True,) <$> kw kwAssign
<|> (False,) <$> kw kwIn
return (isInit, Irrelevant kwr, n, pat)
val <- parseExpressionAtoms
_iteratorInitializers <-
if
| firstIsInit -> do
inis <- many (semicolon >> initializer)
rparen
let ini =
Initializer
{ _initializerPattern = pat,
_initializerAssignKw = keywordRef,
_initializerExpression = val
}
return (ini : inis)
| otherwise -> return []
_iteratorRanges <-
if
| not firstIsInit -> do
rngs <- many (semicolon >> range)
rparen
let ran =
Range
{ _rangeExpression = val,
_rangePattern = pat,
_rangeInKw = keywordRef
}
return (ran : rngs)
| otherwise -> fmap (maybe [] toList) . optional $ do
lparen
rngs <- P.sepBy1 range semicolon
rparen
return rngs
when (null _iteratorRanges) $
parseFailure off "an iterator must have at least one range"
(_iteratorBody, _iteratorBodyBraces) <-
(,True) <$> braces parseExpressionAtoms
<|> (,False) <$> parseExpressionAtoms
let _iteratorParens = False
return Iterator {..}
where
iteratorHelper :: ParsecS r (Iterator 'Parsed)
iteratorHelper = P.try $ do
-- TODO remove top P.Try
(firstIsInit, keywordRef, _iteratorName, pat) <- P.try $ do
n <- name
lparen
initializer :: ParsecS r (Initializer 'Parsed)
initializer = do
(_initializerPattern, _initializerAssignKw) <- P.try $ do
pat <- parsePatternAtoms
(isInit, kwr) <-
(True,) <$> kw kwAssign
<|> (False,) <$> kw kwIn
return (isInit, Irrelevant kwr, n, pat)
val <- parseExpressionAtoms
_iteratorInitializers <-
if
| firstIsInit -> do
inis <- many (semicolon >> initializer)
rparen
let ini =
Initializer
{ _initializerPattern = pat,
_initializerAssignKw = keywordRef,
_initializerExpression = val
}
return (ini : inis)
| otherwise -> return []
_iteratorRanges <-
if
| not firstIsInit -> do
rngs <- many (semicolon >> range)
rparen
let ran =
Range
{ _rangeExpression = val,
_rangePattern = pat,
_rangeInKw = keywordRef
}
return (ran : rngs)
| otherwise -> fmap (maybe [] toList) . optional $ do
lparen
rngs <- P.sepBy1 range semicolon
rparen
return rngs
(_iteratorBody, _iteratorBodyBraces) <-
(,True) <$> braces parseExpressionAtoms
<|> (,False) <$> parseExpressionAtoms
let _iteratorParens = False
return Iterator {..}
where
initializer :: ParsecS r (Initializer 'Parsed)
initializer = do
(_initializerPattern, _initializerAssignKw) <- P.try $ do
pat <- parsePatternAtoms
r <- Irrelevant <$> kw kwAssign
return (pat, r)
_initializerExpression <- parseExpressionAtoms
return Initializer {..}
r <- Irrelevant <$> kw kwAssign
return (pat, r)
_initializerExpression <- parseExpressionAtoms
return Initializer {..}
range :: ParsecS r (Range 'Parsed)
range = do
(_rangePattern, _rangeInKw) <- P.try $ do
pat <- parsePatternAtoms
r <- Irrelevant <$> kw kwIn
return (pat, r)
_rangeExpression <- parseExpressionAtoms
return Range {..}
range :: ParsecS r (Range 'Parsed)
range = do
(_rangePattern, _rangeInKw) <- P.try $ do
pat <- parsePatternAtoms
r <- Irrelevant <$> kw kwIn
return (pat, r)
_rangeExpression <- parseExpressionAtoms
return Range {..}
namedApplication ::
forall r.
@ -732,20 +708,27 @@ namedApplication = P.label "<named application>" $ do
let _namedAppArgs = firstBlock :| tailBlocks
_namedAppSignature = Irrelevant ()
return NamedApplication {..}
where
namedArgument :: ParsecS r (NamedArgument 'Parsed)
namedArgument = do
_namedArgName <- symbol
_namedArgAssignKw <- Irrelevant <$> kw kwAssign
_namedArgValue <- parseExpressionAtoms
return NamedArgument {..}
argumentBlock :: ParsecS r (ArgumentBlock 'Parsed)
argumentBlock = P.try $ do
(l, _argBlockImplicit) <- implicitOpen
_argBlockArgs <- P.sepEndBy1 namedArgument semicolon
r <- implicitClose _argBlockImplicit
let _argBlockDelims = Irrelevant (Just (l, r))
return ArgumentBlock {..}
namedArgument ::
forall r.
Members '[InfoTableBuilder, PragmasStash, JudocStash, NameIdGen] r =>
ParsecS r (NamedArgument 'Parsed)
namedArgument = do
_namedArgName <- symbol
_namedArgAssignKw <- Irrelevant <$> kw kwAssign
_namedArgValue <- parseExpressionAtoms
return NamedArgument {..}
argumentBlock ::
forall r.
Members '[InfoTableBuilder, PragmasStash, JudocStash, NameIdGen] r =>
ParsecS r (ArgumentBlock 'Parsed)
argumentBlock = P.try $ do
(l, _argBlockImplicit) <- implicitOpen
_argBlockArgs <- P.sepEndBy1 namedArgument semicolon
r <- implicitClose _argBlockImplicit
let _argBlockDelims = Irrelevant (Just (l, r))
return ArgumentBlock {..}
hole :: (Members '[InfoTableBuilder, PragmasStash, JudocStash, NameIdGen] r) => ParsecS r (HoleType 'Parsed)
hole = kw kwHole

View File

@ -17,6 +17,9 @@ instance FromJSON IteratorAttribs where
checkYamlKeys ["init", "range"]
_iteratorAttribsInitNum <- keyMay "init" asIntegral
_iteratorAttribsRangeNum <- keyMay "range" asIntegral
unless
(maybe True (> 0) _iteratorAttribsRangeNum)
(throwCustomError "the iterator must have at least one range")
return IteratorAttribs {..}
emptyIteratorAttribs :: IteratorAttribs

View File

@ -1,6 +1,6 @@
module Iterators2;
syntax iterator bind {range: 0};
syntax iterator bind {range: 1};
bind : {A B : Type} → (A → B) → A → B;
bind f x := f x;
@ -10,4 +10,4 @@ type Bool :=
| false : Bool;
main : Bool;
main := bind (x in true) x;
main := bind (x in true; y in false) x;

View File

@ -2,13 +2,8 @@ module Iterators;
syntax iterator for {init: 1, range: 1};
for : {A B : Type} → (A → B → A) → A → B → A
| f x y := f x y;
syntax iterator bind {init: 1, range: 0};
bind : {A B : Type} → (A → B) → A → B
| f x := f x;
for {A B : Type} (f : A → B → A) (x : A) (y : B) : A :=
f x y;
syntax iterator itconst {init: 2, range: 2};
@ -22,9 +17,10 @@ type Bool :=
| false : Bool;
main : Bool :=
bind (z := false)
itconst (a := true; b := false) (c in false; d in false)
for (x := true) (y in false)
case x
| true := y
| false := z;
let
z : Bool := false;
in itconst (a := true; b := false) (c in false; d in false)
for (x := true) (y in false)
case x
| true := y
| false := z;