This commit is contained in:
Tsuki 2023-12-18 18:29:13 +08:00
commit 2783fd2740
17 changed files with 804 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.stack-work/
*~

6
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"cSpell.words": [
"elems",
"foldr"
]
}

11
CHANGELOG.md Normal file
View File

@ -0,0 +1,11 @@
# Changelog for `meo`
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to the
[Haskell Package Versioning Policy](https://pvp.haskell.org/).
## Unreleased
## 0.1.0.0 - YYYY-MM-DD

30
LICENSE Normal file
View File

@ -0,0 +1,30 @@
Copyright Author name here (c) 2023
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Author name here nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

1
README.md Normal file
View File

@ -0,0 +1 @@
# meo

2
Setup.hs Normal file
View File

@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain

7
app/Main.hs Normal file
View File

@ -0,0 +1,7 @@
module Main (main) where
import Lib (mainIO)
import Premitive (ttest)
main :: IO ()
main = mainIO

91
meo.cabal Normal file
View File

@ -0,0 +1,91 @@
cabal-version: 2.2
-- This file has been generated from package.yaml by hpack version 0.35.2.
--
-- see: https://github.com/sol/hpack
name: meo
version: 0.1.0.0
description: Please see the README on GitHub at <https://github.com/githubuser/meo#readme>
homepage: https://github.com/githubuser/meo#readme
bug-reports: https://github.com/githubuser/meo/issues
author: Author name here
maintainer: example@example.com
copyright: 2023 Author name here
license: BSD-3-Clause
license-file: LICENSE
build-type: Simple
extra-source-files:
README.md
CHANGELOG.md
source-repository head
type: git
location: https://github.com/githubuser/meo
library
exposed-modules:
Ast
Lib
Premitive
Type
other-modules:
Paths_meo
autogen-modules:
Paths_meo
hs-source-dirs:
src
ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints
build-depends:
base >=4.7 && <5
, cmdargs
, containers
, exceptions
, monad-logger
, mtl
, parsec
, text
default-language: Haskell2010
executable meo-exe
main-is: Main.hs
other-modules:
Paths_meo
autogen-modules:
Paths_meo
hs-source-dirs:
app
ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N
build-depends:
base >=4.7 && <5
, cmdargs
, containers
, exceptions
, meo
, monad-logger
, mtl
, parsec
, text
default-language: Haskell2010
test-suite meo-test
type: exitcode-stdio-1.0
main-is: Spec.hs
other-modules:
Paths_meo
autogen-modules:
Paths_meo
hs-source-dirs:
test
ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N
build-depends:
base >=4.7 && <5
, cmdargs
, containers
, exceptions
, meo
, monad-logger
, mtl
, parsec
, text
default-language: Haskell2010

66
package.yaml Normal file
View File

@ -0,0 +1,66 @@
name: meo
version: 0.1.0.0
github: "githubuser/meo"
license: BSD-3-Clause
author: "Author name here"
maintainer: "example@example.com"
copyright: "2023 Author name here"
extra-source-files:
- README.md
- CHANGELOG.md
# Metadata used when publishing your package
# synopsis: Short description of your package
# category: Web
# To avoid duplicated efforts in documentation and dealing with the
# complications of embedding Haddock markup inside cabal files, it is
# common to point users to the README.md file.
description: Please see the README on GitHub at <https://github.com/githubuser/meo#readme>
dependencies:
- base >= 4.7 && < 5
- parsec
- mtl
- containers
- text
- cmdargs
- exceptions
- monad-logger
ghc-options:
- -Wall
- -Wcompat
- -Widentities
- -Wincomplete-record-updates
- -Wincomplete-uni-patterns
- -Wmissing-export-lists
- -Wmissing-home-modules
- -Wpartial-fields
- -Wredundant-constraints
library:
source-dirs: src
executables:
meo-exe:
main: Main.hs
source-dirs: app
ghc-options:
- -threaded
- -rtsopts
- -with-rtsopts=-N
dependencies:
- meo
tests:
meo-test:
main: Spec.hs
source-dirs: test
ghc-options:
- -threaded
- -rtsopts
- -with-rtsopts=-N
dependencies:
- meo

73
src/Ast.hs Normal file
View File

@ -0,0 +1,73 @@
{-# LANGUAGE TupleSections #-}
module Ast where
import Foreign (Storable)
import Premitive (Expr,Stmt)
type Location = Int
type Index = [String]
type Stack = [Int]
position :: String -> Index -> Location
position name index = let pos n (nm:nms) =
if name == nm
then n
else pos (n+1) nms
pos _ [] = -1
in pos 1 index
fetch :: Location -> Stack -> Int
fetch n (v:vs) = if n == 1
then v
else fetch (n-1) vs
fetch _ [] = -1
put :: Location -> Int -> Stack -> Stack
put n x (v:vs) = if n == 1
then x:vs
else v:put (n-1) x vs
put _ _ [] = []
newtype M a = StOut (Stack -> (a, Stack, String))
unStOut :: M a -> Stack -> (a, Stack, String)
unStOut (StOut o) = o
instance Functor M where
fmap f e = StOut (\n -> let (a,n1,s1) = unStOut e n in
(f a, n1, s1)
)
instance Applicative M where
pure x = StOut (x,,"")
ef <*> e = StOut (\n -> let (f,n1,s1) = unStOut ef n
(a,n2,s2) = unStOut e n1 in
(f a, n2, s1++s2)
)
instance Monad M where
return = pure
e >>= f = StOut (\n -> let (a,n1,s1) = unStOut e n
(b,n2,s2) = unStOut (f a) n1 in
(b, n2, s1++s2)
)
getFrom :: Location -> M Int
getFrom i = StOut (\ns -> (fetch i ns, ns, ""))
write :: Location -> Int -> M ()
write i v = StOut (\ns -> (() , put i v ns, ""))
push :: Int -> M ()
push x = StOut (\ns -> ((), x:ns, ""))
pop :: M ()
pop = StOut (\(_:ns) -> ((), ns,""))
output :: Show a => a -> M ()
output v = StOut ((), , show v)

30
src/Lib.hs Normal file
View File

@ -0,0 +1,30 @@
{-# LANGUAGE OverloadedStrings #-}
module Lib (mainIO) where
import Control.Exception (SomeException)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Logger
import Control.Monad.Reader
import qualified Data.Text.IO as TIO
import System.IO.Error (isDoesNotExistError)
data Config = Config
{ -- | Path to the log file
configLogLevel :: LogLevel -- 日志级别
}
type App = ReaderT Config (LoggingT IO)
-- 日志记录和读取配置的函数
appFunction :: App ()
appFunction = do
Config logLevel <- ask
lift $ filterLogger (\_ level -> level >= logLevel) $ do
logInfoN "This is an informational message."
logDebugN "This is a debug message."
mainIO :: IO ()
mainIO = do
let config = Config LevelInfo
runStdoutLoggingT $ runReaderT appFunction config

209
src/Premitive.hs Normal file
View File

@ -0,0 +1,209 @@
{-# LANGUAGE PatternSynonyms #-}
module Premitive (ttest, Stmt, Expr (..)) where
import Data.Functor.Identity
import Text.Parsec
import qualified Text.Parsec.Expr as E
import Text.Parsec.Language
import Text.Parsec.String
import qualified Text.Parsec.Token as T
types :: [String]
types = ["number", "bool", "string", "list", "nil", "func"]
reservedNames :: [String]
reservedNames = ["if", "then", "else", "while", "do", "process", "pipeline", "in", "()", "skip", "declare", "func"] ++ types
reservedOpNames :: [String]
reservedOpNames = ["+", "-", "*", "/", ":=", "==", "<", ">", "&&", "||", "!", ";", "|", ">>>", "<<<", ">>", "<<"]
-- 定义语言
languageDef =
emptyDef
{ T.commentStart = "/*",
T.commentEnd = "*/",
T.commentLine = "//",
T.nestedComments = True,
T.identStart = letter :: Parser Char,
T.identLetter = alphaNum :: Parser Char,
T.opStart = oneOf "+-*/",
T.opLetter = oneOf "+-*/",
T.reservedOpNames = reservedOpNames,
T.reservedNames = reservedNames
}
-- 创建 TokenParser
lexer = T.makeTokenParser languageDef
data Stmt
= If Expr Stmt Stmt
| While Expr Stmt
| Assign String Expr
| Seq Stmt Stmt
| Skip
deriving (Show)
data Ks = Ks String Expr deriving (Show)
ksTerm :: Parser Ks
ksTerm = do
k <- T.identifier lexer <* T.colon lexer
Ks k <$> expr
data IntOrFloat = IntValue Integer | FloatValue Double deriving (Show)
-- 定义算术表达式的数据类型
data Expr
= Num IntOrFloat
| Bool Bool
| Nil
| Str String
| Pipeln String [Expr]
| List [Expr]
| Process String [Ks]
| Block Stmt Expr
| Symbol String
| IfExpr Expr Expr Expr
| Declare [String] [Expr] Expr
| Bin String Expr Expr
| Con Expr Expr
| Func String Expr
deriving (Show)
data BasicType = NumberType | BoolType | StringType | ListType | NilType | FuncType BasicType BasicType deriving (Show)
prefix :: String -> (Expr -> Expr) -> E.Operator String () Identity Expr
prefix name fun = E.Prefix (do _ <- T.reservedOp lexer name; return fun)
binary :: String -> (Expr -> Expr -> Expr) -> E.Operator String () Identity Expr
binary name fun = E.Infix (T.reservedOp lexer name >> return fun) E.AssocLeft
postfix :: String -> (Expr -> Expr) -> E.Operator String () Identity Expr
postfix name fun = E.Postfix (do T.reservedOp lexer name; return fun)
pattern Mul :: Expr -> Expr -> Expr
pattern Mul a b = Bin "*" a b
pattern Div :: Expr -> Expr -> Expr
pattern Div a b = Bin "/" a b
pattern Add :: Expr -> Expr -> Expr
pattern Add a b = Bin "+" a b
pattern Sub :: Expr -> Expr -> Expr
pattern Sub a b = Bin "-" a b
-- 运算符表及其优先级和结合性
table :: E.OperatorTable String () Identity Expr
table =
[ [binary "*" Mul, binary "/" Div],
[binary "+" Add, binary "-" Sub],
[binary ">>>" Con]
]
-- 定义换行分隔的解析器
lineSepBy :: Parser a -> Parser [a]
lineSepBy element = sepBy element newline
parserProcess :: Parser Expr
parserProcess = do
T.reserved lexer "process"
name <- T.identifier lexer
T.braces lexer $ do
args <- T.semiSep lexer ksTerm
return $ Process name args
-- 整数解析器
numberParser :: Parser Expr
numberParser =
Num <$> do
nf <- T.naturalOrFloat lexer
case nf of
Left i -> return $ IntValue i
Right r -> return $ FloatValue r
nilParser :: Parser Expr
nilParser = T.brackets lexer (return Nil)
blockParser :: Parser Expr
blockParser = T.reserved lexer "do" >> T.braces lexer (Block <$> option Skip stmt <*> option Nil expr)
expr :: ParsecT String () Identity Expr
expr = E.buildExpressionParser table term <?> "expression"
basicTermParser :: Parser Expr
basicTermParser = try blockParser <|> Str <$> T.stringLiteral lexer <|> numberParser <|> nilParser <|> Symbol <$> T.identifier lexer <|> List <$> T.brackets lexer (T.commaSep lexer basicTermParser)
term :: Parser Expr
term = T.parens lexer expr <|> basicTermParser <|> parserProcess <?> "simple expression"
-- 语句解析器
stmt :: Parser Stmt
stmt = try seqStmt <|> ifStmt <|> whileStmt <|> assignStmt <|> skipStmt <?> "statement"
basicStmt :: Parser Stmt
basicStmt = ifStmt <|> whileStmt <?> "statement"
ifExpr :: Parser Expr
ifExpr = do
T.reserved lexer "if"
cond <- expr
T.reserved lexer "then"
expr1 <- expr
T.reserved lexer "else"
IfExpr cond expr1 <$> expr
ifStmt :: Parser Stmt
ifStmt = do
T.reserved lexer "if"
cond <- expr
T.reserved lexer "then"
stmt1 <- stmt
T.reserved lexer "else"
If cond stmt1 <$> stmt
whileStmt :: Parser Stmt
whileStmt = do
T.reserved lexer "while"
cond <- expr
T.reserved lexer "do"
While cond <$> stmt
assignStmt :: Parser Stmt
assignStmt = do
var <- T.identifier lexer
T.reservedOp lexer ":="
Assign var <$> expr
declareExpr :: Parser Expr
declareExpr = do
T.reserved lexer "declare"
v <- T.semiSep1 lexer $ do
var <- T.identifier lexer
T.reservedOp lexer ":="
val <- expr
return (var, val)
T.reservedOp lexer "in"
let (var, val) = unzip v
in Declare var val <$> expr
skipStmt :: Parser Stmt
skipStmt = T.reserved lexer "skip" >> return Skip
seqStmt :: Parser Stmt
seqStmt = do
stmt1 <- basicStmt
_ <- T.semi lexer
Seq stmt1 <$> stmt
parseExpr :: String -> Either ParseError Stmt
parseExpr = parse stmt ""
ttest :: IO ()
ttest = do
putStrLn "Enter a simple arithmetic expression:"
input <- getLine
case parseExpr input of
Left err -> print err
Right x -> print x

149
src/Type.hs Normal file
View File

@ -0,0 +1,149 @@
{-# LANGUAGE PatternSynonyms #-}
module Type where
import Control.Monad.Except
import Control.Monad.RWS as R
( MonadReader (ask, local),
MonadState (get, put),
MonadWriter (tell),
RWST,
)
import qualified Data.Map as Map
import qualified Data.Set as Set
import qualified Premitive as P
type Name = String
newtype TVar = TV Name deriving (Eq, Show, Ord)
data Type
= TCon Name [Type]
| TVar TVar
deriving (Show, Eq, Ord)
pattern TInt = TCon "Int" []
pattern TBool = TCon "Bool" []
pattern a :-> b = TCon "->" [a, b]
pattern TNumber = TCon "Number" []
pattern TList a = TCon "List" [a]
pattern TNil = TCon "Nil" []
data Scheme = Forall (Set.Set TVar) Type
data Constraint = Constraint Type Type
type Context = Map.Map Name Scheme
type Count = Int
type Constraints = [Constraint]
type Infer a = RWST Context Constraints Count (Except String) a
-- constrain :: Type -> Type -> Infer ()
-- constrain t1 t2 = tell [Constraint t1 t2]
fresh :: Infer Type
fresh = do
count <- get
put (count + 1)
return . TVar . TV $ show count
type Subst = Map.Map TVar Type
compose :: Subst -> Subst -> Subst
compose a b = Map.map (apply a) (b `Map.union` a)
class Substitutable a where
apply :: Subst -> a -> a
tvs :: a -> Set.Set TVar
instance Substitutable Type where
tvs (TVar tv) = Set.singleton tv
tvs (TCon _ ts) = foldr (Set.union . tvs) Set.empty ts
apply s t@(TVar tv) = Map.findWithDefault t tv s
apply s (TCon c ts) = TCon c $ map (apply s) ts
instance Substitutable Scheme where
tvs (Forall vs t) = tvs t `Set.difference` vs
apply s (Forall vs t) = Forall vs $ apply (foldr Map.delete s vs) t
instance Substitutable Constraint where
tvs (Constraint t1 t2) = tvs t1 `Set.union` tvs t2
apply s (Constraint t1 t2) = Constraint (apply s t1) (apply s t2)
instance (Substitutable a) => Substitutable [a] where
apply s = map (apply s)
tvs = foldr (Set.union . tvs) Set.empty
generalize :: Context -> Type -> Scheme
generalize ctx t = Forall (tvs t `Set.difference` tvs (Map.elems ctx)) t
instantiate :: Scheme -> Infer Type
instantiate (Forall vs t) = do
let vars = Set.toList vs
ftvs <- traverse (const fresh) vars
let subst = Map.fromList (zip vars ftvs)
return $ apply subst t
constrain :: Type -> Type -> Infer ()
constrain a b = tell [Constraint a b]
infer :: P.Expr -> Infer Type
infer e = case e of
P.Num _ -> return TInt
P.Bool _ -> return TBool -- Boolean literal
P.IfExpr e1 e2 e3 -> do
-- If expression
t1 <- infer e1
constrain t1 TBool
t2 <- infer e2
t3 <- infer e3
constrain t2 t3
return t2
P.Bin op e1 e2 -> do
-- Binary operator
t1 <- infer e1
t2 <- infer e2
constrain t1 TInt
constrain t2 TInt
case op of
"+" -> return TInt
"-" -> return TInt
"*" -> return TInt
"/" -> return TInt
_ -> throwError $ "unknown operator: " ++ op
P.Con e1 e2 -> do
t1 <- infer e1
t2 <- infer e2
constrain t1 t2
return t2
P.Nil -> do
return TNil
P.List es -> do
ts <- traverse infer es
return $ TList (head ts)
P.Symbol s -> do
ctx <- ask
case Map.lookup s ctx of
Nothing -> throwError $ "unbound variable: " ++ s
Just s' -> instantiate s'
P.Declare names es e -> do
ts <- traverse infer es
let ctx = Map.fromList $ zip names (map (generalize Map.empty) ts)
local (Map.union ctx) (infer e)
P.Block s e -> do
t2 <- infer e
return t2
P.Process _ _ -> do
return TNil
P.Pipeln _ _ -> do
return TNil
P.Str _ -> do
return TNil

67
stack.yaml Normal file
View File

@ -0,0 +1,67 @@
# This file was automatically generated by 'stack init'
#
# Some commonly used options have been documented as comments in this file.
# For advanced use and comprehensive documentation of the format, please see:
# https://docs.haskellstack.org/en/stable/yaml_configuration/
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
# A snapshot resolver dictates the compiler version and the set of packages
# to be used for project dependencies. For example:
#
# resolver: lts-3.5
# resolver: nightly-2015-09-21
# resolver: ghc-7.10.2
#
# The location of a snapshot can be provided as a file or url. Stack assumes
# a snapshot provided as a file might change, whereas a url resource does not.
#
# resolver: ./custom-snapshot.yaml
# resolver: https://example.com/snapshots/2018-01-01.yaml
resolver:
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/21.yaml
# User packages to be built.
# Various formats can be used as shown in the example below.
#
# packages:
# - some-directory
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
# subdirs:
# - auto-update
# - wai
packages:
- .
# Dependency packages to be pulled from upstream that are not in the resolver.
# These entries can reference officially published versions as well as
# forks / in-progress versions pinned to a git hash. For example:
#
# extra-deps:
# - acme-missiles-0.3
# - git: https://github.com/commercialhaskell/stack.git
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
#
# extra-deps: []
# Override default flag values for local packages and extra-deps
# flags: {}
# Extra package databases containing global packages
# extra-package-dbs: []
# Control whether we use the GHC we find on the path
# system-ghc: true
#
# Require a specific version of Stack, using version ranges
# require-stack-version: -any # Default
# require-stack-version: ">=2.9"
#
# Override the architecture used by Stack, especially useful on Windows
# arch: i386
# arch: x86_64
#
# Extra directories used by Stack for building
# extra-include-dirs: [/path/to/dir]
# extra-lib-dirs: [/path/to/dir]
#
# Allow a newer minor version of GHC than the snapshot specifies
# compiler-check: newer-minor

13
stack.yaml.lock Normal file
View File

@ -0,0 +1,13 @@
# This file was autogenerated by Stack.
# You should not edit this file by hand.
# For more information, please see the documentation at:
# https://docs.haskellstack.org/en/stable/lock_files
packages: []
snapshots:
- completed:
sha256: 7d4b649cf368f9076d8aa049aa44efe58950971d105892734e9957b2a26a2186
size: 640060
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/21.yaml
original:
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/21.yaml

45
test.pll Normal file
View File

@ -0,0 +1,45 @@
[] , 10 ,10.0, "", process, pipeline, true/false
if then else
while
do while
skip
declare a := 2 in do {
print a;
show a;
3 + 1
}
declare a := 2 in if true then ... else ...
declare a:=2 in do {
if a then ..
}
declare a:=2; b:=2;c :=2 in do{
}
declare a:=2 in declare b:=2 in declare c:=2 in do {
}
a:[]
(c:(b:(a:[])))
process A {
a :: Number
b :: Number
}
pipeline {
A >>> B >>> C
A >>> (B ||| C) >>> D
}

2
test/Spec.hs Normal file
View File

@ -0,0 +1,2 @@
main :: IO ()
main = putStrLn "Test suite not yet implemented"