From f5e7b7c091ee8fe8c2adbbb8804df356581f088d Mon Sep 17 00:00:00 2001 From: vvsob Date: Tue, 30 Dec 2025 01:27:01 +0300 Subject: [PATCH] expressions --- app/Main.hs | 9 +++---- src/Lox/Expr.hs | 6 +++++ src/Lox/Interpreter.hs | 38 ++++++++++++++++++++-------- src/Lox/Parser.hs | 57 +++++++++++++++++++++++++++++++++++++++--- src/Lox/Scanner.hs | 6 ++--- 5 files changed, 95 insertions(+), 21 deletions(-) diff --git a/app/Main.hs b/app/Main.hs index ff2ce5f..a4dff38 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -9,13 +9,12 @@ run source = do case tokensMaybe of Left UnexpectedCharacterError -> putStrLn "Unexpected character" Right tokens -> do - let exprMaybe = parse tokens - case exprMaybe of + let stmtMaybe = parse tokens + case stmtMaybe of Left ExpectedExpressionError -> putStrLn "Expected expression" Left MismatchedParenthesesError -> putStrLn "Mismatched parentheses" - Right expr -> do - result <- eval expr - print result + Left ExpectedSemicolonError -> putStrLn "Expected semicolon" + Right statements -> runStatements statements main :: IO () main = putStr ">> " >> hFlush stdout >> getLine >>= run diff --git a/src/Lox/Expr.hs b/src/Lox/Expr.hs index 7a6c174..e181e66 100644 --- a/src/Lox/Expr.hs +++ b/src/Lox/Expr.hs @@ -1,9 +1,15 @@ module Lox.Expr ( + Stmt (..), Expr (..) ) where import Lox.Scanner +data Stmt = + Expression Expr | + Print Expr + deriving Show + data Expr = Literal Object | Unary Token Expr | diff --git a/src/Lox/Interpreter.hs b/src/Lox/Interpreter.hs index 840b75d..598f8ed 100644 --- a/src/Lox/Interpreter.hs +++ b/src/Lox/Interpreter.hs @@ -1,30 +1,48 @@ module Lox.Interpreter ( + runStatements, eval ) where import Lox.Expr import Lox.Scanner import Control.Monad.State +import Control.Monad -data InterpreterState = InterpreterState +data InterpreterState = InterpreterState (IO ()) + +emptyInterpreter :: InterpreterState +emptyInterpreter = InterpreterState (return ()) + +runStatements :: [Stmt] -> IO () +runStatements s = io + where InterpreterState io = execState (interpret s) emptyInterpreter + +interpret :: [Stmt] -> State InterpreterState () +interpret = foldr ((>>) . execute) (return ()) + +execute :: Stmt -> State InterpreterState () +execute (Print expr) = do + value <- evalFrom expr + modify (\(InterpreterState s) -> InterpreterState (s >> print value)) +execute (Expression value) = void $ evalFrom value eval :: Expr -> IO Object -eval expr = return $ evalState (interpret expr) InterpreterState +eval expr = return $ evalState (evalFrom expr) $ InterpreterState (return ()) -interpret :: Expr -> State InterpreterState Object -interpret (Literal value) = return value -interpret (Grouping expr) = interpret expr -interpret (Unary op expr) = do - right <- interpret expr +evalFrom :: Expr -> State InterpreterState Object +evalFrom (Literal value) = return value +evalFrom (Grouping expr) = evalFrom expr +evalFrom (Unary op expr) = do + right <- evalFrom expr case (tokenType op, right) of (MINUS, NumberObject x) -> return $ NumberObject (-x) (BANG, NullObject) -> return $ BoolObject False (BANG, BoolObject x) -> return $ BoolObject (not x) (BANG, _) -> return $ BoolObject True _ -> error "Type error" -interpret (Binary leftExpr op rightExpr) = do - left <- interpret leftExpr - right <- interpret rightExpr +evalFrom (Binary leftExpr op rightExpr) = do + left <- evalFrom leftExpr + right <- evalFrom rightExpr case (tokenType op, left, right) of (PLUS, NumberObject x, NumberObject y) -> return $ NumberObject (x + y) (MINUS, NumberObject x, NumberObject y) -> return $ NumberObject (x - y) diff --git a/src/Lox/Parser.hs b/src/Lox/Parser.hs index 1966224..377d22e 100644 --- a/src/Lox/Parser.hs +++ b/src/Lox/Parser.hs @@ -10,7 +10,19 @@ import Lox.Expr data ParserState = ParserState {tokens :: [Token]} -data ParserError = MismatchedParenthesesError | ExpectedExpressionError +data ParserError = MismatchedParenthesesError + | ExpectedExpressionError + | ExpectedSemicolonError + deriving Show + +-- program → statement* EOF ; +-- +-- statement → exprStmt +-- | printStmt ; +-- +-- exprStmt → expression ";" ; +-- printStmt → "print" expression ";" ; + -- expression → equality ; -- equality → comparison ( ( "!=" | "==" ) comparison )* ; @@ -22,8 +34,47 @@ data ParserError = MismatchedParenthesesError | ExpectedExpressionError -- primary → NUMBER | STRING | "true" | "false" | "nil" -- | "(" expression ")" ; -parse :: [Token] -> Either ParserError Expr -parse tokens = evalState expression (ParserState {tokens=tokens}) +parse :: [Token] -> Either ParserError [Stmt] +parse tokens = evalState program (ParserState {tokens=tokens}) + +program :: State ParserState (Either ParserError [Stmt]) +program = do + atEnd <- isAtEnd + if atEnd then return $ Right [] else do + headMaybe <- statement + case headMaybe of + Left err -> return $ Left err + Right head -> do + tailMaybe <- program + case tailMaybe of + Left err -> return $ Left err + Right tail -> return $ Right $ head : tail + +statement :: State ParserState (Either ParserError Stmt) +statement = do + printMaybe <- matchToken [PRINT] + case printMaybe of + Just _ -> printStatement + _ -> expressionStatement + +printStatement :: State ParserState (Either ParserError Stmt) +printStatement = do + valueMaybe <- expression + semicolonMaybe <- consume SEMICOLON ExpectedSemicolonError + case (valueMaybe, semicolonMaybe) of + (Left err, _) -> return $ Left err + (_, Left err) -> return $ Left err + (Right value, Right _) -> return $ Right $ Print value + +expressionStatement :: State ParserState (Either ParserError Stmt) +expressionStatement = do + valueMaybe <- expression + semicolonMaybe <- consume SEMICOLON ExpectedSemicolonError + case (valueMaybe, semicolonMaybe) of + (Left err, _) -> return $ Left err + (_, Left err) -> return $ Left err + (Right value, Right _) -> return $ Right $ Expression value + expression :: State ParserState (Either ParserError Expr) expression = equality diff --git a/src/Lox/Scanner.hs b/src/Lox/Scanner.hs index 2570af4..44ace2f 100644 --- a/src/Lox/Scanner.hs +++ b/src/Lox/Scanner.hs @@ -31,7 +31,7 @@ data Object = NullObject instance Show Object where show NullObject = "Nil" - show (StringObject s) = show s + show (StringObject s) = s show (NumberObject x) = show x show (BoolObject False) = "false" show (BoolObject True) = "true" @@ -45,7 +45,7 @@ data Token = Token { data ScannerState = ScannerState {source :: String, current :: String, lineNumber :: Int} -data ScannerError = UnexpectedCharacterError +data ScannerError = UnexpectedCharacterError deriving Show emptyScannerState :: String -> ScannerState emptyScannerState source = @@ -96,7 +96,7 @@ scanToken = do ' ' -> return nothing '\r' -> return nothing '\t' -> return nothing - '\n' -> return nothing + '\n' -> modify (\s@(ScannerState {lineNumber=n}) -> s {lineNumber=n+1}) >> return nothing c -> if isDigit c then ok <$> scanNumber else if isAlpha c then ok <$> scanIdentifier else return $ Left UnexpectedCharacterError scanString :: State ScannerState Token