From 6b74d84c8618e9c5a2b1315bef8ba6dc6ada0db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Czajka?= <62751+lukaszcz@users.noreply.github.com> Date: Mon, 26 Feb 2024 14:27:25 +0100 Subject: [PATCH] Generalize backend builtin primitive operations (#2653) * Closes #2651 * Depends on #2650 --- src/Juvix/Compiler/Asm/Extra/Base.hs | 10 +- src/Juvix/Compiler/Asm/Extra/Recursors.hs | 99 +++++++++---------- src/Juvix/Compiler/Asm/Interpreter.hs | 99 ++++--------------- src/Juvix/Compiler/Asm/Language.hs | 45 ++------- src/Juvix/Compiler/Asm/Pretty/Base.hs | 14 +-- .../Compiler/Asm/Transformation/Prealloc.hs | 8 +- .../Compiler/Asm/Translation/FromSource.hs | 24 ++--- .../Compiler/Asm/Translation/FromTree.hs | 28 ++---- .../Compiler/Backend/C/Translation/FromReg.hs | 39 +++++--- .../Compiler/Nockma/Translation/FromTree.hs | 37 ++++--- src/Juvix/Compiler/Reg/Extra/Base.hs | 36 +++---- src/Juvix/Compiler/Reg/Extra/Info.hs | 28 ++---- src/Juvix/Compiler/Reg/Interpreter.hs | 86 ++++------------ src/Juvix/Compiler/Reg/Language.hs | 53 +++------- src/Juvix/Compiler/Reg/Language/Base.hs | 2 + src/Juvix/Compiler/Reg/Pretty/Base.hs | 51 +++------- src/Juvix/Compiler/Reg/Translation/FromAsm.hs | 46 +++------ .../Compiler/Reg/Translation/FromSource.hs | 70 +++++-------- src/Juvix/Compiler/Tree/Evaluator.hs | 69 ++----------- src/Juvix/Compiler/Tree/Evaluator/Builtins.hs | 77 +++++++++++++++ src/Juvix/Compiler/Tree/EvaluatorEff.hs | 61 ++---------- src/Juvix/Compiler/Tree/EvaluatorSem.hs | 61 ++---------- src/Juvix/Compiler/Tree/Language.hs | 25 ++--- src/Juvix/Compiler/Tree/Language/Builtins.hs | 25 +++++ src/Juvix/Compiler/Tree/Pretty/Base.hs | 32 +++--- .../Compiler/Tree/Transformation/Validate.hs | 32 +++--- .../Compiler/Tree/Translation/FromAsm.hs | 18 +--- .../Compiler/Tree/Translation/FromCore.hs | 22 ++--- .../Compiler/Tree/Translation/FromSource.hs | 24 ++--- 29 files changed, 458 insertions(+), 763 deletions(-) create mode 100644 src/Juvix/Compiler/Tree/Evaluator/Builtins.hs create mode 100644 src/Juvix/Compiler/Tree/Language/Builtins.hs diff --git a/src/Juvix/Compiler/Asm/Extra/Base.hs b/src/Juvix/Compiler/Asm/Extra/Base.hs index f2221422d8..0ac477977e 100644 --- a/src/Juvix/Compiler/Asm/Extra/Base.hs +++ b/src/Juvix/Compiler/Asm/Extra/Base.hs @@ -6,15 +6,21 @@ import Juvix.Compiler.Asm.Language mkInstr :: Instruction -> Command mkInstr = Instr . CmdInstr emptyInfo -mkBinop :: Opcode -> Command +mkBinop :: BinaryOp -> Command mkBinop = mkInstr . Binop mkInstr' :: Maybe Location -> Instruction -> Command mkInstr' loc = Instr . CmdInstr (CommandInfo loc) -mkBinop' :: Maybe Location -> Opcode -> Command +mkBinop' :: Maybe Location -> BinaryOp -> Command mkBinop' loc = mkInstr' loc . Binop +mkUnop :: UnaryOp -> Command +mkUnop = mkInstr . Unop + +mkUnop' :: Maybe Location -> UnaryOp -> Command +mkUnop' loc = mkInstr' loc . Unop + isFinalInstr :: Instruction -> Bool isFinalInstr = \case Return -> True diff --git a/src/Juvix/Compiler/Asm/Extra/Recursors.hs b/src/Juvix/Compiler/Asm/Extra/Recursors.hs index 6e3c248017..42ab09b313 100644 --- a/src/Juvix/Compiler/Asm/Extra/Recursors.hs +++ b/src/Juvix/Compiler/Asm/Extra/Recursors.hs @@ -78,29 +78,10 @@ recurse' sig = go True fixMemInstr :: Memory -> Instruction -> Sem r Memory fixMemInstr mem instr = case instr of - Binop IntAdd -> - fixMemIntOp mem - Binop IntSub -> - fixMemIntOp mem - Binop IntMul -> - fixMemIntOp mem - Binop IntDiv -> - fixMemIntOp mem - Binop IntMod -> - fixMemIntOp mem - Binop IntLt -> - fixMemBinOp mem mkTypeInteger mkTypeInteger mkTypeBool - Binop IntLe -> - fixMemBinOp mem mkTypeInteger mkTypeInteger mkTypeBool - Binop ValEq -> - fixMemBinOp mem TyDynamic TyDynamic mkTypeBool - Binop StrConcat -> - fixMemBinOp mem TyString TyString TyString - ValShow -> - return (pushValueStack TyString (popValueStack 1 mem)) - StrToInt -> do - checkValueStack' loc (sig ^. recursorInfoTable) [TyString] mem - return (pushValueStack mkTypeInteger (popValueStack 1 mem)) + Binop op -> + fixMemBinop mem op + Unop op -> + fixMemUnop mem op Push val -> do ty <- getValueType' loc (sig ^. recursorInfoTable) mem val return (pushValueStack ty mem) @@ -115,12 +96,6 @@ recurse' sig = go True return mem Failure -> return $ pushValueStack TyDynamic (popValueStack 1 mem) - ArgsNum -> do - when (null (mem ^. memoryValueStack)) $ - throw $ - AsmError loc "empty value stack" - checkFunType (topValueStack' 0 mem) - return $ pushValueStack mkTypeInteger (popValueStack 1 mem) Prealloc {} -> return mem AllocConstr tag -> do @@ -157,13 +132,48 @@ recurse' sig = go True return (dropTempStack mem) fixMemIntOp :: Memory -> Sem r Memory - fixMemIntOp mem = fixMemBinOp mem mkTypeInteger mkTypeInteger mkTypeInteger + fixMemIntOp mem = fixMemBinOp' mem mkTypeInteger mkTypeInteger mkTypeInteger - fixMemBinOp :: Memory -> Type -> Type -> Type -> Sem r Memory - fixMemBinOp mem ty0 ty1 rty = do + fixMemBinOp' :: Memory -> Type -> Type -> Type -> Sem r Memory + fixMemBinOp' mem ty0 ty1 rty = do checkValueStack' loc (sig ^. recursorInfoTable) [ty0, ty1] mem return $ pushValueStack rty (popValueStack 2 mem) + fixMemBinop :: Memory -> BinaryOp -> Sem r Memory + fixMemBinop mem op = case op of + OpIntAdd -> + fixMemIntOp mem + OpIntSub -> + fixMemIntOp mem + OpIntMul -> + fixMemIntOp mem + OpIntDiv -> + fixMemIntOp mem + OpIntMod -> + fixMemIntOp mem + OpIntLt -> + fixMemBinOp' mem mkTypeInteger mkTypeInteger mkTypeBool + OpIntLe -> + fixMemBinOp' mem mkTypeInteger mkTypeInteger mkTypeBool + OpEq -> + fixMemBinOp' mem TyDynamic TyDynamic mkTypeBool + OpStrConcat -> + fixMemBinOp' mem TyString TyString TyString + + fixMemUnop :: Memory -> UnaryOp -> Sem r Memory + fixMemUnop mem op = case op of + OpShow -> + return (pushValueStack TyString (popValueStack 1 mem)) + OpStrToInt -> do + checkValueStack' loc (sig ^. recursorInfoTable) [TyString] mem + return (pushValueStack mkTypeInteger (popValueStack 1 mem)) + OpArgsNum -> do + when (null (mem ^. memoryValueStack)) $ + throw $ + AsmError loc "empty value stack" + checkFunType (topValueStack' 0 mem) + return $ pushValueStack mkTypeInteger (popValueStack 1 mem) + fixMemExtendClosure :: Memory -> InstrExtendClosure -> Sem r Memory fixMemExtendClosure mem InstrExtendClosure {..} = do when (length (mem ^. memoryValueStack) < _extendClosureArgsNum + 1) $ @@ -363,27 +373,9 @@ recurseS' sig = go True fixStackInstr :: StackInfo -> Instruction -> Sem r StackInfo fixStackInstr si instr = case instr of - Binop IntAdd -> - fixStackBinOp si - Binop IntSub -> - fixStackBinOp si - Binop IntMul -> - fixStackBinOp si - Binop IntDiv -> - fixStackBinOp si - Binop IntMod -> + Binop {} -> fixStackBinOp si - Binop IntLt -> - fixStackBinOp si - Binop IntLe -> - fixStackBinOp si - Binop ValEq -> - fixStackBinOp si - Binop StrConcat -> - fixStackBinOp si - ValShow -> - return si - StrToInt -> + Unop {} -> return si Push {} -> do return (stackInfoPushValueStack 1 si) @@ -395,9 +387,6 @@ recurseS' sig = go True return si Failure -> return si - ArgsNum -> - -- push + pop = nop - return si Prealloc {} -> return si AllocConstr tag -> do diff --git a/src/Juvix/Compiler/Asm/Interpreter.hs b/src/Juvix/Compiler/Asm/Interpreter.hs index d4268dab64..623aa16979 100644 --- a/src/Juvix/Compiler/Asm/Interpreter.hs +++ b/src/Juvix/Compiler/Asm/Interpreter.hs @@ -13,7 +13,7 @@ import Juvix.Compiler.Asm.Interpreter.Base import Juvix.Compiler.Asm.Interpreter.Extra import Juvix.Compiler.Asm.Interpreter.Runtime import Juvix.Compiler.Asm.Pretty -import Text.Read (readMaybe) +import Juvix.Compiler.Tree.Evaluator.Builtins -- | Interpret JuvixAsm code for a single function. The returned Val is the -- value on top of the value stack at exit, i.e., when executing a toplevel @@ -73,45 +73,8 @@ runCodeR infoTable funInfo = goCode (funInfo ^. functionCode) >> popLastValueSta goInstr :: (Member Runtime r) => Maybe Location -> Instruction -> Code -> Sem r () goInstr loc instr cont = case instr of - Binop IntAdd -> - goIntBinOp (\x y -> ValInteger (x + y)) >> goCode cont - Binop IntSub -> - goIntBinOp (\x y -> ValInteger (x - y)) >> goCode cont - Binop IntMul -> - goIntBinOp (\x y -> ValInteger (x * y)) >> goCode cont - Binop IntDiv -> do - goIntBinOp' - ( \x y -> - if - | y == 0 -> runtimeError "division by zero" - | otherwise -> return $ ValInteger (x `quot` y) - ) - goCode cont - Binop IntMod -> do - goIntBinOp' - ( \x y -> - if - | y == 0 -> runtimeError "division by zero" - | otherwise -> return $ ValInteger (x `rem` y) - ) - goCode cont - Binop IntLt -> - goIntBinOp (\x y -> ValBool (x < y)) >> goCode cont - Binop IntLe -> - goIntBinOp (\x y -> ValBool (x <= y)) >> goCode cont - Binop ValEq -> - goBinOp (\x y -> ValBool (x == y)) >> goCode cont - Binop StrConcat -> - goStrBinOp (\x y -> ValString (x <> y)) >> goCode cont - ValShow -> do - v <- popValueStack - pushValueStack (ValString (printVal v)) - goCode cont - StrToInt -> do - v <- popValueStack - i <- valToInt v - pushValueStack (ValInteger i) - goCode cont + Binop op -> goBinOp (evalBinop op) >> goCode cont + Unop op -> goUnop (evalUnop infoTable op) >> goCode cont Push ref -> do v <- getVal ref pushValueStack v @@ -120,22 +83,14 @@ runCodeR infoTable funInfo = goCode (funInfo ^. functionCode) >> popLastValueSta popValueStack >> goCode cont Trace -> do v <- topValueStack - logMessage (printVal v) + logMessage (printValue infoTable v) goCode cont Dump -> do dumpState goCode cont Failure -> do v <- topValueStack - runtimeError $ mappend "failure: " (printVal v) - ArgsNum -> do - v <- popValueStack - case v of - ValClosure cl -> do - let n = lookupFunInfo infoTable (cl ^. closureSymbol) ^. functionArgsNum - length (cl ^. closureArgs) - pushValueStack (ValInteger (toInteger n)) - goCode cont - _ -> runtimeError "invalid operation: expected closure on top of value stack" + runtimeError $ mappend "failure: " (printValue infoTable v) Prealloc {} -> goCode cont AllocConstr tag -> do @@ -189,26 +144,17 @@ runCodeR infoTable funInfo = goCode (funInfo ^. functionCode) >> popLastValueSta v <- op v1 v2 pushValueStack v - goBinOp :: (Member Runtime r) => (Val -> Val -> Val) -> Sem r () - goBinOp op = goBinOp' (\x y -> return (op x y)) - - goIntBinOp' :: (Member Runtime r) => (Integer -> Integer -> Sem r Val) -> Sem r () - goIntBinOp' op = goBinOp' $ \v1 v2 -> - case (v1, v2) of - (ValInteger i1, ValInteger i2) -> op i1 i2 - _ -> runtimeError "invalid operation: expected two integers on value stack" + goBinOp :: (Member Runtime r) => (Val -> Val -> Either Text Val) -> Sem r () + goBinOp op = goBinOp' (\x y -> eitherToError (op x y)) - goIntBinOp :: (Member Runtime r) => (Integer -> Integer -> Val) -> Sem r () - goIntBinOp op = goIntBinOp' (\v1 v2 -> return (op v1 v2)) - - goStrBinOp' :: (Member Runtime r) => (Text -> Text -> Sem r Val) -> Sem r () - goStrBinOp' op = goBinOp' $ \v1 v2 -> - case (v1, v2) of - (ValString s1, ValString s2) -> op s1 s2 - _ -> runtimeError "invalid operation: expected two strings on value stack" + goUnop' :: (Member Runtime r) => (Val -> Sem r Val) -> Sem r () + goUnop' op = do + v <- popValueStack + v' <- op v + pushValueStack v' - goStrBinOp :: (Member Runtime r) => (Text -> Text -> Val) -> Sem r () - goStrBinOp op = goStrBinOp' (\v1 v2 -> return (op v1 v2)) + goUnop :: (Member Runtime r) => (Val -> Either Text Val) -> Sem r () + goUnop op = goUnop' (eitherToError . op) getConstantVal :: Constant -> Val getConstantVal = \case @@ -331,19 +277,10 @@ runCodeR infoTable funInfo = goCode (funInfo ^. functionCode) >> popLastValueSta goCode (fi ^. functionCode) _ -> runtimeError "invalid indirect call: expected closure on top of value stack" - printVal :: Val -> Text - printVal = \case - ValString s -> s - v -> ppPrint infoTable v - - valToInt :: (Member Runtime r) => Val -> Sem r Integer - valToInt = \case - ValString s -> - case readMaybe (fromText s) of - Just i -> return i - Nothing -> runtimeError "string to integer conversion error" - ValInteger i -> return i - _ -> runtimeError "integer conversion error" + eitherToError :: (Member Runtime r) => Either Text Val -> Sem r Val + eitherToError = \case + Left err -> runtimeError err + Right v -> return v -- | Interpret JuvixAsm code and the resulting IO actions. runCodeIO :: InfoTable -> FunctionInfo -> IO Val diff --git a/src/Juvix/Compiler/Asm/Language.hs b/src/Juvix/Compiler/Asm/Language.hs index 52254d43e0..107eb1a18c 100644 --- a/src/Juvix/Compiler/Asm/Language.hs +++ b/src/Juvix/Compiler/Asm/Language.hs @@ -9,10 +9,12 @@ module Juvix.Compiler.Asm.Language ( module Juvix.Compiler.Asm.Language, module Juvix.Compiler.Tree.Language.Base, + module Juvix.Compiler.Tree.Language.Builtins, ) where import Juvix.Compiler.Tree.Language.Base +import Juvix.Compiler.Tree.Language.Builtins -- In what follows, when referring to the stack we mean the current local value -- stack, unless otherwise stated. By stack[n] we denote the n-th cell from the @@ -35,12 +37,10 @@ data CallType data Instruction = -- | An instruction which takes its operands from the two top stack cells, -- pops the stack by two, and then pushes the result. - Binop Opcode - | -- | Convert the top stack cell to a string. JVA opcode: 'show'. - ValShow - | -- | Convert a string on top of the stack into an integer. JVA opcode: - -- 'atoi'. - StrToInt + Binop BinaryOp + | -- | An instruction which on the top of the value stack, replacing it with + -- the result of a unary operation. + Unop UnaryOp | -- | Push a value on top of the stack. JVA opcode: 'push '. Push Value | -- | Pop the stack. JVA opcode: 'pop'. @@ -53,10 +53,6 @@ data Instruction | -- | Interrupt execution with a runtime error printing the value on top of -- the stack. JVA opcode: 'fail'. Failure - | -- | Computes the number of expected arguments for the closure on top of the - -- stack, pops the stack and pushes the result on top of the stack. JVA - -- opcode: 'argsnum'. - ArgsNum | -- | Preallocate memory. This instruction is inserted automatically before -- translation to JuvixReg. It does not occur in JVA files. Prealloc InstrPrealloc @@ -116,35 +112,6 @@ data Instruction -- JVA opcode: 'ret'. Return -data Opcode - = -- | Add stack[0] + stack[1], pop the stack by two, and push the result. JVA - -- opcode: 'add'. - IntAdd - | -- | Subtract stack[0] - stack[1], pop the stack by two, and push the - -- result. JVA opcode: 'sub'. - IntSub - | -- | Multiply stack[0] * stack[1], pop the stack by two, and push the - -- result. JVA opcode 'mul'. - IntMul - | -- | Divide stack[0] / stack[1], pop the stack by two, and push the result. - -- JVA opcode: 'div'. - IntDiv - | -- | Calculate modulus stack[0] % stack[1], pop the stack by two, and push - -- the result. JVA opcode: 'mod'. - IntMod - | -- | Compare stack[0] < stack[1], pop the stack by two, and push the result. - -- JVA opcode: 'lt'. - IntLt - | -- | Compare stack[0] <= stack[1], pop the stack by two, and push the - -- result. JVA opcode: 'le'. - IntLe - | -- | Compare the two top stack cells with structural equality, pop the stack - -- by two, and push the result. JVA opcode: 'eq'. - ValEq - | -- | Concatenate two string on top of the stack, pop the stack by two, and - -- push the result. JVA opcode: 'strcat'. - StrConcat - newtype InstrPrealloc = InstrPrealloc { -- | How many words to preallocate? _preallocWordsNum :: Int diff --git a/src/Juvix/Compiler/Asm/Pretty/Base.hs b/src/Juvix/Compiler/Asm/Pretty/Base.hs index 16629ac338..b0414d49e2 100644 --- a/src/Juvix/Compiler/Asm/Pretty/Base.hs +++ b/src/Juvix/Compiler/Asm/Pretty/Base.hs @@ -93,23 +93,13 @@ instance PrettyCode Type where instance PrettyCode Instruction where ppCode :: (Member (Reader Options) r) => Instruction -> Sem r (Doc Ann) ppCode = \case - Binop IntAdd -> return $ primitive Str.instrAdd - Binop IntSub -> return $ primitive Str.instrSub - Binop IntMul -> return $ primitive Str.instrMul - Binop IntDiv -> return $ primitive Str.instrDiv - Binop IntMod -> return $ primitive Str.instrMod - Binop IntLt -> return $ primitive Str.instrLt - Binop IntLe -> return $ primitive Str.instrLe - Binop ValEq -> return $ primitive Str.instrEq - Binop StrConcat -> return $ primitive Str.instrStrConcat - ValShow -> return $ primitive Str.instrShow - StrToInt -> return $ primitive Str.instrStrToInt + Binop op -> Tree.ppCode op + Unop op -> Tree.ppCode op Push val -> (primitive Str.instrPush <+>) <$> ppCode val Pop -> return $ primitive Str.instrPop Trace -> return $ primitive Str.instrTrace Dump -> return $ primitive Str.instrDump Failure -> return $ primitive Str.instrFailure - ArgsNum -> return $ primitive Str.instrArgsNum Prealloc InstrPrealloc {..} -> return $ primitive Str.instrPrealloc <+> integer _preallocWordsNum AllocConstr tag -> (primitive Str.instrAlloc <+>) <$> Tree.ppConstrName tag diff --git a/src/Juvix/Compiler/Asm/Transformation/Prealloc.hs b/src/Juvix/Compiler/Asm/Transformation/Prealloc.hs index 20ff3fd0f0..ce2d03adaa 100644 --- a/src/Juvix/Compiler/Asm/Transformation/Prealloc.hs +++ b/src/Juvix/Compiler/Asm/Transformation/Prealloc.hs @@ -42,11 +42,11 @@ computeCodePrealloc maxArgsNum tab code = prealloc <$> foldS sig code (0, []) TailCall {} -> return (0, cmd : prealloc acc) CallClosures {} -> return (0, cmd : prealloc acc) TailCallClosures {} -> return (0, cmd : prealloc acc) - Binop StrConcat -> do + Binop OpStrConcat -> do opts <- ask let size = opts ^. optLimits . limitsMaxStringSize return (k + size, cmd : c) - ValShow -> do + Unop OpShow -> do opts <- ask let size = opts ^. optLimits . limitsMaxStringSize return (k + size, cmd : c) @@ -131,11 +131,11 @@ checkCodePrealloc maxArgsNum tab code = do opts <- ask let size = opts ^. optLimits . limitsClosureHeadSize + maxArgsNum return $ \k -> cont (k - size) - Binop StrConcat -> do + Binop OpStrConcat -> do opts <- ask let size = opts ^. optLimits . limitsMaxStringSize return $ \k -> cont (k - size) - ValShow -> do + Unop OpShow -> do opts <- ask let size = opts ^. optLimits . limitsMaxStringSize return $ \k -> cont (k - size) diff --git a/src/Juvix/Compiler/Asm/Translation/FromSource.hs b/src/Juvix/Compiler/Asm/Translation/FromSource.hs index e92612f9dc..222d10b2f0 100644 --- a/src/Juvix/Compiler/Asm/Translation/FromSource.hs +++ b/src/Juvix/Compiler/Asm/Translation/FromSource.hs @@ -58,27 +58,27 @@ command = do let loc = Just i case txt of "add" -> - return $ mkBinop' loc IntAdd + return $ mkBinop' loc OpIntAdd "sub" -> - return $ mkBinop' loc IntSub + return $ mkBinop' loc OpIntSub "mul" -> - return $ mkBinop' loc IntMul + return $ mkBinop' loc OpIntMul "div" -> - return $ mkBinop' loc IntDiv + return $ mkBinop' loc OpIntDiv "mod" -> - return $ mkBinop' loc IntMod + return $ mkBinop' loc OpIntMod "lt" -> - return $ mkBinop' loc IntLt + return $ mkBinop' loc OpIntLt "le" -> - return $ mkBinop' loc IntLe + return $ mkBinop' loc OpIntLe "eq" -> - return $ mkBinop' loc ValEq + return $ mkBinop' loc OpEq "strcat" -> - return $ mkBinop' loc StrConcat + return $ mkBinop' loc OpStrConcat "show" -> - return $ mkInstr' loc ValShow + return $ mkUnop' loc OpShow "atoi" -> - return $ mkInstr' loc StrToInt + return $ mkUnop' loc OpStrToInt "push" -> mkInstr' loc . Push <$> value "pop" -> @@ -90,7 +90,7 @@ command = do "fail" -> return $ mkInstr' loc Failure "argsnum" -> - return $ mkInstr' loc ArgsNum + return $ mkUnop' loc OpArgsNum "alloc" -> mkInstr' loc . AllocConstr <$> constrTag @Code @(Maybe FunctionInfoExtra) @DirectRef "calloc" -> diff --git a/src/Juvix/Compiler/Asm/Translation/FromTree.hs b/src/Juvix/Compiler/Asm/Translation/FromTree.hs index 45635e1f01..cb9e895142 100644 --- a/src/Juvix/Compiler/Asm/Translation/FromTree.hs +++ b/src/Juvix/Compiler/Asm/Translation/FromTree.hs @@ -57,13 +57,13 @@ genCode fi = (mkInstr Pop) (go isTail _nodeBinopArg2) ) - _ -> + Tree.PrimBinop op -> snocReturn isTail $ DL.append (go False _nodeBinopArg2) ( DL.snoc (go False _nodeBinopArg1) - (genBinOp _nodeBinopOpcode) + (mkBinop op) ) goUnop :: Bool -> Tree.NodeUnop -> Code' @@ -221,27 +221,11 @@ genCode fi = goArgs :: [Tree.Node] -> Code' goArgs args = DL.concat (map (go False) (reverse args)) - genBinOp :: Tree.BinaryOpcode -> Command - genBinOp = \case - Tree.IntAdd -> mkBinop IntAdd - Tree.IntSub -> mkBinop IntSub - Tree.IntMul -> mkBinop IntMul - Tree.IntDiv -> mkBinop IntDiv - Tree.IntMod -> mkBinop IntMod - Tree.IntLt -> mkBinop IntLt - Tree.IntLe -> mkBinop IntLe - Tree.ValEq -> mkBinop ValEq - Tree.StrConcat -> mkBinop StrConcat - Tree.OpSeq -> impossible - genUnOp :: Tree.UnaryOpcode -> Command - genUnOp = - mkInstr . \case - Tree.OpShow -> ValShow - Tree.OpStrToInt -> StrToInt - Tree.OpTrace -> Trace - Tree.OpFail -> Failure - Tree.OpArgsNum -> ArgsNum + genUnOp op = case op of + Tree.PrimUnop op' -> mkUnop op' + Tree.OpTrace -> mkInstr Trace + Tree.OpFail -> mkInstr Failure snocReturn :: Bool -> Code' -> Code' snocReturn True code = DL.snoc code (mkInstr Return) diff --git a/src/Juvix/Compiler/Backend/C/Translation/FromReg.hs b/src/Juvix/Compiler/Backend/C/Translation/FromReg.hs index fd4746afbf..99f3168420 100644 --- a/src/Juvix/Compiler/Backend/C/Translation/FromReg.hs +++ b/src/Juvix/Compiler/Backend/C/Translation/FromReg.hs @@ -221,10 +221,8 @@ fromRegInstr bNoStack info = \case return [] Reg.Binop x -> return [fromBinaryOp x] - Reg.Show Reg.InstrShow {..} -> - return [StatementExpr $ macroCall "JUVIX_SHOW" [fromVarRef _instrShowResult, fromValue _instrShowValue]] - Reg.StrToInt Reg.InstrStrToInt {..} -> - return [StatementExpr $ macroCall "JUVIX_STR_TO_INT" [fromVarRef _instrStrToIntResult, fromValue _instrStrToIntValue]] + Reg.Unop x -> + return [fromUnaryOp x] Reg.Assign Reg.InstrAssign {..} -> return $ stmtsAssign (fromVarRef _instrAssignResult) (fromValue _instrAssignValue) Reg.Trace Reg.InstrTrace {..} -> @@ -233,8 +231,6 @@ fromRegInstr bNoStack info = \case return [StatementExpr $ macroVar "JUVIX_DUMP"] Reg.Failure Reg.InstrFailure {..} -> return [StatementExpr $ macroCall "JUVIX_FAILURE" [fromValue _instrFailureValue]] - Reg.ArgsNum Reg.InstrArgsNum {..} -> - return [StatementExpr $ macroCall "JUVIX_ARGS_NUM" [fromVarRef _instrArgsNumResult, fromValue _instrArgsNumValue]] Reg.Prealloc x -> return [fromPrealloc x] Reg.Alloc x -> @@ -260,18 +256,18 @@ fromRegInstr bNoStack info = \case Reg.Block Reg.InstrBlock {..} -> fromRegCode bNoStack info _instrBlockCode where - fromBinaryOp :: Reg.BinaryOp -> Statement - fromBinaryOp Reg.BinaryOp {..} = + fromBinaryOp :: Reg.InstrBinop -> Statement + fromBinaryOp Reg.InstrBinop {..} = StatementExpr $ macroCall - (getOpcodeMacro _binaryOpCode) - [ fromVarRef _binaryOpResult, - fromValue _binaryOpArg1, - fromValue _binaryOpArg2 + (getBinaryOpMacro _instrBinopOpcode) + [ fromVarRef _instrBinopResult, + fromValue _instrBinopArg1, + fromValue _instrBinopArg2 ] - getOpcodeMacro :: Reg.Opcode -> Text - getOpcodeMacro = \case + getBinaryOpMacro :: Reg.BinaryOp -> Text + getBinaryOpMacro = \case Reg.OpIntAdd -> "JUVIX_INT_ADD" Reg.OpIntSub -> "JUVIX_INT_SUB" Reg.OpIntMul -> "JUVIX_INT_MUL" @@ -282,6 +278,21 @@ fromRegInstr bNoStack info = \case Reg.OpEq -> "JUVIX_VAL_EQ" Reg.OpStrConcat -> "JUVIX_STR_CONCAT" + fromUnaryOp :: Reg.InstrUnop -> Statement + fromUnaryOp Reg.InstrUnop {..} = + StatementExpr $ + macroCall + (getUnaryOpMacro _instrUnopOpcode) + [ fromVarRef _instrUnopResult, + fromValue _instrUnopArg + ] + + getUnaryOpMacro :: Reg.UnaryOp -> Text + getUnaryOpMacro = \case + Reg.OpShow -> "JUVIX_SHOW" + Reg.OpStrToInt -> "JUVIX_STR_TO_INT" + Reg.OpArgsNum -> "JUVIX_ARGS_NUM" + fromVarRef :: Reg.VarRef -> Expression fromVarRef Reg.VarRef {..} = macroCall g [ExpressionLiteral (LiteralInt (toInteger _varRefIndex))] diff --git a/src/Juvix/Compiler/Nockma/Translation/FromTree.hs b/src/Juvix/Compiler/Nockma/Translation/FromTree.hs index 05fdbe781b..068d138508 100644 --- a/src/Juvix/Compiler/Nockma/Translation/FromTree.hs +++ b/src/Juvix/Compiler/Nockma/Translation/FromTree.hs @@ -356,13 +356,17 @@ compile = \case goUnop Tree.NodeUnop {..} = do arg <- compile _nodeUnopArg case _nodeUnopOpcode of - Tree.OpShow -> stringsErr - Tree.OpStrToInt -> stringsErr + Tree.PrimUnop op -> return $ goPrimUnop op arg Tree.OpFail -> return crash Tree.OpTrace -> goTrace arg - Tree.OpArgsNum -> - let getF f = getClosureField f arg - in return (sub (getF ClosureTotalArgsNum) (getF ClosureArgsNum)) + + goPrimUnop :: Tree.UnaryOp -> Term Natural -> Term Natural + goPrimUnop op arg = case op of + Tree.OpShow -> stringsErr + Tree.OpStrToInt -> stringsErr + Tree.OpArgsNum -> + let getF f = getClosureField f arg + in sub (getF ClosureTotalArgsNum) (getF ClosureArgsNum) goTrace :: Term Natural -> Sem r (Term Natural) goTrace arg = do @@ -376,18 +380,21 @@ compile = \case goBinop Tree.NodeBinop {..} = do arg1 <- compile _nodeBinopArg1 arg2 <- compile _nodeBinopArg2 - let args = [arg1, arg2] case _nodeBinopOpcode of - Tree.IntAdd -> return (callStdlib StdlibAdd args) - Tree.IntSub -> return (callStdlib StdlibSub args) - Tree.IntMul -> return (callStdlib StdlibMul args) - Tree.IntDiv -> return (callStdlib StdlibDiv args) - Tree.IntMod -> return (callStdlib StdlibMod args) - Tree.IntLt -> return (callStdlib StdlibLt args) - Tree.IntLe -> return (callStdlib StdlibLe args) + Tree.PrimBinop op -> goPrimBinop op [arg1, arg2] Tree.OpSeq -> return (OpHint # (nockNil' # arg1) # arg2) - Tree.ValEq -> testEq _nodeBinopArg1 _nodeBinopArg2 - Tree.StrConcat -> stringsErr + where + goPrimBinop :: Tree.BinaryOp -> [Term Natural] -> Sem r (Term Natural) + goPrimBinop op args = case op of + Tree.OpIntAdd -> return (callStdlib StdlibAdd args) + Tree.OpIntSub -> return (callStdlib StdlibSub args) + Tree.OpIntMul -> return (callStdlib StdlibMul args) + Tree.OpIntDiv -> return (callStdlib StdlibDiv args) + Tree.OpIntMod -> return (callStdlib StdlibMod args) + Tree.OpIntLt -> return (callStdlib StdlibLt args) + Tree.OpIntLe -> return (callStdlib StdlibLe args) + Tree.OpEq -> testEq _nodeBinopArg1 _nodeBinopArg2 + Tree.OpStrConcat -> stringsErr goAllocClosure :: Tree.NodeAllocClosure -> Sem r (Term Natural) goAllocClosure Tree.NodeAllocClosure {..} = do diff --git a/src/Juvix/Compiler/Reg/Extra/Base.hs b/src/Juvix/Compiler/Reg/Extra/Base.hs index ddcdf9b8b7..42f2716447 100644 --- a/src/Juvix/Compiler/Reg/Extra/Base.hs +++ b/src/Juvix/Compiler/Reg/Extra/Base.hs @@ -4,11 +4,9 @@ import Juvix.Compiler.Reg.Language getResultVar :: Instruction -> Maybe VarRef getResultVar = \case - Binop x -> Just $ x ^. binaryOpResult - Show x -> Just $ x ^. instrShowResult - StrToInt x -> Just $ x ^. instrStrToIntResult + Binop x -> Just $ x ^. instrBinopResult + Unop x -> Just $ x ^. instrUnopResult Assign x -> Just $ x ^. instrAssignResult - ArgsNum x -> Just $ x ^. instrArgsNumResult Alloc x -> Just $ x ^. instrAllocResult AllocClosure x -> Just $ x ^. instrAllocClosureResult ExtendClosure x -> Just $ x ^. instrExtendClosureResult @@ -18,11 +16,9 @@ getResultVar = \case setResultVar :: Instruction -> VarRef -> Instruction setResultVar instr vref = case instr of - Binop x -> Binop $ set binaryOpResult vref x - Show x -> Show $ set instrShowResult vref x - StrToInt x -> StrToInt $ set instrStrToIntResult vref x + Binop x -> Binop $ set instrBinopResult vref x + Unop x -> Unop $ set instrUnopResult vref x Assign x -> Assign $ set instrAssignResult vref x - ArgsNum x -> ArgsNum $ set instrArgsNumResult vref x Alloc x -> Alloc $ set instrAllocResult vref x AllocClosure x -> AllocClosure $ set instrAllocClosureResult vref x ExtendClosure x -> ExtendClosure $ set instrExtendClosureResult vref x @@ -33,10 +29,8 @@ setResultVar instr vref = case instr of overValueRefs :: (VarRef -> VarRef) -> Instruction -> Instruction overValueRefs f = \case Binop x -> Binop $ goBinop x - Show x -> Show $ goShow x - StrToInt x -> StrToInt $ goStrToInt x + Unop x -> Unop $ goUnop x Assign x -> Assign $ goAssign x - ArgsNum x -> ArgsNum $ goArgsNum x Alloc x -> Alloc $ goAlloc x AllocClosure x -> AllocClosure $ goAllocClosure x ExtendClosure x -> ExtendClosure $ goExtendClosure x @@ -63,26 +57,20 @@ overValueRefs f = \case CRef x -> CRef $ goConstrField x VRef x -> VRef $ f x - goBinop :: BinaryOp -> BinaryOp - goBinop BinaryOp {..} = - BinaryOp - { _binaryOpArg1 = goValue _binaryOpArg1, - _binaryOpArg2 = goValue _binaryOpArg2, + goBinop :: InstrBinop -> InstrBinop + goBinop InstrBinop {..} = + InstrBinop + { _instrBinopArg1 = goValue _instrBinopArg1, + _instrBinopArg2 = goValue _instrBinopArg2, .. } - goShow :: InstrShow -> InstrShow - goShow = over instrShowValue goValue - - goStrToInt :: InstrStrToInt -> InstrStrToInt - goStrToInt = over instrStrToIntValue goValue + goUnop :: InstrUnop -> InstrUnop + goUnop = over instrUnopArg goValue goAssign :: InstrAssign -> InstrAssign goAssign = over instrAssignValue goValue - goArgsNum :: InstrArgsNum -> InstrArgsNum - goArgsNum = over instrArgsNumValue goValue - goAlloc :: InstrAlloc -> InstrAlloc goAlloc = over instrAllocArgs (map goValue) diff --git a/src/Juvix/Compiler/Reg/Extra/Info.hs b/src/Juvix/Compiler/Reg/Extra/Info.hs index 54968de995..9a61ae13a0 100644 --- a/src/Juvix/Compiler/Reg/Extra/Info.hs +++ b/src/Juvix/Compiler/Reg/Extra/Info.hs @@ -18,13 +18,11 @@ computeMaxStackHeight lims = maximum . map go go = \case Nop -> 0 Binop {} -> 0 - Show {} -> 0 - StrToInt {} -> 0 + Unop {} -> 0 Assign {} -> 0 Trace {} -> 0 Dump -> 0 Failure {} -> 0 - ArgsNum {} -> 0 Prealloc InstrPrealloc {..} -> length _instrPreallocLiveVars Alloc {} -> 0 @@ -67,13 +65,11 @@ computeMaxCallClosuresArgsNum = maximum . map go go = \case Nop -> 0 Binop {} -> 0 - Show {} -> 0 - StrToInt {} -> 0 + Unop {} -> 0 Assign {} -> 0 Trace {} -> 0 Dump -> 0 Failure {} -> 0 - ArgsNum {} -> 0 Prealloc InstrPrealloc {} -> 0 Alloc {} -> 0 AllocClosure {} -> 0 @@ -106,13 +102,11 @@ computeStringMap strs = snd . run . execState (HashMap.size strs, strs) . mapM g go :: (Member (State (Int, HashMap Text Int)) r) => Instruction -> Sem r () go = \case Nop -> return () - Binop BinaryOp {..} -> do - goVal _binaryOpArg1 - goVal _binaryOpArg2 - Show InstrShow {..} -> do - goVal _instrShowValue - StrToInt InstrStrToInt {..} -> do - goVal _instrStrToIntValue + Binop InstrBinop {..} -> do + goVal _instrBinopArg1 + goVal _instrBinopArg2 + Unop InstrUnop {..} -> do + goVal _instrUnopArg Assign InstrAssign {..} -> goVal _instrAssignValue Trace InstrTrace {..} -> @@ -120,8 +114,6 @@ computeStringMap strs = snd . run . execState (HashMap.size strs, strs) . mapM g Dump -> return () Failure InstrFailure {..} -> goVal _instrFailureValue - ArgsNum InstrArgsNum {..} -> - goVal _instrArgsNumValue Prealloc {} -> return () Alloc InstrAlloc {..} -> mapM_ goVal _instrAllocArgs @@ -167,14 +159,12 @@ computeLocalVarsNum = maximum . map go go :: Instruction -> Int go = \case Nop -> 0 - Binop BinaryOp {..} -> goVarRef _binaryOpResult - Show InstrShow {..} -> goVarRef _instrShowResult - StrToInt InstrStrToInt {..} -> goVarRef _instrStrToIntResult + Binop InstrBinop {..} -> goVarRef _instrBinopResult + Unop InstrUnop {..} -> goVarRef _instrUnopResult Assign InstrAssign {..} -> goVarRef _instrAssignResult Trace {} -> 0 Dump -> 0 Failure {} -> 0 - ArgsNum InstrArgsNum {..} -> goVarRef _instrArgsNumResult Prealloc InstrPrealloc {} -> 0 Alloc InstrAlloc {..} -> goVarRef _instrAllocResult AllocClosure InstrAllocClosure {..} -> goVarRef _instrAllocClosureResult diff --git a/src/Juvix/Compiler/Reg/Interpreter.hs b/src/Juvix/Compiler/Reg/Interpreter.hs index a39ef2cf7f..ebb380a319 100644 --- a/src/Juvix/Compiler/Reg/Interpreter.hs +++ b/src/Juvix/Compiler/Reg/Interpreter.hs @@ -15,8 +15,8 @@ import Juvix.Compiler.Reg.Extra.Info import Juvix.Compiler.Reg.Interpreter.Base import Juvix.Compiler.Reg.Interpreter.Error import Juvix.Compiler.Reg.Pretty +import Juvix.Compiler.Tree.Evaluator.Builtins import System.IO.Unsafe (unsafePerformIO) -import Text.Read (readMaybe) type Vars s = MV.MVector s (Maybe Val) @@ -45,13 +45,11 @@ runFunction hout infoTable args0 info0 = do instr : instrs -> case instr of Nop -> go args tmps instrs Binop x -> goBinop args tmps instrs x - Show x -> goShow args tmps instrs x - StrToInt x -> goStrToInt args tmps instrs x + Unop x -> goUnop args tmps instrs x Assign x -> goAssign args tmps instrs x Trace x -> goTrace args tmps instrs x Dump -> goDump args tmps instrs Failure x -> goFailure args tmps instrs x - ArgsNum x -> goArgsNum args tmps instrs x Prealloc x -> goPrealloc args tmps instrs x Alloc x -> goAlloc args tmps instrs x AllocClosure x -> goAllocClosure args tmps instrs x @@ -109,52 +107,29 @@ runFunction hout infoTable args0 info0 = do VarGroupArgs -> throwRunError "function arguments are not writable" Nothing - goBinop :: Args -> Vars s -> Code -> BinaryOp -> ST s Val - goBinop args tmps instrs BinaryOp {..} = do - v1 <- readValue args tmps _binaryOpArg1 - v2 <- readValue args tmps _binaryOpArg2 - writeVarRef args tmps _binaryOpResult (binop _binaryOpCode v1 v2) + goBinop :: Args -> Vars s -> Code -> InstrBinop -> ST s Val + goBinop args tmps instrs InstrBinop {..} = do + v1 <- readValue args tmps _instrBinopArg1 + v2 <- readValue args tmps _instrBinopArg2 + writeVarRef args tmps _instrBinopResult (binop _instrBinopOpcode v1 v2) go args tmps instrs - binop :: Opcode -> Val -> Val -> Val - binop op = case op of - OpIntAdd -> binopInt (+) - OpIntSub -> binopInt (-) - OpIntMul -> binopInt (*) - OpIntDiv -> binopInt quot - OpIntMod -> binopInt rem - OpIntLt -> binopCmp (<) - OpIntLe -> binopCmp (<=) - OpEq -> \v1 v2 -> ValBool (v1 == v2) - OpStrConcat -> binopStr (<>) - - binopInt :: (Integer -> Integer -> Integer) -> Val -> Val -> Val - binopInt f v1 v2 = case (v1, v2) of - (ValInteger i1, ValInteger i2) -> ValInteger (f i1 i2) - _ -> throwRunError "expected integer" Nothing - - binopCmp :: (Integer -> Integer -> Bool) -> Val -> Val -> Val - binopCmp f v1 v2 = case (v1, v2) of - (ValInteger i1, ValInteger i2) -> ValBool (f i1 i2) - _ -> throwRunError "expected integer" Nothing - - binopStr :: (Text -> Text -> Text) -> Val -> Val -> Val - binopStr f v1 v2 = case (v1, v2) of - (ValString s1, ValString s2) -> ValString (f s1 s2) - _ -> throwRunError "expected string" Nothing - - goShow :: Args -> Vars s -> Code -> InstrShow -> ST s Val - goShow args tmps instrs InstrShow {..} = do - val <- readValue args tmps _instrShowValue - writeVarRef args tmps _instrShowResult (ValString (ppPrint infoTable val)) - go args tmps instrs + binop :: BinaryOp -> Val -> Val -> Val + binop op v1 v2 = case evalBinop op v1 v2 of + Left err -> throwRunError err Nothing + Right v -> v - goStrToInt :: Args -> Vars s -> Code -> InstrStrToInt -> ST s Val - goStrToInt args tmps instrs InstrStrToInt {..} = do - val <- readValue args tmps _instrStrToIntValue - writeVarRef args tmps _instrStrToIntResult (ValInteger (valToInt val)) + goUnop :: Args -> Vars s -> Code -> InstrUnop -> ST s Val + goUnop args tmps instrs InstrUnop {..} = do + val <- readValue args tmps _instrUnopArg + writeVarRef args tmps _instrUnopResult (unop _instrUnopOpcode val) go args tmps instrs + unop :: UnaryOp -> Val -> Val + unop op v = case evalUnop infoTable op v of + Left err -> throwRunError err Nothing + Right v' -> v' + goAssign :: Args -> Vars s -> Code -> InstrAssign -> ST s Val goAssign args tmps instrs InstrAssign {..} = do val <- readValue args tmps _instrAssignValue @@ -181,18 +156,6 @@ runFunction hout infoTable args0 info0 = do val <- readValue args tmps _instrFailureValue throwRunError ("failure: " <> printVal val) Nothing - goArgsNum :: Args -> Vars s -> Code -> InstrArgsNum -> ST s Val - goArgsNum args tmps instrs InstrArgsNum {..} = do - val <- readValue args tmps _instrArgsNumValue - case val of - ValClosure Closure {..} -> do - writeVarRef args tmps _instrArgsNumResult (ValInteger (fromIntegral $ fi ^. functionArgsNum - length _closureArgs)) - go args tmps instrs - where - fi = lookupFunInfo infoTable _closureSymbol - _ -> - throwRunError "expected a closure" Nothing - goPrealloc :: Args -> Vars s -> Code -> InstrPrealloc -> ST s Val goPrealloc args tmps instrs InstrPrealloc {} = go args tmps instrs @@ -350,15 +313,6 @@ runFunction hout infoTable args0 info0 = do ValString s -> s v -> ppPrint infoTable v - valToInt :: Val -> Integer - valToInt = \case - ValString s -> - case readMaybe (fromText s) of - Just i -> i - Nothing -> throwRunError "string to integer conversion error" Nothing - ValInteger i -> i - _ -> throwRunError "integer conversion error" Nothing - runIO :: forall r. (Members '[Error RegError, Embed IO] r) => Handle -> Handle -> InfoTable -> Val -> Sem r Val runIO hin hout infoTable = \case ValConstr (Constr (BuiltinTag TagReturn) [x]) -> diff --git a/src/Juvix/Compiler/Reg/Language.hs b/src/Juvix/Compiler/Reg/Language.hs index 13c0bfda72..bc8f6632d4 100644 --- a/src/Juvix/Compiler/Reg/Language.hs +++ b/src/Juvix/Compiler/Reg/Language.hs @@ -51,11 +51,9 @@ deriving stock instance (Eq ConstrField) deriving stock instance (Eq Value) data Instruction - = Binop BinaryOp - | Show InstrShow - | StrToInt InstrStrToInt + = Binop InstrBinop + | Unop InstrUnop | Assign InstrAssign - | ArgsNum InstrArgsNum | Alloc InstrAlloc | AllocClosure InstrAllocClosure | ExtendClosure InstrExtendClosure @@ -79,35 +77,18 @@ data Instruction type Code = [Instruction] -data BinaryOp = BinaryOp - { _binaryOpCode :: Opcode, - _binaryOpResult :: VarRef, - _binaryOpArg1 :: Value, - _binaryOpArg2 :: Value +data InstrBinop = InstrBinop + { _instrBinopOpcode :: BinaryOp, + _instrBinopResult :: VarRef, + _instrBinopArg1 :: Value, + _instrBinopArg2 :: Value } deriving stock (Eq) -data Opcode - = OpIntAdd - | OpIntSub - | OpIntMul - | OpIntDiv - | OpIntMod - | OpIntLt - | OpIntLe - | OpEq - | OpStrConcat - deriving stock (Eq) - -data InstrShow = InstrShow - { _instrShowResult :: VarRef, - _instrShowValue :: Value - } - deriving stock (Eq) - -data InstrStrToInt = InstrStrToInt - { _instrStrToIntResult :: VarRef, - _instrStrToIntValue :: Value +data InstrUnop = InstrUnop + { _instrUnopOpcode :: UnaryOp, + _instrUnopResult :: VarRef, + _instrUnopArg :: Value } deriving stock (Eq) @@ -127,12 +108,6 @@ newtype InstrFailure = InstrFailure } deriving stock (Eq) -data InstrArgsNum = InstrArgsNum - { _instrArgsNumResult :: VarRef, - _instrArgsNumValue :: Value - } - deriving stock (Eq) - data InstrPrealloc = InstrPrealloc { _instrPreallocWordsNum :: Int, _instrPreallocLiveVars :: [VarRef] @@ -236,7 +211,8 @@ newtype InstrBlock = InstrBlock } deriving stock (Eq) -makeLenses ''BinaryOp +makeLenses ''InstrBinop +makeLenses ''InstrUnop makeLenses ''InstrAssign makeLenses ''InstrTrace makeLenses ''InstrFailure @@ -250,9 +226,6 @@ makeLenses ''InstrBranch makeLenses ''InstrCase makeLenses ''CaseBranch makeLenses ''InstrReturn -makeLenses ''InstrShow -makeLenses ''InstrStrToInt -makeLenses ''InstrArgsNum makeLenses ''InstrTailCall mkVarRef :: VarGroup -> Index -> VarRef diff --git a/src/Juvix/Compiler/Reg/Language/Base.hs b/src/Juvix/Compiler/Reg/Language/Base.hs index 456063d3b0..728c9ad296 100644 --- a/src/Juvix/Compiler/Reg/Language/Base.hs +++ b/src/Juvix/Compiler/Reg/Language/Base.hs @@ -1,10 +1,12 @@ module Juvix.Compiler.Reg.Language.Base ( module Juvix.Compiler.Core.Language.Base, module Juvix.Compiler.Tree.Language.Base, + module Juvix.Compiler.Tree.Language.Builtins, module Juvix.Compiler.Tree.Language.Rep, ) where import Juvix.Compiler.Core.Language.Base import Juvix.Compiler.Tree.Language.Base (Constant (..)) +import Juvix.Compiler.Tree.Language.Builtins import Juvix.Compiler.Tree.Language.Rep diff --git a/src/Juvix/Compiler/Reg/Pretty/Base.hs b/src/Juvix/Compiler/Reg/Pretty/Base.hs index 5b96073422..ba17525ccf 100644 --- a/src/Juvix/Compiler/Reg/Pretty/Base.hs +++ b/src/Juvix/Compiler/Reg/Pretty/Base.hs @@ -47,37 +47,20 @@ instance PrettyCode Value where CRef x -> ppCode x VRef x -> ppCode x -instance PrettyCode Opcode where - ppCode op = return $ case op of - OpIntAdd -> primitive Str.add_ - OpIntSub -> primitive Str.sub_ - OpIntMul -> primitive Str.mul_ - OpIntDiv -> primitive Str.div_ - OpIntMod -> primitive Str.mod_ - OpIntLt -> primitive Str.lt_ - OpIntLe -> primitive Str.le_ - OpEq -> primitive Str.eq - OpStrConcat -> primitive Str.instrStrConcat - -instance PrettyCode BinaryOp where - ppCode BinaryOp {..} = do - res <- ppCode _binaryOpResult - arg1 <- ppCode _binaryOpArg1 - arg2 <- ppCode _binaryOpArg2 - op <- ppCode _binaryOpCode +instance PrettyCode InstrBinop where + ppCode InstrBinop {..} = do + res <- ppCode _instrBinopResult + arg1 <- ppCode _instrBinopArg1 + arg2 <- ppCode _instrBinopArg2 + op <- Tree.ppCode _instrBinopOpcode return $ res <+> primitive Str.equal <+> op <+> arg1 <+> arg2 -instance PrettyCode InstrShow where - ppCode InstrShow {..} = do - res <- ppCode _instrShowResult - val <- ppCode _instrShowValue - return $ res <+> primitive Str.equal <+> primitive Str.show_ <+> val - -instance PrettyCode InstrStrToInt where - ppCode InstrStrToInt {..} = do - res <- ppCode _instrStrToIntResult - val <- ppCode _instrStrToIntValue - return $ res <+> primitive Str.equal <+> primitive Str.instrStrToInt <+> val +instance PrettyCode InstrUnop where + ppCode InstrUnop {..} = do + op <- Tree.ppCode _instrUnopOpcode + res <- ppCode _instrUnopResult + val <- ppCode _instrUnopArg + return $ res <+> primitive Str.equal <+> op <+> val instance PrettyCode InstrAssign where ppCode InstrAssign {..} = do @@ -95,12 +78,6 @@ instance PrettyCode InstrFailure where val <- ppCode _instrFailureValue return $ primitive Str.fail_ <+> val -instance PrettyCode InstrArgsNum where - ppCode InstrArgsNum {..} = do - res <- ppCode _instrArgsNumResult - val <- ppCode _instrArgsNumValue - return $ res <+> primitive Str.equal <+> primitive Str.argsnum <+> val - ppLiveVars :: (Member (Reader Options) r) => [VarRef] -> Sem r (Doc Ann) ppLiveVars vars | null vars = return mempty @@ -260,13 +237,11 @@ instance PrettyCode Instruction where ppCode = \case Nop -> return $ primitive Str.nop Binop x -> ppCode x - Show x -> ppCode x - StrToInt x -> ppCode x + Unop x -> ppCode x Assign x -> ppCode x Trace x -> ppCode x Dump -> return $ primitive Str.dump Failure x -> ppCode x - ArgsNum x -> ppCode x Prealloc x -> ppCode x Alloc x -> ppCode x AllocClosure x -> ppCode x diff --git a/src/Juvix/Compiler/Reg/Translation/FromAsm.hs b/src/Juvix/Compiler/Reg/Translation/FromAsm.hs index c778777b26..268d3b3588 100644 --- a/src/Juvix/Compiler/Reg/Translation/FromAsm.hs +++ b/src/Juvix/Compiler/Reg/Translation/FromAsm.hs @@ -63,15 +63,13 @@ fromAsmInstr :: Sem r Instruction fromAsmInstr funInfo tab si Asm.CmdInstr {..} = case _cmdInstrInstruction of - Asm.Binop op -> return $ mkBinop (mkOpcode op) - Asm.ValShow -> return $ mkShow (mkVarRef VarGroupLocal (ntmps + n)) (VRef $ mkVarRef VarGroupLocal (ntmps + n)) - Asm.StrToInt -> return $ mkStrToInt (mkVarRef VarGroupLocal (ntmps + n)) (VRef $ mkVarRef VarGroupLocal (ntmps + n)) + Asm.Binop op -> return $ mkBinop op + Asm.Unop op -> return $ mkUnop op Asm.Push val -> return $ mkAssign (mkVarRef VarGroupLocal (ntmps + n + 1)) (mkValue val) Asm.Pop -> return Nop Asm.Trace -> return $ Trace $ InstrTrace (VRef $ mkVarRef VarGroupLocal (ntmps + n)) Asm.Dump -> return Dump Asm.Failure -> return $ Failure $ InstrFailure (VRef $ mkVarRef VarGroupLocal (ntmps + n)) - Asm.ArgsNum -> return $ mkArgsNum (mkVarRef VarGroupLocal (ntmps + n)) (VRef $ mkVarRef VarGroupLocal (ntmps + n)) Asm.Prealloc x -> return $ mkPrealloc x Asm.AllocConstr tag -> return $ mkAlloc tag Asm.AllocClosure x -> return $ mkAllocClosure x @@ -107,41 +105,29 @@ fromAsmInstr funInfo tab si Asm.CmdInstr {..} = getArgs :: Int -> Int -> [Value] getArgs s k = map (\i -> VRef $ mkVarRef VarGroupLocal (ntmps + n - i)) [s .. (s + k - 1)] - mkBinop :: Opcode -> Instruction + mkBinop :: BinaryOp -> Instruction mkBinop op = Binop - ( BinaryOp - { _binaryOpCode = op, - _binaryOpResult = mkVarRef VarGroupLocal (ntmps + n - 1), - _binaryOpArg1 = VRef $ mkVarRef VarGroupLocal (ntmps + n), - _binaryOpArg2 = VRef $ mkVarRef VarGroupLocal (ntmps + n - 1) + ( InstrBinop + { _instrBinopOpcode = op, + _instrBinopResult = mkVarRef VarGroupLocal (ntmps + n - 1), + _instrBinopArg1 = VRef $ mkVarRef VarGroupLocal (ntmps + n), + _instrBinopArg2 = VRef $ mkVarRef VarGroupLocal (ntmps + n - 1) } ) - mkOpcode :: Asm.Opcode -> Opcode - mkOpcode = \case - Asm.IntAdd -> OpIntAdd - Asm.IntSub -> OpIntSub - Asm.IntMul -> OpIntMul - Asm.IntDiv -> OpIntDiv - Asm.IntMod -> OpIntMod - Asm.IntLt -> OpIntLt - Asm.IntLe -> OpIntLe - Asm.ValEq -> OpEq - Asm.StrConcat -> OpStrConcat - - mkShow :: VarRef -> Value -> Instruction - mkShow tgt src = Show (InstrShow tgt src) - - mkStrToInt :: VarRef -> Value -> Instruction - mkStrToInt tgt src = StrToInt (InstrStrToInt tgt src) + mkUnop :: UnaryOp -> Instruction + mkUnop op = + Unop + InstrUnop + { _instrUnopOpcode = op, + _instrUnopResult = mkVarRef VarGroupLocal (ntmps + n), + _instrUnopArg = VRef $ mkVarRef VarGroupLocal (ntmps + n) + } mkAssign :: VarRef -> Value -> Instruction mkAssign tgt src = Assign (InstrAssign tgt src) - mkArgsNum :: VarRef -> Value -> Instruction - mkArgsNum tgt src = ArgsNum (InstrArgsNum tgt src) - mkValue :: Asm.Value -> Value mkValue = \case Asm.Constant c -> Const c diff --git a/src/Juvix/Compiler/Reg/Translation/FromSource.hs b/src/Juvix/Compiler/Reg/Translation/FromSource.hs index 96272e2fc6..25f3fbc8ed 100644 --- a/src/Juvix/Compiler/Reg/Translation/FromSource.hs +++ b/src/Juvix/Compiler/Reg/Translation/FromSource.hs @@ -72,9 +72,7 @@ instrWithResult = do vref <- declVarRef kw kwEq (Binop <$> instrBinop vref) - <|> (Show <$> instrShow vref) - <|> (StrToInt <$> instrStrToInt vref) - <|> (ArgsNum <$> instrArgsNum vref) + <|> (Unop <$> instrUnop vref) <|> (Alloc <$> instrAlloc vref) <|> (AllocClosure <$> instrAllocClosure vref) <|> (ExtendClosure <$> instrExtendClosure vref) @@ -88,7 +86,7 @@ instrNop = kw kwNop instrBinop :: (Members '[Reader ParserSig, InfoTableBuilder, State LocalParams] r) => VarRef -> - ParsecS r BinaryOp + ParsecS r InstrBinop instrBinop vref = parseBinaryOp kwAdd_ OpIntAdd vref <|> parseBinaryOp kwSub_ OpIntSub vref @@ -103,45 +101,44 @@ instrBinop vref = parseBinaryOp :: (Members '[Reader ParserSig, InfoTableBuilder, State LocalParams] r) => Keyword -> - Opcode -> + BinaryOp -> VarRef -> - ParsecS r BinaryOp + ParsecS r InstrBinop parseBinaryOp kwd op vref = do kw kwd arg1 <- value arg2 <- value return $ - BinaryOp - { _binaryOpCode = op, - _binaryOpResult = vref, - _binaryOpArg1 = arg1, - _binaryOpArg2 = arg2 + InstrBinop + { _instrBinopOpcode = op, + _instrBinopResult = vref, + _instrBinopArg1 = arg1, + _instrBinopArg2 = arg2 } -instrShow :: +instrUnop :: (Members '[Reader ParserSig, InfoTableBuilder, State LocalParams] r) => VarRef -> - ParsecS r InstrShow -instrShow vref = do - kw kwShow - val <- value - return - InstrShow - { _instrShowResult = vref, - _instrShowValue = val - } + ParsecS r InstrUnop +instrUnop vref = + parseUnaryOp kwShow OpShow vref + <|> parseUnaryOp kwAtoi OpStrToInt vref + <|> parseUnaryOp kwArgsNum OpArgsNum vref -instrStrToInt :: +parseUnaryOp :: (Members '[Reader ParserSig, InfoTableBuilder, State LocalParams] r) => + Keyword -> + UnaryOp -> VarRef -> - ParsecS r InstrStrToInt -instrStrToInt vref = do - kw kwAtoi - val <- value - return - InstrStrToInt - { _instrStrToIntResult = vref, - _instrStrToIntValue = val + ParsecS r InstrUnop +parseUnaryOp kwd op vref = do + kw kwd + arg <- value + return $ + InstrUnop + { _instrUnopOpcode = op, + _instrUnopResult = vref, + _instrUnopArg = arg } instrAssign :: @@ -181,19 +178,6 @@ instrFailure = do { _instrFailureValue = val } -instrArgsNum :: - (Members '[Reader ParserSig, InfoTableBuilder, State LocalParams] r) => - VarRef -> - ParsecS r InstrArgsNum -instrArgsNum vref = do - kw kwArgsNum - val <- value - return - InstrArgsNum - { _instrArgsNumResult = vref, - _instrArgsNumValue = val - } - instrPrealloc :: (Members '[Reader ParserSig, InfoTableBuilder, State LocalParams] r) => ParsecS r InstrPrealloc diff --git a/src/Juvix/Compiler/Tree/Evaluator.hs b/src/Juvix/Compiler/Tree/Evaluator.hs index 3976147f10..95f5948c75 100644 --- a/src/Juvix/Compiler/Tree/Evaluator.hs +++ b/src/Juvix/Compiler/Tree/Evaluator.hs @@ -6,11 +6,11 @@ import GHC.Show qualified as S import Juvix.Compiler.Core.Data.BinderList qualified as BL import Juvix.Compiler.Tree.Data.InfoTable import Juvix.Compiler.Tree.Error +import Juvix.Compiler.Tree.Evaluator.Builtins import Juvix.Compiler.Tree.Extra.Base import Juvix.Compiler.Tree.Language import Juvix.Compiler.Tree.Language.Value import Juvix.Compiler.Tree.Pretty -import Text.Read qualified as T data EvalError = EvalError { _evalErrorLocation :: Maybe Location, @@ -52,79 +52,31 @@ hEval hout tab = eval' [] mempty evalError msg = Exception.throw (EvalError (getNodeLocation node) msg) + eitherToError :: Either Text Value -> Value + eitherToError = \case + Left err -> evalError err + Right v -> v + goBinop :: NodeBinop -> Value goBinop NodeBinop {..} = -- keeping the lets separate ensures that `arg1` is evaluated before `arg2` let !arg1 = eval' args temps _nodeBinopArg1 in let !arg2 = eval' args temps _nodeBinopArg2 in case _nodeBinopOpcode of - IntAdd -> goIntBinop (+) arg1 arg2 - IntSub -> goIntBinop (-) arg1 arg2 - IntMul -> goIntBinop (*) arg1 arg2 - IntDiv - | arg2 == ValInteger 0 -> evalError "division by zero" - | otherwise -> goIntBinop quot arg1 arg2 - IntMod - | arg2 == ValInteger 0 -> evalError "division by zero" - | otherwise -> goIntBinop rem arg1 arg2 - IntLe -> goIntCmpBinop (<=) arg1 arg2 - IntLt -> goIntCmpBinop (<) arg1 arg2 - ValEq - | arg1 == arg2 -> ValBool True - | otherwise -> ValBool False - StrConcat -> goStrConcat arg1 arg2 + PrimBinop op -> eitherToError $ evalBinop op arg1 arg2 OpSeq -> arg2 - goIntBinop :: (Integer -> Integer -> Integer) -> Value -> Value -> Value - goIntBinop f v1 v2 = case (v1, v2) of - (ValInteger i1, ValInteger i2) -> ValInteger (f i1 i2) - _ -> evalError "expected two integer arguments" - - goIntCmpBinop :: (Integer -> Integer -> Bool) -> Value -> Value -> Value - goIntCmpBinop f v1 v2 = case (v1, v2) of - (ValInteger i1, ValInteger i2) -> ValBool (f i1 i2) - _ -> evalError "expected two integer arguments" - - goStrConcat :: Value -> Value -> Value - goStrConcat v1 v2 = case (v1, v2) of - (ValString s1, ValString s2) -> ValString (s1 <> s2) - _ -> evalError "expected two string arguments" - goUnop :: NodeUnop -> Value goUnop NodeUnop {..} = let !v = eval' args temps _nodeUnopArg in case _nodeUnopOpcode of - OpShow -> ValString (printValue tab v) - OpStrToInt -> goStringUnop strToInt v + PrimUnop op -> eitherToError $ evalUnop tab op v OpTrace -> goTrace v OpFail -> goFail v - OpArgsNum -> goArgsNum v - - strToInt :: Text -> Value - strToInt s = case T.readMaybe (fromText s) of - Just i -> - ValInteger i - Nothing -> - evalError "string to integer: not an integer" - - goStringUnop :: (Text -> Value) -> Value -> Value - goStringUnop f = \case - ValString s -> f s - _ -> evalError "expected a string argument" goFail :: Value -> Value goFail v = evalError ("failure: " <> printValue tab v) - goArgsNum :: Value -> Value - goArgsNum = \case - ValClosure Closure {..} -> - ValInteger (fromIntegral argsNum) - where - fi = lookupFunInfo tab _closureSymbol - argsNum = fi ^. functionArgsNum - length _closureArgs - _ -> - evalError "expected a closure" - goTrace :: Value -> Value goTrace v = unsafePerformIO (hPutStrLn hout (printValue tab v) >> return v) @@ -260,11 +212,6 @@ hEval hout tab = eval' [] mempty let !v = eval' args temps _nodeSaveArg in eval' args (BL.cons v temps) _nodeSaveBody -printValue :: InfoTable -> Value -> Text -printValue tab = \case - ValString s -> s - v -> ppPrint tab v - valueToNode :: Value -> Node valueToNode = \case ValInteger i -> mkConst $ ConstInt i diff --git a/src/Juvix/Compiler/Tree/Evaluator/Builtins.hs b/src/Juvix/Compiler/Tree/Evaluator/Builtins.hs new file mode 100644 index 0000000000..6643e1134e --- /dev/null +++ b/src/Juvix/Compiler/Tree/Evaluator/Builtins.hs @@ -0,0 +1,77 @@ +module Juvix.Compiler.Tree.Evaluator.Builtins where + +import Juvix.Compiler.Tree.Data.InfoTable.Base +import Juvix.Compiler.Tree.Language.Builtins +import Juvix.Compiler.Tree.Language.Value +import Juvix.Compiler.Tree.Pretty.Base +import Juvix.Data.PPOutput +import Juvix.Prelude +import Text.Read qualified as T + +type ErrorMsg = Text + +evalBinop :: BinaryOp -> Value -> Value -> Either ErrorMsg Value +evalBinop op arg1 arg2 = case op of + OpIntAdd -> goIntBinop (+) arg1 arg2 + OpIntSub -> goIntBinop (-) arg1 arg2 + OpIntMul -> goIntBinop (*) arg1 arg2 + OpIntDiv + | arg2 == ValInteger 0 -> Left "division by zero" + | otherwise -> goIntBinop quot arg1 arg2 + OpIntMod + | arg2 == ValInteger 0 -> Left "division by zero" + | otherwise -> goIntBinop rem arg1 arg2 + OpIntLe -> goIntCmpBinop (<=) arg1 arg2 + OpIntLt -> goIntCmpBinop (<) arg1 arg2 + OpEq + | arg1 == arg2 -> Right $ ValBool True + | otherwise -> Right $ ValBool False + OpStrConcat -> goStrConcat arg1 arg2 + where + goIntBinop :: (Integer -> Integer -> Integer) -> Value -> Value -> Either ErrorMsg Value + goIntBinop f v1 v2 = case (v1, v2) of + (ValInteger i1, ValInteger i2) -> Right $ ValInteger (f i1 i2) + _ -> Left "expected two integer arguments" + + goIntCmpBinop :: (Integer -> Integer -> Bool) -> Value -> Value -> Either ErrorMsg Value + goIntCmpBinop f v1 v2 = case (v1, v2) of + (ValInteger i1, ValInteger i2) -> Right $ ValBool (f i1 i2) + _ -> Left "expected two integer arguments" + + goStrConcat :: Value -> Value -> Either ErrorMsg Value + goStrConcat v1 v2 = case (v1, v2) of + (ValString s1, ValString s2) -> Right $ ValString (s1 <> s2) + _ -> Left "expected two string arguments" + +evalUnop :: InfoTable' t e -> UnaryOp -> Value -> Either ErrorMsg Value +evalUnop tab op v = case op of + OpShow -> Right $ ValString (printValue tab v) + OpStrToInt -> goStringUnop strToInt v + OpArgsNum -> goArgsNum v + where + strToInt :: Text -> Either ErrorMsg Value + strToInt s = case T.readMaybe (fromText s) of + Just i -> + Right $ ValInteger i + Nothing -> + Left "string to integer: not an integer" + + goStringUnop :: (Text -> Either ErrorMsg Value) -> Value -> Either ErrorMsg Value + goStringUnop f = \case + ValString s -> f s + _ -> Left "expected a string argument" + + goArgsNum :: Value -> Either ErrorMsg Value + goArgsNum = \case + ValClosure Closure {..} -> + Right $ ValInteger (fromIntegral argsNum) + where + fi = lookupFunInfo tab _closureSymbol + argsNum = fi ^. functionArgsNum - length _closureArgs + _ -> + Left "expected a closure" + +printValue :: InfoTable' t e -> Value -> Text +printValue tab = \case + ValString s -> s + v -> toPlainText . mkAnsiText . PPOutput . doc (defaultOptions tab) $ v diff --git a/src/Juvix/Compiler/Tree/EvaluatorEff.hs b/src/Juvix/Compiler/Tree/EvaluatorEff.hs index d1f374275a..6d1ed15410 100644 --- a/src/Juvix/Compiler/Tree/EvaluatorEff.hs +++ b/src/Juvix/Compiler/Tree/EvaluatorEff.hs @@ -4,14 +4,14 @@ import Control.Exception qualified as Exception import Juvix.Compiler.Core.Data.BinderList qualified as BL import Juvix.Compiler.Tree.Data.InfoTable import Juvix.Compiler.Tree.Error -import Juvix.Compiler.Tree.Evaluator (EvalError (..), printValue, toTreeError, valueToNode) +import Juvix.Compiler.Tree.Evaluator (EvalError (..), toTreeError, valueToNode) +import Juvix.Compiler.Tree.Evaluator.Builtins import Juvix.Compiler.Tree.Extra.Base import Juvix.Compiler.Tree.Language hiding (Output, ask, asks, mapError, output, runError) import Juvix.Compiler.Tree.Language.Value import Juvix.Compiler.Tree.Pretty import Juvix.Prelude.Effects (Eff, IOE, runEff, (:>)) import Juvix.Prelude.Effects qualified as E -import Text.Read qualified as T data EvalCtx = EvalCtx { _evalCtxArgs :: [Value], @@ -49,73 +49,30 @@ eval tab = E.runReader emptyEvalCtx . eval' evalError msg = Exception.throw (EvalError (getNodeLocation node) msg) + eitherToError :: Either Text Value -> Eff r' Value + eitherToError = \case + Left err -> evalError err + Right v -> return v + goBinop :: NodeBinop -> Eff r' Value goBinop NodeBinop {..} = do arg1 <- eval' _nodeBinopArg1 arg2 <- eval' _nodeBinopArg2 case _nodeBinopOpcode of - IntAdd -> goIntBinop (+) arg1 arg2 - IntSub -> goIntBinop (-) arg1 arg2 - IntMul -> goIntBinop (*) arg1 arg2 - IntDiv - | arg2 == ValInteger 0 -> evalError "division by zero" - | otherwise -> goIntBinop quot arg1 arg2 - IntMod - | arg2 == ValInteger 0 -> evalError "division by zero" - | otherwise -> goIntBinop rem arg1 arg2 - IntLe -> goIntCmpBinop (<=) arg1 arg2 - IntLt -> goIntCmpBinop (<) arg1 arg2 - ValEq -> return (ValBool (arg1 == arg2)) - StrConcat -> goStrConcat arg1 arg2 + PrimBinop op -> eitherToError $ evalBinop op arg1 arg2 OpSeq -> return arg2 - goIntBinop :: (Integer -> Integer -> Integer) -> Value -> Value -> Eff r' Value - goIntBinop f v1 v2 = case (v1, v2) of - (ValInteger i1, ValInteger i2) -> return (ValInteger (f i1 i2)) - _ -> evalError "expected two integer arguments" - - goIntCmpBinop :: (Integer -> Integer -> Bool) -> Value -> Value -> Eff r' Value - goIntCmpBinop f v1 v2 = case (v1, v2) of - (ValInteger i1, ValInteger i2) -> return (ValBool (f i1 i2)) - _ -> evalError "expected two integer arguments" - - goStrConcat :: Value -> Value -> Eff r' Value - goStrConcat v1 v2 = case (v1, v2) of - (ValString s1, ValString s2) -> return (ValString (s1 <> s2)) - _ -> evalError "expected two string arguments" - goUnop :: NodeUnop -> Eff r' Value goUnop NodeUnop {..} = do v <- eval' _nodeUnopArg case _nodeUnopOpcode of - OpShow -> return (ValString (printValue tab v)) - OpStrToInt -> goStringUnop strToInt v + PrimUnop op -> eitherToError $ evalUnop tab op v OpTrace -> goTrace v OpFail -> goFail v - OpArgsNum -> goArgsNum v - - strToInt :: Text -> Eff r' Value - strToInt s = case T.readMaybe (fromText s) of - Just i -> return (ValInteger i) - Nothing -> evalError "string to integer: not an integer" - - goStringUnop :: (Text -> Eff r' Value) -> Value -> Eff r' Value - goStringUnop f = \case - ValString s -> f s - _ -> evalError "expected a string argument" goFail :: Value -> Eff r' Value goFail v = evalError ("failure: " <> printValue tab v) - goArgsNum :: Value -> Eff r' Value - goArgsNum = \case - ValClosure Closure {..} -> return (ValInteger (fromIntegral argsNum)) - where - fi = lookupFunInfo tab _closureSymbol - argsNum = fi ^. functionArgsNum - length _closureArgs - _ -> - evalError "expected a closure" - goTrace :: Value -> Eff r' Value goTrace v = E.output v $> v diff --git a/src/Juvix/Compiler/Tree/EvaluatorSem.hs b/src/Juvix/Compiler/Tree/EvaluatorSem.hs index d62a97c911..7d9d96ce79 100644 --- a/src/Juvix/Compiler/Tree/EvaluatorSem.hs +++ b/src/Juvix/Compiler/Tree/EvaluatorSem.hs @@ -4,12 +4,12 @@ import Control.Exception qualified as Exception import Juvix.Compiler.Core.Data.BinderList qualified as BL import Juvix.Compiler.Tree.Data.InfoTable import Juvix.Compiler.Tree.Error -import Juvix.Compiler.Tree.Evaluator (EvalError (..), printValue, toTreeError, valueToNode) +import Juvix.Compiler.Tree.Evaluator (EvalError (..), toTreeError, valueToNode) +import Juvix.Compiler.Tree.Evaluator.Builtins import Juvix.Compiler.Tree.Extra.Base import Juvix.Compiler.Tree.Language import Juvix.Compiler.Tree.Language.Value import Juvix.Compiler.Tree.Pretty -import Text.Read qualified as T data EvalCtx = EvalCtx { _evalCtxArgs :: [Value], @@ -47,73 +47,30 @@ eval tab = runReader emptyEvalCtx . eval' evalError msg = Exception.throw (EvalError (getNodeLocation node) msg) + eitherToError :: Either Text Value -> Sem r' Value + eitherToError = \case + Left err -> evalError err + Right v -> return v + goBinop :: NodeBinop -> Sem r' Value goBinop NodeBinop {..} = do arg1 <- eval' _nodeBinopArg1 arg2 <- eval' _nodeBinopArg2 case _nodeBinopOpcode of - IntAdd -> goIntBinop (+) arg1 arg2 - IntSub -> goIntBinop (-) arg1 arg2 - IntMul -> goIntBinop (*) arg1 arg2 - IntDiv - | arg2 == ValInteger 0 -> evalError "division by zero" - | otherwise -> goIntBinop quot arg1 arg2 - IntMod - | arg2 == ValInteger 0 -> evalError "division by zero" - | otherwise -> goIntBinop rem arg1 arg2 - IntLe -> goIntCmpBinop (<=) arg1 arg2 - IntLt -> goIntCmpBinop (<) arg1 arg2 - ValEq -> return (ValBool (arg1 == arg2)) - StrConcat -> goStrConcat arg1 arg2 + PrimBinop op -> eitherToError $ evalBinop op arg1 arg2 OpSeq -> return arg2 - goIntBinop :: (Integer -> Integer -> Integer) -> Value -> Value -> Sem r' Value - goIntBinop f v1 v2 = case (v1, v2) of - (ValInteger i1, ValInteger i2) -> return (ValInteger (f i1 i2)) - _ -> evalError "expected two integer arguments" - - goIntCmpBinop :: (Integer -> Integer -> Bool) -> Value -> Value -> Sem r' Value - goIntCmpBinop f v1 v2 = case (v1, v2) of - (ValInteger i1, ValInteger i2) -> return (ValBool (f i1 i2)) - _ -> evalError "expected two integer arguments" - - goStrConcat :: Value -> Value -> Sem r' Value - goStrConcat v1 v2 = case (v1, v2) of - (ValString s1, ValString s2) -> return (ValString (s1 <> s2)) - _ -> evalError "expected two string arguments" - goUnop :: NodeUnop -> Sem r' Value goUnop NodeUnop {..} = do v <- eval' _nodeUnopArg case _nodeUnopOpcode of - OpShow -> return (ValString (printValue tab v)) - OpStrToInt -> goStringUnop strToInt v + PrimUnop op -> eitherToError $ evalUnop tab op v OpTrace -> goTrace v OpFail -> goFail v - OpArgsNum -> goArgsNum v - - strToInt :: Text -> Sem r' Value - strToInt s = case T.readMaybe (fromText s) of - Just i -> return (ValInteger i) - Nothing -> evalError "string to integer: not an integer" - - goStringUnop :: (Text -> Sem r' Value) -> Value -> Sem r' Value - goStringUnop f = \case - ValString s -> f s - _ -> evalError "expected a string argument" goFail :: Value -> Sem r' Value goFail v = evalError ("failure: " <> printValue tab v) - goArgsNum :: Value -> Sem r' Value - goArgsNum = \case - ValClosure Closure {..} -> return (ValInteger (fromIntegral argsNum)) - where - fi = lookupFunInfo tab _closureSymbol - argsNum = fi ^. functionArgsNum - length _closureArgs - _ -> - evalError "expected a closure" - goTrace :: Value -> Sem r' Value goTrace v = output v $> v diff --git a/src/Juvix/Compiler/Tree/Language.hs b/src/Juvix/Compiler/Tree/Language.hs index 396173f882..17c892a437 100644 --- a/src/Juvix/Compiler/Tree/Language.hs +++ b/src/Juvix/Compiler/Tree/Language.hs @@ -1,10 +1,12 @@ module Juvix.Compiler.Tree.Language ( module Juvix.Compiler.Tree.Language, module Juvix.Compiler.Tree.Language.Base, + module Juvix.Compiler.Tree.Language.Builtins, ) where import Juvix.Compiler.Tree.Language.Base +import Juvix.Compiler.Tree.Language.Builtins -- | Function call type data CallType @@ -53,32 +55,17 @@ newtype NodeInfo = NodeInfo deriving newtype (Semigroup, Monoid) data BinaryOpcode - = IntAdd - | IntSub - | IntMul - | IntDiv - | IntMod - | IntLt - | IntLe - | ValEq - | StrConcat + = PrimBinop BinaryOp | -- | Sequence: evaluate and ignore fist argument, return evaluated second -- argument. JVT code: 'seq(x1, x2)'. OpSeq data UnaryOpcode - = -- | Convert the argument to a string. JVT code: 'show(x)'. - OpShow - | -- | Convert a string to an integer. JVT opcode: 'atoi(x)'. - OpStrToInt - | -- | Print a debug log of the argument and return it. JVT code: 'trace(x)'. + = PrimUnop UnaryOp + | -- | Print a debug log of the argument and return it. OpTrace - | -- | Interrupt execution with a runtime error printing the argument. JVT - -- code: 'fail(x)'. + | -- | Interrupt execution with a runtime error printing the argument. OpFail - | -- | Compute the number of expected arguments for the given closure. JVT - -- code: 'argsnum(x)'. - OpArgsNum data NodeBinop = NodeBinop { _nodeBinopInfo :: NodeInfo, diff --git a/src/Juvix/Compiler/Tree/Language/Builtins.hs b/src/Juvix/Compiler/Tree/Language/Builtins.hs new file mode 100644 index 0000000000..eb3ebd57d8 --- /dev/null +++ b/src/Juvix/Compiler/Tree/Language/Builtins.hs @@ -0,0 +1,25 @@ +module Juvix.Compiler.Tree.Language.Builtins where + +import Juvix.Prelude + +data BinaryOp + = OpIntAdd + | OpIntSub + | OpIntMul + | OpIntDiv + | OpIntMod + | OpIntLt + | OpIntLe + | OpEq + | OpStrConcat + deriving stock (Eq) + +data UnaryOp + = -- | Convert the argument to a string. JV* opcode: `show`. + OpShow + | -- | Convert a string to an integer. JV* opcode: `atoi`. + OpStrToInt + | -- | Compute the number of expected arguments for the given closure. JV* + -- opcode: `argsnum`. + OpArgsNum + deriving stock (Eq) diff --git a/src/Juvix/Compiler/Tree/Pretty/Base.hs b/src/Juvix/Compiler/Tree/Pretty/Base.hs index da6f484c2a..54680e66d5 100644 --- a/src/Juvix/Compiler/Tree/Pretty/Base.hs +++ b/src/Juvix/Compiler/Tree/Pretty/Base.hs @@ -192,17 +192,21 @@ instance PrettyCode Constant where ConstVoid {} -> return $ annotate (AnnKind KNameConstructor) Str.void +instance PrettyCode BinaryOp where + ppCode op = return $ primitive $ case op of + OpIntAdd -> Str.instrAdd + OpIntSub -> Str.instrSub + OpIntMul -> Str.instrMul + OpIntDiv -> Str.instrDiv + OpIntMod -> Str.instrMod + OpIntLt -> Str.instrLt + OpIntLe -> Str.instrLe + OpEq -> Str.instrEq + OpStrConcat -> Str.instrStrConcat + instance PrettyCode BinaryOpcode where ppCode = \case - IntAdd -> return $ primitive Str.instrAdd - IntSub -> return $ primitive Str.instrSub - IntMul -> return $ primitive Str.instrMul - IntDiv -> return $ primitive Str.instrDiv - IntMod -> return $ primitive Str.instrMod - IntLt -> return $ primitive Str.instrLt - IntLe -> return $ primitive Str.instrLe - ValEq -> return $ primitive Str.instrEq - StrConcat -> return $ primitive Str.instrStrConcat + PrimBinop x -> ppCode x OpSeq -> return $ primitive Str.sseq_ instance PrettyCode NodeBinop where @@ -212,13 +216,17 @@ instance PrettyCode NodeBinop where arg2 <- ppCode _nodeBinopArg2 return $ op <> parens (arg1 <> comma <+> arg2) +instance PrettyCode UnaryOp where + ppCode op = return $ primitive $ case op of + OpShow -> Str.instrShow + OpStrToInt -> Str.instrStrToInt + OpArgsNum -> Str.instrArgsNum + instance PrettyCode UnaryOpcode where ppCode = \case - OpShow -> return $ primitive Str.instrShow - OpStrToInt -> return $ primitive Str.instrStrToInt + PrimUnop x -> ppCode x OpTrace -> return $ primitive Str.instrTrace OpFail -> return $ primitive Str.instrFailure - OpArgsNum -> return $ primitive Str.instrArgsNum instance PrettyCode NodeUnop where ppCode NodeUnop {..} = do diff --git a/src/Juvix/Compiler/Tree/Transformation/Validate.hs b/src/Juvix/Compiler/Tree/Transformation/Validate.hs index d297a25f36..c2294ef8c1 100644 --- a/src/Juvix/Compiler/Tree/Transformation/Validate.hs +++ b/src/Juvix/Compiler/Tree/Transformation/Validate.hs @@ -28,15 +28,7 @@ inferType tab funInfo = goInfer mempty goBinop :: BinderList Type -> NodeBinop -> Sem r Type goBinop bl NodeBinop {..} = case _nodeBinopOpcode of - IntAdd -> checkBinop mkTypeInteger mkTypeInteger mkTypeInteger - IntSub -> checkBinop mkTypeInteger mkTypeInteger mkTypeInteger - IntMul -> checkBinop mkTypeInteger mkTypeInteger mkTypeInteger - IntDiv -> checkBinop mkTypeInteger mkTypeInteger mkTypeInteger - IntMod -> checkBinop mkTypeInteger mkTypeInteger mkTypeInteger - IntLt -> checkBinop mkTypeInteger mkTypeInteger mkTypeBool - IntLe -> checkBinop mkTypeInteger mkTypeInteger mkTypeBool - ValEq -> checkBinop TyDynamic TyDynamic mkTypeBool - StrConcat -> checkBinop TyString TyString TyString + PrimBinop x -> checkPrimBinop x OpSeq -> do checkType bl _nodeBinopArg1 TyDynamic goInfer bl _nodeBinopArg2 @@ -51,13 +43,23 @@ inferType tab funInfo = goInfer mempty void $ unifyTypes' loc tab ty2 ty2' return rty + checkPrimBinop :: BinaryOp -> Sem r Type + checkPrimBinop = \case + OpIntAdd -> checkBinop mkTypeInteger mkTypeInteger mkTypeInteger + OpIntSub -> checkBinop mkTypeInteger mkTypeInteger mkTypeInteger + OpIntMul -> checkBinop mkTypeInteger mkTypeInteger mkTypeInteger + OpIntDiv -> checkBinop mkTypeInteger mkTypeInteger mkTypeInteger + OpIntMod -> checkBinop mkTypeInteger mkTypeInteger mkTypeInteger + OpIntLt -> checkBinop mkTypeInteger mkTypeInteger mkTypeBool + OpIntLe -> checkBinop mkTypeInteger mkTypeInteger mkTypeBool + OpEq -> checkBinop TyDynamic TyDynamic mkTypeBool + OpStrConcat -> checkBinop TyString TyString TyString + goUnop :: BinderList Type -> NodeUnop -> Sem r Type goUnop bl NodeUnop {..} = case _nodeUnopOpcode of - OpShow -> checkUnop TyDynamic TyString - OpStrToInt -> checkUnop TyString mkTypeInteger + PrimUnop x -> checkPrimUnop x OpTrace -> goInfer bl _nodeUnopArg OpFail -> checkUnop TyDynamic TyDynamic - OpArgsNum -> checkUnop TyDynamic mkTypeInteger where loc = _nodeUnopInfo ^. nodeInfoLocation @@ -67,6 +69,12 @@ inferType tab funInfo = goInfer mempty void $ unifyTypes' loc tab ty ty' return rty + checkPrimUnop :: UnaryOp -> Sem r Type + checkPrimUnop = \case + OpShow -> checkUnop TyDynamic TyString + OpStrToInt -> checkUnop TyString mkTypeInteger + OpArgsNum -> checkUnop TyDynamic mkTypeInteger + goConst :: BinderList Type -> NodeConstant -> Sem r Type goConst _ NodeConstant {..} = case _nodeConstant of ConstInt {} -> return mkTypeInteger diff --git a/src/Juvix/Compiler/Tree/Translation/FromAsm.hs b/src/Juvix/Compiler/Tree/Translation/FromAsm.hs index 769d7a3f9f..034677fa06 100644 --- a/src/Juvix/Compiler/Tree/Translation/FromAsm.hs +++ b/src/Juvix/Compiler/Tree/Translation/FromAsm.hs @@ -85,16 +85,14 @@ goFunction infoTab fi = do where goInstr :: Asm.CmdInstr -> Sem r Node goInstr Asm.CmdInstr {..} = case _cmdInstrInstruction of - Asm.Binop op -> goBinop (translateBinop op) - Asm.ValShow -> goUnop OpShow - Asm.StrToInt -> goUnop OpStrToInt + Asm.Binop op -> goBinop (PrimBinop op) + Asm.Unop op -> goUnop (PrimUnop op) Asm.Push (Asm.Constant c) -> return (mkConst c) Asm.Push (Asm.Ref r) -> return (mkMemRef r) Asm.Pop -> goPop Asm.Trace -> goTrace Asm.Dump -> unsupported (_cmdInstrInfo ^. Asm.commandInfoLocation) Asm.Failure -> goUnop OpFail - Asm.ArgsNum -> goUnop OpArgsNum Asm.Prealloc {} -> unsupported (_cmdInstrInfo ^. Asm.commandInfoLocation) Asm.AllocConstr tag -> goAllocConstr tag Asm.AllocClosure x -> goAllocClosure x @@ -196,18 +194,6 @@ goFunction infoTab fi = do _nodeSaveBody = body } - translateBinop :: Asm.Opcode -> BinaryOpcode - translateBinop = \case - Asm.IntAdd -> IntAdd - Asm.IntSub -> IntSub - Asm.IntMul -> IntMul - Asm.IntDiv -> IntDiv - Asm.IntMod -> IntMod - Asm.IntLt -> IntLt - Asm.IntLe -> IntLe - Asm.ValEq -> ValEq - Asm.StrConcat -> StrConcat - goBinop :: BinaryOpcode -> Sem r Node goBinop op = do arg1 <- goCode diff --git a/src/Juvix/Compiler/Tree/Translation/FromCore.hs b/src/Juvix/Compiler/Tree/Translation/FromCore.hs index af34559b33..c12ab170f8 100644 --- a/src/Juvix/Compiler/Tree/Translation/FromCore.hs +++ b/src/Juvix/Compiler/Tree/Translation/FromCore.hs @@ -261,22 +261,22 @@ genCode infoTable fi = genBinOp :: Core.BuiltinOp -> BinaryOpcode genBinOp = \case - Core.OpIntAdd -> IntAdd - Core.OpIntSub -> IntSub - Core.OpIntMul -> IntMul - Core.OpIntDiv -> IntDiv - Core.OpIntMod -> IntMod - Core.OpIntLt -> IntLt - Core.OpIntLe -> IntLe - Core.OpEq -> ValEq - Core.OpStrConcat -> StrConcat + Core.OpIntAdd -> PrimBinop OpIntAdd + Core.OpIntSub -> PrimBinop OpIntSub + Core.OpIntMul -> PrimBinop OpIntMul + Core.OpIntDiv -> PrimBinop OpIntDiv + Core.OpIntMod -> PrimBinop OpIntMod + Core.OpIntLt -> PrimBinop OpIntLt + Core.OpIntLe -> PrimBinop OpIntLe + Core.OpEq -> PrimBinop OpEq + Core.OpStrConcat -> PrimBinop OpStrConcat Core.OpSeq -> OpSeq _ -> impossible genUnOp :: Core.BuiltinOp -> UnaryOpcode genUnOp = \case - Core.OpShow -> OpShow - Core.OpStrToInt -> OpStrToInt + Core.OpShow -> PrimUnop OpShow + Core.OpStrToInt -> PrimUnop OpStrToInt Core.OpTrace -> OpTrace Core.OpFail -> OpFail _ -> impossible diff --git a/src/Juvix/Compiler/Tree/Translation/FromSource.hs b/src/Juvix/Compiler/Tree/Translation/FromSource.hs index f231ed1a25..504c243ccb 100644 --- a/src/Juvix/Compiler/Tree/Translation/FromSource.hs +++ b/src/Juvix/Compiler/Tree/Translation/FromSource.hs @@ -68,15 +68,15 @@ parseBinop :: (Members '[Reader ParserSig, InfoTableBuilder, State LocalParams] r) => ParsecS r NodeBinop parseBinop = - parseBinaryOp kwAdd_ IntAdd - <|> parseBinaryOp kwSub_ IntSub - <|> parseBinaryOp kwMul_ IntMul - <|> parseBinaryOp kwDiv_ IntDiv - <|> parseBinaryOp kwMod_ IntMod - <|> parseBinaryOp kwLt_ IntLt - <|> parseBinaryOp kwLe_ IntLe - <|> parseBinaryOp kwEq_ ValEq - <|> parseBinaryOp kwStrcat StrConcat + parseBinaryOp kwAdd_ (PrimBinop OpIntAdd) + <|> parseBinaryOp kwSub_ (PrimBinop OpIntSub) + <|> parseBinaryOp kwMul_ (PrimBinop OpIntMul) + <|> parseBinaryOp kwDiv_ (PrimBinop OpIntDiv) + <|> parseBinaryOp kwMod_ (PrimBinop OpIntMod) + <|> parseBinaryOp kwLt_ (PrimBinop OpIntLt) + <|> parseBinaryOp kwLe_ (PrimBinop OpIntLe) + <|> parseBinaryOp kwEq_ (PrimBinop OpEq) + <|> parseBinaryOp kwStrcat (PrimBinop OpStrConcat) <|> parseBinaryOp kwSeq_ OpSeq parseBinaryOp :: @@ -97,11 +97,11 @@ parseUnop :: (Members '[Reader ParserSig, InfoTableBuilder, State LocalParams] r) => ParsecS r NodeUnop parseUnop = - parseUnaryOp kwShow OpShow - <|> parseUnaryOp kwAtoi OpStrToInt + parseUnaryOp kwShow (PrimUnop OpShow) + <|> parseUnaryOp kwAtoi (PrimUnop OpStrToInt) <|> parseUnaryOp kwTrace OpTrace <|> parseUnaryOp kwFail OpFail - <|> parseUnaryOp kwArgsNum OpArgsNum + <|> parseUnaryOp kwArgsNum (PrimUnop OpArgsNum) parseUnaryOp :: (Members '[Reader ParserSig, InfoTableBuilder, State LocalParams] r) =>