diff --git a/wasm-calc4/src/Calc/ExprUtils.hs b/wasm-calc4/src/Calc/ExprUtils.hs index bc1be3dd..cabf0bf2 100644 --- a/wasm-calc4/src/Calc/ExprUtils.hs +++ b/wasm-calc4/src/Calc/ExprUtils.hs @@ -8,7 +8,6 @@ module Calc.ExprUtils where import Calc.Types -import Data.Bifunctor (second) -- | get the annotation in the first leaf found in an `Expr`. -- useful for getting the overall type of an expression @@ -19,7 +18,7 @@ getOuterAnnotation (EIf ann _ _ _) = ann getOuterAnnotation (EVar ann _) = ann getOuterAnnotation (EApply ann _ _) = ann getOuterAnnotation (ETuple ann _ _) = ann -getOuterAnnotation (EPatternMatch ann _ _) = ann +getOuterAnnotation (ETupleAccess ann _ _) = ann -- | modify the outer annotation of an expression -- useful for adding line numbers during parsing @@ -32,7 +31,7 @@ mapOuterExprAnnotation f expr' = EVar ann a -> EVar (f ann) a EApply ann a b -> EApply (f ann) a b ETuple ann a b -> ETuple (f ann) a b - EPatternMatch ann a b -> EPatternMatch (f ann) a b + ETupleAccess ann a b -> ETupleAccess (f ann) a b -- | Given a function that changes `Expr` values, apply it throughout -- an AST tree @@ -44,5 +43,5 @@ mapExpr f (EApply ann fn args) = EApply ann fn (f <$> args) mapExpr f (EIf ann predExpr thenExpr elseExpr) = EIf ann (f predExpr) (f thenExpr) (f elseExpr) mapExpr f (ETuple ann a as) = ETuple ann (f a) (f <$> as) -mapExpr f (EPatternMatch ann matchExpr patterns) = - EPatternMatch ann (f matchExpr) (fmap (second f) patterns) +mapExpr f (ETupleAccess ann tup nat) = + ETupleAccess ann (f tup) nat diff --git a/wasm-calc4/src/Calc/Interpreter.hs b/wasm-calc4/src/Calc/Interpreter.hs index f52bf95f..f4e18af7 100644 --- a/wasm-calc4/src/Calc/Interpreter.hs +++ b/wasm-calc4/src/Calc/Interpreter.hs @@ -13,15 +13,15 @@ module Calc.Interpreter ) where +import GHC.Natural +import qualified Data.List.NonEmpty as NE import Calc.Types import Control.Monad.Except import Control.Monad.Reader import Control.Monad.State import Data.Coerce -import qualified Data.List.NonEmpty as NE import Data.Map.Strict (Map) import qualified Data.Map.Strict as M -import Data.Monoid (First (..)) -- | type for interpreter state newtype InterpreterState ann = InterpreterState @@ -33,7 +33,8 @@ data InterpreterError ann = NonBooleanPredicate ann (Expr ann) | FunctionNotFound FunctionName [FunctionName] | VarNotFound Identifier [Identifier] - | NoPatternsMatched (Expr ann) (NE.NonEmpty (Pattern ann)) + | AccessNonTuple (Expr ann) + | AccessOutsideTupleBounds (Expr ann) Natural deriving stock (Eq, Ord, Show) -- | type of Reader env for interpreter state @@ -133,9 +134,9 @@ interpret (ETuple ann a as) = do aA <- interpret a asA <- traverse interpret as pure (ETuple ann aA asA) -interpret (EPatternMatch _ expr pats) = do - exprA <- interpret expr - interpretPatternMatch exprA pats +interpret (ETupleAccess _ tup index) = do + aTup <- interpret tup + interpretTupleAccess aTup index interpret (EIf ann predExpr thenExpr elseExpr) = do predA <- interpret predExpr case predA of @@ -143,42 +144,13 @@ interpret (EIf ann predExpr thenExpr elseExpr) = do (EPrim _ (PBool False)) -> interpret elseExpr other -> throwError (NonBooleanPredicate ann other) -interpretPatternMatch :: - Expr ann -> - NE.NonEmpty (Pattern ann, Expr ann) -> - InterpretM ann (Expr ann) -interpretPatternMatch expr' patterns = do - -- interpret match expression - intExpr <- interpret expr' - let foldF (pat, patExpr) = case patternMatches pat intExpr of - Just bindings -> First (Just (patExpr, bindings)) - _ -> First Nothing - - -- get first matching pattern - case getFirst (foldMap foldF patterns) of - Just (patExpr, bindings) -> - let vars = fmap (coerce . fst) bindings - exprs = fmap snd bindings - in withVars vars exprs (interpret patExpr) - _ -> throwError (NoPatternsMatched expr' (fst <$> patterns)) - --- pull vars out of expr to match patterns -patternMatches :: - Pattern ann -> - Expr ann -> - Maybe [(Identifier, Expr ann)] -patternMatches (PWildcard _) _ = pure [] -patternMatches (PVar _ name) expr = pure [(name, expr)] -patternMatches (PTuple _ pA pAs) (ETuple _ a as) = do - matchA <- patternMatches pA a - matchAs <- - traverse - (uncurry patternMatches) - (zip (NE.toList pAs) (NE.toList as)) - pure $ matchA <> mconcat matchAs -patternMatches (PLiteral _ pB) (EPrim _ b) - | pB == b = pure mempty -patternMatches _ _ = Nothing +interpretTupleAccess :: Expr ann -> Natural -> InterpretM ann (Expr ann) +interpretTupleAccess wholeExpr@(ETuple _ fstExpr restExpr) index = do + let items = zip ([0..] :: [Natural]) (fstExpr : NE.toList restExpr) + case lookup (index - 1) items of + Just expr -> pure expr + Nothing -> throwError (AccessOutsideTupleBounds wholeExpr index) +interpretTupleAccess expr _ = throwError (AccessNonTuple expr) interpretModule :: Module ann -> diff --git a/wasm-calc4/src/Calc/Parser/Expr.hs b/wasm-calc4/src/Calc/Parser/Expr.hs index 50e66588..1adf2ec8 100644 --- a/wasm-calc4/src/Calc/Parser/Expr.hs +++ b/wasm-calc4/src/Calc/Parser/Expr.hs @@ -3,7 +3,6 @@ module Calc.Parser.Expr (exprParser) where import Calc.Parser.Identifier -import Calc.Parser.Pattern import Calc.Parser.Primitives import Calc.Parser.Shared import Calc.Parser.Types @@ -12,6 +11,7 @@ import Calc.Types.Expr import Control.Monad.Combinators.Expr import qualified Data.List.NonEmpty as NE import Data.Text +import GHC.Natural import Text.Megaparsec exprParser :: Parser (Expr Annotation) @@ -19,9 +19,9 @@ exprParser = addLocation (makeExprParser exprPart table) "expression" exprPart :: Parser (Expr Annotation) exprPart = - try tupleParser + try tupleAccessParser + <|> try tupleParser <|> inBrackets (addLocation exprParser) - <|> patternMatchParser <|> primExprParser <|> ifParser <|> try applyParser @@ -71,35 +71,15 @@ tupleParser = label "tuple" $ _ <- stringLiteral ")" pure (ETuple mempty (NE.head neArgs) neTail) ------ +tupleAccessParser :: Parser (Expr Annotation) +tupleAccessParser = + let natParser :: Parser Natural + natParser = myLexeme (fromIntegral <$> intParser) -patternMatchParser :: Parser ParserExpr -patternMatchParser = addLocation $ do - matchExpr <- matchExprWithParser - patterns <- - try patternMatchesParser - <|> pure - <$> patternCaseParser - case NE.nonEmpty patterns of - (Just nePatterns) -> pure $ EPatternMatch mempty matchExpr nePatterns - _ -> error "need at least one pattern" - -matchExprWithParser :: Parser ParserExpr -matchExprWithParser = do - stringLiteral "case" - sumExpr <- exprParser - stringLiteral "of" - pure sumExpr - -patternMatchesParser :: Parser [(ParserPattern, ParserExpr)] -patternMatchesParser = - sepBy - patternCaseParser - (stringLiteral "|") - -patternCaseParser :: Parser (ParserPattern, ParserExpr) -patternCaseParser = do - pat <- orInBrackets patternParser - stringLiteral "->" - patExpr <- exprParser - pure (pat, patExpr) + tupParser :: Parser (Expr Annotation) + tupParser = try tupleParser <|> try varParser <|> applyParser + in label "tuple access" $ + addLocation $ do + tup <- tupParser + stringLiteral "." + ETupleAccess mempty tup <$> natParser diff --git a/wasm-calc4/src/Calc/PatternUtils.hs b/wasm-calc4/src/Calc/PatternUtils.hs deleted file mode 100644 index 2433d884..00000000 --- a/wasm-calc4/src/Calc/PatternUtils.hs +++ /dev/null @@ -1,9 +0,0 @@ -module Calc.PatternUtils (getPatternAnnotation) where - -import Calc.Types.Pattern - -getPatternAnnotation :: Pattern ann -> ann -getPatternAnnotation (PLiteral ann _) = ann -getPatternAnnotation (PWildcard ann) = ann -getPatternAnnotation (PVar ann _) = ann -getPatternAnnotation (PTuple ann _ _) = ann diff --git a/wasm-calc4/src/Calc/Patterns/Flatten.hs b/wasm-calc4/src/Calc/Patterns/Flatten.hs deleted file mode 100644 index 80fa90a1..00000000 --- a/wasm-calc4/src/Calc/Patterns/Flatten.hs +++ /dev/null @@ -1,98 +0,0 @@ -{-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE ScopedTypeVariables #-} - -module Calc.Patterns.Flatten (generateMissing) where - -import Calc.PatternUtils (getPatternAnnotation) -import Calc.Types -import Data.Functor (($>)) -import qualified Data.List -import qualified Data.List.NonEmpty as NE - --- | given our patterns, generate everything we need minus the ones we have -generateMissing :: NE.NonEmpty (Pattern (Type ann)) -> [Pattern ()] -generateMissing nePats = - let pats = NE.toList nePats - in filterMissing (removeAnn <$> pats) (generatePatterns pats) - --- | given our patterns, generate any others we might need -generatePatterns :: [Pattern (Type ann)] -> [Pattern ()] -generatePatterns = concatMap generatePattern - -generateForType :: Type ann -> [Pattern ()] -generateForType (TPrim _ TBool) = [PLiteral () (PBool True), PLiteral () (PBool False)] -generateForType (TPrim _ TInt) = [PWildcard ()] -- too many, just do wildcard -generateForType _ = [PWildcard ()] - -typeIsTotal :: Type ann -> Bool -typeIsTotal (TPrim {}) = False -typeIsTotal (TTuple {}) = True -typeIsTotal (TFunction {}) = True - --- | given a pattern, generate all other patterns we'll need -generatePattern :: forall ann. Pattern (Type ann) -> [Pattern ()] -generatePattern (PWildcard _) = mempty -generatePattern (PLiteral _ (PBool True)) = [PLiteral () (PBool False)] -generatePattern (PLiteral _ (PBool False)) = [PLiteral () (PBool True)] -generatePattern (PTuple _ a as) = - let genOrOriginal :: Pattern (Type ann) -> [Pattern ()] - genOrOriginal pat = - case generatePattern pat of - [] -> - if typeIsTotal (getPatternAnnotation pat) - then [removeAnn pat] - else generateForType (getPatternAnnotation pat) - pats -> if isTotal pat then pats else [removeAnn pat] <> pats - - genAs :: [[Pattern ()]] - genAs = fmap genOrOriginal ([a] <> NE.toList as) - - createTuple :: [Pattern ()] -> Pattern () - createTuple items = - let ne = NE.fromList items - in PTuple () (NE.head ne) (NE.fromList $ NE.tail ne) - in fmap createTuple (sequence genAs) -generatePattern _ = mempty - --- | wildcards are total, vars are total, products are total -isTotal :: Pattern ann -> Bool -isTotal (PWildcard _) = True -isTotal (PVar _ _) = True -isTotal (PTuple {}) = True -isTotal _ = False - --- filter outstanding items -filterMissing :: - [Pattern ()] -> - [Pattern ()] -> - [Pattern ()] -filterMissing patterns required = - Data.List.nub $ foldr annihiliatePattern required patterns - where - annihiliatePattern pat = - filter - ( not - . annihilate pat - ) - -removeAnn :: Pattern ann -> Pattern () -removeAnn p = p $> () - --- does left pattern satisfy right pattern? -annihilateAll :: - [(Pattern (), Pattern ())] -> - Bool -annihilateAll = - foldr - (\(a, b) keep -> keep && annihilate a b) - True - --- | if left is on the right, should we get rid? -annihilate :: Pattern () -> Pattern () -> Bool -annihilate a b | a == b = True -annihilate (PWildcard _) _ = True -- wildcard trumps all -annihilate (PVar _ _) _ = True -- as does var -annihilate (PTuple _ a as) (PTuple _ b bs) = - let allPairs = zip ([a] <> NE.toList as) ([b] <> NE.toList bs) - in annihilateAll allPairs -annihilate _ _as = False diff --git a/wasm-calc4/src/Calc/Typecheck/Elaborate.hs b/wasm-calc4/src/Calc/Typecheck/Elaborate.hs index ba3fa212..2520ea7a 100644 --- a/wasm-calc4/src/Calc/Typecheck/Elaborate.hs +++ b/wasm-calc4/src/Calc/Typecheck/Elaborate.hs @@ -2,30 +2,24 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ScopedTypeVariables #-} -module Calc.Typecheck.Elaborate (elaborate, elaborateFunction, elaborateModule) where +module Calc.Typecheck.Elaborate (elaborate, + elaborateFunction, elaborateModule) where +import qualified Data.List as List +import qualified Data.List.NonEmpty as NE import Calc.ExprUtils -import Calc.PatternUtils -import Calc.Patterns.Flatten import Calc.TypeUtils import Calc.Typecheck.Error import Calc.Typecheck.Types import Calc.Types.Expr import Calc.Types.Function -import Calc.Types.Identifier import Calc.Types.Module -import Calc.Types.Pattern import Calc.Types.Prim import Calc.Types.Type -import Calc.Utils import Control.Monad (when, zipWithM) import Control.Monad.Except import Data.Bifunctor (second) -import Data.Foldable (foldrM) import Data.Functor -import qualified Data.List.NonEmpty as NE -import Data.Map.Strict (Map) -import qualified Data.Map.Strict as M elaborateModule :: forall ann. @@ -68,36 +62,6 @@ checkTypeIsEqual tyA tyB = then pure tyA else throwError (TypeMismatch tyA tyB) -checkTypesAreEqual :: NE.NonEmpty (Type ann) -> TypecheckM ann (Type ann) -checkTypesAreEqual tys = - foldrM checkTypeIsEqual (NE.head tys) (NE.tail tys) - --- given the type of the expression in a pattern match, --- check that the pattern makes sense with it -checkPattern :: - Type ann -> - Pattern ann -> - TypecheckM - ann - ( Pattern (Type ann), - Map Identifier (Type ann) - ) -checkPattern checkTy checkPat = do - case (checkTy, checkPat) of - (TTuple _ tA tRest, PTuple ann pA pRest) | length tRest == length pRest -> do - (patA, envA) <- checkPattern tA pA - (patRest, envRest) <- neUnzip <$> neZipWithM checkPattern tRest pRest - let ty = TTuple ann (getPatternAnnotation patA) (getPatternAnnotation <$> patRest) - env = envA <> mconcat (NE.toList envRest) - pure (PTuple ty patA patRest, env) - (ty, PVar _ ident) -> - pure (PVar ty ident, M.singleton ident ty) - (ty, PWildcard _) -> pure (PWildcard ty, mempty) - (ty@(TPrim _ tPrim), PLiteral _ pPrim) - | tPrim == typePrimFromPrim pPrim -> - pure (PLiteral ty pPrim, mempty) - (otherTy, otherPat) -> throwError (PatternMismatch otherPat otherTy) - inferIf :: ann -> Expr ann -> @@ -182,19 +146,16 @@ infer (ETuple ann fstExpr restExpr) = do (getOuterAnnotation typedFst) (getOuterAnnotation <$> typedRest) pure $ ETuple typ typedFst typedRest -infer (EPatternMatch ann matchExpr pats) = do - elabExpr <- infer matchExpr - let withPair (pat, patExpr) = do - (elabPat, newVars) <- checkPattern (getOuterAnnotation elabExpr) pat - elabPatExpr <- withVars (M.toList newVars) (infer patExpr) - pure (elabPat, elabPatExpr) - elabPats <- traverse withPair pats - let allTypes = getOuterAnnotation . snd <$> elabPats - typ <- checkTypesAreEqual allTypes - case generateMissing (fst <$> elabPats) of - [] -> pure () - missingPatterns -> throwError (IncompletePatterns ann missingPatterns) - pure (EPatternMatch typ elabExpr elabPats) +infer (ETupleAccess ann tup index) = do + tyTup <- infer tup + case getOuterAnnotation tyTup of + TTuple _ tyFst tyRest -> + let tyAll = zip ([0 ..] :: [Int]) (tyFst : NE.toList tyRest) + in case List.lookup (fromIntegral $ index - 1) tyAll of + Just ty -> + pure (ETupleAccess ty tyTup index) + Nothing -> throwError $ AccessingOutsideTupleBounds ann (getOuterAnnotation tyTup) index + otherTy -> throwError $ AccessingNonTuple ann otherTy infer (EApply ann fnName args) = do fn <- lookupFunction ann fnName (ty, elabArgs) <- case fn of diff --git a/wasm-calc4/src/Calc/Typecheck/Error.hs b/wasm-calc4/src/Calc/Typecheck/Error.hs index 13fe1725..bc4c877b 100644 --- a/wasm-calc4/src/Calc/Typecheck/Error.hs +++ b/wasm-calc4/src/Calc/Typecheck/Error.hs @@ -4,14 +4,13 @@ module Calc.Typecheck.Error (TypeError (..), typeErrorDiagnostic) where -import Calc.PatternUtils +import GHC.Natural import Calc.SourceSpan import Calc.TypeUtils import Calc.Types.Annotation import Calc.Types.Expr import Calc.Types.FunctionName import Calc.Types.Identifier -import Calc.Types.Pattern import Calc.Types.Type import Data.HashSet (HashSet) import qualified Data.HashSet as HS @@ -28,10 +27,10 @@ data TypeError ann | TypeMismatch (Type ann) (Type ann) | VarNotFound ann Identifier (HashSet Identifier) | FunctionNotFound ann FunctionName (HashSet FunctionName) - | PatternMismatch (Pattern ann) (Type ann) | FunctionArgumentLengthMismatch ann Int Int -- expected, actual | NonFunctionTypeFound ann (Type ann) - | IncompletePatterns ann [Pattern ()] + | AccessingNonTuple ann (Type ann) + | AccessingOutsideTupleBounds ann (Type ann) Natural deriving stock (Eq, Ord, Show) positionFromAnnotation :: @@ -168,10 +167,10 @@ typeErrorDiagnostic input e = ( mapMaybe makeThis pairs ) [] - (VarNotFound ann identifier existing) -> + (AccessingNonTuple ann ty ) -> Diag.Err Nothing - "Variable not found!" + "Accessing non-tuple" ( catMaybes [ (,) <$> positionFromAnnotation @@ -179,26 +178,31 @@ typeErrorDiagnostic input e = input ann <*> pure - ( Diag.This (prettyPrint $ "Could not find identifier " <> PP.pretty identifier) + ( Diag.This (prettyPrint $ "Expected a tuple type here but found " <> PP.pretty ty) ) ] ) - [Diag.Note $ "Available in scope: " <> prettyPrint (prettyHashset existing)] - (PatternMismatch pat ty) -> + [] + (AccessingOutsideTupleBounds ann ty index) -> Diag.Err Nothing - "Pattern mismatch!" + "Accessing item outside tuple" ( catMaybes [ (,) - <$> positionFromAnnotation filename input (getPatternAnnotation pat) - <*> pure (Diag.This (prettyPrint $ "This should have type " <> PP.pretty ty)) + <$> positionFromAnnotation + filename + input + ann + <*> pure + ( Diag.This (prettyPrint $ "Index " <> PP.pretty index <> " cannot be found in tuple " <> PP.pretty ty) + ) ] ) [] - (FunctionNotFound ann fnName existing) -> + (VarNotFound ann identifier existing) -> Diag.Err Nothing - "Function not found!" + "Variable not found!" ( catMaybes [ (,) <$> positionFromAnnotation @@ -206,15 +210,15 @@ typeErrorDiagnostic input e = input ann <*> pure - ( Diag.This (prettyPrint $ "Could not find function " <> PP.pretty fnName) + ( Diag.This (prettyPrint $ "Could not find identifier " <> PP.pretty identifier) ) ] ) [Diag.Note $ "Available in scope: " <> prettyPrint (prettyHashset existing)] - (IncompletePatterns ann missingPatterns) -> + (FunctionNotFound ann fnName existing) -> Diag.Err Nothing - "Pattern match is incomplete!" + "Function not found!" ( catMaybes [ (,) <$> positionFromAnnotation @@ -222,13 +226,11 @@ typeErrorDiagnostic input e = input ann <*> pure - ( Diag.This $ - prettyPrint $ - "Missing patterns: " <> PP.line <> prettyListToLines missingPatterns + ( Diag.This (prettyPrint $ "Could not find function " <> PP.pretty fnName) ) ] ) - [] + [Diag.Note $ "Available in scope: " <> prettyPrint (prettyHashset existing)] in Diag.addReport diag report -- | becomes "a, b, c, d" @@ -238,12 +240,6 @@ prettyHashset hs = (PP.surround PP.comma) (PP.pretty <$> HS.toList hs) -prettyListToLines :: (PP.Pretty a) => [a] -> PP.Doc ann -prettyListToLines as = - PP.concatWith - (PP.surround PP.line) - (PP.pretty <$> as) - renderWithWidth :: Int -> PP.Doc ann -> Text renderWithWidth w doc = PP.renderStrict (PP.layoutPretty layoutOptions (PP.unAnnotate doc)) where diff --git a/wasm-calc4/src/Calc/Types.hs b/wasm-calc4/src/Calc/Types.hs index 2f428857..2e4bf622 100644 --- a/wasm-calc4/src/Calc/Types.hs +++ b/wasm-calc4/src/Calc/Types.hs @@ -6,7 +6,6 @@ module Calc.Types module Calc.Types.Module, module Calc.Types.Prim, module Calc.Types.Type, - module Calc.Types.Pattern, ) where @@ -15,6 +14,5 @@ import Calc.Types.Expr import Calc.Types.Function import Calc.Types.Identifier import Calc.Types.Module -import Calc.Types.Pattern import Calc.Types.Prim import Calc.Types.Type diff --git a/wasm-calc4/src/Calc/Types/Expr.hs b/wasm-calc4/src/Calc/Types/Expr.hs index dd360a1f..f6b05305 100644 --- a/wasm-calc4/src/Calc/Types/Expr.hs +++ b/wasm-calc4/src/Calc/Types/Expr.hs @@ -6,11 +6,11 @@ module Calc.Types.Expr (Expr (..), Op (..)) where import Calc.Types.FunctionName import Calc.Types.Identifier -import Calc.Types.Pattern import Calc.Types.Prim import qualified Data.List.NonEmpty as NE import Prettyprinter ((<+>)) import qualified Prettyprinter as PP +import GHC.Natural data Expr ann = EPrim ann Prim @@ -19,7 +19,7 @@ data Expr ann | EVar ann Identifier | EApply ann FunctionName [Expr ann] | ETuple ann (Expr ann) (NE.NonEmpty (Expr ann)) - | EPatternMatch ann (Expr ann) (NE.NonEmpty (Pattern ann, Expr ann)) + | ETupleAccess ann (Expr ann) Natural deriving stock (Eq, Ord, Show, Functor, Foldable, Traversable) -- when on multilines, indent by `i`, if not then nothing @@ -44,46 +44,8 @@ instance PP.Pretty (Expr ann) where where tupleItems :: a -> NE.NonEmpty a -> [a] tupleItems b bs = b : NE.toList bs - pretty (EPatternMatch _ matchExp patterns) = - prettyPatternMatch matchExp patterns - -prettyPatternMatch :: - Expr ann -> - NE.NonEmpty (Pattern ann, Expr ann) -> - PP.Doc style -prettyPatternMatch sumExpr matches = - "match" - <+> printSubExpr sumExpr - <+> "with" - <+> PP.line - <> PP.indent - 2 - ( PP.align $ - PP.vsep - ( zipWith - (<+>) - (" " : repeat "|") - (printMatch <$> NE.toList matches) - ) - ) - where - printMatch (construct, expr') = - PP.pretty construct - <+> "->" - <+> PP.line - <> indentMulti 4 (printSubExpr expr') - --- print simple things with no brackets, and complex things inside brackets -printSubExpr :: Expr ann -> PP.Doc style -printSubExpr expr = case expr of - all'@EIf {} -> inParens all' - all'@EApply {} -> inParens all' - all'@ETuple {} -> inParens all' - all'@EPatternMatch {} -> inParens all' - a -> PP.pretty a - -inParens :: Expr ann -> PP.Doc style -inParens = PP.parens . PP.pretty + pretty (ETupleAccess _ tup nat) + = PP.pretty tup <> "." <> PP.pretty nat data Op = OpAdd diff --git a/wasm-calc4/src/Calc/Types/Pattern.hs b/wasm-calc4/src/Calc/Types/Pattern.hs deleted file mode 100644 index 9e65c789..00000000 --- a/wasm-calc4/src/Calc/Types/Pattern.hs +++ /dev/null @@ -1,34 +0,0 @@ -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE DeriveTraversable #-} -{-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE OverloadedStrings #-} - -module Calc.Types.Pattern (Pattern (..)) where - -import Calc.Types.Identifier -import Calc.Types.Prim -import qualified Data.List.NonEmpty as NE -import GHC.Generics -import qualified Prettyprinter as PP - -data Pattern ann - = PWildcard ann - | PVar ann Identifier - | PTuple ann (Pattern ann) (NE.NonEmpty (Pattern ann)) - | PLiteral ann Prim - deriving stock - ( Eq, - Ord, - Show, - Functor, - Foldable, - Generic, - Traversable - ) - -instance PP.Pretty (Pattern ann) where - pretty (PWildcard _) = "_" - pretty (PVar _ a) = PP.pretty a - pretty (PLiteral _ lit) = PP.pretty lit - pretty (PTuple _ a as) = - "(" <> PP.hsep (PP.punctuate ", " (PP.pretty <$> ([a] <> NE.toList as))) <> ")" diff --git a/wasm-calc4/src/Calc/Wasm/FromExpr.hs b/wasm-calc4/src/Calc/Wasm/FromExpr.hs index 5183abe4..32bad0af 100644 --- a/wasm-calc4/src/Calc/Wasm/FromExpr.hs +++ b/wasm-calc4/src/Calc/Wasm/FromExpr.hs @@ -5,7 +5,6 @@ module Calc.Wasm.FromExpr (fromModule) where -import Control.Monad (void) import Calc.Types.Expr import Calc.Types.Function import Calc.Types.Identifier @@ -89,25 +88,23 @@ fromExpr (EIf _ predE thenE elseE) = fromExpr (EVar _ ident) = WVar <$> lookupIdent ident fromExpr (EApply _ funcName args) = - WApply <$> lookupFunction funcName <*> traverse fromExpr args -- need to look up the function name in some sort of state + WApply <$> lookupFunction funcName + <*> traverse fromExpr args -- need to look up the function name in some sort of state fromExpr (ETuple ty a as) = do wasmType <- liftEither $ scalarFromType ty index <- addLocal Nothing wasmType let allItems = zip [0 ..] (a : NE.toList as) tupleLength = memorySizeForType ty allocate = WAllocate (fromIntegral tupleLength) + size = memorySize I32 -- we are assuming all things are the same size, which is wrong WSet index allocate <$> traverse ( \(i, item) -> - (,) i <$> fromExpr item + (,) (i * size) <$> fromExpr item ) allItems -fromExpr (EPatternMatch _ matchExpr pats) = do - wasmMatch <- fromExpr matchExpr - -- need to get items from `pat` and put them in scope - let fromPat (pat,expr) = - (,) (void pat) <$> fromExpr expr - WPatternMatch wasmMatch <$> traverse fromPat pats +fromExpr (ETupleAccess _ tup nat) + = WTupleAccess <$> fromExpr tup <*> pure nat memorySizeForType :: Type ann -> Natural memorySizeForType (TPrim _ TInt) = diff --git a/wasm-calc4/src/Calc/Wasm/Helpers.hs b/wasm-calc4/src/Calc/Wasm/Helpers.hs index 51ad26c3..00f15711 100644 --- a/wasm-calc4/src/Calc/Wasm/Helpers.hs +++ b/wasm-calc4/src/Calc/Wasm/Helpers.hs @@ -5,5 +5,5 @@ import GHC.Natural -- our memory is bits of i32s memorySize :: WasmType -> Natural -memorySize I32 = 1 -memorySize Pointer = 1 +memorySize I32 = 8 +memorySize Pointer = 8 diff --git a/wasm-calc4/src/Calc/Wasm/ToWasm.hs b/wasm-calc4/src/Calc/Wasm/ToWasm.hs index fd02676f..855ee1e2 100644 --- a/wasm-calc4/src/Calc/Wasm/ToWasm.hs +++ b/wasm-calc4/src/Calc/Wasm/ToWasm.hs @@ -5,7 +5,6 @@ module Calc.Wasm.ToWasm (moduleToWasm) where import Calc.Types.Expr import Calc.Types.FunctionName import Calc.Types.Prim -import Calc.Utils import Calc.Wasm.Allocator import Calc.Wasm.Types import Data.Maybe (catMaybes) @@ -72,14 +71,17 @@ fromExpr (WSet index container items) = <> [Wasm.SetLocal index] <> foldMap fromItem items <> [Wasm.GetLocal index] +fromExpr (WTupleAccess tup _index) = + let offset = 0 in + fromExpr tup <> [Wasm.I32Load $ Wasm.MemArg offset 0] -- | we load the bump allocator module and build on top of it moduleToWasm :: WasmModule -> Wasm.Module moduleToWasm (WasmModule {wmFunctions}) = - let functions = mapWithIndex (uncurry fromFunction) (ltrace "wmFunctions" wmFunctions) + let functions = mapWithIndex (uncurry fromFunction) wmFunctions types = typeFromFunction <$> wmFunctions exports = catMaybes $ mapWithIndex (uncurry exportFromFunction) wmFunctions - in ltrace "generated module" $ + in moduleWithAllocator { Wasm.types = (Wasm.types moduleWithAllocator !! 0) : types, Wasm.functions = (head (Wasm.functions moduleWithAllocator)) : functions, diff --git a/wasm-calc4/src/Calc/Wasm/Types.hs b/wasm-calc4/src/Calc/Wasm/Types.hs index 02893e0c..e9811ec0 100644 --- a/wasm-calc4/src/Calc/Wasm/Types.hs +++ b/wasm-calc4/src/Calc/Wasm/Types.hs @@ -12,8 +12,6 @@ import Calc.Types.Expr import Calc.Types.Function import Calc.Types.Prim import GHC.Natural -import Calc.Types.Pattern -import qualified Data.List.NonEmpty as NE data WasmType = I32 @@ -45,6 +43,5 @@ data WasmExpr | WApply Natural [WasmExpr] | WAllocate Natural | WSet Natural WasmExpr [(Natural, WasmExpr)] -- `(1,2)` is WSet 3 (WAllocate 2) [(0, 1),(1, 2)] - | WPatternMatch WasmExpr (NE.NonEmpty (Pattern (),WasmExpr)) - + | WTupleAccess WasmExpr Natural deriving stock (Eq, Ord, Show) diff --git a/wasm-calc4/test/Main.hs b/wasm-calc4/test/Main.hs index 6ac25035..c6f18536 100644 --- a/wasm-calc4/test/Main.hs +++ b/wasm-calc4/test/Main.hs @@ -3,7 +3,6 @@ module Main (main) where import Test.Hspec import qualified Test.Interpreter.InterpreterSpec import qualified Test.Parser.ParserSpec -import qualified Test.Patterns.PatternsSpec import qualified Test.Typecheck.TypecheckSpec import qualified Test.Wasm.WasmSpec @@ -12,5 +11,4 @@ main = hspec $ do Test.Parser.ParserSpec.spec Test.Interpreter.InterpreterSpec.spec Test.Typecheck.TypecheckSpec.spec - Test.Patterns.PatternsSpec.spec Test.Wasm.WasmSpec.spec diff --git a/wasm-calc4/test/Test/Helpers.hs b/wasm-calc4/test/Test/Helpers.hs index af4adff2..c6e0d205 100644 --- a/wasm-calc4/test/Test/Helpers.hs +++ b/wasm-calc4/test/Test/Helpers.hs @@ -5,13 +5,9 @@ module Test.Helpers bool, var, tuple, - patternMatch, tyInt, tyBool, tyTuple, - patTuple, - patInt, - patBool, ) where @@ -33,10 +29,6 @@ tuple = \case (a : b : rest) -> ETuple mempty a (b NE.:| rest) _ -> error "not enough items for tuple" -patternMatch :: (Monoid ann) => Expr ann -> [(Pattern ann, Expr ann)] -> Expr ann -patternMatch matchExpr matches = - EPatternMatch mempty matchExpr (NE.fromList matches) - tyInt :: (Monoid ann) => Type ann tyInt = TPrim mempty TInt @@ -47,14 +39,3 @@ tyTuple :: (Monoid ann) => [Type ann] -> Type ann tyTuple = \case (a : b : rest) -> TTuple mempty a (b NE.:| rest) _ -> error "not enough items for tyTuple" - -patTuple :: (Monoid ann) => [Pattern ann] -> Pattern ann -patTuple = \case - (a : b : rest) -> PTuple mempty a (b NE.:| rest) - _ -> error "not enough items for patTuple" - -patInt :: (Monoid ann) => Integer -> Pattern ann -patInt = PLiteral mempty . PInt - -patBool :: (Monoid ann) => Bool -> Pattern ann -patBool = PLiteral mempty . PBool diff --git a/wasm-calc4/test/Test/Interpreter/InterpreterSpec.hs b/wasm-calc4/test/Test/Interpreter/InterpreterSpec.hs index 80b71743..a4cf16aa 100644 --- a/wasm-calc4/test/Test/Interpreter/InterpreterSpec.hs +++ b/wasm-calc4/test/Test/Interpreter/InterpreterSpec.hs @@ -44,7 +44,7 @@ spec = do let cases = [ ("1 + 1", "2"), ("function increment(a: Integer) { a + 1 } increment(-11)", "-10"), - ("function swap(pair: (Integer,Boolean)) { case pair of (a,b) -> (b,a) } swap((1,True))", "(True, 1)") + ("function swap(pair: (Integer,Boolean)) { (pair.2, pair.1) } swap((1,True))", "(True, 1)") ] traverse_ ( \(input, expect) -> @@ -63,7 +63,7 @@ spec = do ("2 + 2 == 5", "False"), ("if False then True else False", "False"), ("if 1 == 1 then 6 else 5", "6"), - ("case (1, True) of (a,False) -> a | (_,c) -> c", "True") + ("(1, True).2", "True") ] traverse_ ( \(input, expect) -> diff --git a/wasm-calc4/test/Test/Parser/ParserSpec.hs b/wasm-calc4/test/Test/Parser/ParserSpec.hs index 85cc5487..fe0e11d5 100644 --- a/wasm-calc4/test/Test/Parser/ParserSpec.hs +++ b/wasm-calc4/test/Test/Parser/ParserSpec.hs @@ -94,20 +94,7 @@ spec = do ("a + 1", EInfix () OpAdd (var "a") (int 1)), ("add(1,2)", EApply () "add" [int 1, int 2]), ("go()", EApply () "go" []), - ( "case (1,2,3) of (5,6,7) -> True | (1,2,3) -> False", - patternMatch - (tuple [int 1, int 2, int 3]) - [ (patTuple [patInt 5, patInt 6, patInt 7], bool True), - (patTuple [patInt 1, patInt 2, patInt 3], bool False) - ] - ), - ( "case a of 100 -> True | _ -> False", - patternMatch - (var "a") - [ (patInt 100, bool True), - (PWildcard (), bool False) - ] - ) + ("tuple.1",ETupleAccess () (var "tuple") 1) ] traverse_ ( \(str, expr) -> it (T.unpack str) $ do diff --git a/wasm-calc4/test/Test/Patterns/PatternsSpec.hs b/wasm-calc4/test/Test/Patterns/PatternsSpec.hs deleted file mode 100644 index 85ea3324..00000000 --- a/wasm-calc4/test/Test/Patterns/PatternsSpec.hs +++ /dev/null @@ -1,77 +0,0 @@ -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TypeApplications #-} - -module Test.Patterns.PatternsSpec (spec) where - -import Calc -import Calc.Patterns.Flatten (generateMissing) -import qualified Data.List.NonEmpty as NE -import Test.Helpers -import Test.Hspec - -spec :: Spec -spec = do - describe "PatternsSpec" $ do - it "Wildcard is exhaustive" $ do - let pats = NE.fromList [PWildcard tyInt] - generateMissing @() pats `shouldBe` [] - it "True needs false" $ do - let pats = NE.fromList [PLiteral tyBool (PBool True)] - generateMissing @() pats `shouldBe` [patBool False] - it "False needs True" $ do - let pats = NE.fromList [PLiteral tyBool (PBool False)] - generateMissing @() pats `shouldBe` [patBool True] - it "False and True needs nothing" $ do - let pats = NE.fromList [PLiteral tyBool (PBool False), PLiteral tyBool (PBool True)] - generateMissing @() pats `shouldBe` [] - it "Tuple of two wildcards needs nothing " $ do - let pats = - NE.fromList - [ PTuple - (tyTuple [tyBool, tyBool]) - (PWildcard tyBool) - (NE.fromList [PWildcard tyBool]) - ] - generateMissing @() pats `shouldBe` [] - - it "Tuple of one wildcard and one true needs a false" $ do - let pats = - NE.fromList - [ PTuple (tyTuple [tyBool, tyBool]) (PWildcard tyBool) (NE.fromList [PLiteral tyBool (PBool True)]) - ] - generateMissing @() pats - `shouldBe` [ patTuple [patBool True, patBool False], - patTuple [patBool False, patBool False] - ] - - it "Tuple of one true and one false needs a bunch" $ do - let pats = - NE.fromList - [ PTuple (tyTuple [tyBool, tyBool]) (PLiteral tyBool (PBool False)) (NE.fromList [PLiteral tyBool (PBool True)]) - ] - generateMissing @() pats - `shouldBe` [ patTuple [patBool False, patBool False], - patTuple [patBool True, patBool True], - patTuple [patBool True, patBool False] - ] - - it "Tuple of booleans with some things supplied" $ do - let pats = - NE.fromList - [ PTuple (tyTuple [tyBool, tyBool]) (PLiteral tyBool (PBool False)) (NE.fromList [PLiteral tyBool (PBool True)]), - PTuple (tyTuple [tyBool, tyBool]) (PLiteral tyBool (PBool True)) (NE.fromList [PLiteral tyBool (PBool False)]) - ] - generateMissing @() pats - `shouldBe` [ patTuple [patBool False, patBool False], - patTuple [patBool True, patBool True] - ] - - it "Tuple of wildcard and boolean" $ do - let pats = - NE.fromList - [ PTuple (tyTuple [tyBool, tyBool]) (PLiteral tyBool (PBool False)) (NE.fromList [PWildcard tyBool]), - PTuple (tyTuple [tyBool, tyBool]) (PLiteral tyBool (PBool True)) (NE.fromList [PLiteral tyBool (PBool False)]) - ] - generateMissing @() pats - `shouldBe` [ patTuple [patBool True, patBool True] - ] diff --git a/wasm-calc4/test/Test/Typecheck/TypecheckSpec.hs b/wasm-calc4/test/Test/Typecheck/TypecheckSpec.hs index de13366a..b68d54cf 100644 --- a/wasm-calc4/test/Test/Typecheck/TypecheckSpec.hs +++ b/wasm-calc4/test/Test/Typecheck/TypecheckSpec.hs @@ -104,8 +104,7 @@ spec = do ("if True then 1 else 2", "Integer"), ("if False then True else False", "Boolean"), ("(1,2,True)", "(Integer,Integer,Boolean)"), - ("case (1,2,3) of (a,b,_) -> a + b", "Integer"), - ("case (1,True) of (2,b) -> b | _ -> False", "Boolean") + ("(1,2,3).2", "Integer") ] describe "Successfully typechecking expressions" $ do @@ -117,8 +116,7 @@ spec = do ("1 + True", InfixTypeMismatch OpAdd [(tyInt, tyBool)]), ("True + False", InfixTypeMismatch OpAdd [(tyInt, tyBool), (tyInt, tyBool)]), ("1 * False", InfixTypeMismatch OpMultiply [(TPrim () TInt, TPrim () TBool)]), - ("True - 1", InfixTypeMismatch OpSubtract [(TPrim () TInt, TPrim () TBool)]), - ("case (1,True) of (a, False) -> a | (_,c) -> c", TypeMismatch tyBool tyInt) + ("True - 1", InfixTypeMismatch OpSubtract [(TPrim () TInt, TPrim () TBool)]) ] describe "Failing typechecking expressions" $ do diff --git a/wasm-calc4/test/Test/Wasm/WasmSpec.hs b/wasm-calc4/test/Test/Wasm/WasmSpec.hs index 9bcd14e3..ddc11fb4 100644 --- a/wasm-calc4/test/Test/Wasm/WasmSpec.hs +++ b/wasm-calc4/test/Test/Wasm/WasmSpec.hs @@ -26,13 +26,13 @@ testCompileExpr (input, result) = it (show input) $ do resp `shouldBe` Just [result] joinLines :: [Text] -> Text -joinLines = foldr (\a b -> a <> " " <> b) "" +joinLines = foldr (\a b -> a <> "\n" <> b) "" spec :: Spec spec = do - fdescribe "WasmSpec" $ do + describe "WasmSpec" $ do let testVals = - [ {-("42", Wasm.VI32 42), + [ ("42", Wasm.VI32 42), ("(1 + 1)", Wasm.VI32 2), ("1 + 2 + 3 + 4 + 5 + 6", Wasm.VI32 21), ("6 * 6", Wasm.VI32 36), @@ -50,7 +50,6 @@ spec = do ("function increment(a: Integer) { a + 1 } increment(41)", Wasm.VI32 42), ("function sum(a: Integer, b: Integer) { a + b } sum(20,22)", Wasm.VI32 42), ("function inc(a: Integer) { a + 1 } inc(inc(inc(inc(0))))", Wasm.VI32 4), - -} ( joinLines [ "function ignoreTuple(pair: (Integer, Boolean)) { True }", "ignoreTuple((1,True))" @@ -58,8 +57,14 @@ spec = do Wasm.VI32 1 ), ( joinLines - [ "function swapIntAndBool(pair: (Integer, Boolean)) { case pair of (a, b) -> (b, a) }", - "function fst(pair: (Boolean, Integer)) { case pair of (a,_) -> a }", + [ + "(1,True).1" + ], + Wasm.VI32 1 -- note we cannot make polymorphic versions of these functions yet, although we will + ), + ( joinLines + [ "function swapIntAndBool(pair: (Integer, Boolean)) { (pair.2, pair.1) }", + "function fst(pair: (Boolean, Integer)) { pair.1 }", "fst(swapIntAndBool((1,True)))" ], Wasm.VI32 1 -- note we cannot make polymorphic versions of these functions yet, although we will diff --git a/wasm-calc4/wasm-calc4.cabal b/wasm-calc4/wasm-calc4.cabal index 507ba7e6..dda952bc 100644 --- a/wasm-calc4/wasm-calc4.cabal +++ b/wasm-calc4/wasm-calc4.cabal @@ -60,13 +60,10 @@ common shared Calc.Parser.Function Calc.Parser.Identifier Calc.Parser.Module - Calc.Parser.Pattern Calc.Parser.Primitives Calc.Parser.Shared Calc.Parser.Type Calc.Parser.Types - Calc.Patterns.Flatten - Calc.PatternUtils Calc.Repl Calc.SourceSpan Calc.Typecheck.Elaborate @@ -79,11 +76,16 @@ common shared Calc.Types.FunctionName Calc.Types.Identifier Calc.Types.Module - Calc.Types.Pattern Calc.Types.Prim Calc.Types.Type Calc.TypeUtils Calc.Utils + Calc.Wasm.Allocator + Calc.Wasm.FromExpr + Calc.Wasm.Helpers + Calc.Wasm.Run + Calc.Wasm.ToWasm + Calc.Wasm.Types library import: shared